qfai 1.0.4 → 1.0.6

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.
Files changed (94) hide show
  1. package/README.md +58 -67
  2. package/assets/init/.qfai/README.md +17 -76
  3. package/assets/init/.qfai/assistant/README.md +9 -0
  4. package/assets/init/.qfai/assistant/agents/README.md +36 -0
  5. package/assets/init/.qfai/assistant/agents/architect.md +73 -0
  6. package/assets/init/.qfai/assistant/agents/backend-engineer.md +73 -0
  7. package/assets/init/.qfai/assistant/agents/code-reviewer.md +73 -0
  8. package/assets/init/.qfai/assistant/agents/contract-designer.md +73 -0
  9. package/assets/init/.qfai/assistant/agents/devops-ci-engineer.md +73 -0
  10. package/assets/init/.qfai/assistant/agents/facilitator.md +74 -0
  11. package/assets/init/.qfai/assistant/agents/frontend-engineer.md +73 -0
  12. package/assets/init/.qfai/assistant/agents/interviewer.md +72 -0
  13. package/assets/init/.qfai/assistant/agents/planner.md +73 -0
  14. package/assets/init/.qfai/assistant/agents/qa-engineer.md +73 -0
  15. package/assets/init/.qfai/assistant/agents/requirements-analyst.md +73 -0
  16. package/assets/init/.qfai/assistant/agents/test-engineer.md +73 -0
  17. package/assets/init/.qfai/assistant/instructions/README.md +10 -0
  18. package/assets/init/.qfai/assistant/instructions/agent-selection.md +28 -0
  19. package/assets/init/.qfai/assistant/instructions/communication.md +27 -0
  20. package/assets/init/.qfai/assistant/instructions/constitution.md +131 -0
  21. package/assets/init/.qfai/assistant/instructions/quality.md +27 -0
  22. package/assets/init/.qfai/assistant/instructions/thinking.md +29 -0
  23. package/assets/init/.qfai/assistant/instructions/workflow.md +75 -0
  24. package/assets/init/.qfai/assistant/prompts/README.md +19 -0
  25. package/assets/init/.qfai/assistant/prompts/qfai-discuss.md +173 -0
  26. package/assets/init/.qfai/assistant/prompts/qfai-implement.md +230 -0
  27. package/assets/init/.qfai/assistant/prompts/qfai-pr.md +209 -0
  28. package/assets/init/.qfai/assistant/prompts/qfai-require.md +264 -0
  29. package/assets/init/.qfai/assistant/prompts/qfai-scenario-test.md +220 -0
  30. package/assets/init/.qfai/assistant/prompts/qfai-spec.md +291 -0
  31. package/assets/init/.qfai/assistant/prompts/qfai-unit-test.md +193 -0
  32. package/assets/init/.qfai/assistant/prompts/qfai-verify.md +222 -0
  33. package/assets/init/.qfai/assistant/prompts.local/README.md +8 -0
  34. package/assets/init/.qfai/assistant/steering/README.md +40 -0
  35. package/assets/init/.qfai/assistant/steering/product.md +32 -0
  36. package/assets/init/.qfai/assistant/steering/structure.md +34 -0
  37. package/assets/init/.qfai/assistant/steering/tech.md +37 -0
  38. package/assets/init/.qfai/contracts/README.md +7 -87
  39. package/assets/init/.qfai/contracts/api/README.md +8 -0
  40. package/assets/init/.qfai/contracts/db/README.md +25 -0
  41. package/assets/init/.qfai/contracts/ui/README.md +8 -0
  42. package/assets/init/.qfai/report/README.md +13 -0
  43. package/assets/init/.qfai/require/README.md +4 -26
  44. package/assets/init/.qfai/require/require.md +74 -0
  45. package/assets/init/.qfai/specs/README.md +7 -55
  46. package/assets/init/root/.github/workflows/qfai.yml +1 -1
  47. package/assets/init/root/qfai.config.yaml +3 -3
  48. package/dist/cli/index.cjs +306 -423
  49. package/dist/cli/index.cjs.map +1 -1
  50. package/dist/cli/index.mjs +288 -405
  51. package/dist/cli/index.mjs.map +1 -1
  52. package/dist/index.cjs +29 -14
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.mjs +29 -14
  55. package/dist/index.mjs.map +1 -1
  56. package/package.json +1 -1
  57. package/assets/init/.qfai/contracts/api/api-0001-sample.yaml +0 -15
  58. package/assets/init/.qfai/contracts/db/db-0001-sample.sql +0 -7
  59. package/assets/init/.qfai/contracts/ui/assets/thema-001-facebook-like/assets.yaml +0 -6
  60. package/assets/init/.qfai/contracts/ui/assets/thema-001-facebook-like/palette.png +0 -0
  61. package/assets/init/.qfai/contracts/ui/assets/ui-0001-sample/assets.yaml +0 -6
  62. package/assets/init/.qfai/contracts/ui/assets/ui-0001-sample/snapshots/login__desktop__light__default.png +0 -0
  63. package/assets/init/.qfai/contracts/ui/thema-001-facebook-like.yml +0 -13
  64. package/assets/init/.qfai/contracts/ui/ui-0001-sample.yaml +0 -17
  65. package/assets/init/.qfai/out/README.md +0 -17
  66. package/assets/init/.qfai/promptpack/commands/implement.md +0 -8
  67. package/assets/init/.qfai/promptpack/commands/plan.md +0 -11
  68. package/assets/init/.qfai/promptpack/commands/release.md +0 -6
  69. package/assets/init/.qfai/promptpack/commands/review.md +0 -6
  70. package/assets/init/.qfai/promptpack/constitution.md +0 -15
  71. package/assets/init/.qfai/promptpack/roles/qa.md +0 -4
  72. package/assets/init/.qfai/promptpack/roles/spec.md +0 -4
  73. package/assets/init/.qfai/promptpack/roles/test.md +0 -4
  74. package/assets/init/.qfai/promptpack/steering/naming.md +0 -7
  75. package/assets/init/.qfai/promptpack/steering/traceability.md +0 -25
  76. package/assets/init/.qfai/prompts/README.md +0 -68
  77. package/assets/init/.qfai/prompts/analyze/README.md +0 -21
  78. package/assets/init/.qfai/prompts/analyze/scenario_test_consistency.md +0 -8
  79. package/assets/init/.qfai/prompts/analyze/scenario_to_test.md +0 -56
  80. package/assets/init/.qfai/prompts/analyze/spec_contract_consistency.md +0 -8
  81. package/assets/init/.qfai/prompts/analyze/spec_scenario_consistency.md +0 -8
  82. package/assets/init/.qfai/prompts/analyze/spec_to_contract.md +0 -54
  83. package/assets/init/.qfai/prompts/analyze/spec_to_scenario.md +0 -56
  84. package/assets/init/.qfai/prompts/makeBusinessFlow.md +0 -34
  85. package/assets/init/.qfai/prompts/makeOverview.md +0 -27
  86. package/assets/init/.qfai/prompts/qfai-generate-test-globs.md +0 -29
  87. package/assets/init/.qfai/prompts/qfai-maintain-contracts.md +0 -35
  88. package/assets/init/.qfai/prompts/qfai-maintain-traceability.md +0 -36
  89. package/assets/init/.qfai/prompts/require-to-spec.md +0 -40
  90. package/assets/init/.qfai/prompts.local/README.md +0 -31
  91. package/assets/init/.qfai/specs/spec-0001/delta.md +0 -25
  92. package/assets/init/.qfai/specs/spec-0001/scenario.feature +0 -11
  93. package/assets/init/.qfai/specs/spec-0001/spec.md +0 -40
  94. package/assets/init/root/tests/qfai-traceability.sample.test.ts +0 -2
