Skip to content

feat(install): support project-local agent setup - #3

Merged
ScriptedAlchemy merged 13 commits into
masterfrom
feature/local-install
Jun 7, 2026
Merged

feat(install): support project-local agent setup#3
ScriptedAlchemy merged 13 commits into
masterfrom
feature/local-install

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add tokensave install --local for project-scoped agent setup without mutating user-level install tracking or global git hooks.
  • Upgrade Cursor local install beyond MCP-only: generate .cursor/mcp.json, .cursor/rules/tokensave.mdc, .cursor/permissions.json, and .cursor/hooks.json from the installer.
  • Cursor project hooks (all fail-open, only act when .tokensave/ exists):
    • sessionStart: steer the Agent toward tokensave MCP tools + report index freshness.
    • subagentStart: block research/explore subagents until tokensave was tried.
    • beforeSubmitPrompt: reset the local token counter.
    • afterFileEdit (matcher Write): targeted single-file sync of only the edited path(s) via sync_if_stale_silent — never a full-tree scan (scan cost scales with repo size, not edit size; the Agent can edit many files per turn).
    • afterShellExecution: branch-aware. Agent-run git checkout/switch/worktree add bootstraps/maintains tokensave branch tracking via the shared library branch-add path (idempotent, no subprocess); other state-changing git commands (pull/merge/rebase/reset/cherry-pick/stash apply|pop) run a coalesced incremental sync.
    • workspaceOpen: ensure the current branch DB exists (branch add if missing) + catch-up incremental sync.
  • Installer reconciles its own hook entries on reinstall so refinements (e.g. the afterFileEdit matcher) reach pre-existing configs.
  • Regenerated this repo's .cursor/* artifacts from the installer (not hand-edited).

Codex lifecycle hooks (added)

Codex now ships a Claude-style lifecycle hook system (verified against Codex 0.136.0). The old src/agents/codex.rs premise "Codex has no hook system" was false and has been removed. This adds a Codex hook set mirroring the Cursor work, on both global (~/.codex/hooks.json) and project-local (<root>/.codex/hooks.json) installs, written in Codex's nested hooks[event] -> [{ matcher?, hooks: [{ type:"command", command, timeout }] }] shape and reconciled idempotently (foreign hooks preserved, backups on overwrite). Each handler reads Codex's single stdin JSON event and emits Codex-shaped stdout (not the Claude/Cursor/Kiro shapes).

New hidden subcommands:

  • hook-codex-session-start (SessionStart) / hook-codex-user-prompt-submit (UserPromptSubmit): emit hookSpecificOutput.additionalContext steering toward tokensave MCP tools + an index-freshness hint (suggest tokensave init if uninitialized). UserPromptSubmit also resets the per-project local counter. Project root resolved from the event cwd.
  • hook-codex-subagent-start (SubagentStart): redirect research/explore subagents toward tokensave MCP tools via additionalContext. Codex SubagentStart cannot hard-stop a subagent (continue:false is ignored for this event per the docs), so this steers rather than denies — a deliberate deviation from the Cursor subagentStart deny.
  • hook-codex-post-tool-use (PostToolUse, matcher Bash|apply_patch): apply_patch edits → targeted single-file sync of just the patched paths (parsed from the patch envelope, no full-tree scan); Bash git commands → reuse the shared classifier (cursor_shell_sync_plan) to route branch switches → branch::add_branch_tracking and other state-changing commands → coalesced incremental sync. Fail-open and silent.

Shared helpers are reused (counter reset, targeted sync, branch add, git-command classifier, and a new run_coalesced_incremental_sync). Codex installer also prints the /hooks trust-gate guidance and tokensave doctor --agent codex reports hook presence + the trust requirement. The repo's .codex/* + AGENTS.md are regenerated from the installer and tracked in git, matching the committed Cursor local-install precedent (.codex/ removed from .gitignore).

Performance decision

afterFileEdit / Codex apply_patch post-tool-use deliberately avoid TokenSave::sync() (full scan_files() over the whole tree) and instead sync only the edited paths. Shell/workspace events use a full incremental sync() because a broad change set is expected there (coalesced/guarded for back-to-back git commands).

Blind spots (intentional, documented)

  • Cursor hooks only observe the Cursor Agent's own actions and IDE lifecycle. Manual/external-terminal git checkout and in-place branch switches are NOT seen (workspaceOpen does not fire for in-place checkout).
  • Codex PostToolUse only fires for apply_patch edits and "simple" Bash — raw-shell file edits, unified_exec, and WebSearch are not observed; there is no first-class branch-switch event, so branch switches are derived from Bash git commands. Codex PreToolUse is intentionally not installed (documented partial guardrail).
  • For all unobserved cases the git post-commit hook + on-demand MCP staleness check remain the freshness mechanism. The Codex accounting parser and session search are deferred to a follow-up (not in this change).

Test plan

  • cargo test --test hooks_test (Cursor + Codex pure classifiers, routing, session context, targeted-edit / apply_patch path resolution, cwd-based project root)
  • cargo test --test agent_test (Cursor + Codex installer output incl. matchers + idempotent reconciliation; global + local)
  • cargo test (full suite — all passing)
  • cargo fmt --check
  • git diff --check
  • cargo run -- install --local --agent cursor
  • cargo run -- install --local --agent codex

@changeset-bot

changeset-bot Bot commented Jun 7, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 2a70261

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e15e3a043

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/kilo.rs Outdated
ScriptedAlchemy added 10 commits June 7, 2026 06:49
Generate Cursor project rules, permissions, and supported hooks from the local installer so Cursor can prefer tokensave safely within a repo.
Add installer-generated Cursor project hooks for the local install:
- sessionStart: steer the Agent toward tokensave MCP tools and report index
  freshness (fire-and-forget).
