gitifact 0.8.6 → 0.9.0

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 (81) 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-C3KamVtV.js +3 -0
  9. package/dist/browser/assets/activity-timeline-BdRnSGAp.js +2 -0
  10. package/dist/browser/assets/changelog-C8_hi0cv.js +1 -0
  11. package/dist/browser/assets/commit-BWyvKMHI.js +4 -0
  12. package/dist/browser/assets/contributors-Bk_xjPNJ.js +1 -0
  13. package/dist/browser/assets/{contributors._email-CEcZZfAR.js → contributors._email-C8GGV2IN.js} +1 -1
  14. package/dist/browser/assets/{contributors.index-DNMBFxLb.js → contributors.index-BKMSdIFJ.js} +1 -1
  15. package/dist/browser/assets/dashboard.index-COyVOMnD.js +1 -0
  16. package/dist/browser/assets/{document-odLLdugz.js → document-BbL374wf.js} +4 -4
  17. package/dist/browser/assets/{document-Cmx0e6kz.css → document-DXzNxu17.css} +1 -1
  18. package/dist/browser/assets/document-Dtyl4_It.js +2 -0
  19. package/dist/browser/assets/features-B1FsIXwk.js +2 -0
  20. package/dist/browser/assets/{features._featureId-Cmg8-2ku.js → features._featureId-DT63dqjw.js} +1 -1
  21. package/dist/browser/assets/{features.index-CBz7BmRW.js → features.index-BGBefKJN.js} +1 -1
  22. package/dist/browser/assets/getting-started-CCqN7NFk.js +1 -0
  23. package/dist/browser/assets/git-CzKF2N75.js +1 -0
  24. package/dist/browser/assets/index-DsduXf6u.js +49 -0
  25. package/dist/browser/assets/{load-more-BVsXjzHC.js → load-more-COb87OhG.js} +1 -1
  26. package/dist/browser/assets/page-header-Cs2fqdqC.js +246 -0
  27. package/dist/browser/assets/{records._recordId-DF-NkdBL.js → records._recordId-DP2stdvW.js} +1 -1
  28. package/dist/browser/assets/{records.commits._commit-BBf_rxHJ.js → records.commits._commit-DQlC8mSd.js} +1 -1
  29. package/dist/browser/assets/{records.docs._docId-Diev5opj.js → records.docs._docId-C7snDAo3.js} +1 -1
  30. package/dist/browser/assets/{records.index-DPWv9blV.js → records.index-DRPp4IAb.js} +1 -1
  31. package/dist/browser/assets/{records.working-D5xv-dTD.js → records.working-Qv9R2KgB.js} +1 -1
  32. package/dist/browser/assets/{settings-N1hE7l-i.js → settings-DDEler3s.js} +1 -1
  33. package/dist/browser/assets/useInfiniteQuery-IoAmrFi4.js +1 -0
  34. package/dist/browser/assets/{useKeyboardHint-CGJ2GVT3.js → useKeyboardHint-Wwnf-tpi.js} +1 -1
  35. package/dist/browser/index.html +18 -24
  36. package/dist/i18n/en/block.md +7 -7
  37. package/dist/i18n/en/changelog.md +14 -0
  38. package/dist/i18n/en/docs/commit.md +1 -1
  39. package/dist/i18n/en/docs/design.md +3 -3
  40. package/dist/i18n/en/docs/migrate.md +31 -34
  41. package/dist/i18n/en/docs/records.md +2 -2
  42. package/dist/i18n/en/docs/spec.md +1 -1
  43. package/dist/i18n/en/docs/workflow.md +12 -12
  44. package/dist/i18n/en/docs/writing.md +4 -4
  45. package/dist/i18n/ko/block.md +7 -7
  46. package/dist/i18n/ko/changelog.md +14 -0
  47. package/dist/i18n/ko/docs/commit.md +1 -1
  48. package/dist/i18n/ko/docs/design.md +3 -3
  49. package/dist/i18n/ko/docs/migrate.md +30 -33
  50. package/dist/i18n/ko/docs/records.md +2 -2
  51. package/dist/i18n/ko/docs/spec.md +1 -1
  52. package/dist/i18n/ko/docs/workflow.md +12 -12
  53. package/dist/i18n/ko/docs/writing.md +4 -4
  54. package/dist/launcher/cli.mjs +77 -0
  55. package/dist/main.js +274 -586
  56. package/package.json +3 -3
  57. package/dist/browser/assets/MetadataListItem-BEwPg27H.js +0 -1
  58. package/dist/browser/assets/about-C3ewDfU2.js +0 -3
  59. package/dist/browser/assets/activity-timeline-B3s0viEt.js +0 -2
  60. package/dist/browser/assets/changelog-DkRd6I8J.js +0 -1
  61. package/dist/browser/assets/commit-BHwKZ7hK.js +0 -4
  62. package/dist/browser/assets/contributor-2mkEa2Wo.css +0 -1
  63. package/dist/browser/assets/contributor-CqqvzxLh.js +0 -1
  64. package/dist/browser/assets/contributors-CuMPZx6_.js +0 -1
  65. package/dist/browser/assets/dashboard.index-nV7KwM5H.js +0 -1
  66. package/dist/browser/assets/document-BnxE7Txb.js +0 -2
  67. package/dist/browser/assets/features-C0YzZiS-.js +0 -2
  68. package/dist/browser/assets/getting-started-CaOhGSBe.js +0 -1
  69. package/dist/browser/assets/git-DwAnKiiN.js +0 -1
  70. package/dist/browser/assets/index-BljzO9go.js +0 -49
  71. package/dist/browser/assets/instructions-2OUMP-vh.css +0 -1
  72. package/dist/browser/assets/instructions-DWS_nwe0.js +0 -1
  73. package/dist/browser/assets/instructions._instructionId-l7aWWvfx.js +0 -1
  74. package/dist/browser/assets/instructions.agents-t7hMe4Pv.js +0 -1
  75. package/dist/browser/assets/instructions.index-CHVCQBSH.js +0 -1
  76. package/dist/browser/assets/lazyRouteComponent-BZI8yQj_.js +0 -1
  77. package/dist/browser/assets/page-header-CRcXie4p.js +0 -248
  78. package/dist/browser/assets/project-instructions-DsWrw-nY.jpg +0 -0
  79. package/dist/browser/assets/useInfiniteQuery-CrEm5UW2.js +0 -1
  80. package/dist/i18n/en/docs/instructions.md +0 -94
  81. 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})",
