ccjk 9.5.6 → 9.7.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.mjs +1 -1
- package/dist/chunks/api-providers.mjs +1 -1
- package/dist/chunks/api.mjs +3 -3
- package/dist/chunks/auto-bootstrap.mjs +1 -1
- package/dist/chunks/auto-updater.mjs +1 -1
- package/dist/chunks/boost.mjs +160 -0
- package/dist/chunks/ccjk-agents.mjs +1 -1
- package/dist/chunks/ccjk-all.mjs +1 -1
- package/dist/chunks/ccjk-config.mjs +1 -1
- package/dist/chunks/ccjk-hooks.mjs +1 -1
- package/dist/chunks/ccjk-mcp.mjs +2 -2
- package/dist/chunks/ccjk-setup.mjs +1 -1
- package/dist/chunks/ccjk-skills.mjs +1 -1
- package/dist/chunks/ccr.mjs +25 -30
- package/dist/chunks/ccu.mjs +1 -1
- package/dist/chunks/check-updates.mjs +3 -4
- package/dist/chunks/claude-code-config-manager.mjs +7 -7
- package/dist/chunks/claude-code-incremental-manager.mjs +2 -2
- package/dist/chunks/claude-config.mjs +4 -4
- package/dist/chunks/claude-wrapper.mjs +2 -2
- package/dist/chunks/codex-config-switch.mjs +4 -5
- package/dist/chunks/codex-provider-manager.mjs +2 -3
- package/dist/chunks/codex-uninstaller.mjs +2 -2
- package/dist/chunks/codex.mjs +207 -6
- package/dist/chunks/commands.mjs +391 -88
- package/dist/chunks/commands2.mjs +88 -391
- package/dist/chunks/completion.mjs +1 -1
- package/dist/chunks/config-consolidator.mjs +2 -2
- package/dist/chunks/config-switch.mjs +3 -4
- package/dist/chunks/config.mjs +78 -7
- package/dist/chunks/config2.mjs +400 -410
- package/dist/chunks/config3.mjs +410 -400
- package/dist/chunks/constants.mjs +1 -1
- package/dist/chunks/doctor.mjs +4 -4
- package/dist/chunks/features.mjs +24 -17
- package/dist/chunks/index.mjs +178 -7
- package/dist/chunks/index2.mjs +1162 -169
- package/dist/chunks/index3.mjs +910 -1076
- package/dist/chunks/index4.mjs +137 -947
- package/dist/chunks/index5.mjs +635 -167
- package/dist/chunks/init.mjs +141 -99
- package/dist/chunks/installer.mjs +147 -649
- package/dist/chunks/installer2.mjs +649 -147
- package/dist/chunks/interview.mjs +2 -2
- package/dist/chunks/marketplace.mjs +1 -1
- package/dist/chunks/mcp.mjs +1058 -17
- package/dist/chunks/menu.mjs +147 -56
- package/dist/chunks/monitor.mjs +2 -2
- package/dist/chunks/notification.mjs +1 -1
- package/dist/chunks/onboarding.mjs +2 -2
- package/dist/chunks/package.mjs +2 -210
- package/dist/chunks/permission-manager.mjs +2 -2
- package/dist/chunks/permissions.mjs +1 -1
- package/dist/chunks/platform.mjs +1 -1
- package/dist/chunks/plugin.mjs +1 -1
- package/dist/chunks/prompts.mjs +1 -1
- package/dist/chunks/providers.mjs +1 -1
- package/dist/chunks/quick-setup.mjs +16 -20
- package/dist/chunks/silent-updater.mjs +1 -1
- package/dist/chunks/simple-config.mjs +2 -2
- package/dist/chunks/skill.mjs +1 -1
- package/dist/chunks/skills-sync.mjs +1 -1
- package/dist/chunks/skills.mjs +1 -1
- package/dist/chunks/startup.mjs +1 -1
- package/dist/chunks/stats.mjs +1 -1
- package/dist/chunks/status.mjs +159 -0
- package/dist/chunks/team.mjs +1 -1
- package/dist/chunks/thinking.mjs +2 -2
- package/dist/chunks/uninstall.mjs +6 -6
- package/dist/chunks/update.mjs +6 -9
- package/dist/chunks/upgrade-manager.mjs +2 -2
- package/dist/chunks/version-checker.mjs +3 -3
- package/dist/chunks/vim.mjs +1 -1
- package/dist/chunks/workflows.mjs +616 -215
- package/dist/cli.mjs +70 -121
- package/dist/index.d.mts +17 -1482
- package/dist/index.d.ts +17 -1482
- package/dist/index.mjs +950 -4740
- package/dist/shared/{ccjk.zCqdxT2Y.mjs → ccjk.Br91zBIG.mjs} +2 -2
- package/dist/shared/ccjk.CSkyCZIM.mjs +638 -0
- package/dist/shared/{ccjk.BKoi8-Hy.mjs → ccjk.DE91nClQ.mjs} +1 -1
- package/dist/shared/{ccjk.f40us0yY.mjs → ccjk.DvIrK0wz.mjs} +2 -2
- package/dist/shared/ccjk.LsPZ2PYo.mjs +1048 -0
- package/dist/shared/{ccjk.DRweXU5F.mjs → ccjk.q1koQxEE.mjs} +2 -2
- package/package.json +1 -1
- package/templates/claude-code/common/settings.json +15 -111
- package/dist/chunks/api-adapter.mjs +0 -180
- package/dist/chunks/cli.mjs +0 -2227
- package/dist/chunks/context-menu.mjs +0 -913
- package/dist/chunks/hooks-sync.mjs +0 -1627
- package/dist/chunks/index6.mjs +0 -663
- package/dist/chunks/mcp-market.mjs +0 -1077
- package/dist/chunks/mcp-server.mjs +0 -776
- package/dist/chunks/project-detector.mjs +0 -131
- package/dist/chunks/provider-registry.mjs +0 -92
- package/dist/chunks/setup-wizard.mjs +0 -362
- package/dist/chunks/tools.mjs +0 -143
- package/dist/chunks/workflows2.mjs +0 -633
- package/dist/shared/ccjk.BM_HZogn.mjs +0 -347
- package/dist/shared/ccjk.BaEp4UHQ.mjs +0 -75
- package/dist/shared/ccjk.CS0ybJCf.mjs +0 -490
- package/dist/shared/ccjk.CZgIwikC.mjs +0 -209
- package/dist/shared/ccjk.tO8zeFh1.mjs +0 -397
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ansis from 'ansis';
|
|
2
2
|
import { homepage, version } from '../chunks/package.mjs';
|
|
3
|
-
import { ensureI18nInitialized, i18n } from '../chunks/
|
|
3
|
+
import { ensureI18nInitialized, i18n } from '../chunks/index.mjs';
|
|
4
4
|
|
|
5
5
|
const theme = {
|
|
6
6
|
// === Core Colors ===
|
|
@@ -135,4 +135,4 @@ const STATUS = {
|
|
|
135
135
|
inProgress: status.work
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
-
export { STATUS as S,
|
|
138
|
+
export { STATUS as S, displayBanner as a, boxify as b, displayBannerWithInfo as d, padToDisplayWidth as p, theme as t };
|
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import process__default from 'node:process';
|
|
3
|
+
import { join } from 'pathe';
|
|
4
|
+
import { SETTINGS_FILE, CLAUDE_AGENTS_DIR, CCJK_SKILLS_DIR } from '../chunks/constants.mjs';
|
|
5
|
+
|
|
6
|
+
function analyzeProject(root) {
|
|
7
|
+
const cwd = process__default.cwd();
|
|
8
|
+
const profile = {
|
|
9
|
+
language: "unknown",
|
|
10
|
+
frameworks: [],
|
|
11
|
+
packageManager: "unknown",
|
|
12
|
+
hasCI: false,
|
|
13
|
+
hasDocker: false,
|
|
14
|
+
hasMCP: false,
|
|
15
|
+
hasClaudeMd: false,
|
|
16
|
+
isMonorepo: false,
|
|
17
|
+
tags: []
|
|
18
|
+
};
|
|
19
|
+
const pkgPath = join(cwd, "package.json");
|
|
20
|
+
let pkg = null;
|
|
21
|
+
if (existsSync(pkgPath)) {
|
|
22
|
+
try {
|
|
23
|
+
pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
24
|
+
profile.projectName = pkg?.name;
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
profile.language = detectLanguage(cwd, pkg);
|
|
29
|
+
profile.packageManager = detectPackageManager(cwd);
|
|
30
|
+
profile.frameworks = detectFrameworks(cwd, pkg);
|
|
31
|
+
profile.testFramework = detectTestFramework(cwd, pkg);
|
|
32
|
+
profile.buildTool = detectBuildTool(cwd, pkg);
|
|
33
|
+
profile.hasCI = existsSync(join(cwd, ".github/workflows")) || existsSync(join(cwd, ".gitlab-ci.yml")) || existsSync(join(cwd, ".circleci")) || existsSync(join(cwd, "Jenkinsfile"));
|
|
34
|
+
profile.hasDocker = existsSync(join(cwd, "Dockerfile")) || existsSync(join(cwd, "docker-compose.yml")) || existsSync(join(cwd, "docker-compose.yaml"));
|
|
35
|
+
profile.hasMCP = checkMcpConfigured();
|
|
36
|
+
profile.hasClaudeMd = existsSync(join(cwd, "CLAUDE.md"));
|
|
37
|
+
profile.isMonorepo = existsSync(join(cwd, "pnpm-workspace.yaml")) || existsSync(join(cwd, "lerna.json")) || pkg?.workspaces != null;
|
|
38
|
+
profile.tags = generateTags(profile);
|
|
39
|
+
return profile;
|
|
40
|
+
}
|
|
41
|
+
function detectLanguage(cwd, pkg) {
|
|
42
|
+
if (existsSync(join(cwd, "tsconfig.json")) || pkg?.devDependencies?.typescript) return "typescript";
|
|
43
|
+
if (existsSync(join(cwd, "pyproject.toml")) || existsSync(join(cwd, "setup.py"))) return "python";
|
|
44
|
+
if (existsSync(join(cwd, "go.mod"))) return "go";
|
|
45
|
+
if (existsSync(join(cwd, "Cargo.toml"))) return "rust";
|
|
46
|
+
if (existsSync(join(cwd, "Gemfile"))) return "ruby";
|
|
47
|
+
if (existsSync(join(cwd, "pom.xml")) || existsSync(join(cwd, "build.gradle"))) return "java";
|
|
48
|
+
if (existsSync(join(cwd, "Package.swift"))) return "swift";
|
|
49
|
+
if (pkg) return "javascript";
|
|
50
|
+
return "unknown";
|
|
51
|
+
}
|
|
52
|
+
function detectPackageManager(cwd) {
|
|
53
|
+
if (existsSync(join(cwd, "pnpm-lock.yaml")) || existsSync(join(cwd, "pnpm-workspace.yaml"))) return "pnpm";
|
|
54
|
+
if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
|
|
55
|
+
if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) return "bun";
|
|
56
|
+
if (existsSync(join(cwd, "package-lock.json"))) return "npm";
|
|
57
|
+
return "unknown";
|
|
58
|
+
}
|
|
59
|
+
function detectFrameworks(_cwd, pkg) {
|
|
60
|
+
const frameworks = [];
|
|
61
|
+
const allDeps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
62
|
+
if (allDeps?.react || allDeps?.["react-dom"]) frameworks.push("react");
|
|
63
|
+
if (allDeps?.vue) frameworks.push("vue");
|
|
64
|
+
if (allDeps?.svelte) frameworks.push("svelte");
|
|
65
|
+
if (allDeps?.angular || allDeps?.["@angular/core"]) frameworks.push("angular");
|
|
66
|
+
if (allDeps?.next) frameworks.push("nextjs");
|
|
67
|
+
if (allDeps?.nuxt) frameworks.push("nuxt");
|
|
68
|
+
if (allDeps?.astro) frameworks.push("astro");
|
|
69
|
+
if (allDeps?.express) frameworks.push("express");
|
|
70
|
+
if (allDeps?.fastify) frameworks.push("fastify");
|
|
71
|
+
if (allDeps?.koa) frameworks.push("koa");
|
|
72
|
+
if (allDeps?.hono) frameworks.push("hono");
|
|
73
|
+
if (allDeps?.["@nestjs/core"]) frameworks.push("nestjs");
|
|
74
|
+
if (allDeps?.electron) frameworks.push("electron");
|
|
75
|
+
if (allDeps?.["@tauri-apps/api"]) frameworks.push("tauri");
|
|
76
|
+
if (allDeps?.["react-native"]) frameworks.push("react-native");
|
|
77
|
+
if (allDeps?.cac || allDeps?.commander || allDeps?.yargs) frameworks.push("cli");
|
|
78
|
+
return frameworks;
|
|
79
|
+
}
|
|
80
|
+
function detectTestFramework(cwd, pkg) {
|
|
81
|
+
const allDeps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
82
|
+
if (allDeps?.vitest || existsSync(join(cwd, "vitest.config.ts"))) return "vitest";
|
|
83
|
+
if (allDeps?.jest || existsSync(join(cwd, "jest.config.js"))) return "jest";
|
|
84
|
+
if (allDeps?.mocha) return "mocha";
|
|
85
|
+
if (allDeps?.playwright || allDeps?.["@playwright/test"]) return "playwright";
|
|
86
|
+
if (allDeps?.cypress) return "cypress";
|
|
87
|
+
if (existsSync(join(cwd, "pytest.ini")) || existsSync(join(cwd, "conftest.py"))) return "pytest";
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
function detectBuildTool(cwd, pkg) {
|
|
91
|
+
const allDeps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
92
|
+
if (allDeps?.vite || existsSync(join(cwd, "vite.config.ts"))) return "vite";
|
|
93
|
+
if (allDeps?.webpack) return "webpack";
|
|
94
|
+
if (allDeps?.esbuild) return "esbuild";
|
|
95
|
+
if (allDeps?.unbuild) return "unbuild";
|
|
96
|
+
if (allDeps?.rollup) return "rollup";
|
|
97
|
+
if (allDeps?.turbo || existsSync(join(cwd, "turbo.json"))) return "turbo";
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
function checkMcpConfigured() {
|
|
101
|
+
try {
|
|
102
|
+
if (!existsSync(SETTINGS_FILE)) return false;
|
|
103
|
+
const settings = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
|
|
104
|
+
return Object.keys(settings.mcpServers || {}).length > 0;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function generateTags(profile) {
|
|
110
|
+
const tags = [];
|
|
111
|
+
tags.push(profile.language);
|
|
112
|
+
tags.push(...profile.frameworks);
|
|
113
|
+
if (profile.testFramework) tags.push(profile.testFramework);
|
|
114
|
+
if (profile.buildTool) tags.push(profile.buildTool);
|
|
115
|
+
if (profile.packageManager !== "unknown") tags.push(profile.packageManager);
|
|
116
|
+
if (profile.hasCI) tags.push("ci");
|
|
117
|
+
if (profile.hasDocker) tags.push("docker");
|
|
118
|
+
if (profile.isMonorepo) tags.push("monorepo");
|
|
119
|
+
if (profile.frameworks.some((f) => ["react", "vue", "svelte", "angular"].includes(f))) tags.push("frontend");
|
|
120
|
+
if (profile.frameworks.some((f) => ["express", "fastify", "koa", "nestjs", "hono"].includes(f))) tags.push("backend");
|
|
121
|
+
if (profile.frameworks.some((f) => ["nextjs", "nuxt", "astro"].includes(f))) tags.push("fullstack");
|
|
122
|
+
if (profile.frameworks.includes("cli")) tags.push("cli-tool");
|
|
123
|
+
return [...new Set(tags)];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const SKILL_RULES = [
|
|
127
|
+
{
|
|
128
|
+
tags: ["typescript", "javascript", "python", "go", "rust", "ruby", "java"],
|
|
129
|
+
skill: { id: "git-commit", name: "Smart Git Commit", description: "AI-powered conventional commit messages", reason: "Every project needs good commits", category: "git" }
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
tags: ["typescript", "javascript", "python", "go", "rust"],
|
|
133
|
+
skill: { id: "code-review", name: "Code Review", description: "Two-phase deep code review", reason: "Catch bugs before they ship", category: "review" }
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
tags: ["react", "vue", "svelte", "angular", "frontend"],
|
|
137
|
+
skill: { id: "component-gen", name: "Component Generator", description: "Generate UI components with tests", reason: "Frontend framework detected", category: "dev" }
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
tags: ["express", "fastify", "koa", "nestjs", "hono", "backend"],
|
|
141
|
+
skill: { id: "api-design", name: "API Design", description: "Design RESTful/GraphQL APIs", reason: "Backend framework detected", category: "dev" }
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
tags: ["backend", "fullstack"],
|
|
145
|
+
skill: { id: "security-audit", name: "Security Audit", description: "Check for common vulnerabilities", reason: "Server-side code needs security review", category: "review" }
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
tags: ["vitest", "jest", "pytest", "mocha"],
|
|
149
|
+
skill: { id: "tdd-workflow", name: "TDD Workflow", description: "Test-driven development cycle", reason: "Test framework detected", category: "testing" }
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
tags: ["playwright", "cypress"],
|
|
153
|
+
skill: { id: "e2e-helper", name: "E2E Test Helper", description: "Generate end-to-end tests", reason: "E2E framework detected", category: "testing" }
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
tags: ["docker"],
|
|
157
|
+
skill: { id: "docker-optimize", name: "Docker Optimizer", description: "Optimize Dockerfile", reason: "Docker detected", category: "devops" }
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
tags: ["ci"],
|
|
161
|
+
skill: { id: "ci-pipeline", name: "CI Pipeline", description: "Optimize CI/CD pipeline", reason: "CI configuration detected", category: "devops" }
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
tags: ["cli-tool", "cli"],
|
|
165
|
+
skill: { id: "cli-ux", name: "CLI UX Design", description: "Better CLI interfaces", reason: "CLI tool detected", category: "dev" }
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
tags: ["monorepo"],
|
|
169
|
+
skill: { id: "monorepo-manage", name: "Monorepo Manager", description: "Manage packages and releases", reason: "Monorepo detected", category: "dev" }
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
tags: ["typescript", "javascript", "python"],
|
|
173
|
+
skill: { id: "doc-gen", name: "Documentation Generator", description: "Generate API docs and README", reason: "Good docs improve maintainability", category: "docs" }
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
tags: ["react", "vue", "nextjs", "nuxt", "frontend"],
|
|
177
|
+
skill: { id: "perf-audit", name: "Performance Audit", description: "Bundle size and Core Web Vitals", reason: "Frontend performance matters", category: "review" }
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
tags: ["nextjs", "nuxt", "astro", "fullstack"],
|
|
181
|
+
skill: { id: "fullstack-debug", name: "Fullstack Debugger", description: "Debug across client/server boundary", reason: "Fullstack framework detected", category: "debug" }
|
|
182
|
+
}
|
|
183
|
+
];
|
|
184
|
+
const MCP_RULES = [
|
|
185
|
+
{
|
|
186
|
+
tags: ["typescript", "javascript", "python", "go", "rust", "ruby", "java"],
|
|
187
|
+
mcp: { id: "context7", name: "Context7", description: "Up-to-date library docs", reason: "Essential for accurate API usage" }
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
tags: ["playwright", "frontend", "fullstack"],
|
|
191
|
+
mcp: { id: "playwright", name: "Playwright MCP", description: "Browser automation", reason: "Frontend/E2E project detected" }
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
tags: ["backend", "fullstack", "express", "fastify", "nestjs"],
|
|
195
|
+
mcp: { id: "sqlite", name: "SQLite MCP", description: "Local database", reason: "Backend project detected" }
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
tags: ["typescript", "javascript", "python", "go", "rust"],
|
|
199
|
+
mcp: { id: "mcp-search", name: "Web Search", description: "Search for docs and solutions", reason: "Quick access to online resources" }
|
|
200
|
+
}
|
|
201
|
+
];
|
|
202
|
+
function matchSkills(profile) {
|
|
203
|
+
const results = [];
|
|
204
|
+
const seen = /* @__PURE__ */ new Set();
|
|
205
|
+
for (const rule of SKILL_RULES) {
|
|
206
|
+
if (seen.has(rule.skill.id)) continue;
|
|
207
|
+
const matchingTags = rule.tags.filter((t) => profile.tags.includes(t));
|
|
208
|
+
if (matchingTags.length === 0) continue;
|
|
209
|
+
const matchScore = Math.min(100, Math.round(matchingTags.length / rule.tags.length * 100) + 20);
|
|
210
|
+
results.push({ ...rule.skill, matchScore });
|
|
211
|
+
seen.add(rule.skill.id);
|
|
212
|
+
}
|
|
213
|
+
results.sort((a, b) => b.matchScore - a.matchScore);
|
|
214
|
+
return results;
|
|
215
|
+
}
|
|
216
|
+
function matchMcpServices(profile) {
|
|
217
|
+
const results = [];
|
|
218
|
+
const seen = /* @__PURE__ */ new Set();
|
|
219
|
+
for (const rule of MCP_RULES) {
|
|
220
|
+
if (seen.has(rule.mcp.id)) continue;
|
|
221
|
+
const matchingTags = rule.tags.filter((t) => profile.tags.includes(t));
|
|
222
|
+
if (matchingTags.length === 0) continue;
|
|
223
|
+
const matchScore = Math.min(100, Math.round(matchingTags.length / rule.tags.length * 100) + 20);
|
|
224
|
+
results.push({ ...rule.mcp, matchScore });
|
|
225
|
+
seen.add(rule.mcp.id);
|
|
226
|
+
}
|
|
227
|
+
results.sort((a, b) => b.matchScore - a.matchScore);
|
|
228
|
+
return results;
|
|
229
|
+
}
|
|
230
|
+
function getRecommendations(profile) {
|
|
231
|
+
const skills = matchSkills(profile);
|
|
232
|
+
const mcpServices = matchMcpServices(profile);
|
|
233
|
+
const parts = [];
|
|
234
|
+
if (profile.language !== "unknown") parts.push(profile.language);
|
|
235
|
+
parts.push(...profile.frameworks.slice(0, 3));
|
|
236
|
+
const stackDesc = parts.join(" + ") || "your project";
|
|
237
|
+
return {
|
|
238
|
+
skills,
|
|
239
|
+
mcpServices,
|
|
240
|
+
summary: `Found ${skills.length} skills and ${mcpServices.length} MCP services for ${stackDesc}`
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const agentsCheck = {
|
|
245
|
+
name: "Agents",
|
|
246
|
+
weight: 4,
|
|
247
|
+
async check() {
|
|
248
|
+
try {
|
|
249
|
+
if (!existsSync(CLAUDE_AGENTS_DIR)) {
|
|
250
|
+
return {
|
|
251
|
+
name: this.name,
|
|
252
|
+
status: "warn",
|
|
253
|
+
score: 30,
|
|
254
|
+
weight: this.weight,
|
|
255
|
+
message: "No agents directory",
|
|
256
|
+
fix: "Create agents for specialized tasks",
|
|
257
|
+
command: "ccjk ccjk:agents --list"
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const files = readdirSync(CLAUDE_AGENTS_DIR).filter((f) => f.endsWith(".md"));
|
|
261
|
+
const agentCount = files.length;
|
|
262
|
+
if (agentCount === 0) {
|
|
263
|
+
return {
|
|
264
|
+
name: this.name,
|
|
265
|
+
status: "warn",
|
|
266
|
+
score: 30,
|
|
267
|
+
weight: this.weight,
|
|
268
|
+
message: "No agents configured",
|
|
269
|
+
fix: "Create agents for your project",
|
|
270
|
+
command: "ccjk ccjk:agents"
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
const score = Math.min(100, 40 + agentCount * 15);
|
|
274
|
+
return {
|
|
275
|
+
name: this.name,
|
|
276
|
+
status: score >= 60 ? "pass" : "warn",
|
|
277
|
+
score,
|
|
278
|
+
weight: this.weight,
|
|
279
|
+
message: `${agentCount} agent${agentCount > 1 ? "s" : ""} configured`,
|
|
280
|
+
details: files.slice(0, 5).map((f) => ` ${f.replace(".md", "")}`)
|
|
281
|
+
};
|
|
282
|
+
} catch {
|
|
283
|
+
return { name: this.name, status: "fail", score: 0, weight: this.weight, message: "Failed to read agents" };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const claudeMdCheck = {
|
|
289
|
+
name: "CLAUDE.md",
|
|
290
|
+
weight: 7,
|
|
291
|
+
async check() {
|
|
292
|
+
const cwd = process.cwd();
|
|
293
|
+
const claudeMdPath = join(cwd, "CLAUDE.md");
|
|
294
|
+
try {
|
|
295
|
+
if (!existsSync(claudeMdPath)) {
|
|
296
|
+
return {
|
|
297
|
+
name: this.name,
|
|
298
|
+
status: "fail",
|
|
299
|
+
score: 0,
|
|
300
|
+
weight: this.weight,
|
|
301
|
+
message: "No CLAUDE.md in project root",
|
|
302
|
+
fix: "Create CLAUDE.md for project-specific AI instructions",
|
|
303
|
+
command: "ccjk init --smart"
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
const stat = statSync(claudeMdPath);
|
|
307
|
+
const content = readFileSync(claudeMdPath, "utf-8");
|
|
308
|
+
const lines = content.split("\n").length;
|
|
309
|
+
const sizeKb = Math.round(stat.size / 1024);
|
|
310
|
+
const hasHeaders = (content.match(/^#{1,3}\s/gm) || []).length;
|
|
311
|
+
const hasCodeBlocks = (content.match(/```/g) || []).length / 2;
|
|
312
|
+
const hasBuildCommands = /build|test|lint|dev/i.test(content);
|
|
313
|
+
let score = 40;
|
|
314
|
+
if (lines > 20) score += 15;
|
|
315
|
+
if (hasHeaders >= 3) score += 15;
|
|
316
|
+
if (hasCodeBlocks >= 1) score += 10;
|
|
317
|
+
if (hasBuildCommands) score += 20;
|
|
318
|
+
score = Math.min(100, score);
|
|
319
|
+
return {
|
|
320
|
+
name: this.name,
|
|
321
|
+
status: score >= 60 ? "pass" : "warn",
|
|
322
|
+
score,
|
|
323
|
+
weight: this.weight,
|
|
324
|
+
message: `${lines} lines, ${sizeKb}KB`,
|
|
325
|
+
details: [
|
|
326
|
+
` Sections: ${hasHeaders}`,
|
|
327
|
+
` Code blocks: ${Math.floor(hasCodeBlocks)}`,
|
|
328
|
+
` Build info: ${hasBuildCommands ? "yes" : "no"}`
|
|
329
|
+
],
|
|
330
|
+
...score < 80 && { fix: "Enrich CLAUDE.md with more project context", command: "ccjk init --smart" }
|
|
331
|
+
};
|
|
332
|
+
} catch {
|
|
333
|
+
return { name: this.name, status: "fail", score: 0, weight: this.weight, message: "Failed to read CLAUDE.md" };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const mcpCheck = {
|
|
339
|
+
name: "MCP Services",
|
|
340
|
+
weight: 8,
|
|
341
|
+
async check() {
|
|
342
|
+
try {
|
|
343
|
+
if (!existsSync(SETTINGS_FILE)) {
|
|
344
|
+
return {
|
|
345
|
+
name: this.name,
|
|
346
|
+
status: "fail",
|
|
347
|
+
score: 0,
|
|
348
|
+
weight: this.weight,
|
|
349
|
+
message: "No settings.json found",
|
|
350
|
+
fix: "Run ccjk init to create settings",
|
|
351
|
+
command: "ccjk init"
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
const settings = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
|
|
355
|
+
const mcpServers = settings.mcpServers || {};
|
|
356
|
+
const serverCount = Object.keys(mcpServers).length;
|
|
357
|
+
if (serverCount === 0) {
|
|
358
|
+
return {
|
|
359
|
+
name: this.name,
|
|
360
|
+
status: "fail",
|
|
361
|
+
score: 0,
|
|
362
|
+
weight: this.weight,
|
|
363
|
+
message: "No MCP services configured",
|
|
364
|
+
fix: "Install MCP services for enhanced capabilities",
|
|
365
|
+
command: "ccjk ccjk:mcp"
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
const essentialServices = ["context7"];
|
|
369
|
+
const hasEssentials = essentialServices.filter(
|
|
370
|
+
(s) => Object.keys(mcpServers).some((k) => k.toLowerCase().includes(s))
|
|
371
|
+
);
|
|
372
|
+
const score = Math.min(100, serverCount * 20 + hasEssentials.length * 20);
|
|
373
|
+
const status = score >= 60 ? "pass" : "warn";
|
|
374
|
+
const details = Object.keys(mcpServers).map((name) => ` ${name}`);
|
|
375
|
+
return {
|
|
376
|
+
name: this.name,
|
|
377
|
+
status,
|
|
378
|
+
score,
|
|
379
|
+
weight: this.weight,
|
|
380
|
+
message: `${serverCount} service${serverCount > 1 ? "s" : ""} active`,
|
|
381
|
+
details,
|
|
382
|
+
...score < 80 && { fix: "Add more MCP services", command: "ccjk ccjk:mcp" }
|
|
383
|
+
};
|
|
384
|
+
} catch {
|
|
385
|
+
return {
|
|
386
|
+
name: this.name,
|
|
387
|
+
status: "fail",
|
|
388
|
+
score: 0,
|
|
389
|
+
weight: this.weight,
|
|
390
|
+
message: "Failed to read MCP configuration",
|
|
391
|
+
command: "ccjk doctor"
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const modelCheck = {
|
|
398
|
+
name: "Default Model",
|
|
399
|
+
weight: 5,
|
|
400
|
+
async check() {
|
|
401
|
+
try {
|
|
402
|
+
if (!existsSync(SETTINGS_FILE)) {
|
|
403
|
+
return {
|
|
404
|
+
name: this.name,
|
|
405
|
+
status: "fail",
|
|
406
|
+
score: 0,
|
|
407
|
+
weight: this.weight,
|
|
408
|
+
message: "No settings file",
|
|
409
|
+
command: "ccjk init"
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
const settings = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
|
|
413
|
+
const hasModel = settings.model || settings.defaultModel || settings.preferredModel;
|
|
414
|
+
const hasApiKey = settings.apiKey || settings.env?.ANTHROPIC_API_KEY || process__default.env.ANTHROPIC_API_KEY;
|
|
415
|
+
if (!hasApiKey) {
|
|
416
|
+
return {
|
|
417
|
+
name: this.name,
|
|
418
|
+
status: "warn",
|
|
419
|
+
score: 40,
|
|
420
|
+
weight: this.weight,
|
|
421
|
+
message: "No API key configured (using default)",
|
|
422
|
+
fix: "Configure API for direct access",
|
|
423
|
+
command: "ccjk menu",
|
|
424
|
+
details: [" Using Claude Code default API"]
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
name: this.name,
|
|
429
|
+
status: "pass",
|
|
430
|
+
score: hasModel ? 100 : 70,
|
|
431
|
+
weight: this.weight,
|
|
432
|
+
message: hasModel ? `Model: ${hasModel}` : "API configured (default model)"
|
|
433
|
+
};
|
|
434
|
+
} catch {
|
|
435
|
+
return { name: this.name, status: "fail", score: 0, weight: this.weight, message: "Failed to read model config" };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const INVALID_PERMISSION_NAMES = /* @__PURE__ */ new Set([
|
|
441
|
+
"AllowEdit",
|
|
442
|
+
"AllowWrite",
|
|
443
|
+
"AllowRead",
|
|
444
|
+
"AllowExec",
|
|
445
|
+
"AllowCreateProcess",
|
|
446
|
+
"AllowKillProcess",
|
|
447
|
+
"AllowNetworkAccess",
|
|
448
|
+
"AllowFileSystemAccess",
|
|
449
|
+
"AllowShellAccess",
|
|
450
|
+
"AllowHttpAccess"
|
|
451
|
+
]);
|
|
452
|
+
function isValidPermission(perm) {
|
|
453
|
+
if (INVALID_PERMISSION_NAMES.has(perm)) return false;
|
|
454
|
+
if (["mcp__.*", "mcp__*", "mcp__(*)"].includes(perm)) return false;
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
const permissionsCheck = {
|
|
458
|
+
name: "Permissions",
|
|
459
|
+
weight: 3,
|
|
460
|
+
async check() {
|
|
461
|
+
try {
|
|
462
|
+
if (!existsSync(SETTINGS_FILE)) {
|
|
463
|
+
return {
|
|
464
|
+
name: this.name,
|
|
465
|
+
status: "warn",
|
|
466
|
+
score: 30,
|
|
467
|
+
weight: this.weight,
|
|
468
|
+
message: "No settings file",
|
|
469
|
+
command: "ccjk init"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
const settings = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
|
|
473
|
+
const allowedTools = settings.permissions?.allow || [];
|
|
474
|
+
if (allowedTools.length === 0) {
|
|
475
|
+
return {
|
|
476
|
+
name: this.name,
|
|
477
|
+
status: "warn",
|
|
478
|
+
score: 40,
|
|
479
|
+
weight: this.weight,
|
|
480
|
+
message: "No tool permissions configured",
|
|
481
|
+
fix: "Configure permissions to reduce prompts",
|
|
482
|
+
command: "ccjk menu"
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
const invalidPerms = allowedTools.filter((p) => !isValidPermission(p));
|
|
486
|
+
const validPerms = allowedTools.filter((p) => isValidPermission(p));
|
|
487
|
+
if (invalidPerms.length > 0) {
|
|
488
|
+
return {
|
|
489
|
+
name: this.name,
|
|
490
|
+
status: "warn",
|
|
491
|
+
score: 50,
|
|
492
|
+
weight: this.weight,
|
|
493
|
+
message: `${invalidPerms.length} invalid permission(s) found (${validPerms.length} valid)`,
|
|
494
|
+
fix: "Run ccjk init to repair permissions",
|
|
495
|
+
command: "ccjk init"
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
const score = Math.min(100, 60 + validPerms.length * 2);
|
|
499
|
+
return {
|
|
500
|
+
name: this.name,
|
|
501
|
+
status: "pass",
|
|
502
|
+
score,
|
|
503
|
+
weight: this.weight,
|
|
504
|
+
message: `${validPerms.length} valid permission${validPerms.length > 1 ? "s" : ""} configured`
|
|
505
|
+
};
|
|
506
|
+
} catch {
|
|
507
|
+
return { name: this.name, status: "fail", score: 0, weight: this.weight, message: "Failed to read permissions" };
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const skillsCheck = {
|
|
513
|
+
name: "Skills",
|
|
514
|
+
weight: 6,
|
|
515
|
+
async check() {
|
|
516
|
+
try {
|
|
517
|
+
if (!existsSync(CCJK_SKILLS_DIR)) {
|
|
518
|
+
return {
|
|
519
|
+
name: this.name,
|
|
520
|
+
status: "warn",
|
|
521
|
+
score: 20,
|
|
522
|
+
weight: this.weight,
|
|
523
|
+
message: "No skills directory found",
|
|
524
|
+
fix: "Install skills to enhance Claude Code",
|
|
525
|
+
command: "ccjk ccjk:skills"
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
const files = readdirSync(CCJK_SKILLS_DIR).filter((f) => f.endsWith(".md"));
|
|
529
|
+
const skillCount = files.length;
|
|
530
|
+
if (skillCount === 0) {
|
|
531
|
+
return {
|
|
532
|
+
name: this.name,
|
|
533
|
+
status: "warn",
|
|
534
|
+
score: 20,
|
|
535
|
+
weight: this.weight,
|
|
536
|
+
message: "No skills installed",
|
|
537
|
+
fix: "Install skills based on your project",
|
|
538
|
+
command: "ccjk ccjk:skills"
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
const score = Math.min(100, 30 + skillCount * 10);
|
|
542
|
+
return {
|
|
543
|
+
name: this.name,
|
|
544
|
+
status: score >= 60 ? "pass" : "warn",
|
|
545
|
+
score,
|
|
546
|
+
weight: this.weight,
|
|
547
|
+
message: `${skillCount} skill${skillCount > 1 ? "s" : ""} installed`,
|
|
548
|
+
details: files.slice(0, 8).map((f) => ` ${f.replace(".md", "")}`),
|
|
549
|
+
...skillCount < 5 && { fix: "Install more project-specific skills", command: "ccjk ccjk:skills" }
|
|
550
|
+
};
|
|
551
|
+
} catch {
|
|
552
|
+
return { name: this.name, status: "fail", score: 0, weight: this.weight, message: "Failed to read skills" };
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
const DEFAULT_CHECKS = [
|
|
558
|
+
mcpCheck,
|
|
559
|
+
skillsCheck,
|
|
560
|
+
claudeMdCheck,
|
|
561
|
+
modelCheck,
|
|
562
|
+
agentsCheck,
|
|
563
|
+
permissionsCheck
|
|
564
|
+
];
|
|
565
|
+
function calculateGrade(score) {
|
|
566
|
+
if (score >= 95) return "S";
|
|
567
|
+
if (score >= 80) return "A";
|
|
568
|
+
if (score >= 65) return "B";
|
|
569
|
+
if (score >= 50) return "C";
|
|
570
|
+
if (score >= 30) return "D";
|
|
571
|
+
return "F";
|
|
572
|
+
}
|
|
573
|
+
function generateRecommendations(results) {
|
|
574
|
+
const recs = [];
|
|
575
|
+
for (const r of results) {
|
|
576
|
+
if (r.status === "fail" && r.command) {
|
|
577
|
+
recs.push({
|
|
578
|
+
priority: "high",
|
|
579
|
+
title: `Fix: ${r.name}`,
|
|
580
|
+
description: r.fix || r.message,
|
|
581
|
+
command: r.command,
|
|
582
|
+
category: mapCategory(r.name)
|
|
583
|
+
});
|
|
584
|
+
} else if (r.status === "warn" && r.command) {
|
|
585
|
+
recs.push({
|
|
586
|
+
priority: "medium",
|
|
587
|
+
title: `Improve: ${r.name}`,
|
|
588
|
+
description: r.fix || r.message,
|
|
589
|
+
command: r.command,
|
|
590
|
+
category: mapCategory(r.name)
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
595
|
+
recs.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
596
|
+
return recs;
|
|
597
|
+
}
|
|
598
|
+
function mapCategory(name) {
|
|
599
|
+
const lower = name.toLowerCase();
|
|
600
|
+
if (lower.includes("mcp")) return "mcp";
|
|
601
|
+
if (lower.includes("skill")) return "skills";
|
|
602
|
+
if (lower.includes("agent")) return "agents";
|
|
603
|
+
if (lower.includes("model") || lower.includes("api")) return "model";
|
|
604
|
+
if (lower.includes("sync")) return "sync";
|
|
605
|
+
if (lower.includes("perm")) return "permissions";
|
|
606
|
+
return "general";
|
|
607
|
+
}
|
|
608
|
+
async function runHealthCheck(checks) {
|
|
609
|
+
const activeChecks = DEFAULT_CHECKS;
|
|
610
|
+
const results = [];
|
|
611
|
+
const promises = activeChecks.map(async (check) => {
|
|
612
|
+
try {
|
|
613
|
+
return await check.check();
|
|
614
|
+
} catch {
|
|
615
|
+
return {
|
|
616
|
+
name: check.name,
|
|
617
|
+
status: "fail",
|
|
618
|
+
score: 0,
|
|
619
|
+
weight: check.weight,
|
|
620
|
+
message: "Check failed unexpectedly"
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
const settled = await Promise.all(promises);
|
|
625
|
+
results.push(...settled);
|
|
626
|
+
const totalWeight = results.reduce((sum, r) => sum + r.weight, 0);
|
|
627
|
+
const weightedSum = results.reduce((sum, r) => sum + r.score * r.weight, 0);
|
|
628
|
+
const totalScore = totalWeight > 0 ? Math.round(weightedSum / totalWeight) : 0;
|
|
629
|
+
return {
|
|
630
|
+
totalScore,
|
|
631
|
+
grade: calculateGrade(totalScore),
|
|
632
|
+
results,
|
|
633
|
+
recommendations: generateRecommendations(results),
|
|
634
|
+
timestamp: Date.now()
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export { analyzeProject as a, getRecommendations as g, runHealthCheck as r };
|
|
@@ -4,7 +4,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
6
|
import { join } from 'pathe';
|
|
7
|
-
import { i18n } from '../chunks/
|
|
7
|
+
import { i18n } from '../chunks/index.mjs';
|
|
8
8
|
|
|
9
9
|
const execAsync = promisify(exec);
|
|
10
10
|
function getClaudePluginDir() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import process__default from 'node:process';
|
|
2
2
|
import ansis from 'ansis';
|
|
3
|
-
import { ensureI18nInitialized, i18n } from '../chunks/
|
|
3
|
+
import { ensureI18nInitialized, i18n } from '../chunks/index.mjs';
|
|
4
4
|
|
|
5
5
|
function handleExitPromptError(error) {
|
|
6
6
|
const isExitError = error instanceof Error && (error.name === "ExitPromptError" || error.message?.includes("ExitPromptError") || error.message?.includes("User force closed the prompt"));
|
|
@@ -22,4 +22,4 @@ function handleGeneralError(error) {
|
|
|
22
22
|
process__default.exit(1);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export {
|
|
25
|
+
export { handleGeneralError as a, handleExitPromptError as h };
|