gitifact 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +45 -32
  2. package/dist/browser/assets/{Grid-D1SNqxih.js → Grid-DlI9bhVm.js} +1 -1
  3. package/dist/browser/assets/{Markdown-DKZFXPE0.js → Markdown-68DgpF6D.js} +1 -1
  4. package/dist/browser/assets/{MetadataListItem-BHqMTUIy.js → MetadataListItem-Bvdm7SCW.js} +1 -1
  5. package/dist/browser/assets/{Table-DykMjEgT.js → Table-GZptlOQa.js} +1 -1
  6. package/dist/browser/assets/{about-CwpTU41C.js → about-LBmmoJtj.js} +1 -1
  7. package/dist/browser/assets/{activity-DJ808sVo.js → activity-paVxugse.js} +1 -1
  8. package/dist/browser/assets/{changelog-DWK3Qw5N.js → changelog-DMndHW2p.js} +1 -1
  9. package/dist/browser/assets/{contributors._email-Bu46gkJ8.js → contributors._email-Ch0pZAte.js} +1 -1
  10. package/dist/browser/assets/{contributors.index-DGAkSUmH.js → contributors.index-BwSL0x8o.js} +1 -1
  11. package/dist/browser/assets/{document-CUHZSDYL.js → document-Ceyg9sKE.js} +1 -1
  12. package/dist/browser/assets/{features._featureId-CZ1QE69X.js → features._featureId-DtlIpe3Y.js} +1 -1
  13. package/dist/browser/assets/{features.index-C0Ecj5SM.js → features.index-CsxYbWGn.js} +1 -1
  14. package/dist/browser/assets/{getting-started-Bo0MbK7i.js → getting-started-LVh16CZ8.js} +1 -1
  15. package/dist/browser/assets/{git-C77vcInF.js → git-H0K9dpC3.js} +1 -1
  16. package/dist/browser/assets/{index-CgDfX0u7.js → index-DClmARNh.js} +16 -16
  17. package/dist/browser/assets/page-header-sbYRXZP4.js +531 -0
  18. package/dist/browser/assets/product-BIGIVBUa.js +10 -0
  19. package/dist/browser/assets/product-DNaVAIwO.css +1 -0
  20. package/dist/browser/assets/{product.index-C_eXOx4v.js → product.index-vzVtG_rC.js} +1 -1
  21. package/dist/browser/assets/{request-state-H0vXVi6T.js → request-state-J0QLm_8G.js} +1 -1
  22. package/dist/browser/assets/{requirements-jd-dp9SQ.js → requirements-POF-07kZ.js} +1 -1
  23. package/dist/browser/assets/{settings-CCOYFeaM.js → settings-eas7Zq56.js} +1 -1
  24. package/dist/browser/assets/{wiki-DZmdT1pI.js → wiki-fpAHbhnh.js} +1 -1
  25. package/dist/browser/index.html +10 -10
  26. package/dist/i18n/en/block.md +4 -4
  27. package/dist/i18n/en/changelog.md +11 -0
  28. package/dist/i18n/en/docs/workflow.md +8 -2
  29. package/dist/i18n/ko/block.md +4 -4
  30. package/dist/i18n/ko/changelog.md +11 -0
  31. package/dist/i18n/ko/docs/workflow.md +8 -2
  32. package/dist/main.js +284 -111
  33. package/package.json +1 -1
  34. package/dist/browser/assets/page-header-DcMV32eV.js +0 -505
  35. package/dist/browser/assets/product-BSEt07YP.css +0 -1
  36. package/dist/browser/assets/product-CN8SmBrX.js +0 -10
package/dist/main.js CHANGED
@@ -24917,6 +24917,69 @@ var updateV3 = external_exports.discriminatedUnion("ok", [
24917
24917
  external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(3), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
24918
24918
  ]);
24919
24919
 
24920
+ // ../../packages/contracts/dist/versions/project-init-v6.js
24921
+ var projectInitV6 = external_exports.discriminatedUnion("ok", [
24922
+ external_exports.strictObject({
24923
+ contract: external_exports.literal("project-init"),
24924
+ version: external_exports.literal(6),
24925
+ ok: external_exports.literal(true),
24926
+ outcome: external_exports.enum(["planned", "created", "replaced", "already-initialized"]),
24927
+ rootPath: external_exports.string(),
24928
+ configPath: external_exports.literal(".gitifact/config.json"),
24929
+ schemaVersion: external_exports.literal(2),
24930
+ baseline: external_exports.discriminatedUnion("kind", [
24931
+ external_exports.strictObject({ kind: external_exports.literal("empty") }),
24932
+ external_exports.strictObject({ kind: external_exports.literal("commit"), objectFormat: external_exports.enum(["sha1", "sha256"]), commit: external_exports.string() })
24933
+ ]),
24934
+ agentDocs: external_exports.strictObject({ mode: external_exports.enum(["install", "remove", "skip"]), paths: external_exports.array(external_exports.string()) }),
24935
+ update: updateStateV1,
24936
+ // Present only when a newer release exists. npx refreshes the project blocks without a global install; npmGlobal remains an alternative.
24937
+ install: external_exports.strictObject({ npx: external_exports.string().min(1), npmGlobal: external_exports.string().min(1) }).nullable()
24938
+ }),
24939
+ external_exports.strictObject({ contract: external_exports.literal("project-init"), version: external_exports.literal(6), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
24940
+ ]);
24941
+
24942
+ // ../../packages/contracts/dist/versions/update-v4.js
24943
+ var commitReason2 = external_exports.enum(["untracked", "other-changes", "commit-failed"]);
24944
+ var agentDocsCommit2 = external_exports.strictObject({
24945
+ state: external_exports.enum(["not-requested", "nothing", "committed", "skipped"]),
24946
+ commit: external_exports.string().nullable(),
24947
+ paths: external_exports.array(external_exports.string()),
24948
+ message: external_exports.string().nullable(),
24949
+ reason: commitReason2.nullable(),
24950
+ detail: external_exports.string().nullable()
24951
+ }).refine((value) => value.state === "committed" === (value.commit !== null) && value.state === "skipped" === (value.reason !== null), "commit is set exactly when committed, reason exactly when skipped.");
24952
+ var updateV4 = external_exports.discriminatedUnion("ok", [
24953
+ external_exports.strictObject({
24954
+ contract: external_exports.literal("update"),
24955
+ version: external_exports.literal(4),
24956
+ ok: external_exports.literal(true),
24957
+ cliVersion: external_exports.string().min(1),
24958
+ update: updateStateV1,
24959
+ // Present only when a newer release exists. npx refreshes the project blocks without a global install; npmGlobal remains an alternative.
24960
+ install: external_exports.strictObject({ npx: external_exports.string().min(1), npmGlobal: external_exports.string().min(1) }).nullable(),
24961
+ // refreshed: blocks whose version line differed were rewritten. current: every block already matched.
24962
+ // not-initialized: no .gitifact config here, so only the version check ran. no-block: no file carries a block.
24963
+ // missing: files `gitifact init` would add and update does not create, e.g. CLAUDE.md when only AGENTS.md has the block.
24964
+ agentDocs: external_exports.strictObject({ state: external_exports.enum(["refreshed", "current", "not-initialized", "no-block"]), paths: external_exports.array(external_exports.string()), missing: external_exports.array(external_exports.string()) }),
24965
+ commit: agentDocsCommit2
24966
+ }),
24967
+ external_exports.strictObject({ contract: external_exports.literal("update"), version: external_exports.literal(4), ok: external_exports.literal(false), error: external_exports.strictObject({ code: external_exports.string(), message: external_exports.string() }) })
24968
+ ]);
24969
+
24970
+ // ../../packages/contracts/dist/versions/browser-session-v3.js
24971
+ var browserSessionV3 = browserSessionV2.omit({ update: true }).extend({ version: external_exports.literal(3) });
24972
+
24973
+ // ../../packages/contracts/dist/versions/update-check-v1.js
24974
+ var updateCheckV1 = external_exports.strictObject({
24975
+ contract: external_exports.literal("update-check"),
24976
+ version: external_exports.literal(1),
24977
+ ok: external_exports.literal(true),
24978
+ cliVersion: external_exports.string().min(1),
24979
+ update: updateStateV1.refine((value) => value.status !== "checking", "The check must have finished."),
24980
+ command: external_exports.string().min(1).nullable()
24981
+ }).refine((value) => value.update.status === "available" === (value.command !== null), "An update command is present exactly when a newer release is available.");
24982
+
24920
24983
  // src/shared/i18n/index.ts
24921
24984
  import { AsyncLocalStorage } from "node:async_hooks";
24922
24985
 
@@ -25095,7 +25158,7 @@ var messages_default3 = {
25095
25158
  "init.text.blockUpdated": "\uBE14\uB85D \uAC31\uC2E0",
25096
25159
  "init.text.storage": "\uC800\uC7A5 \uADDC\uC57D",
25097
25160
  "init.text.agentDocs": "\uC5D0\uC774\uC804\uD2B8 \uC9C0\uCE68",
25098
- "init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC124\uCE58\uD55C \uB4A4 gitifact init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uC9C0\uCE68 \uBE14\uB85D\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uB429\uB2C8\uB2E4.",
25161
+ "init.text.updateAvailable": "\uC0C8 \uBC84\uC804 {version}\uC774 \uC788\uC2B5\uB2C8\uB2E4. {command}\uB85C \uC0C8 \uBC84\uC804\uC744 \uC2E4\uD589\uD558\uACE0 \uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68\uC744 \uAC31\uC2E0\uD558\uC138\uC694.",
25099
25162
  "init.failed": "\uCD08\uAE30\uD654\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uD30C\uC77C \uC811\uADFC \uAD8C\uD55C\uACFC \uC0DD\uC131\uB41C \uC124\uC815\xB7\uC784\uC2DC \uD30C\uC77C\uC744 \uD655\uC778\uD558\uC138\uC694.",
25100
25163
  "config.storeNotDirectory": ".gitifact\uB294 \uB9C1\uD06C\uAC00 \uC544\uB2CC \uC77C\uBC18 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
25101
25164
  "config.migrateFirst": ".tryce \uC800\uC7A5\uC18C\uC785\uB2C8\uB2E4. gitifact migrate\uB85C .gitifact\uC5D0 \uC804\uD658\uD558\uC138\uC694.",
@@ -25132,7 +25195,6 @@ var messages_default3 = {
25132
25195
  "docs.summary.design": "design.md \uBAA9\uCC28, \uCC38\uC870 \uC8FC\uC11D\uACFC \uCC38\uACE0 \uBB38\uC11C, \uAC1C\uC815 \uBC29\uC2DD",
25133
25196
  "docs.summary.writing": "\uC704\uD0A4\xB7\uC694\uAD6C\uC0AC\uD56D\xB7\uC124\uACC4\uC5D0 \uACF5\uD1B5\uC73C\uB85C \uC801\uC6A9\uD558\uB294 \uBB38\uCCB4, \uC778\uC6A9\uACFC Alert, AI \uC2AC\uB86D \uC81C\uAC70",
25134
25197
  "docs.summary.commit": "spec commit \uC785\uB825, \uC774\uC720 \uAE30\uB85D, \uC2E4\uD328 \uD6C4 \uBCF5\uAD6C",
25135
- "help.browserNoUpdateCheck": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC744 \uC704\uD55C npm \uB808\uC9C0\uC2A4\uD2B8\uB9AC \uC870\uD68C\uB97C \uB054 (GITIFACT_NO_UPDATE_CHECK\uC640 \uAC19\uC74C)",
25136
25198
  "help.updateCommit": "\uAC31\uC2E0\uD55C GITIFACT \uBE14\uB85D\uC774 HEAD\uC640 \uBE14\uB85D \uC548\uC5D0\uC11C\uB9CC \uB2E4\uB974\uBA74 \uADF8 \uD30C\uC77C\uB9CC \uACE0\uC815 \uBA54\uC2DC\uC9C0\uB85C \uCEE4\uBC0B",
25137
25199
  "help.update": "\uC0C8 \uBC84\uC804 \uD655\uC778\uACFC \uC124\uCE58 \uBC29\uBC95 \uC548\uB0B4, \uD504\uB85C\uC81D\uD2B8 \uC9C0\uCE68 \uD30C\uC77C\uC758 GITIFACT \uBE14\uB85D\uC744 \uD604\uC7AC \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0",
25138
25200
  "server.changelogNotFound": "\uD328\uCE58\uB178\uD2B8\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
@@ -25142,13 +25204,13 @@ var messages_default3 = {
25142
25204
  "update.text.upToDate": "\uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.",
25143
25205
  "update.text.disabled": "\uC0C8 \uBC84\uC804 \uD655\uC778\uC774 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4.",
25144
25206
  "update.text.unavailable": "\uC0C8 \uBC84\uC804\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uC138\uC694.",
25145
- "update.text.install": "npm \uC804\uC5ED \uC124\uCE58\uC6A9: {command}",
25146
- "update.text.afterInstall": "\uC124\uCE58\uD55C \uB4A4 gitifact update --commit\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uC9C0\uCE68 \uBE14\uB85D\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uB418\uACE0 \uCEE4\uBC0B\uB429\uB2C8\uB2E4.",
25207
+ "update.text.install": "\uC5C5\uB370\uC774\uD2B8 \uBA85\uB839: {command}",
25208
+ "update.text.afterInstall": "\uC704 \uBA85\uB839\uC740 \uC804\uC5ED \uC124\uCE58 \uC5C6\uC774 \uC0C8 \uBC84\uC804\uC73C\uB85C \uC9C0\uCE68\uC744 \uAC31\uC2E0\uD569\uB2C8\uB2E4. \uD504\uB85C\uC81D\uD2B8 \uC758\uC874\uC131\uC73C\uB85C \uAD00\uB9AC\uD55C\uB2E4\uBA74 \uD574\uB2F9 \uBC84\uC804\uC744 \uBA3C\uC800 \uAC31\uC2E0\uD558\uC138\uC694. \uBE0C\uB77C\uC6B0\uC800\uB3C4 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
25147
25209
  "update.text.blockRefreshed": "\uC9C0\uCE68 \uBE14\uB85D \uAC31\uC2E0: {paths}",
25148
25210
  "update.text.blockCurrent": "\uC9C0\uCE68 \uBE14\uB85D\uC740 \uC774\uBBF8 \uD604\uC7AC \uBC84\uC804\uC785\uB2C8\uB2E4: {paths}",
25149
- "update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. gitifact init\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694.",
25211
+ "update.text.noBlock": "GITIFACT \uBE14\uB85D\uC774 \uC788\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. npx --yes gitifact@{version} init\uC73C\uB85C \uC124\uCE58\uD558\uC138\uC694.",
25150
25212
  "update.text.notInitialized": "\uCD08\uAE30\uD654\uB41C \uD504\uB85C\uC81D\uD2B8\uAC00 \uC544\uB2C8\uC5B4\uC11C \uBC84\uC804\uB9CC \uD655\uC778\uD588\uC2B5\uB2C8\uB2E4.",
25151
- "update.text.missing": "{paths} \uD30C\uC77C\uC774 \uC5C6\uC5B4 Claude Code\uAC00 AGENTS.md\uC758 \uC9C0\uCE68\uC744 \uC77D\uC9C0 \uBABB\uD569\uB2C8\uB2E4. gitifact init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 AGENTS.md\uB97C \uAC00\uC838\uC624\uB294 \uD30C\uC77C\uC744 \uB9CC\uB4ED\uB2C8\uB2E4.",
25213
+ "update.text.missing": "{paths} \uD30C\uC77C\uC774 \uC5C6\uC5B4 Claude Code\uAC00 AGENTS.md\uC758 \uC9C0\uCE68\uC744 \uC77D\uC9C0 \uBABB\uD569\uB2C8\uB2E4. npx --yes gitifact@{version} init\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 AGENTS.md\uB97C \uAC00\uC838\uC624\uB294 \uD30C\uC77C\uC744 \uB9CC\uB4ED\uB2C8\uB2E4.",
25152
25214
  "update.text.committed": "\uC9C0\uCE68 \uBE14\uB85D \uCEE4\uBC0B: {commit} ({paths})",
25153
25215
  "update.text.commitNothing": "\uCEE4\uBC0B\uD560 \uC9C0\uCE68 \uBE14\uB85D \uBCC0\uACBD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
25154
25216
  "update.text.commitSkipped.untracked": "\uCEE4\uBC0B\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. Git\uC774 \uCD94\uC801\uD558\uC9C0 \uC54A\uB294 \uC9C0\uCE68 \uD30C\uC77C\uC785\uB2C8\uB2E4: {paths}",
@@ -25161,7 +25223,8 @@ var messages_default3 = {
25161
25223
  "docs.wikiPolicyProject": "\uC6B4\uC601 \uBC29\uCE68 (.gitifact/wiki/README.md)",
25162
25224
  "docs.wikiPolicyDefault": "\uC6B4\uC601 \uBC29\uCE68 (\uAE30\uBCF8\uAC12. .gitifact/wiki/README.md\uAC00 \uC5C6\uC5B4 \uB0B4\uC7A5 \uBC29\uCE68\uC744 \uC2E3\uB294\uB2E4)",
25163
25225
  "init.wikiReadmeTitle": "\uC704\uD0A4 \uC6B4\uC601 \uBC29\uCE68",
25164
- "help.lang": "\uCD9C\uB825 \uC5B8\uC5B4 (ko \uB610\uB294 en)"
25226
+ "help.lang": "\uCD9C\uB825 \uC5B8\uC5B4 (ko \uB610\uB294 en)",
25227
+ "help.updateCheck": "\uD30C\uC77C\uC744 \uBC14\uAFB8\uC9C0 \uC54A\uACE0 \uC0C8 \uBC84\uC804\uB9CC \uD655\uC778"
25165
25228
  };
25166
25229
 
25167
25230
  // src/shared/i18n/en/messages.json
@@ -25339,7 +25402,7 @@ var messages_default4 = {
25339
25402
  "init.text.blockUpdated": "Block refreshed",
25340
25403
  "init.text.storage": "Storage convention",
25341
25404
  "init.text.agentDocs": "Agent instructions",
25342
- "init.text.updateAvailable": "Version {version} is available. Install with {command}, then rerun gitifact init to refresh the instruction blocks.",
25405
+ "init.text.updateAvailable": "Version {version} is available. Run {command} to use it and refresh the project instructions.",
25343
25406
  "init.failed": "Initialization failed. Check file permissions and the generated configuration and temporary files.",
25344
25407
  "config.storeNotDirectory": ".gitifact must be a regular directory, not a link.",
25345
25408
  "config.migrateFirst": "This is a .tryce store. Run gitifact migrate to move it to .gitifact.",
@@ -25376,7 +25439,6 @@ var messages_default4 = {
25376
25439
  "docs.summary.design": "Design structure, requirement references, sources and revisions",
25377
25440
  "docs.summary.writing": "Writing style, quotations, alerts and removing filler from documentation",
25378
25441
  "docs.summary.commit": "spec commit input, change reasons and recovery after failure",
25379
- "help.browserNoUpdateCheck": "Disable the npm update check (also available through GITIFACT_NO_UPDATE_CHECK)",
25380
25442
  "help.updateCommit": "Commit only instruction files changed solely inside the GITIFACT block, using a fixed message",
25381
25443
  "help.update": "Check for updates, show installation instructions and refresh existing GITIFACT blocks",
25382
25444
  "server.changelogNotFound": "Release notes not found.",
@@ -25386,13 +25448,13 @@ var messages_default4 = {
25386
25448
  "update.text.upToDate": "You are using the latest version.",
25387
25449
  "update.text.disabled": "Update checks are disabled.",
25388
25450
  "update.text.unavailable": "Could not check for updates. Check your network connection.",
25389
- "update.text.install": "For a global npm installation: {command}",
25390
- "update.text.afterInstall": "After installation, rerun gitifact update --commit to refresh and commit the instruction blocks.",
25451
+ "update.text.install": "Update command: {command}",
25452
+ "update.text.afterInstall": "This command refreshes the instructions with the new version without a global installation. If you manage Gitifact as a project dependency, update that dependency first. Restart the browser with the new version.",
25391
25453
  "update.text.blockRefreshed": "Instruction blocks refreshed: {paths}",
25392
25454
  "update.text.blockCurrent": "Instruction blocks are already current: {paths}",
25393
- "update.text.noBlock": "No instruction files contain a GITIFACT block. Run gitifact init to install one.",
25455
+ "update.text.noBlock": "No instruction files contain a GITIFACT block. Run npx --yes gitifact@{version} init to install one.",
25394
25456
  "update.text.notInitialized": "Only the version was checked because this project is not initialized.",
25395
- "update.text.missing": "{paths} is missing, so Claude Code cannot load the AGENTS.md instructions. Rerun gitifact init to create the import file.",
25457
+ "update.text.missing": "{paths} is missing, so Claude Code cannot load the AGENTS.md instructions. Rerun npx --yes gitifact@{version} init to create the import file.",
25396
25458
  "update.text.committed": "Instruction blocks committed: {commit} ({paths})",
25397
25459
  "update.text.commitNothing": "No instruction block changes to commit.",
25398
25460
  "update.text.commitSkipped.untracked": "Not committed. Instruction files are untracked: {paths}",
@@ -25405,7 +25467,8 @@ var messages_default4 = {
25405
25467
  "docs.wikiPolicyProject": "Wiki policy (.gitifact/wiki/README.md)",
25406
25468
  "docs.wikiPolicyDefault": "Default wiki policy (.gitifact/wiki/README.md is missing)",
25407
25469
  "init.wikiReadmeTitle": "Wiki policy",
25408
- "help.lang": "Output language (ko or en)"
25470
+ "help.lang": "Output language (ko or en)",
25471
+ "help.updateCheck": "Check for a newer version without changing files"
25409
25472
  };
25410
25473
 
25411
25474
  // src/shared/i18n/index.ts
@@ -25504,63 +25567,13 @@ import { readFile as readFile5 } from "node:fs/promises";
25504
25567
 
25505
25568
  // src/server/status-session.ts
25506
25569
  import { randomUUID as randomUUID2 } from "node:crypto";
25507
-
25508
- // src/shared/update-check.ts
25509
- var updateCheckTimeoutMs = 3e3;
25510
- var release2 = /^(\d+)\.(\d+)\.(\d+)$/;
25511
- var running = /^(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?$/;
25512
- function isNewerRelease(latest, current2) {
25513
- const next = release2.exec(latest);
25514
- const now = running.exec(current2);
25515
- if (!next || !now) return false;
25516
- for (let index = 1; index <= 3; index++) {
25517
- const difference = Number(next[index]) - Number(now[index]);
25518
- if (difference !== 0) return difference > 0;
25519
- }
25520
- return now[4] !== void 0;
25521
- }
25522
- var updateCheckDisabled = (env, flagged = false) => flagged || env.GITIFACT_NO_UPDATE_CHECK !== void 0 && env.GITIFACT_NO_UPDATE_CHECK !== "" && env.GITIFACT_NO_UPDATE_CHECK !== "0";
25523
- var checkingUpdate = { status: "checking", latestVersion: null };
25524
- var disabledUpdate = { status: "disabled", latestVersion: null };
25525
- async function resolveUpdate(current2, fetchLatest, signal, timeoutMs = updateCheckTimeoutMs) {
25526
- const controller = new AbortController();
25527
- const abort = () => controller.abort();
25528
- const timer = setTimeout(abort, timeoutMs);
25529
- signal?.addEventListener("abort", abort, { once: true });
25530
- if (signal?.aborted) controller.abort();
25531
- try {
25532
- const latest = await Promise.race([
25533
- fetchLatest(controller.signal),
25534
- new Promise((_resolve, reject) => {
25535
- if (controller.signal.aborted) reject(new Error("aborted"));
25536
- else controller.signal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
25537
- })
25538
- ]);
25539
- if (typeof latest !== "string" || !release2.test(latest)) return { status: "unavailable", latestVersion: null };
25540
- return { status: isNewerRelease(latest, current2) ? "available" : "up-to-date", latestVersion: latest };
25541
- } catch {
25542
- return { status: "unavailable", latestVersion: null };
25543
- } finally {
25544
- clearTimeout(timer);
25545
- signal?.removeEventListener("abort", abort);
25546
- }
25547
- }
25548
- var npmGlobalInstall = (version2) => "npm install -g gitifact@" + version2;
25549
-
25550
- // src/server/status-session.ts
25551
25570
  function createStatusSession(initial, read, cliVersion = "0.0.0") {
25552
25571
  const identity2 = { sessionId: randomUUID2(), repository: { key: initial.repository.key, worktreeKey: initial.repository.worktreeKey } };
25553
- let update = disabledUpdate;
25572
+ const session = browserSessionV3.parse({ contract: "browser-session", version: 3, ...identity2, cliVersion });
25554
25573
  let latest = initial;
25555
25574
  let pending;
25556
25575
  return {
25557
- // The identity never changes; only the update state moves (checking → a final state), so the session is built per read.
25558
- get session() {
25559
- return browserSessionV2.parse({ contract: "browser-session", version: 2, ...identity2, cliVersion, update });
25560
- },
25561
- setUpdate(next) {
25562
- update = next;
25563
- },
25576
+ session,
25564
25577
  get latest() {
25565
25578
  return latest;
25566
25579
  },
@@ -26335,6 +26348,56 @@ function createCheckoutReader(root, sessionId, inherited = process.env) {
26335
26348
  };
26336
26349
  }
26337
26350
 
26351
+ // src/server/history/merge-records.ts
26352
+ function remergeRecords(patch) {
26353
+ const touched = /* @__PURE__ */ new Set();
26354
+ for (const file2 of patch.split(/^diff --git /m).slice(1)) {
26355
+ const lines = file2.split("\n");
26356
+ const oldPath = lines.find((l) => l.startsWith("--- "))?.slice(4);
26357
+ const newPath = lines.find((l) => l.startsWith("+++ "))?.slice(4);
26358
+ if (!oldPath && !newPath) continue;
26359
+ const path3 = (newPath === "/dev/null" ? oldPath : newPath)?.replace(/^[ab]\//, "");
26360
+ if (!path3 || !recordPathPattern.test(path3) || !path3.endsWith(".md")) continue;
26361
+ const start = lines.findIndex((l) => l.startsWith("@@ "));
26362
+ if (start < 0 || !/^@@ -[01](?:,\d+)? \+[01](?:,\d+)? @@/.test(lines[start])) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
26363
+ for (const side of ["-", "+"]) {
26364
+ const content = [];
26365
+ for (const line of lines.slice(start + 1)) {
26366
+ if (line.startsWith("@@ ")) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
26367
+ if (line.startsWith(" ") || line.startsWith(side)) content.push({ text: line.slice(1).replace(/\r$/, ""), changed: line.startsWith(side) });
26368
+ }
26369
+ if (!path3.endsWith("/requirements.md")) {
26370
+ const source2 = content.map((l) => l.text).join("\n");
26371
+ const id = /^(?:id: "?|<!-- (?:gitifact|tryce)-(?:design|product|guide): )([SWPG]-[a-z2-7]{10})(?:"?$| -->$)/m.exec(source2)?.[1];
26372
+ if (id && content.some((l) => l.changed)) touched.add(id);
26373
+ continue;
26374
+ }
26375
+ let section = [];
26376
+ let fence;
26377
+ const finish = () => {
26378
+ if (!section[0]?.text.startsWith("## ")) return;
26379
+ const id = section.map((l) => /^<!-- (?:gitifact|tryce)-req: (R-[a-z2-7]{10}) -->$/.exec(l.text)?.[1]).find(Boolean);
26380
+ if (id && section.some((l) => l.changed)) touched.add(id);
26381
+ };
26382
+ for (const line of content) {
26383
+ if (!fence && /^## /.test(line.text)) {
26384
+ finish();
26385
+ section = [];
26386
+ }
26387
+ section.push(line);
26388
+ if (fence) {
26389
+ if (new RegExp(`^ {0,3}${fence.char}{${fence.size},}\\s*$`).test(line.text)) fence = void 0;
26390
+ } else {
26391
+ const match = /^ {0,3}(`{3,}|~{3,})/.exec(line.text);
26392
+ if (match) fence = { char: match[1][0], size: match[1].length };
26393
+ }
26394
+ }
26395
+ finish();
26396
+ }
26397
+ }
26398
+ return touched;
26399
+ }
26400
+
26338
26401
  // src/server/history/commit-changes.ts
26339
26402
  var HISTORY_PATHSPECS = [
26340
26403
  ...STORE_DIRS.flatMap((d) => [`:(glob)${d}/spec/*/requirements.md`, `:(glob)${d}/spec/*/design.md`, `:(glob)${d}/spec/*/history.jsonl`]),
@@ -26362,7 +26425,7 @@ function createCommitChanges(root, snapshot3) {
26362
26425
  if (!oldBlob || !newBlob || path3 === void 0) throw new SpecPreviewError(t2("specReader.historyUnreadable"));
26363
26426
  if (recordPathPattern.test(path3) || isLegacy(path3)) entries.push({ oldBlob, newBlob, path: path3 });
26364
26427
  }
26365
- return { commit, parent: parents.split(" ")[0] || void 0, author, email: email3, date: date5, committer, message, entries };
26428
+ return { commit, parent: parents.split(" ")[0] || void 0, parents: parents.split(" ").filter(Boolean), author, email: email3, date: date5, committer, message, entries };
26366
26429
  });
26367
26430
  }
26368
26431
  function wanted(entries) {
@@ -26433,9 +26496,56 @@ function createCommitChanges(root, snapshot3) {
26433
26496
  }
26434
26497
  return { commit: c.commit, events: eventsOf(c, comparePreviewBundles(before, after).changes), boundary };
26435
26498
  }
26499
+ async function merge2(c) {
26500
+ let touched;
26501
+ if (c.parents.length === 2) {
26502
+ const patch = reader.decode(await reader.run([
26503
+ "-c",
26504
+ "merge.conflictStyle=merge",
26505
+ "show",
26506
+ "--remerge-diff",
26507
+ "--format=",
26508
+ "--no-color",
26509
+ "--no-ext-diff",
26510
+ "--no-textconv",
26511
+ "--no-renames",
26512
+ "--src-prefix=a/",
26513
+ "--dst-prefix=b/",
26514
+ "--unified=1048576",
26515
+ c.commit,
26516
+ "--",
26517
+ ...HISTORY_PATHSPECS
26518
+ ]));
26519
+ touched = remergeRecords(patch);
26520
+ if (!touched.size) return { commit: c.commit, events: [], boundary: false };
26521
+ }
26522
+ const after = await snapshot3(c.commit);
26523
+ let boundary = false;
26524
+ const parents = [];
26525
+ for (const parent of c.parents) {
26526
+ try {
26527
+ parents.push(await snapshot3(parent));
26528
+ } catch (error62) {
26529
+ if (error62 instanceof LegacyBaselineError) {
26530
+ parents.push(emptyBundle());
26531
+ boundary = true;
26532
+ } else throw error62;
26533
+ }
26534
+ }
26535
+ const withoutReasons = (b) => ({ specs: b.specs.map((s) => ({ ...s, history: [] })), wiki: { ...b.wiki, history: [] } });
26536
+ const differences = parents.map((p) => comparePreviewBundles(withoutReasons(p), withoutReasons(after)).changes);
26537
+ const reasons = (b) => [...b.specs.flatMap((s) => s.history), ...b.wiki.history];
26538
+ const inherited = new Set(parents.flatMap((p) => reasons(p).map((r) => r.id)));
26539
+ const added = reasons(after).filter((r) => !inherited.has(r.id));
26540
+ const selected = differences[0].filter((change2) => touched ? touched.has(change2.id) : differences.every((d) => d.some((v) => v.id === change2.id)));
26541
+ return { commit: c.commit, events: eventsOf(c, selected.map((change2) => ({
26542
+ ...change2,
26543
+ reasons: added.filter((r) => [...r.requirements, ...r.designs ?? [], ...r.documents ?? []].includes(change2.id))
26544
+ }))), boundary };
26545
+ }
26436
26546
  async function batch(commits) {
26437
26547
  const names = /* @__PURE__ */ new Map();
26438
- for (const c of commits.filter((c2) => !c2.entries.some((e) => isLegacy(e.path) && !isZero(e.newBlob)))) {
26548
+ for (const c of commits.filter((c2) => c2.parents.length < 2 && !c2.entries.some((e) => isLegacy(e.path) && !isZero(e.newBlob)))) {
26439
26549
  const files = wanted(c.entries);
26440
26550
  const boundary = c.entries.some((e) => isLegacy(e.path) && !isZero(e.oldBlob));
26441
26551
  names.set(c, { after: files.map((f) => c.commit + ":" + f), before: c.parent && !boundary ? files.map((f) => c.parent + ":" + f) : [], boundary });
@@ -26448,6 +26558,10 @@ function createCommitChanges(root, snapshot3) {
26448
26558
  }
26449
26559
  const result = [];
26450
26560
  for (const c of commits) {
26561
+ if (c.parents.length > 1) {
26562
+ result.push(await merge2(c));
26563
+ continue;
26564
+ }
26451
26565
  let value;
26452
26566
  const want = names.get(c);
26453
26567
  if (want && blobs) {
@@ -26465,12 +26579,12 @@ function createCommitChanges(root, snapshot3) {
26465
26579
  return result;
26466
26580
  }
26467
26581
  return {
26468
- /** The commits of `head`'s first-parent history that touched records, newest first. One Git process. */
26582
+ /** All reachable record commits, children before parents even when author clocks differ. */
26469
26583
  async lineage(head) {
26470
- const text2 = reader.decode(await reader.run(["rev-list", "--first-parent", head, "--", ...HISTORY_PATHSPECS]));
26584
+ const text2 = reader.decode(await reader.run(["rev-list", "--full-history", "--date-order", head, "--", ...HISTORY_PATHSPECS]));
26471
26585
  return text2.split("\n").filter(Boolean);
26472
26586
  },
26473
- /** The changes of the given commits, in the given order; two Git processes per hundred commits. */
26587
+ /** Changes in the given order; ordinary commits are batched, merges also read their resolution diff. */
26474
26588
  async of(commits) {
26475
26589
  const out = [];
26476
26590
  for (let i = 0; i < commits.length; i += BATCH) {
@@ -26493,7 +26607,7 @@ function createCommitChanges(root, snapshot3) {
26493
26607
  import { DatabaseSync } from "node:sqlite";
26494
26608
  import { mkdir as mkdir3, rm } from "node:fs/promises";
26495
26609
  import { join as join5 } from "node:path";
26496
- var INDEX_FORMAT = 1;
26610
+ var INDEX_FORMAT = 2;
26497
26611
  var SCHEMA = `
26498
26612
  -- Commits already read, and whether the commit's parent was legacy JSON the history does not read past.
26499
26613
  CREATE TABLE commits (oid TEXT PRIMARY KEY, boundary INTEGER NOT NULL);
@@ -26503,7 +26617,7 @@ var SCHEMA = `
26503
26617
  id TEXT NOT NULL, kind TEXT NOT NULL, types TEXT NOT NULL, email TEXT NOT NULL, date TEXT NOT NULL,
26504
26618
  before_spec TEXT, after_spec TEXT, needle TEXT NOT NULL, row TEXT NOT NULL, detail TEXT NOT NULL);
26505
26619
  CREATE INDEX changes_by_commit ON changes (oid, ord);
26506
- -- The commits of one HEAD's first-parent history that touched records, newest first (pos 0).
26620
+ -- All reachable record commits of one HEAD, children before parents (pos 0 is newest).
26507
26621
  CREATE TABLE lineage (head TEXT NOT NULL, pos INTEGER NOT NULL, oid TEXT NOT NULL, PRIMARY KEY (head, pos));
26508
26622
  CREATE INDEX lineage_by_commit ON lineage (head, oid);
26509
26623
  CREATE TABLE heads (head TEXT PRIMARY KEY, seen INTEGER NOT NULL);
@@ -26943,7 +27057,6 @@ async function startBrowserServer(options) {
26943
27057
  ...recordRoutes(root, sessionId, options.env),
26944
27058
  ...assetRoutes(root)
26945
27059
  ];
26946
- let updatePending;
26947
27060
  let closing = false;
26948
27061
  let origin = "";
26949
27062
  const server = createServer({ requestTimeout: 5e3, headersTimeout: 5e3, maxHeaderSize: 8192 }, (request, response) => {
@@ -26980,12 +27093,6 @@ async function startBrowserServer(options) {
26980
27093
  const address = server.address();
26981
27094
  if (!address || typeof address === "string") throw new Error(t2("server.addressUnknown"));
26982
27095
  origin = "http://127.0.0.1:" + address.port;
26983
- if (options.fetchLatest) {
26984
- store.setUpdate(checkingUpdate);
26985
- updatePending = resolveUpdate(store.session.cliVersion, options.fetchLatest, controller.signal, options.updateCheckTimeoutMs).then((state) => {
26986
- store.setUpdate(state);
26987
- });
26988
- }
26989
27096
  let resolveClosed;
26990
27097
  const closed = new Promise((resolve) => {
26991
27098
  resolveClosed = resolve;
@@ -27001,7 +27108,6 @@ async function startBrowserServer(options) {
27001
27108
  server.closeAllConnections();
27002
27109
  });
27003
27110
  await store.pending;
27004
- await updatePending;
27005
27111
  options.signal?.removeEventListener("abort", onAbort);
27006
27112
  options.signal?.removeEventListener("abort", closeOnAbort);
27007
27113
  resolveClosed();
@@ -27018,17 +27124,6 @@ async function startBrowserServer(options) {
27018
27124
  }, close, closed };
27019
27125
  }
27020
27126
 
27021
- // src/adapters/registry/latest-version.ts
27022
- var manifestUrl = "https://registry.npmjs.org/gitifact";
27023
- var fetchLatestVersion = async (signal) => {
27024
- const response = await fetch(manifestUrl, { signal, redirect: "error", headers: { Accept: "application/vnd.npm.install-v1+json" } });
27025
- if (!response.ok) throw new Error("registry status " + response.status);
27026
- const manifest = await response.json();
27027
- const latest = manifest["dist-tags"]?.latest;
27028
- if (typeof latest !== "string") throw new Error("registry answer has no latest tag");
27029
- return latest;
27030
- };
27031
-
27032
27127
  // src/commands/browser.ts
27033
27128
  function parsePort(value) {
27034
27129
  if (!/^\d+$/.test(value) || Number(value) > 65535) throw new InvalidArgumentError(t2("browser.invalidPort"));
@@ -27045,9 +27140,7 @@ async function runBrowser(options, version2) {
27045
27140
  port: options.port,
27046
27141
  dev: options.dev ?? false,
27047
27142
  signal: controller.signal,
27048
- cliVersion: version2,
27049
- // --no-update-check or GITIFACT_NO_UPDATE_CHECK keeps the server from contacting the npm registry.
27050
- ...updateCheckDisabled(process.env, options.updateCheck === false) ? {} : { fetchLatest: fetchLatestVersion }
27143
+ cliVersion: version2
27051
27144
  });
27052
27145
  if (!controller.signal.aborted) process.stdout.write(server.url + "/\n");
27053
27146
  await server.closed;
@@ -27417,6 +27510,48 @@ async function applyAgentDocs(root, plan, recheck) {
27417
27510
  return plan.writes.map((write) => write.path);
27418
27511
  }
27419
27512
 
27513
+ // src/shared/update-check.ts
27514
+ var updateCheckTimeoutMs = 3e3;
27515
+ var release2 = /^(\d+)\.(\d+)\.(\d+)$/;
27516
+ var running = /^(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]+)?$/;
27517
+ function isNewerRelease(latest, current2) {
27518
+ const next = release2.exec(latest);
27519
+ const now = running.exec(current2);
27520
+ if (!next || !now) return false;
27521
+ for (let index = 1; index <= 3; index++) {
27522
+ const difference = Number(next[index]) - Number(now[index]);
27523
+ if (difference !== 0) return difference > 0;
27524
+ }
27525
+ return now[4] !== void 0;
27526
+ }
27527
+ var updateCheckDisabled = (env, flagged = false) => flagged || env.GITIFACT_NO_UPDATE_CHECK !== void 0 && env.GITIFACT_NO_UPDATE_CHECK !== "" && env.GITIFACT_NO_UPDATE_CHECK !== "0";
27528
+ var disabledUpdate = { status: "disabled", latestVersion: null };
27529
+ async function resolveUpdate(current2, fetchLatest, signal, timeoutMs = updateCheckTimeoutMs) {
27530
+ const controller = new AbortController();
27531
+ const abort = () => controller.abort();
27532
+ const timer = setTimeout(abort, timeoutMs);
27533
+ signal?.addEventListener("abort", abort, { once: true });
27534
+ if (signal?.aborted) controller.abort();
27535
+ try {
27536
+ const latest = await Promise.race([
27537
+ fetchLatest(controller.signal),
27538
+ new Promise((_resolve, reject) => {
27539
+ if (controller.signal.aborted) reject(new Error("aborted"));
27540
+ else controller.signal.addEventListener("abort", () => reject(new Error("aborted")), { once: true });
27541
+ })
27542
+ ]);
27543
+ if (typeof latest !== "string" || !release2.test(latest)) return { status: "unavailable", latestVersion: null };
27544
+ return { status: isNewerRelease(latest, current2) ? "available" : "up-to-date", latestVersion: latest };
27545
+ } catch {
27546
+ return { status: "unavailable", latestVersion: null };
27547
+ } finally {
27548
+ clearTimeout(timer);
27549
+ signal?.removeEventListener("abort", abort);
27550
+ }
27551
+ }
27552
+ var npmGlobalInstall = (version2) => "npm install -g gitifact@" + version2;
27553
+ var npxUpdate = (version2) => "npx --yes gitifact@" + version2 + " update";
27554
+
27420
27555
  // src/commands/spec-init.ts
27421
27556
  async function initializeSpecProject(cwd, dryRun = false, env = process.env, beforePublish, agentDocs, readDoc = (name) => readBundledDoc(name, getLanguage()), update = disabledUpdate) {
27422
27557
  const repo = initRepository(cwd, env);
@@ -27425,9 +27560,9 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
27425
27560
  const docsPlan = agentDocs ? await planAgentDocs(root, agentDocs) : skippedAgentDocs;
27426
27561
  const result = async (config3, outcome) => {
27427
27562
  const checked = await update;
27428
- return projectInitV5.parse({
27563
+ return projectInitV6.parse({
27429
27564
  contract: "project-init",
27430
- version: 5,
27565
+ version: 6,
27431
27566
  ok: true,
27432
27567
  outcome,
27433
27568
  rootPath: root,
@@ -27436,7 +27571,7 @@ async function initializeSpecProject(cwd, dryRun = false, env = process.env, bef
27436
27571
  baseline: config3.baseline,
27437
27572
  agentDocs: { mode: docsPlan.mode, paths: docsPlan.paths },
27438
27573
  update: checked,
27439
- install: checked.status === "available" ? { npmGlobal: npmGlobalInstall(checked.latestVersion) } : null
27574
+ install: checked.status === "available" ? { npx: npxUpdate(checked.latestVersion), npmGlobal: npmGlobalInstall(checked.latestVersion) } : null
27440
27575
  });
27441
27576
  };
27442
27577
  const existing = async (text3) => {
@@ -27537,6 +27672,17 @@ async function writeWikiPolicy(root, readDoc) {
27537
27672
  await writeFile2(join10(root, ...WIKI_ENTRY_PATH.split("/")), text2, { flag: "wx" });
27538
27673
  }
27539
27674
 
27675
+ // src/adapters/registry/latest-version.ts
27676
+ var manifestUrl = "https://registry.npmjs.org/gitifact";
27677
+ var fetchLatestVersion = async (signal) => {
27678
+ const response = await fetch(manifestUrl, { signal, redirect: "error", headers: { Accept: "application/vnd.npm.install-v1+json" } });
27679
+ if (!response.ok) throw new Error("registry status " + response.status);
27680
+ const manifest = await response.json();
27681
+ const latest = manifest["dist-tags"]?.latest;
27682
+ if (typeof latest !== "string") throw new Error("registry answer has no latest tag");
27683
+ return latest;
27684
+ };
27685
+
27540
27686
  // src/commands/init.ts
27541
27687
  async function runInit(options, version2) {
27542
27688
  const cancel = new AbortController();
@@ -27553,7 +27699,7 @@ async function runInit(options, version2) {
27553
27699
  );
27554
27700
  if (!dto.ok) throw new Error("unreachable");
27555
27701
  const docs = dto.agentDocs.mode === "skip" ? t2("init.text.skipped") : dto.agentDocs.paths.join(", ") + (dto.agentDocs.mode === "remove" ? " (" + t2("init.text.blockRemoved") + ")" : dto.outcome === "planned" ? " (" + t2("init.text.blockPlanned") + ")" : " (" + t2("init.text.blockUpdated") + ")");
27556
- const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npmGlobal }) + "\n" : "";
27702
+ const newer = dto.install ? t2("init.text.updateAvailable", { version: dto.update.latestVersion, command: dto.install.npx }) + "\n" : "";
27557
27703
  process.stdout.write(options.format === "text" ? `${dto.outcome}: ${dto.rootPath}/.gitifact/config.json
27558
27704
  ${t2("init.text.storage")}: schemaVersion ${dto.schemaVersion}
27559
27705
  ${t2("init.text.agentDocs")}: ${docs}
@@ -27562,7 +27708,7 @@ ${newer}` : JSON.stringify(dto) + "\n");
27562
27708
  cancel.abort();
27563
27709
  const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
27564
27710
  const failure2 = { code: known ? error62.code : "INIT_FAILED", message: known ? error62.message : t2("init.failed") };
27565
- process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(projectInitV5.parse({ contract: "project-init", version: 5, ok: false, error: failure2 })) + "\n");
27711
+ process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(projectInitV6.parse({ contract: "project-init", version: 6, ok: false, error: failure2 })) + "\n");
27566
27712
  process.exitCode = 1;
27567
27713
  }
27568
27714
  }
@@ -27667,13 +27813,13 @@ async function updateCommand(cwd, version2, env = process.env, controls = {}) {
27667
27813
  refreshBlocks(cwd, version2, env, controls)
27668
27814
  ]);
27669
27815
  const commit = controls.commit ? await commitBlocks(agentDocs, version2, env) : commitState("not-requested");
27670
- return updateV3.parse({
27816
+ return updateV4.parse({
27671
27817
  contract: "update",
27672
- version: 3,
27818
+ version: 4,
27673
27819
  ok: true,
27674
27820
  cliVersion: version2,
27675
27821
  update,
27676
- install: update.status === "available" ? { npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
27822
+ install: update.status === "available" ? { npx: npxUpdate(update.latestVersion), npmGlobal: npmGlobalInstall(update.latestVersion) } : null,
27677
27823
  agentDocs: { state: agentDocs.state, paths: agentDocs.paths, missing: agentDocs.missing },
27678
27824
  commit
27679
27825
  });
@@ -27688,9 +27834,9 @@ async function runUpdate(options, version2) {
27688
27834
  }
27689
27835
  const lines = [t2("update.text.current", { version: dto.cliVersion })];
27690
27836
  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"));
27691
- if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npmGlobal }), t2("update.text.afterInstall"));
27692
- 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 === "no-block" ? t2("update.text.noBlock") : t2("update.text.notInitialized"));
27693
- if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", ") }));
27837
+ if (dto.install) lines.push(t2("update.text.install", { command: dto.install.npx }), t2("update.text.afterInstall"));
27838
+ 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 === "no-block" ? t2("update.text.noBlock", { version: dto.cliVersion }) : t2("update.text.notInitialized"));
27839
+ if (dto.agentDocs.missing.length) lines.push(t2("update.text.missing", { paths: dto.agentDocs.missing.join(", "), version: dto.cliVersion }));
27694
27840
  const { commit } = dto;
27695
27841
  if (commit.state === "committed") lines.push(t2("update.text.committed", { commit: commit.commit.slice(0, 12), paths: commit.paths.join(", ") }));
27696
27842
  else if (commit.state === "nothing") lines.push(t2("update.text.commitNothing"));
@@ -27703,11 +27849,38 @@ async function runUpdate(options, version2) {
27703
27849
  } catch (error62) {
27704
27850
  const known = error62 instanceof InitError || error62 instanceof RepositoryReadError;
27705
27851
  const failure2 = { code: known ? error62.code : "UPDATE_FAILED", message: known ? error62.message : t2("update.failed") };
27706
- process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(updateV3.parse({ contract: "update", version: 3, ok: false, error: failure2 })) + "\n");
27852
+ process.stderr.write(options.format === "text" ? failure2.code + ": " + failure2.message + "\n" : JSON.stringify(updateV4.parse({ contract: "update", version: 4, ok: false, error: failure2 })) + "\n");
27707
27853
  process.exitCode = 1;
27708
27854
  }
27709
27855
  }
27710
27856
 
27857
+ // src/commands/update-check.ts
27858
+ async function checkUpdateCommand(version2, env = process.env, controls = {}) {
27859
+ const update = updateCheckDisabled(env) ? disabledUpdate : await resolveUpdate(version2, controls.fetchLatest ?? fetchLatestVersion, void 0, controls.timeoutMs);
27860
+ return updateCheckV1.parse({
27861
+ contract: "update-check",
27862
+ version: 1,
27863
+ ok: true,
27864
+ cliVersion: version2,
27865
+ update,
27866
+ command: update.status === "available" ? npxUpdate(update.latestVersion) : null
27867
+ });
27868
+ }
27869
+ async function runUpdateCheck(format, version2) {
27870
+ const dto = await checkUpdateCommand(version2);
27871
+ if (format === "json") {
27872
+ process.stdout.write(JSON.stringify(dto) + "\n");
27873
+ return;
27874
+ }
27875
+ const state = dto.update;
27876
+ const lines = [
27877
+ t2("update.text.current", { version: dto.cliVersion }),
27878
+ state.status === "available" ? t2("update.text.available", { version: state.latestVersion }) : state.status === "up-to-date" ? t2("update.text.upToDate") : state.status === "disabled" ? t2("update.text.disabled") : t2("update.text.unavailable")
27879
+ ];
27880
+ if (dto.command) lines.push(t2("update.text.install", { command: dto.command }));
27881
+ process.stdout.write(lines.join("\n") + "\n");
27882
+ }
27883
+
27711
27884
  // src/commands/spec-preview.ts
27712
27885
  import { join as join15 } from "node:path";
27713
27886
  import { readFile as readFile12, stat as stat2 } from "node:fs/promises";
@@ -28296,16 +28469,16 @@ for (let i = 0; i < languageArgs.length; i++) {
28296
28469
  if (value === "ko" || value === "en") selectedLanguage = value;
28297
28470
  }
28298
28471
  configureLanguage(selectedLanguage ?? environmentLanguage(), selectedLanguage);
28299
- var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.7.0").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
28472
+ var program2 = new Command().name("gitifact").addOption(new Option("--lang <language>", t2("help.lang")).choices(["ko", "en"])).description(t2("help.program")).version("0.7.1").allowExcessArguments(false).addHelpText("after", "\n" + t2("help.notYet")).action(() => program2.outputHelp());
28300
28473
  program2.command("status").description(t2("help.status")).allowExcessArguments(false).addOption(new Option("--format <format>", t2("help.format")).choices(["json", "text"]).default("json")).action(async (options) => {
28301
28474
  await runStatus(options.format);
28302
28475
  });
28303
- program2.command("browser").description(t2("help.browser")).allowExcessArguments(false).option("--port <port>", t2("help.browserPort"), parsePort, 0).option("--dev", t2("help.browserDev")).option("--no-update-check", t2("help.browserNoUpdateCheck")).action((options) => runBrowser(options, "0.7.0"));
28304
- 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.7.0"));
28476
+ 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.7.1"));
28477
+ 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.7.1"));
28305
28478
  program2.command("docs").description(t2("help.docs")).argument("[topic]", t2("help.docsTopic")).allowExcessArguments(false).action(async (topic) => {
28306
28479
  await runDocs(topic);
28307
28480
  });
28308
- 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")).action((options) => runUpdate(options, "0.7.0"));
28481
+ 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")).action((options) => options.check ? runUpdateCheck(options.format, "0.7.1") : runUpdate(options, "0.7.1"));
28309
28482
  program2.command("migrate").description(t2("help.migrate")).allowExcessArguments(false).option("--dry-run", t2("help.migrateDryRun")).action(runMigrate);
28310
28483
  var spec = program2.command("spec").description(t2("help.spec"));
28311
28484
  spec.command("commit").description(t2("help.specCommit")).allowExcessArguments(false).requiredOption("--file <path>", t2("help.specCommitFile")).option("--dry-run", t2("help.specCommitDryRun")).action((o) => runSpecPreview("commit", o));