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,913 +0,0 @@
|
|
|
1
|
-
import ansis from 'ansis';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
|
-
import { i18n } from './index2.mjs';
|
|
4
|
-
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
5
|
-
import { homedir } from 'node:os';
|
|
6
|
-
import process__default from 'node:process';
|
|
7
|
-
import { join, dirname } from 'pathe';
|
|
8
|
-
import { readFile, writeFileAtomic } from './fs-operations.mjs';
|
|
9
|
-
import 'node:url';
|
|
10
|
-
import 'i18next';
|
|
11
|
-
import 'i18next-fs-backend';
|
|
12
|
-
import 'node:crypto';
|
|
13
|
-
import 'node:fs/promises';
|
|
14
|
-
|
|
15
|
-
function detectProjectContext(projectPath = process__default.cwd()) {
|
|
16
|
-
const context = {
|
|
17
|
-
type: "unknown",
|
|
18
|
-
language: "unknown",
|
|
19
|
-
hasTests: false,
|
|
20
|
-
hasDocker: false,
|
|
21
|
-
hasCi: false,
|
|
22
|
-
monorepo: false,
|
|
23
|
-
detectedPatterns: []
|
|
24
|
-
};
|
|
25
|
-
if (!existsSync(projectPath)) {
|
|
26
|
-
return context;
|
|
27
|
-
}
|
|
28
|
-
const files = safeReadDir(projectPath);
|
|
29
|
-
if (files.includes("package.json")) {
|
|
30
|
-
context.type = "nodejs";
|
|
31
|
-
context.language = files.includes("tsconfig.json") ? "typescript" : "javascript";
|
|
32
|
-
context.detectedPatterns.push("package.json");
|
|
33
|
-
if (files.includes("pnpm-lock.yaml")) {
|
|
34
|
-
context.packageManager = "pnpm";
|
|
35
|
-
} else if (files.includes("yarn.lock")) {
|
|
36
|
-
context.packageManager = "yarn";
|
|
37
|
-
} else if (files.includes("bun.lockb")) {
|
|
38
|
-
context.packageManager = "bun";
|
|
39
|
-
} else if (files.includes("package-lock.json")) {
|
|
40
|
-
context.packageManager = "npm";
|
|
41
|
-
}
|
|
42
|
-
const packageJson = readPackageJson(projectPath);
|
|
43
|
-
if (packageJson) {
|
|
44
|
-
context.framework = detectNodeFramework(packageJson);
|
|
45
|
-
}
|
|
46
|
-
if (files.includes("pnpm-workspace.yaml") || files.includes("lerna.json")) {
|
|
47
|
-
context.monorepo = true;
|
|
48
|
-
context.detectedPatterns.push("monorepo");
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (files.includes("pyproject.toml") || files.includes("setup.py") || files.includes("requirements.txt")) {
|
|
52
|
-
context.type = "python";
|
|
53
|
-
context.language = "python";
|
|
54
|
-
context.detectedPatterns.push(files.includes("pyproject.toml") ? "pyproject.toml" : "requirements.txt");
|
|
55
|
-
if (files.includes("poetry.lock")) {
|
|
56
|
-
context.packageManager = "poetry";
|
|
57
|
-
} else if (files.includes("Pipfile.lock")) {
|
|
58
|
-
context.packageManager = "pipenv";
|
|
59
|
-
} else if (files.includes("uv.lock")) {
|
|
60
|
-
context.packageManager = "uv";
|
|
61
|
-
}
|
|
62
|
-
context.framework = detectPythonFramework(projectPath);
|
|
63
|
-
}
|
|
64
|
-
if (files.includes("Cargo.toml")) {
|
|
65
|
-
context.type = "rust";
|
|
66
|
-
context.language = "rust";
|
|
67
|
-
context.packageManager = "cargo";
|
|
68
|
-
context.detectedPatterns.push("Cargo.toml");
|
|
69
|
-
const cargoContent = readFile(join(projectPath, "Cargo.toml"));
|
|
70
|
-
if (cargoContent?.includes("[workspace]")) {
|
|
71
|
-
context.monorepo = true;
|
|
72
|
-
context.detectedPatterns.push("cargo-workspace");
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (files.includes("go.mod")) {
|
|
76
|
-
context.type = "go";
|
|
77
|
-
context.language = "go";
|
|
78
|
-
context.packageManager = "go";
|
|
79
|
-
context.detectedPatterns.push("go.mod");
|
|
80
|
-
}
|
|
81
|
-
if (files.includes("pom.xml") || files.includes("build.gradle") || files.includes("build.gradle.kts")) {
|
|
82
|
-
context.type = "java";
|
|
83
|
-
context.language = "java";
|
|
84
|
-
context.packageManager = files.includes("pom.xml") ? "maven" : "gradle";
|
|
85
|
-
context.detectedPatterns.push(files.includes("pom.xml") ? "pom.xml" : "build.gradle");
|
|
86
|
-
}
|
|
87
|
-
if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".fsproj") || f.endsWith(".sln"))) {
|
|
88
|
-
context.type = "dotnet";
|
|
89
|
-
context.language = files.some((f) => f.endsWith(".fsproj")) ? "fsharp" : "csharp";
|
|
90
|
-
context.packageManager = "dotnet";
|
|
91
|
-
context.detectedPatterns.push(".NET project");
|
|
92
|
-
}
|
|
93
|
-
if (files.includes("Gemfile")) {
|
|
94
|
-
context.type = "ruby";
|
|
95
|
-
context.language = "ruby";
|
|
96
|
-
context.packageManager = "bundler";
|
|
97
|
-
context.detectedPatterns.push("Gemfile");
|
|
98
|
-
if (files.includes("config") && existsSync(join(projectPath, "config", "routes.rb"))) {
|
|
99
|
-
context.framework = "rails";
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (files.includes("composer.json")) {
|
|
103
|
-
context.type = "php";
|
|
104
|
-
context.language = "php";
|
|
105
|
-
context.packageManager = "composer";
|
|
106
|
-
context.detectedPatterns.push("composer.json");
|
|
107
|
-
if (files.includes("artisan")) {
|
|
108
|
-
context.framework = "laravel";
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
context.hasDocker = files.includes("Dockerfile") || files.includes("docker-compose.yml") || files.includes("docker-compose.yaml");
|
|
112
|
-
context.hasCi = files.includes(".github") || files.includes(".gitlab-ci.yml") || files.includes(".circleci");
|
|
113
|
-
context.hasTests = files.includes("tests") || files.includes("test") || files.includes("__tests__") || files.includes("spec");
|
|
114
|
-
if (context.hasDocker)
|
|
115
|
-
context.detectedPatterns.push("docker");
|
|
116
|
-
if (context.hasCi)
|
|
117
|
-
context.detectedPatterns.push("ci/cd");
|
|
118
|
-
if (context.hasTests)
|
|
119
|
-
context.detectedPatterns.push("tests");
|
|
120
|
-
return context;
|
|
121
|
-
}
|
|
122
|
-
function safeReadDir(dirPath) {
|
|
123
|
-
try {
|
|
124
|
-
return readdirSync(dirPath);
|
|
125
|
-
} catch {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function readPackageJson(projectPath) {
|
|
130
|
-
try {
|
|
131
|
-
const content = readFile(join(projectPath, "package.json"));
|
|
132
|
-
if (content) {
|
|
133
|
-
return JSON.parse(content);
|
|
134
|
-
}
|
|
135
|
-
} catch {
|
|
136
|
-
}
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
function detectNodeFramework(packageJson) {
|
|
140
|
-
const deps = {
|
|
141
|
-
...packageJson.dependencies || {},
|
|
142
|
-
...packageJson.devDependencies || {}
|
|
143
|
-
};
|
|
144
|
-
if (deps.next)
|
|
145
|
-
return "nextjs";
|
|
146
|
-
if (deps.nuxt)
|
|
147
|
-
return "nuxt";
|
|
148
|
-
if (deps["@angular/core"])
|
|
149
|
-
return "angular";
|
|
150
|
-
if (deps.vue)
|
|
151
|
-
return "vue";
|
|
152
|
-
if (deps.react)
|
|
153
|
-
return "react";
|
|
154
|
-
if (deps.svelte)
|
|
155
|
-
return "svelte";
|
|
156
|
-
if (deps.express)
|
|
157
|
-
return "express";
|
|
158
|
-
if (deps.fastify)
|
|
159
|
-
return "fastify";
|
|
160
|
-
if (deps.nestjs || deps["@nestjs/core"])
|
|
161
|
-
return "nestjs";
|
|
162
|
-
if (deps.hono)
|
|
163
|
-
return "hono";
|
|
164
|
-
if (deps.elysia)
|
|
165
|
-
return "elysia";
|
|
166
|
-
return void 0;
|
|
167
|
-
}
|
|
168
|
-
function detectPythonFramework(projectPath) {
|
|
169
|
-
const files = safeReadDir(projectPath);
|
|
170
|
-
if (files.includes("manage.py"))
|
|
171
|
-
return "django";
|
|
172
|
-
if (files.includes("app.py") || files.includes("main.py")) {
|
|
173
|
-
const content = readFile(join(projectPath, files.includes("app.py") ? "app.py" : "main.py"));
|
|
174
|
-
if (content) {
|
|
175
|
-
if (content.includes("FastAPI"))
|
|
176
|
-
return "fastapi";
|
|
177
|
-
if (content.includes("Flask"))
|
|
178
|
-
return "flask";
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return void 0;
|
|
182
|
-
}
|
|
183
|
-
function getContextRules() {
|
|
184
|
-
return [
|
|
185
|
-
// Coding Style Rules
|
|
186
|
-
{
|
|
187
|
-
id: "prefer-functional",
|
|
188
|
-
name: "Prefer Functional Style",
|
|
189
|
-
nameZh: "\u4F18\u5148\u51FD\u6570\u5F0F\u98CE\u683C",
|
|
190
|
-
description: "Prefer functional programming patterns over imperative",
|
|
191
|
-
descriptionZh: "\u4F18\u5148\u4F7F\u7528\u51FD\u6570\u5F0F\u7F16\u7A0B\u6A21\u5F0F\u800C\u975E\u547D\u4EE4\u5F0F",
|
|
192
|
-
content: `## Coding Style
|
|
193
|
-
- Prefer functional programming patterns (map, filter, reduce) over imperative loops
|
|
194
|
-
- Use pure functions where possible
|
|
195
|
-
- Avoid side effects in functions`,
|
|
196
|
-
contentZh: `## \u7F16\u7801\u98CE\u683C
|
|
197
|
-
- \u4F18\u5148\u4F7F\u7528\u51FD\u6570\u5F0F\u7F16\u7A0B\u6A21\u5F0F\uFF08map\u3001filter\u3001reduce\uFF09\u800C\u975E\u547D\u4EE4\u5F0F\u5FAA\u73AF
|
|
198
|
-
- \u5C3D\u53EF\u80FD\u4F7F\u7528\u7EAF\u51FD\u6570
|
|
199
|
-
- \u907F\u514D\u51FD\u6570\u4E2D\u7684\u526F\u4F5C\u7528`,
|
|
200
|
-
category: "coding",
|
|
201
|
-
applicableTo: ["nodejs", "python", "rust", "go", "java", "dotnet", "ruby", "php", "unknown"]
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
id: "explicit-types",
|
|
205
|
-
name: "Explicit Type Annotations",
|
|
206
|
-
nameZh: "\u663E\u5F0F\u7C7B\u578B\u6CE8\u89E3",
|
|
207
|
-
description: "Always use explicit type annotations",
|
|
208
|
-
descriptionZh: "\u59CB\u7EC8\u4F7F\u7528\u663E\u5F0F\u7C7B\u578B\u6CE8\u89E3",
|
|
209
|
-
content: `## Type Safety
|
|
210
|
-
- Always use explicit type annotations for function parameters and return types
|
|
211
|
-
- Avoid using 'any' type
|
|
212
|
-
- Use strict null checks`,
|
|
213
|
-
contentZh: `## \u7C7B\u578B\u5B89\u5168
|
|
214
|
-
- \u59CB\u7EC8\u4E3A\u51FD\u6570\u53C2\u6570\u548C\u8FD4\u56DE\u7C7B\u578B\u4F7F\u7528\u663E\u5F0F\u7C7B\u578B\u6CE8\u89E3
|
|
215
|
-
- \u907F\u514D\u4F7F\u7528 'any' \u7C7B\u578B
|
|
216
|
-
- \u4F7F\u7528\u4E25\u683C\u7684\u7A7A\u503C\u68C0\u67E5`,
|
|
217
|
-
category: "coding",
|
|
218
|
-
applicableTo: ["nodejs", "python", "rust", "java", "dotnet"]
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
id: "error-handling",
|
|
222
|
-
name: "Comprehensive Error Handling",
|
|
223
|
-
nameZh: "\u5168\u9762\u7684\u9519\u8BEF\u5904\u7406",
|
|
224
|
-
description: "Always handle errors explicitly",
|
|
225
|
-
descriptionZh: "\u59CB\u7EC8\u663E\u5F0F\u5904\u7406\u9519\u8BEF",
|
|
226
|
-
content: `## Error Handling
|
|
227
|
-
- Always handle errors explicitly, never ignore them
|
|
228
|
-
- Use try-catch blocks for async operations
|
|
229
|
-
- Provide meaningful error messages
|
|
230
|
-
- Log errors with context information`,
|
|
231
|
-
contentZh: `## \u9519\u8BEF\u5904\u7406
|
|
232
|
-
- \u59CB\u7EC8\u663E\u5F0F\u5904\u7406\u9519\u8BEF\uFF0C\u4E0D\u8981\u5FFD\u7565
|
|
233
|
-
- \u5BF9\u5F02\u6B65\u64CD\u4F5C\u4F7F\u7528 try-catch \u5757
|
|
234
|
-
- \u63D0\u4F9B\u6709\u610F\u4E49\u7684\u9519\u8BEF\u6D88\u606F
|
|
235
|
-
- \u8BB0\u5F55\u5E26\u6709\u4E0A\u4E0B\u6587\u4FE1\u606F\u7684\u9519\u8BEF`,
|
|
236
|
-
category: "coding",
|
|
237
|
-
applicableTo: ["nodejs", "python", "rust", "go", "java", "dotnet", "ruby", "php", "unknown"]
|
|
238
|
-
},
|
|
239
|
-
// Testing Rules
|
|
240
|
-
{
|
|
241
|
-
id: "test-first",
|
|
242
|
-
name: "Test-First Development",
|
|
243
|
-
nameZh: "\u6D4B\u8BD5\u4F18\u5148\u5F00\u53D1",
|
|
244
|
-
description: "Write tests before implementation",
|
|
245
|
-
descriptionZh: "\u5728\u5B9E\u73B0\u4E4B\u524D\u7F16\u5199\u6D4B\u8BD5",
|
|
246
|
-
content: `## Testing
|
|
247
|
-
- Write tests before implementing features (TDD)
|
|
248
|
-
- Each function should have corresponding unit tests
|
|
249
|
-
- Use descriptive test names that explain the expected behavior`,
|
|
250
|
-
contentZh: `## \u6D4B\u8BD5
|
|
251
|
-
- \u5728\u5B9E\u73B0\u529F\u80FD\u4E4B\u524D\u7F16\u5199\u6D4B\u8BD5\uFF08TDD\uFF09
|
|
252
|
-
- \u6BCF\u4E2A\u51FD\u6570\u90FD\u5E94\u6709\u5BF9\u5E94\u7684\u5355\u5143\u6D4B\u8BD5
|
|
253
|
-
- \u4F7F\u7528\u63CF\u8FF0\u6027\u7684\u6D4B\u8BD5\u540D\u79F0\u6765\u89E3\u91CA\u9884\u671F\u884C\u4E3A`,
|
|
254
|
-
category: "testing",
|
|
255
|
-
applicableTo: ["nodejs", "python", "rust", "go", "java", "dotnet", "ruby", "php", "unknown"]
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
id: "high-coverage",
|
|
259
|
-
name: "High Test Coverage",
|
|
260
|
-
nameZh: "\u9AD8\u6D4B\u8BD5\u8986\u76D6\u7387",
|
|
261
|
-
description: "Maintain high test coverage",
|
|
262
|
-
descriptionZh: "\u4FDD\u6301\u9AD8\u6D4B\u8BD5\u8986\u76D6\u7387",
|
|
263
|
-
content: `## Test Coverage
|
|
264
|
-
- Maintain at least 80% code coverage
|
|
265
|
-
- Cover edge cases and error scenarios
|
|
266
|
-
- Include integration tests for critical paths`,
|
|
267
|
-
contentZh: `## \u6D4B\u8BD5\u8986\u76D6\u7387
|
|
268
|
-
- \u4FDD\u6301\u81F3\u5C11 80% \u7684\u4EE3\u7801\u8986\u76D6\u7387
|
|
269
|
-
- \u8986\u76D6\u8FB9\u754C\u60C5\u51B5\u548C\u9519\u8BEF\u573A\u666F
|
|
270
|
-
- \u4E3A\u5173\u952E\u8DEF\u5F84\u5305\u542B\u96C6\u6210\u6D4B\u8BD5`,
|
|
271
|
-
category: "testing",
|
|
272
|
-
applicableTo: ["nodejs", "python", "rust", "go", "java", "dotnet", "ruby", "php", "unknown"]
|
|
273
|
-
},
|
|
274
|
-
// Documentation Rules
|
|
275
|
-
{
|
|
276
|
-
id: "doc-comments",
|
|
277
|
-
name: "Documentation Comments",
|
|
278
|
-
nameZh: "\u6587\u6863\u6CE8\u91CA",
|
|
279
|
-
description: "Add documentation comments to all public APIs",
|
|
280
|
-
descriptionZh: "\u4E3A\u6240\u6709\u516C\u5171 API \u6DFB\u52A0\u6587\u6863\u6CE8\u91CA",
|
|
281
|
-
content: `## Documentation
|
|
282
|
-
- Add JSDoc/docstring comments to all public functions and classes
|
|
283
|
-
- Include parameter descriptions and return value documentation
|
|
284
|
-
- Add usage examples for complex functions`,
|
|
285
|
-
contentZh: `## \u6587\u6863
|
|
286
|
-
- \u4E3A\u6240\u6709\u516C\u5171\u51FD\u6570\u548C\u7C7B\u6DFB\u52A0 JSDoc/docstring \u6CE8\u91CA
|
|
287
|
-
- \u5305\u542B\u53C2\u6570\u63CF\u8FF0\u548C\u8FD4\u56DE\u503C\u6587\u6863
|
|
288
|
-
- \u4E3A\u590D\u6742\u51FD\u6570\u6DFB\u52A0\u4F7F\u7528\u793A\u4F8B`,
|
|
289
|
-
category: "docs",
|
|
290
|
-
applicableTo: ["nodejs", "python", "rust", "go", "java", "dotnet", "ruby", "php", "unknown"]
|
|
291
|
-
},
|
|
292
|
-
// Workflow Rules
|
|
293
|
-
{
|
|
294
|
-
id: "conventional-commits",
|
|
295
|
-
name: "Conventional Commits",
|
|
296
|
-
nameZh: "\u7EA6\u5B9A\u5F0F\u63D0\u4EA4",
|
|
297
|
-
description: "Use conventional commit messages",
|
|
298
|
-
descriptionZh: "\u4F7F\u7528\u7EA6\u5B9A\u5F0F\u63D0\u4EA4\u6D88\u606F",
|
|
299
|
-
content: `## Git Workflow
|
|
300
|
-
- Use conventional commit format: type(scope): description
|
|
301
|
-
- Types: feat, fix, docs, style, refactor, test, chore
|
|
302
|
-
- Keep commits atomic and focused`,
|
|
303
|
-
contentZh: `## Git \u5DE5\u4F5C\u6D41
|
|
304
|
-
- \u4F7F\u7528\u7EA6\u5B9A\u5F0F\u63D0\u4EA4\u683C\u5F0F\uFF1Atype(scope): description
|
|
305
|
-
- \u7C7B\u578B\uFF1Afeat\u3001fix\u3001docs\u3001style\u3001refactor\u3001test\u3001chore
|
|
306
|
-
- \u4FDD\u6301\u63D0\u4EA4\u539F\u5B50\u5316\u548C\u4E13\u6CE8`,
|
|
307
|
-
category: "workflow",
|
|
308
|
-
applicableTo: ["nodejs", "python", "rust", "go", "java", "dotnet", "ruby", "php", "unknown"]
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
id: "pr-guidelines",
|
|
312
|
-
name: "PR Guidelines",
|
|
313
|
-
nameZh: "PR \u6307\u5357",
|
|
314
|
-
description: "Follow PR best practices",
|
|
315
|
-
descriptionZh: "\u9075\u5FAA PR \u6700\u4F73\u5B9E\u8DF5",
|
|
316
|
-
content: `## Pull Requests
|
|
317
|
-
- Keep PRs small and focused (< 400 lines)
|
|
318
|
-
- Include clear description of changes
|
|
319
|
-
- Add screenshots for UI changes
|
|
320
|
-
- Request reviews from relevant team members`,
|
|
321
|
-
contentZh: `## Pull Requests
|
|
322
|
-
- \u4FDD\u6301 PR \u5C0F\u800C\u4E13\u6CE8\uFF08< 400 \u884C\uFF09
|
|
323
|
-
- \u5305\u542B\u6E05\u6670\u7684\u53D8\u66F4\u63CF\u8FF0
|
|
324
|
-
- \u4E3A UI \u53D8\u66F4\u6DFB\u52A0\u622A\u56FE
|
|
325
|
-
- \u5411\u76F8\u5173\u56E2\u961F\u6210\u5458\u8BF7\u6C42\u5BA1\u67E5`,
|
|
326
|
-
category: "workflow",
|
|
327
|
-
applicableTo: ["nodejs", "python", "rust", "go", "java", "dotnet", "ruby", "php", "unknown"]
|
|
328
|
-
},
|
|
329
|
-
// Security Rules
|
|
330
|
-
{
|
|
331
|
-
id: "security-basics",
|
|
332
|
-
name: "Security Best Practices",
|
|
333
|
-
nameZh: "\u5B89\u5168\u6700\u4F73\u5B9E\u8DF5",
|
|
334
|
-
description: "Follow security best practices",
|
|
335
|
-
descriptionZh: "\u9075\u5FAA\u5B89\u5168\u6700\u4F73\u5B9E\u8DF5",
|
|
336
|
-
content: `## Security
|
|
337
|
-
- Never commit secrets or credentials
|
|
338
|
-
- Validate and sanitize all user inputs
|
|
339
|
-
- Use parameterized queries for database operations
|
|
340
|
-
- Keep dependencies updated`,
|
|
341
|
-
contentZh: `## \u5B89\u5168
|
|
342
|
-
- \u6C38\u8FDC\u4E0D\u8981\u63D0\u4EA4\u5BC6\u94A5\u6216\u51ED\u8BC1
|
|
343
|
-
- \u9A8C\u8BC1\u548C\u6E05\u7406\u6240\u6709\u7528\u6237\u8F93\u5165
|
|
344
|
-
- \u5BF9\u6570\u636E\u5E93\u64CD\u4F5C\u4F7F\u7528\u53C2\u6570\u5316\u67E5\u8BE2
|
|
345
|
-
- \u4FDD\u6301\u4F9D\u8D56\u9879\u66F4\u65B0`,
|
|
346
|
-
category: "security",
|
|
347
|
-
applicableTo: ["nodejs", "python", "rust", "go", "java", "dotnet", "ruby", "php", "unknown"]
|
|
348
|
-
}
|
|
349
|
-
];
|
|
350
|
-
}
|
|
351
|
-
function getApplicableRules(projectType) {
|
|
352
|
-
return getContextRules().filter(
|
|
353
|
-
(rule) => rule.applicableTo.includes(projectType) || rule.applicableTo.includes("unknown")
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
function getContextFiles(projectPath = process__default.cwd()) {
|
|
357
|
-
const home = homedir();
|
|
358
|
-
const files = [];
|
|
359
|
-
const globalPath = join(home, ".claude", "CLAUDE.md");
|
|
360
|
-
files.push({
|
|
361
|
-
path: globalPath,
|
|
362
|
-
type: "global",
|
|
363
|
-
exists: existsSync(globalPath),
|
|
364
|
-
...getFileStats(globalPath)
|
|
365
|
-
});
|
|
366
|
-
const projectClaudeMd = join(projectPath, "CLAUDE.md");
|
|
367
|
-
files.push({
|
|
368
|
-
path: projectClaudeMd,
|
|
369
|
-
type: "project",
|
|
370
|
-
exists: existsSync(projectClaudeMd),
|
|
371
|
-
...getFileStats(projectClaudeMd)
|
|
372
|
-
});
|
|
373
|
-
const localPath = join(projectPath, ".claude", "CLAUDE.md");
|
|
374
|
-
files.push({
|
|
375
|
-
path: localPath,
|
|
376
|
-
type: "local",
|
|
377
|
-
exists: existsSync(localPath),
|
|
378
|
-
...getFileStats(localPath)
|
|
379
|
-
});
|
|
380
|
-
return files;
|
|
381
|
-
}
|
|
382
|
-
function getFileStats(filePath) {
|
|
383
|
-
try {
|
|
384
|
-
if (existsSync(filePath)) {
|
|
385
|
-
const stats = statSync(filePath);
|
|
386
|
-
return {
|
|
387
|
-
size: stats.size,
|
|
388
|
-
lastModified: stats.mtime
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
} catch {
|
|
392
|
-
}
|
|
393
|
-
return {};
|
|
394
|
-
}
|
|
395
|
-
function readContextFile(filePath) {
|
|
396
|
-
return readFile(filePath);
|
|
397
|
-
}
|
|
398
|
-
async function writeContextFile(filePath, content) {
|
|
399
|
-
try {
|
|
400
|
-
const dir = dirname(filePath);
|
|
401
|
-
if (!existsSync(dir)) {
|
|
402
|
-
const { mkdirSync } = await import('node:fs');
|
|
403
|
-
mkdirSync(dir, { recursive: true });
|
|
404
|
-
}
|
|
405
|
-
await writeFileAtomic(filePath, content);
|
|
406
|
-
return true;
|
|
407
|
-
} catch {
|
|
408
|
-
return false;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
function generateContextContent(context, selectedRules, lang = "en") {
|
|
412
|
-
const isZh = lang === "zh-CN";
|
|
413
|
-
const rules = getContextRules().filter((r) => selectedRules.includes(r.id));
|
|
414
|
-
const lines = [];
|
|
415
|
-
lines.push(isZh ? "# \u9879\u76EE\u89C4\u5219" : "# Project Rules");
|
|
416
|
-
lines.push("");
|
|
417
|
-
lines.push(isZh ? "## \u9879\u76EE\u4FE1\u606F" : "## Project Information");
|
|
418
|
-
lines.push(`- ${isZh ? "\u7C7B\u578B" : "Type"}: ${context.type}`);
|
|
419
|
-
lines.push(`- ${isZh ? "\u8BED\u8A00" : "Language"}: ${context.language}`);
|
|
420
|
-
if (context.framework) {
|
|
421
|
-
lines.push(`- ${isZh ? "\u6846\u67B6" : "Framework"}: ${context.framework}`);
|
|
422
|
-
}
|
|
423
|
-
if (context.packageManager) {
|
|
424
|
-
lines.push(`- ${isZh ? "\u5305\u7BA1\u7406\u5668" : "Package Manager"}: ${context.packageManager}`);
|
|
425
|
-
}
|
|
426
|
-
lines.push("");
|
|
427
|
-
for (const rule of rules) {
|
|
428
|
-
lines.push(isZh ? rule.contentZh : rule.content);
|
|
429
|
-
lines.push("");
|
|
430
|
-
}
|
|
431
|
-
lines.push("---");
|
|
432
|
-
lines.push(isZh ? `*\u7531 CCJK \u81EA\u52A8\u751F\u6210\u4E8E ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}*` : `*Auto-generated by CCJK on ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}*`);
|
|
433
|
-
return lines.join("\n");
|
|
434
|
-
}
|
|
435
|
-
function getRecommendedRules(context) {
|
|
436
|
-
const recommended = [];
|
|
437
|
-
recommended.push("error-handling", "security-basics");
|
|
438
|
-
if (["nodejs", "python", "rust", "java", "dotnet"].includes(context.type)) {
|
|
439
|
-
if (context.language === "typescript" || context.type === "rust" || context.type === "java") {
|
|
440
|
-
recommended.push("explicit-types");
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
if (context.hasTests) {
|
|
444
|
-
recommended.push("test-first", "high-coverage");
|
|
445
|
-
}
|
|
446
|
-
if (context.hasCi) {
|
|
447
|
-
recommended.push("conventional-commits", "pr-guidelines");
|
|
448
|
-
}
|
|
449
|
-
recommended.push("doc-comments");
|
|
450
|
-
return recommended;
|
|
451
|
-
}
|
|
452
|
-
function mergeContextContent(existingContent, newRules, lang = "en") {
|
|
453
|
-
const isZh = lang === "zh-CN";
|
|
454
|
-
const rules = getContextRules().filter((r) => newRules.includes(r.id));
|
|
455
|
-
const existingRuleIds = [];
|
|
456
|
-
for (const rule of getContextRules()) {
|
|
457
|
-
const marker = isZh ? rule.contentZh.split("\n")[0] : rule.content.split("\n")[0];
|
|
458
|
-
if (existingContent.includes(marker)) {
|
|
459
|
-
existingRuleIds.push(rule.id);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
const newRulesToAdd = rules.filter((r) => !existingRuleIds.includes(r.id));
|
|
463
|
-
if (newRulesToAdd.length === 0) {
|
|
464
|
-
return existingContent;
|
|
465
|
-
}
|
|
466
|
-
const footerMarker = "---";
|
|
467
|
-
const footerIndex = existingContent.lastIndexOf(footerMarker);
|
|
468
|
-
let content = existingContent;
|
|
469
|
-
const newContent = newRulesToAdd.map((r) => isZh ? r.contentZh : r.content).join("\n\n");
|
|
470
|
-
if (footerIndex > 0) {
|
|
471
|
-
content = `${existingContent.slice(0, footerIndex) + newContent}
|
|
472
|
-
|
|
473
|
-
${existingContent.slice(footerIndex)}`;
|
|
474
|
-
} else {
|
|
475
|
-
content = `${existingContent}
|
|
476
|
-
|
|
477
|
-
${newContent}`;
|
|
478
|
-
}
|
|
479
|
-
return content;
|
|
480
|
-
}
|
|
481
|
-
function getProjectTypeLabel(type, lang = "en") {
|
|
482
|
-
const labels = {
|
|
483
|
-
nodejs: { en: "Node.js", zh: "Node.js" },
|
|
484
|
-
python: { en: "Python", zh: "Python" },
|
|
485
|
-
rust: { en: "Rust", zh: "Rust" },
|
|
486
|
-
go: { en: "Go", zh: "Go" },
|
|
487
|
-
java: { en: "Java", zh: "Java" },
|
|
488
|
-
dotnet: { en: ".NET", zh: ".NET" },
|
|
489
|
-
ruby: { en: "Ruby", zh: "Ruby" },
|
|
490
|
-
php: { en: "PHP", zh: "PHP" },
|
|
491
|
-
unknown: { en: "Unknown", zh: "\u672A\u77E5" }
|
|
492
|
-
};
|
|
493
|
-
return lang === "zh-CN" ? labels[type].zh : labels[type].en;
|
|
494
|
-
}
|
|
495
|
-
function getContextFileTypeLabel(type, lang = "en") {
|
|
496
|
-
const labels = {
|
|
497
|
-
global: { en: "Global", zh: "\u5168\u5C40" },
|
|
498
|
-
project: { en: "Project", zh: "\u9879\u76EE" },
|
|
499
|
-
local: { en: "Local", zh: "\u672C\u5730" }
|
|
500
|
-
};
|
|
501
|
-
return lang === "zh-CN" ? labels[type].zh : labels[type].en;
|
|
502
|
-
}
|
|
503
|
-
function formatFileSize(bytes) {
|
|
504
|
-
if (bytes < 1024)
|
|
505
|
-
return `${bytes} B`;
|
|
506
|
-
if (bytes < 1024 * 1024)
|
|
507
|
-
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
508
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
async function showContextMenu() {
|
|
512
|
-
const lang = i18n.language;
|
|
513
|
-
const isZh = lang === "zh-CN";
|
|
514
|
-
console.log(ansis.green.bold(`
|
|
515
|
-
\u{1F4CB} ${isZh ? "\u4E0A\u4E0B\u6587\u7BA1\u7406" : "Context Management"}
|
|
516
|
-
`));
|
|
517
|
-
const context = detectProjectContext();
|
|
518
|
-
displayProjectInfo(context, lang);
|
|
519
|
-
const { action } = await inquirer.prompt([
|
|
520
|
-
{
|
|
521
|
-
type: "list",
|
|
522
|
-
name: "action",
|
|
523
|
-
message: isZh ? "\u9009\u62E9\u64CD\u4F5C" : "Select action",
|
|
524
|
-
choices: [
|
|
525
|
-
{
|
|
526
|
-
name: `\u{1F50D} ${isZh ? "\u67E5\u770B\u4E0A\u4E0B\u6587\u6587\u4EF6" : "View Context Files"}`,
|
|
527
|
-
value: "view"
|
|
528
|
-
},
|
|
529
|
-
{
|
|
530
|
-
name: `\u2728 ${isZh ? "\u81EA\u52A8\u751F\u6210\u89C4\u5219" : "Auto-generate Rules"}`,
|
|
531
|
-
value: "generate"
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
name: `\u{1F4DD} ${isZh ? "\u6DFB\u52A0\u89C4\u5219" : "Add Rules"}`,
|
|
535
|
-
value: "add"
|
|
536
|
-
},
|
|
537
|
-
{
|
|
538
|
-
name: `\u{1F4D6} ${isZh ? "\u67E5\u770B\u53EF\u7528\u89C4\u5219" : "Browse Available Rules"}`,
|
|
539
|
-
value: "browse"
|
|
540
|
-
},
|
|
541
|
-
{
|
|
542
|
-
name: `\u{1F519} ${isZh ? "\u8FD4\u56DE" : "Back"}`,
|
|
543
|
-
value: "back"
|
|
544
|
-
}
|
|
545
|
-
]
|
|
546
|
-
}
|
|
547
|
-
]);
|
|
548
|
-
switch (action) {
|
|
549
|
-
case "view":
|
|
550
|
-
await viewContextFiles(lang);
|
|
551
|
-
break;
|
|
552
|
-
case "generate":
|
|
553
|
-
await generateContextRules(context, lang);
|
|
554
|
-
break;
|
|
555
|
-
case "add":
|
|
556
|
-
await addRulesToContext(context, lang);
|
|
557
|
-
break;
|
|
558
|
-
case "browse":
|
|
559
|
-
await browseRules(context, lang);
|
|
560
|
-
break;
|
|
561
|
-
case "back":
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (action !== "back") {
|
|
565
|
-
await showContextMenu();
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
function displayProjectInfo(context, lang) {
|
|
569
|
-
const isZh = lang === "zh-CN";
|
|
570
|
-
console.log(ansis.dim("\u2500".repeat(50)));
|
|
571
|
-
console.log(ansis.bold(isZh ? "\u68C0\u6D4B\u5230\u7684\u9879\u76EE\u4FE1\u606F\uFF1A" : "Detected Project Info:"));
|
|
572
|
-
const typeLabel = getProjectTypeLabel(context.type, lang);
|
|
573
|
-
console.log(` ${isZh ? "\u7C7B\u578B" : "Type"}: ${ansis.green(typeLabel)}`);
|
|
574
|
-
console.log(` ${isZh ? "\u8BED\u8A00" : "Language"}: ${ansis.green(context.language)}`);
|
|
575
|
-
if (context.framework) {
|
|
576
|
-
console.log(` ${isZh ? "\u6846\u67B6" : "Framework"}: ${ansis.green(context.framework)}`);
|
|
577
|
-
}
|
|
578
|
-
if (context.packageManager) {
|
|
579
|
-
console.log(` ${isZh ? "\u5305\u7BA1\u7406\u5668" : "Package Manager"}: ${ansis.green(context.packageManager)}`);
|
|
580
|
-
}
|
|
581
|
-
const features = [];
|
|
582
|
-
if (context.hasTests)
|
|
583
|
-
features.push(isZh ? "\u6D4B\u8BD5" : "Tests");
|
|
584
|
-
if (context.hasDocker)
|
|
585
|
-
features.push("Docker");
|
|
586
|
-
if (context.hasCi)
|
|
587
|
-
features.push("CI/CD");
|
|
588
|
-
if (context.monorepo)
|
|
589
|
-
features.push("Monorepo");
|
|
590
|
-
if (features.length > 0) {
|
|
591
|
-
console.log(` ${isZh ? "\u7279\u6027" : "Features"}: ${ansis.green(features.join(", "))}`);
|
|
592
|
-
}
|
|
593
|
-
console.log(ansis.dim("\u2500".repeat(50)));
|
|
594
|
-
console.log("");
|
|
595
|
-
}
|
|
596
|
-
function displayContextFile(file, lang) {
|
|
597
|
-
const isZh = lang === "zh-CN";
|
|
598
|
-
const typeLabel = getContextFileTypeLabel(file.type, lang);
|
|
599
|
-
const statusIcon = file.exists ? ansis.green("\u2713") : ansis.dim("\u25CB");
|
|
600
|
-
console.log(` ${statusIcon} ${ansis.bold(typeLabel)}`);
|
|
601
|
-
console.log(` ${ansis.dim(file.path)}`);
|
|
602
|
-
if (file.exists && file.size !== void 0) {
|
|
603
|
-
const sizeStr = formatFileSize(file.size);
|
|
604
|
-
const dateStr = file.lastModified ? file.lastModified.toLocaleDateString() : isZh ? "\u672A\u77E5" : "Unknown";
|
|
605
|
-
console.log(` ${ansis.dim(`${sizeStr} | ${isZh ? "\u4FEE\u6539\u4E8E" : "Modified"}: ${dateStr}`)}`);
|
|
606
|
-
} else if (!file.exists) {
|
|
607
|
-
console.log(` ${ansis.dim(isZh ? "(\u4E0D\u5B58\u5728)" : "(not exists)")}`);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
async function viewContextFiles(lang) {
|
|
611
|
-
const isZh = lang === "zh-CN";
|
|
612
|
-
const files = getContextFiles();
|
|
613
|
-
console.log(ansis.green.bold(`
|
|
614
|
-
\u{1F4C1} ${isZh ? "\u4E0A\u4E0B\u6587\u6587\u4EF6" : "Context Files"}
|
|
615
|
-
`));
|
|
616
|
-
for (const file of files) {
|
|
617
|
-
displayContextFile(file, lang);
|
|
618
|
-
console.log("");
|
|
619
|
-
}
|
|
620
|
-
const existingFiles = files.filter((f) => f.exists);
|
|
621
|
-
if (existingFiles.length > 0) {
|
|
622
|
-
const { viewFile } = await inquirer.prompt([
|
|
623
|
-
{
|
|
624
|
-
type: "list",
|
|
625
|
-
name: "viewFile",
|
|
626
|
-
message: isZh ? "\u67E5\u770B\u6587\u4EF6\u5185\u5BB9\uFF1F" : "View file content?",
|
|
627
|
-
choices: [
|
|
628
|
-
...existingFiles.map((f) => ({
|
|
629
|
-
name: `${getContextFileTypeLabel(f.type, lang)} - ${f.path}`,
|
|
630
|
-
value: f.path
|
|
631
|
-
})),
|
|
632
|
-
{
|
|
633
|
-
name: isZh ? "\u8DF3\u8FC7" : "Skip",
|
|
634
|
-
value: "skip"
|
|
635
|
-
}
|
|
636
|
-
]
|
|
637
|
-
}
|
|
638
|
-
]);
|
|
639
|
-
if (viewFile !== "skip") {
|
|
640
|
-
const content = readContextFile(viewFile);
|
|
641
|
-
if (content) {
|
|
642
|
-
console.log(ansis.dim(`
|
|
643
|
-
${"\u2500".repeat(50)}`));
|
|
644
|
-
console.log(content);
|
|
645
|
-
console.log(ansis.dim(`${"\u2500".repeat(50)}
|
|
646
|
-
`));
|
|
647
|
-
} else {
|
|
648
|
-
console.log(ansis.yellow(isZh ? "\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9" : "Unable to read file content"));
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
async function generateContextRules(context, lang) {
|
|
654
|
-
const isZh = lang === "zh-CN";
|
|
655
|
-
console.log(ansis.green.bold(`
|
|
656
|
-
\u2728 ${isZh ? "\u81EA\u52A8\u751F\u6210\u89C4\u5219" : "Auto-generate Rules"}
|
|
657
|
-
`));
|
|
658
|
-
const recommendedIds = getRecommendedRules(context);
|
|
659
|
-
const applicableRules = getApplicableRules(context.type);
|
|
660
|
-
const { selectedRules } = await inquirer.prompt([
|
|
661
|
-
{
|
|
662
|
-
type: "checkbox",
|
|
663
|
-
name: "selectedRules",
|
|
664
|
-
message: isZh ? "\u9009\u62E9\u8981\u5E94\u7528\u7684\u89C4\u5219\uFF08\u63A8\u8350\u89C4\u5219\u5DF2\u9884\u9009\uFF09" : "Select rules to apply (recommended rules pre-selected)",
|
|
665
|
-
choices: applicableRules.map((rule) => ({
|
|
666
|
-
name: `${recommendedIds.includes(rule.id) ? ansis.green("\u2605") : " "} ${isZh ? rule.nameZh : rule.name} - ${ansis.dim(isZh ? rule.descriptionZh : rule.description)}`,
|
|
667
|
-
value: rule.id,
|
|
668
|
-
checked: recommendedIds.includes(rule.id)
|
|
669
|
-
}))
|
|
670
|
-
}
|
|
671
|
-
]);
|
|
672
|
-
if (selectedRules.length === 0) {
|
|
673
|
-
console.log(ansis.yellow(isZh ? "\u672A\u9009\u62E9\u4EFB\u4F55\u89C4\u5219" : "No rules selected"));
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
const { location } = await inquirer.prompt([
|
|
677
|
-
{
|
|
678
|
-
type: "list",
|
|
679
|
-
name: "location",
|
|
680
|
-
message: isZh ? "\u4FDD\u5B58\u4F4D\u7F6E" : "Save location",
|
|
681
|
-
choices: [
|
|
682
|
-
{
|
|
683
|
-
name: `${isZh ? "\u9879\u76EE\u6839\u76EE\u5F55" : "Project root"} (CLAUDE.md)`,
|
|
684
|
-
value: "project"
|
|
685
|
-
},
|
|
686
|
-
{
|
|
687
|
-
name: `${isZh ? "\u672C\u5730\u76EE\u5F55" : "Local directory"} (.claude/CLAUDE.md)`,
|
|
688
|
-
value: "local"
|
|
689
|
-
},
|
|
690
|
-
{
|
|
691
|
-
name: `${isZh ? "\u5168\u5C40\u76EE\u5F55" : "Global directory"} (~/.claude/CLAUDE.md)`,
|
|
692
|
-
value: "global"
|
|
693
|
-
}
|
|
694
|
-
],
|
|
695
|
-
default: "project"
|
|
696
|
-
}
|
|
697
|
-
]);
|
|
698
|
-
const files = getContextFiles();
|
|
699
|
-
const targetFile = files.find((f) => f.type === location);
|
|
700
|
-
if (!targetFile) {
|
|
701
|
-
console.log(ansis.red(isZh ? "\u65E0\u6CD5\u786E\u5B9A\u76EE\u6807\u8DEF\u5F84" : "Unable to determine target path"));
|
|
702
|
-
return;
|
|
703
|
-
}
|
|
704
|
-
if (targetFile.exists) {
|
|
705
|
-
const { overwrite } = await inquirer.prompt([
|
|
706
|
-
{
|
|
707
|
-
type: "list",
|
|
708
|
-
name: "overwrite",
|
|
709
|
-
message: isZh ? "\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u5982\u4F55\u5904\u7406\uFF1F" : "File exists, how to proceed?",
|
|
710
|
-
choices: [
|
|
711
|
-
{
|
|
712
|
-
name: isZh ? "\u5408\u5E76\uFF08\u6DFB\u52A0\u65B0\u89C4\u5219\uFF09" : "Merge (add new rules)",
|
|
713
|
-
value: "merge"
|
|
714
|
-
},
|
|
715
|
-
{
|
|
716
|
-
name: isZh ? "\u8986\u76D6" : "Overwrite",
|
|
717
|
-
value: "overwrite"
|
|
718
|
-
},
|
|
719
|
-
{
|
|
720
|
-
name: isZh ? "\u53D6\u6D88" : "Cancel",
|
|
721
|
-
value: "cancel"
|
|
722
|
-
}
|
|
723
|
-
]
|
|
724
|
-
}
|
|
725
|
-
]);
|
|
726
|
-
if (overwrite === "cancel") {
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
if (overwrite === "merge") {
|
|
730
|
-
const existingContent = readContextFile(targetFile.path);
|
|
731
|
-
if (existingContent) {
|
|
732
|
-
const mergedContent = mergeContextContent(existingContent, selectedRules, lang);
|
|
733
|
-
const success2 = await writeContextFile(targetFile.path, mergedContent);
|
|
734
|
-
if (success2) {
|
|
735
|
-
console.log(ansis.green(`
|
|
736
|
-
\u2705 ${isZh ? "\u89C4\u5219\u5DF2\u5408\u5E76\u5230" : "Rules merged to"}: ${targetFile.path}`));
|
|
737
|
-
} else {
|
|
738
|
-
console.log(ansis.red(`
|
|
739
|
-
\u274C ${isZh ? "\u5199\u5165\u5931\u8D25" : "Write failed"}`));
|
|
740
|
-
}
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
const content = generateContextContent(context, selectedRules, lang);
|
|
746
|
-
const success = await writeContextFile(targetFile.path, content);
|
|
747
|
-
if (success) {
|
|
748
|
-
console.log(ansis.green(`
|
|
749
|
-
\u2705 ${isZh ? "\u5DF2\u751F\u6210" : "Generated"}: ${targetFile.path}`));
|
|
750
|
-
} else {
|
|
751
|
-
console.log(ansis.red(`
|
|
752
|
-
\u274C ${isZh ? "\u5199\u5165\u5931\u8D25" : "Write failed"}`));
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
async function addRulesToContext(context, lang) {
|
|
756
|
-
const isZh = lang === "zh-CN";
|
|
757
|
-
console.log(ansis.green.bold(`
|
|
758
|
-
\u{1F4DD} ${isZh ? "\u6DFB\u52A0\u89C4\u5219" : "Add Rules"}
|
|
759
|
-
`));
|
|
760
|
-
const applicableRules = getApplicableRules(context.type);
|
|
761
|
-
const { selectedRules } = await inquirer.prompt([
|
|
762
|
-
{
|
|
763
|
-
type: "checkbox",
|
|
764
|
-
name: "selectedRules",
|
|
765
|
-
message: isZh ? "\u9009\u62E9\u8981\u6DFB\u52A0\u7684\u89C4\u5219" : "Select rules to add",
|
|
766
|
-
choices: applicableRules.map((rule) => ({
|
|
767
|
-
name: `${isZh ? rule.nameZh : rule.name} - ${ansis.dim(isZh ? rule.descriptionZh : rule.description)}`,
|
|
768
|
-
value: rule.id
|
|
769
|
-
}))
|
|
770
|
-
}
|
|
771
|
-
]);
|
|
772
|
-
if (selectedRules.length === 0) {
|
|
773
|
-
console.log(ansis.yellow(isZh ? "\u672A\u9009\u62E9\u4EFB\u4F55\u89C4\u5219" : "No rules selected"));
|
|
774
|
-
return;
|
|
775
|
-
}
|
|
776
|
-
const files = getContextFiles();
|
|
777
|
-
const existingFiles = files.filter((f) => f.exists);
|
|
778
|
-
let targetPath;
|
|
779
|
-
if (existingFiles.length > 0) {
|
|
780
|
-
const { target } = await inquirer.prompt([
|
|
781
|
-
{
|
|
782
|
-
type: "list",
|
|
783
|
-
name: "target",
|
|
784
|
-
message: isZh ? "\u6DFB\u52A0\u5230\u54EA\u4E2A\u6587\u4EF6\uFF1F" : "Add to which file?",
|
|
785
|
-
choices: [
|
|
786
|
-
...existingFiles.map((f) => ({
|
|
787
|
-
name: `${getContextFileTypeLabel(f.type, lang)} - ${f.path}`,
|
|
788
|
-
value: f.path
|
|
789
|
-
})),
|
|
790
|
-
{
|
|
791
|
-
name: isZh ? "\u521B\u5EFA\u65B0\u6587\u4EF6" : "Create new file",
|
|
792
|
-
value: "new"
|
|
793
|
-
}
|
|
794
|
-
]
|
|
795
|
-
}
|
|
796
|
-
]);
|
|
797
|
-
if (target === "new") {
|
|
798
|
-
const { location } = await inquirer.prompt([
|
|
799
|
-
{
|
|
800
|
-
type: "list",
|
|
801
|
-
name: "location",
|
|
802
|
-
message: isZh ? "\u4FDD\u5B58\u4F4D\u7F6E" : "Save location",
|
|
803
|
-
choices: [
|
|
804
|
-
{ name: `${isZh ? "\u9879\u76EE\u6839\u76EE\u5F55" : "Project root"} (CLAUDE.md)`, value: "project" },
|
|
805
|
-
{ name: `${isZh ? "\u672C\u5730\u76EE\u5F55" : "Local directory"} (.claude/CLAUDE.md)`, value: "local" },
|
|
806
|
-
{ name: `${isZh ? "\u5168\u5C40\u76EE\u5F55" : "Global directory"} (~/.claude/CLAUDE.md)`, value: "global" }
|
|
807
|
-
]
|
|
808
|
-
}
|
|
809
|
-
]);
|
|
810
|
-
targetPath = files.find((f) => f.type === location)?.path || "";
|
|
811
|
-
} else {
|
|
812
|
-
targetPath = target;
|
|
813
|
-
}
|
|
814
|
-
} else {
|
|
815
|
-
const { location } = await inquirer.prompt([
|
|
816
|
-
{
|
|
817
|
-
type: "list",
|
|
818
|
-
name: "location",
|
|
819
|
-
message: isZh ? "\u4FDD\u5B58\u4F4D\u7F6E" : "Save location",
|
|
820
|
-
choices: [
|
|
821
|
-
{ name: `${isZh ? "\u9879\u76EE\u6839\u76EE\u5F55" : "Project root"} (CLAUDE.md)`, value: "project" },
|
|
822
|
-
{ name: `${isZh ? "\u672C\u5730\u76EE\u5F55" : "Local directory"} (.claude/CLAUDE.md)`, value: "local" },
|
|
823
|
-
{ name: `${isZh ? "\u5168\u5C40\u76EE\u5F55" : "Global directory"} (~/.claude/CLAUDE.md)`, value: "global" }
|
|
824
|
-
]
|
|
825
|
-
}
|
|
826
|
-
]);
|
|
827
|
-
targetPath = files.find((f) => f.type === location)?.path || "";
|
|
828
|
-
}
|
|
829
|
-
if (!targetPath) {
|
|
830
|
-
console.log(ansis.red(isZh ? "\u65E0\u6CD5\u786E\u5B9A\u76EE\u6807\u8DEF\u5F84" : "Unable to determine target path"));
|
|
831
|
-
return;
|
|
832
|
-
}
|
|
833
|
-
const existingContent = readContextFile(targetPath);
|
|
834
|
-
let finalContent;
|
|
835
|
-
if (existingContent) {
|
|
836
|
-
finalContent = mergeContextContent(existingContent, selectedRules, lang);
|
|
837
|
-
} else {
|
|
838
|
-
finalContent = generateContextContent(context, selectedRules, lang);
|
|
839
|
-
}
|
|
840
|
-
const success = await writeContextFile(targetPath, finalContent);
|
|
841
|
-
if (success) {
|
|
842
|
-
console.log(ansis.green(`
|
|
843
|
-
\u2705 ${isZh ? "\u89C4\u5219\u5DF2\u6DFB\u52A0\u5230" : "Rules added to"}: ${targetPath}`));
|
|
844
|
-
} else {
|
|
845
|
-
console.log(ansis.red(`
|
|
846
|
-
\u274C ${isZh ? "\u5199\u5165\u5931\u8D25" : "Write failed"}`));
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
async function browseRules(context, lang) {
|
|
850
|
-
const isZh = lang === "zh-CN";
|
|
851
|
-
console.log(ansis.green.bold(`
|
|
852
|
-
\u{1F4D6} ${isZh ? "\u53EF\u7528\u89C4\u5219" : "Available Rules"}
|
|
853
|
-
`));
|
|
854
|
-
const applicableRules = getApplicableRules(context.type);
|
|
855
|
-
const categories = {};
|
|
856
|
-
for (const rule of applicableRules) {
|
|
857
|
-
if (!categories[rule.category]) {
|
|
858
|
-
categories[rule.category] = [];
|
|
859
|
-
}
|
|
860
|
-
categories[rule.category].push(rule);
|
|
861
|
-
}
|
|
862
|
-
const categoryLabels = {
|
|
863
|
-
coding: { en: "Coding Style", zh: "\u7F16\u7801\u98CE\u683C" },
|
|
864
|
-
testing: { en: "Testing", zh: "\u6D4B\u8BD5" },
|
|
865
|
-
docs: { en: "Documentation", zh: "\u6587\u6863" },
|
|
866
|
-
workflow: { en: "Workflow", zh: "\u5DE5\u4F5C\u6D41" },
|
|
867
|
-
security: { en: "Security", zh: "\u5B89\u5168" }
|
|
868
|
-
};
|
|
869
|
-
for (const [category, rules] of Object.entries(categories)) {
|
|
870
|
-
const label = isZh ? categoryLabels[category]?.zh : categoryLabels[category]?.en;
|
|
871
|
-
console.log(ansis.bold(`
|
|
872
|
-
${label || category}:`));
|
|
873
|
-
for (const rule of rules) {
|
|
874
|
-
const name = isZh ? rule.nameZh : rule.name;
|
|
875
|
-
const desc = isZh ? rule.descriptionZh : rule.description;
|
|
876
|
-
console.log(` ${ansis.green("\u2022")} ${ansis.bold(name)}`);
|
|
877
|
-
console.log(` ${ansis.dim(desc)}`);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
console.log("");
|
|
881
|
-
const { viewRule } = await inquirer.prompt([
|
|
882
|
-
{
|
|
883
|
-
type: "list",
|
|
884
|
-
name: "viewRule",
|
|
885
|
-
message: isZh ? "\u67E5\u770B\u89C4\u5219\u8BE6\u60C5\uFF1F" : "View rule details?",
|
|
886
|
-
choices: [
|
|
887
|
-
...applicableRules.map((r) => ({
|
|
888
|
-
name: isZh ? r.nameZh : r.name,
|
|
889
|
-
value: r.id
|
|
890
|
-
})),
|
|
891
|
-
{
|
|
892
|
-
name: isZh ? "\u8DF3\u8FC7" : "Skip",
|
|
893
|
-
value: "skip"
|
|
894
|
-
}
|
|
895
|
-
]
|
|
896
|
-
}
|
|
897
|
-
]);
|
|
898
|
-
if (viewRule !== "skip") {
|
|
899
|
-
const rule = applicableRules.find((r) => r.id === viewRule);
|
|
900
|
-
if (rule) {
|
|
901
|
-
console.log(ansis.dim(`
|
|
902
|
-
${"\u2500".repeat(50)}`));
|
|
903
|
-
console.log(ansis.bold(isZh ? rule.nameZh : rule.name));
|
|
904
|
-
console.log(ansis.dim(isZh ? rule.descriptionZh : rule.description));
|
|
905
|
-
console.log("");
|
|
906
|
-
console.log(isZh ? rule.contentZh : rule.content);
|
|
907
|
-
console.log(ansis.dim(`${"\u2500".repeat(50)}
|
|
908
|
-
`));
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
export { showContextMenu };
|