@planu/cli 5.3.14 → 5.3.15

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 (48) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/engine/cascade-hooks/hooks/housekeeping-on-done.hook.js +2 -3
  3. package/dist/engine/housekeeping/find-stale-branches.d.ts +3 -3
  4. package/dist/engine/housekeeping/find-stale-branches.js +25 -29
  5. package/dist/engine/housekeeping/sweep-runner.js +105 -19
  6. package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
  7. package/dist/engine/planu-core.darwin-arm64.node.sbom.json +4 -4
  8. package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
  9. package/dist/engine/planu-core.darwin-x64.node.sbom.json +4 -4
  10. package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
  11. package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +4 -4
  12. package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
  13. package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +4 -4
  14. package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
  15. package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +4 -4
  16. package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
  17. package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +4 -4
  18. package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
  19. package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +4 -4
  20. package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
  21. package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +4 -4
  22. package/dist/engine/spec-migrator/planu-root-cleaner.d.ts +2 -2
  23. package/dist/engine/spec-migrator/planu-root-cleaner.js +3 -2
  24. package/dist/engine/spec-migrator/strict-planu-cleanup.d.ts +2 -2
  25. package/dist/engine/spec-migrator/strict-planu-cleanup.js +18 -2
  26. package/dist/tools/git/cleanup-ops.d.ts +2 -2
  27. package/dist/tools/git/cleanup-ops.js +123 -58
  28. package/dist/tools/github-release-handler.js +3 -12
  29. package/dist/tools/housekeeping-sweep.d.ts +1 -0
  30. package/dist/tools/housekeeping-sweep.js +17 -10
  31. package/dist/tools/init-project/git-setup.d.ts +1 -0
  32. package/dist/tools/init-project/git-setup.js +7 -23
  33. package/dist/tools/init-project/handler.js +4 -1
  34. package/dist/tools/init-project/migration-runner.js +8 -1
  35. package/dist/tools/init-project/scaffold-writer.d.ts +1 -0
  36. package/dist/tools/init-project/scaffold-writer.js +1 -0
  37. package/dist/tools/update-status/batch.js +1 -1
  38. package/dist/tools/update-status/index.js +1 -0
  39. package/dist/tools/update-status-actions.d.ts +2 -2
  40. package/dist/tools/update-status-actions.js +20 -10
  41. package/dist/types/git.d.ts +19 -0
  42. package/dist/types/housekeeping.d.ts +9 -1
  43. package/dist/types/index.d.ts +1 -0
  44. package/dist/types/index.js +1 -0
  45. package/dist/types/spec-format.d.ts +3 -0
  46. package/package.json +11 -10
  47. package/planu-native.json +1 -1
  48. package/planu-plugin.json +1 -1
@@ -59,35 +59,19 @@ export async function runGitSetup(projectPath, projectId) {
59
59
  // Auto-configure .gitignore (best-effort)
60
60
  const gitignoreUpdated = await configureGitignore(projectPath);
61
61
  // SPEC-646: Remove planu/*.html from git tracking — they are regenerable, not source files
62
- if (gitRepoDetected) {
63
- await untrackHtmlFiles(projectPath);
64
- }
65
- return { hooksInstalled, gitFlowType, gitignoreUpdated, gitRepoDetected };
62
+ const layoutOffenders = gitRepoDetected ? await untrackHtmlFiles(projectPath) : [];
63
+ return { hooksInstalled, gitFlowType, gitignoreUpdated, gitRepoDetected, layoutOffenders };
66
64
  }
67
65
  /** SPEC-646: Delete legacy planu/*.html files from disk and remove from git index.
68
66
  * These files are regenerable — committing them wastes ≥1MB per repo clone. */
