gsd-pi 2.78.1-dev.eccf86e27 → 2.79.0-dev.579e14e9b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -47
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto-prompts.js +52 -29
- package/dist/resources/extensions/gsd/auto-recovery.js +18 -3
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -37
- package/dist/resources/extensions/gsd/commands/context.js +1 -1
- package/dist/resources/extensions/gsd/preferences-types.js +20 -2
- package/dist/resources/extensions/gsd/preferences-validation.js +3 -3
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +41 -2
- package/dist/resources/extensions/gsd/unit-context-composer.js +32 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +21 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- 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/required-server-files.json +1 -1
- 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/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 +10 -10
- 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/@img/sharp-libvips-linuxmusl-x64/lib/index.js +1 -0
- package/dist/web/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
- package/dist/web/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +42 -0
- package/dist/web/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +30 -0
- package/dist/web/standalone/node_modules/@img/sharp-linuxmusl-x64/LICENSE +191 -0
- package/dist/web/standalone/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
- package/dist/web/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +46 -0
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/workflow-tools.d.ts +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +53 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/workflow-tools.test.ts +116 -0
- package/packages/mcp-server/src/workflow-tools.ts +81 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +106 -28
- package/src/resources/extensions/gsd/auto-recovery.ts +17 -3
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +38 -38
- package/src/resources/extensions/gsd/commands/context.ts +1 -1
- package/src/resources/extensions/gsd/preferences-types.ts +23 -4
- package/src/resources/extensions/gsd/preferences-validation.ts +3 -3
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +68 -1
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/current-directory-root-homedir-fallback.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +3 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +41 -1
- package/src/resources/extensions/gsd/unit-context-composer.ts +49 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +34 -0
- /package/dist/web/standalone/.next/static/{Y5UeGFkXTYM9WIQOWHkot → X6D0ObmOxuQCMG5piZpbE}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Y5UeGFkXTYM9WIQOWHkot → X6D0ObmOxuQCMG5piZpbE}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -27,54 +27,95 @@ One command. Walk away. Come back to a built project with clean git history.
|
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
## What's New in v2.
|
|
30
|
+
## What's New in v2.79
|
|
31
31
|
|
|
32
|
-
###
|
|
32
|
+
### DB-Authoritative Runtime State
|
|
33
33
|
|
|
34
|
-
- **
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
- **
|
|
34
|
+
- **Auto-mode coordination tables (#5247)** — workers, leases, dispatches, and a command queue are now first-class DB rows, replacing ad-hoc files for cross-process auto coordination.
|
|
35
|
+
- **`auto.lock` + `paused-session.json` → DB (#5250)** — phase C pt 2 of the runtime-state migration. The on-disk lockfile and paused-session JSON are gone; lock acquisition, stale-lock detection, and pause state all live in `gsd.db`.
|
|
36
|
+
- **Stuck-state migration (#5249)** — `copyPlanningArtifacts` / `reconcile` deleted; auto-mode writers canonicalize through the DB instead of mirroring artifacts to disk.
|
|
37
|
+
- **`gsd headless recover`** — non-TTY DB recovery command for unattended environments where the interactive recover flow can't run.
|
|
38
|
+
- **Doctor surfaces DB locks** — `gsd doctor` reports DB-backed stale locks and flags exhausted `run-uat` retry counters so wedged runs are visible without log archaeology.
|
|
39
39
|
|
|
40
|
-
###
|
|
40
|
+
### Workspace & Worktree Isolation
|
|
41
41
|
|
|
42
|
-
-
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
46
|
-
- **
|
|
47
|
-
- **
|
|
48
|
-
- **Opt-in `reassess-roadmap` (#4778)** — gated behind the `skip_clean_reassess` preference per ADR-003 §4; auto no longer triggers reassessment unprompted.
|
|
42
|
+
- **`GsdWorkspace` + `MilestoneScope` handle types** — workspace identity is now an explicit handle threaded through writers, metrics, and the DB connection instead of a module singleton.
|
|
43
|
+
- **Symlink-safe canonicalization** — `deriveState` read root and cache key are canonicalized; path normalization unified on `realpathSync.native`; `gsdRootCache` keys normalized and decoupled from per-turn cache clears; `git-root` anchor guarded against `~/.gsd` resolution.
|
|
44
|
+
- **Worktree-scoped DB + metrics** — `gsd-db` connections and metrics ledgers are scoped by workspace identity, so concurrent milestones in separate worktrees can't cross-contaminate state.
|
|
45
|
+
- **Worktree cwd anchoring** — subagent dispatch anchors to the canonical worktree path, `mergeAndExit` anchors cwd at project root (closes [#5079](https://github.com/gsd-build/GSD-2/issues/5079)), MCP cwd is hardened against drift, and dispatch stops on cwd anchor failures.
|
|
46
|
+
- **Slice isolation through resolver (#5246)** — slice merge isolation routed through the worktree resolver; `originalBase` checks use `isSamePath` instead of string equality; `auto-worktree` validates `milestoneId` match in `ByScope` wrappers.
|
|
47
|
+
- **Worktree CLI commands** — `gsd worktree {list, merge, clean, remove}` exposed in the TUI dispatcher.
|
|
49
48
|
|
|
50
|
-
###
|
|
49
|
+
### Deep Planning Mode (Phase 11)
|
|
51
50
|
|
|
52
|
-
-
|
|
53
|
-
- **
|
|
54
|
-
- **
|
|
55
|
-
- **
|
|
51
|
+
- **`/gsd new-project --deep`** — opt-in deep mode for new projects, with `research-decision` and `research-project` dispatch units, deep-mode artifact validators, and gated approval flows that pause the milestone instead of aborting on grounding/discovery questions.
|
|
52
|
+
- **EVAL-REVIEW system** — `/gsd eval-review` command, frontmatter schema, pre-ship soft warning when EVAL-REVIEW status is incomplete, and registration in the catalog + ops dispatcher.
|
|
53
|
+
- **Project-shape-aware discuss depth** — discuss-phase questioning depth scales via the project-shape classifier; tiny apps cap research and auto-skip stale research blockers; deep research is opt-in and blocked while a marker is in flight.
|
|
54
|
+
- **Approval-gate hardening** — deep approval gates verified, opening interview question protected from approval abort, plain-text setup approvals gated, deep setup state guarded against phantom transitions.
|
|
56
55
|
|
|
57
|
-
###
|
|
56
|
+
### Auto Pipeline & Dispatch
|
|
58
57
|
|
|
59
|
-
- **
|
|
60
|
-
- **
|
|
61
|
-
- **
|
|
62
|
-
- **
|
|
63
|
-
-
|
|
64
|
-
- **Visual postinstall (#4641)** — install shows a spinner/banner UX so first-run state is legible.
|
|
65
|
-
- **PR-risk verification** — risk prompt emits a copy-pasteable code block to make follow-up commands one step.
|
|
58
|
+
- **Delegation-policy verdicts** — dispatch actions are annotated with a per-tool background-safety policy verdict; the policy itself is codified instead of inferred per call.
|
|
59
|
+
- **Reactive-execute default** — auto-dispatch defaults to reactive-execute when ≥3 ready tasks are queued, replacing the always-sequential fallback.
|
|
60
|
+
- **Proactive rate limiting (#2996)** — `min_request_interval_ms` preference paces auto-mode requests; the interval is stamped at dispatch.
|
|
61
|
+
- **Manifest-driven skill surfacing** — `auto-prompts` surfaces manifest skills via recommendations + auto-match; planning-dispatch mode added to the unit manifest for slice plan/complete; runtime tools-policy enforced for planning units (#4934).
|
|
62
|
+
- **Subagent telemetry** — dispatch telemetry, stronger prompt guidelines, and model-override threading (`??` consistently used for `modelOverride`).
|
|
66
63
|
|
|
67
|
-
###
|
|
64
|
+
### MCP, Models & Cross-Platform
|
|
68
65
|
|
|
69
|
-
- **
|
|
70
|
-
-
|
|
71
|
-
- **
|
|
72
|
-
- **
|
|
73
|
-
- **
|
|
74
|
-
- **
|
|
75
|
-
- **Empty-turn recovery** — Claude Code CLI tool-block shape canonicalized so empty-turn recovery matches real provider output.
|
|
66
|
+
- **Global MCP config** — `mcp-client` reads `~/.gsd/mcp.json` for user-level MCP server config.
|
|
67
|
+
- **`ask_user_questions` over MCP** — host elicitation tried before remote channel; multi-select array shape preserved; explicit cancellation handled; remote answers normalized into `structuredContent`.
|
|
68
|
+
- **Model routing overhaul** — cross-provider tier resolution, provider-agnostic profile-tier defaults, disabled-provider routing denylist, resolved tier model IDs normalized, and `findModelForTier` with behavioral tests.
|
|
69
|
+
- **Ollama** — configurable probe/request timeouts via env vars (clamped); cloud / long-variant context windows reported correctly.
|
|
70
|
+
- **Windows fixes** — home directory resolved correctly (#5015), `Path` env lookup repaired, Windows-safe env CLI shims, DEP0190 avoided in Claude CLI binary probes, tool-bootstrap skipped when tools are on PATH.
|
|
71
|
+
- **Anthropic prompt-cache preserved (#5019)** — `pi-coding-agent` and `gsd` no longer thrash the cache on dispatch.
|
|
76
72
|
|
|
77
|
-
|
|
73
|
+
### Git, GitHub Sync & Reliability
|
|
74
|
+
|
|
75
|
+
- **Integration-branch self-merge guard (#5024)** — milestone refuses to self-merge when the integration branch equals the milestone branch; self-merge ref guard normalized.
|
|
76
|
+
- **Slice-cadence safety** — integration branch used for slice cadence, dirty worktree state preserved on merge, checked-out slice worktrees advance safely, slice PRs deferred until completion, slice-PR sync stays retryable on failure.
|
|
77
|
+
- **Milestone-ID reservation** — DB rows included in reservation; ghost IDs reused in `nextMilestoneId` to close gaps; `ensurePreconditions` guarded against phantom IDs (#4996); orphan milestone-dir doctor check (#4996); milestone-dir creation deferred until first artifact write.
|
|
78
|
+
- **Auto-commit hygiene** — task commits scoped to reported files, generated commit subjects sanitized, user hooks run on automated commits, milestone-tagged commits bound when `.gsd/` is gitignored (#5033), startup blocked on `git index.lock`.
|
|
79
|
+
- **GitHub sync** — issues no longer closed before delivery, failed task closure / slice PR sync stay retryable, config cache scoped by project, safe git environment used.
|
|
80
|
+
|
|
81
|
+
### Safety, Recovery & Hardening
|
|
82
|
+
|
|
83
|
+
- **Policy-block pause through dispatch errors** — pause state is preserved when dispatch errors out mid-policy-block.
|
|
84
|
+
- **Skip-validation persistence** — skip-validation state persists and gate rows are cleared on recover.
|
|
85
|
+
- **Cancelled-gate hard block** — redirected to `ask_user_questions`; false plain-text claim dropped from the message.
|
|
86
|
+
- **Write-gate basePath required** — `process.cwd()` defaults removed; basePath threaded through pre-existing tests.
|
|
87
|
+
- **Atomic metrics writes** — parallel-mode `metrics.json` writes merge atomically; ledger cache invalidated after prune; `saveLedger` aborts when lock not acquired; stale-lock detection adds PID stamp + async yield.
|
|
88
|
+
- **Auto-worktree teardown** — `activeWorkspace` guaranteed cleared on teardown failure; teardown try/finally broadened to cover `chdir`; cleanup steps mirrored on the abort path; warning emitted on resume when persisted worktree is missing.
|
|
89
|
+
- **Empty-turn nudge** — no longer auto-replies to user questions; deferred on mid-line approval prompts.
|
|
90
|
+
- **Refuses project writes from `$HOME`** — and surfaces real SQL errors from `capture_thought` instead of swallowing them.
|
|
91
|
+
|
|
92
|
+
### VS Code & TUI
|
|
93
|
+
|
|
94
|
+
- **Checkpoint tree view** — registered in the VS Code extension; checkpoint file existence restored; RPC file-mutation events tracked; agent diff scoped to tracked files.
|
|
95
|
+
- **TUI image paste** — pasted images preserved on regular submit.
|
|
96
|
+
- **Bundled `/gsd` slash command protected** — core handler refuses to overwrite the bundled command.
|
|
97
|
+
|
|
98
|
+
See the full [Changelog](./CHANGELOG.md) for the complete v2.79 entry and prior releases.
|
|
99
|
+
|
|
100
|
+
<details>
|
|
101
|
+
<summary>v2.78 highlights</summary>
|
|
102
|
+
|
|
103
|
+
- **Slice-cadence worktree collapse (#4765)** — `git.collapse_cadence: "milestone" | "slice"` shrinks the orphan window per-slice; pair with `git.milestone_resquash: true` to collapse to one milestone commit
|
|
104
|
+
- **Worktree telemetry (#4764)** — journal events + `summarizeWorktreeTelemetry`; `/gsd forensics` worktree section with `worktree-orphan` and `worktree-unmerged-exit` anomalies
|
|
105
|
+
- **Worktree-aware canonical milestone root (#4761)** — `resolveCanonicalMilestoneRoot` routes validators through the live worktree
|
|
106
|
+
- **Bootstrap orphan audit (#4762)** — in-progress milestones with commits ahead of main are no longer skipped
|
|
107
|
+
- **Unified component system** — skills, agents, pipelines, and marketplace as one component model wired through runtime, dispatch, and telemetry
|
|
108
|
+
- **UnitContextManifest v2 (#4924, #4934)** — typed manifest with declarative tools-policy and typed computed artifacts; CI-guarded
|
|
109
|
+
- **Composer migration phase 3 (#4782)** — `complete-slice`, `research-milestone`, `run-uat`, `reassess-roadmap` build context through the manifest composer
|
|
110
|
+
- **Milestone scope classifier + pipeline variants (#4781)** — auto picks a pipeline variant from milestone shape
|
|
111
|
+
- **Per-unit-type skill manifest resolver (#4779)** — skills wire into specific unit types instead of being globally on
|
|
112
|
+
- **Single-writer-v3 control plane** — durable-state writer gaps closed
|
|
113
|
+
- **Extensions framework** — `gsd extensions install / update / uninstall / list / info / validate`; topological load order; cmux↔gsd decoupling via `cmux-events`; extracted `@gsd-extensions/google-search`
|
|
114
|
+
- **GPT-5.5 Codex support** + `xhigh` thinking level; auth mode in `/model`; permission granularity picker; headless auto default → `bypassPermissions` (#4657)
|
|
115
|
+
- **Major git-safety pass** — TOCTOU ancestry guard, atomic sync-lock with PID-verified stale override, `.git/index.lock` 5-min gate, env overlay strip, rebase/cherry-pick/revert detection, working-tree stash before `reset --hard`, unborn-branch worktree guard, user hooks + `commit.gpgsign` on auto-commits
|
|
116
|
+
- **Atomic `.gsd/` state writes**; **compaction correctness fix (#4665)**; **write-gate hardening (#4950)**; **auto state-machine** non-retriable policy errors (#4973), baseline restoration (#4961, #4966); slice + crash recovery; empty-turn recovery shape canonicalized
|
|
117
|
+
|
|
118
|
+
</details>
|
|
78
119
|
|
|
79
120
|
<details>
|
|
80
121
|
<summary>v2.77 highlights</summary>
|
|
@@ -320,25 +361,27 @@ The database is authoritative for milestones, slices, tasks, requirements, decis
|
|
|
320
361
|
|
|
321
362
|
2. **Context pre-loading** — The dispatch prompt includes inlined task plans, slice plans, prior task summaries, dependency summaries, roadmap excerpts, and decisions register. The LLM starts with everything it needs instead of spending tool calls reading files.
|
|
322
363
|
|
|
323
|
-
3. **
|
|
364
|
+
3. **Context Mode** — Context Mode is enabled by default and gives every auto-mode unit guidance for preserving context. Agents are steered toward `gsd_exec` for noisy scans, builds, tests, and diagnostics; full stdout/stderr is saved under `.gsd/exec/` while only a short digest enters the conversation. `gsd_exec_search` lets agents reuse prior runs instead of repeating expensive checks, and `gsd_resume` reads `.gsd/last-snapshot.md` after compaction or resume. Opt out with `context_mode.enabled: false`; tune sandbox timeout/output caps with `context_mode.exec_timeout_ms`, `context_mode.exec_stdout_cap_bytes`, and `context_mode.exec_digest_chars`.
|
|
365
|
+
|
|
366
|
+
4. **Git isolation** — When `git.isolation` is set to `worktree` or `branch`, each milestone runs on its own `milestone/<MID>` branch (in a worktree or in-place). All slice work commits sequentially — no branch switching, no merge conflicts. When the milestone completes, it's squash-merged to main as one clean commit. The default is `none` (work on the current branch), configurable via preferences. If `worktree` is configured in a repo with no committed `HEAD`, GSD temporarily behaves as `none` until the first commit exists because git worktrees need a committed start point.
|
|
324
367
|
|
|
325
|
-
|
|
368
|
+
5. **Crash recovery** — Auto mode persists worker state, unit-dispatch state, and paused-session metadata in the project-root SQLite database. If the session dies, the next `/gsd auto` reconstructs the interrupted unit from DB-backed runtime state, reads the surviving session file, synthesizes a recovery briefing from every tool call that made it to disk, and resumes with full context. Parallel orchestrator IPC still lives under `.gsd/parallel/`, so multi-worker sessions survive crashes too. In headless mode, crashes trigger automatic restart with exponential backoff (default 3 attempts).
|
|
326
369
|
|
|
327
|
-
|
|
370
|
+
6. **Provider error recovery** — Transient provider errors (rate limits, 500/503 server errors, overloaded) auto-resume after a delay. Permanent errors (auth, billing) pause for manual review. The model fallback chain retries transient network errors before switching models.
|
|
328
371
|
|
|
329
|
-
|
|
372
|
+
7. **Stuck and artifact detection** — A sliding-window detector identifies repeated dispatch patterns (including multi-unit cycles). Missing expected artifacts use a separate bounded path: GSD retries artifact verification up to 3 times with failure context, then pauses auto mode with the missing artifact error instead of looping indefinitely.
|
|
330
373
|
|
|
331
|
-
|
|
374
|
+
8. **Timeout supervision** — Soft timeout warns the LLM to wrap up. Idle watchdog detects stalls. Hard timeout pauses auto mode. Recovery steering nudges the LLM to finish durable output before giving up.
|
|
332
375
|
|
|
333
|
-
|
|
376
|
+
9. **Cost tracking** — Every unit's token usage and cost is captured, broken down by phase, slice, and model. The dashboard shows running totals and projections. Budget ceilings can pause auto mode before overspending.
|
|
334
377
|
|
|
335
|
-
|
|
378
|
+
10. **Adaptive replanning** — After each slice completes, the roadmap is reassessed. If the work revealed new information that changes the plan, slices are reordered, added, or removed before continuing.
|
|
336
379
|
|
|
337
|
-
|
|
380
|
+
11. **Verification enforcement** — Configure shell commands (`npm run lint`, `npm run test`, etc.) that run automatically after task execution. Failures trigger auto-fix retries before advancing. Auto-discovered checks from `package.json` run in advisory mode — they log warnings but don't block on pre-existing errors. Configurable via `verification_commands`, `verification_auto_fix`, and `verification_max_retries` preferences.
|
|
338
381
|
|
|
339
|
-
|
|
382
|
+
12. **Milestone validation** — After all slices complete, a `validate-milestone` gate compares roadmap success criteria against actual results before sealing the milestone.
|
|
340
383
|
|
|
341
|
-
|
|
384
|
+
13. **Escape hatch** — Press Escape to pause. The conversation is preserved. Interact with the agent, inspect what happened, or just `/gsd auto` to resume from disk state.
|
|
342
385
|
|
|
343
386
|
### `/gsd` and `/gsd next` — Step Mode
|
|
344
387
|
|
|
@@ -616,6 +659,10 @@ auto_report: true
|
|
|
616
659
|
| `unique_milestone_ids` | Uses unique milestone names to avoid clashes when working in teams of people |
|
|
617
660
|
| `git.isolation` | `none` (default), `worktree`, or `branch` — enable worktree or branch isolation for milestone work. `worktree` requires a committed `HEAD`; zero-commit repos temporarily run as `none` |
|
|
618
661
|
| `git.manage_gitignore` | Set `false` to prevent GSD from modifying `.gitignore` |
|
|
662
|
+
| `context_mode.enabled` | Context Mode is default-on; set `false` to disable `gsd_exec`, exec history guidance, and resume snapshots |
|
|
663
|
+
| `context_mode.exec_timeout_ms` | Timeout for sandboxed `gsd_exec` runs (default: 30000) |
|
|
664
|
+
| `context_mode.exec_stdout_cap_bytes` | Persisted stdout cap for `gsd_exec` output (default: 1048576) |
|
|
665
|
+
| `context_mode.exec_digest_chars` | Trailing stdout characters returned to the agent context (default: 300) |
|
|
619
666
|
| `verification_commands` | Array of shell commands to run after task execution (e.g., `["npm run lint", "npm run test"]`) |
|
|
620
667
|
| `verification_auto_fix` | Auto-retry on verification failures (default: true) |
|
|
621
668
|
| `verification_max_retries` | Max retries for verification failures (default: 2) |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
97e376d99205971f
|
|
@@ -10,6 +10,7 @@ import { hasVerdict, getUatType } from "./verdict-parser.js";
|
|
|
10
10
|
import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
|
|
11
11
|
import { resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveTasksDir, resolveTaskFiles, resolveTaskFile, relMilestoneFile, relSliceFile, relSlicePath, relMilestonePath, resolveGsdRootFile, relGsdRootFile, resolveRuntimeFile, } from "./paths.js";
|
|
12
12
|
import { resolveSkillDiscoveryMode, resolveInlineLevel, loadEffectiveGSDPreferences, resolveAllSkillReferences } from "./preferences.js";
|
|
13
|
+
import { isContextModeEnabled } from "./preferences-types.js";
|
|
13
14
|
import { parseRoadmap } from "./parsers-legacy.js";
|
|
14
15
|
import { getLoadedSkills } from "@gsd/pi-coding-agent";
|
|
15
16
|
import { join, basename } from "node:path";
|
|
@@ -19,7 +20,7 @@ import { getPendingGatesForTurn } from "./gsd-db.js";
|
|
|
19
20
|
import { assertGateCoverage, getGatesForTurn, } from "./gate-registry.js";
|
|
20
21
|
import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
|
|
21
22
|
import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
|
|
22
|
-
import { composeInlinedContext } from "./unit-context-composer.js";
|
|
23
|
+
import { composeContextModeInstructions, composeInlinedContext } from "./unit-context-composer.js";
|
|
23
24
|
import { logWarning } from "./workflow-logger.js";
|
|
24
25
|
import { inlineGraphSubgraph } from "./graph-context.js";
|
|
25
26
|
import { buildExtractionStepsBlock } from "./commands-extract-learnings.js";
|
|
@@ -71,6 +72,21 @@ function capPreamble(preamble) {
|
|
|
71
72
|
return preamble;
|
|
72
73
|
return truncateAtSectionBoundary(preamble, budget).content;
|
|
73
74
|
}
|
|
75
|
+
function renderContextModeForPrompt(unitType, base, renderMode = "standalone") {
|
|
76
|
+
const effectivePrefs = loadEffectiveGSDPreferences(base)?.preferences;
|
|
77
|
+
return composeContextModeInstructions(unitType, {
|
|
78
|
+
enabled: isContextModeEnabled(effectivePrefs),
|
|
79
|
+
renderMode,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function prependContextModeToBlock(unitType, base, block, renderMode = "standalone") {
|
|
83
|
+
const contextMode = renderContextModeForPrompt(unitType, base, renderMode);
|
|
84
|
+
if (!contextMode)
|
|
85
|
+
return block;
|
|
86
|
+
if (!block.trim())
|
|
87
|
+
return contextMode;
|
|
88
|
+
return `${contextMode}\n\n${block}`;
|
|
89
|
+
}
|
|
74
90
|
// ─── Executor Constraints ─────────────────────────────────────────────────────
|
|
75
91
|
/**
|
|
76
92
|
* Format executor context constraints for injection into the plan-slice prompt.
|
|
@@ -1119,6 +1135,7 @@ export async function checkNeedsRunUat(base, mid, state, prefs) {
|
|
|
1119
1135
|
*/
|
|
1120
1136
|
export async function buildDiscussMilestonePrompt(mid, midTitle, base, structuredQuestionsAvailable = "false") {
|
|
1121
1137
|
const discussTemplates = inlineTemplate("context", "Context");
|
|
1138
|
+
const contextModeInstructions = renderContextModeForPrompt("discuss-milestone", base);
|
|
1122
1139
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
1123
1140
|
workingDirectory: base,
|
|
1124
1141
|
milestoneId: mid,
|
|
@@ -1128,13 +1145,14 @@ export async function buildDiscussMilestonePrompt(mid, midTitle, base, structure
|
|
|
1128
1145
|
commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
|
|
1129
1146
|
fastPathInstruction: "",
|
|
1130
1147
|
});
|
|
1148
|
+
const promptWithContextMode = prependContextModeToBlock("discuss-milestone", base, basePrompt);
|
|
1131
1149
|
// If a CONTEXT-DRAFT.md exists, append it as seed material
|
|
1132
1150
|
const draftPath = resolveMilestoneFile(base, mid, "CONTEXT-DRAFT");
|
|
1133
1151
|
const draftContent = draftPath ? await loadFile(draftPath) : null;
|
|
1134
1152
|
if (draftContent) {
|
|
1135
|
-
return `${
|
|
1153
|
+
return `${promptWithContextMode}\n\n## Prior Discussion (Draft Seed)\n\nThe following draft was captured from a prior multi-milestone discussion. Use it as seed material — the user has already provided this context. Start with a brief reflection on what the draft covers, then probe for any gaps or open questions before writing the full CONTEXT.md.\n\n${draftContent}`;
|
|
1136
1154
|
}
|
|
1137
|
-
return basePrompt;
|
|
1155
|
+
return contextModeInstructions ? promptWithContextMode : basePrompt;
|
|
1138
1156
|
}
|
|
1139
1157
|
/**
|
|
1140
1158
|
* Build a prompt for the workflow-preferences unit type (deep mode).
|
|
@@ -1143,10 +1161,10 @@ export async function buildDiscussMilestonePrompt(mid, midTitle, base, structure
|
|
|
1143
1161
|
* in deep-mode bootstrap before discuss-project.
|
|
1144
1162
|
*/
|
|
1145
1163
|
export async function buildWorkflowPreferencesPrompt(base, structuredQuestionsAvailable = "false") {
|
|
1146
|
-
return loadPrompt("guided-workflow-preferences", {
|
|
1164
|
+
return prependContextModeToBlock("workflow-preferences", base, loadPrompt("guided-workflow-preferences", {
|
|
1147
1165
|
workingDirectory: base,
|
|
1148
1166
|
structuredQuestionsAvailable,
|
|
1149
|
-
});
|
|
1167
|
+
}));
|
|
1150
1168
|
}
|
|
1151
1169
|
/**
|
|
1152
1170
|
* Build a prompt for the research-project (parallel) unit type (deep mode).
|
|
@@ -1156,10 +1174,10 @@ export async function buildWorkflowPreferencesPrompt(base, structuredQuestionsAv
|
|
|
1156
1174
|
* are missing. Skipped entirely if user picked "skip".
|
|
1157
1175
|
*/
|
|
1158
1176
|
export async function buildResearchProjectPrompt(base, structuredQuestionsAvailable = "false") {
|
|
1159
|
-
return loadPrompt("guided-research-project", {
|
|
1177
|
+
return prependContextModeToBlock("research-project", base, loadPrompt("guided-research-project", {
|
|
1160
1178
|
workingDirectory: base,
|
|
1161
1179
|
structuredQuestionsAvailable,
|
|
1162
|
-
});
|
|
1180
|
+
}));
|
|
1163
1181
|
}
|
|
1164
1182
|
/**
|
|
1165
1183
|
* Build a prompt for the research-decision unit type (deep mode).
|
|
@@ -1168,10 +1186,10 @@ export async function buildResearchProjectPrompt(base, structuredQuestionsAvaila
|
|
|
1168
1186
|
* and before research-project-parallel.
|
|
1169
1187
|
*/
|
|
1170
1188
|
export async function buildResearchDecisionPrompt(base, structuredQuestionsAvailable = "false") {
|
|
1171
|
-
return loadPrompt("guided-research-decision", {
|
|
1189
|
+
return prependContextModeToBlock("research-decision", base, loadPrompt("guided-research-decision", {
|
|
1172
1190
|
workingDirectory: base,
|
|
1173
1191
|
structuredQuestionsAvailable,
|
|
1174
|
-
});
|
|
1192
|
+
}));
|
|
1175
1193
|
}
|
|
1176
1194
|
/**
|
|
1177
1195
|
* Build a prompt for the discuss-project unit type (deep mode).
|
|
@@ -1181,12 +1199,12 @@ export async function buildResearchDecisionPrompt(base, structuredQuestionsAvail
|
|
|
1181
1199
|
*/
|
|
1182
1200
|
export async function buildDiscussProjectPrompt(base, structuredQuestionsAvailable = "false") {
|
|
1183
1201
|
const inlinedTemplates = inlineTemplate("project", "Project");
|
|
1184
|
-
return loadPrompt("guided-discuss-project", {
|
|
1202
|
+
return prependContextModeToBlock("discuss-project", base, loadPrompt("guided-discuss-project", {
|
|
1185
1203
|
workingDirectory: base,
|
|
1186
1204
|
inlinedTemplates,
|
|
1187
1205
|
structuredQuestionsAvailable,
|
|
1188
1206
|
commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
|
|
1189
|
-
});
|
|
1207
|
+
}));
|
|
1190
1208
|
}
|
|
1191
1209
|
/**
|
|
1192
1210
|
* Build a prompt for the discuss-requirements unit type (deep mode).
|
|
@@ -1196,12 +1214,12 @@ export async function buildDiscussProjectPrompt(base, structuredQuestionsAvailab
|
|
|
1196
1214
|
*/
|
|
1197
1215
|
export async function buildDiscussRequirementsPrompt(base, structuredQuestionsAvailable = "false") {
|
|
1198
1216
|
const inlinedTemplates = inlineTemplate("requirements", "Requirements");
|
|
1199
|
-
return loadPrompt("guided-discuss-requirements", {
|
|
1217
|
+
return prependContextModeToBlock("discuss-requirements", base, loadPrompt("guided-discuss-requirements", {
|
|
1200
1218
|
workingDirectory: base,
|
|
1201
1219
|
inlinedTemplates,
|
|
1202
1220
|
structuredQuestionsAvailable,
|
|
1203
1221
|
commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
|
|
1204
|
-
});
|
|
1222
|
+
}));
|
|
1205
1223
|
}
|
|
1206
1224
|
export async function buildResearchMilestonePrompt(mid, midTitle, base) {
|
|
1207
1225
|
// #4782 phase 3: research-milestone migrated through the composer.
|
|
@@ -1254,7 +1272,7 @@ export async function buildResearchMilestonePrompt(mid, midTitle, base) {
|
|
|
1254
1272
|
if (knowledgeInlineRM)
|
|
1255
1273
|
parts.push(knowledgeInlineRM);
|
|
1256
1274
|
}
|
|
1257
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${parts.join("\n\n---\n\n")}`);
|
|
1275
|
+
const inlinedContext = prependContextModeToBlock("research-milestone", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${parts.join("\n\n---\n\n")}`));
|
|
1258
1276
|
const outputRelPath = relMilestoneFile(base, mid, "RESEARCH");
|
|
1259
1277
|
return loadPrompt("research-milestone", {
|
|
1260
1278
|
workingDirectory: base,
|
|
@@ -1324,7 +1342,7 @@ export async function buildPlanMilestonePrompt(mid, midTitle, base, level) {
|
|
|
1324
1342
|
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
1325
1343
|
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
1326
1344
|
}
|
|
1327
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1345
|
+
const inlinedContext = prependContextModeToBlock("plan-milestone", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`));
|
|
1328
1346
|
const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
|
|
1329
1347
|
const researchOutputPath = join(base, relMilestoneFile(base, mid, "RESEARCH"));
|
|
1330
1348
|
const secretsOutputPath = join(base, relMilestoneFile(base, mid, "SECRETS"));
|
|
@@ -1349,7 +1367,7 @@ export async function buildPlanMilestonePrompt(mid, midTitle, base, level) {
|
|
|
1349
1367
|
...buildSkillDiscoveryVars(),
|
|
1350
1368
|
});
|
|
1351
1369
|
}
|
|
1352
|
-
export async function buildResearchSlicePrompt(mid, _midTitle, sid, sTitle, base) {
|
|
1370
|
+
export async function buildResearchSlicePrompt(mid, _midTitle, sid, sTitle, base, options) {
|
|
1353
1371
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
1354
1372
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1355
1373
|
const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
|
|
@@ -1400,7 +1418,7 @@ export async function buildResearchSlicePrompt(mid, _midTitle, sid, sTitle, base
|
|
|
1400
1418
|
const overridesInline = formatOverridesSection(activeOverrides);
|
|
1401
1419
|
if (overridesInline)
|
|
1402
1420
|
inlined.unshift(overridesInline);
|
|
1403
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1421
|
+
const inlinedContext = prependContextModeToBlock("research-slice", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`), options?.contextModeRenderMode);
|
|
1404
1422
|
const outputRelPath = relSliceFile(base, mid, sid, "RESEARCH");
|
|
1405
1423
|
return loadPrompt("research-slice", {
|
|
1406
1424
|
workingDirectory: base,
|
|
@@ -1483,7 +1501,7 @@ async function renderSlicePrompt(options) {
|
|
|
1483
1501
|
const overridesInline = formatOverridesSection(await loadActiveOverrides(base));
|
|
1484
1502
|
if (overridesInline)
|
|
1485
1503
|
inlined.unshift(overridesInline);
|
|
1486
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1504
|
+
const inlinedContext = prependContextModeToBlock(promptTemplate, base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`), options.contextModeRenderMode);
|
|
1487
1505
|
const executorContextConstraints = formatExecutorConstraints(sessionContextWindow, modelRegistry, sessionProvider);
|
|
1488
1506
|
const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
|
|
1489
1507
|
const commitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
|
|
@@ -1686,6 +1704,7 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
|
|
|
1686
1704
|
const etPending = getPendingGatesForTurn(mid, sid, "execute-task", tid);
|
|
1687
1705
|
assertGateCoverage(etPending, "execute-task", { requireAll: false });
|
|
1688
1706
|
const gatesToClose = renderGatesToCloseBlock(getGatesForTurn("execute-task"), { pending: new Set(etPending.map((g) => g.gate_id)), allowOmit: true });
|
|
1707
|
+
phaseAnchorSection = prependContextModeToBlock("execute-task", base, phaseAnchorSection, opts.contextModeRenderMode);
|
|
1689
1708
|
return loadPrompt("execute-task", {
|
|
1690
1709
|
overridesSection,
|
|
1691
1710
|
runtimeContext,
|
|
@@ -1713,6 +1732,7 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
|
|
|
1713
1732
|
taskTitle: tTitle,
|
|
1714
1733
|
taskPlanContent,
|
|
1715
1734
|
extraContext: [taskPlanInline, slicePlanExcerpt, finalCarryForward, resumeSection],
|
|
1735
|
+
unitType: "execute-task",
|
|
1716
1736
|
}),
|
|
1717
1737
|
});
|
|
1718
1738
|
}
|
|
@@ -1803,7 +1823,7 @@ export async function buildCompleteSlicePrompt(mid, midTitle, sid, sTitle, base,
|
|
|
1803
1823
|
const finalBody = completeOverridesInline
|
|
1804
1824
|
? `${completeOverridesInline}\n\n---\n\n${body}`
|
|
1805
1825
|
: body;
|
|
1806
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${finalBody}`);
|
|
1826
|
+
const inlinedContext = prependContextModeToBlock("complete-slice", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${finalBody}`));
|
|
1807
1827
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1808
1828
|
const sliceRel = relSlicePath(base, mid, sid);
|
|
1809
1829
|
const sliceSummaryPath = join(base, `${sliceRel}/${sid}-SUMMARY.md`);
|
|
@@ -1894,7 +1914,7 @@ export async function buildCompleteMilestonePrompt(mid, midTitle, base, level) {
|
|
|
1894
1914
|
if (contextInline)
|
|
1895
1915
|
inlined.push(contextInline);
|
|
1896
1916
|
inlined.push(inlineTemplate("milestone-summary", "Milestone Summary"));
|
|
1897
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1917
|
+
const inlinedContext = prependContextModeToBlock("complete-milestone", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`));
|
|
1898
1918
|
const milestoneSummaryPath = join(base, `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md`);
|
|
1899
1919
|
const learningsRelPath = join(relMilestonePath(base, mid), `${mid}-LEARNINGS.md`);
|
|
1900
1920
|
const learningsAbsPath = join(base, learningsRelPath);
|
|
@@ -2034,7 +2054,7 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
|
|
|
2034
2054
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
2035
2055
|
if (contextInline)
|
|
2036
2056
|
inlined.push(contextInline);
|
|
2037
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
2057
|
+
const inlinedContext = prependContextModeToBlock("validate-milestone", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`));
|
|
2038
2058
|
const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
|
|
2039
2059
|
const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
|
|
2040
2060
|
// Every milestone validation turn owns MV01–MV04 unconditionally: the
|
|
@@ -2104,7 +2124,7 @@ export async function buildReplanSlicePrompt(mid, midTitle, sid, sTitle, base) {
|
|
|
2104
2124
|
const replanOverridesInline = formatOverridesSection(replanActiveOverrides);
|
|
2105
2125
|
if (replanOverridesInline)
|
|
2106
2126
|
inlined.unshift(replanOverridesInline);
|
|
2107
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
2127
|
+
const inlinedContext = prependContextModeToBlock("replan-slice", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`));
|
|
2108
2128
|
const replanPath = join(base, `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md`);
|
|
2109
2129
|
// Build capture context for replan prompt (captures that triggered this replan)
|
|
2110
2130
|
let captureContext = "(none)";
|
|
@@ -2171,7 +2191,7 @@ export async function buildRunUatPrompt(mid, sliceId, uatPath, uatContent, base)
|
|
|
2171
2191
|
}
|
|
2172
2192
|
};
|
|
2173
2193
|
const composed = await composeInlinedContext("run-uat", resolveArtifact);
|
|
2174
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${composed}`);
|
|
2194
|
+
const inlinedContext = prependContextModeToBlock("run-uat", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${composed}`));
|
|
2175
2195
|
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "ASSESSMENT"));
|
|
2176
2196
|
const uatType = getUatType(uatContent);
|
|
2177
2197
|
return loadPrompt("run-uat", {
|
|
@@ -2242,7 +2262,7 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
|
|
|
2242
2262
|
const knowledgeInlineRA = await inlineKnowledgeBudgeted(base, extractKeywords(midTitle));
|
|
2243
2263
|
if (knowledgeInlineRA)
|
|
2244
2264
|
parts.push(knowledgeInlineRA);
|
|
2245
|
-
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${parts.join("\n\n---\n\n")}`);
|
|
2265
|
+
const inlinedContext = prependContextModeToBlock("reassess-roadmap", base, capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${parts.join("\n\n---\n\n")}`));
|
|
2246
2266
|
const assessmentPath = join(base, relSliceFile(base, mid, completedSliceId, "ASSESSMENT"));
|
|
2247
2267
|
// Build deferred captures context for reassess prompt
|
|
2248
2268
|
let deferredCaptures = "(none)";
|
|
@@ -2313,6 +2333,7 @@ export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, rea
|
|
|
2313
2333
|
sessionContextWindow: opts?.sessionContextWindow,
|
|
2314
2334
|
modelRegistry: opts?.modelRegistry,
|
|
2315
2335
|
sessionProvider: opts?.sessionProvider,
|
|
2336
|
+
contextModeRenderMode: "nested",
|
|
2316
2337
|
});
|
|
2317
2338
|
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
2318
2339
|
subagentSections.push([
|
|
@@ -2332,7 +2353,7 @@ export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, rea
|
|
|
2332
2353
|
milestoneTitle: midTitle,
|
|
2333
2354
|
sliceId: sid,
|
|
2334
2355
|
sliceTitle: sTitle,
|
|
2335
|
-
graphContext,
|
|
2356
|
+
graphContext: prependContextModeToBlock("reactive-execute", base, graphContext),
|
|
2336
2357
|
readyTaskCount: String(readyTaskIds.length),
|
|
2337
2358
|
readyTaskList: readyTaskListLines.join("\n"),
|
|
2338
2359
|
subagentPrompts: subagentSections.join("\n\n---\n\n"),
|
|
@@ -2382,7 +2403,7 @@ export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, b
|
|
|
2382
2403
|
const subagentSections = [];
|
|
2383
2404
|
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
2384
2405
|
for (const slice of slices) {
|
|
2385
|
-
const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath);
|
|
2406
|
+
const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath, { contextModeRenderMode: "nested" });
|
|
2386
2407
|
subagentSections.push([
|
|
2387
2408
|
`### ${slice.id}: ${slice.title}`,
|
|
2388
2409
|
"",
|
|
@@ -2426,6 +2447,8 @@ export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base,
|
|
|
2426
2447
|
for (const def of gateDefs) {
|
|
2427
2448
|
gateListLines.push(`- **${def.id}**: ${def.question}`);
|
|
2428
2449
|
const subPrompt = [
|
|
2450
|
+
renderContextModeForPrompt("gate-evaluate", base, "nested"),
|
|
2451
|
+
"",
|
|
2429
2452
|
`You are evaluating quality gate **${def.id}** for slice ${sid} (${sTitle}).`,
|
|
2430
2453
|
"",
|
|
2431
2454
|
`**Working directory:** \`${normalizedBase}\`. All file reads, writes, and shell commands MUST operate relative to this directory. Do NOT \`cd\` to any other directory.`,
|
|
@@ -2466,7 +2489,7 @@ export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base,
|
|
|
2466
2489
|
milestoneTitle: midTitle,
|
|
2467
2490
|
sliceId: sid,
|
|
2468
2491
|
sliceTitle: sTitle,
|
|
2469
|
-
slicePlanContent: planContent,
|
|
2492
|
+
slicePlanContent: prependContextModeToBlock("gate-evaluate", base, planContent),
|
|
2470
2493
|
gateCount: String(pending.length),
|
|
2471
2494
|
gateList: gateListLines.join("\n"),
|
|
2472
2495
|
subagentPrompts: subagentSections.join("\n\n---\n\n"),
|
|
@@ -2536,7 +2559,7 @@ export async function buildRewriteDocsPrompt(mid, midTitle, activeSlice, base, o
|
|
|
2536
2559
|
`**During:** ${o.appliedAt}`,
|
|
2537
2560
|
].join("\n")).join("\n\n");
|
|
2538
2561
|
const documentList = docList.length > 0 ? docList.join("\n") : "- No active plan documents found.";
|
|
2539
|
-
return loadPrompt("rewrite-docs", {
|
|
2562
|
+
return prependContextModeToBlock("rewrite-docs", base, loadPrompt("rewrite-docs", {
|
|
2540
2563
|
workingDirectory: base,
|
|
2541
2564
|
milestoneId: mid,
|
|
2542
2565
|
milestoneTitle: midTitle,
|
|
@@ -2545,5 +2568,5 @@ export async function buildRewriteDocsPrompt(mid, midTitle, activeSlice, base, o
|
|
|
2545
2568
|
overrideContent,
|
|
2546
2569
|
documentList,
|
|
2547
2570
|
overridesPath: relGsdRootFile("OVERRIDES"),
|
|
2548
|
-
});
|
|
2571
|
+
}));
|
|
2549
2572
|
}
|
|
@@ -257,7 +257,7 @@ function scanGsdTaggedCommits(basePath, milestoneId, gitArgs) {
|
|
|
257
257
|
if (!commitMessageHasGsdTrailer(message))
|
|
258
258
|
continue;
|
|
259
259
|
const commitFiles = getChangedFilesForCommit(basePath, hash);
|
|
260
|
-
if (!commitMatchesMilestone(message, milestoneId, commitFiles))
|
|
260
|
+
if (!commitMatchesMilestone(basePath, message, milestoneId, commitFiles))
|
|
261
261
|
continue;
|
|
262
262
|
matched = true;
|
|
263
263
|
for (const file of commitFiles) {
|
|
@@ -278,22 +278,37 @@ function getChangedFilesForCommit(basePath, hash) {
|
|
|
278
278
|
function commitMessageHasGsdTrailer(message) {
|
|
279
279
|
return /^GSD-(?:Task|Unit):\s*\S+/m.test(message);
|
|
280
280
|
}
|
|
281
|
-
function commitMatchesMilestone(message, milestoneId, files) {
|
|
281
|
+
function commitMatchesMilestone(basePath, message, milestoneId, files) {
|
|
282
282
|
if (commitTrailerStartsWithMilestone(message, milestoneId))
|
|
283
283
|
return true;
|
|
284
284
|
// Meaningful execute-task commits currently store task scope as Sxx/Tyy
|
|
285
285
|
// rather than Mxx/Sxx/Tyy. Bind those commits back to the milestone when
|
|
286
286
|
// either the commit touched this milestone's artifacts, or — for projects
|
|
287
287
|
// where .gsd/ is gitignored/external (#5033) — the message explicitly
|
|
288
|
-
// names the milestone.
|
|
288
|
+
// names the milestone or local GSD state proves the task belongs here.
|
|
289
289
|
if (/^GSD-Task:\s*S[^/\s]+\/T\S+/m.test(message)) {
|
|
290
290
|
if (files.some((file) => isMilestoneArtifactPath(file, milestoneId)))
|
|
291
291
|
return true;
|
|
292
292
|
if (commitMessageMentionsMilestone(message, milestoneId))
|
|
293
293
|
return true;
|
|
294
|
+
if (commitTaskTrailerBelongsToMilestone(basePath, message, milestoneId))
|
|
295
|
+
return true;
|
|
294
296
|
}
|
|
295
297
|
return false;
|
|
296
298
|
}
|
|
299
|
+
function commitTaskTrailerBelongsToMilestone(basePath, message, milestoneId) {
|
|
300
|
+
const match = message.match(/^GSD-Task:\s*(S[^/\s]+)\/(T[^\s]+)/m);
|
|
301
|
+
if (!match)
|
|
302
|
+
return false;
|
|
303
|
+
const [, sliceId, taskId] = match;
|
|
304
|
+
if (getTask(milestoneId, sliceId, taskId))
|
|
305
|
+
return true;
|
|
306
|
+
const tasksDir = resolveTasksDir(basePath, milestoneId, sliceId);
|
|
307
|
+
if (!tasksDir)
|
|
308
|
+
return false;
|
|
309
|
+
return existsSync(join(tasksDir, `${taskId}-PLAN.md`))
|
|
310
|
+
|| existsSync(join(tasksDir, `${taskId}-SUMMARY.md`));
|
|
311
|
+
}
|
|
297
312
|
function commitMessageMentionsMilestone(message, milestoneId) {
|
|
298
313
|
if (!MILESTONE_ID_RE.test(milestoneId))
|
|
299
314
|
return false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// GSD2 — Exec (context-mode) tool registration.
|
|
2
2
|
//
|
|
3
|
-
// Exposes the
|
|
4
|
-
// `context_mode.enabled:
|
|
3
|
+
// Exposes the Context Mode runtime tools in-process. Default-on; opt out with
|
|
4
|
+
// `context_mode.enabled: false` in preferences.
|
|
5
5
|
import { Type } from "@sinclair/typebox";
|
|
6
6
|
export function registerExecTools(pi) {
|
|
7
7
|
pi.registerTool({
|