From 7ef1ca2859a9e1469f8d10dd5bfda117452cbec0 Mon Sep 17 00:00:00 2001 From: Advait Paliwal Date: Mon, 23 Mar 2026 22:33:35 -0700 Subject: [PATCH] Add ASCII logo, spinner during install, all packages core, fix tests - ASCII art logo on website hero and OAuth callback page - Clean spinner during postinstall instead of npm noise - pi-session-search and pi-memory moved to core (13 packages) - pi-generative-ui is the only optional package - Alpha Hub callback page branded with Feynman logo Co-Authored-By: Claude Opus 4.6 (1M context) --- .feynman/settings.json | 2 ++ extensions/research-tools/shared.ts | 9 +----- logo.d.mts | 3 ++ logo.mjs | 12 +++++++ package-lock.json | 4 +-- package.json | 2 +- scripts/patch-embedded-pi.mjs | 45 +++++++++++++++++++++++--- src/pi/package-presets.ts | 19 ++--------- tests/pi-settings.test.ts | 6 ++-- website/src/components/AsciiLogo.astro | 26 +++++++++++++++ website/src/components/Nav.astro | 5 ++- website/src/pages/index.astro | 2 ++ 12 files changed, 97 insertions(+), 38 deletions(-) create mode 100644 logo.d.mts create mode 100644 logo.mjs create mode 100644 website/src/components/AsciiLogo.astro diff --git a/.feynman/settings.json b/.feynman/settings.json index fda575b..257d3e8 100644 --- a/.feynman/settings.json +++ b/.feynman/settings.json @@ -9,7 +9,9 @@ "npm:pi-mermaid", "npm:@aliou/pi-processes", "npm:pi-zotero", + "npm:@kaiserlich-dev/pi-session-search", "npm:pi-schedule-prompt", + "npm:@samfp/pi-memory", "npm:@tmustier/pi-ralph-wiggum" ], "quietStartup": true, diff --git a/extensions/research-tools/shared.ts b/extensions/research-tools/shared.ts index f4678c6..2157753 100644 --- a/extensions/research-tools/shared.ts +++ b/extensions/research-tools/shared.ts @@ -14,14 +14,7 @@ export const FEYNMAN_VERSION = (() => { } })(); -export const FEYNMAN_AGENT_LOGO = [ - "███████╗███████╗██╗ ██╗███╗ ██╗███╗ ███╗ █████╗ ███╗ ██╗", - "██╔════╝██╔════╝╚██╗ ██╔╝████╗ ██║████╗ ████║██╔══██╗████╗ ██║", - "█████╗ █████╗ ╚████╔╝ ██╔██╗ ██║██╔████╔██║███████║██╔██╗ ██║", - "██╔══╝ ██╔══╝ ╚██╔╝ ██║╚██╗██║██║╚██╔╝██║██╔══██║██║╚██╗██║", - "██║ ███████╗ ██║ ██║ ╚████║██║ ╚═╝ ██║██║ ██║██║ ╚████║", - "╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝", -]; +export { FEYNMAN_ASCII_LOGO as FEYNMAN_AGENT_LOGO } from "../../logo.mjs"; export const FEYNMAN_RESEARCH_TOOLS = [ "alpha_search", diff --git a/logo.d.mts b/logo.d.mts new file mode 100644 index 0000000..1552567 --- /dev/null +++ b/logo.d.mts @@ -0,0 +1,3 @@ +export declare const FEYNMAN_ASCII_LOGO: string[]; +export declare const FEYNMAN_ASCII_LOGO_TEXT: string; +export declare const FEYNMAN_ASCII_LOGO_HTML: string; diff --git a/logo.mjs b/logo.mjs new file mode 100644 index 0000000..29b9fef --- /dev/null +++ b/logo.mjs @@ -0,0 +1,12 @@ +export const FEYNMAN_ASCII_LOGO = [ + "███████╗███████╗██╗ ██╗███╗ ██╗███╗ ███╗ █████╗ ███╗ ██╗", + "██╔════╝██╔════╝╚██╗ ██╔╝████╗ ██║████╗ ████║██╔══██╗████╗ ██║", + "█████╗ █████╗ ╚████╔╝ ██╔██╗ ██║██╔████╔██║███████║██╔██╗ ██║", + "██╔══╝ ██╔══╝ ╚██╔╝ ██║╚██╗██║██║╚██╔╝██║██╔══██║██║╚██╗██║", + "██║ ███████╗ ██║ ██║ ╚████║██║ ╚═╝ ██║██║ ██║██║ ╚████║", + "╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝", +]; + +export const FEYNMAN_ASCII_LOGO_TEXT = FEYNMAN_ASCII_LOGO.join("\n"); + +export const FEYNMAN_ASCII_LOGO_HTML = `
${FEYNMAN_ASCII_LOGO_TEXT}
`; diff --git a/package-lock.json b/package-lock.json index 3834ec2..1674b5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@companion-ai/feynman", - "version": "0.2.6", + "version": "0.2.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@companion-ai/feynman", - "version": "0.2.6", + "version": "0.2.7", "hasInstallScript": true, "dependencies": { "@companion-ai/alpha-hub": "^0.1.2", diff --git a/package.json b/package.json index b6a3f3b..9ad73d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@companion-ai/feynman", - "version": "0.2.6", + "version": "0.2.7", "description": "Research-first CLI agent built on Pi and alphaXiv", "type": "module", "engines": { diff --git a/scripts/patch-embedded-pi.mjs b/scripts/patch-embedded-pi.mjs index 2d74588..cc0c657 100644 --- a/scripts/patch-embedded-pi.mjs +++ b/scripts/patch-embedded-pi.mjs @@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; +import { FEYNMAN_ASCII_LOGO_HTML } from "../logo.mjs"; const here = dirname(fileURLToPath(import.meta.url)); const appRoot = resolve(here, ".."); @@ -90,14 +91,27 @@ function ensurePackageWorkspace() { "utf8", ); - console.log("[feynman] installing research packages..."); - const result = spawnSync("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund", "--prefix", workspaceDir, ...packageSpecs], { - stdio: "inherit", + const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; + let frame = 0; + const start = Date.now(); + const spinner = setInterval(() => { + const elapsed = Math.round((Date.now() - start) / 1000); + process.stderr.write(`\r${frames[frame++ % frames.length]} setting up feynman... ${elapsed}s`); + }, 80); + + const result = spawnSync("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund", "--loglevel", "error", "--prefix", workspaceDir, ...packageSpecs], { + stdio: ["ignore", "ignore", "pipe"], timeout: 300000, }); + clearInterval(spinner); + const elapsed = Math.round((Date.now() - start) / 1000); + if (result.status !== 0) { - console.warn("[feynman] warning: package install failed, Pi will retry on first launch"); + process.stderr.write(`\r✗ setup failed (${elapsed}s)\n`); + if (result.stderr?.length) process.stderr.write(result.stderr); + } else { + process.stderr.write(`\r✓ feynman ready (${elapsed}s)\n`); } } @@ -351,12 +365,33 @@ if (oauthPagePath && existsSync(oauthPagePath)) { let source = readFileSync(oauthPagePath, "utf8"); const piLogo = 'const LOGO_SVG = ``;'; if (source.includes(piLogo)) { - const feynmanLogo = 'const LOGO_SVG = `feynman`;'; + const feynmanLogo = `const LOGO_SVG = \`${FEYNMAN_ASCII_LOGO_HTML}\`;`; source = source.replace(piLogo, feynmanLogo); writeFileSync(oauthPagePath, source, "utf8"); } } +const alphaHubAuthPath = findPackageRoot("@companion-ai/alpha-hub") + ? resolve(findPackageRoot("@companion-ai/alpha-hub"), "src", "lib", "auth.js") + : null; + +if (alphaHubAuthPath && existsSync(alphaHubAuthPath)) { + let source = readFileSync(alphaHubAuthPath, "utf8"); + const callbackStyle = `style="font-family:system-ui,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:80vh;background:#050a08;color:#f0f5f2"`; + const logoHtml = FEYNMAN_ASCII_LOGO_HTML.replace('color:#10b981', 'color:#34d399'); + const successPage = `${logoHtml}

Logged in

You can close this tab.

`; + const errorPage = `${logoHtml}

Login failed

You can close this tab.

`; + const oldSuccess = `'

Logged in to Alpha Hub

You can close this tab.

'`; + const oldError = `'

Login failed

You can close this tab.

'`; + if (source.includes(oldSuccess)) { + source = source.replace(oldSuccess, `'${successPage}'`); + } + if (source.includes(oldError)) { + source = source.replace(oldError, `'${errorPage}'`); + } + writeFileSync(alphaHubAuthPath, source, "utf8"); +} + if (existsSync(piMemoryPath)) { let source = readFileSync(piMemoryPath, "utf8"); const memoryOriginal = 'const MEMORY_DIR = join(homedir(), ".pi", "memory");'; diff --git a/src/pi/package-presets.ts b/src/pi/package-presets.ts index fa14a95..de37138 100644 --- a/src/pi/package-presets.ts +++ b/src/pi/package-presets.ts @@ -10,7 +10,9 @@ export const CORE_PACKAGE_SOURCES = [ "npm:pi-mermaid", "npm:@aliou/pi-processes", "npm:pi-zotero", + "npm:@kaiserlich-dev/pi-session-search", "npm:pi-schedule-prompt", + "npm:@samfp/pi-memory", "npm:@tmustier/pi-ralph-wiggum", ] as const; @@ -19,25 +21,11 @@ export const OPTIONAL_PACKAGE_PRESETS = { description: "Interactive Glimpse UI widgets.", sources: ["npm:pi-generative-ui"], }, - memory: { - description: "Cross-session memory and preference recall.", - sources: ["npm:@samfp/pi-memory"], - }, - "session-search": { - description: "Indexed session recall with SQLite-backed search.", - sources: ["npm:@kaiserlich-dev/pi-session-search"], - }, - "all-extras": { - description: "Install all optional packages.", - sources: ["npm:pi-generative-ui", "npm:@samfp/pi-memory", "npm:@kaiserlich-dev/pi-session-search"], - }, } as const; const LEGACY_DEFAULT_PACKAGE_SOURCES = [ ...CORE_PACKAGE_SOURCES, "npm:pi-generative-ui", - "npm:@kaiserlich-dev/pi-session-search", - "npm:@samfp/pi-memory", ] as const; export type OptionalPackagePresetName = keyof typeof OPTIONAL_PACKAGE_PRESETS; @@ -66,9 +54,6 @@ export function getOptionalPackagePresetSources(name: string): string[] | undefi if (normalized === "ui") { return [...OPTIONAL_PACKAGE_PRESETS["generative-ui"].sources]; } - if (normalized === "search") { - return [...OPTIONAL_PACKAGE_PRESETS["session-search"].sources]; - } const preset = OPTIONAL_PACKAGE_PRESETS[normalized as OptionalPackagePresetName]; return preset ? [...preset.sources] : undefined; diff --git a/tests/pi-settings.test.ts b/tests/pi-settings.test.ts index 8461e62..f33de48 100644 --- a/tests/pi-settings.test.ts +++ b/tests/pi-settings.test.ts @@ -49,8 +49,6 @@ test("normalizeFeynmanSettings prunes the legacy slow default package set", () = packages: [ ...CORE_PACKAGE_SOURCES, "npm:pi-generative-ui", - "npm:@kaiserlich-dev/pi-session-search", - "npm:@samfp/pi-memory", ], }, null, @@ -68,8 +66,8 @@ test("normalizeFeynmanSettings prunes the legacy slow default package set", () = }); test("optional package presets map friendly aliases", () => { - assert.deepEqual(getOptionalPackagePresetSources("memory"), ["npm:@samfp/pi-memory"]); + assert.deepEqual(getOptionalPackagePresetSources("memory"), undefined); assert.deepEqual(getOptionalPackagePresetSources("ui"), ["npm:pi-generative-ui"]); - assert.deepEqual(getOptionalPackagePresetSources("search"), ["npm:@kaiserlich-dev/pi-session-search"]); + assert.deepEqual(getOptionalPackagePresetSources("search"), undefined); assert.equal(shouldPruneLegacyDefaultPackages(["npm:custom"]), false); }); diff --git a/website/src/components/AsciiLogo.astro b/website/src/components/AsciiLogo.astro new file mode 100644 index 0000000..4eebd0d --- /dev/null +++ b/website/src/components/AsciiLogo.astro @@ -0,0 +1,26 @@ +--- +interface Props { + class?: string; + size?: 'nav' | 'hero'; +} + +const { class: className = '', size = 'hero' } = Astro.props; + +const sizeClasses = size === 'nav' + ? 'text-[4px] sm:text-[5px]' + : 'text-[6px] sm:text-[8px] md:text-[10px]'; +--- + +
███████╗███████╗██╗   ██╗███╗   ██╗███╗   ███╗ █████╗ ███╗   ██╗
+██╔════╝██╔════╝╚██╗ ██╔╝████╗  ██║████╗ ████║██╔══██╗████╗  ██║
+█████╗  █████╗   ╚████╔╝ ██╔██╗ ██║██╔████╔██║███████║██╔██╗ ██║
+██╔══╝  ██╔══╝    ╚██╔╝  ██║╚██╗██║██║╚██╔╝██║██╔══██║██║╚██╗██║
+██║     ███████╗   ██║   ██║ ╚████║██║ ╚═╝ ██║██║  ██║██║ ╚████║
+╚═╝     ╚══════╝   ╚═╝   ╚═╝  ╚═══╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝
diff --git a/website/src/components/Nav.astro b/website/src/components/Nav.astro index 184610a..cdd99d2 100644 --- a/website/src/components/Nav.astro +++ b/website/src/components/Nav.astro @@ -1,5 +1,6 @@ --- import ThemeToggle from './ThemeToggle.astro'; +import AsciiLogo from './AsciiLogo.astro'; interface Props { active?: 'home' | 'docs'; @@ -10,7 +11,9 @@ const { active = 'home' } = Astro.props;