@workit-dev/core 0.0.1 → 1.0.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/LICENSE +21 -0
- package/README.md +22 -43
- package/package.json +56 -6
- package/schemas/config.json +74 -0
- package/schemas/vcs.json +79 -0
- package/schemas/workspaces.json +1029 -0
- package/schemas/youtrack.json +84 -0
- package/scripts/analyze-release-scope.ts +215 -0
- package/scripts/bundled-deps.json +131 -0
- package/scripts/fixtures/sample-plan.md +15 -0
- package/scripts/fixtures/sample-sdd/progress.md +2 -0
- package/scripts/install-codex-plugin.sh +28 -0
- package/scripts/install-cursor-plugin.sh +146 -0
- package/scripts/install-opencode-plugin.sh +79 -0
- package/scripts/install-pi-package.sh +56 -0
- package/scripts/lib/config-dir.sh +28 -0
- package/scripts/publish-changed-packages.ts +395 -0
- package/scripts/rewrite-workspace-deps.ts +53 -0
- package/scripts/run-cursor-mcp.sh +6 -0
- package/scripts/sync-release-manifests.ts +82 -0
- package/scripts/sync-runtime.sh +142 -0
- package/scripts/validate-cursor-marketplace.ts +171 -0
- package/scripts/verify-bundle-sources.ts +134 -0
- package/scripts/write-schemas.ts +16 -0
- package/skills/workit-architecture/SKILL.md +72 -0
- package/skills/workit-architecture/agents/openai.yaml +3 -0
- package/skills/workit-architecture/references/instruction-files.md +52 -0
- package/skills/workit-architecture/references/vocabulary.md +53 -0
- package/skills/workit-bdd/SKILL.md +53 -0
- package/skills/workit-continue/SKILL.md +54 -0
- package/skills/workit-debug/SKILL.md +45 -0
- package/skills/workit-deslop/SKILL.md +58 -0
- package/skills/workit-fanout/SKILL.md +71 -0
- package/skills/workit-fanout/references/brief.md +141 -0
- package/skills/workit-fanout/references/orchestration.md +169 -0
- package/skills/workit-implement/SKILL.md +71 -0
- package/skills/workit-retro/SKILL.md +72 -0
- package/skills/workit-retro/agents/openai.yaml +3 -0
- package/skills/workit-retro/references/sources.md +35 -0
- package/skills/workit-retro/references/steering.md +39 -0
- package/skills/workit-review/SKILL.md +66 -0
- package/skills/workit-review/references/impact.md +24 -0
- package/skills/workit-shape/SKILL.md +71 -0
- package/skills/workit-shape/references/diagrams.md +17 -0
- package/skills/workit-shape/references/knowledge.md +61 -0
- package/skills/workit-shape/references/mockups.md +15 -0
- package/skills/workit-shape/references/slicing.md +44 -0
- package/skills/workit-ship/SKILL.md +71 -0
- package/skills/workit-ship/references/changelog.md +44 -0
- package/skills/workit-ship/references/pr-body.md +32 -0
- package/skills/workit-ship/references/release.md +39 -0
- package/skills/workit-test-audit/SKILL.md +54 -0
- package/skills/workit-verify-app/SKILL.md +68 -0
- package/skills/workit-verify-app/references/template.md +51 -0
- package/src/autonomy.ts +910 -0
- package/src/check-config.ts +613 -0
- package/src/checks.ts +534 -0
- package/src/ci-waiter.ts +159 -0
- package/src/core/boundary.ts +55 -0
- package/src/core/branch-policy.ts +112 -0
- package/src/core/branch.ts +1192 -0
- package/src/core/changelog-gate.ts +349 -0
- package/src/core/changelog-merge.ts +214 -0
- package/src/core/changelog.ts +392 -0
- package/src/core/cleanup.ts +638 -0
- package/src/core/cli-locate.ts +173 -0
- package/src/core/commit-flavors.ts +65 -0
- package/src/core/config-schemas.ts +653 -0
- package/src/core/config.ts +351 -0
- package/src/core/context-read.ts +144 -0
- package/src/core/docs-gate.ts +69 -0
- package/src/core/finding-evidence.ts +89 -0
- package/src/core/finding-queue.ts +36 -0
- package/src/core/git.ts +73 -0
- package/src/core/gitignore.ts +52 -0
- package/src/core/heavy-eta.ts +170 -0
- package/src/core/heavy-lease.ts +290 -0
- package/src/core/heavy-rules.ts +373 -0
- package/src/core/heavy-scope.ts +131 -0
- package/src/core/heavy-words.ts +83 -0
- package/src/core/heavy.ts +535 -0
- package/src/core/hygiene.ts +149 -0
- package/src/core/init.ts +218 -0
- package/src/core/live-session.ts +333 -0
- package/src/core/logger.ts +310 -0
- package/src/core/methods.ts +185 -0
- package/src/core/model-fallback.ts +208 -0
- package/src/core/node-resolve.mjs +249 -0
- package/src/core/operation-input.ts +632 -0
- package/src/core/package-root.ts +28 -0
- package/src/core/policy/attributes.ts +97 -0
- package/src/core/policy/bookkeeping.ts +119 -0
- package/src/core/policy/carry.ts +122 -0
- package/src/core/policy/check-evidence.ts +115 -0
- package/src/core/policy/current-judgment.ts +20 -0
- package/src/core/policy/derive.ts +387 -0
- package/src/core/policy/judgment.ts +246 -0
- package/src/core/policy/lane.ts +562 -0
- package/src/core/policy/pr-lane.ts +32 -0
- package/src/core/policy/substance.ts +365 -0
- package/src/core/policy-resolver.ts +94 -0
- package/src/core/pr-create.ts +448 -0
- package/src/core/pr-template.ts +162 -0
- package/src/core/release-notes.ts +197 -0
- package/src/core/release-tracks.ts +779 -0
- package/src/core/remotes.ts +436 -0
- package/src/core/repo-context.ts +542 -0
- package/src/core/repo-paths.ts +29 -0
- package/src/core/resources.ts +1299 -0
- package/src/core/role-models.ts +603 -0
- package/src/core/route-intent.ts +305 -0
- package/src/core/rules.ts +65 -0
- package/src/core/runtime-identity.ts +63 -0
- package/src/core/safe-write.ts +87 -0
- package/src/core/scope-glob.ts +56 -0
- package/src/core/scripts.ts +6 -0
- package/src/core/skill-manifests.ts +173 -0
- package/src/core/store-lock.ts +374 -0
- package/src/core/support-matrix.ts +24 -0
- package/src/core/task-context.ts +247 -0
- package/src/core/task-contract.ts +1279 -0
- package/src/core/task-delivery.ts +109 -0
- package/src/core/task-engine.ts +2248 -0
- package/src/core/task-evaluation.ts +1147 -0
- package/src/core/task-store.ts +2649 -0
- package/src/core/task-view.ts +180 -0
- package/src/core/templates.ts +22 -0
- package/src/core/tracker-issues.ts +174 -0
- package/src/core/vcs-config.ts +540 -0
- package/src/core/workers.ts +62 -0
- package/src/core/workit-paths.mjs +242 -0
- package/src/core/workspace-branch-policy.ts +123 -0
- package/src/core/workspaces.ts +1745 -0
- package/src/core/youtrack-backend.ts +288 -0
- package/src/core/youtrack-tools.ts +260 -0
- package/src/core/youtrack-writes.ts +73 -0
- package/src/core/youtrack-ytrack.ts +217 -0
- package/src/core/youtrack.ts +454 -0
- package/src/core/ytrack-skill.ts +297 -0
- package/src/core.ts +171 -0
- package/src/deslop/lex.ts +271 -0
- package/src/deslop/scan.ts +232 -0
- package/src/duration.ts +27 -0
- package/src/fanout-brief.ts +486 -0
- package/src/fanout-check.ts +455 -0
- package/src/fanout-landed.ts +370 -0
- package/src/fanout-stack.ts +108 -0
- package/src/fanout-status.ts +362 -0
- package/src/fanout-worktree.ts +893 -0
- package/src/fanout.ts +1473 -0
- package/src/forge/exec.ts +385 -0
- package/src/forge/github.ts +831 -0
- package/src/forge/gitlab.ts +906 -0
- package/src/forge/issue-forge.ts +209 -0
- package/src/forge/issue-link.ts +468 -0
- package/src/forge/issue-ops.ts +314 -0
- package/src/forge/merge-gate-base-red.ts +72 -0
- package/src/forge/pr-body.ts +100 -0
- package/src/forge/pr-edit.ts +292 -0
- package/src/forge/pr-ops.ts +1109 -0
- package/src/forge/pr-status.ts +10 -0
- package/src/forge/redact.ts +195 -0
- package/src/forge/report.ts +994 -0
- package/src/forge/reruns.ts +132 -0
- package/src/forge/resolve.ts +509 -0
- package/src/forge/types.ts +242 -0
- package/src/forge/verify.ts +398 -0
- package/src/git/default-base.ts +130 -0
- package/src/git/env-redirect.ts +28 -0
- package/src/git/facts.ts +122 -0
- package/src/git/ops.ts +1823 -0
- package/src/git/refs-read.ts +332 -0
- package/src/git/release.ts +1020 -0
- package/src/git/rev.ts +1175 -0
- package/src/git/session-hook.ts +597 -0
- package/src/git/stage.ts +63 -0
- package/src/hooks/agent-model.ts +149 -0
- package/src/hooks/budget-signal.ts +284 -0
- package/src/hooks/context.ts +461 -0
- package/src/hooks/cwd-normalize.ts +79 -0
- package/src/hooks/deadline.ts +228 -0
- package/src/hooks/delivery-verbs.ts +182 -0
- package/src/hooks/descriptor.ts +124 -0
- package/src/hooks/handle.ts +433 -0
- package/src/hooks/heavy-gate.ts +99 -0
- package/src/hooks/hook-runs.ts +130 -0
- package/src/hooks/hosts/claude-code.ts +380 -0
- package/src/hooks/hosts/codex.ts +372 -0
- package/src/hooks/hosts/cursor.ts +419 -0
- package/src/hooks/hosts/fields.ts +98 -0
- package/src/hooks/hosts/opencode.ts +79 -0
- package/src/hooks/hosts/pi.ts +78 -0
- package/src/hooks/index.ts +60 -0
- package/src/hooks/model-limit.ts +378 -0
- package/src/hooks/model-switch.ts +35 -0
- package/src/hooks/policy.ts +29 -0
- package/src/hooks/process.ts +48 -0
- package/src/hooks/protocol.ts +135 -0
- package/src/hooks/raw-git.ts +2289 -0
- package/src/hooks/repeat-failure.ts +115 -0
- package/src/hooks/run.ts +54 -0
- package/src/hooks/session-marker.ts +151 -0
- package/src/hooks/shell-words.ts +532 -0
- package/src/hooks/skill-nudge.ts +295 -0
- package/src/hooks/spawn-gate.ts +72 -0
- package/src/hooks/standing-signal.ts +99 -0
- package/src/hooks/standing.ts +225 -0
- package/src/hooks/steering.ts +74 -0
- package/src/hooks/stop.ts +408 -0
- package/src/hooks/worktree-confine.ts +415 -0
- package/src/hooks/write-gate.ts +1174 -0
- package/src/host-session.ts +459 -0
- package/src/knowledge.ts +368 -0
- package/src/ledger.ts +2913 -0
- package/src/stack.ts +2570 -0
- package/src/store/event-log.ts +217 -0
- package/src/store/notes.ts +29 -0
- package/src/store/patch.ts +135 -0
- package/src/store/paths.ts +368 -0
- package/src/store/reduce.ts +203 -0
- package/src/test-audit-mocks.ts +309 -0
- package/src/verdict-cover.ts +151 -0
- package/src/worktree-deps.ts +142 -0
- package/templates/headers.md +3 -0
- package/templates/hygiene/.editorconfig +8 -0
- package/templates/hygiene/.gitattributes +3 -0
- package/templates/hygiene/CHANGELOG.md +14 -0
- package/templates/hygiene/CONTRIBUTING.md +3 -0
- package/templates/hygiene/LICENSE +21 -0
- package/templates/hygiene/README.md +3 -0
- package/templates/issue-update.md +5 -0
- package/templates/plan-template.md +26 -0
- package/templates/spec-template.md +64 -0
- package/templates/workit-contract.md +13 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
// Claude Code: command hooks mapped onto the protocol. The plugin that
|
|
2
|
+
// registers them ships separately; this is the field mapping only.
|
|
3
|
+
import type { HostDescriptor } from "../descriptor";
|
|
4
|
+
import type {
|
|
5
|
+
HookDecision,
|
|
6
|
+
HookEvent,
|
|
7
|
+
HookEventKind,
|
|
8
|
+
HostAdapter,
|
|
9
|
+
SessionSource,
|
|
10
|
+
} from "../protocol";
|
|
11
|
+
import {
|
|
12
|
+
commandText,
|
|
13
|
+
existingDirectory,
|
|
14
|
+
isRecord,
|
|
15
|
+
isWriteTool,
|
|
16
|
+
nonEmpty,
|
|
17
|
+
optionalText,
|
|
18
|
+
writeTargets,
|
|
19
|
+
} from "./fields";
|
|
20
|
+
|
|
21
|
+
type ClaudeHookEvent =
|
|
22
|
+
| "SessionStart"
|
|
23
|
+
| "UserPromptSubmit"
|
|
24
|
+
| "PreToolUse"
|
|
25
|
+
| "PostToolUse"
|
|
26
|
+
| "SubagentStart"
|
|
27
|
+
| "SubagentStop"
|
|
28
|
+
| "PreCompact"
|
|
29
|
+
| "Stop";
|
|
30
|
+
|
|
31
|
+
export const CLAUDE_CODE_DESCRIPTOR: HostDescriptor = {
|
|
32
|
+
host: "claude_code",
|
|
33
|
+
label: "Claude Code",
|
|
34
|
+
verifiedAgainst: "claude 2.1.288 (hook zod schemas in the binary)",
|
|
35
|
+
docs: ["https://code.claude.com/docs/en/hooks"],
|
|
36
|
+
transport: "hook-process",
|
|
37
|
+
events: {
|
|
38
|
+
"session.start": { support: "native", native: "SessionStart" },
|
|
39
|
+
"context.turn": { support: "native", native: "UserPromptSubmit" },
|
|
40
|
+
"shell.pre": { support: "native", native: "PreToolUse" },
|
|
41
|
+
"tool.pre": { support: "native", native: "PreToolUse" },
|
|
42
|
+
"write.pre": { support: "native", native: "PreToolUse" },
|
|
43
|
+
"shell.post": { support: "native", native: "PostToolUse" },
|
|
44
|
+
// SubagentStart output is additionalContext only: it cannot block or bind.
|
|
45
|
+
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
46
|
+
"prompt.submit": { support: "native", native: "UserPromptSubmit" },
|
|
47
|
+
// SubagentStop is never blocked: the launcher answers it itself, dropping
|
|
48
|
+
// the stopped agent's tool-call budget files (hooks/budget-signal.ts).
|
|
49
|
+
// Claude Code has PreCompact, but the plugin does not register it: events
|
|
50
|
+
// list what workit registers (docs/agents/hosts.md, Host parity);
|
|
51
|
+
// compaction restore runs on SessionStart source=compact.
|
|
52
|
+
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
53
|
+
"compact.pre": { support: "none", native: null },
|
|
54
|
+
// `{"decision":"block","reason"}` continues the turn; the input carries
|
|
55
|
+
// `stop_hook_active` and `last_assistant_message` (claude 2.1.295).
|
|
56
|
+
stop: { support: "native", native: "Stop" },
|
|
57
|
+
},
|
|
58
|
+
shellPolicy: {
|
|
59
|
+
deny: "native",
|
|
60
|
+
channel: "permissionDecision",
|
|
61
|
+
failClosed: false,
|
|
62
|
+
rewrite: "native",
|
|
63
|
+
},
|
|
64
|
+
context: {
|
|
65
|
+
sessionStart: "native",
|
|
66
|
+
perTurn: "native",
|
|
67
|
+
afterCompact: "native",
|
|
68
|
+
task: "session-bound",
|
|
69
|
+
// Each additionalContext stays in the transcript: resend only on change.
|
|
70
|
+
turnResend: "on-change",
|
|
71
|
+
},
|
|
72
|
+
subagents: {
|
|
73
|
+
identity: "native",
|
|
74
|
+
parentBinding: "partial",
|
|
75
|
+
blockStart: "none",
|
|
76
|
+
worktreeIsolation: "native",
|
|
77
|
+
maxConcurrency: "undocumented",
|
|
78
|
+
// Plugin agents are namespaced: `workit:implementer`, never a bare `workit-implementer`.
|
|
79
|
+
agentPrefix: "workit:",
|
|
80
|
+
},
|
|
81
|
+
provenance: { sessionId: "native", agentIdOnTool: "native", postToolObserve: "native" },
|
|
82
|
+
interaction: { questions: "undocumented", writeBoundary: "partial" },
|
|
83
|
+
stopControl: "native",
|
|
84
|
+
shellAvailable: "native",
|
|
85
|
+
perEventCost: "low",
|
|
86
|
+
capabilities: [
|
|
87
|
+
{
|
|
88
|
+
name: "known_product_writes",
|
|
89
|
+
surface: "PreToolUse",
|
|
90
|
+
refs: ["PreToolUse"],
|
|
91
|
+
requires: [],
|
|
92
|
+
assurance: "unavailable",
|
|
93
|
+
reason: "file writes are host-policy; Claude Code permissions govern them",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "native_subagents",
|
|
97
|
+
// SubagentStop is not registered: identity and guidance come from SubagentStart.
|
|
98
|
+
surface: "SubagentStart",
|
|
99
|
+
refs: ["SubagentStart"],
|
|
100
|
+
requires: ["event:subagent.start"],
|
|
101
|
+
observed: ["subagent.start"],
|
|
102
|
+
assurance: "agent_guided",
|
|
103
|
+
reason: "Claude Code reports stable agent identities, but SubagentStart cannot block or bind",
|
|
104
|
+
unavailableReason: "Claude Code SubagentStart is unavailable",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "fresh-context-review",
|
|
108
|
+
surface: "SubagentStart",
|
|
109
|
+
refs: ["SubagentStart"],
|
|
110
|
+
requires: ["event:subagent.start"],
|
|
111
|
+
observed: ["subagent.start"],
|
|
112
|
+
assurance: "agent_guided",
|
|
113
|
+
reason:
|
|
114
|
+
"independent review runs as a fresh subagent; evidence evaluation enforces reviewer exclusivity",
|
|
115
|
+
unavailableReason: "Claude Code SubagentStart is unavailable for independent review",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "arbitrary_shell_write",
|
|
119
|
+
surface: "unobservable_shell",
|
|
120
|
+
refs: ["PreToolUse"],
|
|
121
|
+
requires: [],
|
|
122
|
+
assurance: "unavailable",
|
|
123
|
+
reason:
|
|
124
|
+
"Only literal Bash commands are interceptable; arbitrary shell writes are not provable",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "compact_context",
|
|
128
|
+
surface: "SessionStart",
|
|
129
|
+
refs: ["SessionStart"],
|
|
130
|
+
requires: ["context.afterCompact"],
|
|
131
|
+
observed: ["session.start"],
|
|
132
|
+
assurance: "agent_guided",
|
|
133
|
+
reason: "SessionStart source=compact is the single restore path",
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const EVENTS: Record<ClaudeHookEvent, HookEventKind> = {
|
|
139
|
+
SessionStart: "session.start",
|
|
140
|
+
UserPromptSubmit: "context.turn",
|
|
141
|
+
PreToolUse: "shell.pre",
|
|
142
|
+
PostToolUse: "shell.post",
|
|
143
|
+
SubagentStart: "subagent.start",
|
|
144
|
+
SubagentStop: "subagent.stop",
|
|
145
|
+
PreCompact: "compact.pre",
|
|
146
|
+
Stop: "stop",
|
|
147
|
+
};
|
|
148
|
+
const SOURCES = new Set<SessionSource>(["startup", "resume", "clear", "compact", "fork"]);
|
|
149
|
+
|
|
150
|
+
type Parsed = { ok: true; event: HookEvent } | { ok: false; error: string };
|
|
151
|
+
|
|
152
|
+
/** Claude's shell tools: Bash everywhere, PowerShell on Windows. */
|
|
153
|
+
const SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
|
|
154
|
+
const dialectOf = (value: Record<string, unknown>) =>
|
|
155
|
+
value.tool_name === "PowerShell" ? ("powershell" as const) : ("posix" as const);
|
|
156
|
+
const toolCommand = (value: Record<string, unknown>): string | null =>
|
|
157
|
+
isRecord(value.tool_input) ? commandText(value.tool_input.command) : null;
|
|
158
|
+
|
|
159
|
+
const eventOf = (name: ClaudeHookEvent, value: Record<string, unknown>): Parsed => {
|
|
160
|
+
const toolUseId = optionalText(value.tool_use_id);
|
|
161
|
+
switch (name) {
|
|
162
|
+
case "SessionStart":
|
|
163
|
+
return SOURCES.has(value.source as SessionSource)
|
|
164
|
+
? { ok: true, event: { kind: "session.start", source: value.source as SessionSource } }
|
|
165
|
+
: { ok: false, error: "SessionStart source is required" };
|
|
166
|
+
case "UserPromptSubmit":
|
|
167
|
+
return { ok: true, event: { kind: "context.turn", prompt: optionalText(value.prompt) } };
|
|
168
|
+
case "PreToolUse": {
|
|
169
|
+
if (!nonEmpty(value.tool_name)) return { ok: false, error: "tool_name is required" };
|
|
170
|
+
if (isWriteTool(value.tool_name))
|
|
171
|
+
return {
|
|
172
|
+
ok: true,
|
|
173
|
+
event: {
|
|
174
|
+
kind: "write.pre",
|
|
175
|
+
tool: value.tool_name,
|
|
176
|
+
paths: writeTargets(value.tool_input),
|
|
177
|
+
toolUseId,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
if (value.tool_name === "Skill")
|
|
181
|
+
return {
|
|
182
|
+
ok: true,
|
|
183
|
+
event: {
|
|
184
|
+
kind: "tool.pre",
|
|
185
|
+
tool: value.tool_name,
|
|
186
|
+
toolUseId,
|
|
187
|
+
skill: isRecord(value.tool_input) ? optionalText(value.tool_input.skill) : null,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
// `Task` is the Agent tool's legacy name.
|
|
191
|
+
if (value.tool_name === "Agent" || value.tool_name === "Task")
|
|
192
|
+
return {
|
|
193
|
+
ok: true,
|
|
194
|
+
event: {
|
|
195
|
+
kind: "tool.pre",
|
|
196
|
+
tool: value.tool_name,
|
|
197
|
+
toolUseId,
|
|
198
|
+
...(isRecord(value.tool_input) ? { agentInput: value.tool_input } : {}),
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
if (!SHELL_TOOLS.has(value.tool_name))
|
|
202
|
+
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
203
|
+
const command = toolCommand(value);
|
|
204
|
+
return command
|
|
205
|
+
? {
|
|
206
|
+
ok: true,
|
|
207
|
+
event: {
|
|
208
|
+
kind: "shell.pre",
|
|
209
|
+
command,
|
|
210
|
+
toolUseId,
|
|
211
|
+
dialect: dialectOf(value),
|
|
212
|
+
toolInput: value.tool_input as Record<string, unknown>,
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
: { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
|
|
216
|
+
}
|
|
217
|
+
case "PostToolUse": {
|
|
218
|
+
const command = toolCommand(value);
|
|
219
|
+
if (!SHELL_TOOLS.has(String(value.tool_name)) || !command)
|
|
220
|
+
return { ok: false, error: "only shell PostToolUse is mapped" };
|
|
221
|
+
const response = isRecord(value.tool_response) ? value.tool_response : {};
|
|
222
|
+
return {
|
|
223
|
+
ok: true,
|
|
224
|
+
event: {
|
|
225
|
+
kind: "shell.post",
|
|
226
|
+
command,
|
|
227
|
+
stdout: optionalText(response.stdout) ?? "",
|
|
228
|
+
exitCode: typeof response.exit_code === "number" ? response.exit_code : null,
|
|
229
|
+
toolUseId,
|
|
230
|
+
dialect: dialectOf(value),
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
case "SubagentStart":
|
|
235
|
+
return nonEmpty(value.agent_id) && nonEmpty(value.agent_type)
|
|
236
|
+
? {
|
|
237
|
+
ok: true,
|
|
238
|
+
event: {
|
|
239
|
+
kind: "subagent.start",
|
|
240
|
+
agentId: value.agent_id,
|
|
241
|
+
agentType: value.agent_type,
|
|
242
|
+
task: null,
|
|
243
|
+
},
|
|
244
|
+
}
|
|
245
|
+
: { ok: false, error: "agent_id and agent_type are required" };
|
|
246
|
+
case "SubagentStop":
|
|
247
|
+
return {
|
|
248
|
+
ok: true,
|
|
249
|
+
event: {
|
|
250
|
+
kind: "subagent.stop",
|
|
251
|
+
agentId: optionalText(value.agent_id),
|
|
252
|
+
agentType: optionalText(value.agent_type),
|
|
253
|
+
lastMessage: optionalText(value.last_assistant_message),
|
|
254
|
+
stopHookActive: value.stop_hook_active === true,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
case "PreCompact":
|
|
258
|
+
return {
|
|
259
|
+
ok: true,
|
|
260
|
+
event: { kind: "compact.pre", trigger: value.trigger === "manual" ? "manual" : "auto" },
|
|
261
|
+
};
|
|
262
|
+
case "Stop":
|
|
263
|
+
return {
|
|
264
|
+
ok: true,
|
|
265
|
+
event: {
|
|
266
|
+
kind: "stop",
|
|
267
|
+
lastMessage: optionalText(value.last_assistant_message),
|
|
268
|
+
stopHookActive: value.stop_hook_active === true,
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/** Events whose hookSpecificOutput takes additionalContext (PreToolUse: a raw-git nudge). */
|
|
275
|
+
const CONTEXT_EVENTS = new Set([
|
|
276
|
+
"SessionStart",
|
|
277
|
+
"UserPromptSubmit",
|
|
278
|
+
"SubagentStart",
|
|
279
|
+
"PreToolUse",
|
|
280
|
+
"PostToolUse",
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Never emits `allow`: that would bypass the user's own permission prompt. A
|
|
285
|
+
* rewrite is `updatedInput` with no permissionDecision: Claude Code applies it
|
|
286
|
+
* and runs its normal permission flow on the rewritten command (claude
|
|
287
|
+
* 2.1.306: a hook result with updatedInput and no permissionBehavior is
|
|
288
|
+
* yielded as the input to run).
|
|
289
|
+
*/
|
|
290
|
+
const render = (decision: HookDecision, native: string | null) => {
|
|
291
|
+
if (
|
|
292
|
+
(decision.kind === "rewrite" || decision.kind === "update-input") &&
|
|
293
|
+
native === "PreToolUse" &&
|
|
294
|
+
decision.input
|
|
295
|
+
)
|
|
296
|
+
return {
|
|
297
|
+
json: {
|
|
298
|
+
hookSpecificOutput: {
|
|
299
|
+
hookEventName: "PreToolUse",
|
|
300
|
+
updatedInput: decision.input,
|
|
301
|
+
...(decision.text ? { additionalContext: decision.text } : {}),
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
exitCode: 0,
|
|
305
|
+
};
|
|
306
|
+
if (decision.kind === "deny" && native === "PreToolUse")
|
|
307
|
+
return {
|
|
308
|
+
json: {
|
|
309
|
+
hookSpecificOutput: {
|
|
310
|
+
hookEventName: "PreToolUse",
|
|
311
|
+
permissionDecision: "deny",
|
|
312
|
+
permissionDecisionReason: decision.reason,
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
exitCode: 0,
|
|
316
|
+
};
|
|
317
|
+
if (decision.kind === "ask" && native === "PreToolUse")
|
|
318
|
+
return {
|
|
319
|
+
json: {
|
|
320
|
+
hookSpecificOutput: {
|
|
321
|
+
hookEventName: "PreToolUse",
|
|
322
|
+
permissionDecision: "ask",
|
|
323
|
+
permissionDecisionReason: decision.reason,
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
exitCode: 0,
|
|
327
|
+
};
|
|
328
|
+
if (decision.kind === "context" && native !== null && CONTEXT_EVENTS.has(native))
|
|
329
|
+
return {
|
|
330
|
+
json: { hookSpecificOutput: { hookEventName: native, additionalContext: decision.text } },
|
|
331
|
+
exitCode: 0,
|
|
332
|
+
};
|
|
333
|
+
// PreCompact has no hookSpecificOutput; the common systemMessage field
|
|
334
|
+
// shows the notice to the user.
|
|
335
|
+
if (decision.kind === "notice")
|
|
336
|
+
return { json: { systemMessage: decision.userMessage }, exitCode: 0 };
|
|
337
|
+
if (decision.kind === "continue" && (native === "Stop" || native === "SubagentStop"))
|
|
338
|
+
return { json: { decision: "block", reason: decision.reason }, exitCode: 0 };
|
|
339
|
+
return { json: {}, exitCode: 0 };
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export const claudeCodeAdapter: HostAdapter = {
|
|
343
|
+
descriptor: CLAUDE_CODE_DESCRIPTOR,
|
|
344
|
+
parse(raw) {
|
|
345
|
+
const native =
|
|
346
|
+
isRecord(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name))
|
|
347
|
+
? (raw.hook_event_name as ClaudeHookEvent)
|
|
348
|
+
: null;
|
|
349
|
+
const fail = (error: string) => ({
|
|
350
|
+
ok: false as const,
|
|
351
|
+
error,
|
|
352
|
+
native,
|
|
353
|
+
event: native ? EVENTS[native] : null,
|
|
354
|
+
});
|
|
355
|
+
if (!isRecord(raw) || !native) return fail("hook_event_name is required");
|
|
356
|
+
if (!nonEmpty(raw.session_id)) return fail("session_id is required");
|
|
357
|
+
const cwd = existingDirectory(raw.cwd);
|
|
358
|
+
if (!cwd) return fail("cwd must be an existing absolute directory");
|
|
359
|
+
const event = eventOf(native, raw);
|
|
360
|
+
if (!event.ok) return fail(event.error);
|
|
361
|
+
return {
|
|
362
|
+
ok: true,
|
|
363
|
+
native,
|
|
364
|
+
input: {
|
|
365
|
+
host: "claude_code",
|
|
366
|
+
cwd,
|
|
367
|
+
session: {
|
|
368
|
+
id: raw.session_id,
|
|
369
|
+
agentId: optionalText(raw.agent_id),
|
|
370
|
+
agentType: optionalText(raw.agent_type),
|
|
371
|
+
parentId: nonEmpty(raw.agent_id) ? raw.session_id : null,
|
|
372
|
+
},
|
|
373
|
+
permissionMode: optionalText(raw.permission_mode),
|
|
374
|
+
transcriptPath: optionalText(raw.transcript_path),
|
|
375
|
+
event: event.event,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
},
|
|
379
|
+
render,
|
|
380
|
+
};
|