@cmodjs/core 0.1.4 → 0.1.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 (53) hide show
  1. package/README.md +4 -0
  2. package/connect.d.ts.map +1 -1
  3. package/connect.js +35 -37
  4. package/docs/commands.md +15 -6
  5. package/docs/index.md +3 -3
  6. package/docs/jobs.md +20 -20
  7. package/docs/mod.md +4 -4
  8. package/docs/testing.md +1 -1
  9. package/jobs/check.d.ts +2 -2
  10. package/jobs/check.d.ts.map +1 -1
  11. package/jobs/check.js +5 -5
  12. package/jobs/context.d.ts +14 -0
  13. package/jobs/context.d.ts.map +1 -0
  14. package/jobs/{part-context.js → context.js} +6 -6
  15. package/jobs/permissions.d.ts +2 -2
  16. package/jobs/permissions.d.ts.map +1 -1
  17. package/jobs/permissions.js +7 -7
  18. package/jobs/program.d.ts +2 -2
  19. package/jobs/program.d.ts.map +1 -1
  20. package/jobs/prompt.d.ts +2 -2
  21. package/jobs/prompt.d.ts.map +1 -1
  22. package/jobs/prompt.js +17 -17
  23. package/jobs/slash-command.d.ts +2 -2
  24. package/jobs/slash-command.d.ts.map +1 -1
  25. package/jobs/slash-command.js +5 -5
  26. package/jobs/status-line.d.ts +2 -2
  27. package/jobs/status-line.d.ts.map +1 -1
  28. package/jobs/status-line.js +5 -5
  29. package/jobs/tool.d.ts +2 -2
  30. package/jobs/tool.d.ts.map +1 -1
  31. package/jobs/tool.js +5 -5
  32. package/mod.d.ts +3 -3
  33. package/mod.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/runtime/lifecycle.d.ts.map +1 -1
  36. package/runtime/lifecycle.js +8 -9
  37. package/runtime/tool-calls.d.ts +3 -2
  38. package/runtime/tool-calls.d.ts.map +1 -1
  39. package/runtime/tool-calls.js +6 -6
  40. package/src/connect.ts +36 -39
  41. package/src/jobs/check.ts +7 -7
  42. package/src/jobs/{part-context.ts → context.ts} +10 -10
  43. package/src/jobs/permissions.ts +9 -9
  44. package/src/jobs/program.ts +2 -2
  45. package/src/jobs/prompt.ts +19 -19
  46. package/src/jobs/slash-command.ts +7 -7
  47. package/src/jobs/status-line.ts +7 -7
  48. package/src/jobs/tool.ts +7 -7
  49. package/src/mod.ts +3 -3
  50. package/src/runtime/lifecycle.ts +9 -10
  51. package/src/runtime/tool-calls.ts +8 -7
  52. package/jobs/part-context.d.ts +0 -14
  53. package/jobs/part-context.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/heyJordanParker/cmod/main/.claude-plugin/icon.png" alt="Claude Mod Manager" width="128">
3
+ </p>
4
+
1
5
  # @cmodjs/core
2
6
 
3
7
  The library every Claude Mod Manager mod imports. Claude Mod Manager (CMod) installs, tries, builds, checks, and publishes Claude Code mods: plugins with hooks, panes, slash commands, tools, permission rules, and an install step that runs with your consent.
package/connect.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["src/connect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoD,EAAE,EAAE,MAAM,aAAa,CAAA;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AA2E7C,wBAAgB,OAAO,CAAC,KAAK,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,CA8B5F"}
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["src/connect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoD,EAAE,EAAE,MAAM,aAAa,CAAA;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAwE7C,wBAAgB,OAAO,CAAC,KAAK,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,CA8B5F"}
package/connect.js CHANGED
@@ -1,14 +1,7 @@
1
1
  import { createLifecycle, readPlugin } from './runtime/lifecycle.js';
2
2
  let lifecycle;
