gitifact 0.8.6 → 0.9.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.
Files changed (85) hide show
  1. package/README.md +14 -36
  2. package/dist/browser/assets/{HgiRefresh-CnBE2KZ8.js → HgiRefresh-CmdxsKUC.js} +1 -1
  3. package/dist/browser/assets/{Markdown-75QVB57V.js → Markdown-BpWtY88p.js} +1 -1
  4. package/dist/browser/assets/{Selector-E8jyEKNB.js → Selector-Dji19JSe.js} +1 -1
  5. package/dist/browser/assets/{Tab-C-PhLRIZ.js → Tab-BY4IuGKn.js} +1 -1
  6. package/dist/browser/assets/{Table-Cr2jvka_.js → Table-Dm6wz2kf.js} +2 -2
  7. package/dist/browser/assets/{Token-BBk7tRek.js → Token-9hr7eTF1.js} +1 -1
  8. package/dist/browser/assets/about-C3s6Keuk.js +3 -0
  9. package/dist/browser/assets/activity-ClCBIQkT.jpg +0 -0
  10. package/dist/browser/assets/activity-timeline-BdRnSGAp.js +2 -0
  11. package/dist/browser/assets/changelog-CNUFmKj-.js +1 -0
  12. package/dist/browser/assets/commit-BWyvKMHI.js +4 -0
  13. package/dist/browser/assets/contributors-Bk_xjPNJ.js +1 -0
  14. package/dist/browser/assets/{contributors._email-CEcZZfAR.js → contributors._email-BPZ6AqpX.js} +1 -1
  15. package/dist/browser/assets/{contributors.index-DNMBFxLb.js → contributors.index-Ca6OC-eu.js} +1 -1
  16. package/dist/browser/assets/dashboard.index-Cgm78J6S.js +1 -0
  17. package/dist/browser/assets/{document-odLLdugz.js → document-BbL374wf.js} +4 -4
  18. package/dist/browser/assets/{document-Cmx0e6kz.css → document-DXzNxu17.css} +1 -1
  19. package/dist/browser/assets/document-Dtyl4_It.js +2 -0
  20. package/dist/browser/assets/feature-requirements-wfH5PlkX.jpg +0 -0
  21. package/dist/browser/assets/features-B1FsIXwk.js +2 -0
  22. package/dist/browser/assets/{features._featureId-Cmg8-2ku.js → features._featureId-CQYDdSK2.js} +1 -1
  23. package/dist/browser/assets/{features.index-CBz7BmRW.js → features.index-BcCdHpgS.js} +1 -1
  24. package/dist/browser/assets/getting-started-CCqN7NFk.js +1 -0
  25. package/dist/browser/assets/git-DCEKgdx_.js +1 -0
  26. package/dist/browser/assets/index-Bnx5f4X5.js +49 -0
  27. package/dist/browser/assets/{load-more-BVsXjzHC.js → load-more-COb87OhG.js} +1 -1
  28. package/dist/browser/assets/page-header-Cs2fqdqC.js +246 -0
  29. package/dist/browser/assets/{records._recordId-DF-NkdBL.js → records._recordId-CfPnm30x.js} +1 -1
  30. package/dist/browser/assets/{records.commits._commit-BBf_rxHJ.js → records.commits._commit-C-jisbh2.js} +1 -1
  31. package/dist/browser/assets/{records.docs._docId-Diev5opj.js → records.docs._docId-D1pQYz_I.js} +1 -1
  32. package/dist/browser/assets/{records.index-DPWv9blV.js → records.index-FJMKSXN0.js} +1 -1
  33. package/dist/browser/assets/{records.working-D5xv-dTD.js → records.working-cSfNWSM7.js} +1 -1
  34. package/dist/browser/assets/{settings-N1hE7l-i.js → settings-DDEler3s.js} +1 -1
  35. package/dist/browser/assets/useInfiniteQuery-IoAmrFi4.js +1 -0
  36. package/dist/browser/assets/{useKeyboardHint-CGJ2GVT3.js → useKeyboardHint-Wwnf-tpi.js} +1 -1
  37. package/dist/browser/index.html +18 -24
  38. package/dist/i18n/en/block.md +7 -7
  39. package/dist/i18n/en/changelog.md +20 -0
  40. package/dist/i18n/en/docs/commit.md +2 -2
  41. package/dist/i18n/en/docs/design.md +3 -3
  42. package/dist/i18n/en/docs/migrate.md +32 -35
  43. package/dist/i18n/en/docs/records.md +2 -2
  44. package/dist/i18n/en/docs/spec.md +1 -1
  45. package/dist/i18n/en/docs/workflow.md +12 -12
  46. package/dist/i18n/en/docs/writing.md +4 -4
  47. package/dist/i18n/ko/block.md +7 -7
  48. package/dist/i18n/ko/changelog.md +20 -0
  49. package/dist/i18n/ko/docs/commit.md +2 -2
  50. package/dist/i18n/ko/docs/design.md +3 -3
  51. package/dist/i18n/ko/docs/migrate.md +31 -34
  52. package/dist/i18n/ko/docs/records.md +2 -2
  53. package/dist/i18n/ko/docs/spec.md +1 -1
  54. package/dist/i18n/ko/docs/workflow.md +12 -12
  55. package/dist/i18n/ko/docs/writing.md +4 -4
  56. package/dist/launcher/cli.mjs +77 -0
  57. package/dist/main.js +281 -589
  58. package/package.json +1 -1
  59. package/dist/browser/assets/MetadataListItem-BEwPg27H.js +0 -1
  60. package/dist/browser/assets/about-C3ewDfU2.js +0 -3
  61. package/dist/browser/assets/activity-CiuGYDQ8.jpg +0 -0
  62. package/dist/browser/assets/activity-timeline-B3s0viEt.js +0 -2
  63. package/dist/browser/assets/changelog-DkRd6I8J.js +0 -1
  64. package/dist/browser/assets/commit-BHwKZ7hK.js +0 -4
  65. package/dist/browser/assets/contributor-2mkEa2Wo.css +0 -1
  66. package/dist/browser/assets/contributor-CqqvzxLh.js +0 -1
  67. package/dist/browser/assets/contributors-CuMPZx6_.js +0 -1
  68. package/dist/browser/assets/dashboard.index-nV7KwM5H.js +0 -1
  69. package/dist/browser/assets/document-BnxE7Txb.js +0 -2
  70. package/dist/browser/assets/feature-requirements-CA8f9Bcf.jpg +0 -0
  71. package/dist/browser/assets/features-C0YzZiS-.js +0 -2
  72. package/dist/browser/assets/getting-started-CaOhGSBe.js +0 -1
  73. package/dist/browser/assets/git-DwAnKiiN.js +0 -1
  74. package/dist/browser/assets/index-BljzO9go.js +0 -49
  75. package/dist/browser/assets/instructions-2OUMP-vh.css +0 -1
  76. package/dist/browser/assets/instructions-DWS_nwe0.js +0 -1
  77. package/dist/browser/assets/instructions._instructionId-l7aWWvfx.js +0 -1
  78. package/dist/browser/assets/instructions.agents-t7hMe4Pv.js +0 -1
  79. package/dist/browser/assets/instructions.index-CHVCQBSH.js +0 -1
  80. package/dist/browser/assets/lazyRouteComponent-BZI8yQj_.js +0 -1
  81. package/dist/browser/assets/page-header-CRcXie4p.js +0 -248
  82. package/dist/browser/assets/project-instructions-DsWrw-nY.jpg +0 -0
  83. package/dist/browser/assets/useInfiniteQuery-CrEm5UW2.js +0 -1
  84. package/dist/i18n/en/docs/instructions.md +0 -94
  85. package/dist/i18n/ko/docs/instructions.md +0 -94
package/dist/main.js CHANGED
@@ -3381,7 +3381,7 @@ var program = new Command();
3381
3381
  import { createServer } from "node:http";
3382
3382
  import { randomUUID as randomUUID3 } from "node:crypto";
3383
3383
  import { fileURLToPath } from "node:url";
3384
- import { readFile as readFile8 } from "node:fs/promises";
3384
+ import { readFile as readFile6 } from "node:fs/promises";
3385
3385
 
3386
3386
  // ../../packages/core/dist/domain/repository-status.js
