ccjk 13.5.7 → 13.6.0

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.
Files changed (140) hide show
  1. package/dist/chunks/agent-teams.mjs +3 -3
  2. package/dist/chunks/agent.mjs +2 -2
  3. package/dist/chunks/agents.mjs +3 -3
  4. package/dist/chunks/api-cli.mjs +5 -5
  5. package/dist/chunks/api-providers.mjs +1 -1
  6. package/dist/chunks/api.mjs +4 -4
  7. package/dist/chunks/auto-bootstrap.mjs +1 -1
  8. package/dist/chunks/auto-fix.mjs +17 -5
  9. package/dist/chunks/auto-fixer.mjs +3 -3
  10. package/dist/chunks/auto-init.mjs +3 -3
  11. package/dist/chunks/auto-updater.mjs +7 -7
  12. package/dist/chunks/banner.mjs +2 -2
  13. package/dist/chunks/boost.mjs +5 -5
  14. package/dist/chunks/ccjk-agents.mjs +2 -2
  15. package/dist/chunks/ccjk-all.mjs +6 -6
  16. package/dist/chunks/ccjk-config.mjs +2 -2
  17. package/dist/chunks/ccjk-hooks.mjs +3 -3
  18. package/dist/chunks/ccjk-mcp.mjs +4 -4
  19. package/dist/chunks/ccjk-setup.mjs +4 -4
  20. package/dist/chunks/ccjk-skills.mjs +4 -4
  21. package/dist/chunks/ccr.mjs +16 -16
  22. package/dist/chunks/ccu.mjs +2 -2
  23. package/dist/chunks/check-updates.mjs +8 -8
  24. package/dist/chunks/claude-code-config-manager.mjs +5 -5
  25. package/dist/chunks/claude-code-incremental-manager.mjs +6 -6
  26. package/dist/chunks/claude-config.mjs +2 -2
  27. package/dist/chunks/claude-wrapper.mjs +1 -1
  28. package/dist/chunks/cli-hook.mjs +5 -5
  29. package/dist/chunks/codex-config-switch.mjs +6 -6
  30. package/dist/chunks/codex-provider-manager.mjs +7 -7
  31. package/dist/chunks/codex-uninstaller.mjs +2 -2
  32. package/dist/chunks/codex.mjs +7 -7
  33. package/dist/chunks/commands.mjs +2 -2
  34. package/dist/chunks/commands2.mjs +2 -2
  35. package/dist/chunks/commit.mjs +3 -3
  36. package/dist/chunks/completion.mjs +2 -2
  37. package/dist/chunks/config-consolidator.mjs +2 -2
  38. package/dist/chunks/config-switch.mjs +8 -8
  39. package/dist/chunks/config.mjs +20 -9
  40. package/dist/chunks/config2.mjs +4 -4
  41. package/dist/chunks/config3.mjs +3 -3
  42. package/dist/chunks/constants.mjs +1 -1
  43. package/dist/chunks/context.mjs +3 -3
  44. package/dist/chunks/dashboard.mjs +2 -2
  45. package/dist/chunks/doctor.mjs +4 -4
  46. package/dist/chunks/evolution.mjs +2 -2
  47. package/dist/chunks/features.mjs +8 -8
  48. package/dist/chunks/help.mjs +1 -1
  49. package/dist/chunks/impact.mjs +1 -1
  50. package/dist/chunks/index.mjs +18 -19
  51. package/dist/chunks/index10.mjs +5145 -528
  52. package/dist/chunks/index11.mjs +1061 -569
  53. package/dist/chunks/index12.mjs +193 -1146
  54. package/dist/chunks/index13.mjs +607 -937
  55. package/dist/chunks/index14.mjs +951 -136
  56. package/dist/chunks/index15.mjs +185 -209
  57. package/dist/chunks/index2.mjs +7623 -12
  58. package/dist/chunks/index3.mjs +160 -19081
  59. package/dist/chunks/index4.mjs +19 -18
  60. package/dist/chunks/index5.mjs +12 -7623
  61. package/dist/chunks/index6.mjs +19077 -156
  62. package/dist/chunks/index7.mjs +565 -1551
  63. package/dist/chunks/index8.mjs +1535 -5166
  64. package/dist/chunks/init.mjs +1107 -1101
  65. package/dist/chunks/installer.mjs +5 -5
  66. package/dist/chunks/installer2.mjs +2 -2
  67. package/dist/chunks/interview.mjs +4 -4
  68. package/dist/chunks/manager.mjs +1 -1
  69. package/dist/chunks/marketplace.mjs +3 -3
  70. package/dist/chunks/mcp-cli.mjs +9 -9
  71. package/dist/chunks/mcp.mjs +10 -10
  72. package/dist/chunks/memory-check.mjs +2 -2
  73. package/dist/chunks/memory-paths.mjs +2 -2
  74. package/dist/chunks/memory-sync.mjs +2 -2
  75. package/dist/chunks/memory.mjs +4 -4
  76. package/dist/chunks/metrics-display.mjs +1 -1
  77. package/dist/chunks/migrator.mjs +1 -1
  78. package/dist/chunks/monitor.mjs +2 -2
  79. package/dist/chunks/notification.mjs +4 -4
  80. package/dist/chunks/onboarding.mjs +4 -4
  81. package/dist/chunks/package.mjs +1 -1
  82. package/dist/chunks/paradigm.mjs +1 -1
  83. package/dist/chunks/permission-manager.mjs +2 -2
  84. package/dist/chunks/permissions.mjs +3 -3
  85. package/dist/chunks/persistence-manager.mjs +3 -3
  86. package/dist/chunks/platform.mjs +1 -1
  87. package/dist/chunks/plugin.mjs +2 -2
  88. package/dist/chunks/prompts.mjs +5 -5
  89. package/dist/chunks/providers.mjs +2 -2
  90. package/dist/chunks/quick-actions.mjs +3 -3
  91. package/dist/chunks/quick-provider.mjs +4 -4
  92. package/dist/chunks/quick-setup.mjs +11 -11
  93. package/dist/chunks/remote.mjs +5 -5
  94. package/dist/chunks/session-manager.mjs +1 -1
  95. package/dist/chunks/session.mjs +2 -2
  96. package/dist/chunks/sessions.mjs +1 -1
  97. package/dist/chunks/silent-updater.mjs +1 -1
  98. package/dist/chunks/simple-config.mjs +1 -1
  99. package/dist/chunks/skill2.mjs +3 -3
  100. package/dist/chunks/skills-sync.mjs +5 -5
  101. package/dist/chunks/skills.mjs +3 -3
  102. package/dist/chunks/slash-commands.mjs +3 -3
  103. package/dist/chunks/startup.mjs +1 -1
  104. package/dist/chunks/stats.mjs +2 -2
  105. package/dist/chunks/status.mjs +3 -3
  106. package/dist/chunks/team.mjs +3 -3
  107. package/dist/chunks/thinking.mjs +4 -4
  108. package/dist/chunks/trace.mjs +2 -2
  109. package/dist/chunks/uninstall.mjs +8 -8
  110. package/dist/chunks/update.mjs +12 -12
  111. package/dist/chunks/upgrade-manager.mjs +3 -3
  112. package/dist/chunks/upgrade.mjs +1 -1
  113. package/dist/chunks/version-checker.mjs +4 -4
  114. package/dist/chunks/vim.mjs +3 -3
  115. package/dist/chunks/workflows.mjs +1 -1
  116. package/dist/chunks/wsl.mjs +1 -1
  117. package/dist/chunks/zero-config.mjs +3 -3
  118. package/dist/cli.mjs +195 -185
  119. package/dist/index.mjs +8 -8
  120. package/dist/shared/{ccjk.BJMRY2Ra.mjs → ccjk.8oaxX4iR.mjs} +1 -1
  121. package/dist/shared/{ccjk.8VhZ_dEO.mjs → ccjk.Bq8TqZG_.mjs} +1 -1
  122. package/dist/shared/{ccjk.Bb9Mpi2O.mjs → ccjk.C1hANZTu.mjs} +1 -1
  123. package/dist/shared/{ccjk.KpFl2RDA.mjs → ccjk.C4m4ypdk.mjs} +5 -5
  124. package/dist/shared/{ccjk.I6IuYdc_.mjs → ccjk.CQd7SKNC.mjs} +2 -2
  125. package/dist/shared/{ccjk.Cjj8SVrn.mjs → ccjk.CiKtBUW_.mjs} +1 -1
  126. package/dist/shared/{ccjk.DKBI0lAR.mjs → ccjk.Cv13QsGp.mjs} +3 -3
  127. package/dist/shared/{ccjk.D8BqKRwH.mjs → ccjk.CwsecBYP.mjs} +4 -4
  128. package/dist/shared/{ccjk.DCw2WnZU.mjs → ccjk.D75wivnp.mjs} +1 -1
  129. package/dist/shared/{ccjk.DZ4ehAHg.mjs → ccjk.DDq2hqA5.mjs} +24 -4
  130. package/dist/shared/{ccjk.BIxuVL3_.mjs → ccjk.DGllfVCZ.mjs} +3 -3
  131. package/dist/shared/{ccjk.BWFpnOr3.mjs → ccjk.DZ2LLOa-.mjs} +1 -1
  132. package/dist/shared/{ccjk.eDO9cM5o.mjs → ccjk.DbigonEQ.mjs} +1 -1
  133. package/dist/shared/{ccjk.CIObNAcl.mjs → ccjk.Dhu8ia5S.mjs} +1 -1
  134. package/dist/shared/{ccjk.CHUEFqmw.mjs → ccjk.DlTXS9rP.mjs} +1 -1
  135. package/dist/shared/{ccjk.CxAFICpL.mjs → ccjk.DvAP4XfP.mjs} +4 -4
  136. package/dist/shared/{ccjk.BOu1yav7.mjs → ccjk.KPLeMP-o.mjs} +1 -1
  137. package/dist/shared/{ccjk.D8ZLYSZZ.mjs → ccjk.vO3d1ABk.mjs} +1 -1
  138. package/dist/templates/claude-code/common/settings.json +0 -1
  139. package/package.json +68 -65
  140. package/templates/claude-code/common/settings.json +0 -1
