@companion-ai/feynman 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.feynman/settings.json +0 -3
- package/README.md +3 -1
- package/bin/feynman.js +7 -1
- package/dist/cli.js +64 -0
- package/dist/pi/package-presets.js +71 -0
- package/dist/pi/settings.js +7 -0
- package/metadata/commands.mjs +3 -1
- package/package.json +4 -4
- package/scripts/patch-embedded-pi.mjs +70 -1
package/.feynman/settings.json
CHANGED
|
@@ -6,13 +6,10 @@
|
|
|
6
6
|
"npm:pi-web-access",
|
|
7
7
|
"npm:pi-markdown-preview",
|
|
8
8
|
"npm:@walterra/pi-charts",
|
|
9
|
-
"npm:pi-generative-ui",
|
|
10
9
|
"npm:pi-mermaid",
|
|
11
10
|
"npm:@aliou/pi-processes",
|
|
12
11
|
"npm:pi-zotero",
|
|
13
|
-
"npm:@kaiserlich-dev/pi-session-search",
|
|
14
12
|
"npm:pi-schedule-prompt",
|
|
15
|
-
"npm:@samfp/pi-memory",
|
|
16
13
|
"npm:@tmustier/pi-ralph-wiggum"
|
|
17
14
|
],
|
|
18
15
|
"quietStartup": true,
|
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ Four bundled research agents, dispatched automatically or via subagent commands.
|
|
|
61
61
|
- **Docker** — isolated container execution for safe experiments on your machine
|
|
62
62
|
- **[Agent Computer](https://agentcomputer.ai)** — secure cloud execution for long-running research and GPU workloads
|
|
63
63
|
- **Web search** — Gemini or Perplexity, zero-config default via signed-in Chromium
|
|
64
|
-
- **Session search** — indexed recall across prior research sessions
|
|
64
|
+
- **Session search** — optional indexed recall across prior research sessions
|
|
65
65
|
- **Preview** — browser and PDF export of generated artifacts
|
|
66
66
|
|
|
67
67
|
---
|
|
@@ -76,6 +76,8 @@ feynman status # current config summary
|
|
|
76
76
|
feynman model login [provider] # model auth
|
|
77
77
|
feynman model set <provider/model> # set default model
|
|
78
78
|
feynman alpha login # alphaXiv auth
|
|
79
|
+
feynman packages list # core vs optional packages
|
|
80
|
+
feynman packages install memory # opt into heavier packages on demand
|
|
79
81
|
feynman search status # web search config
|
|
80
82
|
```
|
|
81
83
|
|
package/bin/feynman.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
const v = process.versions.node.split(".").map(Number);
|
|
3
|
+
if (v[0] < 20) {
|
|
4
|
+
console.error(`feynman requires Node.js 20 or later (you have ${process.versions.node})`);
|
|
5
|
+
console.error("upgrade: https://nodejs.org or nvm install 20");
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
import("../dist/index.js");
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { AuthStorage, DefaultPackageManager, ModelRegistry, SettingsManager } fr
|
|
|
8
8
|
import { syncBundledAssets } from "./bootstrap/sync.js";
|
|
9
9
|
import { ensureFeynmanHome, getDefaultSessionDir, getFeynmanAgentDir, getFeynmanHome } from "./config/paths.js";
|
|
10
10
|
import { launchPiChat } from "./pi/launch.js";
|
|
11
|
+
import { CORE_PACKAGE_SOURCES, getOptionalPackagePresetSources, listOptionalPackagePresets } from "./pi/package-presets.js";
|
|
11
12
|
import { normalizeFeynmanSettings, normalizeThinkingLevel, parseModelSpec } from "./pi/settings.js";
|
|
12
13
|
import { loginModelProvider, logoutModelProvider, printModelList, setDefaultModelSpec, } from "./model/commands.js";
|
|
13
14
|
import { printSearchStatus } from "./search/commands.js";
|
|
@@ -125,6 +126,65 @@ async function handleUpdateCommand(workingDir, feynmanAgentDir, source) {
|
|
|
125
126
|
await settingsManager.flush();
|
|
126
127
|
console.log("All packages up to date.");
|
|
127
128
|
}
|
|
129
|
+
async function handlePackagesCommand(subcommand, args, workingDir, feynmanAgentDir) {
|
|
130
|
+
const settingsManager = SettingsManager.create(workingDir, feynmanAgentDir);
|
|
131
|
+
const configuredSources = new Set(settingsManager
|
|
132
|
+
.getPackages()
|
|
133
|
+
.map((entry) => (typeof entry === "string" ? entry : entry.source))
|
|
134
|
+
.filter((entry) => typeof entry === "string"));
|
|
135
|
+
if (!subcommand || subcommand === "list") {
|
|
136
|
+
printPanel("Feynman Packages", [
|
|
137
|
+
"Core packages are installed by default to keep first-run setup fast.",
|
|
138
|
+
]);
|
|
139
|
+
printSection("Core");
|
|
140
|
+
for (const source of CORE_PACKAGE_SOURCES) {
|
|
141
|
+
printInfo(source);
|
|
142
|
+
}
|
|
143
|
+
printSection("Optional");
|
|
144
|
+
for (const preset of listOptionalPackagePresets()) {
|
|
145
|
+
const installed = preset.sources.every((source) => configuredSources.has(source));
|
|
146
|
+
printInfo(`${preset.name}${installed ? " (installed)" : ""} ${preset.description}`);
|
|
147
|
+
}
|
|
148
|
+
printInfo("Install with: feynman packages install <preset>");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (subcommand !== "install") {
|
|
152
|
+
throw new Error(`Unknown packages command: ${subcommand}`);
|
|
153
|
+
}
|
|
154
|
+
const target = args[0];
|
|
155
|
+
if (!target) {
|
|
156
|
+
throw new Error("Usage: feynman packages install <generative-ui|memory|session-search|all-extras>");
|
|
157
|
+
}
|
|
158
|
+
const sources = getOptionalPackagePresetSources(target);
|
|
159
|
+
if (!sources) {
|
|
160
|
+
throw new Error(`Unknown package preset: ${target}`);
|
|
161
|
+
}
|
|
162
|
+
const packageManager = new DefaultPackageManager({
|
|
163
|
+
cwd: workingDir,
|
|
164
|
+
agentDir: feynmanAgentDir,
|
|
165
|
+
settingsManager,
|
|
166
|
+
});
|
|
167
|
+
packageManager.setProgressCallback((event) => {
|
|
168
|
+
if (event.type === "start") {
|
|
169
|
+
console.log(`Installing ${event.source}...`);
|
|
170
|
+
}
|
|
171
|
+
else if (event.type === "complete") {
|
|
172
|
+
console.log(`Installed ${event.source}`);
|
|
173
|
+
}
|
|
174
|
+
else if (event.type === "error") {
|
|
175
|
+
console.error(`Failed to install ${event.source}: ${event.message ?? "unknown error"}`);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
for (const source of sources) {
|
|
179
|
+
if (configuredSources.has(source)) {
|
|
180
|
+
console.log(`${source} already installed`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
await packageManager.install(source);
|
|
184
|
+
}
|
|
185
|
+
await settingsManager.flush();
|
|
186
|
+
console.log("Optional packages installed.");
|
|
187
|
+
}
|
|
128
188
|
function handleSearchCommand(subcommand) {
|
|
129
189
|
if (!subcommand || subcommand === "status") {
|
|
130
190
|
printSearchStatus();
|
|
@@ -267,6 +327,10 @@ export async function main() {
|
|
|
267
327
|
handleSearchCommand(rest[0]);
|
|
268
328
|
return;
|
|
269
329
|
}
|
|
330
|
+
if (command === "packages") {
|
|
331
|
+
await handlePackagesCommand(rest[0], rest.slice(1), workingDir, feynmanAgentDir);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
270
334
|
if (command === "update") {
|
|
271
335
|
await handleUpdateCommand(workingDir, feynmanAgentDir, rest[0]);
|
|
272
336
|
return;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export const CORE_PACKAGE_SOURCES = [
|
|
2
|
+
"npm:pi-subagents",
|
|
3
|
+
"npm:pi-btw",
|
|
4
|
+
"npm:pi-docparser",
|
|
5
|
+
"npm:pi-web-access",
|
|
6
|
+
"npm:pi-markdown-preview",
|
|
7
|
+
"npm:@walterra/pi-charts",
|
|
8
|
+
"npm:pi-mermaid",
|
|
9
|
+
"npm:@aliou/pi-processes",
|
|
10
|
+
"npm:pi-zotero",
|
|
11
|
+
"npm:pi-schedule-prompt",
|
|
12
|
+
"npm:@tmustier/pi-ralph-wiggum",
|
|
13
|
+
];
|
|
14
|
+
export const OPTIONAL_PACKAGE_PRESETS = {
|
|
15
|
+
"generative-ui": {
|
|
16
|
+
description: "Interactive Glimpse UI widgets.",
|
|
17
|
+
sources: ["npm:pi-generative-ui"],
|
|
18
|
+
},
|
|
19
|
+
memory: {
|
|
20
|
+
description: "Cross-session memory and preference recall.",
|
|
21
|
+
sources: ["npm:@samfp/pi-memory"],
|
|
22
|
+
},
|
|
23
|
+
"session-search": {
|
|
24
|
+
description: "Indexed session recall with SQLite-backed search.",
|
|
25
|
+
sources: ["npm:@kaiserlich-dev/pi-session-search"],
|
|
26
|
+
},
|
|
27
|
+
"all-extras": {
|
|
28
|
+
description: "Install all optional packages.",
|
|
29
|
+
sources: ["npm:pi-generative-ui", "npm:@samfp/pi-memory", "npm:@kaiserlich-dev/pi-session-search"],
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const LEGACY_DEFAULT_PACKAGE_SOURCES = [
|
|
33
|
+
...CORE_PACKAGE_SOURCES,
|
|
34
|
+
"npm:pi-generative-ui",
|
|
35
|
+
"npm:@kaiserlich-dev/pi-session-search",
|
|
36
|
+
"npm:@samfp/pi-memory",
|
|
37
|
+
];
|
|
38
|
+
function arraysMatchAsSets(left, right) {
|
|
39
|
+
if (left.length !== right.length) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const rightSet = new Set(right);
|
|
43
|
+
return left.every((entry) => rightSet.has(entry));
|
|
44
|
+
}
|
|
45
|
+
export function shouldPruneLegacyDefaultPackages(packages) {
|
|
46
|
+
if (!Array.isArray(packages)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (packages.some((entry) => typeof entry !== "string")) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return arraysMatchAsSets(packages, LEGACY_DEFAULT_PACKAGE_SOURCES);
|
|
53
|
+
}
|
|
54
|
+
export function getOptionalPackagePresetSources(name) {
|
|
55
|
+
const normalized = name.trim().toLowerCase();
|
|
56
|
+
if (normalized === "ui") {
|
|
57
|
+
return [...OPTIONAL_PACKAGE_PRESETS["generative-ui"].sources];
|
|
58
|
+
}
|
|
59
|
+
if (normalized === "search") {
|
|
60
|
+
return [...OPTIONAL_PACKAGE_PRESETS["session-search"].sources];
|
|
61
|
+
}
|
|
62
|
+
const preset = OPTIONAL_PACKAGE_PRESETS[normalized];
|
|
63
|
+
return preset ? [...preset.sources] : undefined;
|
|
64
|
+
}
|
|
65
|
+
export function listOptionalPackagePresets() {
|
|
66
|
+
return Object.entries(OPTIONAL_PACKAGE_PRESETS).map(([name, preset]) => ({
|
|
67
|
+
name: name,
|
|
68
|
+
description: preset.description,
|
|
69
|
+
sources: [...preset.sources],
|
|
70
|
+
}));
|
|
71
|
+
}
|
package/dist/pi/settings.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
|
|
4
|
+
import { CORE_PACKAGE_SOURCES, shouldPruneLegacyDefaultPackages } from "./package-presets.js";
|
|
4
5
|
export function parseModelSpec(spec, modelRegistry) {
|
|
5
6
|
const trimmed = spec.trim();
|
|
6
7
|
const separator = trimmed.includes(":") ? ":" : trimmed.includes("/") ? "/" : null;
|
|
@@ -83,6 +84,12 @@ export function normalizeFeynmanSettings(settingsPath, bundledSettingsPath, defa
|
|
|
83
84
|
settings.theme = "feynman";
|
|
84
85
|
settings.quietStartup = true;
|
|
85
86
|
settings.collapseChangelog = true;
|
|
87
|
+
if (!Array.isArray(settings.packages) || settings.packages.length === 0) {
|
|
88
|
+
settings.packages = [...CORE_PACKAGE_SOURCES];
|
|
89
|
+
}
|
|
90
|
+
else if (shouldPruneLegacyDefaultPackages(settings.packages)) {
|
|
91
|
+
settings.packages = [...CORE_PACKAGE_SOURCES];
|
|
92
|
+
}
|
|
86
93
|
const authStorage = AuthStorage.create(authPath);
|
|
87
94
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
88
95
|
const availableModels = modelRegistry.getAvailable().map((model) => ({
|
package/metadata/commands.mjs
CHANGED
|
@@ -98,6 +98,8 @@ export const cliCommandSections = [
|
|
|
98
98
|
{
|
|
99
99
|
title: "Utilities",
|
|
100
100
|
commands: [
|
|
101
|
+
{ usage: "feynman packages list", description: "Show core and optional Pi package presets." },
|
|
102
|
+
{ usage: "feynman packages install <preset>", description: "Install optional package presets on demand." },
|
|
101
103
|
{ usage: "feynman search status", description: "Show Pi web-access status and config path." },
|
|
102
104
|
{ usage: "feynman update [package]", description: "Update installed packages, or a specific package." },
|
|
103
105
|
],
|
|
@@ -118,7 +120,7 @@ export const legacyFlags = [
|
|
|
118
120
|
{ usage: "--setup-preview", description: "Alias for `feynman setup preview`." },
|
|
119
121
|
];
|
|
120
122
|
|
|
121
|
-
export const topLevelCommandNames = ["alpha", "chat", "doctor", "help", "model", "search", "setup", "status", "update"];
|
|
123
|
+
export const topLevelCommandNames = ["alpha", "chat", "doctor", "help", "model", "packages", "search", "setup", "status", "update"];
|
|
122
124
|
|
|
123
125
|
export function formatSlashUsage(command) {
|
|
124
126
|
return `/${command.name}${command.args ? ` ${command.args}` : ""}`;
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@companion-ai/feynman",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Research-first CLI agent built on Pi and alphaXiv",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20.18.1"
|
|
8
|
+
},
|
|
6
9
|
"bin": {
|
|
7
10
|
"feynman": "bin/feynman.js"
|
|
8
11
|
},
|
|
@@ -57,9 +60,6 @@
|
|
|
57
60
|
"tsx": "^4.21.0",
|
|
58
61
|
"typescript": "^5.9.3"
|
|
59
62
|
},
|
|
60
|
-
"engines": {
|
|
61
|
-
"node": ">=20.18.1"
|
|
62
|
-
},
|
|
63
63
|
"repository": {
|
|
64
64
|
"type": "git",
|
|
65
65
|
"url": "git+https://github.com/getcompanion-ai/feynman.git"
|
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
|
|
6
6
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const appRoot = resolve(here, "..");
|
|
8
|
+
const isGlobalInstall = process.env.npm_config_global === "true" || process.env.npm_config_location === "global";
|
|
8
9
|
|
|
9
10
|
function findNodeModules() {
|
|
10
11
|
let dir = appRoot;
|
|
@@ -53,7 +54,75 @@ const settingsPath = resolve(appRoot, ".feynman", "settings.json");
|
|
|
53
54
|
const workspaceDir = resolve(appRoot, ".feynman", "npm");
|
|
54
55
|
const workspacePackageJsonPath = resolve(workspaceDir, "package.json");
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
function resolveExecutable(name, fallbackPaths = []) {
|
|
58
|
+
for (const candidate of fallbackPaths) {
|
|
59
|
+
if (existsSync(candidate)) return candidate;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const result = spawnSync("sh", ["-lc", `command -v ${name}`], {
|
|
63
|
+
encoding: "utf8",
|
|
64
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
65
|
+
});
|
|
66
|
+
if (result.status === 0) {
|
|
67
|
+
const resolved = result.stdout.trim();
|
|
68
|
+
if (resolved) return resolved;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function ensurePackageWorkspace() {
|
|
74
|
+
if (!existsSync(settingsPath)) return;
|
|
75
|
+
|
|
76
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
77
|
+
const packageSpecs = Array.isArray(settings.packages)
|
|
78
|
+
? settings.packages
|
|
79
|
+
.filter((v) => typeof v === "string" && v.startsWith("npm:"))
|
|
80
|
+
.map((v) => v.slice(4))
|
|
81
|
+
: [];
|
|
82
|
+
|
|
83
|
+
if (packageSpecs.length === 0) return;
|
|
84
|
+
if (existsSync(resolve(workspaceRoot, packageSpecs[0]))) return;
|
|
85
|
+
|
|
86
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
87
|
+
writeFileSync(
|
|
88
|
+
workspacePackageJsonPath,
|
|
89
|
+
JSON.stringify({ name: "feynman-packages", private: true }, null, 2) + "\n",
|
|
90
|
+
"utf8",
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
console.log("[feynman] installing research packages...");
|
|
94
|
+
const result = spawnSync("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund", "--prefix", workspaceDir, ...packageSpecs], {
|
|
95
|
+
stdio: "inherit",
|
|
96
|
+
timeout: 300000,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (result.status !== 0) {
|
|
100
|
+
console.warn("[feynman] warning: package install failed, Pi will retry on first launch");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
ensurePackageWorkspace();
|
|
105
|
+
|
|
106
|
+
function ensurePandoc() {
|
|
107
|
+
if (!isGlobalInstall) return;
|
|
108
|
+
if (process.platform !== "darwin") return;
|
|
109
|
+
if (process.env.FEYNMAN_SKIP_PANDOC_INSTALL === "1") return;
|
|
110
|
+
if (resolveExecutable("pandoc", ["/opt/homebrew/bin/pandoc", "/usr/local/bin/pandoc"])) return;
|
|
111
|
+
|
|
112
|
+
const brewPath = resolveExecutable("brew", ["/opt/homebrew/bin/brew", "/usr/local/bin/brew"]);
|
|
113
|
+
if (!brewPath) return;
|
|
114
|
+
|
|
115
|
+
console.log("[feynman] installing pandoc...");
|
|
116
|
+
const result = spawnSync(brewPath, ["install", "pandoc"], {
|
|
117
|
+
stdio: "inherit",
|
|
118
|
+
timeout: 300000,
|
|
119
|
+
});
|
|
120
|
+
if (result.status !== 0) {
|
|
121
|
+
console.warn("[feynman] warning: pandoc install failed, run `feynman --setup-preview` later");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
ensurePandoc();
|
|
57
126
|
|
|
58
127
|
if (existsSync(packageJsonPath)) {
|
|
59
128
|
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|