@juho0719/cckit 0.2.0 → 0.2.3
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/assets/claudemd/CLAUDE.md +69 -0
- package/assets/hooks/post-edit-format.js +0 -1
- package/assets/hooks/post-edit-typecheck.js +0 -2
- package/dist/chunk-ID643AV4.js +37 -0
- package/dist/{chunk-6B46AIFM.js → chunk-OLLOS3GG.js} +16 -0
- package/dist/{chunk-3UNN3IBE.js → chunk-SLVASXTF.js} +1 -1
- package/dist/chunk-SW3OJLHC.js +35 -0
- package/dist/claudemd-KKQ2DL7P.js +7 -0
- package/dist/{cli-H6IDWH7Q.js → cli-6WQMAFNA.js} +33 -11
- package/dist/{hooks-IIG2XK4I.js → hooks-S73JTX2I.js} +1 -1
- package/dist/index.js +24 -17
- package/dist/{registry-EGXWYWWK.js → registry-BU55RMHU.js} +3 -1
- package/dist/rules-EFSJ3L3A.js +9 -0
- package/dist/{uninstall-cli-KYQTJCKO.js → uninstall-cli-7XGNDIUC.js} +139 -77
- package/package.json +1 -1
- package/dist/chunk-RMUKD7CW.js +0 -44
- package/dist/rules-2CPBVNNJ.js +0 -7
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Claude Behavioral Guidelines
|
|
3
|
+
description: LLM coding best practices - think before coding, simplicity, surgical changes, goal-driven execution
|
|
4
|
+
---
|
|
5
|
+
# Claude Behavioral Guidelines
|
|
6
|
+
|
|
7
|
+
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
|
8
|
+
|
|
9
|
+
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
|
10
|
+
|
|
11
|
+
## 1. Think Before Coding
|
|
12
|
+
|
|
13
|
+
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
|
14
|
+
|
|
15
|
+
Before implementing:
|
|
16
|
+
- State your assumptions explicitly. If uncertain, ask.
|
|
17
|
+
- If multiple interpretations exist, present them - don't pick silently.
|
|
18
|
+
- If a simpler approach exists, say so. Push back when warranted.
|
|
19
|
+
- If something is unclear, stop. Name what's confusing. Ask.
|
|
20
|
+
|
|
21
|
+
## 2. Simplicity First
|
|
22
|
+
|
|
23
|
+
**Minimum code that solves the problem. Nothing speculative.**
|
|
24
|
+
|
|
25
|
+
- No features beyond what was asked.
|
|
26
|
+
- No abstractions for single-use code.
|
|
27
|
+
- No "flexibility" or "configurability" that wasn't requested.
|
|
28
|
+
- No error handling for impossible scenarios.
|
|
29
|
+
- If you write 200 lines and it could be 50, rewrite it.
|
|
30
|
+
|
|
31
|
+
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
|
32
|
+
|
|
33
|
+
## 3. Surgical Changes
|
|
34
|
+
|
|
35
|
+
**Touch only what you must. Clean up only your own mess.**
|
|
36
|
+
|
|
37
|
+
When editing existing code:
|
|
38
|
+
- Don't "improve" adjacent code, comments, or formatting.
|
|
39
|
+
- Don't refactor things that aren't broken.
|
|
40
|
+
- Match existing style, even if you'd do it differently.
|
|
41
|
+
- If you notice unrelated dead code, mention it - don't delete it.
|
|
42
|
+
|
|
43
|
+
When your changes create orphans:
|
|
44
|
+
- Remove imports/variables/functions that YOUR changes made unused.
|
|
45
|
+
- Don't remove pre-existing dead code unless asked.
|
|
46
|
+
|
|
47
|
+
The test: Every changed line should trace directly to the user's request.
|
|
48
|
+
|
|
49
|
+
## 4. Goal-Driven Execution
|
|
50
|
+
|
|
51
|
+
**Define success criteria. Loop until verified.**
|
|
52
|
+
|
|
53
|
+
Transform tasks into verifiable goals:
|
|
54
|
+
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
|
55
|
+
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
|
56
|
+
- "Refactor X" → "Ensure tests pass before and after"
|
|
57
|
+
|
|
58
|
+
For multi-step tasks, state a brief plan:
|
|
59
|
+
```
|
|
60
|
+
1. [Step] → verify: [check]
|
|
61
|
+
2. [Step] → verify: [check]
|
|
62
|
+
3. [Step] → verify: [check]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
|
@@ -32,7 +32,6 @@ process.stdin.on("end", () => {
|
|
|
32
32
|
if (filePath && /\.(ts|tsx)$/.test(filePath)) {
|
|
33
33
|
const resolvedPath = path.resolve(filePath);
|
|
34
34
|
if (!fs.existsSync(resolvedPath)) {
|
|
35
|
-
process.stdout.write(data);
|
|
36
35
|
process.exit(0);
|
|
37
36
|
}
|
|
38
37
|
// Find nearest tsconfig.json by walking up (max 20 levels to prevent infinite loop)
|
|
@@ -91,6 +90,5 @@ process.stdin.on("end", () => {
|
|
|
91
90
|
// Invalid input — pass through
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
process.stdout.write(data);
|
|
95
93
|
process.exit(0);
|
|
96
94
|
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
backupIfExists
|
|
3
|
+
} from "./chunk-K25UZZVG.js";
|
|
4
|
+
import {
|
|
5
|
+
copyFileUtil,
|
|
6
|
+
ensureDir
|
|
7
|
+
} from "./chunk-3GUKEMND.js";
|
|
8
|
+
import {
|
|
9
|
+
getAssetsDir
|
|
10
|
+
} from "./chunk-5XOKKPAA.js";
|
|
11
|
+
|
|
12
|
+
// src/installers/rules.ts
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { readdir } from "fs/promises";
|
|
15
|
+
async function installRules(categories, targetDir) {
|
|
16
|
+
const srcRulesDir = join(getAssetsDir(), "rules");
|
|
17
|
+
for (const category of categories) {
|
|
18
|
+
const srcCatDir = join(srcRulesDir, category);
|
|
19
|
+
let files;
|
|
20
|
+
try {
|
|
21
|
+
files = (await readdir(srcCatDir)).filter((f) => f.endsWith(".md"));
|
|
22
|
+
} catch {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const destCatDir = join(targetDir, "rules", category);
|
|
26
|
+
await ensureDir(destCatDir);
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const dest = join(destCatDir, file);
|
|
29
|
+
await backupIfExists(dest);
|
|
30
|
+
await copyFileUtil(join(srcCatDir, file), dest);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
installRules
|
|
37
|
+
};
|
|
@@ -98,6 +98,21 @@ async function getRuleCategories() {
|
|
|
98
98
|
}
|
|
99
99
|
return categories;
|
|
100
100
|
}
|
|
101
|
+
async function getClaudemdItems() {
|
|
102
|
+
const dir = join(getAssetsDir(), "claudemd");
|
|
103
|
+
const files = (await readdir(dir)).filter((f) => f.endsWith(".md"));
|
|
104
|
+
const items = [];
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
const content = await readFile(join(dir, file), "utf-8");
|
|
107
|
+
const fm = parseFrontmatter(content);
|
|
108
|
+
items.push({
|
|
109
|
+
name: fm["name"] || basename(file, ".md"),
|
|
110
|
+
description: fm["description"] || "",
|
|
111
|
+
file
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return items;
|
|
115
|
+
}
|
|
101
116
|
async function getMcpServers() {
|
|
102
117
|
const filePath = join(getAssetsDir(), "mcps", "mcp-servers.json");
|
|
103
118
|
const content = await readFile(filePath, "utf-8");
|
|
@@ -132,5 +147,6 @@ export {
|
|
|
132
147
|
getCommands,
|
|
133
148
|
getHooks,
|
|
134
149
|
getRuleCategories,
|
|
150
|
+
getClaudemdItems,
|
|
135
151
|
getMcpServers
|
|
136
152
|
};
|
|
@@ -44,7 +44,7 @@ async function installHooks(hooks, targetDir) {
|
|
|
44
44
|
const dest = join(hooksDir, hook.file);
|
|
45
45
|
await backupIfExists(dest);
|
|
46
46
|
await copyFileUtil(join(srcDir, hook.file), dest);
|
|
47
|
-
const hookCommand = `node ${
|
|
47
|
+
const hookCommand = `node "$CLAUDE_PROJECT_DIR"/.claude/hooks/${hook.file}`;
|
|
48
48
|
await mergeHookSettings(settingsPath, hook.hookType, hook.matcher, hookCommand);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAssetsDir
|
|
3
|
+
} from "./chunk-5XOKKPAA.js";
|
|
4
|
+
|
|
5
|
+
// src/installers/claudemd.ts
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { readFile, writeFile, access } from "fs/promises";
|
|
8
|
+
import { constants } from "fs";
|
|
9
|
+
async function installClaudemd(items, targetDir) {
|
|
10
|
+
const claudemdDir = join(getAssetsDir(), "claudemd");
|
|
11
|
+
const targetClaudeMd = join(targetDir, "CLAUDE.md");
|
|
12
|
+
let existing = "";
|
|
13
|
+
try {
|
|
14
|
+
await access(targetClaudeMd, constants.F_OK);
|
|
15
|
+
existing = await readFile(targetClaudeMd, "utf-8");
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
const toAppend = [];
|
|
19
|
+
for (const item of items) {
|
|
20
|
+
const raw = await readFile(join(claudemdDir, item.file), "utf-8");
|
|
21
|
+
const content = raw.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "").trim();
|
|
22
|
+
const headerMatch = content.match(/^#{1,2}\s+.+/m);
|
|
23
|
+
if (headerMatch) {
|
|
24
|
+
if (existing.includes(headerMatch[0])) continue;
|
|
25
|
+
}
|
|
26
|
+
toAppend.push(content);
|
|
27
|
+
}
|
|
28
|
+
if (toAppend.length === 0) return;
|
|
29
|
+
const appendContent = (existing.endsWith("\n") ? "" : "\n") + toAppend.join("\n\n") + "\n";
|
|
30
|
+
await writeFile(targetClaudeMd, existing + appendContent, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
installClaudemd
|
|
35
|
+
};
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installMcps
|
|
3
3
|
} from "./chunk-W63UKEIT.js";
|
|
4
|
+
import {
|
|
5
|
+
installClaudemd
|
|
6
|
+
} from "./chunk-SW3OJLHC.js";
|
|
4
7
|
import {
|
|
5
8
|
getAgents,
|
|
9
|
+
getClaudemdItems,
|
|
6
10
|
getCommands,
|
|
7
11
|
getHooks,
|
|
8
12
|
getMcpServers,
|
|
9
13
|
getRuleCategories,
|
|
10
14
|
getSkills
|
|
11
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-OLLOS3GG.js";
|
|
12
16
|
import {
|
|
13
17
|
installAgents
|
|
14
18
|
} from "./chunk-EYY2IZ7N.js";
|
|
@@ -20,12 +24,12 @@ import {
|
|
|
20
24
|
} from "./chunk-3Y26YU4R.js";
|
|
21
25
|
import {
|
|
22
26
|
installHooks
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-K25UZZVG.js";
|
|
25
|
-
import "./chunk-3GUKEMND.js";
|
|
27
|
+
} from "./chunk-SLVASXTF.js";
|
|
26
28
|
import {
|
|
27
29
|
installRules
|
|
28
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-ID643AV4.js";
|
|
31
|
+
import "./chunk-K25UZZVG.js";
|
|
32
|
+
import "./chunk-3GUKEMND.js";
|
|
29
33
|
import {
|
|
30
34
|
getGlobalDir,
|
|
31
35
|
getProjectDir
|
|
@@ -74,7 +78,8 @@ async function runCli() {
|
|
|
74
78
|
{ name: "Commands - Slash commands", value: "commands" },
|
|
75
79
|
{ name: "Hooks - Post-tool hooks", value: "hooks" },
|
|
76
80
|
{ name: "Rules - CLAUDE.md rules", value: "rules" },
|
|
77
|
-
{ name: "MCPs - MCP server configs", value: "mcps" }
|
|
81
|
+
{ name: "MCPs - MCP server configs", value: "mcps" },
|
|
82
|
+
{ name: "ClaudeMd - Behavioral guidelines", value: "claudemd" }
|
|
78
83
|
]
|
|
79
84
|
});
|
|
80
85
|
if (selectedCategories.length === 0) {
|
|
@@ -89,7 +94,8 @@ async function runCli() {
|
|
|
89
94
|
hooks: [],
|
|
90
95
|
ruleCategories: [],
|
|
91
96
|
mcps: [],
|
|
92
|
-
mcpEnvValues: /* @__PURE__ */ new Map()
|
|
97
|
+
mcpEnvValues: /* @__PURE__ */ new Map(),
|
|
98
|
+
claudemds: []
|
|
93
99
|
};
|
|
94
100
|
const skippedMcps = /* @__PURE__ */ new Map();
|
|
95
101
|
if (selectedCategories.includes("agents")) {
|
|
@@ -147,6 +153,17 @@ async function runCli() {
|
|
|
147
153
|
}))
|
|
148
154
|
});
|
|
149
155
|
}
|
|
156
|
+
if (selectedCategories.includes("claudemd")) {
|
|
157
|
+
const allClaudemds = await getClaudemdItems();
|
|
158
|
+
plan.claudemds = await checkbox({
|
|
159
|
+
message: "Select claudemd items:",
|
|
160
|
+
choices: allClaudemds.map((c) => ({
|
|
161
|
+
name: `${chalk.bold(c.name.padEnd(35))} ${chalk.gray(c.description.slice(0, 55))}`,
|
|
162
|
+
value: c,
|
|
163
|
+
checked: false
|
|
164
|
+
}))
|
|
165
|
+
});
|
|
166
|
+
}
|
|
150
167
|
if (selectedCategories.includes("mcps")) {
|
|
151
168
|
const allMcps = await getMcpServers();
|
|
152
169
|
plan.mcps = await checkbox({
|
|
@@ -196,8 +213,9 @@ async function runCli() {
|
|
|
196
213
|
if (plan.hooks.length) console.log(` Hooks : ${plan.hooks.map((h) => h.name).join(", ")}`);
|
|
197
214
|
if (plan.ruleCategories.length) console.log(` Rules : ${plan.ruleCategories.join(", ")}`);
|
|
198
215
|
if (plan.mcps.length) console.log(` MCPs : ${plan.mcps.map((m) => m.name).join(", ")}`);
|
|
216
|
+
if (plan.claudemds.length) console.log(` ClaudeMd: ${plan.claudemds.map((c) => c.name).join(", ")}`);
|
|
199
217
|
console.log("");
|
|
200
|
-
const totalItems = plan.agents.length + plan.skills.length + plan.commands.length + plan.hooks.length + plan.ruleCategories.length + plan.mcps.length;
|
|
218
|
+
const totalItems = plan.agents.length + plan.skills.length + plan.commands.length + plan.hooks.length + plan.ruleCategories.length + plan.mcps.length + plan.claudemds.length;
|
|
201
219
|
if (totalItems === 0) {
|
|
202
220
|
console.log(chalk.yellow(" Nothing selected. Exiting."));
|
|
203
221
|
return;
|
|
@@ -228,21 +246,25 @@ async function runCli() {
|
|
|
228
246
|
}
|
|
229
247
|
if (plan.ruleCategories.length) {
|
|
230
248
|
spinner.text = "Installing rules...";
|
|
231
|
-
|
|
232
|
-
await installRules(plan.ruleCategories, claudeMd);
|
|
249
|
+
await installRules(plan.ruleCategories, targetDir);
|
|
233
250
|
}
|
|
234
251
|
if (plan.mcps.length) {
|
|
235
252
|
spinner.text = "Installing MCP servers...";
|
|
236
253
|
await installMcps(plan.mcps, plan.scope, plan.mcpEnvValues);
|
|
237
254
|
}
|
|
255
|
+
if (plan.claudemds.length) {
|
|
256
|
+
spinner.text = "Installing claudemd...";
|
|
257
|
+
await installClaudemd(plan.claudemds, targetDir);
|
|
258
|
+
}
|
|
238
259
|
spinner.succeed(chalk.green("Installation complete!"));
|
|
239
260
|
console.log("");
|
|
240
261
|
if (plan.agents.length) console.log(` ${chalk.green("\u2713")} ${plan.agents.length} agent(s) \u2192 ${join(targetDir, "agents")}`);
|
|
241
262
|
if (plan.skills.length) console.log(` ${chalk.green("\u2713")} ${plan.skills.length} skill(s) \u2192 ${join(targetDir, "skills")}`);
|
|
242
263
|
if (plan.commands.length) console.log(` ${chalk.green("\u2713")} ${plan.commands.length} command(s) \u2192 ${join(targetDir, "commands")}`);
|
|
243
264
|
if (plan.hooks.length) console.log(` ${chalk.green("\u2713")} ${plan.hooks.length} hook(s) \u2192 ${join(targetDir, "hooks")}`);
|
|
244
|
-
if (plan.ruleCategories.length) console.log(` ${chalk.green("\u2713")} Rules
|
|
265
|
+
if (plan.ruleCategories.length) console.log(` ${chalk.green("\u2713")} Rules copied \u2192 ${join(targetDir, "rules")}`);
|
|
245
266
|
if (plan.mcps.length) console.log(` ${chalk.green("\u2713")} ${plan.mcps.length} MCP server(s) \u2192 ${scope === "global" ? "~/.claude.json" : "./.claude.json"}`);
|
|
267
|
+
if (plan.claudemds.length) console.log(` ${chalk.green("\u2713")} ClaudeMd appended \u2192 ${join(targetDir, "CLAUDE.md")}`);
|
|
246
268
|
console.log("");
|
|
247
269
|
if (skippedMcps.size > 0) {
|
|
248
270
|
console.log(chalk.yellow(" \u26A0 The following MCP servers have placeholder env vars:"));
|
package/dist/index.js
CHANGED
|
@@ -51,33 +51,38 @@ Installing all to ${targetDir}...
|
|
|
51
51
|
{ getHooks },
|
|
52
52
|
{ getRuleCategories },
|
|
53
53
|
{ getMcpServers },
|
|
54
|
+
{ getClaudemdItems },
|
|
54
55
|
{ installAgents },
|
|
55
56
|
{ installSkills },
|
|
56
57
|
{ installCommands },
|
|
57
58
|
{ installHooks },
|
|
58
59
|
{ installRules },
|
|
59
|
-
{ installMcps }
|
|
60
|
+
{ installMcps },
|
|
61
|
+
{ installClaudemd }
|
|
60
62
|
] = await Promise.all([
|
|
61
|
-
import("./registry-
|
|
62
|
-
import("./registry-
|
|
63
|
-
import("./registry-
|
|
64
|
-
import("./registry-
|
|
65
|
-
import("./registry-
|
|
66
|
-
import("./registry-
|
|
63
|
+
import("./registry-BU55RMHU.js"),
|
|
64
|
+
import("./registry-BU55RMHU.js"),
|
|
65
|
+
import("./registry-BU55RMHU.js"),
|
|
66
|
+
import("./registry-BU55RMHU.js"),
|
|
67
|
+
import("./registry-BU55RMHU.js"),
|
|
68
|
+
import("./registry-BU55RMHU.js"),
|
|
69
|
+
import("./registry-BU55RMHU.js"),
|
|
67
70
|
import("./agents-AEKT67A6.js"),
|
|
68
71
|
import("./skills-ULMW3UCM.js"),
|
|
69
72
|
import("./commands-P5LILVZ5.js"),
|
|
70
|
-
import("./hooks-
|
|
71
|
-
import("./rules-
|
|
72
|
-
import("./mcps-67Q7TBGW.js")
|
|
73
|
+
import("./hooks-S73JTX2I.js"),
|
|
74
|
+
import("./rules-EFSJ3L3A.js"),
|
|
75
|
+
import("./mcps-67Q7TBGW.js"),
|
|
76
|
+
import("./claudemd-KKQ2DL7P.js")
|
|
73
77
|
]);
|
|
74
|
-
const [agents, skills, commands, hooks, ruleCategories, mcps] = await Promise.all([
|
|
78
|
+
const [agents, skills, commands, hooks, ruleCategories, mcps, claudemds] = await Promise.all([
|
|
75
79
|
getAgents(),
|
|
76
80
|
getSkills(),
|
|
77
81
|
getCommands(),
|
|
78
82
|
getHooks(),
|
|
79
83
|
getRuleCategories(),
|
|
80
|
-
getMcpServers()
|
|
84
|
+
getMcpServers(),
|
|
85
|
+
getClaudemdItems()
|
|
81
86
|
]);
|
|
82
87
|
spinner.text = "Installing agents...";
|
|
83
88
|
await installAgents(agents, targetDir);
|
|
@@ -88,10 +93,11 @@ Installing all to ${targetDir}...
|
|
|
88
93
|
spinner.text = "Installing hooks...";
|
|
89
94
|
await installHooks(hooks, targetDir);
|
|
90
95
|
spinner.text = "Installing rules...";
|
|
91
|
-
|
|
92
|
-
await installRules(ruleCategories.map((rc) => rc.category), claudeMd);
|
|
96
|
+
await installRules(ruleCategories.map((rc) => rc.category), targetDir);
|
|
93
97
|
spinner.text = "Installing MCP servers...";
|
|
94
98
|
await installMcps(mcps, scope, /* @__PURE__ */ new Map());
|
|
99
|
+
spinner.text = "Installing claudemd...";
|
|
100
|
+
await installClaudemd(claudemds, targetDir);
|
|
95
101
|
spinner.succeed(chalk.green("All items installed successfully!"));
|
|
96
102
|
console.log(`
|
|
97
103
|
${chalk.green("\u2713")} ${agents.length} agents`);
|
|
@@ -99,7 +105,8 @@ Installing all to ${targetDir}...
|
|
|
99
105
|
console.log(` ${chalk.green("\u2713")} ${commands.length} commands`);
|
|
100
106
|
console.log(` ${chalk.green("\u2713")} ${hooks.length} hooks`);
|
|
101
107
|
console.log(` ${chalk.green("\u2713")} Rules (${ruleCategories.length} categories)`);
|
|
102
|
-
console.log(` ${chalk.green("\u2713")} ${mcps.length} MCP servers
|
|
108
|
+
console.log(` ${chalk.green("\u2713")} ${mcps.length} MCP servers`);
|
|
109
|
+
console.log(` ${chalk.green("\u2713")} ${claudemds.length} claudemd item(s)
|
|
103
110
|
`);
|
|
104
111
|
} catch (err) {
|
|
105
112
|
spinner.fail("Installation failed");
|
|
@@ -117,7 +124,7 @@ async function main() {
|
|
|
117
124
|
return;
|
|
118
125
|
}
|
|
119
126
|
if (args.includes("--uninstall") || args.includes("-u")) {
|
|
120
|
-
const { runUninstallCli } = await import("./uninstall-cli-
|
|
127
|
+
const { runUninstallCli } = await import("./uninstall-cli-7XGNDIUC.js");
|
|
121
128
|
await runUninstallCli();
|
|
122
129
|
return;
|
|
123
130
|
}
|
|
@@ -127,7 +134,7 @@ async function main() {
|
|
|
127
134
|
await installAll(scope);
|
|
128
135
|
return;
|
|
129
136
|
}
|
|
130
|
-
const { runCli } = await import("./cli-
|
|
137
|
+
const { runCli } = await import("./cli-6WQMAFNA.js");
|
|
131
138
|
await runCli();
|
|
132
139
|
}
|
|
133
140
|
main().catch((err) => {
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAgents,
|
|
3
|
+
getClaudemdItems,
|
|
3
4
|
getCommands,
|
|
4
5
|
getHooks,
|
|
5
6
|
getMcpServers,
|
|
6
7
|
getRuleCategories,
|
|
7
8
|
getSkills
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-OLLOS3GG.js";
|
|
9
10
|
import "./chunk-5XOKKPAA.js";
|
|
10
11
|
export {
|
|
11
12
|
getAgents,
|
|
13
|
+
getClaudemdItems,
|
|
12
14
|
getCommands,
|
|
13
15
|
getHooks,
|
|
14
16
|
getMcpServers,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAgents,
|
|
3
|
+
getClaudemdItems,
|
|
3
4
|
getCommands,
|
|
4
5
|
getHooks,
|
|
5
6
|
getMcpServers,
|
|
6
7
|
getRuleCategories,
|
|
7
8
|
getSkills
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-OLLOS3GG.js";
|
|
9
10
|
import {
|
|
10
11
|
getAssetsDir,
|
|
11
12
|
getGlobalDir,
|
|
@@ -16,7 +17,6 @@ import {
|
|
|
16
17
|
import { checkbox, select, confirm } from "@inquirer/prompts";
|
|
17
18
|
import chalk from "chalk";
|
|
18
19
|
import ora from "ora";
|
|
19
|
-
import { join as join8 } from "path";
|
|
20
20
|
|
|
21
21
|
// src/scanner.ts
|
|
22
22
|
import { readdir, readFile, access } from "fs/promises";
|
|
@@ -75,6 +75,19 @@ async function getInstalledHooks(targetDir) {
|
|
|
75
75
|
return registryHooks.filter((h) => installedFiles.includes(h.file));
|
|
76
76
|
}
|
|
77
77
|
async function getInstalledRuleCategories(targetDir) {
|
|
78
|
+
const rulesDir = join(targetDir, "rules");
|
|
79
|
+
let installedDirs = [];
|
|
80
|
+
try {
|
|
81
|
+
const entries = await readdir(rulesDir, { withFileTypes: true });
|
|
82
|
+
installedDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
83
|
+
} catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const allCategories = await getRuleCategories();
|
|
87
|
+
const registryCategoryNames = new Set(allCategories.map((rc) => rc.category));
|
|
88
|
+
return installedDirs.filter((d) => registryCategoryNames.has(d));
|
|
89
|
+
}
|
|
90
|
+
async function getInstalledClaudemd(targetDir) {
|
|
78
91
|
const claudeMdPath = join(targetDir, "CLAUDE.md");
|
|
79
92
|
let content = "";
|
|
80
93
|
try {
|
|
@@ -88,25 +101,20 @@ async function getInstalledRuleCategories(targetDir) {
|
|
|
88
101
|
const match = line.match(/^(#{1,2}\s+.+)/);
|
|
89
102
|
if (match) installedHeaders.add(match[1].trim());
|
|
90
103
|
}
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
for (const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (headerMatch && installedHeaders.has(headerMatch[1].trim())) {
|
|
101
|
-
found = true;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
} catch {
|
|
104
|
+
const allItems = await getClaudemdItems();
|
|
105
|
+
const result = [];
|
|
106
|
+
for (const item of allItems) {
|
|
107
|
+
try {
|
|
108
|
+
const raw = await readFile(join(getAssetsDir(), "claudemd", item.file), "utf-8");
|
|
109
|
+
const itemContent = raw.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "");
|
|
110
|
+
const headerMatch = itemContent.match(/^(#{1,2}\s+.+)/m);
|
|
111
|
+
if (headerMatch && installedHeaders.has(headerMatch[1].trim())) {
|
|
112
|
+
result.push(item);
|
|
105
113
|
}
|
|
114
|
+
} catch {
|
|
106
115
|
}
|
|
107
|
-
if (found) installedCategories.push(rc.category);
|
|
108
116
|
}
|
|
109
|
-
return
|
|
117
|
+
return result;
|
|
110
118
|
}
|
|
111
119
|
async function getInstalledMcps(scope) {
|
|
112
120
|
const filePath = getClaudeJsonPath(scope);
|
|
@@ -176,7 +184,7 @@ async function uninstallCommands(commands, targetDir) {
|
|
|
176
184
|
import { join as join5 } from "path";
|
|
177
185
|
import { rm as rm4, readFile as readFile2, writeFile, access as access5 } from "fs/promises";
|
|
178
186
|
import { constants as constants5 } from "fs";
|
|
179
|
-
async function removeHookFromSettings(settingsPath,
|
|
187
|
+
async function removeHookFromSettings(settingsPath, hookFileName) {
|
|
180
188
|
let settings = {};
|
|
181
189
|
try {
|
|
182
190
|
await access5(settingsPath, constants5.F_OK);
|
|
@@ -186,11 +194,12 @@ async function removeHookFromSettings(settingsPath, hookCommand) {
|
|
|
186
194
|
return;
|
|
187
195
|
}
|
|
188
196
|
if (!settings.hooks) return;
|
|
197
|
+
const suffix = `/hooks/${hookFileName}`;
|
|
189
198
|
for (const hookType of Object.keys(settings.hooks)) {
|
|
190
199
|
const matchers = settings.hooks[hookType];
|
|
191
200
|
for (let i = matchers.length - 1; i >= 0; i--) {
|
|
192
201
|
const matcher = matchers[i];
|
|
193
|
-
matcher.hooks = matcher.hooks.filter((h) => h.command
|
|
202
|
+
matcher.hooks = matcher.hooks.filter((h) => !h.command.endsWith(suffix));
|
|
194
203
|
if (matcher.hooks.length === 0) {
|
|
195
204
|
matchers.splice(i, 1);
|
|
196
205
|
}
|
|
@@ -209,8 +218,7 @@ async function uninstallHooks(hooks, targetDir) {
|
|
|
209
218
|
const settingsPath = join5(targetDir, "settings.json");
|
|
210
219
|
for (const hook of hooks) {
|
|
211
220
|
const filePath = join5(hooksDir, hook.file);
|
|
212
|
-
|
|
213
|
-
await removeHookFromSettings(settingsPath, hookCommand);
|
|
221
|
+
await removeHookFromSettings(settingsPath, hook.file);
|
|
214
222
|
try {
|
|
215
223
|
await access5(filePath, constants5.F_OK);
|
|
216
224
|
await rm4(filePath);
|
|
@@ -220,69 +228,47 @@ async function uninstallHooks(hooks, targetDir) {
|
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
// src/uninstallers/rules.ts
|
|
223
|
-
import {
|
|
224
|
-
import { constants as constants6 } from "fs";
|
|
231
|
+
import { readdir as readdir2, rm as rm5 } from "fs/promises";
|
|
225
232
|
import { join as join6 } from "path";
|
|
226
|
-
async function uninstallRules(categories,
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
await access6(claudeMdPath, constants6.F_OK);
|
|
230
|
-
existing = await readFile3(claudeMdPath, "utf-8");
|
|
231
|
-
} catch {
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const headersToRemove = /* @__PURE__ */ new Set();
|
|
233
|
+
async function uninstallRules(categories, targetDir) {
|
|
234
|
+
const assetsRulesDir = join6(getAssetsDir(), "rules");
|
|
235
235
|
for (const category of categories) {
|
|
236
|
-
const
|
|
237
|
-
let
|
|
236
|
+
const srcCatDir = join6(assetsRulesDir, category);
|
|
237
|
+
let assetFiles;
|
|
238
238
|
try {
|
|
239
|
-
|
|
240
|
-
files = (await readdir2(catDir)).filter((f) => f.endsWith(".md"));
|
|
239
|
+
assetFiles = (await readdir2(srcCatDir)).filter((f) => f.endsWith(".md"));
|
|
241
240
|
} catch {
|
|
242
241
|
continue;
|
|
243
242
|
}
|
|
244
|
-
|
|
243
|
+
const destCatDir = join6(targetDir, "rules", category);
|
|
244
|
+
for (const file of assetFiles) {
|
|
245
245
|
try {
|
|
246
|
-
|
|
247
|
-
const headerMatch = content.match(/^(#{1,2}\s+.+)/m);
|
|
248
|
-
if (headerMatch) {
|
|
249
|
-
headersToRemove.add(headerMatch[1].trim());
|
|
250
|
-
}
|
|
246
|
+
await rm5(join6(destCatDir, file));
|
|
251
247
|
} catch {
|
|
252
248
|
}
|
|
253
249
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
let inRemovedSection = false;
|
|
259
|
-
let currentDepth = 0;
|
|
260
|
-
for (const line of lines) {
|
|
261
|
-
const headerMatch = line.match(/^(#{1,2})\s+/);
|
|
262
|
-
if (headerMatch) {
|
|
263
|
-
const depth = headerMatch[1].length;
|
|
264
|
-
const trimmed = line.trim();
|
|
265
|
-
if (headersToRemove.has(trimmed)) {
|
|
266
|
-
inRemovedSection = true;
|
|
267
|
-
currentDepth = depth;
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
if (inRemovedSection && depth <= currentDepth) {
|
|
271
|
-
inRemovedSection = false;
|
|
250
|
+
try {
|
|
251
|
+
const remaining = await readdir2(destCatDir);
|
|
252
|
+
if (remaining.length === 0) {
|
|
253
|
+
await rm5(destCatDir, { recursive: true });
|
|
272
254
|
}
|
|
255
|
+
} catch {
|
|
273
256
|
}
|
|
274
|
-
|
|
275
|
-
|
|
257
|
+
}
|
|
258
|
+
const rulesDir = join6(targetDir, "rules");
|
|
259
|
+
try {
|
|
260
|
+
const remaining = await readdir2(rulesDir);
|
|
261
|
+
if (remaining.length === 0) {
|
|
262
|
+
await rm5(rulesDir, { recursive: true });
|
|
276
263
|
}
|
|
264
|
+
} catch {
|
|
277
265
|
}
|
|
278
|
-
const cleaned = result.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
279
|
-
await writeFile2(claudeMdPath, cleaned, "utf-8");
|
|
280
266
|
}
|
|
281
267
|
|
|
282
268
|
// src/uninstallers/mcps.ts
|
|
283
269
|
import { join as join7 } from "path";
|
|
284
|
-
import { readFile as
|
|
285
|
-
import { constants as
|
|
270
|
+
import { readFile as readFile3, writeFile as writeFile2, access as access6 } from "fs/promises";
|
|
271
|
+
import { constants as constants6 } from "fs";
|
|
286
272
|
import { homedir as homedir2 } from "os";
|
|
287
273
|
function getClaudeJsonPath2(scope) {
|
|
288
274
|
if (scope === "global") {
|
|
@@ -294,8 +280,8 @@ async function uninstallMcps(serverNames, scope) {
|
|
|
294
280
|
const filePath = getClaudeJsonPath2(scope);
|
|
295
281
|
let claudeJson = {};
|
|
296
282
|
try {
|
|
297
|
-
await
|
|
298
|
-
const content = await
|
|
283
|
+
await access6(filePath, constants6.F_OK);
|
|
284
|
+
const content = await readFile3(filePath, "utf-8");
|
|
299
285
|
claudeJson = JSON.parse(content);
|
|
300
286
|
} catch {
|
|
301
287
|
return;
|
|
@@ -304,7 +290,59 @@ async function uninstallMcps(serverNames, scope) {
|
|
|
304
290
|
for (const name of serverNames) {
|
|
305
291
|
delete claudeJson.mcpServers[name];
|
|
306
292
|
}
|
|
307
|
-
await
|
|
293
|
+
await writeFile2(filePath, JSON.stringify(claudeJson, null, 2) + "\n", "utf-8");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/uninstallers/claudemd.ts
|
|
297
|
+
import { readFile as readFile4, writeFile as writeFile3, access as access7 } from "fs/promises";
|
|
298
|
+
import { constants as constants7 } from "fs";
|
|
299
|
+
import { join as join8 } from "path";
|
|
300
|
+
async function uninstallClaudemd(items, targetDir) {
|
|
301
|
+
const claudeMdPath = join8(targetDir, "CLAUDE.md");
|
|
302
|
+
let existing = "";
|
|
303
|
+
try {
|
|
304
|
+
await access7(claudeMdPath, constants7.F_OK);
|
|
305
|
+
existing = await readFile4(claudeMdPath, "utf-8");
|
|
306
|
+
} catch {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const headersToRemove = /* @__PURE__ */ new Set();
|
|
310
|
+
for (const item of items) {
|
|
311
|
+
try {
|
|
312
|
+
const raw = await readFile4(join8(getAssetsDir(), "claudemd", item.file), "utf-8");
|
|
313
|
+
const content = raw.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "");
|
|
314
|
+
const headerMatch = content.match(/^(#{1,2}\s+.+)/m);
|
|
315
|
+
if (headerMatch) {
|
|
316
|
+
headersToRemove.add(headerMatch[1].trim());
|
|
317
|
+
}
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (headersToRemove.size === 0) return;
|
|
322
|
+
const lines = existing.split("\n");
|
|
323
|
+
const result = [];
|
|
324
|
+
let inRemovedSection = false;
|
|
325
|
+
let currentDepth = 0;
|
|
326
|
+
for (const line of lines) {
|
|
327
|
+
const headerMatch = line.match(/^(#{1,2})\s+/);
|
|
328
|
+
if (headerMatch) {
|
|
329
|
+
const depth = headerMatch[1].length;
|
|
330
|
+
const trimmed = line.trim();
|
|
331
|
+
if (headersToRemove.has(trimmed)) {
|
|
332
|
+
inRemovedSection = true;
|
|
333
|
+
currentDepth = depth;
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (inRemovedSection && depth <= currentDepth) {
|
|
337
|
+
inRemovedSection = false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (!inRemovedSection) {
|
|
341
|
+
result.push(line);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const cleaned = result.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
345
|
+
await writeFile3(claudeMdPath, cleaned, "utf-8");
|
|
308
346
|
}
|
|
309
347
|
|
|
310
348
|
// src/uninstall-cli.ts
|
|
@@ -344,16 +382,18 @@ async function runUninstallCli() {
|
|
|
344
382
|
installedCommands,
|
|
345
383
|
installedHooks,
|
|
346
384
|
installedRuleCategories,
|
|
347
|
-
installedMcpNames
|
|
385
|
+
installedMcpNames,
|
|
386
|
+
installedClaudemds
|
|
348
387
|
] = await Promise.all([
|
|
349
388
|
getInstalledAgents(targetDir),
|
|
350
389
|
getInstalledSkills(targetDir),
|
|
351
390
|
getInstalledCommands(targetDir),
|
|
352
391
|
getInstalledHooks(targetDir),
|
|
353
392
|
getInstalledRuleCategories(targetDir),
|
|
354
|
-
getInstalledMcps(scope)
|
|
393
|
+
getInstalledMcps(scope),
|
|
394
|
+
getInstalledClaudemd(targetDir)
|
|
355
395
|
]);
|
|
356
|
-
const totalInstalled = installedAgents.length + installedSkills.length + installedCommands.length + installedHooks.length + installedRuleCategories.length + installedMcpNames.length;
|
|
396
|
+
const totalInstalled = installedAgents.length + installedSkills.length + installedCommands.length + installedHooks.length + installedRuleCategories.length + installedMcpNames.length + installedClaudemds.length;
|
|
357
397
|
if (totalInstalled === 0) {
|
|
358
398
|
console.log(chalk.yellow(" Nothing installed. Exiting."));
|
|
359
399
|
return;
|
|
@@ -389,6 +429,11 @@ async function runUninstallCli() {
|
|
|
389
429
|
name: `MCPs ${chalk.gray(`(${installedMcpNames.length} installed)`)}`,
|
|
390
430
|
value: "mcps"
|
|
391
431
|
});
|
|
432
|
+
if (installedClaudemds.length > 0)
|
|
433
|
+
availableCategories.push({
|
|
434
|
+
name: `ClaudeMd ${chalk.gray(`(${installedClaudemds.length} installed)`)}`,
|
|
435
|
+
value: "claudemd"
|
|
436
|
+
});
|
|
392
437
|
const selectedCategories = await checkbox({
|
|
393
438
|
message: "Select categories to uninstall:",
|
|
394
439
|
choices: availableCategories
|
|
@@ -404,7 +449,8 @@ async function runUninstallCli() {
|
|
|
404
449
|
commands: [],
|
|
405
450
|
hooks: [],
|
|
406
451
|
ruleCategories: [],
|
|
407
|
-
mcpNames: []
|
|
452
|
+
mcpNames: [],
|
|
453
|
+
claudemds: []
|
|
408
454
|
};
|
|
409
455
|
if (selectedCategories.includes("agents") && installedAgents.length > 0) {
|
|
410
456
|
plan.agents = await checkbox({
|
|
@@ -466,7 +512,17 @@ async function runUninstallCli() {
|
|
|
466
512
|
}))
|
|
467
513
|
});
|
|
468
514
|
}
|
|
469
|
-
|
|
515
|
+
if (selectedCategories.includes("claudemd") && installedClaudemds.length > 0) {
|
|
516
|
+
plan.claudemds = await checkbox({
|
|
517
|
+
message: "Select claudemd items to uninstall:",
|
|
518
|
+
choices: installedClaudemds.map((c) => ({
|
|
519
|
+
name: `${chalk.bold(c.name.padEnd(35))} ${chalk.gray(c.description.slice(0, 55))}`,
|
|
520
|
+
value: c,
|
|
521
|
+
checked: false
|
|
522
|
+
}))
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
const totalToRemove = plan.agents.length + plan.skills.length + plan.commands.length + plan.hooks.length + plan.ruleCategories.length + plan.mcpNames.length + plan.claudemds.length;
|
|
470
526
|
if (totalToRemove === 0) {
|
|
471
527
|
console.log(chalk.yellow("\n Nothing selected. Exiting."));
|
|
472
528
|
return;
|
|
@@ -482,6 +538,7 @@ async function runUninstallCli() {
|
|
|
482
538
|
if (plan.ruleCategories.length)
|
|
483
539
|
console.log(` Rules : ${plan.ruleCategories.join(", ")}`);
|
|
484
540
|
if (plan.mcpNames.length) console.log(` MCPs : ${plan.mcpNames.join(", ")}`);
|
|
541
|
+
if (plan.claudemds.length) console.log(` ClaudeMd: ${plan.claudemds.map((c) => c.name).join(", ")}`);
|
|
485
542
|
console.log("");
|
|
486
543
|
const ok = await confirm({
|
|
487
544
|
message: chalk.red("Proceed with uninstallation? This cannot be undone."),
|
|
@@ -512,13 +569,16 @@ async function runUninstallCli() {
|
|
|
512
569
|
}
|
|
513
570
|
if (plan.ruleCategories.length) {
|
|
514
571
|
spinner.text = "Removing rules...";
|
|
515
|
-
|
|
516
|
-
await uninstallRules(plan.ruleCategories, claudeMd);
|
|
572
|
+
await uninstallRules(plan.ruleCategories, targetDir);
|
|
517
573
|
}
|
|
518
574
|
if (plan.mcpNames.length) {
|
|
519
575
|
spinner.text = "Removing MCP servers...";
|
|
520
576
|
await uninstallMcps(plan.mcpNames, scope);
|
|
521
577
|
}
|
|
578
|
+
if (plan.claudemds.length) {
|
|
579
|
+
spinner.text = "Removing claudemd...";
|
|
580
|
+
await uninstallClaudemd(plan.claudemds, targetDir);
|
|
581
|
+
}
|
|
522
582
|
spinner.succeed(chalk.green("Uninstallation complete!"));
|
|
523
583
|
console.log("");
|
|
524
584
|
if (plan.agents.length)
|
|
@@ -533,6 +593,8 @@ async function runUninstallCli() {
|
|
|
533
593
|
console.log(` ${chalk.red("\u2717")} Rules removed (${plan.ruleCategories.join(", ")})`);
|
|
534
594
|
if (plan.mcpNames.length)
|
|
535
595
|
console.log(` ${chalk.red("\u2717")} ${plan.mcpNames.length} MCP server(s) removed`);
|
|
596
|
+
if (plan.claudemds.length)
|
|
597
|
+
console.log(` ${chalk.red("\u2717")} ClaudeMd removed (${plan.claudemds.map((c) => c.name).join(", ")})`);
|
|
536
598
|
console.log("");
|
|
537
599
|
} catch (err) {
|
|
538
600
|
spinner.fail(chalk.red("Uninstallation failed"));
|
package/package.json
CHANGED
package/dist/chunk-RMUKD7CW.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getAssetsDir
|
|
3
|
-
} from "./chunk-5XOKKPAA.js";
|
|
4
|
-
|
|
5
|
-
// src/installers/rules.ts
|
|
6
|
-
import { join } from "path";
|
|
7
|
-
import { readFile, writeFile, access } from "fs/promises";
|
|
8
|
-
import { constants } from "fs";
|
|
9
|
-
async function installRules(categories, targetClaudeMd) {
|
|
10
|
-
const rulesDir = join(getAssetsDir(), "rules");
|
|
11
|
-
let existing = "";
|
|
12
|
-
try {
|
|
13
|
-
await access(targetClaudeMd, constants.F_OK);
|
|
14
|
-
existing = await readFile(targetClaudeMd, "utf-8");
|
|
15
|
-
} catch {
|
|
16
|
-
}
|
|
17
|
-
const toAppend = [];
|
|
18
|
-
for (const category of categories) {
|
|
19
|
-
const catDir = join(rulesDir, category);
|
|
20
|
-
let files;
|
|
21
|
-
try {
|
|
22
|
-
const { readdir } = await import("fs/promises");
|
|
23
|
-
files = (await readdir(catDir)).filter((f) => f.endsWith(".md"));
|
|
24
|
-
} catch {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
for (const file of files) {
|
|
28
|
-
const content = await readFile(join(catDir, file), "utf-8");
|
|
29
|
-
const headerMatch = content.match(/^#{1,2}\s+(.+)/m);
|
|
30
|
-
if (headerMatch) {
|
|
31
|
-
const header = headerMatch[0];
|
|
32
|
-
if (existing.includes(header)) continue;
|
|
33
|
-
}
|
|
34
|
-
toAppend.push(content.trim());
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if (toAppend.length === 0) return;
|
|
38
|
-
const appendContent = (existing.endsWith("\n") ? "" : "\n") + toAppend.join("\n\n") + "\n";
|
|
39
|
-
await writeFile(targetClaudeMd, existing + appendContent, "utf-8");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export {
|
|
43
|
-
installRules
|
|
44
|
-
};
|