gitifact 0.8.4 → 0.8.6

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 (78) hide show
  1. package/README.md +1 -0
  2. package/dist/browser/assets/{HgiRefresh-CAJ0Xdvi.js → HgiRefresh-CnBE2KZ8.js} +1 -1
  3. package/dist/browser/assets/{Markdown-ev4hMr67.js → Markdown-75QVB57V.js} +1 -1
  4. package/dist/browser/assets/Selector-E8jyEKNB.js +2 -0
  5. package/dist/browser/assets/Tab-C-PhLRIZ.js +4 -0
  6. package/dist/browser/assets/{Table-XuC9zK9w.js → Table-Cr2jvka_.js} +2 -2
  7. package/dist/browser/assets/Token-BBk7tRek.js +2 -0
  8. package/dist/browser/assets/Token-CKcN25HT.css +1 -0
  9. package/dist/browser/assets/{about-vlKWfG7J.js → about-C3ewDfU2.js} +2 -2
  10. package/dist/browser/assets/activity-timeline-B3s0viEt.js +2 -0
  11. package/dist/browser/assets/changelog-DkRd6I8J.js +1 -0
  12. package/dist/browser/assets/commit-BHwKZ7hK.js +4 -0
  13. package/dist/browser/assets/{contributor-Bs3RWHQv.js → contributor-CqqvzxLh.js} +1 -1
  14. package/dist/browser/assets/contributors-CuMPZx6_.js +1 -0
  15. package/dist/browser/assets/{contributors._email-BR9sLgCs.js → contributors._email-CEcZZfAR.js} +1 -1
  16. package/dist/browser/assets/{contributors.index-FoYwG7VE.js → contributors.index-DNMBFxLb.js} +1 -1
  17. package/dist/browser/assets/dashboard.index-nV7KwM5H.js +1 -0
  18. package/dist/browser/assets/document-BnxE7Txb.js +2 -0
  19. package/dist/browser/assets/{document-DPtVyR5y.js → document-odLLdugz.js} +4 -4
  20. package/dist/browser/assets/features-C0YzZiS-.js +2 -0
  21. package/dist/browser/assets/features-FKxsPKab.css +1 -0
  22. package/dist/browser/assets/{features._featureId-xxpihTEs.js → features._featureId-Cmg8-2ku.js} +1 -1
  23. package/dist/browser/assets/{features.index-CO7APsrh.js → features.index-CBz7BmRW.js} +1 -1
  24. package/dist/browser/assets/getting-started-CaOhGSBe.js +1 -0
  25. package/dist/browser/assets/git-DwAnKiiN.js +1 -0
  26. package/dist/browser/assets/index-BljzO9go.js +49 -0
  27. package/dist/browser/assets/index-CswY1wJ4.css +1 -0
  28. package/dist/browser/assets/instructions-DWS_nwe0.js +1 -0
  29. package/dist/browser/assets/{instructions._instructionId-Bp9u9xx9.js → instructions._instructionId-l7aWWvfx.js} +1 -1
  30. package/dist/browser/assets/instructions.agents-t7hMe4Pv.js +1 -0
  31. package/dist/browser/assets/instructions.index-CHVCQBSH.js +1 -0
  32. package/dist/browser/assets/{lazyRouteComponent-D4lq6X5s.js → lazyRouteComponent-BZI8yQj_.js} +1 -1
  33. package/dist/browser/assets/load-more-BVsXjzHC.js +1 -0
  34. package/dist/browser/assets/page-header-CRcXie4p.js +248 -0
  35. package/dist/browser/assets/records._recordId-DF-NkdBL.js +1 -0
  36. package/dist/browser/assets/records.commits._commit-BBf_rxHJ.js +1 -0
  37. package/dist/browser/assets/records.docs._docId-Diev5opj.js +1 -0
  38. package/dist/browser/assets/records.index-DPWv9blV.js +1 -0
  39. package/dist/browser/assets/records.working-D5xv-dTD.js +1 -0
  40. package/dist/browser/assets/search-CbtiuqNx.js +1 -0
  41. package/dist/browser/assets/{settings-BNIy3TaP.js → settings-N1hE7l-i.js} +1 -1
  42. package/dist/browser/assets/{useInfiniteQuery-03AIcFvA.js → useInfiniteQuery-CrEm5UW2.js} +1 -1
  43. package/dist/browser/assets/{useKeyboardHint-DN1hAi8g.js → useKeyboardHint-CGJ2GVT3.js} +1 -1
  44. package/dist/browser/index.html +24 -24
  45. package/dist/i18n/en/changelog.md +26 -0
  46. package/dist/i18n/en/docs/design.md +2 -0
  47. package/dist/i18n/en/docs/spec.md +108 -7
  48. package/dist/i18n/ko/changelog.md +26 -0
  49. package/dist/i18n/ko/docs/design.md +2 -0
  50. package/dist/i18n/ko/docs/spec.md +108 -7
  51. package/dist/main.js +399 -174
  52. package/package.json +3 -3
  53. package/dist/browser/assets/Selector-CGOdL2eo.js +0 -2
  54. package/dist/browser/assets/Tab-BwJj0rUr.js +0 -1
  55. package/dist/browser/assets/Token-BV_4bSkY.js +0 -2
  56. package/dist/browser/assets/Token-Bqkc8eyB.css +0 -1
  57. package/dist/browser/assets/activity-timeline-Dmc-97N6.js +0 -2
  58. package/dist/browser/assets/changelog-6QcNKW8u.js +0 -1
  59. package/dist/browser/assets/commit-BcdbBdP4.js +0 -4
  60. package/dist/browser/assets/contributors-B_bVSY4J.js +0 -1
  61. package/dist/browser/assets/dashboard.index-BFp6Hqsf.js +0 -1
  62. package/dist/browser/assets/document-Q3lvgyX8.js +0 -2
  63. package/dist/browser/assets/features-CmvypKgf.js +0 -2
  64. package/dist/browser/assets/features-DUHxz0_L.css +0 -1
  65. package/dist/browser/assets/getting-started-11zYPwlJ.js +0 -1
  66. package/dist/browser/assets/git-CVY8qpaV.js +0 -1
  67. package/dist/browser/assets/index-BeeP8ZFW.js +0 -49
  68. package/dist/browser/assets/index-D1JeDnJc.css +0 -1
  69. package/dist/browser/assets/instructions-D3-EfbZG.js +0 -1
  70. package/dist/browser/assets/instructions.agents-DvFHo8x8.js +0 -1
  71. package/dist/browser/assets/instructions.index-zHzGHq7D.js +0 -1
  72. package/dist/browser/assets/load-more-CymfxLt7.js +0 -1
  73. package/dist/browser/assets/page-header-CVCjLFQo.js +0 -246
  74. package/dist/browser/assets/records._recordId-Be8aM0F-.js +0 -1
  75. package/dist/browser/assets/records.commits._commit-Dm3gX-bQ.js +0 -1
  76. package/dist/browser/assets/records.index-BgfJnGAl.js +0 -1
  77. package/dist/browser/assets/records.working-Eeag2L5N.js +0 -1
  78. package/dist/browser/assets/search-CSSuO-jR.js +0 -1
package/dist/main.js CHANGED
@@ -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}",
@@ -3517,6 +3518,12 @@ var messages_default = {
3517
3518
  "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}",
3518
3519
  "warn.UNREFERENCED_ASSET": "\uC5B4\uB5A4 \uBB38\uC11C\uB3C4 \uAC00\uB9AC\uD0A4\uC9C0 \uC54A\uB294 \uC5D0\uC14B\uC785\uB2C8\uB2E4: {path}",
3519
3520
  "warn.ASSETS_TOTAL_SIZE": "\uC5D0\uC14B \uC804\uCCB4\uAC00 \uAD8C\uC7A5 \uD06C\uAE30(50MB)\uB97C \uB118\uC2B5\uB2C8\uB2E4: {bytes}\uBC14\uC774\uD2B8",
3521
+ "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}",
3522
+ "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}",
3523
+ "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}",
3524
+ "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}",
3525
+ "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}",
3526
+ "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}",
3520
3527
  "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."
3521
3528
  };
3522
3529
 
@@ -3528,6 +3535,7 @@ var messages_default2 = {
3528
3535
  "config.schemaFields": "Check the schemaVersion and baseline fields.",
3529
3536
  "config.cliVersion": "The config field cli must be a release version such as 0.8.3.",
3530
3537
  "config.language": "The config field language must be one of {languages}.",
3538
+ "config.requirementStyle": "The config field requirementStyle must be one of {styles}.",
3531
3539
  "document.pathTooLong": "Wiki page path exceeds the depth or length limit: {path}",
3532
3540
  "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}",
3533
3541
  "document.folderName": "Wiki folder names must use lowercase letters, digits and hyphens: {path}",
@@ -3636,6 +3644,12 @@ var messages_default2 = {
3636
3644
  "warn.ASSET_EXTENSION": "The browser does not preview this extension and offers it only as a download: {path}",
3637
3645
  "warn.UNREFERENCED_ASSET": "No document refers to this asset: {path}",
3638
3646
  "warn.ASSETS_TOTAL_SIZE": "All assets together are over the recommended size (50 MB): {bytes} bytes",
3647
+ "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}",
3648
+ "warn.REQUIREMENT_SCOPE_FORMAT": "Scope and constraints come before the acceptance criteria, as - items only (guide show spec): {path}",
3649
+ "warn.REQUIREMENT_CRITERIA_FORMAT": "The acceptance criteria hold numbered items only; move rules to scope and constraints (guide show spec): {path}",
3650
+ "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}",
3651
+ "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}",
3652
+ "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}",
3639
3653
  "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."
3640
3654
  };
3641
3655
 
@@ -3676,6 +3690,29 @@ async function readRepositoryStatus(reader, services) {
3676
3690
  };
3677
3691
  }
3678
3692
 
3693
+ // ../../packages/core/dist/domain/document.js
3694
+ var DOC_ID_PREFIX = { feature: "S", requirement: "R", design: "D", wiki: "W", instruction: "I" };
3695
+ var RECORD_ID_PREFIX = "DR";
3696
+ var token = "[a-z2-7]{10}";
3697
+ var docIdPattern = new RegExp(`^[SRDWI]-${token}$`);
3698
+ var recordIdPattern = new RegExp(`^DR-${token}$`);
3699
+ var reasonIdPattern = new RegExp(`^H-${token}$`);
3700
+ var idPatternOf = (kind2) => new RegExp(`^${DOC_ID_PREFIX[kind2]}-${token}$`);
3701
+ var kindOfId = (id) => Object.entries(DOC_ID_PREFIX).find(([, prefix]) => id.startsWith(prefix + "-"))?.[0];
3702
+ var requirementStyles = ["default", "usecase"];
3703
+ var docWarning = (code, path3, values = {}) => ({ code, path: path3, message: t(`warn.${code}`, { path: path3, ...values }) });
3704
+ var docProblem = (code, path3, values = {}) => ({ code, path: path3, message: t(`doc.${code}`, { path: path3, ...values }) });
3705
+ var DocumentError = class extends Error {
3706
+ code;
3707
+ path;
3708
+ constructor(code, path3, message) {
3709
+ super(message);
3710
+ this.code = code;
3711
+ this.path = path3;
3712
+ this.name = "DocumentError";
3713
+ }
3714
+ };
3715
+
3679
3716
  // ../../packages/core/dist/formats/spec-project.js
3680
3717
  var InitError = class extends Error {
3681
3718
  code;
@@ -3740,6 +3777,11 @@ function parseManagedConfig(text4) {
3740
3777
  throw new InitError("INVALID_CONFIG", t("config.language", { languages: PROJECT_LANGUAGES.join(", ") }));
3741
3778
  config2.language = value.language;
3742
3779
  }
3780
+ if ("requirementStyle" in value) {
3781
+ if (!requirementStyles.includes(value.requirementStyle))
3782
+ throw new InitError("INVALID_CONFIG", t("config.requirementStyle", { styles: requirementStyles.join(", ") }));
3783
+ config2.requirementStyle = value.requirementStyle;
3784
+ }
3743
3785
  return config2;
3744
3786
  }
