@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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
|
|
4
4
|
import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
|
|
5
5
|
import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
|
|
6
|
-
import { getActiveRequirements,
|
|
6
|
+
import { getActiveRequirements, getAllMilestones, getMilestone, getSliceStatusSummary, getSliceTaskCounts, insertMilestone, insertAssessment, insertGateRun, readTransaction, saveGateResult, upsertQualityGate, } from "../gsd-db.js";
|
|
7
7
|
import { GATE_REGISTRY } from "../gate-registry.js";
|
|
8
8
|
import { generateRequirementsMd, saveArtifactToDb } from "../db-writer.js";
|
|
9
9
|
import { clearPathCache, relSliceFile, resolveGsdPathContract, resolveMilestoneFile, resolveSliceFile } from "../paths.js";
|
|
@@ -74,6 +74,103 @@ function registerProjectMilestoneSequence(content) {
|
|
|
74
74
|
}
|
|
75
75
|
return registered;
|
|
76
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Best-effort recovery of the human one-liner for each milestone id from a
|
|
79
|
+
* (possibly malformed) Milestone Sequence body. Deliberately lenient: tolerates
|
|
80
|
+
* any separator the canonical MILESTONE_LINE_RE rejects (en-dash, " : ", a
|
|
81
|
+
* missing checkbox, etc.) so a model formatting slip does not discard the prose.
|
|
82
|
+
*/
|
|
83
|
+
function recoverMilestoneTails(sequenceBody) {
|
|
84
|
+
const out = new Map();
|
|
85
|
+
const lenient = /^\s*(?:-\s*)?(?:\[[ xX]\]\s*)?(M\d{3})\b\s*[:.\-–—]*\s*(.*)$/;
|
|
86
|
+
for (const rawLine of sequenceBody.split("\n")) {
|
|
87
|
+
const m = rawLine.match(lenient);
|
|
88
|
+
if (m)
|
|
89
|
+
out.set(m[1], m[2].trim());
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function firstSentence(text) {
|
|
94
|
+
const trimmed = text.trim();
|
|
95
|
+
if (!trimmed)
|
|
96
|
+
return "";
|
|
97
|
+
const idx = trimmed.search(/[.!?](\s|$)/);
|
|
98
|
+
return (idx >= 0 ? trimmed.slice(0, idx + 1) : trimmed).trim();
|
|
99
|
+
}
|
|
100
|
+
/** Render one canonical, parseable milestone line for the given DB row. */
|
|
101
|
+
function renderMilestoneLine(m, recoveredTail) {
|
|
102
|
+
const done = m.status === "complete";
|
|
103
|
+
let oneLiner = recoveredTail;
|
|
104
|
+
// The recovered tail often still carries the title (e.g. "Foo — bar" or
|
|
105
|
+
// "Foo : bar"). Strip a leading repetition of the title, then any separator.
|
|
106
|
+
if (oneLiner.toLowerCase().startsWith(m.title.toLowerCase())) {
|
|
107
|
+
oneLiner = oneLiner.slice(m.title.length).replace(/^\s*[:.\-–—]+\s*/, "").trim();
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const sep = oneLiner.match(/\s+(?:—|–|--|-|:)\s+/);
|
|
111
|
+
if (sep && sep.index !== undefined)
|
|
112
|
+
oneLiner = oneLiner.slice(sep.index + sep[0].length).trim();
|
|
113
|
+
}
|
|
114
|
+
// MILESTONE_LINE_RE requires non-empty prose after the separator.
|
|
115
|
+
if (!oneLiner)
|
|
116
|
+
oneLiner = firstSentence(m.vision) || (done ? "Completed." : "Planned.");
|
|
117
|
+
return `- [${done ? "x" : " "}] ${m.id}: ${m.title} — ${oneLiner}`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Rebuild the "## Milestone Sequence" section from authoritative DB rows when a
|
|
121
|
+
* model-authored PROJECT.md projection parsed to zero milestone lines but the DB
|
|
122
|
+
* already holds milestones. The DB is the source of truth (markdown is a
|
|
123
|
+
* projection), so this repairs the projection rather than failing the save.
|
|
124
|
+
* Preserves a leading HTML comment in the section and recovers one-liners
|
|
125
|
+
* best-effort. The returned content parses cleanly under MILESTONE_LINE_RE.
|
|
126
|
+
*/
|
|
127
|
+
function rebuildMilestoneSequenceSection(content, milestones) {
|
|
128
|
+
const lines = content.split("\n");
|
|
129
|
+
const headerIdx = lines.findIndex(l => /^##\s+Milestone Sequence\s*$/.test(l));
|
|
130
|
+
const canonicalLines = (() => {
|
|
131
|
+
// Recover tails from the existing (malformed) body when the section exists.
|
|
132
|
+
let body = "";
|
|
133
|
+
if (headerIdx !== -1) {
|
|
134
|
+
let end = headerIdx + 1;
|
|
135
|
+
while (end < lines.length && !/^##\s+/.test(lines[end]))
|
|
136
|
+
end++;
|
|
137
|
+
body = lines.slice(headerIdx + 1, end).join("\n");
|
|
138
|
+
}
|
|
139
|
+
const tails = recoverMilestoneTails(body);
|
|
140
|
+
return milestones.map(m => renderMilestoneLine(m, tails.get(m.id) ?? ""));
|
|
141
|
+
})();
|
|
142
|
+
if (headerIdx === -1) {
|
|
143
|
+
// No section at all — append a fresh, canonical one.
|
|
144
|
+
const sep = content.endsWith("\n") ? "" : "\n";
|
|
145
|
+
return `${content}${sep}\n## Milestone Sequence\n\n${canonicalLines.join("\n")}\n`;
|
|
146
|
+
}
|
|
147
|
+
let bodyEnd = headerIdx + 1;
|
|
148
|
+
while (bodyEnd < lines.length && !/^##\s+/.test(lines[bodyEnd]))
|
|
149
|
+
bodyEnd++;
|
|
150
|
+
const existingBody = lines.slice(headerIdx + 1, bodyEnd);
|
|
151
|
+
// Preserve a contiguous leading HTML comment block (the "Check off…" hint).
|
|
152
|
+
let i = 0;
|
|
153
|
+
while (i < existingBody.length && existingBody[i].trim() === "")
|
|
154
|
+
i++;
|
|
155
|
+
const preserved = [];
|
|
156
|
+
if (i < existingBody.length && existingBody[i].trim().startsWith("<!--")) {
|
|
157
|
+
while (i < existingBody.length) {
|
|
158
|
+
preserved.push(existingBody[i]);
|
|
159
|
+
const closed = existingBody[i].includes("-->");
|
|
160
|
+
i++;
|
|
161
|
+
if (closed)
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return [
|
|
166
|
+
...lines.slice(0, headerIdx + 1),
|
|
167
|
+
"",
|
|
168
|
+
...(preserved.length ? [...preserved, ""] : []),
|
|
169
|
+
...canonicalLines,
|
|
170
|
+
"",
|
|
171
|
+
...lines.slice(bodyEnd),
|
|
172
|
+
].join("\n");
|
|
173
|
+
}
|
|
77
174
|
async function mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, content) {
|
|
78
175
|
const contract = resolveGsdPathContract(basePath);
|
|
79
176
|
if (!contract.worktreeGsd)
|
|
@@ -192,6 +289,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
192
289
|
}, basePath);
|
|
193
290
|
await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, contentToSave);
|
|
194
291
|
let registeredMilestones = [];
|
|
292
|
+
let milestoneSequenceSelfHealed = false;
|
|
195
293
|
if (params.artifact_type === "PROJECT") {
|
|
196
294
|
try {
|
|
197
295
|
registeredMilestones = registerProjectMilestoneSequence(contentToSave);
|
|
@@ -227,28 +325,78 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
227
325
|
};
|
|
228
326
|
}
|
|
229
327
|
if (registeredMilestones.length === 0) {
|
|
230
|
-
|
|
328
|
+
const existingMilestones = getAllMilestones();
|
|
329
|
+
if (existingMilestones.length === 0) {
|
|
330
|
+
// Genuine first-save failure: no milestones parsed AND none in the DB.
|
|
331
|
+
// /gsd really would report "No Active Milestone" — hard-fail so the
|
|
332
|
+
// caller rewrites the sequence before proceeding.
|
|
333
|
+
logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
|
|
334
|
+
tool: "gsd_summary_save",
|
|
335
|
+
});
|
|
336
|
+
// PROJECT.md was persisted; invalidate so subsequent reads see the new
|
|
337
|
+
// artifacts row even though no milestones registered.
|
|
338
|
+
invalidateStateCache();
|
|
339
|
+
return {
|
|
340
|
+
content: [{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
|
|
343
|
+
`so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
|
|
344
|
+
`Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
|
|
345
|
+
`\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
|
|
346
|
+
}],
|
|
347
|
+
details: {
|
|
348
|
+
operation: "save_summary",
|
|
349
|
+
path: relativePath,
|
|
350
|
+
artifact_type: params.artifact_type,
|
|
351
|
+
error: "milestone_registration_empty_parse",
|
|
352
|
+
},
|
|
353
|
+
isError: true,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
// Existing DB rows mean this is projection drift, not data loss. Rebuild
|
|
357
|
+
// the section from DB state and re-persist a parseable projection.
|
|
358
|
+
logWarning("tool", `gsd_summary_save: PROJECT.md parsed zero milestone lines but DB has ${existingMilestones.length} — rebuilding Milestone Sequence from DB (projection self-heal)`, {
|
|
231
359
|
tool: "gsd_summary_save",
|
|
360
|
+
path: relativePath,
|
|
232
361
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
362
|
+
try {
|
|
363
|
+
const healed = rebuildMilestoneSequenceSection(contentToSave, existingMilestones);
|
|
364
|
+
await saveArtifactToDb({ path: relativePath, artifact_type: params.artifact_type, content: healed }, basePath);
|
|
365
|
+
await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, healed);
|
|
366
|
+
const healedRegisteredMilestones = registerProjectMilestoneSequence(healed);
|
|
367
|
+
if (healedRegisteredMilestones.length === 0) {
|
|
368
|
+
throw new Error("self-healed PROJECT.md still parsed zero milestone lines");
|
|
369
|
+
}
|
|
370
|
+
registeredMilestones = healedRegisteredMilestones;
|
|
371
|
+
milestoneSequenceSelfHealed = true;
|
|
372
|
+
}
|
|
373
|
+
catch (healErr) {
|
|
374
|
+
const msg = healErr instanceof Error ? healErr.message : String(healErr);
|
|
375
|
+
logError("tool", `gsd_summary_save: Milestone Sequence self-heal failed: ${msg}`, {
|
|
376
|
+
tool: "gsd_summary_save",
|
|
246
377
|
path: relativePath,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
378
|
+
error: msg,
|
|
379
|
+
});
|
|
380
|
+
invalidateStateCache();
|
|
381
|
+
return {
|
|
382
|
+
content: [{
|
|
383
|
+
type: "text",
|
|
384
|
+
text: `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
|
|
385
|
+
`and automatic DB-backed Milestone Sequence repair failed: ${msg}. ` +
|
|
386
|
+
`Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
|
|
387
|
+
`\`- [ ] M001: <Title> — <One-liner>\`, then re-call gsd_summary_save(PROJECT).`,
|
|
388
|
+
}],
|
|
389
|
+
details: {
|
|
390
|
+
operation: "save_summary",
|
|
391
|
+
path: relativePath,
|
|
392
|
+
artifact_type: params.artifact_type,
|
|
393
|
+
error: "milestone_sequence_self_heal_failed",
|
|
394
|
+
self_heal_error: msg,
|
|
395
|
+
},
|
|
396
|
+
isError: true,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
invalidateStateCache();
|
|
252
400
|
}
|
|
253
401
|
}
|
|
254
402
|
if (params.artifact_type === "CONTEXT" && !params.task_id) {
|
|
@@ -271,6 +419,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
271
419
|
artifact_type: params.artifact_type,
|
|
272
420
|
content_source: contentSource,
|
|
273
421
|
...(registeredMilestones.length > 0 ? { registeredMilestones } : {}),
|
|
422
|
+
...(milestoneSequenceSelfHealed ? { milestoneSequenceSelfHealed: true } : {}),
|
|
274
423
|
},
|
|
275
424
|
};
|
|
276
425
|
}
|
|
@@ -10,6 +10,26 @@ const APPROVAL_QUESTION_RE = /\b(?:confirm|confirmation|approve|approval|approve
|
|
|
10
10
|
const APPROVAL_RIGHT_QUESTION_RE = /\b(?:does|do|is|are|was|were|did)\b[^\n?]{0,120}\bright\b/i;
|
|
11
11
|
const APPROVAL_CHANGE_QUESTION_RE = /\b(?:anything\s+else|anything|something)\s+to\s+(?:adjust|add|remove|reclassify)\b/i;
|
|
12
12
|
const RESEARCH_DECISION_QUESTION_RE = /\b(?:research|skip)\b/i;
|
|
13
|
+
function extractVisibleTextFromMessage(msg) {
|
|
14
|
+
if (!msg || typeof msg !== "object")
|
|
15
|
+
return "";
|
|
16
|
+
const content = msg.content;
|
|
17
|
+
if (typeof content === "string")
|
|
18
|
+
return content;
|
|
19
|
+
if (!Array.isArray(content))
|
|
20
|
+
return "";
|
|
21
|
+
const parts = [];
|
|
22
|
+
for (const block of content) {
|
|
23
|
+
if (!block || typeof block !== "object")
|
|
24
|
+
continue;
|
|
25
|
+
const typed = block;
|
|
26
|
+
if (typed.type === "text" && typeof typed.text === "string") {
|
|
27
|
+
parts.push(typed.text);
|
|
28
|
+
}
|
|
29
|
+
// thinking blocks intentionally excluded — they are internal reasoning, not user-visible
|
|
30
|
+
}
|
|
31
|
+
return parts.join("\n");
|
|
32
|
+
}
|
|
13
33
|
function extractTextFromMessage(msg) {
|
|
14
34
|
if (!msg || typeof msg !== "object")
|
|
15
35
|
return "";
|
|
@@ -26,6 +46,9 @@ function extractTextFromMessage(msg) {
|
|
|
26
46
|
if (typed.type === "text" && typeof typed.text === "string") {
|
|
27
47
|
parts.push(typed.text);
|
|
28
48
|
}
|
|
49
|
+
if (typed.type === "thinking" && typeof typed.thinking === "string") {
|
|
50
|
+
parts.push(typed.thinking);
|
|
51
|
+
}
|
|
29
52
|
}
|
|
30
53
|
return parts.join("\n");
|
|
31
54
|
}
|
|
@@ -44,6 +67,21 @@ export function lastAssistantText(messages) {
|
|
|
44
67
|
}
|
|
45
68
|
return "";
|
|
46
69
|
}
|
|
70
|
+
function lastAssistantVisibleText(messages) {
|
|
71
|
+
if (!Array.isArray(messages))
|
|
72
|
+
return "";
|
|
73
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
74
|
+
const msg = messages[i];
|
|
75
|
+
if (!msg || typeof msg !== "object")
|
|
76
|
+
continue;
|
|
77
|
+
if (msg.role !== "assistant")
|
|
78
|
+
continue;
|
|
79
|
+
const text = extractVisibleTextFromMessage(msg).trim();
|
|
80
|
+
if (text)
|
|
81
|
+
return text;
|
|
82
|
+
}
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
47
85
|
function anyMessageMatches(messages, pattern) {
|
|
48
86
|
if (!Array.isArray(messages))
|
|
49
87
|
return false;
|
|
@@ -52,7 +90,7 @@ function anyMessageMatches(messages, pattern) {
|
|
|
52
90
|
return false;
|
|
53
91
|
if (msg.role === "user")
|
|
54
92
|
return false;
|
|
55
|
-
return pattern.test(
|
|
93
|
+
return pattern.test(extractVisibleTextFromMessage(msg));
|
|
56
94
|
});
|
|
57
95
|
}
|
|
58
96
|
function hasApprovalQuestion(text) {
|
|
@@ -117,7 +155,7 @@ export function isAwaitingUserInput(messages) {
|
|
|
117
155
|
return true;
|
|
118
156
|
if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE))
|
|
119
157
|
return true;
|
|
120
|
-
const text =
|
|
158
|
+
const text = lastAssistantVisibleText(messages);
|
|
121
159
|
if (!text)
|
|
122
160
|
return false;
|
|
123
161
|
if (APPROVAL_WAIT_RE.test(text))
|
|
@@ -132,7 +170,7 @@ export function isAwaitingApprovalBoundary(messages) {
|
|
|
132
170
|
return true;
|
|
133
171
|
if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE))
|
|
134
172
|
return true;
|
|
135
|
-
const text =
|
|
173
|
+
const text = lastAssistantVisibleText(messages);
|
|
136
174
|
if (!text)
|
|
137
175
|
return false;
|
|
138
176
|
if (APPROVAL_WAIT_RE.test(text))
|
|
@@ -146,7 +184,7 @@ export function shouldPauseForUserApprovalQuestion(unitType, messages) {
|
|
|
146
184
|
return true;
|
|
147
185
|
if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE))
|
|
148
186
|
return true;
|
|
149
|
-
const text =
|
|
187
|
+
const text = lastAssistantVisibleText(messages);
|
|
150
188
|
if (!text)
|
|
151
189
|
return false;
|
|
152
190
|
if (APPROVAL_WAIT_RE.test(text))
|