@planu/cli 4.3.24 → 4.3.25

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/engine/handoff-artifacts/index.d.ts +1 -16
  2. package/dist/engine/handoff-artifacts/io.d.ts +1 -1
  3. package/dist/engine/handoff-artifacts/schemas.d.ts +1 -1
  4. package/dist/engine/handoff-artifacts/validation-result.d.ts +18 -0
  5. package/dist/engine/handoff-artifacts/validation-result.js +3 -0
  6. package/dist/engine/hooks/handlers/on-impl-change.js +3 -2
  7. package/dist/engine/test-contract-generator.js +41 -39
  8. package/dist/engine/test-scaffold-generator/unit-scaffold.js +10 -10
  9. package/dist/engine/test-spec-generator/criterion-parser.js +5 -24
  10. package/dist/storage/base-store.d.ts +0 -1
  11. package/dist/storage/base-store.js +0 -4
  12. package/dist/tools/generate-tests/generators/concurrency-test-generator/java-templates.js +8 -13
  13. package/dist/tools/generate-tests/generators/concurrency-test-generator/js-templates.js +21 -47
  14. package/dist/tools/generate-tests/generators/concurrency-test-generator/python-rust-templates.js +3 -14
  15. package/dist/tools/git/auto-complete-ops.d.ts +18 -0
  16. package/dist/tools/git/auto-complete-ops.js +63 -0
  17. package/dist/tools/git/branch-ops.d.ts +0 -17
  18. package/dist/tools/git/branch-ops.js +0 -54
  19. package/dist/tools/git/cleanup-ops.js +0 -16
  20. package/dist/tools/git/release-ops.js +1 -1
  21. package/dist/tools/init-project/handler.js +1 -1
  22. package/dist/tools/manage-hooks.js +3 -1
  23. package/dist/tools/status-handler.js +1 -1
  24. package/dist/tools/update-status-actions.js +8 -5
  25. package/dist/types/clarification-token.d.ts +1 -1
  26. package/dist/types/clarification.d.ts +2 -18
  27. package/dist/types/hook-status-update.d.ts +10 -0
  28. package/dist/types/hook-status-update.js +3 -0
  29. package/dist/types/index.d.ts +1 -0
  30. package/dist/types/index.js +1 -0
  31. package/dist/types/interactive-question.d.ts +19 -0
  32. package/dist/types/interactive-question.js +3 -0
  33. package/dist/types/storage.d.ts +1 -1
  34. package/package.json +9 -9
  35. package/planu-native.json +8 -29
  36. package/planu-plugin.json +7 -35
  37. package/dist/engine/hooks/index.d.ts +0 -20
  38. package/dist/engine/hooks/index.js +0 -25
  39. package/dist/storage/crud-store-factory.d.ts +0 -22
  40. package/dist/storage/crud-store-factory.js +0 -72
@@ -1,5 +1,4 @@
1
1
  import { specStore } from '../../storage/index.js';
2
- import { handleUpdateStatus } from '../update-status/index.js';
3
2
  import { ti } from '../../i18n/index.js';
4
3
  import { git, resolveProjectPath, slugify, mergeConfig, detectGitFlowType, resolveBaseBranch, DEFAULT_BRANCH_PREFIXES, DEFAULT_PROTECTED_BRANCHES, } from './git-helpers.js';
5
4
  import { compactResult, compactError, formatKeyValue, formatTable, formatList, } from '../output-formatter.js';
@@ -20,59 +19,6 @@ function randomSuffix() {
20
19
  .toString(16)
21
20
  .padStart(8, '0');
22
21
  }
