create-egregore 0.19.5 → 0.19.6
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/assets/egregore-launcher.js +407 -25
- package/bin/cli.js +34 -20
- package/lib/local.js +21 -5
- package/lib/pi-runtime.js +186 -0
- package/lib/setup.js +56 -11
- package/lib/ui.js +1 -1
- package/package.json +7 -5
- package/runtime/codex/.codex/spec-manifest.json +1 -1
- package/runtime/codex/AGENTS.md +7 -2
- package/runtime/codex/bin/codex-render-spec.mjs +6 -1
- package/runtime/codex/bin/codex-session-start.sh +5 -59
- package/runtime/codex/bin/codex-skill-render.mjs +7 -5
- package/runtime/codex/bin/lib/context.sh +8 -1
- package/runtime/codex/bin/lib/greeting.sh +12 -108
- package/runtime/codex/bin/lib/metrics.sh +374 -0
- package/runtime/codex/bin/publish-artifact.sh +14 -0
- package/runtime/codex/bin/settings.sh +274 -0
- package/runtime/codex/manifest.json +2 -0
- package/runtime/pi/.codex/config.toml +15 -0
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
- package/runtime/pi/.codex/hooks/search-hint.js +65 -0
- package/runtime/pi/.codex/hooks.json +30 -0
- package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
- package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
- package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
- package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
- package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
- package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
- package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
- package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
- package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
- package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
- package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
- package/runtime/pi/.codex/spec-manifest.json +114 -0
- package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
- package/runtime/pi/.pi/extensions/egregore.ts +324 -0
- package/runtime/pi/.pi/settings.json +6 -0
- package/runtime/pi/.pi/spec-manifest.json +12 -0
- package/runtime/pi/AGENTS.md +350 -0
- package/runtime/pi/bin/activity-data.sh +242 -0
- package/runtime/pi/bin/agent.sh +677 -0
- package/runtime/pi/bin/codex-install-skills.sh +78 -0
- package/runtime/pi/bin/codex-render-spec.mjs +394 -0
- package/runtime/pi/bin/codex-session-start.sh +188 -0
- package/runtime/pi/bin/codex-skill-render.mjs +323 -0
- package/runtime/pi/bin/codex-sync-skills.sh +227 -0
- package/runtime/pi/bin/dashboard-data.sh +178 -0
- package/runtime/pi/bin/graph-batch.sh +60 -0
- package/runtime/pi/bin/graph-op.sh +431 -0
- package/runtime/pi/bin/graph-wal.sh +228 -0
- package/runtime/pi/bin/graph.sh +177 -0
- package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/pi/bin/handoff-run.sh +581 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
- package/runtime/pi/bin/index-handoff.sh +312 -0
- package/runtime/pi/bin/lib/artifact-id.sh +46 -0
- package/runtime/pi/bin/lib/config.sh +71 -0
- package/runtime/pi/bin/lib/context.sh +542 -0
- package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
- package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
- package/runtime/pi/bin/lib/git-sync.sh +288 -0
- package/runtime/pi/bin/lib/greeting.sh +340 -0
- package/runtime/pi/bin/lib/hash.sh +13 -0
- package/runtime/pi/bin/lib/identity.sh +174 -0
- package/runtime/pi/bin/lib/metrics.sh +374 -0
- package/runtime/pi/bin/lib/permissions.sh +106 -0
- package/runtime/pi/bin/lib/time.sh +56 -0
- package/runtime/pi/bin/lib/worktree-links.sh +54 -0
- package/runtime/pi/bin/notify.sh +207 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
- package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
- package/runtime/pi/bin/pi-render-spec.mjs +114 -0
- package/runtime/pi/bin/pi-session-start.sh +24 -0
- package/runtime/pi/bin/publish-artifact.sh +201 -0
- package/runtime/pi/bin/publish-references.sh +144 -0
- package/runtime/pi/bin/repo-state.sh +99 -0
- package/runtime/pi/bin/telemetry.sh +391 -0
- package/runtime/pi/bin/worktree-create.sh +118 -0
- package/runtime/pi/bin/worktree-remove.sh +50 -0
- package/runtime/pi/bin/worktree.sh +198 -0
- package/runtime/pi/manifest.json +176 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// egregore-launcher-version: 0.
|
|
2
|
+
// egregore-launcher-version: 0.7.1
|
|
3
3
|
//
|
|
4
4
|
// egregore — launch and browse Egregore instances from your terminal.
|
|
5
5
|
// Installed by create-egregore. Reads ~/.egregore/instances.json.
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// egregore ls | list list instances (non-interactive table)
|
|
15
15
|
// egregore use <slug> set the default for bare `egregore`
|
|
16
16
|
// egregore /invite <user> invite a GitHub user to an instance
|
|
17
|
+
// egregore settings [slug] hosting on/off, managed repos, admins (s in browser)
|
|
17
18
|
// egregore prune [--dry-run] remove entries whose path is gone
|
|
18
19
|
// egregore rm <slug|path> remove a specific entry
|
|
19
20
|
// egregore browse force the full-screen browser
|
|
@@ -30,7 +31,7 @@ const https = require("node:https");
|
|
|
30
31
|
const { spawnSync, spawn } = require("node:child_process");
|
|
31
32
|
const readline = require("node:readline");
|
|
32
33
|
|
|
33
|
-
const VERSION = "0.
|
|
34
|
+
const VERSION = "0.7.1";
|
|
34
35
|
const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
|
|
35
36
|
const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
|
|
36
37
|
const PUBLIC_NPX_PACKAGE = "create-egregore";
|
|
@@ -345,6 +346,7 @@ function normalizeRuntime(raw) {
|
|
|
345
346
|
if (!value) return null;
|
|
346
347
|
if (value === "claude" || value === "claude-code" || value === "claude_code") return "claude-code";
|
|
347
348
|
if (value === "codex" || value === "openai-codex" || value === "openai_codex") return "codex";
|
|
349
|
+
if (value === "pi" || value === "pi-coding-agent" || value === "pi_coding_agent") return "pi";
|
|
348
350
|
return null;
|
|
349
351
|
}
|
|
350
352
|
|
|
@@ -355,6 +357,8 @@ function parseRuntimeFlags(argv) {
|
|
|
355
357
|
const arg = argv[i];
|
|
356
358
|
if (arg === "--codex") {
|
|
357
359
|
runtimeOverride = "codex";
|
|
360
|
+
} else if (arg === "--pi") {
|
|
361
|
+
runtimeOverride = "pi";
|
|
358
362
|
} else if (arg === "--claude" || arg === "--claude-code") {
|
|
359
363
|
runtimeOverride = "claude-code";
|
|
360
364
|
} else if (arg === "--runtime") {
|
|
@@ -368,7 +372,7 @@ function parseRuntimeFlags(argv) {
|
|
|
368
372
|
}
|
|
369
373
|
}
|
|
370
374
|
const normalized = runtimeOverride ? normalizeRuntime(runtimeOverride) : null;
|
|
371
|
-
if (runtimeOverride && !normalized) die(`unknown runtime: ${runtimeOverride}\n expected claude-code or
|
|
375
|
+
if (runtimeOverride && !normalized) die(`unknown runtime: ${runtimeOverride}\n expected claude-code, codex, or pi`);
|
|
372
376
|
return { args: rest, runtimeOverride: normalized };
|
|
373
377
|
}
|
|
374
378
|
|
|
@@ -385,21 +389,32 @@ function runtimeFor(inst, runtimeOverride) {
|
|
|
385
389
|
}
|
|
386
390
|
|
|
387
391
|
function runtimeLabel(runtime) {
|
|
388
|
-
|
|
392
|
+
if (runtime === "codex") return "Codex";
|
|
393
|
+
if (runtime === "pi") return "Pi";
|
|
394
|
+
return "Claude Code";
|
|
389
395
|
}
|
|
390
396
|
|
|
391
397
|
function runtimeShort(runtime) {
|
|
392
|
-
return runtime === "codex" ? "codex" : "claude-code";
|
|
398
|
+
return runtime === "codex" ? "codex" : runtime === "pi" ? "pi" : "claude-code";
|
|
393
399
|
}
|
|
394
400
|
|
|
395
401
|
function runtimeBinary(runtime) {
|
|
396
|
-
return runtime === "codex" ? "codex" : "claude";
|
|
402
|
+
return runtime === "codex" ? "codex" : runtime === "pi" ? "pi" : "claude";
|
|
397
403
|
}
|
|
398
404
|
|
|
399
405
|
function assistantInstallCommand(runtime) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
406
|
+
if (runtime === "codex") return "npm install -g @openai/codex";
|
|
407
|
+
if (runtime === "pi") return "npm install -g --ignore-scripts @earendil-works/pi-coding-agent";
|
|
408
|
+
return "npm install -g @anthropic-ai/claude-code";
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function piTrustArgs() {
|
|
412
|
+
try {
|
|
413
|
+
const help = spawnSync("pi", ["--help"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
414
|
+
return `${help.stdout || ""}\n${help.stderr || ""}`.includes("--approve") ? ["--approve"] : [];
|
|
415
|
+
} catch {
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
403
418
|
}
|
|
404
419
|
|
|
405
420
|
function commandExists(bin) {
|
|
@@ -410,13 +425,12 @@ function runtimeChoiceDescription(runtime, inst, stored) {
|
|
|
410
425
|
const installed = commandExists(runtimeBinary(runtime));
|
|
411
426
|
const bits = [];
|
|
412
427
|
if (!installed) {
|
|
413
|
-
return runtime
|
|
414
|
-
? "not installed · install Codex, then run egregore"
|
|
415
|
-
: "not installed · install Claude Code, then run egregore";
|
|
428
|
+
return `not installed · install ${runtimeLabel(runtime)}, then run egregore`;
|
|
416
429
|
}
|
|
417
430
|
bits.push("installed");
|
|
418
431
|
if (runtime === stored) bits.push("saved default");
|
|
419
432
|
if (runtime === "codex") bits.push("portable agent protocol");
|
|
433
|
+
else if (runtime === "pi") bits.push("project-local meta-harness");
|
|
420
434
|
else bits.push("Claude Code integration");
|
|
421
435
|
return bits.join(" · ");
|
|
422
436
|
}
|
|
@@ -439,7 +453,7 @@ function runtimePickerLines(inst, runtimes, stored, cursor) {
|
|
|
439
453
|
lines.push(` ${marker} ${BOLD}${label}${RESET} ${DIM}${desc}${RESET}`);
|
|
440
454
|
}
|
|
441
455
|
// Coming-soon harnesses — shown for visibility, not selectable yet.
|
|
442
|
-
for (const soon of ["Hermes"
|
|
456
|
+
for (const soon of ["Hermes"]) {
|
|
443
457
|
lines.push(` ${DIM}· ${pad(soon, labelWidth)} coming soon${RESET}`);
|
|
444
458
|
}
|
|
445
459
|
lines.push("");
|
|
@@ -529,9 +543,7 @@ function runtimeChoicesFor(inst, runtimeOverride) {
|
|
|
529
543
|
const override = normalizeRuntime(runtimeOverride);
|
|
530
544
|
if (override) return [override];
|
|
531
545
|
const stored = storedRuntimeFor(inst);
|
|
532
|
-
const choices = stored
|
|
533
|
-
? ["codex", "claude-code"]
|
|
534
|
-
: ["claude-code", "codex"];
|
|
546
|
+
const choices = [stored, ...["claude-code", "codex", "pi"].filter((runtime) => runtime !== stored)];
|
|
535
547
|
return choices.sort((a, b) => Number(commandExists(runtimeBinary(b))) - Number(commandExists(runtimeBinary(a))));
|
|
536
548
|
}
|
|
537
549
|
|
|
@@ -637,6 +649,25 @@ function hasCodexCoreSupport(egregoreDir) {
|
|
|
637
649
|
);
|
|
638
650
|
}
|
|
639
651
|
|
|
652
|
+
function hasPiCoreSupport(egregoreDir) {
|
|
653
|
+
return (
|
|
654
|
+
fs.existsSync(path.join(egregoreDir, "bin", "pi-session-start.sh")) &&
|
|
655
|
+
fs.existsSync(path.join(egregoreDir, ".pi", "APPEND_SYSTEM.md")) &&
|
|
656
|
+
fs.existsSync(path.join(egregoreDir, ".pi", "settings.json")) &&
|
|
657
|
+
fs.existsSync(path.join(egregoreDir, ".pi", "extensions", "egregore.ts")) &&
|
|
658
|
+
fs.existsSync(path.join(egregoreDir, ".codex", "skills"))
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function piSupportMessage(egregoreDir) {
|
|
663
|
+
return [
|
|
664
|
+
"Pi meta-harness support is missing from this Egregore checkout.",
|
|
665
|
+
` path: ${egregoreDir}`,
|
|
666
|
+
"",
|
|
667
|
+
"Run npx -y create-egregore@latest to install the packaged Pi runtime, then rerun egregore.",
|
|
668
|
+
].join("\n");
|
|
669
|
+
}
|
|
670
|
+
|
|
640
671
|
function installCodexSkills(egregoreDir) {
|
|
641
672
|
const script = path.join(egregoreDir, "bin", "codex-install-skills.sh");
|
|
642
673
|
if (!fs.existsSync(script)) return false;
|
|
@@ -662,6 +693,7 @@ function runtimeLaunchArgs(runtime, egregoreDir) {
|
|
|
662
693
|
"--cd", egregoreDir,
|
|
663
694
|
];
|
|
664
695
|
}
|
|
696
|
+
if (runtime === "pi") return piTrustArgs();
|
|
665
697
|
return ["start"];
|
|
666
698
|
}
|
|
667
699
|
|
|
@@ -695,6 +727,8 @@ async function launch(inst, runtimeOverride) {
|
|
|
695
727
|
if (!skillsInstalled || !cardRendered) {
|
|
696
728
|
die(codexSupportMessage(inst.path));
|
|
697
729
|
}
|
|
730
|
+
} else if (runtime === "pi") {
|
|
731
|
+
if (!hasPiCoreSupport(inst.path)) die(piSupportMessage(inst.path));
|
|
698
732
|
}
|
|
699
733
|
// Replace the current process with the selected assistant in the instance dir.
|
|
700
734
|
// spawn + detached + exit-on-exit replicates exec() in Node.
|
|
@@ -822,10 +856,12 @@ function cmdHelp() {
|
|
|
822
856
|
` ${CYAN}egregore${RESET} pick an instance, then pick a harness`,
|
|
823
857
|
` ${CYAN}egregore${RESET} ${DIM}<slug|name>${RESET} pick a harness for the matching instance`,
|
|
824
858
|
` ${CYAN}egregore --runtime${RESET} ${DIM}<name> <slug>${RESET} advanced harness override`,
|
|
859
|
+
` ${DIM}names: claude-code · codex · pi${RESET}`,
|
|
825
860
|
` ${CYAN}egregore browse${RESET} force the full-screen browser`,
|
|
826
861
|
` ${CYAN}egregore ls${RESET} ${DIM}| list${RESET} list installed instances`,
|
|
827
862
|
` ${CYAN}egregore use${RESET} ${DIM}<slug>${RESET} set the default for bare ${BOLD}egregore${RESET}`,
|
|
828
863
|
` ${CYAN}egregore /invite${RESET} ${DIM}<user> [slug]${RESET} invite a GitHub user`,
|
|
864
|
+
` ${CYAN}egregore settings${RESET} ${DIM}[slug]${RESET} hosting on/off, repos, admins`,
|
|
829
865
|
` ${CYAN}egregore prune${RESET} ${DIM}[--dry-run]${RESET} remove entries with missing paths`,
|
|
830
866
|
` ${CYAN}egregore rm${RESET} ${DIM}<slug|path>${RESET} remove a specific entry`,
|
|
831
867
|
``,
|
|
@@ -836,6 +872,7 @@ function cmdHelp() {
|
|
|
836
872
|
` ${DIM}n / +${RESET} create a new Egregore`,
|
|
837
873
|
` ${DIM}u${RESET} set selected as default`,
|
|
838
874
|
` ${DIM}i${RESET} invite a GitHub user to selected instance`,
|
|
875
|
+
` ${DIM}s${RESET} settings (hosting on/off, repos, admins)`,
|
|
839
876
|
` ${DIM}d${RESET} delete selected entry (offers cleanup of ${RED}✗${RESET} missing ones)`,
|
|
840
877
|
` ${DIM}?${RESET} toggle help overlay`,
|
|
841
878
|
` ${DIM}q${RESET} quit browser (esc goes back — it never quits)`,
|
|
@@ -1251,6 +1288,343 @@ async function cmdInvite(args) {
|
|
|
1251
1288
|
return inviteToInstance(inst, username);
|
|
1252
1289
|
}
|
|
1253
1290
|
|
|
1291
|
+
// ── settings ───────────────────────────────────────────────────────
|
|
1292
|
+
//
|
|
1293
|
+
// The launcher stays a THIN caller: every config change shells out to the
|
|
1294
|
+
// instance's deterministic `bin/settings.sh` verbs (one source of truth, no
|
|
1295
|
+
// JS reimplementation to drift from the script). This is the reliability point
|
|
1296
|
+
// — the correctness lives in the tested script; the screen below just drives it.
|
|
1297
|
+
|
|
1298
|
+
function settingsScriptPath(inst) {
|
|
1299
|
+
return path.join(inst.path, "bin", "settings.sh");
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
function runSettings(inst, args) {
|
|
1303
|
+
const script = settingsScriptPath(inst);
|
|
1304
|
+
if (!fs.existsSync(script)) {
|
|
1305
|
+
return { status: 127, stdout: "", stderr: "bin/settings.sh not found — update this egregore (run /pull) to get settings.\n", missing: true };
|
|
1306
|
+
}
|
|
1307
|
+
try {
|
|
1308
|
+
return spawnSync("bash", [script, ...args], { cwd: inst.path, stdio: "pipe", encoding: "utf-8", timeout: 15000 });
|
|
1309
|
+
} catch (err) {
|
|
1310
|
+
return { status: 1, stdout: "", stderr: String(err) };
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
function settingsDump(inst) {
|
|
1315
|
+
const r = runSettings(inst, ["dump"]);
|
|
1316
|
+
if (!r || r.status !== 0) return null;
|
|
1317
|
+
try { return JSON.parse(r.stdout); } catch { return null; }
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// Nested raw-mode list editor for a domain (repo | admin | people): hover an
|
|
1321
|
+
// item and press `d` to remove it, `a` to open an inline add field, esc to go
|
|
1322
|
+
// back. Mirrors the main browser's affordances. Its own raw-mode loop; resolves
|
|
1323
|
+
// when the user backs out. Each mutation is ONE deterministic settings.sh verb;
|
|
1324
|
+
// validation, the last-admin guard, and GitHub-access notes surface as notices.
|
|
1325
|
+
function listEditor(inst, domain, label) {
|
|
1326
|
+
const fieldFor = (d) => {
|
|
1327
|
+
if (domain === "repo") return (d.repos || []).map((r) => r.name || r);
|
|
1328
|
+
if (domain === "admin") return d.admins || [];
|
|
1329
|
+
return d.people || [];
|
|
1330
|
+
};
|
|
1331
|
+
const noun = domain === "repo" ? "repo name" : "github handle";
|
|
1332
|
+
|
|
1333
|
+
return new Promise((resolve) => {
|
|
1334
|
+
let items = fieldFor(settingsDump(inst) || {});
|
|
1335
|
+
let cursor = 0;
|
|
1336
|
+
let mode = "browse"; // browse | add | confirm
|
|
1337
|
+
let input = "";
|
|
1338
|
+
let notice = "";
|
|
1339
|
+
let rendered = 0;
|
|
1340
|
+
let done = false;
|
|
1341
|
+
const previousRaw = !!process.stdin.isRaw;
|
|
1342
|
+
const isMouseEvent = makeMouseFilter();
|
|
1343
|
+
|
|
1344
|
+
const refresh = () => {
|
|
1345
|
+
items = fieldFor(settingsDump(inst) || {});
|
|
1346
|
+
if (cursor >= items.length) cursor = Math.max(0, items.length - 1);
|
|
1347
|
+
};
|
|
1348
|
+
|
|
1349
|
+
const erase = () => {
|
|
1350
|
+
if (!rendered) return;
|
|
1351
|
+
process.stdout.write(`\x1b[${rendered}A` + ANSI.clearDown);
|
|
1352
|
+
rendered = 0;
|
|
1353
|
+
};
|
|
1354
|
+
|
|
1355
|
+
const buildLines = () => {
|
|
1356
|
+
const out = [];
|
|
1357
|
+
out.push(``);
|
|
1358
|
+
out.push(` ${BOLD}${label}${RESET}`);
|
|
1359
|
+
out.push(` ${DIM}────────────────────────────────────────${RESET}`);
|
|
1360
|
+
if (items.length === 0) {
|
|
1361
|
+
out.push(` ${DIM}(none)${RESET}`);
|
|
1362
|
+
} else {
|
|
1363
|
+
items.forEach((it, i) => {
|
|
1364
|
+
const sel = i === cursor;
|
|
1365
|
+
out.push(` ${sel ? CYAN + "❯" + RESET : " "} ${sel ? BOLD + it + RESET : it}`);
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
out.push(``);
|
|
1369
|
+
if (notice) out.push(` ${notice}`);
|
|
1370
|
+
if (mode === "add") {
|
|
1371
|
+
out.push(` ${CYAN}add ${noun}:${RESET} ${input}${CYAN}▌${RESET} ${DIM}enter save · esc cancel${RESET}`);
|
|
1372
|
+
} else if (mode === "confirm") {
|
|
1373
|
+
out.push(` ${YELLOW}remove ${items[cursor]}?${RESET} ${DIM}y / N${RESET}`);
|
|
1374
|
+
} else {
|
|
1375
|
+
out.push(` ${DIM}↑↓ nav · ${RESET}${BOLD}a${RESET}${DIM} add · ${RESET}${BOLD}d${RESET}${DIM} delete · esc back${RESET}`);
|
|
1376
|
+
}
|
|
1377
|
+
return out;
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
const render = () => {
|
|
1381
|
+
erase();
|
|
1382
|
+
const lines = buildLines();
|
|
1383
|
+
rendered = lines.length;
|
|
1384
|
+
process.stdout.write(lines.map((l) => l + ANSI.clearLine).join("\n") + "\n");
|
|
1385
|
+
};
|
|
1386
|
+
|
|
1387
|
+
const cleanup = () => {
|
|
1388
|
+
if (done) return;
|
|
1389
|
+
done = true;
|
|
1390
|
+
process.stdin.removeListener("keypress", onKey);
|
|
1391
|
+
process.stdout.write(ANSI.mouseOff);
|
|
1392
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(previousRaw);
|
|
1393
|
+
if (!previousRaw) process.stdin.pause();
|
|
1394
|
+
erase();
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1397
|
+
// Run a verb (spawnSync blocks briefly — people ops hit GitHub). Surface the
|
|
1398
|
+
// last stdout/stderr line as a notice; refresh the list from disk after.
|
|
1399
|
+
const runVerb = (args) => {
|
|
1400
|
+
const r = runSettings(inst, args);
|
|
1401
|
+
const msg = ((r.stdout || "") + " " + (r.stderr || "")).trim().split("\n").map((s) => s.trim()).filter(Boolean).pop() || "";
|
|
1402
|
+
notice = r && r.status === 0 ? msg : `${YELLOW}${msg}${RESET}`;
|
|
1403
|
+
refresh();
|
|
1404
|
+
};
|
|
1405
|
+
|
|
1406
|
+
function onKey(chunk, key = {}) {
|
|
1407
|
+
if (isMouseEvent(chunk, key)) return;
|
|
1408
|
+
if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
|
|
1409
|
+
|
|
1410
|
+
if (mode === "add") {
|
|
1411
|
+
if (key.name === "escape") { mode = "browse"; input = ""; render(); return; }
|
|
1412
|
+
if (key.name === "return") {
|
|
1413
|
+
const v = input.trim().split(/\s+/)[0];
|
|
1414
|
+
mode = "browse"; input = "";
|
|
1415
|
+
if (v) runVerb([domain, "add", v]);
|
|
1416
|
+
render();
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
if (key.name === "backspace") { input = input.slice(0, -1); render(); return; }
|
|
1420
|
+
if (chunk && chunk.length === 1 && chunk >= " " && !key.ctrl && !key.meta) { input += chunk; render(); return; }
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
if (mode === "confirm") {
|
|
1425
|
+
if (chunk === "y" || chunk === "Y") {
|
|
1426
|
+
const victim = items[cursor];
|
|
1427
|
+
mode = "browse";
|
|
1428
|
+
if (victim) runVerb([domain, "remove", victim]);
|
|
1429
|
+
} else { mode = "browse"; }
|
|
1430
|
+
render();
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// browse
|
|
1435
|
+
if (key.name === "escape" || key.name === "left") { cleanup(); resolve(); return; }
|
|
1436
|
+
if (key.name === "up" || key.name === "k") { if (items.length) cursor = (cursor - 1 + items.length) % items.length; render(); return; }
|
|
1437
|
+
if (key.name === "down" || key.name === "j") { if (items.length) cursor = (cursor + 1) % items.length; render(); return; }
|
|
1438
|
+
if (chunk === "a") { mode = "add"; input = ""; notice = ""; render(); return; }
|
|
1439
|
+
if (chunk === "d") { if (items.length) { mode = "confirm"; render(); } return; }
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
readline.emitKeypressEvents(process.stdin);
|
|
1443
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
1444
|
+
process.stdin.resume();
|
|
1445
|
+
process.stdout.write(ANSI.mouseOff);
|
|
1446
|
+
process.stdin.on("keypress", onKey);
|
|
1447
|
+
render();
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// The `s`-key settings screen: a raw-mode menu (hosting toggle + repo/admin
|
|
1452
|
+
// editors) over bin/settings.sh. Modeled on pickRuntimeInteractive.
|
|
1453
|
+
function settingsScreen(inst) {
|
|
1454
|
+
const title = inst.slug || inst.name || path.basename(inst.path);
|
|
1455
|
+
|
|
1456
|
+
if (!fs.existsSync(settingsScriptPath(inst))) {
|
|
1457
|
+
console.log(`\n ${YELLOW}Settings unavailable${RESET} — ${BOLD}${title}${RESET} has no ${DIM}bin/settings.sh${RESET}.`);
|
|
1458
|
+
console.log(` Update it: open the egregore and run ${CYAN}/pull${RESET}.\n`);
|
|
1459
|
+
return Promise.resolve();
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
let data = settingsDump(inst) || { hosting: true, repos: [], admins: [], people: [] };
|
|
1463
|
+
|
|
1464
|
+
const rows = [
|
|
1465
|
+
{
|
|
1466
|
+
label: () => `Hosted Artifacts`,
|
|
1467
|
+
value: () => (data.hosting ? `${GREEN}ON${RESET}` : `${YELLOW}OFF${RESET}`),
|
|
1468
|
+
detailLines: () => [data.hosting
|
|
1469
|
+
? ` ${DIM}Enter toggles. When OFF, artifacts never upload to egregore.xyz.${RESET}`
|
|
1470
|
+
: ` ${DIM}Artifacts stay local. /view still renders them on localhost.${RESET}`],
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
label: () => `Managed repos`,
|
|
1474
|
+
value: () => `${DIM}${data.repos.length}${RESET}`,
|
|
1475
|
+
detailLines: () => data.repos.length
|
|
1476
|
+
? data.repos.map((r) => ` ${DIM}·${RESET} ${r.name || r}`)
|
|
1477
|
+
: [` ${DIM}(none)${RESET}`],
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
label: () => `Admins`,
|
|
1481
|
+
value: () => `${DIM}${data.admins.length}${RESET}`,
|
|
1482
|
+
detailLines: () => data.admins.length
|
|
1483
|
+
? [` ${DIM}${data.admins.join(", ")}${RESET}`]
|
|
1484
|
+
: [` ${DIM}(none)${RESET}`],
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
label: () => `People`,
|
|
1488
|
+
value: () => `${DIM}${(data.people || []).length}${RESET}`,
|
|
1489
|
+
detailLines: () => (data.people && data.people.length)
|
|
1490
|
+
? [` ${DIM}${data.people.join(", ")}${RESET}`]
|
|
1491
|
+
: [` ${DIM}(none)${RESET}`],
|
|
1492
|
+
},
|
|
1493
|
+
];
|
|
1494
|
+
|
|
1495
|
+
return new Promise((resolve) => {
|
|
1496
|
+
let cursor = 0;
|
|
1497
|
+
let rendered = 0;
|
|
1498
|
+
let done = false;
|
|
1499
|
+
let notice = "";
|
|
1500
|
+
const previousRaw = !!process.stdin.isRaw;
|
|
1501
|
+
const isMouseEvent = makeMouseFilter();
|
|
1502
|
+
|
|
1503
|
+
const erase = () => {
|
|
1504
|
+
if (!rendered) return;
|
|
1505
|
+
// Move to the TOP of the last-rendered block, then wipe everything below
|
|
1506
|
+
// in one shot. Clearing line-by-line leaves the cursor at the BOTTOM, so
|
|
1507
|
+
// the next redraw starts a row lower each time — that's the drift that
|
|
1508
|
+
// pushes blank "margins" on top. Up-N + clearDown returns to the top and
|
|
1509
|
+
// clears cleanly, so every render repaints in place.
|
|
1510
|
+
process.stdout.write(`\x1b[${rendered}A` + ANSI.clearDown);
|
|
1511
|
+
rendered = 0;
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1514
|
+
const buildLines = () => {
|
|
1515
|
+
const out = [];
|
|
1516
|
+
out.push(``);
|
|
1517
|
+
out.push(` ${BOLD}Settings${RESET} ${DIM}·${RESET} ${BOLD}${title}${RESET}`);
|
|
1518
|
+
out.push(` ${DIM}────────────────────────────────────────${RESET}`);
|
|
1519
|
+
rows.forEach((row, i) => {
|
|
1520
|
+
const sel = i === cursor;
|
|
1521
|
+
const marker = sel ? `${CYAN}❯${RESET}` : ` `;
|
|
1522
|
+
const name = sel ? `${BOLD}${row.label()}${RESET}` : row.label();
|
|
1523
|
+
out.push(` ${marker} ${name} ${row.value()}`);
|
|
1524
|
+
if (sel) for (const dl of row.detailLines()) out.push(dl);
|
|
1525
|
+
});
|
|
1526
|
+
out.push(``);
|
|
1527
|
+
if (notice) out.push(` ${notice}`);
|
|
1528
|
+
out.push(` ${DIM}↑↓ nav · enter ${cursor === 0 ? "toggle" : "edit"} · esc back · q quit${RESET}`);
|
|
1529
|
+
return out;
|
|
1530
|
+
};
|
|
1531
|
+
|
|
1532
|
+
const render = () => {
|
|
1533
|
+
erase();
|
|
1534
|
+
const lines = buildLines();
|
|
1535
|
+
rendered = lines.length;
|
|
1536
|
+
process.stdout.write(lines.map((l) => l + ANSI.clearLine).join("\n") + "\n");
|
|
1537
|
+
};
|
|
1538
|
+
|
|
1539
|
+
const teardownRaw = () => {
|
|
1540
|
+
process.stdin.removeListener("keypress", onKey);
|
|
1541
|
+
process.stdout.write(ANSI.mouseOff);
|
|
1542
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(previousRaw);
|
|
1543
|
+
if (!previousRaw) process.stdin.pause();
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1546
|
+
const setupRaw = () => {
|
|
1547
|
+
readline.emitKeypressEvents(process.stdin);
|
|
1548
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
1549
|
+
process.stdin.resume();
|
|
1550
|
+
process.stdout.write(ANSI.mouseOff);
|
|
1551
|
+
process.stdin.on("keypress", onKey);
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1554
|
+
const cleanup = () => {
|
|
1555
|
+
if (done) return;
|
|
1556
|
+
done = true;
|
|
1557
|
+
teardownRaw();
|
|
1558
|
+
erase();
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
const toggleHosting = () => {
|
|
1562
|
+
const r = runSettings(inst, ["hosting", data.hosting ? "off" : "on"]);
|
|
1563
|
+
if (!r || r.status !== 0) {
|
|
1564
|
+
notice = `${YELLOW}${((r && r.stderr) || "could not change hosting").trim().split("\n")[0]}${RESET}`;
|
|
1565
|
+
} else {
|
|
1566
|
+
data = settingsDump(inst) || data;
|
|
1567
|
+
notice = `hosting ${data.hosting ? `${GREEN}ON${RESET}` : `${YELLOW}OFF${RESET}`}`;
|
|
1568
|
+
}
|
|
1569
|
+
render();
|
|
1570
|
+
};
|
|
1571
|
+
|
|
1572
|
+
// Hand off to the nested raw-mode list editor, then repaint this screen with
|
|
1573
|
+
// refreshed counts when the user backs out.
|
|
1574
|
+
const openList = (domain, label) => {
|
|
1575
|
+
erase();
|
|
1576
|
+
teardownRaw();
|
|
1577
|
+
listEditor(inst, domain, label).then(() => {
|
|
1578
|
+
data = settingsDump(inst) || data;
|
|
1579
|
+
notice = "";
|
|
1580
|
+
setupRaw();
|
|
1581
|
+
render();
|
|
1582
|
+
});
|
|
1583
|
+
};
|
|
1584
|
+
|
|
1585
|
+
function onKey(chunk, key = {}) {
|
|
1586
|
+
if (isMouseEvent(chunk, key)) return;
|
|
1587
|
+
if (key.ctrl && key.name === "c") { cleanup(); process.exit(130); }
|
|
1588
|
+
if (key.name === "q") { cleanup(); resolve(); return; }
|
|
1589
|
+
// Esc / ← = back (esc never quits).
|
|
1590
|
+
if (key.name === "escape" || key.name === "left") { cleanup(); resolve(); return; }
|
|
1591
|
+
if (key.name === "up" || key.name === "k") { cursor = (cursor - 1 + rows.length) % rows.length; render(); return; }
|
|
1592
|
+
if (key.name === "down" || key.name === "j") { cursor = (cursor + 1) % rows.length; render(); return; }
|
|
1593
|
+
if (key.name === "return" || chunk === " ") {
|
|
1594
|
+
if (cursor === 0) toggleHosting();
|
|
1595
|
+
else if (cursor === 1) openList("repo", "Managed repos");
|
|
1596
|
+
else if (cursor === 2) openList("admin", "Admins");
|
|
1597
|
+
else if (cursor === 3) openList("people", "People");
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
setupRaw();
|
|
1602
|
+
render();
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
async function cmdSettings(args) {
|
|
1607
|
+
const registry = readRegistry();
|
|
1608
|
+
if (registry.length === 0) die("no egregores installed yet.\n run: npx -y create-egregore@latest");
|
|
1609
|
+
const DOMAINS = new Set(["hosting", "repo", "admin", "people", "dump"]);
|
|
1610
|
+
let rest = args.slice();
|
|
1611
|
+
let slug = "";
|
|
1612
|
+
if (rest.length && !DOMAINS.has(rest[0])) { slug = rest[0]; rest = rest.slice(1); }
|
|
1613
|
+
const inst = resolveInviteInstance(registry, slug);
|
|
1614
|
+
if (!inst) {
|
|
1615
|
+
die(`multiple egregores installed; choose one:\n egregore settings <slug> [hosting|repo|admin]\n run: egregore ls`);
|
|
1616
|
+
}
|
|
1617
|
+
// No verb + a TTY → interactive screen. Otherwise forward straight to the verb.
|
|
1618
|
+
if (rest.length === 0) {
|
|
1619
|
+
if (isTTY && canPrompt()) return settingsScreen(inst);
|
|
1620
|
+
rest = ["dump"];
|
|
1621
|
+
}
|
|
1622
|
+
const r = runSettings(inst, rest);
|
|
1623
|
+
if (r.stdout) process.stdout.write(r.stdout.endsWith("\n") ? r.stdout : r.stdout + "\n");
|
|
1624
|
+
if (r.stderr) process.stderr.write(r.stderr);
|
|
1625
|
+
process.exitCode = r.status || 0;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1254
1628
|
// ── tiny sync confirm (non-TUI flows) ───────────────────────────────
|
|
1255
1629
|
|
|
1256
1630
|
function syncConfirm(question) {
|
|
@@ -1370,7 +1744,7 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
|
|
|
1370
1744
|
? `filter /${filter}`
|
|
1371
1745
|
: "press ? for help";
|
|
1372
1746
|
const canShowBigLogo = bodyWidth >= Math.max(...EGREGORE_ART.map((line) => line.length)) && height >= 24;
|
|
1373
|
-
const skillStatus = `Codex ${nativeCount} native · ${adapterCount} adapter · ${skillsCount}
|
|
1747
|
+
const skillStatus = `Codex ${nativeCount} native · ${adapterCount} adapter · Pi ${skillsCount} shared · Claude ${claudeCount}`;
|
|
1374
1748
|
const skillGroups = [
|
|
1375
1749
|
`${DIM}core${RESET}: ${workflows}`,
|
|
1376
1750
|
`${DIM}team${RESET}: ${joinShort(["/handoff", "/ask", "/invite", "/quest"], Math.max(16, bodyWidth - 8))}`,
|
|
@@ -1411,8 +1785,8 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
|
|
|
1411
1785
|
`${BOLD}Harnesses${RESET}`,
|
|
1412
1786
|
`${DIM}claude-code${RESET}: ${commandExists("claude") ? "installed" : "not installed"}`,
|
|
1413
1787
|
`${DIM}codex${RESET}: ${commandExists("codex") ? "installed" : "not installed"}`,
|
|
1788
|
+
`${DIM}pi${RESET}: ${commandExists("pi") ? "installed" : "not installed"}`,
|
|
1414
1789
|
`${DIM}Hermes — Coming Soon${RESET}`,
|
|
1415
|
-
`${DIM}Pi — Coming Soon${RESET}`,
|
|
1416
1790
|
];
|
|
1417
1791
|
const rows = Math.max(EGREGORE_SIGIL.length + 2, right.length);
|
|
1418
1792
|
for (let i = 0; i < rows; i++) {
|
|
@@ -1424,11 +1798,11 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
|
|
|
1424
1798
|
lines.push(boxLine(`${BOLD}Available Skills${RESET}`, innerWidth, margin));
|
|
1425
1799
|
for (const group of skillGroups) lines.push(boxLine(group, innerWidth, margin));
|
|
1426
1800
|
lines.push(emptyBoxLine(innerWidth, margin));
|
|
1427
|
-
lines.push(boxLine(`${BOLD}Harnesses${RESET} ${DIM}claude-code${RESET} ${commandExists("claude") ? "installed" : "missing"} · ${DIM}codex${RESET} ${commandExists("codex") ? "installed" : "missing"} · ${DIM}Hermes
|
|
1801
|
+
lines.push(boxLine(`${BOLD}Harnesses${RESET} ${DIM}claude-code${RESET} ${commandExists("claude") ? "installed" : "missing"} · ${DIM}codex${RESET} ${commandExists("codex") ? "installed" : "missing"} · ${DIM}pi${RESET} ${commandExists("pi") ? "installed" : "missing"} · ${DIM}Hermes — Coming Soon${RESET}`, innerWidth, margin));
|
|
1428
1802
|
}
|
|
1429
1803
|
|
|
1430
1804
|
lines.push(emptyBoxLine(innerWidth, margin));
|
|
1431
|
-
lines.push(boxLine(`${DIM}enter selects an instance, then choose Claude Code or
|
|
1805
|
+
lines.push(boxLine(`${DIM}enter selects an instance, then choose Claude Code, Codex, or Pi without leaving this screen${RESET}`, innerWidth, margin));
|
|
1432
1806
|
lines.push(bottom);
|
|
1433
1807
|
return lines;
|
|
1434
1808
|
}
|
|
@@ -1461,7 +1835,7 @@ function renderRuntimePanel({ inst, cursor, width, height }) {
|
|
|
1461
1835
|
lines.push(boxLine(`${marker} ${pad(labelText, 16)} ${DIM}${desc}${RESET}`, innerWidth, margin));
|
|
1462
1836
|
}
|
|
1463
1837
|
// Coming-soon harnesses — shown for visibility, not selectable yet.
|
|
1464
|
-
for (const soon of ["Hermes"
|
|
1838
|
+
for (const soon of ["Hermes"]) {
|
|
1465
1839
|
lines.push(boxLine(`${DIM}· ${pad(soon, 16)} coming soon${RESET}`, innerWidth, margin));
|
|
1466
1840
|
}
|
|
1467
1841
|
|
|
@@ -1674,7 +2048,7 @@ async function browser(args = {}) {
|
|
|
1674
2048
|
const label = (k) => `${DIM}${pad(k, 10)}${RESET}`;
|
|
1675
2049
|
previewLines.push(` ${label("action")} ${BOLD}Set up a new Egregore${RESET}`);
|
|
1676
2050
|
previewLines.push(` ${label("flow")} guided install, Telegram, invite, ready/start`);
|
|
1677
|
-
previewLines.push(` ${label("harness")} choose Claude Code or
|
|
2051
|
+
previewLines.push(` ${label("harness")} choose Claude Code, Codex, or Pi during setup`);
|
|
1678
2052
|
previewLines.push(` ${label("start")} press enter to begin`);
|
|
1679
2053
|
} else if (!selected) {
|
|
1680
2054
|
previewLines.push(`${DIM} (no selection)${RESET}`);
|
|
@@ -1697,7 +2071,7 @@ async function browser(args = {}) {
|
|
|
1697
2071
|
if (pv.skills) {
|
|
1698
2072
|
const skillLine = selectedWorkflowLine(pv, Math.max(16, blockWidth - 20));
|
|
1699
2073
|
previewLines.push(` ${label("invoke")} ${skillLine}`);
|
|
1700
|
-
previewLines.push(` ${label("skills")} Codex ${BOLD}${pv.skills.native.length}${RESET} native
|
|
2074
|
+
previewLines.push(` ${label("skills")} Codex ${BOLD}${pv.skills.native.length}${RESET} native + ${BOLD}${pv.skills.adapterCount}${RESET} adapters; Pi ${BOLD}${pv.skills.names.length}${RESET} shared; Claude ${BOLD}${pv.skills.claudeCount || 0}${RESET}`);
|
|
1701
2075
|
}
|
|
1702
2076
|
const modeBits = [];
|
|
1703
2077
|
modeBits.push(runtimeShort(runtimeFor(selected, args.runtimeOverride)));
|
|
@@ -1757,8 +2131,8 @@ async function browser(args = {}) {
|
|
|
1757
2131
|
footer = band(`● ${stripAnsi(state.message)}`, bg);
|
|
1758
2132
|
} else {
|
|
1759
2133
|
footer = blockWidth < 96
|
|
1760
|
-
? band(`${key("↑↓", "nav")} ${key("enter", "select")} ${key("n/+", "new")} ${key("i", "invite")} ${key("d", "delete")} ${key("?", "help")} ${key("q", "quit")}`, 46)
|
|
1761
|
-
: band(`${key("↑↓", "nav")} ${key("enter", "select")} ${key("n/+", "new")} ${key("/", "filter")} ${key("i", "invite")} ${key("u", "default")} ${key("d", "delete")} ${key("?", "help")} ${key("q", "quit")}`, 46);
|
|
2134
|
+
? band(`${key("↑↓", "nav")} ${key("enter", "select")} ${key("n/+", "new")} ${key("i", "invite")} ${key("s", "settings")} ${key("d", "delete")} ${key("?", "help")} ${key("q", "quit")}`, 46)
|
|
2135
|
+
: band(`${key("↑↓", "nav")} ${key("enter", "select")} ${key("n/+", "new")} ${key("/", "filter")} ${key("i", "invite")} ${key("s", "settings")} ${key("u", "default")} ${key("d", "delete")} ${key("?", "help")} ${key("q", "quit")}`, 46);
|
|
1762
2136
|
}
|
|
1763
2137
|
out.push(M + footer + "\n");
|
|
1764
2138
|
if (state.showHelp) {
|
|
@@ -2056,6 +2430,13 @@ async function browser(args = {}) {
|
|
|
2056
2430
|
render();
|
|
2057
2431
|
return;
|
|
2058
2432
|
}
|
|
2433
|
+
if (chunk === "s") {
|
|
2434
|
+
const pick = visible()[state.cursor]?.e;
|
|
2435
|
+
if (!pick) return;
|
|
2436
|
+
cleanup();
|
|
2437
|
+
resolve(settingsScreen(pick));
|
|
2438
|
+
return;
|
|
2439
|
+
}
|
|
2059
2440
|
if (chunk === "u") {
|
|
2060
2441
|
const pick = visible()[state.cursor]?.e;
|
|
2061
2442
|
if (!pick) return;
|
|
@@ -2174,6 +2555,7 @@ function main() {
|
|
|
2174
2555
|
if (cmd === "ls" || cmd === "list") return cmdLs();
|
|
2175
2556
|
if (cmd === "use") return cmdUse(args[1]);
|
|
2176
2557
|
if (cmd === "/invite" || cmd === "invite") return cmdInvite(args.slice(1));
|
|
2558
|
+
if (cmd === "settings" || cmd === "config") return cmdSettings(args.slice(1));
|
|
2177
2559
|
if (cmd === "prune") return cmdPruneNonInteractive(args[1] === "--dry-run" || args[1] === "-n");
|
|
2178
2560
|
if (cmd === "rm" || cmd === "remove") return cmdRmNonInteractive(args[1]);
|
|
2179
2561
|
if (cmd === "browse") return browser({ force: true, runtimeOverride });
|