@tekmidian/pai 1.0.0 → 1.0.2

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 (218) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +67 -5
  3. package/dist/aibroker-client-BxzeEHPF.mjs +3 -0
  4. package/dist/{aibroker-client-CXEYBzbf.mjs → aibroker-client-CQPGqLxL.mjs} +1 -1
  5. package/dist/atomic-file-BorMa4T3.mjs +21 -0
  6. package/dist/{auto-route-D5SXrMpB.mjs → auto-route-B4azPk4l.mjs} +3 -3
  7. package/dist/auto-route-BjoMHKyI.mjs +11 -0
  8. package/dist/budget-Dod6rnA9.mjs +15 -0
  9. package/dist/{caller-identity-CVTuiLSs.mjs → caller-identity-yLFb7CKq.mjs} +6 -5
  10. package/dist/{capacity-DaXrVj3J.mjs → capacity-DEveQdFV.mjs} +3 -3
  11. package/dist/{chain-BcQlIlEd.mjs → chain-D-yvET_y.mjs} +5 -5
  12. package/dist/cli/index.mjs +1621 -357
  13. package/dist/cockpit-actions-BWunx3rY.mjs +20 -0
  14. package/dist/{cockpit-actions-ODmt8yF9.mjs → cockpit-actions-C8GlTzts.mjs} +84 -42
  15. package/dist/cockpit-memory-vTlFWV7F.mjs +20 -0
  16. package/dist/{config-BSXKyeJ1.mjs → config-Bu1_vbY8.mjs} +42 -3
  17. package/dist/{config-B955yYmo.mjs → config-Cxi-aBf4.mjs} +2 -2
  18. package/dist/{config-TaWmdTLx.mjs → config-DtENJ6cL.mjs} +1 -1
  19. package/dist/{config-D5chi2LO.mjs → config-XpZWOYOG.mjs} +2 -2
  20. package/dist/{controls-BGkK_Bdi.mjs → controls-BcKFHeG1.mjs} +37 -22
  21. package/dist/daemon/index.mjs +43 -42
  22. package/dist/{daemon-CorGh6Ub.mjs → daemon-ColVHwGZ.mjs} +103 -55
  23. package/dist/daemon-N7WhYi6I.mjs +45 -0
  24. package/dist/daemon-mcp/index.mjs +40 -39
  25. package/dist/{define-DGFu-sCf.mjs → define-CjJsmq-6.mjs} +1 -1
  26. package/dist/{detect-C1d5cUkT.mjs → detect-9CZNO0yf.mjs} +2 -2
  27. package/dist/detect-D1ziFFfA.mjs +10 -0
  28. package/dist/{embed-pass-CeQD8kRE.mjs → embed-pass-DIWjA68P.mjs} +5 -5
  29. package/dist/{embedding-gate-BHuBsgHD.mjs → embedding-gate-6rZrWRXa.mjs} +5 -5
  30. package/dist/{embedding-gate-Z7CJSOJk.mjs → embedding-gate-Cnk4YCyX.mjs} +3 -3
  31. package/dist/{embeddings-D4t6L-EH.mjs → embeddings-BVq3esBF.mjs} +1 -1
  32. package/dist/{embeddings-OF9Pt4by.mjs → embeddings-CIkJBKd-.mjs} +1 -1
  33. package/dist/{endreason-x1KOSrcO.mjs → endreason-C577xIQz.mjs} +7 -6
  34. package/dist/{env-DArqGq-Z.mjs → env-BE8mrsxr.mjs} +1 -1
  35. package/dist/factory-Bsib-qDG.mjs +9 -0
  36. package/dist/{factory-BtjbQ0np.mjs → factory-DjAAsqr_.mjs} +15 -13
  37. package/dist/{fallback-BIyEpPsQ.mjs → fallback-DFlItw1Z.mjs} +5 -5
  38. package/dist/{fmt-age-BiSHrTKN.mjs → fmt-age-ByDkvkoU.mjs} +1 -1
  39. package/dist/{health-C6jZWvRG.mjs → health-Dnym-qcT.mjs} +4 -4
  40. package/dist/{helpers-DFmYRCOa.mjs → helpers-BD6V-p9w.mjs} +2 -2
  41. package/dist/helpers-CMGjy_0N.mjs +10 -0
  42. package/dist/{helpers-CJpb_MNB.mjs → helpers-CRuBUas3.mjs} +87 -38
  43. package/dist/hooks/capture-all-events.mjs +52 -11
  44. package/dist/hooks/capture-session-summary.mjs +64 -47
  45. package/dist/hooks/capture-tool-output.mjs +49 -8
  46. package/dist/hooks/context-compression-hook.mjs +30 -10
  47. package/dist/hooks/initialize-session.mjs +1 -0
  48. package/dist/hooks/load-core-context.mjs +1 -1
  49. package/dist/hooks/load-project-context.mjs +32 -29
  50. package/dist/hooks/security-validator.mjs +45 -13
  51. package/dist/hooks/stop-hook.mjs +54 -16
  52. package/dist/hooks/supervision-banner.mjs +9 -0
  53. package/dist/hooks/sync-todo-to-md.mjs +147 -113
  54. package/dist/hooks/worker-edit-repair.mjs +2 -1
  55. package/dist/hooks/worker-guard.mjs +40 -16
  56. package/dist/hooks/worker-note.mjs +12 -5
  57. package/dist/hooks/worker-path-repair.mjs +1 -1
  58. package/dist/hooks/worker-pool-host.mjs +214 -116
  59. package/dist/hooks/worker-status-line.mjs +195 -114
  60. package/dist/index.d.mts +18 -0
  61. package/dist/index.mjs +12 -12
  62. package/dist/{indexer-backend-BM2CUSu8.mjs → indexer-backend-_a6UNhXJ.mjs} +149 -96
  63. package/dist/{ipc-client-BMqWw_gv.mjs → ipc-client-CaiGJnSM.mjs} +1 -1
  64. package/dist/{ipc-client-YYDD6zzU.mjs → ipc-client-ZRmDKd5Z.mjs} +1 -1
  65. package/dist/kill-tree-BG7dRVtp.mjs +10 -0
  66. package/dist/{kill-tree-BjYz5BxK.mjs → kill-tree-EWYaEk5v.mjs} +8 -8
  67. package/dist/{launch-CLp0OJxV.mjs → launch-CXVlZjHt.mjs} +8 -8
  68. package/dist/{launch-DgqQdAjI.mjs → launch-Jh5NVeNe.mjs} +3 -3
  69. package/dist/local-candidate-DLdwQt0s.mjs +112 -0
  70. package/dist/local-instances-DOxSgT_L.mjs +210 -0
  71. package/dist/local-instances-ZeRkkuS5.mjs +6 -0
  72. package/dist/{local-tiers-DPZYqv9m.mjs → local-tiers-BE4jGaMa.mjs} +2 -2
  73. package/dist/{local-tiers-DnBIAkbK.mjs → local-tiers-DNMI5W86.mjs} +3 -3
  74. package/dist/{main-config-ops-C0SflOWk.mjs → main-config-ops-BEMpuhDz.mjs} +3 -3
  75. package/dist/{main-resolver-DVPpSWVy.mjs → main-resolver-3Ns1HPmS.mjs} +18 -17
  76. package/dist/{migrate-BUJHadA8.mjs → migrate-DrA_OwlZ.mjs} +45 -14
  77. package/dist/min-max-DrSRUaWQ.mjs +16 -0
  78. package/dist/{model-inventory-Dasber3S.mjs → model-inventory-BFVSNFT3.mjs} +13 -12
  79. package/dist/{models-add-Bt7aUHDs.mjs → models-add-BtB3Se6f.mjs} +39 -38
  80. package/dist/operator-BTqvTIc8.mjs +7 -0
  81. package/dist/{operator-DZxcP-g8.mjs → operator-CyuB3aVw.mjs} +3 -3
  82. package/dist/{pai-marker-kIYuNt3c.mjs → pai-marker-DurzIbw8.mjs} +7 -6
  83. package/dist/{pane-audit-OGYrOAik.mjs → pane-audit-BQytYa60.mjs} +7 -7
  84. package/dist/{paths-DoRuFc60.mjs → paths-CFHTloSe.mjs} +3 -3
  85. package/dist/{paths-DoIyRnhx.mjs → paths-CHifubDh.mjs} +3 -3
  86. package/dist/{planner-DLGOYlVb.mjs → planner-BM27GBfC.mjs} +31 -30
  87. package/dist/{pool-CZ5MZaXg.mjs → pool-BRvoOkv6.mjs} +3 -3
  88. package/dist/{pool-cli-BxNSPzzY.mjs → pool-cli-B_YR8_Gl.mjs} +5 -5
  89. package/dist/{pool-cli-CbJfl37y.mjs → pool-cli-DkZTAlSW.mjs} +6 -6
  90. package/dist/postgres-BdBNkL_l.mjs +7 -0
  91. package/dist/{postgres-CDnd_k5f.mjs → postgres-C6ti4haA.mjs} +55 -34
  92. package/dist/{project-setup-DM4npnL3.mjs → project-setup-CfpApz5o.mjs} +131 -74
  93. package/dist/providers-BeDiVEb-.mjs +16 -0
  94. package/dist/{providers-UsKtLcUR.mjs → providers-CblTFrhz.mjs} +6 -6
  95. package/dist/{query-feedback-I-txSnld.mjs → query-feedback-BRfPtqki.mjs} +2 -2
  96. package/dist/registrable-C7b6givx.mjs +9 -0
  97. package/dist/{registrable-LrRIVYQc.mjs → registrable-Uzgmfyhz.mjs} +4 -4
  98. package/dist/render-Cc2j5QG9.mjs +13 -0
  99. package/dist/{render-BDbb4CF-.mjs → render-CwIrgxLi.mjs} +8 -8
  100. package/dist/{reranker-B4r4vykM.mjs → reranker-8g6xMwP7.mjs} +1 -1
  101. package/dist/{reranker-DxxMCtA0.mjs → reranker-CqCbpGnX.mjs} +1 -1
  102. package/dist/{root-BMeJuHOb.mjs → root-BWy0c2Lq.mjs} +11 -11
  103. package/dist/root-wC_6Fx0B.mjs +36 -0
  104. package/dist/route-table-B7zpoypi.mjs +15 -0
  105. package/dist/{router-L8Nu-OHF.mjs → router-CQSc3lk3.mjs} +17 -7
  106. package/dist/{router-D6Ec6Fid.mjs → router-DwHJRgG9.mjs} +1 -1
  107. package/dist/{routing-CUyiglfu.mjs → routing-BAAST9vT.mjs} +27 -308
  108. package/dist/{routing-ZuGlc0ge.mjs → routing-CENt4HH1.mjs} +13 -12
  109. package/dist/{run-ulAi5dFD.mjs → run-6Q9kCPem.mjs} +35 -33
  110. package/dist/{scan-B7I0gPe-.mjs → scan-ZQWm-_se.mjs} +6 -6
  111. package/dist/{local-candidate-jOjTNlOL.mjs → scheduler-CtUyWJ3O.mjs} +7 -109
  112. package/dist/scheduler-dmHXoRvm.mjs +3 -0
  113. package/dist/{scope-D5VSQxe4.mjs → scope-D-h6F6DN.mjs} +4 -4
  114. package/dist/{search-CPoE0830.mjs → search-RSyeQqj1.mjs} +18 -18
  115. package/dist/session-Bok2PqRS.mjs +35 -0
  116. package/dist/{session-CNXF6DLP.mjs → session-DVUExUnX.mjs} +55 -31
  117. package/dist/session-scan-BJI_ZO8M.mjs +9 -0
  118. package/dist/{session-scan-BDLEc4w1.mjs → session-scan-W-gbwJHM.mjs} +6 -6
  119. package/dist/{sources-CjhUKKtO.mjs → sources-ESUkZaWv.mjs} +1 -1
  120. package/dist/{status-DYydKQUI.mjs → status-Dm4dYciN.mjs} +4 -4
  121. package/dist/{status-CvVi9cDf.mjs → status-ggJ7lgVg.mjs} +17 -9
  122. package/dist/{stop-words-SdSL1WKH.mjs → stop-words-DSEYQdNe.mjs} +1 -1
  123. package/dist/{test-isolation-CoDeOVXb.mjs → test-isolation-lqSmYxr0.mjs} +1 -1
  124. package/dist/{timeouts-OwEONmc8.mjs → timeouts-1MLooEBB.mjs} +1 -1
  125. package/dist/{tool-errors-AeRQuG3Y.mjs → tool-errors-7E13StWd.mjs} +4 -3
  126. package/dist/{tool-errors-R5E0fCgf.mjs → tool-errors-CJWODvlo.mjs} +6 -6
  127. package/dist/{viewer-DtWj5Vti.mjs → viewer-DcpJwGKE.mjs} +15 -15
  128. package/dist/viewer-MWBVJP3u.mjs +22 -0
  129. package/dist/{wait-BMUR-U-U.mjs → wait-ByV0OyMZ.mjs} +3 -3
  130. package/dist/{worker-config-BLqa1yiD.mjs → worker-config-BDYNxbYz.mjs} +294 -92
  131. package/dist/worker-config-gmAq_-rI.mjs +41 -0
  132. package/dist/{workers-config-DEI9lrV4.mjs → workers-config-f8KrkY4L.mjs} +2 -2
  133. package/dist/{worktree-zmRf556h.mjs → worktree-CrnOqTBW.mjs} +37 -7
  134. package/dist/{zettelkasten-nigq7fqs.mjs → zettelkasten-B_rRRRvD.mjs} +5 -5
  135. package/dist/{zettelkasten-Cr_Xj0mG.mjs → zettelkasten-BzMm8REH.mjs} +29 -19
  136. package/docs/README.md +2 -0
  137. package/docs/cockpit.md +10 -4
  138. package/docs/commands/README.md +4 -4
  139. package/docs/commands/backup.md +2 -0
  140. package/docs/commands/install.md +29 -4
  141. package/docs/commands/providers.md +1 -1
  142. package/docs/commands/restore.md +4 -0
  143. package/docs/commands/setup.md +29 -4
  144. package/docs/commands/worker.md +1 -1
  145. package/docs/config.md +2 -0
  146. package/docs/features.md +299 -0
  147. package/docs/images/architecture.drawio +51 -0
  148. package/docs/images/architecture.svg +3 -0
  149. package/docs/images/cockpit-01-system-tokens.jpg +0 -0
  150. package/docs/images/cockpit-02-totals-cache.jpg +0 -0
  151. package/docs/images/cockpit-03-local-servers-models-pool.jpg +0 -0
  152. package/docs/images/cockpit-04-workers-memory.jpg +0 -0
  153. package/docs/images/cockpit-05-performance.jpg +0 -0
  154. package/docs/images/cockpit-06-runs-speed-routing.jpg +0 -0
  155. package/docs/images/cockpit-07-settings-registry.jpg +0 -0
  156. package/docs/install/index.md +29 -0
  157. package/docs/three-tier-coding.md +1 -1
  158. package/gemini-extension.json +1 -1
  159. package/package.json +1 -5
  160. package/pai-plugin.json +1 -1
  161. package/plugins/context-preservation/plugin.json +1 -1
  162. package/plugins/core/plugin.json +1 -1
  163. package/plugins/core/skills/setup/SKILL.md +41 -9
  164. package/plugins/creative/plugin.json +1 -1
  165. package/plugins/observability/plugin.json +1 -1
  166. package/plugins/productivity/plugin.json +1 -1
  167. package/plugins/semantic-search/plugin.json +1 -1
  168. package/plugins/ui/plugin.json +1 -1
  169. package/plugins/zettelkasten/plugin.json +1 -1
  170. package/src/hooks/session-autosave.sh +8 -1
  171. package/src/hooks/session-stop.sh +8 -1
  172. package/src/hooks/ts/capture-all-events.ts +7 -12
  173. package/src/hooks/ts/lib/context-fill.ts +2 -1
  174. package/src/hooks/ts/lib/path-repair.ts +1 -1
  175. package/src/hooks/ts/lib/project-utils/notify.ts +1 -0
  176. package/src/hooks/ts/lib/project-utils/session-notes.ts +23 -6
  177. package/src/hooks/ts/lib/raw-capture.ts +36 -0
  178. package/src/hooks/ts/lib/session-summary.ts +129 -0
  179. package/src/hooks/ts/lib/todo-sync.ts +120 -0
  180. package/src/hooks/ts/lib/worker-git-guard.ts +1 -1
  181. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +6 -10
  182. package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +5 -105
  183. package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
  184. package/src/hooks/ts/pre-tool-use/security-validator.ts +35 -14
  185. package/src/hooks/ts/session-end/capture-session-summary.ts +20 -128
  186. package/src/hooks/ts/session-start/load-core-context.ts +3 -3
  187. package/src/hooks/ts/session-start/load-project-context.ts +18 -22
  188. package/src/hooks/ts/stop/stop-hook.ts +13 -5
  189. package/src/hooks/ztk-auto.sh +7 -2
  190. package/dist/aibroker-client-JjE74jo0.mjs +0 -3
  191. package/dist/auto-route-BRk8NEFv.mjs +0 -11
  192. package/dist/budget-DuhQ2zzG.mjs +0 -14
  193. package/dist/cockpit-actions-DcQ-ucU8.mjs +0 -19
  194. package/dist/cockpit-memory-KV45PPO2.mjs +0 -19
  195. package/dist/daemon-C7yt_ptC.mjs +0 -44
  196. package/dist/detect-DRUkmHnb.mjs +0 -10
  197. package/dist/factory-DAGG9_n8.mjs +0 -9
  198. package/dist/helpers-DPJEHPq6.mjs +0 -10
  199. package/dist/kill-tree-LV0Q7Mte.mjs +0 -10
  200. package/dist/operator-CfL0HiBK.mjs +0 -7
  201. package/dist/postgres-DgqWyqhN.mjs +0 -7
  202. package/dist/providers-CTu2s1z2.mjs +0 -15
  203. package/dist/registrable-D9aNZOTh.mjs +0 -9
  204. package/dist/render-BSfVlYHj.mjs +0 -13
  205. package/dist/root-xI9DqVIe.mjs +0 -35
  206. package/dist/route-table-HjvnLVEU.mjs +0 -14
  207. package/dist/session-BJHzmUg2.mjs +0 -34
  208. package/dist/session-scan-YORKE1NO.mjs +0 -9
  209. package/dist/viewer-B8709jDN.mjs +0 -22
  210. package/dist/worker-config-D8tRuvo7.mjs +0 -40
  211. package/docs/images/cockpit-limits-routing.jpg +0 -0
  212. package/docs/images/cockpit-models.jpg +0 -0
  213. package/docs/images/cockpit-usage.jpg +0 -0
  214. package/docs/images/cockpit-workers-system.jpg +0 -0
  215. package/scripts/build-docs.mjs +0 -41
  216. package/scripts/build-hooks.mjs +0 -311
  217. package/scripts/build-skill-stubs.mjs +0 -216
  218. package/scripts/copy-defaults.mjs +0 -46
