@lovrozagar/grunt 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.agents/agents/grunt.md +4 -2
  2. package/.agents/agents/orchestrator.md +2 -2
  3. package/.agents/hooks.json +12 -1
  4. package/.agents/skills/commit/SKILL.md +21 -0
  5. package/.agents/skills/commit-and-push/SKILL.md +15 -0
  6. package/.agents/skills/explain/SKILL.md +14 -0
  7. package/.agents/skills/handoff/SKILL.md +84 -0
  8. package/.agents/skills/parent/SKILL.md +11 -0
  9. package/.agents/skills/solo/SKILL.md +21 -0
  10. package/.claude/agents/grunt.md +2 -0
  11. package/.claude/agents/orchestrator.md +3 -3
  12. package/.claude/settings.json +34 -1
  13. package/.claude/skills/commit/SKILL.md +21 -0
  14. package/.claude/skills/commit-and-push/SKILL.md +15 -0
  15. package/.claude/skills/explain/SKILL.md +14 -0
  16. package/.claude/skills/handoff/SKILL.md +84 -0
  17. package/.claude/skills/parent/SKILL.md +11 -0
  18. package/.claude/skills/solo/SKILL.md +21 -0
  19. package/.codex/agents/grunt.toml +4 -2
  20. package/.codex/agents/orchestrator.toml +2 -2
  21. package/.codex/hooks.json +34 -1
  22. package/.grok/agents/grunt.md +4 -2
  23. package/.grok/agents/orchestrator.md +2 -2
  24. package/.grok/hooks/orchestrate-parent.js +263 -29
  25. package/.grok/skills/commit/SKILL.md +21 -0
  26. package/.grok/skills/commit-and-push/SKILL.md +15 -0
  27. package/.grok/skills/handoff/SKILL.md +84 -0
  28. package/.grok/skills/solo/SKILL.md +21 -0
  29. package/.rulesync/hooks.jsonc +11 -3
  30. package/.rulesync/reference/cascade.md +4 -3
  31. package/.rulesync/reference/hooks.md +5 -1
  32. package/.rulesync/reference/map.md +18 -3
  33. package/.rulesync/rules/CLAUDE.md +2 -1
  34. package/.rulesync/rules/overview.md +2 -1
  35. package/.rulesync/skills/commit/SKILL.md +22 -0
  36. package/.rulesync/skills/commit-and-push/SKILL.md +16 -0
  37. package/.rulesync/skills/handoff/SKILL.md +84 -0
  38. package/.rulesync/skills/solo/SKILL.md +21 -0
  39. package/.rulesync/subagents/grunt.md +6 -2
  40. package/.rulesync/subagents/implementer.md +2 -0
  41. package/.rulesync/subagents/orchestrator.md +4 -2
  42. package/.rulesync/subagents/thinker.md +2 -0
  43. package/AGENTS.md +2 -1
  44. package/CLAUDE.md +2 -1
  45. package/README.md +2 -2
  46. package/cli/init.mjs +105 -6
  47. package/cli/init.test.ts +303 -6
  48. package/package.json +12 -6
  49. package/scripts/emit-agent-shell-tools.mjs +118 -0
  50. package/scripts/emit-gemini.mjs +219 -0
  51. package/scripts/emit-mcp-policy.mjs +23 -0
  52. package/scripts/gate-fat-tools.mjs +64 -0
  53. package/scripts/hooks-union.mjs +391 -0
  54. package/scripts/persist-handoff.mjs +231 -0
  55. package/scripts/scrub-spawn-prompt.mjs +37 -17
@@ -4,9 +4,10 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
6
  import { scrubText } from "./scrub-text-lib.mjs";
7
+ import { denyResponse } from "./gate-fat-tools.mjs";
7
8
 
8
9
  const ALLOWED_TYPES = new Set(["grunt", "implementer", "thinker"]);
9
- export const MAX_PROMPT_CHARS = 8000;
10
+ export const MAX_PROMPT_CHARS = 100000;
10
11
  export const TRUNCATE_SUFFIX = "…[truncated]";
