@davesheffer/hunch 1.45.1 → 1.46.0
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 +165 -33
- package/dist/cli/invocation.d.ts +14 -0
- package/dist/cli/invocation.js +23 -0
- package/dist/cli/serve.d.ts +2 -0
- package/dist/cli/serve.js +2 -1
- package/dist/cli/verify.d.ts +28 -0
- package/dist/cli/verify.js +265 -0
- package/dist/constitution/experiment.d.ts +1 -1
- package/dist/core/agenthook.d.ts +1 -0
- package/dist/core/agenthook.js +82 -26
- package/dist/core/boundedSpawn.d.ts +57 -0
- package/dist/core/boundedSpawn.js +321 -0
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.js +1 -0
- package/dist/core/delivery.d.ts +10 -0
- package/dist/core/delivery.js +117 -9
- package/dist/core/migrate.d.ts +10 -1
- package/dist/core/migrate.js +17 -6
- package/dist/core/pipeline.d.ts +9 -2
- package/dist/core/pipeline.js +13 -3
- package/dist/core/procstart.d.ts +17 -3
- package/dist/core/procstart.js +26 -5
- package/dist/core/projectDna.js +11 -1
- package/dist/core/relativeImports.js +6 -2
- package/dist/core/taskSelection.d.ts +1 -0
- package/dist/core/taskSelection.js +132 -11
- package/dist/core/testSelection.d.ts +122 -0
- package/dist/core/testSelection.js +588 -0
- package/dist/core/whyRecord.d.ts +47 -0
- package/dist/core/whyRecord.js +105 -0
- package/dist/extractors/git.d.ts +32 -0
- package/dist/extractors/git.js +206 -94
- package/dist/extractors/indexer.js +42 -6
- package/dist/extractors/languages.d.ts +9 -0
- package/dist/extractors/languages.js +28 -0
- package/dist/extractors/parse.d.ts +20 -0
- package/dist/extractors/parse.js +81 -5
- package/dist/extractors/workspaces.js +3 -1
- package/dist/integrations/health.d.ts +6 -0
- package/dist/integrations/health.js +33 -12
- package/dist/integrations/hooks.d.ts +4 -2
- package/dist/integrations/hooks.js +12 -2
- package/dist/integrations/mergeDriver.js +5 -3
- package/dist/integrations/optout.d.ts +3 -0
- package/dist/integrations/optout.js +17 -0
- package/dist/integrations/providers.d.ts +6 -2
- package/dist/integrations/providers.js +19 -2
- package/dist/integrations/team.js +6 -4
- package/dist/integrations/uninstall.d.ts +17 -4
- package/dist/integrations/uninstall.js +35 -13
- package/dist/mcp/server.js +179 -30
- package/dist/serve/app.js +9 -1
- package/dist/serve/config.js +5 -0
- package/dist/serve/writelock.d.ts +6 -5
- package/dist/serve/writelock.js +26 -21
- package/dist/store/hunchStore.d.ts +12 -1
- package/dist/store/hunchStore.js +86 -4
- package/dist/store/jsonStore.d.ts +24 -4
- package/dist/store/jsonStore.js +75 -26
- package/dist/store/stateBinding.js +17 -4
- package/dist/synthesis/provider.js +26 -78
- package/dist/synthesis/synthesize.js +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
* Every provider returns the same shape so the rest of the system never knows
|
|
24
24
|
* (or cares) which one ran.
|
|
25
25
|
*/
|
|
26
|
-
import { execFile, spawn } from "node:child_process";
|
|
27
26
|
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
28
27
|
import { isIP } from "node:net";
|
|
29
28
|
import { tmpdir } from "node:os";
|
|
30
29
|
import { dirname, join } from "node:path";
|
|
30
|
+
import { boundedSpawn } from "../core/boundedSpawn.js";
|
|
31
31
|
import { writeFileAtomic } from "../core/io.js";
|
|
32
32
|
import { summarizeDiff } from "../extractors/diff.js";
|
|
33
33
|
import { languageFor } from "../extractors/languages.js";
|
|
@@ -45,84 +45,32 @@ const IS_WIN = process.platform === "win32";
|
|
|
45
45
|
* win32 routes through cmd.exe so the shim resolves. Safe here because
|
|
46
46
|
* every argv we pass is a trusted, space-free flag (the prompt is stdin).
|
|
47
47
|
*/
|
|
48
|
-
export function pexecIn(cmd, args, opts = {}) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
windowsHide: true,
|
|
65
|
-
});
|
|
66
|
-
const max = opts.maxBuffer ?? 16 * 1024 * 1024;
|
|
67
|
-
let out = "";
|
|
68
|
-
let err = "";
|
|
69
|
-
let outLen = 0;
|
|
70
|
-
let settled = false;
|
|
71
|
-
// Windows spawns through a cmd.exe wrapper (shell:true), and child.kill()
|
|
72
|
-
// terminates only that wrapper — the actual agent CLI survives as an orphan,
|
|
73
|
-
// still burning the user's subscription after every timeout, accumulating
|
|
74
|
-
// with each post-commit hook fire (issue #44). taskkill /T fells the tree.
|
|
75
|
-
// Best-effort by design: the wrapper kill below still runs either way.
|
|
76
|
-
const killTree = () => {
|
|
77
|
-
if (IS_WIN && child.pid) {
|
|
78
|
-
try {
|
|
79
|
-
execFile("taskkill", ["/pid", String(child.pid), "/T", "/F"], { windowsHide: true }, () => { });
|
|
80
|
-
}
|
|
81
|
-
catch { /* taskkill unavailable — fall through to the wrapper kill */ }
|
|
82
|
-
}
|
|
83
|
-
child.kill();
|
|
84
|
-
};
|
|
85
|
-
const done = (fn) => {
|
|
86
|
-
if (settled)
|
|
87
|
-
return;
|
|
88
|
-
settled = true;
|
|
89
|
-
if (timer)
|
|
90
|
-
clearTimeout(timer);
|
|
91
|
-
fn();
|
|
92
|
-
};
|
|
93
|
-
const timer = opts.timeout
|
|
94
|
-
? setTimeout(() => {
|
|
95
|
-
killTree();
|
|
96
|
-
done(() => reject(new Error(`"${cmd}" timed out after ${opts.timeout}ms`)));
|
|
97
|
-
}, opts.timeout)
|
|
98
|
-
: null;
|
|
99
|
-
child.on("error", (e) => done(() => reject(e)));
|
|
100
|
-
child.stdout.on("data", (d) => {
|
|
101
|
-
outLen += d.length;
|
|
102
|
-
if (outLen > max) {
|
|
103
|
-
killTree();
|
|
104
|
-
done(() => reject(new Error(`"${cmd}" exceeded maxBuffer (${max} bytes)`)));
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
out += d.toString();
|
|
108
|
-
});
|
|
109
|
-
child.stderr.on("data", (d) => {
|
|
110
|
-
err += d.toString();
|
|
111
|
-
});
|
|
112
|
-
child.on("close", (code) => {
|
|
113
|
-
done(() => {
|
|
114
|
-
if (code === 0)
|
|
115
|
-
resolve({ stdout: out });
|
|
116
|
-
else
|
|
117
|
-
reject(new Error(`"${cmd}" exited ${code}: ${err.slice(0, 300)}`));
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
// Feed stdin (the prompt) then close it; commands with no input just get EOF.
|
|
121
|
-
if (opts.input != null)
|
|
122
|
-
child.stdin.write(opts.input);
|
|
123
|
-
child.stdin.on("error", () => { }); // ignore EPIPE if the child exits early
|
|
124
|
-
child.stdin.end();
|
|
48
|
+
export async function pexecIn(cmd, args, opts = {}) {
|
|
49
|
+
// Windows: launch through cmd.exe so the `claude` .cmd/.ps1 shim resolves,
|
|
50
|
+
// and pass the whole line as ONE shell string (args are trusted, space-free
|
|
51
|
+
// flags) — avoids Node's DEP0190 warning for `args + shell:true`. The prompt
|
|
52
|
+
// is never here; it goes via stdin. POSIX: no shell, argv as-is.
|
|
53
|
+
// boundedSpawn owns the timer and, on timeout or overflow, kills the WHOLE
|
|
54
|
+
// tree while the root is alive: on Windows child.kill() alone ends only the
|
|
55
|
+
// cmd.exe wrapper and orphans the agent CLI, still burning the user's
|
|
56
|
+
// subscription after every timeout (issues #44, #489).
|
|
57
|
+
const result = await boundedSpawn(IS_WIN ? [cmd, ...args].join(" ") : cmd, IS_WIN ? [] : args, {
|
|
58
|
+
shell: IS_WIN,
|
|
59
|
+
env: initiatorChildEnv(opts.env ?? process.env),
|
|
60
|
+
cwd: opts.cwd,
|
|
61
|
+
input: opts.input,
|
|
62
|
+
timeoutMs: opts.timeout,
|
|
63
|
+
maxBuffer: opts.maxBuffer ?? 16 * 1024 * 1024,
|
|
125
64
|
});
|
|
65
|
+
if (result.error)
|
|
66
|
+
throw result.error;
|
|
67
|
+
if (result.timedOut)
|
|
68
|
+
throw new Error(`"${cmd}" timed out after ${opts.timeout}ms`);
|
|
69
|
+
if (result.overflowed)
|
|
70
|
+
throw new Error(`"${cmd}" exceeded maxBuffer (${opts.maxBuffer ?? 16 * 1024 * 1024} bytes)`);
|
|
71
|
+
if (result.status !== 0)
|
|
72
|
+
throw new Error(`"${cmd}" exited ${result.status}: ${result.stderr.slice(0, 300)}`);
|
|
73
|
+
return { stdout: result.stdout };
|
|
126
74
|
}
|
|
127
75
|
/** Every selectable synthesis mode. `auto` is a preference value rather than a
|
|
128
76
|
* provider: it resolves the invocation's origin without choosing by availability.
|
|
@@ -12,7 +12,7 @@ import { isSubstantive } from "../extractors/languages.js";
|
|
|
12
12
|
const SKIP_SUBJECT = /^(merge|revert|bump|format|lint|wip)\b|^chore\(deps\):/i;
|
|
13
13
|
// Below this many changed code lines, a commit with no structural change and no
|
|
14
14
|
// explanatory body isn't worth a paid LLM call. Tunable via HUNCH_SIG_MIN_LINES.
|
|
15
|
-
const SIG_MIN_LINES = Number(process.env.HUNCH_SIG_MIN_LINES)
|
|
15
|
+
const SIG_MIN_LINES = ((raw) => Number.isFinite(raw) && raw > 0 ? raw : 12)(Number(process.env.HUNCH_SIG_MIN_LINES));
|
|
16
16
|
const SIG_MIN_BODY = 40;
|
|
17
17
|
/** Trivial-subject commits (merge/revert/bump/format/...) are noise UNLESS the body
|
|
18
18
|
* carries real content — a squash/PR description often lands there, not on the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davesheffer/hunch",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.46.0",
|
|
4
4
|
"mcpName": "io.github.davesheffer/hunch",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Dave Sheffer <dave.sheffer1@gmail.com>",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"tree-sitter-php": "0.23.12",
|
|
104
104
|
"tree-sitter-python": "0.23.4",
|
|
105
105
|
"tree-sitter-typescript": "^0.23.2",
|
|
106
|
-
"zod": "^4.
|
|
106
|
+
"zod": "^4.6.6"
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
109
|
"@types/cross-spawn": "^6.0.6",
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://www.hunchmemory.com",
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.46.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@davesheffer/hunch",
|
|
16
|
-
"version": "1.
|
|
16
|
+
"version": "1.46.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"packageArguments": [
|
|
19
19
|
{
|