@davesheffer/hunch 1.21.0 → 1.21.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
@@ -174,7 +174,7 @@ repository, separate from the code repository. Hunch does not host it. Create a
174
174
  that every teammate can access, install Hunch on team machines and CI, then have one maintainer run:
175
175
 
176
176
  ```bash
177
- npm i -g @davesheffer/hunch@1.21.0
177
+ npm i -g @davesheffer/hunch@1.21.1
178
178
  hunch shared --repo git@github.com:acme/project-hunch-memory.git
179
179
  git add .gitignore .hunch/team.json
180
180
  git commit -m "chore: connect shared Hunch memory"
@@ -189,7 +189,7 @@ printed by Hunch. Omit `--migrate` for a new setup.
189
189
  After the pointer commit lands, teammates need Hunch installed and Git access to the memory repo:
190
190
 
191
191
  ```bash
192
- npm i -g @davesheffer/hunch@1.21.0
192
+ npm i -g @davesheffer/hunch@1.21.1
193
193
  git pull
194
194
  hunch init
195
195
  hunch doctor
package/dist/cli/index.js CHANGED
@@ -309,7 +309,7 @@ program
309
309
  // Claude's native hooks run alongside provider-specific hooks below. Every
310
310
  // adapter reads firmness at run time, so changing it needs no config rewrite.
311
311
  if (opts.agentHooks !== false) {
312
- const a = installClaudeHooks(root, `${inv.shell} hook`);
312
+ const a = installClaudeHooks(root, `${inv.agentHookShell} hook`);
313
313
  console.log(` ✓ Claude Code agent hooks ${a.action} (firmness: ${firmness} — change with \`hunch firmness <level>\`)`);
314
314
  }
315
315
  // Multi-assistant compatibility: MCP + grounding + lifecycle adapters share
