@planu/cli 3.9.13 → 4.0.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 (72) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/cli/commands/spec.js +20 -1
  3. package/dist/cli/commands/status.js +18 -1
  4. package/dist/config/license-plans.json +1 -0
  5. package/dist/engine/ai-integration/codex/hooks-generator.js +1 -0
  6. package/dist/engine/ai-integration/gemini/settings-generator.js +4 -1
  7. package/dist/engine/ai-integration/kiro/hooks-generator.js +2 -1
  8. package/dist/engine/autopilot/action-registry.js +5 -14
  9. package/dist/engine/autopilot/state-updater.js +13 -10
  10. package/dist/engine/cascade-hooks/hooks/git-auto-stage.hook.js +3 -0
  11. package/dist/engine/cascade-hooks/hooks/html-regen.hook.js +1 -1
  12. package/dist/engine/cascade-hooks/hooks/status-json.hook.js +1 -1
  13. package/dist/engine/cascade-hooks/state-drift-detector.d.ts +1 -1
  14. package/dist/engine/cascade-hooks/state-drift-detector.js +15 -12
  15. package/dist/engine/git/planu-autocommit.d.ts +1 -0
  16. package/dist/engine/git/planu-autocommit.js +6 -0
  17. package/dist/engine/git-hook-injector.js +3 -3
  18. package/dist/engine/handoff-artifacts/io.js +3 -2
  19. package/dist/engine/handoff-packager.js +2 -1
  20. package/dist/engine/hooks/full-spectrum-generator.d.ts +2 -1
  21. package/dist/engine/hooks/full-spectrum-generator.js +5 -3
  22. package/dist/engine/release/postmortem-generator.d.ts +1 -1
  23. package/dist/engine/release/postmortem-generator.js +3 -2
  24. package/dist/engine/safety/cross-process-lock.js +2 -2
  25. package/dist/engine/session/checkpoint-writer.js +0 -1
  26. package/dist/engine/session-context-generator.js +4 -1
  27. package/dist/engine/spec-audit/index.js +2 -2
  28. package/dist/engine/spec-audit/report-writer.d.ts +1 -1
  29. package/dist/engine/spec-audit/report-writer.js +5 -4
  30. package/dist/engine/spec-migrator/index.d.ts +1 -0
  31. package/dist/engine/spec-migrator/index.js +1 -0
  32. package/dist/engine/spec-migrator/planu-canonical-policy.d.ts +9 -0
  33. package/dist/engine/spec-migrator/planu-canonical-policy.js +62 -0
  34. package/dist/engine/spec-migrator/planu-root-cleaner.js +18 -94
  35. package/dist/engine/spec-migrator/strict-planu-cleanup.d.ts +6 -0
  36. package/dist/engine/spec-migrator/strict-planu-cleanup.js +199 -0
  37. package/dist/engine/spec-summary-html.d.ts +5 -5
  38. package/dist/engine/spec-summary-html.js +7 -32
  39. package/dist/storage/gaps-log.js +4 -4
  40. package/dist/storage/transition-log.js +3 -2
  41. package/dist/tools/audit-specs-drift.js +3 -3
  42. package/dist/tools/create-spec/post-creation.d.ts +2 -1
  43. package/dist/tools/create-spec/post-creation.js +9 -11
  44. package/dist/tools/create-spec/spec-builder.js +1 -1
  45. package/dist/tools/create-spec.js +42 -18
  46. package/dist/tools/flag-spec-gap.d.ts +1 -1
  47. package/dist/tools/flag-spec-gap.js +1 -1
  48. package/dist/tools/generate-dashboard.js +3 -3
  49. package/dist/tools/housekeeping-sweep.js +16 -0
  50. package/dist/tools/init-project/git-setup.js +11 -2
  51. package/dist/tools/init-project/handler.js +1 -27
  52. package/dist/tools/init-project/migration-runner.js +8 -0
  53. package/dist/tools/license-gate.d.ts +1 -0
  54. package/dist/tools/license-gate.js +5 -1
  55. package/dist/tools/list-specs.js +13 -0
  56. package/dist/tools/register-sdd-tools.d.ts +1 -1
  57. package/dist/tools/register-sdd-tools.js +1 -0
  58. package/dist/tools/register-spec-tools/core-spec-tools.js +16 -0
  59. package/dist/tools/spec-lock-handler.js +1 -1
  60. package/dist/tools/sync-spec-state-handler.js +7 -0
  61. package/dist/tools/tool-registry/group-misc.js +4 -4
  62. package/dist/tools/update-status/batch.d.ts +3 -0
  63. package/dist/tools/update-status/batch.js +96 -0
  64. package/dist/tools/update-status/dod-gates.js +1 -1
  65. package/dist/tools/update-status/file-sync.js +3 -1
  66. package/dist/tools/update-status/index.js +15 -2
  67. package/dist/tools/update-status-actions.js +2 -6
  68. package/dist/tools/validate.js +27 -0
  69. package/dist/tools/workspace-dashboard-handler.js +6 -9
  70. package/dist/types/git.d.ts +1 -1
  71. package/dist/types/spec-format.d.ts +26 -0
  72. package/package.json +7 -7
