gsd-pi 2.18.0 → 2.20.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 +5 -1
- package/dist/cli.js +3 -3
- package/dist/onboarding.d.ts +3 -1
- package/dist/onboarding.js +77 -3
- package/dist/remote-questions-config.d.ts +1 -1
- package/dist/resources/extensions/google-search/index.ts +164 -47
- package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/dist/resources/extensions/gsd/auto.ts +690 -39
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +654 -36
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/context-budget.ts +243 -0
- package/dist/resources/extensions/gsd/context-store.ts +195 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/dist/resources/extensions/gsd/db-writer.ts +341 -0
- package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/dist/resources/extensions/gsd/doctor.ts +283 -2
- package/dist/resources/extensions/gsd/export.ts +81 -2
- package/dist/resources/extensions/gsd/files.ts +39 -9
- package/dist/resources/extensions/gsd/git-service.ts +6 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
- package/dist/resources/extensions/gsd/history.ts +0 -1
- package/dist/resources/extensions/gsd/index.ts +277 -1
- package/dist/resources/extensions/gsd/md-importer.ts +526 -0
- package/dist/resources/extensions/gsd/metrics.ts +84 -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/notifications.ts +0 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/dist/resources/extensions/gsd/preferences.ts +198 -150
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
- 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 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/dist/resources/extensions/gsd/quick.ts +156 -0
- package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/dist/resources/extensions/gsd/skill-health.ts +417 -0
- package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/dist/resources/extensions/gsd/state.ts +30 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -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/context-budget.test.ts +283 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -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-isolation.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -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/unit-runtime.test.ts +25 -1
- package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -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/types.ts +29 -0
- package/dist/resources/extensions/gsd/undo.ts +0 -1
- package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/dist/resources/extensions/remote-questions/config.ts +4 -2
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/dist/resources/extensions/remote-questions/format.ts +166 -14
- package/dist/resources/extensions/remote-questions/manager.ts +14 -4
- package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/dist/resources/extensions/remote-questions/types.ts +2 -1
- package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/dist/resources/extensions/voice/index.ts +4 -3
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +25 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -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 +43 -11
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
- package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
- package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
- package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
- package/src/resources/extensions/google-search/index.ts +164 -47
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/src/resources/extensions/gsd/auto.ts +690 -39
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +654 -36
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/context-budget.ts +243 -0
- package/src/resources/extensions/gsd/context-store.ts +195 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/src/resources/extensions/gsd/db-writer.ts +341 -0
- package/src/resources/extensions/gsd/debug-logger.ts +178 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/src/resources/extensions/gsd/doctor.ts +283 -2
- package/src/resources/extensions/gsd/export.ts +81 -2
- package/src/resources/extensions/gsd/files.ts +39 -9
- package/src/resources/extensions/gsd/git-service.ts +6 -0
- package/src/resources/extensions/gsd/gsd-db.ts +752 -0
- package/src/resources/extensions/gsd/guided-flow.ts +26 -1
- package/src/resources/extensions/gsd/history.ts +0 -1
- package/src/resources/extensions/gsd/index.ts +277 -1
- package/src/resources/extensions/gsd/md-importer.ts +526 -0
- package/src/resources/extensions/gsd/metrics.ts +84 -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/notifications.ts +0 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/src/resources/extensions/gsd/preferences.ts +198 -150
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
- 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 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/src/resources/extensions/gsd/quick.ts +156 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/src/resources/extensions/gsd/skill-health.ts +417 -0
- package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/src/resources/extensions/gsd/state.ts +30 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -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/context-budget.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -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-isolation.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -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/unit-runtime.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -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/types.ts +29 -0
- package/src/resources/extensions/gsd/undo.ts +0 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/src/resources/extensions/gsd/worktree-command.ts +18 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/src/resources/extensions/remote-questions/config.ts +4 -2
- package/src/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/src/resources/extensions/remote-questions/format.ts +166 -14
- package/src/resources/extensions/remote-questions/manager.ts +14 -4
- package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/src/resources/extensions/remote-questions/types.ts +2 -1
- package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/src/resources/extensions/voice/index.ts +4 -3
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* feature-branch-lifecycle.test.ts — Integration tests for the feature-branch workflow.
|
|
3
|
+
*
|
|
4
|
+
* Proves the core invariant: when auto-mode starts on a feature branch,
|
|
5
|
+
* the milestone worktree branches from that feature branch and merges
|
|
6
|
+
* back to it. `main` is never touched.
|
|
7
|
+
*
|
|
8
|
+
* Scenarios:
|
|
9
|
+
* 1. Full lifecycle: feature branch → worktree → slices → merge back to feature branch
|
|
10
|
+
* 2. Uncommitted changes on feature branch are included via pre-worktree commit
|
|
11
|
+
* 3. Unique milestone IDs (M001-abc123 format) work end-to-end
|
|
12
|
+
* 4. Main branch is completely untouched throughout
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
mkdtempSync, mkdirSync, writeFileSync, rmSync,
|
|
17
|
+
existsSync, realpathSync, readFileSync,
|
|
18
|
+
} from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { execSync } from "node:child_process";
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
createAutoWorktree,
|
|
25
|
+
mergeMilestoneToMain,
|
|
26
|
+
autoWorktreeBranch,
|
|
27
|
+
} from "../auto-worktree.ts";
|
|
28
|
+
import { captureIntegrationBranch, getSliceBranchName } from "../worktree.ts";
|
|
29
|
+
import { writeIntegrationBranch, readIntegrationBranch } from "../git-service.ts";
|
|
30
|
+
import { nextMilestoneId, generateMilestoneSuffix } from "../guided-flow.ts";
|
|
31
|
+
|
|
32
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
33
|
+
|
|
34
|
+
const { assertEq, assertTrue, assertMatch, report } = createTestContext();
|
|
35
|
+
|
|
36
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
function run(cmd: string, cwd: string): string {
|
|
39
|
+
return execSync(cmd, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function commitCount(cwd: string, branch: string): number {
|
|
43
|
+
return parseInt(run(`git rev-list --count ${branch}`, cwd), 10);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function headSha(cwd: string, ref: string): string {
|
|
47
|
+
return run(`git rev-parse ${ref}`, cwd);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function branchExists(cwd: string, branch: string): boolean {
|
|
51
|
+
try {
|
|
52
|
+
run(`git show-ref --verify --quiet refs/heads/${branch}`, cwd);
|
|
53
|
+
return true;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function allBranches(cwd: string): string[] {
|
|
60
|
+
return run("git branch --format='%(refname:short)'", cwd)
|
|
61
|
+
.split("\n")
|
|
62
|
+
.map(b => b.replace(/^'|'$/g, ""))
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create a temp repo with an initial commit on main and a feature branch.
|
|
68
|
+
* Returns { repo, featureBranch } with HEAD on the feature branch.
|
|
69
|
+
*/
|
|
70
|
+
function createFeatureBranchRepo(featureBranch: string): string {
|
|
71
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-fb-lifecycle-")));
|
|
72
|
+
run("git init", dir);
|
|
73
|
+
run("git config user.email test@test.com", dir);
|
|
74
|
+
run("git config user.name Test", dir);
|
|
75
|
+
|
|
76
|
+
// Initial commit on main
|
|
77
|
+
writeFileSync(join(dir, "README.md"), "# project\n");
|
|
78
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
79
|
+
writeFileSync(join(dir, ".gsd", "STATE.md"), "# State\n");
|
|
80
|
+
run("git add .", dir);
|
|
81
|
+
run("git commit -m init", dir);
|
|
82
|
+
run("git branch -M main", dir);
|
|
83
|
+
|
|
84
|
+
// Create and switch to feature branch
|
|
85
|
+
run(`git checkout -b ${featureBranch}`, dir);
|
|
86
|
+
|
|
87
|
+
// Add a commit on the feature branch so it diverges from main
|
|
88
|
+
writeFileSync(join(dir, "feature-setup.ts"), "export const setup = true;\n");
|
|
89
|
+
run("git add .", dir);
|
|
90
|
+
run("git commit -m \"feat: feature branch setup\"", dir);
|
|
91
|
+
|
|
92
|
+
return dir;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function makeRoadmap(
|
|
96
|
+
milestoneId: string,
|
|
97
|
+
title: string,
|
|
98
|
+
slices: Array<{ id: string; title: string }>,
|
|
99
|
+
): string {
|
|
100
|
+
const sliceLines = slices.map(s => `- [x] **${s.id}: ${s.title}**`).join("\n");
|
|
101
|
+
return `# ${milestoneId}: ${title}\n\n## Slices\n${sliceLines}\n`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Add commits to a slice branch on the worktree, merge to milestone branch. */
|
|
105
|
+
function addSliceToMilestone(
|
|
106
|
+
wtPath: string,
|
|
107
|
+
milestoneId: string,
|
|
108
|
+
sliceId: string,
|
|
109
|
+
sliceTitle: string,
|
|
110
|
+
commits: Array<{ file: string; content: string; message: string }>,
|
|
111
|
+
): void {
|
|
112
|
+
const normalizedPath = wtPath.replaceAll("\\", "/");
|
|
113
|
+
const marker = "/.gsd/worktrees/";
|
|
114
|
+
const idx = normalizedPath.indexOf(marker);
|
|
115
|
+
const worktreeName = idx !== -1
|
|
116
|
+
? normalizedPath.slice(idx + marker.length).split("/")[0]
|
|
117
|
+
: null;
|
|
118
|
+
|
|
119
|
+
const sliceBranch = getSliceBranchName(milestoneId, sliceId, worktreeName);
|
|
120
|
+
|
|
121
|
+
run(`git checkout -b ${sliceBranch}`, wtPath);
|
|
122
|
+
for (const c of commits) {
|
|
123
|
+
writeFileSync(join(wtPath, c.file), c.content);
|
|
124
|
+
run("git add .", wtPath);
|
|
125
|
+
run(`git commit -m "${c.message}"`, wtPath);
|
|
126
|
+
}
|
|
127
|
+
run(`git checkout milestone/${milestoneId}`, wtPath);
|
|
128
|
+
run(
|
|
129
|
+
`git merge --no-ff ${sliceBranch} -m "feat(${milestoneId}/${sliceId}): ${sliceTitle}"`,
|
|
130
|
+
wtPath,
|
|
131
|
+
);
|
|
132
|
+
run(`git branch -d ${sliceBranch}`, wtPath);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Tests ──────────────────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
async function main(): Promise<void> {
|
|
138
|
+
const savedCwd = process.cwd();
|
|
139
|
+
const tempDirs: string[] = [];
|
|
140
|
+
|
|
141
|
+
function fresh(featureBranch: string): string {
|
|
142
|
+
const d = createFeatureBranchRepo(featureBranch);
|
|
143
|
+
tempDirs.push(d);
|
|
144
|
+
return d;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
// ================================================================
|
|
149
|
+
// Test 1: Full feature-branch lifecycle with unique milestone IDs
|
|
150
|
+
//
|
|
151
|
+
// Start on f-new-shiny-thing with uncommitted changes, create
|
|
152
|
+
// worktree, add slices, merge back. Assert main is untouched.
|
|
153
|
+
// ================================================================
|
|
154
|
+
console.log("\n=== Feature-branch lifecycle with unique milestone IDs ===");
|
|
155
|
+
{
|
|
156
|
+
const featureBranch = "f-new-shiny-thing";
|
|
157
|
+
const repo = fresh(featureBranch);
|
|
158
|
+
|
|
159
|
+
// Generate a unique milestone ID (M001-xxxxxx format)
|
|
160
|
+
const milestoneId = nextMilestoneId([], true);
|
|
161
|
+
assertMatch(milestoneId, /^M001-[a-z0-9]{6}$/, "unique milestone ID format");
|
|
162
|
+
|
|
163
|
+
// Snapshot main before anything happens
|
|
164
|
+
const mainShaBefore = headSha(repo, "main");
|
|
165
|
+
const mainCommitsBefore = commitCount(repo, "main");
|
|
166
|
+
|
|
167
|
+
// ── Add uncommitted changes on the feature branch ──
|
|
168
|
+
// Simulates a user with dirty working tree when they start auto-mode.
|
|
169
|
+
writeFileSync(join(repo, "wip-config.ts"), "export const config = { debug: true };\n");
|
|
170
|
+
writeFileSync(join(repo, "wip-types.ts"), "export type AppState = { ready: boolean };\n");
|
|
171
|
+
|
|
172
|
+
// Verify files are uncommitted
|
|
173
|
+
const statusBefore = run("git status --short", repo);
|
|
174
|
+
assertTrue(statusBefore.includes("wip-config.ts"), "wip-config.ts is uncommitted");
|
|
175
|
+
assertTrue(statusBefore.includes("wip-types.ts"), "wip-types.ts is uncommitted");
|
|
176
|
+
|
|
177
|
+
// ── Simulate what startAuto does: commit dirty state, capture integration branch ──
|
|
178
|
+
// startAuto bootstraps .gsd/ which commits .gsd/ files. It also calls
|
|
179
|
+
// captureIntegrationBranch which commits META.json. But user's dirty
|
|
180
|
+
// files need to be committed first so the worktree branches from a
|
|
181
|
+
// commit that includes them.
|
|
182
|
+
//
|
|
183
|
+
// In production, the first dispatch unit (research-milestone) would
|
|
184
|
+
// auto-commit via autoCommitCurrentBranch. But the worktree is created
|
|
185
|
+
// BEFORE any unit runs. So we simulate the pre-worktree state:
|
|
186
|
+
// GSD bootstraps .gsd/ and captureIntegrationBranch commits metadata.
|
|
187
|
+
// The user's dirty files are NOT auto-committed pre-worktree — they
|
|
188
|
+
// stay in the original working directory.
|
|
189
|
+
|
|
190
|
+
// Create milestone directory (happens during guided-flow)
|
|
191
|
+
mkdirSync(join(repo, ".gsd", "milestones", milestoneId), { recursive: true });
|
|
192
|
+
|
|
193
|
+
// Write integration branch metadata (what captureIntegrationBranch does)
|
|
194
|
+
writeIntegrationBranch(repo, milestoneId, featureBranch);
|
|
195
|
+
|
|
196
|
+
// Verify integration branch recorded
|
|
197
|
+
const recorded = readIntegrationBranch(repo, milestoneId);
|
|
198
|
+
assertEq(recorded, featureBranch, "integration branch recorded as feature branch");
|
|
199
|
+
|
|
200
|
+
// Snapshot feature branch SHA after metadata commit (HEAD may have advanced)
|
|
201
|
+
const featureShaBeforeWorktree = headSha(repo, featureBranch);
|
|
202
|
+
|
|
203
|
+
// ── Create the auto-worktree ──
|
|
204
|
+
const wtPath = createAutoWorktree(repo, milestoneId);
|
|
205
|
+
tempDirs.push(wtPath);
|
|
206
|
+
assertTrue(existsSync(wtPath), "worktree directory created");
|
|
207
|
+
|
|
208
|
+
// Worktree should be on milestone/<unique-id> branch
|
|
209
|
+
const wtBranch = run("git branch --show-current", wtPath);
|
|
210
|
+
assertEq(wtBranch, `milestone/${milestoneId}`, "worktree is on milestone branch");
|
|
211
|
+
|
|
212
|
+
// Milestone branch should be rooted at the feature branch, not main
|
|
213
|
+
const milestoneBranchBase = headSha(repo, `milestone/${milestoneId}`);
|
|
214
|
+
assertEq(
|
|
215
|
+
milestoneBranchBase,
|
|
216
|
+
featureShaBeforeWorktree,
|
|
217
|
+
"milestone branch starts from feature branch HEAD",
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
// Feature-branch-only file should be in the worktree
|
|
221
|
+
assertTrue(
|
|
222
|
+
existsSync(join(wtPath, "feature-setup.ts")),
|
|
223
|
+
"feature branch file (feature-setup.ts) exists in worktree",
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Main should be completely untouched at this point
|
|
227
|
+
assertEq(headSha(repo, "main"), mainShaBefore, "main SHA unchanged after worktree creation");
|
|
228
|
+
|
|
229
|
+
// ── Do work in slices ──
|
|
230
|
+
addSliceToMilestone(wtPath, milestoneId, "S01", "Auth module", [
|
|
231
|
+
{ file: "auth.ts", content: "export const auth = true;\n", message: "feat: add auth" },
|
|
232
|
+
{ file: "auth-utils.ts", content: "export const hash = () => {};\n", message: "feat: auth utils" },
|
|
233
|
+
]);
|
|
234
|
+
addSliceToMilestone(wtPath, milestoneId, "S02", "Dashboard", [
|
|
235
|
+
{ file: "dashboard.ts", content: "export const dash = true;\n", message: "feat: add dashboard" },
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
// ── Merge milestone back to feature branch ──
|
|
239
|
+
const roadmap = makeRoadmap(milestoneId, "New shiny feature", [
|
|
240
|
+
{ id: "S01", title: "Auth module" },
|
|
241
|
+
{ id: "S02", title: "Dashboard" },
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
process.chdir(wtPath);
|
|
245
|
+
const result = mergeMilestoneToMain(repo, milestoneId, roadmap);
|
|
246
|
+
process.chdir(savedCwd);
|
|
247
|
+
|
|
248
|
+
// ── Assert: feature branch received the merge ──
|
|
249
|
+
const currentBranch = run("git branch --show-current", repo);
|
|
250
|
+
assertEq(currentBranch, featureBranch, "repo is on feature branch after merge");
|
|
251
|
+
|
|
252
|
+
// Exactly one new commit on feature branch (the squash merge)
|
|
253
|
+
const featureLog = run(`git log --oneline ${featureBranch}`, repo);
|
|
254
|
+
assertTrue(
|
|
255
|
+
featureLog.includes(`feat(${milestoneId})`),
|
|
256
|
+
"feature branch has milestone merge commit",
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// Slice files are on the feature branch
|
|
260
|
+
assertTrue(existsSync(join(repo, "auth.ts")), "auth.ts on feature branch");
|
|
261
|
+
assertTrue(existsSync(join(repo, "dashboard.ts")), "dashboard.ts on feature branch");
|
|
262
|
+
assertTrue(existsSync(join(repo, "auth-utils.ts")), "auth-utils.ts on feature branch");
|
|
263
|
+
|
|
264
|
+
// Original feature branch file still present
|
|
265
|
+
assertTrue(existsSync(join(repo, "feature-setup.ts")), "feature-setup.ts still on feature branch");
|
|
266
|
+
|
|
267
|
+
// Commit message is well-formed
|
|
268
|
+
assertTrue(result.commitMessage.includes("New shiny feature"), "commit message has milestone title");
|
|
269
|
+
assertTrue(result.commitMessage.includes("S01: Auth module"), "commit message lists S01");
|
|
270
|
+
assertTrue(result.commitMessage.includes("S02: Dashboard"), "commit message lists S02");
|
|
271
|
+
assertTrue(
|
|
272
|
+
result.commitMessage.includes(`milestone/${milestoneId}`),
|
|
273
|
+
"commit message references milestone branch with unique ID",
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// ── Assert: main is COMPLETELY untouched ──
|
|
277
|
+
assertEq(headSha(repo, "main"), mainShaBefore, "main SHA unchanged after merge");
|
|
278
|
+
assertEq(commitCount(repo, "main"), mainCommitsBefore, "main commit count unchanged");
|
|
279
|
+
|
|
280
|
+
// Main should NOT have any of the milestone files
|
|
281
|
+
run("git checkout main", repo);
|
|
282
|
+
assertTrue(!existsSync(join(repo, "auth.ts")), "auth.ts NOT on main");
|
|
283
|
+
assertTrue(!existsSync(join(repo, "dashboard.ts")), "dashboard.ts NOT on main");
|
|
284
|
+
assertTrue(!existsSync(join(repo, "feature-setup.ts")), "feature-setup.ts NOT on main");
|
|
285
|
+
run(`git checkout ${featureBranch}`, repo);
|
|
286
|
+
|
|
287
|
+
// ── Assert: worktree cleaned up ──
|
|
288
|
+
const worktreeDir = join(repo, ".gsd", "worktrees", milestoneId);
|
|
289
|
+
assertTrue(!existsSync(worktreeDir), "worktree directory removed");
|
|
290
|
+
|
|
291
|
+
// Milestone branch deleted
|
|
292
|
+
assertTrue(
|
|
293
|
+
!branchExists(repo, `milestone/${milestoneId}`),
|
|
294
|
+
"milestone branch deleted after merge",
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
// Only expected branches remain
|
|
298
|
+
const branches = allBranches(repo);
|
|
299
|
+
assertTrue(branches.includes("main"), "main branch exists");
|
|
300
|
+
assertTrue(branches.includes(featureBranch), "feature branch exists");
|
|
301
|
+
assertTrue(
|
|
302
|
+
!branches.some(b => b.startsWith("milestone/")),
|
|
303
|
+
"no milestone branches remain",
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ================================================================
|
|
308
|
+
// Test 2: Uncommitted .gsd/ planning files are available in worktree
|
|
309
|
+
//
|
|
310
|
+
// When auto-mode starts, .gsd/ files may be untracked/uncommitted.
|
|
311
|
+
// copyPlanningArtifacts should carry them into the worktree even if
|
|
312
|
+
// they weren't committed on the feature branch.
|
|
313
|
+
// ================================================================
|
|
314
|
+
console.log("\n=== Untracked planning files copied to worktree ===");
|
|
315
|
+
{
|
|
316
|
+
const featureBranch = "f-planning-test";
|
|
317
|
+
const repo = fresh(featureBranch);
|
|
318
|
+
const milestoneId = nextMilestoneId([], true);
|
|
319
|
+
|
|
320
|
+
// Write planning files that are NOT committed
|
|
321
|
+
mkdirSync(join(repo, ".gsd", "milestones", milestoneId, "slices", "S01", "tasks"), { recursive: true });
|
|
322
|
+
writeFileSync(
|
|
323
|
+
join(repo, ".gsd", "milestones", milestoneId, `${milestoneId}-ROADMAP.md`),
|
|
324
|
+
makeRoadmap(milestoneId, "Planning test", [{ id: "S01", title: "First" }]),
|
|
325
|
+
);
|
|
326
|
+
writeFileSync(
|
|
327
|
+
join(repo, ".gsd", "milestones", milestoneId, "slices", "S01", "S01-PLAN.md"),
|
|
328
|
+
"# S01: First\n\n**Goal:** Test\n**Demo:** Test\n\n## Tasks\n- [ ] **T01: Do it** `est:10m`\n",
|
|
329
|
+
);
|
|
330
|
+
writeFileSync(join(repo, ".gsd", "PROJECT.md"), "# Planning Test Project\n");
|
|
331
|
+
writeFileSync(join(repo, ".gsd", "DECISIONS.md"), "# Decisions\n\n## D001\nTest decision.\n");
|
|
332
|
+
|
|
333
|
+
// These files are untracked
|
|
334
|
+
assertTrue(run("git status --short", repo).length > 0, "repo has untracked files");
|
|
335
|
+
|
|
336
|
+
// Record integration branch and create worktree
|
|
337
|
+
writeIntegrationBranch(repo, milestoneId, featureBranch);
|
|
338
|
+
const wtPath = createAutoWorktree(repo, milestoneId);
|
|
339
|
+
tempDirs.push(wtPath);
|
|
340
|
+
|
|
341
|
+
// Planning files should exist in the worktree (via copyPlanningArtifacts)
|
|
342
|
+
assertTrue(
|
|
343
|
+
existsSync(join(wtPath, ".gsd", "milestones", milestoneId, `${milestoneId}-ROADMAP.md`)),
|
|
344
|
+
"ROADMAP.md copied to worktree",
|
|
345
|
+
);
|
|
346
|
+
assertTrue(
|
|
347
|
+
existsSync(join(wtPath, ".gsd", "milestones", milestoneId, "slices", "S01", "S01-PLAN.md")),
|
|
348
|
+
"S01-PLAN.md copied to worktree",
|
|
349
|
+
);
|
|
350
|
+
assertTrue(
|
|
351
|
+
existsSync(join(wtPath, ".gsd", "PROJECT.md")),
|
|
352
|
+
"PROJECT.md copied to worktree",
|
|
353
|
+
);
|
|
354
|
+
assertTrue(
|
|
355
|
+
existsSync(join(wtPath, ".gsd", "DECISIONS.md")),
|
|
356
|
+
"DECISIONS.md copied to worktree",
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// Clean up: chdir back before teardown
|
|
360
|
+
process.chdir(savedCwd);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ================================================================
|
|
364
|
+
// Test 3: Multiple milestones on the same feature branch
|
|
365
|
+
//
|
|
366
|
+
// Proves that unique IDs prevent collision when running successive
|
|
367
|
+
// milestones, and each merge lands on the feature branch.
|
|
368
|
+
// ================================================================
|
|
369
|
+
console.log("\n=== Multiple unique milestones on same feature branch ===");
|
|
370
|
+
{
|
|
371
|
+
const featureBranch = "f-multi-milestone";
|
|
372
|
+
const repo = fresh(featureBranch);
|
|
373
|
+
|
|
374
|
+
const mainShaBefore = headSha(repo, "main");
|
|
375
|
+
|
|
376
|
+
// First milestone
|
|
377
|
+
const mid1 = nextMilestoneId([], true);
|
|
378
|
+
mkdirSync(join(repo, ".gsd", "milestones", mid1), { recursive: true });
|
|
379
|
+
writeIntegrationBranch(repo, mid1, featureBranch);
|
|
380
|
+
|
|
381
|
+
const wt1 = createAutoWorktree(repo, mid1);
|
|
382
|
+
tempDirs.push(wt1);
|
|
383
|
+
addSliceToMilestone(wt1, mid1, "S01", "First milestone work", [
|
|
384
|
+
{ file: "m1-feature.ts", content: "export const m1 = true;\n", message: "feat: m1" },
|
|
385
|
+
]);
|
|
386
|
+
process.chdir(wt1);
|
|
387
|
+
mergeMilestoneToMain(repo, mid1, makeRoadmap(mid1, "First", [{ id: "S01", title: "First milestone work" }]));
|
|
388
|
+
process.chdir(savedCwd);
|
|
389
|
+
|
|
390
|
+
assertTrue(existsSync(join(repo, "m1-feature.ts")), "m1 file on feature branch");
|
|
391
|
+
|
|
392
|
+
// Second milestone — different unique ID
|
|
393
|
+
const mid2 = nextMilestoneId([mid1], true);
|
|
394
|
+
assertTrue(mid1 !== mid2, "second milestone has different ID");
|
|
395
|
+
assertMatch(mid2, /^M002-[a-z0-9]{6}$/, "second milestone is M002-xxxxxx");
|
|
396
|
+
|
|
397
|
+
mkdirSync(join(repo, ".gsd", "milestones", mid2), { recursive: true });
|
|
398
|
+
writeIntegrationBranch(repo, mid2, featureBranch);
|
|
399
|
+
|
|
400
|
+
const wt2 = createAutoWorktree(repo, mid2);
|
|
401
|
+
tempDirs.push(wt2);
|
|
402
|
+
addSliceToMilestone(wt2, mid2, "S01", "Second milestone work", [
|
|
403
|
+
{ file: "m2-feature.ts", content: "export const m2 = true;\n", message: "feat: m2" },
|
|
404
|
+
]);
|
|
405
|
+
process.chdir(wt2);
|
|
406
|
+
mergeMilestoneToMain(repo, mid2, makeRoadmap(mid2, "Second", [{ id: "S01", title: "Second milestone work" }]));
|
|
407
|
+
process.chdir(savedCwd);
|
|
408
|
+
|
|
409
|
+
// Both milestone files on feature branch
|
|
410
|
+
assertTrue(existsSync(join(repo, "m1-feature.ts")), "m1 file still on feature branch");
|
|
411
|
+
assertTrue(existsSync(join(repo, "m2-feature.ts")), "m2 file on feature branch");
|
|
412
|
+
|
|
413
|
+
// Main completely untouched
|
|
414
|
+
assertEq(headSha(repo, "main"), mainShaBefore, "main unchanged after two milestones");
|
|
415
|
+
|
|
416
|
+
// No milestone branches remain
|
|
417
|
+
const branches = allBranches(repo);
|
|
418
|
+
assertTrue(
|
|
419
|
+
!branches.some(b => b.startsWith("milestone/")),
|
|
420
|
+
"no milestone branches remain after two milestones",
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
} finally {
|
|
425
|
+
process.chdir(savedCwd);
|
|
426
|
+
for (const d of tempDirs) {
|
|
427
|
+
try { rmSync(d, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
report();
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
main();
|