@juho0719/cckit 0.2.5 → 0.2.7
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/dist/cli-DABSKXWN.js +477 -0
- package/dist/index.js +1 -1
- package/package.json +2 -1
- package/dist/cli-KZYBSIXO.js +0 -314
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import {
|
|
2
|
+
installMcps
|
|
3
|
+
} from "./chunk-W63UKEIT.js";
|
|
4
|
+
import {
|
|
5
|
+
installClaudemd
|
|
6
|
+
} from "./chunk-SW3OJLHC.js";
|
|
7
|
+
import {
|
|
8
|
+
getAgents,
|
|
9
|
+
getClaudemdItems,
|
|
10
|
+
getCommands,
|
|
11
|
+
getHooks,
|
|
12
|
+
getMcpServers,
|
|
13
|
+
getRuleCategories,
|
|
14
|
+
getSkills
|
|
15
|
+
} from "./chunk-E3INXQNO.js";
|
|
16
|
+
import {
|
|
17
|
+
installAgents
|
|
18
|
+
} from "./chunk-EYY2IZ7N.js";
|
|
19
|
+
import {
|
|
20
|
+
installSkills
|
|
21
|
+
} from "./chunk-KEENFBLL.js";
|
|
22
|
+
import {
|
|
23
|
+
installCommands
|
|
24
|
+
} from "./chunk-3Y26YU4R.js";
|
|
25
|
+
import {
|
|
26
|
+
installHooks
|
|
27
|
+
} from "./chunk-W7RWPDBH.js";
|
|
28
|
+
import {
|
|
29
|
+
installRules
|
|
30
|
+
} from "./chunk-ID643AV4.js";
|
|
31
|
+
import {
|
|
32
|
+
backupIfExists
|
|
33
|
+
} from "./chunk-K25UZZVG.js";
|
|
34
|
+
import {
|
|
35
|
+
copyFileUtil
|
|
36
|
+
} from "./chunk-3GUKEMND.js";
|
|
37
|
+
import {
|
|
38
|
+
getAssetsDir,
|
|
39
|
+
getGlobalDir,
|
|
40
|
+
getProjectDir
|
|
41
|
+
} from "./chunk-5XOKKPAA.js";
|
|
42
|
+
|
|
43
|
+
// src/cli.ts
|
|
44
|
+
import { checkbox, select, input, confirm } from "@inquirer/prompts";
|
|
45
|
+
import { AbortPromptError, ExitPromptError } from "@inquirer/core";
|
|
46
|
+
import chalk from "chalk";
|
|
47
|
+
import ora from "ora";
|
|
48
|
+
import { join as join2 } from "path";
|
|
49
|
+
|
|
50
|
+
// src/installers/statusline.ts
|
|
51
|
+
import { join } from "path";
|
|
52
|
+
import { chmod } from "fs/promises";
|
|
53
|
+
async function installStatusline() {
|
|
54
|
+
const src = join(getAssetsDir(), "statusline", "statusline.sh");
|
|
55
|
+
const dest = join(getGlobalDir(), "statusline.sh");
|
|
56
|
+
await backupIfExists(dest);
|
|
57
|
+
await copyFileUtil(src, dest);
|
|
58
|
+
await chmod(dest, 493);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/cli.ts
|
|
62
|
+
var BACK = /* @__PURE__ */ Symbol("BACK");
|
|
63
|
+
var STEP_SCOPE = 0;
|
|
64
|
+
var STEP_CATEGORIES = 1;
|
|
65
|
+
var STEP_ITEMS = 2;
|
|
66
|
+
var STEP_CONFIRM = 3;
|
|
67
|
+
async function withEsc(promptFn) {
|
|
68
|
+
const ac = new AbortController();
|
|
69
|
+
const onData = (data) => {
|
|
70
|
+
if (data.length === 1 && data[0] === 27) {
|
|
71
|
+
ac.abort();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
process.stdin.on("data", onData);
|
|
75
|
+
try {
|
|
76
|
+
return await promptFn({ signal: ac.signal });
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (err instanceof AbortPromptError) {
|
|
79
|
+
return BACK;
|
|
80
|
+
}
|
|
81
|
+
throw err;
|
|
82
|
+
} finally {
|
|
83
|
+
process.stdin.removeListener("data", onData);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function printBanner() {
|
|
87
|
+
console.log(
|
|
88
|
+
chalk.cyan.bold(`
|
|
89
|
+
\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
|
|
90
|
+
\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
|
|
91
|
+
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
92
|
+
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
93
|
+
\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
|
|
94
|
+
\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
|
|
95
|
+
`)
|
|
96
|
+
);
|
|
97
|
+
console.log(chalk.gray(" Claude Code Harness Installer"));
|
|
98
|
+
console.log(chalk.gray(" ESC: back / Ctrl+C: exit\n"));
|
|
99
|
+
}
|
|
100
|
+
function createEmptyPlan(scope) {
|
|
101
|
+
return {
|
|
102
|
+
scope,
|
|
103
|
+
agents: [],
|
|
104
|
+
skills: [],
|
|
105
|
+
commands: [],
|
|
106
|
+
hooks: [],
|
|
107
|
+
ruleCategories: [],
|
|
108
|
+
mcps: [],
|
|
109
|
+
mcpEnvValues: /* @__PURE__ */ new Map(),
|
|
110
|
+
claudemds: [],
|
|
111
|
+
statusline: false
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function runCli() {
|
|
115
|
+
printBanner();
|
|
116
|
+
let step = STEP_SCOPE;
|
|
117
|
+
let scope = "global";
|
|
118
|
+
let targetDir = "";
|
|
119
|
+
let selectedCategories = [];
|
|
120
|
+
let plan = createEmptyPlan(scope);
|
|
121
|
+
let skippedMcps = /* @__PURE__ */ new Map();
|
|
122
|
+
try {
|
|
123
|
+
while (step <= STEP_CONFIRM) {
|
|
124
|
+
switch (step) {
|
|
125
|
+
// ── Step 0: 설치 범위 선택 ──
|
|
126
|
+
case STEP_SCOPE: {
|
|
127
|
+
const result = await withEsc(
|
|
128
|
+
(ctx) => select({
|
|
129
|
+
message: "Install scope:",
|
|
130
|
+
choices: [
|
|
131
|
+
{
|
|
132
|
+
name: `Global ${chalk.gray("~/.claude/")}`,
|
|
133
|
+
value: "global"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: `Project ${chalk.gray("./.claude/")}`,
|
|
137
|
+
value: "project"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}, ctx)
|
|
141
|
+
);
|
|
142
|
+
if (result === BACK) {
|
|
143
|
+
console.log(chalk.yellow("\n Exiting."));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
scope = result;
|
|
147
|
+
targetDir = scope === "global" ? getGlobalDir() : getProjectDir();
|
|
148
|
+
step = STEP_CATEGORIES;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
// ── Step 1: 카테고리 멀티셀렉트 ──
|
|
152
|
+
case STEP_CATEGORIES: {
|
|
153
|
+
const result = await withEsc(
|
|
154
|
+
(ctx) => checkbox({
|
|
155
|
+
message: "Select categories to install:",
|
|
156
|
+
choices: [
|
|
157
|
+
{ name: "Agents - AI sub-agents", value: "agents" },
|
|
158
|
+
{ name: "Skills - Reusable skill prompts", value: "skills" },
|
|
159
|
+
{ name: "Commands - Slash commands", value: "commands" },
|
|
160
|
+
{ name: "Hooks - Post-tool hooks", value: "hooks" },
|
|
161
|
+
{ name: "Rules - CLAUDE.md rules", value: "rules" },
|
|
162
|
+
{ name: "MCPs - MCP server configs", value: "mcps" },
|
|
163
|
+
{ name: "ClaudeMd - Behavioral guidelines", value: "claudemd" },
|
|
164
|
+
{ name: "Statusline - Terminal statusline script", value: "statusline" }
|
|
165
|
+
]
|
|
166
|
+
}, ctx)
|
|
167
|
+
);
|
|
168
|
+
if (result === BACK) {
|
|
169
|
+
step = STEP_SCOPE;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
if (result.length === 0) {
|
|
173
|
+
console.log(chalk.yellow("\n No categories selected.\n"));
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
selectedCategories = result;
|
|
177
|
+
plan = createEmptyPlan(scope);
|
|
178
|
+
skippedMcps = /* @__PURE__ */ new Map();
|
|
179
|
+
step = STEP_ITEMS;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
// ── Step 2: 각 카테고리별 항목 선택 ──
|
|
183
|
+
case STEP_ITEMS: {
|
|
184
|
+
let wentBack = false;
|
|
185
|
+
for (const cat of selectedCategories) {
|
|
186
|
+
if (cat === "statusline") {
|
|
187
|
+
plan.statusline = true;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (cat === "agents") {
|
|
191
|
+
const allAgents = await getAgents();
|
|
192
|
+
const result = await withEsc(
|
|
193
|
+
(ctx) => checkbox({
|
|
194
|
+
message: "Select agents:",
|
|
195
|
+
choices: allAgents.map((a) => ({
|
|
196
|
+
name: `${chalk.bold(a.name.padEnd(30))} ${chalk.gray(a.description.slice(0, 60))}`,
|
|
197
|
+
value: a,
|
|
198
|
+
checked: false
|
|
199
|
+
}))
|
|
200
|
+
}, ctx)
|
|
201
|
+
);
|
|
202
|
+
if (result === BACK) {
|
|
203
|
+
wentBack = true;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
plan.agents = result;
|
|
207
|
+
}
|
|
208
|
+
if (cat === "skills") {
|
|
209
|
+
const allSkills = await getSkills();
|
|
210
|
+
const result = await withEsc(
|
|
211
|
+
(ctx) => checkbox({
|
|
212
|
+
message: "Select skills:",
|
|
213
|
+
choices: allSkills.map((s) => ({
|
|
214
|
+
name: `${chalk.bold(s.name.padEnd(35))} ${chalk.gray(s.description.slice(0, 55))}`,
|
|
215
|
+
value: s,
|
|
216
|
+
checked: false
|
|
217
|
+
}))
|
|
218
|
+
}, ctx)
|
|
219
|
+
);
|
|
220
|
+
if (result === BACK) {
|
|
221
|
+
wentBack = true;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
plan.skills = result;
|
|
225
|
+
}
|
|
226
|
+
if (cat === "commands") {
|
|
227
|
+
const allCommands = await getCommands();
|
|
228
|
+
const result = await withEsc(
|
|
229
|
+
(ctx) => checkbox({
|
|
230
|
+
message: "Select commands:",
|
|
231
|
+
choices: allCommands.map((c) => ({
|
|
232
|
+
name: `${chalk.bold(c.name.padEnd(25))} ${chalk.gray(c.description.slice(0, 65))}`,
|
|
233
|
+
value: c,
|
|
234
|
+
checked: false
|
|
235
|
+
}))
|
|
236
|
+
}, ctx)
|
|
237
|
+
);
|
|
238
|
+
if (result === BACK) {
|
|
239
|
+
wentBack = true;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
plan.commands = result;
|
|
243
|
+
}
|
|
244
|
+
if (cat === "hooks") {
|
|
245
|
+
const allHooks = await getHooks();
|
|
246
|
+
const result = await withEsc(
|
|
247
|
+
(ctx) => checkbox({
|
|
248
|
+
message: "Select hooks:",
|
|
249
|
+
choices: allHooks.map((h) => ({
|
|
250
|
+
name: `${chalk.bold(h.name.padEnd(35))} ${chalk.gray(h.description.slice(0, 55))}`,
|
|
251
|
+
value: h,
|
|
252
|
+
checked: false
|
|
253
|
+
}))
|
|
254
|
+
}, ctx)
|
|
255
|
+
);
|
|
256
|
+
if (result === BACK) {
|
|
257
|
+
wentBack = true;
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
plan.hooks = result;
|
|
261
|
+
}
|
|
262
|
+
if (cat === "rules") {
|
|
263
|
+
const allCategories = await getRuleCategories();
|
|
264
|
+
const result = await withEsc(
|
|
265
|
+
(ctx) => checkbox({
|
|
266
|
+
message: "Select rule categories:",
|
|
267
|
+
choices: allCategories.map((rc) => ({
|
|
268
|
+
name: `${chalk.bold(rc.category.padEnd(20))} ${chalk.gray(`(${rc.files.length} files)`)}`,
|
|
269
|
+
value: rc.category,
|
|
270
|
+
checked: false
|
|
271
|
+
}))
|
|
272
|
+
}, ctx)
|
|
273
|
+
);
|
|
274
|
+
if (result === BACK) {
|
|
275
|
+
wentBack = true;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
plan.ruleCategories = result;
|
|
279
|
+
}
|
|
280
|
+
if (cat === "claudemd") {
|
|
281
|
+
const allClaudemds = await getClaudemdItems();
|
|
282
|
+
const result = await withEsc(
|
|
283
|
+
(ctx) => checkbox({
|
|
284
|
+
message: "Select claudemd items:",
|
|
285
|
+
choices: allClaudemds.map((c) => ({
|
|
286
|
+
name: `${chalk.bold(c.name.padEnd(35))} ${chalk.gray(c.description.slice(0, 55))}`,
|
|
287
|
+
value: c,
|
|
288
|
+
checked: false
|
|
289
|
+
}))
|
|
290
|
+
}, ctx)
|
|
291
|
+
);
|
|
292
|
+
if (result === BACK) {
|
|
293
|
+
wentBack = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
plan.claudemds = result;
|
|
297
|
+
}
|
|
298
|
+
if (cat === "mcps") {
|
|
299
|
+
const allMcps = await getMcpServers();
|
|
300
|
+
const mcpResult = await withEsc(
|
|
301
|
+
(ctx) => checkbox({
|
|
302
|
+
message: "Select MCP servers:",
|
|
303
|
+
choices: allMcps.map((m) => ({
|
|
304
|
+
name: `${chalk.bold(m.name.padEnd(30))} ${chalk.gray(m.description.slice(0, 55))}`,
|
|
305
|
+
value: m,
|
|
306
|
+
checked: false
|
|
307
|
+
}))
|
|
308
|
+
}, ctx)
|
|
309
|
+
);
|
|
310
|
+
if (mcpResult === BACK) {
|
|
311
|
+
wentBack = true;
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
plan.mcps = mcpResult;
|
|
315
|
+
for (const server of plan.mcps) {
|
|
316
|
+
if (server.type === "http" || server.envVars.length === 0) continue;
|
|
317
|
+
console.log(chalk.yellow(`
|
|
318
|
+
${server.name} requires environment variables:`));
|
|
319
|
+
const envMap = {};
|
|
320
|
+
const skippedKeys = [];
|
|
321
|
+
for (const envKey of server.envVars) {
|
|
322
|
+
const action = await withEsc(
|
|
323
|
+
(ctx) => select({
|
|
324
|
+
message: ` How do you want to set ${chalk.bold(envKey)}?`,
|
|
325
|
+
choices: [
|
|
326
|
+
{ name: "Enter value now", value: "enter" },
|
|
327
|
+
{ name: `Skip ${chalk.gray(`(keep placeholder: YOUR_${envKey}_HERE)`)}`, value: "skip" }
|
|
328
|
+
]
|
|
329
|
+
}, ctx)
|
|
330
|
+
);
|
|
331
|
+
if (action === BACK) {
|
|
332
|
+
wentBack = true;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
if (action === "skip") {
|
|
336
|
+
skippedKeys.push(envKey);
|
|
337
|
+
} else {
|
|
338
|
+
const value = await withEsc(
|
|
339
|
+
(ctx) => input({
|
|
340
|
+
message: ` ${envKey}:`,
|
|
341
|
+
default: ""
|
|
342
|
+
}, ctx)
|
|
343
|
+
);
|
|
344
|
+
if (value === BACK) {
|
|
345
|
+
wentBack = true;
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
if (value) envMap[envKey] = value;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (wentBack) break;
|
|
352
|
+
plan.mcpEnvValues.set(server.name, envMap);
|
|
353
|
+
if (skippedKeys.length > 0) {
|
|
354
|
+
skippedMcps.set(server.name, skippedKeys);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (wentBack) break;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (wentBack) {
|
|
361
|
+
plan = createEmptyPlan(scope);
|
|
362
|
+
skippedMcps = /* @__PURE__ */ new Map();
|
|
363
|
+
step = STEP_CATEGORIES;
|
|
364
|
+
} else {
|
|
365
|
+
step = STEP_CONFIRM;
|
|
366
|
+
}
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
// ── Step 3: 설치 요약 + 확인 ──
|
|
370
|
+
case STEP_CONFIRM: {
|
|
371
|
+
console.log(chalk.bold("\n Installation Summary"));
|
|
372
|
+
console.log(chalk.gray(" \u2500".repeat(40)));
|
|
373
|
+
console.log(` Scope : ${chalk.cyan(scope)} (${targetDir})`);
|
|
374
|
+
if (plan.agents.length) console.log(` Agents : ${plan.agents.map((a) => a.name).join(", ")}`);
|
|
375
|
+
if (plan.skills.length) console.log(` Skills : ${plan.skills.map((s) => s.name).join(", ")}`);
|
|
376
|
+
if (plan.commands.length) console.log(` Commands: ${plan.commands.map((c) => c.name).join(", ")}`);
|
|
377
|
+
if (plan.hooks.length) console.log(` Hooks : ${plan.hooks.map((h) => h.name).join(", ")}`);
|
|
378
|
+
if (plan.ruleCategories.length) console.log(` Rules : ${plan.ruleCategories.join(", ")}`);
|
|
379
|
+
if (plan.mcps.length) console.log(` MCPs : ${plan.mcps.map((m) => m.name).join(", ")}`);
|
|
380
|
+
if (plan.claudemds.length) console.log(` ClaudeMd : ${plan.claudemds.map((c) => c.name).join(", ")}`);
|
|
381
|
+
if (plan.statusline) console.log(` Statusline: statusline.sh \u2192 ~/.claude/statusline.sh`);
|
|
382
|
+
console.log("");
|
|
383
|
+
const totalItems = plan.agents.length + plan.skills.length + plan.commands.length + plan.hooks.length + plan.ruleCategories.length + plan.mcps.length + plan.claudemds.length + (plan.statusline ? 1 : 0);
|
|
384
|
+
if (totalItems === 0) {
|
|
385
|
+
console.log(chalk.yellow(" Nothing selected.\n"));
|
|
386
|
+
plan = createEmptyPlan(scope);
|
|
387
|
+
skippedMcps = /* @__PURE__ */ new Map();
|
|
388
|
+
step = STEP_CATEGORIES;
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
const ok = await withEsc(
|
|
392
|
+
(ctx) => confirm({ message: "Proceed with installation?", default: true }, ctx)
|
|
393
|
+
);
|
|
394
|
+
if (ok === BACK) {
|
|
395
|
+
plan = createEmptyPlan(scope);
|
|
396
|
+
skippedMcps = /* @__PURE__ */ new Map();
|
|
397
|
+
step = STEP_CATEGORIES;
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
if (!ok) {
|
|
401
|
+
console.log(chalk.yellow("\n Aborted."));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
console.log("");
|
|
405
|
+
const spinner = ora("Installing...").start();
|
|
406
|
+
try {
|
|
407
|
+
if (plan.agents.length) {
|
|
408
|
+
spinner.text = "Installing agents...";
|
|
409
|
+
await installAgents(plan.agents, targetDir);
|
|
410
|
+
}
|
|
411
|
+
if (plan.skills.length) {
|
|
412
|
+
spinner.text = "Installing skills...";
|
|
413
|
+
await installSkills(plan.skills, targetDir);
|
|
414
|
+
}
|
|
415
|
+
if (plan.commands.length) {
|
|
416
|
+
spinner.text = "Installing commands...";
|
|
417
|
+
await installCommands(plan.commands, targetDir);
|
|
418
|
+
}
|
|
419
|
+
if (plan.hooks.length) {
|
|
420
|
+
spinner.text = "Installing hooks...";
|
|
421
|
+
await installHooks(plan.hooks, targetDir);
|
|
422
|
+
}
|
|
423
|
+
if (plan.ruleCategories.length) {
|
|
424
|
+
spinner.text = "Installing rules...";
|
|
425
|
+
await installRules(plan.ruleCategories, targetDir);
|
|
426
|
+
}
|
|
427
|
+
if (plan.mcps.length) {
|
|
428
|
+
spinner.text = "Installing MCP servers...";
|
|
429
|
+
await installMcps(plan.mcps, plan.scope, plan.mcpEnvValues);
|
|
430
|
+
}
|
|
431
|
+
if (plan.claudemds.length) {
|
|
432
|
+
spinner.text = "Installing claudemd...";
|
|
433
|
+
await installClaudemd(plan.claudemds, targetDir);
|
|
434
|
+
}
|
|
435
|
+
if (plan.statusline) {
|
|
436
|
+
spinner.text = "Installing statusline...";
|
|
437
|
+
await installStatusline();
|
|
438
|
+
}
|
|
439
|
+
spinner.succeed(chalk.green("Installation complete!"));
|
|
440
|
+
console.log("");
|
|
441
|
+
if (plan.agents.length) console.log(` ${chalk.green("\u2713")} ${plan.agents.length} agent(s) \u2192 ${join2(targetDir, "agents")}`);
|
|
442
|
+
if (plan.skills.length) console.log(` ${chalk.green("\u2713")} ${plan.skills.length} skill(s) \u2192 ${join2(targetDir, "skills")}`);
|
|
443
|
+
if (plan.commands.length) console.log(` ${chalk.green("\u2713")} ${plan.commands.length} command(s) \u2192 ${join2(targetDir, "commands")}`);
|
|
444
|
+
if (plan.hooks.length) console.log(` ${chalk.green("\u2713")} ${plan.hooks.length} hook(s) \u2192 ${join2(targetDir, "hooks")}`);
|
|
445
|
+
if (plan.ruleCategories.length) console.log(` ${chalk.green("\u2713")} Rules copied \u2192 ${join2(targetDir, "rules")}`);
|
|
446
|
+
if (plan.mcps.length) console.log(` ${chalk.green("\u2713")} ${plan.mcps.length} MCP server(s) \u2192 ${scope === "global" ? "~/.claude.json" : "./.claude.json"}`);
|
|
447
|
+
if (plan.claudemds.length) console.log(` ${chalk.green("\u2713")} ClaudeMd appended \u2192 ${join2(targetDir, "CLAUDE.md")}`);
|
|
448
|
+
if (plan.statusline) console.log(` ${chalk.green("\u2713")} Statusline installed \u2192 ~/.claude/statusline.sh`);
|
|
449
|
+
console.log("");
|
|
450
|
+
if (skippedMcps.size > 0) {
|
|
451
|
+
console.log(chalk.yellow(" \u26A0 The following MCP servers have placeholder env vars:"));
|
|
452
|
+
for (const [serverName, keys] of skippedMcps) {
|
|
453
|
+
console.log(` ${chalk.bold("-")} ${serverName} ${chalk.gray(`(${keys.join(", ")})`)} `);
|
|
454
|
+
}
|
|
455
|
+
const configPath = scope === "global" ? "~/.claude.json" : "./.claude.json";
|
|
456
|
+
console.log(chalk.gray(` \u2192 Edit ${configPath} to set the actual values`));
|
|
457
|
+
console.log("");
|
|
458
|
+
}
|
|
459
|
+
} catch (err) {
|
|
460
|
+
spinner.fail(chalk.red("Installation failed"));
|
|
461
|
+
throw err;
|
|
462
|
+
}
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
} catch (err) {
|
|
468
|
+
if (err instanceof ExitPromptError) {
|
|
469
|
+
console.log(chalk.yellow("\n Exiting."));
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
throw err;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
export {
|
|
476
|
+
runCli
|
|
477
|
+
};
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juho0719/cckit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Claude Code Harness Installer - Interactive CLI for installing Claude Code agents, skills, commands, hooks, rules, and MCP servers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"author": "",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@inquirer/core": "^10.0.0",
|
|
27
28
|
"@inquirer/prompts": "^7.0.0",
|
|
28
29
|
"chalk": "^5.0.0",
|
|
29
30
|
"ora": "^8.0.0"
|
package/dist/cli-KZYBSIXO.js
DELETED
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
installMcps
|
|
3
|
-
} from "./chunk-W63UKEIT.js";
|
|
4
|
-
import {
|
|
5
|
-
installClaudemd
|
|
6
|
-
} from "./chunk-SW3OJLHC.js";
|
|
7
|
-
import {
|
|
8
|
-
getAgents,
|
|
9
|
-
getClaudemdItems,
|
|
10
|
-
getCommands,
|
|
11
|
-
getHooks,
|
|
12
|
-
getMcpServers,
|
|
13
|
-
getRuleCategories,
|
|
14
|
-
getSkills
|
|
15
|
-
} from "./chunk-E3INXQNO.js";
|
|
16
|
-
import {
|
|
17
|
-
installAgents
|
|
18
|
-
} from "./chunk-EYY2IZ7N.js";
|
|
19
|
-
import {
|
|
20
|
-
installSkills
|
|
21
|
-
} from "./chunk-KEENFBLL.js";
|
|
22
|
-
import {
|
|
23
|
-
installCommands
|
|
24
|
-
} from "./chunk-3Y26YU4R.js";
|
|
25
|
-
import {
|
|
26
|
-
installHooks
|
|
27
|
-
} from "./chunk-W7RWPDBH.js";
|
|
28
|
-
import {
|
|
29
|
-
installRules
|
|
30
|
-
} from "./chunk-ID643AV4.js";
|
|
31
|
-
import {
|
|
32
|
-
backupIfExists
|
|
33
|
-
} from "./chunk-K25UZZVG.js";
|
|
34
|
-
import {
|
|
35
|
-
copyFileUtil
|
|
36
|
-
} from "./chunk-3GUKEMND.js";
|
|
37
|
-
import {
|
|
38
|
-
getAssetsDir,
|
|
39
|
-
getGlobalDir,
|
|
40
|
-
getProjectDir
|
|
41
|
-
} from "./chunk-5XOKKPAA.js";
|
|
42
|
-
|
|
43
|
-
// src/cli.ts
|
|
44
|
-
import { checkbox, select, input, confirm } from "@inquirer/prompts";
|
|
45
|
-
import chalk from "chalk";
|
|
46
|
-
import ora from "ora";
|
|
47
|
-
import { join as join2 } from "path";
|
|
48
|
-
|
|
49
|
-
// src/installers/statusline.ts
|
|
50
|
-
import { join } from "path";
|
|
51
|
-
import { chmod } from "fs/promises";
|
|
52
|
-
async function installStatusline() {
|
|
53
|
-
const src = join(getAssetsDir(), "statusline", "statusline.sh");
|
|
54
|
-
const dest = join(getGlobalDir(), "statusline.sh");
|
|
55
|
-
await backupIfExists(dest);
|
|
56
|
-
await copyFileUtil(src, dest);
|
|
57
|
-
await chmod(dest, 493);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// src/cli.ts
|
|
61
|
-
function printBanner() {
|
|
62
|
-
console.log(
|
|
63
|
-
chalk.cyan.bold(`
|
|
64
|
-
\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
|
|
65
|
-
\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
|
|
66
|
-
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
67
|
-
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551
|
|
68
|
-
\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
|
|
69
|
-
\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
|
|
70
|
-
`)
|
|
71
|
-
);
|
|
72
|
-
console.log(chalk.gray(" Claude Code Harness Installer\n"));
|
|
73
|
-
}
|
|
74
|
-
async function runCli() {
|
|
75
|
-
printBanner();
|
|
76
|
-
const scopeAnswer = await select({
|
|
77
|
-
message: "Install scope:",
|
|
78
|
-
choices: [
|
|
79
|
-
{
|
|
80
|
-
name: `Global ${chalk.gray("~/.claude/")}`,
|
|
81
|
-
value: "global"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: `Project ${chalk.gray("./.claude/")}`,
|
|
85
|
-
value: "project"
|
|
86
|
-
}
|
|
87
|
-
]
|
|
88
|
-
});
|
|
89
|
-
const scope = scopeAnswer;
|
|
90
|
-
const targetDir = scope === "global" ? getGlobalDir() : getProjectDir();
|
|
91
|
-
const selectedCategories = await checkbox({
|
|
92
|
-
message: "Select categories to install:",
|
|
93
|
-
choices: [
|
|
94
|
-
{ name: "Agents - AI sub-agents", value: "agents" },
|
|
95
|
-
{ name: "Skills - Reusable skill prompts", value: "skills" },
|
|
96
|
-
{ name: "Commands - Slash commands", value: "commands" },
|
|
97
|
-
{ name: "Hooks - Post-tool hooks", value: "hooks" },
|
|
98
|
-
{ name: "Rules - CLAUDE.md rules", value: "rules" },
|
|
99
|
-
{ name: "MCPs - MCP server configs", value: "mcps" },
|
|
100
|
-
{ name: "ClaudeMd - Behavioral guidelines", value: "claudemd" },
|
|
101
|
-
{ name: "Statusline - Terminal statusline script", value: "statusline" }
|
|
102
|
-
]
|
|
103
|
-
});
|
|
104
|
-
if (selectedCategories.length === 0) {
|
|
105
|
-
console.log(chalk.yellow("\nNo categories selected. Exiting."));
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
const plan = {
|
|
109
|
-
scope,
|
|
110
|
-
agents: [],
|
|
111
|
-
skills: [],
|
|
112
|
-
commands: [],
|
|
113
|
-
hooks: [],
|
|
114
|
-
ruleCategories: [],
|
|
115
|
-
mcps: [],
|
|
116
|
-
mcpEnvValues: /* @__PURE__ */ new Map(),
|
|
117
|
-
claudemds: [],
|
|
118
|
-
statusline: false
|
|
119
|
-
};
|
|
120
|
-
const skippedMcps = /* @__PURE__ */ new Map();
|
|
121
|
-
if (selectedCategories.includes("agents")) {
|
|
122
|
-
const allAgents = await getAgents();
|
|
123
|
-
plan.agents = await checkbox({
|
|
124
|
-
message: "Select agents:",
|
|
125
|
-
choices: allAgents.map((a) => ({
|
|
126
|
-
name: `${chalk.bold(a.name.padEnd(30))} ${chalk.gray(a.description.slice(0, 60))}`,
|
|
127
|
-
value: a,
|
|
128
|
-
checked: false
|
|
129
|
-
}))
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
if (selectedCategories.includes("skills")) {
|
|
133
|
-
const allSkills = await getSkills();
|
|
134
|
-
plan.skills = await checkbox({
|
|
135
|
-
message: "Select skills:",
|
|
136
|
-
choices: allSkills.map((s) => ({
|
|
137
|
-
name: `${chalk.bold(s.name.padEnd(35))} ${chalk.gray(s.description.slice(0, 55))}`,
|
|
138
|
-
value: s,
|
|
139
|
-
checked: false
|
|
140
|
-
}))
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
if (selectedCategories.includes("commands")) {
|
|
144
|
-
const allCommands = await getCommands();
|
|
145
|
-
plan.commands = await checkbox({
|
|
146
|
-
message: "Select commands:",
|
|
147
|
-
choices: allCommands.map((c) => ({
|
|
148
|
-
name: `${chalk.bold(c.name.padEnd(25))} ${chalk.gray(c.description.slice(0, 65))}`,
|
|
149
|
-
value: c,
|
|
150
|
-
checked: false
|
|
151
|
-
}))
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
if (selectedCategories.includes("hooks")) {
|
|
155
|
-
const allHooks = await getHooks();
|
|
156
|
-
plan.hooks = await checkbox({
|
|
157
|
-
message: "Select hooks:",
|
|
158
|
-
choices: allHooks.map((h) => ({
|
|
159
|
-
name: `${chalk.bold(h.name.padEnd(35))} ${chalk.gray(h.description.slice(0, 55))}`,
|
|
160
|
-
value: h,
|
|
161
|
-
checked: false
|
|
162
|
-
}))
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
if (selectedCategories.includes("rules")) {
|
|
166
|
-
const allCategories = await getRuleCategories();
|
|
167
|
-
plan.ruleCategories = await checkbox({
|
|
168
|
-
message: "Select rule categories:",
|
|
169
|
-
choices: allCategories.map((rc) => ({
|
|
170
|
-
name: `${chalk.bold(rc.category.padEnd(20))} ${chalk.gray(`(${rc.files.length} files)`)}`,
|
|
171
|
-
value: rc.category,
|
|
172
|
-
checked: false
|
|
173
|
-
}))
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
if (selectedCategories.includes("claudemd")) {
|
|
177
|
-
const allClaudemds = await getClaudemdItems();
|
|
178
|
-
plan.claudemds = await checkbox({
|
|
179
|
-
message: "Select claudemd items:",
|
|
180
|
-
choices: allClaudemds.map((c) => ({
|
|
181
|
-
name: `${chalk.bold(c.name.padEnd(35))} ${chalk.gray(c.description.slice(0, 55))}`,
|
|
182
|
-
value: c,
|
|
183
|
-
checked: false
|
|
184
|
-
}))
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
if (selectedCategories.includes("statusline")) {
|
|
188
|
-
plan.statusline = true;
|
|
189
|
-
}
|
|
190
|
-
if (selectedCategories.includes("mcps")) {
|
|
191
|
-
const allMcps = await getMcpServers();
|
|
192
|
-
plan.mcps = await checkbox({
|
|
193
|
-
message: "Select MCP servers:",
|
|
194
|
-
choices: allMcps.map((m) => ({
|
|
195
|
-
name: `${chalk.bold(m.name.padEnd(30))} ${chalk.gray(m.description.slice(0, 55))}`,
|
|
196
|
-
value: m,
|
|
197
|
-
checked: false
|
|
198
|
-
}))
|
|
199
|
-
});
|
|
200
|
-
for (const server of plan.mcps) {
|
|
201
|
-
if (server.type === "http" || server.envVars.length === 0) continue;
|
|
202
|
-
console.log(chalk.yellow(`
|
|
203
|
-
${server.name} requires environment variables:`));
|
|
204
|
-
const envMap = {};
|
|
205
|
-
const skippedKeys = [];
|
|
206
|
-
for (const envKey of server.envVars) {
|
|
207
|
-
const action = await select({
|
|
208
|
-
message: ` How do you want to set ${chalk.bold(envKey)}?`,
|
|
209
|
-
choices: [
|
|
210
|
-
{ name: "Enter value now", value: "enter" },
|
|
211
|
-
{ name: `Skip ${chalk.gray(`(keep placeholder: YOUR_${envKey}_HERE)`)}`, value: "skip" }
|
|
212
|
-
]
|
|
213
|
-
});
|
|
214
|
-
if (action === "skip") {
|
|
215
|
-
skippedKeys.push(envKey);
|
|
216
|
-
} else {
|
|
217
|
-
const value = await input({
|
|
218
|
-
message: ` ${envKey}:`,
|
|
219
|
-
default: ""
|
|
220
|
-
});
|
|
221
|
-
if (value) envMap[envKey] = value;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
plan.mcpEnvValues.set(server.name, envMap);
|
|
225
|
-
if (skippedKeys.length > 0) {
|
|
226
|
-
skippedMcps.set(server.name, skippedKeys);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
console.log(chalk.bold("\n Installation Summary"));
|
|
231
|
-
console.log(chalk.gray(" \u2500".repeat(40)));
|
|
232
|
-
console.log(` Scope : ${chalk.cyan(scope)} (${targetDir})`);
|
|
233
|
-
if (plan.agents.length) console.log(` Agents : ${plan.agents.map((a) => a.name).join(", ")}`);
|
|
234
|
-
if (plan.skills.length) console.log(` Skills : ${plan.skills.map((s) => s.name).join(", ")}`);
|
|
235
|
-
if (plan.commands.length) console.log(` Commands: ${plan.commands.map((c) => c.name).join(", ")}`);
|
|
236
|
-
if (plan.hooks.length) console.log(` Hooks : ${plan.hooks.map((h) => h.name).join(", ")}`);
|
|
237
|
-
if (plan.ruleCategories.length) console.log(` Rules : ${plan.ruleCategories.join(", ")}`);
|
|
238
|
-
if (plan.mcps.length) console.log(` MCPs : ${plan.mcps.map((m) => m.name).join(", ")}`);
|
|
239
|
-
if (plan.claudemds.length) console.log(` ClaudeMd : ${plan.claudemds.map((c) => c.name).join(", ")}`);
|
|
240
|
-
if (plan.statusline) console.log(` Statusline: statusline.sh \u2192 ~/.claude/statusline.sh`);
|
|
241
|
-
console.log("");
|
|
242
|
-
const totalItems = plan.agents.length + plan.skills.length + plan.commands.length + plan.hooks.length + plan.ruleCategories.length + plan.mcps.length + plan.claudemds.length + (plan.statusline ? 1 : 0);
|
|
243
|
-
if (totalItems === 0) {
|
|
244
|
-
console.log(chalk.yellow(" Nothing selected. Exiting."));
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
const ok = await confirm({ message: "Proceed with installation?", default: true });
|
|
248
|
-
if (!ok) {
|
|
249
|
-
console.log(chalk.yellow("\n Aborted."));
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
console.log("");
|
|
253
|
-
const spinner = ora("Installing...").start();
|
|
254
|
-
try {
|
|
255
|
-
if (plan.agents.length) {
|
|
256
|
-
spinner.text = "Installing agents...";
|
|
257
|
-
await installAgents(plan.agents, targetDir);
|
|
258
|
-
}
|
|
259
|
-
if (plan.skills.length) {
|
|
260
|
-
spinner.text = "Installing skills...";
|
|
261
|
-
await installSkills(plan.skills, targetDir);
|
|
262
|
-
}
|
|
263
|
-
if (plan.commands.length) {
|
|
264
|
-
spinner.text = "Installing commands...";
|
|
265
|
-
await installCommands(plan.commands, targetDir);
|
|
266
|
-
}
|
|
267
|
-
if (plan.hooks.length) {
|
|
268
|
-
spinner.text = "Installing hooks...";
|
|
269
|
-
await installHooks(plan.hooks, targetDir);
|
|
270
|
-
}
|
|
271
|
-
if (plan.ruleCategories.length) {
|
|
272
|
-
spinner.text = "Installing rules...";
|
|
273
|
-
await installRules(plan.ruleCategories, targetDir);
|
|
274
|
-
}
|
|
275
|
-
if (plan.mcps.length) {
|
|
276
|
-
spinner.text = "Installing MCP servers...";
|
|
277
|
-
await installMcps(plan.mcps, plan.scope, plan.mcpEnvValues);
|
|
278
|
-
}
|
|
279
|
-
if (plan.claudemds.length) {
|
|
280
|
-
spinner.text = "Installing claudemd...";
|
|
281
|
-
await installClaudemd(plan.claudemds, targetDir);
|
|
282
|
-
}
|
|
283
|
-
if (plan.statusline) {
|
|
284
|
-
spinner.text = "Installing statusline...";
|
|
285
|
-
await installStatusline();
|
|
286
|
-
}
|
|
287
|
-
spinner.succeed(chalk.green("Installation complete!"));
|
|
288
|
-
console.log("");
|
|
289
|
-
if (plan.agents.length) console.log(` ${chalk.green("\u2713")} ${plan.agents.length} agent(s) \u2192 ${join2(targetDir, "agents")}`);
|
|
290
|
-
if (plan.skills.length) console.log(` ${chalk.green("\u2713")} ${plan.skills.length} skill(s) \u2192 ${join2(targetDir, "skills")}`);
|
|
291
|
-
if (plan.commands.length) console.log(` ${chalk.green("\u2713")} ${plan.commands.length} command(s) \u2192 ${join2(targetDir, "commands")}`);
|
|
292
|
-
if (plan.hooks.length) console.log(` ${chalk.green("\u2713")} ${plan.hooks.length} hook(s) \u2192 ${join2(targetDir, "hooks")}`);
|
|
293
|
-
if (plan.ruleCategories.length) console.log(` ${chalk.green("\u2713")} Rules copied \u2192 ${join2(targetDir, "rules")}`);
|
|
294
|
-
if (plan.mcps.length) console.log(` ${chalk.green("\u2713")} ${plan.mcps.length} MCP server(s) \u2192 ${scope === "global" ? "~/.claude.json" : "./.claude.json"}`);
|
|
295
|
-
if (plan.claudemds.length) console.log(` ${chalk.green("\u2713")} ClaudeMd appended \u2192 ${join2(targetDir, "CLAUDE.md")}`);
|
|
296
|
-
if (plan.statusline) console.log(` ${chalk.green("\u2713")} Statusline installed \u2192 ~/.claude/statusline.sh`);
|
|
297
|
-
console.log("");
|
|
298
|
-
if (skippedMcps.size > 0) {
|
|
299
|
-
console.log(chalk.yellow(" \u26A0 The following MCP servers have placeholder env vars:"));
|
|
300
|
-
for (const [serverName, keys] of skippedMcps) {
|
|
301
|
-
console.log(` ${chalk.bold("-")} ${serverName} ${chalk.gray(`(${keys.join(", ")})`)} `);
|
|
302
|
-
}
|
|
303
|
-
const configPath = scope === "global" ? "~/.claude.json" : "./.claude.json";
|
|
304
|
-
console.log(chalk.gray(` \u2192 Edit ${configPath} to set the actual values`));
|
|
305
|
-
console.log("");
|
|
306
|
-
}
|
|
307
|
-
} catch (err) {
|
|
308
|
-
spinner.fail(chalk.red("Installation failed"));
|
|
309
|
-
throw err;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
export {
|
|
313
|
-
runCli
|
|
314
|
-
};
|