qfai 0.6.3 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,7 +48,7 @@ npx qfai report
48
48
 
49
49
  ## できること
50
50
 
51
- - `npx qfai init` によるテンプレート生成(specs/contracts に加え、`.qfai/require/README.md`、`.qfai/rules/pnpm.md`、`.qfai/prompts/require-to-spec.md`、`.qfai/prompts/qfai-generate-test-globs.md`、`.qfai/prompts/qfai-maintain-traceability.md`、`.qfai/prompts/qfai-maintain-contracts.md`、`.qfai/prompts/qfai-classify-change.md`、`.qfai/promptpack/` を含む)
51
+ - `npx qfai init` によるテンプレート生成(specs/contracts に加え、`.qfai/require/README.md`、`.qfai/rules/pnpm.md`、`.qfai/prompts/**`、`.qfai/prompts.local/README.md`、`.qfai/promptpack/` を含む)
52
52
  - `npx qfai validate` による `.qfai/` 内ドキュメントの整合性・トレーサビリティ検査
53
53
  - `npx qfai validate` による SC→Test 参照の検証(`validation.traceability.testFileGlobs` に一致するテストファイルから `QFAI:SC-xxxx` を抽出)
54
54
  - `npx qfai doctor` による設定/探索/パス/glob/validate.json の事前診断
@@ -61,6 +61,15 @@ npx qfai report
61
61
  `validate` は `--fail-on` / `--strict` によって CI ゲート化できます。`validate` は常に `.qfai/out/validate.json`(`output.validateJsonPath`)へ JSON を出力します。`--format` は画面表示(text/github)のみを制御します。`--format github` はアノテーションの上限と重複排除を行い、先頭にサマリを出します(全量は `validate.json` か `--format text` を参照)。
62
62
  `report` は `.qfai/out/validate.json` を既定入力とし、`--in` で上書きできます(優先順位: CLI > config)。`--run-validate` を指定すると validate を実行してから report を生成します。出力先は `--out` で変更できます(`--format json` の場合は `.qfai/out/report.json`)。
63
63
  `doctor` は validate/report の前段で設定/探索/パス/glob/validate.json を診断します。`--format text|json`、`--out` をサポートし、診断のみ(修復はしません)。`--fail-on warning|error` を指定すると該当 severity 以上で exit 1(未指定は常に exit 0)になります。
64
+
65
+ ### Prompts Overlay(v0.7 以降の方針)
66
+
67
+ QFAI が提供するプロンプト資産は次の 2 つに分離します。
68
+
69
+ - `.qfai/prompts/**`: QFAI 標準資産(更新や `qfai init` 再実行で上書きされ得る。利用者編集は非推奨・非サポート)
70
+ - `.qfai/prompts.local/**`: 利用者カスタム資産(QFAI はここを上書きしない)
71
+
72
+ 同じ相対パスのファイルがある場合は `.qfai/prompts.local` を優先して参照する運用とします。
64
73
  `report.json` は非契約(experimental / internal)として扱います。外部 consumer は依存しないでください。フィールドは例であり固定ではありません。短い例:
65
74
 
66
75
  ```json
@@ -80,7 +89,7 @@ doctor(text)の例:
80
89
  ```text
81
90
  qfai doctor: root=. config=qfai.config.yaml (found)
82
91
  [ok] config.search: qfai.config.yaml found
