@companion-ai/feynman 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ import { printSearchStatus } from "./search/commands.js";
15
15
  import { runDoctor, runStatus } from "./setup/doctor.js";
16
16
  import { setupPreviewDependencies } from "./setup/preview.js";
17
17
  import { runSetup } from "./setup/setup.js";
18
- import { printInfo, printPanel, printSection } from "./ui/terminal.js";
18
+ import { printAsciiHeader, printInfo, printPanel, printSection } from "./ui/terminal.js";
19
19
  import { cliCommandSections, formatCliWorkflowUsage, legacyFlags, readPromptSpecs, topLevelCommandNames, } from "../metadata/commands.mjs";
20
20
  const TOP_LEVEL_COMMANDS = new Set(topLevelCommandNames);
21
21
  function printHelpLine(usage, description) {
@@ -25,7 +25,7 @@ function printHelpLine(usage, description) {
25
25
  }
26
26
  function printHelp(appRoot) {
27
27
  const workflowCommands = readPromptSpecs(appRoot).filter((command) => command.section === "Research Workflows" && command.topLevelCli);
28
- printPanel("Feynman", [
28
+ printAsciiHeader([
29
29
  "Research-first agent shell built on Pi.",
30
30
  "Use `feynman setup` first if this is a new machine.",
31
31
  ]);
@@ -87,7 +87,7 @@ async function handleModelCommand(subcommand, args, feynmanSettingsPath, feynman
87
87
  return;
88
88
  }
89
89
  if (subcommand === "login") {
90
- await loginModelProvider(feynmanAuthPath, args[0]);
90
+ await loginModelProvider(feynmanAuthPath, args[0], feynmanSettingsPath);
91
91
  return;
92
92
  }
93
93
  if (subcommand === "logout") {
@@ -3,7 +3,7 @@ import { writeFileSync } from "node:fs";
3
3
  import { readJson } from "../pi/settings.js";
4
4
  import { promptChoice, promptText } from "../setup/prompts.js";
5
5
  import { printInfo, printSection, printSuccess, printWarning } from "../ui/terminal.js";
6
- import { buildModelStatusSnapshotFromRecords, getAvailableModelRecords, getSupportedModelRecords, } from "./catalog.js";
6
+ import { buildModelStatusSnapshotFromRecords, chooseRecommendedModel, getAvailableModelRecords, getSupportedModelRecords, } from "./catalog.js";
7
7
  function collectModelStatus(settingsPath, authPath) {
8
8
  return buildModelStatusSnapshotFromRecords(getSupportedModelRecords(authPath), getAvailableModelRecords(authPath), getCurrentModelSpec(settingsPath));
9
9
  }
@@ -80,7 +80,7 @@ export function printModelList(settingsPath, authPath) {
80
80
  printInfo(`${spec}${markers.length > 0 ? ` (${markers.join(", ")})` : ""}`);
81
81
  }
82
82
  }
83
- export async function loginModelProvider(authPath, providerId) {
83
+ export async function loginModelProvider(authPath, providerId, settingsPath) {
84
84
  const provider = providerId ? resolveOAuthProvider(authPath, providerId) : await selectOAuthProvider(authPath, "login");
85
85
  if (!provider) {
86
86
  if (providerId) {
@@ -111,6 +111,19 @@ export async function loginModelProvider(authPath, providerId) {
111
111
  signal: abortController.signal,
112
112
  });
113
113
  printSuccess(`Model provider login complete: ${provider.id}`);
114
+ if (settingsPath) {
115
+ const currentSpec = getCurrentModelSpec(settingsPath);
116
+ const available = getAvailableModelRecords(authPath);
117
+ const currentValid = currentSpec
118
+ ? available.some((m) => `${m.provider}/${m.id}` === currentSpec)
119
+ : false;
120
+ if ((!currentSpec || !currentValid) && available.length > 0) {
121
+ const recommended = chooseRecommendedModel(authPath);
122
+ if (recommended) {
123
+ setDefaultModelSpec(settingsPath, authPath, recommended.spec);
124
+ }
125
+ }
126
+ }
114
127
  }
115
128
  export async function logoutModelProvider(authPath, providerId) {
116
129
  const provider = providerId ? resolveOAuthProvider(authPath, providerId) : await selectOAuthProvider(authPath, "logout");
@@ -1,3 +1,4 @@
1
+ import { FEYNMAN_ASCII_LOGO } from "../../logo.mjs";
1
2
  const RESET = "\x1b[0m";
2
3
  const BOLD = "\x1b[1m";
3
4
  const DIM = "\x1b[2m";
@@ -31,6 +32,16 @@ export function printSection(title) {
31
32
  console.log("");
32
33
  console.log(paint(`◆ ${title}`, TEAL, BOLD));
33
34
  }
35
+ export function printAsciiHeader(subtitleLines = []) {
36
+ console.log("");
37
+ for (const line of FEYNMAN_ASCII_LOGO) {
38
+ console.log(paint(` ${line}`, TEAL, BOLD));
39
+ }
40
+ for (const line of subtitleLines) {
41
+ console.log(paint(` ${line}`, ASH));
42
+ }
43
+ console.log("");
44
+ }
34
45
  export function printPanel(title, subtitleLines = []) {
35
46
  const inner = 53;
36
47
  const border = "─".repeat(inner + 2);
@@ -230,8 +230,10 @@ export function installFeynmanHeader(
230
230
 
231
231
  push("");
232
232
  if (cardW >= 70) {
233
+ const maxLogoW = Math.max(...FEYNMAN_AGENT_LOGO.map((l) => l.length));
234
+ const logoOffset = " ".repeat(Math.max(0, Math.floor((cardW - maxLogoW) / 2)));
233
235
  for (const logoLine of FEYNMAN_AGENT_LOGO) {
234
- push(theme.fg("accent", theme.bold(centerText(truncateVisible(logoLine, cardW), cardW))));
236
+ push(theme.fg("accent", theme.bold(`${logoOffset}${truncateVisible(logoLine, cardW)}`)));
235
237
  }
236
238
  push("");
237
239
  }
package/logo.d.mts CHANGED
@@ -1,3 +1,3 @@
1
1
  export declare const FEYNMAN_ASCII_LOGO: string[];
2
2
  export declare const FEYNMAN_ASCII_LOGO_TEXT: string;
3
- export declare const FEYNMAN_ASCII_LOGO_HTML: string;
3
+ export declare const FEYNMAN_LOGO_HTML: string;
package/logo.mjs CHANGED
@@ -1,12 +1,15 @@
1
1
  export const FEYNMAN_ASCII_LOGO = [
2
- "███████╗███████╗██╗ ██╗███╗ ██╗███╗ ███╗ █████╗ ███╗ ██╗",
3
- "██╔════╝██╔════╝╚██╗ ██╔╝████╗ ██║████╗ ████║██╔══██╗████╗ ██║",
4
- "█████╗ █████╗ ╚████╔╝ ██╔██╗ ██║██╔████╔██║███████║██╔██╗ ██║",
5
- "██╔══╝ ██╔══╝ ╚██╔╝ ██║╚██╗██║██║╚██╔╝██║██╔══██║██║╚██╗██║",
6
- "██║ ███████╗ ██║ ██║ ╚████║██║ ╚═╝ ██║██║ ██║██║ ╚████║",
7
- "╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝",
2
+ " ██████",
3
+ " ███",
4
+ "█████████ ████████ ███ ███ ███ ██████ ██ ███ ████ ███████ ███ ██████",
5
+ " ███ ███ ███ ███ ███ ████ ███ ███ ██ ███ ███ ████ ███",
6
+ " ███ ████████████ ███ ███ ███ ███ ███ ██ ███ █████████ ███ ███",
7
+ " ███ ███ ██ ███ ███ ███ ███ ██ ███ ███ ███ ███ ███",
8
+ "███████ ████████ ████ ███ ███ ███ ██ ███ ██████ ███ ███ ███",
9
+ " ███",
10
+ " █████",
8
11
  ];
9
12
 
10
13
  export const FEYNMAN_ASCII_LOGO_TEXT = FEYNMAN_ASCII_LOGO.join("\n");
11
14
 
12
- export const FEYNMAN_LOGO_HTML = `<link href="https://fonts.googleapis.com/css2?family=Silkscreen:wght@700&display=swap" rel="stylesheet"><span style="font-family:'Silkscreen',cursive;font-size:48px;font-weight:700;color:#10b981">feynman</span>`;
15
+ export const FEYNMAN_LOGO_HTML = `<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet"><span style="font-family:'VT323',monospace;font-size:64px;letter-spacing:-0.05em;color:#10b981">feynman</span>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companion-ai/feynman",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Research-first CLI agent built on Pi and alphaXiv",
5
5
  "type": "module",
6
6
  "engines": {
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { FEYNMAN_ASCII_LOGO_HTML } from "../logo.mjs";
5
+ import { FEYNMAN_LOGO_HTML } from "../logo.mjs";
6
6
 
7
7
  const here = dirname(fileURLToPath(import.meta.url));
8
8
  const appRoot = resolve(here, "..");
@@ -365,7 +365,7 @@ if (oauthPagePath && existsSync(oauthPagePath)) {
365
365
  let source = readFileSync(oauthPagePath, "utf8");
366
366
  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>`;';
367
367
  if (source.includes(piLogo)) {
368
- const feynmanLogo = `const LOGO_SVG = \`${FEYNMAN_ASCII_LOGO_HTML}\`;`;
368
+ const feynmanLogo = `const LOGO_SVG = \`${FEYNMAN_LOGO_HTML}\`;`;
369
369
  source = source.replace(piLogo, feynmanLogo);
370
370
  writeFileSync(oauthPagePath, source, "utf8");
371
371
  }
@@ -377,17 +377,17 @@ const alphaHubAuthPath = findPackageRoot("@companion-ai/alpha-hub")
377
377
 
378
378
  if (alphaHubAuthPath && existsSync(alphaHubAuthPath)) {
379
379
  let source = readFileSync(alphaHubAuthPath, "utf8");
380
- 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"`;
381
- const logoHtml = FEYNMAN_ASCII_LOGO_HTML.replace('color:#10b981', 'color:#34d399');
382
- 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>`;
383
- 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>`;
384
- const oldSuccess = `'<html><body><h2>Logged in to Alpha Hub</h2><p>You can close this tab.</p></body></html>'`;
385
- const oldError = `'<html><body><h2>Login failed</h2><p>You can close this tab.</p></body></html>'`;
380
+ const oldSuccess = "'<html><body><h2>Logged in to Alpha Hub</h2><p>You can close this tab.</p></body></html>'";
381
+ const oldError = "'<html><body><h2>Login failed</h2><p>You can close this tab.</p></body></html>'";
382
+ const bodyAttr = `style="font-family:system-ui,sans-serif;text-align:center;padding-top:20vh;background:#050a08;color:#f0f5f2"`;
383
+ const logo = `<h1 style="font-family:monospace;font-size:48px;color:#34d399;margin:0">feynman</h1>`;
384
+ const newSuccess = `'<html><body ${bodyAttr}>${logo}<h2 style="color:#34d399;margin-top:16px">Logged in</h2><p style="color:#8aaa9a">You can close this tab.</p></body></html>'`;
385
+ const newError = `'<html><body ${bodyAttr}>${logo}<h2 style="color:#ef4444;margin-top:16px">Login failed</h2><p style="color:#8aaa9a">You can close this tab.</p></body></html>'`;
386
386
  if (source.includes(oldSuccess)) {
387
- source = source.replace(oldSuccess, `'${successPage}'`);
387
+ source = source.replace(oldSuccess, newSuccess);
388
388
  }
389
389
  if (source.includes(oldError)) {
390
- source = source.replace(oldError, `'${errorPage}'`);
390
+ source = source.replace(oldError, newError);
391
391
  }
392
392
  writeFileSync(alphaHubAuthPath, source, "utf8");
393
393
  }