gsd-pi 2.8.2 → 2.9.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 +2 -1
- package/dist/cli.js +5 -0
- package/dist/loader.js +1 -1
- package/dist/update-check.d.ts +24 -0
- package/dist/update-check.js +93 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.d.ts +4 -2
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.d.ts +46 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.js +758 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.d.ts +23 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.js +267 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.d.ts +17 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.js +101 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.d.ts +15 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.js +46 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.d.ts +35 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.js +709 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts +2 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +308 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.d.ts +34 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.js +136 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.d.ts +262 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.js +64 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.d.ts +50 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.js +574 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.d.ts +13 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.js +4 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +10 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +2 -2
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js +80 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +5 -0
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/src/core/extensions/types.ts +4 -2
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/client.ts +880 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/config.ts +325 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/defaults.json +456 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/edits.ts +109 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/helpers.ts +54 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/index.ts +943 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +407 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lsp.md +33 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lspmux.ts +199 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/types.ts +421 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/utils.ts +682 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/tools/index.ts +10 -0
- package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +2 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/interactive-mode.ts +94 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/rpc/rpc-types.ts +2 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +4 -2
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +46 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js +758 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +23 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.js +267 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.d.ts +17 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.js +101 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.js +46 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.js +709 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +308 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts +34 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +136 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +262 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js +64 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +50 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +574 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -0
- 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/index.d.ts +13 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +4 -0
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +10 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -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 +80 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +4 -2
- package/packages/pi-coding-agent/src/core/lsp/client.ts +880 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +325 -0
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +456 -0
- package/packages/pi-coding-agent/src/core/lsp/edits.ts +109 -0
- package/packages/pi-coding-agent/src/core/lsp/helpers.ts +54 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +943 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +407 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +33 -0
- package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +199 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +421 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +682 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/core/tools/index.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +94 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +2 -1
- package/src/resources/extensions/ask-user-questions.ts +42 -2
- package/src/resources/extensions/bg-shell/index.ts +34 -37
- package/src/resources/extensions/browser-tools/core.d.ts +205 -0
- package/src/resources/extensions/browser-tools/index.ts +2 -2
- package/src/resources/extensions/browser-tools/refs.ts +1 -1
- package/src/resources/extensions/browser-tools/tools/session.ts +1 -1
- package/src/resources/extensions/context7/index.ts +2 -2
- package/src/resources/extensions/get-secrets-from-user.ts +3 -2
- package/src/resources/extensions/google-search/index.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +126 -12
- package/src/resources/extensions/gsd/commands.ts +218 -3
- package/src/resources/extensions/gsd/doctor.ts +1 -1
- package/src/resources/extensions/gsd/git-service.ts +163 -13
- package/src/resources/extensions/gsd/guided-flow.ts +19 -9
- package/src/resources/extensions/gsd/index.ts +17 -7
- package/src/resources/extensions/gsd/preferences.ts +1 -1
- package/src/resources/extensions/gsd/tests/git-service.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +10 -10
- package/src/resources/extensions/gsd/tests/next-milestone-id.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +352 -0
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/worktree.ts +20 -1
- package/src/resources/extensions/mac-tools/index.ts +1 -1
- package/src/resources/extensions/search-the-web/command-search-provider.ts +1 -1
- package/src/resources/extensions/search-the-web/format.ts +1 -1
- package/src/resources/extensions/search-the-web/index.ts +5 -5
- package/src/resources/extensions/search-the-web/native-search.ts +5 -6
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +7 -7
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +11 -11
- package/src/resources/extensions/search-the-web/tool-search.ts +10 -10
- package/src/resources/extensions/shared/interview-ui.ts +2 -2
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
RUNTIME_EXCLUSION_PATHS,
|
|
10
10
|
VALID_BRANCH_NAME,
|
|
11
11
|
runGit,
|
|
12
|
+
readIntegrationBranch,
|
|
13
|
+
writeIntegrationBranch,
|
|
12
14
|
type GitPreferences,
|
|
13
15
|
type CommitOptions,
|
|
14
16
|
type MergeSliceResult,
|
|
@@ -1370,6 +1372,230 @@ async function main(): Promise<void> {
|
|
|
1370
1372
|
assert(true, "PreMergeCheckResult type exported and usable");
|
|
1371
1373
|
}
|
|
1372
1374
|
|
|
1375
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1376
|
+
// Integration branch — feature-branch workflow support
|
|
1377
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1378
|
+
|
|
1379
|
+
// ─── writeIntegrationBranch / readIntegrationBranch: round-trip ────────
|
|
1380
|
+
|
|
1381
|
+
console.log("\n=== Integration branch: write and read ===");
|
|
1382
|
+
|
|
1383
|
+
{
|
|
1384
|
+
const repo = initBranchTestRepo();
|
|
1385
|
+
|
|
1386
|
+
// Initially no integration branch
|
|
1387
|
+
assertEq(readIntegrationBranch(repo, "M001"), null, "readIntegrationBranch returns null when no metadata");
|
|
1388
|
+
|
|
1389
|
+
// Write integration branch
|
|
1390
|
+
writeIntegrationBranch(repo, "M001", "f-123-new-thing");
|
|
1391
|
+
assertEq(readIntegrationBranch(repo, "M001"), "f-123-new-thing", "readIntegrationBranch returns written branch");
|
|
1392
|
+
|
|
1393
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
// ─── writeIntegrationBranch: idempotent — doesn't overwrite ───────────
|
|
1397
|
+
|
|
1398
|
+
console.log("\n=== Integration branch: idempotent write ===");
|
|
1399
|
+
|
|
1400
|
+
{
|
|
1401
|
+
const repo = initBranchTestRepo();
|
|
1402
|
+
|
|
1403
|
+
writeIntegrationBranch(repo, "M001", "f-123-first");
|
|
1404
|
+
writeIntegrationBranch(repo, "M001", "f-456-second"); // should NOT overwrite
|
|
1405
|
+
|
|
1406
|
+
assertEq(readIntegrationBranch(repo, "M001"), "f-123-first", "second write does not overwrite existing integration branch");
|
|
1407
|
+
|
|
1408
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
// ─── writeIntegrationBranch: rejects slice branches ───────────────────
|
|
1412
|
+
|
|
1413
|
+
console.log("\n=== Integration branch: rejects slice branches ===");
|
|
1414
|
+
|
|
1415
|
+
{
|
|
1416
|
+
const repo = initBranchTestRepo();
|
|
1417
|
+
|
|
1418
|
+
writeIntegrationBranch(repo, "M001", "gsd/M001/S01");
|
|
1419
|
+
assertEq(readIntegrationBranch(repo, "M001"), null, "slice branches are not recorded as integration branch");
|
|
1420
|
+
|
|
1421
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
// ─── writeIntegrationBranch: rejects invalid branch names ─────────────
|
|
1425
|
+
|
|
1426
|
+
console.log("\n=== Integration branch: rejects invalid names ===");
|
|
1427
|
+
|
|
1428
|
+
{
|
|
1429
|
+
const repo = initBranchTestRepo();
|
|
1430
|
+
|
|
1431
|
+
writeIntegrationBranch(repo, "M001", "bad; rm -rf /");
|
|
1432
|
+
assertEq(readIntegrationBranch(repo, "M001"), null, "invalid branch name is not recorded");
|
|
1433
|
+
|
|
1434
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// ─── getMainBranch: uses integration branch when milestone set ────────
|
|
1438
|
+
|
|
1439
|
+
console.log("\n=== getMainBranch: integration branch from milestone metadata ===");
|
|
1440
|
+
|
|
1441
|
+
{
|
|
1442
|
+
const repo = initBranchTestRepo();
|
|
1443
|
+
|
|
1444
|
+
// Create a feature branch
|
|
1445
|
+
run("git checkout -b f-123-feature", repo);
|
|
1446
|
+
run("git checkout main", repo);
|
|
1447
|
+
|
|
1448
|
+
// Write integration branch metadata
|
|
1449
|
+
writeIntegrationBranch(repo, "M001", "f-123-feature");
|
|
1450
|
+
|
|
1451
|
+
// Without milestone set, getMainBranch returns "main"
|
|
1452
|
+
const svc = new GitServiceImpl(repo);
|
|
1453
|
+
assertEq(svc.getMainBranch(), "main", "getMainBranch returns main when no milestone set");
|
|
1454
|
+
|
|
1455
|
+
// With milestone set, getMainBranch returns the integration branch
|
|
1456
|
+
svc.setMilestoneId("M001");
|
|
1457
|
+
assertEq(svc.getMainBranch(), "f-123-feature", "getMainBranch returns integration branch when milestone set");
|
|
1458
|
+
|
|
1459
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// ─── getMainBranch: main_branch pref still takes priority ─────────────
|
|
1463
|
+
|
|
1464
|
+
console.log("\n=== getMainBranch: main_branch pref overrides integration branch ===");
|
|
1465
|
+
|
|
1466
|
+
{
|
|
1467
|
+
const repo = initBranchTestRepo();
|
|
1468
|
+
|
|
1469
|
+
run("git checkout -b f-123-feature", repo);
|
|
1470
|
+
run("git checkout -b trunk", repo);
|
|
1471
|
+
run("git checkout main", repo);
|
|
1472
|
+
|
|
1473
|
+
writeIntegrationBranch(repo, "M001", "f-123-feature");
|
|
1474
|
+
|
|
1475
|
+
// Explicit preference still wins
|
|
1476
|
+
const svc = new GitServiceImpl(repo, { main_branch: "trunk" });
|
|
1477
|
+
svc.setMilestoneId("M001");
|
|
1478
|
+
assertEq(svc.getMainBranch(), "trunk", "main_branch preference overrides integration branch");
|
|
1479
|
+
|
|
1480
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// ─── getMainBranch: falls back when integration branch deleted ────────
|
|
1484
|
+
|
|
1485
|
+
console.log("\n=== getMainBranch: fallback when integration branch deleted ===");
|
|
1486
|
+
|
|
1487
|
+
{
|
|
1488
|
+
const repo = initBranchTestRepo();
|
|
1489
|
+
|
|
1490
|
+
// Write metadata pointing to a branch that doesn't exist
|
|
1491
|
+
writeIntegrationBranch(repo, "M001", "deleted-branch");
|
|
1492
|
+
|
|
1493
|
+
const svc = new GitServiceImpl(repo);
|
|
1494
|
+
svc.setMilestoneId("M001");
|
|
1495
|
+
assertEq(svc.getMainBranch(), "main", "getMainBranch falls back to main when integration branch no longer exists");
|
|
1496
|
+
|
|
1497
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
// ─── End-to-end: feature branch workflow ──────────────────────────────
|
|
1501
|
+
|
|
1502
|
+
console.log("\n=== End-to-end: feature branch workflow ===");
|
|
1503
|
+
|
|
1504
|
+
{
|
|
1505
|
+
const repo = initBranchTestRepo();
|
|
1506
|
+
|
|
1507
|
+
// Simulate: user creates feature branch and starts GSD
|
|
1508
|
+
run("git checkout -b f-123-new-thing", repo);
|
|
1509
|
+
createFile(repo, "setup.txt", "initial setup");
|
|
1510
|
+
run("git add -A", repo);
|
|
1511
|
+
run("git commit -m 'initial feature setup'", repo);
|
|
1512
|
+
|
|
1513
|
+
// Record integration branch (this is what auto.ts does at startup)
|
|
1514
|
+
writeIntegrationBranch(repo, "M001", "f-123-new-thing");
|
|
1515
|
+
|
|
1516
|
+
// Create GitServiceImpl with milestone set
|
|
1517
|
+
const svc = new GitServiceImpl(repo);
|
|
1518
|
+
svc.setMilestoneId("M001");
|
|
1519
|
+
|
|
1520
|
+
// Verify getMainBranch returns the feature branch, not "main"
|
|
1521
|
+
assertEq(svc.getMainBranch(), "f-123-new-thing", "e2e: getMainBranch returns feature branch");
|
|
1522
|
+
|
|
1523
|
+
// Create slice branch — should branch from f-123-new-thing (current)
|
|
1524
|
+
svc.ensureSliceBranch("M001", "S01");
|
|
1525
|
+
assertEq(svc.getCurrentBranch(), "gsd/M001/S01", "e2e: slice branch created");
|
|
1526
|
+
|
|
1527
|
+
// The slice branch should have the feature branch's commit
|
|
1528
|
+
const log = run("git log --oneline", repo);
|
|
1529
|
+
assert(log.includes("initial feature setup"), "e2e: slice branch inherits feature branch content");
|
|
1530
|
+
|
|
1531
|
+
// Do work on the slice branch
|
|
1532
|
+
createFile(repo, "src/feature.ts", "export const feature = true;");
|
|
1533
|
+
svc.commit({ message: "feat: add feature module" });
|
|
1534
|
+
|
|
1535
|
+
// switchToMain should go to feature branch
|
|
1536
|
+
svc.switchToMain();
|
|
1537
|
+
assertEq(svc.getCurrentBranch(), "f-123-new-thing", "e2e: switchToMain goes to feature branch, not main");
|
|
1538
|
+
|
|
1539
|
+
// mergeSliceToMain should merge into feature branch
|
|
1540
|
+
const result = svc.mergeSliceToMain("M001", "S01", "Add feature module");
|
|
1541
|
+
assertEq(result.mergedCommitMessage, "feat(M001/S01): Add feature module", "e2e: merge commit message correct");
|
|
1542
|
+
assertEq(svc.getCurrentBranch(), "f-123-new-thing", "e2e: after merge, still on feature branch");
|
|
1543
|
+
|
|
1544
|
+
// The feature branch should have the merged work
|
|
1545
|
+
const files = run("git ls-files", repo);
|
|
1546
|
+
assert(files.includes("src/feature.ts"), "e2e: merged file exists on feature branch");
|
|
1547
|
+
|
|
1548
|
+
// Main should NOT have the merged work
|
|
1549
|
+
run("git checkout main", repo);
|
|
1550
|
+
const mainFiles = run("git ls-files", repo);
|
|
1551
|
+
assert(!mainFiles.includes("src/feature.ts"), "e2e: main does NOT have merged work — it stays on the feature branch");
|
|
1552
|
+
|
|
1553
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// ─── Per-milestone isolation: different milestones, different targets ──
|
|
1557
|
+
|
|
1558
|
+
console.log("\n=== Integration branch: per-milestone isolation ===");
|
|
1559
|
+
|
|
1560
|
+
{
|
|
1561
|
+
const repo = initBranchTestRepo();
|
|
1562
|
+
|
|
1563
|
+
run("git checkout -b feature-a", repo);
|
|
1564
|
+
run("git checkout -b feature-b", repo);
|
|
1565
|
+
run("git checkout main", repo);
|
|
1566
|
+
|
|
1567
|
+
writeIntegrationBranch(repo, "M001", "feature-a");
|
|
1568
|
+
writeIntegrationBranch(repo, "M002", "feature-b");
|
|
1569
|
+
|
|
1570
|
+
const svc = new GitServiceImpl(repo);
|
|
1571
|
+
|
|
1572
|
+
svc.setMilestoneId("M001");
|
|
1573
|
+
assertEq(svc.getMainBranch(), "feature-a", "M001 integration branch is feature-a");
|
|
1574
|
+
|
|
1575
|
+
svc.setMilestoneId("M002");
|
|
1576
|
+
assertEq(svc.getMainBranch(), "feature-b", "M002 integration branch is feature-b");
|
|
1577
|
+
|
|
1578
|
+
svc.setMilestoneId(null);
|
|
1579
|
+
assertEq(svc.getMainBranch(), "main", "no milestone set → falls back to main");
|
|
1580
|
+
|
|
1581
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// ─── Backward compatibility: no metadata → existing behavior ──────────
|
|
1585
|
+
|
|
1586
|
+
console.log("\n=== Integration branch: backward compat ===");
|
|
1587
|
+
|
|
1588
|
+
{
|
|
1589
|
+
const repo = initBranchTestRepo();
|
|
1590
|
+
const svc = new GitServiceImpl(repo);
|
|
1591
|
+
|
|
1592
|
+
// Set milestone but no metadata file exists
|
|
1593
|
+
svc.setMilestoneId("M001");
|
|
1594
|
+
assertEq(svc.getMainBranch(), "main", "backward compat: no metadata file → falls back to main");
|
|
1595
|
+
|
|
1596
|
+
rmSync(repo, { recursive: true, force: true });
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1373
1599
|
// ─── untrackRuntimeFiles: removes tracked runtime files from index ───
|
|
1374
1600
|
|
|
1375
1601
|
console.log("\n=== untrackRuntimeFiles ===");
|
|
@@ -354,8 +354,8 @@ async function main(): Promise<void> {
|
|
|
354
354
|
assert(state.phase !== undefined, 'pipeline: deriveState returns phase');
|
|
355
355
|
assert(state.activeMilestone !== null, 'pipeline: deriveState has activeMilestone');
|
|
356
356
|
assertEq(state.activeMilestone!.id, 'M001', 'pipeline: deriveState activeMilestone is M001');
|
|
357
|
-
assert(state.progress
|
|
358
|
-
assert(state.progress
|
|
357
|
+
assert(state.progress!.slices !== undefined, 'pipeline: deriveState has slices progress');
|
|
358
|
+
assert(state.progress!.tasks !== undefined, 'pipeline: deriveState has tasks progress');
|
|
359
359
|
|
|
360
360
|
} finally {
|
|
361
361
|
rmSync(base, { recursive: true, force: true });
|
|
@@ -317,7 +317,7 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
317
317
|
assertEq(doneSlice?.tasks[0]?.summary?.duration, '2h', 'completion: summary duration from frontmatter');
|
|
318
318
|
assertEq(doneSlice?.tasks[0]?.summary?.provides, ['feature-01'], 'completion: summary provides from frontmatter');
|
|
319
319
|
assertEq(doneSlice?.tasks[0]?.summary?.keyFiles, ['file-01.ts'], 'completion: summary keyFiles from frontmatter');
|
|
320
|
-
assert(doneSlice?.tasks[0]?.summary?.whatHappened?.includes('Summary body'), 'completion: summary whatHappened from body');
|
|
320
|
+
assert(doneSlice?.tasks[0]?.summary?.whatHappened?.includes('Summary body') ?? false, 'completion: summary whatHappened from body');
|
|
321
321
|
assert(doneSlice?.summary !== null, 'completion: done slice has slice summary');
|
|
322
322
|
assert(activeSlice?.summary === null, 'completion: active slice has null summary');
|
|
323
323
|
assertEq(doneSlice?.tasks[0]?.estimate, '2h', 'completion: task estimate from summary duration');
|
|
@@ -234,18 +234,18 @@ async function main(): Promise<void> {
|
|
|
234
234
|
assertEq(state.activeSlice!.id, 'S02', 'incomplete: deriveState activeSlice is S02');
|
|
235
235
|
assert(state.activeTask !== null, 'incomplete: deriveState has activeTask');
|
|
236
236
|
assertEq(state.activeTask!.id, 'T03', 'incomplete: deriveState activeTask is T03');
|
|
237
|
-
assert(state.progress
|
|
238
|
-
assertEq(state.progress
|
|
239
|
-
assertEq(state.progress
|
|
240
|
-
assert(state.progress
|
|
237
|
+
assert(state.progress!.slices !== undefined, 'incomplete: deriveState has slices progress');
|
|
238
|
+
assertEq(state.progress!.slices!.done, 1, 'incomplete: deriveState slices done count');
|
|
239
|
+
assertEq(state.progress!.slices!.total, 2, 'incomplete: deriveState slices total count');
|
|
240
|
+
assert(state.progress!.tasks !== undefined, 'incomplete: deriveState has tasks progress');
|
|
241
241
|
// S02 has 1 task, 0 done (only active slice tasks counted)
|
|
242
|
-
assertEq(state.progress
|
|
243
|
-
assertEq(state.progress
|
|
242
|
+
assertEq(state.progress!.tasks!.done, 0, 'incomplete: deriveState tasks done (in active slice)');
|
|
243
|
+
assertEq(state.progress!.tasks!.total, 1, 'incomplete: deriveState tasks total (in active slice)');
|
|
244
244
|
// Requirements
|
|
245
|
-
assertEq(state.requirements
|
|
246
|
-
assertEq(state.requirements
|
|
247
|
-
assertEq(state.requirements
|
|
248
|
-
assertEq(state.requirements
|
|
245
|
+
assertEq(state.requirements!.active, 1, 'incomplete: deriveState requirements active');
|
|
246
|
+
assertEq(state.requirements!.validated, 1, 'incomplete: deriveState requirements validated');
|
|
247
|
+
assertEq(state.requirements!.deferred, 1, 'incomplete: deriveState requirements deferred');
|
|
248
|
+
assertEq(state.requirements!.outOfScope, 1, 'incomplete: deriveState requirements outOfScope');
|
|
249
249
|
|
|
250
250
|
// (f) generatePreview
|
|
251
251
|
console.log(' --- generatePreview ---');
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Tests for nextMilestoneId and maxMilestoneNum — milestone ID generation
|
|
2
|
+
// using max-based approach to avoid collisions after deletions.
|
|
3
|
+
//
|
|
4
|
+
// Sections:
|
|
5
|
+
// (a) Empty array returns M001
|
|
6
|
+
// (b) Sequential IDs return next in sequence
|
|
7
|
+
// (c) IDs with gaps (deletion) use max, not fill
|
|
8
|
+
// (d) Non-numeric directory names mixed in are ignored
|
|
9
|
+
|
|
10
|
+
import { nextMilestoneId, maxMilestoneNum } from '../guided-flow.ts';
|
|
11
|
+
|
|
12
|
+
// ─── Assertion helpers ─────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
let passed = 0;
|
|
15
|
+
let failed = 0;
|
|
16
|
+
|
|
17
|
+
function assertEq<T>(actual: T, expected: T, message: string): void {
|
|
18
|
+
if (JSON.stringify(actual) === JSON.stringify(expected)) {
|
|
19
|
+
passed++;
|
|
20
|
+
} else {
|
|
21
|
+
failed++;
|
|
22
|
+
console.error(` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ─── Tests ─────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
async function main(): Promise<void> {
|
|
29
|
+
console.log('nextMilestoneId / maxMilestoneNum tests');
|
|
30
|
+
|
|
31
|
+
// (a) Empty array → M001
|
|
32
|
+
{
|
|
33
|
+
assertEq(maxMilestoneNum([]), 0, 'maxMilestoneNum([]) === 0');
|
|
34
|
+
assertEq(nextMilestoneId([]), 'M001', 'nextMilestoneId([]) === "M001"');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// (b) Sequential IDs → next in sequence
|
|
38
|
+
{
|
|
39
|
+
assertEq(
|
|
40
|
+
nextMilestoneId(['M001', 'M002', 'M003']),
|
|
41
|
+
'M004',
|
|
42
|
+
'sequential IDs return M004',
|
|
43
|
+
);
|
|
44
|
+
assertEq(maxMilestoneNum(['M001', 'M002', 'M003']), 3, 'max of sequential is 3');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// (c) IDs with gaps (deletion scenario) → uses max, not fill
|
|
48
|
+
{
|
|
49
|
+
assertEq(
|
|
50
|
+
nextMilestoneId(['M001', 'M003']),
|
|
51
|
+
'M004',
|
|
52
|
+
'gap scenario returns M004, not M002',
|
|
53
|
+
);
|
|
54
|
+
assertEq(maxMilestoneNum(['M001', 'M003']), 3, 'max with gap is 3');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// (d) Non-numeric directory names mixed in are ignored
|
|
58
|
+
{
|
|
59
|
+
assertEq(
|
|
60
|
+
nextMilestoneId(['M001', 'notes', '.DS_Store', 'M003']),
|
|
61
|
+
'M004',
|
|
62
|
+
'non-numeric names ignored, returns M004',
|
|
63
|
+
);
|
|
64
|
+
assertEq(
|
|
65
|
+
maxMilestoneNum(['M001', 'notes', '.DS_Store', 'M003']),
|
|
66
|
+
3,
|
|
67
|
+
'max ignores non-numeric entries',
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
72
|
+
// Results
|
|
73
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
74
|
+
|
|
75
|
+
console.log(`\n${'='.repeat(40)}`);
|
|
76
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
77
|
+
if (failed > 0) {
|
|
78
|
+
process.exit(1);
|
|
79
|
+
} else {
|
|
80
|
+
console.log('All tests passed');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
main().catch((error) => {
|
|
85
|
+
console.error(error);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
});
|