Make Feynman a standalone CLI
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
.feynman
|
.feynman
|
||||||
|
dist
|
||||||
outputs/*
|
outputs/*
|
||||||
!outputs/.gitkeep
|
!outputs/.gitkeep
|
||||||
|
|||||||
25
README.md
25
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Feynman
|
# Feynman
|
||||||
|
|
||||||
`feynman` is a research-first agent scaffold built on `@mariozechner/pi-coding-agent`.
|
`feynman` is a research-first CLI built on `@mariozechner/pi-coding-agent`.
|
||||||
|
|
||||||
It keeps the useful parts of a coding agent:
|
It keeps the useful parts of a coding agent:
|
||||||
- file access
|
- file access
|
||||||
@@ -19,7 +19,20 @@ But it biases the runtime toward research work:
|
|||||||
The primary paper backend is `@companion-ai/alpha-hub` and your alphaXiv account.
|
The primary paper backend is `@companion-ai/alpha-hub` and your alphaXiv account.
|
||||||
The rest of the workflow is augmented through a curated `.pi/settings.json` package stack.
|
The rest of the workflow is augmented through a curated `.pi/settings.json` package stack.
|
||||||
|
|
||||||
## Setup
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g @companion-ai/feynman
|
||||||
|
```
|
||||||
|
|
||||||
|
Then authenticate alphaXiv and start the CLI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
feynman --alpha-login
|
||||||
|
feynman
|
||||||
|
```
|
||||||
|
|
||||||
|
For local development:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /Users/advaitpaliwal/Companion/Code/feynman
|
cd /Users/advaitpaliwal/Companion/Code/feynman
|
||||||
@@ -28,13 +41,7 @@ cp .env.example .env
|
|||||||
npm run start
|
npm run start
|
||||||
```
|
```
|
||||||
|
|
||||||
If you already use `pi`, Feynman will reuse the usual auth/config locations for model access.
|
Feynman uses Pi under the hood, but the user-facing entrypoint is `feynman`, not `pi`.
|
||||||
|
|
||||||
Before deep paper work, make sure alphaXiv auth is set up:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx @companion-ai/alpha-hub login
|
|
||||||
```
|
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
|
|||||||
2
bin/feynman.js
Executable file
2
bin/feynman.js
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import "../dist/index.js";
|
||||||
11
package-lock.json
generated
11
package-lock.json
generated
@@ -1,19 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "feynman",
|
"name": "@companion-ai/feynman",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "feynman",
|
"name": "@companion-ai/feynman",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@companion-ai/alpha-hub": "file:../alpha-hub/cli",
|
"@companion-ai/alpha-hub": "^0.1.1",
|
||||||
"@mariozechner/pi-ai": "^0.56.1",
|
"@mariozechner/pi-ai": "^0.56.1",
|
||||||
"@mariozechner/pi-coding-agent": "^0.56.1",
|
"@mariozechner/pi-coding-agent": "^0.56.1",
|
||||||
"@sinclair/typebox": "^0.34.41",
|
"@sinclair/typebox": "^0.34.41",
|
||||||
"dotenv": "^16.4.7"
|
"dotenv": "^16.4.7"
|
||||||
},
|
},
|
||||||
|
"bin": {
|
||||||
|
"feynman": "bin/feynman.js"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
"tsx": "^4.20.5",
|
"tsx": "^4.20.5",
|
||||||
@@ -25,7 +28,7 @@
|
|||||||
},
|
},
|
||||||
"../alpha-hub/cli": {
|
"../alpha-hub/cli": {
|
||||||
"name": "@companion-ai/alpha-hub",
|
"name": "@companion-ai/alpha-hub",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.27.1",
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||||
|
|||||||
30
package.json
30
package.json
@@ -1,12 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "feynman",
|
"name": "@companion-ai/feynman",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"description": "Research-first CLI agent built on Pi and alphaXiv",
|
||||||
"description": "Research-first agent built on pi-coding-agent",
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"bin": {
|
||||||
|
"feynman": "./bin/feynman.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"bin/",
|
||||||
|
"dist/",
|
||||||
|
".pi/",
|
||||||
|
"extensions/",
|
||||||
|
"prompts/",
|
||||||
|
"skills/",
|
||||||
|
"README.md",
|
||||||
|
".env.example"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.build.json",
|
||||||
"dev": "tsx src/index.ts",
|
"dev": "tsx src/index.ts",
|
||||||
"start": "tsx src/index.ts",
|
"start": "tsx src/index.ts",
|
||||||
|
"start:dist": "node ./bin/feynman.js",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -27,7 +41,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@companion-ai/alpha-hub": "^0.1.0",
|
"@companion-ai/alpha-hub": "^0.1.1",
|
||||||
"@mariozechner/pi-ai": "^0.56.1",
|
"@mariozechner/pi-ai": "^0.56.1",
|
||||||
"@mariozechner/pi-coding-agent": "^0.56.1",
|
"@mariozechner/pi-coding-agent": "^0.56.1",
|
||||||
"@sinclair/typebox": "^0.34.41",
|
"@sinclair/typebox": "^0.34.41",
|
||||||
@@ -40,5 +54,13 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.6.0"
|
"node": ">=20.6.0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/getcompanion-ai/feynman.git"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/getcompanion-ai/feynman#readme",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/getcompanion-ai/feynman/issues"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
73
src/index.ts
73
src/index.ts
@@ -7,6 +7,12 @@ import { parseArgs } from "node:util";
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import readline from "node:readline/promises";
|
import readline from "node:readline/promises";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getUserName as getAlphaUserName,
|
||||||
|
isLoggedIn as isAlphaLoggedIn,
|
||||||
|
login as loginAlpha,
|
||||||
|
logout as logoutAlpha,
|
||||||
|
} from "@companion-ai/alpha-hub/lib";
|
||||||
import {
|
import {
|
||||||
AuthStorage,
|
AuthStorage,
|
||||||
createAgentSession,
|
createAgentSession,
|
||||||
@@ -24,6 +30,9 @@ type ThinkingLevel = "off" | "low" | "medium" | "high";
|
|||||||
function printHelp(): void {
|
function printHelp(): void {
|
||||||
console.log(`Feynman commands:
|
console.log(`Feynman commands:
|
||||||
/help Show this help
|
/help Show this help
|
||||||
|
/alpha-login Sign in to alphaXiv
|
||||||
|
/alpha-logout Clear alphaXiv auth
|
||||||
|
/alpha-status Show alphaXiv auth status
|
||||||
/new Start a fresh persisted session
|
/new Start a fresh persisted session
|
||||||
/exit Quit the REPL
|
/exit Quit the REPL
|
||||||
/lit-review <topic> Expand the literature review prompt template
|
/lit-review <topic> Expand the literature review prompt template
|
||||||
@@ -34,6 +43,9 @@ function printHelp(): void {
|
|||||||
|
|
||||||
CLI flags:
|
CLI flags:
|
||||||
--prompt "<text>" Run one prompt and exit
|
--prompt "<text>" Run one prompt and exit
|
||||||
|
--alpha-login Sign in to alphaXiv and exit
|
||||||
|
--alpha-logout Clear alphaXiv auth and exit
|
||||||
|
--alpha-status Show alphaXiv auth status and exit
|
||||||
--model provider:model Force a specific model
|
--model provider:model Force a specific model
|
||||||
--thinking level off | low | medium | high
|
--thinking level off | low | medium | high
|
||||||
--cwd /path/to/workdir Working directory for tools
|
--cwd /path/to/workdir Working directory for tools
|
||||||
@@ -78,6 +90,9 @@ async function main(): Promise<void> {
|
|||||||
options: {
|
options: {
|
||||||
cwd: { type: "string" },
|
cwd: { type: "string" },
|
||||||
help: { type: "boolean" },
|
help: { type: "boolean" },
|
||||||
|
"alpha-login": { type: "boolean" },
|
||||||
|
"alpha-logout": { type: "boolean" },
|
||||||
|
"alpha-status": { type: "boolean" },
|
||||||
model: { type: "string" },
|
model: { type: "string" },
|
||||||
"new-session": { type: "boolean" },
|
"new-session": { type: "boolean" },
|
||||||
prompt: { type: "string" },
|
prompt: { type: "string" },
|
||||||
@@ -91,6 +106,35 @@ async function main(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (values["alpha-login"]) {
|
||||||
|
const result = await loginAlpha();
|
||||||
|
const name =
|
||||||
|
(result.userInfo &&
|
||||||
|
typeof result.userInfo === "object" &&
|
||||||
|
"name" in result.userInfo &&
|
||||||
|
typeof result.userInfo.name === "string")
|
||||||
|
? result.userInfo.name
|
||||||
|
: getAlphaUserName();
|
||||||
|
console.log(name ? `alphaXiv login complete: ${name}` : "alphaXiv login complete");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values["alpha-logout"]) {
|
||||||
|
logoutAlpha();
|
||||||
|
console.log("alphaXiv auth cleared");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values["alpha-status"]) {
|
||||||
|
if (isAlphaLoggedIn()) {
|
||||||
|
const name = getAlphaUserName();
|
||||||
|
console.log(name ? `alphaXiv logged in as ${name}` : "alphaXiv logged in");
|
||||||
|
} else {
|
||||||
|
console.log("alphaXiv not logged in");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const workingDir = resolve(values.cwd ?? process.cwd());
|
const workingDir = resolve(values.cwd ?? process.cwd());
|
||||||
const sessionDir = resolve(values["session-dir"] ?? resolve(appRoot, ".feynman", "sessions"));
|
const sessionDir = resolve(values["session-dir"] ?? resolve(appRoot, ".feynman", "sessions"));
|
||||||
mkdirSync(sessionDir, { recursive: true });
|
mkdirSync(sessionDir, { recursive: true });
|
||||||
@@ -191,6 +235,35 @@ async function main(): Promise<void> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (line === "/alpha-login") {
|
||||||
|
const result = await loginAlpha();
|
||||||
|
const name =
|
||||||
|
(result.userInfo &&
|
||||||
|
typeof result.userInfo === "object" &&
|
||||||
|
"name" in result.userInfo &&
|
||||||
|
typeof result.userInfo.name === "string")
|
||||||
|
? result.userInfo.name
|
||||||
|
: getAlphaUserName();
|
||||||
|
console.log(name ? `alphaXiv login complete: ${name}` : "alphaXiv login complete");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line === "/alpha-logout") {
|
||||||
|
logoutAlpha();
|
||||||
|
console.log("alphaXiv auth cleared");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line === "/alpha-status") {
|
||||||
|
if (isAlphaLoggedIn()) {
|
||||||
|
const name = getAlphaUserName();
|
||||||
|
console.log(name ? `alphaXiv logged in as ${name}` : "alphaXiv logged in");
|
||||||
|
} else {
|
||||||
|
console.log("alphaXiv not logged in");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (line === "/new") {
|
if (line === "/new") {
|
||||||
await session.newSession();
|
await session.newSession();
|
||||||
console.log("started a new session");
|
console.log("started a new session");
|
||||||
|
|||||||
12
tsconfig.build.json
Normal file
12
tsconfig.build.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": false,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"declaration": false,
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user