@lovrozagar/grunt 0.1.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.
Files changed (79) hide show
  1. package/.agents/agents/grunt.md +36 -0
  2. package/.agents/agents/implementer.md +40 -0
  3. package/.agents/agents/orchestrator.md +50 -0
  4. package/.agents/agents/thinker.md +27 -0
  5. package/.agents/hooks.json +26 -0
  6. package/.agents/mcp_config.json +3 -0
  7. package/.claude/agents/grunt.md +44 -0
  8. package/.claude/agents/implementer.md +46 -0
  9. package/.claude/agents/orchestrator.md +55 -0
  10. package/.claude/agents/thinker.md +34 -0
  11. package/.claude/settings.json +36 -0
  12. package/.codex/agents/grunt.toml +30 -0
  13. package/.codex/agents/implementer.toml +34 -0
  14. package/.codex/agents/orchestrator.toml +45 -0
  15. package/.codex/agents/thinker.toml +23 -0
  16. package/.codex/config.toml +2 -0
  17. package/.codex/hooks.json +26 -0
  18. package/.grok/agents/grunt.md +31 -0
  19. package/.grok/agents/implementer.md +35 -0
  20. package/.grok/agents/orchestrator.md +47 -0
  21. package/.grok/agents/thinker.md +25 -0
  22. package/.grok/config.toml +6 -0
  23. package/.grok/global-settings.toml +35 -0
  24. package/.grok/hooks/orchestrate-parent.js +476 -0
  25. package/.grok/hooks/orchestrate-parent.json +60 -0
  26. package/.grok/hooks/rtk-grok.js +137 -0
  27. package/.grok/hooks/rtk.json +16 -0
  28. package/.grok/hooks/rulesync.json +3 -0
  29. package/.grok/parent.md +1 -0
  30. package/.grok/roles/grunt.toml +5 -0
  31. package/.grok/roles/implementer.toml +5 -0
  32. package/.grok/roles/thinker.toml +5 -0
  33. package/.grok/skills/explain/SKILL.md +15 -0
  34. package/.grok/skills/implement-plan/SKILL.md +91 -0
  35. package/.grok/skills/parent/SKILL.md +12 -0
  36. package/.grok/skills/shared/plan-format.md +158 -0
  37. package/.grok/skills/write-plan/SKILL.md +84 -0
  38. package/.mcp.json +3 -0
  39. package/.rulesync/global-settings/antigravity.json +1 -0
  40. package/.rulesync/global-settings/claude.json +1 -0
  41. package/.rulesync/global-settings/codex.toml +1 -0
  42. package/.rulesync/global-settings/manifest.json +37 -0
  43. package/.rulesync/hooks.jsonc +22 -0
  44. package/.rulesync/mcp-policy.jsonc +15 -0
  45. package/.rulesync/reference/cascade.md +85 -0
  46. package/.rulesync/reference/hooks.md +19 -0
  47. package/.rulesync/reference/map.md +57 -0
  48. package/.rulesync/reference/rtk.md +23 -0
  49. package/.rulesync/rules/CLAUDE.md +13 -0
  50. package/.rulesync/rules/overview.md +23 -0
  51. package/.rulesync/skills/explain/SKILL.md +15 -0
  52. package/.rulesync/skills/parent/SKILL.md +12 -0
  53. package/.rulesync/skills/terse/SKILL.md +8 -0
  54. package/.rulesync/subagents/grunt.md +51 -0
  55. package/.rulesync/subagents/implementer.md +54 -0
  56. package/.rulesync/subagents/orchestrator.md +69 -0
  57. package/.rulesync/subagents/thinker.md +44 -0
  58. package/AGENTS.md +12 -0
  59. package/CLAUDE.md +7 -0
  60. package/LICENSE +21 -0
  61. package/README.md +56 -0
  62. package/bin/grunt.js +4 -0
  63. package/cli/grunt.mjs +54 -0
  64. package/cli/grunt.test.ts +117 -0
  65. package/cli/init.mjs +113 -0
  66. package/cli/init.test.ts +268 -0
  67. package/package.json +77 -0
  68. package/scripts/check-globals.mjs +98 -0
  69. package/scripts/emit-mcp-policy.mjs +434 -0
  70. package/scripts/gate-fat-tools.mjs +485 -0
  71. package/scripts/grunt-job.mjs +482 -0
  72. package/scripts/parse-need.mjs +75 -0
  73. package/scripts/persist-plan.mjs +309 -0
  74. package/scripts/purge-global-mcps.mjs +291 -0
  75. package/scripts/scrub-spawn-prompt.mjs +219 -0
  76. package/scripts/scrub-text +58 -0
  77. package/scripts/scrub-text-lib.mjs +210 -0
  78. package/scripts/sync-global-settings.mjs +239 -0
  79. package/scripts/telemetry.mjs +38 -0
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ /** Grok PreToolUse: rewrite Bash/run_terminal_command through `rtk rewrite`.
3
+
4
+ Fail-open: parse error, missing rtk, timeout, no rewrite → empty stdout, exit 0.
5
+ Keep the full toolInput (Grok needs `description`); only replace `command`.
6
+ */
7
+ "use strict";
8
+
9
+ const { spawnSync } = require("node:child_process");
10
+
11
+ const RTK_TIMEOUT_MS = 3000;
12
+
13
+ const ULTRA_SUBS = new Set([
14
+ "grep",
15
+ "rg",
16
+ "curl",
17
+ "wget",
18
+ "vitest",
19
+ "jest",
20
+ "pytest",
21
+ "test",
22
+ "lint",
23
+ "tsc",
24
+ "npm",
25
+ "npx",
26
+ "read",
27
+ "find",
28
+ "diff",
29
+ "json",
30
+ "log",
31
+ "playwright",
32
+ "cargo",
33
+ "ruff",
34
+ "prettier",
35
+ "format",
36
+ ]);
37
+
38
+ function main() {
39
+ try {
40
+ const data = readJsonValue();
41
+ if (data === undefined) return 0;
42
+ if (!data || typeof data !== "object" || Array.isArray(data)) return 0;
43
+ let toolInput = data.toolInput;
44
+ if (toolInput == null) toolInput = data.tool_input;
45
+ if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return 0;
46
+ const cmdRaw = toolInput.command;
47
+ if (typeof cmdRaw !== "string") return 0;
48
+ const cmd = cmdRaw.trim();
49
+ if (!cmd) return 0;
50
+
51
+ const first = cmd.split(/\s+/, 1)[0] || "";
52
+ const already = first === "rtk" || first.endsWith("/rtk");
53
+ let rewritten;
54
+ if (already) {
55
+ rewritten = injectUltra(cmd);
56
+ if (rewritten === cmd) return 0;
57
+ } else {
58
+ rewritten = rewrite(cmd);
59
+ if (!rewritten) return 0;
60
+ }
61
+
62
+ const updated = Object.assign({}, toolInput, { command: rewritten });
63
+ process.stdout.write(
64
+ JSON.stringify({
65
+ hookSpecificOutput: {
66
+ hookEventName: "PreToolUse",
67
+ permissionDecisionReason: "RTK auto-rewrite",
68
+ updatedInput: updated,
69
+ },
70
+ }),
71
+ );
72
+ return 0;
73
+ } catch {
74
+ return 0;
75
+ }
76
+ }
77
+
78
+ function injectUltra(rewritten) {
79
+ if (rewritten.includes("--ultra-compact")) return rewritten;
80
+ const m = rewritten.match(/^(\S+)\s+(\S+)(?:\s+([\s\S]*))?$/);
81
+ if (!m || m[1] !== "rtk") return rewritten;
82
+ if (!ULTRA_SUBS.has(m[2])) return rewritten;
83
+ const rest = m[3] || "";
84
+ return "rtk " + m[2] + " --ultra-compact" + (rest ? " " + rest : "");
85
+ }
86
+
87
+ function whichRtk() {
88
+ const rtk = spawnSync("sh", ["-c", "command -v rtk"], { encoding: "utf8" });
89
+ const path = (rtk.stdout || "").trim();
90
+ return path || null;
91
+ }
92
+
93
+ function rewrite(cmd) {
94
+ const rtk = whichRtk();
95
+ if (!rtk) return null;
96
+ try {
97
+ const proc = spawnSync(rtk, ["rewrite", "--ultra-compact", cmd], {
98
+ encoding: "utf8",
99
+ timeout: RTK_TIMEOUT_MS,
100
+ });
101
+ const out = (proc.stdout || "").trim();
102
+ if (!out || out === cmd) return null;
103
+ return injectUltra(out);
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ /** Read one JSON value (Python json.load): stop when parseable, do not wait for EOF. */
110
+ function readJsonValue() {
111
+ const fs = require("node:fs");
112
+ let buf = Buffer.alloc(0);
113
+ const tmp = Buffer.alloc(8192);
114
+ for (;;) {
115
+ let n;
116
+ try {
117
+ n = fs.readSync(0, tmp, 0, tmp.length, null);
118
+ } catch {
119
+ break;
120
+ }
121
+ if (n === 0) break;
122
+ buf = Buffer.concat([buf, tmp.subarray(0, n)]);
123
+ try {
124
+ return JSON.parse(buf.toString("utf8"));
125
+ } catch {
126
+ // incomplete
127
+ }
128
+ }
129
+ if (!buf.length) return undefined;
130
+ try {
131
+ return JSON.parse(buf.toString("utf8"));
132
+ } catch {
133
+ return undefined;
134
+ }
135
+ }
136
+
137
+ process.exit(main());
@@ -0,0 +1,16 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node \"$GROK_WORKSPACE_ROOT/.grok/hooks/rtk-grok.js\"",
10
+ "timeout": 5
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "hooks": {}
3
+ }
@@ -0,0 +1 @@
1
+ SSOT: `.grok/agents/orchestrator.md` (`[agent] name = "orchestrator"`).
@@ -0,0 +1,5 @@
1
+ description = "Low reasoning role"
2
+ default_capability_mode = "all"
3
+ reasoning_effort = "low"
4
+ model = "grok-4.5"
5
+ default_fork_context = false
@@ -0,0 +1,5 @@
1
+ description = "Medium reasoning role"
2
+ default_capability_mode = "all"
3
+ reasoning_effort = "medium"
4
+ model = "grok-4.6"
5
+ default_fork_context = false
@@ -0,0 +1,5 @@
1
+ description = "Deep reasoner role"
2
+ default_capability_mode = "read-only"
3
+ reasoning_effort = "high"
4
+ model = "grok-4.6"
5
+ default_fork_context = false
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: explain
3
+ description: >
4
+ One-off human explanation. Use for /explain, "explain this", or a request
5
+ to explain for a human. Not a session mode. Escape superterse for this
6
+ reply only, then revert.
7
+ ---
8
+
9
+ This reply only: explain for a human. Not a mode switch or session flag. After this reply, revert to maximal superterse automatically.
10
+
11
+ Still concise. Complete sentences OK. Prioritize clarity over token compression. Do not dump, sprawl, or narrate process.
12
+
13
+ en-US unless asked. Keep need:/verdict:/plan grammar when those apply.
14
+
15
+ This skill is the only one-off escape from superterse.
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: implement-plan
3
+ description: >
4
+ Implementer executes a local .tmp/plans checklist by serial, marks [x],
5
+ skips completed leaves. Use to implement/continue a written local plan
6
+ or /implement-plan. Not /implement review-loop, not /execute-plan PR DAG.
7
+ when-to-use: >
8
+ Use when asked to "implement plan", "run plan N", "continue plan",
9
+ "execute the checklist", or "/implement-plan".
10
+ argument-hint: "<plan_id_serial>"
11
+ ---
12
+
13
+ # implement-plan
14
+
15
+ Resolve serial (cheap-in-parent) → spawn implementer → parent verifies boxes. Format: `../shared/plan-format.md` — read it; do not paste.
16
+
17
+ ## Invocation
18
+
19
+ `/implement-plan <serial>`
20
+
21
+ Todos: `resolve` → `implement` → `verify`. One serial per run. Partial progress is ok; re-run continues.
22
+
23
+ ## Resolve serial (no spawn)
24
+
25
+ Arg = `$ARGUMENTS` stripped.
26
+
27
+ | input | action |
28
+ |---|---|
29
+ | empty | list `.tmp/plans/*.md` as `serial slug status` (read frontmatter `status` if cheap; else filename). Stop: `need serial. have: ...` |
30
+ | int (`1`, `0001`) | files whose leading number == that int |
31
+ | `{serial}-slug` or `*.md` or path | that file if under `.tmp/plans/` |
32
+ | slug only (`add-auth`) | unique match `*-{slug}-*.md` or `*-{slug}.md` |
33
+ | `.tmp/plans` missing / no files | `no plans. /write-plan first` stop |
34
+ | 0 matches | `no plan serial={n}. have: {list}` stop |
35
+ | >1 matches | `ambiguous: {paths}` stop |
36
+ | all boxes already `[x]` / `status: done` | `[implementer]: already done path=...` **no spawn** |
37
+
38
+ Lookup: integer equality so `1` matches canonical `1-foo-20260826T143000Z.md` and legacy `1-foo.md`. `0001-foo.md` may still resolve if an old padded file exists. Normalize to abs path + int serial.
39
+
40
+ Gather listed plan-leaf absolute paths plus prefetch `verdict:` blobs before the up-spawn (search|exec via parent grunt-job). Do not `resume_from` for dumps already listed in the plan leaves. Do not add a repo-wide explore. Then spawn.
41
+
42
+ ## Read format
43
+
44
+ `read_file` `{repo}/.grok/skills/shared/plan-format.md`.
45
+
46
+ ## Spawn implementer
47
+
48
+ `subagent_type: implementer`, `background: true`, omit `model`, `isolation: none`, `description: "implement plan {serial}"`.
49
+
50
+ Prompt:
51
+
52
+ ```
53
+ You are implementer subagent. Do not spawn.
54
+
55
+ Read {abs}/.grok/skills/shared/plan-format.md and the plan at {abs_plan_path}.
56
+
57
+ Implement remaining `[ ]` leaves in numeric order. Skip `[x]`.
58
+ After each leaf: flip that line to `[x]` in the plan file (only the box).
59
+ When a phase's children are all `[x]`, mark the phase `[x]`.
60
+ Frontmatter status: in-progress at start; done when all boxes `[x]`; blocked if you cannot finish.
61
+ Do not expand scope, add steps, or start a review loop.
62
+ If blocked: leave remaining `[ ]`, status: blocked, report reason + leftover ids.
63
+ Run Verify leaves. Report abs paths you changed.
64
+ ```
65
+
66
+ Wait. Spawn fail → `[implementer]: failed: {err}` stop.
67
+
68
+ ## Parent verify (cheap 1-file)
69
+
70
+ Grep plan for `\[ \]` vs `\[x\]` (and `[X]`).
71
+
72
+ ```
73
+ [implementer]: serial={int} path=.tmp/plans/{file} status={status} done={x}/{total}
74
+ files: {abs paths}
75
+ {if leftover:}
76
+ leftover:
77
+ {up to 10 open leaf lines}
78
+ {if blocked:} blocked: {reason}
79
+ ```
80
+
81
+ ## Rules
82
+
83
+ - Skip `[x]`; only flip boxes; no renumber/rewrite leaf text.
84
+ - No review-fix loop. Not bundled `/implement`.
85
+ - No auto `/write-plan` chain. Escalation → tell user to `/write-plan` a follow-up.
86
+ - Omit `model`. `subagent_type: implementer` only for this skill.
87
+ - First prompt sentence: `You are implementer subagent.`
88
+ - Child prompt = task + abs paths + verdicts only (no pasted transcripts).
89
+ - Resume prompt = `You are implementer subagent.` + new verdicts only. Do not re-send the original task. Max 3 `resume_from` per child id; then report blocked/partial.
90
+ - Tool-call first for spawn claims.
91
+ - Do not re-plan. Partial = skill success (plan file is SoT).
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: parent
3
+ description: >
4
+ One-turn parent-orchestrator escape. Use for /parent when this turn
5
+ must complete in-parent. Not a session mode.
6
+ ---
7
+
8
+ This turn only: parent may finish in-session. Not a mode. After this reply, Stop enforcement resumes.
9
+
10
+ `/parent` writes `parent-escape-{sid}` (Stop consume+unlink once). Protocol: `.rulesync/reference/cascade.md`. Do not paste cascade here.
11
+
12
+ Do not stay in-parent after this reply. Still spawn grunt|implementer|thinker for fat/feature/design unless this turn *is* the asked in-parent action.
@@ -0,0 +1,158 @@
1
+ # Plan format (SSOT)
2
+
3
+ Local implementer checklist. Not a design doc, PR stack, or DAG.
4
+
5
+ ## Path
6
+
7
+ `.tmp/plans/{serial}-{slug}-{YYYYMMDDTHHMMSSZ}.md`
8
+
9
+ - **serial:** unpadded decimal int only (`1`, `12`, `10000`). **Never** `0001`, never quoted `"0001"` / `"1"`. Serial ≥ 1.
10
+ - **filename:** unpadded serial, slug, UTC datetime stamp (`6-sync-global-settings-20260826T143000Z.md`, not `0001-slug.md`). Stamp is `created` with `-` and `:` dropped (keep `T` and `Z`).
11
+ - **lookup:** integer equality on leading digits (`1` matches `1-foo-20260826T143000Z.md` and legacy `1-foo.md`; also `0001-foo.md` if an old padded file still exists).
12
+ - **valid filename (new):** `^[0-9]+-[a-z0-9]+(-[a-z0-9]+)*-\d{8}T\d{6}Z\.md$`
13
+ - **valid filename (legacy):** `^[0-9]+-[a-z0-9]+(-[a-z0-9]+)*\.md$` (no stamp; still accepted for existing files)
14
+ - Frontmatter line must match `^serial: [1-9][0-9]*$`. Leading zeros are invalid (YAML octal / VS Code warning).
15
+ - **New writes** always use the stamped filename. Collision: increment serial, rebuild filename, same stamp.
16
+
17
+ ## Slugify (`PLAN_NAME`)
18
+
19
+ 1. lower
20
+ 2. non `[a-z0-9]` → `-`
21
+ 3. collapse `-`, strip edges
22
+ 4. trim to 50 chars; if cut on `-`, strip trailing `-`
23
+ 5. empty → `unnamed`
24
+ 6. strip trailing `.lock` if any
25
+
26
+ ## Allocate next serial (grunt, bash)
27
+
28
+ ```bash
29
+ mkdir -p .tmp/plans
30
+ next=1
31
+ for f in .tmp/plans/*; do
32
+ [ -f "$f" ] || continue
33
+ b=$(basename "$f")
34
+ case "$b" in
35
+ [0-9]*.md) n=${b%%-*}; n=$((10#$n)); [ "$n" -ge "$next" ] && next=$((n+1));;
36
+ esac
37
+ done
38
+ # filename prefix: unpadded decimal "$next" (1, 12, 10000)
39
+ ```
40
+
41
+ Collision: if target path exists, increment and retry (max 5).
42
+
43
+ ## Frontmatter (required)
44
+
45
+ ```yaml
46
+ ---
47
+ serial: 1
48
+ name: add-auth
49
+ status: ready # ready | in-progress | done | blocked
50
+ created: 2026-08-26T14:30:00Z # UTC ISO-8601 seconds + Z; no milliseconds
51
+ source: "<≤120 chars of user task>"
52
+ ---
53
+ ```
54
+
55
+ `name` is the slug only (no datetime). `source` ≤120 chars.
56
+
57
+ `created` for **new writes:** `new Date().toISOString()` with milliseconds stripped (`.\d{3}Z` → `Z`), e.g. `2026-08-26T14:30:00Z`. Existing files may still have `created: YYYY-MM-DD`.
58
+
59
+ ### Status machine
60
+
61
+ `ready` → `in-progress` → `done` | `blocked`
62
+
63
+ - Fresh write: `ready`
64
+ - Implement start: `in-progress`
65
+ - All boxes `[x]`: `done`
66
+ - Cannot finish: `blocked` (leave remaining `[ ]`)
67
+
68
+ ## Body headings (this order only)
69
+
70
+ `# {slug}`, `## Goal`, `## Context`, `## Constraints`, `## Watch-outs`, `## Steps`, `## Verify`
71
+
72
+ No other `##` headings. No PR Plan, Key Decisions, or DAG.
73
+
74
+ ## Checkbox grammar
75
+
76
+ - Open: `[ ]` (space in box required)
77
+ - Done: `[x]` (accept `[X]`, write `[x]`)
78
+ - Line: `^{id} [ ] {text}$` with optional 0–2 space indent
79
+ - `{id}` = `N` / `N.M` / `N.M.K` (digits + dots). **Not** `1. [ ]`. **Not** GitHub `- [ ]`
80
+ - Fresh plans: all `[ ]`, never `[x]`
81
+ - Marking done: only flip the box. No renumber, delete, or rewrite leaf text
82
+ - Depth 2 default (`N` phase + `N.M` leaf). 3rd level only if a leaf would itself be a mini-plan
83
+ - Every phase `N` has ≥1 leaf `N.M`. Implementer **executes leaves**. Roll up: all children `[x]` ⇒ parent `[x]`
84
+ - Verify = last numbered phase, same grammar
85
+ - Done plan: every checkbox `[x]`, `status: done`
86
+
87
+ ## Prose vs steps
88
+
89
+ | Section | Amount |
90
+ |---|---|
91
+ | Goal | 1–3 sentences. What “done” means. |
92
+ | Context / Constraints / Watch-outs | bullets only. Watch-outs: non-obvious footguns; empty allowed (`(none)`). |
93
+ | Steps | bulk of the file; each leaf = one coherent edit or one file-level action |
94
+ | Verify | 1–5 runnable leaves (cmd or observable) |
95
+
96
+ Thinker cites **absolute paths**. Leaves must be implementable without re-planning. Product fork → pick the narrowest default, state it under Constraints, still emit steps.
97
+
98
+ ## Validation checklist
99
+
100
+ - filename matches new stamped regex or legacy unstamped; new writes unpadded + stamp (`1-foo-20260826T143000Z.md` not `0001-foo.md`); serial int equals leading digits; `name` equals slug after stripping optional trailing `-\d{8}T\d{6}Z`
101
+ - frontmatter `serial` line matches `^serial: [1-9][0-9]*$` (unpadded decimal ≥ 1; reject `0001`, `"0001"`, `0`, `01`)
102
+ - frontmatter keys: `serial`, `name`, `status`, `created`, `source`
103
+ - `status` ∈ `ready` | `in-progress` | `done` | `blocked`; `created` `YYYY-MM-DDTHH:MM:SSZ` (new writes) or legacy `YYYY-MM-DD`; `source` ≤120
104
+ - headings in order only; no extra `##`
105
+ - ≥1 `N.M [ ]` leaf; every phase has ≥1 leaf; Verify is last numbered phase
106
+ - checkbox grammar (`N [ ]` / `N.M [ ]` / `N.M.K [ ]`); no `- [ ]`; no `1. [ ]`
107
+ - fresh write: zero `[x]` / `[X]`; Goal 1–3 sentences; Context/Constraints/Watch-outs bullets; Verify 1–5 leaves
108
+
109
+ ## Worked example
110
+
111
+ File: `.tmp/plans/1-add-tmp-ignore-20260826T143000Z.md`
112
+
113
+ ```markdown
114
+ ---
115
+ serial: 1
116
+ name: add-tmp-ignore
117
+ status: ready
118
+ created: 2026-08-26T14:30:00Z
119
+ source: "gitignore .tmp so local plans stay untracked"
120
+ ---
121
+
122
+ # add-tmp-ignore
123
+
124
+ ## Goal
125
+ Repo `.gitignore` ignores `.tmp/` so files under `.tmp/plans/` stay untracked. Existing ignore entries stay.
126
+
127
+ ## Context
128
+ - `/home/ecomet/Development/example-app/.gitignore` may be missing or lack `.tmp/`
129
+ - Plans live at `/home/ecomet/Development/example-app/.tmp/plans/`
130
+ - No python helpers; bash only
131
+
132
+ ## Constraints
133
+ - Do not clobber other `.gitignore` lines
134
+ - Do not create sample plans under `.tmp/plans` for git
135
+ - Do not edit `AGENTS.md`
136
+
137
+ ## Watch-outs
138
+ - Duplicate `.tmp/` if append without grep
139
+ - Use directory form `.tmp/`, not `.tmp`
140
+
141
+ ## Steps
142
+ 1 [ ] gitignore
143
+ 1.1 [ ] read `/home/ecomet/Development/example-app/.gitignore` if it exists
144
+ 1.2 [ ] create `/home/ecomet/Development/example-app/.gitignore` when missing, body `.tmp/`
145
+ 1.3 [ ] append `.tmp/` to `/home/ecomet/Development/example-app/.gitignore` if the line is absent
146
+ 2 [ ] plans dir
147
+ 2.1 [ ] mkdir -p `/home/ecomet/Development/example-app/.tmp/plans`
148
+ 2.2 [ ] confirm the dir exists; do not add a sample `*.md` under it
149
+ 3 [ ] sanity
150
+ 3.1 [ ] grep `^\.tmp/` in `/home/ecomet/Development/example-app/.gitignore`
151
+ 3.2 [ ] confirm no extra hunks in that file besides the ignore line
152
+ 3.3 [ ] `git check-ignore -v /home/ecomet/Development/example-app/.tmp/plans` from repo root
153
+
154
+ ## Verify
155
+ 4 [ ] Verify
156
+ 4.1 [ ] `git check-ignore -v .tmp/plans` reports ignored
157
+ 4.2 [ ] `git status --short -- .gitignore` shows only the ignore edit
158
+ ```
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: write-plan
3
+ description: >
4
+ Thinker drafts a local implementer-ready checklist plan; parent Write
5
+ persists .tmp/plans/{serial}-{slug}-{YYYYMMDDTHHMMSSZ}.md. Use for a numbered local plan, checklist
6
+ for implementer, "write a plan", or /write-plan. Not a design doc or PR stack.
7
+ when-to-use: >
8
+ Use when asked to "write a plan", "make a checklist plan",
9
+ "plan this for implementer", or "/write-plan".
10
+ argument-hint: "<task or context>"
11
+ ---
12
+
13
+ # write-plan
14
+
15
+ Parent orchestrates thinker then persists via Write under `.tmp/plans/`. Parent does not author the plan body. Format: `../shared/plan-format.md` — read it; do not paste.
16
+
17
+ ## Invocation
18
+
19
+ `/write-plan <task>`
20
+
21
+ Optional todos: `think` → `write` → `report`. Always a new serial.
22
+
23
+ ## Cheap-in-parent (no spawn)
24
+
25
+ Empty `$ARGUMENTS` **and** no task in conversation → `need task` and stop. Do not list the repo.
26
+
27
+ ## Read format
28
+
29
+ `read_file` `{repo}/.grok/skills/shared/plan-format.md` (`dirname(SKILL.md)/../shared/plan-format.md`).
30
+
31
+ Gather the listed plan-format/SSOT absolute paths before the up-spawn. Prefetch search|exec via parent `node scripts/grunt-job.mjs` (not LLM grunt spawn). Do not `need:` for paths already listed.
32
+
33
+ ## Step 1 — thinker
34
+
35
+ Spawn `subagent_type: thinker`, `background: true`, omit `model`, `description: "write plan"`.
36
+
37
+ Prompt:
38
+
39
+ ```
40
+ You are thinker subagent. Read-only. Do not write files. Do not spawn.
41
+
42
+ Read {abs}/.grok/skills/shared/plan-format.md. Produce a plan that matches it.
43
+
44
+ Task:
45
+ {full $ARGUMENTS + relevant conversation context}
46
+
47
+ Cite absolute paths. Use only listed abs paths plus prefetch `verdict:` blobs. Do not explore the repo. Narrowest change that solves the task.
48
+
49
+ Output rules:
50
+ - Line 1: PLAN_NAME: <3-6 word name>
51
+ - Then the plan body only: # heading, Goal, Context, Constraints, Watch-outs, Steps, Verify
52
+ - All boxes [ ]
53
+ - No preamble, no summary, no extra sections
54
+ - Final message IS the plan (parent persists it verbatim)
55
+ ```
56
+
57
+ Wait. Fail → `[thinker]: failed: {err}` stop.
58
+
59
+ **Truncation:** if output lacks `PLAN_NAME:` or has no `N.M [ ]` leaf, resume once (`resume_from`). Resume prompt = `You are thinker subagent.` plus the new instruction only (do not re-send the original task). Still bad → stop, no write. Max 3 `resume_from` per child id.
60
+
61
+ ## Step 2 — persist (parent Write)
62
+
63
+ No grunt spawn. Parent `write`s the thinker final message under `{repo}/.tmp/plans/` (any filename in that dir). `orchestrate-parent.js` runs `scripts/persist-plan.mjs`: serial/slug from `PLAN_NAME:` / heading, inject YAML, rewrite path+content. Deny invalid plans and any write outside `.tmp/plans/`.
64
+
65
+ Report serial/path from the tool result (`updatedInput.file_path`). Fail/deny → `[write-plan]: failed: {err}` stop.
66
+
67
+ ## Report
68
+
69
+ ```
70
+ [thinker]: {one-line goal}; {N} leaves
71
+ [write-plan]: serial={int} path=.tmp/plans/{serial}-{slug}-{YYYYMMDDTHHMMSSZ}.md
72
+ next: /implement-plan {int}
73
+ ```
74
+
75
+ Do not dump the plan.
76
+
77
+ ## Rules
78
+
79
+ - Omit `model`. `subagent_type` ∈ {grunt, implementer, thinker} only (thinker for draft).
80
+ - First prompt sentence: `You are {agent} subagent.`
81
+ - Child prompt = task + abs paths + verdicts only (no pasted transcripts).
82
+ - Parent coordinates; parent Write under `.tmp/plans/` only; thinker never writes.
83
+ - Not `/design`, not `/execute-plan`, not a design doc.
84
+ - Tool-call first: emit `spawn_subagent` in the same turn as any spawn claim. Past tense after the tool result.
package/.mcp.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "mcpServers": {}
3
+ }
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1 @@
1
+ # reserved: noop / project-only
@@ -0,0 +1,37 @@
1
+ {
2
+ "version": 1,
3
+ "skipKeyPattern": "^(secrets?|api[_-]?key|access[_-]?token|tokens?|passwords?|authorization|mcp(servers)?|hooks)$",
4
+ "hosts": [
5
+ {
6
+ "id": "grok",
7
+ "mode": "merge",
8
+ "format": "toml",
9
+ "payload": ".grok/global-settings.toml",
10
+ "dest": ".grok/config.toml"
11
+ },
12
+ {
13
+ "id": "claude",
14
+ "mode": "noop",
15
+ "scope": "project-only",
16
+ "format": "json",
17
+ "payload": ".rulesync/global-settings/claude.json",
18
+ "dest": null
19
+ },
20
+ {
21
+ "id": "codex",
22
+ "mode": "noop",
23
+ "scope": "project-only",
24
+ "format": "toml",
25
+ "payload": ".rulesync/global-settings/codex.toml",
26
+ "dest": null
27
+ },
28
+ {
29
+ "id": "antigravity",
30
+ "mode": "noop",
31
+ "scope": "project-only",
32
+ "format": "json",
33
+ "payload": ".rulesync/global-settings/antigravity.json",
34
+ "dest": null
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ // Claude / Codex / Antigravity only (generate -t omits grokcli for hooks).
5
+ // Grok spawn/fat SSOT is hand-placed .grok/hooks/orchestrate-parent.js + orchestrate-parent.json.
6
+ "preToolUse": [
7
+ {
8
+ "matcher": "spawn_subagent|Task|Agent|spawn_agent",
9
+ "type": "command",
10
+ "command": "node \"${GROK_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/scripts/scrub-spawn-prompt.mjs\"",
11
+ "timeout": 5
12
+ },
13
+ {
14
+ // Fat Read/Grep/Glob/Bash: inject limits, path denylist, implementer dump deny.
15
+ "matcher": "Read|read_file|Grep|grep|Glob|list_dir|Bash|run_terminal_command",
16
+ "type": "command",
17
+ "command": "node \"${GROK_WORKSPACE_ROOT:-${CLAUDE_PROJECT_DIR:-.}}/scripts/gate-fat-tools.mjs\"",
18
+ "timeout": 5
19
+ }
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1,15 @@
1
+ // MCP deny-default SSOT. Named allow is empty this pass; do not add cloudflare/docker.
2
+ {
3
+ "default": "deny",
4
+ "allow": [],
5
+ "plugins": {
6
+ "denyDefault": true
7
+ },
8
+ "servers": {},
9
+ "agents": {
10
+ "orchestrator": { "inherit": false },
11
+ "implementer": { "inherit": false },
12
+ "thinker": { "inherit": false },
13
+ "grunt": { "inherit": false }
14
+ }
15
+ }