@tekmidian/pai 0.42.4 → 0.42.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 (292) hide show
  1. package/ARCHITECTURE.md +9 -9
  2. package/README.md +26 -8
  3. package/dist/{aibroker-client-BP74gGwD.mjs → aibroker-client-Bn9SnGMa.mjs} +3 -3
  4. package/dist/{aibroker-client-BP74gGwD.mjs.map → aibroker-client-Bn9SnGMa.mjs.map} +1 -1
  5. package/dist/{aibroker-client-C6m71p6O.mjs → aibroker-client-D5NT0S1N.mjs} +1 -1
  6. package/dist/{auto-route-_5zzJz5q.mjs → auto-route-CcD0OWWB.mjs} +4 -4
  7. package/dist/{auto-route-_5zzJz5q.mjs.map → auto-route-CcD0OWWB.mjs.map} +1 -1
  8. package/dist/{chain-DTII7xeL.mjs → chain-BbcfP77K.mjs} +5 -4
  9. package/dist/{chain-DTII7xeL.mjs.map → chain-BbcfP77K.mjs.map} +1 -1
  10. package/dist/cli/index.mjs +23 -21
  11. package/dist/cli/index.mjs.map +1 -1
  12. package/dist/cli/program.d.mts.map +1 -1
  13. package/dist/cli/program.mjs +23 -21
  14. package/dist/{clusters-j0ZuNYp9.mjs → clusters-Cw_P6lO-.mjs} +2 -2
  15. package/dist/{clusters-j0ZuNYp9.mjs.map → clusters-Cw_P6lO-.mjs.map} +1 -1
  16. package/dist/{config-B64vFg14.mjs → config-DCu8THKi.mjs} +41 -9
  17. package/dist/{config-B64vFg14.mjs.map → config-DCu8THKi.mjs.map} +1 -1
  18. package/dist/config-DeqFqa1N.mjs +3 -0
  19. package/dist/config-DxMZjpvY.mjs +1289 -0
  20. package/dist/config-DxMZjpvY.mjs.map +1 -0
  21. package/dist/context-handover-cache-B9NKSjwR.mjs +3242 -0
  22. package/dist/context-handover-cache-B9NKSjwR.mjs.map +1 -0
  23. package/dist/daemon/index.mjs +23 -22
  24. package/dist/daemon/index.mjs.map +1 -1
  25. package/dist/daemon-BNjbVsQU.mjs +24 -0
  26. package/dist/{daemon-B8N1egl0.mjs → daemon-BppziPi6.mjs} +52 -38
  27. package/dist/daemon-BppziPi6.mjs.map +1 -0
  28. package/dist/daemon-mcp/index.mjs +144 -117
  29. package/dist/daemon-mcp/index.mjs.map +1 -1
  30. package/dist/db-BOBfewFH.mjs +3 -0
  31. package/dist/{db-a1ixZQjr.mjs → db-DJ8rk0VM.mjs} +15 -6
  32. package/dist/{db-a1ixZQjr.mjs.map → db-DJ8rk0VM.mjs.map} +1 -1
  33. package/dist/db-DbY2O8QO.mjs +3 -0
  34. package/dist/{db-Ca5qfsMC.mjs → db-Ddns80yf.mjs} +15 -6
  35. package/dist/{db-Ca5qfsMC.mjs.map → db-Ddns80yf.mjs.map} +1 -1
  36. package/dist/{detect-CdaA48EI.mjs → detect-Bf2z-oKB.mjs} +1 -1
  37. package/dist/{detect-CdaA48EI.mjs.map → detect-Bf2z-oKB.mjs.map} +1 -1
  38. package/dist/detector-CkwkXuGh.mjs +5 -0
  39. package/dist/{detector-C73Nxm94.mjs → detector-DWXkeTlk.mjs} +2 -2
  40. package/dist/{detector-C73Nxm94.mjs.map → detector-DWXkeTlk.mjs.map} +1 -1
  41. package/dist/{embeddings-CEBGrzwu.mjs → embeddings-ClqKtLZ8.mjs} +1 -1
  42. package/dist/{embeddings-DOLZnT1X.mjs → embeddings-DvRKcQyt.mjs} +1 -1
  43. package/dist/{embeddings-DOLZnT1X.mjs.map → embeddings-DvRKcQyt.mjs.map} +1 -1
  44. package/dist/factory-47Qf_df7.mjs +3 -0
  45. package/dist/{factory-DEb9NmHJ.mjs → factory-DNYAqjH0.mjs} +8 -8
  46. package/dist/{factory-DEb9NmHJ.mjs.map → factory-DNYAqjH0.mjs.map} +1 -1
  47. package/dist/{fallback-oJl3BaIN.mjs → fallback-Cwt4hmh5.mjs} +182 -122
  48. package/dist/fallback-Cwt4hmh5.mjs.map +1 -0
  49. package/dist/{helpers-IjZkXBhj.mjs → helpers-crDEr6S2.mjs} +1 -1
  50. package/dist/{helpers-IjZkXBhj.mjs.map → helpers-crDEr6S2.mjs.map} +1 -1
  51. package/dist/hooks/capture-all-events.mjs +117 -34
  52. package/dist/hooks/capture-all-events.mjs.map +4 -4
  53. package/dist/hooks/capture-session-summary.mjs +110 -33
  54. package/dist/hooks/capture-session-summary.mjs.map +4 -4
  55. package/dist/hooks/capture-tool-output.mjs +105 -28
  56. package/dist/hooks/capture-tool-output.mjs.map +4 -4
  57. package/dist/hooks/cleanup-session-files.mjs +54 -13
  58. package/dist/hooks/cleanup-session-files.mjs.map +2 -2
  59. package/dist/hooks/context-compression-hook.mjs +7565 -122
  60. package/dist/hooks/context-compression-hook.mjs.map +4 -4
  61. package/dist/hooks/initialize-session.mjs +55 -14
  62. package/dist/hooks/initialize-session.mjs.map +2 -2
  63. package/dist/hooks/inject-observations.mjs +79 -18
  64. package/dist/hooks/inject-observations.mjs.map +3 -3
  65. package/dist/hooks/load-core-context.mjs +55 -14
  66. package/dist/hooks/load-core-context.mjs.map +2 -2
  67. package/dist/hooks/load-project-context.mjs +185 -103
  68. package/dist/hooks/load-project-context.mjs.map +4 -4
  69. package/dist/hooks/mcp-deferred-gate.mjs +54 -13
  70. package/dist/hooks/mcp-deferred-gate.mjs.map +2 -2
  71. package/dist/hooks/mcp-deferred-reminder.mjs +9 -0
  72. package/dist/hooks/mcp-deferred-reminder.mjs.map +3 -3
  73. package/dist/hooks/observe.mjs +54 -13
  74. package/dist/hooks/observe.mjs.map +2 -2
  75. package/dist/hooks/post-compact-inject.mjs +7419 -16
  76. package/dist/hooks/post-compact-inject.mjs.map +4 -4
  77. package/dist/hooks/route-agents-to-worker.mjs +7778 -171
  78. package/dist/hooks/route-agents-to-worker.mjs.map +4 -4
  79. package/dist/hooks/route-edits-to-worker.mjs +90 -0
  80. package/dist/hooks/route-edits-to-worker.mjs.map +7 -0
  81. package/dist/hooks/security-validator.mjs +147 -6
  82. package/dist/hooks/security-validator.mjs.map +4 -4
  83. package/dist/hooks/session-commands.mjs +9 -0
  84. package/dist/hooks/session-commands.mjs.map +3 -3
  85. package/dist/hooks/stop-hook.mjs +182 -105
  86. package/dist/hooks/stop-hook.mjs.map +4 -4
  87. package/dist/hooks/subagent-stop-hook.mjs +3 -0
  88. package/dist/hooks/subagent-stop-hook.mjs.map +1 -1
  89. package/dist/hooks/sync-todo-to-md.mjs +54 -13
  90. package/dist/hooks/sync-todo-to-md.mjs.map +2 -2
  91. package/dist/hooks/update-tab-on-action.mjs +3 -0
  92. package/dist/hooks/update-tab-on-action.mjs.map +1 -1
  93. package/dist/hooks/update-tab-titles.mjs +3 -0
  94. package/dist/hooks/update-tab-titles.mjs.map +1 -1
  95. package/dist/hooks/whisper-reinject.mjs +3 -0
  96. package/dist/hooks/whisper-reinject.mjs.map +1 -1
  97. package/dist/hooks/whisper-rules.mjs +134 -29
  98. package/dist/hooks/whisper-rules.mjs.map +4 -4
  99. package/dist/hooks/worker-proxy.mjs +7737 -117
  100. package/dist/hooks/worker-proxy.mjs.map +4 -4
  101. package/dist/hooks/worker-status-line.mjs +7791 -144
  102. package/dist/hooks/worker-status-line.mjs.map +4 -4
  103. package/dist/hooks/worker-supervision.mjs +7721 -115
  104. package/dist/hooks/worker-supervision.mjs.map +4 -4
  105. package/dist/index.d.mts +4 -2
  106. package/dist/index.d.mts.map +1 -1
  107. package/dist/index.mjs +10 -10
  108. package/dist/{indexer-backend-BPTzKFZU.mjs → indexer-backend-CdvCJCkD.mjs} +3 -3
  109. package/dist/{indexer-backend-BPTzKFZU.mjs.map → indexer-backend-CdvCJCkD.mjs.map} +1 -1
  110. package/dist/{ipc-client-BmypMNYk.mjs → ipc-client-C6NryMMb.mjs} +2 -2
  111. package/dist/{ipc-client-BmypMNYk.mjs.map → ipc-client-C6NryMMb.mjs.map} +1 -1
  112. package/dist/{kg-entity-r8duqhi9.mjs → kg-entity-BSjM5xZh.mjs} +1 -1
  113. package/dist/{kg-entity-r8duqhi9.mjs.map → kg-entity-BSjM5xZh.mjs.map} +1 -1
  114. package/dist/{latent-ideas-CtSca4qG.mjs → latent-ideas-BfL4nCQ3.mjs} +4 -4
  115. package/dist/{latent-ideas-CtSca4qG.mjs.map → latent-ideas-BfL4nCQ3.mjs.map} +1 -1
  116. package/dist/{link-boost-Bucnz3n8.mjs → link-boost-C5PaNdzm.mjs} +1 -1
  117. package/dist/{link-boost-Bucnz3n8.mjs.map → link-boost-C5PaNdzm.mjs.map} +1 -1
  118. package/dist/main-resolver-CnfnWa8C.mjs +7 -0
  119. package/dist/{main-resolver-DnkxPYkr.mjs → main-resolver-DvYu9jbl.mjs} +21 -11
  120. package/dist/main-resolver-DvYu9jbl.mjs.map +1 -0
  121. package/dist/{migrate-Cjzeefn9.mjs → migrate-CfERwXhY.mjs} +2 -2
  122. package/dist/{migrate-Cjzeefn9.mjs.map → migrate-CfERwXhY.mjs.map} +1 -1
  123. package/dist/{neighborhood-CM7Jgb51.mjs → neighborhood-CETR_P4y.mjs} +2 -2
  124. package/dist/{neighborhood-CM7Jgb51.mjs.map → neighborhood-CETR_P4y.mjs.map} +1 -1
  125. package/dist/{note-context-DCwE2A-M.mjs → note-context-DCcRWBoN.mjs} +1 -1
  126. package/dist/{note-context-DCwE2A-M.mjs.map → note-context-DCcRWBoN.mjs.map} +1 -1
  127. package/dist/pai-home-ChAOupZc.mjs +164 -0
  128. package/dist/pai-home-ChAOupZc.mjs.map +1 -0
  129. package/dist/{pai-marker-CHtbJMwJ.mjs → pai-marker-B20KqhA8.mjs} +1 -1
  130. package/dist/{pai-marker-CHtbJMwJ.mjs.map → pai-marker-B20KqhA8.mjs.map} +1 -1
  131. package/dist/{planner-D5Ui7DAm.mjs → planner-pXRc-8yt.mjs} +7 -6
  132. package/dist/{planner-D5Ui7DAm.mjs.map → planner-pXRc-8yt.mjs.map} +1 -1
  133. package/dist/{postgres-CtOzfoQU.mjs → postgres-CUSXWgTG.mjs} +2 -2
  134. package/dist/{postgres-CtOzfoQU.mjs.map → postgres-CUSXWgTG.mjs.map} +1 -1
  135. package/dist/{program-zpoYnJGz.mjs → program-LLNPpxlj.mjs} +8308 -6065
  136. package/dist/program-LLNPpxlj.mjs.map +1 -0
  137. package/dist/query-feedback-C7rSfiXU.mjs +3 -0
  138. package/dist/{query-feedback-Dqz-HC__.mjs → query-feedback-CYyOGi3y.mjs} +20 -6
  139. package/dist/query-feedback-CYyOGi3y.mjs.map +1 -0
  140. package/dist/{reranker-CwTCNsgA.mjs → reranker-B5qUTamW.mjs} +1 -1
  141. package/dist/{reranker-xPm04PXx.mjs → reranker-HHmhZk6v.mjs} +1 -1
  142. package/dist/{reranker-xPm04PXx.mjs.map → reranker-HHmhZk6v.mjs.map} +1 -1
  143. package/dist/router-CremHKTI.mjs +3 -0
  144. package/dist/{router-DD6D4j31.mjs → router-DBAZfE7X.mjs} +2 -2
  145. package/dist/{router-DD6D4j31.mjs.map → router-DBAZfE7X.mjs.map} +1 -1
  146. package/dist/{run-D7K5UALn.mjs → run-DDdT9teG.mjs} +530 -210
  147. package/dist/run-DDdT9teG.mjs.map +1 -0
  148. package/dist/{runtime-paths-rni52zHX.mjs → runtime-paths-B0P1TvUr.mjs} +1 -1
  149. package/dist/{runtime-paths-rni52zHX.mjs.map → runtime-paths-B0P1TvUr.mjs.map} +1 -1
  150. package/dist/{search-Rpk1cSBC.mjs → search-B69cQwDB.mjs} +3 -3
  151. package/dist/{search-Rpk1cSBC.mjs.map → search-B69cQwDB.mjs.map} +1 -1
  152. package/dist/{search-CfPpJAWQ.mjs → search-fb-5ovj4.mjs} +2 -2
  153. package/dist/{server-D4vAieF4.mjs → server-Ch99xt5K.mjs} +15 -14
  154. package/dist/server-Ch99xt5K.mjs.map +1 -0
  155. package/dist/skills/Advisor/SKILL.md +3 -3
  156. package/dist/skills/Providers/SKILL.md +30 -0
  157. package/dist/skills/Reconstruct/SKILL.md +1 -1
  158. package/dist/skills/VaultContext/SKILL.md +1 -1
  159. package/dist/skills/Whisper/SKILL.md +3 -3
  160. package/dist/skills/Worker/SKILL.md +1 -1
  161. package/dist/{sources-CS43CpuS.mjs → sources-KBGEA_E6.mjs} +2 -2
  162. package/dist/{sources-CS43CpuS.mjs.map → sources-KBGEA_E6.mjs.map} +1 -1
  163. package/dist/{sqlite-Cq3gJcq9.mjs → sqlite-By6r2YhZ.mjs} +3 -3
  164. package/dist/{sqlite-Cq3gJcq9.mjs.map → sqlite-By6r2YhZ.mjs.map} +1 -1
  165. package/dist/{state-CY6I-Hnc.mjs → state-DnYGRmS7.mjs} +1 -1
  166. package/dist/{state-BRdB-Bf1.mjs → state-PJrgfDLW.mjs} +1 -1
  167. package/dist/{state-BRdB-Bf1.mjs.map → state-PJrgfDLW.mjs.map} +1 -1
  168. package/dist/{stop-words-Hfu8u22w.mjs → stop-words-BaMEGVeY.mjs} +1 -1
  169. package/dist/{stop-words-Hfu8u22w.mjs.map → stop-words-BaMEGVeY.mjs.map} +1 -1
  170. package/dist/{sync-BWbe8JTg.mjs → sync-Btcb6xA4.mjs} +3 -3
  171. package/dist/{sync-BWbe8JTg.mjs.map → sync-Btcb6xA4.mjs.map} +1 -1
  172. package/dist/{themes-B7h9YDiy.mjs → themes-BVoAnLZj.mjs} +3 -3
  173. package/dist/{themes-B7h9YDiy.mjs.map → themes-BVoAnLZj.mjs.map} +1 -1
  174. package/dist/{tools-BW3NLMPk.mjs → tools-BradfQk7.mjs} +4 -3
  175. package/dist/{tools-LlzcV_eK.mjs → tools-UrSHIe29.mjs} +30 -233
  176. package/dist/tools-UrSHIe29.mjs.map +1 -0
  177. package/dist/{trace-u4hT-pLD.mjs → trace-Cy8quQTm.mjs} +1 -1
  178. package/dist/{trace-u4hT-pLD.mjs.map → trace-Cy8quQTm.mjs.map} +1 -1
  179. package/dist/{utils-DhMex3Ox.mjs → utils-BD8g8oSf.mjs} +1 -1
  180. package/dist/{utils-9Err2RBW.mjs → utils-CJymHkYm.mjs} +1 -1
  181. package/dist/{utils-9Err2RBW.mjs.map → utils-CJymHkYm.mjs.map} +1 -1
  182. package/dist/{vault-indexer-mata9S7b.mjs → vault-indexer-C5i-OAyr.mjs} +2 -2
  183. package/dist/{vault-indexer-mata9S7b.mjs.map → vault-indexer-C5i-OAyr.mjs.map} +1 -1
  184. package/dist/wakeup-TnGjJY3I.mjs +218 -0
  185. package/dist/wakeup-TnGjJY3I.mjs.map +1 -0
  186. package/dist/work-queue-worker-BA5IDBwQ.mjs +567 -0
  187. package/dist/work-queue-worker-BA5IDBwQ.mjs.map +1 -0
  188. package/dist/work-queue-worker-DLM2wlqM.mjs +14 -0
  189. package/dist/{zettelkasten-F0D_XfQ4.mjs → zettelkasten-BBjA7GcP.mjs} +4 -4
  190. package/dist/{zettelkasten-F0D_XfQ4.mjs.map → zettelkasten-BBjA7GcP.mjs.map} +1 -1
  191. package/docs/commands/README.md +30 -3
  192. package/docs/commands/audit.md +88 -0
  193. package/docs/commands/backup.md +2 -2
  194. package/docs/commands/clear-names.md +1 -1
  195. package/docs/commands/config.md +59 -0
  196. package/docs/commands/daemon.md +1 -1
  197. package/docs/commands/db.md +1 -1
  198. package/docs/commands/end.md +1 -1
  199. package/docs/commands/help.md +1 -1
  200. package/docs/commands/identity.md +1 -1
  201. package/docs/commands/kg.md +1 -1
  202. package/docs/commands/launch.md +45 -0
  203. package/docs/commands/mcp.md +1 -1
  204. package/docs/commands/memory.md +3 -3
  205. package/docs/commands/notify.md +1 -1
  206. package/docs/commands/observation.md +1 -1
  207. package/docs/commands/obsidian.md +1 -1
  208. package/docs/commands/pause.md +1 -1
  209. package/docs/commands/project.md +46 -1
  210. package/docs/commands/projects.md +46 -1
  211. package/docs/commands/registry.md +1 -1
  212. package/docs/commands/restore.md +1 -1
  213. package/docs/commands/session.md +1 -1
  214. package/docs/commands/sessions.md +1 -1
  215. package/docs/commands/setup.md +1 -1
  216. package/docs/commands/shell-init.md +1 -1
  217. package/docs/commands/skill.md +1 -1
  218. package/docs/commands/task.md +1 -1
  219. package/docs/commands/topic.md +1 -1
  220. package/docs/commands/update.md +1 -1
  221. package/docs/commands/worker.md +95 -6
  222. package/docs/commands/zettel.md +1 -1
  223. package/docs/provider-abstraction.md +1 -1
  224. package/docs/provider-independence-details.md +8 -5
  225. package/docs/task-bus.md +1 -1
  226. package/docs/worker.md +136 -83
  227. package/docs/workers-config.md +446 -0
  228. package/package.json +4 -2
  229. package/scripts/build-hooks.mjs +56 -3
  230. package/src/hooks/pre-compact.sh +5 -2
  231. package/src/hooks/session-stop.sh +29 -4
  232. package/src/hooks/ts/capture-all-events.ts +3 -3
  233. package/src/hooks/ts/lib/advisor-budget.test.ts +98 -0
  234. package/src/hooks/ts/lib/advisor-budget.ts +90 -0
  235. package/src/hooks/ts/lib/agent-gate.test.ts +101 -0
  236. package/src/hooks/ts/lib/agent-gate.ts +112 -0
  237. package/src/hooks/ts/lib/context-fill.ts +2 -2
  238. package/src/hooks/ts/lib/edit-gate.test.ts +52 -0
  239. package/src/hooks/ts/lib/edit-gate.ts +51 -0
  240. package/src/hooks/ts/lib/mcp-deferred-reminder.test.ts +8 -0
  241. package/src/hooks/ts/lib/pai-paths.test.ts +93 -0
  242. package/src/hooks/ts/lib/pai-paths.ts +168 -24
  243. package/src/hooks/ts/lib/project-utils/index.ts +1 -0
  244. package/src/hooks/ts/lib/project-utils/paths.ts +5 -4
  245. package/src/hooks/ts/lib/project-utils.ts +1 -0
  246. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +2 -2
  247. package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
  248. package/src/hooks/ts/pre-tool-use/route-agents-to-worker.ts +46 -99
  249. package/src/hooks/ts/pre-tool-use/route-edits-to-worker.test.ts +47 -0
  250. package/src/hooks/ts/pre-tool-use/route-edits-to-worker.ts +96 -0
  251. package/src/hooks/ts/pre-tool-use/security-validator.ts +2 -1
  252. package/src/hooks/ts/session-end/capture-session-summary.ts +4 -4
  253. package/src/hooks/ts/session-start/initialize-session.ts +3 -3
  254. package/src/hooks/ts/session-start/inject-observations.test.ts +56 -0
  255. package/src/hooks/ts/session-start/inject-observations.ts +28 -8
  256. package/src/hooks/ts/session-start/load-core-context.ts +4 -4
  257. package/src/hooks/ts/session-start/load-project-context.ts +2 -2
  258. package/src/hooks/ts/session-start/mcp-deferred-reminder.ts +2 -0
  259. package/src/hooks/ts/session-start/session-commands.ts +4 -0
  260. package/src/hooks/ts/session-start/session-start-worker-guard.test.ts +36 -0
  261. package/src/hooks/ts/stop/stop-hook.ts +6 -11
  262. package/src/hooks/ts/user-prompt/whisper-rules.test.ts +49 -0
  263. package/src/hooks/ts/user-prompt/whisper-rules.ts +88 -47
  264. package/statusline-command.sh +277 -116
  265. package/templates/agent-prefs.example.md +8 -8
  266. package/templates/claude-md.template.md +1 -1
  267. package/templates/voices.example.json +3 -3
  268. package/dist/config-C_ErGddD.mjs +0 -3
  269. package/dist/config-_lQI_W08.mjs +0 -640
  270. package/dist/config-_lQI_W08.mjs.map +0 -1
  271. package/dist/context-handover-cache-pNFdBNrU.mjs +0 -1548
  272. package/dist/context-handover-cache-pNFdBNrU.mjs.map +0 -1
  273. package/dist/daemon-B8N1egl0.mjs.map +0 -1
  274. package/dist/daemon-DHfyoTlk.mjs +0 -23
  275. package/dist/db-O-cyAPfS.mjs +0 -3
  276. package/dist/db-XEwJbuGO.mjs +0 -3
  277. package/dist/detector-C-7KRi42.mjs +0 -5
  278. package/dist/factory-Dbo8yNPi.mjs +0 -3
  279. package/dist/fallback-oJl3BaIN.mjs.map +0 -1
  280. package/dist/main-resolver-BAfOMppW.mjs +0 -7
  281. package/dist/main-resolver-DnkxPYkr.mjs.map +0 -1
  282. package/dist/program-zpoYnJGz.mjs.map +0 -1
  283. package/dist/query-feedback-DGoVRaWD.mjs +0 -3
  284. package/dist/query-feedback-Dqz-HC__.mjs.map +0 -1
  285. package/dist/router-BTIGZR2_.mjs +0 -3
  286. package/dist/run-D7K5UALn.mjs.map +0 -1
  287. package/dist/server-D4vAieF4.mjs.map +0 -1
  288. package/dist/tools-LlzcV_eK.mjs.map +0 -1
  289. package/dist/work-queue-worker-AEmfVE0F.mjs +0 -14
  290. package/dist/work-queue-worker-DIT-VLSh.mjs +0 -2136
  291. package/dist/work-queue-worker-DIT-VLSh.mjs.map +0 -1
  292. /package/dist/{indexer-D7MvSQPY.mjs → indexer-C5Le7XE7.mjs} +0 -0
