spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

mods: each mod's tests run from the engine's seat, $ the engine's own and the hooks on() registers beneath the modBrowse files
poteat authored on Sep 11, 2026 parent f6cd2ca · commit 38b8cf3a3f64
5 changed files+677 -0compared with f6cd2ca
Amods/diff/tests/boot-probe.test.ts
Amods/diff/tests/command.test.ts
Amods/diff/tests/pane.test.ts
Amods/sec-default/tests/sec-default.test.ts
Amods/telemetry/tests/telemetry.test.ts
@@ -1,0 +1,58 @@
1+import type {
2+ CommandRunInput,
3+ ProcessRunResult,
4+ SessionStartInput,
5+} from 'claude-code'
6+import { clock, expect, seat, test } from 'claude-code/testing'
7+
8+seat('builtin')
9+
10+const SESSION: SessionStartInput = {
11+ surface: 'terminal',
12+ isInteractive: true,
13+ cwd: '/work',
14+}
15+const DIFF: CommandRunInput = {
16+ command: 'diff',
17+ args: '',
18+ origin: { kind: 'composer' },
19+}
20+const NOT_A_REPOSITORY: ProcessRunResult = {
21+ exitCode: 128,
22+ stdout: '',
23+ stderr: 'fatal: not a git repository',
24+}
25+const GIT_TIMEOUT_MS = 5000
26+const GIT_HUNG = `git aborted: still running after ${GIT_TIMEOUT_MS}ms`
27+
28+test('/diff at boot joins the boot probe, then asks again', async ($, on) => {
29+ const probes: (readonly string[])[] = []
30+ on('session.start', ($, e) => ({ cwd: e.cwd }))
31+ on('command.register', ($, e) => ({ value: { command: e.name } }))
32+ on('process.run', async ($, e) => {
33+ probes.push(e.argv)
34+
35+ if (probes.length === 1) {
36+ await clock.sleep(GIT_TIMEOUT_MS)
37+
38+ return { deny: GIT_HUNG }
39+ }
40+
41+ return { value: NOT_A_REPOSITORY }
42+ })
43+
44+ const booting = $.session.start(SESSION)
45+ await clock.advance(0)
46+ const ran = $.command.run(DIFF)
47+ await clock.advance(0)
48+
49+ expect(probes, 'the boot’s probe, which /diff joined').toHaveLength(1)
50+
51+ await clock.advance(GIT_TIMEOUT_MS)
52+ await booting
53+
54+ expect(await ran).toEqual({
55+ text: expect.stringContaining('isn’t in a git repository'),
56+ })
57+ expect(probes, 'then one more of its own').toHaveLength(2)
58+})
@@ -1,0 +1,88 @@
1+import type {
2+ CommandRunInput,
3+ ProcessRunResult,
4+ SessionStartInput,
5+} from 'claude-code'
6+import { expect, seat, test } from 'claude-code/testing'
7+
8+seat('builtin')
9+
10+const SESSION: SessionStartInput = {
11+ surface: 'terminal',
12+ isInteractive: true,
13+ cwd: '/work',
14+}
15+const DIFF: CommandRunInput = {
16+ command: 'diff',
17+ args: '',
18+ origin: { kind: 'composer' },
19+}
20+const NOT_A_REPOSITORY: ProcessRunResult = {
21+ exitCode: 128,
22+ stdout: '',
23+ stderr: 'fatal: not a git repository',
24+}
25+const BUILTIN_HOLDS = '"/diff" refused: it is the built-in /diff'
26+
27+test('outside a git repository /diff says so and opens nothing', async ($, on) => {
28+ const opened: string[] = []
29+ on('session.start', ($, e) => ({ cwd: e.cwd }))
30+ on('command.register', ($, e) => ({ value: { command: e.name } }))
31+ on('process.run', () => ({ value: NOT_A_REPOSITORY }))
32+ on('ui.open', ($, e, next) => {
33+ opened.push(e.id)
34+ return next(e)
35+ })
36+
37+ await $.session.start(SESSION)
38+ const { text } = await $.command.run(DIFF)
39+
40+ expect(text).toContain('isn’t in a git repository')
41+ expect(opened).toEqual([])
42+})
43+
44+test('a git that never answers is not “no repository”', async ($, on) => {
45+ on('session.start', ($, e) => ({ cwd: e.cwd }))
46+ on('command.register', ($, e) => ({ value: { command: e.name } }))
47+ on('process.run', () => ({
48+ deny: 'git aborted: still running after 5000ms',
49+ }))
50+
51+ await $.session.start(SESSION)
52+ const { text } = await $.command.run(DIFF)
53+
54+ expect(text).toContain('git didn’t answer')
55+})
56+
57+test('with the built-in holding /diff, the plugin stands down', async ($, on) => {
58+ const logged: string[] = []
59+ on('session.start', ($, e) => ({ cwd: e.cwd }))
60+ on('command.register', () => ({ deny: BUILTIN_HOLDS }))
61+ on('command.run', () => ({ text: 'the built-in /diff ran' }))
62+ on('ui.log', ($, e) => {
63+ logged.push(e.text)
64+ return { value: undefined }
65+ })
66+
67+ await $.session.start(SESSION)
68+
69+ expect(await $.command.run(DIFF)).toEqual({ text: 'the built-in /diff ran' })
70+ expect(logged).toEqual([])
71+})
72+
73+test('a refusal the built-in did not cause is said aloud', async ($, on) => {
74+ const logged: string[] = []
75+ on('session.start', ($, e) => ({ cwd: e.cwd }))
76+ on('command.register', () => ({ deny: '32 commands are registered already' }))
77+ on('ui.log', ($, e) => {
78+ logged.push(e.text)
79+ return { value: undefined }
80+ })
81+
82+ await $.session.start(SESSION)
83+
84+ expect(logged).toEqual([
85+ 'could not register /diff: diff: $.command.register: 32 commands are ' +
86+ 'registered already; the diff panel is unavailable this session',
87+ ])
88+})
@@ -1,0 +1,177 @@
1+import type {
2+ Args,
3+ CommandRunInput,
4+ On,
5+ ProcessRunResult,
6+ RenderElement,
7+ RenderInput,
8+ SessionStartInput,
9+} from 'claude-code'
10+import {
11+ clock,
12+ expect,
13+ memoryStore,
14+ seat,
15+ test,
16+ textOf,
17+} from 'claude-code/testing'
18+
19+seat('builtin')
20+
21+const SESSION: SessionStartInput = {
22+ surface: 'terminal',
23+ isInteractive: true,
24+ cwd: '/work',
25+}
26+const DIFF: CommandRunInput = {
27+ command: 'diff',
28+ args: '',
29+ origin: { kind: 'composer' },
30+}
31+const CLEAR: CommandRunInput = {
32+ command: 'clear',
33+ args: '',
34+ origin: { kind: 'composer' },
35+}
36+const PANE: RenderInput<'Pane'> = {
37+ component: 'Pane',
38+ surface: 'terminal',
39+ requestId: 'diff',
40+ viewport: { columns: 160, rows: 40 },
41+ props: {
42+ title: 'Diff',
43+ isFocused: false,
44+ bodyColumns: 80,
45+ placement: 'dock',
46+ scroll: { offset: 0, bodyRows: 30 },
47+ },
48+}
49+const HINT: RenderInput<'PromptHint'> = {
50+ component: 'PromptHint',
51+ surface: 'terminal',
52+ requestId: 'hint',
53+ viewport: { columns: 160, rows: 40 },
54+ props: { isDraft: false, isWorking: false, hint: '' },
55+}
56+const HINT_DRAWN: RenderElement = {
57+ type: 'Text',
58+ children: ['? for shortcuts'],
59+}
60+
61+/**
62+ * A repository at /work with one changed file: an invocation of git whose
63+ * command line holds a key answers that output, and any other fails as git
64+ * does outside a repository.
65+ */
66+const REPOSITORY: Readonly<Record<string, string>> = {
67+ 'rev-parse --path-format=absolute': '/work\n/work/.git\n/work/.git\n',
68+ 'HEAD --shortstat': ' 1 file changed, 1 insertion(+)',
69+ 'HEAD --numstat': '1\t0\tapp.ts\0',
70+ 'ls-files': '',
71+ '-- app.ts': '@@ -1 +1 @@\n-const a = 1\n+const a = 2\n',
72+}
73+
74+function gitIn(argv: readonly string[]): ProcessRunResult {
75+ const line = argv.join(' ')
76+ const found = Object.entries(REPOSITORY).find(([key]) => line.includes(key))
77+
78+ return found
79+ ? { exitCode: 0, stdout: found[1], stderr: '' }
80+ : { exitCode: 128, stdout: '', stderr: 'fatal: not a git repository' }
81+}
82+
83+/**
84+ * A session in that repository: /diff registers, git answers, the store
85+ * starts empty, the engine draws the prompt's hint, and what the plugin
86+ * asks of the world (each git run, each pane opened or closed) is kept.
87+ */
88+function inRepository(on: On) {
89+ const runs: Args<'process.run'>[] = []
90+ const opened: Args<'ui.open'>[] = []
91+ const closed: Args<'ui.close'>[] = []
92+ on('session.start', ($, e) => ({ cwd: e.cwd }))
93+ on('command.register', ($, e) => ({ value: { command: e.name } }))
94+ on('process.run', ($, e) => {
95+ runs.push(e)
96+ return { value: gitIn(e.argv) }
97+ })
98+ on('ui.open', ($, e) => {
99+ opened.push(e)
100+ return { value: undefined }
101+ })
102+ on('ui.close', ($, e) => {
103+ closed.push(e)
104+ return { value: undefined }
105+ })
106+ on('ui.invalidate', () => ({ value: undefined }))
107+ on('ui.render', { component: 'PromptHint' }, () => HINT_DRAWN)
108+ on('session.messages', () => ({ value: [] }))
109+ memoryStore(on)
110+
111+ return { runs, opened, closed }
112+}
113+
114+test('/diff opens the pane over the session’s changes', async ($, on) => {
115+ const world = inRepository(on)
116+
117+ await $.session.start(SESSION)
118+
119+ expect(await $.command.run(DIFF)).toEqual({})
120+ expect(world.opened.map(pane => pane.id)).toEqual(['diff'])
121+
122+ await clock.advance(1000)
123+ const drawn = textOf(await $.ui.render(PANE))
124+
125+ expect(drawn).toContain('1 file changed')
126+ expect(drawn).toContain('app.ts')
127+})
128+
129+test('every git child is pinned to the repository and reads the C locale', async ($, on) => {
130+ const world = inRepository(on)
131+
132+ await $.session.start(SESSION)
133+ await $.command.run(DIFF)
134+ await clock.advance(1000)
135+ const [discovery, ...pinned] = world.runs
136+
137+ expect(discovery?.argv).toContain('--show-toplevel')
138+ expect(discovery?.init?.cwd).toBeUndefined()
139+ expect(pinned.length).toBeGreaterThan(0)
140+
141+ for (const run of pinned) {
142+ expect(run.argv.slice(0, 3)).toEqual([
143+ 'git',
144+ '--git-dir=/work/.git',
145+ '--work-tree=/work',
146+ ])
147+ expect(run.init).toMatchObject({ cwd: '/work', env: { LC_ALL: 'C' } })
148+ }
149+})
150+
151+test('Claude’s first edit opens the pane on a wide terminal', async ($, on) => {
152+ const world = inRepository(on)
153+ on('tool.call', () => ({ result: 'edited' }))
154+
155+ await $.session.start(SESSION)
156+ await $.ui.render(HINT)
157+ await $.tool.call({
158+ tool: 'Edit',
159+ file_path: '/work/app.ts',
160+ old_string: '1',
161+ new_string: '2',
162+ })
163+ await clock.advance(1000)
164+
165+ expect(world.opened.map(pane => pane.id)).toEqual(['diff'])
166+})
167+
168+test('/clear closes the pane it finds open', async ($, on) => {
169+ const world = inRepository(on)
170+ on('command.run', { command: 'clear' }, () => ({}))
171+
172+ await $.session.start(SESSION)
173+ await $.command.run(DIFF)
174+ await $.command.run(CLEAR)
175+
176+ expect(world.closed.map(pane => pane.id)).toEqual(['diff'])
177+})
@@ -1,0 +1,196 @@
1+import type {
2+ CommandRunInput,
3+ On,
4+ PromptSectionInput,
5+ SessionStartInput,
6+ Settings,
7+ ToolInfo,
8+} from 'claude-code'
9+import type { Plugin } from 'claude-code/testing'
10+import { expect, seat, test } from 'claude-code/testing'
11+
12+seat('prepend')
13+
14+const SESSION: SessionStartInput = {
15+ surface: 'terminal',
16+ isInteractive: true,
17+ cwd: '/work',
18+}
19+const TOOLS_COMMAND: CommandRunInput = {
20+ command: 'tools',
21+ args: '',
22+ origin: { kind: 'composer' },
23+}
24+const ALLOWLIST: Settings = { allowedMcpServers: [{ serverName: 'corp' }] }
25+const NO_ALLOWLIST: Settings = { permissions: { allow: [] } }
26+const MEMORY: PromptSectionInput = { name: 'memory', text: 'the org says hi' }
27+const TOOLS: ToolInfo[] = [
28+ {
29+ name: 'mcp__corp__search',
30+ description: 'Searches the corp wiki.',
31+ mcp: true,
32+ },
33+ { name: 'Bash', description: 'Runs a command.', mcp: false },
34+]
35+
36+/**
37+ * A plugin that registers a tool when the session starts, in the tier
38+ * given: the person's own by default.
39+ */
40+const registering = (name: string, tier?: Plugin['tier']): Plugin => ({
41+ name,
42+ tier,
43+ register(on) {
44+ on('session.start', async ($, e, next) => {
45+ await $.tool.register({
46+ name: 'greet',
47+ description: 'Says hello.',
48+ inputSchema: { type: 'object' },
49+ })
50+
51+ return next(e)
52+ })
53+ },
54+})
55+
56+/**
57+ * A session starting, where each tool a plugin asks for is registered and
58+ * kept by the name of the plugin that asked.
59+ */
60+function toolsRegistered(on: On) {
61+ const registered: string[] = []
62+ on('session.start', ($, e) => ({ cwd: e.cwd }))
63+ on('tool.register', ($, e, next) => {
64+ registered.push(next.origin.plugin)
65+
66+ return { value: { tool: `mcp__plugin_${next.origin.plugin}__${e.name}` } }
67+ })
68+
69+ return registered
70+}
71+
72+/**
73+ * A plugin the person installed that hides the organization's tools from
74+ * the listing and relabels every other one.
75+ */
76+const relabeling: Plugin = {
77+ name: 'relabeling',
78+ register(on) {
79+ on('tool.list', async ($, e, next) => {
80+ const listed = await next(e)
81+
82+ return listed.value === undefined
83+ ? listed
84+ : {
85+ value: listed.value
86+ .filter(tool => !tool.name.startsWith('mcp__corp__'))
87+ .map(tool => ({ ...tool, description: 'relabeled' })),
88+ }
89+ })
90+ },
91+}
92+
93+/**
94+ * A plugin whose `/tools` answers the tool listing it reads, one
95+ * `name: description` line per tool.
96+ */
97+const listing: Plugin = {
98+ name: 'listing',
99+ register(on) {
100+ on('command.run', { command: 'tools' }, async $ => ({
101+ text: (await $.tool.list())
102+ .map(tool => `${tool.name}: ${tool.description}`)
103+ .join('\n'),
104+ }))
105+ },
106+}
107+
108+/**
109+ * A plugin the person installed that drops the memory section.
110+ */
111+const dropping: Plugin = {
112+ name: 'dropping',
113+ register(on) {
114+ on('prompt.section', () => ({ text: null }))
115+ },
116+}
117+
118+/**
119+ * The organization's own plugin, seated last, which signs the section.
120+ */
121+const signing: Plugin = {
122+ name: 'signing',
123+ tier: 'append',
124+ register(on) {
125+ on('prompt.section', ($, e, next) =>
126+ next({ ...e, text: `${e.text} (signed)` }),
127+ )
128+ },
129+}
130+
131+test(
132+ 'under an MCP allowlist a plugin the person installed may not add a tool',
133+ { plugins: [registering('mine'), registering('bundled', 'builtin')] },
134+ async ($, on) => {
135+ on('settings.read', () => ({ value: ALLOWLIST }))
136+ const registered = toolsRegistered(on)
137+
138+ await $.session.start(SESSION)
139+
140+ expect(registered).toEqual(['bundled'])
141+ },
142+)
143+
144+test(
145+ 'with no allowlist, the person’s plugin adds its tool',
146+ { plugins: [registering('mine')] },
147+ async ($, on) => {
148+ on('settings.read', () => ({ value: NO_ALLOWLIST }))
149+ const registered = toolsRegistered(on)
150+
151+ await $.session.start(SESSION)
152+
153+ expect(registered).toEqual(['mine'])
154+ },
155+)
156+
157+test(
158+ 'a policy that cannot be read counts as one in force',
159+ { plugins: [registering('mine')] },
160+ async ($, on) => {
161+ on('settings.read', () => ({ deny: 'managed settings unreadable' }))
162+ const registered = toolsRegistered(on)
163+
164+ await $.session.start(SESSION)
165+
166+ expect(registered).toEqual([])
167+ },
168+)
169+
170+test(
171+ 'the organization’s tools are listed as its tiers listed them',
172+ { plugins: [relabeling, listing] },
173+ async ($, on) => {
174+ on('settings.read', () => ({ value: ALLOWLIST }))
175+ on('tool.list', () => ({ value: TOOLS }))
176+
177+ const { text } = await $.command.run(TOOLS_COMMAND)
178+
179+ expect(text?.split('\n')).toEqual([
180+ 'mcp__corp__search: Searches the corp wiki.',
181+ 'Bash: relabeled',
182+ ])
183+ },
184+)
185+
186+test(
187+ 'a prompt section continues past the person’s plugins, not the organization’s',
188+ { plugins: [dropping, signing] },
189+ async ($, on) => {
190+ on('prompt.section', ($, e) => ({ text: e.text }))
191+
192+ expect(await $.prompt.section(MEMORY)).toEqual({
193+ text: 'the org says hi (signed)',
194+ })
195+ },
196+)
@@ -1,0 +1,158 @@
1+import type {
2+ Args,
3+ CommandRunInput,
4+ HttpResponse,
5+ On,
6+ SessionAuthorization,
7+} from 'claude-code'
8+import type { Plugin } from 'claude-code/testing'
9+import { expect, memoryEnv, seat, test } from 'claude-code/testing'
10+
11+seat('builtin')
12+
13+const SURVEY: CommandRunInput = {
14+ command: 'survey',
15+ args: '',
16+ origin: { kind: 'composer' },
17+}
18+const SHOWN: CommandRunInput = {
19+ command: 'shown',
20+ args: '',
21+ origin: { kind: 'composer' },
22+}
23+const BEARER: SessionAuthorization = { handle: 'the-handle', kind: 'bearer' }
24+const ACCEPTED: HttpResponse = { status: 200, ok: true, headers: {}, text: '' }
25+
26+/**
27+ * A plugin that records through `$.telemetry`: `/survey` logs an answered
28+ * survey, `/shown` a row under the built-in panel's own name; each answers
29+ * "sent", or why the row was refused.
30+ */
31+const recording: Plugin = {
32+ name: 'recording',
33+ register(on) {
34+ on('command.run', { command: 'survey' }, $ =>
35+ $.telemetry
36+ .log({ event: 'survey_answered', props: { answer: 2, seen: true } })
37+ .then(
38+ () => ({ text: 'sent' }),
39+ (error: unknown) => ({ text: String(error) }),
40+ ),
41+ )
42+ on('command.run', { command: 'shown' }, $ =>
43+ $.telemetry
44+ .log({ event: 'tengu_repl_diff_panel_shown', props: { seen: true } })
45+ .then(
46+ () => ({ text: 'sent' }),
47+ (error: unknown) => ({ text: String(error) }),
48+ ),
49+ )
50+ },
51+}
52+
53+/**
54+ * A first-party session: its id and model, the credential it holds, and
55+ * the ingest accepting each row, which is kept as it was posted.
56+ */
57+function firstPartySession(
58+ on: On,
59+ authorization: SessionAuthorization = BEARER,
60+) {
61+ const posts: Args<'http.fetch'>[] = []
62+ on('session.id', () => ({ value: 'the-session' }))
63+ on('session.model', () => ({ value: 'the-model' }))
64+ on('session.authorize', () => ({ value: authorization }))
65+ on('http.fetch', ($, e) => {
66+ posts.push(e)
67+
68+ return { value: ACCEPTED }
69+ })
70+
71+ return posts
72+}
73+
74+/**
75+ * The one row a post to the ingest carries.
76+ */
77+function rowOf(post: Args<'http.fetch'> | undefined) {
78+ const batch: { events: { event_data: Readonly<Record<string, unknown>> }[] } =
79+ JSON.parse(String(post?.init?.body))
80+
81+ return batch.events[0]?.event_data
82+}
83+
84+test(
85+ 'a plugin’s $.telemetry.log posts one first-party row',
86+ { plugins: [recording] },
87+ async ($, on) => {
88+ memoryEnv(on, { USER_TYPE: 'ant' })
89+ const posts = firstPartySession(on)
90+
91+ expect(await $.command.run(SURVEY)).toEqual({ text: 'sent' })
92+ expect(posts).toHaveLength(1)
93+ expect(posts[0]?.init).toMatchObject({ method: 'POST', auth: 'the-handle' })
94+ expect(rowOf(posts[0])).toMatchObject({
95+ event_name: 'tengu_plugin_survey_answered',
96+ session_id: 'the-session',
97+ model: 'the-model',
98+ user_type: 'ant',
99+ })
100+ },
101+)
102+
103+test(
104+ 'a row already named tengu_ is sent under its own name',
105+ { plugins: [recording] },
106+ async ($, on) => {
107+ memoryEnv(on, {})
108+ const posts = firstPartySession(on)
109+
110+ await $.command.run(SHOWN)
111+
112+ expect(rowOf(posts[0])).toMatchObject({
113+ event_name: 'tengu_repl_diff_panel_shown',
114+ user_type: 'external',
115+ })
116+ },
117+)
118+
119+test(
120+ 'nothing is sent for a person who asked not to be tracked',
121+ { plugins: [recording] },
122+ async ($, on) => {
123+ memoryEnv(on, { DO_NOT_TRACK: '1' })
124+ const posts = firstPartySession(on)
125+
126+ expect(await $.command.run(SURVEY)).toEqual({ text: 'sent' })
127+ expect(posts).toEqual([])
128+ },
129+)
130+
131+test(
132+ 'nothing is sent on a third-party provider',
133+ { plugins: [recording] },
134+ async ($, on) => {
135+ memoryEnv(on, { CLAUDE_CODE_USE_BEDROCK: '1' })
136+ const posts = firstPartySession(on)
137+
138+ await $.command.run(SURVEY)
139+
140+ expect(posts).toEqual([])
141+ },
142+)
143+
144+test(
145+ 'a session with no first-party credential is refused, nothing sent',
146+ { plugins: [recording] },
147+ async ($, on) => {
148+ memoryEnv(on, {})
149+ const posts = firstPartySession(on, null)
150+ const { text } = await $.command.run(SURVEY)
151+
152+ expect(text).toEndWith(
153+ '$.telemetry.log: this session has no first-party credential to ' +
154+ 'authorize',
155+ )
156+ expect(posts).toEqual([])
157+ },
158+)