@@ -1,229 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/cli/commands/analyze.ts
4
- import { readFile } from "fs/promises";
5
- import path2 from "path";
6
-
7
- // src/core/fs.ts
8
- import { access, readdir } from "fs/promises";
9
- import path from "path";
10
- import fg from "fast-glob";
11
- var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
12
- "node_modules",
13
- ".git",
14
- "dist",
15
- ".pnpm",
16
- "tmp",
17
- ".mcp-tools"
18
- ]);
19
- var DEFAULT_GLOB_FILE_LIMIT = 2e4;
20
- async function collectFiles(root, options = {}) {
21
- const entries = [];
22
- if (!await exists(root)) {
23
- return entries;
24
- }
25
- const ignoreDirs = /* @__PURE__ */ new Set([
26
- ...DEFAULT_IGNORE_DIRS,
27
- ...options.ignoreDirs ?? []
28
- ]);
29
- const extensions = options.extensions?.map((ext) => ext.toLowerCase()) ?? [];
30
- await walk(root, root, ignoreDirs, extensions, entries);
31
- return entries;
32
- }
33
- async function collectFilesByGlobs(root, options) {
34
- const limit = normalizeLimit(options.limit);
35
- if (options.globs.length === 0) {
36
- return { files: [], truncated: false, matchedFileCount: 0, limit };
37
- }
38
- const stream = fg.stream(options.globs, {
39
- cwd: root,
40
- ignore: options.ignore ?? [],
41
- onlyFiles: true,
42
- absolute: true,
43
- unique: true
44
- });
45
- const files = [];
46
- let truncated = false;
47
- for await (const entry of stream) {
48
- if (files.length >= limit) {
49
- truncated = true;
50
- destroyStream(stream);
51
- break;
52
- }
53
- files.push(String(entry));
54
- }
55
- const matchedFileCount = files.length;
56
- return { files, truncated, matchedFileCount, limit };
57
- }
58
- async function walk(base, current, ignoreDirs, extensions, out) {
59
- const items = await readdir(current, { withFileTypes: true });
60
- for (const item of items) {
61
- const fullPath = path.join(current, item.name);
62
- if (item.isDirectory()) {
63
- if (ignoreDirs.has(item.name)) {
64
- continue;
65
- }
66
- await walk(base, fullPath, ignoreDirs, extensions, out);
67
- continue;
68
- }
69
- if (item.isFile()) {
70
- if (extensions.length > 0) {
71
- const ext = path.extname(item.name).toLowerCase();
72
- if (!extensions.includes(ext)) {
73
- continue;
74
- }
75
- }
76
- out.push(fullPath);
77
- }
78
- }
79
- }
80
- async function exists(target) {
81
- try {
82
- await access(target);
83
- return true;
84
- } catch {
85
- return false;
86
- }
87
- }
88
- function normalizeLimit(value) {
89
- if (typeof value !== "number" || Number.isNaN(value)) {
90
- return DEFAULT_GLOB_FILE_LIMIT;
91
- }
92
- const flooredValue = Math.floor(value);
93
- if (flooredValue <= 0) {
94
- return DEFAULT_GLOB_FILE_LIMIT;
95
- }
96
- return flooredValue;
97
- }
98
- function destroyStream(stream) {
99
- if (!stream || typeof stream !== "object") {
100
- return;
101
- }
102
- const record2 = stream;
103
- if (typeof record2.destroy === "function") {
104
- record2.destroy();
105
- }
106
- }
107
-
108
- // src/cli/commands/analyze.ts
109
- async function runAnalyze(options) {
110
- const root = path2.resolve(options.root);
111
- const localDir = path2.join(root, ".qfai", "prompts.local", "analyze");
112
- const standardDir = path2.join(root, ".qfai", "prompts", "analyze");
113
- const available = await listPromptNames([localDir, standardDir]);
114
- const promptName = normalizePromptName(options.prompt);
115
- if (!promptName || options.list) {
116
- emitList(available);
117
- return 0;
118
- }
119
- const resolved = await resolvePromptPath(promptName, [localDir, standardDir]);
120
- if (!resolved) {
121
- emitPromptNotFound(promptName, available);
122
- return 1;
123
- }
124
- const content = await readFile(resolved, "utf-8");
125
- process.stdout.write(content);
126
- if (!content.endsWith("\n")) {
127
- process.stdout.write("\n");
128
- }
129
- return 0;
130
- }
131
- function normalizePromptName(value) {
132
- const trimmed = (value ?? "").trim();
133
- if (!trimmed) {
134
- return null;
135
- }
136
- return trimmed.endsWith(".md") ? trimmed.slice(0, -3) : trimmed;
137
- }
138
- async function listPromptNames(dirs) {
139
- const byName = /* @__PURE__ */ new Map();
140
- for (const dir of dirs) {
141
- const files = await collectFiles(dir, { extensions: [".md"] });
142
- for (const abs of files) {
143
- const base = path2.basename(abs);
144
- if (base.toLowerCase() === "readme.md") {
145
- continue;
146
- }
147
- const name = base.slice(0, -3);
148
- if (byName.has(name)) {
149
- continue;
150
- }
151
- if (await isDeprecatedPrompt(abs)) {
152
- continue;
153
- }
154
- byName.set(name, abs);
155
- }
156
- }
157
- return [...byName.keys()].sort((a, b) => a.localeCompare(b));
158
- }
159
- function emitList(names) {
160
- process.stdout.write("# qfai analyze: prompts\n\n");
161
- if (names.length === 0) {
162
- process.stdout.write(
163
- "\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"
164
- );
165
- return;
166
- }
167
- process.stdout.write("\u5229\u7528\u53EF\u80FD\u306A\u30D7\u30ED\u30F3\u30D7\u30C8\u4E00\u89A7:\n\n");
168
- for (const name of names) {
169
- process.stdout.write(`- ${name}
170
- `);
171
- }
172
- }
173
- async function resolvePromptPath(promptName, dirs) {
174
- const filename = `${promptName}.md`;
175
- for (const dir of dirs) {
176
- const full = path2.join(dir, filename);
177
- try {
178
- await readFile(full, "utf-8");
179
- return full;
180
- } catch {
181
- }
182
- }
183
- return null;
184
- }
185
- async function isDeprecatedPrompt(filePath) {
186
- try {
187
- const content = await readFile(filePath, "utf-8");
188
- const firstLine = firstLineOf(content);
189
- return firstLine.trim() === "# Deprecated";
190
- } catch {
191
- return false;
192
- }
193
- }
194
- function firstLineOf(content) {
195
- return content.match(/^[^\r\n]*/)?.[0] ?? "";
196
- }
197
- function emitPromptNotFound(promptName, candidates) {
198
- process.stderr.write(`qfai analyze: prompt not found: ${promptName}
199
- `);
200
- if (candidates.length > 0) {
201
- process.stderr.write("candidates:\n");
202
- for (const c of candidates) {
203
- process.stderr.write(`- ${c}
204
- `);
205
- }
206
- }
207
- }
208
-
209
3
  // src/cli/commands/doctor.ts
210
4
  import { mkdir, writeFile } from "fs/promises";
211
- import path12 from "path";
5
+ import path11 from "path";
212
6
 
213
7
  // src/core/doctor.ts
214
8
  import { access as access4 } from "fs/promises";
215
- import path11 from "path";
9
+ import path10 from "path";
216
10
 
217
11
  // src/core/config.ts
218
- import { access as access2, readFile as readFile2 } from "fs/promises";
219
- import path3 from "path";
12
+ import { access, readFile } from "fs/promises";
13
+ import path from "path";
220
14
  import { parse as parseYaml } from "yaml";
221
15
  var defaultConfig = {
222
16
  paths: {
223
17
  contractsDir: ".qfai/contracts",
224
18
  specsDir: ".qfai/specs",
225
- outDir: ".qfai/out",
226
- promptsDir: ".qfai/prompts",
19
+ outDir: ".qfai/report",
20
+ promptsDir: ".qfai/assistant/prompts",
227
21
  srcDir: "src",
228
22
  testsDir: "tests"
229
23
  },
@@ -251,21 +45,21 @@ var defaultConfig = {
251
45
  }
252
46
  },
253
47
  output: {
254
- validateJsonPath: ".qfai/out/validate.json"
48
+ validateJsonPath: ".qfai/report/validate.json"
255
49
  }
256
50
  };
257
51
  function getConfigPath(root) {
258
- return path3.join(root, "qfai.config.yaml");
52
+ return path.join(root, "qfai.config.yaml");
259
53
  }
