qfai 1.0.4 → 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 (89) hide show
  1. package/README.md +53 -65
  2. package/assets/init/.qfai/README.md +17 -77
  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 -56
  42. package/assets/init/root/.github/workflows/qfai.yml +1 -1
  43. package/assets/init/root/qfai.config.yaml +3 -3
  44. package/dist/cli/index.cjs +306 -423
  45. package/dist/cli/index.cjs.map +1 -1
  46. package/dist/cli/index.mjs +288 -405
  47. package/dist/cli/index.mjs.map +1 -1
  48. package/dist/index.cjs +29 -14
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.mjs +29 -14
  51. package/dist/index.mjs.map +1 -1
  52. package/package.json +1 -1
  53. package/assets/init/.qfai/contracts/api/api-0001-sample.yaml +0 -15
  54. package/assets/init/.qfai/contracts/db/db-0001-sample.sql +0 -7
  55. package/assets/init/.qfai/contracts/ui/assets/thema-001-facebook-like/assets.yaml +0 -6
  56. package/assets/init/.qfai/contracts/ui/assets/thema-001-facebook-like/palette.png +0 -0
  57. package/assets/init/.qfai/contracts/ui/assets/ui-0001-sample/assets.yaml +0 -6
  58. package/assets/init/.qfai/contracts/ui/assets/ui-0001-sample/snapshots/login__desktop__light__default.png +0 -0
  59. package/assets/init/.qfai/contracts/ui/thema-001-facebook-like.yml +0 -13
  60. package/assets/init/.qfai/contracts/ui/ui-0001-sample.yaml +0 -17
  61. package/assets/init/.qfai/out/README.md +0 -17
  62. package/assets/init/.qfai/promptpack/commands/implement.md +0 -8
  63. package/assets/init/.qfai/promptpack/commands/plan.md +0 -11
  64. package/assets/init/.qfai/promptpack/commands/release.md +0 -6
  65. package/assets/init/.qfai/promptpack/commands/review.md +0 -6
  66. package/assets/init/.qfai/promptpack/constitution.md +0 -15
  67. package/assets/init/.qfai/promptpack/roles/qa.md +0 -4
  68. package/assets/init/.qfai/promptpack/roles/spec.md +0 -4
  69. package/assets/init/.qfai/promptpack/roles/test.md +0 -4
  70. package/assets/init/.qfai/promptpack/steering/naming.md +0 -7
  71. package/assets/init/.qfai/promptpack/steering/traceability.md +0 -25
  72. package/assets/init/.qfai/prompts/README.md +0 -68
  73. package/assets/init/.qfai/prompts/analyze/README.md +0 -21
  74. package/assets/init/.qfai/prompts/analyze/scenario_test_consistency.md +0 -8
  75. package/assets/init/.qfai/prompts/analyze/scenario_to_test.md +0 -56
  76. package/assets/init/.qfai/prompts/analyze/spec_contract_consistency.md +0 -8
  77. package/assets/init/.qfai/prompts/analyze/spec_scenario_consistency.md +0 -8
  78. package/assets/init/.qfai/prompts/analyze/spec_to_contract.md +0 -54
  79. package/assets/init/.qfai/prompts/analyze/spec_to_scenario.md +0 -56
  80. package/assets/init/.qfai/prompts/makeBusinessFlow.md +0 -34
  81. package/assets/init/.qfai/prompts/makeOverview.md +0 -27
  82. package/assets/init/.qfai/prompts/qfai-generate-test-globs.md +0 -29
  83. package/assets/init/.qfai/prompts/qfai-maintain-contracts.md +0 -35
  84. package/assets/init/.qfai/prompts/qfai-maintain-traceability.md +0 -36
  85. package/assets/init/.qfai/prompts/require-to-spec.md +0 -40
  86. package/assets/init/.qfai/prompts.local/README.md +0 -31
  87. package/assets/init/.qfai/specs/spec-0001/delta.md +0 -25
  88. package/assets/init/.qfai/specs/spec-0001/scenario.feature +0 -11
  89. package/assets/init/.qfai/specs/spec-0001/spec.md +0 -40
@@ -23,230 +23,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  mod
24
24
  ));
25
25
 
26
- // src/cli/commands/analyze.ts
27
- var import_promises2 = require("fs/promises");
28
- var import_node_path2 = __toESM(require("path"), 1);
29
-
30
- // src/core/fs.ts
31
- var import_promises = require("fs/promises");
32
- var import_node_path = __toESM(require("path"), 1);
33
- var import_fast_glob = __toESM(require("fast-glob"), 1);
34
- var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
35
- "node_modules",
36
- ".git",
37
- "dist",
38
- ".pnpm",
39
- "tmp",
40
- ".mcp-tools"
41
- ]);
42
- var DEFAULT_GLOB_FILE_LIMIT = 2e4;
43
- async function collectFiles(root, options = {}) {
44
- const entries = [];
45
- if (!await exists(root)) {
46
- return entries;
47
- }
48
- const ignoreDirs = /* @__PURE__ */ new Set([
49
- ...DEFAULT_IGNORE_DIRS,
50
- ...options.ignoreDirs ?? []
51
- ]);
52
- const extensions = options.extensions?.map((ext) => ext.toLowerCase()) ?? [];
53
- await walk(root, root, ignoreDirs, extensions, entries);
54
- return entries;
55
- }
56
- async function collectFilesByGlobs(root, options) {
57
- const limit = normalizeLimit(options.limit);
58
- if (options.globs.length === 0) {
59
- return { files: [], truncated: false, matchedFileCount: 0, limit };
60
- }
61
- const stream = import_fast_glob.default.stream(options.globs, {
62
- cwd: root,
63
- ignore: options.ignore ?? [],
64
- onlyFiles: true,
65
- absolute: true,
66
- unique: true
67
- });
68
- const files = [];
69
- let truncated = false;
70
- for await (const entry of stream) {
71
- if (files.length >= limit) {
72
- truncated = true;
73
- destroyStream(stream);
74
- break;
75
- }
76
- files.push(String(entry));
77
- }
78
- const matchedFileCount = files.length;
79
- return { files, truncated, matchedFileCount, limit };
80
- }
81
- async function walk(base, current, ignoreDirs, extensions, out) {
82
- const items = await (0, import_promises.readdir)(current, { withFileTypes: true });
83
- for (const item of items) {
84
- const fullPath = import_node_path.default.join(current, item.name);
85
- if (item.isDirectory()) {
86
- if (ignoreDirs.has(item.name)) {
87
- continue;
88
- }
89
- await walk(base, fullPath, ignoreDirs, extensions, out);
90
- continue;
91
- }
92
- if (item.isFile()) {
93
- if (extensions.length > 0) {
94
- const ext = import_node_path.default.extname(item.name).toLowerCase();
95
- if (!extensions.includes(ext)) {
96
- continue;
97
- }
98
- }
99
- out.push(fullPath);
100
- }
101
- }
102
- }
103
- async function exists(target) {
104
- try {
105
- await (0, import_promises.access)(target);
106
- return true;
107
- } catch {
108
- return false;
109
- }
110
- }
111
- function normalizeLimit(value) {
112
- if (typeof value !== "number" || Number.isNaN(value)) {
113
- return DEFAULT_GLOB_FILE_LIMIT;
114
- }
115
- const flooredValue = Math.floor(value);
116
- if (flooredValue <= 0) {
117
- return DEFAULT_GLOB_FILE_LIMIT;
118
- }
119
- return flooredValue;
120
- }
121
- function destroyStream(stream) {
122
- if (!stream || typeof stream !== "object") {
123
- return;
124
- }
125
- const record2 = stream;
126
- if (typeof record2.destroy === "function") {
127
- record2.destroy();
128
- }
129
- }
130
-
131
- // src/cli/commands/analyze.ts
132
- async function runAnalyze(options) {
133
- const root = import_node_path2.default.resolve(options.root);
134
- const localDir = import_node_path2.default.join(root, ".qfai", "prompts.local", "analyze");
135
- const standardDir = import_node_path2.default.join(root, ".qfai", "prompts", "analyze");
136
- const available = await listPromptNames([localDir, standardDir]);
137
- const promptName = normalizePromptName(options.prompt);
138
- if (!promptName || options.list) {
139
- emitList(available);
140
- return 0;
141
- }
142
- const resolved = await resolvePromptPath(promptName, [localDir, standardDir]);
143
- if (!resolved) {
144
- emitPromptNotFound(promptName, available);
145
- return 1;
146
- }
147
- const content = await (0, import_promises2.readFile)(resolved, "utf-8");
148
- process.stdout.write(content);
149
- if (!content.endsWith("\n")) {
150
- process.stdout.write("\n");
151
- }
152
- return 0;
153
- }
154
- function normalizePromptName(value) {
155
- const trimmed = (value ?? "").trim();
156
- if (!trimmed) {
157
- return null;
158
- }
159
- return trimmed.endsWith(".md") ? trimmed.slice(0, -3) : trimmed;
160
- }
161
- async function listPromptNames(dirs) {
162
- const byName = /* @__PURE__ */ new Map();
163
- for (const dir of dirs) {
164
- const files = await collectFiles(dir, { extensions: [".md"] });
165
- for (const abs of files) {
166
- const base = import_node_path2.default.basename(abs);
167
- if (base.toLowerCase() === "readme.md") {
168
- continue;
169
- }
170
- const name = base.slice(0, -3);
171
- if (byName.has(name)) {
172
- continue;
173
- }
174
- if (await isDeprecatedPrompt(abs)) {
175
- continue;
176
- }
177
- byName.set(name, abs);
178
- }
179
- }
180
- return [...byName.keys()].sort((a, b) => a.localeCompare(b));
181
- }
182
- function emitList(names) {
183
- process.stdout.write("# qfai analyze: prompts\n\n");
184
- if (names.length === 0) {
185
- process.stdout.write(
186
- "\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"
187
- );
188
- return;
189
- }
190
- process.stdout.write("\u5229\u7528\u53EF\u80FD\u306A\u30D7\u30ED\u30F3\u30D7\u30C8\u4E00\u89A7:\n\n");
191
- for (const name of names) {
192
- process.stdout.write(`- ${name}
193
- `);
194
- }
195
- }
196
- async function resolvePromptPath(promptName, dirs) {
197
- const filename = `${promptName}.md`;
198
- for (const dir of dirs) {
199
- const full = import_node_path2.default.join(dir, filename);
200
- try {
201
- await (0, import_promises2.readFile)(full, "utf-8");
202
- return full;
203
- } catch {
204
- }
205
- }
206
- return null;
207
- }
208
- async function isDeprecatedPrompt(filePath) {
209
- try {
210
- const content = await (0, import_promises2.readFile)(filePath, "utf-8");
211
- const firstLine = firstLineOf(content);
212
- return firstLine.trim() === "# Deprecated";
213
- } catch {
214
- return false;
215
- }
216
- }
217
- function firstLineOf(content) {
218
- return content.match(/^[^\r\n]*/)?.[0] ?? "";
219
- }
220
- function emitPromptNotFound(promptName, candidates) {
221
- process.stderr.write(`qfai analyze: prompt not found: ${promptName}
222
- `);
223
- if (candidates.length > 0) {
224
- process.stderr.write("candidates:\n");
225
- for (const c of candidates) {
226
- process.stderr.write(`- ${c}
227
- `);
228
- }
229
- }
230
- }
231
-
232
26
  // src/cli/commands/doctor.ts
