ccjk 2.4.3 → 2.5.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/api-providers.mjs +73 -1
- package/dist/chunks/ccjk-config.mjs +13 -77
- package/dist/chunks/ccr.mjs +9 -4
- package/dist/chunks/check-updates.mjs +4 -2
- package/dist/chunks/claude-code-config-manager.mjs +9 -15
- package/dist/chunks/claude-code-incremental-manager.mjs +5 -8
- package/dist/chunks/codex.mjs +10 -569
- package/dist/chunks/config-switch.mjs +7 -5
- package/dist/chunks/config.mjs +573 -0
- package/dist/chunks/config2.mjs +451 -0
- package/dist/chunks/doctor.mjs +89 -1
- package/dist/chunks/features.mjs +13 -10
- package/dist/chunks/index.mjs +10 -1164
- package/dist/chunks/index2.mjs +8 -2
- package/dist/chunks/init.mjs +14 -11
- package/dist/chunks/json-config.mjs +59 -0
- package/dist/chunks/mcp-server.mjs +776 -0
- package/dist/chunks/mcp.mjs +10 -8
- package/dist/chunks/menu.mjs +5 -5
- package/dist/chunks/package.mjs +1 -1
- package/dist/chunks/permissions.mjs +420 -0
- package/dist/chunks/prompts.mjs +2 -1
- package/dist/chunks/providers.mjs +261 -0
- package/dist/chunks/session.mjs +484 -41
- package/dist/chunks/skills.mjs +553 -0
- package/dist/chunks/stats.mjs +411 -0
- package/dist/chunks/uninstall.mjs +4 -3
- package/dist/chunks/update.mjs +6 -3
- package/dist/chunks/workflows2.mjs +140 -0
- package/dist/cli.mjs +316 -10
- package/dist/i18n/locales/en/hooks.json +47 -0
- package/dist/i18n/locales/en/mcp.json +55 -0
- package/dist/i18n/locales/en/permissions.json +43 -0
- package/dist/i18n/locales/en/sandbox.json +44 -0
- package/dist/i18n/locales/en/skills.json +89 -129
- package/dist/i18n/locales/en/stats.json +20 -0
- package/dist/i18n/locales/zh-CN/hooks.json +47 -0
- package/dist/i18n/locales/zh-CN/mcp.json +55 -0
- package/dist/i18n/locales/zh-CN/permissions.json +43 -0
- package/dist/i18n/locales/zh-CN/sandbox.json +44 -0
- package/dist/i18n/locales/zh-CN/skills.json +88 -128
- package/dist/i18n/locales/zh-CN/stats.json +20 -0
- package/dist/index.mjs +12 -8
- package/dist/shared/ccjk.B-lZxV2u.mjs +1162 -0
- package/dist/shared/{ccjk.CURU8gbR.mjs → ccjk.CUdzQluX.mjs} +1 -1
- package/dist/shared/{ccjk.ByTIGCUC.mjs → ccjk.Dut3wyoP.mjs} +1 -1
- package/dist/shared/ccjk.J8YiPsOw.mjs +259 -0
- package/dist/shared/{ccjk.CGTmRqsu.mjs → ccjk.rLRHmcqD.mjs} +5 -134
- package/dist/shared/{ccjk.QbS8EAOd.mjs → ccjk.uVUeWAt8.mjs} +2 -1
- package/package.json +1 -1
- package/templates/common/skills/code-review.md +343 -0
- package/templates/common/skills/summarize.md +312 -0
- package/templates/common/skills/translate.md +202 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { i18n } from './index2.mjs';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { join } from 'pathe';
|
|
6
|
+
import 'node:process';
|
|
7
|
+
import 'node:url';
|
|
8
|
+
import 'i18next';
|
|
9
|
+
import 'i18next-fs-backend';
|
|
10
|
+
|
|
11
|
+
class StatsStorage {
|
|
12
|
+
baseDir;
|
|
13
|
+
recordsDir;
|
|
14
|
+
dailyDir;
|
|
15
|
+
constructor(baseDir) {
|
|
16
|
+
this.baseDir = baseDir || join(homedir(), ".ccjk", "stats");
|
|
17
|
+
this.recordsDir = join(this.baseDir, "records");
|
|
18
|
+
this.dailyDir = join(this.baseDir, "daily");
|
|
19
|
+
this.ensureDirectories();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Ensure storage directories exist
|
|
23
|
+
*/
|
|
24
|
+
ensureDirectories() {
|
|
25
|
+
for (const dir of [this.baseDir, this.recordsDir, this.dailyDir]) {
|
|
26
|
+
if (!existsSync(dir)) {
|
|
27
|
+
mkdirSync(dir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get file path for a specific date
|
|
33
|
+
*/
|
|
34
|
+
getRecordFilePath(date) {
|
|
35
|
+
return join(this.recordsDir, `${date}.json`);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get daily stats file path for a specific date
|
|
39
|
+
*/
|
|
40
|
+
getDailyStatsFilePath(date) {
|
|
41
|
+
return join(this.dailyDir, `${date}.json`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Format date as YYYY-MM-DD
|
|
45
|
+
*/
|
|
46
|
+
formatDate(timestamp) {
|
|
47
|
+
const date = new Date(timestamp);
|
|
48
|
+
const year = date.getFullYear();
|
|
49
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
50
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
51
|
+
return `${year}-${month}-${day}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Save a request record
|
|
55
|
+
*/
|
|
56
|
+
saveRecord(record) {
|
|
57
|
+
const date = this.formatDate(record.timestamp);
|
|
58
|
+
const filePath = this.getRecordFilePath(date);
|
|
59
|
+
let records = [];
|
|
60
|
+
if (existsSync(filePath)) {
|
|
61
|
+
try {
|
|
62
|
+
const content = readFileSync(filePath, "utf-8");
|
|
63
|
+
records = JSON.parse(content);
|
|
64
|
+
} catch {
|
|
65
|
+
records = [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
records.push(record);
|
|
69
|
+
writeFileSync(filePath, JSON.stringify(records, null, 2), "utf-8");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get records for a specific date
|
|
73
|
+
*/
|
|
74
|
+
getRecordsByDate(date) {
|
|
75
|
+
const filePath = this.getRecordFilePath(date);
|
|
76
|
+
if (!existsSync(filePath)) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const content = readFileSync(filePath, "utf-8");
|
|
81
|
+
return JSON.parse(content);
|
|
82
|
+
} catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get records for a date range
|
|
88
|
+
*/
|
|
89
|
+
getRecordsByDateRange(startDate, endDate) {
|
|
90
|
+
const records = [];
|
|
91
|
+
const start = new Date(startDate);
|
|
92
|
+
const end = new Date(endDate);
|
|
93
|
+
const d = new Date(start);
|
|
94
|
+
while (d <= end) {
|
|
95
|
+
const dateStr = this.formatDate(d.getTime());
|
|
96
|
+
records.push(...this.getRecordsByDate(dateStr));
|
|
97
|
+
d.setDate(d.getDate() + 1);
|
|
98
|
+
}
|
|
99
|
+
return records;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get all available record dates
|
|
103
|
+
*/
|
|
104
|
+
getAvailableDates() {
|
|
105
|
+
if (!existsSync(this.recordsDir)) {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
const files = readdirSync(this.recordsDir);
|
|
109
|
+
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", "")).sort();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Save daily statistics
|
|
113
|
+
*/
|
|
114
|
+
saveDailyStats(stats) {
|
|
115
|
+
const filePath = this.getDailyStatsFilePath(stats.date);
|
|
116
|
+
writeFileSync(filePath, JSON.stringify(stats, null, 2), "utf-8");
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get daily statistics for a specific date
|
|
120
|
+
*/
|
|
121
|
+
getDailyStats(date) {
|
|
122
|
+
const filePath = this.getDailyStatsFilePath(date);
|
|
123
|
+
if (!existsSync(filePath)) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const content = readFileSync(filePath, "utf-8");
|
|
128
|
+
return JSON.parse(content);
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get daily statistics for a date range
|
|
135
|
+
*/
|
|
136
|
+
getDailyStatsByDateRange(startDate, endDate) {
|
|
137
|
+
const stats = [];
|
|
138
|
+
const start = new Date(startDate);
|
|
139
|
+
const end = new Date(endDate);
|
|
140
|
+
const d = new Date(start);
|
|
141
|
+
while (d <= end) {
|
|
142
|
+
const dateStr = this.formatDate(d.getTime());
|
|
143
|
+
const dailyStats = this.getDailyStats(dateStr);
|
|
144
|
+
if (dailyStats) {
|
|
145
|
+
stats.push(dailyStats);
|
|
146
|
+
}
|
|
147
|
+
d.setDate(d.getDate() + 1);
|
|
148
|
+
}
|
|
149
|
+
return stats;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get all available daily stats dates
|
|
153
|
+
*/
|
|
154
|
+
getAvailableDailyStatsDates() {
|
|
155
|
+
if (!existsSync(this.dailyDir)) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
const files = readdirSync(this.dailyDir);
|
|
159
|
+
return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", "")).sort();
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Calculate date range for a period
|
|
163
|
+
*/
|
|
164
|
+
getDateRangeForPeriod(period) {
|
|
165
|
+
const now = /* @__PURE__ */ new Date();
|
|
166
|
+
const endDate = this.formatDate(now.getTime());
|
|
167
|
+
if (period === "all") {
|
|
168
|
+
const dates = this.getAvailableDates();
|
|
169
|
+
const startDate2 = dates.length > 0 ? dates[0] : endDate;
|
|
170
|
+
return { startDate: startDate2, endDate };
|
|
171
|
+
}
|
|
172
|
+
const days = period === "1d" ? 1 : period === "7d" ? 7 : period === "30d" ? 30 : 90;
|
|
173
|
+
const start = new Date(now);
|
|
174
|
+
start.setDate(start.getDate() - days + 1);
|
|
175
|
+
const startDate = this.formatDate(start.getTime());
|
|
176
|
+
return { startDate, endDate };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Clean up old records (older than specified days)
|
|
180
|
+
*/
|
|
181
|
+
cleanupOldRecords(daysToKeep) {
|
|
182
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
183
|
+
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
|
|
184
|
+
const cutoffStr = this.formatDate(cutoffDate.getTime());
|
|
185
|
+
const dates = this.getAvailableDates();
|
|
186
|
+
let deletedCount = 0;
|
|
187
|
+
for (const date of dates) {
|
|
188
|
+
if (date < cutoffStr) {
|
|
189
|
+
try {
|
|
190
|
+
const recordFile = this.getRecordFilePath(date);
|
|
191
|
+
const dailyFile = this.getDailyStatsFilePath(date);
|
|
192
|
+
if (existsSync(recordFile)) {
|
|
193
|
+
deletedCount++;
|
|
194
|
+
}
|
|
195
|
+
if (existsSync(dailyFile)) {
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return deletedCount;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get storage statistics
|
|
205
|
+
*/
|
|
206
|
+
getStorageStats() {
|
|
207
|
+
const recordDates = this.getAvailableDates();
|
|
208
|
+
const dailyDates = this.getAvailableDailyStatsDates();
|
|
209
|
+
let totalRecords = 0;
|
|
210
|
+
for (const date of recordDates) {
|
|
211
|
+
const records = this.getRecordsByDate(date);
|
|
212
|
+
totalRecords += records.length;
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
totalRecordFiles: recordDates.length,
|
|
216
|
+
totalDailyFiles: dailyDates.length,
|
|
217
|
+
oldestDate: recordDates.length > 0 ? recordDates[0] : null,
|
|
218
|
+
newestDate: recordDates.length > 0 ? recordDates[recordDates.length - 1] : null,
|
|
219
|
+
totalRecords
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
let storageInstance = null;
|
|
224
|
+
function getStatsStorage() {
|
|
225
|
+
if (!storageInstance) {
|
|
226
|
+
storageInstance = new StatsStorage();
|
|
227
|
+
}
|
|
228
|
+
return storageInstance;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function stats(options = {}) {
|
|
232
|
+
const period = options.period || "7d";
|
|
233
|
+
const format = options.format || "table";
|
|
234
|
+
const storage = getStatsStorage();
|
|
235
|
+
const { startDate, endDate } = storage.getDateRangeForPeriod(period);
|
|
236
|
+
const records = storage.getRecordsByDateRange(startDate, endDate);
|
|
237
|
+
if (records.length === 0) {
|
|
238
|
+
console.log(chalk.yellow(`
|
|
239
|
+
${i18n.t("stats:noData")}
|
|
240
|
+
`));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const filteredRecords = options.provider ? records.filter((r) => r.provider === options.provider) : records;
|
|
244
|
+
if (filteredRecords.length === 0) {
|
|
245
|
+
console.log(chalk.yellow(`
|
|
246
|
+
${i18n.t("stats:noData")} for provider: ${options.provider}
|
|
247
|
+
`));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const stats2 = calculateStats(filteredRecords);
|
|
251
|
+
if (format === "json") {
|
|
252
|
+
console.log(JSON.stringify(stats2, null, 2));
|
|
253
|
+
} else if (format === "csv") {
|
|
254
|
+
displayCSV(stats2);
|
|
255
|
+
} else {
|
|
256
|
+
displayTable(stats2, period);
|
|
257
|
+
}
|
|
258
|
+
if (options.export) {
|
|
259
|
+
await exportStats(stats2, options.export, format);
|
|
260
|
+
console.log(chalk.green(`
|
|
261
|
+
${i18n.t("stats:exportSuccess")}: ${options.export}
|
|
262
|
+
`));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function calculateStats(records) {
|
|
266
|
+
const totalRequests = records.length;
|
|
267
|
+
const successfulRequests = records.filter((r) => r.success).length;
|
|
268
|
+
const successRate = totalRequests > 0 ? successfulRequests / totalRequests * 100 : 0;
|
|
269
|
+
let totalInputTokens = 0;
|
|
270
|
+
let totalOutputTokens = 0;
|
|
271
|
+
let totalCost = 0;
|
|
272
|
+
let totalLatency = 0;
|
|
273
|
+
const providerCounts = {};
|
|
274
|
+
const modelCounts = {};
|
|
275
|
+
for (const record of records) {
|
|
276
|
+
totalInputTokens += record.inputTokens || 0;
|
|
277
|
+
totalOutputTokens += record.outputTokens || 0;
|
|
278
|
+
totalCost += record.cost || 0;
|
|
279
|
+
totalLatency += record.latency || 0;
|
|
280
|
+
const provider = record.provider || "unknown";
|
|
281
|
+
providerCounts[provider] = (providerCounts[provider] || 0) + 1;
|
|
282
|
+
const model = record.model || "unknown";
|
|
283
|
+
modelCounts[model] = (modelCounts[model] || 0) + 1;
|
|
284
|
+
}
|
|
285
|
+
const averageLatency = totalRequests > 0 ? totalLatency / totalRequests : 0;
|
|
286
|
+
return {
|
|
287
|
+
totalRequests,
|
|
288
|
+
successfulRequests,
|
|
289
|
+
failedRequests: totalRequests - successfulRequests,
|
|
290
|
+
successRate,
|
|
291
|
+
totalInputTokens,
|
|
292
|
+
totalOutputTokens,
|
|
293
|
+
totalTokens: totalInputTokens + totalOutputTokens,
|
|
294
|
+
totalCost,
|
|
295
|
+
averageLatency,
|
|
296
|
+
providerCounts,
|
|
297
|
+
modelCounts
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function displayTable(stats2, period) {
|
|
301
|
+
console.log(chalk.cyan.bold(`
|
|
302
|
+
\u{1F4CA} ${i18n.t("stats:title")} - ${i18n.t(`stats:period.${period}`)}`));
|
|
303
|
+
console.log(chalk.gray("\u2500".repeat(60)));
|
|
304
|
+
console.log(chalk.yellow("\n\u{1F4C8} Request Statistics:"));
|
|
305
|
+
console.log(` ${i18n.t("stats:totalRequests")}: ${chalk.bold(stats2.totalRequests.toLocaleString())}`);
|
|
306
|
+
console.log(` ${chalk.green("\u2713")} Successful: ${chalk.bold(stats2.successfulRequests.toLocaleString())}`);
|
|
307
|
+
console.log(` ${chalk.red("\u2717")} Failed: ${chalk.bold(stats2.failedRequests.toLocaleString())}`);
|
|
308
|
+
console.log(` ${i18n.t("stats:successRate")}: ${chalk.bold(stats2.successRate.toFixed(2))}%`);
|
|
309
|
+
console.log(chalk.yellow("\n\u{1F3AF} Token Usage:"));
|
|
310
|
+
console.log(` ${i18n.t("stats:input")}: ${chalk.bold(stats2.totalInputTokens.toLocaleString())}`);
|
|
311
|
+
console.log(` ${i18n.t("stats:output")}: ${chalk.bold(stats2.totalOutputTokens.toLocaleString())}`);
|
|
312
|
+
console.log(` ${i18n.t("stats:totalTokens")}: ${chalk.bold(stats2.totalTokens.toLocaleString())}`);
|
|
313
|
+
console.log(chalk.yellow("\n\u{1F4B0} Cost Analysis:"));
|
|
314
|
+
console.log(` ${i18n.t("stats:estimatedCost")}: ${chalk.bold(`$${stats2.totalCost.toFixed(4)}`)}`);
|
|
315
|
+
console.log(chalk.yellow("\n\u26A1 Performance:"));
|
|
316
|
+
console.log(` ${i18n.t("stats:averageLatency")}: ${chalk.bold(stats2.averageLatency.toFixed(0))}ms`);
|
|
317
|
+
if (Object.keys(stats2.providerCounts).length > 0) {
|
|
318
|
+
console.log(chalk.yellow(`
|
|
319
|
+
\u2601\uFE0F ${i18n.t("stats:providerDistribution")}:`));
|
|
320
|
+
for (const [provider, count] of Object.entries(stats2.providerCounts)) {
|
|
321
|
+
const percentage = (count / stats2.totalRequests * 100).toFixed(1);
|
|
322
|
+
console.log(` ${provider}: ${chalk.bold(count)} (${percentage}%)`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (Object.keys(stats2.modelCounts).length > 0) {
|
|
326
|
+
console.log(chalk.yellow("\n\u{1F916} Model Distribution:"));
|
|
327
|
+
for (const [model, count] of Object.entries(stats2.modelCounts)) {
|
|
328
|
+
const percentage = (count / stats2.totalRequests * 100).toFixed(1);
|
|
329
|
+
console.log(` ${model}: ${chalk.bold(count)} (${percentage}%)`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
console.log(chalk.gray(`
|
|
333
|
+
${"\u2500".repeat(60)}
|
|
334
|
+
`));
|
|
335
|
+
}
|
|
336
|
+
function displayCSV(stats2) {
|
|
337
|
+
console.log("metric,value");
|
|
338
|
+
console.log(`total_requests,${stats2.totalRequests}`);
|
|
339
|
+
console.log(`successful_requests,${stats2.successfulRequests}`);
|
|
340
|
+
console.log(`failed_requests,${stats2.failedRequests}`);
|
|
341
|
+
console.log(`success_rate,${stats2.successRate.toFixed(2)}`);
|
|
342
|
+
console.log(`total_input_tokens,${stats2.totalInputTokens}`);
|
|
343
|
+
console.log(`total_output_tokens,${stats2.totalOutputTokens}`);
|
|
344
|
+
console.log(`total_tokens,${stats2.totalTokens}`);
|
|
345
|
+
console.log(`total_cost,${stats2.totalCost.toFixed(4)}`);
|
|
346
|
+
console.log(`average_latency,${stats2.averageLatency.toFixed(0)}`);
|
|
347
|
+
}
|
|
348
|
+
async function exportStats(stats2, filePath, format) {
|
|
349
|
+
const { writeFileSync } = await import('node:fs');
|
|
350
|
+
let content;
|
|
351
|
+
if (format === "json") {
|
|
352
|
+
content = JSON.stringify(stats2, null, 2);
|
|
353
|
+
} else if (format === "csv") {
|
|
354
|
+
const lines = [
|
|
355
|
+
"metric,value",
|
|
356
|
+
`total_requests,${stats2.totalRequests}`,
|
|
357
|
+
`successful_requests,${stats2.successfulRequests}`,
|
|
358
|
+
`failed_requests,${stats2.failedRequests}`,
|
|
359
|
+
`success_rate,${stats2.successRate.toFixed(2)}`,
|
|
360
|
+
`total_input_tokens,${stats2.totalInputTokens}`,
|
|
361
|
+
`total_output_tokens,${stats2.totalOutputTokens}`,
|
|
362
|
+
`total_tokens,${stats2.totalTokens}`,
|
|
363
|
+
`total_cost,${stats2.totalCost.toFixed(4)}`,
|
|
364
|
+
`average_latency,${stats2.averageLatency.toFixed(0)}`
|
|
365
|
+
];
|
|
366
|
+
content = lines.join("\n");
|
|
367
|
+
} else {
|
|
368
|
+
content = JSON.stringify(stats2, null, 2);
|
|
369
|
+
}
|
|
370
|
+
writeFileSync(filePath, content, "utf-8");
|
|
371
|
+
}
|
|
372
|
+
async function listStatsDates() {
|
|
373
|
+
const storage = getStatsStorage();
|
|
374
|
+
const dates = storage.getAvailableDates();
|
|
375
|
+
if (dates.length === 0) {
|
|
376
|
+
console.log(chalk.yellow("\nNo statistics data available\n"));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
console.log(chalk.cyan.bold("\n\u{1F4C5} Available Statistics Dates:"));
|
|
380
|
+
console.log(chalk.gray("\u2500".repeat(40)));
|
|
381
|
+
for (const date of dates) {
|
|
382
|
+
const records = storage.getRecordsByDate(date);
|
|
383
|
+
console.log(` ${date}: ${chalk.bold(records.length)} requests`);
|
|
384
|
+
}
|
|
385
|
+
console.log(chalk.gray(`
|
|
386
|
+
${"\u2500".repeat(40)}
|
|
387
|
+
`));
|
|
388
|
+
}
|
|
389
|
+
async function storageStats() {
|
|
390
|
+
const storage = getStatsStorage();
|
|
391
|
+
const stats2 = storage.getStorageStats();
|
|
392
|
+
console.log(chalk.cyan.bold("\n\u{1F4BE} Storage Statistics:"));
|
|
393
|
+
console.log(chalk.gray("\u2500".repeat(40)));
|
|
394
|
+
console.log(` Total record files: ${chalk.bold(stats2.totalRecordFiles)}`);
|
|
395
|
+
console.log(` Total daily files: ${chalk.bold(stats2.totalDailyFiles)}`);
|
|
396
|
+
console.log(` Total records: ${chalk.bold(stats2.totalRecords.toLocaleString())}`);
|
|
397
|
+
console.log(` Oldest date: ${chalk.bold(stats2.oldestDate || "N/A")}`);
|
|
398
|
+
console.log(` Newest date: ${chalk.bold(stats2.newestDate || "N/A")}`);
|
|
399
|
+
console.log(chalk.gray(`
|
|
400
|
+
${"\u2500".repeat(40)}
|
|
401
|
+
`));
|
|
402
|
+
}
|
|
403
|
+
async function cleanupStats(daysToKeep = 90) {
|
|
404
|
+
const storage = getStatsStorage();
|
|
405
|
+
const deletedCount = storage.cleanupOldRecords(daysToKeep);
|
|
406
|
+
console.log(chalk.green(`
|
|
407
|
+
\u2713 Cleanup complete: ${deletedCount} old records marked for deletion
|
|
408
|
+
`));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export { cleanupStats, listStatsDates, stats, storageStats };
|
|
@@ -2,14 +2,15 @@ import ansis from 'ansis';
|
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import { ZCF_CONFIG_FILE, DEFAULT_CODE_TOOL_TYPE, isCodeToolType } from './constants.mjs';
|
|
4
4
|
import { i18n, ensureI18nInitialized } from './index2.mjs';
|
|
5
|
-
import {
|
|
6
|
-
import { r as resolveCodeType } from '../shared/ccjk.
|
|
5
|
+
import { readZcfConfig } from './ccjk-config.mjs';
|
|
6
|
+
import { r as resolveCodeType } from '../shared/ccjk.CUdzQluX.mjs';
|
|
7
7
|
import { h as handleExitPromptError, a as handleGeneralError } from '../shared/ccjk.tB4-Y4Qb.mjs';
|
|
8
8
|
import { p as promptBoolean, a as addNumbersToChoices } from '../shared/ccjk.DhBeLRzf.mjs';
|
|
9
9
|
import { homedir } from 'node:os';
|
|
10
10
|
import { pathExists } from 'fs-extra';
|
|
11
11
|
import { join } from 'pathe';
|
|
12
12
|
import { exec } from 'tinyexec';
|
|
13
|
+
import { readJsonConfig, writeJsonConfig } from './json-config.mjs';
|
|
13
14
|
import { m as moveToTrash } from '../shared/ccjk.DGjQxTq_.mjs';
|
|
14
15
|
import 'node:fs';
|
|
15
16
|
import 'node:process';
|
|
@@ -547,7 +548,7 @@ async function uninstall(options = {}) {
|
|
|
547
548
|
}
|
|
548
549
|
const uninstaller = new ZcfUninstaller(options.lang || "en");
|
|
549
550
|
if (codeType === "codex") {
|
|
550
|
-
const { runCodexUninstall } = await import('./codex.mjs').then(function (n) { return n.
|
|
551
|
+
const { runCodexUninstall } = await import('./codex.mjs').then(function (n) { return n.Q; });
|
|
551
552
|
await runCodexUninstall();
|
|
552
553
|
return;
|
|
553
554
|
}
|
package/dist/chunks/update.mjs
CHANGED
|
@@ -3,9 +3,9 @@ import { version } from './package.mjs';
|
|
|
3
3
|
import { DEFAULT_CODE_TOOL_TYPE, resolveCodeToolType as resolveCodeToolType$1, isCodeToolType } from './constants.mjs';
|
|
4
4
|
import { i18n } from './index2.mjs';
|
|
5
5
|
import { d as displayBanner } from '../shared/ccjk.BhKlRJ0h.mjs';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { u as updatePromptOnly, s as selectAndInstallWorkflows } from '../shared/ccjk.
|
|
6
|
+
import { readZcfConfig, updateZcfConfig } from './ccjk-config.mjs';
|
|
7
|
+
import { C as runCodexUpdate } from './codex.mjs';
|
|
8
|
+
import { u as updatePromptOnly, s as selectAndInstallWorkflows } from '../shared/ccjk.rLRHmcqD.mjs';
|
|
9
9
|
import { h as handleExitPromptError, a as handleGeneralError } from '../shared/ccjk.tB4-Y4Qb.mjs';
|
|
10
10
|
import { resolveAiOutputLanguage } from './prompts.mjs';
|
|
11
11
|
import { checkClaudeCodeVersionAndPrompt } from './version-checker.mjs';
|
|
@@ -20,15 +20,18 @@ import 'smol-toml';
|
|
|
20
20
|
import './fs-operations.mjs';
|
|
21
21
|
import 'node:crypto';
|
|
22
22
|
import 'node:fs/promises';
|
|
23
|
+
import './json-config.mjs';
|
|
23
24
|
import 'dayjs';
|
|
24
25
|
import 'inquirer';
|
|
25
26
|
import 'ora';
|
|
26
27
|
import 'semver';
|
|
27
28
|
import 'tinyexec';
|
|
29
|
+
import './config.mjs';
|
|
28
30
|
import './platform.mjs';
|
|
29
31
|
import '../shared/ccjk.DhBeLRzf.mjs';
|
|
30
32
|
import 'inquirer-toggle';
|
|
31
33
|
import 'node:child_process';
|
|
34
|
+
import './workflows2.mjs';
|
|
32
35
|
import 'node:path';
|
|
33
36
|
import 'node:util';
|
|
34
37
|
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { ensureI18nInitialized, i18n } from './index2.mjs';
|
|
2
|
+
import 'node:fs';
|
|
3
|
+
import 'node:process';
|
|
4
|
+
import 'node:url';
|
|
5
|
+
import 'i18next';
|
|
6
|
+
import 'i18next-fs-backend';
|
|
7
|
+
import 'pathe';
|
|
8
|
+
|
|
9
|
+
const WORKFLOW_CONFIG_BASE = [
|
|
10
|
+
{
|
|
11
|
+
id: "interviewWorkflow",
|
|
12
|
+
defaultSelected: true,
|
|
13
|
+
order: 1,
|
|
14
|
+
commands: ["interview.md"],
|
|
15
|
+
agents: [],
|
|
16
|
+
autoInstallAgents: false,
|
|
17
|
+
category: "interview",
|
|
18
|
+
displayCategory: "planning",
|
|
19
|
+
outputDir: "interview",
|
|
20
|
+
metadata: {
|
|
21
|
+
version: "1.0.0",
|
|
22
|
+
addedDate: "2025-01",
|
|
23
|
+
tags: ["recommended", "popular"],
|
|
24
|
+
difficulty: "beginner"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "essentialTools",
|
|
29
|
+
defaultSelected: true,
|
|
30
|
+
order: 2,
|
|
31
|
+
commands: ["init-project.md", "feat.md"],
|
|
32
|
+
agents: [
|
|
33
|
+
{ id: "init-architect", filename: "init-architect.md", required: true },
|
|
34
|
+
{ id: "get-current-datetime", filename: "get-current-datetime.md", required: true },
|
|
35
|
+
{ id: "planner", filename: "planner.md", required: true },
|
|
36
|
+
{ id: "ui-ux-designer", filename: "ui-ux-designer.md", required: true }
|
|
37
|
+
],
|
|
38
|
+
autoInstallAgents: true,
|
|
39
|
+
category: "essential",
|
|
40
|
+
displayCategory: "planning",
|
|
41
|
+
outputDir: "essential",
|
|
42
|
+
metadata: {
|
|
43
|
+
version: "1.0.0",
|
|
44
|
+
addedDate: "2025-01",
|
|
45
|
+
tags: ["essential"],
|
|
46
|
+
difficulty: "beginner"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "gitWorkflow",
|
|
51
|
+
defaultSelected: true,
|
|
52
|
+
order: 3,
|
|
53
|
+
commands: ["git-commit.md", "git-rollback.md", "git-cleanBranches.md", "git-worktree.md"],
|
|
54
|
+
agents: [],
|
|
55
|
+
autoInstallAgents: false,
|
|
56
|
+
category: "git",
|
|
57
|
+
displayCategory: "versionControl",
|
|
58
|
+
outputDir: "git",
|
|
59
|
+
metadata: {
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
addedDate: "2025-01",
|
|
62
|
+
tags: ["popular"],
|
|
63
|
+
difficulty: "beginner"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "sixStepsWorkflow",
|
|
68
|
+
defaultSelected: false,
|
|
69
|
+
order: 4,
|
|
70
|
+
commands: ["workflow.md"],
|
|
71
|
+
agents: [],
|
|
72
|
+
autoInstallAgents: false,
|
|
73
|
+
category: "sixStep",
|
|
74
|
+
displayCategory: "development",
|
|
75
|
+
outputDir: "workflow",
|
|
76
|
+
metadata: {
|
|
77
|
+
version: "1.0.0",
|
|
78
|
+
addedDate: "2025-01",
|
|
79
|
+
tags: ["professional"],
|
|
80
|
+
difficulty: "intermediate"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
];
|
|
84
|
+
function getWorkflowConfigs() {
|
|
85
|
+
ensureI18nInitialized();
|
|
86
|
+
const workflowTranslations = [
|
|
87
|
+
{
|
|
88
|
+
id: "interviewWorkflow",
|
|
89
|
+
name: i18n.t("workflow:workflowOption.interviewWorkflow"),
|
|
90
|
+
description: i18n.t("workflow:workflowDescription.interviewWorkflow"),
|
|
91
|
+
stats: i18n.t("workflow:workflowStats.interviewWorkflow")
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "essentialTools",
|
|
95
|
+
name: i18n.t("workflow:workflowOption.essentialTools"),
|
|
96
|
+
description: i18n.t("workflow:workflowDescription.essentialTools"),
|
|
97
|
+
stats: i18n.t("workflow:workflowStats.essentialTools")
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "gitWorkflow",
|
|
101
|
+
name: i18n.t("workflow:workflowOption.gitWorkflow"),
|
|
102
|
+
description: i18n.t("workflow:workflowDescription.gitWorkflow"),
|
|
103
|
+
stats: i18n.t("workflow:workflowStats.gitWorkflow")
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "sixStepsWorkflow",
|
|
107
|
+
name: i18n.t("workflow:workflowOption.sixStepsWorkflow"),
|
|
108
|
+
description: i18n.t("workflow:workflowDescription.sixStepsWorkflow"),
|
|
109
|
+
stats: i18n.t("workflow:workflowStats.sixStepsWorkflow")
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
return WORKFLOW_CONFIG_BASE.map((baseConfig) => {
|
|
113
|
+
const translation = workflowTranslations.find((t) => t.id === baseConfig.id);
|
|
114
|
+
return {
|
|
115
|
+
...baseConfig,
|
|
116
|
+
name: translation?.name || baseConfig.id,
|
|
117
|
+
description: translation?.description,
|
|
118
|
+
stats: translation?.stats
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function getWorkflowConfig(workflowId) {
|
|
123
|
+
return getWorkflowConfigs().find((config) => config.id === workflowId);
|
|
124
|
+
}
|
|
125
|
+
function getOrderedWorkflows() {
|
|
126
|
+
return getWorkflowConfigs().sort((a, b) => a.order - b.order);
|
|
127
|
+
}
|
|
128
|
+
function getTagLabel(tag) {
|
|
129
|
+
ensureI18nInitialized();
|
|
130
|
+
const tagKeys = {
|
|
131
|
+
recommended: "workflow:tags.recommended",
|
|
132
|
+
popular: "workflow:tags.popular",
|
|
133
|
+
new: "workflow:tags.new",
|
|
134
|
+
essential: "workflow:tags.essential",
|
|
135
|
+
professional: "workflow:tags.professional"
|
|
136
|
+
};
|
|
137
|
+
return i18n.t(tagKeys[tag]);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export { WORKFLOW_CONFIG_BASE, getOrderedWorkflows, getTagLabel, getWorkflowConfig, getWorkflowConfigs };
|