@x-code-cli/core 0.2.9 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/compression.d.ts +12 -2
- package/dist/agent/compression.d.ts.map +1 -1
- package/dist/agent/compression.js +51 -2
- package/dist/agent/compression.js.map +1 -1
- package/dist/agent/file-ingest.js +2 -2
- package/dist/agent/file-ingest.js.map +1 -1
- package/dist/agent/loop-state.d.ts +3 -2
- package/dist/agent/loop-state.d.ts.map +1 -1
- package/dist/agent/loop-state.js.map +1 -1
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +140 -9
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/memory-extractor.js +5 -5
- package/dist/agent/memory-extractor.js.map +1 -1
- package/dist/agent/plan-storage.js +1 -1
- package/dist/agent/plan-storage.js.map +1 -1
- package/dist/agent/sub-agents/index.d.ts +2 -1
- package/dist/agent/sub-agents/index.d.ts.map +1 -1
- package/dist/agent/sub-agents/index.js +1 -1
- package/dist/agent/sub-agents/index.js.map +1 -1
- package/dist/agent/sub-agents/loader.d.ts +13 -3
- package/dist/agent/sub-agents/loader.d.ts.map +1 -1
- package/dist/agent/sub-agents/loader.js +36 -9
- package/dist/agent/sub-agents/loader.js.map +1 -1
- package/dist/agent/sub-agents/registry.d.ts +18 -1
- package/dist/agent/sub-agents/registry.d.ts.map +1 -1
- package/dist/agent/sub-agents/registry.js +38 -5
- package/dist/agent/sub-agents/registry.js.map +1 -1
- package/dist/agent/sub-agents/runner.d.ts.map +1 -1
- package/dist/agent/sub-agents/runner.js +45 -1
- package/dist/agent/sub-agents/runner.js.map +1 -1
- package/dist/agent/sub-agents/types.d.ts +4 -1
- package/dist/agent/sub-agents/types.d.ts.map +1 -1
- package/dist/agent/system-prompt.d.ts +21 -0
- package/dist/agent/system-prompt.d.ts.map +1 -1
- package/dist/agent/system-prompt.js +68 -2
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/agent/tool-execution.d.ts.map +1 -1
- package/dist/agent/tool-execution.js +220 -1
- package/dist/agent/tool-execution.js.map +1 -1
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +3 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/loader.d.ts +13 -0
- package/dist/commands/loader.d.ts.map +1 -0
- package/dist/commands/loader.js +93 -0
- package/dist/commands/loader.js.map +1 -0
- package/dist/commands/registry.d.ts +44 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +102 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/types.d.ts +23 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +26 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +12 -10
- package/dist/config/index.js.map +1 -1
- package/dist/hooks/bus.d.ts +54 -0
- package/dist/hooks/bus.d.ts.map +1 -0
- package/dist/hooks/bus.js +165 -0
- package/dist/hooks/bus.js.map +1 -0
- package/dist/hooks/config-schema.d.ts +854 -0
- package/dist/hooks/config-schema.d.ts.map +1 -0
- package/dist/hooks/config-schema.js +79 -0
- package/dist/hooks/config-schema.js.map +1 -0
- package/dist/hooks/executor.d.ts +16 -0
- package/dist/hooks/executor.d.ts.map +1 -0
- package/dist/hooks/executor.js +183 -0
- package/dist/hooks/executor.js.map +1 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +6 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/registry.d.ts +23 -0
- package/dist/hooks/registry.d.ts.map +1 -0
- package/dist/hooks/registry.js +49 -0
- package/dist/hooks/registry.js.map +1 -0
- package/dist/hooks/types.d.ts +165 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +25 -0
- package/dist/hooks/types.js.map +1 -0
- package/dist/hooks/variables.d.ts +22 -0
- package/dist/hooks/variables.d.ts.map +1 -0
- package/dist/hooks/variables.js +80 -0
- package/dist/hooks/variables.js.map +1 -0
- package/dist/index.d.ts +56 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/auto-memory.d.ts +1 -1
- package/dist/knowledge/auto-memory.d.ts.map +1 -1
- package/dist/knowledge/auto-memory.js +10 -10
- package/dist/knowledge/auto-memory.js.map +1 -1
- package/dist/knowledge/loader.js +12 -12
- package/dist/knowledge/loader.js.map +1 -1
- package/dist/mcp/arg-parser.d.ts +49 -0
- package/dist/mcp/arg-parser.d.ts.map +1 -0
- package/dist/mcp/arg-parser.js +357 -0
- package/dist/mcp/arg-parser.js.map +1 -0
- package/dist/mcp/client.d.ts +73 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +376 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/config-schema.d.ts +64 -0
- package/dist/mcp/config-schema.d.ts.map +1 -0
- package/dist/mcp/config-schema.js +86 -0
- package/dist/mcp/config-schema.js.map +1 -0
- package/dist/mcp/config-writer.d.ts +41 -0
- package/dist/mcp/config-writer.d.ts.map +1 -0
- package/dist/mcp/config-writer.js +138 -0
- package/dist/mcp/config-writer.js.map +1 -0
- package/dist/mcp/env-safety.d.ts +12 -0
- package/dist/mcp/env-safety.d.ts.map +1 -0
- package/dist/mcp/env-safety.js +80 -0
- package/dist/mcp/env-safety.js.map +1 -0
- package/dist/mcp/expand-env.d.ts +14 -0
- package/dist/mcp/expand-env.d.ts.map +1 -0
- package/dist/mcp/expand-env.js +52 -0
- package/dist/mcp/expand-env.js.map +1 -0
- package/dist/mcp/loader.d.ts +81 -0
- package/dist/mcp/loader.d.ts.map +1 -0
- package/dist/mcp/loader.js +223 -0
- package/dist/mcp/loader.js.map +1 -0
- package/dist/mcp/name-mangling.d.ts +11 -0
- package/dist/mcp/name-mangling.d.ts.map +1 -0
- package/dist/mcp/name-mangling.js +82 -0
- package/dist/mcp/name-mangling.js.map +1 -0
- package/dist/mcp/oauth/callback-server.d.ts +25 -0
- package/dist/mcp/oauth/callback-server.d.ts.map +1 -0
- package/dist/mcp/oauth/callback-server.js +118 -0
- package/dist/mcp/oauth/callback-server.js.map +1 -0
- package/dist/mcp/oauth/provider.d.ts +80 -0
- package/dist/mcp/oauth/provider.d.ts.map +1 -0
- package/dist/mcp/oauth/provider.js +292 -0
- package/dist/mcp/oauth/provider.js.map +1 -0
- package/dist/mcp/oauth/token-storage.d.ts +42 -0
- package/dist/mcp/oauth/token-storage.d.ts.map +1 -0
- package/dist/mcp/oauth/token-storage.js +121 -0
- package/dist/mcp/oauth/token-storage.js.map +1 -0
- package/dist/mcp/permissions.d.ts +28 -0
- package/dist/mcp/permissions.d.ts.map +1 -0
- package/dist/mcp/permissions.js +105 -0
- package/dist/mcp/permissions.js.map +1 -0
- package/dist/mcp/registry.d.ts +150 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +334 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/resources.d.ts +7 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +40 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/tool-bridge.d.ts +16 -0
- package/dist/mcp/tool-bridge.d.ts.map +1 -0
- package/dist/mcp/tool-bridge.js +56 -0
- package/dist/mcp/tool-bridge.js.map +1 -0
- package/dist/mcp/trust.d.ts +31 -0
- package/dist/mcp/trust.d.ts.map +1 -0
- package/dist/mcp/trust.js +103 -0
- package/dist/mcp/trust.js.map +1 -0
- package/dist/mcp/types.d.ts +73 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +13 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/permissions/session-store.d.ts +13 -2
- package/dist/permissions/session-store.d.ts.map +1 -1
- package/dist/permissions/session-store.js +264 -62
- package/dist/permissions/session-store.js.map +1 -1
- package/dist/plugins/consent.d.ts +87 -0
- package/dist/plugins/consent.d.ts.map +1 -0
- package/dist/plugins/consent.js +181 -0
- package/dist/plugins/consent.js.map +1 -0
- package/dist/plugins/enable-state.d.ts +34 -0
- package/dist/plugins/enable-state.d.ts.map +1 -0
- package/dist/plugins/enable-state.js +159 -0
- package/dist/plugins/enable-state.js.map +1 -0
- package/dist/plugins/installer.d.ts +64 -0
- package/dist/plugins/installer.d.ts.map +1 -0
- package/dist/plugins/installer.js +416 -0
- package/dist/plugins/installer.js.map +1 -0
- package/dist/plugins/integration.d.ts +91 -0
- package/dist/plugins/integration.d.ts.map +1 -0
- package/dist/plugins/integration.js +233 -0
- package/dist/plugins/integration.js.map +1 -0
- package/dist/plugins/loader.d.ts +69 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +243 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/manifest.d.ts +23 -0
- package/dist/plugins/manifest.d.ts.map +1 -0
- package/dist/plugins/manifest.js +143 -0
- package/dist/plugins/manifest.js.map +1 -0
- package/dist/plugins/marketplace.d.ts +100 -0
- package/dist/plugins/marketplace.d.ts.map +1 -0
- package/dist/plugins/marketplace.js +529 -0
- package/dist/plugins/marketplace.js.map +1 -0
- package/dist/plugins/paths.d.ts +44 -0
- package/dist/plugins/paths.d.ts.map +1 -0
- package/dist/plugins/paths.js +89 -0
- package/dist/plugins/paths.js.map +1 -0
- package/dist/plugins/refresh.d.ts +61 -0
- package/dist/plugins/refresh.d.ts.map +1 -0
- package/dist/plugins/refresh.js +98 -0
- package/dist/plugins/refresh.js.map +1 -0
- package/dist/plugins/registry.d.ts +40 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +80 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/plugins/types.d.ts +225 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +16 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/plugins/user-config.d.ts +22 -0
- package/dist/plugins/user-config.d.ts.map +1 -0
- package/dist/plugins/user-config.js +96 -0
- package/dist/plugins/user-config.js.map +1 -0
- package/dist/providers/cache-control.d.ts +9 -0
- package/dist/providers/cache-control.d.ts.map +1 -1
- package/dist/providers/cache-control.js +31 -4
- package/dist/providers/cache-control.js.map +1 -1
- package/dist/skills/loader.d.ts +19 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +197 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/registry.d.ts +74 -0
- package/dist/skills/registry.d.ts.map +1 -0
- package/dist/skills/registry.js +136 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/settings.d.ts +13 -0
- package/dist/skills/settings.d.ts.map +1 -0
- package/dist/skills/settings.js +100 -0
- package/dist/skills/settings.js.map +1 -0
- package/dist/tools/activate-skill.d.ts +5 -0
- package/dist/tools/activate-skill.d.ts.map +1 -0
- package/dist/tools/activate-skill.js +33 -0
- package/dist/tools/activate-skill.js.map +1 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/todo-write.d.ts +1 -1
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +2 -1
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/types/index.d.ts +46 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils.d.ts +23 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +76 -20
- package/dist/utils.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +47 -0
- package/dist/version.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["../../src/hooks/config-schema.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAgB5C,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAcb,CAAA;AAEhB,qBAAa,oBAAqB,SAAQ,KAAK;aAG3B,WAAW,EAAE,MAAM;gBADnC,OAAO,EAAE,MAAM,EACC,WAAW,EAAE,MAAM;CAKtC;AAED;+DAC+D;AAC/D,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CA0B7E"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// @x-code-cli/core — hooks.json zod schema
|
|
2
|
+
//
|
|
3
|
+
// Validates a `HookConfig` whether it came from a hooks.json file on
|
|
4
|
+
// disk or an inline manifest object. Same schema both paths — keeps the
|
|
5
|
+
// failure mode identical so plugin authors don't get different errors
|
|
6
|
+
// depending on which form they used.
|
|
7
|
+
//
|
|
8
|
+
// Bad regex in `matcher` is NOT a schema error — it'd be inconvenient
|
|
9
|
+
// to require authors to author / test their regex against zod's strict
|
|
10
|
+
// mode. The bus catches RegExp construction errors at emit time and
|
|
11
|
+
// degrades to "matches every tool" (logged for support).
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
const hookEntrySchema = z.object({
|
|
14
|
+
matcher: z.string().optional(),
|
|
15
|
+
command: z.string().min(1),
|
|
16
|
+
// Platform-specific overrides. Optional; missing on a platform falls
|
|
17
|
+
// back to `command`. We deliberately don't enforce that at least one
|
|
18
|
+
// of them is set — the base command is always required.
|
|
19
|
+
commandWindows: z.string().min(1).optional(),
|
|
20
|
+
commandDarwin: z.string().min(1).optional(),
|
|
21
|
+
commandLinux: z.string().min(1).optional(),
|
|
22
|
+
timeout: z.number().int().positive().max(30_000).optional(),
|
|
23
|
+
description: z.string().optional(),
|
|
24
|
+
failurePolicy: z.enum(['allow', 'block']).optional(),
|
|
25
|
+
});
|
|
26
|
+
export const hookConfigSchema = z
|
|
27
|
+
.object({
|
|
28
|
+
SessionStart: z.array(hookEntrySchema).optional(),
|
|
29
|
+
UserPromptSubmit: z.array(hookEntrySchema).optional(),
|
|
30
|
+
PreToolUse: z.array(hookEntrySchema).optional(),
|
|
31
|
+
PostToolUse: z.array(hookEntrySchema).optional(),
|
|
32
|
+
PreCompact: z.array(hookEntrySchema).optional(),
|
|
33
|
+
PostCompact: z.array(hookEntrySchema).optional(),
|
|
34
|
+
SubagentStart: z.array(hookEntrySchema).optional(),
|
|
35
|
+
SubagentStop: z.array(hookEntrySchema).optional(),
|
|
36
|
+
TurnComplete: z.array(hookEntrySchema).optional(),
|
|
37
|
+
SessionEnd: z.array(hookEntrySchema).optional(),
|
|
38
|
+
})
|
|
39
|
+
// Unknown keys are tolerated for forward compat (future event names).
|
|
40
|
+
.passthrough();
|
|
41
|
+
export class HookConfigParseError extends Error {
|
|
42
|
+
sourceLabel;
|
|
43
|
+
constructor(message, sourceLabel) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.sourceLabel = sourceLabel;
|
|
46
|
+
this.name = 'HookConfigParseError';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Validate an already-parsed-object form. Used for inline manifest
|
|
50
|
+
* configs and for the body of hooks.json after JSON.parse. */
|
|
51
|
+
export function parseHookConfig(raw, sourceLabel) {
|
|
52
|
+
const result = hookConfigSchema.safeParse(raw);
|
|
53
|
+
if (!result.success) {
|
|
54
|
+
const issues = result.error.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`).join('; ');
|
|
55
|
+
throw new HookConfigParseError(`invalid hooks config — ${issues}`, sourceLabel);
|
|
56
|
+
}
|
|
57
|
+
// Strip unknown future keys at the type boundary — passthrough kept
|
|
58
|
+
// them on the runtime object, but our HookConfig type only knows the
|
|
59
|
+
// ten events.
|
|
60
|
+
const known = {};
|
|
61
|
+
for (const k of [
|
|
62
|
+
'SessionStart',
|
|
63
|
+
'UserPromptSubmit',
|
|
64
|
+
'PreToolUse',
|
|
65
|
+
'PostToolUse',
|
|
66
|
+
'PreCompact',
|
|
67
|
+
'PostCompact',
|
|
68
|
+
'SubagentStart',
|
|
69
|
+
'SubagentStop',
|
|
70
|
+
'TurnComplete',
|
|
71
|
+
'SessionEnd',
|
|
72
|
+
]) {
|
|
73
|
+
const arr = result.data[k];
|
|
74
|
+
if (Array.isArray(arr))
|
|
75
|
+
known[k] = arr;
|
|
76
|
+
}
|
|
77
|
+
return known;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=config-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../src/hooks/config-schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HookDecision, HookEvent, RegisteredHook } from './types.js';
|
|
2
|
+
export interface ExecuteHookOptions {
|
|
3
|
+
/** Cancels the hook child process when fired. Agent loop's abort
|
|
4
|
+
* signal flows through here so Esc during a slow hook kills it
|
|
5
|
+
* promptly. */
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
/** Override the default 5s timeout. Per-hook `entry.timeout` still
|
|
8
|
+
* wins when both are set. Both are capped at 30s. */
|
|
9
|
+
defaultTimeoutMs?: number;
|
|
10
|
+
}
|
|
11
|
+
/** Run one hook against one event. Returns the parsed decision (default
|
|
12
|
+
* allow on anything unexpected). Never throws unless the caller's
|
|
13
|
+
* AbortSignal fires — abort is the one error worth bubbling because
|
|
14
|
+
* the caller's loop is already shutting down. */
|
|
15
|
+
export declare function executeHook(hook: RegisteredHook, event: HookEvent, opts?: ExecuteHookOptions): Promise<HookDecision>;
|
|
16
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/hooks/executor.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAmB,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAwB1F,MAAM,WAAW,kBAAkB;IACjC;;oBAEgB;IAChB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;0DACsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;kDAGkD;AAClD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,SAAS,EAChB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAiEvB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// @x-code-cli/core — Hook command executor
|
|
2
|
+
//
|
|
3
|
+
// Spawns a hook's shell command, writes the event JSON on stdin, reads
|
|
4
|
+
// the decision JSON from stdout. The whole protocol is line-oriented:
|
|
5
|
+
// one JSON object in, one JSON object out (or empty stdout = default
|
|
6
|
+
// allow). Anything else on stdout is ignored — common pattern for hooks
|
|
7
|
+
// that just want to log to stderr without influencing the agent.
|
|
8
|
+
//
|
|
9
|
+
// Failure handling is deliberately permissive (default `failurePolicy:
|
|
10
|
+
// 'allow'`): a broken hook must never wedge the agent loop. Non-zero
|
|
11
|
+
// exit, timeout, or crash all degrade to `allow` and log a debug
|
|
12
|
+
// breadcrumb. The `block` policy is opt-in and reserved for hooks the
|
|
13
|
+
// plugin author has explicitly designed as gating hooks.
|
|
14
|
+
//
|
|
15
|
+
// AbortSignal propagation: passed to execa's `cancelSignal` so the
|
|
16
|
+
// child process is SIGKILL'd when the user hits Esc mid-hook. Same
|
|
17
|
+
// machinery the shell tool uses.
|
|
18
|
+
import { execa } from 'execa';
|
|
19
|
+
import { getPluginUserConfigEnv } from '../plugins/user-config.js';
|
|
20
|
+
import { debugLog } from '../utils.js';
|
|
21
|
+
import { buildVariableContext, expandVariables } from './variables.js';
|
|
22
|
+
/** Return the command appropriate for the current OS. Plugin authors
|
|
23
|
+
* set `command` as a portable default and may add `commandWindows` /
|
|
24
|
+
* `commandDarwin` / `commandLinux` to handle per-OS differences (e.g.
|
|
25
|
+
* shebang line, executable name, quoting). Unknown platforms (freebsd,
|
|
26
|
+
* sunos, aix) fall through to the base. */
|
|
27
|
+
function pickPlatformCommand(entry) {
|
|
28
|
+
switch (process.platform) {
|
|
29
|
+
case 'win32':
|
|
30
|
+
return entry.commandWindows ?? entry.command;
|
|
31
|
+
case 'darwin':
|
|
32
|
+
return entry.commandDarwin ?? entry.command;
|
|
33
|
+
case 'linux':
|
|
34
|
+
return entry.commandLinux ?? entry.command;
|
|
35
|
+
default:
|
|
36
|
+
return entry.command;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
40
|
+
const MAX_TIMEOUT_MS = 30_000;
|
|
41
|
+
/** Run one hook against one event. Returns the parsed decision (default
|
|
42
|
+
* allow on anything unexpected). Never throws unless the caller's
|
|
43
|
+
* AbortSignal fires — abort is the one error worth bubbling because
|
|
44
|
+
* the caller's loop is already shutting down. */
|
|
45
|
+
export async function executeHook(hook, event, opts = {}) {
|
|
46
|
+
const timeoutMs = Math.min(hook.entry.timeout ?? opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
|
|
47
|
+
const vars = buildVariableContext({
|
|
48
|
+
pluginDir: hook.pluginDir,
|
|
49
|
+
cwd: event.session.cwd,
|
|
50
|
+
pluginId: hook.pluginId,
|
|
51
|
+
});
|
|
52
|
+
const expandedCommand = expandVariables(pickPlatformCommand(hook.entry), vars);
|
|
53
|
+
const stdinPayload = JSON.stringify(buildStdinPayload(hook, event));
|
|
54
|
+
// Merge the owning plugin's userConfig values into the hook's env.
|
|
55
|
+
// Hook scripts that need an API key declared in the manifest read it as
|
|
56
|
+
// `process.env[KEY]` without writing any glue — `${env:KEY}` substitution
|
|
57
|
+
// in the command string also resolves against this merged env. We fail
|
|
58
|
+
// silent if the read errors (no userConfig set yet ⇒ empty map).
|
|
59
|
+
let pluginEnv = {};
|
|
60
|
+
try {
|
|
61
|
+
pluginEnv = await getPluginUserConfigEnv(hook.pluginId);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
debugLog('hooks.user-config-read-failed', `${hook.pluginId}: ${String(err)}`);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const result = await execa(expandedCommand, [], {
|
|
68
|
+
shell: true,
|
|
69
|
+
input: stdinPayload,
|
|
70
|
+
timeout: timeoutMs,
|
|
71
|
+
cancelSignal: opts.signal,
|
|
72
|
+
stdio: 'pipe',
|
|
73
|
+
reject: false, // Non-zero exits handled below explicitly, not as throws.
|
|
74
|
+
cwd: event.session.cwd,
|
|
75
|
+
env: { ...process.env, ...pluginEnv },
|
|
76
|
+
});
|
|
77
|
+
if (opts.signal?.aborted) {
|
|
78
|
+
// Aborted mid-execution. Caller's loop is winding down — surface
|
|
79
|
+
// by throwing so the bus stops cascading further hooks.
|
|
80
|
+
throw new Error('aborted');
|
|
81
|
+
}
|
|
82
|
+
if (result.timedOut) {
|
|
83
|
+
debugLog('hooks.exec-timeout', `${hook.pluginId} ${event.name}: timed out after ${timeoutMs}ms`);
|
|
84
|
+
return failurePolicyDecision(hook, `hook timed out after ${timeoutMs}ms`);
|
|
85
|
+
}
|
|
86
|
+
if (typeof result.exitCode === 'number' && result.exitCode !== 0) {
|
|
87
|
+
const stderrTail = (result.stderr ?? '').toString().slice(0, 200);
|
|
88
|
+
debugLog('hooks.exec-nonzero', `${hook.pluginId} ${event.name}: exit ${result.exitCode} stderr=${stderrTail}`);
|
|
89
|
+
return failurePolicyDecision(hook, `hook exited ${result.exitCode}`);
|
|
90
|
+
}
|
|
91
|
+
const decision = parseDecision(result.stdout ?? '', hook, event);
|
|
92
|
+
// Trace successful hook runs so plugin authors can confirm their
|
|
93
|
+
// hook actually fired without needing to add their own logging.
|
|
94
|
+
// Stdio is `pipe`d (we read the JSON decision out of stdout), so
|
|
95
|
+
// anything the hook writes to its own stderr is otherwise invisible
|
|
96
|
+
// — this breadcrumb is what `--plugin-debug` / `DEBUG_STDOUT=1`
|
|
97
|
+
// users grep for to verify the wiring.
|
|
98
|
+
debugLog('hooks.exec-ran', `${hook.pluginId} ${event.name}: decision=${decision.decision}`);
|
|
99
|
+
return decision;
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
if (opts.signal?.aborted)
|
|
103
|
+
throw err;
|
|
104
|
+
debugLog('hooks.exec-error', `${hook.pluginId} ${event.name}: ${String(err)}`);
|
|
105
|
+
return failurePolicyDecision(hook, `hook crashed: ${err instanceof Error ? err.message : String(err)}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function failurePolicyDecision(hook, reason) {
|
|
109
|
+
if (hook.entry.failurePolicy === 'block')
|
|
110
|
+
return { decision: 'deny', reason };
|
|
111
|
+
return { decision: 'allow' };
|
|
112
|
+
}
|
|
113
|
+
/** Build the JSON object sent to the hook over stdin. Event-specific
|
|
114
|
+
* fields are flattened in at the top level (matches Claude Code's
|
|
115
|
+
* hook protocol shape). */
|
|
116
|
+
function buildStdinPayload(hook, event) {
|
|
117
|
+
const base = {
|
|
118
|
+
event: event.name,
|
|
119
|
+
session: event.session,
|
|
120
|
+
plugin: { id: hook.pluginId, dir: hook.pluginDir },
|
|
121
|
+
};
|
|
122
|
+
switch (event.name) {
|
|
123
|
+
case 'UserPromptSubmit':
|
|
124
|
+
base.prompt = event.prompt;
|
|
125
|
+
break;
|
|
126
|
+
case 'PreToolUse':
|
|
127
|
+
base.tool = event.tool;
|
|
128
|
+
break;
|
|
129
|
+
case 'PostToolUse':
|
|
130
|
+
base.tool = event.tool;
|
|
131
|
+
break;
|
|
132
|
+
case 'PreCompact':
|
|
133
|
+
base.trigger = event.trigger;
|
|
134
|
+
base.messageCount = event.messageCount;
|
|
135
|
+
base.tokenEstimate = event.tokenEstimate;
|
|
136
|
+
break;
|
|
137
|
+
case 'PostCompact':
|
|
138
|
+
base.trigger = event.trigger;
|
|
139
|
+
base.messageCount = event.messageCount;
|
|
140
|
+
base.summary = event.summary;
|
|
141
|
+
break;
|
|
142
|
+
case 'SubagentStart':
|
|
143
|
+
base.agent = event.agent;
|
|
144
|
+
break;
|
|
145
|
+
case 'SubagentStop':
|
|
146
|
+
base.agent = event.agent;
|
|
147
|
+
base.durationMs = event.durationMs;
|
|
148
|
+
base.outcome = event.outcome;
|
|
149
|
+
if (event.tokenUsage)
|
|
150
|
+
base.tokenUsage = event.tokenUsage;
|
|
151
|
+
break;
|
|
152
|
+
case 'TurnComplete':
|
|
153
|
+
base.turn = event.turn;
|
|
154
|
+
if (event.tokenUsage)
|
|
155
|
+
base.tokenUsage = event.tokenUsage;
|
|
156
|
+
break;
|
|
157
|
+
// SessionStart / SessionEnd have no extra fields beyond session/plugin.
|
|
158
|
+
}
|
|
159
|
+
return base;
|
|
160
|
+
}
|
|
161
|
+
function parseDecision(stdout, hook, event) {
|
|
162
|
+
const trimmed = (stdout ?? '').toString().trim();
|
|
163
|
+
if (!trimmed)
|
|
164
|
+
return { decision: 'allow' };
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(trimmed);
|
|
167
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
168
|
+
const obj = parsed;
|
|
169
|
+
const d = obj.decision;
|
|
170
|
+
if (d === 'allow' || d === 'deny' || d === 'modify') {
|
|
171
|
+
return obj;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Not JSON — many hooks intend stdout for human eyes (logs). Treat
|
|
177
|
+
// as default allow but breadcrumb in case the user expected it to
|
|
178
|
+
// influence the agent.
|
|
179
|
+
debugLog('hooks.decision-not-json', `${hook.pluginId} ${event.name}: ignoring non-JSON stdout: ${trimmed.slice(0, 200)}`);
|
|
180
|
+
}
|
|
181
|
+
return { decision: 'allow' };
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/hooks/executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type { DecisionEvent, HookConfig, HookConfigEntry, HookDecision, HookEvent, HookEventName, RegisteredHook, SessionContext, } from './types.js';
|
|
2
|
+
export { hookConfigSchema, parseHookConfig, HookConfigParseError } from './config-schema.js';
|
|
3
|
+
export { buildVariableContext, expandVariables } from './variables.js';
|
|
4
|
+
export type { VariableContext } from './variables.js';
|
|
5
|
+
export { executeHook } from './executor.js';
|
|
6
|
+
export type { ExecuteHookOptions } from './executor.js';
|
|
7
|
+
export { HookRegistry, buildHookRegistry, emptyHookRegistry } from './registry.js';
|
|
8
|
+
export { HookBus, emptyHookBus, aggregatePreToolUse, aggregatePostToolUse, aggregateUserPromptSubmit } from './bus.js';
|
|
9
|
+
export type { EmitOptions, PreToolEffect, PostToolEffect, UserPromptEffect } from './bus.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,UAAU,EACV,eAAe,EACf,YAAY,EACZ,SAAS,EACT,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC5F,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACtE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClF,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AACtH,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { hookConfigSchema, parseHookConfig, HookConfigParseError } from './config-schema.js';
|
|
2
|
+
export { buildVariableContext, expandVariables } from './variables.js';
|
|
3
|
+
export { executeHook } from './executor.js';
|
|
4
|
+
export { HookRegistry, buildHookRegistry, emptyHookRegistry } from './registry.js';
|
|
5
|
+
export { HookBus, emptyHookBus, aggregatePreToolUse, aggregatePostToolUse, aggregateUserPromptSubmit } from './bus.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC5F,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAEtE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClF,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HookConfig, HookEventName, RegisteredHook } from './types.js';
|
|
2
|
+
export declare class HookRegistry {
|
|
3
|
+
private byEvent;
|
|
4
|
+
constructor(hooks?: ReadonlyArray<RegisteredHook>);
|
|
5
|
+
/** Hooks bound to a given event, in registration order. */
|
|
6
|
+
get(event: HookEventName): readonly RegisteredHook[];
|
|
7
|
+
/** Cheap check the bus uses to skip event-payload construction when
|
|
8
|
+
* no hook is listening — every emit-site is in a hot path. */
|
|
9
|
+
has(event: HookEventName): boolean;
|
|
10
|
+
/** Every registered hook. Used by `/plugin doctor` to list what's
|
|
11
|
+
* active alongside which plugin contributed it. */
|
|
12
|
+
list(): readonly RegisteredHook[];
|
|
13
|
+
}
|
|
14
|
+
/** Build a registry from per-plugin hook configs. Iteration order of
|
|
15
|
+
* the input array determines emit order — the caller (integration.ts)
|
|
16
|
+
* is responsible for handing us plugins in a stable order. */
|
|
17
|
+
export declare function buildHookRegistry(pluginHooks: ReadonlyArray<{
|
|
18
|
+
pluginId: string;
|
|
19
|
+
pluginDir: string;
|
|
20
|
+
config: HookConfig;
|
|
21
|
+
}>): HookRegistry;
|
|
22
|
+
export declare function emptyHookRegistry(): HookRegistry;
|
|
23
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/hooks/registry.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3E,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAsC;gBAEzC,KAAK,GAAE,aAAa,CAAC,cAAc,CAAM;IASrD,2DAA2D;IAC3D,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,cAAc,EAAE;IAIpD;mEAC+D;IAC/D,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IAIlC;wDACoD;IACpD,IAAI,IAAI,SAAS,cAAc,EAAE;CAKlC;AAED;;+DAE+D;AAC/D,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,aAAa,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,GACtF,YAAY,CAYd;AAED,wBAAgB,iBAAiB,IAAI,YAAY,CAEhD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export class HookRegistry {
|
|
2
|
+
byEvent;
|
|
3
|
+
constructor(hooks = []) {
|
|
4
|
+
this.byEvent = new Map();
|
|
5
|
+
for (const h of hooks) {
|
|
6
|
+
const list = this.byEvent.get(h.event) ?? [];
|
|
7
|
+
list.push(h);
|
|
8
|
+
this.byEvent.set(h.event, list);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Hooks bound to a given event, in registration order. */
|
|
12
|
+
get(event) {
|
|
13
|
+
return this.byEvent.get(event) ?? [];
|
|
14
|
+
}
|
|
15
|
+
/** Cheap check the bus uses to skip event-payload construction when
|
|
16
|
+
* no hook is listening — every emit-site is in a hot path. */
|
|
17
|
+
has(event) {
|
|
18
|
+
return (this.byEvent.get(event)?.length ?? 0) > 0;
|
|
19
|
+
}
|
|
20
|
+
/** Every registered hook. Used by `/plugin doctor` to list what's
|
|
21
|
+
* active alongside which plugin contributed it. */
|
|
22
|
+
list() {
|
|
23
|
+
const all = [];
|
|
24
|
+
for (const arr of this.byEvent.values())
|
|
25
|
+
all.push(...arr);
|
|
26
|
+
return all;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Build a registry from per-plugin hook configs. Iteration order of
|
|
30
|
+
* the input array determines emit order — the caller (integration.ts)
|
|
31
|
+
* is responsible for handing us plugins in a stable order. */
|
|
32
|
+
export function buildHookRegistry(pluginHooks) {
|
|
33
|
+
const all = [];
|
|
34
|
+
for (const { pluginId, pluginDir, config } of pluginHooks) {
|
|
35
|
+
for (const eventName of Object.keys(config)) {
|
|
36
|
+
const entries = config[eventName];
|
|
37
|
+
if (!entries)
|
|
38
|
+
continue;
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
all.push({ pluginId, pluginDir, event: eventName, entry });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return new HookRegistry(all);
|
|
45
|
+
}
|
|
46
|
+
export function emptyHookRegistry() {
|
|
47
|
+
return new HookRegistry([]);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/hooks/registry.ts"],"names":[],"mappings":"AAWA,MAAM,OAAO,YAAY;IACf,OAAO,CAAsC;IAErD,YAAY,QAAuC,EAAE;QACnD,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAA;QACxB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;YAC5C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACZ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,GAAG,CAAC,KAAoB;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;IACtC,CAAC;IAED;mEAC+D;IAC/D,GAAG,CAAC,KAAoB;QACtB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;IACnD,CAAC;IAED;wDACoD;IACpD,IAAI;QACF,MAAM,GAAG,GAAqB,EAAE,CAAA;QAChC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAA;QACzD,OAAO,GAAG,CAAA;IACZ,CAAC;CACF;AAED;;+DAE+D;AAC/D,MAAM,UAAU,iBAAiB,CAC/B,WAAuF;IAEvF,MAAM,GAAG,GAAqB,EAAE,CAAA;IAChC,KAAK,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC1D,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAoB,EAAE,CAAC;YAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAA;YACjC,IAAI,CAAC,OAAO;gBAAE,SAAQ;YACtB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;YAC5D,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,YAAY,CAAC,GAAG,CAAC,CAAA;AAC9B,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,YAAY,CAAC,EAAE,CAAC,CAAA;AAC7B,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
export type HookEventName = 'SessionStart' | 'UserPromptSubmit' | 'PreToolUse' | 'PostToolUse' | 'PreCompact' | 'PostCompact' | 'SubagentStart' | 'SubagentStop' | 'TurnComplete' | 'SessionEnd';
|
|
2
|
+
/** Subset of events that emit a decision the agent acts on. Other events
|
|
3
|
+
* are fire-and-forget — hooks may run side effects (logging,
|
|
4
|
+
* notifications) but the agent ignores their stdout. */
|
|
5
|
+
export type DecisionEvent = 'UserPromptSubmit' | 'PreToolUse' | 'PostToolUse';
|
|
6
|
+
/** One hook entry as it appears in hooks.json. */
|
|
7
|
+
export interface HookConfigEntry {
|
|
8
|
+
/** Optional regex matching tool name. Only meaningful for PreToolUse /
|
|
9
|
+
* PostToolUse — ignored for other events. A missing matcher means
|
|
10
|
+
* "every tool". */
|
|
11
|
+
matcher?: string;
|
|
12
|
+
/** Shell command to run on the current platform when no platform-specific
|
|
13
|
+
* override below is set. Supports `${pluginDir}` / `${pluginDataDir}` /
|
|
14
|
+
* `${cwd}` / `${homedir}` / `${env:NAME}` / `${sep}` variable expansion
|
|
15
|
+
* (see [[variables]]).
|
|
16
|
+
*
|
|
17
|
+
* We require this even when the platform overrides are set so plugin
|
|
18
|
+
* authors can't accidentally ship a plugin that runs on *only* one OS
|
|
19
|
+
* — the base command is the safety net for any platform the author
|
|
20
|
+
* didn't explicitly think about. */
|
|
21
|
+
command: string;
|
|
22
|
+
/** Platform-specific override commands. When set, the matching one
|
|
23
|
+
* replaces `command` on that OS. Keys match `process.platform`
|
|
24
|
+
* values; unknown platforms (freebsd / sunos / aix) fall through to
|
|
25
|
+
* the base `command`. */
|
|
26
|
+
commandWindows?: string;
|
|
27
|
+
commandDarwin?: string;
|
|
28
|
+
commandLinux?: string;
|
|
29
|
+
/** Per-hook timeout in ms (default 5000, capped at 30000). */
|
|
30
|
+
timeout?: number;
|
|
31
|
+
description?: string;
|
|
32
|
+
/** What to do when the hook exits non-zero or crashes:
|
|
33
|
+
*
|
|
34
|
+
* 'allow' (default) — log warning, treat as if the hook said allow
|
|
35
|
+
* 'block' — treat as deny (only meaningful for DecisionEvents)
|
|
36
|
+
*
|
|
37
|
+
* The default is permissive on purpose: a broken hook must not be
|
|
38
|
+
* able to wedge the agent loop indefinitely. */
|
|
39
|
+
failurePolicy?: 'allow' | 'block';
|
|
40
|
+
}
|
|
41
|
+
/** A whole hooks.json file. Each event name maps to an ordered array of
|
|
42
|
+
* entries — earlier entries run first, and for decision events a deny
|
|
43
|
+
* short-circuits the rest. */
|
|
44
|
+
export type HookConfig = Partial<Record<HookEventName, HookConfigEntry[]>>;
|
|
45
|
+
/** Session-level context attached to every event payload. */
|
|
46
|
+
export interface SessionContext {
|
|
47
|
+
cwd: string;
|
|
48
|
+
modelId: string;
|
|
49
|
+
/** Optional — when the CLI assigns a session id we pass it through so
|
|
50
|
+
* hooks can correlate events. */
|
|
51
|
+
sessionId?: string;
|
|
52
|
+
}
|
|
53
|
+
/** Discriminated union of every event payload shape. The `name` field
|
|
54
|
+
* doubles as the tag. The CLI builds these and hands them to
|
|
55
|
+
* [[HookBus.emit]] — the executor serialises them as JSON for stdin. */
|
|
56
|
+
export type HookEvent = {
|
|
57
|
+
name: 'SessionStart';
|
|
58
|
+
session: SessionContext;
|
|
59
|
+
} | {
|
|
60
|
+
name: 'UserPromptSubmit';
|
|
61
|
+
session: SessionContext;
|
|
62
|
+
prompt: string;
|
|
63
|
+
} | {
|
|
64
|
+
name: 'PreToolUse';
|
|
65
|
+
session: SessionContext;
|
|
66
|
+
tool: {
|
|
67
|
+
name: string;
|
|
68
|
+
args: unknown;
|
|
69
|
+
callId: string;
|
|
70
|
+
};
|
|
71
|
+
} | {
|
|
72
|
+
name: 'PostToolUse';
|
|
73
|
+
session: SessionContext;
|
|
74
|
+
tool: {
|
|
75
|
+
name: string;
|
|
76
|
+
args: unknown;
|
|
77
|
+
callId: string;
|
|
78
|
+
output: string;
|
|
79
|
+
isError: boolean;
|
|
80
|
+
};
|
|
81
|
+
} | {
|
|
82
|
+
name: 'PreCompact';
|
|
83
|
+
session: SessionContext;
|
|
84
|
+
/** Why compaction is about to run — useful for hooks that want to
|
|
85
|
+
* decide whether to checkpoint state or skip. */
|
|
86
|
+
trigger: 'proactive' | 'reactive';
|
|
87
|
+
/** Approximate message count and token count before compaction. */
|
|
88
|
+
messageCount: number;
|
|
89
|
+
tokenEstimate: number;
|
|
90
|
+
} | {
|
|
91
|
+
name: 'PostCompact';
|
|
92
|
+
session: SessionContext;
|
|
93
|
+
trigger: 'proactive' | 'reactive';
|
|
94
|
+
/** Message count after compaction — the delta from PreCompact's
|
|
95
|
+
* messageCount tells the hook how much was reclaimed. */
|
|
96
|
+
messageCount: number;
|
|
97
|
+
/** Empty string when the path was a light-compact (no LLM summary
|
|
98
|
+
* was written). */
|
|
99
|
+
summary: string;
|
|
100
|
+
} | {
|
|
101
|
+
name: 'SubagentStart';
|
|
102
|
+
session: SessionContext;
|
|
103
|
+
agent: {
|
|
104
|
+
/** The sub-agent's registered name (e.g. `code-reviewer`). */
|
|
105
|
+
name: string;
|
|
106
|
+
/** The parent agent's one-line task description. */
|
|
107
|
+
description: string;
|
|
108
|
+
/** The full prompt the parent agent sent to the sub-agent. */
|
|
109
|
+
prompt: string;
|
|
110
|
+
};
|
|
111
|
+
} | {
|
|
112
|
+
name: 'SubagentStop';
|
|
113
|
+
session: SessionContext;
|
|
114
|
+
agent: {
|
|
115
|
+
name: string;
|
|
116
|
+
description: string;
|
|
117
|
+
};
|
|
118
|
+
/** Wall-clock duration of the sub-agent run. */
|
|
119
|
+
durationMs: number;
|
|
120
|
+
/** How the sub-agent finished. `aborted` includes Esc cancellation
|
|
121
|
+
* and reaching the per-agent maxTurns cap without finalising. */
|
|
122
|
+
outcome: 'completed' | 'aborted' | 'failed';
|
|
123
|
+
tokenUsage?: {
|
|
124
|
+
inputTokens: number;
|
|
125
|
+
outputTokens: number;
|
|
126
|
+
totalTokens: number;
|
|
127
|
+
};
|
|
128
|
+
} | {
|
|
129
|
+
name: 'TurnComplete';
|
|
130
|
+
session: SessionContext;
|
|
131
|
+
turn: number;
|
|
132
|
+
tokenUsage?: {
|
|
133
|
+
inputTokens: number;
|
|
134
|
+
outputTokens: number;
|
|
135
|
+
totalTokens: number;
|
|
136
|
+
};
|
|
137
|
+
} | {
|
|
138
|
+
name: 'SessionEnd';
|
|
139
|
+
session: SessionContext;
|
|
140
|
+
};
|
|
141
|
+
/** What a hook can ask the agent to do via its stdout JSON. */
|
|
142
|
+
export type HookDecision = {
|
|
143
|
+
decision: 'allow';
|
|
144
|
+
context?: string;
|
|
145
|
+
} | {
|
|
146
|
+
decision: 'deny';
|
|
147
|
+
reason?: string;
|
|
148
|
+
} | {
|
|
149
|
+
decision: 'modify';
|
|
150
|
+
args?: unknown;
|
|
151
|
+
output?: string;
|
|
152
|
+
context?: string;
|
|
153
|
+
};
|
|
154
|
+
/** A hook ready to execute — paired with its owning plugin's identity
|
|
155
|
+
* and rootDir so variable expansion can resolve `${pluginDir}`. Built
|
|
156
|
+
* by [[buildHookRegistry]] at startup, immutable for the session. */
|
|
157
|
+
export interface RegisteredHook {
|
|
158
|
+
pluginId: string;
|
|
159
|
+
/** Absolute path to the plugin's root dir — substituted into the
|
|
160
|
+
* hook command via `${pluginDir}`. */
|
|
161
|
+
pluginDir: string;
|
|
162
|
+
event: HookEventName;
|
|
163
|
+
entry: HookConfigEntry;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/hooks/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// @x-code-cli/core — Hooks subsystem types
|
|
2
|
+
//
|
|
3
|
+
// A hook is a shell command a plugin registers against one of six agent
|
|
4
|
+
// lifecycle events. The CLI emits an event payload to the hook on stdin
|
|
5
|
+
// as one JSON line; the hook may reply on stdout with a one-line JSON
|
|
6
|
+
// `HookDecision` to influence what the agent does next (allow / deny /
|
|
7
|
+
// modify args / inject context).
|
|
8
|
+
//
|
|
9
|
+
// Why shell commands and not a programmatic SDK: lowest barrier to entry
|
|
10
|
+
// for plugin authors, matches the format users already see in Claude
|
|
11
|
+
// Code, and keeps the surface area small (no plugin code runs inside
|
|
12
|
+
// our process). See [[plugin-marketplace-design]] §8 for the full
|
|
13
|
+
// rationale.
|
|
14
|
+
//
|
|
15
|
+
// Why ten events: enough to cover the high-value lifecycle integrations
|
|
16
|
+
// (context injection, tool gating, sub-agent audit, compaction
|
|
17
|
+
// instrumentation, completion notifications) without exposing every
|
|
18
|
+
// internal seam we may want to refactor. Adding events later is cheap;
|
|
19
|
+
// removing them is a breaking change. PreCompact / PostCompact and
|
|
20
|
+
// SubagentStart / SubagentStop were added in round 2 to match the
|
|
21
|
+
// Claude/Codex shape — plugins that want to log every sub-agent
|
|
22
|
+
// invocation or persist state before compaction wipes it had no other
|
|
23
|
+
// hook to attach to.
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/hooks/types.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,sEAAsE;AACtE,uEAAuE;AACvE,iCAAiC;AACjC,EAAE;AACF,yEAAyE;AACzE,qEAAqE;AACrE,qEAAqE;AACrE,kEAAkE;AAClE,aAAa;AACb,EAAE;AACF,wEAAwE;AACxE,+DAA+D;AAC/D,oEAAoE;AACpE,uEAAuE;AACvE,mEAAmE;AACnE,kEAAkE;AAClE,gEAAgE;AAChE,sEAAsE;AACtE,qBAAqB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface VariableContext {
|
|
2
|
+
pluginDir: string;
|
|
3
|
+
/** Persistent per-plugin data directory. Pre-created by
|
|
4
|
+
* [[buildVariableContext]] when a `pluginId` is supplied. */
|
|
5
|
+
pluginDataDir?: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
homedir?: string;
|
|
8
|
+
sep?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Default variables derived from the current process + caller context.
|
|
11
|
+
* Pass `pluginId` to enable `${pluginDataDir}` — we'll resolve the
|
|
12
|
+
* per-plugin data dir path and `mkdir -p` it so the plugin can write
|
|
13
|
+
* there immediately. mkdirSync on an existing dir is a cheap no-op. */
|
|
14
|
+
export declare function buildVariableContext(input: {
|
|
15
|
+
pluginDir: string;
|
|
16
|
+
cwd: string;
|
|
17
|
+
pluginId?: string;
|
|
18
|
+
}): VariableContext;
|
|
19
|
+
/** Expand `${pluginDir}` / `${pluginDataDir}` / `${cwd}` / `${homedir}` /
|
|
20
|
+
* `${sep}` / `${env:NAME}` references. Unknown patterns are left verbatim. */
|
|
21
|
+
export declare function expandVariables(source: string, ctx: VariableContext): string;
|
|
22
|
+
//# sourceMappingURL=variables.d.ts.map
|