roadmapsmith 0.6.0 → 0.7.0
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/bin/cli.js +35 -3
- package/package.json +11 -3
- package/src/generator/index.js +563 -562
- package/src/model.js +1 -0
- package/src/renderer/professional.js +536 -476
- package/src/validator/index.js +48 -9
package/src/validator/index.js
CHANGED
|
@@ -6,6 +6,8 @@ const { walkFiles, detectTestFrameworks } = require('../io');
|
|
|
6
6
|
const { collectPluginContributions } = require('../config');
|
|
7
7
|
const { escapeRegExp, tokenize } = require('../utils');
|
|
8
8
|
|
|
9
|
+
const CONFIDENCE_RANK = { low: 0, medium: 1, high: 2 };
|
|
10
|
+
|
|
9
11
|
const CODE_EXTENSIONS = new Set([
|
|
10
12
|
'.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.py', '.go', '.rs', '.java', '.kt', '.swift', '.rb', '.php', '.cs'
|
|
11
13
|
]);
|
|
@@ -29,6 +31,13 @@ const GENERIC_TASK_TOKENS = new Set([
|
|
|
29
31
|
'tests'
|
|
30
32
|
]);
|
|
31
33
|
|
|
34
|
+
const CANONICAL_FILES = {
|
|
35
|
+
security: 'SECURITY.md',
|
|
36
|
+
readme: 'README.md',
|
|
37
|
+
changelog: 'CHANGELOG.md',
|
|
38
|
+
license: 'LICENSE'
|
|
39
|
+
};
|
|
40
|
+
|
|
32
41
|
function readFileIndex(projectRoot, files) {
|
|
33
42
|
const index = [];
|
|
34
43
|
for (const relativePath of files) {
|
|
@@ -213,10 +222,23 @@ function findTestEvidence(taskText, fileIndex) {
|
|
|
213
222
|
|
|
214
223
|
function findArtifactEvidence(taskText, fileIndex) {
|
|
215
224
|
const normalized = String(taskText).toLowerCase();
|
|
216
|
-
const
|
|
225
|
+
const files = [];
|
|
226
|
+
const heuristicArtifacts = [];
|
|
227
|
+
|
|
228
|
+
for (const [keyword, filename] of Object.entries(CANONICAL_FILES)) {
|
|
229
|
+
if (normalized.includes(keyword)) {
|
|
230
|
+
const hit = fileIndex.find(
|
|
231
|
+
(f) => f.relativePath === filename || f.relativePath.endsWith('/' + filename)
|
|
232
|
+
);
|
|
233
|
+
if (hit) {
|
|
234
|
+
files.push(hit.relativePath);
|
|
235
|
+
heuristicArtifacts.push(hit.relativePath);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
217
239
|
|
|
218
240
|
if (!isDocTask(taskText) && !normalized.includes('artifact') && !normalized.includes('release')) {
|
|
219
|
-
return
|
|
241
|
+
return { files, heuristicArtifacts };
|
|
220
242
|
}
|
|
221
243
|
|
|
222
244
|
const artifactPatterns = [
|
|
@@ -229,12 +251,12 @@ function findArtifactEvidence(taskText, fileIndex) {
|
|
|
229
251
|
];
|
|
230
252
|
|
|
231
253
|
for (const file of fileIndex) {
|
|
232
|
-
if (artifactPatterns.some((pattern) => pattern.test(file.relativePath))) {
|
|
233
|
-
|
|
254
|
+
if (artifactPatterns.some((pattern) => pattern.test(file.relativePath)) && !files.includes(file.relativePath)) {
|
|
255
|
+
files.push(file.relativePath);
|
|
234
256
|
}
|
|
235
257
|
}
|
|
236
258
|
|
|
237
|
-
return
|
|
259
|
+
return { files: files.slice(0, 20), heuristicArtifacts };
|
|
238
260
|
}
|
|
239
261
|
|
|
240
262
|
function evaluateRule(rule, task, context) {
|
|
@@ -326,7 +348,7 @@ function validateTask(task, context, config, plugins) {
|
|
|
326
348
|
const filesFromSymbols = findFilesBySymbols(symbolHints, context.fileIndex);
|
|
327
349
|
const filesFromCode = findCodeEvidence(task.text, context.fileIndex);
|
|
328
350
|
const filesFromTests = findTestEvidence(task.text, context.fileIndex);
|
|
329
|
-
const filesFromArtifacts = findArtifactEvidence(task.text, context.fileIndex);
|
|
351
|
+
const { files: filesFromArtifacts, heuristicArtifacts } = findArtifactEvidence(task.text, context.fileIndex);
|
|
330
352
|
|
|
331
353
|
const evidence = {
|
|
332
354
|
code: filesFromCode.length > 0 || filesFromSymbols.length > 0,
|
|
@@ -336,7 +358,8 @@ function validateTask(task, context, config, plugins) {
|
|
|
336
358
|
symbols: filesFromSymbols,
|
|
337
359
|
codeFiles: filesFromCode,
|
|
338
360
|
testFiles: filesFromTests,
|
|
339
|
-
artifactFiles: filesFromArtifacts
|
|
361
|
+
artifactFiles: filesFromArtifacts,
|
|
362
|
+
heuristicArtifacts
|
|
340
363
|
};
|
|
341
364
|
|
|
342
365
|
const reasons = [];
|
|
@@ -370,7 +393,7 @@ function validateTask(task, context, config, plugins) {
|
|
|
370
393
|
const attempted = hasEvidence || pathHints.length > 0 || symbolHints.length > 0;
|
|
371
394
|
|
|
372
395
|
const evidenceCount = [evidence.code, evidence.test, evidence.artifact].filter(Boolean).length;
|
|
373
|
-
const confidence = evidenceCount >= 2 ? 'high' : evidenceCount === 1 ? 'medium' :
|
|
396
|
+
const confidence = evidenceCount >= 2 ? 'high' : evidenceCount === 1 ? 'medium' : 'low';
|
|
374
397
|
|
|
375
398
|
return {
|
|
376
399
|
taskId: task.id,
|
|
@@ -417,9 +440,25 @@ function auditValidation(tasks, results) {
|
|
|
417
440
|
};
|
|
418
441
|
}
|
|
419
442
|
|
|
443
|
+
function applyMinimumConfidence(results, minimumConfidence) {
|
|
444
|
+
const minRank = CONFIDENCE_RANK[minimumConfidence] ?? 0;
|
|
445
|
+
if (minRank === 0) return;
|
|
446
|
+
for (const result of Object.values(results)) {
|
|
447
|
+
if ((CONFIDENCE_RANK[result.confidence] ?? 0) < minRank) {
|
|
448
|
+
result.passed = false;
|
|
449
|
+
result.reasons = [
|
|
450
|
+
...result.reasons,
|
|
451
|
+
`validation confidence "${result.confidence}" is below required "${minimumConfidence}"`
|
|
452
|
+
];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
420
457
|
module.exports = {
|
|
421
458
|
auditValidation,
|
|
422
459
|
buildValidationContext,
|
|
423
460
|
validateTask,
|
|
424
|
-
validateTasks
|
|
461
|
+
validateTasks,
|
|
462
|
+
CONFIDENCE_RANK,
|
|
463
|
+
applyMinimumConfidence
|
|
425
464
|
};
|