3
- async function start($, e, next) {
4
- await lifecycle.start(claudeOf($), readPlugin);
5
- return next(e);
6
- }
7
- function route(_$, e, next) {
8
- return lifecycle.route(next.event, e, (passed) => next(passed));
9
- }
10
- function claudeOf($) {
11
- return {
3
+ async function startMod($, eventInput, passOn) {
4
+ const claude = {
12
5
  plugin: { name: $.plugin.name, root: $.plugin.root },
13
6
  ui: {
14
7
  toast: (text, options) => $.ui.toast(text, options),
@@ -64,35 +57,40 @@ function claudeOf($) {
64
57
  },
65
58
  cmod: { call: (input) => $.cmod.call(input) },
66
59
  };
60
+ await lifecycle.start(claude, readPlugin);
61
+ return passOn(eventInput);
62
+ }
63
+ function routeToMod(_$, eventInput, passOn) {
64
+ return lifecycle.route(passOn.event, eventInput, (passed) => passOn(passed));
67
65
  }
68
66
  export function connect(on, definition) {
69
67
  lifecycle = createLifecycle(definition);
70
- on('session.start', start);
71
- on('classic.SessionStart', route);
72
- on('classic.SessionEnd', route);
73
- on('classic.UserPromptSubmit', route);
74
- on('classic.InstructionsLoaded', route);
75
- on('classic.PreToolUse', route);
76
- on('classic.PermissionRequest', route);
77
- on('classic.PermissionDenied', route);
78
- on('classic.PostToolUse', route);
79
- on('classic.PostToolUseFailure', route);
80
- on('classic.PostToolBatch', route);
81
- on('classic.SubagentStart', route);
82
- on('classic.SubagentStop', route);
83
- on('classic.Notification', route);
84
- on('classic.PreCompact', route);
85
- on('classic.Stop', route);
86
- on('classic.StopFailure', route);
87
- on('tool.check', route);
88
- on('tool.call', route);
89
- on('prompt.submit', route);
90
- on('prompt.context', route);
91
- on('command.run', route);
92
- on('session.measure', route);
93
- on('skill.prompt', route);
94
- on('ui.render', route);
95
- on('ui.press', route);
96
- on('ui.close', route);
97
- on('cmod.call', route);
68
+ on('session.start', startMod);
69
+ on('classic.SessionStart', routeToMod);
70
+ on('classic.SessionEnd', routeToMod);
71
+ on('classic.UserPromptSubmit', routeToMod);
72
+ on('classic.InstructionsLoaded', routeToMod);
73
+ on('classic.PreToolUse', routeToMod);
74
+ on('classic.PermissionRequest', routeToMod);
75
+ on('classic.PermissionDenied', routeToMod);
76
+ on('classic.PostToolUse', routeToMod);
77
+ on('classic.PostToolUseFailure', routeToMod);
78
+ on('classic.PostToolBatch', routeToMod);
79
+ on('classic.SubagentStart', routeToMod);
80
+ on('classic.SubagentStop', routeToMod);
81
+ on('classic.Notification', routeToMod);
82
+ on('classic.PreCompact', routeToMod);
83
+ on('classic.Stop', routeToMod);
84
+ on('classic.StopFailure', routeToMod);
85
+ on('tool.check', routeToMod);
86
+ on('tool.call', routeToMod);
87
+ on('prompt.submit', routeToMod);
88
+ on('prompt.context', routeToMod);
89
+ on('command.run', routeToMod);
90
+ on('session.measure', routeToMod);
91
+ on('skill.prompt', routeToMod);
92
+ on('ui.render', routeToMod);
93
+ on('ui.press', routeToMod);
94
+ on('ui.close', routeToMod);
95
+ on('cmod.call', routeToMod);
98
96
  }
package/docs/commands.md CHANGED
@@ -82,22 +82,31 @@ cmod publish [path] [--dry-run]
82
82
 
83
83
  Releases the mod at `path` (default: the current folder) at the version in its `plugin.json`:
84
84
 
85
- 1. Builds the release archive from the committed files with `git archive`, leaving out `cli/`.
86
- 2. Builds the program `cli/` declares.
87
- 3. Writes `SHA256SUMS` for every file of the release.
88
- 4. Writes `.claude-plugin/marketplace.json`, listing the archive and the CMod plugin.
89
- 5. Commits that file, tags `v<version>`, pushes, and creates the GitHub release.
85
+ 1. Builds the release from the committed files, leaving out `cli/`, `.github/`, and `.claude/`. A `"files"` list in `package.json` limits the release to the paths it lists, plus `.claude-plugin/`, `package.json`, the README, and the license, the way `npm publish` reads it. List every folder the hooks module imports from, such as `"files": ["hooks", "src", "skills"]`, because the bundle in step 2 is built from the release.
86
+ 2. Bundles the hooks module that `hooks/hooks.json` names, with the mod's source and packages, into one readable `.js` file, and points `hooks/hooks.json` at it. Anthropic's plugin directory reads a repository without installing its packages, so it can follow a mod only when all its code is in one file.
87
+ 3. Checks the release with `claude plugin validate --strict`, and stops with the validator's message when it fails.
88
+ 4. Commits the release as the `release` branch, and builds the release archive from that commit.
89
+ 5. Builds the program `cli/` declares, and writes `SHA256SUMS` for every file of the release.
90
+ 6. Writes `.claude-plugin/marketplace.json`, listing the archive and the CMod plugin.
91
+ 7. Commits that file, tags `v<version>`, pushes the tag and the `release` branch, and creates the GitHub release.
92
+ 8. Prints the link to paste as the Repository when you submit the mod at [claude.ai/directory/manage](https://claude.ai/directory/manage), such as `https://github.com/owner/greeter/tree/release`. The portal reads the branch from the link, so the directory follows `release`.
93
+
94
+ The release keeps `package.json`, so Claude Code still installs the mod's packages for its install and uninstall steps. It keeps `bun.lock` too, unless a `"files"` list leaves it out. `main` keeps the source only.
90
95
 
91
96
  It needs, and refuses to start without:
92
97
 
93
98
  - the mod's folder as the root of a git repository with at least one commit, because `git archive` builds from `HEAD`
99
+ - `node_modules` in the mod's folder, from `bun install`, when the hooks module imports a package
100
+ - a `register` declared as a function in the hooks module, as `cmod new` writes it, because the validator refuses a bundled `register` that is not one
101
+ - the `claude` command, which validates the release
94
102
  - an `origin` remote on GitHub
95
103
  - a `package.json` with no dependency on a `file:` or `link:` path, which no user has
104
+ - a `"files"` list, when `package.json` has one, whose every path matches a committed file
96
105
  - a `"program"` in the `cmod` key of `package.json` that names the program `cli/` declares, when `cli/` declares one
97
106
  - the network, to hash the CMod plugin release it lists, unless the mod is CMod itself
98
107
  - for a real publish, a working tree with no uncommitted changes, a `v<version>` tag that does not exist yet, a `git push` to `origin` that succeeds, and the `gh` command, which creates the GitHub release
99
108
 
100
- `--dry-run` builds and writes everything, and pushes nothing. It allows uncommitted changes and an existing tag, and builds from the last commit.
109
+ `--dry-run` builds and writes everything, and pushes nothing. It allows uncommitted changes and an existing tag, and builds from the last commit. It prints the folder that holds the release, so you can read what the directory will read.
101
110
 
102
111
  ## Install mods
103
112
 
package/docs/index.md CHANGED
@@ -30,7 +30,7 @@ my-mod/
30
30
 
31
31
  The template is a working example to replace. It counts the prompts of the session in `state.session.prompts`, shows the count in a pane titled with the mod's name and above the prompt, and tests both. Replace the counter, the pane, the component, the Skill, and the tests with the mod's own.
32
32
 
33
- A mod imports `@cmodjs/core` by path, such as `../node_modules/@cmodjs/core/mod.js`, because Claude Code loads a plugin's own files only. Types such as `RenderElement` and `Register` come from the `claude-code` module.
33
+ A mod imports `@cmodjs/core` by path, such as `../node_modules/@cmodjs/core/mod.js`, because Claude Code loads a plugin's own files only. Types such as `RenderElement` and `On` come from the `claude-code` module.
34
34
 
35
35
  ## Type-check after the first session
36
36
 
@@ -70,7 +70,7 @@ A mod imports from the files below, and every other file in this package is inte
70
70
 
71
71
  | Import | Exports | Docs |
72
72
  | --- | --- | --- |
73
- | `mod.js` | `defineMod`, `ModDefinition`, `Mod`, `PartContext`, `Part`, `ProgressStep`, `PaneHandle`, `longestMs`, `messageOf` | [mod.md](mod.md), [jobs.md](jobs.md), [ui.md](ui.md) |
73
+ | `mod.js` | `defineMod`, `ModDefinition`, `Mod`, `JobContext`, `Job`, `ProgressStep`, `PaneHandle`, `longestMs`, `messageOf` | [mod.md](mod.md), [jobs.md](jobs.md), [ui.md](ui.md) |
74
74
  | `mod.js` | `Claude`, `RoutedEvent`, `RoutedHook`, `ToolCalls` | [jobs.md](jobs.md) |
75
75
  | `mod.js` | `ModEvent`, `ModHook`, `HookAnswer` | [hooks.md](hooks.md) |
76
76
  | `mod.js` | `notInstalled` | [dependencies.md](dependencies.md) |
@@ -87,7 +87,7 @@ A mod imports from the files below, and every other file in this package is inte
87
87
  | `jobs/prompt.js` | `prompt` | [jobs.md](jobs.md) |
88
88
  | `jobs/status-line.js` | `statusLine`, `Usage` | [jobs.md](jobs.md) |
89
89
  | `jobs/program.js` | `program`, `Program` | [jobs.md](jobs.md) |
90
- | `jobs/part-context.js` | `afterCall`, `modWithin`, `modOf`, `workspaceReader` | [jobs.md](jobs.md) |
90
+ | `jobs/context.js` | `afterCall`, `modWithin`, `modOf`, `workspaceReader` | [jobs.md](jobs.md) |
91
91
  | `jobs/permissions/decide-permission.js` | `decidePermission`, `stricterVerdict`, `Rule`, `PermissionRules`, `Decision`, `Verdict` | [jobs.md](jobs.md) |
92
92
  | `jobs/permissions/find-project-scope.js` | `findProjectScope`, `ProjectScope`, `Workspace` | [jobs.md](jobs.md) |
93
93
 
package/docs/jobs.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # Jobs
2
2
 
3
- A job is a ready-made part of a mod: a slash command, a tool, permission rules, a check, a prompt, a status line, or a background program. Add each in `setup` with `mod.use`.
3
+ A job is a ready-made feature of a mod: a slash command, a tool, permission rules, a check, a prompt, a status line, or a background program. Add each in `setup` with `mod.use`.
4
4
 
5
5
  ```ts
6
- mod.use<Handle>(part: Part<Handle, State>): Handle
6
+ mod.use<Handle>(job: Job<Handle, State>): Handle
7
7
 
8
- type Part<Handle, State> = (context: PartContext<State>) => Handle
8
+ type Job<Handle, State> = (context: JobContext<State>) => Handle
9
9
  ```
10
10
 
11
- Each job function, such as `slashCommand(options)`, returns a `Part`. `mod.use` runs the part and returns its handle. A job that has nothing to hand back returns `void`.
11
+ Each job function, such as `slashCommand(options)`, returns a `Job`. `mod.use` runs the job and returns its handle. A job that has nothing to hand back returns `void`.
12
12
 
13
- A job's bad options throw at one of two times. `check`, `permissions`, and `prompt` check their options when the job function is called. `slashCommand`, `tool`, `statusLine`, and `program` check theirs inside the part, at `mod.use`, so the error surfaces as `its setup function threw: …`.
13
+ A job's bad options throw at one of two times. `check`, `permissions`, and `prompt` check their options when the job function is called. `slashCommand`, `tool`, `statusLine`, and `program` check theirs when the job runs, at `mod.use`, so the error surfaces as `its setup function threw: …`.
14
14
 
15
15
  | Job | Import | Adds |
16
16
  | --- | --- | --- |
@@ -48,7 +48,7 @@ slashCommand<State>(options: {
48
48
  readonly argumentHint?: string
49
49
  readonly immediate?: true
50
50
  readonly reply: (input: { args: string; positionals: string[] }, mod: Mod<State>) => Reply | void | Promise<Reply | void>
51
- }): Part<void, State>
51
+ }): Job<void, State>
52
52
 
53
53
  type Reply = string | { text?: string; context?: string } | undefined
54
54
  ```
@@ -94,7 +94,7 @@ tool<Schema, State>(options: {
94
94
  readonly description: string
95
95
  readonly inputSchema?: Schema
96
96
  readonly execute: (input: FromSchema<Schema>, mod: Mod<State>) => unknown
97
- }): Part<{ readonly name: string }, State>
97
+ }): Job<{ readonly name: string }, State>
98
98
  ```
99
99
 
100
100
  - `name` is 1 to 64 letters, digits, `_`, or `-`. Claude sees the tool as `mcp__<plugin name>__<name>`, and the handle's `name` is that full name.
@@ -160,7 +160,7 @@ A `command` pattern is not a glob. CMod splits it into words the way it splits a
160
160
  ## permissions
161
161
 
162
162
  ```ts
163
- permissions<State>(rules: { deny?: Rule<State>[]; ask?: Rule<State>[] }): Part<void, State>
163
+ permissions<State>(rules: { deny?: Rule<State>[]; ask?: Rule<State>[] }): Job<void, State>
164
164
 
165
165
  type Rule<State> = Target & {
166
166
  when?: (call: <the call of the target's key>, mod: Mod<State>) => boolean | Promise<boolean>
@@ -211,7 +211,7 @@ export const guard = defineMod({
211
211
  ## check
212
212
 
213
213
  ```ts
214
- check<State>(options: { readonly after: Target | Target[]; readonly run: readonly string[]; readonly timeoutMs?: number }): Part<void, State>
214
+ check<State>(options: { readonly after: Target | Target[]; readonly run: readonly string[]; readonly timeoutMs?: number }): Job<void, State>
215
215
  ```
216
216
 
217
217
  - After each call that matches `after` and succeeds, CMod runs `run`. A call that was denied or failed runs nothing.
@@ -241,7 +241,7 @@ prompt<State>(options: {
241
241
  prompt: string | ((input: { userPrompt?: string; call?: ToolCall }, mod: Mod<State>) => string | undefined | Promise<string | undefined>)
242
242
  when?: RegExp | ((userPrompt: string) => boolean)
243
243
  after?: Target | Target[]
244
- }): Part<void, State>
244
+ }): Job<void, State>
245
245
  ```
246
246
 
247
247
  `prompt` adds text that Claude reads. `when` and `after` pick when:
@@ -277,7 +277,7 @@ export const conventions = defineMod({
277
277
  statusLine<State>(options: {
278
278
  readonly text: (usage: Usage, mod: Mod<State>) => string | undefined | Promise<string | undefined>
279
279
  readonly interval?: number
280
- }): Part<void, State>
280
+ }): Job<void, State>
281
281
 
282
282
  type Usage = { model: string; context: { tokens?: number; window: number; percent?: number }; cost?: { usd: number } }
283
283
  ```
@@ -303,7 +303,7 @@ export const usage = defineMod({
303
303
  ## program
304
304
 
305
305
  ```ts
306
- program(options: { readonly command: readonly string[]; readonly environment?: Record<string, string> }): Part<Program>
306
+ program(options: { readonly command: readonly string[]; readonly environment?: Record<string, string> }): Job<Program>
307
307
 
308
308
  type Program = {
309
309
  readonly state: 'stopped' | 'starting' | 'running' | 'backoff' | 'fatal'
@@ -345,10 +345,10 @@ export const preview = defineMod({
345
345
 
346
346
  ## Write a job of your own
347
347
 
348
- A `Part` is a function. `mod.use` calls it with a `PartContext` and returns what it returns:
348
+ A `Job` is a function. `mod.use` calls it with a `JobContext` and returns what it returns:
349
349
 
350
350
  ```ts
351
- type PartContext<State> = {
351
+ type JobContext<State> = {
352
352
  readonly mod: Mod<State>
353
353
  readonly claude: Claude
354
354
  on<N extends RoutedEvent>(event: N, hook: RoutedHook<N>): void
@@ -367,9 +367,9 @@ type PartContext<State> = {
367
367
  - `toolCalls.cwdOf(toolUseId)` returns the working folder at the call's `PreToolUse`, or `undefined` once its `PostToolUse` or `PostToolUseFailure` has run. CMod keeps the folders of the last 100 calls.
368
368
 
369
369
  ```ts
370
- import { defineMod, type Part } from '../node_modules/@cmodjs/core/mod.js'
370
+ import { defineMod, type Job } from '../node_modules/@cmodjs/core/mod.js'
371
371
 
372
- const clock: Part<{ now(): Promise<number> }> = ({ claude, announce }) => {
372
+ const clock: Job<{ now(): Promise<number> }> = ({ claude, announce }) => {
373
373
  announce('a clock')
374
374
  return { now: () => claude.clock.now() }
375
375
  }
@@ -436,10 +436,10 @@ The built-in jobs are written from these building blocks, which a job of your ow
436
436
 
437
437
  | Import | Export | What it does |
438
438
  | --- | --- | --- |
439
- | `jobs/part-context.js` | `afterCall(part, contextAfter, failed)` | Runs `contextAfter` after each call that succeeds, and adds the lines it returns to the call's result. |
440
- | `jobs/part-context.js` | `modWithin(part, deadline, workTree?)` | A `mod` whose calls fail at `deadline`, `{ ms, job?, longestMs? }`. |
441
- | `jobs/part-context.js` | `modOf(part, { call?, folder }, workspace, deadline)` | `modWithin`, with `mod.process` calls run in the call's work tree. |
442
- | `jobs/part-context.js` | `workspaceReader(part)` | A function that reads the session's `projectRoot`, `cwd`, `home`, file access, and project scope. |
439
+ | `jobs/context.js` | `afterCall(job, contextAfter, failed)` | Runs `contextAfter` after each call that succeeds, and adds the lines it returns to the call's result. |
440
+ | `jobs/context.js` | `modWithin(job, deadline, workTree?)` | A `mod` whose calls fail at `deadline`, `{ ms, job?, longestMs? }`. |
441
+ | `jobs/context.js` | `modOf(job, { call?, folder }, workspace, deadline)` | `modWithin`, with `mod.process` calls run in the call's work tree. |
442
+ | `jobs/context.js` | `workspaceReader(job)` | A function that reads the session's `projectRoot`, `cwd`, `home`, file access, and project scope. |
443
443
  | `jobs/permissions/decide-permission.js` | `decidePermission`, `stricterVerdict`, and the types `Rule`, `PermissionRules`, `Decision`, `Verdict` | Decides a call against permission rules. |
444
444
  | `jobs/permissions/find-project-scope.js` | `findProjectScope`, and the types `ProjectScope`, `Workspace` | Finds the repository a project plugin belongs to. |
445
445
 
package/docs/mod.md CHANGED
@@ -59,11 +59,11 @@ Each of these says `Fix it, then run /reload-plugins.`
59
59
  `hooks/hooks.json` names `hooks/register.ts` as the plugin's hooks module. `register.ts` calls `connect` and does nothing else:
60
60
 
61
61
  ```ts
62
- import type { Register } from 'claude-code'
62
+ import type { On } from 'claude-code'
63
63
  import { connect } from '../node_modules/@cmodjs/core/connect.js'
64
64
  import { greeter } from '../src/mod.js'
65
65
 
66
- export const register: Register = (on) => {
66
+ export function register(on: On): void {
67
67
  connect(on, greeter)
68
68
  }
69
69
  ```
@@ -72,7 +72,7 @@ export const register: Register = (on) => {
72
72
  connect<State>(on: On, definition: ModDefinition<State>): void
73
73
  ```
74
74
 
75
- `connect` registers one handler per Claude Code event the mod can use, and routes each event to the mod. Keep `register.ts` this small. Claude Code checks a hooks module before it loads it, and refuses some shapes, such as a `$` passed to a function in another module.
75
+ `connect` registers one handler per Claude Code event the mod can use, and routes each event to the mod. Keep `register.ts` this small. Claude Code checks a hooks module before it loads it, and refuses some shapes, such as a `$` passed to a function in another module. Declare `register` as a function, as here: `cmod publish` bundles the hooks module into one file, and Claude Code refuses a bundled `register` that is not one.
76
76
 
77
77
  ## What `mod` can call
78
78
 
@@ -86,7 +86,7 @@ type Mod<State> = {
86
86
  readonly projectRoot: string
87
87
  readonly cwd: string
88
88
  on(event, hook): void
89
- use(part): Handle
89
+ use(job): Handle
90
90
  readonly ui: { pane, render, toast, progress, ask }
91
91
  readonly process: { run, spawn }
92
92
  readonly fs: { read, write, list }
package/docs/testing.md CHANGED
@@ -69,7 +69,7 @@ type TestedMod<State> = {
69
69
 
70
70
  ### start
71
71
 
72
- Starts the mod: loads its state, runs `setup`, and adds its parts. `fire`, `lines`, `type`, `callTool`, `press`, and `moveTo` start the mod first, so a test calls `start` only before it reads `state` or `shown` with nothing else to run. It rejects with the reason when the mod does not start.
72
+ Starts the mod: loads its state, runs `setup`, and adds its hooks, panes, and jobs. `fire`, `lines`, `type`, `callTool`, `press`, and `moveTo` start the mod first, so a test calls `start` only before it reads `state` or `shown` with nothing else to run. It rejects with the reason when the mod does not start.
73
73
 
74
74
  ### fire
75
75
 
package/jobs/check.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import type { Part } from '../mod.js';
1
+ import type { Job } from '../mod.js';
2
2
  import { type Target } from './permissions/match-target.js';
3
3
  export declare function check<State extends object = Record<never, never>>(options: {
4
4
  readonly after: Target | Target[];
5
5
  readonly run: readonly string[];
6
6
  readonly timeoutMs?: number;
7
- }): Part<void, State>;
7
+ }): Job<void, State>;
8
8
  //# sourceMappingURL=check.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/jobs/check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAOrC,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAIlF,wBAAgB,KAAK,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CA4BlM"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/jobs/check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAOpC,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAIlF,wBAAgB,KAAK,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CA4BjM"}
package/jobs/check.js CHANGED
@@ -2,7 +2,7 @@ import { longestMs } from '../runtime/deadline.js';
2
2
  import { callEffects } from '../utils/call-effects.js';
3
3
  import { parentOf } from '../utils/paths.js';
4
4
  import { listed, messageOf } from '../utils/text.js';
5
- import { afterCall, modOf, targetWords } from './part-context.js';
5
+ import { afterCall, modOf, targetWords } from './context.js';
6
6
  import { matchTarget, targetOf } from './permissions/match-target.js';
7
7
  const defaultMs = 60_000;
8
8
  export function check(options) {
@@ -18,21 +18,21 @@ export function check(options) {
18
18
  if (!(timeoutMs > 0 && timeoutMs <= longestMs))
19
19
  throw new Error(`check: the ${named} has timeoutMs ${timeoutMs}. Set it above 0 and at most ${longestMs} (${longestMs / 60_000} minutes).`);
20
20
  const deadline = { ms: timeoutMs, job: 'check' };
21
- return (context) => {
22
- afterCall(context, async (use, workspace) => {
21
+ return (job) => {
22
+ afterCall(job, async (use, workspace) => {
23
23
  const triggered = await triggeredRun(targets, run, use, workspace);
24
24
  if (triggered === undefined)
25
25
  return [];
26
26
  try {
27
27
  const { command, folder } = triggered;
28
- const { exitCode, stdout, stderr } = await modOf(context, { folder }, workspace, deadline).process.run(command);
28
+ const { exitCode, stdout, stderr } = await modOf(job, { folder }, workspace, deadline).process.run(command);
29
29
  return exitCode === 0 ? [] : [`${command.join(' ')} exited with ${exitCode}:\n${`${stdout}${stderr}`.trim()}`];
30
30
  }
31
31
  catch (error) {
32
32
  return [`The ${named} failed: ${messageOf(error)}`];
33
33
  }
34
34
  }, (error) => [`The check job failed, so the ${named} did not run: ${messageOf(error)}`]);
35
- context.announce(`a ${named}`);
35
+ job.announce(`a ${named}`);
36
36
  };
37
37
  }
38
38
  async function triggeredRun(after, run, use, workspace) {
@@ -0,0 +1,14 @@
1
+ import type { JobContext, Mod } from '../mod.js';
2
+ import { type Deadline } from '../runtime/deadline.js';
3
+ import type { ToolCall, ToolUse } from '../utils/call-effects.js';
4
+ import { type Workspace } from './permissions/find-project-scope.js';
5
+ import { type Target } from './permissions/match-target.js';
6
+ export declare function workspaceReader({ claude }: JobContext): () => Promise<Workspace>;
7
+ export declare function afterCall<State extends object>(job: JobContext<State>, contextAfter: (use: ToolUse, workspace: Workspace) => Promise<readonly string[]>, failed: (error: unknown) => readonly string[]): void;
8
+ export declare function modOf<State extends object>(job: JobContext<State>, { call, folder }: {
9
+ readonly call?: ToolCall;
10
+ readonly folder: string;
11
+ }, workspace: Workspace, deadline: Deadline): Mod<State>;
12
+ export declare function modWithin<State extends object>({ mod, claude }: JobContext<State>, deadline: Deadline, workTree?: () => Promise<string | undefined>): Mod<State>;
13
+ export declare function targetWords(target: Target): string;
14
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/jobs/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAEhD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAItE,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,qCAAqC,CAAA;AACtF,OAAO,EAAY,KAAK,MAAM,EAAE,MAAM,+BAA+B,CAAA;AAOrE,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,UAAU,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAOhF;AAaD,wBAAgB,SAAS,CAAC,KAAK,SAAS,MAAM,EAC5C,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,EACtB,YAAY,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,EAChF,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,MAAM,EAAE,GAC5C,IAAI,CAUN;AAMD,wBAAgB,KAAK,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,CAEjM;AAED,wBAAgB,SAAS,CAAC,KAAK,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAyB,GAAG,GAAG,CAAC,KAAK,CAAC,CAwBvL;AAwBD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASlD"}
@@ -23,10 +23,10 @@ async function readSession(claude) {
23
23
  throw new Error('HOME is not set, so ~ in a path has no meaning. Start Claude Code with HOME set.');
24
24
  return { home, fs, scope: await findProjectScope(claude.plugin.root, home, fs) };
25
25
  }
26
- export function afterCall(part, contextAfter, failed) {
27
- const readWorkspace = workspaceReader(part);
28
- part.on('tool.call', async (e, next) => {
29
- const before = Promise.all([useOf(part, e), readWorkspace()]);
26
+ export function afterCall(job, contextAfter, failed) {
27
+ const readWorkspace = workspaceReader(job);
28
+ job.on('tool.call', async (e, next) => {
29
+ const before = Promise.all([useOf(job, e), readWorkspace()]);
30
30
  await before.catch(() => undefined);
31
31
  const result = await next(e);
32
32
  if (result.deny !== undefined || result.isError === true)
@@ -38,8 +38,8 @@ export function afterCall(part, contextAfter, failed) {
38
38
  async function useOf({ toolCalls }, e) {
39
39
  return { tool: e.tool, input: toolInputOf(e), ...(await toolCalls.agentOf(e.tool_use_id)) };
40
40
  }
41
- export function modOf(context, { call, folder }, workspace, deadline) {
42
- return modWithin(context, deadline, async () => workspace.scope?.workTreeOf(call !== undefined && 'path' in call ? call.path : folder));
41
+ export function modOf(job, { call, folder }, workspace, deadline) {
42
+ return modWithin(job, deadline, async () => workspace.scope?.workTreeOf(call !== undefined && 'path' in call ? call.path : folder));
43
43
  }
44
44
  export function modWithin({ mod, claude }, deadline, workTree = async () => undefined) {
45
45
  const within = (call, task) => beforeDeadline(claude, deadline, call, task);
@@ -1,8 +1,8 @@
1
- import type { Mod, Part } from '../mod.js';
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
3
  export type { CommandCall, FetchCall, FileCall, ToolCall } 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>>;
7
- export declare function permissions<State extends object = Record<never, never>>(rules: PermissionRules<State>): Part<void, State>;
7
+ export declare function permissions<State extends object = Record<never, never>>(rules: PermissionRules<State>): Job<void, State>;
8
8
  //# sourceMappingURL=permissions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/jobs/permissions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAG1C,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,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAwBzH"}
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,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAwBxH"}
@@ -1,7 +1,7 @@
1
1
  import { decidePermission, stricterVerdict } from './permissions/decide-permission.js';
2
2
  import { targetOf } from './permissions/match-target.js';
3
3
  import { messageOf } from '../utils/text.js';
4
- import { modOf, workspaceReader } from './part-context.js';
4
+ import { modOf, workspaceReader } from './context.js';
5
5
  const deadline = { ms: 2000, job: 'permissions' };
6
6
  export function permissions(rules) {
7
7
  const count = (rules.deny?.length ?? 0) + (rules.ask?.length ?? 0);
@@ -9,22 +9,22 @@ export function permissions(rules) {
9
9
  throw new Error('permissions: give it a deny or an ask rule, or remove it from setup.');
10
10
  for (const rule of [...(rules.deny ?? []), ...(rules.ask ?? [])])
11
11
  targetOf(rule);
12
- return (context) => {
13
- const readWorkspace = workspaceReader(context);
12
+ return (job) => {
13
+ const readWorkspace = workspaceReader(job);
14
14
  const verdictOf = async (e) => {
15
15
  try {
16
- const [agent, workspace] = await Promise.all([context.toolCalls.agentOf(e.tool_use_id), readWorkspace()]);
16
+ const [agent, workspace] = await Promise.all([job.toolCalls.agentOf(e.tool_use_id), readWorkspace()]);
17
17
  const use = { tool: e.tool, input: e.input, ...agent };
18
- return await decidePermission(rules, use, workspace, (call, folder) => modOf(context, { call, folder }, workspace, deadline));
18
+ return await decidePermission(rules, use, workspace, (call, folder) => modOf(job, { call, folder }, workspace, deadline));
19
19
  }
20
20
  catch (error) {
21
21
  return { decision: 'deny', reason: `The permissions job could not read the session, so it denies the call: ${messageOf(error)}` };
22
22
  }
23
23
  };
24
- context.on('tool.check', async (e, next) => {
24
+ job.on('tool.check', async (e, next) => {
25
25
  const [below, ours] = await Promise.all([next(e), verdictOf(e)]);
26
26
  return ours === undefined ? below : stricterVerdict(below, ours);
27
27
  });
28
- context.announce(`${count} permission rule${count === 1 ? '' : 's'}`);
28
+ job.announce(`${count} permission rule${count === 1 ? '' : 's'}`);
29
29
  };
30
30
  }
package/jobs/program.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Part } from '../mod.js';
1
+ import type { Job } from '../mod.js';
2
2
  export type Program = {
3
3
  readonly state: 'stopped' | 'starting' | 'running' | 'backoff' | 'fatal';
4
4
  ready(): Promise<{
@@ -9,5 +9,5 @@ export type Program = {
9
9
  export declare function program(options: {
10
10
  readonly command: readonly string[];
11
11
  readonly environment?: Record<string, string>;
12
- }): Part<Program>;
12
+ }): Job<Program>;
13
13
  //# sourceMappingURL=program.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/jobs/program.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGrC,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,IAAI,CAAC,OAAO,CAAC,CAyEtI"}
1
+ {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/jobs/program.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAGpC,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,CAyErI"}
package/jobs/prompt.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Mod, Part } from '../mod.js';
1
+ import type { Job, Mod } from '../mod.js';
2
2
  import { type ToolCall } from '../utils/call-effects.js';
3
3
  import { type Target } from './permissions/match-target.js';
4
4
  type PromptInput = {
@@ -11,6 +11,6 @@ type PromptOptions<State extends object> = {
11
11
  when?: RegExp | ((userPrompt: string) => boolean);
12
12
  after?: Target | Target[];
13
13
  };
14
- export declare function prompt<State extends object = Record<never, never>>({ name, prompt: text, when, after }: PromptOptions<State>): Part<void, State>;
14
+ export declare function prompt<State extends object = Record<never, never>>({ name, prompt: text, when, after }: PromptOptions<State>): Job<void, State>;
15
15
  export {};
16
16
  //# sourceMappingURL=prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/jobs/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAe,MAAM,WAAW,CAAA;AAGvD,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,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAoChJ"}
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;AAGrD,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"}
package/jobs/prompt.js CHANGED
@@ -2,7 +2,7 @@ import { longestMs } from '../runtime/deadline.js';
2
2
  import { listed, messageOf } from '../utils/text.js';
3
3
  import { callEffects } from '../utils/call-effects.js';
4
4
  import { matchTarget, targetOf } from './permissions/match-target.js';
5
- import { afterCall, modOf, modWithin, targetWords } from './part-context.js';
5
+ import { afterCall, modOf, modWithin, targetWords } from './context.js';
6
6
  const deadline = { ms: 5000, longestMs, job: 'prompt' };
7
7
  export function prompt({ name, prompt: text, when, after }) {
8
8
  if (name.trim() === '')
@@ -12,9 +12,9 @@ export function prompt({ name, prompt: text, when, after }) {
12
12
  const targets = after === undefined ? [] : Array.isArray(after) ? after : [after];
13
13
  for (const target of targets)
14
14
  targetOf(target);
15
- return (part) => {
16
- part.reserveName('prompt', name, `prompt: ${part.mod.name} adds the name "${name}" two times. Give each prompt its own name.`);
17
- const log = (reason) => part.claude.ui.log(`prompt "${name}" added nothing: ${reason}`, { to: 'debug' });
15
+ return (job) => {
16
+ job.reserveName('prompt', name, `prompt: ${job.mod.name} adds the name "${name}" two times. Give each prompt its own name.`);
17
+ const log = (reason) => job.claude.ui.log(`prompt "${name}" added nothing: ${reason}`, { to: 'debug' });
18
18
  const textFor = async (input, mod) => {
19
19
  try {
20
20
  const value = typeof text === 'string' ? text : await text(input, mod);
@@ -30,21 +30,21 @@ export function prompt({ name, prompt: text, when, after }) {
30
30
  return value === undefined ? undefined : `# ${name}\n${value}`;
31
31
  };
32
32
  if (when !== undefined) {
33
- addAfterUserPrompts(part, when, (userPrompt) => headedText({ userPrompt }, modWithin(part, deadline)), log);
34
- part.announce(`the ${name} prompt after matching user prompts`);
33
+ addAfterUserPrompts(job, when, (userPrompt) => headedText({ userPrompt }, modWithin(job, deadline)), log);
34
+ job.announce(`the ${name} prompt after matching user prompts`);
35
35
  }
36
36
  else if (targets.length > 0) {
37
- addAfterCalls(part, targets, headedText, log);
38
- part.announce(`the ${name} prompt after ${listed(targets.map(targetWords))}`);
37
+ addAfterCalls(job, targets, headedText, log);
38
+ job.announce(`the ${name} prompt after ${listed(targets.map(targetWords))}`);
39
39
  }
40
40
  else {
41
- addOncePerConversation(part, name, () => textFor({}, modWithin(part, deadline)), log);
42
- part.announce(`the ${name} prompt`);
41
+ addOncePerConversation(job, name, () => textFor({}, modWithin(job, deadline)), log);
42
+ job.announce(`the ${name} prompt`);
43
43
  }
44
44
  };
45
45
  }
46
- function addOncePerConversation(part, name, textFor, log) {
47
- part.on('prompt.context', async (e, next) => {
46
+ function addOncePerConversation(job, name, textFor, log) {
47
+ job.on('prompt.context', async (e, next) => {
48
48
  const below = await next(e);
49
49
  if (below.blocks.some((block) => block.name === name)) {
50
50
  log(`a block named "${name}" is already in the context`);
@@ -54,8 +54,8 @@ function addOncePerConversation(part, name, textFor, log) {
54
54
  return value === undefined ? below : { ...below, blocks: [...below.blocks, { name, text: value }] };
55
55
  });
56
56
  }
57
- function addAfterUserPrompts(part, when, textFor, log) {
58
- part.on('prompt.submit', async (e, next) => {
57
+ function addAfterUserPrompts(job, when, textFor, log) {
58
+ job.on('prompt.submit', async (e, next) => {
59
59
  let value;
60
60
  try {
61
61
  if (typeof when === 'function' ? when(e.text) : e.text.search(when) >= 0)
@@ -67,14 +67,14 @@ function addAfterUserPrompts(part, when, textFor, log) {
67
67
  return next(value === undefined ? e : { ...e, context: [...(e.context ?? []), value] });
68
68
  });
69
69
  }
70
- function addAfterCalls(part, targets, textFor, log) {
71
- afterCall(part, async (use, workspace) => {
70
+ function addAfterCalls(job, targets, textFor, log) {
71
+ afterCall(job, async (use, workspace) => {
72
72
  const effects = callEffects(use, workspace);
73
73
  for (const target of targets) {
74
74
  const [matched] = await matchTarget(target, use, effects, workspace);
75
75
  if (matched === undefined)
76
76
  continue;
77
- const value = await textFor({ call: matched.call }, modOf(part, matched, workspace, deadline));
77
+ const value = await textFor({ call: matched.call }, modOf(job, matched, workspace, deadline));
78
78
  return value === undefined ? [] : [value];
79
79
  }
80
80
  return [];