3745
3787
  function formatManagedConfig(config2, previous) {
@@ -3747,7 +3789,7 @@ function formatManagedConfig(config2, previous) {
3747
3789
  if (previous !== void 0) {
3748
3790
  const value = JSON.parse(previous);
3749
3791
  if (object(value)) {
3750
- const { schemaVersion: _s, baseline: _b, cli: _c, language: _l, ...rest } = value;
3792
+ const { schemaVersion: _s, baseline: _b, cli: _c, language: _l, requirementStyle: _r, ...rest } = value;
3751
3793
  unknown2 = rest;
3752
3794
  }
3753
3795
  }
@@ -3755,7 +3797,8 @@ function formatManagedConfig(config2, previous) {
3755
3797
  schemaVersion: config2.schemaVersion,
3756
3798
  baseline: config2.baseline,
3757
3799
  ...config2.cli === void 0 ? {} : { cli: config2.cli },
3758
- ...config2.language === void 0 ? {} : { language: config2.language }
3800
+ ...config2.language === void 0 ? {} : { language: config2.language },
3801
+ ...config2.requirementStyle === void 0 ? {} : { requirementStyle: config2.requirementStyle }
3759
3802
  };
3760
3803
  return JSON.stringify({ ...known, ...unknown2 }, null, 2) + "\n";
3761
3804
  }
@@ -3828,7 +3871,7 @@ var WIKI_DIR = ".gitifact/wiki";
3828
3871
  var WIKI_HISTORY_PATH = WIKI_DIR + "/history.jsonl";
3829
3872
  var WIKI_ENTRY = "README.md";
3830
3873
  var WIKI_ENTRY_PATH = WIKI_DIR + "/" + WIKI_ENTRY;
3831
- var token = "[a-z2-7]{10}";
3874
+ var token2 = "[a-z2-7]{10}";
3832
3875
  var STORE_DIR = ".gitifact";
3833
3876
  var marker = "gitifact";
3834
3877
  var recordPathPattern = /^(?:\.gitifact\/spec\/[^/]+\/(?:requirements\.md|design\.md|history\.jsonl)|\.gitifact\/wiki\/(?:(?:[^/]+\/)*[^/]+\.md|history\.jsonl))$/;
@@ -3847,7 +3890,7 @@ function validateDocumentRelativePath(relative) {
3847
3890
  if (!nameToken.test(part) || part.length > 80)
3848
3891
  fail(t("document.folderName", { path: relative }));
3849
3892
  }
3850
- var reqId = new RegExp(`^R-${token}$`);
3893
+ var reqId = new RegExp(`^R-${token2}$`);
3851
3894
  var fail = (message) => {
3852
3895
  throw new StoreError(message);
3853
3896
  };
@@ -3860,20 +3903,20 @@ var scalar = (value) => {
3860
3903
  fail(t("frontmatter.scalar", { value }));
3861
3904
  return value;
3862
3905
  };
3863
- function parseFrontmatter(lines, kind2, legacyIds) {
3864
- const legacy = new RegExp(`^<!-- ${marker}-(?:${kind2 === "wiki" ? "product|guide" : kind2}): (${legacyIds}-${token}) -->$`).exec(lines[0] ?? "");
3906
+ function parseFrontmatter(lines2, kind2, legacyIds) {
3907
+ const legacy = new RegExp(`^<!-- ${marker}-(?:${kind2 === "wiki" ? "product|guide" : kind2}): (${legacyIds}-${token2}) -->$`).exec(lines2[0] ?? "");
3865
3908
  if (legacy) {
3866
- lines.shift();
3909
+ lines2.shift();
3867
3910
  return { id: legacy[1] };
3868
3911
  }
3869
- if (lines[0] !== "---")
3912
+ if (lines2[0] !== "---")
3870
3913
  fail(t("frontmatter.required"));
3871
- lines.shift();
3872
- const end = lines.indexOf("---");
3914
+ lines2.shift();
3915
+ const end = lines2.indexOf("---");
3873
3916
  if (end === -1)
3874
3917
  fail(t("frontmatter.unclosed"));
3875
- const block = lines.splice(0, end);
3876
- lines.shift();
3918
+ const block = lines2.splice(0, end);
3919
+ lines2.shift();
3877
3920
  const fields2 = /* @__PURE__ */ new Map();
3878
3921
  for (let i = 0; i < block.length; i++) {
3879
3922
  const line2 = block[i];
@@ -3909,7 +3952,7 @@ function parseFrontmatter(lines, kind2, legacyIds) {
3909
3952
  fields2.set("sources", sources3);
3910
3953
  }
3911
3954
  const id = fields2.get("id");
3912
- if (typeof id !== "string" || !new RegExp(`^(?:${legacyIds})-${token}$`).test(id))
3955
+ if (typeof id !== "string" || !new RegExp(`^(?:${legacyIds})-${token2}$`).test(id))
3913
3956
  fail(t("frontmatter.id", { kind: kind2 }));
3914
3957
  for (const key of fields2.keys())
3915
3958
  if (key !== "id" && !(key === "sources" && kind2 === "design"))
@@ -3938,17 +3981,17 @@ function validateSource(value) {
3938
3981
  function parseDesign(source2, specId) {
3939
3982
  if (source2.includes("\0") || /\r(?!\n)/.test(source2))
3940
3983
  fail(t("design.invalidCharacters"));
3941
- const lines = normalized(source2).split("\n");
3942
- const front = parseFrontmatter(lines, "design", "S");
3984
+ const lines2 = normalized(source2).split("\n");
3985
+ const front = parseFrontmatter(lines2, "design", "S");
3943
3986
  if (front.id !== specId)
3944
3987
  fail(t("design.idMismatch"));
3945
- while (lines[0] === "")
3946
- lines.shift();
3947
- const heading = lines.shift();
3988
+ while (lines2[0] === "")
3989
+ lines2.shift();
3990
+ const heading = lines2.shift();
3948
3991
  if (!heading?.startsWith("# ") || !heading.slice(2).trim())
3949
3992
  fail(t("design.titleRequired"));
3950
3993
  const title = heading.slice(2).trim();
3951
- const raw = normalized(lines.join("\n"));
3994
+ const raw = normalized(lines2.join("\n"));
3952
3995
  if (!raw)
3953
3996
  fail(t("design.bodyRequired"));
3954
3997
  const references = /* @__PURE__ */ new Set();
@@ -3986,15 +4029,15 @@ function parseDocument(path3, source2) {
3986
4029
  validateDocumentRelativePath(match[1]);
3987
4030
  if (source2.includes("\0") || /\r(?!\n)/.test(source2))
3988
4031
  fail(t("document.invalidCharacters", { path: path3 }));
3989
- const lines = normalized(source2).split("\n");
3990
- const { id } = parseFrontmatter(lines, "wiki", "W");
3991
- while (lines[0] === "")
3992
- lines.shift();
3993
- const heading = lines.shift();
4032
+ const lines2 = normalized(source2).split("\n");
4033
+ const { id } = parseFrontmatter(lines2, "wiki", "W");
4034
+ while (lines2[0] === "")
4035
+ lines2.shift();
4036
+ const heading = lines2.shift();
3994
4037
  if (!heading?.startsWith("# ") || !heading.slice(2).trim())
3995
4038
  fail(t("document.titleRequired", { path: path3 }));
3996
4039
  const title = heading.slice(2).trim();
3997
- const body2 = normalized(lines.join("\n"));
4040
+ const body2 = normalized(lines2.join("\n"));
3998
4041
  if (!body2)
3999
4042
  fail(t("document.bodyRequired", { path: path3 }));
4000
4043
  let fence;
@@ -4081,8 +4124,8 @@ function parseSpec(path3, source2, history = "", designSource) {
4081
4124
  fail(t("spec.unsupportedPath", { path: path3 }));
4082
4125
  if (source2.includes("\0") || source2.includes("\r") && /\r(?!\n)/.test(source2))
4083
4126
  fail(t("spec.invalidCharacters", { path: path3 }));
4084
- const lines = normalized(source2).split("\n");
4085
- const specId = parseFrontmatter(lines, "spec", "S").id;
4127
+ const lines2 = normalized(source2).split("\n");
4128
+ const specId = parseFrontmatter(lines2, "spec", "S").id;
4086
4129
  let title = "";
4087
4130
  let description = "";
4088
4131
  let current2;
@@ -4095,10 +4138,10 @@ function parseSpec(path3, source2, history = "", designSource) {
4095
4138
  else
4096
4139
  description += line2 + "\n";
4097
4140
  };
4098
- for (let i = 0; i < lines.length; i++) {
4099
- const line2 = lines[i];
4141
+ for (let i = 0; i < lines2.length; i++) {
4142
+ const line2 = lines2[i];
4100
4143
  if (waitingId) {
4101
- const match = new RegExp(`^<!-- ${marker}-req: (R-${token}) -->$`).exec(line2);
4144
+ const match = new RegExp(`^<!-- ${marker}-req: (R-${token2}) -->$`).exec(line2);
4102
4145
  if (!match || !current2)
4103
4146
  fail(t("spec.requirementIdRequired", { path: path3 }));
4104
4147
  current2.id = match[1];
@@ -4170,7 +4213,7 @@ function parseHistory(source2, scope = "spec") {
4170
4213
  if (!value || typeof value !== "object" || Array.isArray(value))
4171
4214
  fail(t("history.invalidRecord"));
4172
4215
  const r = value;
4173
- 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)
4216
+ 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)
4174
4217
  fail(t("history.invalidFields"));
4175
4218
  ids.add(r.id);
4176
4219
  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 });
@@ -4289,28 +4332,6 @@ function compareStoreBundles(a, b) {
4289
4332
  return { specChanges: specs2.specChanges, changes: [...specs2.changes, ...compareWikis(a.wiki, b.wiki)] };
4290
4333
  }
4291
4334
 
4292
- // ../../packages/core/dist/domain/document.js
4293
- var DOC_ID_PREFIX = { feature: "S", requirement: "R", design: "D", wiki: "W", instruction: "I" };
4294
- var RECORD_ID_PREFIX = "DR";
4295
- var token2 = "[a-z2-7]{10}";
4296
- var docIdPattern = new RegExp(`^[SRDWI]-${token2}$`);
4297
- var recordIdPattern = new RegExp(`^DR-${token2}$`);
4298
- var reasonIdPattern = new RegExp(`^H-${token2}$`);
4299
- var idPatternOf = (kind2) => new RegExp(`^${DOC_ID_PREFIX[kind2]}-${token2}$`);
4300
- var kindOfId = (id) => Object.entries(DOC_ID_PREFIX).find(([, prefix]) => id.startsWith(prefix + "-"))?.[0];
4301
- var docWarning = (code, path3, values = {}) => ({ code, path: path3, message: t(`warn.${code}`, { path: path3, ...values }) });
4302
- var docProblem = (code, path3, values = {}) => ({ code, path: path3, message: t(`doc.${code}`, { path: path3, ...values }) });
4303
- var DocumentError = class extends Error {
4304
- code;
4305
- path;
4306
- constructor(code, path3, message) {
4307
- super(message);
4308
- this.code = code;
4309
- this.path = path3;
4310
- this.name = "DocumentError";
4311
- }
4312
- };
4313
-
4314
4335
  // ../../packages/core/dist/domain/record.js
4315
4336
  var recordSectionKeys = ["context", "decision", "alternatives"];
4316
4337
  var RECORD_REQUIRED_SECTIONS = ["context", "decision"];
@@ -4336,13 +4357,13 @@ function quoteScalar(value) {
4336
4357
  return plain2 ? value : '"' + value.replace(/[\\"]/g, "\\$&") + '"';
4337
4358
  }
4338
4359
  function parseFrontmatterBlock(source2, path3) {
4339
- const lines = source2.split("\n");
4340
- if (lines[0] !== "---")
4360
+ const lines2 = source2.split("\n");
4361
+ if (lines2[0] !== "---")
4341
4362
  throw new DocumentError("FRONTMATTER_REQUIRED", path3, t("doc.FRONTMATTER_REQUIRED", { path: path3 }));
4342
- const end = lines.indexOf("---", 1);
4363
+ const end = lines2.indexOf("---", 1);
4343
4364
  if (end === -1)
4344
4365
  throw new DocumentError("FRONTMATTER_UNCLOSED", path3, t("doc.FRONTMATTER_UNCLOSED", { path: path3 }));
4345
- const block = lines.slice(1, end);
4366
+ const block = lines2.slice(1, end);
4346
4367
  const fields2 = /* @__PURE__ */ new Map();
4347
4368
  const bad = (line2) => new DocumentError("FRONTMATTER_LINE", path3, t("doc.FRONTMATTER_LINE", { path: path3, line: line2 }));
4348
4369
  for (let i = 0; i < block.length; i++) {
@@ -4385,7 +4406,7 @@ function parseFrontmatterBlock(source2, path3) {
4385
4406
  throw bad(line2);
4386
4407
  fields2.set(key, scalars.length ? { type: "list", items: scalars } : { type: "maps", items: maps });
4387
4408
  }
4388
- return { fields: fields2, body: lines.slice(end + 1).join("\n") };
4409
+ return { fields: fields2, body: lines2.slice(end + 1).join("\n") };
4389
4410
  }
4390
4411
  function renderFrontmatterBlock(fields2) {
4391
4412
  const out = ["---"];
@@ -4577,7 +4598,7 @@ function classifyDocPath(path3) {
4577
4598
  }
4578
4599
  var KEYS2 = {
4579
4600
  feature: ["id", "title", "description", "draft"],
4580
- requirement: ["id", "title", "description", "order", "draft"],
4601
+ requirement: ["id", "title", "description", "order", "style", "draft"],
4581
4602
  design: ["id", "title", "description", "order", "requirements", "sources", "draft"],
4582
4603
  wiki: ["id", "title", "description", "draft"],
4583
4604
  instruction: ["id", "title", "description", "draft"]
@@ -4683,8 +4704,12 @@ function parseDocumentFile(path3, source2) {
4683
4704
  return { kind: "wiki", ...common };
4684
4705
  if (where.kind === "instruction")
4685
4706
  return { kind: "instruction", name: where.name, ...common };
4686
- if (where.kind === "requirement")
4687
- 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
+ }
4688
4713
  const refs = fields2.get("requirements");
4689
4714
  if (refs && (refs.type !== "list" || refs.items.some((r) => !idPatternOf("requirement").test(r)) || new Set(refs.items).size !== refs.items.length)) {
4690
4715
  throw new DocumentError("FRONTMATTER_VALUE", path3, t("doc.FRONTMATTER_VALUE", { path: path3, value: "requirements" }));
@@ -4710,6 +4735,7 @@ function renderDocumentFile(doc) {
4710
4735
  ["title", doc.title],
4711
4736
  ["description", doc.description],
4712
4737
  ["order", doc.kind === "requirement" || doc.kind === "design" ? doc.order : void 0],
4738
+ ["style", doc.kind === "requirement" ? doc.style : void 0],
4713
4739
  ["requirements", doc.kind === "design" ? doc.requirements : void 0],
4714
4740
  ["sources", doc.kind === "design" ? doc.sources.map((s) => ({ ...s })) : void 0],
4715
4741
  ["draft", doc.draft ? true : void 0]
@@ -4929,6 +4955,7 @@ function compareDocumentSets(before, after) {
4929
4955
  }
4930
4956
 
4931
4957
  // ../../packages/core/dist/use-cases/document-warnings.js
4958
+ var OVERVIEW_SECTION_LIMIT = 7;
4932
4959
  function documentBodyLinks(documents) {
4933
4960
  return documents.flatMap((doc) => extractLinks(doc.body).flatMap((link3) => {
4934
4961
  const target = resolveLink(doc.path, link3);
@@ -4963,6 +4990,79 @@ function documentWarnings(documents, assets, existing) {
4963
4990
  }
4964
4991
  if (total > ASSETS_TOTAL_LIMIT)
4965
4992
  warnings.push(docWarning("ASSETS_TOTAL_SIZE", ASSETS_DIR, { bytes: total }));
4993
+ for (const doc of documents) {
4994
+ if (doc.kind !== "design" || !doc.path.endsWith("/design/overview.md"))
4995
+ continue;
4996
+ const sections2 = lines(doc.body).filter((line2) => /^##\s/.test(line2)).length;
4997
+ if (sections2 >= OVERVIEW_SECTION_LIMIT)
4998
+ warnings.push(docWarning("DESIGN_OVERVIEW_LARGE", doc.path, { sections: sections2 }));
4999
+ }
5000
+ return warnings;
5001
+ }
5002
+ var lines = (body2) => body2.replace(/\r\n/g, "\n").split("\n");
5003
+ var scopeHeading = /^###\s+(?:범위와 제약|scope and constraints)\s*$/i;
5004
+ var acceptanceHeading = /^###\s+(?:수용 조건|acceptance criteria)\s*$/i;
5005
+ var useCaseSections = [
5006
+ ["scope", scopeHeading],
5007
+ ["preconditions", /^###\s+(?:사전 조건|preconditions)\s*$/i],
5008
+ ["basic", /^###\s+(?:기본 흐름|basic flow)\s*$/i],
5009
+ ["alternatives", /^###\s+(?:대체 흐름|alternative flows?)\s*$/i],
5010
+ ["postconditions", /^###\s+(?:사후 조건|postconditions)\s*$/i],
5011
+ ["acceptance", acceptanceHeading]
5012
+ ];
5013
+ var pathLine = /^\s*(?:경로|path)\s*:\s*(.*)$/i;
5014
+ var basicFlowName = /^(?:기본 흐름|basic flow)$/i;
5015
+ var alternativeId = /^-\s+(?:\*\*)?(A\d+)\b/;
5016
+ function requirementFormatWarnings(doc, projectStyle) {
5017
+ if (doc.kind !== "requirement")
5018
+ return [];
5019
+ const missing2 = projectStyle === "usecase" && !doc.style ? [docWarning("REQUIREMENT_STYLE_MISSING", doc.path)] : [];
5020
+ const body2 = lines(doc.body);
5021
+ const scope = body2.findIndex((line2) => scopeHeading.test(line2.trim()));
5022
+ const acceptance = body2.findIndex((line2) => acceptanceHeading.test(line2.trim()));
5023
+ const sectionOf = (start) => {
5024
+ const rest = body2.slice(start + 1);
5025
+ const end = rest.findIndex((line2) => /^#{1,3}\s/.test(line2));
5026
+ return end < 0 ? rest : rest.slice(0, end);
5027
+ };
5028
+ const outside = (section2, item) => section2.some((line2) => line2.trim() !== "" && !item.test(line2) && !/^\s+\S/.test(line2));
5029
+ const warnings = [];
5030
+ if (scope >= 0 && (acceptance >= 0 && scope > acceptance || outside(sectionOf(scope), /^- /)))
5031
+ warnings.push(docWarning("REQUIREMENT_SCOPE_FORMAT", doc.path));
5032
+ if (acceptance >= 0 && (outside(sectionOf(acceptance), /^\d+\.\s/) || body2.slice(acceptance + 1).some((line2) => /^#{1,3}\s/.test(line2) && !scopeHeading.test(line2.trim()))))
5033
+ warnings.push(docWarning("REQUIREMENT_CRITERIA_FORMAT", doc.path));
5034
+ if (doc.style === "usecase")
5035
+ warnings.push(...useCaseWarnings(doc.path, body2, sectionOf, acceptance));
5036
+ return [...missing2, ...warnings];
5037
+ }
5038
+ function useCaseWarnings(path3, body2, sectionOf, acceptance) {
5039
+ const warnings = [];
5040
+ const found = useCaseSections.map(([key, heading]) => ({ key, at: body2.findIndex((line2) => heading.test(line2.trim())) })).filter((s) => s.at >= 0);
5041
+ const ordered = found.every((s, i) => i === 0 || s.at > found[i - 1].at);
5042
+ if (!found.some((s) => s.key === "basic") || !ordered)
5043
+ warnings.push(docWarning("REQUIREMENT_FLOW_FORMAT", path3));
5044
+ if (acceptance < 0)
5045
+ return warnings;
5046
+ const alternatives = found.find((s) => s.key === "alternatives");
5047
+ const defined = new Set(alternatives ? sectionOf(alternatives.at).flatMap((line2) => {
5048
+ const m = alternativeId.exec(line2);
5049
+ return m ? [m[1]] : [];
5050
+ }) : []);
5051
+ const items2 = [];
5052
+ for (const line2 of sectionOf(acceptance)) {
5053
+ if (/^\d+\.\s/.test(line2))
5054
+ items2.push([line2.replace(/^\d+\.\s+/, "")]);
5055
+ else if (items2.length && /^\s+\S/.test(line2))
5056
+ items2[items2.length - 1].push(line2);
5057
+ }
5058
+ const named = (flow) => basicFlowName.test(flow) || defined.has(flow);
5059
+ const unpathed = items2.some((item) => {
5060
+ const match = item.map((line2) => pathLine.exec(line2)).find(Boolean);
5061
+ const flows = match ? match[1].split(/[,,、]/).map((f) => f.trim()).filter(Boolean) : [];
5062
+ return !flows.length || !flows.every(named);
5063
+ });
5064
+ if (unpathed)
5065
+ warnings.push(docWarning("REQUIREMENT_PATH_FORMAT", path3));
4966
5066
  return warnings;
4967
5067
  }
4968
5068
 
@@ -4981,12 +5081,12 @@ function parseChangelog(text4) {
4981
5081
  let entry2;
4982
5082
  let section2;
4983
5083
  const fail4 = (line2, key, values = {}) => new InitError("INVALID_CHANGELOG", t("changelog.atLine", { line: line2, reason: t(key, values) }));
4984
- const lines = text4.replace(/\r\n/g, "\n").split("\n");
5084
+ const lines2 = text4.replace(/\r\n/g, "\n").split("\n");
4985
5085
  const close = (line2) => {
4986
5086
  if (entry2 && changelogSections.every((name2) => entry2[name2].length === 0))
4987
5087
  throw fail4(line2, "changelog.emptyVersion", { version: entry2.version });
4988
5088
  };
4989
- lines.forEach((raw, index) => {
5089
+ lines2.forEach((raw, index) => {
4990
5090
  const line2 = index + 1;
4991
5091
  if (raw.trim() === "")
4992
5092
  return;
@@ -5024,7 +5124,7 @@ function parseChangelog(text4) {
5024
5124
  }
5025
5125
  throw fail4(line2, "changelog.unknownLine", { text: raw });
5026
5126
  });
5027
- close(lines.length);
5127
+ close(lines2.length);
5028
5128
  if (entries.length === 0)
5029
5129
  throw fail4(1, "changelog.noVersions");
5030
5130
  return entries;
@@ -6997,14 +7097,14 @@ function toDotPath(_path) {
6997
7097
  return segs.join("");
6998
7098
  }
6999
7099
  function prettifyError(error62) {
7000
- const lines = [];
7100
+ const lines2 = [];
7001
7101
  const issues = [...error62.issues].sort((a, b) => (a.path ?? []).length - (b.path ?? []).length);
7002
7102
  for (const issue2 of issues) {
7003
- lines.push(`\u2716 ${issue2.message}`);
7103
+ lines2.push(`\u2716 ${issue2.message}`);
7004
7104
  if (issue2.path?.length)
7005
- lines.push(` \u2192 at ${toDotPath(issue2.path)}`);
7105
+ lines2.push(` \u2192 at ${toDotPath(issue2.path)}`);
7006
7106
  }
7007
- return lines.join("\n");
7107
+ return lines2.join("\n");
7008
7108
  }
7009
7109
 
7010
7110
  // ../../node_modules/.pnpm/zod@4.6.4/node_modules/zod/v4/core/parse.js
@@ -7839,9 +7939,9 @@ var Doc = class {
7839
7939
  return;
7840
7940
  }
7841
7941
  const content = arg;
7842
- const lines = content.split("\n").filter((x) => x);
7843
- const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));
7844
- const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
7942
+ const lines2 = content.split("\n").filter((x) => x);
7943
+ const minIndent = Math.min(...lines2.map((x) => x.length - x.trimStart().length));
7944
+ const dedented = lines2.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
7845
7945
  for (const line2 of dedented) {
7846
7946
  this.content.push(line2);
7847
7947
  }
@@ -25056,7 +25156,8 @@ var state = external_exports.enum(["committed", "added", "modified", "deleted"])
25056
25156
  var standing = { state, previousPath: external_exports.string().optional() };
25057
25157
  var contributor = external_exports.strictObject({ email: external_exports.string(), name: external_exports.string(), commits: external_exports.number().int().nonnegative(), latest: external_exports.string() });
25058
25158
  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() });
25059
- 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 });
25159
+ var style = external_exports.enum(["default", "usecase"]);
25160
+ 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 });
25060
25161
  var design = external_exports.strictObject({
25061
25162
  id: external_exports.string(),
25062
25163
  path: external_exports.string(),
@@ -25143,7 +25244,7 @@ var browserFeaturesV1 = external_exports.strictObject({
25143
25244
  next: external_exports.string().nullable(),
25144
25245
  features: external_exports.array(featureRow)
25145
25246
  });
25146
- var browserFeatureV1 = external_exports.strictObject({ contract: external_exports.literal("browser-feature"), version: external_exports.literal(1), sessionId: external_exports.string(), feature });
25247
+ var browserFeatureV2 = external_exports.strictObject({ contract: external_exports.literal("browser-feature"), version: external_exports.literal(2), sessionId: external_exports.string(), feature });
25147
25248
  var browserInstructionsV1 = external_exports.strictObject({
25148
25249
  contract: external_exports.literal("browser-instructions"),
25149
25250
  version: external_exports.literal(1),
@@ -25177,6 +25278,7 @@ var snapshot = external_exports.strictObject({
25177
25278
  specId: external_exports.string(),
25178
25279
  path: external_exports.string(),
25179
25280
  order: external_exports.number().int().optional(),
25281
+ style: style.optional(),
25180
25282
  requirements: external_exports.array(external_exports.string()).optional(),
25181
25283
  sources: external_exports.array(source).optional()
25182
25284
  }).nullable();
@@ -25198,16 +25300,25 @@ var event = external_exports.strictObject({
25198
25300
  after: reference,
25199
25301
  records: external_exports.array(record2)
25200
25302
  });
25201
- var browserHistoryV6 = external_exports.strictObject({
25303
+ var browserHistoryV7 = external_exports.strictObject({
25202
25304
  contract: external_exports.literal("browser-history"),
25203
- version: external_exports.literal(6),
25305
+ version: external_exports.literal(7),
25204
25306
  sessionId: external_exports.string(),
25205
25307
  head: oid2,
25206
25308
  // Matching changes and the commits that hold them in all of history, not in this page.
25207
25309
  total: external_exports.number().int().nonnegative(),
25208
25310
  commits: external_exports.number().int().nonnegative(),
25209
25311
  next: oid2.nullable(),
25210
- events: external_exports.array(event)
25312
+ events: external_exports.array(event),
25313
+ // Every document and those no record explains, and each record with how many documents it explains, in commit order.
25314
+ whole: external_exports.array(external_exports.strictObject({
25315
+ commit: oid2,
25316
+ documents: external_exports.number().int().nonnegative(),
25317
+ unrecorded: external_exports.number().int().nonnegative(),
25318
+ records: external_exports.array(external_exports.strictObject({ id: external_exports.string(), documents: external_exports.number().int().positive() }))
25319
+ })),
25320
+ // The kinds of document the history holds at all, so the filter offers a kind only when it can find something.
25321
+ kinds: external_exports.array(kind)
25211
25322
  });
25212
25323
  var browserHistorySummaryV5 = external_exports.strictObject({
25213
25324
  contract: external_exports.literal("browser-history-summary"),
@@ -25243,13 +25354,14 @@ var browserSearchV3 = external_exports.strictObject({
25243
25354
  });
25244
25355
  var headQuery = oid2;
25245
25356
  var count = (max) => external_exports.string().regex(/^(0|[1-9]\d{0,6})$/).transform(Number).pipe(external_exports.number().int().min(0).max(max));
25246
- var browserHistoryQueryV4 = external_exports.strictObject({
25357
+ var browserHistoryQueryV5 = external_exports.strictObject({
25247
25358
  head: headQuery,
25248
25359
  after: oid2.optional(),
25249
25360
  limit: count(50).pipe(external_exports.number().min(1)).optional(),
25250
- kind: changeType.optional(),
25361
+ // A feature or an instruction; `record=missing` keeps the changes no record explains.
25362
+ target: external_exports.string().regex(/^[SI]-[a-z2-7]{10}$/).optional(),
25251
25363
  document: kind.optional(),
25252
- feature: external_exports.string().regex(/^S-[a-z2-7]{10}$/).optional(),
25364
+ record: external_exports.literal("missing").optional(),
25253
25365
  author: external_exports.string().min(1).max(320).optional(),
25254
25366
  q: external_exports.string().max(200).optional()
25255
25367
  });
@@ -25324,9 +25436,9 @@ var browserCommitV4 = external_exports.strictObject({
25324
25436
  });
25325
25437
  var recordId = external_exports.string().regex(/^(?:DR|H)-[A-Za-z0-9_-]{1,64}$/);
25326
25438
  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() });
25327
- var browserCommitChangeV1 = external_exports.strictObject({
25439
+ var browserCommitChangeV2 = external_exports.strictObject({
25328
25440
  contract: external_exports.literal("browser-commit-change"),
25329
- version: external_exports.literal(1),
25441
+ version: external_exports.literal(2),
25330
25442
  sessionId: external_exports.string(),
25331
25443
  commit: oid2,
25332
25444
  event,
@@ -25345,6 +25457,24 @@ var browserRecordV2 = external_exports.strictObject({
25345
25457
  others: external_exports.number().int().nonnegative()
25346
25458
  });
25347
25459
  var browserRecordQueryV1 = external_exports.strictObject({ head: oid2, id: recordId });
25460
+ var browserDocumentHistoryV1 = external_exports.strictObject({
25461
+ contract: external_exports.literal("browser-document-history"),
25462
+ version: external_exports.literal(1),
25463
+ sessionId: external_exports.string(),
25464
+ head: oid2,
25465
+ doc: external_exports.strictObject({ id: external_exports.string(), title: external_exports.string().nullable(), kind: kind.nullable(), path: external_exports.string().nullable() }),
25466
+ total: external_exports.number().int().nonnegative(),
25467
+ recorded: external_exports.number().int().nonnegative(),
25468
+ withoutRecord: external_exports.number().int().nonnegative(),
25469
+ next: oid2.nullable(),
25470
+ events: external_exports.array(event)
25471
+ });
25472
+ var browserDocumentHistoryQueryV1 = external_exports.strictObject({
25473
+ head: oid2,
25474
+ id: external_exports.string().regex(/^[SRDWI]-[a-z2-7]{10}$/),
25475
+ after: oid2.optional(),
25476
+ limit: count(50).pipe(external_exports.number().min(1)).optional()
25477
+ });
25348
25478
  var browserInstructionFileV1 = external_exports.strictObject({
25349
25479
  contract: external_exports.literal("browser-instruction-file"),
25350
25480
  version: external_exports.literal(1),
@@ -25367,9 +25497,9 @@ var browserWorkingV1 = external_exports.strictObject({
25367
25497
  changes: external_exports.array(workingChange),
25368
25498
  withoutRecord: external_exports.array(external_exports.string())
25369
25499
  });
25370
- var browserWorkingChangeV1 = external_exports.strictObject({
25500
+ var browserWorkingChangeV2 = external_exports.strictObject({
25371
25501
  contract: external_exports.literal("browser-working-change"),
25372
- version: external_exports.literal(1),
25502
+ version: external_exports.literal(2),
25373
25503
  sessionId: external_exports.string(),
25374
25504
  change: workingChange,
25375
25505
  before: snapshot,
@@ -25635,6 +25765,7 @@ var messages_default3 = {
25635
25765
  "server.cursorNotFound": "\uC774\uC5B4 \uC77D\uC744 \uC704\uCE58\uB97C \uBAA9\uB85D\uC5D0\uC11C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uCC98\uC74C\uBD80\uD130 \uB2E4\uC2DC \uBD88\uB7EC\uC624\uC138\uC694.",
25636
25766
  "server.workingChangeNotFound": "\uCEE4\uBC0B \uC804 \uBCC0\uACBD\uC5D0 \uADF8 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
25637
25767
  "server.recordNotFound": "\uC774 \uC774\uB825\uC5D0 \uADF8 \uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25768
+ "server.documentNotFound": "\uC774 \uC774\uB825\uACFC \uC791\uC5C5 \uD3F4\uB354\uC5D0 \uADF8 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
25638
25769
  "server.commitFileNotFound": "\uADF8 \uCEE4\uBC0B\uC774 \uBC14\uAFBC \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
25639
25770
  "server.instructionFileNotFound": "\uC9C0\uCE68 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
25640
25771
  "server.specsUnreadable": "\uBA85\uC138\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
@@ -25769,7 +25900,8 @@ var messages_default3 = {
25769
25900
  "docs.pathExample.design": "payment/api (.gitifact/spec/payment/design/api.md)",
25770
25901
  "docs.pathExample.instruction": "cli-architecture (.gitifact/instructions/cli-architecture/index.md)",
25771
25902
  "docs.skeleton.feature": "(\uC774 \uAE30\uB2A5\uC774 \uBB34\uC5C7\uC774\uACE0 \uC5B4\uB514\uAE4C\uC9C0\uC778\uC9C0)",
25772
- "docs.skeleton.requirement": "(\uC0AC\uC6A9\uC790)\uB85C\uC11C, (\uBAA9\uC801)\uC744 \uC704\uD574 (\uB3D9\uC791)\uC744 \uD558\uACE0 \uC2F6\uB2E4.\n\n### \uC218\uC6A9 \uC870\uAC74\n\n1. \uC870\uAC74: (\uC0C1\uD669)\n \uAE30\uB300 \uB3D9\uC791: (\uACB0\uACFC)",
25903
+ "docs.skeleton.requirement": "(\uC0AC\uC6A9\uC790)\uB85C\uC11C, (\uBAA9\uC801)\uC744 \uC704\uD574 (\uB3D9\uC791)\uC744 \uD558\uACE0 \uC2F6\uB2E4.\n\n### \uBC94\uC704\uC640 \uC81C\uC57D\n\n- (\uCC38\xB7\uAC70\uC9D3\uC744 \uAC00\uB9B4 \uC218 \uC788\uB294 \uADDC\uCE59 \uD55C \uBB38\uC7A5. \uC5C6\uC73C\uBA74 \uC774 \uC808\uC744 \uC9C0\uC6B4\uB2E4)\n\n### \uC218\uC6A9 \uC870\uAC74\n\n1. \uC870\uAC74: (\uC0C1\uD669)\n \uAE30\uB300 \uB3D9\uC791: (\uACB0\uACFC)",
25904
+ "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)",
25773
25905
  "docs.skeleton.design": "(\uC774 \uC124\uACC4\uAC00 \uB2E4\uB8E8\uB294 \uAD6C\uC870\uC640 \uCC98\uB9AC \uBC29\uC2DD)",
25774
25906
  "docs.skeleton.instruction": "(\uC774 \uC791\uC5C5\uC744 \uD560 \uB54C \uC9C0\uD0AC \uADDC\uCE59\uACFC \uC808\uCC28. \uC790\uC138\uD55C \uB0B4\uC6A9\uC740 references/ \uC544\uB798 \uD30C\uC77C\uB85C \uB098\uB204\uACE0 \uC5EC\uAE30\uC11C \uB9C1\uD06C\uD55C\uB2E4)",
25775
25907
  "docs.exists": "\uC774\uBBF8 \uC788\uB294 \uD30C\uC77C\uC785\uB2C8\uB2E4: {path}",
@@ -25792,8 +25924,10 @@ var messages_default3 = {
25792
25924
  "commit.checkFailed": "\uBB38\uC11C \uAC80\uC0AC\uC5D0\uC11C \uBB38\uC81C {count}\uAC1C\uAC00 \uB098\uC640 \uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.",
25793
25925
  "commit.unknownDocument": "\uACB0\uC815\uAE30\uB85D\uC774 \uAC00\uB9AC\uD0A4\uB294 \uBB38\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4: {ids}",
25794
25926
  "docs.singleLine": "{field}\uC740 \uD55C \uC904\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
25927
+ "docs.styleRequirementOnly": "--style\uC740 \uC694\uAD6C\uC0AC\uD56D(requirement)\uC744 \uB9CC\uB4E4 \uB54C\uB9CC \uC501\uB2C8\uB2E4.",
25795
25928
  "changes.problems": "\uBB38\uC11C \uAC80\uC0AC: \uBB38\uC81C {count}\uAC1C (check\uB85C \uD655\uC778)",
25796
25929
  "changes.warnings": "\uBB38\uC11C \uACBD\uACE0: {count}\uAC1C (check\uB85C \uD655\uC778, \uCEE4\uBC0B\uC740 \uB9C9\uC9C0 \uC54A\uC74C)",
25930
+ "changes.formatWarnings": "\uD615\uC2DD \uACBD\uACE0 {count}\uAC1C (\uC774\uBC88\uC5D0 \uBC14\uB010 \uC694\uAD6C\uC0AC\uD56D, \uCEE4\uBC0B\uC740 \uB9C9\uC9C0 \uC54A\uC74C)",
25797
25931
  "changes.clean": "\uBB38\uC11C \uAC80\uC0AC: \uBB38\uC81C \uC5C6\uC74C",
25798
25932
  "changes.pendingRecords": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC740 \uACB0\uC815\uAE30\uB85D {count}\uAC1C (*\uB294 \uC774\uBC88\uC5D0 \uBC14\uB00C\uC9C0 \uC54A\uC740 \uBB38\uC11C)",
25799
25933
  "changes.withoutRecord": "\uACB0\uC815\uAE30\uB85D\uC774 \uC5C6\uB294 \uC218\uC815\xB7\uC774\uB3D9\xB7\uC0AD\uC81C: {ids}",
@@ -25830,6 +25964,7 @@ var messages_default3 = {
25830
25964
  "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)",
25831
25965
  "help.specsNewKind": "feature, requirement, design \uC911 \uD558\uB098",
25832
25966
  "help.specsNewPath": "\uAE30\uB2A5 \uC774\uB984 \uB610\uB294 \uAE30\uB2A5/slug (\uC608: payment, payment/cancel)",
25967
+ "help.specsNewStyle": "\uC694\uAD6C\uC0AC\uD56D \uD615\uC2DD(usecase, default). \uC5C6\uC73C\uBA74 \uC124\uC815\uC758 requirementStyle\uC744 \uB530\uB978\uB2E4",
25833
25968
  "help.newTitle": "\uBB38\uC11C \uC81C\uBAA9",
25834
25969
  "help.newDescription": "\uD55C \uC904 \uC124\uBA85",
25835
25970
  "help.instructions": "\uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC758 \uBAA9\uB85D\xB7\uC6D0\uBB38\xB7\uC0DD\uC131",
@@ -25949,6 +26084,7 @@ var messages_default4 = {
25949
26084
  "server.cursorNotFound": "The place to read on from is no longer in the list. Load it again from the start.",
25950
26085
  "server.workingChangeNotFound": "No uncommitted change of that document.",
25951
26086
  "server.recordNotFound": "This history has no such decision record.",
26087
+ "server.documentNotFound": "Neither this history nor the working tree has that document.",
25952
26088
  "server.commitFileNotFound": "That commit did not change this file.",
25953
26089
  "server.instructionFileNotFound": "The instruction file was not found.",
25954
26090
  "server.specsUnreadable": "Could not read specifications.",
@@ -26083,7 +26219,8 @@ var messages_default4 = {
26083
26219
  "docs.pathExample.design": "payment/api (.gitifact/spec/payment/design/api.md)",
26084
26220
  "docs.pathExample.instruction": "cli-architecture (.gitifact/instructions/cli-architecture/index.md)",
26085
26221
  "docs.skeleton.feature": "(What this feature is and where it ends)",
26086
- "docs.skeleton.requirement": "As (a user), I want to (act) so that (purpose).\n\n### Acceptance criteria\n\n1. Condition: (situation)\n Expected: (result)",
26222
+ "docs.skeleton.requirement": "As (a user), I want to (act) so that (purpose).\n\n### Scope and constraints\n\n- (One rule that is either true or false. Remove this section if there is none)\n\n### Acceptance criteria\n\n1. Condition: (situation)\n Expected: (result)",
26223
+ "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)",
26087
26224
  "docs.skeleton.design": "(The structure and processing this design covers)",
26088
26225
  "docs.skeleton.instruction": "(Rules and steps to follow for this kind of work. Put details in files under references/ and link them from here)",
26089
26226
  "docs.exists": "The file already exists: {path}",
@@ -26106,8 +26243,10 @@ var messages_default4 = {
26106
26243
  "commit.checkFailed": "The document check found {count} problems; nothing was committed.",
26107
26244
  "commit.unknownDocument": "A record names documents that do not exist: {ids}",
26108
26245
  "docs.singleLine": "The {field} must be a single line.",
26246
+ "docs.styleRequirementOnly": "--style applies only when creating a requirement.",
26109
26247
  "changes.problems": "Document check: {count} problems (see check)",
26110
26248
  "changes.warnings": "Document warnings: {count} (see check; they do not block a commit)",
26249
+ "changes.formatWarnings": "Format warnings: {count} (requirements changed here; they do not block a commit)",
26111
26250
  "changes.clean": "Document check: no problems",
26112
26251
  "changes.pendingRecords": "{count} uncommitted record(s) (* marks a document this change does not touch)",
26113
26252
  "changes.withoutRecord": "Changed, moved or deleted without a record: {ids}",
@@ -26144,6 +26283,7 @@ var messages_default4 = {
26144
26283
  "help.specsNew": "Create a spec file with an issued ID, frontmatter and a body skeleton (marked draft: true)",
26145
26284
  "help.specsNewKind": "One of feature, requirement, design",
26146
26285
  "help.specsNewPath": "Feature name or feature/slug (e.g. payment, payment/cancel)",
26286
+ "help.specsNewStyle": "Requirement shape (usecase, default). Defaults to the config requirementStyle",
26147
26287
  "help.newTitle": "Document title",
26148
26288
  "help.newDescription": "One-line description",
26149
26289
  "help.instructions": "List, read and create project instructions",
@@ -26559,7 +26699,7 @@ function storeReader(cwd) {
26559
26699
  import { DatabaseSync } from "node:sqlite";
26560
26700
  import { lstat, mkdir, rm, writeFile } from "node:fs/promises";
26561
26701
  import { join as join2 } from "node:path";
26562
- var CACHE_FORMAT = 11;
26702
+ var CACHE_FORMAT = 12;
26563
26703
  var CACHE_DIR = ".gitifact/cache";
26564
26704
  var SCHEMA = `
26565
26705
  -- Working-tree files already read: the modification time and size decide whether a file is read again.
@@ -26711,6 +26851,10 @@ function snippet(text4, query) {
26711
26851
  return (from > 0 ? "\u2026" : "") + text4.slice(from, at + query.length + 70).trim() + (at + query.length + 70 < text4.length ? "\u2026" : "");
26712
26852
  }
26713
26853
  var containing = (query) => "%" + query.replace(/[\\%_]/g, (c) => "\\" + c) + "%";
26854
+ function hashPrefix(query) {
26855
+ const wanted = query?.trim().toLowerCase();
26856
+ return wanted && /^[0-9a-f]{7,64}$/.test(wanted) ? wanted : void 0;
26857
+ }
26714
26858
 
26715
26859
  // src/adapters/cache/documents.ts
26716
26860
  var FILE_LIMIT = 1024 * 1024;
@@ -27003,7 +27147,7 @@ var recordKind = (path3) => {
27003
27147
  var folderOf = (path3) => path3.startsWith(SPEC_ROOT + "/") ? path3.split("/").slice(0, 3).join("/") : null;
27004
27148
  function snapshot3(doc, specId) {
27005
27149
  const base = { id: doc.id, kind: doc.kind, title: doc.title, description: doc.description, body: doc.body, specId, path: doc.path };
27006
- if (doc.kind === "requirement") return { ...base, order: doc.order };
27150
+ if (doc.kind === "requirement") return { ...base, order: doc.order, ...doc.style ? { style: doc.style } : {} };
27007
27151
  if (doc.kind === "design") return { ...base, order: doc.order, requirements: doc.requirements, sources: doc.sources };
27008
27152
  return base;
27009
27153
  }
@@ -27026,7 +27170,7 @@ function sideOf2(files) {
27026
27170
  }
27027
27171
  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 };
27028
27172
  }
27029
- 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);
27173
+ 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);
27030
27174
  function compare(before, after) {
27031
27175
  const out = [];
27032
27176
  for (const id of [.../* @__PURE__ */ new Set([...before.docs.keys(), ...after.docs.keys()])].sort()) {
@@ -27103,10 +27247,10 @@ function createCommitChanges(git) {
27103
27247
  }
27104
27248
  };
27105
27249
  for (const chunk of patch.split("").filter(Boolean)) {
27106
- const lines = chunk.split("\n");
27107
- const commit = lines[0].trim();
27108
- const removed = new Set(lines.filter((l) => l.startsWith("-") && !l.startsWith("---")).flatMap((l) => read(l.slice(1))).map((r) => r.id));
27109
- found.set(commit, lines.filter((l) => l.startsWith("+") && !l.startsWith("+++")).flatMap((l) => read(l.slice(1))).filter((r) => !removed.has(r.id)).map((r) => reasonRecord(r.id, r.docs, r.reason)));
27250
+ const lines2 = chunk.split("\n");
27251
+ const commit = lines2[0].trim();
27252
+ const removed = new Set(lines2.filter((l) => l.startsWith("-") && !l.startsWith("---")).flatMap((l) => read(l.slice(1))).map((r) => r.id));
27253
+ found.set(commit, lines2.filter((l) => l.startsWith("+") && !l.startsWith("+++")).flatMap((l) => read(l.slice(1))).filter((r) => !removed.has(r.id)).map((r) => reasonRecord(r.id, r.docs, r.reason)));
27110
27254
  }
27111
27255
  return found;
27112
27256
  }
@@ -27415,25 +27559,30 @@ function createHistory(database, git, originals) {
27415
27559
  function where(filter) {
27416
27560
  const clauses = [];
27417
27561
  const params = [];
27418
- if (filter.kind) {
27419
- clauses.push("instr(',' || c.types || ',', ?) > 0");
27420
- params.push("," + filter.kind + ",");
27562
+ if (filter.target?.startsWith("I-")) {
27563
+ clauses.push("c.id = ?");
27564
+ params.push(filter.target);
27565
+ } else if (filter.target) {
27566
+ clauses.push("(c.before_spec = ? OR c.after_spec = ?)");
27567
+ params.push(filter.target, filter.target);
27421
27568
  }
27422
27569
  if (filter.document) {
27423
27570
  clauses.push("c.kind = ?");
27424
27571
  params.push(filter.document);
27425
27572
  }
27426
- if (filter.feature) {
27427
- clauses.push("(c.before_spec = ? OR c.after_spec = ?)");
27428
- params.push(filter.feature, filter.feature);
27429
- }
27573
+ if (filter.unrecorded) clauses.push("json_array_length(c.row, '$.records') = 0");
27430
27574
  if (filter.author) {
27431
27575
  clauses.push("c.email = ?");
27432
27576
  params.push(filter.author);
27433
27577
  }
27434
- if (filter.q?.trim()) {
27435
- clauses.push("instr(c.needle, ?) > 0");
27436
- params.push(filter.q.trim().toLowerCase());
27578
+ const q = filter.q?.trim().toLowerCase();
27579
+ if (q) {
27580
+ const record4 = `EXISTS (SELECT 1 FROM json_each(c.row, '$.records') j JOIN records r ON r.oid = c.oid AND r.id = j.value
27581
+ WHERE instr(lower(r.title), ?) > 0 OR instr(lower(r.id), ?) > 0
27582
+ OR instr(lower((SELECT group_concat(json_extract(s.value, '$.body'), ' ') FROM json_each(r.sections) s)), ?) > 0)`;
27583
+ const hash3 = hashPrefix(q) ? " OR substr(c.oid, 1, length(?)) = ?" : "";
27584
+ clauses.push(`(instr(c.needle, ?) > 0 OR ${record4}${hash3})`);
27585
+ params.push(q, q, q, q, ...hash3 ? [q, q] : []);
27437
27586
  }
27438
27587
  return { sql: clauses.map((c) => " AND " + c).join(""), params };
27439
27588
  }
@@ -27461,11 +27610,28 @@ function createHistory(database, git, originals) {
27461
27610
  const commits = db.prepare(`SELECT l.pos AS pos, l.oid AS oid ${from} AND l.pos > ? GROUP BY l.pos, l.oid ORDER BY l.pos LIMIT ?`).all(head, ...params, start, limit + 1);
27462
27611
  const shown = commits.slice(0, limit);
27463
27612
  const rows = !shown.length ? [] : db.prepare(`SELECT c.row ${from} AND l.pos >= ? AND l.pos <= ? ORDER BY l.pos, c.ord`).all(head, ...params, shown[0].pos, shown[shown.length - 1].pos);
27613
+ const oids = JSON.stringify(shown.map((c) => c.oid));
27614
+ const sizes = db.prepare(`SELECT oid, count(*) AS documents, sum(json_array_length(row, '$.records') = 0) AS unrecorded
27615
+ FROM changes WHERE oid IN (SELECT value FROM json_each(?)) GROUP BY oid`).all(oids);
27616
+ const explained = db.prepare(`SELECT c.oid AS oid, j.value AS id, count(*) AS documents FROM changes c, json_each(c.row, '$.records') j
27617
+ WHERE c.oid IN (SELECT value FROM json_each(?)) GROUP BY c.oid, j.value ORDER BY c.oid, min(c.ord)`).all(oids);
27618
+ const whole = shown.map(({ oid: oid3 }) => {
27619
+ const size = sizes.find((s) => s.oid === oid3);
27620
+ return {
27621
+ commit: oid3,
27622
+ documents: Number(size?.documents ?? 0),
27623
+ unrecorded: Number(size?.unrecorded ?? 0),
27624
+ records: explained.filter((r) => r.oid === oid3).map((r) => ({ id: r.id, documents: Number(r.documents) }))
27625
+ };
27626
+ });
27627
+ const kinds = db.prepare("SELECT DISTINCT c.kind AS kind FROM lineage l JOIN changes c ON c.oid = l.oid WHERE l.head = ?").all(head).map((k) => k.kind);
27464
27628
  return {
27465
27629
  total: Number(counted.n),
27466
27630
  commits: Number(counted.commits),
27467
27631
  next: commits.length > limit ? shown[shown.length - 1].oid : null,
27468
- events: hydrate(db, rows.map((r) => r.row))
27632
+ events: hydrate(db, rows.map((r) => r.row)),
27633
+ whole,
27634
+ kinds
27469
27635
  };
27470
27636
  });
27471
27637
  },
@@ -27515,9 +27681,11 @@ function createHistory(database, git, originals) {
27515
27681
  clauses.push("(lower(json_extract(row, '$.author')) = ? OR lower(json_extract(row, '$.email')) = ?)");
27516
27682
  params.push(filter.author.toLowerCase(), filter.author.toLowerCase());
27517
27683
  }
27518
- if (filter.q?.trim()) {
27519
- clauses.push("instr(lower(title || ' ' || body || ' ' || json_extract(row, '$.message')), ?) > 0");
27520
- params.push(filter.q.trim().toLowerCase());
27684
+ const q = filter.q?.trim().toLowerCase();
27685
+ const hash3 = hashPrefix(q);
27686
+ if (q) {
27687
+ clauses.push(`(instr(lower(title || ' ' || id || ' ' || coalesce(body, '')), ?) > 0${hash3 ? " OR substr(oid, 1, length(?)) = ?" : ""})`);
27688
+ params.push(q, ...hash3 ? [hash3, hash3] : []);
27521
27689
  }
27522
27690
  if (filter.from !== void 0) {
27523
27691
  clauses.push("unixepoch(json_extract(row, '$.date')) * 1000 >= ?");
@@ -27725,8 +27893,8 @@ function createCommitLog(database, git, root) {
27725
27893
  * prints it. Anything shorter or not a hash names none. Newest first, a page after the commit `after`.
27726
27894
  */
27727
27895
  async commitsNamed(head, raw, after, limit) {
27728
- const prefix = raw.trim().toLowerCase();
27729
- if (!/^[0-9a-f]{7,64}$/.test(prefix)) return { total: 0, next: null, hits: [] };
27896
+ const prefix = hashPrefix(raw);
27897
+ if (!prefix) return { total: 0, next: null, hits: [] };
27730
27898
  await ensure(head);
27731
27899
  return database.with((db) => {
27732
27900
  const rows = db.prepare("SELECT oid, name, date, subject FROM log WHERE reach = 1 AND substr(oid, 1, length(?)) = ? ORDER BY time DESC, oid").all(prefix, prefix);
@@ -28077,6 +28245,8 @@ function createStampReader(root, inherited = process.env) {
28077
28245
  const info5 = await lstat8(join9(root, ...path3.split("/"))).catch(() => void 0);
28078
28246
  return path3 + " " + (info5 ? info5.mtimeMs + ":" + info5.size : "-");
28079
28247
  }));
28248
+ const mailmap = await lstat8(join9(root, ".mailmap")).catch(() => void 0);
28249
+ seen.push(".mailmap " + (mailmap ? mailmap.mtimeMs + ":" + mailmap.size : "-"));
28080
28250
  return createHash2("sha256").update(head.trim() + "\n" + seen.sort().join("\n")).digest("hex").slice(0, 20);
28081
28251
  };
28082
28252
  }
@@ -28111,7 +28281,7 @@ function checkoutDocuments(states, authors) {
28111
28281
  description: index.description,
28112
28282
  body: index.body,
28113
28283
  ...standing2(index.id),
28114
- 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) })),
28284
+ 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) })),
28115
28285
  designs: designs.map((d) => ({
28116
28286
  id: d.id,
28117
28287
  path: d.path,
@@ -28152,23 +28322,23 @@ function createCheckoutReader(root, cache, inherited = process.env) {
28152
28322
  return head || null;
28153
28323
  };
28154
28324
  const pending = /* @__PURE__ */ new Map();
28325
+ const last = /* @__PURE__ */ new Map();
28155
28326
  const readStamp = createStampReader(root, inherited);
28156
- async function read() {
28327
+ async function read(stamp) {
28157
28328
  const raw = await readConfigFile(root);
28158
28329
  if (!raw) throw new StoreError(t2("specReader.schemaRequired"));
28159
28330
  parseManagedConfig(raw);
28160
28331
  const head = await readHead();
28161
- const stamp = await readStamp();
28162
- const [current2, dirty, authors, people] = await settled([
28332
+ const [current3, dirty, authors, people] = await settled([
28163
28333
  cache.documents.list(),
28164
28334
  head ? git(["status", "--porcelain=v1", "--", ".gitifact/spec", INSTRUCTIONS_ROOT]) : Promise.resolve(""),
28165
28335
  // Who touched each folder and who committed at all, counted over the commit log in the cache.
28166
28336
  head ? cache.log.folders(head) : Promise.resolve(/* @__PURE__ */ new Map()),
28167
28337
  head ? cache.log.contributors(head) : Promise.resolve([])
28168
28338
  ]);
28169
- const states = await documentStates({ head: async () => head, reader }, current2.documents);
28339
+ const states = await documentStates({ head: async () => head, reader }, current3.documents);
28170
28340
  const { features, instructions: instructions2, orphans } = checkoutDocuments(states, authors);
28171
- const problems = [...current2.problems, ...orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
28341
+ const problems = [...current3.problems, ...orphans.map((feature2) => docProblem("FEATURE_INDEX_REQUIRED", `.gitifact/spec/${feature2}/index.md`, { feature: feature2 }))];
28172
28342
  if (await readHead() !== head) throw new StoreError(t2("specReader.projectChanged"));
28173
28343
  return {
28174
28344
  head,
@@ -28182,11 +28352,19 @@ function createCheckoutReader(root, cache, inherited = process.env) {
28182
28352
  folders: authors
28183
28353
  };
28184
28354
  }
28355
+ async function current2(language) {
28356
+ const stamp = await readStamp();
28357
+ const known = last.get(language);
28358
+ if (known?.stamp === stamp) return { ...known, observedAt: (/* @__PURE__ */ new Date()).toISOString() };
28359
+ const fresh = await read(stamp);
28360
+ last.set(language, fresh);
28361
+ return fresh;
28362
+ }
28185
28363
  const base = () => {
28186
28364
  const language = getLanguage();
28187
28365
  let value = pending.get(language);
28188
28366
  if (!value) {
28189
- value = read().finally(() => pending.delete(language));
28367
+ value = current2(language).finally(() => pending.delete(language));
28190
28368
  pending.set(language, value);
28191
28369
  }
28192
28370
  return value;
@@ -28240,15 +28418,15 @@ function createCommitFiles(git) {
28240
28418
  const revs = parent ? [parent, commit] : [commit];
28241
28419
  const names = (await text4([...base, "--name-status", ...revs, "--"])).split("\0");
28242
28420
  const counts = (await text4([...base, "--numstat", ...revs, "--"])).split("\0");
28243
- const lines = /* @__PURE__ */ new Map();
28421
+ const lines2 = /* @__PURE__ */ new Map();
28244
28422
  for (let i = 0; i < counts.length; i++) {
28245
28423
  const [added, deleted, path3] = counts[i].split(" ");
28246
28424
  if (added === void 0 || deleted === void 0) continue;
28247
28425
  const count2 = { additions: added === "-" ? null : Number(added), deletions: deleted === "-" ? null : Number(deleted) };
28248
28426
  if (path3 === "") {
28249
- lines.set(counts[i + 2], count2);
28427
+ lines2.set(counts[i + 2], count2);
28250
28428
  i += 2;
28251
- } else if (path3 !== void 0) lines.set(path3, count2);
28429
+ } else if (path3 !== void 0) lines2.set(path3, count2);
28252
28430
  }
28253
28431
  const files = [];
28254
28432
  for (let i = 0; i < names.length - 1; i++) {
@@ -28258,7 +28436,7 @@ function createCommitFiles(git) {
28258
28436
  const previousPath = renamed ? names[++i] : void 0;
28259
28437
  const path3 = names[++i];
28260
28438
  if (!isSource(path3) && (!previousPath || !isSource(previousPath))) continue;
28261
- files.push({ path: path3, ...renamed && code.startsWith("R") ? { previousPath } : {}, status: statusOf(code), ...lines.get(path3) ?? { additions: null, deletions: null } });
28439
+ files.push({ path: path3, ...renamed && code.startsWith("R") ? { previousPath } : {}, status: statusOf(code), ...lines2.get(path3) ?? { additions: null, deletions: null } });
28262
28440
  }
28263
28441
  return { commit, parent, total: files.length, files };
28264
28442
  }
@@ -28339,7 +28517,7 @@ function snapshot4(doc, docs) {
28339
28517
  const folder = doc.path.split("/").slice(0, 3).join("/");
28340
28518
  const specId = doc.kind === "wiki" || doc.kind === "instruction" ? doc.kind : docs.find((d) => d.kind === "feature" && d.path === folder + "/index.md")?.id ?? "";
28341
28519
  const base = { id: doc.id, kind: doc.kind, title: doc.title, description: doc.description, body: doc.body, specId, path: doc.path };
28342
- if (doc.kind === "requirement") return { ...base, order: doc.order };
28520
+ if (doc.kind === "requirement") return { ...base, order: doc.order, ...doc.style ? { style: doc.style } : {} };
28343
28521
  if (doc.kind === "design") return { ...base, order: doc.order, requirements: doc.requirements, sources: doc.sources };
28344
28522
  return base;
28345
28523
  }
@@ -28400,6 +28578,25 @@ async function recent(source2, head) {
28400
28578
  return { group: "recent", total: hits.length, next: null, hits };
28401
28579
  }
28402
28580
 
28581
+ // src/queries/document-history.ts
28582
+ async function documentHistory(source2, head, id) {
28583
+ const events = head ? await source2.cache.history.ofDocument(head, id) : [];
28584
+ const current2 = (await source2.cache.documents.list()).documents.find((d) => d.id === id);
28585
+ const last = events.map((e) => e.after ?? e.before).find(Boolean);
28586
+ if (!current2 && !events.length) return void 0;
28587
+ const doc = { id, title: current2?.title ?? last?.title ?? null, kind: current2?.kind ?? events[0]?.kind ?? null, path: current2?.path ?? last?.path ?? null };
28588
+ return { doc, events };
28589
+ }
28590
+ async function documentHistoryPage(source2, head, id, request) {
28591
+ const found = await documentHistory(source2, head, id);
28592
+ if (!found) return void 0;
28593
+ const page = pageOf(found.events, (e) => e.commit, request);
28594
+ if (!page) return null;
28595
+ const recorded = found.events.filter((e) => e.records.length).length;
28596
+ const withoutRecord = found.events.filter((e) => !e.records.length && e.types.some((type) => type !== "created")).length;
28597
+ return { doc: found.doc, total: page.total, recorded, withoutRecord, next: page.next, events: page.rows };
28598
+ }
28599
+
28403
28600
  // src/queries/checkout.ts
28404
28601
  var SHOWN_REQUIREMENTS = 12;
28405
28602
  function checkoutIndex(base) {
@@ -28416,13 +28613,13 @@ function checkoutIndex(base) {
28416
28613
  people: base.people.map((p) => ({ name: p.name, email: p.email }))
28417
28614
  };
28418
28615
  }
28419
- var acceptanceHeading = /^###\s+(?:수용 조건|acceptance criteria)\s*$/i;
28616
+ var acceptanceHeading2 = /^###\s+(?:수용 조건|acceptance criteria)\s*$/i;
28420
28617
  function acceptanceCount(body2) {
28421
- const lines = body2.replace(/\r\n/g, "\n").split("\n");
28422
- const start = lines.findIndex((line2) => acceptanceHeading.test(line2.trim()));
28618
+ const lines2 = body2.replace(/\r\n/g, "\n").split("\n");
28619
+ const start = lines2.findIndex((line2) => acceptanceHeading2.test(line2.trim()));
28423
28620
  if (start < 0) return null;
28424
28621
  let count2 = 0;
28425
- for (const line2 of lines.slice(start + 1)) {
28622
+ for (const line2 of lines2.slice(start + 1)) {
28426
28623
  if (/^#{1,6}\s/.test(line2)) break;
28427
28624
  if (/^\d+\.\s/.test(line2)) count2++;
28428
28625
  }
@@ -28529,7 +28726,7 @@ function recordRoutes(root, sessionId, env) {
28529
28726
  route({ method: "GET", path: "/api/v1/feature", session: true, query: browserFeatureQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28530
28727
  const feature2 = featureOf(await checkout.base(), query.id);
28531
28728
  if (!feature2) throw new HttpError(404, "NOT_FOUND", t2("server.featureNotFound"));
28532
- return ok(browserFeatureV1.parse({ contract: "browser-feature", version: 1, sessionId, feature: feature2 }));
28729
+ return ok(browserFeatureV2.parse({ contract: "browser-feature", version: 2, sessionId, feature: feature2 }));
28533
28730
  } }),
28534
28731
  route({ method: "GET", path: "/api/v1/instructions", session: true, unreadable: unreadable2, handle: async () => ok(browserInstructionsV1.parse({ contract: "browser-instructions", version: 1, sessionId, ...await checkout.instructions() })) }),
28535
28732
  route({ method: "GET", path: "/api/v1/contributors", session: true, query: browserContributorsQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
@@ -28544,10 +28741,16 @@ function recordRoutes(root, sessionId, env) {
28544
28741
  } }),
28545
28742
  // Whether the screen is behind, asked when the reader comes back to the tab; cheap enough to ask every time.
28546
28743
  route({ method: "GET", path: "/api/v1/stamp", session: true, unreadable: unreadable2, handle: async () => ok(browserStampV1.parse({ contract: "browser-stamp", version: 1, sessionId, stamp: await readStamp() })) }),
28547
- route({ method: "GET", path: "/api/v1/history", session: true, query: browserHistoryQueryV4, unreadable: unreadable2, handle: async ({ query }) => {
28548
- const page = await cache.history.commits(query.head, { kind: query.kind, document: query.document, feature: query.feature, author: query.author, q: query.q }, query.after, query.limit ?? PAGE);
28744
+ route({ method: "GET", path: "/api/v1/history", session: true, query: browserHistoryQueryV5, unreadable: unreadable2, handle: async ({ query }) => {
28745
+ const page = await cache.history.commits(query.head, {
28746
+ target: query.target,
28747
+ document: query.document,
28748
+ unrecorded: query.record === "missing",
28749
+ author: query.author,
28750
+ q: query.q
28751
+ }, query.after, query.limit ?? PAGE);
28549
28752
  if (!page) throw cursorGone2();
28550
- return ok(browserHistoryV6.parse({ contract: "browser-history", version: 6, sessionId, head: query.head, ...page }));
28753
+ return ok(browserHistoryV7.parse({ contract: "browser-history", version: 7, sessionId, head: query.head, ...page }));
28551
28754
  } }),
28552
28755
  route({ method: "GET", path: "/api/v1/history/summary", session: true, query: browserHistorySummaryQueryV1, unreadable: unreadable2, handle: async ({ query }) => ok(browserHistorySummaryV5.parse({
28553
28756
  contract: "browser-history-summary",
@@ -28584,7 +28787,7 @@ function recordRoutes(root, sessionId, env) {
28584
28787
  route({ method: "GET", path: "/api/v1/commit/change", session: true, query: browserCommitChangeQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28585
28788
  const change2 = await cache.history.commitChange(query.commit, query.id);
28586
28789
  if (!change2) throw new HttpError(404, "NOT_FOUND", t2("server.commitNotFound"));
28587
- return ok(browserCommitChangeV1.parse({ contract: "browser-commit-change", version: 1, sessionId, commit: query.commit, event: change2.event, before: change2.before, after: change2.after }));
28790
+ return ok(browserCommitChangeV2.parse({ contract: "browser-commit-change", version: 2, sessionId, commit: query.commit, event: change2.event, before: change2.before, after: change2.after }));
28588
28791
  } }),
28589
28792
  // A record's page names the record; which commit added it is read from the history of the HEAD the reader is on.
28590
28793
  route({ method: "GET", path: "/api/v1/record", session: true, query: browserRecordQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
@@ -28592,12 +28795,19 @@ function recordRoutes(root, sessionId, env) {
28592
28795
  if (!found) throw new HttpError(404, "NOT_FOUND", t2("server.recordNotFound"));
28593
28796
  return ok(browserRecordV2.parse({ contract: "browser-record", version: 2, sessionId, head: query.head, id: query.id, ...found }));
28594
28797
  } }),
28798
+ // One document's decision flow in the HEAD the reader is on: the commits that changed it, a page of them at a time.
28799
+ route({ method: "GET", path: "/api/v1/document-history", session: true, query: browserDocumentHistoryQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28800
+ const found = await documentHistoryPage(source2, query.head, query.id, { after: query.after, limit: query.limit ?? PAGE });
28801
+ if (found === void 0) throw new HttpError(404, "NOT_FOUND", t2("server.documentNotFound"));
28802
+ if (found === null) throw cursorGone2();
28803
+ return ok(browserDocumentHistoryV1.parse({ contract: "browser-document-history", version: 1, sessionId, head: query.head, ...found }));
28804
+ } }),
28595
28805
  // What is not committed yet, worked out again on every request: the uncommitted entry of the records list.
28596
28806
  route({ method: "GET", path: "/api/v1/working", session: true, unreadable: unreadable2, handle: async () => ok(browserWorkingV1.parse({ contract: "browser-working", version: 1, sessionId, ...await workingOverview(working2) })) }),
28597
28807
  route({ method: "GET", path: "/api/v1/working/change", session: true, query: browserWorkingChangeQueryV1, unreadable: unreadable2, handle: async ({ query }) => {
28598
28808
  const change2 = await workingChange2(working2, query.id);
28599
28809
  if (!change2) throw new HttpError(404, "NOT_FOUND", t2("server.workingChangeNotFound"));
28600
- return ok(browserWorkingChangeV1.parse({ contract: "browser-working-change", version: 1, sessionId, ...change2 }));
28810
+ return ok(browserWorkingChangeV2.parse({ contract: "browser-working-change", version: 2, sessionId, ...change2 }));
28601
28811
  } }),
28602
28812
  // The source a commit changed beside its documents, read from Git when the commit or record page asks for it.
28603
28813
  route({ method: "GET", path: "/api/v1/commit/files", session: true, query: browserCommitFilesQueryV2, unreadable: unreadable2, handle: async ({ query }) => {
@@ -28973,11 +29183,11 @@ import { readFile as readFile11 } from "node:fs/promises";
28973
29183
  // src/commands/output.ts
28974
29184
  var formats = ["text", "json"];
28975
29185
  var CommandError = class extends Error {
28976
- constructor(code, message, details = {}, lines = []) {
29186
+ constructor(code, message, details = {}, lines2 = []) {
28977
29187
  super(message);
28978
29188
  this.code = code;
28979
29189
  this.details = details;
28980
- this.lines = lines;
29190
+ this.lines = lines2;
28981
29191
  }
28982
29192
  code;
28983
29193
  details;
@@ -28994,13 +29204,13 @@ async function runCommand(contract, format2, run) {
28994
29204
  const code = known ? error62.code : contract.toUpperCase() + "_FAILED";
28995
29205
  const message = known ? error62.message : t2("command.failed");
28996
29206
  const details = error62 instanceof CommandError ? error62.details : {};
28997
- const lines = error62 instanceof CommandError ? error62.lines : [];
28998
- process.stderr.write(format2 === "json" ? JSON.stringify({ ...envelope, ok: false, error: { code, message, ...details } }) + "\n" : [code + ": " + message, ...lines].join("\n") + "\n");
29207
+ const lines2 = error62 instanceof CommandError ? error62.lines : [];
29208
+ process.stderr.write(format2 === "json" ? JSON.stringify({ ...envelope, ok: false, error: { code, message, ...details } }) + "\n" : [code + ": " + message, ...lines2].join("\n") + "\n");
28999
29209
  process.exitCode = 1;
29000
29210
  }
29001
29211
  }
29002
- var section = (label, lines, indent = " ") => lines.length ? [label, ...lines.map((line2) => indent + line2)] : [];
29003
- var text2 = (lines) => lines.join("\n") + "\n";
29212
+ var section = (label, lines2, indent = " ") => lines2.length ? [label, ...lines2.map((line2) => indent + line2)] : [];
29213
+ var text2 = (lines2) => lines2.join("\n") + "\n";
29004
29214
 
29005
29215
  // src/commands/guide.ts
29006
29216
  var guideTopics = ["workflow", "spec", "design", "instructions", "records", "writing", "commit", "migrate"];
@@ -29071,8 +29281,8 @@ function findBlock(text4) {
29071
29281
  function isWrapperFile(text4) {
29072
29282
  const found = findBlock(text4);
29073
29283
  const own2 = found ? text4.slice(0, found.start) + text4.slice(found.end) : text4;
29074
- const lines = own2.split(/\r?\n/).map((line2) => line2.trim()).filter(Boolean);
29075
- return lines.length > 0 && lines.every((line2) => /^@(?:\.\/)?[^\s]+$/.test(line2) && candidatePaths.includes(line2.replace(/^@(?:\.\/)?/, "")));
29284
+ const lines2 = own2.split(/\r?\n/).map((line2) => line2.trim()).filter(Boolean);
29285
+ return lines2.length > 0 && lines2.every((line2) => /^@(?:\.\/)?[^\s]+$/.test(line2) && candidatePaths.includes(line2.replace(/^@(?:\.\/)?/, "")));
29076
29286
  }
29077
29287
  var CLAUDE_WRAPPER_PATH = "CLAUDE.md";
29078
29288
  function claudeWrapperFor(agents2) {
@@ -29662,6 +29872,10 @@ async function createDocument(project, kind2, path3, options) {
29662
29872
  for (const [field, value] of [["title", options.title], ["description", options.description]]) {
29663
29873
  if (/[\r\n]/.test(value)) throw new CommandError("INVALID_VALUE", t2("docs.singleLine", { field }));
29664
29874
  }
29875
+ if (options.style && kind2 !== "requirement") throw new CommandError("INVALID_VALUE", t2("docs.styleRequirementOnly"));
29876
+ const projectStyle = project.config.requirementStyle;
29877
+ const style2 = kind2 === "requirement" ? options.style ?? projectStyle ?? "default" : void 0;
29878
+ const styleKey = style2 === "usecase" || style2 === "default" && projectStyle === "usecase" ? { style: style2 } : {};
29665
29879
  const target = newPath(kind2, path3);
29666
29880
  const { documents } = await project.cache.documents.list();
29667
29881
  const taken = new Set(documents.map((d) => d.id));
@@ -29672,9 +29886,10 @@ async function createDocument(project, kind2, path3, options) {
29672
29886
  const folder = target.slice(0, target.lastIndexOf("/") + 1);
29673
29887
  const siblings = documents.filter((d) => (d.kind === "requirement" || d.kind === "design") && d.path.startsWith(folder) && !d.path.slice(folder.length).includes("/"));
29674
29888
  const order = Math.min(999999, Math.max(0, ...siblings.map((d) => d.order)) + 10);
29675
- const base = { id, path: target, title: options.title.trim(), description: options.description.trim(), body: skeleton[kind2](), draft: true };
29889
+ const body2 = style2 === "usecase" ? t2("docs.skeleton.requirementUsecase") : skeleton[kind2]();
29890
+ const base = { id, path: target, title: options.title.trim(), description: options.description.trim(), body: body2, draft: true };
29676
29891
  const feature2 = target.split("/")[2];
29677
- 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 };
29892
+ const doc = kind2 === "feature" ? { kind: kind2, feature: feature2, ...base } : kind2 === "instruction" ? { kind: kind2, name: path3, ...base } : kind2 === "requirement" ? { kind: kind2, feature: feature2, order, ...styleKey, ...base } : { kind: kind2, feature: feature2, order, requirements: [], sources: [], ...base };
29678
29893
  const rendered = renderDocumentFile(doc);
29679
29894
  parseDocumentFile(target, rendered);
29680
29895
  try {
@@ -29732,13 +29947,14 @@ function cursorGone(options) {
29732
29947
  throw new CommandError("INVALID_VALUE", t2("list.after", { cursor: options.after ?? "" }));
29733
29948
  }
29734
29949
  var pageLine = (page, before = 0, unit = t2("list.items")) => page.next === null ? [] : [t2("list.more", { shown: before + page.rows.length, total: page.total, unit, cursor: page.next })];
29735
- function aside(format2, lines) {
29736
- if (format2 === "text" && lines.length) process.stderr.write(lines.join("\n") + "\n");
29950
+ function aside(format2, lines2) {
29951
+ if (format2 === "text" && lines2.length) process.stderr.write(lines2.join("\n") + "\n");
29737
29952
  return "";
29738
29953
  }
29739
29954
 
29740
29955
  // src/commands/specs.ts
29741
29956
  var specKinds = ["feature", "requirement", "design"];
29957
+ var specStyles = requirementStyles;
29742
29958
  var specSorts = ["order", "title", "updated"];
29743
29959
  var columns = ["id", "kind", "feature", "path", "title", "description", "order", "draft", "state", "previousPath", "requirements", "sources", "designs", "updated", "line"];
29744
29960
  var kindLabel = { feature: () => t2("docs.feature"), requirement: () => t2("docs.requirement"), design: () => t2("docs.design") };
@@ -30136,6 +30352,10 @@ var recordLine = (r, changes2) => {
30136
30352
  const changed = new Set(changes2.map((c) => c.id));
30137
30353
  return `${r.id} ${r.title}${r.draft ? " (" + t2("docs.draft") + ")" : ""} \u2014 ${r.docs.map((d) => changed.has(d) ? d : d + "*").join(", ")}`;
30138
30354
  };
30355
+ var formatWarnings = (changes2, documents, project) => {
30356
+ const touched = new Set(changes2.map((c) => c.id));
30357
+ return documents.filter((d) => touched.has(d.id)).flatMap((d) => requirementFormatWarnings(d, project.config.requirementStyle));
30358
+ };
30139
30359
  var recordSummary = (r) => ({ id: r.id, title: r.title, docs: r.docs, path: r.path, ...r.draft ? { draft: true } : {} });
30140
30360
  var runChangesList = (options, controls = {}) => runCommand("changes", options.format, async () => {
30141
30361
  const project = await openProject(process.cwd());
@@ -30146,7 +30366,8 @@ var runChangesList = (options, controls = {}) => runCommand("changes", options.f
30146
30366
  const withoutRecord = uncovered(changes2, records2);
30147
30367
  const sharedDocuments = shared(changes2, records2);
30148
30368
  const problems = [...working2.problems, ...pending.problems, ...checked.problems];
30149
- const warnings = await readDocumentWarnings(project.root, checked.documents);
30369
+ const formats2 = formatWarnings(changes2, checked.documents, project);
30370
+ const warnings = [...await readDocumentWarnings(project.root, checked.documents), ...formats2];
30150
30371
  const inputs = await prepareAgentInputs(project.root, controls).catch(() => void 0);
30151
30372
  const out = changes2.length ? [t2("changes.changed", { count: changes2.length }), ...changes2.map((c) => " " + changeLine(c))] : [t2("changes.none")];
30152
30373
  out.push(...section(t2("changes.pendingRecords", { count: records2.length }), records2.map((r) => recordLine(r, changes2))));
@@ -30154,7 +30375,8 @@ var runChangesList = (options, controls = {}) => runCommand("changes", options.f
30154
30375
  if (sharedDocuments.length) out.push(t2("changes.sharedDocuments", { ids: sharedDocuments.join(", ") }));
30155
30376
  if (pending.altered.length) out.push(t2("changes.recordAltered", { paths: pending.altered.join(", ") }));
30156
30377
  out.push(problems.length ? t2("changes.problems", { count: problems.length }) : t2("changes.clean"));
30157
- if (warnings.length) out.push(t2("changes.warnings", { count: warnings.length }));
30378
+ if (warnings.length > formats2.length) out.push(t2("changes.warnings", { count: warnings.length - formats2.length }));
30379
+ out.push(...section(t2("changes.formatWarnings", { count: formats2.length }), formats2.map((w) => w.code + " " + w.message)));
30158
30380
  if (inputs) out.push(t2("changes.input", { path: inputs.commit }));
30159
30381
  return { json: { head, changes: changes2, pendingRecords: records2.map(recordSummary), withoutRecord, sharedDocuments, alteredRecords: pending.altered, problems, warnings, inputs: inputs ?? null }, text: text2(out) };
30160
30382
  });
@@ -30169,6 +30391,7 @@ var runChangesCommit = (options, controls = {}) => runCommand("changes", options
30169
30391
  ...result.changes.map((c) => " " + changeLine(c)),
30170
30392
  ...result.records.map((r) => " " + r.id + " " + r.title),
30171
30393
  ...result.withoutRecord.length ? [t2("changes.withoutRecord", { ids: result.withoutRecord.join(", ") })] : [],
30394
+ ...section(t2("changes.formatWarnings", { count: result.warnings.length }), result.warnings.map((w) => w.code + " " + w.message)),
30172
30395
  ...result.trailers
30173
30396
  ];
30174
30397
  return { json: { ...json3, ...inputRemoved === void 0 ? {} : { inputRemoved } }, text: text2(out) };
@@ -30225,6 +30448,7 @@ async function commitChanges(cwd, input2, dryRun) {
30225
30448
  const split = changes2.filter((c) => c.previousPath !== void 0 && !(selected2.includes(c.path) && selected2.includes(c.previousPath)));
30226
30449
  if (split.length) fail3(t2("commit.selectMove", { ids: split.map((c) => c.id).join(", ") }));
30227
30450
  const withoutRecord = uncovered(changes2, records2);
30451
+ const warnings = formatWarnings(changes2, checked.documents, project);
30228
30452
  const status = (await git(["status", "--porcelain=v1", "-z", "--untracked-files=all", "--no-renames", "--", ".gitifact"])).toString("utf8").split("\0").filter(Boolean);
30229
30453
  const removed = new Set(status.filter((entry2) => entry2.slice(0, 2).includes("D")).map((entry2) => entry2.slice(3)));
30230
30454
  checkStoreSelection(selected2.filter((p) => !(underDocuments(p) && removed.has(p))));
@@ -30241,7 +30465,7 @@ async function commitChanges(cwd, input2, dryRun) {
30241
30465
  const contextPaths = policyPaths(selected2);
30242
30466
  const hashes = async (list) => new Map(await Promise.all(list.map(async (p) => [p, await fingerprint(root, p)])));
30243
30467
  const started = await hashes([.../* @__PURE__ */ new Set([...selected2, ...contextPaths])]);
30244
- const summary = { root, head: base.head, message, paths: selected2, changes: changes2, records: records2.map(recordSummary), withoutRecord, trailers };
30468
+ const summary = { root, head: base.head, message, paths: selected2, changes: changes2, records: records2.map(recordSummary), withoutRecord, warnings, trailers };
30245
30469
  if (dryRun) {
30246
30470
  if (!same2(base, await project.reader.baseline())) fail3(t2("commit.headChanged"));
30247
30471
  return { outcome: "dry-run", committed: false, ...summary, commit: void 0 };
@@ -30406,12 +30630,13 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
30406
30630
  const after = await since(project, options.since, head);
30407
30631
  const by = byAuthor(options.author);
30408
30632
  const has2 = contains(options.q);
30633
+ const hash3 = hashPrefix(options.q);
30409
30634
  const id = options.doc;
30410
- const events = head ? await project.cache.history.ofDocument(head, id) : [];
30411
- const { documents } = await project.cache.documents.list();
30412
- const title = documents.find((d) => d.id === id)?.title ?? events.map((e) => (e.after ?? e.before)?.title).find(Boolean) ?? null;
30413
- if (!title && !events.length) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: id }));
30414
- const page = paginate(events.filter((e) => after(e) && by(e) && has2(e.message, ...e.records.flatMap((r) => [r.title, ...sectionText(r.sections)]))).map((e) => ({
30635
+ const found2 = await documentHistory(project, head, id);
30636
+ if (!found2) throw new CommandError("UNKNOWN_DOCUMENT", t2("docs.unknownDocument", { ids: id }));
30637
+ const { events } = found2;
30638
+ const title = found2.doc.title;
30639
+ const page = paginate(events.filter((e) => after(e) && by(e) && (has2(...e.records.flatMap((r) => [r.title, r.id, ...sectionText(r.sections)])) || !!hash3 && e.commit.startsWith(hash3))).map((e) => ({
30415
30640
  commit: e.commit,
30416
30641
  date: e.date,
30417
30642
  author: e.author,
@@ -30449,7 +30674,7 @@ var runRecordsList = (options) => runCommand("records", options.format, async ()
30449
30674
  } catch {
30450
30675
  return [];
30451
30676
  }
30452
- }).map((r) => ({ id: r.id, title: r.title, commit: null, date: null, author: null, docs: r.docs, sections: r.sections.map((s) => ({ key: s.key, body: s.body })) }))).filter((r) => has(r.title, ...sectionText(r.sections))).sort((a, b) => a.id < b.id ? -1 : 1);
30677
+ }).map((r) => ({ id: r.id, title: r.title, commit: null, date: null, author: null, docs: r.docs, sections: r.sections.map((s) => ({ key: s.key, body: s.body })) }))).filter((r) => has(r.title, r.id, ...sectionText(r.sections))).sort((a, b) => a.id < b.id ? -1 : 1);
30453
30678
  const size = options.all ? void 0 : options.limit ?? PAGE_SIZE;
30454
30679
  const inPending = options.after === void 0 ? 0 : pending.findIndex((r) => r.id === options.after) + 1;
30455
30680
  const afterCommitted = options.after !== void 0 && !inPending ? options.after : void 0;
@@ -30759,23 +30984,23 @@ async function runUpdate(options, version2) {
30759
30984
  process.stdout.write(JSON.stringify(dto) + "\n");
30760
30985
  return;
30761
30986
  }
30762
- const lines = [t2("update.text.current", { version: dto.cliVersion })];
30763
- lines.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"));
30764
- if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
30765
- lines.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"));
30766
- if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
30767
- if (dto.project.state === "written") lines.push(t2("update.text.projectWritten", { path: CONFIG_PATH, version: dto.project.cli ?? dto.cliVersion }));
30768
- if (dto.project.cli && isNewerRelease(dto.project.cli, dto.cliVersion)) lines.push(t2("update.text.projectAhead", { project: dto.project.cli, version: dto.cliVersion, command: npmGlobalInstall(dto.project.cli) }));
30769
- if (dto.migrationRequired) lines.push(t2("update.text.migrationRequired", { version: dto.cliVersion }));
30987
+ const lines2 = [t2("update.text.current", { version: dto.cliVersion })];
30988
+ lines2.push(dto.update.status === "available" ? t2("update.text.available", { version: dto.update.latestVersion }) : dto.update.status === "up-to-date" ? t2("update.text.upToDate") : dto.update.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable"));
30989
+ if (dto.install) lines2.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
30990
+ lines2.push(dto.agentDocs.state === "refreshed" ? t2("update.text.blockRefreshed", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "current" ? t2("update.text.blockCurrent", { paths: dto.agentDocs.paths.join(", ") }) : dto.agentDocs.state === "kept" ? t2("update.text.blockKept", { paths: dto.agentDocs.paths.join(", ") || "-" }) : dto.agentDocs.state === "no-block" ? t2("update.text.noBlock") : t2("update.text.notInitialized"));
30991
+ if (dto.agentDocs.missing.length) lines2.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
30992
+ if (dto.project.state === "written") lines2.push(t2("update.text.projectWritten", { path: CONFIG_PATH, version: dto.project.cli ?? dto.cliVersion }));
30993
+ if (dto.project.cli && isNewerRelease(dto.project.cli, dto.cliVersion)) lines2.push(t2("update.text.projectAhead", { project: dto.project.cli, version: dto.cliVersion, command: npmGlobalInstall(dto.project.cli) }));
30994
+ if (dto.migrationRequired) lines2.push(t2("update.text.migrationRequired", { version: dto.cliVersion }));
30770
30995
  const { commit } = dto;
30771
- if (commit.state === "committed") lines.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
30772
- else if (commit.state === "nothing") lines.push(t2("update.text.commitNothing"));
30996
+ if (commit.state === "committed") lines2.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
30997
+ else if (commit.state === "nothing") lines2.push(t2("update.text.commitNothing"));
30773
30998
  else if (commit.state === "skipped") {
30774
30999
  const paths2 = commit.paths.join(", ");
30775
31000
  const text4 = commit.reason === "untracked" ? t2("update.text.commitSkipped.untracked", { paths: paths2 }) : commit.reason === "other-changes" ? t2("update.text.commitSkipped.other-changes", { paths: paths2 }) : t2("update.text.commitSkipped.commit-failed", { paths: paths2 });
30776
- lines.push(text4 + (commit.detail ? " (" + commit.detail + ")" : ""));
31001
+ lines2.push(text4 + (commit.detail ? " (" + commit.detail + ")" : ""));
30777
31002
  }
30778
- process.stdout.write(lines.join("\n") + "\n");
31003
+ process.stdout.write(lines2.join("\n") + "\n");
30779
31004
  } catch (error62) {
30780
31005
  const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
30781
31006
  const failure2 = { code: known ? error62.code : "UPDATE_FAILED", message: known ? error62.message : t2("update.failed") };
@@ -30804,12 +31029,12 @@ async function runUpdateCheck(format2, version2) {
30804
31029
  return;
30805
31030
  }
30806
31031
  const state2 = dto.update;
30807
- const lines = [
31032
+ const lines2 = [
30808
31033
  t2("update.text.current", { version: dto.cliVersion }),
30809
31034
  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")
30810
31035
  ];
30811
- if (state2.status === "available") lines.push(t2("update.text.install", { command: npmGlobalInstall(state2.latestVersion) }), t2("update.text.afterInstall"));
30812
- process.stdout.write(lines.join("\n") + "\n");
31036
+ if (state2.status === "available") lines2.push(t2("update.text.install", { command: npmGlobalInstall(state2.latestVersion) }), t2("update.text.afterInstall"));
31037
+ process.stdout.write(lines2.join("\n") + "\n");
30813
31038
  }
30814
31039
 
30815
31040
  // src/commands/version-notice.ts
@@ -30884,17 +31109,17 @@ for (let i = 0; i < languageArgs.length; i++) {
30884
31109
  if (value === "ko" || value === "en") selectedLanguage = value;
30885
31110
  }
30886
31111
  configureLanguage(selectedLanguage ?? environmentLanguage(), selectedLanguage);
30887
- var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.4").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
31112
+ var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.8.6").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
30888
31113
  program2.hook("preAction", async (_program, action) => {
30889
31114
  let top = action;
30890
31115
  while (top.parent && top.parent !== program2) top = top.parent;
30891
- if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.8.4", top.name());
31116
+ if (top.name() !== REFRESH_COMMAND) await noticeVersion("0.8.6", top.name());
30892
31117
  });
30893
- program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.8.4"));
31118
+ program2.command(REFRESH_COMMAND, { hidden: true }).allowExcessArguments(false).action(() => refreshUpdateCache("0.8.6"));
30894
31119
  var format = () => new Option("--format <format>", t2("help.format")).choices(formats).default("text");
30895
- 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.4"));
30896
- 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.4"));
30897
- 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.4") : options.check ? runUpdateCheck(options.format, "0.8.4") : runUpdate(options, "0.8.4"));
31120
+ program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).action((options) => runBrowser(options, "0.8.6"));
31121
+ program2.command("init").description(t2("help.init")).allowExcessArguments(false).option("--dry-run", t2("help.initDryRun")).addOption(new Option("--agent <agent>", t2("help.initAgent")).choices([...agentPresetNames])).addOption(new Option("--remove-agents", t2("help.initRemoveAgents")).conflicts("skipAgents")).option("--skip-agents", t2("help.initSkipAgents")).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action((options) => runInit(options, "0.8.6"));
31122
+ program2.command("update").description(t2("help.update")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).option("--commit", t2("help.updateCommit")).addOption(new Option("--check", t2("help.updateCheck")).conflicts("commit")).addOption(new Option("--later", t2("help.updateLater")).conflicts(["check", "commit"])).action((options) => options.later ? runUpdateLater(options.format, "0.8.6") : options.check ? runUpdateCheck(options.format, "0.8.6") : runUpdate(options, "0.8.6"));
30898
31123
  var listed2 = (command, sorts) => {
30899
31124
  command.allowExcessArguments(false).option("--q <words>", t2("help.listQ")).option("--author <name>", t2("help.listAuthor"));
30900
31125
  if (sorts) command.addOption(new Option("--sort <key>", t2("help.listSort")).choices([...sorts]).default(sorts[0]));
@@ -30903,7 +31128,7 @@ var listed2 = (command, sorts) => {
30903
31128
  var specs = program2.command("specs").description(t2("help.specs"));
30904
31129
  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));
30905
31130
  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));
30906
- 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));
31131
+ 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));
30907
31132
  var instructions = program2.command("instructions").description(t2("help.instructions"));
30908
31133
  listed2(instructions.command("list").description(t2("help.instructionsList")), instructionSorts).action((o) => runInstructionsList(o));
30909
31134
  instructions.command("show").description(t2("help.instructionsShow")).argument("<targets...>", t2("help.instructionsShowTargets")).option("--ref <commit>", t2("help.showRef")).option("--file <path>", t2("help.instructionsShowFile")).addOption(format()).action((targets, o) => runInstructionsShow(targets, o));
@@ -30916,7 +31141,7 @@ program2.command("check").description(t2("help.check")).allowExcessArguments(fal
30916
31141
  var changes = program2.command("changes").description(t2("help.changes"));
30917
31142
  changes.command("list").description(t2("help.changesList")).allowExcessArguments(false).addOption(format()).action((o) => runChangesList(o));
30918
31143
  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));
30919
- 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.4"));
31144
+ program2.command("feedback").description(t2("help.feedback")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.feedbackFile")).option("--dry-run", t2("help.feedbackDryRun")).addOption(format()).action((o) => runFeedback(o, "0.8.6"));
30920
31145
  var guide = program2.command("guide").description(t2("help.guide"));
30921
31146
  guide.command("list").description(t2("help.guideList")).allowExcessArguments(false).addOption(format()).action((o) => runGuideList(o));
30922
31147
  guide.command("show").description(t2("help.guideShow")).argument("<topic>", t2("help.guideTopic")).allowExcessArguments(false).addOption(format()).action((topic, o) => runGuideShow(topic, o));