@planu/cli 5.4.0 → 5.4.1
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
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [5.4.1] - 2026-08-29
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
- fix(SPEC-1675): strip FILES/FUNCTIONS/TEST metadata markers from contradiction analysis
|
|
5
|
+
- fix(SPEC-1676): rebuild expired project knowledge graph and re-stamp cached reuse
|
|
6
|
+
|
|
7
|
+
|
|
1
8
|
## [5.4.0] - 2026-08-29
|
|
2
9
|
|
|
3
10
|
### Features
|
package/dist/.planu-build.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":1,"commit":"
|
|
1
|
+
{"schemaVersion":1,"commit":"c920f236390f64d91a02ca3e0566e59210f00a49"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { collectProjectGraphSources, diffSourceHashes, hashText, hydrateProjectGraphSource, loadProjectGraphPolicy, projectGraphCachePath, projectGraphExtractorVersion, projectGraphPath, readProjectGraphArtifact, readProjectGraphCache, withProjectGraphBuildLock, writeProjectGraphArtifacts, } from './cache.js';
|
|
2
|
+
import { collectProjectGraphSources, diffSourceHashes, hashText, hydrateProjectGraphSource, loadProjectGraphPolicy, projectGraphCachePath, projectGraphExtractorVersion, projectGraphPath, readProjectGraphArtifact, readProjectGraphCache, withProjectGraphBuildLock, writeProjectGraph, writeProjectGraphArtifacts, } from './cache.js';
|
|
3
3
|
import { extractGitGraph, extractReleaseGraph } from './extractors/git-extractor.js';
|
|
4
4
|
import { extractHandoffGraph } from './extractors/handoff-extractor.js';
|
|
5
5
|
import { extractDecisionStoreGraph } from './extractors/decision-store-extractor.js';
|
|
@@ -210,13 +210,16 @@ async function buildProjectKnowledgeGraphUnlocked(args) {
|
|
|
210
210
|
Array.isArray(existingGraph.nodes) &&
|
|
211
211
|
Array.isArray(existingGraph.edges);
|
|
212
212
|
if (hasConsistentExistingGraph && diff.changed.length === 0 && diff.removed.length === 0) {
|
|
213
|
+
const refreshedGeneratedAt = new Date().toISOString();
|
|
213
214
|
const graphWithCurrentOversizedSources = {
|
|
214
215
|
...existingGraph,
|
|
216
|
+
generatedAt: refreshedGeneratedAt,
|
|
215
217
|
oversizedSources,
|
|
216
218
|
};
|
|
219
|
+
await writeProjectGraph(args.projectId, args.policy, graphWithCurrentOversizedSources);
|
|
217
220
|
publishFreshProjectGraphProjection({
|
|
218
221
|
projectId: args.projectId,
|
|
219
|
-
generatedAt:
|
|
222
|
+
generatedAt: refreshedGeneratedAt,
|
|
220
223
|
generation: existingGraph.generation,
|
|
221
224
|
});
|
|
222
225
|
return {
|
|
@@ -55,7 +55,8 @@ async function ensureFreshProjectGraph(args) {
|
|
|
55
55
|
const freshness = await getProjectGraphFreshness(args);
|
|
56
56
|
if (!freshness.exists ||
|
|
57
57
|
freshness.reason === 'source_changed' ||
|
|
58
|
-
freshness.reason === 'corrupt'
|
|
58
|
+
freshness.reason === 'corrupt' ||
|
|
59
|
+
freshness.reason === 'expired') {
|
|
59
60
|
await buildProjectKnowledgeGraph(args);
|
|
60
61
|
return getProjectGraphFreshness(args);
|
|
61
62
|
}
|
|
@@ -203,14 +203,35 @@ function assertsActionNearScope(clause, outOfScopeItem) {
|
|
|
203
203
|
}
|
|
204
204
|
return false;
|
|
205
205
|
}
|
|
206
|
+
const METADATA_MARKER_KEYWORD = /\b(?:FILES?|FUNCTIONS?|TESTS?)\s*:/gi;
|
|
207
|
+
const SENTENCE_BOUNDARY_AFTER_MARKER = /\.\s+(?=[A-Z])/;
|
|
208
|
+
function stripMetadataMarkers(text) {
|
|
209
|
+
const markers = [...text.matchAll(METADATA_MARKER_KEYWORD)];
|
|
210
|
+
if (markers.length === 0) {
|
|
211
|
+
return text;
|
|
212
|
+
}
|
|
213
|
+
let result = '';
|
|
214
|
+
let cursor = 0;
|
|
215
|
+
for (let i = 0; i < markers.length; i++) {
|
|
216
|
+
const start = markers[i]?.index ?? 0;
|
|
217
|
+
result += text.slice(cursor, start);
|
|
218
|
+
const nextMarkerStart = markers[i + 1]?.index ?? text.length;
|
|
219
|
+
const searchRegion = text.slice(start, nextMarkerStart);
|
|
220
|
+
const boundaryMatch = SENTENCE_BOUNDARY_AFTER_MARKER.exec(searchRegion);
|
|
221
|
+
cursor = boundaryMatch ? start + boundaryMatch.index + 1 : nextMarkerStart;
|
|
222
|
+
}
|
|
223
|
+
result += text.slice(cursor);
|
|
224
|
+
return result.replace(/\s+/g, ' ').trim();
|
|
225
|
+
}
|
|
206
226
|
/**
|
|
207
227
|
* Determine whether a criterion text contradicts an out-of-scope item.
|
|
208
228
|
* Uses substring match first, then keyword overlap as fuzzy fallback.
|
|
209
229
|
*/
|
|
210
230
|
function contradicts(criterionText, outOfScopeItem) {
|
|
211
|
-
const
|
|
231
|
+
const strippedCriterionText = stripMetadataMarkers(criterionText);
|
|
232
|
+
const normCriterion = normalize(strippedCriterionText);
|
|
212
233
|
const normScope = normalize(outOfScopeItem);
|
|
213
|
-
const clauses = splitClauses(
|
|
234
|
+
const clauses = splitClauses(strippedCriterionText);
|
|
214
235
|
const relevantClauses = clauses.filter((clause) => clauseMentionsScope(clause, outOfScopeItem));
|
|
215
236
|
const hasConditionalException = /\b(?:unless|except)\b/i.test(criterionText);
|
|
216
237
|
if (!hasConditionalException &&
|
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.4.
|
|
5
|
+
"version": "5.4.1",
|
|
6
6
|
"icon": "assets/plugin/icon.svg",
|
|
7
7
|
"command": ["npx", "@planu/cli@latest"],
|
|
8
8
|
"packageName": "@planu/cli",
|