gitifact 0.8.5 → 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 (82) hide show
  1. package/README.md +14 -36
  2. package/dist/browser/assets/{HgiRefresh-BSPkIVFZ.js → HgiRefresh-CmdxsKUC.js} +1 -1
  3. package/dist/browser/assets/{Markdown-B1P3lpA0.js → Markdown-BpWtY88p.js} +1 -1
  4. package/dist/browser/assets/{Selector-BG3Gd_OR.js → Selector-Dji19JSe.js} +1 -1
  5. package/dist/browser/assets/Tab-BY4IuGKn.js +4 -0
  6. package/dist/browser/assets/{Table-DjTkmoR7.js → Table-Dm6wz2kf.js} +2 -2
  7. package/dist/browser/assets/{Token-D337a1e3.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-B7ATmxN1.js → contributors._email-C8GGV2IN.js} +1 -1
  14. package/dist/browser/assets/{contributors.index-Cc0QRJwQ.js → contributors.index-BKMSdIFJ.js} +1 -1
  15. package/dist/browser/assets/dashboard.index-COyVOMnD.js +1 -0
  16. package/dist/browser/assets/{document-kGFspd5u.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-DGUBnzjL.js → features._featureId-DT63dqjw.js} +1 -1
  21. package/dist/browser/assets/{features.index-Dgw4kiMG.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-DVsjzCxE.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-C2qxnLKN.js → records._recordId-DP2stdvW.js} +1 -1
  28. package/dist/browser/assets/{records.commits._commit-CSa3sQ8C.js → records.commits._commit-DQlC8mSd.js} +1 -1
  29. package/dist/browser/assets/{records.docs._docId-SIPsGTwH.js → records.docs._docId-C7snDAo3.js} +1 -1
  30. package/dist/browser/assets/{records.index-DoE_3GrP.js → records.index-DRPp4IAb.js} +1 -1
  31. package/dist/browser/assets/{records.working-Cp5ualrr.js → records.working-Qv9R2KgB.js} +1 -1
  32. package/dist/browser/assets/{settings-tVQIx1Uf.js → settings-DDEler3s.js} +1 -1
  33. package/dist/browser/assets/useInfiniteQuery-IoAmrFi4.js +1 -0
  34. package/dist/browser/assets/{useKeyboardHint-D5jGavym.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 +25 -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 +80 -4
  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 +25 -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 +80 -4
  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 +405 -639
  56. package/package.json +3 -3
  57. package/dist/browser/assets/MetadataListItem-BEwPg27H.js +0 -1
  58. package/dist/browser/assets/Tab-RKLZrCiK.js +0 -1
  59. package/dist/browser/assets/about-D06ipt4C.js +0 -3
  60. package/dist/browser/assets/activity-timeline-Bl6a55GR.js +0 -2
  61. package/dist/browser/assets/changelog-DCvEVceR.js +0 -1
  62. package/dist/browser/assets/commit-Cj7mcn9u.js +0 -4
  63. package/dist/browser/assets/contributor-2mkEa2Wo.css +0 -1
  64. package/dist/browser/assets/contributor-D1TZSDq9.js +0 -1
  65. package/dist/browser/assets/contributors-lkpVuCXS.js +0 -1
  66. package/dist/browser/assets/dashboard.index-BNTyn3_V.js +0 -1
  67. package/dist/browser/assets/document-7jVYmB_Z.js +0 -2
  68. package/dist/browser/assets/features-CBeEOrok.js +0 -2
  69. package/dist/browser/assets/getting-started-B_LeBSZE.js +0 -1
  70. package/dist/browser/assets/git-DW-pzDOm.js +0 -1
  71. package/dist/browser/assets/index-IML0_BCy.js +0 -49
  72. package/dist/browser/assets/instructions-2OUMP-vh.css +0 -1
  73. package/dist/browser/assets/instructions-Qhbd8_KG.js +0 -1
  74. package/dist/browser/assets/instructions._instructionId-CnX6OyFZ.js +0 -1
  75. package/dist/browser/assets/instructions.agents-0tKcPahQ.js +0 -1
  76. package/dist/browser/assets/instructions.index-B3v6vtEg.js +0 -1
  77. package/dist/browser/assets/lazyRouteComponent-tJOK5sxC.js +0 -1
  78. package/dist/browser/assets/page-header-AS8jZ1ve.js +0 -248
  79. package/dist/browser/assets/project-instructions-DsWrw-nY.jpg +0 -0
  80. package/dist/browser/assets/useInfiniteQuery-C0rB-9Ci.js +0 -1
  81. package/dist/i18n/en/docs/instructions.md +0 -94
  82. 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) {
@@ -3409,6 +3409,7 @@ var messages_default = {
3409
3409
  "config.schemaFields": "schemaVersion\uACFC baseline \uC124\uC815 \uD544\uB4DC\uB97C \uD655\uC778\uD558\uC138\uC694.",
3410
3410
  "config.cliVersion": "\uC124\uC815\uC758 cli\uB294 0.8.3 \uAC19\uC740 \uBC30\uD3EC \uBC84\uC804\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
3411
3411
  "config.language": "\uC124\uC815\uC758 language\uB294 {languages} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
3412
+ "config.requirementStyle": "\uC124\uC815\uC758 requirementStyle\uC740 {styles} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
3412
3413
  "document.pathTooLong": "\uC704\uD0A4 \uD398\uC774\uC9C0 \uACBD\uB85C \uAE4A\uC774 \uB610\uB294 \uAE38\uC774 \uD55C\uB3C4\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4: {path}",
3413
3414
  "document.fileName": "\uC704\uD0A4 \uD398\uC774\uC9C0 \uD30C\uC77C \uC774\uB984\uC740 \uC18C\uBB38\uC790\xB7\uC22B\uC790\xB7\uD558\uC774\uD508\uACFC .md\uC5EC\uC57C \uD569\uB2C8\uB2E4(\uB8E8\uD2B8\uC758 README.md\xB7ARCHITECTURE.md \uAC19\uC740 \uB300\uBB38\uC790 \uC774\uB984 \uC608\uC678): {path}",
3414
3415
  "document.folderName": "\uC704\uD0A4 \uD3F4\uB354 \uC774\uB984\uC740 \uC18C\uBB38\uC790\xB7\uC22B\uC790\xB7\uD558\uC774\uD508\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: {path}",
@@ -3509,9 +3510,8 @@ var messages_default = {
3509
3510
  "doc.DESIGN_OVERVIEW_REQUIRED": "\uC124\uACC4\uAC00 \uC788\uB294 \uAE30\uB2A5\uC5D0\uB294 design/overview.md\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: {feature}",
3510
3511
  "doc.FILE_TOO_LARGE": "\uBB38\uC11C \uD30C\uC77C\uC774 1MB\uB97C \uB118\uC5B4 \uC77D\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: {path}",
3511
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}",
3512
- "doc.INSTRUCTION_INDEX_REQUIRED": "\uC9C0\uCE68 \uD3F4\uB354\uC5D0 index.md\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: {path}",
3513
- "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}",
3514
- "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}",
3515
3515
  "warn.MISSING_LINK_TARGET": "\uB9C1\uD06C \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {path}: {link}",
3516
3516
  "warn.ASSET_SIZE": "\uC5D0\uC14B\uC774 \uAD8C\uC7A5 \uD06C\uAE30(1MB)\uB97C \uB118\uC2B5\uB2C8\uB2E4: {path} ({bytes}\uBC14\uC774\uD2B8)",
3517
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}",
@@ -3520,6 +3520,9 @@ var messages_default = {
3520
3520
  "warn.DESIGN_OVERVIEW_LARGE": "\uC124\uACC4 overview.md\uC758 \uC808\uC774 {sections}\uAC1C\uC785\uB2C8\uB2E4. \uB367\uBD99\uC774\uAE30 \uC804\uC5D0 \uCD95 \uD30C\uC77C\uB85C \uB098\uB20C\uC9C0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3C\uC73C\uC138\uC694(guide show design\uC758 \uAC1C\uC815): {path}",
3521
3521
  "warn.REQUIREMENT_SCOPE_FORMAT": "\uBC94\uC704\uC640 \uC81C\uC57D\uC740 \uC218\uC6A9 \uC870\uAC74 \uC55E\uC5D0 - \uBAA9\uB85D\uC73C\uB85C\uB9CC \uC501\uB2C8\uB2E4(guide show spec): {path}",
3522
3522
  "warn.REQUIREMENT_CRITERIA_FORMAT": "\uC218\uC6A9 \uC870\uAC74 \uC544\uB798\uC5D0\uB294 \uBC88\uD638 \uD56D\uBAA9\uB9CC \uB461\uB2C8\uB2E4. \uADDC\uCE59\uC740 \uBC94\uC704\uC640 \uC81C\uC57D\uC73C\uB85C \uC62E\uAE30\uC138\uC694(guide show spec): {path}",
3523
+ "warn.REQUIREMENT_FLOW_FORMAT": "\uC720\uC988\uCF00\uC774\uC2A4 \uC694\uAD6C\uC0AC\uD56D\uC5D0\uB294 \uAE30\uBCF8 \uD750\uB984\uC774 \uC788\uC5B4\uC57C \uD558\uACE0, \uC808\uC740 \uBC94\uC704\uC640 \uC81C\uC57D\xB7\uC0AC\uC804 \uC870\uAC74\xB7\uAE30\uBCF8 \uD750\uB984\xB7\uB300\uCCB4 \uD750\uB984\xB7\uC0AC\uD6C4 \uC870\uAC74\xB7\uC218\uC6A9 \uC870\uAC74 \uC21C\uC11C\uC785\uB2C8\uB2E4(guide show spec): {path}",
3524
+ "warn.REQUIREMENT_PATH_FORMAT": "\uC720\uC988\uCF00\uC774\uC2A4 \uC694\uAD6C\uC0AC\uD56D\uC758 \uC218\uC6A9 \uC870\uAC74\uB9C8\uB2E4 \uACBD\uB85C: \uC904\uC744 \uB450\uACE0, \uAE30\uBCF8 \uD750\uB984\uACFC \uC774 \uC694\uAD6C\uC0AC\uD56D\uC758 \uB300\uCCB4 \uD750\uB984(A1\u2026)\uB9CC \uAC00\uB9AC\uD0B5\uB2C8\uB2E4(guide show spec): {path}",
3525
+ "warn.REQUIREMENT_STYLE_MISSING": "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 \uC694\uAD6C\uC0AC\uD56D\uC744 \uC720\uC988\uCF00\uC774\uC2A4\uB85C \uC501\uB2C8\uB2E4(config\uC758 requirementStyle). \uD504\uB860\uD2B8\uB9E4\uD130\uC5D0 style: usecase\uB97C, \uAE30\uBCF8 \uD615\uC2DD\uC73C\uB85C \uB458 \uC694\uAD6C\uC0AC\uD56D\uC774\uBA74 style: default\uB97C \uC801\uC73C\uC138\uC694(guide show spec): {path}",
3523
3526
  "config.migrationRequired": "\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 0.7 \uC800\uC7A5 \uADDC\uC57D(schemaVersion 2)\uC785\uB2C8\uB2E4. 0.8.0 CLI\uB294 \uC61B \uD615\uC2DD\uC744 \uC77D\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. gitifact guide show migrate\uC758 \uC808\uCC28\uB85C \uBB38\uC11C\uB97C \uC0C8 \uAD6C\uC870\uB85C \uC62E\uAE30\uC138\uC694."
3524
3527
  };
3525
3528
 
@@ -3531,6 +3534,7 @@ var messages_default2 = {
3531
3534
  "config.schemaFields": "Check the schemaVersion and baseline fields.",
3532
3535
  "config.cliVersion": "The config field cli must be a release version such as 0.8.3.",
3533
3536
  "config.language": "The config field language must be one of {languages}.",
3537
+ "config.requirementStyle": "The config field requirementStyle must be one of {styles}.",
3534
3538
  "document.pathTooLong": "Wiki page path exceeds the depth or length limit: {path}",
3535
3539
  "document.fileName": "Wiki filenames must use lowercase letters, digits, hyphens and .md; uppercase root names such as README.md and ARCHITECTURE.md are allowed: {path}",
3536
3540
  "document.folderName": "Wiki folder names must use lowercase letters, digits and hyphens: {path}",
@@ -3631,9 +3635,8 @@ var messages_default2 = {
3631
3635
  "doc.DESIGN_OVERVIEW_REQUIRED": "A feature with a design needs design/overview.md: {feature}",
3632
3636
  "doc.FILE_TOO_LARGE": "The document file exceeds 1 MB and was not read: {path}",
3633
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}",
3634
- "doc.INSTRUCTION_INDEX_REQUIRED": "An instruction folder needs an index.md: {path}",
3635
- "doc.WIKI_REMOVED": "The wiki became project instructions in 0.8.0. Move this page to .gitifact/instructions/ (gitifact guide show migrate): {path}",
3636
- "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}",
3637
3640
  "warn.MISSING_LINK_TARGET": "The link target does not exist: {path}: {link}",
3638
3641
  "warn.ASSET_SIZE": "The asset is over the recommended size (1 MB): {path} ({bytes} bytes)",
3639
3642
  "warn.ASSET_EXTENSION": "The browser does not preview this extension and offers it only as a download: {path}",
@@ -3642,6 +3645,9 @@ var messages_default2 = {
3642
3645
  "warn.DESIGN_OVERVIEW_LARGE": "The design overview.md has {sections} sections. Before adding to it, ask the user whether to split it into axis files (revising, in guide show design): {path}",
3643
3646
  "warn.REQUIREMENT_SCOPE_FORMAT": "Scope and constraints come before the acceptance criteria, as - items only (guide show spec): {path}",
3644
3647
  "warn.REQUIREMENT_CRITERIA_FORMAT": "The acceptance criteria hold numbered items only; move rules to scope and constraints (guide show spec): {path}",
3648
+ "warn.REQUIREMENT_FLOW_FORMAT": "A use-case requirement has a basic flow, and its sections run scope and constraints, preconditions, basic flow, alternative flows, postconditions, acceptance criteria (guide show spec): {path}",
3649
+ "warn.REQUIREMENT_PATH_FORMAT": "Each acceptance criterion of a use-case requirement has a Path: line naming only the basic flow and this requirement's alternative flows (A1\u2026) (guide show spec): {path}",
3650
+ "warn.REQUIREMENT_STYLE_MISSING": "This project writes requirements as use cases (config requirementStyle). Add style: usecase to the frontmatter, or style: default for a requirement that keeps the default shape (guide show spec): {path}",
3645
3651
  "config.migrationRequired": "This project uses the 0.7 storage convention (schemaVersion 2). The 0.8.0 CLI does not read the old format. Move the documents to the new structure by following gitifact guide show migrate."
3646
3652
  };
3647
3653
 
@@ -3682,6 +3688,29 @@ async function readRepositoryStatus(reader, services) {
3682
3688
  };
3683
3689
  }
3684
3690
 
3691
+ // ../../packages/core/dist/domain/document.js
3692
+ var DOC_ID_PREFIX = { feature: "S", requirement: "R", design: "D", wiki: "W", instruction: "I" };
3693
+ var RECORD_ID_PREFIX = "DR";
3694
+ var token = "[a-z2-7]{10}";
3695
+ var docIdPattern = new RegExp(`^[SRDWI]-${token}$`);
3696
+ var recordIdPattern = new RegExp(`^DR-${token}$`);
3697
+ var reasonIdPattern = new RegExp(`^H-${token}$`);
3698
+ var idPatternOf = (kind2) => new RegExp(`^${DOC_ID_PREFIX[kind2]}-${token}$`);
3699
+ var kindOfId = (id) => Object.entries(DOC_ID_PREFIX).find(([, prefix]) => id.startsWith(prefix + "-"))?.[0];
3700
+ var requirementStyles = ["default", "usecase"];
3701
+ var docWarning = (code, path3, values = {}) => ({ code, path: path3, message: t(`warn.${code}`, { path: path3, ...values }) });
3702
+ var docProblem = (code, path3, values = {}) => ({ code, path: path3, message: t(`doc.${code}`, { path: path3, ...values }) });
3703
+ var DocumentError = class extends Error {
3704
+ code;
3705
+ path;
3706
+ constructor(code, path3, message) {
3707
+ super(message);
3708
+ this.code = code;
3709
+ this.path = path3;
3710
+ this.name = "DocumentError";
3711
+ }
3712
+ };
3713
+
3685
3714
  // ../../packages/core/dist/formats/spec-project.js
3686
3715
  var InitError = class extends Error {
3687
3716
  code;
@@ -3746,6 +3775,11 @@ function parseManagedConfig(text4) {
3746
3775
  throw new InitError("INVALID_CONFIG", t("config.language", { languages: PROJECT_LANGUAGES.join(", ") }));
3747
3776
  config2.language = value.language;
3748
3777
  }
3778
+ if ("requirementStyle" in value) {
3779
+ if (!requirementStyles.includes(value.requirementStyle))
3780
+ throw new InitError("INVALID_CONFIG", t("config.requirementStyle", { styles: requirementStyles.join(", ") }));
3781
+ config2.requirementStyle = value.requirementStyle;
3782
+ }
3749
3783
  return config2;
3750
3784
  }
3751
3785
  function formatManagedConfig(config2, previous) {
@@ -3753,7 +3787,7 @@ function formatManagedConfig(config2, previous) {
3753
3787
  if (previous !== void 0) {
3754
3788
  const value = JSON.parse(previous);
3755
3789
  if (object(value)) {
3756
- const { schemaVersion: _s, baseline: _b, cli: _c, language: _l, ...rest } = value;
3790
+ const { schemaVersion: _s, baseline: _b, cli: _c, language: _l, requirementStyle: _r, ...rest } = value;
3757
3791
  unknown2 = rest;
3758
3792
  }
3759
3793
  }
@@ -3761,7 +3795,8 @@ function formatManagedConfig(config2, previous) {
3761
3795
  schemaVersion: config2.schemaVersion,
3762
3796
  baseline: config2.baseline,
3763
3797
  ...config2.cli === void 0 ? {} : { cli: config2.cli },
3764
- ...config2.language === void 0 ? {} : { language: config2.language }
3798
+ ...config2.language === void 0 ? {} : { language: config2.language },
3799
+ ...config2.requirementStyle === void 0 ? {} : { requirementStyle: config2.requirementStyle }
3765
3800
  };
3766
3801
  return JSON.stringify({ ...known, ...unknown2 }, null, 2) + "\n";
3767
3802
  }
@@ -3815,9 +3850,9 @@ function extractLinks(body2) {
3815
3850
  fence = void 0;
3816
3851
  continue;
3817
3852
  }
3818
- const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
3819
- if (open6) {
3820
- 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 };
3821
3856
  continue;
3822
3857
  }
3823
3858
  for (const m of line2.replace(/`[^`]*`/g, "").matchAll(/!?\[[^\]]*\]\((?:<([^>]+)>|([^)\s]+))(?:\s+"[^"]*")?\)/g))
@@ -3834,7 +3869,7 @@ var WIKI_DIR = ".gitifact/wiki";
3834
3869
  var WIKI_HISTORY_PATH = WIKI_DIR + "/history.jsonl";
3835
3870
  var WIKI_ENTRY = "README.md";
3836
3871
  var WIKI_ENTRY_PATH = WIKI_DIR + "/" + WIKI_ENTRY;
3837
- var token = "[a-z2-7]{10}";
3872
+ var token2 = "[a-z2-7]{10}";
3838
3873
  var STORE_DIR = ".gitifact";
3839
3874
  var marker = "gitifact";
3840
3875
  var recordPathPattern = /^(?:\.gitifact\/spec\/[^/]+\/(?:requirements\.md|design\.md|history\.jsonl)|\.gitifact\/wiki\/(?:(?:[^/]+\/)*[^/]+\.md|history\.jsonl))$/;
@@ -3853,7 +3888,7 @@ function validateDocumentRelativePath(relative) {
3853
3888
  if (!nameToken.test(part) || part.length > 80)
3854
3889
  fail(t("document.folderName", { path: relative }));
3855
3890
  }
3856
- var reqId = new RegExp(`^R-${token}$`);
3891
+ var reqId = new RegExp(`^R-${token2}$`);
3857
3892
  var fail = (message) => {
3858
3893
  throw new StoreError(message);
3859
3894
  };
@@ -3867,7 +3902,7 @@ var scalar = (value) => {
3867
3902
  return value;
3868
3903
  };
3869
3904
  function parseFrontmatter(lines2, kind2, legacyIds) {
3870
- const legacy = new RegExp(`^<!-- ${marker}-(?:${kind2 === "wiki" ? "product|guide" : kind2}): (${legacyIds}-${token}) -->$`).exec(lines2[0] ?? "");
3905
+ const legacy = new RegExp(`^<!-- ${marker}-(?:${kind2 === "wiki" ? "product|guide" : kind2}): (${legacyIds}-${token2}) -->$`).exec(lines2[0] ?? "");
3871
3906
  if (legacy) {
3872
3907
  lines2.shift();
3873
3908
  return { id: legacy[1] };
@@ -3915,7 +3950,7 @@ function parseFrontmatter(lines2, kind2, legacyIds) {
3915
3950
  fields2.set("sources", sources3);
3916
3951
  }
3917
3952
  const id = fields2.get("id");
3918
- if (typeof id !== "string" || !new RegExp(`^(?:${legacyIds})-${token}$`).test(id))
3953
+ if (typeof id !== "string" || !new RegExp(`^(?:${legacyIds})-${token2}$`).test(id))
3919
3954
  fail(t("frontmatter.id", { kind: kind2 }));
3920
3955
  for (const key of fields2.keys())
3921
3956
  if (key !== "id" && !(key === "sources" && kind2 === "design"))
@@ -3965,9 +4000,9 @@ function parseDesign(source2, specId) {
3965
4000
  fence = void 0;
3966
4001
  return line2;
3967
4002
  }
3968
- const open6 = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
3969
- if (open6) {
3970
- 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 };
3971
4006
  return line2;
3972
4007
  }
3973
4008
  if (/^#\s/.test(line2))
@@ -4010,9 +4045,9 @@ function parseDocument(path3, source2) {
4010
4045
  fence = void 0;
4011
4046
  continue;
4012
4047
  }
4013
- const open6 = /^ {0,3}(`{3,}|~{3,})(.*)$/.exec(line2);
4014
- if (open6) {
4015
- 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 };
4016
4051
  continue;
4017
4052
  }
