Update runtime checks and installer behavior
This commit is contained in:
154
.astro/content.d.ts
vendored
Normal file
154
.astro/content.d.ts
vendored
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
declare module 'astro:content' {
|
||||||
|
export interface RenderResult {
|
||||||
|
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
|
||||||
|
headings: import('astro').MarkdownHeading[];
|
||||||
|
remarkPluginFrontmatter: Record<string, any>;
|
||||||
|
}
|
||||||
|
interface Render {
|
||||||
|
'.md': Promise<RenderResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RenderedContent {
|
||||||
|
html: string;
|
||||||
|
metadata?: {
|
||||||
|
imagePaths: Array<string>;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
|
||||||
|
|
||||||
|
export type CollectionKey = keyof DataEntryMap;
|
||||||
|
export type CollectionEntry<C extends CollectionKey> = Flatten<DataEntryMap[C]>;
|
||||||
|
|
||||||
|
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
|
||||||
|
|
||||||
|
export type ReferenceDataEntry<
|
||||||
|
C extends CollectionKey,
|
||||||
|
E extends keyof DataEntryMap[C] = string,
|
||||||
|
> = {
|
||||||
|
collection: C;
|
||||||
|
id: E;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
|
||||||
|
collection: C;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getCollection<C extends keyof DataEntryMap, E extends CollectionEntry<C>>(
|
||||||
|
collection: C,
|
||||||
|
filter?: (entry: CollectionEntry<C>) => entry is E,
|
||||||
|
): Promise<E[]>;
|
||||||
|
export function getCollection<C extends keyof DataEntryMap>(
|
||||||
|
collection: C,
|
||||||
|
filter?: (entry: CollectionEntry<C>) => unknown,
|
||||||
|
): Promise<CollectionEntry<C>[]>;
|
||||||
|
|
||||||
|
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
|
||||||
|
collection: C,
|
||||||
|
filter?: LiveLoaderCollectionFilterType<C>,
|
||||||
|
): Promise<
|
||||||
|
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export function getEntry<
|
||||||
|
C extends keyof DataEntryMap,
|
||||||
|
E extends keyof DataEntryMap[C] | (string & {}),
|
||||||
|
>(
|
||||||
|
entry: ReferenceDataEntry<C, E>,
|
||||||
|
): E extends keyof DataEntryMap[C]
|
||||||
|
? Promise<DataEntryMap[C][E]>
|
||||||
|
: Promise<CollectionEntry<C> | undefined>;
|
||||||
|
export function getEntry<
|
||||||
|
C extends keyof DataEntryMap,
|
||||||
|
E extends keyof DataEntryMap[C] | (string & {}),
|
||||||
|
>(
|
||||||
|
collection: C,
|
||||||
|
id: E,
|
||||||
|
): E extends keyof DataEntryMap[C]
|
||||||
|
? string extends keyof DataEntryMap[C]
|
||||||
|
? Promise<DataEntryMap[C][E]> | undefined
|
||||||
|
: Promise<DataEntryMap[C][E]>
|
||||||
|
: Promise<CollectionEntry<C> | undefined>;
|
||||||
|
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
|
||||||
|
collection: C,
|
||||||
|
filter: string | LiveLoaderEntryFilterType<C>,
|
||||||
|
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
|
||||||
|
|
||||||
|
/** Resolve an array of entry references from the same collection */
|
||||||
|
export function getEntries<C extends keyof DataEntryMap>(
|
||||||
|
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
|
||||||
|
): Promise<CollectionEntry<C>[]>;
|
||||||
|
|
||||||
|
export function render<C extends keyof DataEntryMap>(
|
||||||
|
entry: DataEntryMap[C][string],
|
||||||
|
): Promise<RenderResult>;
|
||||||
|
|
||||||
|
export function reference<
|
||||||
|
C extends
|
||||||
|
| keyof DataEntryMap
|
||||||
|
// Allow generic `string` to avoid excessive type errors in the config
|
||||||
|
// if `dev` is not running to update as you edit.
|
||||||
|
// Invalid collection names will be caught at build time.
|
||||||
|
| (string & {}),
|
||||||
|
>(
|
||||||
|
collection: C,
|
||||||
|
): import('astro/zod').ZodPipe<
|
||||||
|
import('astro/zod').ZodString,
|
||||||
|
import('astro/zod').ZodTransform<
|
||||||
|
C extends keyof DataEntryMap
|
||||||
|
? {
|
||||||
|
collection: C;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
: never,
|
||||||
|
string
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
|
||||||
|
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
|
||||||
|
type InferEntrySchema<C extends keyof DataEntryMap> = import('astro/zod').infer<
|
||||||
|
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
|
||||||
|
>;
|
||||||
|
type ExtractLoaderConfig<T> = T extends { loader: infer L } ? L : never;
|
||||||
|
type InferLoaderSchema<
|
||||||
|
C extends keyof DataEntryMap,
|
||||||
|
L = ExtractLoaderConfig<ContentConfig['collections'][C]>,
|
||||||
|
> = L extends { schema: import('astro/zod').ZodSchema }
|
||||||
|
? import('astro/zod').infer<L['schema']>
|
||||||
|
: any;
|
||||||
|
|
||||||
|
type DataEntryMap = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
|
||||||
|
infer TData,
|
||||||
|
infer TEntryFilter,
|
||||||
|
infer TCollectionFilter,
|
||||||
|
infer TError
|
||||||
|
>
|
||||||
|
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
|
||||||
|
: { data: never; entryFilter: never; collectionFilter: never; error: never };
|
||||||
|
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
|
||||||
|
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
|
||||||
|
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
|
||||||
|
|
||||||
|
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
|
||||||
|
LiveContentConfig['collections'][C]['schema'] extends undefined
|
||||||
|
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
|
||||||
|
: import('astro/zod').infer<
|
||||||
|
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
|
||||||
|
>;
|
||||||
|
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
|
||||||
|
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
|
||||||
|
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
|
||||||
|
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
|
||||||
|
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
|
||||||
|
LiveContentConfig['collections'][C]['loader']
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ContentConfig = never;
|
||||||
|
export type LiveContentConfig = never;
|
||||||
|
}
|
||||||
2
.astro/types.d.ts
vendored
Normal file
2
.astro/types.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/// <reference types="astro/client" />
|
||||||
|
/// <reference path="content.d.ts" />
|
||||||
@@ -9,7 +9,7 @@ Operating rules:
|
|||||||
- State uncertainty explicitly.
|
- State uncertainty explicitly.
|
||||||
- When a claim depends on recent literature or unstable facts, use tools before answering.
|
- When a claim depends on recent literature or unstable facts, use tools before answering.
|
||||||
- When discussing papers, cite title, year, and identifier or URL when possible.
|
- When discussing papers, cite title, year, and identifier or URL when possible.
|
||||||
- Use the alpha-research skill for academic paper search, paper reading, paper Q&A, repository inspection, and persistent annotations.
|
- Use the `alpha` CLI for academic paper search, paper reading, paper Q&A, repository inspection, and persistent annotations.
|
||||||
- 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.
|
- 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.
|
||||||
- For mixed topics, combine both: use web sources for current reality and paper sources for background literature.
|
- For mixed topics, combine both: use web sources for current reality and paper sources for background literature.
|
||||||
- Never answer a latest/current question from arXiv or alpha-backed paper search alone.
|
- Never answer a latest/current question from arXiv or alpha-backed paper search alone.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"packages": [
|
"packages": [
|
||||||
|
"npm:@companion-ai/alpha-hub",
|
||||||
"npm:pi-subagents",
|
"npm:pi-subagents",
|
||||||
"npm:pi-btw",
|
"npm:pi-btw",
|
||||||
"npm:pi-docparser",
|
"npm:pi-docparser",
|
||||||
|
|||||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -14,3 +14,39 @@ Use this file to track chronology, not release notes. Keep entries short, factua
|
|||||||
- Failed / learned: ...
|
- Failed / learned: ...
|
||||||
- Blockers: ...
|
- Blockers: ...
|
||||||
- Next: ...
|
- Next: ...
|
||||||
|
|
||||||
|
### 2026-03-25 00:00 local — scaling-laws
|
||||||
|
|
||||||
|
- Objective: Set up a deep research workflow for scaling laws.
|
||||||
|
- Changed: Created plan artifact at `outputs/.plans/scaling-laws.md`; defined 4 disjoint researcher dimensions and acceptance criteria.
|
||||||
|
- Verified: Read `CHANGELOG.md` and checked prior memory for related plan `scaling-laws-implications`.
|
||||||
|
- Failed / learned: No prior run-specific changelog entries existed beyond the template.
|
||||||
|
- Blockers: Waiting for user confirmation before launching researcher round 1.
|
||||||
|
- Next: On confirmation, spawn 4 parallel researcher subagents and begin evidence collection.
|
||||||
|
|
||||||
|
### 2026-03-25 00:30 local — scaling-laws (T4 inference/time-scale pass)
|
||||||
|
|
||||||
|
- Objective: Complete T4 on inference/test-time scaling and reasoning-time compute, scoped to 2023–2026.
|
||||||
|
- Changed: Wrote `notes/scaling-laws-research-inference.md`; updated `outputs/.plans/scaling-laws.md` to mark T4 done and log the inference-scaling verification pass.
|
||||||
|
- Verified: Cross-read 13 primary/official sources covering Tree-of-Thoughts, PRMs, repeated sampling, compute-optimal test-time scaling, provable laws, o1, DeepSeek-R1, s1, verifier failures, Anthropic extended thinking, and OpenAI reasoning API docs.
|
||||||
|
- Failed / learned: OpenAI blog fetch for `learning-to-reason-with-llms` returned malformed content, so the note leans on the o1 system card and API docs instead of that blog post.
|
||||||
|
- Blockers: T2 and T5 remain open before final synthesis; no single unified law for inference-time scaling emerged from public sources.
|
||||||
|
- Next: Complete T5 implications synthesis, then reconcile T3/T4 with foundational T2 before drafting the cited brief.
|
||||||
|
|
||||||
|
### 2026-03-25 11:20 local — scaling-laws (T6 draft synthesis)
|
||||||
|
|
||||||
|
- Objective: Synthesize the four research notes into a single user-facing draft brief for the scaling-laws workflow.
|
||||||
|
- Changed: Wrote `outputs/.drafts/scaling-laws-draft.md` with an executive summary, curated reading list, qualitative meta-analysis, core-paper comparison table, explicit training-vs-inference distinction, and numbered inline citations with direct-URL sources.
|
||||||
|
- Verified: Cross-checked the draft against `notes/scaling-laws-research-foundations.md`, `notes/scaling-laws-research-revisions.md`, `notes/scaling-laws-research-inference.md`, and `notes/scaling-laws-research-implications.md` to ensure the brief explicitly states the literature is too heterogeneous for a pooled effect-size estimate.
|
||||||
|
- Failed / learned: The requested temp-run `context.md` and `plan.md` were absent, so the synthesis used `outputs/.plans/scaling-laws.md` plus the four note files as the working context.
|
||||||
|
- Blockers: Citation/claim verification pass still pending; this draft should be treated as pre-verification.
|
||||||
|
- Next: Run verifier/reviewer passes, then promote the draft into the final cited brief and provenance sidecar.
|
||||||
|
|
||||||
|
### 2026-03-25 11:28 local — scaling-laws (final brief + pdf)
|
||||||
|
|
||||||
|
- Objective: Deliver a paper guide and qualitative meta-analysis on AI scaling laws.
|
||||||
|
- Changed: Finalized `outputs/scaling-laws.md` and sidecar `outputs/scaling-laws.provenance.md`; rendered preview PDF at `outputs/scaling-laws.pdf`; updated plan ledger and verification log in `outputs/.plans/scaling-laws.md`.
|
||||||
|
- Verified: Ran a reviewer pass recorded in `notes/scaling-laws-verification.md`; spot-checked key primary papers via alpha-backed reads for Kaplan 2020, Chinchilla 2022, and Snell 2024; confirmed PDF render output exists.
|
||||||
|
- Failed / learned: A pooled statistical meta-analysis would be misleading because the literature mixes heterogeneous outcomes, scaling axes, and evaluation regimes; final deliverable uses a qualitative meta-analysis instead.
|
||||||
|
- Blockers: None for this brief.
|
||||||
|
- Next: If needed, extend into a narrower sub-survey (e.g. only pretraining laws, only inference-time scaling, or only post-Chinchilla data-quality revisions).
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
curl -fsSL https://feynman.is/install | bash
|
curl -fsSL https://feynman.is/install | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you install via `pnpm` or `bun` instead of the standalone bundle, Feynman requires Node.js `20.18.1` or newer.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### What you type → what happens
|
### What you type → what happens
|
||||||
@@ -92,7 +94,10 @@ Built on [Pi](https://github.com/badlogic/pi-mono) for the agent runtime, [alpha
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/getcompanion-ai/feynman.git
|
git clone https://github.com/getcompanion-ai/feynman.git
|
||||||
cd feynman && pnpm install && pnpm start
|
cd feynman
|
||||||
|
nvm use || nvm install
|
||||||
|
pnpm install
|
||||||
|
pnpm start
|
||||||
```
|
```
|
||||||
|
|
||||||
[Docs](https://feynman.is/docs) · [MIT License](LICENSE)
|
[Docs](https://feynman.is/docs) · [MIT License](LICENSE)
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
const v = process.versions.node.split(".").map(Number);
|
const MIN_NODE_VERSION = "20.18.1";
|
||||||
if (v[0] < 20) {
|
|
||||||
console.error(`feynman requires Node.js 20 or later (you have ${process.versions.node})`);
|
function parseNodeVersion(version) {
|
||||||
console.error("upgrade: https://nodejs.org or nvm install 20");
|
const [major = "0", minor = "0", patch = "0"] = version.replace(/^v/, "").split(".");
|
||||||
|
return {
|
||||||
|
major: Number.parseInt(major, 10) || 0,
|
||||||
|
minor: Number.parseInt(minor, 10) || 0,
|
||||||
|
patch: Number.parseInt(patch, 10) || 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareNodeVersions(left, right) {
|
||||||
|
if (left.major !== right.major) return left.major - right.major;
|
||||||
|
if (left.minor !== right.minor) return left.minor - right.minor;
|
||||||
|
return left.patch - right.patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareNodeVersions(parseNodeVersion(process.versions.node), parseNodeVersion(MIN_NODE_VERSION)) < 0) {
|
||||||
|
console.error(`feynman requires Node.js ${MIN_NODE_VERSION} or later (detected ${process.versions.node}).`);
|
||||||
|
console.error("Switch to Node 20 with `nvm install 20 && nvm use 20`, or use the standalone installer:");
|
||||||
|
console.error("curl -fsSL https://feynman.is/install | bash");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
await import("../scripts/patch-embedded-pi.mjs");
|
await import("../scripts/patch-embedded-pi.mjs");
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
".env.example"
|
".env.example"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"preinstall": "node ./scripts/check-node-version.mjs",
|
||||||
"build": "tsc -p tsconfig.build.json",
|
"build": "tsc -p tsconfig.build.json",
|
||||||
"build:native-bundle": "node ./scripts/build-native-bundle.mjs",
|
"build:native-bundle": "node ./scripts/build-native-bundle.mjs",
|
||||||
"dev": "tsx src/index.ts",
|
"dev": "tsx src/index.ts",
|
||||||
|
|||||||
35
scripts/check-node-version.mjs
Normal file
35
scripts/check-node-version.mjs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const MIN_NODE_VERSION = "20.18.1";
|
||||||
|
|
||||||
|
function parseNodeVersion(version) {
|
||||||
|
const [major = "0", minor = "0", patch = "0"] = version.replace(/^v/, "").split(".");
|
||||||
|
return {
|
||||||
|
major: Number.parseInt(major, 10) || 0,
|
||||||
|
minor: Number.parseInt(minor, 10) || 0,
|
||||||
|
patch: Number.parseInt(patch, 10) || 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareNodeVersions(left, right) {
|
||||||
|
if (left.major !== right.major) return left.major - right.major;
|
||||||
|
if (left.minor !== right.minor) return left.minor - right.minor;
|
||||||
|
return left.patch - right.patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSupportedNodeVersion(version = process.versions.node) {
|
||||||
|
return compareNodeVersions(parseNodeVersion(version), parseNodeVersion(MIN_NODE_VERSION)) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnsupportedNodeVersionLines(version = process.versions.node) {
|
||||||
|
return [
|
||||||
|
`feynman requires Node.js ${MIN_NODE_VERSION} or later (detected ${version}).`,
|
||||||
|
"Switch to Node 20 with `nvm install 20 && nvm use 20`, or use the standalone installer:",
|
||||||
|
"curl -fsSL https://feynman.is/install | bash",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSupportedNodeVersion()) {
|
||||||
|
for (const line of getUnsupportedNodeVersionLines()) {
|
||||||
|
console.error(line);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
@@ -146,6 +146,16 @@ Workarounds:
|
|||||||
Write-Host "$installBinDir is already on PATH."
|
Write-Host "$installBinDir is already on PATH."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$resolvedCommand = Get-Command feynman -ErrorAction SilentlyContinue
|
||||||
|
if ($resolvedCommand -and $resolvedCommand.Source -ne $shimPath) {
|
||||||
|
Write-Warning "Current shell resolves feynman to $($resolvedCommand.Source)"
|
||||||
|
Write-Host "Run in a new shell, or run: `$env:Path = '$installBinDir;' + `$env:Path"
|
||||||
|
Write-Host "Then run: feynman"
|
||||||
|
if ($resolvedCommand.Source -like "*node_modules*@companion-ai*feynman*") {
|
||||||
|
Write-Host "If that path is an old global npm install, remove it with: npm uninstall -g @companion-ai/feynman"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "Feynman $resolvedVersion installed successfully."
|
Write-Host "Feynman $resolvedVersion installed successfully."
|
||||||
} finally {
|
} finally {
|
||||||
if (Test-Path $tmpDir) {
|
if (Test-Path $tmpDir) {
|
||||||
|
|||||||
@@ -160,6 +160,27 @@ require_command() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
warn_command_conflict() {
|
||||||
|
expected_path="$INSTALL_BIN_DIR/feynman"
|
||||||
|
resolved_path="$(command -v feynman 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [ -z "$resolved_path" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$resolved_path" != "$expected_path" ]; then
|
||||||
|
step "Warning: current shell resolves feynman to $resolved_path"
|
||||||
|
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && hash -r && feynman"
|
||||||
|
step "Or launch directly: $expected_path"
|
||||||
|
|
||||||
|
case "$resolved_path" in
|
||||||
|
*"/node_modules/@companion-ai/feynman/"* | *"/node_modules/.bin/feynman")
|
||||||
|
step "If that path is an old global npm install, remove it with: npm uninstall -g @companion-ai/feynman"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
resolve_release_metadata() {
|
resolve_release_metadata() {
|
||||||
normalized_version="$(normalize_version "$VERSION")"
|
normalized_version="$(normalize_version "$VERSION")"
|
||||||
|
|
||||||
@@ -290,20 +311,22 @@ add_to_path
|
|||||||
case "$path_action" in
|
case "$path_action" in
|
||||||
added)
|
added)
|
||||||
step "PATH updated for future shells in $path_profile"
|
step "PATH updated for future shells in $path_profile"
|
||||||
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && feynman"
|
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && hash -r && feynman"
|
||||||
;;
|
;;
|
||||||
configured)
|
configured)
|
||||||
step "PATH is already configured for future shells in $path_profile"
|
step "PATH is already configured for future shells in $path_profile"
|
||||||
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && feynman"
|
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && hash -r && feynman"
|
||||||
;;
|
;;
|
||||||
skipped)
|
skipped)
|
||||||
step "PATH update skipped"
|
step "PATH update skipped"
|
||||||
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && feynman"
|
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && hash -r && feynman"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
step "$INSTALL_BIN_DIR is already on PATH"
|
step "$INSTALL_BIN_DIR is already on PATH"
|
||||||
step "Run: feynman"
|
step "Run: hash -r && feynman"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
warn_command_conflict
|
||||||
|
|
||||||
printf 'Feynman %s installed successfully.\n' "$resolved_version"
|
printf 'Feynman %s installed successfully.\n' "$resolved_version"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Use the `alpha` CLI via bash for all paper research operations.
|
|||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| `alpha search "<query>"` | Search papers. Modes: `--mode semantic`, `--mode keyword`, `--mode agentic` |
|
| `alpha search "<query>"` | Search papers. Prefer `--mode semantic` by default; use `--mode keyword` only for exact-term lookup and `--mode agentic` for broader retrieval. |
|
||||||
| `alpha get <arxiv-id-or-url>` | Fetch paper content and any local annotation |
|
| `alpha get <arxiv-id-or-url>` | Fetch paper content and any local annotation |
|
||||||
| `alpha get --full-text <arxiv-id>` | Get raw full text instead of AI report |
|
| `alpha get --full-text <arxiv-id>` | Get raw full text instead of AI report |
|
||||||
| `alpha ask <arxiv-id> "<question>"` | Ask a question about a paper's PDF |
|
| `alpha ask <arxiv-id> "<question>"` | Ask a question about a paper's PDF |
|
||||||
@@ -22,7 +22,7 @@ Use the `alpha` CLI via bash for all paper research operations.
|
|||||||
|
|
||||||
## Auth
|
## Auth
|
||||||
|
|
||||||
Run `alpha login` to authenticate with alphaXiv. Check status with `alpha status`.
|
Run `alpha login` to authenticate with alphaXiv. Check status with `feynman alpha status`, or `alpha status` once your installed `alpha-hub` version includes it.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
10
src/index.ts
10
src/index.ts
@@ -1,6 +1,12 @@
|
|||||||
import { main } from "./cli.js";
|
import { ensureSupportedNodeVersion } from "./system/node-version.js";
|
||||||
|
|
||||||
main().catch((error) => {
|
async function run(): Promise<void> {
|
||||||
|
ensureSupportedNodeVersion();
|
||||||
|
const { main } = await import("./cli.js");
|
||||||
|
await main();
|
||||||
|
}
|
||||||
|
|
||||||
|
run().catch((error) => {
|
||||||
console.error(error instanceof Error ? error.message : String(error));
|
console.error(error instanceof Error ? error.message : String(error));
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import { spawn } from "node:child_process";
|
|||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
|
|
||||||
import { buildPiArgs, buildPiEnv, type PiRuntimeOptions, resolvePiPaths } from "./runtime.js";
|
import { buildPiArgs, buildPiEnv, type PiRuntimeOptions, resolvePiPaths } from "./runtime.js";
|
||||||
|
import { ensureSupportedNodeVersion } from "../system/node-version.js";
|
||||||
|
|
||||||
export async function launchPiChat(options: PiRuntimeOptions): Promise<void> {
|
export async function launchPiChat(options: PiRuntimeOptions): Promise<void> {
|
||||||
|
ensureSupportedNodeVersion();
|
||||||
|
|
||||||
const { piCliPath, promisePolyfillPath } = resolvePiPaths(options.appRoot);
|
const { piCliPath, promisePolyfillPath } = resolvePiPaths(options.appRoot);
|
||||||
if (!existsSync(piCliPath)) {
|
if (!existsSync(piCliPath)) {
|
||||||
throw new Error(`Pi CLI not found: ${piCliPath}`);
|
throw new Error(`Pi CLI not found: ${piCliPath}`);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { PackageSource } from "@mariozechner/pi-coding-agent";
|
import type { PackageSource } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
export const CORE_PACKAGE_SOURCES = [
|
export const CORE_PACKAGE_SOURCES = [
|
||||||
|
"npm:@companion-ai/alpha-hub",
|
||||||
"npm:pi-subagents",
|
"npm:pi-subagents",
|
||||||
"npm:pi-btw",
|
"npm:pi-btw",
|
||||||
"npm:pi-docparser",
|
"npm:pi-docparser",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ export function buildPiEnv(options: PiRuntimeOptions): NodeJS.ProcessEnv {
|
|||||||
const paths = resolvePiPaths(options.appRoot);
|
const paths = resolvePiPaths(options.appRoot);
|
||||||
|
|
||||||
const currentPath = process.env.PATH ?? "";
|
const currentPath = process.env.PATH ?? "";
|
||||||
const binPath = paths.nodeModulesBinPath;
|
const binEntries = [paths.nodeModulesBinPath, resolve(paths.piWorkspaceNodeModulesPath, ".bin")];
|
||||||
|
const binPath = binEntries.join(":");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...process.env,
|
...process.env,
|
||||||
|
|||||||
40
src/system/node-version.ts
Normal file
40
src/system/node-version.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
export const MIN_NODE_VERSION = "20.18.1";
|
||||||
|
|
||||||
|
type ParsedNodeVersion = {
|
||||||
|
major: number;
|
||||||
|
minor: number;
|
||||||
|
patch: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseNodeVersion(version: string): ParsedNodeVersion {
|
||||||
|
const [major = "0", minor = "0", patch = "0"] = version.replace(/^v/, "").split(".");
|
||||||
|
return {
|
||||||
|
major: Number.parseInt(major, 10) || 0,
|
||||||
|
minor: Number.parseInt(minor, 10) || 0,
|
||||||
|
patch: Number.parseInt(patch, 10) || 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareNodeVersions(left: ParsedNodeVersion, right: ParsedNodeVersion): number {
|
||||||
|
if (left.major !== right.major) return left.major - right.major;
|
||||||
|
if (left.minor !== right.minor) return left.minor - right.minor;
|
||||||
|
return left.patch - right.patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSupportedNodeVersion(version = process.versions.node): boolean {
|
||||||
|
return compareNodeVersions(parseNodeVersion(version), parseNodeVersion(MIN_NODE_VERSION)) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUnsupportedNodeVersionLines(version = process.versions.node): string[] {
|
||||||
|
return [
|
||||||
|
`feynman requires Node.js ${MIN_NODE_VERSION} or later (detected ${version}).`,
|
||||||
|
"Switch to Node 20 with `nvm install 20 && nvm use 20`, or use the standalone installer:",
|
||||||
|
"curl -fsSL https://feynman.is/install | bash",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ensureSupportedNodeVersion(version = process.versions.node): void {
|
||||||
|
if (!isSupportedNodeVersion(version)) {
|
||||||
|
throw new Error(getUnsupportedNodeVersionLines(version).join("\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
35
tests/node-version.test.ts
Normal file
35
tests/node-version.test.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import test from "node:test";
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
|
import {
|
||||||
|
MIN_NODE_VERSION,
|
||||||
|
ensureSupportedNodeVersion,
|
||||||
|
getUnsupportedNodeVersionLines,
|
||||||
|
isSupportedNodeVersion,
|
||||||
|
} from "../src/system/node-version.js";
|
||||||
|
|
||||||
|
test("isSupportedNodeVersion enforces the exact minimum floor", () => {
|
||||||
|
assert.equal(isSupportedNodeVersion("20.18.1"), true);
|
||||||
|
assert.equal(isSupportedNodeVersion("20.19.0"), true);
|
||||||
|
assert.equal(isSupportedNodeVersion("21.0.0"), true);
|
||||||
|
assert.equal(isSupportedNodeVersion("20.18.0"), false);
|
||||||
|
assert.equal(isSupportedNodeVersion("18.17.0"), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("ensureSupportedNodeVersion throws a guided upgrade message", () => {
|
||||||
|
assert.throws(
|
||||||
|
() => ensureSupportedNodeVersion("18.17.0"),
|
||||||
|
(error: unknown) =>
|
||||||
|
error instanceof Error &&
|
||||||
|
error.message.includes(`Node.js ${MIN_NODE_VERSION}`) &&
|
||||||
|
error.message.includes("nvm install 20 && nvm use 20") &&
|
||||||
|
error.message.includes("https://feynman.is/install"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("unsupported version guidance reports the detected version", () => {
|
||||||
|
const lines = getUnsupportedNodeVersionLines("18.17.0");
|
||||||
|
|
||||||
|
assert.equal(lines[0], "feynman requires Node.js 20.18.1 or later (detected 18.17.0).");
|
||||||
|
assert.ok(lines.some((line) => line.includes("curl -fsSL https://feynman.is/install | bash")));
|
||||||
|
});
|
||||||
@@ -41,6 +41,7 @@ test("buildPiEnv wires Feynman paths into the Pi environment", () => {
|
|||||||
assert.equal(env.FEYNMAN_SESSION_DIR, "/sessions");
|
assert.equal(env.FEYNMAN_SESSION_DIR, "/sessions");
|
||||||
assert.equal(env.FEYNMAN_BIN_PATH, "/repo/feynman/bin/feynman.js");
|
assert.equal(env.FEYNMAN_BIN_PATH, "/repo/feynman/bin/feynman.js");
|
||||||
assert.equal(env.FEYNMAN_MEMORY_DIR, "/home/.feynman/memory");
|
assert.equal(env.FEYNMAN_MEMORY_DIR, "/home/.feynman/memory");
|
||||||
|
assert.ok(env.PATH?.startsWith("/repo/feynman/node_modules/.bin:/repo/feynman/.feynman/npm/node_modules/.bin:"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("resolvePiPaths includes the Promise.withResolvers polyfill path", () => {
|
test("resolvePiPaths includes the Promise.withResolvers polyfill path", () => {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -160,6 +160,27 @@ require_command() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
warn_command_conflict() {
|
||||||
|
expected_path="$INSTALL_BIN_DIR/feynman"
|
||||||
|
resolved_path="$(command -v feynman 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [ -z "$resolved_path" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$resolved_path" != "$expected_path" ]; then
|
||||||
|
step "Warning: current shell resolves feynman to $resolved_path"
|
||||||
|
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && hash -r && feynman"
|
||||||
|
step "Or launch directly: $expected_path"
|
||||||
|
|
||||||
|
case "$resolved_path" in
|
||||||
|
*"/node_modules/@companion-ai/feynman/"* | *"/node_modules/.bin/feynman")
|
||||||
|
step "If that path is an old global npm install, remove it with: npm uninstall -g @companion-ai/feynman"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
resolve_release_metadata() {
|
resolve_release_metadata() {
|
||||||
normalized_version="$(normalize_version "$VERSION")"
|
normalized_version="$(normalize_version "$VERSION")"
|
||||||
|
|
||||||
@@ -290,20 +311,22 @@ add_to_path
|
|||||||
case "$path_action" in
|
case "$path_action" in
|
||||||
added)
|
added)
|
||||||
step "PATH updated for future shells in $path_profile"
|
step "PATH updated for future shells in $path_profile"
|
||||||
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && feynman"
|
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && hash -r && feynman"
|
||||||
;;
|
;;
|
||||||
configured)
|
configured)
|
||||||
step "PATH is already configured for future shells in $path_profile"
|
step "PATH is already configured for future shells in $path_profile"
|
||||||
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && feynman"
|
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && hash -r && feynman"
|
||||||
;;
|
;;
|
||||||
skipped)
|
skipped)
|
||||||
step "PATH update skipped"
|
step "PATH update skipped"
|
||||||
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && feynman"
|
step "Run now: export PATH=\"$INSTALL_BIN_DIR:\$PATH\" && hash -r && feynman"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
step "$INSTALL_BIN_DIR is already on PATH"
|
step "$INSTALL_BIN_DIR is already on PATH"
|
||||||
step "Run: feynman"
|
step "Run: hash -r && feynman"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
warn_command_conflict
|
||||||
|
|
||||||
printf 'Feynman %s installed successfully.\n' "$resolved_version"
|
printf 'Feynman %s installed successfully.\n' "$resolved_version"
|
||||||
|
|||||||
@@ -146,6 +146,16 @@ Workarounds:
|
|||||||
Write-Host "$installBinDir is already on PATH."
|
Write-Host "$installBinDir is already on PATH."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$resolvedCommand = Get-Command feynman -ErrorAction SilentlyContinue
|
||||||
|
if ($resolvedCommand -and $resolvedCommand.Source -ne $shimPath) {
|
||||||
|
Write-Warning "Current shell resolves feynman to $($resolvedCommand.Source)"
|
||||||
|
Write-Host "Run in a new shell, or run: `$env:Path = '$installBinDir;' + `$env:Path"
|
||||||
|
Write-Host "Then run: feynman"
|
||||||
|
if ($resolvedCommand.Source -like "*node_modules*@companion-ai*feynman*") {
|
||||||
|
Write-Host "If that path is an old global npm install, remove it with: npm uninstall -g @companion-ai/feynman"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "Feynman $resolvedVersion installed successfully."
|
Write-Host "Feynman $resolvedVersion installed successfully."
|
||||||
} finally {
|
} finally {
|
||||||
if (Test-Path $tmpDir) {
|
if (Test-Path $tmpDir) {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ curl -fsSL https://feynman.is/install | bash
|
|||||||
|
|
||||||
The installer detects your OS and architecture automatically. On macOS it supports both Intel and Apple Silicon. On Linux it supports x64 and arm64. The launcher is installed to `~/.local/bin`, the bundled runtime is unpacked into `~/.local/share/feynman`, and your `PATH` is updated when needed.
|
The installer detects your OS and architecture automatically. On macOS it supports both Intel and Apple Silicon. On Linux it supports x64 and arm64. The launcher is installed to `~/.local/bin`, the bundled runtime is unpacked into `~/.local/share/feynman`, and your `PATH` is updated when needed.
|
||||||
|
|
||||||
|
If you previously installed Feynman via `npm`, `pnpm`, or `bun` and still see local Node.js errors after a curl install, your shell is probably still resolving the older global binary first. Run `which -a feynman`, then `hash -r`, or launch the standalone shim directly with `~/.local/bin/feynman`.
|
||||||
|
|
||||||
By default, the one-line installer tracks the rolling `edge` channel from `main`.
|
By default, the one-line installer tracks the rolling `edge` channel from `main`.
|
||||||
|
|
||||||
On **Windows**, open PowerShell as Administrator and run:
|
On **Windows**, open PowerShell as Administrator and run:
|
||||||
@@ -45,7 +47,7 @@ You can also pin an exact version by replacing `stable` with a version such as `
|
|||||||
|
|
||||||
## pnpm
|
## pnpm
|
||||||
|
|
||||||
If you already have Node.js 20.18.1+ installed, you can install Feynman globally via `pnpm`:
|
If you already have Node.js `20.18.1` or newer installed, you can install Feynman globally via `pnpm`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm add -g @companion-ai/feynman
|
pnpm add -g @companion-ai/feynman
|
||||||
@@ -59,6 +61,8 @@ pnpm dlx @companion-ai/feynman
|
|||||||
|
|
||||||
## bun
|
## bun
|
||||||
|
|
||||||
|
`bun add -g` and `bunx` still use your local Node runtime for Feynman itself, so the same Node.js `20.18.1+` requirement applies.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun add -g @companion-ai/feynman
|
bun add -g @companion-ai/feynman
|
||||||
```
|
```
|
||||||
@@ -98,6 +102,7 @@ For contributing or running Feynman from source:
|
|||||||
```bash
|
```bash
|
||||||
git clone https://github.com/getcompanion-ai/feynman.git
|
git clone https://github.com/getcompanion-ai/feynman.git
|
||||||
cd feynman
|
cd feynman
|
||||||
|
nvm use || nvm install
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm start
|
pnpm start
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user