@planu/cli 5.3.3 → 5.3.5
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/CHANGELOG.md +35 -0
- package/dist/engine/autopilot/action-registry.js +20 -2
- package/dist/engine/constitution/sdd-rules-registry.js +8 -4
- package/dist/engine/drift/violation-resolver.js +30 -8
- package/dist/engine/evidence-gates/evidence-skeletons.js +9 -4
- package/dist/engine/execution/validate-job-executor.js +25 -10
- package/dist/engine/handoff-packager.js +10 -3
- package/dist/engine/implementation-contract/common.d.ts +13 -1
- package/dist/engine/implementation-contract/common.js +19 -3
- package/dist/engine/implementation-contract/evaluator.js +116 -35
- package/dist/engine/implementation-contract/renderer.js +62 -31
- package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
- package/dist/engine/planu-core.darwin-arm64.node.sbom.json +4 -4
- package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
- package/dist/engine/planu-core.darwin-x64.node.sbom.json +4 -4
- package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +4 -4
- package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +4 -4
- package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +4 -4
- package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +4 -4
- package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
- package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +4 -4
- package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
- package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +4 -4
- package/dist/engine/reconcile/apply-changes.d.ts +0 -12
- package/dist/engine/reconcile/apply-changes.js +11 -33
- package/dist/engine/reconcile/verify-write.js +2 -3
- package/dist/engine/rules-reconciler.js +21 -2
- package/dist/engine/spec-format/acceptance-criteria.js +7 -1
- package/dist/engine/spec-format/unified-spec-builder.js +32 -8
- package/dist/engine/spec-generator/fallback-generator.js +5 -3
- package/dist/engine/spec-quality/generic-output-gate.d.ts +7 -1
- package/dist/engine/spec-quality/generic-output-gate.js +199 -8
- package/dist/engine/spec-quality-scorer.js +9 -5
- package/dist/engine/universal-rules/catalog.js +6 -0
- package/dist/engine/universal-rules/rules/agent-teams.js +0 -2
- package/dist/engine/universal-rules/rules/planu-approval-gates.js +0 -1
- package/dist/engine/universal-rules/rules/planu-bdd-criteria.js +0 -1
- package/dist/engine/universal-rules/rules/planu-clean-code-no-comments.d.ts +3 -0
- package/dist/engine/universal-rules/rules/planu-clean-code-no-comments.js +30 -0
- package/dist/engine/universal-rules/rules/planu-debate-review.d.ts +3 -0
- package/dist/engine/universal-rules/rules/planu-debate-review.js +38 -0
- package/dist/engine/universal-rules/rules/planu-dogfood-bugs.js +0 -2
- package/dist/engine/universal-rules/rules/planu-english-specs.js +0 -1
- package/dist/engine/universal-rules/rules/planu-minimal-change.js +0 -2
- package/dist/engine/universal-rules/rules/planu-modes.js +0 -2
- package/dist/engine/universal-rules/rules/planu-release-policy.js +0 -1
- package/dist/engine/universal-rules/rules/planu-revert-proof-tests.d.ts +3 -0
- package/dist/engine/universal-rules/rules/planu-revert-proof-tests.js +34 -0
- package/dist/engine/universal-rules/rules/planu-sdd-model-routing.js +0 -1
- package/dist/engine/universal-rules/rules/planu-workflow.js +0 -2
- package/dist/engine/validator/validation-report-writer.js +1 -1
- package/dist/engine/workers/handlers/auto-drift.js +22 -8
- package/dist/tools/create-spec.js +5 -0
- package/dist/tools/reconcile-spec.d.ts +1 -1
- package/dist/tools/reconcile-spec.js +303 -178
- package/dist/tools/update-status/dod-gates.js +3 -3
- package/dist/tools/update-status/evidence-gate.js +10 -17
- package/dist/tools/update-status/transition-guard.js +26 -14
- package/dist/types/reconcile.d.ts +5 -17
- package/dist/types/reconcile.js +1 -7
- package/dist/types/spec-quality.d.ts +6 -1
- package/package.json +9 -9
- package/planu-native.json +1 -1
- package/planu-plugin.json +1 -1
|
@@ -68,7 +68,7 @@ import { captureValidationFreshnessLease } from '../../engine/validation/validat
|
|
|
68
68
|
import { computeDurableValidationBindings, toValidationReceiptBindings, } from '../../engine/validation/durable-validation.js';
|
|
69
69
|
import { checkReconciliationFreshness } from '../../engine/evidence-gates/reconciliation-freshness.js';
|
|
70
70
|
import { extractCanonicalFileOwnership } from '../../engine/handoff-packager.js';
|
|
71
|
-
import {
|
|
71
|
+
import { generateTaskPlanSkeleton, writeEvidenceSkeleton, } from '../../engine/evidence-gates/evidence-skeletons.js';
|
|
72
72
|
import { extractSection } from '../../engine/spec-format/markdown-sections.js';
|
|
73
73
|
const CANONICAL_SCOPE_PATH = /(?:src|tests|scripts|website)\/[\w/.@-]+\.\w+/g;
|
|
74
74
|
/** SPEC-1356: repo-relative paths mentioned in the spec's Problem/Technical prose also count
|
|
@@ -187,28 +187,21 @@ function isArtifactAbsent(artifacts, label) {
|
|
|
187
187
|
return !artifacts.invalidArtifacts.some((entry) => entry.startsWith(label));
|
|
188
188
|
}
|
|
189
189
|
/**
|
|
190
|
-
* SPEC-1356: self-heal missing (not malformed)
|
|
191
|
-
*
|
|
192
|
-
*
|
|
190
|
+
* SPEC-1356: self-heal missing (not malformed) task-plan handoff evidence by generating a
|
|
191
|
+
* schema-valid skeleton and persisting it, instead of blocking the transition on a manual
|
|
192
|
+
* authoring step. Malformed existing artifacts still fail the gate as before.
|
|
193
|
+
*
|
|
194
|
+
* SPEC-1406 (DEFECT 3): discovery evidence for `approved` is intentionally NOT self-healed —
|
|
195
|
+
* writing a skeleton here made the blocking discovery gate self-certify by fabricating the
|
|
196
|
+
* evidence it was supposed to require. `checkLifecycleEvidenceGate` (lifecycle-gate.ts) reports
|
|
197
|
+
* `discovery_missing` with the specific missing fields named when discovery is absent, and that
|
|
198
|
+
* is the intended, honest outcome.
|
|
193
199
|
*/
|
|
194
200
|
async function autofillMissingEvidenceSkeletons(args) {
|
|
195
201
|
if (args.spec.scope === 'trivial') {
|
|
196
202
|
return args.artifacts;
|
|
197
203
|
}
|
|
198
204
|
let artifacts = args.artifacts;
|
|
199
|
-
if (args.transition === 'approved' &&
|
|
200
|
-
!artifacts.discovery &&
|
|
201
|
-
isArtifactAbsent(artifacts, 'Discovery evidence')) {
|
|
202
|
-
const skeleton = generateDiscoverySkeleton({ spec: args.spec, body: args.body });
|
|
203
|
-
await writeEvidenceSkeleton({
|
|
204
|
-
projectId: args.projectId,
|
|
205
|
-
specId: args.specId,
|
|
206
|
-
filename: 'discovery.json',
|
|
207
|
-
artifact: skeleton,
|
|
208
|
-
});
|
|
209
|
-
console.warn('[planu:evidence-skeleton]', { specId: args.specId, filename: 'discovery.json' });
|
|
210
|
-
artifacts = { ...artifacts, discovery: skeleton };
|
|
211
|
-
}
|
|
212
205
|
if (args.transition === 'implementing' &&
|
|
213
206
|
!artifacts.taskPlan &&
|
|
214
207
|
isArtifactAbsent(artifacts, 'Task plan evidence')) {
|
|
@@ -7,7 +7,7 @@ import { scoreAmbiguityFromPath } from '../../engine/ambiguity-scorer.js';
|
|
|
7
7
|
import { checkSpecReadiness } from '../../engine/readiness-checker.js';
|
|
8
8
|
import { validateEnglishOnlySpecText } from '../../engine/spec-language/english-only.js';
|
|
9
9
|
import { checkGroundedSpecContract } from '../../engine/spec-grounding/contract.js';
|
|
10
|
-
import { checkGenericSpecOutput } from '../../engine/spec-quality/generic-output-gate.js';
|
|
10
|
+
import { checkGenericSpecOutput, STRUCTURAL_INTERPOLATION_KIND, } from '../../engine/spec-quality/generic-output-gate.js';
|
|
11
11
|
import { formatKeyValue } from '../output-formatter.js';
|
|
12
12
|
import { getResolvedChallengeEvidence, requiredResolvedChallenges, } from '../challenge-spec/challenge-report.js';
|
|
13
13
|
import { evaluateSpecDependencies } from '../../engine/dependency-evaluator.js';
|
|
@@ -319,14 +319,22 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
|
|
|
319
319
|
};
|
|
320
320
|
}
|
|
321
321
|
const genericOutputGate = checkGenericSpecOutput(body);
|
|
322
|
-
|
|
322
|
+
// SPEC-1406: structural-interpolation findings warn instead of block here — the
|
|
323
|
+
// `create_spec` call site is where this new detector is blocking; ~1100 already-persisted
|
|
324
|
+
// specs must stay transitionable at update_status even if their rendered body still
|
|
325
|
+
// contains an interpolated-list pattern.
|
|
326
|
+
const blockingGenericOutputIssues = genericOutputGate.issues.filter((issue) => issue.kind !== STRUCTURAL_INTERPOLATION_KIND);
|
|
327
|
+
const structuralInterpolationWarnings = genericOutputGate.issues
|
|
328
|
+
.filter((issue) => issue.kind === STRUCTURAL_INTERPOLATION_KIND)
|
|
329
|
+
.map((issue) => `${issue.phrase}: ${issue.reason}`);
|
|
330
|
+
if (blockingGenericOutputIssues.length > 0) {
|
|
323
331
|
return {
|
|
324
332
|
blockResult: {
|
|
325
333
|
content: [
|
|
326
334
|
{
|
|
327
335
|
type: 'text',
|
|
328
336
|
text: `Spec quality gate blocked transition to ${newStatus}. ` +
|
|
329
|
-
|
|
337
|
+
blockingGenericOutputIssues
|
|
330
338
|
.slice(0, 3)
|
|
331
339
|
.map((issue) => `${issue.phrase}: ${issue.reason}`)
|
|
332
340
|
.join('; '),
|
|
@@ -336,11 +344,15 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
|
|
|
336
344
|
structuredContent: {
|
|
337
345
|
error: 'GENERIC_SPEC_OUTPUT_BLOCKED',
|
|
338
346
|
sddConstitutionRuleId: 'sdd.no-generic-output',
|
|
339
|
-
issues:
|
|
347
|
+
issues: blockingGenericOutputIssues,
|
|
340
348
|
fixHint: 'Replace generic criteria or placeholder references with grounded, testable behavior before review or approval.',
|
|
341
349
|
},
|
|
342
350
|
},
|
|
343
|
-
|
|
351
|
+
// SPEC-1406 (AC3, round 2): the offending section must be named in the output on
|
|
352
|
+
// every return path, not only the success path — this blockResult already fired
|
|
353
|
+
// for a different reason, but any structural-interpolation warnings computed above
|
|
354
|
+
// are still real findings and must not be dropped.
|
|
355
|
+
qualityWarnings: structuralInterpolationWarnings,
|
|
344
356
|
};
|
|
345
357
|
}
|
|
346
358
|
let readiness;
|
|
@@ -350,7 +362,7 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
|
|
|
350
362
|
catch (error) {
|
|
351
363
|
/* reliability-optional: READINESS_EVALUATION_FAILED — transition fails closed below */
|
|
352
364
|
reportClassifiedDegradation('READINESS_EVALUATION_FAILED', error);
|
|
353
|
-
return readinessUnavailable('READINESS_EVALUATION_FAILED', error instanceof Error ? error.message : 'readiness evaluation failed');
|
|
365
|
+
return readinessUnavailable('READINESS_EVALUATION_FAILED', error instanceof Error ? error.message : 'readiness evaluation failed', structuralInterpolationWarnings);
|
|
354
366
|
}
|
|
355
367
|
const { score } = readiness;
|
|
356
368
|
const blockers = [...readiness.issues.blockers];
|
|
@@ -377,7 +389,7 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
|
|
|
377
389
|
fixHint: 'Add at least 3 testable acceptance criteria before moving to review.',
|
|
378
390
|
},
|
|
379
391
|
},
|
|
380
|
-
qualityWarnings:
|
|
392
|
+
qualityWarnings: structuralInterpolationWarnings,
|
|
381
393
|
};
|
|
382
394
|
}
|
|
383
395
|
const question = {
|
|
@@ -412,11 +424,11 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
|
|
|
412
424
|
interactiveQuestions: [question],
|
|
413
425
|
},
|
|
414
426
|
},
|
|
415
|
-
qualityWarnings:
|
|
427
|
+
qualityWarnings: structuralInterpolationWarnings,
|
|
416
428
|
};
|
|
417
429
|
}
|
|
418
430
|
if (readiness.ready) {
|
|
419
|
-
return { blockResult: null, qualityWarnings:
|
|
431
|
+
return { blockResult: null, qualityWarnings: structuralInterpolationWarnings };
|
|
420
432
|
}
|
|
421
433
|
const diagnostic = score < 70
|
|
422
434
|
? `score ${String(score)}/100 is below the 70 threshold`
|
|
@@ -442,13 +454,13 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
|
|
|
442
454
|
fixHint: 'Complete the spec before moving from draft to review.',
|
|
443
455
|
},
|
|
444
456
|
},
|
|
445
|
-
qualityWarnings:
|
|
457
|
+
qualityWarnings: structuralInterpolationWarnings,
|
|
446
458
|
};
|
|
447
459
|
}
|
|
448
460
|
// Score < 70
|
|
449
461
|
if (forceApprove) {
|
|
450
462
|
// Caller wants to force through — proceed with warnings attached
|
|
451
|
-
return { blockResult: null, qualityWarnings: issues };
|
|
463
|
+
return { blockResult: null, qualityWarnings: [...issues, ...structuralInterpolationWarnings] };
|
|
452
464
|
}
|
|
453
465
|
const question = {
|
|
454
466
|
header: 'Readiness gate',
|
|
@@ -486,7 +498,7 @@ export async function checkReadinessGate(spec, newStatus, forceApprove) {
|
|
|
486
498
|
interactiveQuestions: [question],
|
|
487
499
|
},
|
|
488
500
|
},
|
|
489
|
-
qualityWarnings:
|
|
501
|
+
qualityWarnings: structuralInterpolationWarnings,
|
|
490
502
|
};
|
|
491
503
|
}
|
|
492
504
|
function gateUnavailableResult(code, message) {
|
|
@@ -496,8 +508,8 @@ function gateUnavailableResult(code, message) {
|
|
|
496
508
|
structuredContent: { error: code, message, retryable: true },
|
|
497
509
|
};
|
|
498
510
|
}
|
|
499
|
-
function readinessUnavailable(code, message) {
|
|
500
|
-
return { blockResult: gateUnavailableResult(code, message), qualityWarnings
|
|
511
|
+
function readinessUnavailable(code, message, qualityWarnings = []) {
|
|
512
|
+
return { blockResult: gateUnavailableResult(code, message), qualityWarnings };
|
|
501
513
|
}
|
|
502
514
|
/**
|
|
503
515
|
* SPEC-964: Challenge gate — block 'review' if challenge_spec was never run
|
|
@@ -1,58 +1,46 @@
|
|
|
1
|
-
|
|
2
|
-
* Sections whose values do not appear as literal text in spec.md body —
|
|
3
|
-
* applied/verified at the metadata layer, never via string-replace.
|
|
4
|
-
* Shared by apply-changes.ts (skip) and verify-write.ts (skip).
|
|
5
|
-
*/
|
|
6
|
-
export declare const RECONCILE_METADATA_SECTIONS: ReadonlySet<string>;
|
|
7
|
-
/** A single failed post-write verification entry (Bug D). */
|
|
1
|
+
export declare const SECTIONS_WITHOUT_LITERAL_BODY_TEXT: ReadonlySet<string>;
|
|
8
2
|
export interface VerifyWriteFailure {
|
|
9
3
|
section: string;
|
|
10
4
|
newValue: string;
|
|
11
5
|
reason: string;
|
|
12
6
|
}
|
|
13
|
-
/** Result of verifyWriteSucceeded (Bug D). */
|
|
14
7
|
export interface VerifyWriteResult {
|
|
15
8
|
verified: boolean;
|
|
16
9
|
missingChanges: VerifyWriteFailure[];
|
|
17
10
|
}
|
|
18
|
-
/** A change that could not be applied to spec.md body (Bug D). */
|
|
19
11
|
export interface SkippedChange {
|
|
20
12
|
section: string;
|
|
21
13
|
originalValue: string;
|
|
22
14
|
reason: string;
|
|
15
|
+
reasonCategory: 'skipped' | 'ambiguous';
|
|
23
16
|
}
|
|
24
|
-
/** Result of applyChangesToSpec (Bug D). */
|
|
25
17
|
export interface ApplyChangesResult {
|
|
26
18
|
applied: number;
|
|
27
19
|
skipped: SkippedChange[];
|
|
28
20
|
fileWritten: boolean;
|
|
29
21
|
}
|
|
30
|
-
|
|
22
|
+
export interface ApplyChangesToSpecFilesResult {
|
|
23
|
+
auditWritten: boolean;
|
|
24
|
+
}
|
|
31
25
|
export interface SectionBounds {
|
|
32
26
|
start: number;
|
|
33
27
|
end: number;
|
|
34
28
|
}
|
|
35
|
-
/** Parsed snapshot of status.json used by status-reconciler (Bug G). */
|
|
36
29
|
export interface CurrentStatus {
|
|
37
30
|
totalSpecs: number;
|
|
38
31
|
content: Record<string, unknown>;
|
|
39
|
-
/** True when status.json exists; false when missing (ENOENT). */
|
|
40
32
|
exists: boolean;
|
|
41
|
-
/** True when status.json existed but could not be parsed/read for non-ENOENT reasons. */
|
|
42
33
|
corrupt: boolean;
|
|
43
34
|
}
|
|
44
|
-
/** Result of reconcileStatusFromDisk (Bug G). */
|
|
45
35
|
export interface StatusReconcileResult {
|
|
46
36
|
reconciled: boolean;
|
|
47
37
|
before: number;
|
|
48
38
|
after: number;
|
|
49
39
|
}
|
|
50
40
|
import type { ProjectKnowledge } from './project/core.js';
|
|
51
|
-
/** Result when a project was resolved via resolveProjectFromPath (Bug F). */
|
|
52
41
|
export interface ResolvedProject {
|
|
53
42
|
projectId: string;
|
|
54
43
|
knowledge: ProjectKnowledge;
|
|
55
|
-
/** True when knowledge was recovered from disk fingerprint. */
|
|
56
44
|
recovered: boolean;
|
|
57
45
|
}
|
|
58
46
|
//# sourceMappingURL=reconcile.d.ts.map
|
package/dist/types/reconcile.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Sections whose values do not appear as literal text in spec.md body —
|
|
4
|
-
* applied/verified at the metadata layer, never via string-replace.
|
|
5
|
-
* Shared by apply-changes.ts (skip) and verify-write.ts (skip).
|
|
6
|
-
*/
|
|
7
|
-
export const RECONCILE_METADATA_SECTIONS = new Set([
|
|
1
|
+
export const SECTIONS_WITHOUT_LITERAL_BODY_TEXT = new Set([
|
|
8
2
|
'estimation',
|
|
9
3
|
'cost',
|
|
10
4
|
'status',
|
|
@@ -35,7 +35,12 @@ export interface SpecQualityReport {
|
|
|
35
35
|
risk: QualityDimensionDetail;
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
export type GenericSpecOutputIssueKind = 'generic-criterion' | 'unsupported-verification' | 'placeholder-reference' | 'generic-technical-reference'
|
|
38
|
+
export type GenericSpecOutputIssueKind = 'generic-criterion' | 'unsupported-verification' | 'placeholder-reference' | 'generic-technical-reference'
|
|
39
|
+
/** SPEC-1406: dedicated kind for structural-interpolation findings (checkInterpolatedSections).
|
|
40
|
+
* Kept distinct from 'generic-technical-reference' — severity routing in transition-guard.ts
|
|
41
|
+
* and sdd-rules-registry.ts filters BY KIND, so aliasing the two would silently downgrade
|
|
42
|
+
* whichever kind collides the day something else emits the shared value. */
|
|
43
|
+
| 'structural-interpolation';
|
|
39
44
|
export interface GenericSpecOutputIssue {
|
|
40
45
|
kind: GenericSpecOutputIssueKind;
|
|
41
46
|
phrase: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planu/cli",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.5",
|
|
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.
|
|
39
|
-
"@planu/core-darwin-x64": "5.3.
|
|
40
|
-
"@planu/core-linux-arm64-gnu": "5.3.
|
|
41
|
-
"@planu/core-linux-arm64-musl": "5.3.
|
|
42
|
-
"@planu/core-linux-x64-gnu": "5.3.
|
|
43
|
-
"@planu/core-linux-x64-musl": "5.3.
|
|
44
|
-
"@planu/core-win32-arm64-msvc": "5.3.
|
|
45
|
-
"@planu/core-win32-x64-msvc": "5.3.
|
|
38
|
+
"@planu/core-darwin-arm64": "5.3.5",
|
|
39
|
+
"@planu/core-darwin-x64": "5.3.5",
|
|
40
|
+
"@planu/core-linux-arm64-gnu": "5.3.5",
|
|
41
|
+
"@planu/core-linux-arm64-musl": "5.3.5",
|
|
42
|
+
"@planu/core-linux-x64-gnu": "5.3.5",
|
|
43
|
+
"@planu/core-linux-x64-musl": "5.3.5",
|
|
44
|
+
"@planu/core-win32-arm64-msvc": "5.3.5",
|
|
45
|
+
"@planu/core-win32-x64-msvc": "5.3.5"
|
|
46
46
|
},
|
|
47
47
|
"engines": {
|
|
48
48
|
"node": ">=24.0.0"
|
package/planu-native.json
CHANGED
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.
|
|
5
|
+
"version": "5.3.5",
|
|
6
6
|
"icon": "assets/plugin/icon.svg",
|
|
7
7
|
"command": ["npx", "@planu/cli@latest"],
|
|
8
8
|
"packageName": "@planu/cli",
|