ccjk 9.0.2 → 9.0.4
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/ccjk-hooks.mjs +5 -1
- package/dist/chunks/ccjk-mcp.mjs +6 -6
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/startup.mjs +489 -0
- package/dist/cli.mjs +2 -0
- package/package.json +1 -1
|
@@ -1009,7 +1009,11 @@ function groupHooksByCategory(hooks) {
|
|
|
1009
1009
|
"lifecycle": []
|
|
1010
1010
|
};
|
|
1011
1011
|
for (const hook of hooks) {
|
|
1012
|
-
|
|
1012
|
+
const category = hook.category || "lifecycle";
|
|
1013
|
+
if (!groups[category]) {
|
|
1014
|
+
groups[category] = [];
|
|
1015
|
+
}
|
|
1016
|
+
groups[category].push(hook);
|
|
1013
1017
|
}
|
|
1014
1018
|
return groups;
|
|
1015
1019
|
}
|
package/dist/chunks/ccjk-mcp.mjs
CHANGED
|
@@ -499,7 +499,7 @@ async function ccjkMcp(options = {}) {
|
|
|
499
499
|
const config = readMcpConfig() || { mcpServers: {} };
|
|
500
500
|
const newServers = {};
|
|
501
501
|
for (const serviceId of result.installed) {
|
|
502
|
-
const service = mcpServiceTemplates[serviceId];
|
|
502
|
+
const service = mcpServiceTemplates[serviceId] || servicesToInstall.find((s) => s.id === serviceId);
|
|
503
503
|
if (service) {
|
|
504
504
|
newServers[serviceId] = {
|
|
505
505
|
type: service.type,
|
|
@@ -513,8 +513,8 @@ async function ccjkMcp(options = {}) {
|
|
|
513
513
|
writeMcpConfig(mergedConfig);
|
|
514
514
|
if (!options.json) {
|
|
515
515
|
result.installed.forEach((serviceId) => {
|
|
516
|
-
const service = mcpServiceTemplates[serviceId];
|
|
517
|
-
const name = isZh ? service.name["zh-CN"] : service.name.en;
|
|
516
|
+
const service = mcpServiceTemplates[serviceId] || servicesToInstall.find((s) => s.id === serviceId);
|
|
517
|
+
const name = service ? isZh ? service.name["zh-CN"] : service.name.en : serviceId;
|
|
518
518
|
console.log(` ${ansis.green("\u2713")} ${serviceId} (${name})`);
|
|
519
519
|
});
|
|
520
520
|
}
|
|
@@ -526,10 +526,10 @@ async function ccjkMcp(options = {}) {
|
|
|
526
526
|
`));
|
|
527
527
|
}
|
|
528
528
|
for (const serviceId of result.installed) {
|
|
529
|
-
const service = mcpServiceTemplates[serviceId];
|
|
530
|
-
const verified = await verifyService(service);
|
|
529
|
+
const service = mcpServiceTemplates[serviceId] || servicesToInstall.find((s) => s.id === serviceId);
|
|
530
|
+
const verified = service ? await verifyService(service) : false;
|
|
531
531
|
if (!options.json) {
|
|
532
|
-
const name = isZh ? service.name["zh-CN"] : service.name.en;
|
|
532
|
+
const name = service ? isZh ? service.name["zh-CN"] : service.name.en : serviceId;
|
|
533
533
|
const status = verified ? ansis.green("\u2713") : ansis.red("\u2717");
|
|
534
534
|
console.log(` ${status} ${serviceId} (${name})`);
|
|
535
535
|
}
|
package/dist/chunks/package.mjs
CHANGED
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join, basename } from 'pathe';
|
|
5
|
+
import { i18n } from './index.mjs';
|
|
6
|
+
import 'node:process';
|
|
7
|
+
import 'node:url';
|
|
8
|
+
import 'i18next';
|
|
9
|
+
import 'i18next-fs-backend';
|
|
10
|
+
|
|
11
|
+
class PlanPersistenceManager {
|
|
12
|
+
baseDir;
|
|
13
|
+
projectPlanDir;
|
|
14
|
+
constructor(projectPath) {
|
|
15
|
+
this.baseDir = join(homedir(), ".ccjk", "plans");
|
|
16
|
+
const cwd = projectPath || process.cwd();
|
|
17
|
+
this.projectPlanDir = join(cwd, ".ccjk", "plan", "current");
|
|
18
|
+
this.ensureDirectories();
|
|
19
|
+
}
|
|
20
|
+
ensureDirectories() {
|
|
21
|
+
for (const dir of [this.baseDir, this.projectPlanDir]) {
|
|
22
|
+
if (!existsSync(dir)) {
|
|
23
|
+
mkdirSync(dir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 生成 Plan ID
|
|
29
|
+
*/
|
|
30
|
+
generateId() {
|
|
31
|
+
return `plan-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 生成安全的文件名
|
|
35
|
+
*/
|
|
36
|
+
sanitizeFileName(name) {
|
|
37
|
+
return name.replace(/[<>:"/\\|?*]/g, "-").replace(/\s+/g, "-").replace(/-+/g, "-").substring(0, 50);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 保存 Plan 文档
|
|
41
|
+
*/
|
|
42
|
+
savePlan(plan, options = {}) {
|
|
43
|
+
const now = /* @__PURE__ */ new Date();
|
|
44
|
+
const id = this.generateId();
|
|
45
|
+
const document = {
|
|
46
|
+
...plan,
|
|
47
|
+
id,
|
|
48
|
+
createdAt: now,
|
|
49
|
+
updatedAt: now
|
|
50
|
+
};
|
|
51
|
+
const timestamp = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
52
|
+
const safeName = this.sanitizeFileName(plan.name);
|
|
53
|
+
const fileName = `${safeName}-${timestamp}.md`;
|
|
54
|
+
const projectFilePath = join(this.projectPlanDir, fileName);
|
|
55
|
+
this.writePlanFile(projectFilePath, document, options.overwrite);
|
|
56
|
+
const globalFilePath = join(this.baseDir, fileName);
|
|
57
|
+
this.writePlanFile(globalFilePath, document, true);
|
|
58
|
+
return document;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 写入 Plan 文件
|
|
62
|
+
*/
|
|
63
|
+
writePlanFile(filePath, plan, overwrite = false) {
|
|
64
|
+
if (existsSync(filePath) && !overwrite) {
|
|
65
|
+
const ext = ".md";
|
|
66
|
+
const base = filePath.slice(0, -ext.length);
|
|
67
|
+
let counter = 1;
|
|
68
|
+
let newPath = `${base}-v${counter}${ext}`;
|
|
69
|
+
while (existsSync(newPath)) {
|
|
70
|
+
counter++;
|
|
71
|
+
newPath = `${base}-v${counter}${ext}`;
|
|
72
|
+
}
|
|
73
|
+
filePath = newPath;
|
|
74
|
+
}
|
|
75
|
+
const content = this.buildPlanMarkdown(plan);
|
|
76
|
+
writeFileSync(filePath, content, "utf-8");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 构建 Plan Markdown 内容
|
|
80
|
+
*/
|
|
81
|
+
buildPlanMarkdown(plan) {
|
|
82
|
+
const lines = [];
|
|
83
|
+
lines.push("---");
|
|
84
|
+
lines.push(`id: ${plan.id}`);
|
|
85
|
+
lines.push(`name: ${plan.name}`);
|
|
86
|
+
lines.push(`created: ${plan.createdAt.toISOString()}`);
|
|
87
|
+
lines.push(`updated: ${plan.updatedAt.toISOString()}`);
|
|
88
|
+
if (plan.sessionId) {
|
|
89
|
+
lines.push(`session: ${plan.sessionId}`);
|
|
90
|
+
}
|
|
91
|
+
if (plan.sourceSkill) {
|
|
92
|
+
lines.push(`skill: ${plan.sourceSkill}`);
|
|
93
|
+
}
|
|
94
|
+
if (plan.metadata.tokenEstimate) {
|
|
95
|
+
lines.push(`tokens: ${plan.metadata.tokenEstimate}`);
|
|
96
|
+
}
|
|
97
|
+
lines.push("---");
|
|
98
|
+
lines.push("");
|
|
99
|
+
lines.push(plan.content);
|
|
100
|
+
if (plan.metadata.tasks && plan.metadata.tasks.length > 0) {
|
|
101
|
+
lines.push("");
|
|
102
|
+
lines.push("## \u4EFB\u52A1\u6E05\u5355");
|
|
103
|
+
lines.push("");
|
|
104
|
+
for (const task of plan.metadata.tasks) {
|
|
105
|
+
const checkbox = task.completed ? "[x]" : "[ ]";
|
|
106
|
+
const priority = task.priority ? ` (${task.priority})` : "";
|
|
107
|
+
lines.push(`- ${checkbox} ${task.title}${priority}`);
|
|
108
|
+
if (task.description) {
|
|
109
|
+
lines.push(` - ${task.description}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (plan.metadata.keyDecisions && plan.metadata.keyDecisions.length > 0) {
|
|
114
|
+
lines.push("");
|
|
115
|
+
lines.push("## \u5173\u952E\u51B3\u7B56");
|
|
116
|
+
lines.push("");
|
|
117
|
+
for (const decision of plan.metadata.keyDecisions) {
|
|
118
|
+
lines.push(`- ${decision}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 读取 Plan 文档
|
|
125
|
+
*/
|
|
126
|
+
readPlan(fileName) {
|
|
127
|
+
let filePath = join(this.projectPlanDir, fileName);
|
|
128
|
+
if (!existsSync(filePath)) {
|
|
129
|
+
filePath = join(this.baseDir, fileName);
|
|
130
|
+
}
|
|
131
|
+
if (!existsSync(filePath)) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return this.parsePlanFile(filePath);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 解析 Plan 文件
|
|
138
|
+
*/
|
|
139
|
+
parsePlanFile(filePath) {
|
|
140
|
+
try {
|
|
141
|
+
const content = readFileSync(filePath, "utf-8");
|
|
142
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
143
|
+
if (!frontmatterMatch) {
|
|
144
|
+
return {
|
|
145
|
+
id: basename(filePath, ".md"),
|
|
146
|
+
name: basename(filePath, ".md"),
|
|
147
|
+
content,
|
|
148
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
149
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
150
|
+
metadata: {}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const frontmatter = frontmatterMatch[1];
|
|
154
|
+
const body = frontmatterMatch[2];
|
|
155
|
+
const meta = {};
|
|
156
|
+
for (const line of frontmatter.split("\n")) {
|
|
157
|
+
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
158
|
+
if (match) {
|
|
159
|
+
meta[match[1]] = match[2];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
id: meta.id || basename(filePath, ".md"),
|
|
164
|
+
name: meta.name || basename(filePath, ".md"),
|
|
165
|
+
content: body.trim(),
|
|
166
|
+
sessionId: meta.session,
|
|
167
|
+
sourceSkill: meta.skill,
|
|
168
|
+
createdAt: meta.created ? new Date(meta.created) : /* @__PURE__ */ new Date(),
|
|
169
|
+
updatedAt: meta.updated ? new Date(meta.updated) : /* @__PURE__ */ new Date(),
|
|
170
|
+
metadata: {
|
|
171
|
+
tokenEstimate: meta.tokens ? Number.parseInt(meta.tokens, 10) : void 0
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* 列出所有 Plan 文档
|
|
180
|
+
*/
|
|
181
|
+
listPlans(location = "project") {
|
|
182
|
+
const plans = [];
|
|
183
|
+
if (location === "project" || location === "all") {
|
|
184
|
+
if (existsSync(this.projectPlanDir)) {
|
|
185
|
+
const files = readdirSync(this.projectPlanDir).filter((f) => f.endsWith(".md"));
|
|
186
|
+
plans.push(...files.map((f) => join(this.projectPlanDir, f)));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (location === "global" || location === "all") {
|
|
190
|
+
if (existsSync(this.baseDir)) {
|
|
191
|
+
const files = readdirSync(this.baseDir).filter((f) => f.endsWith(".md"));
|
|
192
|
+
plans.push(...files.map((f) => join(this.baseDir, f)));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return plans;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 获取最新的 Plan 文档
|
|
199
|
+
*/
|
|
200
|
+
getLatestPlan() {
|
|
201
|
+
const plans = this.listPlans("project");
|
|
202
|
+
if (plans.length === 0) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const sorted = plans.map((p) => {
|
|
206
|
+
const doc = this.parsePlanFile(p);
|
|
207
|
+
return doc ? { path: p, doc } : null;
|
|
208
|
+
}).filter((p) => p !== null).sort((a, b) => b.doc.updatedAt.getTime() - a.doc.updatedAt.getTime());
|
|
209
|
+
return sorted[0]?.doc || null;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 获取项目 Plan 目录路径
|
|
213
|
+
*/
|
|
214
|
+
getProjectPlanDir() {
|
|
215
|
+
return this.projectPlanDir;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
let instance$1 = null;
|
|
219
|
+
function getPlanPersistenceManager(projectPath) {
|
|
220
|
+
if (!instance$1) {
|
|
221
|
+
instance$1 = new PlanPersistenceManager(projectPath);
|
|
222
|
+
}
|
|
223
|
+
return instance$1;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const DEFAULT_COMPACT_CONFIG = {
|
|
227
|
+
warningThreshold: 0.7,
|
|
228
|
+
// 70% 时警告
|
|
229
|
+
compactThreshold: 0.85,
|
|
230
|
+
// 85% 时建议清理
|
|
231
|
+
messageThreshold: 100,
|
|
232
|
+
// 100 条消息
|
|
233
|
+
planningMessageThreshold: 50,
|
|
234
|
+
// Plan 阶段 50 条消息
|
|
235
|
+
defaultMaxTokens: 2e5
|
|
236
|
+
// 200k tokens (Claude 3.5 Sonnet)
|
|
237
|
+
};
|
|
238
|
+
class CompactAdvisor {
|
|
239
|
+
config;
|
|
240
|
+
constructor(config = {}) {
|
|
241
|
+
this.config = { ...DEFAULT_COMPACT_CONFIG, ...config };
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 检测是否需要建议 compact
|
|
245
|
+
*/
|
|
246
|
+
shouldSuggestCompact(context) {
|
|
247
|
+
const maxTokens = context.maxTokens || this.config.defaultMaxTokens;
|
|
248
|
+
const usagePercent = context.currentTokens / maxTokens;
|
|
249
|
+
if (usagePercent >= this.config.compactThreshold) {
|
|
250
|
+
return {
|
|
251
|
+
shouldCompact: true,
|
|
252
|
+
reason: "token_threshold",
|
|
253
|
+
message: this.getTokenThresholdMessage(usagePercent, context.currentTokens, maxTokens),
|
|
254
|
+
usagePercent,
|
|
255
|
+
estimatedSavings: Math.floor(context.currentTokens * 0.6),
|
|
256
|
+
// 预计节省 60%
|
|
257
|
+
suggestedActions: this.getSuggestedActions("token_threshold")
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
if (context.messageCount >= this.config.messageThreshold) {
|
|
261
|
+
return {
|
|
262
|
+
shouldCompact: true,
|
|
263
|
+
reason: "message_threshold",
|
|
264
|
+
message: this.getMessageThresholdMessage(context.messageCount),
|
|
265
|
+
usagePercent,
|
|
266
|
+
estimatedSavings: Math.floor(context.currentTokens * 0.5),
|
|
267
|
+
suggestedActions: this.getSuggestedActions("message_threshold")
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (usagePercent >= this.config.warningThreshold) {
|
|
271
|
+
return {
|
|
272
|
+
shouldCompact: false,
|
|
273
|
+
message: this.getWarningMessage(usagePercent, context.currentTokens, maxTokens),
|
|
274
|
+
usagePercent,
|
|
275
|
+
suggestedActions: []
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
shouldCompact: false,
|
|
280
|
+
message: this.getHealthyMessage(usagePercent),
|
|
281
|
+
usagePercent,
|
|
282
|
+
suggestedActions: []
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* 检测 Plan 阶段完成
|
|
287
|
+
*/
|
|
288
|
+
detectPlanCompletion(planContent) {
|
|
289
|
+
const completionPatterns = [
|
|
290
|
+
/##\s*(实施计划|Implementation Plan|执行计划)/i,
|
|
291
|
+
/##\s*(任务(清单|列表)|Task List|Tasks)/i,
|
|
292
|
+
/##\s*(验收标准|Acceptance Criteria)/i,
|
|
293
|
+
/✅\s*(规划完成|Plan Complete|Planning Complete)/i,
|
|
294
|
+
/---\s*\n\s*💡\s*(下一步|Next Step)/i
|
|
295
|
+
];
|
|
296
|
+
return completionPatterns.some((pattern) => pattern.test(planContent));
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* 生成 Plan 完成后的建议
|
|
300
|
+
*/
|
|
301
|
+
generatePlanCompleteSuggestion(plan, context) {
|
|
302
|
+
const maxTokens = context.maxTokens || this.config.defaultMaxTokens;
|
|
303
|
+
const usagePercent = context.currentTokens / maxTokens;
|
|
304
|
+
const planningMessages = context.planningMessageCount || 0;
|
|
305
|
+
const shouldCompact = planningMessages >= this.config.planningMessageThreshold || usagePercent >= 0.3;
|
|
306
|
+
return {
|
|
307
|
+
shouldCompact,
|
|
308
|
+
reason: "plan_complete",
|
|
309
|
+
message: this.getPlanCompleteMessage(plan, context, usagePercent),
|
|
310
|
+
usagePercent,
|
|
311
|
+
estimatedSavings: Math.floor(context.currentTokens * 0.7),
|
|
312
|
+
// Plan 阶段可节省 70%
|
|
313
|
+
suggestedActions: this.getSuggestedActions("plan_complete")
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* 生成建议输出(用于显示给用户)
|
|
318
|
+
*/
|
|
319
|
+
generateSuggestionOutput(suggestion, planPath) {
|
|
320
|
+
const lines = [];
|
|
321
|
+
const isZh = i18n.language === "zh-CN";
|
|
322
|
+
lines.push("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
323
|
+
if (suggestion.reason === "plan_complete") {
|
|
324
|
+
lines.push(isZh ? "\u2705 \u89C4\u5212\u5B8C\u6210\uFF01" : "\u2705 Planning Complete!");
|
|
325
|
+
} else if (suggestion.shouldCompact) {
|
|
326
|
+
lines.push(isZh ? "\u26A0\uFE0F \u4E0A\u4E0B\u6587\u4F7F\u7528\u7387\u8F83\u9AD8" : "\u26A0\uFE0F High Context Usage");
|
|
327
|
+
} else {
|
|
328
|
+
lines.push(isZh ? "\u{1F4CA} \u4E0A\u4E0B\u6587\u72B6\u6001" : "\u{1F4CA} Context Status");
|
|
329
|
+
}
|
|
330
|
+
lines.push("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
331
|
+
lines.push("");
|
|
332
|
+
if (planPath) {
|
|
333
|
+
lines.push(isZh ? `\u{1F4C4} Plan \u5DF2\u4FDD\u5B58: ${planPath}` : `\u{1F4C4} Plan saved: ${planPath}`);
|
|
334
|
+
lines.push("");
|
|
335
|
+
}
|
|
336
|
+
lines.push(isZh ? "\u{1F4CA} \u5F53\u524D\u4E0A\u4E0B\u6587\u72B6\u6001:" : "\u{1F4CA} Current context status:");
|
|
337
|
+
lines.push(` \u2022 Token ${isZh ? "\u4F7F\u7528" : "usage"}: ${(suggestion.usagePercent * 100).toFixed(1)}%`);
|
|
338
|
+
if (suggestion.estimatedSavings) {
|
|
339
|
+
lines.push(` \u2022 ${isZh ? "\u9884\u8BA1\u53EF\u8282\u7701" : "Estimated savings"}: ~${this.formatTokens(suggestion.estimatedSavings)} tokens`);
|
|
340
|
+
}
|
|
341
|
+
lines.push("");
|
|
342
|
+
lines.push(suggestion.message);
|
|
343
|
+
lines.push("");
|
|
344
|
+
if (suggestion.shouldCompact && suggestion.suggestedActions.length > 0) {
|
|
345
|
+
lines.push(isZh ? "\u{1F4A1} \u5EFA\u8BAE\u64CD\u4F5C:" : "\u{1F4A1} Suggested actions:");
|
|
346
|
+
lines.push("");
|
|
347
|
+
for (let i = 0; i < suggestion.suggestedActions.length; i++) {
|
|
348
|
+
lines.push(` [${i + 1}] ${suggestion.suggestedActions[i]}`);
|
|
349
|
+
}
|
|
350
|
+
lines.push("");
|
|
351
|
+
}
|
|
352
|
+
lines.push("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
|
|
353
|
+
return lines.join("\n");
|
|
354
|
+
}
|
|
355
|
+
// ==========================================================================
|
|
356
|
+
// Private Helpers
|
|
357
|
+
// ==========================================================================
|
|
358
|
+
getTokenThresholdMessage(usagePercent, current, max) {
|
|
359
|
+
const isZh = i18n.language === "zh-CN";
|
|
360
|
+
const percent = (usagePercent * 100).toFixed(1);
|
|
361
|
+
if (isZh) {
|
|
362
|
+
return `\u4E0A\u4E0B\u6587\u4F7F\u7528\u7387\u5DF2\u8FBE ${percent}%\uFF08${this.formatTokens(current)} / ${this.formatTokens(max)}\uFF09\uFF0C\u5EFA\u8BAE\u6E05\u7406\u4EE5\u907F\u514D\u6027\u80FD\u4E0B\u964D\u3002`;
|
|
363
|
+
}
|
|
364
|
+
return `Context usage at ${percent}% (${this.formatTokens(current)} / ${this.formatTokens(max)}). Consider clearing to maintain performance.`;
|
|
365
|
+
}
|
|
366
|
+
getMessageThresholdMessage(count) {
|
|
367
|
+
const isZh = i18n.language === "zh-CN";
|
|
368
|
+
if (isZh) {
|
|
369
|
+
return `\u5F53\u524D\u4F1A\u8BDD\u5DF2\u6709 ${count} \u6761\u6D88\u606F\uFF0C\u5EFA\u8BAE\u6E05\u7406\u4EE5\u4FDD\u6301\u54CD\u5E94\u8D28\u91CF\u3002`;
|
|
370
|
+
}
|
|
371
|
+
return `Current session has ${count} messages. Consider clearing to maintain response quality.`;
|
|
372
|
+
}
|
|
373
|
+
getWarningMessage(usagePercent, current, max) {
|
|
374
|
+
const isZh = i18n.language === "zh-CN";
|
|
375
|
+
const percent = (usagePercent * 100).toFixed(1);
|
|
376
|
+
if (isZh) {
|
|
377
|
+
return `\u26A0\uFE0F \u4E0A\u4E0B\u6587\u4F7F\u7528\u7387 ${percent}%\uFF0C\u63A5\u8FD1\u9608\u503C\u3002\u7EE7\u7EED\u5DE5\u4F5C\u65F6\u8BF7\u6CE8\u610F\u3002`;
|
|
378
|
+
}
|
|
379
|
+
return `\u26A0\uFE0F Context usage at ${percent}%, approaching threshold. Monitor as you continue.`;
|
|
380
|
+
}
|
|
381
|
+
getHealthyMessage(usagePercent) {
|
|
382
|
+
const isZh = i18n.language === "zh-CN";
|
|
383
|
+
const percent = (usagePercent * 100).toFixed(1);
|
|
384
|
+
if (isZh) {
|
|
385
|
+
return `\u2705 \u4E0A\u4E0B\u6587\u72B6\u6001\u826F\u597D\uFF08${percent}%\uFF09\uFF0C\u65E0\u9700\u6E05\u7406\u3002`;
|
|
386
|
+
}
|
|
387
|
+
return `\u2705 Context is healthy (${percent}%), no action needed.`;
|
|
388
|
+
}
|
|
389
|
+
getPlanCompleteMessage(plan, context, usagePercent) {
|
|
390
|
+
const isZh = i18n.language === "zh-CN";
|
|
391
|
+
if (isZh) {
|
|
392
|
+
return `Claude Code \u65B0\u529F\u80FD\u652F\u6301\u5728\u63A5\u53D7\u8BA1\u5212\u540E\u81EA\u52A8\u6E05\u7A7A\u4E0A\u4E0B\u6587\uFF0C
|
|
393
|
+
\u8FD9\u80FD\u8BA9 Claude \u66F4\u4E13\u6CE8\u4E8E\u6267\u884C\uFF0C\u63D0\u5347\u4EFB\u52A1\u5B8C\u6210\u8D28\u91CF\u3002
|
|
394
|
+
|
|
395
|
+
Plan "${plan.name}" \u5DF2\u4FDD\u5B58\uFF0C\u60A8\u53EF\u4EE5\u9009\u62E9\uFF1A
|
|
396
|
+
|
|
397
|
+
\u{1F9F9} \u6E05\u7A7A\u4E0A\u4E0B\u6587\u5E76\u6267\u884C\uFF08\u63A8\u8350\uFF09
|
|
398
|
+
- \u81EA\u52A8\u6E05\u7406\u89C4\u5212\u9636\u6BB5\u7684\u8BA8\u8BBA
|
|
399
|
+
- \u4FDD\u7559 Plan \u6587\u6863\u4F5C\u4E3A\u6267\u884C\u4F9D\u636E
|
|
400
|
+
- \u83B7\u5F97\u5E72\u51C0\u7684\u4E0A\u4E0B\u6587\u7A97\u53E3
|
|
401
|
+
|
|
402
|
+
\u{1F4CE} \u4FDD\u7559\u4E0A\u4E0B\u6587\u5E76\u6267\u884C
|
|
403
|
+
- \u4FDD\u7559\u6240\u6709\u5386\u53F2\u5BF9\u8BDD
|
|
404
|
+
- \u9002\u5408\u9700\u8981\u53C2\u8003\u8BA8\u8BBA\u7EC6\u8282\u7684\u60C5\u51B5`;
|
|
405
|
+
}
|
|
406
|
+
return `Claude Code now supports automatically clearing context after accepting a plan.
|
|
407
|
+
This helps Claude stay focused and improves task completion quality.
|
|
408
|
+
|
|
409
|
+
Plan "${plan.name}" has been saved. You can choose:
|
|
410
|
+
|
|
411
|
+
\u{1F9F9} Clear context and execute (Recommended)
|
|
412
|
+
- Automatically clear planning discussions
|
|
413
|
+
- Keep Plan document as execution reference
|
|
414
|
+
- Get a fresh context window
|
|
415
|
+
|
|
416
|
+
\u{1F4CE} Keep context and execute
|
|
417
|
+
- Preserve all conversation history
|
|
418
|
+
- Suitable when you need to reference discussion details`;
|
|
419
|
+
}
|
|
420
|
+
getSuggestedActions(reason) {
|
|
421
|
+
const isZh = i18n.language === "zh-CN";
|
|
422
|
+
switch (reason) {
|
|
423
|
+
case "plan_complete":
|
|
424
|
+
return isZh ? [
|
|
425
|
+
"\u{1F9F9} \u6E05\u7A7A\u4E0A\u4E0B\u6587\u5E76\u6267\u884C (\u63A8\u8350) - \u4F7F\u7528 Claude Code Plan Mode \u7684\u6E05\u7A7A\u9009\u9879",
|
|
426
|
+
"\u{1F4CE} \u4FDD\u7559\u4E0A\u4E0B\u6587\u5E76\u6267\u884C - \u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD",
|
|
427
|
+
"\u{1F4BE} \u4EC5\u4FDD\u5B58 Plan - \u7A0D\u540E\u6267\u884C"
|
|
428
|
+
] : [
|
|
429
|
+
"\u{1F9F9} Clear context and execute (Recommended) - Use Claude Code Plan Mode clear option",
|
|
430
|
+
"\u{1F4CE} Keep context and execute - Continue current session",
|
|
431
|
+
"\u{1F4BE} Save Plan only - Execute later"
|
|
432
|
+
];
|
|
433
|
+
case "token_threshold":
|
|
434
|
+
case "message_threshold":
|
|
435
|
+
return isZh ? [
|
|
436
|
+
"\u6267\u884C /compact \u538B\u7F29\u4E0A\u4E0B\u6587",
|
|
437
|
+
"\u5F00\u59CB\u65B0\u4F1A\u8BDD",
|
|
438
|
+
"\u4FDD\u5B58\u5F53\u524D\u8FDB\u5EA6\u540E\u6E05\u7406"
|
|
439
|
+
] : [
|
|
440
|
+
"Run /compact to compress context",
|
|
441
|
+
"Start a new session",
|
|
442
|
+
"Save progress and clear"
|
|
443
|
+
];
|
|
444
|
+
default:
|
|
445
|
+
return [];
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
formatTokens(tokens) {
|
|
449
|
+
if (tokens >= 1e6) {
|
|
450
|
+
return `${(tokens / 1e6).toFixed(1)}M`;
|
|
451
|
+
}
|
|
452
|
+
if (tokens >= 1e3) {
|
|
453
|
+
return `${(tokens / 1e3).toFixed(1)}k`;
|
|
454
|
+
}
|
|
455
|
+
return tokens.toString();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
let instance = null;
|
|
459
|
+
function getCompactAdvisor(config) {
|
|
460
|
+
if (!instance) {
|
|
461
|
+
instance = new CompactAdvisor(config);
|
|
462
|
+
}
|
|
463
|
+
return instance;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const PLAN_DIRECTORIES = [
|
|
467
|
+
".ccjk/plan/current",
|
|
468
|
+
".ccjk/plan/archive"
|
|
469
|
+
];
|
|
470
|
+
const GLOBAL_PLAN_DIR = join(homedir(), ".ccjk", "plans");
|
|
471
|
+
async function initializeContextFeatures() {
|
|
472
|
+
try {
|
|
473
|
+
if (!existsSync(GLOBAL_PLAN_DIR)) {
|
|
474
|
+
await mkdir(GLOBAL_PLAN_DIR, { recursive: true });
|
|
475
|
+
}
|
|
476
|
+
const cwd = process.cwd();
|
|
477
|
+
for (const dir of PLAN_DIRECTORIES) {
|
|
478
|
+
const fullPath = join(cwd, dir);
|
|
479
|
+
if (!existsSync(fullPath)) {
|
|
480
|
+
await mkdir(fullPath, { recursive: true });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
getCompactAdvisor();
|
|
484
|
+
getPlanPersistenceManager();
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export { initializeContextFeatures };
|
package/dist/cli.mjs
CHANGED
|
@@ -1643,6 +1643,8 @@ async function runLazyCli() {
|
|
|
1643
1643
|
function bootstrapCloudServices() {
|
|
1644
1644
|
setImmediate(async () => {
|
|
1645
1645
|
try {
|
|
1646
|
+
const { initializeContextFeatures } = await import('./chunks/startup.mjs');
|
|
1647
|
+
await initializeContextFeatures();
|
|
1646
1648
|
const { autoBootstrap } = await import('./chunks/auto-bootstrap.mjs');
|
|
1647
1649
|
await autoBootstrap();
|
|
1648
1650
|
const { autoUpgrade } = await import('./chunks/silent-updater.mjs');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccjk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "9.0.
|
|
4
|
+
"version": "9.0.4",
|
|
5
5
|
"packageManager": "pnpm@10.17.1",
|
|
6
6
|
"description": "CCJK v9.0.0 - Revolutionary AI Development Platform with Enterprise Security, Streaming Cloud Sync, CRDT Conflict Resolution, and Unified V3 Architecture",
|
|
7
7
|
"author": {
|