@@ -16,6 +16,13 @@ export function dim(s) {
16
16
  export function publishedMcpInvocation() {
17
17
  return { command: "npx", args: ["-y", `--package=${HUNCH_NPX_PACKAGE_SPEC}`, "hunch"] };
18
18
  }
19
+ /** Render a structured invocation for the host shell. Safe tokens stay bare so
20
+ * PowerShell can execute the first token; paths and other unsafe tokens use
21
+ * JSON string quoting, which is accepted by PowerShell, cmd, and POSIX sh. */
22
+ export function shellInvocation(inv) {
23
+ const token = (part) => /^[A-Za-z0-9_@:=+.,/-]+$/.test(part) ? part : JSON.stringify(part);
24
+ return [inv.command, ...inv.args].map(token).join(" ");
25
+ }
19
26
  /** The doctor command's synthesis-status line(s) for a resolved provider.
20
27
  * Exported for testing — the previous version (a bare provider-name switch,
21
28
  * before the resolveSynthesisProvider preference system existed) had zero
@@ -77,23 +84,29 @@ export function resolveInvocation() {
77
84
  // absolute-node invocation below.
78
85
  const installed = !isDev && entry.replace(/\\/g, "/").includes("/node_modules/");
79
86
  if (installed) {
87
+ const mcp = publishedMcpInvocation();
80
88
  return {
81
89
  shell: `${q(process.execPath)} ${q(entry)}`,
82
- mcp: publishedMcpInvocation(),
90
+ agentHookShell: shellInvocation(mcp),
91
+ mcp,
83
92
  };
84
93
  }
85
94
  if (isDev) {
95
+ const mcp = { command: "npx", args: ["tsx", entry] };
86
96
  return {
87
97
  shell: `npx tsx ${q(entry)}`,
88
- mcp: { command: "npx", args: ["tsx", entry] },
98
+ agentHookShell: shellInvocation(mcp),
99
+ mcp,
89
100
  };
90
101
  }
91
102
  // Source-checkout dist run (e.g. `node dist/cli/index.js`, npm link): inherently
92
103
  // per-machine. Use the absolute node binary (process.execPath) rather than a bare
93
104
  // `node`, so the hook works even when nvm's `node` isn't on the hook's PATH.
105
+ const mcp = { command: process.execPath, args: [entry] };
94
106
  return {
95
107
  shell: `${q(process.execPath)} ${q(entry)}`,
96
- mcp: { command: process.execPath, args: [entry] },
108
+ agentHookShell: shellInvocation(mcp),
109
+ mcp,
97
110
  };
98
111
  }
99
112
  //# sourceMappingURL=invocation.js.map
@@ -101,14 +101,25 @@ Reconcile decision-grounding drift for **$ARGUMENTS** (or the whole repo).
101
101
  3. Only if I explicitly say "the DECISION is stale, not the doc" (Heal B): run /capture to record a superseding decision, then return to step 2 — the prose re-derives from the new decision as a separate confirm.
102
102
  4. Report: healed (Heal A), superseded (Heal B), skipped. Never touch the graph except via an explicit Heal B capture.
103
103
  `;
104
- /** A settings.json hook entry is Hunch's if any of its commands ends with the
105
- * Hunch CLI entry + the `hook` subcommand (e.g. `…/index.js hook`). Matching the
106
- * command TAIL not the absolute path — makes re-init idempotent AND survives a
107
- * repo-folder rename (the path before index.js changes; the tail does not). The
108
- * leading path separator (`/` or `\`) requires `index` to be a full path segment,
109
- * so a foreign tool's `…/myindex.js hook` isn't mistaken for ours and clobbered. */
104
+ /** A settings.json hook entry is Hunch's if any of its commands is either the
105
+ * native/source CLI entry (`…/index.js hook`) or the exact published-package
106
+ * launcher written by older Hunch versions (`npx --package=…@davesheffer/hunch…
107
+ * hunch hook`). Matching both generations makes an upgrade idempotent instead
108
+ * of leaving the portable old hook alongside the new native invocation. The
109
+ * source form still requires `index` to be a full path segment, and the npx form
110
+ * requires both the scoped package and the `hunch hook` tail, so foreign hooks
111
+ * are preserved. */
110
112
  function isHunchHook(entry) {
111
- return !!entry.hooks?.some((h) => typeof h.command === "string" && /[\\/]index\.(js|ts)"?\s+hook\s*$/.test(h.command));
113
+ return !!entry.hooks?.some((h) => {
114
+ if (typeof h.command !== "string")
115
+ return false;
116
+ const command = h.command;
117
+ const nativeOrSource = /[\\/]index\.(js|ts)"?\s+hook\s*$/.test(command);
118
+ const publishedNpx = /^\s*"?npx(?:\.cmd)?"?\s+/i.test(command)
119
+ && /--package=(?:hunch-exact@npm:)?@davesheffer\/hunch(?:@[^"\s]+)?/.test(command)
120
+ && /\s"?hunch"?\s+"?hook"?\s*$/.test(command);
121
+ return nativeOrSource || publishedNpx;
122
+ });
112
123
  }
113
124
  /**
114
125
  * Install the Claude Code AGENT hooks into `.claude/settings.json` so the agent
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davesheffer/hunch",
3
- "version": "1.21.0",
3
+ "version": "1.21.1",
4
4
  "mcpName": "io.github.davesheffer/hunch",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dave Sheffer <dave.sheffer1@gmail.com>",
package/server.json CHANGED
@@ -7,13 +7,13 @@
7
7
  "source": "github"
8
8
  },
9
9
  "websiteUrl": "https://hunch-pi.vercel.app",
10
- "version": "1.21.0",
10
+ "version": "1.21.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "registryBaseUrl": "https://registry.npmjs.org",
15
15
  "identifier": "@davesheffer/hunch",
16
- "version": "1.21.0",
16
+ "version": "1.21.1",
17
17
  "runtimeHint": "npx",
18
18
  "packageArguments": [
19
19
  {