23
- export async function autoCompleteSpecs(projectId, projectPath) {
24
- // Try develop first, fall back to main
25
- let mergedOut;
26
- try {
27
- const r = await git(projectPath, ['branch', '--merged', 'develop']);
28
- mergedOut = r.stdout;
29
- }
30
- catch {
31
- const r = await git(projectPath, ['branch', '--merged', 'main']);
32
- mergedOut = r.stdout;
33
- }
34
- const mergedList = mergedOut
35
- .split('\n')
36
- .map((b) => b.replace(/^\*?\s+/, '').trim())
37
- .filter(Boolean);
38
- const mergedSet = new Set(mergedList);
39
- /** Returns true if the spec's branch (exact or by ID pattern) appears in merged branches. */
40
- function isMerged(specId, gitBranch) {
41
- if (gitBranch && mergedSet.has(gitBranch)) {
42
- return true;
43
- }
44
- const num = /SPEC-(\d+)/i.exec(specId)?.[1];
45
- if (!num) {
46
- return false;
47
- }
48
- const pat = new RegExp(`[/\\-]SPEC-${num}([/\\-]|$)`, 'i');
49
- return mergedList.some((b) => pat.test(b));
50
- }
51
- const specs = (await specStore.listSpecs(projectId)).filter((s) => (s.status === 'implementing' || s.status === 'approved') && isMerged(s.id, s.gitBranch));
52
- const completed = [];
53
- const blocked = [];
54
- for (const spec of specs) {
55
- // SPEC-720: Route through handleUpdateStatus so all gates (DoD/validate/QA) run.
56
- // Never call specStore.updateSpec({status}) directly.
57
- const result = await handleUpdateStatus({
58
- specId: spec.id,
59
- status: 'done',
60
- projectId,
61
- projectPath,
62
- trigger: 'git-merge',
63
- actor: 'system',
64
- reviewNotes: 'Auto-completed: branch merged',
65
- });
66
- if (result.isError) {
67
- const reason = result.content[0]?.type === 'text' ? result.content[0].text : 'gate blocked (no message)';
68
- blocked.push({ specId: spec.id, reason });
69
- }
70
- else {
71
- completed.push(spec.id);
72
- }
73
- }
74
- return { completed, blocked };
75
- }
76
22
  export async function handleCreateBranch(projectId, specId, config) {
77
23
  if (!specId) {
78
24
  return compactError('❌ Error: specId is required for create-branch action');
@@ -3,7 +3,6 @@ import { existsSync } from 'node:fs';
3
3
  import { readdir } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
5
  import { git, resolveProjectPath } from './git-helpers.js';
6
- import { autoCompleteSpecs } from './branch-ops.js';
7
6
  import { compactResult, compactError } from '../output-formatter.js';
8
7
  const WORKTREES_DIR = '.claude/worktrees';
9
8
  const STALE_DAYS_THRESHOLD = 7;
@@ -283,23 +282,12 @@ export async function handleCleanup(projectId, config) {
283
282
  stashesDropped = dropped;
284
283
  errors.push(...stashErrors);
285
284
  }
286
- // 6. Auto-complete implementing specs whose branch is merged to develop (SPEC-397)
287
- // SPEC-720: autoCompleteSpecs now returns {completed, blocked}
288
- const autoCompleteResult = await autoCompleteSpecs(projectId, projectPath).catch(() => ({
289
- completed: [],
290
- blocked: [],
291
- }));
292
- const autoCompleted = autoCompleteResult.completed;
293
285
  const messageParts = [
294
286
  `Worktrees removed: ${worktreesRemoved.length}`,
295
287
  `Branches removed: ${branchesRemoved.length}`,
296
288
  `Remote pruned: ${remotePruned ? 'yes' : 'no'}`,
297
289
  `Stale stashes (>${STALE_DAYS_THRESHOLD}d): ${staleStashes.length}`,
298
290
  ...(force ? [`Stashes dropped: ${stashesDropped}`] : ['Use force:true to drop stale stashes']),
299
- ...(autoCompleted.length > 0 ? [`Specs auto-completed: ${autoCompleted.join(', ')}`] : []),
300
- ...(autoCompleteResult.blocked.length > 0
301
- ? [`Specs blocked (gates): ${autoCompleteResult.blocked.map((b) => b.specId).join(', ')}`]
302
- : []),
303
291
  ...(errors.length > 0 ? [`Errors: ${errors.length}`] : []),
304
292
  ];
305
293
  const report = {
@@ -311,7 +299,6 @@ export async function handleCleanup(projectId, config) {
311
299
  stashesDropped,
312
300
  errors,
313
301
  message: messageParts.join(' | '),
314
- ...(autoCompleted.length > 0 ? { autoCompleted } : {}),
315
302
  };
316
303
  const lines = [report.message];
317
304
  if (worktreesRemoved.length > 0) {
@@ -323,9 +310,6 @@ export async function handleCleanup(projectId, config) {
323
310
  if (staleStashes.length > 0) {
324
311
  lines.push('', `**Stale stashes**: ${staleStashes.map((s) => `${s.ref} (${String(s.daysOld)}d)`).join(', ')}`);
325
312
  }
326
- if (autoCompleted.length > 0) {
327
- lines.push('', `**Auto-completed specs**: ${autoCompleted.join(', ')}`);
328
- }
329
313
  if (errors.length > 0) {
330
314
  lines.push('', `**Errors**: ${errors.join('; ')}`);
331
315
  }
@@ -1,6 +1,6 @@
1
1
  import { git, resolveProjectPath, resolveBaseBranch, mergeConfig } from './git-helpers.js';
2
2
  import { specStore } from '../../storage/index.js';
3
- import { autoCompleteSpecs } from './branch-ops.js';
3
+ import { autoCompleteSpecs } from './auto-complete-ops.js';
4
4
  import { compactResult, formatKeyValue, formatList } from '../output-formatter.js';
5
5
  export async function handleRelease(projectId, config) {
6
6
  const projectPath = await resolveProjectPath(projectId);
@@ -13,7 +13,7 @@ import { checkBundledVersionGap } from '../../engine/version-detector/bundled-ve
13
13
  import { checkAndFixBundledVersion } from '../../engine/mcp-config/mcp-config-writer.js';
14
14
  import { buildInitProjectResult } from './result-builder.js';
15
15
  import { fetchRecommendedSkills } from '../suggest-tooling/skills-fetcher.js';
16
- import { autoCompleteSpecs } from '../git/branch-ops.js';
16
+ import { autoCompleteSpecs } from '../git/auto-complete-ops.js';
17
17
  import { regeneratePages } from '../../engine/doc-generator/portal/index.js';
18
18
  import { detectStackPatterns } from './stack-detector.js';
19
19
  import { buildProjectConfig } from './config-builder.js';
@@ -1,6 +1,8 @@
1
1
  // tools/manage-hooks.ts — MCP tool handler for manage_hooks (SPEC-071)
2
2
  import { hooksStore } from '../storage/index.js';
3
- import { dispatchEvent, dryRunDispatch, getTemplate, listTemplateNames, generateGitHooks, formatGitHookInstallGuide, } from '../engine/hooks/index.js';
3
+ import { dispatchEvent, dryRunDispatch } from '../engine/hooks/core.js';
4
+ import { getTemplate, listTemplateNames } from '../engine/hooks/templates.js';
5
+ import { generateGitHooks, formatGitHookInstallGuide } from '../engine/hooks/git-hook-generator.js';
4
6
  import { compactResult, compactError, formatKeyValue, formatTable, formatList, formatConfirmation, } from './output-formatter.js';
5
7
  // ---------------------------------------------------------------------------
6
8
  // Operation handlers
@@ -8,7 +8,7 @@ import { promisify } from 'node:util';
8
8
  import { join } from 'node:path';
9
9
  import { readFile } from 'node:fs/promises';
10
10
  import { shouldSuggestBtw, buildBtwHint } from '../engine/context-advisor/btw-advisor.js';
11
- import { autoCompleteSpecs } from './git/branch-ops.js';
11
+ import { autoCompleteSpecs } from './git/auto-complete-ops.js';
12
12
  import { computeSessionAgeMinutes } from '../engine/session-handoff-generator.js';
13
13
  import { loadSessionState } from '../storage/session-state-store.js';
14
14
  import { consumeUpdateBanner } from '../engine/update-notifier.js';
@@ -1,7 +1,5 @@
1
1
  import { specStore, knowledgeStore } from '../storage/index.js';
2
- import { handleCreateBranch } from './git/branch-ops.js';
3
2
  import { checkConstitutionCompliance } from './create-spec-tech.js';
4
- import { cleanupSpecOnDone } from './git/cleanup-ops.js';
5
3
  import { withAudit } from '../engine/autopilot/audit-logger.js';
6
4
  import { hasPending, markPending, clearPending } from '../engine/autopilot/cascade-deduplicator.js';
7
5
  export async function runImplementingActions(projectId, specId) {
@@ -27,10 +25,12 @@ export async function runImplementingActions(projectId, specId) {
27
25
  let branchResult;
28
26
  try {
29
27
  const { resolveProjectPath: resolveBranchPath } = await import('./git/git-helpers.js');
28
+ const { handleCreateBranch } = await import('./git/branch-ops.js');
30
29
  const branchProjectPath = await resolveBranchPath(projectId);
31
30
  branchResult = await withAudit(branchProjectPath, 'update_status(implementing)', 'handleCreateBranch', () => handleCreateBranch(projectId, specId), (r) => ({ isError: r.isError }));
32
31
  }
33
32
  catch {
33
+ const { handleCreateBranch } = await import('./git/branch-ops.js');
34
34
  branchResult = await handleCreateBranch(projectId, specId);
35
35
  }
36
36
  if (!branchResult.isError) {
@@ -224,9 +224,12 @@ export async function runDoneActions(projectId, specId, gitBranch) {
224
224
  // SPEC-491: Sweep planu/ to remove legacy files
225
225
  // SPEC-496: Auto-generate portable session context snapshot
226
226
  void Promise.allSettled([
227
- withAudit(projectPath, 'update_status(done)', 'cleanupSpecOnDone', () => cleanupSpecOnDone(projectPath, specId, gitBranch)).catch(() => {
228
- /* best-effort never blocks status transition */
229
- }),
227
+ (async () => {
228
+ const { cleanupSpecOnDone } = await import('./git/cleanup-ops.js');
229
+ await withAudit(projectPath, 'update_status(done)', 'cleanupSpecOnDone', () => cleanupSpecOnDone(projectPath, specId, gitBranch)).catch(() => {
230
+ /* best-effort — never blocks status transition */
231
+ });
232
+ })(),
230
233
  // SPEC-633: Remove spec from session.json index when done
231
234
  (async () => {
232
235
  const { removeSpecFromSession } = await import('../engine/session-state/writer.js');
@@ -1,4 +1,4 @@
1
- import type { InteractiveQuestion } from './clarification.js';
1
+ import type { InteractiveQuestion } from './interactive-question.js';
2
2
  import type { ToolResult } from './common/primitives.js';
3
3
  /**
4
4
  * Persisted token that blocks a tool from proceeding until the LLM relays
@@ -1,21 +1,5 @@
1
- /** A single option in an interactive question. */
2
- export interface InteractiveOption {
3
- /** Display text — add "(Recommended)" suffix to preferred option. */
4
- label: string;
5
- /** What this option means or what happens if chosen. */
6
- description: string;
7
- }
8
- /** A structured question for the user, presented via the LLM's native UI. */
9
- export interface InteractiveQuestion {
10
- /** The full question text, ending with "?". */
11
- question: string;
12
- /** Short chip/tag label (max 12 chars). E.g. "Auth", "Scope", "Target". */
13
- header: string;
14
- /** 2-4 options to choose from. An "Other" free-text option is added automatically by the LLM. */
15
- options: InteractiveOption[];
16
- /** True for non-exclusive choices (user can pick multiple). */
17
- multiSelect: boolean;
18
- }
1
+ import type { InteractiveQuestion } from './interactive-question.js';
2
+ export type { InteractiveOption, InteractiveQuestion } from './interactive-question.js';
19
3
  /** Tool response when interactive clarification is needed instead of creating a spec. */
20
4
  export interface InteractiveClarificationResponse {
21
5
  needsClarification: true;
@@ -0,0 +1,10 @@
1
+ import type { ToolResult } from './common/primitives.js';
2
+ import type { UpdateStatusInput } from './spec/inputs.js';
3
+ export interface HookUpdateStatusInput extends UpdateStatusInput {
4
+ trigger: 'fs-hook';
5
+ actor: 'system';
6
+ }
7
+ export interface UpdateStatusModule {
8
+ handleUpdateStatus: (input: HookUpdateStatusInput) => Promise<ToolResult>;
9
+ }
10
+ //# sourceMappingURL=hook-status-update.d.ts.map
@@ -0,0 +1,3 @@
1
+ // types/hook-status-update.ts — Runtime bridge types for hook-triggered status transitions
2
+ export {};
3
+ //# sourceMappingURL=hook-status-update.js.map
@@ -57,6 +57,7 @@ export * from './changelog.js';
57
57
  export * from './coverage.js';
58
58
  export * from './ai-cost.js';
59
59
  export * from './hooks.js';
60
+ export * from './hook-status-update.js';
60
61
  export * from './webhook.js';
61
62
  export * from './ci.js';
62
63
  export * from './spec-templates.js';
@@ -58,6 +58,7 @@ export * from './changelog.js';
58
58
  export * from './coverage.js';
59
59
  export * from './ai-cost.js';
60
60
  export * from './hooks.js';
61
+ export * from './hook-status-update.js';
61
62
  export * from './webhook.js';
62
63
  export * from './ci.js';
63
64
  export * from './spec-templates.js';
@@ -0,0 +1,19 @@
1
+ /** A single option in an interactive question. */
2
+ export interface InteractiveOption {
3
+ /** Display text — add "(Recommended)" suffix to preferred option. */
4
+ label: string;
5
+ /** What this option means or what happens if chosen. */
6
+ description: string;
7
+ }
8
+ /** A structured question for the user, presented via the LLM's native UI. */
9
+ export interface InteractiveQuestion {
10
+ /** The full question text, ending with "?". */
11
+ question: string;
12
+ /** Short chip/tag label (max 12 chars). E.g. "Auth", "Scope", "Target". */
13
+ header: string;
14
+ /** 2-4 options to choose from. An "Other" free-text option is added automatically by the LLM. */
15
+ options: InteractiveOption[];
16
+ /** True for non-exclusive choices (user can pick multiple). */
17
+ multiSelect: boolean;
18
+ }
19
+ //# sourceMappingURL=interactive-question.d.ts.map
@@ -0,0 +1,3 @@
1
+ // types/interactive-question.ts — LLM-native interactive question primitives
2
+ export {};
3
+ //# sourceMappingURL=interactive-question.js.map
@@ -2,7 +2,7 @@
2
2
  * A minimal CRUD store backed by a single JSON array file.
3
3
  * Items must have an `id: string` field for lookup / update / remove.
4
4
  *
5
- * Created by `createCrudStore()` in `storage/base-store.ts`.
5
+ * JSON-backed stores expose this shape when they implement CRUD operations.
6
6
  */
7
7
  export interface CrudStore<T extends {
8
8
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planu/cli",
3
- "version": "4.3.24",
3
+ "version": "4.3.25",
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",
@@ -34,14 +34,14 @@
34
34
  "packageName": "@planu/core"
35
35
  },
36
36
  "optionalDependencies": {
37
- "@planu/core-darwin-arm64": "4.3.24",
38
- "@planu/core-darwin-x64": "4.3.24",
39
- "@planu/core-linux-arm64-gnu": "4.3.24",
40
- "@planu/core-linux-arm64-musl": "4.3.24",
41
- "@planu/core-linux-x64-gnu": "4.3.24",
42
- "@planu/core-linux-x64-musl": "4.3.24",
43
- "@planu/core-win32-arm64-msvc": "4.3.24",
44
- "@planu/core-win32-x64-msvc": "4.3.24"
37
+ "@planu/core-darwin-arm64": "4.3.25",
38
+ "@planu/core-darwin-x64": "4.3.25",
39
+ "@planu/core-linux-arm64-gnu": "4.3.25",
40
+ "@planu/core-linux-arm64-musl": "4.3.25",
41
+ "@planu/core-linux-x64-gnu": "4.3.25",
42
+ "@planu/core-linux-x64-musl": "4.3.25",
43
+ "@planu/core-win32-arm64-msvc": "4.3.25",
44
+ "@planu/core-win32-x64-msvc": "4.3.25"
45
45
  },
46
46
  "engines": {
47
47
  "node": ">=24.0.0"
package/planu-native.json CHANGED
@@ -1,26 +1,20 @@
1
1
  {
2
2
  "name": "dev.planu.native",
3
3
  "displayName": "Planu Native Lightweight Surface",
4
- "version": "4.3.24",
4
+ "version": "4.3.25",
5
5
  "packageName": "@planu/cli",
6
6
  "modes": {
7
7
  "lightweight": {
8
8
  "requiresMcp": false,
9
9
  "requiresDaemon": false,
10
- "hosts": [
11
- "codex",
12
- "claude-code"
13
- ],
10
+ "hosts": ["codex", "claude-code"],
14
11
  "commands": [
15
12
  {
16
13
  "id": "planu.status",
17
14
  "title": "Project status",
18
15
  "description": "Show the compact Planu project snapshot without loading the MCP tool graph.",
19
16
  "invocation": "planu status",
20
- "hosts": [
21
- "codex",
22
- "claude-code"
23
- ],
17
+ "hosts": ["codex", "claude-code"],
24
18
  "requiresMcp": false,
25
19
  "requiresDaemon": false,
26
20
  "mapsTo": "handlePlanStatus"
@@ -30,10 +24,7 @@
30
24
  "title": "Create spec",
31
25
  "description": "Create a new spec through the CLI-backed SDD contract.",
32
26
  "invocation": "planu spec create \"<title>\"",
33
- "hosts": [
34
- "codex",
35
- "claude-code"
36
- ],
27
+ "hosts": ["codex", "claude-code"],
37
28
  "requiresMcp": false,
38
29
  "requiresDaemon": false,
39
30
  "mapsTo": "handleCreateSpec"
@@ -43,10 +34,7 @@
43
34
  "title": "List specs",
44
35
  "description": "List specs in the current project with optional status/type filters.",
45
36
  "invocation": "planu spec list",
46
- "hosts": [
47
- "codex",
48
- "claude-code"
49
- ],
37
+ "hosts": ["codex", "claude-code"],
50
38
  "requiresMcp": false,
51
39
  "requiresDaemon": false,
52
40
  "mapsTo": "handleListSpecs"
@@ -56,10 +44,7 @@
56
44
  "title": "Validate spec",
57
45
  "description": "Validate a spec against the current codebase from the native CLI surface.",
58
46
  "invocation": "planu spec validate SPEC-001",
59
- "hosts": [
60
- "codex",
61
- "claude-code"
62
- ],
47
+ "hosts": ["codex", "claude-code"],
63
48
  "requiresMcp": false,
64
49
  "requiresDaemon": false,
65
50
  "mapsTo": "handleValidate"
@@ -69,10 +54,7 @@
69
54
  "title": "Audit technical debt",
70
55
  "description": "Run the read-only project audit path for lightweight debt checks.",
71
56
  "invocation": "planu audit debt",
72
- "hosts": [
73
- "codex",
74
- "claude-code"
75
- ],
57
+ "hosts": ["codex", "claude-code"],
76
58
  "requiresMcp": false,
77
59
  "requiresDaemon": false,
78
60
  "mapsTo": "handleAudit"
@@ -82,10 +64,7 @@
82
64
  "title": "Check release readiness",
83
65
  "description": "Check local branch cleanliness and main/develop/release sync readiness.",
84
66
  "invocation": "planu release check",
85
- "hosts": [
86
- "codex",
87
- "claude-code"
88
- ],
67
+ "hosts": ["codex", "claude-code"],
89
68
  "requiresMcp": false,
90
69
  "requiresDaemon": false,
91
70
  "mapsTo": "releaseCommand"
package/planu-plugin.json CHANGED
@@ -2,12 +2,9 @@
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": "4.3.24",
5
+ "version": "4.3.25",
6
6
  "icon": "assets/plugin/icon.svg",
7
- "command": [
8
- "npx",
9
- "@planu/cli@latest"
10
- ],
7
+ "command": ["npx", "@planu/cli@latest"],
11
8
  "packageName": "@planu/cli",
12
9
  "capabilities": {
13
10
  "tools": [
@@ -26,42 +23,17 @@
26
23
  "create_skill",
27
24
  "skill_search"
28
25
  ],
29
- "resources": [
30
- "planu://specs/list",
31
- "planu://specs/{id}",
32
- "planu://project/status",
33
- "planu://roadmap"
34
- ],
35
- "prompts": [
36
- "create-spec-from-idea",
37
- "review-spec-readiness",
38
- "generate-implementation-plan"
39
- ],
40
- "subagents": [
41
- "sdd-orchestrator",
42
- "spec-challenger",
43
- "test-generator"
44
- ]
26
+ "resources": ["planu://specs/list", "planu://specs/{id}", "planu://project/status", "planu://roadmap"],
27
+ "prompts": ["create-spec-from-idea", "review-spec-readiness", "generate-implementation-plan"],
28
+ "subagents": ["sdd-orchestrator", "spec-challenger", "test-generator"]
45
29
  },
46
30
  "compatibility": {
47
31
  "minimumHostVersion": "1.0.0",
48
- "requiredFeatures": [
49
- "mcp-tools",
50
- "file-editing"
51
- ]
32
+ "requiredFeatures": ["mcp-tools", "file-editing"]
52
33
  },
53
34
  "repository": "https://github.com/planu-dev/planu",
54
35
  "author": "Planu",
55
36
  "license": "MIT",
56
37
  "homepage": "https://planu.dev",
57
- "keywords": [
58
- "sdd",
59
- "spec-driven-development",
60
- "mcp",
61
- "specs",
62
- "planning",
63
- "ai",
64
- "bdd",
65
- "tdd"
66
- ]
38
+ "keywords": ["sdd", "spec-driven-development", "mcp", "specs", "planning", "ai", "bdd", "tdd"]
67
39
  }
@@ -1,20 +0,0 @@
1
- export { dispatchEvent, executeHook, dryRunDispatch } from './core.js';
2
- export { buildSpecCreatedPayload, buildStatusChangePayload, buildDriftDetectedPayload, buildCommitPayload, matchesFilter, matchesGlob, } from './triggers.js';
3
- export { generateGitHookScript, generateGitHooks, formatGitHookInstallGuide, } from './git-hook-generator.js';
4
- export { listTemplateNames, getTemplate, getAllTemplates } from './templates.js';
5
- export { FileWatcher, globToRegex, matchesGlobPattern, matchesPatterns } from './file-watcher.js';
6
- export { Debouncer } from './debouncer.js';
7
- export { ConfigLoader } from './config-loader.js';
8
- export { handleOnSave, extractSpecAnnotations, getStoredHash, setStoredHash, removeStoredHash, clearStoredHashes, } from './handlers/on-save.js';
9
- export { handleOnCreate } from './handlers/on-create.js';
10
- export { handleOnDelete } from './handlers/on-delete.js';
11
- export { HookEngine } from './hook-engine.js';
12
- export { EventBus } from './event-bus.js';
13
- export { handleOnTestPass, parseCoverageSummary, computeCoverageDelta, mapFilesToSpecs, clearPreviousCoverage, } from './handlers/on-test-pass.js';
14
- export { handleOnCommit, installGitHook, uninstallGitHook } from './handlers/on-commit.js';
15
- export { handleOnImplChange, isSourceFile, findRelatedSpecs, isRateLimited as isImplChangeRateLimited, clearRateLimitTimestamps as clearImplChangeTimestamps, } from './handlers/on-impl-change.js';
16
- export { handleOnSpecChange, isSpecFile } from './handlers/on-spec-change.js';
17
- export { handleStatusChange, emitSpecStatusChange, subscribeStatusChangeHandler, extractStatusChangePayload, isDashboardRateLimited, clearRegenTimestamps, } from './handlers/on-status-change.js';
18
- export { handleOnPushCheck, isSpecRelatedFile, isSecuritySensitiveFilePath, computeDriftScore, detectSecuritySensitivePaths, getLastPushCheckResult, resetPushCheckCache, } from './handlers/on-push-check.js';
19
- export { handleOnSecurityCheck, isSecuritySensitiveFile, isDependencyFile, isInCooldown as isSecurityCooldown, resetSecurityCooldown, getLastSecurityCheckResult, resetSecurityCheckCache, } from './handlers/on-security-check.js';
20
- //# sourceMappingURL=index.d.ts.map
@@ -1,25 +0,0 @@
1
- // engine/hooks/index.ts — Barrel export for hooks engine (SPEC-071)
2
- export { dispatchEvent, executeHook, dryRunDispatch } from './core.js';
3
- export { buildSpecCreatedPayload, buildStatusChangePayload, buildDriftDetectedPayload, buildCommitPayload, matchesFilter, matchesGlob, } from './triggers.js';
4
- export { generateGitHookScript, generateGitHooks, formatGitHookInstallGuide, } from './git-hook-generator.js';
5
- export { listTemplateNames, getTemplate, getAllTemplates } from './templates.js';
6
- // SPEC-129 — File Event Hooks
7
- export { FileWatcher, globToRegex, matchesGlobPattern, matchesPatterns } from './file-watcher.js';
8
- export { Debouncer } from './debouncer.js';
9
- export { ConfigLoader } from './config-loader.js';
10
- export { handleOnSave, extractSpecAnnotations, getStoredHash, setStoredHash, removeStoredHash, clearStoredHashes, } from './handlers/on-save.js';
11
- export { handleOnCreate } from './handlers/on-create.js';
12
- export { handleOnDelete } from './handlers/on-delete.js';
13
- // SPEC-130 — Hook Engine Advanced
14
- export { HookEngine } from './hook-engine.js';
15
- export { EventBus } from './event-bus.js';
16
- export { handleOnTestPass, parseCoverageSummary, computeCoverageDelta, mapFilesToSpecs, clearPreviousCoverage, } from './handlers/on-test-pass.js';
17
- export { handleOnCommit, installGitHook, uninstallGitHook } from './handlers/on-commit.js';
18
- // SPEC-137 — Auto-reconcile and progress tracking
19
- export { handleOnImplChange, isSourceFile, findRelatedSpecs, isRateLimited as isImplChangeRateLimited, clearRateLimitTimestamps as clearImplChangeTimestamps, } from './handlers/on-impl-change.js';
20
- export { handleOnSpecChange, isSpecFile } from './handlers/on-spec-change.js';
21
- export { handleStatusChange, emitSpecStatusChange, subscribeStatusChangeHandler, extractStatusChangePayload, isDashboardRateLimited, clearRegenTimestamps, } from './handlers/on-status-change.js';
22
- // SPEC-138 — Automated drift detection and security audits
23
- export { handleOnPushCheck, isSpecRelatedFile, isSecuritySensitiveFilePath, computeDriftScore, detectSecuritySensitivePaths, getLastPushCheckResult, resetPushCheckCache, } from './handlers/on-push-check.js';
24
- export { handleOnSecurityCheck, isSecuritySensitiveFile, isDependencyFile, isInCooldown as isSecurityCooldown, resetSecurityCooldown, getLastSecurityCheckResult, resetSecurityCheckCache, } from './handlers/on-security-check.js';
25
- //# sourceMappingURL=index.js.map
@@ -1,22 +0,0 @@
1
- import type { CrudStore } from '../types/index.js';
2
- /**
3
- * Create a generic CRUD store backed by a single JSON array file per project.
4
- *
5
- * @param fileName - File name relative to `projectDataDir(projectId)/`, e.g. `"patterns.json"`.
6
- *
7
- * All mutating methods (add / update / remove) acquire a per-file lock via
8
- * `withFileLock` to prevent concurrent-write races.
9
- *
10
- * @example
11
- * ```ts
12
- * const store = createCrudStore<MyEntity>('my-entities.json');
13
- * const all = await store.list(projectId);
14
- * await store.add(projectId, { id: 'x', ... });
15
- * await store.update(projectId, 'x', { field: 'value' });
16
- * await store.remove(projectId, 'x');
17
- * ```
18
- */
19
- export declare function createCrudStore<T extends {
20
- id: string;
21
- }>(fileName: string): CrudStore<T>;
22
- //# sourceMappingURL=crud-store-factory.d.ts.map
@@ -1,72 +0,0 @@
1
- // storage/crud-store-factory.ts — Generic CRUD store factory
2
- // Kept in a separate file so that tests mocking base-store.js can intercept
3
- // readJson / writeJson calls made by the factory (ESM live binding rule).
4
- import { readJson, writeJson, projectDataDir } from './base-store.js';
5
- import { withFileLock } from './file-mutex.js';
6
- /**
7
- * Create a generic CRUD store backed by a single JSON array file per project.
8
- *
9
- * @param fileName - File name relative to `projectDataDir(projectId)/`, e.g. `"patterns.json"`.
10
- *
11
- * All mutating methods (add / update / remove) acquire a per-file lock via
12
- * `withFileLock` to prevent concurrent-write races.
13
- *
14
- * @example
15
- * ```ts
16
- * const store = createCrudStore<MyEntity>('my-entities.json');
17
- * const all = await store.list(projectId);
18
- * await store.add(projectId, { id: 'x', ... });
19
- * await store.update(projectId, 'x', { field: 'value' });
20
- * await store.remove(projectId, 'x');
21
- * ```
22
- */
23
- export function createCrudStore(fileName) {
24
- function filePath(projectId) {
25
- return `${projectDataDir(projectId)}/${fileName}`;
26
- }
27
- return {
28
- async list(projectId) {
29
- return readJson(filePath(projectId), []);
30
- },
31
- async get(projectId, id) {
32
- const items = await readJson(filePath(projectId), []);
33
- return items.find((item) => item.id === id);
34
- },
35
- async add(projectId, item) {
36
- const fp = filePath(projectId);
37
- await withFileLock(fp, async () => {
38
- const items = await readJson(fp, []);
39
- items.push(item);
40
- await writeJson(fp, items);
41
- });
42
- },
43
- async update(projectId, id, updates) {
44
- const fp = filePath(projectId);
45
- await withFileLock(fp, async () => {
46
- const items = await readJson(fp, []);
47
- const idx = items.findIndex((item) => item.id === id);
48
- if (idx === -1) {
49
- throw new Error(`[Planu] Item "${id}" not found in ${fileName} for project "${projectId}"`);
50
- }
51
- const existing = items[idx];
52
- /* v8 ignore next 5 */
53
- if (!existing) {
54
- throw new Error(`[Planu] Item "${id}" not found in ${fileName} for project "${projectId}"`);
55
- }
56
- items[idx] = { ...existing, ...updates, id };
57
- await writeJson(fp, items);
58
- });
59
- },
60
- async remove(projectId, id) {
61
- const fp = filePath(projectId);
62
- await withFileLock(fp, async () => {
63
- const items = await readJson(fp, []);
64
- const filtered = items.filter((item) => item.id !== id);
65
- if (filtered.length !== items.length) {
66
- await writeJson(fp, filtered);
67
- }
68
- });
69
- },
70
- };
71
- }
72
- //# sourceMappingURL=crud-store-factory.js.map