@smeltjs/core 0.2.0 → 0.4.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/README.md +3 -3
- package/dist/agents/guide.d.ts +68 -0
- package/dist/agents/guide.d.ts.map +1 -0
- package/dist/agents/guide.js +72 -0
- package/dist/agents/guide.js.map +1 -0
- package/dist/agents/instructions.d.ts +133 -0
- package/dist/agents/instructions.d.ts.map +1 -0
- package/dist/agents/instructions.js +212 -0
- package/dist/agents/instructions.js.map +1 -0
- package/dist/agents/lint.d.ts +154 -0
- package/dist/agents/lint.d.ts.map +1 -0
- package/dist/agents/lint.js +761 -0
- package/dist/agents/lint.js.map +1 -0
- package/dist/agents/split.d.ts +113 -0
- package/dist/agents/split.d.ts.map +1 -0
- package/dist/agents/split.js +234 -0
- package/dist/agents/split.js.map +1 -0
- package/dist/apply.d.ts.map +1 -1
- package/dist/apply.js.map +1 -1
- package/dist/cache/prefix.d.ts.map +1 -1
- package/dist/cache/prefix.js.map +1 -1
- package/dist/cli/agents.d.ts +39 -0
- package/dist/cli/agents.d.ts.map +1 -0
- package/dist/cli/agents.js +121 -0
- package/dist/cli/agents.js.map +1 -0
- package/dist/cli/args.d.ts +17 -70
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +18 -338
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/bin.d.ts.map +1 -1
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/config.d.ts +60 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +89 -2
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/hooks.d.ts +55 -51
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +206 -658
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/init.d.ts +30 -15
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +167 -38
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/report.d.ts +23 -0
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +142 -4
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/run.d.ts +29 -90
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +34 -342
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/shell.d.ts +106 -0
- package/dist/cli/shell.d.ts.map +1 -0
- package/dist/cli/shell.js +100 -0
- package/dist/cli/shell.js.map +1 -0
- package/dist/cli/subcommands/agents.d.ts +72 -0
- package/dist/cli/subcommands/agents.d.ts.map +1 -0
- package/dist/cli/subcommands/agents.js +162 -0
- package/dist/cli/subcommands/agents.js.map +1 -0
- package/dist/cli/subcommands/flags.d.ts +144 -0
- package/dist/cli/subcommands/flags.d.ts.map +1 -0
- package/dist/cli/subcommands/flags.js +220 -0
- package/dist/cli/subcommands/flags.js.map +1 -0
- package/dist/cli/subcommands/hooks.d.ts +18 -0
- package/dist/cli/subcommands/hooks.d.ts.map +1 -0
- package/dist/cli/subcommands/hooks.js +80 -0
- package/dist/cli/subcommands/hooks.js.map +1 -0
- package/dist/cli/subcommands/init.d.ts +19 -0
- package/dist/cli/subcommands/init.d.ts.map +1 -0
- package/dist/cli/subcommands/init.js +44 -0
- package/dist/cli/subcommands/init.js.map +1 -0
- package/dist/cli/subcommands/map.d.ts +75 -0
- package/dist/cli/subcommands/map.d.ts.map +1 -0
- package/dist/cli/subcommands/map.js +132 -0
- package/dist/cli/subcommands/map.js.map +1 -0
- package/dist/cli/subcommands/registry.d.ts +81 -0
- package/dist/cli/subcommands/registry.d.ts.map +1 -0
- package/dist/cli/subcommands/registry.js +141 -0
- package/dist/cli/subcommands/registry.js.map +1 -0
- package/dist/cli/subcommands/retrieve.d.ts +74 -0
- package/dist/cli/subcommands/retrieve.d.ts.map +1 -0
- package/dist/cli/subcommands/retrieve.js +84 -0
- package/dist/cli/subcommands/retrieve.js.map +1 -0
- package/dist/cli/subcommands/smelt.d.ts +107 -0
- package/dist/cli/subcommands/smelt.d.ts.map +1 -0
- package/dist/cli/subcommands/smelt.js +304 -0
- package/dist/cli/subcommands/smelt.js.map +1 -0
- package/dist/cli/subcommands/stats.d.ts +43 -0
- package/dist/cli/subcommands/stats.d.ts.map +1 -0
- package/dist/cli/subcommands/stats.js +62 -0
- package/dist/cli/subcommands/stats.js.map +1 -0
- package/dist/cli/subcommands/subcommand.d.ts +113 -0
- package/dist/cli/subcommands/subcommand.d.ts.map +1 -0
- package/dist/cli/subcommands/subcommand.js +3 -0
- package/dist/cli/subcommands/subcommand.js.map +1 -0
- package/dist/cli/usage.d.ts +3 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +95 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js.map +1 -1
- package/dist/errors.d.ts +21 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -1
- package/dist/errors.js.map +1 -1
- package/dist/harness/aider.d.ts +10 -0
- package/dist/harness/aider.d.ts.map +1 -0
- package/dist/harness/aider.js +21 -0
- package/dist/harness/aider.js.map +1 -0
- package/dist/harness/claude-code.d.ts +3 -0
- package/dist/harness/claude-code.d.ts.map +1 -0
- package/dist/harness/claude-code.js +68 -0
- package/dist/harness/claude-code.js.map +1 -0
- package/dist/harness/cline.d.ts +3 -0
- package/dist/harness/cline.d.ts.map +1 -0
- package/dist/harness/cline.js +54 -0
- package/dist/harness/cline.js.map +1 -0
- package/dist/harness/codex.d.ts +3 -0
- package/dist/harness/codex.d.ts.map +1 -0
- package/dist/harness/codex.js +92 -0
- package/dist/harness/codex.js.map +1 -0
- package/dist/harness/cursor.d.ts +3 -0
- package/dist/harness/cursor.d.ts.map +1 -0
- package/dist/harness/cursor.js +54 -0
- package/dist/harness/cursor.js.map +1 -0
- package/dist/harness/gemini.d.ts +3 -0
- package/dist/harness/gemini.d.ts.map +1 -0
- package/dist/harness/gemini.js +60 -0
- package/dist/harness/gemini.js.map +1 -0
- package/dist/harness/grok.d.ts +3 -0
- package/dist/harness/grok.d.ts.map +1 -0
- package/dist/harness/grok.js +47 -0
- package/dist/harness/grok.js.map +1 -0
- package/dist/harness/hermes.d.ts +3 -0
- package/dist/harness/hermes.d.ts.map +1 -0
- package/dist/harness/hermes.js +72 -0
- package/dist/harness/hermes.js.map +1 -0
- package/dist/harness/kilocode.d.ts +3 -0
- package/dist/harness/kilocode.d.ts.map +1 -0
- package/dist/harness/kilocode.js +35 -0
- package/dist/harness/kilocode.js.map +1 -0
- package/dist/harness/opencode.d.ts +3 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +97 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/paths.d.ts +17 -0
- package/dist/harness/paths.d.ts.map +1 -0
- package/dist/harness/paths.js +45 -0
- package/dist/harness/paths.js.map +1 -0
- package/dist/harness/profile.d.ts +209 -0
- package/dist/harness/profile.d.ts.map +1 -0
- package/dist/harness/profile.js +41 -0
- package/dist/harness/profile.js.map +1 -0
- package/dist/harness/registry.d.ts +100 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +144 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/snippet.d.ts +19 -0
- package/dist/harness/snippet.d.ts.map +1 -0
- package/dist/harness/snippet.js +41 -0
- package/dist/harness/snippet.js.map +1 -0
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js.map +1 -1
- package/dist/hooks/guard-core.d.ts +14 -14
- package/dist/hooks/guard-core.d.ts.map +1 -1
- package/dist/hooks/guard-core.js +15 -37
- package/dist/hooks/guard-core.js.map +1 -1
- package/dist/hooks/shim.d.ts +95 -4
- package/dist/hooks/shim.d.ts.map +1 -1
- package/dist/hooks/shim.js +87 -2
- package/dist/hooks/shim.js.map +1 -1
- package/dist/hooks/shims/claude-code.d.ts +5 -18
- package/dist/hooks/shims/claude-code.d.ts.map +1 -1
- package/dist/hooks/shims/claude-code.js +9 -56
- package/dist/hooks/shims/claude-code.js.map +1 -1
- package/dist/hooks/shims/cline.d.ts +5 -12
- package/dist/hooks/shims/cline.d.ts.map +1 -1
- package/dist/hooks/shims/cline.js +9 -34
- package/dist/hooks/shims/cline.js.map +1 -1
- package/dist/hooks/shims/codex.d.ts +5 -18
- package/dist/hooks/shims/codex.d.ts.map +1 -1
- package/dist/hooks/shims/codex.js +9 -51
- package/dist/hooks/shims/codex.js.map +1 -1
- package/dist/hooks/shims/cursor.d.ts +5 -14
- package/dist/hooks/shims/cursor.d.ts.map +1 -1
- package/dist/hooks/shims/cursor.js +9 -42
- package/dist/hooks/shims/cursor.js.map +1 -1
- package/dist/hooks/shims/gemini.d.ts +5 -18
- package/dist/hooks/shims/gemini.d.ts.map +1 -1
- package/dist/hooks/shims/gemini.js +9 -48
- package/dist/hooks/shims/gemini.js.map +1 -1
- package/dist/hooks/shims/grok.d.ts +5 -13
- package/dist/hooks/shims/grok.d.ts.map +1 -1
- package/dist/hooks/shims/grok.js +9 -32
- package/dist/hooks/shims/grok.js.map +1 -1
- package/dist/hooks/shims/hermes.d.ts +5 -17
- package/dist/hooks/shims/hermes.d.ts.map +1 -1
- package/dist/hooks/shims/hermes.js +9 -45
- package/dist/hooks/shims/hermes.js.map +1 -1
- package/dist/index.d.ts +43 -76
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -67
- package/dist/index.js.map +1 -1
- package/dist/lang/bash.d.ts.map +1 -1
- package/dist/lang/bash.js.map +1 -1
- package/dist/lang/c.d.ts.map +1 -1
- package/dist/lang/c.js.map +1 -1
- package/dist/lang/c_sharp.d.ts.map +1 -1
- package/dist/lang/c_sharp.js.map +1 -1
- package/dist/lang/cpp.d.ts.map +1 -1
- package/dist/lang/cpp.js.map +1 -1
- package/dist/lang/go.d.ts.map +1 -1
- package/dist/lang/go.js.map +1 -1
- package/dist/lang/java.d.ts.map +1 -1
- package/dist/lang/java.js.map +1 -1
- package/dist/lang/javascript.d.ts.map +1 -1
- package/dist/lang/javascript.js.map +1 -1
- package/dist/lang/kotlin.d.ts.map +1 -1
- package/dist/lang/kotlin.js.map +1 -1
- package/dist/lang/php.d.ts.map +1 -1
- package/dist/lang/php.js.map +1 -1
- package/dist/lang/profile.d.ts.map +1 -1
- package/dist/lang/profile.js.map +1 -1
- package/dist/lang/python.d.ts.map +1 -1
- package/dist/lang/python.js.map +1 -1
- package/dist/lang/registry.d.ts.map +1 -1
- package/dist/lang/registry.js.map +1 -1
- package/dist/lang/ruby.d.ts.map +1 -1
- package/dist/lang/ruby.js.map +1 -1
- package/dist/lang/rust.d.ts.map +1 -1
- package/dist/lang/rust.js.map +1 -1
- package/dist/lang/swift.d.ts.map +1 -1
- package/dist/lang/swift.js.map +1 -1
- package/dist/lang/tsx.d.ts.map +1 -1
- package/dist/lang/tsx.js.map +1 -1
- package/dist/lang/typescript.d.ts.map +1 -1
- package/dist/lang/typescript.js.map +1 -1
- package/dist/net/policy.d.ts +18 -1
- package/dist/net/policy.d.ts.map +1 -1
- package/dist/net/policy.js +6 -4
- package/dist/net/policy.js.map +1 -1
- package/dist/ops/index.d.ts +39 -0
- package/dist/ops/index.d.ts.map +1 -0
- package/dist/ops/index.js +37 -0
- package/dist/ops/index.js.map +1 -0
- package/dist/ops/inputs.d.ts +170 -0
- package/dist/ops/inputs.d.ts.map +1 -0
- package/dist/ops/inputs.js +142 -0
- package/dist/ops/inputs.js.map +1 -0
- package/dist/ops/verbs.d.ts +136 -0
- package/dist/ops/verbs.d.ts.map +1 -0
- package/dist/ops/verbs.js +76 -0
- package/dist/ops/verbs.js.map +1 -0
- package/dist/plan/auto.d.ts +69 -0
- package/dist/plan/auto.d.ts.map +1 -0
- package/dist/plan/auto.js +70 -0
- package/dist/plan/auto.js.map +1 -0
- package/dist/plan/budget.d.ts +21 -0
- package/dist/plan/budget.d.ts.map +1 -0
- package/dist/plan/budget.js +26 -0
- package/dist/plan/budget.js.map +1 -0
- package/dist/plan/grammar.d.ts +14 -0
- package/dist/plan/grammar.d.ts.map +1 -1
- package/dist/plan/grammar.js +47 -6
- package/dist/plan/grammar.js.map +1 -1
- package/dist/plan/lexical.d.ts.map +1 -1
- package/dist/plan/lexical.js +1 -7
- package/dist/plan/lexical.js.map +1 -1
- package/dist/plan/planners.d.ts +35 -2
- package/dist/plan/planners.d.ts.map +1 -1
- package/dist/plan/planners.js +35 -2
- package/dist/plan/planners.js.map +1 -1
- package/dist/plan/structural.d.ts +19 -5
- package/dist/plan/structural.d.ts.map +1 -1
- package/dist/plan/structural.js +180 -33
- package/dist/plan/structural.js.map +1 -1
- package/dist/repomap/cache.d.ts +48 -0
- package/dist/repomap/cache.d.ts.map +1 -1
- package/dist/repomap/cache.js +112 -13
- package/dist/repomap/cache.js.map +1 -1
- package/dist/repomap/io.d.ts +24 -0
- package/dist/repomap/io.d.ts.map +1 -0
- package/dist/repomap/io.js +34 -0
- package/dist/repomap/io.js.map +1 -0
- package/dist/repomap/map.d.ts +69 -6
- package/dist/repomap/map.d.ts.map +1 -1
- package/dist/repomap/map.js +104 -22
- package/dist/repomap/map.js.map +1 -1
- package/dist/repomap/rank.d.ts +26 -2
- package/dist/repomap/rank.d.ts.map +1 -1
- package/dist/repomap/rank.js +20 -0
- package/dist/repomap/rank.js.map +1 -1
- package/dist/repomap/reader.d.ts +73 -0
- package/dist/repomap/reader.d.ts.map +1 -0
- package/dist/repomap/reader.js +30 -0
- package/dist/repomap/reader.js.map +1 -0
- package/dist/repomap/tags.d.ts.map +1 -1
- package/dist/repomap/tags.js.map +1 -1
- package/dist/retrieve.d.ts +8 -0
- package/dist/retrieve.d.ts.map +1 -1
- package/dist/retrieve.js +12 -1
- package/dist/retrieve.js.map +1 -1
- package/dist/smelter.d.ts +79 -0
- package/dist/smelter.d.ts.map +1 -0
- package/dist/smelter.js +65 -0
- package/dist/smelter.js.map +1 -0
- package/dist/stages.d.ts.map +1 -1
- package/dist/stages.js +2 -2
- package/dist/stages.js.map +1 -1
- package/dist/stats.d.ts +2 -1
- package/dist/stats.d.ts.map +1 -1
- package/dist/stats.js +2 -1
- package/dist/stats.js.map +1 -1
- package/dist/store-dir.d.ts +43 -4
- package/dist/store-dir.d.ts.map +1 -1
- package/dist/store-dir.js +48 -5
- package/dist/store-dir.js.map +1 -1
- package/dist/store.d.ts +6 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +6 -0
- package/dist/store.js.map +1 -1
- package/dist/text/json-edit.d.ts +71 -0
- package/dist/text/json-edit.d.ts.map +1 -0
- package/dist/text/json-edit.js +219 -0
- package/dist/text/json-edit.js.map +1 -0
- package/dist/types.d.ts +35 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +11 -11
- package/dist/cli/resolve.d.ts +0 -103
- package/dist/cli/resolve.d.ts.map +0 -1
- package/dist/cli/resolve.js +0 -106
- package/dist/cli/resolve.js.map +0 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code — VERIFIED tier. Schema, install and removal, end to end.
|
|
3
|
+
*
|
|
4
|
+
* Schema per <https://code.claude.com/docs/en/hooks> (verified 2026-09-02; the deep
|
|
5
|
+
* dive is docs/research/2026-09-02-agent-enforcement.md § 1):
|
|
6
|
+
*
|
|
7
|
+
* - stdin: `{ hook_event_name: "PreToolUse", tool_name, tool_input, cwd }`. For
|
|
8
|
+
* `Read`, `tool_input.file_path` is already absolute and `offset`/`limit` mark a
|
|
9
|
+
* windowed read; for `Bash`, `tool_input.command` is the full command string, and
|
|
10
|
+
* a relative path in it resolves against the payload's `cwd` — the *session's*
|
|
11
|
+
* working directory, which after the model `cd`s differs from the hook process's
|
|
12
|
+
* own cwd.
|
|
13
|
+
* - deny: `hookSpecificOutput.permissionDecision: "deny"` with
|
|
14
|
+
* `permissionDecisionReason` — which is **shown to the model**, so the guard's
|
|
15
|
+
* reason (the exact replacement command, the `smelt retrieve` contract) lands in
|
|
16
|
+
* the transcript as steering.
|
|
17
|
+
* - rewrite (opt-in, `hooks.enforcement: "rewrite"`): `updatedInput` replaces the
|
|
18
|
+
* entire input object of the *same* tool (v2.0.10+), so a Bash command can be
|
|
19
|
+
* substituted but a Read can never become a Bash call — Reads deny in every mode.
|
|
20
|
+
*/
|
|
21
|
+
const HOOKS = {
|
|
22
|
+
readTools: ['Read'],
|
|
23
|
+
bashTools: ['Bash'],
|
|
24
|
+
toolNameKeys: ['tool_name'],
|
|
25
|
+
toolInputKeys: ['tool_input'],
|
|
26
|
+
cwdKey: 'cwd',
|
|
27
|
+
deny: (reason) => ({
|
|
28
|
+
hookSpecificOutput: {
|
|
29
|
+
hookEventName: 'PreToolUse',
|
|
30
|
+
permissionDecision: 'deny',
|
|
31
|
+
permissionDecisionReason: reason,
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
rewrite: {
|
|
35
|
+
document: ({ input, announcement }) => ({
|
|
36
|
+
hookSpecificOutput: {
|
|
37
|
+
hookEventName: 'PreToolUse',
|
|
38
|
+
permissionDecision: 'allow',
|
|
39
|
+
permissionDecisionReason: announcement,
|
|
40
|
+
// updatedInput replaces the whole input object — unchanged fields ride along.
|
|
41
|
+
updatedInput: input,
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
announce: 'reason',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
export const claudeCode = {
|
|
48
|
+
id: 'claude-code',
|
|
49
|
+
name: 'Claude Code',
|
|
50
|
+
tier: 'verified',
|
|
51
|
+
detect: ['.claude'],
|
|
52
|
+
detectHome: ['.claude'],
|
|
53
|
+
instructionFile: 'CLAUDE.md',
|
|
54
|
+
instructions: 'snippet',
|
|
55
|
+
caveats: [],
|
|
56
|
+
hooks: HOOKS,
|
|
57
|
+
install: [
|
|
58
|
+
{
|
|
59
|
+
kind: 'json-hooks',
|
|
60
|
+
file: '.claude/settings.json',
|
|
61
|
+
event: 'PreToolUse',
|
|
62
|
+
matchers: ['Read', 'Bash'],
|
|
63
|
+
entry: 'command-list',
|
|
64
|
+
lifecycle: true,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/harness/claude-code.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,KAAK,GAAsB;IAC/B,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,YAAY,EAAE,CAAC,WAAW,CAAC;IAC3B,aAAa,EAAE,CAAC,YAAY,CAAC;IAC7B,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACjB,kBAAkB,EAAE;YAClB,aAAa,EAAE,YAAY;YAC3B,kBAAkB,EAAE,MAAM;YAC1B,wBAAwB,EAAE,MAAM;SACjC;KACF,CAAC;IACF,OAAO,EAAE;QACP,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;YACtC,kBAAkB,EAAE;gBAClB,aAAa,EAAE,YAAY;gBAC3B,kBAAkB,EAAE,OAAO;gBAC3B,wBAAwB,EAAE,YAAY;gBACtC,8EAA8E;gBAC9E,YAAY,EAAE,KAAK;aACpB;SACF,CAAC;QACF,QAAQ,EAAE,QAAQ;KACnB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAA0B;IAC/C,EAAE,EAAE,aAAa;IACjB,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,CAAC,SAAS,CAAC;IACnB,UAAU,EAAE,CAAC,SAAS,CAAC;IACvB,eAAe,EAAE,WAAW;IAC5B,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE;QACP;YACE,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,uBAAuB;YAC7B,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;YAC1B,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,IAAI;SAChB;KACF;CACF,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Claude Code — VERIFIED tier. Schema, install and removal, end to end.\n *\n * Schema per <https://code.claude.com/docs/en/hooks> (verified 2026-09-02; the deep\n * dive is docs/research/2026-09-02-agent-enforcement.md § 1):\n *\n * - stdin: `{ hook_event_name: \"PreToolUse\", tool_name, tool_input, cwd }`. For\n * `Read`, `tool_input.file_path` is already absolute and `offset`/`limit` mark a\n * windowed read; for `Bash`, `tool_input.command` is the full command string, and\n * a relative path in it resolves against the payload's `cwd` — the *session's*\n * working directory, which after the model `cd`s differs from the hook process's\n * own cwd.\n * - deny: `hookSpecificOutput.permissionDecision: \"deny\"` with\n * `permissionDecisionReason` — which is **shown to the model**, so the guard's\n * reason (the exact replacement command, the `smelt retrieve` contract) lands in\n * the transcript as steering.\n * - rewrite (opt-in, `hooks.enforcement: \"rewrite\"`): `updatedInput` replaces the\n * entire input object of the *same* tool (v2.0.10+), so a Bash command can be\n * substituted but a Read can never become a Bash call — Reads deny in every mode.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['Read'],\n bashTools: ['Bash'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n cwdKey: 'cwd',\n deny: (reason) => ({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'deny',\n permissionDecisionReason: reason,\n },\n }),\n rewrite: {\n document: ({ input, announcement }) => ({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'allow',\n permissionDecisionReason: announcement,\n // updatedInput replaces the whole input object — unchanged fields ride along.\n updatedInput: input,\n },\n }),\n announce: 'reason',\n },\n};\n\nexport const claudeCode: ShimmedHarnessProfile = {\n id: 'claude-code',\n name: 'Claude Code',\n tier: 'verified',\n detect: ['.claude'],\n detectHome: ['.claude'],\n instructionFile: 'CLAUDE.md',\n instructions: 'snippet',\n caveats: [],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.claude/settings.json',\n event: 'PreToolUse',\n matchers: ['Read', 'Bash'],\n entry: 'command-list',\n lifecycle: true,\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cline.d.ts","sourceRoot":"","sources":["../../src/harness/cline.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAyB,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAoCjF,eAAO,MAAM,KAAK,EAAE,qBAmBnB,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport { nodeCommand, shimScriptPath } from './paths.ts';\nimport type { HarnessInstallContext, ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Cline — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * Schema mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Cline row; primary source\n * <https://docs.cline.bot/features/hooks>), not yet smoke-tested against the real\n * binary.\n *\n * - event: `PreToolUse`, delivered to an executable under `.clinerules/hooks/` —\n * hence the two-line wrapper below, which is the file Cline runs; stdin carries the\n * tool call (accepted spellings: `tool_name`/`tool_input`, `toolName`/`toolInput`,\n * or nested under `preToolUse`).\n * - deny: `{ \"cancel\": true, \"errorMessage\": … }`. **Deny-only**: the response has no\n * input-modification field, so under `hooks.enforcement: \"rewrite\"` this harness\n * falls back to the deny, whose reason still carries the exact replacement pipeline.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['Read', 'read_file', 'readFile'],\n bashTools: ['Bash', 'execute_command', 'executeCommand', 'shell'],\n toolNameKeys: ['tool_name', 'toolName', 'preToolUse.toolName', 'preToolUse.tool'],\n toolInputKeys: ['tool_input', 'toolInput', 'preToolUse.toolInput', 'preToolUse.input'],\n deny: (reason) => ({ cancel: true, errorMessage: reason }),\n};\n\n/** Cline's hook is an executable file; this two-liner hands it to the cline shim. */\nfunction clineHookSource(ctx: HarnessInstallContext): string {\n return `#!/bin/sh\n# smelt:hooks v1 — Cline PreToolUse hook. EXPERIMENTAL tier: schema mapped from the\n# capability matrix (docs/research/2026-09-02-harness-capability-matrix.md, Cline row),\n# not yet smoke-tested against the real binary. Written by \\`smelt hooks install\\`.\nexec ${nodeCommand(ctx.cwd, shimScriptPath(cline))}\n`;\n}\n\nexport const cline: ShimmedHarnessProfile = {\n id: 'cline',\n name: 'Cline',\n tier: 'experimental',\n detect: ['.clinerules'],\n detectHome: [],\n instructionFile: '.clinerules/smelt.md',\n instructions: 'snippet',\n caveats: ['deny-only hooks: input rewrite is not supported, so rewrite mode falls back to deny'],\n hooks: HOOKS,\n install: [\n {\n kind: 'own-file',\n file: '.clinerules/hooks/PreToolUse',\n content: clineHookSource,\n mode: 0o755,\n guardOnly: true,\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { nodeCommand, shimScriptPath } from './paths.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cline — EXPERIMENTAL tier. Schema, install and removal, end to end.
|
|
4
|
+
*
|
|
5
|
+
* Schema mapped from the capability matrix
|
|
6
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Cline row; primary source
|
|
7
|
+
* <https://docs.cline.bot/features/hooks>), not yet smoke-tested against the real
|
|
8
|
+
* binary.
|
|
9
|
+
*
|
|
10
|
+
* - event: `PreToolUse`, delivered to an executable under `.clinerules/hooks/` —
|
|
11
|
+
* hence the two-line wrapper below, which is the file Cline runs; stdin carries the
|
|
12
|
+
* tool call (accepted spellings: `tool_name`/`tool_input`, `toolName`/`toolInput`,
|
|
13
|
+
* or nested under `preToolUse`).
|
|
14
|
+
* - deny: `{ "cancel": true, "errorMessage": … }`. **Deny-only**: the response has no
|
|
15
|
+
* input-modification field, so under `hooks.enforcement: "rewrite"` this harness
|
|
16
|
+
* falls back to the deny, whose reason still carries the exact replacement pipeline.
|
|
17
|
+
*/
|
|
18
|
+
const HOOKS = {
|
|
19
|
+
readTools: ['Read', 'read_file', 'readFile'],
|
|
20
|
+
bashTools: ['Bash', 'execute_command', 'executeCommand', 'shell'],
|
|
21
|
+
toolNameKeys: ['tool_name', 'toolName', 'preToolUse.toolName', 'preToolUse.tool'],
|
|
22
|
+
toolInputKeys: ['tool_input', 'toolInput', 'preToolUse.toolInput', 'preToolUse.input'],
|
|
23
|
+
deny: (reason) => ({ cancel: true, errorMessage: reason }),
|
|
24
|
+
};
|
|
25
|
+
/** Cline's hook is an executable file; this two-liner hands it to the cline shim. */
|
|
26
|
+
function clineHookSource(ctx) {
|
|
27
|
+
return `#!/bin/sh
|
|
28
|
+
# smelt:hooks v1 — Cline PreToolUse hook. EXPERIMENTAL tier: schema mapped from the
|
|
29
|
+
# capability matrix (docs/research/2026-09-02-harness-capability-matrix.md, Cline row),
|
|
30
|
+
# not yet smoke-tested against the real binary. Written by \`smelt hooks install\`.
|
|
31
|
+
exec ${nodeCommand(ctx.cwd, shimScriptPath(cline))}
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
export const cline = {
|
|
35
|
+
id: 'cline',
|
|
36
|
+
name: 'Cline',
|
|
37
|
+
tier: 'experimental',
|
|
38
|
+
detect: ['.clinerules'],
|
|
39
|
+
detectHome: [],
|
|
40
|
+
instructionFile: '.clinerules/smelt.md',
|
|
41
|
+
instructions: 'snippet',
|
|
42
|
+
caveats: ['deny-only hooks: input rewrite is not supported, so rewrite mode falls back to deny'],
|
|
43
|
+
hooks: HOOKS,
|
|
44
|
+
install: [
|
|
45
|
+
{
|
|
46
|
+
kind: 'own-file',
|
|
47
|
+
file: '.clinerules/hooks/PreToolUse',
|
|
48
|
+
content: clineHookSource,
|
|
49
|
+
mode: 0o755,
|
|
50
|
+
guardOnly: true,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=cline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cline.js","sourceRoot":"","sources":["../../src/harness/cline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGzD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,KAAK,GAAsB;IAC/B,SAAS,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC;IAC5C,SAAS,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,OAAO,CAAC;IACjE,YAAY,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,qBAAqB,EAAE,iBAAiB,CAAC;IACjF,aAAa,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,sBAAsB,EAAE,kBAAkB,CAAC;IACtF,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;CAC3D,CAAC;AAEF,qFAAqF;AACrF,SAAS,eAAe,CAAC,GAA0B;IACjD,OAAO;;;;OAIF,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;CACjD,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAA0B;IAC1C,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,CAAC,aAAa,CAAC;IACvB,UAAU,EAAE,EAAE;IACd,eAAe,EAAE,sBAAsB;IACvC,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE,CAAC,qFAAqF,CAAC;IAChG,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE;QACP;YACE,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,8BAA8B;YACpC,OAAO,EAAE,eAAe;YACxB,IAAI,EAAE,KAAK;YACX,SAAS,EAAE,IAAI;SAChB;KACF;CACF,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport { nodeCommand, shimScriptPath } from './paths.ts';\nimport type { HarnessInstallContext, ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Cline — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * Schema mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Cline row; primary source\n * <https://docs.cline.bot/features/hooks>), not yet smoke-tested against the real\n * binary.\n *\n * - event: `PreToolUse`, delivered to an executable under `.clinerules/hooks/` —\n * hence the two-line wrapper below, which is the file Cline runs; stdin carries the\n * tool call (accepted spellings: `tool_name`/`tool_input`, `toolName`/`toolInput`,\n * or nested under `preToolUse`).\n * - deny: `{ \"cancel\": true, \"errorMessage\": … }`. **Deny-only**: the response has no\n * input-modification field, so under `hooks.enforcement: \"rewrite\"` this harness\n * falls back to the deny, whose reason still carries the exact replacement pipeline.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['Read', 'read_file', 'readFile'],\n bashTools: ['Bash', 'execute_command', 'executeCommand', 'shell'],\n toolNameKeys: ['tool_name', 'toolName', 'preToolUse.toolName', 'preToolUse.tool'],\n toolInputKeys: ['tool_input', 'toolInput', 'preToolUse.toolInput', 'preToolUse.input'],\n deny: (reason) => ({ cancel: true, errorMessage: reason }),\n};\n\n/** Cline's hook is an executable file; this two-liner hands it to the cline shim. */\nfunction clineHookSource(ctx: HarnessInstallContext): string {\n return `#!/bin/sh\n# smelt:hooks v1 — Cline PreToolUse hook. EXPERIMENTAL tier: schema mapped from the\n# capability matrix (docs/research/2026-09-02-harness-capability-matrix.md, Cline row),\n# not yet smoke-tested against the real binary. Written by \\`smelt hooks install\\`.\nexec ${nodeCommand(ctx.cwd, shimScriptPath(cline))}\n`;\n}\n\nexport const cline: ShimmedHarnessProfile = {\n id: 'cline',\n name: 'Cline',\n tier: 'experimental',\n detect: ['.clinerules'],\n detectHome: [],\n instructionFile: '.clinerules/smelt.md',\n instructions: 'snippet',\n caveats: ['deny-only hooks: input rewrite is not supported, so rewrite mode falls back to deny'],\n hooks: HOOKS,\n install: [\n {\n kind: 'own-file',\n file: '.clinerules/hooks/PreToolUse',\n content: clineHookSource,\n mode: 0o755,\n guardOnly: true,\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/harness/codex.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AA4D1D,eAAO,MAAM,KAAK,EAAE,qBAkCnB,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\nimport { SNIPPET_END_HASH, SNIPPET_START_HASH } from './snippet.ts';\n\n/**\n * Codex CLI — VERIFIED tier. Schema, install and removal, end to end.\n *\n * Codex's experimental hooks (`features.hooks`, rust-v0.114.0+, on by default from\n * 0.150.1) deliberately mirror Claude Code's schema — the survey is\n * docs/research/2026-09-02-agent-enforcement.md § 3, primary source\n * <https://developers.openai.com/codex/hooks>:\n *\n * - stdin: `{ tool_name, tool_input }`; shell input arrives as `tool_name: \"Bash\"`,\n * `tool_input.command`.\n * - deny: `hookSpecificOutput.permissionDecision: \"deny\"` + reason (or exit 2).\n * - rewrite: `permissionDecision: \"allow\"` with `updatedInput` — for Bash,\n * `updatedInput` **must include a string `command` field**, which the shared splice\n * always does.\n *\n * Two documented differences from Claude Code, both honoured here:\n * `permissionDecision: \"ask\"` is NOT supported (a parse error — the tool proceeds),\n * and this schema never emits it; project-layer hooks only run once the project is\n * trusted, which the installer's output says out loud, and which the `[features]`\n * block below is what enables.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['Read'],\n bashTools: ['Bash', 'shell'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n deny: (reason) => ({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'deny',\n permissionDecisionReason: reason,\n },\n }),\n rewrite: {\n document: ({ input, announcement }) => ({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'allow',\n permissionDecisionReason: announcement,\n updatedInput: input,\n },\n }),\n announce: 'reason',\n },\n};\n\n/** The Codex `config.toml` block: enables the hooks feature, marker-bracketed. */\nfunction codexConfigTomlBlock(): string {\n return `${SNIPPET_START_HASH}\n# Enables Codex's hooks feature so .codex/hooks.json is honored. Project-level hooks\n# run only once this project is trusted. Written by \\`smelt hooks install\\`.\n[features]\nhooks = true\n${SNIPPET_END_HASH}\n`;\n}\n\nexport const codex: ShimmedHarnessProfile = {\n id: 'codex',\n name: 'Codex CLI',\n shortName: 'Codex',\n tier: 'verified',\n detect: ['.codex'],\n detectHome: ['.codex'],\n instructionFile: 'AGENTS.md',\n instructions: 'snippet',\n caveats: [\n 'project-level Codex hooks run only once the project is trusted (features.hooks; see docs/research/2026-09-02-agent-enforcement.md § 3)',\n ],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.codex/hooks.json',\n event: 'PreToolUse',\n matchers: ['Read', 'Bash'],\n entry: 'command-list',\n lifecycle: true,\n },\n {\n kind: 'marker-block',\n file: '.codex/config.toml',\n block: codexConfigTomlBlock,\n start: SNIPPET_START_HASH,\n end: SNIPPET_END_HASH,\n skipWhen: {\n contains: '[features]',\n why: 'already has a [features] table — add `hooks = true` to it yourself',\n },\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { SNIPPET_END_HASH, SNIPPET_START_HASH } from './snippet.js';
|
|
2
|
+
/**
|
|
3
|
+
* Codex CLI — VERIFIED tier. Schema, install and removal, end to end.
|
|
4
|
+
*
|
|
5
|
+
* Codex's experimental hooks (`features.hooks`, rust-v0.114.0+, on by default from
|
|
6
|
+
* 0.150.1) deliberately mirror Claude Code's schema — the survey is
|
|
7
|
+
* docs/research/2026-09-02-agent-enforcement.md § 3, primary source
|
|
8
|
+
* <https://developers.openai.com/codex/hooks>:
|
|
9
|
+
*
|
|
10
|
+
* - stdin: `{ tool_name, tool_input }`; shell input arrives as `tool_name: "Bash"`,
|
|
11
|
+
* `tool_input.command`.
|
|
12
|
+
* - deny: `hookSpecificOutput.permissionDecision: "deny"` + reason (or exit 2).
|
|
13
|
+
* - rewrite: `permissionDecision: "allow"` with `updatedInput` — for Bash,
|
|
14
|
+
* `updatedInput` **must include a string `command` field**, which the shared splice
|
|
15
|
+
* always does.
|
|
16
|
+
*
|
|
17
|
+
* Two documented differences from Claude Code, both honoured here:
|
|
18
|
+
* `permissionDecision: "ask"` is NOT supported (a parse error — the tool proceeds),
|
|
19
|
+
* and this schema never emits it; project-layer hooks only run once the project is
|
|
20
|
+
* trusted, which the installer's output says out loud, and which the `[features]`
|
|
21
|
+
* block below is what enables.
|
|
22
|
+
*/
|
|
23
|
+
const HOOKS = {
|
|
24
|
+
readTools: ['Read'],
|
|
25
|
+
bashTools: ['Bash', 'shell'],
|
|
26
|
+
toolNameKeys: ['tool_name'],
|
|
27
|
+
toolInputKeys: ['tool_input'],
|
|
28
|
+
deny: (reason) => ({
|
|
29
|
+
hookSpecificOutput: {
|
|
30
|
+
hookEventName: 'PreToolUse',
|
|
31
|
+
permissionDecision: 'deny',
|
|
32
|
+
permissionDecisionReason: reason,
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
rewrite: {
|
|
36
|
+
document: ({ input, announcement }) => ({
|
|
37
|
+
hookSpecificOutput: {
|
|
38
|
+
hookEventName: 'PreToolUse',
|
|
39
|
+
permissionDecision: 'allow',
|
|
40
|
+
permissionDecisionReason: announcement,
|
|
41
|
+
updatedInput: input,
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
announce: 'reason',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/** The Codex `config.toml` block: enables the hooks feature, marker-bracketed. */
|
|
48
|
+
function codexConfigTomlBlock() {
|
|
49
|
+
return `${SNIPPET_START_HASH}
|
|
50
|
+
# Enables Codex's hooks feature so .codex/hooks.json is honored. Project-level hooks
|
|
51
|
+
# run only once this project is trusted. Written by \`smelt hooks install\`.
|
|
52
|
+
[features]
|
|
53
|
+
hooks = true
|
|
54
|
+
${SNIPPET_END_HASH}
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
export const codex = {
|
|
58
|
+
id: 'codex',
|
|
59
|
+
name: 'Codex CLI',
|
|
60
|
+
shortName: 'Codex',
|
|
61
|
+
tier: 'verified',
|
|
62
|
+
detect: ['.codex'],
|
|
63
|
+
detectHome: ['.codex'],
|
|
64
|
+
instructionFile: 'AGENTS.md',
|
|
65
|
+
instructions: 'snippet',
|
|
66
|
+
caveats: [
|
|
67
|
+
'project-level Codex hooks run only once the project is trusted (features.hooks; see docs/research/2026-09-02-agent-enforcement.md § 3)',
|
|
68
|
+
],
|
|
69
|
+
hooks: HOOKS,
|
|
70
|
+
install: [
|
|
71
|
+
{
|
|
72
|
+
kind: 'json-hooks',
|
|
73
|
+
file: '.codex/hooks.json',
|
|
74
|
+
event: 'PreToolUse',
|
|
75
|
+
matchers: ['Read', 'Bash'],
|
|
76
|
+
entry: 'command-list',
|
|
77
|
+
lifecycle: true,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
kind: 'marker-block',
|
|
81
|
+
file: '.codex/config.toml',
|
|
82
|
+
block: codexConfigTomlBlock,
|
|
83
|
+
start: SNIPPET_START_HASH,
|
|
84
|
+
end: SNIPPET_END_HASH,
|
|
85
|
+
skipWhen: {
|
|
86
|
+
contains: '[features]',
|
|
87
|
+
why: 'already has a [features] table — add `hooks = true` to it yourself',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/harness/codex.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,KAAK,GAAsB;IAC/B,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,CAAC,WAAW,CAAC;IAC3B,aAAa,EAAE,CAAC,YAAY,CAAC;IAC7B,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACjB,kBAAkB,EAAE;YAClB,aAAa,EAAE,YAAY;YAC3B,kBAAkB,EAAE,MAAM;YAC1B,wBAAwB,EAAE,MAAM;SACjC;KACF,CAAC;IACF,OAAO,EAAE;QACP,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;YACtC,kBAAkB,EAAE;gBAClB,aAAa,EAAE,YAAY;gBAC3B,kBAAkB,EAAE,OAAO;gBAC3B,wBAAwB,EAAE,YAAY;gBACtC,YAAY,EAAE,KAAK;aACpB;SACF,CAAC;QACF,QAAQ,EAAE,QAAQ;KACnB;CACF,CAAC;AAEF,kFAAkF;AAClF,SAAS,oBAAoB;IAC3B,OAAO,GAAG,kBAAkB;;;;;EAK5B,gBAAgB;CACjB,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAA0B;IAC1C,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,WAAW;IACjB,SAAS,EAAE,OAAO;IAClB,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,CAAC,QAAQ,CAAC;IAClB,UAAU,EAAE,CAAC,QAAQ,CAAC;IACtB,eAAe,EAAE,WAAW;IAC5B,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE;QACP,wIAAwI;KACzI;IACD,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE;QACP;YACE,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,mBAAmB;YACzB,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;YAC1B,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,IAAI;SAChB;QACD;YACE,IAAI,EAAE,cAAc;YACpB,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,oBAAoB;YAC3B,KAAK,EAAE,kBAAkB;YACzB,GAAG,EAAE,gBAAgB;YACrB,QAAQ,EAAE;gBACR,QAAQ,EAAE,YAAY;gBACtB,GAAG,EAAE,oEAAoE;aAC1E;SACF;KACF;CACF,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\nimport { SNIPPET_END_HASH, SNIPPET_START_HASH } from './snippet.ts';\n\n/**\n * Codex CLI — VERIFIED tier. Schema, install and removal, end to end.\n *\n * Codex's experimental hooks (`features.hooks`, rust-v0.114.0+, on by default from\n * 0.150.1) deliberately mirror Claude Code's schema — the survey is\n * docs/research/2026-09-02-agent-enforcement.md § 3, primary source\n * <https://developers.openai.com/codex/hooks>:\n *\n * - stdin: `{ tool_name, tool_input }`; shell input arrives as `tool_name: \"Bash\"`,\n * `tool_input.command`.\n * - deny: `hookSpecificOutput.permissionDecision: \"deny\"` + reason (or exit 2).\n * - rewrite: `permissionDecision: \"allow\"` with `updatedInput` — for Bash,\n * `updatedInput` **must include a string `command` field**, which the shared splice\n * always does.\n *\n * Two documented differences from Claude Code, both honoured here:\n * `permissionDecision: \"ask\"` is NOT supported (a parse error — the tool proceeds),\n * and this schema never emits it; project-layer hooks only run once the project is\n * trusted, which the installer's output says out loud, and which the `[features]`\n * block below is what enables.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['Read'],\n bashTools: ['Bash', 'shell'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n deny: (reason) => ({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'deny',\n permissionDecisionReason: reason,\n },\n }),\n rewrite: {\n document: ({ input, announcement }) => ({\n hookSpecificOutput: {\n hookEventName: 'PreToolUse',\n permissionDecision: 'allow',\n permissionDecisionReason: announcement,\n updatedInput: input,\n },\n }),\n announce: 'reason',\n },\n};\n\n/** The Codex `config.toml` block: enables the hooks feature, marker-bracketed. */\nfunction codexConfigTomlBlock(): string {\n return `${SNIPPET_START_HASH}\n# Enables Codex's hooks feature so .codex/hooks.json is honored. Project-level hooks\n# run only once this project is trusted. Written by \\`smelt hooks install\\`.\n[features]\nhooks = true\n${SNIPPET_END_HASH}\n`;\n}\n\nexport const codex: ShimmedHarnessProfile = {\n id: 'codex',\n name: 'Codex CLI',\n shortName: 'Codex',\n tier: 'verified',\n detect: ['.codex'],\n detectHome: ['.codex'],\n instructionFile: 'AGENTS.md',\n instructions: 'snippet',\n caveats: [\n 'project-level Codex hooks run only once the project is trusted (features.hooks; see docs/research/2026-09-02-agent-enforcement.md § 3)',\n ],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.codex/hooks.json',\n event: 'PreToolUse',\n matchers: ['Read', 'Bash'],\n entry: 'command-list',\n lifecycle: true,\n },\n {\n kind: 'marker-block',\n file: '.codex/config.toml',\n block: codexConfigTomlBlock,\n start: SNIPPET_START_HASH,\n end: SNIPPET_END_HASH,\n skipWhen: {\n contains: '[features]',\n why: 'already has a [features] table — add `hooks = true` to it yourself',\n },\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/harness/cursor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAkC1D,eAAO,MAAM,MAAM,EAAE,qBAqBpB,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Cursor — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * Schema mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Cursor row; primary source\n * <https://cursor.com/docs/agent/hooks>), not yet smoke-tested against the real binary.\n *\n * - event: `preToolUse` (the broad hook — unlike `beforeShellExecution`, it supports\n * input rewrite), fired for every tool, so the hook file carries one matcher-less\n * entry; stdin `{ tool_name, tool_input }`, and the settings file is versioned\n * (`\"version\": 1`).\n * - deny: `{ \"permission\": \"deny\", \"agentMessage\": … }` — `agentMessage` reaches the\n * model, `userMessage` the human; the guard's steering text goes to the model.\n * - rewrite: `{ \"permission\": \"allow\", \"updated_input\": { … } }` (snake_case, the\n * whole input object), with no message field — announced on stderr instead.\n *\n * Cursor has no static permission-config file — gating is entirely hook code — so this\n * shim plus the instruction file is the whole enforcement story there.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['read_file', 'Read', 'ReadFile'],\n bashTools: ['run_terminal_cmd', 'Shell', 'Bash', 'shell'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n deny: (reason) => ({ permission: 'deny', agentMessage: reason }),\n rewrite: {\n document: ({ input }) => ({ permission: 'allow', updated_input: input }),\n // The allow/updated_input shape carries no message field — announce on stderr.\n announce: 'stderr',\n },\n};\n\nexport const cursor: ShimmedHarnessProfile = {\n id: 'cursor',\n name: 'Cursor',\n tier: 'experimental',\n detect: ['.cursor'],\n detectHome: ['.cursor'],\n instructionFile: 'AGENTS.md',\n instructions: 'snippet',\n caveats: ['Cursor has no static permission config — gating is entirely hook code'],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.cursor/hooks.json',\n event: 'preToolUse',\n matchers: [undefined],\n entry: 'bare-command',\n lifecycle: false,\n shape: { version: 1 },\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor — EXPERIMENTAL tier. Schema, install and removal, end to end.
|
|
3
|
+
*
|
|
4
|
+
* Schema mapped from the capability matrix
|
|
5
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Cursor row; primary source
|
|
6
|
+
* <https://cursor.com/docs/agent/hooks>), not yet smoke-tested against the real binary.
|
|
7
|
+
*
|
|
8
|
+
* - event: `preToolUse` (the broad hook — unlike `beforeShellExecution`, it supports
|
|
9
|
+
* input rewrite), fired for every tool, so the hook file carries one matcher-less
|
|
10
|
+
* entry; stdin `{ tool_name, tool_input }`, and the settings file is versioned
|
|
11
|
+
* (`"version": 1`).
|
|
12
|
+
* - deny: `{ "permission": "deny", "agentMessage": … }` — `agentMessage` reaches the
|
|
13
|
+
* model, `userMessage` the human; the guard's steering text goes to the model.
|
|
14
|
+
* - rewrite: `{ "permission": "allow", "updated_input": { … } }` (snake_case, the
|
|
15
|
+
* whole input object), with no message field — announced on stderr instead.
|
|
16
|
+
*
|
|
17
|
+
* Cursor has no static permission-config file — gating is entirely hook code — so this
|
|
18
|
+
* shim plus the instruction file is the whole enforcement story there.
|
|
19
|
+
*/
|
|
20
|
+
const HOOKS = {
|
|
21
|
+
readTools: ['read_file', 'Read', 'ReadFile'],
|
|
22
|
+
bashTools: ['run_terminal_cmd', 'Shell', 'Bash', 'shell'],
|
|
23
|
+
toolNameKeys: ['tool_name'],
|
|
24
|
+
toolInputKeys: ['tool_input'],
|
|
25
|
+
deny: (reason) => ({ permission: 'deny', agentMessage: reason }),
|
|
26
|
+
rewrite: {
|
|
27
|
+
document: ({ input }) => ({ permission: 'allow', updated_input: input }),
|
|
28
|
+
// The allow/updated_input shape carries no message field — announce on stderr.
|
|
29
|
+
announce: 'stderr',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export const cursor = {
|
|
33
|
+
id: 'cursor',
|
|
34
|
+
name: 'Cursor',
|
|
35
|
+
tier: 'experimental',
|
|
36
|
+
detect: ['.cursor'],
|
|
37
|
+
detectHome: ['.cursor'],
|
|
38
|
+
instructionFile: 'AGENTS.md',
|
|
39
|
+
instructions: 'snippet',
|
|
40
|
+
caveats: ['Cursor has no static permission config — gating is entirely hook code'],
|
|
41
|
+
hooks: HOOKS,
|
|
42
|
+
install: [
|
|
43
|
+
{
|
|
44
|
+
kind: 'json-hooks',
|
|
45
|
+
file: '.cursor/hooks.json',
|
|
46
|
+
event: 'preToolUse',
|
|
47
|
+
matchers: [undefined],
|
|
48
|
+
entry: 'bare-command',
|
|
49
|
+
lifecycle: false,
|
|
50
|
+
shape: { version: 1 },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/harness/cursor.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,KAAK,GAAsB;IAC/B,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC;IAC5C,SAAS,EAAE,CAAC,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC;IACzD,YAAY,EAAE,CAAC,WAAW,CAAC;IAC3B,aAAa,EAAE,CAAC,YAAY,CAAC;IAC7B,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAChE,OAAO,EAAE;QACP,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QACxE,+EAA+E;QAC/E,QAAQ,EAAE,QAAQ;KACnB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,MAAM,GAA0B;IAC3C,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,CAAC,SAAS,CAAC;IACnB,UAAU,EAAE,CAAC,SAAS,CAAC;IACvB,eAAe,EAAE,WAAW;IAC5B,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE,CAAC,uEAAuE,CAAC;IAClF,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE;QACP;YACE,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,CAAC,SAAS,CAAC;YACrB,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE;SACtB;KACF;CACF,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Cursor — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * Schema mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Cursor row; primary source\n * <https://cursor.com/docs/agent/hooks>), not yet smoke-tested against the real binary.\n *\n * - event: `preToolUse` (the broad hook — unlike `beforeShellExecution`, it supports\n * input rewrite), fired for every tool, so the hook file carries one matcher-less\n * entry; stdin `{ tool_name, tool_input }`, and the settings file is versioned\n * (`\"version\": 1`).\n * - deny: `{ \"permission\": \"deny\", \"agentMessage\": … }` — `agentMessage` reaches the\n * model, `userMessage` the human; the guard's steering text goes to the model.\n * - rewrite: `{ \"permission\": \"allow\", \"updated_input\": { … } }` (snake_case, the\n * whole input object), with no message field — announced on stderr instead.\n *\n * Cursor has no static permission-config file — gating is entirely hook code — so this\n * shim plus the instruction file is the whole enforcement story there.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['read_file', 'Read', 'ReadFile'],\n bashTools: ['run_terminal_cmd', 'Shell', 'Bash', 'shell'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n deny: (reason) => ({ permission: 'deny', agentMessage: reason }),\n rewrite: {\n document: ({ input }) => ({ permission: 'allow', updated_input: input }),\n // The allow/updated_input shape carries no message field — announce on stderr.\n announce: 'stderr',\n },\n};\n\nexport const cursor: ShimmedHarnessProfile = {\n id: 'cursor',\n name: 'Cursor',\n tier: 'experimental',\n detect: ['.cursor'],\n detectHome: ['.cursor'],\n instructionFile: 'AGENTS.md',\n instructions: 'snippet',\n caveats: ['Cursor has no static permission config — gating is entirely hook code'],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.cursor/hooks.json',\n event: 'preToolUse',\n matchers: [undefined],\n entry: 'bare-command',\n lifecycle: false,\n shape: { version: 1 },\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/harness/gemini.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAsC1D,eAAO,MAAM,MAAM,EAAE,qBAuBpB,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Gemini CLI — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * The schema below is mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Gemini CLI row; primary\n * source <https://geminicli.com/docs/hooks/reference/>), and the field names were\n * additionally checked 2026-09-02 against the shipped `@google/gemini-cli` 0.58.0\n * bundle (installed headlessly for the shim's verification pass): the BeforeTool stdin\n * payload is `{ tool_name, tool_input }`, `read_file` takes `file_path` with\n * `start_line`/`end_line` windows, the shell tool is `run_shell_command` with\n * `command`, denial is `{ \"decision\": \"deny\", \"reason\": … }`, and\n * `hookSpecificOutput.tool_input` substitutes the input. What has NOT run is a live\n * end-to-end session (needs credentials this environment does not have), so the tier\n * stays experimental — treat surprises as shim bugs and please report them.\n *\n * Carried caveat from the matrix: Gemini's policy engine has an open bug where an\n * `allow` policy is ignored in non-interactive runs\n * (<https://github.com/google-gemini/gemini-cli/issues/20469>) — this preset uses\n * hooks, not the policy engine, but non-interactive behaviour is the least-tested\n * corner; verify before relying on it in CI.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['read_file', 'ReadFile', 'Read'],\n bashTools: ['run_shell_command', 'Shell', 'Bash'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n deny: (reason) => ({ decision: 'deny', reason }),\n rewrite: {\n document: ({ input }) => ({\n hookSpecificOutput: { hookEventName: 'BeforeTool', tool_input: input },\n }),\n // The BeforeTool rewrite shape carries no reason field — announce on stderr.\n announce: 'stderr',\n },\n};\n\nexport const gemini: ShimmedHarnessProfile = {\n id: 'gemini',\n name: 'Gemini CLI',\n shortName: 'Gemini',\n tier: 'experimental',\n detect: ['.gemini'],\n detectHome: ['.gemini'],\n instructionFile: 'GEMINI.md',\n instructions: 'snippet',\n caveats: [\n 'Gemini policy-engine allow rules are ignored in non-interactive runs (google-gemini/gemini-cli#20469) — verify hook behaviour in CI before relying on it',\n ],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.gemini/settings.json',\n event: 'BeforeTool',\n matchers: ['read_file', 'run_shell_command'],\n entry: 'command-list',\n lifecycle: false,\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI — EXPERIMENTAL tier. Schema, install and removal, end to end.
|
|
3
|
+
*
|
|
4
|
+
* The schema below is mapped from the capability matrix
|
|
5
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Gemini CLI row; primary
|
|
6
|
+
* source <https://geminicli.com/docs/hooks/reference/>), and the field names were
|
|
7
|
+
* additionally checked 2026-09-02 against the shipped `@google/gemini-cli` 0.58.0
|
|
8
|
+
* bundle (installed headlessly for the shim's verification pass): the BeforeTool stdin
|
|
9
|
+
* payload is `{ tool_name, tool_input }`, `read_file` takes `file_path` with
|
|
10
|
+
* `start_line`/`end_line` windows, the shell tool is `run_shell_command` with
|
|
11
|
+
* `command`, denial is `{ "decision": "deny", "reason": … }`, and
|
|
12
|
+
* `hookSpecificOutput.tool_input` substitutes the input. What has NOT run is a live
|
|
13
|
+
* end-to-end session (needs credentials this environment does not have), so the tier
|
|
14
|
+
* stays experimental — treat surprises as shim bugs and please report them.
|
|
15
|
+
*
|
|
16
|
+
* Carried caveat from the matrix: Gemini's policy engine has an open bug where an
|
|
17
|
+
* `allow` policy is ignored in non-interactive runs
|
|
18
|
+
* (<https://github.com/google-gemini/gemini-cli/issues/20469>) — this preset uses
|
|
19
|
+
* hooks, not the policy engine, but non-interactive behaviour is the least-tested
|
|
20
|
+
* corner; verify before relying on it in CI.
|
|
21
|
+
*/
|
|
22
|
+
const HOOKS = {
|
|
23
|
+
readTools: ['read_file', 'ReadFile', 'Read'],
|
|
24
|
+
bashTools: ['run_shell_command', 'Shell', 'Bash'],
|
|
25
|
+
toolNameKeys: ['tool_name'],
|
|
26
|
+
toolInputKeys: ['tool_input'],
|
|
27
|
+
deny: (reason) => ({ decision: 'deny', reason }),
|
|
28
|
+
rewrite: {
|
|
29
|
+
document: ({ input }) => ({
|
|
30
|
+
hookSpecificOutput: { hookEventName: 'BeforeTool', tool_input: input },
|
|
31
|
+
}),
|
|
32
|
+
// The BeforeTool rewrite shape carries no reason field — announce on stderr.
|
|
33
|
+
announce: 'stderr',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export const gemini = {
|
|
37
|
+
id: 'gemini',
|
|
38
|
+
name: 'Gemini CLI',
|
|
39
|
+
shortName: 'Gemini',
|
|
40
|
+
tier: 'experimental',
|
|
41
|
+
detect: ['.gemini'],
|
|
42
|
+
detectHome: ['.gemini'],
|
|
43
|
+
instructionFile: 'GEMINI.md',
|
|
44
|
+
instructions: 'snippet',
|
|
45
|
+
caveats: [
|
|
46
|
+
'Gemini policy-engine allow rules are ignored in non-interactive runs (google-gemini/gemini-cli#20469) — verify hook behaviour in CI before relying on it',
|
|
47
|
+
],
|
|
48
|
+
hooks: HOOKS,
|
|
49
|
+
install: [
|
|
50
|
+
{
|
|
51
|
+
kind: 'json-hooks',
|
|
52
|
+
file: '.gemini/settings.json',
|
|
53
|
+
event: 'BeforeTool',
|
|
54
|
+
matchers: ['read_file', 'run_shell_command'],
|
|
55
|
+
entry: 'command-list',
|
|
56
|
+
lifecycle: false,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/harness/gemini.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,KAAK,GAAsB;IAC/B,SAAS,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC;IAC5C,SAAS,EAAE,CAAC,mBAAmB,EAAE,OAAO,EAAE,MAAM,CAAC;IACjD,YAAY,EAAE,CAAC,WAAW,CAAC;IAC3B,aAAa,EAAE,CAAC,YAAY,CAAC;IAC7B,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAChD,OAAO,EAAE;QACP,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACxB,kBAAkB,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,EAAE;SACvE,CAAC;QACF,6EAA6E;QAC7E,QAAQ,EAAE,QAAQ;KACnB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,MAAM,GAA0B;IAC3C,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,YAAY;IAClB,SAAS,EAAE,QAAQ;IACnB,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,CAAC,SAAS,CAAC;IACnB,UAAU,EAAE,CAAC,SAAS,CAAC;IACvB,eAAe,EAAE,WAAW;IAC5B,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE;QACP,0JAA0J;KAC3J;IACD,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE;QACP;YACE,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,uBAAuB;YAC7B,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,CAAC,WAAW,EAAE,mBAAmB,CAAC;YAC5C,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,KAAK;SACjB;KACF;CACF,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Gemini CLI — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * The schema below is mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Gemini CLI row; primary\n * source <https://geminicli.com/docs/hooks/reference/>), and the field names were\n * additionally checked 2026-09-02 against the shipped `@google/gemini-cli` 0.58.0\n * bundle (installed headlessly for the shim's verification pass): the BeforeTool stdin\n * payload is `{ tool_name, tool_input }`, `read_file` takes `file_path` with\n * `start_line`/`end_line` windows, the shell tool is `run_shell_command` with\n * `command`, denial is `{ \"decision\": \"deny\", \"reason\": … }`, and\n * `hookSpecificOutput.tool_input` substitutes the input. What has NOT run is a live\n * end-to-end session (needs credentials this environment does not have), so the tier\n * stays experimental — treat surprises as shim bugs and please report them.\n *\n * Carried caveat from the matrix: Gemini's policy engine has an open bug where an\n * `allow` policy is ignored in non-interactive runs\n * (<https://github.com/google-gemini/gemini-cli/issues/20469>) — this preset uses\n * hooks, not the policy engine, but non-interactive behaviour is the least-tested\n * corner; verify before relying on it in CI.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['read_file', 'ReadFile', 'Read'],\n bashTools: ['run_shell_command', 'Shell', 'Bash'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n deny: (reason) => ({ decision: 'deny', reason }),\n rewrite: {\n document: ({ input }) => ({\n hookSpecificOutput: { hookEventName: 'BeforeTool', tool_input: input },\n }),\n // The BeforeTool rewrite shape carries no reason field — announce on stderr.\n announce: 'stderr',\n },\n};\n\nexport const gemini: ShimmedHarnessProfile = {\n id: 'gemini',\n name: 'Gemini CLI',\n shortName: 'Gemini',\n tier: 'experimental',\n detect: ['.gemini'],\n detectHome: ['.gemini'],\n instructionFile: 'GEMINI.md',\n instructions: 'snippet',\n caveats: [\n 'Gemini policy-engine allow rules are ignored in non-interactive runs (google-gemini/gemini-cli#20469) — verify hook behaviour in CI before relying on it',\n ],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.gemini/settings.json',\n event: 'BeforeTool',\n matchers: ['read_file', 'run_shell_command'],\n entry: 'command-list',\n lifecycle: false,\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grok.d.ts","sourceRoot":"","sources":["../../src/harness/grok.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AA2B1D,eAAO,MAAM,IAAI,EAAE,qBAqBlB,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Grok CLI — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * Schema mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Grok CLI row; primary source\n * <https://docs.x.ai/build/features/hooks>), not yet smoke-tested against the real\n * binary. Note the matrix's provenance warning: the official CLI is\n * `xai-org/grok-build` (binary `grok`) — `superagent-ai/grok-cli` is a third-party\n * clone with a different hook surface.\n *\n * - event: `PreToolUse`; stdin `{ tool_name, tool_input }` (Claude-Code-like tool\n * names).\n * - deny: `{ \"decision\": \"deny\", \"reason\": … }` or exit 2. **Deny-only**: the input\n * is read-only to hooks, so there is no `rewrite` here — under\n * `hooks.enforcement: \"rewrite\"` this harness falls back to the deny, whose reason\n * still carries the exact replacement pipeline.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['Read', 'read_file', 'ReadFile'],\n bashTools: ['Bash', 'shell', 'run_shell_command'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n deny: (reason) => ({ decision: 'deny', reason }),\n};\n\nexport const grok: ShimmedHarnessProfile = {\n id: 'grok',\n name: 'Grok CLI',\n shortName: 'Grok',\n tier: 'experimental',\n detect: ['.grok'],\n detectHome: ['.grok'],\n instructionFile: 'AGENTS.md',\n instructions: 'snippet',\n caveats: ['deny-only hooks: input rewrite is not supported, so rewrite mode falls back to deny'],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.grok/hooks.json',\n event: 'PreToolUse',\n matchers: ['Read', 'Bash'],\n entry: 'command-list',\n lifecycle: false,\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grok CLI — EXPERIMENTAL tier. Schema, install and removal, end to end.
|
|
3
|
+
*
|
|
4
|
+
* Schema mapped from the capability matrix
|
|
5
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Grok CLI row; primary source
|
|
6
|
+
* <https://docs.x.ai/build/features/hooks>), not yet smoke-tested against the real
|
|
7
|
+
* binary. Note the matrix's provenance warning: the official CLI is
|
|
8
|
+
* `xai-org/grok-build` (binary `grok`) — `superagent-ai/grok-cli` is a third-party
|
|
9
|
+
* clone with a different hook surface.
|
|
10
|
+
*
|
|
11
|
+
* - event: `PreToolUse`; stdin `{ tool_name, tool_input }` (Claude-Code-like tool
|
|
12
|
+
* names).
|
|
13
|
+
* - deny: `{ "decision": "deny", "reason": … }` or exit 2. **Deny-only**: the input
|
|
14
|
+
* is read-only to hooks, so there is no `rewrite` here — under
|
|
15
|
+
* `hooks.enforcement: "rewrite"` this harness falls back to the deny, whose reason
|
|
16
|
+
* still carries the exact replacement pipeline.
|
|
17
|
+
*/
|
|
18
|
+
const HOOKS = {
|
|
19
|
+
readTools: ['Read', 'read_file', 'ReadFile'],
|
|
20
|
+
bashTools: ['Bash', 'shell', 'run_shell_command'],
|
|
21
|
+
toolNameKeys: ['tool_name'],
|
|
22
|
+
toolInputKeys: ['tool_input'],
|
|
23
|
+
deny: (reason) => ({ decision: 'deny', reason }),
|
|
24
|
+
};
|
|
25
|
+
export const grok = {
|
|
26
|
+
id: 'grok',
|
|
27
|
+
name: 'Grok CLI',
|
|
28
|
+
shortName: 'Grok',
|
|
29
|
+
tier: 'experimental',
|
|
30
|
+
detect: ['.grok'],
|
|
31
|
+
detectHome: ['.grok'],
|
|
32
|
+
instructionFile: 'AGENTS.md',
|
|
33
|
+
instructions: 'snippet',
|
|
34
|
+
caveats: ['deny-only hooks: input rewrite is not supported, so rewrite mode falls back to deny'],
|
|
35
|
+
hooks: HOOKS,
|
|
36
|
+
install: [
|
|
37
|
+
{
|
|
38
|
+
kind: 'json-hooks',
|
|
39
|
+
file: '.grok/hooks.json',
|
|
40
|
+
event: 'PreToolUse',
|
|
41
|
+
matchers: ['Read', 'Bash'],
|
|
42
|
+
entry: 'command-list',
|
|
43
|
+
lifecycle: false,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=grok.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grok.js","sourceRoot":"","sources":["../../src/harness/grok.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,KAAK,GAAsB;IAC/B,SAAS,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC;IAC5C,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,mBAAmB,CAAC;IACjD,YAAY,EAAE,CAAC,WAAW,CAAC;IAC3B,aAAa,EAAE,CAAC,YAAY,CAAC;IAC7B,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;CACjD,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAA0B;IACzC,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,UAAU;IAChB,SAAS,EAAE,MAAM;IACjB,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,CAAC,OAAO,CAAC;IACjB,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,eAAe,EAAE,WAAW;IAC5B,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE,CAAC,qFAAqF,CAAC;IAChG,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE;QACP;YACE,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,kBAAkB;YACxB,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;YAC1B,KAAK,EAAE,cAAc;YACrB,SAAS,EAAE,KAAK;SACjB;KACF;CACF,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport type { ShimmedHarnessProfile } from './profile.ts';\n\n/**\n * Grok CLI — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * Schema mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Grok CLI row; primary source\n * <https://docs.x.ai/build/features/hooks>), not yet smoke-tested against the real\n * binary. Note the matrix's provenance warning: the official CLI is\n * `xai-org/grok-build` (binary `grok`) — `superagent-ai/grok-cli` is a third-party\n * clone with a different hook surface.\n *\n * - event: `PreToolUse`; stdin `{ tool_name, tool_input }` (Claude-Code-like tool\n * names).\n * - deny: `{ \"decision\": \"deny\", \"reason\": … }` or exit 2. **Deny-only**: the input\n * is read-only to hooks, so there is no `rewrite` here — under\n * `hooks.enforcement: \"rewrite\"` this harness falls back to the deny, whose reason\n * still carries the exact replacement pipeline.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['Read', 'read_file', 'ReadFile'],\n bashTools: ['Bash', 'shell', 'run_shell_command'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['tool_input'],\n deny: (reason) => ({ decision: 'deny', reason }),\n};\n\nexport const grok: ShimmedHarnessProfile = {\n id: 'grok',\n name: 'Grok CLI',\n shortName: 'Grok',\n tier: 'experimental',\n detect: ['.grok'],\n detectHome: ['.grok'],\n instructionFile: 'AGENTS.md',\n instructions: 'snippet',\n caveats: ['deny-only hooks: input rewrite is not supported, so rewrite mode falls back to deny'],\n hooks: HOOKS,\n install: [\n {\n kind: 'json-hooks',\n file: '.grok/hooks.json',\n event: 'PreToolUse',\n matchers: ['Read', 'Bash'],\n entry: 'command-list',\n lifecycle: false,\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hermes.d.ts","sourceRoot":"","sources":["../../src/harness/hermes.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAyB,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAmDjF,eAAO,MAAM,MAAM,EAAE,qBAsBpB,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport { nodeCommand, shimScriptPath } from './paths.ts';\nimport type { HarnessInstallContext, ShimmedHarnessProfile } from './profile.ts';\nimport { SNIPPET_END_HASH, SNIPPET_START_HASH } from './snippet.ts';\n\n/**\n * Hermes Agent — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * Schema mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Hermes Agent row; primary\n * source <https://hermes-agent.nousresearch.com/docs/user-guide/features/hooks>), not\n * yet smoke-tested against the real binary.\n *\n * - event: `pre_tool_call`; stdin carries the tool name and its arguments\n * (`tool_name` + `args`, with `tool_input` accepted as a fallback spelling).\n * - deny: `{ \"action\": \"block\", \"reason\": … }`.\n * - rewrite: `{ \"action\": \"modify\", \"args\": { … } }` — a **shallow merge** into the\n * tool's arguments, so this schema sends only the `command` key and the harness\n * keeps the rest. The modify document has **no reason field**, so the substitution\n * is announced on stderr — a rewrite must never be silent.\n *\n * Carried caveat from the matrix: Hermes has a known bypass where memory tools ignore\n * `disabled_toolsets` (<https://github.com/NousResearch/hermes-agent/issues/46171>) —\n * tool gating there is leaky at the edges, so treat this hook as best-effort steering,\n * not a boundary.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['read_file', 'Read', 'ReadFile'],\n bashTools: ['execute_command', 'terminal', 'bash', 'Bash', 'shell'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['args', 'tool_input'],\n deny: (reason) => ({ action: 'block', reason }),\n rewrite: {\n // A shallow merge: only the key that changed travels, never the spliced whole.\n document: ({ suggestion }) => ({ action: 'modify', args: { command: suggestion } }),\n announce: 'stderr',\n },\n};\n\n/** Hermes hook config, as a mergeable snippet — their config is a home-level YAML. */\nfunction hermesHooksYaml(ctx: HarnessInstallContext): string {\n return `${SNIPPET_START_HASH}\n# Hermes Agent hook config for the smelt guard. EXPERIMENTAL tier: schema mapped from\n# the capability matrix (docs/research/2026-09-02-harness-capability-matrix.md, Hermes\n# row), not yet smoke-tested against the real binary. If Hermes does not read this\n# file directly, merge the \\`hooks:\\` section into ~/.hermes/config.yaml.\nhooks:\n pre_tool_call:\n - command: ${nodeCommand(ctx.cwd, shimScriptPath(hermes))}\n${SNIPPET_END_HASH}\n`;\n}\n\nexport const hermes: ShimmedHarnessProfile = {\n id: 'hermes',\n name: 'Hermes Agent',\n shortName: 'Hermes',\n tier: 'experimental',\n detect: ['.hermes', '.hermes.md'],\n detectHome: ['.hermes'],\n instructionFile: 'AGENTS.md',\n instructions: 'snippet',\n caveats: [\n 'Hermes memory tools bypass disabled_toolsets (NousResearch/hermes-agent#46171) — treat tool gating there as leaky',\n 'hook config may need merging into ~/.hermes/config.yaml by hand; the written file says how',\n ],\n hooks: HOOKS,\n install: [\n {\n kind: 'own-file',\n file: '.hermes/hooks.yaml',\n content: hermesHooksYaml,\n guardOnly: true,\n },\n ],\n};\n"]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { nodeCommand, shimScriptPath } from './paths.js';
|
|
2
|
+
import { SNIPPET_END_HASH, SNIPPET_START_HASH } from './snippet.js';
|
|
3
|
+
/**
|
|
4
|
+
* Hermes Agent — EXPERIMENTAL tier. Schema, install and removal, end to end.
|
|
5
|
+
*
|
|
6
|
+
* Schema mapped from the capability matrix
|
|
7
|
+
* (docs/research/2026-09-02-harness-capability-matrix.md, Hermes Agent row; primary
|
|
8
|
+
* source <https://hermes-agent.nousresearch.com/docs/user-guide/features/hooks>), not
|
|
9
|
+
* yet smoke-tested against the real binary.
|
|
10
|
+
*
|
|
11
|
+
* - event: `pre_tool_call`; stdin carries the tool name and its arguments
|
|
12
|
+
* (`tool_name` + `args`, with `tool_input` accepted as a fallback spelling).
|
|
13
|
+
* - deny: `{ "action": "block", "reason": … }`.
|
|
14
|
+
* - rewrite: `{ "action": "modify", "args": { … } }` — a **shallow merge** into the
|
|
15
|
+
* tool's arguments, so this schema sends only the `command` key and the harness
|
|
16
|
+
* keeps the rest. The modify document has **no reason field**, so the substitution
|
|
17
|
+
* is announced on stderr — a rewrite must never be silent.
|
|
18
|
+
*
|
|
19
|
+
* Carried caveat from the matrix: Hermes has a known bypass where memory tools ignore
|
|
20
|
+
* `disabled_toolsets` (<https://github.com/NousResearch/hermes-agent/issues/46171>) —
|
|
21
|
+
* tool gating there is leaky at the edges, so treat this hook as best-effort steering,
|
|
22
|
+
* not a boundary.
|
|
23
|
+
*/
|
|
24
|
+
const HOOKS = {
|
|
25
|
+
readTools: ['read_file', 'Read', 'ReadFile'],
|
|
26
|
+
bashTools: ['execute_command', 'terminal', 'bash', 'Bash', 'shell'],
|
|
27
|
+
toolNameKeys: ['tool_name'],
|
|
28
|
+
toolInputKeys: ['args', 'tool_input'],
|
|
29
|
+
deny: (reason) => ({ action: 'block', reason }),
|
|
30
|
+
rewrite: {
|
|
31
|
+
// A shallow merge: only the key that changed travels, never the spliced whole.
|
|
32
|
+
document: ({ suggestion }) => ({ action: 'modify', args: { command: suggestion } }),
|
|
33
|
+
announce: 'stderr',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
/** Hermes hook config, as a mergeable snippet — their config is a home-level YAML. */
|
|
37
|
+
function hermesHooksYaml(ctx) {
|
|
38
|
+
return `${SNIPPET_START_HASH}
|
|
39
|
+
# Hermes Agent hook config for the smelt guard. EXPERIMENTAL tier: schema mapped from
|
|
40
|
+
# the capability matrix (docs/research/2026-09-02-harness-capability-matrix.md, Hermes
|
|
41
|
+
# row), not yet smoke-tested against the real binary. If Hermes does not read this
|
|
42
|
+
# file directly, merge the \`hooks:\` section into ~/.hermes/config.yaml.
|
|
43
|
+
hooks:
|
|
44
|
+
pre_tool_call:
|
|
45
|
+
- command: ${nodeCommand(ctx.cwd, shimScriptPath(hermes))}
|
|
46
|
+
${SNIPPET_END_HASH}
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
export const hermes = {
|
|
50
|
+
id: 'hermes',
|
|
51
|
+
name: 'Hermes Agent',
|
|
52
|
+
shortName: 'Hermes',
|
|
53
|
+
tier: 'experimental',
|
|
54
|
+
detect: ['.hermes', '.hermes.md'],
|
|
55
|
+
detectHome: ['.hermes'],
|
|
56
|
+
instructionFile: 'AGENTS.md',
|
|
57
|
+
instructions: 'snippet',
|
|
58
|
+
caveats: [
|
|
59
|
+
'Hermes memory tools bypass disabled_toolsets (NousResearch/hermes-agent#46171) — treat tool gating there as leaky',
|
|
60
|
+
'hook config may need merging into ~/.hermes/config.yaml by hand; the written file says how',
|
|
61
|
+
],
|
|
62
|
+
hooks: HOOKS,
|
|
63
|
+
install: [
|
|
64
|
+
{
|
|
65
|
+
kind: 'own-file',
|
|
66
|
+
file: '.hermes/hooks.yaml',
|
|
67
|
+
content: hermesHooksYaml,
|
|
68
|
+
guardOnly: true,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=hermes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hermes.js","sourceRoot":"","sources":["../../src/harness/hermes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,KAAK,GAAsB;IAC/B,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC;IAC5C,SAAS,EAAE,CAAC,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;IACnE,YAAY,EAAE,CAAC,WAAW,CAAC;IAC3B,aAAa,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC;IACrC,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAC/C,OAAO,EAAE;QACP,+EAA+E;QAC/E,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,CAAC;QACnF,QAAQ,EAAE,QAAQ;KACnB;CACF,CAAC;AAEF,sFAAsF;AACtF,SAAS,eAAe,CAAC,GAA0B;IACjD,OAAO,GAAG,kBAAkB;;;;;;;iBAOb,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;EAC3D,gBAAgB;CACjB,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAA0B;IAC3C,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,cAAc;IACpB,SAAS,EAAE,QAAQ;IACnB,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,UAAU,EAAE,CAAC,SAAS,CAAC;IACvB,eAAe,EAAE,WAAW;IAC5B,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE;QACP,mHAAmH;QACnH,4FAA4F;KAC7F;IACD,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE;QACP;YACE,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,oBAAoB;YAC1B,OAAO,EAAE,eAAe;YACxB,SAAS,EAAE,IAAI;SAChB;KACF;CACF,CAAC","sourcesContent":["import type { HarnessHookSchema } from '../hooks/shim.ts';\n\nimport { nodeCommand, shimScriptPath } from './paths.ts';\nimport type { HarnessInstallContext, ShimmedHarnessProfile } from './profile.ts';\nimport { SNIPPET_END_HASH, SNIPPET_START_HASH } from './snippet.ts';\n\n/**\n * Hermes Agent — EXPERIMENTAL tier. Schema, install and removal, end to end.\n *\n * Schema mapped from the capability matrix\n * (docs/research/2026-09-02-harness-capability-matrix.md, Hermes Agent row; primary\n * source <https://hermes-agent.nousresearch.com/docs/user-guide/features/hooks>), not\n * yet smoke-tested against the real binary.\n *\n * - event: `pre_tool_call`; stdin carries the tool name and its arguments\n * (`tool_name` + `args`, with `tool_input` accepted as a fallback spelling).\n * - deny: `{ \"action\": \"block\", \"reason\": … }`.\n * - rewrite: `{ \"action\": \"modify\", \"args\": { … } }` — a **shallow merge** into the\n * tool's arguments, so this schema sends only the `command` key and the harness\n * keeps the rest. The modify document has **no reason field**, so the substitution\n * is announced on stderr — a rewrite must never be silent.\n *\n * Carried caveat from the matrix: Hermes has a known bypass where memory tools ignore\n * `disabled_toolsets` (<https://github.com/NousResearch/hermes-agent/issues/46171>) —\n * tool gating there is leaky at the edges, so treat this hook as best-effort steering,\n * not a boundary.\n */\nconst HOOKS: HarnessHookSchema = {\n readTools: ['read_file', 'Read', 'ReadFile'],\n bashTools: ['execute_command', 'terminal', 'bash', 'Bash', 'shell'],\n toolNameKeys: ['tool_name'],\n toolInputKeys: ['args', 'tool_input'],\n deny: (reason) => ({ action: 'block', reason }),\n rewrite: {\n // A shallow merge: only the key that changed travels, never the spliced whole.\n document: ({ suggestion }) => ({ action: 'modify', args: { command: suggestion } }),\n announce: 'stderr',\n },\n};\n\n/** Hermes hook config, as a mergeable snippet — their config is a home-level YAML. */\nfunction hermesHooksYaml(ctx: HarnessInstallContext): string {\n return `${SNIPPET_START_HASH}\n# Hermes Agent hook config for the smelt guard. EXPERIMENTAL tier: schema mapped from\n# the capability matrix (docs/research/2026-09-02-harness-capability-matrix.md, Hermes\n# row), not yet smoke-tested against the real binary. If Hermes does not read this\n# file directly, merge the \\`hooks:\\` section into ~/.hermes/config.yaml.\nhooks:\n pre_tool_call:\n - command: ${nodeCommand(ctx.cwd, shimScriptPath(hermes))}\n${SNIPPET_END_HASH}\n`;\n}\n\nexport const hermes: ShimmedHarnessProfile = {\n id: 'hermes',\n name: 'Hermes Agent',\n shortName: 'Hermes',\n tier: 'experimental',\n detect: ['.hermes', '.hermes.md'],\n detectHome: ['.hermes'],\n instructionFile: 'AGENTS.md',\n instructions: 'snippet',\n caveats: [\n 'Hermes memory tools bypass disabled_toolsets (NousResearch/hermes-agent#46171) — treat tool gating there as leaky',\n 'hook config may need merging into ~/.hermes/config.yaml by hand; the written file says how',\n ],\n hooks: HOOKS,\n install: [\n {\n kind: 'own-file',\n file: '.hermes/hooks.yaml',\n content: hermesHooksYaml,\n guardOnly: true,\n },\n ],\n};\n"]}
|