Skip to content

feat(hooks): add Factory Droid integration#1

Open
krimvp wants to merge 4 commits into
developfrom
claude/rebase-pr-2267-0amiwo
Open

feat(hooks): add Factory Droid integration#1
krimvp wants to merge 4 commits into
developfrom
claude/rebase-pr-2267-0amiwo

Conversation

@krimvp

@krimvp krimvp commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Register RTK with Factory Droid so shell commands the agent runs are
transparently rewritten to their token-saving rtk equivalents.

  • rtk init --agent droid: install a native PreToolUse hook into
    ~/.factory/settings.json (matcher "Execute"); supports global (-g) and
    project scope, the FACTORY_HOME override, idempotent install with backup +
    atomic write, and a clean uninstall round-trip.
  • rtk hook droid: process Droid's PreToolUse payload (BOM strip, stdin cap,
    JSON-parse fallback), mirroring the existing provider hooks.
  • Deny verdict steps aside (no output) so Droid's native deny handling fires,
    matching Claude/Cursor/Copilot and the PermissionVerdict::Deny contract.
  • Rewrites are auto-allowed: Droid only applies a hook's updatedInput when the
    decision is "allow", so we mirror run_cursor to avoid silently dropping the
    rewrite (and its savings) on the default verdict.

krimvp and others added 4 commits July 4, 2026 21:16
Register RTK with Factory Droid so shell commands the agent runs are
transparently rewritten to their token-saving `rtk` equivalents.

- `rtk init --agent droid`: install a native PreToolUse hook into
  ~/.factory/settings.json (matcher "Execute"); supports global (-g) and
  project scope, the FACTORY_HOME override, idempotent install with backup +
  atomic write, and a clean uninstall round-trip.
- `rtk hook droid`: process Droid's PreToolUse payload (BOM strip, stdin cap,
  JSON-parse fallback), mirroring the existing provider hooks.
- Deny verdict steps aside (no output) so Droid's native deny handling fires,
  matching Claude/Cursor/Copilot and the PermissionVerdict::Deny contract.
- Rewrites are auto-allowed: Droid only applies a hook's updatedInput when the
  decision is "allow", so we mirror run_cursor to avoid silently dropping the
  rewrite (and its savings) on the default verdict.
Droid 0.140.0 applies a PreToolUse hook's updatedInput regardless of the
permission decision (verified empirically + via binary decompile), so the
forced permissionDecision:"allow" was unnecessary. Worse, Droid resolves
the decision as the first hook result carrying one (no deny-over-allow
priority), so an unconditional allow could suppress another PreToolUse
hook's deny/ask and bypass Droid's native prompt.

Now omit permissionDecision by default and only assert allow on an explicit
allow rule, mirroring process_claude_payload. The rewrite still lands via
Droid's decision-independent "updated input result" path. Updated the
inaccurate comment and the test that required allow.
Adopt the decide_from_verdict/HookDecision flow introduced on develop so
the Droid handler gains the same safety behavior as Claude/Cursor/Copilot:
commands with substitution or file redirects now Defer (no rewrite) instead
of being rewritten, and deny/allow/ask mapping is shared instead of
reimplemented. Also removes an unwrap() in the response builder.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vp4p5YroP2cfFvThhQE4Rv
Adversarial verification against Droid v0.164.0 (shipped code + docs)
found two broken assumptions in the original integration:

- Droid's canonical hooks file is hooks.json (root event map, no wrapper);
  the hooks key of settings.json is only a fallback that hooks.json shadows
  per event key. Droid's own /hooks UI writes hooks.json, so an RTK entry
  in settings.json silently dies as soon as the user adds any PreToolUse
  hook there. Install now targets the file whose PreToolUse array Droid
  actually reads (live hooks.json > live settings.json fallback > create
  canonical hooks.json), migrates stale copies, and uninstall sweeps root
  hooks.json, legacy hooks/hooks.json, and settings.json.

- The config-home env var is FACTORY_HOME_OVERRIDE (replaces $HOME, with
  .factory appended), not FACTORY_HOME pointing at the config dir.

Also corrects the 'legacy Bash matcher' comment (Droid never had a Bash
tool; the acceptance is purely defensive) and records that the
omit-permissionDecision rewrite path is confirmed in v0.164.0: decisions
resolve as find(first result with a decision) and updatedInput applies
via a separate path even when no decision is set.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vp4p5YroP2cfFvThhQE4Rv
@krimvp krimvp force-pushed the claude/rebase-pr-2267-0amiwo branch from 5106c44 to 77d9ee4 Compare July 4, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants