The deny reason is the routing surface — show all cheaper exits:
semantic priming from the timeline, get_observations for details,
and smart_outline/smart_unfold for current code structure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The per-session FileReadGate was never requested and broke the cost
savings loop — subsequent reads in the same session silently bypassed
the timeline, hiding newly created observations.
Now the timeline fires on every read that has observations, using the
hook contract's permissionDecision: "deny" with the timeline as the
reason (exit 0 + JSON) instead of exit code 2 + stderr.
- Delete FileReadGate.ts entirely
- Remove /api/file-context/gate endpoint from DataRoutes
- Switch handler from exit code 2 to permissionDecision: "deny"
- Restore permissionDecision fields to HookResult
- Eliminate one HTTP round-trip per read (no gate check needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Resolve relative filePath against input.cwd before statSync; early-return on ENOENT
- Replace LIKE '%path%' with exact json_each equality to prevent false matches
- Sanitize and parameterize LIMIT to prevent NaN SQL errors
- Fix day-sorting to use earliest epoch in group, not first (specificity-sorted) item
- Use exact path equality in deduplicateObservations instead of substring includes
- Scope FileReadGate by session+cwd to prevent worktree collisions
- Refresh lastAccess TTL on active sessions; throttle prune to every 50 calls
- Type params as (string | number)[] instead of any[]
- Remove unused permissionDecision fields from HookResult
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Skip gate for files under 1,500 bytes — timeline (~370 tokens) costs
more than just reading small files directly
- Deduplicate observations by memory_session_id (one per session)
- Rank by specificity: files_modified > files_read, fewer tagged files > many
- Fetch 40 candidates, dedup/score down to 15 for display
- Reduce default by-file query limit from 30 to 15
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The compiled binary (v10.6.3) creates these columns at runtime via
MigrationRunner, but no corresponding migration exists in the TypeScript
source. Anyone building from source gets observations without these
columns, breaking the feedback pipeline and model tracking.
This migration conditionally adds both columns using PRAGMA table_info
checks, making it safe for databases that already have them.
Refs: #1626
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The per-prompt Chroma vector search injection on UserPromptSubmit adds latency
and context noise. Disable by default while we iterate on a more precise
file-context approach. Users can still opt in via CLAUDE_MEM_SEMANTIC_INJECT=true.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PHP was listed as a supported language in CHANGELOG and .php files were
scanned by search.ts, but parser.ts was missing:
- .php extension in LANG_MAP (causing detectLanguage to return 'unknown')
- 'php' entry in GRAMMAR_PACKAGES (no grammar path to resolve)
- PHP query patterns for symbol extraction
- PHP case in getQueryKey()
This meant smart_search/smart_outline/smart_unfold scanned PHP files
but extracted 0 symbols because the grammar could not be resolved.
Changes:
- Add '.php' -> 'php' to LANG_MAP
- Add 'php' -> 'tree-sitter-php/php' to GRAMMAR_PACKAGES
- Add PHP tree-sitter query patterns (functions, methods, classes, interfaces, traits, use statements)
- Add 'php' case to getQueryKey()
- Add tree-sitter-php ^0.24.2 to devDependencies
sort -V is a GNU extension not available on macOS/BSD sort. Replaced
with sed 's/^v//' | sort -t. -k1,1n -k2,2n -k3,3n which strips the
'v' prefix, sorts numerically by major.minor.patch, then re-prepends
'v' for the final path. Works on both GNU and BSD sort.
Prepend a PATH export to every hook command that invokes node, so that
/bin/sh can locate the binary when Node.js is managed by nvm or
installed via Homebrew. Falls back gracefully when nvm is not present.
Closes#1598
On Bun/Windows, `require.main !== module` in CJS mode causes the worker
to exit silently with code 0. The wrapper already sets CLAUDE_MEM_MANAGED=true
when spawning the inner worker, so checking this env var is a safe fallback
that doesn't affect standalone execution.
Ref #1450 (incomplete fix in PR #1518 — ESM path fixed but CJS branch not).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: wire up Cursor integration in installer — was incorrectly marked "coming soon"
CursorHooksInstaller.ts was fully built but never connected to the
installer. Set supported: true in IDE detection and call installCursorHooks
in the setup flow, matching the pattern used by other integrations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: wire up Cursor MCP configuration during install
PR review flagged that the hint says "hooks + MCP integration" but
configureCursorMcp() was never called during install. Now invoked
after hooks install with graceful fallback if MCP setup fails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>