ccjk 2.0.20 → 2.2.1
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/README.md +601 -35
- package/README.zh-CN.md +651 -0
- package/dist/chunks/api.mjs +100 -0
- package/dist/chunks/auto-updater.mjs +252 -0
- package/dist/chunks/ccjk-config.mjs +261 -0
- package/dist/chunks/ccr.mjs +77 -0
- package/dist/chunks/ccu.mjs +36 -0
- package/dist/chunks/check-updates.mjs +93 -0
- package/dist/chunks/claude-code-config-manager.mjs +28 -21
- package/dist/chunks/claude-code-incremental-manager.mjs +26 -18
- package/dist/chunks/claude-config.mjs +228 -0
- package/dist/chunks/codex.mjs +2134 -0
- package/dist/chunks/commands.mjs +2 -15
- package/dist/chunks/commit.mjs +119 -0
- package/dist/chunks/config-consolidator.mjs +281 -0
- package/dist/chunks/config-switch.mjs +302 -0
- package/dist/chunks/constants.mjs +156 -0
- package/dist/chunks/doctor.mjs +708 -0
- package/dist/chunks/features.mjs +35 -640
- package/dist/chunks/features2.mjs +661 -0
- package/dist/chunks/fs-operations.mjs +180 -0
- package/dist/chunks/index.mjs +3082 -0
- package/dist/chunks/index2.mjs +145 -0
- package/dist/chunks/init.mjs +2468 -0
- package/dist/chunks/interview.mjs +2916 -0
- package/dist/chunks/json-config.mjs +59 -0
- package/dist/chunks/marketplace.mjs +258 -0
- package/dist/chunks/mcp-doctor.mjs +160 -0
- package/dist/chunks/mcp-market.mjs +475 -0
- package/dist/chunks/mcp-performance.mjs +110 -0
- package/dist/chunks/mcp-profile.mjs +220 -0
- package/dist/chunks/mcp-release.mjs +138 -0
- package/dist/chunks/menu.mjs +3599 -0
- package/dist/chunks/notification.mjs +2336 -0
- package/dist/chunks/onboarding.mjs +711 -0
- package/dist/chunks/package.mjs +4 -0
- package/dist/chunks/permission-manager.mjs +210 -0
- package/dist/chunks/platform.mjs +321 -0
- package/dist/chunks/prompts.mjs +228 -0
- package/dist/chunks/session.mjs +355 -0
- package/dist/chunks/shencha.mjs +320 -0
- package/dist/chunks/skills-sync.mjs +4 -13
- package/dist/chunks/team.mjs +51 -0
- package/dist/chunks/tools.mjs +169 -0
- package/dist/chunks/uninstall.mjs +784 -0
- package/dist/chunks/update.mjs +104 -0
- package/dist/chunks/upgrade-manager.mjs +197 -0
- package/dist/chunks/workflows.mjs +100 -0
- package/dist/cli.mjs +581 -15348
- package/dist/i18n/locales/zh-CN/cli.json +1 -1
- package/dist/i18n/locales/zh-CN/common.json +1 -1
- package/dist/index.mjs +43 -2062
- package/dist/shared/ccjk.-FoZ3zat.mjs +761 -0
- package/dist/shared/ccjk.B7169qud.mjs +25 -0
- package/dist/shared/ccjk.BhKlRJ0h.mjs +114 -0
- package/dist/shared/ccjk.Bi-m3LKY.mjs +357 -0
- package/dist/shared/ccjk.COdsoe-Y.mjs +64 -0
- package/dist/shared/ccjk.CUdzQluX.mjs +46 -0
- package/dist/shared/ccjk.Cy-RH2qV.mjs +506 -0
- package/dist/shared/ccjk.DGjQxTq_.mjs +34 -0
- package/dist/shared/ccjk.DJM5aVQJ.mjs +586 -0
- package/dist/shared/ccjk.DhBeLRzf.mjs +28 -0
- package/dist/shared/ccjk.DwDtZ5cK.mjs +266 -0
- package/dist/shared/ccjk.n_AtlHzB.mjs +186 -0
- package/dist/shared/ccjk.qYAnUMuy.mjs +749 -0
- package/package.json +29 -25
- package/dist/chunks/codex-config-switch.mjs +0 -429
- package/dist/chunks/codex-provider-manager.mjs +0 -234
- package/dist/chunks/codex-uninstaller.mjs +0 -406
- package/dist/chunks/plugin-recommendation.mjs +0 -575
- package/dist/chunks/simple-config.mjs +0 -10950
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import ansis from 'ansis';
|
|
4
|
+
import { resolve } from 'pathe';
|
|
5
|
+
|
|
6
|
+
class LLMScanner {
|
|
7
|
+
llmClient;
|
|
8
|
+
constructor(llmClient) {
|
|
9
|
+
this.llmClient = llmClient;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* LLM autonomously discovers what to scan
|
|
13
|
+
*/
|
|
14
|
+
async discoverScanTargets(context) {
|
|
15
|
+
const projectName = context.projectName || "unknown";
|
|
16
|
+
const rootDir = context.rootDir || context.rootPath;
|
|
17
|
+
const languages = context.languages || [];
|
|
18
|
+
const frameworks = context.frameworks || [context.framework].filter(Boolean);
|
|
19
|
+
const fileStructure = context.fileStructure || context.sourceDirs;
|
|
20
|
+
const prompt = `Analyze this project and identify scan targets:
|
|
21
|
+
|
|
22
|
+
Project: ${projectName}
|
|
23
|
+
Root: ${rootDir}
|
|
24
|
+
Languages: ${languages.join(", ")}
|
|
25
|
+
Frameworks: ${frameworks.join(", ")}
|
|
26
|
+
|
|
27
|
+
File structure summary:
|
|
28
|
+
${fileStructure.slice(0, 50).join("\n")}
|
|
29
|
+
${fileStructure.length > 50 ? `... and ${fileStructure.length - 50} more files` : ""}
|
|
30
|
+
|
|
31
|
+
Identify:
|
|
32
|
+
1. Critical code paths that need security auditing
|
|
33
|
+
2. Files with potential performance issues
|
|
34
|
+
3. Code that might have quality problems
|
|
35
|
+
4. Configuration files that need validation
|
|
36
|
+
|
|
37
|
+
For each target, provide:
|
|
38
|
+
- path: file or directory path
|
|
39
|
+
- type: 'file' | 'directory' | 'pattern'
|
|
40
|
+
- priority: 'critical' | 'high' | 'medium' | 'low'
|
|
41
|
+
- reason: why this should be scanned
|
|
42
|
+
|
|
43
|
+
Return as JSON array.`;
|
|
44
|
+
const response = await this.llmClient.complete(prompt);
|
|
45
|
+
return this.parseTargets(response);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* LLM decides how to scan each target
|
|
49
|
+
*/
|
|
50
|
+
async determineScanStrategy(target) {
|
|
51
|
+
const prompt = `Determine the best scanning strategy for:
|
|
52
|
+
|
|
53
|
+
Target: ${target.path}
|
|
54
|
+
Type: ${target.type}
|
|
55
|
+
Priority: ${target.priority}
|
|
56
|
+
Reason: ${target.reason}
|
|
57
|
+
|
|
58
|
+
What should we look for? Consider:
|
|
59
|
+
1. Security vulnerabilities (injection, XSS, auth issues)
|
|
60
|
+
2. Performance problems (N+1 queries, memory leaks, slow algorithms)
|
|
61
|
+
3. Code quality (complexity, duplication, dead code)
|
|
62
|
+
4. Best practices violations
|
|
63
|
+
5. Potential bugs and edge cases
|
|
64
|
+
|
|
65
|
+
Return a scanning strategy as JSON:
|
|
66
|
+
{
|
|
67
|
+
"focusAreas": ["security", "performance", "quality"],
|
|
68
|
+
"patterns": ["regex patterns to search for"],
|
|
69
|
+
"checks": ["specific things to verify"],
|
|
70
|
+
"depth": "shallow" | "deep" | "comprehensive"
|
|
71
|
+
}`;
|
|
72
|
+
const response = await this.llmClient.complete(prompt);
|
|
73
|
+
return this.parseStrategy(response);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* LLM executes scan with dynamic approach
|
|
77
|
+
*/
|
|
78
|
+
async executeScan(target, strategy, fileContent) {
|
|
79
|
+
const focusAreas = strategy.focusAreas || [];
|
|
80
|
+
const prompt = `Scan this code for issues:
|
|
81
|
+
|
|
82
|
+
File: ${target.path}
|
|
83
|
+
Focus Areas: ${focusAreas.join(", ")}
|
|
84
|
+
Checks: ${strategy.checks.join(", ")}
|
|
85
|
+
Depth: ${strategy.depth}
|
|
86
|
+
|
|
87
|
+
Code:
|
|
88
|
+
\`\`\`
|
|
89
|
+
${fileContent.slice(0, 1e4)}
|
|
90
|
+
${fileContent.length > 1e4 ? "\n... [truncated]" : ""}
|
|
91
|
+
\`\`\`
|
|
92
|
+
|
|
93
|
+
Analyze and identify all issues. For each issue provide:
|
|
94
|
+
{
|
|
95
|
+
"id": "unique-id",
|
|
96
|
+
"type": "security" | "performance" | "quality" | "bug" | "style",
|
|
97
|
+
"severity": "critical" | "high" | "medium" | "low",
|
|
98
|
+
"title": "brief title",
|
|
99
|
+
"description": "detailed description",
|
|
100
|
+
"location": {
|
|
101
|
+
"file": "path",
|
|
102
|
+
"startLine": number,
|
|
103
|
+
"endLine": number,
|
|
104
|
+
"snippet": "code snippet"
|
|
105
|
+
},
|
|
106
|
+
"suggestion": "how to fix",
|
|
107
|
+
"confidence": 0.0 to 1.0
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
Return as JSON array of issues. Be thorough but avoid false positives.`;
|
|
111
|
+
const response = await this.llmClient.complete(prompt);
|
|
112
|
+
const issues = this.parseIssues(response, target.path);
|
|
113
|
+
return {
|
|
114
|
+
target,
|
|
115
|
+
strategy,
|
|
116
|
+
issues,
|
|
117
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
118
|
+
scannedAt: /* @__PURE__ */ new Date(),
|
|
119
|
+
duration: 0,
|
|
120
|
+
metrics: { linesScanned: fileContent.split("\n").length },
|
|
121
|
+
notes: ""
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Batch scan multiple targets
|
|
126
|
+
*/
|
|
127
|
+
async scanAll(context, readFile) {
|
|
128
|
+
const targets = await this.discoverScanTargets(context);
|
|
129
|
+
const results = [];
|
|
130
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
131
|
+
targets.sort((a, b) => (priorityOrder[a.priority] || 2) - (priorityOrder[b.priority] || 2));
|
|
132
|
+
for (const target of targets) {
|
|
133
|
+
if (target.type === "file") {
|
|
134
|
+
try {
|
|
135
|
+
const strategy = await this.determineScanStrategy(target);
|
|
136
|
+
const content = await readFile(target.path);
|
|
137
|
+
const result = await this.executeScan(target, strategy, content);
|
|
138
|
+
results.push(result);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.warn(`Could not scan ${target.path}: ${error}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return results;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Parse targets from LLM response
|
|
148
|
+
*/
|
|
149
|
+
parseTargets(response) {
|
|
150
|
+
try {
|
|
151
|
+
const json = this.extractJson(response);
|
|
152
|
+
return json.map((t) => ({
|
|
153
|
+
path: t.path,
|
|
154
|
+
type: t.type || "file",
|
|
155
|
+
priority: t.priority || 5,
|
|
156
|
+
complexity: t.complexity || "medium",
|
|
157
|
+
reason: t.reason || "",
|
|
158
|
+
tags: t.tags || []
|
|
159
|
+
}));
|
|
160
|
+
} catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Parse strategy from LLM response
|
|
166
|
+
*/
|
|
167
|
+
parseStrategy(response) {
|
|
168
|
+
try {
|
|
169
|
+
const json = this.extractJson(response);
|
|
170
|
+
return {
|
|
171
|
+
type: json.type || "comprehensive",
|
|
172
|
+
focusAreas: json.focusAreas || ["security", "quality"],
|
|
173
|
+
checks: json.checks || [],
|
|
174
|
+
depth: json.depth || "deep",
|
|
175
|
+
contextFiles: json.contextFiles || [],
|
|
176
|
+
model: json.model || "sonnet"
|
|
177
|
+
};
|
|
178
|
+
} catch {
|
|
179
|
+
return {
|
|
180
|
+
type: "comprehensive",
|
|
181
|
+
focusAreas: ["security", "quality"],
|
|
182
|
+
checks: [],
|
|
183
|
+
depth: "deep",
|
|
184
|
+
contextFiles: [],
|
|
185
|
+
model: "sonnet"
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Parse issues from LLM response
|
|
191
|
+
*/
|
|
192
|
+
parseIssues(response, filePath) {
|
|
193
|
+
try {
|
|
194
|
+
const json = this.extractJson(response);
|
|
195
|
+
if (!Array.isArray(json))
|
|
196
|
+
return [];
|
|
197
|
+
return json.map((i, idx) => {
|
|
198
|
+
const issueType = this.mapIssueType(i.type);
|
|
199
|
+
const category = this.mapCategory(issueType);
|
|
200
|
+
return {
|
|
201
|
+
id: i.id || `issue-${idx}`,
|
|
202
|
+
type: issueType,
|
|
203
|
+
category,
|
|
204
|
+
severity: this.mapSeverity(i.severity),
|
|
205
|
+
title: i.title || "Untitled issue",
|
|
206
|
+
description: i.description || "",
|
|
207
|
+
file: i.location?.file || filePath,
|
|
208
|
+
line: i.location?.startLine || 1,
|
|
209
|
+
column: i.location?.column,
|
|
210
|
+
snippet: i.location?.snippet || "",
|
|
211
|
+
location: {
|
|
212
|
+
file: i.location?.file || filePath,
|
|
213
|
+
startLine: i.location?.startLine || 1,
|
|
214
|
+
endLine: i.location?.endLine || i.location?.startLine || 1,
|
|
215
|
+
snippet: i.location?.snippet || ""
|
|
216
|
+
},
|
|
217
|
+
suggestion: i.suggestion || "",
|
|
218
|
+
suggestedFix: i.suggestion || "",
|
|
219
|
+
confidence: i.confidence || 0.5,
|
|
220
|
+
autoFixable: i.autoFixable ?? false
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
} catch {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Extract JSON from LLM response
|
|
229
|
+
*/
|
|
230
|
+
extractJson(response) {
|
|
231
|
+
if (!response || typeof response !== "string") {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const jsonMatch = response.match(/\[[\s\S]*\]|\{[\s\S]*\}/);
|
|
236
|
+
if (jsonMatch) {
|
|
237
|
+
return JSON.parse(jsonMatch[0]);
|
|
238
|
+
}
|
|
239
|
+
return JSON.parse(response);
|
|
240
|
+
} catch {
|
|
241
|
+
console.warn("Failed to parse LLM response as JSON");
|
|
242
|
+
return [];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Map string to IssueType
|
|
247
|
+
*/
|
|
248
|
+
mapIssueType(type) {
|
|
249
|
+
const typeMap = {
|
|
250
|
+
security: "security",
|
|
251
|
+
performance: "performance",
|
|
252
|
+
quality: "quality",
|
|
253
|
+
bug: "bug",
|
|
254
|
+
style: "style",
|
|
255
|
+
logic: "logic",
|
|
256
|
+
accessibility: "accessibility"
|
|
257
|
+
};
|
|
258
|
+
return typeMap[type?.toLowerCase()] || "quality";
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Map IssueType to category (category doesn't include 'bug')
|
|
262
|
+
*/
|
|
263
|
+
mapCategory(type) {
|
|
264
|
+
if (type === "bug") {
|
|
265
|
+
return "quality";
|
|
266
|
+
}
|
|
267
|
+
return type;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Map string to IssueSeverity
|
|
271
|
+
*/
|
|
272
|
+
mapSeverity(severity) {
|
|
273
|
+
const severityMap = {
|
|
274
|
+
critical: "critical",
|
|
275
|
+
high: "high",
|
|
276
|
+
medium: "medium",
|
|
277
|
+
low: "low",
|
|
278
|
+
info: "info"
|
|
279
|
+
};
|
|
280
|
+
return severityMap[severity?.toLowerCase()] || "medium";
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function createMockLLMClient() {
|
|
284
|
+
return {
|
|
285
|
+
async complete(_prompt) {
|
|
286
|
+
return "[]";
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function shenchaScan(options = {}) {
|
|
292
|
+
console.log(ansis.cyan("\u{1F50D} ShenCha: Scanning code..."));
|
|
293
|
+
const context = {
|
|
294
|
+
rootPath: process.cwd(),
|
|
295
|
+
projectName: "current-project",
|
|
296
|
+
sourceDirs: ["src"],
|
|
297
|
+
testDirs: ["tests"],
|
|
298
|
+
configFiles: []
|
|
299
|
+
};
|
|
300
|
+
const llmClient = createMockLLMClient();
|
|
301
|
+
const scanner = new LLMScanner(llmClient);
|
|
302
|
+
const readFileWrapper = async (path) => {
|
|
303
|
+
return await readFile(resolve(context.rootPath, path), "utf-8");
|
|
304
|
+
};
|
|
305
|
+
const results = await scanner.scanAll(context, readFileWrapper);
|
|
306
|
+
console.log(ansis.green(`\u2713 Scan complete. Found ${results.length} results.`));
|
|
307
|
+
if (options.output) {
|
|
308
|
+
console.log(ansis.gray(`Output saved to: ${options.output}`));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async function shenchaReport(_options = {}) {
|
|
312
|
+
console.log(ansis.cyan("\u{1F4CA} ShenCha: Generating report..."));
|
|
313
|
+
console.log(ansis.green("\u2713 Report generated."));
|
|
314
|
+
}
|
|
315
|
+
async function shenchaFix(_options = {}) {
|
|
316
|
+
console.log(ansis.cyan("\u{1F527} ShenCha: Fixing issues..."));
|
|
317
|
+
console.log(ansis.green("\u2713 Fixes applied."));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export { shenchaFix, shenchaReport, shenchaScan };
|
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
import ansis from 'ansis';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
|
-
import {
|
|
3
|
+
import { getTranslation } from './index2.mjs';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import { existsSync, readdirSync, readFileSync, mkdirSync } from 'node:fs';
|
|
6
6
|
import process from 'node:process';
|
|
7
7
|
import { join } from 'pathe';
|
|
8
|
+
import { CCJK_SKILLS_DIR, CCJK_CONFIG_DIR } from './constants.mjs';
|
|
9
|
+
import { writeFileAtomic } from './fs-operations.mjs';
|
|
8
10
|
import { readFile, stat } from 'node:fs/promises';
|
|
9
11
|
import matter from 'gray-matter';
|
|
10
|
-
import 'node:os';
|
|
11
|
-
import 'smol-toml';
|
|
12
|
-
import 'dayjs';
|
|
13
|
-
import 'node:child_process';
|
|
14
|
-
import 'node:util';
|
|
15
12
|
import 'node:url';
|
|
16
|
-
import 'inquirer-toggle';
|
|
17
|
-
import 'ora';
|
|
18
|
-
import 'tinyexec';
|
|
19
|
-
import 'node:path';
|
|
20
|
-
import 'semver';
|
|
21
|
-
import 'fs-extra';
|
|
22
|
-
import 'trash';
|
|
23
13
|
import 'i18next';
|
|
24
14
|
import 'i18next-fs-backend';
|
|
15
|
+
import 'node:os';
|
|
25
16
|
|
|
26
17
|
function parseSkillMd(content, filePath = "unknown") {
|
|
27
18
|
try {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import ansis from 'ansis';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { join } from 'pathe';
|
|
5
|
+
import { i18n } from './index2.mjs';
|
|
6
|
+
import { writeFileAtomic } from './fs-operations.mjs';
|
|
7
|
+
import 'node:process';
|
|
8
|
+
import 'node:url';
|
|
9
|
+
import 'i18next';
|
|
10
|
+
import 'i18next-fs-backend';
|
|
11
|
+
import 'node:crypto';
|
|
12
|
+
import 'node:fs/promises';
|
|
13
|
+
|
|
14
|
+
const TEAM_DIR = ".ccjk/team";
|
|
15
|
+
const CONFIG_FILE = join(TEAM_DIR, "config.json");
|
|
16
|
+
async function teamInit() {
|
|
17
|
+
if (!existsSync(TEAM_DIR)) {
|
|
18
|
+
mkdirSync(TEAM_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
const { name } = await inquirer.prompt({
|
|
21
|
+
type: "input",
|
|
22
|
+
name: "name",
|
|
23
|
+
message: i18n.t("team:enterTeamName"),
|
|
24
|
+
default: "my-team"
|
|
25
|
+
});
|
|
26
|
+
const config = {
|
|
27
|
+
name,
|
|
28
|
+
members: [],
|
|
29
|
+
sharedSettings: {}
|
|
30
|
+
};
|
|
31
|
+
writeFileAtomic(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
32
|
+
console.log(ansis.green(`\u2714 ${i18n.t("team:teamInitialized")}: ${name}`));
|
|
33
|
+
}
|
|
34
|
+
async function teamShare() {
|
|
35
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
36
|
+
console.log(ansis.yellow(i18n.t("team:noTeamConfig")));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const config = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
40
|
+
console.log(ansis.cyan(i18n.t("team:shareConfig")));
|
|
41
|
+
console.log(JSON.stringify(config, null, 2));
|
|
42
|
+
}
|
|
43
|
+
async function teamSync() {
|
|
44
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
45
|
+
console.log(ansis.yellow(i18n.t("team:noTeamConfig")));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
console.log(ansis.green(`\u2714 ${i18n.t("team:syncComplete")}`));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { teamInit, teamShare, teamSync };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import ansis from 'ansis';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { CODE_TOOL_INFO, CODE_TOOL_TYPES } from './constants.mjs';
|
|
4
|
+
import { format, i18n } from './index2.mjs';
|
|
5
|
+
import { S as STATUS, C as COLORS, b as boxify } from '../shared/ccjk.BhKlRJ0h.mjs';
|
|
6
|
+
import { c as getToolStatus, d as getAllToolsStatus, f as installTool } from '../shared/ccjk.n_AtlHzB.mjs';
|
|
7
|
+
import 'node:os';
|
|
8
|
+
import 'pathe';
|
|
9
|
+
import 'node:fs';
|
|
10
|
+
import 'node:process';
|
|
11
|
+
import 'node:url';
|
|
12
|
+
import 'i18next';
|
|
13
|
+
import 'i18next-fs-backend';
|
|
14
|
+
import './package.mjs';
|
|
15
|
+
import 'tinyexec';
|
|
16
|
+
|
|
17
|
+
async function listTools(options = {}) {
|
|
18
|
+
const spinner = ora(i18n.t("tools:scanning")).start();
|
|
19
|
+
const toolsStatus = await getAllToolsStatus();
|
|
20
|
+
spinner.stop();
|
|
21
|
+
if (options.json) {
|
|
22
|
+
console.log(JSON.stringify(toolsStatus, null, 2));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
console.log("");
|
|
26
|
+
console.log(COLORS.primary("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
27
|
+
console.log(COLORS.primary("\u2551") + COLORS.accent(` ${i18n.t("tools:title")} `.slice(0, 60)) + COLORS.primary("\u2551"));
|
|
28
|
+
console.log(COLORS.primary("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
29
|
+
console.log("");
|
|
30
|
+
const cliTools = toolsStatus.filter((t) => CODE_TOOL_INFO[t.tool].category === "cli");
|
|
31
|
+
const extensionTools = toolsStatus.filter((t) => CODE_TOOL_INFO[t.tool].category === "extension");
|
|
32
|
+
const editorTools = toolsStatus.filter((t) => CODE_TOOL_INFO[t.tool].category === "editor");
|
|
33
|
+
const renderToolList = (tools, title) => {
|
|
34
|
+
console.log(COLORS.secondary(` ${title}:`));
|
|
35
|
+
for (const tool of tools) {
|
|
36
|
+
const info = CODE_TOOL_INFO[tool.tool];
|
|
37
|
+
const statusIcon = tool.installed ? ansis.green("\u2713") : ansis.gray("\u25CB");
|
|
38
|
+
const version = tool.version ? ansis.cyan(` v${tool.version}`) : "";
|
|
39
|
+
const configStatus = tool.installed ? tool.configExists ? ansis.green(` [${i18n.t("tools:configured")}]`) : ansis.yellow(` [${i18n.t("tools:notConfigured")}]`) : "";
|
|
40
|
+
console.log(` ${statusIcon} ${info.name}${version}${configStatus}`);
|
|
41
|
+
console.log(ansis.gray(` ${info.description}`));
|
|
42
|
+
}
|
|
43
|
+
console.log("");
|
|
44
|
+
};
|
|
45
|
+
if (cliTools.length > 0)
|
|
46
|
+
renderToolList(cliTools, `\u{1F5A5}\uFE0F ${i18n.t("tools:cliTools")}`);
|
|
47
|
+
if (extensionTools.length > 0)
|
|
48
|
+
renderToolList(extensionTools, `\u{1F50C} ${i18n.t("tools:ideExtensions")}`);
|
|
49
|
+
if (editorTools.length > 0)
|
|
50
|
+
renderToolList(editorTools, `\u270F\uFE0F ${i18n.t("tools:aiEditors")}`);
|
|
51
|
+
const installed = toolsStatus.filter((t) => t.installed).length;
|
|
52
|
+
const configured = toolsStatus.filter((t) => t.configExists).length;
|
|
53
|
+
console.log(ansis.gray(` ${format(i18n.t("tools:summary"), { installed: String(installed), total: String(toolsStatus.length), configured: String(configured), installedCount: String(installed) })}`));
|
|
54
|
+
console.log("");
|
|
55
|
+
}
|
|
56
|
+
async function installToolCommand(toolId, _options = {}) {
|
|
57
|
+
if (!CODE_TOOL_TYPES.includes(toolId)) {
|
|
58
|
+
console.log(STATUS.error(format(i18n.t("tools:unknownTool"), { tool: toolId })));
|
|
59
|
+
console.log(ansis.gray(format(i18n.t("tools:availableTools"), { tools: CODE_TOOL_TYPES.join(", ") })));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const tool = toolId;
|
|
63
|
+
const info = CODE_TOOL_INFO[tool];
|
|
64
|
+
const status = await getToolStatus(tool);
|
|
65
|
+
if (status.installed) {
|
|
66
|
+
console.log(STATUS.info(format(i18n.t("tools:alreadyInstalled"), { name: info.name, version: status.version || "unknown" })));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const spinner = ora(format(i18n.t("tools:installing"), { name: info.name })).start();
|
|
70
|
+
const result = await installTool(tool);
|
|
71
|
+
if (result.success) {
|
|
72
|
+
spinner.succeed(result.message);
|
|
73
|
+
} else {
|
|
74
|
+
spinner.fail(result.message);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function showToolStatus(toolId, options = {}) {
|
|
78
|
+
if (!CODE_TOOL_TYPES.includes(toolId)) {
|
|
79
|
+
console.log(STATUS.error(format(i18n.t("tools:unknownTool"), { tool: toolId })));
|
|
80
|
+
console.log(ansis.gray(format(i18n.t("tools:availableTools"), { tools: CODE_TOOL_TYPES.join(", ") })));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const tool = toolId;
|
|
84
|
+
const info = CODE_TOOL_INFO[tool];
|
|
85
|
+
const status = await getToolStatus(tool);
|
|
86
|
+
if (options.json) {
|
|
87
|
+
console.log(JSON.stringify({ ...status, info }, null, 2));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
console.log("");
|
|
91
|
+
console.log(boxify(`
|
|
92
|
+
${info.name}
|
|
93
|
+
|
|
94
|
+
${i18n.t("tools:status")}: ${status.installed ? `\u2713 ${i18n.t("tools:installed")}` : `\u2717 ${i18n.t("tools:notInstalled")}`}
|
|
95
|
+
${i18n.t("tools:version")}: ${status.version || "N/A"}
|
|
96
|
+
${i18n.t("tools:config")}: ${status.configExists ? `\u2713 ${i18n.t("tools:configured")}` : `\u26A0 ${i18n.t("tools:notConfigured")}`}
|
|
97
|
+
${i18n.t("tools:configPath")}: ${status.configPath}
|
|
98
|
+
${i18n.t("tools:category")}: ${info.category}
|
|
99
|
+
${i18n.t("tools:website")}: ${info.website}
|
|
100
|
+
|
|
101
|
+
${i18n.t("tools:installCommand")}:
|
|
102
|
+
${info.installCmd}
|
|
103
|
+
`, "rounded", info.name));
|
|
104
|
+
}
|
|
105
|
+
async function showRecommendedTools(_options = {}) {
|
|
106
|
+
const recommended = [
|
|
107
|
+
{
|
|
108
|
+
tool: "claude-code",
|
|
109
|
+
reason: "Best for complex reasoning and code generation"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
tool: "aider",
|
|
113
|
+
reason: "Great for terminal-based pair programming"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
tool: "continue",
|
|
117
|
+
reason: "Excellent VS Code integration with multiple models"
|
|
118
|
+
}
|
|
119
|
+
];
|
|
120
|
+
console.log("");
|
|
121
|
+
console.log(COLORS.secondary(` \u{1F31F} ${i18n.t("tools:recommended")}`));
|
|
122
|
+
console.log("");
|
|
123
|
+
for (const rec of recommended) {
|
|
124
|
+
const info = CODE_TOOL_INFO[rec.tool];
|
|
125
|
+
const status = await getToolStatus(rec.tool);
|
|
126
|
+
const statusIcon = status.installed ? ansis.green("\u2713") : ansis.gray("\u25CB");
|
|
127
|
+
console.log(` ${statusIcon} ${ansis.bold(info.name)}`);
|
|
128
|
+
console.log(ansis.gray(` ${rec.reason}`));
|
|
129
|
+
if (!status.installed) {
|
|
130
|
+
console.log(ansis.cyan(` ${format(i18n.t("tools:installCmd"), { cmd: info.installCmd })}`));
|
|
131
|
+
}
|
|
132
|
+
console.log("");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function toolsCommand(action = "list", target, options = {}) {
|
|
136
|
+
switch (action) {
|
|
137
|
+
case "list":
|
|
138
|
+
case "ls":
|
|
139
|
+
await listTools(options);
|
|
140
|
+
break;
|
|
141
|
+
case "install":
|
|
142
|
+
case "i":
|
|
143
|
+
if (!target) {
|
|
144
|
+
console.log(STATUS.error(i18n.t("tools:specifyTool")));
|
|
145
|
+
console.log(ansis.gray(format(i18n.t("tools:usage"), { action: "install" })));
|
|
146
|
+
console.log(ansis.gray(format(i18n.t("tools:availableTools"), { tools: CODE_TOOL_TYPES.join(", ") })));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await installToolCommand(target, options);
|
|
150
|
+
break;
|
|
151
|
+
case "status":
|
|
152
|
+
case "s":
|
|
153
|
+
if (!target) {
|
|
154
|
+
await listTools(options);
|
|
155
|
+
} else {
|
|
156
|
+
await showToolStatus(target, options);
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
case "recommend":
|
|
160
|
+
case "rec":
|
|
161
|
+
await showRecommendedTools(options);
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
console.log(STATUS.error(format(i18n.t("tools:unknownAction"), { action })));
|
|
165
|
+
console.log(ansis.gray(i18n.t("tools:availableActions")));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { toolsCommand as default, installToolCommand, listTools, showRecommendedTools, showToolStatus, toolsCommand };
|