@lovrozagar/grunt 0.4.2 → 0.5.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 -1
- package/.agents/agents/implementer.md +1 -1
- package/.agents/agents/orchestrator.md +19 -13
- package/.agents/agents/thinker.md +8 -6
- package/.agents/skills/ask/SKILL.md +18 -0
- package/.agents/skills/auto/SKILL.md +18 -0
- package/.agents/skills/browser/SKILL.md +4 -3
- package/.agents/skills/cascade/SKILL.md +5 -3
- package/.agents/skills/commit-and-push/SKILL.md +3 -3
- package/.agents/skills/explain/SKILL.md +1 -0
- package/.agents/skills/handoff/SKILL.md +1 -1
- package/.agents/skills/implement-plan/SKILL.md +117 -0
- package/.agents/skills/solo/SKILL.md +5 -3
- package/.agents/skills/tmp/SKILL.md +58 -0
- package/.agents/skills/write-plan/SKILL.md +85 -0
- package/.claude/agents/grunt.md +2 -1
- package/.claude/agents/implementer.md +1 -1
- package/.claude/agents/orchestrator.md +19 -13
- package/.claude/agents/thinker.md +8 -6
- package/.claude/skills/ask/SKILL.md +18 -0
- package/.claude/skills/auto/SKILL.md +18 -0
- package/.claude/skills/browser/SKILL.md +4 -3
- package/.claude/skills/cascade/SKILL.md +5 -3
- package/.claude/skills/commit-and-push/SKILL.md +3 -3
- package/.claude/skills/explain/SKILL.md +1 -0
- package/.claude/skills/handoff/SKILL.md +1 -1
- package/.claude/skills/implement-plan/SKILL.md +117 -0
- package/.claude/skills/solo/SKILL.md +5 -3
- package/.claude/skills/tmp/SKILL.md +58 -0
- package/.claude/skills/write-plan/SKILL.md +85 -0
- package/.codex/agents/grunt.toml +2 -1
- package/.codex/agents/implementer.toml +1 -1
- package/.codex/agents/orchestrator.toml +19 -13
- package/.codex/agents/thinker.toml +8 -6
- package/.grok/agents/grunt.md +2 -1
- package/.grok/agents/implementer.md +1 -1
- package/.grok/agents/orchestrator.md +19 -13
- package/.grok/agents/thinker.md +8 -6
- package/.grok/hooks/orchestrate-parent.js +280 -62
- package/.grok/skills/ask/SKILL.md +18 -0
- package/.grok/skills/auto/SKILL.md +18 -0
- package/.grok/skills/browser/SKILL.md +4 -3
- package/.grok/skills/cascade/SKILL.md +5 -3
- package/.grok/skills/commit-and-push/SKILL.md +3 -3
- package/.grok/skills/explain/SKILL.md +1 -0
- package/.grok/skills/handoff/SKILL.md +1 -1
- package/.grok/skills/implement-plan/SKILL.md +49 -20
- package/.grok/skills/solo/SKILL.md +5 -3
- package/.grok/skills/tmp/SKILL.md +58 -0
- package/.grok/skills/write-plan/SKILL.md +17 -14
- package/.rulesync/grunt.config.jsonc +7 -0
- package/.rulesync/reference/INDEX.md +46 -0
- package/.rulesync/reference/cascade.md +19 -15
- package/.rulesync/reference/hooks.md +6 -4
- package/.rulesync/reference/law.md +9 -0
- package/.rulesync/reference/map.md +20 -10
- package/.rulesync/reference/output.md +23 -8
- package/{.grok/skills/shared → .rulesync/reference}/plan-format.md +12 -11
- package/.rulesync/reference/refs-map.md +14 -0
- package/.rulesync/reference/skills-map.md +23 -0
- package/.rulesync/rules/CLAUDE.md +22 -15
- package/.rulesync/rules/overview.md +22 -15
- package/.rulesync/skills/ask/SKILL.md +18 -0
- package/.rulesync/skills/auto/SKILL.md +18 -0
- package/.rulesync/skills/browser/SKILL.md +2 -2
- package/.rulesync/skills/cascade/SKILL.md +5 -3
- package/.rulesync/skills/commit-and-push/SKILL.md +3 -3
- package/.rulesync/skills/explain/SKILL.md +1 -0
- package/.rulesync/skills/handoff/SKILL.md +1 -1
- package/.rulesync/skills/implement-plan/SKILL.md +127 -0
- package/.rulesync/skills/solo/SKILL.md +5 -3
- package/.rulesync/skills/tmp/SKILL.md +58 -0
- package/.rulesync/skills/write-plan/SKILL.md +92 -0
- package/.rulesync/subagents/grunt.md +2 -1
- package/.rulesync/subagents/implementer.md +1 -1
- package/.rulesync/subagents/orchestrator.md +19 -13
- package/.rulesync/subagents/thinker.md +8 -6
- package/AGENTS.md +22 -15
- package/CHANGELOG.md +56 -0
- package/CLAUDE.md +22 -15
- package/README.md +27 -22
- package/cli/grunt.mjs +12 -12
- package/cli/grunt.test.ts +29 -20
- package/cli/init.mjs +135 -22
- package/cli/init.test.ts +624 -36
- package/package.json +11 -4
- package/scripts/doctor.mjs +64 -2
- package/scripts/emit-maps.mjs +422 -0
- package/scripts/gate-fat-tools.mjs +2 -2
- package/scripts/grunt-config.mjs +50 -0
- package/scripts/guarded-roots.mjs +4 -7
- package/scripts/persist-plan.mjs +17 -2
- package/scripts/persist-tmp.mjs +123 -0
- package/scripts/pipeline.mjs +67 -0
- package/scripts/scrub-spawn-prompt.mjs +1 -1
- package/scripts/skill-conflicts.mjs +71 -0
package/cli/init.mjs
CHANGED
|
@@ -2,6 +2,11 @@ import { execFileSync } from "node:child_process"
|
|
|
2
2
|
import fs from "node:fs"
|
|
3
3
|
import path from "node:path"
|
|
4
4
|
import { fileURLToPath } from "node:url"
|
|
5
|
+
import {
|
|
6
|
+
WORKSPACE_SKILLS_REL,
|
|
7
|
+
findSkillContentConflicts,
|
|
8
|
+
formatSkillConflictWarn,
|
|
9
|
+
} from "../scripts/skill-conflicts.mjs"
|
|
5
10
|
|
|
6
11
|
const PKG_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
7
12
|
|
|
@@ -9,13 +14,17 @@ const PRODUCT_SCRIPTS = [
|
|
|
9
14
|
"check-globals.mjs",
|
|
10
15
|
"emit-agent-shell-tools.mjs",
|
|
11
16
|
"emit-gemini.mjs",
|
|
17
|
+
"emit-maps.mjs",
|
|
12
18
|
"guarded-roots.mjs",
|
|
13
19
|
"emit-mcp-policy.mjs",
|
|
14
20
|
"gate-fat-tools.mjs",
|
|
15
21
|
"hooks-union.mjs",
|
|
22
|
+
"pipeline.mjs",
|
|
16
23
|
"grunt-job.mjs",
|
|
24
|
+
"grunt-config.mjs",
|
|
17
25
|
"parse-need.mjs",
|
|
18
26
|
"persist-handoff.mjs",
|
|
27
|
+
"persist-tmp.mjs",
|
|
19
28
|
"persist-plan.mjs",
|
|
20
29
|
"purge-global-mcps.mjs",
|
|
21
30
|
"scrub-spawn-prompt.mjs",
|
|
@@ -23,6 +32,7 @@ const PRODUCT_SCRIPTS = [
|
|
|
23
32
|
"sync-global-settings.mjs",
|
|
24
33
|
"browser.mjs",
|
|
25
34
|
"doctor.mjs",
|
|
35
|
+
"skill-conflicts.mjs",
|
|
26
36
|
"scrub-text",
|
|
27
37
|
]
|
|
28
38
|
|
|
@@ -30,14 +40,30 @@ const COPY_DIRS = [".rulesync", ".grok", ".codex", ".claude", ".agents"]
|
|
|
30
40
|
const GUARDED_MD_FILES = ["AGENTS.md", "CLAUDE.md"]
|
|
31
41
|
export const GUARDED_ROOT_FILES = ["AGENTS.md", "CLAUDE.md", "GEMINI.md"]
|
|
32
42
|
// .mcp.json is intentionally not copied here: scripts/emit-mcp-policy.mjs
|
|
33
|
-
// (run via `rulesync:generate`, see mergeClaudeSettings ordering) owns and
|
|
43
|
+
// (run via consumer `grunt:rulesync:generate`, see mergeClaudeSettings ordering) owns and
|
|
34
44
|
// merges it, so a plain-copy would just be clobbered/redundant.
|
|
45
|
+
export const GRUNT_NPM_PREFIX = "grunt:"
|
|
46
|
+
export function toGruntScriptName(k) {
|
|
47
|
+
return k.startsWith(GRUNT_NPM_PREFIX) ? k : `${GRUNT_NPM_PREFIX}${k}`
|
|
48
|
+
}
|
|
49
|
+
export const LAUNCH_SCRIPTS = {
|
|
50
|
+
antigravity: "antigravity --dangerously-skip-permissions",
|
|
51
|
+
claude: "claude --dangerously-skip-permissions",
|
|
52
|
+
codex: "codex --dangerously-bypass-approvals-and-sandbox",
|
|
53
|
+
gemini: "gemini --yolo",
|
|
54
|
+
grok: "grok --yolo",
|
|
55
|
+
}
|
|
35
56
|
const OWNED_HOOK_FILES = ["scrub-spawn-prompt.mjs", "gate-fat-tools.mjs", "orchestrate-parent.js"]
|
|
36
57
|
export const SENTINEL_BEGIN = "<!-- grunt:begin -->"
|
|
37
58
|
export const SENTINEL_END = "<!-- grunt:end -->"
|
|
38
59
|
export const MAX_GUARDED_MARKDOWN_BYTES = 2 * 1024 * 1024
|
|
39
|
-
const GRUNT_REGION_RE =
|
|
40
|
-
|
|
60
|
+
const GRUNT_REGION_RE =
|
|
61
|
+
/(?:^|(?<=\n)|(?<=\r\n))[ \t]*<!-- grunt:begin -->[ \t]*\r?\n?[\s\S]*?(?:^|(?<=\n)|(?<=\r\n))[ \t]*<!-- grunt:end -->[ \t]*\r?\n?/g
|
|
62
|
+
const GRUNT_INTERIOR_RE =
|
|
63
|
+
/(?:^|(?<=\n)|(?<=\r\n))[ \t]*<!-- grunt:begin -->[ \t]*\r?\n?([\s\S]*?)(?:^|(?<=\n)|(?<=\r\n))[ \t]*<!-- grunt:end -->/g
|
|
64
|
+
const ORPHAN_END_RE = /(?:^|\r?\n)[ \t]*<!-- grunt:end -->[ \t]*(?=\r?\n|$)|<!-- grunt:end -->/g
|
|
65
|
+
const BEGIN_LINE_RE = /^[ \t]*<!-- grunt:begin -->[ \t]*$/
|
|
66
|
+
const END_LINE_RE = /^[ \t]*<!-- grunt:end -->[ \t]*$/
|
|
41
67
|
|
|
42
68
|
function sortKeys(obj) {
|
|
43
69
|
return Object.fromEntries(Object.keys(obj).sort().map((k) => [k, obj[k]]))
|
|
@@ -110,18 +136,43 @@ function inspectGuardedBuffer(buf) {
|
|
|
110
136
|
}
|
|
111
137
|
|
|
112
138
|
export function extractUserMarkdown(text) {
|
|
113
|
-
|
|
139
|
+
let user = String(text).replace(cloneRe(GRUNT_REGION_RE), "")
|
|
140
|
+
const i = user.indexOf(SENTINEL_BEGIN)
|
|
141
|
+
if (i >= 0) user = user.slice(0, i)
|
|
142
|
+
user = user.replace(cloneRe(ORPHAN_END_RE), "")
|
|
143
|
+
return userRemainderIsBlank(user) ? "" : user
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function unwrapStackedMarkers(text) {
|
|
147
|
+
const lines = String(text).split(/\r?\n/)
|
|
148
|
+
let start = 0
|
|
149
|
+
let end = lines.length
|
|
150
|
+
while (start < end && BEGIN_LINE_RE.test(lines[start])) start++
|
|
151
|
+
while (end > start && lines[end - 1] === "") end--
|
|
152
|
+
while (end > start && END_LINE_RE.test(lines[end - 1])) {
|
|
153
|
+
end--
|
|
154
|
+
while (end > start && lines[end - 1] === "") end--
|
|
155
|
+
}
|
|
156
|
+
return lines.slice(start, end).join("\n")
|
|
114
157
|
}
|
|
115
158
|
|
|
116
159
|
export function extractGruntBody(text) {
|
|
160
|
+
const src = String(text)
|
|
117
161
|
const parts = []
|
|
118
162
|
const re = cloneRe(GRUNT_INTERIOR_RE)
|
|
119
163
|
let m
|
|
120
|
-
while ((m = re.exec(
|
|
121
|
-
parts.push(trimTrailingNewlines(m[1]))
|
|
164
|
+
while ((m = re.exec(src))) {
|
|
165
|
+
parts.push(trimTrailingNewlines(unwrapStackedMarkers(m[1])))
|
|
122
166
|
}
|
|
123
|
-
if (
|
|
124
|
-
|
|
167
|
+
if (parts.length) return parts.join("\n")
|
|
168
|
+
const i = src.indexOf(SENTINEL_BEGIN)
|
|
169
|
+
if (i >= 0) {
|
|
170
|
+
return trimTrailingNewlines(
|
|
171
|
+
unwrapStackedMarkers(src.slice(i + SENTINEL_BEGIN.length).replace(/^[ \t]*\r?\n/, "")),
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
if (!src.includes(SENTINEL_END)) return null
|
|
175
|
+
return trimTrailingNewlines(unwrapStackedMarkers(src))
|
|
125
176
|
}
|
|
126
177
|
|
|
127
178
|
function userRemainderIsBlank(user) {
|
|
@@ -129,9 +180,10 @@ function userRemainderIsBlank(user) {
|
|
|
129
180
|
}
|
|
130
181
|
|
|
131
182
|
export function composeGuardedMarkdown(gruntBody, userRemainder, nl = "\n") {
|
|
132
|
-
const
|
|
183
|
+
const raw = extractGruntBody(gruntBody) ?? gruntBody
|
|
184
|
+
const body = toNewline(trimTrailingNewlines(raw), nl)
|
|
133
185
|
const block = `${SENTINEL_BEGIN}${nl}${body}${nl}${SENTINEL_END}${nl}`
|
|
134
|
-
const user = userRemainder == null ? "" : String(userRemainder)
|
|
186
|
+
const user = extractUserMarkdown(userRemainder == null ? "" : String(userRemainder))
|
|
135
187
|
if (!user || userRemainderIsBlank(user)) return block
|
|
136
188
|
return block + toNewline(user, nl)
|
|
137
189
|
}
|
|
@@ -176,7 +228,7 @@ export function mergeGuardedMarkdown(dest, pkgRoot, file, { alreadyInited = fals
|
|
|
176
228
|
const d = path.join(dest, file)
|
|
177
229
|
if (samePath(src, d)) return
|
|
178
230
|
const srcText = fs.readFileSync(src, "utf8")
|
|
179
|
-
return writeMergedGuardedFile(d, srcText)
|
|
231
|
+
return writeMergedGuardedFile(d, extractGruntBody(srcText) ?? srcText)
|
|
180
232
|
}
|
|
181
233
|
|
|
182
234
|
export function snapshotGuardedRoots(root) {
|
|
@@ -331,7 +383,13 @@ export function mergeClaudeSettings(destRoot, pkgRoot) {
|
|
|
331
383
|
}
|
|
332
384
|
|
|
333
385
|
function looksGruntOwnedPrefix(prefix) {
|
|
334
|
-
return /rulesync|sync:globals|^npm run /.test(
|
|
386
|
+
return /rulesync|sync:globals|^npm run |guarded-roots\.mjs|hooks-union\.mjs|doctor\.mjs|sync-global-settings\.mjs|check-globals\.mjs|purge-global-mcps\.mjs/.test(
|
|
387
|
+
prefix,
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function escapeRe(s) {
|
|
392
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
335
393
|
}
|
|
336
394
|
|
|
337
395
|
function commandCount(script) {
|
|
@@ -356,14 +414,29 @@ function extraOwnedSuffix(cur, newSrc) {
|
|
|
356
414
|
return null
|
|
357
415
|
}
|
|
358
416
|
|
|
359
|
-
function mergeScriptValue(
|
|
360
|
-
if (cur == null) return newSrc
|
|
417
|
+
function mergeScriptValue(newSrc, cur) {
|
|
361
418
|
if (cur.startsWith(newSrc)) return cur
|
|
362
419
|
const suffix = extraOwnedSuffix(cur, newSrc)
|
|
363
420
|
if (suffix != null) return newSrc + suffix
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
421
|
+
return newSrc
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function rewriteNpmRunRefs(scripts, srcKeys) {
|
|
425
|
+
const keys = srcKeys
|
|
426
|
+
.filter((k) => k !== toGruntScriptName(k))
|
|
427
|
+
.sort((a, b) => b.length - a.length)
|
|
428
|
+
if (!keys.length) return
|
|
429
|
+
for (const name of Object.keys(scripts)) {
|
|
430
|
+
let val = scripts[name]
|
|
431
|
+
if (typeof val !== "string") continue
|
|
432
|
+
for (const k of keys) {
|
|
433
|
+
val = val.replace(
|
|
434
|
+
new RegExp(`npm run ${escapeRe(k)}(?=$|\\s|;|&)`, "g"),
|
|
435
|
+
`npm run ${toGruntScriptName(k)}`,
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
scripts[name] = val
|
|
439
|
+
}
|
|
367
440
|
}
|
|
368
441
|
|
|
369
442
|
export function mergePackageJson(dest, pkgRoot) {
|
|
@@ -376,10 +449,39 @@ export function mergePackageJson(dest, pkgRoot) {
|
|
|
376
449
|
|
|
377
450
|
const srcPkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, "package.json"), "utf8"))
|
|
378
451
|
destPkg.scripts = { ...(destPkg.scripts || {}) }
|
|
452
|
+
const srcKeys = []
|
|
379
453
|
for (const [k, v] of Object.entries(srcPkg.scripts || {})) {
|
|
380
|
-
if (k === "test") continue
|
|
381
|
-
|
|
454
|
+
if (k === "test" || k.endsWith(":raw") || k in LAUNCH_SCRIPTS) continue
|
|
455
|
+
srcKeys.push(k)
|
|
456
|
+
const destKey = toGruntScriptName(k)
|
|
457
|
+
const current = destPkg.scripts[destKey]
|
|
458
|
+
const legacy = destPkg.scripts[k]
|
|
459
|
+
const ownedLegacy =
|
|
460
|
+
legacy != null &&
|
|
461
|
+
(legacy.startsWith(v) || extraOwnedSuffix(legacy, v) != null || looksGruntOwnedPrefix(legacy))
|
|
462
|
+
if (current != null) {
|
|
463
|
+
destPkg.scripts[destKey] = mergeScriptValue(v, current)
|
|
464
|
+
} else if (ownedLegacy) {
|
|
465
|
+
destPkg.scripts[destKey] = mergeScriptValue(v, legacy)
|
|
466
|
+
} else {
|
|
467
|
+
destPkg.scripts[destKey] = v
|
|
468
|
+
}
|
|
469
|
+
if (k !== destKey && ownedLegacy) {
|
|
470
|
+
delete destPkg.scripts[k]
|
|
471
|
+
} else if (k !== destKey && legacy != null) {
|
|
472
|
+
console.warn(
|
|
473
|
+
`script \`${k}\` left untouched (unrelated customization); writing \`${destKey}\``,
|
|
474
|
+
)
|
|
475
|
+
}
|
|
382
476
|
}
|
|
477
|
+
for (const [k, v] of Object.entries(LAUNCH_SCRIPTS)) {
|
|
478
|
+
const destKey = toGruntScriptName(k)
|
|
479
|
+
const current = destPkg.scripts[destKey]
|
|
480
|
+
destPkg.scripts[destKey] = current != null ? mergeScriptValue(v, current) : v
|
|
481
|
+
const staleKey = `grunt:yolo:${k}`
|
|
482
|
+
if (destPkg.scripts[staleKey] === v) delete destPkg.scripts[staleKey]
|
|
483
|
+
}
|
|
484
|
+
rewriteNpmRunRefs(destPkg.scripts, srcKeys)
|
|
383
485
|
destPkg.devDependencies = { ...(destPkg.devDependencies || {}) }
|
|
384
486
|
destPkg.devDependencies["smol-toml"] = srcPkg.devDependencies["smol-toml"]
|
|
385
487
|
destPkg.devDependencies["rulesync"] = srcPkg.devDependencies["rulesync"]
|
|
@@ -406,6 +508,17 @@ export function init(dest, { pkgRoot: pkgRootOpt, execFileSync: exec = execFileS
|
|
|
406
508
|
const self = samePath(dest, pkgRoot)
|
|
407
509
|
|
|
408
510
|
phase("merge", () => {
|
|
511
|
+
const wsSkills = path.join(dest, WORKSPACE_SKILLS_REL)
|
|
512
|
+
const pkgSkills = path.join(pkgRoot, WORKSPACE_SKILLS_REL)
|
|
513
|
+
if (!samePath(wsSkills, pkgSkills)) {
|
|
514
|
+
for (const c of findSkillContentConflicts({
|
|
515
|
+
workspaceSkillsDir: wsSkills,
|
|
516
|
+
packagedSkillsDir: pkgSkills,
|
|
517
|
+
})) {
|
|
518
|
+
console.warn(formatSkillConflictWarn(c.name))
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
409
522
|
for (const dir of COPY_DIRS) {
|
|
410
523
|
const src = path.join(pkgRoot, dir)
|
|
411
524
|
const d = path.join(dest, dir)
|
|
@@ -454,12 +567,12 @@ export function init(dest, { pkgRoot: pkgRootOpt, execFileSync: exec = execFileS
|
|
|
454
567
|
}
|
|
455
568
|
runNpm("install", ["install"])
|
|
456
569
|
const guardedSnap = snapshotGuardedRoots(dest)
|
|
457
|
-
runNpm("generate", ["run", "rulesync:generate"])
|
|
570
|
+
runNpm("generate", ["run", toGruntScriptName("rulesync:generate")])
|
|
458
571
|
remergeGuardedRoots(dest, guardedSnap)
|
|
459
572
|
if (!skipGlobalsApply) {
|
|
460
|
-
runNpm("sync-globals", ["run", "sync:globals:apply"])
|
|
573
|
+
runNpm("sync-globals", ["run", toGruntScriptName("sync:globals:apply")])
|
|
461
574
|
}
|
|
462
575
|
withGuardedCheckInteriors(dest, () => {
|
|
463
|
-
runNpm("check", ["run", "rulesync:check"])
|
|
576
|
+
runNpm("check", ["run", toGruntScriptName("rulesync:check")])
|
|
464
577
|
})
|
|
465
578
|
}
|