gsd-pi 2.16.0 → 2.18.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 +4 -0
- package/dist/resources/extensions/gsd/auto-dispatch.ts +9 -3
- package/dist/resources/extensions/gsd/auto-prompts.ts +91 -42
- package/dist/resources/extensions/gsd/auto-recovery.ts +7 -2
- package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/dist/resources/extensions/gsd/auto.ts +177 -25
- package/dist/resources/extensions/gsd/commands.ts +264 -23
- package/dist/resources/extensions/gsd/complexity.ts +236 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +202 -2
- package/dist/resources/extensions/gsd/files.ts +129 -3
- package/dist/resources/extensions/gsd/git-service.ts +19 -8
- package/dist/resources/extensions/gsd/gitignore.ts +41 -2
- package/dist/resources/extensions/gsd/guided-flow.ts +247 -10
- package/dist/resources/extensions/gsd/index.ts +47 -3
- package/dist/resources/extensions/gsd/metrics.ts +44 -0
- package/dist/resources/extensions/gsd/native-git-bridge.ts +5 -0
- package/dist/resources/extensions/gsd/native-parser-bridge.ts +5 -0
- package/dist/resources/extensions/gsd/paths.ts +9 -0
- package/dist/resources/extensions/gsd/preferences.ts +181 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/dist/resources/extensions/gsd/prompts/system.md +2 -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/routing-history.ts +290 -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-recovery.test.ts +50 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/dist/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
- package/dist/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +132 -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/preferences-git.test.ts +28 -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/routing-history.test.ts +87 -0
- package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
- package/dist/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
- package/dist/resources/extensions/gsd/types.ts +28 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/dist/resources/extensions/gsd/worktree.ts +24 -2
- package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +493 -13
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +422 -62
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.d.ts +12 -0
- package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.js +9 -22
- package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-shared.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/google-shared.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/google-shared.test.js +125 -0
- package/packages/pi-ai/dist/providers/google-shared.test.js.map +1 -0
- package/packages/pi-ai/src/models.generated.ts +422 -62
- package/packages/pi-ai/src/providers/google-shared.test.ts +137 -0
- package/packages/pi-ai/src/providers/google-shared.ts +10 -19
- 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/core/tools/edit-diff.d.ts +7 -7
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +209 -13
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -0
- 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/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.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/core/tools/edit-diff.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +245 -17
- 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/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +13 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +10 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/src/resources/extensions/gsd/activity-log.ts +37 -7
- package/src/resources/extensions/gsd/auto-dashboard.ts +4 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +9 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +91 -42
- package/src/resources/extensions/gsd/auto-recovery.ts +7 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
- package/src/resources/extensions/gsd/auto.ts +177 -25
- package/src/resources/extensions/gsd/commands.ts +264 -23
- package/src/resources/extensions/gsd/complexity.ts +236 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
- package/src/resources/extensions/gsd/docs/preferences-reference.md +202 -2
- package/src/resources/extensions/gsd/files.ts +129 -3
- package/src/resources/extensions/gsd/git-service.ts +19 -8
- package/src/resources/extensions/gsd/gitignore.ts +41 -2
- package/src/resources/extensions/gsd/guided-flow.ts +247 -10
- package/src/resources/extensions/gsd/index.ts +47 -3
- package/src/resources/extensions/gsd/metrics.ts +44 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +5 -0
- package/src/resources/extensions/gsd/native-parser-bridge.ts +5 -0
- package/src/resources/extensions/gsd/paths.ts +9 -0
- package/src/resources/extensions/gsd/preferences.ts +181 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
- package/src/resources/extensions/gsd/prompts/system.md +2 -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/routing-history.ts +290 -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-recovery.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
- package/src/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +132 -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/preferences-git.test.ts +28 -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/routing-history.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
- package/src/resources/extensions/gsd/types.ts +28 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +8 -5
- package/src/resources/extensions/gsd/worktree.ts +24 -2
- 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) {
|
|
@@ -35,6 +35,10 @@ export interface AutoDashboardData {
|
|
|
35
35
|
/** Running cost and token totals from metrics ledger */
|
|
36
36
|
totalCost: number;
|
|
37
37
|
totalTokens: number;
|
|
38
|
+
/** Projected remaining cost based on unit-type averages (undefined if insufficient data) */
|
|
39
|
+
projectedRemainingCost?: number;
|
|
40
|
+
/** Whether token profile has been auto-downgraded due to budget prediction */
|
|
41
|
+
profileDowngraded?: boolean;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
// ─── Unit Description Helpers ─────────────────────────────────────────────────
|
|
@@ -122,7 +122,9 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
122
122
|
},
|
|
123
123
|
{
|
|
124
124
|
name: "reassess-roadmap (post-completion)",
|
|
125
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
125
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
126
|
+
// Phase skip: skip reassess when preference or profile says so
|
|
127
|
+
if (prefs?.phases?.skip_reassess) return null;
|
|
126
128
|
const needsReassess = await checkNeedsReassessment(basePath, mid, state);
|
|
127
129
|
if (!needsReassess) return null;
|
|
128
130
|
return {
|
|
@@ -160,8 +162,10 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
160
162
|
},
|
|
161
163
|
{
|
|
162
164
|
name: "pre-planning (no research) → research-milestone",
|
|
163
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
165
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
164
166
|
if (state.phase !== "pre-planning") return null;
|
|
167
|
+
// Phase skip: skip research when preference or profile says so
|
|
168
|
+
if (prefs?.phases?.skip_research) return null;
|
|
165
169
|
const researchFile = resolveMilestoneFile(basePath, mid, "RESEARCH");
|
|
166
170
|
if (researchFile) return null; // has research, fall through
|
|
167
171
|
return {
|
|
@@ -186,8 +190,10 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
186
190
|
},
|
|
187
191
|
{
|
|
188
192
|
name: "planning (no research, not S01) → research-slice",
|
|
189
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
193
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
190
194
|
if (state.phase !== "planning") return null;
|
|
195
|
+
// Phase skip: skip research when preference or profile says so
|
|
196
|
+
if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research) return null;
|
|
191
197
|
const sid = state.activeSlice!.id;
|
|
192
198
|
const sTitle = state.activeSlice!.title;
|
|
193
199
|
const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
|
|
@@ -15,8 +15,8 @@ import {
|
|
|
15
15
|
relMilestoneFile, relSliceFile, relSlicePath, relMilestonePath,
|
|
16
16
|
resolveGsdRootFile, relGsdRootFile,
|
|
17
17
|
} from "./paths.js";
|
|
18
|
-
import { resolveSkillDiscoveryMode } from "./preferences.js";
|
|
19
|
-
import type { GSDState } from "./types.js";
|
|
18
|
+
import { resolveSkillDiscoveryMode, resolveInlineLevel } from "./preferences.js";
|
|
19
|
+
import type { GSDState, InlineLevel } from "./types.js";
|
|
20
20
|
import type { GSDPreferences } from "./preferences.js";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { existsSync } from "node:fs";
|
|
@@ -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")}`;
|
|
@@ -393,7 +395,8 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
393
395
|
});
|
|
394
396
|
}
|
|
395
397
|
|
|
396
|
-
export async function buildPlanMilestonePrompt(mid: string, midTitle: string, base: string): Promise<string> {
|
|
398
|
+
export async function buildPlanMilestonePrompt(mid: string, midTitle: string, base: string, level?: InlineLevel): Promise<string> {
|
|
399
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
397
400
|
const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
|
|
398
401
|
const contextRel = relMilestoneFile(base, mid, "CONTEXT");
|
|
399
402
|
const researchPath = resolveMilestoneFile(base, mid, "RESEARCH");
|
|
@@ -406,17 +409,25 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
406
409
|
const { inlinePriorMilestoneSummary } = await import("./files.js");
|
|
407
410
|
const priorSummaryInline = await inlinePriorMilestoneSummary(mid, base);
|
|
408
411
|
if (priorSummaryInline) inlined.push(priorSummaryInline);
|
|
409
|
-
const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
|
|
412
|
+
const projectInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "project.md", "Project") : null;
|
|
410
413
|
if (projectInline) inlined.push(projectInline);
|
|
411
|
-
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
414
|
+
const requirementsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "requirements.md", "Requirements") : null;
|
|
412
415
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
413
|
-
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
416
|
+
const decisionsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "decisions.md", "Decisions") : null;
|
|
414
417
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
418
|
+
const knowledgeInlinePM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
419
|
+
if (knowledgeInlinePM) inlined.push(knowledgeInlinePM);
|
|
415
420
|
inlined.push(inlineTemplate("roadmap", "Roadmap"));
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
421
|
+
if (inlineLevel === "full") {
|
|
422
|
+
inlined.push(inlineTemplate("decisions", "Decisions"));
|
|
423
|
+
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
424
|
+
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
425
|
+
inlined.push(inlineTemplate("secrets-manifest", "Secrets Manifest"));
|
|
426
|
+
} else if (inlineLevel === "standard") {
|
|
427
|
+
inlined.push(inlineTemplate("decisions", "Decisions"));
|
|
428
|
+
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
429
|
+
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
430
|
+
}
|
|
420
431
|
|
|
421
432
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
422
433
|
|
|
@@ -454,6 +465,8 @@ export async function buildResearchSlicePrompt(
|
|
|
454
465
|
if (decisionsInline) inlined.push(decisionsInline);
|
|
455
466
|
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
456
467
|
if (requirementsInline) inlined.push(requirementsInline);
|
|
468
|
+
const knowledgeInlineRS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
469
|
+
if (knowledgeInlineRS) inlined.push(knowledgeInlineRS);
|
|
457
470
|
inlined.push(inlineTemplate("research", "Research"));
|
|
458
471
|
|
|
459
472
|
const depContent = await inlineDependencySummaries(mid, sid, base);
|
|
@@ -479,8 +492,9 @@ export async function buildResearchSlicePrompt(
|
|
|
479
492
|
}
|
|
480
493
|
|
|
481
494
|
export async function buildPlanSlicePrompt(
|
|
482
|
-
mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
|
|
495
|
+
mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
|
|
483
496
|
): Promise<string> {
|
|
497
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
484
498
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
485
499
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
486
500
|
const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
|
|
@@ -490,12 +504,18 @@ export async function buildPlanSlicePrompt(
|
|
|
490
504
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
491
505
|
const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
|
|
492
506
|
if (researchInline) inlined.push(researchInline);
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
507
|
+
if (inlineLevel !== "minimal") {
|
|
508
|
+
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
509
|
+
if (decisionsInline) inlined.push(decisionsInline);
|
|
510
|
+
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
511
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
512
|
+
}
|
|
513
|
+
const knowledgeInlinePS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
514
|
+
if (knowledgeInlinePS) inlined.push(knowledgeInlinePS);
|
|
497
515
|
inlined.push(inlineTemplate("plan", "Slice Plan"));
|
|
498
|
-
|
|
516
|
+
if (inlineLevel === "full") {
|
|
517
|
+
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
518
|
+
}
|
|
499
519
|
|
|
500
520
|
const depContent = await inlineDependencySummaries(mid, sid, base);
|
|
501
521
|
const planActiveOverrides = await loadActiveOverrides(base);
|
|
@@ -519,8 +539,9 @@ export async function buildPlanSlicePrompt(
|
|
|
519
539
|
|
|
520
540
|
export async function buildExecuteTaskPrompt(
|
|
521
541
|
mid: string, sid: string, sTitle: string,
|
|
522
|
-
tid: string, tTitle: string, base: string,
|
|
542
|
+
tid: string, tTitle: string, base: string, level?: InlineLevel,
|
|
523
543
|
): Promise<string> {
|
|
544
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
524
545
|
|
|
525
546
|
const priorSummaries = await getPriorTaskSummaryPaths(mid, sid, tid, base);
|
|
526
547
|
const priorLines = priorSummaries.length > 0
|
|
@@ -560,11 +581,22 @@ export async function buildExecuteTaskPrompt(
|
|
|
560
581
|
legacyContinuePath ? `${relSlicePath(base, mid, sid)}/continue.md` : null,
|
|
561
582
|
);
|
|
562
583
|
|
|
563
|
-
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
584
|
+
// For minimal inline level, only carry forward the most recent prior summary
|
|
585
|
+
const effectivePriorSummaries = inlineLevel === "minimal" && priorSummaries.length > 1
|
|
586
|
+
? priorSummaries.slice(-1)
|
|
587
|
+
: priorSummaries;
|
|
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
|
+
|
|
593
|
+
const inlinedTemplates = inlineLevel === "minimal"
|
|
594
|
+
? inlineTemplate("task-summary", "Task Summary")
|
|
595
|
+
: [
|
|
596
|
+
inlineTemplate("task-summary", "Task Summary"),
|
|
597
|
+
inlineTemplate("decisions", "Decisions"),
|
|
598
|
+
...(knowledgeInlineET ? [knowledgeInlineET] : []),
|
|
599
|
+
].join("\n\n---\n\n");
|
|
568
600
|
|
|
569
601
|
const taskSummaryPath = `${relSlicePath(base, mid, sid)}/tasks/${tid}-SUMMARY.md`;
|
|
570
602
|
|
|
@@ -589,8 +621,9 @@ export async function buildExecuteTaskPrompt(
|
|
|
589
621
|
}
|
|
590
622
|
|
|
591
623
|
export async function buildCompleteSlicePrompt(
|
|
592
|
-
mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
|
|
624
|
+
mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
|
|
593
625
|
): Promise<string> {
|
|
626
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
594
627
|
|
|
595
628
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
596
629
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
@@ -600,8 +633,12 @@ export async function buildCompleteSlicePrompt(
|
|
|
600
633
|
const inlined: string[] = [];
|
|
601
634
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
602
635
|
inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
|
|
603
|
-
|
|
604
|
-
|
|
636
|
+
if (inlineLevel !== "minimal") {
|
|
637
|
+
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
638
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
639
|
+
}
|
|
640
|
+
const knowledgeInlineCS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
641
|
+
if (knowledgeInlineCS) inlined.push(knowledgeInlineCS);
|
|
605
642
|
|
|
606
643
|
// Inline all task summaries for this slice
|
|
607
644
|
const tDir = resolveTasksDir(base, mid, sid);
|
|
@@ -618,7 +655,9 @@ export async function buildCompleteSlicePrompt(
|
|
|
618
655
|
}
|
|
619
656
|
}
|
|
620
657
|
inlined.push(inlineTemplate("slice-summary", "Slice Summary"));
|
|
621
|
-
|
|
658
|
+
if (inlineLevel !== "minimal") {
|
|
659
|
+
inlined.push(inlineTemplate("uat", "UAT"));
|
|
660
|
+
}
|
|
622
661
|
const completeActiveOverrides = await loadActiveOverrides(base);
|
|
623
662
|
const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
|
|
624
663
|
if (completeOverridesInline) inlined.unshift(completeOverridesInline);
|
|
@@ -641,8 +680,9 @@ export async function buildCompleteSlicePrompt(
|
|
|
641
680
|
}
|
|
642
681
|
|
|
643
682
|
export async function buildCompleteMilestonePrompt(
|
|
644
|
-
mid: string, midTitle: string, base: string,
|
|
683
|
+
mid: string, midTitle: string, base: string, level?: InlineLevel,
|
|
645
684
|
): Promise<string> {
|
|
685
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
646
686
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
647
687
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
648
688
|
|
|
@@ -663,13 +703,17 @@ export async function buildCompleteMilestonePrompt(
|
|
|
663
703
|
}
|
|
664
704
|
}
|
|
665
705
|
|
|
666
|
-
// Inline root GSD files
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
706
|
+
// Inline root GSD files (skip for minimal — completion can read these if needed)
|
|
707
|
+
if (inlineLevel !== "minimal") {
|
|
708
|
+
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
709
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
710
|
+
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
711
|
+
if (decisionsInline) inlined.push(decisionsInline);
|
|
712
|
+
const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
|
|
713
|
+
if (projectInline) inlined.push(projectInline);
|
|
714
|
+
}
|
|
715
|
+
const knowledgeInlineCM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
716
|
+
if (knowledgeInlineCM) inlined.push(knowledgeInlineCM);
|
|
673
717
|
// Inline milestone context file (milestone-level, not GSD root)
|
|
674
718
|
const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
|
|
675
719
|
const contextRel = relMilestoneFile(base, mid, "CONTEXT");
|
|
@@ -779,8 +823,9 @@ export async function buildRunUatPrompt(
|
|
|
779
823
|
}
|
|
780
824
|
|
|
781
825
|
export async function buildReassessRoadmapPrompt(
|
|
782
|
-
mid: string, midTitle: string, completedSliceId: string, base: string,
|
|
826
|
+
mid: string, midTitle: string, completedSliceId: string, base: string, level?: InlineLevel,
|
|
783
827
|
): Promise<string> {
|
|
828
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
784
829
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
785
830
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
786
831
|
const summaryPath = resolveSliceFile(base, mid, completedSliceId, "SUMMARY");
|
|
@@ -789,12 +834,16 @@ export async function buildReassessRoadmapPrompt(
|
|
|
789
834
|
const inlined: string[] = [];
|
|
790
835
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Current Roadmap"));
|
|
791
836
|
inlined.push(await inlineFile(summaryPath, summaryRel, `${completedSliceId} Summary`));
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
837
|
+
if (inlineLevel !== "minimal") {
|
|
838
|
+
const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
|
|
839
|
+
if (projectInline) inlined.push(projectInline);
|
|
840
|
+
const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
841
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
842
|
+
const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
|
|
843
|
+
if (decisionsInline) inlined.push(decisionsInline);
|
|
844
|
+
}
|
|
845
|
+
const knowledgeInlineRA = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
846
|
+
if (knowledgeInlineRA) inlined.push(knowledgeInlineRA);
|
|
798
847
|
|
|
799
848
|
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
800
849
|
|
|
@@ -11,6 +11,7 @@ import type { ExtensionContext } from "@gsd/pi-coding-agent";
|
|
|
11
11
|
import {
|
|
12
12
|
clearUnitRuntimeRecord,
|
|
13
13
|
} from "./unit-runtime.js";
|
|
14
|
+
import { clearParseCache } from "./files.js";
|
|
14
15
|
import {
|
|
15
16
|
nativeConflictFiles,
|
|
16
17
|
nativeCommit,
|
|
@@ -107,9 +108,13 @@ export function verifyExpectedArtifact(unitType: string, unitId: string, base: s
|
|
|
107
108
|
// is managed by the hook engine, not the artifact verification system.
|
|
108
109
|
if (unitType.startsWith("hook/")) return true;
|
|
109
110
|
|
|
110
|
-
// Clear stale directory listing cache so artifact checks see
|
|
111
|
-
//
|
|
111
|
+
// Clear stale directory listing cache AND parse cache so artifact checks see
|
|
112
|
+
// fresh disk state (#431). The parse cache must also be cleared because
|
|
113
|
+
// cacheKey() uses length + first/last 100 chars — when a checkbox changes
|
|
114
|
+
// from [ ] to [x], the key collides with the pre-edit version, returning
|
|
115
|
+
// stale parsed results (e.g., slice.done = false when it's actually true).
|
|
112
116
|
clearPathCache();
|
|
117
|
+
clearParseCache();
|
|
113
118
|
|
|
114
119
|
if (unitType === "rewrite-docs") {
|
|
115
120
|
const overridesPath = resolveGsdRootFile(base, "OVERRIDES");
|
|
@@ -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
|