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.
@@ -1009,7 +1009,11 @@ function groupHooksByCategory(hooks) {
1009
1009
  "lifecycle": []
1010
1010
  };
1011
1011
  for (const hook of hooks) {
1012
- groups[hook.category].push(hook);
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
  }
@@ -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
  }
@@ -1,4 +1,4 @@
1
- const version = "9.0.2";
1
+ const version = "9.0.3";
2
2
  const homepage = "https://github.com/miounet11/ccjk";
3
3
 
4
4
  export { homepage, version };
@@ -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.2",
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": {