260
54
  async function findConfigRoot(startDir) {
261
- const resolvedStart = path3.resolve(startDir);
55
+ const resolvedStart = path.resolve(startDir);
262
56
  let current = resolvedStart;
263
57
  while (true) {
264
58
  const configPath = getConfigPath(current);
265
- if (await exists2(configPath)) {
59
+ if (await exists(configPath)) {
266
60
  return { root: current, configPath, found: true };
267
61
  }
268
- const parent = path3.dirname(current);
62
+ const parent = path.dirname(current);
269
63
  if (parent === current) {
270
64
  break;
271
65
  }
@@ -282,7 +76,7 @@ async function loadConfig(root) {
282
76
  const issues = [];
283
77
  let parsed;
284
78
  try {
285
- const raw = await readFile2(configPath, "utf-8");
79
+ const raw = await readFile(configPath, "utf-8");
286
80
  parsed = parseYaml(raw);
287
81
  } catch (error2) {
288
82
  if (isMissingFile(error2)) {
@@ -295,7 +89,7 @@ async function loadConfig(root) {
295
89
  return { config: normalized, issues, configPath };
296
90
  }
297
91
  function resolvePath(root, config, key) {
298
- return path3.resolve(root, config.paths[key]);
92
+ return path.resolve(root, config.paths[key]);
299
93
  }
300
94
  function normalizeConfig(raw, configPath, issues) {
301
95
  if (!isRecord(raw)) {
@@ -588,9 +382,9 @@ function isMissingFile(error2) {
588
382
  }
589
383
  return false;
590
384
  }
591
- async function exists2(target) {
385
+ async function exists(target) {
592
386
  try {
593
- await access2(target);
387
+ await access(target);
594
388
  return true;
595
389
  } catch {
596
390
  return false;
@@ -608,26 +402,127 @@ function isRecord(value) {
608
402
 
609
403
  // src/core/discovery.ts
610
404
  import { access as access3 } from "fs/promises";
611
- import path5 from "path";
405
+ import path4 from "path";
406
+
407
+ // src/core/fs.ts
408
+ import { access as access2, readdir } from "fs/promises";
409
+ import path2 from "path";
410
+ import fg from "fast-glob";
411
+ var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
412
+ "node_modules",
413
+ ".git",
414
+ "dist",
415
+ ".pnpm",
416
+ "tmp",
417
+ ".mcp-tools"
418
+ ]);
419
+ var DEFAULT_GLOB_FILE_LIMIT = 2e4;
420
+ async function collectFiles(root, options = {}) {
421
+ const entries = [];
422
+ if (!await exists2(root)) {
423
+ return entries;
424
+ }
425
+ const ignoreDirs = /* @__PURE__ */ new Set([
426
+ ...DEFAULT_IGNORE_DIRS,
427
+ ...options.ignoreDirs ?? []
428
+ ]);
429
+ const extensions = options.extensions?.map((ext) => ext.toLowerCase()) ?? [];
430
+ await walk(root, root, ignoreDirs, extensions, entries);
431
+ return entries;
432
+ }
433
+ async function collectFilesByGlobs(root, options) {
434
+ const limit = normalizeLimit(options.limit);
435
+ if (options.globs.length === 0) {
436
+ return { files: [], truncated: false, matchedFileCount: 0, limit };
437
+ }
438
+ const stream = fg.stream(options.globs, {
439
+ cwd: root,
440
+ ignore: options.ignore ?? [],
441
+ onlyFiles: true,
442
+ absolute: true,
443
+ unique: true
444
+ });
445
+ const files = [];
446
+ let truncated = false;
447
+ for await (const entry of stream) {
448
+ if (files.length >= limit) {
449
+ truncated = true;
450
+ destroyStream(stream);
451
+ break;
452
+ }
453
+ files.push(String(entry));
454
+ }
455
+ const matchedFileCount = files.length;
456
+ return { files, truncated, matchedFileCount, limit };
457
+ }
458
+ async function walk(base, current, ignoreDirs, extensions, out) {
459
+ const items = await readdir(current, { withFileTypes: true });
460
+ for (const item of items) {
461
+ const fullPath = path2.join(current, item.name);
462
+ if (item.isDirectory()) {
463
+ if (ignoreDirs.has(item.name)) {
464
+ continue;
465
+ }
466
+ await walk(base, fullPath, ignoreDirs, extensions, out);
467
+ continue;
468
+ }
469
+ if (item.isFile()) {
470
+ if (extensions.length > 0) {
471
+ const ext = path2.extname(item.name).toLowerCase();
472
+ if (!extensions.includes(ext)) {
473
+ continue;
474
+ }
475
+ }
476
+ out.push(fullPath);
477
+ }
478
+ }
479
+ }
480
+ async function exists2(target) {
481
+ try {
482
+ await access2(target);
483
+ return true;
484
+ } catch {
485
+ return false;
486
+ }
487
+ }
488
+ function normalizeLimit(value) {
489
+ if (typeof value !== "number" || Number.isNaN(value)) {
490
+ return DEFAULT_GLOB_FILE_LIMIT;
491
+ }
492
+ const flooredValue = Math.floor(value);
493
+ if (flooredValue <= 0) {
494
+ return DEFAULT_GLOB_FILE_LIMIT;
495
+ }
496
+ return flooredValue;
497
+ }
498
+ function destroyStream(stream) {
499
+ if (!stream || typeof stream !== "object") {
500
+ return;
501
+ }
502
+ const record2 = stream;
503
+ if (typeof record2.destroy === "function") {
504
+ record2.destroy();
505
+ }
506
+ }
612
507
 
613
508
  // src/core/specLayout.ts
614
509
  import { readdir as readdir2 } from "fs/promises";
615
- import path4 from "path";
510
+ import path3 from "path";
616
511
  var SPEC_DIR_RE = /^spec-\d{4}$/;
617
512
  async function collectSpecEntries(specsRoot) {
618
513
  const dirs = await listSpecDirs(specsRoot);
619
514
  const entries = dirs.map((dir) => ({
620
515
  dir,
621
- specPath: path4.join(dir, "spec.md"),
622
- deltaPath: path4.join(dir, "delta.md"),
623
- scenarioPath: path4.join(dir, "scenario.feature")
516
+ specPath: path3.join(dir, "spec.md"),
517
+ deltaPath: path3.join(dir, "delta.md"),
518
+ scenarioPath: path3.join(dir, "scenario.feature")
624
519
  }));
625
520
  return entries.sort((a, b) => a.dir.localeCompare(b.dir));
626
521
  }
627
522
  async function listSpecDirs(specsRoot) {
628
523
  try {
629
524
  const items = await readdir2(specsRoot, { withFileTypes: true });
630
- return items.filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => SPEC_DIR_RE.test(name.toLowerCase())).map((name) => path4.join(specsRoot, name));
525
+ return items.filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => SPEC_DIR_RE.test(name.toLowerCase())).map((name) => path3.join(specsRoot, name));
631
526
  } catch (error2) {
632
527
  if (isMissingFileError(error2)) {
633
528
  return [];
@@ -698,20 +593,20 @@ async function exists3(target) {
698
593
  function filterByBasenamePrefix(files, prefix) {
699
594
  const lowerPrefix = prefix.toLowerCase();
700
595
  return files.filter(
701
- (file) => path5.basename(file).toLowerCase().startsWith(lowerPrefix)
596
+ (file) => path4.basename(file).toLowerCase().startsWith(lowerPrefix)
702
597
  );
703
598
  }
704
599
 
705
600
  // src/core/paths.ts
706
- import path6 from "path";
601
+ import path5 from "path";
707
602
  function toRelativePath(root, target) {
708
603
  if (!target) {
709
604
  return target;
710
605
  }
711
- if (!path6.isAbsolute(target)) {
606
+ if (!path5.isAbsolute(target)) {
712
607
  return toPosixPath(target);
713
608
  }
714
- const relative = path6.relative(root, target);
609
+ const relative = path5.relative(root, target);
715
610
  if (!relative) {
716
611
  return ".";
717
612
  }
@@ -722,8 +617,8 @@ function toPosixPath(value) {
722
617
  }
723
618
 
724
619
  // src/core/traceability.ts
725
- import { readFile as readFile3 } from "fs/promises";
726
- import path7 from "path";
620
+ import { readFile as readFile2 } from "fs/promises";
621
+ import path6 from "path";
727
622
 
728
623
  // src/core/gherkin/parse.ts
729
624
  import {
@@ -879,7 +774,7 @@ function extractAnnotatedScIds(text) {
879
774
  async function collectScIdsFromScenarioFiles(scenarioFiles) {
880
775
  const scIds = /* @__PURE__ */ new Set();
881
776
  for (const file of scenarioFiles) {
882
- const text = await readFile3(file, "utf-8");
777
+ const text = await readFile2(file, "utf-8");
883
778
  const { document, errors } = parseScenarioDocument(text, file);
884
779
  if (!document || errors.length > 0) {
885
780
  continue;
@@ -897,7 +792,7 @@ async function collectScIdsFromScenarioFiles(scenarioFiles) {
897
792
  async function collectScIdSourcesFromScenarioFiles(scenarioFiles) {
898
793
  const sources = /* @__PURE__ */ new Map();
899
794
  for (const file of scenarioFiles) {
900
- const text = await readFile3(file, "utf-8");
795
+ const text = await readFile2(file, "utf-8");
901
796
  const { document, errors } = parseScenarioDocument(text, file);
902
797
  if (!document || errors.length > 0) {
903
798
  continue;
@@ -955,10 +850,10 @@ async function collectScTestReferences(root, globs, excludeGlobs) {
955
850
  };
956
851
  }
957
852
  const normalizedFiles = Array.from(
958
- new Set(scanResult.files.map((file) => path7.normalize(file)))
853
+ new Set(scanResult.files.map((file) => path6.normalize(file)))
959
854
  );
960
855
  for (const file of normalizedFiles) {
961
- const text = await readFile3(file, "utf-8");
856
+ const text = await readFile2(file, "utf-8");
962
857
  const scIds = extractAnnotatedScIds(text);
963
858
  if (scIds.length === 0) {
964
859
  continue;
@@ -1017,20 +912,20 @@ function formatError3(error2) {
1017
912
  }
1018
913
 
1019
914
  // src/core/promptsIntegrity.ts
1020
- import { readFile as readFile4 } from "fs/promises";
1021
- import path9 from "path";
915
+ import { readFile as readFile3 } from "fs/promises";
916
+ import path8 from "path";
1022
917
 
1023
918
  // src/shared/assets.ts
1024
919
  import { existsSync } from "fs";
1025
- import path8 from "path";
920
+ import path7 from "path";
1026
921
  import { fileURLToPath } from "url";
1027
922
  function getInitAssetsDir() {
1028
923
  const base = import.meta.url;
1029
924
  const basePath = base.startsWith("file:") ? fileURLToPath(base) : base;
1030
- const baseDir = path8.dirname(basePath);
925
+ const baseDir = path7.dirname(basePath);
1031
926
  const candidates = [
1032
- path8.resolve(baseDir, "../../../assets/init"),
1033
- path8.resolve(baseDir, "../../assets/init")
927
+ path7.resolve(baseDir, "../../../assets/init"),
928
+ path7.resolve(baseDir, "../../assets/init")
1034
929
  ];
1035
930
  for (const candidate of candidates) {
1036
931
  if (existsSync(candidate)) {
@@ -1048,11 +943,24 @@ function getInitAssetsDir() {
1048
943
 
1049
944
  // src/core/promptsIntegrity.ts
1050
945
  var LEGACY_OK_EXTRA = /* @__PURE__ */ new Set(["qfai-classify-change.md"]);
1051
- async function diffProjectPromptsAgainstInitAssets(root) {
1052
- const promptsDir = path9.resolve(root, ".qfai", "prompts");
946
+ async function diffProjectPromptsAgainstInitAssets(root, config) {
947
+ const promptsDirConfig = config.paths.promptsDir;
948
+ const promptsDir = path8.isAbsolute(promptsDirConfig) ? promptsDirConfig : path8.resolve(root, promptsDirConfig);
1053
949
  let templateDir;
1054
950
  try {
1055
- templateDir = path9.join(getInitAssetsDir(), ".qfai", "prompts");
951
+ const rel = path8.isAbsolute(promptsDirConfig) ? path8.relative(root, promptsDirConfig) : promptsDirConfig;
952
+ const normalized = rel.replace(/^[\\/]+/, "");
953
+ if (normalized.length === 0 || normalized.startsWith("..")) {
954
+ return {
955
+ status: "skipped_missing_assets",
956
+ promptsDir,
957
+ templateDir: "",
958
+ missing: [],
959
+ extra: [],
960
+ changed: []
961
+ };
962
+ }
963
+ templateDir = path8.join(getInitAssetsDir(), normalized);
1056
964
  } catch {
1057
965
  return {
1058
966
  status: "skipped_missing_assets",
@@ -1106,8 +1014,8 @@ async function diffProjectPromptsAgainstInitAssets(root) {
1106
1014
  }
1107
1015
  try {
1108
1016
  const [a, b] = await Promise.all([
1109
- readFile4(templateAbs, "utf-8"),
1110
- readFile4(projectAbs, "utf-8")
1017
+ readFile3(templateAbs, "utf-8"),
1018
+ readFile3(projectAbs, "utf-8")
1111
1019
  ]);
1112
1020
  if (normalizeNewlines(a) !== normalizeNewlines(b)) {
1113
1021
  changed.push(rel);
@@ -1130,7 +1038,7 @@ function normalizeNewlines(text) {
1130
1038
  return text.replace(/\r\n/g, "\n");
1131
1039
  }
1132
1040
  function toRel(base, abs) {
1133
- const rel = path9.relative(base, abs);
1041
+ const rel = path8.relative(base, abs);
1134
1042
  return rel.replace(/[\\/]+/g, "/");
1135
1043
  }
1136
1044
  function intersectKeys(a, b) {
@@ -1144,16 +1052,16 @@ function intersectKeys(a, b) {
1144
1052
  }
1145
1053
 
1146
1054
  // src/core/version.ts
1147
- import { readFile as readFile5 } from "fs/promises";
1148
- import path10 from "path";
1055
+ import { readFile as readFile4 } from "fs/promises";
1056
+ import path9 from "path";
1149
1057
  import { fileURLToPath as fileURLToPath2 } from "url";
1150
1058
  async function resolveToolVersion() {
1151
- if ("1.0.4".length > 0) {
1152
- return "1.0.4";
1059
+ if ("1.0.6".length > 0) {
1060
+ return "1.0.6";
1153
1061
  }
1154
1062
  try {
1155
1063
  const packagePath = resolvePackageJsonPath();
1156
- const raw = await readFile5(packagePath, "utf-8");
1064
+ const raw = await readFile4(packagePath, "utf-8");
1157
1065
  const parsed = JSON.parse(raw);
1158
1066
  const version = typeof parsed.version === "string" ? parsed.version : "";
1159
1067
  return version.length > 0 ? version : "unknown";
@@ -1164,7 +1072,7 @@ async function resolveToolVersion() {
1164
1072
  function resolvePackageJsonPath() {
1165
1073
  const base = import.meta.url;
1166
1074
  const basePath = base.startsWith("file:") ? fileURLToPath2(base) : base;
1167
- return path10.resolve(path10.dirname(basePath), "../../package.json");
1075
+ return path9.resolve(path9.dirname(basePath), "../../package.json");
1168
1076
  }
1169
1077
 
1170
1078
  // src/core/doctor.ts
@@ -1190,7 +1098,7 @@ function normalizeGlobs2(values) {
1190
1098
  return values.map((glob) => glob.trim()).filter((glob) => glob.length > 0);
1191
1099
  }
1192
1100
  async function createDoctorData(options) {
1193
- const startDir = path11.resolve(options.startDir);
1101
+ const startDir = path10.resolve(options.startDir);
1194
1102
  const checks = [];
1195
1103
  const configPath = getConfigPath(startDir);
1196
1104
  const search = options.rootExplicit ? {
@@ -1252,9 +1160,9 @@ async function createDoctorData(options) {
1252
1160
  details: { path: toRelativePath(root, resolved) }
1253
1161
  });
1254
1162
  if (key === "promptsDir") {
1255
- const promptsLocalDir = path11.join(
1256
- path11.dirname(resolved),
1257
- `${path11.basename(resolved)}.local`
1163
+ const promptsLocalDir = path10.join(
1164
+ path10.dirname(resolved),
1165
+ `${path10.basename(resolved)}.local`
1258
1166
  );
1259
1167
  const found = await exists4(promptsLocalDir);
1260
1168
  addCheck(checks, {
@@ -1264,12 +1172,12 @@ async function createDoctorData(options) {
1264
1172
  message: found ? "prompts.local exists (overlay can be used)" : "prompts.local is optional (create it to override prompts)",
1265
1173
  details: { path: toRelativePath(root, promptsLocalDir) }
1266
1174
  });
1267
- const diff = await diffProjectPromptsAgainstInitAssets(root);
1175
+ const diff = await diffProjectPromptsAgainstInitAssets(root, config);
1268
1176
  if (diff.status === "skipped_missing_prompts") {
1269
1177
  addCheck(checks, {
1270
1178
  id: "prompts.integrity",
1271
1179
  severity: "info",
1272
- title: "Prompts integrity (.qfai/prompts)",
1180
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1273
1181
  message: "prompts \u304C\u672A\u4F5C\u6210\u306E\u305F\u3081\u691C\u67FB\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F\uFF08'qfai init' \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\uFF09",
1274
1182
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1275
1183
  });
@@ -1277,7 +1185,7 @@ async function createDoctorData(options) {
1277
1185
  addCheck(checks, {
1278
1186
  id: "prompts.integrity",
1279
1187
  severity: "info",
1280
- title: "Prompts integrity (.qfai/prompts)",
1188
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1281
1189
  message: "init assets \u304C\u898B\u3064\u304B\u3089\u306A\u3044\u305F\u3081\u691C\u67FB\u3092\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F\uFF08\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u72B6\u614B\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\uFF09",
1282
1190
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1283
1191
  });
@@ -1285,7 +1193,7 @@ async function createDoctorData(options) {
1285
1193
  addCheck(checks, {
1286
1194
  id: "prompts.integrity",
1287
1195
  severity: "ok",
1288
- title: "Prompts integrity (.qfai/prompts)",
1196
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1289
1197
  message: "\u6A19\u6E96 assets \u3068\u4E00\u81F4\u3057\u3066\u3044\u307E\u3059",
1290
1198
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1291
1199
  });
@@ -1293,15 +1201,15 @@ async function createDoctorData(options) {
1293
1201
  addCheck(checks, {
1294
1202
  id: "prompts.integrity",
1295
1203
  severity: "error",
1296
- title: "Prompts integrity (.qfai/prompts)",
1297
- message: "\u6A19\u6E96\u8CC7\u7523 '.qfai/prompts/**' \u304C\u6539\u5909\u3055\u308C\u3066\u3044\u307E\u3059\u3002prompts \u306E\u76F4\u7DE8\u96C6\u306F\u975E\u63A8\u5968\u3067\u3059\uFF08\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8/\u518D init \u3067\u4E0A\u66F8\u304D\u3055\u308C\u5F97\u307E\u3059\uFF09\u3002",
1204
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1205
+ message: "\u6A19\u6E96\u8CC7\u7523 '.qfai/assistant/prompts/**' \u304C\u6539\u5909\u3055\u308C\u3066\u3044\u307E\u3059\u3002prompts \u306E\u76F4\u7DE8\u96C6\u306F\u975E\u63A8\u5968\u3067\u3059\uFF08\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8/\u518D init \u3067\u4E0A\u66F8\u304D\u3055\u308C\u5F97\u307E\u3059\uFF09\u3002",
1298
1206
  details: {
1299
1207
  promptsDir: toRelativePath(root, diff.promptsDir),
1300
1208
  missing: diff.missing,
1301
1209
  extra: diff.extra,
1302
1210
  changed: diff.changed,
1303
1211
  nextActions: [
1304
- "\u5909\u66F4\u5185\u5BB9\u3092 .qfai/prompts.local/** \u306B\u79FB\u3059\uFF08\u540C\u4E00\u76F8\u5BFE\u30D1\u30B9\u3067\u914D\u7F6E\uFF09",
1212
+ "\u5909\u66F4\u5185\u5BB9\u3092 .qfai/assistant/prompts.local/** \u306B\u79FB\u3059\uFF08\u540C\u4E00\u76F8\u5BFE\u30D1\u30B9\u3067\u914D\u7F6E\uFF09",
1305
1213
  "\u5FC5\u8981\u306A\u3089 qfai init --force \u3067 prompts \u3092\u6A19\u6E96\u72B6\u614B\u3078\u623B\u3059\uFF08prompts.local \u306F\u4FDD\u8B77\u3055\u308C\u307E\u3059\uFF09"
1306
1214
  ]
1307
1215
  }
@@ -1327,7 +1235,7 @@ async function createDoctorData(options) {
1327
1235
  message: missingFiles === 0 ? `All spec packs have required files (count=${entries.length})` : `Missing required files in spec packs (missingFiles=${missingFiles})`,
1328
1236
  details: { specPacks: entries.length, missingFiles }
1329
1237
  });
1330
- const validateJsonAbs = path11.isAbsolute(config.output.validateJsonPath) ? config.output.validateJsonPath : path11.resolve(root, config.output.validateJsonPath);
1238
+ const validateJsonAbs = path10.isAbsolute(config.output.validateJsonPath) ? config.output.validateJsonPath : path10.resolve(root, config.output.validateJsonPath);
1331
1239
  const validateJsonExists = await exists4(validateJsonAbs);
1332
1240
  addCheck(checks, {
1333
1241
  id: "output.validateJson",
@@ -1337,8 +1245,8 @@ async function createDoctorData(options) {
1337
1245
  details: { path: toRelativePath(root, validateJsonAbs) }
1338
1246
  });
1339
1247
  const outDirAbs = resolvePath(root, config, "outDir");
1340
- const rel = path11.relative(outDirAbs, validateJsonAbs);
1341
- const inside = rel !== "" && !rel.startsWith("..") && !path11.isAbsolute(rel);
1248
+ const rel = path10.relative(outDirAbs, validateJsonAbs);
1249
+ const inside = rel !== "" && !rel.startsWith("..") && !path10.isAbsolute(rel);
1342
1250
  addCheck(checks, {
1343
1251
  id: "output.pathAlignment",
1344
1252
  severity: inside ? "ok" : "warning",
@@ -1461,12 +1369,12 @@ async function detectOutDirCollisions(root) {
1461
1369
  });
1462
1370
  const configPaths = configScan.files;
1463
1371
  const configRoots = Array.from(
1464
- new Set(configPaths.map((configPath) => path11.dirname(configPath)))
1372
+ new Set(configPaths.map((configPath) => path10.dirname(configPath)))
1465
1373
  ).sort((a, b) => a.localeCompare(b));
1466
1374
  const outDirToRoots = /* @__PURE__ */ new Map();
1467
1375
  for (const configRoot of configRoots) {
1468
1376
  const { config } = await loadConfig(configRoot);
1469
- const outDir = path11.normalize(resolvePath(configRoot, config, "outDir"));
1377
+ const outDir = path10.normalize(resolvePath(configRoot, config, "outDir"));
1470
1378
  const roots = outDirToRoots.get(outDir) ?? /* @__PURE__ */ new Set();
1471
1379
  roots.add(configRoot);
1472
1380
  outDirToRoots.set(outDir, roots);
@@ -1492,20 +1400,20 @@ async function detectOutDirCollisions(root) {
1492
1400
  };
1493
1401
  }
1494
1402
  async function findMonorepoRoot(startDir) {
1495
- let current = path11.resolve(startDir);
1403
+ let current = path10.resolve(startDir);
1496
1404
  while (true) {
1497
- const gitPath = path11.join(current, ".git");
1498
- const workspacePath = path11.join(current, "pnpm-workspace.yaml");
1405
+ const gitPath = path10.join(current, ".git");
1406
+ const workspacePath = path10.join(current, "pnpm-workspace.yaml");
1499
1407
  if (await exists4(gitPath) || await exists4(workspacePath)) {
1500
1408
  return current;
1501
1409
  }
1502
- const parent = path11.dirname(current);
1410
+ const parent = path10.dirname(current);
1503
1411
  if (parent === current) {
1504
1412
  break;
1505
1413
  }
1506
1414
  current = parent;
1507
1415
  }
1508
- return path11.resolve(startDir);
1416
+ return path10.resolve(startDir);
1509
1417
  }
1510
1418
 
1511
1419
  // src/cli/lib/logger.ts
@@ -1547,8 +1455,8 @@ async function runDoctor(options) {
1547
1455
  const output = options.format === "json" ? formatDoctorJson(data) : formatDoctorText(data);
1548
1456
  const exitCode = shouldFailDoctor(data.summary, options.failOn) ? 1 : 0;
1549
1457
  if (options.outPath) {
1550
- const outAbs = path12.isAbsolute(options.outPath) ? options.outPath : path12.resolve(process.cwd(), options.outPath);
1551
- await mkdir(path12.dirname(outAbs), { recursive: true });
1458
+ const outAbs = path11.isAbsolute(options.outPath) ? options.outPath : path11.resolve(process.cwd(), options.outPath);
1459
+ await mkdir(path11.dirname(outAbs), { recursive: true });
1552
1460
  await writeFile(outAbs, `${output}
1553
1461
  `, "utf-8");
1554
1462
  info(`doctor: wrote ${outAbs}`);
@@ -1568,11 +1476,11 @@ function shouldFailDoctor(summary, failOn) {
1568
1476
  }
1569
1477
 
1570
1478
  // src/cli/commands/init.ts
1571
- import path14 from "path";
1479
+ import path13 from "path";
1572
1480
 
1573
1481
  // src/cli/lib/fs.ts
1574
1482
  import { access as access5, copyFile, mkdir as mkdir2, readdir as readdir3 } from "fs/promises";
1575
- import path13 from "path";
1483
+ import path12 from "path";
1576
1484
  async function copyTemplateTree(sourceRoot, destRoot, options) {
1577
1485
  const files = await collectTemplateFiles(sourceRoot);
1578
1486
  return copyFiles(files, sourceRoot, destRoot, options);
@@ -1580,7 +1488,7 @@ async function copyTemplateTree(sourceRoot, destRoot, options) {
1580
1488
  async function copyTemplatePaths(sourceRoot, destRoot, relativePaths, options) {
1581
1489
  const allFiles = [];
1582
1490
  for (const relPath of relativePaths) {
1583
- const fullPath = path13.join(sourceRoot, relPath);
1491
+ const fullPath = path12.join(sourceRoot, relPath);
1584
1492
  const files = await collectTemplateFiles(fullPath);
1585
1493
  allFiles.push(...files);
1586
1494
  }
@@ -1590,13 +1498,13 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1590
1498
  const copied = [];
1591
1499
  const skipped = [];
1592
1500
  const conflicts = [];
1593
- const protectPrefixes = (options.protect ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + path13.sep);
1594
- const excludePrefixes = (options.exclude ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + path13.sep);
1501
+ const protectPrefixes = (options.protect ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + path12.sep);
1502
+ const excludePrefixes = (options.exclude ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + path12.sep);
1595
1503
  const isProtectedRelative = (relative) => {
1596
1504
  if (protectPrefixes.length === 0) {
1597
1505
  return false;
1598
1506
  }
1599
- const normalized = relative.replace(/[\\/]+/g, path13.sep);
1507
+ const normalized = relative.replace(/[\\/]+/g, path12.sep);
1600
1508
  return protectPrefixes.some(
1601
1509
  (prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
1602
1510
  );
@@ -1605,7 +1513,7 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1605
1513
  if (excludePrefixes.length === 0) {
1606
1514
  return false;
1607
1515
  }
1608
- const normalized = relative.replace(/[\\/]+/g, path13.sep);
1516
+ const normalized = relative.replace(/[\\/]+/g, path12.sep);
1609
1517
  return excludePrefixes.some(
1610
1518
  (prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
1611
1519
  );
@@ -1613,14 +1521,14 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1613
1521
  const conflictPolicy = options.conflictPolicy ?? "error";
1614
1522
  if (!options.force && conflictPolicy === "error") {
1615
1523
  for (const file of files) {
1616
- const relative = path13.relative(sourceRoot, file);
1524
+ const relative = path12.relative(sourceRoot, file);
1617
1525
  if (isExcludedRelative(relative)) {
1618
1526
  continue;
1619
1527
  }
1620
1528
  if (isProtectedRelative(relative)) {
1621
1529
  continue;
1622
1530
  }
1623
- const dest = path13.join(destRoot, relative);
1531
+ const dest = path12.join(destRoot, relative);
1624
1532
  if (!await shouldWrite(dest, options.force)) {
1625
1533
  conflicts.push(dest);
1626
1534
  }
@@ -1630,18 +1538,18 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1630
1538
  }
1631
1539
  }
1632
1540
  for (const file of files) {
1633
- const relative = path13.relative(sourceRoot, file);
1541
+ const relative = path12.relative(sourceRoot, file);
1634
1542
  if (isExcludedRelative(relative)) {
1635
1543
  continue;
1636
1544
  }
1637
- const dest = path13.join(destRoot, relative);
1545
+ const dest = path12.join(destRoot, relative);
1638
1546
  const forceForThisFile = isProtectedRelative(relative) ? false : options.force;
1639
1547
  if (!await shouldWrite(dest, forceForThisFile)) {
1640
1548
  skipped.push(dest);
1641
1549
  continue;
1642
1550
  }
1643
1551
  if (!options.dryRun) {
1644
- await mkdir2(path13.dirname(dest), { recursive: true });
1552
+ await mkdir2(path12.dirname(dest), { recursive: true });
1645
1553
  await copyFile(file, dest);
1646
1554
  }
1647
1555
  copied.push(dest);
@@ -1665,7 +1573,7 @@ async function collectTemplateFiles(root) {
1665
1573
  }
1666
1574
  const items = await readdir3(root, { withFileTypes: true });
1667
1575
  for (const item of items) {
1668
- const fullPath = path13.join(root, item.name);
1576
+ const fullPath = path12.join(root, item.name);
1669
1577
  if (item.isDirectory()) {
1670
1578
  const nested = await collectTemplateFiles(fullPath);
1671
1579
  entries.push(...nested);
@@ -1695,13 +1603,13 @@ async function exists5(target) {
1695
1603
  // src/cli/commands/init.ts
1696
1604
  async function runInit(options) {
1697
1605
  const assetsRoot = getInitAssetsDir();
1698
- const rootAssets = path14.join(assetsRoot, "root");
1699
- const qfaiAssets = path14.join(assetsRoot, ".qfai");
1700
- const destRoot = path14.resolve(options.dir);
1701
- const destQfai = path14.join(destRoot, ".qfai");
1606
+ const rootAssets = path13.join(assetsRoot, "root");
1607
+ const qfaiAssets = path13.join(assetsRoot, ".qfai");
1608
+ const destRoot = path13.resolve(options.dir);
1609
+ const destQfai = path13.join(destRoot, ".qfai");
1702
1610
  if (options.force) {
1703
1611
  info(
1704
- "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"
1612
+ "NOTE: --force \u306F .qfai/assistant/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"
1705
1613
  );
1706
1614
  }
1707
1615
  const rootResult = await copyTemplateTree(rootAssets, destRoot, {
@@ -1713,18 +1621,18 @@ async function runInit(options) {
1713
1621
  force: false,
1714
1622
  dryRun: options.dryRun,
1715
1623
  conflictPolicy: "skip",
1716
- protect: ["prompts.local"],
1717
- exclude: ["prompts"]
1624
+ protect: ["assistant/prompts.local"],
1625
+ exclude: ["assistant/prompts"]
1718
1626
  });
1719
1627
  const promptsResult = await copyTemplatePaths(
1720
1628
  qfaiAssets,
1721
1629
  destQfai,
1722
- ["prompts"],
1630
+ ["assistant/prompts"],
1723
1631
  {
1724
1632
  force: options.force,
1725
1633
  dryRun: options.dryRun,
1726
1634
  conflictPolicy: "skip",
1727
- protect: ["prompts.local"]
1635
+ protect: ["assistant/prompts.local"]
1728
1636
  }
1729
1637
  );
1730
1638
  report(
@@ -1745,8 +1653,8 @@ function report(copied, skipped, dryRun, label) {
1745
1653
  }
1746
1654
 
1747
1655
  // src/cli/commands/report.ts
1748
- import { mkdir as mkdir3, readFile as readFile14, writeFile as writeFile2 } from "fs/promises";
1749
- import path22 from "path";
1656
+ import { mkdir as mkdir3, readFile as readFile13, writeFile as writeFile2 } from "fs/promises";
1657
+ import path21 from "path";
1750
1658
 
1751
1659
  // src/core/normalize.ts
1752
1660
  function normalizeIssuePaths(root, issues) {
@@ -1786,12 +1694,12 @@ function normalizeValidationResult(root, result) {
1786
1694
  }
1787
1695
 
1788
1696
  // src/core/report.ts
1789
- import { readFile as readFile13 } from "fs/promises";
1790
- import path21 from "path";
1697
+ import { readFile as readFile12 } from "fs/promises";
1698
+ import path20 from "path";
1791
1699
 
1792
1700
  // src/core/contractIndex.ts
1793
- import { readFile as readFile6 } from "fs/promises";
1794
- import path15 from "path";
1701
+ import { readFile as readFile5 } from "fs/promises";
1702
+ import path14 from "path";
1795
1703
 
1796
1704
  // src/core/contractsDecl.ts
1797
1705
  var CONTRACT_DECLARATION_RE = /^\s*(?:#|\/\/|--|\/\*+|\*+)?\s*QFAI-CONTRACT-ID:\s*((?:API|UI|DB)-\d{4}|THEMA-\d{3})\s*(?:\*\/)?\s*$/gm;
@@ -1813,9 +1721,9 @@ function stripContractDeclarationLines(text) {
1813
1721
  // src/core/contractIndex.ts
1814
1722
  async function buildContractIndex(root, config) {
1815
1723
  const contractsRoot = resolvePath(root, config, "contractsDir");
1816
- const uiRoot = path15.join(contractsRoot, "ui");
1817
- const apiRoot = path15.join(contractsRoot, "api");
1818
- const dbRoot = path15.join(contractsRoot, "db");
1724
+ const uiRoot = path14.join(contractsRoot, "ui");
1725
+ const apiRoot = path14.join(contractsRoot, "api");
1726
+ const dbRoot = path14.join(contractsRoot, "db");
1819
1727
  const [uiFiles, themaFiles, apiFiles, dbFiles] = await Promise.all([
1820
1728
  collectUiContractFiles(uiRoot),
1821
1729
  collectThemaContractFiles(uiRoot),
@@ -1835,7 +1743,7 @@ async function buildContractIndex(root, config) {
1835
1743
  }
1836
1744
  async function indexContractFiles(files, index) {
1837
1745
  for (const file of files) {
1838
- const text = await readFile6(file, "utf-8");
1746
+ const text = await readFile5(file, "utf-8");
1839
1747
  extractDeclaredContractIds(text).forEach((id) => record(index, id, file));
1840
1748
  }
1841
1749
  }
@@ -2080,14 +1988,14 @@ function parseSpec(md, file) {
2080
1988
  }
2081
1989
 
2082
1990
  // src/core/validators/contracts.ts
2083
- import { access as access6, readFile as readFile7 } from "fs/promises";
2084
- import path17 from "path";
1991
+ import { access as access6, readFile as readFile6 } from "fs/promises";
1992
+ import path16 from "path";
2085
1993
 
2086
1994
  // src/core/contracts.ts
2087
- import path16 from "path";
1995
+ import path15 from "path";
2088
1996
  import { parse as parseYaml2 } from "yaml";
2089
1997
  function parseStructuredContract(file, text) {
2090
- const ext = path16.extname(file).toLowerCase();
1998
+ const ext = path15.extname(file).toLowerCase();
2091
1999
  if (ext === ".json") {
2092
2000
  return JSON.parse(text);
2093
2001
  }
@@ -2109,14 +2017,14 @@ async function validateContracts(root, config) {
2109
2017
  const issues = [];
2110
2018
  const contractIndex = await buildContractIndex(root, config);
2111
2019
  const contractsRoot = resolvePath(root, config, "contractsDir");
2112
- const uiRoot = path17.join(contractsRoot, "ui");
2020
+ const uiRoot = path16.join(contractsRoot, "ui");
2113
2021
  const themaIds = new Set(
2114
2022
  Array.from(contractIndex.ids).filter((id) => id.startsWith("THEMA-"))
2115
2023
  );
2116
2024
  issues.push(...await validateUiContracts(uiRoot, themaIds));
2117
2025
  issues.push(...await validateThemaContracts(uiRoot));
2118
- issues.push(...await validateApiContracts(path17.join(contractsRoot, "api")));
2119
- issues.push(...await validateDbContracts(path17.join(contractsRoot, "db")));
2026
+ issues.push(...await validateApiContracts(path16.join(contractsRoot, "api")));
2027
+ issues.push(...await validateDbContracts(path16.join(contractsRoot, "db")));
2120
2028
  issues.push(...validateDuplicateContractIds(contractIndex));
2121
2029
  return issues;
2122
2030
  }
@@ -2135,7 +2043,7 @@ async function validateUiContracts(uiRoot, themaIds) {
2135
2043
  }
2136
2044
  const issues = [];
2137
2045
  for (const file of files) {
2138
- const text = await readFile7(file, "utf-8");
2046
+ const text = await readFile6(file, "utf-8");
2139
2047
  const declaredIds = extractDeclaredContractIds(text);
2140
2048
  issues.push(...validateDeclaredContractIds(declaredIds, file, "UI"));
2141
2049
  let doc = null;
@@ -2189,7 +2097,7 @@ async function validateThemaContracts(uiRoot) {
2189
2097
  }
2190
2098
  const issues = [];
2191
2099
  for (const file of files) {
2192
- const text = await readFile7(file, "utf-8");
2100
+ const text = await readFile6(file, "utf-8");
2193
2101
  const invalidIds = extractInvalidIds(text, [
2194
2102
  "SPEC",
2195
2103
  "BR",
@@ -2323,7 +2231,7 @@ async function validateApiContracts(apiRoot) {
2323
2231
  }
2324
2232
  const issues = [];
2325
2233
  for (const file of files) {
2326
- const text = await readFile7(file, "utf-8");
2234
+ const text = await readFile6(file, "utf-8");
2327
2235
  const invalidIds = extractInvalidIds(text, [
2328
2236
  "SPEC",
2329
2237
  "BR",
@@ -2392,7 +2300,7 @@ async function validateDbContracts(dbRoot) {
2392
2300
  }
2393
2301
  const issues = [];
2394
2302
  for (const file of files) {
2395
- const text = await readFile7(file, "utf-8");
2303
+ const text = await readFile6(file, "utf-8");
2396
2304
  const invalidIds = extractInvalidIds(text, [
2397
2305
  "SPEC",
2398
2306
  "BR",
@@ -2589,9 +2497,9 @@ async function validateUiAssets(assets, file, uiRoot) {
2589
2497
  );
2590
2498
  return issues;
2591
2499
  }
2592
- const packDir = path17.resolve(uiRoot, packValue);
2593
- const packRelative = path17.relative(uiRoot, packDir);
2594
- if (packRelative.startsWith("..") || path17.isAbsolute(packRelative)) {
2500
+ const packDir = path16.resolve(uiRoot, packValue);
2501
+ const packRelative = path16.relative(uiRoot, packDir);
2502
+ if (packRelative.startsWith("..") || path16.isAbsolute(packRelative)) {
2595
2503
  issues.push(
2596
2504
  issue(
2597
2505
  "QFAI-ASSET-001",
@@ -2617,7 +2525,7 @@ async function validateUiAssets(assets, file, uiRoot) {
2617
2525
  );
2618
2526
  return issues;
2619
2527
  }
2620
- const assetsYamlPath = path17.join(packDir, "assets.yaml");
2528
+ const assetsYamlPath = path16.join(packDir, "assets.yaml");
2621
2529
  if (!await exists6(assetsYamlPath)) {
2622
2530
  issues.push(
2623
2531
  issue(
@@ -2632,7 +2540,7 @@ async function validateUiAssets(assets, file, uiRoot) {
2632
2540
  }
2633
2541
  let manifest;
2634
2542
  try {
2635
- const manifestText = await readFile7(assetsYamlPath, "utf-8");
2543
+ const manifestText = await readFile6(assetsYamlPath, "utf-8");
2636
2544
  manifest = parseStructuredContract(assetsYamlPath, manifestText);
2637
2545
  } catch (error2) {
2638
2546
  issues.push(
@@ -2705,9 +2613,9 @@ async function validateUiAssets(assets, file, uiRoot) {
2705
2613
  );
2706
2614
  continue;
2707
2615
  }
2708
- const assetPath = path17.resolve(packDir, entry.path);
2709
- const assetRelative = path17.relative(packDir, assetPath);
2710
- if (assetRelative.startsWith("..") || path17.isAbsolute(assetRelative)) {
2616
+ const assetPath = path16.resolve(packDir, entry.path);
2617
+ const assetRelative = path16.relative(packDir, assetPath);
2618
+ if (assetRelative.startsWith("..") || path16.isAbsolute(assetRelative)) {
2711
2619
  issues.push(
2712
2620
  issue(
2713
2621
  "QFAI-ASSET-004",
@@ -2748,7 +2656,7 @@ function shouldIgnoreInvalidId(value, doc) {
2748
2656
  return false;
2749
2657
  }
2750
2658
  const normalized = packValue.replace(/\\/g, "/");
2751
- const basename = path17.posix.basename(normalized);
2659
+ const basename = path16.posix.basename(normalized);
2752
2660
  if (!basename) {
2753
2661
  return false;
2754
2662
  }
@@ -2758,7 +2666,7 @@ function isSafeRelativePath(value) {
2758
2666
  if (!value) {
2759
2667
  return false;
2760
2668
  }
2761
- if (path17.isAbsolute(value)) {
2669
+ if (path16.isAbsolute(value)) {
2762
2670
  return false;
2763
2671
  }
2764
2672
  const normalized = value.replace(/\\/g, "/");
@@ -2808,8 +2716,8 @@ function issue(code, message, severity, file, rule, refs, category = "compatibil
2808
2716
  }
2809
2717
 
2810
2718
  // src/core/validators/delta.ts
2811
- import { readFile as readFile8 } from "fs/promises";
2812
- import path18 from "path";
2719
+ import { readFile as readFile7 } from "fs/promises";
2720
+ import path17 from "path";
2813
2721
  async function validateDeltas(root, config) {
2814
2722
  const specsRoot = resolvePath(root, config, "specsDir");
2815
2723
  const packs = await collectSpecPackDirs(specsRoot);
@@ -2818,9 +2726,9 @@ async function validateDeltas(root, config) {
2818
2726
  }
2819
2727
  const issues = [];
2820
2728
  for (const pack of packs) {
2821
- const deltaPath = path18.join(pack, "delta.md");
2729
+ const deltaPath = path17.join(pack, "delta.md");
2822
2730
  try {
2823
- await readFile8(deltaPath, "utf-8");
2731
+ await readFile7(deltaPath, "utf-8");
2824
2732
  } catch (error2) {
2825
2733
  if (isMissingFileError2(error2)) {
2826
2734
  issues.push(
@@ -2871,8 +2779,8 @@ function issue2(code, message, severity, file, rule, refs, category = "change",
2871
2779
  }
2872
2780
 
2873
2781
  // src/core/validators/ids.ts
2874
- import { readFile as readFile9 } from "fs/promises";
2875
- import path19 from "path";
2782
+ import { readFile as readFile8 } from "fs/promises";
2783
+ import path18 from "path";
2876
2784
  var SC_TAG_RE3 = /^SC-\d{4}$/;
2877
2785
  async function validateDefinedIds(root, config) {
2878
2786
  const issues = [];
@@ -2907,7 +2815,7 @@ async function validateDefinedIds(root, config) {
2907
2815
  }
2908
2816
  async function collectSpecDefinitionIds(files, out) {
2909
2817
  for (const file of files) {
2910
- const text = await readFile9(file, "utf-8");
2818
+ const text = await readFile8(file, "utf-8");
2911
2819
  const parsed = parseSpec(text, file);
2912
2820
  if (parsed.specId) {
2913
2821
  recordId(out, parsed.specId, file);
@@ -2917,7 +2825,7 @@ async function collectSpecDefinitionIds(files, out) {
2917
2825
  }
2918
2826
  async function collectScenarioDefinitionIds(files, out) {
2919
2827
  for (const file of files) {
2920
- const text = await readFile9(file, "utf-8");
2828
+ const text = await readFile8(file, "utf-8");
2921
2829
  const { document, errors } = parseScenarioDocument(text, file);
2922
2830
  if (!document || errors.length > 0) {
2923
2831
  continue;
@@ -2938,7 +2846,7 @@ function recordId(out, id, file) {
2938
2846
  }
2939
2847
  function formatFileList(files, root) {
2940
2848
  return files.map((file) => {
2941
- const relative = path19.relative(root, file);
2849
+ const relative = path18.relative(root, file);
2942
2850
  return relative.length > 0 ? relative : file;
2943
2851
  }).join(", ");
2944
2852
  }
@@ -2965,8 +2873,8 @@ function issue3(code, message, severity, file, rule, refs, category = "compatibi
2965
2873
  }
2966
2874
 
2967
2875
  // src/core/validators/promptsIntegrity.ts
2968
- async function validatePromptsIntegrity(root) {
2969
- const diff = await diffProjectPromptsAgainstInitAssets(root);
2876
+ async function validatePromptsIntegrity(root, config) {
2877
+ const diff = await diffProjectPromptsAgainstInitAssets(root, config);
2970
2878
  if (diff.status !== "modified") {
2971
2879
  return [];
2972
2880
  }
@@ -2983,11 +2891,11 @@ async function validatePromptsIntegrity(root) {
2983
2891
  code: "QFAI-PROMPTS-001",
2984
2892
  severity: "error",
2985
2893
  category: "change",
2986
- message: `\u6A19\u6E96\u8CC7\u7523 '.qfai/prompts/**' \u304C\u6539\u5909\u3055\u308C\u3066\u3044\u307E\u3059\uFF08${hints || `\u5DEE\u5206=${total}`}\uFF09\u3002${sampleText}`,
2894
+ message: `\u6A19\u6E96\u8CC7\u7523 '.qfai/assistant/prompts/**' \u304C\u6539\u5909\u3055\u308C\u3066\u3044\u307E\u3059\uFF08${hints || `\u5DEE\u5206=${total}`}\uFF09\u3002${sampleText}`,
2987
2895
  suggested_action: [
2988
2896
  "prompts \u306E\u76F4\u7DE8\u96C6\u306F\u975E\u63A8\u5968\u3067\u3059\uFF08\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8/\u518D init \u3067\u4E0A\u66F8\u304D\u3055\u308C\u5F97\u307E\u3059\uFF09\u3002",
2989
2897
  "\u6B21\u306E\u3044\u305A\u308C\u304B\u3092\u5B9F\u65BD\u3057\u3066\u304F\u3060\u3055\u3044:",
2990
- "- \u5909\u66F4\u3057\u305F\u3044\u5834\u5408: \u540C\u4E00\u76F8\u5BFE\u30D1\u30B9\u3067 '.qfai/prompts.local/**' \u306B\u7F6E\u3044\u3066 overlay",
2898
+ "- \u5909\u66F4\u3057\u305F\u3044\u5834\u5408: \u540C\u4E00\u76F8\u5BFE\u30D1\u30B9\u3067 '.qfai/assistant/prompts.local/**' \u306B\u7F6E\u3044\u3066 overlay",
2991
2899
  "- \u6A19\u6E96\u72B6\u614B\u3078\u623B\u3059\u5834\u5408: 'qfai init --force' \u3092\u5B9F\u884C\uFF08prompts \u306E\u307F\u4E0A\u66F8\u304D\u3001prompts.local \u306F\u4FDD\u8B77\uFF09"
2992
2900
  ].join("\n"),
2993
2901
  rule: "prompts.integrity"
@@ -2996,8 +2904,8 @@ async function validatePromptsIntegrity(root) {
2996
2904
  }
2997
2905
 
2998
2906
  // src/core/validators/scenario.ts
2999
- import { access as access7, readFile as readFile10 } from "fs/promises";
3000
- import path20 from "path";
2907
+ import { access as access7, readFile as readFile9 } from "fs/promises";
2908
+ import path19 from "path";
3001
2909
  var GIVEN_PATTERN = /\bGiven\b/;
3002
2910
  var WHEN_PATTERN = /\bWhen\b/;
3003
2911
  var THEN_PATTERN = /\bThen\b/;
@@ -3020,7 +2928,7 @@ async function validateScenarios(root, config) {
3020
2928
  }
3021
2929
  const issues = [];
3022
2930
  for (const entry of entries) {
3023
- const legacyScenarioPath = path20.join(entry.dir, "scenario.md");
2931
+ const legacyScenarioPath = path19.join(entry.dir, "scenario.md");
3024
2932
  if (await fileExists(legacyScenarioPath)) {
3025
2933
  issues.push(
3026
2934
  issue4(
@@ -3034,7 +2942,7 @@ async function validateScenarios(root, config) {
3034
2942
  }
3035
2943
  let text;
3036
2944
  try {
3037
- text = await readFile10(entry.scenarioPath, "utf-8");
2945
+ text = await readFile9(entry.scenarioPath, "utf-8");
3038
2946
  } catch (error2) {
3039
2947
  if (isMissingFileError3(error2)) {
3040
2948
  issues.push(
@@ -3217,7 +3125,7 @@ async function fileExists(target) {
3217
3125
  }
3218
3126
 
3219
3127
  // src/core/validators/spec.ts
3220
- import { readFile as readFile11 } from "fs/promises";
3128
+ import { readFile as readFile10 } from "fs/promises";
3221
3129
  async function validateSpecs(root, config) {
3222
3130
  const specsRoot = resolvePath(root, config, "specsDir");
3223
3131
  const entries = await collectSpecEntries(specsRoot);
@@ -3238,7 +3146,7 @@ async function validateSpecs(root, config) {
3238
3146
  for (const entry of entries) {
3239
3147
  let text;
3240
3148
  try {
3241
- text = await readFile11(entry.specPath, "utf-8");
3149
+ text = await readFile10(entry.specPath, "utf-8");
3242
3150
  } catch (error2) {
3243
3151
  if (isMissingFileError4(error2)) {
3244
3152
  issues.push(
@@ -3392,7 +3300,7 @@ function isMissingFileError4(error2) {
3392
3300
  }
3393
3301
 
3394
3302
  // src/core/validators/traceability.ts
3395
- import { readFile as readFile12 } from "fs/promises";
3303
+ import { readFile as readFile11 } from "fs/promises";
3396
3304
  var SPEC_TAG_RE3 = /^SPEC-\d{4}$/;
3397
3305
  var BR_TAG_RE2 = /^BR-\d{4}$/;
3398
3306
  async function validateTraceability(root, config) {
@@ -3412,7 +3320,7 @@ async function validateTraceability(root, config) {
3412
3320
  const contractIndex = await buildContractIndex(root, config);
3413
3321
  const contractIds = contractIndex.ids;
3414
3322
  for (const file of specFiles) {
3415
- const text = await readFile12(file, "utf-8");
3323
+ const text = await readFile11(file, "utf-8");
3416
3324
  extractAllIds(text).forEach((id) => upstreamIds.add(id));
3417
3325
  const parsed = parseSpec(text, file);
3418
3326
  if (parsed.specId) {
@@ -3485,7 +3393,7 @@ async function validateTraceability(root, config) {
3485
3393
  }
3486
3394
  }
3487
3395
  for (const file of scenarioFiles) {
3488
- const text = await readFile12(file, "utf-8");
3396
+ const text = await readFile11(file, "utf-8");
3489
3397
  extractAllIds(text).forEach((id) => upstreamIds.add(id));
3490
3398
  const scenarioContractRefs = parseContractRefs(text, {
3491
3399
  allowCommentPrefix: true
@@ -3807,7 +3715,7 @@ async function validateCodeReferences(upstreamIds, srcRoot, testsRoot) {
3807
3715
  const pattern = buildIdPattern(Array.from(upstreamIds));
3808
3716
  let found = false;
3809
3717
  for (const file of targetFiles) {
3810
- const text = await readFile12(file, "utf-8");
3718
+ const text = await readFile11(file, "utf-8");
3811
3719
  if (pattern.test(text)) {
3812
3720
  found = true;
3813
3721
  break;
@@ -3858,7 +3766,7 @@ async function validateProject(root, configResult) {
3858
3766
  const { config, issues: configIssues } = resolved;
3859
3767
  const issues = [
3860
3768
  ...configIssues,
3861
- ...await validatePromptsIntegrity(root),
3769
+ ...await validatePromptsIntegrity(root, config),
3862
3770
  ...await validateSpecs(root, config),
3863
3771
  ...await validateDeltas(root, config),
3864
3772
  ...await validateScenarios(root, config),
@@ -3907,15 +3815,15 @@ var ID_PREFIXES2 = [
3907
3815
  "THEMA"
3908
3816
  ];
3909
3817
  async function createReportData(root, validation, configResult) {
3910
- const resolvedRoot = path21.resolve(root);
3818
+ const resolvedRoot = path20.resolve(root);
3911
3819
  const resolved = configResult ?? await loadConfig(resolvedRoot);
3912
3820
  const config = resolved.config;
3913
3821
  const configPath = resolved.configPath;
3914
3822
  const specsRoot = resolvePath(resolvedRoot, config, "specsDir");
3915
3823
  const contractsRoot = resolvePath(resolvedRoot, config, "contractsDir");
3916
- const apiRoot = path21.join(contractsRoot, "api");
3917
- const uiRoot = path21.join(contractsRoot, "ui");
3918
- const dbRoot = path21.join(contractsRoot, "db");
3824
+ const apiRoot = path20.join(contractsRoot, "api");
3825
+ const uiRoot = path20.join(contractsRoot, "ui");
3826
+ const dbRoot = path20.join(contractsRoot, "db");
3919
3827
  const srcRoot = resolvePath(resolvedRoot, config, "srcDir");
3920
3828
  const testsRoot = resolvePath(resolvedRoot, config, "testsDir");
3921
3829
  const specFiles = await collectSpecFiles(specsRoot);
@@ -4381,7 +4289,9 @@ function formatReportMarkdown(data, options = {}) {
4381
4289
  );
4382
4290
  }
4383
4291
  lines.push("- \u5909\u66F4\u5185\u5BB9\u30FB\u53D7\u5165\u89B3\u70B9\u306F `.qfai/specs/*/delta.md` \u306B\u8A18\u9332\u3057\u307E\u3059\u3002");
4384
- lines.push("- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/promptpack/steering/traceability.md`");
4292
+ lines.push(
4293
+ "- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/assistant/instructions/constitution.md`"
4294
+ );
4385
4295
  return lines.join("\n");
4386
4296
  }
4387
4297
  function formatReportJson(data) {
@@ -4395,7 +4305,7 @@ async function collectSpecContractRefs(specFiles, contractIdList) {
4395
4305
  idToSpecs.set(contractId, /* @__PURE__ */ new Set());
4396
4306
  }
4397
4307
  for (const file of specFiles) {
4398
- const text = await readFile13(file, "utf-8");
4308
+ const text = await readFile12(file, "utf-8");
4399
4309
  const parsed = parseSpec(text, file);
4400
4310
  const specKey = parsed.specId;
4401
4311
  if (!specKey) {
@@ -4437,7 +4347,7 @@ async function collectIds(files) {
4437
4347
  THEMA: /* @__PURE__ */ new Set()
4438
4348
  };
4439
4349
  for (const file of files) {
4440
- const text = await readFile13(file, "utf-8");
4350
+ const text = await readFile12(file, "utf-8");
4441
4351
  for (const prefix of ID_PREFIXES2) {
4442
4352
  const ids = extractIds(text, prefix);
4443
4353
  ids.forEach((id) => result[prefix].add(id));
@@ -4456,7 +4366,7 @@ async function collectIds(files) {
4456
4366
  async function collectUpstreamIds(files) {
4457
4367
  const ids = /* @__PURE__ */ new Set();
4458
4368
  for (const file of files) {
4459
- const text = await readFile13(file, "utf-8");
4369
+ const text = await readFile12(file, "utf-8");
4460
4370
  extractAllIds(text).forEach((id) => ids.add(id));
4461
4371
  }
4462
4372
  return ids;
@@ -4477,7 +4387,7 @@ async function evaluateTraceability(upstreamIds, srcRoot, testsRoot) {
4477
4387
  }
4478
4388
  const pattern = buildIdPattern2(Array.from(upstreamIds));
4479
4389
  for (const file of targetFiles) {
4480
- const text = await readFile13(file, "utf-8");
4390
+ const text = await readFile12(file, "utf-8");
4481
4391
  if (pattern.test(text)) {
4482
4392
  return true;
4483
4393
  }
@@ -4614,7 +4524,7 @@ function warnIfTruncated(scan, context) {
4614
4524
 
4615
4525
  // src/cli/commands/report.ts
4616
4526
  async function runReport(options) {
4617
- const root = path22.resolve(options.root);
4527
+ const root = path21.resolve(options.root);
4618
4528
  const configResult = await loadConfig(root);
4619
4529
  let validation;
4620
4530
  if (options.runValidate) {
@@ -4631,7 +4541,7 @@ async function runReport(options) {
4631
4541
  validation = normalized;
4632
4542
  } else {
4633
4543
  const input = options.inputPath ?? configResult.config.output.validateJsonPath;
4634
- const inputPath = path22.isAbsolute(input) ? input : path22.resolve(root, input);
4544
+ const inputPath = path21.isAbsolute(input) ? input : path21.resolve(root, input);
4635
4545
  try {
4636
4546
  validation = await readValidationResult(inputPath);
4637
4547
  } catch (err) {
@@ -4642,7 +4552,7 @@ async function runReport(options) {
4642
4552
  "",
4643
4553
  "\u307E\u305A qfai validate \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B:",
4644
4554
  " qfai validate",
4645
- "\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u51FA\u529B\u5148: .qfai/out/validate.json\uFF09",
4555
+ "\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u51FA\u529B\u5148: .qfai/report/validate.json\uFF09",
4646
4556
  "",
4647
4557
  "\u307E\u305F\u306F report \u306B --run-validate \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
4648
4558
  "GitHub Actions \u30C6\u30F3\u30D7\u30EC\u3092\u4F7F\u3063\u3066\u3044\u308B\u5834\u5408\u306F\u3001workflow \u306E validate \u30B8\u30E7\u30D6\u3092\u5148\u306B\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
@@ -4658,10 +4568,10 @@ async function runReport(options) {
4658
4568
  warnIfTruncated(data.traceability.testFiles, "report");
4659
4569
  const output = options.format === "json" ? formatReportJson(data) : options.baseUrl ? formatReportMarkdown(data, { baseUrl: options.baseUrl }) : formatReportMarkdown(data);
4660
4570
  const outRoot = resolvePath(root, configResult.config, "outDir");
4661
- const defaultOut = options.format === "json" ? path22.join(outRoot, "report.json") : path22.join(outRoot, "report.md");
4571
+ const defaultOut = options.format === "json" ? path21.join(outRoot, "report.json") : path21.join(outRoot, "report.md");
4662
4572
  const out = options.outPath ?? defaultOut;
4663
- const outPath = path22.isAbsolute(out) ? out : path22.resolve(root, out);
4664
- await mkdir3(path22.dirname(outPath), { recursive: true });
4573
+ const outPath = path21.isAbsolute(out) ? out : path21.resolve(root, out);
4574
+ await mkdir3(path21.dirname(outPath), { recursive: true });
4665
4575
  await writeFile2(outPath, `${output}
4666
4576
  `, "utf-8");
4667
4577
  info(
@@ -4670,7 +4580,7 @@ async function runReport(options) {
4670
4580
  info(`wrote report: ${outPath}`);
4671
4581
  }
4672
4582
  async function readValidationResult(inputPath) {
4673
- const raw = await readFile14(inputPath, "utf-8");
4583
+ const raw = await readFile13(inputPath, "utf-8");
4674
4584
  const parsed = JSON.parse(raw);
4675
4585
  if (!isValidationResult(parsed)) {
4676
4586
  throw new Error(`validate.json \u306E\u5F62\u5F0F\u304C\u4E0D\u6B63\u3067\u3059: ${inputPath}`);
@@ -4726,15 +4636,15 @@ function isMissingFileError5(error2) {
4726
4636
  return record2.code === "ENOENT";
4727
4637
  }
4728
4638
  async function writeValidationResult(root, outputPath, result) {
4729
- const abs = path22.isAbsolute(outputPath) ? outputPath : path22.resolve(root, outputPath);
4730
- await mkdir3(path22.dirname(abs), { recursive: true });
4639
+ const abs = path21.isAbsolute(outputPath) ? outputPath : path21.resolve(root, outputPath);
4640
+ await mkdir3(path21.dirname(abs), { recursive: true });
4731
4641
  await writeFile2(abs, `${JSON.stringify(result, null, 2)}
4732
4642
  `, "utf-8");
4733
4643
  }
4734
4644
 
4735
4645
  // src/cli/commands/validate.ts
4736
4646
  import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
4737
- import path23 from "path";
4647
+ import path22 from "path";
4738
4648
 
4739
4649
  // src/cli/lib/failOn.ts
4740
4650
  function shouldFail(result, failOn) {
@@ -4749,7 +4659,7 @@ function shouldFail(result, failOn) {
4749
4659
 
4750
4660
  // src/cli/commands/validate.ts
4751
4661
  async function runValidate(options) {
4752
- const root = path23.resolve(options.root);
4662
+ const root = path22.resolve(options.root);
4753
4663
  const configResult = await loadConfig(root);
4754
4664
  const result = await validateProject(root, configResult);
4755
4665
  const normalized = normalizeValidationResult(root, result);
@@ -4874,12 +4784,12 @@ function issueKey(issue7) {
4874
4784
  }
4875
4785
  async function emitJson(result, root, jsonPath) {
4876
4786
  const abs = resolveJsonPath(root, jsonPath);
4877
- await mkdir4(path23.dirname(abs), { recursive: true });
4787
+ await mkdir4(path22.dirname(abs), { recursive: true });
4878
4788
  await writeFile3(abs, `${JSON.stringify(result, null, 2)}
4879
4789
  `, "utf-8");
4880
4790
  }
4881
4791
  function resolveJsonPath(root, jsonPath) {
4882
- return path23.isAbsolute(jsonPath) ? jsonPath : path23.resolve(root, jsonPath);
4792
+ return path22.isAbsolute(jsonPath) ? jsonPath : path22.resolve(root, jsonPath);
4883
4793
  }
4884
4794
  var GITHUB_ANNOTATION_LIMIT = 100;
4885
4795
 
@@ -4892,7 +4802,6 @@ function parseArgs(argv, cwd) {
4892
4802
  force: false,
4893
4803
  yes: false,
4894
4804
  dryRun: false,
4895
- analyzeList: false,
4896
4805
  reportFormat: "md",
4897
4806
  reportRunValidate: false,
4898
4807
  doctorFormat: "text",
@@ -4944,18 +4853,6 @@ function parseArgs(argv, cwd) {
4944
4853
  case "--dry-run":
4945
4854
  options.dryRun = true;
4946
4855
  break;
4947
- case "--list":
4948
- options.analyzeList = true;
4949
- break;
4950
- case "--prompt":
4951
- {
4952
- const next = readOptionValue(args, i);
4953
- if (next) {
4954
- options.analyzePrompt = next;
4955
- i += 1;
4956
- }
4957
- }
4958
- break;
4959
4856
  case "--format": {
4960
4857
  const next = readOptionValue(args, i);
4961
4858
  if (next === null) {
@@ -5089,17 +4986,6 @@ async function run(argv, cwd) {
5089
4986
  yes: options.yes
5090
4987
  });
5091
4988
  return;
5092
- case "analyze":
5093
- {
5094
- const resolvedRoot = await resolveRoot(options);
5095
- const exitCode = await runAnalyze({
5096
- root: resolvedRoot,
5097
- list: options.analyzeList,
5098
- ...options.analyzePrompt !== void 0 ? { prompt: options.analyzePrompt } : {}
5099
- });
5100
- process.exitCode = exitCode;
5101
- }
5102
- return;
5103
4989
  case "validate":
5104
4990
  {
5105
4991
  const resolvedRoot = await resolveRoot(options);
@@ -5147,7 +5033,6 @@ function usage() {
5147
5033
 
5148
5034
  Commands:
5149
5035
  init \u30C6\u30F3\u30D7\u30EC\u3092\u751F\u6210
5150
- analyze \u610F\u5473\u30EC\u30D9\u30EB\u306E\u30EC\u30D3\u30E5\u30FC\u88DC\u52A9\uFF08\u30D7\u30ED\u30F3\u30D7\u30C8\u51FA\u529B\uFF09
5151
5036
  validate \u4ED5\u69D8/\u5951\u7D04/\u53C2\u7167\u306E\u691C\u67FB
5152
5037
  report \u691C\u8A3C\u7D50\u679C\u3068\u96C6\u8A08\u3092\u51FA\u529B
5153
5038
  doctor \u8A2D\u5B9A/\u30D1\u30B9/\u51FA\u529B\u524D\u63D0\u306E\u8A3A\u65AD
@@ -5155,11 +5040,9 @@ Commands:
5155
5040
  Options:
5156
5041
  --root <path> \u5BFE\u8C61\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA
5157
5042
  --dir <path> init \u306E\u51FA\u529B\u5148
5158
- --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
5043
+ --force init: .qfai/assistant/prompts \u306E\u307F\u4E0A\u66F8\u304D\uFF08\u305D\u308C\u4EE5\u5916\u306F\u65E2\u5B58\u304C\u3042\u308C\u3070\u30B9\u30AD\u30C3\u30D7\uFF09
5159
5044
  --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
5160
5045
  --dry-run \u5909\u66F4\u3092\u884C\u308F\u305A\u8868\u793A\u306E\u307F
5161
- --list analyze: \u5229\u7528\u53EF\u80FD\u306A\u30D7\u30ED\u30F3\u30D7\u30C8\u4E00\u89A7\u3092\u8868\u793A
5162
- --prompt <name> analyze: \u6307\u5B9A\u30D7\u30ED\u30F3\u30D7\u30C8\uFF08.md\u7701\u7565\u53EF\uFF09\u3092\u51FA\u529B
5163
5046
  --format <text|github> validate \u306E\u51FA\u529B\u5F62\u5F0F
5164
5047
  --format <md|json> report \u306E\u51FA\u529B\u5F62\u5F0F
5165
5048
  --format <text|json> doctor \u306E\u51FA\u529B\u5F62\u5F0F