83
- summary: ok=10 warning=2 error=0
92
+ summary: ok=10 info=1 warning=2 error=0
84
93
  ```
85
94
 
86
95
  doctor の JSON も非契約(内部形式。将来予告なく変更あり)です。フィールドは例であり固定ではありません。短い例:
@@ -221,6 +230,8 @@ qfai.config.yaml
221
230
  qfai-maintain-traceability.md
222
231
  qfai-maintain-contracts.md
223
232
  qfai-classify-change.md
233
+ prompts.local/
234
+ README.md
224
235
  contracts/
225
236
  README.md
226
237
  api/
@@ -24,7 +24,8 @@ npx qfai report
24
24
  - `contracts/` : UI / API / DB 契約を置く場所
25
25
  - `require/` : 既存要件の集約(validate 対象外)
26
26
  - `rules/` : 規約・運用ルール
27
- - `prompts/` : 生成プロンプト資産(自動読取はしない)
27
+ - `prompts/` : QFAI 標準のプロンプト資産(自動読取はしない。更新や再 init で上書きされ得る)
28
+ - `prompts.local/` : 利用者カスタムのプロンプト資産(存在する場合は overlay でこちらを優先して読む運用)
28
29
  - `promptpack/` : PromptPack(SSOT、運用ルール/観点の正本)
29
30
  - `out/` : `validate` / `report` の出力先(gitignore 推奨)
30
31
 
@@ -36,6 +37,7 @@ npx qfai report
36
37
  - `rules/conventions.md`
37
38
  - `rules/pnpm.md`
38
39
  - `prompts/README.md`
40
+ - `prompts.local/README.md`
39
41
  - `prompts/require-to-spec.md`
40
42
  - `prompts/qfai-generate-test-globs.md`
41
43
  - `prompts/qfai-maintain-traceability.md`
@@ -54,6 +56,12 @@ npx qfai report
54
56
 
55
57
  `prompts/` は **人間が手動で使う資産**です。現時点では自動読取は行いません(将来のバージョンで CLI 連携を検討します)。
56
58
 
59
+ v0.7 以降、プロンプト資産のカスタマイズは `.qfai/prompts.local/**` に集約します(overlay 運用)。
60
+
61
+ - `.qfai/prompts/**` は QFAI 標準資産であり、更新や `qfai init` 再実行で上書きされ得ます
62
+ - 利用者が `.qfai/prompts/**` を直接編集することは非推奨・非サポートです
63
+ - 変更したい場合は同一相対パスで `.qfai/prompts.local/**` に置いて上書きしてください
64
+
57
65
  例:
58
66
 
59
67
  - Copilot: `.github/copilot-instructions.md` に要旨を転記
@@ -2,12 +2,22 @@
2
2
 
3
3
  このディレクトリのプロンプトは **手動で使う資産**です。現時点では自動読取は行いません(将来のバージョンで CLI 連携を検討します)。
4
4
 
5
+ 重要: `.qfai/prompts/**` は **QFAI 標準資産**です。QFAI のアップデートや `qfai init` の再実行により **上書きされ得ます**。
6
+ 利用者が直接編集することは **非推奨・非サポート(ほぼ禁止)**です。変更したい場合は `.qfai/prompts.local/**` に同一相対パスで置いて overlay してください。
7
+
5
8
  ## 目的
6
9
 
7
10
  - Spec から overview / Business Flow を生成するための素材
8
11
  - トレーサビリティ/契約/変更区分の運用支援(CIで止めない領域)
9
12
  - 将来(v0.9)の adapter/emit 実装に備えた配布物
10
13
 
14
+ ## Overlay(prompts.local)
15
+
16
+ カスタムしたい場合は、次の優先順位で参照する運用とします。
17
+
18
+ 1. `.qfai/prompts.local/<relativePath>`
19
+ 2. `.qfai/prompts/<relativePath>`
20
+
11
21
  ## プロンプト一覧
12
22
 
13
23
  - `makeOverview.md`: Spec 一覧(overview)生成
@@ -0,0 +1,25 @@
1
+ # Prompts Local(利用者カスタム / Overlay)
2
+
3
+ このディレクトリは **利用者が自由に編集・追加できるプロンプト資産**の置き場です。
4
+
5
+ QFAI v0.7 以降は、プロンプト資産のカスタマイズ手段を **overlay(prompts.local 優先)**に一本化します。
6
+
7
+ ## Overlay ルール(重要)
8
+
9
+ 同じ相対パスのファイルが存在する場合、次の優先順位で参照する運用とします。
10
+
11
+ 1. `.qfai/prompts.local/<relativePath>`(存在すればこちらを優先)
12
+ 2. `.qfai/prompts/<relativePath>`(無ければ base を参照)
13
+
14
+ 例:
15
+
16
+ - base: `.qfai/prompts/require-to-spec.md`
17
+ - local override: `.qfai/prompts.local/require-to-spec.md`
18
+
19
+ → `prompts.local` に同じ相対パスで置けば、以降は local を読む運用にできます。
20
+
21
+ ## 重要な注意(サポート境界)
22
+
23
+ - `.qfai/prompts/**` は **QFAI 標準資産**です(更新や `qfai init` の再実行で上書きされ得ます)。
24
+ - 利用者が `.qfai/prompts/**` を直接編集することは **非推奨・非サポート(ほぼ禁止)**です。
25
+ - 変更したい場合は、対象ファイルを `prompts.local` にコピーして上書きしてください。
@@ -889,8 +889,8 @@ var import_promises6 = require("fs/promises");
889
889
  var import_node_path6 = __toESM(require("path"), 1);
890
890
  var import_node_url = require("url");
891
891
  async function resolveToolVersion() {
892
- if ("0.6.3".length > 0) {
893
- return "0.6.3";
892
+ if ("0.7.1".length > 0) {
893
+ return "0.7.1";
894
894
  }
895
895
  try {
896
896
  const packagePath = resolvePackageJsonPath();
@@ -921,7 +921,7 @@ function addCheck(checks, check) {
921
921
  checks.push(check);
922
922
  }
923
923
  function summarize(checks) {
924
- const summary = { ok: 0, warning: 0, error: 0 };
924
+ const summary = { ok: 0, info: 0, warning: 0, error: 0 };
925
925
  for (const check of checks) {
926
926
  summary[check.severity] += 1;
927
927
  }
@@ -993,6 +993,20 @@ async function createDoctorData(options) {
993
993
  message: ok ? `${key} exists` : `${key} is missing (did you run 'qfai init'?)`,
994
994
  details: { path: toRelativePath(root, resolved) }
995
995
  });
996
+ if (key === "promptsDir") {
997
+ const promptsLocalDir = import_node_path7.default.join(
998
+ import_node_path7.default.dirname(resolved),
999
+ `${import_node_path7.default.basename(resolved)}.local`
1000
+ );
1001
+ const found = await exists4(promptsLocalDir);
1002
+ addCheck(checks, {
1003
+ id: "paths.promptsLocalDir",
1004
+ severity: "info",
1005
+ title: "Prompts overlay (prompts.local)",
1006
+ message: found ? "prompts.local exists (overlay can be used)" : "prompts.local is optional (create it to override prompts)",
1007
+ details: { path: toRelativePath(root, promptsLocalDir) }
1008
+ });
1009
+ }
996
1010
  }
997
1011
  const specsRoot = resolvePath(root, config, "specsDir");
998
1012
  const entries = await collectSpecEntries(specsRoot);
@@ -1191,7 +1205,7 @@ function formatDoctorText(data) {
1191
1205
  lines.push(`[${check.severity}] ${check.id}: ${check.message}`);
1192
1206
  }
1193
1207
  lines.push(
1194
- `summary: ok=${data.summary.ok} warning=${data.summary.warning} error=${data.summary.error}`
1208
+ `summary: ok=${data.summary.ok} info=${data.summary.info} warning=${data.summary.warning} error=${data.summary.error}`
1195
1209
  );
1196
1210
  return lines.join("\n");
1197
1211
  }
@@ -1240,9 +1254,22 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1240
1254
  const copied = [];
1241
1255
  const skipped = [];
1242
1256
  const conflicts = [];
1257
+ const protectPrefixes = (options.protect ?? []).map((p) => p.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "")).filter((p) => p.length > 0).map((p) => p + import_node_path9.default.sep);
1258
+ const isProtectedRelative = (relative) => {
1259
+ if (protectPrefixes.length === 0) {
1260
+ return false;
1261
+ }
1262
+ const normalized = relative.replace(/[\\/]+/g, import_node_path9.default.sep);
1263
+ return protectPrefixes.some(
1264
+ (prefix) => normalized === prefix.slice(0, -1) || normalized.startsWith(prefix)
1265
+ );
1266
+ };
1243
1267
  if (!options.force) {
1244
1268
  for (const file of files) {
1245
1269
  const relative = import_node_path9.default.relative(sourceRoot, file);
1270
+ if (isProtectedRelative(relative)) {
1271
+ continue;
1272
+ }
1246
1273
  const dest = import_node_path9.default.join(destRoot, relative);
1247
1274
  if (!await shouldWrite(dest, options.force)) {
1248
1275
  conflicts.push(dest);
@@ -1255,7 +1282,8 @@ async function copyFiles(files, sourceRoot, destRoot, options) {
1255
1282
  for (const file of files) {
1256
1283
  const relative = import_node_path9.default.relative(sourceRoot, file);
1257
1284
  const dest = import_node_path9.default.join(destRoot, relative);
1258
- if (!await shouldWrite(dest, options.force)) {
1285
+ const forceForThisFile = isProtectedRelative(relative) ? false : options.force;
1286
+ if (!await shouldWrite(dest, forceForThisFile)) {
1259
1287
  skipped.push(dest);
1260
1288
  continue;
1261
1289
  }
@@ -1350,7 +1378,8 @@ async function runInit(options) {
1350
1378
  });
1351
1379
  const qfaiResult = await copyTemplateTree(qfaiAssets, destQfai, {
1352
1380
  force: options.force,
1353
- dryRun: options.dryRun
1381
+ dryRun: options.dryRun,
1382
+ protect: ["prompts.local"]
1354
1383
  });
1355
1384
  report(
1356
1385
  [...rootResult.copied, ...qfaiResult.copied],