@lovrozagar/grunt 0.5.2 → 0.6.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.
- package/.agents/agents/grunt.md +2 -24
- package/.agents/agents/orchestrator.md +5 -50
- package/.agents/skills/ask/SKILL.md +5 -12
- package/.agents/skills/auto/SKILL.md +7 -12
- package/.agents/skills/browser/SKILL.md +4 -8
- package/.agents/skills/clasp/SKILL.md +11 -0
- package/.agents/skills/commit/SKILL.md +3 -1
- package/.agents/skills/commit-push/SKILL.md +2 -0
- package/.agents/skills/commit-push-deploy/SKILL.md +2 -0
- package/.agents/skills/commit-push-release/SKILL.md +2 -0
- package/.agents/skills/explain/SKILL.md +7 -13
- package/.agents/skills/google-workspace/SKILL.md +13 -0
- package/.agents/skills/handoff/SKILL.md +7 -11
- package/.agents/skills/implement-plan/SKILL.md +5 -109
- package/.agents/skills/listen/SKILL.md +15 -0
- package/.agents/skills/pickup/SKILL.md +6 -12
- package/.agents/skills/speak/SKILL.md +13 -0
- package/.agents/skills/tmp/SKILL.md +6 -10
- package/.agents/skills/write-plan/SKILL.md +5 -77
- package/.claude/agents/grunt.md +2 -26
- package/.claude/agents/orchestrator.md +12 -50
- package/.claude/skills/ask/SKILL.md +5 -12
- package/.claude/skills/auto/SKILL.md +7 -12
- package/.claude/skills/browser/SKILL.md +4 -8
- package/.claude/skills/clasp/SKILL.md +11 -0
- package/.claude/skills/commit/SKILL.md +3 -1
- package/.claude/skills/commit-push/SKILL.md +2 -0
- package/.claude/skills/commit-push-deploy/SKILL.md +2 -0
- package/.claude/skills/commit-push-release/SKILL.md +2 -0
- package/.claude/skills/explain/SKILL.md +7 -13
- package/.claude/skills/google-workspace/SKILL.md +13 -0
- package/.claude/skills/handoff/SKILL.md +7 -11
- package/.claude/skills/implement-plan/SKILL.md +5 -109
- package/.claude/skills/listen/SKILL.md +15 -0
- package/.claude/skills/pickup/SKILL.md +6 -12
- package/.claude/skills/speak/SKILL.md +13 -0
- package/.claude/skills/tmp/SKILL.md +6 -10
- package/.claude/skills/write-plan/SKILL.md +5 -77
- package/.codex/agents/grunt.toml +2 -26
- package/.codex/agents/orchestrator.toml +4 -50
- package/.grok/agents/grunt.md +2 -24
- package/.grok/agents/orchestrator.md +4 -48
- package/.grok/global-settings.toml +1 -3
- package/.grok/hooks/orchestrate-parent.js +190 -358
- package/.grok/skills/ask/SKILL.md +5 -12
- package/.grok/skills/auto/SKILL.md +7 -12
- package/.grok/skills/browser/SKILL.md +4 -8
- package/.grok/skills/clasp/SKILL.md +11 -0
- package/.grok/skills/commit/SKILL.md +3 -1
- package/.grok/skills/commit-push/SKILL.md +2 -0
- package/.grok/skills/commit-push-deploy/SKILL.md +2 -0
- package/.grok/skills/commit-push-release/SKILL.md +2 -0
- package/.grok/skills/explain/SKILL.md +7 -13
- package/.grok/skills/google-workspace/SKILL.md +13 -0
- package/.grok/skills/handoff/SKILL.md +7 -11
- package/.grok/skills/implement-plan/SKILL.md +5 -109
- package/.grok/skills/listen/SKILL.md +15 -0
- package/.grok/skills/pickup/SKILL.md +6 -12
- package/.grok/skills/speak/SKILL.md +13 -0
- package/.grok/skills/tmp/SKILL.md +6 -10
- package/.grok/skills/write-plan/SKILL.md +5 -77
- package/.rulesync/hooks.jsonc +1 -1
- package/.rulesync/mcp-policy.jsonc +0 -2
- package/.rulesync/reference/INDEX.md +30 -20
- package/.rulesync/reference/browser.md +13 -6
- package/.rulesync/reference/cascade.md +6 -86
- package/.rulesync/reference/clasp.md +21 -0
- package/.rulesync/reference/google-workspace.md +79 -0
- package/.rulesync/reference/hooks.md +7 -11
- package/.rulesync/reference/implementation-format.md +113 -0
- package/.rulesync/reference/law.md +5 -3
- package/.rulesync/reference/listen.md +80 -0
- package/.rulesync/reference/map.md +40 -36
- package/.rulesync/reference/output.md +3 -51
- package/.rulesync/reference/plan-format.md +4 -2
- package/.rulesync/reference/refs-map.md +12 -5
- package/.rulesync/reference/rtk.md +3 -2
- package/.rulesync/reference/scope.md +37 -0
- package/.rulesync/reference/setup.md +27 -0
- package/.rulesync/reference/skills-map.md +13 -12
- package/.rulesync/reference/speak.md +59 -0
- package/.rulesync/rules/CLAUDE.md +3 -49
- package/.rulesync/rules/overview.md +9 -47
- package/.rulesync/skills/ask/SKILL.md +5 -12
- package/.rulesync/skills/auto/SKILL.md +5 -12
- package/.rulesync/skills/browser/SKILL.md +4 -8
- package/.rulesync/skills/clasp/SKILL.md +9 -0
- package/.rulesync/skills/commit/SKILL.md +3 -1
- package/.rulesync/skills/commit-push/SKILL.md +2 -0
- package/.rulesync/skills/commit-push-deploy/SKILL.md +2 -0
- package/.rulesync/skills/commit-push-release/SKILL.md +2 -0
- package/.rulesync/skills/explain/SKILL.md +7 -14
- package/.rulesync/skills/google-workspace/SKILL.md +11 -0
- package/.rulesync/skills/handoff/SKILL.md +7 -11
- package/.rulesync/skills/implement-plan/SKILL.md +5 -119
- package/.rulesync/skills/listen/SKILL.md +13 -0
- package/.rulesync/skills/pickup/SKILL.md +6 -12
- package/.rulesync/skills/speak/SKILL.md +13 -0
- package/.rulesync/skills/tmp/SKILL.md +6 -10
- package/.rulesync/skills/write-plan/SKILL.md +5 -84
- package/.rulesync/subagents/grunt.md +2 -24
- package/.rulesync/subagents/orchestrator.md +14 -59
- package/AGENTS.md +9 -47
- package/CHANGELOG.md +67 -0
- package/CLAUDE.md +1 -49
- package/README.md +89 -80
- package/cli/grunt.mjs +21 -3
- package/cli/grunt.test.ts +47 -3
- package/cli/init.mjs +134 -234
- package/cli/init.test.ts +241 -58
- package/cli/prompt.mjs +9 -36
- package/cli/prompt.test.ts +14 -0
- package/package.json +10 -2
- package/scripts/browser.mjs +128 -7
- package/scripts/doctor.mjs +104 -10
- package/scripts/emit-gemini.mjs +2 -2
- package/scripts/emit-mcp-policy.mjs +1 -1
- package/scripts/gate-fat-tools.mjs +223 -173
- package/scripts/google-workspace.mjs +713 -0
- package/scripts/grunt-job.mjs +453 -69
- package/scripts/guarded-md.mjs +230 -0
- package/scripts/guarded-roots.mjs +1 -1
- package/scripts/interactive.mjs +15 -0
- package/scripts/listen.mjs +829 -0
- package/scripts/parse-need.mjs +21 -2
- package/scripts/persist-implementation.mjs +278 -0
- package/scripts/prompt.mjs +32 -0
- package/scripts/scrub-spawn-prompt.mjs +3 -50
- package/scripts/setup.mjs +746 -0
- package/scripts/speak.mjs +291 -0
- package/.agents/agents/implementer.md +0 -32
- package/.agents/agents/thinker.md +0 -29
- package/.agents/skills/cascade/SKILL.md +0 -28
- package/.agents/skills/parent/SKILL.md +0 -6
- package/.agents/skills/solo/SKILL.md +0 -25
- package/.claude/agents/implementer.md +0 -37
- package/.claude/agents/thinker.md +0 -36
- package/.claude/skills/cascade/SKILL.md +0 -28
- package/.claude/skills/parent/SKILL.md +0 -6
- package/.claude/skills/solo/SKILL.md +0 -25
- package/.codex/agents/implementer.toml +0 -26
- package/.codex/agents/thinker.toml +0 -26
- package/.grok/agents/implementer.md +0 -27
- package/.grok/agents/thinker.md +0 -27
- package/.grok/parent.md +0 -1
- package/.grok/roles/implementer.toml +0 -5
- package/.grok/roles/thinker.toml +0 -5
- package/.grok/skills/cascade/SKILL.md +0 -28
- package/.grok/skills/parent/SKILL.md +0 -6
- package/.grok/skills/solo/SKILL.md +0 -25
- package/.rulesync/grunt.config.jsonc +0 -7
- package/.rulesync/skills/cascade/SKILL.md +0 -28
- package/.rulesync/skills/parent/SKILL.md +0 -6
- package/.rulesync/skills/solo/SKILL.md +0 -25
- package/.rulesync/subagents/implementer.md +0 -49
- package/.rulesync/subagents/thinker.md +0 -49
- package/scripts/grunt-config.mjs +0 -50
package/cli/init.mjs
CHANGED
|
@@ -6,24 +6,51 @@ import {
|
|
|
6
6
|
WORKSPACE_SKILLS_REL,
|
|
7
7
|
findSkillContentConflicts,
|
|
8
8
|
formatSkillConflictWarn,
|
|
9
|
+
listSkillDirNames,
|
|
9
10
|
} from "../scripts/skill-conflicts.mjs"
|
|
11
|
+
import {
|
|
12
|
+
SENTINEL_BEGIN,
|
|
13
|
+
extractGruntBody,
|
|
14
|
+
writeMergedGuardedFile,
|
|
15
|
+
snapshotGuardedRoots,
|
|
16
|
+
remergeGuardedRoots,
|
|
17
|
+
withGuardedCheckInteriors,
|
|
18
|
+
} from "../scripts/guarded-md.mjs"
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
GUARDED_ROOT_FILES,
|
|
22
|
+
SENTINEL_BEGIN,
|
|
23
|
+
SENTINEL_END,
|
|
24
|
+
MAX_GUARDED_MARKDOWN_BYTES,
|
|
25
|
+
extractUserMarkdown,
|
|
26
|
+
extractGruntBody,
|
|
27
|
+
composeGuardedMarkdown,
|
|
28
|
+
mergeGuardedContent,
|
|
29
|
+
writeMergedGuardedFile,
|
|
30
|
+
guardedMarkdownDrift,
|
|
31
|
+
snapshotGuardedRoots,
|
|
32
|
+
remergeGuardedRoots,
|
|
33
|
+
healGuardedRootFile,
|
|
34
|
+
withGuardedCheckInteriors,
|
|
35
|
+
} from "../scripts/guarded-md.mjs"
|
|
10
36
|
|
|
11
37
|
const PKG_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
12
38
|
|
|
13
|
-
const PRODUCT_SCRIPTS = [
|
|
39
|
+
export const PRODUCT_SCRIPTS = [
|
|
14
40
|
"check-globals.mjs",
|
|
15
41
|
"emit-agent-shell-tools.mjs",
|
|
16
42
|
"emit-gemini.mjs",
|
|
17
43
|
"emit-maps.mjs",
|
|
44
|
+
"guarded-md.mjs",
|
|
18
45
|
"guarded-roots.mjs",
|
|
19
46
|
"emit-mcp-policy.mjs",
|
|
20
47
|
"gate-fat-tools.mjs",
|
|
21
48
|
"hooks-union.mjs",
|
|
22
49
|
"pipeline.mjs",
|
|
23
50
|
"grunt-job.mjs",
|
|
24
|
-
"grunt-config.mjs",
|
|
25
51
|
"parse-need.mjs",
|
|
26
52
|
"persist-handoff.mjs",
|
|
53
|
+
"persist-implementation.mjs",
|
|
27
54
|
"persist-tmp.mjs",
|
|
28
55
|
"persist-plan.mjs",
|
|
29
56
|
"purge-global-mcps.mjs",
|
|
@@ -31,14 +58,64 @@ const PRODUCT_SCRIPTS = [
|
|
|
31
58
|
"scrub-text-lib.mjs",
|
|
32
59
|
"sync-global-settings.mjs",
|
|
33
60
|
"browser.mjs",
|
|
61
|
+
"speak.mjs",
|
|
62
|
+
"listen.mjs",
|
|
63
|
+
"google-workspace.mjs",
|
|
64
|
+
"interactive.mjs",
|
|
65
|
+
"prompt.mjs",
|
|
66
|
+
"setup.mjs",
|
|
34
67
|
"doctor.mjs",
|
|
35
68
|
"skill-conflicts.mjs",
|
|
36
69
|
"scrub-text",
|
|
37
70
|
]
|
|
38
71
|
|
|
39
72
|
const COPY_DIRS = [".rulesync", ".grok", ".codex", ".claude", ".agents"]
|
|
73
|
+
export const RETIRED_SKILLS = ["parent", "solo", "cascade"]
|
|
74
|
+
export const RETIRED_AGENTS = ["implementer", "thinker"]
|
|
75
|
+
/** Cumulative. Dest `scripts/<name>` deleted on init/upgrade even if no longer shipped. */
|
|
76
|
+
export const RETIRED_SCRIPTS = ["telemetry.mjs", "grunt-config.mjs"]
|
|
77
|
+
/** Repo-relative paths grunt used to ship. Deleted on init/upgrade. Consumer extras elsewhere kept. */
|
|
78
|
+
export const RETIRED_PATHS = [
|
|
79
|
+
".grok/parent.md",
|
|
80
|
+
".grok/skills/shared",
|
|
81
|
+
".rulesync/grunt.config.jsonc",
|
|
82
|
+
".rulesync/grunt.config.local.jsonc",
|
|
83
|
+
".rulesync/grunt.config.local.jsonc.example",
|
|
84
|
+
]
|
|
85
|
+
export const RESERVED_SKILLS = [
|
|
86
|
+
"ask",
|
|
87
|
+
"auto",
|
|
88
|
+
"browser",
|
|
89
|
+
"clasp",
|
|
90
|
+
"commit",
|
|
91
|
+
"commit-and-push",
|
|
92
|
+
"commit-push",
|
|
93
|
+
"commit-push-deploy",
|
|
94
|
+
"commit-push-release",
|
|
95
|
+
"explain",
|
|
96
|
+
"google-workspace",
|
|
97
|
+
"handoff",
|
|
98
|
+
"implement-plan",
|
|
99
|
+
"listen",
|
|
100
|
+
"pickup",
|
|
101
|
+
"speak",
|
|
102
|
+
"tmp",
|
|
103
|
+
"write-plan",
|
|
104
|
+
]
|
|
105
|
+
const SKILL_MIRROR_DIRS = [
|
|
106
|
+
[".rulesync", "skills"],
|
|
107
|
+
[".grok", "skills"],
|
|
108
|
+
[".claude", "skills"],
|
|
109
|
+
[".agents", "skills"],
|
|
110
|
+
]
|
|
111
|
+
const AGENT_MIRROR_DIRS = [
|
|
112
|
+
[".rulesync", "subagents"],
|
|
113
|
+
[".claude", "agents"],
|
|
114
|
+
[".grok", "agents"],
|
|
115
|
+
[".agents", "agents"],
|
|
116
|
+
[".codex", "agents"],
|
|
117
|
+
]
|
|
40
118
|
const GUARDED_MD_FILES = ["AGENTS.md", "CLAUDE.md"]
|
|
41
|
-
export const GUARDED_ROOT_FILES = ["AGENTS.md", "CLAUDE.md", "GEMINI.md"]
|
|
42
119
|
// .mcp.json is intentionally not copied here: scripts/emit-mcp-policy.mjs
|
|
43
120
|
// (run via consumer `grunt:rulesync:generate`, see mergeClaudeSettings ordering) owns and
|
|
44
121
|
// merges it, so a plain-copy would just be clobbered/redundant.
|
|
@@ -54,16 +131,6 @@ export const LAUNCH_SCRIPTS = {
|
|
|
54
131
|
grok: "grok --yolo",
|
|
55
132
|
}
|
|
56
133
|
const OWNED_HOOK_FILES = ["scrub-spawn-prompt.mjs", "gate-fat-tools.mjs", "orchestrate-parent.js"]
|
|
57
|
-
export const SENTINEL_BEGIN = "<!-- grunt:begin -->"
|
|
58
|
-
export const SENTINEL_END = "<!-- grunt:end -->"
|
|
59
|
-
export const MAX_GUARDED_MARKDOWN_BYTES = 2 * 1024 * 1024
|
|
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]*$/
|
|
67
134
|
|
|
68
135
|
function sortKeys(obj) {
|
|
69
136
|
return Object.fromEntries(Object.keys(obj).sort().map((k) => [k, obj[k]]))
|
|
@@ -77,17 +144,58 @@ export function samePath(a, b) {
|
|
|
77
144
|
}
|
|
78
145
|
}
|
|
79
146
|
|
|
147
|
+
const GITIGNORE_ENTRIES = [{ re: /^\.tmp\/?$/, line: ".tmp/" }]
|
|
148
|
+
|
|
149
|
+
function rmQuiet(abs) {
|
|
150
|
+
fs.rmSync(abs, { recursive: true, force: true })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function pruneRetired(dest, { pkgRoot } = {}) {
|
|
154
|
+
const root = path.resolve(dest)
|
|
155
|
+
for (const segs of SKILL_MIRROR_DIRS) {
|
|
156
|
+
for (const name of RETIRED_SKILLS) {
|
|
157
|
+
rmQuiet(path.join(root, ...segs, name))
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (pkgRoot) {
|
|
161
|
+
const packaged = new Set(
|
|
162
|
+
listSkillDirNames(path.join(path.resolve(pkgRoot), WORKSPACE_SKILLS_REL)),
|
|
163
|
+
)
|
|
164
|
+
for (const segs of SKILL_MIRROR_DIRS) {
|
|
165
|
+
for (const name of RESERVED_SKILLS) {
|
|
166
|
+
if (packaged.has(name)) continue
|
|
167
|
+
rmQuiet(path.join(root, ...segs, name))
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
for (const segs of AGENT_MIRROR_DIRS) {
|
|
172
|
+
for (const name of RETIRED_AGENTS) {
|
|
173
|
+
rmQuiet(path.join(root, ...segs, `${name}.md`))
|
|
174
|
+
rmQuiet(path.join(root, ...segs, `${name}.toml`))
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
for (const name of RETIRED_AGENTS) {
|
|
178
|
+
rmQuiet(path.join(root, ".gemini", "agents", name))
|
|
179
|
+
rmQuiet(path.join(root, ".grok", "roles", `${name}.toml`))
|
|
180
|
+
}
|
|
181
|
+
for (const name of RETIRED_SCRIPTS) {
|
|
182
|
+
rmQuiet(path.join(root, "scripts", name))
|
|
183
|
+
}
|
|
184
|
+
for (const rel of RETIRED_PATHS) {
|
|
185
|
+
rmQuiet(path.join(root, ...rel.split("/")))
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
80
189
|
export function mergeGitignore(dest) {
|
|
81
190
|
const gi = path.join(dest, ".gitignore")
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
fs.appendFileSync(gi, `${prefix}.tmp/\n`)
|
|
191
|
+
const text = fs.existsSync(gi) ? fs.readFileSync(gi, "utf8") : ""
|
|
192
|
+
const present = text.split(/\r?\n/)
|
|
193
|
+
const missing = GITIGNORE_ENTRIES.filter(
|
|
194
|
+
({ re }) => !present.some((line) => re.test(line)),
|
|
195
|
+
).map(({ line }) => line)
|
|
196
|
+
if (missing.length === 0) return
|
|
197
|
+
const prefix = text.length === 0 || text.endsWith("\n") ? "" : "\n"
|
|
198
|
+
fs.writeFileSync(gi, `${text}${prefix}${missing.join("\n")}\n`)
|
|
91
199
|
}
|
|
92
200
|
|
|
93
201
|
function destHasGruntSentinel(dest) {
|
|
@@ -108,120 +216,6 @@ export function shouldAutoSkipGlobals(dest) {
|
|
|
108
216
|
return destHasGruntSentinel(dest)
|
|
109
217
|
}
|
|
110
218
|
|
|
111
|
-
function detectNewline(text) {
|
|
112
|
-
return String(text).includes("\r\n") ? "\r\n" : "\n"
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function toNewline(text, nl) {
|
|
116
|
-
return String(text).replace(/\r\n/g, "\n").replace(/\n/g, nl)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function trimTrailingNewlines(text) {
|
|
120
|
-
return String(text).replace(/(?:\r?\n)+$/, "")
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function normEq(a, b) {
|
|
124
|
-
return trimTrailingNewlines(String(a).replace(/\r\n/g, "\n")) ===
|
|
125
|
-
trimTrailingNewlines(String(b).replace(/\r\n/g, "\n"))
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function cloneRe(re) {
|
|
129
|
-
return new RegExp(re.source, re.flags)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function inspectGuardedBuffer(buf) {
|
|
133
|
-
if (buf.length > MAX_GUARDED_MARKDOWN_BYTES) return "huge"
|
|
134
|
-
if (buf.includes(0)) return "binary"
|
|
135
|
-
return null
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function extractUserMarkdown(text) {
|
|
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")
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function extractGruntBody(text) {
|
|
160
|
-
const src = String(text)
|
|
161
|
-
const parts = []
|
|
162
|
-
const re = cloneRe(GRUNT_INTERIOR_RE)
|
|
163
|
-
let m
|
|
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
|
-
)
|
|
173
|
-
}
|
|
174
|
-
if (!src.includes(SENTINEL_END)) return null
|
|
175
|
-
return trimTrailingNewlines(unwrapStackedMarkers(src))
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function userRemainderIsBlank(user) {
|
|
179
|
-
return String(user).replace(/\r\n/g, "\n").replace(/\n/g, "") === ""
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function composeGuardedMarkdown(gruntBody, userRemainder, nl = "\n") {
|
|
183
|
-
const raw = extractGruntBody(gruntBody) ?? gruntBody
|
|
184
|
-
const body = toNewline(trimTrailingNewlines(raw), nl)
|
|
185
|
-
const block = `${SENTINEL_BEGIN}${nl}${body}${nl}${SENTINEL_END}${nl}`
|
|
186
|
-
const user = extractUserMarkdown(userRemainder == null ? "" : String(userRemainder))
|
|
187
|
-
if (!user || userRemainderIsBlank(user)) return block
|
|
188
|
-
return block + toNewline(user, nl)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function mergeGuardedContent(existingText, gruntBody) {
|
|
192
|
-
const existing = existingText == null ? "" : String(existingText)
|
|
193
|
-
const nl = existing.includes("\r\n") || String(gruntBody).includes("\r\n")
|
|
194
|
-
? detectNewline(existing || gruntBody)
|
|
195
|
-
: "\n"
|
|
196
|
-
if (!existing) return composeGuardedMarkdown(gruntBody, "", nl)
|
|
197
|
-
const hadSentinel = existing.includes(SENTINEL_BEGIN) && existing.includes(SENTINEL_END)
|
|
198
|
-
const user = extractUserMarkdown(existing)
|
|
199
|
-
if (!hadSentinel && normEq(existing, gruntBody)) {
|
|
200
|
-
return composeGuardedMarkdown(gruntBody, "", nl)
|
|
201
|
-
}
|
|
202
|
-
return composeGuardedMarkdown(gruntBody, user, nl)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export function writeMergedGuardedFile(destPath, gruntBody) {
|
|
206
|
-
if (!fs.existsSync(destPath)) {
|
|
207
|
-
fs.writeFileSync(destPath, mergeGuardedContent("", gruntBody))
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
const buf = fs.readFileSync(destPath)
|
|
211
|
-
if (inspectGuardedBuffer(buf)) return "aborted-unsafe"
|
|
212
|
-
fs.writeFileSync(destPath, mergeGuardedContent(buf.toString("utf8"), gruntBody))
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export function guardedMarkdownDrift(destPath, gruntBody) {
|
|
216
|
-
if (!fs.existsSync(destPath)) return true
|
|
217
|
-
const buf = fs.readFileSync(destPath)
|
|
218
|
-
if (inspectGuardedBuffer(buf)) return true
|
|
219
|
-
const text = buf.toString("utf8")
|
|
220
|
-
const interior = extractGruntBody(text)
|
|
221
|
-
if (interior == null) return !normEq(text, gruntBody)
|
|
222
|
-
return !normEq(interior, gruntBody)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
219
|
export function mergeGuardedMarkdown(dest, pkgRoot, file, { alreadyInited = false } = {}) {
|
|
226
220
|
void alreadyInited
|
|
227
221
|
const src = path.join(pkgRoot, file)
|
|
@@ -231,104 +225,6 @@ export function mergeGuardedMarkdown(dest, pkgRoot, file, { alreadyInited = fals
|
|
|
231
225
|
return writeMergedGuardedFile(d, extractGruntBody(srcText) ?? srcText)
|
|
232
226
|
}
|
|
233
227
|
|
|
234
|
-
export function snapshotGuardedRoots(root) {
|
|
235
|
-
const snap = {}
|
|
236
|
-
for (const file of GUARDED_ROOT_FILES) {
|
|
237
|
-
const p = path.join(root, file)
|
|
238
|
-
if (!fs.existsSync(p)) continue
|
|
239
|
-
const buf = fs.readFileSync(p)
|
|
240
|
-
const unsafe = inspectGuardedBuffer(buf)
|
|
241
|
-
if (unsafe) {
|
|
242
|
-
snap[file] = { unsafe, rawBuf: buf }
|
|
243
|
-
continue
|
|
244
|
-
}
|
|
245
|
-
const raw = buf.toString("utf8")
|
|
246
|
-
snap[file] = { raw, user: extractUserMarkdown(raw) }
|
|
247
|
-
}
|
|
248
|
-
return snap
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
export function remergeGuardedRoots(root, snap) {
|
|
252
|
-
for (const file of GUARDED_ROOT_FILES) {
|
|
253
|
-
const p = path.join(root, file)
|
|
254
|
-
const rec = snap[file]
|
|
255
|
-
if (rec?.unsafe) {
|
|
256
|
-
fs.writeFileSync(p, rec.rawBuf)
|
|
257
|
-
continue
|
|
258
|
-
}
|
|
259
|
-
if (!fs.existsSync(p)) {
|
|
260
|
-
if (rec?.raw != null) fs.writeFileSync(p, rec.raw)
|
|
261
|
-
continue
|
|
262
|
-
}
|
|
263
|
-
const buf = fs.readFileSync(p)
|
|
264
|
-
if (inspectGuardedBuffer(buf)) {
|
|
265
|
-
if (rec?.raw != null) fs.writeFileSync(p, rec.raw)
|
|
266
|
-
continue
|
|
267
|
-
}
|
|
268
|
-
const current = buf.toString("utf8")
|
|
269
|
-
if (rec && current === rec.raw) continue
|
|
270
|
-
const gruntBody = extractGruntBody(current) ?? current
|
|
271
|
-
const nl = detectNewline((rec && rec.raw) || current)
|
|
272
|
-
let user = rec ? rec.user : extractUserMarkdown(current)
|
|
273
|
-
if (rec && !rec.raw.includes(SENTINEL_BEGIN) && normEq(rec.raw, gruntBody)) user = ""
|
|
274
|
-
if (!rec && extractGruntBody(current) == null && normEq(current, gruntBody)) user = ""
|
|
275
|
-
fs.writeFileSync(p, composeGuardedMarkdown(gruntBody, user, nl))
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
export function healGuardedRootFile(root, file, snap) {
|
|
280
|
-
if (!GUARDED_ROOT_FILES.includes(file)) return
|
|
281
|
-
const p = path.join(root, file)
|
|
282
|
-
const rec = snap[file]
|
|
283
|
-
if (!fs.existsSync(p)) return
|
|
284
|
-
if (rec?.unsafe) {
|
|
285
|
-
fs.writeFileSync(p, rec.rawBuf)
|
|
286
|
-
return
|
|
287
|
-
}
|
|
288
|
-
const buf = fs.readFileSync(p)
|
|
289
|
-
if (inspectGuardedBuffer(buf)) {
|
|
290
|
-
if (rec?.raw != null) fs.writeFileSync(p, rec.raw)
|
|
291
|
-
return
|
|
292
|
-
}
|
|
293
|
-
const current = buf.toString("utf8")
|
|
294
|
-
const interior = extractGruntBody(current)
|
|
295
|
-
if (interior != null) {
|
|
296
|
-
snap[file] = { raw: current, user: extractUserMarkdown(current) }
|
|
297
|
-
return
|
|
298
|
-
}
|
|
299
|
-
if (rec && current === rec.raw) return
|
|
300
|
-
const gruntBody = current
|
|
301
|
-
const nl = detectNewline((rec && rec.raw) || current)
|
|
302
|
-
let user = rec ? rec.user : extractUserMarkdown(current)
|
|
303
|
-
if (rec && !rec.raw.includes(SENTINEL_BEGIN) && normEq(rec.raw, gruntBody)) user = ""
|
|
304
|
-
if (!rec && normEq(current, gruntBody)) user = ""
|
|
305
|
-
const out = composeGuardedMarkdown(gruntBody, user, nl)
|
|
306
|
-
fs.writeFileSync(p, out)
|
|
307
|
-
snap[file] = { raw: out, user }
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export function withGuardedCheckInteriors(root, fn) {
|
|
311
|
-
const snap = snapshotGuardedRoots(root)
|
|
312
|
-
try {
|
|
313
|
-
for (const file of GUARDED_ROOT_FILES) {
|
|
314
|
-
const rec = snap[file]
|
|
315
|
-
if (!rec || rec.unsafe) continue
|
|
316
|
-
const body = extractGruntBody(rec.raw)
|
|
317
|
-
if (body == null) continue
|
|
318
|
-
const nl = detectNewline(rec.raw)
|
|
319
|
-
fs.writeFileSync(path.join(root, file), `${toNewline(trimTrailingNewlines(body), nl)}${nl}`)
|
|
320
|
-
}
|
|
321
|
-
return fn()
|
|
322
|
-
} finally {
|
|
323
|
-
for (const file of GUARDED_ROOT_FILES) {
|
|
324
|
-
const rec = snap[file]
|
|
325
|
-
if (!rec) continue
|
|
326
|
-
if (rec.unsafe) fs.writeFileSync(path.join(root, file), rec.rawBuf)
|
|
327
|
-
else fs.writeFileSync(path.join(root, file), rec.raw)
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
228
|
export function mergeClaudeSettings(destRoot, pkgRoot) {
|
|
333
229
|
const srcPath = path.join(pkgRoot, ".claude", "settings.json")
|
|
334
230
|
const destPath = path.join(destRoot, ".claude", "settings.json")
|
|
@@ -485,6 +381,8 @@ export function mergePackageJson(dest, pkgRoot) {
|
|
|
485
381
|
destPkg.devDependencies = { ...(destPkg.devDependencies || {}) }
|
|
486
382
|
destPkg.devDependencies["smol-toml"] = srcPkg.devDependencies["smol-toml"]
|
|
487
383
|
destPkg.devDependencies["rulesync"] = srcPkg.devDependencies["rulesync"]
|
|
384
|
+
const clack = srcPkg.dependencies?.["@clack/prompts"]
|
|
385
|
+
if (clack) destPkg.devDependencies["@clack/prompts"] = clack
|
|
488
386
|
destPkg.scripts = sortKeys(destPkg.scripts)
|
|
489
387
|
destPkg.devDependencies = sortKeys(destPkg.devDependencies)
|
|
490
388
|
fs.writeFileSync(destPath, `${JSON.stringify(destPkg, null, 2)}\n`)
|
|
@@ -536,6 +434,8 @@ export function init(dest, { pkgRoot: pkgRootOpt, execFileSync: exec = execFileS
|
|
|
536
434
|
}
|
|
537
435
|
}
|
|
538
436
|
|
|
437
|
+
pruneRetired(dest, { pkgRoot })
|
|
438
|
+
|
|
539
439
|
for (const file of GUARDED_MD_FILES) {
|
|
540
440
|
mergeGuardedMarkdown(dest, pkgRoot, file)
|
|
541
441
|
}
|