Skip to content

feat(cli): engram reindex <file> + optional --auto-reindex hook (#8)#13

Merged
NickCirv merged 1 commit intoNickCirv:mainfrom
gabiudrescu:feat/issue-8-reindex-subcommand
Apr 21, 2026
Merged

feat(cli): engram reindex <file> + optional --auto-reindex hook (#8)#13
NickCirv merged 1 commit intoNickCirv:mainfrom
gabiudrescu:feat/issue-8-reindex-subcommand

Conversation

@gabiudrescu
Copy link
Copy Markdown
Contributor

@gabiudrescu gabiudrescu commented Apr 18, 2026

Closes #8.

Decisions you might push back on

  • Prune produces a log line, not silent no-op. AC 4 says missing
    files exit silently; your acceptance note says
    missing-but-previously-indexed should be "an explicit prune, not a
    silent no-op". I split it: never-indexed missing file → silent;
    previously-indexed now-missing → engram: pruned <file> (<N> nodes).
    One-line change in formatReindexLine to flip prune back to silent
    if you'd rather.
  • runReindexHook walks from the FILE path, not cwd. Caught
    this live during smoke testing: a Claude Code session cwd often
    sits ABOVE the engram-initialized project that owns the edited file
    (monorepo, multi-project parent). findProjectRoot(cwd) silent
    no-ops there. Fixed, test covers the shape.
  • Installer idempotence check narrowed. isEngramHookEntry had
    to broaden to match engram reindex-hook so uninstall cleans it
    up. But using the broadened check for install idempotence would
    false-positive on an auto-reindex-only PostToolUse and skip adding
    the missing intercept entry. Split it:
    entryContainsCommand(entry, "engram intercept") for install
    targeting; isEngramHookEntry stays broad for uninstall.
  • --quiet skipped. I proposed it in Feature: engram reindex <file> subcommand + optional PostToolUse auto-reindex #8; you didn't accept or
    reject. The hook subcommand is silent by design and CLI output is
    one line, so I left it out. Easy add if you want it.
  • Follow-up issue coming. Smoke-testing surfaced the obvious
    next question — what about rm foo.ts via Bash? Out of scope here
    (matcher is Edit|Write|MultiEdit per your note), but I'll file a
    separate issue proposing to widen to Bash and parse
    tool_input.command for rm/mv/git-rm/redirections.
    src/intercept/handlers/bash.ts is the precedent for that kind of
    parsing.

Test plan

  • npm run lint clean
  • npm test -- --run — 704 passed (+16 new)
  • npm run build:nogrammars clean
  • Live against Claude Code: install-hook --auto-reindex
    Write new file → engram query finds new function → delete +
    engram reindex emits correct prune line → query empty →
    uninstall-hook removes both engram entries, preserves non-engram.

🤖 Generated with Claude Code

…Cirv#8)

Closes NickCirv#8. Two parts, one commit; the maintainer offered to split into
two PRs in the acceptance comment — happy to split on request.

Part 1 — CLI subcommand
- `engram reindex <file> [-p, --project <path>] [--verbose]`: re-indexes
  a single file via the shared `syncFile()` primitive from NickCirv#9/NickCirv#12.
  Semantics match `engram watch`: exists → reindex; missing-but-
  previously-indexed → prune; unsupported ext or ignored dir → silent
  exit 0 (safe to fire from a PostToolUse hook on every edit).
- On success: single line `engram: reindexed <file> (<N> nodes)` (or
  `pruned`) using locale-stable `formatThousands` — no Windows ICU
  flake, matching the e099bf1 migration.
- Missing graph: exits 1 with `engram: no graph found at <root>. Run
  'engram init' first.`, mirroring `watchProject`'s explicit error.
- Errors: single stderr line; `--verbose` surfaces the stack trace.
- New pure formatter `formatReindexLine(result, displayPath)` exported
  from `src/watcher.ts` (returns null for skipped so callers stay silent).

Part 2 — optional auto-reindex wiring
- `engram reindex-hook`: PostToolUse stdin entry point. Reads Claude
  Code's JSON payload, extracts `tool_input.file_path`, delegates to
  `runReindexHook()` → `syncFile()`. ALWAYS exits 0 — malformed JSON,
  missing fields, non-project cwd, and internal errors all resolve to
  a silent no-op. Same bounded-stdin + watchdog pattern as `intercept`;
  `process.exitCode = 0` without `process.exit` so sql.js WASM drains.
- `engram install-hook --auto-reindex`: off by default so existing
  users aren't surprised. Appends a second PostToolUse entry with
  matcher `Edit|Write|MultiEdit` (per the maintainer's acceptance)
  whose command is `engram reindex-hook`. Idempotent.
- `isEngramHookEntry()` broadened to also match `engram reindex-hook`
  so `engram uninstall-hook` strips the auto-reindex entry alongside
  the primary intercept entries.
- Installer idempotence check targets `engram intercept` specifically
  (via a new internal `entryContainsCommand` helper) — otherwise the
  newly-broadened `isEngramHookEntry` would false-positive when only
  the reindex-hook entry is present, causing install to skip adding
  the missing intercept entry.

Implementation notes
- `runReindexHook` walks up from the FILE path (not `cwd`) to find the
  project root. Claude Code's session `cwd` often sits above the
  engram-initialized project that owns the edited file (multi-project
  parent, monorepo subtree); `findProjectRoot(cwd)` would silently
  no-op in that case. Test covers this exact shape.
- MultiEdit included in the matcher per the maintainer's acceptance
  note (the issue originally said Edit|Write).

Tests (+16 new, all green — 704 total)
- `formatReindexLine`: 4 unit tests (indexed/pruned/skipped/locale-stable).
- `engram reindex` E2E via subprocess: happy path, non-code silent,
  no-graph error, modified-file-updates-graph (old fn out / new fn in),
  two-process concurrency (AC 6, honestly labelled as multi-process).
- `runReindexHook`: 5 unit tests (happy, relative path, malformed-shape
  matrix, cwd-outside-project, walks-up-from-file-not-cwd).
- `engram reindex-hook` E2E via subprocess: happy + malformed JSON +
  empty stdin, always exits 0.
- `installEngramHooks({ autoReindex: true })`: 6 tests (adds second
  PostToolUse entry, off-by-default, idempotent, add-onto-existing,
  uninstall-strips-it, uninstall-preserves-non-engram).
- `buildReindexHookEntry`, broadened `isEngramHookEntry`, and
  `formatInstallDiff` coverage for the new entry.

Live smoke test (my machine)
- `engram install-hook --scope user --auto-reindex` wrote the entry
  cleanly with a backup.
- A `Write` of `src/proof-issue-8.ts` fired `engram reindex-hook` via
  Claude Code; `engram query` found the new function immediately.
- `engram reindex src/proof-issue-8.ts` after `rm` produced
  `engram: pruned src/proof-issue-8.ts (2 nodes)` and the query came
  back empty.
- `engram uninstall-hook` removed both engram entries cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@NickCirv NickCirv merged commit ad41f11 into NickCirv:main Apr 21, 2026
4 checks passed
NickCirv added a commit that referenced this pull request Apr 21, 2026
Captures the brainstorming session outcome for the three-release plan:

- v2.1 "Reliability + Zero-Friction Install" — close the bleeding,
  merge contributor PRs (#6, #13), fix #11, ship engram update /
  engram doctor / engram setup, close #14 via Bash PostTool parser.
- v2.2 "Spine" — integrate Serena as an engram provider via a new
  reusable MCP-client subsystem. Engram becomes the orchestrator,
  not a fighter of semantic-search tools.
- v3.0 "Landmines" — mistakes-as-moat expansion + R2 repositioning
  ("the context tool that remembers what broke"). Keep the name,
  rebrand the tagline.

Strategic choices recorded:
- Trilogy (alpha) over mega-release (beta) or split (gamma)
- R2 (keep name, rebrand tagline) over R1 (keep everything) or R3
  (rename, prohibitive cost)
- Update UX: option A — passive notify + manual install, zero
  telemetry, ENGRAM_NO_UPDATE_CHECK + \$CI opt-out

Grounded in measured research:
- npm downloads 1.3K/week, 10/day organic baseline
- r/LocalLLaMA post ratioed (0.44) due to name collision with 4
  other "Engram" projects launched Mar-Apr 2026
- Serena just hit stable with published evals — credible complement
- 2 active external contributors writing substantive PRs

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NickCirv added a commit that referenced this pull request Apr 21, 2026
Adds a strict parser for file-mutating Bash commands — rm, mv, cp,
git rm, git mv, and single-redirect commands. Returns a list of
FileOp { action: "reindex" | "prune", path: absolute } that the
PostToolUse dispatch path will hand to syncFile() from watcher.ts.

This is HALF of issue #14. The other half is the dispatch wire-up
(route PostToolUse:Bash through this parser and invoke syncFile for
each op) which depends on PR #13's --auto-reindex flag landing first.
The parser ships standalone so it is review-ready and test-complete
before #13 merges.

Parser philosophy (mirrors handlers/bash.ts PreToolUse):

  STRICT. Any ambiguity → passthrough. False negatives cost tokens;
  false positives corrupt the graph. Optimize for the latter.

Supported shapes (intercepted):
  rm [-rf] <path> [<path>...]           → prune each
  mv <src> <dst>                        → prune src, reindex dst
  cp <src> <dst>                        → reindex dst
  git rm [-r] <path>                    → prune
  git mv <src> <dst>                    → prune src, reindex dst
  <simple-cmd> > <dst>                  → reindex dst (single redirect)
  <simple-cmd> >> <dst>                 → reindex dst (single redirect)

Intentionally NOT supported (pass through):
  - globs (rm *.ts)
  - pipes (find . | xargs rm)
  - subshells / command substitution (\$(…), backticks, <(…), >(…))
  - touch (empty file, nothing to index)
  - directory-level ops (needs directory-prefix prune primitive,
    tracked for v2.2)

Tests: 29 new tests across rm/mv/cp/git-rm/git-mv variants,
redirections, pass-through edge cases, and the high-level
handleBashPostTool wrapper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NickCirv added a commit that referenced this pull request Apr 21, 2026
…h-postool

## Closes #11 — AST/LSP reported unavailable despite enabled

Credit: Tommaso Tessarolo (@ttessarolo) — precise forensics + suggested fix.

### checkAst: flattened-bundle path resolution

When tsup/esbuild flattens the CLI into `engramx/dist/chunk-*.js`,
import.meta.url resolves `here = engramx/dist`. The previous candidates:

  - `here + '../grammars'`          → engramx/grammars  (✗ does not exist)
  - `here + '../../dist/grammars'`  → parent/dist/grammars (✗ does not exist)

Fix: add `join(here, "grammars")` as the FIRST candidate. This resolves
to `engramx/dist/grammars/` where grammars actually ship. Dev-time
layout (src/intercept/) still works via the third candidate.

### checkLsp: socket candidates out of sync

The check only looked for `tsserver.sock` and `typescript-language-server.sock`,
but `lsp-connection.ts::candidateSockets()` also probes:

  - tsserver-<uid>.sock
  - lsp-server.sock
  - pyright-<uid>.sock
  - rust-analyzer.sock

Also: the `.engram/lsp-available` flag was documented as "written by
the lsp provider on successful connection" but no code path writes it.
Kept the marker as an explicit user opt-in (back-compat) but synced
the socket list with lsp-connection.ts so HUD availability matches
actual provider availability.

### Regression test

tests/intercept/component-status-11.test.ts — asserts refreshComponentStatus
returns a boolean for every scenario + honors the .engram/lsp-available
marker.

## Wires bash-postool into PostToolUse (issue #14 full wire-up)

With @gabiudrescu's PR #12 already merged, syncFile() is available.
We can wire the v2.1 Bash parser into the PostToolUse observer path
without waiting for PR #13's install-hook --auto-reindex flag.

- post-tool.ts imports handleBashPostTool + syncFile.
- On PostToolUse with tool=Bash, parse the command; for each FileOp,
  call syncFile(absPath, projectRoot) fire-and-forget.
- Gated by ENGRAM_AUTO_REINDEX=1 (opt-in) until PR #13 lands and we
  can migrate to the install-hook flag.
- Observer semantics preserved: never blocks the PostToolUse response,
  errors swallowed, never surfaces bugs to Claude Code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gabiudrescu gabiudrescu deleted the feat/issue-8-reindex-subcommand branch April 21, 2026 06:09
NickCirv added a commit that referenced this pull request Apr 24, 2026
Follow-up to cherry-picks b4f7944 (PR #6 commit 1) + 411ad13 (PR #6 commit 2)
from @mechtar-ru.

Deletes redundant DEFAULT_EXCLUDED_DIRS (15 entries) + loadEngramIgnore()
(16 lines) that lived in parallel with the canonical DEFAULT_SKIP_DIRS +
loadIgnorePatterns() pair (the latter shipped in v2.1.0 via PR #13).
Both pairs implemented the same .engramignore feature — keeping only
the v2.1 canonical pair keeps one source of truth.

Also tightens entries typing: 'let entries: Dirent[]' in extractDirectory
(ReturnType<typeof readdirSync> resolves to the string[] default overload,
not the Dirent[] shape actually returned with { withFileTypes: true }).

All 784 tests pass. TypeScript clean.

Closes issue #5 (via PR #6 content: MAX_DEPTH=100 + MAX_FILES_PER_COMMIT=50
+ .engramignore support + expanded default skip dirs — the OOM crash on
init for 2.2GB/34K-file projects like Axolotl is fixed).
shahe-dev pushed a commit to shahe-dev/engram that referenced this pull request Apr 25, 2026
First release in the v2.1 / v2.2 / v3.0 elevation trilogy. Ships:

- engram update / doctor / setup — zero-friction install + self-upgrade
- engram init --with-hook — one-command init + hook install
- First-run hint on stderr (throttled, \$CI-safe, opt-out env)
- Bash PostTool parser for auto-reindex (issue NickCirv#14 half, opt-in via
  ENGRAM_AUTO_REINDEX=1 until PR NickCirv#13's --auto-reindex flag becomes
  authoritative)
- Fix issue NickCirv#11 — AST grammar detection in flattened bundles
  (@ttessarolo) + LSP socket candidate sync
- ESM require() runtime bug fix across doctor/setup (would have
  silently failed in production; caught by in-process audit, not by
  CI because vitest provides CommonJS interop)
- Windows cross-platform test fix for bash-postool (platform-native
  path.resolve in expected values, not hard-coded POSIX)

See CHANGELOG.md [2.1.0] for full detail + contributor credits.

Release artifacts:
- package.json version 2.0.2 → 2.1.0
- package-lock.json synced
- CHANGELOG [Unreleased] entries rehomed under [2.1.0] — 2026-04-21

Post-merge steps (not in this commit):
  git tag -a v2.1.0 -m "v2.1.0 Reliability + Zero-Friction Install"
  git push --tags
  npm publish
  gh release create v2.1.0 --generate-notes

Co-Authored-By: Claude Opus 4.7 (1M context) <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.

Feature: engram reindex <file> subcommand + optional PostToolUse auto-reindex

2 participants