qfai 1.0.3 → 1.0.5

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 (99) hide show
  1. package/README.md +53 -74
  2. package/assets/init/.qfai/README.md +17 -82
  3. package/assets/init/.qfai/assistant/README.md +9 -0
  4. package/assets/init/.qfai/assistant/agents/README.md +34 -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 +6 -0
  18. package/assets/init/.qfai/assistant/instructions/constitution.md +131 -0
  19. package/assets/init/.qfai/assistant/instructions/workflow.md +75 -0
  20. package/assets/init/.qfai/assistant/prompts/README.md +19 -0
  21. package/assets/init/.qfai/assistant/prompts/qfai-discuss.md +173 -0
  22. package/assets/init/.qfai/assistant/prompts/qfai-implement.md +239 -0
  23. package/assets/init/.qfai/assistant/prompts/qfai-pr.md +218 -0
  24. package/assets/init/.qfai/assistant/prompts/qfai-require.md +273 -0
  25. package/assets/init/.qfai/assistant/prompts/qfai-scenario-test.md +229 -0
  26. package/assets/init/.qfai/assistant/prompts/qfai-spec.md +287 -0
  27. package/assets/init/.qfai/assistant/prompts/qfai-unit-test.md +202 -0
  28. package/assets/init/.qfai/assistant/prompts/qfai-verify.md +231 -0
  29. package/assets/init/.qfai/assistant/prompts.local/README.md +6 -0
  30. package/assets/init/.qfai/assistant/steering/README.md +33 -0
  31. package/assets/init/.qfai/assistant/steering/product.md +32 -0
  32. package/assets/init/.qfai/assistant/steering/structure.md +34 -0
  33. package/assets/init/.qfai/assistant/steering/tech.md +37 -0
  34. package/assets/init/.qfai/contracts/README.md +7 -87
  35. package/assets/init/.qfai/contracts/api/README.md +8 -0
  36. package/assets/init/.qfai/contracts/db/README.md +8 -0
  37. package/assets/init/.qfai/contracts/ui/README.md +8 -0
  38. package/assets/init/.qfai/report/README.md +13 -0
  39. package/assets/init/.qfai/require/README.md +4 -26
  40. package/assets/init/.qfai/require/require.md +74 -0
  41. package/assets/init/.qfai/specs/README.md +6 -57
  42. package/assets/init/root/.github/workflows/qfai.yml +1 -1
  43. package/assets/init/root/qfai.config.yaml +3 -4
  44. package/dist/cli/index.cjs +313 -472
  45. package/dist/cli/index.cjs.map +1 -1
  46. package/dist/cli/index.mjs +295 -454
  47. package/dist/cli/index.mjs.map +1 -1
  48. package/dist/index.cjs +37 -63
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +0 -1
  51. package/dist/index.d.ts +0 -1
  52. package/dist/index.mjs +37 -63
  53. package/dist/index.mjs.map +1 -1
  54. package/package.json +1 -1
  55. package/assets/init/.qfai/contracts/api/api-0001-sample.yaml +0 -15
  56. package/assets/init/.qfai/contracts/db/db-0001-sample.sql +0 -7
  57. package/assets/init/.qfai/contracts/ui/assets/thema-001-facebook-like/assets.yaml +0 -6
  58. package/assets/init/.qfai/contracts/ui/assets/thema-001-facebook-like/palette.png +0 -0
  59. package/assets/init/.qfai/contracts/ui/assets/ui-0001-sample/assets.yaml +0 -6
  60. package/assets/init/.qfai/contracts/ui/assets/ui-0001-sample/snapshots/login__desktop__light__default.png +0 -0
  61. package/assets/init/.qfai/contracts/ui/thema-001-facebook-like.yml +0 -13
  62. package/assets/init/.qfai/contracts/ui/ui-0001-sample.yaml +0 -17
  63. package/assets/init/.qfai/out/README.md +0 -17
  64. package/assets/init/.qfai/promptpack/commands/implement.md +0 -8
  65. package/assets/init/.qfai/promptpack/commands/plan.md +0 -11
  66. package/assets/init/.qfai/promptpack/commands/release.md +0 -6
  67. package/assets/init/.qfai/promptpack/commands/review.md +0 -7
  68. package/assets/init/.qfai/promptpack/constitution.md +0 -15
  69. package/assets/init/.qfai/promptpack/modes/change.md +0 -5
  70. package/assets/init/.qfai/promptpack/modes/compatibility.md +0 -6
  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/compatibility-vs-change.md +0 -42
  75. package/assets/init/.qfai/promptpack/steering/naming.md +0 -7
  76. package/assets/init/.qfai/promptpack/steering/traceability.md +0 -25
  77. package/assets/init/.qfai/prompts/README.md +0 -70
  78. package/assets/init/.qfai/prompts/analyze/README.md +0 -21
  79. package/assets/init/.qfai/prompts/analyze/scenario_test_consistency.md +0 -8
  80. package/assets/init/.qfai/prompts/analyze/scenario_to_test.md +0 -56
  81. package/assets/init/.qfai/prompts/analyze/spec_contract_consistency.md +0 -8
  82. package/assets/init/.qfai/prompts/analyze/spec_scenario_consistency.md +0 -8
  83. package/assets/init/.qfai/prompts/analyze/spec_to_contract.md +0 -54
  84. package/assets/init/.qfai/prompts/analyze/spec_to_scenario.md +0 -56
  85. package/assets/init/.qfai/prompts/makeBusinessFlow.md +0 -34
  86. package/assets/init/.qfai/prompts/makeOverview.md +0 -27
  87. package/assets/init/.qfai/prompts/qfai-classify-change.md +0 -33
  88. package/assets/init/.qfai/prompts/qfai-generate-test-globs.md +0 -29
  89. package/assets/init/.qfai/prompts/qfai-maintain-contracts.md +0 -35
  90. package/assets/init/.qfai/prompts/qfai-maintain-traceability.md +0 -36
  91. package/assets/init/.qfai/prompts/require-to-spec.md +0 -41
  92. package/assets/init/.qfai/prompts.local/README.md +0 -31
  93. package/assets/init/.qfai/rules/conventions.md +0 -27
  94. package/assets/init/.qfai/rules/pnpm.md +0 -29
  95. package/assets/init/.qfai/samples/analyze/analysis.md +0 -38
  96. package/assets/init/.qfai/samples/analyze/input_bundle.md +0 -54
  97. package/assets/init/.qfai/specs/spec-0001/delta.md +0 -30
  98. package/assets/init/.qfai/specs/spec-0001/scenario.feature +0 -11
  99. package/assets/init/.qfai/specs/spec-0001/spec.md +0 -40
@@ -1,230 +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
- rulesDir: ".qfai/rules",
226
- outDir: ".qfai/out",
227
- promptsDir: ".qfai/prompts",
19
+ outDir: ".qfai/report",
20
+ promptsDir: ".qfai/assistant/prompts",
228
21
  srcDir: "src",
229
22
  testsDir: "tests"
230
23
  },
@@ -252,21 +45,21 @@ var defaultConfig = {
252
45
  }
253
46
  },
254
47
  output: {
255
- validateJsonPath: ".qfai/out/validate.json"
48
+ validateJsonPath: ".qfai/report/validate.json"
256
49
  }
257
50
  };
258
51
  function getConfigPath(root) {
259
- return path3.join(root, "qfai.config.yaml");
52
+ return path.join(root, "qfai.config.yaml");
260
53
  }