233
- var import_promises10 = require("fs/promises");
234
- var import_node_path12 = __toESM(require("path"), 1);
235
-
236
- // src/core/doctor.ts
237
27
  var import_promises9 = require("fs/promises");
238
28
  var import_node_path11 = __toESM(require("path"), 1);
239
29
 
30
+ // src/core/doctor.ts
31
+ var import_promises8 = require("fs/promises");
32
+ var import_node_path10 = __toESM(require("path"), 1);
33
+
240
34
  // src/core/config.ts
241
- var import_promises3 = require("fs/promises");
242
- var import_node_path3 = __toESM(require("path"), 1);
35
+ var import_promises = require("fs/promises");
36
+ var import_node_path = __toESM(require("path"), 1);
243
37
  var import_yaml = require("yaml");
244
38
  var defaultConfig = {
245
39
  paths: {
246
40
  contractsDir: ".qfai/contracts",
247
41
  specsDir: ".qfai/specs",
248
- outDir: ".qfai/out",
249
- promptsDir: ".qfai/prompts",
42
+ outDir: ".qfai/report",
43
+ promptsDir: ".qfai/assistant/prompts",
250
44
  srcDir: "src",
251
45
  testsDir: "tests"
252
46
  },
@@ -274,21 +68,21 @@ var defaultConfig = {
274
68
  }
275
69
  },
276
70
  output: {
277
- validateJsonPath: ".qfai/out/validate.json"
71
+ validateJsonPath: ".qfai/report/validate.json"
278
72
  }
279
73
  };
280
74
  function getConfigPath(root) {
281
- return import_node_path3.default.join(root, "qfai.config.yaml");
75
+ return import_node_path.default.join(root, "qfai.config.yaml");
282
76
  }
