@cmodjs/core 0.3.0 → 1.5.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 (280) hide show
  1. package/docs/commands.md +61 -10
  2. package/docs/dependencies.md +16 -14
  3. package/docs/hooks.md +64 -29
  4. package/docs/index.md +29 -25
  5. package/docs/install-steps.md +13 -9
  6. package/docs/jobs.md +59 -111
  7. package/docs/mod.md +227 -175
  8. package/docs/options.md +2 -2
  9. package/docs/permissions.md +27 -16
  10. package/docs/session.md +311 -0
  11. package/docs/state.md +11 -7
  12. package/docs/testing.md +68 -23
  13. package/docs/ui.md +116 -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 +14 -4
  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 +178 -75
  48. package/mod.d.ts.map +1 -1
  49. package/mod.js +127 -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 +13 -8
  54. package/records.d.ts.map +1 -1
  55. package/records.js +33 -17
  56. package/register.d.ts.map +1 -1
  57. package/register.js +34 -5
  58. package/runtime/announce.d.ts +18 -0
  59. package/runtime/announce.d.ts.map +1 -0
  60. package/runtime/announce.js +68 -0
  61. package/runtime/before.d.ts +32 -0
  62. package/runtime/before.d.ts.map +1 -0
  63. package/runtime/before.js +214 -0
  64. package/runtime/bound.d.ts +4 -0
  65. package/runtime/bound.d.ts.map +1 -0
  66. package/runtime/bound.js +9 -0
  67. package/runtime/claude.d.ts +4 -2
  68. package/runtime/claude.d.ts.map +1 -1
  69. package/runtime/conversations.d.ts +17 -0
  70. package/runtime/conversations.d.ts.map +1 -0
  71. package/runtime/conversations.js +563 -0
  72. package/runtime/deadline.d.ts +0 -1
  73. package/runtime/deadline.d.ts.map +1 -1
  74. package/runtime/deadline.js +1 -1
  75. package/runtime/dependencies.d.ts +2 -0
  76. package/runtime/dependencies.d.ts.map +1 -1
  77. package/runtime/dependencies.js +10 -1
  78. package/runtime/engine.d.ts +13 -0
  79. package/runtime/engine.d.ts.map +1 -0
  80. package/runtime/engine.js +10 -0
  81. package/runtime/grants.d.ts +4 -0
  82. package/runtime/grants.d.ts.map +1 -1
  83. package/runtime/grants.js +68 -31
  84. package/runtime/hooks.d.ts +5 -4
  85. package/runtime/hooks.d.ts.map +1 -1
  86. package/runtime/hooks.js +29 -9
  87. package/runtime/installer.d.ts +1 -1
  88. package/runtime/installer.d.ts.map +1 -1
  89. package/runtime/installer.js +17 -14
  90. package/runtime/lifecycle.d.ts +3 -1
  91. package/runtime/lifecycle.d.ts.map +1 -1
  92. package/runtime/lifecycle.js +414 -212
  93. package/runtime/metadata.d.ts +3 -1
  94. package/runtime/metadata.d.ts.map +1 -1
  95. package/runtime/metadata.js +25 -14
  96. package/runtime/options.d.ts +3 -1
  97. package/runtime/options.d.ts.map +1 -1
  98. package/runtime/options.js +4 -4
  99. package/runtime/programs.d.ts +8 -0
  100. package/runtime/programs.d.ts.map +1 -1
  101. package/runtime/programs.js +39 -1
  102. package/runtime/router.d.ts +2 -1
  103. package/runtime/router.d.ts.map +1 -1
  104. package/runtime/router.js +11 -3
  105. package/runtime/sessions.d.ts +58 -0
  106. package/runtime/sessions.d.ts.map +1 -0
  107. package/runtime/sessions.js +433 -0
  108. package/runtime/state.d.ts +6 -2
  109. package/runtime/state.d.ts.map +1 -1
  110. package/runtime/state.js +46 -13
  111. package/runtime/toaster.d.ts +24 -0
  112. package/runtime/toaster.d.ts.map +1 -0
  113. package/runtime/toaster.js +286 -0
  114. package/runtime/ui.d.ts +6 -17
  115. package/runtime/ui.d.ts.map +1 -1
  116. package/runtime/ui.js +62 -118
  117. package/session/context/context.d.ts +12 -0
  118. package/session/context/context.d.ts.map +1 -0
  119. package/session/context/context.js +2 -0
  120. package/session/context/turn/message/document.d.ts +7 -0
  121. package/session/context/turn/message/document.d.ts.map +1 -0
  122. package/session/context/turn/message/document.js +1 -0
  123. package/session/context/turn/message/message.d.ts +55 -0
  124. package/session/context/turn/message/message.d.ts.map +1 -0
  125. package/session/context/turn/message/message.js +178 -0
  126. package/session/context/turn/message/note.d.ts +8 -0
  127. package/session/context/turn/message/note.d.ts.map +1 -0
  128. package/session/context/turn/message/note.js +1 -0
  129. package/session/context/turn/message/notice.d.ts +6 -0
  130. package/session/context/turn/message/notice.d.ts.map +1 -0
  131. package/session/context/turn/message/notice.js +1 -0
  132. package/session/context/turn/message/prompt.d.ts +8 -0
  133. package/session/context/turn/message/prompt.d.ts.map +1 -0
  134. package/session/context/turn/message/prompt.js +1 -0
  135. package/session/context/turn/message/reply.d.ts +6 -0
  136. package/session/context/turn/message/reply.d.ts.map +1 -0
  137. package/session/context/turn/message/reply.js +1 -0
  138. package/session/context/turn/message/sender.d.ts +14 -0
  139. package/session/context/turn/message/sender.d.ts.map +1 -0
  140. package/session/context/turn/message/sender.js +30 -0
  141. package/session/context/turn/message/skill.d.ts +7 -0
  142. package/session/context/turn/message/skill.d.ts.map +1 -0
  143. package/session/context/turn/message/skill.js +1 -0
  144. package/session/context/turn/message/tool-call.d.ts +9 -0
  145. package/session/context/turn/message/tool-call.d.ts.map +1 -0
  146. package/session/context/turn/message/tool-call.js +1 -0
  147. package/session/context/turn/message/tool-result.d.ts +8 -0
  148. package/session/context/turn/message/tool-result.d.ts.map +1 -0
  149. package/session/context/turn/message/tool-result.js +1 -0
  150. package/session/context/turn/turn.d.ts +13 -0
  151. package/session/context/turn/turn.d.ts.map +1 -0
  152. package/session/context/turn/turn.js +2 -0
  153. package/session/conversation.d.ts +73 -0
  154. package/session/conversation.d.ts.map +1 -0
  155. package/session/conversation.js +146 -0
  156. package/session/goal/goal.d.ts +11 -0
  157. package/session/goal/goal.d.ts.map +1 -0
  158. package/session/goal/goal.js +1 -0
  159. package/session/goal/task.d.ts +16 -0
  160. package/session/goal/task.d.ts.map +1 -0
  161. package/session/goal/task.js +3 -0
  162. package/session/session.d.ts +158 -0
  163. package/session/session.d.ts.map +1 -0
  164. package/session/session.js +2 -0
  165. package/src/jobs/check.ts +7 -4
  166. package/src/jobs/context.ts +24 -82
  167. package/src/jobs/permissions/decide-permission.ts +10 -8
  168. package/src/jobs/permissions/find-project-scope.ts +8 -14
  169. package/src/jobs/permissions/match-target.ts +3 -3
  170. package/src/jobs/permissions.ts +8 -6
  171. package/src/jobs/program.ts +13 -4
  172. package/src/jobs/prompt.ts +21 -24
  173. package/src/jobs/schedule.ts +14 -14
  174. package/src/jobs/slash-command.ts +20 -14
  175. package/src/jobs/status-line.ts +9 -21
  176. package/src/jobs/tool.ts +17 -11
  177. package/src/mod.ts +252 -97
  178. package/src/options.ts +22 -22
  179. package/src/records.ts +40 -23
  180. package/src/register.ts +34 -5
  181. package/src/runtime/announce.ts +76 -0
  182. package/src/runtime/before.ts +200 -0
  183. package/src/runtime/bound.ts +12 -0
  184. package/src/runtime/claude.ts +7 -2
  185. package/src/runtime/conversations.ts +579 -0
  186. package/src/runtime/deadline.ts +2 -2
  187. package/src/runtime/dependencies.ts +13 -1
  188. package/src/runtime/engine.ts +23 -0
  189. package/src/runtime/grants.ts +69 -29
  190. package/src/runtime/hooks.ts +39 -14
  191. package/src/runtime/installer.ts +16 -14
  192. package/src/runtime/lifecycle.ts +413 -201
  193. package/src/runtime/metadata.ts +37 -14
  194. package/src/runtime/options.ts +19 -4
  195. package/src/runtime/programs.ts +40 -1
  196. package/src/runtime/router.ts +11 -4
  197. package/src/runtime/sessions.ts +447 -0
  198. package/src/runtime/state.ts +50 -14
  199. package/src/runtime/toaster.ts +334 -0
  200. package/src/runtime/ui.ts +65 -143
  201. package/src/session/context/context.ts +14 -0
  202. package/src/session/context/turn/message/document.ts +8 -0
  203. package/src/session/context/turn/message/message.ts +203 -0
  204. package/src/session/context/turn/message/note.ts +9 -0
  205. package/src/session/context/turn/message/notice.ts +7 -0
  206. package/src/session/context/turn/message/prompt.ts +9 -0
  207. package/src/session/context/turn/message/reply.ts +7 -0
  208. package/src/session/context/turn/message/sender.ts +38 -0
  209. package/src/session/context/turn/message/skill.ts +8 -0
  210. package/src/session/context/turn/message/tool-call.ts +10 -0
  211. package/src/session/context/turn/message/tool-result.ts +9 -0
  212. package/src/session/context/turn/turn.ts +15 -0
  213. package/src/session/conversation.ts +194 -0
  214. package/src/session/goal/goal.ts +10 -0
  215. package/src/session/goal/task.ts +19 -0
  216. package/src/session/session.ts +141 -0
  217. package/src/testing/fake-claude.ts +25 -17
  218. package/src/testing/fake-elements.ts +3 -2
  219. package/src/testing/fake-files.ts +1 -1
  220. package/src/testing/fake-sessions.ts +277 -0
  221. package/src/testing.ts +138 -35
  222. package/src/ui/components.ts +3 -3
  223. package/src/ui/elements.ts +22 -8
  224. package/src/ui/markdown.ts +22 -26
  225. package/src/ui/places.ts +49 -0
  226. package/src/utils/call-effects.ts +5 -5
  227. package/src/utils/changelog.ts +32 -0
  228. package/src/utils/disposable.ts +3 -0
  229. package/src/utils/matcher.ts +37 -0
  230. package/src/utils/metadata.ts +17 -15
  231. package/src/utils/parse-shell.ts +9 -8
  232. package/src/utils/paths.ts +21 -4
  233. package/src/utils/text.ts +4 -0
  234. package/testing/fake-claude.d.ts +5 -1
  235. package/testing/fake-claude.d.ts.map +1 -1
  236. package/testing/fake-claude.js +21 -12
  237. package/testing/fake-elements.js +4 -2
  238. package/testing/fake-files.js +1 -1
  239. package/testing/fake-sessions.d.ts +81 -0
  240. package/testing/fake-sessions.d.ts.map +1 -0
  241. package/testing/fake-sessions.js +249 -0
  242. package/testing.d.ts +34 -13
  243. package/testing.d.ts.map +1 -1
  244. package/testing.js +115 -23
  245. package/ui/components.d.ts +2 -3
  246. package/ui/components.d.ts.map +1 -1
  247. package/ui/components.js +3 -3
  248. package/ui/elements.d.ts.map +1 -1
  249. package/ui/elements.js +19 -9
  250. package/ui/markdown.d.ts +35 -33
  251. package/ui/markdown.d.ts.map +1 -1
  252. package/ui/markdown.js +14 -16
  253. package/ui/places.d.ts +43 -0
  254. package/ui/places.d.ts.map +1 -0
  255. package/ui/places.js +31 -0
  256. package/utils/call-effects.d.ts +2 -1
  257. package/utils/call-effects.d.ts.map +1 -1
  258. package/utils/call-effects.js +3 -3
  259. package/utils/changelog.d.ts +9 -0
  260. package/utils/changelog.d.ts.map +1 -0
  261. package/utils/changelog.js +29 -0
  262. package/utils/disposable.d.ts +4 -0
  263. package/utils/disposable.d.ts.map +1 -0
  264. package/utils/disposable.js +1 -0
  265. package/utils/matcher.d.ts +13 -0
  266. package/utils/matcher.d.ts.map +1 -0
  267. package/utils/matcher.js +32 -0
  268. package/utils/metadata.d.ts +3 -3
  269. package/utils/metadata.js +18 -14
  270. package/utils/parse-shell.js +9 -7
  271. package/utils/paths.d.ts +4 -1
  272. package/utils/paths.d.ts.map +1 -1
  273. package/utils/paths.js +19 -4
  274. package/utils/text.d.ts +1 -0
  275. package/utils/text.d.ts.map +1 -1
  276. package/utils/text.js +3 -0
  277. package/src/ui/slots.ts +0 -36
  278. package/ui/slots.d.ts +0 -208
  279. package/ui/slots.d.ts.map +0 -1
  280. package/ui/slots.js +0 -19
