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.
- package/dist/chunks/agent-teams.mjs +3 -3
- package/dist/chunks/agent.mjs +2 -2
- package/dist/chunks/agents.mjs +3 -3
- package/dist/chunks/api-cli.mjs +5 -5
- package/dist/chunks/api-providers.mjs +1 -1
- package/dist/chunks/api.mjs +4 -4
- package/dist/chunks/auto-bootstrap.mjs +1 -1
- package/dist/chunks/auto-fix.mjs +17 -5
- package/dist/chunks/auto-fixer.mjs +3 -3
- package/dist/chunks/auto-init.mjs +3 -3
- package/dist/chunks/auto-updater.mjs +7 -7
- package/dist/chunks/banner.mjs +2 -2
- package/dist/chunks/boost.mjs +5 -5
- package/dist/chunks/ccjk-agents.mjs +2 -2
- package/dist/chunks/ccjk-all.mjs +6 -6
- package/dist/chunks/ccjk-config.mjs +2 -2
- package/dist/chunks/ccjk-hooks.mjs +3 -3
- package/dist/chunks/ccjk-mcp.mjs +4 -4
- package/dist/chunks/ccjk-setup.mjs +4 -4
- package/dist/chunks/ccjk-skills.mjs +4 -4
- package/dist/chunks/ccr.mjs +16 -16
- package/dist/chunks/ccu.mjs +2 -2
- package/dist/chunks/check-updates.mjs +8 -8
- package/dist/chunks/claude-code-config-manager.mjs +5 -5
- package/dist/chunks/claude-code-incremental-manager.mjs +6 -6
- package/dist/chunks/claude-config.mjs +2 -2
- package/dist/chunks/claude-wrapper.mjs +1 -1
- package/dist/chunks/cli-hook.mjs +5 -5
- package/dist/chunks/codex-config-switch.mjs +6 -6
- package/dist/chunks/codex-provider-manager.mjs +7 -7
- package/dist/chunks/codex-uninstaller.mjs +2 -2
- package/dist/chunks/codex.mjs +7 -7
- package/dist/chunks/commands.mjs +2 -2
- package/dist/chunks/commands2.mjs +2 -2
- package/dist/chunks/commit.mjs +3 -3
- package/dist/chunks/completion.mjs +2 -2
- package/dist/chunks/config-consolidator.mjs +2 -2
- package/dist/chunks/config-switch.mjs +8 -8
- package/dist/chunks/config.mjs +20 -9
- package/dist/chunks/config2.mjs +4 -4
- package/dist/chunks/config3.mjs +3 -3
- package/dist/chunks/constants.mjs +1 -1
- package/dist/chunks/context.mjs +3 -3
- package/dist/chunks/dashboard.mjs +2 -2
- package/dist/chunks/doctor.mjs +4 -4
- package/dist/chunks/evolution.mjs +2 -2
- package/dist/chunks/features.mjs +8 -8
- package/dist/chunks/help.mjs +1 -1
- package/dist/chunks/impact.mjs +1 -1
- package/dist/chunks/index.mjs +18 -19
- package/dist/chunks/index10.mjs +5145 -528
- package/dist/chunks/index11.mjs +1061 -569
- package/dist/chunks/index12.mjs +193 -1146
- package/dist/chunks/index13.mjs +607 -937
- package/dist/chunks/index14.mjs +951 -136
- package/dist/chunks/index15.mjs +185 -209
- package/dist/chunks/index2.mjs +7623 -12
- package/dist/chunks/index3.mjs +160 -19081
- package/dist/chunks/index4.mjs +19 -18
- package/dist/chunks/index5.mjs +12 -7623
- package/dist/chunks/index6.mjs +19077 -156
- package/dist/chunks/index7.mjs +565 -1551
- package/dist/chunks/index8.mjs +1535 -5166
- package/dist/chunks/init.mjs +1107 -1101
- package/dist/chunks/installer.mjs +5 -5
- package/dist/chunks/installer2.mjs +2 -2
- package/dist/chunks/interview.mjs +4 -4
- package/dist/chunks/manager.mjs +1 -1
- package/dist/chunks/marketplace.mjs +3 -3
- package/dist/chunks/mcp-cli.mjs +9 -9
- package/dist/chunks/mcp.mjs +10 -10
- package/dist/chunks/memory-check.mjs +2 -2
- package/dist/chunks/memory-paths.mjs +2 -2
- package/dist/chunks/memory-sync.mjs +2 -2
- package/dist/chunks/memory.mjs +4 -4
- package/dist/chunks/metrics-display.mjs +1 -1
- package/dist/chunks/migrator.mjs +1 -1
- package/dist/chunks/monitor.mjs +2 -2
- package/dist/chunks/notification.mjs +4 -4
- package/dist/chunks/onboarding.mjs +4 -4
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/paradigm.mjs +1 -1
- package/dist/chunks/permission-manager.mjs +2 -2
- package/dist/chunks/permissions.mjs +3 -3
- package/dist/chunks/persistence-manager.mjs +3 -3
- package/dist/chunks/platform.mjs +1 -1
- package/dist/chunks/plugin.mjs +2 -2
- package/dist/chunks/prompts.mjs +5 -5
- package/dist/chunks/providers.mjs +2 -2
- package/dist/chunks/quick-actions.mjs +3 -3
- package/dist/chunks/quick-provider.mjs +4 -4
- package/dist/chunks/quick-setup.mjs +11 -11
- package/dist/chunks/remote.mjs +5 -5
- package/dist/chunks/session-manager.mjs +1 -1
- package/dist/chunks/session.mjs +2 -2
- package/dist/chunks/sessions.mjs +1 -1
- package/dist/chunks/silent-updater.mjs +1 -1
- package/dist/chunks/simple-config.mjs +1 -1
- package/dist/chunks/skill2.mjs +3 -3
- package/dist/chunks/skills-sync.mjs +5 -5
- package/dist/chunks/skills.mjs +3 -3
- package/dist/chunks/slash-commands.mjs +3 -3
- package/dist/chunks/startup.mjs +1 -1
- package/dist/chunks/stats.mjs +2 -2
- package/dist/chunks/status.mjs +3 -3
- package/dist/chunks/team.mjs +3 -3
- package/dist/chunks/thinking.mjs +4 -4
- package/dist/chunks/trace.mjs +2 -2
- package/dist/chunks/uninstall.mjs +8 -8
- package/dist/chunks/update.mjs +12 -12
- package/dist/chunks/upgrade-manager.mjs +3 -3
- package/dist/chunks/upgrade.mjs +1 -1
- package/dist/chunks/version-checker.mjs +4 -4
- package/dist/chunks/vim.mjs +3 -3
- package/dist/chunks/workflows.mjs +1 -1
- package/dist/chunks/wsl.mjs +1 -1
- package/dist/chunks/zero-config.mjs +3 -3
- package/dist/cli.mjs +195 -185
- package/dist/index.mjs +8 -8
- package/dist/shared/{ccjk.BJMRY2Ra.mjs → ccjk.8oaxX4iR.mjs} +1 -1
- package/dist/shared/{ccjk.8VhZ_dEO.mjs → ccjk.Bq8TqZG_.mjs} +1 -1
- package/dist/shared/{ccjk.Bb9Mpi2O.mjs → ccjk.C1hANZTu.mjs} +1 -1
- package/dist/shared/{ccjk.KpFl2RDA.mjs → ccjk.C4m4ypdk.mjs} +5 -5
- package/dist/shared/{ccjk.I6IuYdc_.mjs → ccjk.CQd7SKNC.mjs} +2 -2
- package/dist/shared/{ccjk.Cjj8SVrn.mjs → ccjk.CiKtBUW_.mjs} +1 -1
- package/dist/shared/{ccjk.DKBI0lAR.mjs → ccjk.Cv13QsGp.mjs} +3 -3
- package/dist/shared/{ccjk.D8BqKRwH.mjs → ccjk.CwsecBYP.mjs} +4 -4
- package/dist/shared/{ccjk.DCw2WnZU.mjs → ccjk.D75wivnp.mjs} +1 -1
- package/dist/shared/{ccjk.DZ4ehAHg.mjs → ccjk.DDq2hqA5.mjs} +24 -4
- package/dist/shared/{ccjk.BIxuVL3_.mjs → ccjk.DGllfVCZ.mjs} +3 -3
- package/dist/shared/{ccjk.BWFpnOr3.mjs → ccjk.DZ2LLOa-.mjs} +1 -1
- package/dist/shared/{ccjk.eDO9cM5o.mjs → ccjk.DbigonEQ.mjs} +1 -1
- package/dist/shared/{ccjk.CIObNAcl.mjs → ccjk.Dhu8ia5S.mjs} +1 -1
- package/dist/shared/{ccjk.CHUEFqmw.mjs → ccjk.DlTXS9rP.mjs} +1 -1
- package/dist/shared/{ccjk.CxAFICpL.mjs → ccjk.DvAP4XfP.mjs} +4 -4
- package/dist/shared/{ccjk.BOu1yav7.mjs → ccjk.KPLeMP-o.mjs} +1 -1
- package/dist/shared/{ccjk.D8ZLYSZZ.mjs → ccjk.vO3d1ABk.mjs} +1 -1
- package/dist/templates/claude-code/common/settings.json +0 -1
- package/package.json +68 -65
- package/templates/claude-code/common/settings.json +0 -1
package/dist/chunks/index13.mjs
CHANGED
|
@@ -1,1009 +1,679 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
50
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
51
|
+
return JSON.parse(content);
|
|
173
52
|
} catch {
|
|
174
|
-
|
|
53
|
+
return null;
|
|
175
54
|
}
|
|
176
55
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
233
|
-
|
|
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 (
|
|
250
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
361
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
106
|
+
return "npm";
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Detect project type
|
|
110
|
+
*/
|
|
111
|
+
detectProjectType(techStack, packageJson) {
|
|
112
|
+
if (!packageJson) {
|
|
113
|
+
return "unknown";
|
|
380
114
|
}
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
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
|
-
|
|
406
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
-
|
|
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
|
-
|
|
503
|
-
|
|
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
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
)
|
|
528
|
-
|
|
529
|
-
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
ref: shortMatch[3],
|
|
545
|
-
originalUrl: source
|
|
358
|
+
agents: agentConfigs,
|
|
359
|
+
skills: skillConfigs,
|
|
360
|
+
summary: this.generateSummary(agentConfigs, skillConfigs)
|
|
546
361
|
};
|
|
547
362
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
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
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
|
576
|
-
const
|
|
577
|
-
|
|
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
|
|
601
|
-
const
|
|
602
|
-
|
|
459
|
+
async function writeConfigs(config) {
|
|
460
|
+
const generator = new ConfigGenerator();
|
|
461
|
+
await generator.write(config);
|
|
603
462
|
}
|
|
604
463
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
-
|
|
682
|
-
|
|
683
|
-
|
|
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
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
-
|
|
798
|
-
|
|
799
|
-
|
|
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
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
833
|
-
|
|
834
|
-
|
|
587
|
+
/**
|
|
588
|
+
* Load skill index
|
|
589
|
+
*/
|
|
590
|
+
loadSkillIndex() {
|
|
835
591
|
try {
|
|
836
|
-
|
|
837
|
-
|
|
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
|
-
|
|
840
|
-
|
|
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
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
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
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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
|
-
|
|
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
|
|
896
|
-
|
|
897
|
-
|
|
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
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
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
|
-
|
|
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 {
|
|
679
|
+
export { ConfigGenerator, ProjectAnalyzer, TemplateSelector, analyzeProject, generateConfigs, selectTemplates, smartGenerate, smartGenerateAndInstall, writeConfigs };
|