@aporthq/aport-agent-guardrails 1.0.17 → 1.0.18

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.
Files changed (128) hide show
  1. package/README.md +80 -56
  2. package/bin/aport-claude-code-hook.sh +12 -11
  3. package/bin/aport-create-passport.sh +40 -14
  4. package/bin/frameworks/claude-code.sh +11 -12
  5. package/bin/frameworks/cursor.sh +17 -13
  6. package/bin/lib/agentsmd.sh +108 -0
  7. package/bin/lib/config.sh +1 -1
  8. package/bin/lib/detect.sh +2 -4
  9. package/bin/lib/framework-setup.sh +37 -0
  10. package/bin/lib/validation.sh +18 -7
  11. package/bin/openclaw +32 -3
  12. package/docs/HOSTED_PASSPORT_SETUP.md +3 -3
  13. package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +64 -0
  14. package/docs/OPENCLAW_COMPATIBILITY.md +1 -1
  15. package/docs/QUICKSTART.md +14 -14
  16. package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +3 -3
  17. package/docs/README.md +1 -1
  18. package/docs/RELEASE.md +3 -3
  19. package/docs/SECURITY_MODEL.md +2 -2
  20. package/docs/frameworks/openclaw.md +1 -1
  21. package/extensions/openclaw-aport/README.md +13 -13
  22. package/extensions/openclaw-aport/index.js +11 -2
  23. package/extensions/openclaw-aport/index.ts +73 -88
  24. package/extensions/openclaw-aport/node_modules/@types/node/README.md +3 -3
  25. package/extensions/openclaw-aport/node_modules/@types/node/assert/strict.d.ts +105 -2
  26. package/extensions/openclaw-aport/node_modules/@types/node/assert.d.ts +232 -159
  27. package/extensions/openclaw-aport/node_modules/@types/node/async_hooks.d.ts +74 -57
  28. package/extensions/openclaw-aport/node_modules/@types/node/buffer.buffer.d.ts +18 -3
  29. package/extensions/openclaw-aport/node_modules/@types/node/buffer.d.ts +64 -31
  30. package/extensions/openclaw-aport/node_modules/@types/node/child_process.d.ts +125 -102
  31. package/extensions/openclaw-aport/node_modules/@types/node/cluster.d.ts +21 -21
  32. package/extensions/openclaw-aport/node_modules/@types/node/console.d.ts +17 -17
  33. package/extensions/openclaw-aport/node_modules/@types/node/crypto.d.ts +575 -534
  34. package/extensions/openclaw-aport/node_modules/@types/node/dgram.d.ts +45 -41
  35. package/extensions/openclaw-aport/node_modules/@types/node/diagnostics_channel.d.ts +63 -36
  36. package/extensions/openclaw-aport/node_modules/@types/node/dns/promises.d.ts +52 -26
  37. package/extensions/openclaw-aport/node_modules/@types/node/dns.d.ts +89 -26
  38. package/extensions/openclaw-aport/node_modules/@types/node/domain.d.ts +5 -5
  39. package/extensions/openclaw-aport/node_modules/@types/node/events.d.ts +229 -116
  40. package/extensions/openclaw-aport/node_modules/@types/node/fs/promises.d.ts +211 -124
  41. package/extensions/openclaw-aport/node_modules/@types/node/fs.d.ts +467 -338
  42. package/extensions/openclaw-aport/node_modules/@types/node/globals.d.ts +5 -3
  43. package/extensions/openclaw-aport/node_modules/@types/node/globals.typedarray.d.ts +17 -0
  44. package/extensions/openclaw-aport/node_modules/@types/node/http.d.ts +307 -137
  45. package/extensions/openclaw-aport/node_modules/@types/node/http2.d.ts +93 -29
  46. package/extensions/openclaw-aport/node_modules/@types/node/https.d.ts +99 -69
  47. package/extensions/openclaw-aport/node_modules/@types/node/index.d.ts +5 -0
  48. package/extensions/openclaw-aport/node_modules/@types/node/inspector.d.ts +253 -0
  49. package/extensions/openclaw-aport/node_modules/@types/node/inspector.generated.d.ts +1763 -486
  50. package/extensions/openclaw-aport/node_modules/@types/node/module.d.ts +426 -38
  51. package/extensions/openclaw-aport/node_modules/@types/node/net.d.ts +197 -64
  52. package/extensions/openclaw-aport/node_modules/@types/node/os.d.ts +52 -26
  53. package/extensions/openclaw-aport/node_modules/@types/node/package.json +8 -8
  54. package/extensions/openclaw-aport/node_modules/@types/node/path.d.ts +13 -4
  55. package/extensions/openclaw-aport/node_modules/@types/node/perf_hooks.d.ts +150 -42
  56. package/extensions/openclaw-aport/node_modules/@types/node/process.d.ts +607 -155
  57. package/extensions/openclaw-aport/node_modules/@types/node/punycode.d.ts +4 -4
  58. package/extensions/openclaw-aport/node_modules/@types/node/querystring.d.ts +24 -12
  59. package/extensions/openclaw-aport/node_modules/@types/node/readline/promises.d.ts +58 -51
  60. package/extensions/openclaw-aport/node_modules/@types/node/readline.d.ts +46 -167
  61. package/extensions/openclaw-aport/node_modules/@types/node/repl.d.ts +25 -27
  62. package/extensions/openclaw-aport/node_modules/@types/node/sea.d.ts +153 -0
  63. package/extensions/openclaw-aport/node_modules/@types/node/sqlite.d.ts +721 -0
  64. package/extensions/openclaw-aport/node_modules/@types/node/stream/consumers.d.ts +2 -2
  65. package/extensions/openclaw-aport/node_modules/@types/node/stream/web.d.ts +98 -3
  66. package/extensions/openclaw-aport/node_modules/@types/node/stream.d.ts +191 -207
  67. package/extensions/openclaw-aport/node_modules/@types/node/string_decoder.d.ts +5 -5
  68. package/extensions/openclaw-aport/node_modules/@types/node/test.d.ts +1176 -221
  69. package/extensions/openclaw-aport/node_modules/@types/node/timers/promises.d.ts +2 -2
  70. package/extensions/openclaw-aport/node_modules/@types/node/timers.d.ts +7 -6
  71. package/extensions/openclaw-aport/node_modules/@types/node/tls.d.ts +228 -113
  72. package/extensions/openclaw-aport/node_modules/@types/node/trace_events.d.ts +41 -15
  73. package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +17 -4
  74. package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +15 -0
  75. package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/index.d.ts +5 -0
  76. package/extensions/openclaw-aport/node_modules/@types/node/tty.d.ts +10 -8
  77. package/extensions/openclaw-aport/node_modules/@types/node/url.d.ts +70 -43
  78. package/extensions/openclaw-aport/node_modules/@types/node/util.d.ts +689 -166
  79. package/extensions/openclaw-aport/node_modules/@types/node/v8.d.ts +215 -48
  80. package/extensions/openclaw-aport/node_modules/@types/node/vm.d.ts +433 -137
  81. package/extensions/openclaw-aport/node_modules/@types/node/wasi.d.ts +46 -25
  82. package/extensions/openclaw-aport/node_modules/@types/node/web-globals/events.d.ts +16 -0
  83. package/extensions/openclaw-aport/node_modules/@types/node/web-globals/fetch.d.ts +17 -0
  84. package/extensions/openclaw-aport/node_modules/@types/node/web-globals/navigator.d.ts +22 -0
  85. package/extensions/openclaw-aport/node_modules/@types/node/web-globals/storage.d.ts +24 -0
  86. package/extensions/openclaw-aport/node_modules/@types/node/worker_threads.d.ts +140 -54
  87. package/extensions/openclaw-aport/node_modules/@types/node/zlib.d.ts +296 -66
  88. package/extensions/openclaw-aport/openclaw.plugin.json +21 -4
  89. package/extensions/openclaw-aport/package.json +5 -5
  90. package/extensions/openclaw-aport/test.js +49 -0
  91. package/package.json +1 -2
  92. package/docs/DEPLOYMENT_READINESS.md +0 -81
  93. package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +0 -1105
  94. package/extensions/openclaw-aport/node_modules/undici-types/README.md +0 -6
  95. package/extensions/openclaw-aport/node_modules/undici-types/agent.d.ts +0 -31
  96. package/extensions/openclaw-aport/node_modules/undici-types/api.d.ts +0 -43
  97. package/extensions/openclaw-aport/node_modules/undici-types/balanced-pool.d.ts +0 -18
  98. package/extensions/openclaw-aport/node_modules/undici-types/cache.d.ts +0 -36
  99. package/extensions/openclaw-aport/node_modules/undici-types/client.d.ts +0 -97
  100. package/extensions/openclaw-aport/node_modules/undici-types/connector.d.ts +0 -34
  101. package/extensions/openclaw-aport/node_modules/undici-types/content-type.d.ts +0 -21
  102. package/extensions/openclaw-aport/node_modules/undici-types/cookies.d.ts +0 -28
  103. package/extensions/openclaw-aport/node_modules/undici-types/diagnostics-channel.d.ts +0 -67
  104. package/extensions/openclaw-aport/node_modules/undici-types/dispatcher.d.ts +0 -241
  105. package/extensions/openclaw-aport/node_modules/undici-types/errors.d.ts +0 -128
  106. package/extensions/openclaw-aport/node_modules/undici-types/fetch.d.ts +0 -209
  107. package/extensions/openclaw-aport/node_modules/undici-types/file.d.ts +0 -39
  108. package/extensions/openclaw-aport/node_modules/undici-types/filereader.d.ts +0 -54
  109. package/extensions/openclaw-aport/node_modules/undici-types/formdata.d.ts +0 -108
  110. package/extensions/openclaw-aport/node_modules/undici-types/global-dispatcher.d.ts +0 -9
  111. package/extensions/openclaw-aport/node_modules/undici-types/global-origin.d.ts +0 -7
  112. package/extensions/openclaw-aport/node_modules/undici-types/handlers.d.ts +0 -9
  113. package/extensions/openclaw-aport/node_modules/undici-types/header.d.ts +0 -4
  114. package/extensions/openclaw-aport/node_modules/undici-types/index.d.ts +0 -63
  115. package/extensions/openclaw-aport/node_modules/undici-types/interceptors.d.ts +0 -5
  116. package/extensions/openclaw-aport/node_modules/undici-types/mock-agent.d.ts +0 -50
  117. package/extensions/openclaw-aport/node_modules/undici-types/mock-client.d.ts +0 -25
  118. package/extensions/openclaw-aport/node_modules/undici-types/mock-errors.d.ts +0 -12
  119. package/extensions/openclaw-aport/node_modules/undici-types/mock-interceptor.d.ts +0 -93
  120. package/extensions/openclaw-aport/node_modules/undici-types/mock-pool.d.ts +0 -25
  121. package/extensions/openclaw-aport/node_modules/undici-types/package.json +0 -55
  122. package/extensions/openclaw-aport/node_modules/undici-types/patch.d.ts +0 -71
  123. package/extensions/openclaw-aport/node_modules/undici-types/pool-stats.d.ts +0 -19
  124. package/extensions/openclaw-aport/node_modules/undici-types/pool.d.ts +0 -28
  125. package/extensions/openclaw-aport/node_modules/undici-types/proxy-agent.d.ts +0 -30
  126. package/extensions/openclaw-aport/node_modules/undici-types/readable.d.ts +0 -61
  127. package/extensions/openclaw-aport/node_modules/undici-types/webidl.d.ts +0 -220
  128. package/extensions/openclaw-aport/node_modules/undici-types/websocket.d.ts +0 -131
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- # 🛡️ APort Agent Guardrails
3
+ # <img src="https://aport.io/logo.svg" alt="APort logo" width="31" /> APort Agent Guardrails
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@aporthq/aport-agent-guardrails.svg)](https://www.npmjs.com/package/@aporthq/aport-agent-guardrails)
6
6
  [![PyPI](https://img.shields.io/pypi/v/aport-agent-guardrails.svg)](https://pypi.org/project/aport-agent-guardrails/)
@@ -8,7 +8,7 @@
8
8
  [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests/)
9
9
  [![Node](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](package.json)
10
10
  [![Python](https://img.shields.io/badge/python-3.10%2B-brightgreen.svg)](python/aport_guardrails/pyproject.toml)
11
- [![OpenClaw](https://img.shields.io/badge/OpenClaw-%3E%3D2026.2.0-blue.svg)](extensions/openclaw-aport/package.json)
11
+ [![OpenClaw](https://img.shields.io/badge/OpenClaw-%3E%3D2026.3.0-blue.svg)](extensions/openclaw-aport/package.json)
12
12
 
13
13
  <p>
14
14
  <a href="https://www.npmjs.com/package/@aporthq/aport-agent-guardrails">npm</a> •
@@ -24,69 +24,75 @@
24
24
 
25
25
  ---
26
26
 
27
- ## The problem: OpenClaw skills can exfiltrate data without you knowing
27
+ Deterministic pre-action authorization for AI agents. Guardrails run before tool execution, so prompt injection cannot bypass policy checks.
28
28
 
29
- [Cisco's AI security team](https://blogs.cisco.com/ai/personal-ai-agents-like-openclaw-are-a-security-nightmare) documented that **OpenClaw skills can perform silent data exfiltration and prompt-injection attacks**—third-party skills run with the same trust as the agent, so a malicious or compromised skill can read files, run commands, or call external APIs without user awareness. That finding has been amplified by [FourWeekMBA](https://fourweekmba.com/openclaws-security-nightmare-the-risk-openai-just-inherited/), [AuthMind](https://www.authmind.com/post/openclaw-malicious-skills-agentic-ai-supply-chain), [Bitsight](https://www.bitsight.com/blog), and others. Additional vulnerabilities (e.g. [CVE-2026-25253](https://www.securityweek.com/vulnerability-allows-hackers-to-hijack-openclaw-ai-assistant/) token exfiltration leading to gateway compromise) keep surfacing.
29
+ ### CTF Evidence
30
30
 
31
- **APort Agent Guardrails is the pre-action authorization layer that blocks this before it executes.** Every tool call is checked against a **passport** (identity + capabilities + limits) in the platform's `before_tool_call` hook. The model cannot skip it; malicious or injected commands are denied before they run. See [SECURITY.md](SECURITY.md) for how we address the Cisco findings, prompt injection, and related attack vectors.
31
+ From the live APort Vault adversarial testbed:
32
32
 
33
- ---
33
+ | Metric | Result |
34
+ |---|---|
35
+ | Total authorization decisions observed | 4,437 |
36
+ | Total attack sessions observed | 1,151 |
37
+ | Level 5 ("Vault") restrictive attempts | 879 |
38
+ | Level 5 ("Vault") successful breaches | 0 |
39
+ | **Level 5 restrictive success rate** | **0%** |
40
+ | Permissive baseline success rate (for comparison) | 74.6% |
34
41
 
35
- ## Why pre-action authorization?
42
+ - [CTF Results](https://vault.aport.io/results)
43
+ - [CTF Replay](https://vault.aport.io/replay)
44
+ - [CTF Leaderboard](https://vault.aport.io/leaderboard/page)
36
45
 
37
- Your agent should **only do what you explicitly allow**. APort runs in the hook—not in the prompt—so enforcement is deterministic and cannot be bypassed by prompt injection. No “trust the prompt”. The guardrail runs in the hook; the model cannot skip it.
38
46
 
47
+ ## Start Here
39
48
 
40
- | | Without APort | With APort (plugin) |
41
- |---|----------------|---------------------|
42
- | **Enforcement** | Best-effort (prompts) | Deterministic (platform hook) |
43
- | **Bypass risk** | High (prompt injection) | None |
44
- | **Command control** | Agent can run anything | Allowlist + blocked patterns |
45
- | **Audit** | Optional / ad hoc | Every decision logged |
49
+ ### Install in 30 seconds
46
50
 
47
- ---
51
+ ```bash
52
+ npx @aporthq/aport-agent-guardrails
53
+ ```
48
54
 
49
- ## Features
55
+ - Choose your framework: `openclaw`, `cursor`, `claude-code`, `langchain`, `crewai`, `deerflow`, `n8n`
56
+ - OpenClaw direct: `npx @aporthq/aport-agent-guardrails openclaw`
57
+ - Hosted passport: `npx @aporthq/aport-agent-guardrails openclaw <agent_id>`
50
58
 
51
- 🛡️ **Pre-action checks** Policy runs *before* the tool executes; deny = tool never runs
52
- 📋 **Passport-driven** — OAP v1.0 passports define capabilities and limits (allowed commands, message caps, etc.)
53
- 🔌 **OpenClaw plugin** — `before_tool_call` hook; 5-minute setup, no code changes
54
- 🌐 **API (default) or local** — APort API (recommended, full OAP) or bash evaluator (offline / no network). Both modes now have identical behavior (exec mapping fixed); messaging runs at assurance L0 by default.
55
- 🔴 **Kill switch** — Suspend the agent so all tool execution is denied: local file (local mode) or **Global Suspend** (hosted passport + API; see below).
56
- ⚡ **Sub-100ms (API)** — Python API verification is typically **~60–65 ms** (mean); local evaluation sub-300ms. [Benchmarks](tests/performance/README.md)
57
- 🔄 **Framework-agnostic** — OpenClaw, IronClaw, PicoClaw, or any runtime that can call a script or API
59
+ ### Why Developers and teams trust APort
58
60
 
59
- **What’s protected (out of the box):**
61
+ - **Deterministic enforcement:** runtime hook, not prompt instructions
62
+ - **Fail-closed defaults:** verification failures block risky actions
63
+ - **Auditable decisions:** each allow/deny is logged with context
64
+ - **Open standard artifacts:** Open Agent Passport ([OAP](https://github.com/aporthq/aport-spec)) v1.0 passport and decision formats
65
+ - **Research-backed outcomes:** in a live adversarial testbed, permissive-policy success was 74.6% vs 0% under restrictive OAP policy (879 top-tier attempts)
66
+ - **Low latency at production scale:** cloud API verification p50 ~53ms at N=1,000
67
+ - **Security docs:** [SECURITY.md](SECURITY.md), [SECURITY_MODEL.md](docs/SECURITY_MODEL.md)
68
+ - **Backed by Peer-reviewed research:** [arXiv preprint (Mar 2026)](https://arxiv.org/html/2603.20953v1)
60
69
 
61
- | Policy | What it guards |
62
- |--------|----------------|
63
- | **system.command.execute.v1** | Shell commands — allowlist, 50+ blocked patterns (`rm -rf`, `sudo`, `nc`, `find -exec rm`, injection); passport `allowed_paths` override for path rules |
64
- | **mcp.tool.execute.v1** | MCP tool calls — server allowlist, rate limits |
65
- | **messaging.message.send.v1** | Message sends — rate caps, capability checks |
66
- | **agent.session.create.v1** / **agent.tool.register.v1** | Sessions and tool registration |
70
+ ### Fast path by persona
67
71
 
68
- ---
72
+ - **I need OpenClaw now:** [docs/QUICKSTART_OPENCLAW_PLUGIN.md](docs/QUICKSTART_OPENCLAW_PLUGIN.md)
73
+ - **I already have agent_id:** [docs/HOSTED_PASSPORT_SETUP.md](docs/HOSTED_PASSPORT_SETUP.md)
74
+ - **I need framework setup docs:** [docs/frameworks](docs/frameworks)
75
+
76
+ ### Brand personality (optional)
69
77
 
70
- ## 🔴 Kill switch (suspend agent) core feature
78
+ Security should feel rigorous, not intimidating. Meet Porter, the APort mascot used across the product experience: [Meet Porter](https://aport.io/brand-mascot-agent/).
71
79
 
72
- The **kill switch suspends the agent**: once active, every tool call is denied until the switch is cleared. No tool runs until you re-enable the agent. **Same standard across all frameworks** (OpenClaw, Cursor, LangChain, CrewAI, n8n): the **passport is the source of truth**; we do **not** create or read any separate file. Local = passport `status`; remote = status in registry.
80
+ <details>
81
+ <summary><strong style="font-size:18pt">Deeper background (threat model, rationale, evidence)</strong></summary>
73
82
 
74
- | Mode | How it works | Scope |
75
- |------|----------------|--------|
76
- | **Local** | The **passport is the source of truth**. Set passport `status` to `suspended` (e.g. edit `passport.json` or `jq '.status = "suspended"' passport.json`). The guardrail checks passport status first and denies with `oap.passport_suspended`. Set back to `active` to resume. No separate file. | Single machine / config dir only. |
77
- | **Global Suspend** | Use a **hosted passport** (agent_id) with API mode. **Log in** to [APort](https://aport.io) and suspend the passport. Per the [Open Agent Passport (OAP) spec](external/aport-spec/oap/oap-spec.md): when a passport is suspended or revoked, validators MUST treat cached decisions as invalid within **≤30 seconds** globally. | **All systems** using that passport deny within &lt;200ms. |
83
+ The security concern is that agent tools and skills can execute sensitive actions (files, commands, external calls). APort addresses this by verifying each tool call against a passport and policy limits before execution. This reduces prompt-injection and “agent decided wrong” risk from runtime behavior to policy configuration.
78
84
 
79
- **Remote passports and Global Suspend (pro):** When the same passport is used **across multiple systems** (e.g. team agents, many machines), a **hosted passport** plus API mode lets you **log in once** and suspend that passport in the registry. Every agent using that passport then stops authorizing tool calls within 200ms, per OAP suspend/revoke semantics. That's **Global Suspend** — a major advantage when one passport is shared. See [OAP spec: Suspend/Revoke Semantics](external/aport-spec/oap/oap-spec.md) and [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md).
85
+ </details>
80
86
 
81
87
  ---
82
88
 
83
89
  ## 🔌 Supported frameworks
84
90
 
85
- **APort Agent Guardrail** adapters are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI):** run the same CLI for the wizard and config, then `pip install aport-agent-guardrails-langchain` or `aport-agent-guardrails-crewai` and `aport-langchain setup` / `aport-crewai setup`. **n8n:** coming soon.
91
+ **APort Agent Guardrail** adapters are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI/DeerFlow):** run the same CLI for the wizard and config, then install the framework adapter/provider package shown in the framework doc.
86
92
 
87
- **Two ways to use APort:** (1) **Guardrails (CLI/setup)** — run the installer to create your passport and config; (2) **Core (library)** — use the evaluator or framework callback in your app so each tool call is verified. Each framework doc ([LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [Cursor](docs/frameworks/cursor.md), [OpenClaw](docs/frameworks/openclaw.md)) describes both and how to use them for that framework (Python and Node where applicable).
93
+ **Two ways to use APort:** (1) **Guardrails (CLI/setup)** — run the installer to create your passport and config; (2) **Core (library)** — use the evaluator or framework callback in your app so each tool call is verified. Framework docs: [OpenClaw](docs/frameworks/openclaw.md), [Cursor](docs/frameworks/cursor.md), [Claude Code](docs/frameworks/claude-code.md), [LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [DeerFlow](docs/frameworks/deerflow.md), [n8n](docs/frameworks/n8n.md).
88
94
 
89
- **Production-ready today:** OpenClaw (plugin + full installer), Cursor (hooks), **Claude Code** (PreToolUse hook), **Python** LangChain/CrewAI (`pip install aport-agent-guardrails-langchain` / `aport-agent-guardrails-crewai`), and **Node** (CLI + `@aporthq/aport-agent-guardrails-core`, `-langchain`, `-crewai`, `-cursor`, `-claude-code` published via CI). See [Deployment readiness](docs/DEPLOYMENT_READINESS.md).
95
+ **CLI-supported frameworks:** `openclaw`, `langchain`, `crewai`, `cursor`, `claude-code`, `deerflow`, `n8n`. OpenClaw/Cursor/Claude Code include runtime-specific integration scripts; DeerFlow/LangChain/CrewAI use framework docs plus generic setup output from the CLI. See [Deployment readiness](docs/DEPLOYMENT_READINESS.md).
90
96
 
91
97
  | Framework | Doc | Integration | Install |
92
98
  |-----------|-----|--------------|--------|
@@ -95,13 +101,14 @@ The **kill switch suspends the agent**: once active, every tool call is denied u
95
101
  | **Claude Code** | [docs/frameworks/claude-code.md](docs/frameworks/claude-code.md) | PreToolUse hook → writes `~/.claude/settings.json` (Claude Code format; not Cursor). | `npx @aporthq/aport-agent-guardrails claude-code` |
96
102
  | **LangChain / LangGraph** | [docs/frameworks/langchain.md](docs/frameworks/langchain.md) | **Python:** `APortCallback` (`on_tool_start`) | `npx @aporthq/aport-agent-guardrails langchain` then `pip install aport-agent-guardrails-langchain` + `aport-langchain setup` |
97
103
  | **CrewAI** | [docs/frameworks/crewai.md](docs/frameworks/crewai.md) | **Python:** `@before_tool_call` hook, `register_aport_guardrail` | `npx @aporthq/aport-agent-guardrails crewai` then `pip install aport-agent-guardrails-crewai` + `aport-crewai setup` |
104
+ | **DeerFlow** | [docs/frameworks/deerflow.md](docs/frameworks/deerflow.md) | **Python:** generic OAP provider wiring in DeerFlow config | `npx @aporthq/aport-agent-guardrails deerflow` then follow printed `uv`/config steps |
98
105
  | **n8n** | [docs/frameworks/n8n.md](docs/frameworks/n8n.md) | *Coming soon* — custom node and runtime in progress | — |
99
106
 
100
107
  Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when prompted). OpenClaw can also use the full installer flow. **For LangChain and CrewAI, the Node CLI only runs the wizard and writes config; you must then run the printed `pip install` and setup commands to install the runtime adapter.** **Python** adapters on PyPI; **Node** adapters on npm (same version as the CLI).
101
108
 
102
109
  **Passport path:** Each framework has its own **default** passport path (where that framework stores data): e.g. Cursor → `~/.cursor/aport/passport.json`, OpenClaw → `~/.openclaw/aport/passport.json`, LangChain → `~/.aport/langchain/aport/passport.json`. The passport wizard’s **first question** is “Passport file path [default]:” — press Enter for the framework default or type a different path. In non-interactive mode (e.g. CI) use **`--output /path/to/passport.json`** to choose the path. Roadmap: [docs/FRAMEWORK_ROADMAP.md](docs/FRAMEWORK_ROADMAP.md).
103
110
 
104
- **Using SDKs or middleware directly:** If you prefer to integrate with the APort API from your own app (no CLI/framework installer), use the official SDKs and middleware: **Node** — [@aporthq/sdk-node](https://www.npmjs.com/package/@aporthq/sdk-node), [@aporthq/middleware-express](https://www.npmjs.com/package/@aporthq/middleware-express); **Python** — [aporthq-sdk-python](https://pypi.org/project/aporthq-sdk-python/), [aporthq-middleware-fastapi](https://pypi.org/project/aporthq-middleware-fastapi/). See [User Stories (Story F)](docs/launch/USER_STORIES.md#story-f) for details.
111
+ **Using SDKs or middleware directly:** If you prefer to integrate with the APort API from your own app (no CLI/framework installer), use the official SDKs and middleware: **Node** — [@aporthq/sdk-node](https://www.npmjs.com/package/@aporthq/sdk-node), [@aporthq/middleware-express](https://www.npmjs.com/package/@aporthq/middleware-express); **Python** — [aporthq-sdk-python](https://pypi.org/project/aporthq-sdk-python/), [aporthq-middleware-fastapi](https://pypi.org/project/aporthq-middleware-fastapi/).
105
112
 
106
113
  ---
107
114
 
@@ -114,7 +121,7 @@ Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when pr
114
121
  **Node (Cursor, OpenClaw, or to create config for any framework):**
115
122
  ```bash
116
123
  npx @aporthq/aport-agent-guardrails
117
- # or: npx @aporthq/aport-agent-guardrails cursor | openclaw | langchain | crewai
124
+ # or: npx @aporthq/aport-agent-guardrails openclaw | cursor | claude-code | langchain | crewai | deerflow | n8n
118
125
  ```
119
126
 
120
127
  **Python (LangChain or CrewAI only):** Run the wizard via the Node command above, then install the Python adapter and run the framework setup (see the printed next steps). Or use the Python CLI to see the exact commands:
@@ -151,7 +158,7 @@ aport-guardrail system.command.execute '{"command":"rm -rf /"}' # DENY (blocked
151
158
 
152
159
  Your framework doc (Cursor, OpenClaw, LangChain, CrewAI) describes where the config dir is and any framework-specific status commands.
153
160
 
154
- 📖 **Per-framework:** [OpenClaw](docs/frameworks/openclaw.md) · [Cursor](docs/frameworks/cursor.md) · [LangChain](docs/frameworks/langchain.md) · [CrewAI](docs/frameworks/crewai.md)
161
+ 📖 **Per-framework:** [OpenClaw](docs/frameworks/openclaw.md) · [Cursor](docs/frameworks/cursor.md) · [Claude Code](docs/frameworks/claude-code.md) · [LangChain](docs/frameworks/langchain.md) · [CrewAI](docs/frameworks/crewai.md) · [DeerFlow](docs/frameworks/deerflow.md) · [n8n](docs/frameworks/n8n.md)
155
162
  🌐 **Hosted passport:** [Use agent_id from aport.io](docs/HOSTED_PASSPORT_SETUP.md)
156
163
 
157
164
  ---
@@ -188,20 +195,23 @@ Deep dive (what each supports, comparison table): [Verification methods](docs/VE
188
195
 
189
196
  ## ⚡ Performance
190
197
 
191
- Guardrail verification latency (real API + local). Python is typically fastest for API mode. Below: **Python only** (n=30, warmup=10, real APort API).
198
+ Guardrail verification latency from the latest preprint benchmark set (**N=1,000**).
192
199
 
193
- | Method | Identity | Policy | Mean (ms) | p50 | p95 | p99 | N |
194
- |--------|----------|--------|-----------|-----|-----|-----|---|
195
- | API | agent_id (cloud) | pack in path | 63.42 | 63.16 | 70.31 | 70.65 | 30 |
196
- | API | agent_id (cloud) | policy in body | 62.62 | 62.37 | 69.58 | 72.22 | 30 |
197
- | API | passport in body | pack in path | 63.03 | 63.12 | 67.48 | 71.45 | 30 |
198
- | API | passport in body | policy in body | 61.60 | 62.11 | 66.90 | 70.39 | 30 |
199
- | Local | passport file | pack in path | 120.27 | 117.85 | 131.62 | 145.38 | 30² |
200
+ | Mode | p50 | p95 | p99 | N |
201
+ |------|-----|-----|-----|---|
202
+ | Cloud API (agent_id, pack in path) | 53ms | 63ms | 76ms | 1,000 |
203
+ | Cloud API (agent_id, policy in body) | 53ms | 62ms | 77ms | 1,000 |
204
+ | Cloud API (passport in body, pack in path) | 54ms | 63ms | 74ms | 1,000 |
205
+ | Cloud API (passport in body, policy in body) | 53ms | 63ms | 71ms | 1,000 |
206
+ | Local policy evaluation | 174ms | 243ms | 358ms | 1,000 |
200
207
 
201
- ² Policy in body not yet for local evaluation.
208
+ Source: [Before the Tool Call: Deterministic Pre-Action Authorization for Autonomous AI Agents](https://arxiv.org/html/2603.20953v1)
202
209
 
203
210
  ---
204
211
 
212
+ <details>
213
+ <summary><strong>📐 How It Works (expand)</strong></summary>
214
+
205
215
  ## 📐 How It Works
206
216
 
207
217
  <div align="center">
@@ -284,6 +294,11 @@ User → "Delete all log files"
284
294
 
285
295
  ---
286
296
 
297
+ </details>
298
+
299
+ <details>
300
+ <summary><strong>🏛️ Security model (three layers) (expand)</strong></summary>
301
+
287
302
  ## 🏛️ Security model (three layers)
288
303
 
289
304
  APort enforces **identity → authorization → audit** before any tool runs. This repo implements the **plugin (Option 2)** integration: OpenClaw calls the APort extension in `before_tool_call`; the extension uses either local script or API to evaluate policy.
@@ -346,6 +361,8 @@ graph TB
346
361
 
347
362
  ---
348
363
 
364
+ </details>
365
+
349
366
  ## 🌐 When to use API vs local
350
367
 
351
368
  | Use **local** when | Use **API** (default) when |
@@ -375,7 +392,7 @@ See [Verification methods](docs/VERIFICATION_METHODS.md) for a detailed comparis
375
392
 
376
393
  | Command | Purpose |
377
394
  |--------|---------|
378
- | `agent-guardrails` | Main entry — prompt for framework or pass one: `agent-guardrails cursor \| openclaw \| langchain \| crewai`. Args after the framework are passed through (e.g. `agent-guardrails openclaw <agent_id>`). |
395
+ | `agent-guardrails` | Main entry — prompt for framework or pass one: `agent-guardrails openclaw \| cursor \| claude-code \| langchain \| crewai \| deerflow \| n8n`. Args after the framework are passed through (e.g. `agent-guardrails openclaw <agent_id>`). |
379
396
  | `aport` | OpenClaw one-command setup (passport + plugin + wrappers). Optional: `aport <agent_id>` for hosted passport. |
380
397
  | `aport-guardrail` | Run guardrail check from the CLI (e.g. `aport-guardrail system.command.execute '{"command":"ls"}'`). Uses passport from your framework config dir. |
381
398
 
@@ -399,7 +416,7 @@ Use the framework-specific doc for where config and passport live and for any ex
399
416
  | Doc | Description |
400
417
  |-----|-------------|
401
418
  | [QuickStart: OpenClaw Plugin](docs/QUICKSTART_OPENCLAW_PLUGIN.md) | 5-minute OpenClaw setup |
402
- | [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md) | Use passport from aport.io — `npx ... <agent_id>` or choose hosted in wizard |
419
+ | [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md) | Use passport from aport.io — `npx ... openclaw <agent_id>` or choose hosted in wizard |
403
420
  | [Verification methods (local vs API)](docs/VERIFICATION_METHODS.md) | Deep dive: bash vs API evaluator |
404
421
  | [Quick Start Guide](docs/QUICKSTART.md) | Passport wizard, copy-paste option |
405
422
  | [OpenClaw Local Integration](docs/OPENCLAW_LOCAL_INTEGRATION.md) | API, Python example |
@@ -409,13 +426,18 @@ Use the framework-specific doc for where config and passport live and for any ex
409
426
  | **Frameworks** | Per-framework setup and how guardrails run |
410
427
  | → [OpenClaw](docs/frameworks/openclaw.md) | `before_tool_call` plugin |
411
428
  | → [Cursor](docs/frameworks/cursor.md) | beforeShellExecution / preToolUse hooks, `~/.cursor/hooks.json` |
429
+ | → [Claude Code](docs/frameworks/claude-code.md) | PreToolUse hook, `~/.claude/settings.json` |
412
430
  | → [LangChain / LangGraph](docs/frameworks/langchain.md) | `APortCallback` handler |
413
431
  | → [CrewAI](docs/frameworks/crewai.md) | `@before_tool_call` hook, `register_aport_guardrail` |
432
+ | → [DeerFlow](docs/frameworks/deerflow.md) | Generic provider wiring via DeerFlow `config.yaml` |
414
433
  | → [n8n](docs/frameworks/n8n.md) | Custom node, branch on allow/deny |
415
- | [Framework support plan](docs/launch/FRAMEWORK_SUPPORT_PLAN.md) | Strategy, rankings, roadmap |
434
+ | [Framework roadmap](docs/FRAMEWORK_ROADMAP.md) | Support status and roadmap |
416
435
 
417
436
  ---
418
437
 
438
+ <details>
439
+ <summary><strong>🏗️ Architecture (expand)</strong></summary>
440
+
419
441
  ## 🏗️ Architecture
420
442
 
421
443
  <div align="center">
@@ -471,6 +493,8 @@ Defense in depth: policy *before* execution, runtime safety *during* execution.
471
493
 
472
494
  ---
473
495
 
496
+ </details>
497
+
474
498
  ## 🤝 Contributing
475
499
 
476
500
  Contributions welcome: policy packs, framework adapters, docs. See [CONTRIBUTING.md](CONTRIBUTING.md).
@@ -39,6 +39,7 @@ set -e
39
39
  if [ "$JQ_EXIT" -ne 0 ] || [ -z "$TOOL_NAME" ]; then
40
40
  TOOL_NAME="unknown"
41
41
  fi
42
+ TOOL_NAME_NORM="$(printf '%s' "$TOOL_NAME" | tr -d '[:space:]' | sed 's/^functions\.//' | tr '[:upper:]' '[:lower:]')"
42
43
  set +e
43
44
  TOOL_INPUT="$(echo "$INPUT" | jq -c '.tool_input // {}' 2> /dev/null)"
44
45
  JQ_EXIT=$?
@@ -69,44 +70,44 @@ deny() {
69
70
  GUARDRAIL_TOOL=""
70
71
  CONTEXT_JSON="{}"
71
72
 
72
- case "$TOOL_NAME" in
73
- Bash)
73
+ case "$TOOL_NAME_NORM" in
74
+ bash | shell)
74
75
  GUARDRAIL_TOOL="bash"
75
76
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{command: (.command // "")}')"
76
77
  ;;
77
- Read | Glob | LS | Grep | TodoRead | ToolSearch | AskUserQuestion)
78
+ read | glob | ls | grep | todoread | toolsearch | askuserquestion | readfile | semanticsearch)
78
79
  # Read-family + user-interaction tools: allow without calling evaluator
79
80
  exit 0
80
81
  ;;
81
- TaskGet | TaskList | TaskOutput | CronList)
82
+ taskget | tasklist | taskoutput | cronlist)
82
83
  # Read-only task/cron queries: allow without evaluator
83
84
  exit 0
84
85
  ;;
85
- EnterPlanMode | ExitPlanMode)
86
+ enterplanmode | exitplanmode)
86
87
  # Internal state transitions: allow without evaluator
87
88
  exit 0
88
89
  ;;
89
- Write | Edit | MultiEdit | NotebookEdit | TodoWrite)
90
+ write | edit | multiedit | notebookedit | todowrite | delete | strreplace | editnotebook)
90
91
  GUARDRAIL_TOOL="write"
91
92
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{file_path: (.file_path // .path // "")}')"
92
93
  ;;
93
- WebSearch | WebFetch)
94
+ websearch | webfetch)
94
95
  GUARDRAIL_TOOL="webfetch"
95
96
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // .query // "")}')"
96
97
  ;;
97
- Browser)
98
+ browser)
98
99
  GUARDRAIL_TOOL="browser"
99
100
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // "")}')"
100
101
  ;;
101
- Task | TaskCreate | TaskUpdate | TaskStop | Agent | Skill | EnterWorktree)
102
+ task | taskcreate | taskupdate | taskstop | agent | skill | enterworktree | subagent | subagentstart)
102
103
  GUARDRAIL_TOOL="session.create"
103
104
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .prompt // "")}')"
104
105
  ;;
105
- CronCreate | CronDelete)
106
+ croncreate | crondelete)
106
107
  GUARDRAIL_TOOL="cron"
107
108
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .schedule // "")}')"
108
109
  ;;
109
- mcp__*)
110
+ mcp__* | mcp:* | callmcptool)
110
111
  GUARDRAIL_TOOL="mcp.tool"
111
112
  CONTEXT_JSON="$TOOL_INPUT"
112
113
  ;;
@@ -104,15 +104,15 @@ get_identity_description() {
104
104
  DEFAULT_EMAIL=$(get_default_email) || true
105
105
  DEFAULT_EMAIL=${DEFAULT_EMAIL:-"user@example.com"}
106
106
  DEFAULT_OWNER_TYPE="user"
107
+ # Agent name/description: try OpenClaw IDENTITY.md first; overridden per framework below
107
108
  DEFAULT_AGENT_NAME=$(get_identity_name) || true
108
- DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"OpenClaw Agent"}
109
109
  DEFAULT_AGENT_DESC=$(get_identity_description) || true
110
- DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"Local OpenClaw AI agent with APort guardrails"}
111
110
 
112
- # Framework-aware defaults: each framework needs different capabilities to function
111
+ # Framework-aware defaults: capabilities and agent identity vary by framework
113
112
  case "${APORT_FRAMEWORK:-}" in
114
113
  claude-code)
115
- # Claude Code: file ops, web, sub-agents, MCP tools are core functionality
114
+ DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"Claude Code Agent"}
115
+ DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"Claude Code AI agent with APort guardrails"}
116
116
  DEFAULT_FILE_READ=y
117
117
  DEFAULT_FILE_WRITE=y
118
118
  DEFAULT_WEB_FETCH=y
@@ -121,7 +121,8 @@ case "${APORT_FRAMEWORK:-}" in
121
121
  DEFAULT_MCP_TOOL=y
122
122
  ;;
123
123
  cursor)
124
- # Cursor: IDE with shell, file ops, web search, agent mode
124
+ DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"Cursor Agent"}
125
+ DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"Cursor IDE AI agent with APort guardrails"}
125
126
  DEFAULT_FILE_READ=y
