@davesheffer/hunch 0.32.0 → 0.33.1

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.
package/README.md CHANGED
@@ -185,6 +185,16 @@ is **OS-agnostic**: paths are stored in POSIX form and an installed Hunch regist
185
185
  server by package name, so Windows / macOS / Linux teammates share one memory without
186
186
  per-machine fixups. → [docs](https://hunch-pi.vercel.app/docs#team)
187
187
 
188
+ ### Branches & worktrees
189
+
190
+ Memory follows you across every branch and **git worktree**, with no per-worktree setup. The
191
+ private overlay is registered once at the repo's **git common dir** (shared by all worktrees), so
192
+ a fresh `git worktree add` on any branch sees the same decisions, bugs, and invariants. Create one
193
+ already wired in with **`hunch worktree <path> [-b <branch>]`**, or just run `hunch init` / `hunch
194
+ private` once and every worktree picks it up. Auto-captured decisions are tagged with their branch,
195
+ and concurrent overlay writes are serialized — so parallel worktrees never corrupt or lose memory.
196
+ `hunch doctor` confirms a worktree is sharing.
197
+
188
198
  ## Private memory (public repo, private context)
189
199
 
190
200
  Open-source your code without open-sourcing your *reasoning*. **`hunch private`** sets up a
package/dist/cli/index.js CHANGED
@@ -34,6 +34,7 @@ import { extractInlineIntent } from "../extractors/comments.js";
34
34
  import { renderText, renderMarkdown, reportFailsStrict } from "../core/checkreport.js";
35
35
  import { partitionReview, READY_MIN_GROUNDED } from "../core/reviewqueue.js";
36
36
  import { installPostCommitHook, installPreCommitHook } from "../integrations/hooks.js";
37
+ import { ensureSharedOverlayPointer } from "../integrations/worktree.js";
37
38
  import { installMergeDriver } from "../integrations/mergeDriver.js";
38
39
  import { ensureGitignore, ignoreHunchMemory, HUNCH_MEMORY_DIRS } from "../integrations/gitignore.js";
39
40
  import { writeCiWorkflow } from "../integrations/ciAction.js";
@@ -154,6 +155,15 @@ program
154
155
  // case-split in ~/.claude.json, merge it so hunch resolves under either casing.
155
156
  // No-op (silent) off Windows.
156
157
  reportClaudeConfigHeal();
158
+ // Worktree-seamless: register any configured overlay at the git common dir so EVERY
159
+ // worktree of this repo auto-discovers it (also backfills pre-0.32 single-worktree setups),
160
+ // and note when we're initializing inside a linked worktree (memory is shared, not separate).
161
+ if (ensureSharedOverlayPointer(root, store.privateDir, store.privateAutoCommit)) {
162
+ console.log(` ✓ private overlay registered at the git common dir — shared by every worktree of this repo`);
163
+ }
164
+ if (isLinkedWorktree(root)) {
165
+ console.log(` ✓ linked worktree — sharing the repo's hooks + memory (no separate setup needed)`);
166
+ }
157
167
  store.close();
158
168
  console.log("\nNext: make a commit (the hook captures a decision), then ask your coding assistant \"why is X built this way?\"");
159
169
  console.log("Cold start? Seed from history: hunch backfill --since 90d");
@@ -348,12 +358,8 @@ program
348
358
  // (current + future, any branch) auto-discovers the same memory with zero per-worktree
349
359
  // setup. Stored ABSOLUTE — a linked worktree resolves relative paths from its OWN root, so
350
360
  // only an absolute path survives the move. Lives under .git/ (never tracked; nothing to ignore).
351
- const commonDir = gitCommonDir(root);
352
361
  let worktreeNote = "";
353
- if (commonDir) {
354
- const sharedDir = join(commonDir, "hunch");
355
- mkdirSync(sharedDir, { recursive: true });
356
- writeFileAtomic(join(sharedDir, "local.json"), JSON.stringify({ privateDir: hunchDir, autoCommit: !!opts.autoCommit }, null, 2) + "\n");
362
+ if (ensureSharedOverlayPointer(root, hunchDir, !!opts.autoCommit)) {
357
363
  worktreeNote = ` ✓ registered in the git common dir — shared by every worktree of this repo, on any branch\n`;
358
364
  }
359
365
  // 4) route post-commit synthesis to the overlay (local hook, never committed)
@@ -397,6 +403,68 @@ program
397
403
  ` record sensitive items with private:true (hunch_record_decision / hunch_record_correction)\n` +
398
404
  ` override per-shell with HUNCH_PRIVATE_DIR; CI / public PR comments stay public-only.`);
399
405
  });
