Skip to content

Factory AI Reference

Factory AI (Droid) integrates with Cupcake through external hooks configured in .factory/settings.json. It uses a similar architecture to Claude Code but with camelCase field names and additional features like permissionMode and updatedInput.

Supported Events

Factory AI supports 9 hook events (same as Claude Code):

Event Description Context Injection Can Block
PreToolUse Before tool execution Yes (+ updatedInput) Yes
PostToolUse After tool execution Yes Yes
UserPromptSubmit User submits prompt Yes Yes
SessionStart Session starts/resumes Yes No
SessionEnd Session ends No No
PreCompact Before memory compaction Yes No
Notification Agent notifications No No
Stop Main agent stopping No Yes
SubagentStop Subagent stopping No Yes

Event Fields

Common Fields

All Factory AI events include (note camelCase naming):

{
  "hookEventName": "PreToolUse",
  "sessionId": "abc123",
  "transcriptPath": "/path/to/transcript.md",
  "cwd": "/working/directory",
  "permissionMode": "default"
}

Permission Mode values: default, plan, auto-medium, auto-full

PreToolUse

{
  "hookEventName": "PreToolUse",
  "sessionId": "abc123",
  "transcriptPath": "/path/to/transcript.md",
  "cwd": "/working/directory",
  "permissionMode": "default",
  "tool_name": "Bash",
  "tool_input": {
    "command": "echo hello"
  }
}

PostToolUse

{
  "hookEventName": "PostToolUse",
  "sessionId": "abc123",
  "transcriptPath": "/path/to/transcript.md",
  "cwd": "/working/directory",
  "permissionMode": "default",
  "tool_name": "Edit",
  "tool_input": {
    "file_path": "/path/to/file.ts",
    "old_string": "foo",
    "new_string": "bar"
  },
  "tool_response": {
    "success": true
  }
}

UserPromptSubmit

{
  "hookEventName": "UserPromptSubmit",
  "sessionId": "abc123",
  "transcriptPath": "/path/to/transcript.md",
  "cwd": "/working/directory",
  "permissionMode": "default",
  "prompt": "Please fix the bug in main.ts"
}

SessionStart

{
  "hookEventName": "SessionStart",
  "sessionId": "abc123",
  "transcriptPath": "/path/to/transcript.md",
  "cwd": "/working/directory",
  "permissionMode": "default",
  "source": "startup"
}

Source values: startup, resume, clear, compact

SessionEnd

{
  "hookEventName": "SessionEnd",
  "sessionId": "abc123",
  "transcriptPath": "/path/to/transcript.md",
  "cwd": "/working/directory",
  "permissionMode": "default",
  "reason": "logout"
}

Reason values: clear, logout, PromptInputExit, other

PreCompact

{
  "hookEventName": "PreCompact",
  "sessionId": "abc123",
  "transcriptPath": "/path/to/transcript.md",
  "cwd": "/working/directory",
  "permissionMode": "default",
  "trigger": "manual",
  "custom_instructions": ""
}

Response Formats

Factory AI uses the same response format as Claude Code, with one key addition: updatedInput for PreToolUse.

PreToolUse Responses

Allow:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow"
  }
}

Deny:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Dangerous command blocked by policy"
  }
}

Ask:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "ask",
    "permissionDecisionReason": "This operation requires confirmation"
  }
}

Allow with Modified Input (Factory-specific):

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "command": "echo 'sanitized command'"
    }
  }
}

The updatedInput field allows policies to modify tool parameters before execution. This is unique to Factory AI.

UserPromptSubmit Responses

Block:

{
  "decision": "block",
  "reason": "Prompt contains prohibited content"
}

Allow with context:

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "Remember: Always run tests before committing"
  }
}

Other Responses

All other responses follow the same format as Claude Code. See the Claude Code Reference for details.

Hook Configuration

The cupcake init --harness factory command configures hooks in .factory/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "cupcake eval --harness factory --policy-dir \"$FACTORY_PROJECT_DIR\"/.cupcake"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "cupcake eval --harness factory --policy-dir \"$FACTORY_PROJECT_DIR\"/.cupcake"
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cupcake eval --harness factory --policy-dir \"$FACTORY_PROJECT_DIR\"/.cupcake"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cupcake eval --harness factory --policy-dir \"$FACTORY_PROJECT_DIR\"/.cupcake"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cupcake eval --harness factory --policy-dir \"$FACTORY_PROJECT_DIR\"/.cupcake"
          }
        ]
      }
    ],
    "SubagentStop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cupcake eval --harness factory --policy-dir \"$FACTORY_PROJECT_DIR\"/.cupcake"
          }
        ]
      }
    ]
  }
}

Writing Policies

Basic Policy Structure

# METADATA
# scope: package
# custom:
#   routing:
#     required_events: ["PreToolUse"]
#     required_tools: ["Bash"]
package cupcake.policies.factory.shell_policy

import rego.v1

deny contains decision if {
    input.hook_event_name == "PreToolUse"
    input.tool_name == "Bash"
    contains(input.tool_input.command, "rm -rf")

    decision := {
        "rule_id": "FACTORY-SAFETY-001",
        "reason": "Destructive command blocked",
        "severity": "CRITICAL"
    }
}

Using Permission Mode

# METADATA
# scope: package
# custom:
#   routing:
#     required_events: ["PreToolUse"]
package cupcake.policies.factory.auto_mode_restrictions

import rego.v1

# Block destructive operations in auto modes
deny contains decision if {
    input.hook_event_name == "PreToolUse"
    input.tool_name == "Bash"

    # Check if running in auto mode
    startswith(input.permissionMode, "auto-")

    # Block dangerous commands in auto mode
    contains(input.tool_input.command, "rm")

    decision := {
        "rule_id": "FACTORY-AUTO-001",
        "reason": "Delete operations are not allowed in auto mode",
        "severity": "HIGH"
    }
}

Policy Portability with Claude Code

Factory AI uses the same event structure as Claude Code, making most policies portable:

Field Factory AI Claude Code
Event name field hookEventName hook_event_name
Session ID sessionId session_id
Transcript path transcriptPath transcript_path
Tool name tool_name tool_name
Tool input tool_input tool_input
Prompt prompt prompt

Cupcake normalizes these differences - policies using input.hook_event_name and input.tool_name work across both harnesses.

Key Differences from Claude Code

Feature Claude Code Factory AI
Field naming snake_case camelCase
Event tag field hook_event_name hookEventName
Permission mode Not available permissionMode field
Input modification Not supported updatedInput in response
Config file .claude/settings.json .factory/settings.json
Project dir var $CLAUDE_PROJECT_DIR $FACTORY_PROJECT_DIR

Resources