283
77
  async function findConfigRoot(startDir) {
284
- const resolvedStart = import_node_path3.default.resolve(startDir);
78
+ const resolvedStart = import_node_path.default.resolve(startDir);
285
79
  let current = resolvedStart;
286
80
  while (true) {
287
81
  const configPath = getConfigPath(current);
288
- if (await exists2(configPath)) {
82
+ if (await exists(configPath)) {
289
83
  return { root: current, configPath, found: true };
290
84
  }
291
- const parent = import_node_path3.default.dirname(current);
85
+ const parent = import_node_path.default.dirname(current);
292
86
  if (parent === current) {
293
87
  break;
294
88
  }
@@ -305,7 +99,7 @@ async function loadConfig(root) {
305
99
  const issues = [];
306
100
  let parsed;
307
101
  try {
308
- const raw = await (0, import_promises3.readFile)(configPath, "utf-8");
102
+ const raw = await (0, import_promises.readFile)(configPath, "utf-8");
309
103
  parsed = (0, import_yaml.parse)(raw);
310
104
  } catch (error2) {
311
105
  if (isMissingFile(error2)) {
@@ -318,7 +112,7 @@ async function loadConfig(root) {
318
112
  return { config: normalized, issues, configPath };
319
113
  }
320
114
  function resolvePath(root, config, key) {
321
- return import_node_path3.default.resolve(root, config.paths[key]);
115
+ return import_node_path.default.resolve(root, config.paths[key]);
322
116
  }
323
117
  function normalizeConfig(raw, configPath, issues) {
324
118
  if (!isRecord(raw)) {
@@ -611,9 +405,9 @@ function isMissingFile(error2) {
611
405
  }
612
406
  return false;
613
407
  }
614
- async function exists2(target) {
408
+ async function exists(target) {
615
409
  try {
616
- await (0, import_promises3.access)(target);
410
+ await (0, import_promises.access)(target);
617
411
  return true;
618
412
  } catch {
619
413
  return false;
@@ -630,27 +424,128 @@ function isRecord(value) {
630
424
  }
631
425
 
632
426
  // src/core/discovery.ts
633
- var import_promises5 = require("fs/promises");
634
- var import_node_path5 = __toESM(require("path"), 1);
635
-
636
- // src/core/specLayout.ts
637
427
  var import_promises4 = require("fs/promises");
638
428
  var import_node_path4 = __toESM(require("path"), 1);
429
+
430
+ // src/core/fs.ts
431
+ var import_promises2 = require("fs/promises");
432
+ var import_node_path2 = __toESM(require("path"), 1);
433
+ var import_fast_glob = __toESM(require("fast-glob"), 1);
434
+ var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
435
+ "node_modules",
436
+ ".git",
437
+ "dist",
438
+ ".pnpm",
439
+ "tmp",
440
+ ".mcp-tools"
441
+ ]);
442
+ var DEFAULT_GLOB_FILE_LIMIT = 2e4;
443
+ async function collectFiles(root, options = {}) {
444
+ const entries = [];
445
+ if (!await exists2(root)) {
446
+ return entries;
447
+ }
448
+ const ignoreDirs = /* @__PURE__ */ new Set([
449
+ ...DEFAULT_IGNORE_DIRS,
450
+ ...options.ignoreDirs ?? []
451
+ ]);
452
+ const extensions = options.extensions?.map((ext) => ext.toLowerCase()) ?? [];
453
+ await walk(root, root, ignoreDirs, extensions, entries);
454
+ return entries;
455
+ }
456
+ async function collectFilesByGlobs(root, options) {
457
+ const limit = normalizeLimit(options.limit);
458
+ if (options.globs.length === 0) {
459
+ return { files: [], truncated: false, matchedFileCount: 0, limit };
460
+ }
461
+ const stream = import_fast_glob.default.stream(options.globs, {
462
+ cwd: root,
463
+ ignore: options.ignore ?? [],
464
+ onlyFiles: true,
465
+ absolute: true,
466
+ unique: true
467
+ });
468
+ const files = [];
469
+ let truncated = false;
470
+ for await (const entry of stream) {
471
+ if (files.length >= limit) {
472
+ truncated = true;
473
+ destroyStream(stream);
474
+ break;
475
+ }
476
+ files.push(String(entry));
477
+ }
478
+ const matchedFileCount = files.length;
479
+ return { files, truncated, matchedFileCount, limit };
480
+ }
481
+ async function walk(base, current, ignoreDirs, extensions, out) {
482
+ const items = await (0, import_promises2.readdir)(current, { withFileTypes: true });
483
+ for (const item of items) {
484
+ const fullPath = import_node_path2.default.join(current, item.name);
485
+ if (item.isDirectory()) {
486
+ if (ignoreDirs.has(item.name)) {
487
+ continue;
488
+ }
489
+ await walk(base, fullPath, ignoreDirs, extensions, out);
490
+ continue;
491
+ }
492
+ if (item.isFile()) {
493
+ if (extensions.length > 0) {
494
+ const ext = import_node_path2.default.extname(item.name).toLowerCase();
495
+ if (!extensions.includes(ext)) {
496
+ continue;
497
+ }
498
+ }
499
+ out.push(fullPath);
500
+ }
501
+ }
502
+ }
503
+ async function exists2(target) {
504
+ try {
505
+ await (0, import_promises2.access)(target);
506
+ return true;
507
+ } catch {
508
+ return false;
509
+ }
510
+ }
511
+ function normalizeLimit(value) {
512
+ if (typeof value !== "number" || Number.isNaN(value)) {
513
+ return DEFAULT_GLOB_FILE_LIMIT;
514
+ }
515
+ const flooredValue = Math.floor(value);
516
+ if (flooredValue <= 0) {
517
+ return DEFAULT_GLOB_FILE_LIMIT;
518
+ }
519
+ return flooredValue;
520
+ }
521
+ function destroyStream(stream) {
522
+ if (!stream || typeof stream !== "object") {
523
+ return;
524
+ }
525
+ const record2 = stream;
526
+ if (typeof record2.destroy === "function") {
527
+ record2.destroy();
528
+ }
529
+ }
530
+
531
+ // src/core/specLayout.ts
532
+ var import_promises3 = require("fs/promises");
533
+ var import_node_path3 = __toESM(require("path"), 1);
639
534
  var SPEC_DIR_RE = /^spec-\d{4}$/;
640
535
  async function collectSpecEntries(specsRoot) {
641
536
  const dirs = await listSpecDirs(specsRoot);
642
537
  const entries = dirs.map((dir) => ({
643
538
  dir,
644
- specPath: import_node_path4.default.join(dir, "spec.md"),
645
- deltaPath: import_node_path4.default.join(dir, "delta.md"),
646
- scenarioPath: import_node_path4.default.join(dir, "scenario.feature")
539
+ specPath: import_node_path3.default.join(dir, "spec.md"),
540
+ deltaPath: import_node_path3.default.join(dir, "delta.md"),
541
+ scenarioPath: import_node_path3.default.join(dir, "scenario.feature")
647
542
  }));
648
543
  return entries.sort((a, b) => a.dir.localeCompare(b.dir));
649
544
  }
650
545
  async function listSpecDirs(specsRoot) {
651
546
  try {
652
- const items = await (0, import_promises4.readdir)(specsRoot, { withFileTypes: true });
653
- return items.filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => SPEC_DIR_RE.test(name.toLowerCase())).map((name) => import_node_path4.default.join(specsRoot, name));
547
+ const items = await (0, import_promises3.readdir)(specsRoot, { withFileTypes: true });
548
+ return items.filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => SPEC_DIR_RE.test(name.toLowerCase())).map((name) => import_node_path3.default.join(specsRoot, name));
654
549
  } catch (error2) {
655
550
  if (isMissingFileError(error2)) {
656
551
  return [];
@@ -712,7 +607,7 @@ async function filterExisting(files) {
712
607
  }
713
608
  async function exists3(target) {
714
609
  try {
715
- await (0, import_promises5.access)(target);
610
+ await (0, import_promises4.access)(target);
716
611
  return true;
717
612
  } catch {
718
613
  return false;
@@ -721,20 +616,20 @@ async function exists3(target) {
721
616
  function filterByBasenamePrefix(files, prefix) {
722
617
  const lowerPrefix = prefix.toLowerCase();
723
618
  return files.filter(
724
- (file) => import_node_path5.default.basename(file).toLowerCase().startsWith(lowerPrefix)
619
+ (file) => import_node_path4.default.basename(file).toLowerCase().startsWith(lowerPrefix)
725
620
  );
726
621
  }
727
622
 
728
623
  // src/core/paths.ts
729
- var import_node_path6 = __toESM(require("path"), 1);
624
+ var import_node_path5 = __toESM(require("path"), 1);
730
625
  function toRelativePath(root, target) {
731
626
  if (!target) {
732
627
  return target;
733
628
  }
734
- if (!import_node_path6.default.isAbsolute(target)) {
629
+ if (!import_node_path5.default.isAbsolute(target)) {
735
630
  return toPosixPath(target);
736
631
  }
737
- const relative = import_node_path6.default.relative(root, target);
632
+ const relative = import_node_path5.default.relative(root, target);
738
633
  if (!relative) {
739
634
  return ".";
740
635
  }
@@ -745,8 +640,8 @@ function toPosixPath(value) {
745
640
  }
746
641
 
747
642
  // src/core/traceability.ts
748
- var import_promises6 = require("fs/promises");
749
- var import_node_path7 = __toESM(require("path"), 1);
643
+ var import_promises5 = require("fs/promises");
644
+ var import_node_path6 = __toESM(require("path"), 1);
750
645
 
751
646
  // src/core/gherkin/parse.ts
752
647
  var import_gherkin = require("@cucumber/gherkin");
@@ -898,7 +793,7 @@ function extractAnnotatedScIds(text) {
898
793
  async function collectScIdsFromScenarioFiles(scenarioFiles) {
899
794
  const scIds = /* @__PURE__ */ new Set();
900
795
  for (const file of scenarioFiles) {
901
- const text = await (0, import_promises6.readFile)(file, "utf-8");
796
+ const text = await (0, import_promises5.readFile)(file, "utf-8");
902
797
  const { document, errors } = parseScenarioDocument(text, file);
903
798
  if (!document || errors.length > 0) {
904
799
  continue;
@@ -916,7 +811,7 @@ async function collectScIdsFromScenarioFiles(scenarioFiles) {
916
811
  async function collectScIdSourcesFromScenarioFiles(scenarioFiles) {
917
812
  const sources = /* @__PURE__ */ new Map();
918
813
  for (const file of scenarioFiles) {
919
- const text = await (0, import_promises6.readFile)(file, "utf-8");
814
+ const text = await (0, import_promises5.readFile)(file, "utf-8");
920
815
  const { document, errors } = parseScenarioDocument(text, file);
921
816
  if (!document || errors.length > 0) {
922
817
  continue;
@@ -974,10 +869,10 @@ async function collectScTestReferences(root, globs, excludeGlobs) {
974
869
  };
975
870
  }
976
871
  const normalizedFiles = Array.from(
977
- new Set(scanResult.files.map((file) => import_node_path7.default.normalize(file)))
872
+ new Set(scanResult.files.map((file) => import_node_path6.default.normalize(file)))
978
873
  );
979
874
  for (const file of normalizedFiles) {
980
- const text = await (0, import_promises6.readFile)(file, "utf-8");
875
+ const text = await (0, import_promises5.readFile)(file, "utf-8");
981
876
  const scIds = extractAnnotatedScIds(text);
982
877
  if (scIds.length === 0) {
983
878
  continue;
@@ -1036,20 +931,20 @@ function formatError3(error2) {
1036
931
  }
1037
932
 
1038
933
  // src/core/promptsIntegrity.ts
1039
- var import_promises7 = require("fs/promises");
1040
- var import_node_path9 = __toESM(require("path"), 1);
934
+ var import_promises6 = require("fs/promises");
935
+ var import_node_path8 = __toESM(require("path"), 1);
1041
936
 
1042
937
  // src/shared/assets.ts
1043
938
  var import_node_fs = require("fs");
1044
- var import_node_path8 = __toESM(require("path"), 1);
939
+ var import_node_path7 = __toESM(require("path"), 1);
1045
940
  var import_node_url = require("url");
1046
941
  function getInitAssetsDir() {
1047
942
  const base = __filename;
1048
943
  const basePath = base.startsWith("file:") ? (0, import_node_url.fileURLToPath)(base) : base;
1049
- const baseDir = import_node_path8.default.dirname(basePath);
944
+ const baseDir = import_node_path7.default.dirname(basePath);
1050
945
  const candidates = [
1051
- import_node_path8.default.resolve(baseDir, "../../../assets/init"),
1052
- import_node_path8.default.resolve(baseDir, "../../assets/init")
946
+ import_node_path7.default.resolve(baseDir, "../../../assets/init"),
947
+ import_node_path7.default.resolve(baseDir, "../../assets/init")
1053
948
  ];
1054
949
  for (const candidate of candidates) {
1055
950
  if ((0, import_node_fs.existsSync)(candidate)) {
@@ -1067,11 +962,24 @@ function getInitAssetsDir() {
1067
962
 
1068
963
  // src/core/promptsIntegrity.ts
1069
964
  var LEGACY_OK_EXTRA = /* @__PURE__ */ new Set(["qfai-classify-change.md"]);
1070
- async function diffProjectPromptsAgainstInitAssets(root) {
1071
- const promptsDir = import_node_path9.default.resolve(root, ".qfai", "prompts");
965
+ async function diffProjectPromptsAgainstInitAssets(root, config) {
966
+ const promptsDirConfig = config.paths.promptsDir;
967
+ const promptsDir = import_node_path8.default.isAbsolute(promptsDirConfig) ? promptsDirConfig : import_node_path8.default.resolve(root, promptsDirConfig);
1072
968
  let templateDir;
1073
969
  try {
1074
- templateDir = import_node_path9.default.join(getInitAssetsDir(), ".qfai", "prompts");
970
+ const rel = import_node_path8.default.isAbsolute(promptsDirConfig) ? import_node_path8.default.relative(root, promptsDirConfig) : promptsDirConfig;
971
+ const normalized = rel.replace(/^[\\/]+/, "");
972
+ if (normalized.length === 0 || normalized.startsWith("..")) {
973
+ return {
974
+ status: "skipped_missing_assets",
975
+ promptsDir,
976
+ templateDir: "",
977
+ missing: [],
978
+ extra: [],
979
+ changed: []
980
+ };
981
+ }
982
+ templateDir = import_node_path8.default.join(getInitAssetsDir(), normalized);
1075
983
  } catch {
1076
984
  return {
1077
985
  status: "skipped_missing_assets",
@@ -1125,8 +1033,8 @@ async function diffProjectPromptsAgainstInitAssets(root) {
1125
1033
  }
1126
1034
  try {
1127
1035
  const [a, b] = await Promise.all([
1128
- (0, import_promises7.readFile)(templateAbs, "utf-8"),
1129
- (0, import_promises7.readFile)(projectAbs, "utf-8")
1036
+ (0, import_promises6.readFile)(templateAbs, "utf-8"),
1037
+ (0, import_promises6.readFile)(projectAbs, "utf-8")
1130
1038
  ]);
1131
1039
  if (normalizeNewlines(a) !== normalizeNewlines(b)) {
1132
1040
  changed.push(rel);
@@ -1149,7 +1057,7 @@ function normalizeNewlines(text) {
1149
1057
  return text.replace(/\r\n/g, "\n");
1150
1058
  }
1151
1059
  function toRel(base, abs) {
1152
- const rel = import_node_path9.default.relative(base, abs);
1060
+ const rel = import_node_path8.default.relative(base, abs);
1153
1061
  return rel.replace(/[\\/]+/g, "/");
1154
1062
  }
1155
1063
  function intersectKeys(a, b) {
@@ -1163,16 +1071,16 @@ function intersectKeys(a, b) {
1163
1071
  }
1164
1072
 
1165
1073
  // src/core/version.ts
1166
- var import_promises8 = require("fs/promises");
1167
- var import_node_path10 = __toESM(require("path"), 1);
1074
+ var import_promises7 = require("fs/promises");
1075
+ var import_node_path9 = __toESM(require("path"), 1);
1168
1076
  var import_node_url2 = require("url");
1169
1077
  async function resolveToolVersion() {
1170
- if ("1.0.4".length > 0) {
1171
- return "1.0.4";
1078
+ if ("1.0.5".length > 0) {
1079
+ return "1.0.5";
1172
1080
  }
1173
1081
  try {
1174
1082
  const packagePath = resolvePackageJsonPath();
1175
- const raw = await (0, import_promises8.readFile)(packagePath, "utf-8");
1083
+ const raw = await (0, import_promises7.readFile)(packagePath, "utf-8");
1176
1084
  const parsed = JSON.parse(raw);
1177
1085
  const version = typeof parsed.version === "string" ? parsed.version : "";
1178
1086
  return version.length > 0 ? version : "unknown";
@@ -1183,13 +1091,13 @@ async function resolveToolVersion() {
1183
1091
  function resolvePackageJsonPath() {
1184
1092
  const base = __filename;
1185
1093
  const basePath = base.startsWith("file:") ? (0, import_node_url2.fileURLToPath)(base) : base;
1186
- return import_node_path10.default.resolve(import_node_path10.default.dirname(basePath), "../../package.json");
1094
+ return import_node_path9.default.resolve(import_node_path9.default.dirname(basePath), "../../package.json");
1187
1095
  }
1188
1096
 
1189
1097
  // src/core/doctor.ts
1190
1098
  async function exists4(target) {
1191
1099
  try {
1192
- await (0, import_promises9.access)(target);
1100
+ await (0, import_promises8.access)(target);
1193
1101
  return true;
1194
1102
  } catch {
1195
1103
  return false;
@@ -1209,7 +1117,7 @@ function normalizeGlobs2(values) {
1209
1117
  return values.map((glob) => glob.trim()).filter((glob) => glob.length > 0);
1210
1118
  }
1211
1119
  async function createDoctorData(options) {
1212
- const startDir = import_node_path11.default.resolve(options.startDir);
1120
+ const startDir = import_node_path10.default.resolve(options.startDir);
1213
1121
  const checks = [];
1214
1122
  const configPath = getConfigPath(startDir);
1215
1123
  const search = options.rootExplicit ? {
@@ -1271,9 +1179,9 @@ async function createDoctorData(options) {
1271
1179
  details: { path: toRelativePath(root, resolved) }
1272
1180
  });
1273
1181
  if (key === "promptsDir") {
1274
- const promptsLocalDir = import_node_path11.default.join(
1275
- import_node_path11.default.dirname(resolved),
1276
- `${import_node_path11.default.basename(resolved)}.local`
1182
+ const promptsLocalDir = import_node_path10.default.join(
1183
+ import_node_path10.default.dirname(resolved),
1184
+ `${import_node_path10.default.basename(resolved)}.local`
1277
1185
  );
1278
1186
  const found = await exists4(promptsLocalDir);
1279
1187
  addCheck(checks, {
@@ -1283,12 +1191,12 @@ async function createDoctorData(options) {
1283
1191
  message: found ? "prompts.local exists (overlay can be used)" : "prompts.local is optional (create it to override prompts)",
1284
1192
  details: { path: toRelativePath(root, promptsLocalDir) }
1285
1193
  });
1286
- const diff = await diffProjectPromptsAgainstInitAssets(root);
1194
+ const diff = await diffProjectPromptsAgainstInitAssets(root, config);
1287
1195
  if (diff.status === "skipped_missing_prompts") {
1288
1196
  addCheck(checks, {
1289
1197
  id: "prompts.integrity",
1290
1198
  severity: "info",
1291
- title: "Prompts integrity (.qfai/prompts)",
1199
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1292
1200
  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",
1293
1201
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1294
1202
  });
@@ -1296,7 +1204,7 @@ async function createDoctorData(options) {
1296
1204
  addCheck(checks, {
1297
1205
  id: "prompts.integrity",
1298
1206
  severity: "info",
1299
- title: "Prompts integrity (.qfai/prompts)",
1207
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1300
1208
  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",
1301
1209
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1302
1210
  });
@@ -1304,7 +1212,7 @@ async function createDoctorData(options) {
1304
1212
  addCheck(checks, {
1305
1213
  id: "prompts.integrity",
1306
1214
  severity: "ok",
1307
- title: "Prompts integrity (.qfai/prompts)",
1215
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1308
1216
  message: "\u6A19\u6E96 assets \u3068\u4E00\u81F4\u3057\u3066\u3044\u307E\u3059",
1309
1217
  details: { promptsDir: toRelativePath(root, diff.promptsDir) }
1310
1218
  });
@@ -1312,15 +1220,15 @@ async function createDoctorData(options) {
1312
1220
  addCheck(checks, {
1313
1221
  id: "prompts.integrity",
1314
1222
  severity: "error",
1315
- title: "Prompts integrity (.qfai/prompts)",
1316
- 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",
1223
+ title: "Prompts integrity (.qfai/assistant/prompts)",
1224
+ 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",
1317
1225
  details: {
1318
1226
  promptsDir: toRelativePath(root, diff.promptsDir),
1319
1227
  missing: diff.missing,
1320
1228
  extra: diff.extra,
1321
1229
  changed: diff.changed,
1322
1230
  nextActions: [
1323
- "\u5909\u66F4\u5185\u5BB9\u3092 .qfai/prompts.local/** \u306B\u79FB\u3059\uFF08\u540C\u4E00\u76F8\u5BFE\u30D1\u30B9\u3067\u914D\u7F6E\uFF09",
1231
+ "\u5909\u66F4\u5185\u5BB9\u3092 .qfai/assistant/prompts.local/** \u306B\u79FB\u3059\uFF08\u540C\u4E00\u76F8\u5BFE\u30D1\u30B9\u3067\u914D\u7F6E\uFF09",
1324
1232
  "\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"
1325
1233
  ]
1326
1234
  }
@@ -1346,7 +1254,7 @@ async function createDoctorData(options) {
1346
1254
  message: missingFiles === 0 ? `All spec packs have required files (count=${entries.length})` : `Missing required files in spec packs (missingFiles=${missingFiles})`,
1347
1255
  details: { specPacks: entries.length, missingFiles }
1348
1256
  });
1349
- const validateJsonAbs = import_node_path11.default.isAbsolute(config.output.validateJsonPath) ? config.output.validateJsonPath : import_node_path11.default.resolve(root, config.output.validateJsonPath);
1257
+ const validateJsonAbs = import_node_path10.default.isAbsolute(config.output.validateJsonPath) ? config.output.validateJsonPath : import_node_path10.default.resolve(root, config.output.validateJsonPath);
1350
1258
  const validateJsonExists = await exists4(validateJsonAbs);
1351
1259
  addCheck(checks, {
1352
1260
  id: "output.validateJson",
@@ -1356,8 +1264,8 @@ async function createDoctorData(options) {
1356
1264
  details: { path: toRelativePath(root, validateJsonAbs) }
1357
1265
  });
1358
1266
  const outDirAbs = resolvePath(root, config, "outDir");
1359
- const rel = import_node_path11.default.relative(outDirAbs, validateJsonAbs);
1360
- const inside = rel !== "" && !rel.startsWith("..") && !import_node_path11.default.isAbsolute(rel);
1267
+ const rel = import_node_path10.default.relative(outDirAbs, validateJsonAbs);
1268
+ const inside = rel !== "" && !rel.startsWith("..") && !import_node_path10.default.isAbsolute(rel);
1361
1269
  addCheck(checks, {
1362
1270
  id: "output.pathAlignment",
1363
1271
  severity: inside ? "ok" : "warning",
@@ -1480,12 +1388,12 @@ async function detectOutDirCollisions(root) {
1480
1388
  });
1481
1389
  const configPaths = configScan.files;
1482
1390
  const configRoots = Array.from(
1483
- new Set(configPaths.map((configPath) => import_node_path11.default.dirname(configPath)))
1391
+ new Set(configPaths.map((configPath) => import_node_path10.default.dirname(configPath)))
1484
1392
  ).sort((a, b) => a.localeCompare(b));
1485
1393
  const outDirToRoots = /* @__PURE__ */ new Map();
1486
1394
  for (const configRoot of configRoots) {
1487
1395
  const { config } = await loadConfig(configRoot);
1488
- const outDir = import_node_path11.default.normalize(resolvePath(configRoot, config, "outDir"));
1396
+ const outDir = import_node_path10.default.normalize(resolvePath(configRoot, config, "outDir"));
1489
1397
  const roots = outDirToRoots.get(outDir) ?? /* @__PURE__ */ new Set();
1490
1398
  roots.add(configRoot);
1491
1399
  outDirToRoots.set(outDir, roots);
@@ -1511,20 +1419,20 @@ async function detectOutDirCollisions(root) {
1511
1419
  };
1512
1420
  }
1513
1421
  async function findMonorepoRoot(startDir) {
1514
- let current = import_node_path11.default.resolve(startDir);
1422
+ let current = import_node_path10.default.resolve(startDir);
1515
1423
  while (true) {
1516
- const gitPath = import_node_path11.default.join(current, ".git");
1517
- const workspacePath = import_node_path11.default.join(current, "pnpm-workspace.yaml");
1424
+ const gitPath = import_node_path10.default.join(current, ".git");
1425
+ const workspacePath = import_node_path10.default.join(current, "pnpm-workspace.yaml");
1518
1426
  if (await exists4(gitPath) || await exists4(workspacePath)) {
1519
1427
  return current;
1520
1428
  }
1521
- const parent = import_node_path11.default.dirname(current);
1429
+ const parent = import_node_path10.default.dirname(current);
1522
1430
  if (parent === current) {
1523
1431
  break;
1524
1432
  }
1525
1433
  current = parent;
1526
1434
  }
1527
- return import_node_path11.default.resolve(startDir);
1435
+ return import_node_path10.default.resolve(startDir);
1528
1436
  }
1529
1437
 
1530
1438
  // src/cli/lib/logger.ts
@@ -1566,9 +1474,9 @@ async function runDoctor(options) {
1566
1474
  const output = options.format === "json" ? formatDoctorJson(data) : formatDoctorText(data);
1567
1475
  const exitCode = shouldFailDoctor(data.summary, options.failOn) ? 1 : 0;
1568
1476
  if (options.outPath) {
1569
- const outAbs = import_node_path12.default.isAbsolute(options.outPath) ? options.outPath : import_node_path12.default.resolve(process.cwd(), options.outPath);
1570
- await (0, import_promises10.mkdir)(import_node_path12.default.dirname(outAbs), { recursive: true });
1571
- await (0, import_promises10.writeFile)(outAbs, `${output}
1477
+ const outAbs = import_node_path11.default.isAbsolute(options.outPath) ? options.outPath : import_node_path11.default.resolve(process.cwd(), options.outPath);
1478
+ await (0, import_promises9.mkdir)(import_node_path11.default.dirname(outAbs), { recursive: true });
1479
+ await (0, import_promises9.writeFile)(outAbs, `${output}
1572
1480
  `, "utf-8");
1573
1481
  info(`doctor: wrote ${outAbs}`);
1574
1482
  return exitCode;
@@ -1587,11 +1495,11 @@ function shouldFailDoctor(summary, failOn) {
1587
1495
  }
1588
1496
 
1589
1497
  // src/cli/commands/init.ts
1590
- var import_node_path14 = __toESM(require("path"), 1);
1498
+ var import_node_path13 = __toESM(require("path"), 1);
1591
1499
 
1592
1500
  // src/cli/lib/fs.ts
1593
- var import_promises11 = require("fs/promises");
1594
- var import_node_path13 = __toESM(require("path"), 1);
1501
+ var import_promises10 = require("fs/promises");
1502
+ var import_node_path12 = __toESM(require("path"), 1);
1595
1503
  async function copyTemplateTree(sourceRoot, destRoot, options) {
1596
1504
  const files = await collectTemplateFiles(sourceRoot);
1597
1505
  return copyFiles(files, sourceRoot, destRoot, options);
@@ -1599,7 +1507,7 @@ async function copyTemplateTree(sourceRoot, destRoot, options) {
1599
1507
  async function copyTemplatePaths(sourceRoot, destRoot, relativePaths, options) {
1600
1508
  const allFiles = [];
1601
1509
  for (const relPath of relativePaths) {
1602
- const fullPath = import_node_path13.default.join(sourceRoot, relPath);
1510
+ const fullPath = import_node_path12.default.join(sourceRoot, relPath);
1603
1511
  const files = await collectTemplateFiles(fullPath);
1604
1512
  allFiles.push(...files);
1605
1513
  }
@@ -1609,13 +1517,13 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1609
1517
  const copied = [];
1610
1518
  const skipped = [];
1611
1519
  const conflicts = [];
1612
- const protectPrefixes = (options.protect ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + import_node_path13.default.sep);
1613
- const excludePrefixes = (options.exclude ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + import_node_path13.default.sep);
1520
+ const protectPrefixes = (options.protect ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + import_node_path12.default.sep);
1521
+ const excludePrefixes = (options.exclude ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + import_node_path12.default.sep);
1614
1522
  const isProtectedRelative = (relative) => {
1615
1523
  if (protectPrefixes.length === 0) {
1616
1524
  return false;
1617
1525
  }
1618
- const normalized = relative.replace(/[\\/]+/g, import_node_path13.default.sep);
1526
+ const normalized = relative.replace(/[\\/]+/g, import_node_path12.default.sep);
1619
1527
  return protectPrefixes.some(
1620
1528
  (prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
1621
1529
  );
@@ -1624,7 +1532,7 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1624
1532
  if (excludePrefixes.length === 0) {
1625
1533
  return false;
1626
1534
  }
1627
- const normalized = relative.replace(/[\\/]+/g, import_node_path13.default.sep);
1535
+ const normalized = relative.replace(/[\\/]+/g, import_node_path12.default.sep);
1628
1536
  return excludePrefixes.some(
1629
1537
  (prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
1630
1538
  );
@@ -1632,14 +1540,14 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1632
1540
  const conflictPolicy = options.conflictPolicy ?? "error";
1633
1541
  if (!options.force && conflictPolicy === "error") {
1634
1542
  for (const file of files) {
1635
- const relative = import_node_path13.default.relative(sourceRoot, file);
1543
+ const relative = import_node_path12.default.relative(sourceRoot, file);
1636
1544
  if (isExcludedRelative(relative)) {
1637
1545
  continue;
1638
1546
  }
1639
1547
  if (isProtectedRelative(relative)) {
1640
1548
  continue;
1641
1549
  }
1642
- const dest = import_node_path13.default.join(destRoot, relative);
1550
+ const dest = import_node_path12.default.join(destRoot, relative);
1643
1551
  if (!await shouldWrite(dest, options.force)) {
1644
1552
  conflicts.push(dest);
1645
1553
  }
@@ -1649,19 +1557,19 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1649
1557
  }
1650
1558
  }
1651
1559
  for (const file of files) {
1652
- const relative = import_node_path13.default.relative(sourceRoot, file);
1560
+ const relative = import_node_path12.default.relative(sourceRoot, file);
1653
1561
  if (isExcludedRelative(relative)) {
1654
1562
  continue;
1655
1563
  }
1656
- const dest = import_node_path13.default.join(destRoot, relative);
1564
+ const dest = import_node_path12.default.join(destRoot, relative);
1657
1565
  const forceForThisFile = isProtectedRelative(relative) ? false : options.force;
1658
1566
  if (!await shouldWrite(dest, forceForThisFile)) {
1659
1567
  skipped.push(dest);
1660
1568
  continue;
1661
1569
  }
1662
1570
  if (!options.dryRun) {
1663
- await (0, import_promises11.mkdir)(import_node_path13.default.dirname(dest), { recursive: true });
1664
- await (0, import_promises11.copyFile)(file, dest);
1571
+ await (0, import_promises10.mkdir)(import_node_path12.default.dirname(dest), { recursive: true });
1572
+ await (0, import_promises10.copyFile)(file, dest);
1665
1573
  }
1666
1574
  copied.push(dest);
1667
1575
  }
@@ -1682,9 +1590,9 @@ async function collectTemplateFiles(root) {
1682
1590
  if (!await exists5(root)) {
1683
1591
  return entries;
1684
1592
  }
1685
- const items = await (0, import_promises11.readdir)(root, { withFileTypes: true });
1593
+ const items = await (0, import_promises10.readdir)(root, { withFileTypes: true });
1686
1594
  for (const item of items) {
1687
- const fullPath = import_node_path13.default.join(root, item.name);
1595
+ const fullPath = import_node_path12.default.join(root, item.name);
1688
1596
  if (item.isDirectory()) {
1689
1597
  const nested = await collectTemplateFiles(fullPath);
1690
1598
  entries.push(...nested);
@@ -1704,7 +1612,7 @@ async function shouldWrite(target, force) {
1704
1612
  }
1705
1613
  async function exists5(target) {
1706
1614
  try {
1707
- await (0, import_promises11.access)(target);
1615
+ await (0, import_promises10.access)(target);
1708
1616
  return true;
1709
1617
  } catch {
1710
1618
  return false;
@@ -1714,13 +1622,13 @@ async function exists5(target) {
1714
1622
  // src/cli/commands/init.ts
1715
1623
  async function runInit(options) {
1716
1624
  const assetsRoot = getInitAssetsDir();
1717
- const rootAssets = import_node_path14.default.join(assetsRoot, "root");
1718
- const qfaiAssets = import_node_path14.default.join(assetsRoot, ".qfai");
1719
- const destRoot = import_node_path14.default.resolve(options.dir);
1720
- const destQfai = import_node_path14.default.join(destRoot, ".qfai");
1625
+ const rootAssets = import_node_path13.default.join(assetsRoot, "root");
1626
+ const qfaiAssets = import_node_path13.default.join(assetsRoot, ".qfai");
1627
+ const destRoot = import_node_path13.default.resolve(options.dir);
1628
+ const destQfai = import_node_path13.default.join(destRoot, ".qfai");
1721
1629
  if (options.force) {
1722
1630
  info(
1723
- "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"
1631
+ "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"
1724
1632
  );
1725
1633
  }
1726
1634
  const rootResult = await copyTemplateTree(rootAssets, destRoot, {
@@ -1732,18 +1640,18 @@ async function runInit(options) {
1732
1640
  force: false,
1733
1641
  dryRun: options.dryRun,
1734
1642
  conflictPolicy: "skip",
1735
- protect: ["prompts.local"],
1736
- exclude: ["prompts"]
1643
+ protect: ["assistant/prompts.local"],
1644
+ exclude: ["assistant/prompts"]
1737
1645
  });
1738
1646
  const promptsResult = await copyTemplatePaths(
1739
1647
  qfaiAssets,
1740
1648
  destQfai,
1741
- ["prompts"],
1649
+ ["assistant/prompts"],
1742
1650
  {
1743
1651
  force: options.force,
1744
1652
  dryRun: options.dryRun,
1745
1653
  conflictPolicy: "skip",
1746
- protect: ["prompts.local"]
1654
+ protect: ["assistant/prompts.local"]
1747
1655
  }
1748
1656
  );
1749
1657
  report(
@@ -1764,8 +1672,8 @@ function report(copied, skipped, dryRun, label) {
1764
1672
  }
1765
1673
 
1766
1674
  // src/cli/commands/report.ts
1767
- var import_promises20 = require("fs/promises");
1768
- var import_node_path22 = __toESM(require("path"), 1);
1675
+ var import_promises19 = require("fs/promises");
1676
+ var import_node_path21 = __toESM(require("path"), 1);
1769
1677
 
1770
1678
  // src/core/normalize.ts
1771
1679
  function normalizeIssuePaths(root, issues) {
@@ -1805,12 +1713,12 @@ function normalizeValidationResult(root, result) {
1805
1713
  }
1806
1714
 
1807
1715
  // src/core/report.ts
1808
- var import_promises19 = require("fs/promises");
1809
- var import_node_path21 = __toESM(require("path"), 1);
1716
+ var import_promises18 = require("fs/promises");
1717
+ var import_node_path20 = __toESM(require("path"), 1);
1810
1718
 
1811
1719
  // src/core/contractIndex.ts
1812
- var import_promises12 = require("fs/promises");
1813
- var import_node_path15 = __toESM(require("path"), 1);
1720
+ var import_promises11 = require("fs/promises");
1721
+ var import_node_path14 = __toESM(require("path"), 1);
1814
1722
 
1815
1723
  // src/core/contractsDecl.ts
1816
1724
  var CONTRACT_DECLARATION_RE = /^\s*(?:#|\/\/|--|\/\*+|\*+)?\s*QFAI-CONTRACT-ID:\s*((?:API|UI|DB)-\d{4}|THEMA-\d{3})\s*(?:\*\/)?\s*$/gm;
@@ -1832,9 +1740,9 @@ function stripContractDeclarationLines(text) {
1832
1740
  // src/core/contractIndex.ts
1833
1741
  async function buildContractIndex(root, config) {
1834
1742
  const contractsRoot = resolvePath(root, config, "contractsDir");
1835
- const uiRoot = import_node_path15.default.join(contractsRoot, "ui");
1836
- const apiRoot = import_node_path15.default.join(contractsRoot, "api");
1837
- const dbRoot = import_node_path15.default.join(contractsRoot, "db");
1743
+ const uiRoot = import_node_path14.default.join(contractsRoot, "ui");
1744
+ const apiRoot = import_node_path14.default.join(contractsRoot, "api");
1745
+ const dbRoot = import_node_path14.default.join(contractsRoot, "db");
1838
1746
  const [uiFiles, themaFiles, apiFiles, dbFiles] = await Promise.all([
1839
1747
  collectUiContractFiles(uiRoot),
1840
1748
  collectThemaContractFiles(uiRoot),
@@ -1854,7 +1762,7 @@ async function buildContractIndex(root, config) {
1854
1762
  }
1855
1763
  async function indexContractFiles(files, index) {
1856
1764
  for (const file of files) {
1857
- const text = await (0, import_promises12.readFile)(file, "utf-8");
1765
+ const text = await (0, import_promises11.readFile)(file, "utf-8");
1858
1766
  extractDeclaredContractIds(text).forEach((id) => record(index, id, file));
1859
1767
  }
1860
1768
  }
@@ -2099,14 +2007,14 @@ function parseSpec(md, file) {
2099
2007
  }
2100
2008
 
2101
2009
  // src/core/validators/contracts.ts
2102
- var import_promises13 = require("fs/promises");
2103
- var import_node_path17 = __toESM(require("path"), 1);
2010
+ var import_promises12 = require("fs/promises");
2011
+ var import_node_path16 = __toESM(require("path"), 1);
2104
2012
 
2105
2013
  // src/core/contracts.ts
2106
- var import_node_path16 = __toESM(require("path"), 1);
2014
+ var import_node_path15 = __toESM(require("path"), 1);
2107
2015
  var import_yaml2 = require("yaml");
2108
2016
  function parseStructuredContract(file, text) {
2109
- const ext = import_node_path16.default.extname(file).toLowerCase();
2017
+ const ext = import_node_path15.default.extname(file).toLowerCase();
2110
2018
  if (ext === ".json") {
2111
2019
  return JSON.parse(text);
2112
2020
  }
@@ -2128,14 +2036,14 @@ async function validateContracts(root, config) {
2128
2036
  const issues = [];
2129
2037
  const contractIndex = await buildContractIndex(root, config);
2130
2038
  const contractsRoot = resolvePath(root, config, "contractsDir");
2131
- const uiRoot = import_node_path17.default.join(contractsRoot, "ui");
2039
+ const uiRoot = import_node_path16.default.join(contractsRoot, "ui");
2132
2040
  const themaIds = new Set(
2133
2041
  Array.from(contractIndex.ids).filter((id) => id.startsWith("THEMA-"))
2134
2042
  );
2135
2043
  issues.push(...await validateUiContracts(uiRoot, themaIds));
2136
2044
  issues.push(...await validateThemaContracts(uiRoot));
2137
- issues.push(...await validateApiContracts(import_node_path17.default.join(contractsRoot, "api")));
2138
- issues.push(...await validateDbContracts(import_node_path17.default.join(contractsRoot, "db")));
2045
+ issues.push(...await validateApiContracts(import_node_path16.default.join(contractsRoot, "api")));
2046
+ issues.push(...await validateDbContracts(import_node_path16.default.join(contractsRoot, "db")));
2139
2047
  issues.push(...validateDuplicateContractIds(contractIndex));
2140
2048
  return issues;
2141
2049
  }
@@ -2154,7 +2062,7 @@ async function validateUiContracts(uiRoot, themaIds) {
2154
2062
  }
2155
2063
  const issues = [];
2156
2064
  for (const file of files) {
2157
- const text = await (0, import_promises13.readFile)(file, "utf-8");
2065
+ const text = await (0, import_promises12.readFile)(file, "utf-8");
2158
2066
  const declaredIds = extractDeclaredContractIds(text);
2159
2067
  issues.push(...validateDeclaredContractIds(declaredIds, file, "UI"));
2160
2068
  let doc = null;
@@ -2208,7 +2116,7 @@ async function validateThemaContracts(uiRoot) {
2208
2116
  }
2209
2117
  const issues = [];
2210
2118
  for (const file of files) {
2211
- const text = await (0, import_promises13.readFile)(file, "utf-8");
2119
+ const text = await (0, import_promises12.readFile)(file, "utf-8");
2212
2120
  const invalidIds = extractInvalidIds(text, [
2213
2121
  "SPEC",
2214
2122
  "BR",
@@ -2342,7 +2250,7 @@ async function validateApiContracts(apiRoot) {
2342
2250
  }
2343
2251
  const issues = [];
2344
2252
  for (const file of files) {
2345
- const text = await (0, import_promises13.readFile)(file, "utf-8");
2253
+ const text = await (0, import_promises12.readFile)(file, "utf-8");
2346
2254
  const invalidIds = extractInvalidIds(text, [
2347
2255
  "SPEC",
2348
2256
  "BR",
@@ -2411,7 +2319,7 @@ async function validateDbContracts(dbRoot) {
2411
2319
  }
2412
2320
  const issues = [];
2413
2321
  for (const file of files) {
2414
- const text = await (0, import_promises13.readFile)(file, "utf-8");
2322
+ const text = await (0, import_promises12.readFile)(file, "utf-8");
2415
2323
  const invalidIds = extractInvalidIds(text, [
2416
2324
  "SPEC",
2417
2325
  "BR",
@@ -2608,9 +2516,9 @@ async function validateUiAssets(assets, file, uiRoot) {
2608
2516
  );
2609
2517
  return issues;
2610
2518
  }
2611
- const packDir = import_node_path17.default.resolve(uiRoot, packValue);
2612
- const packRelative = import_node_path17.default.relative(uiRoot, packDir);
2613
- if (packRelative.startsWith("..") || import_node_path17.default.isAbsolute(packRelative)) {
2519
+ const packDir = import_node_path16.default.resolve(uiRoot, packValue);
2520
+ const packRelative = import_node_path16.default.relative(uiRoot, packDir);
2521
+ if (packRelative.startsWith("..") || import_node_path16.default.isAbsolute(packRelative)) {
2614
2522
  issues.push(
2615
2523
  issue(
2616
2524
  "QFAI-ASSET-001",
@@ -2636,7 +2544,7 @@ async function validateUiAssets(assets, file, uiRoot) {
2636
2544
  );
2637
2545
  return issues;
2638
2546
  }
2639
- const assetsYamlPath = import_node_path17.default.join(packDir, "assets.yaml");
2547
+ const assetsYamlPath = import_node_path16.default.join(packDir, "assets.yaml");
2640
2548
  if (!await exists6(assetsYamlPath)) {
2641
2549
  issues.push(
2642
2550
  issue(
@@ -2651,7 +2559,7 @@ async function validateUiAssets(assets, file, uiRoot) {
2651
2559
  }
2652
2560
  let manifest;
2653
2561
  try {
2654
- const manifestText = await (0, import_promises13.readFile)(assetsYamlPath, "utf-8");
2562
+ const manifestText = await (0, import_promises12.readFile)(assetsYamlPath, "utf-8");
2655
2563
  manifest = parseStructuredContract(assetsYamlPath, manifestText);
2656
2564
  } catch (error2) {
2657
2565
  issues.push(
@@ -2724,9 +2632,9 @@ async function validateUiAssets(assets, file, uiRoot) {
2724
2632
  );
2725
2633
  continue;
2726
2634
  }
2727
- const assetPath = import_node_path17.default.resolve(packDir, entry.path);
2728
- const assetRelative = import_node_path17.default.relative(packDir, assetPath);
2729
- if (assetRelative.startsWith("..") || import_node_path17.default.isAbsolute(assetRelative)) {
2635
+ const assetPath = import_node_path16.default.resolve(packDir, entry.path);
2636
+ const assetRelative = import_node_path16.default.relative(packDir, assetPath);
2637
+ if (assetRelative.startsWith("..") || import_node_path16.default.isAbsolute(assetRelative)) {
2730
2638
  issues.push(
2731
2639
  issue(
2732
2640
  "QFAI-ASSET-004",
@@ -2767,7 +2675,7 @@ function shouldIgnoreInvalidId(value, doc) {
2767
2675
  return false;
2768
2676
  }
2769
2677
  const normalized = packValue.replace(/\\/g, "/");
2770
- const basename = import_node_path17.default.posix.basename(normalized);
2678
+ const basename = import_node_path16.default.posix.basename(normalized);
2771
2679
  if (!basename) {
2772
2680
  return false;
2773
2681
  }
@@ -2777,7 +2685,7 @@ function isSafeRelativePath(value) {
2777
2685
  if (!value) {
2778
2686
  return false;
2779
2687
  }
2780
- if (import_node_path17.default.isAbsolute(value)) {
2688
+ if (import_node_path16.default.isAbsolute(value)) {
2781
2689
  return false;
2782
2690
  }
2783
2691
  const normalized = value.replace(/\\/g, "/");
@@ -2792,7 +2700,7 @@ function isSafeRelativePath(value) {
2792
2700
  }
2793
2701
  async function exists6(target) {
2794
2702
  try {
2795
- await (0, import_promises13.access)(target);
2703
+ await (0, import_promises12.access)(target);
2796
2704
  return true;
2797
2705
  } catch {
2798
2706
  return false;
@@ -2827,8 +2735,8 @@ function issue(code, message, severity, file, rule, refs, category = "compatibil
2827
2735
  }
2828
2736
 
2829
2737
  // src/core/validators/delta.ts
2830
- var import_promises14 = require("fs/promises");
2831
- var import_node_path18 = __toESM(require("path"), 1);
2738
+ var import_promises13 = require("fs/promises");
2739
+ var import_node_path17 = __toESM(require("path"), 1);
2832
2740
  async function validateDeltas(root, config) {
2833
2741
  const specsRoot = resolvePath(root, config, "specsDir");
2834
2742
  const packs = await collectSpecPackDirs(specsRoot);
@@ -2837,9 +2745,9 @@ async function validateDeltas(root, config) {
2837
2745
  }
2838
2746
  const issues = [];
2839
2747
  for (const pack of packs) {
2840
- const deltaPath = import_node_path18.default.join(pack, "delta.md");
2748
+ const deltaPath = import_node_path17.default.join(pack, "delta.md");
2841
2749
  try {
2842
- await (0, import_promises14.readFile)(deltaPath, "utf-8");
2750
+ await (0, import_promises13.readFile)(deltaPath, "utf-8");
2843
2751
  } catch (error2) {
2844
2752
  if (isMissingFileError2(error2)) {
2845
2753
  issues.push(
@@ -2890,8 +2798,8 @@ function issue2(code, message, severity, file, rule, refs, category = "change",
2890
2798
  }
2891
2799
 
2892
2800
  // src/core/validators/ids.ts
2893
- var import_promises15 = require("fs/promises");
2894
- var import_node_path19 = __toESM(require("path"), 1);
2801
+ var import_promises14 = require("fs/promises");
2802
+ var import_node_path18 = __toESM(require("path"), 1);
2895
2803
  var SC_TAG_RE3 = /^SC-\d{4}$/;
2896
2804
  async function validateDefinedIds(root, config) {
2897
2805
  const issues = [];
@@ -2926,7 +2834,7 @@ async function validateDefinedIds(root, config) {
2926
2834
  }
2927
2835
  async function collectSpecDefinitionIds(files, out) {
2928
2836
  for (const file of files) {
2929
- const text = await (0, import_promises15.readFile)(file, "utf-8");
2837
+ const text = await (0, import_promises14.readFile)(file, "utf-8");
2930
2838
  const parsed = parseSpec(text, file);
2931
2839
  if (parsed.specId) {
2932
2840
  recordId(out, parsed.specId, file);
@@ -2936,7 +2844,7 @@ async function collectSpecDefinitionIds(files, out) {
2936
2844
  }
2937
2845
  async function collectScenarioDefinitionIds(files, out) {
2938
2846
  for (const file of files) {
2939
- const text = await (0, import_promises15.readFile)(file, "utf-8");
2847
+ const text = await (0, import_promises14.readFile)(file, "utf-8");
2940
2848
  const { document, errors } = parseScenarioDocument(text, file);
2941
2849
  if (!document || errors.length > 0) {
2942
2850
  continue;
@@ -2957,7 +2865,7 @@ function recordId(out, id, file) {
2957
2865
  }
2958
2866
  function formatFileList(files, root) {
2959
2867
  return files.map((file) => {
2960
- const relative = import_node_path19.default.relative(root, file);
2868
+ const relative = import_node_path18.default.relative(root, file);
2961
2869
  return relative.length > 0 ? relative : file;
2962
2870
  }).join(", ");
2963
2871
  }
@@ -2984,8 +2892,8 @@ function issue3(code, message, severity, file, rule, refs, category = "compatibi
2984
2892
  }
2985
2893
 
2986
2894
  // src/core/validators/promptsIntegrity.ts
2987
- async function validatePromptsIntegrity(root) {
2988
- const diff = await diffProjectPromptsAgainstInitAssets(root);
2895
+ async function validatePromptsIntegrity(root, config) {
2896
+ const diff = await diffProjectPromptsAgainstInitAssets(root, config);
2989
2897
  if (diff.status !== "modified") {
2990
2898
  return [];
2991
2899
  }
@@ -3002,11 +2910,11 @@ async function validatePromptsIntegrity(root) {
3002
2910
  code: "QFAI-PROMPTS-001",
3003
2911
  severity: "error",
3004
2912
  category: "change",
3005
- message: `\u6A19\u6E96\u8CC7\u7523 '.qfai/prompts/**' \u304C\u6539\u5909\u3055\u308C\u3066\u3044\u307E\u3059\uFF08${hints || `\u5DEE\u5206=${total}`}\uFF09\u3002${sampleText}`,
2913
+ 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}`,
3006
2914
  suggested_action: [
3007
2915
  "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",
3008
2916
  "\u6B21\u306E\u3044\u305A\u308C\u304B\u3092\u5B9F\u65BD\u3057\u3066\u304F\u3060\u3055\u3044:",
3009
- "- \u5909\u66F4\u3057\u305F\u3044\u5834\u5408: \u540C\u4E00\u76F8\u5BFE\u30D1\u30B9\u3067 '.qfai/prompts.local/**' \u306B\u7F6E\u3044\u3066 overlay",
2917
+ "- \u5909\u66F4\u3057\u305F\u3044\u5834\u5408: \u540C\u4E00\u76F8\u5BFE\u30D1\u30B9\u3067 '.qfai/assistant/prompts.local/**' \u306B\u7F6E\u3044\u3066 overlay",
3010
2918
  "- \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"
3011
2919
  ].join("\n"),
3012
2920
  rule: "prompts.integrity"
@@ -3015,8 +2923,8 @@ async function validatePromptsIntegrity(root) {
3015
2923
  }
3016
2924
 
3017
2925
  // src/core/validators/scenario.ts
3018
- var import_promises16 = require("fs/promises");
3019
- var import_node_path20 = __toESM(require("path"), 1);
2926
+ var import_promises15 = require("fs/promises");
2927
+ var import_node_path19 = __toESM(require("path"), 1);
3020
2928
  var GIVEN_PATTERN = /\bGiven\b/;
3021
2929
  var WHEN_PATTERN = /\bWhen\b/;
3022
2930
  var THEN_PATTERN = /\bThen\b/;
@@ -3039,7 +2947,7 @@ async function validateScenarios(root, config) {
3039
2947
  }
3040
2948
  const issues = [];
3041
2949
  for (const entry of entries) {
3042
- const legacyScenarioPath = import_node_path20.default.join(entry.dir, "scenario.md");
2950
+ const legacyScenarioPath = import_node_path19.default.join(entry.dir, "scenario.md");
3043
2951
  if (await fileExists(legacyScenarioPath)) {
3044
2952
  issues.push(
3045
2953
  issue4(
@@ -3053,7 +2961,7 @@ async function validateScenarios(root, config) {
3053
2961
  }
3054
2962
  let text;
3055
2963
  try {
3056
- text = await (0, import_promises16.readFile)(entry.scenarioPath, "utf-8");
2964
+ text = await (0, import_promises15.readFile)(entry.scenarioPath, "utf-8");
3057
2965
  } catch (error2) {
3058
2966
  if (isMissingFileError3(error2)) {
3059
2967
  issues.push(
@@ -3228,7 +3136,7 @@ function isMissingFileError3(error2) {
3228
3136
  }
3229
3137
  async function fileExists(target) {
3230
3138
  try {
3231
- await (0, import_promises16.access)(target);
3139
+ await (0, import_promises15.access)(target);
3232
3140
  return true;
3233
3141
  } catch {
3234
3142
  return false;
@@ -3236,7 +3144,7 @@ async function fileExists(target) {
3236
3144
  }
3237
3145
 
3238
3146
  // src/core/validators/spec.ts
3239
- var import_promises17 = require("fs/promises");
3147
+ var import_promises16 = require("fs/promises");
3240
3148
  async function validateSpecs(root, config) {
3241
3149
  const specsRoot = resolvePath(root, config, "specsDir");
3242
3150
  const entries = await collectSpecEntries(specsRoot);
@@ -3257,7 +3165,7 @@ async function validateSpecs(root, config) {
3257
3165
  for (const entry of entries) {
3258
3166
  let text;
3259
3167
  try {
3260
- text = await (0, import_promises17.readFile)(entry.specPath, "utf-8");
3168
+ text = await (0, import_promises16.readFile)(entry.specPath, "utf-8");
3261
3169
  } catch (error2) {
3262
3170
  if (isMissingFileError4(error2)) {
3263
3171
  issues.push(
@@ -3411,7 +3319,7 @@ function isMissingFileError4(error2) {
3411
3319
  }
3412
3320
 
3413
3321
  // src/core/validators/traceability.ts
3414
- var import_promises18 = require("fs/promises");
3322
+ var import_promises17 = require("fs/promises");
3415
3323
  var SPEC_TAG_RE3 = /^SPEC-\d{4}$/;
3416
3324
  var BR_TAG_RE2 = /^BR-\d{4}$/;
3417
3325
  async function validateTraceability(root, config) {
@@ -3431,7 +3339,7 @@ async function validateTraceability(root, config) {
3431
3339
  const contractIndex = await buildContractIndex(root, config);
3432
3340
  const contractIds = contractIndex.ids;
3433
3341
  for (const file of specFiles) {
3434
- const text = await (0, import_promises18.readFile)(file, "utf-8");
3342
+ const text = await (0, import_promises17.readFile)(file, "utf-8");
3435
3343
  extractAllIds(text).forEach((id) => upstreamIds.add(id));
3436
3344
  const parsed = parseSpec(text, file);
3437
3345
  if (parsed.specId) {
@@ -3504,7 +3412,7 @@ async function validateTraceability(root, config) {
3504
3412
  }
3505
3413
  }
3506
3414
  for (const file of scenarioFiles) {
3507
- const text = await (0, import_promises18.readFile)(file, "utf-8");
3415
+ const text = await (0, import_promises17.readFile)(file, "utf-8");
3508
3416
  extractAllIds(text).forEach((id) => upstreamIds.add(id));
3509
3417
  const scenarioContractRefs = parseContractRefs(text, {
3510
3418
  allowCommentPrefix: true
@@ -3826,7 +3734,7 @@ async function validateCodeReferences(upstreamIds, srcRoot, testsRoot) {
3826
3734
  const pattern = buildIdPattern(Array.from(upstreamIds));
3827
3735
  let found = false;
3828
3736
  for (const file of targetFiles) {
3829
- const text = await (0, import_promises18.readFile)(file, "utf-8");
3737
+ const text = await (0, import_promises17.readFile)(file, "utf-8");
3830
3738
  if (pattern.test(text)) {
3831
3739
  found = true;
3832
3740
  break;
@@ -3877,7 +3785,7 @@ async function validateProject(root, configResult) {
3877
3785
  const { config, issues: configIssues } = resolved;
3878
3786
  const issues = [
3879
3787
  ...configIssues,
3880
- ...await validatePromptsIntegrity(root),
3788
+ ...await validatePromptsIntegrity(root, config),
3881
3789
  ...await validateSpecs(root, config),
3882
3790
  ...await validateDeltas(root, config),
3883
3791
  ...await validateScenarios(root, config),
@@ -3926,15 +3834,15 @@ var ID_PREFIXES2 = [
3926
3834
  "THEMA"
3927
3835
  ];
3928
3836
  async function createReportData(root, validation, configResult) {
3929
- const resolvedRoot = import_node_path21.default.resolve(root);
3837
+ const resolvedRoot = import_node_path20.default.resolve(root);
3930
3838
  const resolved = configResult ?? await loadConfig(resolvedRoot);
3931
3839
  const config = resolved.config;
3932
3840
  const configPath = resolved.configPath;
3933
3841
  const specsRoot = resolvePath(resolvedRoot, config, "specsDir");
3934
3842
  const contractsRoot = resolvePath(resolvedRoot, config, "contractsDir");
3935
- const apiRoot = import_node_path21.default.join(contractsRoot, "api");
3936
- const uiRoot = import_node_path21.default.join(contractsRoot, "ui");
3937
- const dbRoot = import_node_path21.default.join(contractsRoot, "db");
3843
+ const apiRoot = import_node_path20.default.join(contractsRoot, "api");
3844
+ const uiRoot = import_node_path20.default.join(contractsRoot, "ui");
3845
+ const dbRoot = import_node_path20.default.join(contractsRoot, "db");
3938
3846
  const srcRoot = resolvePath(resolvedRoot, config, "srcDir");
3939
3847
  const testsRoot = resolvePath(resolvedRoot, config, "testsDir");
3940
3848
  const specFiles = await collectSpecFiles(specsRoot);
@@ -4400,7 +4308,9 @@ function formatReportMarkdown(data, options = {}) {
4400
4308
  );
4401
4309
  }
4402
4310
  lines.push("- \u5909\u66F4\u5185\u5BB9\u30FB\u53D7\u5165\u89B3\u70B9\u306F `.qfai/specs/*/delta.md` \u306B\u8A18\u9332\u3057\u307E\u3059\u3002");
4403
- lines.push("- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/promptpack/steering/traceability.md`");
4311
+ lines.push(
4312
+ "- \u53C2\u7167\u30EB\u30FC\u30EB\u306E\u6B63\u672C: `.qfai/assistant/instructions/constitution.md`"
4313
+ );
4404
4314
  return lines.join("\n");
4405
4315
  }
4406
4316
  function formatReportJson(data) {
@@ -4414,7 +4324,7 @@ async function collectSpecContractRefs(specFiles, contractIdList) {
4414
4324
  idToSpecs.set(contractId, /* @__PURE__ */ new Set());
4415
4325
  }
4416
4326
  for (const file of specFiles) {
4417
- const text = await (0, import_promises19.readFile)(file, "utf-8");
4327
+ const text = await (0, import_promises18.readFile)(file, "utf-8");
4418
4328
  const parsed = parseSpec(text, file);
4419
4329
  const specKey = parsed.specId;
4420
4330
  if (!specKey) {
@@ -4456,7 +4366,7 @@ async function collectIds(files) {
4456
4366
  THEMA: /* @__PURE__ */ new Set()
4457
4367
  };
4458
4368
  for (const file of files) {
4459
- const text = await (0, import_promises19.readFile)(file, "utf-8");
4369
+ const text = await (0, import_promises18.readFile)(file, "utf-8");
4460
4370
  for (const prefix of ID_PREFIXES2) {
4461
4371
  const ids = extractIds(text, prefix);
4462
4372
  ids.forEach((id) => result[prefix].add(id));
@@ -4475,7 +4385,7 @@ async function collectIds(files) {
4475
4385
  async function collectUpstreamIds(files) {
4476
4386
  const ids = /* @__PURE__ */ new Set();
4477
4387
  for (const file of files) {
4478
- const text = await (0, import_promises19.readFile)(file, "utf-8");
4388
+ const text = await (0, import_promises18.readFile)(file, "utf-8");
4479
4389
  extractAllIds(text).forEach((id) => ids.add(id));
4480
4390
  }
4481
4391
  return ids;
@@ -4496,7 +4406,7 @@ async function evaluateTraceability(upstreamIds, srcRoot, testsRoot) {
4496
4406
  }
4497
4407
  const pattern = buildIdPattern2(Array.from(upstreamIds));
4498
4408
  for (const file of targetFiles) {
4499
- const text = await (0, import_promises19.readFile)(file, "utf-8");
4409
+ const text = await (0, import_promises18.readFile)(file, "utf-8");
4500
4410
  if (pattern.test(text)) {
4501
4411
  return true;
4502
4412
  }
@@ -4633,7 +4543,7 @@ function warnIfTruncated(scan, context) {
4633
4543
 
4634
4544
  // src/cli/commands/report.ts
4635
4545
  async function runReport(options) {
4636
- const root = import_node_path22.default.resolve(options.root);
4546
+ const root = import_node_path21.default.resolve(options.root);
4637
4547
  const configResult = await loadConfig(root);
4638
4548
  let validation;
4639
4549
  if (options.runValidate) {
@@ -4650,7 +4560,7 @@ async function runReport(options) {
4650
4560
  validation = normalized;
4651
4561
  } else {
4652
4562
  const input = options.inputPath ?? configResult.config.output.validateJsonPath;
4653
- const inputPath = import_node_path22.default.isAbsolute(input) ? input : import_node_path22.default.resolve(root, input);
4563
+ const inputPath = import_node_path21.default.isAbsolute(input) ? input : import_node_path21.default.resolve(root, input);
4654
4564
  try {
4655
4565
  validation = await readValidationResult(inputPath);
4656
4566
  } catch (err) {
@@ -4661,7 +4571,7 @@ async function runReport(options) {
4661
4571
  "",
4662
4572
  "\u307E\u305A qfai validate \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B:",
4663
4573
  " qfai validate",
4664
- "\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u51FA\u529B\u5148: .qfai/out/validate.json\uFF09",
4574
+ "\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u51FA\u529B\u5148: .qfai/report/validate.json\uFF09",
4665
4575
  "",
4666
4576
  "\u307E\u305F\u306F report \u306B --run-validate \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
4667
4577
  "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"
@@ -4677,11 +4587,11 @@ async function runReport(options) {
4677
4587
  warnIfTruncated(data.traceability.testFiles, "report");
4678
4588
  const output = options.format === "json" ? formatReportJson(data) : options.baseUrl ? formatReportMarkdown(data, { baseUrl: options.baseUrl }) : formatReportMarkdown(data);
4679
4589
  const outRoot = resolvePath(root, configResult.config, "outDir");
4680
- const defaultOut = options.format === "json" ? import_node_path22.default.join(outRoot, "report.json") : import_node_path22.default.join(outRoot, "report.md");
4590
+ const defaultOut = options.format === "json" ? import_node_path21.default.join(outRoot, "report.json") : import_node_path21.default.join(outRoot, "report.md");
4681
4591
  const out = options.outPath ?? defaultOut;
4682
- const outPath = import_node_path22.default.isAbsolute(out) ? out : import_node_path22.default.resolve(root, out);
4683
- await (0, import_promises20.mkdir)(import_node_path22.default.dirname(outPath), { recursive: true });
4684
- await (0, import_promises20.writeFile)(outPath, `${output}
4592
+ const outPath = import_node_path21.default.isAbsolute(out) ? out : import_node_path21.default.resolve(root, out);
4593
+ await (0, import_promises19.mkdir)(import_node_path21.default.dirname(outPath), { recursive: true });
4594
+ await (0, import_promises19.writeFile)(outPath, `${output}
4685
4595
  `, "utf-8");
4686
4596
  info(
4687
4597
  `report: info=${validation.counts.info} warning=${validation.counts.warning} error=${validation.counts.error}`
@@ -4689,7 +4599,7 @@ async function runReport(options) {
4689
4599
  info(`wrote report: ${outPath}`);
4690
4600
  }
4691
4601
  async function readValidationResult(inputPath) {
4692
- const raw = await (0, import_promises20.readFile)(inputPath, "utf-8");
4602
+ const raw = await (0, import_promises19.readFile)(inputPath, "utf-8");
4693
4603
  const parsed = JSON.parse(raw);
4694
4604
  if (!isValidationResult(parsed)) {
4695
4605
  throw new Error(`validate.json \u306E\u5F62\u5F0F\u304C\u4E0D\u6B63\u3067\u3059: ${inputPath}`);
@@ -4745,15 +4655,15 @@ function isMissingFileError5(error2) {
4745
4655
  return record2.code === "ENOENT";
4746
4656
  }
4747
4657
  async function writeValidationResult(root, outputPath, result) {
4748
- const abs = import_node_path22.default.isAbsolute(outputPath) ? outputPath : import_node_path22.default.resolve(root, outputPath);
4749
- await (0, import_promises20.mkdir)(import_node_path22.default.dirname(abs), { recursive: true });
4750
- await (0, import_promises20.writeFile)(abs, `${JSON.stringify(result, null, 2)}
4658
+ const abs = import_node_path21.default.isAbsolute(outputPath) ? outputPath : import_node_path21.default.resolve(root, outputPath);
4659
+ await (0, import_promises19.mkdir)(import_node_path21.default.dirname(abs), { recursive: true });
4660
+ await (0, import_promises19.writeFile)(abs, `${JSON.stringify(result, null, 2)}
4751
4661
  `, "utf-8");
4752
4662
  }
4753
4663
 
4754
4664
  // src/cli/commands/validate.ts
4755
- var import_promises21 = require("fs/promises");
4756
- var import_node_path23 = __toESM(require("path"), 1);
4665
+ var import_promises20 = require("fs/promises");
4666
+ var import_node_path22 = __toESM(require("path"), 1);
4757
4667
 
4758
4668
  // src/cli/lib/failOn.ts
4759
4669
  function shouldFail(result, failOn) {
@@ -4768,7 +4678,7 @@ function shouldFail(result, failOn) {
4768
4678
 
4769
4679
  // src/cli/commands/validate.ts
4770
4680
  async function runValidate(options) {
4771
- const root = import_node_path23.default.resolve(options.root);
4681
+ const root = import_node_path22.default.resolve(options.root);
4772
4682
  const configResult = await loadConfig(root);
4773
4683
  const result = await validateProject(root, configResult);
4774
4684
  const normalized = normalizeValidationResult(root, result);
@@ -4893,12 +4803,12 @@ function issueKey(issue7) {
4893
4803
  }
4894
4804
  async function emitJson(result, root, jsonPath) {
4895
4805
  const abs = resolveJsonPath(root, jsonPath);
4896
- await (0, import_promises21.mkdir)(import_node_path23.default.dirname(abs), { recursive: true });
4897
- await (0, import_promises21.writeFile)(abs, `${JSON.stringify(result, null, 2)}
4806
+ await (0, import_promises20.mkdir)(import_node_path22.default.dirname(abs), { recursive: true });
4807
+ await (0, import_promises20.writeFile)(abs, `${JSON.stringify(result, null, 2)}
4898
4808
  `, "utf-8");
4899
4809
  }
4900
4810
  function resolveJsonPath(root, jsonPath) {
4901
- return import_node_path23.default.isAbsolute(jsonPath) ? jsonPath : import_node_path23.default.resolve(root, jsonPath);
4811
+ return import_node_path22.default.isAbsolute(jsonPath) ? jsonPath : import_node_path22.default.resolve(root, jsonPath);
4902
4812
  }
4903
4813
  var GITHUB_ANNOTATION_LIMIT = 100;
4904
4814
 
@@ -4911,7 +4821,6 @@ function parseArgs(argv, cwd) {
4911
4821
  force: false,
4912
4822
  yes: false,
4913
4823
  dryRun: false,
4914
- analyzeList: false,
4915
4824
  reportFormat: "md",
4916
4825
  reportRunValidate: false,
4917
4826
  doctorFormat: "text",
@@ -4963,18 +4872,6 @@ function parseArgs(argv, cwd) {
4963
4872
  case "--dry-run":
4964
4873
  options.dryRun = true;
4965
4874
  break;
4966
- case "--list":
4967
- options.analyzeList = true;
4968
- break;
4969
- case "--prompt":
4970
- {
4971
- const next = readOptionValue(args, i);
4972
- if (next) {
4973
- options.analyzePrompt = next;
4974
- i += 1;
4975
- }
4976
- }
4977
- break;
4978
4875
  case "--format": {
4979
4876
  const next = readOptionValue(args, i);
4980
4877
  if (next === null) {
@@ -5108,17 +5005,6 @@ async function run(argv, cwd) {
5108
5005
  yes: options.yes
5109
5006
  });
5110
5007
  return;
5111
- case "analyze":
5112
- {
5113
- const resolvedRoot = await resolveRoot(options);
5114
- const exitCode = await runAnalyze({
5115
- root: resolvedRoot,
5116
- list: options.analyzeList,
5117
- ...options.analyzePrompt !== void 0 ? { prompt: options.analyzePrompt } : {}
5118
- });
5119
- process.exitCode = exitCode;
5120
- }
5121
- return;
5122
5008
  case "validate":
5123
5009
  {
5124
5010
  const resolvedRoot = await resolveRoot(options);
@@ -5166,7 +5052,6 @@ function usage() {
5166
5052
 
5167
5053
  Commands:
5168
5054
  init \u30C6\u30F3\u30D7\u30EC\u3092\u751F\u6210
5169
- analyze \u610F\u5473\u30EC\u30D9\u30EB\u306E\u30EC\u30D3\u30E5\u30FC\u88DC\u52A9\uFF08\u30D7\u30ED\u30F3\u30D7\u30C8\u51FA\u529B\uFF09
5170
5055
  validate \u4ED5\u69D8/\u5951\u7D04/\u53C2\u7167\u306E\u691C\u67FB
5171
5056
  report \u691C\u8A3C\u7D50\u679C\u3068\u96C6\u8A08\u3092\u51FA\u529B
5172
5057
  doctor \u8A2D\u5B9A/\u30D1\u30B9/\u51FA\u529B\u524D\u63D0\u306E\u8A3A\u65AD
@@ -5174,11 +5059,9 @@ Commands:
5174
5059
  Options:
5175
5060
  --root <path> \u5BFE\u8C61\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA
5176
5061
  --dir <path> init \u306E\u51FA\u529B\u5148
5177
- --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
5062
+ --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
5178
5063
  --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
5179
5064
  --dry-run \u5909\u66F4\u3092\u884C\u308F\u305A\u8868\u793A\u306E\u307F
5180
- --list analyze: \u5229\u7528\u53EF\u80FD\u306A\u30D7\u30ED\u30F3\u30D7\u30C8\u4E00\u89A7\u3092\u8868\u793A
5181
- --prompt <name> analyze: \u6307\u5B9A\u30D7\u30ED\u30F3\u30D7\u30C8\uFF08.md\u7701\u7565\u53EF\uFF09\u3092\u51FA\u529B
5182
5065
  --format <text|github> validate \u306E\u51FA\u529B\u5F62\u5F0F
5183
5066
  --format <md|json> report \u306E\u51FA\u529B\u5F62\u5F0F
5184
5067
  --format <text|json> doctor \u306E\u51FA\u529B\u5F62\u5F0F