@cmodjs/core 0.2.3 → 0.3.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.
package/docs/commands.md CHANGED
@@ -209,3 +209,11 @@ cmod permission <mod> <name> [value] on|off
209
209
  ```
210
210
 
211
211
  Turns one permission of a set-up mod on or off, such as `cmod permission ci-watch model off` or `cmod permission ci-watch network api.github.com on`. A toggle in `/mods` runs it. It refuses a permission the mod's `package.json` `cmod.permissions` does not list, and the mod sees the change before its next call, with no reload ([permissions.md](permissions.md)).
212
+
213
+ ### cmod option
214
+
215
+ ```text
216
+ cmod option <name> <key> <value>
217
+ ```
218
+
219
+ Sets one option of an installed plugin through `claude plugin configure`, the way `/config` sets it, such as `cmod option ci-watch branch main`. A list option takes its items separated by commas. It refuses a key the plugin's `plugin.json` `userConfig` does not declare. A field in `/mods` and the option questions a mod asks at its first start run it, and the mod reads the new value at its next start ([options.md](options.md)).
package/docs/hooks.md CHANGED
@@ -107,7 +107,7 @@ The input of `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` has `files: {
107
107
  parseShell(line: string): ParsedShell
108
108
 
109
109
  type ParsedShell = { commands: ShellCommand[]; reads: string[]; writes: string[]; fetches: string[]; isFullyParsed: boolean }
110
- type ShellCommand = { argv: [string, ...string[]]; folder: string }
110
+ type ShellCommand = { argv: [string, ...string[]]; folder: string; input?: number }
111
111
 
112
112
  resolve(...paths): string join(...paths): string dirname(path): string basename(path, suffix?): string
113
113
  extname(path): string relative(from, to): string isAbsolute(path): boolean normalize(path): string
@@ -115,6 +115,7 @@ extname(path): string relative(from, to): string isAbsolute(path): bool
115
115
 
116
116
  - `commands` lists every command the line runs, those inside `sudo`, `bash -c`, `$(…)`, and heredocs included, with flags such as `-rf` split into `-r` and `-f`.
117
117
  - `folder` is where a command runs, as the line wrote it: `''` for the folder the line starts in, `src` after `cd src`, `/tmp` after `cd /tmp`, and `~/p` after `cd ~/p`. `resolve(input.cwd, folder)` makes it absolute.
118
+ - `input` is the index in `commands` of the command whose output this one reads through `|` or `|&`, and is absent for a command that reads no pipe. `trace read a.ts | head -5` gives `head` the `input` `0`. A wrapper such as `timeout 5 trace read a.ts` writes the output of the command it runs, so `input` names `trace`, not `timeout`.
118
119
  - `reads`, `writes`, and `fetches` are the paths and URLs the commands name, as written. A path that holds a shell expansion makes `isFullyParsed` false, and so does a line the parser cannot read.
119
120
  - `resolve` has no working folder to start from in Claude Code's hooks, so pass an absolute folder first, such as `resolve(mod.cwd, path)`.
120
121
 
package/docs/jobs.md CHANGED
@@ -448,7 +448,7 @@ export const timer = defineMod({
448
448
 
449
449
  Each member calls the member of the same name in Claude Code's hooks API, `$`, with the same arguments. The `env` members read `$.env.get` instead. `mod.claude.on` adds a handler, as [Write a job of your own](#write-a-job-of-your-own) shows.
450
450
 
451
- A member that does what a permission covers checks it first and rejects with the fix: `process` needs `run`, `http.fetch` needs `network`, `fs.write` outside the project and the data folder needs `files` or, for Claude Code's own settings files, `config`, `fs.read` of a transcript needs `conversation`, `session.messages` needs `conversation`, `session.append` and `prompt.submit` need `prompt`, `model.complete` needs `model`, `agent.spawn` needs `agents`, `config.set` needs `config`, and `tool.call` needs `tools`, except `CronCreate`, which needs `prompt`, and `CronList` and `CronDelete`, which need nothing ([permissions.md](permissions.md)).
451
+ A member that does what a permission covers checks it first and rejects with the fix: `process` needs `run`, `http.fetch` needs `network`, `fs.write` outside the project and the data folder needs `files` or, for Claude Code's own settings files, `config`, `fs.read` of a transcript needs `conversation`, `session.messages` needs `conversation`, `session.append` and `prompt.submit` need `prompt`, `model.complete` needs `model`, `agent.spawn` needs `agents`, and `tool.call` needs `tools`, except `CronCreate`, which needs `prompt`, and `CronList` and `CronDelete`, which need nothing ([permissions.md](permissions.md)).
452
452
 
453
453
  | Member | What it does |
454
454
  | --- | --- |
@@ -470,7 +470,7 @@ A member that does what a permission covers checks it first and rejects with the
470
470
  | `fs.read`, `fs.write`, `fs.list`, `fs.exists`, `fs.stat` | What `mod.fs` calls. A relative path is under the session's working folder. |
471
471
  | `http.fetch(url, init?)` | What `mod.http.fetch` calls. |
472
472
  | `settings.read(args?)` | Reads Claude Code's own `settings.json` files. Without `{ source }` it answers every source merged. `user`, `project`, `local`, `flag`, and `policy` each answer one. |
473
- | `config.list()`, `config.set({ key, value })` | Lists the rows `/config` shows, and changes one, such as `<mod>.<option>`. `set` needs `config`. |
473
+ | `config.list()` | Lists the rows `/config` shows, such as `<mod>.<option>`. A mod sets an option through `cmod option <mod> <key> <value>`, as `/mods` does. |
474
474
  | `store.get`, `store.set`, `store.delete`, `store.keys` | The plugin's key-value store, where cmod also keeps `mod.state`. |
475
475
  | `clock.now()` | Resolves milliseconds since the epoch. |
476
476
  | `clock.after(ms, fn)` | Calls `fn` once after `ms` milliseconds, and returns `{ cancel() }`. |
package/docs/options.md CHANGED
@@ -49,7 +49,7 @@ Each value is, in order of precedence:
49
49
  3. The value the person set in `/config`.
50
50
  4. The `default` in the declaration.
51
51
 
52
- An option with no default and no value is asked before `setup` runs, on a page of the installer pane: one field per option, each saved to `/config` as the person submits it. A secret is set in `/config` itself, where Claude Code keeps it in the keychain, and the page says so. When the person chooses Not now, or no one can answer, as in `claude -p`, the mod does not start: its progress line says `it needs <title>` and `Set it in /config.`, and Claude Code reloads the mod once the value is set.
52
+ An option with no default and no value is asked before `setup` runs, on a page of the installer pane: one field per option, each saved to `/config` through `cmod option` as the person submits it. A secret is set in `/config` itself, where Claude Code keeps it in the keychain, and the page says so. When the person chooses Not now, or no one can answer, as in `claude -p`, the mod does not start: its progress line says `it needs <title>` and `Set it in /config.`, and Claude Code reloads the mod once the value is set.
53
53
 
54
54
  `cmod install`, `cmod link`, and `cmod try` set options from the terminal with `--option key=value`, once per option ([commands.md](commands.md)).
55
55
 
@@ -24,7 +24,7 @@ They sit in the `package.json` `cmod` key, beside `install` and `keys`, because
24
24
  | `"model": true` | Ask a model, which uses your plan | `mod.model.complete` |
25
25
  | `"agents": true` | Start agents | `mod.agent.spawn` |
26
26
  | `"tools": true` | Use and change Claude's tool calls | `mod.claude.tool.call`, and a hook that changes a tool call's input |
27
- | `"config": true` | Change your Claude Code settings | `mod.claude.config.set`, and writes to `.claude/settings.json`, `.claude/settings.local.json`, and `.mcp.json` |
27
+ | `"config": true` | Change your Claude Code settings | writes to `.claude/settings.json`, `.claude/settings.local.json`, and `.mcp.json` |
28
28
  | `"approve": true` | Approve Claude's tool calls for you | an allow or an ask from the mod's permission rules or hooks, and a `retry`. A deny needs no permission |
29
29
 
30
30
  Reading files, drawing panes and slot renders, toasts, slash commands, tools of the mod's own, and hooks need no permission.
package/docs/testing.md CHANGED
@@ -241,7 +241,7 @@ Set a fake to answer a call:
241
241
  - `clock.after(ms, fn)` and `clock.every(ms, fn)` each return `{ cancel() }`, and so must a fake of either. `clock.after` runs a real timer by default. `clock.every` never fires unless a fake keeps `fn` for the test to call, so a test of `mod.every` keeps it and calls it once per tick.
242
242
  - `session.append` and `prompt.submit` take every note and prompt by default, into `shown.notes` and `shown.prompts`. A fake that answers `{ deny }` or `{ drop }` refuses one.
243
243
  - `model.complete` has no default: set it to the answer the model gives, such as `async () => ({ isAnswered: true, text: 'no', usage })`.
244
- - `config.list` answers no `/config` rows by default, and `config.set` answers every change with `{ value }`. A `config.list` fake that answers a row with `isLocked: true` locks that option as managed settings do.
244
+ - `config.list` answers no `/config` rows by default. A `config.list` fake that answers a row with `isLocked: true` locks that option as managed settings do.
245
245
  - `tool.call` answers `CronCreate`, `CronDelete`, and `CronList` by default, keeping the crons a `schedule` makes in `shown.schedules`. A call to any other tool needs a fake.
246
246
  - `files` takes symbolic links too: `testMod(mod, { files, links: { '/work/skills': '/shared/skills' } })` makes `/work/skills` a link to `/shared/skills`, so a test of `mod.fs.find` or `mod.metadata.update` follows it as a real file system does.
247
247
  - `testMod` runs no installer pane: an option with no default and no value fails `start()` with its fix, and the `installer` steps are not shown. Test a step's `render` and `isDone` by calling them with the started `mod`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmodjs/core",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Build Claude Code mods: hooks, panes, permissions, checks, and an install that just works.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/register.js CHANGED
@@ -34,7 +34,7 @@ async function startMod($, eventInput, passOn) {
34
34
  },
35
35
  http: { fetch: (url, init) => $.http.fetch(url, init) },
36
36
  settings: { read: (args) => $.settings.read(args) },
37
- config: { list: () => $.config.list(), set: (args) => $.config.set(args) },
37
+ config: { list: () => $.config.list() },
38
38
  store: {
39
39
  get: (key) => $.store.get(key),
40
40
  set: (key, value) => $.store.set(key, value),
@@ -36,7 +36,6 @@ export type Claude = {
36
36
  };
37
37
  readonly config: {
38
38
  list(): Promise<ConfigRow[]>;
39
- set: EngineInterface['config']['set'];
40
39
  };
41
40
  readonly store: {
42
41
  get(key: string): Promise<unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/runtime/claude.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/runtime/claude.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/runtime/grants.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAA;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,SAAS,MAAM,EAAE,CAAA;CAC9C,CAAA;AAED,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAc/C,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA6ErE;AAmBD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAUvG;AAcD,wBAAgB,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9G;AA4BD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1C,CAAA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAa9G;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BxJ"}
1
+ {"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/runtime/grants.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,CAAA;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAA;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,SAAS,MAAM,EAAE,CAAA;CAC9C,CAAA;AAED,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAc/C,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAsErE;AAmBD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAUvG;AAcD,wBAAgB,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9G;AA4BD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1C,CAAA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAa9G;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BxJ"}
package/runtime/grants.js CHANGED
@@ -46,13 +46,6 @@ export function checkingGrants(claude, grants) {
46
46
  return claude.http.fetch(url, init);
47
47
  },
48
48
  },
49
- config: {
50
- ...claude.config,
51
- set: (async (args) => {
52
- await gate('config.set', 'config');
53
- return claude.config.set(args);
54
- }),
55
- },
56
49
  session: {
57
50
  ...claude.session,
58
51
  messages: (async (...args) => {
@@ -153,8 +153,8 @@ export function createLifecycle(definition, checksPermissions = () => true, opti
153
153
  const misfit = option.kind === 'toggle' && !['true', 'false'].includes(text.trim()) ? 'takes true or false' : fitsOption(option, value);
154
154
  if (misfit !== undefined)
155
155
  return `${option.title} ${misfit}.`;
156
- const saved = await claude().config.set({ key: `${plugin.name}.${key}`, value });
157
- return saved.deny;
156
+ const saved = await claude().process.run(['cmod', 'option', plugin.name, key, Array.isArray(value) ? value.join(',') : String(value)]);
157
+ return saved.exitCode === 0 ? undefined : lastLineOf(saved.stderr) ?? `cmod option exited ${saved.exitCode}.`;
158
158
  };
159
159
  const askOptions = async (missing) => {
160
160
  if (!asksPerson || plugin === undefined || (await claude().session.surfaces()).length === 0)
package/src/register.ts CHANGED
@@ -44,7 +44,7 @@ async function startMod($: EngineInterface, eventInput: Frozen<Args<'session.sta
44
44
  },
45
45
  http: { fetch: (url, init) => $.http.fetch(url, init) },
46
46
  settings: { read: (args) => $.settings.read(args) },
47
- config: { list: () => $.config.list(), set: (args) => $.config.set(args) },
47
+ config: { list: () => $.config.list() },
48
48
  store: {
49
49
  get: (key) => $.store.get(key),
50
50
  set: (key, value) => $.store.set(key, value),
@@ -65,7 +65,7 @@ export type Claude = {
65
65
  }
66
66
  readonly http: { fetch(url: string, init?: HttpInit): Promise<HttpResponse> }
67
67
  readonly settings: { read(args?: SettingsReadArgs): Promise<Settings> }
68
- readonly config: { list(): Promise<ConfigRow[]>; set: EngineInterface['config']['set'] }
68
+ readonly config: { list(): Promise<ConfigRow[]> }
69
69
  readonly store: {
70
70
  get(key: string): Promise<unknown>
71
71
  set(key: string, value: unknown): Promise<void>
@@ -63,13 +63,6 @@ export function checkingGrants(claude: Claude, grants: Grants): Claude {
63
63
  return claude.http.fetch(url, init)
64
64
  },
65
65
  },
66
- config: {
67
- ...claude.config,
68
- set: (async (args: Parameters<Claude['config']['set']>[0]) => {
69
- await gate('config.set', 'config')
70
- return claude.config.set(args)
71
- }) as Claude['config']['set'],
72
- },
73
66
  session: {
74
67
  ...claude.session,
75
68
  messages: (async (...args: Parameters<Claude['session']['messages']>) => {
@@ -209,8 +209,8 @@ export function createLifecycle<State extends object, Declared extends Options =
209
209
  const value = option.kind === 'number' ? Number(text) : option.kind === 'toggle' ? text.trim() === 'true' : option.kind === 'list' ? text.split(',').map((item) => item.trim()).filter((item) => item !== '') : text
210
210
  const misfit = option.kind === 'toggle' && !['true', 'false'].includes(text.trim()) ? 'takes true or false' : fitsOption(option, value)
211
211
  if (misfit !== undefined) return `${option.title} ${misfit}.`
212
- const saved = await claude().config.set({ key: `${plugin.name}.${key}`, value })
213
- return saved.deny
212
+ const saved = await claude().process.run(['cmod', 'option', plugin.name, key, Array.isArray(value) ? value.join(',') : String(value)])
213
+ return saved.exitCode === 0 ? undefined : lastLineOf(saved.stderr) ?? `cmod option exited ${saved.exitCode}.`
214
214
  }
215
215
 
216
216
  const askOptions = async (missing: MissingOptions): Promise<boolean> => {
@@ -12,7 +12,7 @@ export type Fakes = {
12
12
  fs: { read?: Claude['fs']['read']; write?: Claude['fs']['write']; list?: Claude['fs']['list']; exists?: Claude['fs']['exists']; stat?: Claude['fs']['stat'] }
13
13
  http: { fetch?: Claude['http']['fetch'] }
14
14
  settings: { read?: Claude['settings']['read'] }
15
- config: { list?: Claude['config']['list']; set?: Claude['config']['set'] }
15
+ config: { list?: Claude['config']['list'] }
16
16
  ui: { ask?: Claude['ui']['ask']; open?: Claude['ui']['open']; scroll?: Claude['ui']['scroll'] }
17
17
  session: { messages?: Claude['session']['messages']; append?: Claude['session']['append'] }
18
18
  prompt: { submit?: Claude['prompt']['submit'] }
@@ -151,7 +151,6 @@ export function fakeClaude(plugin: { readonly name: string; readonly root: strin
151
151
  settings: { read: rejected('settings.read', () => fakes.settings.read) },
152
152
  config: {
153
153
  list: rejected('config.list', () => fakes.config.list ?? (async () => [])),
154
- set: rejected('config.set', () => fakes.config.set ?? (async ({ value }) => ({ value }))) as Claude['config']['set'],
155
154
  },
156
155
  agent: {
157
156
  list: rejected('agent.list', () => fakes.agent.list),
@@ -1,6 +1,6 @@
1
1
  import { basename, parse, type GlobPattern, type ParseEntry } from '../vendor.js'
2
2
 
3
- export type ShellCommand = { argv: [string, ...string[]]; folder: string }
3
+ export type ShellCommand = { argv: [string, ...string[]]; folder: string; input?: number }
4
4
 
5
5
  export type ParsedShell = {
6
6
  commands: ShellCommand[]
@@ -16,7 +16,8 @@ type Expansion = Marker & { end: number }
16
16
  type Heredoc = { delimiter: string; stripsTabs: boolean; isQuoted: boolean; marker: Marker; end: number }
17
17
  type Scan = { text: string; markers: Marker[]; end: number }
18
18
  type Redirect = { operator: string; target: Word }
19
- type Command = { words: Word[]; redirects: Redirect[]; isPipedIn: boolean }
19
+ type Pipe = { writer: number | undefined }
20
+ type Command = { words: Word[]; redirects: Redirect[]; pipe: Pipe | undefined }
20
21
  type Stdin = { kind: 'text'; text: string } | { kind: 'pipe' }
21
22
  type Access = 'reads' | 'writes'
22
23
  type OptionSpec = { valued?: string; optional?: string; longValued?: readonly string[] }
@@ -470,7 +471,7 @@ function readHeredocBodies(source: string, from: number, heredocs: Heredoc[]): n
470
471
  function walk(tokens: ParseEntry[], markers: Marker[], start: string, result: ParsedShell): void {
471
472
  let folder = start
472
473
  const subshells: string[] = []
473
- let command = newCommand(false)
474
+ let command = newCommand(undefined)
474
475
  let caseState: 'header' | 'pattern' | undefined
475
476
  for (let index = 0; index < tokens.length; index += 1) {
476
477
  const token = tokens[index]!
@@ -500,12 +501,14 @@ function walk(tokens: ParseEntry[], markers: Marker[], start: string, result: Pa
500
501
  }
501
502
  const following = tokens[index + 1]
502
503
  if (operator === '(' && command.words.length === 1 && following !== undefined && !isWordToken(following) && 'op' in following && following.op === ')') {
503
- command = newCommand(false)
504
+ command = newCommand(undefined)
504
505
  index += 1
505
506
  continue
506
507
  }
508
+ const before = result.commands.length
507
509
  folder = finish(command, operator, folder, result)
508
- command = newCommand(operator === '|' || operator === '|&')
510
+ const writer = result.commands.length > before ? result.commands.length - 1 : undefined
511
+ command = newCommand(operator === '|' || operator === '|&' ? { writer } : undefined)
509
512
  if (operator === '(') subshells.push(folder)
510
513
  else if (operator === ')') {
511
514
  const outer = subshells.pop()
@@ -517,8 +520,8 @@ function walk(tokens: ParseEntry[], markers: Marker[], start: string, result: Pa
517
520
  finish(command, ';', folder, result)
518
521
  }
519
522
 
520
- function newCommand(isPipedIn: boolean): Command {
521
- return { words: [], redirects: [], isPipedIn }
523
+ function newCommand(pipe: Pipe | undefined): Command {
524
+ return { words: [], redirects: [], pipe }
522
525
  }
523
526
 
524
527
  function isWordToken(token: ParseEntry): token is string | GlobPattern {
@@ -543,9 +546,13 @@ function finish(command: Command, operator: string, folder: string, result: Pars
543
546
  const [first, ...args] = words
544
547
  if (first === undefined || first.text === 'for' || first.text === 'select') return folder
545
548
  const here = command.redirects.findLast((redirect) => redirect.operator === '<<<')
546
- const stdin: Stdin | undefined = here !== undefined ? { kind: 'text', text: here.target.text } : command.isPipedIn ? { kind: 'pipe' } : undefined
549
+ const stdin: Stdin | undefined = here !== undefined ? { kind: 'text', text: here.target.text } : command.pipe !== undefined ? { kind: 'pipe' } : undefined
550
+ const reader = result.commands.length
547
551
  const changed = addCommand(first, args, folder, stdin, result)
548
- const isPiped = command.isPipedIn || operator === '|' || operator === '|&' || operator === '&'
552
+ const read = result.commands[reader]
553
+ const writer = command.pipe?.writer
554
+ if (read !== undefined && writer !== undefined && here === undefined) result.commands[reader] = { ...read, input: writer }
555
+ const isPiped = command.pipe !== undefined || operator === '|' || operator === '|&' || operator === '&'
549
556
  return changed === undefined || isPiped ? folder : changed
550
557
  }
551
558
 
@@ -23,7 +23,6 @@ export type Fakes = {
23
23
  };
24
24
  config: {
25
25
  list?: Claude['config']['list'];
26
- set?: Claude['config']['set'];
27
26
  };
28
27
  ui: {
29
28
  ask?: Claude['ui']['ask'];
@@ -1 +1 @@
1
- {"version":3,"file":"fake-claude.d.ts","sourceRoot":"","sources":["../src/testing/fake-claude.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fake-claude.d.ts","sourceRoot":"","sources":["../src/testing/fake-claude.ts"],"names":[],"mappings":"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"}
@@ -105,7 +105,6 @@ export function fakeClaude(plugin) {
105
105
  settings: { read: rejected('settings.read', () => fakes.settings.read) },
106
106
  config: {
107
107
  list: rejected('config.list', () => fakes.config.list ?? (async () => [])),
108
- set: rejected('config.set', () => fakes.config.set ?? (async ({ value }) => ({ value }))),
109
108
  },
110
109
  agent: {
111
110
  list: rejected('agent.list', () => fakes.agent.list),
@@ -1,6 +1,7 @@
1
1
  export type ShellCommand = {
2
2
  argv: [string, ...string[]];
3
3
  folder: string;
4
+ input?: number;
4
5
  };
5
6
  export type ParsedShell = {
6
7
  commands: ShellCommand[];
@@ -1 +1 @@
1
- {"version":3,"file":"parse-shell.d.ts","sourceRoot":"","sources":["../src/utils/parse-shell.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1E,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,aAAa,EAAE,OAAO,CAAA;CACvB,CAAA;AA2BD,eAAO,MAAM,cAAc,QAAS,CAAA;AAiKpC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAIpD"}
1
+ {"version":3,"file":"parse-shell.d.ts","sourceRoot":"","sources":["../src/utils/parse-shell.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1F,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,aAAa,EAAE,OAAO,CAAA;CACvB,CAAA;AA4BD,eAAO,MAAM,cAAc,QAAS,CAAA;AAiKpC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAIpD"}
@@ -453,7 +453,7 @@ function readHeredocBodies(source, from, heredocs) {
453
453
  function walk(tokens, markers, start, result) {
454
454
  let folder = start;
455
455
  const subshells = [];
456
- let command = newCommand(false);
456
+ let command = newCommand(undefined);
457
457
  let caseState;
458
458
  for (let index = 0; index < tokens.length; index += 1) {
459
459
  const token = tokens[index];
@@ -492,12 +492,14 @@ function walk(tokens, markers, start, result) {
492
492
  }
493
493
  const following = tokens[index + 1];
494
494
  if (operator === '(' && command.words.length === 1 && following !== undefined && !isWordToken(following) && 'op' in following && following.op === ')') {
495
- command = newCommand(false);
495
+ command = newCommand(undefined);
496
496
  index += 1;
497
497
  continue;
498
498
  }
499
+ const before = result.commands.length;
499
500
  folder = finish(command, operator, folder, result);
500
- command = newCommand(operator === '|' || operator === '|&');
501
+ const writer = result.commands.length > before ? result.commands.length - 1 : undefined;
502
+ command = newCommand(operator === '|' || operator === '|&' ? { writer } : undefined);
501
503
  if (operator === '(')
502
504
  subshells.push(folder);
503
505
  else if (operator === ')') {
@@ -513,8 +515,8 @@ function walk(tokens, markers, start, result) {
513
515
  }
514
516
  finish(command, ';', folder, result);
515
517
  }
516
- function newCommand(isPipedIn) {
517
- return { words: [], redirects: [], isPipedIn };
518
+ function newCommand(pipe) {
519
+ return { words: [], redirects: [], pipe };
518
520
  }
519
521
  function isWordToken(token) {
520
522
  return typeof token === 'string' || ('op' in token && token.op === 'glob');
@@ -539,9 +541,14 @@ function finish(command, operator, folder, result) {
539
541
  if (first === undefined || first.text === 'for' || first.text === 'select')
540
542
  return folder;
541
543
  const here = command.redirects.findLast((redirect) => redirect.operator === '<<<');
542
- const stdin = here !== undefined ? { kind: 'text', text: here.target.text } : command.isPipedIn ? { kind: 'pipe' } : undefined;
544
+ const stdin = here !== undefined ? { kind: 'text', text: here.target.text } : command.pipe !== undefined ? { kind: 'pipe' } : undefined;
545
+ const reader = result.commands.length;
543
546
  const changed = addCommand(first, args, folder, stdin, result);
544
- const isPiped = command.isPipedIn || operator === '|' || operator === '|&' || operator === '&';
547
+ const read = result.commands[reader];
548
+ const writer = command.pipe?.writer;
549
+ if (read !== undefined && writer !== undefined && here === undefined)
550
+ result.commands[reader] = { ...read, input: writer };
551
+ const isPiped = command.pipe !== undefined || operator === '|' || operator === '|&' || operator === '&';
545
552
  return changed === undefined || isPiped ? folder : changed;
546
553
  }
547
554
  function withoutPrefixes(words) {