3387
3387
  function summarizeChanges(changes2) {
@@ -3510,9 +3510,8 @@ var messages_default = {
3510
3510
  "doc.DESIGN_OVERVIEW_REQUIRED": "\uC124\uACC4\uAC00 \uC788\uB294 \uAE30\uB2A5\uC5D0\uB294 design/overview.md\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: {feature}",
3511
3511
  "doc.FILE_TOO_LARGE": "\uBB38\uC11C \uD30C\uC77C\uC774 1MB\uB97C \uB118\uC5B4 \uC77D\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: {path}",
3512
3512
  "doc.DOC_DRAFT": "new \uBA85\uB839\uC774 \uB9CC\uB4E0 \uCD08\uC548 \uD45C\uC2DC\uAC00 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4. \uBCF8\uBB38\uC744 \uB2E4 \uC4F4 \uB4A4 draft: true \uC904\uC744 \uC9C0\uC6B0\uC138\uC694: {path}",
3513
- "doc.INSTRUCTION_INDEX_REQUIRED": "\uC9C0\uCE68 \uD3F4\uB354\uC5D0 index.md\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: {path}",
3514
- "doc.WIKI_REMOVED": "\uC704\uD0A4\uB294 0.8.0\uC5D0\uC11C \uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC73C\uB85C \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uC774 \uD398\uC774\uC9C0\uB97C .gitifact/instructions/\uB85C \uC62E\uAE30\uC138\uC694(gitifact guide show migrate): {path}",
3515
- "doc.INSTRUCTION_SPEC_LINK": "\uC9C0\uCE68\uC740 \uBA85\uC138\uB97C \uAC00\uB9AC\uD0A4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC124\uACC4\uAC00 sources\uB85C \uC9C0\uCE68\uC744 \uAC00\uB9AC\uD0B5\uB2C8\uB2E4: {path}: {link}",
3513
+ "doc.INSTRUCTION_MOVED": "0.9.0\uBD80\uD130 Gitifact\uB294 \uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC744 \uAD00\uB9AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC774 \uC9C0\uCE68\uC744 \uC4F0\uB294 \uC5D0\uC774\uC804\uD2B8\uC758 \uC2A4\uD0AC \uD3F4\uB354\uB85C \uC62E\uAE30\uC138\uC694(gitifact guide show migrate): {path}",
3514
+ "doc.WIKI_REMOVED": "\uC704\uD0A4\uB294 0.8.0\uC5D0\uC11C \uC5C6\uC5B4\uC84C\uC2B5\uB2C8\uB2E4. \uC774 \uD398\uC774\uC9C0\uB97C \uC5D0\uC774\uC804\uD2B8\uC758 \uC2A4\uD0AC \uD3F4\uB354\uB098 \uB2E4\uB978 \uBB38\uC11C\uB85C \uC62E\uAE30\uC138\uC694(gitifact guide show migrate): {path}",
3516
3515
  "warn.MISSING_LINK_TARGET": "\uB9C1\uD06C \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {path}: {link}",
3517
3516
  "warn.ASSET_SIZE": "\uC5D0\uC14B\uC774 \uAD8C\uC7A5 \uD06C\uAE30(1MB)\uB97C \uB118\uC2B5\uB2C8\uB2E4: {path} ({bytes}\uBC14\uC774\uD2B8)",
3518
3517
  "warn.ASSET_EXTENSION": "\uBE0C\uB77C\uC6B0\uC800\uAC00 \uBBF8\uB9AC \uBCF4\uC5EC \uC8FC\uC9C0 \uC54A\uB294 \uD655\uC7A5\uC790\uB77C \uB0B4\uB824\uBC1B\uAE30\uB85C\uB9CC \uC5F4\uB9BD\uB2C8\uB2E4: {path}",
@@ -3636,9 +3635,8 @@ var messages_default2 = {
3636
3635
  "doc.DESIGN_OVERVIEW_REQUIRED": "A feature with a design needs design/overview.md: {feature}",
3637
3636
  "doc.FILE_TOO_LARGE": "The document file exceeds 1 MB and was not read: {path}",
3638
3637
  "doc.DOC_DRAFT": "The draft mark a new command added is still there. Remove the draft: true line once the body is written: {path}",
3639
- "doc.INSTRUCTION_INDEX_REQUIRED": "An instruction folder needs an index.md: {path}",
3640
- "doc.WIKI_REMOVED": "The wiki became project instructions in 0.8.0. Move this page to .gitifact/instructions/ (gitifact guide show migrate): {path}",
3641
- "doc.INSTRUCTION_SPEC_LINK": "An instruction does not point at specs; designs name the instructions they follow in sources: {path}: {link}",
3638
+ "doc.INSTRUCTION_MOVED": "Gitifact no longer keeps project instructions from 0.9.0. Move this instruction to the skills folder of the agent you use (gitifact guide show migrate): {path}",
3639
+ "doc.WIKI_REMOVED": "The wiki went in 0.8.0. Move this page to the agent's skills folder or another document (gitifact guide show migrate): {path}",
3642
3640
  "warn.MISSING_LINK_TARGET": "The link target does not exist: {path}: {link}",
3643
3641
  "warn.ASSET_SIZE": "The asset is over the recommended size (1 MB): {path} ({bytes} bytes)",
3644
3642
  "warn.ASSET_EXTENSION": "The browser does not preview this extension and offers it only as a download: {path}",
@@ -3852,9 +3850,9 @@ function extractLinks(body2) {
3852
3850
  fence = void 0;
3853
3851
  continue;
3854
3852
  }
3855
- const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
3856
- if (open6) {
3857
- fence = { char: open6[1][0], size: open6[1].length };
3853
+ const open5 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
3854
+ if (open5) {
3855
+ fence = { char: open5[1][0], size: open5[1].length };
3858
3856
  continue;
3859
3857
  }
3860
3858
  for (const m of line2.replace(/`[^`]*`/g, "").matchAll(/!?\[[^\]]*\]\((?:<([^>]+)>|([^)\s]+))(?:\s+"[^"]*")?\)/g))
@@ -4002,9 +4000,9 @@ function parseDesign(source2, specId) {
4002
4000
  fence = void 0;
4003
4001
  return line2;
4004
4002
  }
4005
- const open6 = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
4006
- if (open6) {
4007
- fence = { char: open6[1][0], size: open6[1].length };
4003
+ const open5 = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
4004
+ if (open5) {
4005
+ fence = { char: open5[1][0], size: open5[1].length };
4008
4006
  return line2;
4009
4007
  }
4010
4008
  if (/^#\s/.test(line2))
@@ -4047,9 +4045,9 @@ function parseDocument(path3, source2) {
4047
4045
  fence = void 0;
4048
4046
  continue;
4049
4047
  }
4050
- const open6 = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
4051
- if (open6) {
4052
- fence = { char: open6[1][0], size: open6[1].length };
4048
+ const open5 = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
4049
+ if (open5) {
4050
+ fence = { char: open5[1][0], size: open5[1].length };
4053
4051
  continue;
4054
4052
  }
4055
4053
  if (/^#\s/.test(line2))
@@ -4459,14 +4457,14 @@ function sections(raw, path3) {
4459
4457
  found.at(-1)?.lines.push(line2);
4460
4458
  continue;
4461
4459
  }
4462
- const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
4463
- if (open6)
4464
- fence = { char: open6[1][0], size: open6[1].length };
4460
+ const open5 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
4461
+ if (open5)
4462
+ fence = { char: open5[1][0], size: open5[1].length };
4465
4463
  else if (/^#\s/.test(line2))
4466
4464
  throw new DocumentError("BODY_HEADING", path3, t("doc.BODY_HEADING", { path: path3 }));
4467
4465
  else if (/<!-- gitifact-/.test(line2))
4468
4466
  throw new DocumentError("BODY_MARKER", path3, t("doc.BODY_MARKER", { path: path3 }));
4469
- const heading = !open6 && /^##\s+(.+?)\s*#*\s*$/.exec(line2);
4467
+ const heading = !open5 && /^##\s+(.+?)\s*#*\s*$/.exec(line2);
4470
4468
  if (heading) {
4471
4469
  const key = sectionKeyOf(heading[1]);
4472
4470
  if (!key || found.some((s) => s.key === key))
@@ -4550,6 +4548,8 @@ var HISTORY_PATH = ".gitifact/history.jsonl";
4550
4548
  var name = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4551
4549
  var rootUpperName2 = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*\.md$/;
4552
4550
  var isWikiPage = (path3) => path3.startsWith(WIKI_ROOT + "/") && path3.endsWith(".md");
4551
+ var isInstructionPath = (path3) => path3.startsWith(INSTRUCTIONS_ROOT + "/");
4552
+ var instructionFolderOf = (path3) => /^\.gitifact\/instructions\/[^/]+/.exec(path3)?.[0];
4553
4553
  function classifyDocPath(path3) {
4554
4554
  const unsupported = () => new DocumentError("PATH_UNSUPPORTED", path3, t("doc.PATH_UNSUPPORTED", { path: path3 }));
4555
4555
  if (path3 === HISTORY_PATH)
@@ -4650,9 +4650,9 @@ function body(raw, path3) {
4650
4650
  fence = void 0;
4651
4651
  continue;
4652
4652
  }
4653
- const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
4654
- if (open6) {
4655
- fence = { char: open6[1][0], size: open6[1].length };
4653
+ const open5 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
4654
+ if (open5) {
4655
+ fence = { char: open5[1][0], size: open5[1].length };
4656
4656
  continue;
4657
4657
  }
4658
4658
  if (/^#\s/.test(line2))
@@ -4716,19 +4716,6 @@ function parseDocumentFile(path3, source2) {
4716
4716
  }
4717
4717
  return { kind: "design", feature: where.feature, order: order(), requirements: refs?.type === "list" ? refs.items : [], sources: sources(fields2, path3), ...common };
4718
4718
  }
4719
- var isInstructionReference = (path3) => path3.endsWith(".md");
4720
- function parseInstructionFile(path3, source2) {
4721
- if (source2.includes("\0") || /\r(?!\n)/.test(source2) || source2.charCodeAt(0) === 65279)
4722
- throw new DocumentError("INVALID_CHARACTERS", path3, t("doc.INVALID_CHARACTERS", { path: path3 }));
4723
- const { fields: fields2 } = parseFrontmatterBlock(source2.replace(/\r\n/g, "\n"), path3);
4724
- for (const key of fields2.keys())
4725
- if (key !== "title" && key !== "description")
4726
- throw new DocumentError("FRONTMATTER_UNKNOWN_KEY", path3, t("doc.FRONTMATTER_UNKNOWN_KEY", { path: path3, key }));
4727
- for (const key of ["title", "description"])
4728
- if (!fields2.has(key))
4729
- throw new DocumentError("FRONTMATTER_MISSING_KEY", path3, t("doc.FRONTMATTER_MISSING_KEY", { path: path3, key }));
4730
- return { title: text(fields2, "title", path3, 200), description: text(fields2, "description", path3, 300) };
4731
- }
4732
4719
  function renderDocumentFile(doc) {
4733
4720
  const front = renderFrontmatterBlock([
4734
4721
  ["id", doc.id],
@@ -4773,17 +4760,18 @@ function checkDocuments(files) {
4773
4760
  const report = (code, path3, values = {}) => problems.push(docProblem(code, path3, values));
4774
4761
  const features = /* @__PURE__ */ new Set();
4775
4762
  const designFolders = /* @__PURE__ */ new Map();
4776
- const instructionFolders = /* @__PURE__ */ new Map();
4777
- const specLinks = (path3, body2) => {
4778
- for (const link3 of extractLinks(body2))
4779
- if (resolveLink(path3, link3)?.startsWith(SPEC_ROOT + "/"))
4780
- report("INSTRUCTION_SPEC_LINK", path3, { link: link3 });
4781
- };
4763
+ const instructionFolders = /* @__PURE__ */ new Set();
4782
4764
  for (const [path3, source2] of [...files].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
4783
4765
  if (isWikiPage(path3)) {
4784
4766
  report("WIKI_REMOVED", path3);
4785
4767
  continue;
4786
4768
  }
4769
+ if (isInstructionPath(path3)) {
4770
+ const folder = instructionFolderOf(path3);
4771
+ if (folder)
4772
+ instructionFolders.add(folder);
4773
+ continue;
4774
+ }
4787
4775
  try {
4788
4776
  const where = classifyDocPath(path3);
4789
4777
  if (where.type === "ignored")
@@ -4796,16 +4784,8 @@ function checkDocuments(files) {
4796
4784
  records2.push(parseRecordFile(path3, source2));
4797
4785
  continue;
4798
4786
  }
4799
- if (where.type === "instruction-file") {
4800
- instructionFolders.set(where.name, instructionFolders.get(where.name) === true);
4801
- if (isInstructionReference(path3)) {
4802
- specLinks(path3, source2);
4803
- parseInstructionFile(path3, source2);
4804
- }
4787
+ if (where.type !== "doc")
4805
4788
  continue;
4806
- }
4807
- if (where.kind === "instruction")
4808
- instructionFolders.set(where.name, true);
4809
4789
  if (where.kind === "requirement" || where.kind === "design")
4810
4790
  features.add(where.feature);
4811
4791
  if (where.kind === "design")
@@ -4821,9 +4801,6 @@ function checkDocuments(files) {
4821
4801
  for (const doc of documents)
4822
4802
  if (doc.draft)
4823
4803
  report("DOC_DRAFT", doc.path);
4824
- for (const doc of documents)
4825
- if (doc.kind === "instruction")
4826
- specLinks(doc.path, doc.body);
4827
4804
  for (const doc of documents) {
4828
4805
  const seen = byId.get(doc.id);
4829
4806
  if (seen)
@@ -4871,9 +4848,8 @@ function checkDocuments(files) {
4871
4848
  for (const [feature2, overview] of designFolders)
4872
4849
  if (!overview)
4873
4850
  report("DESIGN_OVERVIEW_REQUIRED", `.gitifact/spec/${feature2}/design/overview.md`, { feature: feature2 });
4874
- for (const [name2, hasFile] of instructionFolders)
4875
- if (!hasFile)
4876
- report("INSTRUCTION_INDEX_REQUIRED", `${INSTRUCTIONS_ROOT}/${name2}/${INSTRUCTION_FILE}`);
4851
+ for (const folder of [...instructionFolders].sort())
4852
+ report("INSTRUCTION_MOVED", folder);
4877
4853
  return { documents, records: records2, problems };
4878
4854
  }
4879
4855
 
@@ -4889,12 +4865,11 @@ function arrangeDocuments(documents) {
4889
4865
  return f;
4890
4866
  };
4891
4867
  const wiki = [];
4892
- const instructions2 = [];
4893
4868
  for (const doc of documents) {
4894
4869
  if (doc.kind === "wiki")
4895
4870
  wiki.push(doc);
4896
4871
  else if (doc.kind === "instruction")
4897
- instructions2.push(doc);
4872
+ continue;
4898
4873
  else if (doc.kind === "feature")
4899
4874
  folder(doc.feature).index = doc;
4900
4875
  else if (doc.kind === "requirement")
@@ -4911,7 +4886,7 @@ function arrangeDocuments(documents) {
4911
4886
  }
4912
4887
  features.push({ index: f.index, requirements: f.requirements.sort(byOrder), designs: f.designs.sort(byOrder) });
4913
4888
  }
4914
- return { features, wiki: wiki.sort(byPath), instructions: instructions2.sort(byPath), orphans };
4889
+ return { features, wiki: wiki.sort(byPath), orphans };
4915
4890
  }
4916
4891
 
4917
4892
  // ../../packages/core/dist/use-cases/compare-documents.js
@@ -4974,7 +4949,7 @@ function documentWarnings(documents, assets, existing) {
4974
4949
  referenced.add(target);
4975
4950
  continue;
4976
4951
  }
4977
- if ((!target.startsWith(".gitifact/") || target.startsWith(INSTRUCTIONS_ROOT + "/")) && existing.has(target))
4952
+ if (!target.startsWith(".gitifact/") && existing.has(target))
4978
4953
  continue;
4979
4954
  warnings.push(docWarning("MISSING_LINK_TARGET", from, { link: link3, target }));
4980
4955
  }
@@ -25182,21 +25157,6 @@ var feature = external_exports.strictObject({
25182
25157
  contributors: external_exports.array(contributor),
25183
25158
  updatedAt: external_exports.string().nullable()
25184
25159
  });
25185
- var instructionFile = external_exports.strictObject({ path: external_exports.string(), size: external_exports.number().int().nonnegative(), title: external_exports.string().optional(), description: external_exports.string().optional() });
25186
- var instruction = external_exports.strictObject({
25187
- id: external_exports.string(),
25188
- name: external_exports.string(),
25189
- path: external_exports.string(),
25190
- title: external_exports.string(),
25191
- description: external_exports.string(),
25192
- body: external_exports.string(),
25193
- ...standing,
25194
- // Paths relative to the instruction folder, index.md left out; `limited` when the folder holds more than the list carries.
25195
- files: external_exports.array(instructionFile),
25196
- filesLimited: external_exports.boolean(),
25197
- updatedAt: external_exports.string().nullable()
25198
- });
25199
- var agents = external_exports.strictObject({ path: external_exports.string(), body: external_exports.string(), updatedAt: external_exports.string().nullable() });
25200
25160
  var problem = external_exports.strictObject({ code: external_exports.string(), path: external_exports.string(), message: external_exports.string() });
25201
25161
  var oid2 = external_exports.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
25202
25162
  var indexFeature = external_exports.strictObject({
@@ -25207,17 +25167,16 @@ var indexFeature = external_exports.strictObject({
25207
25167
  requirements: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string() })),
25208
25168
  designs: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string() }))
25209
25169
  });
25210
- var indexInstruction = external_exports.strictObject({ id: external_exports.string(), name: external_exports.string(), path: external_exports.string(), title: external_exports.string() });
25211
- var browserCheckoutV1 = external_exports.strictObject({
25170
+ var browserCheckoutV2 = external_exports.strictObject({
25212
25171
  contract: external_exports.literal("browser-checkout"),
25213
- version: external_exports.literal(1),
25172
+ version: external_exports.literal(2),
25214
25173
  sessionId: external_exports.string(),
25215
25174
  head: oid2.nullable(),
25216
25175
  observedAt: external_exports.string(),
25217
25176
  working: external_exports.boolean(),
25218
25177
  stamp: external_exports.string(),
25219
25178
  problems: external_exports.array(problem),
25220
- index: external_exports.strictObject({ features: external_exports.array(indexFeature), instructions: external_exports.array(indexInstruction), people: external_exports.array(external_exports.strictObject({ name: external_exports.string(), email: external_exports.string() })) })
25179
+ index: external_exports.strictObject({ features: external_exports.array(indexFeature), people: external_exports.array(external_exports.strictObject({ name: external_exports.string(), email: external_exports.string() })) })
25221
25180
  });
25222
25181
  var featureRow = external_exports.strictObject({
25223
25182
  id: external_exports.string(),
@@ -25245,13 +25204,6 @@ var browserFeaturesV1 = external_exports.strictObject({
25245
25204
  features: external_exports.array(featureRow)
25246
25205
  });
25247
25206
  var browserFeatureV2 = external_exports.strictObject({ contract: external_exports.literal("browser-feature"), version: external_exports.literal(2), sessionId: external_exports.string(), feature });
25248
- var browserInstructionsV1 = external_exports.strictObject({
25249
- contract: external_exports.literal("browser-instructions"),
25250
- version: external_exports.literal(1),
25251
- sessionId: external_exports.string(),
25252
- instructions: external_exports.array(instruction),
25253
- agents: agents.nullable()
25254
- });
25255
25207
  var browserContributorsV1 = external_exports.strictObject({
25256
25208
  contract: external_exports.literal("browser-contributors"),
25257
25209
  version: external_exports.literal(1),
@@ -25334,20 +25286,20 @@ var browserHistorySummaryV5 = external_exports.strictObject({
25334
25286
  // The newest commits: a few of their changes each and how many there are in all.
25335
25287
  recent: external_exports.array(external_exports.strictObject({ commit: oid2, count: external_exports.number().int().positive(), events: external_exports.array(event) }))
25336
25288
  });
25337
- var searchKind = external_exports.enum(["feature", "requirement", "design", "instruction", "record", "commit"]);
25289
+ var searchKind = external_exports.enum(["feature", "requirement", "design", "record", "commit"]);
25338
25290
  var searchHit = external_exports.strictObject({
25339
25291
  id: external_exports.string(),
25340
25292
  kind: searchKind,
25341
25293
  title: external_exports.string(),
25342
25294
  where: external_exports.string(),
25343
25295
  line: external_exports.string(),
25344
- // What the hit opens: a feature (with the requirement or design tab), an instruction, a record, or a commit.
25296
+ // What the hit opens: a feature (with the requirement or design tab), a record, or a commit.
25345
25297
  featureId: external_exports.string().optional(),
25346
25298
  commit: oid2.optional()
25347
25299
  });
25348
- var browserSearchV3 = external_exports.strictObject({
25300
+ var browserSearchV4 = external_exports.strictObject({
25349
25301
  contract: external_exports.literal("browser-search"),
25350
- version: external_exports.literal(3),
25302
+ version: external_exports.literal(4),
25351
25303
  sessionId: external_exports.string(),
25352
25304
  query: external_exports.string(),
25353
25305
  groups: external_exports.array(external_exports.strictObject({ group: external_exports.union([searchKind, external_exports.literal("recent")]), total: external_exports.number().int().nonnegative(), next: external_exports.string().nullable(), hits: external_exports.array(searchHit) }))
@@ -25379,14 +25331,13 @@ var browserFeaturesQueryV1 = external_exports.strictObject({
25379
25331
  var browserFeatureQueryV1 = external_exports.strictObject({ id: featureId });
25380
25332
  var browserContributorsQueryV1 = external_exports.strictObject({ q: external_exports.string().max(200).optional(), after: external_exports.string().min(1).max(320).optional(), limit: count(50).pipe(external_exports.number().min(1)).optional() });
25381
25333
  var browserContributorQueryV1 = external_exports.strictObject({ email: external_exports.string().min(1).max(320) });
25382
- var browserSearchQueryV2 = external_exports.strictObject({
25334
+ var browserSearchQueryV3 = external_exports.strictObject({
25383
25335
  q: external_exports.string().max(200),
25384
25336
  head: headQuery.optional(),
25385
25337
  group: searchKind.optional(),
25386
25338
  after: external_exports.string().min(1).max(200).optional(),
25387
25339
  limit: count(50).pipe(external_exports.number().min(1)).optional()
25388
25340
  });
25389
- var browserInstructionFileQueryV1 = external_exports.strictObject({ id: external_exports.string().regex(/^I-[a-z2-7]{10}$/), path: external_exports.string().min(1).max(1e3) });
25390
25341
  var changelogQueryV1 = external_exports.strictObject({ lang: external_exports.string().regex(/^[a-z]{2}(?:-[A-Z]{2})?$/).optional() });
25391
25342
  var fileStatus = external_exports.enum(["added", "modified", "deleted", "renamed"]);
25392
25343
  var commitFile = external_exports.strictObject({
@@ -25475,17 +25426,6 @@ var browserDocumentHistoryQueryV1 = external_exports.strictObject({
25475
25426
  after: oid2.optional(),
25476
25427
  limit: count(50).pipe(external_exports.number().min(1)).optional()
25477
25428
  });
25478
- var browserInstructionFileV1 = external_exports.strictObject({
25479
- contract: external_exports.literal("browser-instruction-file"),
25480
- version: external_exports.literal(1),
25481
- sessionId: external_exports.string(),
25482
- id: external_exports.string(),
25483
- path: external_exports.string(),
25484
- text: external_exports.string().nullable(),
25485
- size: external_exports.number().int().nonnegative(),
25486
- binary: external_exports.boolean(),
25487
- tooLarge: external_exports.boolean()
25488
- });
25489
25429
  var workingChange = external_exports.strictObject({ id: external_exports.string(), kind, title: external_exports.string(), path: external_exports.string(), previousPath: external_exports.string().optional(), types: external_exports.array(changeType) });
25490
25430
  var pendingRecord = external_exports.strictObject({ id: external_exports.string(), title: external_exports.string(), docs: external_exports.array(external_exports.string()), sections: external_exports.array(recordSection), draft: external_exports.boolean() });
25491
25431
  var browserWorkingV1 = external_exports.strictObject({
@@ -25586,7 +25526,8 @@ var updateV6 = external_exports.discriminatedUnion("ok", [
25586
25526
  // refreshed: blocks whose text differed were rewritten. current: every block already matched. kept: the project is on a
25587
25527
  // release newer than this CLI, so its blocks were left as they are.
25588
25528
  // not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
25589
- // missing: files `gitifact init` would add and update does not create, e.g. CLAUDE.md when only AGENTS.md has the block.
25529
+ // missing: files `gitifact init` would add and update does not create. Always empty since 0.9.0, when init stopped adding
25530
+ // a CLAUDE.md that imports AGENTS.md.
25590
25531
  agentDocs: external_exports.strictObject({ state: external_exports.enum(["refreshed", "current", "kept", "not-initialized", "no-block"]), paths: external_exports.array(external_exports.string()), missing: external_exports.array(external_exports.string()) }),
25591
25532
  // written: .gitifact/config.json now names this release as `cli` and the block language. current: it already did.
25592
25533
  // cli is the project's release afterwards; one newer than cliVersion was kept, since an older CLI does not lower it.
@@ -25767,7 +25708,6 @@ var messages_default3 = {
25767
25708
  "server.recordNotFound": "\uC774 \uC774\uB825\uC5D0 \uADF8 \uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25768
25709
  "server.documentNotFound": "\uC774 \uC774\uB825\uACFC \uC791\uC5C5 \uD3F4\uB354\uC5D0 \uADF8 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
25769
25710
  "server.commitFileNotFound": "\uADF8 \uCEE4\uBC0B\uC774 \uBC14\uAFBC \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
25770
- "server.instructionFileNotFound": "\uC9C0\uCE68 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
25771
25711
  "server.specsUnreadable": "\uBA85\uC138\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
25772
25712
  "server.refreshOrigin": "\uC0C8\uB85C\uACE0\uCE68\uC5D0\uB294 \uAC19\uC740 \uCD9C\uCC98\uC758 Origin\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
25773
25713
  "server.fileNotFound": "\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
@@ -25809,7 +25749,7 @@ var messages_default3 = {
25809
25749
  "init.text.storage": "\uC800\uC7A5 \uADDC\uC57D",
25810
25750
  "init.text.agentDocs": "\uC5D0\uC774\uC804\uD2B8 \uC9C0\uCE68",
25811
25751
  "init.text.lineEndings": "\uC904\uBC14\uAFC8: {path}\uB85C \uBB38\uC11C\uB97C LF\uB85C \uACE0\uC815\uD588\uC2B5\uB2C8\uB2E4. \uC774\uBBF8 \uC788\uB358 \uBB38\uC11C\uAC00 \uBCC0\uACBD\uC73C\uB85C \uBCF4\uC774\uBA74 \uC904\uBC14\uAFC8\uB9CC \uBC14\uB010 \uAC83\uC774\uB2C8 \uADF8 \uBB38\uC11C\uB4E4\uC744 \uD55C \uBC88 \uCEE4\uBC0B\uD574 \uC815\uB9AC\uD558\uC138\uC694.",
25812
- "init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC124\uCE58\uD55C \uB4A4 gitifact update\uB85C \uD504\uB85C\uC81D\uD2B8\uB97C \uC0C8 \uBC84\uC804\uC5D0 \uB9DE\uCD94\uC138\uC694.",
25752
+ "init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \uD504\uB85C\uC81D\uD2B8\uB97C \uC0C8 \uBC84\uC804\uC5D0 \uB9DE\uCD94\uC138\uC694.",
25813
25753
  "init.failed": "\uCD08\uAE30\uD654\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uD30C\uC77C \uC811\uADFC \uAD8C\uD55C\uACFC \uC0DD\uC131\uB41C \uC124\uC815\xB7\uC784\uC2DC \uD30C\uC77C\uC744 \uD655\uC778\uD558\uC138\uC694.",
25814
25754
  "config.storeNotDirectory": ".gitifact\uB294 \uB9C1\uD06C\uAC00 \uC544\uB2CC \uC77C\uBC18 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
25815
25755
  "config.fileShape": "config.json\uC740 64 KiB \uC774\uD558\uC758 \uC77C\uBC18 \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
@@ -25844,14 +25784,13 @@ var messages_default3 = {
25844
25784
  "update.text.upToDate": "\uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.",
25845
25785
  "update.text.disabled": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC774 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4.",
25846
25786
  "update.text.unavailable": "\uC0C8 \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uC138\uC694.",
25847
- "update.text.install": "\uC124\uCE58 \uBA85\uB839: {command}",
25848
- "update.text.afterInstall": "\uC124\uCE58\uD55C \uB4A4 gitifact update\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uACFC \uC9C0\uCE68\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uBC14\uB01D\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800\uB3C4 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
25787
+ "update.text.install": "\uC5C5\uB370\uC774\uD2B8 \uBA85\uB839: {command}",
25788
+ "update.text.afterInstall": "\uC774 \uBA85\uB839\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uACFC \uC9C0\uCE68 \uBE14\uB85D, \uC2E4\uD589\uAE30(.gitifact/bin/cli.mjs)\uB97C \uBC14\uAFC9\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800\uB3C4 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
25849
25789
  "update.text.blockRefreshed": "\uC9C0\uCE68 \uBE14\uB85D \uAC31\uC2E0: {paths}",
25850
25790
  "update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
25851
25791
  "update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. gitifact init\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694.",
25852
25792
  "update.text.notInitialized": "\uCD08\uAE30\uD654\uB41C \uD504\uB85C\uC81D\uD2B8\uAC00 \uC544\uB2C8\uC5B4\uC11C \uBC84\uC804\uB9CC \uD655\uC778\uD588\uC2B5\uB2C8\uB2E4.",
25853
- "update.text.missing": "{paths} \uD30C\uC77C\uC774 \uC5C6\uC5B4 Claude Code\uAC00 AGENTS.md\uC758 \uC9C0\uCE68\uC744 \uC77D\uC9C0 \uBABB\uD569\uB2C8\uB2E4. gitifact init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 AGENTS.md\uB97C \uAC00\uC838\uC624\uB294 \uD30C\uC77C\uC744 \uB9CC\uB4ED\uB2C8\uB2E4.",
25854
- "update.text.migrationRequired": "\uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uBB38\uC11C\uB294 \uC544\uC9C1 0.7 \uC800\uC7A5 \uADDC\uC57D(schemaVersion 2)\uC785\uB2C8\uB2E4. \uC9C0\uCE68 \uBE14\uB85D\uC740 \uC0C8 \uBA85\uB839\uC744 \uC548\uB0B4\uD558\uC9C0\uB9CC \uC804\uD658 \uC804\uC5D0\uB294 specs\xB7instructions\xB7records\xB7check\xB7changes \uBA85\uB839\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. npx --yes gitifact@{version} guide show migrate\uC758 \uC808\uCC28\uB85C \uBB38\uC11C\uB97C \uC804\uD658\uD558\uC138\uC694.",
25793
+ "update.text.migrationRequired": "\uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uBB38\uC11C\uB294 \uC544\uC9C1 0.7 \uC800\uC7A5 \uADDC\uC57D(schemaVersion 2)\uC785\uB2C8\uB2E4. \uC9C0\uCE68 \uBE14\uB85D\uC740 \uC0C8 \uBA85\uB839\uC744 \uC548\uB0B4\uD558\uC9C0\uB9CC \uC804\uD658 \uC804\uC5D0\uB294 specs\xB7records\xB7check\xB7changes \uBA85\uB839\uC774 \uB3D9\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. npx --yes gitifact@{version} guide show migrate\uC758 \uC808\uCC28\uB85C \uBB38\uC11C\uB97C \uC804\uD658\uD558\uC138\uC694.",
25855
25794
  "update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
25856
25795
  "update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25857
25796
  "update.text.commitSkipped.untracked": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. Git\uC774 \uCD94\uC801\uD558\uC9C0 \uC54A\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC785\uB2C8\uB2E4: {paths}",
@@ -25871,7 +25810,7 @@ var messages_default3 = {
25871
25810
  "help.guide": "\uC5D0\uC774\uC804\uD2B8\uC6A9 \uC791\uC131 \uC9C0\uCE68",
25872
25811
  "help.guideList": "\uC9C0\uCE68 \uBAA9\uB85D (\uC81C\uBAA9\uACFC \uC124\uBA85)",
25873
25812
  "help.guideShow": "\uC9C0\uCE68 \uD558\uB098\uB97C \uCD9C\uB825",
25874
- "help.guideTopic": "workflow, spec, design, instructions, records, writing, commit, migrate \uC911 \uD558\uB098",
25813
+ "help.guideTopic": "workflow, spec, design, records, writing, commit, migrate \uC911 \uD558\uB098",
25875
25814
  "command.failed": "\uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
25876
25815
  "project.notInitialized": "\uBA3C\uC800 gitifact init\uC73C\uB85C \uCD08\uAE30\uD654\uD558\uC138\uC694.",
25877
25816
  "input.file": "\uC785\uB825\uC740 1 MiB \uC774\uD558\uC758 JSON \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
@@ -25893,17 +25832,14 @@ var messages_default3 = {
25893
25832
  "docs.unknownDocument": "\uBB38\uC11C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: {ids}",
25894
25833
  "docs.references": "\uCC38\uC870",
25895
25834
  "docs.referencedBy": "\uAC00\uB9AC\uD0A4\uB294 \uBB38\uC11C",
25896
- "docs.files": "\uD30C\uC77C",
25897
25835
  "docs.invalidPath": "{kind} \uBB38\uC11C\uC758 \uACBD\uB85C\uAC00 \uC544\uB2D9\uB2C8\uB2E4: {path}. \uC608: {example}",
25898
25836
  "docs.pathExample.feature": "payment (.gitifact/spec/payment/index.md)",
25899
25837
  "docs.pathExample.requirement": "payment/cancel (.gitifact/spec/payment/requirements/cancel.md)",
25900
25838
  "docs.pathExample.design": "payment/api (.gitifact/spec/payment/design/api.md)",
25901
- "docs.pathExample.instruction": "cli-architecture (.gitifact/instructions/cli-architecture/index.md)",
25902
25839
  "docs.skeleton.feature": "(\uC774 \uAE30\uB2A5\uC774 \uBB34\uC5C7\uC774\uACE0 \uC5B4\uB514\uAE4C\uC9C0\uC778\uC9C0)",
25903
25840
  "docs.skeleton.requirement": "(\uC0AC\uC6A9\uC790)\uB85C\uC11C, (\uBAA9\uC801)\uC744 \uC704\uD574 (\uB3D9\uC791)\uC744 \uD558\uACE0 \uC2F6\uB2E4.\n\n### \uBC94\uC704\uC640 \uC81C\uC57D\n\n- (\uCC38\xB7\uAC70\uC9D3\uC744 \uAC00\uB9B4 \uC218 \uC788\uB294 \uADDC\uCE59 \uD55C \uBB38\uC7A5. \uC5C6\uC73C\uBA74 \uC774 \uC808\uC744 \uC9C0\uC6B4\uB2E4)\n\n### \uC218\uC6A9 \uC870\uAC74\n\n1. \uC870\uAC74: (\uC0C1\uD669)\n \uAE30\uB300 \uB3D9\uC791: (\uACB0\uACFC)",
25904
25841
  "docs.skeleton.requirementUsecase": "(\uC0AC\uC6A9\uC790)\uB85C\uC11C, (\uBAA9\uC801)\uC744 \uC704\uD574 (\uB3D9\uC791)\uC744 \uD558\uACE0 \uC2F6\uB2E4.\n\n### \uBC94\uC704\uC640 \uC81C\uC57D\n\n- (\uCC38\xB7\uAC70\uC9D3\uC744 \uAC00\uB9B4 \uC218 \uC788\uB294 \uADDC\uCE59 \uD55C \uBB38\uC7A5. \uC5C6\uC73C\uBA74 \uC774 \uC808\uC744 \uC9C0\uC6B4\uB2E4)\n\n### \uC0AC\uC804 \uC870\uAC74\n\n- (\uC2DC\uC791 \uC804\uC5D0 \uCC38\uC774\uC5B4\uC57C \uD558\uB294 \uAC83. \uC5C6\uC73C\uBA74 \uC774 \uC808\uC744 \uC9C0\uC6B4\uB2E4)\n\n### \uAE30\uBCF8 \uD750\uB984\n\n1. (\uD589\uC704\uC790)\uAC00 (\uB3D9\uC791)\uD55C\uB2E4.\n2. (\uC2DC\uC2A4\uD15C)\uC774 (\uACB0\uACFC)\uB97C \uBCF4\uC778\uB2E4.\n\n### \uB300\uCCB4 \uD750\uB984\n\n- **A1. (\uAC08\uB77C\uC9C0\uB294 \uC0C1\uD669)** (1\uB2E8\uACC4\uC5D0\uC11C)\n 1. (\uADF8\uB54C\uC758 \uB3D9\uC791)\n\n### \uC0AC\uD6C4 \uC870\uAC74\n\n- (\uB05D\uB09C \uB4A4 \uCC38\uC778 \uAC83. \uC5C6\uC73C\uBA74 \uC774 \uC808\uC744 \uC9C0\uC6B4\uB2E4)\n\n### \uC218\uC6A9 \uC870\uAC74\n\n1. \uACBD\uB85C: \uAE30\uBCF8 \uD750\uB984\n \uC870\uAC74: (\uC0C1\uD669)\n \uAE30\uB300 \uB3D9\uC791: (\uACB0\uACFC)\n2. \uACBD\uB85C: \uAE30\uBCF8 \uD750\uB984, A1\n \uC870\uAC74: (\uC0C1\uD669)\n \uAE30\uB300 \uB3D9\uC791: (\uACB0\uACFC)",
25905
25842
  "docs.skeleton.design": "(\uC774 \uC124\uACC4\uAC00 \uB2E4\uB8E8\uB294 \uAD6C\uC870\uC640 \uCC98\uB9AC \uBC29\uC2DD)",
25906
- "docs.skeleton.instruction": "(\uC774 \uC791\uC5C5\uC744 \uD560 \uB54C \uC9C0\uD0AC \uADDC\uCE59\uACFC \uC808\uCC28. \uC790\uC138\uD55C \uB0B4\uC6A9\uC740 references/ \uC544\uB798 \uD30C\uC77C\uB85C \uB098\uB204\uACE0 \uC5EC\uAE30\uC11C \uB9C1\uD06C\uD55C\uB2E4)",
25907
25843
  "docs.exists": "\uC774\uBBF8 \uC788\uB294 \uD30C\uC77C\uC785\uB2C8\uB2E4: {path}",
25908
25844
  "docs.notFolder": "\uD3F4\uB354 \uC790\uB9AC\uC5D0 \uC77C\uBC18 \uD3F4\uB354\uAC00 \uC544\uB2CC \uAC83\uC774 \uC788\uC2B5\uB2C8\uB2E4: {path}",
25909
25845
  "docs.created": "\uB9CC\uB4E4\uC5C8\uC2B5\uB2C8\uB2E4: {path} ({id})",
@@ -25920,6 +25856,7 @@ var messages_default3 = {
25920
25856
  "changes.committed": "\uCEE4\uBC0B\uD588\uC2B5\uB2C8\uB2E4: {commit} {message}",
25921
25857
  "changes.dryRun": "\uBBF8\uB9AC\uBCF4\uAE30: \uD30C\uC77C\uC744 \uC4F0\uAC70\uB098 \uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.",
25922
25858
  "changes.summary": "\uD30C\uC77C {paths}\uAC1C, \uBC14\uB010 \uBB38\uC11C {changes}\uAC1C, \uACB0\uC815\uAE30\uB85D {records}\uAC1C",
25859
+ "commit.tooManyStorePaths": "\uC774 \uCEE4\uBC0B\uC5D0 .gitifact \uC544\uB798 \uACBD\uB85C\uAC00 {count}\uAC1C\uC785\uB2C8\uB2E4. \uBB38\uC11C\xB7\uACB0\uC815\uAE30\uB85D\xB7\uC5D0\uC14B\uC740 \uD55C \uCEE4\uBC0B\uC5D0 {limit}\uAC1C\uAE4C\uC9C0 \uB2F4\uC2B5\uB2C8\uB2E4. \uACB0\uC815 \uB2E8\uC704\uB85C \uB098\uB220 \uCEE4\uBC0B\uD558\uC138\uC694(\uCF54\uB4DC \uACBD\uB85C\uB294 \uC774 \uD55C\uB3C4\uC5D0 \uB4E4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4).",
25923
25860
  "commit.migrationFlag": "migration\uC740 true\uC774\uAC70\uB098 \uC5C6\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
25924
25861
  "commit.checkFailed": "\uBB38\uC11C \uAC80\uC0AC\uC5D0\uC11C \uBB38\uC81C {count}\uAC1C\uAC00 \uB098\uC640 \uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.",
25925
25862
  "commit.unknownDocument": "\uACB0\uC815\uAE30\uB85D\uC774 \uAC00\uB9AC\uD0A4\uB294 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4: {ids}",
@@ -25935,7 +25872,8 @@ var messages_default3 = {
25935
25872
  "changes.recordAltered": "\uCEE4\uBC0B\uB41C \uACB0\uC815\uAE30\uB85D\uC774 \uBC14\uB00C\uAC70\uB098 \uC9C0\uC6CC\uC9D0(\uCEE4\uBC0B\uC774 \uAC70\uBD80\uB428): {paths}",
25936
25873
  "commit.recordAltered": "\uC774\uBBF8 \uCEE4\uBC0B\uB41C \uACB0\uC815\uAE30\uB85D\uC740 \uACE0\uCE58\uAC70\uB098 \uC9C0\uC6B8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: {paths}. HEAD\uB300\uB85C \uB418\uB3CC\uB9AC\uACE0, \uBC14\uB010 \uACB0\uC815\uC740 \uC0C8 \uAE30\uB85D(records new)\uC73C\uB85C \uC4F0\uC138\uC694.",
25937
25874
  "commit.selectMove": "\uC62E\uAE34 \uBB38\uC11C\uB294 \uC61B \uACBD\uB85C\uC640 \uC0C8 \uACBD\uB85C\uB97C \uD568\uAED8 \uC120\uD0DD\uD558\uC138\uC694: {ids}",
25938
- "records.invalidDocs": "--docs\uC5D0 \uB300\uC0C1 \uBB38\uC11C ID(S-\xB7R-\xB7D-\xB7I-)\uB97C \uD558\uB098 \uC774\uC0C1 \uC801\uC73C\uC138\uC694.",
25875
+ "records.invalidDocs": "--docs\uC5D0 \uB300\uC0C1 \uBB38\uC11C ID(S-\xB7R-\xB7D-)\uB97C \uD558\uB098 \uC774\uC0C1 \uC801\uC73C\uC138\uC694.",
25876
+ "records.notDocIds": "--docs\uC5D0 \uBB38\uC11C ID(S-\xB7R-\xB7D-)\uAC00 \uC544\uB2CC \uAC12\uC774 \uC788\uC2B5\uB2C8\uB2E4: {ids}",
25939
25877
  "records.skeleton": "(\uB0B4\uC6A9)",
25940
25878
  "records.createdNext": "\uC139\uC158\uC744 \uCC44\uC6B0\uACE0 draft: true \uC904\uC744 \uC9C0\uC6B4 \uB4A4, \uCEE4\uBC0B\uD560 \uB54C \uC774 \uD30C\uC77C\uC744 paths\uC5D0 \uB123\uC73C\uC138\uC694.",
25941
25879
  "records.unknown": "\uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {ids}",
@@ -25967,17 +25905,10 @@ var messages_default3 = {
25967
25905
  "help.specsNewStyle": "\uC694\uAD6C\uC0AC\uD56D \uD615\uC2DD(usecase, default). \uC5C6\uC73C\uBA74 \uC124\uC815\uC758 requirementStyle\uC744 \uB530\uB978\uB2E4",
25968
25906
  "help.newTitle": "\uBB38\uC11C \uC81C\uBAA9",
25969
25907
  "help.newDescription": "\uD55C \uC904 \uC124\uBA85",
25970
- "help.instructions": "\uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC758 \uBAA9\uB85D\xB7\uC6D0\uBB38\xB7\uC0DD\uC131",
25971
- "help.instructionsList": "AGENTS.md\uC640 \uC9C0\uCE68 \uBAA9\uB85D (\uC9C0\uCE68\uB9C8\uB2E4 \uB538\uB9B0 \uD30C\uC77C\uC758 \uC81C\uBAA9\uACFC \uC124\uBA85)",
25972
- "help.instructionsShow": "\uC9C0\uCE68 index.md \uC6D0\uBB38, \uB530\uB974\uB294 \uC124\uACC4, \uD3F4\uB354 \uD30C\uC77C \uBAA9\uB85D",
25973
- "help.instructionsShowTargets": "\uC9C0\uCE68 \uD3F4\uB354 \uC774\uB984\uC774\uB098 I- ID (\uC5EC\uB7EC \uAC1C \uAC00\uB2A5)",
25974
- "help.instructionsShowFile": "\uC9C0\uCE68 \uD3F4\uB354 \uC548\uC758 \uD30C\uC77C \uD558\uB098\uB97C \uC77D\uC74C (\uC608: references/code-style.md)",
25975
- "help.instructionsNew": "ID\uB97C \uBC1C\uAE09\uD558\uACE0 \uC9C0\uCE68 \uD3F4\uB354\uC640 index.md \uBF08\uB300 \uC0DD\uC131 (draft: true \uD45C\uC2DC)",
25976
- "help.instructionsNewName": "\uC9C0\uCE68 \uD3F4\uB354 \uC774\uB984 (\uC608: code-review)",
25977
25908
  "help.recordsList": "HEAD \uC774\uB825\uC758 \uACB0\uC815\uAE30\uB85D (\uCD5C\uC2E0\uC21C, \uCEE4\uBC0B \uC804 \uAE30\uB85D\uC774 \uBA3C\uC800)",
25978
25909
  "help.recordsListDoc": "\uD55C \uBB38\uC11C\uC758 \uACB0\uC815 \uD750\uB984: \uADF8 \uBB38\uC11C\uAC00 \uBC14\uB010 \uCEE4\uBC0B\uB9C8\uB2E4 \uACB0\uC815\uAE30\uB85D",
25979
25910
  "help.recordsListSince": "\uADF8 \uB0A0\uC9DC(YYYY-MM-DD)\uB098 \uCEE4\uBC0B \uB4A4\uC758 \uAC83\uB9CC",
25980
- "help.check": "\uBA85\uC138\xB7\uC9C0\uCE68\xB7\uCEE4\uBC0B \uC804 \uACB0\uC815\uAE30\uB85D \uC804\uCCB4\uC758 \uD615\uC2DD\xB7ID\xB7\uCC38\uC870\xB7\uCD08\uC548 \uD45C\uC2DC \uAC80\uC0AC (\uBB38\uC81C\uAC00 \uC788\uC73C\uBA74 \uC885\uB8CC \uCF54\uB4DC 1)",
25911
+ "help.check": "\uBA85\uC138\xB7\uCEE4\uBC0B \uC804 \uACB0\uC815\uAE30\uB85D \uC804\uCCB4\uC758 \uD615\uC2DD\xB7ID\xB7\uCC38\uC870\xB7\uCD08\uC548 \uD45C\uC2DC \uAC80\uC0AC (\uBB38\uC81C\uAC00 \uC788\uC73C\uBA74 \uC885\uB8CC \uCF54\uB4DC 1)",
25981
25912
  "help.listQ": "\uC81C\uBAA9\xB7\uC124\uBA85\xB7\uBCF8\uBB38\uC5D0 \uC774 \uB9D0\uC774 \uC788\uB294 \uAC83\uB9CC",
25982
25913
  "help.listAuthor": "\uC774 Git \uC791\uC131\uC790(\uC774\uB984\uC774\uB098 \uC774\uBA54\uC77C)\uAC00 \uBC14\uAFBC \uAC83\uB9CC",
25983
25914
  "help.listSort": "\uC815\uB82C \uAE30\uC900",
@@ -25994,18 +25925,9 @@ var messages_default3 = {
25994
25925
  "list.features": "\uAE30\uB2A5",
25995
25926
  "list.commits": "\uCEE4\uBC0B",
25996
25927
  "list.records": "\uACB0\uC815\uAE30\uB85D",
25997
- "docs.otherResource": "\uC774 \uBA85\uB839\uC774 \uB2E4\uB8E8\uB294 \uBB38\uC11C\uAC00 \uC544\uB2D9\uB2C8\uB2E4: {ids}. {command}\uB85C \uC77D\uC73C\uC138\uC694.",
25998
25928
  "docs.noMatch": "\uC870\uAC74\uC5D0 \uB9DE\uB294 \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25999
25929
  "docs.requirement": "\uC694\uAD6C\uC0AC\uD56D",
26000
25930
  "docs.design": "\uC124\uACC4",
26001
- "instructions.agents": "AGENTS.md \u2014 \uBE14\uB85D \uBC16 \uC0C9\uC778\uC774 \uC5B4\uB5A4 \uC791\uC5C5\uC5D0 \uC5B4\uB290 \uC9C0\uCE68\uC744 \uC77D\uC744\uC9C0 \uC54C\uB9B0\uB2E4",
26002
- "instructions.noAgents": "AGENTS.md \uC5C6\uC74C \u2014 \uC9C0\uCE68\uC744 \uC5B8\uC81C \uC77D\uC744\uC9C0 \uC54C\uB9AC\uB294 \uC0C9\uC778\uC774 \uC5C6\uC2B5\uB2C8\uB2E4",
26003
- "instructions.filesLimited": "(\uD30C\uC77C\uC774 \uB354 \uC788\uC5B4 \uC55E {count}\uAC1C\uB9CC \uBCF4\uC785\uB2C8\uB2E4)",
26004
- "instructions.empty": "\uC9C0\uCE68\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. instructions new\uB85C \uB9CC\uB4DC\uC138\uC694.",
26005
- "instructions.fileWithOne": "--file\uC740 \uC9C0\uCE68 \uD558\uB098\uC640 \uD568\uAED8 \uC4F0\uBA70 --ref\uC640 \uD568\uAED8 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
26006
- "instructions.unknownFile": "\uC9C0\uCE68 {name} \uD3F4\uB354\uC5D0 \uADF8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
26007
- "instructions.fileTooLarge": "(\uD30C\uC77C\uC774 \uCEE4\uC11C \uC6D0\uBB38\uC744 \uBCF4\uC774\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {size}\uBC14\uC774\uD2B8)",
26008
- "instructions.fileBinary": "(\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: {size}\uBC14\uC774\uD2B8)",
26009
25931
  "records.empty": "\uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
26010
25932
  "records.moreDocs": "\uC678 {count}\uAC1C",
26011
25933
  "help.feedback": "Gitifact \uC790\uCCB4\uC758 \uBC84\uADF8\xB7\uAC1C\uC120\uC744 Gitifact \uC800\uC7A5\uC18C \uC774\uC288\uB85C \uBCF4\uB0C4 (gh\uAC00 \uC5C6\uC73C\uBA74 \uC774\uC288 \uC791\uC131 \uC8FC\uC18C \uCD9C\uB825)",
@@ -26021,11 +25943,11 @@ var messages_default3 = {
26021
25943
  "feedback.truncated": "\uBCF8\uBB38\uC774 \uAE38\uC5B4 \uC8FC\uC18C\uC5D0\uB294 \uC55E\uBD80\uBD84\uB9CC \uB2F4\uACBC\uC2B5\uB2C8\uB2E4. \uC544\uB798 \uC804\uCCB4 \uBCF8\uBB38\uC744 \uC791\uC131 \uD398\uC774\uC9C0\uC5D0 \uBD99\uC5EC \uB123\uC73C\uC138\uC694.",
26022
25944
  "feedback.title": "\uC81C\uBAA9: {title}",
26023
25945
  "update.text.projectWritten": "{path}\uC758 \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC744 gitifact {version}\uC73C\uB85C \uC801\uC5C8\uC2B5\uB2C8\uB2E4.",
26024
- "update.text.projectAhead": "\uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC740 gitifact {project}\uC785\uB2C8\uB2E4. \uC774 CLI({version})\uAC00 \uB354 \uC624\uB798\uB418\uC5B4 \uB0AE\uCD94\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC62C\uB9AC\uC138\uC694.",
25946
+ "update.text.projectAhead": "\uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC740 gitifact {project}\uC785\uB2C8\uB2E4. \uC774 CLI({version})\uAC00 \uB354 \uC624\uB798\uB418\uC5B4 \uB0AE\uCD94\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uD504\uB85C\uC81D\uD2B8\uC758 \uC2E4\uD589\uAE30 node .gitifact/bin/cli.mjs\uB85C \uC2E4\uD589\uD558\uC138\uC694.",
26025
25947
  "update.later.postponed": "{latest} \uC548\uB0B4\uB97C {until}\uAE4C\uC9C0 \uC27D\uB2C8\uB2E4. \uB354 \uC0C8 \uBC84\uC804\uC774 \uB098\uC624\uBA74 \uBC14\uB85C \uB2E4\uC2DC \uC548\uB0B4\uD569\uB2C8\uB2E4.",
26026
25948
  "update.later.nothing": "\uBBF8\uB8F0 \uC0C8 \uBC84\uC804 \uC548\uB0B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(\uC9C0\uAE08 {version}).",
26027
- "notice.release": "\uC0C8 \uBC84\uC804 {latest}\uAC00 \uC788\uC2B5\uB2C8\uB2E4(\uC9C0\uAE08 {version}). \uC5C5\uB370\uC774\uD2B8: {command} \uD6C4 gitifact update \xB7 \uB098\uC911\uC5D0: gitifact update --later",
26028
- "notice.projectNewer": "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 gitifact {project} \uAE30\uC900\uC785\uB2C8\uB2E4(\uC2E4\uD589 \uC911 {version}). {command}\uB85C \uC62C\uB9AC\uC138\uC694.",
25949
+ "notice.release": "\uC0C8 \uBC84\uC804 {latest}\uAC00 \uC788\uC2B5\uB2C8\uB2E4(\uC9C0\uAE08 {version}). \uC5C5\uB370\uC774\uD2B8: {command} \xB7 \uB098\uC911\uC5D0: gitifact update --later",
25950
+ "notice.projectNewer": "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 gitifact {project} \uAE30\uC900\uC785\uB2C8\uB2E4(\uC2E4\uD589 \uC911 {version}). \uD504\uB85C\uC81D\uD2B8\uC758 \uC2E4\uD589\uAE30 node .gitifact/bin/cli.mjs\uB85C \uC2E4\uD589\uD558\uC138\uC694.",
26029
25951
  "notice.projectOlder": "\uC774 CLI({version})\uAC00 \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900({project})\uBCF4\uB2E4 \uC0C8 \uBC84\uC804\uC785\uB2C8\uB2E4. gitifact update\uB85C \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC744 \uC62C\uB9B4 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
26030
25952
  "help.updateLater": "\uC9C0\uAE08 \uC548\uB0B4\uB41C \uC0C8 \uBC84\uC804\uC744 24\uC2DC\uAC04 \uB3D9\uC548 \uC548\uB0B4\uD558\uC9C0 \uC54A\uC74C. \uB354 \uC0C8 \uBC84\uC804\uC740 \uBC14\uB85C \uC548\uB0B4",
26031
25953
  "update.text.blockKept": "\uD504\uB85C\uC81D\uD2B8\uAC00 \uC774 CLI\uBCF4\uB2E4 \uC0C8 \uBC84\uC804 \uAE30\uC900\uC774\uB77C \uC9C0\uCE68 \uBE14\uB85D\uC744 \uADF8\uB300\uB85C \uB450\uC5C8\uC2B5\uB2C8\uB2E4: {paths}"
@@ -26086,7 +26008,6 @@ var messages_default4 = {
26086
26008
  "server.recordNotFound": "This history has no such decision record.",
26087
26009
  "server.documentNotFound": "Neither this history nor the working tree has that document.",
26088
26010
  "server.commitFileNotFound": "That commit did not change this file.",
26089
- "server.instructionFileNotFound": "The instruction file was not found.",
26090
26011
  "server.specsUnreadable": "Could not read specifications.",
26091
26012
  "server.refreshOrigin": "Refresh requires a same-origin Origin header.",
26092
26013
  "server.fileNotFound": "File not found.",
@@ -26128,7 +26049,7 @@ var messages_default4 = {
26128
26049
  "init.text.storage": "Storage convention",
26129
26050
  "init.text.agentDocs": "Agent instructions",
26130
26051
  "init.text.lineEndings": "Line endings: {path} keeps documents in LF. Documents that now show as changed differ only in line endings; commit them once to settle it.",
26131
- "init.text.updateAvailable": "Version {version} is available. Install it with {command}, then run gitifact update to move the project to it.",
26052
+ "init.text.updateAvailable": "Version {version} is available. Move the project to it with {command}.",
26132
26053
  "init.failed": "Initialization failed. Check file permissions and the generated configuration and temporary files.",
26133
26054
  "config.storeNotDirectory": ".gitifact must be a regular directory, not a link.",
26134
26055
  "config.fileShape": "config.json must be a regular file no larger than 64 KiB.",
@@ -26163,14 +26084,13 @@ var messages_default4 = {
26163
26084
  "update.text.upToDate": "You are using the latest version.",
26164
26085
  "update.text.disabled": "Update checks are disabled.",
26165
26086
  "update.text.unavailable": "Could not check for updates. Check your network connection.",
26166
- "update.text.install": "Install command: {command}",
26167
- "update.text.afterInstall": "After installing, run gitifact update again to move the project and its instructions to the new version. Restart the browser with the new version too.",
26087
+ "update.text.install": "Update command: {command}",
26088
+ "update.text.afterInstall": "It moves the project, its instruction blocks and the launcher (.gitifact/bin/cli.mjs) to the new version. Restart the browser with the new version too.",
26168
26089
  "update.text.blockRefreshed": "Instruction blocks refreshed: {paths}",
26169
26090
  "update.text.blockCurrent": "Instruction blocks are already current: {paths}",
26170
26091
  "update.text.noBlock": "No instruction files contain a GITIFACT block. Run gitifact init to install one.",
26171
26092
  "update.text.notInitialized": "Only the version was checked because this project is not initialized.",
26172
- "update.text.missing": "{paths} is missing, so Claude Code cannot load the AGENTS.md instructions. Rerun gitifact init to create the import file.",
26173
- "update.text.migrationRequired": "This project's documents still use the 0.7 storage convention (schemaVersion 2). The blocks describe the new commands, but the specs, instructions, records, check and changes commands refuse to run until the documents are moved. Follow npx --yes gitifact@{version} guide show migrate to move them.",
26093
+ "update.text.migrationRequired": "This project's documents still use the 0.7 storage convention (schemaVersion 2). The blocks describe the new commands, but the specs, records, check and changes commands refuse to run until the documents are moved. Follow npx --yes gitifact@{version} guide show migrate to move them.",
26174
26094
  "update.text.committed": "Instruction blocks committed: {commit} ({paths})",
26175
26095
  "update.text.commitNothing": "No instruction block changes to commit.",
26176
26096
  "update.text.commitSkipped.untracked": "Not committed. Instruction files are untracked: {paths}",
@@ -26190,7 +26110,7 @@ var messages_default4 = {
26190
26110
  "help.guide": "Writing guides for agents",
26191
26111
  "help.guideList": "List guides (title and description)",
26192
26112
  "help.guideShow": "Print one guide",
26193
- "help.guideTopic": "One of workflow, spec, design, instructions, records, writing, commit, migrate",
26113
+ "help.guideTopic": "One of workflow, spec, design, records, writing, commit, migrate",
26194
26114
  "command.failed": "The command failed.",
26195
26115
  "project.notInitialized": "Initialize the project with gitifact init first.",
26196
26116
  "input.file": "The input must be a JSON file of 1 MiB or less.",
@@ -26212,17 +26132,14 @@ var messages_default4 = {
26212
26132
  "docs.unknownDocument": "Documents not found: {ids}",
26213
26133
  "docs.references": "References",
26214
26134
  "docs.referencedBy": "Referenced by",
26215
- "docs.files": "Files",
26216
26135
  "docs.invalidPath": "Not a path for a {kind} document: {path}. Example: {example}",
26217
26136
  "docs.pathExample.feature": "payment (.gitifact/spec/payment/index.md)",
26218
26137
  "docs.pathExample.requirement": "payment/cancel (.gitifact/spec/payment/requirements/cancel.md)",
26219
26138
  "docs.pathExample.design": "payment/api (.gitifact/spec/payment/design/api.md)",
26220
- "docs.pathExample.instruction": "cli-architecture (.gitifact/instructions/cli-architecture/index.md)",
26221
26139
  "docs.skeleton.feature": "(What this feature is and where it ends)",
26222
26140
  "docs.skeleton.requirement": "As (a user), I want to (act) so that (purpose).\n\n### Scope and constraints\n\n- (One rule that is either true or false. Remove this section if there is none)\n\n### Acceptance criteria\n\n1. Condition: (situation)\n Expected: (result)",
26223
26141
  "docs.skeleton.requirementUsecase": "As (a user), I want to (act) so that (purpose).\n\n### Scope and constraints\n\n- (One rule that is either true or false. Remove this section if there is none)\n\n### Preconditions\n\n- (What holds before it starts. Remove this section if there is none)\n\n### Basic flow\n\n1. (The actor) (acts).\n2. (The system) shows (the result).\n\n### Alternative flows\n\n- **A1. (Where it branches)** (at step 1)\n 1. (What happens then)\n\n### Postconditions\n\n- (What holds when it ends. Remove this section if there is none)\n\n### Acceptance criteria\n\n1. Path: Basic flow\n Condition: (situation)\n Expected: (result)\n2. Path: Basic flow, A1\n Condition: (situation)\n Expected: (result)",
26224
26142
  "docs.skeleton.design": "(The structure and processing this design covers)",
26225
- "docs.skeleton.instruction": "(Rules and steps to follow for this kind of work. Put details in files under references/ and link them from here)",
26226
26143
  "docs.exists": "The file already exists: {path}",
26227
26144
  "docs.notFolder": "Something other than a plain folder stands where a folder is needed: {path}",
26228
26145
  "docs.created": "Created {path} ({id})",
@@ -26239,6 +26156,7 @@ var messages_default4 = {
26239
26156
  "changes.committed": "Committed {commit} {message}",
26240
26157
  "changes.dryRun": "Preview: no files were written and nothing was committed.",
26241
26158
  "changes.summary": "{paths} file(s), {changes} changed document(s), {records} record(s)",
26159
+ "commit.tooManyStorePaths": "This commit has {count} paths under .gitifact. One commit takes up to {limit} documents, records and assets; split the commits by decision (code paths do not count toward this limit).",
26242
26160
  "commit.migrationFlag": "migration must be true or absent.",
26243
26161
  "commit.checkFailed": "The document check found {count} problems; nothing was committed.",
26244
26162
  "commit.unknownDocument": "A record names documents that do not exist: {ids}",
@@ -26254,7 +26172,8 @@ var messages_default4 = {
26254
26172
  "changes.recordAltered": "Committed records edited or deleted (the commit is refused): {paths}",
26255
26173
  "commit.recordAltered": "Committed records cannot be edited or deleted: {paths}. Restore them from HEAD and write a changed decision as a new record (records new).",
26256
26174
  "commit.selectMove": "Select both the old and the new path of a moved document: {ids}",
26257
- "records.invalidDocs": "Give at least one target document ID (S-, R-, D-, I-) with --docs.",
26175
+ "records.invalidDocs": "Give at least one target document ID (S-, R-, D-) with --docs.",
26176
+ "records.notDocIds": "--docs has values that are not document IDs (S-, R-, D-): {ids}",
26258
26177
  "records.skeleton": "(Content)",
26259
26178
  "records.createdNext": "Fill in the sections, remove the draft: true line, and list this file in paths when you commit.",
26260
26179
  "records.unknown": "No such record: {ids}",
@@ -26286,17 +26205,10 @@ var messages_default4 = {
26286
26205
  "help.specsNewStyle": "Requirement shape (usecase, default). Defaults to the config requirementStyle",
26287
26206
  "help.newTitle": "Document title",
26288
26207
  "help.newDescription": "One-line description",
26289
- "help.instructions": "List, read and create project instructions",
26290
- "help.instructionsList": "AGENTS.md and the instructions (with the title and description of each file in their folders)",
26291
- "help.instructionsShow": "An instruction's index.md as written, the designs that follow it and its folder's files",
26292
- "help.instructionsShowTargets": "Instruction folder names or I- IDs (one or more)",
26293
- "help.instructionsShowFile": "Read one file of the instruction folder (e.g. references/code-style.md)",
26294
- "help.instructionsNew": "Create an instruction folder and index.md skeleton with an issued ID (marked draft: true)",
26295
- "help.instructionsNewName": "Instruction folder name (e.g. code-review)",
26296
26208
  "help.recordsList": "Decision records of HEAD's history (newest first, uncommitted ones first)",
26297
26209
  "help.recordsListDoc": "One document's decision flow: the records of each commit that changed it",
26298
26210
  "help.recordsListSince": "Only those after that date (YYYY-MM-DD) or commit",
26299
- "help.check": "Check the format, IDs, references and draft marks of all specs, instructions and uncommitted records (exit code 1 on problems)",
26211
+ "help.check": "Check the format, IDs, references and draft marks of all specs and uncommitted records (exit code 1 on problems)",
26300
26212
  "help.listQ": "Only those whose title, description or text holds these words",
26301
26213
  "help.listAuthor": "Only those this Git author (name or email) changed",
26302
26214
  "help.listSort": "Sort key",
@@ -26313,18 +26225,9 @@ var messages_default4 = {
26313
26225
  "list.features": "Features",
26314
26226
  "list.commits": "Commits",
26315
26227
  "list.records": "Records",
26316
- "docs.otherResource": "Not a document this command reads: {ids}. Use {command}.",
26317
26228
  "docs.noMatch": "Nothing matches.",
26318
26229
  "docs.requirement": "requirement",
26319
26230
  "docs.design": "design",
26320
- "instructions.agents": "AGENTS.md \u2014 the index outside the block says which instruction to read for which work",
26321
- "instructions.noAgents": "No AGENTS.md \u2014 nothing tells when to read which instruction",
26322
- "instructions.filesLimited": "(the folder holds more; only the first {count} are shown)",
26323
- "instructions.empty": "No instructions yet. Create one with instructions new.",
26324
- "instructions.fileWithOne": "--file takes one instruction and does not go with --ref.",
26325
- "instructions.unknownFile": "The folder of instruction {name} has no such file: {path}",
26326
- "instructions.fileTooLarge": "(Too large to show: {size} bytes)",
26327
- "instructions.fileBinary": "(Not a text file: {size} bytes)",
26328
26231
  "records.empty": "No decision records yet.",
26329
26232
  "records.moreDocs": "and {count} more",
26330
26233
  "help.feedback": "Send a Gitifact bug or idea as an issue to Gitifact's repository (prints a new-issue address without gh)",
@@ -26340,11 +26243,11 @@ var messages_default4 = {
26340
26243
  "feedback.truncated": "The body is long, so the address holds only its beginning. Paste the full body below into the page.",
26341
26244
  "feedback.title": "Title: {title}",
26342
26245
  "update.text.projectWritten": "Set the project to gitifact {version} in {path}.",
26343
- "update.text.projectAhead": "The project is set to gitifact {project}. This CLI ({version}) is older, so it was not lowered. Install it with {command}.",
26246
+ "update.text.projectAhead": "The project is set to gitifact {project}. This CLI ({version}) is older, so it was not lowered. Run it through the project's launcher, node .gitifact/bin/cli.mjs.",
26344
26247
  "update.later.postponed": "The notice for {latest} is quiet until {until}. A newer release is announced right away.",
26345
26248
  "update.later.nothing": "No newer release to postpone (running {version}).",
26346
- "notice.release": "Version {latest} is available (running {version}). Update: {command}, then gitifact update \xB7 Later: gitifact update --later",
26347
- "notice.projectNewer": "This project is set to gitifact {project} (running {version}). Install it with {command}.",
26249
+ "notice.release": "Version {latest} is available (running {version}). Update: {command} \xB7 Later: gitifact update --later",
26250
+ "notice.projectNewer": "This project is set to gitifact {project} (running {version}). Run it through the project's launcher, node .gitifact/bin/cli.mjs.",
26348
26251
  "notice.projectOlder": "This CLI ({version}) is newer than the project ({project}). Run gitifact update to move the project to it.",
26349
26252
  "help.updateLater": "Quiet the announced newer release for 24 hours; a release newer than it is announced right away",
26350
26253
  "update.text.blockKept": "The project is set to a newer release than this CLI, so the instruction blocks were left as they are: {paths}"
@@ -26699,7 +26602,7 @@ function storeReader(cwd) {
26699
26602
  import { DatabaseSync } from "node:sqlite";
26700
26603
  import { lstat, mkdir, rm, writeFile } from "node:fs/promises";
26701
26604
  import { join as join2 } from "node:path";
26702
- var CACHE_FORMAT = 12;
26605
+ var CACHE_FORMAT = 13;
26703
26606
  var CACHE_DIR = ".gitifact/cache";
26704
26607
  var SCHEMA = `
26705
26608
  -- Working-tree files already read: the modification time and size decide whether a file is read again.
@@ -26885,6 +26788,7 @@ function createDocumentCache(root, database) {
26885
26788
  async function read(path3, seen) {
26886
26789
  try {
26887
26790
  if (isWikiPage(path3)) return { problem: docProblem("WIKI_REMOVED", path3) };
26791
+ if (isInstructionPath(path3)) return path3.endsWith("/index.md") && classifyDocPath(path3).type === "doc" ? { problem: docProblem("INSTRUCTION_MOVED", instructionFolderOf(path3)) } : {};
26888
26792
  if (classifyDocPath(path3).type !== "doc") return {};
26889
26793
  if (seen.link) return { problem: docProblem("PATH_UNSUPPORTED", path3) };
26890
26794
  if (seen.size > FILE_LIMIT) return { problem: docProblem("FILE_TOO_LARGE", path3) };
@@ -26959,13 +26863,6 @@ function createDocumentCache(root, database) {
26959
26863
  return true;
26960
26864
  }
26961
26865
  };
26962
- const instructionFile3 = (path3) => {
26963
- try {
26964
- return classifyDocPath(path3).type === "instruction-file";
26965
- } catch {
26966
- return false;
26967
- }
26968
- };
26969
26866
  return {
26970
26867
  sync,
26971
26868
  /**
@@ -26982,7 +26879,7 @@ function createDocumentCache(root, database) {
26982
26879
  if (history) seen.set(HISTORY_PATH, { mtime: history.mtimeMs, size: history.size, link: history.isSymbolicLink() || !history.isFile() });
26983
26880
  const read2 = await Promise.all([...seen].map(async ([path3, s]) => {
26984
26881
  if (!readable(path3)) return { path: path3 };
26985
- if (instructionFile3(path3) && !path3.endsWith(".md")) return { path: path3, text: "" };
26882
+ if (isInstructionPath(path3) && !path3.endsWith(".md")) return { path: path3, text: "" };
26986
26883
  if (s.link) return { path: path3, problem: docProblem("PATH_UNSUPPORTED", path3) };
26987
26884
  if (s.size > (path3 === HISTORY_PATH ? 64 * FILE_LIMIT : FILE_LIMIT)) return { path: path3, problem: docProblem("FILE_TOO_LARGE", path3) };
26988
26885
  try {
@@ -28079,88 +27976,6 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
28079
27976
  }
28080
27977
  }
28081
27978
 
28082
- // src/adapters/filesystem/instruction-folder.ts
28083
- import { lstat as lstat6, open as open3, readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
28084
- import { join as join7 } from "node:path";
28085
- var INSTRUCTION_FILE_LIMIT = 500;
28086
- var INSTRUCTION_TEXT_LIMIT = 512 * 1024;
28087
- var folderOf3 = (root, instructionPath) => join7(root, ...instructionPath.split("/").slice(0, -1));
28088
- async function listInstructionFiles(root, instructionPath) {
28089
- const files = [];
28090
- let limited = false;
28091
- async function visit2(folder, prefix, depth) {
28092
- if (depth > 10) return;
28093
- const entries = await readdir4(folder, { withFileTypes: true }).catch(() => []);
28094
- for (const entry2 of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
28095
- if (files.length >= INSTRUCTION_FILE_LIMIT) {
28096
- limited = true;
28097
- return;
28098
- }
28099
- const path3 = prefix + entry2.name;
28100
- if (entry2.isDirectory()) await visit2(join7(folder, entry2.name), path3 + "/", depth + 1);
28101
- else if (entry2.isFile() && path3 !== INSTRUCTION_FILE) files.push({ path: path3, size: (await lstat6(join7(folder, entry2.name))).size });
28102
- }
28103
- }
28104
- await visit2(folderOf3(root, instructionPath), "", 0);
28105
- return { files, limited };
28106
- }
28107
- var FRONTMATTER_WINDOW = 8 * 1024;
28108
- async function describeFiles(root, instructionPath, files) {
28109
- const folder = folderOf3(root, instructionPath);
28110
- return Promise.all(files.map(async (file2) => {
28111
- if (!isInstructionReference(file2.path)) return file2;
28112
- let handle;
28113
- try {
28114
- handle = await open3(join7(folder, ...file2.path.split("/")), "r");
28115
- const buffer = Buffer.alloc(Math.min(FRONTMATTER_WINDOW, file2.size));
28116
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
28117
- return { ...file2, ...parseInstructionFile(file2.path, buffer.subarray(0, bytesRead).toString("utf8")) };
28118
- } catch {
28119
- return file2;
28120
- } finally {
28121
- await handle?.close();
28122
- }
28123
- }));
28124
- }
28125
- async function readInstructionFiles(root, instructionPath) {
28126
- const { files, limited } = await listInstructionFiles(root, instructionPath);
28127
- return { files: await describeFiles(root, instructionPath, files), limited };
28128
- }
28129
- async function readInstructionFile(root, instructionPath, path3) {
28130
- const parts = path3.split("/");
28131
- if (parts.some((p) => !p || p === "." || p === ".." || /[\\:\0]/.test(p))) return void 0;
28132
- let full = folderOf3(root, instructionPath);
28133
- for (const [index, part] of parts.entries()) {
28134
- full = join7(full, part);
28135
- const info5 = await lstat6(full).catch(() => void 0);
28136
- if (!info5 || info5.isSymbolicLink() || (index < parts.length - 1 ? !info5.isDirectory() : !info5.isFile())) return void 0;
28137
- }
28138
- const size = (await lstat6(full)).size;
28139
- if (size > INSTRUCTION_TEXT_LIMIT) return { text: null, size, binary: false, tooLarge: true };
28140
- const bytes = await readFile5(full);
28141
- try {
28142
- if (bytes.includes(0)) throw new TypeError("binary");
28143
- return { text: new TextDecoder("utf-8", { fatal: true }).decode(bytes), size, binary: false, tooLarge: false };
28144
- } catch {
28145
- return { text: null, size, binary: true, tooLarge: false };
28146
- }
28147
- }
28148
-
28149
- // src/adapters/filesystem/agents-file.ts
28150
- import { lstat as lstat7, readFile as readFile6 } from "node:fs/promises";
28151
- import { join as join8 } from "node:path";
28152
- var LIMIT = 1024 * 1024;
28153
- async function readAgentsFile(root) {
28154
- const file2 = join8(root, "AGENTS.md");
28155
- const info5 = await lstat7(file2).catch(() => void 0);
28156
- if (!info5?.isFile() || info5.size > LIMIT) return null;
28157
- try {
28158
- return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(await readFile6(file2));
28159
- } catch {
28160
- return null;
28161
- }
28162
- }
28163
-
28164
27979
  // src/queries/document-states.ts
28165
27980
  var isDocument = (path3) => {
28166
27981
  try {
@@ -28229,8 +28044,8 @@ async function documentsAtHead(project, head, paths2) {
28229
28044
 
28230
28045
  // src/server/checkout/stamp.ts
28231
28046
  import { createHash as createHash2 } from "node:crypto";
28232
- import { lstat as lstat8 } from "node:fs/promises";
28233
- import { join as join9 } from "node:path";
28047
+ import { lstat as lstat6 } from "node:fs/promises";
28048
+ import { join as join7 } from "node:path";
28234
28049
  function createStampReader(root, inherited = process.env) {
28235
28050
  const runner = createGitRunner();
28236
28051
  const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_REPLACE_OBJECTS: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "0", LC_ALL: "C" };
@@ -28242,10 +28057,10 @@ function createStampReader(root, inherited = process.env) {
28242
28057
  ]);
28243
28058
  const paths2 = status.split("\0").filter(Boolean).map((entry2) => entry2.slice(3)).filter((path3) => !path3.startsWith(".gitifact/cache/"));
28244
28059
  const seen = await Promise.all(paths2.map(async (path3) => {
28245
- const info5 = await lstat8(join9(root, ...path3.split("/"))).catch(() => void 0);
28060
+ const info5 = await lstat6(join7(root, ...path3.split("/"))).catch(() => void 0);
28246
28061
  return path3 + " " + (info5 ? info5.mtimeMs + ":" + info5.size : "-");
28247
28062
  }));
28248
- const mailmap = await lstat8(join9(root, ".mailmap")).catch(() => void 0);
28063
+ const mailmap = await lstat6(join7(root, ".mailmap")).catch(() => void 0);
28249
28064
  seen.push(".mailmap " + (mailmap ? mailmap.mtimeMs + ":" + mailmap.size : "-"));
28250
28065
  return createHash2("sha256").update(head.trim() + "\n" + seen.sort().join("\n")).digest("hex").slice(0, 20);
28251
28066
  };
@@ -28258,7 +28073,7 @@ async function settled(reads) {
28258
28073
  if (failure2) throw failure2.reason;
28259
28074
  return results.map((r) => r.value);
28260
28075
  }
28261
- var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
28076
+ var folderOf3 = (path3) => path3.split("/").slice(0, 3).join("/");
28262
28077
  function checkoutDocuments(states, authors) {
28263
28078
  const documents = states.documents;
28264
28079
  const standing2 = (id) => {
@@ -28273,7 +28088,7 @@ function checkoutDocuments(states, authors) {
28273
28088
  };
28274
28089
  const arranged = arrangeDocuments(documents);
28275
28090
  const features = arranged.features.map(({ index, requirements, designs }) => {
28276
- const entry2 = authors.get(folderOf4(index.path));
28091
+ const entry2 = authors.get(folderOf3(index.path));
28277
28092
  return {
28278
28093
  id: index.id,
28279
28094
  path: index.path,
@@ -28298,17 +28113,7 @@ function checkoutDocuments(states, authors) {
28298
28113
  updatedAt: entry2?.latest ?? null
28299
28114
  };
28300
28115
  });
28301
- const instructions2 = arranged.instructions.map((k) => ({
28302
- id: k.id,
28303
- name: k.name,
28304
- path: k.path,
28305
- title: k.title,
28306
- description: k.description,
28307
- body: k.body,
28308
- ...standing2(k.id),
28309
- updatedAt: authors.get(folderOf4(k.path))?.latest ?? null
28310
- }));
28311
- return { features, instructions: instructions2, orphans: arranged.orphans };
28116
+ return { features, orphans: arranged.orphans };
28312
28117
  }
28313
28118
  function createCheckoutReader(root, cache, inherited = process.env) {
28314
28119
  const reader = storeReader(root);
@@ -28337,16 +28142,15 @@ function createCheckoutReader(root, cache, inherited = process.env) {
28337
28142
  head ? cache.log.contributors(head) : Promise.resolve([])
28338
28143
  ]);
28339
28144
  const states = await documentStates({ head: async () => head, reader }, current3.documents);
28340
- const { features, instructions: instructions2, orphans } = checkoutDocuments(states, authors);
28145
+ const { features, orphans } = checkoutDocuments(states, authors);
28341
28146
  const problems = [...current3.problems, ...orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
28342
28147
  if (await readHead() !== head) throw new StoreError(t2("specReader.projectChanged"));
28343
28148
  return {
28344
28149
  head,
28345
28150
  stamp,
28346
28151
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
28347
- working: head ? !!dirty.trim() : features.length > 0 || instructions2.length > 0,
28152
+ working: head ? !!dirty.trim() : features.length > 0,
28348
28153
  features,
28349
- instructions: instructions2,
28350
28154
  problems,
28351
28155
  people,
28352
28156
  folders: authors
@@ -28369,19 +28173,7 @@ function createCheckoutReader(root, cache, inherited = process.env) {
28369
28173
  }
28370
28174
  return value;
28371
28175
  };
28372
- return {
28373
- base,
28374
- /** Every instruction by name with the other files of its folder, and AGENTS.md: few enough to read whole. */
28375
- async instructions() {
28376
- const checkout = await base();
28377
- const instructions2 = await Promise.all([...checkout.instructions].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0).map(async (instruction2) => {
28378
- const { files, limited } = instruction2.state === "deleted" ? { files: [], limited: false } : await readInstructionFiles(root, instruction2.path);
28379
- return { ...instruction2, files, filesLimited: limited };
28380
- }));
28381
- const agentsText = await readAgentsFile(root);
28382
- return { instructions: instructions2, agents: agentsText === null ? null : { path: "AGENTS.md", body: agentsText, updatedAt: checkout.folders.get("AGENTS.md")?.latest ?? null } };
28383
- }
28384
- };
28176
+ return { base };
28385
28177
  }
28386
28178
 
28387
28179
  // src/queries/paging.ts
@@ -28546,11 +28338,11 @@ async function workingChange2(source2, id) {
28546
28338
  }
28547
28339
 
28548
28340
  // src/queries/search.ts
28549
- var SEARCH_GROUPS = ["feature", "requirement", "design", "instruction", "record", "commit"];
28341
+ var SEARCH_GROUPS = ["feature", "requirement", "design", "record", "commit"];
28550
28342
  var FIRST = 5;
28551
28343
  var RECENT = 6;
28552
28344
  var place2 = (path3) => path3.replace(/^\.gitifact\//, "");
28553
- var folderOf5 = (path3) => path3.split("/").slice(0, 3).join("/");
28345
+ var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
28554
28346
  async function searchRecords(source2, request) {
28555
28347
  const head = request.head ?? await source2.head();
28556
28348
  const query = request.q.trim().toLowerCase();
@@ -28567,14 +28359,7 @@ async function searchRecords(source2, request) {
28567
28359
  var empty = () => ({ total: 0, next: null, hits: [] });
28568
28360
  async function recent(source2, head) {
28569
28361
  const [{ documents }, folders] = await Promise.all([source2.cache.documents.list(), head ? source2.cache.log.folders(head) : /* @__PURE__ */ new Map()]);
28570
- const hits = documents.filter((d) => d.kind === "feature" || d.kind === "instruction").map((d) => ({ doc: d, latest: folders.get(folderOf5(d.path))?.latest })).filter((d) => !!d.latest).sort((a, b) => Date.parse(b.latest) - Date.parse(a.latest) || a.doc.id.localeCompare(b.doc.id)).slice(0, RECENT).map(({ doc }) => ({
28571
- id: doc.id,
28572
- kind: doc.kind,
28573
- title: doc.title,
28574
- where: place2(doc.path),
28575
- line: doc.description,
28576
- ...doc.kind === "feature" ? { featureId: doc.id } : {}
28577
- }));
28362
+ const hits = documents.filter((d) => d.kind === "feature").map((d) => ({ doc: d, latest: folders.get(folderOf4(d.path))?.latest })).filter((d) => !!d.latest).sort((a, b) => Date.parse(b.latest) - Date.parse(a.latest) || a.doc.id.localeCompare(b.doc.id)).slice(0, RECENT).map(({ doc }) => ({ id: doc.id, kind: "feature", title: doc.title, where: place2(doc.path), line: doc.description, featureId: doc.id }));
28578
28363
  return { group: "recent", total: hits.length, next: null, hits };
28579
28364
  }
28580
28365
 
@@ -28609,7 +28394,6 @@ function checkoutIndex(base) {
28609
28394
  requirements: f.requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description })),
28610
28395
  designs: f.designs.map((d) => ({ id: d.id, path: d.path, title: d.title }))
28611
28396
  })),
28612
- instructions: base.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title })),
28613
28397
  people: base.people.map((p) => ({ name: p.name, email: p.email }))
