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

telemetry, agents-md: the test plugins hook and call the collector stream by name, and stand in for telemetry through its events (#96930)Browse files
* telemetry: the test plugins a person installed hook and call the collector's stream by name

* agents-md: the test plugin standing in for telemetry hooks its two events, adding the noun only where the engine has none
Alice T'Poteat authored 28 days ago · GitHub committed 28 days ago parent 163ae3a · commit e1bb7b065bc2
8 changed files+56 -35compared with 163ae3a
Mmods/agents-md/tests/fixtures/recording.ts
Mmods/telemetry/tests/fixtures/plugins/holding.ts
Mmods/telemetry/tests/fixtures/plugins/meddling.ts
Mmods/telemetry/tests/fixtures/plugins/reaching.ts
Mmods/telemetry/tests/fixtures/plugins/replacing.ts
Mmods/telemetry/tests/fixtures/plugins/swallowing.ts
Mmods/telemetry/tests/fixtures/plugins/visiting.ts
Mmods/telemetry/tests/gate.test.ts
@@ -1,29 +1,38 @@
11 import type { Plugin } from 'claude-code/testing'
22
33 /**
4- * A plugin standing in for the telemetry built-in: it adds the `telemetry`
5- * noun at `engine.create` over the nouns beneath.
4+ * A plugin standing in for the telemetry built-in: it hooks `telemetry.log`
5+ * and `telemetry.mark` and answers them without going on.
66 *
7- * Its `log` and `mark` write the entry they are handed as one `$.ui.log`
8- * line, `<op> <json>`, which startedOf keeps and rowsOf reads back.
7+ * Each writes the entry as the caller handed it (`to` left off) as one
8+ * `$.ui.log` line, `<op> <json>`, which startedOf keeps and rowsOf reads
9+ * back. Where the engine has no `telemetry` its engine.create step adds one.
910 */
1011 export const RECORDING: Plugin = {
1112 name: 'recording',
1213 tier: 'builtin',
1314 register(on) {
15+ on('telemetry.log', async ($, e) => {
16+ const { to: _to, ...entry } = e
17+
18+ await $.ui.log(`log ${JSON.stringify(entry)}`)
19+
20+ return { value: undefined }
21+ })
22+
23+ on('telemetry.mark', async ($, e) => {
24+ await $.ui.log(`mark ${JSON.stringify(e)}`)
25+
26+ return { value: undefined }
27+ })
28+
1429 on('engine.create', async ($, e, next) => {
1530 const beneath = await next(e)
16- const built = { ...beneath }
17- built.telemetry = {
18- log: async entry => {
19- await beneath.ui.log(`log ${JSON.stringify(entry)}`)
20- },
21- mark: async entry => {
22- await beneath.ui.log(`mark ${JSON.stringify(entry)}`)
23- },
31+ const added = {
32+ telemetry: { log: async () => undefined, mark: async () => undefined },
2433 }
2534
26- return built
35+ return { ...added, ...beneath }
2736 })
2837 },
2938 }
@@ -21,7 +21,7 @@
2121 })
2222
2323 on('command.run', { command: 'hold' }, async () => {
24- await held?.telemetry.log({ event: 'held' })
24+ await held?.telemetry.log({ to: 'collector', event: 'held' })
2525
2626 return { text: 'served' }
2727 })
@@ -1,17 +1,20 @@
11 import type { Plugin } from 'claude-code/testing'
22
33 /**
4- * A plugin a person installed that hooks `telemetry.log` above the gate to
5- * rename every row, and logs one of its own through `/meddle`.
4+ * A plugin a person installed that hooks `telemetry.log` above the gate for
5+ * the one stream it may, the collector's, to rename every record there, and
6+ * logs one of its own through `/meddle`.
67 */
78 export const meddling: Plugin = {
89 name: 'meddling',
910 tier: 'user',
1011 register(on) {
11- on('telemetry.log', (_$, e, next) => next({ ...e, event: 'renamed' }))
12+ on('telemetry.log', { to: 'collector' }, (_$, e, next) =>
13+ next({ ...e, event: 'renamed' }),
14+ )
1215
1316 on('command.run', { command: 'meddle' }, $ =>
14- $.telemetry.log({ event: 'mine' }).then(
17+ $.telemetry.log({ to: 'collector', event: 'mine' }).then(
1518 () => ({ text: 'served' }),
1619 (error: unknown) => ({ text: String(error) }),
1720 ),
@@ -17,7 +17,7 @@
1717 const beneath = await next(e)
1818
1919 during = await beneath.telemetry
20- .log({ event: 'during_create' })
20+ .log({ to: 'collector', event: 'during_create' })
2121 .then(() => 'served', String)
2222
2323 return beneath
@@ -26,7 +26,7 @@
2626 on('session.start', ($, e, next) => {
2727 $.clock.after(1, () => {
2828 void $.telemetry
29- .log({ event: 'from_timer' })
29+ .log({ to: 'collector', event: 'from_timer' })
3030 .then(() => 'served', String)
3131 .then(outcome => $.ui.log(`timer: ${outcome}`, { to: 'debug' }))
3232 })
@@ -36,7 +36,7 @@
3636
3737 on('tool.call', { tool: 'Read' }, async ($, e, next) => {
3838 const outcome = await $.telemetry
39- .log({ event: 'from_tool' })
39+ .log({ to: 'collector', event: 'from_tool' })
4040 .then(() => 'served', String)
4141
4242 await $.ui.log(`tool: ${outcome}`, { to: 'debug' })
@@ -16,7 +16,7 @@
1616 }))
1717
1818 on('command.run', { command: 'replace' }, $ =>
19- $.telemetry.log({ event: 'mine' }).then(
19+ $.telemetry.log({ to: 'collector', event: 'mine' }).then(
2020 () => ({ text: 'served' }),
2121 (error: unknown) => ({ text: String(error) }),
2222 ),
@@ -2,12 +2,13 @@
22
33 /**
44 * A plugin a person installed that answers `telemetry.log` above the gate
5- * without going on beneath: the rows of everyone beneath it stop there.
5+ * without going on beneath, for the one stream it may hook, the
6+ * collector's: the records of everyone beneath it stop there.
67 */
78 export const swallowing: Plugin = {
89 name: 'swallowing',
910 tier: 'user',
1011 register(on) {
11- on('telemetry.log', () => ({ value: undefined }))
12+ on('telemetry.log', { to: 'collector' }, () => ({ value: undefined }))
1213 },
1314 }
@@ -1,18 +1,21 @@
11 import type { Plugin } from 'claude-code/testing'
22
33 /**
4- * A plugin a person installed whose `/visit <entry>` logs the entry through
5- * `$.telemetry`, answering "queued", or why the row was refused.
4+ * A plugin a person installed whose `/visit <entry>` logs the entry's event
5+ * and properties through `$.telemetry`, naming the one stream it may, the
6+ * collector's; it answers "queued", or why the call was refused.
67 */
78 export const visiting: Plugin = {
89 name: 'visiting',
910 tier: 'user',
1011 register(on) {
11- on('command.run', { command: 'visit' }, ($, e) =>
12- $.telemetry.log(JSON.parse(e.args)).then(
12+ on('command.run', { command: 'visit' }, ($, e) => {
13+ const { event, props } = JSON.parse(e.args)
14+
15+ return $.telemetry.log({ to: 'collector', event, props }).then(
1316 () => ({ text: 'queued' }),
1417 (error: unknown) => ({ text: String(error) }),
15- ),
16- )
18+ )
19+ })
1720 },
1821 }
@@ -61,7 +61,7 @@
6161 )
6262
6363 test(
64- 'a hook above may rename a row a built-in sends, never send its own',
64+ "a hook above on the collector's stream renames no row, sends none",
6565 { plugins: [Fixtures.recording, Fixtures.meddling] },
6666 async ($, on) => {
6767 mock.env(on, Fixtures.SENDING_ENV)
@@ -77,13 +77,13 @@
7777 rows: Fixtures.rowsOf(session).map(Fixtures.eventNameOf),
7878 }).toEqual({
7979 own: `HooksError: meddling: $.telemetry.log: ${Hooks.REFUSED.deny}`,
80- rows: ['tengu_plugin_renamed'],
80+ rows: ['tengu_plugin_survey_answered'],
8181 })
8282 },
8383 )
8484
8585 test(
86- 'a hook above that answers without going on sends nothing at all',
86+ "a hook above that swallows the collector's stream stops no row",
8787 { plugins: [Fixtures.recording, Fixtures.swallowing] },
8888 async ($, on) => {
8989 mock.env(on, Fixtures.SENDING_ENV)
@@ -96,9 +96,14 @@
9696
9797 await session.clock.advance(Hooks.BATCH_WINDOW_MS)
9898
99- expect({ answer, posts: session.posts }).toEqual({
99+ expect({
100+ answer,
101+ posts: session.posts.length,
102+ rows: Fixtures.rowsOf(session).map(Fixtures.eventNameOf),
103+ }).toEqual({
100104 answer: 'queued',
101- posts: [],
105+ posts: 1,
106+ rows: ['tengu_plugin_survey_answered'],
102107 })
103108 },
104109 )