cleargate 0.12.0 → 0.13.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.
Files changed (40) hide show
  1. package/dist/MANIFEST.json +13 -13
  2. package/dist/{chunk-HZPJ5QX4.js → chunk-EG6YGT2O.js} +315 -33
  3. package/dist/chunk-EG6YGT2O.js.map +1 -0
  4. package/dist/cli.cjs +612 -289
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/cli.js +73 -37
  7. package/dist/cli.js.map +1 -1
  8. package/dist/lib/lifecycle-reconcile.cjs +318 -34
  9. package/dist/lib/lifecycle-reconcile.cjs.map +1 -1
  10. package/dist/lib/lifecycle-reconcile.d.cts +55 -4
  11. package/dist/lib/lifecycle-reconcile.d.ts +55 -4
  12. package/dist/lib/lifecycle-reconcile.js +7 -3
  13. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +1 -1
  14. package/dist/templates/cleargate-planning/.claude/agents/developer.md +8 -4
  15. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +2 -0
  16. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +73 -0
  17. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +1 -1
  18. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +1 -1
  19. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +1 -1
  20. package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +1 -1
  21. package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +1 -1
  22. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +1 -1
  23. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +1 -1
  24. package/dist/templates/cleargate-planning/CLAUDE.md +2 -0
  25. package/dist/templates/cleargate-planning/MANIFEST.json +13 -13
  26. package/package.json +8 -9
  27. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +1 -1
  28. package/templates/cleargate-planning/.claude/agents/developer.md +8 -4
  29. package/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +2 -0
  30. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +73 -0
  31. package/templates/cleargate-planning/.cleargate/templates/Bug.md +1 -1
  32. package/templates/cleargate-planning/.cleargate/templates/CR.md +1 -1
  33. package/templates/cleargate-planning/.cleargate/templates/epic.md +1 -1
  34. package/templates/cleargate-planning/.cleargate/templates/hotfix.md +1 -1
  35. package/templates/cleargate-planning/.cleargate/templates/initiative.md +1 -1
  36. package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +1 -1
  37. package/templates/cleargate-planning/.cleargate/templates/story.md +1 -1
  38. package/templates/cleargate-planning/CLAUDE.md +2 -0
  39. package/templates/cleargate-planning/MANIFEST.json +13 -13
  40. package/dist/chunk-HZPJ5QX4.js.map +0 -1
