@kood/claude-code 0.5.3 → 0.5.4
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/index.js +548 -340
- package/package.json +1 -1
- package/templates/.claude/agents/document-writer.md +71 -304
- package/templates/.claude/instructions/agent-patterns/index.md +7 -7
- package/templates/.claude/instructions/document-templates/ralph-templates.md +71 -0
- package/templates/.claude/instructions/index.md +14 -14
- package/templates/.claude/instructions/multi-agent/agent-roster.md +14 -14
- package/templates/.claude/instructions/multi-agent/index.md +4 -4
- package/templates/.claude/skills/docs-creator/AGENTS.md +54 -176
- package/templates/.claude/skills/docs-creator/SKILL.md +97 -463
- package/templates/.claude/skills/docs-refactor/AGENTS.md +61 -190
- package/templates/.claude/skills/docs-refactor/SKILL.md +66 -442
- package/templates/.claude/skills/execute/SKILL.md +540 -13
- package/templates/.claude/skills/plan/SKILL.md +83 -17
- package/templates/.claude/skills/ralph/SKILL.md +17 -14
- package/templates/.claude/skills/refactor/AGENTS.md +269 -0
- package/templates/.claude/skills/refactor/SKILL.md +424 -66
- package/templates/.claude/skills/stitch-design/README.md +34 -0
- package/templates/.claude/skills/stitch-design/SKILL.md +213 -0
- package/templates/.claude/skills/stitch-design/examples/DESIGN.md +154 -0
- package/templates/.claude/skills/stitch-loop/README.md +54 -0
- package/templates/.claude/skills/stitch-loop/SKILL.md +316 -0
- package/templates/.claude/skills/stitch-loop/examples/SITE.md +73 -0
- package/templates/.claude/skills/stitch-loop/examples/next-prompt.md +25 -0
- package/templates/.claude/skills/stitch-loop/resources/baton-schema.md +61 -0
- package/templates/.claude/skills/stitch-loop/resources/site-template.md +104 -0
- package/templates/.claude/skills/stitch-react/README.md +36 -0
- package/templates/.claude/skills/stitch-react/SKILL.md +323 -0
- package/templates/.claude/skills/stitch-react/examples/gold-standard-card.tsx +88 -0
- package/templates/.claude/skills/stitch-react/package-lock.json +231 -0
- package/templates/.claude/skills/stitch-react/package.json +16 -0
- package/templates/.claude/skills/stitch-react/resources/architecture-checklist.md +15 -0
- package/templates/.claude/skills/stitch-react/resources/component-template.tsx +37 -0
- package/templates/.claude/skills/stitch-react/resources/stitch-api-reference.md +14 -0
- package/templates/.claude/skills/stitch-react/resources/style-guide.json +24 -0
- package/templates/.claude/skills/stitch-react/scripts/fetch-stitch.sh +30 -0
- package/templates/.claude/skills/stitch-react/scripts/validate.js +77 -0
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
-
// src/
|
|
6
|
+
// src/shared/logger.ts
|
|
7
7
|
import pc from "picocolors";
|
|
8
8
|
var logger = {
|
|
9
9
|
info: (msg) => console.log(pc.cyan("\u2139"), msg),
|
|
@@ -22,18 +22,14 @@ var banner = () => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
25
|
-
import
|
|
26
|
-
import fs2 from "fs-extra";
|
|
25
|
+
import fs7 from "fs-extra";
|
|
27
26
|
|
|
28
|
-
// src/
|
|
29
|
-
import
|
|
30
|
-
import path from "path";
|
|
27
|
+
// src/features/templates/template-path-resolver.ts
|
|
28
|
+
import path2 from "path";
|
|
31
29
|
import { fileURLToPath } from "url";
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return path.resolve(__dirname2, "../templates");
|
|
36
|
-
};
|
|
30
|
+
|
|
31
|
+
// src/shared/validators/path-validators.ts
|
|
32
|
+
import path from "path";
|
|
37
33
|
var validateTemplateName = (template) => {
|
|
38
34
|
const sanitized = path.basename(template);
|
|
39
35
|
if (sanitized !== template || template.includes("..")) {
|
|
@@ -66,15 +62,30 @@ var validateTargetDir = (targetDir) => {
|
|
|
66
62
|
}
|
|
67
63
|
}
|
|
68
64
|
};
|
|
65
|
+
|
|
66
|
+
// src/features/templates/template-path-resolver.ts
|
|
67
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
68
|
+
var __dirname2 = path2.dirname(__filename2);
|
|
69
|
+
var getTemplatesDir = () => {
|
|
70
|
+
return path2.resolve(__dirname2, "../templates");
|
|
71
|
+
};
|
|
72
|
+
var getTemplatePath = (template) => {
|
|
73
|
+
validateTemplateName(template);
|
|
74
|
+
return path2.join(getTemplatesDir(), template);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// src/features/templates/template-installer.ts
|
|
78
|
+
import fs2 from "fs-extra";
|
|
79
|
+
import path4 from "path";
|
|
80
|
+
|
|
81
|
+
// src/shared/filesystem/copy-utils.ts
|
|
82
|
+
import fs from "fs-extra";
|
|
83
|
+
import path3 from "path";
|
|
69
84
|
var hasFiles = async (dir) => {
|
|
70
85
|
if (!await fs.pathExists(dir)) return false;
|
|
71
86
|
const items = await fs.readdir(dir);
|
|
72
87
|
return items.length > 0;
|
|
73
88
|
};
|
|
74
|
-
var getTemplatePath = (template) => {
|
|
75
|
-
validateTemplateName(template);
|
|
76
|
-
return path.join(getTemplatesDir(), template);
|
|
77
|
-
};
|
|
78
89
|
var copyRecursive = async (src, dest, counter) => {
|
|
79
90
|
const stat = await fs.stat(src);
|
|
80
91
|
if (stat.isDirectory()) {
|
|
@@ -82,76 +93,33 @@ var copyRecursive = async (src, dest, counter) => {
|
|
|
82
93
|
counter.directories++;
|
|
83
94
|
const items = await fs.readdir(src);
|
|
84
95
|
for (const item of items) {
|
|
85
|
-
await copyRecursive(
|
|
96
|
+
await copyRecursive(path3.join(src, item), path3.join(dest, item), counter);
|
|
86
97
|
}
|
|
87
98
|
} else {
|
|
88
99
|
await fs.copy(src, dest);
|
|
89
100
|
counter.files++;
|
|
90
101
|
}
|
|
91
102
|
};
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
await copyRecursive(docsSrc, docsDest, counter);
|
|
111
|
-
}
|
|
112
|
-
return counter;
|
|
113
|
-
};
|
|
114
|
-
var copyMultipleTemplates = async (templates, targetDir) => {
|
|
115
|
-
validateTargetDir(targetDir);
|
|
116
|
-
const counter = { files: 0, directories: 0 };
|
|
117
|
-
const docsDir = path.join(targetDir, "docs");
|
|
118
|
-
if (await fs.pathExists(docsDir)) {
|
|
119
|
-
await fs.remove(docsDir);
|
|
120
|
-
}
|
|
121
|
-
for (const template of templates) {
|
|
122
|
-
const templatePath = getTemplatePath(template);
|
|
123
|
-
if (!await fs.pathExists(templatePath)) {
|
|
124
|
-
throw new Error(`Template "${template}" not found at ${templatePath}`);
|
|
125
|
-
}
|
|
126
|
-
await copyRecursive(
|
|
127
|
-
templatePath,
|
|
128
|
-
path.join(targetDir, "docs", template),
|
|
129
|
-
counter
|
|
130
|
-
);
|
|
103
|
+
|
|
104
|
+
// src/features/templates/template-metadata.ts
|
|
105
|
+
var templateMetadata = {
|
|
106
|
+
"tanstack-start": {
|
|
107
|
+
name: "TanStack Start",
|
|
108
|
+
description: "React + TanStack Router SSR \uD480\uC2A4\uD0DD \uD504\uB85C\uC81D\uD2B8",
|
|
109
|
+
stack: "React, TanStack Router, Vite, TypeScript"
|
|
110
|
+
},
|
|
111
|
+
hono: {
|
|
112
|
+
name: "Hono",
|
|
113
|
+
description: "Edge Runtime \uBC31\uC5D4\uB4DC API \uD504\uB85C\uC81D\uD2B8",
|
|
114
|
+
stack: "Hono, TypeScript, Cloudflare Workers"
|
|
115
|
+
},
|
|
116
|
+
npx: {
|
|
117
|
+
name: "NPX CLI",
|
|
118
|
+
description: "NPX\uB85C \uC2E4\uD589 \uAC00\uB2A5\uD55C CLI \uB3C4\uAD6C \uD504\uB85C\uC81D\uD2B8",
|
|
119
|
+
stack: "Node.js, TypeScript, Commander.js"
|
|
131
120
|
}
|
|
132
|
-
const indexContent = generateIndexClaudeMd(templates);
|
|
133
|
-
await fs.writeFile(path.join(targetDir, "CLAUDE.md"), indexContent);
|
|
134
|
-
counter.files++;
|
|
135
|
-
return counter;
|
|
136
121
|
};
|
|
137
122
|
var generateIndexClaudeMd = (templates) => {
|
|
138
|
-
const templateMetadata = {
|
|
139
|
-
"tanstack-start": {
|
|
140
|
-
name: "TanStack Start",
|
|
141
|
-
description: "React + TanStack Router SSR \uD480\uC2A4\uD0DD \uD504\uB85C\uC81D\uD2B8",
|
|
142
|
-
stack: "React, TanStack Router, Vite, TypeScript"
|
|
143
|
-
},
|
|
144
|
-
hono: {
|
|
145
|
-
name: "Hono",
|
|
146
|
-
description: "Edge Runtime \uBC31\uC5D4\uB4DC API \uD504\uB85C\uC81D\uD2B8",
|
|
147
|
-
stack: "Hono, TypeScript, Cloudflare Workers"
|
|
148
|
-
},
|
|
149
|
-
npx: {
|
|
150
|
-
name: "NPX CLI",
|
|
151
|
-
description: "NPX\uB85C \uC2E4\uD589 \uAC00\uB2A5\uD55C CLI \uB3C4\uAD6C \uD504\uB85C\uC81D\uD2B8",
|
|
152
|
-
stack: "Node.js, TypeScript, Commander.js"
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
123
|
const templateSections = templates.map((template) => {
|
|
156
124
|
const meta = templateMetadata[template] || {
|
|
157
125
|
name: template,
|
|
@@ -180,37 +148,94 @@ ${templateSections}
|
|
|
180
148
|
**\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uC5D0 \uB9DE\uB294 \uAC00\uC774\uB4DC\uB97C CLAUDE.md\uC758 \`@\` \uC778\uC2A4\uD2B8\uB7ED\uC158\uC5D0 \uCD94\uAC00\uD558\uC138\uC694.**
|
|
181
149
|
`;
|
|
182
150
|
};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
151
|
+
|
|
152
|
+
// src/features/templates/template-installer.ts
|
|
153
|
+
var copySingleTemplate = async (template, targetDir) => {
|
|
154
|
+
validateTargetDir(targetDir);
|
|
155
|
+
const templatePath = getTemplatePath(template);
|
|
156
|
+
if (!await fs2.pathExists(templatePath)) {
|
|
157
|
+
throw new Error(`Template "${template}" not found at ${templatePath}`);
|
|
189
158
|
}
|
|
190
|
-
|
|
191
|
-
|
|
159
|
+
const counter = { files: 0, directories: 0 };
|
|
160
|
+
const claudeMdSrc = path4.join(templatePath, "CLAUDE.md");
|
|
161
|
+
if (await fs2.pathExists(claudeMdSrc)) {
|
|
162
|
+
await fs2.copy(claudeMdSrc, path4.join(targetDir, "CLAUDE.md"));
|
|
163
|
+
counter.files++;
|
|
192
164
|
}
|
|
193
|
-
|
|
165
|
+
const docsSrc = path4.join(templatePath, "docs");
|
|
166
|
+
const docsDest = path4.join(targetDir, "docs");
|
|
167
|
+
if (await fs2.pathExists(docsSrc)) {
|
|
168
|
+
if (await fs2.pathExists(docsDest)) {
|
|
169
|
+
await fs2.remove(docsDest);
|
|
170
|
+
}
|
|
171
|
+
await copyRecursive(docsSrc, docsDest, counter);
|
|
172
|
+
}
|
|
173
|
+
return counter;
|
|
194
174
|
};
|
|
175
|
+
var copyMultipleTemplates = async (templates, targetDir) => {
|
|
176
|
+
validateTargetDir(targetDir);
|
|
177
|
+
const counter = { files: 0, directories: 0 };
|
|
178
|
+
const docsDir = path4.join(targetDir, "docs");
|
|
179
|
+
if (await fs2.pathExists(docsDir)) {
|
|
180
|
+
await fs2.remove(docsDir);
|
|
181
|
+
}
|
|
182
|
+
for (const template of templates) {
|
|
183
|
+
const templatePath = getTemplatePath(template);
|
|
184
|
+
if (!await fs2.pathExists(templatePath)) {
|
|
185
|
+
throw new Error(`Template "${template}" not found at ${templatePath}`);
|
|
186
|
+
}
|
|
187
|
+
await copyRecursive(
|
|
188
|
+
templatePath,
|
|
189
|
+
path4.join(targetDir, "docs", template),
|
|
190
|
+
counter
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
const indexContent = generateIndexClaudeMd(templates);
|
|
194
|
+
await fs2.writeFile(path4.join(targetDir, "CLAUDE.md"), indexContent);
|
|
195
|
+
counter.files++;
|
|
196
|
+
return counter;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/features/templates/template-discovery.ts
|
|
200
|
+
import fs3 from "fs-extra";
|
|
201
|
+
import path5 from "path";
|
|
195
202
|
var listAvailableTemplates = async () => {
|
|
196
203
|
const templatesDir = getTemplatesDir();
|
|
197
|
-
if (!await
|
|
204
|
+
if (!await fs3.pathExists(templatesDir)) {
|
|
198
205
|
return [];
|
|
199
206
|
}
|
|
200
|
-
const items = await
|
|
207
|
+
const items = await fs3.readdir(templatesDir);
|
|
201
208
|
const templates = [];
|
|
202
209
|
for (const item of items) {
|
|
203
210
|
if (item.startsWith(".")) {
|
|
204
211
|
continue;
|
|
205
212
|
}
|
|
206
|
-
const itemPath =
|
|
207
|
-
const stat = await
|
|
213
|
+
const itemPath = path5.join(templatesDir, item);
|
|
214
|
+
const stat = await fs3.stat(itemPath);
|
|
208
215
|
if (stat.isDirectory()) {
|
|
209
216
|
templates.push(item);
|
|
210
217
|
}
|
|
211
218
|
}
|
|
212
219
|
return templates;
|
|
213
220
|
};
|
|
221
|
+
var checkExistingFiles = async (targetDir) => {
|
|
222
|
+
const existingFiles = [];
|
|
223
|
+
const claudeMd = path5.join(targetDir, "CLAUDE.md");
|
|
224
|
+
const docsDir = path5.join(targetDir, "docs");
|
|
225
|
+
if (await fs3.pathExists(claudeMd)) {
|
|
226
|
+
existingFiles.push("CLAUDE.md");
|
|
227
|
+
}
|
|
228
|
+
if (await fs3.pathExists(docsDir)) {
|
|
229
|
+
existingFiles.push("docs/");
|
|
230
|
+
}
|
|
231
|
+
return existingFiles;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// src/features/extras/extras-copier.ts
|
|
235
|
+
import fs4 from "fs-extra";
|
|
236
|
+
import path6 from "path";
|
|
237
|
+
|
|
238
|
+
// src/shared/constants.ts
|
|
214
239
|
var FRAMEWORK_SPECIFIC_SKILLS_MAP = {
|
|
215
240
|
nextjs: ["nextjs-react-best-practices", "korea-uiux-design", "figma-to-code"],
|
|
216
241
|
"tanstack-start": [
|
|
@@ -229,14 +254,31 @@ var COMMON_SKILLS = [
|
|
|
229
254
|
"ralph",
|
|
230
255
|
"execute"
|
|
231
256
|
];
|
|
257
|
+
|
|
258
|
+
// src/features/extras/extras-copier.ts
|
|
259
|
+
var createExtrasCopier = (extrasType) => {
|
|
260
|
+
return async (_templates, targetDir) => {
|
|
261
|
+
const counter = { files: 0, directories: 0 };
|
|
262
|
+
const targetExtrasDir = path6.join(targetDir, ".claude", extrasType);
|
|
263
|
+
const extrasSrc = path6.join(getTemplatesDir(), ".claude", extrasType);
|
|
264
|
+
if (await fs4.pathExists(extrasSrc)) {
|
|
265
|
+
await fs4.ensureDir(targetExtrasDir);
|
|
266
|
+
await copyRecursive(extrasSrc, targetExtrasDir, counter);
|
|
267
|
+
}
|
|
268
|
+
return counter;
|
|
269
|
+
};
|
|
270
|
+
};
|
|
271
|
+
var copyCommands = createExtrasCopier("commands");
|
|
272
|
+
var copyAgents = createExtrasCopier("agents");
|
|
273
|
+
var copyInstructions = createExtrasCopier("instructions");
|
|
232
274
|
var copySkills = async (templates, targetDir) => {
|
|
233
275
|
const counter = { files: 0, directories: 0 };
|
|
234
|
-
const targetSkillsDir =
|
|
235
|
-
const skillsSrc =
|
|
236
|
-
if (!await
|
|
276
|
+
const targetSkillsDir = path6.join(targetDir, ".claude", "skills");
|
|
277
|
+
const skillsSrc = path6.join(getTemplatesDir(), ".claude", "skills");
|
|
278
|
+
if (!await fs4.pathExists(skillsSrc)) {
|
|
237
279
|
return counter;
|
|
238
280
|
}
|
|
239
|
-
await
|
|
281
|
+
await fs4.ensureDir(targetSkillsDir);
|
|
240
282
|
const skillsToCopy = /* @__PURE__ */ new Set();
|
|
241
283
|
const skillTemplateMap = /* @__PURE__ */ new Map();
|
|
242
284
|
COMMON_SKILLS.forEach((skill) => skillsToCopy.add(skill));
|
|
@@ -251,11 +293,11 @@ var copySkills = async (templates, targetDir) => {
|
|
|
251
293
|
});
|
|
252
294
|
}
|
|
253
295
|
for (const skill of skillsToCopy) {
|
|
254
|
-
const skillSrc =
|
|
255
|
-
const skillDest =
|
|
256
|
-
if (await
|
|
257
|
-
if (await
|
|
258
|
-
await
|
|
296
|
+
const skillSrc = path6.join(skillsSrc, skill);
|
|
297
|
+
const skillDest = path6.join(targetSkillsDir, skill);
|
|
298
|
+
if (await fs4.pathExists(skillSrc)) {
|
|
299
|
+
if (await fs4.pathExists(skillDest)) {
|
|
300
|
+
await fs4.remove(skillDest);
|
|
259
301
|
}
|
|
260
302
|
await copyRecursive(skillSrc, skillDest, counter);
|
|
261
303
|
}
|
|
@@ -274,65 +316,35 @@ var copySkills = async (templates, targetDir) => {
|
|
|
274
316
|
...duplicateSkills.length > 0 && { duplicateSkills }
|
|
275
317
|
};
|
|
276
318
|
};
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (await fs.pathExists(commandsSrc)) {
|
|
282
|
-
await fs.ensureDir(targetCommandsDir);
|
|
283
|
-
await copyRecursive(commandsSrc, targetCommandsDir, counter);
|
|
284
|
-
}
|
|
285
|
-
return counter;
|
|
286
|
-
};
|
|
319
|
+
|
|
320
|
+
// src/features/extras/extras-checker.ts
|
|
321
|
+
import fs5 from "fs-extra";
|
|
322
|
+
import path7 from "path";
|
|
287
323
|
var checkExistingClaudeFiles = async (targetDir) => {
|
|
288
324
|
const existingFiles = [];
|
|
289
|
-
const skillsDir =
|
|
290
|
-
const commandsDir =
|
|
291
|
-
const agentsDir =
|
|
292
|
-
const instructionsDir =
|
|
293
|
-
if (await
|
|
325
|
+
const skillsDir = path7.join(targetDir, ".claude", "skills");
|
|
326
|
+
const commandsDir = path7.join(targetDir, ".claude", "commands");
|
|
327
|
+
const agentsDir = path7.join(targetDir, ".claude", "agents");
|
|
328
|
+
const instructionsDir = path7.join(targetDir, ".claude", "instructions");
|
|
329
|
+
if (await fs5.pathExists(skillsDir)) {
|
|
294
330
|
existingFiles.push(".claude/skills/");
|
|
295
331
|
}
|
|
296
|
-
if (await
|
|
332
|
+
if (await fs5.pathExists(commandsDir)) {
|
|
297
333
|
existingFiles.push(".claude/commands/");
|
|
298
334
|
}
|
|
299
|
-
if (await
|
|
335
|
+
if (await fs5.pathExists(agentsDir)) {
|
|
300
336
|
existingFiles.push(".claude/agents/");
|
|
301
337
|
}
|
|
302
|
-
if (await
|
|
338
|
+
if (await fs5.pathExists(instructionsDir)) {
|
|
303
339
|
existingFiles.push(".claude/instructions/");
|
|
304
340
|
}
|
|
305
341
|
return existingFiles;
|
|
306
342
|
};
|
|
307
|
-
var copyAgents = async (_templates, targetDir) => {
|
|
308
|
-
const counter = { files: 0, directories: 0 };
|
|
309
|
-
const targetAgentsDir = path.join(targetDir, ".claude", "agents");
|
|
310
|
-
const agentsSrc = path.join(getTemplatesDir(), ".claude", "agents");
|
|
311
|
-
if (await fs.pathExists(agentsSrc)) {
|
|
312
|
-
await fs.ensureDir(targetAgentsDir);
|
|
313
|
-
await copyRecursive(agentsSrc, targetAgentsDir, counter);
|
|
314
|
-
}
|
|
315
|
-
return counter;
|
|
316
|
-
};
|
|
317
|
-
var copyInstructions = async (_templates, targetDir) => {
|
|
318
|
-
const counter = { files: 0, directories: 0 };
|
|
319
|
-
const targetInstructionsDir = path.join(targetDir, ".claude", "instructions");
|
|
320
|
-
const instructionsSrc = path.join(
|
|
321
|
-
getTemplatesDir(),
|
|
322
|
-
".claude",
|
|
323
|
-
"instructions"
|
|
324
|
-
);
|
|
325
|
-
if (await fs.pathExists(instructionsSrc)) {
|
|
326
|
-
await fs.ensureDir(targetInstructionsDir);
|
|
327
|
-
await copyRecursive(instructionsSrc, targetInstructionsDir, counter);
|
|
328
|
-
}
|
|
329
|
-
return counter;
|
|
330
|
-
};
|
|
331
343
|
var checkAllExtrasExist = async (templates) => {
|
|
332
|
-
const claudeDir =
|
|
333
|
-
const commandsSrc =
|
|
334
|
-
const agentsSrc =
|
|
335
|
-
const instructionsSrc =
|
|
344
|
+
const claudeDir = path7.join(getTemplatesDir(), ".claude");
|
|
345
|
+
const commandsSrc = path7.join(claudeDir, "commands");
|
|
346
|
+
const agentsSrc = path7.join(claudeDir, "agents");
|
|
347
|
+
const instructionsSrc = path7.join(claudeDir, "instructions");
|
|
336
348
|
const hasFrameworkSkills = templates.some((template) => {
|
|
337
349
|
const skills = FRAMEWORK_SPECIFIC_SKILLS_MAP[template];
|
|
338
350
|
return skills && skills.length > 0;
|
|
@@ -344,16 +356,332 @@ var checkAllExtrasExist = async (templates) => {
|
|
|
344
356
|
return { hasSkills, hasCommands, hasAgents, hasInstructions };
|
|
345
357
|
};
|
|
346
358
|
|
|
359
|
+
// src/shared/prompts/prompt-helpers.ts
|
|
360
|
+
import prompts from "prompts";
|
|
361
|
+
async function promptConfirm(message, initial = false) {
|
|
362
|
+
const response = await prompts({
|
|
363
|
+
type: "confirm",
|
|
364
|
+
name: "confirmed",
|
|
365
|
+
message,
|
|
366
|
+
initial
|
|
367
|
+
});
|
|
368
|
+
return { confirmed: response.confirmed ?? false };
|
|
369
|
+
}
|
|
370
|
+
async function promptSelect(message, choices) {
|
|
371
|
+
const response = await prompts({
|
|
372
|
+
type: "select",
|
|
373
|
+
name: "value",
|
|
374
|
+
message,
|
|
375
|
+
choices
|
|
376
|
+
});
|
|
377
|
+
return { value: response.value };
|
|
378
|
+
}
|
|
379
|
+
async function promptMultiselect(message, choices, options) {
|
|
380
|
+
const response = await prompts({
|
|
381
|
+
type: "multiselect",
|
|
382
|
+
name: "values",
|
|
383
|
+
message,
|
|
384
|
+
choices,
|
|
385
|
+
min: options?.min,
|
|
386
|
+
hint: options?.hint
|
|
387
|
+
});
|
|
388
|
+
return { values: response.values || [] };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/shared/prompts/prompt-functions.ts
|
|
392
|
+
async function promptTemplateSelection(options) {
|
|
393
|
+
const { providedTemplates, availableTemplates, templateDescriptions } = options;
|
|
394
|
+
let templates = providedTemplates || [];
|
|
395
|
+
if (templates.length === 0) {
|
|
396
|
+
const response = await promptMultiselect(
|
|
397
|
+
"Select templates (space to select, enter to confirm):",
|
|
398
|
+
availableTemplates.map((t) => ({
|
|
399
|
+
title: t,
|
|
400
|
+
description: templateDescriptions[t] || "",
|
|
401
|
+
value: t
|
|
402
|
+
})),
|
|
403
|
+
{
|
|
404
|
+
min: 1,
|
|
405
|
+
hint: "- Space to select. Return to submit"
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
if (response.values.length === 0) {
|
|
409
|
+
logger.warn("Operation cancelled.");
|
|
410
|
+
process.exit(0);
|
|
411
|
+
}
|
|
412
|
+
templates = response.values;
|
|
413
|
+
}
|
|
414
|
+
const invalidTemplates = templates.filter(
|
|
415
|
+
(t) => !availableTemplates.includes(t)
|
|
416
|
+
);
|
|
417
|
+
if (invalidTemplates.length > 0) {
|
|
418
|
+
logger.error(`Templates not found: ${invalidTemplates.join(", ")}`);
|
|
419
|
+
logger.info(`Available templates: ${availableTemplates.join(", ")}`);
|
|
420
|
+
process.exit(1);
|
|
421
|
+
}
|
|
422
|
+
return { templates };
|
|
423
|
+
}
|
|
424
|
+
async function promptOverwrite(options) {
|
|
425
|
+
const { existingFiles, force } = options;
|
|
426
|
+
if (existingFiles.length > 0 && !force) {
|
|
427
|
+
logger.warn("The following files/folders already exist:");
|
|
428
|
+
existingFiles.forEach((f) => logger.step(f));
|
|
429
|
+
logger.blank();
|
|
430
|
+
const result = await promptConfirm("Overwrite existing files?", false);
|
|
431
|
+
if (!result.confirmed) {
|
|
432
|
+
logger.info("Operation cancelled.");
|
|
433
|
+
process.exit(0);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function promptExtrasSelection(options) {
|
|
438
|
+
const {
|
|
439
|
+
skills,
|
|
440
|
+
commands,
|
|
441
|
+
agents,
|
|
442
|
+
instructions,
|
|
443
|
+
hasSkills,
|
|
444
|
+
hasCommands,
|
|
445
|
+
hasAgents,
|
|
446
|
+
hasInstructions
|
|
447
|
+
} = options;
|
|
448
|
+
let installSkills = skills ?? false;
|
|
449
|
+
let installCommands = commands ?? false;
|
|
450
|
+
let installAgents = agents ?? hasAgents;
|
|
451
|
+
let installInstructions = instructions ?? hasInstructions;
|
|
452
|
+
const noOptionsProvided = skills === void 0 && commands === void 0 && agents === void 0 && instructions === void 0;
|
|
453
|
+
if (noOptionsProvided && (hasSkills || hasCommands || hasAgents || hasInstructions)) {
|
|
454
|
+
logger.blank();
|
|
455
|
+
if (hasSkills) {
|
|
456
|
+
const result = await promptConfirm(
|
|
457
|
+
"Install skills to .claude/skills/?",
|
|
458
|
+
false
|
|
459
|
+
);
|
|
460
|
+
installSkills = result.confirmed;
|
|
461
|
+
}
|
|
462
|
+
if (hasCommands) {
|
|
463
|
+
const result = await promptConfirm(
|
|
464
|
+
"Install commands to .claude/commands/?",
|
|
465
|
+
false
|
|
466
|
+
);
|
|
467
|
+
installCommands = result.confirmed;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
installSkills,
|
|
472
|
+
installCommands,
|
|
473
|
+
installAgents,
|
|
474
|
+
installInstructions
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// src/features/extras/extras-installer.ts
|
|
479
|
+
async function handleDuplicateFiles(existingClaudeFiles, force) {
|
|
480
|
+
if (existingClaudeFiles.length === 0 || force) {
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
logger.warn("The following .claude files/folders already exist:");
|
|
484
|
+
existingClaudeFiles.forEach((f) => logger.step(f));
|
|
485
|
+
logger.blank();
|
|
486
|
+
const response = await promptConfirm(
|
|
487
|
+
"Overwrite existing .claude files?",
|
|
488
|
+
false
|
|
489
|
+
);
|
|
490
|
+
return response.confirmed;
|
|
491
|
+
}
|
|
492
|
+
async function handleDuplicateSkills(duplicateSkills, templates, targetDir) {
|
|
493
|
+
logger.blank();
|
|
494
|
+
logger.warn("The following skills are included in multiple templates:");
|
|
495
|
+
duplicateSkills.forEach(({ skill, templates: skillTemplates }) => {
|
|
496
|
+
logger.step(`${skill} (${skillTemplates.join(", ")})`);
|
|
497
|
+
});
|
|
498
|
+
logger.blank();
|
|
499
|
+
const response = await promptSelect(
|
|
500
|
+
"Which template's version should be used?",
|
|
501
|
+
templates.map((t) => ({
|
|
502
|
+
title: t,
|
|
503
|
+
value: t
|
|
504
|
+
}))
|
|
505
|
+
);
|
|
506
|
+
if (response.value) {
|
|
507
|
+
logger.info(`Reinstalling skills with ${response.value} template...`);
|
|
508
|
+
const reinstallResult = await copySkills([response.value], targetDir);
|
|
509
|
+
logger.success(
|
|
510
|
+
`Skills: ${reinstallResult.files} files, ${reinstallResult.directories} directories`
|
|
511
|
+
);
|
|
512
|
+
return reinstallResult;
|
|
513
|
+
} else {
|
|
514
|
+
logger.warn("No template selected. Using all templates.");
|
|
515
|
+
return { files: 0, directories: 0 };
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
async function installSkillsIfNeeded(templates, targetDir, shouldInstall, hasSkills) {
|
|
519
|
+
if (!shouldInstall) {
|
|
520
|
+
return { files: 0, directories: 0 };
|
|
521
|
+
}
|
|
522
|
+
if (!hasSkills) {
|
|
523
|
+
logger.warn("No skills found in selected templates.");
|
|
524
|
+
return { files: 0, directories: 0 };
|
|
525
|
+
}
|
|
526
|
+
logger.blank();
|
|
527
|
+
logger.info("Installing skills...");
|
|
528
|
+
const skillsResult = await copySkills(templates, targetDir);
|
|
529
|
+
if (skillsResult.duplicateSkills && skillsResult.duplicateSkills.length > 0) {
|
|
530
|
+
return await handleDuplicateSkills(
|
|
531
|
+
skillsResult.duplicateSkills,
|
|
532
|
+
templates,
|
|
533
|
+
targetDir
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
logger.success(
|
|
537
|
+
`Skills: ${skillsResult.files} files, ${skillsResult.directories} directories`
|
|
538
|
+
);
|
|
539
|
+
return skillsResult;
|
|
540
|
+
}
|
|
541
|
+
async function installCommandsIfNeeded(templates, targetDir, shouldInstall, hasCommands) {
|
|
542
|
+
if (!shouldInstall) {
|
|
543
|
+
return { files: 0, directories: 0 };
|
|
544
|
+
}
|
|
545
|
+
if (!hasCommands) {
|
|
546
|
+
logger.warn("No commands found in selected templates.");
|
|
547
|
+
return { files: 0, directories: 0 };
|
|
548
|
+
}
|
|
549
|
+
logger.blank();
|
|
550
|
+
logger.info("Installing commands...");
|
|
551
|
+
const commandsResult = await copyCommands(templates, targetDir);
|
|
552
|
+
logger.success(
|
|
553
|
+
`Commands: ${commandsResult.files} files, ${commandsResult.directories} directories`
|
|
554
|
+
);
|
|
555
|
+
return commandsResult;
|
|
556
|
+
}
|
|
557
|
+
async function installAgentsIfNeeded(templates, targetDir, shouldInstall, hasAgents) {
|
|
558
|
+
if (!shouldInstall) {
|
|
559
|
+
return { files: 0, directories: 0 };
|
|
560
|
+
}
|
|
561
|
+
if (!hasAgents) {
|
|
562
|
+
logger.warn("No agents found in selected templates.");
|
|
563
|
+
return { files: 0, directories: 0 };
|
|
564
|
+
}
|
|
565
|
+
logger.blank();
|
|
566
|
+
logger.info("Installing agents...");
|
|
567
|
+
const agentsResult = await copyAgents(templates, targetDir);
|
|
568
|
+
logger.success(
|
|
569
|
+
`Agents: ${agentsResult.files} files, ${agentsResult.directories} directories`
|
|
570
|
+
);
|
|
571
|
+
return agentsResult;
|
|
572
|
+
}
|
|
573
|
+
async function installInstructionsIfNeeded(templates, targetDir, shouldInstall, hasInstructions) {
|
|
574
|
+
if (!shouldInstall) {
|
|
575
|
+
return { files: 0, directories: 0 };
|
|
576
|
+
}
|
|
577
|
+
if (!hasInstructions) {
|
|
578
|
+
logger.warn("No instructions found in selected templates.");
|
|
579
|
+
return { files: 0, directories: 0 };
|
|
580
|
+
}
|
|
581
|
+
logger.blank();
|
|
582
|
+
logger.info("Installing instructions...");
|
|
583
|
+
const instructionsResult = await copyInstructions(templates, targetDir);
|
|
584
|
+
logger.success(
|
|
585
|
+
`Instructions: ${instructionsResult.files} files, ${instructionsResult.directories} directories`
|
|
586
|
+
);
|
|
587
|
+
return instructionsResult;
|
|
588
|
+
}
|
|
589
|
+
async function installExtras(templates, targetDir, flags, availability, force) {
|
|
590
|
+
const { installSkills, installCommands, installAgents, installInstructions } = flags;
|
|
591
|
+
if (!installSkills && !installCommands && !installAgents && !installInstructions) {
|
|
592
|
+
return { files: 0, directories: 0 };
|
|
593
|
+
}
|
|
594
|
+
const existingClaudeFiles = await checkExistingClaudeFiles(targetDir);
|
|
595
|
+
const shouldProceed = await handleDuplicateFiles(existingClaudeFiles, force);
|
|
596
|
+
if (!shouldProceed) {
|
|
597
|
+
logger.info("Skipping extras installation.");
|
|
598
|
+
return { files: 0, directories: 0 };
|
|
599
|
+
}
|
|
600
|
+
const skillsResult = await installSkillsIfNeeded(
|
|
601
|
+
templates,
|
|
602
|
+
targetDir,
|
|
603
|
+
installSkills,
|
|
604
|
+
availability.hasSkills
|
|
605
|
+
);
|
|
606
|
+
const commandsResult = await installCommandsIfNeeded(
|
|
607
|
+
templates,
|
|
608
|
+
targetDir,
|
|
609
|
+
installCommands,
|
|
610
|
+
availability.hasCommands
|
|
611
|
+
);
|
|
612
|
+
const agentsResult = await installAgentsIfNeeded(
|
|
613
|
+
templates,
|
|
614
|
+
targetDir,
|
|
615
|
+
installAgents,
|
|
616
|
+
availability.hasAgents
|
|
617
|
+
);
|
|
618
|
+
const instructionsResult = await installInstructionsIfNeeded(
|
|
619
|
+
templates,
|
|
620
|
+
targetDir,
|
|
621
|
+
installInstructions,
|
|
622
|
+
availability.hasInstructions
|
|
623
|
+
);
|
|
624
|
+
const totalFiles = skillsResult.files + commandsResult.files + agentsResult.files + instructionsResult.files;
|
|
625
|
+
const totalDirectories = skillsResult.directories + commandsResult.directories + agentsResult.directories + instructionsResult.directories;
|
|
626
|
+
return { files: totalFiles, directories: totalDirectories };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/shared/gitignore-manager.ts
|
|
630
|
+
import fs6 from "fs-extra";
|
|
631
|
+
import path8 from "path";
|
|
632
|
+
var CLAUDE_GENERATED_FOLDERS = ["CLAUDE.md", "docs/", ".claude/"];
|
|
633
|
+
async function updateGitignore(targetDir) {
|
|
634
|
+
const gitignorePath = path8.join(targetDir, ".gitignore");
|
|
635
|
+
const sectionComment = "# Claude Code generated files";
|
|
636
|
+
let content = "";
|
|
637
|
+
let hasGitignore = false;
|
|
638
|
+
try {
|
|
639
|
+
content = await fs6.readFile(gitignorePath, "utf-8");
|
|
640
|
+
hasGitignore = true;
|
|
641
|
+
} catch (error) {
|
|
642
|
+
content = "";
|
|
643
|
+
}
|
|
644
|
+
if (content.includes(sectionComment)) {
|
|
645
|
+
logger.info(".gitignore already contains Claude Code section");
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const linesToAdd = [];
|
|
649
|
+
const existingLines = content.split("\n").map((line) => line.trim());
|
|
650
|
+
for (const folder of CLAUDE_GENERATED_FOLDERS) {
|
|
651
|
+
if (!existingLines.includes(folder)) {
|
|
652
|
+
linesToAdd.push(folder);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (linesToAdd.length === 0) {
|
|
656
|
+
logger.info(".gitignore already contains all Claude Code patterns");
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
let newContent = content;
|
|
660
|
+
if (newContent && !newContent.endsWith("\n")) {
|
|
661
|
+
newContent += "\n";
|
|
662
|
+
}
|
|
663
|
+
if (newContent) {
|
|
664
|
+
newContent += "\n";
|
|
665
|
+
}
|
|
666
|
+
newContent += sectionComment + "\n";
|
|
667
|
+
newContent += linesToAdd.join("\n") + "\n";
|
|
668
|
+
await fs6.writeFile(gitignorePath, newContent, "utf-8");
|
|
669
|
+
if (hasGitignore) {
|
|
670
|
+
logger.success(`.gitignore updated with ${linesToAdd.length} patterns`);
|
|
671
|
+
} else {
|
|
672
|
+
logger.success(`.gitignore created with ${linesToAdd.length} patterns`);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
347
676
|
// src/commands/init.ts
|
|
348
677
|
var TEMPLATE_DESCRIPTIONS = {
|
|
349
678
|
"tanstack-start": "TanStack Start + React \uD480\uC2A4\uD0DD \uD504\uB85C\uC81D\uD2B8",
|
|
350
679
|
hono: "Hono \uC11C\uBC84 \uD504\uB808\uC784\uC6CC\uD06C \uD504\uB85C\uC81D\uD2B8",
|
|
351
680
|
npx: "NPX CLI \uB3C4\uAD6C \uD504\uB85C\uC81D\uD2B8"
|
|
352
681
|
};
|
|
353
|
-
|
|
354
|
-
const targetDir = options.cwd || process.cwd();
|
|
682
|
+
async function validateTargetDirectory(targetDir) {
|
|
355
683
|
try {
|
|
356
|
-
const stat = await
|
|
684
|
+
const stat = await fs7.stat(targetDir);
|
|
357
685
|
if (!stat.isDirectory()) {
|
|
358
686
|
logger.error(`Target is not a directory: ${targetDir}`);
|
|
359
687
|
process.exit(1);
|
|
@@ -367,60 +695,25 @@ var init = async (options) => {
|
|
|
367
695
|
process.exit(1);
|
|
368
696
|
}
|
|
369
697
|
try {
|
|
370
|
-
await
|
|
698
|
+
await fs7.access(targetDir, fs7.constants.W_OK);
|
|
371
699
|
} catch {
|
|
372
700
|
logger.error(`No write permission for: ${targetDir}`);
|
|
373
701
|
process.exit(1);
|
|
374
702
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
message: "Select templates (space to select, enter to confirm):",
|
|
386
|
-
choices: availableTemplates.map((t) => ({
|
|
387
|
-
title: t,
|
|
388
|
-
description: TEMPLATE_DESCRIPTIONS[t] || "",
|
|
389
|
-
value: t
|
|
390
|
-
})),
|
|
391
|
-
min: 1,
|
|
392
|
-
hint: "- Space to select. Return to submit"
|
|
393
|
-
});
|
|
394
|
-
if (!response.templates || response.templates.length === 0) {
|
|
395
|
-
logger.warn("Operation cancelled.");
|
|
396
|
-
process.exit(0);
|
|
397
|
-
}
|
|
398
|
-
templates = response.templates;
|
|
399
|
-
}
|
|
400
|
-
const invalidTemplates = templates.filter(
|
|
401
|
-
(t) => !availableTemplates.includes(t)
|
|
402
|
-
);
|
|
403
|
-
if (invalidTemplates.length > 0) {
|
|
404
|
-
logger.error(`Templates not found: ${invalidTemplates.join(", ")}`);
|
|
405
|
-
logger.info(`Available templates: ${availableTemplates.join(", ")}`);
|
|
406
|
-
process.exit(1);
|
|
407
|
-
}
|
|
703
|
+
}
|
|
704
|
+
async function selectTemplates(options, availableTemplates) {
|
|
705
|
+
const result = await promptTemplateSelection({
|
|
706
|
+
providedTemplates: options.templates,
|
|
707
|
+
availableTemplates,
|
|
708
|
+
templateDescriptions: TEMPLATE_DESCRIPTIONS
|
|
709
|
+
});
|
|
710
|
+
return result.templates;
|
|
711
|
+
}
|
|
712
|
+
async function confirmOverwriteIfNeeded(targetDir, force) {
|
|
408
713
|
const existingFiles = await checkExistingFiles(targetDir);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
logger.blank();
|
|
413
|
-
const response = await prompts({
|
|
414
|
-
type: "confirm",
|
|
415
|
-
name: "overwrite",
|
|
416
|
-
message: "Overwrite existing files?",
|
|
417
|
-
initial: false
|
|
418
|
-
});
|
|
419
|
-
if (!response.overwrite) {
|
|
420
|
-
logger.info("Operation cancelled.");
|
|
421
|
-
process.exit(0);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
714
|
+
await promptOverwrite({ existingFiles, force });
|
|
715
|
+
}
|
|
716
|
+
async function installTemplates(templates, targetDir) {
|
|
424
717
|
const isSingleTemplate = templates.length === 1;
|
|
425
718
|
let totalFiles = 0;
|
|
426
719
|
let totalDirectories = 0;
|
|
@@ -454,154 +747,27 @@ var init = async (options) => {
|
|
|
454
747
|
}
|
|
455
748
|
logger.blank();
|
|
456
749
|
logger.success(`Total: ${totalFiles} files, ${totalDirectories} directories`);
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
installSkills = skillsResponse.install ?? false;
|
|
473
|
-
}
|
|
474
|
-
if (hasCommands) {
|
|
475
|
-
const commandsResponse = await prompts({
|
|
476
|
-
type: "confirm",
|
|
477
|
-
name: "install",
|
|
478
|
-
message: "Install commands to .claude/commands/?",
|
|
479
|
-
initial: false
|
|
480
|
-
});
|
|
481
|
-
installCommands = commandsResponse.install ?? false;
|
|
482
|
-
}
|
|
483
|
-
if (hasAgents) {
|
|
484
|
-
installAgents = true;
|
|
485
|
-
}
|
|
486
|
-
if (hasInstructions) {
|
|
487
|
-
installInstructions = true;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
if (installSkills || installCommands || installAgents || installInstructions) {
|
|
491
|
-
const existingClaudeFiles = await checkExistingClaudeFiles(targetDir);
|
|
492
|
-
if (existingClaudeFiles.length > 0 && !options.force) {
|
|
493
|
-
logger.warn("The following .claude files/folders already exist:");
|
|
494
|
-
existingClaudeFiles.forEach((f) => logger.step(f));
|
|
495
|
-
logger.blank();
|
|
496
|
-
const response = await prompts({
|
|
497
|
-
type: "confirm",
|
|
498
|
-
name: "overwrite",
|
|
499
|
-
message: "Overwrite existing .claude files?",
|
|
500
|
-
initial: false
|
|
501
|
-
});
|
|
502
|
-
if (!response.overwrite) {
|
|
503
|
-
logger.info("Skipping extras installation.");
|
|
504
|
-
installSkills = false;
|
|
505
|
-
installCommands = false;
|
|
506
|
-
installAgents = false;
|
|
507
|
-
installInstructions = false;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
if (installSkills && hasSkills) {
|
|
511
|
-
logger.blank();
|
|
512
|
-
logger.info("Installing skills...");
|
|
513
|
-
const skillsResult = await copySkills(templates, targetDir);
|
|
514
|
-
if (skillsResult.duplicateSkills && skillsResult.duplicateSkills.length > 0) {
|
|
515
|
-
logger.blank();
|
|
516
|
-
logger.warn("The following skills are included in multiple templates:");
|
|
517
|
-
skillsResult.duplicateSkills.forEach(
|
|
518
|
-
({ skill, templates: skillTemplates }) => {
|
|
519
|
-
logger.step(`${skill} (${skillTemplates.join(", ")})`);
|
|
520
|
-
}
|
|
521
|
-
);
|
|
522
|
-
logger.blank();
|
|
523
|
-
const response = await prompts({
|
|
524
|
-
type: "select",
|
|
525
|
-
name: "selectedTemplate",
|
|
526
|
-
message: "Which template's version should be used?",
|
|
527
|
-
choices: templates.map((t) => ({
|
|
528
|
-
title: t,
|
|
529
|
-
value: t
|
|
530
|
-
}))
|
|
531
|
-
});
|
|
532
|
-
if (response.selectedTemplate) {
|
|
533
|
-
logger.info(
|
|
534
|
-
`Reinstalling skills with ${response.selectedTemplate} template...`
|
|
535
|
-
);
|
|
536
|
-
const reinstallResult = await copySkills(
|
|
537
|
-
[response.selectedTemplate],
|
|
538
|
-
targetDir
|
|
539
|
-
);
|
|
540
|
-
totalFiles += reinstallResult.files;
|
|
541
|
-
totalDirectories += reinstallResult.directories;
|
|
542
|
-
logger.success(
|
|
543
|
-
`Skills: ${reinstallResult.files} files, ${reinstallResult.directories} directories`
|
|
544
|
-
);
|
|
545
|
-
} else {
|
|
546
|
-
logger.warn("No template selected. Using all templates.");
|
|
547
|
-
totalFiles += skillsResult.files;
|
|
548
|
-
totalDirectories += skillsResult.directories;
|
|
549
|
-
logger.success(
|
|
550
|
-
`Skills: ${skillsResult.files} files, ${skillsResult.directories} directories`
|
|
551
|
-
);
|
|
552
|
-
}
|
|
553
|
-
} else {
|
|
554
|
-
totalFiles += skillsResult.files;
|
|
555
|
-
totalDirectories += skillsResult.directories;
|
|
556
|
-
logger.success(
|
|
557
|
-
`Skills: ${skillsResult.files} files, ${skillsResult.directories} directories`
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
} else if (installSkills && !hasSkills) {
|
|
561
|
-
logger.warn("No skills found in selected templates.");
|
|
562
|
-
}
|
|
563
|
-
if (installCommands && hasCommands) {
|
|
564
|
-
logger.blank();
|
|
565
|
-
logger.info("Installing commands...");
|
|
566
|
-
const commandsResult = await copyCommands(templates, targetDir);
|
|
567
|
-
totalFiles += commandsResult.files;
|
|
568
|
-
totalDirectories += commandsResult.directories;
|
|
569
|
-
logger.success(
|
|
570
|
-
`Commands: ${commandsResult.files} files, ${commandsResult.directories} directories`
|
|
571
|
-
);
|
|
572
|
-
} else if (installCommands && !hasCommands) {
|
|
573
|
-
logger.warn("No commands found in selected templates.");
|
|
574
|
-
}
|
|
575
|
-
if (installAgents && hasAgents) {
|
|
576
|
-
logger.blank();
|
|
577
|
-
logger.info("Installing agents...");
|
|
578
|
-
const agentsResult = await copyAgents(templates, targetDir);
|
|
579
|
-
totalFiles += agentsResult.files;
|
|
580
|
-
totalDirectories += agentsResult.directories;
|
|
581
|
-
logger.success(
|
|
582
|
-
`Agents: ${agentsResult.files} files, ${agentsResult.directories} directories`
|
|
583
|
-
);
|
|
584
|
-
} else if (installAgents && !hasAgents) {
|
|
585
|
-
logger.warn("No agents found in selected templates.");
|
|
586
|
-
}
|
|
587
|
-
if (installInstructions && hasInstructions) {
|
|
588
|
-
logger.blank();
|
|
589
|
-
logger.info("Installing instructions...");
|
|
590
|
-
const instructionsResult = await copyInstructions(templates, targetDir);
|
|
591
|
-
totalFiles += instructionsResult.files;
|
|
592
|
-
totalDirectories += instructionsResult.directories;
|
|
593
|
-
logger.success(
|
|
594
|
-
`Instructions: ${instructionsResult.files} files, ${instructionsResult.directories} directories`
|
|
595
|
-
);
|
|
596
|
-
} else if (installInstructions && !hasInstructions) {
|
|
597
|
-
logger.warn("No instructions found in selected templates.");
|
|
598
|
-
}
|
|
599
|
-
}
|
|
750
|
+
return { files: totalFiles, directories: totalDirectories };
|
|
751
|
+
}
|
|
752
|
+
async function promptForExtrasInstallation(options, hasSkills, hasCommands, hasAgents, hasInstructions) {
|
|
753
|
+
return await promptExtrasSelection({
|
|
754
|
+
skills: options.skills,
|
|
755
|
+
commands: options.commands,
|
|
756
|
+
agents: options.agents,
|
|
757
|
+
instructions: options.instructions,
|
|
758
|
+
hasSkills,
|
|
759
|
+
hasCommands,
|
|
760
|
+
hasAgents,
|
|
761
|
+
hasInstructions
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAgents, hasInstructions) {
|
|
600
765
|
logger.blank();
|
|
601
766
|
logger.success("Claude Code documentation installed!");
|
|
602
767
|
logger.blank();
|
|
603
768
|
logger.info("Installed templates:");
|
|
604
769
|
templates.forEach((t) => logger.step(t));
|
|
770
|
+
const { installSkills, installCommands, installAgents, installInstructions } = flags;
|
|
605
771
|
if (installSkills && hasSkills || installCommands && hasCommands || installAgents && hasAgents || installInstructions && hasInstructions) {
|
|
606
772
|
logger.blank();
|
|
607
773
|
logger.info("Installed extras:");
|
|
@@ -623,11 +789,53 @@ var init = async (options) => {
|
|
|
623
789
|
logger.step("Read CLAUDE.md for project guidelines");
|
|
624
790
|
logger.step("Explore docs/ for detailed documentation");
|
|
625
791
|
logger.blank();
|
|
792
|
+
}
|
|
793
|
+
var init = async (options) => {
|
|
794
|
+
const targetDir = options.cwd || process.cwd();
|
|
795
|
+
await validateTargetDirectory(targetDir);
|
|
796
|
+
const availableTemplates = await listAvailableTemplates();
|
|
797
|
+
if (availableTemplates.length === 0) {
|
|
798
|
+
logger.error("No templates found. Package may be corrupted.");
|
|
799
|
+
process.exit(1);
|
|
800
|
+
}
|
|
801
|
+
const templates = await selectTemplates(options, availableTemplates);
|
|
802
|
+
await confirmOverwriteIfNeeded(targetDir, options.force ?? false);
|
|
803
|
+
await installTemplates(templates, targetDir);
|
|
804
|
+
const { hasSkills, hasCommands, hasAgents, hasInstructions } = await checkAllExtrasExist(templates);
|
|
805
|
+
const flags = await promptForExtrasInstallation(
|
|
806
|
+
options,
|
|
807
|
+
hasSkills,
|
|
808
|
+
hasCommands,
|
|
809
|
+
hasAgents,
|
|
810
|
+
hasInstructions
|
|
811
|
+
);
|
|
812
|
+
await installExtras(
|
|
813
|
+
templates,
|
|
814
|
+
targetDir,
|
|
815
|
+
flags,
|
|
816
|
+
{ hasSkills, hasCommands, hasAgents, hasInstructions },
|
|
817
|
+
options.force ?? false
|
|
818
|
+
);
|
|
819
|
+
showInstallationSummary(
|
|
820
|
+
templates,
|
|
821
|
+
flags,
|
|
822
|
+
hasSkills,
|
|
823
|
+
hasCommands,
|
|
824
|
+
hasAgents,
|
|
825
|
+
hasInstructions
|
|
826
|
+
);
|
|
827
|
+
try {
|
|
828
|
+
await updateGitignore(targetDir);
|
|
829
|
+
} catch (error) {
|
|
830
|
+
logger.warn(
|
|
831
|
+
`Failed to update .gitignore: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
832
|
+
);
|
|
833
|
+
}
|
|
626
834
|
};
|
|
627
835
|
|
|
628
836
|
// src/index.ts
|
|
629
837
|
var program = new Command();
|
|
630
|
-
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.5.
|
|
838
|
+
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.5.4");
|
|
631
839
|
program.option(
|
|
632
840
|
"-t, --template <names>",
|
|
633
841
|
"template names (comma-separated: tanstack-start,hono)"
|