@@ -32,11 +32,68 @@ input=$(cat)
32
32
  DA_NAME="${DA:-Assistant}" # Assistant name
33
33
  DA_COLOR="${DA_COLOR:-purple}" # Color for the assistant name
34
34
 
35
- # Extract data from JSON input
36
- current_dir=$(echo "$input" | jq -r '.workspace.current_dir')
37
- model_name=$(echo "$input" | jq -r '.model.display_name')
38
- model_id=$(echo "$input" | jq -r '.model.id // .model.display_name // empty')
39
- cc_version=$(echo "$input" | jq -r '.version // "unknown"')
35
+ # Where the status line keeps what it may cache between renders: the usage
36
+ # snapshots per provider and the Claude Code version. One knob rather than a
37
+ # path repeated six times, so a test (or a second user on the same machine)
38
+ # can point the whole lot somewhere private.
39
+ pai_cache_dir="${PAI_CACHE_DIR:-/tmp/claude}"
40
+
41
+ # PAI's per-user namespace dir — see src/config/pai-home.ts (paiHomeDir) for
42
+ # the canonical (TypeScript) version of this same resolver; this script
43
+ # can't import that module, so the new/old fallback logic is duplicated here.
44
+ pai_home_dir="${PAI_HOME:-$HOME/.claude/pai}"
45
+
46
+ # Resolve a PAI per-user file: the new PAI_HOME path if it exists, else the
47
+ # first existing old candidate (args 2+), else the new path. Mirrors
48
+ # resolvePaiFile() in src/config/pai-home.ts.
49
+ _pai_resolve_file() {
50
+ _new="$1"; shift
51
+ [ -f "$_new" ] && { printf '%s' "$_new"; return; }
52
+ for _old in "$@"; do
53
+ if [ -f "$_old" ]; then
54
+ printf 'pai: %s is at an old location — run `pai config migrate` to move it to %s\n' "$_old" "$_new" >&2
55
+ printf '%s' "$_old"
56
+ return
57
+ fi
58
+ done
59
+ printf '%s' "$_new"
60
+ }
61
+
62
+ # Extract data from JSON input.
63
+ #
64
+ # `.workspace.current_dir` is the documented field and a full session always
65
+ # sends it — but `jq -r` prints the JSON null as the four characters "null"
66
+ # when it is absent, and `basename null` is "null", so any payload without a
67
+ # workspace block rendered a confident "📁 null". A folder name is never
68
+ # unknowable here: the payload also carries the flat `.cwd`, and failing both
69
+ # we are running in the directory ourselves. Fall through all three.
70
+ current_dir=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // empty' 2>/dev/null)
71
+ [ -n "$current_dir" ] && [ "$current_dir" != "null" ] || current_dir="${PWD:-}"
72
+ model_name=$(echo "$input" | jq -r '.model.display_name // empty' 2>/dev/null)
73
+ [ -n "$model_name" ] || model_name="?"
74
+ model_id=$(echo "$input" | jq -r '.model.id // .model.display_name // empty' 2>/dev/null)
75
+
76
+ # Claude Code version. The payload carries `.version` as a plain string, but a
77
+ # caller that sends a partial payload (a test harness, an older client) left it
78
+ # rendering the literal word "unknown" next to a version that `claude --version`
79
+ # would have answered immediately. Ask the binary, and cache the answer for a
80
+ # day: it is a ~200MB executable and the status line runs on every keystroke.
81
+ cc_version=$(echo "$input" | jq -r '.version // empty' 2>/dev/null)
82
+ if [ -z "$cc_version" ]; then
83
+ _ccv_cache="${pai_cache_dir}/cc-version"
84
+ _ccv_age=$(( $(date +%s) - $(stat -f %m "$_ccv_cache" 2>/dev/null || echo 0) ))
85
+ if [ -s "$_ccv_cache" ] && [ "$_ccv_age" -lt 86400 ] 2>/dev/null; then
86
+ read -r cc_version < "$_ccv_cache"
87
+ elif command -v claude >/dev/null 2>&1; then
88
+ # "2.1.267 (Claude Code)" -> "2.1.267"
89
+ cc_version=$(command claude --version 2>/dev/null | awk 'NR==1{print $1}')
90
+ if [ -n "$cc_version" ]; then
91
+ mkdir -p "$pai_cache_dir" 2>/dev/null
92
+ printf '%s\n' "$cc_version" > "$_ccv_cache" 2>/dev/null || true
93
+ fi
94
+ fi
95
+ fi
96
+ [ -n "$cc_version" ] || cc_version="?"
40
97
 