4018
4053
  if (/^#\s/.test(line2))
@@ -4104,7 +4139,7 @@ function parseSpec(path3, source2, history = "", designSource) {
4104
4139
  for (let i = 0; i < lines2.length; i++) {
4105
4140
  const line2 = lines2[i];
4106
4141
  if (waitingId) {
4107
- const match = new RegExp(`^<!-- ${marker}-req: (R-${token}) -->$`).exec(line2);
4142
+ const match = new RegExp(`^<!-- ${marker}-req: (R-${token2}) -->$`).exec(line2);
4108
4143
  if (!match || !current2)
4109
4144
  fail(t("spec.requirementIdRequired", { path: path3 }));
4110
4145
  current2.id = match[1];
@@ -4176,7 +4211,7 @@ function parseHistory(source2, scope = "spec") {
4176
4211
  if (!value || typeof value !== "object" || Array.isArray(value))
4177
4212
  fail(t("history.invalidRecord"));
4178
4213
  const r = value;
4179
- if (!shapes.includes(Object.keys(r).sort().join(",")) || typeof r.id !== "string" || !new RegExp(`^H-${token}$`).test(r.id) || ids.has(r.id) || typeof r.reason !== "string" || !r.reason.trim() || !Array.isArray(r.requirements) || !r.requirements.length && !Array.isArray(r.designs) && !Array.isArray(r.documents) || r.documents !== void 0 && (r.requirements.length || !Array.isArray(r.documents) || !r.documents.length || r.documents.some((id) => typeof id !== "string" || !/^W-[a-z2-7]{10}$/.test(id)) || new Set(r.documents).size !== r.documents.length) || r.designs !== void 0 && (!Array.isArray(r.designs) || !r.designs.length || r.designs.some((id) => typeof id !== "string" || !/^S-[a-z2-7]{10}$/.test(id)) || new Set(r.designs).size !== r.designs.length) || r.requirements.some((id) => typeof id !== "string" || !reqId.test(id)) || new Set(r.requirements).size !== r.requirements.length)
4214
+ if (!shapes.includes(Object.keys(r).sort().join(",")) || typeof r.id !== "string" || !new RegExp(`^H-${token2}$`).test(r.id) || ids.has(r.id) || typeof r.reason !== "string" || !r.reason.trim() || !Array.isArray(r.requirements) || !r.requirements.length && !Array.isArray(r.designs) && !Array.isArray(r.documents) || r.documents !== void 0 && (r.requirements.length || !Array.isArray(r.documents) || !r.documents.length || r.documents.some((id) => typeof id !== "string" || !/^W-[a-z2-7]{10}$/.test(id)) || new Set(r.documents).size !== r.documents.length) || r.designs !== void 0 && (!Array.isArray(r.designs) || !r.designs.length || r.designs.some((id) => typeof id !== "string" || !/^S-[a-z2-7]{10}$/.test(id)) || new Set(r.designs).size !== r.designs.length) || r.requirements.some((id) => typeof id !== "string" || !reqId.test(id)) || new Set(r.requirements).size !== r.requirements.length)
4180
4215
  fail(t("history.invalidFields"));
4181
4216
  ids.add(r.id);
4182
4217
  records2.push({ id: r.id, requirements: r.requirements, ...r.designs === void 0 ? {} : { designs: r.designs }, ...r.documents === void 0 ? {} : { documents: r.documents }, reason: r.reason });
@@ -4295,28 +4330,6 @@ function compareStoreBundles(a, b) {
4295
4330
  return { specChanges: specs2.specChanges, changes: [...specs2.changes, ...compareWikis(a.wiki, b.wiki)] };
4296
4331
  }
4297
4332
 
4298
- // ../../packages/core/dist/domain/document.js
4299
- var DOC_ID_PREFIX = { feature: "S", requirement: "R", design: "D", wiki: "W", instruction: "I" };
4300
- var RECORD_ID_PREFIX = "DR";
4301
- var token2 = "[a-z2-7]{10}";
4302
- var docIdPattern = new RegExp(`^[SRDWI]-${token2}$`);
4303
- var recordIdPattern = new RegExp(`^DR-${token2}$`);
4304
- var reasonIdPattern = new RegExp(`^H-${token2}$`);
4305
- var idPatternOf = (kind2) => new RegExp(`^${DOC_ID_PREFIX[kind2]}-${token2}$`);
4306
- var kindOfId = (id) => Object.entries(DOC_ID_PREFIX).find(([, prefix]) => id.startsWith(prefix + "-"))?.[0];
4307
- var docWarning = (code, path3, values = {}) => ({ code, path: path3, message: t(`warn.${code}`, { path: path3, ...values }) });
4308
- var docProblem = (code, path3, values = {}) => ({ code, path: path3, message: t(`doc.${code}`, { path: path3, ...values }) });
4309
- var DocumentError = class extends Error {
4310
- code;
4311
- path;
4312
- constructor(code, path3, message) {
4313
- super(message);
4314
- this.code = code;
4315
- this.path = path3;
4316
- this.name = "DocumentError";
4317
- }
4318
- };
4319
-
4320
4333
  // ../../packages/core/dist/domain/record.js
4321
4334
  var recordSectionKeys = ["context", "decision", "alternatives"];
4322
4335
  var RECORD_REQUIRED_SECTIONS = ["context", "decision"];
@@ -4444,14 +4457,14 @@ function sections(raw, path3) {
4444
4457
  found.at(-1)?.lines.push(line2);
4445
4458
  continue;
4446
4459
  }
4447
- const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
4448
- if (open6)
4449
- 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 };
4450
4463
  else if (/^#\s/.test(line2))
4451
4464
  throw new DocumentError("BODY_HEADING", path3, t("doc.BODY_HEADING", { path: path3 }));
4452
4465
  else if (/<!-- gitifact-/.test(line2))
4453
4466
  throw new DocumentError("BODY_MARKER", path3, t("doc.BODY_MARKER", { path: path3 }));
4454
- const heading = !open6 && /^##\s+(.+?)\s*#*\s*$/.exec(line2);
4467
+ const heading = !open5 && /^##\s+(.+?)\s*#*\s*$/.exec(line2);
4455
4468
  if (heading) {
4456
4469
  const key = sectionKeyOf(heading[1]);
4457
4470
  if (!key || found.some((s) => s.key === key))
@@ -4535,6 +4548,8 @@ var HISTORY_PATH = ".gitifact/history.jsonl";
4535
4548
  var name = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4536
4549
  var rootUpperName2 = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*\.md$/;
4537
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];
4538
4553
  function classifyDocPath(path3) {
4539
4554
  const unsupported = () => new DocumentError("PATH_UNSUPPORTED", path3, t("doc.PATH_UNSUPPORTED", { path: path3 }));
4540
4555
  if (path3 === HISTORY_PATH)
@@ -4583,7 +4598,7 @@ function classifyDocPath(path3) {
4583
4598
  }
4584
4599
  var KEYS2 = {
4585
4600
  feature: ["id", "title", "description", "draft"],
4586
- requirement: ["id", "title", "description", "order", "draft"],
4601
+ requirement: ["id", "title", "description", "order", "style", "draft"],
4587
4602
  design: ["id", "title", "description", "order", "requirements", "sources", "draft"],
4588
4603
  wiki: ["id", "title", "description", "draft"],
4589
4604
  instruction: ["id", "title", "description", "draft"]
@@ -4635,9 +4650,9 @@ function body(raw, path3) {
4635
4650
  fence = void 0;
4636
4651
  continue;
4637
4652
  }
4638
- const open6 = /^ {0,3}(`{3,}|~{3,})/.exec(line2);
4639
- if (open6) {
4640
- 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 };
4641
4656
  continue;
4642
4657
  }
4643
4658
  if (/^#\s/.test(line2))
@@ -4689,33 +4704,25 @@ function parseDocumentFile(path3, source2) {
4689
4704
  return { kind: "wiki", ...common };
4690
4705
  if (where.kind === "instruction")
4691
4706
  return { kind: "instruction", name: where.name, ...common };
4692
- if (where.kind === "requirement")
4693
- return { kind: "requirement", feature: where.feature, order: order(), ...common };
4707
+ if (where.kind === "requirement") {
4708
+ const style2 = fields2.get("style");
4709
+ if (style2 && (style2.type !== "scalar" || !requirementStyles.includes(style2.value)))
4710
+ throw new DocumentError("FRONTMATTER_VALUE", path3, t("doc.FRONTMATTER_VALUE", { path: path3, value: "style" }));
4711
+ return { kind: "requirement", feature: where.feature, order: order(), ...style2 ? { style: style2.value } : {}, ...common };
4712
+ }
4694
4713
  const refs = fields2.get("requirements");
4695
4714
  if (refs && (refs.type !== "list" || refs.items.some((r) => !idPatternOf("requirement").test(r)) || new Set(refs.items).size !== refs.items.length)) {
4696
4715
  throw new DocumentError("FRONTMATTER_VALUE", path3, t("doc.FRONTMATTER_VALUE", { path: path3, value: "requirements" }));
4697
4716
  }
4698
4717
  return { kind: "design", feature: where.feature, order: order(), requirements: refs?.type === "list" ? refs.items : [], sources: sources(fields2, path3), ...common };
4699
4718
  }
4700
- var isInstructionReference = (path3) => path3.endsWith(".md");
4701
- function parseInstructionFile(path3, source2) {
4702
- if (source2.includes("\0") || /\r(?!\n)/.test(source2) || source2.charCodeAt(0) === 65279)
4703
- throw new DocumentError("INVALID_CHARACTERS", path3, t("doc.INVALID_CHARACTERS", { path: path3 }));
4704
- const { fields: fields2 } = parseFrontmatterBlock(source2.replace(/\r\n/g, "\n"), path3);
4705
- for (const key of fields2.keys())
4706
- if (key !== "title" && key !== "description")
4707
- throw new DocumentError("FRONTMATTER_UNKNOWN_KEY", path3, t("doc.FRONTMATTER_UNKNOWN_KEY", { path: path3, key }));
4708
- for (const key of ["title", "description"])
4709
- if (!fields2.has(key))
4710
- throw new DocumentError("FRONTMATTER_MISSING_KEY", path3, t("doc.FRONTMATTER_MISSING_KEY", { path: path3, key }));
4711
- return { title: text(fields2, "title", path3, 200), description: text(fields2, "description", path3, 300) };
4712
- }
4713
4719
  function renderDocumentFile(doc) {
4714
4720
  const front = renderFrontmatterBlock([
4715
4721
  ["id", doc.id],
4716
4722
  ["title", doc.title],
4717
4723
  ["description", doc.description],
4718
4724
  ["order", doc.kind === "requirement" || doc.kind === "design" ? doc.order : void 0],
4725
+ ["style", doc.kind === "requirement" ? doc.style : void 0],
4719
4726
  ["requirements", doc.kind === "design" ? doc.requirements : void 0],
4720
4727
  ["sources", doc.kind === "design" ? doc.sources.map((s) => ({ ...s })) : void 0],
4721
4728
  ["draft", doc.draft ? true : void 0]
@@ -4753,17 +4760,18 @@ function checkDocuments(files) {
4753
4760
  const report = (code, path3, values = {}) => problems.push(docProblem(code, path3, values));
4754
4761
  const features = /* @__PURE__ */ new Set();
4755
4762
  const designFolders = /* @__PURE__ */ new Map();
4756
- const instructionFolders = /* @__PURE__ */ new Map();
4757
- const specLinks = (path3, body2) => {
4758
- for (const link3 of extractLinks(body2))
4759
- if (resolveLink(path3, link3)?.startsWith(SPEC_ROOT + "/"))
4760
- report("INSTRUCTION_SPEC_LINK", path3, { link: link3 });
4761
- };
4763
+ const instructionFolders = /* @__PURE__ */ new Set();
4762
4764
  for (const [path3, source2] of [...files].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
4763
4765
  if (isWikiPage(path3)) {
4764
4766
  report("WIKI_REMOVED", path3);
4765
4767
  continue;
4766
4768
  }
4769
+ if (isInstructionPath(path3)) {
4770
+ const folder = instructionFolderOf(path3);
4771
+ if (folder)
4772
+ instructionFolders.add(folder);
4773
+ continue;
4774
+ }
4767
4775
  try {
4768
4776
  const where = classifyDocPath(path3);
4769
4777
  if (where.type === "ignored")
@@ -4776,16 +4784,8 @@ function checkDocuments(files) {
4776
4784
  records2.push(parseRecordFile(path3, source2));
4777
4785
  continue;
4778
4786
  }
4779
- if (where.type === "instruction-file") {
4780
- instructionFolders.set(where.name, instructionFolders.get(where.name) === true);
4781
- if (isInstructionReference(path3)) {
4782
- specLinks(path3, source2);
4783
- parseInstructionFile(path3, source2);
4784
- }
4787
+ if (where.type !== "doc")
4785
4788
  continue;
4786
- }
4787
- if (where.kind === "instruction")
4788
- instructionFolders.set(where.name, true);
4789
4789
  if (where.kind === "requirement" || where.kind === "design")
4790
4790
  features.add(where.feature);
4791
4791
  if (where.kind === "design")
@@ -4801,9 +4801,6 @@ function checkDocuments(files) {
4801
4801
  for (const doc of documents)
4802
4802
  if (doc.draft)
4803
4803
  report("DOC_DRAFT", doc.path);
4804
- for (const doc of documents)
4805
- if (doc.kind === "instruction")
4806
- specLinks(doc.path, doc.body);
4807
4804
  for (const doc of documents) {
4808
4805
  const seen = byId.get(doc.id);
4809
4806
  if (seen)
@@ -4851,9 +4848,8 @@ function checkDocuments(files) {
4851
4848
  for (const [feature2, overview] of designFolders)
4852
4849
  if (!overview)
4853
4850
  report("DESIGN_OVERVIEW_REQUIRED", `.gitifact/spec/${feature2}/design/overview.md`, { feature: feature2 });
4854
- for (const [name2, hasFile] of instructionFolders)
4855
- if (!hasFile)
4856
- report("INSTRUCTION_INDEX_REQUIRED", `${INSTRUCTIONS_ROOT}/${name2}/${INSTRUCTION_FILE}`);
4851
+ for (const folder of [...instructionFolders].sort())
4852
+ report("INSTRUCTION_MOVED", folder);
4857
4853
  return { documents, records: records2, problems };
4858
4854
  }
4859
4855
 
@@ -4869,12 +4865,11 @@ function arrangeDocuments(documents) {
4869
4865
  return f;
4870
4866
  };
4871
4867
  const wiki = [];
4872
- const instructions2 = [];
4873
4868
  for (const doc of documents) {
4874
4869
  if (doc.kind === "wiki")
4875
4870
  wiki.push(doc);
4876
4871
  else if (doc.kind === "instruction")
4877
- instructions2.push(doc);
4872
+ continue;
4878
4873
  else if (doc.kind === "feature")
4879
4874
  folder(doc.feature).index = doc;
4880
4875
  else if (doc.kind === "requirement")
@@ -4891,7 +4886,7 @@ function arrangeDocuments(documents) {
4891
4886
  }
4892
4887
  features.push({ index: f.index, requirements: f.requirements.sort(byOrder), designs: f.designs.sort(byOrder) });
4893
4888
  }
4894
- return { features, wiki: wiki.sort(byPath), instructions: instructions2.sort(byPath), orphans };
4889
+ return { features, wiki: wiki.sort(byPath), orphans };
4895
4890
  }
4896
4891
 
4897
4892
  // ../../packages/core/dist/use-cases/compare-documents.js
@@ -4954,7 +4949,7 @@ function documentWarnings(documents, assets, existing) {
4954
4949
  referenced.add(target);
4955
4950
  continue;
4956
4951
  }
4957
- if ((!target.startsWith(".gitifact/") || target.startsWith(INSTRUCTIONS_ROOT + "/")) && existing.has(target))
4952
+ if (!target.startsWith(".gitifact/") && existing.has(target))
4958
4953
  continue;
4959
4954
  warnings.push(docWarning("MISSING_LINK_TARGET", from, { link: link3, target }));
4960
4955
  }
@@ -4982,9 +4977,21 @@ function documentWarnings(documents, assets, existing) {
4982
4977
  var lines = (body2) => body2.replace(/\r\n/g, "\n").split("\n");
4983
4978
  var scopeHeading = /^###\s+(?:범위와 제약|scope and constraints)\s*$/i;
4984
4979
  var acceptanceHeading = /^###\s+(?:수용 조건|acceptance criteria)\s*$/i;
4985
- function requirementFormatWarnings(doc) {
4980
+ var useCaseSections = [
4981
+ ["scope", scopeHeading],
4982
+ ["preconditions", /^###\s+(?:사전 조건|preconditions)\s*$/i],
4983
+ ["basic", /^###\s+(?:기본 흐름|basic flow)\s*$/i],
4984
+ ["alternatives", /^###\s+(?:대체 흐름|alternative flows?)\s*$/i],
4985
+ ["postconditions", /^###\s+(?:사후 조건|postconditions)\s*$/i],
4986
+ ["acceptance", acceptanceHeading]
4987
+ ];
4988
+ var pathLine = /^\s*(?:경로|path)\s*:\s*(.*)$/i;
4989
+ var basicFlowName = /^(?:기본 흐름|basic flow)$/i;
4990
+ var alternativeId = /^-\s+(?:\*\*)?(A\d+)\b/;
4991
+ function requirementFormatWarnings(doc, projectStyle) {
4986
4992
  if (doc.kind !== "requirement")
4987
4993
  return [];
4994
+ const missing2 = projectStyle === "usecase" && !doc.style ? [docWarning("REQUIREMENT_STYLE_MISSING", doc.path)] : [];
4988
4995
  const body2 = lines(doc.body);
4989
4996
  const scope = body2.findIndex((line2) => scopeHeading.test(line2.trim()));
4990
4997
  const acceptance = body2.findIndex((line2) => acceptanceHeading.test(line2.trim()));
@@ -4999,6 +5006,38 @@ function requirementFormatWarnings(doc) {
4999
5006
  warnings.push(docWarning("REQUIREMENT_SCOPE_FORMAT", doc.path));
5000
5007
  if (acceptance >= 0 && (outside(sectionOf(acceptance), /^\d+\.\s/) || body2.slice(acceptance + 1).some((line2) => /^#{1,3}\s/.test(line2) && !scopeHeading.test(line2.trim()))))
5001
5008
  warnings.push(docWarning("REQUIREMENT_CRITERIA_FORMAT", doc.path));
5009
+ if (doc.style === "usecase")
5010
+ warnings.push(...useCaseWarnings(doc.path, body2, sectionOf, acceptance));
5011
+ return [...missing2, ...warnings];
5012
+ }
5013
+ function useCaseWarnings(path3, body2, sectionOf, acceptance) {
5014
+ const warnings = [];
5015
+ const found = useCaseSections.map(([key, heading]) => ({ key, at: body2.findIndex((line2) => heading.test(line2.trim())) })).filter((s) => s.at >= 0);
5016
+ const ordered = found.every((s, i) => i === 0 || s.at > found[i - 1].at);
5017
+ if (!found.some((s) => s.key === "basic") || !ordered)
5018
+ warnings.push(docWarning("REQUIREMENT_FLOW_FORMAT", path3));
5019
+ if (acceptance < 0)
5020
+ return warnings;
5021
+ const alternatives = found.find((s) => s.key === "alternatives");
5022
+ const defined = new Set(alternatives ? sectionOf(alternatives.at).flatMap((line2) => {
5023
+ const m = alternativeId.exec(line2);
5024
+ return m ? [m[1]] : [];
5025
+ }) : []);
5026
+ const items2 = [];
5027
+ for (const line2 of sectionOf(acceptance)) {
5028
+ if (/^\d+\.\s/.test(line2))
5029
+ items2.push([line2.replace(/^\d+\.\s+/, "")]);
5030
+ else if (items2.length && /^\s+\S/.test(line2))
5031
+ items2[items2.length - 1].push(line2);
5032
+ }
5033
+ const named = (flow) => basicFlowName.test(flow) || defined.has(flow);
5034
+ const unpathed = items2.some((item) => {
5035
+ const match = item.map((line2) => pathLine.exec(line2)).find(Boolean);
5036
+ const flows = match ? match[1].split(/[,,、]/).map((f) => f.trim()).filter(Boolean) : [];
5037
+ return !flows.length || !flows.every(named);
5038
+ });
5039
+ if (unpathed)
5040
+ warnings.push(docWarning("REQUIREMENT_PATH_FORMAT", path3));
5002
5041
  return warnings;
5003
5042
  }
5004
5043
 
@@ -25092,7 +25131,8 @@ var state = external_exports.enum(["committed", "added", "modified", "deleted"])
25092
25131
  var standing = { state, previousPath: external_exports.string().optional() };
25093
25132
  var contributor = external_exports.strictObject({ email: external_exports.string(), name: external_exports.string(), commits: external_exports.number().int().nonnegative(), latest: external_exports.string() });
25094
25133
  var source = external_exports.strictObject({ id: external_exports.string().optional(), title: external_exports.string().optional(), path: external_exports.string().optional(), url: external_exports.string().optional(), note: external_exports.string().optional() });
25095
- var requirement = external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string(), order: external_exports.number().int().nonnegative(), body: external_exports.string(), ...standing });
25134
+ var style = external_exports.enum(["default", "usecase"]);
25135
+ var requirement = external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string(), order: external_exports.number().int().nonnegative(), style: style.optional(), body: external_exports.string(), ...standing });
25096
25136
  var design = external_exports.strictObject({
25097
25137
  id: external_exports.string(),
25098
25138
  path: external_exports.string(),
@@ -25117,21 +25157,6 @@ var feature = external_exports.strictObject({
25117
25157
  contributors: external_exports.array(contributor),
25118
25158
  updatedAt: external_exports.string().nullable()
25119
25159
  });
25120
- 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() });
25121
- var instruction = external_exports.strictObject({
25122
- id: external_exports.string(),
25123
- name: external_exports.string(),
25124
- path: external_exports.string(),
25125
- title: external_exports.string(),
25126
- description: external_exports.string(),
25127
- body: external_exports.string(),
25128
- ...standing,
25129
- // Paths relative to the instruction folder, index.md left out; `limited` when the folder holds more than the list carries.
25130
- files: external_exports.array(instructionFile),
25131
- filesLimited: external_exports.boolean(),
25132
- updatedAt: external_exports.string().nullable()
25133
- });
25134
- var agents = external_exports.strictObject({ path: external_exports.string(), body: external_exports.string(), updatedAt: external_exports.string().nullable() });
25135
25160
  var problem = external_exports.strictObject({ code: external_exports.string(), path: external_exports.string(), message: external_exports.string() });
25136
25161
  var oid2 = external_exports.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
25137
25162
  var indexFeature = external_exports.strictObject({
@@ -25142,17 +25167,16 @@ var indexFeature = external_exports.strictObject({
25142
25167
  requirements: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string(), description: external_exports.string() })),
25143
25168
  designs: external_exports.array(external_exports.strictObject({ id: external_exports.string(), path: external_exports.string(), title: external_exports.string() }))
25144
25169
  });
25145
- var indexInstruction = external_exports.strictObject({ id: external_exports.string(), name: external_exports.string(), path: external_exports.string(), title: external_exports.string() });
25146
- var browserCheckoutV1 = external_exports.strictObject({
25170
+ var browserCheckoutV2 = external_exports.strictObject({
25147
25171
  contract: external_exports.literal("browser-checkout"),
25148
- version: external_exports.literal(1),
25172
+ version: external_exports.literal(2),
25149
25173
  sessionId: external_exports.string(),
25150
25174
  head: oid2.nullable(),
25151
25175
  observedAt: external_exports.string(),
25152
25176
  working: external_exports.boolean(),
25153
25177
  stamp: external_exports.string(),
25154
25178
  problems: external_exports.array(problem),
25155
- 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() })) })
25156
25180
  });
25157
25181
  var featureRow = external_exports.strictObject({
25158
25182
  id: external_exports.string(),
@@ -25179,14 +25203,7 @@ var browserFeaturesV1 = external_exports.strictObject({
25179
25203
  next: external_exports.string().nullable(),
25180
25204
  features: external_exports.array(featureRow)
25181
25205
  });
25182
- var browserFeatureV1 = external_exports.strictObject({ contract: external_exports.literal("browser-feature"), version: external_exports.literal(1), sessionId: external_exports.string(), feature });
25183
- var browserInstructionsV1 = external_exports.strictObject({
25184
- contract: external_exports.literal("browser-instructions"),
25185
- version: external_exports.literal(1),
25186
- sessionId: external_exports.string(),
25187
- instructions: external_exports.array(instruction),
25188
- agents: agents.nullable()
25189
- });
25206
+ var browserFeatureV2 = external_exports.strictObject({ contract: external_exports.literal("browser-feature"), version: external_exports.literal(2), sessionId: external_exports.string(), feature });
25190
25207
  var browserContributorsV1 = external_exports.strictObject({
25191
25208
  contract: external_exports.literal("browser-contributors"),
25192
25209
  version: external_exports.literal(1),
@@ -25213,6 +25230,7 @@ var snapshot = external_exports.strictObject({
25213
25230
  specId: external_exports.string(),
25214
25231
  path: external_exports.string(),
25215
25232
  order: external_exports.number().int().optional(),
25233
+ style: style.optional(),
25216
25234
  requirements: external_exports.array(external_exports.string()).optional(),
25217
25235
  sources: external_exports.array(source).optional()
25218
25236
  }).nullable();
@@ -25268,20 +25286,20 @@ var browserHistorySummaryV5 = external_exports.strictObject({
25268
25286
  // The newest commits: a few of their changes each and how many there are in all.
25269
25287
  recent: external_exports.array(external_exports.strictObject({ commit: oid2, count: external_exports.number().int().positive(), events: external_exports.array(event) }))
25270
25288
  });
25271
- var searchKind = external_exports.enum(["feature", "requirement", "design", "instruction", "record", "commit"]);
25289
+ var searchKind = external_exports.enum(["feature", "requirement", "design", "record", "commit"]);
25272
25290
  var searchHit = external_exports.strictObject({
25273
25291
  id: external_exports.string(),
25274
25292
  kind: searchKind,
25275
25293
  title: external_exports.string(),
25276
25294
  where: external_exports.string(),
25277
25295
  line: external_exports.string(),
25278
- // 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.
25279
25297
  featureId: external_exports.string().optional(),
25280
25298
  commit: oid2.optional()
25281
25299
  });
25282
- var browserSearchV3 = external_exports.strictObject({
25300
+ var browserSearchV4 = external_exports.strictObject({
25283
25301
  contract: external_exports.literal("browser-search"),
25284
- version: external_exports.literal(3),
25302
+ version: external_exports.literal(4),
25285
25303
  sessionId: external_exports.string(),
25286
25304
  query: external_exports.string(),
25287
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) }))
@@ -25313,14 +25331,13 @@ var browserFeaturesQueryV1 = external_exports.strictObject({
25313
25331
  var browserFeatureQueryV1 = external_exports.strictObject({ id: featureId });
25314
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() });
25315
25333
  var browserContributorQueryV1 = external_exports.strictObject({ email: external_exports.string().min(1).max(320) });
25316
- var browserSearchQueryV2 = external_exports.strictObject({
25334
+ var browserSearchQueryV3 = external_exports.strictObject({
25317
25335
  q: external_exports.string().max(200),
25318
25336
  head: headQuery.optional(),
25319
25337
  group: searchKind.optional(),
25320
25338
  after: external_exports.string().min(1).max(200).optional(),
25321
25339
  limit: count(50).pipe(external_exports.number().min(1)).optional()
25322
25340
  });
25323
- var browserInstructionFileQueryV1 = external_exports.strictObject({ id: external_exports.string().regex(/^I-[a-z2-7]{10}$/), path: external_exports.string().min(1).max(1e3) });
25324
25341
  var changelogQueryV1 = external_exports.strictObject({ lang: external_exports.string().regex(/^[a-z]{2}(?:-[A-Z]{2})?$/).optional() });
25325
25342
  var fileStatus = external_exports.enum(["added", "modified", "deleted", "renamed"]);
25326
25343
  var commitFile = external_exports.strictObject({
@@ -25370,9 +25387,9 @@ var browserCommitV4 = external_exports.strictObject({
25370
25387
  });
25371
25388
  var recordId = external_exports.string().regex(/^(?:DR|H)-[A-Za-z0-9_-]{1,64}$/);
25372
25389
  var browserCommitQueryV3 = external_exports.strictObject({ commit: oid2, after: external_exports.string().min(1).max(200).optional(), limit: count(100).pipe(external_exports.number().min(1)).optional(), record: recordId.optional() });
25373
- var browserCommitChangeV1 = external_exports.strictObject({
25390
+ var browserCommitChangeV2 = external_exports.strictObject({
25374
25391
  contract: external_exports.literal("browser-commit-change"),
25375
- version: external_exports.literal(1),
25392
+ version: external_exports.literal(2),
25376
25393
  sessionId: external_exports.string(),
25377
25394
  commit: oid2,
25378
25395
  event,
@@ -25409,17 +25426,6 @@ var browserDocumentHistoryQueryV1 = external_exports.strictObject({
25409
25426
  after: oid2.optional(),
25410
25427
  limit: count(50).pipe(external_exports.number().min(1)).optional()
25411
25428
  });
25412
- var browserInstructionFileV1 = external_exports.strictObject({
25413
- contract: external_exports.literal("browser-instruction-file"),
25414
- version: external_exports.literal(1),
25415
- sessionId: external_exports.string(),
25416
- id: external_exports.string(),
25417
- path: external_exports.string(),
25418
- text: external_exports.string().nullable(),
25419
- size: external_exports.number().int().nonnegative(),
25420
- binary: external_exports.boolean(),
25421
- tooLarge: external_exports.boolean()
25422
- });
25423
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) });
25424
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() });
25425
25431
  var browserWorkingV1 = external_exports.strictObject({
@@ -25431,9 +25437,9 @@ var browserWorkingV1 = external_exports.strictObject({
25431
25437
  changes: external_exports.array(workingChange),
25432
25438
  withoutRecord: external_exports.array(external_exports.string())
25433
25439
  });
25434
- var browserWorkingChangeV1 = external_exports.strictObject({
25440
+ var browserWorkingChangeV2 = external_exports.strictObject({
25435
25441
  contract: external_exports.literal("browser-working-change"),
25436
- version: external_exports.literal(1),
25442
+ version: external_exports.literal(2),
25437
25443
  sessionId: external_exports.string(),
25438
25444
  change: workingChange,
25439
25445
  before: snapshot,
@@ -25520,7 +25526,8 @@ var updateV6 = external_exports.discriminatedUnion("ok", [
25520
25526
  // refreshed: blocks whose text differed were rewritten. current: every block already matched. kept: the project is on a
25521
25527
  // release newer than this CLI, so its blocks were left as they are.
25522
25528
  // not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
25523
- // 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.
25524
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()) }),
25525
25532
  // written: .gitifact/config.json now names this release as `cli` and the block language. current: it already did.
25526
25533
  // cli is the project's release afterwards; one newer than cliVersion was kept, since an older CLI does not lower it.
@@ -25701,7 +25708,6 @@ var messages_default3 = {
25701
25708
  "server.recordNotFound": "\uC774 \uC774\uB825\uC5D0 \uADF8 \uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25702
25709
  "server.documentNotFound": "\uC774 \uC774\uB825\uACFC \uC791\uC5C5 \uD3F4\uB354\uC5D0 \uADF8 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
25703
25710
  "server.commitFileNotFound": "\uADF8 \uCEE4\uBC0B\uC774 \uBC14\uAFBC \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
25704
- "server.instructionFileNotFound": "\uC9C0\uCE68 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
25705
25711
  "server.specsUnreadable": "\uBA85\uC138\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
25706
25712
  "server.refreshOrigin": "\uC0C8\uB85C\uACE0\uCE68\uC5D0\uB294 \uAC19\uC740 \uCD9C\uCC98\uC758 Origin\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
25707
25713
  "server.fileNotFound": "\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
@@ -25743,7 +25749,7 @@ var messages_default3 = {
25743
25749
  "init.text.storage": "\uC800\uC7A5 \uADDC\uC57D",
25744
25750
  "init.text.agentDocs": "\uC5D0\uC774\uC804\uD2B8 \uC9C0\uCE68",
25745
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.",
25746
- "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.",
25747
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.",
25748
25754
  "config.storeNotDirectory": ".gitifact\uB294 \uB9C1\uD06C\uAC00 \uC544\uB2CC \uC77C\uBC18 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
25749
25755
  "config.fileShape": "config.json\uC740 64 KiB \uC774\uD558\uC758 \uC77C\uBC18 \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
@@ -25778,14 +25784,13 @@ var messages_default3 = {
25778
25784
  "update.text.upToDate": "\uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.",
25779
25785
  "update.text.disabled": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC774 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4.",
25780
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.",
25781
- "update.text.install": "\uC124\uCE58 \uBA85\uB839: {command}",
25782
- "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.",
25783
25789
  "update.text.blockRefreshed": "\uC9C0\uCE68 \uBE14\uB85D \uAC31\uC2E0: {paths}",
25784
25790
  "update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
25785
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.",
25786
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.",
25787
- "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.",
25788
- "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.",
25789
25794
  "update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
25790
25795
  "update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25791
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}",
@@ -25805,7 +25810,7 @@ var messages_default3 = {
25805
25810
  "help.guide": "\uC5D0\uC774\uC804\uD2B8\uC6A9 \uC791\uC131 \uC9C0\uCE68",
25806
25811
  "help.guideList": "\uC9C0\uCE68 \uBAA9\uB85D (\uC81C\uBAA9\uACFC \uC124\uBA85)",
25807
25812
  "help.guideShow": "\uC9C0\uCE68 \uD558\uB098\uB97C \uCD9C\uB825",
25808
- "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",
25809
25814
  "command.failed": "\uBA85\uB839\uC744 \uC2E4\uD589\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
25810
25815
  "project.notInitialized": "\uBA3C\uC800 gitifact init\uC73C\uB85C \uCD08\uAE30\uD654\uD558\uC138\uC694.",
25811
25816
  "input.file": "\uC785\uB825\uC740 1 MiB \uC774\uD558\uC758 JSON \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
@@ -25827,16 +25832,14 @@ var messages_default3 = {
25827
25832
  "docs.unknownDocument": "\uBB38\uC11C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: {ids}",
25828
25833
  "docs.references": "\uCC38\uC870",
25829
25834
  "docs.referencedBy": "\uAC00\uB9AC\uD0A4\uB294 \uBB38\uC11C",
25830
- "docs.files": "\uD30C\uC77C",
25831
25835
  "docs.invalidPath": "{kind} \uBB38\uC11C\uC758 \uACBD\uB85C\uAC00 \uC544\uB2D9\uB2C8\uB2E4: {path}. \uC608: {example}",
25832
25836
  "docs.pathExample.feature": "payment (.gitifact/spec/payment/index.md)",
25833
25837
  "docs.pathExample.requirement": "payment/cancel (.gitifact/spec/payment/requirements/cancel.md)",
25834
25838
  "docs.pathExample.design": "payment/api (.gitifact/spec/payment/design/api.md)",
25835
- "docs.pathExample.instruction": "cli-architecture (.gitifact/instructions/cli-architecture/index.md)",
25836
25839
  "docs.skeleton.feature": "(\uC774 \uAE30\uB2A5\uC774 \uBB34\uC5C7\uC774\uACE0 \uC5B4\uB514\uAE4C\uC9C0\uC778\uC9C0)",
25837
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)",
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)",
25838
25842
  "docs.skeleton.design": "(\uC774 \uC124\uACC4\uAC00 \uB2E4\uB8E8\uB294 \uAD6C\uC870\uC640 \uCC98\uB9AC \uBC29\uC2DD)",
25839
- "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)",
25840
25843
  "docs.exists": "\uC774\uBBF8 \uC788\uB294 \uD30C\uC77C\uC785\uB2C8\uB2E4: {path}",
25841
25844
  "docs.notFolder": "\uD3F4\uB354 \uC790\uB9AC\uC5D0 \uC77C\uBC18 \uD3F4\uB354\uAC00 \uC544\uB2CC \uAC83\uC774 \uC788\uC2B5\uB2C8\uB2E4: {path}",
25842
25845
  "docs.created": "\uB9CC\uB4E4\uC5C8\uC2B5\uB2C8\uB2E4: {path} ({id})",
@@ -25857,6 +25860,7 @@ var messages_default3 = {
25857
25860
  "commit.checkFailed": "\uBB38\uC11C \uAC80\uC0AC\uC5D0\uC11C \uBB38\uC81C {count}\uAC1C\uAC00 \uB098\uC640 \uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.",
25858
25861
  "commit.unknownDocument": "\uACB0\uC815\uAE30\uB85D\uC774 \uAC00\uB9AC\uD0A4\uB294 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4: {ids}",
25859
25862
  "docs.singleLine": "{field}\uC740 \uD55C \uC904\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
25863
+ "docs.styleRequirementOnly": "--style\uC740 \uC694\uAD6C\uC0AC\uD56D(requirement)\uC744 \uB9CC\uB4E4 \uB54C\uB9CC \uC501\uB2C8\uB2E4.",
25860
25864
  "changes.problems": "\uBB38\uC11C \uAC80\uC0AC: \uBB38\uC81C {count}\uAC1C (check\uB85C \uD655\uC778)",
25861
25865
  "changes.warnings": "\uBB38\uC11C \uACBD\uACE0: {count}\uAC1C (check\uB85C \uD655\uC778, \uCEE4\uBC0B\uC740 \uB9C9\uC9C0 \uC54A\uC74C)",
25862
25866
  "changes.formatWarnings": "\uD615\uC2DD \uACBD\uACE0 {count}\uAC1C (\uC774\uBC88\uC5D0 \uBC14\uB010 \uC694\uAD6C\uC0AC\uD56D, \uCEE4\uBC0B\uC740 \uB9C9\uC9C0 \uC54A\uC74C)",
@@ -25867,7 +25871,8 @@ var messages_default3 = {
25867
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}",
25868
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.",
25869
25873
  "commit.selectMove": "\uC62E\uAE34 \uBB38\uC11C\uB294 \uC61B \uACBD\uB85C\uC640 \uC0C8 \uACBD\uB85C\uB97C \uD568\uAED8 \uC120\uD0DD\uD558\uC138\uC694: {ids}",
25870
- "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}",
25871
25876
  "records.skeleton": "(\uB0B4\uC6A9)",
25872
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.",
25873
25878
  "records.unknown": "\uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {ids}",
@@ -25896,19 +25901,13 @@ var messages_default3 = {
25896
25901
  "help.specsNew": "ID\uB97C \uBC1C\uAE09\uD558\uACE0 \uD504\uB860\uD2B8\uB9E4\uD130\uC640 \uBCF8\uBB38 \uBF08\uB300\uB97C \uCC44\uC6B4 \uC0C8 \uBA85\uC138 \uD30C\uC77C \uC0DD\uC131 (draft: true \uD45C\uC2DC)",
25897
25902
  "help.specsNewKind": "feature, requirement, design \uC911 \uD558\uB098",
25898
25903
  "help.specsNewPath": "\uAE30\uB2A5 \uC774\uB984 \uB610\uB294 \uAE30\uB2A5/slug (\uC608: payment, payment/cancel)",
25904
+ "help.specsNewStyle": "\uC694\uAD6C\uC0AC\uD56D \uD615\uC2DD(usecase, default). \uC5C6\uC73C\uBA74 \uC124\uC815\uC758 requirementStyle\uC744 \uB530\uB978\uB2E4",
25899
25905
  "help.newTitle": "\uBB38\uC11C \uC81C\uBAA9",
25900
25906
  "help.newDescription": "\uD55C \uC904 \uC124\uBA85",
25901
- "help.instructions": "\uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC758 \uBAA9\uB85D\xB7\uC6D0\uBB38\xB7\uC0DD\uC131",
25902
- "help.instructionsList": "AGENTS.md\uC640 \uC9C0\uCE68 \uBAA9\uB85D (\uC9C0\uCE68\uB9C8\uB2E4 \uB538\uB9B0 \uD30C\uC77C\uC758 \uC81C\uBAA9\uACFC \uC124\uBA85)",
25903
- "help.instructionsShow": "\uC9C0\uCE68 index.md \uC6D0\uBB38, \uB530\uB974\uB294 \uC124\uACC4, \uD3F4\uB354 \uD30C\uC77C \uBAA9\uB85D",
25904
- "help.instructionsShowTargets": "\uC9C0\uCE68 \uD3F4\uB354 \uC774\uB984\uC774\uB098 I- ID (\uC5EC\uB7EC \uAC1C \uAC00\uB2A5)",
25905
- "help.instructionsShowFile": "\uC9C0\uCE68 \uD3F4\uB354 \uC548\uC758 \uD30C\uC77C \uD558\uB098\uB97C \uC77D\uC74C (\uC608: references/code-style.md)",
25906
- "help.instructionsNew": "ID\uB97C \uBC1C\uAE09\uD558\uACE0 \uC9C0\uCE68 \uD3F4\uB354\uC640 index.md \uBF08\uB300 \uC0DD\uC131 (draft: true \uD45C\uC2DC)",
25907
- "help.instructionsNewName": "\uC9C0\uCE68 \uD3F4\uB354 \uC774\uB984 (\uC608: code-review)",
25908
25907
  "help.recordsList": "HEAD \uC774\uB825\uC758 \uACB0\uC815\uAE30\uB85D (\uCD5C\uC2E0\uC21C, \uCEE4\uBC0B \uC804 \uAE30\uB85D\uC774 \uBA3C\uC800)",
25909
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",
25910
25909
  "help.recordsListSince": "\uADF8 \uB0A0\uC9DC(YYYY-MM-DD)\uB098 \uCEE4\uBC0B \uB4A4\uC758 \uAC83\uB9CC",
25911
- "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)",
25912
25911
  "help.listQ": "\uC81C\uBAA9\xB7\uC124\uBA85\xB7\uBCF8\uBB38\uC5D0 \uC774 \uB9D0\uC774 \uC788\uB294 \uAC83\uB9CC",
25913
25912
  "help.listAuthor": "\uC774 Git \uC791\uC131\uC790(\uC774\uB984\uC774\uB098 \uC774\uBA54\uC77C)\uAC00 \uBC14\uAFBC \uAC83\uB9CC",
25914
25913
  "help.listSort": "\uC815\uB82C \uAE30\uC900",
@@ -25925,18 +25924,9 @@ var messages_default3 = {
25925
25924
  "list.features": "\uAE30\uB2A5",
25926
25925
  "list.commits": "\uCEE4\uBC0B",
25927
25926
  "list.records": "\uACB0\uC815\uAE30\uB85D",
25928
- "docs.otherResource": "\uC774 \uBA85\uB839\uC774 \uB2E4\uB8E8\uB294 \uBB38\uC11C\uAC00 \uC544\uB2D9\uB2C8\uB2E4: {ids}. {command}\uB85C \uC77D\uC73C\uC138\uC694.",
25929
25927
  "docs.noMatch": "\uC870\uAC74\uC5D0 \uB9DE\uB294 \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25930
25928
  "docs.requirement": "\uC694\uAD6C\uC0AC\uD56D",
25931
25929
  "docs.design": "\uC124\uACC4",
25932
- "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",
25933
- "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",
25934
- "instructions.filesLimited": "(\uD30C\uC77C\uC774 \uB354 \uC788\uC5B4 \uC55E {count}\uAC1C\uB9CC \uBCF4\uC785\uB2C8\uB2E4)",
25935
- "instructions.empty": "\uC9C0\uCE68\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. instructions new\uB85C \uB9CC\uB4DC\uC138\uC694.",
25936
- "instructions.fileWithOne": "--file\uC740 \uC9C0\uCE68 \uD558\uB098\uC640 \uD568\uAED8 \uC4F0\uBA70 --ref\uC640 \uD568\uAED8 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
25937
- "instructions.unknownFile": "\uC9C0\uCE68 {name} \uD3F4\uB354\uC5D0 \uADF8 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: {path}",
25938
- "instructions.fileTooLarge": "(\uD30C\uC77C\uC774 \uCEE4\uC11C \uC6D0\uBB38\uC744 \uBCF4\uC774\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: {size}\uBC14\uC774\uD2B8)",
25939
- "instructions.fileBinary": "(\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: {size}\uBC14\uC774\uD2B8)",
25940
25930
  "records.empty": "\uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25941
25931
  "records.moreDocs": "\uC678 {count}\uAC1C",
25942
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)",
@@ -25952,11 +25942,11 @@ var messages_default3 = {
25952
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.",
25953
25943
  "feedback.title": "\uC81C\uBAA9: {title}",
25954
25944
  "update.text.projectWritten": "{path}\uC758 \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC744 gitifact {version}\uC73C\uB85C \uC801\uC5C8\uC2B5\uB2C8\uB2E4.",
25955
- "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.",
25956
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.",
25957
25947
  "update.later.nothing": "\uBBF8\uB8F0 \uC0C8 \uBC84\uC804 \uC548\uB0B4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(\uC9C0\uAE08 {version}).",
25958
- "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",
25959
- "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.",
25960
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.",
25961
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",
25962
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}"
@@ -26017,7 +26007,6 @@ var messages_default4 = {
26017
26007
  "server.recordNotFound": "This history has no such decision record.",
26018
26008
  "server.documentNotFound": "Neither this history nor the working tree has that document.",
26019
26009
  "server.commitFileNotFound": "That commit did not change this file.",
26020
- "server.instructionFileNotFound": "The instruction file was not found.",
26021
26010
  "server.specsUnreadable": "Could not read specifications.",
26022
26011
  "server.refreshOrigin": "Refresh requires a same-origin Origin header.",
26023
26012
  "server.fileNotFound": "File not found.",
@@ -26059,7 +26048,7 @@ var messages_default4 = {
26059
26048
  "init.text.storage": "Storage convention",
26060
26049
  "init.text.agentDocs": "Agent instructions",
26061
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.",
26062
- "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}.",
26063
26052
  "init.failed": "Initialization failed. Check file permissions and the generated configuration and temporary files.",
26064
26053
  "config.storeNotDirectory": ".gitifact must be a regular directory, not a link.",
26065
26054
  "config.fileShape": "config.json must be a regular file no larger than 64 KiB.",
@@ -26094,14 +26083,13 @@ var messages_default4 = {
26094
26083
  "update.text.upToDate": "You are using the latest version.",
26095
26084
  "update.text.disabled": "Update checks are disabled.",
26096
26085
  "update.text.unavailable": "Could not check for updates. Check your network connection.",
26097
- "update.text.install": "Install command: {command}",
26098
- "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.",
26099
26088
  "update.text.blockRefreshed": "Instruction blocks refreshed: {paths}",
26100
26089
  "update.text.blockCurrent": "Instruction blocks are already current: {paths}",
26101
26090
  "update.text.noBlock": "No instruction files contain a GITIFACT block. Run gitifact init to install one.",
26102
26091
  "update.text.notInitialized": "Only the version was checked because this project is not initialized.",
26103
- "update.text.missing": "{paths} is missing, so Claude Code cannot load the AGENTS.md instructions. Rerun gitifact init to create the import file.",
26104
- "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.",
26105
26093
  "update.text.committed": "Instruction blocks committed: {commit} ({paths})",
26106
26094
  "update.text.commitNothing": "No instruction block changes to commit.",
26107
26095
  "update.text.commitSkipped.untracked": "Not committed. Instruction files are untracked: {paths}",
@@ -26121,7 +26109,7 @@ var messages_default4 = {
26121
26109
  "help.guide": "Writing guides for agents",
26122
26110
  "help.guideList": "List guides (title and description)",
26123
26111
  "help.guideShow": "Print one guide",
26124
- "help.guideTopic": "One of workflow, spec, design, instructions, records, writing, commit, migrate",
26112
+ "help.guideTopic": "One of workflow, spec, design, records, writing, commit, migrate",
26125
26113
  "command.failed": "The command failed.",
26126
26114
  "project.notInitialized": "Initialize the project with gitifact init first.",
26127
26115
  "input.file": "The input must be a JSON file of 1 MiB or less.",
@@ -26143,16 +26131,14 @@ var messages_default4 = {
26143
26131
  "docs.unknownDocument": "Documents not found: {ids}",
26144
26132
  "docs.references": "References",
26145
26133
  "docs.referencedBy": "Referenced by",
26146
- "docs.files": "Files",
26147
26134
  "docs.invalidPath": "Not a path for a {kind} document: {path}. Example: {example}",
26148
26135
  "docs.pathExample.feature": "payment (.gitifact/spec/payment/index.md)",
26149
26136
  "docs.pathExample.requirement": "payment/cancel (.gitifact/spec/payment/requirements/cancel.md)",
26150
26137
  "docs.pathExample.design": "payment/api (.gitifact/spec/payment/design/api.md)",
26151
- "docs.pathExample.instruction": "cli-architecture (.gitifact/instructions/cli-architecture/index.md)",
26152
26138
  "docs.skeleton.feature": "(What this feature is and where it ends)",
26153
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)",
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)",
26154
26141
  "docs.skeleton.design": "(The structure and processing this design covers)",
26155
- "docs.skeleton.instruction": "(Rules and steps to follow for this kind of work. Put details in files under references/ and link them from here)",
26156
26142
  "docs.exists": "The file already exists: {path}",
26157
26143
  "docs.notFolder": "Something other than a plain folder stands where a folder is needed: {path}",
26158
26144
  "docs.created": "Created {path} ({id})",
@@ -26173,6 +26159,7 @@ var messages_default4 = {
26173
26159
  "commit.checkFailed": "The document check found {count} problems; nothing was committed.",
26174
26160
  "commit.unknownDocument": "A record names documents that do not exist: {ids}",
26175
26161
  "docs.singleLine": "The {field} must be a single line.",
26162
+ "docs.styleRequirementOnly": "--style applies only when creating a requirement.",
26176
26163
  "changes.problems": "Document check: {count} problems (see check)",
26177
26164
  "changes.warnings": "Document warnings: {count} (see check; they do not block a commit)",
26178
26165
  "changes.formatWarnings": "Format warnings: {count} (requirements changed here; they do not block a commit)",
@@ -26183,7 +26170,8 @@ var messages_default4 = {
26183
26170
  "changes.recordAltered": "Committed records edited or deleted (the commit is refused): {paths}",
26184
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).",
26185
26172
  "commit.selectMove": "Select both the old and the new path of a moved document: {ids}",
26186
- "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}",
26187
26175
  "records.skeleton": "(Content)",
26188
26176
  "records.createdNext": "Fill in the sections, remove the draft: true line, and list this file in paths when you commit.",
26189
26177
  "records.unknown": "No such record: {ids}",
@@ -26212,19 +26200,13 @@ var messages_default4 = {
26212
26200
  "help.specsNew": "Create a spec file with an issued ID, frontmatter and a body skeleton (marked draft: true)",
26213
26201
  "help.specsNewKind": "One of feature, requirement, design",
26214
26202
  "help.specsNewPath": "Feature name or feature/slug (e.g. payment, payment/cancel)",
26203
+ "help.specsNewStyle": "Requirement shape (usecase, default). Defaults to the config requirementStyle",
26215
26204
  "help.newTitle": "Document title",
26216
26205
  "help.newDescription": "One-line description",
26217
- "help.instructions": "List, read and create project instructions",
26218
- "help.instructionsList": "AGENTS.md and the instructions (with the title and description of each file in their folders)",
26219
- "help.instructionsShow": "An instruction's index.md as written, the designs that follow it and its folder's files",
26220
- "help.instructionsShowTargets": "Instruction folder names or I- IDs (one or more)",
26221
- "help.instructionsShowFile": "Read one file of the instruction folder (e.g. references/code-style.md)",
26222
- "help.instructionsNew": "Create an instruction folder and index.md skeleton with an issued ID (marked draft: true)",
26223
- "help.instructionsNewName": "Instruction folder name (e.g. code-review)",
26224
26206
  "help.recordsList": "Decision records of HEAD's history (newest first, uncommitted ones first)",
26225
26207
  "help.recordsListDoc": "One document's decision flow: the records of each commit that changed it",
26226
26208
  "help.recordsListSince": "Only those after that date (YYYY-MM-DD) or commit",
26227
- "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)",
26228
26210
  "help.listQ": "Only those whose title, description or text holds these words",
26229
26211
  "help.listAuthor": "Only those this Git author (name or email) changed",
26230
26212
  "help.listSort": "Sort key",
@@ -26241,18 +26223,9 @@ var messages_default4 = {
26241
26223
  "list.features": "Features",
26242
26224
  "list.commits": "Commits",
26243
26225
  "list.records": "Records",
26244
- "docs.otherResource": "Not a document this command reads: {ids}. Use {command}.",
26245
26226
  "docs.noMatch": "Nothing matches.",
26246
26227
  "docs.requirement": "requirement",
26247
26228
  "docs.design": "design",
26248
- "instructions.agents": "AGENTS.md \u2014 the index outside the block says which instruction to read for which work",
26249
- "instructions.noAgents": "No AGENTS.md \u2014 nothing tells when to read which instruction",
26250
- "instructions.filesLimited": "(the folder holds more; only the first {count} are shown)",
26251
- "instructions.empty": "No instructions yet. Create one with instructions new.",
26252
- "instructions.fileWithOne": "--file takes one instruction and does not go with --ref.",
26253
- "instructions.unknownFile": "The folder of instruction {name} has no such file: {path}",
26254
- "instructions.fileTooLarge": "(Too large to show: {size} bytes)",
26255
- "instructions.fileBinary": "(Not a text file: {size} bytes)",
26256
26229
  "records.empty": "No decision records yet.",
26257
26230
  "records.moreDocs": "and {count} more",
26258
26231
  "help.feedback": "Send a Gitifact bug or idea as an issue to Gitifact's repository (prints a new-issue address without gh)",
@@ -26268,11 +26241,11 @@ var messages_default4 = {
26268
26241
  "feedback.truncated": "The body is long, so the address holds only its beginning. Paste the full body below into the page.",
26269
26242
  "feedback.title": "Title: {title}",
26270
26243
  "update.text.projectWritten": "Set the project to gitifact {version} in {path}.",
26271
- "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.",
26272
26245
  "update.later.postponed": "The notice for {latest} is quiet until {until}. A newer release is announced right away.",
26273
26246
  "update.later.nothing": "No newer release to postpone (running {version}).",
26274
- "notice.release": "Version {latest} is available (running {version}). Update: {command}, then gitifact update \xB7 Later: gitifact update --later",
26275
- "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.",
26276
26249
  "notice.projectOlder": "This CLI ({version}) is newer than the project ({project}). Run gitifact update to move the project to it.",
26277
26250
  "help.updateLater": "Quiet the announced newer release for 24 hours; a release newer than it is announced right away",
26278
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}"
@@ -26627,7 +26600,7 @@ function storeReader(cwd) {
26627
26600
  import { DatabaseSync } from "node:sqlite";
26628
26601
  import { lstat, mkdir, rm, writeFile } from "node:fs/promises";
26629
26602
  import { join as join2 } from "node:path";
26630
- var CACHE_FORMAT = 11;
26603
+ var CACHE_FORMAT = 13;
26631
26604
  var CACHE_DIR = ".gitifact/cache";
26632
26605
  var SCHEMA = `
26633
26606
  -- Working-tree files already read: the modification time and size decide whether a file is read again.
@@ -26813,6 +26786,7 @@ function createDocumentCache(root, database) {
26813
26786
  async function read(path3, seen) {
26814
26787
  try {
26815
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)) } : {};
26816
26790
  if (classifyDocPath(path3).type !== "doc") return {};
26817
26791
  if (seen.link) return { problem: docProblem("PATH_UNSUPPORTED", path3) };
26818
26792
  if (seen.size > FILE_LIMIT) return { problem: docProblem("FILE_TOO_LARGE", path3) };
@@ -26887,13 +26861,6 @@ function createDocumentCache(root, database) {
26887
26861
  return true;
26888
26862
  }
26889
26863
  };
26890
- const instructionFile3 = (path3) => {
26891
- try {
26892
- return classifyDocPath(path3).type === "instruction-file";
26893
- } catch {
26894
- return false;
26895
- }
26896
- };
26897
26864
  return {
26898
26865
  sync,
26899
26866
  /**
@@ -26910,7 +26877,7 @@ function createDocumentCache(root, database) {
26910
26877
  if (history) seen.set(HISTORY_PATH, { mtime: history.mtimeMs, size: history.size, link: history.isSymbolicLink() || !history.isFile() });
26911
26878
  const read2 = await Promise.all([...seen].map(async ([path3, s]) => {
26912
26879
  if (!readable(path3)) return { path: path3 };
26913
- if (instructionFile3(path3) && !path3.endsWith(".md")) return { path: path3, text: "" };
26880
+ if (isInstructionPath(path3) && !path3.endsWith(".md")) return { path: path3, text: "" };
26914
26881
  if (s.link) return { path: path3, problem: docProblem("PATH_UNSUPPORTED", path3) };
26915
26882
  if (s.size > (path3 === HISTORY_PATH ? 64 * FILE_LIMIT : FILE_LIMIT)) return { path: path3, problem: docProblem("FILE_TOO_LARGE", path3) };
26916
26883
  try {
@@ -27075,7 +27042,7 @@ var recordKind = (path3) => {
27075
27042
  var folderOf = (path3) => path3.startsWith(SPEC_ROOT + "/") ? path3.split("/").slice(0, 3).join("/") : null;
27076
27043
  function snapshot3(doc, specId) {
27077
27044
  const base = { id: doc.id, kind: doc.kind, title: doc.title, description: doc.description, body: doc.body, specId, path: doc.path };
27078
- if (doc.kind === "requirement") return { ...base, order: doc.order };
27045
+ if (doc.kind === "requirement") return { ...base, order: doc.order, ...doc.style ? { style: doc.style } : {} };
27079
27046
  if (doc.kind === "design") return { ...base, order: doc.order, requirements: doc.requirements, sources: doc.sources };
27080
27047
  return base;
27081
27048
  }
@@ -27098,7 +27065,7 @@ function sideOf2(files) {
27098
27065
  }
27099
27066
  return { docs: new Map(docs.map((d) => [d.id, snapshot3(d, d.kind === "wiki" || d.kind === "instruction" ? d.kind : specIds.get(folderOf(d.path)) ?? "")])), reasons };
27100
27067
  }
27101
- var same = (a, b) => a.title === b.title && a.description === b.description && a.body === b.body && a.order === b.order && JSON.stringify(a.requirements) === JSON.stringify(b.requirements) && JSON.stringify(a.sources) === JSON.stringify(b.sources);
27068
+ var same = (a, b) => a.title === b.title && a.description === b.description && a.body === b.body && a.order === b.order && a.style === b.style && JSON.stringify(a.requirements) === JSON.stringify(b.requirements) && JSON.stringify(a.sources) === JSON.stringify(b.sources);
27102
27069
  function compare(before, after) {
27103
27070
  const out = [];
27104
27071
  for (const id of [.../* @__PURE__ */ new Set([...before.docs.keys(), ...after.docs.keys()])].sort()) {
@@ -28007,88 +27974,6 @@ async function publishConfig(root, text4, recheck, beforePublish, replace = fals
28007
27974
  }
28008
27975
  }
28009
27976
 
28010
- // src/adapters/filesystem/instruction-folder.ts
28011
- import { lstat as lstat6, open as open3, readdir as readdir4, readFile as readFile5 } from "node:fs/promises";
28012
- import { join as join7 } from "node:path";
28013
- var INSTRUCTION_FILE_LIMIT = 500;
28014
- var INSTRUCTION_TEXT_LIMIT = 512 * 1024;
28015
- var folderOf3 = (root, instructionPath) => join7(root, ...instructionPath.split("/").slice(0, -1));
28016
- async function listInstructionFiles(root, instructionPath) {
28017
- const files = [];
28018
- let limited = false;
28019
- async function visit2(folder, prefix, depth) {
28020
- if (depth > 10) return;
28021
- const entries = await readdir4(folder, { withFileTypes: true }).catch(() => []);
28022
- for (const entry2 of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
28023
- if (files.length >= INSTRUCTION_FILE_LIMIT) {
28024
- limited = true;
28025
- return;
28026
- }
28027
- const path3 = prefix + entry2.name;
28028
- if (entry2.isDirectory()) await visit2(join7(folder, entry2.name), path3 + "/", depth + 1);
28029
- else if (entry2.isFile() && path3 !== INSTRUCTION_FILE) files.push({ path: path3, size: (await lstat6(join7(folder, entry2.name))).size });
28030
- }
28031
- }
28032
- await visit2(folderOf3(root, instructionPath), "", 0);
28033
- return { files, limited };
28034
- }
28035
- var FRONTMATTER_WINDOW = 8 * 1024;
28036
- async function describeFiles(root, instructionPath, files) {
28037
- const folder = folderOf3(root, instructionPath);
28038
- return Promise.all(files.map(async (file2) => {
28039
- if (!isInstructionReference(file2.path)) return file2;
28040
- let handle;
28041
- try {
28042
- handle = await open3(join7(folder, ...file2.path.split("/")), "r");
28043
- const buffer = Buffer.alloc(Math.min(FRONTMATTER_WINDOW, file2.size));
28044
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
28045
- return { ...file2, ...parseInstructionFile(file2.path, buffer.subarray(0, bytesRead).toString("utf8")) };
28046
- } catch {
28047
- return file2;
28048
- } finally {
28049
- await handle?.close();
28050
- }
28051
- }));
28052
- }
28053
- async function readInstructionFiles(root, instructionPath) {
28054
- const { files, limited } = await listInstructionFiles(root, instructionPath);
28055
- return { files: await describeFiles(root, instructionPath, files), limited };
28056
- }
28057
- async function readInstructionFile(root, instructionPath, path3) {
28058
- const parts = path3.split("/");
28059
- if (parts.some((p) => !p || p === "." || p === ".." || /[\\:\0]/.test(p))) return void 0;
28060
- let full = folderOf3(root, instructionPath);
28061
- for (const [index, part] of parts.entries()) {
28062
- full = join7(full, part);
28063
- const info5 = await lstat6(full).catch(() => void 0);
28064
- if (!info5 || info5.isSymbolicLink() || (index < parts.length - 1 ? !info5.isDirectory() : !info5.isFile())) return void 0;
28065
- }
28066
- const size = (await lstat6(full)).size;
28067
- if (size > INSTRUCTION_TEXT_LIMIT) return { text: null, size, binary: false, tooLarge: true };
28068
- const bytes = await readFile5(full);
28069
- try {
28070
- if (bytes.includes(0)) throw new TypeError("binary");
28071
- return { text: new TextDecoder("utf-8", { fatal: true }).decode(bytes), size, binary: false, tooLarge: false };
28072
- } catch {
28073
- return { text: null, size, binary: true, tooLarge: false };
28074
- }
28075
- }
28076
-
28077
- // src/adapters/filesystem/agents-file.ts
28078
- import { lstat as lstat7, readFile as readFile6 } from "node:fs/promises";
28079
- import { join as join8 } from "node:path";
28080
- var LIMIT = 1024 * 1024;
28081
- async function readAgentsFile(root) {
28082
- const file2 = join8(root, "AGENTS.md");
28083
- const info5 = await lstat7(file2).catch(() => void 0);
28084
- if (!info5?.isFile() || info5.size > LIMIT) return null;
28085
- try {
28086
- return new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(await readFile6(file2));
28087
- } catch {
28088
- return null;
28089
- }
28090
- }
28091
-
28092
27977
  // src/queries/document-states.ts
28093
27978
  var isDocument = (path3) => {
28094
27979
  try {
@@ -28157,8 +28042,8 @@ async function documentsAtHead(project, head, paths2) {
28157
28042
 
28158
28043
  // src/server/checkout/stamp.ts
28159
28044
  import { createHash as createHash2 } from "node:crypto";
28160
- import { lstat as lstat8 } from "node:fs/promises";
28161
- import { join as join9 } from "node:path";
28045
+ import { lstat as lstat6 } from "node:fs/promises";
28046
+ import { join as join7 } from "node:path";
28162
28047
  function createStampReader(root, inherited = process.env) {
28163
28048
  const runner = createGitRunner();
28164
28049
  const env = { ...inherited, GIT_OPTIONAL_LOCKS: "0", GIT_NO_REPLACE_OBJECTS: "1", GIT_TERMINAL_PROMPT: "0", GIT_LITERAL_PATHSPECS: "0", LC_ALL: "C" };
@@ -28170,10 +28055,10 @@ function createStampReader(root, inherited = process.env) {
28170
28055
  ]);
28171
28056
  const paths2 = status.split("\0").filter(Boolean).map((entry2) => entry2.slice(3)).filter((path3) => !path3.startsWith(".gitifact/cache/"));
28172
28057
  const seen = await Promise.all(paths2.map(async (path3) => {
28173
- const info5 = await lstat8(join9(root, ...path3.split("/"))).catch(() => void 0);
28058
+ const info5 = await lstat6(join7(root, ...path3.split("/"))).catch(() => void 0);
28174
28059
  return path3 + " " + (info5 ? info5.mtimeMs + ":" + info5.size : "-");
28175
28060
  }));
28176
- const mailmap = await lstat8(join9(root, ".mailmap")).catch(() => void 0);
28061
+ const mailmap = await lstat6(join7(root, ".mailmap")).catch(() => void 0);
28177
28062
  seen.push(".mailmap " + (mailmap ? mailmap.mtimeMs + ":" + mailmap.size : "-"));
28178
28063
  return createHash2("sha256").update(head.trim() + "\n" + seen.sort().join("\n")).digest("hex").slice(0, 20);
28179
28064
  };
@@ -28186,7 +28071,7 @@ async function settled(reads) {
28186
28071
  if (failure2) throw failure2.reason;
28187
28072
  return results.map((r) => r.value);
28188
28073
  }
28189
- var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
28074
+ var folderOf3 = (path3) => path3.split("/").slice(0, 3).join("/");
28190
28075
  function checkoutDocuments(states, authors) {
28191
28076
  const documents = states.documents;
28192
28077
  const standing2 = (id) => {
@@ -28201,7 +28086,7 @@ function checkoutDocuments(states, authors) {
28201
28086
  };
28202
28087
  const arranged = arrangeDocuments(documents);
28203
28088
  const features = arranged.features.map(({ index, requirements, designs }) => {
28204
- const entry2 = authors.get(folderOf4(index.path));
28089
+ const entry2 = authors.get(folderOf3(index.path));
28205
28090
  return {
28206
28091
  id: index.id,
28207
28092
  path: index.path,
@@ -28209,7 +28094,7 @@ function checkoutDocuments(states, authors) {
28209
28094
  description: index.description,
28210
28095
  body: index.body,
28211
28096
  ...standing2(index.id),
28212
- requirements: requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description, order: r.order, body: r.body, ...standing2(r.id) })),
28097
+ requirements: requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description, order: r.order, ...r.style ? { style: r.style } : {}, body: r.body, ...standing2(r.id) })),
28213
28098
  designs: designs.map((d) => ({
28214
28099
  id: d.id,
28215
28100
  path: d.path,
@@ -28226,17 +28111,7 @@ function checkoutDocuments(states, authors) {
28226
28111
  updatedAt: entry2?.latest ?? null
28227
28112
  };
28228
28113
  });
28229
- const instructions2 = arranged.instructions.map((k) => ({
28230
- id: k.id,
28231
- name: k.name,
28232
- path: k.path,
28233
- title: k.title,
28234
- description: k.description,
28235
- body: k.body,
28236
- ...standing2(k.id),
28237
- updatedAt: authors.get(folderOf4(k.path))?.latest ?? null
28238
- }));
28239
- return { features, instructions: instructions2, orphans: arranged.orphans };
28114
+ return { features, orphans: arranged.orphans };
28240
28115
  }
28241
28116
  function createCheckoutReader(root, cache, inherited = process.env) {
28242
28117
  const reader = storeReader(root);
@@ -28265,16 +28140,15 @@ function createCheckoutReader(root, cache, inherited = process.env) {
28265
28140
  head ? cache.log.contributors(head) : Promise.resolve([])
28266
28141
  ]);
28267
28142
  const states = await documentStates({ head: async () => head, reader }, current3.documents);
28268
- const { features, instructions: instructions2, orphans } = checkoutDocuments(states, authors);
28143
+ const { features, orphans } = checkoutDocuments(states, authors);
28269
28144
  const problems = [...current3.problems, ...orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
28270
28145
  if (await readHead() !== head) throw new StoreError(t2("specReader.projectChanged"));
28271
28146
  return {
28272
28147
  head,
28273
28148
  stamp,
28274
28149
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
28275
- working: head ? !!dirty.trim() : features.length > 0 || instructions2.length > 0,
28150
+ working: head ? !!dirty.trim() : features.length > 0,
28276
28151
  features,
28277
- instructions: instructions2,
28278
28152
  problems,
28279
28153
  people,
28280
28154
  folders: authors
@@ -28297,19 +28171,7 @@ function createCheckoutReader(root, cache, inherited = process.env) {
28297
28171
  }
28298
28172
  return value;
28299
28173
  };
28300
- return {
28301
- base,
28302
- /** Every instruction by name with the other files of its folder, and AGENTS.md: few enough to read whole. */
28303
- async instructions() {
28304
- const checkout = await base();
28305
- const instructions2 = await Promise.all([...checkout.instructions].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0).map(async (instruction2) => {
28306
- const { files, limited } = instruction2.state === "deleted" ? { files: [], limited: false } : await readInstructionFiles(root, instruction2.path);
28307
- return { ...instruction2, files, filesLimited: limited };
28308
- }));
28309
- const agentsText = await readAgentsFile(root);
28310
- return { instructions: instructions2, agents: agentsText === null ? null : { path: "AGENTS.md", body: agentsText, updatedAt: checkout.folders.get("AGENTS.md")?.latest ?? null } };
28311
- }
28312
- };
28174
+ return { base };
28313
28175
  }
28314
28176
 
28315
28177
  // src/queries/paging.ts
@@ -28445,7 +28307,7 @@ function snapshot4(doc, docs) {
28445
28307
  const folder = doc.path.split("/").slice(0, 3).join("/");
28446
28308
  const specId = doc.kind === "wiki" || doc.kind === "instruction" ? doc.kind : docs.find((d) => d.kind === "feature" && d.path === folder + "/index.md")?.id ?? "";
28447
28309
  const base = { id: doc.id, kind: doc.kind, title: doc.title, description: doc.description, body: doc.body, specId, path: doc.path };
28448
- if (doc.kind === "requirement") return { ...base, order: doc.order };
28310
+ if (doc.kind === "requirement") return { ...base, order: doc.order, ...doc.style ? { style: doc.style } : {} };
28449
28311
  if (doc.kind === "design") return { ...base, order: doc.order, requirements: doc.requirements, sources: doc.sources };
28450
28312
  return base;
28451
28313
  }
@@ -28474,11 +28336,11 @@ async function workingChange2(source2, id) {
28474
28336
  }
28475
28337
 
28476
28338
  // src/queries/search.ts
28477
- var SEARCH_GROUPS = ["feature", "requirement", "design", "instruction", "record", "commit"];
28339
+ var SEARCH_GROUPS = ["feature", "requirement", "design", "record", "commit"];
28478
28340
  var FIRST = 5;
28479
28341
  var RECENT = 6;
28480
28342
  var place2 = (path3) => path3.replace(/^\.gitifact\//, "");
28481
- var folderOf5 = (path3) => path3.split("/").slice(0, 3).join("/");
28343
+ var folderOf4 = (path3) => path3.split("/").slice(0, 3).join("/");
28482
28344
  async function searchRecords(source2, request) {
28483
28345
  const head = request.head ?? await source2.head();
28484
28346
  const query = request.q.trim().toLowerCase();
@@ -28495,14 +28357,7 @@ async function searchRecords(source2, request) {
28495
28357
  var empty = () => ({ total: 0, next: null, hits: [] });
28496
28358
  async function recent(source2, head) {
28497
28359
  const [{ documents }, folders] = await Promise.all([source2.cache.documents.list(), head ? source2.cache.log.folders(head) : /* @__PURE__ */ new Map()]);
28498
- 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 }) => ({
28499
- id: doc.id,
28500
- kind: doc.kind,
28501
- title: doc.title,
28502
- where: place2(doc.path),
28503
- line: doc.description,
28504
- ...doc.kind === "feature" ? { featureId: doc.id } : {}
28505
- }));
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 }));
28506
28361
  return { group: "recent", total: hits.length, next: null, hits };
28507
28362
  }
28508
28363
 
@@ -28537,7 +28392,6 @@ function checkoutIndex(base) {
28537
28392
  requirements: f.requirements.map((r) => ({ id: r.id, path: r.path, title: r.title, description: r.description })),
28538
28393
  designs: f.designs.map((d) => ({ id: d.id, path: d.path, title: d.title }))
28539
28394
  })),
28540
- instructions: base.instructions.map((k) => ({ id: k.id, name: k.name, path: k.path, title: k.title })),
28541
28395
  people: base.people.map((p) => ({ name: p.name, email: p.email }))
28542
28396
  };
28543
28397
  }
