@lovrozagar/grunt 0.5.0 → 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 +17 -12
- package/.agents/agents/thinker.md +6 -5
- 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/handoff/SKILL.md +1 -1
- package/.agents/skills/implement-plan/SKILL.md +12 -11
- package/.agents/skills/solo/SKILL.md +5 -3
- package/.agents/skills/tmp/SKILL.md +58 -0
- package/.agents/skills/write-plan/SKILL.md +11 -8
- package/.claude/agents/grunt.md +2 -1
- package/.claude/agents/implementer.md +1 -1
- package/.claude/agents/orchestrator.md +17 -12
- package/.claude/agents/thinker.md +6 -5
- 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/handoff/SKILL.md +1 -1
- package/.claude/skills/implement-plan/SKILL.md +12 -11
- package/.claude/skills/solo/SKILL.md +5 -3
- package/.claude/skills/tmp/SKILL.md +58 -0
- package/.claude/skills/write-plan/SKILL.md +11 -8
- package/.codex/agents/grunt.toml +2 -1
- package/.codex/agents/implementer.toml +1 -1
- package/.codex/agents/orchestrator.toml +17 -12
- package/.codex/agents/thinker.toml +6 -5
- package/.grok/agents/grunt.md +2 -1
- package/.grok/agents/implementer.md +1 -1
- package/.grok/agents/orchestrator.md +17 -12
- package/.grok/agents/thinker.md +6 -5
- 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/handoff/SKILL.md +1 -1
- package/.grok/skills/implement-plan/SKILL.md +12 -11
- package/.grok/skills/solo/SKILL.md +5 -3
- package/.grok/skills/tmp/SKILL.md +58 -0
- package/.grok/skills/write-plan/SKILL.md +11 -8
- package/.rulesync/grunt.config.jsonc +7 -0
- package/.rulesync/reference/INDEX.md +7 -4
- package/.rulesync/reference/cascade.md +15 -13
- package/.rulesync/reference/hooks.md +6 -4
- package/.rulesync/reference/law.md +1 -1
- package/.rulesync/reference/map.md +14 -9
- package/.rulesync/reference/output.md +18 -8
- package/.rulesync/reference/plan-format.md +10 -10
- package/.rulesync/reference/skills-map.md +6 -3
- package/.rulesync/rules/CLAUDE.md +18 -13
- package/.rulesync/rules/overview.md +18 -13
- 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/handoff/SKILL.md +1 -1
- package/.rulesync/skills/implement-plan/SKILL.md +14 -11
- package/.rulesync/skills/solo/SKILL.md +5 -3
- package/.rulesync/skills/tmp/SKILL.md +58 -0
- package/.rulesync/skills/write-plan/SKILL.md +12 -8
- package/.rulesync/subagents/grunt.md +2 -1
- package/.rulesync/subagents/implementer.md +1 -1
- package/.rulesync/subagents/orchestrator.md +17 -12
- package/.rulesync/subagents/thinker.md +6 -5
- package/AGENTS.md +18 -13
- package/CHANGELOG.md +33 -0
- package/CLAUDE.md +18 -13
- package/README.md +17 -16
- package/cli/grunt.mjs +12 -12
- package/cli/grunt.test.ts +29 -20
- package/cli/init.mjs +116 -22
- package/cli/init.test.ts +545 -34
- package/package.json +8 -1
- package/scripts/gate-fat-tools.mjs +2 -2
- package/scripts/grunt-config.mjs +50 -0
- package/scripts/persist-plan.mjs +17 -2
- package/scripts/persist-tmp.mjs +123 -0
- package/scripts/scrub-spawn-prompt.mjs +1 -1
package/cli/init.mjs
CHANGED
|
@@ -21,8 +21,10 @@ const PRODUCT_SCRIPTS = [
|
|
|
21
21
|
"hooks-union.mjs",
|
|
22
22
|
"pipeline.mjs",
|
|
23
23
|
"grunt-job.mjs",
|
|
24
|
+
"grunt-config.mjs",
|
|
24
25
|
"parse-need.mjs",
|
|
25
26
|
"persist-handoff.mjs",
|
|
27
|
+
"persist-tmp.mjs",
|
|
26
28
|
"persist-plan.mjs",
|
|
27
29
|
"purge-global-mcps.mjs",
|
|
28
30
|
"scrub-spawn-prompt.mjs",
|
|
@@ -38,14 +40,30 @@ const COPY_DIRS = [".rulesync", ".grok", ".codex", ".claude", ".agents"]
|
|
|
38
40
|
const GUARDED_MD_FILES = ["AGENTS.md", "CLAUDE.md"]
|
|
39
41
|
export const GUARDED_ROOT_FILES = ["AGENTS.md", "CLAUDE.md", "GEMINI.md"]
|
|
40
42
|
// .mcp.json is intentionally not copied here: scripts/emit-mcp-policy.mjs
|
|
41
|
-
// (run via `rulesync:generate`, see mergeClaudeSettings ordering) owns and
|
|
43
|
+
// (run via consumer `grunt:rulesync:generate`, see mergeClaudeSettings ordering) owns and
|
|
42
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
|
+
}
|
|
43
56
|
const OWNED_HOOK_FILES = ["scrub-spawn-prompt.mjs", "gate-fat-tools.mjs", "orchestrate-parent.js"]
|
|
44
57
|
export const SENTINEL_BEGIN = "<!-- grunt:begin -->"
|
|
45
58
|
export const SENTINEL_END = "<!-- grunt:end -->"
|
|
46
59
|
export const MAX_GUARDED_MARKDOWN_BYTES = 2 * 1024 * 1024
|
|
47
|
-
const GRUNT_REGION_RE =
|
|
48
|
-
|
|
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]*$/
|
|
49
67
|
|
|
50
68
|
function sortKeys(obj) {
|
|
51
69
|
return Object.fromEntries(Object.keys(obj).sort().map((k) => [k, obj[k]]))
|
|
@@ -118,18 +136,43 @@ function inspectGuardedBuffer(buf) {
|
|
|
118
136
|
}
|
|
119
137
|
|
|
120
138
|
export function extractUserMarkdown(text) {
|
|
121
|
-
|
|
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")
|
|
122
157
|
}
|
|
123
158
|
|
|
124
159
|
export function extractGruntBody(text) {
|
|
160
|
+
const src = String(text)
|
|
125
161
|
const parts = []
|
|
126
162
|
const re = cloneRe(GRUNT_INTERIOR_RE)
|
|
127
163
|
let m
|
|
128
|
-
while ((m = re.exec(
|
|
129
|
-
parts.push(trimTrailingNewlines(m[1]))
|
|
164
|
+
while ((m = re.exec(src))) {
|
|
165
|
+
parts.push(trimTrailingNewlines(unwrapStackedMarkers(m[1])))
|
|
166
|
+
}
|
|
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
|
+
)
|
|
130
173
|
}
|
|
131
|
-
if (!
|
|
132
|
-
return
|
|
174
|
+
if (!src.includes(SENTINEL_END)) return null
|
|
175
|
+
return trimTrailingNewlines(unwrapStackedMarkers(src))
|
|
133
176
|
}
|
|
134
177
|
|
|
135
178
|
function userRemainderIsBlank(user) {
|
|
@@ -137,9 +180,10 @@ function userRemainderIsBlank(user) {
|
|
|
137
180
|
}
|
|
138
181
|
|
|
139
182
|
export function composeGuardedMarkdown(gruntBody, userRemainder, nl = "\n") {
|
|
140
|
-
const
|
|
183
|
+
const raw = extractGruntBody(gruntBody) ?? gruntBody
|
|
184
|
+
const body = toNewline(trimTrailingNewlines(raw), nl)
|
|
141
185
|
const block = `${SENTINEL_BEGIN}${nl}${body}${nl}${SENTINEL_END}${nl}`
|
|
142
|
-
const user = userRemainder == null ? "" : String(userRemainder)
|
|
186
|
+
const user = extractUserMarkdown(userRemainder == null ? "" : String(userRemainder))
|
|
143
187
|
if (!user || userRemainderIsBlank(user)) return block
|
|
144
188
|
return block + toNewline(user, nl)
|
|
145
189
|
}
|
|
@@ -184,7 +228,7 @@ export function mergeGuardedMarkdown(dest, pkgRoot, file, { alreadyInited = fals
|
|
|
184
228
|
const d = path.join(dest, file)
|
|
185
229
|
if (samePath(src, d)) return
|
|
186
230
|
const srcText = fs.readFileSync(src, "utf8")
|
|
187
|
-
return writeMergedGuardedFile(d, srcText)
|
|
231
|
+
return writeMergedGuardedFile(d, extractGruntBody(srcText) ?? srcText)
|
|
188
232
|
}
|
|
189
233
|
|
|
190
234
|
export function snapshotGuardedRoots(root) {
|
|
@@ -339,7 +383,13 @@ export function mergeClaudeSettings(destRoot, pkgRoot) {
|
|
|
339
383
|
}
|
|
340
384
|
|
|
341
385
|
function looksGruntOwnedPrefix(prefix) {
|
|
342
|
-
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, "\\$&")
|
|
343
393
|
}
|
|
344
394
|
|
|
345
395
|
function commandCount(script) {
|
|
@@ -364,14 +414,29 @@ function extraOwnedSuffix(cur, newSrc) {
|
|
|
364
414
|
return null
|
|
365
415
|
}
|
|
366
416
|
|
|
367
|
-
function mergeScriptValue(
|
|
368
|
-
if (cur == null) return newSrc
|
|
417
|
+
function mergeScriptValue(newSrc, cur) {
|
|
369
418
|
if (cur.startsWith(newSrc)) return cur
|
|
370
419
|
const suffix = extraOwnedSuffix(cur, newSrc)
|
|
371
420
|
if (suffix != null) return newSrc + suffix
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
+
}
|
|
375
440
|
}
|
|
376
441
|
|
|
377
442
|
export function mergePackageJson(dest, pkgRoot) {
|
|
@@ -384,10 +449,39 @@ export function mergePackageJson(dest, pkgRoot) {
|
|
|
384
449
|
|
|
385
450
|
const srcPkg = JSON.parse(fs.readFileSync(path.join(pkgRoot, "package.json"), "utf8"))
|
|
386
451
|
destPkg.scripts = { ...(destPkg.scripts || {}) }
|
|
452
|
+
const srcKeys = []
|
|
387
453
|
for (const [k, v] of Object.entries(srcPkg.scripts || {})) {
|
|
388
|
-
if (k === "test" || k.endsWith(":raw")) continue
|
|
389
|
-
|
|
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
|
+
}
|
|
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]
|
|
390
483
|
}
|
|
484
|
+
rewriteNpmRunRefs(destPkg.scripts, srcKeys)
|
|
391
485
|
destPkg.devDependencies = { ...(destPkg.devDependencies || {}) }
|
|
392
486
|
destPkg.devDependencies["smol-toml"] = srcPkg.devDependencies["smol-toml"]
|
|
393
487
|
destPkg.devDependencies["rulesync"] = srcPkg.devDependencies["rulesync"]
|
|
@@ -473,12 +567,12 @@ export function init(dest, { pkgRoot: pkgRootOpt, execFileSync: exec = execFileS
|
|
|
473
567
|
}
|
|
474
568
|
runNpm("install", ["install"])
|
|
475
569
|
const guardedSnap = snapshotGuardedRoots(dest)
|
|
476
|
-
runNpm("generate", ["run", "rulesync:generate"])
|
|
570
|
+
runNpm("generate", ["run", toGruntScriptName("rulesync:generate")])
|
|
477
571
|
remergeGuardedRoots(dest, guardedSnap)
|
|
478
572
|
if (!skipGlobalsApply) {
|
|
479
|
-
runNpm("sync-globals", ["run", "sync:globals:apply"])
|
|
573
|
+
runNpm("sync-globals", ["run", toGruntScriptName("sync:globals:apply")])
|
|
480
574
|
}
|
|
481
575
|
withGuardedCheckInteriors(dest, () => {
|
|
482
|
-
runNpm("check", ["run", "rulesync:check"])
|
|
576
|
+
runNpm("check", ["run", toGruntScriptName("rulesync:check")])
|
|
483
577
|
})
|
|
484
578
|
}
|