@@ -25935,7 +25871,8 @@ var messages_default3 = {
25935
25871
  "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
25872
  "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
25873
  "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.",
25874
+ "records.invalidDocs": "--docs\uC5D0 \uB300\uC0C1 \uBB38\uC11C ID(S-\xB7R-\xB7D-)\uB97C \uD558\uB098 \uC774\uC0C1 \uC801\uC73C\uC138\uC694.",
25875
+ "records.notDocIds": "--docs\uC5D0 \uBB38\uC11C ID(S-\xB7R-\xB7D-)\uAC00 \uC544\uB2CC \uAC12\uC774 \uC788\uC2B5\uB2C8\uB2E4: {ids}",
25939
25876
  "records.skeleton": "(\uB0B4\uC6A9)",
25940
25877
  "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
25878
  "records.unknown": "\uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {ids}",
@@ -25967,17 +25904,10 @@ var messages_default3 = {
25967
25904
  "help.specsNewStyle": "\uC694\uAD6C\uC0AC\uD56D \uD615\uC2DD(usecase, default). \uC5C6\uC73C\uBA74 \uC124\uC815\uC758 requirementStyle\uC744 \uB530\uB978\uB2E4",
25968
25905
  "help.newTitle": "\uBB38\uC11C \uC81C\uBAA9",
25969
25906
  "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
25907
  "help.recordsList": "HEAD \uC774\uB825\uC758 \uACB0\uC815\uAE30\uB85D (\uCD5C\uC2E0\uC21C, \uCEE4\uBC0B \uC804 \uAE30\uB85D\uC774 \uBA3C\uC800)",
25978
25908
  "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
25909
  "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)",
25910
+ "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
25911
  "help.listQ": "\uC81C\uBAA9\xB7\uC124\uBA85\xB7\uBCF8\uBB38\uC5D0 \uC774 \uB9D0\uC774 \uC788\uB294 \uAC83\uB9CC",
25982
25912
  "help.listAuthor": "\uC774 Git \uC791\uC131\uC790(\uC774\uB984\uC774\uB098 \uC774\uBA54\uC77C)\uAC00 \uBC14\uAFBC \uAC83\uB9CC",
25983
25913
  "help.listSort": "\uC815\uB82C \uAE30\uC900",
@@ -25994,18 +25924,9 @@ var messages_default3 = {
25994
25924
  "list.features": "\uAE30\uB2A5",
25995
25925
  "list.commits": "\uCEE4\uBC0B",
25996
25926
  "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
25927
  "docs.noMatch": "\uC870\uAC74\uC5D0 \uB9DE\uB294 \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25999
25928
  "docs.requirement": "\uC694\uAD6C\uC0AC\uD56D",
26000
25929
  "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
25930
  "records.empty": "\uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
26010
25931
  "records.moreDocs": "\uC678 {count}\uAC1C",
26011
25932
  "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 +25942,11 @@ var messages_default3 = {
26021
25942
  "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
25943
  "feedback.title": "\uC81C\uBAA9: {title}",
26023
25944
  "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.",
25945
+ "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
25946
  "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
25947
  "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.",
25948
+ "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",
25949
+ "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
25950
  "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
25951
  "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
25952
  "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 +26007,6 @@ var messages_default4 = {
26086
26007
  "server.recordNotFound": "This history has no such decision record.",
26087
26008
  "server.documentNotFound": "Neither this history nor the working tree has that document.",
26088
26009
  "server.commitFileNotFound": "That commit did not change this file.",
26089
- "server.instructionFileNotFound": "The instruction file was not found.",
26090
26010
  "server.specsUnreadable": "Could not read specifications.",
26091
26011
  "server.refreshOrigin": "Refresh requires a same-origin Origin header.",
26092
26012
  "server.fileNotFound": "File not found.",
@@ -26128,7 +26048,7 @@ var messages_default4 = {
26128
26048
  "init.text.storage": "Storage convention",
26129
26049
  "init.text.agentDocs": "Agent instructions",
26130
26050
  "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.",
26051
+ "init.text.updateAvailable": "Version {version} is available. Move the project to it with {command}.",
26132
26052
  "init.failed": "Initialization failed. Check file permissions and the generated configuration and temporary files.",
26133
26053
  "config.storeNotDirectory": ".gitifact must be a regular directory, not a link.",
26134
26054
  "config.fileShape": "config.json must be a regular file no larger than 64 KiB.",
@@ -26163,14 +26083,13 @@ var messages_default4 = {
26163
26083
  "update.text.upToDate": "You are using the latest version.",
26164
26084
  "update.text.disabled": "Update checks are disabled.",
26165
26085
  "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.",
26086
+ "update.text.install": "Update command: {command}",
26087
+ "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
26088
  "update.text.blockRefreshed": "Instruction blocks refreshed: {paths}",
26169
26089
  "update.text.blockCurrent": "Instruction blocks are already current: {paths}",
26170
26090
  "update.text.noBlock": "No instruction files contain a GITIFACT block. Run gitifact init to install one.",
26171
26091
  "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.",
26092
+ "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
26093
  "update.text.committed": "Instruction blocks committed: {commit} ({paths})",
26175
26094
  "update.text.commitNothing": "No instruction block changes to commit.",
26176
26095
  "update.text.commitSkipped.untracked": "Not committed. Instruction files are untracked: {paths}",
@@ -26190,7 +26109,7 @@ var messages_default4 = {
26190
26109
  "help.guide": "Writing guides for agents",
26191
26110
  "help.guideList": "List guides (title and description)",
26192
26111
  "help.guideShow": "Print one guide",
26193
- "help.guideTopic": "One of workflow, spec, design, instructions, records, writing, commit, migrate",
26112
+ "help.guideTopic": "One of workflow, spec, design, records, writing, commit, migrate",
26194
26113
  "command.failed": "The command failed.",
26195
26114
  "project.notInitialized": "Initialize the project with gitifact init first.",
26196
26115
  "input.file": "The input must be a JSON file of 1 MiB or less.",
@@ -26212,17 +26131,14 @@ var messages_default4 = {
26212
26131
  "docs.unknownDocument": "Documents not found: {ids}",
26213
26132
  "docs.references": "References",
26214
26133
  "docs.referencedBy": "Referenced by",
26215
- "docs.files": "Files",
26216
26134
  "docs.invalidPath": "Not a path for a {kind} document: {path}. Example: {example}",
26217
26135
  "docs.pathExample.feature": "payment (.gitifact/spec/payment/index.md)",
26218
26136
  "docs.pathExample.requirement": "payment/cancel (.gitifact/spec/payment/requirements/cancel.md)",
26219
26137
  "docs.pathExample.design": "payment/api (.gitifact/spec/payment/design/api.md)",
26220
- "docs.pathExample.instruction": "cli-architecture (.gitifact/instructions/cli-architecture/index.md)",
26221
26138
  "docs.skeleton.feature": "(What this feature is and where it ends)",
26222
26139
  "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
26140
  "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
26141
  "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
26142
  "docs.exists": "The file already exists: {path}",
26227
26143
  "docs.notFolder": "Something other than a plain folder stands where a folder is needed: {path}",
26228
26144
  "docs.created": "Created {path} ({id})",
@@ -26254,7 +26170,8 @@ var messages_default4 = {
26254
26170
  "changes.recordAltered": "Committed records edited or deleted (the commit is refused): {paths}",
26255
26171
  "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
26172
  "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.",
26173
+ "records.invalidDocs": "Give at least one target document ID (S-, R-, D-) with --docs.",
26174
+ "records.notDocIds": "--docs has values that are not document IDs (S-, R-, D-): {ids}",
26258
26175
  "records.skeleton": "(Content)",
26259
26176
  "records.createdNext": "Fill in the sections, remove the draft: true line, and list this file in paths when you commit.",
26260
26177
  "records.unknown": "No such record: {ids}",
@@ -26286,17 +26203,10 @@ var messages_default4 = {
26286
26203
  "help.specsNewStyle": "Requirement shape (usecase, default). Defaults to the config requirementStyle",
26287
26204
  "help.newTitle": "Document title",
26288
26205
  "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
26206
  "help.recordsList": "Decision records of HEAD's history (newest first, uncommitted ones first)",
26297
26207
  "help.recordsListDoc": "One document's decision flow: the records of each commit that changed it",
26298
26208
  "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)",
26209
+ "help.check": "Check the format, IDs, references and draft marks of all specs and uncommitted records (exit code 1 on problems)",
26300
26210
  "help.listQ": "Only those whose title, description or text holds these words",
26301
26211
  "help.listAuthor": "Only those this Git author (name or email) changed",
26302
26212
  "help.listSort": "Sort key",
@@ -26313,18 +26223,9 @@ var messages_default4 = {
26313
26223
  "list.features": "Features",
26314
26224
  "list.commits": "Commits",
26315
26225
  "list.records": "Records",
26316
- "docs.otherResource": "Not a document this command reads: {ids}. Use {command}.",
26317
26226
  "docs.noMatch": "Nothing matches.",
26318
26227
  "docs.requirement": "requirement",
26319
26228
  "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
26229
  "records.empty": "No decision records yet.",
26329
26230
  "records.moreDocs": "and {count} more",
26330
26231
  "help.feedback": "Send a Gitifact bug or idea as an issue to Gitifact's repository (prints a new-issue address without gh)",
@@ -26340,11 +26241,11 @@ var messages_default4 = {
26340
26241
  "feedback.truncated": "The body is long, so the address holds only its beginning. Paste the full body below into the page.",
26341
26242
  "feedback.title": "Title: {title}",
26342
26243
  "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}.",
26244
+ "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
26245
  "update.later.postponed": "The notice for {latest} is quiet until {until}. A newer release is announced right away.",
26345
26246
  "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}.",
26247
+ "notice.release": "Version {latest} is available (running {version}). Update: {command} \xB7 Later: gitifact update --later",
26248
+ "notice.projectNewer": "This project is set to gitifact {project} (running {version}). Run it through the project's launcher, node .gitifact/bin/cli.mjs.",
26348
26249
  "notice.projectOlder": "This CLI ({version}) is newer than the project ({project}). Run gitifact update to move the project to it.",
26349
26250
  "help.updateLater": "Quiet the announced newer release for 24 hours; a release newer than it is announced right away",
26350
26251
  "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 +26600,7 @@ function storeReader(cwd) {
26699
26600
  import { DatabaseSync } from "node:sqlite";
26700
26601
  import { lstat, mkdir, rm, writeFile } from "node:fs/promises";
26701
26602
  import { join as join2 } from "node:path";
26702
- var CACHE_FORMAT = 12;
26603
+ var CACHE_FORMAT = 13;
26703
26604
  var CACHE_DIR = ".gitifact/cache";
26704
26605
  var SCHEMA = `
26705
26606
  -- Working-tree files already read: the modification time and size decide whether a file is read again.
@@ -26885,6 +26786,7 @@ function createDocumentCache(root, database) {
26885
26786
  async function read(path3, seen) {
26886
26787
  try {
26887
26788
  if (isWikiPage(path3)) return { problem: docProblem("WIKI_REMOVED", path3) };
26789
+ if (isInstructionPath(path3)) return path3.endsWith("/index.md") && classifyDocPath(path3).type === "doc" ? { problem: docProblem("INSTRUCTION_MOVED", instructionFolderOf(path3)) } : {};
26888
26790
  if (classifyDocPath(path3).type !== "doc") return {};
26889
26791
  if (seen.link) return { problem: docProblem("PATH_UNSUPPORTED", path3) };
26890
26792
  if (seen.size > FILE_LIMIT) return { problem: docProblem("FILE_TOO_LARGE", path3) };
@@ -26959,13 +26861,6 @@ function createDocumentCache(root, database) {
26959
26861
  return true;
26960
26862
  }
26961
26863
  };
26962
- const instructionFile3 = (path3) => {
26963
- try {
26964
- return classifyDocPath(path3).type === "instruction-file";
26965
- } catch {
26966
- return false;
26967
- }
26968
- };
26969
26864
  return {
26970
26865
  sync,
26971
26866
  /**
@@ -26982,7 +26877,7 @@ function createDocumentCache(root, database) {
26982
26877
  if (history) seen.set(HISTORY_PATH, { mtime: history.mtimeMs, size: history.size, link: history.isSymbolicLink() || !history.isFile() });
26983
26878
  const read2 = await Promise.all([...seen].map(async ([path3, s]) => {
26984
26879
  if (!readable(path3)) return { path: path3 };
26985
- if (instructionFile3(path3) && !path3.endsWith(".md")) return { path: path3, text: "" };
26880
+ if (isInstructionPath(path3) && !path3.endsWith(".md")) return { path: path3, text: "" };
26986
26881
  if (s.link) return { path: path3, problem: docProblem("PATH_UNSUPPORTED", path3) };
26987
26882
  if (s.size > (path3 === HISTORY_PATH ? 64 * FILE_LIMIT : FILE_LIMIT)) return { path: path3, problem: docProblem("FILE_TOO_LARGE", path3) };
26988
26883
  try {
@@ -28079,88 +27974,6 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
28079
27974
  }
28080
27975
  }
28081
27976
 
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
27977
  // src/queries/document-states.ts
28165
27978
  var isDocument = (path3) => {
28166
27979
  try {
@@ -28229,8 +28042,8 @@ async function documentsAtHead(project, head, paths2) {
28229
28042
 
28230
28043
  // src/server/checkout/stamp.ts
28231
28044
  import { createHash as createHash2 } from "node:crypto";
28232
- import { lstat as lstat8 } from "node:fs/promises";
28233
- import { join as join9 } from "node:path";
28045
+ import { lstat as lstat6 } from "node:fs/promises";
28046
+ import { join as join7 } from "node:path";
28234
28047
  function createStampReader(root, inherited = process.env) {
28235
28048
  const runner = createGitRunner();
28236
28049
  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 +28055,10 @@ function createStampReader(root, inherited = process.env) {
28242
28055
  ]);
28243
28056
  const paths2 = status.split("\0").filter(Boolean).map((entry2) => entry2.slice(3)).filter((path3) => !path3.startsWith(".gitifact/cache/"));
28244
28057
  const seen = await Promise.all(paths2.map(async (path3) => {
28245
- const info5 = await lstat8(join9(root, ...path3.split("/"))).catch(() => void 0);
28058
+ const info5 = await lstat6(join7(root, ...path3.split("/"))).catch(() => void 0);
28246
28059
  return path3 + " " + (info5 ? info5.mtimeMs + ":" + info5.size : "-");
28247
28060
  }));
28248
- const mailmap = await lstat8(join9(root, ".mailmap")).catch(() => void 0);
28061
+ const mailmap = await lstat6(join7(root, ".mailmap")).catch(() => void 0);
28249
28062
  seen.push(".mailmap " + (mailmap ? mailmap.mtimeMs + ":" + mailmap.size : "-"));
28250
28063
  return createHash2("sha256").update(head.trim() + "\n" + seen.sort().join("\n")).digest("hex").slice(0, 20);
28251
28064
  };
@@ -28258,7 +28071,7 @@ async function settled(reads) {
28258
28071
  if (failure2) throw failure2.reason;
28259
28072
  return results.map((r) => r.value);
28260
28073
  }
28261
- var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
28074
+ var folderOf3 = (path3) => path3.split("/").slice(0, 3).join("/");
28262
28075
  function checkoutDocuments(states, authors) {
28263
28076
  const documents = states.documents;
28264
28077
  const standing2 = (id) => {
@@ -28273,7 +28086,7 @@ function checkoutDocuments(states, authors) {
28273
28086
  };
28274
28087
  const arranged = arrangeDocuments(documents);
28275
28088
  const features = arranged.features.map(({ index, requirements, designs }) => {
28276
- const entry2 = authors.get(folderOf4(index.path));
28089
+ const entry2 = authors.get(folderOf3(index.path));
28277
28090
  return {
28278
28091
  id: index.id,
28279
28092
  path: index.path,
@@ -28298,17 +28111,7 @@ function checkoutDocuments(states, authors) {
28298
28111
  updatedAt: entry2?.latest ?? null
28299
28112
  };
28300
28113
  });
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 };
28114
+ return { features, orphans: arranged.orphans };
28312
28115
  }
28313
28116
  function createCheckoutReader(root, cache, inherited = process.env) {
28314
28117
  const reader = storeReader(root);
@@ -28337,16 +28140,15 @@ function createCheckoutReader(root, cache, inherited = process.env) {
28337
28140
  head ? cache.log.contributors(head) : Promise.resolve([])
28338
28141
  ]);
28339
28142
  const states = await documentStates({ head: async () => head, reader }, current3.documents);
28340
- const { features, instructions: instructions2, orphans } = checkoutDocuments(states, authors);
28143
+ const { features, orphans } = checkoutDocuments(states, authors);
28341
28144
  const problems = [...current3.problems, ...orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
28342
28145
  if (await readHead() !== head) throw new StoreError(t2("specReader.projectChanged"));
28343
28146
  return {
28344
28147
  head,
28345
28148
  stamp,
28346
28149
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
28347
- working: head ? !!dirty.trim() : features.length > 0 || instructions2.length > 0,
28150
+ working: head ? !!dirty.trim() : features.length > 0,
28348
28151
  features,
28349
- instructions: instructions2,
28350
28152
  problems,
28351
28153
  people,
28352
28154
  folders: authors
@@ -28369,19 +28171,7 @@ function createCheckoutReader(root, cache, inherited = process.env) {
28369
28171
  }
28370
28172
  return value;
28371
28173
  };
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
- };
28174
+ return { base };
28385
28175
  }
28386
28176
 
28387
28177
  // src/queries/paging.ts
@@ -28546,11 +28336,11 @@ async function workingChange2(source2, id) {
28546
28336
  }
28547
28337
 
28548
28338
  // src/queries/search.ts
28549
- var SEARCH_GROUPS = ["feature", "requirement", "design", "instruction", "record", "commit"];
28339
+ var SEARCH_GROUPS = ["feature", "requirement", "design", "record", "commit"];
28550
28340
  var FIRST = 5;
28551
28341
  var RECENT = 6;
28552
28342
  var place2 = (path3) => path3.replace(/^\.gitifact\//, "");
28553
- var folderOf5 = (path3) => path3.split("/").slice(0, 3).join("/");
28343
+ var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
28554
28344
  async function searchRecords(source2, request) {
28555
28345
  const head = request.head ?? await source2.head();
28556
28346
  const query = request.q.trim().toLowerCase();
@@ -28567,14 +28357,7 @@ async function searchRecords(source2, request) {
28567
28357
  var empty = () => ({ total: 0, next: null, hits: [] });
28568
28358
  async function recent(source2, head) {
28569
28359
  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
- }));
28360
+ 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
28361
  return { group: "recent", total: hits.length, next: null, hits };
28579
28362
  }
28580
28363
 
@@ -28609,7 +28392,6 @@ function checkoutIndex(base) {
28609
28392
  requirements: f.requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description })),
28610
28393
  designs: f.designs.map((d) => ({ id: d.id, path: d.path, title: d.title }))
28611
28394
  })),
28612
- instructions: base.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title })),
28613
28395
  people: base.people.map((p) => ({ name: p.name, email: p.email }))
28614
28396
  };
28615
28397
  }
@@ -28703,12 +28485,12 @@ function recordRoutes(root, sessionId, env) {
28703
28485
  const cursorGone2 = () => new HttpError(404, "NOT_FOUND", t2("server.cursorNotFound"));
28704
28486
  return [
28705
28487
  // 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.
28488
+ // first, the feature list and one feature, and the contributors.
28707
28489
  route({ method: "GET", path: "/api/v1/checkout", session: true, unreadable: unreadable2, handle: async () => {
28708
28490
  const base = await checkout.base();
28709
- return ok(browserCheckoutV1.parse({
28491
+ return ok(browserCheckoutV2.parse({
28710
28492
  contract: "browser-checkout",
28711
- version: 1,
28493
+ version: 2,
28712
28494
  sessionId,
28713
28495
  head: base.head,
28714
28496
  observedAt: base.observedAt,
@@ -28728,7 +28510,6 @@ function recordRoutes(root, sessionId, env) {
28728
28510
  if (!feature2) throw new HttpError(404, "NOT_FOUND", t2("server.featureNotFound"));
28729
28511
  return ok(browserFeatureV2.parse({ contract: "browser-feature", version: 2, sessionId, feature: feature2 }));
28730
28512
  } }),
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
28513
  route({ method: "GET", path: "/api/v1/contributors", session: true, query: browserContributorsQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28733
28514
  const page = listContributors(await checkout.base(), query.q, { after: query.after, limit: query.limit ?? PAGE });
28734
28515
  if (!page) throw cursorGone2();
@@ -28821,35 +28602,28 @@ function recordRoutes(root, sessionId, env) {
28821
28602
  if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.commitFileNotFound"));
28822
28603
  return ok(browserCommitFileV1.parse({ contract: "browser-commit-file", version: 1, sessionId, commit: query.commit, ...found }));
28823
28604
  } }),
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
28605
  // 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 }) => {
28606
+ route({ method: "GET", path: "/api/v1/search", session: true, query: browserSearchQueryV3, unreadable: unreadable2, handle: async ({ query }) => {
28833
28607
  const groups = await searchRecords(source2, query);
28834
28608
  if (!groups) throw cursorGone2();
28835
- return ok(browserSearchV3.parse({ contract: "browser-search", version: 3, sessionId, query: query.q, groups }));
28609
+ return ok(browserSearchV4.parse({ contract: "browser-search", version: 4, sessionId, query: query.q, groups }));
28836
28610
  } })
28837
28611
  ];
28838
28612
  }
28839
28613
 
28840
28614
  // 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";
28615
+ import { lstat as lstat7, readFile as readFile5 } from "node:fs/promises";
28616
+ import { join as join8 } from "node:path";
28843
28617
  function assetRoutes(root) {
28844
28618
  return [
28845
28619
  route({ method: "GET", path: "/api/v1/assets/*", session: false, handle: async ({ path: path3, response }) => {
28846
28620
  const relative = path3.slice("/api/v1/assets/".length);
28847
28621
  const assetPath = ASSETS_DIR + "/" + relative;
28848
28622
  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);
28623
+ const file2 = join8(root, ...assetPath.split("/"));
28624
+ const stat3 = await lstat7(file2).catch(() => void 0);
28851
28625
  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);
28626
+ const bytes = await readFile5(file2);
28853
28627
  const extension = assetExtension(relative);
28854
28628
  const inline = ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(extension);
28855
28629
  response.writeHead(200, {
@@ -28866,7 +28640,7 @@ function assetRoutes(root) {
28866
28640
  }
28867
28641
 
28868
28642
  // src/server/browser-server.ts
28869
- var readBundledChangelog = (language) => readFile8(new URL("./i18n/" + language + "/changelog.md", import.meta.url), "utf8").catch((error62) => {
28643
+ var readBundledChangelog = (language) => readFile6(new URL("./i18n/" + language + "/changelog.md", import.meta.url), "utf8").catch((error62) => {
28870
28644
  if (error62.code === "ENOENT") return null;
28871
28645
  throw error62;
28872
28646
  });
@@ -28996,14 +28770,14 @@ async function runBrowser(options, version2) {
28996
28770
  }
28997
28771
 
28998
28772
  // src/commands/spec-init.ts
28999
- import { readdir as readdir5 } from "node:fs/promises";
29000
- import { join as join14 } from "node:path";
28773
+ import { readdir as readdir4 } from "node:fs/promises";
28774
+ import { join as join13 } from "node:path";
29001
28775
 
29002
28776
  // src/adapters/git/init-repository.ts
29003
28777
  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) => {
28778
+ import { lstat as lstat8, readFile as readFile7 } from "node:fs/promises";
28779
+ import { join as join9 } from "node:path";
28780
+ var markerPresent = (path3) => lstat8(path3).then(() => true, (error62) => {
29007
28781
  if (error62.code === "ENOENT") return false;
29008
28782
  throw error62;
29009
28783
  });
@@ -29026,10 +28800,10 @@ function initRepository(cwd, inherited = process.env) {
29026
28800
  const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
29027
28801
  const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
29028
28802
  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 }));
28803
+ if (await markerPresent(join9(gitDir, marker2))) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.operationInProgress", { marker: marker2 }));
29030
28804
  }
29031
28805
  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) => {
28806
+ const index = await readFile7(indexPath).catch((error62) => {
29033
28807
  if (error62.code === "ENOENT") return Buffer.alloc(0);
29034
28808
  throw error62;
29035
28809
  });
@@ -29064,14 +28838,14 @@ function initRepository(cwd, inherited = process.env) {
29064
28838
 
29065
28839
  // src/adapters/filesystem/document-file.ts
29066
28840
  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";
28841
+ import { lstat as lstat9, mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
28842
+ import { join as join10 } from "node:path";
29069
28843
  var generateId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
29070
28844
  async function createDocumentFile(root, path3, text4) {
29071
28845
  const parts = path3.split("/");
29072
28846
  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) => {
28847
+ const folder = join10(root, ...parts.slice(0, i));
28848
+ const info5 = await lstat9(folder).catch((error62) => {
29075
28849
  if (error62.code === "ENOENT") return void 0;
29076
28850
  throw error62;
29077
28851
  });
@@ -29081,7 +28855,7 @@ async function createDocumentFile(root, path3, text4) {
29081
28855
  }
29082
28856
  if (!info5.isDirectory() || info5.isSymbolicLink()) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
29083
28857
  }
29084
- await writeFile2(join12(root, ...parts), text4, { flag: "wx" });
28858
+ await writeFile2(join10(root, ...parts), text4, { flag: "wx" });
29085
28859
  }
29086
28860
  var LINE_ENDINGS_PATH = ".gitifact/.gitattributes";
29087
28861
  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 +28869,50 @@ async function writeLineEndings(root) {
29095
28869
  }
29096
28870
  }
29097
28871
 
28872
+ // src/adapters/filesystem/launcher-file.ts
28873
+ import { lstat as lstat10, mkdir as mkdir4, readFile as readFile8, writeFile as writeFile3 } from "node:fs/promises";
28874
+ import { join as join11 } from "node:path";
28875
+ var LAUNCHER_PATH = ".gitifact/bin/cli.mjs";
28876
+ var readBundledLauncher = () => readFile8(new URL("./launcher/cli.mjs", import.meta.url), "utf8").catch((error62) => {
28877
+ if (error62.code === "ENOENT") return void 0;
28878
+ throw error62;
28879
+ });
28880
+ async function refreshLauncher(root) {
28881
+ const text4 = await readBundledLauncher();
28882
+ return text4 === void 0 ? false : writeLauncher(root, text4.replace(/\r\n/g, "\n"));
28883
+ }
28884
+ async function writeLauncher(root, text4) {
28885
+ const folder = join11(root, ".gitifact", "bin");
28886
+ const file2 = join11(folder, "cli.mjs");
28887
+ const folderInfo = await lstat10(folder).catch((error62) => {
28888
+ if (error62.code === "ENOENT") return void 0;
28889
+ throw error62;
28890
+ });
28891
+ if (folderInfo && (!folderInfo.isDirectory() || folderInfo.isSymbolicLink())) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
28892
+ if (!folderInfo) await mkdir4(folder);
28893
+ const info5 = await lstat10(file2).catch((error62) => {
28894
+ if (error62.code === "ENOENT") return void 0;
28895
+ throw error62;
28896
+ });
28897
+ if (info5 && (!info5.isFile() || info5.isSymbolicLink())) throw Object.assign(new Error(file2), { code: "ENOTDIR" });
28898
+ if (info5 && (await readFile8(file2, "utf8")).replace(/\r\n/g, "\n") === text4) return false;
28899
+ await writeFile3(file2, text4);
28900
+ return true;
28901
+ }
28902
+
29098
28903
  // 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";
28904
+ 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";
28905
+ import { join as join12, dirname } from "node:path";
29101
28906
  import { randomUUID as randomUUID4 } from "node:crypto";
29102
28907
  var DEFAULT_LIMIT = 1024 * 1024;
29103
28908
  async function managedPath(root, path3, create = false) {
29104
28909
  let full = root;
29105
28910
  const parts = path3.split("/");
29106
28911
  for (let i = 0; i < parts.length; i++) {
29107
- full = join13(full, parts[i]);
28912
+ full = join12(full, parts[i]);
29108
28913
  let info5 = await fileInfo(full);
29109
28914
  if (!info5 && create && i < parts.length - 1) {
29110
- await mkdir4(full).catch((error62) => {
28915
+ await mkdir5(full).catch((error62) => {
29111
28916
  if (error62.code !== "EEXIST") throw error62;
29112
28917
  });
29113
28918
  info5 = await fileInfo(full);
@@ -29123,7 +28928,7 @@ async function managedRead(root, path3, limit = DEFAULT_LIMIT) {
29123
28928
  const before = await fileInfo(full);
29124
28929
  if (!before) return null;
29125
28930
  if (before.size > limit) throw new InitError("FILE_TOO_LARGE", t2("managedFile.tooLarge", { kib: limit / 1024, path: path3 }));
29126
- const bytes = await readFile10(full);
28931
+ const bytes = await readFile9(full);
29127
28932
  await managedPath(root, path3);
29128
28933
  const after = await fileInfo(full);
29129
28934
  if (!after || before.ino !== after.ino || before.dev !== after.dev || before.mtimeMs !== after.mtimeMs || bytes.length > limit) {
@@ -29152,8 +28957,8 @@ async function managedWrite(root, path3, previous, next, recheck) {
29152
28957
  if (previous !== null) await unlink2(full);
29153
28958
  return;
29154
28959
  }
29155
- const temp = join13(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
29156
- const handle = await open4(temp, "wx", 384);
28960
+ const temp = join12(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
28961
+ const handle = await open3(temp, "wx", 384);
29157
28962
  const tempOwner = await handle.stat();
29158
28963
  try {
29159
28964
  try {
@@ -29164,7 +28969,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
29164
28969
  }
29165
28970
  await recheck();
29166
28971
  await unchanged();
29167
- if (await managedRead(root, path3) !== previous || await readFile10(temp, "utf8") !== next) throw new InitError("INPUT_CHANGED", t2("managedFile.publishTargetChanged", { path: path3 }));
28972
+ if (await managedRead(root, path3) !== previous || await readFile9(temp, "utf8") !== next) throw new InitError("INPUT_CHANGED", t2("managedFile.publishTargetChanged", { path: path3 }));
29168
28973
  if (previous === null) await link2(temp, full);
29169
28974
  else await rename2(temp, full);
29170
28975
  } finally {
@@ -29175,10 +28980,10 @@ async function managedWrite(root, path3, previous, next, recheck) {
29175
28980
  }
29176
28981
 
29177
28982
  // src/commands/agent-block.ts
29178
- import { readFile as readFile12 } from "node:fs/promises";
28983
+ import { readFile as readFile11 } from "node:fs/promises";
29179
28984
 
29180
28985
  // src/commands/guide.ts
29181
- import { readFile as readFile11 } from "node:fs/promises";
28986
+ import { readFile as readFile10 } from "node:fs/promises";
29182
28987
 
29183
28988
  // src/commands/output.ts
29184
28989
  var formats = ["text", "json"];
@@ -29213,9 +29018,9 @@ var section = (label, lines2, indent = " ") => lines2.length ? [label, ...lines
29213
29018
  var text2 = (lines2) => lines2.join("\n") + "\n";
29214
29019
 
29215
29020
  // src/commands/guide.ts
29216
- var guideTopics = ["workflow", "spec", "design", "instructions", "records", "writing", "commit", "migrate"];
29021
+ var guideTopics = ["workflow", "spec", "design", "records", "writing", "commit", "migrate"];
29217
29022
  var isGuideTopic = (value) => guideTopics.includes(value);
29218
- var readBundledGuide = (name2, lang) => readFile11(new URL("./i18n/" + lang + "/docs/" + name2 + ".md", import.meta.url), "utf8");
29023
+ var readBundledGuide = (name2, lang) => readFile10(new URL("./i18n/" + lang + "/docs/" + name2 + ".md", import.meta.url), "utf8");
29219
29024
  function guideSummary(source2) {
29220
29025
  const { fields: fields2 } = parseFrontmatterBlock(source2.replace(/\r\n/g, "\n"), "guide");
29221
29026
  const value = (key) => {
@@ -29256,7 +29061,7 @@ function boilerplateFor(path3) {
29256
29061
  const name2 = path3 === "AGENTS.md" ? path3 : path3.split("/").pop().replace(/^\./, "").replace(/\.md$/, "");
29257
29062
  return "# " + name2 + "\n\nProject-specific guidance for AI coding agents.\n";
29258
29063
  }
29259
- var readBundledBlock = (lang) => readFile12(new URL("./i18n/" + lang + "/block.md", import.meta.url), "utf8");
29064
+ var readBundledBlock = (lang) => readFile11(new URL("./i18n/" + lang + "/block.md", import.meta.url), "utf8");
29260
29065
  async function renderAgentBlock(controls = {}, lang = getLanguage()) {
29261
29066
  const body2 = await (controls.readBlock ?? readBundledBlock)(lang);
29262
29067
  return AGENT_START + "\n" + body2.trimEnd().replace(/\{topics\}/g, guideTopics.join(", ")) + "\n" + AGENT_END;
@@ -29285,15 +29090,9 @@ function isWrapperFile(text4) {
29285
29090
  return lines2.length > 0 && lines2.every((line2) => /^@(?:\.\/)?[^\s]+$/.test(line2) && candidatePaths.includes(line2.replace(/^@(?:\.\/)?/, "")));
29286
29091
  }
29287
29092
  var CLAUDE_WRAPPER_PATH = "CLAUDE.md";
29288
- function claudeWrapperFor(agents2) {
29289
- return "@AGENTS.md" + eolOf(agents2);
29290
- }
29291
29093
  function isGeneratedClaudeWrapper(text4) {
29292
29094
  return text4.trim() === "@AGENTS.md";
29293
29095
  }
29294
- function lacksClaudeFile(existing) {
29295
- return !existing.has("CLAUDE.md") && !existing.has(".claude/CLAUDE.md");
29296
- }
29297
29096
  function resolveAgentPaths(preset, existing) {
29298
29097
  if (preset && preset !== "all") {
29299
29098
  const order = agentPresets[preset];
@@ -29327,7 +29126,7 @@ function removeBlock(text4, boilerplate) {
29327
29126
  }
29328
29127
 
29329
29128
  // src/commands/agent-docs.ts
29330
- var skippedAgentDocs = (language = explicitLanguage() ?? getLanguage()) => ({ mode: "skip", paths: [], writes: [], missing: [], language });
29129
+ var skippedAgentDocs = (language = explicitLanguage() ?? getLanguage()) => ({ mode: "skip", paths: [], writes: [], language });
29331
29130
  var blockOf = (text4) => {
29332
29131
  try {
29333
29132
  const found = findBlock(text4);
@@ -29362,7 +29161,7 @@ async function planAgentDocs(root, options) {
29362
29161
  paths2.push(CLAUDE_WRAPPER_PATH);
29363
29162
  writes.push({ path: CLAUDE_WRAPPER_PATH, previous: wrapper, next: null });
29364
29163
  }
29365
- return { mode: "remove", paths: paths2, writes, missing: [], language };
29164
+ return { mode: "remove", paths: paths2, writes, language };
29366
29165
  }
29367
29166
  let block;
29368
29167
  const blockFor = async () => block ??= await renderAgentBlock(options, language);
@@ -29377,19 +29176,11 @@ async function planAgentDocs(root, options) {
29377
29176
  paths2.push(create);
29378
29177
  writes.push({ path: create, previous: null, next: injectBlock(null, await blockFor(), boilerplateFor(create)) });
29379
29178
  }
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
29179
  for (const [path3, text4] of existing) {
29389
29180
  if (paths2.includes(path3) || !isWrapperFile(text4) || !findBlock(text4)) continue;
29390
29181
  writes.push({ path: path3, previous: text4, next: removeBlock(text4, boilerplateFor(path3)) });
29391
29182
  }
29392
- return { mode: "install", paths: paths2, writes, missing: missing2, language };
29183
+ return { mode: "install", paths: paths2, writes, language };
29393
29184
  }
29394
29185
  async function applyAgentDocs(root, plan, recheck) {
29395
29186
  for (const write of plan.writes) await managedWrite(root, write.path, write.previous, write.next, recheck);
@@ -29498,6 +29289,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
29498
29289
  current2 = next;
29499
29290
  }
29500
29291
  await applyAgentDocs(root, docsPlan, unchanged);
29292
+ if (agentDocs && !(config3.cli && isNewerRelease(config3.cli, agentDocs.version))) await refreshLauncher(root);
29501
29293
  return result(settled2.config, "already-initialized", await writeLineEndings(root));
29502
29294
  };
29503
29295
  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 +29301,14 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
29509
29301
  };
29510
29302
  await published();
29511
29303
  await applyAgentDocs(root, docsPlan, published);
29304
+ if (agentDocs) await refreshLauncher(root);
29512
29305
  return result(config2, outcome, await writeLineEndings(root));
29513
29306
  };
29514
29307
  const old = await readConfigFile(root);
29515
29308
  if (old !== void 0) return existing(old);
29516
29309
  if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
29517
29310
  const checkRecords = async () => {
29518
- const entries = await readdir5(join14(root, ".gitifact")).catch((e) => {
29311
+ const entries = await readdir4(join13(root, ".gitifact")).catch((e) => {
29519
29312
  if (e.code === "ENOENT") return [];
29520
29313
  throw e;
29521
29314
  });
@@ -29553,22 +29346,22 @@ var fetchLatestVersion = async (signal) => {
29553
29346
  };
29554
29347
 
29555
29348
  // 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";
29349
+ import { mkdir as mkdir6, readFile as readFile12, rename as rename3, unlink as unlink3, writeFile as writeFile4 } from "node:fs/promises";
29557
29350
  import { homedir } from "node:os";
29558
- import { join as join15 } from "node:path";
29351
+ import { join as join14 } from "node:path";
29559
29352
  import { randomUUID as randomUUID5 } from "node:crypto";
29560
29353
  function userCacheDirectory(env = process.env, platform = process.platform, home = homedir()) {
29561
29354
  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");
29355
+ if (platform === "win32") return join14(env.LOCALAPPDATA || join14(home, "AppData", "Local"), "gitifact");
29356
+ if (platform === "darwin") return join14(home, "Library", "Caches", "gitifact");
29357
+ return join14(env.XDG_CACHE_HOME || join14(home, ".cache"), "gitifact");
29565
29358
  }
29566
- var cacheFile = (env) => join15(userCacheDirectory(env), "update.json");
29359
+ var cacheFile = (env) => join14(userCacheDirectory(env), "update.json");
29567
29360
  var isTime = (value) => typeof value === "string" && !Number.isNaN(Date.parse(value));
29568
29361
  async function readUpdateCache(env = process.env) {
29569
29362
  let value;
29570
29363
  try {
29571
- value = JSON.parse(await readFile13(cacheFile(env), "utf8"));
29364
+ value = JSON.parse(await readFile12(cacheFile(env), "utf8"));
29572
29365
  } catch {
29573
29366
  return {};
29574
29367
  }
@@ -29583,11 +29376,11 @@ async function readUpdateCache(env = process.env) {
29583
29376
  }
29584
29377
  async function changeUpdateCache(env, change2) {
29585
29378
  const directory = userCacheDirectory(env);
29586
- const temporary = join15(directory, "update-" + randomUUID5() + ".tmp");
29379
+ const temporary = join14(directory, "update-" + randomUUID5() + ".tmp");
29587
29380
  try {
29588
29381
  const next = change2(await readUpdateCache(env));
29589
- await mkdir5(directory, { recursive: true });
29590
- await writeFile3(temporary, JSON.stringify(next) + "\n", "utf8");
29382
+ await mkdir6(directory, { recursive: true });
29383
+ await writeFile4(temporary, JSON.stringify(next) + "\n", "utf8");
29591
29384
  await rename3(temporary, cacheFile(env));
29592
29385
  } catch {
29593
29386
  await unlink3(temporary).catch(() => {
@@ -29616,7 +29409,7 @@ async function runInit(options, version2) {
29616
29409
  await recordUpdateCheck(process.env, dto.update);
29617
29410
  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
29411
  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" : "";
29412
+ const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npx }) + "\n" : "";
29620
29413
  process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
29621
29414
  ${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
29622
29415
  ${t2("init.text.agentDocs")}: ${docs}
@@ -29631,17 +29424,17 @@ ${lineEndings}${newer}` : JSON.stringify(dto) + "\n");
29631
29424
  }
29632
29425
 
29633
29426
  // src/commands/documents.ts
29634
- import { readFile as readFile14 } from "node:fs/promises";
29635
- import { join as join17 } from "node:path";
29427
+ import { readFile as readFile13 } from "node:fs/promises";
29428
+ import { join as join16 } from "node:path";
29636
29429
 
29637
29430
  // src/commands/project.ts
29638
- import { join as join16 } from "node:path";
29431
+ import { join as join15 } from "node:path";
29639
29432
  var operations = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
29640
29433
  async function openProject(cwd, options = {}) {
29641
29434
  const reader = storeReader(cwd);
29642
29435
  const location = await reader.location();
29643
29436
  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 }));
29437
+ 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
29438
  if (await reader.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
29646
29439
  }
29647
29440
  const raw = await readConfigFile(location.root);
@@ -29800,12 +29593,11 @@ var stateLabel = {
29800
29593
  };
29801
29594
  var stateMark = (state2) => state2 === "committed" ? "" : " (" + stateLabel[state2]() + ")";
29802
29595
  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
29596
  async function working(project) {
29805
29597
  const { documents } = await project.cache.documents.list();
29806
29598
  return {
29807
29599
  byId: new Map(documents.map((d) => [d.id, d])),
29808
- read: (path3) => readFile14(join17(project.root, ...path3.split("/")), "utf8"),
29600
+ read: (path3) => readFile13(join16(project.root, ...path3.split("/")), "utf8"),
29809
29601
  referencing: async (id) => [...new Set((await project.cache.documents.referencing(id)).map((r) => r.from))]
29810
29602
  };
29811
29603
  }
@@ -29818,12 +29610,12 @@ async function committed(project, commit) {
29818
29610
  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
29611
  };
29820
29612
  }
29821
- async function showDocuments(project, ids, at, kinds, otherResource) {
29613
+ async function showDocuments(project, ids, at, kinds) {
29822
29614
  const view = at === void 0 ? await working(project) : await committed(project, at);
29823
29615
  const unknown2 = ids.filter((id) => !view.byId.has(id));
29824
29616
  if (unknown2.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: unknown2.join(", ") }));
29825
29617
  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 }));
29618
+ if (other.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: other.join(", ") }));
29827
29619
  const shown = await Promise.all([...new Set(ids)].map(async (id) => {
29828
29620
  const doc = view.byId.get(id);
29829
29621
  const references = doc.kind === "design" ? [
@@ -29831,34 +29623,30 @@ async function showDocuments(project, ids, at, kinds, otherResource) {
29831
29623
  ...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
29624
  ] : [];
29833
29625
  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 };
29626
+ return { id, kind: doc.kind, path: doc.path, title: doc.title, text: await view.read(doc.path), references, referencedBy };
29836
29627
  }));
29837
29628
  const named = (r) => r.id ? r.id + " " + (r.title ?? "(" + t2("docs.missing") + ")") : r.title + " <" + r.url + ">";
29838
29629
  const blocks = shown.map((d) => [
29839
29630
  `== ${d.id} ${d.path}${at ? " @ " + at.slice(0, 7) : ""}`,
29840
29631
  d.text.replace(/\n$/, ""),
29841
29632
  ...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))
29633
+ ...section("-- " + t2("docs.referencedBy"), d.referencedBy.map(named))
29844
29634
  ].join("\n"));
29845
29635
  return { json: { ...at ? { commit: at } : {}, documents: shown }, text: blocks.join("\n\n") + "\n" };
29846
29636
  }
29847
29637
  var pathExample = {
29848
29638
  feature: () => t2("docs.pathExample.feature"),
29849
29639
  requirement: () => t2("docs.pathExample.requirement"),
29850
- design: () => t2("docs.pathExample.design"),
29851
- instruction: () => t2("docs.pathExample.instruction")
29640
+ design: () => t2("docs.pathExample.design")
29852
29641
  };
29853
29642
  var skeleton = {
29854
29643
  feature: () => t2("docs.skeleton.feature"),
29855
29644
  requirement: () => t2("docs.skeleton.requirement"),
29856
- design: () => t2("docs.skeleton.design"),
29857
- instruction: () => t2("docs.skeleton.instruction")
29645
+ design: () => t2("docs.skeleton.design")
29858
29646
  };
29859
29647
  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" ? /^[^/]+$/ : /^[^/]+\/[^/]+$/;
29648
+ const target = kind2 === "feature" ? `${SPEC_ROOT}/${path3}/index.md` : `${SPEC_ROOT}/${path3.split("/")[0]}/${kind2 === "requirement" ? "requirements" : "design"}/${path3.split("/").slice(1).join("/")}.md`;
29649
+ const shape = kind2 === "feature" ? /^[^/]+$/ : /^[^/]+\/[^/]+$/;
29862
29650
  let where;
