@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.
- package/dist/engine/handoff-artifacts/index.d.ts +1 -16
- package/dist/engine/handoff-artifacts/io.d.ts +1 -1
- package/dist/engine/handoff-artifacts/schemas.d.ts +1 -1
- package/dist/engine/handoff-artifacts/validation-result.d.ts +18 -0
- package/dist/engine/handoff-artifacts/validation-result.js +3 -0
- package/dist/engine/hooks/handlers/on-impl-change.js +3 -2
- package/dist/engine/test-contract-generator.js +41 -39
- package/dist/engine/test-scaffold-generator/unit-scaffold.js +10 -10
- package/dist/engine/test-spec-generator/criterion-parser.js +5 -24
- package/dist/storage/base-store.d.ts +0 -1
- package/dist/storage/base-store.js +0 -4
- package/dist/tools/generate-tests/generators/concurrency-test-generator/java-templates.js +8 -13
- package/dist/tools/generate-tests/generators/concurrency-test-generator/js-templates.js +21 -47
- package/dist/tools/generate-tests/generators/concurrency-test-generator/python-rust-templates.js +3 -14
- package/dist/tools/git/auto-complete-ops.d.ts +18 -0
- package/dist/tools/git/auto-complete-ops.js +63 -0
- package/dist/tools/git/branch-ops.d.ts +0 -17
- package/dist/tools/git/branch-ops.js +0 -54
- package/dist/tools/git/cleanup-ops.js +0 -16
- package/dist/tools/git/release-ops.js +1 -1
- package/dist/tools/init-project/handler.js +1 -1
- package/dist/tools/manage-hooks.js +3 -1
- package/dist/tools/status-handler.js +1 -1
- package/dist/tools/update-status-actions.js +8 -5
- package/dist/types/clarification-token.d.ts +1 -1
- package/dist/types/clarification.d.ts +2 -18
- package/dist/types/hook-status-update.d.ts +10 -0
- package/dist/types/hook-status-update.js +3 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/interactive-question.d.ts +19 -0
- package/dist/types/interactive-question.js +3 -0
- package/dist/types/storage.d.ts +1 -1
- package/package.json +9 -9
- package/planu-native.json +8 -29
- package/planu-plugin.json +7 -35
- package/dist/engine/hooks/index.d.ts +0 -20
- package/dist/engine/hooks/index.js +0 -25
- package/dist/storage/crud-store-factory.d.ts +0 -22
- 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 './
|
|
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/
|
|
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
|
|
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/
|
|
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
|
-
|
|
228
|
-
|
|
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 './
|
|
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
|
-
|
|
2
|
-
export
|
|
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
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/types/index.js
CHANGED
|
@@ -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
|
package/dist/types/storage.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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.
|
|
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.
|
|
38
|
-
"@planu/core-darwin-x64": "4.3.
|
|
39
|
-
"@planu/core-linux-arm64-gnu": "4.3.
|
|
40
|
-
"@planu/core-linux-arm64-musl": "4.3.
|
|
41
|
-
"@planu/core-linux-x64-gnu": "4.3.
|
|
42
|
-
"@planu/core-linux-x64-musl": "4.3.
|
|
43
|
-
"@planu/core-win32-arm64-msvc": "4.3.
|
|
44
|
-
"@planu/core-win32-x64-msvc": "4.3.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
31
|
-
|
|
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
|