69
67
  async function untrackHtmlFiles(projectPath) {
70
68
  const htmlFiles = ['planu/index.html', 'planu/roadmap.html'];
71
- // Remove from git index if tracked (best-effort)
72
69
  try {
73
70
  const { stdout } = await git(projectPath, ['ls-files', ...htmlFiles]);
74
- const tracked = stdout.trim().split('\n').filter(Boolean);
75
- if (tracked.length > 0) {
76
- await git(projectPath, ['rm', '--force', ...tracked]);
77
- }
71
+ return stdout.trim().split('\n').filter(Boolean);
78
72
  }
79
73
  catch {
80
- /* not a git repo or files not tracked */
81
- }
82
- // Also delete any remaining files from disk (e.g. not tracked but still present)
83
- for (const rel of htmlFiles) {
84
- try {
85
- const { unlink } = await import('node:fs/promises');
86
- await unlink(join(projectPath, rel));
87
- }
88
- catch {
89
- /* file doesn't exist or can't be deleted — skip */
90
- }
74
+ return [];
91
75
  }
92
76
  }
93
77
  /** SPEC-466: Also exported for list_specs auto-cleanup. */
@@ -95,11 +79,11 @@ export async function configureGitignoreForPlanu(projectPath) {
95
79
  return configureGitignore(projectPath);
96
80
  }
