@companion-ai/feynman 0.5.9 → 0.5.11

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/README.md CHANGED
@@ -10,176 +10,70 @@
10
10
  <a href="https://deepwiki.com/Companion-Inc/feynman"><img alt="Ask DeepWiki" src="https://deepwiki.com/badge.svg" /></a>
11
11
  </p>
12
12
 
13
- ---
13
+ ## Install
14
14
 
15
- ### Installation
16
-
17
- **macOS / Linux:**
15
+ macOS / Linux:
18
16
 
19
17
  ```bash
20
18
  curl -fsSL https://feynman.is/install | bash
21
19
  ```
22
20
 
23
- **Windows (PowerShell):**
21
+ Windows (PowerShell):
24
22
 
25
23
  ```powershell
26
24
  irm https://feynman.is/install.ps1 | iex
27
25
  ```
28
26
 
29
- The one-line installer fetches the latest tagged release as a standalone bundle with its own Node.js runtime and verifies its SHA-256 before installing. To pin a version, pass it explicitly, for example `curl -fsSL https://feynman.is/install | bash -s -- 0.5.3`. Rerun the installer to upgrade; `feynman update` only updates optional Pi packages you installed.
30
-
31
- **npm alternative** (uses your local Node.js `>=22.22.0`):
32
-
33
- ```bash
34
- npm install -g @companion-ai/feynman
35
- ```
36
-
37
- If you installed the interim `@advaitpaliwal/feynman` package, migrate once:
38
-
39
- ```bash
40
- npm uninstall -g @advaitpaliwal/feynman
41
- npm install -g @companion-ai/feynman
42
- ```
43
-
44
- Then run `feynman setup` to sign in to a model provider. To use Feynman in an ACP editor such as Zed, run it through [pi-acp](https://github.com/svkozak/pi-acp): `"agent_servers": { "Feynman": { "command": "npx", "args": ["-y", "pi-acp"], "env": { "PI_ACP_PI_COMMAND": "feynman" } } }`. See the [installation guide](https://feynman.is/docs/getting-started/installation) for uninstalling and the [setup guide](https://feynman.is/docs/getting-started/setup) for local models (LM Studio, LiteLLM, Ollama, vLLM) and Amazon Bedrock.
45
-
46
- ### Skills Only
47
-
48
- If you want just the research skills without the full terminal app:
27
+ Or with npm and Node.js 22.22 or newer: `npm install -g @companion-ai/feynman`.
49
28
 
50
- **macOS / Linux:**
29
+ Then sign in to a model provider:
51
30
 
52
31
  ```bash
53
- curl -fsSL https://feynman.is/install-skills | bash
32
+ feynman setup
54
33
  ```
55
34
 
56
- **Windows (PowerShell):**
35
+ The [installation guide](https://feynman.is/docs/getting-started/installation) covers pinned versions, uninstalling, and the skills-only bundle for Codex and OpenCode. The [setup guide](https://feynman.is/docs/getting-started/setup) covers local models and editors such as Zed.
57
36
 
58
- ```powershell
59
- irm https://feynman.is/install-skills.ps1 | iex
60
- ```
61
-
62
- That installs the skill library into `~/.codex/skills/feynman` for Codex. For other targets, pass a scope:
37
+ ## Use
63
38
 
64
39
  ```bash
65
- curl -fsSL https://feynman.is/install-skills | bash -s -- --codex # ~/.codex/skills/feynman (default)
66
- curl -fsSL https://feynman.is/install-skills | bash -s -- --repo # .agents/skills/feynman in the current repo
67
- curl -fsSL https://feynman.is/install-skills | bash -s -- --opencode # .opencode/skills/feynman in the current repo
68
- ```
69
-
70
- ```powershell
71
- & ([scriptblock]::Create((irm https://feynman.is/install-skills.ps1))) -Scope Codex # or -Scope Repo, -Scope OpenCode
40
+ feynman "what do we know about scaling laws"
41
+ feynman lit "RLHF alternatives"
42
+ feynman deepresearch "mechanistic interpretability"
72
43
  ```
73
44
 
74
- These installers download the bundled `skills/` and `prompts/` trees plus the repo guidance files referenced by those skills. They do not install the Feynman terminal, bundled Node runtime, auth storage, or Pi packages.
75
-
76
- ---
77
-
78
- ### What you type → what happens
79
-
80
- ```
81
- $ feynman "what do we know about scaling laws"
82
- → Searches papers and the web, answers with cited sources
83
-
84
- $ feynman deepresearch "mechanistic interpretability"
85
- → Plan-first investigation with parallel researchers, synthesis, and citation verification
86
-
87
- $ feynman lit "RLHF alternatives"
88
- → Literature review with consensus, disagreements, open questions, and lab/PI corpus mode when the input names a research group
89
-
90
- $ feynman audit 2401.12345
91
- → Compares paper claims against the public codebase
92
-
93
- $ feynman replicate "chain-of-thought improves math"
94
- → Plans replication checks and runs them only after an explicit environment choice
95
-
96
- $ feynman recipe "fine-tune a small model for math reasoning"
97
- → Finds ranked, implementable ML training recipes from papers, datasets, docs, and code
98
- ```
99
-
100
- ---
101
-
102
- ### Workflows
103
-
104
- Ask naturally or use slash commands as shortcuts.
105
-
106
- | Command | What it does |
45
+ | Workflow | What it does |
107
46
  | --- | --- |
108
- | `/deepresearch <topic>` | Source-heavy multi-agent investigation |
109
- | `/lit <topic-or-lab>` | Literature review from paper search and primary sources; lab/PI inputs map publication trajectories and originality-ranked papers |
110
- | `/review <artifact>` | Research review with severity and revision plan |
111
- | `/audit <item>` | Paper vs. codebase mismatch audit |
112
- | `/replicate <paper>` | Plan replication checks; execute only after choosing an environment |
113
- | `/recipe <task-or-paper>` | Ranked ML training recipes with dataset, method, code, and verification status |
47
+ | `/deepresearch <topic>` | Planned investigation with parallel researchers and citation checks |
48
+ | `/lit <topic or lab>` | Literature review: consensus, disagreements, open questions |
49
+ | `/review <paper or draft>` | Critique with severity levels and a revision plan |
50
+ | `/audit <paper>` | Paper claims compared with its public code |
51
+ | `/replicate <paper>` | Replication plan; runs only after you choose an environment |
52
+ | `/recipe <task>` | Ranked ML training recipes with datasets and code |
114
53
  | `/compare <topic>` | Source comparison matrix |
115
54
  | `/draft <topic>` | Paper-style draft from research findings |
116
- | `/autoresearch <idea>` | Bounded experiment loop with benchmark evidence |
117
- | `/summarize <source>` | Summarize a paper, report, repo, or PDF without loading it raw into context |
118
- | `/btw <question>` | Side conversation while the main research agent is busy, with optional handoff back into the main thread |
119
- | `/outputs` | Browse all research artifacts |
120
-
121
- ---
122
-
123
- ### Agents
124
-
125
- Four bundled research agents, invoked by workflow prompts when decomposition helps.
126
-
127
- - **Researcher** — gather evidence across papers, web, repos, docs
128
- - **Reviewer** — internal research critique with severity-graded feedback
129
- - **Writer** — structured drafts from research notes
130
- - **Verifier** — inline citations, source URL verification, dead link cleanup
55
+ | `/autoresearch <idea>` | Bounded experiment loop against a benchmark |
56
+ | `/summarize <source>` | Summary of a paper, repository, or PDF |
131
57
 
132
- ---
58
+ Results are saved to `outputs/` (drafts to `papers/`). `/lit`, `/deepresearch`, and `/recipe` also write a provenance file listing the sources used and what was verified.
133
59
 
134
- ### Skills & Tools
60
+ ## Sources
135
61
 
136
- - **[alphaXiv](https://www.alphaxiv.org/)** — paper search, Q&A, code reading, and annotations (via Feynman's `alpha` tools and `feynman alpha` command)
137
- - **Literature databases** — read-only Semantic Scholar, OpenAlex, arXiv ID lookup, PubMed, Europe PMC full text, bioRxiv/medRxiv, and Crossref, with stable identifiers. Set the free `OPENALEX_API_KEY` ([create one](https://openalex.org/settings/api)) and optionally `SEMANTIC_SCHOLAR_API_KEY` ([request one](https://www.semanticscholar.org/product/api#api-key-form)) to use your own rate limits
138
- - **[Hugging Face Hub](https://huggingface.co/docs/hub/api)** — dataset metadata, split/schema inspection, and small file reads from model, dataset, and Space repos
139
- - **Web research** — search, page fetching, and PDF extraction through [pi-web-access](https://github.com/nicobailon/pi-web-access); Exa works without a key, and `feynman search set` configures Perplexity, Exa, or Gemini
140
- - **Documents** — local PDF and office-document parsing through [pi-docparser](https://github.com/maxedapps/pi-docparser)
141
- - **Compute** — Docker, plus Modal or RunPod when their CLIs are installed, for replication and experiment runs you explicitly approve
62
+ Feynman searches alphaXiv, Semantic Scholar, OpenAlex, arXiv, PubMed, Europe PMC, bioRxiv, medRxiv, Crossref, the web, and Hugging Face, and reads local PDFs and documents. Free `OPENALEX_API_KEY` and `SEMANTIC_SCHOLAR_API_KEY` keys avoid the shared rate limits. It runs on stock [Pi](https://github.com/earendil-works/pi) with four research agents: researcher, verifier, reviewer, and writer.
142
63
 
143
- ---
64
+ ## Telemetry
144
65
 
145
- ### How it works
66
+ Feynman sends anonymous usage data to PostHog: commands, workflows, tools, models, token counts, and errors with their messages and stack traces (your home folder shown as `~`). It never sends prompts, model output, paper content, or tool arguments. Turn it off with `FEYNMAN_TELEMETRY=off`. [What is sent](https://feynman.is/docs/getting-started/configuration#telemetry)
146
67
 
147
- Feynman runs on stock [Pi](https://github.com/earendil-works/pi) (`@earendil-works/pi-coding-agent`). Its prompts, skills, agents, and research tools load as a Pi [package](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/packages.md) alongside the bundled `pi-subagents`, `pi-web-access`, `pi-docparser`, and `pi-btw` packages. Paper search and analysis use [alphaXiv](https://www.alphaxiv.org/). Research claims link to papers, docs, or repos with direct URLs.
148
-
149
- ---
150
-
151
- ### Telemetry
152
-
153
- Feynman sends anonymous usage telemetry to PostHog by default and prints a one-time notice on first run. It sends commands, workflow names and outcomes, tool names, model and provider names, token counts, latency, and error messages with stack traces (home folder shown as `~`) under a random install ID. It never sends prompts, model output, paper content, or tool arguments. Set `FEYNMAN_TELEMETRY=off` (or `DO_NOT_TRACK=1`) to disable it; `feynman status` shows the current setting. The full event list is in the [configuration docs](https://feynman.is/docs/getting-started/configuration#telemetry).
154
-
155
- ---
156
-
157
- ### Star History
158
-
159
- <a href="https://www.star-history.com/?repos=Companion-Inc%2Ffeynman&type=date&legend=top-left">
160
- <picture>
161
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=Companion-Inc/feynman&type=date&theme=dark&legend=top-left" />
162
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=Companion-Inc/feynman&type=date&legend=top-left" />
163
- <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=Companion-Inc/feynman&type=date&legend=top-left" />
164
- </picture>
165
- </a>
166
-
167
- ---
168
-
169
- The bundled Pi packages are pinned and update with Feynman, not through `feynman update`. See the [package stack](https://feynman.is/docs/reference/package-stack) and [release notes](https://feynman.is/docs/reference/releases).
170
-
171
- ### Contributing
172
-
173
- See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contributor guide.
68
+ ## Contributing
174
69
 
175
70
  ```bash
176
71
  git clone https://github.com/Companion-Inc/feynman.git
177
72
  cd feynman
178
- nvm use || nvm install
179
73
  npm install
180
74
  npm test
181
- npm run typecheck
182
- npm run build
183
75
  ```
184
76
 
185
- [Docs](https://feynman.is/docs) · [Release Notes](RELEASES.md) · [MIT License](LICENSE)
77
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
78
+
79
+ [Docs](https://feynman.is/docs) · [Release notes](RELEASES.md) · [MIT License](LICENSE)
package/RELEASES.md CHANGED
@@ -6,6 +6,17 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## v0.5.11 - 2026-09-27
10
+
11
+ - **Crossref lookups no longer fail in bursts.** Crossref's public pool allows one request at a time, one per second, and parallel DOI checks (the verifier cross-checks every citation) got `429 Too Many Requests` on most calls. Crossref requests now run one after another at the pool's pace. Set `CROSSREF_MAILTO` to your email for Crossref's faster polite pool; a 429 now says so instead of a generic "Science database request failed".
12
+ - Telemetry's "wrote output" flag no longer misses a report written in the first second of a run on filesystems that round file times (HFS+, FAT, ext3).
13
+
14
+ ## v0.5.10 - 2026-09-27
15
+
16
+ - **Semantic Scholar understands the documented query syntax.** A `semantic:` prefix and `year_from=`/`year_to=` flags, which the model reuses from the OpenAlex examples, were sent to Semantic Scholar as search text and returned unrelated papers or nothing. They now become Semantic Scholar's own year filter.
17
+ - **The startup header lists only Feynman's workflows.** It also listed prompt commands that pi-subagents ships, such as `/parallel-review` and `/review-loop`.
18
+ - **Cleaner README and landing page** with a current screenshot, and a shorter configuration page.
19
+
9
20
  ## v0.5.9 - 2026-09-27
10
21
 
11
22
  - **Subagents keep their search tools in the foreground.** A `researcher` or `verifier` launched in the foreground failed at once because foreground children never load the parent's extensions, so `web_search`, `fetch_content`, and `feynman_science_database_search` were missing. Feynman now points pi-subagents' `defaultSubagentOnlyExtensions` setting at its research tools and pi-web-access, so every child has them, in the foreground or background.
@@ -6,6 +6,8 @@ import { resolve as resolvePath } from "node:path";
6
6
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
7
7
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
8
8
 
9
+ import { readPromptSpecs } from "../../metadata/commands.mjs";
10
+
9
11
  import {
10
12
  APP_ROOT,
11
13
  FEYNMAN_AGENT_LOGO,
@@ -161,9 +163,11 @@ function detectSystemResources(): SystemResources {
161
163
 
162
164
  type WorkflowInfo = { name: string; description: string };
163
165
 
164
- function getResearchWorkflows(pi: ExtensionAPI): WorkflowInfo[] {
166
+ // Only Feynman's own workflows; other packages (pi-subagents) also ship prompt commands.
167
+ export function getResearchWorkflows(pi: Pick<ExtensionAPI, "getCommands">, appRoot = APP_ROOT): WorkflowInfo[] {
168
+ const feynmanWorkflows = new Set(readPromptSpecs(appRoot).map((spec) => spec.name));
165
169
  return pi.getCommands()
166
- .filter((cmd) => cmd.source === "prompt")
170
+ .filter((cmd) => cmd.source === "prompt" && feynmanWorkflows.has(cmd.name))
167
171
  .map((cmd) => ({ name: `/${cmd.name}`, description: cmd.description ?? "" }))
168
172
  .sort((a, b) => a.name.localeCompare(b.name));
169
173
  }
@@ -137,6 +137,25 @@ function crossrefAuthors(value: unknown): string[] {
137
137
  .slice(0, 8);
138
138
  }
139
139
 
140
+ // Crossref's public pool allows one request at a time, one per second
141
+ // (x-concurrency-limit: 1); the polite pool, used when a mailto is sent, allows
142
+ // three per second. Parallel DOI checks got 429 on most calls, so run Crossref
143
+ // requests one after another at the pool's pace.
144
+ let crossrefQueue: Promise<unknown> = Promise.resolve();
145
+ let crossrefLastStartedAt = Number.NEGATIVE_INFINITY;
146
+
147
+ export function withCrossrefPacing<T>(polite: boolean, run: () => Promise<T>): Promise<T> {
148
+ const minGapMs = polite ? 350 : 1000;
149
+ const next = crossrefQueue.then(async () => {
150
+ const wait = crossrefLastStartedAt + minGapMs - Date.now();
151
+ if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));
152
+ crossrefLastStartedAt = Date.now();
153
+ return run();
154
+ });
155
+ crossrefQueue = next.catch(() => {});
156
+ return next;
157
+ }
158
+
140
159
  async function searchCrossref(params: ScienceDatabaseSearchParams): Promise<Record<string, unknown>> {
141
160
  const query = cleanQuery(params.query);
142
161
  const limit = safeLimit(params.limit);
@@ -148,7 +167,17 @@ async function searchCrossref(params: ScienceDatabaseSearchParams): Promise<Reco
148
167
  select: "DOI,title,published-print,published-online,issued,container-title,author,is-referenced-by-count,URL,type",
149
168
  ...(mailto ? { mailto } : {}),
150
169
  }).toString();
151
- const payload = recordValue(await fetchJson(url));
170
+ let payload: Record<string, unknown>;
171
+ try {
172
+ payload = recordValue(await withCrossrefPacing(Boolean(mailto), () => fetchJson(url)));
173
+ } catch (error) {
174
+ if (!(error instanceof ScienceDatabaseRequestError) || error.status !== 429) throw error;
175
+ throw new Error(
176
+ mailto
177
+ ? "Crossref rate-limited this request (HTTP 429). Wait a few seconds and retry, or look the DOI up with source openalex."
178
+ : "Crossref's public pool rate-limited this request (HTTP 429). Set CROSSREF_MAILTO to your email for Crossref's faster polite pool, wait and retry, or look the DOI up with source openalex.",
179
+ );
180
+ }
152
181
  const message = recordValue(payload.message);
153
182
  const results = arrayValue(message.items).flatMap((item) => {
154
183
  const record = recordValue(item);
@@ -257,12 +286,31 @@ function truncateText(value: string | undefined, maxChars: number): string | und
257
286
  return `${value.slice(0, maxChars).trimEnd()}…`;
258
287
  }
259
288
 
260
- function semanticScholarSearchUrl(query: string, limit: number, sort: string): URL {
289
+ // The model reuses the OpenAlex syntax this tool documents (a `semantic:`
290
+ // prefix, year_from=/year_to= flags). Sent as text, it returned junk or zero
291
+ // results; map it to Semantic Scholar's own year filter instead.
292
+ export function parseSemanticScholarQuery(raw: string): { query: string; year?: string } {
293
+ let from = "";
294
+ let to = "";
295
+ const query = raw
296
+ .replace(/^\s*semantic:\s*/i, "")
297
+ .replace(/\byear_(from|to)=(\d{4})\b/gi, (_match, kind: string, year: string) => {
298
+ if (kind.toLowerCase() === "from") from = year;
299
+ else to = year;
300
+ return " ";
301
+ })
302
+ .replace(/\s+/g, " ")
303
+ .trim();
304
+ return from || to ? { query, year: `${from}-${to}` } : { query };
305
+ }
306
+
307
+ function semanticScholarSearchUrl(query: string, limit: number, sort: string, year?: string): URL {
261
308
  const relevance = sort === "relevance";
262
309
  const url = new URL(`${SEMANTIC_SCHOLAR_BASE}/paper/search${relevance ? "" : "/bulk"}`);
263
310
  url.search = new URLSearchParams({
264
311
  query,
265
312
  fields: SEMANTIC_SCHOLAR_FIELDS,
313
+ ...(year ? { year } : {}),
266
314
  ...(relevance ? { limit: String(limit) } : { sort }),
267
315
  }).toString();
268
316
  return url;
@@ -271,10 +319,10 @@ function semanticScholarSearchUrl(query: string, limit: number, sort: string): U
271
319
  // Bulk search sorted by citation count surfaces seminal papers that relevance
272
320
  // ranking misses; it returns up to 1,000 rows, so only the top `limit` are kept.
273
321
  async function searchSemanticScholar(params: ScienceDatabaseSearchParams): Promise<Record<string, unknown>> {
274
- const query = cleanQuery(params.query);
322
+ const { query, year } = parseSemanticScholarQuery(cleanQuery(params.query));
275
323
  const limit = safeLimit(params.limit);
276
324
  let sort = params.sort === "relevance" ? "relevance" : params.sort === "pub_date" ? "publicationDate:desc" : "citationCount:desc";
277
- const endpoints = [semanticScholarSearchUrl(query, limit, sort)];
325
+ const endpoints = [semanticScholarSearchUrl(query, limit, sort, year)];
278
326
  let note: string | undefined;
279
327
  let payload: Record<string, unknown>;
280
328
  try {
@@ -284,7 +332,7 @@ async function searchSemanticScholar(params: ScienceDatabaseSearchParams): Promi
284
332
  const anonymous = !process.env.SEMANTIC_SCHOLAR_API_KEY?.trim();
285
333
  if (sort !== "relevance" || !anonymous || !(error instanceof ScienceDatabaseRequestError) || error.status !== 429) throw error;
286
334
  sort = "citationCount:desc";
287
- endpoints.push(semanticScholarSearchUrl(query, limit, sort));
335
+ endpoints.push(semanticScholarSearchUrl(query, limit, sort, year));
288
336
  note = `Relevance search was rate-limited (HTTP 429) on Semantic Scholar's shared anonymous pool, so these are citation-sorted bulk results. A free SEMANTIC_SCHOLAR_API_KEY avoids this: ${SEMANTIC_SCHOLAR_KEY_URL}`;
289
337
  payload = recordValue(await fetchSemanticScholar(endpoints[1]!));
290
338
  }
@@ -314,6 +362,7 @@ async function searchSemanticScholar(params: ScienceDatabaseSearchParams): Promi
314
362
  schema: "feynman.scienceDatabaseSearch.v1",
315
363
  source: "semanticscholar",
316
364
  query,
365
+ ...(year ? { year } : {}),
317
366
  sort,
318
367
  ...(note ? { note } : {}),
319
368
  totalCount: numberValue(payload.total) ?? results.length,
@@ -95,6 +95,11 @@ export function workflowName(text: string, workflows: ReadonlySet<string>): stri
95
95
  return name && workflows.has(name) ? name : "chat";
96
96
  }
97
97
 
98
+ // Filesystems stamp modification times from a coarse clock, and some (HFS+,
99
+ // FAT, ext3) round them to whole seconds, so a file written just after the run
100
+ // started can carry an earlier mtime. Allow for that rounding.
101
+ const MTIME_TOLERANCE_MS = 2000;
102
+
98
103
  export function wroteResearchOutput(cwd: string, since: number): boolean {
99
104
  let scanned = 0;
100
105
  for (const dir of OUTPUT_DIRS) {
@@ -108,7 +113,7 @@ export function wroteResearchOutput(cwd: string, since: number): boolean {
108
113
  if (++scanned > OUTPUT_SCAN_LIMIT) return false;
109
114
  try {
110
115
  const stats = statSync(join(cwd, dir, entry));
111
- if (stats.isFile() && stats.mtimeMs >= since) return true;
116
+ if (stats.isFile() && stats.mtimeMs >= since - MTIME_TOLERANCE_MS) return true;
112
117
  } catch {}
113
118
  }
114
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companion-ai/feynman",
3
- "version": "0.5.9",
3
+ "version": "0.5.11",
4
4
  "description": "Research-first CLI agent built on Pi and alphaXiv",
5
5
  "license": "MIT",
6
6
  "type": "module",