@juho0719/cckit 0.1.1 → 0.2.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/assets/claudemd/CLAUDE.md +69 -0
- package/dist/{chunk-6B46AIFM.js → chunk-OLLOS3GG.js} +16 -0
- package/dist/chunk-SW3OJLHC.js +35 -0
- package/dist/claudemd-KKQ2DL7P.js +7 -0
- package/dist/{cli-VZRGF733.js → cli-D2Q5QUO7.js} +56 -8
- package/dist/index.js +34 -19
- package/dist/{registry-EGXWYWWK.js → registry-BU55RMHU.js} +3 -1
- package/dist/uninstall-cli-VCOZGDBM.js +651 -0
- package/package.json +1 -1
|
@@ -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.
|
|
@@ -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
|
};
|
|
@@ -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";
|
|
@@ -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,8 +94,10 @@ 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
|
};
|
|
100
|
+
const skippedMcps = /* @__PURE__ */ new Map();
|
|
94
101
|
if (selectedCategories.includes("agents")) {
|
|
95
102
|
const allAgents = await getAgents();
|
|
96
103
|
plan.agents = await checkbox({
|
|
@@ -146,6 +153,17 @@ async function runCli() {
|
|
|
146
153
|
}))
|
|
147
154
|
});
|
|
148
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
|
+
}
|
|
149
167
|
if (selectedCategories.includes("mcps")) {
|
|
150
168
|
const allMcps = await getMcpServers();
|
|
151
169
|
plan.mcps = await checkbox({
|
|
@@ -161,14 +179,29 @@ async function runCli() {
|
|
|
161
179
|
console.log(chalk.yellow(`
|
|
162
180
|
${server.name} requires environment variables:`));
|
|
163
181
|
const envMap = {};
|
|
182
|
+
const skippedKeys = [];
|
|
164
183
|
for (const envKey of server.envVars) {
|
|
165
|
-
const
|
|
166
|
-
message: ` ${envKey}
|
|
167
|
-
|
|
184
|
+
const action = await select({
|
|
185
|
+
message: ` How do you want to set ${chalk.bold(envKey)}?`,
|
|
186
|
+
choices: [
|
|
187
|
+
{ name: "Enter value now", value: "enter" },
|
|
188
|
+
{ name: `Skip ${chalk.gray(`(keep placeholder: YOUR_${envKey}_HERE)`)}`, value: "skip" }
|
|
189
|
+
]
|
|
168
190
|
});
|
|
169
|
-
if (
|
|
191
|
+
if (action === "skip") {
|
|
192
|
+
skippedKeys.push(envKey);
|
|
193
|
+
} else {
|
|
194
|
+
const value = await input({
|
|
195
|
+
message: ` ${envKey}:`,
|
|
196
|
+
default: ""
|
|
197
|
+
});
|
|
198
|
+
if (value) envMap[envKey] = value;
|
|
199
|
+
}
|
|
170
200
|
}
|
|
171
201
|
plan.mcpEnvValues.set(server.name, envMap);
|
|
202
|
+
if (skippedKeys.length > 0) {
|
|
203
|
+
skippedMcps.set(server.name, skippedKeys);
|
|
204
|
+
}
|
|
172
205
|
}
|
|
173
206
|
}
|
|
174
207
|
console.log(chalk.bold("\n Installation Summary"));
|
|
@@ -180,8 +213,9 @@ async function runCli() {
|
|
|
180
213
|
if (plan.hooks.length) console.log(` Hooks : ${plan.hooks.map((h) => h.name).join(", ")}`);
|
|
181
214
|
if (plan.ruleCategories.length) console.log(` Rules : ${plan.ruleCategories.join(", ")}`);
|
|
182
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(", ")}`);
|
|
183
217
|
console.log("");
|
|
184
|
-
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;
|
|
185
219
|
if (totalItems === 0) {
|
|
186
220
|
console.log(chalk.yellow(" Nothing selected. Exiting."));
|
|
187
221
|
return;
|
|
@@ -219,6 +253,10 @@ async function runCli() {
|
|
|
219
253
|
spinner.text = "Installing MCP servers...";
|
|
220
254
|
await installMcps(plan.mcps, plan.scope, plan.mcpEnvValues);
|
|
221
255
|
}
|
|
256
|
+
if (plan.claudemds.length) {
|
|
257
|
+
spinner.text = "Installing claudemd...";
|
|
258
|
+
await installClaudemd(plan.claudemds, targetDir);
|
|
259
|
+
}
|
|
222
260
|
spinner.succeed(chalk.green("Installation complete!"));
|
|
223
261
|
console.log("");
|
|
224
262
|
if (plan.agents.length) console.log(` ${chalk.green("\u2713")} ${plan.agents.length} agent(s) \u2192 ${join(targetDir, "agents")}`);
|
|
@@ -227,7 +265,17 @@ async function runCli() {
|
|
|
227
265
|
if (plan.hooks.length) console.log(` ${chalk.green("\u2713")} ${plan.hooks.length} hook(s) \u2192 ${join(targetDir, "hooks")}`);
|
|
228
266
|
if (plan.ruleCategories.length) console.log(` ${chalk.green("\u2713")} Rules appended \u2192 ${join(targetDir, "CLAUDE.md")}`);
|
|
229
267
|
if (plan.mcps.length) console.log(` ${chalk.green("\u2713")} ${plan.mcps.length} MCP server(s) \u2192 ${scope === "global" ? "~/.claude.json" : "./.claude.json"}`);
|
|
268
|
+
if (plan.claudemds.length) console.log(` ${chalk.green("\u2713")} ClaudeMd appended \u2192 ${join(targetDir, "CLAUDE.md")}`);
|
|
230
269
|
console.log("");
|
|
270
|
+
if (skippedMcps.size > 0) {
|
|
271
|
+
console.log(chalk.yellow(" \u26A0 The following MCP servers have placeholder env vars:"));
|
|
272
|
+
for (const [serverName, keys] of skippedMcps) {
|
|
273
|
+
console.log(` ${chalk.bold("-")} ${serverName} ${chalk.gray(`(${keys.join(", ")})`)} `);
|
|
274
|
+
}
|
|
275
|
+
const configPath = scope === "global" ? "~/.claude.json" : "./.claude.json";
|
|
276
|
+
console.log(chalk.gray(` \u2192 Edit ${configPath} to set the actual values`));
|
|
277
|
+
console.log("");
|
|
278
|
+
}
|
|
231
279
|
} catch (err) {
|
|
232
280
|
spinner.fail(chalk.red("Installation failed"));
|
|
233
281
|
throw err;
|
package/dist/index.js
CHANGED
|
@@ -20,15 +20,17 @@ function printHelp() {
|
|
|
20
20
|
cckit - Claude Code Harness Installer v${getVersion()}
|
|
21
21
|
|
|
22
22
|
Usage:
|
|
23
|
-
cckit
|
|
24
|
-
cckit --
|
|
25
|
-
cckit --
|
|
26
|
-
cckit --
|
|
23
|
+
cckit Interactive installation wizard
|
|
24
|
+
cckit --uninstall Interactive uninstall wizard
|
|
25
|
+
cckit --all Install all items (global scope)
|
|
26
|
+
cckit --version Show version
|
|
27
|
+
cckit --help Show this help
|
|
27
28
|
|
|
28
29
|
Options:
|
|
29
|
-
--
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
--uninstall, -u Launch interactive uninstall wizard
|
|
31
|
+
--all Install all agents, skills, commands, hooks, rules, and MCPs
|
|
32
|
+
to ~/.claude/ without interactive prompts
|
|
33
|
+
--scope Scope for --all flag: global (default) or project
|
|
32
34
|
`);
|
|
33
35
|
}
|
|
34
36
|
async function installAll(scope) {
|
|
@@ -49,33 +51,38 @@ Installing all to ${targetDir}...
|
|
|
49
51
|
{ getHooks },
|
|
50
52
|
{ getRuleCategories },
|
|
51
53
|
{ getMcpServers },
|
|
54
|
+
{ getClaudemdItems },
|
|
52
55
|
{ installAgents },
|
|
53
56
|
{ installSkills },
|
|
54
57
|
{ installCommands },
|
|
55
58
|
{ installHooks },
|
|
56
59
|
{ installRules },
|
|
57
|
-
{ installMcps }
|
|
60
|
+
{ installMcps },
|
|
61
|
+
{ installClaudemd }
|
|
58
62
|
] = await Promise.all([
|
|
59
|
-
import("./registry-
|
|
60
|
-
import("./registry-
|
|
61
|
-
import("./registry-
|
|
62
|
-
import("./registry-
|
|
63
|
-
import("./registry-
|
|
64
|
-
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"),
|
|
65
70
|
import("./agents-AEKT67A6.js"),
|
|
66
71
|
import("./skills-ULMW3UCM.js"),
|
|
67
72
|
import("./commands-P5LILVZ5.js"),
|
|
68
73
|
import("./hooks-IIG2XK4I.js"),
|
|
69
74
|
import("./rules-2CPBVNNJ.js"),
|
|
70
|
-
import("./mcps-67Q7TBGW.js")
|
|
75
|
+
import("./mcps-67Q7TBGW.js"),
|
|
76
|
+
import("./claudemd-KKQ2DL7P.js")
|
|
71
77
|
]);
|
|
72
|
-
const [agents, skills, commands, hooks, ruleCategories, mcps] = await Promise.all([
|
|
78
|
+
const [agents, skills, commands, hooks, ruleCategories, mcps, claudemds] = await Promise.all([
|
|
73
79
|
getAgents(),
|
|
74
80
|
getSkills(),
|
|
75
81
|
getCommands(),
|
|
76
82
|
getHooks(),
|
|
77
83
|
getRuleCategories(),
|
|
78
|
-
getMcpServers()
|
|
84
|
+
getMcpServers(),
|
|
85
|
+
getClaudemdItems()
|
|
79
86
|
]);
|
|
80
87
|
spinner.text = "Installing agents...";
|
|
81
88
|
await installAgents(agents, targetDir);
|
|
@@ -90,6 +97,8 @@ Installing all to ${targetDir}...
|
|
|
90
97
|
await installRules(ruleCategories.map((rc) => rc.category), claudeMd);
|
|
91
98
|
spinner.text = "Installing MCP servers...";
|
|
92
99
|
await installMcps(mcps, scope, /* @__PURE__ */ new Map());
|
|
100
|
+
spinner.text = "Installing claudemd...";
|
|
101
|
+
await installClaudemd(claudemds, targetDir);
|
|
93
102
|
spinner.succeed(chalk.green("All items installed successfully!"));
|
|
94
103
|
console.log(`
|
|
95
104
|
${chalk.green("\u2713")} ${agents.length} agents`);
|
|
@@ -97,7 +106,8 @@ Installing all to ${targetDir}...
|
|
|
97
106
|
console.log(` ${chalk.green("\u2713")} ${commands.length} commands`);
|
|
98
107
|
console.log(` ${chalk.green("\u2713")} ${hooks.length} hooks`);
|
|
99
108
|
console.log(` ${chalk.green("\u2713")} Rules (${ruleCategories.length} categories)`);
|
|
100
|
-
console.log(` ${chalk.green("\u2713")} ${mcps.length} MCP servers
|
|
109
|
+
console.log(` ${chalk.green("\u2713")} ${mcps.length} MCP servers`);
|
|
110
|
+
console.log(` ${chalk.green("\u2713")} ${claudemds.length} claudemd item(s)
|
|
101
111
|
`);
|
|
102
112
|
} catch (err) {
|
|
103
113
|
spinner.fail("Installation failed");
|
|
@@ -114,13 +124,18 @@ async function main() {
|
|
|
114
124
|
printHelp();
|
|
115
125
|
return;
|
|
116
126
|
}
|
|
127
|
+
if (args.includes("--uninstall") || args.includes("-u")) {
|
|
128
|
+
const { runUninstallCli } = await import("./uninstall-cli-VCOZGDBM.js");
|
|
129
|
+
await runUninstallCli();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
117
132
|
if (args.includes("--all")) {
|
|
118
133
|
const scopeIdx = args.indexOf("--scope");
|
|
119
134
|
const scope = scopeIdx !== -1 && args[scopeIdx + 1] === "project" ? "project" : "global";
|
|
120
135
|
await installAll(scope);
|
|
121
136
|
return;
|
|
122
137
|
}
|
|
123
|
-
const { runCli } = await import("./cli-
|
|
138
|
+
const { runCli } = await import("./cli-D2Q5QUO7.js");
|
|
124
139
|
await runCli();
|
|
125
140
|
}
|
|
126
141
|
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,
|
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAgents,
|
|
3
|
+
getClaudemdItems,
|
|
4
|
+
getCommands,
|
|
5
|
+
getHooks,
|
|
6
|
+
getMcpServers,
|
|
7
|
+
getRuleCategories,
|
|
8
|
+
getSkills
|
|
9
|
+
} from "./chunk-OLLOS3GG.js";
|
|
10
|
+
import {
|
|
11
|
+
getAssetsDir,
|
|
12
|
+
getGlobalDir,
|
|
13
|
+
getProjectDir
|
|
14
|
+
} from "./chunk-5XOKKPAA.js";
|
|
15
|
+
|
|
16
|
+
// src/uninstall-cli.ts
|
|
17
|
+
import { checkbox, select, confirm } from "@inquirer/prompts";
|
|
18
|
+
import chalk from "chalk";
|
|
19
|
+
import ora from "ora";
|
|
20
|
+
import { join as join9 } from "path";
|
|
21
|
+
|
|
22
|
+
// src/scanner.ts
|
|
23
|
+
import { readdir, readFile, access } from "fs/promises";
|
|
24
|
+
import { constants } from "fs";
|
|
25
|
+
import { join } from "path";
|
|
26
|
+
import { homedir } from "os";
|
|
27
|
+
function getClaudeJsonPath(scope) {
|
|
28
|
+
if (scope === "global") {
|
|
29
|
+
return join(homedir(), ".claude.json");
|
|
30
|
+
}
|
|
31
|
+
return join(process.cwd(), ".claude.json");
|
|
32
|
+
}
|
|
33
|
+
async function getInstalledAgents(targetDir) {
|
|
34
|
+
const agentsDir = join(targetDir, "agents");
|
|
35
|
+
let installedFiles = [];
|
|
36
|
+
try {
|
|
37
|
+
installedFiles = (await readdir(agentsDir)).filter((f) => f.endsWith(".md"));
|
|
38
|
+
} catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
const registryAgents = await getAgents();
|
|
42
|
+
return registryAgents.filter((a) => installedFiles.includes(a.file));
|
|
43
|
+
}
|
|
44
|
+
async function getInstalledSkills(targetDir) {
|
|
45
|
+
const skillsDir = join(targetDir, "skills");
|
|
46
|
+
let installedDirs = [];
|
|
47
|
+
try {
|
|
48
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
49
|
+
installedDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
50
|
+
} catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
const registrySkills = await getSkills();
|
|
54
|
+
return registrySkills.filter((s) => installedDirs.includes(s.dir));
|
|
55
|
+
}
|
|
56
|
+
async function getInstalledCommands(targetDir) {
|
|
57
|
+
const commandsDir = join(targetDir, "commands");
|
|
58
|
+
let installedFiles = [];
|
|
59
|
+
try {
|
|
60
|
+
installedFiles = (await readdir(commandsDir)).filter((f) => f.endsWith(".md"));
|
|
61
|
+
} catch {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const registryCommands = await getCommands();
|
|
65
|
+
return registryCommands.filter((c) => installedFiles.includes(c.file));
|
|
66
|
+
}
|
|
67
|
+
async function getInstalledHooks(targetDir) {
|
|
68
|
+
const hooksDir = join(targetDir, "hooks");
|
|
69
|
+
let installedFiles = [];
|
|
70
|
+
try {
|
|
71
|
+
installedFiles = (await readdir(hooksDir)).filter((f) => f.endsWith(".js"));
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const registryHooks = await getHooks();
|
|
76
|
+
return registryHooks.filter((h) => installedFiles.includes(h.file));
|
|
77
|
+
}
|
|
78
|
+
async function getInstalledRuleCategories(targetDir) {
|
|
79
|
+
const claudeMdPath = join(targetDir, "CLAUDE.md");
|
|
80
|
+
let content = "";
|
|
81
|
+
try {
|
|
82
|
+
await access(claudeMdPath, constants.F_OK);
|
|
83
|
+
content = await readFile(claudeMdPath, "utf-8");
|
|
84
|
+
} catch {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
const installedHeaders = /* @__PURE__ */ new Set();
|
|
88
|
+
for (const line of content.split("\n")) {
|
|
89
|
+
const match = line.match(/^(#{1,2}\s+.+)/);
|
|
90
|
+
if (match) installedHeaders.add(match[1].trim());
|
|
91
|
+
}
|
|
92
|
+
const allCategories = await getRuleCategories();
|
|
93
|
+
const installedCategories = [];
|
|
94
|
+
for (const rc of allCategories) {
|
|
95
|
+
const catDir = join(getAssetsDir(), "rules", rc.category);
|
|
96
|
+
let found = false;
|
|
97
|
+
for (const file of rc.files) {
|
|
98
|
+
try {
|
|
99
|
+
const fileContent = await readFile(join(catDir, file), "utf-8");
|
|
100
|
+
const headerMatch = fileContent.match(/^(#{1,2}\s+.+)/m);
|
|
101
|
+
if (headerMatch && installedHeaders.has(headerMatch[1].trim())) {
|
|
102
|
+
found = true;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (found) installedCategories.push(rc.category);
|
|
109
|
+
}
|
|
110
|
+
return installedCategories;
|
|
111
|
+
}
|
|
112
|
+
async function getInstalledClaudemd(targetDir) {
|
|
113
|
+
const claudeMdPath = join(targetDir, "CLAUDE.md");
|
|
114
|
+
let content = "";
|
|
115
|
+
try {
|
|
116
|
+
await access(claudeMdPath, constants.F_OK);
|
|
117
|
+
content = await readFile(claudeMdPath, "utf-8");
|
|
118
|
+
} catch {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
const installedHeaders = /* @__PURE__ */ new Set();
|
|
122
|
+
for (const line of content.split("\n")) {
|
|
123
|
+
const match = line.match(/^(#{1,2}\s+.+)/);
|
|
124
|
+
if (match) installedHeaders.add(match[1].trim());
|
|
125
|
+
}
|
|
126
|
+
const allItems = await getClaudemdItems();
|
|
127
|
+
const result = [];
|
|
128
|
+
for (const item of allItems) {
|
|
129
|
+
try {
|
|
130
|
+
const raw = await readFile(join(getAssetsDir(), "claudemd", item.file), "utf-8");
|
|
131
|
+
const itemContent = raw.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "");
|
|
132
|
+
const headerMatch = itemContent.match(/^(#{1,2}\s+.+)/m);
|
|
133
|
+
if (headerMatch && installedHeaders.has(headerMatch[1].trim())) {
|
|
134
|
+
result.push(item);
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
async function getInstalledMcps(scope) {
|
|
142
|
+
const filePath = getClaudeJsonPath(scope);
|
|
143
|
+
let claudeJson = {};
|
|
144
|
+
try {
|
|
145
|
+
await access(filePath, constants.F_OK);
|
|
146
|
+
const raw = await readFile(filePath, "utf-8");
|
|
147
|
+
claudeJson = JSON.parse(raw);
|
|
148
|
+
} catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
if (!claudeJson.mcpServers) return [];
|
|
152
|
+
const installedNames = new Set(Object.keys(claudeJson.mcpServers));
|
|
153
|
+
const registryMcps = await getMcpServers();
|
|
154
|
+
return registryMcps.filter((m) => installedNames.has(m.name)).map((m) => m.name);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/uninstallers/agents.ts
|
|
158
|
+
import { join as join2 } from "path";
|
|
159
|
+
import { rm, access as access2 } from "fs/promises";
|
|
160
|
+
import { constants as constants2 } from "fs";
|
|
161
|
+
async function uninstallAgents(agents, targetDir) {
|
|
162
|
+
const agentsDir = join2(targetDir, "agents");
|
|
163
|
+
for (const agent of agents) {
|
|
164
|
+
const filePath = join2(agentsDir, agent.file);
|
|
165
|
+
try {
|
|
166
|
+
await access2(filePath, constants2.F_OK);
|
|
167
|
+
await rm(filePath);
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/uninstallers/skills.ts
|
|
174
|
+
import { join as join3 } from "path";
|
|
175
|
+
import { rm as rm2, access as access3 } from "fs/promises";
|
|
176
|
+
import { constants as constants3 } from "fs";
|
|
177
|
+
async function uninstallSkills(skills, targetDir) {
|
|
178
|
+
const skillsDir = join3(targetDir, "skills");
|
|
179
|
+
for (const skill of skills) {
|
|
180
|
+
const dirPath = join3(skillsDir, skill.dir);
|
|
181
|
+
try {
|
|
182
|
+
await access3(dirPath, constants3.F_OK);
|
|
183
|
+
await rm2(dirPath, { recursive: true });
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/uninstallers/commands.ts
|
|
190
|
+
import { join as join4 } from "path";
|
|
191
|
+
import { rm as rm3, access as access4 } from "fs/promises";
|
|
192
|
+
import { constants as constants4 } from "fs";
|
|
193
|
+
async function uninstallCommands(commands, targetDir) {
|
|
194
|
+
const commandsDir = join4(targetDir, "commands");
|
|
195
|
+
for (const cmd of commands) {
|
|
196
|
+
const filePath = join4(commandsDir, cmd.file);
|
|
197
|
+
try {
|
|
198
|
+
await access4(filePath, constants4.F_OK);
|
|
199
|
+
await rm3(filePath);
|
|
200
|
+
} catch {
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/uninstallers/hooks.ts
|
|
206
|
+
import { join as join5 } from "path";
|
|
207
|
+
import { rm as rm4, readFile as readFile2, writeFile, access as access5 } from "fs/promises";
|
|
208
|
+
import { constants as constants5 } from "fs";
|
|
209
|
+
async function removeHookFromSettings(settingsPath, hookCommand) {
|
|
210
|
+
let settings = {};
|
|
211
|
+
try {
|
|
212
|
+
await access5(settingsPath, constants5.F_OK);
|
|
213
|
+
const content = await readFile2(settingsPath, "utf-8");
|
|
214
|
+
settings = JSON.parse(content);
|
|
215
|
+
} catch {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (!settings.hooks) return;
|
|
219
|
+
for (const hookType of Object.keys(settings.hooks)) {
|
|
220
|
+
const matchers = settings.hooks[hookType];
|
|
221
|
+
for (let i = matchers.length - 1; i >= 0; i--) {
|
|
222
|
+
const matcher = matchers[i];
|
|
223
|
+
matcher.hooks = matcher.hooks.filter((h) => h.command !== hookCommand);
|
|
224
|
+
if (matcher.hooks.length === 0) {
|
|
225
|
+
matchers.splice(i, 1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (matchers.length === 0) {
|
|
229
|
+
delete settings.hooks[hookType];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
233
|
+
delete settings.hooks;
|
|
234
|
+
}
|
|
235
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
236
|
+
}
|
|
237
|
+
async function uninstallHooks(hooks, targetDir) {
|
|
238
|
+
const hooksDir = join5(targetDir, "hooks");
|
|
239
|
+
const settingsPath = join5(targetDir, "settings.json");
|
|
240
|
+
for (const hook of hooks) {
|
|
241
|
+
const filePath = join5(hooksDir, hook.file);
|
|
242
|
+
const hookCommand = `node ${filePath}`;
|
|
243
|
+
await removeHookFromSettings(settingsPath, hookCommand);
|
|
244
|
+
try {
|
|
245
|
+
await access5(filePath, constants5.F_OK);
|
|
246
|
+
await rm4(filePath);
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/uninstallers/rules.ts
|
|
253
|
+
import { readFile as readFile3, writeFile as writeFile2, access as access6 } from "fs/promises";
|
|
254
|
+
import { constants as constants6 } from "fs";
|
|
255
|
+
import { join as join6 } from "path";
|
|
256
|
+
async function uninstallRules(categories, claudeMdPath) {
|
|
257
|
+
let existing = "";
|
|
258
|
+
try {
|
|
259
|
+
await access6(claudeMdPath, constants6.F_OK);
|
|
260
|
+
existing = await readFile3(claudeMdPath, "utf-8");
|
|
261
|
+
} catch {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const headersToRemove = /* @__PURE__ */ new Set();
|
|
265
|
+
for (const category of categories) {
|
|
266
|
+
const catDir = join6(getAssetsDir(), "rules", category);
|
|
267
|
+
let files;
|
|
268
|
+
try {
|
|
269
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
270
|
+
files = (await readdir2(catDir)).filter((f) => f.endsWith(".md"));
|
|
271
|
+
} catch {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
for (const file of files) {
|
|
275
|
+
try {
|
|
276
|
+
const content = await readFile3(join6(catDir, file), "utf-8");
|
|
277
|
+
const headerMatch = content.match(/^(#{1,2}\s+.+)/m);
|
|
278
|
+
if (headerMatch) {
|
|
279
|
+
headersToRemove.add(headerMatch[1].trim());
|
|
280
|
+
}
|
|
281
|
+
} catch {
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (headersToRemove.size === 0) return;
|
|
286
|
+
const lines = existing.split("\n");
|
|
287
|
+
const result = [];
|
|
288
|
+
let inRemovedSection = false;
|
|
289
|
+
let currentDepth = 0;
|
|
290
|
+
for (const line of lines) {
|
|
291
|
+
const headerMatch = line.match(/^(#{1,2})\s+/);
|
|
292
|
+
if (headerMatch) {
|
|
293
|
+
const depth = headerMatch[1].length;
|
|
294
|
+
const trimmed = line.trim();
|
|
295
|
+
if (headersToRemove.has(trimmed)) {
|
|
296
|
+
inRemovedSection = true;
|
|
297
|
+
currentDepth = depth;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (inRemovedSection && depth <= currentDepth) {
|
|
301
|
+
inRemovedSection = false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (!inRemovedSection) {
|
|
305
|
+
result.push(line);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const cleaned = result.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
309
|
+
await writeFile2(claudeMdPath, cleaned, "utf-8");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/uninstallers/mcps.ts
|
|
313
|
+
import { join as join7 } from "path";
|
|
314
|
+
import { readFile as readFile4, writeFile as writeFile3, access as access7 } from "fs/promises";
|
|
315
|
+
import { constants as constants7 } from "fs";
|
|
316
|
+
import { homedir as homedir2 } from "os";
|
|
317
|
+
function getClaudeJsonPath2(scope) {
|
|
318
|
+
if (scope === "global") {
|
|
319
|
+
return join7(homedir2(), ".claude.json");
|
|
320
|
+
}
|
|
321
|
+
return join7(process.cwd(), ".claude.json");
|
|
322
|
+
}
|
|
323
|
+
async function uninstallMcps(serverNames, scope) {
|
|
324
|
+
const filePath = getClaudeJsonPath2(scope);
|
|
325
|
+
let claudeJson = {};
|
|
326
|
+
try {
|
|
327
|
+
await access7(filePath, constants7.F_OK);
|
|
328
|
+
const content = await readFile4(filePath, "utf-8");
|
|
329
|
+
claudeJson = JSON.parse(content);
|
|
330
|
+
} catch {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (!claudeJson.mcpServers) return;
|
|
334
|
+
for (const name of serverNames) {
|
|
335
|
+
delete claudeJson.mcpServers[name];
|
|
336
|
+
}
|
|
337
|
+
await writeFile3(filePath, JSON.stringify(claudeJson, null, 2) + "\n", "utf-8");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/uninstallers/claudemd.ts
|
|
341
|
+
import { readFile as readFile5, writeFile as writeFile4, access as access8 } from "fs/promises";
|
|
342
|
+
import { constants as constants8 } from "fs";
|
|
343
|
+
import { join as join8 } from "path";
|
|
344
|
+
async function uninstallClaudemd(items, targetDir) {
|
|
345
|
+
const claudeMdPath = join8(targetDir, "CLAUDE.md");
|
|
346
|
+
let existing = "";
|
|
347
|
+
try {
|
|
348
|
+
await access8(claudeMdPath, constants8.F_OK);
|
|
349
|
+
existing = await readFile5(claudeMdPath, "utf-8");
|
|
350
|
+
} catch {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const headersToRemove = /* @__PURE__ */ new Set();
|
|
354
|
+
for (const item of items) {
|
|
355
|
+
try {
|
|
356
|
+
const raw = await readFile5(join8(getAssetsDir(), "claudemd", item.file), "utf-8");
|
|
357
|
+
const content = raw.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "");
|
|
358
|
+
const headerMatch = content.match(/^(#{1,2}\s+.+)/m);
|
|
359
|
+
if (headerMatch) {
|
|
360
|
+
headersToRemove.add(headerMatch[1].trim());
|
|
361
|
+
}
|
|
362
|
+
} catch {
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (headersToRemove.size === 0) return;
|
|
366
|
+
const lines = existing.split("\n");
|
|
367
|
+
const result = [];
|
|
368
|
+
let inRemovedSection = false;
|
|
369
|
+
let currentDepth = 0;
|
|
370
|
+
for (const line of lines) {
|
|
371
|
+
const headerMatch = line.match(/^(#{1,2})\s+/);
|
|
372
|
+
if (headerMatch) {
|
|
373
|
+
const depth = headerMatch[1].length;
|
|
374
|
+
const trimmed = line.trim();
|
|
375
|
+
if (headersToRemove.has(trimmed)) {
|
|
376
|
+
inRemovedSection = true;
|
|
377
|
+
currentDepth = depth;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (inRemovedSection && depth <= currentDepth) {
|
|
381
|
+
inRemovedSection = false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (!inRemovedSection) {
|
|
385
|
+
result.push(line);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const cleaned = result.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
389
|
+
await writeFile4(claudeMdPath, cleaned, "utf-8");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/uninstall-cli.ts
|
|
393
|
+
function printBanner() {
|
|
394
|
+
console.log(
|
|
395
|
+
chalk.red.bold(`
|
|
396
|
+
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
397
|
+
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D
|
|
398
|
+
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
399
|
+
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
400
|
+
\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
|
|
401
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
|
|
402
|
+
`)
|
|
403
|
+
);
|
|
404
|
+
console.log(chalk.gray(" Claude Code Harness Uninstaller\n"));
|
|
405
|
+
}
|
|
406
|
+
async function runUninstallCli() {
|
|
407
|
+
printBanner();
|
|
408
|
+
const scope = await select({
|
|
409
|
+
message: "Uninstall scope:",
|
|
410
|
+
choices: [
|
|
411
|
+
{
|
|
412
|
+
name: `Global ${chalk.gray("~/.claude/")}`,
|
|
413
|
+
value: "global"
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: `Project ${chalk.gray("./.claude/")}`,
|
|
417
|
+
value: "project"
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
});
|
|
421
|
+
const targetDir = scope === "global" ? getGlobalDir() : getProjectDir();
|
|
422
|
+
console.log(chalk.gray("\n Scanning installed items...\n"));
|
|
423
|
+
const [
|
|
424
|
+
installedAgents,
|
|
425
|
+
installedSkills,
|
|
426
|
+
installedCommands,
|
|
427
|
+
installedHooks,
|
|
428
|
+
installedRuleCategories,
|
|
429
|
+
installedMcpNames,
|
|
430
|
+
installedClaudemds
|
|
431
|
+
] = await Promise.all([
|
|
432
|
+
getInstalledAgents(targetDir),
|
|
433
|
+
getInstalledSkills(targetDir),
|
|
434
|
+
getInstalledCommands(targetDir),
|
|
435
|
+
getInstalledHooks(targetDir),
|
|
436
|
+
getInstalledRuleCategories(targetDir),
|
|
437
|
+
getInstalledMcps(scope),
|
|
438
|
+
getInstalledClaudemd(targetDir)
|
|
439
|
+
]);
|
|
440
|
+
const totalInstalled = installedAgents.length + installedSkills.length + installedCommands.length + installedHooks.length + installedRuleCategories.length + installedMcpNames.length + installedClaudemds.length;
|
|
441
|
+
if (totalInstalled === 0) {
|
|
442
|
+
console.log(chalk.yellow(" Nothing installed. Exiting."));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const availableCategories = [];
|
|
446
|
+
if (installedAgents.length > 0)
|
|
447
|
+
availableCategories.push({
|
|
448
|
+
name: `Agents ${chalk.gray(`(${installedAgents.length} installed)`)}`,
|
|
449
|
+
value: "agents"
|
|
450
|
+
});
|
|
451
|
+
if (installedSkills.length > 0)
|
|
452
|
+
availableCategories.push({
|
|
453
|
+
name: `Skills ${chalk.gray(`(${installedSkills.length} installed)`)}`,
|
|
454
|
+
value: "skills"
|
|
455
|
+
});
|
|
456
|
+
if (installedCommands.length > 0)
|
|
457
|
+
availableCategories.push({
|
|
458
|
+
name: `Commands ${chalk.gray(`(${installedCommands.length} installed)`)}`,
|
|
459
|
+
value: "commands"
|
|
460
|
+
});
|
|
461
|
+
if (installedHooks.length > 0)
|
|
462
|
+
availableCategories.push({
|
|
463
|
+
name: `Hooks ${chalk.gray(`(${installedHooks.length} installed)`)}`,
|
|
464
|
+
value: "hooks"
|
|
465
|
+
});
|
|
466
|
+
if (installedRuleCategories.length > 0)
|
|
467
|
+
availableCategories.push({
|
|
468
|
+
name: `Rules ${chalk.gray(`(${installedRuleCategories.length} categories)`)}`,
|
|
469
|
+
value: "rules"
|
|
470
|
+
});
|
|
471
|
+
if (installedMcpNames.length > 0)
|
|
472
|
+
availableCategories.push({
|
|
473
|
+
name: `MCPs ${chalk.gray(`(${installedMcpNames.length} installed)`)}`,
|
|
474
|
+
value: "mcps"
|
|
475
|
+
});
|
|
476
|
+
if (installedClaudemds.length > 0)
|
|
477
|
+
availableCategories.push({
|
|
478
|
+
name: `ClaudeMd ${chalk.gray(`(${installedClaudemds.length} installed)`)}`,
|
|
479
|
+
value: "claudemd"
|
|
480
|
+
});
|
|
481
|
+
const selectedCategories = await checkbox({
|
|
482
|
+
message: "Select categories to uninstall:",
|
|
483
|
+
choices: availableCategories
|
|
484
|
+
});
|
|
485
|
+
if (selectedCategories.length === 0) {
|
|
486
|
+
console.log(chalk.yellow("\n No categories selected. Exiting."));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const plan = {
|
|
490
|
+
scope,
|
|
491
|
+
agents: [],
|
|
492
|
+
skills: [],
|
|
493
|
+
commands: [],
|
|
494
|
+
hooks: [],
|
|
495
|
+
ruleCategories: [],
|
|
496
|
+
mcpNames: [],
|
|
497
|
+
claudemds: []
|
|
498
|
+
};
|
|
499
|
+
if (selectedCategories.includes("agents") && installedAgents.length > 0) {
|
|
500
|
+
plan.agents = await checkbox({
|
|
501
|
+
message: "Select agents to uninstall:",
|
|
502
|
+
choices: installedAgents.map((a) => ({
|
|
503
|
+
name: `${chalk.bold(a.name.padEnd(30))} ${chalk.gray(a.description.slice(0, 60))}`,
|
|
504
|
+
value: a,
|
|
505
|
+
checked: false
|
|
506
|
+
}))
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
if (selectedCategories.includes("skills") && installedSkills.length > 0) {
|
|
510
|
+
plan.skills = await checkbox({
|
|
511
|
+
message: "Select skills to uninstall:",
|
|
512
|
+
choices: installedSkills.map((s) => ({
|
|
513
|
+
name: `${chalk.bold(s.name.padEnd(35))} ${chalk.gray(s.description.slice(0, 55))}`,
|
|
514
|
+
value: s,
|
|
515
|
+
checked: false
|
|
516
|
+
}))
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
if (selectedCategories.includes("commands") && installedCommands.length > 0) {
|
|
520
|
+
plan.commands = await checkbox({
|
|
521
|
+
message: "Select commands to uninstall:",
|
|
522
|
+
choices: installedCommands.map((c) => ({
|
|
523
|
+
name: `${chalk.bold(c.name.padEnd(25))} ${chalk.gray(c.description.slice(0, 65))}`,
|
|
524
|
+
value: c,
|
|
525
|
+
checked: false
|
|
526
|
+
}))
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
if (selectedCategories.includes("hooks") && installedHooks.length > 0) {
|
|
530
|
+
plan.hooks = await checkbox({
|
|
531
|
+
message: "Select hooks to uninstall:",
|
|
532
|
+
choices: installedHooks.map((h) => ({
|
|
533
|
+
name: `${chalk.bold(h.name.padEnd(35))} ${chalk.gray(h.description.slice(0, 55))}`,
|
|
534
|
+
value: h,
|
|
535
|
+
checked: false
|
|
536
|
+
}))
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
if (selectedCategories.includes("rules") && installedRuleCategories.length > 0) {
|
|
540
|
+
plan.ruleCategories = await checkbox({
|
|
541
|
+
message: "Select rule categories to uninstall:",
|
|
542
|
+
choices: installedRuleCategories.map((cat) => ({
|
|
543
|
+
name: chalk.bold(cat),
|
|
544
|
+
value: cat,
|
|
545
|
+
checked: false
|
|
546
|
+
}))
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
if (selectedCategories.includes("mcps") && installedMcpNames.length > 0) {
|
|
550
|
+
plan.mcpNames = await checkbox({
|
|
551
|
+
message: "Select MCP servers to uninstall:",
|
|
552
|
+
choices: installedMcpNames.map((name) => ({
|
|
553
|
+
name: chalk.bold(name),
|
|
554
|
+
value: name,
|
|
555
|
+
checked: false
|
|
556
|
+
}))
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
if (selectedCategories.includes("claudemd") && installedClaudemds.length > 0) {
|
|
560
|
+
plan.claudemds = await checkbox({
|
|
561
|
+
message: "Select claudemd items to uninstall:",
|
|
562
|
+
choices: installedClaudemds.map((c) => ({
|
|
563
|
+
name: `${chalk.bold(c.name.padEnd(35))} ${chalk.gray(c.description.slice(0, 55))}`,
|
|
564
|
+
value: c,
|
|
565
|
+
checked: false
|
|
566
|
+
}))
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
const totalToRemove = plan.agents.length + plan.skills.length + plan.commands.length + plan.hooks.length + plan.ruleCategories.length + plan.mcpNames.length + plan.claudemds.length;
|
|
570
|
+
if (totalToRemove === 0) {
|
|
571
|
+
console.log(chalk.yellow("\n Nothing selected. Exiting."));
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
console.log(chalk.bold("\n Uninstall Summary"));
|
|
575
|
+
console.log(chalk.gray(" \u2500".repeat(40)));
|
|
576
|
+
console.log(` Scope : ${chalk.cyan(scope)} (${targetDir})`);
|
|
577
|
+
if (plan.agents.length) console.log(` Agents : ${plan.agents.map((a) => a.name).join(", ")}`);
|
|
578
|
+
if (plan.skills.length) console.log(` Skills : ${plan.skills.map((s) => s.name).join(", ")}`);
|
|
579
|
+
if (plan.commands.length)
|
|
580
|
+
console.log(` Commands: ${plan.commands.map((c) => c.name).join(", ")}`);
|
|
581
|
+
if (plan.hooks.length) console.log(` Hooks : ${plan.hooks.map((h) => h.name).join(", ")}`);
|
|
582
|
+
if (plan.ruleCategories.length)
|
|
583
|
+
console.log(` Rules : ${plan.ruleCategories.join(", ")}`);
|
|
584
|
+
if (plan.mcpNames.length) console.log(` MCPs : ${plan.mcpNames.join(", ")}`);
|
|
585
|
+
if (plan.claudemds.length) console.log(` ClaudeMd: ${plan.claudemds.map((c) => c.name).join(", ")}`);
|
|
586
|
+
console.log("");
|
|
587
|
+
const ok = await confirm({
|
|
588
|
+
message: chalk.red("Proceed with uninstallation? This cannot be undone."),
|
|
589
|
+
default: false
|
|
590
|
+
});
|
|
591
|
+
if (!ok) {
|
|
592
|
+
console.log(chalk.yellow("\n Aborted."));
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
console.log("");
|
|
596
|
+
const spinner = ora("Uninstalling...").start();
|
|
597
|
+
try {
|
|
598
|
+
if (plan.agents.length) {
|
|
599
|
+
spinner.text = "Removing agents...";
|
|
600
|
+
await uninstallAgents(plan.agents, targetDir);
|
|
601
|
+
}
|
|
602
|
+
if (plan.skills.length) {
|
|
603
|
+
spinner.text = "Removing skills...";
|
|
604
|
+
await uninstallSkills(plan.skills, targetDir);
|
|
605
|
+
}
|
|
606
|
+
if (plan.commands.length) {
|
|
607
|
+
spinner.text = "Removing commands...";
|
|
608
|
+
await uninstallCommands(plan.commands, targetDir);
|
|
609
|
+
}
|
|
610
|
+
if (plan.hooks.length) {
|
|
611
|
+
spinner.text = "Removing hooks...";
|
|
612
|
+
await uninstallHooks(plan.hooks, targetDir);
|
|
613
|
+
}
|
|
614
|
+
if (plan.ruleCategories.length) {
|
|
615
|
+
spinner.text = "Removing rules...";
|
|
616
|
+
const claudeMd = join9(targetDir, "CLAUDE.md");
|
|
617
|
+
await uninstallRules(plan.ruleCategories, claudeMd);
|
|
618
|
+
}
|
|
619
|
+
if (plan.mcpNames.length) {
|
|
620
|
+
spinner.text = "Removing MCP servers...";
|
|
621
|
+
await uninstallMcps(plan.mcpNames, scope);
|
|
622
|
+
}
|
|
623
|
+
if (plan.claudemds.length) {
|
|
624
|
+
spinner.text = "Removing claudemd...";
|
|
625
|
+
await uninstallClaudemd(plan.claudemds, targetDir);
|
|
626
|
+
}
|
|
627
|
+
spinner.succeed(chalk.green("Uninstallation complete!"));
|
|
628
|
+
console.log("");
|
|
629
|
+
if (plan.agents.length)
|
|
630
|
+
console.log(` ${chalk.red("\u2717")} ${plan.agents.length} agent(s) removed`);
|
|
631
|
+
if (plan.skills.length)
|
|
632
|
+
console.log(` ${chalk.red("\u2717")} ${plan.skills.length} skill(s) removed`);
|
|
633
|
+
if (plan.commands.length)
|
|
634
|
+
console.log(` ${chalk.red("\u2717")} ${plan.commands.length} command(s) removed`);
|
|
635
|
+
if (plan.hooks.length)
|
|
636
|
+
console.log(` ${chalk.red("\u2717")} ${plan.hooks.length} hook(s) removed`);
|
|
637
|
+
if (plan.ruleCategories.length)
|
|
638
|
+
console.log(` ${chalk.red("\u2717")} Rules removed (${plan.ruleCategories.join(", ")})`);
|
|
639
|
+
if (plan.mcpNames.length)
|
|
640
|
+
console.log(` ${chalk.red("\u2717")} ${plan.mcpNames.length} MCP server(s) removed`);
|
|
641
|
+
if (plan.claudemds.length)
|
|
642
|
+
console.log(` ${chalk.red("\u2717")} ClaudeMd removed (${plan.claudemds.map((c) => c.name).join(", ")})`);
|
|
643
|
+
console.log("");
|
|
644
|
+
} catch (err) {
|
|
645
|
+
spinner.fail(chalk.red("Uninstallation failed"));
|
|
646
|
+
throw err;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
export {
|
|
650
|
+
runUninstallCli
|
|
651
|
+
};
|
package/package.json
CHANGED