97
81
  async function configureGitignore(projectPath) {
98
- const gitignorePath = join(projectPath, '.gitignore');
82
+ const targetPath = join(projectPath, '.gitignore');
99
83
  try {
100
84
  let gitignoreContent = '';
101
85
  try {
102
- gitignoreContent = await readFile(gitignorePath, 'utf-8');
86
+ gitignoreContent = await readFile(targetPath, 'utf-8');
103
87
  }
104
88
  catch {
105
89
  /* file doesn't exist */
@@ -145,7 +129,7 @@ async function configureGitignore(projectPath) {
145
129
  const addition = linesToAdd.length > 0
146
130
  ? `${separator}# Planu (auto-configured)\n${linesToAdd.join('\n')}\n`
147
131
  : '';
148
- await writeFile(gitignorePath, gitignoreContent + addition, 'utf-8');
132
+ await writeFile(targetPath, gitignoreContent + addition, 'utf-8');
149
133
  updated = true;
150
134
  }
151
135
  return updated;
@@ -428,7 +428,7 @@ export async function handleInitProject(params, server) {
428
428
  const { skillsAutoInstalled, skillsPendingInstall, skillsSkipped } = await orchestrateSkillInstalls(recommendedSkills, projectPath, autoInstallFromConfig);
429
429
  // Write scaffold files: rules, git setup, constitution, CLAUDE.md, lint, architecture rules
430
430
  const scaffoldResult = await runScaffoldWriter(projectPath, projectId, knowledge, recommendedSkills, params.permissionsMode, params.pluginsMode, autoInstallFromConfig);
431
- const { platform, generatedRules, rulesWritten, additionalFilesWritten, hooksInstalled, gitFlowType, gitignoreUpdated, gitRepoDetected, constitutionInitialized, claudeMdUpdated, eslintCreated, prettierCreated, lintSuggestions, architectureRulesWritten, architectureRulesSkipped, planuWorkflowInjected, planuHooksConfigured, planuRulesWritten, gitAutoStageInjected, } = scaffoldResult;
431
+ const { platform, generatedRules, rulesWritten, additionalFilesWritten, hooksInstalled, gitFlowType, gitignoreUpdated, gitRepoDetected, layoutOffenders, constitutionInitialized, claudeMdUpdated, eslintCreated, prettierCreated, lintSuggestions, architectureRulesWritten, architectureRulesSkipped, planuWorkflowInjected, planuHooksConfigured, planuRulesWritten, gitAutoStageInjected, } = scaffoldResult;
432
432
  // SPEC-444: Inject proactive behavior rules into project CLAUDE.md
433
433
  const proactiveRulesInjected = await injectProactiveRules(join(projectPath, 'CLAUDE.md'), '1.22.0')
434
434
  .then(() => true)
@@ -545,6 +545,9 @@ export async function handleInitProject(params, server) {
545
545
  if (gitignoreUpdated) {
546
546
  collector.pushOk('gitignore', 'Updated .gitignore with planu/ and project rules');
547
547
  }
548
+ if (layoutOffenders.length > 0) {
549
+ collector.pushOk('planu-layout-offenders', layoutOffenders.join(', '));
550
+ }
548
551
  if (skillsAutoInstalled.length > 0) {
549
552
  collector.pushOk('skills-installed', `Auto-installed skills: ${skillsAutoInstalled.join(', ')}`);
550
553
  }
@@ -166,7 +166,14 @@ export async function runSpecMigrations(projectPath, projectId, knowledge, optio
166
166
  // SPEC-1017: strict managed planu/ cleanup after all legacy migrations.
167
167
  try {
168
168
  const { runStrictPlanuCleanup } = await import('../../engine/spec-migrator/index.js');
169
- await runStrictPlanuCleanup(projectPath);
169
+ const cleanup = await runStrictPlanuCleanup(projectPath);
170
+ for (const path of cleanup.proposed) {
171
+ criticalMigrationFailures.push({
172
+ phase: 'strict-planu-validate',
173
+ severity: 'critical',
174
+ message: `Non-canonical planu path requires manual review: ${path}`,
175
+ });
176
+ }
170
177
  }
171
178
  catch (err) {
172
179
  criticalMigrationFailures.push(issueFromError('strict-planu-cleanup', 'critical', err));
@@ -11,6 +11,7 @@ export interface ScaffoldWriteResult {
11
11
  gitFlowType: string;
12
12
  gitignoreUpdated: boolean;
13
13
  gitRepoDetected: boolean;
14
+ layoutOffenders: string[];
14
15
  constitutionInitialized: boolean;
15
16
  claudeMdUpdated: boolean;
16
17
  eslintCreated: boolean;
@@ -383,6 +383,7 @@ export async function runScaffoldWriter(projectPath, projectId, knowledge, recom
383
383
  gitFlowType: gitResult.gitFlowType,
384
384
  gitignoreUpdated: gitResult.gitignoreUpdated,
385
385
  gitRepoDetected: gitResult.gitRepoDetected,
386
+ layoutOffenders: gitResult.layoutOffenders,
386
387
  constitutionInitialized,
387
388
  claudeMdUpdated,
388
389
  eslintCreated,
@@ -150,7 +150,7 @@ export async function handleUpdateStatusBatch(input) {
150
150
  updated,
151
151
  skipped,
152
152
  failed,
153
- sideEffectsFlushed: ['strict-planu-cleanup'],
153
+ sideEffectsFlushed: ['strict-planu-validate'],
154
154
  ...(aggregatedNextAction ? { nextAction: aggregatedNextAction } : {}),
155
155
  },
156
156
  };
@@ -1446,6 +1446,7 @@ export async function handleUpdateStatus(params, server) {
1446
1446
  if (doneActions?.prSuggestion) {
1447
1447
  collector.pushOk('pr-created', doneActions.prSuggestion.title);
1448
1448
  }
1449
+ collector.pushOk('housekeeping', 'Housekeeping: pending cleanup proposals — run housekeeping_sweep to review');
1449
1450
  }
1450
1451
  if (newStatus === 'approved' && versionSnapshotTag) {
1451
1452
  collector.pushOk('version-snapshot', `Snapshot queued: ${versionSnapshotTag}`);
@@ -1,4 +1,4 @@
1
- import type { ConstitutionViolation } from '../types/index.js';
1
+ import type { ConstitutionViolation, DoneSideEffectsReport } from '../types/index.js';
2
2
  export declare function runImplementingActions(projectId: string, specId: string, options?: {
3
3
  deferSideEffects?: boolean;
4
4
  projectPath?: string;
@@ -24,7 +24,7 @@ export declare function runDoneActions(projectId: string, specId: string, gitBra
24
24
  autopilotSummary: string[];
25
25
  }>;
26
26
  /** Run cleanup and state refreshes only after the done transition is durable. */
27
- export declare function runDoneSideEffects(projectId: string, specId: string, gitBranch: string | undefined, transitionProjectPath?: string): Promise<void>;
27
+ export declare function runDoneSideEffects(projectId: string, specId: string, gitBranch: string | undefined, transitionProjectPath?: string): Promise<DoneSideEffectsReport>;
28
28
  /**
29
29
  * Best-effort constitution compliance check for status transitions.
30
30
  * Returns warnings (never blocks the transition).
@@ -241,7 +241,7 @@ export async function runDoneSideEffects(projectId, specId, gitBranch, transitio
241
241
  const results = await Promise.allSettled([
242
242
  (async () => {
243
243
  const { cleanupSpecOnDone } = await import('./git/cleanup-ops.js');
244
- await withAudit(projectPath, 'update_status(done)', 'cleanupSpecOnDone', () => cleanupSpecOnDone(projectPath, specId, gitBranch));
244
+ return withAudit(projectPath, 'update_status(done)', 'cleanupSpecOnDone', () => cleanupSpecOnDone(projectPath, specId, gitBranch));
245
245
  })(),
246
246
  (async () => {
247
247
  const { removeSpecFromSession } = await import('../engine/session-state/writer.js');
@@ -259,11 +259,11 @@ export async function runDoneSideEffects(projectId, specId, gitBranch, transitio
259
259
  (async () => {
260
260
  const { join } = await import('node:path');
261
261
  const { cleanPlanuRoot } = await import('../engine/spec-migrator/planu-root-cleaner.js');
262
- await cleanPlanuRoot(join(projectPath, 'planu'));
262
+ return cleanPlanuRoot(join(projectPath, 'planu'));
263
263
  })(),
264
264
  (async () => {
265
265
  const { runHousekeepingSweep } = await import('../engine/housekeeping/index.js');
266
- await runHousekeepingSweep({ projectPath, dryRun: false });
266
+ return runHousekeepingSweep({ projectPath });
267
267
  })(),
268
268
  (async () => {
269
269
  if (hasPending(specId, 'generateSessionContext')) {
@@ -278,18 +278,28 @@ export async function runDoneSideEffects(projectId, specId, gitBranch, transitio
278
278
  clearPending(specId, 'generateSessionContext');
279
279
  }
280
280
  })(),
281
- (async () => {
282
- const { glob } = await import('glob');
283
- const { unlink } = await import('node:fs/promises');
284
- const specFiles = await glob(`planu/specs/${specId}-*/prompt.md`, { cwd: projectPath, absolute: true });
285
- const exactFiles = await glob(`planu/specs/${specId}/prompt.md`, { cwd: projectPath, absolute: true });
286
- await Promise.allSettled([...specFiles, ...exactFiles].map((file) => unlink(file)));
287
- })(),
288
281
  ]);
289
282
  const failures = results.filter((result) => result.status === 'rejected');
290
283
  if (failures.length > 0) {
291
284
  throw new AggregateError(normalizeRejectedReasons(failures), `${String(failures.length)} done side effect(s) failed`);
292
285
  }
286
+ const cleanup = results[0].status === 'fulfilled' ? results[0].value : null;
287
+ const planuCleanup = results[4].status === 'fulfilled' ? results[4].value : null;
288
+ const housekeeping = results[5].status === 'fulfilled' ? results[5].value : null;
289
+ return {
290
+ cleanupProposals: cleanup?.proposals ?? [],
291
+ housekeepingProposal: housekeeping ?? {
292
+ mode: 'report',
293
+ sweepAt: new Date().toISOString(),
294
+ projectPath,
295
+ wouldDelete: { branches: [], worktrees: [], stashes: [] },
296
+ kept: { branches: [] },
297
+ executed: false,
298
+ deleted: { branches: 0, worktrees: 0, stashes: 0, backups: 0 },
299
+ errors: [],
300
+ },
301
+ planuLayoutOffenders: planuCleanup?.proposed ?? [],
302
+ };
293
303
  }
294
304
  /**
295
305
  * Best-effort constitution compliance check for status transitions.
@@ -1,5 +1,6 @@
1
1
  import type { GitAction } from './common/index.js';
2
2
  import type { GeneratedDocument } from './docs.js';
3
+ import type { HousekeepingReport } from './housekeeping.js';
3
4
  export interface GitConfig {
4
5
  branchPrefix?: Record<string, string>;
5
6
  commitFormat?: 'conventional' | 'spec-id' | 'custom';
@@ -173,12 +174,30 @@ export interface CleanupReport {
173
174
  message: string;
174
175
  autoCompleted?: string[];
175
176
  }
177
+ export type CleanupProposalKind = 'worktree' | 'local-branch' | 'remote-branch';
178
+ export type CleanupMergedState = 'merged' | 'not_merged' | 'unknown';
179
+ export type CleanupSuggestedAction = 'delete' | 'keep' | 'inspect';
180
+ export interface SpecDoneCleanupProposal {
181
+ kind: CleanupProposalKind;
182
+ ref: string;
183
+ worktreeClean: boolean | null;
184
+ mergedState: CleanupMergedState;
185
+ reason: string;
186
+ suggestedAction: CleanupSuggestedAction;
187
+ }
176
188
  export interface SpecDoneCleanupResult {
189
+ mode: 'report' | 'execute';
190
+ proposals: SpecDoneCleanupProposal[];
177
191
  worktreeRemoved: string | null;
178
192
  localBranchRemoved: string | null;
179
193
  remoteBranchRemoved: string | null;
180
194
  errors: string[];
181
195
  }
196
+ export interface DoneSideEffectsReport {
197
+ cleanupProposals: SpecDoneCleanupProposal[];
198
+ housekeepingProposal: HousekeepingReport;
199
+ planuLayoutOffenders: string[];
200
+ }
182
201
  export interface GitHubIssueImportResult {
183
202
  action: 'import-issue';
184
203
  specId: string;
@@ -36,7 +36,14 @@ export interface StaleStashInfo {
36
36
  */
37
37
  reason: 'age' | 'orphan-branch' | 'orphan-merged';
38
38
  }
39
+ export type HousekeepingAuthority = {
40
+ mode: 'report';
41
+ } | {
42
+ mode: 'execute';
43
+ deleteRemoteRefs: boolean;
44
+ };
39
45
  export interface HousekeepingReport {
46
+ mode: 'report' | 'execute';
40
47
  /** ISO timestamp of when the sweep ran. */
41
48
  sweepAt: string;
42
49
  /** Project path that was analysed. */
@@ -61,6 +68,7 @@ export interface HousekeepingReport {
61
68
  /** SPEC-771: stale .bak.* files removed from planu/specs/. */
62
69
  backups: number;
63
70
  };
71
+ errors: string[];
64
72
  }
65
73
  export type OrphanMarkdownAction = 'deleted_matched' | 'imported_then_deleted' | 'skipped';
66
74
  export interface OrphanMarkdownEntry {
@@ -124,7 +132,7 @@ export interface FindStaleStashesInput {
124
132
  }
125
133
  export interface RunHousekeepingSweepInput {
126
134
  projectPath: string;
127
- dryRun?: boolean;
135
+ authority?: HousekeepingAuthority;
128
136
  aggressive?: boolean;
129
137
  includeStashes?: boolean;
130
138
  }
@@ -141,6 +141,7 @@ export * from './red-team.js';
141
141
  export * from './update-notifier.js';
142
142
  export * from './batch-script.js';
143
143
  export * from './hooks-advanced.js';
144
+ export * from './housekeeping.js';
144
145
  export * from './codex-integration.js';
145
146
  export * from './gemini-integration.js';
146
147
  export * from './ai-integration.js';
@@ -138,6 +138,7 @@ export * from './red-team.js';
138
138
  export * from './update-notifier.js';
139
139
  export * from './batch-script.js';
140
140
  export * from './hooks-advanced.js';
141
+ export * from './housekeeping.js';
141
142
  export * from './codex-integration.js';
142
143
  export * from './gemini-integration.js';
143
144
  export * from './ai-integration.js';
@@ -64,6 +64,7 @@ export interface CleanupResult {
64
64
  deletedRootFiles: string[];
65
65
  deletedSpecFiles: string[];
66
66
  totalDeleted: number;
67
+ proposed: string[];
67
68
  }
68
69
  export interface PlanuCanonicalPathPolicy {
69
70
  readonly canonicalRootFiles: readonly string[];
@@ -75,8 +76,10 @@ export interface PlanuCanonicalPathPolicy {
75
76
  readonly legacyMergeBeforeDeleteFiles: readonly string[];
76
77
  }
77
78
  export interface StrictPlanuCleanupResult {
79
+ mode: 'report' | 'execute';
78
80
  deleted: string[];
79
81
  merged: string[];
82
+ proposed: string[];
80
83
  gitignoreUpdated: boolean;
81
84
  }
82
85
  export interface StrictPlanuValidationResult {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planu/cli",
3
- "version": "5.3.14",
3
+ "version": "5.3.15",
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",
@@ -35,14 +35,14 @@
35
35
  "packageName": "@planu/core"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@planu/core-darwin-arm64": "5.3.14",
39
- "@planu/core-darwin-x64": "5.3.14",
40
- "@planu/core-linux-arm64-gnu": "5.3.14",
41
- "@planu/core-linux-arm64-musl": "5.3.14",
42
- "@planu/core-linux-x64-gnu": "5.3.14",
43
- "@planu/core-linux-x64-musl": "5.3.14",
44
- "@planu/core-win32-arm64-msvc": "5.3.14",
45
- "@planu/core-win32-x64-msvc": "5.3.14"
38
+ "@planu/core-darwin-arm64": "5.3.15",
39
+ "@planu/core-darwin-x64": "5.3.15",
40
+ "@planu/core-linux-arm64-gnu": "5.3.15",
41
+ "@planu/core-linux-arm64-musl": "5.3.15",
42
+ "@planu/core-linux-x64-gnu": "5.3.15",
43
+ "@planu/core-linux-x64-musl": "5.3.15",
44
+ "@planu/core-win32-arm64-msvc": "5.3.15",
45
+ "@planu/core-win32-x64-msvc": "5.3.15"
46
46
  },
47
47
  "engines": {
48
48
  "node": ">=24.0.0"
@@ -86,7 +86,8 @@
86
86
  "native:project-graph:e2e": "PLANU_NATIVE_E2E=1 vitest run tests/integration/native-project-graph-e2e.test.ts",
87
87
  "native:project-graph:benchmark": "node scripts/benchmark-native-performance.mjs --operation project_graph_query --samples 30 --warmups 5 --project-path . --spec-id SPEC-1119 --json",
88
88
  "benchmark:execution-kernel": "pnpm build && node scripts/benchmark-execution-kernel.mjs",
89
- "check": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm check:release-authorities && pnpm check:commercial-surfaces && pnpm check:tool-registration && pnpm check:public-privacy && pnpm check:website-proof && pnpm check:donation-assets && pnpm check:website-public-assets",
89
+ "check": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm check:destructive-ops && pnpm check:release-authorities && pnpm check:commercial-surfaces && pnpm check:tool-registration && pnpm check:public-privacy && pnpm check:website-proof && pnpm check:donation-assets && pnpm check:website-public-assets",
90
+ "check:destructive-ops": "bash scripts/check-no-automatic-destructive-ops.sh",
90
91
  "check:strict": "pnpm check && pnpm generate:host-tool-registry -- --check && pnpm check:native-version-parity && pnpm check:environment-schema && pnpm audit:hardcodes && pnpm audit:deadcode && pnpm audit:circular && pnpm audit:types && pnpm audit:security && pnpm audit:i18n",
91
92
  "check:reliability": "node scripts/check-reliability-policies.mjs",
92
93
  "check:environment-schema": "node scripts/check-environment-schema.mjs",
package/planu-native.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dev.planu.native",
3
3
  "displayName": "Planu Native Lightweight Surface",
4
- "version": "5.3.14",
4
+ "version": "5.3.15",
5
5
  "packageName": "@planu/cli",
6
6
  "modes": {
7
7
  "lightweight": {
package/planu-plugin.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dev.planu.cli",
3
3
  "displayName": "Planu — Spec Driven Development",
4
4
  "description": "Manage software specs, estimations, and autonomous SDD workflows. Language-agnostic MCP server for Claude Code.",
5
- "version": "5.3.14",
5
+ "version": "5.3.15",
6
6
  "icon": "assets/plugin/icon.svg",
7
7
  "command": ["npx", "@planu/cli@latest"],
8
8
  "packageName": "@planu/cli",