@planu/cli 4.10.10 ā 4.10.12
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
CHANGED
|
@@ -2,8 +2,9 @@ import { appendArtifact, readArtifact } from '../handoff-artifacts/io.js';
|
|
|
2
2
|
import { runSpecCompliance, } from './spec-compliance-runner.js';
|
|
3
3
|
export async function writeImplementationReviewReport(input) {
|
|
4
4
|
const specCompliance = await runSpecCompliance(input.spec, input.projectPath);
|
|
5
|
+
const reportScore = resolveExecutableValidationScore(input.score, specCompliance);
|
|
5
6
|
const gates = buildGates({
|
|
6
|
-
score:
|
|
7
|
+
score: reportScore,
|
|
7
8
|
lintPassed: input.lintPassed,
|
|
8
9
|
conventionRegression: input.conventionRegression ?? false,
|
|
9
10
|
specCompliance,
|
|
@@ -25,7 +26,7 @@ export async function writeImplementationReviewReport(input) {
|
|
|
25
26
|
reviewer,
|
|
26
27
|
specCompliance: reportCompliance,
|
|
27
28
|
minimalityReport: input.minimalityReport,
|
|
28
|
-
score:
|
|
29
|
+
score: reportScore ?? undefined,
|
|
29
30
|
completedAt: new Date().toISOString(),
|
|
30
31
|
};
|
|
31
32
|
try {
|
|
@@ -56,6 +57,9 @@ export async function writeImplementationReviewReport(input) {
|
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
}
|
|
60
|
+
function resolveExecutableValidationScore(score, specCompliance) {
|
|
61
|
+
return specCompliance.perScenario.length > 0 ? specCompliance.dimensionScore : score;
|
|
62
|
+
}
|
|
59
63
|
async function preserveUnobservedGates(input, nextGates) {
|
|
60
64
|
const currentGateNames = new Set(nextGates.map((gate) => gate.name));
|
|
61
65
|
const explicitlyObserved = new Set([
|
|
@@ -6,7 +6,7 @@ import { dirname, join } from 'node:path';
|
|
|
6
6
|
import { formatLintFailureDiagnostics, resolveProjectCommandPlan, runProjectCommandPlan, } from './validate-runtime.js';
|
|
7
7
|
/** Allowlist: alphanumerics, spaces, and safe shell chars. Rejects ; | $ ` & < > */
|
|
8
8
|
const SAFE_COMMAND_RE = /^[\w\s./:@~=-]+$/;
|
|
9
|
-
const DEFAULT_LINT_CHECK_TIMEOUT_MS =
|
|
9
|
+
const DEFAULT_LINT_CHECK_TIMEOUT_MS = 60_000;
|
|
10
10
|
const LINT_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
11
11
|
function isSafeCommand(cmd) {
|
|
12
12
|
return SAFE_COMMAND_RE.test(cmd);
|
package/dist/tools/validate.js
CHANGED
|
@@ -99,23 +99,24 @@ export async function handleValidate(args, server) {
|
|
|
99
99
|
conventionRegression: regressionDetected,
|
|
100
100
|
minimalityReport: minimalityReport ?? undefined,
|
|
101
101
|
});
|
|
102
|
+
const effectiveResult = buildEffectiveValidationResult(result, validationReport.specCompliance);
|
|
102
103
|
const qualityGateSummary = buildQualityGateSummary({
|
|
103
104
|
conventionViolations,
|
|
104
105
|
regressionDetected,
|
|
105
106
|
lintPassed: lintCheck.passed,
|
|
106
107
|
assurancePassed: assuranceGates.newCode.passed,
|
|
107
108
|
minimalityBlocked: minimalityReport?.blocked === true,
|
|
108
|
-
score:
|
|
109
|
+
score: effectiveResult.score,
|
|
109
110
|
});
|
|
110
111
|
const output = {
|
|
111
112
|
specId,
|
|
112
113
|
title: spec.title,
|
|
113
|
-
score:
|
|
114
|
-
fieldsImplemented:
|
|
115
|
-
fieldsTotal:
|
|
116
|
-
matches:
|
|
117
|
-
missing:
|
|
118
|
-
extra:
|
|
114
|
+
score: effectiveResult.score,
|
|
115
|
+
fieldsImplemented: effectiveResult.fieldsImplemented,
|
|
116
|
+
fieldsTotal: effectiveResult.fieldsTotal,
|
|
117
|
+
matches: effectiveResult.matches,
|
|
118
|
+
missing: effectiveResult.missing,
|
|
119
|
+
extra: effectiveResult.extra,
|
|
119
120
|
qualityIssues: result.qualityIssues.map((issue) => ({
|
|
120
121
|
file: issue.file,
|
|
121
122
|
line: issue.line,
|
|
@@ -202,7 +203,7 @@ export async function handleValidate(args, server) {
|
|
|
202
203
|
}
|
|
203
204
|
// Auto-suggest corrective actions based on validation results
|
|
204
205
|
// score is null when no criteria can be extracted ā treat as 0 for comparisons.
|
|
205
|
-
const scoreValue =
|
|
206
|
+
const scoreValue = effectiveResult.score ?? 0;
|
|
206
207
|
const suggestions = [];
|
|
207
208
|
if (scoreValue < 50) {
|
|
208
209
|
suggestions.push('Critical: Many acceptance criteria not implemented. Review spec requirements.');
|
|
@@ -216,8 +217,8 @@ export async function handleValidate(args, server) {
|
|
|
216
217
|
if (!dod.isDone && spec.status === 'implementing') {
|
|
217
218
|
suggestions.push('Implementation incomplete. Review DoD checklist before marking as done.');
|
|
218
219
|
}
|
|
219
|
-
if (
|
|
220
|
-
suggestions.push(`${
|
|
220
|
+
if (effectiveResult.missing.length > 0) {
|
|
221
|
+
suggestions.push(`${effectiveResult.missing.length} criteria still missing. Run detect_drift to identify gaps.`);
|
|
221
222
|
}
|
|
222
223
|
if (graphCoverage.gaps.length > 0) {
|
|
223
224
|
suggestions.push(`${graphCoverage.gaps.length} graph-backed coverage gap(s) found without broad repository scan.`);
|
|
@@ -243,8 +244,8 @@ export async function handleValidate(args, server) {
|
|
|
243
244
|
specId,
|
|
244
245
|
input: { specType: spec.type, scope: spec.scope },
|
|
245
246
|
outcome: {
|
|
246
|
-
score:
|
|
247
|
-
missingCount:
|
|
247
|
+
score: effectiveResult.score,
|
|
248
|
+
missingCount: effectiveResult.missing.length,
|
|
248
249
|
qualityIssueCount: result.qualityIssues.length,
|
|
249
250
|
dorPassed: dor.isReady,
|
|
250
251
|
dodPassed: dod.isDone,
|
|
@@ -261,7 +262,7 @@ export async function handleValidate(args, server) {
|
|
|
261
262
|
type: 'validate',
|
|
262
263
|
specId,
|
|
263
264
|
input: { trigger: 'auto-quality-learn', rules: topIssueRules },
|
|
264
|
-
outcome: { score:
|
|
265
|
+
outcome: { score: effectiveResult.score, antipatterns: topIssueRules },
|
|
265
266
|
delta: { qualityGap: 100 - scoreValue },
|
|
266
267
|
timestamp: new Date().toISOString(),
|
|
267
268
|
}).catch((err) => {
|
|
@@ -277,10 +278,10 @@ export async function handleValidate(args, server) {
|
|
|
277
278
|
? '\n\nš HTML reports updated ā commit to save: `git add planu/specs/ planu/conventions.json && git commit -m "chore(planu): update reports"`'
|
|
278
279
|
: '';
|
|
279
280
|
// SPEC-512: Compact output ā only surface failures, cap to 200 tokens
|
|
280
|
-
const totalCriteria =
|
|
281
|
-
const passedCount =
|
|
281
|
+
const totalCriteria = effectiveResult.fieldsTotal;
|
|
282
|
+
const passedCount = effectiveResult.fieldsImplemented;
|
|
282
283
|
const failedCount = totalCriteria - passedCount;
|
|
283
|
-
const indeterminate =
|
|
284
|
+
const indeterminate = effectiveResult.indeterminate ?? [];
|
|
284
285
|
const effectiveScore = qualityGateSummary.effectiveScore ?? scoreValue;
|
|
285
286
|
const compactText = buildCompactValidateText({
|
|
286
287
|
score: effectiveScore,
|
|
@@ -288,20 +289,20 @@ export async function handleValidate(args, server) {
|
|
|
288
289
|
failedCount,
|
|
289
290
|
totalCriteria,
|
|
290
291
|
qualityFailures: qualityGateSummary.failures,
|
|
291
|
-
missing:
|
|
292
|
+
missing: effectiveResult.missing,
|
|
292
293
|
indeterminate,
|
|
293
294
|
commitReminder,
|
|
294
295
|
planuReminder,
|
|
295
296
|
});
|
|
296
297
|
const graphText = formatGraphCoverageText(graphCoverage);
|
|
297
298
|
// SPEC-512: Compact structuredContent ā essential fields only at top level
|
|
298
|
-
const compactSummary = buildCompactSummary(specId,
|
|
299
|
+
const compactSummary = buildCompactSummary(specId, effectiveResult.score === null ? null : effectiveScore, passedCount, failedCount + (qualityGateSummary.passed ? 0 : 1), totalCriteria);
|
|
299
300
|
const structuredBase = buildValidateStructuredContent({
|
|
300
301
|
specId,
|
|
301
302
|
title: spec.title,
|
|
302
|
-
score:
|
|
303
|
+
score: effectiveResult.score === null ? null : effectiveScore,
|
|
303
304
|
compactSummary,
|
|
304
|
-
result,
|
|
305
|
+
result: effectiveResult,
|
|
305
306
|
indeterminate,
|
|
306
307
|
dor,
|
|
307
308
|
dod,
|
|
@@ -384,7 +385,6 @@ function buildQualityGateSummary(input) {
|
|
|
384
385
|
const blockingConventionViolations = input.conventionViolations.filter((violation) => violation.severity === 'critical');
|
|
385
386
|
const failures = [
|
|
386
387
|
...(input.regressionDetected ? ['convention-regression'] : []),
|
|
387
|
-
...(blockingConventionViolations.length > 0 ? ['blocking-convention-violations'] : []),
|
|
388
388
|
...(!input.lintPassed ? ['lint'] : []),
|
|
389
389
|
...(!input.assurancePassed ? ['assurance'] : []),
|
|
390
390
|
...(input.minimalityBlocked ? ['minimality'] : []),
|
|
@@ -400,6 +400,40 @@ function buildQualityGateSummary(input) {
|
|
|
400
400
|
minimalityPassed: !input.minimalityBlocked,
|
|
401
401
|
};
|
|
402
402
|
}
|
|
403
|
+
function buildExecutableCoverage(specCompliance) {
|
|
404
|
+
if (specCompliance.scenarios.length === 0) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
const matches = specCompliance.scenarios
|
|
408
|
+
.filter((scenario) => scenario.verdict === 'pass')
|
|
409
|
+
.map((scenario) => scenario.title);
|
|
410
|
+
const missing = specCompliance.scenarios
|
|
411
|
+
.filter((scenario) => scenario.verdict !== 'pass')
|
|
412
|
+
.map((scenario) => scenario.title);
|
|
413
|
+
return {
|
|
414
|
+
score: specCompliance.score,
|
|
415
|
+
fieldsImplemented: matches.length,
|
|
416
|
+
fieldsTotal: specCompliance.scenarios.length,
|
|
417
|
+
matches,
|
|
418
|
+
missing,
|
|
419
|
+
indeterminate: [],
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function buildEffectiveValidationResult(result, specCompliance) {
|
|
423
|
+
const executableCoverage = buildExecutableCoverage(specCompliance);
|
|
424
|
+
if (executableCoverage === null) {
|
|
425
|
+
return result;
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
...result,
|
|
429
|
+
score: executableCoverage.score,
|
|
430
|
+
fieldsImplemented: executableCoverage.fieldsImplemented,
|
|
431
|
+
fieldsTotal: executableCoverage.fieldsTotal,
|
|
432
|
+
matches: executableCoverage.matches,
|
|
433
|
+
missing: executableCoverage.missing,
|
|
434
|
+
indeterminate: executableCoverage.indeterminate,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
403
437
|
function buildCompactSummary(specId, score, passedCount, failedCount, totalCriteria) {
|
|
404
438
|
return {
|
|
405
439
|
specId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planu/cli",
|
|
3
|
-
"version": "4.10.
|
|
3
|
+
"version": "4.10.12",
|
|
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.10.
|
|
38
|
-
"@planu/core-darwin-x64": "4.10.
|
|
39
|
-
"@planu/core-linux-arm64-gnu": "4.10.
|
|
40
|
-
"@planu/core-linux-arm64-musl": "4.10.
|
|
41
|
-
"@planu/core-linux-x64-gnu": "4.10.
|
|
42
|
-
"@planu/core-linux-x64-musl": "4.10.
|
|
43
|
-
"@planu/core-win32-arm64-msvc": "4.10.
|
|
44
|
-
"@planu/core-win32-x64-msvc": "4.10.
|
|
37
|
+
"@planu/core-darwin-arm64": "4.10.12",
|
|
38
|
+
"@planu/core-darwin-x64": "4.10.12",
|
|
39
|
+
"@planu/core-linux-arm64-gnu": "4.10.12",
|
|
40
|
+
"@planu/core-linux-arm64-musl": "4.10.12",
|
|
41
|
+
"@planu/core-linux-x64-gnu": "4.10.12",
|
|
42
|
+
"@planu/core-linux-x64-musl": "4.10.12",
|
|
43
|
+
"@planu/core-win32-arm64-msvc": "4.10.12",
|
|
44
|
+
"@planu/core-win32-x64-msvc": "4.10.12"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"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": "4.10.
|
|
5
|
+
"version": "4.10.12",
|
|
6
6
|
"icon": "assets/plugin/icon.svg",
|
|
7
7
|
"command": [
|
|
8
8
|
"npx",
|