126
127
  DEFAULT_FILE_WRITE=y
127
128
  DEFAULT_WEB_FETCH=y
@@ -130,7 +131,8 @@ case "${APORT_FRAMEWORK:-}" in
130
131
  DEFAULT_MCP_TOOL=n
131
132
  ;;
132
133
  crewai)
133
- # CrewAI: multi-agent framework — agents spawn sub-agents, use tools
134
+ DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"CrewAI Agent"}
135
+ DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"CrewAI multi-agent system with APort guardrails"}
134
136
  DEFAULT_FILE_READ=y
135
137
  DEFAULT_FILE_WRITE=y
136
138
  DEFAULT_WEB_FETCH=y
@@ -139,7 +141,8 @@ case "${APORT_FRAMEWORK:-}" in
139
141
  DEFAULT_MCP_TOOL=y
140
142
  ;;
141
143
  langchain)
142
- # LangChain/LangGraph: tool calling, web, files
144
+ DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"LangChain Agent"}
145
+ DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"LangChain/LangGraph AI agent with APort guardrails"}
143
146
  DEFAULT_FILE_READ=y
144
147
  DEFAULT_FILE_WRITE=y
145
148
  DEFAULT_WEB_FETCH=y
@@ -148,7 +151,8 @@ case "${APORT_FRAMEWORK:-}" in
148
151
  DEFAULT_MCP_TOOL=n