41
98
  # Provider detection: which worker provider is this session running on? The
42
99
  # answer comes from the PAI config, not from model-name prefixes — a session
@@ -44,7 +101,19 @@ cc_version=$(echo "$input" | jq -r '.version // "unknown"')
44
101
  # Any [...] suffix (e.g. the [1m] context marker) is stripped on both sides,
45
102
  # so a session on "glm-5.3[1m]" matches a configured "glm-5.3" and vice versa.
46
103
  # No config, no jq, unreadable JSON or no match => anthropic, silently.
47
- pai_config="${PAI_CONFIG:-${XDG_CONFIG_HOME:-$HOME/.config}/pai/config.json}"
104
+ # PAI_HOME (~/.claude/pai by default — see src/config/pai-home.ts) first;
105
+ # else fall back through the pre-2026-09-19 locations in the same order
106
+ # paiConfigFilePath() in src/daemon/config.ts does, since a shell script
107
+ # can't import that resolver.
108
+ pai_home_dir="${PAI_HOME:-$HOME/.claude/pai}"
109
+ pai_config="${PAI_CONFIG:-$pai_home_dir/config.json}"
110
+ if [ ! -f "$pai_config" ]; then
111
+ if [ -f "$HOME/.claude/pai.json" ]; then
112
+ pai_config="$HOME/.claude/pai.json"
113
+ elif [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/pai/config.json" ]; then
114
+ pai_config="${XDG_CONFIG_HOME:-$HOME/.config}/pai/config.json"
115
+ fi
116
+ fi
48
117
  model_base="${model_id%%\[*}"
49
118
  session_provider=anthropic
50
119
  if [ -n "$model_base" ] && [ -f "$pai_config" ] && command -v jq >/dev/null 2>&1; then
@@ -58,7 +127,8 @@ if [ -n "$model_base" ] && [ -f "$pai_config" ] && command -v jq >/dev/null 2>&1
58
127
  fi
59
128
 
60
129
  # Get directory name
61
- dir_name=$(basename "$current_dir")
130
+ dir_name=$(basename "$current_dir" 2>/dev/null)
131
+ [ -n "$dir_name" ] || dir_name="?"
62
132
 
63
133
  # Read Whazaa session name from iTerm2 user variable
64
134
  pai_session_name=""
@@ -372,80 +442,175 @@ fi
372
442
  printf "${LINE2_PRIMARY}${EMOJI_PLUG} MCPs${RESET}${LINE2_PRIMARY}${SEPARATOR_COLOR}: ${RESET}${mcp_line1}${RESET}\n"
373
443
 
374
444
 
375
- # Usage suffix: provider-aware. The built-in anthropic provider reads the
376
- # Claude Code OAuth usage endpoint (5-hour + 1d pace + 7-day); every other
377
- # provider is rendered from its own `usage` block in the PAI config (see
378
- # docs/provider-abstraction.md) — endpoint, auth header and per-window jq
379
- # expressions are config, not code. Both paths cache for usage_cache_ttl.
380
- usage_cache="/tmp/claude/statusline-usage-cache.json"
381
- usage_cache_ttl=60 # seconds
445
+ # Usage suffix: provider-aware. Every non-anthropic provider is rendered from
446
+ # its own `usage` block in the PAI config (see docs/provider-abstraction.md) —
447
+ # endpoint, auth header and per-window jq expressions are config, not code.
448
+ #
449
+ # The anthropic numbers come from Claude Code itself. Its status line payload
450
+ # carries a `rate_limits` object built from the same internal reading that
451
+ # /usage renders:
452
+ #
453
+ # "rate_limits": {
454
+ # "five_hour": { "used_percentage": 9, "resets_at": 1789810200 },
455
+ # "seven_day": { "used_percentage": 2, "resets_at": 1790402400 }
456
+ # }
457
+ #
458
+ # `used_percentage` is already 0-100 (used, not remaining) and `resets_at` is
459
+ # Unix epoch SECONDS. It is live, free and needs no credential.
460
+ #
461
+ # The OAuth usage endpoint below stays only as a fallback for payloads that
462
+ # carry no rate_limits (it reports `utilization` on the same 0-100 scale but
463
+ # `resets_at` as an ISO-8601 UTC string, so the two are parsed differently).
464
+ # That endpoint needs an OAuth token out of the Keychain, and when the token
465
+ # goes away the fetch fails silently and leaves whatever it cached last. A
466
+ # cache with no expiry is not a reading, it is a photograph: this one sat at
467
+ # "7d: 97%" for a day and a half after the window had already reset to 2%, and
468
+ # that number is not decoration — it is written to advisor-mode.json and
469
+ # injected into every session as an instruction about how much work to do. So
470
+ # the fallback is refused once it is older than usage_cache_max_age, and any
471
+ # window whose resets_at has already passed is dropped. What neither source can
472
+ # answer renders "?", which is the honest reading.
473
+ usage_cache="${pai_cache_dir}/statusline-usage-cache.json"
474
+ usage_cache_ttl=60 # refetch the fallback after this many seconds
475
+ usage_cache_max_age=300 # refuse to render from the fallback past this age
382
476
  usage_suffix=""
383
477
 
384
- # Color based on utilization: green < 50%, yellow 50-75%, red > 75%
478
+ # Color based on utilization: green < 50%, orange 50-75%, red > 75%.
479
+ # An unknown percentage is neither good nor bad news — render it neutral.
385
480
  _usage_color() {
386
481
  local pct=$1
387
- if [ "$pct" -gt 75 ] 2>/dev/null; then echo "$BRIGHT_RED"
388
- elif [ "$pct" -gt 50 ] 2>/dev/null; then echo "$BRIGHT_YELLOW"
482
+ if [ -z "$pct" ]; then echo "$LINE3_ACCENT"
483
+ elif [ "$pct" -gt 75 ] 2>/dev/null; then echo "$BRIGHT_RED"
484
+ elif [ "$pct" -gt 50 ] 2>/dev/null; then echo "$BRIGHT_ORANGE"
389
485
  else echo "$BRIGHT_GREEN"; fi
390
486
  }
391
487
 
392
488
  _fetch_usage() {
393
489
  # Try to get OAuth token from macOS Keychain
394
490
  local token=""
395
- token=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
396
- [ -z "$token" ] && return
491
+ # More than one keychain item can carry this service name: the real one is
492
+ # stored under the login user account, and a stub with empty tokens has
493
+ # been seen under acct "unknown". Without -a the first match wins, so ask
494
+ # for the user item first and only then fall back to an unqualified read.
495
+ local raw
496
+ raw=$(security find-generic-password -s "Claude Code-credentials" -a "$(id -un)" -w 2>/dev/null)
497
+ token=$(printf '%s' "$raw" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
498
+ if [ -z "$token" ]; then
499
+ raw=$(security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null)
500
+ token=$(printf '%s' "$raw" | jq -r '.claudeAiOauth.accessToken // empty' 2>/dev/null)
501
+ fi
397
502
 
398
- mkdir -p /tmp/claude
399
- local response
400
- response=$(curl -sf --max-time 3 \
503
+ mkdir -p "$pai_cache_dir"
504
+ if [ -z "$token" ]; then
505
+ echo "no usable OAuth token in keychain item Claude Code-credentials" > "$usage_cache.error"
506
+ return
507
+ fi
508
+ # Per-process temp file: several renders may fetch at once.
509
+ local tmp="$usage_cache.tmp.$$" http
510
+ http=$(curl -s --max-time 3 -o "$tmp" -w '%{http_code}' \
401
511
  -H "Authorization: Bearer $token" \
402
512
  -H "anthropic-beta: oauth-2025-04-20" \
403
513
  "https://api.anthropic.com/api/oauth/usage" 2>/dev/null)
404
- [ -n "$response" ] && echo "$response" > "$usage_cache"
514
+ if [ "$http" = "200" ] && [ -s "$tmp" ]; then
515
+ mv -f "$tmp" "$usage_cache"
516
+ rm -f "$usage_cache.error"
517
+ else
518
+ echo "usage fetch failed: http ${http:-timeout}" > "$usage_cache.error"
519
+ rm -f "$tmp"
520
+ fi
405
521
  }
406
522
 
407
- # Use cache if fresh, otherwise fetch in background (Anthropic plan only)
523
+ # Resolve the anthropic windows — skipped on non-anthropic sessions so
524
+ # Anthropic-plan numbers (usage line AND the advisor-mode budget file)
525
+ # never leak into a glm or kimi statusline. A worker running on another
526
+ # provider spends another budget; counting it here would be the same lie in
527
+ # the other direction.
528
+ five_hour_int=""
529
+ seven_day_int=""
530
+ five_reset_epoch=0
531
+ seven_reset_epoch=0
532
+ usage_source=""
533
+
534
+ # Source 1 (authoritative): rate_limits on stdin, straight from Claude Code.
408
535
  if [ "$session_provider" = "anthropic" ]; then
536
+ _rl=$(echo "$input" | jq -r '
537
+ [ (.rate_limits.five_hour.used_percentage // ""),
538
+ (.rate_limits.five_hour.resets_at // ""),
539
+ (.rate_limits.seven_day.used_percentage // ""),
540
+ (.rate_limits.seven_day.resets_at // "") ] | @tsv' 2>/dev/null)
541
+ _p5=$(printf '%s' "$_rl" | cut -f1)
542
+ _r5=$(printf '%s' "$_rl" | cut -f2)
543
+ _p7=$(printf '%s' "$_rl" | cut -f3)
544
+ _r7=$(printf '%s' "$_rl" | cut -f4)
545
+ if [ -n "$_p5" ] || [ -n "$_p7" ]; then
546
+ usage_source="payload"
547
+ [ -n "$_p5" ] && five_hour_int=$(printf "%.0f" "$_p5" 2>/dev/null || echo "")
548
+ [ -n "$_p7" ] && seven_day_int=$(printf "%.0f" "$_p7" 2>/dev/null || echo "")
549
+ # resets_at is epoch seconds here, not an ISO string.
550
+ case "$_r5" in ''|*[!0-9.]*) ;; *) five_reset_epoch="${_r5%%.*}" ;; esac
551
+ case "$_r7" in ''|*[!0-9.]*) ;; *) seven_reset_epoch="${_r7%%.*}" ;; esac
552
+ fi
553
+ fi
554
+
555
+ # Refresh the fallback in the background, but only when the payload did not
556
+ # already answer — a keychain lookup plus an HTTPS round trip on every render
557
+ # buys nothing when the live numbers arrived on stdin.
558
+ if [ "$session_provider" = "anthropic" ] && [ -z "$usage_source" ]; then
409
559
  if [ -f "$usage_cache" ]; then
410
560
  cache_age=$(( $(date +%s) - $(stat -f %m "$usage_cache" 2>/dev/null || echo 0) ))
411
- if [ "$cache_age" -gt "$usage_cache_ttl" ]; then
412
- _fetch_usage &
413
- fi
561
+ [ "$cache_age" -gt "$usage_cache_ttl" ] && _fetch_usage &
414
562
  else
415
563
  _fetch_usage &
416
564
  fi
417
565
  fi
418
566
 
419
- # Read cached usage data — skipped on non-anthropic sessions so
420
- # Anthropic-plan numbers (usage line AND the advisor-mode budget file)
421
- # never leak into a glm or kimi statusline
422
- if [ "$session_provider" = "anthropic" ] && [ -f "$usage_cache" ]; then
423
- five_hour=$(jq -r '.five_hour.utilization // 0' "$usage_cache" 2>/dev/null)
424
- seven_day=$(jq -r '.seven_day.utilization // 0' "$usage_cache" 2>/dev/null)
425
- five_reset=$(jq -r '.five_hour.resets_at // empty' "$usage_cache" 2>/dev/null)
426
- seven_reset=$(jq -r '.seven_day.resets_at // empty' "$usage_cache" 2>/dev/null)
427
-
428
- # Round to integers
429
- five_hour_int=$(printf "%.0f" "$five_hour" 2>/dev/null || echo 0)
430
- seven_day_int=$(printf "%.0f" "$seven_day" 2>/dev/null || echo 0)
431
-
432
- # Format reset times as HH:MM (local time)
433
- five_reset_fmt=""
434
- seven_reset_fmt=""
435
- seven_reset_epoch=0
436
- if [ -n "$five_reset" ]; then
437
- # The API states these in UTC ("...T06:00:00+00:00"). Parsing without -u
438
- # reads 06:00 UTC as 06:00 local and prints a reset time that is out by
439
- # the offset — two hours here, which is long enough to plan around and
440
- # be wrong. Parse as UTC to get the epoch, then let date render it in
441
- # local time, which is the only form worth showing a person.
442
- five_reset_epoch=$(date -j -u -f "%Y-%m-%dT%H:%M:%S" "$(echo "$five_reset" | cut -c1-19)" "+%s" 2>/dev/null || date -d "$five_reset" "+%s" 2>/dev/null || echo 0)
443
- five_reset_fmt=$([ "$five_reset_epoch" -gt 0 ] 2>/dev/null && date -r "$five_reset_epoch" "+%H:%M" 2>/dev/null || echo "")
567
+ # Source 2 (fallback): the OAuth endpoint cache, and only while it is fresh.
568
+ if [ "$session_provider" = "anthropic" ] && [ -z "$usage_source" ] && [ -f "$usage_cache" ]; then
569
+ _usage_cache_age=$(( $(date +%s) - $(stat -f %m "$usage_cache" 2>/dev/null || echo 0) ))
570
+ if [ "$_usage_cache_age" -le "$usage_cache_max_age" ] 2>/dev/null; then
571
+ usage_source="oauth"
572
+ five_hour=$(jq -r '.five_hour.utilization // empty' "$usage_cache" 2>/dev/null)
573
+ seven_day=$(jq -r '.seven_day.utilization // empty' "$usage_cache" 2>/dev/null)
574
+ five_reset=$(jq -r '.five_hour.resets_at // empty' "$usage_cache" 2>/dev/null)
575
+ seven_reset=$(jq -r '.seven_day.resets_at // empty' "$usage_cache" 2>/dev/null)
576
+ [ -n "$five_hour" ] && five_hour_int=$(printf "%.0f" "$five_hour" 2>/dev/null || echo "")
577
+ [ -n "$seven_day" ] && seven_day_int=$(printf "%.0f" "$seven_day" 2>/dev/null || echo "")
578
+ # The endpoint states these in UTC ("...T06:00:00+00:00"). Parsing
579
+ # without -u reads 06:00 UTC as 06:00 local and prints a reset time out
580
+ # by the offset — two hours here, long enough to plan around and be
581
+ # wrong. Parse as UTC to get the epoch; render local further down.
582
+ if [ -n "$five_reset" ]; then
583
+ five_reset_epoch=$(date -j -u -f "%Y-%m-%dT%H:%M:%S" "$(echo "$five_reset" | cut -c1-19)" "+%s" 2>/dev/null || date -d "$five_reset" "+%s" 2>/dev/null || echo 0)
584
+ fi
585
+ if [ -n "$seven_reset" ]; then
586
+ seven_reset_epoch=$(date -j -u -f "%Y-%m-%dT%H:%M:%S" "$(echo "$seven_reset" | cut -c1-19)" "+%s" 2>/dev/null || date -d "$seven_reset" "+%s" 2>/dev/null || echo 0)
587
+ fi
444
588
  fi
445
- if [ -n "$seven_reset" ]; then
446
- seven_reset_epoch=$(date -j -u -f "%Y-%m-%dT%H:%M:%S" "$(echo "$seven_reset" | cut -c1-19)" "+%s" 2>/dev/null || date -d "$seven_reset" "+%s" 2>/dev/null || echo 0)
447
- seven_reset_fmt=$([ "$seven_reset_epoch" -gt 0 ] 2>/dev/null && date -r "$seven_reset_epoch" "+%a %H:%M" 2>/dev/null || echo "")
589
+ fi
590
+
591
+ # A window whose reset has already passed describes a window that no longer
592
+ # exists. Its percentage belongs to a period that has been and gone, so it is
593
+ # dropped rather than carried forward — a spent budget rendered as the current
594
+ # one is exactly the failure this whole block exists to avoid.
595
+ if [ "$session_provider" = "anthropic" ]; then
596
+ _now_epoch=$(date +%s)
597
+ if [ "$five_reset_epoch" -gt 0 ] 2>/dev/null && [ "$five_reset_epoch" -le "$_now_epoch" ] 2>/dev/null; then
598
+ five_hour_int=""; five_reset_epoch=0
599
+ fi
600
+ if [ "$seven_reset_epoch" -gt 0 ] 2>/dev/null && [ "$seven_reset_epoch" -le "$_now_epoch" ] 2>/dev/null; then
601
+ seven_day_int=""; seven_reset_epoch=0
448
602
  fi
603
+ fi
604
+
605
+ # Rendered even when both windows are unknown: "5h: ? │ 7d: ?" says the
606
+ # instrument is blind, where dropping the section altogether would read as
607
+ # "this session is not on the anthropic plan".
608
+ if [ "$session_provider" = "anthropic" ]; then
609
+ # Format reset times in local time
610
+ five_reset_fmt=""
611
+ seven_reset_fmt=""
612
+ [ "$five_reset_epoch" -gt 0 ] 2>/dev/null && five_reset_fmt=$(date -r "$five_reset_epoch" "+%H:%M" 2>/dev/null || echo "")
613
+ [ "$seven_reset_epoch" -gt 0 ] 2>/dev/null && seven_reset_fmt=$(date -r "$seven_reset_epoch" "+%a %H:%M" 2>/dev/null || echo "")
449
614
 
450
615
  five_color=$(_usage_color "$five_hour_int")
451
616
  seven_color=$(_usage_color "$seven_day_int")
@@ -453,83 +618,75 @@ if [ "$session_provider" = "anthropic" ] && [ -f "$usage_cache" ]; then
453
618
  # Budget pace indicator for 7-day window
454
619
  # Compare actual usage vs linear expected usage based on elapsed time
455
620
  pace_dot=""
456
- if [ "$seven_reset_epoch" -gt 0 ] 2>/dev/null; then
621
+ if [ "$seven_reset_epoch" -gt 0 ] 2>/dev/null && [ -n "$seven_day_int" ]; then
457
622
  now_epoch=$(date +%s)
458
623
  window_secs=$((7 * 86400))
459
624
  remaining_secs=$((seven_reset_epoch - now_epoch))
460
625
  [ "$remaining_secs" -lt 0 ] && remaining_secs=0
461
626
  elapsed_secs=$((window_secs - remaining_secs))
462
- # Expected usage if spending linearly: elapsed/total * 100
463
- expected_pct=$(( elapsed_secs * 100 / window_secs ))
464
627
  # Daily pace: actual spend/day vs dynamic budget
465
628
  # Budget = remaining capacity / remaining days (not static 100/7)
466
629
  elapsed_days_x10=$((elapsed_secs * 10 / 86400))
467
- [ "$elapsed_days_x10" -lt 1 ] && elapsed_days_x10=1
468
- spend_per_day=$((seven_day_int * 10 / elapsed_days_x10))
469
- remaining_days_x10=$((remaining_secs * 10 / 86400))
470
- [ "$remaining_days_x10" -lt 1 ] && remaining_days_x10=1
471
- remaining_budget=$((100 - seven_day_int))
472
- budget_per_day=$((remaining_budget * 10 / remaining_days_x10))
473
- # Color: green = under budget, orange = near budget, red = over budget
474
- overspend=$((spend_per_day - budget_per_day))
475
- if [ "$overspend" -le -3 ] 2>/dev/null; then
476
- pace_color="$BRIGHT_GREEN" # well under budget
477
- elif [ "$overspend" -le 2 ] 2>/dev/null; then
478
- pace_color="$BRIGHT_ORANGE" # near budget
479
- else
480
- pace_color="$BRIGHT_RED" # over budget
630
+ # A per-day rate divided by a fraction of a day is not a rate, it is an
631
+ # extrapolation from noise: an hour into a fresh window the old clamp to
632
+ # 0.1 days turned 2% spent into "20% per day" and painted the pace red.
633
+ # Below half a day there is nothing to pace against yet — say nothing.
634
+ if [ "$elapsed_days_x10" -ge 5 ]; then
635
+ spend_per_day=$((seven_day_int * 10 / elapsed_days_x10))
636
+ remaining_days_x10=$((remaining_secs * 10 / 86400))
637
+ [ "$remaining_days_x10" -lt 1 ] && remaining_days_x10=1
638
+ remaining_budget=$((100 - seven_day_int))
639
+ budget_per_day=$((remaining_budget * 10 / remaining_days_x10))
640
+ # Color: green = under budget, orange = near budget, red = over budget
641
+ overspend=$((spend_per_day - budget_per_day))
642
+ if [ "$overspend" -le -3 ] 2>/dev/null; then
643
+ pace_color="$BRIGHT_GREEN" # well under budget
644
+ elif [ "$overspend" -le 2 ] 2>/dev/null; then
645
+ pace_color="$BRIGHT_ORANGE" # near budget
646
+ else
647
+ pace_color="$BRIGHT_RED" # over budget
648
+ fi
649
+ pace_dot="${pace_color}${spend_per_day}%% / ${budget_per_day}%%${RESET}"
481
650
  fi
482
- pace_dot="${pace_color}${spend_per_day}%% / ${budget_per_day}%%${RESET}"
483
651
  fi
484
652
 
485
- # Write weekly budget to advisor-mode.json for the whisper hook
486
- # Preserve existing mode if manually set — only update weeklyBudgetPercent
487
- _advisor_file="${HOME}/.claude/advisor-mode.json"
488
- if [ -n "$seven_day_int" ] 2>/dev/null; then
489
- _existing_mode="auto"
490
- _existing_force=""
491
- if [ -f "$_advisor_file" ]; then
492
- _existing_mode=$(jq -r '.mode // "auto"' "$_advisor_file" 2>/dev/null)
493
- _existing_force=$(jq -r '.forceModel // empty' "$_advisor_file" 2>/dev/null)
494
- fi
653
+ # Write weekly budget to advisor-mode.json for the whisper hook.
654
+ # Preserve existing mode if manually set — only update weeklyBudgetPercent.
655
+ #
656
+ # `asOf` is the epoch second the percentage was read. The consumer refuses a
657
+ # percentage without one, or one that has gone stale: this file is the input
658
+ # to an instruction injected on every prompt ("weekly budget at N% — do as
659
+ # little as possible"), so a number that stopped being refreshed must stop
660
+ # being obeyed rather than quietly harden into a permanent constraint.
661
+ # Nothing is written at all while the percentage is unknown.
662
+ _advisor_file=$(_pai_resolve_file "${pai_home_dir}/advisor-mode.json" "${HOME}/.claude/advisor-mode.json")
663
+ mkdir -p "$(dirname "$_advisor_file")" 2>/dev/null
664
+ _existing_mode="auto"
665
+ _existing_force=""
666
+ if [ -f "$_advisor_file" ]; then
667
+ _existing_mode=$(jq -r '.mode // "auto"' "$_advisor_file" 2>/dev/null)
668
+ _existing_force=$(jq -r '.forceModel // empty' "$_advisor_file" 2>/dev/null)
669
+ fi
670
+ case "$_existing_mode" in
671
+ normal|conservative|strict|critical|auto) ;;
672
+ *) _existing_mode="auto" ;;
673
+ esac
674
+ if [ -n "$seven_day_int" ]; then
495
675
  if [ -n "$_existing_force" ]; then
496
- printf '{"weeklyBudgetPercent":%d,"mode":"%s","forceModel":"%s"}\n' "$seven_day_int" "$_existing_mode" "$_existing_force" > "$_advisor_file" 2>/dev/null
676
+ printf '{"weeklyBudgetPercent":%d,"asOf":%d,"mode":"%s","forceModel":"%s"}\n' "$seven_day_int" "$(date +%s)" "$_existing_mode" "$_existing_force" > "$_advisor_file" 2>/dev/null
497
677
  else
498
- printf '{"weeklyBudgetPercent":%d,"mode":"%s"}\n' "$seven_day_int" "$_existing_mode" > "$_advisor_file" 2>/dev/null
499
- fi
500
- fi
501
-
502
- # Compute advisor mode label (mirrors thresholds in whisper-rules.ts)
503
- # If mode is manually set (not "auto"), show that instead of auto-calculated
504
- advisor_label=""
505
- advisor_label_color=""
506
- _display_mode="$_existing_mode"
507
- if [ "$_display_mode" = "auto" ]; then
508
- if [ "$seven_day_int" -ge 92 ] 2>/dev/null; then
509
- _display_mode="critical"
510
- elif [ "$seven_day_int" -ge 80 ] 2>/dev/null; then
511
- _display_mode="strict"
512
- elif [ "$seven_day_int" -ge 60 ] 2>/dev/null; then
513
- _display_mode="conservative"
678
+ printf '{"weeklyBudgetPercent":%d,"asOf":%d,"mode":"%s"}\n' "$seven_day_int" "$(date +%s)" "$_existing_mode" > "$_advisor_file" 2>/dev/null
514
679
  fi
515
680
  fi
516
- case "$_display_mode" in
517
- "critical") advisor_label="critical"; advisor_label_color="$BRIGHT_RED" ;;
518
- "strict") advisor_label="strict"; advisor_label_color="$BRIGHT_ORANGE" ;;
519
- "conservative") advisor_label="conserve"; advisor_label_color="$BRIGHT_YELLOW" ;;
520
- "normal") advisor_label="normal"; advisor_label_color="$BRIGHT_GREEN" ;;
521
- esac
522
- # Mark forced modes with a pin symbol so user knows it's not auto
523
- if [ "$_existing_mode" != "auto" ] && [ -n "$advisor_label" ]; then
524
- advisor_label="📌${advisor_label}"
525
- fi
526
681
 
