@cmodjs/core 0.2.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/docs/commands.md +58 -10
  2. package/docs/dependencies.md +12 -12
  3. package/docs/hooks.md +41 -28
  4. package/docs/index.md +29 -25
  5. package/docs/install-steps.md +9 -7
  6. package/docs/jobs.md +59 -111
  7. package/docs/mod.md +200 -174
  8. package/docs/options.md +2 -2
  9. package/docs/permissions.md +27 -16
  10. package/docs/session.md +248 -0
  11. package/docs/state.md +11 -7
  12. package/docs/testing.md +66 -23
  13. package/docs/ui.md +108 -76
  14. package/jobs/check.d.ts.map +1 -1
  15. package/jobs/check.js +5 -2
  16. package/jobs/context.d.ts +7 -8
  17. package/jobs/context.d.ts.map +1 -1
  18. package/jobs/context.js +23 -73
  19. package/jobs/permissions/decide-permission.d.ts +5 -3
  20. package/jobs/permissions/decide-permission.d.ts.map +1 -1
  21. package/jobs/permissions/decide-permission.js +6 -4
  22. package/jobs/permissions/find-project-scope.d.ts +1 -1
  23. package/jobs/permissions/find-project-scope.d.ts.map +1 -1
  24. package/jobs/permissions/find-project-scope.js +8 -11
  25. package/jobs/permissions/match-target.d.ts +2 -2
  26. package/jobs/permissions/match-target.d.ts.map +1 -1
  27. package/jobs/permissions.d.ts +1 -1
  28. package/jobs/permissions.d.ts.map +1 -1
  29. package/jobs/permissions.js +6 -4
  30. package/jobs/program.d.ts.map +1 -1
  31. package/jobs/program.js +8 -3
  32. package/jobs/prompt.d.ts +4 -2
  33. package/jobs/prompt.d.ts.map +1 -1
  34. package/jobs/prompt.js +16 -15
  35. package/jobs/schedule.d.ts +2 -2
  36. package/jobs/schedule.d.ts.map +1 -1
  37. package/jobs/schedule.js +9 -9
  38. package/jobs/slash-command.d.ts +4 -4
  39. package/jobs/slash-command.d.ts.map +1 -1
  40. package/jobs/slash-command.js +14 -9
  41. package/jobs/status-line.d.ts +1 -11
  42. package/jobs/status-line.d.ts.map +1 -1
  43. package/jobs/status-line.js +8 -12
  44. package/jobs/tool.d.ts +2 -2
  45. package/jobs/tool.d.ts.map +1 -1
  46. package/jobs/tool.js +15 -9
  47. package/mod.d.ts +154 -75
  48. package/mod.d.ts.map +1 -1
  49. package/mod.js +94 -3
  50. package/options.d.ts +2 -2
  51. package/options.js +19 -19
  52. package/package.json +3 -3
  53. package/records.d.ts +10 -8
  54. package/records.d.ts.map +1 -1
  55. package/records.js +28 -17
  56. package/register.d.ts.map +1 -1
  57. package/register.js +18 -6
  58. package/runtime/announce.d.ts +18 -0
  59. package/runtime/announce.d.ts.map +1 -0
  60. package/runtime/announce.js +66 -0
  61. package/runtime/bound.d.ts +4 -0
  62. package/runtime/bound.d.ts.map +1 -0
  63. package/runtime/bound.js +9 -0
  64. package/runtime/claude.d.ts +3 -3
  65. package/runtime/claude.d.ts.map +1 -1
  66. package/runtime/conversations.d.ts +16 -0
  67. package/runtime/conversations.d.ts.map +1 -0
  68. package/runtime/conversations.js +291 -0
  69. package/runtime/deadline.d.ts +0 -1
  70. package/runtime/deadline.d.ts.map +1 -1
  71. package/runtime/deadline.js +1 -1
  72. package/runtime/dependencies.d.ts +2 -0
  73. package/runtime/dependencies.d.ts.map +1 -1
  74. package/runtime/dependencies.js +9 -0
  75. package/runtime/engine.d.ts +17 -0
  76. package/runtime/engine.d.ts.map +1 -0
  77. package/runtime/engine.js +10 -0
  78. package/runtime/grants.d.ts +4 -0
  79. package/runtime/grants.d.ts.map +1 -1
  80. package/runtime/grants.js +63 -33
  81. package/runtime/hooks.d.ts +5 -4
  82. package/runtime/hooks.d.ts.map +1 -1
  83. package/runtime/hooks.js +10 -5
  84. package/runtime/installer.d.ts.map +1 -1
  85. package/runtime/installer.js +11 -9
  86. package/runtime/lifecycle.d.ts +3 -1
  87. package/runtime/lifecycle.d.ts.map +1 -1
  88. package/runtime/lifecycle.js +317 -186
  89. package/runtime/metadata.d.ts +3 -1
  90. package/runtime/metadata.d.ts.map +1 -1
  91. package/runtime/metadata.js +21 -11
  92. package/runtime/options.d.ts +3 -1
  93. package/runtime/options.d.ts.map +1 -1
  94. package/runtime/options.js +4 -4
  95. package/runtime/programs.d.ts +8 -0
  96. package/runtime/programs.d.ts.map +1 -1
  97. package/runtime/programs.js +39 -1
  98. package/runtime/router.d.ts +2 -1
  99. package/runtime/router.d.ts.map +1 -1
  100. package/runtime/router.js +11 -3
  101. package/runtime/sessions.d.ts +36 -0
  102. package/runtime/sessions.d.ts.map +1 -0
  103. package/runtime/sessions.js +235 -0
  104. package/runtime/state.d.ts +6 -2
  105. package/runtime/state.d.ts.map +1 -1
  106. package/runtime/state.js +46 -13
  107. package/runtime/toaster.d.ts +31 -0
  108. package/runtime/toaster.d.ts.map +1 -0
  109. package/runtime/toaster.js +176 -0
  110. package/runtime/ui.d.ts +6 -17
  111. package/runtime/ui.d.ts.map +1 -1
  112. package/runtime/ui.js +36 -112
  113. package/session/context/context.d.ts +10 -0
  114. package/session/context/context.d.ts.map +1 -0
  115. package/session/context/context.js +2 -0
  116. package/session/context/turn/message/document.d.ts +7 -0
  117. package/session/context/turn/message/document.d.ts.map +1 -0
  118. package/session/context/turn/message/document.js +1 -0
  119. package/session/context/turn/message/message.d.ts +54 -0
  120. package/session/context/turn/message/message.d.ts.map +1 -0
  121. package/session/context/turn/message/message.js +137 -0
  122. package/session/context/turn/message/note.d.ts +8 -0
  123. package/session/context/turn/message/note.d.ts.map +1 -0
  124. package/session/context/turn/message/note.js +1 -0
  125. package/session/context/turn/message/notice.d.ts +6 -0
  126. package/session/context/turn/message/notice.d.ts.map +1 -0
  127. package/session/context/turn/message/notice.js +1 -0
  128. package/session/context/turn/message/prompt.d.ts +8 -0
  129. package/session/context/turn/message/prompt.d.ts.map +1 -0
  130. package/session/context/turn/message/prompt.js +1 -0
  131. package/session/context/turn/message/reply.d.ts +6 -0
  132. package/session/context/turn/message/reply.d.ts.map +1 -0
  133. package/session/context/turn/message/reply.js +1 -0
  134. package/session/context/turn/message/sender.d.ts +14 -0
  135. package/session/context/turn/message/sender.d.ts.map +1 -0
  136. package/session/context/turn/message/sender.js +30 -0
  137. package/session/context/turn/message/skill.d.ts +7 -0
  138. package/session/context/turn/message/skill.d.ts.map +1 -0
  139. package/session/context/turn/message/skill.js +1 -0
  140. package/session/context/turn/message/tool-call.d.ts +9 -0
  141. package/session/context/turn/message/tool-call.d.ts.map +1 -0
  142. package/session/context/turn/message/tool-call.js +1 -0
  143. package/session/context/turn/message/tool-result.d.ts +8 -0
  144. package/session/context/turn/message/tool-result.d.ts.map +1 -0
  145. package/session/context/turn/message/tool-result.js +1 -0
  146. package/session/context/turn/turn.d.ts +12 -0
  147. package/session/context/turn/turn.d.ts.map +1 -0
  148. package/session/context/turn/turn.js +2 -0
  149. package/session/conversation.d.ts +64 -0
  150. package/session/conversation.d.ts.map +1 -0
  151. package/session/conversation.js +123 -0
  152. package/session/goal/goal.d.ts +10 -0
  153. package/session/goal/goal.d.ts.map +1 -0
  154. package/session/goal/goal.js +1 -0
  155. package/session/goal/task.d.ts +16 -0
  156. package/session/goal/task.d.ts.map +1 -0
  157. package/session/goal/task.js +3 -0
  158. package/session/session.d.ts +99 -0
  159. package/session/session.d.ts.map +1 -0
  160. package/session/session.js +1 -0
  161. package/src/jobs/check.ts +7 -4
  162. package/src/jobs/context.ts +24 -82
  163. package/src/jobs/permissions/decide-permission.ts +10 -8
  164. package/src/jobs/permissions/find-project-scope.ts +8 -14
  165. package/src/jobs/permissions/match-target.ts +3 -3
  166. package/src/jobs/permissions.ts +8 -6
  167. package/src/jobs/program.ts +7 -3
  168. package/src/jobs/prompt.ts +21 -24
  169. package/src/jobs/schedule.ts +14 -14
  170. package/src/jobs/slash-command.ts +20 -14
  171. package/src/jobs/status-line.ts +9 -21
  172. package/src/jobs/tool.ts +17 -11
  173. package/src/mod.ts +209 -97
  174. package/src/options.ts +22 -22
  175. package/src/records.ts +35 -23
  176. package/src/register.ts +18 -6
  177. package/src/runtime/announce.ts +74 -0
  178. package/src/runtime/bound.ts +12 -0
  179. package/src/runtime/claude.ts +5 -3
  180. package/src/runtime/conversations.ts +302 -0
  181. package/src/runtime/deadline.ts +2 -2
  182. package/src/runtime/dependencies.ts +12 -0
  183. package/src/runtime/engine.ts +22 -0
  184. package/src/runtime/grants.ts +66 -31
  185. package/src/runtime/hooks.ts +19 -10
  186. package/src/runtime/installer.ts +10 -9
  187. package/src/runtime/lifecycle.ts +313 -181
  188. package/src/runtime/metadata.ts +33 -11
  189. package/src/runtime/options.ts +19 -4
  190. package/src/runtime/programs.ts +40 -1
  191. package/src/runtime/router.ts +11 -4
  192. package/src/runtime/sessions.ts +242 -0
  193. package/src/runtime/state.ts +50 -14
  194. package/src/runtime/toaster.ts +218 -0
  195. package/src/runtime/ui.ts +42 -139
  196. package/src/session/context/context.ts +12 -0
  197. package/src/session/context/turn/message/document.ts +8 -0
  198. package/src/session/context/turn/message/message.ts +165 -0
  199. package/src/session/context/turn/message/note.ts +9 -0
  200. package/src/session/context/turn/message/notice.ts +7 -0
  201. package/src/session/context/turn/message/prompt.ts +9 -0
  202. package/src/session/context/turn/message/reply.ts +7 -0
  203. package/src/session/context/turn/message/sender.ts +38 -0
  204. package/src/session/context/turn/message/skill.ts +8 -0
  205. package/src/session/context/turn/message/tool-call.ts +10 -0
  206. package/src/session/context/turn/message/tool-result.ts +9 -0
  207. package/src/session/context/turn/turn.ts +14 -0
  208. package/src/session/conversation.ts +169 -0
  209. package/src/session/goal/goal.ts +9 -0
  210. package/src/session/goal/task.ts +19 -0
  211. package/src/session/session.ts +94 -0
  212. package/src/testing/fake-claude.ts +23 -18
  213. package/src/testing/fake-elements.ts +3 -2
  214. package/src/testing/fake-files.ts +1 -1
  215. package/src/testing/fake-sessions.ts +203 -0
  216. package/src/testing.ts +112 -33
  217. package/src/ui/components.ts +3 -3
  218. package/src/ui/elements.ts +22 -8
  219. package/src/ui/markdown.ts +22 -26
  220. package/src/ui/places.ts +49 -0
  221. package/src/utils/call-effects.ts +3 -3
  222. package/src/utils/changelog.ts +32 -0
  223. package/src/utils/matcher.ts +37 -0
  224. package/src/utils/metadata.ts +17 -15
  225. package/src/utils/parse-shell.ts +24 -16
  226. package/src/utils/paths.ts +15 -1
  227. package/src/utils/text.ts +4 -0
  228. package/testing/fake-claude.d.ts +4 -2
  229. package/testing/fake-claude.d.ts.map +1 -1
  230. package/testing/fake-claude.js +18 -12
  231. package/testing/fake-elements.js +4 -2
  232. package/testing/fake-files.js +1 -1
  233. package/testing/fake-sessions.d.ts +74 -0
  234. package/testing/fake-sessions.d.ts.map +1 -0
  235. package/testing/fake-sessions.js +170 -0
  236. package/testing.d.ts +33 -13
  237. package/testing.d.ts.map +1 -1
  238. package/testing.js +92 -21
  239. package/ui/components.d.ts +2 -3
  240. package/ui/components.d.ts.map +1 -1
  241. package/ui/components.js +3 -3
  242. package/ui/elements.d.ts.map +1 -1
  243. package/ui/elements.js +19 -9
  244. package/ui/markdown.d.ts +35 -33
  245. package/ui/markdown.d.ts.map +1 -1
  246. package/ui/markdown.js +14 -16
  247. package/ui/places.d.ts +43 -0
  248. package/ui/places.d.ts.map +1 -0
  249. package/ui/places.js +31 -0
  250. package/utils/call-effects.d.ts +1 -1
  251. package/utils/call-effects.d.ts.map +1 -1
  252. package/utils/call-effects.js +2 -2
  253. package/utils/changelog.d.ts +9 -0
  254. package/utils/changelog.d.ts.map +1 -0
  255. package/utils/changelog.js +29 -0
  256. package/utils/matcher.d.ts +13 -0
  257. package/utils/matcher.d.ts.map +1 -0
  258. package/utils/matcher.js +32 -0
  259. package/utils/metadata.d.ts +3 -3
  260. package/utils/metadata.js +18 -14
  261. package/utils/parse-shell.d.ts +1 -0
  262. package/utils/parse-shell.d.ts.map +1 -1
  263. package/utils/parse-shell.js +22 -13
  264. package/utils/paths.d.ts +2 -0
  265. package/utils/paths.d.ts.map +1 -1
  266. package/utils/paths.js +13 -1
  267. package/utils/text.d.ts +1 -0
  268. package/utils/text.d.ts.map +1 -1
  269. package/utils/text.js +3 -0
  270. package/src/ui/slots.ts +0 -36
  271. package/ui/slots.d.ts +0 -208
  272. package/ui/slots.d.ts.map +0 -1
  273. package/ui/slots.js +0 -19
