Commit Graph

4 Commits

Author SHA1 Message Date
Charles Wiltgen 8a7086351a feat(cursor): port subagent skill awareness to subagentStart
The third and last capability the ledger recorded as downgraded, replaced by a
static preamble in each agent file. Cursor's subagentStart response accepts
additional_context and its query carries subagent_type, which is the only field
the canonical hook reads, so the port is a field rename.

This is additive, not a replacement. The generated preamble carries an agent's
own declared skills; the hook carries the general skill-awareness text gated by
agent type. Both now ship.

Verified as far as a free Cursor plan allows: the hook registers, fires, and
delivers a payload carrying subagent_type, task, and subagent_model — confirmed
against a live Cursor 3.17.8 delegation, which fires subagentStart three times
before refusing to start the subagent with "Named models unavailable. Free
plans can only use Auto." What a free plan cannot show is whether the returned
context reaches a subagent that never starts. The docs and the submission
matrix say so rather than implying full verification. If delivery does not
work, the preamble still carries the declared skills, so the failure mode is
the status quo rather than a regression.

The context guard needed widening: skill awareness is legitimately
multi-paragraph, where the router and crash hints are single-line. Newline and
tab are now permitted, every other control character is still rejected, and the
bound is separate at 8 KiB.
2026-08-23 13:18:08 -07:00
Charles Wiltgen 3bf999dfc3 feat(cursor): port crash-report read routing to preToolUse
The second capability recorded as "omitted" without rationale. Cursor supports
it: PreToolUseRequestQuery carries tool_name and tool_input, and
PreToolUseRequestResponse accepts additional_context, so the canonical
PreToolUse(Read) crash routing maps directly onto preToolUse with a Read
matcher. Verified against a live Cursor 3.17.8 session — asked to read a
Sample.ips, the model reported being advised to use the axiom_xcsym_crash MCP
tool, and the read was not gated.

The canonical hints name the bare `xcsym` binary, which a Cursor install cannot
run — the plugin routes xcsym through MCP. Shipping them verbatim would have
told the model to run something that is not there, which is the likeliest
reason this looked unportable. The adapter maps the four invocation forms onto
the MCP tool and fails closed if any backticked `xcsym` survives: no hint is
strictly better than a wrong one.

No `permission` field is emitted, so the plugin never gates a read, consistent
with the documented position that no Axiom hook is a permission boundary. The
hint interpolates the file path, so the adapter bounds its length and rejects
control characters before it reaches the model's turn.

Contract disposition moves from "omitted" to "preToolUse.additional_context".
2026-08-23 13:09:08 -07:00
Charles Wiltgen a0cea09a5c feat(cursor): port per-prompt router injection to beforeSubmitPrompt
The capability ledger recorded UserPromptSubmit as "omitted" with no stated
rationale, which dropped Axiom's core routing-enforcement mechanism and left
skill activation in Cursor resting entirely on description-based discovery.
Cursor supports the capability.

Its beforeSubmitPrompt event carries the prompt text and its response accepts
additional_context — the same mechanism user-prompt-submit.py already emits
through hookSpecificOutput.additionalContext. Verified against a live Cursor
3.17.8 session, not inferred: an injected marker token came back at the head of
the model's reply, and with this adapter mode in place the prompt "My SwiftUI
list scrolls badly on device" is routed to axiom-swiftui, which the model then
opens before answering.

The adapter gains a prompt-submit mode that translates the payload, runs the
canonical router as a child, and returns its guidance as additional_context.
The router child now ships with the distribution.

Two details worth noting:

_workspace_root centralises the fact that Cursor omits cwd on several events
but always sends workspace_roots. Children run their own project gate against
the directory they start in, so the router child is now started in the
workspace rather than the plugin directory. session_start uses the same helper.

Child output is not forwarded blindly. The router emits a fixed template naming
skills from its own table and never prompt text, so the adapter requires that
shape, bounds the length, and rejects control characters before the string
reaches the model's turn.

Also stops the hooks writing __pycache__ into the plugin directory Cursor
manages, via sys.dont_write_bytecode and -B on the child spawn.
2026-08-23 12:38:09 -07:00
Eude Kinsley Lesperance a4cd97cf16 feat(cursor): add advisory hook runtime 2026-08-20 19:25:33 -07:00