omegon 0.6.3 → 0.6.4
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/README.md +12 -10
- package/bin/omegon.mjs +40 -0
- package/bin/pi.mjs +5 -26
- package/extensions/00-secrets/index.ts +146 -39
- package/extensions/01-auth/auth.ts +1 -1
- package/extensions/01-auth/index.ts +3 -3
- package/extensions/auto-compact.ts +1 -1
- package/extensions/bootstrap/deps.ts +42 -0
- package/extensions/bootstrap/index.ts +326 -110
- package/extensions/chronos/index.ts +1 -1
- package/extensions/cleave/dispatcher.ts +6 -6
- package/extensions/cleave/index.ts +6 -6
- package/extensions/cleave/planner.ts +1 -1
- package/extensions/cleave/worktree.ts +1 -1
- package/extensions/core-renderers.ts +24 -84
- package/extensions/dashboard/footer.ts +184 -40
- package/extensions/dashboard/git.ts +2 -2
- package/extensions/dashboard/index.ts +4 -4
- package/extensions/dashboard/overlay-data.ts +5 -5
- package/extensions/dashboard/overlay.ts +5 -5
- package/extensions/dashboard/render-utils.ts +1 -1
- package/extensions/dashboard/types.ts +15 -0
- package/extensions/defaults.ts +4 -12
- package/extensions/design-tree/dashboard-state.ts +6 -6
- package/extensions/design-tree/design-card.ts +3 -3
- package/extensions/design-tree/index.ts +64 -44
- package/extensions/design-tree/types.ts +4 -2
- package/extensions/distill.ts +1 -1
- package/extensions/effort/index.ts +137 -10
- package/extensions/lib/model-routing.ts +304 -32
- package/extensions/lib/operator-fallback.ts +1 -1
- package/extensions/lib/operator-profile.ts +1 -1
- package/extensions/lib/provider-env.ts +163 -0
- package/extensions/{sci-ui.ts → lib/sci-ui.ts} +119 -2
- package/extensions/{shared-state.ts → lib/shared-state.ts} +13 -9
- package/extensions/lib/slash-command-bridge.ts +1 -1
- package/extensions/{types.d.ts → lib/types.d.ts} +3 -3
- package/extensions/local-inference/index.ts +1 -1
- package/extensions/mcp-bridge/index.ts +1 -1
- package/extensions/model-budget.ts +10 -10
- package/extensions/offline-driver.ts +11 -4
- package/extensions/openspec/archive-gate.ts +1 -1
- package/extensions/openspec/branch-cleanup.ts +1 -1
- package/extensions/openspec/dashboard-state.ts +3 -3
- package/extensions/openspec/index.ts +5 -5
- package/extensions/project-memory/factstore.ts +5 -11
- package/extensions/project-memory/index.ts +48 -34
- package/extensions/project-memory/package.json +1 -1
- package/extensions/project-memory/sci-renderers.ts +1 -1
- package/extensions/render/index.ts +1 -1
- package/extensions/session-log.ts +1 -1
- package/extensions/spinner-verbs.ts +1 -1
- package/extensions/style.ts +1 -1
- package/extensions/terminal-title.ts +3 -3
- package/extensions/tool-profile/index.ts +1 -1
- package/extensions/vault/index.ts +1 -1
- package/extensions/version-check.ts +13 -9
- package/extensions/view/index.ts +4 -4
- package/extensions/web-search/index.ts +5 -2
- package/extensions/web-ui/index.ts +1 -1
- package/extensions/web-ui/state.ts +1 -1
- package/package.json +8 -7
- package/scripts/preinstall.sh +19 -3
- package/scripts/publish-pi-mono.sh +92 -0
- package/skills/pi-extensions/SKILL.md +2 -2
- package/skills/pi-tui/SKILL.md +17 -17
- package/skills/typescript/SKILL.md +1 -1
- package/themes/alpharius.json +7 -6
- /package/extensions/{debug.ts → lib/debug.ts} +0 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* /bootstrap — Run interactive setup (install missing deps + profile)
|
|
11
11
|
* /bootstrap status — Show dependency checklist without installing
|
|
12
12
|
* /bootstrap install — Install all missing core + recommended deps
|
|
13
|
-
* /update-pi — Update pi binary to latest @
|
|
13
|
+
* /update-pi — Update pi binary to latest @styrene-lab/pi-coding-agent release
|
|
14
14
|
* /update-pi --dry-run — Check for update without installing
|
|
15
15
|
*
|
|
16
16
|
* Guards:
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { spawn } from "node:child_process";
|
|
23
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
24
|
-
import { join } from "node:path";
|
|
23
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
24
|
+
import { dirname, join } from "node:path";
|
|
25
25
|
import { homedir, tmpdir } from "node:os";
|
|
26
|
-
import type { ExtensionAPI } from "@
|
|
26
|
+
import type { ExtensionAPI } from "@styrene-lab/pi-coding-agent";
|
|
27
27
|
import { checkAllProviders, type AuthResult } from "../01-auth/auth.ts";
|
|
28
28
|
import { loadPiConfig } from "../lib/model-preferences.ts";
|
|
29
29
|
import {
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
type OperatorCapabilityProfile,
|
|
34
34
|
type OperatorProfileCandidate,
|
|
35
35
|
} from "../lib/operator-profile.ts";
|
|
36
|
-
import { sharedState } from "../shared-state.ts";
|
|
36
|
+
import { sharedState } from "../lib/shared-state.ts";
|
|
37
37
|
import { getDefaultPolicy, type ProviderRoutingPolicy } from "../lib/model-routing.ts";
|
|
38
38
|
import { DEPS, checkAll, formatReport, bestInstallCmd, sortByRequires, type DepStatus, type DepTier } from "./deps.ts";
|
|
39
39
|
|
|
@@ -313,6 +313,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
313
313
|
if (!isFirstRun()) return;
|
|
314
314
|
if (!ctx.hasUI) return;
|
|
315
315
|
|
|
316
|
+
// Signal other extensions to suppress redundant "no providers" warnings
|
|
317
|
+
sharedState.bootstrapPending = true;
|
|
318
|
+
|
|
316
319
|
const statuses = checkAll();
|
|
317
320
|
const missing = statuses.filter((s) => !s.available);
|
|
318
321
|
const needsProfile = needsOperatorProfileSetup(getConfigRoot(ctx));
|
|
@@ -376,124 +379,316 @@ export default function (pi: ExtensionAPI) {
|
|
|
376
379
|
},
|
|
377
380
|
});
|
|
378
381
|
|
|
379
|
-
// --- /update
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
382
|
+
// --- /update: unified update command ---
|
|
383
|
+
// Detects dev vs installed mode and runs the appropriate lifecycle:
|
|
384
|
+
// Dev mode (.git exists): pull → submodule sync → build → dependency refresh → relink → verify → restart handoff
|
|
385
|
+
// Installed (no .git): npm install -g omegon@latest → verify → restart handoff
|
|
386
|
+
// Replaces the old split update mental model with a singular-package lifecycle.
|
|
387
|
+
pi.registerCommand("update", {
|
|
388
|
+
description: "Run the authoritative Omegon update lifecycle, then hand off to restart",
|
|
384
389
|
handler: async (args, ctx) => {
|
|
385
390
|
const dryRun = args.trim() === "--dry-run";
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
// Resolve the npm registry latest
|
|
389
|
-
ctx.ui.notify(`Checking latest version of ${PKG}…`, "info");
|
|
390
|
-
let latestVersion: string;
|
|
391
|
-
try {
|
|
392
|
-
latestVersion = await new Promise<string>((resolve, reject) => {
|
|
393
|
-
let out = "";
|
|
394
|
-
const child = spawn("npm", ["view", PKG, "version", "--json"], {
|
|
395
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
396
|
-
});
|
|
397
|
-
child.stdout.on("data", (d: Buffer) => { out += d.toString(); });
|
|
398
|
-
child.on("close", (code: number) => {
|
|
399
|
-
if (code !== 0) return reject(new Error("npm view failed"));
|
|
400
|
-
resolve(JSON.parse(out.trim()));
|
|
401
|
-
});
|
|
402
|
-
});
|
|
403
|
-
} catch {
|
|
404
|
-
ctx.ui.notify(`Failed to query npm registry. Are you online?`, "warning");
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
391
|
+
const omegonRoot = process.env.PI_CODING_AGENT_DIR ?? join(import.meta.dirname ?? ".", "..");
|
|
392
|
+
const isDevMode = existsSync(join(omegonRoot, ".git"));
|
|
407
393
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
let out = "";
|
|
413
|
-
const child = spawn("npm", ["list", "-g", PKG, "--json", "--depth=0"], {
|
|
414
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
415
|
-
});
|
|
416
|
-
child.stdout.on("data", (d: Buffer) => { out += d.toString(); });
|
|
417
|
-
child.on("close", () => {
|
|
418
|
-
try {
|
|
419
|
-
const data = JSON.parse(out);
|
|
420
|
-
resolve(data.dependencies?.[PKG]?.version ?? "unknown");
|
|
421
|
-
} catch {
|
|
422
|
-
resolve("unknown");
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
} catch { /* ignore */ }
|
|
427
|
-
|
|
428
|
-
if (installedVersion === latestVersion) {
|
|
429
|
-
ctx.ui.notify(`Already on latest: ${PKG}@${latestVersion} ✅`, "info");
|
|
430
|
-
return;
|
|
394
|
+
if (isDevMode) {
|
|
395
|
+
await updateDevMode(omegonRoot, dryRun, ctx);
|
|
396
|
+
} else {
|
|
397
|
+
await updateInstalledMode(dryRun, ctx);
|
|
431
398
|
}
|
|
432
|
-
|
|
433
|
-
ctx.ui.notify(
|
|
434
|
-
`Update available: ${installedVersion} → ${latestVersion}\n` +
|
|
435
|
-
(dryRun ? "(dry run — not installing)" : "Installing…"),
|
|
436
|
-
"info"
|
|
437
|
-
);
|
|
438
|
-
|
|
439
|
-
if (dryRun) return;
|
|
440
|
-
|
|
441
|
-
const confirmed = await ctx.ui.confirm(
|
|
442
|
-
"Update pi binary?",
|
|
443
|
-
`Install ${PKG}@${latestVersion} globally via npm?\n\nThis will replace the currently running binary. Restart pi after the update completes.`
|
|
444
|
-
);
|
|
445
|
-
if (!confirmed) {
|
|
446
|
-
ctx.ui.notify("Update cancelled.", "info");
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
await new Promise<void>((resolve, reject) => {
|
|
451
|
-
let stderr = "";
|
|
452
|
-
const child = spawn("npm", ["install", "-g", `${PKG}@${latestVersion}`], {
|
|
453
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
454
|
-
});
|
|
455
|
-
child.stderr.on("data", (d: Buffer) => { stderr += d.toString(); });
|
|
456
|
-
child.on("close", (code: number) => {
|
|
457
|
-
if (code !== 0) {
|
|
458
|
-
ctx.ui.notify(`npm install failed:\n${stderr}`, "warning");
|
|
459
|
-
reject(new Error("install failed"));
|
|
460
|
-
} else {
|
|
461
|
-
resolve();
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
}).catch(() => { /* error already notified */ return; });
|
|
465
|
-
|
|
466
|
-
ctx.ui.notify(
|
|
467
|
-
`✅ Updated to ${PKG}@${latestVersion}.\n` +
|
|
468
|
-
"Restart pi to use the new version (/exit, then pi).",
|
|
469
|
-
"info"
|
|
470
|
-
);
|
|
471
399
|
},
|
|
472
400
|
});
|
|
473
401
|
|
|
474
|
-
// --- /refresh:
|
|
475
|
-
// jiti's fs cache uses path-based hashing, so source changes aren't
|
|
476
|
-
// detected on /reload. /refresh clears the cache first.
|
|
402
|
+
// --- /refresh: lightweight cache clear + reload only ---
|
|
477
403
|
pi.registerCommand("refresh", {
|
|
478
|
-
description: "Clear transpilation cache and reload extensions",
|
|
404
|
+
description: "Clear transpilation cache and reload extensions without package/runtime mutation",
|
|
479
405
|
handler: async (_args, ctx) => {
|
|
480
|
-
|
|
481
|
-
let cleared = 0;
|
|
482
|
-
if (existsSync(jitiCacheDir)) {
|
|
483
|
-
try {
|
|
484
|
-
const files = readdirSync(jitiCacheDir);
|
|
485
|
-
cleared = files.length;
|
|
486
|
-
rmSync(jitiCacheDir, { recursive: true, force: true });
|
|
487
|
-
} catch { /* best-effort */ }
|
|
488
|
-
}
|
|
489
|
-
ctx.ui.notify(cleared > 0
|
|
490
|
-
? `Cleared ${cleared} cached transpilations. Reloading…`
|
|
491
|
-
: "No transpilation cache found. Reloading…", "info");
|
|
406
|
+
clearJitiCache(ctx);
|
|
492
407
|
await ctx.reload();
|
|
493
408
|
},
|
|
494
409
|
});
|
|
495
410
|
}
|
|
496
411
|
|
|
412
|
+
// ── /update helpers ──────────────────────────────────────────────────────
|
|
413
|
+
|
|
414
|
+
/** Run a command, collect stdout+stderr, resolve with exit code. */
|
|
415
|
+
function run(
|
|
416
|
+
cmd: string, args: string[], opts?: { cwd?: string },
|
|
417
|
+
): Promise<{ code: number; stdout: string; stderr: string }> {
|
|
418
|
+
return new Promise((resolve) => {
|
|
419
|
+
let stdout = "", stderr = "";
|
|
420
|
+
const child = spawn(cmd, args, { cwd: opts?.cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
421
|
+
child.stdout.on("data", (d: Buffer) => { stdout += d.toString(); });
|
|
422
|
+
child.stderr.on("data", (d: Buffer) => { stderr += d.toString(); });
|
|
423
|
+
child.on("close", (code: number) => resolve({ code: code ?? 1, stdout, stderr }));
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Clear jiti transpilation cache. Returns count of cleared entries. */
|
|
428
|
+
function clearJitiCache(_ctx?: unknown): number {
|
|
429
|
+
const jitiCacheDir = join(tmpdir(), "jiti");
|
|
430
|
+
let cleared = 0;
|
|
431
|
+
if (existsSync(jitiCacheDir)) {
|
|
432
|
+
try {
|
|
433
|
+
cleared = readdirSync(jitiCacheDir).length;
|
|
434
|
+
rmSync(jitiCacheDir, { recursive: true, force: true });
|
|
435
|
+
} catch { /* best-effort */ }
|
|
436
|
+
}
|
|
437
|
+
return cleared;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export interface PiResolutionInfo {
|
|
441
|
+
omegonRoot: string;
|
|
442
|
+
cli: string;
|
|
443
|
+
resolutionMode: "vendor" | "npm";
|
|
444
|
+
agentDir: string;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export interface OmegonBinaryVerification {
|
|
448
|
+
ok: boolean;
|
|
449
|
+
executableName: string;
|
|
450
|
+
executablePath: string;
|
|
451
|
+
realExecutablePath: string;
|
|
452
|
+
resolution?: PiResolutionInfo;
|
|
453
|
+
reason?: string;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
export function normalizeExecutablePath(executablePath: string): string {
|
|
457
|
+
if (!executablePath) return "";
|
|
458
|
+
try {
|
|
459
|
+
return realpathSync(executablePath);
|
|
460
|
+
} catch {
|
|
461
|
+
return executablePath;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async function getActiveExecutablePath(executableName = "omegon"): Promise<string> {
|
|
466
|
+
const which = await run("which", [executableName]);
|
|
467
|
+
return which.code === 0 ? which.stdout.trim() : "";
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export function validateOmegonBinaryVerification(
|
|
471
|
+
executableName: string,
|
|
472
|
+
executablePath: string,
|
|
473
|
+
realExecutablePath: string,
|
|
474
|
+
resolution: PiResolutionInfo,
|
|
475
|
+
): OmegonBinaryVerification {
|
|
476
|
+
const binaryLooksOwnedByOmegon = /[\\/]omegon[\\/]/.test(realExecutablePath) || /[\\/]omegon[\\/]bin[\\/](?:omegon|pi)(?:\.mjs)?$/.test(realExecutablePath);
|
|
477
|
+
if (!/omegon(?:[\\/]|$)/.test(resolution.omegonRoot)) {
|
|
478
|
+
return { ok: false, executableName, executablePath, realExecutablePath, resolution, reason: `active ${executableName} resolved to non-Omegon root: ${resolution.omegonRoot}` };
|
|
479
|
+
}
|
|
480
|
+
if (!binaryLooksOwnedByOmegon) {
|
|
481
|
+
return { ok: false, executableName, executablePath, realExecutablePath, resolution, reason: `active ${executableName} symlink target does not appear to point at Omegon: ${realExecutablePath}` };
|
|
482
|
+
}
|
|
483
|
+
return { ok: true, executableName, executablePath, realExecutablePath, resolution };
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async function inspectActiveOmegonBinary(): Promise<OmegonBinaryVerification> {
|
|
487
|
+
const executableName = "omegon";
|
|
488
|
+
const executablePath = await getActiveExecutablePath(executableName);
|
|
489
|
+
if (!executablePath) {
|
|
490
|
+
return { ok: false, executableName, executablePath: "", realExecutablePath: "", reason: "`omegon` command not found on PATH" };
|
|
491
|
+
}
|
|
492
|
+
const realExecutablePath = normalizeExecutablePath(executablePath);
|
|
493
|
+
const probe = await run(executablePath, ["--where"]);
|
|
494
|
+
if (probe.code !== 0) {
|
|
495
|
+
return { ok: false, executableName, executablePath, realExecutablePath, reason: "active omegon binary did not return Omegon resolution metadata" };
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
const resolution = JSON.parse(probe.stdout.trim()) as PiResolutionInfo;
|
|
499
|
+
return validateOmegonBinaryVerification(executableName, executablePath, realExecutablePath, resolution);
|
|
500
|
+
} catch {
|
|
501
|
+
return { ok: false, executableName, executablePath, realExecutablePath, reason: "active omegon returned invalid verification metadata" };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function formatVerification(verification: OmegonBinaryVerification): string {
|
|
506
|
+
if (!verification.ok || !verification.resolution) {
|
|
507
|
+
return `✗ omegon target verification failed${verification.reason ? `: ${verification.reason}` : ""}`;
|
|
508
|
+
}
|
|
509
|
+
return [
|
|
510
|
+
`✓ active ${verification.executableName}: ${verification.executablePath}`,
|
|
511
|
+
`✓ binary target: ${verification.realExecutablePath}`,
|
|
512
|
+
`✓ runtime root: ${verification.resolution.omegonRoot}`,
|
|
513
|
+
`✓ core resolution: ${verification.resolution.resolutionMode} (${verification.resolution.cli})`,
|
|
514
|
+
].join("\n");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** Dev mode: git pull → submodule update → build → install deps → relink → verify → restart handoff. */
|
|
518
|
+
async function updateDevMode(
|
|
519
|
+
omegonRoot: string,
|
|
520
|
+
dryRun: boolean,
|
|
521
|
+
ctx: { ui: { notify: (message: string, type?: "error" | "warning" | "info") => void } },
|
|
522
|
+
): Promise<void> {
|
|
523
|
+
const steps: string[] = [];
|
|
524
|
+
|
|
525
|
+
// ── Step 1: git pull omegon ──────────────────────────────────────
|
|
526
|
+
ctx.ui.notify("▸ Pulling omegon…", "info");
|
|
527
|
+
const pull = await run("git", ["pull", "--ff-only"], { cwd: omegonRoot });
|
|
528
|
+
if (pull.code !== 0) {
|
|
529
|
+
// Non-ff merge needed — not fatal, just skip
|
|
530
|
+
const msg = pull.stderr.includes("fatal")
|
|
531
|
+
? `git pull failed: ${pull.stderr.trim().split("\n")[0]}`
|
|
532
|
+
: "git pull: non-fast-forward — skipping (merge manually if needed)";
|
|
533
|
+
steps.push(`⚠ ${msg}`);
|
|
534
|
+
} else {
|
|
535
|
+
const summary = pull.stdout.trim().split("\n").pop() ?? "";
|
|
536
|
+
const upToDate = pull.stdout.includes("Already up to date");
|
|
537
|
+
steps.push(upToDate ? "✓ omegon: already up to date" : `✓ omegon: ${summary}`);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// ── Step 2: update submodule (pi-mono fork) ──────────────────────
|
|
541
|
+
ctx.ui.notify("▸ Updating pi-mono submodule…", "info");
|
|
542
|
+
const sub = await run(
|
|
543
|
+
"git", ["submodule", "update", "--init", "--recursive"],
|
|
544
|
+
{ cwd: omegonRoot },
|
|
545
|
+
);
|
|
546
|
+
if (sub.code !== 0) {
|
|
547
|
+
steps.push(`⚠ submodule update failed: ${sub.stderr.trim().split("\n")[0]}`);
|
|
548
|
+
} else {
|
|
549
|
+
steps.push("✓ pi-mono submodule synced");
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ── Step 3: build pi-mono ────────────────────────────────────────
|
|
553
|
+
if (dryRun) {
|
|
554
|
+
steps.push("· build: skipped (dry run)");
|
|
555
|
+
} else {
|
|
556
|
+
ctx.ui.notify("▸ Building pi-mono…", "info");
|
|
557
|
+
const piMonoRoot = join(omegonRoot, "vendor/pi-mono");
|
|
558
|
+
const build = await run("npm", ["run", "build"], { cwd: piMonoRoot });
|
|
559
|
+
if (build.code !== 0) {
|
|
560
|
+
const errLine = build.stderr.trim().split("\n").filter(l => !l.startsWith("npm warn")).pop() ?? "unknown error";
|
|
561
|
+
steps.push(`✗ build failed: ${errLine}`);
|
|
562
|
+
ctx.ui.notify(`Update incomplete:\n${steps.join("\n")}`, "warning");
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
steps.push("✓ pi-mono built");
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// ── Step 4: npm install (pick up any new deps) ───────────────────
|
|
569
|
+
if (dryRun) {
|
|
570
|
+
steps.push("· npm install: skipped (dry run)");
|
|
571
|
+
} else {
|
|
572
|
+
ctx.ui.notify("▸ Refreshing omegon dependencies…", "info");
|
|
573
|
+
const inst = await run("npm", ["install", "--install-links=false"], { cwd: omegonRoot });
|
|
574
|
+
if (inst.code !== 0) {
|
|
575
|
+
steps.push(`⚠ npm install had issues (non-fatal)`);
|
|
576
|
+
} else {
|
|
577
|
+
steps.push("✓ omegon dependencies refreshed");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// ── Step 5: relink omegon globally ───────────────────────────────
|
|
582
|
+
if (dryRun) {
|
|
583
|
+
steps.push("· npm link --force: skipped (dry run)");
|
|
584
|
+
} else {
|
|
585
|
+
ctx.ui.notify("▸ Relinking omegon globally…", "info");
|
|
586
|
+
const link = await run("npm", ["link", "--force"], { cwd: omegonRoot });
|
|
587
|
+
if (link.code !== 0) {
|
|
588
|
+
steps.push(`✗ npm link failed: ${(link.stderr.trim().split("\n").filter((l) => !l.startsWith("npm warn")).pop() ?? "unknown error")}`);
|
|
589
|
+
ctx.ui.notify(`Update incomplete:\n${steps.join("\n")}`, "warning");
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
steps.push("✓ omegon relinked globally");
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// ── Step 6: verify active binary target ──────────────────────────
|
|
596
|
+
if (dryRun) {
|
|
597
|
+
steps.push("· omegon target verification: skipped (dry run)");
|
|
598
|
+
ctx.ui.notify(`Dry run:\n${steps.join("\n")}`, "info");
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const verification = await inspectActiveOmegonBinary();
|
|
602
|
+
if (!verification.ok) {
|
|
603
|
+
steps.push(formatVerification(verification));
|
|
604
|
+
ctx.ui.notify(`Update incomplete:\n${steps.join("\n")}`, "warning");
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
steps.push(formatVerification(verification));
|
|
608
|
+
|
|
609
|
+
// ── Step 7: clear cache + explicit restart handoff ───────────────
|
|
610
|
+
const cleared = clearJitiCache(ctx);
|
|
611
|
+
if (cleared > 0) steps.push(`✓ cleared ${cleared} cached transpilations`);
|
|
612
|
+
steps.push("✓ update complete — restart Omegon now (/exit, then `omegon`) to load the rebuilt runtime");
|
|
613
|
+
ctx.ui.notify(steps.join("\n"), "info");
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** Installed mode: npm install -g omegon@latest → verify → cache clear → restart handoff. */
|
|
617
|
+
async function updateInstalledMode(
|
|
618
|
+
dryRun: boolean,
|
|
619
|
+
ctx: {
|
|
620
|
+
ui: {
|
|
621
|
+
notify: (message: string, type?: "error" | "warning" | "info") => void;
|
|
622
|
+
confirm: (title: string, message: string) => Promise<boolean>;
|
|
623
|
+
};
|
|
624
|
+
},
|
|
625
|
+
): Promise<void> {
|
|
626
|
+
const PKG = "omegon";
|
|
627
|
+
|
|
628
|
+
// Check latest version on npm
|
|
629
|
+
ctx.ui.notify(`Checking latest version of ${PKG}…`, "info");
|
|
630
|
+
const view = await run("npm", ["view", PKG, "version", "--json"]);
|
|
631
|
+
if (view.code !== 0) {
|
|
632
|
+
ctx.ui.notify("Failed to query npm registry. Are you online?", "warning");
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
const latestVersion = JSON.parse(view.stdout.trim());
|
|
636
|
+
|
|
637
|
+
// Determine installed version
|
|
638
|
+
const list = await run("npm", ["list", "-g", PKG, "--json", "--depth=0"]);
|
|
639
|
+
let installedVersion = "unknown";
|
|
640
|
+
try {
|
|
641
|
+
const data = JSON.parse(list.stdout);
|
|
642
|
+
installedVersion = data.dependencies?.[PKG]?.version ?? "unknown";
|
|
643
|
+
} catch { /* ignore */ }
|
|
644
|
+
|
|
645
|
+
if (installedVersion === latestVersion) {
|
|
646
|
+
ctx.ui.notify(`Already on latest: ${PKG}@${latestVersion} ✅`, "info");
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
ctx.ui.notify(
|
|
651
|
+
`Update available: ${installedVersion} → ${latestVersion}` +
|
|
652
|
+
(dryRun ? "\n(dry run — not installing)" : ""),
|
|
653
|
+
"info"
|
|
654
|
+
);
|
|
655
|
+
if (dryRun) return;
|
|
656
|
+
|
|
657
|
+
const confirmed = await ctx.ui.confirm(
|
|
658
|
+
"Update omegon?",
|
|
659
|
+
`Install ${PKG}@${latestVersion} globally via npm?\n\nThis will update Omegon, its bundled agent core, extensions, themes, and skills.\nRestart Omegon after the update completes.`,
|
|
660
|
+
);
|
|
661
|
+
if (!confirmed) {
|
|
662
|
+
ctx.ui.notify("Update cancelled.", "info");
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
ctx.ui.notify("Installing…", "info");
|
|
667
|
+
const inst = await run("npm", ["install", "-g", `${PKG}@${latestVersion}`]);
|
|
668
|
+
if (inst.code !== 0) {
|
|
669
|
+
ctx.ui.notify(`npm install failed:\n${inst.stderr}`, "warning");
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const verification = await inspectActiveOmegonBinary();
|
|
674
|
+
if (!verification.ok) {
|
|
675
|
+
ctx.ui.notify(
|
|
676
|
+
`Updated to ${PKG}@${latestVersion}, but post-install verification failed.\n${formatVerification(verification)}\nResolve the Omegon binary target before restarting Omegon.`,
|
|
677
|
+
"warning",
|
|
678
|
+
);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const cleared = clearJitiCache(ctx);
|
|
683
|
+
ctx.ui.notify(
|
|
684
|
+
`✅ Updated to ${PKG}@${latestVersion}.` +
|
|
685
|
+
`\n${formatVerification(verification)}` +
|
|
686
|
+
(cleared > 0 ? `\nCleared ${cleared} cached transpilations.` : "") +
|
|
687
|
+
"\nRestart Omegon to use the new version (/exit, then omegon).",
|
|
688
|
+
"info"
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
497
692
|
async function interactiveSetup(pi: ExtensionAPI, ctx: CommandContext): Promise<void> {
|
|
498
693
|
const statuses = checkAll();
|
|
499
694
|
const missing = statuses.filter((s) => !s.available);
|
|
@@ -544,14 +739,35 @@ async function interactiveSetup(pi: ExtensionAPI, ctx: CommandContext): Promise<
|
|
|
544
739
|
);
|
|
545
740
|
}
|
|
546
741
|
|
|
742
|
+
// API key guidance — check if any cloud provider is configured
|
|
743
|
+
const providerReadiness = await checkAllProviders(pi);
|
|
744
|
+
const hasAnyCloudKey = providerReadiness.some(
|
|
745
|
+
(r: AuthResult) => r.status === "ok" && r.provider !== "local",
|
|
746
|
+
);
|
|
747
|
+
if (!hasAnyCloudKey) {
|
|
748
|
+
ctx.ui.notify(
|
|
749
|
+
"\n🔑 **No cloud API keys detected.**\n" +
|
|
750
|
+
"Omegon needs at least one provider key to function. The fastest options:\n" +
|
|
751
|
+
" • Anthropic: `/secrets configure ANTHROPIC_API_KEY` (get key at console.anthropic.com)\n" +
|
|
752
|
+
" • OpenAI: `/secrets configure OPENAI_API_KEY` (get key at platform.openai.com)\n" +
|
|
753
|
+
" • GitHub Copilot: `/login github` (requires Copilot subscription)\n",
|
|
754
|
+
"warning"
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
547
758
|
await ensureOperatorProfile(pi, ctx);
|
|
548
759
|
|
|
549
760
|
const recheck = checkAll();
|
|
550
761
|
const stillMissing = recheck.filter((s) => !s.available && (s.dep.tier === "core" || s.dep.tier === "recommended"));
|
|
551
762
|
|
|
552
|
-
if (stillMissing.length === 0) {
|
|
763
|
+
if (stillMissing.length === 0 && hasAnyCloudKey) {
|
|
553
764
|
ctx.ui.notify("\n🎉 Setup complete! All core and recommended dependencies are available.");
|
|
554
765
|
markDone();
|
|
766
|
+
} else if (stillMissing.length === 0) {
|
|
767
|
+
ctx.ui.notify(
|
|
768
|
+
"\n✅ Dependencies installed. Configure an API key (see above) to start using Omegon.",
|
|
769
|
+
);
|
|
770
|
+
markDone();
|
|
555
771
|
} else {
|
|
556
772
|
ctx.ui.notify(
|
|
557
773
|
`\n⚠️ ${stillMissing.length} dep${stillMissing.length > 1 ? "s" : ""} still missing. `
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { existsSync } from "node:fs";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { StringEnum } from "../lib/typebox-helpers";
|
|
16
|
-
import type { ExtensionAPI } from "@
|
|
16
|
+
import type { ExtensionAPI } from "@styrene-lab/pi-coding-agent";
|
|
17
17
|
import { Type } from "@sinclair/typebox";
|
|
18
18
|
|
|
19
19
|
const CHRONOS_SH = join(import.meta.dirname ?? __dirname, "chronos.sh");
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
import { spawn } from "node:child_process";
|
|
20
20
|
import { readFileSync } from "node:fs";
|
|
21
21
|
import { join } from "node:path";
|
|
22
|
-
import type { ExtensionAPI } from "@
|
|
23
|
-
import { DASHBOARD_UPDATE_EVENT, sharedState } from "../shared-state.ts";
|
|
22
|
+
import type { ExtensionAPI } from "@styrene-lab/pi-coding-agent";
|
|
23
|
+
import { DASHBOARD_UPDATE_EVENT, sharedState } from "../lib/shared-state.ts";
|
|
24
24
|
import type { ChildState, CleaveState, ModelTier } from "./types.ts";
|
|
25
25
|
import { computeDispatchWaves } from "./planner.ts";
|
|
26
26
|
import { executeWithReview, type ReviewConfig, type ReviewExecutor, DEFAULT_REVIEW_CONFIG } from "./review.ts";
|
|
27
27
|
import { saveState } from "./workspace.ts";
|
|
28
|
-
import { resolveTier, getDefaultPolicy, type ProviderRoutingPolicy, type RegistryModel } from "../lib/model-routing.ts";
|
|
28
|
+
import { resolveTier, getDefaultPolicy, getViableModels, type ProviderRoutingPolicy, type RegistryModel } from "../lib/model-routing.ts";
|
|
29
29
|
|
|
30
30
|
// ─── Large-run threshold ────────────────────────────────────────────────────
|
|
31
31
|
|
|
@@ -678,14 +678,14 @@ async function dispatchSingleChild(
|
|
|
678
678
|
try {
|
|
679
679
|
const registry = (pi as any).modelRegistry;
|
|
680
680
|
if (registry != null) {
|
|
681
|
-
registryModels = registry
|
|
681
|
+
registryModels = getViableModels(registry);
|
|
682
682
|
}
|
|
683
683
|
// If modelRegistry is absent (e.g. test environment), registryModels stays []
|
|
684
684
|
// and resolveTier will use policy-based fallbacks.
|
|
685
685
|
} catch (err) {
|
|
686
|
-
//
|
|
686
|
+
// getViableModels() threw — log and continue with empty registry so resolver can still
|
|
687
687
|
// apply policy-based fallbacks rather than silently passing no --model flag.
|
|
688
|
-
console.warn("[cleave]
|
|
688
|
+
console.warn("[cleave] getViableModels() threw:", err);
|
|
689
689
|
}
|
|
690
690
|
const modelFlag = resolveModelIdForTier(effectiveTier, registryModels, activePolicy, localModel);
|
|
691
691
|
child.backend = child.executeModel === "local" ? "local" : "cloud";
|
|
@@ -14,18 +14,18 @@
|
|
|
14
14
|
* The Claude Code SDK calls are replaced with pi's extension API.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { ExtensionAPI, ExtensionCommandContext, AgentToolUpdateCallback } from "@
|
|
18
|
-
import { truncateTail, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "@
|
|
17
|
+
import type { ExtensionAPI, ExtensionCommandContext, AgentToolUpdateCallback } from "@styrene-lab/pi-coding-agent";
|
|
18
|
+
import { truncateTail, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "@styrene-lab/pi-coding-agent";
|
|
19
19
|
|
|
20
|
-
import { Text } from "@
|
|
20
|
+
import { Text } from "@styrene-lab/pi-tui";
|
|
21
21
|
import { Type } from "@sinclair/typebox";
|
|
22
22
|
import { spawn, execFile } from "node:child_process";
|
|
23
23
|
import { promisify } from "node:util";
|
|
24
24
|
import { createHash } from "node:crypto";
|
|
25
25
|
|
|
26
|
-
import { sharedState, DASHBOARD_UPDATE_EVENT } from "../shared-state.ts";
|
|
27
|
-
import { sciCall, sciOk, sciErr, sciExpanded } from "../sci-ui.ts";
|
|
28
|
-
import { debug } from "../debug.ts";
|
|
26
|
+
import { sharedState, DASHBOARD_UPDATE_EVENT } from "../lib/shared-state.ts";
|
|
27
|
+
import { sciCall, sciOk, sciErr, sciExpanded } from "../lib/sci-ui.ts";
|
|
28
|
+
import { debug } from "../lib/debug.ts";
|
|
29
29
|
import { emitOpenSpecState } from "../openspec/dashboard-state.ts";
|
|
30
30
|
import { getSharedBridge, buildSlashCommandResult } from "../lib/slash-command-bridge.ts";
|
|
31
31
|
import { buildAssessBridgeResult } from "./bridge.ts";
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { mkdirSync } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { join } from "node:path";
|
|
12
|
-
import type { ExtensionAPI } from "@
|
|
12
|
+
import type { ExtensionAPI } from "@styrene-lab/pi-coding-agent";
|
|
13
13
|
|
|
14
14
|
/** Base directory for cleave worktrees. */
|
|
15
15
|
const WORKTREE_HOME = join(homedir(), ".pi", "cleave", "wt");
|