qfai 0.9.0 → 1.0.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/README.md +23 -10
- package/assets/init/.qfai/README.md +4 -3
- package/assets/init/.qfai/prompts/README.md +3 -3
- package/assets/init/.qfai/prompts/analyze/README.md +8 -25
- package/assets/init/.qfai/prompts/analyze/scenario_test_consistency.md +5 -32
- package/assets/init/.qfai/prompts/analyze/scenario_to_test.md +56 -0
- package/assets/init/.qfai/prompts/analyze/spec_contract_consistency.md +5 -33
- package/assets/init/.qfai/prompts/analyze/spec_scenario_consistency.md +5 -32
- package/assets/init/.qfai/prompts/analyze/spec_to_contract.md +54 -0
- package/assets/init/.qfai/prompts/analyze/spec_to_scenario.md +56 -0
- package/assets/init/.qfai/samples/analyze/analysis.md +3 -3
- package/assets/init/.qfai/samples/analyze/input_bundle.md +54 -0
- package/dist/cli/index.cjs +415 -242
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.mjs +399 -226
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
package/dist/cli/index.cjs
CHANGED
|
@@ -23,17 +23,190 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
mod
|
|
24
24
|
));
|
|
25
25
|
|
|
26
|
+
// src/cli/commands/analyze.ts
|
|
27
|
+
var import_promises2 = require("fs/promises");
|
|
28
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
29
|
+
|
|
30
|
+
// src/core/fs.ts
|
|
31
|
+
var import_promises = require("fs/promises");
|
|
32
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
33
|
+
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
34
|
+
var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
35
|
+
"node_modules",
|
|
36
|
+
".git",
|
|
37
|
+
"dist",
|
|
38
|
+
".pnpm",
|
|
39
|
+
"tmp",
|
|
40
|
+
".mcp-tools"
|
|
41
|
+
]);
|
|
42
|
+
async function collectFiles(root, options = {}) {
|
|
43
|
+
const entries = [];
|
|
44
|
+
if (!await exists(root)) {
|
|
45
|
+
return entries;
|
|
46
|
+
}
|
|
47
|
+
const ignoreDirs = /* @__PURE__ */ new Set([
|
|
48
|
+
...DEFAULT_IGNORE_DIRS,
|
|
49
|
+
...options.ignoreDirs ?? []
|
|
50
|
+
]);
|
|
51
|
+
const extensions = options.extensions?.map((ext) => ext.toLowerCase()) ?? [];
|
|
52
|
+
await walk(root, root, ignoreDirs, extensions, entries);
|
|
53
|
+
return entries;
|
|
54
|
+
}
|
|
55
|
+
async function collectFilesByGlobs(root, options) {
|
|
56
|
+
if (options.globs.length === 0) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return (0, import_fast_glob.default)(options.globs, {
|
|
60
|
+
cwd: root,
|
|
61
|
+
ignore: options.ignore ?? [],
|
|
62
|
+
onlyFiles: true,
|
|
63
|
+
absolute: true,
|
|
64
|
+
unique: true
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async function walk(base, current, ignoreDirs, extensions, out) {
|
|
68
|
+
const items = await (0, import_promises.readdir)(current, { withFileTypes: true });
|
|
69
|
+
for (const item of items) {
|
|
70
|
+
const fullPath = import_node_path.default.join(current, item.name);
|
|
71
|
+
if (item.isDirectory()) {
|
|
72
|
+
if (ignoreDirs.has(item.name)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
await walk(base, fullPath, ignoreDirs, extensions, out);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (item.isFile()) {
|
|
79
|
+
if (extensions.length > 0) {
|
|
80
|
+
const ext = import_node_path.default.extname(item.name).toLowerCase();
|
|
81
|
+
if (!extensions.includes(ext)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
out.push(fullPath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function exists(target) {
|
|
90
|
+
try {
|
|
91
|
+
await (0, import_promises.access)(target);
|
|
92
|
+
return true;
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/cli/commands/analyze.ts
|
|
99
|
+
async function runAnalyze(options) {
|
|
100
|
+
const root = import_node_path2.default.resolve(options.root);
|
|
101
|
+
const localDir = import_node_path2.default.join(root, ".qfai", "prompts.local", "analyze");
|
|
102
|
+
const standardDir = import_node_path2.default.join(root, ".qfai", "prompts", "analyze");
|
|
103
|
+
const available = await listPromptNames([localDir, standardDir]);
|
|
104
|
+
const promptName = normalizePromptName(options.prompt);
|
|
105
|
+
if (!promptName || options.list) {
|
|
106
|
+
emitList(available);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
const resolved = await resolvePromptPath(promptName, [localDir, standardDir]);
|
|
110
|
+
if (!resolved) {
|
|
111
|
+
emitPromptNotFound(promptName, available);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
const content = await (0, import_promises2.readFile)(resolved, "utf-8");
|
|
115
|
+
process.stdout.write(content);
|
|
116
|
+
if (!content.endsWith("\n")) {
|
|
117
|
+
process.stdout.write("\n");
|
|
118
|
+
}
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
function normalizePromptName(value) {
|
|
122
|
+
const trimmed = (value ?? "").trim();
|
|
123
|
+
if (!trimmed) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return trimmed.endsWith(".md") ? trimmed.slice(0, -3) : trimmed;
|
|
127
|
+
}
|
|
128
|
+
async function listPromptNames(dirs) {
|
|
129
|
+
const byName = /* @__PURE__ */ new Map();
|
|
130
|
+
for (const dir of dirs) {
|
|
131
|
+
const files = await collectFiles(dir, { extensions: [".md"] });
|
|
132
|
+
for (const abs of files) {
|
|
133
|
+
const base = import_node_path2.default.basename(abs);
|
|
134
|
+
if (base.toLowerCase() === "readme.md") {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const name = base.slice(0, -3);
|
|
138
|
+
if (byName.has(name)) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (await isDeprecatedPrompt(abs)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
byName.set(name, abs);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return [...byName.keys()].sort((a, b) => a.localeCompare(b));
|
|
148
|
+
}
|
|
149
|
+
function emitList(names) {
|
|
150
|
+
process.stdout.write("# qfai analyze: prompts\n\n");
|
|
151
|
+
if (names.length === 0) {
|
|
152
|
+
process.stdout.write(
|
|
153
|
+
"\u5229\u7528\u53EF\u80FD\u306A\u30D7\u30ED\u30F3\u30D7\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u307E\u305A `qfai init` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n"
|
|
154
|
+
);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
process.stdout.write("\u5229\u7528\u53EF\u80FD\u306A\u30D7\u30ED\u30F3\u30D7\u30C8\u4E00\u89A7:\n\n");
|
|
158
|
+
for (const name of names) {
|
|
159
|
+
process.stdout.write(`- ${name}
|
|
160
|
+
`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function resolvePromptPath(promptName, dirs) {
|
|
164
|
+
const filename = `${promptName}.md`;
|
|
165
|
+
for (const dir of dirs) {
|
|
166
|
+
const full = import_node_path2.default.join(dir, filename);
|
|
167
|
+
try {
|
|
168
|
+
await (0, import_promises2.readFile)(full, "utf-8");
|
|
169
|
+
return full;
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
async function isDeprecatedPrompt(filePath) {
|
|
176
|
+
try {
|
|
177
|
+
const content = await (0, import_promises2.readFile)(filePath, "utf-8");
|
|
178
|
+
const firstLine = firstLineOf(content);
|
|
179
|
+
return firstLine.trim() === "# Deprecated";
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function firstLineOf(content) {
|
|
185
|
+
return content.match(/^[^\r\n]*/)?.[0] ?? "";
|
|
186
|
+
}
|
|
187
|
+
function emitPromptNotFound(promptName, candidates) {
|
|
188
|
+
process.stderr.write(`qfai analyze: prompt not found: ${promptName}
|
|
189
|
+
`);
|
|
190
|
+
if (candidates.length > 0) {
|
|
191
|
+
process.stderr.write("candidates:\n");
|
|
192
|
+
for (const c of candidates) {
|
|
193
|
+
process.stderr.write(`- ${c}
|
|
194
|
+
`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
26
199
|
// src/cli/commands/doctor.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
200
|
+
var import_promises10 = require("fs/promises");
|
|
201
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
29
202
|
|
|
30
203
|
// src/core/doctor.ts
|
|
31
|
-
var
|
|
32
|
-
var
|
|
204
|
+
var import_promises9 = require("fs/promises");
|
|
205
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
33
206
|
|
|
34
207
|
// src/core/config.ts
|
|
35
|
-
var
|
|
36
|
-
var
|
|
208
|
+
var import_promises3 = require("fs/promises");
|
|
209
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
37
210
|
var import_yaml = require("yaml");
|
|
38
211
|
var defaultConfig = {
|
|
39
212
|
paths: {
|
|
@@ -73,17 +246,17 @@ var defaultConfig = {
|
|
|
73
246
|
}
|
|
74
247
|
};
|
|
75
248
|
function getConfigPath(root) {
|
|
76
|
-
return
|
|
249
|
+
return import_node_path3.default.join(root, "qfai.config.yaml");
|
|
77
250
|
}
|
|
78
251
|
async function findConfigRoot(startDir) {
|
|
79
|
-
const resolvedStart =
|
|
252
|
+
const resolvedStart = import_node_path3.default.resolve(startDir);
|
|
80
253
|
let current = resolvedStart;
|
|
81
254
|
while (true) {
|
|
82
255
|
const configPath = getConfigPath(current);
|
|
83
|
-
if (await
|
|
256
|
+
if (await exists2(configPath)) {
|
|
84
257
|
return { root: current, configPath, found: true };
|
|
85
258
|
}
|
|
86
|
-
const parent =
|
|
259
|
+
const parent = import_node_path3.default.dirname(current);
|
|
87
260
|
if (parent === current) {
|
|
88
261
|
break;
|
|
89
262
|
}
|
|
@@ -100,7 +273,7 @@ async function loadConfig(root) {
|
|
|
100
273
|
const issues = [];
|
|
101
274
|
let parsed;
|
|
102
275
|
try {
|
|
103
|
-
const raw = await (0,
|
|
276
|
+
const raw = await (0, import_promises3.readFile)(configPath, "utf-8");
|
|
104
277
|
parsed = (0, import_yaml.parse)(raw);
|
|
105
278
|
} catch (error2) {
|
|
106
279
|
if (isMissingFile(error2)) {
|
|
@@ -113,7 +286,7 @@ async function loadConfig(root) {
|
|
|
113
286
|
return { config: normalized, issues, configPath };
|
|
114
287
|
}
|
|
115
288
|
function resolvePath(root, config, key) {
|
|
116
|
-
return
|
|
289
|
+
return import_node_path3.default.resolve(root, config.paths[key]);
|
|
117
290
|
}
|
|
118
291
|
function normalizeConfig(raw, configPath, issues) {
|
|
119
292
|
if (!isRecord(raw)) {
|
|
@@ -413,9 +586,9 @@ function isMissingFile(error2) {
|
|
|
413
586
|
}
|
|
414
587
|
return false;
|
|
415
588
|
}
|
|
416
|
-
async function
|
|
589
|
+
async function exists2(target) {
|
|
417
590
|
try {
|
|
418
|
-
await (0,
|
|
591
|
+
await (0, import_promises3.access)(target);
|
|
419
592
|
return true;
|
|
420
593
|
} catch {
|
|
421
594
|
return false;
|
|
@@ -432,94 +605,26 @@ function isRecord(value) {
|
|
|
432
605
|
}
|
|
433
606
|
|
|
434
607
|
// src/core/discovery.ts
|
|
435
|
-
var
|
|
436
|
-
|
|
437
|
-
// src/core/fs.ts
|
|
438
|
-
var import_promises2 = require("fs/promises");
|
|
439
|
-
var import_node_path2 = __toESM(require("path"), 1);
|
|
440
|
-
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
441
|
-
var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
442
|
-
"node_modules",
|
|
443
|
-
".git",
|
|
444
|
-
"dist",
|
|
445
|
-
".pnpm",
|
|
446
|
-
"tmp",
|
|
447
|
-
".mcp-tools"
|
|
448
|
-
]);
|
|
449
|
-
async function collectFiles(root, options = {}) {
|
|
450
|
-
const entries = [];
|
|
451
|
-
if (!await exists2(root)) {
|
|
452
|
-
return entries;
|
|
453
|
-
}
|
|
454
|
-
const ignoreDirs = /* @__PURE__ */ new Set([
|
|
455
|
-
...DEFAULT_IGNORE_DIRS,
|
|
456
|
-
...options.ignoreDirs ?? []
|
|
457
|
-
]);
|
|
458
|
-
const extensions = options.extensions?.map((ext) => ext.toLowerCase()) ?? [];
|
|
459
|
-
await walk(root, root, ignoreDirs, extensions, entries);
|
|
460
|
-
return entries;
|
|
461
|
-
}
|
|
462
|
-
async function collectFilesByGlobs(root, options) {
|
|
463
|
-
if (options.globs.length === 0) {
|
|
464
|
-
return [];
|
|
465
|
-
}
|
|
466
|
-
return (0, import_fast_glob.default)(options.globs, {
|
|
467
|
-
cwd: root,
|
|
468
|
-
ignore: options.ignore ?? [],
|
|
469
|
-
onlyFiles: true,
|
|
470
|
-
absolute: true,
|
|
471
|
-
unique: true
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
async function walk(base, current, ignoreDirs, extensions, out) {
|
|
475
|
-
const items = await (0, import_promises2.readdir)(current, { withFileTypes: true });
|
|
476
|
-
for (const item of items) {
|
|
477
|
-
const fullPath = import_node_path2.default.join(current, item.name);
|
|
478
|
-
if (item.isDirectory()) {
|
|
479
|
-
if (ignoreDirs.has(item.name)) {
|
|
480
|
-
continue;
|
|
481
|
-
}
|
|
482
|
-
await walk(base, fullPath, ignoreDirs, extensions, out);
|
|
483
|
-
continue;
|
|
484
|
-
}
|
|
485
|
-
if (item.isFile()) {
|
|
486
|
-
if (extensions.length > 0) {
|
|
487
|
-
const ext = import_node_path2.default.extname(item.name).toLowerCase();
|
|
488
|
-
if (!extensions.includes(ext)) {
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
out.push(fullPath);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
async function exists2(target) {
|
|
497
|
-
try {
|
|
498
|
-
await (0, import_promises2.access)(target);
|
|
499
|
-
return true;
|
|
500
|
-
} catch {
|
|
501
|
-
return false;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
608
|
+
var import_promises5 = require("fs/promises");
|
|
504
609
|
|
|
505
610
|
// src/core/specLayout.ts
|
|
506
|
-
var
|
|
507
|
-
var
|
|
611
|
+
var import_promises4 = require("fs/promises");
|
|
612
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
508
613
|
var SPEC_DIR_RE = /^spec-\d{4}$/;
|
|
509
614
|
async function collectSpecEntries(specsRoot) {
|
|
510
615
|
const dirs = await listSpecDirs(specsRoot);
|
|
511
616
|
const entries = dirs.map((dir) => ({
|
|
512
617
|
dir,
|
|
513
|
-
specPath:
|
|
514
|
-
deltaPath:
|
|
515
|
-
scenarioPath:
|
|
618
|
+
specPath: import_node_path4.default.join(dir, "spec.md"),
|
|
619
|
+
deltaPath: import_node_path4.default.join(dir, "delta.md"),
|
|
620
|
+
scenarioPath: import_node_path4.default.join(dir, "scenario.md")
|
|
516
621
|
}));
|
|
517
622
|
return entries.sort((a, b) => a.dir.localeCompare(b.dir));
|
|
518
623
|
}
|
|
519
624
|
async function listSpecDirs(specsRoot) {
|
|
520
625
|
try {
|
|
521
|
-
const items = await (0,
|
|
522
|
-
return items.filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => SPEC_DIR_RE.test(name.toLowerCase())).map((name) =>
|
|
626
|
+
const items = await (0, import_promises4.readdir)(specsRoot, { withFileTypes: true });
|
|
627
|
+
return items.filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => SPEC_DIR_RE.test(name.toLowerCase())).map((name) => import_node_path4.default.join(specsRoot, name));
|
|
523
628
|
} catch (error2) {
|
|
524
629
|
if (isMissingFileError(error2)) {
|
|
525
630
|
return [];
|
|
@@ -575,7 +680,7 @@ async function filterExisting(files) {
|
|
|
575
680
|
}
|
|
576
681
|
async function exists3(target) {
|
|
577
682
|
try {
|
|
578
|
-
await (0,
|
|
683
|
+
await (0, import_promises5.access)(target);
|
|
579
684
|
return true;
|
|
580
685
|
} catch {
|
|
581
686
|
return false;
|
|
@@ -583,15 +688,15 @@ async function exists3(target) {
|
|
|
583
688
|
}
|
|
584
689
|
|
|
585
690
|
// src/core/paths.ts
|
|
586
|
-
var
|
|
691
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
587
692
|
function toRelativePath(root, target) {
|
|
588
693
|
if (!target) {
|
|
589
694
|
return target;
|
|
590
695
|
}
|
|
591
|
-
if (!
|
|
696
|
+
if (!import_node_path5.default.isAbsolute(target)) {
|
|
592
697
|
return toPosixPath(target);
|
|
593
698
|
}
|
|
594
|
-
const relative =
|
|
699
|
+
const relative = import_node_path5.default.relative(root, target);
|
|
595
700
|
if (!relative) {
|
|
596
701
|
return ".";
|
|
597
702
|
}
|
|
@@ -602,8 +707,8 @@ function toPosixPath(value) {
|
|
|
602
707
|
}
|
|
603
708
|
|
|
604
709
|
// src/core/traceability.ts
|
|
605
|
-
var
|
|
606
|
-
var
|
|
710
|
+
var import_promises6 = require("fs/promises");
|
|
711
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
607
712
|
|
|
608
713
|
// src/core/gherkin/parse.ts
|
|
609
714
|
var import_gherkin = require("@cucumber/gherkin");
|
|
@@ -755,7 +860,7 @@ function extractAnnotatedScIds(text) {
|
|
|
755
860
|
async function collectScIdsFromScenarioFiles(scenarioFiles) {
|
|
756
861
|
const scIds = /* @__PURE__ */ new Set();
|
|
757
862
|
for (const file of scenarioFiles) {
|
|
758
|
-
const text = await (0,
|
|
863
|
+
const text = await (0, import_promises6.readFile)(file, "utf-8");
|
|
759
864
|
const { document, errors } = parseScenarioDocument(text, file);
|
|
760
865
|
if (!document || errors.length > 0) {
|
|
761
866
|
continue;
|
|
@@ -773,7 +878,7 @@ async function collectScIdsFromScenarioFiles(scenarioFiles) {
|
|
|
773
878
|
async function collectScIdSourcesFromScenarioFiles(scenarioFiles) {
|
|
774
879
|
const sources = /* @__PURE__ */ new Map();
|
|
775
880
|
for (const file of scenarioFiles) {
|
|
776
|
-
const text = await (0,
|
|
881
|
+
const text = await (0, import_promises6.readFile)(file, "utf-8");
|
|
777
882
|
const { document, errors } = parseScenarioDocument(text, file);
|
|
778
883
|
if (!document || errors.length > 0) {
|
|
779
884
|
continue;
|
|
@@ -826,10 +931,10 @@ async function collectScTestReferences(root, globs, excludeGlobs) {
|
|
|
826
931
|
};
|
|
827
932
|
}
|
|
828
933
|
const normalizedFiles = Array.from(
|
|
829
|
-
new Set(files.map((file) =>
|
|
934
|
+
new Set(files.map((file) => import_node_path6.default.normalize(file)))
|
|
830
935
|
);
|
|
831
936
|
for (const file of normalizedFiles) {
|
|
832
|
-
const text = await (0,
|
|
937
|
+
const text = await (0, import_promises6.readFile)(file, "utf-8");
|
|
833
938
|
const scIds = extractAnnotatedScIds(text);
|
|
834
939
|
if (scIds.length === 0) {
|
|
835
940
|
continue;
|
|
@@ -886,20 +991,20 @@ function formatError3(error2) {
|
|
|
886
991
|
}
|
|
887
992
|
|
|
888
993
|
// src/core/promptsIntegrity.ts
|
|
889
|
-
var
|
|
890
|
-
var
|
|
994
|
+
var import_promises7 = require("fs/promises");
|
|
995
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
891
996
|
|
|
892
997
|
// src/shared/assets.ts
|
|
893
998
|
var import_node_fs = require("fs");
|
|
894
|
-
var
|
|
999
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
895
1000
|
var import_node_url = require("url");
|
|
896
1001
|
function getInitAssetsDir() {
|
|
897
1002
|
const base = __filename;
|
|
898
1003
|
const basePath = base.startsWith("file:") ? (0, import_node_url.fileURLToPath)(base) : base;
|
|
899
|
-
const baseDir =
|
|
1004
|
+
const baseDir = import_node_path7.default.dirname(basePath);
|
|
900
1005
|
const candidates = [
|
|
901
|
-
|
|
902
|
-
|
|
1006
|
+
import_node_path7.default.resolve(baseDir, "../../../assets/init"),
|
|
1007
|
+
import_node_path7.default.resolve(baseDir, "../../assets/init")
|
|
903
1008
|
];
|
|
904
1009
|
for (const candidate of candidates) {
|
|
905
1010
|
if ((0, import_node_fs.existsSync)(candidate)) {
|
|
@@ -917,10 +1022,10 @@ function getInitAssetsDir() {
|
|
|
917
1022
|
|
|
918
1023
|
// src/core/promptsIntegrity.ts
|
|
919
1024
|
async function diffProjectPromptsAgainstInitAssets(root) {
|
|
920
|
-
const promptsDir =
|
|
1025
|
+
const promptsDir = import_node_path8.default.resolve(root, ".qfai", "prompts");
|
|
921
1026
|
let templateDir;
|
|
922
1027
|
try {
|
|
923
|
-
templateDir =
|
|
1028
|
+
templateDir = import_node_path8.default.join(getInitAssetsDir(), ".qfai", "prompts");
|
|
924
1029
|
} catch {
|
|
925
1030
|
return {
|
|
926
1031
|
status: "skipped_missing_assets",
|
|
@@ -973,8 +1078,8 @@ async function diffProjectPromptsAgainstInitAssets(root) {
|
|
|
973
1078
|
}
|
|
974
1079
|
try {
|
|
975
1080
|
const [a, b] = await Promise.all([
|
|
976
|
-
(0,
|
|
977
|
-
(0,
|
|
1081
|
+
(0, import_promises7.readFile)(templateAbs, "utf-8"),
|
|
1082
|
+
(0, import_promises7.readFile)(projectAbs, "utf-8")
|
|
978
1083
|
]);
|
|
979
1084
|
if (normalizeNewlines(a) !== normalizeNewlines(b)) {
|
|
980
1085
|
changed.push(rel);
|
|
@@ -997,7 +1102,7 @@ function normalizeNewlines(text) {
|
|
|
997
1102
|
return text.replace(/\r\n/g, "\n");
|
|
998
1103
|
}
|
|
999
1104
|
function toRel(base, abs) {
|
|
1000
|
-
const rel =
|
|
1105
|
+
const rel = import_node_path8.default.relative(base, abs);
|
|
1001
1106
|
return rel.replace(/[\\/]+/g, "/");
|
|
1002
1107
|
}
|
|
1003
1108
|
function intersectKeys(a, b) {
|
|
@@ -1011,16 +1116,16 @@ function intersectKeys(a, b) {
|
|
|
1011
1116
|
}
|
|
1012
1117
|
|
|
1013
1118
|
// src/core/version.ts
|
|
1014
|
-
var
|
|
1015
|
-
var
|
|
1119
|
+
var import_promises8 = require("fs/promises");
|
|
1120
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
1016
1121
|
var import_node_url2 = require("url");
|
|
1017
1122
|
async function resolveToolVersion() {
|
|
1018
|
-
if ("0.
|
|
1019
|
-
return "0.
|
|
1123
|
+
if ("1.0.0".length > 0) {
|
|
1124
|
+
return "1.0.0";
|
|
1020
1125
|
}
|
|
1021
1126
|
try {
|
|
1022
1127
|
const packagePath = resolvePackageJsonPath();
|
|
1023
|
-
const raw = await (0,
|
|
1128
|
+
const raw = await (0, import_promises8.readFile)(packagePath, "utf-8");
|
|
1024
1129
|
const parsed = JSON.parse(raw);
|
|
1025
1130
|
const version = typeof parsed.version === "string" ? parsed.version : "";
|
|
1026
1131
|
return version.length > 0 ? version : "unknown";
|
|
@@ -1031,13 +1136,13 @@ async function resolveToolVersion() {
|
|
|
1031
1136
|
function resolvePackageJsonPath() {
|
|
1032
1137
|
const base = __filename;
|
|
1033
1138
|
const basePath = base.startsWith("file:") ? (0, import_node_url2.fileURLToPath)(base) : base;
|
|
1034
|
-
return
|
|
1139
|
+
return import_node_path9.default.resolve(import_node_path9.default.dirname(basePath), "../../package.json");
|
|
1035
1140
|
}
|
|
1036
1141
|
|
|
1037
1142
|
// src/core/doctor.ts
|
|
1038
1143
|
async function exists4(target) {
|
|
1039
1144
|
try {
|
|
1040
|
-
await (0,
|
|
1145
|
+
await (0, import_promises9.access)(target);
|
|
1041
1146
|
return true;
|
|
1042
1147
|
} catch {
|
|
1043
1148
|
return false;
|
|
@@ -1057,7 +1162,7 @@ function normalizeGlobs2(values) {
|
|
|
1057
1162
|
return values.map((glob) => glob.trim()).filter((glob) => glob.length > 0);
|
|
1058
1163
|
}
|
|
1059
1164
|
async function createDoctorData(options) {
|
|
1060
|
-
const startDir =
|
|
1165
|
+
const startDir = import_node_path10.default.resolve(options.startDir);
|
|
1061
1166
|
const checks = [];
|
|
1062
1167
|
const configPath = getConfigPath(startDir);
|
|
1063
1168
|
const search = options.rootExplicit ? {
|
|
@@ -1120,9 +1225,9 @@ async function createDoctorData(options) {
|
|
|
1120
1225
|
details: { path: toRelativePath(root, resolved) }
|
|
1121
1226
|
});
|
|
1122
1227
|
if (key === "promptsDir") {
|
|
1123
|
-
const promptsLocalDir =
|
|
1124
|
-
|
|
1125
|
-
`${
|
|
1228
|
+
const promptsLocalDir = import_node_path10.default.join(
|
|
1229
|
+
import_node_path10.default.dirname(resolved),
|
|
1230
|
+
`${import_node_path10.default.basename(resolved)}.local`
|
|
1126
1231
|
);
|
|
1127
1232
|
const found = await exists4(promptsLocalDir);
|
|
1128
1233
|
addCheck(checks, {
|
|
@@ -1195,7 +1300,7 @@ async function createDoctorData(options) {
|
|
|
1195
1300
|
message: missingFiles === 0 ? `All spec packs have required files (count=${entries.length})` : `Missing required files in spec packs (missingFiles=${missingFiles})`,
|
|
1196
1301
|
details: { specPacks: entries.length, missingFiles }
|
|
1197
1302
|
});
|
|
1198
|
-
const validateJsonAbs =
|
|
1303
|
+
const validateJsonAbs = import_node_path10.default.isAbsolute(config.output.validateJsonPath) ? config.output.validateJsonPath : import_node_path10.default.resolve(root, config.output.validateJsonPath);
|
|
1199
1304
|
const validateJsonExists = await exists4(validateJsonAbs);
|
|
1200
1305
|
addCheck(checks, {
|
|
1201
1306
|
id: "output.validateJson",
|
|
@@ -1205,8 +1310,8 @@ async function createDoctorData(options) {
|
|
|
1205
1310
|
details: { path: toRelativePath(root, validateJsonAbs) }
|
|
1206
1311
|
});
|
|
1207
1312
|
const outDirAbs = resolvePath(root, config, "outDir");
|
|
1208
|
-
const rel =
|
|
1209
|
-
const inside = rel !== "" && !rel.startsWith("..") && !
|
|
1313
|
+
const rel = import_node_path10.default.relative(outDirAbs, validateJsonAbs);
|
|
1314
|
+
const inside = rel !== "" && !rel.startsWith("..") && !import_node_path10.default.isAbsolute(rel);
|
|
1210
1315
|
addCheck(checks, {
|
|
1211
1316
|
id: "output.pathAlignment",
|
|
1212
1317
|
severity: inside ? "ok" : "warning",
|
|
@@ -1312,12 +1417,12 @@ async function detectOutDirCollisions(root) {
|
|
|
1312
1417
|
ignore: DEFAULT_CONFIG_SEARCH_IGNORE_GLOBS
|
|
1313
1418
|
});
|
|
1314
1419
|
const configRoots = Array.from(
|
|
1315
|
-
new Set(configPaths.map((configPath) =>
|
|
1420
|
+
new Set(configPaths.map((configPath) => import_node_path10.default.dirname(configPath)))
|
|
1316
1421
|
).sort((a, b) => a.localeCompare(b));
|
|
1317
1422
|
const outDirToRoots = /* @__PURE__ */ new Map();
|
|
1318
1423
|
for (const configRoot of configRoots) {
|
|
1319
1424
|
const { config } = await loadConfig(configRoot);
|
|
1320
|
-
const outDir =
|
|
1425
|
+
const outDir = import_node_path10.default.normalize(resolvePath(configRoot, config, "outDir"));
|
|
1321
1426
|
const roots = outDirToRoots.get(outDir) ?? /* @__PURE__ */ new Set();
|
|
1322
1427
|
roots.add(configRoot);
|
|
1323
1428
|
outDirToRoots.set(outDir, roots);
|
|
@@ -1334,20 +1439,20 @@ async function detectOutDirCollisions(root) {
|
|
|
1334
1439
|
return { monorepoRoot, configRoots, collisions };
|
|
1335
1440
|
}
|
|
1336
1441
|
async function findMonorepoRoot(startDir) {
|
|
1337
|
-
let current =
|
|
1442
|
+
let current = import_node_path10.default.resolve(startDir);
|
|
1338
1443
|
while (true) {
|
|
1339
|
-
const gitPath =
|
|
1340
|
-
const workspacePath =
|
|
1444
|
+
const gitPath = import_node_path10.default.join(current, ".git");
|
|
1445
|
+
const workspacePath = import_node_path10.default.join(current, "pnpm-workspace.yaml");
|
|
1341
1446
|
if (await exists4(gitPath) || await exists4(workspacePath)) {
|
|
1342
1447
|
return current;
|
|
1343
1448
|
}
|
|
1344
|
-
const parent =
|
|
1449
|
+
const parent = import_node_path10.default.dirname(current);
|
|
1345
1450
|
if (parent === current) {
|
|
1346
1451
|
break;
|
|
1347
1452
|
}
|
|
1348
1453
|
current = parent;
|
|
1349
1454
|
}
|
|
1350
|
-
return
|
|
1455
|
+
return import_node_path10.default.resolve(startDir);
|
|
1351
1456
|
}
|
|
1352
1457
|
|
|
1353
1458
|
// src/cli/lib/logger.ts
|
|
@@ -1389,9 +1494,9 @@ async function runDoctor(options) {
|
|
|
1389
1494
|
const output = options.format === "json" ? formatDoctorJson(data) : formatDoctorText(data);
|
|
1390
1495
|
const exitCode = shouldFailDoctor(data.summary, options.failOn) ? 1 : 0;
|
|
1391
1496
|
if (options.outPath) {
|
|
1392
|
-
const outAbs =
|
|
1393
|
-
await (0,
|
|
1394
|
-
await (0,
|
|
1497
|
+
const outAbs = import_node_path11.default.isAbsolute(options.outPath) ? options.outPath : import_node_path11.default.resolve(process.cwd(), options.outPath);
|
|
1498
|
+
await (0, import_promises10.mkdir)(import_node_path11.default.dirname(outAbs), { recursive: true });
|
|
1499
|
+
await (0, import_promises10.writeFile)(outAbs, `${output}
|
|
1395
1500
|
`, "utf-8");
|
|
1396
1501
|
info(`doctor: wrote ${outAbs}`);
|
|
1397
1502
|
return exitCode;
|
|
@@ -1410,11 +1515,11 @@ function shouldFailDoctor(summary, failOn) {
|
|
|
1410
1515
|
}
|
|
1411
1516
|
|
|
1412
1517
|
// src/cli/commands/init.ts
|
|
1413
|
-
var
|
|
1518
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
1414
1519
|
|
|
1415
1520
|
// src/cli/lib/fs.ts
|
|
1416
|
-
var
|
|
1417
|
-
var
|
|
1521
|
+
var import_promises11 = require("fs/promises");
|
|
1522
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
1418
1523
|
async function copyTemplateTree(sourceRoot, destRoot, options) {
|
|
1419
1524
|
const files = await collectTemplateFiles(sourceRoot);
|
|
1420
1525
|
return copyFiles(files, sourceRoot, destRoot, options);
|
|
@@ -1422,7 +1527,7 @@ async function copyTemplateTree(sourceRoot, destRoot, options) {
|
|
|
1422
1527
|
async function copyTemplatePaths(sourceRoot, destRoot, relativePaths, options) {
|
|
1423
1528
|
const allFiles = [];
|
|
1424
1529
|
for (const relPath of relativePaths) {
|
|
1425
|
-
const fullPath =
|
|
1530
|
+
const fullPath = import_node_path12.default.join(sourceRoot, relPath);
|
|
1426
1531
|
const files = await collectTemplateFiles(fullPath);
|
|
1427
1532
|
allFiles.push(...files);
|
|
1428
1533
|
}
|
|
@@ -1432,13 +1537,13 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
|
|
|
1432
1537
|
const copied = [];
|
|
1433
1538
|
const skipped = [];
|
|
1434
1539
|
const conflicts = [];
|
|
1435
|
-
const protectPrefixes = (options.protect ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p +
|
|
1436
|
-
const excludePrefixes = (options.exclude ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p +
|
|
1540
|
+
const protectPrefixes = (options.protect ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + import_node_path12.default.sep);
|
|
1541
|
+
const excludePrefixes = (options.exclude ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + import_node_path12.default.sep);
|
|
1437
1542
|
const isProtectedRelative = (relative) => {
|
|
1438
1543
|
if (protectPrefixes.length === 0) {
|
|
1439
1544
|
return false;
|
|
1440
1545
|
}
|
|
1441
|
-
const normalized = relative.replace(/[\\/]+/g,
|
|
1546
|
+
const normalized = relative.replace(/[\\/]+/g, import_node_path12.default.sep);
|
|
1442
1547
|
return protectPrefixes.some(
|
|
1443
1548
|
(prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
|
|
1444
1549
|
);
|
|
@@ -1447,7 +1552,7 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
|
|
|
1447
1552
|
if (excludePrefixes.length === 0) {
|
|
1448
1553
|
return false;
|
|
1449
1554
|
}
|
|
1450
|
-
const normalized = relative.replace(/[\\/]+/g,
|
|
1555
|
+
const normalized = relative.replace(/[\\/]+/g, import_node_path12.default.sep);
|
|
1451
1556
|
return excludePrefixes.some(
|
|
1452
1557
|
(prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
|
|
1453
1558
|
);
|
|
@@ -1455,14 +1560,14 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
|
|
|
1455
1560
|
const conflictPolicy = options.conflictPolicy ?? "error";
|
|
1456
1561
|
if (!options.force && conflictPolicy === "error") {
|
|
1457
1562
|
for (const file of files) {
|
|
1458
|
-
const relative =
|
|
1563
|
+
const relative = import_node_path12.default.relative(sourceRoot, file);
|
|
1459
1564
|
if (isExcludedRelative(relative)) {
|
|
1460
1565
|
continue;
|
|
1461
1566
|
}
|
|
1462
1567
|
if (isProtectedRelative(relative)) {
|
|
1463
1568
|
continue;
|
|
1464
1569
|
}
|
|
1465
|
-
const dest =
|
|
1570
|
+
const dest = import_node_path12.default.join(destRoot, relative);
|
|
1466
1571
|
if (!await shouldWrite(dest, options.force)) {
|
|
1467
1572
|
conflicts.push(dest);
|
|
1468
1573
|
}
|
|
@@ -1472,19 +1577,19 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
|
|
|
1472
1577
|
}
|
|
1473
1578
|
}
|
|
1474
1579
|
for (const file of files) {
|
|
1475
|
-
const relative =
|
|
1580
|
+
const relative = import_node_path12.default.relative(sourceRoot, file);
|
|
1476
1581
|
if (isExcludedRelative(relative)) {
|
|
1477
1582
|
continue;
|
|
1478
1583
|
}
|
|
1479
|
-
const dest =
|
|
1584
|
+
const dest = import_node_path12.default.join(destRoot, relative);
|
|
1480
1585
|
const forceForThisFile = isProtectedRelative(relative) ? false : options.force;
|
|
1481
1586
|
if (!await shouldWrite(dest, forceForThisFile)) {
|
|
1482
1587
|
skipped.push(dest);
|
|
1483
1588
|
continue;
|
|
1484
1589
|
}
|
|
1485
1590
|
if (!options.dryRun) {
|
|
1486
|
-
await (0,
|
|
1487
|
-
await (0,
|
|
1591
|
+
await (0, import_promises11.mkdir)(import_node_path12.default.dirname(dest), { recursive: true });
|
|
1592
|
+
await (0, import_promises11.copyFile)(file, dest);
|
|
1488
1593
|
}
|
|
1489
1594
|
copied.push(dest);
|
|
1490
1595
|
}
|
|
@@ -1505,9 +1610,9 @@ async function collectTemplateFiles(root) {
|
|
|
1505
1610
|
if (!await exists5(root)) {
|
|
1506
1611
|
return entries;
|
|
1507
1612
|
}
|
|
1508
|
-
const items = await (0,
|
|
1613
|
+
const items = await (0, import_promises11.readdir)(root, { withFileTypes: true });
|
|
1509
1614
|
for (const item of items) {
|
|
1510
|
-
const fullPath =
|
|
1615
|
+
const fullPath = import_node_path12.default.join(root, item.name);
|
|
1511
1616
|
if (item.isDirectory()) {
|
|
1512
1617
|
const nested = await collectTemplateFiles(fullPath);
|
|
1513
1618
|
entries.push(...nested);
|
|
@@ -1527,7 +1632,7 @@ async function shouldWrite(target, force) {
|
|
|
1527
1632
|
}
|
|
1528
1633
|
async function exists5(target) {
|
|
1529
1634
|
try {
|
|
1530
|
-
await (0,
|
|
1635
|
+
await (0, import_promises11.access)(target);
|
|
1531
1636
|
return true;
|
|
1532
1637
|
} catch {
|
|
1533
1638
|
return false;
|
|
@@ -1537,10 +1642,10 @@ async function exists5(target) {
|
|
|
1537
1642
|
// src/cli/commands/init.ts
|
|
1538
1643
|
async function runInit(options) {
|
|
1539
1644
|
const assetsRoot = getInitAssetsDir();
|
|
1540
|
-
const rootAssets =
|
|
1541
|
-
const qfaiAssets =
|
|
1542
|
-
const destRoot =
|
|
1543
|
-
const destQfai =
|
|
1645
|
+
const rootAssets = import_node_path13.default.join(assetsRoot, "root");
|
|
1646
|
+
const qfaiAssets = import_node_path13.default.join(assetsRoot, ".qfai");
|
|
1647
|
+
const destRoot = import_node_path13.default.resolve(options.dir);
|
|
1648
|
+
const destQfai = import_node_path13.default.join(destRoot, ".qfai");
|
|
1544
1649
|
if (options.force) {
|
|
1545
1650
|
info(
|
|
1546
1651
|
"NOTE: --force \u306F .qfai/prompts/** \u306E\u307F\u4E0A\u66F8\u304D\u3057\u307E\u3059\uFF08prompts.local \u306F\u4FDD\u8B77\u3055\u308C\u3001specs/contracts \u7B49\u306F\u4E0A\u66F8\u304D\u3057\u307E\u305B\u3093\uFF09\u3002"
|
|
@@ -1587,8 +1692,8 @@ function report(copied, skipped, dryRun, label) {
|
|
|
1587
1692
|
}
|
|
1588
1693
|
|
|
1589
1694
|
// src/cli/commands/report.ts
|
|
1590
|
-
var
|
|
1591
|
-
var
|
|
1695
|
+
var import_promises20 = require("fs/promises");
|
|
1696
|
+
var import_node_path20 = __toESM(require("path"), 1);
|
|
1592
1697
|
|
|
1593
1698
|
// src/core/normalize.ts
|
|
1594
1699
|
function normalizeIssuePaths(root, issues) {
|
|
@@ -1628,12 +1733,12 @@ function normalizeValidationResult(root, result) {
|
|
|
1628
1733
|
}
|
|
1629
1734
|
|
|
1630
1735
|
// src/core/report.ts
|
|
1631
|
-
var
|
|
1632
|
-
var
|
|
1736
|
+
var import_promises19 = require("fs/promises");
|
|
1737
|
+
var import_node_path19 = __toESM(require("path"), 1);
|
|
1633
1738
|
|
|
1634
1739
|
// src/core/contractIndex.ts
|
|
1635
|
-
var
|
|
1636
|
-
var
|
|
1740
|
+
var import_promises12 = require("fs/promises");
|
|
1741
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
1637
1742
|
|
|
1638
1743
|
// src/core/contractsDecl.ts
|
|
1639
1744
|
var CONTRACT_DECLARATION_RE = /^\s*(?:#|\/\/|--|\/\*+|\*+)?\s*QFAI-CONTRACT-ID:\s*((?:API|UI|DB)-\d{4})\s*(?:\*\/)?\s*$/gm;
|
|
@@ -1655,9 +1760,9 @@ function stripContractDeclarationLines(text) {
|
|
|
1655
1760
|
// src/core/contractIndex.ts
|
|
1656
1761
|
async function buildContractIndex(root, config) {
|
|
1657
1762
|
const contractsRoot = resolvePath(root, config, "contractsDir");
|
|
1658
|
-
const uiRoot =
|
|
1659
|
-
const apiRoot =
|
|
1660
|
-
const dbRoot =
|
|
1763
|
+
const uiRoot = import_node_path14.default.join(contractsRoot, "ui");
|
|
1764
|
+
const apiRoot = import_node_path14.default.join(contractsRoot, "api");
|
|
1765
|
+
const dbRoot = import_node_path14.default.join(contractsRoot, "db");
|
|
1661
1766
|
const [uiFiles, apiFiles, dbFiles] = await Promise.all([
|
|
1662
1767
|
collectUiContractFiles(uiRoot),
|
|
1663
1768
|
collectApiContractFiles(apiRoot),
|
|
@@ -1675,7 +1780,7 @@ async function buildContractIndex(root, config) {
|
|
|
1675
1780
|
}
|
|
1676
1781
|
async function indexContractFiles(files, index) {
|
|
1677
1782
|
for (const file of files) {
|
|
1678
|
-
const text = await (0,
|
|
1783
|
+
const text = await (0, import_promises12.readFile)(file, "utf-8");
|
|
1679
1784
|
extractDeclaredContractIds(text).forEach((id) => record(index, id, file));
|
|
1680
1785
|
}
|
|
1681
1786
|
}
|
|
@@ -1910,14 +2015,14 @@ function parseSpec(md, file) {
|
|
|
1910
2015
|
}
|
|
1911
2016
|
|
|
1912
2017
|
// src/core/validators/contracts.ts
|
|
1913
|
-
var
|
|
1914
|
-
var
|
|
2018
|
+
var import_promises13 = require("fs/promises");
|
|
2019
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
1915
2020
|
|
|
1916
2021
|
// src/core/contracts.ts
|
|
1917
|
-
var
|
|
2022
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
1918
2023
|
var import_yaml2 = require("yaml");
|
|
1919
2024
|
function parseStructuredContract(file, text) {
|
|
1920
|
-
const ext =
|
|
2025
|
+
const ext = import_node_path15.default.extname(file).toLowerCase();
|
|
1921
2026
|
if (ext === ".json") {
|
|
1922
2027
|
return JSON.parse(text);
|
|
1923
2028
|
}
|
|
@@ -1937,9 +2042,9 @@ var SQL_DANGEROUS_PATTERNS = [
|
|
|
1937
2042
|
async function validateContracts(root, config) {
|
|
1938
2043
|
const issues = [];
|
|
1939
2044
|
const contractsRoot = resolvePath(root, config, "contractsDir");
|
|
1940
|
-
issues.push(...await validateUiContracts(
|
|
1941
|
-
issues.push(...await validateApiContracts(
|
|
1942
|
-
issues.push(...await validateDbContracts(
|
|
2045
|
+
issues.push(...await validateUiContracts(import_node_path16.default.join(contractsRoot, "ui")));
|
|
2046
|
+
issues.push(...await validateApiContracts(import_node_path16.default.join(contractsRoot, "api")));
|
|
2047
|
+
issues.push(...await validateDbContracts(import_node_path16.default.join(contractsRoot, "db")));
|
|
1943
2048
|
const contractIndex = await buildContractIndex(root, config);
|
|
1944
2049
|
issues.push(...validateDuplicateContractIds(contractIndex));
|
|
1945
2050
|
return issues;
|
|
@@ -1959,7 +2064,7 @@ async function validateUiContracts(uiRoot) {
|
|
|
1959
2064
|
}
|
|
1960
2065
|
const issues = [];
|
|
1961
2066
|
for (const file of files) {
|
|
1962
|
-
const text = await (0,
|
|
2067
|
+
const text = await (0, import_promises13.readFile)(file, "utf-8");
|
|
1963
2068
|
const invalidIds = extractInvalidIds(text, [
|
|
1964
2069
|
"SPEC",
|
|
1965
2070
|
"BR",
|
|
@@ -2014,7 +2119,7 @@ async function validateApiContracts(apiRoot) {
|
|
|
2014
2119
|
}
|
|
2015
2120
|
const issues = [];
|
|
2016
2121
|
for (const file of files) {
|
|
2017
|
-
const text = await (0,
|
|
2122
|
+
const text = await (0, import_promises13.readFile)(file, "utf-8");
|
|
2018
2123
|
const invalidIds = extractInvalidIds(text, [
|
|
2019
2124
|
"SPEC",
|
|
2020
2125
|
"BR",
|
|
@@ -2082,7 +2187,7 @@ async function validateDbContracts(dbRoot) {
|
|
|
2082
2187
|
}
|
|
2083
2188
|
const issues = [];
|
|
2084
2189
|
for (const file of files) {
|
|
2085
|
-
const text = await (0,
|
|
2190
|
+
const text = await (0, import_promises13.readFile)(file, "utf-8");
|
|
2086
2191
|
const invalidIds = extractInvalidIds(text, [
|
|
2087
2192
|
"SPEC",
|
|
2088
2193
|
"BR",
|
|
@@ -2225,8 +2330,8 @@ function issue(code, message, severity, file, rule, refs, category = "compatibil
|
|
|
2225
2330
|
}
|
|
2226
2331
|
|
|
2227
2332
|
// src/core/validators/delta.ts
|
|
2228
|
-
var
|
|
2229
|
-
var
|
|
2333
|
+
var import_promises14 = require("fs/promises");
|
|
2334
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
2230
2335
|
var SECTION_RE = /^##\s+変更区分/m;
|
|
2231
2336
|
var COMPAT_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Compatibility\b/m;
|
|
2232
2337
|
var CHANGE_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Change\/Improvement\b/m;
|
|
@@ -2240,10 +2345,10 @@ async function validateDeltas(root, config) {
|
|
|
2240
2345
|
}
|
|
2241
2346
|
const issues = [];
|
|
2242
2347
|
for (const pack of packs) {
|
|
2243
|
-
const deltaPath =
|
|
2348
|
+
const deltaPath = import_node_path17.default.join(pack, "delta.md");
|
|
2244
2349
|
let text;
|
|
2245
2350
|
try {
|
|
2246
|
-
text = await (0,
|
|
2351
|
+
text = await (0, import_promises14.readFile)(deltaPath, "utf-8");
|
|
2247
2352
|
} catch (error2) {
|
|
2248
2353
|
if (isMissingFileError2(error2)) {
|
|
2249
2354
|
issues.push(
|
|
@@ -2319,8 +2424,8 @@ function issue2(code, message, severity, file, rule, refs, category = "change",
|
|
|
2319
2424
|
}
|
|
2320
2425
|
|
|
2321
2426
|
// src/core/validators/ids.ts
|
|
2322
|
-
var
|
|
2323
|
-
var
|
|
2427
|
+
var import_promises15 = require("fs/promises");
|
|
2428
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
2324
2429
|
var SC_TAG_RE3 = /^SC-\d{4}$/;
|
|
2325
2430
|
async function validateDefinedIds(root, config) {
|
|
2326
2431
|
const issues = [];
|
|
@@ -2355,7 +2460,7 @@ async function validateDefinedIds(root, config) {
|
|
|
2355
2460
|
}
|
|
2356
2461
|
async function collectSpecDefinitionIds(files, out) {
|
|
2357
2462
|
for (const file of files) {
|
|
2358
|
-
const text = await (0,
|
|
2463
|
+
const text = await (0, import_promises15.readFile)(file, "utf-8");
|
|
2359
2464
|
const parsed = parseSpec(text, file);
|
|
2360
2465
|
if (parsed.specId) {
|
|
2361
2466
|
recordId(out, parsed.specId, file);
|
|
@@ -2365,7 +2470,7 @@ async function collectSpecDefinitionIds(files, out) {
|
|
|
2365
2470
|
}
|
|
2366
2471
|
async function collectScenarioDefinitionIds(files, out) {
|
|
2367
2472
|
for (const file of files) {
|
|
2368
|
-
const text = await (0,
|
|
2473
|
+
const text = await (0, import_promises15.readFile)(file, "utf-8");
|
|
2369
2474
|
const { document, errors } = parseScenarioDocument(text, file);
|
|
2370
2475
|
if (!document || errors.length > 0) {
|
|
2371
2476
|
continue;
|
|
@@ -2386,7 +2491,7 @@ function recordId(out, id, file) {
|
|
|
2386
2491
|
}
|
|
2387
2492
|
function formatFileList(files, root) {
|
|
2388
2493
|
return files.map((file) => {
|
|
2389
|
-
const relative =
|
|
2494
|
+
const relative = import_node_path18.default.relative(root, file);
|
|
2390
2495
|
return relative.length > 0 ? relative : file;
|
|
2391
2496
|
}).join(", ");
|
|
2392
2497
|
}
|
|
@@ -2444,7 +2549,7 @@ async function validatePromptsIntegrity(root) {
|
|
|
2444
2549
|
}
|
|
2445
2550
|
|
|
2446
2551
|
// src/core/validators/scenario.ts
|
|
2447
|
-
var
|
|
2552
|
+
var import_promises16 = require("fs/promises");
|
|
2448
2553
|
var GIVEN_PATTERN = /\bGiven\b/;
|
|
2449
2554
|
var WHEN_PATTERN = /\bWhen\b/;
|
|
2450
2555
|
var THEN_PATTERN = /\bThen\b/;
|
|
@@ -2470,7 +2575,7 @@ async function validateScenarios(root, config) {
|
|
|
2470
2575
|
for (const entry of entries) {
|
|
2471
2576
|
let text;
|
|
2472
2577
|
try {
|
|
2473
|
-
text = await (0,
|
|
2578
|
+
text = await (0, import_promises16.readFile)(entry.scenarioPath, "utf-8");
|
|
2474
2579
|
} catch (error2) {
|
|
2475
2580
|
if (isMissingFileError3(error2)) {
|
|
2476
2581
|
issues.push(
|
|
@@ -2644,7 +2749,7 @@ function isMissingFileError3(error2) {
|
|
|
2644
2749
|
}
|
|
2645
2750
|
|
|
2646
2751
|
// src/core/validators/spec.ts
|
|
2647
|
-
var
|
|
2752
|
+
var import_promises17 = require("fs/promises");
|
|
2648
2753
|
async function validateSpecs(root, config) {
|
|
2649
2754
|
const specsRoot = resolvePath(root, config, "specsDir");
|
|
2650
2755
|
const entries = await collectSpecEntries(specsRoot);
|
|
@@ -2665,7 +2770,7 @@ async function validateSpecs(root, config) {
|
|
|
2665
2770
|
for (const entry of entries) {
|
|
2666
2771
|
let text;
|
|
2667
2772
|
try {
|
|
2668
|
-
text = await (0,
|
|
2773
|
+
text = await (0, import_promises17.readFile)(entry.specPath, "utf-8");
|
|
2669
2774
|
} catch (error2) {
|
|
2670
2775
|
if (isMissingFileError4(error2)) {
|
|
2671
2776
|
issues.push(
|
|
@@ -2818,7 +2923,7 @@ function isMissingFileError4(error2) {
|
|
|
2818
2923
|
}
|
|
2819
2924
|
|
|
2820
2925
|
// src/core/validators/traceability.ts
|
|
2821
|
-
var
|
|
2926
|
+
var import_promises18 = require("fs/promises");
|
|
2822
2927
|
var SPEC_TAG_RE3 = /^SPEC-\d{4}$/;
|
|
2823
2928
|
var BR_TAG_RE2 = /^BR-\d{4}$/;
|
|
2824
2929
|
async function validateTraceability(root, config) {
|
|
@@ -2838,7 +2943,7 @@ async function validateTraceability(root, config) {
|
|
|
2838
2943
|
const contractIndex = await buildContractIndex(root, config);
|
|
2839
2944
|
const contractIds = contractIndex.ids;
|
|
2840
2945
|
for (const file of specFiles) {
|
|
2841
|
-
const text = await (0,
|
|
2946
|
+
const text = await (0, import_promises18.readFile)(file, "utf-8");
|
|
2842
2947
|
extractAllIds(text).forEach((id) => upstreamIds.add(id));
|
|
2843
2948
|
const parsed = parseSpec(text, file);
|
|
2844
2949
|
if (parsed.specId) {
|
|
@@ -2911,7 +3016,7 @@ async function validateTraceability(root, config) {
|
|
|
2911
3016
|
}
|
|
2912
3017
|
}
|
|
2913
3018
|
for (const file of scenarioFiles) {
|
|
2914
|
-
const text = await (0,
|
|
3019
|
+
const text = await (0, import_promises18.readFile)(file, "utf-8");
|
|
2915
3020
|
extractAllIds(text).forEach((id) => upstreamIds.add(id));
|
|
2916
3021
|
const scenarioContractRefs = parseContractRefs(text, {
|
|
2917
3022
|
allowCommentPrefix: true
|
|
@@ -3233,7 +3338,7 @@ async function validateCodeReferences(upstreamIds, srcRoot, testsRoot) {
|
|
|
3233
3338
|
const pattern = buildIdPattern(Array.from(upstreamIds));
|
|
3234
3339
|
let found = false;
|
|
3235
3340
|
for (const file of targetFiles) {
|
|
3236
|
-
const text = await (0,
|
|
3341
|
+
const text = await (0, import_promises18.readFile)(file, "utf-8");
|
|
3237
3342
|
if (pattern.test(text)) {
|
|
3238
3343
|
found = true;
|
|
3239
3344
|
break;
|
|
@@ -3325,15 +3430,15 @@ function countIssues(issues) {
|
|
|
3325
3430
|
// src/core/report.ts
|
|
3326
3431
|
var ID_PREFIXES2 = ["SPEC", "BR", "SC", "UI", "API", "DB"];
|
|
3327
3432
|
async function createReportData(root, validation, configResult) {
|
|
3328
|
-
const resolvedRoot =
|
|
3433
|
+
const resolvedRoot = import_node_path19.default.resolve(root);
|
|
3329
3434
|
const resolved = configResult ?? await loadConfig(resolvedRoot);
|
|
3330
3435
|
const config = resolved.config;
|
|
3331
3436
|
const configPath = resolved.configPath;
|
|
3332
3437
|
const specsRoot = resolvePath(resolvedRoot, config, "specsDir");
|
|
3333
3438
|
const contractsRoot = resolvePath(resolvedRoot, config, "contractsDir");
|
|
3334
|
-
const apiRoot =
|
|
3335
|
-
const uiRoot =
|
|
3336
|
-
const dbRoot =
|
|
3439
|
+
const apiRoot = import_node_path19.default.join(contractsRoot, "api");
|
|
3440
|
+
const uiRoot = import_node_path19.default.join(contractsRoot, "ui");
|
|
3441
|
+
const dbRoot = import_node_path19.default.join(contractsRoot, "db");
|
|
3337
3442
|
const srcRoot = resolvePath(resolvedRoot, config, "srcDir");
|
|
3338
3443
|
const testsRoot = resolvePath(resolvedRoot, config, "testsDir");
|
|
3339
3444
|
const specFiles = await collectSpecFiles(specsRoot);
|
|
@@ -3804,7 +3909,7 @@ async function collectSpecContractRefs(specFiles, contractIdList) {
|
|
|
3804
3909
|
idToSpecs.set(contractId, /* @__PURE__ */ new Set());
|
|
3805
3910
|
}
|
|
3806
3911
|
for (const file of specFiles) {
|
|
3807
|
-
const text = await (0,
|
|
3912
|
+
const text = await (0, import_promises19.readFile)(file, "utf-8");
|
|
3808
3913
|
const parsed = parseSpec(text, file);
|
|
3809
3914
|
const specKey = parsed.specId;
|
|
3810
3915
|
if (!specKey) {
|
|
@@ -3845,7 +3950,7 @@ async function collectIds(files) {
|
|
|
3845
3950
|
DB: /* @__PURE__ */ new Set()
|
|
3846
3951
|
};
|
|
3847
3952
|
for (const file of files) {
|
|
3848
|
-
const text = await (0,
|
|
3953
|
+
const text = await (0, import_promises19.readFile)(file, "utf-8");
|
|
3849
3954
|
for (const prefix of ID_PREFIXES2) {
|
|
3850
3955
|
const ids = extractIds(text, prefix);
|
|
3851
3956
|
ids.forEach((id) => result[prefix].add(id));
|
|
@@ -3863,7 +3968,7 @@ async function collectIds(files) {
|
|
|
3863
3968
|
async function collectUpstreamIds(files) {
|
|
3864
3969
|
const ids = /* @__PURE__ */ new Set();
|
|
3865
3970
|
for (const file of files) {
|
|
3866
|
-
const text = await (0,
|
|
3971
|
+
const text = await (0, import_promises19.readFile)(file, "utf-8");
|
|
3867
3972
|
extractAllIds(text).forEach((id) => ids.add(id));
|
|
3868
3973
|
}
|
|
3869
3974
|
return ids;
|
|
@@ -3884,7 +3989,7 @@ async function evaluateTraceability(upstreamIds, srcRoot, testsRoot) {
|
|
|
3884
3989
|
}
|
|
3885
3990
|
const pattern = buildIdPattern2(Array.from(upstreamIds));
|
|
3886
3991
|
for (const file of targetFiles) {
|
|
3887
|
-
const text = await (0,
|
|
3992
|
+
const text = await (0, import_promises19.readFile)(file, "utf-8");
|
|
3888
3993
|
if (pattern.test(text)) {
|
|
3889
3994
|
return true;
|
|
3890
3995
|
}
|
|
@@ -3976,7 +4081,7 @@ function buildHotspots(issues) {
|
|
|
3976
4081
|
|
|
3977
4082
|
// src/cli/commands/report.ts
|
|
3978
4083
|
async function runReport(options) {
|
|
3979
|
-
const root =
|
|
4084
|
+
const root = import_node_path20.default.resolve(options.root);
|
|
3980
4085
|
const configResult = await loadConfig(root);
|
|
3981
4086
|
let validation;
|
|
3982
4087
|
if (options.runValidate) {
|
|
@@ -3993,7 +4098,7 @@ async function runReport(options) {
|
|
|
3993
4098
|
validation = normalized;
|
|
3994
4099
|
} else {
|
|
3995
4100
|
const input = options.inputPath ?? configResult.config.output.validateJsonPath;
|
|
3996
|
-
const inputPath =
|
|
4101
|
+
const inputPath = import_node_path20.default.isAbsolute(input) ? input : import_node_path20.default.resolve(root, input);
|
|
3997
4102
|
try {
|
|
3998
4103
|
validation = await readValidationResult(inputPath);
|
|
3999
4104
|
} catch (err) {
|
|
@@ -4019,11 +4124,11 @@ async function runReport(options) {
|
|
|
4019
4124
|
const data = await createReportData(root, validation, configResult);
|
|
4020
4125
|
const output = options.format === "json" ? formatReportJson(data) : formatReportMarkdown(data);
|
|
4021
4126
|
const outRoot = resolvePath(root, configResult.config, "outDir");
|
|
4022
|
-
const defaultOut = options.format === "json" ?
|
|
4127
|
+
const defaultOut = options.format === "json" ? import_node_path20.default.join(outRoot, "report.json") : import_node_path20.default.join(outRoot, "report.md");
|
|
4023
4128
|
const out = options.outPath ?? defaultOut;
|
|
4024
|
-
const outPath =
|
|
4025
|
-
await (0,
|
|
4026
|
-
await (0,
|
|
4129
|
+
const outPath = import_node_path20.default.isAbsolute(out) ? out : import_node_path20.default.resolve(root, out);
|
|
4130
|
+
await (0, import_promises20.mkdir)(import_node_path20.default.dirname(outPath), { recursive: true });
|
|
4131
|
+
await (0, import_promises20.writeFile)(outPath, `${output}
|
|
4027
4132
|
`, "utf-8");
|
|
4028
4133
|
info(
|
|
4029
4134
|
`report: info=${validation.counts.info} warning=${validation.counts.warning} error=${validation.counts.error}`
|
|
@@ -4031,7 +4136,7 @@ async function runReport(options) {
|
|
|
4031
4136
|
info(`wrote report: ${outPath}`);
|
|
4032
4137
|
}
|
|
4033
4138
|
async function readValidationResult(inputPath) {
|
|
4034
|
-
const raw = await (0,
|
|
4139
|
+
const raw = await (0, import_promises20.readFile)(inputPath, "utf-8");
|
|
4035
4140
|
const parsed = JSON.parse(raw);
|
|
4036
4141
|
if (!isValidationResult(parsed)) {
|
|
4037
4142
|
throw new Error(`validate.json \u306E\u5F62\u5F0F\u304C\u4E0D\u6B63\u3067\u3059: ${inputPath}`);
|
|
@@ -4087,15 +4192,15 @@ function isMissingFileError5(error2) {
|
|
|
4087
4192
|
return record2.code === "ENOENT";
|
|
4088
4193
|
}
|
|
4089
4194
|
async function writeValidationResult(root, outputPath, result) {
|
|
4090
|
-
const abs =
|
|
4091
|
-
await (0,
|
|
4092
|
-
await (0,
|
|
4195
|
+
const abs = import_node_path20.default.isAbsolute(outputPath) ? outputPath : import_node_path20.default.resolve(root, outputPath);
|
|
4196
|
+
await (0, import_promises20.mkdir)(import_node_path20.default.dirname(abs), { recursive: true });
|
|
4197
|
+
await (0, import_promises20.writeFile)(abs, `${JSON.stringify(result, null, 2)}
|
|
4093
4198
|
`, "utf-8");
|
|
4094
4199
|
}
|
|
4095
4200
|
|
|
4096
4201
|
// src/cli/commands/validate.ts
|
|
4097
|
-
var
|
|
4098
|
-
var
|
|
4202
|
+
var import_promises21 = require("fs/promises");
|
|
4203
|
+
var import_node_path21 = __toESM(require("path"), 1);
|
|
4099
4204
|
|
|
4100
4205
|
// src/cli/lib/failOn.ts
|
|
4101
4206
|
function shouldFail(result, failOn) {
|
|
@@ -4110,7 +4215,7 @@ function shouldFail(result, failOn) {
|
|
|
4110
4215
|
|
|
4111
4216
|
// src/cli/commands/validate.ts
|
|
4112
4217
|
async function runValidate(options) {
|
|
4113
|
-
const root =
|
|
4218
|
+
const root = import_node_path21.default.resolve(options.root);
|
|
4114
4219
|
const configResult = await loadConfig(root);
|
|
4115
4220
|
const result = await validateProject(root, configResult);
|
|
4116
4221
|
const normalized = normalizeValidationResult(root, result);
|
|
@@ -4234,12 +4339,12 @@ function issueKey(issue7) {
|
|
|
4234
4339
|
}
|
|
4235
4340
|
async function emitJson(result, root, jsonPath) {
|
|
4236
4341
|
const abs = resolveJsonPath(root, jsonPath);
|
|
4237
|
-
await (0,
|
|
4238
|
-
await (0,
|
|
4342
|
+
await (0, import_promises21.mkdir)(import_node_path21.default.dirname(abs), { recursive: true });
|
|
4343
|
+
await (0, import_promises21.writeFile)(abs, `${JSON.stringify(result, null, 2)}
|
|
4239
4344
|
`, "utf-8");
|
|
4240
4345
|
}
|
|
4241
4346
|
function resolveJsonPath(root, jsonPath) {
|
|
4242
|
-
return
|
|
4347
|
+
return import_node_path21.default.isAbsolute(jsonPath) ? jsonPath : import_node_path21.default.resolve(root, jsonPath);
|
|
4243
4348
|
}
|
|
4244
4349
|
var GITHUB_ANNOTATION_LIMIT = 100;
|
|
4245
4350
|
|
|
@@ -4252,6 +4357,7 @@ function parseArgs(argv, cwd) {
|
|
|
4252
4357
|
force: false,
|
|
4253
4358
|
yes: false,
|
|
4254
4359
|
dryRun: false,
|
|
4360
|
+
analyzeList: false,
|
|
4255
4361
|
reportFormat: "md",
|
|
4256
4362
|
reportRunValidate: false,
|
|
4257
4363
|
doctorFormat: "text",
|
|
@@ -4261,6 +4367,7 @@ function parseArgs(argv, cwd) {
|
|
|
4261
4367
|
};
|
|
4262
4368
|
const args = [...argv];
|
|
4263
4369
|
let command = args.shift() ?? null;
|
|
4370
|
+
let invalid = false;
|
|
4264
4371
|
if (command === "--help" || command === "-h") {
|
|
4265
4372
|
options.help = true;
|
|
4266
4373
|
command = null;
|
|
@@ -4269,13 +4376,29 @@ function parseArgs(argv, cwd) {
|
|
|
4269
4376
|
const arg = args[i];
|
|
4270
4377
|
switch (arg) {
|
|
4271
4378
|
case "--root":
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4379
|
+
{
|
|
4380
|
+
const next = readOptionValue(args, i);
|
|
4381
|
+
if (next === null) {
|
|
4382
|
+
invalid = true;
|
|
4383
|
+
options.help = true;
|
|
4384
|
+
break;
|
|
4385
|
+
}
|
|
4386
|
+
options.root = next;
|
|
4387
|
+
options.rootExplicit = true;
|
|
4388
|
+
i += 1;
|
|
4389
|
+
}
|
|
4275
4390
|
break;
|
|
4276
4391
|
case "--dir":
|
|
4277
|
-
|
|
4278
|
-
|
|
4392
|
+
{
|
|
4393
|
+
const next = readOptionValue(args, i);
|
|
4394
|
+
if (next === null) {
|
|
4395
|
+
invalid = true;
|
|
4396
|
+
options.help = true;
|
|
4397
|
+
break;
|
|
4398
|
+
}
|
|
4399
|
+
options.dir = next;
|
|
4400
|
+
i += 1;
|
|
4401
|
+
}
|
|
4279
4402
|
break;
|
|
4280
4403
|
case "--force":
|
|
4281
4404
|
options.force = true;
|
|
@@ -4286,8 +4409,25 @@ function parseArgs(argv, cwd) {
|
|
|
4286
4409
|
case "--dry-run":
|
|
4287
4410
|
options.dryRun = true;
|
|
4288
4411
|
break;
|
|
4412
|
+
case "--list":
|
|
4413
|
+
options.analyzeList = true;
|
|
4414
|
+
break;
|
|
4415
|
+
case "--prompt":
|
|
4416
|
+
{
|
|
4417
|
+
const next = readOptionValue(args, i);
|
|
4418
|
+
if (next) {
|
|
4419
|
+
options.analyzePrompt = next;
|
|
4420
|
+
i += 1;
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
break;
|
|
4289
4424
|
case "--format": {
|
|
4290
|
-
const next = args
|
|
4425
|
+
const next = readOptionValue(args, i);
|
|
4426
|
+
if (next === null) {
|
|
4427
|
+
invalid = true;
|
|
4428
|
+
options.help = true;
|
|
4429
|
+
break;
|
|
4430
|
+
}
|
|
4291
4431
|
applyFormatOption(command, next, options);
|
|
4292
4432
|
i += 1;
|
|
4293
4433
|
break;
|
|
@@ -4296,35 +4436,44 @@ function parseArgs(argv, cwd) {
|
|
|
4296
4436
|
options.strict = true;
|
|
4297
4437
|
break;
|
|
4298
4438
|
case "--fail-on": {
|
|
4299
|
-
const next = args
|
|
4439
|
+
const next = readOptionValue(args, i);
|
|
4440
|
+
if (next === null) {
|
|
4441
|
+
invalid = true;
|
|
4442
|
+
options.help = true;
|
|
4443
|
+
break;
|
|
4444
|
+
}
|
|
4300
4445
|
if (next === "never" || next === "warning" || next === "error") {
|
|
4301
4446
|
options.failOn = next;
|
|
4302
4447
|
}
|
|
4303
4448
|
i += 1;
|
|
4304
4449
|
break;
|
|
4305
4450
|
}
|
|
4306
|
-
case "--out":
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4451
|
+
case "--out": {
|
|
4452
|
+
const next = readOptionValue(args, i);
|
|
4453
|
+
if (next === null) {
|
|
4454
|
+
invalid = true;
|
|
4455
|
+
options.help = true;
|
|
4456
|
+
break;
|
|
4457
|
+
}
|
|
4458
|
+
if (command === "doctor") {
|
|
4459
|
+
options.doctorOut = next;
|
|
4460
|
+
} else {
|
|
4461
|
+
options.reportOut = next;
|
|
4316
4462
|
}
|
|
4317
4463
|
i += 1;
|
|
4318
4464
|
break;
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4465
|
+
}
|
|
4466
|
+
case "--in": {
|
|
4467
|
+
const next = readOptionValue(args, i);
|
|
4468
|
+
if (next === null) {
|
|
4469
|
+
invalid = true;
|
|
4470
|
+
options.help = true;
|
|
4471
|
+
break;
|
|
4325
4472
|
}
|
|
4473
|
+
options.reportIn = next;
|
|
4326
4474
|
i += 1;
|
|
4327
4475
|
break;
|
|
4476
|
+
}
|
|
4328
4477
|
case "--run-validate":
|
|
4329
4478
|
options.reportRunValidate = true;
|
|
4330
4479
|
break;
|
|
@@ -4336,7 +4485,14 @@ function parseArgs(argv, cwd) {
|
|
|
4336
4485
|
break;
|
|
4337
4486
|
}
|
|
4338
4487
|
}
|
|
4339
|
-
return { command, options };
|
|
4488
|
+
return { command, invalid, options };
|
|
4489
|
+
}
|
|
4490
|
+
function readOptionValue(args, index) {
|
|
4491
|
+
const next = args[index + 1];
|
|
4492
|
+
if (!next || next.startsWith("--")) {
|
|
4493
|
+
return null;
|
|
4494
|
+
}
|
|
4495
|
+
return next;
|
|
4340
4496
|
}
|
|
4341
4497
|
function applyFormatOption(command, value, options) {
|
|
4342
4498
|
if (!value) {
|
|
@@ -4370,9 +4526,12 @@ function applyFormatOption(command, value, options) {
|
|
|
4370
4526
|
|
|
4371
4527
|
// src/cli/main.ts
|
|
4372
4528
|
async function run(argv, cwd) {
|
|
4373
|
-
const { command, options } = parseArgs(argv, cwd);
|
|
4529
|
+
const { command, invalid, options } = parseArgs(argv, cwd);
|
|
4374
4530
|
if (!command || options.help) {
|
|
4375
4531
|
info(usage());
|
|
4532
|
+
if (invalid) {
|
|
4533
|
+
process.exitCode = 1;
|
|
4534
|
+
}
|
|
4376
4535
|
return;
|
|
4377
4536
|
}
|
|
4378
4537
|
switch (command) {
|
|
@@ -4384,6 +4543,17 @@ async function run(argv, cwd) {
|
|
|
4384
4543
|
yes: options.yes
|
|
4385
4544
|
});
|
|
4386
4545
|
return;
|
|
4546
|
+
case "analyze":
|
|
4547
|
+
{
|
|
4548
|
+
const resolvedRoot = await resolveRoot(options);
|
|
4549
|
+
const exitCode = await runAnalyze({
|
|
4550
|
+
root: resolvedRoot,
|
|
4551
|
+
list: options.analyzeList,
|
|
4552
|
+
...options.analyzePrompt !== void 0 ? { prompt: options.analyzePrompt } : {}
|
|
4553
|
+
});
|
|
4554
|
+
process.exitCode = exitCode;
|
|
4555
|
+
}
|
|
4556
|
+
return;
|
|
4387
4557
|
case "validate":
|
|
4388
4558
|
{
|
|
4389
4559
|
const resolvedRoot = await resolveRoot(options);
|
|
@@ -4430,6 +4600,7 @@ function usage() {
|
|
|
4430
4600
|
|
|
4431
4601
|
Commands:
|
|
4432
4602
|
init \u30C6\u30F3\u30D7\u30EC\u3092\u751F\u6210
|
|
4603
|
+
analyze \u610F\u5473\u30EC\u30D9\u30EB\u306E\u30EC\u30D3\u30E5\u30FC\u88DC\u52A9\uFF08\u30D7\u30ED\u30F3\u30D7\u30C8\u51FA\u529B\uFF09
|
|
4433
4604
|
validate \u4ED5\u69D8/\u5951\u7D04/\u53C2\u7167\u306E\u691C\u67FB
|
|
4434
4605
|
report \u691C\u8A3C\u7D50\u679C\u3068\u96C6\u8A08\u3092\u51FA\u529B
|
|
4435
4606
|
doctor \u8A2D\u5B9A/\u30D1\u30B9/\u51FA\u529B\u524D\u63D0\u306E\u8A3A\u65AD
|
|
@@ -4440,6 +4611,8 @@ Options:
|
|
|
4440
4611
|
--force init: .qfai/prompts \u306E\u307F\u4E0A\u66F8\u304D\uFF08\u305D\u308C\u4EE5\u5916\u306F\u65E2\u5B58\u304C\u3042\u308C\u3070\u30B9\u30AD\u30C3\u30D7\uFF09
|
|
4441
4612
|
--yes init: \u4E88\u7D04\u30D5\u30E9\u30B0\uFF08\u73FE\u72B6\u306F\u975E\u5BFE\u8A71\u306E\u305F\u3081\u6319\u52D5\u5DEE\u306A\u3057\u3002\u5C06\u6765\u306E\u5BFE\u8A71\u5C0E\u5165\u6642\u306B\u81EA\u52D5Yes\uFF09
|
|
4442
4613
|
--dry-run \u5909\u66F4\u3092\u884C\u308F\u305A\u8868\u793A\u306E\u307F
|
|
4614
|
+
--list analyze: \u5229\u7528\u53EF\u80FD\u306A\u30D7\u30ED\u30F3\u30D7\u30C8\u4E00\u89A7\u3092\u8868\u793A
|
|
4615
|
+
--prompt <name> analyze: \u6307\u5B9A\u30D7\u30ED\u30F3\u30D7\u30C8\uFF08.md\u7701\u7565\u53EF\uFF09\u3092\u51FA\u529B
|
|
4443
4616
|
--format <text|github> validate \u306E\u51FA\u529B\u5F62\u5F0F
|
|
4444
4617
|
--format <md|json> report \u306E\u51FA\u529B\u5F62\u5F0F
|
|
4445
4618
|
--format <text|json> doctor \u306E\u51FA\u529B\u5F62\u5F0F
|