527
- # Build usage suffix: 5h: 8% → 00:59 │ 1d: ● 29% / 36% │ 7d: ⚡strict 91% → Fr. 08:00
528
- five_label="5h: ${five_hour_int}%%"
682
+ # Build usage suffix: 5h: 8% → 00:59 │ 1d: ● 29% / 36% │ 7d: 91% → Fr. 08:00
683
+ # A window we could not read renders "?" — a gauge that admits it cannot
684
+ # tell beats one reading 0% while the tank drains. The advisor mode word is
685
+ # deliberately not rendered here; the whisper hook is where it acts.
686
+ five_label="5h: ${five_hour_int:-?}%%"
529
687
  [ -n "$five_reset_fmt" ] && five_label="${five_label} → ${five_reset_fmt}"
530
688
  seven_label="7d: "
531
- [ -n "$advisor_label" ] && seven_label="${seven_label}${advisor_label_color}${advisor_label}${RESET} "
532
- seven_label="${seven_label}${seven_day_int}%%"
689
+ seven_label="${seven_label}${seven_day_int:-?}%%"
533
690
  [ -n "$seven_reset_fmt" ] && seven_label="${seven_label} → ${seven_reset_fmt}"
534
691
 
535
692
  usage_suffix=" ${SEPARATOR_COLOR}│${RESET} ${five_color}${five_label}${RESET}"
