gsd-pi 2.17.0 → 2.19.0
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 +39 -0
- package/dist/onboarding.js +2 -2
- package/dist/remote-questions-config.d.ts +10 -0
- package/dist/remote-questions-config.js +36 -0
- package/dist/resources/extensions/gsd/activity-log.ts +37 -7
- package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +65 -16
- package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/dist/resources/extensions/gsd/auto.ts +399 -29
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +382 -23
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +201 -2
- package/dist/resources/extensions/gsd/files.ts +123 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +237 -4
- package/dist/resources/extensions/gsd/index.ts +47 -3
- package/dist/resources/extensions/gsd/metrics.ts +48 -0
- package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/dist/resources/extensions/gsd/model-router.ts +256 -0
- package/dist/resources/extensions/gsd/paths.ts +9 -0
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/dist/resources/extensions/gsd/preferences.ts +132 -1
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/dist/resources/extensions/gsd/prompts/system.md +2 -0
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/dist/resources/extensions/gsd/queue-order.ts +231 -0
- package/dist/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/dist/resources/extensions/gsd/state.ts +15 -3
- package/dist/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +14 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/dist/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/dist/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/dist/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/dist/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +227 -1
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +198 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +255 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
- package/dist/resources/extensions/gsd/visualizer-data.ts +154 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +193 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +293 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/dist/resources/extensions/gsd/worktree.ts +22 -0
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +33 -0
- package/dist/resources/extensions/remote-questions/format.ts +12 -6
- package/dist/resources/extensions/remote-questions/manager.ts +8 -0
- package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/cli/args.d.ts +5 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +21 -0
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts +14 -3
- package/packages/pi-coding-agent/dist/cli/list-models.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/list-models.js +52 -17
- package/packages/pi-coding-agent/dist/cli/list-models.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts +27 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +79 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +140 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js +162 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +100 -0
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +113 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +26 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +98 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts +62 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js +145 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js +118 -0
- package/packages/pi-coding-agent/dist/core/models-json-writer.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +5 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +4 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +17 -2
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +25 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +121 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +32 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/cli/args.ts +21 -0
- package/packages/pi-coding-agent/src/cli/list-models.ts +70 -17
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +170 -0
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +97 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +125 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +231 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +135 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +107 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.test.ts +145 -0
- package/packages/pi-coding-agent/src/core/models-json-writer.ts +188 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +21 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/index.ts +5 -0
- package/packages/pi-coding-agent/src/main.ts +19 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +163 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +37 -0
- package/src/resources/extensions/gsd/activity-log.ts +37 -7
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +65 -16
- package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/src/resources/extensions/gsd/auto.ts +399 -29
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +382 -23
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/src/resources/extensions/gsd/docs/preferences-reference.md +201 -2
- package/src/resources/extensions/gsd/files.ts +123 -1
- package/src/resources/extensions/gsd/guided-flow.ts +237 -4
- package/src/resources/extensions/gsd/index.ts +47 -3
- package/src/resources/extensions/gsd/metrics.ts +48 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/src/resources/extensions/gsd/model-router.ts +256 -0
- package/src/resources/extensions/gsd/paths.ts +9 -0
- package/src/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +132 -1
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/src/resources/extensions/gsd/prompts/system.md +2 -0
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/src/resources/extensions/gsd/queue-order.ts +231 -0
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
- package/src/resources/extensions/gsd/state.ts +15 -3
- package/src/resources/extensions/gsd/templates/knowledge.md +19 -0
- package/src/resources/extensions/gsd/templates/preferences.md +14 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
- package/src/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +227 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +198 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +255 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/src/resources/extensions/gsd/triage-ui.ts +175 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +154 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +193 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +293 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/src/resources/extensions/gsd/worktree.ts +22 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +33 -0
- package/src/resources/extensions/remote-questions/format.ts +12 -6
- package/src/resources/extensions/remote-questions/manager.ts +8 -0
- package/src/resources/extensions/shared/next-action-ui.ts +16 -1
package/README.md
CHANGED
|
@@ -21,6 +21,25 @@ One command. Walk away. Come back to a built project with clean git history.
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
+
## Documentation
|
|
25
|
+
|
|
26
|
+
Full documentation is available in the [`docs/`](./docs/) directory:
|
|
27
|
+
|
|
28
|
+
- **[Getting Started](./docs/getting-started.md)** — install, first run, basic usage
|
|
29
|
+
- **[Auto Mode](./docs/auto-mode.md)** — autonomous execution deep-dive
|
|
30
|
+
- **[Configuration](./docs/configuration.md)** — all preferences, models, git, and hooks
|
|
31
|
+
- **[Token Optimization](./docs/token-optimization.md)** — profiles, context compression, complexity routing (v2.17)
|
|
32
|
+
- **[Cost Management](./docs/cost-management.md)** — budgets, tracking, projections
|
|
33
|
+
- **[Git Strategy](./docs/git-strategy.md)** — worktree isolation, branching, merge behavior
|
|
34
|
+
- **[Working in Teams](./docs/working-in-teams.md)** — unique IDs, shared artifacts
|
|
35
|
+
- **[Skills](./docs/skills.md)** — bundled skills, discovery, custom authoring
|
|
36
|
+
- **[Commands Reference](./docs/commands.md)** — all commands and keyboard shortcuts
|
|
37
|
+
- **[Architecture](./docs/architecture.md)** — system design and dispatch pipeline
|
|
38
|
+
- **[Troubleshooting](./docs/troubleshooting.md)** — common issues, doctor, recovery
|
|
39
|
+
- **[Migration from v1](./docs/migration.md)** — `.planning` → `.gsd` migration
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
24
43
|
## What Changed From v1
|
|
25
44
|
|
|
26
45
|
The original GSD was a collection of markdown prompts installed into `~/.claude/commands/`. It relied entirely on the LLM reading those prompts and doing the right thing. That worked surprisingly well — but it had hard limits:
|
|
@@ -334,6 +353,26 @@ unique_milestone_ids: true
|
|
|
334
353
|
| `skill_rules` | Situational rules for skill routing |
|
|
335
354
|
| `unique_milestone_ids` | Uses unique milestone names to avoid clashes when working in teams of people |
|
|
336
355
|
|
|
356
|
+
### Token Optimization (v2.17)
|
|
357
|
+
|
|
358
|
+
GSD 2.17 introduced a coordinated token optimization system that reduces usage by 40-60% on cost-sensitive workloads. Set a single preference to coordinate model selection, phase skipping, and context compression:
|
|
359
|
+
|
|
360
|
+
```yaml
|
|
361
|
+
token_profile: budget # or balanced (default), quality
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
| Profile | Savings | What It Does |
|
|
365
|
+
|---------|---------|-------------|
|
|
366
|
+
| `budget` | 40-60% | Cheap models, skip research/reassess, minimal context inlining |
|
|
367
|
+
| `balanced` | 10-20% | Default models, skip slice research, standard context |
|
|
368
|
+
| `quality` | 0% | All phases, all context, full model power |
|
|
369
|
+
|
|
370
|
+
**Complexity-based routing** automatically classifies tasks as simple/standard/complex and routes to appropriate models. Simple docs tasks get Haiku; complex architectural work gets Opus. The classification is heuristic (sub-millisecond, no LLM calls) and learns from outcomes via a persistent routing history.
|
|
371
|
+
|
|
372
|
+
**Budget pressure** graduates model downgrading as you approach your budget ceiling — 50%, 75%, and 90% thresholds progressively shift work to cheaper tiers.
|
|
373
|
+
|
|
374
|
+
See the full [Token Optimization Guide](./docs/token-optimization.md) for details.
|
|
375
|
+
|
|
337
376
|
### Bundled Tools
|
|
338
377
|
|
|
339
378
|
GSD ships with 14 extensions, all loaded automatically:
|
package/dist/onboarding.js
CHANGED
|
@@ -633,7 +633,7 @@ async function runRemoteQuestionsStep(p, pc, authStorage) {
|
|
|
633
633
|
});
|
|
634
634
|
if (p.isCancel(channelId) || !channelId)
|
|
635
635
|
return null;
|
|
636
|
-
const { saveRemoteQuestionsConfig } = await import('./
|
|
636
|
+
const { saveRemoteQuestionsConfig } = await import('./remote-questions-config.js');
|
|
637
637
|
saveRemoteQuestionsConfig('slack', channelId.trim());
|
|
638
638
|
p.log.success(`Slack channel: ${pc.green(channelId.trim())}`);
|
|
639
639
|
return 'Slack';
|
|
@@ -736,7 +736,7 @@ async function runDiscordChannelStep(p, pc, token) {
|
|
|
736
736
|
channelId = channelChoice;
|
|
737
737
|
}
|
|
738
738
|
// Save remote questions config
|
|
739
|
-
const { saveRemoteQuestionsConfig } = await import('./
|
|
739
|
+
const { saveRemoteQuestionsConfig } = await import('./remote-questions-config.js');
|
|
740
740
|
saveRemoteQuestionsConfig('discord', channelId);
|
|
741
741
|
const channelName = channels.find(ch => ch.id === channelId)?.name;
|
|
742
742
|
p.log.success(`Discord channel: ${pc.green(channelName ? `#${channelName}` : channelId)}`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Questions Config Helper
|
|
3
|
+
*
|
|
4
|
+
* Extracted from remote-questions extension so onboarding.ts can import
|
|
5
|
+
* it without crossing the compiled/uncompiled boundary. The extension
|
|
6
|
+
* files in src/resources/ are shipped as raw .ts and loaded via jiti,
|
|
7
|
+
* but onboarding.ts is compiled by tsc — dynamic imports from compiled
|
|
8
|
+
* JS to uncompiled .ts fail at runtime (#592).
|
|
9
|
+
*/
|
|
10
|
+
export declare function saveRemoteQuestionsConfig(channel: "slack" | "discord", channelId: string): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Questions Config Helper
|
|
3
|
+
*
|
|
4
|
+
* Extracted from remote-questions extension so onboarding.ts can import
|
|
5
|
+
* it without crossing the compiled/uncompiled boundary. The extension
|
|
6
|
+
* files in src/resources/ are shipped as raw .ts and loaded via jiti,
|
|
7
|
+
* but onboarding.ts is compiled by tsc — dynamic imports from compiled
|
|
8
|
+
* JS to uncompiled .ts fail at runtime (#592).
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { getGlobalGSDPreferencesPath } from "./resources/extensions/gsd/preferences.js";
|
|
13
|
+
export function saveRemoteQuestionsConfig(channel, channelId) {
|
|
14
|
+
const prefsPath = getGlobalGSDPreferencesPath();
|
|
15
|
+
const block = [
|
|
16
|
+
"remote_questions:",
|
|
17
|
+
` channel: ${channel}`,
|
|
18
|
+
` channel_id: "${channelId}"`,
|
|
19
|
+
" timeout_minutes: 5",
|
|
20
|
+
" poll_interval_seconds: 5",
|
|
21
|
+
].join("\n");
|
|
22
|
+
const content = existsSync(prefsPath) ? readFileSync(prefsPath, "utf-8") : "";
|
|
23
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
24
|
+
let next = content;
|
|
25
|
+
if (fmMatch) {
|
|
26
|
+
let frontmatter = fmMatch[1];
|
|
27
|
+
const regex = /remote_questions:[\s\S]*?(?=\n[a-zA-Z_]|\n---|$)/;
|
|
28
|
+
frontmatter = regex.test(frontmatter) ? frontmatter.replace(regex, block) : `${frontmatter.trimEnd()}\n${block}`;
|
|
29
|
+
next = `---\n${frontmatter}\n---${content.slice(fmMatch[0].length)}`;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
next = `---\n${block}\n---\n\n${content}`;
|
|
33
|
+
}
|
|
34
|
+
mkdirSync(dirname(prefsPath), { recursive: true });
|
|
35
|
+
writeFileSync(prefsPath, next, "utf-8");
|
|
36
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Diagnostic extraction is handled by session-forensics.ts.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { writeFileSync, mkdirSync, readdirSync, unlinkSync, statSync, openSync, closeSync, constants } from "node:fs";
|
|
11
|
+
import { writeFileSync, writeSync, mkdirSync, readdirSync, unlinkSync, statSync, openSync, closeSync, constants } from "node:fs";
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
|
|
@@ -23,6 +23,15 @@ interface ActivityLogState {
|
|
|
23
23
|
|
|
24
24
|
const activityLogState = new Map<string, ActivityLogState>();
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Clear accumulated activity log state (#611).
|
|
28
|
+
* Call when auto-mode stops to prevent unbounded memory growth
|
|
29
|
+
* from lastSnapshotKeyByUnit maps accumulating across units.
|
|
30
|
+
*/
|
|
31
|
+
export function clearActivityLogState(): void {
|
|
32
|
+
activityLogState.clear();
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
function scanNextSequence(activityDir: string): number {
|
|
27
36
|
let maxSeq = 0;
|
|
28
37
|
try {
|
|
@@ -46,9 +55,21 @@ function getActivityState(activityDir: string): ActivityLogState {
|
|
|
46
55
|
return state;
|
|
47
56
|
}
|
|
48
57
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Build a lightweight dedup key from session entries without serializing
|
|
60
|
+
* the entire content to a string (#611). Uses entry count + hash of
|
|
61
|
+
* the last few entries as a fingerprint instead of hashing megabytes.
|
|
62
|
+
*/
|
|
63
|
+
function snapshotKey(unitType: string, unitId: string, entries: unknown[]): string {
|
|
64
|
+
const hash = createHash("sha1");
|
|
65
|
+
hash.update(`${unitType}\0${unitId}\0${entries.length}\0`);
|
|
66
|
+
// Hash only the last 3 entries as a fingerprint — if the session grew,
|
|
67
|
+
// the count change alone detects it; if content changed, the tail hash catches it.
|
|
68
|
+
const tail = entries.slice(-3);
|
|
69
|
+
for (const entry of tail) {
|
|
70
|
+
hash.update(JSON.stringify(entry));
|
|
71
|
+
}
|
|
72
|
+
return hash.digest("hex");
|
|
52
73
|
}
|
|
53
74
|
|
|
54
75
|
function nextActivityFilePath(
|
|
@@ -91,14 +112,23 @@ export function saveActivityLog(
|
|
|
91
112
|
mkdirSync(activityDir, { recursive: true });
|
|
92
113
|
|
|
93
114
|
const safeUnitId = unitId.replace(/\//g, "-");
|
|
94
|
-
const content = `${entries.map(entry => JSON.stringify(entry)).join("\n")}\n`;
|
|
95
115
|
const state = getActivityState(activityDir);
|
|
96
116
|
const unitKey = `${unitType}\0${safeUnitId}`;
|
|
97
|
-
|
|
117
|
+
// Use lightweight fingerprint instead of serializing all entries (#611)
|
|
118
|
+
const key = snapshotKey(unitType, safeUnitId, entries);
|
|
98
119
|
if (state.lastSnapshotKeyByUnit.get(unitKey) === key) return;
|
|
99
120
|
|
|
100
121
|
const filePath = nextActivityFilePath(activityDir, state, unitType, safeUnitId);
|
|
101
|
-
|
|
122
|
+
// Stream entries to disk line-by-line instead of building one massive string (#611).
|
|
123
|
+
// For large sessions, the single-string approach allocated hundreds of MB.
|
|
124
|
+
const fd = openSync(filePath, "w");
|
|
125
|
+
try {
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
writeSync(fd, JSON.stringify(entry) + "\n");
|
|
128
|
+
}
|
|
129
|
+
} finally {
|
|
130
|
+
closeSync(fd);
|
|
131
|
+
}
|
|
102
132
|
state.nextSeq += 1;
|
|
103
133
|
state.lastSnapshotKeyByUnit.set(unitKey, key);
|
|
104
134
|
} catch (e) {
|
|
@@ -10,7 +10,7 @@ import type { ExtensionContext, ExtensionCommandContext } from "@gsd/pi-coding-a
|
|
|
10
10
|
import type { GSDState } from "./types.js";
|
|
11
11
|
import { getCurrentBranch } from "./worktree.js";
|
|
12
12
|
import { getActiveHook } from "./post-unit-hooks.js";
|
|
13
|
-
import { getLedger, getProjectTotals, formatCost, formatTokenCount } from "./metrics.js";
|
|
13
|
+
import { getLedger, getProjectTotals, formatCost, formatTokenCount, formatTierSavings } from "./metrics.js";
|
|
14
14
|
import {
|
|
15
15
|
resolveMilestoneFile,
|
|
16
16
|
resolveSliceFile,
|
|
@@ -39,6 +39,8 @@ export interface AutoDashboardData {
|
|
|
39
39
|
projectedRemainingCost?: number;
|
|
40
40
|
/** Whether token profile has been auto-downgraded due to budget prediction */
|
|
41
41
|
profileDowngraded?: boolean;
|
|
42
|
+
/** Number of pending captures awaiting triage (0 if none or file missing) */
|
|
43
|
+
pendingCaptureCount: number;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
// ─── Unit Description Helpers ─────────────────────────────────────────────────
|
|
@@ -239,6 +241,7 @@ export function updateProgressWidget(
|
|
|
239
241
|
unitId: string,
|
|
240
242
|
state: GSDState,
|
|
241
243
|
accessors: WidgetStateAccessors,
|
|
244
|
+
tierBadge?: string,
|
|
242
245
|
): void {
|
|
243
246
|
if (!ctx.hasUI) return;
|
|
244
247
|
|
|
@@ -319,7 +322,8 @@ export function updateProgressWidget(
|
|
|
319
322
|
|
|
320
323
|
const target = task ? `${task.id}: ${task.title}` : unitId;
|
|
321
324
|
const actionLeft = `${pad}${theme.fg("accent", "▸")} ${theme.fg("accent", verb)} ${theme.fg("text", target)}`;
|
|
322
|
-
const
|
|
325
|
+
const tierTag = tierBadge ? theme.fg("dim", `[${tierBadge}] `) : "";
|
|
326
|
+
const phaseBadge = `${tierTag}${theme.fg("dim", phaseLabel)}`;
|
|
323
327
|
lines.push(rightAlign(actionLeft, phaseBadge, width));
|
|
324
328
|
lines.push("");
|
|
325
329
|
|
|
@@ -414,6 +418,14 @@ export function updateProgressWidget(
|
|
|
414
418
|
? `${modelPhase}${theme.fg("dim", modelDisplay)}`
|
|
415
419
|
: "";
|
|
416
420
|
lines.push(rightAlign(`${pad}${sLeft}`, sRight, width));
|
|
421
|
+
|
|
422
|
+
// Dynamic routing savings summary
|
|
423
|
+
if (mLedger && mLedger.units.some(u => u.tier)) {
|
|
424
|
+
const savings = formatTierSavings(mLedger.units);
|
|
425
|
+
if (savings) {
|
|
426
|
+
lines.push(truncateToWidth(theme.fg("dim", `${pad}${savings}`), width));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
417
429
|
}
|
|
418
430
|
|
|
419
431
|
const hintParts: string[] = [];
|
|
@@ -89,7 +89,7 @@ export async function inlineDependencySummaries(
|
|
|
89
89
|
export async function inlineGsdRootFile(
|
|
90
90
|
base: string, filename: string, label: string,
|
|
91
91
|
): Promise<string | null> {
|
|
92
|
-
const key = filename.replace(/\.md$/i, "").toUpperCase() as "PROJECT" | "DECISIONS" | "QUEUE" | "STATE" | "REQUIREMENTS";
|
|
92
|
+
const key = filename.replace(/\.md$/i, "").toUpperCase() as "PROJECT" | "DECISIONS" | "QUEUE" | "STATE" | "REQUIREMENTS" | "KNOWLEDGE";
|
|
93
93
|
const absPath = resolveGsdRootFile(base, key);
|
|
94
94
|
if (!existsSync(absPath)) return null;
|
|
95
95
|
return inlineFileOptional(absPath, relGsdRootFile(key), label);
|
|
@@ -377,6 +377,8 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
377
377
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
378
378
|
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
379
379
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
380
|
+
const knowledgeInlineRM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
381
|
+
if (knowledgeInlineRM) inlined.push(knowledgeInlineRM);
|
|
380
382
|
inlined.push(inlineTemplate("research", "Research"));
|
|
381
383
|
|
|
382
384
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
@@ -387,7 +389,7 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
387
389
|
milestoneId: mid, milestoneTitle: midTitle,
|
|
388
390
|
milestonePath: relMilestonePath(base, mid),
|
|
389
391
|
contextPath: contextRel,
|
|
390
|
-
outputPath: outputRelPath,
|
|
392
|
+
outputPath: join(base, outputRelPath),
|
|
391
393
|
inlinedContext,
|
|
392
394
|
...buildSkillDiscoveryVars(),
|
|
393
395
|
});
|
|
@@ -413,6 +415,8 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
413
415
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
414
416
|
const decisionsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "decisions.md", "Decisions") : null;
|
|
415
417
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
418
|
+
const knowledgeInlinePM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
419
|
+
if (knowledgeInlinePM) inlined.push(knowledgeInlinePM);
|
|
416
420
|
inlined.push(inlineTemplate("roadmap", "Roadmap"));
|
|
417
421
|
if (inlineLevel === "full") {
|
|
418
422
|
inlined.push(inlineTemplate("decisions", "Decisions"));
|
|
@@ -428,14 +432,14 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
428
432
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
429
433
|
|
|
430
434
|
const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
|
|
431
|
-
const secretsOutputPath = relMilestoneFile(base, mid, "SECRETS");
|
|
435
|
+
const secretsOutputPath = join(base, relMilestoneFile(base, mid, "SECRETS"));
|
|
432
436
|
return loadPrompt("plan-milestone", {
|
|
433
437
|
workingDirectory: base,
|
|
434
438
|
milestoneId: mid, milestoneTitle: midTitle,
|
|
435
439
|
milestonePath: relMilestonePath(base, mid),
|
|
436
440
|
contextPath: contextRel,
|
|
437
441
|
researchPath: researchRel,
|
|
438
|
-
outputPath: outputRelPath,
|
|
442
|
+
outputPath: join(base, outputRelPath),
|
|
439
443
|
secretsOutputPath,
|
|
440
444
|
inlinedContext,
|
|
441
445
|
});
|
|
@@ -461,6 +465,8 @@ export async function buildResearchSlicePrompt(
|
|
|
461
465
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
462
466
|
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
463
467
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
468
|
+
const knowledgeInlineRS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
469
|
+
if (knowledgeInlineRS) inlined.push(knowledgeInlineRS);
|
|
464
470
|
inlined.push(inlineTemplate("research", "Research"));
|
|
465
471
|
|
|
466
472
|
const depContent = await inlineDependencySummaries(mid, sid, base);
|
|
@@ -478,7 +484,7 @@ export async function buildResearchSlicePrompt(
|
|
|
478
484
|
roadmapPath: roadmapRel,
|
|
479
485
|
contextPath: contextRel,
|
|
480
486
|
milestoneResearchPath: milestoneResearchRel,
|
|
481
|
-
outputPath: outputRelPath,
|
|
487
|
+
outputPath: join(base, outputRelPath),
|
|
482
488
|
inlinedContext,
|
|
483
489
|
dependencySummaries: depContent,
|
|
484
490
|
...buildSkillDiscoveryVars(),
|
|
@@ -504,6 +510,8 @@ export async function buildPlanSlicePrompt(
|
|
|
504
510
|
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
505
511
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
506
512
|
}
|
|
513
|
+
const knowledgeInlinePS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
514
|
+
if (knowledgeInlinePS) inlined.push(knowledgeInlinePS);
|
|
507
515
|
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
508
516
|
if (inlineLevel === "full") {
|
|
509
517
|
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
@@ -523,7 +531,7 @@ export async function buildPlanSlicePrompt(
|
|
|
523
531
|
slicePath: relSlicePath(base, mid, sid),
|
|
524
532
|
roadmapPath: roadmapRel,
|
|
525
533
|
researchPath: researchRel,
|
|
526
|
-
outputPath: outputRelPath,
|
|
534
|
+
outputPath: join(base, outputRelPath),
|
|
527
535
|
inlinedContext,
|
|
528
536
|
dependencySummaries: depContent,
|
|
529
537
|
});
|
|
@@ -578,14 +586,19 @@ export async function buildExecuteTaskPrompt(
|
|
|
578
586
|
? priorSummaries.slice(-1)
|
|
579
587
|
: priorSummaries;
|
|
580
588
|
const carryForwardSection = await buildCarryForwardSection(effectivePriorSummaries, base);
|
|
589
|
+
|
|
590
|
+
// Inline project knowledge if available
|
|
591
|
+
const knowledgeInlineET = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
592
|
+
|
|
581
593
|
const inlinedTemplates = inlineLevel === "minimal"
|
|
582
594
|
? inlineTemplate("task-summary", "Task Summary")
|
|
583
595
|
: [
|
|
584
596
|
inlineTemplate("task-summary", "Task Summary"),
|
|
585
597
|
inlineTemplate("decisions", "Decisions"),
|
|
598
|
+
...(knowledgeInlineET ? [knowledgeInlineET] : []),
|
|
586
599
|
].join("\n\n---\n\n");
|
|
587
600
|
|
|
588
|
-
const taskSummaryPath = `${relSlicePath(base, mid, sid)}/tasks/${tid}-SUMMARY.md
|
|
601
|
+
const taskSummaryPath = join(base, `${relSlicePath(base, mid, sid)}/tasks/${tid}-SUMMARY.md`);
|
|
589
602
|
|
|
590
603
|
const activeOverrides = await loadActiveOverrides(base);
|
|
591
604
|
const overridesSection = formatOverridesSection(activeOverrides);
|
|
@@ -594,7 +607,7 @@ export async function buildExecuteTaskPrompt(
|
|
|
594
607
|
overridesSection,
|
|
595
608
|
workingDirectory: base,
|
|
596
609
|
milestoneId: mid, sliceId: sid, sliceTitle: sTitle, taskId: tid, taskTitle: tTitle,
|
|
597
|
-
planPath: relSliceFile(base, mid, sid, "PLAN"),
|
|
610
|
+
planPath: join(base, relSliceFile(base, mid, sid, "PLAN")),
|
|
598
611
|
slicePath: relSlicePath(base, mid, sid),
|
|
599
612
|
taskPlanPath: taskPlanRelPath,
|
|
600
613
|
taskPlanInline,
|
|
@@ -624,6 +637,8 @@ export async function buildCompleteSlicePrompt(
|
|
|
624
637
|
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
625
638
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
626
639
|
}
|
|
640
|
+
const knowledgeInlineCS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
641
|
+
if (knowledgeInlineCS) inlined.push(knowledgeInlineCS);
|
|
627
642
|
|
|
628
643
|
// Inline all task summaries for this slice
|
|
629
644
|
const tDir = resolveTasksDir(base, mid, sid);
|
|
@@ -650,14 +665,14 @@ export async function buildCompleteSlicePrompt(
|
|
|
650
665
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
651
666
|
|
|
652
667
|
const sliceRel = relSlicePath(base, mid, sid);
|
|
653
|
-
const sliceSummaryPath = `${sliceRel}/${sid}-SUMMARY.md
|
|
654
|
-
const sliceUatPath = `${sliceRel}/${sid}-UAT.md
|
|
668
|
+
const sliceSummaryPath = join(base, `${sliceRel}/${sid}-SUMMARY.md`);
|
|
669
|
+
const sliceUatPath = join(base, `${sliceRel}/${sid}-UAT.md`);
|
|
655
670
|
|
|
656
671
|
return loadPrompt("complete-slice", {
|
|
657
672
|
workingDirectory: base,
|
|
658
673
|
milestoneId: mid, sliceId: sid, sliceTitle: sTitle,
|
|
659
674
|
slicePath: sliceRel,
|
|
660
|
-
roadmapPath: roadmapRel,
|
|
675
|
+
roadmapPath: join(base, roadmapRel),
|
|
661
676
|
inlinedContext,
|
|
662
677
|
sliceSummaryPath,
|
|
663
678
|
sliceUatPath,
|
|
@@ -697,6 +712,8 @@ export async function buildCompleteMilestonePrompt(
|
|
|
697
712
|
const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
|
|
698
713
|
if (projectInline) inlined.push(projectInline);
|
|
699
714
|
}
|
|
715
|
+
const knowledgeInlineCM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
716
|
+
if (knowledgeInlineCM) inlined.push(knowledgeInlineCM);
|
|
700
717
|
// Inline milestone context file (milestone-level, not GSD root)
|
|
701
718
|
const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
|
|
702
719
|
const contextRel = relMilestoneFile(base, mid, "CONTEXT");
|
|
@@ -706,7 +723,7 @@ export async function buildCompleteMilestonePrompt(
|
|
|
706
723
|
|
|
707
724
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
708
725
|
|
|
709
|
-
const milestoneSummaryPath = `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md
|
|
726
|
+
const milestoneSummaryPath = join(base, `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md`);
|
|
710
727
|
|
|
711
728
|
return loadPrompt("complete-milestone", {
|
|
712
729
|
workingDirectory: base,
|
|
@@ -758,7 +775,21 @@ export async function buildReplanSlicePrompt(
|
|
|
758
775
|
|
|
759
776
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
760
777
|
|
|
761
|
-
const replanPath = `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md
|
|
778
|
+
const replanPath = join(base, `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md`);
|
|
779
|
+
|
|
780
|
+
// Build capture context for replan prompt (captures that triggered this replan)
|
|
781
|
+
let captureContext = "(none)";
|
|
782
|
+
try {
|
|
783
|
+
const { loadReplanCaptures } = await import("./triage-resolution.js");
|
|
784
|
+
const replanCaptures = loadReplanCaptures(base);
|
|
785
|
+
if (replanCaptures.length > 0) {
|
|
786
|
+
captureContext = replanCaptures.map(c =>
|
|
787
|
+
`- **${c.id}**: "${c.text}" — ${c.rationale ?? "no rationale"}`
|
|
788
|
+
).join("\n");
|
|
789
|
+
}
|
|
790
|
+
} catch {
|
|
791
|
+
// Non-fatal — captures module may not be available
|
|
792
|
+
}
|
|
762
793
|
|
|
763
794
|
return loadPrompt("replan-slice", {
|
|
764
795
|
workingDirectory: base,
|
|
@@ -766,10 +797,11 @@ export async function buildReplanSlicePrompt(
|
|
|
766
797
|
sliceId: sid,
|
|
767
798
|
sliceTitle: sTitle,
|
|
768
799
|
slicePath: relSlicePath(base, mid, sid),
|
|
769
|
-
planPath: slicePlanRel,
|
|
800
|
+
planPath: join(base, slicePlanRel),
|
|
770
801
|
blockerTaskId,
|
|
771
802
|
inlinedContext,
|
|
772
803
|
replanPath,
|
|
804
|
+
captureContext,
|
|
773
805
|
});
|
|
774
806
|
}
|
|
775
807
|
|
|
@@ -791,7 +823,7 @@ export async function buildRunUatPrompt(
|
|
|
791
823
|
|
|
792
824
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
793
825
|
|
|
794
|
-
const uatResultPath = relSliceFile(base, mid, sliceId, "UAT-RESULT");
|
|
826
|
+
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "UAT-RESULT"));
|
|
795
827
|
const uatType = extractUatType(uatContent) ?? "human-experience";
|
|
796
828
|
|
|
797
829
|
return loadPrompt("run-uat", {
|
|
@@ -825,10 +857,26 @@ export async function buildReassessRoadmapPrompt(
|
|
|
825
857
|
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
826
858
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
827
859
|
}
|
|
860
|
+
const knowledgeInlineRA = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
861
|
+
if (knowledgeInlineRA) inlined.push(knowledgeInlineRA);
|
|
828
862
|
|
|
829
863
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
830
864
|
|
|
831
|
-
const assessmentPath = relSliceFile(base, mid, completedSliceId, "ASSESSMENT");
|
|
865
|
+
const assessmentPath = join(base, relSliceFile(base, mid, completedSliceId, "ASSESSMENT"));
|
|
866
|
+
|
|
867
|
+
// Build deferred captures context for reassess prompt
|
|
868
|
+
let deferredCaptures = "(none)";
|
|
869
|
+
try {
|
|
870
|
+
const { loadDeferredCaptures } = await import("./triage-resolution.js");
|
|
871
|
+
const deferred = loadDeferredCaptures(base);
|
|
872
|
+
if (deferred.length > 0) {
|
|
873
|
+
deferredCaptures = deferred.map(c =>
|
|
874
|
+
`- **${c.id}**: "${c.text}" — ${c.rationale ?? "deferred during triage"}`
|
|
875
|
+
).join("\n");
|
|
876
|
+
}
|
|
877
|
+
} catch {
|
|
878
|
+
// Non-fatal — captures module may not be available
|
|
879
|
+
}
|
|
832
880
|
|
|
833
881
|
return loadPrompt("reassess-roadmap", {
|
|
834
882
|
workingDirectory: base,
|
|
@@ -839,6 +887,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
839
887
|
completedSliceSummaryPath: summaryRel,
|
|
840
888
|
assessmentPath,
|
|
841
889
|
inlinedContext,
|
|
890
|
+
deferredCaptures,
|
|
842
891
|
});
|
|
843
892
|
}
|
|
844
893
|
|
|
@@ -14,8 +14,10 @@ import {
|
|
|
14
14
|
removeWorktree,
|
|
15
15
|
worktreePath,
|
|
16
16
|
} from "./worktree-manager.js";
|
|
17
|
+
import { detectWorktreeName } from "./worktree.js";
|
|
17
18
|
import {
|
|
18
19
|
MergeConflictError,
|
|
20
|
+
readIntegrationBranch,
|
|
19
21
|
} from "./git-service.js";
|
|
20
22
|
import { parseRoadmap } from "./files.js";
|
|
21
23
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
@@ -90,7 +92,12 @@ export function autoWorktreeBranch(milestoneId: string): string {
|
|
|
90
92
|
*/
|
|
91
93
|
export function createAutoWorktree(basePath: string, milestoneId: string): string {
|
|
92
94
|
const branch = autoWorktreeBranch(milestoneId);
|
|
93
|
-
|
|
95
|
+
|
|
96
|
+
// Use the integration branch recorded in META.json as the start point.
|
|
97
|
+
// This ensures the worktree branch is created from the branch the user
|
|
98
|
+
// was on when they started the milestone (e.g. f-setup-gsd-2), not main.
|
|
99
|
+
const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
|
|
100
|
+
const info = createWorktree(basePath, milestoneId, { branch, startPoint: integrationBranch });
|
|
94
101
|
|
|
95
102
|
// Copy .gsd/ planning artifacts from the source repo into the new worktree.
|
|
96
103
|
// Worktrees are fresh git checkouts — untracked files don't carry over.
|
|
@@ -224,6 +231,27 @@ export function getAutoWorktreeOriginalBase(): string | null {
|
|
|
224
231
|
return originalBase;
|
|
225
232
|
}
|
|
226
233
|
|
|
234
|
+
export function getActiveAutoWorktreeContext(): {
|
|
235
|
+
originalBase: string;
|
|
236
|
+
worktreeName: string;
|
|
237
|
+
branch: string;
|
|
238
|
+
} | null {
|
|
239
|
+
if (!originalBase) return null;
|
|
240
|
+
const cwd = process.cwd();
|
|
241
|
+
const resolvedBase = existsSync(originalBase) ? realpathSync(originalBase) : originalBase;
|
|
242
|
+
const wtDir = join(resolvedBase, ".gsd", "worktrees");
|
|
243
|
+
if (!cwd.startsWith(wtDir)) return null;
|
|
244
|
+
const worktreeName = detectWorktreeName(cwd);
|
|
245
|
+
if (!worktreeName) return null;
|
|
246
|
+
const branch = nativeGetCurrentBranch(cwd);
|
|
247
|
+
if (!branch.startsWith("milestone/")) return null;
|
|
248
|
+
return {
|
|
249
|
+
originalBase,
|
|
250
|
+
worktreeName,
|
|
251
|
+
branch,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
227
255
|
// ─── Merge Milestone -> Main ───────────────────────────────────────────────
|
|
228
256
|
|
|
229
257
|
/**
|
|
@@ -279,11 +307,12 @@ export function mergeMilestoneToMain(
|
|
|
279
307
|
const previousCwd = process.cwd();
|
|
280
308
|
process.chdir(originalBasePath_);
|
|
281
309
|
|
|
282
|
-
// 4. Resolve
|
|
310
|
+
// 4. Resolve integration branch — prefer milestone metadata, fall back to preferences / "main"
|
|
283
311
|
const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
|
|
284
|
-
const
|
|
312
|
+
const integrationBranch = readIntegrationBranch(originalBasePath_, milestoneId);
|
|
313
|
+
const mainBranch = integrationBranch ?? prefs.main_branch ?? "main";
|
|
285
314
|
|
|
286
|
-
// 5. Checkout
|
|
315
|
+
// 5. Checkout integration branch
|
|
287
316
|
nativeCheckoutBranch(originalBasePath_, mainBranch);
|
|
288
317
|
|
|
289
318
|
// 6. Build rich commit message
|