11
12
  /** `router` is a legacy transcript prefix; keep matching it to strip old pastes. */
12
13
  const TRANSCRIPT_PREFIX =
@@ -72,14 +73,25 @@ export function extractVerdictBlocks(text) {
72
73
  return { body: body.join("\n"), blocks };
73
74
  }
74
75
 
75
- export function truncatePrompt(text, max = MAX_PROMPT_CHARS) {
76
- const s = String(text);
77
- if (s.length <= max) return s;
78
- const { body, blocks } = extractVerdictBlocks(s);
79
- const verdictPart = blocks.length ? "\n" + blocks.join("\n") : "";
80
- const budget = max - verdictPart.length - TRUNCATE_SUFFIX.length;
81
- const cut = Math.max(0, budget);
82
- return body.slice(0, cut) + TRUNCATE_SUFFIX + verdictPart;
76
+ export function truncatePrompt(text, _max = MAX_PROMPT_CHARS) {
77
+ return String(text);
78
+ }
79
+
80
+ export function workspaceRootOf(data) {
81
+ return (
82
+ process.env.GROK_WORKSPACE_ROOT ||
83
+ (data && (data.workspaceRoot || data.workspace_root || data.cwd)) ||
84
+ process.env.CLAUDE_PROJECT_DIR ||
85
+ process.cwd() ||
86
+ ""
87
+ );
88
+ }
89
+
90
+ export function spawnCapReason(workspaceRoot) {
91
+ const root = workspaceRoot || process.cwd() || "";
92
+ return (
93
+ `spawn prompt exceeds ${MAX_PROMPT_CHARS} chars after scrub; write it under ${root}/.tmp/plans/ and re-spawn with that abs path`
94
+ );
83
95
  }
84
96
 
85
97
  const INLINE_TRANSCRIPT =
@@ -108,13 +120,7 @@ export function capSpawnPrompt(prompt) {
108
120
  const again = extractVerdictBlocks(s);
109
121
  const blocks = extracted.blocks.concat(again.blocks);
110
122
  const verdictPart = blocks.length ? "\n" + blocks.join("\n") : "";
111
- const maxCore = Math.max(0, MAX_PROMPT_CHARS - verdictPart.length);
112
- let core = again.body;
113
- if (core.length > maxCore) {
114
- const cut = Math.max(0, maxCore - TRUNCATE_SUFFIX.length);
115
- core = core.slice(0, cut) + TRUNCATE_SUFFIX;
116
- }
117
- return (core + verdictPart).trim();
123
+ return (again.body + verdictPart).trim();
118
124
  }
119
125
 
120
126
  export function rewriteSpawnToolInput(toolInput, { defaultGrunt = false } = {}) {
@@ -160,9 +166,19 @@ export function processHookPayload(data) {
160
166
  if (!data || typeof data !== "object" || Array.isArray(data)) return null;
161
167
  let toolInput = data.toolInput;
162
168
  if (toolInput == null) toolInput = data.tool_input;
163
- return rewriteSpawnToolInput(toolInput, {
169
+ const updated = rewriteSpawnToolInput(toolInput, {
164
170
  defaultGrunt: grokDefaultGrunt(),
165
171
  });
172
+ const prompt =
173
+ updated && typeof updated.prompt === "string"
174
+ ? updated.prompt
175
+ : toolInput && typeof toolInput.prompt === "string"
176
+ ? toolInput.prompt
177
+ : "";
178
+ if (prompt.length > MAX_PROMPT_CHARS) {
179
+ return denyResponse(spawnCapReason(workspaceRootOf(data)));
180
+ }
181
+ return updated;
166
182
  }
167
183
 
168
184
  export function hookResponse(updated) {
@@ -179,6 +195,10 @@ function main() {
179
195
  const data = readJsonValue();
180
196
  const updated = processHookPayload(data);
181
197
  if (!updated) return 0;
198
+ if (updated.decision === "deny") {
199
+ process.stdout.write(JSON.stringify(updated));
200
+ return 0;
201
+ }
182
202
  process.stdout.write(JSON.stringify(hookResponse(updated)));
183
203
  return 0;
184
204
  } catch {