@ncoderz/awa 1.5.0 → 1.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/dist/{chunk-ALEGCDAZ.js → chunk-OQZTQ5ZI.js} +1 -1
- package/dist/chunk-OQZTQ5ZI.js.map +1 -0
- package/dist/{config-LWUO5EXL.js → config-WL3SLSP6.js} +2 -2
- package/dist/index.js +646 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/awa/.awa/.agent/schemas/ARCHITECTURE.schema.yaml +0 -10
- package/templates/awa/.awa/.agent/schemas/DESIGN.schema.yaml +0 -10
- package/templates/awa/.awa/.agent/schemas/EXAMPLES.schema.yaml +0 -9
- package/templates/awa/.awa/.agent/schemas/FEAT.schema.yaml +0 -8
- package/templates/awa/.awa/.agent/schemas/PLAN.schema.yaml +5 -10
- package/templates/awa/.awa/.agent/schemas/REQ.schema.yaml +0 -9
- package/templates/awa/.awa/.agent/schemas/TASK.schema.yaml +13 -3
- package/templates/awa/_partials/awa.check.md +1 -1
- package/templates/awa/_partials/awa.code.md +9 -1
- package/templates/awa/_partials/awa.refactor.md +1 -0
- package/templates/awa/_partials/awa.tasks.md +7 -1
- package/templates/awa/_partials/awa.upgrade.md +0 -1
- package/templates/awa/_partials/awa.usage.md +17 -9
- package/templates/awa/_partials/awa.vibe.md +3 -0
- package/dist/chunk-ALEGCDAZ.js.map +0 -1
- /package/dist/{config-LWUO5EXL.js.map → config-WL3SLSP6.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -16,15 +16,15 @@ import {
|
|
|
16
16
|
rmDir,
|
|
17
17
|
walkDirectory,
|
|
18
18
|
writeTextFile
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-OQZTQ5ZI.js";
|
|
20
20
|
|
|
21
21
|
// src/cli/index.ts
|
|
22
|
-
import { Command } from "commander";
|
|
22
|
+
import { Command, Option } from "commander";
|
|
23
23
|
|
|
24
24
|
// src/_generated/package_info.ts
|
|
25
25
|
var PACKAGE_INFO = {
|
|
26
26
|
"name": "@ncoderz/awa",
|
|
27
|
-
"version": "1.
|
|
27
|
+
"version": "1.7.0",
|
|
28
28
|
"author": "Richard Sewell <richard.sewell@ncoderz.com>",
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"description": "awa is an Agent Workflow for AIs. It is also a CLI tool to powerfully manage agent workflow files using templates."
|
|
@@ -121,6 +121,83 @@ function checkCodeAgainstSpec(markers, specs, config) {
|
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
+
for (const propId of specs.propertyIds) {
|
|
125
|
+
if (!testedIds.has(propId)) {
|
|
126
|
+
const loc = specs.idLocations.get(propId);
|
|
127
|
+
const specFile = loc ? void 0 : specs.specFiles.find((sf) => sf.propertyIds.includes(propId));
|
|
128
|
+
findings.push({
|
|
129
|
+
severity: "warning",
|
|
130
|
+
code: "uncovered-property",
|
|
131
|
+
message: `Property '${propId}' has no @awa-test reference`,
|
|
132
|
+
filePath: loc?.filePath ?? specFile?.filePath,
|
|
133
|
+
line: loc?.line,
|
|
134
|
+
id: propId
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const componentFiles = /* @__PURE__ */ new Map();
|
|
139
|
+
for (const marker of markers.markers) {
|
|
140
|
+
if (marker.type === "component") {
|
|
141
|
+
if (!componentFiles.has(marker.id)) {
|
|
142
|
+
componentFiles.set(marker.id, /* @__PURE__ */ new Set());
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const fileToComponents = /* @__PURE__ */ new Map();
|
|
147
|
+
for (const marker of markers.markers) {
|
|
148
|
+
if (marker.type === "component") {
|
|
149
|
+
const existing = fileToComponents.get(marker.filePath) ?? [];
|
|
150
|
+
existing.push(marker.id);
|
|
151
|
+
fileToComponents.set(marker.filePath, existing);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
for (const marker of markers.markers) {
|
|
155
|
+
if (marker.type === "impl") {
|
|
156
|
+
const components = fileToComponents.get(marker.filePath);
|
|
157
|
+
if (components) {
|
|
158
|
+
for (const comp of components) {
|
|
159
|
+
componentFiles.get(comp)?.add(marker.id);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const designImplements = /* @__PURE__ */ new Map();
|
|
165
|
+
for (const specFile of specs.specFiles) {
|
|
166
|
+
if (specFile.componentImplements) {
|
|
167
|
+
for (const [comp, ids] of specFile.componentImplements) {
|
|
168
|
+
const existing = designImplements.get(comp) ?? /* @__PURE__ */ new Set();
|
|
169
|
+
for (const id of ids) existing.add(id);
|
|
170
|
+
designImplements.set(comp, existing);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const [compName, codeImplIds] of componentFiles) {
|
|
175
|
+
const designImplIds = designImplements.get(compName);
|
|
176
|
+
if (!designImplIds) continue;
|
|
177
|
+
for (const implId of codeImplIds) {
|
|
178
|
+
if (!designImplIds.has(implId)) {
|
|
179
|
+
findings.push({
|
|
180
|
+
severity: "warning",
|
|
181
|
+
code: "impl-not-in-implements",
|
|
182
|
+
message: `@awa-impl '${implId}' in component '${compName}' is not listed in its IMPLEMENTS`,
|
|
183
|
+
id: implId
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
for (const implId of designImplIds) {
|
|
188
|
+
if (!codeImplIds.has(implId)) {
|
|
189
|
+
const loc = specs.idLocations.get(compName);
|
|
190
|
+
findings.push({
|
|
191
|
+
severity: "warning",
|
|
192
|
+
code: "implements-not-in-impl",
|
|
193
|
+
message: `IMPLEMENTS '${implId}' in component '${compName}' has no @awa-impl in code`,
|
|
194
|
+
filePath: loc?.filePath,
|
|
195
|
+
line: loc?.line,
|
|
196
|
+
id: implId
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
124
201
|
return { findings };
|
|
125
202
|
}
|
|
126
203
|
|
|
@@ -177,7 +254,7 @@ function buildMarkerRegex(markerNames) {
|
|
|
177
254
|
const escaped = markerNames.map((m) => m.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
178
255
|
return new RegExp(`(${escaped.join("|")}):\\s*(.+)`, "g");
|
|
179
256
|
}
|
|
180
|
-
var ID_TOKEN_RE = /^([A-Z][A-Z0-9]*(?:[-_][A-Za-z0-9]+)*(?:\.\d+)?)/;
|
|
257
|
+
var ID_TOKEN_RE = /^([A-Z][A-Z0-9]*(?:[-_][A-Za-z0-9]+)*(?:\.\d+)?(?:[-_][A-Za-z0-9]+)*)/;
|
|
181
258
|
async function scanFile(filePath, markerNames) {
|
|
182
259
|
let content;
|
|
183
260
|
try {
|
|
@@ -259,6 +336,347 @@ async function collectCodeFiles(codeGlobs, ignore) {
|
|
|
259
336
|
return collectFiles(codeGlobs, ignore);
|
|
260
337
|
}
|
|
261
338
|
|
|
339
|
+
// src/core/check/matrix-fixer.ts
|
|
340
|
+
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
341
|
+
import { basename } from "path";
|
|
342
|
+
async function fixMatrices(specs, crossRefPatterns) {
|
|
343
|
+
const codeToReqFile = buildCodeToReqFileMap(specs.specFiles);
|
|
344
|
+
const fileResults = [];
|
|
345
|
+
for (const specFile of specs.specFiles) {
|
|
346
|
+
const fileName = basename(specFile.filePath);
|
|
347
|
+
if (fileName.startsWith("DESIGN-")) {
|
|
348
|
+
const changed = await fixDesignMatrix(specFile.filePath, codeToReqFile, crossRefPatterns);
|
|
349
|
+
fileResults.push({ filePath: specFile.filePath, changed });
|
|
350
|
+
} else if (fileName.startsWith("TASK-")) {
|
|
351
|
+
const changed = await fixTaskMatrix(
|
|
352
|
+
specFile.filePath,
|
|
353
|
+
codeToReqFile,
|
|
354
|
+
specs,
|
|
355
|
+
crossRefPatterns
|
|
356
|
+
);
|
|
357
|
+
fileResults.push({ filePath: specFile.filePath, changed });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
filesFixed: fileResults.filter((r) => r.changed).length,
|
|
362
|
+
fileResults
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function buildCodeToReqFileMap(specFiles) {
|
|
366
|
+
const map = /* @__PURE__ */ new Map();
|
|
367
|
+
for (const sf of specFiles) {
|
|
368
|
+
if (/\bREQ-/.test(basename(sf.filePath)) && sf.code) {
|
|
369
|
+
map.set(sf.code, basename(sf.filePath));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return map;
|
|
373
|
+
}
|
|
374
|
+
function getCodePrefix(id) {
|
|
375
|
+
const match = /^([A-Z][A-Z0-9]*)[-_]/.exec(id);
|
|
376
|
+
return match?.[1] ?? "";
|
|
377
|
+
}
|
|
378
|
+
async function fixDesignMatrix(filePath, codeToReqFile, crossRefPatterns) {
|
|
379
|
+
let content;
|
|
380
|
+
try {
|
|
381
|
+
content = await readFile2(filePath, "utf-8");
|
|
382
|
+
} catch {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
const { components, properties } = parseDesignFileData(content, crossRefPatterns);
|
|
386
|
+
const acToComponents = /* @__PURE__ */ new Map();
|
|
387
|
+
for (const comp of components) {
|
|
388
|
+
for (const acId of comp.implements) {
|
|
389
|
+
const existing = acToComponents.get(acId) ?? [];
|
|
390
|
+
existing.push(comp.name);
|
|
391
|
+
acToComponents.set(acId, existing);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
const acToProperties = /* @__PURE__ */ new Map();
|
|
395
|
+
for (const prop of properties) {
|
|
396
|
+
for (const acId of prop.validates) {
|
|
397
|
+
const existing = acToProperties.get(acId) ?? [];
|
|
398
|
+
existing.push(prop.id);
|
|
399
|
+
acToProperties.set(acId, existing);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const allAcIds = [...acToComponents.keys()];
|
|
403
|
+
const grouped = groupByReqFile(allAcIds, codeToReqFile);
|
|
404
|
+
const newSection = generateDesignSection(grouped, acToComponents, acToProperties);
|
|
405
|
+
const newContent = replaceTraceabilitySection(content, newSection);
|
|
406
|
+
if (newContent === content) return false;
|
|
407
|
+
await writeFile(filePath, newContent, "utf-8");
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
function parseDesignFileData(content, crossRefPatterns) {
|
|
411
|
+
const lines = content.split("\n");
|
|
412
|
+
const components = [];
|
|
413
|
+
const properties = [];
|
|
414
|
+
const componentRegex = /^###\s+([A-Z][A-Z0-9]*-[A-Za-z][A-Za-z0-9]*(?:[A-Z][a-z0-9]*)*)\s*$/;
|
|
415
|
+
const reqIdRegex = /^###\s+([A-Z][A-Z0-9]*-\d+(?:\.\d+)?)\s*:/;
|
|
416
|
+
const propIdRegex = /^-\s+([A-Z][A-Z0-9]*_P-\d+)\s/;
|
|
417
|
+
let currentComponent = null;
|
|
418
|
+
let lastPropertyId = null;
|
|
419
|
+
for (const line of lines) {
|
|
420
|
+
const compMatch = componentRegex.exec(line);
|
|
421
|
+
if (compMatch?.[1] && !reqIdRegex.test(line)) {
|
|
422
|
+
currentComponent = compMatch[1];
|
|
423
|
+
lastPropertyId = null;
|
|
424
|
+
components.push({ name: currentComponent, implements: [] });
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
if (/^#{1,2}\s/.test(line) && !compMatch) {
|
|
428
|
+
currentComponent = null;
|
|
429
|
+
lastPropertyId = null;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const propMatch = propIdRegex.exec(line);
|
|
433
|
+
if (propMatch?.[1]) {
|
|
434
|
+
lastPropertyId = propMatch[1];
|
|
435
|
+
properties.push({ id: lastPropertyId, validates: [] });
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
for (const pattern of crossRefPatterns) {
|
|
439
|
+
const patIdx = line.indexOf(pattern);
|
|
440
|
+
if (patIdx !== -1) {
|
|
441
|
+
const afterPattern = line.slice(patIdx + pattern.length);
|
|
442
|
+
const ids = extractIdsFromText(afterPattern);
|
|
443
|
+
if (ids.length > 0) {
|
|
444
|
+
const isImplements = pattern.toLowerCase().includes("implements");
|
|
445
|
+
if (isImplements && currentComponent) {
|
|
446
|
+
const comp = components.find((c) => c.name === currentComponent);
|
|
447
|
+
if (comp) comp.implements.push(...ids);
|
|
448
|
+
} else if (!isImplements && lastPropertyId) {
|
|
449
|
+
const prop = properties.find((p) => p.id === lastPropertyId);
|
|
450
|
+
if (prop) prop.validates.push(...ids);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return { components, properties };
|
|
457
|
+
}
|
|
458
|
+
function generateDesignSection(grouped, acToComponents, acToProperties) {
|
|
459
|
+
const lines = [];
|
|
460
|
+
const reqFiles = [...grouped.keys()].sort();
|
|
461
|
+
for (const reqFile of reqFiles) {
|
|
462
|
+
lines.push(`### ${reqFile}`);
|
|
463
|
+
lines.push("");
|
|
464
|
+
const acIds = grouped.get(reqFile) ?? [];
|
|
465
|
+
acIds.sort(compareIds);
|
|
466
|
+
for (const acId of acIds) {
|
|
467
|
+
const components = acToComponents.get(acId) ?? [];
|
|
468
|
+
const props = acToProperties.get(acId) ?? [];
|
|
469
|
+
for (const comp of components) {
|
|
470
|
+
const propStr = props.length > 0 ? ` (${props.join(", ")})` : "";
|
|
471
|
+
lines.push(`- ${acId} \u2192 ${comp}${propStr}`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
lines.push("");
|
|
475
|
+
}
|
|
476
|
+
return lines.join("\n");
|
|
477
|
+
}
|
|
478
|
+
async function fixTaskMatrix(filePath, codeToReqFile, specs, crossRefPatterns) {
|
|
479
|
+
let content;
|
|
480
|
+
try {
|
|
481
|
+
content = await readFile2(filePath, "utf-8");
|
|
482
|
+
} catch {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
const { tasks, sourceReqs, sourceDesigns } = parseTaskFileData(content, crossRefPatterns);
|
|
486
|
+
const acToTasks = /* @__PURE__ */ new Map();
|
|
487
|
+
for (const task of tasks) {
|
|
488
|
+
for (const acId of task.implements) {
|
|
489
|
+
const existing = acToTasks.get(acId) ?? [];
|
|
490
|
+
existing.push(task.id);
|
|
491
|
+
acToTasks.set(acId, existing);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const idToTestTasks = /* @__PURE__ */ new Map();
|
|
495
|
+
for (const task of tasks) {
|
|
496
|
+
for (const testId of task.tests) {
|
|
497
|
+
const existing = idToTestTasks.get(testId) ?? [];
|
|
498
|
+
existing.push(task.id);
|
|
499
|
+
idToTestTasks.set(testId, existing);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const sourceAcIds = /* @__PURE__ */ new Set();
|
|
503
|
+
for (const reqName of sourceReqs) {
|
|
504
|
+
const reqCode = extractCodeFromFileName(reqName);
|
|
505
|
+
for (const sf of specs.specFiles) {
|
|
506
|
+
if (sf.code === reqCode && /\bREQ-/.test(basename(sf.filePath))) {
|
|
507
|
+
for (const acId of sf.acIds) sourceAcIds.add(acId);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const sourcePropertyIds = /* @__PURE__ */ new Set();
|
|
512
|
+
for (const designName of sourceDesigns) {
|
|
513
|
+
const designCode = extractCodeFromFileName(designName);
|
|
514
|
+
for (const sf of specs.specFiles) {
|
|
515
|
+
if (sf.code === designCode && /\bDESIGN-/.test(basename(sf.filePath))) {
|
|
516
|
+
for (const propId of sf.propertyIds) sourcePropertyIds.add(propId);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const allRefIds = /* @__PURE__ */ new Set([...acToTasks.keys(), ...idToTestTasks.keys()]);
|
|
521
|
+
const uncovered = [];
|
|
522
|
+
for (const acId of sourceAcIds) {
|
|
523
|
+
if (!allRefIds.has(acId)) uncovered.push(acId);
|
|
524
|
+
}
|
|
525
|
+
for (const propId of sourcePropertyIds) {
|
|
526
|
+
if (!allRefIds.has(propId)) uncovered.push(propId);
|
|
527
|
+
}
|
|
528
|
+
uncovered.sort(compareIds);
|
|
529
|
+
const allIdsForMatrix = [.../* @__PURE__ */ new Set([...acToTasks.keys(), ...idToTestTasks.keys()])];
|
|
530
|
+
const grouped = groupByReqFile(allIdsForMatrix, codeToReqFile);
|
|
531
|
+
const newSection = generateTaskSection(
|
|
532
|
+
grouped,
|
|
533
|
+
acToTasks,
|
|
534
|
+
idToTestTasks,
|
|
535
|
+
sourcePropertyIds,
|
|
536
|
+
uncovered
|
|
537
|
+
);
|
|
538
|
+
const newContent = replaceTraceabilitySection(content, newSection);
|
|
539
|
+
if (newContent === content) return false;
|
|
540
|
+
await writeFile(filePath, newContent, "utf-8");
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
function parseTaskFileData(content, crossRefPatterns) {
|
|
544
|
+
const lines = content.split("\n");
|
|
545
|
+
const tasks = [];
|
|
546
|
+
const sourceReqs = [];
|
|
547
|
+
const sourceDesigns = [];
|
|
548
|
+
const sourceRegex = /^SOURCE:\s*(.+)/;
|
|
549
|
+
const taskIdRegex = /^-\s+\[[ x]\]\s+(T-[A-Z][A-Z0-9]*-\d+)/;
|
|
550
|
+
let currentTaskId = null;
|
|
551
|
+
for (const line of lines) {
|
|
552
|
+
const sourceMatch = sourceRegex.exec(line);
|
|
553
|
+
if (sourceMatch?.[1]) {
|
|
554
|
+
const parts = sourceMatch[1].split(",").map((s) => s.trim());
|
|
555
|
+
for (const part of parts) {
|
|
556
|
+
if (part.startsWith("REQ-")) sourceReqs.push(part);
|
|
557
|
+
else if (part.startsWith("DESIGN-")) sourceDesigns.push(part);
|
|
558
|
+
}
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const taskMatch = taskIdRegex.exec(line);
|
|
562
|
+
if (taskMatch?.[1]) {
|
|
563
|
+
currentTaskId = taskMatch[1];
|
|
564
|
+
tasks.push({ id: currentTaskId, implements: [], tests: [] });
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
if (/^##\s/.test(line)) {
|
|
568
|
+
currentTaskId = null;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
if (/^---/.test(line)) {
|
|
572
|
+
currentTaskId = null;
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
if (currentTaskId) {
|
|
576
|
+
const task = tasks.find((t) => t.id === currentTaskId);
|
|
577
|
+
if (task) {
|
|
578
|
+
for (const pattern of crossRefPatterns) {
|
|
579
|
+
const patIdx = line.indexOf(pattern);
|
|
580
|
+
if (patIdx !== -1) {
|
|
581
|
+
const afterPattern = line.slice(patIdx + pattern.length);
|
|
582
|
+
const ids = extractIdsFromText(afterPattern);
|
|
583
|
+
if (ids.length > 0) {
|
|
584
|
+
const isImplements = pattern.toLowerCase().includes("implements");
|
|
585
|
+
if (isImplements) {
|
|
586
|
+
task.implements.push(...ids);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
const testsIdx = line.indexOf("TESTS:");
|
|
592
|
+
if (testsIdx !== -1) {
|
|
593
|
+
const afterTests = line.slice(testsIdx + "TESTS:".length);
|
|
594
|
+
const ids = extractIdsFromText(afterTests);
|
|
595
|
+
if (ids.length > 0) {
|
|
596
|
+
task.tests.push(...ids);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return { tasks, sourceReqs, sourceDesigns };
|
|
603
|
+
}
|
|
604
|
+
function generateTaskSection(grouped, acToTasks, idToTestTasks, propertyIds, uncovered) {
|
|
605
|
+
const lines = [];
|
|
606
|
+
const reqFiles = [...grouped.keys()].sort();
|
|
607
|
+
for (const reqFile of reqFiles) {
|
|
608
|
+
lines.push(`### ${reqFile}`);
|
|
609
|
+
lines.push("");
|
|
610
|
+
const ids = grouped.get(reqFile) ?? [];
|
|
611
|
+
const acIds = ids.filter((id) => !propertyIds.has(id));
|
|
612
|
+
const propIds = ids.filter((id) => propertyIds.has(id));
|
|
613
|
+
acIds.sort(compareIds);
|
|
614
|
+
propIds.sort(compareIds);
|
|
615
|
+
for (const acId of acIds) {
|
|
616
|
+
const tasks = acToTasks.get(acId) ?? [];
|
|
617
|
+
const testTasks = idToTestTasks.get(acId) ?? [];
|
|
618
|
+
const taskStr = tasks.length > 0 ? tasks.join(", ") : "(none)";
|
|
619
|
+
const testStr = testTasks.length > 0 ? ` (${testTasks.join(", ")})` : "";
|
|
620
|
+
lines.push(`- ${acId} \u2192 ${taskStr}${testStr}`);
|
|
621
|
+
}
|
|
622
|
+
for (const propId of propIds) {
|
|
623
|
+
const testTasks = idToTestTasks.get(propId) ?? [];
|
|
624
|
+
const testStr = testTasks.length > 0 ? testTasks.join(", ") : "(none)";
|
|
625
|
+
lines.push(`- ${propId} \u2192 ${testStr}`);
|
|
626
|
+
}
|
|
627
|
+
lines.push("");
|
|
628
|
+
}
|
|
629
|
+
const uncoveredStr = uncovered.length > 0 ? uncovered.join(", ") : "(none)";
|
|
630
|
+
lines.push(`UNCOVERED: ${uncoveredStr}`);
|
|
631
|
+
return lines.join("\n");
|
|
632
|
+
}
|
|
633
|
+
function groupByReqFile(ids, codeToReqFile) {
|
|
634
|
+
const groups = /* @__PURE__ */ new Map();
|
|
635
|
+
for (const id of ids) {
|
|
636
|
+
const code = getCodePrefix(id);
|
|
637
|
+
const reqFile = codeToReqFile.get(code);
|
|
638
|
+
if (!reqFile) continue;
|
|
639
|
+
const existing = groups.get(reqFile) ?? [];
|
|
640
|
+
existing.push(id);
|
|
641
|
+
groups.set(reqFile, existing);
|
|
642
|
+
}
|
|
643
|
+
return groups;
|
|
644
|
+
}
|
|
645
|
+
function replaceTraceabilitySection(content, newSection) {
|
|
646
|
+
const lines = content.split("\n");
|
|
647
|
+
const sectionStart = lines.findIndex((l) => /^##\s+Requirements Traceability\s*$/.test(l));
|
|
648
|
+
if (sectionStart === -1) return content;
|
|
649
|
+
let sectionEnd = lines.length;
|
|
650
|
+
for (let i = sectionStart + 1; i < lines.length; i++) {
|
|
651
|
+
const line = lines[i];
|
|
652
|
+
if (line !== void 0 && /^##\s/.test(line)) {
|
|
653
|
+
sectionEnd = i;
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
const before = lines.slice(0, sectionStart + 1);
|
|
658
|
+
const after = lines.slice(sectionEnd);
|
|
659
|
+
const result = [...before, "", newSection.trimEnd(), "", ...after];
|
|
660
|
+
return result.join("\n");
|
|
661
|
+
}
|
|
662
|
+
function extractIdsFromText(text) {
|
|
663
|
+
const idRegex = /[A-Z][A-Z0-9]*-\d+(?:\.\d+)?(?:_AC-\d+)?|[A-Z][A-Z0-9]*_P-\d+/g;
|
|
664
|
+
const ids = [];
|
|
665
|
+
let match = idRegex.exec(text);
|
|
666
|
+
while (match !== null) {
|
|
667
|
+
ids.push(match[0]);
|
|
668
|
+
match = idRegex.exec(text);
|
|
669
|
+
}
|
|
670
|
+
return ids;
|
|
671
|
+
}
|
|
672
|
+
function extractCodeFromFileName(fileName) {
|
|
673
|
+
const match = /^(?:REQ|DESIGN|FEAT|EXAMPLES|API|TASK)-([A-Z][A-Z0-9]*)-/.exec(fileName);
|
|
674
|
+
return match?.[1] ?? "";
|
|
675
|
+
}
|
|
676
|
+
function compareIds(a, b) {
|
|
677
|
+
return a.localeCompare(b, void 0, { numeric: true });
|
|
678
|
+
}
|
|
679
|
+
|
|
262
680
|
// src/core/check/reporter.ts
|
|
263
681
|
import chalk from "chalk";
|
|
264
682
|
function report(findings, format) {
|
|
@@ -334,7 +752,7 @@ function printRuleContext(f) {
|
|
|
334
752
|
}
|
|
335
753
|
|
|
336
754
|
// src/core/check/rule-loader.ts
|
|
337
|
-
import { readFile as
|
|
755
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
338
756
|
import { join } from "path";
|
|
339
757
|
import { parse as parseYaml } from "yaml";
|
|
340
758
|
async function loadRules(schemaDir) {
|
|
@@ -355,7 +773,7 @@ function matchesTargetGlob(filePath, targetGlob) {
|
|
|
355
773
|
async function loadRuleFile(filePath) {
|
|
356
774
|
let content;
|
|
357
775
|
try {
|
|
358
|
-
content = await
|
|
776
|
+
content = await readFile3(filePath, "utf-8");
|
|
359
777
|
} catch {
|
|
360
778
|
return null;
|
|
361
779
|
}
|
|
@@ -547,7 +965,7 @@ var RuleValidationError = class extends Error {
|
|
|
547
965
|
};
|
|
548
966
|
|
|
549
967
|
// src/core/check/schema-checker.ts
|
|
550
|
-
import { readFile as
|
|
968
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
551
969
|
import remarkGfm from "remark-gfm";
|
|
552
970
|
import remarkParse from "remark-parse";
|
|
553
971
|
import { unified } from "unified";
|
|
@@ -594,7 +1012,7 @@ async function checkSchemasAsync(specFiles, ruleSets) {
|
|
|
594
1012
|
if (matchingRules.length === 0) continue;
|
|
595
1013
|
let content;
|
|
596
1014
|
try {
|
|
597
|
-
content = await
|
|
1015
|
+
content = await readFile4(spec.filePath, "utf-8");
|
|
598
1016
|
} catch {
|
|
599
1017
|
continue;
|
|
600
1018
|
}
|
|
@@ -1039,8 +1457,8 @@ function collectAllCodeBlocks(section) {
|
|
|
1039
1457
|
}
|
|
1040
1458
|
|
|
1041
1459
|
// src/core/check/spec-parser.ts
|
|
1042
|
-
import { readFile as
|
|
1043
|
-
import { basename } from "path";
|
|
1460
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
1461
|
+
import { basename as basename2 } from "path";
|
|
1044
1462
|
async function parseSpecs(config) {
|
|
1045
1463
|
const files = await collectSpecFiles(config.specGlobs, config.specIgnore);
|
|
1046
1464
|
const specFiles = [];
|
|
@@ -1068,7 +1486,7 @@ async function parseSpecs(config) {
|
|
|
1068
1486
|
async function parseSpecFile(filePath, crossRefPatterns) {
|
|
1069
1487
|
let content;
|
|
1070
1488
|
try {
|
|
1071
|
-
content = await
|
|
1489
|
+
content = await readFile5(filePath, "utf-8");
|
|
1072
1490
|
} catch {
|
|
1073
1491
|
return null;
|
|
1074
1492
|
}
|
|
@@ -1080,10 +1498,12 @@ async function parseSpecFile(filePath, crossRefPatterns) {
|
|
|
1080
1498
|
const componentNames = [];
|
|
1081
1499
|
const crossRefs = [];
|
|
1082
1500
|
const idLocations = /* @__PURE__ */ new Map();
|
|
1501
|
+
const componentImplements = /* @__PURE__ */ new Map();
|
|
1083
1502
|
const reqIdRegex = /^###\s+([A-Z][A-Z0-9]*-\d+(?:\.\d+)?)\s*:/;
|
|
1084
1503
|
const acIdRegex = /^-\s+(?:\[[ x]\]\s+)?([A-Z][A-Z0-9]*-\d+(?:\.\d+)?_AC-\d+)\s/;
|
|
1085
1504
|
const propIdRegex = /^-\s+([A-Z][A-Z0-9]*_P-\d+)\s/;
|
|
1086
1505
|
const componentRegex = /^###\s+([A-Z][A-Z0-9]*-[A-Za-z][A-Za-z0-9]*(?:[A-Z][a-z0-9]*)*)\s*$/;
|
|
1506
|
+
let currentComponent = null;
|
|
1087
1507
|
for (const [i, line] of lines.entries()) {
|
|
1088
1508
|
const lineNum = i + 1;
|
|
1089
1509
|
const reqMatch = reqIdRegex.exec(line);
|
|
@@ -1106,16 +1526,25 @@ async function parseSpecFile(filePath, crossRefPatterns) {
|
|
|
1106
1526
|
if (!reqIdRegex.test(line)) {
|
|
1107
1527
|
componentNames.push(compMatch[1]);
|
|
1108
1528
|
idLocations.set(compMatch[1], { filePath, line: lineNum });
|
|
1529
|
+
currentComponent = compMatch[1];
|
|
1109
1530
|
}
|
|
1110
1531
|
}
|
|
1532
|
+
if (/^#{1,2}\s/.test(line) && !compMatch) {
|
|
1533
|
+
currentComponent = null;
|
|
1534
|
+
}
|
|
1111
1535
|
for (const pattern of crossRefPatterns) {
|
|
1112
1536
|
const patIdx = line.indexOf(pattern);
|
|
1113
1537
|
if (patIdx !== -1) {
|
|
1114
1538
|
const afterPattern = line.slice(patIdx + pattern.length);
|
|
1115
|
-
const ids =
|
|
1539
|
+
const ids = extractIdsFromText2(afterPattern);
|
|
1116
1540
|
if (ids.length > 0) {
|
|
1117
1541
|
const type = pattern.toLowerCase().includes("implements") ? "implements" : "validates";
|
|
1118
1542
|
crossRefs.push({ type, ids, filePath, line: i + 1 });
|
|
1543
|
+
if (type === "implements" && currentComponent) {
|
|
1544
|
+
const existing = componentImplements.get(currentComponent) ?? [];
|
|
1545
|
+
existing.push(...ids);
|
|
1546
|
+
componentImplements.set(currentComponent, existing);
|
|
1547
|
+
}
|
|
1119
1548
|
}
|
|
1120
1549
|
}
|
|
1121
1550
|
}
|
|
@@ -1128,16 +1557,17 @@ async function parseSpecFile(filePath, crossRefPatterns) {
|
|
|
1128
1557
|
propertyIds,
|
|
1129
1558
|
componentNames,
|
|
1130
1559
|
crossRefs,
|
|
1131
|
-
idLocations
|
|
1560
|
+
idLocations,
|
|
1561
|
+
componentImplements
|
|
1132
1562
|
};
|
|
1133
1563
|
}
|
|
1134
1564
|
function extractCodePrefix(filePath) {
|
|
1135
|
-
const name =
|
|
1565
|
+
const name = basename2(filePath, ".md");
|
|
1136
1566
|
const match = /^(?:REQ|DESIGN|FEAT|EXAMPLES|API)-([A-Z][A-Z0-9]*)-/.exec(name);
|
|
1137
1567
|
if (match?.[1]) return match[1];
|
|
1138
1568
|
return "";
|
|
1139
1569
|
}
|
|
1140
|
-
function
|
|
1570
|
+
function extractIdsFromText2(text) {
|
|
1141
1571
|
const idRegex = /[A-Z][A-Z0-9]*-\d+(?:\.\d+)?(?:_AC-\d+)?|[A-Z][A-Z0-9]*_P-\d+/g;
|
|
1142
1572
|
const ids = [];
|
|
1143
1573
|
let match = idRegex.exec(text);
|
|
@@ -1170,6 +1600,37 @@ function checkSpecAgainstSpec(specs, markers, config) {
|
|
|
1170
1600
|
}
|
|
1171
1601
|
}
|
|
1172
1602
|
}
|
|
1603
|
+
const implementedAcIds = /* @__PURE__ */ new Set();
|
|
1604
|
+
for (const specFile of specs.specFiles) {
|
|
1605
|
+
for (const crossRef of specFile.crossRefs) {
|
|
1606
|
+
if (crossRef.type === "implements") {
|
|
1607
|
+
for (const id of crossRef.ids) {
|
|
1608
|
+
implementedAcIds.add(id);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
const reqAcIds = /* @__PURE__ */ new Set();
|
|
1614
|
+
for (const specFile of specs.specFiles) {
|
|
1615
|
+
if (/\bREQ-/.test(specFile.filePath)) {
|
|
1616
|
+
for (const acId of specFile.acIds) {
|
|
1617
|
+
reqAcIds.add(acId);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
for (const acId of reqAcIds) {
|
|
1622
|
+
if (!implementedAcIds.has(acId)) {
|
|
1623
|
+
const loc = specs.idLocations.get(acId);
|
|
1624
|
+
findings.push({
|
|
1625
|
+
severity: "error",
|
|
1626
|
+
code: "unlinked-ac",
|
|
1627
|
+
message: `Acceptance criterion '${acId}' is not claimed by any DESIGN IMPLEMENTS`,
|
|
1628
|
+
filePath: loc?.filePath,
|
|
1629
|
+
line: loc?.line,
|
|
1630
|
+
id: acId
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1173
1634
|
if (!config.specOnly) {
|
|
1174
1635
|
const referencedCodes = /* @__PURE__ */ new Set();
|
|
1175
1636
|
for (const marker of markers.markers) {
|
|
@@ -1238,7 +1699,8 @@ var DEFAULT_CHECK_CONFIG = {
|
|
|
1238
1699
|
schemaDir: ".awa/.agent/schemas",
|
|
1239
1700
|
schemaEnabled: true,
|
|
1240
1701
|
allowWarnings: false,
|
|
1241
|
-
specOnly: false
|
|
1702
|
+
specOnly: false,
|
|
1703
|
+
fix: true
|
|
1242
1704
|
};
|
|
1243
1705
|
|
|
1244
1706
|
// src/commands/check.ts
|
|
@@ -1267,7 +1729,20 @@ async function checkCommand(cliOptions) {
|
|
|
1267
1729
|
const allFindings = config.allowWarnings ? combinedFindings : combinedFindings.map(
|
|
1268
1730
|
(f) => f.severity === "warning" ? { ...f, severity: "error" } : f
|
|
1269
1731
|
);
|
|
1270
|
-
|
|
1732
|
+
const isSummary = cliOptions.summary === true;
|
|
1733
|
+
if (isSummary) {
|
|
1734
|
+
const errors = allFindings.filter((f) => f.severity === "error").length;
|
|
1735
|
+
const warnings = allFindings.filter((f) => f.severity === "warning").length;
|
|
1736
|
+
console.log(`errors: ${errors}, warnings: ${warnings}`);
|
|
1737
|
+
} else {
|
|
1738
|
+
report(allFindings, config.format);
|
|
1739
|
+
}
|
|
1740
|
+
if (config.fix) {
|
|
1741
|
+
const fixResult = await fixMatrices(specs, config.crossRefPatterns);
|
|
1742
|
+
if (fixResult.filesFixed > 0) {
|
|
1743
|
+
logger.info(`Fixed traceability matrices in ${fixResult.filesFixed} file(s)`);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1271
1746
|
const hasErrors = allFindings.some((f) => f.severity === "error");
|
|
1272
1747
|
return hasErrors ? 1 : 0;
|
|
1273
1748
|
} catch (error) {
|
|
@@ -1299,11 +1774,12 @@ function buildCheckConfig(fileConfig, cliOptions) {
|
|
|
1299
1774
|
const ignoreMarkers = toStringArray(section?.["ignore-markers"]) ?? [
|
|
1300
1775
|
...DEFAULT_CHECK_CONFIG.ignoreMarkers
|
|
1301
1776
|
];
|
|
1302
|
-
const format = cliOptions.format === "json" ? "json" : section?.format === "json" ? "json" : DEFAULT_CHECK_CONFIG.format;
|
|
1777
|
+
const format = cliOptions.json === true ? "json" : cliOptions.format === "json" ? "json" : section?.format === "json" ? "json" : DEFAULT_CHECK_CONFIG.format;
|
|
1303
1778
|
const schemaDir = typeof section?.["schema-dir"] === "string" ? section["schema-dir"] : DEFAULT_CHECK_CONFIG.schemaDir;
|
|
1304
1779
|
const schemaEnabled = typeof section?.["schema-enabled"] === "boolean" ? section["schema-enabled"] : DEFAULT_CHECK_CONFIG.schemaEnabled;
|
|
1305
1780
|
const allowWarnings = cliOptions.allowWarnings === true ? true : typeof section?.["allow-warnings"] === "boolean" ? section["allow-warnings"] : DEFAULT_CHECK_CONFIG.allowWarnings;
|
|
1306
1781
|
const specOnly = cliOptions.specOnly === true ? true : typeof section?.["spec-only"] === "boolean" ? section["spec-only"] : DEFAULT_CHECK_CONFIG.specOnly;
|
|
1782
|
+
const fix = cliOptions.fix === false ? false : DEFAULT_CHECK_CONFIG.fix;
|
|
1307
1783
|
return {
|
|
1308
1784
|
specGlobs,
|
|
1309
1785
|
codeGlobs,
|
|
@@ -1317,7 +1793,8 @@ function buildCheckConfig(fileConfig, cliOptions) {
|
|
|
1317
1793
|
schemaDir,
|
|
1318
1794
|
schemaEnabled,
|
|
1319
1795
|
allowWarnings,
|
|
1320
|
-
specOnly
|
|
1796
|
+
specOnly,
|
|
1797
|
+
fix
|
|
1321
1798
|
};
|
|
1322
1799
|
}
|
|
1323
1800
|
function toStringArray(value) {
|
|
@@ -2341,8 +2818,8 @@ async function prepareDiff(options) {
|
|
|
2341
2818
|
async function diffCommand(cliOptions) {
|
|
2342
2819
|
try {
|
|
2343
2820
|
const fileConfig = await configLoader.load(cliOptions.config ?? null);
|
|
2344
|
-
if (cliOptions.
|
|
2345
|
-
const mode = cliOptions.
|
|
2821
|
+
if (cliOptions.allTargets || cliOptions.target) {
|
|
2822
|
+
const mode = cliOptions.allTargets ? "all" : "single";
|
|
2346
2823
|
const targets = batchRunner.resolveTargets(cliOptions, fileConfig, mode, cliOptions.target);
|
|
2347
2824
|
let hasDifferences = false;
|
|
2348
2825
|
for (const { targetName, options: options2 } of targets) {
|
|
@@ -2473,7 +2950,7 @@ var FeaturesReporter = class {
|
|
|
2473
2950
|
var featuresReporter = new FeaturesReporter();
|
|
2474
2951
|
|
|
2475
2952
|
// src/core/features/scanner.ts
|
|
2476
|
-
import { readdir, readFile as
|
|
2953
|
+
import { readdir, readFile as readFile6 } from "fs/promises";
|
|
2477
2954
|
import { join as join7, relative as relative3 } from "path";
|
|
2478
2955
|
var FEATURE_PATTERN = /it\.features\.(?:includes|indexOf)\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
2479
2956
|
async function* walkAllFiles(dir) {
|
|
@@ -2507,7 +2984,7 @@ var FeatureScanner = class {
|
|
|
2507
2984
|
for await (const filePath of walkAllFiles(templatePath)) {
|
|
2508
2985
|
filesScanned++;
|
|
2509
2986
|
try {
|
|
2510
|
-
const content = await
|
|
2987
|
+
const content = await readFile6(filePath, "utf-8");
|
|
2511
2988
|
const flags = this.extractFlags(content);
|
|
2512
2989
|
const relPath = relative3(templatePath, filePath);
|
|
2513
2990
|
for (const flag of flags) {
|
|
@@ -2532,22 +3009,37 @@ var featureScanner = new FeatureScanner();
|
|
|
2532
3009
|
|
|
2533
3010
|
// src/commands/features.ts
|
|
2534
3011
|
async function featuresCommand(cliOptions) {
|
|
3012
|
+
let mergedDir = null;
|
|
2535
3013
|
try {
|
|
2536
|
-
|
|
3014
|
+
const silent = cliOptions.json || cliOptions.summary;
|
|
3015
|
+
if (!silent) {
|
|
2537
3016
|
intro2("awa CLI - Feature Discovery");
|
|
2538
3017
|
}
|
|
2539
3018
|
const fileConfig = await configLoader.load(cliOptions.config ?? null);
|
|
2540
3019
|
const templateSource = cliOptions.template ?? fileConfig?.template ?? null;
|
|
2541
3020
|
const refresh = cliOptions.refresh ?? fileConfig?.refresh ?? false;
|
|
2542
3021
|
const template2 = await templateResolver.resolve(templateSource, refresh);
|
|
2543
|
-
const
|
|
3022
|
+
const overlays = cliOptions.overlay ?? fileConfig?.overlay ?? [];
|
|
3023
|
+
let templatePath = template2.localPath;
|
|
3024
|
+
if (overlays.length > 0) {
|
|
3025
|
+
const overlayDirs = await resolveOverlays(overlays, refresh);
|
|
3026
|
+
mergedDir = await buildMergedDir(template2.localPath, overlayDirs);
|
|
3027
|
+
templatePath = mergedDir;
|
|
3028
|
+
}
|
|
3029
|
+
const scanResult = await featureScanner.scan(templatePath);
|
|
2544
3030
|
const presets = fileConfig?.presets;
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
}
|
|
2550
|
-
|
|
3031
|
+
if (cliOptions.summary) {
|
|
3032
|
+
console.log(
|
|
3033
|
+
`features: ${scanResult.features.length}, files-scanned: ${scanResult.filesScanned}`
|
|
3034
|
+
);
|
|
3035
|
+
} else {
|
|
3036
|
+
featuresReporter.report({
|
|
3037
|
+
scanResult,
|
|
3038
|
+
json: cliOptions.json ?? false,
|
|
3039
|
+
presets
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
if (!silent) {
|
|
2551
3043
|
outro2("Feature discovery complete!");
|
|
2552
3044
|
}
|
|
2553
3045
|
return 0;
|
|
@@ -2558,6 +3050,10 @@ async function featuresCommand(cliOptions) {
|
|
|
2558
3050
|
logger.error(String(error));
|
|
2559
3051
|
}
|
|
2560
3052
|
return 1;
|
|
3053
|
+
} finally {
|
|
3054
|
+
if (mergedDir) {
|
|
3055
|
+
await rmDir(mergedDir);
|
|
3056
|
+
}
|
|
2561
3057
|
}
|
|
2562
3058
|
}
|
|
2563
3059
|
|
|
@@ -2649,8 +3145,8 @@ async function generateCommand(cliOptions) {
|
|
|
2649
3145
|
if (!cliOptions.config && fileConfig === null) {
|
|
2650
3146
|
logger.info("Tip: create .awa.toml to save your options for next time.");
|
|
2651
3147
|
}
|
|
2652
|
-
if (cliOptions.
|
|
2653
|
-
const mode = cliOptions.
|
|
3148
|
+
if (cliOptions.allTargets || cliOptions.target) {
|
|
3149
|
+
const mode = cliOptions.allTargets ? "all" : "single";
|
|
2654
3150
|
const targets = batchRunner.resolveTargets(cliOptions, fileConfig, mode, cliOptions.target);
|
|
2655
3151
|
for (const { targetName, options: options2 } of targets) {
|
|
2656
3152
|
batchRunner.logForTarget(targetName, "Starting generation...");
|
|
@@ -2684,7 +3180,7 @@ import { intro as intro4, outro as outro4 } from "@clack/prompts";
|
|
|
2684
3180
|
|
|
2685
3181
|
// src/core/template-test/fixture-loader.ts
|
|
2686
3182
|
import { readdir as readdir2 } from "fs/promises";
|
|
2687
|
-
import { basename as
|
|
3183
|
+
import { basename as basename3, extname, join as join8 } from "path";
|
|
2688
3184
|
import { parse } from "smol-toml";
|
|
2689
3185
|
async function discoverFixtures(templatePath) {
|
|
2690
3186
|
const testsDir = join8(templatePath, "_tests");
|
|
@@ -2706,7 +3202,7 @@ async function discoverFixtures(templatePath) {
|
|
|
2706
3202
|
async function parseFixture(filePath) {
|
|
2707
3203
|
const content = await readTextFile(filePath);
|
|
2708
3204
|
const parsed = parse(content);
|
|
2709
|
-
const name =
|
|
3205
|
+
const name = basename3(filePath, extname(filePath));
|
|
2710
3206
|
const features = toStringArray2(parsed.features) ?? [];
|
|
2711
3207
|
const preset = toStringArray2(parsed.preset) ?? [];
|
|
2712
3208
|
const removeFeatures = toStringArray2(parsed["remove-features"]) ?? [];
|
|
@@ -2729,7 +3225,11 @@ function toStringArray2(value) {
|
|
|
2729
3225
|
|
|
2730
3226
|
// src/core/template-test/reporter.ts
|
|
2731
3227
|
import chalk4 from "chalk";
|
|
2732
|
-
function report2(result) {
|
|
3228
|
+
function report2(result, options) {
|
|
3229
|
+
if (options?.json) {
|
|
3230
|
+
reportJson2(result);
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
2733
3233
|
console.log("");
|
|
2734
3234
|
for (const fixture of result.results) {
|
|
2735
3235
|
reportFixture(fixture);
|
|
@@ -2743,6 +3243,27 @@ function report2(result) {
|
|
|
2743
3243
|
}
|
|
2744
3244
|
console.log("");
|
|
2745
3245
|
}
|
|
3246
|
+
function reportJson2(result) {
|
|
3247
|
+
const output = {
|
|
3248
|
+
total: result.total,
|
|
3249
|
+
passed: result.passed,
|
|
3250
|
+
failed: result.failed,
|
|
3251
|
+
results: result.results.map((r) => ({
|
|
3252
|
+
name: r.name,
|
|
3253
|
+
passed: r.passed,
|
|
3254
|
+
...r.error ? { error: r.error } : {},
|
|
3255
|
+
fileResults: r.fileResults.map((f) => ({
|
|
3256
|
+
path: f.path,
|
|
3257
|
+
found: f.found
|
|
3258
|
+
})),
|
|
3259
|
+
snapshotResults: r.snapshotResults.map((s) => ({
|
|
3260
|
+
path: s.path,
|
|
3261
|
+
status: s.status
|
|
3262
|
+
}))
|
|
3263
|
+
}))
|
|
3264
|
+
};
|
|
3265
|
+
console.log(JSON.stringify(output, null, 2));
|
|
3266
|
+
}
|
|
2746
3267
|
function reportFixture(fixture) {
|
|
2747
3268
|
const icon = fixture.passed ? chalk4.green("\u2714") : chalk4.red("\u2716");
|
|
2748
3269
|
console.log(`${icon} ${fixture.name}`);
|
|
@@ -2905,31 +3426,61 @@ async function runAll(fixtures, templatePath, options, presetDefinitions = {}) {
|
|
|
2905
3426
|
|
|
2906
3427
|
// src/commands/test.ts
|
|
2907
3428
|
async function testCommand(options) {
|
|
3429
|
+
let mergedDir = null;
|
|
2908
3430
|
try {
|
|
2909
|
-
|
|
3431
|
+
const isJson = options.json === true;
|
|
3432
|
+
const isSummary = options.summary === true;
|
|
3433
|
+
const silent = isJson || isSummary;
|
|
3434
|
+
if (!silent) {
|
|
3435
|
+
intro4("awa CLI - Template Test");
|
|
3436
|
+
}
|
|
2910
3437
|
const fileConfig = await configLoader.load(options.config ?? null);
|
|
2911
3438
|
const templateSource = options.template ?? fileConfig?.template ?? null;
|
|
2912
|
-
const
|
|
2913
|
-
const
|
|
3439
|
+
const refresh = options.refresh ?? false;
|
|
3440
|
+
const template2 = await templateResolver.resolve(templateSource, refresh);
|
|
3441
|
+
const overlays = options.overlay ?? fileConfig?.overlay ?? [];
|
|
3442
|
+
let templatePath = template2.localPath;
|
|
3443
|
+
if (overlays.length > 0) {
|
|
3444
|
+
const overlayDirs = await resolveOverlays(overlays, refresh);
|
|
3445
|
+
mergedDir = await buildMergedDir(template2.localPath, overlayDirs);
|
|
3446
|
+
templatePath = mergedDir;
|
|
3447
|
+
}
|
|
3448
|
+
const fixtures = await discoverFixtures(templatePath);
|
|
2914
3449
|
if (fixtures.length === 0) {
|
|
2915
|
-
|
|
2916
|
-
|
|
3450
|
+
if (isSummary) {
|
|
3451
|
+
console.log("passed: 0, failed: 0, total: 0");
|
|
3452
|
+
} else if (isJson) {
|
|
3453
|
+
console.log(JSON.stringify({ total: 0, passed: 0, failed: 0, results: [] }, null, 2));
|
|
3454
|
+
} else {
|
|
3455
|
+
logger.warn("No test fixtures found in _tests/ directory");
|
|
3456
|
+
outro4("No tests to run.");
|
|
3457
|
+
}
|
|
2917
3458
|
return 0;
|
|
2918
3459
|
}
|
|
2919
|
-
|
|
3460
|
+
if (!silent) {
|
|
3461
|
+
logger.info(`Found ${fixtures.length} fixture(s)`);
|
|
3462
|
+
}
|
|
2920
3463
|
const presetDefinitions = fileConfig?.presets ?? {};
|
|
2921
3464
|
const result = await runAll(
|
|
2922
3465
|
fixtures,
|
|
2923
|
-
|
|
3466
|
+
templatePath,
|
|
2924
3467
|
{ updateSnapshots: options.updateSnapshots },
|
|
2925
3468
|
presetDefinitions
|
|
2926
3469
|
);
|
|
2927
|
-
|
|
3470
|
+
if (isSummary) {
|
|
3471
|
+
console.log(`passed: ${result.passed}, failed: ${result.failed}, total: ${result.total}`);
|
|
3472
|
+
} else {
|
|
3473
|
+
report2(result, { json: isJson });
|
|
3474
|
+
}
|
|
2928
3475
|
if (result.failed > 0) {
|
|
2929
|
-
|
|
3476
|
+
if (!silent) {
|
|
3477
|
+
outro4(`${result.failed} fixture(s) failed.`);
|
|
3478
|
+
}
|
|
2930
3479
|
return 1;
|
|
2931
3480
|
}
|
|
2932
|
-
|
|
3481
|
+
if (!silent) {
|
|
3482
|
+
outro4("All tests passed!");
|
|
3483
|
+
}
|
|
2933
3484
|
return 0;
|
|
2934
3485
|
} catch (error) {
|
|
2935
3486
|
if (error instanceof Error) {
|
|
@@ -2938,11 +3489,15 @@ async function testCommand(options) {
|
|
|
2938
3489
|
logger.error(String(error));
|
|
2939
3490
|
}
|
|
2940
3491
|
return 2;
|
|
3492
|
+
} finally {
|
|
3493
|
+
if (mergedDir) {
|
|
3494
|
+
await rmDir(mergedDir);
|
|
3495
|
+
}
|
|
2941
3496
|
}
|
|
2942
3497
|
}
|
|
2943
3498
|
|
|
2944
3499
|
// src/core/trace/content-assembler.ts
|
|
2945
|
-
import { readFile as
|
|
3500
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2946
3501
|
var DEFAULT_BEFORE_CONTEXT = 5;
|
|
2947
3502
|
var DEFAULT_AFTER_CONTEXT = 20;
|
|
2948
3503
|
async function assembleContent(result, taskPath, contextOptions) {
|
|
@@ -3141,7 +3696,7 @@ function findEnclosingBlock(lines, lineIdx, beforeContext = DEFAULT_BEFORE_CONTE
|
|
|
3141
3696
|
}
|
|
3142
3697
|
async function safeReadFile(filePath) {
|
|
3143
3698
|
try {
|
|
3144
|
-
return await
|
|
3699
|
+
return await readFile7(filePath, "utf-8");
|
|
3145
3700
|
} catch {
|
|
3146
3701
|
return null;
|
|
3147
3702
|
}
|
|
@@ -3493,7 +4048,7 @@ function pushToMap(map, key, value) {
|
|
|
3493
4048
|
}
|
|
3494
4049
|
|
|
3495
4050
|
// src/core/trace/input-resolver.ts
|
|
3496
|
-
import { readFile as
|
|
4051
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
3497
4052
|
function resolveIds(ids, index) {
|
|
3498
4053
|
const resolved = [];
|
|
3499
4054
|
const warnings = [];
|
|
@@ -3509,7 +4064,7 @@ function resolveIds(ids, index) {
|
|
|
3509
4064
|
async function resolveTaskFile(taskPath, index) {
|
|
3510
4065
|
let content;
|
|
3511
4066
|
try {
|
|
3512
|
-
content = await
|
|
4067
|
+
content = await readFile8(taskPath, "utf-8");
|
|
3513
4068
|
} catch {
|
|
3514
4069
|
return { ids: [], warnings: [`Task file not found: ${taskPath}`] };
|
|
3515
4070
|
}
|
|
@@ -3560,7 +4115,7 @@ async function resolveTaskFile(taskPath, index) {
|
|
|
3560
4115
|
async function resolveSourceFile(filePath, index) {
|
|
3561
4116
|
let content;
|
|
3562
4117
|
try {
|
|
3563
|
-
content = await
|
|
4118
|
+
content = await readFile8(filePath, "utf-8");
|
|
3564
4119
|
} catch {
|
|
3565
4120
|
return { ids: [], warnings: [`Source file not found: ${filePath}`] };
|
|
3566
4121
|
}
|
|
@@ -3861,6 +4416,7 @@ function buildScanConfig(fileConfig, overrides) {
|
|
|
3861
4416
|
schemaEnabled: false,
|
|
3862
4417
|
allowWarnings: true,
|
|
3863
4418
|
specOnly: false,
|
|
4419
|
+
fix: true,
|
|
3864
4420
|
...overrides
|
|
3865
4421
|
};
|
|
3866
4422
|
}
|
|
@@ -3964,6 +4520,13 @@ async function traceCommand(options) {
|
|
|
3964
4520
|
}
|
|
3965
4521
|
return 1;
|
|
3966
4522
|
}
|
|
4523
|
+
if (options.summary) {
|
|
4524
|
+
const chainCount = result.chains.length;
|
|
4525
|
+
const notFoundCount = result.notFound.length;
|
|
4526
|
+
process.stdout.write(`chains: ${chainCount}, not-found: ${notFoundCount}
|
|
4527
|
+
`);
|
|
4528
|
+
return result.notFound.length > 0 && result.chains.length === 0 ? 1 : 0;
|
|
4529
|
+
}
|
|
3967
4530
|
let output;
|
|
3968
4531
|
const isContentMode = options.content || options.maxTokens !== void 0;
|
|
3969
4532
|
const queryLabel = ids.join(", ");
|
|
@@ -4063,7 +4626,7 @@ function printUpdateWarning(log, result) {
|
|
|
4063
4626
|
}
|
|
4064
4627
|
|
|
4065
4628
|
// src/utils/update-check-cache.ts
|
|
4066
|
-
import { mkdir as mkdir3, readFile as
|
|
4629
|
+
import { mkdir as mkdir3, readFile as readFile9, writeFile as writeFile2 } from "fs/promises";
|
|
4067
4630
|
import { homedir } from "os";
|
|
4068
4631
|
import { dirname, join as join11 } from "path";
|
|
4069
4632
|
var CACHE_DIR = join11(homedir(), ".cache", "awa");
|
|
@@ -4071,7 +4634,7 @@ var CACHE_FILE = join11(CACHE_DIR, "update-check.json");
|
|
|
4071
4634
|
var DEFAULT_INTERVAL_MS = 864e5;
|
|
4072
4635
|
async function shouldCheck(intervalMs = DEFAULT_INTERVAL_MS) {
|
|
4073
4636
|
try {
|
|
4074
|
-
const raw = await
|
|
4637
|
+
const raw = await readFile9(CACHE_FILE, "utf-8");
|
|
4075
4638
|
const data = JSON.parse(raw);
|
|
4076
4639
|
if (typeof data.timestamp !== "number" || typeof data.latestVersion !== "string") {
|
|
4077
4640
|
return true;
|
|
@@ -4088,7 +4651,7 @@ async function writeCache(latestVersion) {
|
|
|
4088
4651
|
timestamp: Date.now(),
|
|
4089
4652
|
latestVersion
|
|
4090
4653
|
};
|
|
4091
|
-
await
|
|
4654
|
+
await writeFile2(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
4092
4655
|
} catch {
|
|
4093
4656
|
}
|
|
4094
4657
|
}
|
|
@@ -4108,7 +4671,7 @@ function configureGenerateCommand(cmd) {
|
|
|
4108
4671
|
"--delete",
|
|
4109
4672
|
"Enable deletion of files listed in the delete list (default: warn only)",
|
|
4110
4673
|
false
|
|
4111
|
-
).option("-c, --config <path>", "Path to configuration file").option("--refresh", "Force refresh of cached Git templates", false).option("--all", "Process all named targets from config", false).option("--target <name>", "Process a specific named target from config").option(
|
|
4674
|
+
).option("-c, --config <path>", "Path to configuration file").option("--refresh", "Force refresh of cached Git templates", false).option("--all-targets", "Process all named targets from config", false).option("--target <name>", "Process a specific named target from config").option(
|
|
4112
4675
|
"--overlay <path...>",
|
|
4113
4676
|
"Overlay directory paths applied over base template (repeatable)"
|
|
4114
4677
|
).option("--json", "Output results as JSON (implies --dry-run)", false).option("--summary", "Output compact one-line summary", false).action(async (output, options) => {
|
|
@@ -4123,7 +4686,8 @@ function configureGenerateCommand(cmd) {
|
|
|
4123
4686
|
delete: options.delete,
|
|
4124
4687
|
config: options.config,
|
|
4125
4688
|
refresh: options.refresh,
|
|
4126
|
-
all: options.
|
|
4689
|
+
all: options.allTargets,
|
|
4690
|
+
allTargets: options.allTargets,
|
|
4127
4691
|
target: options.target,
|
|
4128
4692
|
overlay: options.overlay || [],
|
|
4129
4693
|
json: options.json,
|
|
@@ -4137,7 +4701,7 @@ configureGenerateCommand(program.command("init"));
|
|
|
4137
4701
|
template.command("diff").description("Compare template output with existing target directory").argument("[target]", "Target directory to compare against (optional if specified in config)").option("-t, --template <source>", "Template source (local path or Git repository)").option("-f, --features <flag...>", "Feature flags (can be specified multiple times)").option("--preset <name...>", "Preset names to enable (can be specified multiple times)").option(
|
|
4138
4702
|
"--remove-features <flag...>",
|
|
4139
4703
|
"Feature flags to remove (can be specified multiple times)"
|
|
4140
|
-
).option("-c, --config <path>", "Path to configuration file").option("--refresh", "Force refresh of cached Git templates", false).option("--list-unknown", "Include target-only files in diff results", false).option("--all", "Process all named targets from config", false).option("--target <name>", "Process a specific named target from config").option("-w, --watch", "Watch template directory for changes and re-run diff", false).option("--overlay <path...>", "Overlay directory paths applied over base template (repeatable)").option("--json", "Output results as JSON", false).option("--summary", "Output compact one-line summary", false).action(async (target, options) => {
|
|
4704
|
+
).option("-c, --config <path>", "Path to configuration file").option("--refresh", "Force refresh of cached Git templates", false).option("--list-unknown", "Include target-only files in diff results", false).option("--all-targets", "Process all named targets from config", false).option("--target <name>", "Process a specific named target from config").option("-w, --watch", "Watch template directory for changes and re-run diff", false).option("--overlay <path...>", "Overlay directory paths applied over base template (repeatable)").option("--json", "Output results as JSON", false).option("--summary", "Output compact one-line summary", false).action(async (target, options) => {
|
|
4141
4705
|
const cliOptions = {
|
|
4142
4706
|
output: target,
|
|
4143
4707
|
// Use target as output for consistency
|
|
@@ -4148,7 +4712,8 @@ template.command("diff").description("Compare template output with existing targ
|
|
|
4148
4712
|
config: options.config,
|
|
4149
4713
|
refresh: options.refresh,
|
|
4150
4714
|
listUnknown: options.listUnknown,
|
|
4151
|
-
all: options.
|
|
4715
|
+
all: options.allTargets,
|
|
4716
|
+
allTargets: options.allTargets,
|
|
4152
4717
|
target: options.target,
|
|
4153
4718
|
watch: options.watch,
|
|
4154
4719
|
overlay: options.overlay || [],
|
|
@@ -4160,7 +4725,9 @@ template.command("diff").description("Compare template output with existing targ
|
|
|
4160
4725
|
});
|
|
4161
4726
|
program.command("check").description(
|
|
4162
4727
|
"Validate spec files against schemas and check traceability between code markers and specs"
|
|
4163
|
-
).option("-c, --config <path>", "Path to configuration file").option("--spec-ignore <pattern...>", "Glob patterns to exclude from spec file scanning").option("--code-ignore <pattern...>", "Glob patterns to exclude from code file scanning").option("--
|
|
4728
|
+
).option("-c, --config <path>", "Path to configuration file").option("--spec-ignore <pattern...>", "Glob patterns to exclude from spec file scanning").option("--code-ignore <pattern...>", "Glob patterns to exclude from code file scanning").option("--json", "Output results as JSON", false).addOption(
|
|
4729
|
+
new Option("--format <format>", "Output format (text or json)").default("text").hideHelp()
|
|
4730
|
+
).option("--summary", "Output compact one-line summary", false).option(
|
|
4164
4731
|
"--allow-warnings",
|
|
4165
4732
|
"Allow warnings without failing (default: warnings are errors)",
|
|
4166
4733
|
false
|
|
@@ -4168,38 +4735,50 @@ program.command("check").description(
|
|
|
4168
4735
|
"--spec-only",
|
|
4169
4736
|
"Run only spec-level checks (schema and cross-refs); skip code-to-spec traceability",
|
|
4170
4737
|
false
|
|
4738
|
+
).option(
|
|
4739
|
+
"--no-fix",
|
|
4740
|
+
"Skip regeneration of Requirements Traceability sections in DESIGN and TASK files"
|
|
4171
4741
|
).action(async (options) => {
|
|
4172
4742
|
const cliOptions = {
|
|
4173
4743
|
config: options.config,
|
|
4174
4744
|
specIgnore: options.specIgnore,
|
|
4175
4745
|
codeIgnore: options.codeIgnore,
|
|
4176
4746
|
format: options.format,
|
|
4747
|
+
json: options.json,
|
|
4748
|
+
summary: options.summary,
|
|
4177
4749
|
allowWarnings: options.allowWarnings,
|
|
4178
|
-
specOnly: options.specOnly
|
|
4750
|
+
specOnly: options.specOnly,
|
|
4751
|
+
fix: options.fix
|
|
4179
4752
|
};
|
|
4180
4753
|
const exitCode = await checkCommand(cliOptions);
|
|
4181
4754
|
process.exit(exitCode);
|
|
4182
4755
|
});
|
|
4183
|
-
template.command("features").description("Discover feature flags available in a template").option("-t, --template <source>", "Template source (local path or Git repository)").option("-c, --config <path>", "Path to configuration file").option("--refresh", "Force refresh of cached Git templates", false).option("--json", "Output results as JSON", false).action(async (options) => {
|
|
4756
|
+
template.command("features").description("Discover feature flags available in a template").option("-t, --template <source>", "Template source (local path or Git repository)").option("-c, --config <path>", "Path to configuration file").option("--refresh", "Force refresh of cached Git templates", false).option("--overlay <path...>", "Overlay directory paths applied over base template (repeatable)").option("--json", "Output results as JSON", false).option("--summary", "Output compact one-line summary", false).action(async (options) => {
|
|
4184
4757
|
const exitCode = await featuresCommand({
|
|
4185
4758
|
template: options.template,
|
|
4186
4759
|
config: options.config,
|
|
4187
4760
|
refresh: options.refresh,
|
|
4188
|
-
json: options.json
|
|
4761
|
+
json: options.json,
|
|
4762
|
+
summary: options.summary,
|
|
4763
|
+
overlay: options.overlay || []
|
|
4189
4764
|
});
|
|
4190
4765
|
process.exit(exitCode);
|
|
4191
4766
|
});
|
|
4192
|
-
template.command("test").description("Run template test fixtures to verify expected output").option("-t, --template <source>", "Template source (local path or Git repository)").option("-c, --config <path>", "Path to configuration file").option("--update-snapshots", "Update stored snapshots with current rendered output", false).action(async (options) => {
|
|
4767
|
+
template.command("test").description("Run template test fixtures to verify expected output").option("-t, --template <source>", "Template source (local path or Git repository)").option("-c, --config <path>", "Path to configuration file").option("--update-snapshots", "Update stored snapshots with current rendered output", false).option("--refresh", "Force refresh of cached Git templates", false).option("--overlay <path...>", "Overlay directory paths applied over base template (repeatable)").option("--json", "Output results as JSON", false).option("--summary", "Output compact one-line summary", false).action(async (options) => {
|
|
4193
4768
|
const testOptions = {
|
|
4194
4769
|
template: options.template,
|
|
4195
4770
|
config: options.config,
|
|
4196
|
-
updateSnapshots: options.updateSnapshots
|
|
4771
|
+
updateSnapshots: options.updateSnapshots,
|
|
4772
|
+
refresh: options.refresh,
|
|
4773
|
+
json: options.json,
|
|
4774
|
+
summary: options.summary,
|
|
4775
|
+
overlay: options.overlay || []
|
|
4197
4776
|
};
|
|
4198
4777
|
const exitCode = await testCommand(testOptions);
|
|
4199
4778
|
process.exit(exitCode);
|
|
4200
4779
|
});
|
|
4201
4780
|
program.addCommand(template);
|
|
4202
|
-
program.command("trace").description("Explore traceability chains and assemble context from specs, code, and tests").argument("[ids...]", "Traceability ID(s) to trace").option("--all", "Trace all known IDs in the project", false).option("--task <path>", "Resolve IDs from a task file").option("--file <path>", "Resolve IDs from a source file's markers").option("--content", "Output actual file sections instead of locations", false).option("--list", "Output file paths only (no content or tree)", false).option("--max-tokens <n>", "Cap content output size (implies --content)").option("--depth <n>", "Maximum traversal depth").option("--scope <code>", "Limit results to a feature code").option("--direction <dir>", "Traversal direction: both, forward, reverse", "both").option("--no-code", "Exclude source code (spec-only context)").option("--no-tests", "Exclude test files").option("--json", "Output as JSON", false).option("-A <n>", "Lines of context after a code marker (--content only; default: 20)").option("-B <n>", "Lines of context before a code marker (--content only; default: 5)").option("-C <n>", "Lines of context before and after (--content only; overrides -A and -B)").option("-c, --config <path>", "Path to configuration file").action(async (ids, options) => {
|
|
4781
|
+
program.command("trace").description("Explore traceability chains and assemble context from specs, code, and tests").argument("[ids...]", "Traceability ID(s) to trace").option("--all", "Trace all known IDs in the project", false).option("--task <path>", "Resolve IDs from a task file").option("--file <path>", "Resolve IDs from a source file's markers").option("--content", "Output actual file sections instead of locations", false).option("--list", "Output file paths only (no content or tree)", false).option("--max-tokens <n>", "Cap content output size (implies --content)").option("--depth <n>", "Maximum traversal depth").option("--scope <code>", "Limit results to a feature code").option("--direction <dir>", "Traversal direction: both, forward, reverse", "both").option("--no-code", "Exclude source code (spec-only context)").option("--no-tests", "Exclude test files").option("--json", "Output results as JSON", false).option("--summary", "Output compact one-line summary", false).option("-A <n>", "Lines of context after a code marker (--content only; default: 20)").option("-B <n>", "Lines of context before a code marker (--content only; default: 5)").option("-C <n>", "Lines of context before and after (--content only; overrides -A and -B)").option("-c, --config <path>", "Path to configuration file").action(async (ids, options) => {
|
|
4203
4782
|
const traceOptions = {
|
|
4204
4783
|
ids,
|
|
4205
4784
|
all: options.all,
|
|
@@ -4208,6 +4787,7 @@ program.command("trace").description("Explore traceability chains and assemble c
|
|
|
4208
4787
|
content: options.content,
|
|
4209
4788
|
list: options.list,
|
|
4210
4789
|
json: options.json,
|
|
4790
|
+
summary: options.summary,
|
|
4211
4791
|
maxTokens: options.maxTokens !== void 0 ? Number(options.maxTokens) : void 0,
|
|
4212
4792
|
depth: options.depth !== void 0 ? Number(options.depth) : void 0,
|
|
4213
4793
|
scope: options.scope,
|
|
@@ -4228,7 +4808,7 @@ var isDisabledByEnv = !!process.env.NO_UPDATE_NOTIFIER;
|
|
|
4228
4808
|
if (!isJsonOrSummary && isTTY && !isDisabledByEnv) {
|
|
4229
4809
|
updateCheckPromise = (async () => {
|
|
4230
4810
|
try {
|
|
4231
|
-
const { configLoader: configLoader2 } = await import("./config-
|
|
4811
|
+
const { configLoader: configLoader2 } = await import("./config-WL3SLSP6.js");
|
|
4232
4812
|
const configPath = process.argv.indexOf("-c") !== -1 ? process.argv[process.argv.indexOf("-c") + 1] : process.argv.indexOf("--config") !== -1 ? process.argv[process.argv.indexOf("--config") + 1] : void 0;
|
|
4233
4813
|
const fileConfig = await configLoader2.load(configPath ?? null);
|
|
4234
4814
|
const updateCheckConfig = fileConfig?.["update-check"];
|