From 9cbcba21f59edc5b741b6e8bfeef80aa1801502c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sun, 19 Jul 2026 10:31:33 -0400 Subject: [PATCH] docs: verify Factory Droid hook contract --- AGENTS.md | 1 + integrations/catalog.json | 32 ++++++++++++++++++++-- test/integration-catalog.test.mjs | 45 +++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b8f75e7..fc4f56e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,6 +28,7 @@ This repository publishes the open-source server, console, integration catalog, - 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, preserve official hook input byte-for-byte, use `process.execPath` in Node and Go wrappers, and resolve Python runtimes from absolute shebangs. Translate guard exit code `2` into JSON stdout with `cancel: true`. Treat hook errors, timeouts, and invalid control JSON as fail-open. +- Preserve Factory Droid's scope-root `hooks.json` precedence, legacy nested hook source, and per-event `settings.json` fallback. Edit only effective user sources; keep project and organization sources read-only. ## Mirror Workflow diff --git a/integrations/catalog.json b/integrations/catalog.json index a13c2e5..1369988 100644 --- a/integrations/catalog.json +++ b/integrations/catalog.json @@ -169,7 +169,9 @@ "config_format": "json", "config_paths": [ "~/.factory/hooks.json", - ".factory/hooks.json" + "~/.factory/settings.json", + ".factory/hooks.json", + ".factory/settings.json" ], "events": { "before_tool": "PreToolUse", @@ -186,7 +188,33 @@ }, "adapters": { "node": false, "python": false, "go": false }, "delivery_state": "planned", - "source_url": "https://docs.factory.ai/reference/hooks-reference" + "source_url": "https://docs.factory.ai/reference/hooks-reference", + "contract_variants": [ + { + "id": "nestedhooks", + "release_channel": "legacy", + "activation": "An existing nested hooks file remains active until Droid saves and archives it", + "config_format": "json", + "config_paths": [ + "~/.factory/hooks/hooks.json", + ".factory/hooks/hooks.json" + ], + "events": { + "before_tool": "PreToolUse", + "after_tool": "PostToolUse" + }, + "event_fields": { + "name": "tool_name", + "input": "tool_input", + "session": "session_id" + }, + "blocking": { + "mechanism": "exit_code_2", + "failure_mode": "fail_open" + }, + "source_url": "https://docs.factory.ai/reference/hooks-reference" + } + ] }, { "id": "gemini", diff --git a/test/integration-catalog.test.mjs b/test/integration-catalog.test.mjs index cc9095e..92400af 100644 --- a/test/integration-catalog.test.mjs +++ b/test/integration-catalog.test.mjs @@ -214,6 +214,51 @@ test('high-drift providers retain their verified hook contracts', async () => { '~/.codex/hooks.json', '.codex/hooks.json', ]); + assert.deepEqual(providers.get('droid').config_paths, [ + '~/.factory/hooks.json', + '~/.factory/settings.json', + '.factory/hooks.json', + '.factory/settings.json', + ]); + assert.deepEqual(providers.get('droid').events, { + before_tool: 'PreToolUse', + after_tool: 'PostToolUse', + }); + assert.deepEqual(providers.get('droid').event_fields, { + name: 'tool_name', + input: 'tool_input', + session: 'session_id', + }); + assert.deepEqual(providers.get('droid').blocking, { + mechanism: 'exit_code_2', + failure_mode: 'fail_open', + }); + assert.deepEqual(providers.get('droid').contract_variants, [ + { + id: 'nestedhooks', + release_channel: 'legacy', + activation: 'An existing nested hooks file remains active until Droid saves and archives it', + config_format: 'json', + config_paths: [ + '~/.factory/hooks/hooks.json', + '.factory/hooks/hooks.json', + ], + events: { + before_tool: 'PreToolUse', + after_tool: 'PostToolUse', + }, + event_fields: { + name: 'tool_name', + input: 'tool_input', + session: 'session_id', + }, + blocking: { + mechanism: 'exit_code_2', + failure_mode: 'fail_open', + }, + source_url: 'https://docs.factory.ai/reference/hooks-reference', + }, + ]); assert.equal(providers.get('cline').integration_mode, 'command_hooks'); assert.equal(providers.get('cline').config_format, 'script'); assert.deepEqual(providers.get('cline').config_paths, [