@lovrozagar/grunt 0.4.0 → 0.4.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/.agents/agents/grunt.md +2 -2
- package/.agents/agents/implementer.md +3 -2
- package/.agents/agents/orchestrator.md +17 -11
- package/.agents/agents/thinker.md +7 -2
- package/.agents/skills/explain/SKILL.md +2 -0
- package/.agents/skills/handoff/SKILL.md +1 -1
- package/.agents/skills/solo/SKILL.md +1 -1
- package/.claude/agents/grunt.md +2 -2
- package/.claude/agents/implementer.md +5 -2
- package/.claude/agents/orchestrator.md +17 -11
- package/.claude/agents/thinker.md +9 -2
- package/.claude/skills/explain/SKILL.md +2 -0
- package/.claude/skills/handoff/SKILL.md +1 -1
- package/.claude/skills/solo/SKILL.md +1 -1
- package/.codex/agents/grunt.toml +2 -2
- package/.codex/agents/implementer.toml +3 -2
- package/.codex/agents/orchestrator.toml +17 -11
- package/.codex/agents/thinker.toml +7 -2
- package/.grok/agents/grunt.md +2 -2
- package/.grok/agents/implementer.md +3 -2
- package/.grok/agents/orchestrator.md +17 -11
- package/.grok/agents/thinker.md +7 -2
- package/.grok/hooks/orchestrate-parent.js +28 -110
- package/.grok/skills/explain/SKILL.md +2 -0
- package/.grok/skills/handoff/SKILL.md +1 -1
- package/.grok/skills/solo/SKILL.md +1 -1
- package/.rulesync/reference/cascade.md +12 -6
- package/.rulesync/reference/hooks.md +4 -4
- package/.rulesync/reference/map.md +2 -3
- package/.rulesync/reference/output.md +38 -1
- package/.rulesync/rules/CLAUDE.md +18 -12
- package/.rulesync/rules/overview.md +18 -12
- package/.rulesync/skills/explain/SKILL.md +2 -0
- package/.rulesync/skills/handoff/SKILL.md +1 -1
- package/.rulesync/skills/solo/SKILL.md +1 -1
- package/.rulesync/subagents/grunt.md +2 -2
- package/.rulesync/subagents/implementer.md +3 -2
- package/.rulesync/subagents/orchestrator.md +17 -11
- package/.rulesync/subagents/thinker.md +7 -2
- package/AGENTS.md +20 -12
- package/CHANGELOG.md +187 -0
- package/CLAUDE.md +20 -12
- package/README.md +32 -15
- package/cli/init.mjs +195 -29
- package/cli/init.test.ts +458 -43
- package/package.json +11 -7
- package/scripts/emit-gemini.mjs +7 -1
- package/scripts/grunt-job.mjs +0 -9
- package/scripts/guarded-roots.mjs +83 -0
- package/scripts/telemetry.mjs +0 -38
package/scripts/emit-gemini.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
import { guardedMarkdownDrift, writeMergedGuardedFile } from "../cli/init.mjs";
|
|
6
7
|
|
|
7
8
|
export const AGENT_IDS = ["orchestrator", "implementer", "thinker", "grunt"];
|
|
8
9
|
const SSOT_REL = ".rulesync/subagents";
|
|
@@ -173,6 +174,10 @@ export function emitGemini({ workspaceRoot, check = false } = {}) {
|
|
|
173
174
|
if (check) {
|
|
174
175
|
const drift = [];
|
|
175
176
|
for (const f of files) {
|
|
177
|
+
if (f.rel === GEMINI_MD_REL) {
|
|
178
|
+
if (guardedMarkdownDrift(path.join(ws, f.rel), f.text)) drift.push(f.rel);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
176
181
|
const disk = readText(path.join(ws, f.rel));
|
|
177
182
|
if (disk !== f.text) drift.push(f.rel);
|
|
178
183
|
}
|
|
@@ -185,7 +190,8 @@ export function emitGemini({ workspaceRoot, check = false } = {}) {
|
|
|
185
190
|
for (const f of files) {
|
|
186
191
|
const abs = path.join(ws, f.rel);
|
|
187
192
|
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
188
|
-
|
|
193
|
+
if (f.rel === GEMINI_MD_REL) writeMergedGuardedFile(abs, f.text);
|
|
194
|
+
else fs.writeFileSync(abs, f.text);
|
|
189
195
|
}
|
|
190
196
|
return { ok: true, check: false };
|
|
191
197
|
}
|
package/scripts/grunt-job.mjs
CHANGED
|
@@ -4,7 +4,6 @@ import { spawnSync } from "node:child_process";
|
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
-
import { logTelemetry } from "./telemetry.mjs";
|
|
8
7
|
|
|
9
8
|
export const FALLBACK = "FALLBACK";
|
|
10
9
|
export const SHELL_META = /[|&;`$(){}<>\n\r]/;
|
|
@@ -443,14 +442,12 @@ function main() {
|
|
|
443
442
|
try {
|
|
444
443
|
const parsed = parseArgv(process.argv.slice(2));
|
|
445
444
|
if (parsed.unknown) {
|
|
446
|
-
logTelemetry("grunt-job", { fallback: true, query: parsed.query }, process.cwd());
|
|
447
445
|
process.stdout.write(FALLBACK + "\n");
|
|
448
446
|
return 2;
|
|
449
447
|
}
|
|
450
448
|
const ws = process.cwd();
|
|
451
449
|
const cwd = parsed.cwd ? resolveJobCwd(parsed.cwd, ws) : ws;
|
|
452
450
|
if (!cwd) {
|
|
453
|
-
logTelemetry("grunt-job", { fallback: true, query: parsed.query }, ws);
|
|
454
451
|
process.stdout.write(FALLBACK + "\n");
|
|
455
452
|
return 2;
|
|
456
453
|
}
|
|
@@ -461,15 +458,9 @@ function main() {
|
|
|
461
458
|
path: parsed.path,
|
|
462
459
|
glob: parsed.glob,
|
|
463
460
|
});
|
|
464
|
-
logTelemetry(
|
|
465
|
-
"grunt-job",
|
|
466
|
-
{ job: parsed.job, fallback: Boolean(result.fallback), query: parsed.query },
|
|
467
|
-
ws,
|
|
468
|
-
);
|
|
469
461
|
process.stdout.write(result.text);
|
|
470
462
|
return result.code;
|
|
471
463
|
} catch {
|
|
472
|
-
logTelemetry("grunt-job", { fallback: true }, process.cwd());
|
|
473
464
|
process.stdout.write(FALLBACK + "\n");
|
|
474
465
|
return 2;
|
|
475
466
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Snapshot/remerge AGENTS.md CLAUDE.md GEMINI.md around generate/check/watch. */
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import {
|
|
8
|
+
GUARDED_ROOT_FILES,
|
|
9
|
+
healGuardedRootFile,
|
|
10
|
+
remergeGuardedRoots,
|
|
11
|
+
snapshotGuardedRoots,
|
|
12
|
+
withGuardedCheckInteriors,
|
|
13
|
+
} from "../cli/init.mjs";
|
|
14
|
+
|
|
15
|
+
const GENERATE_RAW = "rulesync:generate:raw";
|
|
16
|
+
const CHECK_RAW = "rulesync:check:raw";
|
|
17
|
+
const WATCH_RAW = "rulesync:watch:raw";
|
|
18
|
+
|
|
19
|
+
export function attachGuardedRootWatchers(cwd, heal) {
|
|
20
|
+
const w = fs.watch(cwd, (_event, filename) => {
|
|
21
|
+
if (!filename) return;
|
|
22
|
+
const base = path.basename(String(filename));
|
|
23
|
+
if (!GUARDED_ROOT_FILES.includes(base)) return;
|
|
24
|
+
heal(base);
|
|
25
|
+
});
|
|
26
|
+
return () => {
|
|
27
|
+
try {
|
|
28
|
+
w.close();
|
|
29
|
+
} catch {
|
|
30
|
+
/* ignore */
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function runGuardedRoots(mode, { cwd = process.cwd(), exec = execSync, attachWatchers } = {}) {
|
|
36
|
+
if (mode === "generate") {
|
|
37
|
+
const snap = snapshotGuardedRoots(cwd);
|
|
38
|
+
try {
|
|
39
|
+
exec(`npm run ${GENERATE_RAW}`, { cwd, stdio: "inherit", shell: true });
|
|
40
|
+
} finally {
|
|
41
|
+
remergeGuardedRoots(cwd, snap);
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (mode === "check") {
|
|
46
|
+
withGuardedCheckInteriors(cwd, () => {
|
|
47
|
+
exec(`npm run ${CHECK_RAW}`, { cwd, stdio: "inherit", shell: true });
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (mode === "watch") {
|
|
52
|
+
const snap = snapshotGuardedRoots(cwd);
|
|
53
|
+
const heal = (file) => healGuardedRootFile(cwd, file, snap);
|
|
54
|
+
const attach = attachWatchers || ((opts) => attachGuardedRootWatchers(opts.cwd, opts.heal));
|
|
55
|
+
const stop = attach({ cwd, snap, heal });
|
|
56
|
+
try {
|
|
57
|
+
exec(`npm run ${WATCH_RAW}`, { cwd, stdio: "inherit", shell: true });
|
|
58
|
+
} finally {
|
|
59
|
+
try {
|
|
60
|
+
stop?.();
|
|
61
|
+
} catch {
|
|
62
|
+
/* ignore */
|
|
63
|
+
}
|
|
64
|
+
remergeGuardedRoots(cwd, snap);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
throw new Error("usage: guarded-roots.mjs generate|check|watch");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function main() {
|
|
72
|
+
try {
|
|
73
|
+
runGuardedRoots(process.argv[2] || "");
|
|
74
|
+
return 0;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
process.stderr.write((err && err.message ? err.message : String(err)) + "\n");
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
82
|
+
const invoked = process.argv[1] ? path.resolve(process.argv[1]) : "";
|
|
83
|
+
if (invoked === thisFile) process.exit(main());
|
package/scripts/telemetry.mjs
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/** Append-only hook sidecar. Never throws. Never writes stdout. */
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
|
|
6
|
-
const QUERY_MAX = 120;
|
|
7
|
-
|
|
8
|
-
export const ORCHESTRATOR_LOGS_DIR = ".tmp/orchestrator-logs";
|
|
9
|
-
|
|
10
|
-
export function telemetryPath(workspaceRoot) {
|
|
11
|
-
const root =
|
|
12
|
-
workspaceRoot ||
|
|
13
|
-
process.env.GROK_WORKSPACE_ROOT ||
|
|
14
|
-
process.cwd();
|
|
15
|
-
return path.join(root, ORCHESTRATOR_LOGS_DIR, "telemetry.ndjson");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function truncateQuery(q) {
|
|
19
|
-
const s = String(q ?? "");
|
|
20
|
-
if (s.length <= QUERY_MAX) return s;
|
|
21
|
-
return s.slice(0, QUERY_MAX);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function logTelemetry(event, fields, workspaceRoot) {
|
|
25
|
-
try {
|
|
26
|
-
const rec = { ts: Date.now(), event: String(event || "") };
|
|
27
|
-
const src = fields && typeof fields === "object" ? fields : {};
|
|
28
|
-
for (const [k, v] of Object.entries(src)) {
|
|
29
|
-
if (k === "query" && v != null) rec.query = truncateQuery(v);
|
|
30
|
-
else rec[k] = v;
|
|
31
|
-
}
|
|
32
|
-
const dest = telemetryPath(workspaceRoot);
|
|
33
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
34
|
-
fs.appendFileSync(dest, JSON.stringify(rec) + "\n");
|
|
35
|
-
} catch {
|
|
36
|
-
// fail-open
|
|
37
|
-
}
|
|
38
|
-
}
|