406
+ // ---- worktree (one-command worktree wired into Hunch) ----------------------
407
+ program
408
+ .command("worktree <path>")
409
+ .description("Create a git worktree already wired into Hunch — it shares this repo's memory (the private overlay), with zero per-worktree setup.")
410
+ .option("-b, --branch <name>", "create the worktree on a NEW branch")
411
+ .option("--no-share", "don't register the overlay at the git common dir (the worktree won't see private memory)")
412
+ .option("--no-index", "don't build the new worktree's code graph (skip if you'll index later)")
413
+ .action((path, opts) => {
414
+ const root = findRoot();
415
+ if (!isGitRepo(root))
416
+ return fail("`hunch worktree` needs a git repo");
417
+ const dest = resolve(root, path);
418
+ if (existsSync(dest))
419
+ return fail(`path already exists: ${dest}`);
420
+ // 1) create the worktree (on a new branch if asked, else a checkout of HEAD)
421
+ const r = spawnSync("git", ["-C", root, "worktree", "add", ...(opts.branch ? ["-b", opts.branch] : []), dest], { stdio: "inherit" });
422
+ if (r.status !== 0)
423
+ return fail("git worktree add failed");
424
+ // 2) register the overlay at the SHARED git common dir so the new worktree (and every
425
+ // other) auto-discovers the same memory — also backfills pre-0.32 single-worktree setups.
426
+ const store = new HunchStore(hunchPaths(root));
427
+ const overlay = store.privateDir;
428
+ const autoCommit = store.privateAutoCommit;
429
+ store.close();
430
+ let shareNote;
431
+ if (!opts.share) {
432
+ shareNote = ` · --no-share — the worktree will NOT see private memory`;
433
+ }
434
+ else if (overlay && ensureSharedOverlayPointer(root, overlay, autoCommit)) {
435
+ shareNote = ` ✓ memory shared via the git common dir — this worktree sees the same decisions / bugs / constraints`;
436
+ }
437
+ else if (overlay) {
438
+ shareNote = ` · could not register the shared overlay pointer (no git common dir?)`;
439
+ }
440
+ else {
441
+ shareNote = ` · no private overlay configured — run \`hunch private\` to share memory across worktrees`;
442
+ }
443
+ // 3) build the new worktree's CODE GRAPH (symbols/edges → blast-radius / dependents).
444
+ // Indexed IN-PROCESS (uses THIS install's tree-sitter, so the worktree needs no
445
+ // node_modules) and ONLY when the graph isn't already committed in the checkout —
446
+ // re-parsing a normal repo would just dirty its tracked .hunch/*.json. Writes the
447
+ // derived (gitignored) index, never the working tree.
448
+ let indexNote = "";
449
+ if (opts.index !== false) {
450
+ const wstore = new HunchStore(hunchPaths(dest));
451
+ wstore.json.ensureDirs();
452
+ if (wstore.json.loadAll("symbols").length === 0) {
453
+ const res = indexRepo(wstore, dest);
454
+ wstore.reindex();
455
+ indexNote = `\n ✓ indexed ${res.files} file(s) → ${res.symbols} symbols — blast-radius ready (code graph isn't committed here)`;
456
+ }
457
+ else {
458
+ wstore.reindex(); // committed graph already in the checkout → just build the derived SQLite
459
+ indexNote = `\n ✓ code graph present in the checkout — blast-radius ready`;
460
+ }
461
+ wstore.close();
462
+ }
463
+ console.log(`✓ worktree created → ${dest}${opts.branch ? ` (new branch ${opts.branch})` : ""}\n` +
464
+ `${shareNote}${indexNote}\n` +
465
+ ` hooks + MCP server are shared (worktree-aware) — open your assistant in the new worktree to start.\n` +
466
+ ` (needs \`hunch\` installed globally; a worktree has no node_modules of its own)`);
467
+ });
400
468
  // ---- query ----------------------------------------------------------------
401
469
  program
402
470
  .command("query")
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Worktree wiring — make a repo's private-overlay memory seamless across every git
3
+ * worktree (v0.32+). A worktree's gitignored `.hunch/local.json` pointer doesn't exist
4
+ * in a fresh checkout, so we register the overlay at the SHARED git common dir
5
+ * (`git rev-parse --git-common-dir`), which every linked worktree resolves identically.
6
+ */
7
+ import { existsSync, readFileSync, mkdirSync } from "node:fs";
8
+ import { join, resolve } from "node:path";
9
+ import { gitCommonDir } from "../extractors/git.js";
10
+ import { writeFileAtomic } from "../core/io.js";
11
+ /** Register the resolved private overlay at the shared git common dir, so every worktree
12
+ * of this repo auto-discovers the same memory. Idempotent (writes only when missing or
13
+ * changed). Stored ABSOLUTE — a worktree resolves relative paths from its OWN root.
14
+ * Returns true once the shared pointer is in place (memory is worktree-shared), false when
15
+ * there's no overlay configured or no git common dir. Reused by `init`/`worktree`/`private`. */
16
+ export function ensureSharedOverlayPointer(root, overlayDir, autoCommit) {
17
+ const common = overlayDir ? gitCommonDir(root) : "";
18
+ if (!common || !overlayDir)
19
+ return false;
20
+ const file = join(common, "hunch", "local.json");
21
+ const want = JSON.stringify({ privateDir: resolve(overlayDir), autoCommit }, null, 2) + "\n";
22
+ try {
23
+ if (!(existsSync(file) && readFileSync(file, "utf8") === want)) {
24
+ mkdirSync(join(common, "hunch"), { recursive: true });
25
+ writeFileAtomic(file, want);
26
+ }
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ //# sourceMappingURL=worktree.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davesheffer/hunch",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Dave Sheffer <dave.sheffer1@gmail.com>",
6
6
  "description": "Hunch — an Engineering Memory OS: a persistent, git-native reasoning graph over a codebase, exposed to Claude Code via MCP.",