149
152
  ;;
150
153
  n8n)
151
- # n8n: workflow automation — HTTP, file, database, messaging
154
+ DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"n8n Agent"}
155
+ DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"n8n workflow agent with APort guardrails"}
152
156
  DEFAULT_FILE_READ=y
153
157
  DEFAULT_FILE_WRITE=y
154
158
  DEFAULT_WEB_FETCH=y
@@ -156,8 +160,29 @@ case "${APORT_FRAMEWORK:-}" in
156
160
  DEFAULT_AGENT_SESSION=n
157
161
  DEFAULT_MCP_TOOL=n
158
162
  ;;
163
+ deerflow)
164
+ DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"DeerFlow Agent"}
165
+ DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"DeerFlow LangGraph agent with APort guardrails"}
166
+ DEFAULT_FILE_READ=y
167
+ DEFAULT_FILE_WRITE=y
168
+ DEFAULT_WEB_FETCH=y
169
+ DEFAULT_WEB_BROWSER=n
170
+ DEFAULT_AGENT_SESSION=y
171
+ DEFAULT_MCP_TOOL=y
172
+ ;;
173
+ openclaw)
174
+ DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"OpenClaw Agent"}
175
+ DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"Local OpenClaw AI agent with APort guardrails"}
176
+ DEFAULT_FILE_READ=y
177
+ DEFAULT_FILE_WRITE=y
178
+ DEFAULT_WEB_FETCH=y
179
+ DEFAULT_WEB_BROWSER=n
180
+ DEFAULT_AGENT_SESSION=y
181
+ DEFAULT_MCP_TOOL=n
182
+ ;;
159
183
  *)