@@ -7998,6 +7998,13 @@ var init_scratch = __esm({
7998
7998
  }
7999
7999
  });
8000
8000
 
8001
+ // src/config/atomic-file.ts
8002
+ var init_atomic_file = __esm({
8003
+ "src/config/atomic-file.ts"() {
8004
+ "use strict";
8005
+ }
8006
+ });
8007
+
8001
8008
  // src/utils/fmt-age.ts
8002
8009
  var init_fmt_age = __esm({
8003
8010
  "src/utils/fmt-age.ts"() {
@@ -8009,6 +8016,7 @@ var init_fmt_age = __esm({
8009
8016
  var init_pending = __esm({
8010
8017
  "src/workers/pending.ts"() {
8011
8018
  "use strict";
8019
+ init_atomic_file();
8012
8020
  init_status();
8013
8021
  init_paths();
8014
8022
  init_worktree();
@@ -8080,6 +8088,7 @@ var init_tree = __esm({
8080
8088
  var init_endreason = __esm({
8081
8089
  "src/workers/endreason.ts"() {
8082
8090
  "use strict";
8091
+ init_atomic_file();
8083
8092
  init_paths();
8084
8093
  init_ledger();
8085
8094
  init_status();
@@ -7370,42 +7370,149 @@ function isWorkerSession(env = process.env) {
7370
7370
  }
7371
7371
 
7372
7372
  // src/hooks/ts/post-tool-use/sync-todo-to-md.ts
7373
- import { writeFileSync as writeFileSync2, existsSync as existsSync5, readFileSync as readFileSync4, mkdirSync as mkdirSync4 } from "fs";
7373
+ import { existsSync as existsSync5, readFileSync as readFileSync4, mkdirSync as mkdirSync4 } from "fs";
7374
+ import { dirname as dirname4 } from "path";
7375
+
7376
+ // src/hooks/ts/lib/todo-sync.ts
7377
+ import { writeFileSync, renameSync, unlinkSync } from "fs";
7378
+ import { dirname, join, basename } from "path";
7379
+ var MANAGED_HEADING = /^## Current Session[ \t]*$/;
7380
+ var FOOTER = /(\n---[ \t\r\n]*)?(\n\*Last updated:[^\n]*\*[ \t\r\n]*)?$/;
7381
+ function formatSessionTodos(todos) {
7382
+ const inProgress = todos.filter((t) => t.status === "in_progress");
7383
+ const pending = todos.filter((t) => t.status === "pending");
7384
+ const completed = todos.filter((t) => t.status === "completed");
7385
+ let content = "";
7386
+ if (inProgress.length > 0) {
7387
+ content += `### In Progress
7388
+
7389
+ `;
7390
+ for (const todo of inProgress) {
7391
+ content += `- [ ] **${todo.content}** _(${todo.activeForm})_
7392
+ `;
7393
+ }
7394
+ content += "\n";
7395
+ }
7396
+ if (pending.length > 0) {
7397
+ content += `### Pending
7398
+
7399
+ `;
7400
+ for (const todo of pending) {
7401
+ content += `- [ ] ${todo.content}
7402
+ `;
7403
+ }
7404
+ content += "\n";
7405
+ }
7406
+ if (completed.length > 0) {
7407
+ content += `### Completed
7408
+
7409
+ `;
7410
+ for (const todo of completed) {
7411
+ content += `- [x] ${todo.content}
7412
+ `;
7413
+ }
7414
+ content += "\n";
7415
+ }
7416
+ if (todos.length === 0) {
7417
+ content += `_(No active session tasks)_
7418
+
7419
+ `;
7420
+ }
7421
+ return content;
7422
+ }
7423
+ function splitSections(content) {
7424
+ const starts = [];
7425
+ let offset = 0;
7426
+ for (const line of content.split("\n")) {
7427
+ if (line.startsWith("## ")) starts.push(offset);
7428
+ offset += line.length + 1;
7429
+ }
7430
+ if (starts.length === 0) return { preamble: content, sections: [] };
7431
+ const sections = starts.map((s, i) => content.slice(s, starts[i + 1] ?? content.length));
7432
+ return { preamble: content.slice(0, starts[0]), sections };
7433
+ }
7434
+ function buildTodoContent(todos, existingContent, now = (/* @__PURE__ */ new Date()).toISOString()) {
7435
+ const body = existingContent.replace(FOOTER, "");
7436
+ const managed = `## Current Session
7437
+
7438
+ ${formatSessionTodos(todos)}`;
7439
+ const { preamble, sections } = splitSections(body);
7440
+ let out;
7441
+ const idx = sections.findIndex((s) => MANAGED_HEADING.test(s.split("\n", 1)[0]));
7442
+ if (idx >= 0) {
7443
+ const old = sections[idx];
7444
+ const trailing = old.slice(old.trimEnd().length);
7445
+ const replaced = sections.slice();
7446
+ replaced[idx] = managed.trimEnd() + (idx < sections.length - 1 ? trailing.length > 1 ? trailing : "\n\n" : "\n");
7447
+ out = preamble + replaced.join("");
7448
+ } else if (body.trim() === "") {
7449
+ out = `# TODO
7450
+
7451
+ ${managed}`;
7452
+ } else {
7453
+ const titleEnd = /^# [^\n]*\n\n?/.exec(preamble);
7454
+ const head = titleEnd ? preamble.slice(0, titleEnd[0].length) : "";
7455
+ const rest = preamble.slice(head.length);
7456
+ out = head + managed + rest + sections.join("");
7457
+ }
7458
+ if (!out.endsWith("\n")) out += "\n";
7459
+ return `${out}
7460
+ ---
7461
+
7462
+ *Last updated: ${now}*
7463
+ `;
7464
+ }
7465
+ function fixMalformedHeadings(content) {
7466
+ return content.replace(/^---#/gm, "#");
7467
+ }
7468
+ function writeFileAtomic(path, data) {
7469
+ const tmp = join(dirname(path), `.${basename(path)}.${process.pid}.tmp`);
7470
+ try {
7471
+ writeFileSync(tmp, data);
7472
+ renameSync(tmp, path);
7473
+ } catch (e) {
7474
+ try {
7475
+ unlinkSync(tmp);
7476
+ } catch {
7477
+ }
7478
+ throw e;
7479
+ }
7480
+ }
7374
7481
 
7375
7482
  // src/hooks/ts/lib/project-utils/paths.ts
7376
7483
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, linkSync, copyFileSync as copyFileSync2 } from "fs";
7377
- import { join as join3, basename } from "path";
7484
+ import { join as join4, basename as basename2 } from "path";
7378
7485
 
7379
7486
  // src/hooks/ts/lib/pai-paths.ts
7380
7487
  import { homedir as homedir2 } from "os";
7381
- import { resolve, join as join2 } from "path";
7488
+ import { resolve, join as join3 } from "path";
7382
7489
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
7383
7490
 
7384
7491
  // src/config/pai-home.ts
7385
- import { existsSync, mkdirSync, readFileSync, copyFileSync, renameSync, readdirSync, statSync } from "node:fs";
7492
+ import { existsSync, mkdirSync, readFileSync, copyFileSync, renameSync as renameSync2, readdirSync, statSync } from "node:fs";
7386
7493
  import { homedir } from "node:os";
7387
- import { join, dirname } from "node:path";
7494
+ import { join as join2, dirname as dirname2 } from "node:path";
7388
7495
  function legacyPaiHomeDir() {
7389
- return join(homedir(), ".claude", "pai");
7496
+ return join2(homedir(), ".claude", "pai");
7390
7497
  }
7391
7498
  function claudeHomeDir() {
7392
- return process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude");
7499
+ return process.env.CLAUDE_CONFIG_DIR || join2(homedir(), ".claude");
7393
7500
  }
7394
7501
  function legacyConfigDir() {
7395
- return join(homedir(), ".config", "pai");
7502
+ return join2(homedir(), ".config", "pai");
7396
7503
  }
7397
7504
  function defaultPaiHomeDir() {
7398
- return join(homedir(), ".pai");
7505
+ return join2(homedir(), ".pai");
7399
7506
  }
7400
7507
  function paiHomeDir() {
7401
7508
  if (process.env.PAI_HOME) return process.env.PAI_HOME;
7402
7509
  const fresh = defaultPaiHomeDir();
7403
7510
  const legacy = legacyPaiHomeDir();
7404
- if (!existsSync(join(fresh, "config.yaml")) && existsSync(join(legacy, "config.yaml"))) return legacy;
7511
+ if (!existsSync(join2(fresh, "config.yaml")) && existsSync(join2(legacy, "config.yaml"))) return legacy;
7405
7512
  return fresh;
7406
7513
  }
7407
7514
  function paiHomePath(...segments) {
7408
- return join(paiHomeDir(), ...segments);
7515
+ return join2(paiHomeDir(), ...segments);
7409
7516
  }
7410
7517
  var noticesPrinted = /* @__PURE__ */ new Set();
7411
7518
  function resolvePaiFile(newPath, oldCandidates, migrateHint) {
@@ -7500,32 +7607,32 @@ function resolveAdapterDir() {
7500
7607
  return defaultAdapterDir();
7501
7608
  }
7502
7609
  var ADAPTER_DIR = resolveAdapterDir();
7503
- var HOOKS_DIR = join2(ADAPTER_DIR, "Hooks");
7504
- var SKILLS_DIR = join2(ADAPTER_DIR, "skills");
7505
- var AGENTS_DIR = join2(ADAPTER_DIR, "agents");
7506
- var COMMANDS_DIR = join2(ADAPTER_DIR, "commands");
7610
+ var HOOKS_DIR = join3(ADAPTER_DIR, "Hooks");
7611
+ var SKILLS_DIR = join3(ADAPTER_DIR, "skills");
7612
+ var AGENTS_DIR = join3(ADAPTER_DIR, "agents");
7613
+ var COMMANDS_DIR = join3(ADAPTER_DIR, "commands");
7507
7614
 
7508
7615
  // src/hooks/ts/lib/project-utils/paths.ts
7509
- var PROJECTS_DIR = join3(ADAPTER_DIR, "projects");
7616
+ var PROJECTS_DIR = join4(ADAPTER_DIR, "projects");
7510
7617
  function encodePath(path) {
7511
7618
  return path.replace(/\//g, "-").replace(/\./g, "-").replace(/ /g, "-");
7512
7619
  }
7513
7620
  function getProjectDir(cwd) {
7514
7621
  const encoded = encodePath(cwd);
7515
- return join3(PROJECTS_DIR, encoded);
7622
+ return join4(PROJECTS_DIR, encoded);
7516
7623
  }
7517
7624
  function getNotesDir(cwd) {
7518
- return join3(getProjectDir(cwd), "Notes");
7625
+ return join4(getProjectDir(cwd), "Notes");
7519
7626
  }
7520
7627
  function findNotesDir(cwd) {
7521
- const cwdBasename = basename(cwd).toLowerCase();
7628
+ const cwdBasename = basename2(cwd).toLowerCase();
7522
7629
  if (cwdBasename === "notes" && existsSync3(cwd)) {
7523
7630
  return { path: cwd, isLocal: true };
7524
7631
  }
7525
7632
  const localPaths = [
7526
- join3(cwd, "Notes"),
7527
- join3(cwd, "notes"),
7528
- join3(cwd, ".claude", "Notes")
7633
+ join4(cwd, "Notes"),
7634
+ join4(cwd, "notes"),
7635
+ join4(cwd, ".claude", "Notes")
7529
7636
  ];
7530
7637
  for (const path of localPaths) {
7531
7638
  if (existsSync3(path)) {
@@ -7536,20 +7643,20 @@ function findNotesDir(cwd) {
7536
7643
  }
7537
7644
  function findTodoPath(cwd) {
7538
7645
  const localPaths = [
7539
- join3(cwd, "TODO.md"),
7540
- join3(cwd, "notes", "TODO.md"),
7541
- join3(cwd, "Notes", "TODO.md"),
7542
- join3(cwd, ".claude", "TODO.md")
7646
+ join4(cwd, "TODO.md"),
7647
+ join4(cwd, "notes", "TODO.md"),
7648
+ join4(cwd, "Notes", "TODO.md"),
7649
+ join4(cwd, ".claude", "TODO.md")
7543
7650
  ];
7544
7651
  for (const path of localPaths) {
7545
7652
  if (existsSync3(path)) return path;
7546
7653
  }
7547
- return join3(getNotesDir(cwd), "TODO.md");
7654
+ return join4(getNotesDir(cwd), "TODO.md");
7548
7655
  }
7549
7656
 
7550
7657
  // src/hooks/ts/lib/project-utils/session-notes.ts
7551
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync3, writeFileSync, renameSync as renameSync2 } from "fs";
7552
- import { join as join4, basename as basename2 } from "path";
7658
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync3 } from "fs";
7659
+ import { join as join5, basename as basename3 } from "path";
7553
7660
  function getCurrentNotePath(notesDir) {
7554
7661
  if (!existsSync4(notesDir)) return null;
7555
7662
  const findLatestIn = (dir) => {
@@ -7560,18 +7667,18 @@ function getCurrentNotePath(notesDir) {
7560
7667
  return numA - numB;
7561
7668
  });
7562
7669
  if (files.length === 0) return null;
7563
- return join4(dir, files[files.length - 1]);
7670
+ return join5(dir, files[files.length - 1]);
7564
7671
  };
7565
7672
  const now = /* @__PURE__ */ new Date();
7566
7673
  const year = String(now.getFullYear());
7567
7674
  const month = String(now.getMonth() + 1).padStart(2, "0");
7568
- const currentMonthDir = join4(notesDir, year, month);
7675
+ const currentMonthDir = join5(notesDir, year, month);
7569
7676
  const found = findLatestIn(currentMonthDir);
7570
7677
  if (found) return found;
7571
7678
  const prevDate = new Date(now.getFullYear(), now.getMonth() - 1, 1);
7572
7679
  const prevYear = String(prevDate.getFullYear());
7573
7680
  const prevMonth = String(prevDate.getMonth() + 1).padStart(2, "0");
7574
- const prevMonthDir = join4(notesDir, prevYear, prevMonth);
7681
+ const prevMonthDir = join5(notesDir, prevYear, prevMonth);
7575
7682
  const prevFound = findLatestIn(prevMonthDir);
7576
7683
  if (prevFound) return prevFound;
7577
7684
  return findLatestIn(notesDir);
@@ -7608,8 +7715,8 @@ function addWorkToSessionNote(notePath, workItems, sectionTitle) {
7608
7715
  content = content.substring(0, nextStepsIndex) + workText + "\n" + content.substring(nextStepsIndex);
7609
7716
  }
7610
7717
  }
7611
- writeFileSync(notePath, content);
7612
- console.error(`Added ${workItems.length} work item(s) to: ${basename2(notePath)}`);
7718
+ writeFileSync2(notePath, content);
7719
+ console.error(`Added ${workItems.length} work item(s) to: ${basename3(notePath)}`);
7613
7720
  }
7614
7721
 
7615
7722
  // src/session/forge-sink.ts
@@ -7619,7 +7726,7 @@ var import_yaml4 = __toESM(require_dist(), 1);
7619
7726
  var PREAMBLE_MAX_BYTES = 16 * 1024;
7620
7727
 
7621
7728
  // src/daemon/config.ts
7622
- import { join as join5, dirname as dirname2, basename as basename3 } from "node:path";
7729
+ import { join as join6, dirname as dirname3, basename as basename4 } from "node:path";
7623
7730
 
7624
7731
  // src/config/shipped-defaults.ts
7625
7732
  var import_yaml3 = __toESM(require_dist(), 1);
@@ -7632,93 +7739,20 @@ var import_yaml = __toESM(require_dist(), 1);
7632
7739
 
7633
7740
  // src/daemon/config.ts
7634
7741
  var NEW_CONFIG_FILE = paiHomePath("config.json");
7635
- var oldConfigFile = () => join5(claudeHomeDir(), "pai.json");
7636
- var legacyConfigFile = () => join5(legacyConfigDir(), "config.json");
7742
+ var oldConfigFile = () => join6(claudeHomeDir(), "pai.json");
7743
+ var legacyConfigFile = () => join6(legacyConfigDir(), "config.json");
7637
7744
  function paiConfigFilePath() {
7638
7745
  const override = process.env.PAI_CONFIG_FILE;
7639
7746
  if (override) return override;
7640
7747
  return resolvePaiFile(NEW_CONFIG_FILE, [oldConfigFile(), legacyConfigFile()], "pai config migrate");
7641
7748
  }
7642
7749
  var CONFIG_FILE = paiConfigFilePath();
7643
- var CONFIG_DIR = dirname2(CONFIG_FILE);
7750
+ var CONFIG_DIR = dirname3(CONFIG_FILE);
7644
7751
 
7645
7752
  // src/session/checkpoint-block.ts
7646
7753
  var AUTHORED_GRACE_MS = 60 * 60 * 1e3;
7647
7754
 
7648
7755
  // src/hooks/ts/post-tool-use/sync-todo-to-md.ts
7649
- function formatSessionTodos(todos) {
7650
- const inProgress = todos.filter((t) => t.status === "in_progress");
7651
- const pending = todos.filter((t) => t.status === "pending");
7652
- const completed = todos.filter((t) => t.status === "completed");
7653
- let content = "";
7654
- if (inProgress.length > 0) {
7655
- content += `### In Progress
7656
-
7657
- `;
7658
- for (const todo of inProgress) {
7659
- content += `- [ ] **${todo.content}** _(${todo.activeForm})_
7660
- `;
7661
- }
7662
- content += "\n";
7663
- }
7664
- if (pending.length > 0) {
7665
- content += `### Pending
7666
-
7667
- `;
7668
- for (const todo of pending) {
7669
- content += `- [ ] ${todo.content}
7670
- `;
7671
- }
7672
- content += "\n";
7673
- }
7674
- if (completed.length > 0) {
7675
- content += `### Completed
7676
-
7677
- `;
7678
- for (const todo of completed) {
7679
- content += `- [x] ${todo.content}
7680
- `;
7681
- }
7682
- content += "\n";
7683
- }
7684
- if (todos.length === 0) {
7685
- content += `_(No active session tasks)_
7686
-
7687
- `;
7688
- }
7689
- return content;
7690
- }
7691
- function extractPreservedSections(content) {
7692
- let preserved = "";
7693
- const sectionRegex = /\n(## (?!Current Session)[^\n]+[\s\S]*?)(?=\n## |\n---\n+\*Last updated|$)/g;
7694
- const matches = content.matchAll(sectionRegex);
7695
- for (const match of matches) {
7696
- preserved += match[1];
7697
- }
7698
- return preserved;
7699
- }
7700
- function fixMalformedHeadings(content) {
7701
- return content.replace(/^---#/gm, "#");
7702
- }
7703
- function buildTodoContent(todos, existingContent) {
7704
- const now = (/* @__PURE__ */ new Date()).toISOString();
7705
- const preserved = extractPreservedSections(existingContent);
7706
- let content = `# TODO
7707
-
7708
- ## Current Session
7709
-
7710
- ${formatSessionTodos(todos)}`;
7711
- if (preserved.trim()) {
7712
- content += preserved;
7713
- }
7714
- content = content.replace(/(\n---\s*)*(\n\*Last updated:.*\*\s*)*$/, "");
7715
- content += `
7716
- ---
7717
-
7718
- *Last updated: ${now}*
7719
- `;
7720
- return content;
7721
- }
7722
7756
  async function main() {
7723
7757
  if (isWorkerSession()) return;
7724
7758
  try {
@@ -7743,7 +7777,7 @@ async function main() {
7743
7777
  const cwd = hookInput.cwd || process.cwd();
7744
7778
  const todoPath = findTodoPath(cwd);
7745
7779
  if (!existsSync5(todoPath)) {
7746
- const parentDir = todoPath.replace(/\/[^/]+$/, "");
7780
+ const parentDir = dirname4(todoPath);
7747
7781
  mkdirSync4(parentDir, { recursive: true });
7748
7782
  console.error(`Creating TODO.md at ${todoPath}`);
7749
7783
  }
@@ -7754,7 +7788,7 @@ async function main() {
7754
7788
  }
7755
7789
  let newContent = buildTodoContent(todos, existingContent);
7756
7790
  newContent = fixMalformedHeadings(newContent);
7757
- writeFileSync2(todoPath, newContent);
7791
+ writeFileAtomic(todoPath, newContent);
7758
7792
  const stats = {
7759
7793
  inProgress: todos.filter((t) => t.status === "in_progress").length,
7760
7794
  pending: todos.filter((t) => t.status === "pending").length,
@@ -9119,8 +9119,9 @@ function computeSimilarity(s1, s2) {
9119
9119
  break;
9120
9120
  }
9121
9121
  }
9122
+ const overlapCap = minLength - prefixLength;
9122
9123
  const totalLength = Math.max(s1.length, s2.length);
9123
- return totalLength > 0 ? (prefixLength + suffixLength) / totalLength : 0;
9124
+ return (prefixLength + Math.min(suffixLength, overlapCap)) / totalLength;
9124
9125
  }
9125
9126
 
9126
9127
  // src/hooks/ts/lib/render-decision.ts
@@ -9051,16 +9051,23 @@ function resolveToken() {
9051
9051
  }
9052
9052
  return process.env.FORGE_TOKEN || process.env.AIBROKER_FORGE_TOKEN || void 0;
9053
9053
  }
9054
- var curlHttp = (method, url, token, body, timeoutMs) => {
9055
- const cfg = [
9056
- `url = "${url}"`,
9057
- `request = "${method}"`,
9058
- `header = "Authorization: token ${token}"`,
9054
+ function curlQuote(v) {
9055
+ if (/[\r\n]/.test(v)) throw new ForgeError("refusing newline in curl config value");
9056
+ return v.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
9057
+ }
9058
+ function buildCurlConfig(method, url, token, timeoutMs) {
9059
+ return [
9060
+ `url = "${curlQuote(url)}"`,
9061
+ `request = "${curlQuote(method)}"`,
9062
+ `header = "Authorization: token ${curlQuote(token)}"`,
9059
9063
  `header = "Content-Type: application/json"`,
9060
9064
  `max-time = ${Math.max(0.5, timeoutMs / 1e3)}`,
9061
9065
  `silent`,
9062
9066
  `write-out = "\\n%{http_code}"`
9063
9067
  ].join("\n");
9068
+ }
9069
+ var curlHttp = (method, url, token, body, timeoutMs) => {
9070
+ const cfg = buildCurlConfig(method, url, token, timeoutMs);
9064
9071
  const argv = body !== void 0 ? ["-K", "-", "--data-raw", body] : ["-K", "-"];
9065
9072
  const r = spawnSync("curl", argv, { input: cfg, encoding: "utf8", timeout: timeoutMs + 500, maxBuffer: 16 * 1024 * 1024 });
9066
9073
  if (r.error || r.status !== 0) throw new ForgeError(`curl failed (${r.error?.message ?? `exit ${r.status}`})`);
@@ -9321,9 +9328,9 @@ function currentWorkerId(env = process.env, logDir) {
9321
9328
 
9322
9329
  // src/hooks/ts/pre-tool-use/worker-guard.ts
9323
9330
  import { execFileSync as execFileSync11 } from "node:child_process";
9324
- import { readFileSync as readFileSync18 } from "node:fs";
9331
+ import { readFileSync as readFileSync17 } from "node:fs";
9325
9332
  import { homedir as homedir3 } from "node:os";
9326
- import { dirname as dirname8, join as join16 } from "node:path";
9333
+ import { dirname as dirname9, join as join16 } from "node:path";
9327
9334
 
9328
9335
  // src/hooks/ts/lib/render-decision.ts
9329
9336
  var GUARD_MARKER = "PAI guard: ";
@@ -11094,7 +11101,24 @@ function recordToolError(logDir, rec, now = Date.now()) {
11094
11101
 
11095
11102
  // src/workers/loop-stop.ts
11096
11103
  import { execFileSync as execFileSync10 } from "node:child_process";
11097
- import { existsSync as existsSync16, readFileSync as readFileSync17, unlinkSync as unlinkSync2, writeFileSync as writeFileSync7 } from "node:fs";
11104
+
11105
+ // src/config/atomic-file.ts
11106
+ import { mkdirSync as mkdirSync7, renameSync as renameSync5, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
11107
+ import { randomBytes } from "node:crypto";
11108
+ import { dirname as dirname8 } from "node:path";
11109
+ function writeFileAtomic(path, text) {
11110
+ mkdirSync7(dirname8(path), { recursive: true });
11111
+ const tmp = `${path}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
11112
+ try {
11113
+ writeFileSync6(tmp, text, "utf8");
11114
+ renameSync5(tmp, path);
11115
+ } catch (e) {
11116
+ rmSync3(tmp, { force: true });
11117
+ throw e;
11118
+ }
11119
+ }
11120
+
11121
+ // src/workers/loop-stop.ts
11098
11122
  import { join as join15 } from "node:path";
11099
11123
 
11100
11124
  // src/workers/endreason.ts
@@ -11107,7 +11131,7 @@ function appendStopEvent(logDir, id, stop) {
11107
11131
  }
11108
11132
 
11109
11133
  // src/builds/queue.ts
11110
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync4, readFileSync as readFileSync16, renameSync as renameSync5, rmSync as rmSync3, rmdirSync, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
11134
+ import { existsSync as existsSync15, mkdirSync as mkdirSync8, readdirSync as readdirSync4, readFileSync as readFileSync16, renameSync as renameSync6, rmSync as rmSync4, rmdirSync, statSync as statSync5, writeFileSync as writeFileSync7 } from "node:fs";
11111
11135
  import { join as join14 } from "node:path";
11112
11136
  var defaultDeps = () => ({
11113
11137
  isAlive: (pid) => {
@@ -11135,7 +11159,7 @@ var ticketFile = (dir, id) => join14(ticketsDir(dir), `${id}.json`);
11135
11159
  function breakStaleMutex(m, staleMs = 5e3) {
11136
11160
  const guard = `${m}.break`;
11137
11161
  try {
11138
- mkdirSync7(guard);
11162
+ mkdirSync8(guard);
11139
11163
  } catch {
11140
11164
  try {
11141
11165
  if (Date.now() - statSync5(guard).mtimeMs > staleMs) rmdirSync(guard);
@@ -11154,12 +11178,12 @@ function breakStaleMutex(m, staleMs = 5e3) {
11154
11178
  }
11155
11179
  }
11156
11180
  async function withMutex(dir, fn) {
11157
- mkdirSync7(dir, { recursive: true });
11181
+ mkdirSync8(dir, { recursive: true });
11158
11182
  const m = join14(dir, "mutex");
11159
11183
  const t0 = Date.now();
11160
11184
  for (; ; ) {
11161
11185
  try {
11162
- mkdirSync7(m);
11186
+ mkdirSync8(m);
11163
11187
  break;
11164
11188
  } catch {
11165
11189
  breakStaleMutex(m);
@@ -11203,7 +11227,7 @@ async function releaseBuildsOf(dir, worker, d = {}) {
11203
11227
  const mine = readTickets(dir).filter((t) => t.worker === worker);
11204
11228
  for (const t of mine) {
11205
11229
  killTicket(t, deps, true);
11206
- rmSync3(ticketFile(dir, t.id), { force: true });
11230
+ rmSync4(ticketFile(dir, t.id), { force: true });
11207
11231
  }
11208
11232
  return mine.map((t) => t.id);
11209
11233
  });
@@ -11232,7 +11256,7 @@ function requestLoopStop(logDir, id, reason, deps = {}) {
11232
11256
  const { kill = (pid, sig) => process.kill(pid, sig), claudePid = defaultClaudePid } = deps;
11233
11257
  try {
11234
11258
  const m = { ts: (/* @__PURE__ */ new Date()).toISOString(), reason };
11235
- writeFileSync7(loopStopMarkerPath(logDir, id), JSON.stringify(m));
11259
+ writeFileAtomic(loopStopMarkerPath(logDir, id), JSON.stringify(m));
11236
11260
  } catch {
11237
11261
  }
11238
11262
  appendStopEvent(logDir, id, { killed_by: "loop-guard", forced: false, reason });
@@ -11267,7 +11291,7 @@ function buildContext(cwd) {
11267
11291
  home: homedir3(),
11268
11292
  worktreeRoot,
11269
11293
  inWorktree,
11270
- mainCheckout: inWorktree && common ? dirname8(common) : null,
11294
+ mainCheckout: inWorktree && common ? dirname9(common) : null,
11271
11295
  sharedCheckout: () => {
11272
11296
  let logDir = null;
11273
11297
  try {
@@ -11278,7 +11302,7 @@ function buildContext(cwd) {
11278
11302
  },
11279
11303
  testScript: () => {
11280
11304
  try {
11281
- const pkg = JSON.parse(readFileSync18(join16(worktreeRoot ?? cwd, "package.json"), "utf8"));
11305
+ const pkg = JSON.parse(readFileSync17(join16(worktreeRoot ?? cwd, "package.json"), "utf8"));
11282
11306
  return typeof pkg?.scripts?.test === "string" ? pkg.scripts.test : null;
11283
11307
  } catch {
11284
11308
  return null;
@@ -9043,16 +9043,23 @@ function resolveToken() {
9043
9043
  }
9044
9044
  return process.env.FORGE_TOKEN || process.env.AIBROKER_FORGE_TOKEN || void 0;
9045
9045
  }
9046
- var curlHttp = (method, url, token, body, timeoutMs) => {
9047
- const cfg = [
9048
- `url = "${url}"`,
9049
- `request = "${method}"`,
9050
- `header = "Authorization: token ${token}"`,
9046
+ function curlQuote(v) {
9047
+ if (/[\r\n]/.test(v)) throw new ForgeError("refusing newline in curl config value");
9048
+ return v.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
9049
+ }
9050
+ function buildCurlConfig(method, url, token, timeoutMs) {
9051
+ return [
9052
+ `url = "${curlQuote(url)}"`,
9053
+ `request = "${curlQuote(method)}"`,
9054
+ `header = "Authorization: token ${curlQuote(token)}"`,
9051
9055
  `header = "Content-Type: application/json"`,
9052
9056
  `max-time = ${Math.max(0.5, timeoutMs / 1e3)}`,
9053
9057
  `silent`,
9054
9058
  `write-out = "\\n%{http_code}"`
9055
9059
  ].join("\n");
9060
+ }
9061
+ var curlHttp = (method, url, token, body, timeoutMs) => {
9062
+ const cfg = buildCurlConfig(method, url, token, timeoutMs);
9056
9063
  const argv = body !== void 0 ? ["-K", "-", "--data-raw", body] : ["-K", "-"];
9057
9064
  const r = spawnSync("curl", argv, { input: cfg, encoding: "utf8", timeout: timeoutMs + 500, maxBuffer: 16 * 1024 * 1024 });
9058
9065
  if (r.error || r.status !== 0) throw new ForgeError(`curl failed (${r.error?.message ?? `exit ${r.status}`})`);
@@ -294,7 +294,7 @@ function cdReason(command, cwd) {
294
294
  if (existsSync2(target)) continue;
295
295
  const fixed = repairValue(target, cwd, true, existsSync2);
296
296
  if (!fixed) continue;
297
- const next = command.replace(target, fixed);
297
+ const next = command.replace(target, () => fixed);
298
298
  return `Wrong path in cd: ${target}. You are already in your worktree ${cwd}; drop the cd. Corrected command: ${next}`;
299
299
  }
300
300
  return null;