@@ -28631,12 +28485,12 @@ function recordRoutes(root, sessionId, env) {
28631
28485
  const cursorGone2 = () => new HttpError(404, "NOT_FOUND", t2("server.cursorNotFound"));
28632
28486
  return [
28633
28487
  // The checkout a part at a time, each shaped by the query layer (`queries/checkout.ts`): the frame every screen reads
28634
- // first, the feature list and one feature, the instructions, and the contributors.
28488
+ // first, the feature list and one feature, and the contributors.
28635
28489
  route({ method: "GET", path: "/api/v1/checkout", session: true, unreadable: unreadable2, handle: async () => {
28636
28490
  const base = await checkout.base();
28637
- return ok(browserCheckoutV1.parse({
28491
+ return ok(browserCheckoutV2.parse({
28638
28492
  contract: "browser-checkout",
28639
- version: 1,
28493
+ version: 2,
28640
28494
  sessionId,
28641
28495
  head: base.head,
28642
28496
  observedAt: base.observedAt,
@@ -28654,9 +28508,8 @@ function recordRoutes(root, sessionId, env) {
28654
28508
  route({ method: "GET", path: "/api/v1/feature", session: true, query: browserFeatureQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28655
28509
  const feature2 = featureOf(await checkout.base(), query.id);
28656
28510
  if (!feature2) throw new HttpError(404, "NOT_FOUND", t2("server.featureNotFound"));
28657
- return ok(browserFeatureV1.parse({ contract: "browser-feature", version: 1, sessionId, feature: feature2 }));
28511
+ return ok(browserFeatureV2.parse({ contract: "browser-feature", version: 2, sessionId, feature: feature2 }));
28658
28512
  } }),
28659
- 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() })) }),
28660
28513
  route({ method: "GET", path: "/api/v1/contributors", session: true, query: browserContributorsQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28661
28514
  const page = listContributors(await checkout.base(), query.q, { after: query.after, limit: query.limit ?? PAGE });
28662
28515
  if (!page) throw cursorGone2();
@@ -28715,7 +28568,7 @@ function recordRoutes(root, sessionId, env) {
28715
28568
  route({ method: "GET", path: "/api/v1/commit/change", session: true, query: browserCommitChangeQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28716
28569
  const change2 = await cache.history.commitChange(query.commit, query.id);
28717
28570
  if (!change2) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
28718
- return ok(browserCommitChangeV1.parse({ contract: "browser-commit-change", version: 1, sessionId, commit: query.commit, event: change2.event, before: change2.before, after: change2.after }));
28571
+ return ok(browserCommitChangeV2.parse({ contract: "browser-commit-change", version: 2, sessionId, commit: query.commit, event: change2.event, before: change2.before, after: change2.after }));
28719
28572
  } }),
28720
28573
  // A record's page names the record; which commit added it is read from the history of the HEAD the reader is on.
28721
28574
  route({ method: "GET", path: "/api/v1/record", session: true, query: browserRecordQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
@@ -28735,7 +28588,7 @@ function recordRoutes(root, sessionId, env) {
28735
28588
  route({ method: "GET", path: "/api/v1/working/change", session: true, query: browserWorkingChangeQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28736
28589
  const change2 = await workingChange2(working2, query.id);
28737
28590
  if (!change2) throw new HttpError(404, "NOT_FOUND", t2("server.workingChangeNotFound"));
28738
- return ok(browserWorkingChangeV1.parse({ contract: "browser-working-change", version: 1, sessionId, ...change2 }));
28591
+ return ok(browserWorkingChangeV2.parse({ contract: "browser-working-change", version: 2, sessionId, ...change2 }));
28739
28592
  } }),
28740
28593
  // The source a commit changed beside its documents, read from Git when the commit or record page asks for it.
28741
28594
  route({ method: "GET", path: "/api/v1/commit/files", session: true, query: browserCommitFilesQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
@@ -28749,35 +28602,28 @@ function recordRoutes(root, sessionId, env) {
28749
28602
  if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.commitFileNotFound"));
28750
28603
  return ok(browserCommitFileV1.parse({ contract: "browser-commit-file", version: 1, sessionId, commit: query.commit, ...found }));
28751
28604
  } }),
28752
- // One file of an instruction folder, read from the working tree when the reader opens it; the checkout lists the files.
28753
- route({ method: "GET", path: "/api/v1/instructions/file", session: true, query: browserInstructionFileQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28754
- const instruction2 = (await cache.documents.list()).documents.find((d) => d.id === query.id && d.kind === "instruction");
28755
- const found = instruction2 && await readInstructionFile(root, instruction2.path, query.path);
28756
- if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.instructionFileNotFound"));
28757
- return ok(browserInstructionFileV1.parse({ contract: "browser-instruction-file", version: 1, sessionId, id: query.id, path: query.path, ...found }));
28758
- } }),
28759
28605
  // The search box's groups as the query layer works them out (`queries/search.ts`); the browser only draws them.
28760
- 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 }) => {
28761
28607
  const groups = await searchRecords(source2, query);
28762
28608
  if (!groups) throw cursorGone2();
28763
- 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 }));
28764
28610
  } })
28765
28611
  ];
28766
28612
  }
