@kood/claude-code 0.5.3 → 0.5.5
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 +552 -340
- package/package.json +1 -1
- package/templates/.claude/agents/document-writer.md +73 -306
- 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 +98 -464
- package/templates/.claude/skills/docs-refactor/AGENTS.md +61 -190
- package/templates/.claude/skills/docs-refactor/SKILL.md +67 -443
- package/templates/.claude/skills/execute/SKILL.md +540 -13
- package/templates/.claude/skills/plan/SKILL.md +84 -18
- 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/templates/hono/CLAUDE.md +28 -28
- package/templates/hono/docs/architecture.md +24 -24
- package/templates/hono/docs/deployment/cloudflare.md +18 -18
- package/templates/hono/docs/deployment/docker.md +13 -13
- package/templates/hono/docs/deployment/index.md +19 -19
- package/templates/hono/docs/deployment/railway.md +32 -32
- package/templates/hono/docs/deployment/vercel.md +29 -29
- package/templates/hono/docs/guides/conventions.md +57 -57
- package/templates/hono/docs/guides/env-setup.md +47 -47
- package/templates/hono/docs/guides/getting-started.md +27 -27
- package/templates/hono/docs/library/hono/error-handling.md +11 -11
- package/templates/hono/docs/library/hono/index.md +4 -4
- package/templates/hono/docs/library/hono/middleware.md +18 -18
- package/templates/hono/docs/library/hono/rpc.md +7 -7
- package/templates/hono/docs/library/hono/validation.md +6 -6
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +29 -29
- package/templates/hono/docs/library/prisma/config.md +16 -16
- package/templates/hono/docs/library/prisma/index.md +32 -32
- package/templates/hono/docs/library/t3-env/index.md +22 -22
- package/templates/hono/docs/library/zod/index.md +31 -31
- package/templates/nextjs/CLAUDE.md +54 -54
- package/templates/nextjs/docs/architecture.md +146 -146
- package/templates/nextjs/docs/design.md +183 -183
- package/templates/nextjs/docs/guides/conventions.md +86 -86
- package/templates/nextjs/docs/guides/getting-started.md +28 -28
- package/templates/nextjs/docs/guides/routes.md +32 -32
- package/templates/nextjs/docs/library/better-auth/index.md +70 -70
- package/templates/nextjs/docs/library/nextjs/app-router.md +43 -43
- package/templates/nextjs/docs/library/nextjs/caching.md +73 -73
- package/templates/nextjs/docs/library/nextjs/index.md +51 -51
- package/templates/nextjs/docs/library/nextjs/middleware.md +41 -41
- package/templates/nextjs/docs/library/nextjs/route-handlers.md +31 -31
- package/templates/nextjs/docs/library/nextjs/server-actions.md +34 -34
- package/templates/nextjs/docs/library/prisma/cloudflare-d1.md +20 -20
- package/templates/nextjs/docs/library/prisma/config.md +18 -18
- package/templates/nextjs/docs/library/prisma/crud.md +17 -17
- package/templates/nextjs/docs/library/prisma/index.md +18 -18
- package/templates/nextjs/docs/library/prisma/relations.md +16 -16
- package/templates/nextjs/docs/library/prisma/schema.md +23 -23
- package/templates/nextjs/docs/library/prisma/setup.md +6 -6
- package/templates/nextjs/docs/library/prisma/transactions.md +10 -10
- package/templates/nextjs/docs/library/tanstack-query/index.md +6 -6
- package/templates/nextjs/docs/library/tanstack-query/invalidation.md +20 -20
- package/templates/nextjs/docs/library/tanstack-query/optimistic-updates.md +4 -4
- package/templates/nextjs/docs/library/tanstack-query/use-mutation.md +15 -15
- package/templates/nextjs/docs/library/tanstack-query/use-query.md +22 -22
- package/templates/nextjs/docs/library/zod/complex-types.md +11 -11
- package/templates/nextjs/docs/library/zod/index.md +8 -8
- package/templates/nextjs/docs/library/zod/transforms.md +11 -11
- package/templates/nextjs/docs/library/zod/validation.md +9 -9
- package/templates/npx/CLAUDE.md +38 -38
- package/templates/npx/docs/library/commander/index.md +12 -12
- package/templates/npx/docs/library/fs-extra/index.md +9 -9
- package/templates/npx/docs/library/prompts/index.md +3 -3
- package/templates/npx/docs/references/patterns.md +12 -12
- package/templates/tanstack-start/CLAUDE.md +54 -54
- package/templates/tanstack-start/docs/architecture.md +128 -128
- package/templates/tanstack-start/docs/design.md +169 -169
- package/templates/tanstack-start/docs/guides/conventions.md +43 -43
- package/templates/tanstack-start/docs/guides/env-setup.md +35 -35
- package/templates/tanstack-start/docs/guides/getting-started.md +19 -19
- package/templates/tanstack-start/docs/guides/hooks.md +45 -45
- package/templates/tanstack-start/docs/guides/routes.md +54 -54
- package/templates/tanstack-start/docs/guides/services.md +45 -45
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +19 -19
- package/templates/tanstack-start/docs/library/prisma/config.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/crud.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/relations.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/schema.md +23 -23
- package/templates/tanstack-start/docs/library/prisma/setup.md +6 -6
- package/templates/tanstack-start/docs/library/prisma/transactions.md +10 -10
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +19 -19
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +4 -4
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +14 -14
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +21 -21
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +11 -11
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +17 -17
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +5 -5
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +10 -10
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +8 -8
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +4 -4
- package/templates/tanstack-start/docs/library/zod/complex-types.md +11 -11
- package/templates/tanstack-start/docs/library/zod/transforms.md +11 -11
- package/templates/tanstack-start/docs/library/zod/validation.md +9 -9
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;
|
|
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;
|
|
194
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,336 @@ 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 = [
|
|
633
|
+
".claude/plan/",
|
|
634
|
+
".claude/ralph/",
|
|
635
|
+
".claude/refactor/"
|
|
636
|
+
];
|
|
637
|
+
async function updateGitignore(targetDir) {
|
|
638
|
+
const gitignorePath = path8.join(targetDir, ".gitignore");
|
|
639
|
+
const sectionComment = "# Claude Code generated files";
|
|
640
|
+
let content = "";
|
|
641
|
+
let hasGitignore = false;
|
|
642
|
+
try {
|
|
643
|
+
content = await fs6.readFile(gitignorePath, "utf-8");
|
|
644
|
+
hasGitignore = true;
|
|
645
|
+
} catch (error) {
|
|
646
|
+
content = "";
|
|
647
|
+
}
|
|
648
|
+
if (content.includes(sectionComment)) {
|
|
649
|
+
logger.info(".gitignore already contains Claude Code section");
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
const linesToAdd = [];
|
|
653
|
+
const existingLines = content.split("\n").map((line) => line.trim());
|
|
654
|
+
for (const folder of CLAUDE_GENERATED_FOLDERS) {
|
|
655
|
+
if (!existingLines.includes(folder)) {
|
|
656
|
+
linesToAdd.push(folder);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (linesToAdd.length === 0) {
|
|
660
|
+
logger.info(".gitignore already contains all Claude Code patterns");
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
let newContent = content;
|
|
664
|
+
if (newContent && !newContent.endsWith("\n")) {
|
|
665
|
+
newContent += "\n";
|
|
666
|
+
}
|
|
667
|
+
if (newContent) {
|
|
668
|
+
newContent += "\n";
|
|
669
|
+
}
|
|
670
|
+
newContent += sectionComment + "\n";
|
|
671
|
+
newContent += linesToAdd.join("\n") + "\n";
|
|
672
|
+
await fs6.writeFile(gitignorePath, newContent, "utf-8");
|
|
673
|
+
if (hasGitignore) {
|
|
674
|
+
logger.success(`.gitignore updated with ${linesToAdd.length} patterns`);
|
|
675
|
+
} else {
|
|
676
|
+
logger.success(`.gitignore created with ${linesToAdd.length} patterns`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
347
680
|
// src/commands/init.ts
|
|
348
681
|
var TEMPLATE_DESCRIPTIONS = {
|
|
349
682
|
"tanstack-start": "TanStack Start + React \uD480\uC2A4\uD0DD \uD504\uB85C\uC81D\uD2B8",
|
|
350
683
|
hono: "Hono \uC11C\uBC84 \uD504\uB808\uC784\uC6CC\uD06C \uD504\uB85C\uC81D\uD2B8",
|
|
351
684
|
npx: "NPX CLI \uB3C4\uAD6C \uD504\uB85C\uC81D\uD2B8"
|
|
352
685
|
};
|
|
353
|
-
|
|
354
|
-
const targetDir = options.cwd || process.cwd();
|
|
686
|
+
async function validateTargetDirectory(targetDir) {
|
|
355
687
|
try {
|
|
356
|
-
const stat = await
|
|
688
|
+
const stat = await fs7.stat(targetDir);
|
|
357
689
|
if (!stat.isDirectory()) {
|
|
358
690
|
logger.error(`Target is not a directory: ${targetDir}`);
|
|
359
691
|
process.exit(1);
|
|
@@ -367,60 +699,25 @@ var init = async (options) => {
|
|
|
367
699
|
process.exit(1);
|
|
368
700
|
}
|
|
369
701
|
try {
|
|
370
|
-
await
|
|
702
|
+
await fs7.access(targetDir, fs7.constants.W_OK);
|
|
371
703
|
} catch {
|
|
372
704
|
logger.error(`No write permission for: ${targetDir}`);
|
|
373
705
|
process.exit(1);
|
|
374
706
|
}
|
|
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
|
-
}
|
|
707
|
+
}
|
|
708
|
+
async function selectTemplates(options, availableTemplates) {
|
|
709
|
+
const result = await promptTemplateSelection({
|
|
710
|
+
providedTemplates: options.templates,
|
|
711
|
+
availableTemplates,
|
|
712
|
+
templateDescriptions: TEMPLATE_DESCRIPTIONS
|
|
713
|
+
});
|
|
714
|
+
return result.templates;
|
|
715
|
+
}
|
|
716
|
+
async function confirmOverwriteIfNeeded(targetDir, force) {
|
|
408
717
|
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
|
-
}
|
|
718
|
+
await promptOverwrite({ existingFiles, force });
|
|
719
|
+
}
|
|
720
|
+
async function installTemplates(templates, targetDir) {
|
|
424
721
|
const isSingleTemplate = templates.length === 1;
|
|
425
722
|
let totalFiles = 0;
|
|
426
723
|
let totalDirectories = 0;
|
|
@@ -454,154 +751,27 @@ var init = async (options) => {
|
|
|
454
751
|
}
|
|
455
752
|
logger.blank();
|
|
456
753
|
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
|
-
}
|
|
754
|
+
return { files: totalFiles, directories: totalDirectories };
|
|
755
|
+
}
|
|
756
|
+
async function promptForExtrasInstallation(options, hasSkills, hasCommands, hasAgents, hasInstructions) {
|
|
757
|
+
return await promptExtrasSelection({
|
|
758
|
+
skills: options.skills,
|
|
759
|
+
commands: options.commands,
|
|
760
|
+
agents: options.agents,
|
|
761
|
+
instructions: options.instructions,
|
|
762
|
+
hasSkills,
|
|
763
|
+
hasCommands,
|
|
764
|
+
hasAgents,
|
|
765
|
+
hasInstructions
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
function showInstallationSummary(templates, flags, hasSkills, hasCommands, hasAgents, hasInstructions) {
|
|
600
769
|
logger.blank();
|
|
601
770
|
logger.success("Claude Code documentation installed!");
|
|
602
771
|
logger.blank();
|
|
603
772
|
logger.info("Installed templates:");
|
|
604
773
|
templates.forEach((t) => logger.step(t));
|
|
774
|
+
const { installSkills, installCommands, installAgents, installInstructions } = flags;
|
|
605
775
|
if (installSkills && hasSkills || installCommands && hasCommands || installAgents && hasAgents || installInstructions && hasInstructions) {
|
|
606
776
|
logger.blank();
|
|
607
777
|
logger.info("Installed extras:");
|
|
@@ -623,11 +793,53 @@ var init = async (options) => {
|
|
|
623
793
|
logger.step("Read CLAUDE.md for project guidelines");
|
|
624
794
|
logger.step("Explore docs/ for detailed documentation");
|
|
625
795
|
logger.blank();
|
|
796
|
+
}
|
|
797
|
+
var init = async (options) => {
|
|
798
|
+
const targetDir = options.cwd || process.cwd();
|
|
799
|
+
await validateTargetDirectory(targetDir);
|
|
800
|
+
const availableTemplates = await listAvailableTemplates();
|
|
801
|
+
if (availableTemplates.length === 0) {
|
|
802
|
+
logger.error("No templates found. Package may be corrupted.");
|
|
803
|
+
process.exit(1);
|
|
804
|
+
}
|
|
805
|
+
const templates = await selectTemplates(options, availableTemplates);
|
|
806
|
+
await confirmOverwriteIfNeeded(targetDir, options.force ?? false);
|
|
807
|
+
await installTemplates(templates, targetDir);
|
|
808
|
+
const { hasSkills, hasCommands, hasAgents, hasInstructions } = await checkAllExtrasExist(templates);
|
|
809
|
+
const flags = await promptForExtrasInstallation(
|
|
810
|
+
options,
|
|
811
|
+
hasSkills,
|
|
812
|
+
hasCommands,
|
|
813
|
+
hasAgents,
|
|
814
|
+
hasInstructions
|
|
815
|
+
);
|
|
816
|
+
await installExtras(
|
|
817
|
+
templates,
|
|
818
|
+
targetDir,
|
|
819
|
+
flags,
|
|
820
|
+
{ hasSkills, hasCommands, hasAgents, hasInstructions },
|
|
821
|
+
options.force ?? false
|
|
822
|
+
);
|
|
823
|
+
showInstallationSummary(
|
|
824
|
+
templates,
|
|
825
|
+
flags,
|
|
826
|
+
hasSkills,
|
|
827
|
+
hasCommands,
|
|
828
|
+
hasAgents,
|
|
829
|
+
hasInstructions
|
|
830
|
+
);
|
|
831
|
+
try {
|
|
832
|
+
await updateGitignore(targetDir);
|
|
833
|
+
} catch (error) {
|
|
834
|
+
logger.warn(
|
|
835
|
+
`Failed to update .gitignore: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
836
|
+
);
|
|
837
|
+
}
|
|
626
838
|
};
|
|
627
839
|
|
|
628
840
|
// src/index.ts
|
|
629
841
|
var program = new Command();
|
|
630
|
-
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.5.
|
|
842
|
+
program.name("claude-code").description("Claude Code documentation installer for projects").version("0.5.5");
|
|
631
843
|
program.option(
|
|
632
844
|
"-t, --template <names>",
|
|
633
845
|
"template names (comma-separated: tanstack-start,hono)"
|