29863
29651
  try {
29864
29652
  where = shape.test(path3) ? classifyDocPath(target) : void 0;
@@ -29881,7 +29669,7 @@ async function createDocument(project, kind2, path3, options) {
29881
29669
  const taken = new Set(documents.map((d) => d.id));
29882
29670
  let id;
29883
29671
  do
29884
- id = generateId({ feature: "S", requirement: "R", design: "D", instruction: "I" }[kind2]);
29672
+ id = generateId({ feature: "S", requirement: "R", design: "D" }[kind2]);
29885
29673
  while (taken.has(id));
29886
29674
  const folder = target.slice(0, target.lastIndexOf("/") + 1);
29887
29675
  const siblings = documents.filter((d) => (d.kind === "requirement" || d.kind === "design") && d.path.startsWith(folder) && !d.path.slice(folder.length).includes("/"));
@@ -29889,7 +29677,7 @@ async function createDocument(project, kind2, path3, options) {
29889
29677
  const body2 = style2 === "usecase" ? t2("docs.skeleton.requirementUsecase") : skeleton[kind2]();
29890
29678
  const base = { id, path: target, title: options.title.trim(), description: options.description.trim(), body: body2, draft: true };
29891
29679
  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 };
29680
+ 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
29681
  const rendered = renderDocumentFile(doc);
29894
29682
  parseDocumentFile(target, rendered);
29895
29683
  try {
@@ -29912,11 +29700,11 @@ function parseLimit(value) {
29912
29700
  if (!/^[1-9]\d{0,5}$/.test(value)) throw new InvalidArgumentError(t2("list.limit"));
29913
29701
  return Number(value);
29914
29702
  }
29915
- var parseFields = (value) => value.split(",").map((f) => f.trim()).filter(Boolean);
29916
- function checkFields(fields2, columns3) {
29703
+ var parseFields = (value) => value.split(/[\s,]+/).filter(Boolean);
29704
+ function checkFields(fields2, columns2) {
29917
29705
  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(", ") }));
29706
+ const unknown2 = fields2.filter((f) => !columns2.includes(f));
29707
+ if (unknown2.length || !fields2.length) throw new CommandError("INVALID_VALUE", t2("list.unknownFields", { fields: unknown2.join(", "), columns: columns2.join(", ") }));
29920
29708
  return [...new Set(fields2)];
29921
29709
  }
29922
29710
  function cell(value) {
@@ -30004,120 +29792,14 @@ var runSpecsList = (options) => runCommand("specs", options.format, async () =>
30004
29792
  var runSpecsShow = (ids, options) => runCommand("specs", options.format, async () => {
30005
29793
  const project = await openProject(process.cwd());
30006
29794
  const at = options.ref === void 0 ? void 0 : await project.reader.resolve(options.ref);
30007
- return showDocuments(project, ids, at, specKinds, "instructions show");
29795
+ return showDocuments(project, ids, at, at === void 0 ? specKinds : [...specKinds, "instruction"]);
30008
29796
  });
30009
29797
  var runSpecsNew = (kind2, path3, options) => runCommand("specs", options.format, async () => createDocument(await openProject(process.cwd()), kind2, path3, options));
30010
29798
 
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
29799
  // 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) => {
29800
+ import { lstat as lstat11, readdir as readdir5 } from "node:fs/promises";
29801
+ import { join as join17 } from "node:path";
29802
+ var info2 = async (path3) => lstat11(path3).catch((e) => {
30121
29803
  if (e.code === "ENOENT") return void 0;
30122
29804
  throw e;
30123
29805
  });
@@ -30125,20 +29807,20 @@ var posix = (path3) => path3.split("\\").join("/");
30125
29807
  async function listAssets(root) {
30126
29808
  const out = [];
30127
29809
  async function visit2(relative, depth) {
30128
- const stat3 = await info2(join19(root, relative));
29810
+ const stat3 = await info2(join17(root, relative));
30129
29811
  if (!stat3 || stat3.isSymbolicLink()) return;
30130
29812
  if (stat3.isDirectory()) {
30131
29813
  if (depth > 8 || out.length > 4e3) return;
30132
- for (const name2 of (await readdir6(join19(root, relative))).sort()) await visit2(relative + "/" + name2, depth + 1);
29814
+ for (const name2 of (await readdir5(join17(root, relative))).sort()) await visit2(relative + "/" + name2, depth + 1);
30133
29815
  } else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
30134
29816
  }
30135
29817
  await visit2(ASSETS_DIR, 0);
30136
29818
  return out;
30137
29819
  }
30138
29820
  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 + "/")))];