@@ -1,3 +1,4 @@
1
+ import { ifMissing } from '../../utils/paths.js';
1
2
  import { basename, dirname, resolve } from '../../vendor.js';
2
3
  export async function findProjectScope(pluginRoot, home, fs) {
3
4
  const skills = dirname(pluginRoot);
@@ -32,20 +33,16 @@ export async function findProjectScope(pluginRoot, home, fs) {
32
33
  }
33
34
  async function readRepository(workTree, fs) {
34
35
  const gitPath = resolve(workTree, '.git');
35
- const git = await fs.stat(gitPath).catch(() => undefined);
36
+ const git = await fs.stat(gitPath).catch(ifMissing(undefined));
36
37
  if (git === undefined)
37
38
  return undefined;
38
39
  if (git.kind === 'dir')
39
40
  return git.realPath ?? gitPath;
40
- try {
41
- const gitDirectory = /^gitdir:\s*(.+)$/m.exec(await fs.read(gitPath))?.[1]?.trim();
42
- if (gitDirectory === undefined)
43
- throw new Error(`${gitPath} has no gitdir line.`);
44
- const linkedDirectory = resolve(workTree, gitDirectory);
45
- const commonDirectory = await fs.read(resolve(linkedDirectory, 'commondir')).then((text) => resolve(linkedDirectory, text.trim()), () => linkedDirectory);
46
- return (await fs.stat(commonDirectory)).realPath ?? commonDirectory;
47
- }
48
- catch {
41
+ const gitDirectory = /^gitdir:\s*(.+)$/m.exec(await fs.read(gitPath))?.[1]?.trim();
42
+ if (gitDirectory === undefined)
49
43
  return workTree;
50
- }
44
+ const linkedDirectory = resolve(workTree, gitDirectory);
45
+ const commonDirectory = await fs.read(resolve(linkedDirectory, 'commondir')).then((text) => resolve(linkedDirectory, text.trim()), ifMissing(linkedDirectory));
46
+ const common = await fs.stat(commonDirectory).catch(ifMissing(undefined));
47
+ return common === undefined ? workTree : (common.realPath ?? commonDirectory);
51
48
  }
@@ -1,4 +1,4 @@
1
- import { type CallBase, type CallEffects, type CommandCall, type FetchCall, type FileAccess, type FileCall, type ToolCall } from '../../utils/call-effects.js';
1
+ import { type CallBase, type CallEffects, type CommandCall, type FetchCall, type FileAccess, type FileCall, type Call } from '../../utils/call-effects.js';
2
2
  import type { Workspace } from './find-project-scope.js';
3
3
  export type TargetCalls = {
4
4
  command: CommandCall;
@@ -14,7 +14,7 @@ export type Target = {
14
14
  [Key in TargetKey]: TargetOf<Key>;
15
15
  }[TargetKey];
16
16
  export type MatchedCall = {
17
- call: ToolCall;
17
+ call: Call;
18
18
  access: FileAccess | undefined;
19
19
  folder: string;
20
20
  };
@@ -1 +1 @@
1
- {"version":3,"file":"match-target.d.ts","sourceRoot":"","sources":["../../src/jobs/permissions/match-target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AAC1K,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAIxD,MAAM,MAAM,WAAW,GAAG;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AACzI,MAAM,MAAM,SAAS,GAAG,MAAM,WAAW,CAAA;AACzC,MAAM,MAAM,QAAQ,CAAC,GAAG,SAAS,SAAS,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAA;AAC9H,MAAM,MAAM,MAAM,GAAG;KAAG,GAAG,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC;CAAE,CAAC,SAAS,CAAC,CAAA;AACrE,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAI5F,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAWrG;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAkCpI;AA4BD,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI/H"}
1
+ {"version":3,"file":"match-target.d.ts","sourceRoot":"","sources":["../../src/jobs/permissions/match-target.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,MAAM,6BAA6B,CAAA;AACtK,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAIxD,MAAM,MAAM,WAAW,GAAG;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AACzI,MAAM,MAAM,SAAS,GAAG,MAAM,WAAW,CAAA;AACzC,MAAM,MAAM,QAAQ,CAAC,GAAG,SAAS,SAAS,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAA;AAC9H,MAAM,MAAM,MAAM,GAAG;KAAG,GAAG,IAAI,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC;CAAE,CAAC,SAAS,CAAC,CAAA;AACrE,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAIxF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAWrG;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAkCpI;AA4BD,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI/H"}
@@ -1,6 +1,6 @@
1
1
  import type { Job, Mod } from '../mod.js';
2
2
  import { type PermissionRules as RulesFor, type Rule as RuleFor } from './permissions/decide-permission.js';
3
- export type { CommandCall, FetchCall, FileCall, ToolCall } from '../utils/call-effects.js';
3
+ export type { CommandCall, FetchCall, FileCall, Call } from '../utils/call-effects.js';
4
4
  export type { Target } from './permissions/match-target.js';
5
5
  export type Rule<State extends object = Record<never, never>> = RuleFor<Mod<State>>;
6
6
  export type PermissionRules<State extends object = Record<never, never>> = RulesFor<Mod<State>>;
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/jobs/permissions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAGzC,OAAO,EAAqC,KAAK,eAAe,IAAI,QAAQ,EAAE,KAAK,IAAI,IAAI,OAAO,EAAgB,MAAM,oCAAoC,CAAA;AAK5J,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAC1F,YAAY,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAC3D,MAAM,MAAM,IAAI,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AACnF,MAAM,MAAM,eAAe,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAI/F,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,eAAe,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CA6BrK"}
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/jobs/permissions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAIzC,OAAO,EAAqC,KAAK,eAAe,IAAI,QAAQ,EAAE,KAAK,IAAI,IAAI,OAAO,EAAgB,MAAM,oCAAoC,CAAA;AAK5J,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AACtF,YAAY,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAC3D,MAAM,MAAM,IAAI,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AACnF,MAAM,MAAM,eAAe,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAI/F,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,eAAe,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CA8BrK"}
@@ -1,14 +1,16 @@
1
+ import { engineOf } from '../runtime/engine.js';
1
2
  import { inputOf } from '../utils/call-effects.js';
2
3
  import { decidePermission, stricterVerdict } from './permissions/decide-permission.js';
3
4
  import { targetOf } from './permissions/match-target.js';
4
5
  import { messageOf } from '../utils/text.js';
5
- import { modOf, workspaceReader } from './context.js';
6
+ import { withinDeadline, workspaceReader } from './context.js';
6
7
  const deadline = { ms: 2000, job: 'permissions' };
7
8
  export function permissions(rules) {
8
9
  const count = typeof rules === 'function' ? undefined : checkedCount(rules);
9
10
  if (count === 0)
10
11
  throw new Error('permissions: give it a deny or an ask rule, or remove it from setup.');
11
12
  return (job) => {
13
+ const { router, toolCalls } = engineOf(job.mod);
12
14
  const readWorkspace = workspaceReader(job);
13
15
  const verdictOf = async (e) => {
14
16
  let current;
@@ -20,14 +22,14 @@ export function permissions(rules) {
20
22
  return { decision: 'deny', reason: `The permission rules of ${job.mod.name} failed, so it denies the call: ${messageOf(error)}` };
21
23
  }
22
24
  try {
23
- const [agent, workspace] = await Promise.all([job.toolCalls.agentOf(e.tool_use_id), readWorkspace()]);
24
- return await decidePermission(current, { tool: e.tool, input: inputOf(e.input), ...agent }, workspace, (call, folder) => modOf(job, { call, folder }, workspace, deadline));
25
+ const [agent, workspace] = await Promise.all([toolCalls.agentOf(e.tool_use_id), readWorkspace()]);
26
+ return await decidePermission(current, { tool: e.tool, input: inputOf(e.input), ...agent }, workspace, job.mod, (run) => withinDeadline(job.mod, deadline, 'it', run));
25
27
  }
26
28
  catch (error) {
27
29
  return { decision: 'deny', reason: `The permissions job could not read the session, so it denies the call: ${messageOf(error)}` };
28
30
  }
29
31
  };
30
- job.mod.claude.on('tool.check', async (e, next) => {
32
+ router.add('tool.check', async (e, next) => {
31
33
  const [below, ours] = await Promise.all([next(e), verdictOf(e)]);
32
34
  return ours === undefined ? below : stricterVerdict(below, ours);
33
35
  });
@@ -1 +1 @@
1
- {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/jobs/program.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAIpC,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;IACxE,KAAK,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACvD,CAAA;AAMD,wBAAgB,OAAO,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CA2ErI"}
1
+ {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/jobs/program.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAKpC,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;IACxE,KAAK,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACvD,CAAA;AAMD,wBAAgB,OAAO,CAAC,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CA8ErI"}
package/jobs/program.js CHANGED
@@ -1,10 +1,11 @@
1
- import { PermissionRefused } from '../runtime/grants.js';
1
+ import { PermissionRefused, programAddresses } from '../runtime/grants.js';
2
+ import { engineOf } from '../runtime/engine.js';
2
3
  import { messageOf } from '../utils/text.js';
3
4
  const backoffSeconds = [1, 2, 4, 8, 16];
4
5
  export function program(options) {
5
6
  const { command, environment } = options;
6
7
  return ({ mod, announce }) => {
7
- const { claude } = mod;
8
+ const { claude, report } = engineOf(mod);
8
9
  const [executable] = command;
9
10
  if (executable === undefined)
10
11
  throw new Error(`${mod.name}: the program has no command. Name the program on PATH and its arguments, such as ['preview-server', '--port', '0'].`);
@@ -13,10 +14,11 @@ export function program(options) {
13
14
  let failure;
14
15
  let exits = 0;
15
16
  const waiting = [];
17
+ const ownAddresses = programAddresses(mod);
16
18
  const stop = (reason) => {
17
19
  state = 'fatal';
18
20
  failure = new Error(`${mod.name}: the ${executable} program stopped: ${reason}`);
19
- claude.ui.log(failure.message);
21
+ report({ id: `program:${executable}`, message: `The ${executable} program stopped`, description: reason });
20
22
  for (const waiter of waiting.splice(0))
21
23
  waiter.reject(failure);
22
24
  };
@@ -44,6 +46,7 @@ export function program(options) {
44
46
  }
45
47
  state = 'running';
46
48
  exits = 0;
49
+ ownAddresses.add(address.socketPath ?? new URL(address.url).host);
47
50
  for (const waiter of waiting.splice(0))
48
51
  waiter.resolve(address);
49
52
  }
@@ -53,6 +56,8 @@ export function program(options) {
53
56
  return stop(`${messageOf(error)} Add it, then run /reload-plugins.`);
54
57
  lastError = messageOf(error);
55
58
  }
59
+ if (address !== undefined)
60
+ ownAddresses.delete(address.socketPath ?? new URL(address.url).host);
56
61
  address = undefined;
57
62
  exits += 1;
58
63
  const wait = backoffSeconds[exits - 1];
package/jobs/prompt.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import type { Job, Mod } from '../mod.js';
2
- import { type ToolCall } from '../utils/call-effects.js';
2
+ import { type Call } from '../utils/call-effects.js';
3
3
  import { type Target } from './permissions/match-target.js';
4
4
  type PromptInput = {
5
5
  userPrompt?: string;
6
- call?: ToolCall;
6
+ call?: Call & {
7
+ readonly folder: string;
8
+ };
7
9
  };
8
10
  type PromptOptions<State extends object> = {
9
11
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/jobs/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAc,GAAG,EAAE,MAAM,WAAW,CAAA;AAIrD,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACrE,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAGlF,KAAK,WAAW,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAA;AAE3D,KAAK,aAAa,CAAC,KAAK,SAAS,MAAM,IAAI;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IAC5G,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAA;IACjD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAC1B,CAAA;AAID,wBAAgB,MAAM,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAoC/I"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/jobs/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAc,GAAG,EAAE,MAAM,WAAW,CAAA;AAKrD,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAGlF,KAAK,WAAW,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,GAAG;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAA;AAErF,KAAK,aAAa,CAAC,KAAK,SAAS,MAAM,IAAI;IACzC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IAC5G,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAA;IACjD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAC1B,CAAA;AAID,wBAAgB,MAAM,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAoC/I"}
package/jobs/prompt.js CHANGED
@@ -1,10 +1,10 @@
1
- import { longestMs } from '../runtime/deadline.js';
1
+ import { engineOf } from '../runtime/engine.js';
2
2
  import { onStateChange } from '../runtime/state.js';
3
3
  import { listed, messageOf } from '../utils/text.js';
4
4
  import { callEffects } from '../utils/call-effects.js';
5
5
  import { matchTarget, targetOf } from './permissions/match-target.js';
6
- import { afterCall, modOf, modWithin, targetWords } from './context.js';
7
- const deadline = { ms: 5000, longestMs, job: 'prompt' };
6
+ import { afterCall, inFolder, targetWords, withinDeadline } from './context.js';
7
+ const deadline = { ms: 5000, job: 'prompt' };
8
8
  export function prompt({ name, prompt: text, when, after }) {
9
9
  if (name.trim() === '')
10
10
  throw new Error('prompt: give the block a name. Claude reads the text under it.');
@@ -15,10 +15,10 @@ export function prompt({ name, prompt: text, when, after }) {
15
15
  targetOf(target);
16
16
  return (job) => {
17
17
  job.reserveName('prompt', name, `prompt: ${job.mod.name} adds the name "${name}" two times. Give each prompt its own name.`);
18
- const log = (reason) => job.mod.claude.ui.log(`prompt "${name}" added nothing: ${reason}`, { to: 'debug' });
19
- const textFor = async (input, mod) => {
18
+ const log = (reason) => job.mod.log(`prompt "${name}" added nothing: ${reason}`);
19
+ const textFor = async (input) => {
20
20
  try {
21
- const value = typeof text === 'string' ? text : await text(input, mod);
21
+ const value = typeof text === 'string' ? text : await withinDeadline(job.mod, deadline, `the ${name} prompt`, () => text(input, job.mod));
22
22
  return value === '' ? undefined : value;
23
23
  }
24
24
  catch (error) {
@@ -26,12 +26,12 @@ export function prompt({ name, prompt: text, when, after }) {
26
26
  return undefined;
27
27
  }
28
28
  };
29
- const headedText = async (input, mod) => {
30
- const value = await textFor(input, mod);
29
+ const headedText = async (input) => {
30
+ const value = await textFor(input);
31
31
  return value === undefined ? undefined : `# ${name}\n${value}`;
32
32
  };
33
33
  if (when !== undefined) {
34
- addAfterUserPrompts(job, when, (userPrompt) => headedText({ userPrompt }, modWithin(job, deadline)), log);
34
+ addAfterUserPrompts(job, when, (userPrompt) => headedText({ userPrompt }), log);
35
35
  job.announce(`the ${name} prompt after matching user prompts`);
36
36
  }
37
37
  else if (targets.length > 0) {
@@ -39,23 +39,24 @@ export function prompt({ name, prompt: text, when, after }) {
39
39
  job.announce(`the ${name} prompt after ${listed(targets.map(targetWords))}`);
40
40
  }
41
41
  else {
42
- addKeptCurrent(job, name, () => textFor({}, modWithin(job, deadline)), log);
42
+ addKeptCurrent(job, name, () => textFor({}), log);
43
43
  job.announce(`the ${name} prompt`);
44
44
  }
45
45
  };
46
46
  }
47
47
  function addKeptCurrent(job, name, textFor, log) {
48
+ const { claude, router } = engineOf(job.mod);
48
49
  let given;
49
50
  let hasStarted = false;
50
51
  let isQueued = false;
51
52
  let updates = Promise.resolve();
52
- job.mod.claude.on('classic.SessionStart', (e, next) => {
53
+ router.add('classic.SessionStart', (e, next) => {
53
54
  hasStarted = e.source === 'resume' || e.source === 'compact';
54
55
  if (!hasStarted)
55
56
  given = undefined;
56
57
  return next(e);
57
58
  });
58
- job.mod.claude.on('prompt.context', async (e, next) => {
59
+ router.add('prompt.context', async (e, next) => {
59
60
  const below = await next(e);
60
61
  hasStarted = true;
61
62
  if (below.blocks.some((block) => block.name === name)) {
@@ -73,7 +74,7 @@ function addKeptCurrent(job, name, textFor, log) {
73
74
  if (value === undefined || value === given)
74
75
  return;
75
76
  given = value;
76
- await job.mod.claude.session.append({ message: { type: 'user', content: [{ type: 'text', text: `# ${name}\n${value}` }] } }).then((added) => {
77
+ await claude.session.append({ message: { type: 'user', content: [{ type: 'text', text: `# ${name}\n${value}` }] } }).then((added) => {
77
78
  if (added.deny !== undefined)
78
79
  log(`Claude Code refused the new text: ${added.deny}`);
79
80
  });
@@ -86,7 +87,7 @@ function addKeptCurrent(job, name, textFor, log) {
86
87
  });
87
88
  }
88
89
  function addAfterUserPrompts(job, when, textFor, log) {
89
- job.mod.claude.on('prompt.submit', async (e, next) => {
90
+ engineOf(job.mod).router.add('prompt.submit', async (e, next) => {
90
91
  let value;
91
92
  try {
92
93
  if (typeof when === 'function' ? when(e.text) : e.text.search(when) >= 0)
@@ -105,7 +106,7 @@ function addAfterCalls(job, targets, textFor, log) {
105
106
  const [matched] = await matchTarget(target, use, effects, workspace);
106
107
  if (matched === undefined)
107
108
  continue;
108
- const value = await textFor({ call: matched.call }, modOf(job, matched, workspace, deadline));
109
+ const value = await textFor({ call: await inFolder(matched.call, matched.folder, workspace) });
109
110
  return value === undefined ? [] : [value];
110
111
  }
111
112
  return [];
@@ -1,7 +1,7 @@
1
1
  import type { Job } from '../mod.js';
2
- export type Schedule = {
2
+ export type ScheduledPrompt = {
3
3
  readonly cron: string;
4
4
  readonly prompt: string;
5
5
  };
6
- export declare function schedule<State extends object = Record<never, never>>(entry: Schedule | ((state: State) => Schedule | undefined)): Job<void, State>;
6
+ export declare function schedule<State extends object = Record<never, never>>(entry: ScheduledPrompt | ((state: State) => ScheduledPrompt | undefined)): Job<void, State>;
7
7
  //# sourceMappingURL=schedule.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../src/jobs/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAIpC,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAIzE,wBAAgB,QAAQ,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,QAAQ,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAoDlJ"}
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../src/jobs/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAIpC,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAIhF,wBAAgB,QAAQ,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,eAAe,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,eAAe,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAoDhK"}
package/jobs/schedule.js CHANGED
@@ -1,20 +1,20 @@
1
1
  import { onStateChange } from '../runtime/state.js';
2
- import { messageOf } from '../utils/text.js';
2
+ import { engineOf } from '../runtime/engine.js';
3
3
  const scheduleCounts = new WeakMap();
4
4
  export function schedule(entry) {
5
5
  if (typeof entry !== 'function')
6
6
  checked(entry);
7
7
  return (job) => {
8
8
  const { mod } = job;
9
- const { claude } = mod;
9
+ const { claude, report: reportFailure } = engineOf(mod);
10
10
  const order = scheduleCounts.get(mod) ?? 0;
11
11
  scheduleCounts.set(mod, order + 1);
12
12
  const storeKey = `cmod:schedule:${order}`;
13
- const log = (reason) => claude.ui.log(`${mod.name}: the schedule ${reason}`);
13
+ const report = (message, error) => reportFailure({ id: storeKey, message: `The schedule ${message}`, error });
14
14
  let current;
15
15
  let isQueued = false;
16
16
  const remove = async (id) => {
17
- await claude.tool.call({ tool: 'CronDelete', id }).catch(() => undefined);
17
+ await claude.tool.call({ tool: 'CronDelete', id }).catch((error) => report(`could not delete its last cron ${id}`, error));
18
18
  await claude.store.delete(storeKey);
19
19
  };
20
20
  const update = async () => {
@@ -26,7 +26,7 @@ export function schedule(entry) {
26
26
  checked(next);
27
27
  }
28
28
  catch (error) {
29
- log(`kept its last cron: ${messageOf(error)}`);
29
+ report('kept its last cron', error);
30
30
  return;
31
31
  }
32
32
  const wanted = next === undefined ? undefined : JSON.stringify([next.cron, next.prompt]);
@@ -39,9 +39,9 @@ export function schedule(entry) {
39
39
  return;
40
40
  const answer = await claude.tool.call({ tool: 'CronCreate', cron: next.cron, prompt: next.prompt, recurring: true });
41
41
  if (answer.deny !== undefined)
42
- return log(`was refused: ${answer.deny}`);
42
+ return report('was refused', answer.deny);
43
43
  if (answer.isError === true)
44
- return log(`failed: ${answer.text ?? 'CronCreate answered an error'}`);
44
+ return report('failed', answer.text ?? 'CronCreate answered an error');
45
45
  current = { id: answer.result.id, wanted: wanted };
46
46
  await claude.store.set(storeKey, current.id);
47
47
  };
@@ -49,12 +49,12 @@ export function schedule(entry) {
49
49
  .get(storeKey)
50
50
  .then((left) => (typeof left === 'string' ? remove(left) : undefined))
51
51
  .then(update)
52
- .catch((error) => log(`failed: ${messageOf(error)}`));
52
+ .catch((error) => report('failed', error));
53
53
  onStateChange(mod.state, () => {
54
54
  if (isQueued)
55
55
  return;
56
56
  isQueued = true;
57
- updates = updates.then(update).catch((error) => log(`failed: ${messageOf(error)}`));
57
+ updates = updates.then(update).catch((error) => report('failed', error));
58
58
  });
59
59
  job.announce(typeof entry === 'function' ? 'a schedule from its state' : `a schedule at ${entry.cron}`);
60
60
  };
@@ -1,5 +1,5 @@
1
- import type { Job, Mod } from '../mod.js';
2
- export type Reply = string | {
1
+ import type { Disposable, Job, Mod } from '../mod.js';
2
+ export type CommandReply = string | {
3
3
  text?: string;
4
4
  context?: string;
5
5
  } | undefined;
@@ -11,6 +11,6 @@ export declare function slashCommand<State extends object = Record<never, never>
11
11
  readonly reply: (input: {
12
12
  args: string;
13
13
  positionals: string[];
14
- }, mod: Mod<State>) => Reply | void | Promise<Reply | void>;
15
- }): Job<void, State>;
14
+ }, mod: Mod<State>) => CommandReply | void | Promise<CommandReply | void>;
15
+ }): Job<Disposable, State>;
16
16
  //# sourceMappingURL=slash-command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slash-command.d.ts","sourceRoot":"","sources":["../src/jobs/slash-command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAMzC,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAA;AAM5E,wBAAgB,YAAY,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAA;CAC1H,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CA6BnB"}
1
+ {"version":3,"file":"slash-command.d.ts","sourceRoot":"","sources":["../src/jobs/slash-command.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAOrD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAA;AAMnF,wBAAgB,YAAY,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,YAAY,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;CACxI,GAAG,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAkCzB"}
@@ -1,35 +1,40 @@
1
1
  import { parse } from '../vendor.js';
2
- import { longestMs } from '../runtime/deadline.js';
2
+ import { engineOf } from '../runtime/engine.js';
3
3
  import { messageOf } from '../utils/text.js';
4
- import { modWithin } from './context.js';
5
- const deadline = { ms: 30000, longestMs, job: 'slashCommand' };
4
+ import { withinDeadline } from './context.js';
5
+ const deadline = { ms: 30000, job: 'slashCommand' };
6
6
  const commandName = /^[A-Za-z0-9_-]{1,64}$/;
7
7
  export function slashCommand(options) {
8
8
  const { name, description, argumentHint, immediate, reply } = options;
9
9
  return (job) => {
10
10
  const { mod, announce, reserveName } = job;
11
- const { claude } = mod;
11
+ const { claude, router, report } = engineOf(mod);
12
12
  if (!commandName.test(name))
13
13
  throw new Error(`${mod.name}: "${name}" is not a slash command name. Use 1 to 64 letters, digits, "_", or "-", without the slash.`);
14
- reserveName('slashCommand', name, `${mod.name}: the slash command /${name} is already added. Give each slashCommand its own name.`);
14
+ const reserved = reserveName('slashCommand', name, `${mod.name}: the slash command /${name} is already added. Give each slashCommand its own name.`);
15
15
  let registered;
16
16
  claude.command.register({ name, description, ...(argumentHint === undefined ? {} : { argumentHint }), ...(immediate === undefined ? {} : { immediate }) }).then(({ command }) => {
17
17
  registered = command;
18
18
  }, (error) => {
19
- claude.ui.log(`${mod.name}: /${name} is not added: ${oneLine(error)}`);
19
+ report({ id: `command:${name}`, message: `/${name} is not added`, error });
20
20
  });
21
21
  announce(`/${name}`);
22
- const replyMod = modWithin(job, deadline);
23
- claude.on('command.run', async (e, next) => {
22
+ const answering = router.add('command.run', async (e, next) => {
24
23
  if (e.command !== registered)
25
24
  return next(e);
26
25
  try {
27
- return answerOf(await reply({ args: e.args, positionals: splitWords(e.args) }, replyMod));
26
+ return answerOf(await withinDeadline(mod, deadline, `/${name}`, () => reply({ args: e.args, positionals: splitWords(e.args) }, mod)));
28
27
  }
29
28
  catch (error) {
30
29
  return { text: `/${name} failed: ${oneLine(error)}` };
31
30
  }
32
31
  });
32
+ return {
33
+ dispose() {
34
+ answering.dispose();
35
+ reserved.dispose();
36
+ },
37
+ };
33
38
  };
34
39
  }
35
40
  function answerOf(reply) {
@@ -1,15 +1,5 @@
1
1
  import type { Job, Mod } from '../mod.js';
2
- export type Usage = {
3
- model: string;
4
- context: {
5
- tokens?: number;
6
- window: number;
7
- percent?: number;
8
- };
9
- cost?: {
10
- usd: number;
11
- };
12
- };
2
+ import type { Usage } from '../session/session.js';
13
3
  export declare function statusLine<State extends object = Record<never, never>>(options: {
14
4
  readonly text: (usage: Usage, mod: Mod<State>) => string | undefined | Promise<string | undefined>;
15
5
  readonly interval?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"status-line.d.ts","sourceRoot":"","sources":["../src/jobs/status-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAKzC,MAAM,MAAM,KAAK,GAAG;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9D,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CACvB,CAAA;AAID,wBAAgB,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IAC/E,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAClG,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CA0CnB"}
1
+ {"version":3,"file":"status-line.d.ts","sourceRoot":"","sources":["../src/jobs/status-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAIzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAMlD,wBAAgB,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IAC/E,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IAClG,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAiCnB"}
@@ -1,40 +1,36 @@
1
+ import { engineOf } from '../runtime/engine.js';
2
+ import { usageOf } from '../runtime/sessions.js';
1
3
  import { messageOf } from '../utils/text.js';
2
- import { modWithin } from './context.js';
4
+ import { withinDeadline } from './context.js';
3
5
  const deadline = { ms: 2000, job: 'statusLine' };
4
6
  export function statusLine(options) {
5
7
  const { text, interval = 10000 } = options;
6
8
  return (job) => {
7
9
  const { mod, announce, reserveName } = job;
8
- const { claude } = mod;
10
+ const { claude, router } = engineOf(mod);
9
11
  if (!(interval >= 1))
10
12
  throw new Error(`${mod.name}: the status line interval is ${interval}. Give it in milliseconds, 1 or more.`);
11
13
  reserveName('statusLine', '', `${mod.name}: a status line is already added. A mod has one status line: join the texts in one statusLine.`);
12
- const textMod = modWithin(job, deadline);
13
14
  let shown;
14
15
  let queue = Promise.resolve();
15
16
  const update = async () => {
16
17
  try {
17
- const [model, usage] = await Promise.all([claude.session.model(), claude.session.usage()]);
18
- const { tokens, window, percent } = usage.context;
19
- const next = await text({
20
- model,
21
- context: { window, ...(tokens === undefined ? {} : { tokens }), ...(percent === undefined ? {} : { percent }) },
22
- ...(usage.cost === undefined ? {} : { cost: { usd: usage.cost.usd } }),
23
- }, textMod);
18
+ const usage = await usageOf(claude);
19
+ const next = await withinDeadline(mod, deadline, 'the status line', () => text(usage, mod));
24
20
  if (next === shown)
25
21
  return;
26
22
  shown = next;
27
23
  claude.ui.status(next);
28
24
  }
29
25
  catch (error) {
30
- claude.ui.log(`${mod.name}: the status line kept its last text: ${messageOf(error)}`, { to: 'debug' });
26
+ mod.log(`the status line kept its last text: ${messageOf(error)}`);
31
27
  }
32
28
  };
33
29
  const refresh = () => {
34
30
  queue = queue.then(update);
35
31
  };
36
32
  announce('a status line');
37
- claude.on('session.measure', (e, next) => {
33
+ router.add('session.measure', (e, next) => {
38
34
  refresh();
39
35
  return next(e);
40
36
  });
package/jobs/tool.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { FromSchema, JSONSchema } from 'json-schema-to-ts';
2
- import type { Job, Mod } from '../mod.js';
2
+ import type { Disposable, Job, Mod } from '../mod.js';
3
3
  export declare function tool<const Schema extends object = {
4
4
  type: 'object';
5
5
  }, State extends object = Record<never, never>>(options: {
@@ -7,7 +7,7 @@ export declare function tool<const Schema extends object = {
7
7
  readonly description: string;
8
8
  readonly inputSchema?: Schema;
9
9
  readonly execute: (input: Schema extends JSONSchema ? FromSchema<Schema> : never, mod: Mod<State>) => unknown;
10
- }): Job<{
10
+ }): Job<Disposable & {
11
11
  readonly name: string;
12
12
  }, State>;
13
13
  //# sourceMappingURL=tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/jobs/tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AASzC,wBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,EAAE,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IAC3H,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,SAAS,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,OAAO,CAAA;CAC9G,GAAG,GAAG,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,CAAC,CAqCxC"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../src/jobs/tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAUrD,wBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,SAAS,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,EAAE,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IAC3H,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,SAAS,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,OAAO,CAAA;CAC9G,GAAG,GAAG,CAAC,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,CAAC,CA0CrD"}
package/jobs/tool.js CHANGED
@@ -1,31 +1,31 @@
1
1
  import { Validator } from '../vendor.js';
2
2
  import { toolDeadline } from '../runtime/deadline.js';
3
+ import { engineOf } from '../runtime/engine.js';
3
4
  import { reservedKeys, toolInputOf } from '../runtime/tool-calls.js';
4
5
  import { messageOf } from '../utils/text.js';
5
- import { modWithin } from './context.js';
6
+ import { withinDeadline } from './context.js';
6
7
  const toolName = /^[A-Za-z0-9_-]{1,64}$/;
7
8
  export function tool(options) {
8
9
  const { name, description, inputSchema, execute } = options;
9
10
  return (job) => {
10
11
  const { mod, announce, reserveName } = job;
11
- const { claude } = mod;
12
+ const { claude, router, report } = engineOf(mod);
12
13
  if (!toolName.test(name))
13
14
  throw new Error(`${mod.name}: "${name}" is not a tool name. Use 1 to 64 letters, digits, "_", or "-".`);
14
15
  const properties = inputSchema?.properties ?? {};
15
16
  const reserved = reservedKeys.filter((key) => Object.hasOwn(properties, key));
16
17
  if (reserved.length > 0)
17
18
  throw new Error(`${mod.name}: the ${name} tool's inputSchema has the property ${reserved.join(', ')}, which Claude Code keeps for itself. Rename it.`);
18
- reserveName('tool', name, `${mod.name}: the tool ${name} is already added. Give each tool its own name.`);
19
- const validator = new Validator((inputSchema ?? { type: 'object' }), '2020-12', false);
19
+ const named = reserveName('tool', name, `${mod.name}: the tool ${name} is already added. Give each tool its own name.`);
20
+ const validator = new Validator(inputSchema ?? { type: 'object' }, '2020-12', false);
20
21
  let registered;
21
22
  claude.tool.register({ name, description, ...(inputSchema === undefined ? {} : { inputSchema: inputSchema }) }).then((answer) => {
22
23
  registered = answer.tool;
23
24
  }, (error) => {
24
- claude.ui.log(`${mod.name}: the ${name} tool is not added: ${messageOf(error)}`);
25
+ report({ id: `tool:${name}`, message: `The ${name} tool is not added`, error });
25
26
  });
26
27
  announce(`the ${name} tool`);
27
- const executeMod = modWithin(job, toolDeadline);
28
- claude.on('tool.call', async (e, next) => {
28
+ const answering = router.add('tool.call', async (e, next) => {
29
29
  if (e.tool !== registered)
30
30
  return next(e);
31
31
  const input = toolInputOf(e);
@@ -33,13 +33,19 @@ export function tool(options) {
33
33
  if (!valid)
34
34
  return { deny: `The ${name} tool input is not valid:\n${errors.map((error) => `- ${error.instanceLocation}: ${error.error}`).join('\n')}` };
35
35
  try {
36
- return { result: resultOf(await execute(input, executeMod)) };
36
+ return { result: resultOf(await withinDeadline(mod, toolDeadline, `the ${name} tool`, () => execute(input, mod))) };
37
37
  }
38
38
  catch (error) {
39
39
  return { deny: `The ${name} tool failed: ${messageOf(error)}` };
40
40
  }
41
41
  });
42
- return { name: `mcp__${claude.plugin.name}__${name}` };
42
+ return {
43
+ name: `mcp__${claude.plugin.name}__${name}`,
44
+ dispose() {
45
+ answering.dispose();
46
+ named.dispose();
47
+ },
48
+ };
43
49
  };
44
50
  }
45
51
  function resultOf(value) {