28767
28613
 
28768
28614
  // src/server/routes/asset-routes.ts
28769
- import { lstat as lstat9, readFile as readFile7 } from "node:fs/promises";
28770
- 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";
28771
28617
  function assetRoutes(root) {
28772
28618
  return [
28773
28619
  route({ method: "GET", path: "/api/v1/assets/*", session: false, handle: async ({ path: path3, response }) => {
28774
28620
  const relative = path3.slice("/api/v1/assets/".length);
28775
28621
  const assetPath = ASSETS_DIR + "/" + relative;
28776
28622
  if (!isAssetPath(assetPath)) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
28777
- const file2 = join10(root, ...assetPath.split("/"));
28778
- const stat3 = await lstat9(file2).catch(() => void 0);
28623
+ const file2 = join8(root, ...assetPath.split("/"));
28624
+ const stat3 = await lstat7(file2).catch(() => void 0);
28779
28625
  if (!stat3?.isFile() || stat3.isSymbolicLink() || stat3.size > 20 * 1024 * 1024) throw new HttpError(404, "NOT_FOUND", t2("server.assetNotFound"));
28780
- const bytes = await readFile7(file2);
28626
+ const bytes = await readFile5(file2);
28781
28627
  const extension = assetExtension(relative);
28782
28628
  const inline = ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(extension);
28783
28629
  response.writeHead(200, {
@@ -28794,7 +28640,7 @@ function assetRoutes(root) {
28794
28640
  }
28795
28641
 
28796
28642
  // src/server/browser-server.ts
28797
- 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) => {
28798
28644
  if (error62.code === "ENOENT") return null;
28799
28645
  throw error62;
28800
28646
  });
@@ -28924,14 +28770,14 @@ async function runBrowser(options, version2) {
28924
28770
  }
28925
28771
 
28926
28772
  // src/commands/spec-init.ts
28927
- import { readdir as readdir5 } from "node:fs/promises";
28928
- import { join as join14 } from "node:path";
28773
+ import { readdir as readdir4 } from "node:fs/promises";
28774
+ import { join as join13 } from "node:path";
28929
28775
 
28930
28776
  // src/adapters/git/init-repository.ts
28931
28777
  import { createHash as createHash3 } from "node:crypto";
28932
- import { lstat as lstat10, readFile as readFile9 } from "node:fs/promises";
28933
- import { join as join11 } from "node:path";
28934
- 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) => {
28935
28781
  if (error62.code === "ENOENT") return false;
28936
28782
  throw error62;
28937
28783
  });
@@ -28954,10 +28800,10 @@ function initRepository(cwd, inherited = process.env) {
28954
28800
  const gitDir = locations.length === 2 ? locations[0] : decodeGitLine(await git(["rev-parse", "--absolute-git-dir"], root));
28955
28801
  const indexPath = locations.length === 2 ? locations[1] : decodeGitLine(await git(["rev-parse", "--path-format=absolute", "--git-path", "index"], root));
28956
28802
  for (const marker2 of ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"]) {
28957
- 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 }));
28958
28804
  }
28959
28805
  if (state2.changes.some((change2) => change2.kind === "unmerged")) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
28960
- const index = await readFile9(indexPath).catch((error62) => {
28806
+ const index = await readFile7(indexPath).catch((error62) => {
28961
28807
  if (error62.code === "ENOENT") return Buffer.alloc(0);
28962
28808
  throw error62;
28963
28809
  });
@@ -28992,14 +28838,14 @@ function initRepository(cwd, inherited = process.env) {
28992
28838
 
28993
28839
  // src/adapters/filesystem/document-file.ts
28994
28840
  import { randomBytes } from "node:crypto";
28995
- import { lstat as lstat11, mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
28996
- 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";
28997
28843
  var generateId = (prefix) => prefix + "-" + [...randomBytes(10)].map((n) => "abcdefghijklmnopqrstuvwxyz234567"[n & 31]).join("");
28998
28844
  async function createDocumentFile(root, path3, text4) {
28999
28845
  const parts = path3.split("/");
29000
28846
  for (let i = 1; i < parts.length; i++) {
29001
- const folder = join12(root, ...parts.slice(0, i));
29002
- const info5 = await lstat11(folder).catch((error62) => {
28847
+ const folder = join10(root, ...parts.slice(0, i));
28848
+ const info5 = await lstat9(folder).catch((error62) => {
29003
28849
  if (error62.code === "ENOENT") return void 0;
29004
28850
  throw error62;
29005
28851
  });
@@ -29009,7 +28855,7 @@ async function createDocumentFile(root, path3, text4) {
29009
28855
  }
29010
28856
  if (!info5.isDirectory() || info5.isSymbolicLink()) throw Object.assign(new Error(folder), { code: "ENOTDIR" });
29011
28857
  }
29012
- await writeFile2(join12(root, ...parts), text4, { flag: "wx" });
28858
+ await writeFile2(join10(root, ...parts), text4, { flag: "wx" });
29013
28859
  }
29014
28860
  var LINE_ENDINGS_PATH = ".gitifact/.gitattributes";
29015
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";
@@ -29023,19 +28869,50 @@ async function writeLineEndings(root) {
29023
28869
  }
29024
28870
  }
29025
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
+
29026
28903
  // src/adapters/filesystem/managed-file.ts
29027
- 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";
29028
- 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";
29029
28906
  import { randomUUID as randomUUID4 } from "node:crypto";
29030
28907
  var DEFAULT_LIMIT = 1024 * 1024;
29031
28908
  async function managedPath(root, path3, create = false) {
29032
28909
  let full = root;
29033
28910
  const parts = path3.split("/");
29034
28911
  for (let i = 0; i < parts.length; i++) {
29035
- full = join13(full, parts[i]);
28912
+ full = join12(full, parts[i]);
29036
28913
  let info5 = await fileInfo(full);
29037
28914
  if (!info5 && create && i < parts.length - 1) {
29038
- await mkdir4(full).catch((error62) => {
28915
+ await mkdir5(full).catch((error62) => {
29039
28916
  if (error62.code !== "EEXIST") throw error62;
29040
28917
  });
29041
28918
  info5 = await fileInfo(full);
@@ -29051,7 +28928,7 @@ async function managedRead(root, path3, limit = DEFAULT_LIMIT) {
29051
28928
  const before = await fileInfo(full);
29052
28929
  if (!before) return null;
29053
28930
  if (before.size > limit) throw new InitError("FILE_TOO_LARGE", t2("managedFile.tooLarge", { kib: limit / 1024, path: path3 }));
29054
- const bytes = await readFile10(full);
28931
+ const bytes = await readFile9(full);
29055
28932
  await managedPath(root, path3);
29056
28933
  const after = await fileInfo(full);
29057
28934
  if (!after || before.ino !== after.ino || before.dev !== after.dev || before.mtimeMs !== after.mtimeMs || bytes.length > limit) {
@@ -29080,8 +28957,8 @@ async function managedWrite(root, path3, previous, next, recheck) {
29080
28957
  if (previous !== null) await unlink2(full);
29081
28958
  return;
29082
28959
  }
29083
- const temp = join13(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
29084
- const handle = await open4(temp, "wx", 384);
28960
+ const temp = join12(directory, ".gitifact-managed-" + randomUUID4() + ".tmp");
28961
+ const handle = await open3(temp, "wx", 384);
29085
28962
  const tempOwner = await handle.stat();
29086
28963
  try {
29087
28964
  try {
@@ -29092,7 +28969,7 @@ async function managedWrite(root, path3, previous, next, recheck) {
29092
28969
  }
29093
28970
  await recheck();
29094
28971
  await unchanged();
29095
- 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 }));
29096
28973
  if (previous === null) await link2(temp, full);
29097
28974
  else await rename2(temp, full);
29098
28975
  } finally {
@@ -29103,10 +28980,10 @@ async function managedWrite(root, path3, previous, next, recheck) {
29103
28980
  }
29104
28981
 
29105
28982
  // src/commands/agent-block.ts
29106
- import { readFile as readFile12 } from "node:fs/promises";
28983
+ import { readFile as readFile11 } from "node:fs/promises";
29107
28984
 
29108
28985
  // src/commands/guide.ts
29109
- import { readFile as readFile11 } from "node:fs/promises";
28986
+ import { readFile as readFile10 } from "node:fs/promises";
29110
28987
 
29111
28988
  // src/commands/output.ts
29112
28989
  var formats = ["text", "json"];
@@ -29141,9 +29018,9 @@ var section = (label, lines2, indent = " ") => lines2.length ? [label, ...lines
29141
29018
  var text2 = (lines2) => lines2.join("\n") + "\n";
29142
29019
 
29143
29020
  // src/commands/guide.ts
29144
- var guideTopics = ["workflow", "spec", "design", "instructions", "records", "writing", "commit", "migrate"];
29021
+ var guideTopics = ["workflow", "spec", "design", "records", "writing", "commit", "migrate"];
29145
29022
  var isGuideTopic = (value) => guideTopics.includes(value);
29146
- 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");
29147
29024
  function guideSummary(source2) {
29148
29025
  const { fields: fields2 } = parseFrontmatterBlock(source2.replace(/\r\n/g, "\n"), "guide");
29149
29026
  const value = (key) => {
@@ -29184,7 +29061,7 @@ function boilerplateFor(path3) {
29184
29061
  const name2 = path3 === "AGENTS.md" ? path3 : path3.split("/").pop().replace(/^\./, "").replace(/\.md$/, "");
29185
29062
  return "# " + name2 + "\n\nProject-specific guidance for AI coding agents.\n";
29186
29063
  }
29187
- 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");
29188
29065
  async function renderAgentBlock(controls = {}, lang = getLanguage()) {
29189
29066
  const body2 = await (controls.readBlock ?? readBundledBlock)(lang);
29190
29067
  return AGENT_START + "\n" + body2.trimEnd().replace(/\{topics\}/g, guideTopics.join(", ")) + "\n" + AGENT_END;
@@ -29213,15 +29090,9 @@ function isWrapperFile(text4) {
29213
29090
  return lines2.length > 0 && lines2.every((line2) => /^@(?:\.\/)?[^\s]+$/.test(line2) && candidatePaths.includes(line2.replace(/^@(?:\.\/)?/, "")));
29214
29091
  }
29215
29092
  var CLAUDE_WRAPPER_PATH = "CLAUDE.md";
29216
- function claudeWrapperFor(agents2) {
29217
- return "@AGENTS.md" + eolOf(agents2);
29218
- }
29219
29093
  function isGeneratedClaudeWrapper(text4) {
29220
29094
  return text4.trim() === "@AGENTS.md";
29221
29095
  }
29222
- function lacksClaudeFile(existing) {
29223
- return !existing.has("CLAUDE.md") && !existing.has(".claude/CLAUDE.md");
29224
- }
29225
29096
  function resolveAgentPaths(preset, existing) {
29226
29097
  if (preset && preset !== "all") {
29227
29098
  const order = agentPresets[preset];
@@ -29255,7 +29126,7 @@ function removeBlock(text4, boilerplate) {
29255
29126
  }
29256
29127
 
29257
29128
  // src/commands/agent-docs.ts
29258
- var skippedAgentDocs = (language = explicitLanguage() ?? getLanguage()) => ({ mode: "skip", paths: [], writes: [], missing: [], language });
29129
+ var skippedAgentDocs = (language = explicitLanguage() ?? getLanguage()) => ({ mode: "skip", paths: [], writes: [], language });
29259
29130
  var blockOf = (text4) => {
29260
29131
  try {
29261
29132
  const found = findBlock(text4);
@@ -29290,7 +29161,7 @@ async function planAgentDocs(root, options) {
29290
29161
  paths2.push(CLAUDE_WRAPPER_PATH);
29291
29162
  writes.push({ path: CLAUDE_WRAPPER_PATH, previous: wrapper, next: null });
29292
29163
  }
29293
- return { mode: "remove", paths: paths2, writes, missing: [], language };
29164
+ return { mode: "remove", paths: paths2, writes, language };
29294
29165
  }
29295
29166
  let block;
29296
29167
  const blockFor = async () => block ??= await renderAgentBlock(options, language);
@@ -29305,19 +29176,11 @@ async function planAgentDocs(root, options) {
29305
29176
  paths2.push(create);
29306
29177
  writes.push({ path: create, previous: null, next: injectBlock(null, await blockFor(), boilerplateFor(create)) });
29307
29178
  }
29308
- const missing2 = [];
29309
- if (paths2.includes("AGENTS.md") && lacksClaudeFile(existing)) {
29310
- if (options.onlyExisting) missing2.push(CLAUDE_WRAPPER_PATH);
29311
- else {
29312
- paths2.push(CLAUDE_WRAPPER_PATH);
29313
- writes.push({ path: CLAUDE_WRAPPER_PATH, previous: null, next: claudeWrapperFor(existing.get("AGENTS.md") ?? null) });
29314
- }
29315
- }
29316
29179
  for (const [path3, text4] of existing) {
29317
29180
  if (paths2.includes(path3) || !isWrapperFile(text4) || !findBlock(text4)) continue;
29318
29181
  writes.push({ path: path3, previous: text4, next: removeBlock(text4, boilerplateFor(path3)) });
29319
29182
  }
29320
- return { mode: "install", paths: paths2, writes, missing: missing2, language };
29183
+ return { mode: "install", paths: paths2, writes, language };
29321
29184
  }
29322
29185
  async function applyAgentDocs(root, plan, recheck) {
29323
29186
  for (const write of plan.writes) await managedWrite(root, write.path, write.previous, write.next, recheck);
@@ -29426,6 +29289,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
29426
29289
  current2 = next;
29427
29290
  }
29428
29291
  await applyAgentDocs(root, docsPlan, unchanged);
29292
+ if (agentDocs && !(config3.cli && isNewerRelease(config3.cli, agentDocs.version))) await refreshLauncher(root);
29429
29293
  return result(settled2.config, "already-initialized", await writeLineEndings(root));
29430
29294
  };
29431
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" } };
@@ -29437,13 +29301,14 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
29437
29301
  };
29438
29302
  await published();
29439
29303
  await applyAgentDocs(root, docsPlan, published);
29304
+ if (agentDocs) await refreshLauncher(root);
29440
29305
  return result(config2, outcome, await writeLineEndings(root));
29441
29306
  };
29442
29307
  const old = await readConfigFile(root);
29443
29308
  if (old !== void 0) return existing(old);
29444
29309
  if (first.trackedConfig) throw new InitError("CONFIG_DELETED", t2("init.configDeleted"));
29445
29310
  const checkRecords = async () => {
29446
- const entries = await readdir5(join14(root, ".gitifact")).catch((e) => {
29311
+ const entries = await readdir4(join13(root, ".gitifact")).catch((e) => {
29447
29312
  if (e.code === "ENOENT") return [];
29448
29313
  throw e;
29449
29314
  });
@@ -29481,22 +29346,22 @@ var fetchLatestVersion = async (signal) => {
29481
29346
  };
29482
29347
 
29483
29348
  // src/adapters/filesystem/user-cache.ts
29484
- 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";
29485
29350
  import { homedir } from "node:os";
29486
- import { join as join15 } from "node:path";
29351
+ import { join as join14 } from "node:path";
29487
29352
  import { randomUUID as randomUUID5 } from "node:crypto";
29488
29353
  function userCacheDirectory(env = process.env, platform = process.platform, home = homedir()) {
29489
29354
  if (env.GITIFACT_CACHE_DIR) return env.GITIFACT_CACHE_DIR;
29490
- if (platform === "win32") return join15(env.LOCALAPPDATA || join15(home, "AppData", "Local"), "gitifact");
29491
- if (platform === "darwin") return join15(home, "Library", "Caches", "gitifact");
29492
- 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");
29493
29358
  }
29494
- var cacheFile = (env) => join15(userCacheDirectory(env), "update.json");
29359
+ var cacheFile = (env) => join14(userCacheDirectory(env), "update.json");
29495
29360
  var isTime = (value) => typeof value === "string" && !Number.isNaN(Date.parse(value));
29496
29361
  async function readUpdateCache(env = process.env) {
29497
29362
  let value;
29498
29363
  try {
29499
- value = JSON.parse(await readFile13(cacheFile(env), "utf8"));
29364
+ value = JSON.parse(await readFile12(cacheFile(env), "utf8"));
29500
29365
  } catch {
29501
29366
  return {};
29502
29367
  }
@@ -29511,11 +29376,11 @@ async function readUpdateCache(env = process.env) {
29511
29376
  }
29512
29377
  async function changeUpdateCache(env, change2) {
29513
29378
  const directory = userCacheDirectory(env);
29514
- const temporary = join15(directory, "update-" + randomUUID5() + ".tmp");
29379
+ const temporary = join14(directory, "update-" + randomUUID5() + ".tmp");
29515
29380
  try {
29516
29381
  const next = change2(await readUpdateCache(env));
29517
- await mkdir5(directory, { recursive: true });
29518
- await writeFile3(temporary, JSON.stringify(next) + "\n", "utf8");
29382
+ await mkdir6(directory, { recursive: true });
29383
+ await writeFile4(temporary, JSON.stringify(next) + "\n", "utf8");
29519
29384
  await rename3(temporary, cacheFile(env));
29520
29385
  } catch {
29521
29386
  await unlink3(temporary).catch(() => {
@@ -29544,7 +29409,7 @@ async function runInit(options, version2) {
29544
29409
  await recordUpdateCheck(process.env, dto.update);
29545
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") + ")");
29546
29411
  const lineEndings = dto.lineEndings.created ? t2("init.text.lineEndings", { path: dto.lineEndings.path }) + "\n" : "";
29547
- 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" : "";
29548
29413
  process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
29549
29414
  ${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
29550
29415
  ${t2("init.text.agentDocs")}: ${docs}
@@ -29559,17 +29424,17 @@ ${lineEndings}${newer}` : JSON.stringify(dto) + "\n");
29559
29424
  }
29560
29425
 
29561
29426
  // src/commands/documents.ts
29562
- import { readFile as readFile14 } from "node:fs/promises";
29563
- import { join as join17 } from "node:path";
29427
+ import { readFile as readFile13 } from "node:fs/promises";
29428
+ import { join as join16 } from "node:path";
29564
29429
 
29565
29430
  // src/commands/project.ts
29566
- import { join as join16 } from "node:path";
29431
+ import { join as join15 } from "node:path";
29567
29432
  var operations = ["MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply", "sequencer", "BISECT_START", "index.lock"];
29568
29433
  async function openProject(cwd, options = {}) {
29569
29434
  const reader = storeReader(cwd);
29570
29435
  const location = await reader.location();
29571
29436
  if (options.writing) {
29572
- 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 }));
29573
29438
  if (await reader.hasUnmerged()) throw new InitError("GIT_OPERATION_IN_PROGRESS", t2("git.resolveConflicts"));
29574
29439
  }
29575
29440
  const raw = await readConfigFile(location.root);
@@ -29728,12 +29593,11 @@ var stateLabel = {
29728
29593
  };
29729
29594
  var stateMark = (state2) => state2 === "committed" ? "" : " (" + stateLabel[state2]() + ")";
29730
29595
  var line = (doc) => `${doc.id} ${doc.title}${draftMark(doc)} \u2014 ${doc.description}`;
29731
- var fileLine = (f) => f.title ? `${f.path} ${f.title} \u2014 ${f.description}` : f.path;
29732
29596
  async function working(project) {
29733
29597
  const { documents } = await project.cache.documents.list();
29734
29598
  return {
29735
29599
  byId: new Map(documents.map((d) => [d.id, d])),
29736
- read: (path3) => readFile14(join17(project.root, ...path3.split("/")), "utf8"),
29600
+ read: (path3) => readFile13(join16(project.root, ...path3.split("/")), "utf8"),
29737
29601
  referencing: async (id) => [...new Set((await project.cache.documents.referencing(id)).map((r) => r.from))]
29738
29602
  };
29739
29603
  }
@@ -29746,12 +29610,12 @@ async function committed(project, commit) {
29746
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)
29747
29611
  };
29748
29612
  }
29749
- async function showDocuments(project, ids, at, kinds, otherResource) {
29613
+ async function showDocuments(project, ids, at, kinds) {
29750
29614
  const view = at === void 0 ? await working(project) : await committed(project, at);
29751
29615
  const unknown2 = ids.filter((id) => !view.byId.has(id));
29752
29616
  if (unknown2.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: unknown2.join(", ") }));
29753
29617
  const other = ids.filter((id) => !kinds.includes(view.byId.get(id).kind));
29754
- 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(", ") }));
29755
29619
  const shown = await Promise.all([...new Set(ids)].map(async (id) => {
29756
29620
  const doc = view.byId.get(id);
29757
29621
  const references = doc.kind === "design" ? [
@@ -29759,34 +29623,30 @@ async function showDocuments(project, ids, at, kinds, otherResource) {
29759
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 })
29760
29624
  ] : [];
29761
29625
  const referencedBy = (await view.referencing(id)).map((from) => ({ id: from, title: view.byId.get(from)?.title ?? null }));
29762
- const files = doc.kind === "instruction" && at === void 0 ? { files: (await readInstructionFiles(project.root, doc.path)).files } : {};
29763
- 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 };
29764
29627
  }));
29765
29628
  const named = (r) => r.id ? r.id + " " + (r.title ?? "(" + t2("docs.missing") + ")") : r.title + " <" + r.url + ">";
29766
29629
  const blocks = shown.map((d) => [
29767
29630
  `== ${d.id} ${d.path}${at ? " @ " + at.slice(0, 7) : ""}`,
29768
29631
  d.text.replace(/\n$/, ""),
29769
29632
  ...section("-- " + t2("docs.references"), d.references.map(named)),
29770
- ...section("-- " + t2("docs.referencedBy"), d.referencedBy.map(named)),
29771
- ...section("-- " + t2("docs.files"), (d.files ?? []).map(fileLine))
29633
+ ...section("-- " + t2("docs.referencedBy"), d.referencedBy.map(named))
29772
29634
  ].join("\n"));
29773
29635
  return { json: { ...at ? { commit: at } : {}, documents: shown }, text: blocks.join("\n\n") + "\n" };
29774
29636
  }
29775
29637
  var pathExample = {
29776
29638
  feature: () => t2("docs.pathExample.feature"),
29777
29639
  requirement: () => t2("docs.pathExample.requirement"),
29778
- design: () => t2("docs.pathExample.design"),
29779
- instruction: () => t2("docs.pathExample.instruction")
29640
+ design: () => t2("docs.pathExample.design")
29780
29641
  };
29781
29642
  var skeleton = {
29782
29643
  feature: () => t2("docs.skeleton.feature"),
29783
29644
  requirement: () => t2("docs.skeleton.requirement"),
29784
- design: () => t2("docs.skeleton.design"),
29785
- instruction: () => t2("docs.skeleton.instruction")
29645
+ design: () => t2("docs.skeleton.design")
29786
29646
  };
29787
29647
  function newPath(kind2, path3) {
29788
- 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`;
29789
- 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" ? /^[^/]+$/ : /^[^/]+\/[^/]+$/;
29790
29650
  let where;
29791
29651
  try {
29792
29652
  where = shape.test(path3) ? classifyDocPath(target) : void 0;
@@ -29800,19 +29660,24 @@ async function createDocument(project, kind2, path3, options) {
29800
29660
  for (const [field, value] of [["title", options.title], ["description", options.description]]) {
29801
29661
  if (/[\r\n]/.test(value)) throw new CommandError("INVALID_VALUE", t2("docs.singleLine", { field }));
29802
29662
  }
29663
+ if (options.style && kind2 !== "requirement") throw new CommandError("INVALID_VALUE", t2("docs.styleRequirementOnly"));
29664
+ const projectStyle = project.config.requirementStyle;
29665
+ const style2 = kind2 === "requirement" ? options.style ?? projectStyle ?? "default" : void 0;
29666
+ const styleKey = style2 === "usecase" || style2 === "default" && projectStyle === "usecase" ? { style: style2 } : {};
29803
29667
  const target = newPath(kind2, path3);
29804
29668
  const { documents } = await project.cache.documents.list();
29805
29669
  const taken = new Set(documents.map((d) => d.id));
29806
29670
  let id;
29807
29671
  do
29808
- id = generateId({ feature: "S", requirement: "R", design: "D", instruction: "I" }[kind2]);
29672
+ id = generateId({ feature: "S", requirement: "R", design: "D" }[kind2]);
29809
29673
  while (taken.has(id));
29810
29674
  const folder = target.slice(0, target.lastIndexOf("/") + 1);
29811
29675
  const siblings = documents.filter((d) => (d.kind === "requirement" || d.kind === "design") && d.path.startsWith(folder) && !d.path.slice(folder.length).includes("/"));
29812
29676
  const order = Math.min(999999, Math.max(0, ...siblings.map((d) => d.order)) + 10);
29813
- const base = { id, path: target, title: options.title.trim(), description: options.description.trim(), body: skeleton[kind2](), draft: true };
29677
+ const body2 = style2 === "usecase" ? t2("docs.skeleton.requirementUsecase") : skeleton[kind2]();
29678
+ const base = { id, path: target, title: options.title.trim(), description: options.description.trim(), body: body2, draft: true };
29814
29679
  const feature2 = target.split("/")[2];
29815
- const doc = kind2 === "feature" ? { kind: kind2, feature: feature2, ...base } : kind2 === "instruction" ? { kind: kind2, name: path3, ...base } : kind2 === "requirement" ? { kind: kind2, feature: feature2, order, ...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 };
29816
29681
  const rendered = renderDocumentFile(doc);
29817
29682
  parseDocumentFile(target, rendered);
29818
29683
  try {
@@ -29835,11 +29700,11 @@ function parseLimit(value) {
29835
29700
  if (!/^[1-9]\d{0,5}$/.test(value)) throw new InvalidArgumentError(t2("list.limit"));
29836
29701
  return Number(value);
29837
29702
  }
29838
- var parseFields = (value) => value.split(",").map((f) => f.trim()).filter(Boolean);
29839
- function checkFields(fields2, columns3) {
29703
+ var parseFields = (value) => value.split(/[\s,]+/).filter(Boolean);
29704
+ function checkFields(fields2, columns2) {
29840
29705
  if (!fields2) return void 0;
29841
- const unknown2 = fields2.filter((f) => !columns3.includes(f));
29842
- 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(", ") }));
29843
29708
  return [...new Set(fields2)];
29844
29709
  }
29845
29710
  function cell(value) {
@@ -29877,6 +29742,7 @@ function aside(format2, lines2) {
29877
29742
 
29878
29743
  // src/commands/specs.ts
29879
29744
  var specKinds = ["feature", "requirement", "design"];
29745
+ var specStyles = requirementStyles;
29880
29746
  var specSorts = ["order", "title", "updated"];
29881
29747
  var columns = ["id", "kind", "feature", "path", "title", "description", "order", "draft", "state", "previousPath", "requirements", "sources", "designs", "updated", "line"];
29882
29748
  var kindLabel = { feature: () => t2("docs.feature"), requirement: () => t2("docs.requirement"), design: () => t2("docs.design") };
@@ -29926,120 +29792,14 @@ var runSpecsList = (options) => runCommand("specs", options.format, async () =>
29926
29792
  var runSpecsShow = (ids, options) => runCommand("specs", options.format, async () => {
29927
29793
  const project = await openProject(process.cwd());
29928
29794
  const at = options.ref === void 0 ? void 0 : await project.reader.resolve(options.ref);
29929
- return showDocuments(project, ids, at, specKinds, "instructions show");
29795
+ return showDocuments(project, ids, at, at === void 0 ? specKinds : [...specKinds, "instruction"]);
29930
29796
  });
29931
29797
  var runSpecsNew = (kind2, path3, options) => runCommand("specs", options.format, async () => createDocument(await openProject(process.cwd()), kind2, path3, options));
29932
29798
 
29933
- // src/queries/instructions.ts
29934
- import { lstat as lstat12 } from "node:fs/promises";
29935
- import { join as join18 } from "node:path";
29936
- var AGENTS = "AGENTS.md";
29937
- async function listInstructions(source2, options, request) {
29938
- const listed3 = await source2.cache.documents.list();
29939
- const problems = listed3.problems;
29940
- const states = await documentStates(source2, listed3.documents);
29941
- const documents = states.documents;
29942
- const agents2 = { path: AGENTS, exists: !!(await lstat12(join18(source2.root, AGENTS)).catch(() => void 0))?.isFile() };
29943
- const historyNeeded = options.sort === "updated" || options.author !== void 0 || !!options.withUpdated;
29944
- const latest = /* @__PURE__ */ new Map();
29945
- const newest = /* @__PURE__ */ new Map();
29946
- let touched;
29947
- if (historyNeeded) {
29948
- const head = await source2.head();
29949
- const changes2 = head ? await source2.cache.history.changesOf(head) : [];
29950
- changes2.forEach((c, rank) => {
29951
- if (!latest.has(c.id)) {
29952
- latest.set(c.id, { commit: c.commit, date: c.date, author: c.author, email: c.email });
29953
- newest.set(c.id, rank);
29954
- }
29955
- });
29956
- if (options.author !== void 0) {
29957
- const by = byAuthor(options.author);
29958
- touched = new Set(changes2.filter(by).map((c) => c.id));
29959
- }
29960
- }
29961
- const matched = options.q !== void 0 ? await source2.cache.documents.matching(options.q) : void 0;
29962
- 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);
29963
- const rows = await Promise.all(instructions2.map(async (d) => {
29964
- const state2 = states.state(d.id);
29965
- const { files, limited } = state2 === "deleted" ? { files: [], limited: false } : await readInstructionFiles(source2.root, d.path);
29966
- return {
29967
- id: d.id,
29968
- name: d.name,
29969
- path: d.path,
29970
- title: d.title,
29971
- description: d.description,
29972
- ...d.draft ? { draft: true } : {},
29973
- state: state2,
29974
- ...states.previousPath(d.id) ? { previousPath: states.previousPath(d.id) } : {},
29975
- files,
29976
- ...limited ? { filesLimited: true } : {},
29977
- ...historyNeeded ? { updated: latest.get(d.id) ?? null } : {},
29978
- ...matched ? { line: matched.get(d.id) } : {}
29979
- };
29980
- }));
29981
- if (options.sort === "updated") rows.sort((a, b) => (newest.get(a.id) ?? Infinity) - (newest.get(b.id) ?? Infinity));
29982
- const page = pageOf(rows, (r) => r.id, request);
29983
- if (!page) return void 0;
29984
- return { agents: agents2, problems, page };
29985
- }
29986
-
29987
- // src/commands/instructions.ts
29988
- var instructionSorts = ["name", "updated"];
29989
- var columns2 = ["id", "name", "path", "title", "description", "draft", "state", "previousPath", "files", "updated", "line"];
29990
- var runInstructionsList = (options) => runCommand("instructions", options.format, async () => {
29991
- const fields2 = checkFields(options.fields, columns2);
29992
- const project = await openProject(process.cwd());
29993
- const listed3 = await listInstructions(project, { ...options, withUpdated: !!fields2?.includes("updated") }, options) ?? cursorGone(options);
29994
- const { agents: agents2, problems, page } = listed3;
29995
- const shown = page.rows;
29996
- const paging = { total: page.total, next: page.next, unit: "instruction" };
29997
- const unreadable2 = problems.filter((p) => p.path.startsWith(INSTRUCTIONS_ROOT + "/"));
29998
- if (fields2) {
29999
- const picked = selected(shown, fields2);
30000
- return {
30001
- json: { agents: agents2, instructions: picked.json, problems: unreadable2, page: paging },
30002
- text: selected(shown.map((r) => ({ ...r, files: r.files.map((f) => f.path) })), fields2).text + aside(options.format, pageLine(page))
30003
- };
30004
- }
30005
- const out = [agents2.exists ? t2("instructions.agents") : t2("instructions.noAgents")];
30006
- for (const r of shown) {
30007
- const when = r.updated ? ` \xB7 ${r.updated.date.slice(0, 10)} ${r.updated.author}` : "";
30008
- out.push(`${r.id} ${r.title}${draftMark(r)}${stateMark(r.state)} (${r.name}) \u2014 ${r.description}${when}`, ...r.line ? [" " + r.line] : []);
30009
- out.push(...r.files.map((f) => " " + fileLine(f)), ...r.filesLimited ? [" " + t2("instructions.filesLimited", { count: r.files.length })] : []);
30010
- }
30011
- if (!shown.length) out.push(options.q !== void 0 || options.author !== void 0 ? t2("docs.noMatch") : t2("instructions.empty"));
30012
- out.push(...pageLine(page));
30013
- if (unreadable2.length) out.push(t2("docs.unreadable", { count: unreadable2.length }));
30014
- return { json: { agents: agents2, instructions: shown, problems: unreadable2, page: paging }, text: text2(out) };
30015
- });
30016
- async function resolveInstructions(project, targets, at) {
30017
- const view = at === void 0 ? await working(project) : await committed(project, at);
30018
- const byName = new Map([...view.byId.values()].filter((d) => d.kind === "instruction").map((d) => [d.name, d.id]));
30019
- return targets.map((target) => byName.get(target) ?? target);
30020
- }
30021
- var runInstructionsShow = (targets, options) => runCommand("instructions", options.format, async () => {
30022
- const project = await openProject(process.cwd());
30023
- if (options.file !== void 0 && (targets.length !== 1 || options.ref !== void 0)) throw new CommandError("INVALID_VALUE", t2("instructions.fileWithOne"));
30024
- const at = options.ref === void 0 ? void 0 : await project.reader.resolve(options.ref);
30025
- const ids = await resolveInstructions(project, targets, at);
30026
- if (options.file === void 0) return showDocuments(project, ids, at, ["instruction"], "specs show");
30027
- const instruction2 = (await working(project)).byId.get(ids[0]);
30028
- if (instruction2?.kind !== "instruction") throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: targets[0] }));
30029
- const found = await readInstructionFile(project.root, instruction2.path, options.file);
30030
- if (!found) throw new CommandError("UNKNOWN_FILE", t2("instructions.unknownFile", { path: options.file, name: instruction2.name }));
30031
- const note = found.text !== null ? [] : [found.tooLarge ? t2("instructions.fileTooLarge", { size: found.size }) : t2("instructions.fileBinary", { size: found.size })];
30032
- return {
30033
- json: { id: instruction2.id, name: instruction2.name, file: { path: options.file, ...found } },
30034
- text: text2([`== ${instruction2.id} ${instruction2.name}/${options.file}`, ...found.text !== null ? [found.text.replace(/\n$/, "")] : note])
30035
- };
30036
- });
30037
- var runInstructionsNew = (name2, options) => runCommand("instructions", options.format, async () => createDocument(await openProject(process.cwd()), "instruction", name2, options));
30038
-
30039
29799
  // src/adapters/filesystem/document-warnings.ts
30040
- import { lstat as lstat13, readdir as readdir6 } from "node:fs/promises";
30041
- import { join as join19 } from "node:path";
30042
- 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) => {
30043
29803
  if (e.code === "ENOENT") return void 0;
30044
29804
  throw e;
30045
29805
  });
@@ -30047,20 +29807,20 @@ var posix = (path3) => path3.split("\\").join("/");
30047
29807
  async function listAssets(root) {
30048
29808
  const out = [];
30049
29809
  async function visit2(relative, depth) {
30050
- const stat3 = await info2(join19(root, relative));
29810
+ const stat3 = await info2(join17(root, relative));
30051
29811
  if (!stat3 || stat3.isSymbolicLink()) return;
30052
29812
  if (stat3.isDirectory()) {
30053
29813
  if (depth > 8 || out.length > 4e3) return;
30054
- 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);
30055
29815
  } else if (stat3.isFile()) out.push({ path: posix(relative), bytes: stat3.size });
30056
29816
  }
30057
29817
  await visit2(ASSETS_DIR, 0);
30058
29818
  return out;
30059
29819
  }
30060
29820
  async function readDocumentWarnings(root, documents) {
30061
- 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/")))];
30062
29822
  const existing = /* @__PURE__ */ new Set();
30063
- 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);
30064
29824
  return documentWarnings(documents, await listAssets(root), existing);
30065
29825
  }
30066
29826
 
@@ -30084,31 +29844,31 @@ var runCheck = (options) => runCommand("check", options.format, async () => {
30084
29844
 
30085
29845
  // src/commands/changes.ts
30086
29846
  import { randomUUID as randomUUID6 } from "node:crypto";
30087
- import { mkdir as mkdir7, open as open5, readFile as readFile17, rename as rename4, rmdir, unlink as unlink5, writeFile as writeFile5 } from "node:fs/promises";
30088
- 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";
30089
29849
 
30090
29850
  // src/adapters/filesystem/agent-inputs.ts
30091
29851
  import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
30092
- 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";
30093
29853
  import { tmpdir } from "node:os";
30094
- import { basename, dirname as dirname2, join as join20 } from "node:path";
29854
+ import { basename, dirname as dirname2, join as join18 } from "node:path";
30095
29855
  var maxAge = 7 * 24 * 60 * 60 * 1e3;
30096
- var info3 = (path3) => lstat14(path3).catch((e) => {
29856
+ var info3 = (path3) => lstat12(path3).catch((e) => {
30097
29857
  if (e.code === "ENOENT") return void 0;
30098
29858
  throw e;
30099
29859
  });
30100
29860
  var comparable = (path3) => process.platform === "win32" ? path3.toLowerCase() : path3;
30101
29861
  async function candidates(root, controls) {
30102
29862
  const key = createHash4("sha256").update(comparable(await realpath3(root))).digest("hex").slice(0, 16);
30103
- 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") };
30104
29864
  }
30105
29865
  async function writable(directory) {
30106
29866
  try {
30107
- await mkdir6(directory, { recursive: true });
29867
+ await mkdir7(directory, { recursive: true });
30108
29868
  const stat3 = await info3(directory);
30109
29869
  if (!stat3?.isDirectory() || stat3.isSymbolicLink()) return false;
30110
- const probe = join20(directory, ".probe-" + randomBytes2(8).toString("hex"));
30111
- await writeFile4(probe, "", { flag: "wx" });
29870
+ const probe = join18(directory, ".probe-" + randomBytes2(8).toString("hex"));
29871
+ await writeFile5(probe, "", { flag: "wx" });
30112
29872
  await unlink4(probe);
30113
29873
  return true;
30114
29874
  } catch {
@@ -30121,18 +29881,18 @@ async function prepareAgentInputs(root, controls = {}) {
30121
29881
  if (await writable(system)) directory = system;
30122
29882
  else if (await writable(project)) {
30123
29883
  directory = project;
30124
- await writeFile4(join20(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
29884
+ await writeFile5(join18(project, ".gitignore"), "*\n", { flag: "wx" }).catch((e) => {
30125
29885
  if (e.code !== "EEXIST") throw e;
30126
29886
  });
30127
29887
  }
30128
29888
  if (!directory) return void 0;
30129
29889
  const now = (controls.now ?? Date.now)();
30130
- for (const name2 of await readdir7(directory).catch(() => [])) {
29890
+ for (const name2 of await readdir6(directory).catch(() => [])) {
30131
29891
  if (name2 === ".gitignore") continue;
30132
- const stat3 = await info3(join20(directory, name2)).catch(() => void 0);
30133
- 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);
30134
29894
  }
30135
- return { commit: join20(directory, "commit.json") };
29895
+ return { commit: join18(directory, "commit.json") };
30136
29896
  }
30137
29897
  async function discardAgentInput(root, file2, controls = {}) {
30138
29898
  try {
@@ -30154,17 +29914,17 @@ async function discardAgentInput(root, file2, controls = {}) {
30154
29914
 
30155
29915
  // src/commands/commit-files.ts
30156
29916
  import { createHash as createHash5 } from "node:crypto";
30157
- import { lstat as lstat15, readFile as readFile15 } from "node:fs/promises";
30158
- 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";
30159
29919
  var fail3 = (message) => {
30160
29920
  throw new CommandError("INVALID_COMMIT", message);
30161
29921
  };
30162
29922
  var hash2 = (value) => createHash5("sha256").update(value).digest("hex");
30163
- var optional2 = (path3) => readFile15(path3).catch((e) => {
29923
+ var optional2 = (path3) => readFile14(path3).catch((e) => {
30164
29924
  if (e.code === "ENOENT") return null;
30165
29925
  throw e;
30166
29926
  });
30167
- var info4 = (path3) => lstat15(path3).catch((e) => {
29927
+ var info4 = (path3) => lstat13(path3).catch((e) => {
30168
29928
  if (e.code === "ENOENT") return void 0;
30169
29929
  throw e;
30170
29930
  });
@@ -30176,7 +29936,7 @@ var record3 = (path3) => {
30176
29936
  return false;
30177
29937
  }
30178
29938
  };
30179
- var instructionFile2 = (path3) => {
29939
+ var instructionFile = (path3) => {
30180
29940
  try {
30181
29941
  return classifyDocPath(path3).type === "instruction-file";
30182
29942
  } catch {
@@ -30190,14 +29950,14 @@ async function fingerprint(root, path3) {
30190
29950
  validPath2(path3);
30191
29951
  const parts = path3.split("/");
30192
29952
  for (let i = 1; i <= parts.length; i++) {
30193
- const target = join21(root, ...parts.slice(0, i));
29953
+ const target = join19(root, ...parts.slice(0, i));
30194
29954
  const stat3 = await info4(target);
30195
29955
  if (!stat3) return null;
30196
29956
  if (stat3.isSymbolicLink() || (i < parts.length ? !stat3.isDirectory() : !stat3.isFile())) fail3(t2("commitFiles.linkOrDirectory"));
30197
- 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"));
30198
29958
  if (i === parts.length) {
30199
29959
  if (stat3.nlink !== 1 || stat3.size > 16 * 1024 * 1024) fail3(t2("commitFiles.hardLinkOrSize"));
30200
- 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)]));
30201
29961
  }
30202
29962
  }
30203
29963
  return null;
@@ -30224,16 +29984,16 @@ function policyPaths(files) {
30224
29984
  }
30225
29985
  return [...all].sort();
30226
29986
  }
30227
- 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]);
30228
29988
  function checkStoreSelection(selected2) {
30229
29989
  for (const p of selected2.filter((p2) => p2.startsWith(".gitifact/") && !record3(p2) && !storeFiles.has(p2))) {
30230
- if (!isAssetPath(p) && !instructionFile2(p)) fail3(t2("commitFiles.notRecord", { path: p }));
29990
+ if (!isAssetPath(p) && !instructionFile(p)) fail3(t2("commitFiles.notRecord", { path: p }));
30231
29991
  }
30232
29992
  }
30233
29993
 
30234
29994
  // src/commands/input.ts
30235
- import { readFile as readFile16, stat as stat2 } from "node:fs/promises";
30236
- var LIMIT2 = 1024 * 1024;
29995
+ import { readFile as readFile15, stat as stat2 } from "node:fs/promises";
29996
+ var LIMIT = 1024 * 1024;
30237
29997
  async function readJsonInput(file2, stdin = process.stdin) {
30238
29998
  const bytes = file2 === "-" ? await readStdin(stdin) : await readInputFile(file2);
30239
29999
  try {
@@ -30244,15 +30004,15 @@ async function readJsonInput(file2, stdin = process.stdin) {
30244
30004
  }
30245
30005
  async function readInputFile(path3) {
30246
30006
  const source2 = await stat2(path3).catch(() => void 0);
30247
- if (!source2?.isFile() || source2.size > LIMIT2) throw new CommandError("INVALID_INPUT", t2("input.file"));
30248
- return readFile16(path3);
30007
+ if (!source2?.isFile() || source2.size > LIMIT) throw new CommandError("INVALID_INPUT", t2("input.file"));
30008
+ return readFile15(path3);
30249
30009
  }
30250
30010
  async function readStdin(stream) {
30251
30011
  const chunks = [];
30252
30012
  let size = 0;
30253
30013
  for await (const chunk of stream) {
30254
30014
  size += chunk.length;
30255
- if (size > LIMIT2) throw new CommandError("INVALID_INPUT", t2("input.size"));
30015
+ if (size > LIMIT) throw new CommandError("INVALID_INPUT", t2("input.size"));
30256
30016
  chunks.push(chunk);
30257
30017
  }
30258
30018
  return Buffer.concat(chunks);
@@ -30274,9 +30034,9 @@ var recordLine = (r, changes2) => {
30274
30034
  const changed = new Set(changes2.map((c) => c.id));
30275
30035
  return `${r.id} ${r.title}${r.draft ? " (" + t2("docs.draft") + ")" : ""} \u2014 ${r.docs.map((d) => changed.has(d) ? d : d + "*").join(", ")}`;
30276
30036
  };
30277
- var formatWarnings = (changes2, documents) => {
30037
+ var formatWarnings = (changes2, documents, project) => {
30278
30038
  const touched = new Set(changes2.map((c) => c.id));
30279
- return documents.filter((d) => touched.has(d.id)).flatMap(requirementFormatWarnings);
30039
+ return documents.filter((d) => touched.has(d.id)).flatMap((d) => requirementFormatWarnings(d, project.config.requirementStyle));
30280
30040
  };
30281
30041
  var recordSummary = (r) => ({ id: r.id, title: r.title, docs: r.docs, path: r.path, ...r.draft ? { draft: true } : {} });
30282
30042
  var runChangesList = (options, controls = {}) => runCommand("changes", options.format, async () => {
@@ -30288,7 +30048,7 @@ var runChangesList = (options, controls = {}) => runCommand("changes", options.f
30288
30048
  const withoutRecord = uncovered(changes2, records2);
30289
30049
  const sharedDocuments = shared(changes2, records2);
30290
30050
  const problems = [...working2.problems, ...pending.problems, ...checked.problems];
30291
- const formats2 = formatWarnings(changes2, checked.documents);
30051
+ const formats2 = formatWarnings(changes2, checked.documents, project);
30292
30052
  const warnings = [...await readDocumentWarnings(project.root, checked.documents), ...formats2];
30293
30053
  const inputs = await prepareAgentInputs(project.root, controls).catch(() => void 0);
30294
30054
  const out = changes2.length ? [t2("changes.changed", { count: changes2.length }), ...changes2.map((c) => " " + changeLine(c))] : [t2("changes.none")];
@@ -30326,7 +30086,7 @@ async function commitChanges(cwd, input2, dryRun) {
30326
30086
  if (Object.keys(authorization).sort().join(",") !== "basis,evidence" || !["user-request", "project-policy"].includes(String(authorization.basis))) fail3(t2("commit.authorizationRequired"));
30327
30087
  text3(authorization.evidence, 2e3);
30328
30088
  const message = text3(request.message, 4e3);
30329
- 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"));
30330
30090
  if (request.migration !== void 0 && request.migration !== true) fail3(t2("commit.migrationFlag"));
30331
30091
  const project = await openProject(cwd, { writing: true });
30332
30092
  const { root, gitDir, indexPath, objectFormat } = project;
@@ -30352,7 +30112,7 @@ async function commitChanges(cwd, input2, dryRun) {
30352
30112
  ...stdin ? { input: stdin } : {}
30353
30113
  });
30354
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();
30355
- const busy = join22(gitDir, "gitifact-changes-commit.lock");
30115
+ const busy = join20(gitDir, "gitifact-changes-commit.lock");
30356
30116
  if (await info4(busy)) fail3(t2("commit.busy", { path: busy }));
30357
30117
  if ((await staged()).length) fail3(t2("commit.existingStaging"));
30358
30118
  const indexHash = hash2(await optional2(indexPath) ?? Buffer.alloc(0));
@@ -30370,7 +30130,7 @@ async function commitChanges(cwd, input2, dryRun) {
30370
30130
  const split = changes2.filter((c) => c.previousPath !== void 0 && !(selected2.includes(c.path) && selected2.includes(c.previousPath)));
30371
30131
  if (split.length) fail3(t2("commit.selectMove", { ids: split.map((c) => c.id).join(", ") }));
30372
30132
  const withoutRecord = uncovered(changes2, records2);
30373
- const warnings = formatWarnings(changes2, checked.documents);
30133
+ const warnings = formatWarnings(changes2, checked.documents, project);
30374
30134
  const status = (await git(["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", ".gitifact"])).toString("utf8").split("\0").filter(Boolean);
30375
30135
  const removed = new Set(status.filter((entry2) => entry2.slice(0, 2).includes("D")).map((entry2) => entry2.slice(3)));
30376
30136
  checkStoreSelection(selected2.filter((p) => !(underDocuments(p) && removed.has(p))));
@@ -30392,7 +30152,7 @@ async function commitChanges(cwd, input2, dryRun) {
30392
30152
  if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
30393
30153
  return { outcome: "dry-run", committed: false, ...summary, commit: void 0 };
30394
30154
  }
30395
- const temporary = join22(gitDir, "gitifact-commit-index-" + randomUUID6());
30155
+ const temporary = join20(gitDir, "gitifact-commit-index-" + randomUUID6());
30396
30156
  let owned = false;
30397
30157
  let uncertain = false;
30398
30158
  let commitStarted = false;
@@ -30400,20 +30160,20 @@ async function commitChanges(cwd, input2, dryRun) {
30400
30160
  let committed2;
30401
30161
  try {
30402
30162
  try {
30403
- await mkdir7(busy);
30163
+ await mkdir8(busy);
30404
30164
  owned = true;
30405
30165
  } catch (e) {
30406
30166
  if (e.code === "EEXIST") fail3(t2("commit.busy", { path: busy }));
30407
30167
  throw e;
30408
30168
  }
30409
- const lock = indexLock = await open5(indexPath + ".lock", "wx", 384);
30169
+ const lock = indexLock = await open4(indexPath + ".lock", "wx", 384);
30410
30170
  const original = await optional2(indexPath);
30411
30171
  if (hash2(original ?? Buffer.alloc(0)) !== indexHash || (await staged()).length) fail3(t2("commit.indexChanged"));
30412
30172
  const before = await hashes([...started.keys()]);
30413
30173
  for (const [path3, value] of started) if (before.get(path3) !== value) fail3(t2("commit.fileChangedRunning", { path: path3 }));
30414
- 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 }));
30415
30175
  const locked = await hashes([.../* @__PURE__ */ new Set([...selected2, ...contextPaths])]);
30416
- if (original) await writeFile5(temporary, original, { flag: "wx" });
30176
+ if (original) await writeFile6(temporary, original, { flag: "wx" });
30417
30177
  else await git(["read-tree", "--empty"], temporary);
30418
30178
  await git(["add", "--pathspec-from-file=-", "--pathspec-file-nul"], temporary, Buffer.from(stageable.join("\0")));
30419
30179
  const actual = await staged(temporary);
@@ -30428,14 +30188,14 @@ async function commitChanges(cwd, input2, dryRun) {
30428
30188
  const size = Number(header[2]);
30429
30189
  if (end < 0 || header[1] !== "blob" || !Number.isSafeInteger(size) || output2[end + 1 + size] !== 10) fail3(t2("commit.stagedUnreadable", { path: path3 }));
30430
30190
  const blob = output2.subarray(end + 1, end + 1 + size);
30431
- const file2 = await readFile17(join22(root, path3));
30191
+ const file2 = await readFile16(join20(root, path3));
30432
30192
  if (!blob.equals(file2) && !blob.equals(Buffer.from(file2.toString("latin1").replace(/\r\n/g, "\n"), "latin1"))) fail3(t2("commit.filterChanged"));
30433
30193
  offset = end + size + 2;
30434
30194
  }
30435
30195
  }
30436
30196
  const again = await hashes([...locked.keys()]);
30437
30197
  for (const [path3, value] of locked) if (again.get(path3) !== value) fail3(t2("commit.fileChangedPreparing", { path: path3 }));
30438
- 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"));
30439
30199
  if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
30440
30200
  const tree = (await git(["write-tree"], temporary)).toString("utf8").trim();
30441
30201
  commitStarted = true;
@@ -30450,7 +30210,7 @@ async function commitChanges(cwd, input2, dryRun) {
30450
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"));
30451
30211
  if (hash2(await optional2(indexPath) ?? Buffer.alloc(0)) !== indexHash) fail3(t2("commit.indexChangedAfter"));
30452
30212
  if ((await git(["write-tree"], temporary)).toString("utf8").trim() !== tree) fail3(t2("commit.hookChangedStaging"));
30453
- await lock.writeFile(await readFile17(temporary));
30213
+ await lock.writeFile(await readFile16(temporary));
30454
30214
  await lock.sync();
30455
30215
  await lock.close();
30456
30216
  await rename4(indexPath + ".lock", indexPath);
@@ -30475,7 +30235,7 @@ async function commitChanges(cwd, input2, dryRun) {
30475
30235
  await unlink5(temporary).catch(() => {
30476
30236
  });
30477
30237
  if (owned) {
30478
- await unlink5(join22(busy, "recovery.json")).catch(() => {
30238
+ await unlink5(join20(busy, "recovery.json")).catch(() => {
30479
30239
  });
30480
30240
  await rmdir(busy);
30481
30241
  }
@@ -30485,12 +30245,14 @@ async function commitChanges(cwd, input2, dryRun) {
30485
30245
  }
30486
30246
 
30487
30247
  // src/commands/records.ts
30488
- import { readFile as readFile18 } from "node:fs/promises";
30489
- import { join as join23 } from "node:path";
30248
+ import { readFile as readFile17 } from "node:fs/promises";
30249
+ import { join as join21 } from "node:path";
30490
30250
  var runRecordsNew = (options) => runCommand("records", options.format, async () => {
30491
30251
  if (/[\r\n]/.test(options.title)) throw new CommandError("INVALID_VALUE", t2("docs.singleLine", { field: "title" }));
30492
- const docs = [...new Set(options.docs.flatMap((d) => d.split(",")).map((d) => d.trim()).filter(Boolean))];
30493
- 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(", ") }));
30494
30256
  const project = await openProject(process.cwd());
30495
30257
  let id;
30496
30258
  do
@@ -30522,7 +30284,7 @@ var runRecordsShow = (ids, options) => runCommand("records", options.format, asy
30522
30284
  const shown = await Promise.all([...new Set(ids)].map(async (id) => {
30523
30285
  const path3 = await findRecord(project.root, id);
30524
30286
  if (!path3) return { id, missing: true };
30525
- const source2 = await readFile18(join23(project.root, ...path3.split("/")), "utf8");
30287
+ const source2 = await readFile17(join21(project.root, ...path3.split("/")), "utf8");
30526
30288
  const log = project.reader.decode(await project.reader.run(["log", "-1", "--diff-filter=A", "--format=%H%x00%aN%x00%aI", "--", path3])).trim();
30527
30289
  const [commit, author, date5] = log.split("\0");
30528
30290
  return { id, path: path3, text: source2, commit: commit ? { commit, author, date: date5 } : null };
@@ -30686,11 +30448,11 @@ function newIssueUrl(title, body2, marker2) {
30686
30448
  }
30687
30449
 
30688
30450
  // src/shared/project-config.ts
30689
- import { readFile as readFile19 } from "node:fs/promises";
30690
- 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";
30691
30453
  async function findProjectConfig(from) {
30692
30454
  for (let folder = from; ; folder = dirname3(folder)) {
30693
- 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);
30694
30456
  if (text4 !== void 0) return { root: folder, text: text4 };
30695
30457
  if (dirname3(folder) === folder) return void 0;
30696
30458
  }
@@ -30752,8 +30514,8 @@ var runFeedback = (options, version2, run) => runCommand("feedback", options.for
30752
30514
  });
30753
30515
 
30754
30516
  // src/commands/update.ts
30755
- import { readFile as readFile20 } from "node:fs/promises";
30756
- import { join as join25 } from "node:path";
30517
+ import { readFile as readFile19 } from "node:fs/promises";
30518
+ import { join as join23 } from "node:path";
30757
30519
  import { isDeepStrictEqual } from "node:util";
30758
30520
 
30759
30521
  // src/adapters/git/agent-docs-commit.ts
@@ -30777,6 +30539,10 @@ function agentDocsGit(root, inherited = process.env) {
30777
30539
  async commitOnly(paths2, message) {
30778
30540
  await git(["commit", "--only", "-m", message, "--", ...paths2]);
30779
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
+ },
30780
30546
  async changedPaths(commit) {
30781
30547
  return (await git(["diff-tree", "--no-commit-id", "--name-only", "--no-renames", "-r", "-z", "--root", commit])).toString("utf8").split("\0").filter(Boolean).sort();
30782
30548
  },
@@ -30790,7 +30556,7 @@ function agentDocsGit(root, inherited = process.env) {
30790
30556
  var CONFIG_PATH = ".gitifact/config.json";
30791
30557
  var updateCommitMessage = (version2) => "chore(gitifact): update project to gitifact v" + version2;
30792
30558
  var commitState = (state2, extra = {}) => ({ state: state2, commit: null, paths: [], message: null, reason: null, detail: null, ...extra });
30793
- 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 } };
30794
30560
  async function refreshProject(cwd, version2, env, controls) {
30795
30561
  const repo = initRepository(cwd, env);
30796
30562
  let first;
@@ -30806,13 +30572,12 @@ async function refreshProject(cwd, version2, env, controls) {
30806
30572
  const root = first.state.repository.rootPath;
30807
30573
  const parsed2 = needsMigration(config2) ? void 0 : parseManagedConfig(config2);
30808
30574
  const plan = await planAgentDocs(root, { version: version2, onlyExisting: true, language: parsed2?.language, ...controls });
30809
- const { missing: missing2 } = plan;
30810
- 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 } };
30811
30576
  let current2 = config2;
30812
30577
  const unchanged = async () => {
30813
30578
  if ((await repo.inspect()).stamp !== first.stamp || await readConfigFile(root) !== current2) throw new InitError("INPUT_CHANGED", t2("init.inputChanged"));
30814
30579
  };
30815
- 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 };
30816
30581
  if (!parsed2) return { docs, project: { state: "current", cli: null } };
30817
30582
  const settled2 = settleConfig(parsed2, version2, plan.language);
30818
30583
  if (settled2.changed) {
@@ -30820,6 +30585,7 @@ async function refreshProject(cwd, version2, env, controls) {
30820
30585
  await publishConfig(root, next, unchanged, void 0, true);
30821
30586
  current2 = next;
30822
30587
  }
30588
+ await refreshLauncher(root);
30823
30589
  return { docs: { ...docs, root }, project: { state: settled2.changed ? "written" : "current", cli: settled2.config.cli ?? null } };
30824
30590
  }
30825
30591
  function outsideBlock(text4) {
@@ -30854,7 +30620,7 @@ async function commitUpdate(docs, version2, env) {
30854
30620
  if (before === null || before !== outsideBlock(working2)) return commitState("skipped", { paths: [path3], reason: "other-changes" });
30855
30621
  changed.push(path3);
30856
30622
  }
30857
- 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);
30858
30624
  const committedConfig = head ? await git.committed(head, CONFIG_PATH) : null;
30859
30625
  if (workingConfig !== null && committedConfig?.replace(/\r\n/g, "\n") !== workingConfig.replace(/\r\n/g, "\n")) {
30860
30626
  if (committedConfig === null) return commitState("skipped", { paths: [CONFIG_PATH], reason: "untracked" });
@@ -30862,10 +30628,14 @@ async function commitUpdate(docs, version2, env) {
30862
30628
  if (before === null || !isDeepStrictEqual(before, withoutSettings(workingConfig))) return commitState("skipped", { paths: [CONFIG_PATH], reason: "other-changes" });
30863
30629
  changed.push(CONFIG_PATH);
30864
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);
30865
30634
  if (changed.length === 0) return commitState("nothing");
30866
30635
  const message = updateCommitMessage(version2);
30867
30636
  let failure2;
30868
30637
  try {
30638
+ if (workingLauncher !== null && committedLauncher === null) await git.intendToAdd([LAUNCHER_PATH]);
30869
30639
  await git.commitOnly(changed, message);
30870
30640
  } catch (error62) {
30871
30641
  failure2 = error62;
@@ -30892,7 +30662,8 @@ async function updateCommand(cwd, version2, env = process.env, controls = {}) {
30892
30662
  cliVersion: version2,
30893
30663
  update,
30894
30664
  install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
30895
- 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: [] },
30896
30667
  project,
30897
30668
  commit,
30898
30669
  migrationRequired: config2 !== void 0 && needsMigration(config2)
@@ -30908,11 +30679,10 @@ async function runUpdate(options, version2) {
30908
30679
  }
30909
30680
  const lines2 = [t2("update.text.current", { version: dto.cliVersion })];
30910
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"));
30911
- 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"));
30912
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"));
30913
- if (dto.agentDocs.missing.length) lines2.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
30914
30684
  if (dto.project.state === "written") lines2.push(t2("update.text.projectWritten", { path: CONFIG_PATH, version: dto.project.cli ?? dto.cliVersion }));
30915
- 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 }));
30916
30686
  if (dto.migrationRequired) lines2.push(t2("update.text.migrationRequired", { version: dto.cliVersion }));
30917
30687
  const { commit } = dto;
30918
30688
  if (commit.state === "committed") lines2.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
@@ -30955,7 +30725,7 @@ async function runUpdateCheck(format2, version2) {
30955
30725
  t2("update.text.current", { version: dto.cliVersion }),
30956
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")
30957
30727
  ];
30958
- 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"));
30959
30729
  process.stdout.write(lines2.join("\n") + "\n");
30960
30730
  }
30961
30731
 
@@ -30976,8 +30746,8 @@ function chooseNotice(running2, project, cache, now, scope = { release: true, pr
30976
30746
  return null;
30977
30747
  }
30978
30748
  function noticeText(notice, running2) {
30979
- if (notice.kind === "project-newer") return t2("notice.projectNewer", { project: notice.project, version: running2, command: npmGlobalInstall(notice.project) });
30980
- 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) });
30981
30751
  return t2("notice.projectOlder", { project: notice.project, version: running2 });
30982
30752
  }
30983
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);
@@ -31031,17 +30801,17 @@ for (let i = 0; i < languageArgs.length; i++) {
31031
30801
  if (value === "ko" || value === "en") selectedLanguage = value;
31032
30802
  }
31033
30803
  configureLanguage(selectedLanguage ?? environmentLanguage(), selectedLanguage);
31034
- var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.5").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());
31035
30805
  program2.hook("preAction", async (_program, action) => {
31036
30806
  let top = action;
31037
30807
  while (top.parent && top.parent !== program2) top = top.parent;
31038
- if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.8.5", top.name());
30808
+ if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.9.0", top.name());
31039
30809
  });
31040
- program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.8.5"));
30810
+ program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.9.0"));
31041
30811
  var format = () => new Option("--format <format>", t2("help.format")).choices(formats).default("text");
31042
- 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.5"));
31043
- 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.5"));
31044
- 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.5") : options.check ? runUpdateCheck(options.format, "0.8.5") : runUpdate(options, "0.8.5"));
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"));
31045
30815
  var listed2 = (command, sorts) => {
31046
30816
  command.allowExcessArguments(false).option("--q <words>", t2("help.listQ")).option("--author <name>", t2("help.listAuthor"));
31047
30817
  if (sorts) command.addOption(new Option("--sort <key>", t2("help.listSort")).choices([...sorts]).default(sorts[0]));
@@ -31050,11 +30820,7 @@ var listed2 = (command, sorts) => {
31050
30820
  var specs = program2.command("specs").description(t2("help.specs"));
31051
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));
31052
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));
31053
- 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(format()).action((kind2, path3, o) => runSpecsNew(kind2, path3, o));
31054
- var instructions = program2.command("instructions").description(t2("help.instructions"));
31055
- listed2(instructions.command("list").description(t2("help.instructionsList")), instructionSorts).action((o) => runInstructionsList(o));
31056
- 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));
31057
- 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));
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));
31058
30824
  var records = program2.command("records").description(t2("help.records"));
31059
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));
31060
30826
  records.command("show").description(t2("help.recordsShow")).argument("<ids...>", t2("help.recordsShowId")).addOption(format()).action((ids, o) => runRecordsShow(ids, o));
@@ -31063,7 +30829,7 @@ program2.command("check").description(t2("help.check")).allowExcessArguments(fal
31063
30829
  var changes = program2.command("changes").description(t2("help.changes"));
31064
30830
  changes.command("list").description(t2("help.changesList")).allowExcessArguments(false).addOption(format()).action((o) => runChangesList(o));
31065
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));
31066
- 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.5"));
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"));
31067
30833
  var guide = program2.command("guide").description(t2("help.guide"));
31068
30834
  guide.command("list").description(t2("help.guideList")).allowExcessArguments(false).addOption(format()).action((o) => runGuideList(o));
31069
30835
  guide.command("show").description(t2("help.guideShow")).argument("<topic>", t2("help.guideTopic")).allowExcessArguments(false).addOption(format()).action((topic, o) => runGuideShow(topic, o));