29821
+ const outside = [...new Set(documentBodyLinks(documents).map((l) => l.target).filter((t3) => !t3.startsWith(".gitifact/")))];
30140
29822
  const existing = /* @__PURE__ */ new Set();
30141
- for (const target of outside) if ((await info2(join19(root, ...target.split("/"))))?.isFile()) existing.add(target);
29823
+ for (const target of outside) if ((await info2(join17(root, ...target.split("/"))))?.isFile()) existing.add(target);
30142
29824
  return documentWarnings(documents, await listAssets(root), existing);
30143
29825
  }
30144
29826
 
@@ -30162,31 +29844,31 @@ var runCheck = (options) => runCommand("check", options.format, async () => {
30162
29844
 
30163
29845
  // src/commands/changes.ts
30164
29846
  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";
29847
+ import { mkdir as mkdir8, open as open4, readFile as readFile16, rename as rename4, rmdir, unlink as unlink5, writeFile as writeFile6 } from "node:fs/promises";
29848
+ import { join as join20 } from "node:path";
30167
29849
 
30168
29850
  // src/adapters/filesystem/agent-inputs.ts
30169
29851
  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";
29852
+ import { lstat as lstat12, mkdir as mkdir7, readdir as readdir6, realpath as realpath3, unlink as unlink4, writeFile as writeFile5 } from "node:fs/promises";
30171
29853
  import { tmpdir } from "node:os";
30172
- import { basename, dirname as dirname2, join as join20 } from "node:path";
29854
+ import { basename, dirname as dirname2, join as join18 } from "node:path";
30173
29855
  var maxAge = 7 * 24 * 60 * 60 * 1e3;
30174
- var info3 = (path3) => lstat14(path3).catch((e) => {
29856
+ var info3 = (path3) => lstat12(path3).catch((e) => {
30175
29857
  if (e.code === "ENOENT") return void 0;
30176
29858
  throw e;
30177
29859
  });
30178
29860
  var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
30179
29861
  async function candidates(root, controls) {
30180
29862
  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") };
29863
+ return { system: join18((controls.tmpdir ?? tmpdir)(), "gitifact", key), project: join18(root, ".gitifact", "tmp") };
30182
29864
  }
30183
29865
  async function writable(directory) {
30184
29866
  try {
30185
- await mkdir6(directory, { recursive: true });
29867
+ await mkdir7(directory, { recursive: true });
30186
29868
  const stat3 = await info3(directory);
30187
29869
  if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
30188
- const probe = join20(directory, ".probe-" + randomBytes2(8).toString("hex"));
30189
- await writeFile4(probe, "", { flag: "wx" });
29870
+ const probe = join18(directory, ".probe-" + randomBytes2(8).toString("hex"));
29871
+ await writeFile5(probe, "", { flag: "wx" });
30190
29872
  await unlink4(probe);
30191
29873
  return true;
30192
29874
  } catch {
@@ -30199,18 +29881,18 @@ async function prepareAgentInputs(root, controls = {}) {
30199
29881
  if (await writable(system)) directory = system;
30200
29882
  else if (await writable(project)) {
30201
29883
  directory = project;
30202
- await writeFile4(join20(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
29884
+ await writeFile5(join18(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
30203
29885
  if (e.code !== "EEXIST") throw e;
30204
29886
  });
30205
29887
  }
30206
29888
  if (!directory) return void 0;
30207
29889
  const now = (controls.now ?? Date.now)();
30208
- for (const name2 of await readdir7(directory).catch(() => [])) {
29890
+ for (const name2 of await readdir6(directory).catch(() => [])) {
30209
29891
  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);
29892
+ const stat3 = await info3(join18(directory, name2)).catch(() => void 0);
29893
+ if (stat3?.isFile() && now - stat3.mtimeMs > maxAge) await unlink4(join18(directory, name2)).catch(() => void 0);
30212
29894
  }
30213
- return { commit: join20(directory, "commit.json") };
29895
+ return { commit: join18(directory, "commit.json") };
30214
29896
  }
30215
29897
  async function discardAgentInput(root, file2, controls = {}) {
30216
29898
  try {
@@ -30232,17 +29914,17 @@ async function discardAgentInput(root, file2, controls = {}) {
30232
29914
 
30233
29915
  // src/commands/commit-files.ts
30234
29916
  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";
29917
+ import { lstat as lstat13, readFile as readFile14 } from "node:fs/promises";
29918
+ import { join as join19 } from "node:path";
30237
29919
  var fail3 = (message) => {
30238
29920
  throw new CommandError("INVALID_COMMIT", message);
30239
29921
  };
30240
29922
  var hash2 = (value) => createHash5("sha256").update(value).digest("hex");
30241
- var optional2 = (path3) => readFile15(path3).catch((e) => {
29923
+ var optional2 = (path3) => readFile14(path3).catch((e) => {
30242
29924
  if (e.code === "ENOENT") return null;
30243
29925
  throw e;
30244
29926
  });
30245
- var info4 = (path3) => lstat15(path3).catch((e) => {
29927
+ var info4 = (path3) => lstat13(path3).catch((e) => {
30246
29928
  if (e.code === "ENOENT") return void 0;
30247
29929
  throw e;
30248
29930
  });
@@ -30254,7 +29936,7 @@ var record3 = (path3) => {
30254
29936
  return false;
30255
29937
  }
30256
29938
  };
30257
- var instructionFile2 = (path3) => {
29939
+ var instructionFile = (path3) => {
30258
29940
  try {
30259
29941
  return classifyDocPath(path3).type === "instruction-file";
30260
29942
  } catch {
@@ -30268,14 +29950,14 @@ async function fingerprint(root, path3) {
30268
29950
  validPath2(path3);
30269
29951
  const parts = path3.split("/");
30270
29952
  for (let i = 1; i <= parts.length; i++) {
30271
- const target = join21(root, ...parts.slice(0, i));
29953
+ const target = join19(root, ...parts.slice(0, i));
30272
29954
  const stat3 = await info4(target);
30273
29955
  if (!stat3) return null;
30274
29956
  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"));
29957
+ if (i < parts.length && await info4(join19(target, ".git"))) fail3(t2("commitFiles.nestedRepository"));
30276
29958
  if (i === parts.length) {
30277
29959
  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)]));
29960
+ return hash2(Buffer.concat([Buffer.from(String(stat3.mode & 73) + "\0"), await readFile14(target)]));
30279
29961
  }
30280
29962
  }
30281
29963
  return null;
@@ -30302,16 +29984,16 @@ function policyPaths(files) {
30302
29984
  }
30303
29985
  return [...all].sort();
30304
29986
  }
30305
- var storeFiles = /* @__PURE__ */ new Set([".gitifact/config.json", LINE_ENDINGS_PATH]);
29987
+ var storeFiles = /* @__PURE__ */ new Set([".gitifact/config.json", LINE_ENDINGS_PATH, LAUNCHER_PATH]);
30306
29988
  function checkStoreSelection(selected2) {
30307
29989
  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 }));
29990
+ if (!isAssetPath(p) && !instructionFile(p)) fail3(t2("commitFiles.notRecord", { path: p }));
30309
29991
  }
30310
29992
  }
30311
29993
 
30312
29994
  // src/commands/input.ts
30313
- import { readFile as readFile16, stat as stat2 } from "node:fs/promises";
30314
- var LIMIT2 = 1024 * 1024;
29995
+ import { readFile as readFile15, stat as stat2 } from "node:fs/promises";
29996
+ var LIMIT = 1024 * 1024;
30315
29997
  async function readJsonInput(file2, stdin = process.stdin) {
30316
29998
  const bytes = file2 === "-" ? await readStdin(stdin) : await readInputFile(file2);
30317
29999
  try {
@@ -30322,15 +30004,15 @@ async function readJsonInput(file2, stdin = process.stdin) {
30322
30004
  }
30323
30005
  async function readInputFile(path3) {
30324
30006
  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);
30007
+ if (!source2?.isFile() || source2.size > LIMIT) throw new CommandError("INVALID_INPUT", t2("input.file"));
30008
+ return readFile15(path3);
30327
30009
  }
30328
30010
  async function readStdin(stream) {
30329
30011
  const chunks = [];
30330
30012
  let size = 0;
30331
30013
  for await (const chunk of stream) {
30332
30014
  size += chunk.length;
30333
- if (size > LIMIT2) throw new CommandError("INVALID_INPUT", t2("input.size"));
30015
+ if (size > LIMIT) throw new CommandError("INVALID_INPUT", t2("input.size"));
30334
30016
  chunks.push(chunk);
30335
30017
  }
30336
30018
  return Buffer.concat(chunks);
@@ -30404,7 +30086,7 @@ async function commitChanges(cwd, input2, dryRun) {
30404
30086
  if (Object.keys(authorization).sort().join(",") !== "basis,evidence" || !["user-request", "project-policy"].includes(String(authorization.basis))) fail3(t2("commit.authorizationRequired"));
30405
30087
  text3(authorization.evidence, 2e3);
30406
30088
  const message = text3(request.message, 4e3);
30407
- if (/^\s*Gitifact-/im.test(message)) fail3(t2("commit.trailerInMessage"));
30089
+ if (/^[ \t]*Gitifact-[A-Za-z0-9-]+[ \t]*:/im.test(message)) fail3(t2("commit.trailerInMessage"));
30408
30090
  if (request.migration !== void 0 && request.migration !== true) fail3(t2("commit.migrationFlag"));
30409
30091
  const project = await openProject(cwd, { writing: true });
30410
30092
  const { root, gitDir, indexPath, objectFormat } = project;
@@ -30430,7 +30112,7 @@ async function commitChanges(cwd, input2, dryRun) {
30430
30112
  ...stdin ? { input: stdin } : {}
30431
30113
  });
30432
30114
  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");
30115
+ const busy = join20(gitDir, "gitifact-changes-commit.lock");
30434
30116
  if (await info4(busy)) fail3(t2("commit.busy", { path: busy }));
30435
30117
  if ((await staged()).length) fail3(t2("commit.existingStaging"));
30436
30118
  const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
@@ -30470,7 +30152,7 @@ async function commitChanges(cwd, input2, dryRun) {
30470
30152
  if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
30471
30153
  return { outcome: "dry-run", committed: false, ...summary, commit: void 0 };
30472
30154
  }
30473
- const temporary = join22(gitDir, "gitifact-commit-index-" + randomUUID6());
30155
+ const temporary = join20(gitDir, "gitifact-commit-index-" + randomUUID6());
30474
30156
  let owned = false;
30475
30157
  let uncertain = false;
30476
30158
  let commitStarted = false;
@@ -30478,20 +30160,20 @@ async function commitChanges(cwd, input2, dryRun) {
30478
30160
  let committed2;
30479
30161
  try {
30480
30162
  try {
30481
- await mkdir7(busy);
30163
+ await mkdir8(busy);
30482
30164
  owned = true;
30483
30165
  } catch (e) {
30484
30166
  if (e.code === "EEXIST") fail3(t2("commit.busy", { path: busy }));
30485
30167
  throw e;
30486
30168
  }
30487
- const lock = indexLock = await open5(indexPath + ".lock", "wx", 384);
30169
+ const lock = indexLock = await open4(indexPath + ".lock", "wx", 384);
30488
30170
  const original = await optional2(indexPath);
30489
30171
  if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail3(t2("commit.indexChanged"));
30490
30172
  const before = await hashes([...started.keys()]);
30491
30173
  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 }));
30174
+ await writeFile6(join20(busy, "recovery.json"), JSON.stringify({ before: base, originalIndex: original?.toString("base64") ?? null, temporary, paths: selected2, message }));
30493
30175
  const locked = await hashes([.../* @__PURE__ */ new Set([...selected2, ...contextPaths])]);
30494
- if (original) await writeFile5(temporary, original, { flag: "wx" });
30176
+ if (original) await writeFile6(temporary, original, { flag: "wx" });
30495
30177
  else await git(["read-tree", "--empty"], temporary);
30496
30178
  await git(["add", "--pathspec-from-file=-", "--pathspec-file-nul"], temporary, Buffer.from(stageable.join("\0")));
30497
30179
  const actual = await staged(temporary);
@@ -30506,14 +30188,14 @@ async function commitChanges(cwd, input2, dryRun) {
30506
30188
  const size = Number(header[2]);
30507
30189
  if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail3(t2("commit.stagedUnreadable", { path: path3 }));
30508
30190
  const blob = output2.subarray(end + 1, end + 1 + size);
30509
- const file2 = await readFile17(join22(root, path3));
30191
+ const file2 = await readFile16(join20(root, path3));
30510
30192
  if (!blob.equals(file2) && !blob.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail3(t2("commit.filterChanged"));
30511
30193
  offset = end + size + 2;
30512
30194
  }
30513
30195
  }
30514
30196
  const again = await hashes([...locked.keys()]);
30515
30197
  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"));
30198
+ for (const name2 of integration) if (await info4(join20(gitDir, name2))) fail3(t2("commit.integrationStarted"));
30517
30199
  if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
30518
30200
  const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
30519
30201
  commitStarted = true;
@@ -30528,7 +30210,7 @@ async function commitChanges(cwd, input2, dryRun) {
30528
30210
  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
30211
  if (hash2(await optional2(indexPath) ?? Buffer.alloc(0)) !== indexHash) fail3(t2("commit.indexChangedAfter"));
30530
30212
  if ((await git(["write-tree"], temporary)).toString("utf8").trim() !== tree) fail3(t2("commit.hookChangedStaging"));
30531
- await lock.writeFile(await readFile17(temporary));
30213
+ await lock.writeFile(await readFile16(temporary));
30532
30214
  await lock.sync();
30533
30215
  await lock.close();
30534
30216
  await rename4(indexPath + ".lock", indexPath);
@@ -30553,7 +30235,7 @@ async function commitChanges(cwd, input2, dryRun) {
30553
30235
  await unlink5(temporary).catch(() => {
30554
30236
  });
30555
30237
  if (owned) {
30556
- await unlink5(join22(busy, "recovery.json")).catch(() => {
30238
+ await unlink5(join20(busy, "recovery.json")).catch(() => {
30557
30239
  });
30558
30240
  await rmdir(busy);
30559
30241
  }
@@ -30563,12 +30245,14 @@ async function commitChanges(cwd, input2, dryRun) {
30563
30245
  }
30564
30246
 
30565
30247
  // src/commands/records.ts
30566
- import { readFile as readFile18 } from "node:fs/promises";
30567
- import { join as join23 } from "node:path";
30248
+ import { readFile as readFile17 } from "node:fs/promises";
30249
+ import { join as join21 } from "node:path";
30568
30250
  var runRecordsNew = (options) => runCommand("records", options.format, async () => {
30569
30251
  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"));
30252
+ const docs = [...new Set(options.docs.flatMap((d) => d.split(/[\s,]+/)).filter(Boolean))];
30253
+ if (!docs.length) throw new CommandError("INVALID_VALUE", t2("records.invalidDocs"));
30254
+ const invalid2 = docs.filter((d) => !docIdPattern.test(d));
30255
+ if (invalid2.length) throw new CommandError("INVALID_VALUE", t2("records.notDocIds", { ids: invalid2.join(", ") }));
30572
30256
  const project = await openProject(process.cwd());
30573
30257
  let id;
30574
30258
  do
@@ -30600,7 +30284,7 @@ var runRecordsShow = (ids, options) => runCommand("records", options.format, asy
30600
30284
  const shown = await Promise.all([...new Set(ids)].map(async (id) => {
30601
30285
  const path3 = await findRecord(project.root, id);
30602
30286
  if (!path3) return { id, missing: true };
30603
- const source2 = await readFile18(join23(project.root, ...path3.split("/")), "utf8");
30287
+ const source2 = await readFile17(join21(project.root, ...path3.split("/")), "utf8");
30604
30288
  const log = project.reader.decode(await project.reader.run(["log", "-1", "--diff-filter=A", "--format=%H%x00%aN%x00%aI", "--", path3])).trim();
30605
30289
  const [commit, author, date5] = log.split("\0");
30606
30290
  return { id, path: path3, text: source2, commit: commit ? { commit, author, date: date5 } : null };
@@ -30764,11 +30448,11 @@ function newIssueUrl(title, body2, marker2) {
30764
30448
  }
30765
30449
 
30766
30450
  // 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";
30451
+ import { readFile as readFile18 } from "node:fs/promises";
30452
+ import { dirname as dirname3, join as join22 } from "node:path";
30769
30453
  async function findProjectConfig(from) {
30770
30454
  for (let folder = from; ; folder = dirname3(folder)) {
30771
- const text4 = await readFile19(join24(folder, ".gitifact", "config.json"), "utf8").catch(() => void 0);
30455
+ const text4 = await readFile18(join22(folder, ".gitifact", "config.json"), "utf8").catch(() => void 0);
30772
30456
  if (text4 !== void 0) return { root: folder, text: text4 };
30773
30457
  if (dirname3(folder) === folder) return void 0;
30774
30458
  }
@@ -30830,8 +30514,8 @@ var runFeedback = (options, version2, run) => runCommand("feedback", options.for
30830
30514
  });
30831
30515
 
30832
30516
  // src/commands/update.ts
30833
- import { readFile as readFile20 } from "node:fs/promises";
30834
- import { join as join25 } from "node:path";
30517
+ import { readFile as readFile19 } from "node:fs/promises";
30518
+ import { join as join23 } from "node:path";
30835
30519
  import { isDeepStrictEqual } from "node:util";
30836
30520
 
30837
30521
  // src/adapters/git/agent-docs-commit.ts
@@ -30855,6 +30539,10 @@ function agentDocsGit(root, inherited = process.env) {
30855
30539
  async commitOnly(paths2, message) {
30856
30540
  await git(["commit", "--only", "-m", message, "--", ...paths2]);
30857
30541
  },
30542
+ // A file Git does not know yet (the launcher on its first update) is marked so `--only` can take it.
30543
+ async intendToAdd(paths2) {
30544
+ await git(["add", "--intent-to-add", "--", ...paths2]);
30545
+ },
30858
30546
  async changedPaths(commit) {
30859
30547
  return (await git(["diff-tree", "--no-commit-id", "--name-only", "--no-renames", "-r", "-z", "--root", commit])).toString("utf8").split("\0").filter(Boolean).sort();
30860
30548
  },
@@ -30868,7 +30556,7 @@ function agentDocsGit(root, inherited = process.env) {
30868
30556
  var CONFIG_PATH = ".gitifact/config.json";
30869
30557
  var updateCommitMessage = (version2) => "chore(gitifact): update project to gitifact v" + version2;
30870
30558
  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 } };
30559
+ var notInitialized = { docs: { state: "not-initialized", paths: [] }, project: { state: "not-initialized", cli: null } };
30872
30560
  async function refreshProject(cwd, version2, env, controls) {
30873
30561
  const repo = initRepository(cwd, env);
30874
30562
  let first;
@@ -30884,13 +30572,12 @@ async function refreshProject(cwd, version2, env, controls) {
30884
30572
  const root = first.state.repository.rootPath;
30885
30573
  const parsed2 = needsMigration(config2) ? void 0 : parseManagedConfig(config2);
30886
30574
  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 } };
30575
+ if (parsed2?.cli && isNewerRelease(parsed2.cli, version2)) return { docs: { state: "kept", paths: plan.paths, root }, project: { state: "current", cli: parsed2.cli } };
30889
30576
  let current2 = config2;
30890
30577
  const unchanged = async () => {
30891
30578
  if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== current2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
30892
30579
  };
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 };
30580
+ 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
30581
  if (!parsed2) return { docs, project: { state: "current", cli: null } };
30895
30582
  const settled2 = settleConfig(parsed2, version2, plan.language);
30896
30583
  if (settled2.changed) {
@@ -30898,6 +30585,7 @@ async function refreshProject(cwd, version2, env, controls) {
30898
30585
  await publishConfig(root, next, unchanged, void 0, true);
30899
30586
  current2 = next;
30900
30587
  }
30588
+ await refreshLauncher(root);
30901
30589
  return { docs: { ...docs, root }, project: { state: settled2.changed ? "written" : "current", cli: settled2.config.cli ?? null } };
30902
30590
  }
30903
30591
  function outsideBlock(text4) {
@@ -30932,7 +30620,7 @@ async function commitUpdate(docs, version2, env) {
30932
30620
  if (before === null || before !== outsideBlock(working2)) return commitState("skipped", { paths: [path3], reason: "other-changes" });
30933
30621
  changed.push(path3);
30934
30622
  }
30935
- const workingConfig = await readFile20(join25(docs.root, CONFIG_PATH), "utf8").catch(() => null);
30623
+ const workingConfig = await readFile19(join23(docs.root, CONFIG_PATH), "utf8").catch(() => null);
30936
30624
  const committedConfig = head ? await git.committed(head, CONFIG_PATH) : null;
30937
30625
  if (workingConfig !== null && committedConfig?.replace(/\r\n/g, "\n") !== workingConfig.replace(/\r\n/g, "\n")) {
30938
30626
  if (committedConfig === null) return commitState("skipped", { paths: [CONFIG_PATH], reason: "untracked" });
@@ -30940,10 +30628,14 @@ async function commitUpdate(docs, version2, env) {
30940
30628
  if (before === null || !isDeepStrictEqual(before, withoutSettings(workingConfig))) return commitState("skipped", { paths: [CONFIG_PATH], reason: "other-changes" });
30941
30629
  changed.push(CONFIG_PATH);
30942
30630
  }
30631
+ const workingLauncher = await readFile19(join23(docs.root, ...LAUNCHER_PATH.split("/")), "utf8").catch(() => null);
30632
+ const committedLauncher = head ? await git.committed(head, LAUNCHER_PATH) : null;
30633
+ if (workingLauncher !== null && committedLauncher?.replace(/\r\n/g, "\n") !== workingLauncher.replace(/\r\n/g, "\n")) changed.push(LAUNCHER_PATH);
30943
30634
  if (changed.length === 0) return commitState("nothing");
30944
30635
  const message = updateCommitMessage(version2);
30945
30636
  let failure2;
30946
30637
  try {
30638
+ if (workingLauncher !== null && committedLauncher === null) await git.intendToAdd([LAUNCHER_PATH]);
30947
30639
  await git.commitOnly(changed, message);
30948
30640
  } catch (error62) {
30949
30641
  failure2 = error62;
@@ -30970,7 +30662,8 @@ async function updateCommand(cwd, version2, env = process.env, controls = {}) {
30970
30662
  cliVersion: version2,
30971
30663
  update,
30972
30664
  install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
30973
- agentDocs: { state: docs.state, paths: docs.paths, missing: docs.missing },
30665
+ // missing stays in the v6 result but is always empty since 0.9.0: init no longer adds a CLAUDE.md.
30666
+ agentDocs: { state: docs.state, paths: docs.paths, missing: [] },
30974
30667
  project,
30975
30668
  commit,
30976
30669
  migrationRequired: config2 !== void 0 && needsMigration(config2)
@@ -30986,11 +30679,10 @@ async function runUpdate(options, version2) {
30986
30679
  }
30987
30680
  const lines2 = [t2("update.text.current", { version: dto.cliVersion })];
30988
30681
  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"));
30682
+ if (dto.install) lines2.push(t2("update.text.install", { command: npxUpdate(dto.update.latestVersion) }), t2("update.text.afterInstall"));
30990
30683
  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
30684
  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) }));
30685
+ if (dto.project.cli && isNewerRelease(dto.project.cli, dto.cliVersion)) lines2.push(t2("update.text.projectAhead", { project: dto.project.cli, version: dto.cliVersion }));
30994
30686
  if (dto.migrationRequired) lines2.push(t2("update.text.migrationRequired", { version: dto.cliVersion }));
30995
30687
  const { commit } = dto;
30996
30688
  if (commit.state === "committed") lines2.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
@@ -31033,7 +30725,7 @@ async function runUpdateCheck(format2, version2) {
31033
30725
  t2("update.text.current", { version: dto.cliVersion }),
31034
30726
  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
30727
  ];
31036
- if (state2.status === "available") lines2.push(t2("update.text.install", { command: npmGlobalInstall(state2.latestVersion) }), t2("update.text.afterInstall"));
30728
+ if (state2.status === "available") lines2.push(t2("update.text.install", { command: npxUpdate(state2.latestVersion) }), t2("update.text.afterInstall"));
31037
30729
  process.stdout.write(lines2.join("\n") + "\n");
31038
30730
  }
31039
30731
 
@@ -31054,8 +30746,8 @@ function chooseNotice(running2, project, cache, now, scope = { release: true, pr
31054
30746
  return null;
31055
30747
  }
31056
30748
  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) });
30749
+ if (notice.kind === "project-newer") return t2("notice.projectNewer", { project: notice.project, version: running2 });
30750
+ if (notice.kind === "release") return t2("notice.release", { latest: notice.latest, version: running2, command: npxUpdate(notice.latest) });
31059
30751
  return t2("notice.projectOlder", { project: notice.project, version: running2 });
31060
30752
  }
31061
30753
  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 +30801,17 @@ for (let i = 0; i < languageArgs.length; i++) {
31109
30801
  if (value === "ko" || value === "en") selectedLanguage = value;
31110
30802
  }
31111
30803
  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());
30804
+ var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.9.0").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
31113
30805
  program2.hook("preAction", async (_program, action) => {
31114
30806
  let top = action;
31115
30807
  while (top.parent && top.parent !== program2) top = top.parent;
31116
- if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.8.6", top.name());
30808
+ if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.9.0", top.name());
31117
30809
  });
31118
- program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.8.6"));
30810
+ program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.9.0"));
31119
30811
  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"));
30812
+ 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.0"));
30813
+ 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.0"));
30814
+ 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.0") : options.check ? runUpdateCheck(options.format, "0.9.0") : runUpdate(options, "0.9.0"));
31123
30815
  var listed2 = (command, sorts) => {
31124
30816
  command.allowExcessArguments(false).option("--q <words>", t2("help.listQ")).option("--author <name>", t2("help.listAuthor"));
31125
30817
  if (sorts) command.addOption(new Option("--sort <key>", t2("help.listSort")).choices([...sorts]).default(sorts[0]));
@@ -31129,10 +30821,6 @@ var specs = program2.command("specs").description(t2("help.specs"));
31129
30821
  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
30822
  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
30823
  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
30824
  var records = program2.command("records").description(t2("help.records"));
31137
30825
  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
30826
  records.command("show").description(t2("help.recordsShow")).argument("<ids...>", t2("help.recordsShowId")).addOption(format()).action((ids, o) => runRecordsShow(ids, o));
@@ -31141,7 +30829,7 @@ program2.command("check").description(t2("help.check")).allowExcessArguments(fal
31141
30829
  var changes = program2.command("changes").description(t2("help.changes"));
31142
30830
  changes.command("list").description(t2("help.changesList")).allowExcessArguments(false).addOption(format()).action((o) => runChangesList(o));
31143
30831
  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"));
30832
+ 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.0"));
31145
30833
  var guide = program2.command("guide").description(t2("help.guide"));
31146
30834
  guide.command("list").description(t2("help.guideList")).allowExcessArguments(false).addOption(format()).action((o) => runGuideList(o));
31147
30835
  guide.command("show").description(t2("help.guideShow")).argument("<topic>", t2("help.guideTopic")).allowExcessArguments(false).addOption(format()).action((topic, o) => runGuideShow(topic, o));