cc-claw 0.12.1 → 0.12.2
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/cli.js +308 -53
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -72,7 +72,7 @@ var VERSION;
|
|
|
72
72
|
var init_version = __esm({
|
|
73
73
|
"src/version.ts"() {
|
|
74
74
|
"use strict";
|
|
75
|
-
VERSION = true ? "0.12.
|
|
75
|
+
VERSION = true ? "0.12.2" : (() => {
|
|
76
76
|
try {
|
|
77
77
|
return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
78
78
|
} catch {
|
|
@@ -678,8 +678,12 @@ var init_identity = __esm({
|
|
|
678
678
|
// src/reflection/store.ts
|
|
679
679
|
var store_exports4 = {};
|
|
680
680
|
__export(store_exports4, {
|
|
681
|
+
advanceReviewSession: () => advanceReviewSession,
|
|
681
682
|
cleanupBackupFiles: () => cleanupBackupFiles,
|
|
682
683
|
cleanupReflectionData: () => cleanupReflectionData,
|
|
684
|
+
createReviewSession: () => createReviewSession,
|
|
685
|
+
deleteReviewSession: () => deleteReviewSession,
|
|
686
|
+
getAppliedCountTodayForFile: () => getAppliedCountTodayForFile,
|
|
683
687
|
getAppliedInsightCountToday: () => getAppliedInsightCountToday,
|
|
684
688
|
getAppliedInsights: () => getAppliedInsights,
|
|
685
689
|
getGrowthMetrics: () => getGrowthMetrics,
|
|
@@ -688,8 +692,10 @@ __export(store_exports4, {
|
|
|
688
692
|
getPendingInsightCount: () => getPendingInsightCount,
|
|
689
693
|
getPendingInsights: () => getPendingInsights,
|
|
690
694
|
getReflectionModelConfig: () => getReflectionModelConfig,
|
|
695
|
+
getReflectionSettings: () => getReflectionSettings,
|
|
691
696
|
getReflectionStatus: () => getReflectionStatus,
|
|
692
697
|
getRejectedInsights: () => getRejectedInsights,
|
|
698
|
+
getReviewSession: () => getReviewSession,
|
|
693
699
|
getSignalCountForChat: () => getSignalCountForChat,
|
|
694
700
|
getUnprocessedSignalCount: () => getUnprocessedSignalCount,
|
|
695
701
|
getUnprocessedSignals: () => getUnprocessedSignals,
|
|
@@ -698,6 +704,7 @@ __export(store_exports4, {
|
|
|
698
704
|
logSignal: () => logSignal,
|
|
699
705
|
markSignalsProcessed: () => markSignalsProcessed,
|
|
700
706
|
setReflectionModelConfig: () => setReflectionModelConfig,
|
|
707
|
+
setReflectionSettings: () => setReflectionSettings,
|
|
701
708
|
setReflectionStatus: () => setReflectionStatus,
|
|
702
709
|
updateInsightEffectiveness: () => updateInsightEffectiveness,
|
|
703
710
|
updateInsightProposal: () => updateInsightProposal,
|
|
@@ -707,16 +714,37 @@ __export(store_exports4, {
|
|
|
707
714
|
});
|
|
708
715
|
import { existsSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
709
716
|
import { join as join3 } from "path";
|
|
710
|
-
function cleanupBackupFiles(ccClawHome) {
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
+
function cleanupBackupFiles(ccClawHome, retentionDays = 7) {
|
|
718
|
+
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
719
|
+
const dirs = [
|
|
720
|
+
join3(ccClawHome, "identity"),
|
|
721
|
+
join3(ccClawHome, "workspace", "context")
|
|
722
|
+
];
|
|
723
|
+
const skillsRoot = join3(ccClawHome, "workspace", "skills");
|
|
724
|
+
try {
|
|
725
|
+
if (existsSync(skillsRoot)) {
|
|
726
|
+
for (const entry of readdirSync(skillsRoot)) {
|
|
727
|
+
const p = join3(skillsRoot, entry);
|
|
728
|
+
try {
|
|
729
|
+
if (statSync(p).isDirectory()) dirs.push(p);
|
|
730
|
+
} catch {
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
} catch {
|
|
735
|
+
}
|
|
736
|
+
for (const dir of dirs) {
|
|
737
|
+
if (!existsSync(dir)) continue;
|
|
717
738
|
try {
|
|
718
|
-
const
|
|
719
|
-
|
|
739
|
+
for (const file of readdirSync(dir)) {
|
|
740
|
+
if (!file.includes(".bak.")) continue;
|
|
741
|
+
const fullPath = join3(dir, file);
|
|
742
|
+
try {
|
|
743
|
+
const stat2 = statSync(fullPath);
|
|
744
|
+
if (stat2.mtimeMs < cutoffMs) unlinkSync(fullPath);
|
|
745
|
+
} catch {
|
|
746
|
+
}
|
|
747
|
+
}
|
|
720
748
|
} catch {
|
|
721
749
|
}
|
|
722
750
|
}
|
|
@@ -792,9 +820,25 @@ function initReflectionTables(db3) {
|
|
|
792
820
|
model TEXT,
|
|
793
821
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
794
822
|
);
|
|
823
|
+
|
|
824
|
+
CREATE TABLE IF NOT EXISTS review_sessions (
|
|
825
|
+
chatId TEXT PRIMARY KEY,
|
|
826
|
+
insightIds TEXT NOT NULL,
|
|
827
|
+
currentIndex INTEGER NOT NULL DEFAULT 0,
|
|
828
|
+
results TEXT NOT NULL DEFAULT '{}',
|
|
829
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
CREATE TABLE IF NOT EXISTS reflection_settings (
|
|
833
|
+
chatId TEXT PRIMARY KEY,
|
|
834
|
+
perFileCap INTEGER NOT NULL DEFAULT 3,
|
|
835
|
+
backupRetentionDays INTEGER NOT NULL DEFAULT 7,
|
|
836
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
837
|
+
);
|
|
795
838
|
`);
|
|
796
839
|
cleanupReflectionData(db3);
|
|
797
|
-
|
|
840
|
+
const settings = getReflectionSettings(db3, "global");
|
|
841
|
+
cleanupBackupFiles(CC_CLAW_HOME, settings.backupRetentionDays);
|
|
798
842
|
}
|
|
799
843
|
function logSignal(db3, params) {
|
|
800
844
|
const result = db3.prepare(`
|
|
@@ -939,6 +983,13 @@ function getAppliedInsightCountToday(db3, chatId) {
|
|
|
939
983
|
`).get(chatId);
|
|
940
984
|
return row.count;
|
|
941
985
|
}
|
|
986
|
+
function getAppliedCountTodayForFile(db3, chatId, targetFile) {
|
|
987
|
+
const row = db3.prepare(`
|
|
988
|
+
SELECT COUNT(*) as count FROM insights
|
|
989
|
+
WHERE chatId = ? AND status = 'applied' AND targetFile = ? AND appliedAt >= date('now') AND appliedAt < date('now', '+1 day')
|
|
990
|
+
`).get(chatId, targetFile);
|
|
991
|
+
return row.count;
|
|
992
|
+
}
|
|
942
993
|
function upsertGrowthMetric(db3, chatId, period, data) {
|
|
943
994
|
db3.prepare(`
|
|
944
995
|
INSERT OR REPLACE INTO growth_metrics (chatId, period, corrections, praises, errors, insightsApplied)
|
|
@@ -966,6 +1017,58 @@ function setReflectionModelConfig(db3, chatId, mode, backend2, model2) {
|
|
|
966
1017
|
VALUES (?, ?, ?, ?)
|
|
967
1018
|
`).run(chatId, mode, backend2 ?? null, model2 ?? null);
|
|
968
1019
|
}
|
|
1020
|
+
function createReviewSession(db3, chatId, insightIds) {
|
|
1021
|
+
db3.prepare(`
|
|
1022
|
+
INSERT OR REPLACE INTO review_sessions (chatId, insightIds, currentIndex, results)
|
|
1023
|
+
VALUES (?, ?, 0, '{}')
|
|
1024
|
+
`).run(chatId, JSON.stringify(insightIds));
|
|
1025
|
+
}
|
|
1026
|
+
function getReviewSession(db3, chatId) {
|
|
1027
|
+
const row = db3.prepare(
|
|
1028
|
+
"SELECT * FROM review_sessions WHERE chatId = ?"
|
|
1029
|
+
).get(chatId);
|
|
1030
|
+
if (!row) return null;
|
|
1031
|
+
return {
|
|
1032
|
+
chatId: row.chatId,
|
|
1033
|
+
insightIds: JSON.parse(row.insightIds),
|
|
1034
|
+
currentIndex: row.currentIndex,
|
|
1035
|
+
results: JSON.parse(row.results),
|
|
1036
|
+
created_at: row.created_at
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function advanceReviewSession(db3, chatId, insightId, action) {
|
|
1040
|
+
const session2 = getReviewSession(db3, chatId);
|
|
1041
|
+
if (!session2) return null;
|
|
1042
|
+
session2.results[insightId] = action;
|
|
1043
|
+
session2.currentIndex = session2.currentIndex + 1;
|
|
1044
|
+
db3.prepare(`
|
|
1045
|
+
UPDATE review_sessions SET currentIndex = ?, results = ? WHERE chatId = ?
|
|
1046
|
+
`).run(session2.currentIndex, JSON.stringify(session2.results), chatId);
|
|
1047
|
+
return session2;
|
|
1048
|
+
}
|
|
1049
|
+
function deleteReviewSession(db3, chatId) {
|
|
1050
|
+
db3.prepare("DELETE FROM review_sessions WHERE chatId = ?").run(chatId);
|
|
1051
|
+
}
|
|
1052
|
+
function getReflectionSettings(db3, chatId) {
|
|
1053
|
+
const row = db3.prepare(
|
|
1054
|
+
"SELECT perFileCap, backupRetentionDays FROM reflection_settings WHERE chatId = ?"
|
|
1055
|
+
).get(chatId);
|
|
1056
|
+
return {
|
|
1057
|
+
perFileCap: row?.perFileCap ?? 3,
|
|
1058
|
+
backupRetentionDays: row?.backupRetentionDays ?? 7
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
function setReflectionSettings(db3, chatId, settings) {
|
|
1062
|
+
const current = getReflectionSettings(db3, chatId);
|
|
1063
|
+
db3.prepare(`
|
|
1064
|
+
INSERT OR REPLACE INTO reflection_settings (chatId, perFileCap, backupRetentionDays)
|
|
1065
|
+
VALUES (?, ?, ?)
|
|
1066
|
+
`).run(
|
|
1067
|
+
chatId,
|
|
1068
|
+
settings.perFileCap ?? current.perFileCap,
|
|
1069
|
+
settings.backupRetentionDays ?? current.backupRetentionDays
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
969
1072
|
function cleanupReflectionData(db3) {
|
|
970
1073
|
db3.prepare(`
|
|
971
1074
|
DELETE FROM feedback_signals WHERE created_at < datetime('now', '-90 days')
|
|
@@ -976,6 +1079,9 @@ function cleanupReflectionData(db3) {
|
|
|
976
1079
|
db3.prepare(`
|
|
977
1080
|
DELETE FROM growth_metrics WHERE created_at < datetime('now', '-365 days')
|
|
978
1081
|
`).run();
|
|
1082
|
+
db3.prepare(`
|
|
1083
|
+
DELETE FROM review_sessions WHERE created_at < datetime('now', '-1 day')
|
|
1084
|
+
`).run();
|
|
979
1085
|
}
|
|
980
1086
|
var init_store4 = __esm({
|
|
981
1087
|
"src/reflection/store.ts"() {
|
|
@@ -6689,6 +6795,7 @@ Rules:
|
|
|
6689
6795
|
- WHY field max 2 sentences
|
|
6690
6796
|
- Only propose changes you are confident about (confidence >= 0.6)
|
|
6691
6797
|
- Never re-propose previously rejected insights
|
|
6798
|
+
- Do not propose more than 3 changes to the same target file in a single analysis (spread across files to avoid drift)
|
|
6692
6799
|
|
|
6693
6800
|
Valid categories:
|
|
6694
6801
|
${categoryList}`);
|
|
@@ -7073,7 +7180,7 @@ __export(apply_exports, {
|
|
|
7073
7180
|
isTargetAllowed: () => isTargetAllowed,
|
|
7074
7181
|
rollbackInsight: () => rollbackInsight
|
|
7075
7182
|
});
|
|
7076
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync3 } from "fs";
|
|
7183
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync3, readdirSync as readdirSync6, unlinkSync as unlinkSync4 } from "fs";
|
|
7077
7184
|
import { join as join11, dirname as dirname2 } from "path";
|
|
7078
7185
|
function isTargetAllowed(relativePath) {
|
|
7079
7186
|
if (relativePath.includes("..")) return false;
|
|
@@ -7124,6 +7231,21 @@ function applyDiff(original, diff, action) {
|
|
|
7124
7231
|
warn(`[reflection/apply] Unknown diff action "${action}", returning original`);
|
|
7125
7232
|
return original;
|
|
7126
7233
|
}
|
|
7234
|
+
function pruneBackups(absolutePath) {
|
|
7235
|
+
const dir = dirname2(absolutePath);
|
|
7236
|
+
const baseName = absolutePath.split("/").pop() ?? "";
|
|
7237
|
+
try {
|
|
7238
|
+
const backups = readdirSync6(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join11(dir, f));
|
|
7239
|
+
while (backups.length > MAX_BACKUPS_PER_FILE) {
|
|
7240
|
+
const oldest = backups.shift();
|
|
7241
|
+
try {
|
|
7242
|
+
unlinkSync4(oldest);
|
|
7243
|
+
} catch {
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
} catch {
|
|
7247
|
+
}
|
|
7248
|
+
}
|
|
7127
7249
|
async function applyInsight(insightId) {
|
|
7128
7250
|
const db3 = getDb();
|
|
7129
7251
|
const insight = getInsightById(db3, insightId);
|
|
@@ -7153,11 +7275,13 @@ async function applyInsight(insightId) {
|
|
|
7153
7275
|
}
|
|
7154
7276
|
}
|
|
7155
7277
|
const chatId = insight.chatId ?? "global";
|
|
7156
|
-
const
|
|
7157
|
-
|
|
7278
|
+
const settings = getReflectionSettings(db3, chatId);
|
|
7279
|
+
const perFileCap = settings.perFileCap;
|
|
7280
|
+
const appliedTodayForFile = getAppliedCountTodayForFile(db3, chatId, insight.targetFile);
|
|
7281
|
+
if (appliedTodayForFile >= perFileCap) {
|
|
7158
7282
|
return {
|
|
7159
7283
|
success: false,
|
|
7160
|
-
message: `
|
|
7284
|
+
message: `Reflection cap reached for ${insight.targetFile} (${appliedTodayForFile}/${perFileCap} today). Try again tomorrow.`
|
|
7161
7285
|
};
|
|
7162
7286
|
}
|
|
7163
7287
|
let original = "";
|
|
@@ -7175,6 +7299,7 @@ async function applyInsight(insightId) {
|
|
|
7175
7299
|
}
|
|
7176
7300
|
if (original) {
|
|
7177
7301
|
writeFileSync3(backupPath, original, "utf-8");
|
|
7302
|
+
pruneBackups(absolutePath);
|
|
7178
7303
|
}
|
|
7179
7304
|
const newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
|
|
7180
7305
|
writeFileSync3(absolutePath, newContent, "utf-8");
|
|
@@ -7295,7 +7420,7 @@ function computeLineDrift(baseline, absolutePath) {
|
|
|
7295
7420
|
const changed = totalBaseline - unchanged;
|
|
7296
7421
|
return changed / totalBaseline;
|
|
7297
7422
|
}
|
|
7298
|
-
var ALLOWED_TARGETS, ALLOWED_PREFIXES, SOUL_LINE_CAP,
|
|
7423
|
+
var ALLOWED_TARGETS, ALLOWED_PREFIXES, SOUL_LINE_CAP, MAX_BACKUPS_PER_FILE;
|
|
7299
7424
|
var init_apply = __esm({
|
|
7300
7425
|
"src/reflection/apply.ts"() {
|
|
7301
7426
|
"use strict";
|
|
@@ -7314,7 +7439,7 @@ var init_apply = __esm({
|
|
|
7314
7439
|
"workspace/skills/"
|
|
7315
7440
|
];
|
|
7316
7441
|
SOUL_LINE_CAP = 100;
|
|
7317
|
-
|
|
7442
|
+
MAX_BACKUPS_PER_FILE = 3;
|
|
7318
7443
|
}
|
|
7319
7444
|
});
|
|
7320
7445
|
|
|
@@ -8132,6 +8257,21 @@ data: ${JSON.stringify(data)}
|
|
|
8132
8257
|
return jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
8133
8258
|
}
|
|
8134
8259
|
}
|
|
8260
|
+
if (url.pathname === "/api/evolve/settings" && req.method === "POST") {
|
|
8261
|
+
try {
|
|
8262
|
+
const body = JSON.parse(await readBody(req));
|
|
8263
|
+
const { setReflectionSettings: setReflectionSettings2, getReflectionSettings: getReflectionSettings2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
8264
|
+
const chatId = body.chatId || (process.env.ALLOWED_CHAT_ID ?? "").split(",")[0]?.trim() || "default";
|
|
8265
|
+
const updates = {};
|
|
8266
|
+
if (body.perFileCap !== void 0) updates.perFileCap = body.perFileCap;
|
|
8267
|
+
if (body.backupRetentionDays !== void 0) updates.backupRetentionDays = body.backupRetentionDays;
|
|
8268
|
+
setReflectionSettings2(getDb(), chatId, updates);
|
|
8269
|
+
const current = getReflectionSettings2(getDb(), chatId);
|
|
8270
|
+
return jsonResponse(res, { success: true, ...current });
|
|
8271
|
+
} catch (err) {
|
|
8272
|
+
return jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8135
8275
|
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
8136
8276
|
return htmlResponse(res, DASHBOARD_HTML.replace("__TOKEN__", DASHBOARD_TOKEN));
|
|
8137
8277
|
}
|
|
@@ -9362,11 +9502,13 @@ __export(propose_exports, {
|
|
|
9362
9502
|
buildEvolveOnboardingKeyboard: () => buildEvolveOnboardingKeyboard,
|
|
9363
9503
|
buildModelKeyboard: () => buildModelKeyboard,
|
|
9364
9504
|
buildProposalKeyboard: () => buildProposalKeyboard,
|
|
9505
|
+
buildReviewCompleteMessage: () => buildReviewCompleteMessage,
|
|
9365
9506
|
buildUndoKeyboard: () => buildUndoKeyboard,
|
|
9366
9507
|
formatDiffCodeBlock: () => formatDiffCodeBlock,
|
|
9367
9508
|
formatGrowthReport: () => formatGrowthReport,
|
|
9368
9509
|
formatNightlySummary: () => formatNightlySummary,
|
|
9369
|
-
formatProposalCard: () => formatProposalCard
|
|
9510
|
+
formatProposalCard: () => formatProposalCard,
|
|
9511
|
+
formatProposalCardWithProgress: () => formatProposalCardWithProgress
|
|
9370
9512
|
});
|
|
9371
9513
|
function pct(ratio) {
|
|
9372
9514
|
return `${Math.round(ratio * 100)}%`;
|
|
@@ -9387,6 +9529,28 @@ function formatProposalCard(params) {
|
|
|
9387
9529
|
}
|
|
9388
9530
|
return lines.join("\n");
|
|
9389
9531
|
}
|
|
9532
|
+
function formatProposalCardWithProgress(params, index, total) {
|
|
9533
|
+
const progress = `\u{1F4CB} Proposal ${index + 1} of ${total}`;
|
|
9534
|
+
const separator = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
|
|
9535
|
+
const card = formatProposalCard(params);
|
|
9536
|
+
return `${progress}
|
|
9537
|
+
${separator}
|
|
9538
|
+
${card}`;
|
|
9539
|
+
}
|
|
9540
|
+
function buildReviewCompleteMessage(results) {
|
|
9541
|
+
const values = Object.values(results);
|
|
9542
|
+
const applied = values.filter((v) => v === "applied").length;
|
|
9543
|
+
const rejected = values.filter((v) => v === "rejected").length;
|
|
9544
|
+
const skipped = values.filter((v) => v === "skipped").length;
|
|
9545
|
+
const total = values.length;
|
|
9546
|
+
const parts = [];
|
|
9547
|
+
if (applied > 0) parts.push(`${applied} applied`);
|
|
9548
|
+
if (rejected > 0) parts.push(`${rejected} rejected`);
|
|
9549
|
+
if (skipped > 0) parts.push(`${skipped} skipped`);
|
|
9550
|
+
return `\u2705 Review complete \u2014 ${total} proposal${total === 1 ? "" : "s"}: ${parts.join(", ")}
|
|
9551
|
+
|
|
9552
|
+
Skipped proposals will appear in your next review.`;
|
|
9553
|
+
}
|
|
9390
9554
|
function formatNightlySummary(insights) {
|
|
9391
9555
|
const count = insights.length;
|
|
9392
9556
|
const header2 = `Nightly Reflection \u2014 ${count} proposal${count === 1 ? "" : "s"} ready`;
|
|
@@ -9430,7 +9594,10 @@ function buildProposalKeyboard(insightId, category) {
|
|
|
9430
9594
|
return [
|
|
9431
9595
|
[
|
|
9432
9596
|
{ label: "Show Diff", data: `evolve:diff:${insightId}`, style: "primary" },
|
|
9433
|
-
{ label: "Discuss", data: `evolve:discuss:${insightId}` }
|
|
9597
|
+
{ label: "Discuss", data: `evolve:discuss:${insightId}` }
|
|
9598
|
+
],
|
|
9599
|
+
[
|
|
9600
|
+
{ label: "Skip \u23ED", data: `evolve:skip:${insightId}` },
|
|
9434
9601
|
{ label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
|
|
9435
9602
|
]
|
|
9436
9603
|
];
|
|
@@ -9438,7 +9605,10 @@ function buildProposalKeyboard(insightId, category) {
|
|
|
9438
9605
|
return [
|
|
9439
9606
|
[
|
|
9440
9607
|
{ label: "Apply", data: `evolve:apply:${insightId}`, style: "success" },
|
|
9441
|
-
{ label: "Discuss", data: `evolve:discuss:${insightId}` }
|
|
9608
|
+
{ label: "Discuss", data: `evolve:discuss:${insightId}` }
|
|
9609
|
+
],
|
|
9610
|
+
[
|
|
9611
|
+
{ label: "Skip \u23ED", data: `evolve:skip:${insightId}` },
|
|
9442
9612
|
{ label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
|
|
9443
9613
|
]
|
|
9444
9614
|
];
|
|
@@ -9959,7 +10129,7 @@ var init_wrap_backend = __esm({
|
|
|
9959
10129
|
});
|
|
9960
10130
|
|
|
9961
10131
|
// src/agents/runners/config-loader.ts
|
|
9962
|
-
import { readFileSync as readFileSync8, readdirSync as
|
|
10132
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync7, existsSync as existsSync14, mkdirSync as mkdirSync5, watchFile, unwatchFile } from "fs";
|
|
9963
10133
|
import { join as join13 } from "path";
|
|
9964
10134
|
import { execFileSync } from "child_process";
|
|
9965
10135
|
function resolveExecutable(config2) {
|
|
@@ -10113,7 +10283,7 @@ function loadAllRunnerConfigs() {
|
|
|
10113
10283
|
mkdirSync5(RUNNERS_PATH, { recursive: true });
|
|
10114
10284
|
return [];
|
|
10115
10285
|
}
|
|
10116
|
-
const files =
|
|
10286
|
+
const files = readdirSync7(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
10117
10287
|
const configs = [];
|
|
10118
10288
|
for (const file of files) {
|
|
10119
10289
|
const config2 = loadRunnerConfig(join13(RUNNERS_PATH, file));
|
|
@@ -10144,7 +10314,7 @@ function watchRunnerConfigs(onChange) {
|
|
|
10144
10314
|
watchedFiles.delete(prev);
|
|
10145
10315
|
}
|
|
10146
10316
|
}
|
|
10147
|
-
const files =
|
|
10317
|
+
const files = readdirSync7(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
|
|
10148
10318
|
for (const file of files) {
|
|
10149
10319
|
const fullPath = join13(RUNNERS_PATH, file);
|
|
10150
10320
|
if (watchedFiles.has(fullPath)) continue;
|
|
@@ -13288,6 +13458,28 @@ function stopAllSideQuests(chatId) {
|
|
|
13288
13458
|
}
|
|
13289
13459
|
}
|
|
13290
13460
|
}
|
|
13461
|
+
async function sendCurrentProposal(chatId, channel) {
|
|
13462
|
+
const { getReviewSession: getReviewSession2, getInsightById: getInsightById2, deleteReviewSession: deleteReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
13463
|
+
const { formatProposalCardWithProgress: formatProposalCardWithProgress2, buildProposalKeyboard: buildProposalKeyboard2, buildReviewCompleteMessage: buildReviewCompleteMessage2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
13464
|
+
const session2 = getReviewSession2(getDb(), chatId);
|
|
13465
|
+
if (!session2) return;
|
|
13466
|
+
if (session2.currentIndex >= session2.insightIds.length) {
|
|
13467
|
+
const summary = buildReviewCompleteMessage2(session2.results);
|
|
13468
|
+
deleteReviewSession2(getDb(), chatId);
|
|
13469
|
+
await channel.sendText(chatId, summary, { parseMode: "plain" });
|
|
13470
|
+
return;
|
|
13471
|
+
}
|
|
13472
|
+
const insightId = session2.insightIds[session2.currentIndex];
|
|
13473
|
+
const insight = getInsightById2(getDb(), insightId);
|
|
13474
|
+
if (!insight || insight.status !== "pending") {
|
|
13475
|
+
const { advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
13476
|
+
advanceReviewSession2(getDb(), chatId, insightId, "skipped");
|
|
13477
|
+
return sendCurrentProposal(chatId, channel);
|
|
13478
|
+
}
|
|
13479
|
+
const card = formatProposalCardWithProgress2(insight, session2.currentIndex, session2.insightIds.length);
|
|
13480
|
+
const kb = buildProposalKeyboard2(insight.id, insight.category);
|
|
13481
|
+
await channel.sendKeyboard(chatId, card, kb);
|
|
13482
|
+
}
|
|
13291
13483
|
async function handleResponseExhaustion(responseText, chatId, msg, channel) {
|
|
13292
13484
|
const raw = responseText.replace(/\n\n🧠 \[.+$/, "").trim();
|
|
13293
13485
|
if (raw.length > 300 || !isExhaustedMessage(raw)) return false;
|
|
@@ -14915,22 +15107,16 @@ Message: "${testMsg}"`, { parseMode: "plain" });
|
|
|
14915
15107
|
await channel.sendText(chatId, "Step 2/3: Analyzing for insights...", { parseMode: "plain" });
|
|
14916
15108
|
try {
|
|
14917
15109
|
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
|
|
14918
|
-
const { formatProposalCard: formatProposalCard2, buildProposalKeyboard: buildProposalKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
14919
15110
|
const insights = await runAnalysis2(chatId, { force: true });
|
|
14920
15111
|
if (insights.length === 0) {
|
|
14921
15112
|
await channel.sendText(chatId, "Step 3/3: No actionable improvements found in this session.", { parseMode: "plain" });
|
|
14922
15113
|
} else {
|
|
14923
|
-
await channel.sendText(chatId, `Step 3/3: Found ${insights.length} proposal(s).`, { parseMode: "plain" });
|
|
14924
|
-
const { getPendingInsights: getPendingInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
15114
|
+
await channel.sendText(chatId, `Step 3/3: Found ${insights.length} proposal(s). Let's review them one by one.`, { parseMode: "plain" });
|
|
15115
|
+
const { getPendingInsights: getPendingInsights2, createReviewSession: createReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
14925
15116
|
const pending = getPendingInsights2(getDb(), chatId);
|
|
14926
|
-
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
await channel.sendKeyboard(chatId, card, kb);
|
|
14930
|
-
}
|
|
14931
|
-
if (insights.length > 5) {
|
|
14932
|
-
await channel.sendText(chatId, `${insights.length - 5} more proposals. Use /evolve to review all.`, { parseMode: "plain" });
|
|
14933
|
-
}
|
|
15117
|
+
const insightIds = pending.slice(0, 5).map((p) => p.id);
|
|
15118
|
+
createReviewSession2(getDb(), chatId, insightIds);
|
|
15119
|
+
await sendCurrentProposal(chatId, channel);
|
|
14934
15120
|
}
|
|
14935
15121
|
} catch (e) {
|
|
14936
15122
|
await channel.sendText(chatId, `Analysis failed: ${e}. You can review any pending proposals with /evolve.`, { parseMode: "plain" });
|
|
@@ -16677,21 +16863,16 @@ Result: ${task.result.slice(0, 500)}` : ""
|
|
|
16677
16863
|
break;
|
|
16678
16864
|
}
|
|
16679
16865
|
case "review": {
|
|
16680
|
-
const { getPendingInsights: getPendingInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16681
|
-
const { formatProposalCard: formatProposalCard2, buildProposalKeyboard: buildProposalKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
16866
|
+
const { getPendingInsights: getPendingInsights2, createReviewSession: createReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16682
16867
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
16683
16868
|
const pending = getPendingInsights2(getDb2(), chatId);
|
|
16684
16869
|
if (pending.length === 0) {
|
|
16685
16870
|
await channel.sendText(chatId, "No pending proposals.", { parseMode: "plain" });
|
|
16686
16871
|
} else {
|
|
16687
|
-
|
|
16688
|
-
|
|
16689
|
-
|
|
16690
|
-
|
|
16691
|
-
}
|
|
16692
|
-
if (pending.length > 5) {
|
|
16693
|
-
await channel.sendText(chatId, `${pending.length - 5} more proposals. Run /evolve again to see next batch.`, { parseMode: "plain" });
|
|
16694
|
-
}
|
|
16872
|
+
const insightIds = pending.slice(0, 5).map((p) => p.id);
|
|
16873
|
+
createReviewSession2(getDb2(), chatId, insightIds);
|
|
16874
|
+
await channel.sendText(chatId, `${pending.length} proposal(s) ready. Let's review them one by one.`, { parseMode: "plain" });
|
|
16875
|
+
await sendCurrentProposal(chatId, channel);
|
|
16695
16876
|
}
|
|
16696
16877
|
break;
|
|
16697
16878
|
}
|
|
@@ -16709,10 +16890,16 @@ Result: ${task.result.slice(0, 500)}` : ""
|
|
|
16709
16890
|
const { applyInsight: applyInsight2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
|
|
16710
16891
|
const result = await applyInsight2(parseInt(idStr, 10));
|
|
16711
16892
|
await channel.sendText(chatId, result.message, { parseMode: "plain" });
|
|
16893
|
+
const { advanceReviewSession: arAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16894
|
+
arAdvance(getDb(), chatId, parseInt(idStr, 10), "applied");
|
|
16895
|
+
await sendCurrentProposal(chatId, channel);
|
|
16712
16896
|
break;
|
|
16713
16897
|
}
|
|
16714
16898
|
case "skip": {
|
|
16715
16899
|
await channel.sendText(chatId, "Skipped \u2014 will show again next review.", { parseMode: "plain" });
|
|
16900
|
+
const { advanceReviewSession: skAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16901
|
+
skAdvance(getDb(), chatId, parseInt(idStr, 10), "skipped");
|
|
16902
|
+
await sendCurrentProposal(chatId, channel);
|
|
16716
16903
|
break;
|
|
16717
16904
|
}
|
|
16718
16905
|
case "discuss": {
|
|
@@ -16784,10 +16971,10 @@ Result: ${task.result.slice(0, 500)}` : ""
|
|
|
16784
16971
|
join19(homedir6(), ".cc-claw", "workspace", "skills")
|
|
16785
16972
|
];
|
|
16786
16973
|
try {
|
|
16787
|
-
const { readdirSync:
|
|
16974
|
+
const { readdirSync: readdirSync8, statSync: statSync9 } = await import("fs");
|
|
16788
16975
|
for (const dir of skillDirs) {
|
|
16789
16976
|
if (!existsSync19(dir)) continue;
|
|
16790
|
-
for (const entry of
|
|
16977
|
+
for (const entry of readdirSync8(dir)) {
|
|
16791
16978
|
if (statSync9(join19(dir, entry)).isDirectory()) {
|
|
16792
16979
|
targets.push({ label: `skills/${entry}`, path: `workspace/skills/${entry}/SKILL.md` });
|
|
16793
16980
|
}
|
|
@@ -16862,14 +17049,20 @@ Pick a different file for this change. Identity files (SOUL/USER) shape personal
|
|
|
16862
17049
|
break;
|
|
16863
17050
|
}
|
|
16864
17051
|
case "discuss-back": {
|
|
16865
|
-
const { getInsightById: bkIns } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
16866
|
-
const { formatProposalCard:
|
|
17052
|
+
const { getInsightById: bkIns, getReviewSession: bkSession } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
17053
|
+
const { formatProposalCardWithProgress: formatProposalCardWithProgress2, formatProposalCard: bkCardFn, buildProposalKeyboard: bkKb } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
16867
17054
|
const { getDb: bkDb } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
16868
17055
|
const bkInsight = bkIns(bkDb(), parseInt(idStr, 10));
|
|
16869
17056
|
if (bkInsight && bkInsight.status === "pending") {
|
|
16870
|
-
const
|
|
17057
|
+
const session2 = bkSession(bkDb(), chatId);
|
|
16871
17058
|
const kb = bkKb(bkInsight.id, bkInsight.category);
|
|
16872
|
-
|
|
17059
|
+
if (session2) {
|
|
17060
|
+
const card = formatProposalCardWithProgress2(bkInsight, session2.currentIndex, session2.insightIds.length);
|
|
17061
|
+
await channel.sendKeyboard(chatId, card, kb);
|
|
17062
|
+
} else {
|
|
17063
|
+
const card = bkCardFn(bkInsight);
|
|
17064
|
+
await channel.sendKeyboard(chatId, card, kb);
|
|
17065
|
+
}
|
|
16873
17066
|
}
|
|
16874
17067
|
break;
|
|
16875
17068
|
}
|
|
@@ -16878,6 +17071,9 @@ Pick a different file for this change. Identity files (SOUL/USER) shape personal
|
|
|
16878
17071
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
16879
17072
|
updateInsightStatus2(getDb2(), parseInt(idStr, 10), "rejected");
|
|
16880
17073
|
await channel.sendText(chatId, "Rejected. Won't propose similar changes.", { parseMode: "plain" });
|
|
17074
|
+
const { advanceReviewSession: rjAdvance } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
17075
|
+
rjAdvance(getDb2(), chatId, parseInt(idStr, 10), "rejected");
|
|
17076
|
+
await sendCurrentProposal(chatId, channel);
|
|
16881
17077
|
break;
|
|
16882
17078
|
}
|
|
16883
17079
|
case "stats": {
|
|
@@ -18862,7 +19058,7 @@ __export(service_exports, {
|
|
|
18862
19058
|
serviceStatus: () => serviceStatus,
|
|
18863
19059
|
uninstallService: () => uninstallService
|
|
18864
19060
|
});
|
|
18865
|
-
import { existsSync as existsSync24, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7, unlinkSync as
|
|
19061
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
18866
19062
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
18867
19063
|
import { homedir as homedir8, platform } from "os";
|
|
18868
19064
|
import { join as join23, dirname as dirname4 } from "path";
|
|
@@ -18959,7 +19155,7 @@ function uninstallMacOS() {
|
|
|
18959
19155
|
execFileSync2("launchctl", ["unload", PLIST_PATH]);
|
|
18960
19156
|
} catch {
|
|
18961
19157
|
}
|
|
18962
|
-
|
|
19158
|
+
unlinkSync5(PLIST_PATH);
|
|
18963
19159
|
console.log(" Service uninstalled.");
|
|
18964
19160
|
}
|
|
18965
19161
|
function formatUptime(seconds) {
|
|
@@ -19047,7 +19243,7 @@ function uninstallLinux() {
|
|
|
19047
19243
|
execFileSync2("systemctl", ["--user", "disable", "cc-claw"]);
|
|
19048
19244
|
} catch {
|
|
19049
19245
|
}
|
|
19050
|
-
|
|
19246
|
+
unlinkSync5(UNIT_PATH);
|
|
19051
19247
|
execFileSync2("systemctl", ["--user", "daemon-reload"]);
|
|
19052
19248
|
console.log(" Service uninstalled.");
|
|
19053
19249
|
}
|
|
@@ -22136,6 +22332,7 @@ __export(evolve_exports, {
|
|
|
22136
22332
|
evolveOff: () => evolveOff,
|
|
22137
22333
|
evolveOn: () => evolveOn,
|
|
22138
22334
|
evolveReject: () => evolveReject,
|
|
22335
|
+
evolveSettings: () => evolveSettings,
|
|
22139
22336
|
evolveStats: () => evolveStats,
|
|
22140
22337
|
evolveStatus: () => evolveStatus,
|
|
22141
22338
|
evolveUndo: () => evolveUndo
|
|
@@ -22492,6 +22689,60 @@ async function evolveHistory(globalOpts, opts) {
|
|
|
22492
22689
|
return lines.join("\n");
|
|
22493
22690
|
});
|
|
22494
22691
|
}
|
|
22692
|
+
async function evolveSettings(globalOpts, opts) {
|
|
22693
|
+
ensureDb3();
|
|
22694
|
+
const { openDatabaseReadOnly: openDatabaseReadOnly2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
22695
|
+
const chatId = resolveChatId(globalOpts);
|
|
22696
|
+
const hasUpdates = opts.perFileCap !== void 0 || opts.backupRetentionDays !== void 0;
|
|
22697
|
+
if (hasUpdates) {
|
|
22698
|
+
await ensureDaemon();
|
|
22699
|
+
const { apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
22700
|
+
const body = {};
|
|
22701
|
+
if (opts.perFileCap !== void 0) {
|
|
22702
|
+
const val = parseInt(opts.perFileCap, 10);
|
|
22703
|
+
if (isNaN(val) || val < 1) {
|
|
22704
|
+
outputError("INVALID_VALUE", "per-file-cap must be a positive integer.");
|
|
22705
|
+
process.exit(1);
|
|
22706
|
+
}
|
|
22707
|
+
body.perFileCap = val;
|
|
22708
|
+
}
|
|
22709
|
+
if (opts.backupRetentionDays !== void 0) {
|
|
22710
|
+
const val = parseInt(opts.backupRetentionDays, 10);
|
|
22711
|
+
if (isNaN(val) || val < 1) {
|
|
22712
|
+
outputError("INVALID_VALUE", "backup-retention-days must be a positive integer.");
|
|
22713
|
+
process.exit(1);
|
|
22714
|
+
}
|
|
22715
|
+
body.backupRetentionDays = val;
|
|
22716
|
+
}
|
|
22717
|
+
const res = await apiPost2("/api/evolve/settings", { chatId, ...body });
|
|
22718
|
+
if (res.ok) {
|
|
22719
|
+
output(res.data, () => `
|
|
22720
|
+
${success("Reflection settings updated.")}
|
|
22721
|
+
`);
|
|
22722
|
+
} else {
|
|
22723
|
+
outputError("SETTINGS_FAILED", `Failed: ${JSON.stringify(res.data)}`);
|
|
22724
|
+
process.exit(1);
|
|
22725
|
+
}
|
|
22726
|
+
} else {
|
|
22727
|
+
const readDb = openDatabaseReadOnly2();
|
|
22728
|
+
const { getReflectionSettings: getReflectionSettings2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22729
|
+
const settings = getReflectionSettings2(readDb, chatId);
|
|
22730
|
+
readDb.close();
|
|
22731
|
+
output(settings, () => {
|
|
22732
|
+
const lines = [
|
|
22733
|
+
"",
|
|
22734
|
+
divider("Reflection Settings"),
|
|
22735
|
+
"",
|
|
22736
|
+
kvLine("Per-file cap", `${settings.perFileCap} changes/day per file`),
|
|
22737
|
+
kvLine("Backup retention", `${settings.backupRetentionDays} days`),
|
|
22738
|
+
"",
|
|
22739
|
+
muted("Update with: cc-claw evolve settings --per-file-cap <n> --backup-retention-days <n>"),
|
|
22740
|
+
""
|
|
22741
|
+
];
|
|
22742
|
+
return lines.join("\n");
|
|
22743
|
+
});
|
|
22744
|
+
}
|
|
22745
|
+
}
|
|
22495
22746
|
var init_evolve = __esm({
|
|
22496
22747
|
"src/cli/commands/evolve.ts"() {
|
|
22497
22748
|
"use strict";
|
|
@@ -23407,6 +23658,10 @@ evolve.command("history").description("Insight history").option("--status <statu
|
|
|
23407
23658
|
const { evolveHistory: evolveHistory2 } = await Promise.resolve().then(() => (init_evolve(), evolve_exports));
|
|
23408
23659
|
await evolveHistory2(program.opts(), opts);
|
|
23409
23660
|
});
|
|
23661
|
+
evolve.command("settings").description("View or update reflection settings").option("--per-file-cap <n>", "Max applies per file per day").option("--backup-retention-days <n>", "Days to keep backup files").action(async (opts) => {
|
|
23662
|
+
const { evolveSettings: evolveSettings2 } = await Promise.resolve().then(() => (init_evolve(), evolve_exports));
|
|
23663
|
+
await evolveSettings2(program.opts(), opts);
|
|
23664
|
+
});
|
|
23410
23665
|
program.command("start", { hidden: true }).description("Run the bot in the foreground (use 'service start' for background daemon)").action(async () => {
|
|
23411
23666
|
await Promise.resolve().then(() => (init_index(), index_exports));
|
|
23412
23667
|
});
|
package/package.json
CHANGED