@companion-ai/feynman 0.2.0

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.
Files changed (72) hide show
  1. package/.env.example +8 -0
  2. package/.feynman/SYSTEM.md +62 -0
  3. package/.feynman/agents/researcher.md +63 -0
  4. package/.feynman/agents/reviewer.md +84 -0
  5. package/.feynman/agents/verifier.md +38 -0
  6. package/.feynman/agents/writer.md +51 -0
  7. package/.feynman/settings.json +20 -0
  8. package/.feynman/themes/feynman.json +85 -0
  9. package/AGENTS.md +53 -0
  10. package/README.md +99 -0
  11. package/bin/feynman.js +2 -0
  12. package/dist/bootstrap/sync.js +98 -0
  13. package/dist/cli.js +297 -0
  14. package/dist/config/commands.js +71 -0
  15. package/dist/config/feynman-config.js +42 -0
  16. package/dist/config/paths.js +32 -0
  17. package/dist/feynman-prompt.js +63 -0
  18. package/dist/index.js +5 -0
  19. package/dist/model/catalog.js +238 -0
  20. package/dist/model/commands.js +165 -0
  21. package/dist/pi/launch.js +31 -0
  22. package/dist/pi/runtime.js +70 -0
  23. package/dist/pi/settings.js +101 -0
  24. package/dist/pi/web-access.js +74 -0
  25. package/dist/search/commands.js +12 -0
  26. package/dist/setup/doctor.js +126 -0
  27. package/dist/setup/preview.js +20 -0
  28. package/dist/setup/prompts.js +29 -0
  29. package/dist/setup/setup.js +119 -0
  30. package/dist/system/executables.js +38 -0
  31. package/dist/system/promise-polyfill.js +12 -0
  32. package/dist/ui/terminal.js +53 -0
  33. package/dist/web-search.js +1 -0
  34. package/extensions/research-tools/alpha.ts +212 -0
  35. package/extensions/research-tools/header.ts +379 -0
  36. package/extensions/research-tools/help.ts +93 -0
  37. package/extensions/research-tools/preview.ts +233 -0
  38. package/extensions/research-tools/project.ts +116 -0
  39. package/extensions/research-tools/session-search.ts +223 -0
  40. package/extensions/research-tools/shared.ts +46 -0
  41. package/extensions/research-tools.ts +25 -0
  42. package/metadata/commands.d.mts +46 -0
  43. package/metadata/commands.mjs +133 -0
  44. package/package.json +71 -0
  45. package/prompts/audit.md +15 -0
  46. package/prompts/autoresearch.md +63 -0
  47. package/prompts/compare.md +16 -0
  48. package/prompts/deepresearch.md +167 -0
  49. package/prompts/delegate.md +21 -0
  50. package/prompts/draft.md +16 -0
  51. package/prompts/jobs.md +16 -0
  52. package/prompts/lit.md +16 -0
  53. package/prompts/log.md +14 -0
  54. package/prompts/replicate.md +22 -0
  55. package/prompts/review.md +15 -0
  56. package/prompts/watch.md +14 -0
  57. package/scripts/patch-embedded-pi.mjs +319 -0
  58. package/skills/agentcomputer/SKILL.md +108 -0
  59. package/skills/agentcomputer/references/acp-flow.md +23 -0
  60. package/skills/agentcomputer/references/cli-cheatsheet.md +68 -0
  61. package/skills/autoresearch/SKILL.md +12 -0
  62. package/skills/deep-research/SKILL.md +12 -0
  63. package/skills/docker/SKILL.md +84 -0
  64. package/skills/jobs/SKILL.md +10 -0
  65. package/skills/literature-review/SKILL.md +12 -0
  66. package/skills/paper-code-audit/SKILL.md +12 -0
  67. package/skills/paper-writing/SKILL.md +12 -0
  68. package/skills/peer-review/SKILL.md +12 -0
  69. package/skills/replication/SKILL.md +14 -0
  70. package/skills/session-log/SKILL.md +10 -0
  71. package/skills/source-comparison/SKILL.md +12 -0
  72. package/skills/watch/SKILL.md +12 -0
