@companion-ai/feynman 0.5.34 → 0.5.36
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/RELEASES.md +17 -0
- package/bin/feynman.js +1 -1
- package/dist/cli.js +20 -12
- package/dist/model/commands.js +14 -18
- package/dist/setup/prompts.js +12 -1
- package/dist/setup/setup.js +13 -6
- package/extensions/research-tools/science-database-pubmed-parsers.ts +1 -1
- package/package.json +4 -4
package/RELEASES.md
CHANGED
|
@@ -6,6 +6,23 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## v0.5.36 - 2026-10-12
|
|
10
|
+
|
|
11
|
+
- **A new user gets to a working model instead of a dead end.** Launching `feynman` with no model opens a menu to set one up.
|
|
12
|
+
- Typing a question there moved the selection onto Cancel: the menu treated `h`, `j`, `k` and `l` as arrow keys. Only the arrow keys move it now.
|
|
13
|
+
- Cancel used to go on to optional packages, an alphaXiv sign-in that defaulted to Yes, and pandoc, then said "Feynman is ready" with no model. It now exits and says how to add a model, and that your question was not sent.
|
|
14
|
+
- The first launch asks only for the model. Packages, alphaXiv and pandoc stay in `feynman setup`, where alphaXiv now defaults to No.
|
|
15
|
+
- Esc in a provider list goes back to the previous menu instead of ending setup.
|
|
16
|
+
- `feynman -p "…"` with no model now says how to add one, instead of pointing at Pi's `/login` and files inside `node_modules`.
|
|
17
|
+
- `feynman help` drops the duplicate "Getting Started" list and calls the flag section "Options".
|
|
18
|
+
|
|
19
|
+
## v0.5.35 - 2026-10-11
|
|
20
|
+
|
|
21
|
+
- **PubMed group authors keep their affiliations.** Consortium and study-group authors came back with no affiliations; they now keep them, as individual authors do. Thanks to @KennyMcSimpson (#335).
|
|
22
|
+
- **pi-subagents 0.77.0.** Subagent tools cost the lead agent far fewer tokens per request (the `subagent` tool's definition shrank from about 18,700 to 3,300 characters), and long subagent reports arrive as a preview plus the file path instead of the full text. Updating Feynman while a session is running no longer breaks every subagent launch in that session. A wrong agent or model name now comes back with suggestions.
|
|
23
|
+
- **pi-btw 0.8.0.** The `/btw` side-chat composer completes slash commands, and side threads no longer fail with Anthropic's "references unknown tool" error.
|
|
24
|
+
- `feynman -v` prints the version, and `--json`, `--work-dir` and `--path` now point to `--mode json` and `--cwd <dir>`.
|
|
25
|
+
|
|
9
26
|
## v0.5.34 - 2026-10-09
|
|
10
27
|
|
|
11
28
|
- **OpenAlex lookups that miss now say why.** Looking up a name or title where OpenAlex expects an ID (for example `openalex_get_work:Attention is all you need` or `author: Geoffrey Hinton`), or an ID that does not exist, returned OpenAlex's raw HTML "404 Not Found" page. The error now says no such work, author or source exists and names the search command to use instead, so the agent recovers on its next call.
|
package/bin/feynman.js
CHANGED
|
@@ -34,7 +34,7 @@ if (compareNodeVersions(parseNodeVersion(process.versions.node), parseNodeVersio
|
|
|
34
34
|
const here = import.meta.dirname;
|
|
35
35
|
|
|
36
36
|
// `--version` needs only package.json; answer it without loading the CLI.
|
|
37
|
-
if (process.argv.length === 3 && process.argv[2] === "--version") {
|
|
37
|
+
if (process.argv.length === 3 && (process.argv[2] === "--version" || process.argv[2] === "-v")) {
|
|
38
38
|
const { readFileSync } = await import("node:fs");
|
|
39
39
|
console.log(JSON.parse(readFileSync(resolve(here, "..", "package.json"), "utf8")).version);
|
|
40
40
|
process.exit(0);
|
package/dist/cli.js
CHANGED
|
@@ -29,6 +29,7 @@ import { fetchLatestFeynmanVersion, getFeynmanUpgradeLines, isNewerVersion } fro
|
|
|
29
29
|
import { runDoctor, runStatus } from "./setup/doctor.js";
|
|
30
30
|
import { setupPreviewDependencies } from "./setup/preview.js";
|
|
31
31
|
import { runSetup } from "./setup/setup.js";
|
|
32
|
+
import { noModelMessage } from "./setup/prompts.js";
|
|
32
33
|
import { captureTelemetryEvent, captureTelemetryException, getCliTelemetryMetadata, initializePostHogTelemetry, shutdownPostHogTelemetry, telemetryErrorProperties, telemetryFirstRunNotice, } from "./telemetry/posthog.js";
|
|
33
34
|
import { ASH, printAsciiHeader, printInfo, printPanel, printSection, RESET, SAGE } from "./ui/terminal.js";
|
|
34
35
|
import { createModelRuntime } from "./model/registry.js";
|
|
@@ -39,6 +40,9 @@ const UNKNOWN_OPTION_HINTS = {
|
|
|
39
40
|
"--setup": "feynman setup",
|
|
40
41
|
"--list-models": "feynman model list",
|
|
41
42
|
"--new": "feynman --new-session",
|
|
43
|
+
"--json": "feynman --mode json",
|
|
44
|
+
"--work-dir": "feynman --cwd <dir>",
|
|
45
|
+
"--path": "feynman --cwd <dir>",
|
|
42
46
|
};
|
|
43
47
|
// Removed commands fail instead of falling through to a chat prompt.
|
|
44
48
|
const REMOVED_COMMANDS = new Set(["jobs", "paper", "rank", "serve", "watch"]);
|
|
@@ -51,15 +55,9 @@ function printHelpLine(usage, description) {
|
|
|
51
55
|
function printHelp(appRoot) {
|
|
52
56
|
const workflowCommands = readPromptSpecs(appRoot).filter((command) => command.section === "Research Workflows" && command.topLevelCli);
|
|
53
57
|
printAsciiHeader([
|
|
54
|
-
"
|
|
58
|
+
"The open source AI research agent.",
|
|
55
59
|
"Use `feynman setup` first if this is a new machine.",
|
|
56
60
|
]);
|
|
57
|
-
printSection("Getting Started");
|
|
58
|
-
printInfo("feynman");
|
|
59
|
-
printInfo("feynman setup");
|
|
60
|
-
printInfo("feynman doctor");
|
|
61
|
-
printInfo("feynman model");
|
|
62
|
-
printInfo("feynman search status");
|
|
63
61
|
printSection("Commands");
|
|
64
62
|
for (const section of cliCommandSections) {
|
|
65
63
|
for (const command of section.commands) {
|
|
@@ -70,7 +68,7 @@ function printHelp(appRoot) {
|
|
|
70
68
|
for (const command of workflowCommands) {
|
|
71
69
|
printHelpLine(formatCliWorkflowUsage(command), command.description);
|
|
72
70
|
}
|
|
73
|
-
printSection("
|
|
71
|
+
printSection("Options");
|
|
74
72
|
for (const flag of legacyFlags) {
|
|
75
73
|
printHelpLine(flag.usage, flag.description);
|
|
76
74
|
}
|
|
@@ -465,7 +463,7 @@ async function runMain(input) {
|
|
|
465
463
|
export: { type: "string" },
|
|
466
464
|
fork: { type: "string" },
|
|
467
465
|
help: { type: "boolean", short: "h" },
|
|
468
|
-
version: { type: "boolean" },
|
|
466
|
+
version: { type: "boolean", short: "v" },
|
|
469
467
|
"alpha-login": { type: "boolean" },
|
|
470
468
|
"alpha-logout": { type: "boolean" },
|
|
471
469
|
"alpha-status": { type: "boolean" },
|
|
@@ -649,6 +647,10 @@ async function runMain(input) {
|
|
|
649
647
|
}
|
|
650
648
|
explicitModelSpec = canonicalModelSpec;
|
|
651
649
|
}
|
|
650
|
+
const workflowCommandNames = new Set(readPromptSpecs(appRoot).filter((s) => s.topLevelCli).map((s) => s.name));
|
|
651
|
+
const workflowRest = appendWorkflowFlagPositionals(command, rest, values);
|
|
652
|
+
const promptOptions = resolvePiPromptOptions(command, workflowRest, values.prompt, workflowCommandNames);
|
|
653
|
+
const promptGiven = Boolean(promptOptions.oneShotPrompt || promptOptions.initialPrompt);
|
|
652
654
|
const currentModelSpec = getCurrentModelSpec(feynmanSettingsPath);
|
|
653
655
|
if (await shouldRunInteractiveSetup(explicitModelSpec, currentModelSpec, Boolean(process.stdin.isTTY && process.stdout.isTTY), feynmanAuthPath)) {
|
|
654
656
|
await runSetup({
|
|
@@ -659,15 +661,21 @@ async function runMain(input) {
|
|
|
659
661
|
sessionDir,
|
|
660
662
|
appRoot,
|
|
661
663
|
defaultThinkingLevel,
|
|
664
|
+
modelOnly: true,
|
|
662
665
|
});
|
|
663
666
|
if (!getCurrentModelSpec(feynmanSettingsPath)) {
|
|
667
|
+
console.error(noModelMessage(promptGiven));
|
|
668
|
+
process.exitCode = 1;
|
|
664
669
|
return;
|
|
665
670
|
}
|
|
666
671
|
await ensureFeynmanSettings(feynmanSettingsPath, bundledSettingsPath, appRoot, defaultThinkingLevel, feynmanAuthPath);
|
|
667
672
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
673
|
+
if (!explicitModelSpec && mode !== "rpc" && !(process.stdin.isTTY && process.stdout.isTTY)
|
|
674
|
+
&& (await getAuthenticatedModelRecords(feynmanAuthPath)).length === 0) {
|
|
675
|
+
console.error(noModelMessage(promptGiven));
|
|
676
|
+
process.exitCode = 1;
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
671
679
|
// Pi's own session flags pass through; Feynman continues the latest
|
|
672
680
|
// session only when none of them is given.
|
|
673
681
|
const piArgs = [
|
package/dist/model/commands.js
CHANGED
|
@@ -4,7 +4,7 @@ import { randomUUID } from "node:crypto";
|
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import { readJson } from "../pi/settings.js";
|
|
7
|
-
import { promptChoice, promptSecret, promptSelect, promptText, validateApiKeyInput } from "../setup/prompts.js";
|
|
7
|
+
import { promptChoice, promptSecret, promptSelect, promptText, SetupCancelledError, validateApiKeyInput } from "../setup/prompts.js";
|
|
8
8
|
import { openUrl } from "../system/open-url.js";
|
|
9
9
|
import { printInfo, printSection, printSuccess, printWarning } from "../ui/terminal.js";
|
|
10
10
|
import { buildModelStatusSnapshotFromRecords, chooseRecommendedModel, getAuthenticatedModelRecords, getAvailableModelRecords, getSupportedModelRecords, isProClassModelSpec, } from "./catalog.js";
|
|
@@ -911,23 +911,19 @@ export async function runModelSetup(settingsPath, authPath) {
|
|
|
911
911
|
"Cancel",
|
|
912
912
|
];
|
|
913
913
|
const selection = await promptChoice("Choose how to configure model access:", choices, 0);
|
|
914
|
-
if (selection ===
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
}
|
|
928
|
-
else {
|
|
929
|
-
printInfo("Setup cancelled.");
|
|
930
|
-
return;
|
|
914
|
+
if (selection === 2)
|
|
915
|
+
throw new SetupCancelledError();
|
|
916
|
+
// Esc inside a provider list goes back to this menu instead of ending setup.
|
|
917
|
+
const done = await (selection === 0
|
|
918
|
+
? loginModelProvider(authPath, undefined, settingsPath)
|
|
919
|
+
: configureApiKeyProvider(authPath)).catch((error) => {
|
|
920
|
+
if (error instanceof SetupCancelledError)
|
|
921
|
+
return false;
|
|
922
|
+
throw error;
|
|
923
|
+
});
|
|
924
|
+
if (!done) {
|
|
925
|
+
status = await collectModelStatus(settingsPath, authPath);
|
|
926
|
+
continue;
|
|
931
927
|
}
|
|
932
928
|
status = await collectModelStatus(settingsPath, authPath);
|
|
933
929
|
if (status.availableModels.length === 0) {
|
package/dist/setup/prompts.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import { confirm as clackConfirm, intro as clackIntro, isCancel, multiselect as clackMultiselect, outro as clackOutro, password as clackPassword, select as clackSelect, text as clackText, } from "@clack/prompts";
|
|
1
|
+
import { confirm as clackConfirm, intro as clackIntro, isCancel, multiselect as clackMultiselect, outro as clackOutro, password as clackPassword, select as clackSelect, text as clackText, settings as clackSettings, } from "@clack/prompts";
|
|
2
|
+
// clack treats h/j/k/l as arrow keys, so a question typed into a setup menu
|
|
3
|
+
// moved the selection (often onto Cancel). Only the arrow keys move it.
|
|
4
|
+
for (const key of ["h", "j", "k", "l"])
|
|
5
|
+
clackSettings.aliases.delete(key);
|
|
2
6
|
export class SetupCancelledError extends Error {
|
|
3
7
|
constructor(message = "setup cancelled") {
|
|
4
8
|
super(message);
|
|
@@ -14,6 +18,13 @@ export function nonInteractiveTerminalMessage(env = process.env, platform = proc
|
|
|
14
18
|
"To skip the prompts, set your provider's API key variable (for example OPENAI_API_KEY or ANTHROPIC_API_KEY) and run `feynman model set <provider/model>`.",
|
|
15
19
|
].join("\n");
|
|
16
20
|
}
|
|
21
|
+
export function noModelMessage(promptGiven = false) {
|
|
22
|
+
return [
|
|
23
|
+
"No model is set up yet, so Feynman can't answer.",
|
|
24
|
+
"Sign in with `feynman model login`, or set ANTHROPIC_API_KEY or OPENAI_API_KEY, then run feynman again.",
|
|
25
|
+
...(promptGiven ? ["Your question was not sent."] : []),
|
|
26
|
+
].join("\n");
|
|
27
|
+
}
|
|
17
28
|
function ensureInteractiveTerminal() {
|
|
18
29
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
19
30
|
throw new Error(nonInteractiveTerminalMessage());
|
package/dist/setup/setup.js
CHANGED
|
@@ -8,7 +8,7 @@ import { buildModelStatusSnapshotFromRecords, getAvailableModelRecords, getSuppo
|
|
|
8
8
|
import { PANDOC_FALLBACK_PATHS, resolveExecutable } from "../system/executables.js";
|
|
9
9
|
import { setupPreviewDependencies } from "./preview.js";
|
|
10
10
|
import { printInfo, printSection, printSuccess } from "../ui/terminal.js";
|
|
11
|
-
import { isInteractiveTerminal, promptConfirm, promptIntro, promptMultiSelect, promptOutro, SetupCancelledError, } from "./prompts.js";
|
|
11
|
+
import { isInteractiveTerminal, noModelMessage, promptConfirm, promptIntro, promptMultiSelect, promptOutro, SetupCancelledError, } from "./prompts.js";
|
|
12
12
|
function printNonInteractiveSetupGuidance() {
|
|
13
13
|
printInfo("Non-interactive terminal. Use explicit commands:");
|
|
14
14
|
printInfo(" feynman model login <provider>");
|
|
@@ -49,7 +49,7 @@ async function maybeLoginAlpha() {
|
|
|
49
49
|
printInfo("alphaXiv already configured.");
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
|
-
const shouldLogin = await promptConfirm("Connect alphaXiv now?",
|
|
52
|
+
const shouldLogin = await promptConfirm("Connect alphaXiv now?", false);
|
|
53
53
|
if (!shouldLogin) {
|
|
54
54
|
printInfo("Skipping alphaXiv login for now.");
|
|
55
55
|
return;
|
|
@@ -88,9 +88,11 @@ export async function runSetup(options) {
|
|
|
88
88
|
try {
|
|
89
89
|
await promptIntro("Feynman setup");
|
|
90
90
|
await runModelSetup(options.settingsPath, options.authPath);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
if (!options.modelOnly) {
|
|
92
|
+
await maybeInstallOptionalPackages(options);
|
|
93
|
+
await maybeLoginAlpha();
|
|
94
|
+
await maybeInstallPreviewDependencies();
|
|
95
|
+
}
|
|
94
96
|
await ensureFeynmanSettings(options.settingsPath, options.bundledSettingsPath, options.appRoot, options.defaultThinkingLevel ?? "medium", options.authPath);
|
|
95
97
|
const modelStatus = buildModelStatusSnapshotFromRecords(await getSupportedModelRecords(options.authPath), await getAvailableModelRecords(options.authPath), getCurrentModelSpec(options.settingsPath));
|
|
96
98
|
printSection("Ready");
|
|
@@ -105,7 +107,12 @@ export async function runSetup(options) {
|
|
|
105
107
|
}
|
|
106
108
|
catch (error) {
|
|
107
109
|
if (error instanceof SetupCancelledError) {
|
|
108
|
-
|
|
110
|
+
// The first launch reports the missing model itself.
|
|
111
|
+
if (!options.modelOnly) {
|
|
112
|
+
printInfo("Setup cancelled.");
|
|
113
|
+
if (!getCurrentModelSpec(options.settingsPath))
|
|
114
|
+
printInfo(noModelMessage());
|
|
115
|
+
}
|
|
109
116
|
return;
|
|
110
117
|
}
|
|
111
118
|
throw error;
|
|
@@ -133,11 +133,11 @@ function parseAuthors(value: unknown): Array<Record<string, unknown>> {
|
|
|
133
133
|
return listValue(value).map((item) => {
|
|
134
134
|
const author = recordValue(item);
|
|
135
135
|
const collective = textValue(author.CollectiveName);
|
|
136
|
-
if (collective) return { collectiveName: collective, affiliations: [] };
|
|
137
136
|
const affiliations = listValue(author.AffiliationInfo)
|
|
138
137
|
.flatMap((info) => listValue(recordValue(info).Affiliation))
|
|
139
138
|
.map((affiliation) => textValue(affiliation))
|
|
140
139
|
.filter((affiliation): affiliation is string => Boolean(affiliation));
|
|
140
|
+
if (collective) return { collectiveName: collective, affiliations };
|
|
141
141
|
return prune({
|
|
142
142
|
lastName: textValue(author.LastName),
|
|
143
143
|
foreName: textValue(author.ForeName),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@companion-ai/feynman",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.36",
|
|
4
|
+
"description": "The open source AI research agent.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
"@clack/prompts": "^1.8.1",
|
|
81
81
|
"@companion-ai/alpha-hub": "0.1.6",
|
|
82
82
|
"fast-xml-parser": "5.11.1",
|
|
83
|
-
"pi-btw": "0.
|
|
83
|
+
"pi-btw": "0.8.0",
|
|
84
84
|
"pi-docparser": "4.0.0",
|
|
85
|
-
"pi-subagents": "0.
|
|
85
|
+
"pi-subagents": "0.77.0",
|
|
86
86
|
"pi-web-access": "0.38.0",
|
|
87
87
|
"posthog-node": "^5.53.0"
|
|
88
88
|
},
|