160
- # Generic / unknown framework: conservative defaults
184
+ DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"APort Agent"}
185
+ DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"AI agent with APort guardrails"}
161
186
  DEFAULT_FILE_READ=n
162
187
  DEFAULT_FILE_WRITE=n
163
188
  DEFAULT_WEB_FETCH=n
@@ -255,11 +280,12 @@ else
255
280
  # Choose capabilities
256
281
  echo " 🔐 Capabilities"
257
282
  echo " ───────────────"
258
- if [ "${APORT_FRAMEWORK:-}" = "claude-code" ]; then
259
- echo " Choose what your agent can do (y/n). Claude Code defaults: most capabilities = yes."
260
- else
261
- echo " Choose what your agent can do (y/n). Defaults: PRs, exec, and messaging = yes (matches README/docs); others = no."
262
- fi
283
+ case "${APORT_FRAMEWORK:-}" in
284
+ claude-code) echo " Choose what your agent can do (y/n). Claude Code defaults: most capabilities = yes." ;;
285
+ cursor) echo " Choose what your agent can do (y/n). Cursor defaults: file ops, exec, web, sub-agents = yes." ;;
286
+ openclaw) echo " Choose what your agent can do (y/n). OpenClaw defaults: PRs, exec, messaging, file ops = yes." ;;
287
+ *) echo " Choose what your agent can do (y/n). Defaults: PRs, exec, and messaging = yes; others vary by framework." ;;
288
+ esac
263
289
  echo ""