@@ -0,0 +1,98 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { dirname, relative, resolve } from "node:path";
4
+ import { getBootstrapStatePath } from "../config/paths.js";
5
+ function sha256(text) {
6
+ return createHash("sha256").update(text).digest("hex");
7
+ }
8
+ function readBootstrapState(path) {
9
+ if (!existsSync(path)) {
10
+ return { version: 1, files: {} };
11
+ }
12
+ try {
13
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
14
+ return {
15
+ version: 1,
16
+ files: parsed.files && typeof parsed.files === "object" ? parsed.files : {},
17
+ };
18
+ }
19
+ catch {
20
+ return { version: 1, files: {} };
21
+ }
22
+ }
23
+ function writeBootstrapState(path, state) {
24
+ mkdirSync(dirname(path), { recursive: true });
25
+ writeFileSync(path, JSON.stringify(state, null, 2) + "\n", "utf8");
26
+ }
27
+ function listFiles(root) {
28
+ if (!existsSync(root)) {
29
+ return [];
30
+ }
31
+ const files = [];
32
+ for (const entry of readdirSync(root, { withFileTypes: true })) {
33
+ const path = resolve(root, entry.name);
34
+ if (entry.isDirectory()) {
35
+ files.push(...listFiles(path));
36
+ continue;
37
+ }
38
+ if (entry.isFile()) {
39
+ files.push(path);
40
+ }
41
+ }
42
+ return files.sort();
43
+ }
44
+ function syncManagedFiles(sourceRoot, targetRoot, state, result) {
45
+ for (const sourcePath of listFiles(sourceRoot)) {
46
+ const key = relative(sourceRoot, sourcePath);
47
+ const targetPath = resolve(targetRoot, key);
48
+ const sourceText = readFileSync(sourcePath, "utf8");
49
+ const sourceHash = sha256(sourceText);
50
+ const previous = state.files[key];
51
+ mkdirSync(dirname(targetPath), { recursive: true });
52
+ if (!existsSync(targetPath)) {
53
+ writeFileSync(targetPath, sourceText, "utf8");
54
+ state.files[key] = {
55
+ lastAppliedSourceHash: sourceHash,
56
+ lastAppliedTargetHash: sourceHash,
57
+ };
58
+ result.copied.push(key);
59
+ continue;
60
+ }
61
+ const currentTargetText = readFileSync(targetPath, "utf8");
62
+ const currentTargetHash = sha256(currentTargetText);
63
+ if (currentTargetHash === sourceHash) {
64
+ state.files[key] = {
65
+ lastAppliedSourceHash: sourceHash,
66
+ lastAppliedTargetHash: currentTargetHash,
67
+ };
68
+ continue;
69
+ }
70
+ if (!previous) {
71
+ result.skipped.push(key);
72
+ continue;
73
+ }
74
+ if (currentTargetHash !== previous.lastAppliedTargetHash) {
75
+ result.skipped.push(key);
76
+ continue;
77
+ }
78
+ writeFileSync(targetPath, sourceText, "utf8");
79
+ state.files[key] = {
80
+ lastAppliedSourceHash: sourceHash,
81
+ lastAppliedTargetHash: sourceHash,
82
+ };
83
+ result.updated.push(key);
84
+ }
85
+ }
86
+ export function syncBundledAssets(appRoot, agentDir) {
87
+ const statePath = getBootstrapStatePath();
88
+ const state = readBootstrapState(statePath);
89
+ const result = {
90
+ copied: [],
91
+ updated: [],
92
+ skipped: [],
93
+ };
94
+ syncManagedFiles(resolve(appRoot, ".feynman", "themes"), resolve(agentDir, "themes"), state, result);
95
+ syncManagedFiles(resolve(appRoot, ".feynman", "agents"), resolve(agentDir, "agents"), state, result);
96
+ writeBootstrapState(statePath, state);
97
+ return result;
98
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,297 @@
1
+ import "dotenv/config";
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { parseArgs } from "node:util";
5
+ import { fileURLToPath } from "node:url";
6
+ import { getUserName as getAlphaUserName, isLoggedIn as isAlphaLoggedIn, login as loginAlpha, logout as logoutAlpha, } from "@companion-ai/alpha-hub/lib";
7
+ import { AuthStorage, DefaultPackageManager, ModelRegistry, SettingsManager } from "@mariozechner/pi-coding-agent";
8
+ import { syncBundledAssets } from "./bootstrap/sync.js";
9
+ import { ensureFeynmanHome, getDefaultSessionDir, getFeynmanAgentDir, getFeynmanHome } from "./config/paths.js";
10
+ import { launchPiChat } from "./pi/launch.js";
11
+ import { normalizeFeynmanSettings, normalizeThinkingLevel, parseModelSpec } from "./pi/settings.js";
12
+ import { loginModelProvider, logoutModelProvider, printModelList, setDefaultModelSpec, } from "./model/commands.js";
13
+ import { printSearchStatus } from "./search/commands.js";
14
+ import { runDoctor, runStatus } from "./setup/doctor.js";
15
+ import { setupPreviewDependencies } from "./setup/preview.js";
16
+ import { runSetup } from "./setup/setup.js";
17
+ import { printInfo, printPanel, printSection } from "./ui/terminal.js";
18
+ import { cliCommandSections, formatCliWorkflowUsage, legacyFlags, readPromptSpecs, topLevelCommandNames, } from "../metadata/commands.mjs";
19
+ const TOP_LEVEL_COMMANDS = new Set(topLevelCommandNames);
20
+ function printHelpLine(usage, description) {
21
+ const width = 30;
22
+ const padding = Math.max(1, width - usage.length);
23
+ printInfo(`${usage}${" ".repeat(padding)}${description}`);
24
+ }
25
+ function printHelp(appRoot) {
26
+ const workflowCommands = readPromptSpecs(appRoot).filter((command) => command.section === "Research Workflows" && command.topLevelCli);
27
+ printPanel("Feynman", [
28
+ "Research-first agent shell built on Pi.",
29
+ "Use `feynman setup` first if this is a new machine.",
30
+ ]);
31
+ printSection("Getting Started");
32
+ printInfo("feynman");
33
+ printInfo("feynman setup");
34
+ printInfo("feynman doctor");
35
+ printInfo("feynman model");
36
+ printInfo("feynman search status");
37
+ printSection("Commands");
38
+ for (const section of cliCommandSections) {
39
+ for (const command of section.commands) {
40
+ printHelpLine(command.usage, command.description);
41
+ }
42
+ }
43
+ printSection("Research Workflows");
44
+ for (const command of workflowCommands) {
45
+ printHelpLine(formatCliWorkflowUsage(command), command.description);
46
+ }
47
+ printSection("Legacy Flags");
48
+ for (const flag of legacyFlags) {
49
+ printHelpLine(flag.usage, flag.description);
50
+ }
51
+ printSection("REPL");
52
+ printInfo("Inside the REPL, slash workflows come from the live prompt-template and extension command set.");
53
+ }
54
+ async function handleAlphaCommand(action) {
55
+ if (action === "login") {
56
+ const result = await loginAlpha();
57
+ const name = result.userInfo &&
58
+ typeof result.userInfo === "object" &&
59
+ "name" in result.userInfo &&
60
+ typeof result.userInfo.name === "string"
61
+ ? result.userInfo.name
62
+ : getAlphaUserName();
63
+ console.log(name ? `alphaXiv login complete: ${name}` : "alphaXiv login complete");
64
+ return;
65
+ }
66
+ if (action === "logout") {
67
+ logoutAlpha();
68
+ console.log("alphaXiv auth cleared");
69
+ return;
70
+ }
71
+ if (!action || action === "status") {
72
+ if (isAlphaLoggedIn()) {
73
+ const name = getAlphaUserName();
74
+ console.log(name ? `alphaXiv logged in as ${name}` : "alphaXiv logged in");
75
+ }
76
+ else {
77
+ console.log("alphaXiv not logged in");
78
+ }
79
+ return;
80
+ }
81
+ throw new Error(`Unknown alpha command: ${action}`);
82
+ }
83
+ async function handleModelCommand(subcommand, args, feynmanSettingsPath, feynmanAuthPath) {
84
+ if (!subcommand || subcommand === "list") {
85
+ printModelList(feynmanSettingsPath, feynmanAuthPath);
86
+ return;
87
+ }
88
+ if (subcommand === "login") {
89
+ await loginModelProvider(feynmanAuthPath, args[0]);
90
+ return;
91
+ }
92
+ if (subcommand === "logout") {
93
+ await logoutModelProvider(feynmanAuthPath, args[0]);
94
+ return;
95
+ }
96
+ if (subcommand === "set") {
97
+ const spec = args[0];
98
+ if (!spec) {
99
+ throw new Error("Usage: feynman model set <provider/model>");
100
+ }
101
+ setDefaultModelSpec(feynmanSettingsPath, feynmanAuthPath, spec);
102
+ return;
103
+ }
104
+ throw new Error(`Unknown model command: ${subcommand}`);
105
+ }
106
+ async function handleUpdateCommand(workingDir, feynmanAgentDir, source) {
107
+ const settingsManager = SettingsManager.create(workingDir, feynmanAgentDir);
108
+ const packageManager = new DefaultPackageManager({
109
+ cwd: workingDir,
110
+ agentDir: feynmanAgentDir,
111
+ settingsManager,
112
+ });
113
+ packageManager.setProgressCallback((event) => {
114
+ if (event.type === "start") {
115
+ console.log(`Updating ${event.source}...`);
116
+ }
117
+ else if (event.type === "complete") {
118
+ console.log(`Updated ${event.source}`);
119
+ }
120
+ else if (event.type === "error") {
121
+ console.error(`Failed to update ${event.source}: ${event.message ?? "unknown error"}`);
122
+ }
123
+ });
124
+ await packageManager.update(source);
125
+ await settingsManager.flush();
126
+ console.log("All packages up to date.");
127
+ }
128
+ function handleSearchCommand(subcommand) {
129
+ if (!subcommand || subcommand === "status") {
130
+ printSearchStatus();
131
+ return;
132
+ }
133
+ throw new Error(`Unknown search command: ${subcommand}`);
134
+ }
135
+ function loadPackageVersion(appRoot) {
136
+ try {
137
+ return JSON.parse(readFileSync(resolve(appRoot, "package.json"), "utf8"));
138
+ }
139
+ catch {
140
+ return {};
141
+ }
142
+ }
143
+ export function resolveInitialPrompt(command, rest, oneShotPrompt, workflowCommands) {
144
+ if (oneShotPrompt) {
145
+ return oneShotPrompt;
146
+ }
147
+ if (!command) {
148
+ return undefined;
149
+ }
150
+ if (command === "chat") {
151
+ return rest.length > 0 ? rest.join(" ") : undefined;
152
+ }
153
+ if (workflowCommands.has(command)) {
154
+ return [`/${command}`, ...rest].join(" ").trim();
155
+ }
156
+ if (!TOP_LEVEL_COMMANDS.has(command)) {
157
+ return [command, ...rest].join(" ");
158
+ }
159
+ return undefined;
160
+ }
161
+ export async function main() {
162
+ const here = dirname(fileURLToPath(import.meta.url));
163
+ const appRoot = resolve(here, "..");
164
+ const feynmanVersion = loadPackageVersion(appRoot).version;
165
+ const bundledSettingsPath = resolve(appRoot, ".feynman", "settings.json");
166
+ const feynmanHome = getFeynmanHome();
167
+ const feynmanAgentDir = getFeynmanAgentDir(feynmanHome);
168
+ ensureFeynmanHome(feynmanHome);
169
+ syncBundledAssets(appRoot, feynmanAgentDir);
170
+ const { values, positionals } = parseArgs({
171
+ args: process.argv.slice(2),
172
+ allowPositionals: true,
173
+ options: {
174
+ cwd: { type: "string" },
175
+ doctor: { type: "boolean" },
176
+ help: { type: "boolean" },
177
+ "alpha-login": { type: "boolean" },
178
+ "alpha-logout": { type: "boolean" },
179
+ "alpha-status": { type: "boolean" },
180
+ model: { type: "string" },
181
+ "new-session": { type: "boolean" },
182
+ prompt: { type: "string" },
183
+ "session-dir": { type: "string" },
184
+ "setup-preview": { type: "boolean" },
185
+ thinking: { type: "string" },
186
+ },
187
+ });
188
+ if (values.help) {
189
+ printHelp(appRoot);
190
+ return;
191
+ }
192
+ const workingDir = resolve(values.cwd ?? process.cwd());
193
+ const sessionDir = resolve(values["session-dir"] ?? getDefaultSessionDir(feynmanHome));
194
+ const feynmanSettingsPath = resolve(feynmanAgentDir, "settings.json");
195
+ const feynmanAuthPath = resolve(feynmanAgentDir, "auth.json");
196
+ const thinkingLevel = normalizeThinkingLevel(values.thinking ?? process.env.FEYNMAN_THINKING) ?? "medium";
197
+ normalizeFeynmanSettings(feynmanSettingsPath, bundledSettingsPath, thinkingLevel, feynmanAuthPath);
198
+ if (values.doctor) {
199
+ runDoctor({
200
+ settingsPath: feynmanSettingsPath,
201
+ authPath: feynmanAuthPath,
202
+ sessionDir,
203
+ workingDir,
204
+ appRoot,
205
+ });
206
+ return;
207
+ }
208
+ if (values["setup-preview"]) {
209
+ const result = setupPreviewDependencies();
210
+ console.log(result.message);
211
+ return;
212
+ }
213
+ if (values["alpha-login"]) {
214
+ await handleAlphaCommand("login");
215
+ return;
216
+ }
217
+ if (values["alpha-logout"]) {
218
+ await handleAlphaCommand("logout");
219
+ return;
220
+ }
221
+ if (values["alpha-status"]) {
222
+ await handleAlphaCommand("status");
223
+ return;
224
+ }
225
+ const [command, ...rest] = positionals;
226
+ if (command === "help") {
227
+ printHelp(appRoot);
228
+ return;
229
+ }
230
+ if (command === "setup") {
231
+ await runSetup({
232
+ settingsPath: feynmanSettingsPath,
233
+ bundledSettingsPath,
234
+ authPath: feynmanAuthPath,
235
+ workingDir,
236
+ sessionDir,
237
+ appRoot,
238
+ defaultThinkingLevel: thinkingLevel,
239
+ });
240
+ return;
241
+ }
242
+ if (command === "doctor") {
243
+ runDoctor({
244
+ settingsPath: feynmanSettingsPath,
245
+ authPath: feynmanAuthPath,
246
+ sessionDir,
247
+ workingDir,
248
+ appRoot,
249
+ });
250
+ return;
251
+ }
252
+ if (command === "status") {
253
+ runStatus({
254
+ settingsPath: feynmanSettingsPath,
255
+ authPath: feynmanAuthPath,
256
+ sessionDir,
257
+ workingDir,
258
+ appRoot,
259
+ });
260
+ return;
261
+ }
262
+ if (command === "model") {
263
+ await handleModelCommand(rest[0], rest.slice(1), feynmanSettingsPath, feynmanAuthPath);
264
+ return;
265
+ }
266
+ if (command === "search") {
267
+ handleSearchCommand(rest[0]);
268
+ return;
269
+ }
270
+ if (command === "update") {
271
+ await handleUpdateCommand(workingDir, feynmanAgentDir, rest[0]);
272
+ return;
273
+ }
274
+ if (command === "alpha") {
275
+ await handleAlphaCommand(rest[0]);
276
+ return;
277
+ }
278
+ const explicitModelSpec = values.model ?? process.env.FEYNMAN_MODEL;
279
+ if (explicitModelSpec) {
280
+ const modelRegistry = new ModelRegistry(AuthStorage.create(feynmanAuthPath));
281
+ const explicitModel = parseModelSpec(explicitModelSpec, modelRegistry);
282
+ if (!explicitModel) {
283
+ throw new Error(`Unknown model: ${explicitModelSpec}`);
284
+ }
285
+ }
286
+ await launchPiChat({
287
+ appRoot,
288
+ workingDir,
289
+ sessionDir,
290
+ feynmanAgentDir,
291
+ feynmanVersion,
292
+ thinkingLevel,
293
+ explicitModelSpec,
294
+ oneShotPrompt: values.prompt,
295
+ initialPrompt: resolveInitialPrompt(command, rest, values.prompt, new Set(readPromptSpecs(appRoot).filter((s) => s.topLevelCli).map((s) => s.name))),
296
+ });
297
+ }
@@ -0,0 +1,71 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { FEYNMAN_CONFIG_PATH, loadFeynmanConfig, saveFeynmanConfig } from "./feynman-config.js";
4
+ function coerceConfigValue(raw) {
5
+ const trimmed = raw.trim();
6
+ if (trimmed === "true")
7
+ return true;
8
+ if (trimmed === "false")
9
+ return false;
10
+ if (trimmed === "null")
11
+ return null;
12
+ if (trimmed === "")
13
+ return "";
14
+ if (/^-?\d+(\.\d+)?$/.test(trimmed))
15
+ return Number(trimmed);
16
+ try {
17
+ return JSON.parse(trimmed);
18
+ }
19
+ catch {
20
+ return raw;
21
+ }
22
+ }
23
+ function getNestedValue(record, path) {
24
+ return path.split(".").reduce((current, segment) => {
25
+ if (!current || typeof current !== "object") {
26
+ return undefined;
27
+ }
28
+ return current[segment];
29
+ }, record);
30
+ }
31
+ function setNestedValue(record, path, value) {
32
+ const segments = path.split(".");
33
+ let current = record;
34
+ for (const segment of segments.slice(0, -1)) {
35
+ const existing = current[segment];
36
+ if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
37
+ current[segment] = {};
38
+ }
39
+ current = current[segment];
40
+ }
41
+ current[segments[segments.length - 1]] = value;
42
+ }
43
+ export function printConfig() {
44
+ console.log(JSON.stringify(loadFeynmanConfig(), null, 2));
45
+ }
46
+ export function printConfigPath() {
47
+ console.log(FEYNMAN_CONFIG_PATH);
48
+ }
49
+ export function editConfig() {
50
+ if (!existsSync(FEYNMAN_CONFIG_PATH)) {
51
+ saveFeynmanConfig(loadFeynmanConfig());
52
+ }
53
+ const editor = process.env.VISUAL || process.env.EDITOR || "vi";
54
+ const result = spawnSync(editor, [FEYNMAN_CONFIG_PATH], {
55
+ stdio: "inherit",
56
+ });
57
+ if (result.status !== 0) {
58
+ throw new Error(`Failed to open editor: ${editor}`);
59
+ }
60
+ }
61
+ export function printConfigValue(key) {
62
+ const config = loadFeynmanConfig();
63
+ const value = getNestedValue(config, key);
64
+ console.log(typeof value === "string" ? value : JSON.stringify(value, null, 2));
65
+ }
66
+ export function setConfigValue(key, rawValue) {
67
+ const config = loadFeynmanConfig();
68
+ setNestedValue(config, key, coerceConfigValue(rawValue));
69
+ saveFeynmanConfig(config);
70
+ console.log(`Updated ${key}`);
71
+ }
@@ -0,0 +1,42 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { getDefaultSessionDir, getFeynmanConfigPath } from "./paths.js";
4
+ export const FEYNMAN_CONFIG_PATH = getFeynmanConfigPath();
5
+ function readJsonFile(path) {
6
+ if (!existsSync(path)) {
7
+ return undefined;
8
+ }
9
+ try {
10
+ return JSON.parse(readFileSync(path, "utf8"));
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ }
16
+ export function loadFeynmanConfig(configPath = FEYNMAN_CONFIG_PATH) {
17
+ const config = readJsonFile(configPath);
18
+ if (config && typeof config === "object") {
19
+ return {
20
+ version: 1,
21
+ sessionDir: typeof config.sessionDir === "string" && config.sessionDir.trim() ? config.sessionDir : undefined,
22
+ preview: config.preview && typeof config.preview === "object" ? { ...config.preview } : undefined,
23
+ };
24
+ }
25
+ return {
26
+ version: 1,
27
+ sessionDir: getDefaultSessionDir(),
28
+ };
29
+ }
30
+ export function saveFeynmanConfig(config, configPath = FEYNMAN_CONFIG_PATH) {
31
+ mkdirSync(dirname(configPath), { recursive: true });
32
+ writeFileSync(configPath, JSON.stringify({
33
+ version: 1,
34
+ ...(config.sessionDir ? { sessionDir: config.sessionDir } : {}),
35
+ ...(config.preview ? { preview: config.preview } : {}),
36
+ }, null, 2) + "\n", "utf8");
37
+ }
38
+ export function getConfiguredSessionDir(config = loadFeynmanConfig()) {
39
+ return typeof config.sessionDir === "string" && config.sessionDir.trim()
40
+ ? config.sessionDir
41
+ : getDefaultSessionDir();
42
+ }
@@ -0,0 +1,32 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { resolve } from "node:path";
4
+ export function getFeynmanHome() {
5
+ return resolve(process.env.FEYNMAN_HOME ?? homedir(), ".feynman");
6
+ }
7
+ export function getFeynmanAgentDir(home = getFeynmanHome()) {
8
+ return resolve(home, "agent");
9
+ }
10
+ export function getFeynmanMemoryDir(home = getFeynmanHome()) {
11
+ return resolve(home, "memory");
12
+ }
13
+ export function getFeynmanStateDir(home = getFeynmanHome()) {
14
+ return resolve(home, ".state");
15
+ }
16
+ export function getDefaultSessionDir(home = getFeynmanHome()) {
17
+ return resolve(home, "sessions");
18
+ }
19
+ export function getBootstrapStatePath(home = getFeynmanHome()) {
20
+ return resolve(getFeynmanStateDir(home), "bootstrap.json");
21
+ }
22
+ export function ensureFeynmanHome(home = getFeynmanHome()) {
23
+ for (const dir of [
24
+ home,
25
+ getFeynmanAgentDir(home),
26
+ getFeynmanMemoryDir(home),
27
+ getFeynmanStateDir(home),
28
+ getDefaultSessionDir(home),
29
+ ]) {
30
+ mkdirSync(dir, { recursive: true });
31
+ }
32
+ }
@@ -0,0 +1,63 @@
1
+ export function buildFeynmanSystemPrompt() {
2
+ return `You are Feynman, a research-first AI agent.
3
+
4
+ Your job is to investigate questions, read primary sources, compare evidence, design experiments when useful, and produce reproducible written artifacts.
5
+
6
+ Operating rules:
7
+ - Evidence over fluency.
8
+ - Prefer papers, official documentation, datasets, code, and direct experimental results over commentary.
9
+ - Separate observations from inferences.
10
+ - State uncertainty explicitly.
11
+ - When a claim depends on recent literature or unstable facts, use tools before answering.
12
+ - When discussing papers, cite title, year, and identifier or URL when possible.
13
+ - Use the alpha-backed research tools for academic paper search, paper reading, paper Q&A, repository inspection, and persistent annotations.
14
+ - Use \`web_search\`, \`fetch_content\`, and \`get_search_content\` first for current topics: products, companies, markets, regulations, software releases, model availability, model pricing, benchmarks, docs, or anything phrased as latest/current/recent/today.
15
+ - For mixed topics, combine both: use web sources for current reality and paper sources for background literature.
16
+ - Never answer a latest/current question from arXiv or alpha-backed paper search alone.
17
+ - For AI model or product claims, prefer official docs/vendor pages plus recent web sources over old papers.
18
+ - Use the installed Pi research packages for broader web/PDF access, document parsing, citation workflows, background processes, memory, session recall, and delegated subtasks when they reduce friction.
19
+ - Feynman ships project subagents for research work. Prefer the \`researcher\`, \`verifier\`, \`reviewer\`, and \`writer\` subagents for larger research tasks when decomposition clearly helps.
20
+ - Use subagents when decomposition meaningfully reduces context pressure or lets you parallelize evidence gathering. For detached long-running work, prefer background subagent execution with \`clarify: false, async: true\`.
21
+ - For deep research, act like a lead researcher by default: plan first, use hidden worker batches only when breadth justifies them, synthesize batch results, and finish with a verification/citation pass.
22
+ - Do not force chain-shaped orchestration onto the user. Multi-agent decomposition is an internal tactic, not the primary UX.
23
+ - For AI research artifacts, default to pressure-testing the work before polishing it. Use review-style workflows to check novelty positioning, evaluation design, baseline fairness, ablations, reproducibility, and likely reviewer objections.
24
+ - Use the visualization packages when a chart, diagram, or interactive widget would materially improve understanding. Prefer charts for quantitative comparisons, Mermaid for simple process/architecture diagrams, and interactive HTML widgets for exploratory visual explanations.
25
+ - Persistent memory is package-backed. Use \`memory_search\` to recall prior preferences and lessons, \`memory_remember\` to store explicit durable facts, and \`memory_lessons\` when prior corrections matter.
26
+ - If the user says "remember", states a stable preference, or asks for something to be the default in future sessions, call \`memory_remember\`. Do not just say you will remember it.
27
+ - Session recall is package-backed. Use \`session_search\` when the user references prior work, asks what has been done before, or when you suspect relevant past context exists.
28
+ - Feynman is intended to support always-on research work. Use the scheduling package when recurring or deferred work is appropriate instead of telling the user to remember manually.
29
+ - Use \`schedule_prompt\` for recurring scans, delayed follow-ups, reminders, and periodic research jobs.
30
+ - If the user asks you to remind, check later, run something nightly, or keep watching something over time, call \`schedule_prompt\`. Do not just promise to do it later.
31
+ - For long-running local work such as experiments, crawls, or log-following, use the process package instead of blocking the main thread unnecessarily. Prefer detached/background execution when the user does not need to steer every intermediate step.
32
+ - Prefer the smallest investigation or experiment that can materially reduce uncertainty before escalating to broader work.
33
+ - When an experiment is warranted, write the code or scripts, run them, capture outputs, and save artifacts to disk.
34
+ - Treat polished scientific communication as part of the job: structure reports cleanly, use Markdown deliberately, and use LaTeX math when equations clarify the argument.
35
+ - For any source-based answer, include an explicit Sources section with direct URLs, not just paper titles.
36
+ - When citing papers from alpha-backed tools, prefer direct arXiv or alphaXiv links and include the arXiv ID.
37
+ - After writing a polished artifact, use \`preview_file\` only when the user wants review or export. Prefer browser preview by default; use PDF only when explicitly requested.
38
+ - Default toward delivering a concrete artifact when the task naturally calls for one: reading list, memo, audit, experiment log, or draft.
39
+ - For user-facing workflows, produce exactly one canonical durable Markdown artifact unless the user explicitly asks for multiple deliverables.
40
+ - Do not create extra user-facing intermediate markdown files just because the workflow has multiple reasoning stages.
41
+ - Treat HTML/PDF preview outputs as temporary render artifacts, not as the canonical saved result.
42
+ - Strong default AI-research artifacts include: related-work map, peer-review simulation, ablation plan, reproducibility audit, and rebuttal matrix.
43
+ - Default artifact locations:
44
+ - outputs/ for reviews, reading lists, and summaries
45
+ - experiments/ for runnable experiment code and result logs
46
+ - notes/ for scratch notes and intermediate synthesis
47
+ - papers/ for polished paper-style drafts and writeups
48
+ - Default deliverables should include: summary, strongest evidence, disagreements or gaps, open questions, recommended next steps, and links to the source material.
49
+
50
+ Default workflow:
51
+ 1. Clarify the research objective if needed.
52
+ 2. Search for relevant primary sources.
53
+ 3. Inspect the most relevant papers or materials directly.
54
+ 4. Synthesize consensus, disagreements, and missing evidence.
55
+ 5. Design and run experiments when they would resolve uncertainty.
56
+ 6. Write the requested output artifact.
57
+
58
+ Style:
59
+ - Concise, skeptical, and explicit.
60
+ - Avoid fake certainty.
61
+ - Do not present unverified claims as facts.
62
+ - When greeting, introducing yourself, or answering "who are you", identify yourself explicitly as Feynman.`;
63
+ }
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { main } from "./cli.js";
2
+ main().catch((error) => {
3
+ console.error(error instanceof Error ? error.message : String(error));
4
+ process.exitCode = 1;
5
+ });