roadmapsmith 0.9.4 → 0.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roadmapsmith",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "Evidence-backed ROADMAP.md generator and sync tool for AI coding agents.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -333,6 +333,38 @@ function findFilesByTaskPathTokens(taskText, fileIndex, pathDerivedTokens = new
333
333
  return Array.from(matches).sort((left, right) => left.localeCompare(right));
334
334
  }
335
335
 
336
+ function extractTaskEvidenceTokens(taskText, pathDerivedTokens = new Set()) {
337
+ return tokenize(taskText)
338
+ .filter((token) => token.length >= 3 && !GENERIC_TASK_TOKENS.has(token) && !token.endsWith('/') && !pathDerivedTokens.has(token))
339
+ .slice(0, 8);
340
+ }
341
+
342
+ function findWeakPathContentSpecificTokens(taskText, fileIndex, weakPathFiles, pathDerivedTokens = new Set()) {
343
+ const tokens = extractTaskEvidenceTokens(taskText, pathDerivedTokens);
344
+ if (tokens.length === 0 || weakPathFiles.length === 0) return [];
345
+
346
+ const weakFiles = new Set(weakPathFiles);
347
+ const matches = new Set();
348
+ for (const file of fileIndex) {
349
+ if (!weakFiles.has(file.relativePath) || !CODE_EXTENSIONS.has(file.ext) || file.isTestFile) {
350
+ continue;
351
+ }
352
+
353
+ const normalizedPath = normalizePathForMatch(file.relativePath);
354
+ const lowered = file.content.toLowerCase();
355
+ for (const token of tokens) {
356
+ if (normalizedPath.includes(token)) {
357
+ continue;
358
+ }
359
+ if (lowered.includes(token)) {
360
+ matches.add(token);
361
+ }
362
+ }
363
+ }
364
+
365
+ return Array.from(matches).sort((left, right) => left.localeCompare(right));
366
+ }
367
+
336
368
  function mergeRuleEvidence(baseEvidence, ruleEvidence) {
337
369
  if (!ruleEvidence || typeof ruleEvidence !== 'object') return baseEvidence;
338
370
  const merged = { ...baseEvidence };
@@ -376,9 +408,7 @@ function extractPathDerivedTokens(pathHints) {
376
408
  }
377
409
 
378
410
  function findCodeEvidence(taskText, fileIndex, pathDerivedTokens = new Set()) {
379
- const tokens = tokenize(taskText)
380
- .filter((token) => token.length >= 3 && !GENERIC_TASK_TOKENS.has(token) && !token.endsWith('/') && !pathDerivedTokens.has(token))
381
- .slice(0, 8);
411
+ const tokens = extractTaskEvidenceTokens(taskText, pathDerivedTokens);
382
412
  if (tokens.length === 0) {
383
413
  return [];
384
414
  }
@@ -750,6 +780,7 @@ function validateTask(task, context, config, plugins) {
750
780
  const pathDerivedTokens = extractPathDerivedTokens([...pathHints, ...standaloneFilenames]);
751
781
  const filesFromCode = findCodeEvidence(task.text, context.fileIndex, pathDerivedTokens);
752
782
  const filesFromWeakPathTokens = findFilesByTaskPathTokens(task.text, context.fileIndex, pathDerivedTokens);
783
+ const weakPathContentTokens = findWeakPathContentSpecificTokens(task.text, context.fileIndex, filesFromWeakPathTokens, pathDerivedTokens);
753
784
  const filesFromTests = findTestEvidence(task.text, context.fileIndex, [...pathHints, ...standaloneFilenames]);
754
785
  const { files: filesFromArtifacts, heuristicArtifacts } = findArtifactEvidence(task.text, context.fileIndex);
755
786
 
@@ -763,6 +794,7 @@ function validateTask(task, context, config, plugins) {
763
794
  symbols: filesFromSymbols,
764
795
  codeFiles: filesFromCode,
765
796
  weakPathFiles: filesFromWeakPathTokens,
797
+ weakPathContentTokens,
766
798
  testFiles: filesFromTests,
767
799
  artifactFiles: filesFromArtifacts,
768
800
  heuristicArtifacts,
@@ -790,6 +822,12 @@ function validateTask(task, context, config, plugins) {
790
822
  reasons.push('no code, test, or artifact evidence found');
791
823
  } else if (!hasEvidence && !hasWeakEvidence && structuralCheck.applicable && structuralCheck.passed) {
792
824
  reasons.push('no code, test, or artifact evidence found');
825
+ } else if (!hasEvidence && hasWeakEvidence) {
826
+ if (weakPathContentTokens.length === 0) {
827
+ reasons.push('weak path-only evidence lacks content-specific token match');
828
+ } else {
829
+ reasons.push('weak path-token evidence lacks strong code, test, or artifact evidence');
830
+ }
793
831
  }
794
832
 
795
833
  const requiresTest = !task.noTest && context.testFrameworks.length > 0 && isCodeTask(task.text) && !isDocTask(task.text);