@davesheffer/hunch 0.17.2 → 0.17.3

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/dist/cli/index.js CHANGED
@@ -67,6 +67,7 @@ program
67
67
  .option("--no-agent-hooks", "skip installing the Claude Code agent hooks (.claude/settings.json)")
68
68
  .option("--firmness <level>", "agent-hook firmness: off | advisory | firm | strict")
69
69
  .option("--private-sync", "post-commit synthesis writes captured decisions into the private overlay (HUNCH_PRIVATE_DIR), never the public repo")
70
+ .option("--auto-commit", "opt-in: the post-commit hook also git add+commit+pushes the captured decision (the repo it landed in)")
70
71
  .action((opts) => {
71
72
  // Validate --firmness up front, before any side effects (indexing, git hooks,
72
73
  // .mcp.json) or opening the store — a bad value must not leave a half-init.
@@ -95,8 +96,8 @@ program
95
96
  console.log(` ⚠ ${res.skipped} file(s) could not be parsed (skipped)`);
96
97
  }
97
98
  if (isGitRepo(root)) {
98
- const h = installPostCommitHook(root, inv.shell, { private: opts.privateSync });
99
- console.log(` ✓ post-commit hook ${h.action} (learning loop)${opts.privateSync ? " — syncs to the private overlay" : ""}`);
99
+ const h = installPostCommitHook(root, inv.shell, { private: opts.privateSync, commit: opts.autoCommit });
100
+ console.log(` ✓ post-commit hook ${h.action} (learning loop)${opts.privateSync ? " — syncs to the private overlay" : ""}${opts.autoCommit ? " — auto-commit+push on" : ""}`);
100
101
  const m = installMergeDriver(root, inv.shell);
101
102
  console.log(` ✓ team merge driver ${m.action}`);
102
103
  // Auto-install the pre-commit guard by default (advisory: flags invariants
@@ -227,6 +228,7 @@ program
227
228
  .option("--quiet", "minimal output")
228
229
  .option("--force", "re-synthesize even if a decision already exists for the commit")
229
230
  .option("--private", "write the synthesized decision into the private overlay (HUNCH_PRIVATE_DIR), not the public repo — for a repo whose memory is kept private")
231
+ .option("--commit", "after a capture, also git add+commit+push the repo the decision landed in (opt-in; best-effort) — the private store under --private, else this repo")
230
232
  .action(async (sha, opts) => {
231
233
  const { store, root } = storeFor();
232
234
  if (!isGitRepo(root))
@@ -243,6 +245,20 @@ program
243
245
  // on every commit. `hunch index`/`init` refresh it intentionally instead.
244
246
  if (!opts.fromHook)
245
247
  updateClaudeMd(root, store);
248
+ // Opt-in: persist the captured decision in the repo it landed in (private store
249
+ // under --private, else this repo). Best-effort — a non-repo dir / offline push
250
+ // just no-ops. Stage ONLY the hunch dir (never sweep unrelated working-tree
251
+ // changes), and set HUNCH_SYNC=1 so the commit we create can't re-trigger this
252
+ // hook (no recursion, including on a manual `hunch sync --commit`).
253
+ const commitTarget = opts.commit ? (opts.private ? store.privateDir : hunchPaths(root).hunch) : undefined;
254
+ if (commitTarget) {
255
+ const g = (args) => { spawnSync("git", ["-C", commitTarget, ...args], { stdio: "ignore", env: { ...process.env, HUNCH_SYNC: "1" } }); };
256
+ g(["add", "--", "."]);
257
+ g(["commit", "-m", `hunch: capture ${r.decision?.id ?? "decision"}`]);
258
+ g(["push"]);
259
+ if (!opts.quiet)
260
+ console.log(` ↳ committed + pushed ${r.decision?.id} (${commitTarget})`);
261
+ }
246
262
  if (!opts.quiet)
247
263
  console.log(`✓ captured decision ${r.decision?.id} via ${r.provider}: "${r.decision?.title}"`);
248
264
  }
@@ -257,6 +273,7 @@ program
257
273
  .description("Enable a PRIVATE memory overlay — sensitive decisions/bugs/constraints kept in a separate location, unioned into local queries, never committed here. Writes a gitignored .hunch/local.json so it's auto-detected (no env var needed).")
258
274
  .option("--repo <url>", "clone a private git repo to use as the store (into ./.hunch-private)")
259
275
  .option("--no-hook", "don't switch the post-commit hook to private sync")
276
+ .option("--auto-commit", "opt-in: the post-commit hook also git add+commit+pushes the private repo after each capture")
260
277
  .action((dir, opts) => {
261
278
  const root = findRoot();
262
279
  const paths = hunchPaths(root);
@@ -290,8 +307,8 @@ program
290
307
  let hookNote = "";
291
308
  if (opts.hook && isGitRepo(root)) {
292
309
  const inv = resolveInvocation();
293
- const h = installPostCommitHook(root, inv.shell, { private: true });
294
- hookNote = ` ✓ post-commit hook ${h.action} — captured decisions route here\n`;
310
+ const h = installPostCommitHook(root, inv.shell, { private: true, commit: opts.autoCommit });
311
+ hookNote = ` ✓ post-commit hook ${h.action} — captured decisions route here${opts.autoCommit ? " (auto-commit+push on)" : ""}\n`;
295
312
  }
296
313
  console.log(`✓ private overlay enabled → ${hunchDir}\n` +
297
314
  ` ✓ recorded in .hunch/local.json (gitignored) — auto-detected, no env var or shell-profile edit\n` +
@@ -11,14 +11,16 @@ const MARK = "# >>> hunch post-commit >>>";
11
11
  const ENDMARK = "# <<< hunch post-commit <<<";
12
12
  function block(invocation, opts = {}) {
13
13
  // --private routes the auto-synthesized decision into the HUNCH_PRIVATE_DIR overlay
14
- // instead of the public repo. The hook script is local (.git/hooks/), never committed,
15
- // so a repo whose memory is kept private leaves no trace of this in the public tree.
14
+ // instead of the public repo. --commit (opt-in) also commits & pushes the repo the
15
+ // decision landed in (the private store under --private, else this repo). The hook
16
+ // script is local (.git/hooks/), never committed.
16
17
  const priv = opts.private ? " --private" : "";
18
+ const commit = opts.commit ? " --commit" : "";
17
19
  return [
18
20
  MARK,
19
21
  'if [ -z "$HUNCH_SYNC" ]; then',
20
22
  " export HUNCH_SYNC=1",
21
- ` ( ${invocation} sync --from-hook --quiet${priv} >/dev/null 2>&1 || true ) &`,
23
+ ` ( ${invocation} sync --from-hook --quiet${priv}${commit} >/dev/null 2>&1 || true ) &`,
22
24
  "fi",
23
25
  ENDMARK,
24
26
  ].join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davesheffer/hunch",
3
- "version": "0.17.2",
3
+ "version": "0.17.3",
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.",