@planu/cli 5.3.61 → 5.3.62
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 +9 -0
- package/dist/.planu-build.json +1 -1
- package/dist/engine/challenge-prioritizer.js +2 -1
- package/dist/tools/challenge-spec.js +1 -1
- package/dist/tools/update-status/dod-gates.js +20 -10
- package/dist/tools/update-status/file-sync.js +37 -5
- package/package.json +1 -1
- package/planu-plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [5.3.62] - 2026-08-26
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
- fix(SPEC-1639,1640,1641,1642): four dogfood defects in challenge, gates, coverage and sync
|
|
5
|
+
|
|
6
|
+
### Chores
|
|
7
|
+
- chore(planu): close SPEC-1639, SPEC-1640, SPEC-1641 and SPEC-1642
|
|
8
|
+
|
|
9
|
+
|
|
1
10
|
## [5.3.61] - 2026-08-26
|
|
2
11
|
|
|
3
12
|
### Refactoring
|
package/dist/.planu-build.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":1,"commit":"
|
|
1
|
+
{"schemaVersion":1,"commit":"653317c22ebdf21ad6b7f82f5d86dd042b05aaab"}
|
|
@@ -72,6 +72,7 @@ export function buildPrioritizedSummary(top3) {
|
|
|
72
72
|
return '';
|
|
73
73
|
}
|
|
74
74
|
const lines = top3.map((s) => `${s.rank}. ${s.scenario}`);
|
|
75
|
-
|
|
75
|
+
const noun = top3.length === 1 ? 'thing' : 'things';
|
|
76
|
+
return `Here are the ${String(top3.length)} ${noun} you MUST address before coding:\n${lines.join('\n')}`;
|
|
76
77
|
}
|
|
77
78
|
//# sourceMappingURL=challenge-prioritizer.js.map
|
|
@@ -314,7 +314,7 @@ export async function handleChallengeSpec(args, server) {
|
|
|
314
314
|
totalScenarios: failureScenarios.length,
|
|
315
315
|
actionableScenarios: failureScenariosScored.length,
|
|
316
316
|
suppressedLowRelevanceScenarios: suppressedScenarioCount + concurrencySuppressedCount,
|
|
317
|
-
shownByDefault:
|
|
317
|
+
shownByDefault: prioritizedBlocking.length,
|
|
318
318
|
mustAddressBeforeCoding: prioritizedSummary,
|
|
319
319
|
remainingAdvisories: advisoryIdentities,
|
|
320
320
|
gateStatus: gateState.gateStatus,
|
|
@@ -634,14 +634,20 @@ export async function checkSpecReviewGate(specId, projectId, _forceApprove) {
|
|
|
634
634
|
fixHint: 'Resolve the spec review blockers and have planu-spec-reviewer write updated evidence, then retry approval.',
|
|
635
635
|
});
|
|
636
636
|
}
|
|
637
|
-
|
|
638
|
-
|
|
637
|
+
const specReviewerBlockers = [];
|
|
638
|
+
if (review.reviewer.kind !== 'spec-review-agent') {
|
|
639
|
+
specReviewerBlockers.push(`reviewer.kind was ${review.reviewer.kind}. Expected spec-review-agent.`);
|
|
640
|
+
}
|
|
641
|
+
if (review.reviewer.agent !== 'planu-spec-reviewer') {
|
|
642
|
+
specReviewerBlockers.push(`reviewer.agent was ${review.reviewer.agent}. Expected planu-spec-reviewer.`);
|
|
643
|
+
}
|
|
644
|
+
if (specReviewerBlockers.length > 0) {
|
|
639
645
|
return specReviewGateError({
|
|
640
646
|
specId,
|
|
641
647
|
error: 'spec_review_wrong_reviewer',
|
|
642
648
|
message: 'Approval requires a dedicated spec reviewer. The implementation reviewer cannot approve the spec plan.',
|
|
643
|
-
blockers:
|
|
644
|
-
fixHint: 'Have planu-spec-reviewer
|
|
649
|
+
blockers: specReviewerBlockers,
|
|
650
|
+
fixHint: 'Have planu-spec-reviewer write the review artifact with reviewer.kind spec-review-agent and reviewer.agent planu-spec-reviewer, then retry approval.',
|
|
645
651
|
});
|
|
646
652
|
}
|
|
647
653
|
return null;
|
|
@@ -698,16 +704,20 @@ export async function checkImplementationReviewGate(specId, projectId, _force) {
|
|
|
698
704
|
fixHint: 'Resolve the implementation review blockers and have planu-implementation-reviewer write updated evidence, then retry done.',
|
|
699
705
|
});
|
|
700
706
|
}
|
|
701
|
-
|
|
702
|
-
|
|
707
|
+
const implementationReviewerBlockers = [];
|
|
708
|
+
if (review.reviewer.kind !== 'implementation-review-agent') {
|
|
709
|
+
implementationReviewerBlockers.push(`reviewer.kind was ${review.reviewer.kind}. Expected implementation-review-agent.`);
|
|
710
|
+
}
|
|
711
|
+
if (review.reviewer.agent !== 'planu-implementation-reviewer') {
|
|
712
|
+
implementationReviewerBlockers.push(`reviewer.agent was ${review.reviewer.agent}. Expected planu-implementation-reviewer.`);
|
|
713
|
+
}
|
|
714
|
+
if (implementationReviewerBlockers.length > 0) {
|
|
703
715
|
return implementationReviewGateError({
|
|
704
716
|
specId,
|
|
705
717
|
error: 'implementation_review_wrong_reviewer',
|
|
706
718
|
message: 'Done requires the recognized independent implementation reviewer identity.',
|
|
707
|
-
blockers:
|
|
708
|
-
|
|
709
|
-
],
|
|
710
|
-
fixHint: 'Have planu-implementation-reviewer (with the recognized identity) write the review artifact, then retry done.',
|
|
719
|
+
blockers: implementationReviewerBlockers,
|
|
720
|
+
fixHint: 'Have planu-implementation-reviewer write the review artifact with reviewer.kind implementation-review-agent and reviewer.agent planu-implementation-reviewer, then retry done.',
|
|
711
721
|
});
|
|
712
722
|
}
|
|
713
723
|
let implementerActor;
|
|
@@ -250,13 +250,45 @@ function isLeanSpecContent(content) {
|
|
|
250
250
|
* SPEC-630: Remove done criteria entries from lean spec and annotate the count on the criteria key.
|
|
251
251
|
* Only call this after markAllCriteriaDone — assumes all completed criteria have `done: true`.
|
|
252
252
|
*/
|
|
253
|
+
function indentWidth(line) {
|
|
254
|
+
return /^[ \t]*/.exec(line)?.[0].length ?? 0;
|
|
255
|
+
}
|
|
256
|
+
function isNestedUnder(line, itemIndent) {
|
|
257
|
+
return line.trim().length === 0 || indentWidth(line) > itemIndent.length;
|
|
258
|
+
}
|
|
259
|
+
function isDoneTrueField(line, fieldIndent) {
|
|
260
|
+
return new RegExp(`^${fieldIndent}done:[ \\t]*true[ \\t]*(?:#.*)?$`).test(line);
|
|
261
|
+
}
|
|
253
262
|
function stripDoneCriteria(content) {
|
|
263
|
+
const lines = content.split('\n');
|
|
264
|
+
const kept = [];
|
|
254
265
|
let doneCount = 0;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
266
|
+
let index = 0;
|
|
267
|
+
while (index < lines.length) {
|
|
268
|
+
const line = lines[index] ?? '';
|
|
269
|
+
const itemIndent = /^([ \t]+)- text:/.exec(line)?.[1];
|
|
270
|
+
if (itemIndent === undefined) {
|
|
271
|
+
kept.push(line);
|
|
272
|
+
index += 1;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
let end = index + 1;
|
|
276
|
+
while (end < lines.length && isNestedUnder(lines[end] ?? '', itemIndent)) {
|
|
277
|
+
end += 1;
|
|
278
|
+
}
|
|
279
|
+
const item = lines.slice(index + 1, end);
|
|
280
|
+
const firstField = item.find((entry) => entry.trim().length > 0);
|
|
281
|
+
const fieldIndent = firstField === undefined ? '' : (/^[ \t]*/.exec(firstField)?.[0] ?? '');
|
|
282
|
+
const isDone = firstField !== undefined && item.some((entry) => isDoneTrueField(entry, fieldIndent));
|
|
283
|
+
if (isDone) {
|
|
284
|
+
doneCount += 1;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
kept.push(line, ...item);
|
|
288
|
+
}
|
|
289
|
+
index = end;
|
|
290
|
+
}
|
|
291
|
+
return kept.join('\n').replace(/^(criteria:)(\s*#.*)?$/m, `$1 # ${String(doneCount)} done`);
|
|
260
292
|
}
|
|
261
293
|
export async function tryReconcile(newStatus, specId, projectId) {
|
|
262
294
|
if (newStatus !== 'done') {
|
package/package.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.62",
|
|
6
6
|
"icon": "assets/plugin/icon.svg",
|
|
7
7
|
"command": ["npx", "@planu/cli@latest"],
|
|
8
8
|
"packageName": "@planu/cli",
|