@opengsd/gsd-pi 1.1.1-dev.b2556262 → 1.2.0-dev.844675c9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/project-sessions.js +4 -2
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +17 -9
- package/dist/resources/extensions/gsd/auto/contracts.js +8 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +659 -57
- package/dist/resources/extensions/gsd/auto-prompts.js +110 -1
- package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +5 -0
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +29 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +24 -17
- package/dist/resources/extensions/gsd/auto.js +62 -464
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -1
- package/dist/resources/extensions/gsd/debug-logger.js +10 -0
- package/dist/resources/extensions/gsd/doctor-proactive.js +7 -2
- package/dist/resources/extensions/gsd/guided-flow.js +2 -2
- package/dist/resources/extensions/gsd/markdown-renderer.js +31 -32
- package/dist/resources/extensions/gsd/mcp-filter.js +6 -0
- package/dist/resources/extensions/gsd/native-git-bridge.js +45 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +6 -7
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -5
- package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +2 -2
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
- package/dist/resources/extensions/gsd/schemas/parsers.js +6 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +21 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +169 -20
- package/dist/resources/extensions/gsd/user-input-boundary.js +42 -4
- package/dist/tsconfig.extensions.tsbuildinfo +1 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/chunks/5047.js +2 -0
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -0
- package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/container.js +26 -18
- package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +47 -14
- package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
- package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
- package/dist/web/standalone/node_modules/postcss/lib/input.js +54 -29
- package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +47 -37
- package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +26 -9
- package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +57 -55
- package/dist/web/standalone/node_modules/postcss/lib/node.js +99 -31
- package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/parser.js +10 -9
- package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
- package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +30 -11
- package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
- package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
- package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
- package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +69 -28
- package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +6 -2
- package/dist/web/standalone/node_modules/postcss/package.json +48 -48
- package/package.json +16 -11
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +0 -34
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +12 -46
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +11 -3
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/scripts/install/deps.js +10 -0
- package/scripts/link-workspace-packages.cjs +7 -40
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -8
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +2 -2
- package/src/resources/extensions/gsd/auto/contracts.ts +8 -119
- package/src/resources/extensions/gsd/auto/orchestrator.ts +794 -58
- package/src/resources/extensions/gsd/auto-prompts.ts +114 -1
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +5 -0
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +33 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +24 -16
- package/src/resources/extensions/gsd/auto.ts +81 -500
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -0
- package/src/resources/extensions/gsd/debug-logger.ts +11 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +8 -2
- package/src/resources/extensions/gsd/guided-flow.ts +2 -2
- package/src/resources/extensions/gsd/markdown-renderer.ts +38 -19
- package/src/resources/extensions/gsd/mcp-filter.ts +7 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +48 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +6 -7
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -5
- package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +2 -2
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
- package/src/resources/extensions/gsd/schemas/parsers.ts +6 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +31 -10
- package/src/resources/extensions/gsd/tests/artifact-db-drift-memo.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/auto-dispatch-baseline-harness.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +590 -855
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +38 -10
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +64 -1
- package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer-parse-cache.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/native-merge-regular.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/orchestrator-legacy-parity.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/parse-project-milestone-bridge.test.ts +77 -0
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +29 -2
- package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +19 -5
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -3
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +183 -21
- package/src/resources/extensions/gsd/user-input-boundary.ts +37 -5
- package/dist/web/standalone/.next/server/chunks/678.js +0 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +0 -21
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +0 -213
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.d.ts +0 -28
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.d.ts.map +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.js +0 -249
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.js.map +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.d.ts +0 -19
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.d.ts.map +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.js +0 -797
- package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.js.map +0 -1
- package/scripts/ensure-workspace-builds.cjs +0 -129
- /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → Qbr81pQ-pbQXP4bq2VXLv}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → Qbr81pQ-pbQXP4bq2VXLv}/_ssgManifest.js +0 -0
|
@@ -6,10 +6,11 @@ import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-
|
|
|
6
6
|
import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
|
|
7
7
|
import {
|
|
8
8
|
getActiveRequirements,
|
|
9
|
-
|
|
9
|
+
getAllMilestones,
|
|
10
10
|
getMilestone,
|
|
11
11
|
getSliceStatusSummary,
|
|
12
12
|
getSliceTaskCounts,
|
|
13
|
+
insertMilestone,
|
|
13
14
|
insertAssessment,
|
|
14
15
|
insertGateRun,
|
|
15
16
|
readTransaction,
|
|
@@ -126,6 +127,111 @@ function registerProjectMilestoneSequence(content: string): string[] {
|
|
|
126
127
|
return registered;
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
/** Minimal shape of a DB milestone row needed to re-render the sequence section. */
|
|
131
|
+
interface MilestoneSeqRow {
|
|
132
|
+
id: string;
|
|
133
|
+
title: string;
|
|
134
|
+
status: string;
|
|
135
|
+
vision: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Best-effort recovery of the human one-liner for each milestone id from a
|
|
140
|
+
* (possibly malformed) Milestone Sequence body. Deliberately lenient: tolerates
|
|
141
|
+
* any separator the canonical MILESTONE_LINE_RE rejects (en-dash, " : ", a
|
|
142
|
+
* missing checkbox, etc.) so a model formatting slip does not discard the prose.
|
|
143
|
+
*/
|
|
144
|
+
function recoverMilestoneTails(sequenceBody: string): Map<string, string> {
|
|
145
|
+
const out = new Map<string, string>();
|
|
146
|
+
const lenient = /^\s*(?:-\s*)?(?:\[[ xX]\]\s*)?(M\d{3})\b\s*[:.\-–—]*\s*(.*)$/;
|
|
147
|
+
for (const rawLine of sequenceBody.split("\n")) {
|
|
148
|
+
const m = rawLine.match(lenient);
|
|
149
|
+
if (m) out.set(m[1], m[2].trim());
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function firstSentence(text: string): string {
|
|
155
|
+
const trimmed = text.trim();
|
|
156
|
+
if (!trimmed) return "";
|
|
157
|
+
const idx = trimmed.search(/[.!?](\s|$)/);
|
|
158
|
+
return (idx >= 0 ? trimmed.slice(0, idx + 1) : trimmed).trim();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Render one canonical, parseable milestone line for the given DB row. */
|
|
162
|
+
function renderMilestoneLine(m: MilestoneSeqRow, recoveredTail: string): string {
|
|
163
|
+
const done = m.status === "complete";
|
|
164
|
+
let oneLiner = recoveredTail;
|
|
165
|
+
// The recovered tail often still carries the title (e.g. "Foo — bar" or
|
|
166
|
+
// "Foo : bar"). Strip a leading repetition of the title, then any separator.
|
|
167
|
+
if (oneLiner.toLowerCase().startsWith(m.title.toLowerCase())) {
|
|
168
|
+
oneLiner = oneLiner.slice(m.title.length).replace(/^\s*[:.\-–—]+\s*/, "").trim();
|
|
169
|
+
} else {
|
|
170
|
+
const sep = oneLiner.match(/\s+(?:—|–|--|-|:)\s+/);
|
|
171
|
+
if (sep && sep.index !== undefined) oneLiner = oneLiner.slice(sep.index + sep[0].length).trim();
|
|
172
|
+
}
|
|
173
|
+
// MILESTONE_LINE_RE requires non-empty prose after the separator.
|
|
174
|
+
if (!oneLiner) oneLiner = firstSentence(m.vision) || (done ? "Completed." : "Planned.");
|
|
175
|
+
return `- [${done ? "x" : " "}] ${m.id}: ${m.title} — ${oneLiner}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Rebuild the "## Milestone Sequence" section from authoritative DB rows when a
|
|
180
|
+
* model-authored PROJECT.md projection parsed to zero milestone lines but the DB
|
|
181
|
+
* already holds milestones. The DB is the source of truth (markdown is a
|
|
182
|
+
* projection), so this repairs the projection rather than failing the save.
|
|
183
|
+
* Preserves a leading HTML comment in the section and recovers one-liners
|
|
184
|
+
* best-effort. The returned content parses cleanly under MILESTONE_LINE_RE.
|
|
185
|
+
*/
|
|
186
|
+
function rebuildMilestoneSequenceSection(content: string, milestones: MilestoneSeqRow[]): string {
|
|
187
|
+
const lines = content.split("\n");
|
|
188
|
+
const headerIdx = lines.findIndex(l => /^##\s+Milestone Sequence\s*$/.test(l));
|
|
189
|
+
|
|
190
|
+
const canonicalLines = (() => {
|
|
191
|
+
// Recover tails from the existing (malformed) body when the section exists.
|
|
192
|
+
let body = "";
|
|
193
|
+
if (headerIdx !== -1) {
|
|
194
|
+
let end = headerIdx + 1;
|
|
195
|
+
while (end < lines.length && !/^##\s+/.test(lines[end])) end++;
|
|
196
|
+
body = lines.slice(headerIdx + 1, end).join("\n");
|
|
197
|
+
}
|
|
198
|
+
const tails = recoverMilestoneTails(body);
|
|
199
|
+
return milestones.map(m => renderMilestoneLine(m, tails.get(m.id) ?? ""));
|
|
200
|
+
})();
|
|
201
|
+
|
|
202
|
+
if (headerIdx === -1) {
|
|
203
|
+
// No section at all — append a fresh, canonical one.
|
|
204
|
+
const sep = content.endsWith("\n") ? "" : "\n";
|
|
205
|
+
return `${content}${sep}\n## Milestone Sequence\n\n${canonicalLines.join("\n")}\n`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let bodyEnd = headerIdx + 1;
|
|
209
|
+
while (bodyEnd < lines.length && !/^##\s+/.test(lines[bodyEnd])) bodyEnd++;
|
|
210
|
+
const existingBody = lines.slice(headerIdx + 1, bodyEnd);
|
|
211
|
+
|
|
212
|
+
// Preserve a contiguous leading HTML comment block (the "Check off…" hint).
|
|
213
|
+
let i = 0;
|
|
214
|
+
while (i < existingBody.length && existingBody[i].trim() === "") i++;
|
|
215
|
+
const preserved: string[] = [];
|
|
216
|
+
if (i < existingBody.length && existingBody[i].trim().startsWith("<!--")) {
|
|
217
|
+
while (i < existingBody.length) {
|
|
218
|
+
preserved.push(existingBody[i]);
|
|
219
|
+
const closed = existingBody[i].includes("-->");
|
|
220
|
+
i++;
|
|
221
|
+
if (closed) break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return [
|
|
226
|
+
...lines.slice(0, headerIdx + 1),
|
|
227
|
+
"",
|
|
228
|
+
...(preserved.length ? [...preserved, ""] : []),
|
|
229
|
+
...canonicalLines,
|
|
230
|
+
"",
|
|
231
|
+
...lines.slice(bodyEnd),
|
|
232
|
+
].join("\n");
|
|
233
|
+
}
|
|
234
|
+
|
|
129
235
|
async function mirrorArtifactToActiveWorktreeProjection(
|
|
130
236
|
basePath: string,
|
|
131
237
|
relativePath: string,
|
|
@@ -260,6 +366,7 @@ export async function executeSummarySave(
|
|
|
260
366
|
await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, contentToSave);
|
|
261
367
|
|
|
262
368
|
let registeredMilestones: string[] = [];
|
|
369
|
+
let milestoneSequenceSelfHealed = false;
|
|
263
370
|
if (params.artifact_type === "PROJECT") {
|
|
264
371
|
try {
|
|
265
372
|
registeredMilestones = registerProjectMilestoneSequence(contentToSave);
|
|
@@ -294,29 +401,83 @@ export async function executeSummarySave(
|
|
|
294
401
|
};
|
|
295
402
|
}
|
|
296
403
|
if (registeredMilestones.length === 0) {
|
|
297
|
-
|
|
404
|
+
const existingMilestones = getAllMilestones();
|
|
405
|
+
if (existingMilestones.length === 0) {
|
|
406
|
+
// Genuine first-save failure: no milestones parsed AND none in the DB.
|
|
407
|
+
// /gsd really would report "No Active Milestone" — hard-fail so the
|
|
408
|
+
// caller rewrites the sequence before proceeding.
|
|
409
|
+
logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
|
|
410
|
+
tool: "gsd_summary_save",
|
|
411
|
+
});
|
|
412
|
+
// PROJECT.md was persisted; invalidate so subsequent reads see the new
|
|
413
|
+
// artifacts row even though no milestones registered.
|
|
414
|
+
invalidateStateCache();
|
|
415
|
+
return {
|
|
416
|
+
content: [{
|
|
417
|
+
type: "text",
|
|
418
|
+
text:
|
|
419
|
+
`Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
|
|
420
|
+
`so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
|
|
421
|
+
`Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
|
|
422
|
+
`\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
|
|
423
|
+
}],
|
|
424
|
+
details: {
|
|
425
|
+
operation: "save_summary",
|
|
426
|
+
path: relativePath,
|
|
427
|
+
artifact_type: params.artifact_type,
|
|
428
|
+
error: "milestone_registration_empty_parse",
|
|
429
|
+
},
|
|
430
|
+
isError: true,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Existing DB rows mean this is projection drift, not data loss. Rebuild
|
|
435
|
+
// the section from DB state and re-persist a parseable projection.
|
|
436
|
+
logWarning("tool", `gsd_summary_save: PROJECT.md parsed zero milestone lines but DB has ${existingMilestones.length} — rebuilding Milestone Sequence from DB (projection self-heal)`, {
|
|
298
437
|
tool: "gsd_summary_save",
|
|
438
|
+
path: relativePath,
|
|
299
439
|
});
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
440
|
+
try {
|
|
441
|
+
const healed = rebuildMilestoneSequenceSection(contentToSave, existingMilestones);
|
|
442
|
+
await saveArtifactToDb(
|
|
443
|
+
{ path: relativePath, artifact_type: params.artifact_type, content: healed },
|
|
444
|
+
basePath,
|
|
445
|
+
);
|
|
446
|
+
await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, healed);
|
|
447
|
+
const healedRegisteredMilestones = registerProjectMilestoneSequence(healed);
|
|
448
|
+
if (healedRegisteredMilestones.length === 0) {
|
|
449
|
+
throw new Error("self-healed PROJECT.md still parsed zero milestone lines");
|
|
450
|
+
}
|
|
451
|
+
registeredMilestones = healedRegisteredMilestones;
|
|
452
|
+
milestoneSequenceSelfHealed = true;
|
|
453
|
+
} catch (healErr) {
|
|
454
|
+
const msg = healErr instanceof Error ? healErr.message : String(healErr);
|
|
455
|
+
logError("tool", `gsd_summary_save: Milestone Sequence self-heal failed: ${msg}`, {
|
|
456
|
+
tool: "gsd_summary_save",
|
|
314
457
|
path: relativePath,
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
458
|
+
error: msg,
|
|
459
|
+
});
|
|
460
|
+
invalidateStateCache();
|
|
461
|
+
return {
|
|
462
|
+
content: [{
|
|
463
|
+
type: "text",
|
|
464
|
+
text:
|
|
465
|
+
`Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
|
|
466
|
+
`and automatic DB-backed Milestone Sequence repair failed: ${msg}. ` +
|
|
467
|
+
`Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
|
|
468
|
+
`\`- [ ] M001: <Title> — <One-liner>\`, then re-call gsd_summary_save(PROJECT).`,
|
|
469
|
+
}],
|
|
470
|
+
details: {
|
|
471
|
+
operation: "save_summary",
|
|
472
|
+
path: relativePath,
|
|
473
|
+
artifact_type: params.artifact_type,
|
|
474
|
+
error: "milestone_sequence_self_heal_failed",
|
|
475
|
+
self_heal_error: msg,
|
|
476
|
+
},
|
|
477
|
+
isError: true,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
invalidateStateCache();
|
|
320
481
|
}
|
|
321
482
|
}
|
|
322
483
|
|
|
@@ -339,6 +500,7 @@ export async function executeSummarySave(
|
|
|
339
500
|
artifact_type: params.artifact_type,
|
|
340
501
|
content_source: contentSource,
|
|
341
502
|
...(registeredMilestones.length > 0 ? { registeredMilestones } : {}),
|
|
503
|
+
...(milestoneSequenceSelfHealed ? { milestoneSequenceSelfHealed: true } : {}),
|
|
342
504
|
},
|
|
343
505
|
};
|
|
344
506
|
} catch (err) {
|
|
@@ -23,7 +23,7 @@ const APPROVAL_CHANGE_QUESTION_RE =
|
|
|
23
23
|
const RESEARCH_DECISION_QUESTION_RE =
|
|
24
24
|
/\b(?:research|skip)\b/i;
|
|
25
25
|
|
|
26
|
-
function
|
|
26
|
+
function extractVisibleTextFromMessage(msg: unknown): string {
|
|
27
27
|
if (!msg || typeof msg !== "object") return "";
|
|
28
28
|
const content = (msg as { content?: unknown }).content;
|
|
29
29
|
if (typeof content === "string") return content;
|
|
@@ -35,6 +35,26 @@ function extractTextFromMessage(msg: unknown): string {
|
|
|
35
35
|
if (typed.type === "text" && typeof typed.text === "string") {
|
|
36
36
|
parts.push(typed.text);
|
|
37
37
|
}
|
|
38
|
+
// thinking blocks intentionally excluded — they are internal reasoning, not user-visible
|
|
39
|
+
}
|
|
40
|
+
return parts.join("\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function extractTextFromMessage(msg: unknown): string {
|
|
44
|
+
if (!msg || typeof msg !== "object") return "";
|
|
45
|
+
const content = (msg as { content?: unknown }).content;
|
|
46
|
+
if (typeof content === "string") return content;
|
|
47
|
+
if (!Array.isArray(content)) return "";
|
|
48
|
+
const parts: string[] = [];
|
|
49
|
+
for (const block of content) {
|
|
50
|
+
if (!block || typeof block !== "object") continue;
|
|
51
|
+
const typed = block as { type?: unknown; text?: unknown; thinking?: unknown };
|
|
52
|
+
if (typed.type === "text" && typeof typed.text === "string") {
|
|
53
|
+
parts.push(typed.text);
|
|
54
|
+
}
|
|
55
|
+
if (typed.type === "thinking" && typeof typed.thinking === "string") {
|
|
56
|
+
parts.push(typed.thinking);
|
|
57
|
+
}
|
|
38
58
|
}
|
|
39
59
|
return parts.join("\n");
|
|
40
60
|
}
|
|
@@ -51,12 +71,24 @@ export function lastAssistantText(messages: unknown[] | null | undefined): strin
|
|
|
51
71
|
return "";
|
|
52
72
|
}
|
|
53
73
|
|
|
74
|
+
function lastAssistantVisibleText(messages: unknown[] | null | undefined): string {
|
|
75
|
+
if (!Array.isArray(messages)) return "";
|
|
76
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
77
|
+
const msg = messages[i];
|
|
78
|
+
if (!msg || typeof msg !== "object") continue;
|
|
79
|
+
if ((msg as { role?: unknown }).role !== "assistant") continue;
|
|
80
|
+
const text = extractVisibleTextFromMessage(msg).trim();
|
|
81
|
+
if (text) return text;
|
|
82
|
+
}
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
|
|
54
86
|
function anyMessageMatches(messages: unknown[] | undefined, pattern: RegExp): boolean {
|
|
55
87
|
if (!Array.isArray(messages)) return false;
|
|
56
88
|
return messages.some((msg) => {
|
|
57
89
|
if (!msg || typeof msg !== "object") return false;
|
|
58
90
|
if ((msg as { role?: unknown }).role === "user") return false;
|
|
59
|
-
return pattern.test(
|
|
91
|
+
return pattern.test(extractVisibleTextFromMessage(msg));
|
|
60
92
|
});
|
|
61
93
|
}
|
|
62
94
|
|
|
@@ -134,7 +166,7 @@ export function isExplicitApprovalResponse(
|
|
|
134
166
|
export function isAwaitingUserInput(messages: unknown[] | undefined): boolean {
|
|
135
167
|
if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
|
|
136
168
|
if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
|
|
137
|
-
const text =
|
|
169
|
+
const text = lastAssistantVisibleText(messages);
|
|
138
170
|
if (!text) return false;
|
|
139
171
|
if (APPROVAL_WAIT_RE.test(text)) return true;
|
|
140
172
|
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
@@ -145,7 +177,7 @@ export function isAwaitingUserInput(messages: unknown[] | undefined): boolean {
|
|
|
145
177
|
export function isAwaitingApprovalBoundary(messages: unknown[] | undefined): boolean {
|
|
146
178
|
if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
|
|
147
179
|
if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
|
|
148
|
-
const text =
|
|
180
|
+
const text = lastAssistantVisibleText(messages);
|
|
149
181
|
if (!text) return false;
|
|
150
182
|
if (APPROVAL_WAIT_RE.test(text)) return true;
|
|
151
183
|
return hasApprovalQuestion(text);
|
|
@@ -158,7 +190,7 @@ export function shouldPauseForUserApprovalQuestion(
|
|
|
158
190
|
if (!unitType || !USER_APPROVAL_UNIT_TYPES.has(unitType)) return false;
|
|
159
191
|
if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
|
|
160
192
|
if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
|
|
161
|
-
const text =
|
|
193
|
+
const text = lastAssistantVisibleText(messages);
|
|
162
194
|
if (!text) return false;
|
|
163
195
|
if (APPROVAL_WAIT_RE.test(text)) return true;
|
|
164
196
|
if (unitType === "research-decision") return hasResearchDecisionQuestion(text);
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";exports.id=678,exports.ids=[678],exports.modules={10678:(a,b,c)=>{let d;c.d(b,{isOnboardingComplete:()=>r,readOnboardingRecord:()=>q});var e=c(89477),f=c(86697);c(76212),c(78335);var g=c(65521),h=c(62228);c(95398),process.env.GSD_ENABLE_NATIVE_GSD_PARSER,Symbol("native-unavailable"),new g.AsyncLocalStorage,c(97526),Object.values({Q3:{id:"Q3",scope:"slice",ownerTurn:"gate-evaluate",question:"How can this be exploited?",guidance:"Identify abuse scenarios: parameter tampering, replay attacks, privilege escalation.\nMap data exposure risks: PII, tokens, secrets accessible through this slice.\nDefine input trust boundaries: untrusted user input reaching DB, API, or filesystem.\nIf none apply, return verdict 'omitted' with rationale explaining why.",promptSection:"Abuse Surface"},Q4:{id:"Q4",scope:"slice",ownerTurn:"gate-evaluate",question:"Which existing requirements (R-IDs) does this slice touch, and which must be re-tested?",guidance:"List the R-IDs (e.g. R001, R003) touched by this slice; see the milestone requirements artifact at .gsd/milestones/<id>/REQUIREMENTS.md.\nIdentify what must be re-tested after shipping.\nFlag decisions that should be revisited given the new scope.\nIf no existing requirements are affected, return verdict 'omitted'.",promptSection:"Broken Promises"},Q5:{id:"Q5",scope:"task",ownerTurn:"execute-task",question:"What breaks when dependencies fail?",guidance:"Enumerate the task's external dependencies (APIs, filesystem, network, subprocesses).\nDescribe the failure path for each: timeout, malformed response, connection loss.\nVerify the implementation handles each failure or explicitly bubbles the error.\nReturn verdict 'omitted' only if the task has no external dependencies.",promptSection:"Failure Modes"},Q6:{id:"Q6",scope:"task",ownerTurn:"execute-task",question:"What is the 10x load breakpoint?",guidance:"Identify the resource that saturates first at 10x the expected load.\nDescribe the protection applied (pool sizing, rate limiting, pagination, caching).\nReturn verdict 'omitted' if the task has no runtime load dimension.",promptSection:"Load Profile"},Q7:{id:"Q7",scope:"task",ownerTurn:"execute-task",question:"What negative tests protect this task?",guidance:"List malformed inputs, error paths, and boundary conditions the tests cover.\nPoint to the specific test files or cases that assert each negative scenario.\nReturn verdict 'omitted' only if the task has no meaningful negative surface.",promptSection:"Negative Tests"},Q8:{id:"Q8",scope:"slice",ownerTurn:"complete-slice",question:"How will ops know this slice is healthy or broken?",guidance:"Describe the health signal (metric, log line, dashboard) that proves the slice works.\nDescribe the failure signal that triggers an alert or paging.\nDocument the recovery procedure and any monitoring gaps.\nReturn verdict 'omitted' only for slices with no runtime behavior at all.",promptSection:"Operational Readiness"},MV01:{id:"MV01",scope:"milestone",ownerTurn:"validate-milestone",question:"Is every success criterion in the milestone roadmap satisfied?",guidance:"Walk the success-criteria checklist from the milestone roadmap.\nFor each criterion, point to the slice / assessment / verification evidence that proves it.\nReturn verdict 'flag' if any criterion is unmet or unverifiable.",promptSection:"Success Criteria Checklist"},MV02:{id:"MV02",scope:"milestone",ownerTurn:"validate-milestone",question:"Does every slice have a SUMMARY.md and a passing assessment?",guidance:"Confirm every slice listed in the roadmap has a SUMMARY.md.\nConfirm each slice has an ASSESSMENT verdict of 'pass' (or justified 'omitted').\nFlag missing artifacts and slices with outstanding follow-ups or known limitations.",promptSection:"Slice Delivery Audit"},MV03:{id:"MV03",scope:"milestone",ownerTurn:"validate-milestone",question:"Do the slices integrate end-to-end?",guidance:"Trace at least one cross-slice flow proving the pieces compose.\nFlag gaps where two slices were built in isolation with no integration evidence.",promptSection:"Cross-Slice Integration"},MV04:{id:"MV04",scope:"milestone",ownerTurn:"validate-milestone",question:"Are all touched requirements covered and still coherent?",guidance:"For each requirement advanced, validated, surfaced, or invalidated across the milestone's slices, confirm the milestone-level evidence matches.\nFlag requirements that slices claim to advance but no artifact proves.",promptSection:"Requirement Coverage"}});class i{get(a){return this.entries.get(a)}set(a,b){this.entries.set(a,b)}has(a){return this.entries.has(a)}delete(a){return this.entries.delete(a)}asReadonlyMap(){return this.entries}closeNonActive(a,b){for(let[c,d]of this.entries)d.db!==a&&(this.entries.delete(c),b(d))}constructor(){this.entries=new Map}}class j{markAttempted(){this.attempted=!0}clearError(){this.lastError=null,this.lastPhase=null}recordError(a,b){this.lastPhase=a,this.lastError=b instanceof Error?b:Error(String(b))}reset(){this.attempted=!1,this.clearError()}snapshot(){return{attempted:this.attempted,lastError:this.lastError,lastPhase:this.lastPhase}}constructor(){this.attempted=!1,this.lastError=null,this.lastPhase=null}}class k{isInTransaction(){return this.depth>0}transaction(a,b){if(this.depth>0)return this.runNested(b);a.begin(),this.depth++;try{let c=b();return a.commit(),c}catch(b){throw a.rollback(),b}finally{this.depth--}}readTransaction(a,b,c){if(this.depth>0)return this.runNested(b);a.beginRead(),this.depth++;try{let c=b();return a.commit(),c}catch(b){try{a.rollback()}catch(a){c(a instanceof Error?a:Error(String(a)))}throw b}finally{this.depth--}}runNested(a){this.depth++;try{return a()}finally{this.depth--}}constructor(){this.depth=0}}class l{constructor(a){this.providerName=null,this.providerModule=null,this.loadAttempted=!1,this.deps=a}load(){if(this.loadAttempted)return;this.loadAttempted=!0;try{this.deps.suppressSqliteWarning();let a=this.deps.tryRequireNodeSqlite();if(a.DatabaseSync){this.providerModule=a,this.providerName="node:sqlite";return}}catch{}let a=this.loadBetterSqliteModule();if(a){this.providerModule=a,this.providerName="better-sqlite3";return}let b=22>parseInt(this.deps.nodeVersion.split(".")[0],10)?` GSD requires Node >= 22.0.0 (current: v${this.deps.nodeVersion}). Upgrade Node to fix this.`:"";this.deps.writeStderr(`gsd-db: No SQLite provider available (tried node:sqlite, better-sqlite3).${b}
|
|
2
|
-
`)}getProviderName(){return this.providerName}openRaw(a){if(this.load(),!this.providerModule||!this.providerName)return null;if("node:sqlite"===this.providerName){let{DatabaseSync:b}=this.providerModule;return new b(a)}return new this.providerModule(a)}tryOpenBetterSqliteFallback(a){if("node:sqlite"!==this.providerName)return null;let b=this.loadBetterSqliteModule();return b?{providerName:"better-sqlite3",providerModule:b,rawDb:new b(a)}:null}commitFallback(a){this.providerName=a.providerName,this.providerModule=a.providerModule}reset(){this.loadAttempted=!1,this.providerModule=null,this.providerName=null}loadBetterSqliteModule(){try{let a=this.deps.tryRequireBetterSqlite3();if("function"==typeof a)return a;if(a&&"function"==typeof a.default)return a.default}catch{}return null}}function m(){if(void 0!==d)return d;try{d=createRequire(import.meta.url)}catch{d=null}return d}new l({tryRequireNodeSqlite:()=>{let a=m();if(!a)throw Error("unavailable");return a("node:sqlite")},tryRequireBetterSqlite3:()=>{let a=m();if(!a)throw Error("unavailable");return a("better-sqlite3")},suppressSqliteWarning:function(){let a=process.emit;process.emit=function(b,...c){return!("warning"===b&&c[0]&&"object"==typeof c[0]&&"name"in c[0]&&"ExperimentalWarning"===c[0].name&&"message"in c[0]&&"string"==typeof c[0].message&&c[0].message.includes("SQLite"))&&a.apply(process,[b,...c])}},nodeVersion:process.versions.node,writeStderr:a=>process.stderr.write(a)}),new j,new i,new k;let n=process.env.GSD_CODING_AGENT_DIR||(0,f.join)(process.env.GSD_HOME?(0,f.resolve)(process.env.GSD_HOME):(0,f.join)((0,h.homedir)(),".gsd"),"agent"),o=(0,f.join)(n,"onboarding.json"),p={version:1,flowVersion:1,completedAt:null,completedSteps:[],skippedSteps:[],lastResumePoint:null};function q(){if(!(0,e.existsSync)(o))return{...p};try{let a=JSON.parse((0,e.readFileSync)(o,"utf-8"));return{version:"number"==typeof a.version?a.version:1,flowVersion:"number"==typeof a.flowVersion?a.flowVersion:0,completedAt:"string"==typeof a.completedAt?a.completedAt:null,completedSteps:Array.isArray(a.completedSteps)?a.completedSteps.filter(a=>"string"==typeof a):[],skippedSteps:Array.isArray(a.skippedSteps)?a.skippedSteps.filter(a=>"string"==typeof a):[],lastResumePoint:"string"==typeof a.lastResumePoint?a.lastResumePoint:null}}catch{return{...p}}}function r(){let a=q();return null!==a.completedAt&&1===a.flowVersion}},97526:(a,b,c)=>{c.d(b,{DZ:()=>e,rs:()=>d});let d="GSD_GIT_ERROR";class e extends Error{constructor(a,b,c){super(b,c),this.name="GSDError",this.code=a}}}};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export type WidgetPrototypeId = "current-small" | "horizontal-bar" | "split-ribbon" | "dense-grid";
|
|
2
|
-
export interface WidgetPrototype {
|
|
3
|
-
id: WidgetPrototypeId;
|
|
4
|
-
label: string;
|
|
5
|
-
tagline: string;
|
|
6
|
-
render: (width: number) => string[];
|
|
7
|
-
}
|
|
8
|
-
declare const RECOMMENDED_WIDGET_PROTOTYPE_ID: WidgetPrototypeId;
|
|
9
|
-
export { RECOMMENDED_WIDGET_PROTOTYPE_ID };
|
|
10
|
-
declare function lineStats(lines: string[]): {
|
|
11
|
-
total: number;
|
|
12
|
-
blank: number;
|
|
13
|
-
nonBlank: number;
|
|
14
|
-
};
|
|
15
|
-
export { lineStats };
|
|
16
|
-
export declare const WIDGET_PROTOTYPES: WidgetPrototype[];
|
|
17
|
-
export declare function getWidgetPrototype(id: string): WidgetPrototype;
|
|
18
|
-
export declare function resolveWidgetPrototypeSet(arg: string): WidgetPrototype[];
|
|
19
|
-
export declare function listWidgetPrototypeIds(): WidgetPrototypeId[];
|
|
20
|
-
export declare function renderWidgetPrototypeBanner(prototype: WidgetPrototype, width: number): string[];
|
|
21
|
-
//# sourceMappingURL=gsd-widget-prototype.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gsd-widget-prototype.d.ts","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__prototype__/gsd-widget-prototype.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,gBAAgB,GAAG,cAAc,GAAG,YAAY,CAAC;AAEnG,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,iBAAiB,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;CACpC;AAsBD,QAAA,MAAM,+BAA+B,EAAE,iBAAgC,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,CAAC;AAE3C,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAItF;AAED,OAAO,EAAE,SAAS,EAAE,CAAC;AAiKrB,eAAO,MAAM,iBAAiB,EAAE,eAAe,EAyB9C,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,CAM9D;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAIxE;AAED,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CAE5D;AAED,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAO/F"}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
// PROTOTYPE - throwaway /gsd auto progress widget layout options.
|
|
2
|
-
// Question: can the widget stay small while using terminal width horizontally?
|
|
3
|
-
// Run: pnpm run prototype:tui-widget [-- current-small|horizontal-bar|split-ribbon|dense-grid|all]
|
|
4
|
-
import { alignRight, padRight, truncateToWidth, visibleWidth } from "@gsd/pi-tui";
|
|
5
|
-
import { theme } from "@gsd/pi-coding-agent/theme/theme.js";
|
|
6
|
-
const SAMPLE = {
|
|
7
|
-
mode: "AUTO",
|
|
8
|
-
state: "running",
|
|
9
|
-
health: "green",
|
|
10
|
-
elapsed: "1h 42m",
|
|
11
|
-
eta: "18m left",
|
|
12
|
-
phase: "execute-task",
|
|
13
|
-
verb: "Execute",
|
|
14
|
-
milestone: "M004",
|
|
15
|
-
slice: "S02",
|
|
16
|
-
unit: "T03",
|
|
17
|
-
title: "Compare horizontal dashboard density",
|
|
18
|
-
slicesDone: 2,
|
|
19
|
-
slicesTotal: 5,
|
|
20
|
-
tasksDone: 2,
|
|
21
|
-
tasksTotal: 4,
|
|
22
|
-
tokens: "182k",
|
|
23
|
-
cost: "$1.83",
|
|
24
|
-
};
|
|
25
|
-
const RECOMMENDED_WIDGET_PROTOTYPE_ID = "dense-grid";
|
|
26
|
-
export { RECOMMENDED_WIDGET_PROTOTYPE_ID };
|
|
27
|
-
function lineStats(lines) {
|
|
28
|
-
const total = lines.length;
|
|
29
|
-
const blank = lines.filter((line) => line.trim().length === 0).length;
|
|
30
|
-
return { total, blank, nonBlank: total - blank };
|
|
31
|
-
}
|
|
32
|
-
export { lineStats };
|
|
33
|
-
function padLine(line, width) {
|
|
34
|
-
return padRight(truncateToWidth(line, width, ""), width);
|
|
35
|
-
}
|
|
36
|
-
function rule(width, color = "borderMuted") {
|
|
37
|
-
return theme.fg(color, "─".repeat(Math.max(1, width)));
|
|
38
|
-
}
|
|
39
|
-
function dim(text) {
|
|
40
|
-
return theme.fg("dim", text);
|
|
41
|
-
}
|
|
42
|
-
function accent(text) {
|
|
43
|
-
return theme.fg("accent", text);
|
|
44
|
-
}
|
|
45
|
-
function success(text) {
|
|
46
|
-
return theme.fg("success", text);
|
|
47
|
-
}
|
|
48
|
-
function text(textValue) {
|
|
49
|
-
return theme.fg("text", textValue);
|
|
50
|
-
}
|
|
51
|
-
function label(textValue, color = "borderAccent") {
|
|
52
|
-
return theme.fg(color, theme.bold(textValue.toUpperCase()));
|
|
53
|
-
}
|
|
54
|
-
function progressBar(width) {
|
|
55
|
-
const done = SAMPLE.slicesDone;
|
|
56
|
-
const total = SAMPLE.slicesTotal;
|
|
57
|
-
const filled = Math.max(0, Math.min(width, Math.round((done / total) * width)));
|
|
58
|
-
return `${success("█".repeat(filled))}${dim("░".repeat(Math.max(0, width - filled)))}`;
|
|
59
|
-
}
|
|
60
|
-
function metric(name, value, width) {
|
|
61
|
-
const raw = `${dim(name)} ${text(value)}`;
|
|
62
|
-
return truncateToWidth(raw, width, "…");
|
|
63
|
-
}
|
|
64
|
-
function column(content, width) {
|
|
65
|
-
return padRight(truncateToWidth(content, width, "…"), width);
|
|
66
|
-
}
|
|
67
|
-
function fitColumns(width, parts) {
|
|
68
|
-
if (parts.length === 0)
|
|
69
|
-
return "";
|
|
70
|
-
const gap = dim(" │ ");
|
|
71
|
-
const gapWidth = visibleWidth(gap) * (parts.length - 1);
|
|
72
|
-
const available = Math.max(parts.length * 8, width - gapWidth);
|
|
73
|
-
const base = Math.floor(available / parts.length);
|
|
74
|
-
let remaining = available - base * parts.length;
|
|
75
|
-
const cols = parts.map((part) => {
|
|
76
|
-
const w = base + (remaining > 0 ? 1 : 0);
|
|
77
|
-
remaining--;
|
|
78
|
-
return column(part, w);
|
|
79
|
-
});
|
|
80
|
-
return truncateToWidth(cols.join(gap), width, "…");
|
|
81
|
-
}
|
|
82
|
-
function stateLine(id, width, renderedLineCount) {
|
|
83
|
-
const state = [
|
|
84
|
-
`variant=${id}`,
|
|
85
|
-
"mode=small-prototype",
|
|
86
|
-
`width=${width}`,
|
|
87
|
-
`lines=${renderedLineCount}`,
|
|
88
|
-
`unit=${SAMPLE.milestone}/${SAMPLE.slice}/${SAMPLE.unit}`,
|
|
89
|
-
];
|
|
90
|
-
return padLine(dim(`STATE ${state.join(" ")}`), width);
|
|
91
|
-
}
|
|
92
|
-
function actionTarget(maxWidth) {
|
|
93
|
-
return truncateToWidth(`${SAMPLE.unit}: ${SAMPLE.title}`, Math.max(8, maxWidth), "…");
|
|
94
|
-
}
|
|
95
|
-
function renderCurrentSmall(width) {
|
|
96
|
-
const pad = " ";
|
|
97
|
-
const lines = [];
|
|
98
|
-
lines.push(rule(width));
|
|
99
|
-
lines.push(alignRight(`${pad}${accent("◒")} ${accent(theme.bold("GSD"))} ${success(SAMPLE.mode)} ${success(SAMPLE.state)}`, dim(`${SAMPLE.elapsed} · ${SAMPLE.eta}`), width));
|
|
100
|
-
lines.push("");
|
|
101
|
-
lines.push(alignRight(`${pad}${accent("▸")} ${accent(SAMPLE.verb)} ${text(actionTarget(Math.floor(width * 0.58)))}`, dim(SAMPLE.phase), width));
|
|
102
|
-
const barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));
|
|
103
|
-
lines.push(`${pad}${progressBar(barWidth)} ${text(`${SAMPLE.slicesDone}`)}${dim(`/${SAMPLE.slicesTotal} slices · task `)}${accent(String(SAMPLE.tasksDone + 1))}${dim(`/${SAMPLE.tasksTotal}`)}`);
|
|
104
|
-
lines.push(rule(width));
|
|
105
|
-
lines.push(stateLine("current-small", width, lines.length + 1));
|
|
106
|
-
return lines.map((line) => padLine(line, width));
|
|
107
|
-
}
|
|
108
|
-
function renderHorizontalBar(width) {
|
|
109
|
-
const lines = [];
|
|
110
|
-
const left = `${accent("◒ GSD")} ${success(SAMPLE.mode)} ${dim("·")} ${success(SAMPLE.state)} ${dim("·")} ${text(`${SAMPLE.verb} ${actionTarget(Math.floor(width * 0.45))}`)}`;
|
|
111
|
-
const right = dim(`${SAMPLE.phase} · ${SAMPLE.elapsed} · ${SAMPLE.eta}`);
|
|
112
|
-
lines.push(rule(width, "borderAccent"));
|
|
113
|
-
lines.push(alignRight(left, right, width));
|
|
114
|
-
const barWidth = Math.max(8, Math.min(16, Math.floor(width * 0.14)));
|
|
115
|
-
const progressLeft = `${progressBar(barWidth)} ${text(`${SAMPLE.slicesDone}/${SAMPLE.slicesTotal}`)}${dim(" slices")} ${dim("· task ")}${accent(String(SAMPLE.tasksDone + 1))}${dim(`/${SAMPLE.tasksTotal}`)}`;
|
|
116
|
-
const rightBudget = Math.max(20, width - visibleWidth(progressLeft) - 4);
|
|
117
|
-
const progressRight = truncateToWidth(`${metric("tokens", SAMPLE.tokens, 18)} ${dim("·")} ${metric("cost", SAMPLE.cost, 12)}`, rightBudget, "…");
|
|
118
|
-
lines.push(alignRight(progressLeft, progressRight, width));
|
|
119
|
-
lines.push(rule(width, "borderAccent"));
|
|
120
|
-
lines.push(stateLine("horizontal-bar", width, lines.length + 1));
|
|
121
|
-
return lines.map((line) => padLine(line, width));
|
|
122
|
-
}
|
|
123
|
-
function renderSplitRibbon(width) {
|
|
124
|
-
const lines = [];
|
|
125
|
-
const title = `${accent("GSD")} ${success(SAMPLE.mode)} ${dim("·")} ${SAMPLE.milestone}/${SAMPLE.slice}/${SAMPLE.unit}`;
|
|
126
|
-
const time = dim(`${SAMPLE.elapsed} · ${SAMPLE.eta}`);
|
|
127
|
-
lines.push(rule(width));
|
|
128
|
-
lines.push(alignRight(title, time, width));
|
|
129
|
-
const actionWidth = Math.max(18, Math.floor(width * 0.42));
|
|
130
|
-
const phaseWidth = Math.max(14, Math.floor(width * 0.18));
|
|
131
|
-
const progressWidth = Math.max(22, Math.floor(width * 0.22));
|
|
132
|
-
const action = `${label("work")} ${text(actionTarget(actionWidth))}`;
|
|
133
|
-
const phase = `${label("phase", "border")} ${dim(SAMPLE.phase)}`;
|
|
134
|
-
const progress = `${label("progress")} ${progressBar(Math.max(8, progressWidth - 18))} ${text(`${SAMPLE.slicesDone}/${SAMPLE.slicesTotal}`)}`;
|
|
135
|
-
const run = `${label("run", "border")} ${dim(`${SAMPLE.tokens} · ${SAMPLE.cost}`)}`;
|
|
136
|
-
lines.push(fitColumns(width, [action, phase, progress, run]));
|
|
137
|
-
lines.push(rule(width));
|
|
138
|
-
lines.push(stateLine("split-ribbon", width, lines.length + 1));
|
|
139
|
-
return lines.map((line) => padLine(line, width));
|
|
140
|
-
}
|
|
141
|
-
function renderDenseGrid(width) {
|
|
142
|
-
const lines = [];
|
|
143
|
-
const rowOne = fitColumns(width, [
|
|
144
|
-
`${label("status", "border")} ${success(`${SAMPLE.mode} ${SAMPLE.state}`)}`,
|
|
145
|
-
`${label("unit")} ${text(`${SAMPLE.milestone}/${SAMPLE.slice}/${SAMPLE.unit}`)}`,
|
|
146
|
-
`${label("spend", "border")} ${dim(`${SAMPLE.tokens} · ${SAMPLE.cost}`)}`,
|
|
147
|
-
`${label("time")} ${dim(`${SAMPLE.elapsed} · ${SAMPLE.eta}`)}`,
|
|
148
|
-
]);
|
|
149
|
-
const rowTwo = fitColumns(width, [
|
|
150
|
-
`${label("phase", "border")} ${dim(SAMPLE.phase)}`,
|
|
151
|
-
`${label("work")} ${text(actionTarget(Math.floor(width * 0.28)))}`,
|
|
152
|
-
`${label("task", "border")} ${accent(String(SAMPLE.tasksDone + 1))}${dim(`/${SAMPLE.tasksTotal}`)}`,
|
|
153
|
-
`${label("slice")} ${progressBar(Math.max(8, Math.min(16, Math.floor(width * 0.16))))} ${text(`${SAMPLE.slicesDone}/${SAMPLE.slicesTotal}`)}`,
|
|
154
|
-
]);
|
|
155
|
-
lines.push(rule(width, "borderAccent"));
|
|
156
|
-
lines.push(rowOne);
|
|
157
|
-
lines.push(rowTwo);
|
|
158
|
-
lines.push(rule(width, "borderAccent"));
|
|
159
|
-
lines.push(stateLine("dense-grid", width, lines.length + 1));
|
|
160
|
-
return lines.map((line) => padLine(line, width));
|
|
161
|
-
}
|
|
162
|
-
export const WIDGET_PROTOTYPES = [
|
|
163
|
-
{
|
|
164
|
-
id: "current-small",
|
|
165
|
-
label: "A · Current small",
|
|
166
|
-
tagline: "Baseline: existing small mode shape, vertical action/progress stack",
|
|
167
|
-
render: renderCurrentSmall,
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
id: "horizontal-bar",
|
|
171
|
-
label: "B · Horizontal bar",
|
|
172
|
-
tagline: "3-line widget, action + status use the full terminal width",
|
|
173
|
-
render: renderHorizontalBar,
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
id: "split-ribbon",
|
|
177
|
-
label: "C · Split ribbon",
|
|
178
|
-
tagline: "Four labeled columns without duplicating footer commands",
|
|
179
|
-
render: renderSplitRibbon,
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
id: "dense-grid",
|
|
183
|
-
label: "D · Dense grid",
|
|
184
|
-
tagline: "Selected: two metric rows, maximum scan data without growing height",
|
|
185
|
-
render: renderDenseGrid,
|
|
186
|
-
},
|
|
187
|
-
];
|
|
188
|
-
export function getWidgetPrototype(id) {
|
|
189
|
-
const found = WIDGET_PROTOTYPES.find((prototype) => prototype.id === id);
|
|
190
|
-
if (!found) {
|
|
191
|
-
throw new Error(`Unknown widget prototype: ${id}. Options: ${WIDGET_PROTOTYPES.map((p) => p.id).join(", ")}`);
|
|
192
|
-
}
|
|
193
|
-
return found;
|
|
194
|
-
}
|
|
195
|
-
export function resolveWidgetPrototypeSet(arg) {
|
|
196
|
-
if (arg === "all")
|
|
197
|
-
return WIDGET_PROTOTYPES;
|
|
198
|
-
if (arg === "recommended")
|
|
199
|
-
return [getWidgetPrototype(RECOMMENDED_WIDGET_PROTOTYPE_ID)];
|
|
200
|
-
return [getWidgetPrototype(arg)];
|
|
201
|
-
}
|
|
202
|
-
export function listWidgetPrototypeIds() {
|
|
203
|
-
return WIDGET_PROTOTYPES.map((prototype) => prototype.id);
|
|
204
|
-
}
|
|
205
|
-
export function renderWidgetPrototypeBanner(prototype, width) {
|
|
206
|
-
const labelText = `${prototype.label} ${prototype.id === RECOMMENDED_WIDGET_PROTOTYPE_ID ? "★ " : ""}${prototype.tagline}`;
|
|
207
|
-
return [
|
|
208
|
-
rule(width, prototype.id === RECOMMENDED_WIDGET_PROTOTYPE_ID ? "borderAccent" : "borderMuted"),
|
|
209
|
-
padLine(labelText, width),
|
|
210
|
-
rule(width, prototype.id === RECOMMENDED_WIDGET_PROTOTYPE_ID ? "borderAccent" : "borderMuted"),
|
|
211
|
-
];
|
|
212
|
-
}
|
|
213
|
-
//# sourceMappingURL=gsd-widget-prototype.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gsd-widget-prototype.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__prototype__/gsd-widget-prototype.ts"],"names":[],"mappings":"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","sourcesContent":["// PROTOTYPE - throwaway /gsd auto progress widget layout options.\n// Question: can the widget stay small while using terminal width horizontally?\n// Run: pnpm run prototype:tui-widget [-- current-small|horizontal-bar|split-ribbon|dense-grid|all]\n\nimport { alignRight, padRight, truncateToWidth, visibleWidth } from \"@gsd/pi-tui\";\nimport { theme, type ThemeColor } from \"@gsd/pi-coding-agent/theme/theme.js\";\n\nexport type WidgetPrototypeId = \"current-small\" | \"horizontal-bar\" | \"split-ribbon\" | \"dense-grid\";\n\nexport interface WidgetPrototype {\n\tid: WidgetPrototypeId;\n\tlabel: string;\n\ttagline: string;\n\trender: (width: number) => string[];\n}\n\nconst SAMPLE = {\n\tmode: \"AUTO\",\n\tstate: \"running\",\n\thealth: \"green\",\n\telapsed: \"1h 42m\",\n\teta: \"18m left\",\n\tphase: \"execute-task\",\n\tverb: \"Execute\",\n\tmilestone: \"M004\",\n\tslice: \"S02\",\n\tunit: \"T03\",\n\ttitle: \"Compare horizontal dashboard density\",\n\tslicesDone: 2,\n\tslicesTotal: 5,\n\ttasksDone: 2,\n\ttasksTotal: 4,\n\ttokens: \"182k\",\n\tcost: \"$1.83\",\n};\n\nconst RECOMMENDED_WIDGET_PROTOTYPE_ID: WidgetPrototypeId = \"dense-grid\";\nexport { RECOMMENDED_WIDGET_PROTOTYPE_ID };\n\nfunction lineStats(lines: string[]): { total: number; blank: number; nonBlank: number } {\n\tconst total = lines.length;\n\tconst blank = lines.filter((line) => line.trim().length === 0).length;\n\treturn { total, blank, nonBlank: total - blank };\n}\n\nexport { lineStats };\n\nfunction padLine(line: string, width: number): string {\n\treturn padRight(truncateToWidth(line, width, \"\"), width);\n}\n\nfunction rule(width: number, color: ThemeColor = \"borderMuted\"): string {\n\treturn theme.fg(color, \"─\".repeat(Math.max(1, width)));\n}\n\nfunction dim(text: string): string {\n\treturn theme.fg(\"dim\", text);\n}\n\nfunction accent(text: string): string {\n\treturn theme.fg(\"accent\", text);\n}\n\nfunction success(text: string): string {\n\treturn theme.fg(\"success\", text);\n}\n\nfunction text(textValue: string): string {\n\treturn theme.fg(\"text\", textValue);\n}\n\nfunction label(textValue: string, color: ThemeColor = \"borderAccent\"): string {\n\treturn theme.fg(color, theme.bold(textValue.toUpperCase()));\n}\n\nfunction progressBar(width: number): string {\n\tconst done = SAMPLE.slicesDone;\n\tconst total = SAMPLE.slicesTotal;\n\tconst filled = Math.max(0, Math.min(width, Math.round((done / total) * width)));\n\treturn `${success(\"█\".repeat(filled))}${dim(\"░\".repeat(Math.max(0, width - filled)))}`;\n}\n\nfunction metric(name: string, value: string, width: number): string {\n\tconst raw = `${dim(name)} ${text(value)}`;\n\treturn truncateToWidth(raw, width, \"…\");\n}\n\nfunction column(content: string, width: number): string {\n\treturn padRight(truncateToWidth(content, width, \"…\"), width);\n}\n\nfunction fitColumns(width: number, parts: string[]): string {\n\tif (parts.length === 0) return \"\";\n\tconst gap = dim(\" │ \");\n\tconst gapWidth = visibleWidth(gap) * (parts.length - 1);\n\tconst available = Math.max(parts.length * 8, width - gapWidth);\n\tconst base = Math.floor(available / parts.length);\n\tlet remaining = available - base * parts.length;\n\tconst cols = parts.map((part) => {\n\t\tconst w = base + (remaining > 0 ? 1 : 0);\n\t\tremaining--;\n\t\treturn column(part, w);\n\t});\n\treturn truncateToWidth(cols.join(gap), width, \"…\");\n}\n\nfunction stateLine(id: WidgetPrototypeId, width: number, renderedLineCount: number): string {\n\tconst state = [\n\t\t`variant=${id}`,\n\t\t\"mode=small-prototype\",\n\t\t`width=${width}`,\n\t\t`lines=${renderedLineCount}`,\n\t\t`unit=${SAMPLE.milestone}/${SAMPLE.slice}/${SAMPLE.unit}`,\n\t];\n\treturn padLine(dim(`STATE ${state.join(\" \")}`), width);\n}\n\nfunction actionTarget(maxWidth: number): string {\n\treturn truncateToWidth(`${SAMPLE.unit}: ${SAMPLE.title}`, Math.max(8, maxWidth), \"…\");\n}\n\nfunction renderCurrentSmall(width: number): string[] {\n\tconst pad = \" \";\n\tconst lines: string[] = [];\n\tlines.push(rule(width));\n\tlines.push(alignRight(\n\t\t`${pad}${accent(\"◒\")} ${accent(theme.bold(\"GSD\"))} ${success(SAMPLE.mode)} ${success(SAMPLE.state)}`,\n\t\tdim(`${SAMPLE.elapsed} · ${SAMPLE.eta}`),\n\t\twidth,\n\t));\n\tlines.push(\"\");\n\tlines.push(alignRight(\n\t\t`${pad}${accent(\"▸\")} ${accent(SAMPLE.verb)} ${text(actionTarget(Math.floor(width * 0.58)))}`,\n\t\tdim(SAMPLE.phase),\n\t\twidth,\n\t));\n\tconst barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));\n\tlines.push(`${pad}${progressBar(barWidth)} ${text(`${SAMPLE.slicesDone}`)}${dim(`/${SAMPLE.slicesTotal} slices · task `)}${accent(String(SAMPLE.tasksDone + 1))}${dim(`/${SAMPLE.tasksTotal}`)}`);\n\tlines.push(rule(width));\n\tlines.push(stateLine(\"current-small\", width, lines.length + 1));\n\treturn lines.map((line) => padLine(line, width));\n}\n\nfunction renderHorizontalBar(width: number): string[] {\n\tconst lines: string[] = [];\n\tconst left = `${accent(\"◒ GSD\")} ${success(SAMPLE.mode)} ${dim(\"·\")} ${success(SAMPLE.state)} ${dim(\"·\")} ${text(`${SAMPLE.verb} ${actionTarget(Math.floor(width * 0.45))}`)}`;\n\tconst right = dim(`${SAMPLE.phase} · ${SAMPLE.elapsed} · ${SAMPLE.eta}`);\n\tlines.push(rule(width, \"borderAccent\"));\n\tlines.push(alignRight(left, right, width));\n\n\tconst barWidth = Math.max(8, Math.min(16, Math.floor(width * 0.14)));\n\tconst progressLeft = `${progressBar(barWidth)} ${text(`${SAMPLE.slicesDone}/${SAMPLE.slicesTotal}`)}${dim(\" slices\")} ${dim(\"· task \")}${accent(String(SAMPLE.tasksDone + 1))}${dim(`/${SAMPLE.tasksTotal}`)}`;\n\tconst rightBudget = Math.max(20, width - visibleWidth(progressLeft) - 4);\n\tconst progressRight = truncateToWidth(\n\t\t`${metric(\"tokens\", SAMPLE.tokens, 18)} ${dim(\"·\")} ${metric(\"cost\", SAMPLE.cost, 12)}`,\n\t\trightBudget,\n\t\t\"…\",\n\t);\n\tlines.push(alignRight(progressLeft, progressRight, width));\n\tlines.push(rule(width, \"borderAccent\"));\n\tlines.push(stateLine(\"horizontal-bar\", width, lines.length + 1));\n\treturn lines.map((line) => padLine(line, width));\n}\n\nfunction renderSplitRibbon(width: number): string[] {\n\tconst lines: string[] = [];\n\tconst title = `${accent(\"GSD\")} ${success(SAMPLE.mode)} ${dim(\"·\")} ${SAMPLE.milestone}/${SAMPLE.slice}/${SAMPLE.unit}`;\n\tconst time = dim(`${SAMPLE.elapsed} · ${SAMPLE.eta}`);\n\tlines.push(rule(width));\n\tlines.push(alignRight(title, time, width));\n\n\tconst actionWidth = Math.max(18, Math.floor(width * 0.42));\n\tconst phaseWidth = Math.max(14, Math.floor(width * 0.18));\n\tconst progressWidth = Math.max(22, Math.floor(width * 0.22));\n\tconst action = `${label(\"work\")} ${text(actionTarget(actionWidth))}`;\n\tconst phase = `${label(\"phase\", \"border\")} ${dim(SAMPLE.phase)}`;\n\tconst progress = `${label(\"progress\")} ${progressBar(Math.max(8, progressWidth - 18))} ${text(`${SAMPLE.slicesDone}/${SAMPLE.slicesTotal}`)}`;\n\tconst run = `${label(\"run\", \"border\")} ${dim(`${SAMPLE.tokens} · ${SAMPLE.cost}`)}`;\n\tlines.push(fitColumns(width, [action, phase, progress, run]));\n\tlines.push(rule(width));\n\tlines.push(stateLine(\"split-ribbon\", width, lines.length + 1));\n\treturn lines.map((line) => padLine(line, width));\n}\n\nfunction renderDenseGrid(width: number): string[] {\n\tconst lines: string[] = [];\n\tconst rowOne = fitColumns(width, [\n\t\t`${label(\"status\", \"border\")} ${success(`${SAMPLE.mode} ${SAMPLE.state}`)}`,\n\t\t`${label(\"unit\")} ${text(`${SAMPLE.milestone}/${SAMPLE.slice}/${SAMPLE.unit}`)}`,\n\t\t`${label(\"spend\", \"border\")} ${dim(`${SAMPLE.tokens} · ${SAMPLE.cost}`)}`,\n\t\t`${label(\"time\")} ${dim(`${SAMPLE.elapsed} · ${SAMPLE.eta}`)}`,\n\t]);\n\tconst rowTwo = fitColumns(width, [\n\t\t`${label(\"phase\", \"border\")} ${dim(SAMPLE.phase)}`,\n\t\t`${label(\"work\")} ${text(actionTarget(Math.floor(width * 0.28)))}`,\n\t\t`${label(\"task\", \"border\")} ${accent(String(SAMPLE.tasksDone + 1))}${dim(`/${SAMPLE.tasksTotal}`)}`,\n\t\t`${label(\"slice\")} ${progressBar(Math.max(8, Math.min(16, Math.floor(width * 0.16))))} ${text(`${SAMPLE.slicesDone}/${SAMPLE.slicesTotal}`)}`,\n\t]);\n\tlines.push(rule(width, \"borderAccent\"));\n\tlines.push(rowOne);\n\tlines.push(rowTwo);\n\tlines.push(rule(width, \"borderAccent\"));\n\tlines.push(stateLine(\"dense-grid\", width, lines.length + 1));\n\treturn lines.map((line) => padLine(line, width));\n}\n\nexport const WIDGET_PROTOTYPES: WidgetPrototype[] = [\n\t{\n\t\tid: \"current-small\",\n\t\tlabel: \"A · Current small\",\n\t\ttagline: \"Baseline: existing small mode shape, vertical action/progress stack\",\n\t\trender: renderCurrentSmall,\n\t},\n\t{\n\t\tid: \"horizontal-bar\",\n\t\tlabel: \"B · Horizontal bar\",\n\t\ttagline: \"3-line widget, action + status use the full terminal width\",\n\t\trender: renderHorizontalBar,\n\t},\n\t{\n\t\tid: \"split-ribbon\",\n\t\tlabel: \"C · Split ribbon\",\n\t\ttagline: \"Four labeled columns without duplicating footer commands\",\n\t\trender: renderSplitRibbon,\n\t},\n\t{\n\t\tid: \"dense-grid\",\n\t\tlabel: \"D · Dense grid\",\n\t\ttagline: \"Selected: two metric rows, maximum scan data without growing height\",\n\t\trender: renderDenseGrid,\n\t},\n];\n\nexport function getWidgetPrototype(id: string): WidgetPrototype {\n\tconst found = WIDGET_PROTOTYPES.find((prototype) => prototype.id === id);\n\tif (!found) {\n\t\tthrow new Error(`Unknown widget prototype: ${id}. Options: ${WIDGET_PROTOTYPES.map((p) => p.id).join(\", \")}`);\n\t}\n\treturn found;\n}\n\nexport function resolveWidgetPrototypeSet(arg: string): WidgetPrototype[] {\n\tif (arg === \"all\") return WIDGET_PROTOTYPES;\n\tif (arg === \"recommended\") return [getWidgetPrototype(RECOMMENDED_WIDGET_PROTOTYPE_ID)];\n\treturn [getWidgetPrototype(arg)];\n}\n\nexport function listWidgetPrototypeIds(): WidgetPrototypeId[] {\n\treturn WIDGET_PROTOTYPES.map((prototype) => prototype.id);\n}\n\nexport function renderWidgetPrototypeBanner(prototype: WidgetPrototype, width: number): string[] {\n\tconst labelText = `${prototype.label} ${prototype.id === RECOMMENDED_WIDGET_PROTOTYPE_ID ? \"★ \" : \"\"}${prototype.tagline}`;\n\treturn [\n\t\trule(width, prototype.id === RECOMMENDED_WIDGET_PROTOTYPE_ID ? \"borderAccent\" : \"borderMuted\"),\n\t\tpadLine(labelText, width),\n\t\trule(width, prototype.id === RECOMMENDED_WIDGET_PROTOTYPE_ID ? \"borderAccent\" : \"borderMuted\"),\n\t];\n}\n"]}
|