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
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Core Tool Renderers — Sci-UI rendering for
|
|
2
|
+
* Core Tool Renderers — Sci-UI rendering for Omegon's custom tools.
|
|
3
3
|
*
|
|
4
4
|
* Uses registerToolRenderer() to attach renderCall/renderResult
|
|
5
|
-
* to
|
|
5
|
+
* to tools that have no built-in rendering in pi-mono.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* IMPORTANT: Do NOT register renderers for built-in tools (read, edit, write,
|
|
8
|
+
* bash, grep, find, ls). The built-in renderer provides syntax highlighting,
|
|
9
|
+
* diffs, and streaming output. Registering even just renderCall causes pi to
|
|
10
|
+
* take the custom renderer path, losing all built-in content rendering.
|
|
10
11
|
*/
|
|
11
|
-
import type { ExtensionAPI } from "@
|
|
12
|
-
import { sciCall, sciOk, sciErr, sciLoading } from "./sci-ui.ts";
|
|
12
|
+
import type { ExtensionAPI } from "@styrene-lab/pi-coding-agent";
|
|
13
|
+
import { sciCall, sciOk, sciErr, sciLoading } from "./lib/sci-ui.ts";
|
|
13
14
|
|
|
14
15
|
/** Shorten a file path for display — keep last 2-3 segments. */
|
|
15
16
|
function shortenPath(p: string | null | undefined, maxLen = 55): string {
|
|
@@ -22,83 +23,22 @@ function shortenPath(p: string | null | undefined, maxLen = 55): string {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export default function coreRenderers(pi: ExtensionAPI): void {
|
|
25
|
-
//
|
|
26
|
-
pi.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
pi.registerToolRenderer("edit", {
|
|
42
|
-
renderCall(args: any, theme: any) {
|
|
43
|
-
const p = shortenPath(args?.file_path ?? args?.path);
|
|
44
|
-
// Show the size of the change: lines changed
|
|
45
|
-
const oldLines = (args?.old_text ?? args?.oldText ?? "").split("\n").length;
|
|
46
|
-
const newLines = (args?.new_text ?? args?.newText ?? "").split("\n").length;
|
|
47
|
-
const delta = newLines - oldLines;
|
|
48
|
-
const deltaStr = delta === 0 ? `${oldLines}L` : delta > 0 ? `+${delta}L` : `${delta}L`;
|
|
49
|
-
return sciCall("edit", `${p} (${deltaStr})`, theme);
|
|
50
|
-
},
|
|
51
|
-
// renderResult omitted — built-in handles diff rendering
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// ── Write ─────────────────────────────────────────────────────────────
|
|
55
|
-
pi.registerToolRenderer("write", {
|
|
56
|
-
renderCall(args: any, theme: any) {
|
|
57
|
-
const p = shortenPath(args?.file_path ?? args?.path);
|
|
58
|
-
const content = args?.content ?? "";
|
|
59
|
-
const lines = content.split("\n").length;
|
|
60
|
-
return sciCall("write", `${p} (${lines}L)`, theme);
|
|
61
|
-
},
|
|
62
|
-
// renderResult omitted — built-in handles syntax highlighting
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// ── Bash ──────────────────────────────────────────────────────────────
|
|
66
|
-
pi.registerToolRenderer("bash", {
|
|
67
|
-
renderCall(args: any, theme: any) {
|
|
68
|
-
const cmd = args?.command ?? "";
|
|
69
|
-
// Truncate long commands
|
|
70
|
-
const display = cmd.length > 70 ? cmd.slice(0, 67) + "…" : cmd;
|
|
71
|
-
return sciCall("bash", display, theme);
|
|
72
|
-
},
|
|
73
|
-
// renderResult omitted — built-in handles output display + truncation
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// ── Grep ──────────────────────────────────────────────────────────────
|
|
77
|
-
pi.registerToolRenderer("grep", {
|
|
78
|
-
renderCall(args: any, theme: any) {
|
|
79
|
-
const pattern = args?.pattern ?? "";
|
|
80
|
-
const p = shortenPath(args?.path);
|
|
81
|
-
const glob = args?.glob ? ` (${args.glob})` : "";
|
|
82
|
-
return sciCall("grep", `/${pattern}/ in ${p}${glob}`, theme);
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// ── Find ──────────────────────────────────────────────────────────────
|
|
87
|
-
pi.registerToolRenderer("find", {
|
|
88
|
-
renderCall(args: any, theme: any) {
|
|
89
|
-
const pattern = args?.pattern ?? "";
|
|
90
|
-
const p = shortenPath(args?.path);
|
|
91
|
-
return sciCall("find", `${pattern} in ${p}`, theme);
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// ── Ls ─────────────────────────────────────────────────────────────────
|
|
96
|
-
pi.registerToolRenderer("ls", {
|
|
97
|
-
renderCall(args: any, theme: any) {
|
|
98
|
-
const p = shortenPath(args?.path || ".");
|
|
99
|
-
return sciCall("ls", p, theme);
|
|
100
|
-
},
|
|
101
|
-
});
|
|
26
|
+
// registerToolRenderer was added in pi-mono 0965ae87 — gracefully skip
|
|
27
|
+
// if the published pi version doesn't have it yet.
|
|
28
|
+
if (typeof (pi as any).registerToolRenderer !== "function") {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ─── Built-in tools (read, edit, write, bash, grep, find, ls) ────────
|
|
33
|
+
// NOT registered here. The built-in renderer handles:
|
|
34
|
+
// read → syntax-highlighted code, line ranges, truncation
|
|
35
|
+
// edit → colored diffs with context, line numbers
|
|
36
|
+
// write → syntax-highlighted content preview
|
|
37
|
+
// bash → streaming output, metadata (cwd, duration, exit code)
|
|
38
|
+
// grep → highlighted matches
|
|
39
|
+
// find → directory listing
|
|
40
|
+
// ls → directory listing with limits
|
|
41
|
+
// Registering renderCall would cause pi to skip all of this.
|
|
102
42
|
|
|
103
43
|
// ── View ──────────────────────────────────────────────────────────────
|
|
104
44
|
pi.registerToolRenderer("view", {
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
* Reads ExtensionContext for token stats, model, context usage.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import type { Component } from "@
|
|
14
|
-
import type { Theme, ThemeColor } from "@
|
|
15
|
-
import type { ReadonlyFooterDataProvider } from "@
|
|
16
|
-
import type { ExtensionContext } from "@
|
|
17
|
-
import type { TUI } from "@
|
|
18
|
-
import { truncateToWidth, visibleWidth } from "@
|
|
13
|
+
import type { Component } from "@styrene-lab/pi-tui";
|
|
14
|
+
import type { Theme, ThemeColor } from "@styrene-lab/pi-coding-agent";
|
|
15
|
+
import type { ReadonlyFooterDataProvider } from "@styrene-lab/pi-coding-agent";
|
|
16
|
+
import type { ExtensionContext } from "@styrene-lab/pi-coding-agent";
|
|
17
|
+
import type { TUI } from "@styrene-lab/pi-tui";
|
|
18
|
+
import { truncateToWidth, visibleWidth } from "@styrene-lab/pi-tui";
|
|
19
19
|
import { leftRight, mergeColumns, padRight } from "./render-utils.ts";
|
|
20
20
|
import { buildBranchTreeLines, readLocalBranches } from "./git.ts";
|
|
21
|
-
import type { DashboardState, RecoveryCooldownSummary, RecoveryDashboardState } from "./types.ts";
|
|
22
|
-
import { sharedState } from "../shared-state.ts";
|
|
23
|
-
import { debug } from "../debug.ts";
|
|
21
|
+
import type { DashboardModelRoleSummary, DashboardState, RecoveryCooldownSummary, RecoveryDashboardState } from "./types.ts";
|
|
22
|
+
import { sharedState } from "../lib/shared-state.ts";
|
|
23
|
+
import { debug } from "../lib/debug.ts";
|
|
24
24
|
import { linkDashboardFile, linkOpenSpecArtifact, linkOpenSpecChange } from "./uri-helper.ts";
|
|
25
25
|
import { designSpecBadge } from "./overlay-data.ts";
|
|
26
26
|
import { buildContextGaugeModel } from "./context-gauge.ts";
|
|
@@ -91,6 +91,8 @@ function summarizeCooldown(cooldowns: RecoveryCooldownSummary[] | undefined): st
|
|
|
91
91
|
const CLEAVE_STALE_MS = 30_000;
|
|
92
92
|
/** Recovery notices auto-suppress in compact mode after this many ms with no new error. */
|
|
93
93
|
const RECOVERY_STALE_MS = 45_000;
|
|
94
|
+
const RAISED_NARROW_WIDTH = 100;
|
|
95
|
+
const RAISED_WIDE_WIDTH = 140;
|
|
94
96
|
|
|
95
97
|
type PrioritySegment = {
|
|
96
98
|
text: string;
|
|
@@ -141,6 +143,16 @@ function composePrimaryMetaLine(
|
|
|
141
143
|
], separator);
|
|
142
144
|
}
|
|
143
145
|
|
|
146
|
+
function normalizeLocalModelLabel(model: string): { canonical: string; alias?: string } {
|
|
147
|
+
if (model === "devstral-small-2:24b") {
|
|
148
|
+
return { canonical: "Devstral 24B", alias: "devstral-small-2:24b" };
|
|
149
|
+
}
|
|
150
|
+
if (model === "devstral:24b") {
|
|
151
|
+
return { canonical: "Devstral 24B" };
|
|
152
|
+
}
|
|
153
|
+
return { canonical: model };
|
|
154
|
+
}
|
|
155
|
+
|
|
144
156
|
export class DashboardFooter implements Component {
|
|
145
157
|
private tui: TUI;
|
|
146
158
|
private theme: Theme;
|
|
@@ -221,7 +233,8 @@ export class DashboardFooter implements Component {
|
|
|
221
233
|
if (dt && dt.nodeCount > 0) {
|
|
222
234
|
if (ultraWide && dt.focusedNode) {
|
|
223
235
|
// Ultra-wide: show focused node title inline
|
|
224
|
-
const statusIcon = dt.focusedNode.status === "
|
|
236
|
+
const statusIcon = dt.focusedNode.status === "resolved" ? "◉"
|
|
237
|
+
: dt.focusedNode.status === "decided" ? "●"
|
|
225
238
|
: dt.focusedNode.status === "implementing" ? "⚙"
|
|
226
239
|
: dt.focusedNode.status === "exploring" ? "◐"
|
|
227
240
|
: "○";
|
|
@@ -352,7 +365,9 @@ export class DashboardFooter implements Component {
|
|
|
352
365
|
// ── Raised Mode (Layer 1) ─────────────────────────────────────
|
|
353
366
|
|
|
354
367
|
private renderRaised(width: number): string[] {
|
|
355
|
-
|
|
368
|
+
if (width < RAISED_NARROW_WIDTH) return this.renderRaisedNarrow(width);
|
|
369
|
+
if (width < RAISED_WIDE_WIDTH) return this.renderRaisedMedium(width);
|
|
370
|
+
return this.renderRaisedWide(width);
|
|
356
371
|
}
|
|
357
372
|
|
|
358
373
|
/**
|
|
@@ -451,10 +466,10 @@ export class DashboardFooter implements Component {
|
|
|
451
466
|
}
|
|
452
467
|
|
|
453
468
|
/**
|
|
454
|
-
*
|
|
455
|
-
*
|
|
469
|
+
* Narrow layout for terminals under 100 cols.
|
|
470
|
+
* Lifecycle/work stays above; lower dashboard uses stacked summary cards.
|
|
456
471
|
*/
|
|
457
|
-
private
|
|
472
|
+
private renderRaisedNarrow(width: number): string[] {
|
|
458
473
|
const innerWidth = width - 4;
|
|
459
474
|
const branchLines = this.buildBranchTree(innerWidth);
|
|
460
475
|
const [topLine = "", ...extraBranchLines] = branchLines;
|
|
@@ -480,12 +495,9 @@ export class DashboardFooter implements Component {
|
|
|
480
495
|
}
|
|
481
496
|
|
|
482
497
|
/**
|
|
483
|
-
*
|
|
484
|
-
* Left: Design tree + Recovery + Cleave (active work context)
|
|
485
|
-
* Right: Implementation (spec/task progress)
|
|
486
|
-
* Footer zone: shared meta, memory, footer data
|
|
498
|
+
* Medium layout (100–139 cols) — two-column work area with compact summary cards below.
|
|
487
499
|
*/
|
|
488
|
-
private
|
|
500
|
+
private renderRaisedMedium(width: number): string[] {
|
|
489
501
|
const innerWidth = width - 4;
|
|
490
502
|
const leftColWidth = Math.floor((innerWidth - 1) / 2);
|
|
491
503
|
const rightColWidth = innerWidth - leftColWidth - 1;
|
|
@@ -515,6 +527,37 @@ export class DashboardFooter implements Component {
|
|
|
515
527
|
return this.renderBoxed(contentLines, this.buildFooterZone(innerWidth), topLine, width);
|
|
516
528
|
}
|
|
517
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Wide layout (140+ cols) keeps the same work summary above but gives the
|
|
532
|
+
* lower footer zone enough width to render distinct horizontal summary cards.
|
|
533
|
+
*/
|
|
534
|
+
private renderRaisedWide(width: number): string[] {
|
|
535
|
+
const innerWidth = width - 4;
|
|
536
|
+
const leftColWidth = Math.floor((innerWidth - 1) / 2);
|
|
537
|
+
const rightColWidth = innerWidth - leftColWidth - 1;
|
|
538
|
+
const colDivider = this.theme.fg("dim", BOX.v);
|
|
539
|
+
|
|
540
|
+
const branchLines = this.buildBranchTree(innerWidth);
|
|
541
|
+
const [topLine = "", ...extraBranchLines] = branchLines;
|
|
542
|
+
const alignedBranchLines = extraBranchLines.map((l) => " " + l);
|
|
543
|
+
|
|
544
|
+
const leftLines = [
|
|
545
|
+
...this.buildDesignTreeLines(leftColWidth),
|
|
546
|
+
...this.buildRecoveryLines(leftColWidth),
|
|
547
|
+
...this.buildCleaveLines(leftColWidth),
|
|
548
|
+
];
|
|
549
|
+
const rightLines = this.buildOpenSpecLines(rightColWidth);
|
|
550
|
+
|
|
551
|
+
const contentLines: string[] = [
|
|
552
|
+
...alignedBranchLines,
|
|
553
|
+
...(leftLines.length > 0 || rightLines.length > 0
|
|
554
|
+
? mergeColumns(leftLines, rightLines, leftColWidth, rightColWidth, colDivider)
|
|
555
|
+
: []),
|
|
556
|
+
];
|
|
557
|
+
|
|
558
|
+
return this.renderBoxed(contentLines, this.buildFooterZone(innerWidth), topLine, width);
|
|
559
|
+
}
|
|
560
|
+
|
|
518
561
|
// ── HUD Footer Zone (raised mode) ────────────────────────────
|
|
519
562
|
|
|
520
563
|
/**
|
|
@@ -710,35 +753,135 @@ export class DashboardFooter implements Component {
|
|
|
710
753
|
return lines;
|
|
711
754
|
}
|
|
712
755
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
756
|
+
private buildModelTopologySummaries(): DashboardModelRoleSummary[] {
|
|
757
|
+
const ctx = this.ctxRef;
|
|
758
|
+
const summaries: DashboardModelRoleSummary[] = [];
|
|
759
|
+
const memoryStatus = this.footerData.getExtensionStatuses().get("memory") ?? "";
|
|
760
|
+
const offlineStatus = this.footerData.getExtensionStatuses().get("offline-driver") ?? "";
|
|
761
|
+
|
|
762
|
+
if (ctx?.model) {
|
|
763
|
+
summaries.push({
|
|
764
|
+
role: "driver",
|
|
765
|
+
label: "Driver",
|
|
766
|
+
model: ctx.model.id,
|
|
767
|
+
source: ctx.model.provider === "local" ? "local" : "cloud",
|
|
768
|
+
state: offlineStatus.includes("OFFLINE:") ? "offline" : "active",
|
|
769
|
+
detail: this.footerData.getAvailableProviderCount() > 1 ? ctx.model.provider : undefined,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
720
772
|
|
|
721
|
-
const
|
|
773
|
+
const effort = sharedState.effort;
|
|
774
|
+
if (memoryStatus || effort?.resolvedExtractionModelId) {
|
|
775
|
+
const extractionModel = effort?.resolvedExtractionModelId ?? effort?.extraction ?? "?";
|
|
776
|
+
const extractionLocal = extractionModel.includes(":") || effort?.extraction === "local";
|
|
777
|
+
summaries.push({
|
|
778
|
+
role: "extraction",
|
|
779
|
+
label: "Extraction",
|
|
780
|
+
model: extractionModel,
|
|
781
|
+
source: extractionLocal ? "local" : "cloud",
|
|
782
|
+
state: extractionLocal ? "ready" : "active",
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (memoryStatus) {
|
|
787
|
+
const embedMatch = memoryStatus.match(/semantic/i);
|
|
788
|
+
summaries.push({
|
|
789
|
+
role: "embeddings",
|
|
790
|
+
label: "Embeddings",
|
|
791
|
+
model: embedMatch ? "semantic retrieval" : "available",
|
|
792
|
+
source: "unknown",
|
|
793
|
+
state: "ready",
|
|
794
|
+
});
|
|
795
|
+
}
|
|
722
796
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
797
|
+
if (offlineStatus.includes("OFFLINE:")) {
|
|
798
|
+
const raw = sanitizeStatusText(offlineStatus).replace(/^.*OFFLINE:\s*/i, "");
|
|
799
|
+
summaries.push({
|
|
800
|
+
role: "fallback",
|
|
801
|
+
label: "Fallback",
|
|
802
|
+
model: raw || "local fallback",
|
|
803
|
+
source: "local",
|
|
804
|
+
state: "offline",
|
|
805
|
+
});
|
|
727
806
|
}
|
|
728
807
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
808
|
+
return summaries;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
private formatModelTopologyLine(summary: DashboardModelRoleSummary, width: number, compact = false): string {
|
|
812
|
+
const theme = this.theme;
|
|
813
|
+
const sourceBadge = summary.source === "local"
|
|
814
|
+
? theme.fg("accent", "local")
|
|
815
|
+
: summary.source === "cloud"
|
|
816
|
+
? theme.fg("muted", "cloud")
|
|
817
|
+
: theme.fg("dim", summary.source);
|
|
818
|
+
const stateBadge = summary.state === "active"
|
|
819
|
+
? theme.fg("success", "active")
|
|
820
|
+
: summary.state === "offline"
|
|
821
|
+
? theme.fg("warning", "offline")
|
|
822
|
+
: summary.state === "fallback"
|
|
823
|
+
? theme.fg("warning", "fallback")
|
|
824
|
+
: theme.fg("dim", summary.state);
|
|
825
|
+
const normalized = normalizeLocalModelLabel(summary.model);
|
|
826
|
+
const alias = normalized.alias ? theme.fg("dim", `alias ${normalized.alias}`) : "";
|
|
827
|
+
const primary = compact
|
|
828
|
+
? `${theme.fg("accent", summary.label)} ${theme.fg("muted", normalized.canonical)}`
|
|
829
|
+
: `${theme.fg("accent", summary.label)} ${theme.fg("dim", "·")} ${theme.fg("muted", normalized.canonical)}`;
|
|
830
|
+
return truncateToWidth(composePrimaryMetaLine(width, primary, [sourceBadge, stateBadge, summary.detail ? theme.fg("dim", summary.detail) : "", alias]), width, "…");
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
private buildSummaryCard(title: string, lines: string[], width: number): string[] {
|
|
834
|
+
if (lines.length === 0) return [];
|
|
835
|
+
return [this.buildHudSectionDivider(title, width), ...lines.map((line) => truncateToWidth(` ${line}`, width, "…"))];
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
private buildFooterZone(width: number): string[] {
|
|
839
|
+
this._updateTokenCache();
|
|
840
|
+
|
|
841
|
+
const contextCard = this.buildSummaryCard("context", this.buildHudContextLines(Math.max(1, width - 2)).map((l) => l.trimStart()), width);
|
|
842
|
+
const modelCard = this.buildSummaryCard(
|
|
843
|
+
"models",
|
|
844
|
+
this.buildModelTopologySummaries().map((s) => this.formatModelTopologyLine(s, Math.max(1, width - 2), width < 70)),
|
|
845
|
+
width,
|
|
846
|
+
);
|
|
847
|
+
const memoryCard = this.buildSummaryCard("memory", (() => {
|
|
848
|
+
const line = this.buildHudMemoryLine(Math.max(1, width - 2));
|
|
849
|
+
return line ? [line.trimStart()] : [];
|
|
850
|
+
})(), width);
|
|
851
|
+
const systemCard = this.buildSummaryCard("system", this.buildHudSystemLines(Math.max(1, width - 2)).map((l) => l.trimStart()), width);
|
|
852
|
+
const recoveryCard = this.buildSummaryCard("recovery", this.buildRecoveryLines(Math.max(1, width - 2)).map((l) => l.trimStart()), width);
|
|
853
|
+
|
|
854
|
+
if (width < RAISED_NARROW_WIDTH) {
|
|
855
|
+
return [
|
|
856
|
+
...contextCard,
|
|
857
|
+
...modelCard,
|
|
858
|
+
...memoryCard,
|
|
859
|
+
...(recoveryCard.length > 0 ? recoveryCard : []),
|
|
860
|
+
...systemCard,
|
|
861
|
+
];
|
|
733
862
|
}
|
|
734
863
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
864
|
+
if (width < RAISED_WIDE_WIDTH) {
|
|
865
|
+
const left = [...contextCard, ...memoryCard];
|
|
866
|
+
const right = [...modelCard, ...(recoveryCard.length > 0 ? recoveryCard : []), ...systemCard];
|
|
867
|
+
const colWidth = Math.floor((width - 1) / 2);
|
|
868
|
+
const rightWidth = width - colWidth - 1;
|
|
869
|
+
return mergeColumns(left, right, colWidth, rightWidth, this.theme.fg("dim", BOX.v));
|
|
739
870
|
}
|
|
740
871
|
|
|
741
|
-
|
|
872
|
+
const cards = [contextCard, modelCard, memoryCard, recoveryCard.length > 0 ? recoveryCard : systemCard];
|
|
873
|
+
const totalCols = cards.length;
|
|
874
|
+
const colWidth = Math.floor((width - (totalCols - 1)) / totalCols);
|
|
875
|
+
const divider = this.theme.fg("dim", BOX.v);
|
|
876
|
+
let merged = cards[0] ?? [];
|
|
877
|
+
let usedWidth = colWidth;
|
|
878
|
+
for (let i = 1; i < cards.length; i++) {
|
|
879
|
+
const remainingCols = totalCols - i;
|
|
880
|
+
const nextWidth = i === cards.length - 1 ? width - usedWidth - 1 : colWidth;
|
|
881
|
+
merged = mergeColumns(merged, cards[i] ?? [], usedWidth, nextWidth, divider);
|
|
882
|
+
usedWidth += 1 + nextWidth;
|
|
883
|
+
}
|
|
884
|
+
return merged;
|
|
742
885
|
}
|
|
743
886
|
|
|
744
887
|
// ── Section builders (shared by stacked + wide layouts) ───────
|
|
@@ -938,6 +1081,7 @@ export class DashboardFooter implements Component {
|
|
|
938
1081
|
private nodeStatusIcon(status: string): string {
|
|
939
1082
|
const theme = this.theme;
|
|
940
1083
|
switch (status) {
|
|
1084
|
+
case "resolved": return theme.fg("success", "◉");
|
|
941
1085
|
case "decided": return theme.fg("success", "●");
|
|
942
1086
|
case "implementing": return theme.fg("accent", "⚙");
|
|
943
1087
|
case "implemented": return theme.fg("success", "✓");
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
-
import { visibleWidth } from "@
|
|
11
|
-
import type { Theme } from "@
|
|
10
|
+
import { visibleWidth } from "@styrene-lab/pi-tui";
|
|
11
|
+
import type { Theme } from "@styrene-lab/pi-coding-agent";
|
|
12
12
|
|
|
13
13
|
// Shared ASCII-compat flag — same logic as footer.ts
|
|
14
14
|
const useAscii = (() => {
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
* Subscribes to "dashboard:update" events for live re-rendering.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { ExtensionAPI, ExtensionContext } from "@
|
|
18
|
-
import type { OverlayHandle } from "@
|
|
19
|
-
import { DASHBOARD_UPDATE_EVENT } from "../shared-state.ts";
|
|
17
|
+
import type { ExtensionAPI, ExtensionContext } from "@styrene-lab/pi-coding-agent";
|
|
18
|
+
import type { OverlayHandle } from "@styrene-lab/pi-tui";
|
|
19
|
+
import { DASHBOARD_UPDATE_EVENT } from "../lib/shared-state.ts";
|
|
20
20
|
import { getSharedBridge, buildSlashCommandResult } from "../lib/slash-command-bridge.ts";
|
|
21
21
|
import { DashboardFooter } from "./footer.ts";
|
|
22
22
|
import { DashboardOverlay, showDashboardOverlay } from "./overlay.ts";
|
|
23
23
|
import type { DashboardState, DashboardMode } from "./types.ts";
|
|
24
|
-
import { debug } from "../debug.ts";
|
|
24
|
+
import { debug } from "../lib/debug.ts";
|
|
25
25
|
|
|
26
26
|
/** Valid /dashboard subcommands for tab completion (legacy) */
|
|
27
27
|
const DASHBOARD_SUBCOMMANDS = ["compact", "raised", "panel", "focus", "open"];
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* All rendering/theme concerns are abstracted via the ThemeFn callback.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { sharedState } from "../shared-state.ts";
|
|
8
|
+
import { sharedState } from "../lib/shared-state.ts";
|
|
9
9
|
import type { CleaveState, DesignAssessmentResult, DesignSpecBindingState, DesignTreeDashboardState, OpenSpecDashboardState } from "./types.ts";
|
|
10
10
|
import type { ProviderRoutingPolicy } from "../lib/model-routing.ts";
|
|
11
11
|
import {
|
|
@@ -527,10 +527,10 @@ function effortItems(effort: any | undefined, expandedKeys: Set<string>): ListIt
|
|
|
527
527
|
if (!expandedKeys.has(key)) return items;
|
|
528
528
|
|
|
529
529
|
const fields: Array<[string, string]> = [
|
|
530
|
-
["level",
|
|
531
|
-
["driver",
|
|
532
|
-
["
|
|
533
|
-
["thinking",
|
|
530
|
+
["level", String(effort.level ?? "?")],
|
|
531
|
+
["driver", effort.driverModel ?? "?"],
|
|
532
|
+
["extraction", effort.extractionModel ?? effort.resolvedExtractionModelId ?? "?"],
|
|
533
|
+
["thinking", effort.thinkingLevel ?? "?"],
|
|
534
534
|
];
|
|
535
535
|
for (const [label, val] of fields) {
|
|
536
536
|
items.push({
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { spawn } from "node:child_process";
|
|
20
|
-
import type { ExtensionContext } from "@
|
|
21
|
-
import type { Theme } from "@
|
|
22
|
-
import type { TUI } from "@
|
|
23
|
-
import { matchesKey, truncateToWidth, visibleWidth } from "@
|
|
24
|
-
import { DASHBOARD_UPDATE_EVENT, sharedState } from "../shared-state.ts";
|
|
20
|
+
import type { ExtensionContext } from "@styrene-lab/pi-coding-agent";
|
|
21
|
+
import type { Theme } from "@styrene-lab/pi-coding-agent";
|
|
22
|
+
import type { TUI } from "@styrene-lab/pi-tui";
|
|
23
|
+
import { matchesKey, truncateToWidth, visibleWidth } from "@styrene-lab/pi-tui";
|
|
24
|
+
import { DASHBOARD_UPDATE_EVENT, sharedState } from "../lib/shared-state.ts";
|
|
25
25
|
import {
|
|
26
26
|
TABS,
|
|
27
27
|
MAX_CONTENT_LINES,
|
|
@@ -180,6 +180,21 @@ export interface RecoveryDashboardState {
|
|
|
180
180
|
cooldowns?: RecoveryCooldownSummary[];
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
// ── Dashboard model-topology summaries ──────────────────────
|
|
184
|
+
|
|
185
|
+
export type DashboardModelRole = "driver" | "embeddings" | "extraction" | "fallback";
|
|
186
|
+
export type DashboardModelSource = "cloud" | "local" | "mixed" | "unknown";
|
|
187
|
+
export type DashboardModelState = "active" | "ready" | "fallback" | "offline" | "legacy-alias" | "unknown";
|
|
188
|
+
|
|
189
|
+
export interface DashboardModelRoleSummary {
|
|
190
|
+
role: DashboardModelRole;
|
|
191
|
+
label: string;
|
|
192
|
+
model: string;
|
|
193
|
+
source: DashboardModelSource;
|
|
194
|
+
state: DashboardModelState;
|
|
195
|
+
detail?: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
183
198
|
// ── Dashboard UI ─────────────────────────────────────────────
|
|
184
199
|
|
|
185
200
|
export type DashboardMode = "compact" | "raised" | "panel" | "focused";
|
package/extensions/defaults.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
import * as crypto from "node:crypto";
|
|
15
|
-
import type { ExtensionAPI } from "@
|
|
15
|
+
import type { ExtensionAPI } from "@styrene-lab/pi-coding-agent";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Resolve the agent directory the same way pi's getAgentDir() does.
|
|
@@ -161,17 +161,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
161
161
|
syncKittyTheme(() => {}); // silent in non-UI mode (e.g. pi -p children)
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
if (ctx.hasUI) {
|
|
168
|
-
const sessionName = ctx.sessionManager.getSessionName();
|
|
169
|
-
const cwdBasename = path.basename(ctx.cwd);
|
|
170
|
-
const title = sessionName
|
|
171
|
-
? `Ω - ${sessionName} - ${cwdBasename}`
|
|
172
|
-
: `Ω - ${cwdBasename}`;
|
|
173
|
-
ctx.ui.setTitle(title);
|
|
174
|
-
}
|
|
164
|
+
// Terminal title is owned entirely by extensions/terminal-title.ts.
|
|
165
|
+
// Do NOT set title here — it creates a startup flash before the dynamic
|
|
166
|
+
// title takes over, and the branding prefix was inconsistent (Ω here vs π there).
|
|
175
167
|
|
|
176
168
|
// --- Theme default ---
|
|
177
169
|
try {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
|
|
4
|
-
import type { ExtensionAPI } from "@
|
|
4
|
+
import type { ExtensionAPI } from "@styrene-lab/pi-coding-agent";
|
|
5
5
|
|
|
6
6
|
import type { DesignNode, DesignTree } from "./types.ts";
|
|
7
7
|
import { getAllOpenQuestions, countAcceptanceCriteria } from "./tree.ts";
|
|
8
|
-
import { sharedState, DASHBOARD_UPDATE_EVENT } from "../shared-state.ts";
|
|
9
|
-
import type { DesignTreeDashboardState } from "../shared-state.ts";
|
|
8
|
+
import { sharedState, DASHBOARD_UPDATE_EVENT } from "../lib/shared-state.ts";
|
|
9
|
+
import type { DesignTreeDashboardState } from "../lib/shared-state.ts";
|
|
10
10
|
import type { DesignAssessmentResult, DesignPipelineCounts } from "../dashboard/types.ts";
|
|
11
11
|
import type { DesignSpecBinding } from "../openspec/archive-gate.ts";
|
|
12
|
-
import { debug } from "../debug.ts";
|
|
12
|
+
import { debug } from "../lib/debug.ts";
|
|
13
13
|
|
|
14
14
|
/** Read assessment.json from openspec/design/<id>/assessment.json if it exists. */
|
|
15
15
|
function readAssessmentResult(cwd: string, nodeId: string): DesignAssessmentResult | null {
|
|
@@ -75,7 +75,7 @@ export function emitDesignTreeState(pi: ExtensionAPI, dt: DesignTree, focused: D
|
|
|
75
75
|
|
|
76
76
|
const enrichedNodes = nodes.map((n) => {
|
|
77
77
|
const isSeedLike = n.status === "seed";
|
|
78
|
-
const isActivePhase = ["exploring", "decided", "implementing"].includes(n.status);
|
|
78
|
+
const isActivePhase = ["exploring", "resolved", "decided", "implementing"].includes(n.status);
|
|
79
79
|
// W3 fix: deferred/blocked also receive the neutral sentinel (not undefined)
|
|
80
80
|
const isPassive = n.status === "deferred" || n.status === "blocked";
|
|
81
81
|
|
|
@@ -97,7 +97,7 @@ export function emitDesignTreeState(pi: ExtensionAPI, dt: DesignTree, focused: D
|
|
|
97
97
|
|
|
98
98
|
// Accumulate pipeline counts
|
|
99
99
|
// C3 fix: deferred/blocked fall into needsSpec so funnel totals reconcile
|
|
100
|
-
if (n.status === "decided") {
|
|
100
|
+
if (n.status === "decided" || n.status === "resolved") {
|
|
101
101
|
pipelineCounts.decided++;
|
|
102
102
|
} else if (n.status === "implementing") {
|
|
103
103
|
pipelineCounts.implementing++;
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
* ╰──────────────────────────────────────────────────────────────────
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@
|
|
30
|
-
import type { Component } from "@
|
|
31
|
-
import type { Theme } from "@
|
|
29
|
+
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@styrene-lab/pi-tui";
|
|
30
|
+
import type { Component } from "@styrene-lab/pi-tui";
|
|
31
|
+
import type { Theme } from "@styrene-lab/pi-coding-agent";
|
|
32
32
|
import { STATUS_ICONS, STATUS_COLORS, ISSUE_TYPE_ICONS, PRIORITY_LABELS } from "./types.ts";
|
|
33
33
|
import type { NodeStatus, IssueType, Priority } from "./types.ts";
|
|
34
34
|
|