@kood/claude-code 0.5.1 → 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 +604 -261
- package/package.json +1 -1
- package/templates/.claude/agents/analyst.md +6 -14
- package/templates/.claude/agents/architect.md +6 -14
- package/templates/.claude/agents/code-reviewer.md +8 -14
- package/templates/.claude/agents/dependency-manager.md +8 -14
- package/templates/.claude/agents/deployment-validator.md +8 -14
- package/templates/.claude/agents/designer.md +8 -0
- package/templates/.claude/agents/document-writer.md +75 -316
- package/templates/.claude/agents/explore.md +8 -3
- package/templates/.claude/agents/git-operator.md +63 -17
- package/templates/.claude/agents/implementation-executor.md +14 -37
- package/templates/.claude/agents/ko-to-en-translator.md +8 -13
- package/templates/.claude/agents/lint-fixer.md +8 -172
- package/templates/.claude/agents/planner.md +6 -14
- package/templates/.claude/agents/refactor-advisor.md +8 -14
- package/templates/.claude/commands/git-all.md +3 -167
- package/templates/.claude/commands/git-session.md +3 -71
- package/templates/.claude/commands/lint-fix.md +119 -82
- package/templates/.claude/commands/lint-init.md +2 -54
- package/templates/.claude/commands/pre-deploy.md +143 -82
- package/templates/.claude/commands/version-update.md +171 -66
- package/templates/.claude/instructions/agent-patterns/agent-coordination.md +208 -0
- package/templates/.claude/instructions/agent-patterns/index.md +264 -0
- package/templates/.claude/instructions/agent-patterns/model-routing.md +167 -0
- package/templates/.claude/instructions/agent-patterns/parallel-execution.md +91 -0
- package/templates/.claude/instructions/agent-patterns/read-parallelization.md +106 -0
- package/templates/.claude/instructions/document-templates/ralph-templates.md +71 -0
- package/templates/.claude/instructions/index.md +350 -0
- package/templates/.claude/instructions/multi-agent/agent-roster.md +811 -0
- package/templates/.claude/{PARALLEL_AGENTS.md → instructions/multi-agent/coordination-guide.md} +27 -336
- package/templates/.claude/instructions/{parallel-agent-execution.md → multi-agent/execution-patterns.md} +127 -438
- package/templates/.claude/instructions/multi-agent/index.md +282 -0
- package/templates/.claude/instructions/multi-agent/performance-optimization.md +456 -0
- package/templates/.claude/instructions/tech-stack/design-standards.md +282 -0
- package/templates/.claude/instructions/tech-stack/index.md +70 -0
- package/templates/.claude/instructions/tech-stack/prisma-patterns.md +352 -0
- package/templates/.claude/instructions/tech-stack/tanstack-patterns.md +275 -0
- package/templates/.claude/instructions/validation/forbidden-patterns.md +281 -0
- package/templates/.claude/instructions/validation/index.md +32 -0
- package/templates/.claude/instructions/validation/required-behaviors.md +331 -0
- package/templates/.claude/instructions/validation/verification-checklist.md +318 -0
- package/templates/.claude/instructions/workflow-patterns/index.md +18 -0
- package/templates/.claude/instructions/workflow-patterns/phase-based-workflow.md +250 -0
- package/templates/.claude/instructions/workflow-patterns/sequential-thinking.md +217 -0
- package/templates/.claude/instructions/workflow-patterns/todowrite-pattern.md +215 -0
- package/templates/.claude/skills/bug-fix/SKILL.md +972 -0
- package/templates/.claude/skills/docs-creator/AGENTS.md +54 -173
- package/templates/.claude/skills/docs-creator/SKILL.md +146 -254
- package/templates/.claude/skills/docs-refactor/AGENTS.md +61 -187
- package/templates/.claude/skills/docs-refactor/SKILL.md +84 -315
- package/templates/.claude/skills/execute/SKILL.md +454 -154
- package/templates/.claude/skills/figma-to-code/AGENTS.md +4 -1
- package/templates/.claude/skills/figma-to-code/SKILL.md +306 -0
- package/templates/.claude/skills/global-uiux-design/AGENTS.md +4 -1
- package/templates/.claude/skills/global-uiux-design/SKILL.md +455 -125
- package/templates/.claude/skills/korea-uiux-design/AGENTS.md +4 -1
- package/templates/.claude/skills/korea-uiux-design/SKILL.md +461 -116
- package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +177 -0
- package/templates/.claude/skills/plan/SKILL.md +1157 -87
- package/templates/.claude/skills/prd/SKILL.md +367 -66
- package/templates/.claude/skills/ralph/AGENTS.md +4 -1
- package/templates/.claude/skills/ralph/SKILL.md +100 -14
- package/templates/.claude/skills/refactor/AGENTS.md +269 -0
- package/templates/.claude/skills/refactor/SKILL.md +1572 -0
- 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/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +149 -0
- package/templates/.claude/commands/bug-fix.md +0 -510
- package/templates/.claude/commands/refactor.md +0 -788
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,25 +22,70 @@ var banner = () => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
25
|
-
import
|
|
25
|
+
import fs7 from "fs-extra";
|
|
26
26
|
|
|
27
|
-
// src/
|
|
28
|
-
import
|
|
29
|
-
import path from "path";
|
|
27
|
+
// src/features/templates/template-path-resolver.ts
|
|
28
|
+
import path2 from "path";
|
|
30
29
|
import { fileURLToPath } from "url";
|
|
30
|
+
|
|
31
|
+
// src/shared/validators/path-validators.ts
|
|
32
|
+
import path from "path";
|
|
33
|
+
var validateTemplateName = (template) => {
|
|
34
|
+
const sanitized = path.basename(template);
|
|
35
|
+
if (sanitized !== template || template.includes("..")) {
|
|
36
|
+
throw new Error(`Invalid template name: "${template}"`);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var validateTargetDir = (targetDir) => {
|
|
40
|
+
const resolved = path.resolve(targetDir);
|
|
41
|
+
const protectedPaths = [
|
|
42
|
+
"/",
|
|
43
|
+
"/usr",
|
|
44
|
+
"/etc",
|
|
45
|
+
"/bin",
|
|
46
|
+
"/sbin",
|
|
47
|
+
"/home",
|
|
48
|
+
"/var",
|
|
49
|
+
"/root"
|
|
50
|
+
];
|
|
51
|
+
if (process.platform === "win32") {
|
|
52
|
+
const winProtected = [
|
|
53
|
+
"C:\\Windows",
|
|
54
|
+
"C:\\Program Files",
|
|
55
|
+
"C:\\Program Files (x86)"
|
|
56
|
+
];
|
|
57
|
+
protectedPaths.push(...winProtected);
|
|
58
|
+
}
|
|
59
|
+
for (const protectedPath of protectedPaths) {
|
|
60
|
+
if (resolved === protectedPath || resolved.startsWith(protectedPath + path.sep)) {
|
|
61
|
+
throw new Error(`Cannot modify protected system path: ${resolved}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/features/templates/template-path-resolver.ts
|
|
31
67
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
32
|
-
var __dirname2 =
|
|
68
|
+
var __dirname2 = path2.dirname(__filename2);
|
|
33
69
|
var getTemplatesDir = () => {
|
|
34
|
-
return
|
|
70
|
+
return path2.resolve(__dirname2, "../templates");
|
|
35
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";
|
|
36
84
|
var hasFiles = async (dir) => {
|
|
37
85
|
if (!await fs.pathExists(dir)) return false;
|
|
38
86
|
const items = await fs.readdir(dir);
|
|
39
87
|
return items.length > 0;
|
|
40
88
|
};
|
|
41
|
-
var getTemplatePath = (template) => {
|
|
42
|
-
return path.join(getTemplatesDir(), template);
|
|
43
|
-
};
|
|
44
89
|
var copyRecursive = async (src, dest, counter) => {
|
|
45
90
|
const stat = await fs.stat(src);
|
|
46
91
|
if (stat.isDirectory()) {
|
|
@@ -48,102 +93,149 @@ var copyRecursive = async (src, dest, counter) => {
|
|
|
48
93
|
counter.directories++;
|
|
49
94
|
const items = await fs.readdir(src);
|
|
50
95
|
for (const item of items) {
|
|
51
|
-
await copyRecursive(
|
|
96
|
+
await copyRecursive(path3.join(src, item), path3.join(dest, item), counter);
|
|
52
97
|
}
|
|
53
98
|
} else {
|
|
54
99
|
await fs.copy(src, dest);
|
|
55
100
|
counter.files++;
|
|
56
101
|
}
|
|
57
102
|
};
|
|
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"
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var generateIndexClaudeMd = (templates) => {
|
|
123
|
+
const templateSections = templates.map((template) => {
|
|
124
|
+
const meta = templateMetadata[template] || {
|
|
125
|
+
name: template,
|
|
126
|
+
description: `${template} \uD504\uB85C\uC81D\uD2B8`,
|
|
127
|
+
stack: "TypeScript"
|
|
128
|
+
};
|
|
129
|
+
return `### ${meta.name}
|
|
130
|
+
- **\uC6A9\uB3C4:** ${meta.description}
|
|
131
|
+
- **\uC8FC\uC694 \uC2A4\uD0DD:** ${meta.stack}
|
|
132
|
+
- **\uAC00\uC774\uB4DC:** [docs/${template}/CLAUDE.md](docs/${template}/CLAUDE.md)`;
|
|
133
|
+
}).join("\n\n");
|
|
134
|
+
return `# CLAUDE.md
|
|
135
|
+
|
|
136
|
+
> \uC774 \uD504\uB85C\uC81D\uD2B8\uB294 \uC5EC\uB7EC \uD504\uB808\uC784\uC6CC\uD06C\uC758 Claude Code \uAC00\uC774\uB4DC\uB97C \uD3EC\uD568\uD569\uB2C8\uB2E4.
|
|
137
|
+
|
|
138
|
+
## \uC0AC\uC6A9 \uBC29\uBC95
|
|
139
|
+
|
|
140
|
+
\uC544\uB798 \uD15C\uD50C\uB9BF \uC911 **\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC0AC\uC6A9 \uC911\uC778 \uD504\uB808\uC784\uC6CC\uD06C**\uB97C \uC120\uD0DD\uD558\uC5EC \uD574\uB2F9 \uAC00\uC774\uB4DC\uB97C \uB530\uB974\uC138\uC694.
|
|
141
|
+
|
|
142
|
+
## \uD15C\uD50C\uB9BF \uBAA9\uB85D
|
|
143
|
+
|
|
144
|
+
${templateSections}
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
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.**
|
|
149
|
+
`;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// src/features/templates/template-installer.ts
|
|
58
153
|
var copySingleTemplate = async (template, targetDir) => {
|
|
154
|
+
validateTargetDir(targetDir);
|
|
59
155
|
const templatePath = getTemplatePath(template);
|
|
60
|
-
if (!await
|
|
156
|
+
if (!await fs2.pathExists(templatePath)) {
|
|
61
157
|
throw new Error(`Template "${template}" not found at ${templatePath}`);
|
|
62
158
|
}
|
|
63
159
|
const counter = { files: 0, directories: 0 };
|
|
64
|
-
const claudeMdSrc =
|
|
65
|
-
if (await
|
|
66
|
-
await
|
|
160
|
+
const claudeMdSrc = path4.join(templatePath, "CLAUDE.md");
|
|
161
|
+
if (await fs2.pathExists(claudeMdSrc)) {
|
|
162
|
+
await fs2.copy(claudeMdSrc, path4.join(targetDir, "CLAUDE.md"));
|
|
67
163
|
counter.files++;
|
|
68
164
|
}
|
|
69
|
-
const docsSrc =
|
|
70
|
-
const docsDest =
|
|
71
|
-
if (await
|
|
72
|
-
if (await
|
|
73
|
-
await
|
|
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);
|
|
74
170
|
}
|
|
75
171
|
await copyRecursive(docsSrc, docsDest, counter);
|
|
76
172
|
}
|
|
77
173
|
return counter;
|
|
78
174
|
};
|
|
79
175
|
var copyMultipleTemplates = async (templates, targetDir) => {
|
|
176
|
+
validateTargetDir(targetDir);
|
|
80
177
|
const counter = { files: 0, directories: 0 };
|
|
81
|
-
const docsDir =
|
|
82
|
-
if (await
|
|
83
|
-
await
|
|
178
|
+
const docsDir = path4.join(targetDir, "docs");
|
|
179
|
+
if (await fs2.pathExists(docsDir)) {
|
|
180
|
+
await fs2.remove(docsDir);
|
|
84
181
|
}
|
|
85
182
|
for (const template of templates) {
|
|
86
183
|
const templatePath = getTemplatePath(template);
|
|
87
|
-
if (!await
|
|
184
|
+
if (!await fs2.pathExists(templatePath)) {
|
|
88
185
|
throw new Error(`Template "${template}" not found at ${templatePath}`);
|
|
89
186
|
}
|
|
90
187
|
await copyRecursive(
|
|
91
188
|
templatePath,
|
|
92
|
-
|
|
189
|
+
path4.join(targetDir, "docs", template),
|
|
93
190
|
counter
|
|
94
191
|
);
|
|
95
192
|
}
|
|
96
193
|
const indexContent = generateIndexClaudeMd(templates);
|
|
97
|
-
await
|
|
194
|
+
await fs2.writeFile(path4.join(targetDir, "CLAUDE.md"), indexContent);
|
|
98
195
|
counter.files++;
|
|
99
196
|
return counter;
|
|
100
197
|
};
|
|
101
|
-
var generateIndexClaudeMd = (templates) => {
|
|
102
|
-
const templateLinks = templates.map((t) => `- [${t}](docs/${t}/CLAUDE.md)`).join("\n");
|
|
103
|
-
return `# CLAUDE.md
|
|
104
|
-
|
|
105
|
-
> \uC774 \uD504\uB85C\uC81D\uD2B8\uB294 \uC5EC\uB7EC \uD15C\uD50C\uB9BF\uC758 Claude Code \uBB38\uC11C\uB97C \uD3EC\uD568\uD569\uB2C8\uB2E4.
|
|
106
|
-
|
|
107
|
-
## \uD15C\uD50C\uB9BF \uBB38\uC11C
|
|
108
198
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
\uAC01 \uD15C\uD50C\uB9BF\uC758 \`CLAUDE.md\`\uB97C \uCC38\uC870\uD558\uC5EC \uD574\uB2F9 \uAE30\uC220 \uC2A4\uD0DD\uC758 \uAC00\uC774\uB4DC\uB77C\uC778\uC744 \uD655\uC778\uD558\uC138\uC694.
|
|
114
|
-
`;
|
|
115
|
-
};
|
|
116
|
-
var checkExistingFiles = async (targetDir) => {
|
|
117
|
-
const existingFiles = [];
|
|
118
|
-
const claudeMd = path.join(targetDir, "CLAUDE.md");
|
|
119
|
-
const docsDir = path.join(targetDir, "docs");
|
|
120
|
-
if (await fs.pathExists(claudeMd)) {
|
|
121
|
-
existingFiles.push("CLAUDE.md");
|
|
122
|
-
}
|
|
123
|
-
if (await fs.pathExists(docsDir)) {
|
|
124
|
-
existingFiles.push("docs/");
|
|
125
|
-
}
|
|
126
|
-
return existingFiles;
|
|
127
|
-
};
|
|
199
|
+
// src/features/templates/template-discovery.ts
|
|
200
|
+
import fs3 from "fs-extra";
|
|
201
|
+
import path5 from "path";
|
|
128
202
|
var listAvailableTemplates = async () => {
|
|
129
203
|
const templatesDir = getTemplatesDir();
|
|
130
|
-
if (!await
|
|
204
|
+
if (!await fs3.pathExists(templatesDir)) {
|
|
131
205
|
return [];
|
|
132
206
|
}
|
|
133
|
-
const items = await
|
|
207
|
+
const items = await fs3.readdir(templatesDir);
|
|
134
208
|
const templates = [];
|
|
135
209
|
for (const item of items) {
|
|
136
210
|
if (item.startsWith(".")) {
|
|
137
211
|
continue;
|
|
138
212
|
}
|
|
139
|
-
const itemPath =
|
|
140
|
-
const stat = await
|
|
213
|
+
const itemPath = path5.join(templatesDir, item);
|
|
214
|
+
const stat = await fs3.stat(itemPath);
|
|
141
215
|
if (stat.isDirectory()) {
|
|
142
216
|
templates.push(item);
|
|
143
217
|
}
|
|
144
218
|
}
|
|
145
219
|
return templates;
|
|
146
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
|
|
147
239
|
var FRAMEWORK_SPECIFIC_SKILLS_MAP = {
|
|
148
240
|
nextjs: ["nextjs-react-best-practices", "korea-uiux-design", "figma-to-code"],
|
|
149
241
|
"tanstack-start": [
|
|
@@ -162,91 +254,97 @@ var COMMON_SKILLS = [
|
|
|
162
254
|
"ralph",
|
|
163
255
|
"execute"
|
|
164
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");
|
|
165
274
|
var copySkills = async (templates, targetDir) => {
|
|
166
275
|
const counter = { files: 0, directories: 0 };
|
|
167
|
-
const targetSkillsDir =
|
|
168
|
-
const skillsSrc =
|
|
169
|
-
if (!await
|
|
276
|
+
const targetSkillsDir = path6.join(targetDir, ".claude", "skills");
|
|
277
|
+
const skillsSrc = path6.join(getTemplatesDir(), ".claude", "skills");
|
|
278
|
+
if (!await fs4.pathExists(skillsSrc)) {
|
|
170
279
|
return counter;
|
|
171
280
|
}
|
|
172
|
-
await
|
|
281
|
+
await fs4.ensureDir(targetSkillsDir);
|
|
173
282
|
const skillsToCopy = /* @__PURE__ */ new Set();
|
|
283
|
+
const skillTemplateMap = /* @__PURE__ */ new Map();
|
|
174
284
|
COMMON_SKILLS.forEach((skill) => skillsToCopy.add(skill));
|
|
175
285
|
for (const template of templates) {
|
|
176
286
|
const skills = FRAMEWORK_SPECIFIC_SKILLS_MAP[template] || [];
|
|
177
|
-
skills.forEach((skill) =>
|
|
287
|
+
skills.forEach((skill) => {
|
|
288
|
+
skillsToCopy.add(skill);
|
|
289
|
+
if (!skillTemplateMap.has(skill)) {
|
|
290
|
+
skillTemplateMap.set(skill, /* @__PURE__ */ new Set());
|
|
291
|
+
}
|
|
292
|
+
skillTemplateMap.get(skill).add(template);
|
|
293
|
+
});
|
|
178
294
|
}
|
|
179
295
|
for (const skill of skillsToCopy) {
|
|
180
|
-
const skillSrc =
|
|
181
|
-
const skillDest =
|
|
182
|
-
if (await
|
|
183
|
-
if (await
|
|
184
|
-
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);
|
|
185
301
|
}
|
|
186
302
|
await copyRecursive(skillSrc, skillDest, counter);
|
|
187
303
|
}
|
|
188
304
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
await copyRecursive(commandsSrc, targetCommandsDir, counter);
|
|
305
|
+
const duplicateSkills = [];
|
|
306
|
+
for (const [skill, templateSet] of skillTemplateMap.entries()) {
|
|
307
|
+
if (templateSet.size > 1) {
|
|
308
|
+
duplicateSkills.push({
|
|
309
|
+
skill,
|
|
310
|
+
templates: Array.from(templateSet).sort()
|
|
311
|
+
});
|
|
312
|
+
}
|
|
198
313
|
}
|
|
199
|
-
return
|
|
314
|
+
return {
|
|
315
|
+
...counter,
|
|
316
|
+
...duplicateSkills.length > 0 && { duplicateSkills }
|
|
317
|
+
};
|
|
200
318
|
};
|
|
319
|
+
|
|
320
|
+
// src/features/extras/extras-checker.ts
|
|
321
|
+
import fs5 from "fs-extra";
|
|
322
|
+
import path7 from "path";
|
|
201
323
|
var checkExistingClaudeFiles = async (targetDir) => {
|
|
202
324
|
const existingFiles = [];
|
|
203
|
-
const skillsDir =
|
|
204
|
-
const commandsDir =
|
|
205
|
-
const agentsDir =
|
|
206
|
-
const instructionsDir =
|
|
207
|
-
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)) {
|
|
208
330
|
existingFiles.push(".claude/skills/");
|
|
209
331
|
}
|
|
210
|
-
if (await
|
|
332
|
+
if (await fs5.pathExists(commandsDir)) {
|
|
211
333
|
existingFiles.push(".claude/commands/");
|
|
212
334
|
}
|
|
213
|
-
if (await
|
|
335
|
+
if (await fs5.pathExists(agentsDir)) {
|
|
214
336
|
existingFiles.push(".claude/agents/");
|
|
215
337
|
}
|
|
216
|
-
if (await
|
|
338
|
+
if (await fs5.pathExists(instructionsDir)) {
|
|
217
339
|
existingFiles.push(".claude/instructions/");
|
|
218
340
|
}
|
|
219
341
|
return existingFiles;
|
|
220
342
|
};
|
|
221
|
-
var copyAgents = async (_templates, targetDir) => {
|
|
222
|
-
const counter = { files: 0, directories: 0 };
|
|
223
|
-
const targetAgentsDir = path.join(targetDir, ".claude", "agents");
|
|
224
|
-
const agentsSrc = path.join(getTemplatesDir(), ".claude", "agents");
|
|
225
|
-
if (await fs.pathExists(agentsSrc)) {
|
|
226
|
-
await fs.ensureDir(targetAgentsDir);
|
|
227
|
-
await copyRecursive(agentsSrc, targetAgentsDir, counter);
|
|
228
|
-
}
|
|
229
|
-
return counter;
|
|
230
|
-
};
|
|
231
|
-
var copyInstructions = async (_templates, targetDir) => {
|
|
232
|
-
const counter = { files: 0, directories: 0 };
|
|
233
|
-
const targetInstructionsDir = path.join(targetDir, ".claude", "instructions");
|
|
234
|
-
const instructionsSrc = path.join(
|
|
235
|
-
getTemplatesDir(),
|
|
236
|
-
".claude",
|
|
237
|
-
"instructions"
|
|
238
|
-
);
|
|
239
|
-
if (await fs.pathExists(instructionsSrc)) {
|
|
240
|
-
await fs.ensureDir(targetInstructionsDir);
|
|
241
|
-
await copyRecursive(instructionsSrc, targetInstructionsDir, counter);
|
|
242
|
-
}
|
|
243
|
-
return counter;
|
|
244
|
-
};
|
|
245
343
|
var checkAllExtrasExist = async (templates) => {
|
|
246
|
-
const claudeDir =
|
|
247
|
-
const commandsSrc =
|
|
248
|
-
const agentsSrc =
|
|
249
|
-
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");
|
|
250
348
|
const hasFrameworkSkills = templates.some((template) => {
|
|
251
349
|
const skills = FRAMEWORK_SPECIFIC_SKILLS_MAP[template];
|
|
252
350
|
return skills && skills.length > 0;
|
|
@@ -258,38 +356,60 @@ var checkAllExtrasExist = async (templates) => {
|
|
|
258
356
|
return { hasSkills, hasCommands, hasAgents, hasInstructions };
|
|
259
357
|
};
|
|
260
358
|
|
|
261
|
-
// src/
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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 || [];
|
|
275
395
|
if (templates.length === 0) {
|
|
276
|
-
const response = await
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
message: "Select templates (space to select, enter to confirm):",
|
|
280
|
-
choices: availableTemplates.map((t) => ({
|
|
396
|
+
const response = await promptMultiselect(
|
|
397
|
+
"Select templates (space to select, enter to confirm):",
|
|
398
|
+
availableTemplates.map((t) => ({
|
|
281
399
|
title: t,
|
|
282
|
-
description:
|
|
400
|
+
description: templateDescriptions[t] || "",
|
|
283
401
|
value: t
|
|
284
402
|
})),
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
403
|
+
{
|
|
404
|
+
min: 1,
|
|
405
|
+
hint: "- Space to select. Return to submit"
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
if (response.values.length === 0) {
|
|
289
409
|
logger.warn("Operation cancelled.");
|
|
290
410
|
process.exit(0);
|
|
291
411
|
}
|
|
292
|
-
templates = response.
|
|
412
|
+
templates = response.values;
|
|
293
413
|
}
|
|
294
414
|
const invalidTemplates = templates.filter(
|
|
295
415
|
(t) => !availableTemplates.includes(t)
|
|
@@ -299,22 +419,301 @@ var init = async (options) => {
|
|
|
299
419
|
logger.info(`Available templates: ${availableTemplates.join(", ")}`);
|
|
300
420
|
process.exit(1);
|
|
301
421
|
}
|
|
302
|
-
|
|
303
|
-
|
|
422
|
+
return { templates };
|
|
423
|
+
}
|
|
424
|
+
async function promptOverwrite(options) {
|
|
425
|
+
const { existingFiles, force } = options;
|
|
426
|
+
if (existingFiles.length > 0 && !force) {
|
|
304
427
|
logger.warn("The following files/folders already exist:");
|
|
305
428
|
existingFiles.forEach((f) => logger.step(f));
|
|
306
429
|
logger.blank();
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
name: "overwrite",
|
|
310
|
-
message: "Overwrite existing files?",
|
|
311
|
-
initial: false
|
|
312
|
-
});
|
|
313
|
-
if (!response.overwrite) {
|
|
430
|
+
const result = await promptConfirm("Overwrite existing files?", false);
|
|
431
|
+
if (!result.confirmed) {
|
|
314
432
|
logger.info("Operation cancelled.");
|
|
315
433
|
process.exit(0);
|
|
316
434
|
}
|
|
317
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
|
+
|
|
676
|
+
// src/commands/init.ts
|
|
677
|
+
var TEMPLATE_DESCRIPTIONS = {
|
|
678
|
+
"tanstack-start": "TanStack Start + React \uD480\uC2A4\uD0DD \uD504\uB85C\uC81D\uD2B8",
|
|
679
|
+
hono: "Hono \uC11C\uBC84 \uD504\uB808\uC784\uC6CC\uD06C \uD504\uB85C\uC81D\uD2B8",
|
|
680
|
+
npx: "NPX CLI \uB3C4\uAD6C \uD504\uB85C\uC81D\uD2B8"
|
|
681
|
+
};
|
|
682
|
+
async function validateTargetDirectory(targetDir) {
|
|
683
|
+
try {
|
|
684
|
+
const stat = await fs7.stat(targetDir);
|
|
685
|
+
if (!stat.isDirectory()) {
|
|
686
|
+
logger.error(`Target is not a directory: ${targetDir}`);
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
} catch (error) {
|
|
690
|
+
if (error.code === "ENOENT") {
|
|
691
|
+
logger.error(`Target directory does not exist: ${targetDir}`);
|
|
692
|
+
} else {
|
|
693
|
+
logger.error(`Cannot access target directory: ${targetDir}`);
|
|
694
|
+
}
|
|
695
|
+
process.exit(1);
|
|
696
|
+
}
|
|
697
|
+
try {
|
|
698
|
+
await fs7.access(targetDir, fs7.constants.W_OK);
|
|
699
|
+
} catch {
|
|
700
|
+
logger.error(`No write permission for: ${targetDir}`);
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
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) {
|
|
713
|
+
const existingFiles = await checkExistingFiles(targetDir);
|
|
714
|
+
await promptOverwrite({ existingFiles, force });
|
|
715
|
+
}
|
|
716
|
+
async function installTemplates(templates, targetDir) {
|
|
318
717
|
const isSingleTemplate = templates.length === 1;
|
|
319
718
|
let totalFiles = 0;
|
|
320
719
|
let totalDirectories = 0;
|
|
@@ -348,125 +747,27 @@ var init = async (options) => {
|
|
|
348
747
|
}
|
|
349
748
|
logger.blank();
|
|
350
749
|
logger.success(`Total: ${totalFiles} files, ${totalDirectories} directories`);
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
installSkills = skillsResponse.install ?? false;
|
|
367
|
-
}
|
|
368
|
-
if (hasCommands) {
|
|
369
|
-
const commandsResponse = await prompts({
|
|
370
|
-
type: "confirm",
|
|
371
|
-
name: "install",
|
|
372
|
-
message: "Install commands to .claude/commands/?",
|
|
373
|
-
initial: false
|
|
374
|
-
});
|
|
375
|
-
installCommands = commandsResponse.install ?? false;
|
|
376
|
-
}
|
|
377
|
-
if (hasAgents) {
|
|
378
|
-
const agentsResponse = await prompts({
|
|
379
|
-
type: "confirm",
|
|
380
|
-
name: "install",
|
|
381
|
-
message: "Install agents to .claude/agents/?",
|
|
382
|
-
initial: false
|
|
383
|
-
});
|
|
384
|
-
installAgents = agentsResponse.install ?? false;
|
|
385
|
-
}
|
|
386
|
-
if (hasInstructions) {
|
|
387
|
-
const instructionsResponse = await prompts({
|
|
388
|
-
type: "confirm",
|
|
389
|
-
name: "install",
|
|
390
|
-
message: "Install instructions to .claude/instructions/?",
|
|
391
|
-
initial: false
|
|
392
|
-
});
|
|
393
|
-
installInstructions = instructionsResponse.install ?? false;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
if (installSkills || installCommands || installAgents || installInstructions) {
|
|
397
|
-
const existingClaudeFiles = await checkExistingClaudeFiles(targetDir);
|
|
398
|
-
if (existingClaudeFiles.length > 0 && !options.force) {
|
|
399
|
-
logger.warn("The following .claude files/folders already exist:");
|
|
400
|
-
existingClaudeFiles.forEach((f) => logger.step(f));
|
|
401
|
-
logger.blank();
|
|
402
|
-
const response = await prompts({
|
|
403
|
-
type: "confirm",
|
|
404
|
-
name: "overwrite",
|
|
405
|
-
message: "Overwrite existing .claude files?",
|
|
406
|
-
initial: false
|
|
407
|
-
});
|
|
408
|
-
if (!response.overwrite) {
|
|
409
|
-
logger.info("Skipping extras installation.");
|
|
410
|
-
installSkills = false;
|
|
411
|
-
installCommands = false;
|
|
412
|
-
installAgents = false;
|
|
413
|
-
installInstructions = false;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
if (installSkills && hasSkills) {
|
|
417
|
-
logger.blank();
|
|
418
|
-
logger.info("Installing skills...");
|
|
419
|
-
const skillsResult = await copySkills(templates, targetDir);
|
|
420
|
-
totalFiles += skillsResult.files;
|
|
421
|
-
totalDirectories += skillsResult.directories;
|
|
422
|
-
logger.success(
|
|
423
|
-
`Skills: ${skillsResult.files} files, ${skillsResult.directories} directories`
|
|
424
|
-
);
|
|
425
|
-
} else if (installSkills && !hasSkills) {
|
|
426
|
-
logger.warn("No skills found in selected templates.");
|
|
427
|
-
}
|
|
428
|
-
if (installCommands && hasCommands) {
|
|
429
|
-
logger.blank();
|
|
430
|
-
logger.info("Installing commands...");
|
|
431
|
-
const commandsResult = await copyCommands(templates, targetDir);
|
|
432
|
-
totalFiles += commandsResult.files;
|
|
433
|
-
totalDirectories += commandsResult.directories;
|
|
434
|
-
logger.success(
|
|
435
|
-
`Commands: ${commandsResult.files} files, ${commandsResult.directories} directories`
|
|
436
|
-
);
|
|
437
|
-
} else if (installCommands && !hasCommands) {
|
|
438
|
-
logger.warn("No commands found in selected templates.");
|
|
439
|
-
}
|
|
440
|
-
if (installAgents && hasAgents) {
|
|
441
|
-
logger.blank();
|
|
442
|
-
logger.info("Installing agents...");
|
|
443
|
-
const agentsResult = await copyAgents(templates, targetDir);
|
|
444
|
-
totalFiles += agentsResult.files;
|
|
445
|
-
totalDirectories += agentsResult.directories;
|
|
446
|
-
logger.success(
|
|
447
|
-
`Agents: ${agentsResult.files} files, ${agentsResult.directories} directories`
|
|
448
|
-
);
|
|
449
|
-
} else if (installAgents && !hasAgents) {
|
|
450
|
-
logger.warn("No agents found in selected templates.");
|
|
451
|
-
}
|
|
452
|
-
if (installInstructions && hasInstructions) {
|
|
453
|
-
logger.blank();
|
|
454
|
-
logger.info("Installing instructions...");
|
|
455
|
-
const instructionsResult = await copyInstructions(templates, targetDir);
|
|
456
|
-
totalFiles += instructionsResult.files;
|
|
457
|
-
totalDirectories += instructionsResult.directories;
|
|
458
|
-
logger.success(
|
|
459
|
-
`Instructions: ${instructionsResult.files} files, ${instructionsResult.directories} directories`
|
|
460
|
-
);
|
|
461
|
-
} else if (installInstructions && !hasInstructions) {
|
|
462
|
-
logger.warn("No instructions found in selected templates.");
|
|
463
|
-
}
|
|
464
|
-
}
|
|
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) {
|
|
465
765
|
logger.blank();
|
|
466
766
|
logger.success("Claude Code documentation installed!");
|
|
467
767
|
logger.blank();
|
|
468
768
|
logger.info("Installed templates:");
|
|
469
769
|
templates.forEach((t) => logger.step(t));
|
|
770
|
+
const { installSkills, installCommands, installAgents, installInstructions } = flags;
|
|
470
771
|
if (installSkills && hasSkills || installCommands && hasCommands || installAgents && hasAgents || installInstructions && hasInstructions) {
|
|
471
772
|
logger.blank();
|
|
472
773
|
logger.info("Installed extras:");
|
|
@@ -488,11 +789,53 @@ var init = async (options) => {
|
|
|
488
789
|
logger.step("Read CLAUDE.md for project guidelines");
|
|
489
790
|
logger.step("Explore docs/ for detailed documentation");
|
|
490
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
|
+
}
|
|
491
834
|
};
|
|
492
835
|
|
|
493
836
|
// src/index.ts
|
|
494
837
|
var program = new Command();
|
|
495
|
-
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");
|
|
496
839
|
program.option(
|
|
497
840
|
"-t, --template <names>",
|
|
498
841
|
"template names (comma-separated: tanstack-start,hono)"
|