package/dist/cli.js CHANGED
@@ -3,8 +3,9 @@ import {
3
3
  checkVerbMismatch,
4
4
  parseFrontmatter,
5
5
  reconcileDecomposition,
6
- reconcileLifecycle
7
- } from "./chunk-HZPJ5QX4.js";
6
+ reconcileLifecycle,
7
+ walkActiveParents
8
+ } from "./chunk-EG6YGT2O.js";
8
9
  import {
9
10
  AcquireError,
10
11
  acquireAccessToken,
@@ -22,7 +23,7 @@ import { Command } from "commander";
22
23
  // package.json
23
24
  var package_default = {
24
25
  name: "cleargate",
25
- version: "0.12.0",
26
+ version: "0.13.0",
26
27
  private: false,
27
28
  type: "module",
28
29
  description: "Planning framework for Claude Code agents \u2014 sprint/epic/story protocol, five-role agent team (architect/developer/qa/devops/reporter), Karpathy-style awareness wiki.",
@@ -69,12 +70,11 @@ var package_default = {
69
70
  build: "tsup",
70
71
  dev: "tsup --watch",
71
72
  typecheck: "tsc --noEmit",
72
- test: "tsx --test --test-reporter=spec 'test/**/*.node.test.ts'",
73
- "test:file": "tsx --test --test-reporter=spec",
74
- "test:vitest": "npm run build && vitest run",
75
- "test:vitest:watch": "vitest",
76
- "test:node": "tsx --test --test-reporter=spec 'test/**/*.node.test.ts'",
77
- "test:node:file": "tsx --test --test-reporter=spec"
73
+ test: "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec 'test/**/*.node.test.ts' '!test/fixtures/**'",
74
+ "test:file": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec",
75
+ "test:node": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec 'test/**/*.node.test.ts' '!test/fixtures/**'",
76
+ "test:node:file": "tsx --test --test-concurrency=1 --experimental-test-module-mocks --test-reporter=spec",
77
+ "check:no-vitest": `node -e "const r=require('child_process').execSync('grep -rE \\"\\\\b(vitest|vi\\\\.fn|vi\\\\.mock|vi\\\\.spyOn|vi\\\\.stubGlobal|vi\\\\.useFakeTimers|vi\\\\.useRealTimers|vi\\\\.advanceTimersByTime|vi\\\\.hoisted)\\\\b\\" --include=\\"*.ts\\" --include=\\"*.js\\" --include=\\"*.mjs\\" --exclude-dir=test/fixtures . 2>/dev/null || true', {encoding:'utf8'}); if(r.trim()){console.error('vitest residue detected:\\\\n'+r); process.exit(1)} console.log('no vitest residue')"`
78
78
  },
79
79
  dependencies: {
80
80
  "@napi-rs/keyring": "^1.2.0",
@@ -89,10 +89,10 @@ var package_default = {
89
89
  "@types/js-yaml": "^4.0.9",
90
90
  "@types/node": "^24.0.0",
91
91
  "@types/pg": "^8.11.10",
92
+ "ts-morph": "28.0.0",
92
93
  tsup: "^8",
93
94
  tsx: "^4.21.0",
94
- typescript: "^5.8.0",
95
- vitest: "^2.1.0"
95
+ typescript: "^5.8.0"
96
96
  }
97
97
  };
98
98
 
@@ -6463,22 +6463,50 @@ function reconcileLifecycleCliHandler(opts, cli) {
6463
6463
  });
6464
6464
  if (result.drift.length === 0) {
6465
6465
  stdoutFn(`lifecycle: clean (${result.clean} artifacts reconciled)`);
6466
- return exitFn(0);
6467
- }
6468
- stderrFn(`lifecycle: DRIFT detected (${result.drift.length} unreconciled artifacts):`);
6469
- for (const item of result.drift) {
6470
- stderrFn(
6471
- ` DRIFT: ${item.id} status=${item.actual_status ?? "missing"} in ${item.in_archive ? "archive" : "pending-sync"}, expected ${item.expected_status} (commit ${item.commit_shas[0] ?? "unknown"})`
6472
- );
6473
- stderrFn(
6474
- ` Remediation: git mv .cleargate/delivery/pending-sync/${item.file_path?.replace("pending-sync/", "") ?? item.id + "_*.md"} .cleargate/delivery/archive/ && update status: ${item.expected_status}`
6475
- );
6466
+ } else {
6467
+ stderrFn(`lifecycle: DRIFT detected (${result.drift.length} unreconciled artifacts):`);
6468
+ for (const item of result.drift) {
6469
+ stderrFn(
6470
+ ` DRIFT: ${item.id} status=${item.actual_status ?? "missing"} in ${item.in_archive ? "archive" : "pending-sync"}, expected ${item.expected_status} (commit ${item.commit_shas[0] ?? "unknown"})`
6471
+ );
6472
+ stderrFn(
6473
+ ` Remediation: git mv .cleargate/delivery/pending-sync/${item.file_path?.replace("pending-sync/", "") ?? item.id + "_*.md"} .cleargate/delivery/archive/ && update status: ${item.expected_status}`
6474
+ );
6475
+ }
6476
+ if (!opts.parents) {
6477
+ return exitFn(1);
6478
+ }
6476
6479
  }
6477
- return exitFn(1);
6478
6480
  } catch (err) {
6479
6481
  stderrFn(`lifecycle reconciliation error: ${err instanceof Error ? err.message : String(err)}`);
6480
- return exitFn(1);
6482
+ if (!opts.parents) {
6483
+ return exitFn(1);
6484
+ }
6485
+ }
6486
+ if (opts.parents) {
6487
+ const archiveRoot = path31.join(deliveryRoot, "archive");
6488
+ walkActiveParents({ deliveryRoot, archiveRoot }).then((results) => {
6489
+ stdoutFn("Parent rollup audit (--parents):");
6490
+ for (const r of results) {
6491
+ if (r.verdict === "auto-flip") {
6492
+ stdoutFn(
6493
+ ` ${r.parent_id} \u2713 proposed: Completed (${r.terminal_children.length}/${r.terminal_children.length} children Completed)`
6494
+ );
6495
+ } else if (r.verdict === "halt-partial" || r.verdict === "halt-zero-children") {
6496
+ stdoutFn(` ${r.parent_id} \u2717 ${r.verdict}: ${r.halt_reason ?? "no details"}`);
6497
+ } else if (r.verdict === "no-op") {
6498
+ } else {
6499
+ stdoutFn(` ${r.parent_id} ~ ${r.verdict}`);
6500
+ }
6501
+ }
6502
+ exitFn(0);
6503
+ }).catch((err) => {
6504
+ stderrFn(`--parents audit error: ${err instanceof Error ? err.message : String(err)}`);
6505
+ exitFn(0);
6506
+ });
6507
+ return;
6481
6508
  }