28614
28398
  };
28615
28399
  }
@@ -28703,12 +28487,12 @@ function recordRoutes(root, sessionId, env) {
28703
28487
  const cursorGone2 = () => new HttpError(404, "NOT_FOUND", t2("server.cursorNotFound"));
28704
28488
  return [
28705
28489
  // The checkout a part at a time, each shaped by the query layer (`queries/checkout.ts`): the frame every screen reads
28706
- // first, the feature list and one feature, the instructions, and the contributors.
28490
+ // first, the feature list and one feature, and the contributors.
28707
28491
  route({ method: "GET", path: "/api/v1/checkout", session: true, unreadable: unreadable2, handle: async () => {
28708
28492
  const base = await checkout.base();
28709
- return ok(browserCheckoutV1.parse({
28493
+ return ok(browserCheckoutV2.parse({
28710
28494
  contract: "browser-checkout",
28711
- version: 1,
28495
+ version: 2,
28712
28496
  sessionId,
28713
28497
  head: base.head,
28714
28498
  observedAt: base.observedAt,
@@ -28728,7 +28512,6 @@ function recordRoutes(root, sessionId, env) {
28728
28512
  if (!feature2) throw new HttpError(404, "NOT_FOUND", t2("server.featureNotFound"));
28729
28513
  return ok(browserFeatureV2.parse({ contract: "browser-feature", version: 2, sessionId, feature: feature2 }));
28730
28514
  } }),
28731
- route({ method: "GET", path: "/api/v1/instructions", session: true, unreadable: unreadable2, handle: async () => ok(browserInstructionsV1.parse({ contract: "browser-instructions", version: 1, sessionId, ...await checkout.instructions() })) }),
28732
28515
  route({ method: "GET", path: "/api/v1/contributors", session: true, query: browserContributorsQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28733
28516
  const page = listContributors(await checkout.base(), query.q, { after: query.after, limit: query.limit ?? PAGE });
28734
28517
  if (!page) throw cursorGone2();
@@ -28821,35 +28604,28 @@ function recordRoutes(root, sessionId, env) {
28821
28604
  if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.commitFileNotFound"));
28822
28605
  return ok(browserCommitFileV1.parse({ contract: "browser-commit-file", version: 1, sessionId, commit: query.commit, ...found }));
28823
28606
  } }),
28824
- // One file of an instruction folder, read from the working tree when the reader opens it; the checkout lists the files.
28825
- route({ method: "GET", path: "/api/v1/instructions/file", session: true, query: browserInstructionFileQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28826
- const instruction2 = (await cache.documents.list()).documents.find((d) => d.id === query.id && d.kind === "instruction");
28827
- const found = instruction2 && await readInstructionFile(root, instruction2.path, query.path);
28828
- if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.instructionFileNotFound"));
28829
- return ok(browserInstructionFileV1.parse({ contract: "browser-instruction-file", version: 1, sessionId, id: query.id, path: query.path, ...found }));
28830
- } }),
28831
28607
  // The search box's groups as the query layer works them out (`queries/search.ts`); the browser only draws them.
28832
- route({ method: "GET", path: "/api/v1/search", session: true, query: browserSearchQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
28608
+ route({ method: "GET", path: "/api/v1/search", session: true, query: browserSearchQueryV3, unreadable: unreadable2, handle: async ({ query }) => {
28833
28609
  const groups = await searchRecords(source2, query);
28834
28610
  if (!groups) throw cursorGone2();
28835
- return ok(browserSearchV3.parse({ contract: "browser-search", version: 3, sessionId, query: query.q, groups }));
28611
+ return ok(browserSearchV4.parse({ contract: "browser-search", version: 4, sessionId, query: query.q, groups }));
28836
28612
  } })
28837
28613
  ];
28838
28614
  }
28839
28615
 
28840
28616
  // src/server/routes/asset-routes.ts
28841
- import { lstat as lstat9, readFile as readFile7 } from "node:fs/promises";
28842
- import { join as join10 } from "node:path";
28617
+ import { lstat as lstat7, readFile as readFile5 } from "node:fs/promises";
28618
+ import { join as join8 } from "node:path";
28843
28619
  function assetRoutes(root) {
28844
28620
  return [
28845
28621
  route({ method: "GET", path: "/api/v1/assets/*", session: false, handle: async ({ path: path3, response }) => {
28846
28622
  const relative = path3.slice("/api/v1/assets/".length);
28847
28623
  const assetPath = ASSETS_DIR + "/" + relative;
28848
28624
  if (!isAssetPath(assetPath)) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
28849
- const file2 = join10(root, ...assetPath.split("/"));
28850
- const stat3 = await lstat9(file2).catch(() => void 0);
28625
+ const file2 = join8(root, ...assetPath.split("/"));
28626
+ const stat3 = await lstat7(file2).catch(() => void 0);
28851
28627
  if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > 20 * 1024 * 1024) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
28852
- const bytes = await readFile7(file2);
28628
+ const bytes = await readFile5(file2);
28853
28629
  const extension = assetExtension(relative);
28854
28630
  const inline = ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(extension);
28855
28631
  response.writeHead(200, {
@@ -28866,7 +28642,7 @@ function assetRoutes(root) {
28866
28642
  }
28867
28643
 
28868
28644
  // src/server/browser-server.ts
28869
- var readBundledChangelog = (language) => readFile8(new URL("./i18n/" + language + "/changelog.md", import.meta.url), "utf8").catch((error62) => {
28645
+ var readBundledChangelog = (language) => readFile6(new URL("./i18n/" + language + "/changelog.md", import.meta.url), "utf8").catch((error62) => {
28870
28646
  if (error62.code === "ENOENT") return null;
28871
28647
  throw error62;
28872
28648
  });
@@ -28996,14 +28772,14 @@ async function runBrowser(options, version2) {
28996
28772
  }
28997
28773
 
28998
28774
  // src/commands/spec-init.ts
28999
- import { readdir as readdir5 } from "node:fs/promises";
29000
- import { join as join14 } from "node:path";
28775
+ import { readdir as readdir4 } from "node:fs/promises";
28776
+ import { join as join13 } from "node:path";
29001
28777
 
29002
28778
  // src/adapters/git/init-repository.ts
29003
28779
  import { createHash as createHash3 } from "node:crypto";
29004
- import { lstat as lstat10, readFile as readFile9 } from "node:fs/promises";
29005
- import { join as join11 } from "node:path";
29006
- var markerPresent = (path3) => lstat10(path3).then(() => true, (error62) => {
28780
+ import { lstat as lstat8, readFile as readFile7 } from "node:fs/promises";
28781
+ import { join as join9 } from "node:path";
28782
+ var markerPresent = (path3) => lstat8(path3).then(() => true, (error62) => {
29007
28783
  if (error62.code === "ENOENT") return false;
29008
28784
  throw error62;
29009
28785
  });
@@ -29026,10 +28802,10 @@ function initRepository(cwd, inherited = process.env) {
29026
28802
  const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
29027
28803
  const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
29028
28804
  for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
29029
- if (await markerPresent(join11(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
28805
+ if (await markerPresent(join9(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
29030
28806
  }
29031
28807
  if (state2.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
29032
- const index = await readFile9(indexPath).catch((error62) => {
28808
+ const index = await readFile7(indexPath).catch((error62) => {
29033
28809
  if (error62.code === "ENOENT") return Buffer.alloc(0);
29034
28810
  throw error62;
29035
28811
  });
@@ -29064,14 +28840,14 @@ function initRepository(cwd, inherited = process.env) {
29064
28840
 
29065
28841
  // src/adapters/filesystem/document-file.ts
29066
28842
  import { randomBytes } from "node:crypto";
29067
- import { lstat as lstat11, mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
29068
- import { join as join12 } from "node:path";
28843
+ import { lstat as lstat9, mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
28844
+ import { join as join10 } from "node:path";
29069
28845
  var generateId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
29070
28846
  async function createDocumentFile(root, path3, text4) {
29071
28847
  const parts = path3.split("/");
29072
28848
  for (let i = 1; i < parts.length; i++) {
29073
- const folder = join12(root, ...parts.slice(0, i));
29074
- const info5 = await lstat11(folder).catch((error62) => {
28849
+ const folder = join10(root, ...parts.slice(0, i));
28850
+ const info5 = await lstat9(folder).catch((error62) => {
29075
28851
  if (error62.code === "ENOENT") return void 0;
29076
28852
  throw error62;
29077
28853
  });
@@ -29081,7 +28857,7 @@ async function createDocumentFile(root, path3, text4) {
29081
28857
  }
29082
28858
  if (!info5.isDirectory() || info5.isSymbolicLink()) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
29083
28859
  }
29084
- await writeFile2(join12(root, ...parts), text4, { flag: "wx" });
28860
+ await writeFile2(join10(root, ...parts), text4, { flag: "wx" });
29085
28861
  }
29086
28862
  var LINE_ENDINGS_PATH = ".gitifact/.gitattributes";
29087
28863
  var LINE_ENDINGS = "# Written by gitifact init: documents keep LF on every OS, so what Git holds and the files read the same.\n* text=auto eol=lf\n";
@@ -29095,19 +28871,50 @@ async function writeLineEndings(root) {
29095
28871
  }
29096
28872
  }
29097
28873
 
28874
+ // src/adapters/filesystem/launcher-file.ts
28875
+ import { lstat as lstat10, mkdir as mkdir4, readFile as readFile8, writeFile as writeFile3 } from "node:fs/promises";
28876
+ import { join as join11 } from "node:path";
28877
+ var LAUNCHER_PATH = ".gitifact/bin/cli.mjs";
28878
+ var readBundledLauncher = () => readFile8(new URL("./launcher/cli.mjs", import.meta.url), "utf8").catch((error62) => {
28879
+ if (error62.code === "ENOENT") return void 0;
28880
+ throw error62;
28881
+ });
28882
+ async function refreshLauncher(root) {
28883
+ const text4 = await readBundledLauncher();
28884
+ return text4 === void 0 ? false : writeLauncher(root, text4.replace(/\r\n/g, "\n"));
28885
+ }
28886
+ async function writeLauncher(root, text4) {
28887
+ const folder = join11(root, ".gitifact", "bin");
28888
+ const file2 = join11(folder, "cli.mjs");
28889
+ const folderInfo = await lstat10(folder).catch((error62) => {
28890
+ if (error62.code === "ENOENT") return void 0;
28891
+ throw error62;
28892
+ });
28893
+ if (folderInfo && (!folderInfo.isDirectory() || folderInfo.isSymbolicLink())) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
28894
+ if (!folderInfo) await mkdir4(folder);
28895
+ const info5 = await lstat10(file2).catch((error62) => {
28896
+ if (error62.code === "ENOENT") return void 0;
28897
+ throw error62;
28898
+ });
28899
+ if (info5 && (!info5.isFile() || info5.isSymbolicLink())) throw Object.assign(new Error(file2), { code: "ENOTDIR" });
28900
+ if (info5 && (await readFile8(file2, "utf8")).replace(/\r\n/g, "\n") === text4) return false;
28901
+ await writeFile3(file2, text4);
28902
+ return true;
28903
+ }
28904
+
29098
28905
  // src/adapters/filesystem/managed-file.ts
29099
- import { open as open4, readFile as readFile10, mkdir as mkdir4, realpath as realpath2, rename as rename2, link as link2, unlink as unlink2 } from "node:fs/promises";
29100
- import { join as join13, dirname } from "node:path";
28906
+ import { open as open3, readFile as readFile9, mkdir as mkdir5, realpath as realpath2, rename as rename2, link as link2, unlink as unlink2 } from "node:fs/promises";
28907
+ import { join as join12, dirname } from "node:path";
29101
28908
  import { randomUUID as randomUUID4 } from "node:crypto";
29102
28909
  var DEFAULT_LIMIT = 1024 * 1024;
29103
28910
  async function managedPath(root, path3, create = false) {
29104
28911
  let full = root;
29105
28912
  const parts = path3.split("/");
29106
28913
  for (let i = 0; i < parts.length; i++) {
29107
- full = join13(full, parts[i]);
28914
+ full = join12(full, parts[i]);
29108
28915
  let info5 = await fileInfo(full);
29109
28916
  if (!info5 && create && i < parts.length - 1) {
29110
- await mkdir4(full).catch((error62) => {
28917
+ await mkdir5(full).catch((error62) => {
29111
28918
  if (error62.code !== "EEXIST") throw error62;
29112
28919
  });
29113
28920
  info5 = await fileInfo(full);
@@ -29123,7 +28930,7 @@ async function managedRead(root, path3, limit = DEFAULT_LIMIT) {
29123
28930
  const before = await fileInfo(full);
29124
28931
  if (!before) return null;
29125
28932
  if (before.size > limit) throw new InitError("FILE_TOO_LARGE", t2("managedFile.tooLarge", { kib: limit / 1024, path: path3 }));
29126
- const bytes = await readFile10(full);
28933
+ const bytes = await readFile9(full);
29127
28934
  await managedPath(root, path3);
29128
28935
  const after = await fileInfo(full);
29129
28936
  if (!after || before.ino !== after.ino || before.dev !== after.dev || before.mtimeMs !== after.mtimeMs || bytes.length > limit) {
@@ -29152,8 +28959,8 @@ async function managedWrite(root, path3, previous, next, recheck) {
29152
28959
  if (previous !== null) await unlink2(full);
29153
28960
  return;
29154
28961
  }
29155
- const temp = join13(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
29156
- const handle = await open4(temp, "wx", 384);
28962
+ const temp = join12(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
28963
+ const handle = await open3(temp, "wx", 384);
29157
28964
  const tempOwner = await handle.stat();
29158
28965
  try {
29159
28966
  try {
@@ -29164,7 +28971,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
29164
28971
  }
29165
28972
  await recheck();
29166
28973
  await unchanged();
29167
- if (await managedRead(root, path3) !== previous || await readFile10(temp, "utf8") !== next) throw new InitError("INPUT_CHANGED", t2("managedFile.publishTargetChanged", { path: path3 }));
28974
+ if (await managedRead(root, path3) !== previous || await readFile9(temp, "utf8") !== next) throw new InitError("INPUT_CHANGED", t2("managedFile.publishTargetChanged", { path: path3 }));
29168
28975
  if (previous === null) await link2(temp, full);
29169
28976
  else await rename2(temp, full);
29170
28977
  } finally {
@@ -29175,10 +28982,10 @@ async function managedWrite(root, path3, previous, next, recheck) {
29175
28982
  }
29176
28983
 
29177
28984
  // src/commands/agent-block.ts
29178
- import { readFile as readFile12 } from "node:fs/promises";
28985
+ import { readFile as readFile11 } from "node:fs/promises";
29179
28986
 
29180
28987
  // src/commands/guide.ts
29181
- import { readFile as readFile11 } from "node:fs/promises";
28988
+ import { readFile as readFile10 } from "node:fs/promises";
29182
28989
 
29183
28990
  // src/commands/output.ts
29184
28991
  var formats = ["text", "json"];
@@ -29213,9 +29020,9 @@ var section = (label, lines2, indent = " ") => lines2.length ? [label, ...lines
29213
29020
  var text2 = (lines2) => lines2.join("\n") + "\n";
29214
29021
 
29215
29022
  // src/commands/guide.ts
29216
- var guideTopics = ["workflow", "spec", "design", "instructions", "records", "writing", "commit", "migrate"];
29023
+ var guideTopics = ["workflow", "spec", "design", "records", "writing", "commit", "migrate"];
29217
29024
  var isGuideTopic = (value) => guideTopics.includes(value);
29218
- var readBundledGuide = (name2, lang) => readFile11(new URL("./i18n/" + lang + "/docs/" + name2 + ".md", import.meta.url), "utf8");
29025
+ var readBundledGuide = (name2, lang) => readFile10(new URL("./i18n/" + lang + "/docs/" + name2 + ".md", import.meta.url), "utf8");
29219
29026
  function guideSummary(source2) {
29220
29027
  const { fields: fields2 } = parseFrontmatterBlock(source2.replace(/\r\n/g, "\n"), "guide");
29221
29028
  const value = (key) => {
@@ -29256,7 +29063,7 @@ function boilerplateFor(path3) {
29256
29063
  const name2 = path3 === "AGENTS.md" ? path3 : path3.split("/").pop().replace(/^\./, "").replace(/\.md$/, "");
29257
29064
  return "# " + name2 + "\n\nProject-specific guidance for AI coding agents.\n";
29258
29065
  }
29259
- var readBundledBlock = (lang) => readFile12(new URL("./i18n/" + lang + "/block.md", import.meta.url), "utf8");
29066
+ var readBundledBlock = (lang) => readFile11(new URL("./i18n/" + lang + "/block.md", import.meta.url), "utf8");
29260
29067
  async function renderAgentBlock(controls = {}, lang = getLanguage()) {
29261
29068
  const body2 = await (controls.readBlock ?? readBundledBlock)(lang);
29262
29069
  return AGENT_START + "\n" + body2.trimEnd().replace(/\{topics\}/g, guideTopics.join(", ")) + "\n" + AGENT_END;
@@ -29285,15 +29092,9 @@ function isWrapperFile(text4) {
29285
29092
  return lines2.length > 0 && lines2.every((line2) => /^@(?:\.\/)?[^\s]+$/.test(line2) && candidatePaths.includes(line2.replace(/^@(?:\.\/)?/, "")));
29286
29093
  }
29287
29094
  var CLAUDE_WRAPPER_PATH = "CLAUDE.md";
29288
- function claudeWrapperFor(agents2) {
29289
- return "@AGENTS.md" + eolOf(agents2);
29290
- }
29291
29095
  function isGeneratedClaudeWrapper(text4) {
29292
29096
  return text4.trim() === "@AGENTS.md";
29293
29097
  }
29294
- function lacksClaudeFile(existing) {
29295
- return !existing.has("CLAUDE.md") && !existing.has(".claude/CLAUDE.md");
29296
- }
29297
29098
  function resolveAgentPaths(preset, existing) {
29298
29099
  if (preset && preset !== "all") {
29299
29100
  const order = agentPresets[preset];
@@ -29327,7 +29128,7 @@ function removeBlock(text4, boilerplate) {
29327
29128
  }
29328
29129
 
29329
29130
  // src/commands/agent-docs.ts
29330
- var skippedAgentDocs = (language = explicitLanguage() ?? getLanguage()) => ({ mode: "skip", paths: [], writes: [], missing: [], language });
29131
+ var skippedAgentDocs = (language = explicitLanguage() ?? getLanguage()) => ({ mode: "skip", paths: [], writes: [], language });
29331
29132
  var blockOf = (text4) => {
29332
29133
  try {
29333
29134
  const found = findBlock(text4);
@@ -29362,7 +29163,7 @@ async function planAgentDocs(root, options) {
29362
29163
  paths2.push(CLAUDE_WRAPPER_PATH);
29363
29164
  writes.push({ path: CLAUDE_WRAPPER_PATH, previous: wrapper, next: null });
29364
29165
  }
29365
- return { mode: "remove", paths: paths2, writes, missing: [], language };
29166
+ return { mode: "remove", paths: paths2, writes, language };
29366
29167
  }
29367
29168
  let block;
29368
29169
  const blockFor = async () => block ??= await renderAgentBlock(options, language);
@@ -29377,19 +29178,11 @@ async function planAgentDocs(root, options) {
29377
29178
  paths2.push(create);
29378
29179
  writes.push({ path: create, previous: null, next: injectBlock(null, await blockFor(), boilerplateFor(create)) });
29379
29180
  }
29380
- const missing2 = [];
29381
- if (paths2.includes("AGENTS.md") && lacksClaudeFile(existing)) {
29382
- if (options.onlyExisting) missing2.push(CLAUDE_WRAPPER_PATH);
29383
- else {
29384
- paths2.push(CLAUDE_WRAPPER_PATH);
29385
- writes.push({ path: CLAUDE_WRAPPER_PATH, previous: null, next: claudeWrapperFor(existing.get("AGENTS.md") ?? null) });
29386
- }
29387
- }
29388
29181
  for (const [path3, text4] of existing) {
29389
29182
  if (paths2.includes(path3) || !isWrapperFile(text4) || !findBlock(text4)) continue;
29390
29183
  writes.push({ path: path3, previous: text4, next: removeBlock(text4, boilerplateFor(path3)) });
29391
29184
  }
29392
- return { mode: "install", paths: paths2, writes, missing: missing2, language };
29185
+ return { mode: "install", paths: paths2, writes, language };
29393
29186
  }
29394
29187
  async function applyAgentDocs(root, plan, recheck) {
29395
29188
  for (const write of plan.writes) await managedWrite(root, write.path, write.previous, write.next, recheck);
@@ -29498,6 +29291,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
29498
29291
  current2 = next;
29499
29292
  }
29500
29293
  await applyAgentDocs(root, docsPlan, unchanged);
29294
+ if (agentDocs && !(config3.cli && isNewerRelease(config3.cli, agentDocs.version))) await refreshLauncher(root);
29501
29295
  return result(settled2.config, "already-initialized", await writeLineEndings(root));
29502
29296
  };
29503
29297
  const base = { schemaVersion: SCHEMA_VERSION, baseline: first.state.head.commit ? { kind: "commit", objectFormat: first.state.repository.objectFormat, commit: first.state.head.commit } : { kind: "empty" } };
@@ -29509,13 +29303,14 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
29509
29303
  };
29510
29304
  await published();
29511
29305
  await applyAgentDocs(root, docsPlan, published);
29306
+ if (agentDocs) await refreshLauncher(root);
29512
29307
  return result(config2, outcome, await writeLineEndings(root));
29513
29308
  };
29514
29309
  const old = await readConfigFile(root);
29515
29310
  if (old !== void 0) return existing(old);
29516
29311
  if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
29517
29312
  const checkRecords = async () => {
29518
- const entries = await readdir5(join14(root, ".gitifact")).catch((e) => {
29313
+ const entries = await readdir4(join13(root, ".gitifact")).catch((e) => {
29519
29314
  if (e.code === "ENOENT") return [];
29520
29315
  throw e;
29521
29316
  });
@@ -29553,22 +29348,22 @@ var fetchLatestVersion = async (signal) => {
29553
29348
  };
29554
29349
 
29555
29350
  // src/adapters/filesystem/user-cache.ts
29556
- import { mkdir as mkdir5, readFile as readFile13, rename as rename3, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
29351
+ import { mkdir as mkdir6, readFile as readFile12, rename as rename3, unlink as unlink3, writeFile as writeFile4 } from "node:fs/promises";
29557
29352
  import { homedir } from "node:os";
29558
- import { join as join15 } from "node:path";
29353
+ import { join as join14 } from "node:path";
29559
29354
  import { randomUUID as randomUUID5 } from "node:crypto";
29560
29355
  function userCacheDirectory(env = process.env, platform = process.platform, home = homedir()) {
29561
29356
  if (env.GITIFACT_CACHE_DIR) return env.GITIFACT_CACHE_DIR;
29562
- if (platform === "win32") return join15(env.LOCALAPPDATA || join15(home, "AppData", "Local"), "gitifact");
29563
- if (platform === "darwin") return join15(home, "Library", "Caches", "gitifact");
29564
- return join15(env.XDG_CACHE_HOME || join15(home, ".cache"), "gitifact");
29357
+ if (platform === "win32") return join14(env.LOCALAPPDATA || join14(home, "AppData", "Local"), "gitifact");
29358
+ if (platform === "darwin") return join14(home, "Library", "Caches", "gitifact");
29359
+ return join14(env.XDG_CACHE_HOME || join14(home, ".cache"), "gitifact");
29565
29360
  }
29566
- var cacheFile = (env) => join15(userCacheDirectory(env), "update.json");
29361
+ var cacheFile = (env) => join14(userCacheDirectory(env), "update.json");
29567
29362
  var isTime = (value) => typeof value === "string" && !Number.isNaN(Date.parse(value));
29568
29363
  async function readUpdateCache(env = process.env) {
29569
29364
  let value;
29570
29365
  try {
29571
- value = JSON.parse(await readFile13(cacheFile(env), "utf8"));
29366
+ value = JSON.parse(await readFile12(cacheFile(env), "utf8"));
29572
29367
  } catch {
29573
29368
  return {};
29574
29369
  }
@@ -29583,11 +29378,11 @@ async function readUpdateCache(env = process.env) {
29583
29378
  }
29584
29379
  async function changeUpdateCache(env, change2) {
29585
29380
  const directory = userCacheDirectory(env);
29586
- const temporary = join15(directory, "update-" + randomUUID5() + ".tmp");
29381
+ const temporary = join14(directory, "update-" + randomUUID5() + ".tmp");
29587
29382
  try {
29588
29383
  const next = change2(await readUpdateCache(env));
29589
- await mkdir5(directory, { recursive: true });
29590
- await writeFile3(temporary, JSON.stringify(next) + "\n", "utf8");
29384
+ await mkdir6(directory, { recursive: true });
29385
+ await writeFile4(temporary, JSON.stringify(next) + "\n", "utf8");
29591
29386
  await rename3(temporary, cacheFile(env));
29592
29387
  } catch {
29593
29388
  await unlink3(temporary).catch(() => {
@@ -29616,7 +29411,7 @@ async function runInit(options, version2) {
29616
29411
  await recordUpdateCheck(process.env, dto.update);
29617
29412
  const docs = dto.agentDocs.mode === "skip" ? t2("init.text.skipped") : dto.agentDocs.paths.join(", ") + (dto.agentDocs.mode === "remove" ? " (" + t2("init.text.blockRemoved") + ")" : dto.outcome === "planned" ? " (" + t2("init.text.blockPlanned") + ")" : " (" + t2("init.text.blockUpdated") + ")");
29618
29413
  const lineEndings = dto.lineEndings.created ? t2("init.text.lineEndings", { path: dto.lineEndings.path }) + "\n" : "";
29619
- const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npmGlobal }) + "\n" : "";
29414
+ const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npx }) + "\n" : "";
29620
29415
  process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
29621
29416
  ${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
29622
29417
  ${t2("init.text.agentDocs")}: ${docs}
@@ -29631,17 +29426,17 @@ ${lineEndings}${newer}` : JSON.stringify(dto) + "\n");
29631
29426
  }
29632
29427
 
29633
29428
  // src/commands/documents.ts
29634
- import { readFile as readFile14 } from "node:fs/promises";
29635
- import { join as join17 } from "node:path";
29429
+ import { readFile as readFile13 } from "node:fs/promises";
29430
+ import { join as join16 } from "node:path";
29636
29431
 
29637
29432
  // src/commands/project.ts
29638
- import { join as join16 } from "node:path";
29433
+ import { join as join15 } from "node:path";
29639
29434
  var operations = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
29640
29435
  async function openProject(cwd, options = {}) {
29641
29436
  const reader = storeReader(cwd);
29642
29437
  const location = await reader.location();
29643
29438
  if (options.writing) {
29644
- for (const marker2 of operations) if (await fileInfo(join16(location.gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
29439
+ for (const marker2 of operations) if (await fileInfo(join15(location.gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
29645
29440
  if (await reader.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
29646
29441
  }
29647
29442
  const raw = await readConfigFile(location.root);
@@ -29800,12 +29595,11 @@ var stateLabel = {
29800
29595
  };
29801
29596
  var stateMark = (state2) => state2 === "committed" ? "" : " (" + stateLabel[state2]() + ")";
29802
29597
  var line = (doc) => `${doc.id} ${doc.title}${draftMark(doc)} \u2014 ${doc.description}`;
29803
- var fileLine = (f) => f.title ? `${f.path} ${f.title} \u2014 ${f.description}` : f.path;
29804
29598
  async function working(project) {
29805
29599
  const { documents } = await project.cache.documents.list();
29806
29600
  return {
29807
29601
  byId: new Map(documents.map((d) => [d.id, d])),
29808
- read: (path3) => readFile14(join17(project.root, ...path3.split("/")), "utf8"),
29602
+ read: (path3) => readFile13(join16(project.root, ...path3.split("/")), "utf8"),
29809
29603
  referencing: async (id) => [...new Set((await project.cache.documents.referencing(id)).map((r) => r.from))]
29810
29604
  };
29811
29605
  }
@@ -29818,12 +29612,12 @@ async function committed(project, commit) {
29818
29612
  referencing: async (id) => documents.filter((d) => d.kind === "design" && (d.requirements.includes(id) || d.sources.some((s) => "id" in s && s.id === id))).map((d) => d.id)
29819
29613
  };
29820
29614
  }
29821
- async function showDocuments(project, ids, at, kinds, otherResource) {
29615
+ async function showDocuments(project, ids, at, kinds) {
29822
29616
  const view = at === void 0 ? await working(project) : await committed(project, at);
29823
29617
  const unknown2 = ids.filter((id) => !view.byId.has(id));
29824
29618
  if (unknown2.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: unknown2.join(", ") }));
29825
29619
  const other = ids.filter((id) => !kinds.includes(view.byId.get(id).kind));
29826
- if (other.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.otherResource", { ids: other.join(", "), command: otherResource }));
29620
+ if (other.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: other.join(", ") }));
29827
29621
  const shown = await Promise.all([...new Set(ids)].map(async (id) => {
29828
29622
  const doc = view.byId.get(id);
29829
29623
  const references = doc.kind === "design" ? [
@@ -29831,34 +29625,30 @@ async function showDocuments(project, ids, at, kinds, otherResource) {
29831
29625
  ...doc.sources.map((s) => "id" in s ? { id: s.id, title: view.byId.get(s.id)?.title ?? null } : { title: s.title, url: s.url })
29832
29626
  ] : [];
29833
29627
  const referencedBy = (await view.referencing(id)).map((from) => ({ id: from, title: view.byId.get(from)?.title ?? null }));
29834
- const files = doc.kind === "instruction" && at === void 0 ? { files: (await readInstructionFiles(project.root, doc.path)).files } : {};
29835
- return { id, kind: doc.kind, path: doc.path, title: doc.title, text: await view.read(doc.path), references, referencedBy, ...files };
29628
+ return { id, kind: doc.kind, path: doc.path, title: doc.title, text: await view.read(doc.path), references, referencedBy };
29836
29629
  }));
29837
29630
  const named = (r) => r.id ? r.id + " " + (r.title ?? "(" + t2("docs.missing") + ")") : r.title + " <" + r.url + ">";
29838
29631
  const blocks = shown.map((d) => [
29839
29632
  `== ${d.id} ${d.path}${at ? " @ " + at.slice(0, 7) : ""}`,
29840
29633
  d.text.replace(/\n$/, ""),
29841
29634
  ...section("-- " + t2("docs.references"), d.references.map(named)),
29842
- ...section("-- " + t2("docs.referencedBy"), d.referencedBy.map(named)),
29843
- ...section("-- " + t2("docs.files"), (d.files ?? []).map(fileLine))
29635
+ ...section("-- " + t2("docs.referencedBy"), d.referencedBy.map(named))
29844
29636
  ].join("\n"));
29845
29637
  return { json: { ...at ? { commit: at } : {}, documents: shown }, text: blocks.join("\n\n") + "\n" };
29846
29638
  }
29847
29639
  var pathExample = {
29848
29640
  feature: () => t2("docs.pathExample.feature"),
29849
29641
  requirement: () => t2("docs.pathExample.requirement"),
29850
- design: () => t2("docs.pathExample.design"),
29851
- instruction: () => t2("docs.pathExample.instruction")
29642
+ design: () => t2("docs.pathExample.design")
29852
29643
  };
29853
29644
  var skeleton = {
29854
29645
  feature: () => t2("docs.skeleton.feature"),
29855
29646
  requirement: () => t2("docs.skeleton.requirement"),
29856
- design: () => t2("docs.skeleton.design"),
29857
- instruction: () => t2("docs.skeleton.instruction")
29647
+ design: () => t2("docs.skeleton.design")
29858
29648
  };
29859
29649
  function newPath(kind2, path3) {
29860
- const target = kind2 === "feature" ? `${SPEC_ROOT}/${path3}/index.md` : kind2 === "instruction" ? `${INSTRUCTIONS_ROOT}/${path3}/${INSTRUCTION_FILE}` : `${SPEC_ROOT}/${path3.split("/")[0]}/${kind2 === "requirement" ? "requirements" : "design"}/${path3.split("/").slice(1).join("/")}.md`;
29861
- const shape = kind2 === "feature" || kind2 === "instruction" ? /^[^/]+$/ : /^[^/]+\/[^/]+$/;
29650
+ const target = kind2 === "feature" ? `${SPEC_ROOT}/${path3}/index.md` : `${SPEC_ROOT}/${path3.split("/")[0]}/${kind2 === "requirement" ? "requirements" : "design"}/${path3.split("/").slice(1).join("/")}.md`;
29651
+ const shape = kind2 === "feature" ? /^[^/]+$/ : /^[^/]+\/[^/]+$/;
29862
29652
  let where;
29863
29653
  try {
29864
29654
  where = shape.test(path3) ? classifyDocPath(target) : void 0;
@@ -29881,7 +29671,7 @@ async function createDocument(project, kind2, path3, options) {
29881
29671
  const taken = new Set(documents.map((d) => d.id));
29882
29672
  let id;
29883
29673
  do
29884
- id = generateId({ feature: "S", requirement: "R", design: "D", instruction: "I" }[kind2]);
29674
+ id = generateId({ feature: "S", requirement: "R", design: "D" }[kind2]);
29885
29675
  while (taken.has(id));
29886
29676
  const folder = target.slice(0, target.lastIndexOf("/") + 1);
29887
29677
  const siblings = documents.filter((d) => (d.kind === "requirement" || d.kind === "design") && d.path.startsWith(folder) && !d.path.slice(folder.length).includes("/"));
@@ -29889,7 +29679,7 @@ async function createDocument(project, kind2, path3, options) {
29889
29679
  const body2 = style2 === "usecase" ? t2("docs.skeleton.requirementUsecase") : skeleton[kind2]();
29890
29680
  const base = { id, path: target, title: options.title.trim(), description: options.description.trim(), body: body2, draft: true };
29891
29681
  const feature2 = target.split("/")[2];
29892
- const doc = kind2 === "feature" ? { kind: kind2, feature: feature2, ...base } : kind2 === "instruction" ? { kind: kind2, name: path3, ...base } : kind2 === "requirement" ? { kind: kind2, feature: feature2, order, ...styleKey, ...base } : { kind: kind2, feature: feature2, order, requirements: [], sources: [], ...base };
29682
+ const doc = kind2 === "feature" ? { kind: kind2, feature: feature2, ...base } : kind2 === "requirement" ? { kind: kind2, feature: feature2, order, ...styleKey, ...base } : { kind: kind2, feature: feature2, order, requirements: [], sources: [], ...base };
29893
29683
  const rendered = renderDocumentFile(doc);
29894
29684
  parseDocumentFile(target, rendered);
29895
29685
  try {
@@ -29912,11 +29702,11 @@ function parseLimit(value) {
29912
29702
  if (!/^[1-9]\d{0,5}$/.test(value)) throw new InvalidArgumentError(t2("list.limit"));
29913
29703
  return Number(value);
29914
29704
  }
29915
- var parseFields = (value) => value.split(",").map((f) => f.trim()).filter(Boolean);
29916
- function checkFields(fields2, columns3) {
29705
+ var parseFields = (value) => value.split(/[\s,]+/).filter(Boolean);
29706
+ function checkFields(fields2, columns2) {
29917
29707
  if (!fields2) return void 0;
29918
- const unknown2 = fields2.filter((f) => !columns3.includes(f));
29919
- if (unknown2.length || !fields2.length) throw new CommandError("INVALID_VALUE", t2("list.unknownFields", { fields: unknown2.join(", "), columns: columns3.join(", ") }));
29708
+ const unknown2 = fields2.filter((f) => !columns2.includes(f));
29709
+ if (unknown2.length || !fields2.length) throw new CommandError("INVALID_VALUE", t2("list.unknownFields", { fields: unknown2.join(", "), columns: columns2.join(", ") }));
29920
29710
  return [...new Set(fields2)];
29921
29711
  }
29922
29712
  function cell(value) {
@@ -30004,120 +29794,14 @@ var runSpecsList = (options) => runCommand("specs", options.format, async () =>
30004
29794
  var runSpecsShow = (ids, options) => runCommand("specs", options.format, async () => {
30005
29795
  const project = await openProject(process.cwd());
30006
29796
  const at = options.ref === void 0 ? void 0 : await project.reader.resolve(options.ref);
30007
- return showDocuments(project, ids, at, specKinds, "instructions show");
29797
+ return showDocuments(project, ids, at, at === void 0 ? specKinds : [...specKinds, "instruction"]);
30008
29798
  });
30009
29799
  var runSpecsNew = (kind2, path3, options) => runCommand("specs", options.format, async () => createDocument(await openProject(process.cwd()), kind2, path3, options));
30010
29800
 
30011
- // src/queries/instructions.ts
30012
- import { lstat as lstat12 } from "node:fs/promises";
30013
- import { join as join18 } from "node:path";
30014
- var AGENTS = "AGENTS.md";
30015
- async function listInstructions(source2, options, request) {
30016
- const listed3 = await source2.cache.documents.list();
30017
- const problems = listed3.problems;
30018
- const states = await documentStates(source2, listed3.documents);
30019
- const documents = states.documents;
30020
- const agents2 = { path: AGENTS, exists: !!(await lstat12(join18(source2.root, AGENTS)).catch(() => void 0))?.isFile() };
30021
- const historyNeeded = options.sort === "updated" || options.author !== void 0 || !!options.withUpdated;
30022
- const latest = /* @__PURE__ */ new Map();
30023
- const newest = /* @__PURE__ */ new Map();
30024
- let touched;
30025
- if (historyNeeded) {
30026
- const head = await source2.head();
30027
- const changes2 = head ? await source2.cache.history.changesOf(head) : [];
30028
- changes2.forEach((c, rank) => {
30029
- if (!latest.has(c.id)) {
30030
- latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
30031
- newest.set(c.id, rank);
30032
- }
30033
- });
30034
- if (options.author !== void 0) {
30035
- const by = byAuthor(options.author);
30036
- touched = new Set(changes2.filter(by).map((c) => c.id));
30037
- }
30038
- }
30039
- const matched = options.q !== void 0 ? await source2.cache.documents.matching(options.q) : void 0;
30040
- const instructions2 = documents.filter((d) => d.kind === "instruction").filter((d) => (!touched || touched.has(d.id)) && (!matched || matched.has(d.id))).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
30041
- const rows = await Promise.all(instructions2.map(async (d) => {
30042
- const state2 = states.state(d.id);
30043
- const { files, limited } = state2 === "deleted" ? { files: [], limited: false } : await readInstructionFiles(source2.root, d.path);
30044
- return {
30045
- id: d.id,
30046
- name: d.name,
30047
- path: d.path,
30048
- title: d.title,
30049
- description: d.description,
30050
- ...d.draft ? { draft: true } : {},
30051
- state: state2,
30052
- ...states.previousPath(d.id) ? { previousPath: states.previousPath(d.id) } : {},
30053
- files,
30054
- ...limited ? { filesLimited: true } : {},
30055
- ...historyNeeded ? { updated: latest.get(d.id) ?? null } : {},
30056
- ...matched ? { line: matched.get(d.id) } : {}
30057
- };
30058
- }));
30059
- if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
30060
- const page = pageOf(rows, (r) => r.id, request);
30061
- if (!page) return void 0;
30062
- return { agents: agents2, problems, page };
30063
- }
30064
-
30065
- // src/commands/instructions.ts
30066
- var instructionSorts = ["name", "updated"];
30067
- var columns2 = ["id", "name", "path", "title", "description", "draft", "state", "previousPath", "files", "updated", "line"];
30068
- var runInstructionsList = (options) => runCommand("instructions", options.format, async () => {
30069
- const fields2 = checkFields(options.fields, columns2);
30070
- const project = await openProject(process.cwd());
30071
- const listed3 = await listInstructions(project, { ...options, withUpdated: !!fields2?.includes("updated") }, options) ?? cursorGone(options);
30072
- const { agents: agents2, problems, page } = listed3;
30073
- const shown = page.rows;
30074
- const paging = { total: page.total, next: page.next, unit: "instruction" };
30075
- const unreadable2 = problems.filter((p) => p.path.startsWith(INSTRUCTIONS_ROOT + "/"));
30076
- if (fields2) {
30077
- const picked = selected(shown, fields2);
30078
- return {
30079
- json: { agents: agents2, instructions: picked.json, problems: unreadable2, page: paging },
30080
- text: selected(shown.map((r) => ({ ...r, files: r.files.map((f) => f.path) })), fields2).text + aside(options.format, pageLine(page))
30081
- };
30082
- }
30083
- const out = [agents2.exists ? t2("instructions.agents") : t2("instructions.noAgents")];
30084
- for (const r of shown) {
30085
- const when = r.updated ? ` \xB7 ${r.updated.date.slice(0, 10)} ${r.updated.author}` : "";
30086
- out.push(`${r.id} ${r.title}${draftMark(r)}${stateMark(r.state)} (${r.name}) \u2014 ${r.description}${when}`, ...r.line ? [" " + r.line] : []);
30087
- out.push(...r.files.map((f) => " " + fileLine(f)), ...r.filesLimited ? [" " + t2("instructions.filesLimited", { count: r.files.length })] : []);
30088
- }
30089
- if (!shown.length) out.push(options.q !== void 0 || options.author !== void 0 ? t2("docs.noMatch") : t2("instructions.empty"));
30090
- out.push(...pageLine(page));
30091
- if (unreadable2.length) out.push(t2("docs.unreadable", { count: unreadable2.length }));
30092
- return { json: { agents: agents2, instructions: shown, problems: unreadable2, page: paging }, text: text2(out) };
30093
- });
30094
- async function resolveInstructions(project, targets, at) {
30095
- const view = at === void 0 ? await working(project) : await committed(project, at);
30096
- const byName = new Map([...view.byId.values()].filter((d) => d.kind === "instruction").map((d) => [d.name, d.id]));
30097
- return targets.map((target) => byName.get(target) ?? target);
30098
- }
30099
- var runInstructionsShow = (targets, options) => runCommand("instructions", options.format, async () => {
30100
- const project = await openProject(process.cwd());
30101
- if (options.file !== void 0 && (targets.length !== 1 || options.ref !== void 0)) throw new CommandError("INVALID_VALUE", t2("instructions.fileWithOne"));
30102
- const at = options.ref === void 0 ? void 0 : await project.reader.resolve(options.ref);
30103
- const ids = await resolveInstructions(project, targets, at);
30104
- if (options.file === void 0) return showDocuments(project, ids, at, ["instruction"], "specs show");
30105
- const instruction2 = (await working(project)).byId.get(ids[0]);
30106
- if (instruction2?.kind !== "instruction") throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: targets[0] }));
30107
- const found = await readInstructionFile(project.root, instruction2.path, options.file);
30108
- if (!found) throw new CommandError("UNKNOWN_FILE", t2("instructions.unknownFile", { path: options.file, name: instruction2.name }));
30109
- const note = found.text !== null ? [] : [found.tooLarge ? t2("instructions.fileTooLarge", { size: found.size }) : t2("instructions.fileBinary", { size: found.size })];
30110
- return {
30111
- json: { id: instruction2.id, name: instruction2.name, file: { path: options.file, ...found } },
30112
- text: text2([`== ${instruction2.id} ${instruction2.name}/${options.file}`, ...found.text !== null ? [found.text.replace(/\n$/, "")] : note])
30113
- };
30114
- });
30115
- var runInstructionsNew = (name2, options) => runCommand("instructions", options.format, async () => createDocument(await openProject(process.cwd()), "instruction", name2, options));
30116
-
30117
29801
  // src/adapters/filesystem/document-warnings.ts
30118
- import { lstat as lstat13, readdir as readdir6 } from "node:fs/promises";
30119
- import { join as join19 } from "node:path";
30120
- var info2 = async (path3) => lstat13(path3).catch((e) => {
29802
+ import { lstat as lstat11, readdir as readdir5 } from "node:fs/promises";
29803
+ import { join as join17 } from "node:path";
29804
+ var info2 = async (path3) => lstat11(path3).catch((e) => {
30121
29805
  if (e.code === "ENOENT") return void 0;
30122
29806
  throw e;
30123
29807
  });
@@ -30125,20 +29809,20 @@ var posix = (path3) => path3.split("\\").join("/");
30125
29809
  async function listAssets(root) {
30126
29810
  const out = [];
30127
29811
  async function visit2(relative, depth) {
30128
- const stat3 = await info2(join19(root, relative));
29812
+ const stat3 = await info2(join17(root, relative));
30129
29813
  if (!stat3 || stat3.isSymbolicLink()) return;
30130
29814
  if (stat3.isDirectory()) {
30131
29815
  if (depth > 8 || out.length > 4e3) return;
30132
- for (const name2 of (await readdir6(join19(root, relative))).sort()) await visit2(relative + "/" + name2, depth + 1);
29816
+ for (const name2 of (await readdir5(join17(root, relative))).sort()) await visit2(relative + "/" + name2, depth + 1);
30133
29817
  } else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
30134
29818
  }
30135
29819
  await visit2(ASSETS_DIR, 0);
30136
29820
  return out;
30137
29821
  }
30138
29822
  async function readDocumentWarnings(root, documents) {
30139
- const outside = [...new Set(documentBodyLinks(documents).map((l) => l.target).filter((t3) => !t3.startsWith(".gitifact/") || t3.startsWith(INSTRUCTIONS_ROOT + "/")))];
29823
+ const outside = [...new Set(documentBodyLinks(documents).map((l) => l.target).filter((t3) => !t3.startsWith(".gitifact/")))];
30140
29824
  const existing = /* @__PURE__ */ new Set();
30141
- for (const target of outside) if ((await info2(join19(root, ...target.split("/"))))?.isFile()) existing.add(target);
29825
+ for (const target of outside) if ((await info2(join17(root, ...target.split("/"))))?.isFile()) existing.add(target);
30142
29826
  return documentWarnings(documents, await listAssets(root), existing);
30143
29827
  }
30144
29828
 
@@ -30162,31 +29846,31 @@ var runCheck = (options) => runCommand("check", options.format, async () => {
30162
29846
 
30163
29847
  // src/commands/changes.ts
30164
29848
  import { randomUUID as randomUUID6 } from "node:crypto";
30165
- import { mkdir as mkdir7, open as open5, readFile as readFile17, rename as rename4, rmdir, unlink as unlink5, writeFile as writeFile5 } from "node:fs/promises";
30166
- import { join as join22 } from "node:path";
29849
+ import { mkdir as mkdir8, open as open4, readFile as readFile16, rename as rename4, rmdir, unlink as unlink5, writeFile as writeFile6 } from "node:fs/promises";
29850
+ import { join as join20 } from "node:path";
30167
29851
 
30168
29852
  // src/adapters/filesystem/agent-inputs.ts
30169
29853
  import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
30170
- import { lstat as lstat14, mkdir as mkdir6, readdir as readdir7, realpath as realpath3, unlink as unlink4, writeFile as writeFile4 } from "node:fs/promises";
29854
+ import { lstat as lstat12, mkdir as mkdir7, readdir as readdir6, realpath as realpath3, unlink as unlink4, writeFile as writeFile5 } from "node:fs/promises";
30171
29855
  import { tmpdir } from "node:os";
30172
- import { basename, dirname as dirname2, join as join20 } from "node:path";
29856
+ import { basename, dirname as dirname2, join as join18 } from "node:path";
30173
29857
  var maxAge = 7 * 24 * 60 * 60 * 1e3;
30174
- var info3 = (path3) => lstat14(path3).catch((e) => {
29858
+ var info3 = (path3) => lstat12(path3).catch((e) => {
30175
29859
  if (e.code === "ENOENT") return void 0;
30176
29860
  throw e;
30177
29861
  });
30178
29862
  var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
30179
29863
  async function candidates(root, controls) {
30180
29864
  const key = createHash4("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
30181
- return { system: join20((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join20(root, ".gitifact", "tmp") };
29865
+ return { system: join18((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join18(root, ".gitifact", "tmp") };
30182
29866
  }
30183
29867
  async function writable(directory) {
30184
29868
  try {
30185
- await mkdir6(directory, { recursive: true });
29869
+ await mkdir7(directory, { recursive: true });
30186
29870
  const stat3 = await info3(directory);
30187
29871
  if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
30188
- const probe = join20(directory, ".probe-" + randomBytes2(8).toString("hex"));
30189
- await writeFile4(probe, "", { flag: "wx" });
29872
+ const probe = join18(directory, ".probe-" + randomBytes2(8).toString("hex"));
29873
+ await writeFile5(probe, "", { flag: "wx" });
30190
29874
  await unlink4(probe);
30191
29875
  return true;
30192
29876
  } catch {
@@ -30199,18 +29883,18 @@ async function prepareAgentInputs(root, controls = {}) {
30199
29883
  if (await writable(system)) directory = system;
30200
29884
  else if (await writable(project)) {
30201
29885
  directory = project;
30202
- await writeFile4(join20(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
29886
+ await writeFile5(join18(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
30203
29887
  if (e.code !== "EEXIST") throw e;
30204
29888
  });
30205
29889
  }
30206
29890
  if (!directory) return void 0;
30207
29891
  const now = (controls.now ?? Date.now)();
30208
- for (const name2 of await readdir7(directory).catch(() => [])) {
29892
+ for (const name2 of await readdir6(directory).catch(() => [])) {
30209
29893
  if (name2 === ".gitignore") continue;
30210
- const stat3 = await info3(join20(directory, name2)).catch(() => void 0);
30211
- if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink4(join20(directory, name2)).catch(() => void 0);
29894
+ const stat3 = await info3(join18(directory, name2)).catch(() => void 0);
29895
+ if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink4(join18(directory, name2)).catch(() => void 0);
30212
29896
  }
30213
- return { commit: join20(directory, "commit.json") };
29897
+ return { commit: join18(directory, "commit.json") };
30214
29898
  }
30215
29899
  async function discardAgentInput(root, file2, controls = {}) {
30216
29900
  try {
@@ -30232,17 +29916,17 @@ async function discardAgentInput(root, file2, controls = {}) {
30232
29916
 
30233
29917
  // src/commands/commit-files.ts
30234
29918
  import { createHash as createHash5 } from "node:crypto";
30235
- import { lstat as lstat15, readFile as readFile15 } from "node:fs/promises";
30236
- import { join as join21 } from "node:path";
29919
+ import { lstat as lstat13, readFile as readFile14 } from "node:fs/promises";
29920
+ import { join as join19 } from "node:path";
30237
29921
  var fail3 = (message) => {
30238
29922
  throw new CommandError("INVALID_COMMIT", message);
30239
29923
  };
30240
29924
  var hash2 = (value) => createHash5("sha256").update(value).digest("hex");
30241
- var optional2 = (path3) => readFile15(path3).catch((e) => {
29925
+ var optional2 = (path3) => readFile14(path3).catch((e) => {
30242
29926
  if (e.code === "ENOENT") return null;
30243
29927
  throw e;
30244
29928
  });
30245
- var info4 = (path3) => lstat15(path3).catch((e) => {
29929
+ var info4 = (path3) => lstat13(path3).catch((e) => {
30246
29930
  if (e.code === "ENOENT") return void 0;
30247
29931
  throw e;
30248
29932
  });
@@ -30254,7 +29938,7 @@ var record3 = (path3) => {
30254
29938
  return false;
30255
29939
  }
30256
29940
  };
30257
- var instructionFile2 = (path3) => {
29941
+ var instructionFile = (path3) => {
30258
29942
  try {
30259
29943
  return classifyDocPath(path3).type === "instruction-file";
30260
29944
  } catch {
@@ -30268,14 +29952,14 @@ async function fingerprint(root, path3) {
30268
29952
  validPath2(path3);
30269
29953
  const parts = path3.split("/");
30270
29954
  for (let i = 1; i <= parts.length; i++) {
30271
- const target = join21(root, ...parts.slice(0, i));
29955
+ const target = join19(root, ...parts.slice(0, i));
30272
29956
  const stat3 = await info4(target);
30273
29957
  if (!stat3) return null;
30274
29958
  if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail3(t2("commitFiles.linkOrDirectory"));
30275
- if (i < parts.length && await info4(join21(target, ".git"))) fail3(t2("commitFiles.nestedRepository"));
29959
+ if (i < parts.length && await info4(join19(target, ".git"))) fail3(t2("commitFiles.nestedRepository"));
30276
29960
  if (i === parts.length) {
30277
29961
  if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail3(t2("commitFiles.hardLinkOrSize"));
30278
- return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile15(target)]));
29962
+ return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile14(target)]));
30279
29963
  }
30280
29964
  }
30281
29965
  return null;
@@ -30302,16 +29986,16 @@ function policyPaths(files) {
30302
29986
  }
30303
29987
  return [...all].sort();
30304
29988
  }
30305
- var storeFiles = /* @__PURE__ */ new Set([".gitifact/config.json", LINE_ENDINGS_PATH]);
29989
+ var storeFiles = /* @__PURE__ */ new Set([".gitifact/config.json", LINE_ENDINGS_PATH, LAUNCHER_PATH]);
30306
29990
  function checkStoreSelection(selected2) {
30307
29991
  for (const p of selected2.filter((p2) => p2.startsWith(".gitifact/") && !record3(p2) && !storeFiles.has(p2))) {
30308
- if (!isAssetPath(p) && !instructionFile2(p)) fail3(t2("commitFiles.notRecord", { path: p }));
29992
+ if (!isAssetPath(p) && !instructionFile(p)) fail3(t2("commitFiles.notRecord", { path: p }));
30309
29993
  }
30310
29994
  }
30311
29995
 
30312
29996
  // src/commands/input.ts
30313
- import { readFile as readFile16, stat as stat2 } from "node:fs/promises";
30314
- var LIMIT2 = 1024 * 1024;
29997
+ import { readFile as readFile15, stat as stat2 } from "node:fs/promises";
29998
+ var LIMIT = 1024 * 1024;
30315
29999
  async function readJsonInput(file2, stdin = process.stdin) {
30316
30000
  const bytes = file2 === "-" ? await readStdin(stdin) : await readInputFile(file2);
30317
30001
  try {
@@ -30322,15 +30006,15 @@ async function readJsonInput(file2, stdin = process.stdin) {
30322
30006
  }
30323
30007
  async function readInputFile(path3) {
30324
30008
  const source2 = await stat2(path3).catch(() => void 0);
30325
- if (!source2?.isFile() || source2.size > LIMIT2) throw new CommandError("INVALID_INPUT", t2("input.file"));
30326
- return readFile16(path3);
30009
+ if (!source2?.isFile() || source2.size > LIMIT) throw new CommandError("INVALID_INPUT", t2("input.file"));
30010
+ return readFile15(path3);
30327
30011
  }
30328
30012
  async function readStdin(stream) {
30329
30013
  const chunks = [];
30330
30014
  let size = 0;
30331
30015
  for await (const chunk of stream) {
30332
30016
  size += chunk.length;
30333
- if (size > LIMIT2) throw new CommandError("INVALID_INPUT", t2("input.size"));
30017
+ if (size > LIMIT) throw new CommandError("INVALID_INPUT", t2("input.size"));
30334
30018
  chunks.push(chunk);
30335
30019
  }
30336
30020
  return Buffer.concat(chunks);
@@ -30340,8 +30024,8 @@ async function readStdin(stream) {
30340
30024
  var fields = ["paths", "message", "authorization", "migration"];
30341
30025
  var integration = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply"];
30342
30026
  var MIGRATION_TARGET = "0.8.0";
30343
- var PATH_LIMIT = 128;
30344
- var MIGRATION_PATH_LIMIT = 5e3;
30027
+ var PATH_LIMIT = 5e3;
30028
+ var STORE_PATH_LIMIT = 128;
30345
30029
  var same2 = (a, b) => JSON.stringify(a) === JSON.stringify(b);
30346
30030
  var problemLines2 = (problems) => problems.map((p) => " " + p.code + " " + p.message);
30347
30031
  var underDocuments = (path3) => path3.startsWith(SPEC_ROOT + "/") || path3.startsWith(WIKI_ROOT + "/") || path3 === HISTORY_PATH;
@@ -30399,12 +30083,14 @@ var runChangesCommit = (options, controls = {}) => runCommand("changes", options
30399
30083
  async function commitChanges(cwd, input2, dryRun) {
30400
30084
  const request = object3(input2);
30401
30085
  if (Object.keys(request).some((k) => !fields.includes(k))) fail3(t2("commit.unknownField"));
30402
- const selected2 = paths(request.paths, request.migration === true ? MIGRATION_PATH_LIMIT : PATH_LIMIT);
30086
+ const selected2 = paths(request.paths, PATH_LIMIT);
30403
30087
  const authorization = object3(request.authorization);
30088
+ const storePaths = selected2.filter((p) => p.startsWith(".gitifact/")).length;
30089
+ if (request.migration !== true && storePaths > STORE_PATH_LIMIT) fail3(t2("commit.tooManyStorePaths", { count: storePaths, limit: STORE_PATH_LIMIT }));
30404
30090
  if (Object.keys(authorization).sort().join(",") !== "basis,evidence" || !["user-request", "project-policy"].includes(String(authorization.basis))) fail3(t2("commit.authorizationRequired"));
30405
30091
  text3(authorization.evidence, 2e3);
30406
30092
  const message = text3(request.message, 4e3);
30407
- if (/^\s*Gitifact-/im.test(message)) fail3(t2("commit.trailerInMessage"));
30093
+ if (/^[ \t]*Gitifact-[A-Za-z0-9-]+[ \t]*:/im.test(message)) fail3(t2("commit.trailerInMessage"));
30408
30094
  if (request.migration !== void 0 && request.migration !== true) fail3(t2("commit.migrationFlag"));
30409
30095
  const project = await openProject(cwd, { writing: true });
30410
30096
  const { root, gitDir, indexPath, objectFormat } = project;
@@ -30430,7 +30116,7 @@ async function commitChanges(cwd, input2, dryRun) {
30430
30116
  ...stdin ? { input: stdin } : {}
30431
30117
  });
30432
30118
  const staged = async (index) => (await git(["diff", "--cached", "--ita-visible-in-index", "--name-only", "--no-renames", "-z"], index)).toString("utf8").split("\0").filter(Boolean).sort();
30433
- const busy = join22(gitDir, "gitifact-changes-commit.lock");
30119
+ const busy = join20(gitDir, "gitifact-changes-commit.lock");
30434
30120
  if (await info4(busy)) fail3(t2("commit.busy", { path: busy }));
30435
30121
  if ((await staged()).length) fail3(t2("commit.existingStaging"));
30436
30122
  const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
@@ -30470,7 +30156,7 @@ async function commitChanges(cwd, input2, dryRun) {
30470
30156
  if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
30471
30157
  return { outcome: "dry-run", committed: false, ...summary, commit: void 0 };
30472
30158
  }
30473
- const temporary = join22(gitDir, "gitifact-commit-index-" + randomUUID6());
30159
+ const temporary = join20(gitDir, "gitifact-commit-index-" + randomUUID6());
30474
30160
  let owned = false;
30475
30161
  let uncertain = false;
30476
30162
  let commitStarted = false;
@@ -30478,20 +30164,20 @@ async function commitChanges(cwd, input2, dryRun) {
30478
30164
  let committed2;
30479
30165
  try {
30480
30166
  try {
30481
- await mkdir7(busy);
30167
+ await mkdir8(busy);
30482
30168
  owned = true;
30483
30169
  } catch (e) {
30484
30170
  if (e.code === "EEXIST") fail3(t2("commit.busy", { path: busy }));
30485
30171
  throw e;
30486
30172
  }
30487
- const lock = indexLock = await open5(indexPath + ".lock", "wx", 384);
30173
+ const lock = indexLock = await open4(indexPath + ".lock", "wx", 384);
30488
30174
  const original = await optional2(indexPath);
30489
30175
  if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail3(t2("commit.indexChanged"));
30490
30176
  const before = await hashes([...started.keys()]);
30491
30177
  for (const [path3, value] of started) if (before.get(path3) !== value) fail3(t2("commit.fileChangedRunning", { path: path3 }));
30492
- await writeFile5(join22(busy, "recovery.json"), JSON.stringify({ before: base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected2, message }));
30178
+ await writeFile6(join20(busy, "recovery.json"), JSON.stringify({ before: base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected2, message }));
30493
30179
  const locked = await hashes([.../* @__PURE__ */ new Set([...selected2, ...contextPaths])]);
30494
- if (original) await writeFile5(temporary, original, { flag: "wx" });
30180
+ if (original) await writeFile6(temporary, original, { flag: "wx" });
30495
30181
  else await git(["read-tree", "--empty"], temporary);
30496
30182
  await git(["add", "--pathspec-from-file=-", "--pathspec-file-nul"], temporary, Buffer.from(stageable.join("\0")));
30497
30183
  const actual = await staged(temporary);
@@ -30506,14 +30192,14 @@ async function commitChanges(cwd, input2, dryRun) {
30506
30192
  const size = Number(header[2]);
30507
30193
  if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail3(t2("commit.stagedUnreadable", { path: path3 }));
30508
30194
  const blob = output2.subarray(end + 1, end + 1 + size);
30509
- const file2 = await readFile17(join22(root, path3));
30195
+ const file2 = await readFile16(join20(root, path3));
30510
30196
  if (!blob.equals(file2) && !blob.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail3(t2("commit.filterChanged"));
30511
30197
  offset = end + size + 2;
30512
30198
  }
30513
30199
  }
30514
30200
  const again = await hashes([...locked.keys()]);
30515
30201
  for (const [path3, value] of locked) if (again.get(path3) !== value) fail3(t2("commit.fileChangedPreparing", { path: path3 }));
30516
- for (const name2 of integration) if (await info4(join22(gitDir, name2))) fail3(t2("commit.integrationStarted"));
30202
+ for (const name2 of integration) if (await info4(join20(gitDir, name2))) fail3(t2("commit.integrationStarted"));
30517
30203
  if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
30518
30204
  const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
30519
30205
  commitStarted = true;
@@ -30528,7 +30214,7 @@ async function commitChanges(cwd, input2, dryRun) {
30528
30214
  if (headers[0] !== "tree " + tree || after.branch !== base.branch || !same2(parents, base.head ? [base.head] : []) || trailers.some((line2) => !body2.split(/\r?\n/).includes(line2))) fail3(t2("commit.resultAltered"));
30529
30215
  if (hash2(await optional2(indexPath) ?? Buffer.alloc(0)) !== indexHash) fail3(t2("commit.indexChangedAfter"));
30530
30216
  if ((await git(["write-tree"], temporary)).toString("utf8").trim() !== tree) fail3(t2("commit.hookChangedStaging"));
30531
- await lock.writeFile(await readFile17(temporary));
30217
+ await lock.writeFile(await readFile16(temporary));
30532
30218
  await lock.sync();
30533
30219
  await lock.close();
30534
30220
  await rename4(indexPath + ".lock", indexPath);
@@ -30553,7 +30239,7 @@ async function commitChanges(cwd, input2, dryRun) {
30553
30239
  await unlink5(temporary).catch(() => {
30554
30240
  });
30555
30241
  if (owned) {
30556
- await unlink5(join22(busy, "recovery.json")).catch(() => {
30242
+ await unlink5(join20(busy, "recovery.json")).catch(() => {
30557
30243
  });
30558
30244
  await rmdir(busy);
30559
30245
  }
@@ -30563,12 +30249,14 @@ async function commitChanges(cwd, input2, dryRun) {
30563
30249
  }
30564
30250
 
30565
30251
  // src/commands/records.ts
30566
- import { readFile as readFile18 } from "node:fs/promises";
30567
- import { join as join23 } from "node:path";
30252
+ import { readFile as readFile17 } from "node:fs/promises";
30253
+ import { join as join21 } from "node:path";
30568
30254
  var runRecordsNew = (options) => runCommand("records", options.format, async () => {
30569
30255
  if (/[\r\n]/.test(options.title)) throw new CommandError("INVALID_VALUE", t2("docs.singleLine", { field: "title" }));
30570
- const docs = [...new Set(options.docs.flatMap((d) => d.split(",")).map((d) => d.trim()).filter(Boolean))];
30571
- if (!docs.length || docs.some((d) => !docIdPattern.test(d))) throw new CommandError("INVALID_VALUE", t2("records.invalidDocs"));
30256
+ const docs = [...new Set(options.docs.flatMap((d) => d.split(/[\s,]+/)).filter(Boolean))];
30257
+ if (!docs.length) throw new CommandError("INVALID_VALUE", t2("records.invalidDocs"));
30258
+ const invalid2 = docs.filter((d) => !docIdPattern.test(d));
30259
+ if (invalid2.length) throw new CommandError("INVALID_VALUE", t2("records.notDocIds", { ids: invalid2.join(", ") }));
30572
30260
  const project = await openProject(process.cwd());
30573
30261
  let id;
30574
30262
  do
@@ -30600,7 +30288,7 @@ var runRecordsShow = (ids, options) => runCommand("records", options.format, asy
30600
30288
  const shown = await Promise.all([...new Set(ids)].map(async (id) => {
30601
30289
  const path3 = await findRecord(project.root, id);
30602
30290
  if (!path3) return { id, missing: true };
30603
- const source2 = await readFile18(join23(project.root, ...path3.split("/")), "utf8");
30291
+ const source2 = await readFile17(join21(project.root, ...path3.split("/")), "utf8");
30604
30292
  const log = project.reader.decode(await project.reader.run(["log", "-1", "--diff-filter=A", "--format=%H%x00%aN%x00%aI", "--", path3])).trim();
30605
30293
  const [commit, author, date5] = log.split("\0");
30606
30294
  return { id, path: path3, text: source2, commit: commit ? { commit, author, date: date5 } : null };
@@ -30764,11 +30452,11 @@ function newIssueUrl(title, body2, marker2) {
30764
30452
  }
30765
30453
 
30766
30454
  // src/shared/project-config.ts
30767
- import { readFile as readFile19 } from "node:fs/promises";
30768
- import { dirname as dirname3, join as join24 } from "node:path";
30455
+ import { readFile as readFile18 } from "node:fs/promises";
30456
+ import { dirname as dirname3, join as join22 } from "node:path";
30769
30457
  async function findProjectConfig(from) {
30770
30458
  for (let folder = from; ; folder = dirname3(folder)) {
30771
- const text4 = await readFile19(join24(folder, ".gitifact", "config.json"), "utf8").catch(() => void 0);
30459
+ const text4 = await readFile18(join22(folder, ".gitifact", "config.json"), "utf8").catch(() => void 0);
30772
30460
  if (text4 !== void 0) return { root: folder, text: text4 };
30773
30461
  if (dirname3(folder) === folder) return void 0;
30774
30462
  }
@@ -30830,8 +30518,8 @@ var runFeedback = (options, version2, run) => runCommand("feedback", options.for
30830
30518
  });
30831
30519
 
30832
30520
  // src/commands/update.ts
30833
- import { readFile as readFile20 } from "node:fs/promises";
30834
- import { join as join25 } from "node:path";
30521
+ import { readFile as readFile19 } from "node:fs/promises";
30522
+ import { join as join23 } from "node:path";
30835
30523
  import { isDeepStrictEqual } from "node:util";
30836
30524
 
30837
30525
  // src/adapters/git/agent-docs-commit.ts
@@ -30855,6 +30543,10 @@ function agentDocsGit(root, inherited = process.env) {
30855
30543
  async commitOnly(paths2, message) {
30856
30544
  await git(["commit", "--only", "-m", message, "--", ...paths2]);
30857
30545
  },
30546
+ // A file Git does not know yet (the launcher on its first update) is marked so `--only` can take it.
30547
+ async intendToAdd(paths2) {
30548
+ await git(["add", "--intent-to-add", "--", ...paths2]);
30549
+ },
30858
30550
  async changedPaths(commit) {
30859
30551
  return (await git(["diff-tree", "--no-commit-id", "--name-only", "--no-renames", "-r", "-z", "--root", commit])).toString("utf8").split("\0").filter(Boolean).sort();
30860
30552
  },
@@ -30868,7 +30560,7 @@ function agentDocsGit(root, inherited = process.env) {
30868
30560
  var CONFIG_PATH = ".gitifact/config.json";
30869
30561
  var updateCommitMessage = (version2) => "chore(gitifact): update project to gitifact v" + version2;
30870
30562
  var commitState = (state2, extra = {}) => ({ state: state2, commit: null, paths: [], message: null, reason: null, detail: null, ...extra });
30871
- var notInitialized = { docs: { state: "not-initialized", paths: [], missing: [] }, project: { state: "not-initialized", cli: null } };
30563
+ var notInitialized = { docs: { state: "not-initialized", paths: [] }, project: { state: "not-initialized", cli: null } };
30872
30564
  async function refreshProject(cwd, version2, env, controls) {
30873
30565
  const repo = initRepository(cwd, env);
30874
30566
  let first;
@@ -30884,13 +30576,12 @@ async function refreshProject(cwd, version2, env, controls) {
30884
30576
  const root = first.state.repository.rootPath;
30885
30577
  const parsed2 = needsMigration(config2) ? void 0 : parseManagedConfig(config2);
30886
30578
  const plan = await planAgentDocs(root, { version: version2, onlyExisting: true, language: parsed2?.language, ...controls });
30887
- const { missing: missing2 } = plan;
30888
- if (parsed2?.cli && isNewerRelease(parsed2.cli, version2)) return { docs: { state: "kept", paths: plan.paths, missing: missing2, root }, project: { state: "current", cli: parsed2.cli } };
30579
+ if (parsed2?.cli && isNewerRelease(parsed2.cli, version2)) return { docs: { state: "kept", paths: plan.paths, root }, project: { state: "current", cli: parsed2.cli } };
30889
30580
  let current2 = config2;
30890
30581
  const unchanged = async () => {
30891
30582
  if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== current2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
30892
30583
  };
30893
- const docs = plan.paths.length === 0 ? { state: "no-block", paths: [], missing: missing2, root } : plan.writes.length === 0 ? { state: "current", paths: plan.paths, missing: missing2, root, candidates: plan.paths } : { state: "refreshed", paths: await applyAgentDocs(root, plan, unchanged), missing: missing2, root, candidates: plan.paths };
30584
+ const docs = plan.paths.length === 0 ? { state: "no-block", paths: [], root } : plan.writes.length === 0 ? { state: "current", paths: plan.paths, root, candidates: plan.paths } : { state: "refreshed", paths: await applyAgentDocs(root, plan, unchanged), root, candidates: plan.paths };
30894
30585
  if (!parsed2) return { docs, project: { state: "current", cli: null } };
30895
30586
  const settled2 = settleConfig(parsed2, version2, plan.language);
30896
30587
  if (settled2.changed) {
@@ -30898,6 +30589,7 @@ async function refreshProject(cwd, version2, env, controls) {
30898
30589
  await publishConfig(root, next, unchanged, void 0, true);
30899
30590
  current2 = next;
30900
30591
  }
30592
+ await refreshLauncher(root);
30901
30593
  return { docs: { ...docs, root }, project: { state: settled2.changed ? "written" : "current", cli: settled2.config.cli ?? null } };
30902
30594
  }
30903
30595
  function outsideBlock(text4) {
@@ -30932,7 +30624,7 @@ async function commitUpdate(docs, version2, env) {
30932
30624
  if (before === null || before !== outsideBlock(working2)) return commitState("skipped", { paths: [path3], reason: "other-changes" });
30933
30625
  changed.push(path3);
30934
30626
  }
30935
- const workingConfig = await readFile20(join25(docs.root, CONFIG_PATH), "utf8").catch(() => null);
30627
+ const workingConfig = await readFile19(join23(docs.root, CONFIG_PATH), "utf8").catch(() => null);
30936
30628
  const committedConfig = head ? await git.committed(head, CONFIG_PATH) : null;
30937
30629
  if (workingConfig !== null && committedConfig?.replace(/\r\n/g, "\n") !== workingConfig.replace(/\r\n/g, "\n")) {
30938
30630
  if (committedConfig === null) return commitState("skipped", { paths: [CONFIG_PATH], reason: "untracked" });
@@ -30940,10 +30632,14 @@ async function commitUpdate(docs, version2, env) {
30940
30632
  if (before === null || !isDeepStrictEqual(before, withoutSettings(workingConfig))) return commitState("skipped", { paths: [CONFIG_PATH], reason: "other-changes" });
30941
30633
  changed.push(CONFIG_PATH);
30942
30634
  }
30635
+ const workingLauncher = await readFile19(join23(docs.root, ...LAUNCHER_PATH.split("/")), "utf8").catch(() => null);
30636
+ const committedLauncher = head ? await git.committed(head, LAUNCHER_PATH) : null;
30637
+ if (workingLauncher !== null && committedLauncher?.replace(/\r\n/g, "\n") !== workingLauncher.replace(/\r\n/g, "\n")) changed.push(LAUNCHER_PATH);
30943
30638
  if (changed.length === 0) return commitState("nothing");
30944
30639
  const message = updateCommitMessage(version2);
30945
30640
  let failure2;
30946
30641
  try {
30642
+ if (workingLauncher !== null && committedLauncher === null) await git.intendToAdd([LAUNCHER_PATH]);
30947
30643
  await git.commitOnly(changed, message);
30948
30644
  } catch (error62) {
30949
30645
  failure2 = error62;
@@ -30970,7 +30666,8 @@ async function updateCommand(cwd, version2, env = process.env, controls = {}) {
30970
30666
  cliVersion: version2,
30971
30667
  update,
30972
30668
  install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
30973
- agentDocs: { state: docs.state, paths: docs.paths, missing: docs.missing },
30669
+ // missing stays in the v6 result but is always empty since 0.9.0: init no longer adds a CLAUDE.md.
30670
+ agentDocs: { state: docs.state, paths: docs.paths, missing: [] },
30974
30671
  project,
30975
30672
  commit,
30976
30673
  migrationRequired: config2 !== void 0 && needsMigration(config2)
@@ -30986,11 +30683,10 @@ async function runUpdate(options, version2) {
30986
30683
  }
30987
30684
  const lines2 = [t2("update.text.current", { version: dto.cliVersion })];
30988
30685
  lines2.push(dto.update.status === "available" ? t2("update.text.available", { version: dto.update.latestVersion }) : dto.update.status === "up-to-date" ? t2("update.text.upToDate") : dto.update.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable"));
30989
- if (dto.install) lines2.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
30686
+ if (dto.install) lines2.push(t2("update.text.install", { command: npxUpdate(dto.update.latestVersion) }), t2("update.text.afterInstall"));
30990
30687
  lines2.push(dto.agentDocs.state === "refreshed" ? t2("update.text.blockRefreshed", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "current" ? t2("update.text.blockCurrent", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "kept" ? t2("update.text.blockKept", { paths: dto.agentDocs.paths.join(", ") || "-" }) : dto.agentDocs.state === "no-block" ? t2("update.text.noBlock") : t2("update.text.notInitialized"));
30991
- if (dto.agentDocs.missing.length) lines2.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
30992
30688
  if (dto.project.state === "written") lines2.push(t2("update.text.projectWritten", { path: CONFIG_PATH, version: dto.project.cli ?? dto.cliVersion }));
30993
- if (dto.project.cli && isNewerRelease(dto.project.cli, dto.cliVersion)) lines2.push(t2("update.text.projectAhead", { project: dto.project.cli, version: dto.cliVersion, command: npmGlobalInstall(dto.project.cli) }));
30689
+ if (dto.project.cli && isNewerRelease(dto.project.cli, dto.cliVersion)) lines2.push(t2("update.text.projectAhead", { project: dto.project.cli, version: dto.cliVersion }));
30994
30690
  if (dto.migrationRequired) lines2.push(t2("update.text.migrationRequired", { version: dto.cliVersion }));
30995
30691
  const { commit } = dto;
30996
30692
  if (commit.state === "committed") lines2.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
@@ -31033,7 +30729,7 @@ async function runUpdateCheck(format2, version2) {
31033
30729
  t2("update.text.current", { version: dto.cliVersion }),
31034
30730
  state2.status === "available" ? t2("update.text.available", { version: state2.latestVersion }) : state2.status === "up-to-date" ? t2("update.text.upToDate") : state2.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable")
31035
30731
  ];
31036
- if (state2.status === "available") lines2.push(t2("update.text.install", { command: npmGlobalInstall(state2.latestVersion) }), t2("update.text.afterInstall"));
30732
+ if (state2.status === "available") lines2.push(t2("update.text.install", { command: npxUpdate(state2.latestVersion) }), t2("update.text.afterInstall"));
31037
30733
  process.stdout.write(lines2.join("\n") + "\n");
31038
30734
  }
31039
30735
 
@@ -31054,8 +30750,8 @@ function chooseNotice(running2, project, cache, now, scope = { release: true, pr
31054
30750
  return null;
31055
30751
  }
31056
30752
  function noticeText(notice, running2) {
31057
- if (notice.kind === "project-newer") return t2("notice.projectNewer", { project: notice.project, version: running2, command: npmGlobalInstall(notice.project) });
31058
- if (notice.kind === "release") return t2("notice.release", { latest: notice.latest, version: running2, command: npmGlobalInstall(notice.latest) });
30753
+ if (notice.kind === "project-newer") return t2("notice.projectNewer", { project: notice.project, version: running2 });
30754
+ if (notice.kind === "release") return t2("notice.release", { latest: notice.latest, version: running2, command: npxUpdate(notice.latest) });
31059
30755
  return t2("notice.projectOlder", { project: notice.project, version: running2 });
31060
30756
  }
31061
30757
  var needsRefresh = (cache, now) => !(cache.checkedAt && now - Date.parse(cache.checkedAt) < REFRESH_AFTER_MS) && !(cache.checkingSince && now - Date.parse(cache.checkingSince) < CHECKING_WINDOW_MS);
@@ -31109,17 +30805,17 @@ for (let i = 0; i < languageArgs.length; i++) {
31109
30805
  if (value === "ko" || value === "en") selectedLanguage = value;
31110
30806
  }
31111
30807
  configureLanguage(selectedLanguage ?? environmentLanguage(), selectedLanguage);
31112
- var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.6").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
30808
+ var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.9.1").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
31113
30809
  program2.hook("preAction", async (_program, action) => {
31114
30810
  let top = action;
31115
30811
  while (top.parent && top.parent !== program2) top = top.parent;
31116
- if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.8.6", top.name());
30812
+ if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.9.1", top.name());
31117
30813
  });
31118
- program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.8.6"));
30814
+ program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.9.1"));
31119
30815
  var format = () => new Option("--format <format>", t2("help.format")).choices(formats).default("text");
31120
- program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).action((options) => runBrowser(options, "0.8.6"));
31121
- program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.8.6"));
31122
- program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).addOption(new Option("--check", t2("help.updateCheck")).conflicts("commit")).addOption(new Option("--later", t2("help.updateLater")).conflicts(["check", "commit"])).action((options) => options.later ? runUpdateLater(options.format, "0.8.6") : options.check ? runUpdateCheck(options.format, "0.8.6") : runUpdate(options, "0.8.6"));
30816
+ program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).action((options) => runBrowser(options, "0.9.1"));
30817
+ program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.9.1"));
30818
+ program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).addOption(new Option("--check", t2("help.updateCheck")).conflicts("commit")).addOption(new Option("--later", t2("help.updateLater")).conflicts(["check", "commit"])).action((options) => options.later ? runUpdateLater(options.format, "0.9.1") : options.check ? runUpdateCheck(options.format, "0.9.1") : runUpdate(options, "0.9.1"));
31123
30819
  var listed2 = (command, sorts) => {
31124
30820
  command.allowExcessArguments(false).option("--q <words>", t2("help.listQ")).option("--author <name>", t2("help.listAuthor"));
31125
30821
  if (sorts) command.addOption(new Option("--sort <key>", t2("help.listSort")).choices([...sorts]).default(sorts[0]));
@@ -31129,10 +30825,6 @@ var specs = program2.command("specs").description(t2("help.specs"));
31129
30825
  listed2(specs.command("list").description(t2("help.specsList")).addOption(new Option("--type <kind>", t2("help.specsListType")).choices([...specKinds])).option("--feature <folder>", t2("help.specsListFeature")).option("--without-design", t2("help.specsListWithoutDesign")).option("--uncovered", t2("help.specsListUncovered")).option("--draft", t2("help.specsListDraft")).option("--changed-since <date|commit>", t2("help.specsListChangedSince")), specSorts).action((o) => runSpecsList(o));
31130
30826
  specs.command("show").description(t2("help.specsShow")).argument("<ids...>", t2("help.specsShowIds")).option("--ref <commit>", t2("help.showRef")).addOption(format()).action((ids, o) => runSpecsShow(ids, o));
31131
30827
  specs.command("new").description(t2("help.specsNew")).allowExcessArguments(false).addArgument(new Argument("<kind>", t2("help.specsNewKind")).choices(specKinds)).argument("<path>", t2("help.specsNewPath")).requiredOption("--title <title>", t2("help.newTitle")).requiredOption("--description <text>", t2("help.newDescription")).addOption(new Option("--style <style>", t2("help.specsNewStyle")).choices(specStyles)).addOption(format()).action((kind2, path3, o) => runSpecsNew(kind2, path3, o));
31132
- var instructions = program2.command("instructions").description(t2("help.instructions"));
31133
- listed2(instructions.command("list").description(t2("help.instructionsList")), instructionSorts).action((o) => runInstructionsList(o));
31134
- instructions.command("show").description(t2("help.instructionsShow")).argument("<targets...>", t2("help.instructionsShowTargets")).option("--ref <commit>", t2("help.showRef")).option("--file <path>", t2("help.instructionsShowFile")).addOption(format()).action((targets, o) => runInstructionsShow(targets, o));
31135
- instructions.command("new").description(t2("help.instructionsNew")).allowExcessArguments(false).argument("<name>", t2("help.instructionsNewName")).requiredOption("--title <title>", t2("help.newTitle")).requiredOption("--description <text>", t2("help.newDescription")).addOption(format()).action((name2, o) => runInstructionsNew(name2, o));
31136
30828
  var records = program2.command("records").description(t2("help.records"));
31137
30829
  listed2(records.command("list").description(t2("help.recordsList")).option("--doc <id>", t2("help.recordsListDoc")).option("--since <date|commit>", t2("help.recordsListSince"))).action((o) => runRecordsList(o));
31138
30830
  records.command("show").description(t2("help.recordsShow")).argument("<ids...>", t2("help.recordsShowId")).addOption(format()).action((ids, o) => runRecordsShow(ids, o));
@@ -31141,7 +30833,7 @@ program2.command("check").description(t2("help.check")).allowExcessArguments(fal
31141
30833
  var changes = program2.command("changes").description(t2("help.changes"));
31142
30834
  changes.command("list").description(t2("help.changesList")).allowExcessArguments(false).addOption(format()).action((o) => runChangesList(o));
31143
30835
  changes.command("commit").description(t2("help.changesCommit")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.changesCommitFile")).option("--dry-run", t2("help.changesCommitDryRun")).addOption(format()).action((o) => runChangesCommit(o));
31144
- program2.command("feedback").description(t2("help.feedback")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.feedbackFile")).option("--dry-run", t2("help.feedbackDryRun")).addOption(format()).action((o) => runFeedback(o, "0.8.6"));
30836
+ program2.command("feedback").description(t2("help.feedback")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.feedbackFile")).option("--dry-run", t2("help.feedbackDryRun")).addOption(format()).action((o) => runFeedback(o, "0.9.1"));
31145
30837
  var guide = program2.command("guide").description(t2("help.guide"));
31146
30838
  guide.command("list").description(t2("help.guideList")).allowExcessArguments(false).addOption(format()).action((o) => runGuideList(o));
31147
30839
  guide.command("show").description(t2("help.guideShow")).argument("<topic>", t2("help.guideTopic")).allowExcessArguments(false).addOption(format()).action((topic, o) => runGuideShow(topic, o));