@@ -1,1009 +1,679 @@
1
- import a from './index2.mjs';
2
- import ora from './index7.mjs';
3
- import fs from 'node:fs/promises';
4
- import os__default from 'node:os';
5
- import path__default from 'node:path';
6
- import { exec } from 'node:child_process';
7
- import { promisify } from 'node:util';
8
- import '../shared/ccjk.BAGoDD49.mjs';
9
- import 'node:process';
10
- import '../shared/ccjk.Cjgrln_h.mjs';
11
- import '../shared/ccjk.DeWpAShp.mjs';
1
+ import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { j as join, d as dirname } from '../shared/ccjk.bQ7Dh1g4.mjs';
3
+ import { homedir } from 'node:os';
4
+ import { fileURLToPath } from 'node:url';
12
5
 
13
- function getCcjkConfigDir() {
14
- if (process.env.CCJK_CONFIG_DIR) {
15
- return process.env.CCJK_CONFIG_DIR;
16
- }
17
- return path__default.join(os__default.homedir(), ".ccjk");
18
- }
19
- function getInstallPath(pluginType, name) {
20
- const configDir = getCcjkConfigDir();
21
- const typeDir = {
22
- skill: "skills",
23
- mcp: "mcp-servers",
24
- agent: "agents",
25
- hook: "hooks"
26
- }[pluginType];
27
- return path__default.join(configDir, typeDir, name);
28
- }
29
- async function copyDirectory(src, dest) {
30
- await fs.mkdir(dest, { recursive: true });
31
- const entries = await fs.readdir(src, { withFileTypes: true });
32
- for (const entry of entries) {
33
- const srcPath = path__default.join(src, entry.name);
34
- const destPath = path__default.join(dest, entry.name);
35
- if (entry.isDirectory()) {
36
- await copyDirectory(srcPath, destPath);
37
- } else {
38
- await fs.copyFile(srcPath, destPath);
39
- }
40
- }
41
- }
42
- async function downloadFile(url, destPath) {
43
- const response = await fetch(url, {
44
- headers: {
45
- "User-Agent": "ccjk-cli"
46
- },
47
- redirect: "follow"
48
- });
49
- if (!response.ok) {
50
- throw new Error(`Failed to download: ${response.statusText}`);
51
- }
52
- const buffer = Buffer.from(await response.arrayBuffer());
53
- await fs.writeFile(destPath, buffer);
54
- }
55
-
56
- async function installFromGitHub(sourceInfo, pluginType, options = {}) {
57
- const { force = false, dryRun = false } = options;
58
- const { owner, repo, ref = "main", subpath } = sourceInfo;
59
- try {
60
- const repoInfo = await fetchRepoInfo(owner, repo);
61
- const defaultBranch = repoInfo.default_branch || "main";
62
- const actualRef = ref || defaultBranch;
63
- const installPath = getInstallPath(pluginType, repo);
64
- if (!force) {
65
- try {
66
- await fs.access(installPath);
67
- return {
68
- success: false,
69
- source: sourceInfo.originalUrl,
70
- sourceType: "github",
71
- pluginType,
72
- error: `Plugin already exists at ${installPath}. Use --force to overwrite.`
73
- };
74
- } catch {
75
- }
76
- }
77
- if (dryRun) {
78
- const files = await listRepoFiles(owner, repo, actualRef, subpath);
79
- return {
80
- success: true,
81
- source: sourceInfo.originalUrl,
82
- sourceType: "github",
83
- pluginType,
84
- installedPath: installPath,
85
- details: {
86
- name: repo,
87
- version: actualRef,
88
- description: repoInfo.description || void 0,
89
- files: files.slice(0, 20)
90
- }
91
- };
92
- }
93
- const tempDir = await downloadRepo(owner, repo, actualRef);
94
- const sourcePath = subpath ? path__default.join(tempDir, subpath) : tempDir;
95
- await fs.mkdir(path__default.dirname(installPath), { recursive: true });
96
- await copyDirectory(sourcePath, installPath);
97
- await fs.rm(tempDir, { recursive: true, force: true });
98
- const installedFiles = await listInstalledFiles$1(installPath);
6
+ class ProjectAnalyzer {
7
+ projectRoot;
8
+ constructor(projectRoot = process.cwd()) {
9
+ this.projectRoot = projectRoot;
10
+ }
11
+ /**
12
+ * Analyze project and return comprehensive analysis
13
+ */
14
+ async analyze() {
15
+ const packageJson = this.readPackageJson();
16
+ const techStack = this.detectTechStack(packageJson);
17
+ const projectType = this.detectProjectType(techStack, packageJson);
18
+ const frameworks = this.detectFrameworks(packageJson);
19
+ const hasTests = this.detectTestFramework(packageJson);
20
+ const hasDatabase = this.detectDatabase(packageJson);
21
+ const hasApi = this.detectApiEndpoints();
22
+ const buildTool = this.detectBuildTool(packageJson);
23
+ const cicd = this.detectCICD();
24
+ const patterns = this.detectPatterns();
25
+ const confidence = this.calculateConfidence(techStack, frameworks);
99
26
  return {
100
- success: true,
101
- source: sourceInfo.originalUrl,
102
- sourceType: "github",
103
- pluginType,
104
- installedPath: installPath,
105
- details: {
106
- name: repo,
107
- version: actualRef,
108
- description: repoInfo.description || void 0,
109
- files: installedFiles
110
- }
111
- };
112
- } catch (error) {
113
- return {
114
- success: false,
115
- source: sourceInfo.originalUrl,
116
- sourceType: "github",
117
- pluginType,
118
- error: error instanceof Error ? error.message : String(error)
27
+ projectRoot: this.projectRoot,
28
+ projectType,
29
+ techStack,
30
+ frameworks,
31
+ hasTests,
32
+ hasDatabase,
33
+ hasApi,
34
+ buildTool,
35
+ cicd,
36
+ patterns,
37
+ confidence,
38
+ packageJson: packageJson ?? void 0
119
39
  };
120
40
  }
121
- }
122
- async function fetchRepoInfo(owner, repo) {
123
- const url = `https://api.github.com/repos/${owner}/${repo}`;
124
- const response = await fetch(url, {
125
- headers: {
126
- "Accept": "application/vnd.github.v3+json",
127
- "User-Agent": "ccjk-cli"
128
- }
129
- });
130
- if (!response.ok) {
131
- if (response.status === 404) {
132
- throw new Error(`Repository not found: ${owner}/${repo}`);
41
+ /**
42
+ * Read and parse package.json
43
+ */
44
+ readPackageJson() {
45
+ const packageJsonPath = join(this.projectRoot, "package.json");
46
+ if (!existsSync(packageJsonPath)) {
47
+ return null;
133
48
  }
134
- throw new Error(`Failed to fetch repository info: ${response.statusText}`);
135
- }
136
- return response.json();
137
- }
138
- async function listRepoFiles(owner, repo, ref, subpath) {
139
- const treePath = subpath || "";
140
- const url = `https://api.github.com/repos/${owner}/${repo}/git/trees/${ref}?recursive=1`;
141
- const response = await fetch(url, {
142
- headers: {
143
- "Accept": "application/vnd.github.v3+json",
144
- "User-Agent": "ccjk-cli"
145
- }
146
- });
147
- if (!response.ok) {
148
- return [];
149
- }
150
- const data = await response.json();
151
- const files = (data.tree || []).filter((item) => item.type === "blob").map((item) => item.path).filter((filePath) => !treePath || filePath.startsWith(treePath));
152
- return files;
153
- }
154
- async function downloadRepo(owner, repo, ref) {
155
- const zipUrl = `https://github.com/${owner}/${repo}/archive/${ref}.zip`;
156
- const tempDir = path__default.join(os__default.tmpdir(), `ccjk-${repo}-${Date.now()}`);
157
- const zipPath = path__default.join(tempDir, "repo.zip");
158
- await fs.mkdir(tempDir, { recursive: true });
159
- await downloadFile(zipUrl, zipPath);
160
- const extractedDir = await extractZip(zipPath, tempDir);
161
- await fs.unlink(zipPath);
162
- return extractedDir;
163
- }
164
- async function extractZip(zipPath, destDir) {
165
- const { exec } = await import('node:child_process');
166
- const { promisify } = await import('node:util');
167
- const execAsync = promisify(exec);
168
- try {
169
- await execAsync(`unzip -q "${zipPath}" -d "${destDir}"`);
170
- } catch {
171
49
  try {
172
- await execAsync(`tar -xf "${zipPath}" -C "${destDir}"`);
50
+ const content = readFileSync(packageJsonPath, "utf-8");
51
+ return JSON.parse(content);
173
52
  } catch {
174
- throw new Error("Failed to extract archive. Please ensure unzip or tar is installed.");
53
+ return null;
175
54
  }
176
55
  }
177
- const entries = await fs.readdir(destDir);
178
- const extractedDir = entries.find((entry) => !entry.endsWith(".zip"));
179
- if (!extractedDir) {
180
- throw new Error("Failed to find extracted directory");
181
- }
182
- return path__default.join(destDir, extractedDir);
183
- }
184
- async function listInstalledFiles$1(dir) {
185
- const files = [];
186
- async function walk(currentDir, prefix = "") {
187
- const entries = await fs.readdir(currentDir, { withFileTypes: true });
188
- for (const entry of entries) {
189
- const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
190
- if (entry.isDirectory()) {
191
- await walk(path__default.join(currentDir, entry.name), relativePath);
192
- } else {
193
- files.push(relativePath);
194
- }
195
- }
196
- }
197
- await walk(dir);
198
- return files;
199
- }
200
-
201
- async function installFromLocal(sourceInfo, pluginType, options = {}) {
202
- const { force = false, dryRun = false } = options;
203
- const { absolutePath, originalPath } = sourceInfo;
204
- try {
205
- const stat = await fs.stat(absolutePath);
206
- const isFile = stat.isFile();
207
- const isDirectory = stat.isDirectory();
208
- if (!isFile && !isDirectory) {
56
+ /**
57
+ * Detect technology stack from dependencies
58
+ */
59
+ detectTechStack(packageJson) {
60
+ if (!packageJson) {
209
61
  return {
210
- success: false,
211
- source: originalPath,
212
- sourceType: "local",
213
- pluginType,
214
- error: `Source path is neither a file nor a directory: ${absolutePath}`
62
+ languages: ["javascript"],
63
+ runtime: "node",
64
+ packageManager: this.detectPackageManager()
215
65
  };
216
66
  }
217
- const pluginName = path__default.basename(absolutePath, path__default.extname(absolutePath));
218
- const installPath = getInstallPath(pluginType, pluginName);
219
- if (!force) {
220
- try {
221
- await fs.access(installPath);
222
- return {
223
- success: false,
224
- source: originalPath,
225
- sourceType: "local",
226
- pluginType,
227
- error: `Plugin already exists at ${installPath}. Use --force to overwrite.`
228
- };
229
- } catch {
230
- }
67
+ const deps = {
68
+ ...packageJson.dependencies,
69
+ ...packageJson.devDependencies
70
+ };
71
+ const languages = [];
72
+ let runtime = "node";
73
+ if (deps.typescript || existsSync(join(this.projectRoot, "tsconfig.json"))) {
74
+ languages.push("typescript");
75
+ } else {
76
+ languages.push("javascript");
231
77
  }
232
- const files = isFile ? [path__default.basename(absolutePath)] : await listFiles(absolutePath);
233
- const pluginInfo = await readPluginInfo(absolutePath, isFile);
234
- if (dryRun) {
235
- return {
236
- success: true,
237
- source: originalPath,
238
- sourceType: "local",
239
- pluginType,
240
- installedPath: installPath,
241
- details: {
242
- name: pluginInfo.name || pluginName,
243
- version: pluginInfo.version,
244
- description: pluginInfo.description,
245
- files
246
- }
247
- };
78
+ if (existsSync(join(this.projectRoot, "deno.json")) || existsSync(join(this.projectRoot, "deno.jsonc"))) {
79
+ runtime = "deno";
248
80
  }
249
- if (isFile) {
250
- await fs.mkdir(path__default.dirname(installPath), { recursive: true });
251
- if (absolutePath.endsWith(".md")) {
252
- await fs.copyFile(absolutePath, installPath);
253
- } else {
254
- await fs.mkdir(installPath, { recursive: true });
255
- await fs.copyFile(
256
- absolutePath,
257
- path__default.join(installPath, path__default.basename(absolutePath))
258
- );
259
- }
260
- } else {
261
- await fs.mkdir(path__default.dirname(installPath), { recursive: true });
262
- await copyDirectory(absolutePath, installPath);
81
+ if (existsSync(join(this.projectRoot, "bun.lockb"))) {
82
+ runtime = "bun";
263
83
  }
264
- const installedFiles = isFile ? [path__default.basename(absolutePath)] : await listFiles(installPath);
265
- return {
266
- success: true,
267
- source: originalPath,
268
- sourceType: "local",
269
- pluginType,
270
- installedPath: installPath,
271
- details: {
272
- name: pluginInfo.name || pluginName,
273
- version: pluginInfo.version,
274
- description: pluginInfo.description,
275
- files: installedFiles
276
- }
277
- };
278
- } catch (error) {
279
84
  return {
280
- success: false,
281
- source: originalPath,
282
- sourceType: "local",
283
- pluginType,
284
- error: error instanceof Error ? error.message : String(error)
85
+ languages,
86
+ runtime,
87
+ packageManager: this.detectPackageManager()
285
88
  };
286
89
  }
287
- }
288
- async function listFiles(dir) {
289
- const files = [];
290
- async function walk(currentDir, prefix = "") {
291
- const entries = await fs.readdir(currentDir, { withFileTypes: true });
292
- for (const entry of entries) {
293
- if (entry.name.startsWith(".") || entry.name === "node_modules") {
294
- continue;
295
- }
296
- const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
297
- if (entry.isDirectory()) {
298
- await walk(path__default.join(currentDir, entry.name), relativePath);
299
- } else {
300
- files.push(relativePath);
301
- }
90
+ /**
91
+ * Detect package manager
92
+ */
93
+ detectPackageManager() {
94
+ if (existsSync(join(this.projectRoot, "pnpm-lock.yaml"))) {
95
+ return "pnpm";
302
96
  }
303
- }
304
- await walk(dir);
305
- return files;
306
- }
307
- async function readPluginInfo(sourcePath, isFile) {
308
- if (isFile) {
309
- const name = path__default.basename(sourcePath, path__default.extname(sourcePath));
310
- return { name };
311
- }
312
- try {
313
- const packageJsonPath = path__default.join(sourcePath, "package.json");
314
- const content = await fs.readFile(packageJsonPath, "utf-8");
315
- const packageJson = JSON.parse(content);
316
- return {
317
- name: packageJson.name,
318
- version: packageJson.version,
319
- description: packageJson.description
320
- };
321
- } catch {
322
- }
323
- const metaFiles = ["SKILL.md", "skill.md", "README.md", "readme.md"];
324
- for (const metaFile of metaFiles) {
325
- try {
326
- const metaPath = path__default.join(sourcePath, metaFile);
327
- const content = await fs.readFile(metaPath, "utf-8");
328
- const titleMatch = content.match(/^#\s+(.+)$/m);
329
- if (titleMatch) {
330
- return { name: titleMatch[1].trim() };
331
- }
332
- } catch {
97
+ if (existsSync(join(this.projectRoot, "yarn.lock"))) {
98
+ return "yarn";
333
99
  }
334
- }
335
- return {};
336
- }
337
-
338
- const execAsync = promisify(exec);
339
- async function installFromNpm(sourceInfo, pluginType, options = {}) {
340
- const { force = false, dryRun = false } = options;
341
- const { packageName, version } = sourceInfo;
342
- try {
343
- const packageInfo = await fetchPackageInfo(packageName, version);
344
- const packageVersion = version || packageInfo.version;
345
- const shortName = getShortName(packageName);
346
- const installPath = getInstallPath(pluginType, shortName);
347
- if (!force) {
348
- try {
349
- await fs.access(installPath);
350
- return {
351
- success: false,
352
- source: packageName,
353
- sourceType: "npm",
354
- pluginType,
355
- error: `Plugin already exists at ${installPath}. Use --force to overwrite.`
356
- };
357
- } catch {
358
- }
100
+ if (existsSync(join(this.projectRoot, "bun.lockb"))) {
101
+ return "bun";
359
102
  }
360
- if (dryRun) {
361
- const files = packageInfo.files || ["(package files)"];
362
- return {
363
- success: true,
364
- source: packageName,
365
- sourceType: "npm",
366
- pluginType,
367
- installedPath: installPath,
368
- details: {
369
- name: packageName,
370
- version: packageVersion,
371
- description: packageInfo.description,
372
- files
373
- }
374
- };
103
+ if (existsSync(join(this.projectRoot, "package-lock.json"))) {
104
+ return "npm";
375
105
  }
376
- if (pluginType === "mcp") {
377
- await installMcpPackage(packageName, packageVersion, installPath);
378
- } else {
379
- await installGenericPackage(packageName, packageVersion, installPath);
106
+ return "npm";
107
+ }
108
+ /**
109
+ * Detect project type
110
+ */
111
+ detectProjectType(techStack, packageJson) {
112
+ if (!packageJson) {
113
+ return "unknown";
380
114
  }
381
- const installedFiles = await listInstalledFiles(installPath);
382
- return {
383
- success: true,
384
- source: packageName,
385
- sourceType: "npm",
386
- pluginType,
387
- installedPath: installPath,
388
- details: {
389
- name: packageName,
390
- version: packageVersion,
391
- description: packageInfo.description,
392
- files: installedFiles
393
- }
394
- };
395
- } catch (error) {
396
- return {
397
- success: false,
398
- source: packageName,
399
- sourceType: "npm",
400
- pluginType,
401
- error: error instanceof Error ? error.message : String(error)
115
+ const deps = {
116
+ ...packageJson.dependencies,
117
+ ...packageJson.devDependencies
402
118
  };
403
- }
404
- }
405
- async function fetchPackageInfo(packageName, version) {
406
- const url = version ? `https://registry.npmjs.org/${packageName}/${version}` : `https://registry.npmjs.org/${packageName}/latest`;
407
- const response = await fetch(url);
408
- if (!response.ok) {
409
- if (response.status === 404) {
410
- throw new Error(`Package not found: ${packageName}`);
119
+ const hasFrontend = deps.react || deps.vue || deps["@angular/core"] || deps.svelte;
120
+ const hasBackend = deps.express || deps.fastify || deps.koa || deps["@nestjs/core"] || deps.hapi;
121
+ if (deps.next || deps.nuxt || deps["@remix-run/react"] || deps["@sveltejs/kit"]) {
122
+ return "fullstack";
411
123
  }
412
- throw new Error(`Failed to fetch package info: ${response.statusText}`);
413
- }
414
- const data = await response.json();
415
- return {
416
- version: data.version,
417
- description: data.description,
418
- files: data.files
419
- };
420
- }
421
- function getShortName(packageName) {
422
- if (packageName.startsWith("@")) {
423
- const parts = packageName.split("/");
424
- return parts[1] || packageName;
425
- }
426
- return packageName;
427
- }
428
- async function installMcpPackage(packageName, version, installPath) {
429
- await fs.mkdir(installPath, { recursive: true });
430
- const packageJson = {
431
- name: `ccjk-mcp-${getShortName(packageName)}`,
432
- version: "1.0.0",
433
- private: true,
434
- dependencies: {
435
- [packageName]: version
124
+ if (packageJson.bin || deps.commander || deps.yargs || deps.cac) {
125
+ return "cli";
436
126
  }
437
- };
438
- await fs.writeFile(
439
- path__default.join(installPath, "package.json"),
440
- JSON.stringify(packageJson, null, 2)
441
- );
442
- try {
443
- await execAsync("npm install", { cwd: installPath });
444
- } catch (error) {
445
- try {
446
- await execAsync("pnpm install", { cwd: installPath });
447
- } catch {
448
- throw new Error(
449
- `Failed to install package. Original error: ${error instanceof Error ? error.message : String(error)}`
450
- );
127
+ if (packageJson.main && !hasFrontend && !hasBackend) {
128
+ return "library";
451
129
  }
130
+ if (hasFrontend && hasBackend) {
131
+ return "fullstack";
132
+ }
133
+ if (hasFrontend) {
134
+ return "frontend";
135
+ }
136
+ if (hasBackend) {
137
+ return "backend";
138
+ }
139
+ return "unknown";
452
140
  }
453
- }
454
- async function installGenericPackage(packageName, version, installPath) {
455
- const registryUrl = `https://registry.npmjs.org/${packageName}/${version}`;
456
- const response = await fetch(registryUrl);
457
- if (!response.ok) {
458
- throw new Error(`Failed to fetch package info: ${response.statusText}`);
459
- }
460
- const data = await response.json();
461
- const tarballUrl = data.dist?.tarball;
462
- if (!tarballUrl) {
463
- throw new Error("Failed to get tarball URL");
464
- }
465
- const tarballResponse = await fetch(tarballUrl);
466
- if (!tarballResponse.ok) {
467
- throw new Error(`Failed to download tarball: ${tarballResponse.statusText}`);
468
- }
469
- await fs.mkdir(installPath, { recursive: true });
470
- const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
471
- const tarballPath = path__default.join(installPath, "package.tgz");
472
- await fs.writeFile(tarballPath, tarballBuffer);
473
- try {
474
- await execAsync(`tar -xzf package.tgz --strip-components=1`, { cwd: installPath });
475
- } finally {
476
- await fs.unlink(tarballPath).catch(() => {
477
- });
478
- }
479
- }
480
- async function listInstalledFiles(dir) {
481
- const files = [];
482
- async function walk(currentDir, prefix = "") {
141
+ /**
142
+ * Detect frameworks
143
+ */
144
+ detectFrameworks(packageJson) {
145
+ if (!packageJson) {
146
+ return [];
147
+ }
148
+ const deps = {
149
+ ...packageJson.dependencies,
150
+ ...packageJson.devDependencies
151
+ };
152
+ const frameworks = [];
153
+ if (deps.react)
154
+ frameworks.push("react");
155
+ if (deps.vue)
156
+ frameworks.push("vue");
157
+ if (deps["@angular/core"])
158
+ frameworks.push("angular");
159
+ if (deps.svelte)
160
+ frameworks.push("svelte");
161
+ if (deps.next)
162
+ frameworks.push("next.js");
163
+ if (deps.nuxt)
164
+ frameworks.push("nuxt");
165
+ if (deps["@remix-run/react"])
166
+ frameworks.push("remix");
167
+ if (deps["@sveltejs/kit"])
168
+ frameworks.push("sveltekit");
169
+ if (deps.express)
170
+ frameworks.push("express");
171
+ if (deps.fastify)
172
+ frameworks.push("fastify");
173
+ if (deps.koa)
174
+ frameworks.push("koa");
175
+ if (deps["@nestjs/core"])
176
+ frameworks.push("nestjs");
177
+ if (deps.hapi)
178
+ frameworks.push("hapi");
179
+ return frameworks;
180
+ }
181
+ /**
182
+ * Detect test framework
183
+ */
184
+ detectTestFramework(packageJson) {
185
+ if (!packageJson) {
186
+ return false;
187
+ }
188
+ const deps = {
189
+ ...packageJson.dependencies,
190
+ ...packageJson.devDependencies
191
+ };
192
+ return !!(deps.vitest || deps.jest || deps.mocha || deps.ava || deps["@playwright/test"] || deps.cypress);
193
+ }
194
+ /**
195
+ * Detect database usage
196
+ */
197
+ detectDatabase(packageJson) {
198
+ if (!packageJson) {
199
+ return false;
200
+ }
201
+ const deps = {
202
+ ...packageJson.dependencies,
203
+ ...packageJson.devDependencies
204
+ };
205
+ return !!(deps.prisma || deps["@prisma/client"] || deps.drizzle || deps["drizzle-orm"] || deps.typeorm || deps.sequelize || deps.mongoose || deps.pg || deps.mysql || deps.mysql2 || deps.sqlite3 || deps["better-sqlite3"]);
206
+ }
207
+ /**
208
+ * Detect API endpoints
209
+ */
210
+ detectApiEndpoints() {
211
+ const apiDirs = [
212
+ "src/api",
213
+ "src/routes",
214
+ "api",
215
+ "routes",
216
+ "pages/api",
217
+ // Next.js
218
+ "app/api"
219
+ // Next.js App Router
220
+ ];
221
+ return apiDirs.some((dir) => existsSync(join(this.projectRoot, dir)));
222
+ }
223
+ /**
224
+ * Detect build tool
225
+ */
226
+ detectBuildTool(packageJson) {
227
+ if (!packageJson) {
228
+ return void 0;
229
+ }
230
+ const deps = {
231
+ ...packageJson.dependencies,
232
+ ...packageJson.devDependencies
233
+ };
234
+ if (deps.vite || existsSync(join(this.projectRoot, "vite.config.ts"))) {
235
+ return "vite";
236
+ }
237
+ if (deps.webpack || existsSync(join(this.projectRoot, "webpack.config.js"))) {
238
+ return "webpack";
239
+ }
240
+ if (deps.rollup || existsSync(join(this.projectRoot, "rollup.config.js"))) {
241
+ return "rollup";
242
+ }
243
+ if (deps.esbuild) {
244
+ return "esbuild";
245
+ }
246
+ if (deps.turbo || existsSync(join(this.projectRoot, "turbo.json"))) {
247
+ return "turbo";
248
+ }
249
+ return void 0;
250
+ }
251
+ /**
252
+ * Detect CI/CD configuration
253
+ */
254
+ detectCICD() {
255
+ const cicd = [];
256
+ if (existsSync(join(this.projectRoot, ".github", "workflows"))) {
257
+ cicd.push("github-actions");
258
+ }
259
+ if (existsSync(join(this.projectRoot, ".gitlab-ci.yml"))) {
260
+ cicd.push("gitlab-ci");
261
+ }
262
+ if (existsSync(join(this.projectRoot, ".circleci", "config.yml"))) {
263
+ cicd.push("circleci");
264
+ }
265
+ if (existsSync(join(this.projectRoot, ".travis.yml"))) {
266
+ cicd.push("travis-ci");
267
+ }
268
+ return cicd;
269
+ }
270
+ /**
271
+ * Detect common patterns in the project
272
+ */
273
+ detectPatterns() {
274
+ const patterns = [];
275
+ if (existsSync(join(this.projectRoot, "packages")) || existsSync(join(this.projectRoot, "apps"))) {
276
+ patterns.push("monorepo");
277
+ }
278
+ if (existsSync(join(this.projectRoot, "tsconfig.json"))) {
279
+ patterns.push("typescript");
280
+ }
281
+ if (existsSync(join(this.projectRoot, ".eslintrc.js")) || existsSync(join(this.projectRoot, "eslint.config.js"))) {
282
+ patterns.push("eslint");
283
+ }
284
+ if (existsSync(join(this.projectRoot, ".prettierrc")) || existsSync(join(this.projectRoot, "prettier.config.js"))) {
285
+ patterns.push("prettier");
286
+ }
287
+ if (existsSync(join(this.projectRoot, "Dockerfile")) || existsSync(join(this.projectRoot, "docker-compose.yml"))) {
288
+ patterns.push("docker");
289
+ }
290
+ if (existsSync(join(this.projectRoot, ".env.example")) || existsSync(join(this.projectRoot, ".env.local"))) {
291
+ patterns.push("env-config");
292
+ }
293
+ return patterns;
294
+ }
295
+ /**
296
+ * Calculate confidence score based on detected information
297
+ */
298
+ calculateConfidence(techStack, frameworks) {
299
+ let confidence = 0.5;
300
+ if (techStack.languages.length > 0)
301
+ confidence += 0.1;
302
+ if (techStack.runtime !== "unknown")
303
+ confidence += 0.1;
304
+ if (techStack.packageManager)
305
+ confidence += 0.1;
306
+ if (frameworks.length > 0)
307
+ confidence += 0.1;
308
+ if (frameworks.length > 2)
309
+ confidence += 0.1;
310
+ return Math.min(confidence, 1);
311
+ }
312
+ /**
313
+ * Get file count in directory
314
+ */
315
+ getFileCount(dir) {
483
316
  try {
484
- const entries = await fs.readdir(currentDir, { withFileTypes: true });
485
- for (const entry of entries) {
486
- if (entry.name === "node_modules")
487
- continue;
488
- const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
489
- if (entry.isDirectory()) {
490
- await walk(path__default.join(currentDir, entry.name), relativePath);
317
+ if (!existsSync(dir)) {
318
+ return 0;
319
+ }
320
+ let count = 0;
321
+ const items = readdirSync(dir);
322
+ for (const item of items) {
323
+ const fullPath = join(dir, item);
324
+ const stat = statSync(fullPath);
325
+ if (stat.isDirectory()) {
326
+ count += this.getFileCount(fullPath);
491
327
  } else {
492
- files.push(relativePath);
328
+ count++;
493
329
  }
494
330
  }
331
+ return count;
495
332
  } catch {
333
+ return 0;
496
334
  }
497
335
  }
498
- await walk(dir);
499
- return files;
500
336
  }
501
-
502
- function parseSource(source) {
503
- const trimmed = source.trim();
504
- const githubInfo = parseGitHubSource(trimmed);
505
- if (githubInfo) {
506
- return githubInfo;
507
- }
508
- const localInfo = parseLocalSource(trimmed);
509
- if (localInfo) {
510
- return localInfo;
511
- }
512
- return parseNpmSource(trimmed);
337
+ async function analyzeProject(projectRoot) {
338
+ const analyzer = new ProjectAnalyzer(projectRoot);
339
+ return analyzer.analyze();
513
340
  }
514
- function parseGitHubSource(source) {
515
- const prefixMatch = source.match(/^(?:github|gh):([^/]+)\/([^#@/]+)(?:#(.+))?$/);
516
- if (prefixMatch) {
517
- return {
518
- type: "github",
519
- owner: prefixMatch[1],
520
- repo: prefixMatch[2],
521
- ref: prefixMatch[3],
522
- originalUrl: source
523
- };
524
- }
525
- const urlMatch = source.match(
526
- /^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/tree\/([^/]+))?(?:\/(.+))?$/
527
- );
528
- if (urlMatch) {
529
- return {
530
- type: "github",
531
- owner: urlMatch[1],
532
- repo: urlMatch[2],
533
- ref: urlMatch[3],
534
- subpath: urlMatch[4],
535
- originalUrl: source
536
- };
537
- }
538
- const shortMatch = source.match(/^([\w-]+)\/([\w.-]+)(?:#(.+))?$/);
539
- if (shortMatch && !source.startsWith(".") && !source.startsWith("/") && !source.startsWith("~")) {
341
+
342
+ const __filename$1 = fileURLToPath(import.meta.url);
343
+ const __dirname$1 = dirname(__filename$1);
344
+ class ConfigGenerator {
345
+ templatesDir;
346
+ outputDir;
347
+ constructor(templatesDir, outputDir) {
348
+ this.templatesDir = templatesDir || join(__dirname$1, "..", "templates");
349
+ this.outputDir = outputDir || join(homedir(), ".config", "claude");
350
+ }
351
+ /**
352
+ * Generate configurations from template selection
353
+ */
354
+ async generate(selection) {
355
+ const agentConfigs = await this.generateAgents(selection.agents);
356
+ const skillConfigs = await this.generateSkills(selection.skills);
540
357
  return {
541
- type: "github",
542
- owner: shortMatch[1],
543
- repo: shortMatch[2],
544
- ref: shortMatch[3],
545
- originalUrl: source
358
+ agents: agentConfigs,
359
+ skills: skillConfigs,
360
+ summary: this.generateSummary(agentConfigs, skillConfigs)
546
361
  };
547
362
  }
548
- return null;
549
- }
550
- function parseLocalSource(source) {
551
- if (source.startsWith("./") || source.startsWith("../") || source.startsWith("/") || source.startsWith("~/")) {
552
- let absolutePath;
553
- if (source.startsWith("~/")) {
554
- absolutePath = path__default.join(os__default.homedir(), source.slice(2));
555
- } else if (path__default.isAbsolute(source)) {
556
- absolutePath = source;
557
- } else {
558
- absolutePath = path__default.resolve(process.cwd(), source);
363
+ /**
364
+ * Generate agent configurations
365
+ */
366
+ async generateAgents(agents) {
367
+ const configs = [];
368
+ for (const agent of agents) {
369
+ const templatePath = join(this.templatesDir, "agents", agent.file);
370
+ if (!existsSync(templatePath)) {
371
+ console.warn(`Agent template not found: ${agent.file}`);
372
+ continue;
373
+ }
374
+ const content = readFileSync(templatePath, "utf-8");
375
+ const outputPath = join(this.outputDir, "agents", agent.file);
376
+ configs.push({
377
+ id: agent.id,
378
+ path: outputPath,
379
+ content
380
+ });
381
+ }
382
+ return configs;
383
+ }
384
+ /**
385
+ * Generate skill configurations
386
+ */
387
+ async generateSkills(skills) {
388
+ const configs = [];
389
+ for (const skill of skills) {
390
+ const templatePath = join(this.templatesDir, "skills", skill.file);
391
+ if (!existsSync(templatePath)) {
392
+ console.warn(`Skill template not found: ${skill.file}`);
393
+ continue;
394
+ }
395
+ const content = readFileSync(templatePath, "utf-8");
396
+ const outputPath = join(this.outputDir, "skills", skill.file);
397
+ configs.push({
398
+ id: skill.id,
399
+ path: outputPath,
400
+ content
401
+ });
402
+ }
403
+ return configs;
404
+ }
405
+ /**
406
+ * Write configurations to disk
407
+ */
408
+ async write(config) {
409
+ const agentsDir = join(this.outputDir, "agents");
410
+ const skillsDir = join(this.outputDir, "skills");
411
+ if (!existsSync(agentsDir)) {
412
+ mkdirSync(agentsDir, { recursive: true });
413
+ }
414
+ if (!existsSync(skillsDir)) {
415
+ mkdirSync(skillsDir, { recursive: true });
416
+ }
417
+ for (const agent of config.agents) {
418
+ const dir = join(agent.path, "..");
419
+ if (!existsSync(dir)) {
420
+ mkdirSync(dir, { recursive: true });
421
+ }
422
+ writeFileSync(agent.path, agent.content, "utf-8");
423
+ }
424
+ for (const skill of config.skills) {
425
+ const dir = join(skill.path, "..");
426
+ if (!existsSync(dir)) {
427
+ mkdirSync(dir, { recursive: true });
428
+ }
429
+ writeFileSync(skill.path, skill.content, "utf-8");
559
430
  }
560
- return {
561
- type: "local",
562
- absolutePath,
563
- originalPath: source
564
- };
565
431
  }
566
- if (/^[a-z]:[\\/]/i.test(source)) {
567
- return {
568
- type: "local",
569
- absolutePath: path__default.resolve(source),
570
- originalPath: source
571
- };
432
+ /**
433
+ * Generate summary
434
+ */
435
+ generateSummary(agents, skills) {
436
+ const lines = [];
437
+ lines.push("# Generated Configuration Summary\n");
438
+ lines.push(`Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
439
+ `);
440
+ lines.push("## Agents\n");
441
+ for (const agent of agents) {
442
+ lines.push(`- ${agent.id}: ${agent.path}`);
443
+ }
444
+ lines.push("\n## Skills\n");
445
+ for (const skill of skills) {
446
+ lines.push(`- ${skill.id}: ${skill.path}`);
447
+ }
448
+ lines.push("\n## Usage\n");
449
+ lines.push("1. Agents are automatically loaded by Claude Code");
450
+ lines.push("2. Skills can be triggered using their command triggers");
451
+ lines.push("3. Use `claude` command to start coding with AI assistance");
452
+ return lines.join("\n");
572
453
  }
573
- return null;
574
454
  }
575
- function parseNpmSource(source) {
576
- const npmPrefixMatch = source.match(/^npm:(.+)$/);
577
- const packageStr = npmPrefixMatch ? npmPrefixMatch[1] : source;
578
- const scopedMatch = packageStr.match(/^(@[^/]+)\/([^@]+)(?:@(.+))?$/);
579
- if (scopedMatch) {
580
- return {
581
- type: "npm",
582
- scope: scopedMatch[1],
583
- packageName: `${scopedMatch[1]}/${scopedMatch[2]}`,
584
- version: scopedMatch[3]
585
- };
586
- }
587
- const versionMatch = packageStr.match(/^([^@]+)@(.+)$/);
588
- if (versionMatch) {
589
- return {
590
- type: "npm",
591
- packageName: versionMatch[1],
592
- version: versionMatch[2]
593
- };
594
- }
595
- return {
596
- type: "npm",
597
- packageName: packageStr
598
- };
455
+ async function generateConfigs(selection) {
456
+ const generator = new ConfigGenerator();
457
+ return generator.generate(selection);
599
458
  }
600
- function buildGitHubRawUrl(info, filePath) {
601
- const ref = info.ref || "main";
602
- return `https://raw.githubusercontent.com/${info.owner}/${info.repo}/${ref}/${filePath}`;
459
+ async function writeConfigs(config) {
460
+ const generator = new ConfigGenerator();
461
+ await generator.write(config);
603
462
  }
604
463
 
605
- async function detectPluginType(sourceInfo) {
606
- const result = await detectPluginTypeWithConfidence(sourceInfo);
607
- return result.type;
608
- }
609
- async function detectPluginTypeWithConfidence(sourceInfo) {
610
- switch (sourceInfo.type) {
611
- case "github":
612
- return detectFromGitHub(sourceInfo);
613
- case "npm":
614
- return detectFromNpm(sourceInfo);
615
- case "local":
616
- return detectFromLocal(sourceInfo);
617
- default:
618
- return { type: "skill", confidence: "low", reason: "Unknown source type" };
464
+ const __filename = fileURLToPath(import.meta.url);
465
+ const __dirname = dirname(__filename);
466
+ class TemplateSelector {
467
+ templatesDir;
468
+ constructor(templatesDir) {
469
+ this.templatesDir = templatesDir || join(__dirname, "..", "..", "..", "templates");
470
+ }
471
+ /**
472
+ * Select templates based on project analysis
473
+ */
474
+ async select(analysis) {
475
+ const agents = await this.selectAgents(analysis);
476
+ const skills = await this.selectSkills(analysis);
477
+ return {
478
+ agents,
479
+ skills,
480
+ reasoning: this.generateReasoning(analysis, agents, skills)
481
+ };
619
482
  }
620
- }
621
- async function detectFromGitHub(info) {
622
- const repoNameResult = detectFromRepoName(info.repo);
623
- if (repoNameResult.confidence === "high") {
624
- return repoNameResult;
625
- }
626
- try {
627
- const packageJsonUrl = buildGitHubRawUrl(info, "package.json");
628
- const response = await fetch(packageJsonUrl);
629
- if (response.ok) {
630
- const packageJson = await response.json();
631
- const pkgResult = detectFromPackageJson(packageJson);
632
- if (pkgResult.confidence !== "low") {
633
- return pkgResult;
634
- }
483
+ /**
484
+ * Select agent templates
485
+ */
486
+ async selectAgents(analysis) {
487
+ const selectedAgents = [];
488
+ const agentIndex = this.loadAgentIndex();
489
+ switch (analysis.projectType) {
490
+ case "frontend":
491
+ selectedAgents.push(
492
+ ...this.findAgentsByCategory(agentIndex, "frontend")
493
+ );
494
+ break;
495
+ case "backend":
496
+ selectedAgents.push(
497
+ ...this.findAgentsByCategory(agentIndex, "backend")
498
+ );
499
+ break;
500
+ case "fullstack":
501
+ selectedAgents.push(
502
+ ...this.findAgentsByCategory(agentIndex, "fullstack")
503
+ );
504
+ selectedAgents.push(
505
+ ...this.findAgentsByCategory(agentIndex, "frontend").slice(0, 1),
506
+ ...this.findAgentsByCategory(agentIndex, "backend").slice(0, 1)
507
+ );
508
+ break;
509
+ case "cli":
510
+ case "library":
511
+ selectedAgents.push(
512
+ ...this.findAgentsByCategory(agentIndex, "backend")
513
+ );
514
+ break;
635
515
  }
636
- } catch {
637
- }
638
- const filePatterns = [
639
- { file: "SKILL.md", type: "skill" },
640
- { file: "skill.md", type: "skill" },
641
- { file: "AGENT.md", type: "agent" },
642
- { file: "agent.md", type: "agent" },
643
- { file: "mcp.json", type: "mcp" },
644
- { file: "hook.json", type: "hook" }
645
- ];
646
- for (const pattern of filePatterns) {
647
- try {
648
- const fileUrl = buildGitHubRawUrl(info, pattern.file);
649
- const response = await fetch(fileUrl, { method: "HEAD" });
650
- if (response.ok) {
651
- return {
652
- type: pattern.type,
653
- confidence: "high",
654
- reason: `Found ${pattern.file}`
655
- };
656
- }
657
- } catch {
516
+ if (analysis.hasTests) {
517
+ selectedAgents.push(
518
+ ...this.findAgentsByCategory(agentIndex, "testing")
519
+ );
658
520
  }
659
- }
660
- return repoNameResult.confidence !== "low" ? repoNameResult : { type: "skill", confidence: "low", reason: "Default type" };
661
- }
662
- async function detectFromNpm(info) {
663
- const nameResult = detectFromPackageName(info.packageName);
664
- if (nameResult.confidence === "high") {
665
- return nameResult;
666
- }
667
- try {
668
- const registryUrl = `https://registry.npmjs.org/${info.packageName}`;
669
- const response = await fetch(registryUrl);
670
- if (response.ok) {
671
- const data = await response.json();
672
- const latestVersion = data["dist-tags"]?.latest;
673
- if (latestVersion && data.versions?.[latestVersion]) {
674
- const packageJson = data.versions[latestVersion];
675
- const pkgResult = detectFromPackageJson(packageJson);
676
- if (pkgResult.confidence !== "low") {
677
- return pkgResult;
678
- }
679
- }
521
+ if (analysis.cicd && analysis.cicd.length > 0) {
522
+ selectedAgents.push(
523
+ ...this.findAgentsByCategory(agentIndex, "devops")
524
+ );
680
525
  }
681
- } catch {
682
- }
683
- return nameResult.confidence !== "low" ? nameResult : { type: "skill", confidence: "low", reason: "Default type" };
684
- }
685
- async function detectFromLocal(info) {
686
- const { absolutePath } = info;
687
- const dirName = path__default.basename(absolutePath);
688
- const dirResult = detectFromRepoName(dirName);
689
- if (dirResult.confidence === "high") {
690
- return dirResult;
691
- }
692
- try {
693
- const packageJsonPath = path__default.join(absolutePath, "package.json");
694
- const content = await fs.readFile(packageJsonPath, "utf-8");
695
- const packageJson = JSON.parse(content);
696
- const pkgResult = detectFromPackageJson(packageJson);
697
- if (pkgResult.confidence !== "low") {
698
- return pkgResult;
699
- }
700
- } catch {
701
- }
702
- const filePatterns = [
703
- { file: "SKILL.md", type: "skill" },
704
- { file: "skill.md", type: "skill" },
705
- { file: "AGENT.md", type: "agent" },
706
- { file: "agent.md", type: "agent" },
707
- { file: "mcp.json", type: "mcp" },
708
- { file: "hook.json", type: "hook" }
709
- ];
710
- for (const pattern of filePatterns) {
711
- try {
712
- const filePath = path__default.join(absolutePath, pattern.file);
713
- await fs.access(filePath);
714
- return {
715
- type: pattern.type,
716
- confidence: "high",
717
- reason: `Found ${pattern.file}`
718
- };
719
- } catch {
526
+ if (analysis.hasApi || analysis.hasDatabase) {
527
+ selectedAgents.push(
528
+ ...this.findAgentsByCategory(agentIndex, "security")
529
+ );
720
530
  }
721
- }
722
- try {
723
- const stat = await fs.stat(absolutePath);
724
- if (stat.isFile() && absolutePath.endsWith(".md")) {
725
- return {
726
- type: "skill",
727
- confidence: "high",
728
- reason: "Single markdown file"
729
- };
531
+ return this.deduplicateAndSort(selectedAgents);
532
+ }
533
+ /**
534
+ * Select skill templates
535
+ */
536
+ async selectSkills(analysis) {
537
+ const selectedSkills = [];
538
+ const skillIndex = this.loadSkillIndex();
539
+ selectedSkills.push(
540
+ ...this.findSkillsByCategory(skillIndex, "git")
541
+ );
542
+ selectedSkills.push(
543
+ ...this.findSkillsByCategory(skillIndex, "code-quality")
544
+ );
545
+ if (analysis.hasTests) {
546
+ selectedSkills.push(
547
+ ...this.findSkillsByIds(skillIndex, ["generate-tests"])
548
+ );
730
549
  }
731
- } catch {
732
- }
733
- return dirResult.confidence !== "low" ? dirResult : { type: "skill", confidence: "low", reason: "Default type" };
734
- }
735
- function detectFromRepoName(name) {
736
- const lowerName = name.toLowerCase();
737
- if (lowerName.includes("mcp-server") || lowerName.includes("mcp_server") || lowerName.startsWith("mcp-") || lowerName.endsWith("-mcp")) {
738
- return { type: "mcp", confidence: "high", reason: `Name contains MCP pattern: ${name}` };
739
- }
740
- if (lowerName.includes("agent") || lowerName.includes("-agent") || lowerName.endsWith("-agent")) {
741
- return { type: "agent", confidence: "medium", reason: `Name contains agent pattern: ${name}` };
742
- }
743
- if (lowerName.includes("hook") || lowerName.includes("-hook") || lowerName.endsWith("-hook")) {
744
- return { type: "hook", confidence: "medium", reason: `Name contains hook pattern: ${name}` };
745
- }
746
- if (lowerName.includes("skill") || lowerName.includes("-skill") || lowerName.endsWith("-skill")) {
747
- return { type: "skill", confidence: "medium", reason: `Name contains skill pattern: ${name}` };
748
- }
749
- return { type: "skill", confidence: "low", reason: "No pattern matched" };
750
- }
751
- function detectFromPackageName(packageName) {
752
- const lowerName = packageName.toLowerCase();
753
- if (lowerName.startsWith("@modelcontextprotocol/")) {
754
- return { type: "mcp", confidence: "high", reason: "MCP official scope" };
755
- }
756
- if (lowerName.includes("mcp-server") || lowerName.includes("mcp_server")) {
757
- return { type: "mcp", confidence: "high", reason: "MCP server pattern in name" };
758
- }
759
- return detectFromRepoName(packageName);
760
- }
761
- function detectFromPackageJson(packageJson) {
762
- if (packageJson.ccjk && typeof packageJson.ccjk === "object") {
763
- const ccjk = packageJson.ccjk;
764
- if (ccjk.type && typeof ccjk.type === "string") {
765
- const type = ccjk.type;
766
- if (["skill", "mcp", "agent", "hook"].includes(type)) {
767
- return { type, confidence: "high", reason: "Explicit ccjk.type field" };
768
- }
550
+ if (analysis.hasApi) {
551
+ selectedSkills.push(
552
+ ...this.findSkillsByIds(skillIndex, ["api-docs"])
553
+ );
769
554
  }
770
- }
771
- if (Array.isArray(packageJson.keywords)) {
772
- const keywords = packageJson.keywords;
773
- const keywordMap = {
774
- "mcp-server": "mcp",
775
- "mcp": "mcp",
776
- "model-context-protocol": "mcp",
777
- "ccjk-skill": "skill",
778
- "claude-skill": "skill",
779
- "ccjk-agent": "agent",
780
- "ccjk-hook": "hook"
781
- };
782
- for (const keyword of keywords) {
783
- const type = keywordMap[keyword.toLowerCase()];
784
- if (type) {
785
- return { type, confidence: "high", reason: `Keyword: ${keyword}` };
786
- }
555
+ if (analysis.hasDatabase) {
556
+ selectedSkills.push(
557
+ ...this.findSkillsByIds(skillIndex, ["database-migration"])
558
+ );
787
559
  }
788
- }
789
- if (packageJson.bin) {
790
- const binKeys = Object.keys(packageJson.bin);
791
- for (const key of binKeys) {
792
- if (key.includes("mcp") || key.includes("server")) {
793
- return { type: "mcp", confidence: "medium", reason: `Binary name: ${key}` };
794
- }
560
+ if (analysis.projectType === "frontend" || analysis.projectType === "fullstack") {
561
+ selectedSkills.push(
562
+ ...this.findSkillsByIds(skillIndex, ["performance-optimization"])
563
+ );
795
564
  }
796
- }
797
- if (typeof packageJson.main === "string") {
798
- const main = packageJson.main.toLowerCase();
799
- if (main.includes("server") || main.includes("mcp")) {
800
- return { type: "mcp", confidence: "low", reason: `Main file: ${packageJson.main}` };
565
+ if (analysis.packageJson) {
566
+ selectedSkills.push(
567
+ ...this.findSkillsByIds(skillIndex, ["dependency-update"])
568
+ );
801
569
  }
570
+ selectedSkills.push(
571
+ ...this.findSkillsByIds(skillIndex, ["refactor-code"])
572
+ );
573
+ return this.deduplicateAndSort(selectedSkills);
802
574
  }
803
- return { type: "skill", confidence: "low", reason: "No specific indicators" };
804
- }
805
-
806
- async function addCommand(source, options = {}) {
807
- const { force = false, dryRun = false, json = false, lang = "en" } = options;
808
- const i18n = getI18n(lang);
809
- const spinner = json ? null : ora(i18n.parsing).start();
810
- let sourceInfo;
811
- try {
812
- sourceInfo = parseSource(source);
813
- spinner?.succeed(i18n.parsed(sourceInfo.type));
814
- } catch (error) {
815
- spinner?.fail(i18n.parseFailed);
816
- const result = {
817
- success: false,
818
- source,
819
- sourceType: "local",
820
- pluginType: "skill",
821
- error: error instanceof Error ? error.message : String(error)
822
- };
823
- if (json) {
824
- console.log(JSON.stringify(result, null, 2));
825
- } else {
826
- console.error(a.red(`
827
- ${i18n.error}: ${result.error}
828
- `));
575
+ /**
576
+ * Load agent index
577
+ */
578
+ loadAgentIndex() {
579
+ try {
580
+ const indexPath = join(this.templatesDir, "agents", "index.json");
581
+ const content = readFileSync(indexPath, "utf-8");
582
+ return JSON.parse(content);
583
+ } catch {
584
+ return { templates: [] };
829
585
  }
830
- return result;
831
586
  }
832
- let pluginType = options.type;
833
- if (!pluginType) {
834
- spinner?.start(i18n.detectingType);
587
+ /**
588
+ * Load skill index
589
+ */
590
+ loadSkillIndex() {
835
591
  try {
836
- pluginType = await detectPluginType(sourceInfo);
837
- spinner?.succeed(i18n.detectedType(pluginType));
592
+ const indexPath = join(this.templatesDir, "skills", "index.json");
593
+ const content = readFileSync(indexPath, "utf-8");
594
+ return JSON.parse(content);
838
595
  } catch {
839
- spinner?.info(i18n.defaultType);
840
- pluginType = "skill";
596
+ return { templates: [] };
597
+ }
598
+ }
599
+ /**
600
+ * Find agents by category
601
+ */
602
+ findAgentsByCategory(index, category) {
603
+ return index.templates.filter((agent) => agent.category === category);
604
+ }
605
+ /**
606
+ * Find skills by category
607
+ */
608
+ findSkillsByCategory(index, category) {
609
+ return index.templates.filter((skill) => skill.category === category);
610
+ }
611
+ /**
612
+ * Find skills by IDs
613
+ */
614
+ findSkillsByIds(index, ids) {
615
+ return index.templates.filter((skill) => ids.includes(skill.id));
616
+ }
617
+ /**
618
+ * Deduplicate and sort templates by priority
619
+ */
620
+ deduplicateAndSort(templates) {
621
+ const seen = /* @__PURE__ */ new Set();
622
+ const unique = [];
623
+ for (const template of templates) {
624
+ if (!seen.has(template.id)) {
625
+ seen.add(template.id);
626
+ unique.push(template);
627
+ }
841
628
  }
629
+ return unique.sort((a, b) => b.priority - a.priority);
842
630
  }
843
- spinner?.start(dryRun ? i18n.previewInstall : i18n.installing);
844
- try {
845
- let result;
846
- switch (sourceInfo.type) {
847
- case "github":
848
- result = await installFromGitHub(sourceInfo, pluginType, { force, dryRun });
849
- break;
850
- case "npm":
851
- result = await installFromNpm(sourceInfo, pluginType, { force, dryRun });
852
- break;
853
- case "local":
854
- result = await installFromLocal(sourceInfo, pluginType, { force, dryRun });
855
- break;
856
- default:
857
- throw new Error(`Unsupported source type: ${sourceInfo.type}`);
631
+ /**
632
+ * Generate reasoning for template selection
633
+ */
634
+ generateReasoning(analysis, agents, skills) {
635
+ const reasons = [];
636
+ reasons.push(`Detected ${analysis.projectType} project`);
637
+ if (analysis.frameworks.length > 0) {
638
+ reasons.push(`Using frameworks: ${analysis.frameworks.join(", ")}`);
858
639
  }
859
- if (result.success) {
860
- spinner?.succeed(dryRun ? i18n.previewSuccess : i18n.installSuccess);
861
- } else {
862
- spinner?.fail(i18n.installFailed);
863
- }
864
- if (json) {
865
- console.log(JSON.stringify(result, null, 2));
866
- } else if (result.success && !dryRun) {
867
- printSuccessMessage(result, i18n);
868
- } else if (result.success && dryRun) {
869
- printPreviewMessage(result, i18n);
870
- } else {
871
- console.error(a.red(`
872
- ${i18n.error}: ${result.error}
873
- `));
874
- }
875
- return result;
876
- } catch (error) {
877
- spinner?.fail(i18n.installFailed);
878
- const result = {
879
- success: false,
880
- source,
881
- sourceType: sourceInfo.type,
882
- pluginType,
883
- error: error instanceof Error ? error.message : String(error)
884
- };
885
- if (json) {
886
- console.log(JSON.stringify(result, null, 2));
887
- } else {
888
- console.error(a.red(`
889
- ${i18n.error}: ${result.error}
890
- `));
640
+ reasons.push(`Tech stack: ${analysis.techStack.languages.join(", ")} on ${analysis.techStack.runtime}`);
641
+ if (analysis.hasTests) {
642
+ reasons.push("Tests detected - including test engineer and test generation");
643
+ }
644
+ if (analysis.hasDatabase) {
645
+ reasons.push("Database detected - including migration skills");
646
+ }
647
+ if (analysis.hasApi) {
648
+ reasons.push("API endpoints detected - including API documentation");
891
649
  }
892
- return result;
650
+ if (analysis.cicd && analysis.cicd.length > 0) {
651
+ reasons.push(`CI/CD detected (${analysis.cicd.join(", ")}) - including DevOps engineer`);
652
+ }
653
+ reasons.push(`
654
+ Selected ${agents.length} agents and ${skills.length} skills`);
655
+ return reasons.join("\n");
893
656
  }
894
657
  }
895
- function printSuccessMessage(result, i18n) {
896
- console.log();
897
- console.log(a.green.bold(`\u2713 ${i18n.installed}`));
898
- console.log();
899
- console.log(` ${a.gray(i18n.name)}: ${result.details?.name || result.source}`);
900
- if (result.details?.version) {
901
- console.log(` ${a.gray(i18n.version)}: ${result.details.version}`);
902
- }
903
- console.log(` ${a.gray(i18n.type)}: ${result.pluginType}`);
904
- console.log(` ${a.gray(i18n.path)}: ${result.installedPath}`);
905
- console.log();
906
- console.log(a.cyan(i18n.nextSteps));
907
- switch (result.pluginType) {
908
- case "skill":
909
- console.log(` ${a.gray("\u2022")} ${i18n.skillHint}`);
910
- break;
911
- case "mcp":
912
- console.log(` ${a.gray("\u2022")} ${i18n.mcpHint}`);
913
- break;
914
- case "agent":
915
- console.log(` ${a.gray("\u2022")} ${i18n.agentHint}`);
916
- break;
917
- case "hook":
918
- console.log(` ${a.gray("\u2022")} ${i18n.hookHint}`);
919
- break;
920
- }
921
- console.log();
922
- }
923
- function printPreviewMessage(result, i18n) {
924
- console.log();
925
- console.log(a.yellow.bold(`\u26A1 ${i18n.preview}`));
926
- console.log();
927
- console.log(` ${a.gray(i18n.source)}: ${result.source}`);
928
- console.log(` ${a.gray(i18n.type)}: ${result.pluginType}`);
929
- console.log(` ${a.gray(i18n.target)}: ${result.installedPath}`);
930
- if (result.details?.files?.length) {
931
- console.log(` ${a.gray(i18n.files)}:`);
932
- for (const file of result.details.files.slice(0, 5)) {
933
- console.log(` ${a.gray("\u2022")} ${file}`);
934
- }
935
- if (result.details.files.length > 5) {
936
- console.log(` ${a.gray("...")} ${i18n.andMore(result.details.files.length - 5)}`);
937
- }
938
- }
939
- console.log();
940
- console.log(a.gray(i18n.dryRunNote));
941
- console.log();
658
+ async function selectTemplates(analysis) {
659
+ const selector = new TemplateSelector();
660
+ return selector.select(analysis);
942
661
  }
943
- function getI18n(lang) {
944
- const texts = {
945
- "zh-CN": {
946
- parsing: "\u89E3\u6790\u6765\u6E90...",
947
- parsed: (type) => `\u6765\u6E90\u7C7B\u578B: ${type}`,
948
- parseFailed: "\u89E3\u6790\u6765\u6E90\u5931\u8D25",
949
- detectingType: "\u68C0\u6D4B\u63D2\u4EF6\u7C7B\u578B...",
950
- detectedType: (type) => `\u68C0\u6D4B\u5230\u7C7B\u578B: ${type}`,
951
- defaultType: "\u4F7F\u7528\u9ED8\u8BA4\u7C7B\u578B: skill",
952
- installing: "\u5B89\u88C5\u4E2D...",
953
- previewInstall: "\u9884\u89C8\u5B89\u88C5...",
954
- installSuccess: "\u5B89\u88C5\u6210\u529F",
955
- previewSuccess: "\u9884\u89C8\u5B8C\u6210",
956
- installFailed: "\u5B89\u88C5\u5931\u8D25",
957
- error: "\u9519\u8BEF",
958
- installed: "\u63D2\u4EF6\u5DF2\u5B89\u88C5",
959
- preview: "\u5B89\u88C5\u9884\u89C8 (dry-run)",
960
- name: "\u540D\u79F0",
961
- version: "\u7248\u672C",
962
- type: "\u7C7B\u578B",
963
- path: "\u8DEF\u5F84",
964
- source: "\u6765\u6E90",
965
- target: "\u76EE\u6807",
966
- files: "\u6587\u4EF6",
967
- nextSteps: "\u4E0B\u4E00\u6B65:",
968
- skillHint: "\u4F7F\u7528 /skill-name \u5728 Claude Code \u4E2D\u8C03\u7528",
969
- mcpHint: "\u8FD0\u884C ccjk mcp status \u67E5\u770B\u72B6\u6001",
970
- agentHint: "\u4F7F\u7528 ccjk agent list \u67E5\u770B\u5DF2\u5B89\u88C5\u7684 agent",
971
- hookHint: "\u8FD0\u884C ccjk hooks list \u67E5\u770B\u5DF2\u5B89\u88C5\u7684 hook",
972
- dryRunNote: "\u8FD9\u662F\u9884\u89C8\u6A21\u5F0F\uFF0C\u672A\u5B9E\u9645\u5B89\u88C5\u3002\u79FB\u9664 --dry-run \u6267\u884C\u5B89\u88C5\u3002",
973
- andMore: (n) => `\u8FD8\u6709 ${n} \u4E2A\u6587\u4EF6`
974
- },
975
- "en": {
976
- parsing: "Parsing source...",
977
- parsed: (type) => `Source type: ${type}`,
978
- parseFailed: "Failed to parse source",
979
- detectingType: "Detecting plugin type...",
980
- detectedType: (type) => `Detected type: ${type}`,
981
- defaultType: "Using default type: skill",
982
- installing: "Installing...",
983
- previewInstall: "Previewing installation...",
984
- installSuccess: "Installation successful",
985
- previewSuccess: "Preview complete",
986
- installFailed: "Installation failed",
987
- error: "Error",
988
- installed: "Plugin installed",
989
- preview: "Installation preview (dry-run)",
990
- name: "Name",
991
- version: "Version",
992
- type: "Type",
993
- path: "Path",
994
- source: "Source",
995
- target: "Target",
996
- files: "Files",
997
- nextSteps: "Next steps:",
998
- skillHint: "Use /skill-name in Claude Code to invoke",
999
- mcpHint: "Run ccjk mcp status to check status",
1000
- agentHint: "Use ccjk agent list to see installed agents",
1001
- hookHint: "Run ccjk hooks list to see installed hooks",
1002
- dryRunNote: "This is preview mode, nothing was installed. Remove --dry-run to install.",
1003
- andMore: (n) => `and ${n} more files`
1004
- }
662
+
663
+ async function smartGenerate(projectRoot) {
664
+ const analysis = await analyzeProject(projectRoot);
665
+ const selection = await selectTemplates(analysis);
666
+ const config = await generateConfigs(selection);
667
+ return {
668
+ analysis,
669
+ selection,
670
+ config
1005
671
  };
1006
- return texts[lang];
672
+ }
673
+ async function smartGenerateAndInstall(projectRoot) {
674
+ const result = await smartGenerate(projectRoot);
675
+ await writeConfigs(result.config);
676
+ return result;
1007
677
  }
1008
678
 
1009
- export { addCommand, parseSource };
679
+ export { ConfigGenerator, ProjectAnalyzer, TemplateSelector, analyzeProject, generateConfigs, selectTemplates, smartGenerate, smartGenerateAndInstall, writeConfigs };