@@ -162,22 +162,18 @@ export async function runDoneActions(projectId, specId, gitBranch) {
162
162
  }
163
163
  return null;
164
164
  })(),
165
- // SPEC-544: Auto-stage planu/ files, then warn only if non-planu changes remain
166
- // SPEC-575: Auto-commit staged planu/ docs (idempotent, safe-fail)
165
+ // Warn if non-planu changes remain. Planu autocommit/staging is opt-in only.
167
166
  (async () => {
168
167
  try {
169
168
  const { resolveProjectPath, git: gitCmd } = await import('./git/git-helpers.js');
170
169
  const projectPath = await resolveProjectPath(projectId);
171
- try {
170
+ if (process.env.PLANU_ENABLE_AUTOCOMMIT === 'true') {
172
171
  await gitCmd(projectPath, ['add', 'planu/']);
173
172
  const { planuAutoCommit } = await import('../engine/git/planu-autocommit.js');
174
173
  void planuAutoCommit({ projectPath, specId, reason: 'mark-done' }).catch((err) => {
175
174
  console.error('[update-status] planuAutoCommit failed:', err instanceof Error ? err.message : String(err));
176
175
  });
177
176
  }
178
- catch {
179
- // git add failed (nothing to stage, or not a git repo) — continue to status check
180
- }
181
177
  const { stdout } = await gitCmd(projectPath, ['status', '--porcelain']);
182
178
  const nonPlanuLines = stdout
183
179
  .trim()
@@ -68,6 +68,33 @@ export async function handleValidate(args, server) {
68
68
  };
69
69
  }
70
70
  const projectPath = knowledge.projectPath;
71
+ // SPEC-1017: fail closed when planu/ contains non-canonical artifacts.
72
+ try {
73
+ const { runStrictPlanuCleanup, validateStrictPlanuLayout } = await import('../engine/spec-migrator/index.js');
74
+ await runStrictPlanuCleanup(projectPath);
75
+ const layout = await validateStrictPlanuLayout(projectPath);
76
+ if (!layout.ok) {
77
+ return {
78
+ content: [
79
+ {
80
+ type: 'text',
81
+ text: `Strict Planu layout validation failed for ${specId}.\n\n` +
82
+ `Non-canonical paths:\n${layout.offenders.map((p) => `- ${p}`).join('\n')}\n\n` +
83
+ `Canonical contract:\n${layout.contract}`,
84
+ },
85
+ ],
86
+ isError: true,
87
+ structuredContent: {
88
+ error: 'strict_planu_layout_violation',
89
+ offenders: layout.offenders,
90
+ contract: layout.contract,
91
+ },
92
+ };
93
+ }
94
+ }
95
+ catch {
96
+ /* best-effort — implementation validation still runs if layout check is unavailable */
97
+ }
71
98
  // 3. Run validation engine
72
99
  const result = await validateSpec(spec, projectPath);
73
100
  // 4. Generate DoR and DoD checklists
@@ -10,6 +10,7 @@ import { readRecentAutopilotAlerts } from '../storage/autopilot-log-store.js';
10
10
  import { verifyStateFiles } from '../engine/cascade-hooks/state-drift-detector.js';
11
11
  import { readFile } from 'node:fs/promises';
12
12
  import { join } from 'node:path';
13
+ import { getAsyncAnalysisPath } from './create-spec/post-creation.js';
13
14
  export async function handleWorkspaceOverview(args) {
14
15
  const result = await buildWorkspaceOverview(args.healthThreshold);
15
16
  if (result.projectCount === 0) {
@@ -126,10 +127,10 @@ export async function handleWorkspaceSearch(args) {
126
127
  }
127
128
  return { content: [{ type: 'text', text: lines.join('\n') }] };
128
129
  }
129
- /** SPEC-781: Check if a spec has a pending async analysis (no .analysis.json yet). */
130
- async function hasPendingAnalysis(projectPath, specDir) {
130
+ /** SPEC-781: Check if a spec has pending async analysis in external project data. */
131
+ async function hasPendingAnalysis(projectPath, specId) {
131
132
  try {
132
- await readFile(join(projectPath, specDir, '.analysis.json'), 'utf-8');
133
+ await readFile(getAsyncAnalysisPath(projectPath, specId), 'utf-8');
133
134
  return false;
134
135
  }
135
136
  catch {
@@ -144,7 +145,6 @@ async function collectPendingAnalysisAlerts(projectPath) {
144
145
  const results = [];
145
146
  const PENDING_RE = /^pendingAnalysis:\s*true/m;
146
147
  const ID_RE = /^id:\s*(SPEC-\d+)/m;
147
- const SPEC_DIR_RE = /planu\/specs\/([^/]+)\/spec\.md$/;
148
148
  for (const specFile of specFiles) {
149
149
  const content = await readFile(specFile, 'utf-8').catch(() => '');
150
150
  if (!PENDING_RE.test(content)) {
@@ -155,11 +155,8 @@ async function collectPendingAnalysisAlerts(projectPath) {
155
155
  if (specId === undefined) {
156
156
  continue;
157
157
  }
158
- const dirMatch = SPEC_DIR_RE.exec(specFile);
159
- const dirName = dirMatch?.[1];
160
- const specDir = dirName !== undefined ? join('planu/specs', dirName) : null;
161
- if (specDir !== null && !(await hasPendingAnalysis(projectPath, specDir))) {
162
- continue; // .analysis.json exists — analysis done
158
+ if (!(await hasPendingAnalysis(projectPath, specId))) {
159
+ continue;
163
160
  }
164
161
  results.push({ specId, message: `Spec ${specId} analysis still running` });
165
162
  }
@@ -310,7 +310,7 @@ export interface ListSpecPrsInput {
310
310
  projectPath: string;
311
311
  specId?: string;
312
312
  }
313
- export type PlanuAutocommitSkipReason = 'no-staged-changes' | 'mid-merge' | 'detached-head';
313
+ export type PlanuAutocommitSkipReason = 'disabled' | 'no-staged-changes' | 'mid-merge' | 'detached-head';
314
314
  export type PlanuAutocommitReason = 'mark-done' | 'sync-release' | 'status-update' | 'session-checkpoint';
315
315
  export interface PlanuAutocommitResult {
316
316
  committed: boolean;
@@ -1,5 +1,6 @@
1
1
  import type { Spec } from './spec/core.js';
2
2
  import type { Estimation } from './estimation.js';
3
+ import type { SpecStatus } from './common/index.js';
3
4
  /** A criterion in the lean spec frontmatter. */
4
5
  export interface LeanCriterion {
5
6
  text: string;
@@ -42,6 +43,31 @@ export interface CleanupResult {
42
43
  deletedSpecFiles: string[];
43
44
  totalDeleted: number;
44
45
  }
46
+ export interface PlanuCanonicalPathPolicy {
47
+ readonly canonicalRootFiles: readonly string[];
48
+ readonly canonicalRootDirs: readonly string[];
49
+ readonly canonicalSpecFiles: readonly string[];
50
+ readonly generatedRuntimePatterns: readonly string[];
51
+ readonly legacyMergeBeforeDeleteFiles: readonly string[];
52
+ }
53
+ export interface StrictPlanuCleanupResult {
54
+ deleted: string[];
55
+ merged: string[];
56
+ gitignoreUpdated: boolean;
57
+ }
58
+ export interface StrictPlanuValidationResult {
59
+ ok: boolean;
60
+ offenders: string[];
61
+ contract: string;
62
+ }
63
+ export interface UpdateStatusBatchInput {
64
+ specIds: string[];
65
+ status: SpecStatus;
66
+ projectId?: string;
67
+ projectPath?: string;
68
+ dryRun?: boolean;
69
+ reviewNotes?: string;
70
+ }
45
71
  /** Report of detected migration drift (SPEC-715). Read-only — no FS mutations. */
46
72
  export interface MigrationDriftReport {
47
73
  /** Count of specs that still have a stray technical.md (need unified migration). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planu/cli",
3
- "version": "3.9.13",
3
+ "version": "4.0.0",
4
4
  "description": "Planu — MCP Server for Spec Driven Development with native Rust acceleration for hot paths. Cross-platform (Linux/macOS/Windows, x64/arm64, glibc/musl).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,12 +32,12 @@
32
32
  "packageName": "@planu/core"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@planu/core-darwin-arm64": "3.9.13",
36
- "@planu/core-darwin-x64": "3.9.13",
37
- "@planu/core-linux-arm64-gnu": "3.9.13",
38
- "@planu/core-linux-arm64-musl": "3.9.13",
39
- "@planu/core-linux-x64-gnu": "3.9.13",
40
- "@planu/core-linux-x64-musl": "3.9.13"
35
+ "@planu/core-darwin-arm64": "3.9.14",
36
+ "@planu/core-darwin-x64": "3.9.14",
37
+ "@planu/core-linux-arm64-gnu": "3.9.14",
38
+ "@planu/core-linux-arm64-musl": "3.9.14",
39
+ "@planu/core-linux-x64-gnu": "3.9.14",
40
+ "@planu/core-linux-x64-musl": "3.9.14"
41
41
  },
42
42
  "engines": {
43
43
  "node": ">=24.0.0"