@ncoderz/awa 1.1.0 → 1.2.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-3SSUJFKN.js +625 -0
- package/dist/chunk-3SSUJFKN.js.map +1 -0
- package/dist/config-2TOQATI3.js +10 -0
- package/dist/config-2TOQATI3.js.map +1 -0
- package/dist/index.js +261 -664
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/awa/.agent/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/.agents/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/.claude/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/.gemini/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/.github/skills/awa-usage/SKILL.md +8 -0
- package/templates/awa/.kilocode/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/.opencode/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/.qwen/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/.roo/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/.windsurf/skills/awa-usage/SKILL.md +3 -0
- package/templates/awa/_partials/_skill.awa-usage.md +6 -0
- package/templates/awa/_partials/awa.usage.md +265 -0
package/dist/index.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ConfigError,
|
|
4
|
+
DiffError,
|
|
5
|
+
GenerationError,
|
|
6
|
+
TemplateError,
|
|
7
|
+
configLoader,
|
|
8
|
+
deleteFile,
|
|
9
|
+
ensureDir,
|
|
10
|
+
getCacheDir,
|
|
11
|
+
getTemplateDir,
|
|
12
|
+
logger,
|
|
13
|
+
pathExists,
|
|
14
|
+
readBinaryFile,
|
|
15
|
+
readTextFile,
|
|
16
|
+
rmDir,
|
|
17
|
+
walkDirectory,
|
|
18
|
+
writeTextFile
|
|
19
|
+
} from "./chunk-3SSUJFKN.js";
|
|
2
20
|
|
|
3
21
|
// src/cli/index.ts
|
|
4
22
|
import { Command } from "commander";
|
|
@@ -6,7 +24,7 @@ import { Command } from "commander";
|
|
|
6
24
|
// src/_generated/package_info.ts
|
|
7
25
|
var PACKAGE_INFO = {
|
|
8
26
|
"name": "@ncoderz/awa",
|
|
9
|
-
"version": "1.
|
|
27
|
+
"version": "1.2.0",
|
|
10
28
|
"author": "Richard Sewell <richard.sewell@ncoderz.com>",
|
|
11
29
|
"license": "MIT",
|
|
12
30
|
"description": "awa is an Agent Workflow for AIs. It is also a CLI tool to powerfully manage agent workflow files using templates."
|
|
@@ -110,7 +128,7 @@ var IGNORE_LINE_RE = /@awa-ignore\b/;
|
|
|
110
128
|
var IGNORE_START_RE = /@awa-ignore-start\b/;
|
|
111
129
|
var IGNORE_END_RE = /@awa-ignore-end\b/;
|
|
112
130
|
async function scanMarkers(config) {
|
|
113
|
-
const files = await collectCodeFiles(config.codeGlobs, config.
|
|
131
|
+
const files = await collectCodeFiles(config.codeGlobs, config.codeIgnore);
|
|
114
132
|
const markers = [];
|
|
115
133
|
const findings = [];
|
|
116
134
|
for (const filePath of files) {
|
|
@@ -992,7 +1010,7 @@ function collectAllCodeBlocks(section) {
|
|
|
992
1010
|
import { readFile as readFile4 } from "fs/promises";
|
|
993
1011
|
import { basename } from "path";
|
|
994
1012
|
async function parseSpecs(config) {
|
|
995
|
-
const files = await collectSpecFiles(config.specGlobs, config.
|
|
1013
|
+
const files = await collectSpecFiles(config.specGlobs, config.specIgnore);
|
|
996
1014
|
const specFiles = [];
|
|
997
1015
|
const requirementIds = /* @__PURE__ */ new Set();
|
|
998
1016
|
const acIds = /* @__PURE__ */ new Set();
|
|
@@ -1156,9 +1174,30 @@ function checkSpecAgainstSpec(specs, markers, config) {
|
|
|
1156
1174
|
|
|
1157
1175
|
// src/core/check/types.ts
|
|
1158
1176
|
var DEFAULT_CHECK_CONFIG = {
|
|
1159
|
-
specGlobs: [
|
|
1160
|
-
|
|
1161
|
-
|
|
1177
|
+
specGlobs: [
|
|
1178
|
+
".awa/specs/ARCHITECTURE.md",
|
|
1179
|
+
".awa/specs/FEAT-*.md",
|
|
1180
|
+
".awa/specs/REQ-*.md",
|
|
1181
|
+
".awa/specs/DESIGN-*.md",
|
|
1182
|
+
".awa/specs/EXAMPLES-*.md",
|
|
1183
|
+
".awa/specs/API-*.tsp",
|
|
1184
|
+
".awa/tasks/TASK-*.md",
|
|
1185
|
+
".awa/plans/PLAN-*.md",
|
|
1186
|
+
".awa/align/ALIGN-*.md"
|
|
1187
|
+
],
|
|
1188
|
+
codeGlobs: [
|
|
1189
|
+
"**/*.{ts,js,tsx,jsx,mts,mjs,cjs,py,go,rs,java,kt,kts,cs,c,h,cpp,cc,cxx,hpp,hxx,swift,rb,php,scala,ex,exs,dart,lua,zig}"
|
|
1190
|
+
],
|
|
1191
|
+
specIgnore: [],
|
|
1192
|
+
codeIgnore: [
|
|
1193
|
+
"node_modules/**",
|
|
1194
|
+
"dist/**",
|
|
1195
|
+
"vendor/**",
|
|
1196
|
+
"target/**",
|
|
1197
|
+
"build/**",
|
|
1198
|
+
"out/**",
|
|
1199
|
+
".awa/**"
|
|
1200
|
+
],
|
|
1162
1201
|
ignoreMarkers: [],
|
|
1163
1202
|
markers: ["@awa-impl", "@awa-test", "@awa-component"],
|
|
1164
1203
|
idPattern: "([A-Z][A-Z0-9]*-\\d+(?:\\.\\d+)?(?:_AC-\\d+)?|[A-Z][A-Z0-9]*_P-\\d+)",
|
|
@@ -1170,576 +1209,6 @@ var DEFAULT_CHECK_CONFIG = {
|
|
|
1170
1209
|
specOnly: false
|
|
1171
1210
|
};
|
|
1172
1211
|
|
|
1173
|
-
// src/core/config.ts
|
|
1174
|
-
import { parse } from "smol-toml";
|
|
1175
|
-
|
|
1176
|
-
// src/types/index.ts
|
|
1177
|
-
var DiffError = class extends Error {
|
|
1178
|
-
constructor(message) {
|
|
1179
|
-
super(message);
|
|
1180
|
-
this.name = "DiffError";
|
|
1181
|
-
}
|
|
1182
|
-
};
|
|
1183
|
-
var ConfigError = class extends Error {
|
|
1184
|
-
constructor(message, code, filePath) {
|
|
1185
|
-
super(message);
|
|
1186
|
-
this.code = code;
|
|
1187
|
-
this.filePath = filePath;
|
|
1188
|
-
this.name = "ConfigError";
|
|
1189
|
-
}
|
|
1190
|
-
};
|
|
1191
|
-
var TemplateError = class extends Error {
|
|
1192
|
-
constructor(message, code, source) {
|
|
1193
|
-
super(message);
|
|
1194
|
-
this.code = code;
|
|
1195
|
-
this.source = source;
|
|
1196
|
-
this.name = "TemplateError";
|
|
1197
|
-
}
|
|
1198
|
-
};
|
|
1199
|
-
var GenerationError = class extends Error {
|
|
1200
|
-
constructor(message, code) {
|
|
1201
|
-
super(message);
|
|
1202
|
-
this.code = code;
|
|
1203
|
-
this.name = "GenerationError";
|
|
1204
|
-
}
|
|
1205
|
-
};
|
|
1206
|
-
|
|
1207
|
-
// src/utils/fs.ts
|
|
1208
|
-
import { mkdir, readdir, readFile as readFile5, rm, stat, writeFile } from "fs/promises";
|
|
1209
|
-
import { homedir } from "os";
|
|
1210
|
-
import { dirname, join as join2 } from "path";
|
|
1211
|
-
import { fileURLToPath } from "url";
|
|
1212
|
-
async function ensureDir(dirPath) {
|
|
1213
|
-
await mkdir(dirPath, { recursive: true });
|
|
1214
|
-
}
|
|
1215
|
-
async function pathExists(path) {
|
|
1216
|
-
try {
|
|
1217
|
-
await stat(path);
|
|
1218
|
-
return true;
|
|
1219
|
-
} catch {
|
|
1220
|
-
return false;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
async function readTextFile(path) {
|
|
1224
|
-
return readFile5(path, "utf-8");
|
|
1225
|
-
}
|
|
1226
|
-
async function readBinaryFile(path) {
|
|
1227
|
-
return readFile5(path);
|
|
1228
|
-
}
|
|
1229
|
-
async function writeTextFile(path, content) {
|
|
1230
|
-
await ensureDir(dirname(path));
|
|
1231
|
-
await writeFile(path, content, "utf-8");
|
|
1232
|
-
}
|
|
1233
|
-
async function* walkDirectory(dir) {
|
|
1234
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
1235
|
-
for (const entry of entries) {
|
|
1236
|
-
const fullPath = join2(dir, entry.name);
|
|
1237
|
-
if (entry.isDirectory()) {
|
|
1238
|
-
if (entry.name.startsWith("_")) {
|
|
1239
|
-
continue;
|
|
1240
|
-
}
|
|
1241
|
-
yield* walkDirectory(fullPath);
|
|
1242
|
-
} else if (entry.isFile()) {
|
|
1243
|
-
if (entry.name.startsWith("_")) {
|
|
1244
|
-
continue;
|
|
1245
|
-
}
|
|
1246
|
-
yield fullPath;
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
function getCacheDir() {
|
|
1251
|
-
return join2(homedir(), ".cache", "awa", "templates");
|
|
1252
|
-
}
|
|
1253
|
-
function getTemplateDir() {
|
|
1254
|
-
const currentFile = fileURLToPath(import.meta.url);
|
|
1255
|
-
const currentDir = dirname(currentFile);
|
|
1256
|
-
if (currentDir.includes("/dist")) {
|
|
1257
|
-
return join2(dirname(currentDir), "templates");
|
|
1258
|
-
}
|
|
1259
|
-
return join2(currentDir, "..", "..", "templates");
|
|
1260
|
-
}
|
|
1261
|
-
async function rmDir(dirPath) {
|
|
1262
|
-
await rm(dirPath, { recursive: true, force: true });
|
|
1263
|
-
}
|
|
1264
|
-
async function deleteFile(filePath) {
|
|
1265
|
-
await rm(filePath, { force: true });
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
// src/utils/logger.ts
|
|
1269
|
-
import chalk2 from "chalk";
|
|
1270
|
-
var Logger = class {
|
|
1271
|
-
info(message) {
|
|
1272
|
-
console.log(chalk2.blue("\u2139"), message);
|
|
1273
|
-
}
|
|
1274
|
-
success(message) {
|
|
1275
|
-
console.log(chalk2.green("\u2714"), message);
|
|
1276
|
-
}
|
|
1277
|
-
warn(message) {
|
|
1278
|
-
console.warn(chalk2.yellow("\u26A0"), message);
|
|
1279
|
-
}
|
|
1280
|
-
error(message) {
|
|
1281
|
-
console.error(chalk2.red("\u2716"), message);
|
|
1282
|
-
}
|
|
1283
|
-
fileAction(action) {
|
|
1284
|
-
const { type, outputPath } = action;
|
|
1285
|
-
switch (type) {
|
|
1286
|
-
case "create":
|
|
1287
|
-
console.log(chalk2.green(" + "), chalk2.dim(outputPath));
|
|
1288
|
-
break;
|
|
1289
|
-
case "overwrite":
|
|
1290
|
-
console.log(chalk2.yellow(" ~ "), chalk2.dim(outputPath));
|
|
1291
|
-
break;
|
|
1292
|
-
case "skip-user":
|
|
1293
|
-
console.log(chalk2.blue(" - "), chalk2.dim(outputPath), chalk2.dim("(skipped)"));
|
|
1294
|
-
break;
|
|
1295
|
-
case "skip-empty":
|
|
1296
|
-
console.log(chalk2.dim(" \xB7 "), chalk2.dim(outputPath), chalk2.dim("(empty)"));
|
|
1297
|
-
break;
|
|
1298
|
-
case "skip-equal":
|
|
1299
|
-
console.log(chalk2.dim(" = "), chalk2.dim(outputPath), chalk2.dim("(unchanged)"));
|
|
1300
|
-
break;
|
|
1301
|
-
case "delete":
|
|
1302
|
-
console.log(chalk2.red(" \u2716 "), chalk2.dim(outputPath), chalk2.red("(deleted)"));
|
|
1303
|
-
break;
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
// @awa-impl: GEN-9_AC-1, GEN-9_AC-2, GEN-9_AC-3, GEN-9_AC-4, GEN-9_AC-5, GEN-9_AC-6
|
|
1307
|
-
summary(result) {
|
|
1308
|
-
console.log("");
|
|
1309
|
-
console.log(chalk2.bold("Summary:"));
|
|
1310
|
-
if (result.created === 0 && result.overwritten === 0 && result.deleted === 0) {
|
|
1311
|
-
console.log(chalk2.yellow(" \u26A0 No files were created, overwritten, or deleted"));
|
|
1312
|
-
}
|
|
1313
|
-
if (result.created > 0) {
|
|
1314
|
-
console.log(chalk2.green(` Created: ${result.created}`));
|
|
1315
|
-
}
|
|
1316
|
-
if (result.overwritten > 0) {
|
|
1317
|
-
console.log(chalk2.yellow(` Overwritten: ${result.overwritten}`));
|
|
1318
|
-
}
|
|
1319
|
-
if (result.deleted > 0) {
|
|
1320
|
-
console.log(chalk2.red(` Deleted: ${result.deleted}`));
|
|
1321
|
-
}
|
|
1322
|
-
if (result.skippedEqual > 0) {
|
|
1323
|
-
console.log(chalk2.dim(` Skipped (equal): ${result.skippedEqual}`));
|
|
1324
|
-
}
|
|
1325
|
-
if (result.skippedUser > 0) {
|
|
1326
|
-
console.log(chalk2.blue(` Skipped (user): ${result.skippedUser}`));
|
|
1327
|
-
}
|
|
1328
|
-
if (result.skippedEmpty > 0) {
|
|
1329
|
-
console.log(chalk2.dim(` Skipped (empty): ${result.skippedEmpty}`));
|
|
1330
|
-
}
|
|
1331
|
-
console.log("");
|
|
1332
|
-
}
|
|
1333
|
-
// @awa-impl: DIFF-4_AC-3
|
|
1334
|
-
diffLine(line, type) {
|
|
1335
|
-
switch (type) {
|
|
1336
|
-
case "add":
|
|
1337
|
-
console.log(chalk2.green(line));
|
|
1338
|
-
break;
|
|
1339
|
-
case "remove":
|
|
1340
|
-
console.log(chalk2.red(line));
|
|
1341
|
-
break;
|
|
1342
|
-
case "context":
|
|
1343
|
-
console.log(chalk2.dim(line));
|
|
1344
|
-
break;
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
// @awa-impl: DIFF-4_AC-4, DIFF-4_AC-5
|
|
1348
|
-
diffSummary(result) {
|
|
1349
|
-
console.log("");
|
|
1350
|
-
const filesCompared = result.identical + result.modified + result.newFiles + result.extraFiles + result.binaryDiffers + result.deleteListed;
|
|
1351
|
-
const differences = result.modified + result.newFiles + result.extraFiles + result.binaryDiffers + result.deleteListed;
|
|
1352
|
-
console.log(chalk2.bold(`${filesCompared} files compared, ${differences} differences`));
|
|
1353
|
-
if (!result.hasDifferences) {
|
|
1354
|
-
console.log(chalk2.green("\u2714 No differences found"));
|
|
1355
|
-
}
|
|
1356
|
-
console.log(chalk2.bold("Summary:"));
|
|
1357
|
-
console.log(chalk2.dim(` Identical: ${result.identical}`));
|
|
1358
|
-
if (result.modified > 0) {
|
|
1359
|
-
console.log(chalk2.yellow(` Modified: ${result.modified}`));
|
|
1360
|
-
}
|
|
1361
|
-
if (result.newFiles > 0) {
|
|
1362
|
-
console.log(chalk2.green(` New: ${result.newFiles}`));
|
|
1363
|
-
}
|
|
1364
|
-
if (result.extraFiles > 0) {
|
|
1365
|
-
console.log(chalk2.red(` Extra: ${result.extraFiles}`));
|
|
1366
|
-
}
|
|
1367
|
-
if (result.binaryDiffers > 0) {
|
|
1368
|
-
console.log(chalk2.red(` Binary differs: ${result.binaryDiffers}`));
|
|
1369
|
-
}
|
|
1370
|
-
if (result.deleteListed > 0) {
|
|
1371
|
-
console.log(chalk2.red(` Delete listed: ${result.deleteListed}`));
|
|
1372
|
-
}
|
|
1373
|
-
console.log("");
|
|
1374
|
-
}
|
|
1375
|
-
};
|
|
1376
|
-
var logger = new Logger();
|
|
1377
|
-
|
|
1378
|
-
// src/core/config.ts
|
|
1379
|
-
var DEFAULT_CONFIG_PATH = ".awa.toml";
|
|
1380
|
-
var ConfigLoader = class {
|
|
1381
|
-
// @awa-impl: CFG-1_AC-1, CFG-1_AC-2, CFG-1_AC-3, CFG-1_AC-4
|
|
1382
|
-
async load(configPath) {
|
|
1383
|
-
const pathToLoad = configPath ?? DEFAULT_CONFIG_PATH;
|
|
1384
|
-
const exists = await pathExists(pathToLoad);
|
|
1385
|
-
if (configPath && !exists) {
|
|
1386
|
-
throw new ConfigError(
|
|
1387
|
-
`Configuration file not found: ${configPath}`,
|
|
1388
|
-
"FILE_NOT_FOUND",
|
|
1389
|
-
configPath
|
|
1390
|
-
);
|
|
1391
|
-
}
|
|
1392
|
-
if (!configPath && !exists) {
|
|
1393
|
-
return null;
|
|
1394
|
-
}
|
|
1395
|
-
try {
|
|
1396
|
-
const content = await readTextFile(pathToLoad);
|
|
1397
|
-
const parsed = parse(content);
|
|
1398
|
-
const config = {};
|
|
1399
|
-
if (parsed.output !== void 0) {
|
|
1400
|
-
if (typeof parsed.output !== "string") {
|
|
1401
|
-
throw new ConfigError(
|
|
1402
|
-
`Invalid type for 'output': expected string, got ${typeof parsed.output}`,
|
|
1403
|
-
"INVALID_TYPE",
|
|
1404
|
-
pathToLoad
|
|
1405
|
-
);
|
|
1406
|
-
}
|
|
1407
|
-
config.output = parsed.output;
|
|
1408
|
-
}
|
|
1409
|
-
if (parsed.template !== void 0) {
|
|
1410
|
-
if (typeof parsed.template !== "string") {
|
|
1411
|
-
throw new ConfigError(
|
|
1412
|
-
`Invalid type for 'template': expected string, got ${typeof parsed.template}`,
|
|
1413
|
-
"INVALID_TYPE",
|
|
1414
|
-
pathToLoad
|
|
1415
|
-
);
|
|
1416
|
-
}
|
|
1417
|
-
config.template = parsed.template;
|
|
1418
|
-
}
|
|
1419
|
-
if (parsed.features !== void 0) {
|
|
1420
|
-
if (!Array.isArray(parsed.features) || !parsed.features.every((f) => typeof f === "string")) {
|
|
1421
|
-
throw new ConfigError(
|
|
1422
|
-
`Invalid type for 'features': expected array of strings`,
|
|
1423
|
-
"INVALID_TYPE",
|
|
1424
|
-
pathToLoad
|
|
1425
|
-
);
|
|
1426
|
-
}
|
|
1427
|
-
config.features = parsed.features;
|
|
1428
|
-
}
|
|
1429
|
-
if (parsed.preset !== void 0) {
|
|
1430
|
-
if (!Array.isArray(parsed.preset) || !parsed.preset.every((p) => typeof p === "string")) {
|
|
1431
|
-
throw new ConfigError(
|
|
1432
|
-
`Invalid type for 'preset': expected array of strings`,
|
|
1433
|
-
"INVALID_TYPE",
|
|
1434
|
-
pathToLoad
|
|
1435
|
-
);
|
|
1436
|
-
}
|
|
1437
|
-
config.preset = parsed.preset;
|
|
1438
|
-
}
|
|
1439
|
-
if (parsed["remove-features"] !== void 0) {
|
|
1440
|
-
if (!Array.isArray(parsed["remove-features"]) || !parsed["remove-features"].every((f) => typeof f === "string")) {
|
|
1441
|
-
throw new ConfigError(
|
|
1442
|
-
`Invalid type for 'remove-features': expected array of strings`,
|
|
1443
|
-
"INVALID_TYPE",
|
|
1444
|
-
pathToLoad
|
|
1445
|
-
);
|
|
1446
|
-
}
|
|
1447
|
-
config["remove-features"] = parsed["remove-features"];
|
|
1448
|
-
}
|
|
1449
|
-
if (parsed.force !== void 0) {
|
|
1450
|
-
if (typeof parsed.force !== "boolean") {
|
|
1451
|
-
throw new ConfigError(
|
|
1452
|
-
`Invalid type for 'force': expected boolean, got ${typeof parsed.force}`,
|
|
1453
|
-
"INVALID_TYPE",
|
|
1454
|
-
pathToLoad
|
|
1455
|
-
);
|
|
1456
|
-
}
|
|
1457
|
-
config.force = parsed.force;
|
|
1458
|
-
}
|
|
1459
|
-
if (parsed["dry-run"] !== void 0) {
|
|
1460
|
-
if (typeof parsed["dry-run"] !== "boolean") {
|
|
1461
|
-
throw new ConfigError(
|
|
1462
|
-
`Invalid type for 'dry-run': expected boolean, got ${typeof parsed["dry-run"]}`,
|
|
1463
|
-
"INVALID_TYPE",
|
|
1464
|
-
pathToLoad
|
|
1465
|
-
);
|
|
1466
|
-
}
|
|
1467
|
-
config["dry-run"] = parsed["dry-run"];
|
|
1468
|
-
}
|
|
1469
|
-
if (parsed.delete !== void 0) {
|
|
1470
|
-
if (typeof parsed.delete !== "boolean") {
|
|
1471
|
-
throw new ConfigError(
|
|
1472
|
-
`Invalid type for 'delete': expected boolean, got ${typeof parsed.delete}`,
|
|
1473
|
-
"INVALID_TYPE",
|
|
1474
|
-
pathToLoad
|
|
1475
|
-
);
|
|
1476
|
-
}
|
|
1477
|
-
config.delete = parsed.delete;
|
|
1478
|
-
}
|
|
1479
|
-
if (parsed.refresh !== void 0) {
|
|
1480
|
-
if (typeof parsed.refresh !== "boolean") {
|
|
1481
|
-
throw new ConfigError(
|
|
1482
|
-
`Invalid type for 'refresh': expected boolean, got ${typeof parsed.refresh}`,
|
|
1483
|
-
"INVALID_TYPE",
|
|
1484
|
-
pathToLoad
|
|
1485
|
-
);
|
|
1486
|
-
}
|
|
1487
|
-
config.refresh = parsed.refresh;
|
|
1488
|
-
}
|
|
1489
|
-
if (parsed.presets !== void 0) {
|
|
1490
|
-
if (parsed.presets === null || typeof parsed.presets !== "object" || Array.isArray(parsed.presets)) {
|
|
1491
|
-
throw new ConfigError(
|
|
1492
|
-
`Invalid type for 'presets': expected table of string arrays`,
|
|
1493
|
-
"INVALID_PRESET",
|
|
1494
|
-
pathToLoad
|
|
1495
|
-
);
|
|
1496
|
-
}
|
|
1497
|
-
const defs = {};
|
|
1498
|
-
for (const [presetName, value] of Object.entries(
|
|
1499
|
-
parsed.presets
|
|
1500
|
-
)) {
|
|
1501
|
-
if (!Array.isArray(value) || !value.every((v) => typeof v === "string")) {
|
|
1502
|
-
throw new ConfigError(
|
|
1503
|
-
`Invalid preset '${presetName}': expected array of strings`,
|
|
1504
|
-
"INVALID_PRESET",
|
|
1505
|
-
pathToLoad
|
|
1506
|
-
);
|
|
1507
|
-
}
|
|
1508
|
-
defs[presetName] = value;
|
|
1509
|
-
}
|
|
1510
|
-
config.presets = defs;
|
|
1511
|
-
}
|
|
1512
|
-
if (parsed["list-unknown"] !== void 0) {
|
|
1513
|
-
if (typeof parsed["list-unknown"] !== "boolean") {
|
|
1514
|
-
throw new ConfigError(
|
|
1515
|
-
`Invalid type for 'list-unknown': expected boolean, got ${typeof parsed["list-unknown"]}`,
|
|
1516
|
-
"INVALID_TYPE",
|
|
1517
|
-
pathToLoad
|
|
1518
|
-
);
|
|
1519
|
-
}
|
|
1520
|
-
config["list-unknown"] = parsed["list-unknown"];
|
|
1521
|
-
}
|
|
1522
|
-
if (parsed.check !== void 0) {
|
|
1523
|
-
if (parsed.check === null || typeof parsed.check !== "object" || Array.isArray(parsed.check)) {
|
|
1524
|
-
throw new ConfigError(
|
|
1525
|
-
`Invalid type for 'check': expected table`,
|
|
1526
|
-
"INVALID_TYPE",
|
|
1527
|
-
pathToLoad
|
|
1528
|
-
);
|
|
1529
|
-
}
|
|
1530
|
-
config.check = parsed.check;
|
|
1531
|
-
}
|
|
1532
|
-
if (parsed.overlay !== void 0) {
|
|
1533
|
-
if (!Array.isArray(parsed.overlay) || !parsed.overlay.every((o) => typeof o === "string")) {
|
|
1534
|
-
throw new ConfigError(
|
|
1535
|
-
`Invalid type for 'overlay': expected array of strings`,
|
|
1536
|
-
"INVALID_TYPE",
|
|
1537
|
-
pathToLoad
|
|
1538
|
-
);
|
|
1539
|
-
}
|
|
1540
|
-
config.overlay = parsed.overlay;
|
|
1541
|
-
}
|
|
1542
|
-
if (parsed.targets !== void 0) {
|
|
1543
|
-
if (parsed.targets === null || typeof parsed.targets !== "object" || Array.isArray(parsed.targets)) {
|
|
1544
|
-
throw new ConfigError(
|
|
1545
|
-
`Invalid type for 'targets': expected table of target sections`,
|
|
1546
|
-
"INVALID_TYPE",
|
|
1547
|
-
pathToLoad
|
|
1548
|
-
);
|
|
1549
|
-
}
|
|
1550
|
-
const targets = {};
|
|
1551
|
-
for (const [targetName, targetValue] of Object.entries(
|
|
1552
|
-
parsed.targets
|
|
1553
|
-
)) {
|
|
1554
|
-
if (targetValue === null || typeof targetValue !== "object" || Array.isArray(targetValue)) {
|
|
1555
|
-
throw new ConfigError(
|
|
1556
|
-
`Invalid target '${targetName}': expected table`,
|
|
1557
|
-
"INVALID_TYPE",
|
|
1558
|
-
pathToLoad
|
|
1559
|
-
);
|
|
1560
|
-
}
|
|
1561
|
-
targets[targetName] = this.parseTargetSection(
|
|
1562
|
-
targetValue,
|
|
1563
|
-
targetName,
|
|
1564
|
-
pathToLoad
|
|
1565
|
-
);
|
|
1566
|
-
}
|
|
1567
|
-
config.targets = targets;
|
|
1568
|
-
}
|
|
1569
|
-
const knownKeys = /* @__PURE__ */ new Set([
|
|
1570
|
-
"output",
|
|
1571
|
-
"template",
|
|
1572
|
-
"features",
|
|
1573
|
-
"preset",
|
|
1574
|
-
"remove-features",
|
|
1575
|
-
"presets",
|
|
1576
|
-
"force",
|
|
1577
|
-
"dry-run",
|
|
1578
|
-
"delete",
|
|
1579
|
-
"refresh",
|
|
1580
|
-
"list-unknown",
|
|
1581
|
-
"check",
|
|
1582
|
-
"targets",
|
|
1583
|
-
"overlay"
|
|
1584
|
-
]);
|
|
1585
|
-
for (const key of Object.keys(parsed)) {
|
|
1586
|
-
if (!knownKeys.has(key)) {
|
|
1587
|
-
logger.warn(`Unknown configuration option: '${key}'`);
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
return config;
|
|
1591
|
-
} catch (error) {
|
|
1592
|
-
if (error instanceof ConfigError) {
|
|
1593
|
-
throw error;
|
|
1594
|
-
}
|
|
1595
|
-
throw new ConfigError(
|
|
1596
|
-
`Failed to parse TOML configuration: ${error instanceof Error ? error.message : String(error)}`,
|
|
1597
|
-
"PARSE_ERROR",
|
|
1598
|
-
pathToLoad
|
|
1599
|
-
);
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
// @awa-impl: CFG-4_AC-1, CFG-4_AC-2, CFG-4_AC-3, CFG-4_AC-4
|
|
1603
|
-
// @awa-impl: CLI-2_AC-2, CLI-2_AC-3, CLI-2_AC-4
|
|
1604
|
-
merge(cli, file) {
|
|
1605
|
-
const output = cli.output ?? file?.output;
|
|
1606
|
-
if (!output) {
|
|
1607
|
-
throw new ConfigError(
|
|
1608
|
-
"Output directory is required. Provide it as a positional argument or in the config file.",
|
|
1609
|
-
"MISSING_OUTPUT",
|
|
1610
|
-
null
|
|
1611
|
-
);
|
|
1612
|
-
}
|
|
1613
|
-
const template = cli.template ?? file?.template ?? null;
|
|
1614
|
-
const features = cli.features ?? file?.features ?? [];
|
|
1615
|
-
const preset = cli.preset ?? file?.preset ?? [];
|
|
1616
|
-
const removeFeatures = cli.removeFeatures ?? file?.["remove-features"] ?? [];
|
|
1617
|
-
const presets = file?.presets ?? {};
|
|
1618
|
-
const force = cli.force ?? file?.force ?? false;
|
|
1619
|
-
const dryRun = cli.dryRun ?? file?.["dry-run"] ?? false;
|
|
1620
|
-
const enableDelete = cli.delete ?? file?.delete ?? false;
|
|
1621
|
-
const refresh = cli.refresh ?? file?.refresh ?? false;
|
|
1622
|
-
const listUnknown = cli.listUnknown ?? file?.["list-unknown"] ?? false;
|
|
1623
|
-
const overlay = cli.overlay ?? file?.overlay ?? [];
|
|
1624
|
-
const json = cli.json ?? false;
|
|
1625
|
-
const summary = cli.summary ?? false;
|
|
1626
|
-
return {
|
|
1627
|
-
output,
|
|
1628
|
-
template,
|
|
1629
|
-
features,
|
|
1630
|
-
preset,
|
|
1631
|
-
removeFeatures,
|
|
1632
|
-
force,
|
|
1633
|
-
dryRun,
|
|
1634
|
-
delete: enableDelete,
|
|
1635
|
-
refresh,
|
|
1636
|
-
presets,
|
|
1637
|
-
listUnknown,
|
|
1638
|
-
overlay,
|
|
1639
|
-
json,
|
|
1640
|
-
summary
|
|
1641
|
-
};
|
|
1642
|
-
}
|
|
1643
|
-
// Parse a [targets.<name>] section, validating allowed keys and types
|
|
1644
|
-
parseTargetSection(section, targetName, configPath) {
|
|
1645
|
-
const target = {};
|
|
1646
|
-
const allowedKeys = /* @__PURE__ */ new Set(["output", "template", "features", "preset", "remove-features"]);
|
|
1647
|
-
for (const key of Object.keys(section)) {
|
|
1648
|
-
if (!allowedKeys.has(key)) {
|
|
1649
|
-
logger.warn(`Unknown option in target '${targetName}': '${key}'`);
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
if (section.output !== void 0) {
|
|
1653
|
-
if (typeof section.output !== "string") {
|
|
1654
|
-
throw new ConfigError(
|
|
1655
|
-
`Invalid type for 'targets.${targetName}.output': expected string, got ${typeof section.output}`,
|
|
1656
|
-
"INVALID_TYPE",
|
|
1657
|
-
configPath
|
|
1658
|
-
);
|
|
1659
|
-
}
|
|
1660
|
-
target.output = section.output;
|
|
1661
|
-
}
|
|
1662
|
-
if (section.template !== void 0) {
|
|
1663
|
-
if (typeof section.template !== "string") {
|
|
1664
|
-
throw new ConfigError(
|
|
1665
|
-
`Invalid type for 'targets.${targetName}.template': expected string, got ${typeof section.template}`,
|
|
1666
|
-
"INVALID_TYPE",
|
|
1667
|
-
configPath
|
|
1668
|
-
);
|
|
1669
|
-
}
|
|
1670
|
-
target.template = section.template;
|
|
1671
|
-
}
|
|
1672
|
-
if (section.features !== void 0) {
|
|
1673
|
-
if (!Array.isArray(section.features) || !section.features.every((f) => typeof f === "string")) {
|
|
1674
|
-
throw new ConfigError(
|
|
1675
|
-
`Invalid type for 'targets.${targetName}.features': expected array of strings`,
|
|
1676
|
-
"INVALID_TYPE",
|
|
1677
|
-
configPath
|
|
1678
|
-
);
|
|
1679
|
-
}
|
|
1680
|
-
target.features = section.features;
|
|
1681
|
-
}
|
|
1682
|
-
if (section.preset !== void 0) {
|
|
1683
|
-
if (!Array.isArray(section.preset) || !section.preset.every((p) => typeof p === "string")) {
|
|
1684
|
-
throw new ConfigError(
|
|
1685
|
-
`Invalid type for 'targets.${targetName}.preset': expected array of strings`,
|
|
1686
|
-
"INVALID_TYPE",
|
|
1687
|
-
configPath
|
|
1688
|
-
);
|
|
1689
|
-
}
|
|
1690
|
-
target.preset = section.preset;
|
|
1691
|
-
}
|
|
1692
|
-
if (section["remove-features"] !== void 0) {
|
|
1693
|
-
if (!Array.isArray(section["remove-features"]) || !section["remove-features"].every((f) => typeof f === "string")) {
|
|
1694
|
-
throw new ConfigError(
|
|
1695
|
-
`Invalid type for 'targets.${targetName}.remove-features': expected array of strings`,
|
|
1696
|
-
"INVALID_TYPE",
|
|
1697
|
-
configPath
|
|
1698
|
-
);
|
|
1699
|
-
}
|
|
1700
|
-
target["remove-features"] = section["remove-features"];
|
|
1701
|
-
}
|
|
1702
|
-
return target;
|
|
1703
|
-
}
|
|
1704
|
-
// Resolve a target by merging target config with root config (target overrides root via nullish coalescing)
|
|
1705
|
-
resolveTarget(targetName, fileConfig) {
|
|
1706
|
-
const targets = fileConfig.targets;
|
|
1707
|
-
if (!targets || Object.keys(targets).length === 0) {
|
|
1708
|
-
throw new ConfigError(
|
|
1709
|
-
"No targets defined in configuration. Add [targets.<name>] sections to .awa.toml.",
|
|
1710
|
-
"NO_TARGETS",
|
|
1711
|
-
null
|
|
1712
|
-
);
|
|
1713
|
-
}
|
|
1714
|
-
const target = targets[targetName];
|
|
1715
|
-
if (!target) {
|
|
1716
|
-
throw new ConfigError(
|
|
1717
|
-
`Unknown target: '${targetName}'. Available targets: ${Object.keys(targets).join(", ")}`,
|
|
1718
|
-
"UNKNOWN_TARGET",
|
|
1719
|
-
null
|
|
1720
|
-
);
|
|
1721
|
-
}
|
|
1722
|
-
return {
|
|
1723
|
-
...fileConfig,
|
|
1724
|
-
output: target.output ?? fileConfig.output,
|
|
1725
|
-
template: target.template ?? fileConfig.template,
|
|
1726
|
-
features: target.features ?? fileConfig.features,
|
|
1727
|
-
preset: target.preset ?? fileConfig.preset,
|
|
1728
|
-
"remove-features": target["remove-features"] ?? fileConfig["remove-features"],
|
|
1729
|
-
targets: void 0
|
|
1730
|
-
// Don't propagate targets into resolved config
|
|
1731
|
-
};
|
|
1732
|
-
}
|
|
1733
|
-
// Get all target names from config
|
|
1734
|
-
getTargetNames(fileConfig) {
|
|
1735
|
-
if (!fileConfig?.targets) {
|
|
1736
|
-
return [];
|
|
1737
|
-
}
|
|
1738
|
-
return Object.keys(fileConfig.targets);
|
|
1739
|
-
}
|
|
1740
|
-
};
|
|
1741
|
-
var configLoader = new ConfigLoader();
|
|
1742
|
-
|
|
1743
1212
|
// src/commands/check.ts
|
|
1744
1213
|
async function checkCommand(cliOptions) {
|
|
1745
1214
|
try {
|
|
@@ -1787,9 +1256,14 @@ function buildCheckConfig(fileConfig, cliOptions) {
|
|
|
1787
1256
|
...DEFAULT_CHECK_CONFIG.crossRefPatterns
|
|
1788
1257
|
];
|
|
1789
1258
|
const idPattern = typeof section?.["id-pattern"] === "string" ? section["id-pattern"] : DEFAULT_CHECK_CONFIG.idPattern;
|
|
1790
|
-
const
|
|
1791
|
-
|
|
1792
|
-
|
|
1259
|
+
const configSpecIgnore = toStringArray(section?.["spec-ignore"]) ?? [
|
|
1260
|
+
...DEFAULT_CHECK_CONFIG.specIgnore
|
|
1261
|
+
];
|
|
1262
|
+
const configCodeIgnore = toStringArray(section?.["code-ignore"]) ?? [
|
|
1263
|
+
...DEFAULT_CHECK_CONFIG.codeIgnore
|
|
1264
|
+
];
|
|
1265
|
+
const specIgnore = [...configSpecIgnore, ...cliOptions.specIgnore ?? []];
|
|
1266
|
+
const codeIgnore = [...configCodeIgnore, ...cliOptions.codeIgnore ?? []];
|
|
1793
1267
|
const ignoreMarkers = toStringArray(section?.["ignore-markers"]) ?? [
|
|
1794
1268
|
...DEFAULT_CHECK_CONFIG.ignoreMarkers
|
|
1795
1269
|
];
|
|
@@ -1801,7 +1275,8 @@ function buildCheckConfig(fileConfig, cliOptions) {
|
|
|
1801
1275
|
return {
|
|
1802
1276
|
specGlobs,
|
|
1803
1277
|
codeGlobs,
|
|
1804
|
-
|
|
1278
|
+
specIgnore,
|
|
1279
|
+
codeIgnore,
|
|
1805
1280
|
ignoreMarkers,
|
|
1806
1281
|
markers,
|
|
1807
1282
|
idPattern,
|
|
@@ -1882,12 +1357,12 @@ var batchRunner = new BatchRunner();
|
|
|
1882
1357
|
|
|
1883
1358
|
// src/core/differ.ts
|
|
1884
1359
|
import { tmpdir } from "os";
|
|
1885
|
-
import { join as
|
|
1360
|
+
import { join as join4, relative as relative2 } from "path";
|
|
1886
1361
|
import { structuredPatch } from "diff";
|
|
1887
1362
|
import { isBinaryFile as detectBinaryFile } from "isbinaryfile";
|
|
1888
1363
|
|
|
1889
1364
|
// src/core/delete-list.ts
|
|
1890
|
-
import { join as
|
|
1365
|
+
import { join as join2 } from "path";
|
|
1891
1366
|
var DELETE_LIST_FILENAME = "_delete.txt";
|
|
1892
1367
|
function parseDeleteList(content) {
|
|
1893
1368
|
const entries = [];
|
|
@@ -1914,7 +1389,7 @@ function resolveDeleteList(entries, activeFeatures) {
|
|
|
1914
1389
|
return entries.filter((e) => e.features === void 0 || !e.features.some((f) => activeSet.has(f))).map((e) => e.path);
|
|
1915
1390
|
}
|
|
1916
1391
|
async function loadDeleteList(templatePath) {
|
|
1917
|
-
const deleteListPath =
|
|
1392
|
+
const deleteListPath = join2(templatePath, DELETE_LIST_FILENAME);
|
|
1918
1393
|
if (!await pathExists(deleteListPath)) {
|
|
1919
1394
|
return [];
|
|
1920
1395
|
}
|
|
@@ -1923,12 +1398,12 @@ async function loadDeleteList(templatePath) {
|
|
|
1923
1398
|
}
|
|
1924
1399
|
|
|
1925
1400
|
// src/core/generator.ts
|
|
1926
|
-
import { join as
|
|
1401
|
+
import { join as join3, relative } from "path";
|
|
1927
1402
|
|
|
1928
1403
|
// src/core/resolver.ts
|
|
1929
1404
|
import { MultiSelectPrompt } from "@clack/core";
|
|
1930
1405
|
import { isCancel, multiselect } from "@clack/prompts";
|
|
1931
|
-
import
|
|
1406
|
+
import chalk2 from "chalk";
|
|
1932
1407
|
var _unicode = process.platform !== "win32";
|
|
1933
1408
|
var _s = (c, fb) => _unicode ? c : fb;
|
|
1934
1409
|
var _CHECKED = _s("\u25FC", "[+]");
|
|
@@ -1938,20 +1413,20 @@ var _BAR = _s("\u2502", "|");
|
|
|
1938
1413
|
var _BAR_END = _s("\u2514", "-");
|
|
1939
1414
|
function _renderDeleteItem(opt, state) {
|
|
1940
1415
|
const label = opt.label ?? opt.value;
|
|
1941
|
-
const hint = opt.hint ? ` ${
|
|
1416
|
+
const hint = opt.hint ? ` ${chalk2.dim(`(${opt.hint})`)}` : "";
|
|
1942
1417
|
switch (state) {
|
|
1943
1418
|
case "active":
|
|
1944
|
-
return `${
|
|
1419
|
+
return `${chalk2.cyan(_UNCHECKED_A)} ${label}${hint}`;
|
|
1945
1420
|
case "selected":
|
|
1946
|
-
return `${
|
|
1421
|
+
return `${chalk2.red(_CHECKED)} ${chalk2.dim(label)}${hint}`;
|
|
1947
1422
|
case "active-selected":
|
|
1948
|
-
return `${
|
|
1423
|
+
return `${chalk2.red(_CHECKED)} ${label}${hint}`;
|
|
1949
1424
|
case "cancelled":
|
|
1950
|
-
return
|
|
1425
|
+
return chalk2.strikethrough(chalk2.dim(label));
|
|
1951
1426
|
case "submitted":
|
|
1952
|
-
return
|
|
1427
|
+
return chalk2.dim(label);
|
|
1953
1428
|
default:
|
|
1954
|
-
return `${
|
|
1429
|
+
return `${chalk2.dim(_UNCHECKED)} ${chalk2.dim(label)}`;
|
|
1955
1430
|
}
|
|
1956
1431
|
}
|
|
1957
1432
|
async function deleteMultiselect(opts) {
|
|
@@ -1962,8 +1437,8 @@ async function deleteMultiselect(opts) {
|
|
|
1962
1437
|
required,
|
|
1963
1438
|
render() {
|
|
1964
1439
|
const self = this;
|
|
1965
|
-
const header = `${
|
|
1966
|
-
${
|
|
1440
|
+
const header = `${chalk2.gray(_BAR)}
|
|
1441
|
+
${chalk2.cyan(_BAR)} ${message}
|
|
1967
1442
|
`;
|
|
1968
1443
|
const getState = (opt, idx) => {
|
|
1969
1444
|
const active = idx === self.cursor;
|
|
@@ -1975,16 +1450,16 @@ ${chalk3.cyan(_BAR)} ${message}
|
|
|
1975
1450
|
};
|
|
1976
1451
|
switch (self.state) {
|
|
1977
1452
|
case "submit":
|
|
1978
|
-
return `${header}${
|
|
1453
|
+
return `${header}${chalk2.gray(_BAR)} ` + (self.options.filter((o) => self.value.includes(o.value)).map((o) => _renderDeleteItem(o, "submitted")).join(chalk2.dim(", ")) || chalk2.dim("none"));
|
|
1979
1454
|
case "cancel": {
|
|
1980
|
-
const cancelled = self.options.filter((o) => self.value.includes(o.value)).map((o) => _renderDeleteItem(o, "cancelled")).join(
|
|
1981
|
-
return `${header}${
|
|
1982
|
-
${
|
|
1455
|
+
const cancelled = self.options.filter((o) => self.value.includes(o.value)).map((o) => _renderDeleteItem(o, "cancelled")).join(chalk2.dim(", "));
|
|
1456
|
+
return `${header}${chalk2.gray(_BAR)} ${cancelled.trim() ? `${cancelled}
|
|
1457
|
+
${chalk2.gray(_BAR)}` : chalk2.dim("none")}`;
|
|
1983
1458
|
}
|
|
1984
1459
|
default:
|
|
1985
|
-
return `${header}${
|
|
1986
|
-
${
|
|
1987
|
-
${
|
|
1460
|
+
return `${header}${chalk2.cyan(_BAR)} ` + self.options.map((o, i) => _renderDeleteItem(o, getState(o, i))).join(`
|
|
1461
|
+
${chalk2.cyan(_BAR)} `) + `
|
|
1462
|
+
${chalk2.cyan(_BAR_END)}
|
|
1988
1463
|
`;
|
|
1989
1464
|
}
|
|
1990
1465
|
}
|
|
@@ -2254,7 +1729,7 @@ var FileGenerator = class {
|
|
|
2254
1729
|
const generatedOutputPaths = new Set(filesToProcess.map((f) => f.outputFile));
|
|
2255
1730
|
const deleteCandidates = [];
|
|
2256
1731
|
for (const relPath of deleteList) {
|
|
2257
|
-
const absPath =
|
|
1732
|
+
const absPath = join3(outputPath, relPath);
|
|
2258
1733
|
if (generatedOutputPaths.has(absPath)) {
|
|
2259
1734
|
logger.warn(
|
|
2260
1735
|
`Delete list entry '${relPath}' conflicts with generated file \u2014 skipping deletion`
|
|
@@ -2316,7 +1791,7 @@ var FileGenerator = class {
|
|
|
2316
1791
|
// @awa-impl: GEN-1_AC-1, GEN-1_AC-2, GEN-1_AC-3
|
|
2317
1792
|
computeOutputPath(templatePath, templateRoot, outputRoot) {
|
|
2318
1793
|
const relativePath = relative(templateRoot, templatePath);
|
|
2319
|
-
return
|
|
1794
|
+
return join3(outputRoot, relativePath);
|
|
2320
1795
|
}
|
|
2321
1796
|
};
|
|
2322
1797
|
var fileGenerator = new FileGenerator();
|
|
@@ -2358,8 +1833,8 @@ var DiffEngine = class {
|
|
|
2358
1833
|
}
|
|
2359
1834
|
const files = [];
|
|
2360
1835
|
for (const relPath of generatedFiles) {
|
|
2361
|
-
const generatedFilePath =
|
|
2362
|
-
const targetFilePath =
|
|
1836
|
+
const generatedFilePath = join4(tempPath, relPath);
|
|
1837
|
+
const targetFilePath = join4(targetPath, relPath);
|
|
2363
1838
|
if (targetFiles.has(relPath)) {
|
|
2364
1839
|
const fileDiff = await this.compareFiles(generatedFilePath, targetFilePath, relPath);
|
|
2365
1840
|
files.push(fileDiff);
|
|
@@ -2423,7 +1898,7 @@ var DiffEngine = class {
|
|
|
2423
1898
|
const systemTemp = tmpdir();
|
|
2424
1899
|
const timestamp = Date.now();
|
|
2425
1900
|
const random = Math.random().toString(36).substring(2, 8);
|
|
2426
|
-
const tempPath =
|
|
1901
|
+
const tempPath = join4(systemTemp, `awa-diff-${timestamp}-${random}`);
|
|
2427
1902
|
await ensureDir(tempPath);
|
|
2428
1903
|
return tempPath;
|
|
2429
1904
|
}
|
|
@@ -2576,18 +2051,18 @@ function writeJsonOutput(data) {
|
|
|
2576
2051
|
// src/core/overlay.ts
|
|
2577
2052
|
import { cp } from "fs/promises";
|
|
2578
2053
|
import { tmpdir as tmpdir2 } from "os";
|
|
2579
|
-
import { join as
|
|
2054
|
+
import { join as join6 } from "path";
|
|
2580
2055
|
|
|
2581
2056
|
// src/core/template-resolver.ts
|
|
2582
2057
|
import { createHash } from "crypto";
|
|
2583
|
-
import { rm
|
|
2584
|
-
import { isAbsolute, join as
|
|
2058
|
+
import { rm } from "fs/promises";
|
|
2059
|
+
import { isAbsolute, join as join5, resolve } from "path";
|
|
2585
2060
|
import degit from "degit";
|
|
2586
2061
|
var TemplateResolver = class {
|
|
2587
2062
|
// @awa-impl: CLI-3_AC-2, TPL-10_AC-1
|
|
2588
2063
|
async resolve(source, refresh) {
|
|
2589
2064
|
if (!source) {
|
|
2590
|
-
const bundledPath =
|
|
2065
|
+
const bundledPath = join5(getTemplateDir(), "awa");
|
|
2591
2066
|
return {
|
|
2592
2067
|
type: "bundled",
|
|
2593
2068
|
localPath: bundledPath,
|
|
@@ -2624,7 +2099,7 @@ var TemplateResolver = class {
|
|
|
2624
2099
|
try {
|
|
2625
2100
|
if (cacheExists && refresh) {
|
|
2626
2101
|
logger.info(`Refreshing template: ${source}`);
|
|
2627
|
-
await
|
|
2102
|
+
await rm(cachePath, { recursive: true, force: true });
|
|
2628
2103
|
} else {
|
|
2629
2104
|
logger.info(`Fetching template: ${source}`);
|
|
2630
2105
|
}
|
|
@@ -2664,7 +2139,7 @@ var TemplateResolver = class {
|
|
|
2664
2139
|
getCachePath(source) {
|
|
2665
2140
|
const hash = createHash("sha256").update(source).digest("hex").substring(0, 16);
|
|
2666
2141
|
const cacheDir = getCacheDir();
|
|
2667
|
-
return
|
|
2142
|
+
return join5(cacheDir, hash);
|
|
2668
2143
|
}
|
|
2669
2144
|
};
|
|
2670
2145
|
var templateResolver = new TemplateResolver();
|
|
@@ -2681,7 +2156,7 @@ async function resolveOverlays(overlays, refresh) {
|
|
|
2681
2156
|
async function buildMergedDir(baseDir, overlayDirs) {
|
|
2682
2157
|
const timestamp = Date.now();
|
|
2683
2158
|
const random = Math.random().toString(36).substring(2, 8);
|
|
2684
|
-
const tempPath =
|
|
2159
|
+
const tempPath = join6(tmpdir2(), `awa-overlay-${timestamp}-${random}`);
|
|
2685
2160
|
await ensureDir(tempPath);
|
|
2686
2161
|
await cp(baseDir, tempPath, { recursive: true });
|
|
2687
2162
|
for (const overlayDir of overlayDirs) {
|
|
@@ -2905,7 +2380,7 @@ async function diffCommand(cliOptions) {
|
|
|
2905
2380
|
import { intro as intro2, outro as outro2 } from "@clack/prompts";
|
|
2906
2381
|
|
|
2907
2382
|
// src/core/features/reporter.ts
|
|
2908
|
-
import
|
|
2383
|
+
import chalk3 from "chalk";
|
|
2909
2384
|
var FeaturesReporter = class {
|
|
2910
2385
|
// @awa-impl: DISC-6_AC-1, DISC-7_AC-1
|
|
2911
2386
|
/** Render the features report to stdout. */
|
|
@@ -2940,25 +2415,25 @@ var FeaturesReporter = class {
|
|
|
2940
2415
|
reportTable(scanResult, presets) {
|
|
2941
2416
|
const { features, filesScanned } = scanResult;
|
|
2942
2417
|
if (features.length === 0) {
|
|
2943
|
-
console.log(
|
|
2944
|
-
console.log(
|
|
2418
|
+
console.log(chalk3.yellow("No feature flags found."));
|
|
2419
|
+
console.log(chalk3.dim(`(${filesScanned} files scanned)`));
|
|
2945
2420
|
return;
|
|
2946
2421
|
}
|
|
2947
|
-
console.log(
|
|
2422
|
+
console.log(chalk3.bold(`Feature flags (${features.length} found):
|
|
2948
2423
|
`));
|
|
2949
2424
|
for (const feature of features) {
|
|
2950
|
-
console.log(` ${
|
|
2425
|
+
console.log(` ${chalk3.cyan(feature.name)}`);
|
|
2951
2426
|
for (const file of feature.files) {
|
|
2952
|
-
console.log(` ${
|
|
2427
|
+
console.log(` ${chalk3.dim(file)}`);
|
|
2953
2428
|
}
|
|
2954
2429
|
}
|
|
2955
2430
|
console.log("");
|
|
2956
|
-
console.log(
|
|
2431
|
+
console.log(chalk3.dim(`${filesScanned} files scanned`));
|
|
2957
2432
|
if (presets && Object.keys(presets).length > 0) {
|
|
2958
2433
|
console.log("");
|
|
2959
|
-
console.log(
|
|
2434
|
+
console.log(chalk3.bold("Presets (from .awa.toml):\n"));
|
|
2960
2435
|
for (const [name, flags] of Object.entries(presets)) {
|
|
2961
|
-
console.log(` ${
|
|
2436
|
+
console.log(` ${chalk3.green(name)}: ${flags.join(", ")}`);
|
|
2962
2437
|
}
|
|
2963
2438
|
}
|
|
2964
2439
|
}
|
|
@@ -2966,13 +2441,13 @@ var FeaturesReporter = class {
|
|
|
2966
2441
|
var featuresReporter = new FeaturesReporter();
|
|
2967
2442
|
|
|
2968
2443
|
// src/core/features/scanner.ts
|
|
2969
|
-
import { readdir
|
|
2970
|
-
import { join as
|
|
2444
|
+
import { readdir, readFile as readFile5 } from "fs/promises";
|
|
2445
|
+
import { join as join7, relative as relative3 } from "path";
|
|
2971
2446
|
var FEATURE_PATTERN = /it\.features\.(?:includes|indexOf)\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
2972
2447
|
async function* walkAllFiles(dir) {
|
|
2973
|
-
const entries = await
|
|
2448
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
2974
2449
|
for (const entry of entries) {
|
|
2975
|
-
const fullPath =
|
|
2450
|
+
const fullPath = join7(dir, entry.name);
|
|
2976
2451
|
if (entry.isDirectory()) {
|
|
2977
2452
|
yield* walkAllFiles(fullPath);
|
|
2978
2453
|
} else if (entry.isFile()) {
|
|
@@ -3000,7 +2475,7 @@ var FeatureScanner = class {
|
|
|
3000
2475
|
for await (const filePath of walkAllFiles(templatePath)) {
|
|
3001
2476
|
filesScanned++;
|
|
3002
2477
|
try {
|
|
3003
|
-
const content = await
|
|
2478
|
+
const content = await readFile5(filePath, "utf-8");
|
|
3004
2479
|
const flags = this.extractFlags(content);
|
|
3005
2480
|
const relPath = relative3(templatePath, filePath);
|
|
3006
2481
|
for (const flag of flags) {
|
|
@@ -3176,21 +2651,21 @@ async function generateCommand(cliOptions) {
|
|
|
3176
2651
|
import { intro as intro4, outro as outro4 } from "@clack/prompts";
|
|
3177
2652
|
|
|
3178
2653
|
// src/core/template-test/fixture-loader.ts
|
|
3179
|
-
import { readdir as
|
|
3180
|
-
import { basename as basename2, extname, join as
|
|
3181
|
-
import { parse
|
|
2654
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
2655
|
+
import { basename as basename2, extname, join as join8 } from "path";
|
|
2656
|
+
import { parse } from "smol-toml";
|
|
3182
2657
|
async function discoverFixtures(templatePath) {
|
|
3183
|
-
const testsDir =
|
|
2658
|
+
const testsDir = join8(templatePath, "_tests");
|
|
3184
2659
|
let entries;
|
|
3185
2660
|
try {
|
|
3186
|
-
const dirEntries = await
|
|
2661
|
+
const dirEntries = await readdir2(testsDir, { withFileTypes: true });
|
|
3187
2662
|
entries = dirEntries.filter((e) => e.isFile() && extname(e.name) === ".toml").map((e) => e.name).sort();
|
|
3188
2663
|
} catch {
|
|
3189
2664
|
return [];
|
|
3190
2665
|
}
|
|
3191
2666
|
const fixtures = [];
|
|
3192
2667
|
for (const filename of entries) {
|
|
3193
|
-
const filePath =
|
|
2668
|
+
const filePath = join8(testsDir, filename);
|
|
3194
2669
|
const fixture = await parseFixture(filePath);
|
|
3195
2670
|
fixtures.push(fixture);
|
|
3196
2671
|
}
|
|
@@ -3198,7 +2673,7 @@ async function discoverFixtures(templatePath) {
|
|
|
3198
2673
|
}
|
|
3199
2674
|
async function parseFixture(filePath) {
|
|
3200
2675
|
const content = await readTextFile(filePath);
|
|
3201
|
-
const parsed =
|
|
2676
|
+
const parsed = parse(content);
|
|
3202
2677
|
const name = basename2(filePath, extname(filePath));
|
|
3203
2678
|
const features = toStringArray2(parsed.features) ?? [];
|
|
3204
2679
|
const preset = toStringArray2(parsed.preset) ?? [];
|
|
@@ -3221,61 +2696,61 @@ function toStringArray2(value) {
|
|
|
3221
2696
|
}
|
|
3222
2697
|
|
|
3223
2698
|
// src/core/template-test/reporter.ts
|
|
3224
|
-
import
|
|
2699
|
+
import chalk4 from "chalk";
|
|
3225
2700
|
function report2(result) {
|
|
3226
2701
|
console.log("");
|
|
3227
2702
|
for (const fixture of result.results) {
|
|
3228
2703
|
reportFixture(fixture);
|
|
3229
2704
|
}
|
|
3230
2705
|
console.log("");
|
|
3231
|
-
console.log(
|
|
2706
|
+
console.log(chalk4.bold("Test Summary:"));
|
|
3232
2707
|
console.log(` Total: ${result.total}`);
|
|
3233
|
-
console.log(
|
|
2708
|
+
console.log(chalk4.green(` Passed: ${result.passed}`));
|
|
3234
2709
|
if (result.failed > 0) {
|
|
3235
|
-
console.log(
|
|
2710
|
+
console.log(chalk4.red(` Failed: ${result.failed}`));
|
|
3236
2711
|
}
|
|
3237
2712
|
console.log("");
|
|
3238
2713
|
}
|
|
3239
2714
|
function reportFixture(fixture) {
|
|
3240
|
-
const icon = fixture.passed ?
|
|
2715
|
+
const icon = fixture.passed ? chalk4.green("\u2714") : chalk4.red("\u2716");
|
|
3241
2716
|
console.log(`${icon} ${fixture.name}`);
|
|
3242
2717
|
if (fixture.error) {
|
|
3243
|
-
console.log(
|
|
2718
|
+
console.log(chalk4.red(` Error: ${fixture.error}`));
|
|
3244
2719
|
return;
|
|
3245
2720
|
}
|
|
3246
2721
|
const missingFiles = fixture.fileResults.filter((r) => !r.found);
|
|
3247
2722
|
for (const missing of missingFiles) {
|
|
3248
|
-
console.log(
|
|
2723
|
+
console.log(chalk4.red(` Missing file: ${missing.path}`));
|
|
3249
2724
|
}
|
|
3250
2725
|
const snapshotFailures = fixture.snapshotResults.filter((r) => r.status !== "match");
|
|
3251
2726
|
for (const failure of snapshotFailures) {
|
|
3252
2727
|
switch (failure.status) {
|
|
3253
2728
|
case "mismatch":
|
|
3254
|
-
console.log(
|
|
2729
|
+
console.log(chalk4.yellow(` Snapshot mismatch: ${failure.path}`));
|
|
3255
2730
|
break;
|
|
3256
2731
|
case "missing-snapshot":
|
|
3257
|
-
console.log(
|
|
2732
|
+
console.log(chalk4.yellow(` Missing snapshot: ${failure.path}`));
|
|
3258
2733
|
break;
|
|
3259
2734
|
case "extra-snapshot":
|
|
3260
|
-
console.log(
|
|
2735
|
+
console.log(chalk4.yellow(` Extra snapshot (not in output): ${failure.path}`));
|
|
3261
2736
|
break;
|
|
3262
2737
|
}
|
|
3263
2738
|
}
|
|
3264
2739
|
}
|
|
3265
2740
|
|
|
3266
2741
|
// src/core/template-test/runner.ts
|
|
3267
|
-
import { mkdir as
|
|
2742
|
+
import { mkdir as mkdir2, rm as rm3 } from "fs/promises";
|
|
3268
2743
|
import { tmpdir as tmpdir3 } from "os";
|
|
3269
|
-
import { join as
|
|
2744
|
+
import { join as join10 } from "path";
|
|
3270
2745
|
|
|
3271
2746
|
// src/core/template-test/snapshot.ts
|
|
3272
|
-
import { mkdir
|
|
3273
|
-
import { join as
|
|
2747
|
+
import { mkdir, readdir as readdir3, rm as rm2 } from "fs/promises";
|
|
2748
|
+
import { join as join9, relative as relative4 } from "path";
|
|
3274
2749
|
async function walkRelative(dir, base) {
|
|
3275
2750
|
const results = [];
|
|
3276
|
-
const entries = await
|
|
2751
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
3277
2752
|
for (const entry of entries) {
|
|
3278
|
-
const fullPath =
|
|
2753
|
+
const fullPath = join9(dir, entry.name);
|
|
3279
2754
|
if (entry.isDirectory()) {
|
|
3280
2755
|
const sub = await walkRelative(fullPath, base);
|
|
3281
2756
|
results.push(...sub);
|
|
@@ -3292,8 +2767,8 @@ async function compareSnapshots(renderedDir, snapshotDir) {
|
|
|
3292
2767
|
const snapshotSet = new Set(snapshotFiles);
|
|
3293
2768
|
const renderedSet = new Set(renderedFiles);
|
|
3294
2769
|
for (const file of renderedFiles) {
|
|
3295
|
-
const renderedPath =
|
|
3296
|
-
const snapshotPath =
|
|
2770
|
+
const renderedPath = join9(renderedDir, file);
|
|
2771
|
+
const snapshotPath = join9(snapshotDir, file);
|
|
3297
2772
|
if (!snapshotSet.has(file)) {
|
|
3298
2773
|
results.push({ path: file, status: "missing-snapshot" });
|
|
3299
2774
|
continue;
|
|
@@ -3314,13 +2789,13 @@ async function compareSnapshots(renderedDir, snapshotDir) {
|
|
|
3314
2789
|
}
|
|
3315
2790
|
async function updateSnapshots(renderedDir, snapshotDir) {
|
|
3316
2791
|
if (await pathExists(snapshotDir)) {
|
|
3317
|
-
await
|
|
2792
|
+
await rm2(snapshotDir, { recursive: true, force: true });
|
|
3318
2793
|
}
|
|
3319
|
-
await
|
|
2794
|
+
await mkdir(snapshotDir, { recursive: true });
|
|
3320
2795
|
const files = await walkRelative(renderedDir, renderedDir);
|
|
3321
2796
|
for (const file of files) {
|
|
3322
|
-
const srcPath =
|
|
3323
|
-
const destPath =
|
|
2797
|
+
const srcPath = join9(renderedDir, file);
|
|
2798
|
+
const destPath = join9(snapshotDir, file);
|
|
3324
2799
|
const content = await readTextFile(srcPath);
|
|
3325
2800
|
await writeTextFile(destPath, content);
|
|
3326
2801
|
}
|
|
@@ -3328,9 +2803,9 @@ async function updateSnapshots(renderedDir, snapshotDir) {
|
|
|
3328
2803
|
|
|
3329
2804
|
// src/core/template-test/runner.ts
|
|
3330
2805
|
async function runFixture(fixture, templatePath, options, presetDefinitions = {}) {
|
|
3331
|
-
const tempDir =
|
|
2806
|
+
const tempDir = join10(tmpdir3(), `awa-test-${fixture.name}-${Date.now()}`);
|
|
3332
2807
|
try {
|
|
3333
|
-
await
|
|
2808
|
+
await mkdir2(tempDir, { recursive: true });
|
|
3334
2809
|
const features = featureResolver.resolve({
|
|
3335
2810
|
baseFeatures: [...fixture.features],
|
|
3336
2811
|
presetNames: [...fixture.preset],
|
|
@@ -3347,12 +2822,12 @@ async function runFixture(fixture, templatePath, options, presetDefinitions = {}
|
|
|
3347
2822
|
});
|
|
3348
2823
|
const fileResults = [];
|
|
3349
2824
|
for (const expectedFile of fixture.expectedFiles) {
|
|
3350
|
-
const fullPath =
|
|
2825
|
+
const fullPath = join10(tempDir, expectedFile);
|
|
3351
2826
|
const found = await pathExists(fullPath);
|
|
3352
2827
|
fileResults.push({ path: expectedFile, found });
|
|
3353
2828
|
}
|
|
3354
2829
|
const missingFiles = fileResults.filter((r) => !r.found);
|
|
3355
|
-
const snapshotDir =
|
|
2830
|
+
const snapshotDir = join10(templatePath, "_tests", fixture.name);
|
|
3356
2831
|
let snapshotResults = [];
|
|
3357
2832
|
if (options.updateSnapshots) {
|
|
3358
2833
|
await updateSnapshots(tempDir, snapshotDir);
|
|
@@ -3376,7 +2851,7 @@ async function runFixture(fixture, templatePath, options, presetDefinitions = {}
|
|
|
3376
2851
|
error: error instanceof Error ? error.message : String(error)
|
|
3377
2852
|
};
|
|
3378
2853
|
} finally {
|
|
3379
|
-
await
|
|
2854
|
+
await rm3(tempDir, { recursive: true, force: true }).catch(() => {
|
|
3380
2855
|
});
|
|
3381
2856
|
}
|
|
3382
2857
|
}
|
|
@@ -3434,6 +2909,91 @@ async function testCommand(options) {
|
|
|
3434
2909
|
}
|
|
3435
2910
|
}
|
|
3436
2911
|
|
|
2912
|
+
// src/utils/update-check.ts
|
|
2913
|
+
import chalk5 from "chalk";
|
|
2914
|
+
function compareSemver(a, b) {
|
|
2915
|
+
const partsA = a.split(".").map((s) => Number.parseInt(s, 10));
|
|
2916
|
+
const partsB = b.split(".").map((s) => Number.parseInt(s, 10));
|
|
2917
|
+
for (let i = 0; i < 3; i++) {
|
|
2918
|
+
const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0);
|
|
2919
|
+
if (diff !== 0) return diff;
|
|
2920
|
+
}
|
|
2921
|
+
return 0;
|
|
2922
|
+
}
|
|
2923
|
+
function isMajorVersionBump(current, latest) {
|
|
2924
|
+
const currentMajor = Number.parseInt(current.split(".")[0] ?? "0", 10);
|
|
2925
|
+
const latestMajor = Number.parseInt(latest.split(".")[0] ?? "0", 10);
|
|
2926
|
+
return latestMajor > currentMajor;
|
|
2927
|
+
}
|
|
2928
|
+
async function checkForUpdate() {
|
|
2929
|
+
try {
|
|
2930
|
+
const response = await fetch("https://registry.npmjs.org/@ncoderz/awa/latest", {
|
|
2931
|
+
signal: AbortSignal.timeout(5e3)
|
|
2932
|
+
});
|
|
2933
|
+
if (!response.ok) return null;
|
|
2934
|
+
const data = await response.json();
|
|
2935
|
+
const latest = data.version;
|
|
2936
|
+
if (!latest || typeof latest !== "string") return null;
|
|
2937
|
+
const current = PACKAGE_INFO.version;
|
|
2938
|
+
const isOutdated = compareSemver(current, latest) < 0;
|
|
2939
|
+
return {
|
|
2940
|
+
current,
|
|
2941
|
+
latest,
|
|
2942
|
+
isOutdated,
|
|
2943
|
+
isMajorBump: isOutdated && isMajorVersionBump(current, latest)
|
|
2944
|
+
};
|
|
2945
|
+
} catch {
|
|
2946
|
+
return null;
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
function printUpdateWarning(log, result) {
|
|
2950
|
+
if (!result.isOutdated) return;
|
|
2951
|
+
console.log("");
|
|
2952
|
+
if (result.isMajorBump) {
|
|
2953
|
+
log.warn(
|
|
2954
|
+
chalk5.yellow(
|
|
2955
|
+
`New major version available: ${result.current} \u2192 ${result.latest} (breaking changes)`
|
|
2956
|
+
)
|
|
2957
|
+
);
|
|
2958
|
+
log.warn(chalk5.dim(" See https://github.com/ncoderz/awa/releases for details"));
|
|
2959
|
+
} else {
|
|
2960
|
+
log.warn(chalk5.yellow(`Update available: ${result.current} \u2192 ${result.latest}`));
|
|
2961
|
+
}
|
|
2962
|
+
log.warn(chalk5.dim(" Run `npm install -g @ncoderz/awa` to update"));
|
|
2963
|
+
console.log("");
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
// src/utils/update-check-cache.ts
|
|
2967
|
+
import { mkdir as mkdir3, readFile as readFile6, writeFile } from "fs/promises";
|
|
2968
|
+
import { homedir } from "os";
|
|
2969
|
+
import { dirname, join as join11 } from "path";
|
|
2970
|
+
var CACHE_DIR = join11(homedir(), ".cache", "awa");
|
|
2971
|
+
var CACHE_FILE = join11(CACHE_DIR, "update-check.json");
|
|
2972
|
+
var DEFAULT_INTERVAL_MS = 864e5;
|
|
2973
|
+
async function shouldCheck(intervalMs = DEFAULT_INTERVAL_MS) {
|
|
2974
|
+
try {
|
|
2975
|
+
const raw = await readFile6(CACHE_FILE, "utf-8");
|
|
2976
|
+
const data = JSON.parse(raw);
|
|
2977
|
+
if (typeof data.timestamp !== "number" || typeof data.latestVersion !== "string") {
|
|
2978
|
+
return true;
|
|
2979
|
+
}
|
|
2980
|
+
return Date.now() - data.timestamp >= intervalMs;
|
|
2981
|
+
} catch {
|
|
2982
|
+
return true;
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
async function writeCache(latestVersion) {
|
|
2986
|
+
try {
|
|
2987
|
+
await mkdir3(dirname(CACHE_FILE), { recursive: true });
|
|
2988
|
+
const data = {
|
|
2989
|
+
timestamp: Date.now(),
|
|
2990
|
+
latestVersion
|
|
2991
|
+
};
|
|
2992
|
+
await writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
2993
|
+
} catch {
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
|
|
3437
2997
|
// src/cli/index.ts
|
|
3438
2998
|
var version = PACKAGE_INFO.version;
|
|
3439
2999
|
var program = new Command();
|
|
@@ -3491,7 +3051,7 @@ program.command("diff").description("Compare template output with existing targe
|
|
|
3491
3051
|
});
|
|
3492
3052
|
program.command("check").description(
|
|
3493
3053
|
"Validate spec files against schemas and check traceability between code markers and specs"
|
|
3494
|
-
).option("-c, --config <path>", "Path to configuration file").option("--ignore <pattern...>", "Glob patterns
|
|
3054
|
+
).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("--format <format>", "Output format (text or json)", "text").option(
|
|
3495
3055
|
"--allow-warnings",
|
|
3496
3056
|
"Allow warnings without failing (default: warnings are errors)",
|
|
3497
3057
|
false
|
|
@@ -3502,7 +3062,8 @@ program.command("check").description(
|
|
|
3502
3062
|
).action(async (options) => {
|
|
3503
3063
|
const cliOptions = {
|
|
3504
3064
|
config: options.config,
|
|
3505
|
-
|
|
3065
|
+
specIgnore: options.specIgnore,
|
|
3066
|
+
codeIgnore: options.codeIgnore,
|
|
3506
3067
|
format: options.format,
|
|
3507
3068
|
allowWarnings: options.allowWarnings,
|
|
3508
3069
|
specOnly: options.specOnly
|
|
@@ -3528,5 +3089,41 @@ program.command("test").description("Run template test fixtures to verify expect
|
|
|
3528
3089
|
const exitCode = await testCommand(testOptions);
|
|
3529
3090
|
process.exit(exitCode);
|
|
3530
3091
|
});
|
|
3531
|
-
|
|
3092
|
+
var updateCheckPromise = null;
|
|
3093
|
+
var isJsonOrSummary = process.argv.includes("--json") || process.argv.includes("--summary");
|
|
3094
|
+
var isTTY = process.stdout.isTTY === true;
|
|
3095
|
+
var isDisabledByEnv = !!process.env.NO_UPDATE_NOTIFIER;
|
|
3096
|
+
if (!isJsonOrSummary && isTTY && !isDisabledByEnv) {
|
|
3097
|
+
updateCheckPromise = (async () => {
|
|
3098
|
+
try {
|
|
3099
|
+
const { configLoader: configLoader2 } = await import("./config-2TOQATI3.js");
|
|
3100
|
+
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;
|
|
3101
|
+
const fileConfig = await configLoader2.load(configPath ?? null);
|
|
3102
|
+
const updateCheckConfig = fileConfig?.["update-check"];
|
|
3103
|
+
if (updateCheckConfig?.enabled === false) return null;
|
|
3104
|
+
const intervalSeconds = updateCheckConfig?.interval ?? 86400;
|
|
3105
|
+
const intervalMs = intervalSeconds * 1e3;
|
|
3106
|
+
const needsCheck = await shouldCheck(intervalMs);
|
|
3107
|
+
if (!needsCheck) return null;
|
|
3108
|
+
const result = await checkForUpdate();
|
|
3109
|
+
if (result) {
|
|
3110
|
+
await writeCache(result.latest);
|
|
3111
|
+
}
|
|
3112
|
+
return result;
|
|
3113
|
+
} catch {
|
|
3114
|
+
return null;
|
|
3115
|
+
}
|
|
3116
|
+
})();
|
|
3117
|
+
}
|
|
3118
|
+
program.hook("postAction", async () => {
|
|
3119
|
+
if (!updateCheckPromise) return;
|
|
3120
|
+
try {
|
|
3121
|
+
const result = await updateCheckPromise;
|
|
3122
|
+
if (result?.isOutdated) {
|
|
3123
|
+
printUpdateWarning(logger, result);
|
|
3124
|
+
}
|
|
3125
|
+
} catch {
|
|
3126
|
+
}
|
|
3127
|
+
});
|
|
3128
|
+
program.parseAsync();
|
|
3532
3129
|
//# sourceMappingURL=index.js.map
|