docs: verify Cline file hook contract
This commit is contained in:
1 parent
51921e9101
commit
3355cd4d60
4 files changed
+62
-14
No files matched your search
@@ -27,6 +27,7 @@ This repository publishes the open-source server, console, integration catalog,
|
||||
- Write Grok Build integrations to a native user hook file under non-empty `GROK_HOME` or `~/.grok`; keep Claude Code and Cursor compatibility sources read-only.
|
||||
- Write Auggie hooks only to `~/.augment/settings.json`; keep system and workspace settings read-only. Generate `.cmd` wrappers on Windows and `.sh` wrappers on Unix because Auggie dispatches supported script paths, and express hook timeouts in milliseconds.
|
||||
- Validate Auggie matcher syntax during installation with Node.js `new RegExp`; keep status and uninstall independent from the JavaScript validator so recovery remains available offline.
|
||||
- Write Cline file hooks to `$CLINE_DIR/hooks` with `~/.cline/hooks` as the default; keep Documents and workspace hook roots read-only. Use supported event filenames and return JSON stdout with `cancel: true` for blocking. Treat hook errors, timeouts, and invalid control JSON as fail-open.
|
||||
|
||||
## Mirror Workflow
|
||||
|
||||
|
||||
@@ -64,20 +64,30 @@
|
||||
"name": "Cline",
|
||||
"vendor": "Cline",
|
||||
"surface": "cli_and_ide",
|
||||
"integration_mode": "plugin_api",
|
||||
"config_format": "plugin",
|
||||
"config_paths": [],
|
||||
"integration_mode": "command_hooks",
|
||||
"config_format": "script",
|
||||
"config_paths": [
|
||||
"~/Documents/Cline/Hooks/",
|
||||
"$CLINE_DIR/hooks/",
|
||||
".clinerules/hooks/",
|
||||
".cline/hooks/"
|
||||
],
|
||||
"events": {
|
||||
"before_tool": "beforeTool",
|
||||
"after_tool": "afterTool"
|
||||
"before_tool": "PreToolUse",
|
||||
"after_tool": "PostToolUse"
|
||||
},
|
||||
"event_fields": {
|
||||
"name": "tool_call.name/tool_result.name",
|
||||
"input": "tool_call.input/tool_result.input",
|
||||
"session": "taskId"
|
||||
},
|
||||
"blocking": {
|
||||
"mechanism": "hook_policy",
|
||||
"failure_mode": "configurable"
|
||||
"mechanism": "json_stdout_cancel",
|
||||
"failure_mode": "fail_open"
|
||||
},
|
||||
"adapters": { "node": false, "python": false, "go": false },
|
||||
"delivery_state": "planned",
|
||||
"source_url": "https://docs.cline.bot/sdk/plugins"
|
||||
"source_url": "https://github.com/cline/cline/blob/main/sdk/examples/hooks/README.md"
|
||||
},
|
||||
{
|
||||
"id": "codex",
|
||||
|
||||
@@ -71,7 +71,8 @@
|
||||
"enum": [
|
||||
"json",
|
||||
"toml",
|
||||
"plugin"
|
||||
"plugin",
|
||||
"script"
|
||||
]
|
||||
},
|
||||
"config_paths": {
|
||||
@@ -136,7 +137,8 @@
|
||||
"any_nonzero_exit",
|
||||
"exception",
|
||||
"exit_code_2",
|
||||
"hook_policy"
|
||||
"hook_policy",
|
||||
"json_stdout_cancel"
|
||||
]
|
||||
},
|
||||
"failure_mode": {
|
||||
@@ -252,7 +254,8 @@
|
||||
"enum": [
|
||||
"json",
|
||||
"toml",
|
||||
"plugin"
|
||||
"plugin",
|
||||
"script"
|
||||
]
|
||||
},
|
||||
"config_paths": {
|
||||
@@ -318,7 +321,8 @@
|
||||
"any_nonzero_exit",
|
||||
"exception",
|
||||
"exit_code_2",
|
||||
"hook_policy"
|
||||
"hook_policy",
|
||||
"json_stdout_cancel"
|
||||
]
|
||||
},
|
||||
"failure_mode": {
|
||||
|
||||
@@ -93,7 +93,7 @@ test('every provider exposes a complete, machine-readable hook contract', async
|
||||
assert.equal(typeof provider.vendor, 'string');
|
||||
assert.ok(['cli', 'ide', 'cli_and_ide'].includes(provider.surface));
|
||||
assert.ok(['command_hooks', 'plugin_api'].includes(provider.integration_mode));
|
||||
assert.ok(['json', 'toml', 'plugin'].includes(provider.config_format));
|
||||
assert.ok(['json', 'toml', 'plugin', 'script'].includes(provider.config_format));
|
||||
assert.ok(Array.isArray(provider.config_paths));
|
||||
assert.equal(typeof provider.events.before_tool, 'string');
|
||||
assert.equal(typeof provider.events.after_tool, 'string');
|
||||
@@ -102,6 +102,7 @@ test('every provider exposes a complete, machine-readable hook contract', async
|
||||
'exception',
|
||||
'exit_code_2',
|
||||
'hook_policy',
|
||||
'json_stdout_cancel',
|
||||
].includes(provider.blocking.mechanism));
|
||||
assert.ok([
|
||||
'adapter_controlled',
|
||||
@@ -128,7 +129,7 @@ test('every provider exposes a complete, machine-readable hook contract', async
|
||||
assert.match(variant.id, /^[a-z][a-z0-9]*$/);
|
||||
assert.ok(['stable', 'early_access', 'legacy'].includes(variant.release_channel));
|
||||
assert.equal(typeof variant.activation, 'string');
|
||||
assert.ok(['json', 'toml', 'plugin'].includes(variant.config_format));
|
||||
assert.ok(['json', 'toml', 'plugin', 'script'].includes(variant.config_format));
|
||||
assert.ok(Array.isArray(variant.config_paths) && variant.config_paths.length > 0);
|
||||
assert.equal(typeof variant.events.before_tool, 'string');
|
||||
assert.equal(typeof variant.events.after_tool, 'string');
|
||||
@@ -148,6 +149,17 @@ test('schema freezes the provider contract and supported enums', async () => {
|
||||
assert.deepEqual([...providerSchema.required].sort(), [...requiredProviderFields].sort());
|
||||
assert.deepEqual(providerSchema.properties.surface.enum, ['cli', 'ide', 'cli_and_ide']);
|
||||
assert.deepEqual(providerSchema.properties.integration_mode.enum, ['command_hooks', 'plugin_api']);
|
||||
assert.deepEqual(providerSchema.properties.config_format.enum, [
|
||||
'json',
|
||||
'toml',
|
||||
'plugin',
|
||||
'script',
|
||||
]);
|
||||
assert.ok(
|
||||
providerSchema.properties.blocking.properties.mechanism.enum.includes(
|
||||
'json_stdout_cancel',
|
||||
),
|
||||
);
|
||||
assert.deepEqual(providerSchema.properties.delivery_state.enum, ['available', 'partial', 'planned']);
|
||||
assert.equal(providerSchema.properties.contract_variants.items.$ref, '#/$defs/contractVariant');
|
||||
assert.deepEqual(schema.$defs.contractVariant.properties.release_channel.enum, [
|
||||
@@ -202,6 +214,27 @@ test('high-drift providers retain their verified hook contracts', async () => {
|
||||
'~/.codex/hooks.json',
|
||||
'.codex/hooks.json',
|
||||
]);
|
||||
assert.equal(providers.get('cline').integration_mode, 'command_hooks');
|
||||
assert.equal(providers.get('cline').config_format, 'script');
|
||||
assert.deepEqual(providers.get('cline').config_paths, [
|
||||
'~/Documents/Cline/Hooks/',
|
||||
'$CLINE_DIR/hooks/',
|
||||
'.clinerules/hooks/',
|
||||
'.cline/hooks/',
|
||||
]);
|
||||
assert.deepEqual(providers.get('cline').events, {
|
||||
before_tool: 'PreToolUse',
|
||||
after_tool: 'PostToolUse',
|
||||
});
|
||||
assert.deepEqual(providers.get('cline').event_fields, {
|
||||
name: 'tool_call.name/tool_result.name',
|
||||
input: 'tool_call.input/tool_result.input',
|
||||
session: 'taskId',
|
||||
});
|
||||
assert.deepEqual(providers.get('cline').blocking, {
|
||||
mechanism: 'json_stdout_cancel',
|
||||
failure_mode: 'fail_open',
|
||||
});
|
||||
assert.deepEqual(providers.get('kimi').events, {
|
||||
before_tool: 'PreToolUse',
|
||||
after_tool: 'PostToolUse',
|
||||
|
||||
Reference in new issue
Block a user