6509
+ return exitFn(0);
6482
6510
  }
6483
6511
  function parseFileFrontmatter(raw) {
6484
6512
  const lines = raw.split("\n");
@@ -10346,6 +10374,7 @@ function getItemId3(fm) {
10346
10374
  }
10347
10375
 
10348
10376
  // src/commands/push.ts
10377
+ import * as fs40 from "fs";
10349
10378
  import * as fsPromises11 from "fs/promises";
10350
10379
  import * as path49 from "path";
10351
10380
  async function pushHandler(fileOrId, opts = {}) {
@@ -10355,6 +10384,13 @@ async function pushHandler(fileOrId, opts = {}) {
10355
10384
  const stderr = opts.stderr ?? ((s) => process.stderr.write(s));
10356
10385
  const exit = opts.exit ?? ((c) => process.exit(c));
10357
10386
  const nowFn = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
10387
+ const migrationLockPath = path49.join(projectRoot, ".cleargate", ".migration-lock");
10388
+ if (fs40.existsSync(migrationLockPath)) {
10389
+ stderr(`Error: CR-067 migration in progress (.migration-lock held); retry in 30s
10390
+ `);
10391
+ exit(75);
10392
+ return;
10393
+ }
10358
10394
  const identity = resolveIdentity(projectRoot);
10359
10395
  const sprintRoot = resolveActiveSprintDir(projectRoot);
10360
10396
  async function resolveMcp() {
@@ -10750,7 +10786,7 @@ function formatEntry(entry) {
10750
10786
  }
10751
10787
 
10752
10788
  // src/commands/admin-login.ts
10753
- import * as fs40 from "fs";
10789
+ import * as fs41 from "fs";
10754
10790
  import * as path51 from "path";
10755
10791
  import * as os7 from "os";
10756
10792
  var DEFAULT_MCP_URL = "http://localhost:3000";
@@ -10764,10 +10800,10 @@ function resolveAuthFilePath(opts) {
10764
10800
  }
10765
10801
  function writeAdminAuth(filePath, token) {
10766
10802
  const dir = path51.dirname(filePath);
10767
- fs40.mkdirSync(dir, { recursive: true });
10803
+ fs41.mkdirSync(dir, { recursive: true });
10768
10804
  const payload = JSON.stringify({ version: 1, token }, null, 2);
10769
- fs40.writeFileSync(filePath, payload, { encoding: "utf8", mode: 384 });
10770
- fs40.chmodSync(filePath, 384);
10805
+ fs41.writeFileSync(filePath, payload, { encoding: "utf8", mode: 384 });
10806
+ fs41.chmodSync(filePath, 384);
10771
10807
  }
10772
10808
  async function adminLoginHandler(opts = {}) {
10773
10809
  const fetchFn = opts.fetch ?? globalThis.fetch;
@@ -10876,7 +10912,7 @@ async function adminLoginHandler(opts = {}) {
10876
10912
  }
10877
10913
 
10878
10914
  // src/commands/hotfix.ts
10879
- import * as fs41 from "fs";
10915
+ import * as fs42 from "fs";
10880
10916
  import * as path52 from "path";
10881
10917
  function defaultExit4(code) {
10882
10918
  return process.exit(code);
@@ -10887,7 +10923,7 @@ function maxHotfixId(pendingDir) {
10887
10923
  let max = 0;
10888
10924
  let entries;
10889
10925
  try {
10890
- entries = fs41.readdirSync(pendingDir);
10926
+ entries = fs42.readdirSync(pendingDir);
10891
10927
  } catch {
10892
10928
  return 0;
10893
10929
  }
@@ -10907,7 +10943,7 @@ function countActiveHotfixes(repoRoot) {
10907
10943
  let count = 0;
10908
10944
  let pendingEntries = [];
10909
10945
  try {
10910
- pendingEntries = fs41.readdirSync(pendingDir);
10946
+ pendingEntries = fs42.readdirSync(pendingDir);
10911
10947
  } catch {
10912
10948
  }
10913
10949
  for (const entry of pendingEntries) {
@@ -10915,13 +10951,13 @@ function countActiveHotfixes(repoRoot) {
10915
10951
  }
10916
10952
  let archiveEntries = [];
10917
10953
  try {
10918
- archiveEntries = fs41.readdirSync(archiveDir);
10954
+ archiveEntries = fs42.readdirSync(archiveDir);
10919
10955
  } catch {
10920
10956
  }
10921
10957
  for (const entry of archiveEntries) {
10922
10958
  if (entry.startsWith("HOTFIX-") && entry.endsWith(".md")) {
10923
10959
  try {
10924
- const stat = fs41.statSync(path52.join(archiveDir, entry));
10960
+ const stat = fs42.statSync(path52.join(archiveDir, entry));
10925
10961
  if (stat.mtimeMs >= sevenDaysAgo) count++;
10926
10962
  } catch {
10927
10963
  }
@@ -10956,7 +10992,7 @@ function hotfixNewHandler(opts, cli) {
10956
10992
  const templatePath = resolveTemplatePath(repoRoot);
10957
10993
  let templateContent;
10958
10994
  try {
10959
- templateContent = fs41.readFileSync(templatePath, "utf8");
10995
+ templateContent = fs42.readFileSync(templatePath, "utf8");
10960
10996
  } catch {
10961
10997
  stderrFn(`[cleargate hotfix new] template not found: ${templatePath}`);
10962
10998
  return exitFn(2);
@@ -10966,8 +11002,8 @@ function hotfixNewHandler(opts, cli) {
10966
11002
  const fileName = `${idStr}_${fileSlug}.md`;
10967
11003
  const outPath = path52.join(pendingDir, fileName);
10968
11004
  try {
10969
- fs41.mkdirSync(pendingDir, { recursive: true });
10970
- fs41.writeFileSync(outPath, content, "utf8");
11005
+ fs42.mkdirSync(pendingDir, { recursive: true });
11006
+ fs42.writeFileSync(outPath, content, "utf8");
10971
11007
  } catch (err) {
10972
11008
  const msg = err instanceof Error ? err.message : String(err);
10973
11009
  stderrFn(`[cleargate hotfix new] write failed: ${msg}`);
@@ -11394,8 +11430,8 @@ sprint.command("close <sprint-id>").description("close a sprint \u2014 validates
11394
11430
  }
11395
11431
  sprintCloseHandler(handlerOpts);
11396
11432
  });
11397
- sprint.command("reconcile-lifecycle <sprint-id>").description("CR-017: check lifecycle status of artifacts referenced in this sprint's commits (exits 1 on drift)").option("--since <iso-date>", "start of git log range (default: sprint start_date or 90 days ago)").option("--until <iso-date>", "end of git log range (default: now)").action((sprintId, opts) => {
11398
- reconcileLifecycleCliHandler({ sprintId, since: opts.since, until: opts.until });
11433
+ sprint.command("reconcile-lifecycle <sprint-id>").description("CR-017: check lifecycle status of artifacts referenced in this sprint's commits (exits 1 on drift)").option("--since <iso-date>", "start of git log range (default: sprint start_date or 90 days ago)").option("--until <iso-date>", "end of git log range (default: now)").option("--parents", "audit parent (Epic/Sprint) rollup statuses; read-only (CR-066)").action((sprintId, opts) => {
11434
+ reconcileLifecycleCliHandler({ sprintId, since: opts.since, until: opts.until, parents: opts.parents });
11399
11435
  });
11400
11436
  sprint.command("archive <sprint-id>").description("archive a completed sprint \u2014 move pending-sync files, clear .active, merge + delete sprint branch").option("--dry-run", "print the archive plan without making any changes").option("--allow-wiki-lint-debt", "CR-022 M5: waive wiki-lint findings during archive (mirrors --allow-drift pattern)").action(async (sprintId, opts) => {
11401
11437
  const handlerOpts = { sprintId };