@weave_protocol/adapter-geminicli 0.0.0-stage → 0.2.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 +80 -2
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +107 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +80 -0
- package/dist/config.js.map +1 -0
- package/dist/hook.d.ts +50 -0
- package/dist/hook.d.ts.map +1 -0
- package/dist/hook.js +139 -0
- package/dist/hook.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +49 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -4
package/README.md
CHANGED
|
@@ -1,3 +1,81 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🛡️ @weave_protocol/adapter-geminicli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Enforce [WARD.md](https://github.com/Tyox-all/Weave_Protocol/tree/main/ward) in **Gemini CLI** with its `BeforeTool` hook. Every tool call the agent makes is checked against your policy before it runs.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@weave_protocol/adapter-geminicli)
|
|
6
|
+
|
|
7
|
+
The fourth WARD harness adapter, after Claude Code, Antigravity and Microsoft Agent Framework. The same WARD.md works in all of them.
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -g @weave_protocol/adapter-geminicli
|
|
13
|
+
weave-gemini init # adds the hook to ~/.gemini/settings.json
|
|
14
|
+
weave-gemini status # hook installed? which WARD.md is active?
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then restart Gemini CLI. Add a `WARD.md` to your project (`npx @weave_protocol/ward init`), or a user-wide one at `~/.gemini/WARD.md`.
|
|
18
|
+
|
|
19
|
+
## What it checks
|
|
20
|
+
|
|
21
|
+
The hook uses Ward's shared evaluator (`evaluateToolCall`), so the rules match the other adapters exactly:
|
|
22
|
+
|
|
23
|
+
- **Capability:** the tool's WARD capability (`run_shell_command` → `shell_exec`, `read_file` → `file_read`, …) or its raw name.
|
|
24
|
+
- **Arguments:** every path and URL argument, with `..` and `~` normalized, including every entry of `read_many_files`' `include` list.
|
|
25
|
+
- **Shell commands:** every URL and every absolute or `~` path in `run_shell_command`, with relative paths resolved against its `dir_path`.
|
|
26
|
+
- **`web_fetch`:** every URL inside its free-text `prompt`.
|
|
27
|
+
- **MCP tools** (`mcp_<server>_<tool>`): by name, plus their path and URL arguments.
|
|
28
|
+
|
|
29
|
+
Gemini CLI's planning and interaction tools (`ask_user`, `write_todos`, plan mode, the task tracker, `update_topic`) share one capability, `task_planning`, so a default-deny policy can allow them in one line:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
## Capabilities
|
|
33
|
+
allow:
|
|
34
|
+
- file_read
|
|
35
|
+
- task_planning
|
|
36
|
+
default: deny
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Decisions
|
|
40
|
+
|
|
41
|
+
| WARD decision | What Gemini CLI sees |
|
|
42
|
+
|---|---|
|
|
43
|
+
| allow | nothing (`{}`): Gemini CLI's own confirmation prompts still apply |
|
|
44
|
+
| deny | `{"decision": "deny", "reason": "WARD: …"}`; the reason goes to the agent as the tool error |
|
|
45
|
+
| require_approval | a deny whose reason says approval is needed (Gemini CLI hooks have no "ask") |
|
|
46
|
+
|
|
47
|
+
**Failure modes:**
|
|
48
|
+
- **An invalid WARD.md always denies**, so a typo can't silently switch enforcement off.
|
|
49
|
+
- **No WARD.md anywhere means no enforcement.**
|
|
50
|
+
- **Malformed payloads and evaluator errors** allow by default. `weave-gemini init --fail-closed` makes them deny.
|
|
51
|
+
|
|
52
|
+
WARD.md is looked up in this order:
|
|
53
|
+
1. `$WEAVE_WARD_PATH`
|
|
54
|
+
2. `<project>/WARD.md`
|
|
55
|
+
3. `<project>/.gemini/WARD.md`
|
|
56
|
+
4. `<project>/.weave/WARD.md`
|
|
57
|
+
5. `~/.gemini/WARD.md`
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
weave-gemini init [--matcher=REGEX] [--timeout=MS] [--fail-closed]
|
|
63
|
+
weave-gemini disable
|
|
64
|
+
weave-gemini status
|
|
65
|
+
weave-gemini test read_file --input='{"file_path":"secrets/key"}' # dry run
|
|
66
|
+
weave-gemini hook before-tool # what Gemini CLI runs
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`init` backs up `settings.json`, edits it atomically, and finds its own entry by the hook name `weave-ward`, so your other hooks are untouched. It installs at user level. Gemini CLI fingerprints project-level hooks and warns when one changes, but a user-level hook covers every project.
|
|
70
|
+
|
|
71
|
+
## Dashboard
|
|
72
|
+
|
|
73
|
+
Set `WEAVE_DASHBOARD_URL` (and `WEAVE_API_KEY`) where you run the agent, and each decision is reported to a local [Weave API](https://github.com/Tyox-all/Weave_Protocol/tree/main/api) dashboard (0.2.0+). Only the decision and the tool name are sent, never command or argument contents. Reporting never changes a decision and gives up after 250 ms.
|
|
74
|
+
|
|
75
|
+
## Verified
|
|
76
|
+
|
|
77
|
+
Built against the [hooks reference](https://geminicli.com/docs/hooks/reference) and [tools reference](https://geminicli.com/docs/reference/tools). Live-checked in Gemini CLI 0.66.0: a `read_file` of a denied path was blocked with the WARD reason, and an allowed read went through.
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
Apache-2.0
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* weave-gemini CLI
|
|
4
|
+
*
|
|
5
|
+
* weave-gemini init [--matcher=REGEX] [--timeout=MS] [--fail-closed] Install the WARD hook
|
|
6
|
+
* weave-gemini disable Remove it
|
|
7
|
+
* weave-gemini status Hook + active WARD.md
|
|
8
|
+
* weave-gemini test <tool> [--input=JSON] Dry-run a tool call
|
|
9
|
+
* weave-gemini hook before-tool [--fail-closed] Hook handler (run by Gemini CLI)
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* weave-gemini CLI
|
|
4
|
+
*
|
|
5
|
+
* weave-gemini init [--matcher=REGEX] [--timeout=MS] [--fail-closed] Install the WARD hook
|
|
6
|
+
* weave-gemini disable Remove it
|
|
7
|
+
* weave-gemini status Hook + active WARD.md
|
|
8
|
+
* weave-gemini test <tool> [--input=JSON] Dry-run a tool call
|
|
9
|
+
* weave-gemini hook before-tool [--fail-closed] Hook handler (run by Gemini CLI)
|
|
10
|
+
*/
|
|
11
|
+
import { installHook, removeHook, isHookInstalled, readSettings, geminiSettingsPath, userWardPath } from './config.js';
|
|
12
|
+
import { resolveWardForCwd, evaluateCall, runBeforeToolHook } from './hook.js';
|
|
13
|
+
const tty = process.stderr.isTTY && !process.env.NO_COLOR;
|
|
14
|
+
const c = {
|
|
15
|
+
reset: tty ? '\x1b[0m' : '', bold: tty ? '\x1b[1m' : '', red: tty ? '\x1b[31m' : '',
|
|
16
|
+
green: tty ? '\x1b[32m' : '', yellow: tty ? '\x1b[33m' : '', cyan: tty ? '\x1b[36m' : '', gray: tty ? '\x1b[90m' : '',
|
|
17
|
+
};
|
|
18
|
+
const say = (s = '') => console.error(s);
|
|
19
|
+
const flag = (args, name) => args.includes(`--${name}`);
|
|
20
|
+
const option = (args, name) => args.find((a) => a.startsWith(`--${name}=`))?.slice(name.length + 3);
|
|
21
|
+
function help() {
|
|
22
|
+
say(`\n${c.cyan}${c.bold}🛡️ weave-gemini${c.reset} ${c.gray}WARD.md enforcement for Gemini CLI${c.reset}\n`);
|
|
23
|
+
say(`${c.bold}Commands:${c.reset}`);
|
|
24
|
+
say(` ${c.cyan}init${c.reset} ${c.gray}[--matcher=REGEX] [--timeout=MS] [--fail-closed]${c.reset} Install the BeforeTool hook in ~/.gemini/settings.json`);
|
|
25
|
+
say(` ${c.cyan}disable${c.reset} Remove the hook`);
|
|
26
|
+
say(` ${c.cyan}status${c.reset} Show the hook and the active WARD.md`);
|
|
27
|
+
say(` ${c.cyan}test${c.reset} ${c.gray}<tool> [--input=JSON]${c.reset} Dry-run a tool call against WARD.md`);
|
|
28
|
+
say(` ${c.cyan}hook before-tool${c.reset} ${c.gray}[--fail-closed]${c.reset} Hook handler (Gemini CLI runs this)\n`);
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
function init(args) {
|
|
32
|
+
const timeout = option(args, 'timeout');
|
|
33
|
+
installHook({
|
|
34
|
+
matcher: option(args, 'matcher'),
|
|
35
|
+
timeoutMs: timeout ? Number(timeout) : undefined,
|
|
36
|
+
failClosed: flag(args, 'fail-closed'),
|
|
37
|
+
});
|
|
38
|
+
say(`${c.green}✓${c.reset} WARD hook installed in ${geminiSettingsPath()}`);
|
|
39
|
+
const ward = safeResolve(process.cwd());
|
|
40
|
+
if (!ward) {
|
|
41
|
+
say(`${c.yellow}!${c.reset} No WARD.md found here or at ${userWardPath()}; calls are allowed until you add one.`);
|
|
42
|
+
}
|
|
43
|
+
say(`${c.gray}Restart Gemini CLI for the hook to take effect.${c.reset}`);
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
function disable() {
|
|
47
|
+
const { removed } = removeHook();
|
|
48
|
+
say(removed ? `${c.green}✓${c.reset} Removed the WARD hook from ${geminiSettingsPath()}` : 'No WARD hook was installed.');
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
function safeResolve(cwd) {
|
|
52
|
+
try {
|
|
53
|
+
return resolveWardForCwd(cwd);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
say(`${c.red}✗${c.reset} ${err instanceof Error ? err.message : String(err)}`);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function status() {
|
|
61
|
+
const installed = isHookInstalled(readSettings());
|
|
62
|
+
say(`Hook: ${installed ? `${c.green}installed${c.reset}` : `${c.yellow}not installed${c.reset}`} (${geminiSettingsPath()})`);
|
|
63
|
+
const ward = safeResolve(process.cwd());
|
|
64
|
+
say(`WARD.md: ${ward ? ward.source : `${c.yellow}none found${c.reset}`}`);
|
|
65
|
+
return installed && ward ? 0 : 1;
|
|
66
|
+
}
|
|
67
|
+
function test(args) {
|
|
68
|
+
const tool = args.find((a) => !a.startsWith('--'));
|
|
69
|
+
if (!tool) {
|
|
70
|
+
say('Usage: weave-gemini test <tool> [--input=JSON]');
|
|
71
|
+
return 2;
|
|
72
|
+
}
|
|
73
|
+
const ward = safeResolve(process.cwd());
|
|
74
|
+
if (!ward) {
|
|
75
|
+
say('No WARD.md found; nothing to test against.');
|
|
76
|
+
return 2;
|
|
77
|
+
}
|
|
78
|
+
const input = JSON.parse(option(args, 'input') ?? '{}');
|
|
79
|
+
const result = evaluateCall(ward.policy, tool, input, process.cwd());
|
|
80
|
+
const color = result.decision === 'allow' ? c.green : result.decision === 'deny' ? c.red : c.yellow;
|
|
81
|
+
say(`${color}${result.decision}${c.reset} ${tool}`);
|
|
82
|
+
for (const r of result.reasons)
|
|
83
|
+
say(` ${c.gray}${r}${c.reset}`);
|
|
84
|
+
return result.decision === 'allow' ? 0 : 1;
|
|
85
|
+
}
|
|
86
|
+
async function main() {
|
|
87
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
88
|
+
switch (cmd) {
|
|
89
|
+
case 'init': return init(rest);
|
|
90
|
+
case 'disable':
|
|
91
|
+
case 'uninstall': return disable();
|
|
92
|
+
case 'status': return status();
|
|
93
|
+
case 'test': return test(rest);
|
|
94
|
+
case 'hook':
|
|
95
|
+
if (rest[0] === 'before-tool' || !rest[0])
|
|
96
|
+
return runBeforeToolHook(flag(rest, 'fail-closed'));
|
|
97
|
+
// Other events: leave the call alone
|
|
98
|
+
process.stdout.write('{}');
|
|
99
|
+
return 0;
|
|
100
|
+
default: return help();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
main().then((code) => process.exit(code), (err) => {
|
|
104
|
+
say(`weave-gemini: ${err instanceof Error ? err.message : String(err)}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvH,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE/E,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC1D,MAAM,CAAC,GAAG;IACR,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;IACnF,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;CACtH,CAAC;AACF,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEzC,MAAM,IAAI,GAAG,CAAC,IAAc,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;AAC1E,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAEtH,SAAS,IAAI;IACX,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,oBAAoB,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,qCAAqC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAC/G,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACpC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,mDAAmD,CAAC,CAAC,KAAK,0DAA0D,CAAC,CAAC;IAC7J,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,KAAK,+DAA+D,CAAC,CAAC;IACjG,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,KAAK,qFAAqF,CAAC,CAAC;IACtH,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,wBAAwB,CAAC,CAAC,KAAK,gEAAgE,CAAC,CAAC;IACxI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,mBAAmB,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,2DAA2D,CAAC,CAAC;IACzI,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,IAAI,CAAC,IAAc;IAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACxC,WAAW,CAAC;QACV,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC;QAChC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;QAChD,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC;KACtC,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,2BAA2B,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,gCAAgC,YAAY,EAAE,wCAAwC,CAAC,CAAC;IACpH,CAAC;IACD,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,kDAAkD,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1E,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,OAAO;IACd,MAAM,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,CAAC;IACjC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,+BAA+B,kBAAkB,EAAE,EAAE,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC;IAC1H,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,IAAI,CAAC;QACH,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,IAAI,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/E,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,MAAM;IACb,MAAM,SAAS,GAAG,eAAe,CAAC,YAAY,EAAE,CAAC,CAAC;IAClD,GAAG,CAAC,aAAa,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,gBAAgB,CAAC,CAAC,KAAK,EAAE,KAAK,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACjI,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACxC,GAAG,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,aAAa,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC3E,OAAO,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,IAAI,CAAC,IAAc;IAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,GAAG,CAAC,gDAAgD,CAAC,CAAC;QACtD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,GAAG,CAAC,4CAA4C,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACpG,GAAG,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO;QAAE,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACjE,OAAO,MAAM,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7C,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,EAAE,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,KAAK,SAAS,CAAC;QACf,KAAK,WAAW,EAAE,OAAO,OAAO,EAAE,CAAC;QACnC,KAAK,QAAQ,EAAE,OAAO,MAAM,EAAE,CAAC;QAC/B,KAAK,MAAM,EAAE,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,KAAK,MAAM;YACT,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,aAAa,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC;YAC/F,qCAAqC;YACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO,CAAC,CAAC;QACX,SAAS,OAAO,IAAI,EAAE,CAAC;IACzB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE;IAChD,GAAG,CAAC,iBAAiB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manage ~/.gemini/settings.json — install, remove, inspect the WARD hook.
|
|
3
|
+
*
|
|
4
|
+
* Safety rules:
|
|
5
|
+
* - Back up settings.json before changing it, and write atomically
|
|
6
|
+
* - Idempotent: init twice leaves one entry
|
|
7
|
+
* - Our entry is found by its hook name; other hooks and keys are untouched
|
|
8
|
+
*
|
|
9
|
+
* Installed at user level. Gemini CLI fingerprints project-level hooks and
|
|
10
|
+
* warns when they change; a user-level hook applies to every project.
|
|
11
|
+
*/
|
|
12
|
+
import type { GeminiHookEntry } from './types.js';
|
|
13
|
+
export declare const HOOK_NAME = "weave-ward";
|
|
14
|
+
export declare function geminiConfigDir(): string;
|
|
15
|
+
export declare function geminiSettingsPath(): string;
|
|
16
|
+
/** User-global WARD.md, used when a project has none. */
|
|
17
|
+
export declare function userWardPath(): string;
|
|
18
|
+
export interface GeminiSettings {
|
|
19
|
+
hooks?: {
|
|
20
|
+
BeforeTool?: GeminiHookEntry[];
|
|
21
|
+
[event: string]: GeminiHookEntry[] | unknown;
|
|
22
|
+
};
|
|
23
|
+
[other: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
export declare function readSettings(path?: string): GeminiSettings;
|
|
26
|
+
export declare function writeSettings(settings: GeminiSettings, path?: string): void;
|
|
27
|
+
export interface InstallOptions {
|
|
28
|
+
/** Command that runs this CLI. Default: "weave-gemini" */
|
|
29
|
+
binary?: string;
|
|
30
|
+
/** Regex of tool names to gate. Default: every tool */
|
|
31
|
+
matcher?: string;
|
|
32
|
+
/** Hook timeout in milliseconds (Gemini CLI's unit). Default 5000 */
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
/** Deny on malformed payloads and evaluator errors. Default: allow */
|
|
35
|
+
failClosed?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare function isHookInstalled(settings?: GeminiSettings): boolean;
|
|
38
|
+
export declare function installHook(opts?: InstallOptions): GeminiSettings;
|
|
39
|
+
export declare function removeHook(): {
|
|
40
|
+
removed: number;
|
|
41
|
+
settings: GeminiSettings;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,eAAO,MAAM,SAAS,eAAe,CAAC;AAEtC,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,yDAAyD;AACzD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;QAC/B,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,GAAG,OAAO,CAAC;KAC9C,CAAC;IACF,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAuB,GAAG,cAAc,CASxE;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,SAAuB,GAAG,IAAI,CAMzF;AAED,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAID,wBAAgB,eAAe,CAAC,QAAQ,GAAE,cAA+B,GAAG,OAAO,CAElF;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,cAAmB,GAAG,cAAc,CAiBrE;AAED,wBAAgB,UAAU,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,CAW1E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manage ~/.gemini/settings.json — install, remove, inspect the WARD hook.
|
|
3
|
+
*
|
|
4
|
+
* Safety rules:
|
|
5
|
+
* - Back up settings.json before changing it, and write atomically
|
|
6
|
+
* - Idempotent: init twice leaves one entry
|
|
7
|
+
* - Our entry is found by its hook name; other hooks and keys are untouched
|
|
8
|
+
*
|
|
9
|
+
* Installed at user level. Gemini CLI fingerprints project-level hooks and
|
|
10
|
+
* warns when they change; a user-level hook applies to every project.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, copyFileSync } from 'node:fs';
|
|
13
|
+
import { dirname, join } from 'node:path';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
export const HOOK_NAME = 'weave-ward';
|
|
16
|
+
export function geminiConfigDir() {
|
|
17
|
+
return join(homedir(), '.gemini');
|
|
18
|
+
}
|
|
19
|
+
export function geminiSettingsPath() {
|
|
20
|
+
return join(geminiConfigDir(), 'settings.json');
|
|
21
|
+
}
|
|
22
|
+
/** User-global WARD.md, used when a project has none. */
|
|
23
|
+
export function userWardPath() {
|
|
24
|
+
return join(geminiConfigDir(), 'WARD.md');
|
|
25
|
+
}
|
|
26
|
+
export function readSettings(path = geminiSettingsPath()) {
|
|
27
|
+
if (!existsSync(path))
|
|
28
|
+
return {};
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
throw new Error(`Failed to parse ${path}; fix the JSON or move it aside and re-run. ${err instanceof Error ? err.message : String(err)}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function writeSettings(settings, path = geminiSettingsPath()) {
|
|
37
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
38
|
+
if (existsSync(path))
|
|
39
|
+
copyFileSync(path, `${path}.weave-backup`);
|
|
40
|
+
const tmp = `${path}.tmp`;
|
|
41
|
+
writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
42
|
+
renameSync(tmp, path);
|
|
43
|
+
}
|
|
44
|
+
const ours = (e) => e.hooks?.some((h) => h.name === HOOK_NAME);
|
|
45
|
+
export function isHookInstalled(settings = readSettings()) {
|
|
46
|
+
return (settings.hooks?.BeforeTool ?? []).some(ours);
|
|
47
|
+
}
|
|
48
|
+
export function installHook(opts = {}) {
|
|
49
|
+
const settings = readSettings();
|
|
50
|
+
const command = `${opts.binary ?? 'weave-gemini'} hook before-tool${opts.failClosed ? ' --fail-closed' : ''}`;
|
|
51
|
+
const entry = {
|
|
52
|
+
matcher: opts.matcher ?? '.*',
|
|
53
|
+
hooks: [{
|
|
54
|
+
type: 'command',
|
|
55
|
+
name: HOOK_NAME,
|
|
56
|
+
command,
|
|
57
|
+
timeout: opts.timeoutMs ?? 5000,
|
|
58
|
+
description: 'Weave Protocol: enforce WARD.md on every tool call',
|
|
59
|
+
}],
|
|
60
|
+
};
|
|
61
|
+
const hooks = (settings.hooks ??= {});
|
|
62
|
+
hooks.BeforeTool = [...(hooks.BeforeTool ?? []).filter((e) => !ours(e)), entry];
|
|
63
|
+
writeSettings(settings);
|
|
64
|
+
return settings;
|
|
65
|
+
}
|
|
66
|
+
export function removeHook() {
|
|
67
|
+
const settings = readSettings();
|
|
68
|
+
const before = settings.hooks?.BeforeTool ?? [];
|
|
69
|
+
const after = before.filter((e) => !ours(e));
|
|
70
|
+
const removed = before.length - after.length;
|
|
71
|
+
if (removed > 0 && settings.hooks) {
|
|
72
|
+
if (after.length)
|
|
73
|
+
settings.hooks.BeforeTool = after;
|
|
74
|
+
else
|
|
75
|
+
delete settings.hooks.BeforeTool;
|
|
76
|
+
writeSettings(settings);
|
|
77
|
+
}
|
|
78
|
+
return { removed, settings };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/hook.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BeforeTool hook handler: reads Gemini CLI's payload from stdin, finds the
|
|
3
|
+
* active WARD.md, decides with Ward's shared evaluator, and prints the
|
|
4
|
+
* decision.
|
|
5
|
+
*
|
|
6
|
+
* WARD.md resolution (cwd = the payload's cwd):
|
|
7
|
+
* 1. $WEAVE_WARD_PATH
|
|
8
|
+
* 2. <cwd>/WARD.md
|
|
9
|
+
* 3. <cwd>/.gemini/WARD.md
|
|
10
|
+
* 4. <cwd>/.weave/WARD.md
|
|
11
|
+
* 5. ~/.gemini/WARD.md (user-global)
|
|
12
|
+
*
|
|
13
|
+
* Output: a deny prints { decision: "deny", reason }. Anything else prints
|
|
14
|
+
* {} so Gemini CLI's own confirmation flow still applies (an explicit
|
|
15
|
+
* "allow" is never sent). Gemini CLI has no "ask", so require_approval is a
|
|
16
|
+
* deny that says approval is needed.
|
|
17
|
+
*
|
|
18
|
+
* Failure modes:
|
|
19
|
+
* - A WARD.md that exists but is invalid always denies: a typo must not
|
|
20
|
+
* silently switch enforcement off.
|
|
21
|
+
* - Malformed payloads and evaluator errors allow by default (with a note
|
|
22
|
+
* on stderr); --fail-closed denies instead.
|
|
23
|
+
*/
|
|
24
|
+
import { type WardPolicy } from '@weave_protocol/ward';
|
|
25
|
+
import { type GeminiHookOutput } from './types.js';
|
|
26
|
+
export interface ResolvedWard {
|
|
27
|
+
policy: WardPolicy;
|
|
28
|
+
source: string;
|
|
29
|
+
}
|
|
30
|
+
export interface HookDecision {
|
|
31
|
+
decision: 'allow' | 'deny' | 'require_approval';
|
|
32
|
+
reasons: string[];
|
|
33
|
+
}
|
|
34
|
+
export declare function resolveWardForCwd(cwd: string): ResolvedWard | null;
|
|
35
|
+
/**
|
|
36
|
+
* Shape Gemini CLI arguments for Ward: web_fetch's URLs live inside its
|
|
37
|
+
* free-text prompt, and a shell command runs in dir_path.
|
|
38
|
+
*/
|
|
39
|
+
export declare function normalizeInput(toolName: string, input: Record<string, unknown> | undefined, cwd: string): {
|
|
40
|
+
args: {
|
|
41
|
+
[x: string]: unknown;
|
|
42
|
+
};
|
|
43
|
+
commandCwd: string;
|
|
44
|
+
};
|
|
45
|
+
/** Decide a Gemini CLI tool call against a WARD policy. */
|
|
46
|
+
export declare function evaluateCall(policy: WardPolicy, toolName: string, toolInput: Record<string, unknown> | undefined, cwd?: string): HookDecision;
|
|
47
|
+
/** WARD decision → Gemini CLI output. */
|
|
48
|
+
export declare function toHookOutput(result: HookDecision, source: string): GeminiHookOutput;
|
|
49
|
+
export declare function runBeforeToolHook(failClosed: boolean): Promise<never>;
|
|
50
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACxG,OAAO,EAAuC,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGxF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,kBAAkB,CAAC;IAChD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAmBlE;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM;;;;;EAUvG;AAED,2DAA2D;AAC3D,wBAAgB,YAAY,CAC1B,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC9C,GAAG,GAAE,MAAsB,GAC1B,YAAY,CAId;AAED,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAWnF;AAeD,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAsC3E"}
|
package/dist/hook.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BeforeTool hook handler: reads Gemini CLI's payload from stdin, finds the
|
|
3
|
+
* active WARD.md, decides with Ward's shared evaluator, and prints the
|
|
4
|
+
* decision.
|
|
5
|
+
*
|
|
6
|
+
* WARD.md resolution (cwd = the payload's cwd):
|
|
7
|
+
* 1. $WEAVE_WARD_PATH
|
|
8
|
+
* 2. <cwd>/WARD.md
|
|
9
|
+
* 3. <cwd>/.gemini/WARD.md
|
|
10
|
+
* 4. <cwd>/.weave/WARD.md
|
|
11
|
+
* 5. ~/.gemini/WARD.md (user-global)
|
|
12
|
+
*
|
|
13
|
+
* Output: a deny prints { decision: "deny", reason }. Anything else prints
|
|
14
|
+
* {} so Gemini CLI's own confirmation flow still applies (an explicit
|
|
15
|
+
* "allow" is never sent). Gemini CLI has no "ask", so require_approval is a
|
|
16
|
+
* deny that says approval is needed.
|
|
17
|
+
*
|
|
18
|
+
* Failure modes:
|
|
19
|
+
* - A WARD.md that exists but is invalid always denies: a typo must not
|
|
20
|
+
* silently switch enforcement off.
|
|
21
|
+
* - Malformed payloads and evaluator errors allow by default (with a note
|
|
22
|
+
* on stderr); --fail-closed denies instead.
|
|
23
|
+
*/
|
|
24
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
25
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
26
|
+
import { parseAndValidate, evaluateToolCall, reportEvent } from '@weave_protocol/ward';
|
|
27
|
+
import { TOOL_MAPPINGS } from './types.js';
|
|
28
|
+
import { userWardPath } from './config.js';
|
|
29
|
+
export function resolveWardForCwd(cwd) {
|
|
30
|
+
const candidates = [
|
|
31
|
+
process.env.WEAVE_WARD_PATH,
|
|
32
|
+
cwd && join(cwd, 'WARD.md'),
|
|
33
|
+
cwd && join(cwd, '.gemini', 'WARD.md'),
|
|
34
|
+
cwd && join(cwd, '.weave', 'WARD.md'),
|
|
35
|
+
userWardPath(),
|
|
36
|
+
].filter((p) => !!p);
|
|
37
|
+
for (const path of candidates) {
|
|
38
|
+
const full = resolve(path);
|
|
39
|
+
if (!existsSync(full))
|
|
40
|
+
continue;
|
|
41
|
+
const result = parseAndValidate(readFileSync(full, 'utf8'));
|
|
42
|
+
if (!result.valid || !result.policy) {
|
|
43
|
+
throw new Error(`Invalid WARD.md at ${full}: ${result.errors.map((e) => e.message).join('; ')}`);
|
|
44
|
+
}
|
|
45
|
+
return { policy: result.policy, source: full };
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const URL_IN_TEXT = /[a-z][a-z0-9+.-]*:\/\/[^\s'"`<>()]+/gi;
|
|
50
|
+
/**
|
|
51
|
+
* Shape Gemini CLI arguments for Ward: web_fetch's URLs live inside its
|
|
52
|
+
* free-text prompt, and a shell command runs in dir_path.
|
|
53
|
+
*/
|
|
54
|
+
export function normalizeInput(toolName, input, cwd) {
|
|
55
|
+
const args = { ...(input ?? {}) };
|
|
56
|
+
let commandCwd = cwd;
|
|
57
|
+
if (toolName === 'web_fetch' && typeof args.prompt === 'string') {
|
|
58
|
+
args.urls = (args.prompt.match(URL_IN_TEXT) ?? []).map((u) => u.replace(/[.,;:!?]+$/, ''));
|
|
59
|
+
}
|
|
60
|
+
if (toolName === 'run_shell_command' && typeof args.dir_path === 'string' && args.dir_path) {
|
|
61
|
+
commandCwd = isAbsolute(args.dir_path) ? args.dir_path : join(cwd, args.dir_path);
|
|
62
|
+
}
|
|
63
|
+
return { args, commandCwd };
|
|
64
|
+
}
|
|
65
|
+
/** Decide a Gemini CLI tool call against a WARD policy. */
|
|
66
|
+
export function evaluateCall(policy, toolName, toolInput, cwd = process.cwd()) {
|
|
67
|
+
const { args, commandCwd } = normalizeInput(toolName, toolInput, cwd);
|
|
68
|
+
const { decision, reasons } = evaluateToolCall(policy, toolName, args, { mappings: TOOL_MAPPINGS, cwd: commandCwd });
|
|
69
|
+
return { decision, reasons };
|
|
70
|
+
}
|
|
71
|
+
/** WARD decision → Gemini CLI output. */
|
|
72
|
+
export function toHookOutput(result, source) {
|
|
73
|
+
if (result.decision === 'deny') {
|
|
74
|
+
return { decision: 'deny', reason: `WARD: ${result.reasons.join(' | ')} (policy: ${source})` };
|
|
75
|
+
}
|
|
76
|
+
if (result.decision === 'require_approval') {
|
|
77
|
+
return {
|
|
78
|
+
decision: 'deny',
|
|
79
|
+
reason: `WARD requires approval before this call: ${result.reasons.join(' | ')} (policy: ${source}). Ask the user to approve it or update WARD.md.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
async function readStdin() {
|
|
85
|
+
if (process.stdin.isTTY)
|
|
86
|
+
return '';
|
|
87
|
+
let data = '';
|
|
88
|
+
process.stdin.setEncoding('utf8');
|
|
89
|
+
for await (const chunk of process.stdin)
|
|
90
|
+
data += chunk;
|
|
91
|
+
return data;
|
|
92
|
+
}
|
|
93
|
+
function emit(output) {
|
|
94
|
+
process.stdout.write(JSON.stringify(output));
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
export async function runBeforeToolHook(failClosed) {
|
|
98
|
+
const raw = await readStdin();
|
|
99
|
+
if (!raw.trim())
|
|
100
|
+
emit({});
|
|
101
|
+
let payload;
|
|
102
|
+
try {
|
|
103
|
+
payload = JSON.parse(raw);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
if (failClosed)
|
|
107
|
+
emit({ decision: 'deny', reason: 'WARD hook: malformed payload from Gemini CLI' });
|
|
108
|
+
process.stderr.write('weave-gemini: malformed hook payload, allowing\n');
|
|
109
|
+
emit({});
|
|
110
|
+
}
|
|
111
|
+
if (!payload.tool_name)
|
|
112
|
+
emit({});
|
|
113
|
+
const cwd = payload.cwd || process.env.GEMINI_PROJECT_DIR || process.cwd();
|
|
114
|
+
let resolved;
|
|
115
|
+
try {
|
|
116
|
+
resolved = resolveWardForCwd(cwd);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
// A policy exists but can't be used: deny rather than run unprotected.
|
|
120
|
+
emit({ decision: 'deny', reason: `WARD policy could not be loaded: ${err instanceof Error ? err.message : String(err)}. Fix WARD.md to continue.` });
|
|
121
|
+
}
|
|
122
|
+
if (!resolved)
|
|
123
|
+
emit({});
|
|
124
|
+
let result;
|
|
125
|
+
try {
|
|
126
|
+
result = evaluateCall(resolved.policy, payload.tool_name, payload.tool_input, cwd);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
130
|
+
if (failClosed)
|
|
131
|
+
emit({ decision: 'deny', reason: `WARD evaluation failed: ${msg}` });
|
|
132
|
+
process.stderr.write(`weave-gemini: WARD evaluation failed (${msg}), allowing\n`);
|
|
133
|
+
emit({});
|
|
134
|
+
}
|
|
135
|
+
// Dashboard reporting (only when WEAVE_DASHBOARD_URL is set; at most 250ms)
|
|
136
|
+
await reportEvent({ surface: 'gemini-cli', decision: result.decision, subject: payload.tool_name, reason: result.reasons[0] });
|
|
137
|
+
emit(toHookOutput(result, resolved.source));
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=hook.js.map
|
package/dist/hook.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @weave_protocol/adapter-geminicli — WARD.md enforcement for Gemini CLI.
|
|
3
|
+
*/
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export { HOOK_NAME, geminiConfigDir, geminiSettingsPath, userWardPath, readSettings, writeSettings, installHook, removeHook, isHookInstalled, } from './config.js';
|
|
6
|
+
export type { GeminiSettings, InstallOptions } from './config.js';
|
|
7
|
+
export { resolveWardForCwd, evaluateCall, normalizeInput, toHookOutput } from './hook.js';
|
|
8
|
+
export type { ResolvedWard, HookDecision } from './hook.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,SAAS,EACT,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC1F,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @weave_protocol/adapter-geminicli — WARD.md enforcement for Gemini CLI.
|
|
3
|
+
*/
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export { HOOK_NAME, geminiConfigDir, geminiSettingsPath, userWardPath, readSettings, writeSettings, installHook, removeHook, isHookInstalled, } from './config.js';
|
|
6
|
+
export { resolveWardForCwd, evaluateCall, normalizeInput, toHookOutput } from './hook.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,SAAS,EACT,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI hook contract (https://geminicli.com/docs/hooks/reference):
|
|
3
|
+
*
|
|
4
|
+
* stdin { session_id, transcript_path, cwd, hook_event_name, timestamp,
|
|
5
|
+
* tool_name, tool_input, mcp_context? }
|
|
6
|
+
* stdout { decision: "allow" | "deny", reason } (JSON only)
|
|
7
|
+
*
|
|
8
|
+
* There is no "ask" decision, so a WARD require_approval is a deny whose
|
|
9
|
+
* reason says approval is needed. MCP tools arrive as mcp_<server>_<tool>.
|
|
10
|
+
*/
|
|
11
|
+
import type { ToolMapping } from '@weave_protocol/ward';
|
|
12
|
+
export interface GeminiHookInput {
|
|
13
|
+
session_id?: string;
|
|
14
|
+
transcript_path?: string;
|
|
15
|
+
cwd?: string;
|
|
16
|
+
hook_event_name?: string;
|
|
17
|
+
timestamp?: string;
|
|
18
|
+
tool_name?: string;
|
|
19
|
+
tool_input?: Record<string, unknown>;
|
|
20
|
+
mcp_context?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export interface GeminiHookOutput {
|
|
23
|
+
decision?: 'allow' | 'deny';
|
|
24
|
+
reason?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Gemini CLI's built-in tools (https://geminicli.com/docs/reference/tools).
|
|
28
|
+
* Unknown tools, including MCP tools, use their own name as the capability;
|
|
29
|
+
* Ward still checks every path- and URL-named argument they carry.
|
|
30
|
+
*/
|
|
31
|
+
export declare const TOOL_MAPPINGS: Record<string, ToolMapping>;
|
|
32
|
+
/** One BeforeTool entry in settings.json. */
|
|
33
|
+
export interface GeminiHookEntry {
|
|
34
|
+
matcher?: string;
|
|
35
|
+
sequential?: boolean;
|
|
36
|
+
hooks: Array<{
|
|
37
|
+
type: 'command';
|
|
38
|
+
command: string;
|
|
39
|
+
name?: string;
|
|
40
|
+
timeout?: number;
|
|
41
|
+
description?: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CA8BrD,CAAC;AAEF,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACJ"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini CLI hook contract (https://geminicli.com/docs/hooks/reference):
|
|
3
|
+
*
|
|
4
|
+
* stdin { session_id, transcript_path, cwd, hook_event_name, timestamp,
|
|
5
|
+
* tool_name, tool_input, mcp_context? }
|
|
6
|
+
* stdout { decision: "allow" | "deny", reason } (JSON only)
|
|
7
|
+
*
|
|
8
|
+
* There is no "ask" decision, so a WARD require_approval is a deny whose
|
|
9
|
+
* reason says approval is needed. MCP tools arrive as mcp_<server>_<tool>.
|
|
10
|
+
*/
|
|
11
|
+
/** Shared by the planning and interaction tools, so a policy can allow them in one line. */
|
|
12
|
+
const PLANNING = { capability: 'task_planning' };
|
|
13
|
+
/**
|
|
14
|
+
* Gemini CLI's built-in tools (https://geminicli.com/docs/reference/tools).
|
|
15
|
+
* Unknown tools, including MCP tools, use their own name as the capability;
|
|
16
|
+
* Ward still checks every path- and URL-named argument they carry.
|
|
17
|
+
*/
|
|
18
|
+
export const TOOL_MAPPINGS = {
|
|
19
|
+
// dir_path is where the command runs: checked as a read
|
|
20
|
+
run_shell_command: { capability: 'shell_exec', commandField: 'command', pathField: 'dir_path', fsOp: 'read' },
|
|
21
|
+
read_file: { capability: 'file_read', pathField: 'file_path', fsOp: 'read' },
|
|
22
|
+
read_many_files: { capability: 'file_read', pathField: 'include', fsOp: 'read' },
|
|
23
|
+
write_file: { capability: 'file_write', pathField: 'file_path', fsOp: 'write' },
|
|
24
|
+
replace: { capability: 'file_write', pathField: 'file_path', fsOp: 'write' },
|
|
25
|
+
list_directory: { capability: 'file_list', pathField: 'dir_path', fsOp: 'list' },
|
|
26
|
+
glob: { capability: 'file_list', pathField: 'dir_path', fsOp: 'list' },
|
|
27
|
+
grep_search: { capability: 'file_read', pathField: 'dir_path', fsOp: 'read' },
|
|
28
|
+
search_file_content: { capability: 'file_read', pathField: 'dir_path', fsOp: 'read' },
|
|
29
|
+
// web_fetch takes a free-text prompt; the hook pulls its URLs into "urls"
|
|
30
|
+
web_fetch: { capability: 'http_request', urlField: 'urls' },
|
|
31
|
+
google_web_search: { capability: 'web_search' },
|
|
32
|
+
save_memory: { capability: 'memory_write' },
|
|
33
|
+
// Not in the tools reference, but offered by 0.66.0
|
|
34
|
+
invoke_agent: { capability: 'subagent' },
|
|
35
|
+
activate_skill: { capability: 'skill' },
|
|
36
|
+
get_internal_docs: { capability: 'docs_read' },
|
|
37
|
+
ask_user: PLANNING,
|
|
38
|
+
write_todos: PLANNING,
|
|
39
|
+
enter_plan_mode: PLANNING,
|
|
40
|
+
exit_plan_mode: PLANNING,
|
|
41
|
+
update_topic: PLANNING,
|
|
42
|
+
tracker_create_task: PLANNING,
|
|
43
|
+
tracker_update_task: PLANNING,
|
|
44
|
+
tracker_get_task: PLANNING,
|
|
45
|
+
tracker_list_tasks: PLANNING,
|
|
46
|
+
tracker_add_dependency: PLANNING,
|
|
47
|
+
tracker_visualize: PLANNING,
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoBH,4FAA4F;AAC5F,MAAM,QAAQ,GAAgB,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC;AAE9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAgC;IACxD,wDAAwD;IACxD,iBAAiB,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE;IAC7G,SAAS,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE;IAC5E,eAAe,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE;IAChF,UAAU,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE;IAC/E,OAAO,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE;IAC5E,cAAc,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE;IAChF,IAAI,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE;IACtE,WAAW,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE;IAC7E,mBAAmB,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE;IACrF,0EAA0E;IAC1E,SAAS,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE;IAC3D,iBAAiB,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE;IAC/C,WAAW,EAAE,EAAE,UAAU,EAAE,cAAc,EAAE;IAC3C,oDAAoD;IACpD,YAAY,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE;IACxC,cAAc,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE;IACvC,iBAAiB,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE;IAC9C,QAAQ,EAAE,QAAQ;IAClB,WAAW,EAAE,QAAQ;IACrB,eAAe,EAAE,QAAQ;IACzB,cAAc,EAAE,QAAQ;IACxB,YAAY,EAAE,QAAQ;IACtB,mBAAmB,EAAE,QAAQ;IAC7B,mBAAmB,EAAE,QAAQ;IAC7B,gBAAgB,EAAE,QAAQ;IAC1B,kBAAkB,EAAE,QAAQ;IAC5B,sBAAsB,EAAE,QAAQ;IAChC,iBAAiB,EAAE,QAAQ;CAC5B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weave_protocol/adapter-geminicli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Weave Protocol adapter for Gemini CLI - enforces WARD.md policies via Gemini CLI's BeforeTool hook",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"weave-gemini": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch",
|
|
14
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"weave-protocol",
|
|
19
|
+
"ward",
|
|
20
|
+
"gemini-cli",
|
|
21
|
+
"gemini",
|
|
22
|
+
"google",
|
|
23
|
+
"agent-security",
|
|
24
|
+
"ai-security",
|
|
25
|
+
"hooks",
|
|
26
|
+
"harness-adapter",
|
|
27
|
+
"policy-enforcement",
|
|
28
|
+
"ward-md"
|
|
29
|
+
],
|
|
30
|
+
"author": "Trent Yoxall",
|
|
31
|
+
"license": "Apache-2.0",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/Tyox-all/Weave_Protocol.git",
|
|
35
|
+
"directory": "adapter-geminicli"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/Tyox-all/Weave_Protocol/tree/main/adapter-geminicli#readme",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/Tyox-all/Weave_Protocol/issues"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@weave_protocol/ward": "^0.3.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^26.6.6",
|
|
46
|
+
"typescript": "^7.0.2"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.0.0"
|
|
50
|
+
},
|
|
51
|
+
"files": [
|
|
52
|
+
"dist/",
|
|
53
|
+
"README.md"
|
|
54
|
+
]
|
|
55
|
+
}
|