package/src/mod.ts CHANGED
@@ -1,11 +1,8 @@
1
1
  import type {
2
- AgentSpawnArgs,
3
- AgentSpawnResult,
4
2
  AskOptions,
5
3
  ClassicHookInputs,
6
4
  CmodDependencies,
7
- FsEntry,
8
- FsStat,
5
+ ConfigRow,
9
6
  FsStatOptions,
10
7
  HookStream,
11
8
  HttpInit,
@@ -21,26 +18,44 @@ import type {
21
18
  ProcessSpawnRequest,
22
19
  ProcessSpawnResult,
23
20
  RenderElement,
24
- SessionMessage,
25
- Timer,
26
- ToastOptions,
21
+ Settings,
27
22
  UiScrollArgs,
28
23
  UiScrollResult,
29
24
  } from 'claude-code'
30
25
  import type { Step } from './installer.js'
31
26
  import { checkOptions, type Option, type Options, type OptionValues } from './options.js'
32
- import type { FlagPermission, ListPermission } from './records.js'
33
- import type { Claude } from './runtime/claude.js'
34
- import type { RoutedEvent } from './runtime/hooks.js'
35
- import type { RoutedHook } from './runtime/router.js'
36
- import type { ToolCalls } from './runtime/tool-calls.js'
27
+ import { bound } from './runtime/bound.js'
28
+ import { Mod as modSender } from './session/context/turn/message/sender.js'
29
+ import type { Sessions } from './session/session.js'
37
30
  import type { Pane } from './ui/define-pane.js'
38
- import type { Slot, SlotProps } from './ui/slots.js'
31
+ import type { Place, PlaceProps } from './ui/places.js'
32
+ import type { Disposable } from './utils/disposable.js'
33
+ import type { Matcher } from './utils/matcher.js'
39
34
 
40
- export type ProgressStep = {
41
- readonly done: number
42
- readonly total: number
43
- readonly label?: string
35
+ export type { Disposable }
36
+
37
+ export type ToastId = string | number
38
+
39
+ export type ToastType = 'default' | 'success' | 'info' | 'warning' | 'error' | 'loading'
40
+
41
+ export type ToastOptions = {
42
+ readonly id?: ToastId
43
+ readonly description?: string | RenderElement
44
+ readonly action?: { readonly label: string; readonly onClick: () => unknown }
45
+ readonly duration?: number
46
+ }
47
+
48
+ type ToastMessage<Value> = string | ((value: Value) => string)
49
+
50
+ export type Toast = {
51
+ (message: string, options?: ToastOptions): ToastId
52
+ success(message: string, options?: ToastOptions): ToastId
53
+ info(message: string, options?: ToastOptions): ToastId
54
+ warning(message: string, options?: ToastOptions): ToastId
55
+ error(message: string, options?: ToastOptions): ToastId
56
+ loading(message: string, options?: ToastOptions): ToastId
57
+ promise<T>(task: Promise<T> | (() => Promise<T>), messages: { readonly loading: string; readonly success: ToastMessage<T>; readonly error: ToastMessage<unknown> }): { unwrap(): Promise<T> }
58
+ dismiss(id?: ToastId): void
44
59
  }
45
60
 
46
61
  export type PaneHandle = {
@@ -48,32 +63,81 @@ export type PaneHandle = {
48
63
  close(): Promise<void>
49
64
  toggle(options?: Readonly<Pick<PaneOpenArgs, 'focus'>>): Promise<void>
50
65
  readonly isOpen: boolean
66
+ readonly isShown: boolean
67
+ readonly isFocused: boolean
51
68
  }
52
69
 
53
- export type { Claude, RoutedEvent, RoutedHook, ToolCalls }
54
- export { longestMs } from './runtime/deadline.js'
55
- export { notInstalled } from './runtime/dependencies.js'
56
- export { messageOf } from './utils/text.js'
70
+ export const SessionStart = 'SessionStart'
71
+ export const SessionEnd = 'SessionEnd'
72
+ export const UserPromptSubmit = 'UserPromptSubmit'
73
+ export const InstructionsLoaded = 'InstructionsLoaded'
74
+ export const PreToolUse = 'PreToolUse'
75
+ export const PermissionRequest = 'PermissionRequest'
76
+ export const PermissionDenied = 'PermissionDenied'
77
+ export const PostToolUse = 'PostToolUse'
78
+ export const PostToolUseFailure = 'PostToolUseFailure'
79
+ export const PostToolBatch = 'PostToolBatch'
80
+ export const SubagentStart = 'SubagentStart'
81
+ export const SubagentStop = 'SubagentStop'
82
+ export const Notification = 'Notification'
83
+ export const PreCompact = 'PreCompact'
84
+ export const Stop = 'Stop'
85
+ export const StopFailure = 'StopFailure'
86
+ export const CwdChanged = 'CwdChanged'
87
+ export const FileChanged = 'FileChanged'
88
+ export const UserPromptExpansion = 'UserPromptExpansion'
89
+ export const Setup = 'Setup'
90
+ export const ConfigChange = 'ConfigChange'
91
+ export const DirectoryAdded = 'DirectoryAdded'
92
+ export const PreModelSwitch = 'PreModelSwitch'
93
+ export const PostModelSwitch = 'PostModelSwitch'
94
+ export const PostCompact = 'PostCompact'
95
+ export const MessageDisplay = 'MessageDisplay'
96
+ export const TodoCreated = 'TaskCreated'
97
+ export const TodoCompleted = 'TaskCompleted'
98
+ export const TeammateIdle = 'TeammateIdle'
99
+ export const Elicitation = 'Elicitation'
100
+ export const ElicitationResult = 'ElicitationResult'
101
+ export const WorktreeCreate = 'WorktreeCreate'
102
+ export const WorktreeRemove = 'WorktreeRemove'
103
+
104
+ export const hooks = [
105
+ Setup,
106
+ SessionStart,
107
+ SessionEnd,
108
+ UserPromptSubmit,
109
+ UserPromptExpansion,
110
+ InstructionsLoaded,
111
+ ConfigChange,
112
+ DirectoryAdded,
113
+ PreModelSwitch,
114
+ PostModelSwitch,
115
+ PreToolUse,
116
+ PermissionRequest,
117
+ PermissionDenied,
118
+ PostToolUse,
119
+ PostToolUseFailure,
120
+ PostToolBatch,
121
+ MessageDisplay,
122
+ SubagentStart,
123
+ SubagentStop,
124
+ TodoCreated,
125
+ TodoCompleted,
126
+ TeammateIdle,
127
+ Elicitation,
128
+ ElicitationResult,
129
+ Notification,
130
+ PreCompact,
131
+ PostCompact,
132
+ Stop,
133
+ StopFailure,
134
+ CwdChanged,
135
+ FileChanged,
136
+ WorktreeCreate,
137
+ WorktreeRemove,
138
+ ] as const
57
139
 
58
- export type ModEvent =
59
- | 'SessionStart'
60
- | 'SessionEnd'
61
- | 'UserPromptSubmit'
62
- | 'InstructionsLoaded'
63
- | 'PreToolUse'
64
- | 'PermissionRequest'
65
- | 'PermissionDenied'
66
- | 'PostToolUse'
67
- | 'PostToolUseFailure'
68
- | 'PostToolBatch'
69
- | 'SubagentStart'
70
- | 'SubagentStop'
71
- | 'Notification'
72
- | 'PreCompact'
73
- | 'Stop'
74
- | 'StopFailure'
75
- | 'CwdChanged'
76
- | 'FileChanged'
140
+ export type Hook = (typeof hooks)[number]
77
141
 
78
142
  export type HookAnswer = {
79
143
  continue?: boolean
@@ -83,7 +147,7 @@ export type HookAnswer = {
83
147
  decision?: 'block'
84
148
  reason?: string
85
149
  hookSpecificOutput?: {
86
- hookEventName?: ModEvent
150
+ hookEventName?: Hook
87
151
  additionalContext?: string
88
152
  permissionDecision?: 'allow' | 'deny' | 'ask'
89
153
  permissionDecisionReason?: string
@@ -97,6 +161,8 @@ export type HookAnswer = {
97
161
  updatedToolOutput?: unknown
98
162
  updatedMCPToolOutput?: unknown
99
163
  retry?: boolean
164
+ displayContent?: string
165
+ worktreePath?: string
100
166
  }
101
167
  }
102
168
 
@@ -119,20 +185,14 @@ type HookInputs = Omit<ClassicHookInputs, 'PreToolUse' | 'PostToolUse' | 'PostTo
119
185
  CwdChanged: Omit<ClassicHookInputs['CwdChanged'], 'transcript_path'>
120
186
  }
121
187
 
122
- export type HookInput<E extends ModEvent> = HookInputs[E]
188
+ export type HookInput<H extends Hook> = HookInputs[H]
123
189
 
124
- export type ModHook<E extends ModEvent> = (input: HookInput<E>) => HookAnswer | void | Promise<HookAnswer | void>
190
+ export type HookListener<H extends Hook> = (input: HookInput<H>) => HookAnswer | void | Promise<HookAnswer | void>
125
191
 
126
192
  export type HookOptions = {
127
193
  readonly timeoutMs?: number
128
194
  }
129
195
 
130
- export type ScrollArgs = Omit<UiScrollArgs, 'to'> & { readonly to: Exclude<UiScrollArgs['to'], { requestId: string }> }
131
-
132
- export type ModClaude = Claude & {
133
- on<N extends RoutedEvent>(event: N, hook: RoutedHook<N>): void
134
- }
135
-
136
196
  export type Metadata = Readonly<Record<string, string>>
137
197
 
138
198
  export type FileMatch = {
@@ -142,71 +202,145 @@ export type FileMatch = {
142
202
  readonly error?: string
143
203
  }
144
204
 
145
- type SessionMessages = {
146
- (): Promise<SessionMessage[]>
147
- (args: { readonly agentId: string }): Promise<SessionMessage[] | { readonly deny: string }>
205
+ export type Entry = {
206
+ readonly name: string
207
+ readonly isFile: boolean
208
+ readonly isFolder: boolean
209
+ readonly isLink: boolean
210
+ readonly size: number
211
+ readonly modifiedAt: number
148
212
  }
149
213
 
150
- export type Mod<State extends object = Record<never, never>, Declared extends Options = Options> = {
151
- readonly name: string
152
- readonly state: Readonly<State>
153
- readonly options: OptionValues<Declared>
154
- readonly dataFolder: string
155
- on<E extends ModEvent>(event: E, hook: ModHook<E>, options?: HookOptions): void
156
- use<Handle>(job: Job<Handle, State>): Handle
157
- every(ms: number, hook: () => unknown): Timer
158
- readonly ui: {
159
- pane(pane: Pane<State>): PaneHandle
160
- render<S extends Slot>(slot: S, Component: (props: SlotProps<S>) => RenderElement): void
161
- toast(text: string, options?: ToastOptions): void
162
- progress<T>(title: string, task: (report: (step: ProgressStep) => void) => Promise<T>): Promise<T>
163
- ask(question: string, options?: readonly string[] | AskOptions): Promise<string>
164
- scroll(args: ScrollArgs): Promise<UiScrollResult>
165
- }
166
- readonly process: {
167
- run(argv: readonly string[], init?: ProcessRunInit): Promise<ProcessRunResult>
168
- spawn(argv: readonly string[], init?: Omit<ProcessSpawnRequest, 'argv'>): HookStream<ProcessSpawnChunk, ProcessSpawnResult>
169
- }
214
+ export type Stat = Omit<Entry, 'name'> & {
215
+ readonly realPath?: string
216
+ }
217
+
218
+ export type Allowed = {
219
+ allows(value: string): boolean
220
+ }
221
+
222
+ export type Permissions = {
223
+ readonly conversation: boolean
224
+ readonly prompt: boolean
225
+ readonly model: boolean
226
+ readonly tasks: boolean
227
+ readonly tools: boolean
228
+ readonly approve: boolean
229
+ readonly network: Allowed
230
+ readonly run: Allowed
231
+ readonly files: Allowed
232
+ readonly config: Allowed
233
+ }
234
+
235
+ export type SettingsFile = {
236
+ read(): Promise<Settings>
237
+ update(values: Readonly<Record<string, unknown>>): Promise<void>
238
+ }
239
+
240
+ export type Setting = Pick<ConfigRow, 'key' | 'label' | 'kind' | 'value' | 'isLocked'> & {
241
+ readonly description?: string
242
+ readonly options?: readonly string[]
243
+ readonly plugin?: string
244
+ }
245
+
246
+ export type Imports = {
247
+ readonly sessions: Sessions
248
+ readonly settings: { list(): Promise<Setting[]>; readonly user: SettingsFile; readonly project: SettingsFile; readonly local: SettingsFile }
249
+ readonly model: { complete(request: ModelCompleteRequest, options?: ModelCompleteOptions): Promise<ModelCompleteResult> }
170
250
  readonly fs: {
171
251
  read(path: string): Promise<string>
172
252
  write(path: string, text: string): Promise<void>
173
- list(path?: string): Promise<FsEntry[]>
174
253
  exists(path: string): Promise<boolean>
175
- stat(path: string, options?: FsStatOptions): Promise<FsStat>
176
254
  find(glob: string | readonly string[]): Promise<readonly FileMatch[]>
255
+ list(path?: string): Promise<Entry[]>
256
+ stat(path: string, options?: FsStatOptions): Promise<Stat>
177
257
  }
178
258
  readonly metadata: {
179
259
  read(path: string): Promise<Metadata>
180
260
  update(path: string, change: (metadata: Record<string, string>) => void): Promise<void>
181
261
  }
182
- readonly http: {
183
- fetch(url: string, init?: HttpInit): Promise<HttpResponse>
262
+ readonly process: {
263
+ run(argv: readonly string[], init?: ProcessRunInit): Promise<ProcessRunResult>
264
+ spawn(argv: readonly string[], init?: Omit<ProcessSpawnRequest, 'argv'>): HookStream<ProcessSpawnChunk, ProcessSpawnResult>
265
+ }
266
+ readonly http: { fetch(url: string, init?: HttpInit): Promise<HttpResponse> }
267
+ }
268
+
269
+ export const sessions: Imports['sessions'] = {
270
+ get current() {
271
+ return bound().sessions.current
272
+ },
273
+ list: (options) => bound().sessions.list(options),
274
+ search: (text, options) => bound().sessions.search(text, options),
275
+ on: ((...args: Parameters<Imports['sessions']['on']>) => bound().sessions.on(...args)) as Imports['sessions']['on'],
276
+ before: ((...args: Parameters<Imports['sessions']['before']>) => bound().sessions.before(...args)) as Imports['sessions']['before'],
277
+ }
278
+
279
+ export const settings: Imports['settings'] = {
280
+ list: () => bound().settings.list(),
281
+ user: { read: () => bound().settings.user.read(), update: (values) => bound().settings.user.update(values) },
282
+ project: { read: () => bound().settings.project.read(), update: (values) => bound().settings.project.update(values) },
283
+ local: { read: () => bound().settings.local.read(), update: (values) => bound().settings.local.update(values) },
284
+ }
285
+
286
+ export const model: Imports['model'] = {
287
+ complete: (request, options) => bound().model.complete(request, options),
288
+ }
289
+
290
+ export const fs: Imports['fs'] = {
291
+ read: (path) => bound().fs.read(path),
292
+ write: (path, text) => bound().fs.write(path, text),
293
+ exists: (path) => bound().fs.exists(path),
294
+ find: (glob) => bound().fs.find(glob),
295
+ list: (path) => bound().fs.list(path),
296
+ stat: (path, options) => bound().fs.stat(path, options),
297
+ }
298
+
299
+ export const metadata: Imports['metadata'] = {
300
+ read: (path) => bound().metadata.read(path),
301
+ update: (path, change) => bound().metadata.update(path, change),
302
+ }
303
+
304
+ export const process: Imports['process'] = {
305
+ run: (argv, init) => bound().process.run(argv, init),
306
+ spawn: (argv, init) => bound().process.spawn(argv, init),
307
+ }
308
+
309
+ export const http: Imports['http'] = {
310
+ fetch: (url, init) => bound().http.fetch(url, init),
311
+ }
312
+
313
+ export type Mod<State extends object = Record<never, never>, Declared extends Options = Options> = {
314
+ readonly name: string
315
+ readonly folder: string
316
+ readonly dataFolder: string
317
+ readonly state: Readonly<State>
318
+ readonly options: OptionValues<Declared>
319
+ readonly permissions: Permissions
320
+ on<H extends Hook>(hook: H, listener: HookListener<H>, options?: HookOptions): Disposable
321
+ on<H extends Hook>(hook: H, matcher: Matcher<HookInput<H>>, listener: HookListener<H>, options?: HookOptions): Disposable
322
+ every(ms: number, listener: () => unknown): Disposable
323
+ after(ms: number, listener: () => unknown): Disposable
324
+ use<Handle>(job: Job<Handle, State>): Handle
325
+ log(text: string): void
326
+ readonly ui: {
327
+ pane(pane: Pane<State>): PaneHandle
328
+ render<P extends Place>(place: P, Component: (props: PlaceProps<P>) => RenderElement): Disposable
329
+ readonly toast: Toast
330
+ ask(question: string, options?: readonly string[] | AskOptions): Promise<string>
331
+ copy(text: string): Promise<void>
332
+ scroll(args: UiScrollArgs): Promise<UiScrollResult>
333
+ notice(callId: string, text: string | undefined): void
184
334
  }
185
335
  readonly settings: {
186
336
  page(page: Pane<State>): void
187
337
  open(pageId?: string): Promise<void>
188
338
  }
189
- readonly session: {
190
- messages: SessionMessages
191
- append(text: string): Promise<void>
192
- submit(text: string): Promise<void>
193
- }
194
- readonly agent: {
195
- spawn(args: AgentSpawnArgs): Promise<AgentSpawnResult>
196
- }
197
- readonly model: {
198
- complete(request: ModelCompleteRequest, options?: ModelCompleteOptions): Promise<ModelCompleteResult>
199
- }
200
- readonly permissions: {
201
- has(name: ListPermission, value: string): boolean
202
- has(name: FlagPermission): boolean
203
- }
204
- readonly projectRoot: string
205
- readonly cwd: string
206
339
  readonly dependencies: CmodDependencies
207
- readonly claude: ModClaude
208
340
  }
209
341
 
342
+ export const Mod = modSender
343
+
210
344
  type StateGroups = {
211
345
  readonly memory?: object
212
346
  readonly session?: object
@@ -224,14 +358,13 @@ export type ModDefinition<State extends StateGroups = Record<never, never>, Name
224
358
  readonly options?: Declared
225
359
  readonly api?: Name extends keyof CmodDependencies ? Api<CmodDependencies[Name], State> : { readonly [method: string]: (input: never, mod: Mod<State>) => unknown }
226
360
  readonly installer?: readonly Step<State>[]
227
- setup(mod: Mod<State, Declared>): void | Promise<void>
361
+ setup(mod: Mod<State, Declared>): unknown
228
362
  }
229
363
 
230
364
  export type JobContext<State extends object = Record<never, never>> = {
231
365
  readonly mod: Mod<State>
232
366
  announce(feature: string): void
233
- reserveName(kind: string, name: string, taken: string): void
234
- readonly toolCalls: ToolCalls
367
+ reserveName(type: string, name: string, taken: string): Disposable
235
368
  }
236
369
 
237
370
  export type Job<Handle, State extends object = Record<never, never>> = (context: JobContext<State>) => Handle
@@ -241,3 +374,25 @@ export function defineMod<State extends StateGroups = Record<never, never>, Name
241
374
  if (definition.options !== undefined) checkOptions(definition.name, definition.options)
242
375
  return definition
243
376
  }
377
+
378
+ export type { Matcher } from './utils/matcher.js'
379
+ export type { Place, PlaceProps } from './ui/places.js'
380
+ export { AbovePrompt, AskUserQuestion, CommandOutput, PromptHint, SessionMode, Spinner, ToolGroup, ToolProgress, TurnDuration } from './ui/places.js'
381
+ export { Session, UsageChange, type BeforeAnswer, type BeforeEvent, type BeforeListener, type Client, type Listener, type RecordPart, type Refusal, type SearchOptions, type SessionEvent, type SessionPayload, type SessionScope, type SessionSend, type Sessions, type TaskStartArgs, type ToolCallArgs, type Usage } from './session/session.js'
382
+ export { ContextEnd, ContextStart, type Context } from './session/context/context.js'
383
+ export { TurnEnd, TurnStart, type Turn } from './session/context/turn/turn.js'
384
+ export { Message, type MessageType } from './session/context/turn/message/message.js'
385
+ export { Prompt } from './session/context/turn/message/prompt.js'
386
+ export { Note } from './session/context/turn/message/note.js'
387
+ export { Reply } from './session/context/turn/message/reply.js'
388
+ export { ToolCall } from './session/context/turn/message/tool-call.js'
389
+ export { ToolResult } from './session/context/turn/message/tool-result.js'
390
+ export { Skill } from './session/context/turn/message/skill.js'
391
+ export { Document } from './session/context/turn/message/document.js'
392
+ export { Notice } from './session/context/turn/message/notice.js'
393
+ export { Channel, Person, Schedule, Sdk, type Sender, type SenderType } from './session/context/turn/message/sender.js'
394
+ export { GoalChange, type Goal } from './session/goal/goal.js'
395
+ export { Task, TaskEnd, TaskStart } from './session/goal/task.js'
396
+ export { longestMs } from './runtime/deadline.js'
397
+ export { notInstalled } from './runtime/dependencies.js'
398
+ export { messageOf } from './utils/text.js'
package/src/options.ts CHANGED
@@ -1,11 +1,11 @@
1
- export type OptionKind = 'text' | 'secret' | 'number' | 'toggle' | 'choice' | 'folder' | 'file' | 'list'
1
+ export type OptionType = 'text' | 'secret' | 'number' | 'toggle' | 'choice' | 'folder' | 'file' | 'list'
2
2
 
3
3
  export type OptionValue = string | number | boolean | readonly string[]
4
4
 
5
5
  type Described = { readonly title: string; readonly description: string }
6
6
 
7
7
  export type Option<Value extends OptionValue = OptionValue> = Described & {
8
- readonly kind: OptionKind
8
+ readonly type: OptionType
9
9
  readonly default?: Value
10
10
  readonly min?: number
11
11
  readonly max?: number
@@ -27,17 +27,17 @@ export type UserConfigField = Described & {
27
27
  }
28
28
 
29
29
  export const option = {
30
- text: (field: Described & { readonly default?: string }): Option<string> => ({ ...field, kind: 'text' }),
31
- secret: (field: Described): Option<string> => ({ ...field, kind: 'secret' }),
32
- number: (field: Described & { readonly default?: number; readonly min?: number; readonly max?: number }): Option<number> => ({ ...field, kind: 'number' }),
33
- toggle: (field: Described & { readonly default?: boolean }): Option<boolean> => ({ ...field, kind: 'toggle', default: field.default ?? false }),
34
- choice: <const Choice extends string>(choices: readonly Choice[], field: Described & { readonly default?: Choice }): Option<Choice> => ({ ...field, kind: 'choice', choices }),
35
- folder: (field: Described & { readonly default?: string }): Option<string> => ({ ...field, kind: 'folder' }),
36
- file: (field: Described & { readonly default?: string }): Option<string> => ({ ...field, kind: 'file' }),
37
- list: (field: Described & { readonly default?: readonly string[] }): Option<readonly string[]> => ({ ...field, kind: 'list' }),
30
+ text: (field: Described & { readonly default?: string }): Option<string> => ({ ...field, type: 'text' }),
31
+ secret: (field: Described): Option<string> => ({ ...field, type: 'secret' }),
32
+ number: (field: Described & { readonly default?: number; readonly min?: number; readonly max?: number }): Option<number> => ({ ...field, type: 'number' }),
33
+ toggle: (field: Described & { readonly default?: boolean }): Option<boolean> => ({ ...field, type: 'toggle', default: field.default ?? false }),
34
+ choice: <const Choice extends string>(choices: readonly Choice[], field: Described & { readonly default?: Choice }): Option<Choice> => ({ ...field, type: 'choice', choices }),
35
+ folder: (field: Described & { readonly default?: string }): Option<string> => ({ ...field, type: 'folder' }),
36
+ file: (field: Described & { readonly default?: string }): Option<string> => ({ ...field, type: 'file' }),
37
+ list: (field: Described & { readonly default?: readonly string[] }): Option<readonly string[]> => ({ ...field, type: 'list' }),
38
38
  }
39
39
 
40
- const types: Readonly<Record<OptionKind, UserConfigField['type']>> = { text: 'string', secret: 'string', number: 'number', toggle: 'boolean', choice: 'string', folder: 'directory', file: 'file', list: 'string' }
40
+ const types: Readonly<Record<OptionType, UserConfigField['type']>> = { text: 'string', secret: 'string', number: 'number', toggle: 'boolean', choice: 'string', folder: 'directory', file: 'file', list: 'string' }
41
41
 
42
42
  const optionKey = /^[A-Za-z_]\w*$/
43
43
 
@@ -46,8 +46,8 @@ export function checkOptions(name: string, options: Options): Options {
46
46
  const at = `${name}: options.${key}`
47
47
  if (!optionKey.test(key)) throw new Error(`${at} is not an option name. Use letters, digits, and _, starting with a letter, as Claude Code passes each option on as CLAUDE_PLUGIN_OPTION_${key.toUpperCase()}.`)
48
48
  if (declared.title.trim() === '') throw new Error(`${at} needs a title, the label /config shows.`)
49
- if (declared.kind === 'choice' && (declared.choices ?? []).length === 0) throw new Error(`${at} is a choice with no choices. Give option.choice(['a', 'b'], { ... }).`)
50
- if (declared.kind === 'choice' && declared.default !== undefined && !(declared.choices ?? []).includes(declared.default as string)) throw new Error(`${at} defaults to ${String(declared.default)}, which is not one of its choices: ${(declared.choices ?? []).join(', ')}.`)
49
+ if (declared.type === 'choice' && (declared.choices ?? []).length === 0) throw new Error(`${at} is a choice with no choices. Give option.choice(['a', 'b'], { ... }).`)
50
+ if (declared.type === 'choice' && declared.default !== undefined && !(declared.choices ?? []).includes(declared.default as string)) throw new Error(`${at} defaults to ${String(declared.default)}, which is not one of its choices: ${(declared.choices ?? []).join(', ')}.`)
51
51
  }
52
52
  return options
53
53
  }
@@ -57,12 +57,12 @@ export function userConfigOf(options: Options): Record<string, UserConfigField>
57
57
  Object.entries(options).map(([key, declared]) => [
58
58
  key,
59
59
  {
60
- type: types[declared.kind],
60
+ type: types[declared.type],
61
61
  title: declared.title,
62
62
  description: declared.description,
63
- ...(declared.kind === 'secret' ? { sensitive: true } : {}),
64
- ...(declared.kind === 'list' ? { multiple: true } : {}),
65
- ...(declared.kind === 'choice' ? { options: declared.choices } : {}),
63
+ ...(declared.type === 'secret' ? { sensitive: true } : {}),
64
+ ...(declared.type === 'list' ? { multiple: true } : {}),
65
+ ...(declared.type === 'choice' ? { options: declared.choices } : {}),
66
66
  ...(declared.default === undefined ? {} : { default: declared.default }),
67
67
  ...(declared.min === undefined ? {} : { min: declared.min }),
68
68
  ...(declared.max === undefined ? {} : { max: declared.max }),
@@ -76,16 +76,16 @@ export function isUnset(value: unknown): boolean {
76
76
  }
77
77
 
78
78
  export function fitsOption(declared: Option, value: unknown): string | undefined {
79
- const kind = declared.kind
80
- if (kind === 'toggle') return typeof value === 'boolean' ? undefined : 'takes true or false'
81
- if (kind === 'number') {
79
+ const type = declared.type
80
+ if (type === 'toggle') return typeof value === 'boolean' ? undefined : 'takes true or false'
81
+ if (type === 'number') {
82
82
  if (typeof value !== 'number' || !Number.isFinite(value)) return 'takes a number'
83
83
  if (declared.min !== undefined && value < declared.min) return `takes ${declared.min} or more`
84
84
  if (declared.max !== undefined && value > declared.max) return `takes ${declared.max} or less`
85
85
  return undefined
86
86
  }
87
- if (kind === 'list') return Array.isArray(value) && value.every((item) => typeof item === 'string') ? undefined : 'takes a list of text'
87
+ if (type === 'list') return Array.isArray(value) && value.every((item) => typeof item === 'string') ? undefined : 'takes a list of text'
88
88
  if (typeof value !== 'string') return 'takes text'
89
- if (kind === 'choice' && !(declared.choices ?? []).includes(value)) return `takes one of: ${(declared.choices ?? []).join(', ')}`
89
+ if (type === 'choice' && !(declared.choices ?? []).includes(value)) return `takes one of: ${(declared.choices ?? []).join(', ')}`
90
90
  return undefined
91
91
  }