@lovrozagar/grunt 0.3.5 → 0.3.6
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/.agents/agents/orchestrator.md +6 -2
- package/.agents/hooks.json +10 -0
- package/.claude/agents/orchestrator.md +6 -2
- package/.claude/settings.json +10 -0
- package/.codex/agents/orchestrator.toml +6 -2
- package/.codex/hooks.json +10 -0
- package/.grok/agents/orchestrator.md +6 -2
- package/.grok/hooks/orchestrate-parent.js +71 -17
- package/.rulesync/hooks.jsonc +6 -3
- package/.rulesync/reference/cascade.md +2 -1
- package/.rulesync/reference/hooks.md +3 -1
- package/.rulesync/reference/output.md +1 -1
- package/.rulesync/rules/CLAUDE.md +6 -2
- package/.rulesync/rules/overview.md +6 -2
- package/.rulesync/subagents/orchestrator.md +6 -2
- package/AGENTS.md +6 -2
- package/CLAUDE.md +6 -2
- package/package.json +1 -1
- package/scripts/gate-fat-tools.mjs +58 -8
- package/scripts/scrub-spawn-prompt.mjs +29 -7
|
@@ -12,11 +12,14 @@ commandExecutionPolicy: eager
|
|
|
12
12
|
inheritMcp: false
|
|
13
13
|
---
|
|
14
14
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
15
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + **one-line** echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
15
16
|
Protocol: `.rulesync/reference/cascade.md` (peek/kill table need:/resume). Do not paste. Do not open first.
|
|
16
17
|
Always-do:
|
|
17
18
|
|
|
18
19
|
| signal | next |
|
|
19
20
|
|---|---|
|
|
21
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
22
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
20
23
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
21
24
|
| world fact | spawn grunt `job:web` |
|
|
22
25
|
| write defined solution | spawn implementer |
|
|
@@ -25,11 +28,12 @@ Always-do:
|
|
|
25
28
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
26
29
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
27
30
|
| work remains | spawn; do not stop |
|
|
28
|
-
| children done + no findings | recap; stop allowed |
|
|
31
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
29
32
|
| `/parent` | one-turn parent tools |
|
|
30
33
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
34
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
31
35
|
|
|
32
|
-
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
36
|
+
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + **one-line** echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
33
37
|
Spawn only `grunt` | `implementer` | `thinker`. Omit `model`. Isolation `none` unless asked. Never spawn `orchestrator`. Children never spawn.
|
|
34
38
|
grunt ← tools. World fact → grunt `job: web`. Never memory. implementer ← specified solution (not the spec). thinker ← plan/deep reason/spec.
|
|
35
39
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
package/.agents/hooks.json
CHANGED
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
"timeout": 5
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"matcher": "Write|Edit|write|search_replace|Bash|run_terminal_command|Skill|skill|replace_file_content|run_command",
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "node \"${GROK_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.grok/hooks/orchestrate-parent.js\"",
|
|
30
|
+
"timeout": 5
|
|
31
|
+
}
|
|
32
|
+
]
|
|
23
33
|
}
|
|
24
34
|
],
|
|
25
35
|
"Stop": [
|
|
@@ -15,11 +15,14 @@ permissionMode: bypassPermissions
|
|
|
15
15
|
effort: low
|
|
16
16
|
---
|
|
17
17
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
18
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + **one-line** echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
18
19
|
Protocol: `.rulesync/reference/cascade.md` (peek/kill table need:/resume). Do not paste. Do not open first.
|
|
19
20
|
Always-do:
|
|
20
21
|
|
|
21
22
|
| signal | next |
|
|
22
23
|
|---|---|
|
|
24
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
25
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
23
26
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
24
27
|
| world fact | spawn grunt `job:web` |
|
|
25
28
|
| write defined solution | spawn implementer |
|
|
@@ -28,11 +31,12 @@ Always-do:
|
|
|
28
31
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
29
32
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
30
33
|
| work remains | spawn; do not stop |
|
|
31
|
-
| children done + no findings | recap; stop allowed |
|
|
34
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
32
35
|
| `/parent` | one-turn parent tools |
|
|
33
36
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
37
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
34
38
|
|
|
35
|
-
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
39
|
+
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + **one-line** echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
36
40
|
Spawn only `grunt` | `implementer` | `thinker`. Omit `model`. Isolation `none` unless asked. Never spawn `orchestrator`. Children never spawn.
|
|
37
41
|
grunt ← tools. World fact → grunt `job: web`. Never memory. implementer ← specified solution (not the spec). thinker ← plan/deep reason/spec.
|
|
38
42
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
package/.claude/settings.json
CHANGED
|
@@ -29,6 +29,16 @@
|
|
|
29
29
|
"timeout": 5
|
|
30
30
|
}
|
|
31
31
|
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"matcher": "Write|Edit|write|search_replace|Bash|run_terminal_command|Skill|skill|replace_file_content|run_command",
|
|
35
|
+
"hooks": [
|
|
36
|
+
{
|
|
37
|
+
"type": "command",
|
|
38
|
+
"command": "node \"${GROK_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.grok/hooks/orchestrate-parent.js\"",
|
|
39
|
+
"timeout": 5
|
|
40
|
+
}
|
|
41
|
+
]
|
|
32
42
|
}
|
|
33
43
|
],
|
|
34
44
|
"UserPromptSubmit": [
|
|
@@ -6,11 +6,14 @@ sandbox_mode = "danger-full-access"
|
|
|
6
6
|
approval_policy = "never"
|
|
7
7
|
developer_instructions = '''
|
|
8
8
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
9
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + **one-line** echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
9
10
|
Protocol: `.rulesync/reference/cascade.md` (peek/kill table need:/resume). Do not paste. Do not open first.
|
|
10
11
|
Always-do:
|
|
11
12
|
|
|
12
13
|
| signal | next |
|
|
13
14
|
|---|---|
|
|
15
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
16
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
14
17
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
15
18
|
| world fact | spawn grunt `job:web` |
|
|
16
19
|
| write defined solution | spawn implementer |
|
|
@@ -19,11 +22,12 @@ Always-do:
|
|
|
19
22
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
20
23
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
21
24
|
| work remains | spawn; do not stop |
|
|
22
|
-
| children done + no findings | recap; stop allowed |
|
|
25
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
23
26
|
| `/parent` | one-turn parent tools |
|
|
24
27
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
28
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
25
29
|
|
|
26
|
-
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
30
|
+
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + **one-line** echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
27
31
|
Spawn only `grunt` | `implementer` | `thinker`. Omit `model`. Isolation `none` unless asked. Never spawn `orchestrator`. Children never spawn.
|
|
28
32
|
grunt ← tools. World fact → grunt `job: web`. Never memory. implementer ← specified solution (not the spec). thinker ← plan/deep reason/spec.
|
|
29
33
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
package/.codex/hooks.json
CHANGED
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
"timeout": 5
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"matcher": "Write|Edit|write|search_replace|Bash|run_terminal_command|Skill|skill|replace_file_content|run_command",
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "node \"${GROK_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.grok/hooks/orchestrate-parent.js\"",
|
|
30
|
+
"timeout": 5
|
|
31
|
+
}
|
|
32
|
+
]
|
|
23
33
|
}
|
|
24
34
|
],
|
|
25
35
|
"UserPromptSubmit": [
|
|
@@ -8,11 +8,14 @@ mcpInheritance: none
|
|
|
8
8
|
tools: spawn_subagent, read_file, grep, list_dir, write, todo_write, get_command_or_subagent_output, kill_command_or_subagent, run_terminal_command
|
|
9
9
|
---
|
|
10
10
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
11
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + **one-line** echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
11
12
|
Protocol: `.rulesync/reference/cascade.md` (peek/kill table need:/resume). Do not paste. Do not open first.
|
|
12
13
|
Always-do:
|
|
13
14
|
|
|
14
15
|
| signal | next |
|
|
15
16
|
|---|---|
|
|
17
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
18
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
16
19
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
17
20
|
| world fact | spawn grunt `job:web` |
|
|
18
21
|
| write defined solution | spawn implementer |
|
|
@@ -21,11 +24,12 @@ Always-do:
|
|
|
21
24
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
22
25
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
23
26
|
| work remains | spawn; do not stop |
|
|
24
|
-
| children done + no findings | recap; stop allowed |
|
|
27
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
25
28
|
| `/parent` | one-turn parent tools |
|
|
26
29
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
30
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
27
31
|
|
|
28
|
-
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
32
|
+
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + **one-line** echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
29
33
|
Spawn only `grunt` | `implementer` | `thinker`. Omit `model`. Isolation `none` unless asked. Never spawn `orchestrator`. Children never spawn.
|
|
30
34
|
grunt ← tools. World fact → grunt `job: web`. Never memory. implementer ← specified solution (not the spec). thinker ← plan/deep reason/spec.
|
|
31
35
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
2
|
+
/** PreToolUse parent-deny on Claude+Grok+Antigravity; Stop = first-non-empty-line recap; stop-block survives host banners; SubagentStop is single-registration intercept.
|
|
3
3
|
Parent Read/Grep/Glob/Bash/Web denied unless parent-escape (fat-gate still).
|
|
4
4
|
SubagentStop intercepts need: search|exec with grunt-job verdicts.
|
|
5
5
|
Stop: first-non-empty-line [orchestrator]:/[grunt]:/[implementer]:/[thinker]:/[handoff]: recap
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
fatHookOutput,
|
|
20
20
|
fileSizeBytes,
|
|
21
21
|
isWorkspaceGruntJob,
|
|
22
|
+
isParentOrchestrator,
|
|
22
23
|
processFatTools,
|
|
23
24
|
resolveReadPath,
|
|
24
25
|
rewriteGruntScratchPath,
|
|
@@ -30,20 +31,11 @@ import { parseNeed } from "../../scripts/parse-need.mjs";
|
|
|
30
31
|
import { resolveJobCwd, runJob } from "../../scripts/grunt-job.mjs";
|
|
31
32
|
import { logTelemetry, ORCHESTRATOR_LOGS_DIR } from "../../scripts/telemetry.mjs";
|
|
32
33
|
|
|
33
|
-
const DENY_REASON = "
|
|
34
|
+
export const DENY_REASON = "spawn implementer|grunt|thinker";
|
|
34
35
|
export const STOP_REASONS = [
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"Else all children returned and no findings → recap `[orchestrator]:` echo.",
|
|
39
|
-
"Second violation: still no exclusive next action. XOR one. DO NOT stop.\n" +
|
|
40
|
-
"⚠/validate/sim findings → spawn implementer with them; do not recap; spawn; no parent-edit.\n" +
|
|
41
|
-
"Else work remains → spawn grunt|implementer|thinker; do not glue done into the tag.\n" +
|
|
42
|
-
"Else all children returned and no findings → recap `[orchestrator]:` echo.",
|
|
43
|
-
"Third violation: last check before fail-open. XOR one action. DO NOT stop.\n" +
|
|
44
|
-
"⚠/validate/sim findings → spawn implementer with them; do not recap; spawn; no parent-edit.\n" +
|
|
45
|
-
"Else work remains → spawn grunt|implementer|thinker; do not glue done into the tag.\n" +
|
|
46
|
-
"Else all children returned and no findings → recap `[orchestrator]:` echo.",
|
|
36
|
+
"Spawn: ⚠/validate/sim findings or writes remain or parent just tried to Write → spawn implementer. Else facts → grunt. Else no spec → thinker. Else recap `[orchestrator]:` one-line.",
|
|
37
|
+
"Still spawn: ⚠/validate/sim findings or writes remain or parent just tried to Write → spawn implementer. Else facts → grunt. Else no spec → thinker. Else recap `[orchestrator]:` one-line.",
|
|
38
|
+
"Last spawn: ⚠/validate/sim findings or writes remain or parent just tried to Write → spawn implementer. Else facts → grunt. Else no spec → thinker. Else recap `[orchestrator]:` one-line.",
|
|
47
39
|
];
|
|
48
40
|
const TRANSCRIPT_TAIL_BYTES = 512 * 1024;
|
|
49
41
|
const PARENT_TOOLS = new Set([
|
|
@@ -59,7 +51,23 @@ const SPAWN_TOOLS = new Set([
|
|
|
59
51
|
"agent",
|
|
60
52
|
"spawnagent",
|
|
61
53
|
]);
|
|
62
|
-
const WRITE_TOOLS = new Set([
|
|
54
|
+
const WRITE_TOOLS = new Set([
|
|
55
|
+
"write",
|
|
56
|
+
"edit",
|
|
57
|
+
"searchreplace",
|
|
58
|
+
"replacefilecontent",
|
|
59
|
+
]);
|
|
60
|
+
const BASH_TOOLS = new Set(["bash", "runterminalcommand", "runcommand"]);
|
|
61
|
+
const SKILL_TOOLS = new Set(["skill"]);
|
|
62
|
+
const PARENT_SKILLS = new Set([
|
|
63
|
+
"parent",
|
|
64
|
+
"explain",
|
|
65
|
+
"solo",
|
|
66
|
+
"cascade",
|
|
67
|
+
"handoff",
|
|
68
|
+
"write-plan",
|
|
69
|
+
"implement-plan",
|
|
70
|
+
]);
|
|
63
71
|
const READ_TOOLS = new Set(["readfile", "read"]);
|
|
64
72
|
const INTERCEPT_JOBS = new Set(["search", "exec"]);
|
|
65
73
|
const MAX_STOP = 3;
|
|
@@ -97,7 +105,7 @@ function preToolUse(data) {
|
|
|
97
105
|
logPreTool(data, toolKey, toolInput);
|
|
98
106
|
|
|
99
107
|
const sub = subagentTypeOf(data);
|
|
100
|
-
if (sub) {
|
|
108
|
+
if (sub || !isParentOrchestrator(data)) {
|
|
101
109
|
const code = emitFat(data);
|
|
102
110
|
return code == null ? 0 : code;
|
|
103
111
|
}
|
|
@@ -130,6 +138,14 @@ function preToolUse(data) {
|
|
|
130
138
|
emit({ decision: "allow" });
|
|
131
139
|
return 0;
|
|
132
140
|
}
|
|
141
|
+
if (BASH_TOOLS.has(toolKey) && isAllowedParentGruntJob(bashCommandOf(toolInput), workspaceRootOf(data))) {
|
|
142
|
+
emit({ decision: "allow" });
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
if (SKILL_TOOLS.has(toolKey) && isAllowedParentSkill(toolInput)) {
|
|
146
|
+
emit({ decision: "allow" });
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
133
149
|
if (hasParentEscape(data)) {
|
|
134
150
|
const fatCode = emitFat(data);
|
|
135
151
|
if (fatCode !== null) return fatCode;
|
|
@@ -219,6 +235,40 @@ export function isAllowedParentGruntJob(command, workspaceRoot) {
|
|
|
219
235
|
return isWorkspaceGruntJob(command, workspaceRoot, ["search", "exec"]);
|
|
220
236
|
}
|
|
221
237
|
|
|
238
|
+
function bashCommandOf(toolInput) {
|
|
239
|
+
if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) {
|
|
240
|
+
return "";
|
|
241
|
+
}
|
|
242
|
+
return String(toolInput.command ?? toolInput.cmd ?? "");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function skillNameOf(toolInput) {
|
|
246
|
+
if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) {
|
|
247
|
+
return "";
|
|
248
|
+
}
|
|
249
|
+
const raw =
|
|
250
|
+
toolInput.skill ??
|
|
251
|
+
toolInput.skill_name ??
|
|
252
|
+
toolInput.skillName ??
|
|
253
|
+
toolInput.name ??
|
|
254
|
+
"";
|
|
255
|
+
const s = String(raw).trim().toLowerCase().replace(/\\/g, "/");
|
|
256
|
+
const base = s.split("/").pop() || "";
|
|
257
|
+
return base.replace(/\.md$/, "").replace(/_/g, "-");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function isAllowedParentSkill(toolInput) {
|
|
261
|
+
return PARENT_SKILLS.has(skillNameOf(toolInput));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function isHostStopBanner(prompt) {
|
|
265
|
+
const p = String(prompt || "");
|
|
266
|
+
if (/^\s*Stop hook feedback:/.test(p)) return true;
|
|
267
|
+
if (/Blocked by stop hook/.test(p)) return true;
|
|
268
|
+
if (/task[-_ ]?notification/i.test(p)) return true;
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
222
272
|
function parentWrite(data, toolInput) {
|
|
223
273
|
if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) {
|
|
224
274
|
emit({ decision: "deny", reason: DENY_REASON });
|
|
@@ -235,6 +285,10 @@ function parentWrite(data, toolInput) {
|
|
|
235
285
|
if (rewritten) rawPath = rewritten;
|
|
236
286
|
const handoff = isUnderHandoffs(rawPath, ws);
|
|
237
287
|
if (!handoff && !isUnderPlans(rawPath, ws)) {
|
|
288
|
+
if (hasParentEscape(data)) {
|
|
289
|
+
emit({ decision: "allow" });
|
|
290
|
+
return 0;
|
|
291
|
+
}
|
|
238
292
|
emit({ decision: "deny", reason: DENY_REASON });
|
|
239
293
|
return 0;
|
|
240
294
|
}
|
|
@@ -298,7 +352,7 @@ function isParentEscapePrompt(prompt) {
|
|
|
298
352
|
function userPromptSubmit(data) {
|
|
299
353
|
unlinkQuiet(stampPath(data, "tools-used"));
|
|
300
354
|
const prompt = userPromptOf(data);
|
|
301
|
-
if (
|
|
355
|
+
if (!isHostStopBanner(prompt)) {
|
|
302
356
|
unlinkQuiet(stampPath(data, "stop-block"));
|
|
303
357
|
}
|
|
304
358
|
// Sticky: only /solo and /cascade move it. Every other prompt leaves it alone.
|
package/.rulesync/hooks.jsonc
CHANGED
|
@@ -12,15 +12,18 @@
|
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
// Fat Read/Grep/Glob/Bash + Write/Edit scratch rewrite (never deny writes).
|
|
15
|
-
// Parent-deny stays Grok-only (orchestrate-parent.js).
|
|
16
15
|
"matcher": "Read|read_file|Grep|grep|Glob|list_dir|Bash|run_terminal_command|Write|Edit|write|search_replace",
|
|
17
16
|
"type": "command",
|
|
18
17
|
"command": "node \"${GROK_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/scripts/gate-fat-tools.mjs\"",
|
|
19
18
|
"timeout": 5
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"matcher": "Write|Edit|write|search_replace|Bash|run_terminal_command|Skill|skill|replace_file_content|run_command",
|
|
22
|
+
"type": "command",
|
|
23
|
+
"command": "node \"${GROK_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/.grok/hooks/orchestrate-parent.js\"",
|
|
24
|
+
"timeout": 5
|
|
20
25
|
}
|
|
21
26
|
],
|
|
22
|
-
// Parent Stop + /parent stamp. PreToolUse parent-deny stays Grok-only
|
|
23
|
-
// (child subagentType/agent_type unverified on these hosts).
|
|
24
27
|
"beforeSubmitPrompt": [
|
|
25
28
|
{
|
|
26
29
|
"type": "command",
|
|
@@ -13,6 +13,7 @@ Always-do lives in orchestrator/overview (do not open this file first). Peek/kil
|
|
|
13
13
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
|
14
14
|
|
|
15
15
|
Always spawn + prompt. First token = spawn. No try-then-spawn. No parent probe. No tiny-read/grep/list_dir/bash/grunt-job for the user. No skip-spawn. `/parent` is the only parent-tool escape.
|
|
16
|
+
Create/change product files → first token spawn implementer (thinker then implementer if no spec). Never parent Write/Edit/Bash/Skill-that-writes. Child returned is not terminal if writes remain or spawn count is 0 on a file-change turn. plan/spec ready + writes remain → persist plan if needed; spawn implementer; do not recap-stop. plan-only `/write-plan` → recap + `next: /implement-plan`.
|
|
16
17
|
|
|
17
18
|
1. Tools (facts / search / exec / git / web / test / low-reason mechanical write) → grunt. Facts/search → grunt, never thinker.
|
|
18
19
|
2. Write defined solution → implementer.
|
|
@@ -34,7 +35,7 @@ Classify `done` | `alive` | `stuck` vs prior peek: **done** (completed/failed),
|
|
|
34
35
|
Loop until done. No auto-kill. Kill only if the user asks and the host has a kill tool.
|
|
35
36
|
Then print recap as `[orchestrator]: …` (or the child role tag) with real child output. Do not redo the child's work.
|
|
36
37
|
|
|
37
|
-
Parent Stop (`MAX_STOP=3`): do not waive fenced/impl finals for `tools-used` stamp. First non-empty line must match legal `[orchestrator|grunt|implementer|thinker|handoff]:`. Siblings still run → role tag + echo. In-flight wait recap is `[orchestrator]: wait grunt` only. Or consume `parent-escape-{sid}` once. Else block. No `isCheap` / trivia / long definitions.
|
|
38
|
+
Parent Stop (`MAX_STOP=3`): do not waive fenced/impl finals for `tools-used` stamp. Parent `end_turn` is tagged+one-line or `[orchestrator]: wait grunt`. First non-empty line must match legal `[orchestrator|grunt|implementer|thinker|handoff]:`. Siblings still run → role tag + one-line echo. In-flight wait recap is `[orchestrator]: wait grunt` only. Or consume `parent-escape-{sid}` once. Else block. No `isCheap` / trivia / long definitions.
|
|
38
39
|
`/handoff` is the other in-parent turn: context large + work unfinished → parent writes one `.tmp/grunt/handoffs/` file from its own transcript (no child sees the session), recaps `[handoff]: serial=… path=…`, tells the user to continue in a new session. No spawn, one write, not a mode.
|
|
39
40
|
`/solo` is the session mode (not one-turn): `beforeSubmitPrompt` writes `.tmp/orchestrator-logs/grunt-off-{sid}`, every gate short-circuits while it exists, `/cascade` unlinks it. Everything above resumes next turn. Antigravity: skill-only, no stamp create (see hooks.md). `/parent` is one-turn (not a mode): UserPromptSubmit writes `.tmp/orchestrator-logs/parent-escape-{sid}`; Stop consumes+unlinks once. Next Stop resumes spawn/recap rules. Skills point here; do not paste this file.
|
|
40
41
|
|
|
@@ -10,9 +10,11 @@ Grok PostToolUse / UserPromptSubmit / SessionStart are observe-only — do not a
|
|
|
10
10
|
|
|
11
11
|
Claude / Codex / Antigravity PreToolUse still run `scripts/scrub-spawn-prompt.mjs` and `scripts/gate-fat-tools.mjs`. Grok does not — spawn/fat rewrite is `orchestrate-parent.js` only. RTK stays `rtk.json`.
|
|
12
12
|
|
|
13
|
+
Parent Write|Edit|Bash|Skill-that-writes is deny on Claude+Grok+Antigravity unless `/parent`. `.tmp/plans/` and `.tmp/grunt/handoffs/` Write still persist. Parent Bash still allowed for workspace `grunt-job.mjs` `--job search|exec`. Children with a type skip parent-deny (fat-only). Skill allowlist without escape: `parent|explain|solo|cascade|handoff|write-plan|implement-plan`. Deny reason: `spawn implementer|grunt|thinker`. Parent `end_turn` is tagged+one-line or `[orchestrator]: wait grunt`.
|
|
14
|
+
|
|
13
15
|
Parent `run_terminal_command` is deny-by-default except `node` (optional leading `rtk`) invoking `{workspace}/scripts/grunt-job.mjs` with `--job search|exec` and `--query`. Optional `--path` `--glob` (repeatable) `--cwd`. Not `web|test`, not other scripts. META strips `--query` value first so `|` inside query is allowed; pipes/`&&` outside query still deny. Never `cd &&`. Exec shell-meta in query is grunt-job FALLBACK (exit 2), not a parent-bash allow. Unknown flags → FALLBACK.
|
|
14
16
|
|
|
15
|
-
SubagentStop (and child Stop with `subagentType`) intercepts a parseable `need:` whose jobs are all `search|exec` (cap 4): runs grunt-job in-hook and continues the child with concatenated `verdict:` blobs. Mixed/web/test, parse fail, FALLBACK, `stopHookActive`, or more than 3 intercepts per child session → empty stdout (allow stop; parent LLM path). Parent `write` is deny-by-default except `.tmp/plans/` (`persist-plan.mjs`) and `.tmp/grunt/handoffs/` (`persist-handoff.mjs`); both validate + rewrite path/content, invalid body denies. Parent Stop (`MAX_STOP=3`): do not waive impl finals for `tools-used`. First non-empty line must match legal `[orchestrator|grunt|implementer|thinker|handoff]:`. Claude Agent launch ≠ done; in-flight host Stop → wait recap `[orchestrator]: wait grunt` only; no SendMessage. Or consume `parent-escape-{sid}` once (`/parent`). No `isCheap` / trivia. Stamps: `tools-used` / `stop-block` / `need-intercept` / `parent-escape`. Skills point to cascade; do not paste protocol here.
|
|
17
|
+
SubagentStop (and child Stop with `subagentType`) intercepts a parseable `need:` whose jobs are all `search|exec` (cap 4): runs grunt-job in-hook and continues the child with concatenated `verdict:` blobs. Mixed/web/test, parse fail, FALLBACK, `stopHookActive`, or more than 3 intercepts per child session → empty stdout (allow stop; parent LLM path). Parent `write` is deny-by-default except `.tmp/plans/` (`persist-plan.mjs`) and `.tmp/grunt/handoffs/` (`persist-handoff.mjs`); both validate + rewrite path/content, invalid body denies. Parent Stop (`MAX_STOP=3`): do not waive impl finals for `tools-used`. Parent `end_turn` is tagged+one-line or `[orchestrator]: wait grunt`. First non-empty line must match legal `[orchestrator|grunt|implementer|thinker|handoff]:`. Claude Agent launch ≠ done; in-flight host Stop → wait recap `[orchestrator]: wait grunt` only; no SendMessage. Or consume `parent-escape-{sid}` once (`/parent`). No `isCheap` / trivia. Stamps: `tools-used` / `stop-block` / `need-intercept` / `parent-escape`. Skills point to cascade; do not paste protocol here.
|
|
16
18
|
|
|
17
19
|
`/solo` writes `grunt-off-{sid}`; `/cascade` unlinks it (`beforeSubmitPrompt`, exact-match only). While set: PreToolUse allows parent tools (fat gate still runs) and skips the `defaultGrunt` spawn rewrite; Stop returns 0 **before** the `parent-escape` consume, so solo never burns the one-turn stamp. Session-scoped, fail-closed.
|
|
18
20
|
|
|
@@ -4,4 +4,4 @@ tags: [output]
|
|
|
4
4
|
|
|
5
5
|
# Output
|
|
6
6
|
|
|
7
|
-
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only.
|
|
7
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + one-line echo only
|
|
@@ -4,12 +4,15 @@ targets:
|
|
|
4
4
|
- claudecode
|
|
5
5
|
---
|
|
6
6
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
7
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + one-line echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
7
8
|
All output exclude AI attribution, Co-Authored-By, trailers, generated markers.
|
|
8
9
|
Protocol: `.rulesync/reference/cascade.md`. Do not paste. Do not open first.
|
|
9
10
|
Always-do:
|
|
10
11
|
|
|
11
12
|
| signal | next |
|
|
12
13
|
|---|---|
|
|
14
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
15
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
13
16
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
14
17
|
| world fact | spawn grunt `job:web` |
|
|
15
18
|
| write defined solution | spawn implementer |
|
|
@@ -18,13 +21,14 @@ Always-do:
|
|
|
18
21
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
19
22
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
20
23
|
| work remains | spawn; do not stop |
|
|
21
|
-
| children done + no findings | recap; stop allowed |
|
|
24
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
22
25
|
| `/parent` | one-turn parent tools |
|
|
23
26
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
27
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
24
28
|
|
|
25
29
|
Parent = this file not `.claude/agents/orchestrator.md`. You do not talk. Spawn wait echo.
|
|
26
30
|
Spawn only grunt|implementer|thinker. Never spawn orchestrator. Children never spawn.
|
|
27
|
-
First token = spawn. No try-then-spawn. No parent probe. After child: legal tag + echo only. Siblings still run: `[grunt]:` echo. No skip-spawn. /parent = one-turn. /handoff = one-turn in-parent write to `.tmp/grunt/handoffs/`; recap `[handoff]:`.
|
|
31
|
+
First token = spawn. No try-then-spawn. No parent probe. After child: legal tag + one-line echo only. Siblings still run: `[grunt]:` echo. No skip-spawn. /parent = one-turn. /handoff = one-turn in-parent write to `.tmp/grunt/handoffs/`; recap `[handoff]:`.
|
|
28
32
|
grunt ← tools (facts/search/exec/git/web/test/low-reason mechanical write; world fact: `job: web` never memory). implementer ← write defined solution. thinker ← plan/deep reason.
|
|
29
33
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
|
30
34
|
Peek every 60s/child; quote real host fields; `done|alive|stuck`; no invent; no auto-kill. `timeout_ms=60000` every peek. GAP: block on spawn return = `done`. Stuck/blocked: quote host fields only.
|
|
@@ -9,12 +9,15 @@ globs:
|
|
|
9
9
|
- "**/*"
|
|
10
10
|
---
|
|
11
11
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
12
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + one-line echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
12
13
|
All output exclude AI attribution, Co-Authored-By, trailers, generated markers.
|
|
13
14
|
Protocol: `.rulesync/reference/cascade.md`. Do not paste. Do not open first.
|
|
14
15
|
Always-do:
|
|
15
16
|
|
|
16
17
|
| signal | next |
|
|
17
18
|
|---|---|
|
|
19
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
20
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
18
21
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
19
22
|
| world fact | spawn grunt `job:web` |
|
|
20
23
|
| write defined solution | spawn implementer |
|
|
@@ -23,13 +26,14 @@ Always-do:
|
|
|
23
26
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
24
27
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
25
28
|
| work remains | spawn; do not stop |
|
|
26
|
-
| children done + no findings | recap; stop allowed |
|
|
29
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
27
30
|
| `/parent` | one-turn parent tools |
|
|
28
31
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
32
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
29
33
|
|
|
30
34
|
Parent = orchestrator. You do not talk. Spawn wait echo.
|
|
31
35
|
Spawn only grunt|implementer|thinker. Never spawn orchestrator. Children never spawn.
|
|
32
|
-
First token = spawn. No try-then-spawn. No parent probe. After child: legal tag + echo only. Siblings still run: `[grunt]:` echo. No skip-spawn. /parent = one-turn. /handoff = one-turn in-parent write to `.tmp/grunt/handoffs/`; recap `[handoff]:`.
|
|
36
|
+
First token = spawn. No try-then-spawn. No parent probe. After child: legal tag + one-line echo only. Siblings still run: `[grunt]:` echo. No skip-spawn. /parent = one-turn. /handoff = one-turn in-parent write to `.tmp/grunt/handoffs/`; recap `[handoff]:`.
|
|
33
37
|
grunt ← tools (facts/search/exec/git/web/test/low-reason mechanical write; world fact: `job: web` never memory). implementer ← write defined solution. thinker ← plan/deep reason.
|
|
34
38
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
|
35
39
|
Peek every 60s/child; quote real host fields; `done|alive|stuck`; no invent; no auto-kill. `timeout_ms=60000` every peek. GAP: block on spawn return = `done`. Stuck/blocked: quote host fields only.
|
|
@@ -34,11 +34,14 @@ geminicli:
|
|
|
34
34
|
model: gemini-2.5-flash
|
|
35
35
|
---
|
|
36
36
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
37
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + **one-line** echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
37
38
|
Protocol: `.rulesync/reference/cascade.md` (peek/kill table need:/resume). Do not paste. Do not open first.
|
|
38
39
|
Always-do:
|
|
39
40
|
|
|
40
41
|
| signal | next |
|
|
41
42
|
|---|---|
|
|
43
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
44
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
42
45
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
43
46
|
| world fact | spawn grunt `job:web` |
|
|
44
47
|
| write defined solution | spawn implementer |
|
|
@@ -47,11 +50,12 @@ Always-do:
|
|
|
47
50
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
48
51
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
49
52
|
| work remains | spawn; do not stop |
|
|
50
|
-
| children done + no findings | recap; stop allowed |
|
|
53
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
51
54
|
| `/parent` | one-turn parent tools |
|
|
52
55
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
56
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
53
57
|
|
|
54
|
-
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
58
|
+
You do not talk. First token = spawn. No try-then-spawn. No parent probe. No skip-spawn. No trivia/cheap. User-visible = legal tag + **one-line** echo only. Siblings still run: `[grunt]:` echo. Facts/search/trees → grunt, never thinker.
|
|
55
59
|
Spawn only `grunt` | `implementer` | `thinker`. Omit `model`. Isolation `none` unless asked. Never spawn `orchestrator`. Children never spawn.
|
|
56
60
|
grunt ← tools. World fact → grunt `job: web`. Never memory. implementer ← specified solution (not the spec). thinker ← plan/deep reason/spec.
|
|
57
61
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
package/AGENTS.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
2
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + one-line echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
2
3
|
All output exclude AI attribution, Co-Authored-By, trailers, generated markers.
|
|
3
4
|
Protocol: `.rulesync/reference/cascade.md`. Do not paste. Do not open first.
|
|
4
5
|
Always-do:
|
|
5
6
|
|
|
6
7
|
| signal | next |
|
|
7
8
|
|---|---|
|
|
9
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
10
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
8
11
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
9
12
|
| world fact | spawn grunt `job:web` |
|
|
10
13
|
| write defined solution | spawn implementer |
|
|
@@ -13,13 +16,14 @@ Always-do:
|
|
|
13
16
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
14
17
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
15
18
|
| work remains | spawn; do not stop |
|
|
16
|
-
| children done + no findings | recap; stop allowed |
|
|
19
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
17
20
|
| `/parent` | one-turn parent tools |
|
|
18
21
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
22
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
19
23
|
|
|
20
24
|
Parent = orchestrator. You do not talk. Spawn wait echo.
|
|
21
25
|
Spawn only grunt|implementer|thinker. Never spawn orchestrator. Children never spawn.
|
|
22
|
-
First token = spawn. No try-then-spawn. No parent probe. After child: legal tag + echo only. Siblings still run: `[grunt]:` echo. No skip-spawn. /parent = one-turn. /handoff = one-turn in-parent write to `.tmp/grunt/handoffs/`; recap `[handoff]:`.
|
|
26
|
+
First token = spawn. No try-then-spawn. No parent probe. After child: legal tag + one-line echo only. Siblings still run: `[grunt]:` echo. No skip-spawn. /parent = one-turn. /handoff = one-turn in-parent write to `.tmp/grunt/handoffs/`; recap `[handoff]:`.
|
|
23
27
|
grunt ← tools (facts/search/exec/git/web/test/low-reason mechanical write; world fact: `job: web` never memory). implementer ← write defined solution. thinker ← plan/deep reason.
|
|
24
28
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
|
25
29
|
Peek every 60s/child; quote real host fields; `done|alive|stuck`; no invent; no auto-kill. `timeout_ms=60000` every peek. GAP: block on spawn return = `done`. Stuck/blocked: quote host fields only.
|
package/CLAUDE.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
Voice: `.rulesync/reference/output.md` — cite once; apply every turn.
|
|
2
|
+
en-US unless asked. every output maximal superterse. Fragments OK. Sacrifice grammar; keep meaning. Parent and child. Every turn, not final-only. no mid-turn chat; no narration while Agent runs; user-visible = legal `[role]:` + one-line echo only. Zero user-visible tokens before spawn/peek. In-flight host Stop → only `[orchestrator]: wait grunt`.
|
|
2
3
|
All output exclude AI attribution, Co-Authored-By, trailers, generated markers.
|
|
3
4
|
Protocol: `.rulesync/reference/cascade.md`. Do not paste. Do not open first.
|
|
4
5
|
Always-do:
|
|
5
6
|
|
|
6
7
|
| signal | next |
|
|
7
8
|
|---|---|
|
|
9
|
+
| create/change product files | spawn implementer (thinker then implementer if no spec). Never parent Write/Bash/Skill |
|
|
10
|
+
| plan/spec ready + file writes remain | persist plan if needed; spawn implementer; do not recap-stop |
|
|
8
11
|
| tools/facts/git/test/mechanical write | spawn grunt |
|
|
9
12
|
| world fact | spawn grunt `job:web` |
|
|
10
13
|
| write defined solution | spawn implementer |
|
|
@@ -13,13 +16,14 @@ Always-do:
|
|
|
13
16
|
| child `need:` | parse-need + parallel grunt; `resume_from` + new verdicts; max 3 |
|
|
14
17
|
| ⚠ / validate / sim | spawn implementer with findings; do not recap; spawn; no parent-edit |
|
|
15
18
|
| work remains | spawn; do not stop |
|
|
16
|
-
| children done + no findings | recap; stop allowed |
|
|
19
|
+
| children done + no findings | recap; stop allowed only if no writes remain and spawn count > 0 when the user asked for a file change |
|
|
17
20
|
| `/parent` | one-turn parent tools |
|
|
18
21
|
| `/handoff` | one-turn write `.tmp/grunt/handoffs/`; recap `[handoff]:` |
|
|
22
|
+
| `/write-plan` plan-only | recap + `next: /implement-plan` |
|
|
19
23
|
|
|
20
24
|
Parent = this file not `.claude/agents/orchestrator.md`. You do not talk. Spawn wait echo.
|
|
21
25
|
Spawn only grunt|implementer|thinker. Never spawn orchestrator. Children never spawn.
|
|
22
|
-
First token = spawn. No try-then-spawn. No parent probe. After child: legal tag + echo only. Siblings still run: `[grunt]:` echo. No skip-spawn. /parent = one-turn. /handoff = one-turn in-parent write to `.tmp/grunt/handoffs/`; recap `[handoff]:`.
|
|
26
|
+
First token = spawn. No try-then-spawn. No parent probe. After child: legal tag + one-line echo only. Siblings still run: `[grunt]:` echo. No skip-spawn. /parent = one-turn. /handoff = one-turn in-parent write to `.tmp/grunt/handoffs/`; recap `[handoff]:`.
|
|
23
27
|
grunt ← tools (facts/search/exec/git/web/test/low-reason mechanical write; world fact: `job: web` never memory). implementer ← write defined solution. thinker ← plan/deep reason.
|
|
24
28
|
Child prompt first sentence only: `You are {agent} subagent.` Then task + abs paths + verdicts only.
|
|
25
29
|
Peek every 60s/child; quote real host fields; `done|alive|stuck`; no invent; no auto-kill. `timeout_ms=60000` every peek. GAP: block on spawn return = `done`. Stuck/blocked: quote host fields only.
|
package/package.json
CHANGED
|
@@ -79,19 +79,69 @@ export function eventKey(s) {
|
|
|
79
79
|
.replace(/[^a-z0-9]/g, "");
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
const CHILD_TYPE_KEYS = [
|
|
83
|
+
"subagentType",
|
|
84
|
+
"subagent_type",
|
|
85
|
+
"agentType",
|
|
86
|
+
"agent_type",
|
|
87
|
+
"agentName",
|
|
88
|
+
"agent_name",
|
|
89
|
+
];
|
|
90
|
+
const CHILD_ID_KEYS = ["agentId", "agent_id", "spawnedBy", "spawned_by"];
|
|
91
|
+
|
|
92
|
+
function nonEmptyChildId(raw) {
|
|
93
|
+
if (raw == null) return false;
|
|
94
|
+
return String(raw).trim() !== "";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** True when PreToolUse names a child session (UUID ok). Not a fat-gate type. */
|
|
98
|
+
export function hasChildAgentMarker(data) {
|
|
99
|
+
if (!data || typeof data !== "object") return false;
|
|
100
|
+
for (const k of CHILD_ID_KEYS) {
|
|
101
|
+
if (nonEmptyChildId(data[k])) return true;
|
|
102
|
+
}
|
|
103
|
+
if (data.agent && typeof data.agent === "object" && !Array.isArray(data.agent)) {
|
|
104
|
+
for (const k of [...CHILD_ID_KEYS, "id"]) {
|
|
105
|
+
if (nonEmptyChildId(data.agent[k])) return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function coerceChildType(raw) {
|
|
112
|
+
if (raw == null) return "";
|
|
113
|
+
const s = String(raw).trim().toLowerCase();
|
|
114
|
+
if (!s) return "";
|
|
115
|
+
if (
|
|
116
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s)
|
|
117
|
+
) {
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
if (/^[0-9]+$/.test(s)) return "";
|
|
121
|
+
if (s.length > 64 || /[\/\\]/.test(s)) return "";
|
|
122
|
+
return s;
|
|
123
|
+
}
|
|
124
|
+
|
|
82
125
|
export function subagentTypeOf(data) {
|
|
83
126
|
if (!data || typeof data !== "object") return "";
|
|
84
|
-
const
|
|
85
|
-
data
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
""
|
|
90
|
-
|
|
127
|
+
for (const k of [...CHILD_TYPE_KEYS, ...CHILD_ID_KEYS]) {
|
|
128
|
+
const t = coerceChildType(data[k]);
|
|
129
|
+
if (t) return t;
|
|
130
|
+
}
|
|
131
|
+
if (data.agent && typeof data.agent === "object" && !Array.isArray(data.agent)) {
|
|
132
|
+
for (const k of ["subagentType", "subagent_type", "type", "name", "agentType"]) {
|
|
133
|
+
const t = coerceChildType(data.agent[k]);
|
|
134
|
+
if (t) return t;
|
|
135
|
+
}
|
|
136
|
+
} else if (typeof data.agent === "string") {
|
|
137
|
+
const t = coerceChildType(data.agent);
|
|
138
|
+
if (t) return t;
|
|
139
|
+
}
|
|
140
|
+
return "";
|
|
91
141
|
}
|
|
92
142
|
|
|
93
143
|
export function isParentOrchestrator(data) {
|
|
94
|
-
return !subagentTypeOf(data);
|
|
144
|
+
return !subagentTypeOf(data) && !hasChildAgentMarker(data);
|
|
95
145
|
}
|
|
96
146
|
|
|
97
147
|
export function toolInputOf(data) {
|
|
@@ -123,6 +123,28 @@ export function capSpawnPrompt(prompt) {
|
|
|
123
123
|
return (again.body + verdictPart).trim();
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
const SPAWN_TYPE_KEYS = ["subagent_type", "subagentType", "name", "type", "agent"];
|
|
127
|
+
const FORBIDDEN_SPAWN_TYPES = new Set([
|
|
128
|
+
"explore",
|
|
129
|
+
"plan",
|
|
130
|
+
"general-purpose",
|
|
131
|
+
"orchestrator",
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
function spawnTypeCandidate(toolInput) {
|
|
135
|
+
for (const k of SPAWN_TYPE_KEYS) {
|
|
136
|
+
if (!Object.prototype.hasOwnProperty.call(toolInput, k)) continue;
|
|
137
|
+
if (toolInput[k] == null) continue;
|
|
138
|
+
const v = String(toolInput[k]).trim();
|
|
139
|
+
if (!v) continue;
|
|
140
|
+
const key = v.toLowerCase();
|
|
141
|
+
if (ALLOWED_TYPES.has(key)) return key;
|
|
142
|
+
if (FORBIDDEN_SPAWN_TYPES.has(key)) return "";
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
return "";
|
|
146
|
+
}
|
|
147
|
+
|
|
126
148
|
export function rewriteSpawnToolInput(toolInput, { defaultGrunt = false } = {}) {
|
|
127
149
|
if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) {
|
|
128
150
|
return defaultGrunt ? { subagent_type: "grunt" } : null;
|
|
@@ -132,13 +154,13 @@ export function rewriteSpawnToolInput(toolInput, { defaultGrunt = false } = {})
|
|
|
132
154
|
let changed = false;
|
|
133
155
|
|
|
134
156
|
if (defaultGrunt) {
|
|
135
|
-
const cur =
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
157
|
+
const cur = spawnTypeCandidate(toolInput);
|
|
158
|
+
if (ALLOWED_TYPES.has(cur)) {
|
|
159
|
+
if (String(next.subagent_type || "") !== cur) {
|
|
160
|
+
next.subagent_type = cur;
|
|
161
|
+
changed = true;
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
142
164
|
next.subagent_type = "grunt";
|
|
143
165
|
if (Object.prototype.hasOwnProperty.call(toolInput, "subagentType")) {
|
|
144
166
|
next.subagentType = "grunt";
|