Overhaul Feynman harness: streamline agents, prompts, and extensions

Remove legacy chains, skills, and config modules. Add citation agent,
SYSTEM.md, modular research-tools extension, and web-access layer.
Add ralph-wiggum to Pi package stack for long-running loops.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Advait Paliwal
2026-03-23 14:59:30 -07:00
parent d23e679331
commit 406d50b3ff
60 changed files with 2994 additions and 3191 deletions

View File

@@ -1,4 +1,4 @@
import { existsSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import {
@@ -18,7 +18,6 @@ export type PiRuntimeOptions = {
explicitModelSpec?: string;
oneShotPrompt?: string;
initialPrompt?: string;
systemPrompt: string;
};
export function resolvePiPaths(appRoot: string) {
@@ -27,8 +26,8 @@ export function resolvePiPaths(appRoot: string) {
piCliPath: resolve(appRoot, "node_modules", "@mariozechner", "pi-coding-agent", "dist", "cli.js"),
promisePolyfillPath: resolve(appRoot, "dist", "system", "promise-polyfill.js"),
researchToolsPath: resolve(appRoot, "extensions", "research-tools.ts"),
skillsPath: resolve(appRoot, "skills"),
promptTemplatePath: resolve(appRoot, "prompts"),
systemPromptPath: resolve(appRoot, ".pi", "SYSTEM.md"),
piWorkspaceNodeModulesPath: resolve(appRoot, ".pi", "npm", "node_modules"),
};
}
@@ -40,7 +39,6 @@ export function validatePiInstallation(appRoot: string): string[] {
if (!existsSync(paths.piCliPath)) missing.push(paths.piCliPath);
if (!existsSync(paths.promisePolyfillPath)) missing.push(paths.promisePolyfillPath);
if (!existsSync(paths.researchToolsPath)) missing.push(paths.researchToolsPath);
if (!existsSync(paths.skillsPath)) missing.push(paths.skillsPath);
if (!existsSync(paths.promptTemplatePath)) missing.push(paths.promptTemplatePath);
return missing;
@@ -53,14 +51,14 @@ export function buildPiArgs(options: PiRuntimeOptions): string[] {
options.sessionDir,
"--extension",
paths.researchToolsPath,
"--skill",
paths.skillsPath,
"--prompt-template",
paths.promptTemplatePath,
"--system-prompt",
options.systemPrompt,
];
if (existsSync(paths.systemPromptPath)) {
args.push("--system-prompt", readFileSync(paths.systemPromptPath, "utf8"));
}
if (options.explicitModelSpec) {
args.push("--model", options.explicitModelSpec);
}
@@ -81,16 +79,13 @@ export function buildPiEnv(options: PiRuntimeOptions): NodeJS.ProcessEnv {
return {
...process.env,
PI_CODING_AGENT_DIR: options.feynmanAgentDir,
FEYNMAN_CODING_AGENT_DIR: options.feynmanAgentDir,
FEYNMAN_VERSION: options.feynmanVersion,
FEYNMAN_PI_NPM_ROOT: paths.piWorkspaceNodeModulesPath,
FEYNMAN_SESSION_DIR: options.sessionDir,
PI_SESSION_DIR: options.sessionDir,
FEYNMAN_MEMORY_DIR: resolve(dirname(options.feynmanAgentDir), "memory"),
FEYNMAN_NODE_EXECUTABLE: process.execPath,
FEYNMAN_BIN_PATH: resolve(options.appRoot, "bin", "feynman.js"),
PANDOC_PATH: process.env.PANDOC_PATH ?? resolveExecutable("pandoc", PANDOC_FALLBACK_PATHS),
PI_HARDWARE_CURSOR: process.env.PI_HARDWARE_CURSOR ?? "1",
PI_SKIP_VERSION_CHECK: process.env.PI_SKIP_VERSION_CHECK ?? "1",
MERMAID_CLI_PATH: process.env.MERMAID_CLI_PATH ?? resolveExecutable("mmdc", MERMAID_FALLBACK_PATHS),
PUPPETEER_EXECUTABLE_PATH:

109
src/pi/web-access.ts Normal file
View File

@@ -0,0 +1,109 @@
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { resolve } from "node:path";
export type PiWebSearchProvider = "auto" | "perplexity" | "gemini";
export type PiWebAccessConfig = Record<string, unknown> & {
provider?: PiWebSearchProvider;
searchProvider?: PiWebSearchProvider;
perplexityApiKey?: string;
geminiApiKey?: string;
chromeProfile?: string;
};
export type PiWebAccessStatus = {
configPath: string;
searchProvider: PiWebSearchProvider;
requestProvider: PiWebSearchProvider;
perplexityConfigured: boolean;
geminiApiConfigured: boolean;
chromeProfile?: string;
routeLabel: string;
note: string;
};
export function getPiWebSearchConfigPath(home = process.env.HOME ?? homedir()): string {
return resolve(home, ".pi", "web-search.json");
}
function normalizeProvider(value: unknown): PiWebSearchProvider | undefined {
return value === "auto" || value === "perplexity" || value === "gemini" ? value : undefined;
}
function normalizeNonEmptyString(value: unknown): string | undefined {
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
}
export function loadPiWebAccessConfig(configPath = getPiWebSearchConfigPath()): PiWebAccessConfig {
if (!existsSync(configPath)) {
return {};
}
try {
const parsed = JSON.parse(readFileSync(configPath, "utf8")) as PiWebAccessConfig;
return parsed && typeof parsed === "object" ? parsed : {};
} catch {
return {};
}
}
function formatRouteLabel(provider: PiWebSearchProvider): string {
switch (provider) {
case "perplexity":
return "Perplexity";
case "gemini":
return "Gemini";
default:
return "Auto";
}
}
function formatRouteNote(provider: PiWebSearchProvider): string {
switch (provider) {
case "perplexity":
return "Pi web-access will use Perplexity for search.";
case "gemini":
return "Pi web-access will use Gemini API or Gemini Browser.";
default:
return "Pi web-access will try Perplexity, then Gemini API, then Gemini Browser.";
}
}
export function getPiWebAccessStatus(
config: PiWebAccessConfig = loadPiWebAccessConfig(),
configPath = getPiWebSearchConfigPath(),
): PiWebAccessStatus {
const searchProvider = normalizeProvider(config.searchProvider) ?? "auto";
const requestProvider = normalizeProvider(config.provider) ?? searchProvider;
const perplexityConfigured = Boolean(normalizeNonEmptyString(config.perplexityApiKey));
const geminiApiConfigured = Boolean(normalizeNonEmptyString(config.geminiApiKey));
const chromeProfile = normalizeNonEmptyString(config.chromeProfile);
const effectiveProvider = searchProvider;
return {
configPath,
searchProvider,
requestProvider,
perplexityConfigured,
geminiApiConfigured,
chromeProfile,
routeLabel: formatRouteLabel(effectiveProvider),
note: formatRouteNote(effectiveProvider),
};
}
export function formatPiWebAccessDoctorLines(
status: PiWebAccessStatus = getPiWebAccessStatus(),
): string[] {
return [
"web access: pi-web-access",
` search route: ${status.routeLabel}`,
` request route: ${status.requestProvider}`,
` perplexity api: ${status.perplexityConfigured ? "configured" : "not configured"}`,
` gemini api: ${status.geminiApiConfigured ? "configured" : "not configured"}`,
` browser profile: ${status.chromeProfile ?? "default Chromium profile"}`,
` config path: ${status.configPath}`,
` note: ${status.note}`,
];
}