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
| @@ -21,7 +21,7 @@ | ||
| 21 | 21 | }) |
| 22 | 22 | |
| 23 | 23 | on('command.run', { command: 'hold' }, async () => { |
| 24 | - await held?.telemetry.log({ event: 'held' }) | |
| 24 | + await held?.telemetry.log({ to: 'collector', event: 'held' }) | |
| 25 | 25 | |
| 26 | 26 | return { text: 'served' } |
| 27 | 27 | }) |
| @@ -1,17 +1,20 @@ | ||
| 1 | 1 | import type { Plugin } from 'claude-code/testing' |
| 2 | 2 | |
| 3 | 3 | /** |
| 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`. | |
| 6 | 7 | */ |
| 7 | 8 | export const meddling: Plugin = { |
| 8 | 9 | name: 'meddling', |
| 9 | 10 | tier: 'user', |
| 10 | 11 | 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 | + ) | |
| 12 | 15 | |
| 13 | 16 | on('command.run', { command: 'meddle' }, $ => |
| 14 | - $.telemetry.log({ event: 'mine' }).then( | |
| 17 | + $.telemetry.log({ to: 'collector', event: 'mine' }).then( | |
| 15 | 18 | () => ({ text: 'served' }), |
| 16 | 19 | (error: unknown) => ({ text: String(error) }), |
| 17 | 20 | ), |
| @@ -17,7 +17,7 @@ | ||
| 17 | 17 | const beneath = await next(e) |
| 18 | 18 | |
| 19 | 19 | during = await beneath.telemetry |
| 20 | - .log({ event: 'during_create' }) | |
| 20 | + .log({ to: 'collector', event: 'during_create' }) | |
| 21 | 21 | .then(() => 'served', String) |
| 22 | 22 | |
| 23 | 23 | return beneath |
| @@ -26,7 +26,7 @@ | ||
| 26 | 26 | on('session.start', ($, e, next) => { |
| 27 | 27 | $.clock.after(1, () => { |
| 28 | 28 | void $.telemetry |
| 29 | - .log({ event: 'from_timer' }) | |
| 29 | + .log({ to: 'collector', event: 'from_timer' }) | |
| 30 | 30 | .then(() => 'served', String) |
| 31 | 31 | .then(outcome => $.ui.log(`timer: ${outcome}`, { to: 'debug' })) |
| 32 | 32 | }) |
| @@ -36,7 +36,7 @@ | ||
| 36 | 36 | |
| 37 | 37 | on('tool.call', { tool: 'Read' }, async ($, e, next) => { |
| 38 | 38 | const outcome = await $.telemetry |
| 39 | - .log({ event: 'from_tool' }) | |
| 39 | + .log({ to: 'collector', event: 'from_tool' }) | |
| 40 | 40 | .then(() => 'served', String) |
| 41 | 41 | |
| 42 | 42 | await $.ui.log(`tool: ${outcome}`, { to: 'debug' }) |
| @@ -16,7 +16,7 @@ | ||
| 16 | 16 | })) |
| 17 | 17 | |
| 18 | 18 | on('command.run', { command: 'replace' }, $ => |
| 19 | - $.telemetry.log({ event: 'mine' }).then( | |
| 19 | + $.telemetry.log({ to: 'collector', event: 'mine' }).then( | |
| 20 | 20 | () => ({ text: 'served' }), |
| 21 | 21 | (error: unknown) => ({ text: String(error) }), |
| 22 | 22 | ), |
| @@ -2,12 +2,13 @@ | ||
| 2 | 2 | |
| 3 | 3 | /** |
| 4 | 4 | * 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. | |
| 6 | 7 | */ |
| 7 | 8 | export const swallowing: Plugin = { |
| 8 | 9 | name: 'swallowing', |
| 9 | 10 | tier: 'user', |
| 10 | 11 | register(on) { |
| 11 | - on('telemetry.log', () => ({ value: undefined })) | |
| 12 | + on('telemetry.log', { to: 'collector' }, () => ({ value: undefined })) | |
| 12 | 13 | }, |
| 13 | 14 | } |
| @@ -1,18 +1,21 @@ | ||
| 1 | 1 | import type { Plugin } from 'claude-code/testing' |
| 2 | 2 | |
| 3 | 3 | /** |
| 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. | |
| 6 | 7 | */ |
| 7 | 8 | export const visiting: Plugin = { |
| 8 | 9 | name: 'visiting', |
| 9 | 10 | tier: 'user', |
| 10 | 11 | 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( | |
| 13 | 16 | () => ({ text: 'queued' }), |
| 14 | 17 | (error: unknown) => ({ text: String(error) }), |
| 15 | - ), | |
| 16 | - ) | |
| 18 | + ) | |
| 19 | + }) | |
| 17 | 20 | }, |
| 18 | 21 | } |
| @@ -61,7 +61,7 @@ | ||
| 61 | 61 | ) |
| 62 | 62 | |
| 63 | 63 | 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", | |
| 65 | 65 | { plugins: [Fixtures.recording, Fixtures.meddling] }, |
| 66 | 66 | async ($, on) => { |
| 67 | 67 | mock.env(on, Fixtures.SENDING_ENV) |
| @@ -77,13 +77,13 @@ | ||
| 77 | 77 | rows: Fixtures.rowsOf(session).map(Fixtures.eventNameOf), |
| 78 | 78 | }).toEqual({ |
| 79 | 79 | own: `HooksError: meddling: $.telemetry.log: ${Hooks.REFUSED.deny}`, |
| 80 | - rows: ['tengu_plugin_renamed'], | |
| 80 | + rows: ['tengu_plugin_survey_answered'], | |
| 81 | 81 | }) |
| 82 | 82 | }, |
| 83 | 83 | ) |
| 84 | 84 | |
| 85 | 85 | 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", | |
| 87 | 87 | { plugins: [Fixtures.recording, Fixtures.swallowing] }, |
| 88 | 88 | async ($, on) => { |
| 89 | 89 | mock.env(on, Fixtures.SENDING_ENV) |
| @@ -96,9 +96,14 @@ | ||
| 96 | 96 | |
| 97 | 97 | await session.clock.advance(Hooks.BATCH_WINDOW_MS) |
| 98 | 98 | |
| 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({ | |
| 100 | 104 | answer: 'queued', |
| 101 | - posts: [], | |
| 105 | + posts: 1, | |
| 106 | + rows: ['tengu_plugin_survey_answered'], | |
| 102 | 107 | }) |
| 103 | 108 | }, |
| 104 | 109 | ) |