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

claude-code / mods / sec-default / hooks / register.ts
133 lines · 4.2 KBRaw
1import type { On } from 'claude-code'
2
3import Caught from './caught'
4import HeldVerdict from './held-verdict'
5import { managedModsOnlyRefusal } from './managed-mods-only-refusal'
6import PastUsers from './past-users'
7import Policy from './policy'
8import { quietly } from './quietly'
9import { toolRegistered } from './tool-registered'
10import { toolsListed } from './tools-listed'
11
12/**
13 * The built-in's hooks, seated outermost: each keeps one control an
14 * organization has today out of reach of the plugins a person installs.
15 *
16 * Three moves: continue past the user tier (`next.to(e, "append")`), refuse
17 * a user-tier caller or module by name, or pass. Provenance is the event's
18 * pinned `provider` or `tier`; policy is `$.settings.read`; fail closed.
19 *
20 * @param on the engine's registrar
21 */
22export function register(on: On) {
23 const readPolicy = Policy.createPolicyMemo(Policy.POLICY_MEMO_MS)
24 const told = new Set<string>()
25 const toldOfCeiling = new Set<string>()
26
27 on('classic.*', ($, e, next) => next.to(e, 'append')).catch(($, e, next) =>
28 Caught.pastUsersCaught(e, next),
29 )
30
31 on('prompt.section', ($, e, next) => next.to(e, 'append'))
32 on('prompt.context', ($, e, next) => next.to(e, 'append'))
33 on('prompt.compose', ($, e, next) => next.to(e, 'append'))
34 on('skill.prompt', ($, e, next) => next.to(e, 'append'))
35 on('attribution.text', ($, e, next) => next.to(e, 'append'))
36
37 on('settings.read', ($, e, next) => next.to(e, 'append')).catch(
38 ($, e, next) => Caught.pastUsersCaught(e, next),
39 )
40
41 on('tool.describe', ($, e, next) => PastUsers.pastUsers(e, next)).catch(
42 ($, e, next) => Caught.providedCaught(e, next),
43 )
44
45 on('command.describe', ($, e, next) => PastUsers.pastUsers(e, next)).catch(
46 ($, e, next) => Caught.providedCaught(e, next),
47 )
48
49 on('agent.offer', ($, e, next) => PastUsers.pastUsers(e, next)).catch(
50 ($, e, next) => Caught.providedCaught(e, next),
51 )
52
53 on('agent.spawn', ($, e, next) => PastUsers.pastUsers(e, next)).catch(
54 ($, e, next) => Caught.providedCaught(e, next),
55 )
56
57 on('tool.register', ($, e, next) =>
58 toolRegistered(e, next, () =>
59 Policy.decidedByPolicy(
60 readPolicy(() => $.settings.read(Policy.SOURCE)),
61 Policy.hasMcpAllowlist,
62 ),
63 ),
64 ).catch(($, e, next) =>
65 Caught.registerCaught(e, next, () => $.settings.read(Policy.SOURCE)),
66 )
67
68 on('tool.list', async ($, e, next) =>
69 toolsListed(
70 await readPolicy(() => $.settings.read(Policy.SOURCE)).catch(
71 () => undefined,
72 ),
73 e,
74 next,
75 ),
76 ).catch(($, e, next) => Caught.pastUsersCaught(e, next))
77
78 on('tool.check', async ($, e, next) => {
79 const answer = await next(e)
80 const mods = HeldVerdict.loosenedByUsers(next.trace)
81 const lifters = HeldVerdict.liftedByUsers(next.trace, e.ceiling)
82
83 const doRulesHold =
84 answer.decision !== 'deny' &&
85 mods.length > 0 &&
86 (await Policy.decidedByPolicy(
87 readPolicy(() => $.settings.read(Policy.SOURCE)),
88 Policy.denyRulesHold,
89 ))
90
91 if (!doRulesHold && !HeldVerdict.isOverCeiling(answer, e.ceiling)) {
92 return answer
93 }
94
95 const held = await next.to(e, 'append')
96
97 if (doRulesHold && HeldVerdict.isRuleDeny(held)) {
98 for (const mod of HeldVerdict.untold(told, mods)) {
99 told.add(mod)
100 quietly(() => $.ui.log(HeldVerdict.heldNotice(mod, e.tool, held.rule)))
101 }
102
103 return held
104 }
105
106 if (!HeldVerdict.isCeilingHeld(answer, held, e.ceiling)) {
107 return answer
108 }
109
110 for (const mod of HeldVerdict.untold(toldOfCeiling, lifters)) {
111 toldOfCeiling.add(mod)
112 quietly(() => $.ui.log(HeldVerdict.ceilingNotice(mod, e)))
113 }
114
115 return held
116 }).catch(($, e, next) =>
117 Caught.checkCaught(e, next, () => $.settings.read(Policy.SOURCE)),
118 )
119
120 on('ui.log', ($, e, next) =>
121 PastUsers.USER_REACHABLE_TIERS.includes(next.origin.tier)
122 ? next(e)
123 : next.to(e, 'append'),
124 ).catch(($, e, next) => Caught.pastUsersCaught(e, next))
125
126 on('plugin.register', { tier: 'user' }, async ($, e, next) =>
127 Policy.isManagedModsOnly(await $.settings.read(Policy.SOURCE))
128 ? { refuse: managedModsOnlyRefusal(e.name) }
129 : next(e),
130 ).catch(($, e, next) =>
131 Caught.admissionCaught(e, next, line => $.ui.log(line, { to: 'debug' })),
132 )
133}