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) <noreply@anthropic.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
3
logo.d.mts
Normal file
3
logo.d.mts
Normal file
@@ -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;
|
||||
12
logo.mjs
Normal file
12
logo.mjs
Normal file
@@ -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 = `<pre style="font-size:8px;line-height:1.15;font-weight:700;color:#10b981;font-family:monospace;white-space:pre;margin:0">${FEYNMAN_ASCII_LOGO_TEXT}</pre>`;
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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 = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>`;';
|
||||
if (source.includes(piLogo)) {
|
||||
const feynmanLogo = 'const LOGO_SVG = `<span style="font-size:32px;font-weight:700;color:#10b981;font-family:system-ui,sans-serif;letter-spacing:-0.02em">feynman</span>`;';
|
||||
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 = `<html><body ${callbackStyle}>${logoHtml}<h2 style="color:#34d399;margin-top:24px">Logged in</h2><p style="color:#8aaa9a">You can close this tab.</p></body></html>`;
|
||||
const errorPage = `<html><body ${callbackStyle}>${logoHtml}<h2 style="color:#ef4444;margin-top:24px">Login failed</h2><p style="color:#8aaa9a">You can close this tab.</p></body></html>`;
|
||||
const oldSuccess = `'<html><body><h2>Logged in to Alpha Hub</h2><p>You can close this tab.</p></body></html>'`;
|
||||
const oldError = `'<html><body><h2>Login failed</h2><p>You can close this tab.</p></body></html>'`;
|
||||
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");';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
26
website/src/components/AsciiLogo.astro
Normal file
26
website/src/components/AsciiLogo.astro
Normal file
@@ -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]';
|
||||
---
|
||||
|
||||
<pre
|
||||
class:list={[
|
||||
'font-mono text-accent font-bold leading-[1.15] m-0 p-0 inline-block',
|
||||
sizeClasses,
|
||||
className,
|
||||
]}
|
||||
aria-label="Feynman"
|
||||
>███████╗███████╗██╗ ██╗███╗ ██╗███╗ ███╗ █████╗ ███╗ ██╗
|
||||
██╔════╝██╔════╝╚██╗ ██╔╝████╗ ██║████╗ ████║██╔══██╗████╗ ██║
|
||||
█████╗ █████╗ ╚████╔╝ ██╔██╗ ██║██╔████╔██║███████║██╔██╗ ██║
|
||||
██╔══╝ ██╔══╝ ╚██╔╝ ██║╚██╗██║██║╚██╔╝██║██╔══██║██║╚██╗██║
|
||||
██║ ███████╗ ██║ ██║ ╚████║██║ ╚═╝ ██║██║ ██║██║ ╚████║
|
||||
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝</pre>
|
||||
@@ -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;
|
||||
|
||||
<nav class="sticky top-0 z-50 bg-bg">
|
||||
<div class="max-w-6xl mx-auto px-6 h-14 flex items-center justify-between">
|
||||
<a href="/" class="text-xl font-bold text-accent tracking-tight">Feynman</a>
|
||||
<a href="/" class="hover:opacity-80 transition-opacity" aria-label="Feynman">
|
||||
<AsciiLogo size="nav" />
|
||||
</a>
|
||||
<div class="flex items-center gap-6">
|
||||
<a href="/docs/getting-started/installation"
|
||||
class:list={["text-sm transition-colors", active === 'docs' ? 'text-text-primary' : 'text-text-muted hover:text-text-primary']}>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
import AsciiLogo from '../components/AsciiLogo.astro';
|
||||
---
|
||||
|
||||
<Base title="Feynman — The open source AI research agent" active="home">
|
||||
<section class="text-center pt-24 pb-20 px-6">
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<AsciiLogo size="hero" class="mb-8" />
|
||||
<h1 class="text-5xl sm:text-6xl font-bold tracking-tight mb-6" style="text-wrap: balance">The open source AI research agent</h1>
|
||||
<p class="text-lg text-text-muted mb-10 leading-relaxed" style="text-wrap: pretty">Investigate topics, write papers, run experiments, review research, audit codebases — every output cited and source-grounded</p>
|
||||
<div class="inline-flex items-center gap-3 bg-surface rounded-lg px-5 py-3 mb-8 font-mono text-sm">
|
||||
|
||||
Reference in New Issue
Block a user