Speed up install with --prefer-offline --no-audit --no-fund, fix postinstall path resolution
Install down from 60s to ~10s. Core packages only (11), heavy optional packages available via feynman packages install. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,13 +6,10 @@
|
|||||||
"npm:pi-web-access",
|
"npm:pi-web-access",
|
||||||
"npm:pi-markdown-preview",
|
"npm:pi-markdown-preview",
|
||||||
"npm:@walterra/pi-charts",
|
"npm:@walterra/pi-charts",
|
||||||
"npm:pi-generative-ui",
|
|
||||||
"npm:pi-mermaid",
|
"npm:pi-mermaid",
|
||||||
"npm:@aliou/pi-processes",
|
"npm:@aliou/pi-processes",
|
||||||
"npm:pi-zotero",
|
"npm:pi-zotero",
|
||||||
"npm:@kaiserlich-dev/pi-session-search",
|
|
||||||
"npm:pi-schedule-prompt",
|
"npm:pi-schedule-prompt",
|
||||||
"npm:@samfp/pi-memory",
|
|
||||||
"npm:@tmustier/pi-ralph-wiggum"
|
"npm:@tmustier/pi-ralph-wiggum"
|
||||||
],
|
],
|
||||||
"quietStartup": true,
|
"quietStartup": true,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Four bundled research agents, dispatched automatically or via subagent commands.
|
|||||||
- **Docker** — isolated container execution for safe experiments on your machine
|
- **Docker** — isolated container execution for safe experiments on your machine
|
||||||
- **[Agent Computer](https://agentcomputer.ai)** — secure cloud execution for long-running research and GPU workloads
|
- **[Agent Computer](https://agentcomputer.ai)** — secure cloud execution for long-running research and GPU workloads
|
||||||
- **Web search** — Gemini or Perplexity, zero-config default via signed-in Chromium
|
- **Web search** — Gemini or Perplexity, zero-config default via signed-in Chromium
|
||||||
- **Session search** — indexed recall across prior research sessions
|
- **Session search** — optional indexed recall across prior research sessions
|
||||||
- **Preview** — browser and PDF export of generated artifacts
|
- **Preview** — browser and PDF export of generated artifacts
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -76,6 +76,8 @@ feynman status # current config summary
|
|||||||
feynman model login [provider] # model auth
|
feynman model login [provider] # model auth
|
||||||
feynman model set <provider/model> # set default model
|
feynman model set <provider/model> # set default model
|
||||||
feynman alpha login # alphaXiv auth
|
feynman alpha login # alphaXiv auth
|
||||||
|
feynman packages list # core vs optional packages
|
||||||
|
feynman packages install memory # opt into heavier packages on demand
|
||||||
feynman search status # web search config
|
feynman search status # web search config
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ export const cliCommandSections = [
|
|||||||
{
|
{
|
||||||
title: "Utilities",
|
title: "Utilities",
|
||||||
commands: [
|
commands: [
|
||||||
|
{ usage: "feynman packages list", description: "Show core and optional Pi package presets." },
|
||||||
|
{ usage: "feynman packages install <preset>", description: "Install optional package presets on demand." },
|
||||||
{ usage: "feynman search status", description: "Show Pi web-access status and config path." },
|
{ usage: "feynman search status", description: "Show Pi web-access status and config path." },
|
||||||
{ usage: "feynman update [package]", description: "Update installed packages, or a specific package." },
|
{ usage: "feynman update [package]", description: "Update installed packages, or a specific package." },
|
||||||
],
|
],
|
||||||
@@ -118,7 +120,7 @@ export const legacyFlags = [
|
|||||||
{ usage: "--setup-preview", description: "Alias for `feynman setup preview`." },
|
{ usage: "--setup-preview", description: "Alias for `feynman setup preview`." },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const topLevelCommandNames = ["alpha", "chat", "doctor", "help", "model", "search", "setup", "status", "update"];
|
export const topLevelCommandNames = ["alpha", "chat", "doctor", "help", "model", "packages", "search", "setup", "status", "update"];
|
||||||
|
|
||||||
export function formatSlashUsage(command) {
|
export function formatSlashUsage(command) {
|
||||||
return `/${command.name}${command.args ? ` ${command.args}` : ""}`;
|
return `/${command.name}${command.args ? ` ${command.args}` : ""}`;
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@companion-ai/feynman",
|
"name": "@companion-ai/feynman",
|
||||||
"version": "0.2.5",
|
"version": "0.2.6",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@companion-ai/feynman",
|
"name": "@companion-ai/feynman",
|
||||||
"version": "0.2.5",
|
"version": "0.2.6",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@companion-ai/alpha-hub": "^0.1.2",
|
"@companion-ai/alpha-hub": "^0.1.2",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@companion-ai/feynman",
|
"name": "@companion-ai/feynman",
|
||||||
"version": "0.2.5",
|
"version": "0.2.6",
|
||||||
"description": "Research-first CLI agent built on Pi and alphaXiv",
|
"description": "Research-first CLI agent built on Pi and alphaXiv",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url";
|
|||||||
|
|
||||||
const here = dirname(fileURLToPath(import.meta.url));
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
const appRoot = resolve(here, "..");
|
const appRoot = resolve(here, "..");
|
||||||
|
const isGlobalInstall = process.env.npm_config_global === "true" || process.env.npm_config_location === "global";
|
||||||
|
|
||||||
function findNodeModules() {
|
function findNodeModules() {
|
||||||
let dir = appRoot;
|
let dir = appRoot;
|
||||||
@@ -53,7 +54,75 @@ const settingsPath = resolve(appRoot, ".feynman", "settings.json");
|
|||||||
const workspaceDir = resolve(appRoot, ".feynman", "npm");
|
const workspaceDir = resolve(appRoot, ".feynman", "npm");
|
||||||
const workspacePackageJsonPath = resolve(workspaceDir, "package.json");
|
const workspacePackageJsonPath = resolve(workspaceDir, "package.json");
|
||||||
|
|
||||||
// Pi handles package installation from .feynman/settings.json at runtime — no manual install needed
|
function resolveExecutable(name, fallbackPaths = []) {
|
||||||
|
for (const candidate of fallbackPaths) {
|
||||||
|
if (existsSync(candidate)) return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = spawnSync("sh", ["-lc", `command -v ${name}`], {
|
||||||
|
encoding: "utf8",
|
||||||
|
stdio: ["ignore", "pipe", "ignore"],
|
||||||
|
});
|
||||||
|
if (result.status === 0) {
|
||||||
|
const resolved = result.stdout.trim();
|
||||||
|
if (resolved) return resolved;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensurePackageWorkspace() {
|
||||||
|
if (!existsSync(settingsPath)) return;
|
||||||
|
|
||||||
|
const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
||||||
|
const packageSpecs = Array.isArray(settings.packages)
|
||||||
|
? settings.packages
|
||||||
|
.filter((v) => typeof v === "string" && v.startsWith("npm:"))
|
||||||
|
.map((v) => v.slice(4))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (packageSpecs.length === 0) return;
|
||||||
|
if (existsSync(resolve(workspaceRoot, packageSpecs[0]))) return;
|
||||||
|
|
||||||
|
mkdirSync(workspaceDir, { recursive: true });
|
||||||
|
writeFileSync(
|
||||||
|
workspacePackageJsonPath,
|
||||||
|
JSON.stringify({ name: "feynman-packages", private: true }, null, 2) + "\n",
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("[feynman] installing research packages...");
|
||||||
|
const result = spawnSync("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund", "--prefix", workspaceDir, ...packageSpecs], {
|
||||||
|
stdio: "inherit",
|
||||||
|
timeout: 300000,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.status !== 0) {
|
||||||
|
console.warn("[feynman] warning: package install failed, Pi will retry on first launch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensurePackageWorkspace();
|
||||||
|
|
||||||
|
function ensurePandoc() {
|
||||||
|
if (!isGlobalInstall) return;
|
||||||
|
if (process.platform !== "darwin") return;
|
||||||
|
if (process.env.FEYNMAN_SKIP_PANDOC_INSTALL === "1") return;
|
||||||
|
if (resolveExecutable("pandoc", ["/opt/homebrew/bin/pandoc", "/usr/local/bin/pandoc"])) return;
|
||||||
|
|
||||||
|
const brewPath = resolveExecutable("brew", ["/opt/homebrew/bin/brew", "/usr/local/bin/brew"]);
|
||||||
|
if (!brewPath) return;
|
||||||
|
|
||||||
|
console.log("[feynman] installing pandoc...");
|
||||||
|
const result = spawnSync(brewPath, ["install", "pandoc"], {
|
||||||
|
stdio: "inherit",
|
||||||
|
timeout: 300000,
|
||||||
|
});
|
||||||
|
if (result.status !== 0) {
|
||||||
|
console.warn("[feynman] warning: pandoc install failed, run `feynman --setup-preview` later");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensurePandoc();
|
||||||
|
|
||||||
if (existsSync(packageJsonPath)) {
|
if (existsSync(packageJsonPath)) {
|
||||||
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
||||||
|
|||||||
72
src/cli.ts
72
src/cli.ts
@@ -16,6 +16,7 @@ import { AuthStorage, DefaultPackageManager, ModelRegistry, SettingsManager } fr
|
|||||||
import { syncBundledAssets } from "./bootstrap/sync.js";
|
import { syncBundledAssets } from "./bootstrap/sync.js";
|
||||||
import { ensureFeynmanHome, getDefaultSessionDir, getFeynmanAgentDir, getFeynmanHome } from "./config/paths.js";
|
import { ensureFeynmanHome, getDefaultSessionDir, getFeynmanAgentDir, getFeynmanHome } from "./config/paths.js";
|
||||||
import { launchPiChat } from "./pi/launch.js";
|
import { launchPiChat } from "./pi/launch.js";
|
||||||
|
import { CORE_PACKAGE_SOURCES, getOptionalPackagePresetSources, listOptionalPackagePresets } from "./pi/package-presets.js";
|
||||||
import { normalizeFeynmanSettings, normalizeThinkingLevel, parseModelSpec } from "./pi/settings.js";
|
import { normalizeFeynmanSettings, normalizeThinkingLevel, parseModelSpec } from "./pi/settings.js";
|
||||||
import {
|
import {
|
||||||
loginModelProvider,
|
loginModelProvider,
|
||||||
@@ -166,6 +167,72 @@ async function handleUpdateCommand(workingDir: string, feynmanAgentDir: string,
|
|||||||
console.log("All packages up to date.");
|
console.log("All packages up to date.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handlePackagesCommand(subcommand: string | undefined, args: string[], workingDir: string, feynmanAgentDir: string): Promise<void> {
|
||||||
|
const settingsManager = SettingsManager.create(workingDir, feynmanAgentDir);
|
||||||
|
const configuredSources = new Set(
|
||||||
|
settingsManager
|
||||||
|
.getPackages()
|
||||||
|
.map((entry) => (typeof entry === "string" ? entry : entry.source))
|
||||||
|
.filter((entry): entry is string => typeof entry === "string"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!subcommand || subcommand === "list") {
|
||||||
|
printPanel("Feynman Packages", [
|
||||||
|
"Core packages are installed by default to keep first-run setup fast.",
|
||||||
|
]);
|
||||||
|
printSection("Core");
|
||||||
|
for (const source of CORE_PACKAGE_SOURCES) {
|
||||||
|
printInfo(source);
|
||||||
|
}
|
||||||
|
printSection("Optional");
|
||||||
|
for (const preset of listOptionalPackagePresets()) {
|
||||||
|
const installed = preset.sources.every((source) => configuredSources.has(source));
|
||||||
|
printInfo(`${preset.name}${installed ? " (installed)" : ""} ${preset.description}`);
|
||||||
|
}
|
||||||
|
printInfo("Install with: feynman packages install <preset>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subcommand !== "install") {
|
||||||
|
throw new Error(`Unknown packages command: ${subcommand}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = args[0];
|
||||||
|
if (!target) {
|
||||||
|
throw new Error("Usage: feynman packages install <generative-ui|memory|session-search|all-extras>");
|
||||||
|
}
|
||||||
|
|
||||||
|
const sources = getOptionalPackagePresetSources(target);
|
||||||
|
if (!sources) {
|
||||||
|
throw new Error(`Unknown package preset: ${target}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageManager = new DefaultPackageManager({
|
||||||
|
cwd: workingDir,
|
||||||
|
agentDir: feynmanAgentDir,
|
||||||
|
settingsManager,
|
||||||
|
});
|
||||||
|
packageManager.setProgressCallback((event) => {
|
||||||
|
if (event.type === "start") {
|
||||||
|
console.log(`Installing ${event.source}...`);
|
||||||
|
} else if (event.type === "complete") {
|
||||||
|
console.log(`Installed ${event.source}`);
|
||||||
|
} else if (event.type === "error") {
|
||||||
|
console.error(`Failed to install ${event.source}: ${event.message ?? "unknown error"}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const source of sources) {
|
||||||
|
if (configuredSources.has(source)) {
|
||||||
|
console.log(`${source} already installed`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await packageManager.install(source);
|
||||||
|
}
|
||||||
|
await settingsManager.flush();
|
||||||
|
console.log("Optional packages installed.");
|
||||||
|
}
|
||||||
|
|
||||||
function handleSearchCommand(subcommand: string | undefined): void {
|
function handleSearchCommand(subcommand: string | undefined): void {
|
||||||
if (!subcommand || subcommand === "status") {
|
if (!subcommand || subcommand === "status") {
|
||||||
printSearchStatus();
|
printSearchStatus();
|
||||||
@@ -333,6 +400,11 @@ export async function main(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command === "packages") {
|
||||||
|
await handlePackagesCommand(rest[0], rest.slice(1), workingDir, feynmanAgentDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (command === "update") {
|
if (command === "update") {
|
||||||
await handleUpdateCommand(workingDir, feynmanAgentDir, rest[0]);
|
await handleUpdateCommand(workingDir, feynmanAgentDir, rest[0]);
|
||||||
return;
|
return;
|
||||||
|
|||||||
87
src/pi/package-presets.ts
Normal file
87
src/pi/package-presets.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import type { PackageSource } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
|
export const CORE_PACKAGE_SOURCES = [
|
||||||
|
"npm:pi-subagents",
|
||||||
|
"npm:pi-btw",
|
||||||
|
"npm:pi-docparser",
|
||||||
|
"npm:pi-web-access",
|
||||||
|
"npm:pi-markdown-preview",
|
||||||
|
"npm:@walterra/pi-charts",
|
||||||
|
"npm:pi-mermaid",
|
||||||
|
"npm:@aliou/pi-processes",
|
||||||
|
"npm:pi-zotero",
|
||||||
|
"npm:pi-schedule-prompt",
|
||||||
|
"npm:@tmustier/pi-ralph-wiggum",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const OPTIONAL_PACKAGE_PRESETS = {
|
||||||
|
"generative-ui": {
|
||||||
|
description: "Interactive Glimpse UI widgets.",
|
||||||
|
sources: ["npm:pi-generative-ui"],
|
||||||
|
},
|
||||||
|
memory: {
|
||||||
|
description: "Cross-session memory and preference recall.",
|
||||||
|
sources: ["npm:@samfp/pi-memory"],
|
||||||
|
},
|
||||||
|
"session-search": {
|
||||||
|
description: "Indexed session recall with SQLite-backed search.",
|
||||||
|
sources: ["npm:@kaiserlich-dev/pi-session-search"],
|
||||||
|
},
|
||||||
|
"all-extras": {
|
||||||
|
description: "Install all optional packages.",
|
||||||
|
sources: ["npm:pi-generative-ui", "npm:@samfp/pi-memory", "npm:@kaiserlich-dev/pi-session-search"],
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const LEGACY_DEFAULT_PACKAGE_SOURCES = [
|
||||||
|
...CORE_PACKAGE_SOURCES,
|
||||||
|
"npm:pi-generative-ui",
|
||||||
|
"npm:@kaiserlich-dev/pi-session-search",
|
||||||
|
"npm:@samfp/pi-memory",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type OptionalPackagePresetName = keyof typeof OPTIONAL_PACKAGE_PRESETS;
|
||||||
|
|
||||||
|
function arraysMatchAsSets(left: readonly string[], right: readonly string[]): boolean {
|
||||||
|
if (left.length !== right.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rightSet = new Set(right);
|
||||||
|
return left.every((entry) => rightSet.has(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldPruneLegacyDefaultPackages(packages: PackageSource[] | undefined): boolean {
|
||||||
|
if (!Array.isArray(packages)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (packages.some((entry) => typeof entry !== "string")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return arraysMatchAsSets(packages as string[], LEGACY_DEFAULT_PACKAGE_SOURCES);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOptionalPackagePresetSources(name: string): string[] | undefined {
|
||||||
|
const normalized = name.trim().toLowerCase();
|
||||||
|
if (normalized === "ui") {
|
||||||
|
return [...OPTIONAL_PACKAGE_PRESETS["generative-ui"].sources];
|
||||||
|
}
|
||||||
|
if (normalized === "search") {
|
||||||
|
return [...OPTIONAL_PACKAGE_PRESETS["session-search"].sources];
|
||||||
|
}
|
||||||
|
|
||||||
|
const preset = OPTIONAL_PACKAGE_PRESETS[normalized as OptionalPackagePresetName];
|
||||||
|
return preset ? [...preset.sources] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listOptionalPackagePresets(): Array<{
|
||||||
|
name: OptionalPackagePresetName;
|
||||||
|
description: string;
|
||||||
|
sources: string[];
|
||||||
|
}> {
|
||||||
|
return Object.entries(OPTIONAL_PACKAGE_PRESETS).map(([name, preset]) => ({
|
||||||
|
name: name as OptionalPackagePresetName,
|
||||||
|
description: preset.description,
|
||||||
|
sources: [...preset.sources],
|
||||||
|
}));
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
import { dirname } from "node:path";
|
import { dirname } from "node:path";
|
||||||
|
|
||||||
import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
|
import { AuthStorage, ModelRegistry, type PackageSource } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
|
import { CORE_PACKAGE_SOURCES, shouldPruneLegacyDefaultPackages } from "./package-presets.js";
|
||||||
|
|
||||||
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||||
|
|
||||||
@@ -107,6 +109,11 @@ export function normalizeFeynmanSettings(
|
|||||||
settings.theme = "feynman";
|
settings.theme = "feynman";
|
||||||
settings.quietStartup = true;
|
settings.quietStartup = true;
|
||||||
settings.collapseChangelog = true;
|
settings.collapseChangelog = true;
|
||||||
|
if (!Array.isArray(settings.packages) || settings.packages.length === 0) {
|
||||||
|
settings.packages = [...CORE_PACKAGE_SOURCES];
|
||||||
|
} else if (shouldPruneLegacyDefaultPackages(settings.packages as PackageSource[])) {
|
||||||
|
settings.packages = [...CORE_PACKAGE_SOURCES];
|
||||||
|
}
|
||||||
|
|
||||||
const authStorage = AuthStorage.create(authPath);
|
const authStorage = AuthStorage.create(authPath);
|
||||||
const modelRegistry = new ModelRegistry(authStorage);
|
const modelRegistry = new ModelRegistry(authStorage);
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
import test from "node:test";
|
import test from "node:test";
|
||||||
|
|
||||||
import { normalizeThinkingLevel } from "../src/pi/settings.js";
|
import { CORE_PACKAGE_SOURCES, getOptionalPackagePresetSources, shouldPruneLegacyDefaultPackages } from "../src/pi/package-presets.js";
|
||||||
|
import { normalizeFeynmanSettings, normalizeThinkingLevel } from "../src/pi/settings.js";
|
||||||
|
|
||||||
test("normalizeThinkingLevel accepts the latest Pi thinking levels", () => {
|
test("normalizeThinkingLevel accepts the latest Pi thinking levels", () => {
|
||||||
assert.equal(normalizeThinkingLevel("off"), "off");
|
assert.equal(normalizeThinkingLevel("off"), "off");
|
||||||
@@ -16,3 +20,56 @@ test("normalizeThinkingLevel rejects unknown values", () => {
|
|||||||
assert.equal(normalizeThinkingLevel("turbo"), undefined);
|
assert.equal(normalizeThinkingLevel("turbo"), undefined);
|
||||||
assert.equal(normalizeThinkingLevel(undefined), undefined);
|
assert.equal(normalizeThinkingLevel(undefined), undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("normalizeFeynmanSettings seeds the fast core package set", () => {
|
||||||
|
const root = mkdtempSync(join(tmpdir(), "feynman-settings-"));
|
||||||
|
const settingsPath = join(root, "settings.json");
|
||||||
|
const bundledSettingsPath = join(root, "bundled-settings.json");
|
||||||
|
const authPath = join(root, "auth.json");
|
||||||
|
|
||||||
|
writeFileSync(bundledSettingsPath, "{}\n", "utf8");
|
||||||
|
writeFileSync(authPath, "{}\n", "utf8");
|
||||||
|
|
||||||
|
normalizeFeynmanSettings(settingsPath, bundledSettingsPath, "medium", authPath);
|
||||||
|
|
||||||
|
const settings = JSON.parse(readFileSync(settingsPath, "utf8")) as { packages?: string[] };
|
||||||
|
assert.deepEqual(settings.packages, [...CORE_PACKAGE_SOURCES]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("normalizeFeynmanSettings prunes the legacy slow default package set", () => {
|
||||||
|
const root = mkdtempSync(join(tmpdir(), "feynman-settings-"));
|
||||||
|
const settingsPath = join(root, "settings.json");
|
||||||
|
const bundledSettingsPath = join(root, "bundled-settings.json");
|
||||||
|
const authPath = join(root, "auth.json");
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
settingsPath,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
packages: [
|
||||||
|
...CORE_PACKAGE_SOURCES,
|
||||||
|
"npm:pi-generative-ui",
|
||||||
|
"npm:@kaiserlich-dev/pi-session-search",
|
||||||
|
"npm:@samfp/pi-memory",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
) + "\n",
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
writeFileSync(bundledSettingsPath, "{}\n", "utf8");
|
||||||
|
writeFileSync(authPath, "{}\n", "utf8");
|
||||||
|
|
||||||
|
normalizeFeynmanSettings(settingsPath, bundledSettingsPath, "medium", authPath);
|
||||||
|
|
||||||
|
const settings = JSON.parse(readFileSync(settingsPath, "utf8")) as { packages?: string[] };
|
||||||
|
assert.deepEqual(settings.packages, [...CORE_PACKAGE_SOURCES]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("optional package presets map friendly aliases", () => {
|
||||||
|
assert.deepEqual(getOptionalPackagePresetSources("memory"), ["npm:@samfp/pi-memory"]);
|
||||||
|
assert.deepEqual(getOptionalPackagePresetSources("ui"), ["npm:pi-generative-ui"]);
|
||||||
|
assert.deepEqual(getOptionalPackagePresetSources("search"), ["npm:@kaiserlich-dev/pi-session-search"]);
|
||||||
|
assert.equal(shouldPruneLegacyDefaultPackages(["npm:custom"]), false);
|
||||||
|
});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -53,7 +53,19 @@ For PDF and HTML export of generated artifacts, Feynman needs `pandoc`:
|
|||||||
feynman --setup-preview
|
feynman --setup-preview
|
||||||
```
|
```
|
||||||
|
|
||||||
This installs pandoc automatically on macOS/Homebrew systems.
|
Global macOS installs also try to install pandoc automatically when Homebrew is available. Use the command above to retry manually.
|
||||||
|
|
||||||
|
### Optional packages
|
||||||
|
|
||||||
|
Feynman keeps the default package set lean so first-run installs stay fast. Install the heavier optional packages only when you need them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
feynman packages list
|
||||||
|
feynman packages install memory
|
||||||
|
feynman packages install session-search
|
||||||
|
feynman packages install generative-ui
|
||||||
|
feynman packages install all-extras
|
||||||
|
```
|
||||||
|
|
||||||
## Diagnostics
|
## Diagnostics
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ order: 3
|
|||||||
|
|
||||||
Curated Pi packages bundled with Feynman. The runtime package list lives in `.feynman/settings.json`.
|
Curated Pi packages bundled with Feynman. The runtime package list lives in `.feynman/settings.json`.
|
||||||
|
|
||||||
|
## Core packages
|
||||||
|
|
||||||
|
Installed by default.
|
||||||
|
|
||||||
| Package | Purpose |
|
| Package | Purpose |
|
||||||
|---------|---------|
|
|---------|---------|
|
||||||
| `pi-subagents` | Parallel literature gathering and decomposition. |
|
| `pi-subagents` | Parallel literature gathering and decomposition. |
|
||||||
@@ -15,11 +19,18 @@ Curated Pi packages bundled with Feynman. The runtime package list lives in `.fe
|
|||||||
| `pi-web-access` | Web, GitHub, PDF, and media access. |
|
| `pi-web-access` | Web, GitHub, PDF, and media access. |
|
||||||
| `pi-markdown-preview` | Polished Markdown and LaTeX-heavy research writeups. |
|
| `pi-markdown-preview` | Polished Markdown and LaTeX-heavy research writeups. |
|
||||||
| `@walterra/pi-charts` | Charts and quantitative visualizations. |
|
| `@walterra/pi-charts` | Charts and quantitative visualizations. |
|
||||||
| `pi-generative-ui` | Interactive HTML-style widgets. |
|
|
||||||
| `pi-mermaid` | Diagrams in the TUI. |
|
| `pi-mermaid` | Diagrams in the TUI. |
|
||||||
| `@aliou/pi-processes` | Long-running experiments and log tails. |
|
| `@aliou/pi-processes` | Long-running experiments and log tails. |
|
||||||
| `pi-zotero` | Citation-library workflows. |
|
| `pi-zotero` | Citation-library workflows. |
|
||||||
| `@kaiserlich-dev/pi-session-search` | Indexed session recall and summarize/resume UI. |
|
|
||||||
| `pi-schedule-prompt` | Recurring and deferred research jobs. |
|
| `pi-schedule-prompt` | Recurring and deferred research jobs. |
|
||||||
| `@samfp/pi-memory` | Automatic preference and correction memory across sessions. |
|
|
||||||
| `@tmustier/pi-ralph-wiggum` | Long-running agent loops for iterative development. |
|
| `@tmustier/pi-ralph-wiggum` | Long-running agent loops for iterative development. |
|
||||||
|
|
||||||
|
## Optional packages
|
||||||
|
|
||||||
|
Install on demand with `feynman packages install <preset>`.
|
||||||
|
|
||||||
|
| Package | Purpose |
|
||||||
|
|---------|---------|
|
||||||
|
| `pi-generative-ui` | Interactive HTML-style widgets. |
|
||||||
|
| `@kaiserlich-dev/pi-session-search` | Indexed session recall and summarize/resume UI. |
|
||||||
|
| `@samfp/pi-memory` | Automatic preference and correction memory across sessions. |
|
||||||
|
|||||||
Reference in New Issue
Block a user