264
290
  read -p " • Create and merge pull requests? [Y/n]: " pr_cap
265
291
  pr_cap=${pr_cap:-y}
@@ -10,26 +10,25 @@ source "$LIB/common.sh"
10
10
  source "$LIB/passport.sh"
11
11
  # shellcheck source=../lib/config.sh
12
12
  source "$LIB/config.sh"
13
+ # shellcheck source=../lib/framework-setup.sh
14
+ source "$LIB/framework-setup.sh"
13
15
 
14
16
  run_setup() {
15
17
  log_info "Setting up APort guardrails for Claude Code..."
16
- config_dir="$(get_config_dir claude-code)"
17
- config_dir="${config_dir/#\~/$HOME}"
18
- mkdir -p "$config_dir/aport"
19
- chmod 700 "$config_dir/aport"
18
+ config_dir="$(ensure_aport_dir_secure claude-code)"
20
19
 
21
20
  export APORT_FRAMEWORK=claude-code
22
- run_passport_wizard "$@"
21
+
22
+ # Check AGENTS.md for enforcement config — skip wizard if already configured
23
+ # shellcheck source=../lib/agentsmd.sh
24
+ source "$LIB/agentsmd.sh"
25
+ setup_from_agentsmd_or_wizard "$@"
23
26
 
24
27
  # Harden permissions on passport (contains policy/capabilities)
25
28
  [ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
26
29
 
27
30
  # Resolve absolute path to hook script (works from repo or npx package)
28
- HOOK_SCRIPT="${APORT_CLAUDE_CODE_HOOK_SCRIPT:-}"
29
- if [ -z "$HOOK_SCRIPT" ]; then
30
- ROOT_FOR_HOOK="$(cd "$LIB/../.." && pwd)"
31
- HOOK_SCRIPT="$ROOT_FOR_HOOK/bin/aport-claude-code-hook.sh"
32
- fi
31
+ HOOK_SCRIPT="$(resolve_hook_script_path "${APORT_CLAUDE_CODE_HOOK_SCRIPT:-}" "aport-claude-code-hook.sh" "$LIB")"
33
32
  if [ ! -f "$HOOK_SCRIPT" ]; then
34
33
  log_warn "Hook script not found at $HOOK_SCRIPT; settings.json will reference it (create the file for hooks to work)."
35
34
  else
@@ -67,13 +66,13 @@ _write_claude_settings() {
67
66
 
68
67
  if [ -f "$file" ] && command -v jq &> /dev/null; then
69
68
  if jq -e '.hooks' "$file" &> /dev/null; then
70
- # Merge: add APort to PreToolUse array, dedup by command
69
+ # Merge: replace stale APort hook entries (old npx paths), preserve non-APort hooks
71
70
  local tmpfile
72
71
  tmpfile="$(mktemp "${file}.XXXXXX")"
73
72
  jq -c --arg cmd "$cmd" '
74
73
  (.hooks.PreToolUse // []) as $p |
75
74
  .hooks.PreToolUse = ($p | map(select(
76
- (.hooks[0].command != $cmd)
75
+ ((.hooks[0].command != $cmd) and (((.hooks[0].command // "") | test("aport-(cursor-hook|claude-code-hook)\\.sh$")) | not))
77
76
  )) | . + [{"matcher":"*","hooks":[{"type":"command","command":$cmd}]}])
78
77
  ' "$file" > "$tmpfile" && mv "$tmpfile" "$file"
79
78
  return
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
- # Cursor (and optional Copilot/Claude Code) framework installer/setup.
2
+ # Cursor framework installer/setup.
3
3
  # Runs passport wizard and writes ~/.cursor/hooks.json pointing at the APort hook script.
4
- # Same hook script works for Cursor, VS Code + Copilot, and Claude Code.
4
+ # Same hook script works for Cursor and VS Code/Copilot-style hook payloads.
5
5
 
6
6
  LIB="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")/../lib" && pwd)"
7
7
  # shellcheck source=../lib/common.sh
@@ -10,26 +10,26 @@ source "$LIB/common.sh"
10
10
  source "$LIB/passport.sh"
11
11
  # shellcheck source=../lib/config.sh
12
12
  source "$LIB/config.sh"
13
+ # shellcheck source=../lib/framework-setup.sh
14
+ source "$LIB/framework-setup.sh"
13
15
 
14
16
  run_setup() {
15
17
  log_info "Setting up APort guardrails for Cursor..."
16
18
  # Passport and data live under Cursor's config dir (~/.cursor/aport/ by default).
17
- config_dir="$(get_config_dir cursor)"
18
- mkdir -p "$config_dir/aport"
19
- chmod 700 "$config_dir/aport"
19
+ config_dir="$(ensure_aport_dir_secure cursor)"
20
20
 
21
21
  export APORT_FRAMEWORK=cursor
22
- run_passport_wizard "$@"
22
+
23
+ # Check AGENTS.md for enforcement config — skip wizard if already configured
24
+ # shellcheck source=../lib/agentsmd.sh
25
+ source "$LIB/agentsmd.sh"
26
+ setup_from_agentsmd_or_wizard "$@"
23
27
 
24
28
  # Harden permissions on passport (contains policy/capabilities)
25
29
  [ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
26
30
 
27
31
  # Resolve absolute path to hook script (works from repo or npx package)
28
- HOOK_SCRIPT="${APORT_CURSOR_HOOK_SCRIPT:-}"
29
- if [ -z "$HOOK_SCRIPT" ]; then
30
- ROOT_FOR_HOOK="$(cd "$LIB/../.." && pwd)"
31
- HOOK_SCRIPT="$ROOT_FOR_HOOK/bin/aport-cursor-hook.sh"
32
- fi
32
+ HOOK_SCRIPT="$(resolve_hook_script_path "${APORT_CURSOR_HOOK_SCRIPT:-}" "aport-cursor-hook.sh" "$LIB")"
33
33
  if [ ! -f "$HOOK_SCRIPT" ]; then
34
34
  log_warn "Hook script not found at $HOOK_SCRIPT; hooks.json will reference it (create the file for hooks to work)."
35
35
  else
@@ -45,9 +45,13 @@ run_setup() {
45
45
  if [ -f "$CURSOR_HOOKS_FILE" ] && command -v jq &> /dev/null; then
46
46
  EXISTING=$(cat "$CURSOR_HOOKS_FILE")
47
47
  if echo "$EXISTING" | jq -e '.hooks' &> /dev/null; then
48
- # Add APort hook to all supported lifecycle events (avoid duplicate)
48
+ # Add APort hook to all supported lifecycle events.
49
+ # Replace stale APort entries (old npx paths), preserve non-APort hooks.
49
50
  NEW_HOOKS=$(echo "$EXISTING" | jq -c --arg cmd "$HOOK_SCRIPT" '
50
- def upsert_hook: map(select(.command != $cmd)) | . + [{ "command": $cmd }];
51
+ def is_aport_cmd:
52
+ (.command? // "") | test("aport-(cursor-hook|claude-code-hook)\\.sh$");
53
+ def upsert_hook:
54
+ map(select((.command != $cmd) and (is_aport_cmd | not))) | . + [{ "command": $cmd }];
51
55
  .hooks.beforeShellExecution = ((.hooks.beforeShellExecution // []) | upsert_hook) |
52
56
  .hooks.preToolUse = ((.hooks.preToolUse // []) | upsert_hook) |
53
57
  .hooks.beforeMCPExecution = ((.hooks.beforeMCPExecution // []) | upsert_hook) |