@@ -562,7 +719,7 @@ _fetch_provider_usage() {
562
719
  else
563
720
  header="${auth}: ${key}"
564
721
  fi
565
- mkdir -p /tmp/claude
722
+ mkdir -p "$pai_cache_dir"
566
723
  local response
567
724
  response=$(curl -sf --max-time 3 \
568
725
  -H "$header" \
@@ -593,7 +750,7 @@ if [ "$session_provider" != "anthropic" ]; then
593
750
  usage_auth=$(printf '%s' "$provider_usage" | jq -r '.authHeader // "Authorization: Bearer"')
594
751
  usage_ttl=$(printf '%s' "$provider_usage" | jq -r '.ttlSeconds // empty')
595
752
  [ "$usage_ttl" -gt 0 ] 2>/dev/null || usage_ttl="$usage_cache_ttl"
596
- provider_cache="/tmp/claude/statusline-usage-${session_provider}.json"
753
+ provider_cache="${pai_cache_dir}/statusline-usage-${session_provider}.json"
597
754
  usage_probe=$(printf '%s' "$provider_usage" | jq -r '.windows[0].percent // empty')
598
755
 
599
756
  # Use cache if fresh, otherwise refresh in the background
@@ -704,6 +861,10 @@ fi
704
861
  # step, plus today's finished count). Empty when this session has none.
705
862
  # Prefers the standalone built script (plain node, no CLI startup); falls back
706
863
  # to the pai CLI.
864
+ # PAI_QUIET_NOTICES=1: this call's stderr already goes to /dev/null below, but
865
+ # set it anyway so a caller of worker-status-line.mjs outside this script
866
+ # stays quiet too.
867
+ export PAI_QUIET_NOTICES=1
707
868
  worker_status_cmd=""
708
869
  if [ -x "${claude_dir}/worker-status-line.mjs" ]; then
709
870
  worker_status_cmd="${claude_dir}/worker-status-line.mjs"
@@ -1,6 +1,6 @@
1
1
  # PAI Agent Preferences (Personal Configuration)
2
2
 
3
- Copy this file to `~/.config/pai/agent-prefs.md` and customize for your workflow. This file is **NOT** committed to any repository and contains your personal preferences.
3
+ Copy this file to `~/.claude/pai/agent-prefs.md` and customize for your workflow. This file is **NOT** committed to any repository and contains your personal preferences.
4
4
 
5
5
  ---
6
6
 
@@ -115,7 +115,7 @@ researcher | [researcher-voice-id] | Research summaries, findings
115
115
  - No API key needed — runs entirely locally
116
116
  - Better for privacy-sensitive workflows
117
117
 
118
- **Configuration**: Store voice preferences in `~/.config/pai/voices.json` (see voices.example.json).
118
+ **Configuration**: Store voice preferences in `~/.claude/pai/voices.json` (see voices.example.json). Currently unused — no code reads this file's contents yet.
119
119
 
120
120
  ---
121
121
 
@@ -306,14 +306,14 @@ Always Use: ~/dev, ~/Projects, ~/.claude
306
306
 
307
307
  1. **Copy to config directory**:
308
308
  ```bash
309
- cp ~/dev/ai/PAI/templates/agent-prefs.example.md ~/.config/pai/agent-prefs.md
309
+ cp ~/dev/ai/PAI/templates/agent-prefs.example.md ~/.claude/pai/agent-prefs.md
310
310
  ```
311
311
 
312
- 2. **Customize for your workflow**: Edit `~/.config/pai/agent-prefs.md` with your settings.
312
+ 2. **Customize for your workflow**: Edit `~/.claude/pai/agent-prefs.md` with your settings.
313
313
 
314
314
  3. **Make it executable** (optional):
315
315
  ```bash
316
- chmod 600 ~/.config/pai/agent-prefs.md # Restrict permissions if sensitive
316
+ chmod 600 ~/.claude/pai/agent-prefs.md # Restrict permissions if sensitive
317
317
  ```
318
318
 
319
319
  4. **Load in your scripts**: Your PAI daemon/CLI reads this automatically on startup.
@@ -326,13 +326,13 @@ This file contains personal preferences and may reference sensitive information:
326
326
 
327
327
  ```bash
328
328
  # Recommended permissions (user read/write only)
329
- chmod 600 ~/.config/pai/agent-prefs.md
329
+ chmod 600 ~/.claude/pai/agent-prefs.md
330
330
  ```
331
331
 
332
332
  Keep it **out of version control** — add to `.gitignore`:
333
333
  ```
334
- ~/.config/pai/
335
- .config/pai/
334
+ ~/.claude/pai/
335
+ .claude/pai/
336
336
  ```
337
337
 
338
338
  ---
@@ -1,6 +1,6 @@
1
1
  # CLAUDE.md - PAI Global Configuration
2
2
  <!-- Generated by PAI Setup — Do not edit directly. Run `pai setup` to regenerate. -->
3
- <!-- Personal preferences and project mappings are read from ~/.config/pai/agent-prefs.md -->
3
+ <!-- Personal preferences and project mappings are read from ~/.claude/pai/agent-prefs.md -->
4
4
 
5
5
  ---
6
6
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "comment": "PAI Voice Configuration Template. Copy to ~/.config/pai/voices.json and customize.",
2
+ "comment": "PAI Voice Configuration Template. Copy to ~/.claude/pai/voices.json and customize. Currently unused — no code reads this file's contents yet.",
3
3
  "comment_privacy": "This file is NOT committed to any repository. Keep it out of version control.",
4
4
 
5
5
  "voiceSystem": {
@@ -231,10 +231,10 @@
231
231
  },
232
232
 
233
233
  "notes": {
234
- "setup": "Copy this file to ~/.config/pai/voices.json and customize for your preferences.",
234
+ "setup": "Copy this file to ~/.claude/pai/voices.json and customize for your preferences.",
235
235
  "kokoro_first_run": "First use downloads ~160MB model (~30 seconds). Runs entirely offline afterward.",
236
236
  "elevenlabs_cost": "ElevenLabs has usage limits and costs. Check your account for pricing.",
237
- "privacy": "This file is NOT committed to version control. Add ~/.config/pai/ to .gitignore.",
237
+ "privacy": "This file is NOT committed to version control. Add ~/.claude/pai/ to .gitignore.",
238
238
  "reloading": "Changes take effect immediately. No daemon restart required.",
239
239
  "fallback_behavior": "If primary system fails or voice not found, falls back to alternate system.",
240
240
  "environment_variables": "Can also set ELEVENLABS_API_KEY and KOKORO_CACHE_DIR via environment.",
@@ -1,3 +0,0 @@
1
- import { a as expandHome, i as ensureConfigDir, n as CONFIG_FILE, o as loadConfig, r as DEFAULTS, t as CONFIG_DIR } from "./config-B64vFg14.mjs";
2
-
3
- export { ensureConfigDir, loadConfig };