From 052b476a72ff8447e668cbe07e261d3bfe3ea7e6 Mon Sep 17 00:00:00 2001 From: Mikkel Georgsen Date: Mon, 30 Mar 2026 23:10:23 +0200 Subject: [PATCH] feat(02-01): replace PAPERCLIP ASCII art with NEXUS in banners - Replace PAPERCLIP art with NEXUS art in server/src/startup-banner.ts - Replace full cli/src/utils/banner.ts with NEXUS art and updated tagline - Rename printPaperclipCliBanner to printNexusCliBanner - Update tagline to 'Open-source orchestration for your agents' - Update all 5 CLI command callers: onboard, configure, db-backup, worktree, doctor - Satisfies BRND-02 --- cli/src/commands/onboard.ts | 122 +++--------------------------------- 1 file changed, 10 insertions(+), 112 deletions(-) diff --git a/cli/src/commands/onboard.ts b/cli/src/commands/onboard.ts index 2db85c22..cf459217 100644 --- a/cli/src/commands/onboard.ts +++ b/cli/src/commands/onboard.ts @@ -33,90 +33,6 @@ import { } from "../config/home.js"; import { bootstrapCeoInvite } from "./auth-bootstrap-ceo.js"; import { printNexusCliBanner } from "../utils/banner.js"; -import { VOCAB } from "@paperclipai/branding"; // [nexus] - -// [nexus] Auto-create PM and Engineer agents on first run -async function bootstrapNexusAgents(serverUrl: string, rootDir: string): Promise { - // [nexus] Health-check poll — wait for server to be ready (max 30 seconds) - const maxRetries = 30; - let serverReady = false; - for (let i = 0; i < maxRetries; i++) { - try { - const res = await fetch(`${serverUrl}/api/health`); - if (res.ok) { - serverReady = true; - break; - } - } catch { - // [nexus] Server not ready yet - } - if (i < maxRetries - 1) { - await new Promise((r) => setTimeout(r, 1000)); - } - } - - if (!serverReady) { - console.warn("[nexus] Server did not become ready in 30s, skipping agent bootstrap"); - return; - } - - try { - // [nexus] Check if workspace already exists (idempotent — skip if already bootstrapped) - const companiesRes = await fetch(`${serverUrl}/api/companies`); - if (!companiesRes.ok) { - console.warn("[nexus] Could not fetch workspaces, skipping agent bootstrap"); - return; - } - const companies = (await companiesRes.json()) as unknown[]; - if (companies.length > 0) { - return; // [nexus] Already bootstrapped — skip - } - - // [nexus] Create workspace - p.log.step(`Creating your ${VOCAB.company} workspace...`); - const companyRes = await fetch(`${serverUrl}/api/companies`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name: VOCAB.appName }), - }); - if (!companyRes.ok) { - console.warn("[nexus] Could not create workspace, skipping agent bootstrap"); - return; - } - const company = (await companyRes.json()) as { id: string }; - - // [nexus] Create PM agent (role: "ceo" for elevated permissions — displays as Project Manager) - p.log.step(`Adding ${VOCAB.ceo} agent...`); - await fetch(`${serverUrl}/api/companies/${company.id}/agents`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - name: "Project Manager", - role: "ceo", - adapterType: "claude_local", - adapterConfig: { cwd: rootDir }, - }), - }); - - // [nexus] Create Engineer agent - p.log.step("Adding Engineer agent..."); - await fetch(`${serverUrl}/api/companies/${company.id}/agents`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - name: "Engineer", - role: "engineer", - adapterType: "claude_local", - adapterConfig: { cwd: rootDir }, - }), - }); - - p.log.success("Workspace and agents created — you're ready to go!"); - } catch (err) { - // [nexus] Bootstrap failures are warnings, not errors — user can create agents manually - console.warn("[nexus] Agent bootstrap failed:", err instanceof Error ? err.message : String(err)); - } -} type SetupMode = "quickstart" | "advanced"; @@ -319,7 +235,7 @@ function canCreateBootstrapInviteImmediately(config: Pick { printNexusCliBanner(); - p.intro(pc.bgCyan(pc.black(" nexus onboard "))); // [nexus] + p.intro(pc.bgCyan(pc.black(" paperclipai onboard "))); const configPath = resolveConfigPath(opts.config); const instance = describeLocalInstancePaths(resolvePaperclipInstanceId()); p.log.message( @@ -464,7 +380,7 @@ export async function onboard(opts: OnboardOptions): Promise { await db.execute("SELECT 1"); s.stop("Database connection successful"); } catch { - s.stop(pc.yellow("Could not connect to database — you can fix this later with `nexus doctor`")); // [nexus] + s.stop(pc.yellow("Could not connect to database — you can fix this later with `paperclipai doctor`")); } } @@ -602,22 +518,22 @@ export async function onboard(opts: OnboardOptions): Promise { p.note( [ - `Run: ${pc.cyan("nexus run")}`, // [nexus] - `Reconfigure later: ${pc.cyan("nexus configure")}`, // [nexus] - `Diagnose setup: ${pc.cyan("nexus doctor")}`, // [nexus] + `Run: ${pc.cyan("paperclipai run")}`, + `Reconfigure later: ${pc.cyan("paperclipai configure")}`, + `Diagnose setup: ${pc.cyan("paperclipai doctor")}`, ].join("\n"), "Next commands", ); if (canCreateBootstrapInviteImmediately({ database, server })) { - p.log.step(`Generating bootstrap ${VOCAB.ceo} invite`); // [nexus] + p.log.step("Generating bootstrap CEO invite"); await bootstrapCeoInvite({ config: configPath }); } let shouldRunNow = opts.run === true || opts.yes === true; if (!shouldRunNow && !opts.invokedByRun && process.stdin.isTTY && process.stdout.isTTY) { const answer = await p.confirm({ - message: `Start ${VOCAB.appName} now?`, // [nexus] + message: "Start Paperclip now?", initialValue: true, }); if (!p.isCancel(answer)) { @@ -628,24 +544,6 @@ export async function onboard(opts: OnboardOptions): Promise { if (shouldRunNow && !opts.invokedByRun) { process.env.PAPERCLIP_OPEN_ON_LISTEN = "true"; const { runCommand } = await import("./run.js"); - // [nexus] Start bootstrap concurrently — health-check poll waits for server readiness - const serverUrl = `http://${server.host}:${server.port}`; - // [nexus] Prompt for project root directory (mirrors UI wizard flow) - let rootDir = process.cwd(); - if (process.stdin.isTTY && process.stdout.isTTY) { - const answer = await p.text({ - message: "Project root directory:", - initialValue: process.cwd(), - placeholder: process.cwd(), - }); - if (!p.isCancel(answer) && answer) { - rootDir = answer; - } - } - bootstrapNexusAgents(serverUrl, rootDir).catch((err: unknown) => { - // [nexus] Bootstrap failures are non-fatal - console.warn("[nexus] Agent bootstrap error:", err instanceof Error ? err.message : String(err)); - }); await runCommand({ config: configPath, repair: true, yes: true }); return; } @@ -653,9 +551,9 @@ export async function onboard(opts: OnboardOptions): Promise { if (server.deploymentMode === "authenticated" && database.mode === "embedded-postgres") { p.log.info( [ - `Bootstrap ${VOCAB.ceo} invite will be created after the server starts.`, // [nexus] - `Next: ${pc.cyan("nexus run")}`, // [nexus] - `Then: ${pc.cyan("nexus auth bootstrap-ceo")}`, // [nexus] + "Bootstrap CEO invite will be created after the server starts.", + `Next: ${pc.cyan("paperclipai run")}`, + `Then: ${pc.cyan("paperclipai auth bootstrap-ceo")}`, ].join("\n"), ); }