@davesheffer/hunch 1.47.1 → 1.47.2
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 +17 -9
- package/dist/core/correction.js +4 -3
- package/dist/core/subagentrole.d.ts +6 -0
- package/dist/core/subagentrole.js +32 -0
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -23,7 +23,8 @@ import { Command } from "commander";
|
|
|
23
23
|
import { hunchPaths, hunchPathsForDir, findRoot, toPosixTarget, isDir, realpathNorm, repoRelativeTarget } from "../core/paths.js";
|
|
24
24
|
import { writeFileAtomic } from "../core/io.js";
|
|
25
25
|
import { boundedSpawnSync } from "../core/boundedSpawn.js";
|
|
26
|
-
import { looksLikeCorrection, CORRECTION_NUDGE } from "../core/correction.js";
|
|
26
|
+
import { looksLikeCorrection, CORRECTION_NUDGE, stripHostBlocks } from "../core/correction.js";
|
|
27
|
+
import { roleHasHunchTools } from "../core/subagentrole.js";
|
|
27
28
|
import { HUNCH_VERSION } from "../core/version.js";
|
|
28
29
|
import { registerIntegrationCommands } from "./integrations.js";
|
|
29
30
|
import { registerTaskReportCommands } from "./taskReport.js";
|
|
@@ -5456,9 +5457,11 @@ program
|
|
|
5456
5457
|
return flat.length > max ? `${flat.slice(0, max - 1).trimEnd()}…` : flat;
|
|
5457
5458
|
};
|
|
5458
5459
|
const type = (evt.agent_type ?? "").toLowerCase();
|
|
5460
|
+
// A role whose tool list leaves out Hunch's MCP tools is not told to call them.
|
|
5461
|
+
const tools = roleHasHunchTools(root, evt.agent_type ?? "");
|
|
5459
5462
|
const L = [];
|
|
5460
5463
|
const served = [];
|
|
5461
|
-
const route = routedCwd
|
|
5464
|
+
const route = routedCwd && tools
|
|
5462
5465
|
? `Worktree routing: call hunch_context first with cwd: ${JSON.stringify(routedCwd)}, and pass the same cwd to hunch_task, hunch_report, and every Hunch capture/write call in this delegated task.`
|
|
5463
5466
|
: null;
|
|
5464
5467
|
const activeProvider = provider;
|
|
@@ -5478,8 +5481,9 @@ program
|
|
|
5478
5481
|
served.push({ kind: "components", record_id: c.id, token_cost: Math.ceil([...line].length / 4) });
|
|
5479
5482
|
}
|
|
5480
5483
|
if (components.length > 12)
|
|
5481
|
-
L.push(`…and ${components.length - 12} more — hunch_structure() for the full map.`);
|
|
5482
|
-
|
|
5484
|
+
L.push(`…and ${components.length - 12} more${tools ? " — hunch_structure() for the full map" : ""}.`);
|
|
5485
|
+
if (tools)
|
|
5486
|
+
L.push("Orient: hunch_structure(target) · hunch_why(target) · hunch_context(task).");
|
|
5483
5487
|
}
|
|
5484
5488
|
else if (/plan|architect|design/.test(type)) {
|
|
5485
5489
|
// A plan drafted blind re-proposes what the graph already rejected.
|
|
@@ -5496,7 +5500,8 @@ program
|
|
|
5496
5500
|
L.push(line);
|
|
5497
5501
|
served.push({ kind: "decisions", record_id: d.id, token_cost: Math.ceil([...line].length / 4) });
|
|
5498
5502
|
}
|
|
5499
|
-
|
|
5503
|
+
if (tools)
|
|
5504
|
+
L.push("Before finalizing a plan: hunch_why(target) · hunch_current_decision(topic) · hunch_check_constraints(scope).");
|
|
5500
5505
|
}
|
|
5501
5506
|
else {
|
|
5502
5507
|
const sevRank = { blocking: 0, warning: 1, advisory: 2 };
|
|
@@ -5514,8 +5519,9 @@ program
|
|
|
5514
5519
|
served.push({ kind: "constraints", record_id: c.id, token_cost: Math.ceil([...line].length / 4) });
|
|
5515
5520
|
}
|
|
5516
5521
|
if (constraints.length > 8)
|
|
5517
|
-
L.push(`…and ${constraints.length - 8} more — hunch_check_constraints(scope) for your files.`);
|
|
5518
|
-
|
|
5522
|
+
L.push(`…and ${constraints.length - 8} more${tools ? " — hunch_check_constraints(scope) for your files" : "; the edit hook shows the ones your files touch"}.`);
|
|
5523
|
+
if (tools)
|
|
5524
|
+
L.push("Before editing: hunch_check_constraints(scope) · hunch_why(target). Orient: hunch_context(task).");
|
|
5519
5525
|
}
|
|
5520
5526
|
if (route)
|
|
5521
5527
|
L.push(route);
|
|
@@ -7768,19 +7774,21 @@ function promptTaskSelection(root, store, provider, evt) {
|
|
|
7768
7774
|
const taskId = hookReportTaskId(root, provider, evt);
|
|
7769
7775
|
if (!taskId)
|
|
7770
7776
|
return "";
|
|
7777
|
+
// Host-injected blocks (reminders, browser instructions) are not the task the human named.
|
|
7778
|
+
const humanText = stripHostBlocks(evt.prompt ?? "");
|
|
7771
7779
|
const selection = selectForTask(liveSelectionRecords({
|
|
7772
7780
|
decisions: store.recs("decisions"),
|
|
7773
7781
|
bugs: store.recs("bugs"),
|
|
7774
7782
|
constraints: store.recs("constraints"),
|
|
7775
7783
|
findings: store.recs("findings"),
|
|
7776
|
-
}),
|
|
7784
|
+
}), humanText, { root, pathExists: (p) => existsSync(join(root, p)) });
|
|
7777
7785
|
// Same task id again (a notification alias) keeps its own earlier selection; a
|
|
7778
7786
|
// new task that CONTINUES the session's previous one inherits that task's
|
|
7779
7787
|
// selection only on a bare follow-up ("continue", "go on"), so it is not left
|
|
7780
7788
|
// ungrounded. A substantive prompt names its own task: a same-session prompt
|
|
7781
7789
|
// inside the continuation window must not hide memory behind an old selection.
|
|
7782
7790
|
let previous = loadTaskSelection(taskId);
|
|
7783
|
-
if (!previous && isBareFollowUp(
|
|
7791
|
+
if (!previous && isBareFollowUp(humanText)) {
|
|
7784
7792
|
try {
|
|
7785
7793
|
const continues = readTaskReport(root, taskId).task.continues;
|
|
7786
7794
|
previous = continues ? loadTaskSelection(continues) : null;
|
package/dist/core/correction.js
CHANGED
|
@@ -30,11 +30,12 @@ const CORRECTION_PATTERNS = [
|
|
|
30
30
|
/\b(not like that|don'?t do (that|this)( again)?|stop doing (that|this))\b/i,
|
|
31
31
|
];
|
|
32
32
|
/** Host-generated blocks a prompt can carry: a background task's completion
|
|
33
|
-
* (`<task-notification>`, which also wraps a subagent's hand-back report)
|
|
34
|
-
* injected context (`<system-reminder>`, hook output)
|
|
33
|
+
* (`<task-notification>`, which also wraps a subagent's hand-back report),
|
|
34
|
+
* injected context (`<system-reminder>`, hook output) and the browser
|
|
35
|
+
* extension's standing instructions (`<browser_instruction>`). Their words are not the
|
|
35
36
|
* human's, so "never use X" quoted from a constraint is not a correction. A tag
|
|
36
37
|
* left unclosed drops only the rest of its own line, never the human's text below. */
|
|
37
|
-
const HOST_TAGS = "task-notification|system-reminder|local-command-stdout|local-command-stderr|command-message|command-name|command-args";
|
|
38
|
+
const HOST_TAGS = "task-notification|system-reminder|local-command-stdout|local-command-stderr|command-message|command-name|command-args|browser_instruction";
|
|
38
39
|
const HOST_BLOCK = new RegExp(`<(${HOST_TAGS})>[\\s\\S]*?<\\/\\1>`, "gi");
|
|
39
40
|
const HOST_OPEN_LINE = new RegExp(`<(?:${HOST_TAGS})>[^\\n]*`, "gi");
|
|
40
41
|
/** The prompt with host-generated blocks removed: what the human actually typed. */
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Whether a delegated agent can call Hunch's MCP tools. A named role defined in
|
|
2
|
+
* `.claude/agents/<type>.md` (project first, then user) with a `tools:` list that
|
|
3
|
+
* names no `mcp__hunch` tool and no wildcard cannot: telling it to call
|
|
4
|
+
* hunch_why only costs it a failed lookup. Built-in and unknown types inherit
|
|
5
|
+
* the parent's tools. */
|
|
6
|
+
export declare function roleHasHunchTools(root: string, agentType: string, home?: string): boolean;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
/** Whether a delegated agent can call Hunch's MCP tools. A named role defined in
|
|
5
|
+
* `.claude/agents/<type>.md` (project first, then user) with a `tools:` list that
|
|
6
|
+
* names no `mcp__hunch` tool and no wildcard cannot: telling it to call
|
|
7
|
+
* hunch_why only costs it a failed lookup. Built-in and unknown types inherit
|
|
8
|
+
* the parent's tools. */
|
|
9
|
+
export function roleHasHunchTools(root, agentType, home = homedir()) {
|
|
10
|
+
const type = agentType.trim();
|
|
11
|
+
if (!type || !/^[\w.-]+$/.test(type))
|
|
12
|
+
return true;
|
|
13
|
+
for (const dir of [join(root, ".claude", "agents"), join(home, ".claude", "agents")]) {
|
|
14
|
+
let text;
|
|
15
|
+
try {
|
|
16
|
+
text = readFileSync(join(dir, `${type}.md`), "utf8");
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const front = /^?---\r?\n([\s\S]*?)\r?\n---/.exec(text)?.[1];
|
|
22
|
+
if (!front)
|
|
23
|
+
return true;
|
|
24
|
+
// A block list continues on indented "- x" lines; a flow list runs to its "]".
|
|
25
|
+
const tools = /^tools:[ \t]*(\[[^\]]*\]?|.*(?:\r?\n[ \t]+-.*)*)/m.exec(front)?.[1];
|
|
26
|
+
if (tools === undefined || !tools.trim())
|
|
27
|
+
return true;
|
|
28
|
+
return /\*|mcp__hunch/.test(tools);
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=subagentrole.js.map
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://www.hunchmemory.com",
|
|
10
|
-
"version": "1.47.
|
|
10
|
+
"version": "1.47.2",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@davesheffer/hunch",
|
|
16
|
-
"version": "1.47.
|
|
16
|
+
"version": "1.47.2",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"packageArguments": [
|
|
19
19
|
{
|