261
54
  async function findConfigRoot(startDir) {
262
- const resolvedStart = path3.resolve(startDir);
55
+ const resolvedStart = path.resolve(startDir);
263
56
  let current = resolvedStart;
264
57
  while (true) {
265
58
  const configPath = getConfigPath(current);
266
- if (await exists2(configPath)) {
59
+ if (await exists(configPath)) {
267
60
  return { root: current, configPath, found: true };
268
61
  }
269
- const parent = path3.dirname(current);
62
+ const parent = path.dirname(current);
270
63
  if (parent === current) {
271
64
  break;
272
65
  }
@@ -283,7 +76,7 @@ async function loadConfig(root) {
283
76
  const issues = [];
284
77
  let parsed;
285
78
  try {
286
- const raw = await readFile2(configPath, "utf-8");
79
+ const raw = await readFile(configPath, "utf-8");
287
80
  parsed = parseYaml(raw);
288
81
  } catch (error2) {
289
82
  if (isMissingFile(error2)) {
@@ -296,7 +89,7 @@ async function loadConfig(root) {
296
89
  return { config: normalized, issues, configPath };
297
90
  }
298
91
  function resolvePath(root, config, key) {
299
- return path3.resolve(root, config.paths[key]);
92
+ return path.resolve(root, config.paths[key]);
300
93
  }
301
94
  function normalizeConfig(raw, configPath, issues) {
302
95
  if (!isRecord(raw)) {
@@ -335,13 +128,6 @@ function normalizePaths(raw, configPath, issues) {
335
128
  configPath,
336
129
  issues
337
130
  ),
338
- rulesDir: readString(
339
- raw.rulesDir,
340
- base.rulesDir,
341
- "paths.rulesDir",
342
- configPath,
343
- issues
344
- ),
345
131
  outDir: readString(
346
132
  raw.outDir,
347
133
  base.outDir,
@@ -596,9 +382,9 @@ function isMissingFile(error2) {
596
382
  }
597
383
  return false;
598
384
  }
599
- async function exists2(target) {
385
+ async function exists(target) {
600
386
  try {
601
- await access2(target);
387
+ await access(target);
602
388
  return true;
603
389
  } catch {
604
390
  return false;
@@ -616,26 +402,127 @@ function isRecord(value) {
616
402
 
617
403
  // src/core/discovery.ts
618
404
  import { access as access3 } from "fs/promises";
619
- 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
+ }
620
507
 
621
508
  // src/core/specLayout.ts
622
509
  import { readdir as readdir2 } from "fs/promises";
623
- import path4 from "path";
510
+ import path3 from "path";
624
511
  var SPEC_DIR_RE = /^spec-\d{4}$/;
625
512
  async function collectSpecEntries(specsRoot) {
626
513
  const dirs = await listSpecDirs(specsRoot);
627
514
  const entries = dirs.map((dir) => ({
628
515
  dir,
629
- specPath: path4.join(dir, "spec.md"),
630
- deltaPath: path4.join(dir, "delta.md"),
631
- 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")
632
519
  }));
633
520
  return entries.sort((a, b) => a.dir.localeCompare(b.dir));
634
521
  }
635
522
  async function listSpecDirs(specsRoot) {
636
523
  try {
637
524
  const items = await readdir2(specsRoot, { withFileTypes: true });
638
- 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));
639
526
  } catch (error2) {
640
527
  if (isMissingFileError(error2)) {
641
528
  return [];
@@ -706,20 +593,20 @@ async function exists3(target) {
706
593
  function filterByBasenamePrefix(files, prefix) {
707
594
  const lowerPrefix = prefix.toLowerCase();
708
595
  return files.filter(
709
- (file) => path5.basename(file).toLowerCase().startsWith(lowerPrefix)
596
+ (file) => path4.basename(file).toLowerCase().startsWith(lowerPrefix)
710
597
  );
711
598
  }
712
599
 
713
600
  // src/core/paths.ts
714
- import path6 from "path";
601
+ import path5 from "path";
715
602
  function toRelativePath(root, target) {
716
603
  if (!target) {
717
604
  return target;
718
605
  }
719
- if (!path6.isAbsolute(target)) {
606
+ if (!path5.isAbsolute(target)) {
720
607
  return toPosixPath(target);
721
608
  }
722
- const relative = path6.relative(root, target);
609
+ const relative = path5.relative(root, target);
723
610
  if (!relative) {
724
611
  return ".";
725
612
  }
@@ -730,8 +617,8 @@ function toPosixPath(value) {
730
617
  }
731
618
 
732
619
  // src/core/traceability.ts
733
- import { readFile as readFile3 } from "fs/promises";
734
- import path7 from "path";
620
+ import { readFile as readFile2 } from "fs/promises";
621
+ import path6 from "path";
735
622
 
736
623
  // src/core/gherkin/parse.ts
737
624
  import {
@@ -887,7 +774,7 @@ function extractAnnotatedScIds(text) {
887
774
  async function collectScIdsFromScenarioFiles(scenarioFiles) {
888
775
  const scIds = /* @__PURE__ */ new Set();
889
776
  for (const file of scenarioFiles) {
890
- const text = await readFile3(file, "utf-8");
777
+ const text = await readFile2(file, "utf-8");
891
778
  const { document, errors } = parseScenarioDocument(text, file);
892
779
  if (!document || errors.length > 0) {
893
780
  continue;
@@ -905,7 +792,7 @@ async function collectScIdsFromScenarioFiles(scenarioFiles) {
905
792
  async function collectScIdSourcesFromScenarioFiles(scenarioFiles) {
906
793
  const sources = /* @__PURE__ */ new Map();
907
794
  for (const file of scenarioFiles) {
908
- const text = await readFile3(file, "utf-8");
795
+ const text = await readFile2(file, "utf-8");
909
796
  const { document, errors } = parseScenarioDocument(text, file);
910
797
  if (!document || errors.length > 0) {
911
798
  continue;
@@ -963,10 +850,10 @@ async function collectScTestReferences(root, globs, excludeGlobs) {
963
850
  };
964
851
  }
965
852
  const normalizedFiles = Array.from(
966
- new Set(scanResult.files.map((file) => path7.normalize(file)))
853
+ new Set(scanResult.files.map((file) => path6.normalize(file)))
967
854
  );
968
855
  for (const file of normalizedFiles) {
969
- const text = await readFile3(file, "utf-8");
856
+ const text = await readFile2(file, "utf-8");
970
857
  const scIds = extractAnnotatedScIds(text);
971
858
  if (scIds.length === 0) {
972
859
  continue;
@@ -1025,20 +912,20 @@ function formatError3(error2) {
1025
912
  }
1026
913
 
1027
914
  // src/core/promptsIntegrity.ts
1028
- import { readFile as readFile4 } from "fs/promises";
1029
- import path9 from "path";
915
+ import { readFile as readFile3 } from "fs/promises";
916
+ import path8 from "path";
1030
917
 
1031
918
  // src/shared/assets.ts
1032
919
  import { existsSync } from "fs";
1033
- import path8 from "path";
920
+ import path7 from "path";
1034
921
  import { fileURLToPath } from "url";
1035
922
  function getInitAssetsDir() {
1036
923
  const base = import.meta.url;
1037
924
  const basePath = base.startsWith("file:") ? fileURLToPath(base) : base;
1038
- const baseDir = path8.dirname(basePath);
925
+ const baseDir = path7.dirname(basePath);
1039
926
  const candidates = [
1040
- path8.resolve(baseDir, "../../../assets/init"),
1041
- path8.resolve(baseDir, "../../assets/init")
927
+ path7.resolve(baseDir, "../../../assets/init"),
928
+ path7.resolve(baseDir, "../../assets/init")
1042
929
  ];
1043
930
  for (const candidate of candidates) {
1044
931
  if (existsSync(candidate)) {
@@ -1055,11 +942,25 @@ function getInitAssetsDir() {
1055
942
  }
1056
943
 
1057
944
  // src/core/promptsIntegrity.ts
1058
- async function diffProjectPromptsAgainstInitAssets(root) {
1059
- const promptsDir = path9.resolve(root, ".qfai", "prompts");
945
+ var LEGACY_OK_EXTRA = /* @__PURE__ */ new Set(["qfai-classify-change.md"]);
946
+ async function diffProjectPromptsAgainstInitAssets(root, config) {
947
+ const promptsDirConfig = config.paths.promptsDir;
948
+ const promptsDir = path8.isAbsolute(promptsDirConfig) ? promptsDirConfig : path8.resolve(root, promptsDirConfig);
1060
949
  let templateDir;
1061
950
  try {
1062
- 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);
1063
964
  } catch {
1064
965
  return {
1065
966
  status: "skipped_missing_assets",
@@ -1103,6 +1004,7 @@ async function diffProjectPromptsAgainstInitAssets(root) {
1103
1004
  extra.push(rel);
1104
1005
  }
1105
1006
  }
1007
+ const filteredExtra = extra.filter((rel) => !LEGACY_OK_EXTRA.has(rel));
1106
1008
  const common = intersectKeys(templateByRel, projectByRel);
1107
1009
  for (const rel of common) {
1108
1010
  const templateAbs = templateByRel.get(rel);
@@ -1112,8 +1014,8 @@ async function diffProjectPromptsAgainstInitAssets(root) {
1112
1014
  }
1113
1015
  try {
1114
1016
  const [a, b] = await Promise.all([
1115
- readFile4(templateAbs, "utf-8"),
1116
- readFile4(projectAbs, "utf-8")
1017
+ readFile3(templateAbs, "utf-8"),
1018
+ readFile3(projectAbs, "utf-8")
1117
1019
  ]);
1118
1020
  if (normalizeNewlines(a) !== normalizeNewlines(b)) {
1119
1021
  changed.push(rel);
@@ -1122,13 +1024,13 @@ async function diffProjectPromptsAgainstInitAssets(root) {
1122
1024
  changed.push(rel);
1123
1025
  }
1124
1026
  }
1125
- const status = missing.length > 0 || extra.length > 0 || changed.length > 0 ? "modified" : "ok";
1027
+ const status = missing.length > 0 || filteredExtra.length > 0 || changed.length > 0 ? "modified" : "ok";
1126
1028
  return {
1127
1029
  status,
1128
1030
  promptsDir,
1129
1031
  templateDir,
1130
1032
  missing: missing.sort(),
1131
- extra: extra.sort(),
1033
+ extra: filteredExtra.sort(),
1132
1034
  changed: changed.sort()
1133
1035
  };
1134
1036
  }
@@ -1136,7 +1038,7 @@ function normalizeNewlines(text) {
1136
1038
  return text.replace(/\r\n/g, "\n");
1137
1039
  }
1138
1040
  function toRel(base, abs) {
1139
- const rel = path9.relative(base, abs);
1041
+ const rel = path8.relative(base, abs);
1140
1042
  return rel.replace(/[\\/]+/g, "/");
1141
1043
  }
1142
1044
  function intersectKeys(a, b) {
@@ -1150,16 +1052,16 @@ function intersectKeys(a, b) {
1150
1052
  }
1151
1053
 
1152
1054
  // src/core/version.ts
1153
- import { readFile as readFile5 } from "fs/promises";
1154
- import path10 from "path";
1055
+ import { readFile as readFile4 } from "fs/promises";
1056
+ import path9 from "path";
1155
1057
  import { fileURLToPath as fileURLToPath2 } from "url";
1156
1058
  async function resolveToolVersion() {
1157
- if ("1.0.3".length > 0) {
1158
- return "1.0.3";
1059
+ if ("1.0.5".length > 0) {
1060
+ return "1.0.5";
1159
1061
  }
1160
1062
  try {
1161
1063
  const packagePath = resolvePackageJsonPath();
1162
- const raw = await readFile5(packagePath, "utf-8");
1064
+ const raw = await readFile4(packagePath, "utf-8");
1163
1065
  const parsed = JSON.parse(raw);
1164
1066
  const version = typeof parsed.version === "string" ? parsed.version : "";
1165
1067
  return version.length > 0 ? version : "unknown";
@@ -1170,7 +1072,7 @@ async function resolveToolVersion() {
1170
1072
  function resolvePackageJsonPath() {
1171
1073
  const base = import.meta.url;
1172
1074
  const basePath = base.startsWith("file:") ? fileURLToPath2(base) : base;
1173
- return path10.resolve(path10.dirname(basePath), "../../package.json");
1075
+ return path9.resolve(path9.dirname(basePath), "../../package.json");
1174
1076
  }
1175
1077
 
1176
1078
  // src/core/doctor.ts
@@ -1196,7 +1098,7 @@ function normalizeGlobs2(values) {
1196
1098
  return values.map((glob) => glob.trim()).filter((glob) => glob.length > 0);
1197
1099
  }
1198
1100
  async function createDoctorData(options) {
1199
- const startDir = path11.resolve(options.startDir);
1101
+ const startDir = path10.resolve(options.startDir);
1200
1102
  const checks = [];
1201
1103
  const configPath = getConfigPath(startDir);
1202
1104
  const search = options.rootExplicit ? {
@@ -1245,7 +1147,6 @@ async function createDoctorData(options) {
1245
1147
  "outDir",
1246
1148
  "srcDir",
1247
1149
  "testsDir",
1248
- "rulesDir",
1249
1150
  "promptsDir"
1250
1151
  ];
1251
1152
  for (const key of pathKeys) {
@@ -1259,9 +1160,9 @@ async function createDoctorData(options) {
1259
1160
  details: { path: toRelativePath(root, resolved) }
1260
1161
  });
1261
1162
  if (key === "promptsDir") {
1262
- const promptsLocalDir = path11.join(
1263
- path11.dirname(resolved),
1264
- `${path11.basename(resolved)}.local`
1163
+ const promptsLocalDir = path10.join(
1164
+ path10.dirname(resolved),
1165
+ `${path10.basename(resolved)}.local`
1265
1166
  );
1266
1167
  const found = await exists4(promptsLocalDir);
1267
1168
  addCheck(checks, {
@@ -1271,12 +1172,12 @@ async function createDoctorData(options) {
1271
1172
  message: found ? "prompts.local exists (overlay can be used)" : "prompts.local is optional (create it to override prompts)",
1272
1173
  details: { path: toRelativePath(root, promptsLocalDir) }
1273
1174
  });
1274
- const diff = await diffProjectPromptsAgainstInitAssets(root);
1175
+ const diff = await diffProjectPromptsAgainstInitAssets(root, config);
1275
1176
  if (diff.status === "skipped_missing_prompts") {
1276
1177
  addCheck(checks, {
1277
1178
  id: "prompts.integrity",
1278
1179
  severity: "info",
1279
- title: "Prompts integrity (.qfai/prompts)",
1180
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1280
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",
1281
1182
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1282
1183
  });
@@ -1284,7 +1185,7 @@ async function createDoctorData(options) {
1284
1185
  addCheck(checks, {
1285
1186
  id: "prompts.integrity",
1286
1187
  severity: "info",
1287
- title: "Prompts integrity (.qfai/prompts)",
1188
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1288
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",
1289
1190
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1290
1191
  });
@@ -1292,7 +1193,7 @@ async function createDoctorData(options) {
1292
1193
  addCheck(checks, {
1293
1194
  id: "prompts.integrity",
1294
1195
  severity: "ok",
1295
- title: "Prompts integrity (.qfai/prompts)",
1196
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1296
1197
  message: "\u6A19\u6E96 assets \u3068\u4E00\u81F4\u3057\u3066\u3044\u307E\u3059",
1297
1198
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1298
1199
  });
@@ -1300,15 +1201,15 @@ async function createDoctorData(options) {
1300
1201
  addCheck(checks, {
1301
1202
  id: "prompts.integrity",
1302
1203
  severity: "error",
1303
- title: "Prompts integrity (.qfai/prompts)",
1304
- 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",
1305
1206
  details: {
1306
1207
  promptsDir: toRelativePath(root, diff.promptsDir),
1307
1208
  missing: diff.missing,
1308
1209
  extra: diff.extra,
1309
1210
  changed: diff.changed,
1310
1211
  nextActions: [
1311
- "\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",
1312
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"
1313
1214
  ]
1314
1215
  }
@@ -1334,7 +1235,7 @@ async function createDoctorData(options) {
1334
1235
  message: missingFiles === 0 ? `All spec packs have required files (count=${entries.length})` : `Missing required files in spec packs (missingFiles=${missingFiles})`,
1335
1236
  details: { specPacks: entries.length, missingFiles }
1336
1237
  });
1337
- 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);
1338
1239
  const validateJsonExists = await exists4(validateJsonAbs);
1339
1240
  addCheck(checks, {
1340
1241
  id: "output.validateJson",
@@ -1344,8 +1245,8 @@ async function createDoctorData(options) {
1344
1245
  details: { path: toRelativePath(root, validateJsonAbs) }
1345
1246
  });
1346
1247
  const outDirAbs = resolvePath(root, config, "outDir");
1347
- const rel = path11.relative(outDirAbs, validateJsonAbs);
1348
- const inside = rel !== "" && !rel.startsWith("..") && !path11.isAbsolute(rel);
1248
+ const rel = path10.relative(outDirAbs, validateJsonAbs);
1249
+ const inside = rel !== "" && !rel.startsWith("..") && !path10.isAbsolute(rel);
1349
1250
  addCheck(checks, {
1350
1251
  id: "output.pathAlignment",
1351
1252
  severity: inside ? "ok" : "warning",
@@ -1468,12 +1369,12 @@ async function detectOutDirCollisions(root) {
1468
1369
  });
1469
1370
  const configPaths = configScan.files;
1470
1371
  const configRoots = Array.from(
1471
- new Set(configPaths.map((configPath) => path11.dirname(configPath)))
1372
+ new Set(configPaths.map((configPath) => path10.dirname(configPath)))
1472
1373
  ).sort((a, b) => a.localeCompare(b));
1473
1374
  const outDirToRoots = /* @__PURE__ */ new Map();
1474
1375
  for (const configRoot of configRoots) {
1475
1376
  const { config } = await loadConfig(configRoot);
1476
- const outDir = path11.normalize(resolvePath(configRoot, config, "outDir"));
1377
+ const outDir = path10.normalize(resolvePath(configRoot, config, "outDir"));
1477
1378
  const roots = outDirToRoots.get(outDir) ?? /* @__PURE__ */ new Set();
1478
1379
  roots.add(configRoot);
1479
1380
  outDirToRoots.set(outDir, roots);
@@ -1499,20 +1400,20 @@ async function detectOutDirCollisions(root) {
1499
1400
  };
1500
1401
  }
1501
1402
  async function findMonorepoRoot(startDir) {
1502
- let current = path11.resolve(startDir);
1403
+ let current = path10.resolve(startDir);
1503
1404
  while (true) {
1504
- const gitPath = path11.join(current, ".git");
1505
- const workspacePath = path11.join(current, "pnpm-workspace.yaml");
1405
+ const gitPath = path10.join(current, ".git");
1406
+ const workspacePath = path10.join(current, "pnpm-workspace.yaml");
1506
1407
  if (await exists4(gitPath) || await exists4(workspacePath)) {
1507
1408
  return current;
1508
1409
  }
1509
- const parent = path11.dirname(current);
1410
+ const parent = path10.dirname(current);
1510
1411
  if (parent === current) {
1511
1412
  break;
1512
1413
  }
1513
1414
  current = parent;
1514
1415
  }
1515
- return path11.resolve(startDir);
1416
+ return path10.resolve(startDir);
1516
1417
  }
1517
1418
 
1518
1419
  // src/cli/lib/logger.ts
@@ -1554,8 +1455,8 @@ async function runDoctor(options) {
1554
1455
  const output = options.format === "json" ? formatDoctorJson(data) : formatDoctorText(data);
1555
1456
  const exitCode = shouldFailDoctor(data.summary, options.failOn) ? 1 : 0;
1556
1457
  if (options.outPath) {
1557
- const outAbs = path12.isAbsolute(options.outPath) ? options.outPath : path12.resolve(process.cwd(), options.outPath);
1558
- 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 });
1559
1460
  await writeFile(outAbs, `${output}
1560
1461
  `, "utf-8");
1561
1462
  info(`doctor: wrote ${outAbs}`);
@@ -1575,11 +1476,11 @@ function shouldFailDoctor(summary, failOn) {
1575
1476
  }
1576
1477
 
1577
1478
  // src/cli/commands/init.ts
1578
- import path14 from "path";
1479
+ import path13 from "path";
1579
1480
 
1580
1481
  // src/cli/lib/fs.ts
1581
1482
  import { access as access5, copyFile, mkdir as mkdir2, readdir as readdir3 } from "fs/promises";
1582
- import path13 from "path";
1483
+ import path12 from "path";
1583
1484
  async function copyTemplateTree(sourceRoot, destRoot, options) {
1584
1485
  const files = await collectTemplateFiles(sourceRoot);
1585
1486
  return copyFiles(files, sourceRoot, destRoot, options);
@@ -1587,7 +1488,7 @@ async function copyTemplateTree(sourceRoot, destRoot, options) {
1587
1488
  async function copyTemplatePaths(sourceRoot, destRoot, relativePaths, options) {
1588
1489
  const allFiles = [];
1589
1490
  for (const relPath of relativePaths) {
1590
- const fullPath = path13.join(sourceRoot, relPath);
1491
+ const fullPath = path12.join(sourceRoot, relPath);
1591
1492
  const files = await collectTemplateFiles(fullPath);
1592
1493
  allFiles.push(...files);
1593
1494
  }
@@ -1597,13 +1498,13 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1597
1498
  const copied = [];
1598
1499
  const skipped = [];
1599
1500
  const conflicts = [];
1600
- const protectPrefixes = (options.protect ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + path13.sep);
1601
- 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);
1602
1503
  const isProtectedRelative = (relative) => {
1603
1504
  if (protectPrefixes.length === 0) {
1604
1505
  return false;
1605
1506
  }
1606
- const normalized = relative.replace(/[\\/]+/g, path13.sep);
1507
+ const normalized = relative.replace(/[\\/]+/g, path12.sep);
1607
1508
  return protectPrefixes.some(
1608
1509
  (prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
1609
1510
  );
@@ -1612,7 +1513,7 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1612
1513
  if (excludePrefixes.length === 0) {
1613
1514
  return false;
1614
1515
  }
1615
- const normalized = relative.replace(/[\\/]+/g, path13.sep);
1516
+ const normalized = relative.replace(/[\\/]+/g, path12.sep);
1616
1517
  return excludePrefixes.some(
1617
1518
  (prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
1618
1519
  );
@@ -1620,14 +1521,14 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1620
1521
  const conflictPolicy = options.conflictPolicy ?? "error";
1621
1522
  if (!options.force && conflictPolicy === "error") {
1622
1523
  for (const file of files) {
1623
- const relative = path13.relative(sourceRoot, file);
1524
+ const relative = path12.relative(sourceRoot, file);
1624
1525
  if (isExcludedRelative(relative)) {
1625
1526
  continue;
1626
1527
  }
1627
1528
  if (isProtectedRelative(relative)) {
1628
1529
  continue;
1629
1530
  }
1630
- const dest = path13.join(destRoot, relative);
1531
+ const dest = path12.join(destRoot, relative);
1631
1532
  if (!await shouldWrite(dest, options.force)) {
1632
1533
  conflicts.push(dest);
1633
1534
  }
@@ -1637,18 +1538,18 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1637
1538
  }
1638
1539
  }
1639
1540
  for (const file of files) {
1640
- const relative = path13.relative(sourceRoot, file);
1541
+ const relative = path12.relative(sourceRoot, file);
1641
1542
  if (isExcludedRelative(relative)) {
1642
1543
  continue;
1643
1544
  }
1644
- const dest = path13.join(destRoot, relative);
1545
+ const dest = path12.join(destRoot, relative);
1645
1546
  const forceForThisFile = isProtectedRelative(relative) ? false : options.force;
1646
1547
  if (!await shouldWrite(dest, forceForThisFile)) {
1647
1548
  skipped.push(dest);
1648
1549
  continue;
1649
1550
  }
1650
1551
  if (!options.dryRun) {
1651
- await mkdir2(path13.dirname(dest), { recursive: true });
1552
+ await mkdir2(path12.dirname(dest), { recursive: true });
1652
1553
  await copyFile(file, dest);
1653
1554
  }
1654
1555
  copied.push(dest);
@@ -1672,7 +1573,7 @@ async function collectTemplateFiles(root) {
1672
1573
  }
1673
1574
  const items = await readdir3(root, { withFileTypes: true });
1674
1575
  for (const item of items) {
1675
- const fullPath = path13.join(root, item.name);
1576
+ const fullPath = path12.join(root, item.name);
1676
1577
  if (item.isDirectory()) {
1677
1578
  const nested = await collectTemplateFiles(fullPath);
1678
1579
  entries.push(...nested);
@@ -1702,13 +1603,13 @@ async function exists5(target) {
1702
1603
  // src/cli/commands/init.ts
1703
1604
  async function runInit(options) {
1704
1605
  const assetsRoot = getInitAssetsDir();
1705
- const rootAssets = path14.join(assetsRoot, "root");
1706
- const qfaiAssets = path14.join(assetsRoot, ".qfai");
1707
- const destRoot = path14.resolve(options.dir);
1708
- 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");
1709
1610
  if (options.force) {
1710
1611
  info(
1711
- "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"
1712
1613
  );
1713
1614
  }
1714
1615
  const rootResult = await copyTemplateTree(rootAssets, destRoot, {
@@ -1720,18 +1621,18 @@ async function runInit(options) {
1720
1621
  force: false,
1721
1622
  dryRun: options.dryRun,
1722
1623
  conflictPolicy: "skip",
1723
- protect: ["prompts.local"],
1724
- exclude: ["prompts"]
1624
+ protect: ["assistant/prompts.local"],
1625
+ exclude: ["assistant/prompts"]
1725
1626
  });
1726
1627
  const promptsResult = await copyTemplatePaths(
1727
1628
  qfaiAssets,
1728
1629
  destQfai,
1729
- ["prompts"],
1630
+ ["assistant/prompts"],
1730
1631
  {
1731
1632
  force: options.force,
1732
1633
  dryRun: options.dryRun,
1733
1634
  conflictPolicy: "skip",
1734
- protect: ["prompts.local"]
1635
+ protect: ["assistant/prompts.local"]
1735
1636
  }
1736
1637
  );
1737
1638
  report(
@@ -1752,8 +1653,8 @@ function report(copied, skipped, dryRun, label) {
1752
1653
  }
1753
1654
 
1754
1655
  // src/cli/commands/report.ts
1755
- import { mkdir as mkdir3, readFile as readFile14, writeFile as writeFile2 } from "fs/promises";
1756
- import path22 from "path";
1656
+ import { mkdir as mkdir3, readFile as readFile13, writeFile as writeFile2 } from "fs/promises";
1657
+ import path21 from "path";
1757
1658
 
1758
1659
  // src/core/normalize.ts
1759
1660
  function normalizeIssuePaths(root, issues) {
@@ -1793,12 +1694,12 @@ function normalizeValidationResult(root, result) {
1793
1694
  }
1794
1695
 
1795
1696
  // src/core/report.ts
1796
- import { readFile as readFile13 } from "fs/promises";
1797
- import path21 from "path";
1697
+ import { readFile as readFile12 } from "fs/promises";
1698
+ import path20 from "path";
1798
1699
 
1799
1700
  // src/core/contractIndex.ts
1800
- import { readFile as readFile6 } from "fs/promises";
1801
- import path15 from "path";
1701
+ import { readFile as readFile5 } from "fs/promises";
1702
+ import path14 from "path";
1802
1703
 
1803
1704
  // src/core/contractsDecl.ts
1804
1705
  var CONTRACT_DECLARATION_RE = /^\s*(?:#|\/\/|--|\/\*+|\*+)?\s*QFAI-CONTRACT-ID:\s*((?:API|UI|DB)-\d{4}|THEMA-\d{3})\s*(?:\*\/)?\s*$/gm;
@@ -1820,9 +1721,9 @@ function stripContractDeclarationLines(text) {
1820
1721
  // src/core/contractIndex.ts
1821
1722
  async function buildContractIndex(root, config) {
1822
1723
  const contractsRoot = resolvePath(root, config, "contractsDir");
1823
- const uiRoot = path15.join(contractsRoot, "ui");
1824
- const apiRoot = path15.join(contractsRoot, "api");
1825
- 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");
1826
1727
  const [uiFiles, themaFiles, apiFiles, dbFiles] = await Promise.all([
1827
1728
  collectUiContractFiles(uiRoot),
1828
1729
  collectThemaContractFiles(uiRoot),
@@ -1842,7 +1743,7 @@ async function buildContractIndex(root, config) {
1842
1743
  }
1843
1744
  async function indexContractFiles(files, index) {
1844
1745
  for (const file of files) {
1845
- const text = await readFile6(file, "utf-8");
1746
+ const text = await readFile5(file, "utf-8");
1846
1747
  extractDeclaredContractIds(text).forEach((id) => record(index, id, file));
1847
1748
  }
1848
1749
  }
@@ -2087,14 +1988,14 @@ function parseSpec(md, file) {
2087
1988
  }
2088
1989
 
2089
1990
  // src/core/validators/contracts.ts
2090
- import { access as access6, readFile as readFile7 } from "fs/promises";
2091
- import path17 from "path";
1991
+ import { access as access6, readFile as readFile6 } from "fs/promises";
1992
+ import path16 from "path";
2092
1993
 
2093
1994
  // src/core/contracts.ts
2094
- import path16 from "path";
1995
+ import path15 from "path";
2095
1996
  import { parse as parseYaml2 } from "yaml";
2096
1997
  function parseStructuredContract(file, text) {
2097
- const ext = path16.extname(file).toLowerCase();
1998
+ const ext = path15.extname(file).toLowerCase();
2098
1999
  if (ext === ".json") {
2099
2000
  return JSON.parse(text);
2100
2001
  }
@@ -2116,14 +2017,14 @@ async function validateContracts(root, config) {
2116
2017
  const issues = [];
2117
2018
  const contractIndex = await buildContractIndex(root, config);
2118
2019
  const contractsRoot = resolvePath(root, config, "contractsDir");
2119
- const uiRoot = path17.join(contractsRoot, "ui");
2020
+ const uiRoot = path16.join(contractsRoot, "ui");
2120
2021
  const themaIds = new Set(
2121
2022
  Array.from(contractIndex.ids).filter((id) => id.startsWith("THEMA-"))
2122
2023
  );
2123
2024
  issues.push(...await validateUiContracts(uiRoot, themaIds));
2124
2025
  issues.push(...await validateThemaContracts(uiRoot));
2125
- issues.push(...await validateApiContracts(path17.join(contractsRoot, "api")));
2126
- 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")));
2127
2028
  issues.push(...validateDuplicateContractIds(contractIndex));
2128
2029
  return issues;
2129
2030
  }
@@ -2142,7 +2043,7 @@ async function validateUiContracts(uiRoot, themaIds) {
2142
2043
  }
2143
2044
  const issues = [];
2144
2045
  for (const file of files) {
2145
- const text = await readFile7(file, "utf-8");
2046
+ const text = await readFile6(file, "utf-8");
2146
2047
  const declaredIds = extractDeclaredContractIds(text);
2147
2048
  issues.push(...validateDeclaredContractIds(declaredIds, file, "UI"));
2148
2049
  let doc = null;
@@ -2196,7 +2097,7 @@ async function validateThemaContracts(uiRoot) {
2196
2097
  }
2197
2098
  const issues = [];
2198
2099
  for (const file of files) {
2199
- const text = await readFile7(file, "utf-8");
2100
+ const text = await readFile6(file, "utf-8");
2200
2101
  const invalidIds = extractInvalidIds(text, [
2201
2102
  "SPEC",
2202
2103
  "BR",
@@ -2330,7 +2231,7 @@ async function validateApiContracts(apiRoot) {
2330
2231
  }
2331
2232
  const issues = [];
2332
2233
  for (const file of files) {
2333
- const text = await readFile7(file, "utf-8");
2234
+ const text = await readFile6(file, "utf-8");
2334
2235
  const invalidIds = extractInvalidIds(text, [
2335
2236
  "SPEC",
2336
2237
  "BR",
@@ -2399,7 +2300,7 @@ async function validateDbContracts(dbRoot) {
2399
2300
  }
2400
2301
  const issues = [];
2401
2302
  for (const file of files) {
2402
- const text = await readFile7(file, "utf-8");
2303
+ const text = await readFile6(file, "utf-8");
2403
2304
  const invalidIds = extractInvalidIds(text, [
2404
2305
  "SPEC",
2405
2306
  "BR",
@@ -2596,9 +2497,9 @@ async function validateUiAssets(assets, file, uiRoot) {
2596
2497
  );
2597
2498
  return issues;
2598
2499
  }
2599
- const packDir = path17.resolve(uiRoot, packValue);
2600
- const packRelative = path17.relative(uiRoot, packDir);
2601
- 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)) {
2602
2503
  issues.push(
2603
2504
  issue(
2604
2505
  "QFAI-ASSET-001",
@@ -2624,7 +2525,7 @@ async function validateUiAssets(assets, file, uiRoot) {
2624
2525
  );
2625
2526
  return issues;
2626
2527
  }
2627
- const assetsYamlPath = path17.join(packDir, "assets.yaml");
2528
+ const assetsYamlPath = path16.join(packDir, "assets.yaml");
2628
2529
  if (!await exists6(assetsYamlPath)) {
2629
2530
  issues.push(
2630
2531
  issue(
@@ -2639,7 +2540,7 @@ async function validateUiAssets(assets, file, uiRoot) {
2639
2540
  }
2640
2541
  let manifest;
2641
2542
  try {
2642
- const manifestText = await readFile7(assetsYamlPath, "utf-8");
2543
+ const manifestText = await readFile6(assetsYamlPath, "utf-8");
2643
2544
  manifest = parseStructuredContract(assetsYamlPath, manifestText);
2644
2545
  } catch (error2) {
2645
2546
  issues.push(
@@ -2712,9 +2613,9 @@ async function validateUiAssets(assets, file, uiRoot) {
2712
2613
  );
2713
2614
  continue;
2714
2615
  }
2715
- const assetPath = path17.resolve(packDir, entry.path);
2716
- const assetRelative = path17.relative(packDir, assetPath);
2717
- 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)) {
2718
2619
  issues.push(
2719
2620
  issue(
2720
2621
  "QFAI-ASSET-004",
@@ -2755,7 +2656,7 @@ function shouldIgnoreInvalidId(value, doc) {
2755
2656
  return false;
2756
2657
  }
2757
2658
  const normalized = packValue.replace(/\\/g, "/");
2758
- const basename = path17.posix.basename(normalized);
2659
+ const basename = path16.posix.basename(normalized);
2759
2660
  if (!basename) {
2760
2661
  return false;
2761
2662
  }
@@ -2765,7 +2666,7 @@ function isSafeRelativePath(value) {
2765
2666
  if (!value) {
2766
2667
  return false;
2767
2668
  }
2768
- if (path17.isAbsolute(value)) {
2669
+ if (path16.isAbsolute(value)) {
2769
2670
  return false;
2770
2671
  }
2771
2672
  const normalized = value.replace(/\\/g, "/");
@@ -2815,13 +2716,8 @@ function issue(code, message, severity, file, rule, refs, category = "compatibil
2815
2716
  }
2816
2717
 
2817
2718
  // src/core/validators/delta.ts
2818
- import { readFile as readFile8 } from "fs/promises";
2819
- import path18 from "path";
2820
- var SECTION_RE = /^##\s+変更区分/m;
2821
- var COMPAT_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Compatibility\b/m;
2822
- var CHANGE_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Change\/Improvement\b/m;
2823
- var COMPAT_CHECKED_RE = /^\s*-\s*\[[xX]\]\s*Compatibility\b/m;
2824
- var CHANGE_CHECKED_RE = /^\s*-\s*\[[xX]\]\s*Change\/Improvement\b/m;
2719
+ import { readFile as readFile7 } from "fs/promises";
2720
+ import path17 from "path";
2825
2721
  async function validateDeltas(root, config) {
2826
2722
  const specsRoot = resolvePath(root, config, "specsDir");
2827
2723
  const packs = await collectSpecPackDirs(specsRoot);
@@ -2830,10 +2726,9 @@ async function validateDeltas(root, config) {
2830
2726
  }
2831
2727
  const issues = [];
2832
2728
  for (const pack of packs) {
2833
- const deltaPath = path18.join(pack, "delta.md");
2834
- let text;
2729
+ const deltaPath = path17.join(pack, "delta.md");
2835
2730
  try {
2836
- text = await readFile8(deltaPath, "utf-8");
2731
+ await readFile7(deltaPath, "utf-8");
2837
2732
  } catch (error2) {
2838
2733
  if (isMissingFileError2(error2)) {
2839
2734
  issues.push(
@@ -2842,41 +2737,16 @@ async function validateDeltas(root, config) {
2842
2737
  "delta.md \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
2843
2738
  "error",
2844
2739
  deltaPath,
2845
- "delta.exists"
2740
+ "delta.exists",
2741
+ void 0,
2742
+ "change",
2743
+ "spec-xxxx/delta.md \u3092\u4F5C\u6210\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u30C6\u30F3\u30D7\u30EC\u306F init \u751F\u6210\u7269\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\uFF09\u3002"
2846
2744
  )
2847
2745
  );
2848
2746
  continue;
2849
2747
  }
2850
2748
  throw error2;
2851
2749
  }
2852
- const hasSection = SECTION_RE.test(text);
2853
- const hasCompatibility = COMPAT_LINE_RE.test(text);
2854
- const hasChange = CHANGE_LINE_RE.test(text);
2855
- if (!hasSection || !hasCompatibility || !hasChange) {
2856
- issues.push(
2857
- issue2(
2858
- "QFAI-DELTA-002",
2859
- "delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002`## \u5909\u66F4\u533A\u5206` \u3068\u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9\uFF08Compatibility / Change/Improvement\uFF09\u3092\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2860
- "error",
2861
- deltaPath,
2862
- "delta.section"
2863
- )
2864
- );
2865
- continue;
2866
- }
2867
- const compatibilityChecked = COMPAT_CHECKED_RE.test(text);
2868
- const changeChecked = CHANGE_CHECKED_RE.test(text);
2869
- if (compatibilityChecked === changeChecked) {
2870
- issues.push(
2871
- issue2(
2872
- "QFAI-DELTA-003",
2873
- "delta.md \u306E\u5909\u66F4\u533A\u5206\u306F\u3069\u3061\u3089\u304B1\u3064\u3060\u3051\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4E21\u65B9ON/\u4E21\u65B9OFF\u306F\u7121\u52B9\u3067\u3059\uFF09\u3002",
2874
- "error",
2875
- deltaPath,
2876
- "delta.classification"
2877
- )
2878
- );
2879
- }
2880
2750
  }
2881
2751
  return issues;
2882
2752
  }
@@ -2909,8 +2779,8 @@ function issue2(code, message, severity, file, rule, refs, category = "change",
2909
2779
  }
2910
2780
 
2911
2781
  // src/core/validators/ids.ts
2912
- import { readFile as readFile9 } from "fs/promises";
2913
- import path19 from "path";
2782
+ import { readFile as readFile8 } from "fs/promises";
2783
+ import path18 from "path";
2914
2784
  var SC_TAG_RE3 = /^SC-\d{4}$/;
2915
2785
  async function validateDefinedIds(root, config) {
2916
2786
  const issues = [];
@@ -2945,7 +2815,7 @@ async function validateDefinedIds(root, config) {
2945
2815
  }
2946
2816
  async function collectSpecDefinitionIds(files, out) {
2947
2817
  for (const file of files) {
2948
- const text = await readFile9(file, "utf-8");
2818
+ const text = await readFile8(file, "utf-8");
2949
2819
  const parsed = parseSpec(text, file);
2950
2820
  if (parsed.specId) {
2951
2821
  recordId(out, parsed.specId, file);
@@ -2955,7 +2825,7 @@ async function collectSpecDefinitionIds(files, out) {
2955
2825
  }
2956
2826
  async function collectScenarioDefinitionIds(files, out) {
2957
2827
  for (const file of files) {
2958
- const text = await readFile9(file, "utf-8");
2828
+ const text = await readFile8(file, "utf-8");
2959
2829
  const { document, errors } = parseScenarioDocument(text, file);
2960
2830
  if (!document || errors.length > 0) {
2961
2831
  continue;
@@ -2976,7 +2846,7 @@ function recordId(out, id, file) {
2976
2846
  }
2977
2847
  function formatFileList(files, root) {
2978
2848
  return files.map((file) => {
2979
- const relative = path19.relative(root, file);
2849
+ const relative = path18.relative(root, file);
2980
2850
  return relative.length > 0 ? relative : file;
2981
2851
  }).join(", ");
2982
2852
  }
@@ -3003,8 +2873,8 @@ function issue3(code, message, severity, file, rule, refs, category = "compatibi
3003
2873
  }
3004
2874
 
3005
2875
  // src/core/validators/promptsIntegrity.ts
3006
- async function validatePromptsIntegrity(root) {
3007
- const diff = await diffProjectPromptsAgainstInitAssets(root);
2876
+ async function validatePromptsIntegrity(root, config) {
2877
+ const diff = await diffProjectPromptsAgainstInitAssets(root, config);
3008
2878
  if (diff.status !== "modified") {
3009
2879
  return [];
3010
2880
  }
@@ -3021,11 +2891,11 @@ async function validatePromptsIntegrity(root) {
3021
2891
  code: "QFAI-PROMPTS-001",
3022
2892
  severity: "error",
3023
2893
  category: "change",
3024
- 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}`,
3025
2895
  suggested_action: [
3026
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",
3027
2897
  "\u6B21\u306E\u3044\u305A\u308C\u304B\u3092\u5B9F\u65BD\u3057\u3066\u304F\u3060\u3055\u3044:",
3028
- "- \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",
3029
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"
3030
2900
  ].join("\n"),
3031
2901
  rule: "prompts.integrity"
@@ -3034,8 +2904,8 @@ async function validatePromptsIntegrity(root) {
3034
2904
  }
3035
2905
 
3036
2906
  // src/core/validators/scenario.ts
3037
- import { access as access7, readFile as readFile10 } from "fs/promises";
3038
- import path20 from "path";
2907
+ import { access as access7, readFile as readFile9 } from "fs/promises";
2908
+ import path19 from "path";
3039
2909
  var GIVEN_PATTERN = /\bGiven\b/;
3040
2910
  var WHEN_PATTERN = /\bWhen\b/;
3041
2911
  var THEN_PATTERN = /\bThen\b/;
@@ -3058,7 +2928,7 @@ async function validateScenarios(root, config) {
3058
2928
  }
3059
2929
  const issues = [];
3060
2930
  for (const entry of entries) {
3061
- const legacyScenarioPath = path20.join(entry.dir, "scenario.md");
2931
+ const legacyScenarioPath = path19.join(entry.dir, "scenario.md");
3062
2932
  if (await fileExists(legacyScenarioPath)) {
3063
2933
  issues.push(
3064
2934
  issue4(
@@ -3072,7 +2942,7 @@ async function validateScenarios(root, config) {
3072
2942
  }
3073
2943
  let text;
3074
2944
  try {
3075
- text = await readFile10(entry.scenarioPath, "utf-8");
2945
+ text = await readFile9(entry.scenarioPath, "utf-8");
3076
2946
  } catch (error2) {
3077
2947
  if (isMissingFileError3(error2)) {
3078
2948
  issues.push(
@@ -3255,7 +3125,7 @@ async function fileExists(target) {
3255
3125
  }
3256
3126
 
3257
3127
  // src/core/validators/spec.ts
3258
- import { readFile as readFile11 } from "fs/promises";
3128
+ import { readFile as readFile10 } from "fs/promises";
3259
3129
  async function validateSpecs(root, config) {
3260
3130
  const specsRoot = resolvePath(root, config, "specsDir");
3261
3131
  const entries = await collectSpecEntries(specsRoot);
@@ -3276,7 +3146,7 @@ async function validateSpecs(root, config) {
3276
3146
  for (const entry of entries) {
3277
3147
  let text;
3278
3148
  try {
3279
- text = await readFile11(entry.specPath, "utf-8");
3149
+ text = await readFile10(entry.specPath, "utf-8");
3280
3150
  } catch (error2) {
3281
3151
  if (isMissingFileError4(error2)) {
3282
3152
  issues.push(
@@ -3430,7 +3300,7 @@ function isMissingFileError4(error2) {
3430
3300
  }
3431
3301
 
3432
3302
  // src/core/validators/traceability.ts
3433
- import { readFile as readFile12 } from "fs/promises";
3303
+ import { readFile as readFile11 } from "fs/promises";
3434
3304
  var SPEC_TAG_RE3 = /^SPEC-\d{4}$/;
3435
3305
  var BR_TAG_RE2 = /^BR-\d{4}$/;
3436
3306
  async function validateTraceability(root, config) {
@@ -3450,7 +3320,7 @@ async function validateTraceability(root, config) {
3450
3320
  const contractIndex = await buildContractIndex(root, config);
3451
3321
  const contractIds = contractIndex.ids;
3452
3322
  for (const file of specFiles) {
3453
- const text = await readFile12(file, "utf-8");
3323
+ const text = await readFile11(file, "utf-8");
3454
3324
  extractAllIds(text).forEach((id) => upstreamIds.add(id));
3455
3325
  const parsed = parseSpec(text, file);
3456
3326
  if (parsed.specId) {
@@ -3523,7 +3393,7 @@ async function validateTraceability(root, config) {
3523
3393
  }
3524
3394
  }
3525
3395
  for (const file of scenarioFiles) {
3526
- const text = await readFile12(file, "utf-8");
3396
+ const text = await readFile11(file, "utf-8");
3527
3397
  extractAllIds(text).forEach((id) => upstreamIds.add(id));
3528
3398
  const scenarioContractRefs = parseContractRefs(text, {
3529
3399
  allowCommentPrefix: true
@@ -3845,7 +3715,7 @@ async function validateCodeReferences(upstreamIds, srcRoot, testsRoot) {
3845
3715
  const pattern = buildIdPattern(Array.from(upstreamIds));
3846
3716
  let found = false;
3847
3717
  for (const file of targetFiles) {
3848
- const text = await readFile12(file, "utf-8");
3718
+ const text = await readFile11(file, "utf-8");
3849
3719
  if (pattern.test(text)) {
3850
3720
  found = true;
3851
3721
  break;
@@ -3896,7 +3766,7 @@ async function validateProject(root, configResult) {
3896
3766
  const { config, issues: configIssues } = resolved;
3897
3767
  const issues = [
3898
3768
  ...configIssues,
3899
- ...await validatePromptsIntegrity(root),
3769
+ ...await validatePromptsIntegrity(root, config),
3900
3770
  ...await validateSpecs(root, config),
3901
3771
  ...await validateDeltas(root, config),
3902
3772
  ...await validateScenarios(root, config),
@@ -3945,15 +3815,15 @@ var ID_PREFIXES2 = [
3945
3815
  "THEMA"
3946
3816
  ];
3947
3817
  async function createReportData(root, validation, configResult) {
3948
- const resolvedRoot = path21.resolve(root);
3818
+ const resolvedRoot = path20.resolve(root);
3949
3819
  const resolved = configResult ?? await loadConfig(resolvedRoot);
3950
3820
  const config = resolved.config;
3951
3821
  const configPath = resolved.configPath;
3952
3822
  const specsRoot = resolvePath(resolvedRoot, config, "specsDir");
3953
3823
  const contractsRoot = resolvePath(resolvedRoot, config, "contractsDir");
3954
- const apiRoot = path21.join(contractsRoot, "api");
3955
- const uiRoot = path21.join(contractsRoot, "ui");
3956
- 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");
3957
3827
  const srcRoot = resolvePath(resolvedRoot, config, "srcDir");
3958
3828
  const testsRoot = resolvePath(resolvedRoot, config, "testsDir");
3959
3829
  const specFiles = await collectSpecFiles(specsRoot);
@@ -4418,11 +4288,9 @@ function formatReportMarkdown(data, options = {}) {
4418
4288
  "- issue \u306F\u691C\u51FA\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u904B\u7528\u30C6\u30F3\u30D7\u30EC\u306B\u6CBF\u3063\u3066\u7D99\u7D9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
4419
4289
  );
4420
4290
  }
4291
+ lines.push("- \u5909\u66F4\u5185\u5BB9\u30FB\u53D7\u5165\u89B3\u70B9\u306F `.qfai/specs/*/delta.md` \u306B\u8A18\u9332\u3057\u307E\u3059\u3002");
4421
4292
  lines.push(
4422
- "- \u5909\u66F4\u533A\u5206\uFF08Compatibility / Change/Improvement\uFF09\u306F `.qfai/specs/*/delta.md` \u306B\u8A18\u9332\u3057\u307E\u3059\u3002"
4423
- );
4424
- lines.push(
4425
- "- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/promptpack/steering/traceability.md` / `.qfai/promptpack/steering/compatibility-vs-change.md`"
4293
+ "- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/assistant/instructions/constitution.md`"
4426
4294
  );
4427
4295
  return lines.join("\n");
4428
4296
  }
@@ -4437,7 +4305,7 @@ async function collectSpecContractRefs(specFiles, contractIdList) {
4437
4305
  idToSpecs.set(contractId, /* @__PURE__ */ new Set());
4438
4306
  }
4439
4307
  for (const file of specFiles) {
4440
- const text = await readFile13(file, "utf-8");
4308
+ const text = await readFile12(file, "utf-8");
4441
4309
  const parsed = parseSpec(text, file);
4442
4310
  const specKey = parsed.specId;
4443
4311
  if (!specKey) {
@@ -4479,7 +4347,7 @@ async function collectIds(files) {
4479
4347
  THEMA: /* @__PURE__ */ new Set()
4480
4348
  };
4481
4349
  for (const file of files) {
4482
- const text = await readFile13(file, "utf-8");
4350
+ const text = await readFile12(file, "utf-8");
4483
4351
  for (const prefix of ID_PREFIXES2) {
4484
4352
  const ids = extractIds(text, prefix);
4485
4353
  ids.forEach((id) => result[prefix].add(id));
@@ -4498,7 +4366,7 @@ async function collectIds(files) {
4498
4366
  async function collectUpstreamIds(files) {
4499
4367
  const ids = /* @__PURE__ */ new Set();
4500
4368
  for (const file of files) {
4501
- const text = await readFile13(file, "utf-8");
4369
+ const text = await readFile12(file, "utf-8");
4502
4370
  extractAllIds(text).forEach((id) => ids.add(id));
4503
4371
  }
4504
4372
  return ids;
@@ -4519,7 +4387,7 @@ async function evaluateTraceability(upstreamIds, srcRoot, testsRoot) {
4519
4387
  }
4520
4388
  const pattern = buildIdPattern2(Array.from(upstreamIds));
4521
4389
  for (const file of targetFiles) {
4522
- const text = await readFile13(file, "utf-8");
4390
+ const text = await readFile12(file, "utf-8");
4523
4391
  if (pattern.test(text)) {
4524
4392
  return true;
4525
4393
  }
@@ -4656,7 +4524,7 @@ function warnIfTruncated(scan, context) {
4656
4524
 
4657
4525
  // src/cli/commands/report.ts
4658
4526
  async function runReport(options) {
4659
- const root = path22.resolve(options.root);
4527
+ const root = path21.resolve(options.root);
4660
4528
  const configResult = await loadConfig(root);
4661
4529
  let validation;
4662
4530
  if (options.runValidate) {
@@ -4673,7 +4541,7 @@ async function runReport(options) {
4673
4541
  validation = normalized;
4674
4542
  } else {
4675
4543
  const input = options.inputPath ?? configResult.config.output.validateJsonPath;
4676
- const inputPath = path22.isAbsolute(input) ? input : path22.resolve(root, input);
4544
+ const inputPath = path21.isAbsolute(input) ? input : path21.resolve(root, input);
4677
4545
  try {
4678
4546
  validation = await readValidationResult(inputPath);
4679
4547
  } catch (err) {
@@ -4684,7 +4552,7 @@ async function runReport(options) {
4684
4552
  "",
4685
4553
  "\u307E\u305A qfai validate \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B:",
4686
4554
  " qfai validate",
4687
- "\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",
4688
4556
  "",
4689
4557
  "\u307E\u305F\u306F report \u306B --run-validate \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
4690
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"
@@ -4700,10 +4568,10 @@ async function runReport(options) {
4700
4568
  warnIfTruncated(data.traceability.testFiles, "report");
4701
4569
  const output = options.format === "json" ? formatReportJson(data) : options.baseUrl ? formatReportMarkdown(data, { baseUrl: options.baseUrl }) : formatReportMarkdown(data);
4702
4570
  const outRoot = resolvePath(root, configResult.config, "outDir");
4703
- 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");
4704
4572
  const out = options.outPath ?? defaultOut;
4705
- const outPath = path22.isAbsolute(out) ? out : path22.resolve(root, out);
4706
- 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 });
4707
4575
  await writeFile2(outPath, `${output}
4708
4576
  `, "utf-8");
4709
4577
  info(
@@ -4712,7 +4580,7 @@ async function runReport(options) {
4712
4580
  info(`wrote report: ${outPath}`);
4713
4581
  }
4714
4582
  async function readValidationResult(inputPath) {
4715
- const raw = await readFile14(inputPath, "utf-8");
4583
+ const raw = await readFile13(inputPath, "utf-8");
4716
4584
  const parsed = JSON.parse(raw);
4717
4585
  if (!isValidationResult(parsed)) {
4718
4586
  throw new Error(`validate.json \u306E\u5F62\u5F0F\u304C\u4E0D\u6B63\u3067\u3059: ${inputPath}`);
@@ -4768,15 +4636,15 @@ function isMissingFileError5(error2) {
4768
4636
  return record2.code === "ENOENT";
4769
4637
  }
4770
4638
  async function writeValidationResult(root, outputPath, result) {
4771
- const abs = path22.isAbsolute(outputPath) ? outputPath : path22.resolve(root, outputPath);
4772
- 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 });
4773
4641
  await writeFile2(abs, `${JSON.stringify(result, null, 2)}
4774
4642
  `, "utf-8");
4775
4643
  }
4776
4644
 
4777
4645
  // src/cli/commands/validate.ts
4778
4646
  import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
4779
- import path23 from "path";
4647
+ import path22 from "path";
4780
4648
 
4781
4649
  // src/cli/lib/failOn.ts
4782
4650
  function shouldFail(result, failOn) {
@@ -4791,7 +4659,7 @@ function shouldFail(result, failOn) {
4791
4659
 
4792
4660
  // src/cli/commands/validate.ts
4793
4661
  async function runValidate(options) {
4794
- const root = path23.resolve(options.root);
4662
+ const root = path22.resolve(options.root);
4795
4663
  const configResult = await loadConfig(root);
4796
4664
  const result = await validateProject(root, configResult);
4797
4665
  const normalized = normalizeValidationResult(root, result);
@@ -4916,12 +4784,12 @@ function issueKey(issue7) {
4916
4784
  }
4917
4785
  async function emitJson(result, root, jsonPath) {
4918
4786
  const abs = resolveJsonPath(root, jsonPath);
4919
- await mkdir4(path23.dirname(abs), { recursive: true });
4787
+ await mkdir4(path22.dirname(abs), { recursive: true });
4920
4788
  await writeFile3(abs, `${JSON.stringify(result, null, 2)}
4921
4789
  `, "utf-8");
4922
4790
  }
4923
4791
  function resolveJsonPath(root, jsonPath) {
4924
- return path23.isAbsolute(jsonPath) ? jsonPath : path23.resolve(root, jsonPath);
4792
+ return path22.isAbsolute(jsonPath) ? jsonPath : path22.resolve(root, jsonPath);
4925
4793
  }
4926
4794
  var GITHUB_ANNOTATION_LIMIT = 100;
4927
4795
 
@@ -4934,7 +4802,6 @@ function parseArgs(argv, cwd) {
4934
4802
  force: false,
4935
4803
  yes: false,
4936
4804
  dryRun: false,
4937
- analyzeList: false,
4938
4805
  reportFormat: "md",
4939
4806
  reportRunValidate: false,
4940
4807
  doctorFormat: "text",
@@ -4986,18 +4853,6 @@ function parseArgs(argv, cwd) {
4986
4853
  case "--dry-run":
4987
4854
  options.dryRun = true;
4988
4855
  break;
4989
- case "--list":
4990
- options.analyzeList = true;
4991
- break;
4992
- case "--prompt":
4993
- {
4994
- const next = readOptionValue(args, i);
4995
- if (next) {
4996
- options.analyzePrompt = next;
4997
- i += 1;
4998
- }
4999
- }
5000
- break;
5001
4856
  case "--format": {
5002
4857
  const next = readOptionValue(args, i);
5003
4858
  if (next === null) {
@@ -5131,17 +4986,6 @@ async function run(argv, cwd) {
5131
4986
  yes: options.yes
5132
4987
  });
5133
4988
  return;
5134
- case "analyze":
5135
- {
5136
- const resolvedRoot = await resolveRoot(options);
5137
- const exitCode = await runAnalyze({
5138
- root: resolvedRoot,
5139
- list: options.analyzeList,
5140
- ...options.analyzePrompt !== void 0 ? { prompt: options.analyzePrompt } : {}
5141
- });
5142
- process.exitCode = exitCode;
5143
- }
5144
- return;
5145
4989
  case "validate":
5146
4990
  {
5147
4991
  const resolvedRoot = await resolveRoot(options);
@@ -5189,7 +5033,6 @@ function usage() {
5189
5033
 
5190
5034
  Commands:
5191
5035
  init \u30C6\u30F3\u30D7\u30EC\u3092\u751F\u6210
5192
- analyze \u610F\u5473\u30EC\u30D9\u30EB\u306E\u30EC\u30D3\u30E5\u30FC\u88DC\u52A9\uFF08\u30D7\u30ED\u30F3\u30D7\u30C8\u51FA\u529B\uFF09
5193
5036
  validate \u4ED5\u69D8/\u5951\u7D04/\u53C2\u7167\u306E\u691C\u67FB
5194
5037
  report \u691C\u8A3C\u7D50\u679C\u3068\u96C6\u8A08\u3092\u51FA\u529B
5195
5038
  doctor \u8A2D\u5B9A/\u30D1\u30B9/\u51FA\u529B\u524D\u63D0\u306E\u8A3A\u65AD
@@ -5197,11 +5040,9 @@ Commands:
5197
5040
  Options:
5198
5041
  --root <path> \u5BFE\u8C61\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA
5199
5042
  --dir <path> init \u306E\u51FA\u529B\u5148
5200
- --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
5201
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
5202
5045
  --dry-run \u5909\u66F4\u3092\u884C\u308F\u305A\u8868\u793A\u306E\u307F
5203
- --list analyze: \u5229\u7528\u53EF\u80FD\u306A\u30D7\u30ED\u30F3\u30D7\u30C8\u4E00\u89A7\u3092\u8868\u793A
5204
- --prompt <name> analyze: \u6307\u5B9A\u30D7\u30ED\u30F3\u30D7\u30C8\uFF08.md\u7701\u7565\u53EF\uFF09\u3092\u51FA\u529B
5205
5046
  --format <text|github> validate \u306E\u51FA\u529B\u5F62\u5F0F
5206
5047
  --format <md|json> report \u306E\u51FA\u529B\u5F62\u5F0F
5207
5048
  --format <text|json> doctor \u306E\u51FA\u529B\u5F62\u5F0F