- afterShellExecution: on Agent-run git checkout/switch/worktree add, bootstrap
  /maintain branch tracking via the shared library branch-add path (idempotent,
  no subprocess); other state-changing git commands run a coalesced incremental
  sync.
- workspaceOpen: ensure the current branch DB exists (branch add if missing) and
  run a catch-up incremental sync.

Performance: afterFileEdit now does a TARGETED single-file sync of only the
edited path(s) via sync_if_stale_silent (matcher "Write"), never a full-tree
scan, since scan cost scales with repo size not edit size and the Agent can
edit many files per turn.

All Cursor hooks are fail-open and only act when .tokensave/ exists. The
installer now reconciles its own hook entries on reinstall so refinements like
the afterFileEdit matcher reach pre-existing configs.

Caveat (documented): Cursor hooks only observe the Cursor Agent's own actions
and IDE lifecycle. Manual/external-terminal git checkout and in-place branch
switches are not seen (workspaceOpen does not fire for in-place checkout); the
git post-commit hook and on-demand MCP staleness check cover those.
Codex supports a Claude-style lifecycle hook system (verified against
0.136.0); the old "Codex has no hook system" premise was false and is
removed. Mirror the Cursor hook set for Codex on both global and
project-local installs.

New hidden hook subcommands (Codex stdin JSON -> Codex stdout schema):
- hook-codex-session-start / hook-codex-user-prompt-submit: emit
  hookSpecificOutput.additionalContext steering toward tokensave MCP
  tools + index-freshness hint; UserPromptSubmit also resets the
  per-project local counter.
- hook-codex-subagent-start: redirect research/explore subagents via
  additionalContext (Codex SubagentStart cannot hard-stop a subagent).
- hook-codex-post-tool-use: apply_patch edits -> targeted single-file
  sync of the patched paths; Bash git commands -> shared classifier
  routing branch switches to `branch add` and other state changes to a
  coalesced incremental sync. Fail-open and silent.

Installer writes/merges ~/.codex/hooks.json (global) and
<root>/.codex/hooks.json (local) idempotently with backups, registering
SessionStart, UserPromptSubmit, SubagentStart, and PostToolUse
(matcher Bash|apply_patch). Prints the `/hooks` trust-gate guidance and
doctor reports hook presence + the trust requirement.

PreToolUse is intentionally not installed (documented partial guardrail);
accounting parser and session search are deferred to a follow-up.

Reuses shared helpers (counter reset, targeted sync, branch add,
git-command classifier, coalesced sync). Track .codex/ + AGENTS.md in
git to match the committed Cursor local-install precedent.
… hooks

Introduce a shared, pure soft-hint engine (src/hooks/tool_hints.rs) that
returns model-visible context only — it never denies, rewrites, or blocks
tool calls. Hints are conservative (high-confidence search / broad-read /
call-graph / impact / symbol / file-lookup patterns and explore subagents)
and deduped to one per category per session.

Wire the engine into agent-specific adapters:
- Cursor: subagentStart and a new preToolUse handler emit nonblocking
  `additional_context` only (continue: true), never deny.
- Codex: a new PreToolUse handler emits `hookSpecificOutput.additionalContext`
  only, matching Codex's documented schema; SubagentStart steers via context.

Register the new soft-hint hooks in the installers:
- Cursor `.cursor/hooks.json` gains `preToolUse` (matcher
  Shell|Bash|Read|ReadFile|Grep|Glob|Search|Task).
- Codex `.codex/hooks.json` gains `PreToolUse` (matcher
  Bash|Read|Grep|Glob|Search|Task) and uninstall now reconciles it.
Both remain idempotent and preserve foreign hooks.

Also includes branch-tracking hardening (rollback on failed branch add,
path-traversal guards), install-write hardening, and the session schema
foundation (sessions/session_messages tables + FTS in global_db, plus the
provider-neutral SessionRecord/SessionMessageRecord types). Transcript
parsers and the sessions CLI are intentionally deferred to the next phase.

Regenerate the repo's committed `.cursor/` and `.codex/` configs via the
installer (kept as installer output per project policy) and document the
soft-hint behavior + generated-config policy in README and USER-GUIDE.
Cursor's afterFileEdit event carries a single top-level `file_path`; its
`edits` array holds change ranges, not per-edit paths. Drop the speculative
defensive loop (and the dedup Vec it required) in favor of a single-path
early return. Behavior is unchanged for the documented schema.
@ScriptedAlchemy
ScriptedAlchemy merged commit 718dd8a into master Jun 7, 2026
5 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the feature/local-install branch July 4, 2026 01:13
spa5k pushed a commit to spa5k/tracedecay that referenced this pull request Jul 4, 2026
Flake ScriptedAlchemy#3: `broker_bounds_lsp_document_write_hangs` and
`broker_drops_lsp_client_after_partial_diagnostics_frame_timeout`
intermittently failed on loaded runners.

Both tests force an engine crash, then re-run a healthy fake LSP server and
assert it recovers. The recovery collection paid a tight 150ms
(`FAKE_LSP_TIMEOUT`) wall-clock budget that must cover a real python spawn
plus a didOpen -> publishDiagnostics round trip. Under load that spawn can
exceed 150ms, so the recovery `refresh_documents(...).unwrap()` false-fired
with a spurious timeout.

Fix (test only): the two recovery calls now use a dedicated generous
`FAKE_LSP_RECOVERY_TIMEOUT` (3s), well under the 6s outer async bound so a
genuine hang is still caught. The crash-forcing first half keeps its tight
50ms internal timeout — it is *meant* to time out, and load only makes that
expected outcome more certain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant