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
| 1 | import type { Args, On } from 'claude-code' |
| 2 | import { mock } from 'claude-code/testing' |
| 3 | |
| 4 | import Beneath from './beneath' |
| 5 | import { gitIn } from './git-in.js' |
| 6 | import { HINT_DRAWN } from './hint-drawn.js' |
| 7 | import { keeping } from './keeping.js' |
| 8 | import { REPOSITORY } from './repository.js' |
| 9 | import { startsSession } from './starts-session.js' |
| 10 | |
| 11 | /** |
| 12 | * A session in a repository git answers for from a script (REPOSITORY, in |
| 13 | * /work, when none is given), keeping what the plugin does there. |
| 14 | * |
| 15 | * Kept: each git run, ring move, pane opened, left waiting, left unseen or |
| 16 | * closed, status line. The clock starts at 0 and the engine draws the hint. |
| 17 | * |
| 18 | * The rest of the world is the test's (Beneath). Rewriting the script changes |
| 19 | * git's answers. |
| 20 | * |
| 21 | * @param on the test's `on` |
| 22 | * @param script git's output for each invocation whose line holds the key |
| 23 | * @param beneath the store, settings, environment, transcript, whether an |
| 24 | * open is seated, whether a hunks read is refused |
| 25 | * @returns the runs, the ring's moves, the panes opened, left waiting, left |
| 26 | * unseen and closed, the statuses, the clock |
| 27 | */ |
| 28 | export function inRepository( |
| 29 | on: On, |
| 30 | script: Readonly<Record<string, string>> = REPOSITORY, |
| 31 | beneath: Beneath.Beneath = {}, |
| 32 | ) { |
| 33 | const runs: Args<'process.run'>[] = [] |
| 34 | const focused: Args<'ui.focus'>[] = [] |
| 35 | const statuses: (string | undefined)[] = [] |
| 36 | const waiting: Args<'ui.open'>[] = [] |
| 37 | const unseen: Args<'ui.open'>[] = [] |
| 38 | const opened = keeping<Args<'ui.open'>>() |
| 39 | const closed = keeping<Args<'ui.close'>>() |
| 40 | const clock = startsSession(on) |
| 41 | |
| 42 | on('process.run', ($, e) => { |
| 43 | runs.push(e) |
| 44 | |
| 45 | const refusal = e.argv.includes('--raw') ? beneath.hunksRefusal?.() : null |
| 46 | const isRefused = typeof refusal === 'string' |
| 47 | |
| 48 | return isRefused ? { deny: refusal } : { value: gitIn(e.argv, script) } |
| 49 | }) |
| 50 | |
| 51 | on('ui.focus', (_engine, e) => { |
| 52 | focused.push(e) |
| 53 | |
| 54 | return {} |
| 55 | }) |
| 56 | |
| 57 | on('ui.status', ($, e) => { |
| 58 | statuses.push(e.text) |
| 59 | |
| 60 | return { value: undefined } |
| 61 | }) |
| 62 | |
| 63 | on('ui.open', (engine, e) => { |
| 64 | const isWaiting = beneath.isLeftWaiting?.() === true |
| 65 | const isUnseen = beneath.isLeftUnseen?.() === true |
| 66 | |
| 67 | if (isUnseen) { |
| 68 | unseen.push(e) |
| 69 | |
| 70 | return Beneath.LEFT_UNSEEN |
| 71 | } |
| 72 | |
| 73 | if (!isWaiting) { |
| 74 | return opened.hook(engine, e) |
| 75 | } |
| 76 | |
| 77 | waiting.push(e) |
| 78 | |
| 79 | return Beneath.LEFT_WAITING |
| 80 | }) |
| 81 | |
| 82 | on('ui.close', closed.hook) |
| 83 | on('ui.invalidate', () => ({ value: undefined })) |
| 84 | on('ui.render', { component: 'PromptHint' }, () => HINT_DRAWN) |
| 85 | on('session.messages', () => ({ value: [...(beneath.messages?.() ?? [])] })) |
| 86 | on('settings.read', () => ({ value: beneath.settings ?? {} })) |
| 87 | mock.store(on, beneath.stored ?? {}) |
| 88 | mock.env(on, beneath.env ?? {}) |
| 89 | |
| 90 | return { |
| 91 | runs, |
| 92 | focused, |
| 93 | opened: opened.kept, |
| 94 | waiting, |
| 95 | unseen, |
| 96 | closed: closed.kept, |
| 97 | statuses, |
| 98 | clock, |
| 99 | } |
| 100 | } |