gsd-pi 2.41.0-dev.9446b20 → 2.41.0-dev.97349b1
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 +69 -29
- package/dist/cli-web-branch.d.ts +6 -0
- package/dist/cli-web-branch.js +17 -0
- package/dist/onboarding.js +2 -1
- package/dist/resources/extensions/gsd/auto/loop.js +9 -1
- package/dist/resources/extensions/gsd/auto/phases.js +26 -8
- package/dist/resources/extensions/gsd/auto-dashboard.js +6 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +8 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
- package/dist/resources/extensions/gsd/auto.js +36 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +8 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/context-store.js +4 -3
- package/dist/resources/extensions/gsd/db-writer.js +5 -2
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/doctor.js +11 -1
- package/dist/resources/extensions/gsd/exit-command.js +12 -2
- package/dist/resources/extensions/gsd/export.js +9 -13
- package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
- package/dist/resources/extensions/gsd/files.js +28 -11
- package/dist/resources/extensions/gsd/forensics.js +10 -3
- package/dist/resources/extensions/gsd/git-service.js +5 -1
- package/dist/resources/extensions/gsd/gsd-db.js +25 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +7 -3
- package/dist/resources/extensions/gsd/journal.js +85 -0
- package/dist/resources/extensions/gsd/md-importer.js +5 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +1 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +46 -2
- package/dist/resources/extensions/gsd/rule-registry.js +489 -0
- package/dist/resources/extensions/gsd/rule-types.js +6 -0
- package/dist/resources/extensions/gsd/service-tier.js +138 -0
- package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
- package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
- package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
- package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
- package/dist/resources/extensions/subagent/index.js +7 -3
- package/dist/resources/extensions/voice/index.js +4 -4
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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 +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +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 +16 -16
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
- package/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +29 -7
- package/package.json +1 -1
- package/packages/native/src/__tests__/text.test.mjs +33 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
- package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
- package/src/resources/extensions/gsd/auto/loop.ts +10 -1
- package/src/resources/extensions/gsd/auto/phases.ts +28 -8
- package/src/resources/extensions/gsd/auto/types.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +7 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +8 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
- package/src/resources/extensions/gsd/auto.ts +40 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +8 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/context-store.ts +4 -3
- package/src/resources/extensions/gsd/db-writer.ts +6 -2
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/doctor.ts +12 -1
- package/src/resources/extensions/gsd/exit-command.ts +14 -2
- package/src/resources/extensions/gsd/export.ts +8 -15
- package/src/resources/extensions/gsd/extension-manifest.json +2 -2
- package/src/resources/extensions/gsd/files.ts +29 -12
- package/src/resources/extensions/gsd/forensics.ts +9 -3
- package/src/resources/extensions/gsd/git-service.ts +5 -4
- package/src/resources/extensions/gsd/gsd-db.ts +37 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +7 -3
- package/src/resources/extensions/gsd/journal.ts +134 -0
- package/src/resources/extensions/gsd/md-importer.ts +6 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +1 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +47 -2
- package/src/resources/extensions/gsd/rule-registry.ts +599 -0
- package/src/resources/extensions/gsd/rule-types.ts +68 -0
- package/src/resources/extensions/gsd/service-tier.ts +171 -0
- package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
- package/src/resources/extensions/gsd/templates/decisions.md +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +195 -105
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
- package/src/resources/extensions/gsd/types.ts +3 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
- package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
- package/src/resources/extensions/subagent/index.ts +7 -3
- package/src/resources/extensions/voice/index.ts +4 -4
- package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
- /package/dist/web/standalone/.next/static/{02cti5IXH7FycOqkbAkWL → ZrI3HOoXD7Fh84fAHZVxb}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{02cti5IXH7FycOqkbAkWL → ZrI3HOoXD7Fh84fAHZVxb}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -24,35 +24,75 @@ One command. Walk away. Come back to a built project with clean git history.
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
## What's New in v2.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
- **
|
|
51
|
-
- **
|
|
52
|
-
- **
|
|
53
|
-
- **
|
|
54
|
-
-
|
|
55
|
-
- **
|
|
27
|
+
## What's New in v2.41.0
|
|
28
|
+
|
|
29
|
+
### New Features
|
|
30
|
+
|
|
31
|
+
- **Browser-based web interface** — run GSD from the browser with `gsd --web`. Full project management, real-time progress, and multi-project support via server-sent events. (#1717)
|
|
32
|
+
- **Doctor: worktree lifecycle checks** — `/gsd doctor` now validates worktree health, detects orphaned worktrees, consolidates cleanup, and enhances `/worktree list` with lifecycle status. (#1814)
|
|
33
|
+
- **CI: docs-only PR detection** — PRs that only change documentation skip build and test steps, with a new prompt injection scan for security. (#1699)
|
|
34
|
+
- **Custom Models guide** — new documentation for adding custom providers (Ollama, vLLM, LM Studio, proxies) via `models.json`. (#1670)
|
|
35
|
+
|
|
36
|
+
### Data Loss Prevention (Critical Fixes)
|
|
37
|
+
|
|
38
|
+
This release includes 7 fixes preventing silent data loss in auto-mode:
|
|
39
|
+
|
|
40
|
+
- **Hallucination guard** — execute-task agents that complete with zero tool calls are now rejected as hallucinated. Previously, agents could produce detailed but fabricated summaries without writing any code, wasting ~$25/milestone. (#1838)
|
|
41
|
+
- **Merge anchor verification** — before deleting a milestone worktree/branch, GSD now verifies the code is actually on the integration branch. Prevents orphaning commits when squash-merge produces an empty diff. (#1829)
|
|
42
|
+
- **Dirty working tree detection** — `nativeMergeSquash` now distinguishes dirty-tree rejections from content conflicts, preventing silent commit loss when synced `.gsd/` files block the merge. (#1752)
|
|
43
|
+
- **Doctor cleanup safety** — the `orphaned_completed_units` check no longer auto-fixes during post-task health checks. Previously, timing races could cause the doctor to remove valid completion keys, reverting users to earlier tasks. (#1825)
|
|
44
|
+
- **Root file reverse-sync** — worktree teardown now syncs root-level `.gsd/` files (PROJECT.md, REQUIREMENTS.md, completed-units.json) back to the project root. Previously these were lost on milestone closeout. (#1831)
|
|
45
|
+
- **Empty merge guard** — milestone branches with unanchored code changes are preserved instead of deleted when squash-merge produces nothing to commit. (#1755)
|
|
46
|
+
- **Crash-safe task closeout** — orphaned checkboxes in PLAN.md are unchecked on retry, preventing phantom task completion. (#1759)
|
|
47
|
+
|
|
48
|
+
### Auto-Mode Stability
|
|
49
|
+
|
|
50
|
+
- **Terminal hang fix** — `stopAuto()` now resolves pending promises, preventing the terminal from freezing permanently after stopping auto-mode. (#1818)
|
|
51
|
+
- **Signal handler coverage** — SIGHUP and SIGINT now clean up lock files, not just SIGTERM. Prevents stranded locks on VS-Code crash. (#1821)
|
|
52
|
+
- **Needs-discussion routing** — milestones in `needs-discussion` phase now route to the smart entry UI instead of hard-stopping, breaking the infinite loop. (#1820)
|
|
53
|
+
- **Infrastructure error handling** — auto-mode stops immediately on ENOSPC, ENOMEM, and similar unrecoverable errors instead of retrying. (#1780)
|
|
54
|
+
- **Dependency-aware dispatch** — slice dispatch now uses declared `depends_on` instead of positional ordering. (#1770)
|
|
55
|
+
- **Queue mode depth verification** — the write gate now processes depth verification in queue mode, fixing a deadlock where CONTEXT.md writes were permanently blocked. (#1823)
|
|
56
|
+
|
|
57
|
+
### Roadmap Parser Improvements
|
|
58
|
+
|
|
59
|
+
- **Table format support** — roadmaps using markdown tables (`| S01 | Title | Risk | Status |`) are now parsed correctly. (#1741)
|
|
60
|
+
- **Prose header fallback** — when `## Slices` contains H3 headers instead of checkboxes, the prose parser is invoked as a fallback. (#1744)
|
|
61
|
+
- **Completion marker detection** — prose headers with `✓` or `(Complete)` markers are correctly identified as done. (#1816)
|
|
62
|
+
- **Zero-slice stub handling** — stub roadmaps from `/gsd queue` return `pre-planning` instead of `blocked`. (#1826)
|
|
63
|
+
- **Immediate roadmap fix** — roadmap checkbox and UAT stub are fixed immediately after last task instead of deferring to `complete-slice`. (#1819)
|
|
64
|
+
|
|
65
|
+
### State & Git Improvements
|
|
66
|
+
|
|
67
|
+
- **CONTEXT-DRAFT.md fallback** — `depends_on` is read from CONTEXT-DRAFT.md when CONTEXT.md doesn't exist, preventing draft milestones from being promoted past dependency constraints. (#1743)
|
|
68
|
+
- **Unborn branch support** — `nativeBranchExists` handles repos with zero commits, preventing dispatch deadlock on new repos. (#1815)
|
|
69
|
+
- **Ghost milestone detection** — empty `.gsd/milestones/` directories are skipped instead of crashing `deriveState()`. (#1817)
|
|
70
|
+
- **Default branch detection** — milestone merge detects `master` vs `main` instead of hardcoding. (#1669)
|
|
71
|
+
- **Milestone title extraction** — titles are pulled from CONTEXT.md headings when no ROADMAP exists. (#1729)
|
|
72
|
+
|
|
73
|
+
### Windows & Platform
|
|
74
|
+
|
|
75
|
+
- **Windows path handling** — 8.3 short paths, `pathToFileURL` for ESM imports, and `realpathSync.native` fixes across the test suite and verification gate. (#1804)
|
|
76
|
+
- **DEP0190 fix** — `spawnSync` deprecation warning eliminated by passing commands to shell explicitly. (#1827)
|
|
77
|
+
- **Web build skip on Windows** — Next.js webpack EPERM errors on system directories are handled gracefully.
|
|
78
|
+
|
|
79
|
+
### Developer Experience
|
|
80
|
+
|
|
81
|
+
- **@ file finder fix** — typing `@` no longer freezes the TUI. The fix adds debounce, dedup, and empty-query short-circuit. (#1832)
|
|
82
|
+
- **Tool-call loop guard** — detects and breaks infinite tool-call loops within a single unit, preventing stack overflow. (#1801)
|
|
83
|
+
- **Completion deferral fix** — roadmap checkbox and UAT stub are fixed at task level, closing the fragile handoff window between last task and `complete-slice`. (#1819)
|
|
84
|
+
|
|
85
|
+
See the full [Changelog](./CHANGELOG.md) for all 70+ fixes in this release.
|
|
86
|
+
|
|
87
|
+
### Previous highlights (v2.39–v2.40)
|
|
88
|
+
|
|
89
|
+
- **GitHub sync extension** — auto-sync milestones to GitHub Issues, PRs, and Milestones
|
|
90
|
+
- **Skill tool resolution** — skills auto-activate in dispatched prompts
|
|
91
|
+
- **Health check phase 2** — real-time doctor issues in dashboard and visualizer
|
|
92
|
+
- **Forensics upgrade** — full-access GSD debugger with anomaly detection
|
|
93
|
+
- **Pipeline decomposition** — auto-loop rewritten as linear phase pipeline
|
|
94
|
+
- **Sliding-window stuck detection** — pattern-aware, fewer false positives
|
|
95
|
+
- **Data-loss recovery** — automatic detection and recovery from v2.30–v2.38 migration issues
|
|
56
96
|
|
|
57
97
|
---
|
|
58
98
|
|
package/dist/cli-web-branch.d.ts
CHANGED
|
@@ -13,6 +13,12 @@ export interface CliFlags {
|
|
|
13
13
|
web?: boolean;
|
|
14
14
|
/** Optional project path for web mode: `gsd --web <path>` or `gsd web start <path>` */
|
|
15
15
|
webPath?: string;
|
|
16
|
+
/** Custom host to bind web server to: `--host 0.0.0.0` */
|
|
17
|
+
webHost?: string;
|
|
18
|
+
/** Custom port for web server: `--port 8080` */
|
|
19
|
+
webPort?: number;
|
|
20
|
+
/** Additional allowed origins for CORS: `--allowed-origins http://192.168.1.10:8080` */
|
|
21
|
+
webAllowedOrigins?: string[];
|
|
16
22
|
help?: boolean;
|
|
17
23
|
version?: boolean;
|
|
18
24
|
}
|
package/dist/cli-web-branch.js
CHANGED
|
@@ -29,6 +29,20 @@ export function parseCliArgs(argv) {
|
|
|
29
29
|
flags.webPath = args[++i];
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
+
else if (arg === '--host' && i + 1 < args.length) {
|
|
33
|
+
flags.webHost = args[++i];
|
|
34
|
+
}
|
|
35
|
+
else if (arg === '--port' && i + 1 < args.length) {
|
|
36
|
+
const portStr = args[++i];
|
|
37
|
+
const port = parseInt(portStr, 10);
|
|
38
|
+
if (Number.isFinite(port) && port > 0 && port < 65536) {
|
|
39
|
+
flags.webPort = port;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (arg === '--allowed-origins' && i + 1 < args.length) {
|
|
43
|
+
const origins = args[++i].split(',').map(o => o.trim()).filter(Boolean);
|
|
44
|
+
flags.webAllowedOrigins = (flags.webAllowedOrigins ?? []).concat(origins);
|
|
45
|
+
}
|
|
32
46
|
else if (arg === '--model' && i + 1 < args.length) {
|
|
33
47
|
flags.model = args[++i];
|
|
34
48
|
}
|
|
@@ -216,6 +230,9 @@ export async function runWebCliBranch(flags, deps = {}) {
|
|
|
216
230
|
cwd: currentCwd,
|
|
217
231
|
projectSessionsDir,
|
|
218
232
|
agentDir,
|
|
233
|
+
host: flags.webHost,
|
|
234
|
+
port: flags.webPort,
|
|
235
|
+
allowedOrigins: flags.webAllowedOrigins,
|
|
219
236
|
});
|
|
220
237
|
if (!status.ok) {
|
|
221
238
|
emitWebModeFailure(stderr, status);
|
package/dist/onboarding.js
CHANGED
|
@@ -94,7 +94,8 @@ async function loadPico() {
|
|
|
94
94
|
/** Open a URL in the system browser (best-effort, non-blocking) */
|
|
95
95
|
function openBrowser(url) {
|
|
96
96
|
if (process.platform === 'win32') {
|
|
97
|
-
|
|
97
|
+
// PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
|
|
98
|
+
execFile('powershell', ['-c', `Start-Process '${url.replace(/'/g, "''")}'`], () => { });
|
|
98
99
|
}
|
|
99
100
|
else {
|
|
100
101
|
const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Imports from: auto/types, auto/resolve, auto/phases
|
|
8
8
|
*/
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
9
10
|
import { MAX_LOOP_ITERATIONS, } from "./types.js";
|
|
10
11
|
import { _clearCurrentResolve } from "./resolve.js";
|
|
11
12
|
import { runPreDispatch, runDispatch, runGuards, runUnitPhase, runFinalize, } from "./phases.js";
|
|
@@ -27,6 +28,10 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
27
28
|
while (s.active) {
|
|
28
29
|
iteration++;
|
|
29
30
|
debugLog("autoLoop", { phase: "loop-top", iteration });
|
|
31
|
+
// ── Journal: per-iteration flow grouping ──
|
|
32
|
+
const flowId = randomUUID();
|
|
33
|
+
let seqCounter = 0;
|
|
34
|
+
const nextSeq = () => ++seqCounter;
|
|
30
35
|
if (iteration > MAX_LOOP_ITERATIONS) {
|
|
31
36
|
debugLog("autoLoop", {
|
|
32
37
|
phase: "exit",
|
|
@@ -53,6 +58,7 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
53
58
|
unitType: sidecarItem.unitType,
|
|
54
59
|
unitId: sidecarItem.unitId,
|
|
55
60
|
});
|
|
61
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "sidecar-dequeue", data: { kind: sidecarItem.kind, unitType: sidecarItem.unitType, unitId: sidecarItem.unitId } });
|
|
56
62
|
}
|
|
57
63
|
const sessionLockBase = deps.lockBase();
|
|
58
64
|
if (sessionLockBase) {
|
|
@@ -73,7 +79,8 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
73
79
|
break;
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
|
-
const ic = { ctx, pi, s, deps, prefs, iteration };
|
|
82
|
+
const ic = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
|
|
83
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-start", data: { iteration } });
|
|
77
84
|
let iterData;
|
|
78
85
|
if (!sidecarItem) {
|
|
79
86
|
// ── Phase 1: Pre-dispatch ─────────────────────────────────────────
|
|
@@ -121,6 +128,7 @@ export async function autoLoop(ctx, pi, s, deps) {
|
|
|
121
128
|
if (finalizeResult.action === "continue")
|
|
122
129
|
continue;
|
|
123
130
|
consecutiveErrors = 0; // Iteration completed successfully
|
|
131
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
|
|
124
132
|
debugLog("autoLoop", { phase: "iteration-complete", iteration });
|
|
125
133
|
}
|
|
126
134
|
catch (loopErr) {
|
|
@@ -13,6 +13,7 @@ import { runUnit } from "./run-unit.js";
|
|
|
13
13
|
import { debugLog } from "../debug-logger.js";
|
|
14
14
|
import { gsdRoot } from "../paths.js";
|
|
15
15
|
import { atomicWriteSync } from "../atomic-write.js";
|
|
16
|
+
import { PROJECT_FILES } from "../detection.js";
|
|
16
17
|
import { join } from "node:path";
|
|
17
18
|
// ─── generateMilestoneReport ──────────────────────────────────────────────────
|
|
18
19
|
/**
|
|
@@ -121,6 +122,7 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
121
122
|
});
|
|
122
123
|
// ── Milestone transition ────────────────────────────────────────────
|
|
123
124
|
if (mid && s.currentMilestoneId && mid !== s.currentMilestoneId) {
|
|
125
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "milestone-transition", data: { from: s.currentMilestoneId, to: mid } });
|
|
124
126
|
ctx.ui.notify(`Milestone ${s.currentMilestoneId} complete. Advancing to ${mid}: ${midTitle}.`, "info");
|
|
125
127
|
deps.sendDesktopNotification("GSD", `Milestone ${s.currentMilestoneId} complete!`, "success", "milestone");
|
|
126
128
|
deps.logCmuxEvent(prefs, `Milestone ${s.currentMilestoneId} complete. Advancing to ${mid}.`, "success");
|
|
@@ -248,6 +250,7 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
248
250
|
await deps.stopAuto(ctx, pi, `No active milestone — ${incomplete.length} incomplete (${ids}), see diagnostic above`);
|
|
249
251
|
}
|
|
250
252
|
debugLog("autoLoop", { phase: "exit", reason: "no-active-milestone" });
|
|
253
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "no-active-milestone" } });
|
|
251
254
|
return { action: "break", reason: "no-active-milestone" };
|
|
252
255
|
}
|
|
253
256
|
if (!midTitle) {
|
|
@@ -295,6 +298,7 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
295
298
|
deps.logCmuxEvent(prefs, `Milestone ${mid} complete.`, "success");
|
|
296
299
|
await closeoutAndStop(ctx, pi, s, deps, `Milestone ${mid} complete`);
|
|
297
300
|
debugLog("autoLoop", { phase: "exit", reason: "milestone-complete" });
|
|
301
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "milestone-complete", milestoneId: mid } });
|
|
298
302
|
return { action: "break", reason: "milestone-complete" };
|
|
299
303
|
}
|
|
300
304
|
// Terminal: blocked
|
|
@@ -305,6 +309,7 @@ export async function runPreDispatch(ic, loopState) {
|
|
|
305
309
|
deps.sendDesktopNotification("GSD", blockerMsg, "error", "attention");
|
|
306
310
|
deps.logCmuxEvent(prefs, blockerMsg, "error");
|
|
307
311
|
debugLog("autoLoop", { phase: "exit", reason: "blocked" });
|
|
312
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "blocked", blockers: state.blockers } });
|
|
308
313
|
return { action: "break", reason: "blocked" };
|
|
309
314
|
}
|
|
310
315
|
return { action: "next", data: { state, mid, midTitle } };
|
|
@@ -328,6 +333,7 @@ export async function runDispatch(ic, preData, loopState) {
|
|
|
328
333
|
session: s,
|
|
329
334
|
});
|
|
330
335
|
if (dispatchResult.action === "stop") {
|
|
336
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-stop", rule: dispatchResult.matchedRule, data: { reason: dispatchResult.reason } });
|
|
331
337
|
await closeoutAndStop(ctx, pi, s, deps, dispatchResult.reason);
|
|
332
338
|
debugLog("autoLoop", { phase: "exit", reason: "dispatch-stop" });
|
|
333
339
|
return { action: "break", reason: "dispatch-stop" };
|
|
@@ -337,6 +343,7 @@ export async function runDispatch(ic, preData, loopState) {
|
|
|
337
343
|
await new Promise((r) => setImmediate(r));
|
|
338
344
|
return { action: "continue" };
|
|
339
345
|
}
|
|
346
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-match", rule: dispatchResult.matchedRule, data: { unitType: dispatchResult.unitType, unitId: dispatchResult.unitId } });
|
|
340
347
|
let unitType = dispatchResult.unitType;
|
|
341
348
|
let unitId = dispatchResult.unitId;
|
|
342
349
|
let prompt = dispatchResult.prompt;
|
|
@@ -402,6 +409,7 @@ export async function runDispatch(ic, preData, loopState) {
|
|
|
402
409
|
const preDispatchResult = deps.runPreDispatchHooks(unitType, unitId, prompt, s.basePath);
|
|
403
410
|
if (preDispatchResult.firedHooks.length > 0) {
|
|
404
411
|
ctx.ui.notify(`Pre-dispatch hook${preDispatchResult.firedHooks.length > 1 ? "s" : ""}: ${preDispatchResult.firedHooks.join(", ")}`, "info");
|
|
412
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "pre-dispatch-hook", data: { firedHooks: preDispatchResult.firedHooks, action: preDispatchResult.action } });
|
|
405
413
|
}
|
|
406
414
|
if (preDispatchResult.action === "skip") {
|
|
407
415
|
ctx.ui.notify(`Skipping ${unitType} ${unitId} (pre-dispatch hook).`, "info");
|
|
@@ -544,25 +552,27 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
544
552
|
unitType,
|
|
545
553
|
unitId,
|
|
546
554
|
});
|
|
547
|
-
// ── Worktree health check (#1833)
|
|
555
|
+
// ── Worktree health check (#1833, #1843) ────────────────────────────
|
|
548
556
|
// Verify the working directory is a valid git checkout with project
|
|
549
557
|
// files before dispatching work. A broken worktree causes agents to
|
|
550
558
|
// hallucinate summaries since they cannot read or write any files.
|
|
559
|
+
// Uses the shared PROJECT_FILES list from detection.ts to support all
|
|
560
|
+
// ecosystems (Rust, Go, Python, Java, etc.), not just JS.
|
|
551
561
|
if (s.basePath && unitType === "execute-task") {
|
|
552
562
|
const gitMarker = join(s.basePath, ".git");
|
|
553
563
|
const hasGit = deps.existsSync(gitMarker);
|
|
554
|
-
const hasPackageJson = deps.existsSync(join(s.basePath, "package.json"));
|
|
555
|
-
const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
|
|
556
564
|
if (!hasGit) {
|
|
557
565
|
const msg = `Worktree health check failed: ${s.basePath} has no .git — refusing to dispatch ${unitType} ${unitId}`;
|
|
558
|
-
debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit
|
|
566
|
+
debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasGit });
|
|
559
567
|
ctx.ui.notify(msg, "error");
|
|
560
568
|
await deps.stopAuto(ctx, pi, msg);
|
|
561
569
|
return { action: "break", reason: "worktree-invalid" };
|
|
562
570
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
571
|
+
const hasProjectFile = PROJECT_FILES.some((f) => deps.existsSync(join(s.basePath, f)));
|
|
572
|
+
const hasSrcDir = deps.existsSync(join(s.basePath, "src"));
|
|
573
|
+
if (!hasProjectFile && !hasSrcDir) {
|
|
574
|
+
const msg = `Worktree health check failed: ${s.basePath} has no recognized project files — refusing to dispatch ${unitType} ${unitId}`;
|
|
575
|
+
debugLog("runUnitPhase", { phase: "worktree-health-fail", basePath: s.basePath, hasProjectFile, hasSrcDir });
|
|
566
576
|
ctx.ui.notify(msg, "error");
|
|
567
577
|
await deps.stopAuto(ctx, pi, msg);
|
|
568
578
|
return { action: "break", reason: "worktree-invalid" };
|
|
@@ -574,6 +584,8 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
574
584
|
s.currentUnit.id === unitId);
|
|
575
585
|
const previousTier = s.currentUnitRouting?.tier;
|
|
576
586
|
s.currentUnit = { type: unitType, id: unitId, startedAt: Date.now() };
|
|
587
|
+
const unitStartSeq = ic.nextSeq();
|
|
588
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: unitStartSeq, eventType: "unit-start", data: { unitType, unitId } });
|
|
577
589
|
deps.captureAvailableSkills();
|
|
578
590
|
deps.writeUnitRuntimeRecord(s.basePath, unitType, unitId, s.currentUnit.startedAt, {
|
|
579
591
|
phase: "dispatched",
|
|
@@ -683,7 +695,12 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
683
695
|
unitId,
|
|
684
696
|
prefs,
|
|
685
697
|
buildSnapshotOpts: () => deps.buildSnapshotOpts(unitType, unitId),
|
|
686
|
-
buildRecoveryContext: () => ({
|
|
698
|
+
buildRecoveryContext: () => ({
|
|
699
|
+
basePath: s.basePath,
|
|
700
|
+
verbose: s.verbose,
|
|
701
|
+
currentUnitStartedAt: s.currentUnit?.startedAt ?? Date.now(),
|
|
702
|
+
unitRecoveryCount: s.unitRecoveryCount,
|
|
703
|
+
}),
|
|
687
704
|
pauseAuto: deps.pauseAuto,
|
|
688
705
|
});
|
|
689
706
|
// Write preliminary lock (no session path yet — runUnit creates a new session).
|
|
@@ -783,6 +800,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
|
|
|
783
800
|
s.unitDispatchCount.delete(`${unitType}/${unitId}`);
|
|
784
801
|
s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
|
|
785
802
|
}
|
|
803
|
+
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "unit-end", data: { unitType, unitId, status: unitResult.status, artifactVerified }, causedBy: { flowId: ic.flowId, seq: unitStartSeq } });
|
|
786
804
|
return { action: "next", data: { unitStartedAt: s.currentUnit.startedAt } };
|
|
787
805
|
}
|
|
788
806
|
// ─── runFinalize ──────────────────────────────────────────────────────────────
|
|
@@ -18,6 +18,7 @@ import { GLYPH, INDENT } from "../shared/mod.js";
|
|
|
18
18
|
import { computeProgressScore } from "./progress-score.js";
|
|
19
19
|
import { getActiveWorktreeName } from "./worktree-command.js";
|
|
20
20
|
import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
|
|
21
|
+
import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js";
|
|
21
22
|
// ─── UAT Slice Extraction ─────────────────────────────────────────────────────
|
|
22
23
|
/**
|
|
23
24
|
* Extract the target slice ID from a run-uat unit ID (e.g. "M001/S01" → "S01").
|
|
@@ -370,6 +371,8 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
|
|
|
370
371
|
}
|
|
371
372
|
// Pre-fetch last commit for display
|
|
372
373
|
refreshLastCommit(accessors.getBasePath());
|
|
374
|
+
// Cache the effective service tier at widget creation time (reads preferences)
|
|
375
|
+
const effectiveServiceTier = getEffectiveServiceTier();
|
|
373
376
|
ctx.ui.setWidget("gsd-progress", (tui, theme) => {
|
|
374
377
|
let pulseBright = true;
|
|
375
378
|
let cachedLines;
|
|
@@ -471,9 +474,10 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
|
|
|
471
474
|
// Model display — shown in context section, not stats
|
|
472
475
|
const modelId = cmdCtx?.model?.id ?? "";
|
|
473
476
|
const modelProvider = cmdCtx?.model?.provider ?? "";
|
|
474
|
-
const
|
|
477
|
+
const tierIcon = resolveServiceTierIcon(effectiveServiceTier, modelId);
|
|
478
|
+
const modelDisplay = (modelProvider && modelId
|
|
475
479
|
? `${modelProvider}/${modelId}`
|
|
476
|
-
: modelId;
|
|
480
|
+
: modelId) + (tierIcon ? ` ${tierIcon}` : "");
|
|
477
481
|
// ── Mode: off — return empty ──────────────────────────────────
|
|
478
482
|
if (widgetMode === "off") {
|
|
479
483
|
cachedLines = [];
|
|
@@ -24,7 +24,7 @@ function missingSliceStop(mid, phase) {
|
|
|
24
24
|
// ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
|
|
25
25
|
const MAX_REWRITE_ATTEMPTS = 3;
|
|
26
26
|
// ─── Rules ────────────────────────────────────────────────────────────────
|
|
27
|
-
const DISPATCH_RULES = [
|
|
27
|
+
export const DISPATCH_RULES = [
|
|
28
28
|
{
|
|
29
29
|
name: "rewrite-docs (override gate)",
|
|
30
30
|
match: async ({ mid, midTitle, state, basePath, session }) => {
|
|
@@ -480,22 +480,39 @@ const DISPATCH_RULES = [
|
|
|
480
480
|
},
|
|
481
481
|
},
|
|
482
482
|
];
|
|
483
|
+
import { getRegistry } from "./rule-registry.js";
|
|
483
484
|
// ─── Resolver ─────────────────────────────────────────────────────────────
|
|
484
485
|
/**
|
|
485
486
|
* Evaluate dispatch rules in order. Returns the first matching action,
|
|
486
487
|
* or a "stop" action if no rule matches (unhandled phase).
|
|
488
|
+
*
|
|
489
|
+
* Delegates to the RuleRegistry when initialized; falls back to inline
|
|
490
|
+
* loop over DISPATCH_RULES for backward compatibility (tests that import
|
|
491
|
+
* resolveDispatch directly without registry initialization).
|
|
487
492
|
*/
|
|
488
493
|
export async function resolveDispatch(ctx) {
|
|
494
|
+
// Delegate to registry when available
|
|
495
|
+
try {
|
|
496
|
+
const registry = getRegistry();
|
|
497
|
+
return await registry.evaluateDispatch(ctx);
|
|
498
|
+
}
|
|
499
|
+
catch {
|
|
500
|
+
// Registry not initialized — fall back to inline loop
|
|
501
|
+
}
|
|
489
502
|
for (const rule of DISPATCH_RULES) {
|
|
490
503
|
const result = await rule.match(ctx);
|
|
491
|
-
if (result)
|
|
504
|
+
if (result) {
|
|
505
|
+
if (result.action !== "skip")
|
|
506
|
+
result.matchedRule = rule.name;
|
|
492
507
|
return result;
|
|
508
|
+
}
|
|
493
509
|
}
|
|
494
510
|
// No rule matched — unhandled phase
|
|
495
511
|
return {
|
|
496
512
|
action: "stop",
|
|
497
513
|
reason: `Unhandled phase "${ctx.state.phase}" — run /gsd doctor to diagnose.`,
|
|
498
514
|
level: "info",
|
|
515
|
+
matchedRule: "<no-match>",
|
|
499
516
|
};
|
|
500
517
|
}
|
|
501
518
|
/** Exposed for testing — returns the rule names in evaluation order. */
|
|
@@ -31,6 +31,7 @@ import { existsSync, unlinkSync } from "node:fs";
|
|
|
31
31
|
import { join } from "node:path";
|
|
32
32
|
import { uncheckTaskInPlan } from "./undo.js";
|
|
33
33
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
34
|
+
import { _resetHasChangesCache } from "./native-git-bridge.js";
|
|
34
35
|
/** Throttle STATE.md rebuilds — at most once per 30 seconds */
|
|
35
36
|
const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
36
37
|
/**
|
|
@@ -103,6 +104,12 @@ export async function postUnitPreVerification(pctx, opts) {
|
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
}
|
|
107
|
+
// Invalidate the nativeHasChanges cache before auto-commit (#1853).
|
|
108
|
+
// The cache has a 10-second TTL and is keyed by basePath. A stale
|
|
109
|
+
// `false` result causes autoCommit to skip staging entirely, leaving
|
|
110
|
+
// code files only in the working tree where they are destroyed by
|
|
111
|
+
// `git worktree remove --force` during teardown.
|
|
112
|
+
_resetHasChangesCache();
|
|
106
113
|
const commitMsg = autoCommitCurrentBranch(s.basePath, s.currentUnit.type, s.currentUnit.id, taskContext);
|
|
107
114
|
if (commitMsg) {
|
|
108
115
|
ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
|
|
@@ -263,10 +263,15 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
|
|
|
263
263
|
// plan has no tasks, creating an infinite skip loop (#699).
|
|
264
264
|
if (unitType === "plan-slice") {
|
|
265
265
|
const planContent = readFileSync(absPath, "utf-8");
|
|
266
|
-
|
|
266
|
+
// Accept checkbox-style (- [x] **T01: ...) or heading-style (### T01 -- / ### T01: / ### T01 —)
|
|
267
|
+
const hasCheckboxTask = /^- \[[xX ]\] \*\*T\d+:/m.test(planContent);
|
|
268
|
+
const hasHeadingTask = /^#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
|
|
269
|
+
if (!hasCheckboxTask && !hasHeadingTask)
|
|
267
270
|
return false;
|
|
268
271
|
}
|
|
269
|
-
// execute-task must also have its checkbox marked [x] in the slice plan
|
|
272
|
+
// execute-task must also have its checkbox marked [x] in the slice plan.
|
|
273
|
+
// Heading-style plans (### T01 -- Title) have no checkbox — the task summary
|
|
274
|
+
// file existence (checked above via resolveExpectedArtifactPath) is sufficient.
|
|
270
275
|
if (unitType === "execute-task") {
|
|
271
276
|
const parts = unitId.split("/");
|
|
272
277
|
const mid = parts[0];
|
|
@@ -277,8 +282,11 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
|
|
|
277
282
|
if (planAbs && existsSync(planAbs)) {
|
|
278
283
|
const planContent = readFileSync(planAbs, "utf-8");
|
|
279
284
|
const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
280
|
-
const
|
|
281
|
-
|
|
285
|
+
const cbRe = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
|
|
286
|
+
const hdRe = new RegExp(`^#{2,4}\\s+${escapedTid}\\s*(?:--|—|:)`, "m");
|
|
287
|
+
// Heading-style entries count as verified (no checkbox to toggle);
|
|
288
|
+
// checkbox-style entries require [x].
|
|
289
|
+
if (!cbRe.test(planContent) && !hdRe.test(planContent))
|
|
282
290
|
return false;
|
|
283
291
|
}
|
|
284
292
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { deriveState } from "./state.js";
|
|
12
12
|
import { loadFile, getManifestStatus } from "./files.js";
|
|
13
13
|
import { loadEffectiveGSDPreferences, resolveSkillDiscoveryMode, getIsolationMode, } from "./preferences.js";
|
|
14
|
-
import { ensureGsdSymlink, validateProjectId } from "./repo-identity.js";
|
|
14
|
+
import { ensureGsdSymlink, isInheritedRepo, validateProjectId } from "./repo-identity.js";
|
|
15
15
|
import { migrateToExternalState, recoverFailedMigration } from "./migrate-external.js";
|
|
16
16
|
import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
|
|
17
17
|
import { gsdRoot, resolveMilestoneFile } from "./paths.js";
|
|
@@ -69,8 +69,13 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
69
69
|
ctx.ui.notify(`GSD_PROJECT_ID must contain only alphanumeric characters, hyphens, and underscores. Got: "${customProjectId}"`, "error");
|
|
70
70
|
return releaseLockAndReturn();
|
|
71
71
|
}
|
|
72
|
-
// Ensure git repo exists
|
|
73
|
-
if
|
|
72
|
+
// Ensure git repo exists.
|
|
73
|
+
// Guard against inherited repos: if `base` is a subdirectory of another
|
|
74
|
+
// git repo that has no .gsd (i.e. the parent project was never initialised
|
|
75
|
+
// with GSD), create a fresh git repo at `base` so it gets its own identity
|
|
76
|
+
// hash. Without this, repoIdentity() resolves to the parent repo's hash
|
|
77
|
+
// and loads milestones from an unrelated project (#1639).
|
|
78
|
+
if (!nativeIsRepo(base) || isInheritedRepo(base)) {
|
|
74
79
|
const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main";
|
|
75
80
|
nativeInit(base, mainBranch);
|
|
76
81
|
}
|