cc-claw 0.11.0 → 0.11.1

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.
Files changed (2) hide show
  1. package/dist/cli.js +138 -13
  2. 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.11.0" : (() => {
75
+ VERSION = true ? "0.11.1" : (() => {
76
76
  try {
77
77
  return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
78
78
  } catch {
@@ -703,6 +703,7 @@ __export(store_exports4, {
703
703
  setReflectionModelConfig: () => setReflectionModelConfig,
704
704
  setReflectionStatus: () => setReflectionStatus,
705
705
  updateInsightEffectiveness: () => updateInsightEffectiveness,
706
+ updateInsightProposal: () => updateInsightProposal,
706
707
  updateInsightRollback: () => updateInsightRollback,
707
708
  updateInsightStatus: () => updateInsightStatus,
708
709
  upsertGrowthMetric: () => upsertGrowthMetric
@@ -932,6 +933,11 @@ function updateInsightStatus(db3, id, status) {
932
933
  db3.prepare("UPDATE insights SET status = ? WHERE id = ?").run(status, id);
933
934
  }
934
935
  }
936
+ function updateInsightProposal(db3, id, targetFile, proposedDiff, proposedAction) {
937
+ db3.prepare(`
938
+ UPDATE insights SET targetFile = ?, proposedDiff = ?, proposedAction = ? WHERE id = ?
939
+ `).run(targetFile, proposedDiff ?? null, proposedAction ?? null, id);
940
+ }
935
941
  function updateInsightRollback(db3, id, rollbackData) {
936
942
  db3.prepare("UPDATE insights SET rollbackData = ? WHERE id = ?").run(rollbackData, id);
937
943
  }
@@ -12680,16 +12686,20 @@ function pct(ratio) {
12680
12686
  return `${Math.round(ratio * 100)}%`;
12681
12687
  }
12682
12688
  function formatProposalCard(params) {
12683
- const { category, insight, why, targetFile, confidence } = params;
12689
+ const { category, insight, why, targetFile, confidence, proposedAction, proposedDiff } = params;
12684
12690
  const header2 = `[${category}] ${insight}`;
12685
- const whyLine = `Why: ${why}`;
12691
+ const whyLine = why ? `Why: ${why}` : null;
12686
12692
  const confidencePct = pct(confidence);
12687
12693
  const showTarget = category !== "codebase" && targetFile && targetFile !== "codebase";
12688
12694
  const targetLine = showTarget ? `Target: ${targetFile} | Confidence: ${confidencePct}` : `Confidence: ${confidencePct}`;
12689
- return `${header2}
12690
-
12691
- ${whyLine}
12692
- ${targetLine}`;
12695
+ const lines = [header2, ""];
12696
+ if (whyLine) lines.push(whyLine);
12697
+ lines.push(targetLine);
12698
+ if (proposedDiff) {
12699
+ const actionLabel = proposedAction ? ` (${proposedAction})` : "";
12700
+ lines.push("", `Proposed change${actionLabel}:`, "```diff", proposedDiff, "```");
12701
+ }
12702
+ return lines.join("\n");
12693
12703
  }
12694
12704
  function formatNightlySummary(insights) {
12695
12705
  const count = insights.length;
@@ -12734,7 +12744,7 @@ function buildProposalKeyboard(insightId, category) {
12734
12744
  return [
12735
12745
  [
12736
12746
  { label: "Show Diff", data: `evolve:diff:${insightId}`, style: "primary" },
12737
- { label: "Skip", data: `evolve:skip:${insightId}` },
12747
+ { label: "Discuss", data: `evolve:discuss:${insightId}` },
12738
12748
  { label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
12739
12749
  ]
12740
12750
  ];
@@ -12742,7 +12752,7 @@ function buildProposalKeyboard(insightId, category) {
12742
12752
  return [
12743
12753
  [
12744
12754
  { label: "Apply", data: `evolve:apply:${insightId}`, style: "success" },
12745
- { label: "Skip", data: `evolve:skip:${insightId}` },
12755
+ { label: "Discuss", data: `evolve:discuss:${insightId}` },
12746
12756
  { label: "Reject", data: `evolve:reject:${insightId}`, style: "danger" }
12747
12757
  ]
12748
12758
  ];
@@ -14901,6 +14911,26 @@ async function handleText(msg, channel) {
14901
14911
  await handleWizardText(chatId, text, channel);
14902
14912
  return;
14903
14913
  }
14914
+ const discussingInsightId = activeProposalDiscussion.get(chatId);
14915
+ if (discussingInsightId !== void 0) {
14916
+ if (text.toLowerCase() === "done") {
14917
+ activeProposalDiscussion.delete(chatId);
14918
+ const { getInsightById: getInsightById2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
14919
+ const { formatProposalCard: formatProposalCard2, buildProposalKeyboard: buildProposalKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
14920
+ const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
14921
+ const insight = getInsightById2(getDb2(), discussingInsightId);
14922
+ if (insight && insight.status === "pending") {
14923
+ const card = formatProposalCard2(insight);
14924
+ const kb = buildProposalKeyboard2(insight.id, insight.category);
14925
+ await channel.sendKeyboard(chatId, "Discussion complete. Updated proposal:\n\n" + card, kb);
14926
+ } else {
14927
+ await channel.sendText(chatId, "Discussion ended.", { parseMode: "plain" });
14928
+ }
14929
+ return;
14930
+ }
14931
+ await handleProposalDiscussion(chatId, discussingInsightId, text, channel);
14932
+ return;
14933
+ }
14904
14934
  const rememberMatch = text.match(/^remember\s+(?:that\s+)?(.+)/i);
14905
14935
  if (rememberMatch) {
14906
14936
  const content = rememberMatch[1];
@@ -15128,6 +15158,65 @@ After installing, restart the service: cc-claw service restart`;
15128
15158
  }
15129
15159
  return `Error: ${msg}`;
15130
15160
  }
15161
+ async function handleProposalDiscussion(chatId, insightId, userQuestion, channel) {
15162
+ const { getInsightById: getInsightById2, updateInsightProposal: updateInsightProposal2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
15163
+ const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
15164
+ const insight = getInsightById2(getDb2(), insightId);
15165
+ if (!insight) {
15166
+ activeProposalDiscussion.delete(chatId);
15167
+ await channel.sendText(chatId, "Proposal not found. Discussion ended.", { parseMode: "plain" });
15168
+ return;
15169
+ }
15170
+ const prompt = [
15171
+ `You are reviewing a self-learning proposal with the user. They want to discuss it before deciding whether to apply, modify, or reject it.`,
15172
+ ``,
15173
+ `Current proposal:`,
15174
+ `- Category: ${insight.category}`,
15175
+ `- Insight: ${insight.insight}`,
15176
+ `- Why: ${insight.why ?? "not specified"}`,
15177
+ `- Target file: ${insight.targetFile ?? "not specified"}`,
15178
+ `- Proposed action: ${insight.proposedAction ?? "not specified"}`,
15179
+ `- Proposed diff: ${insight.proposedDiff ?? "none"}`,
15180
+ `- Confidence: ${Math.round(insight.confidence * 100)}%`,
15181
+ ``,
15182
+ `The user asks: "${userQuestion}"`,
15183
+ ``,
15184
+ `Answer their question honestly. If they suggest retargeting to a different file or modifying the proposal, agree if it makes sense and output one of these markers:`,
15185
+ `- [RETARGET:filepath] \u2014 to change the target file (e.g., [RETARGET:skills/newsroom/SKILL.md])`,
15186
+ `- [REVISE_DIFF:new diff text] \u2014 to update the proposed change`,
15187
+ `- [REVISE_ACTION:action] \u2014 to change the action type (append/replace/etc.)`,
15188
+ ``,
15189
+ `Only output markers when the user explicitly asks for a change. Otherwise just discuss.`,
15190
+ `Keep responses concise \u2014 this is a quick review conversation, not an essay.`
15191
+ ].join("\n");
15192
+ try {
15193
+ await channel.sendTyping?.(chatId);
15194
+ const response = await askAgent(chatId, prompt, {
15195
+ bootstrapTier: "chat",
15196
+ maxTurns: 1,
15197
+ timeoutMs: 6e4
15198
+ });
15199
+ const responseText = response.text ?? "";
15200
+ const retargetMatch = responseText.match(/\[RETARGET:([^\]]+)\]/);
15201
+ const reviseDiffMatch = responseText.match(/\[REVISE_DIFF:([^\]]+)\]/);
15202
+ const reviseActionMatch = responseText.match(/\[REVISE_ACTION:([^\]]+)\]/);
15203
+ if (retargetMatch || reviseDiffMatch || reviseActionMatch) {
15204
+ const newTarget = retargetMatch?.[1]?.trim() ?? insight.targetFile ?? "";
15205
+ const newDiff = reviseDiffMatch?.[1]?.trim() ?? insight.proposedDiff ?? null;
15206
+ const newAction = reviseActionMatch?.[1]?.trim() ?? insight.proposedAction ?? null;
15207
+ updateInsightProposal2(getDb2(), insightId, newTarget, newDiff, newAction);
15208
+ }
15209
+ const cleanText = responseText.replace(/\[RETARGET:[^\]]+\]/g, "").replace(/\[REVISE_DIFF:[^\]]+\]/g, "").replace(/\[REVISE_ACTION:[^\]]+\]/g, "").trim();
15210
+ if (cleanText) {
15211
+ await channel.sendText(chatId, cleanText);
15212
+ }
15213
+ await channel.sendKeyboard(chatId, "Continue discussing, or type 'done' to finish.", [
15214
+ [{ label: "Done \u2014 show updated proposal", data: `evolve:discuss-done:${insightId}` }]
15215
+ ]);
15216
+ } catch (err) {
15217
+ await channel.sendText(chatId, `Discussion error: ${err.message}`, { parseMode: "plain" });
15218
+ }
15219
+ }
15131
15220
  async function handleSideQuest(parentChatId, msg, channel) {
15132
15221
  const sqId = `sq:${parentChatId}:${randomUUID3()}`;
15133
15222
  const active = activeSideQuests.get(parentChatId) ?? /* @__PURE__ */ new Set();
@@ -16464,7 +16553,8 @@ Result: ${task.result.slice(0, 500)}` : ""
16464
16553
  if (insights.length === 0) {
16465
16554
  await channel.sendText(chatId, "No new insights from recent interactions.", { parseMode: "plain" });
16466
16555
  } else {
16467
- await channel.sendText(chatId, formatNightlySummary2(insights), { parseMode: "plain" });
16556
+ const nightlyItems = insights.map((ins, i) => ({ id: i + 1, ...ins }));
16557
+ await channel.sendText(chatId, formatNightlySummary2(nightlyItems), { parseMode: "plain" });
16468
16558
  }
16469
16559
  } catch (e) {
16470
16560
  await channel.sendText(chatId, `Analysis failed: ${e}`, { parseMode: "plain" });
@@ -16510,6 +16600,29 @@ Result: ${task.result.slice(0, 500)}` : ""
16510
16600
  await channel.sendText(chatId, "Skipped \u2014 will show again next review.", { parseMode: "plain" });
16511
16601
  break;
16512
16602
  }
16603
+ case "discuss": {
16604
+ const insId = parseInt(idStr, 10);
16605
+ activeProposalDiscussion.set(chatId, insId);
16606
+ await channel.sendKeyboard(chatId, `Discussing proposal #${insId}. Send your questions \u2014 type "done" when finished.`, [
16607
+ [{ label: "Done", data: `evolve:discuss-done:${insId}` }]
16608
+ ]);
16609
+ break;
16610
+ }
16611
+ case "discuss-done": {
16612
+ activeProposalDiscussion.delete(chatId);
16613
+ const { getInsightById: getIns } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16614
+ const { formatProposalCard: fmtCard, buildProposalKeyboard: buildKb } = await Promise.resolve().then(() => (init_propose(), propose_exports));
16615
+ const { getDb: getDatabase } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16616
+ const ins = getIns(getDatabase(), parseInt(idStr, 10));
16617
+ if (ins && ins.status === "pending") {
16618
+ const card = fmtCard(ins);
16619
+ const kb = buildKb(ins.id, ins.category);
16620
+ await channel.sendKeyboard(chatId, "Updated proposal:\n\n" + card, kb);
16621
+ } else {
16622
+ await channel.sendText(chatId, "Discussion ended.", { parseMode: "plain" });
16623
+ }
16624
+ break;
16625
+ }
16513
16626
  case "reject": {
16514
16627
  const { updateInsightStatus: updateInsightStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16515
16628
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
@@ -16524,7 +16637,18 @@ Result: ${task.result.slice(0, 500)}` : ""
16524
16637
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16525
16638
  const reportData = buildGrowthReportData2(getDb2(), chatId, 30);
16526
16639
  const modelData = buildModelPerformanceData2(getDb2(), chatId, 30);
16527
- let report = formatGrowthReport2(reportData, modelData);
16640
+ const metricsForReport = {
16641
+ correctionsBefore: reportData.avgCorrectionsFirstHalf,
16642
+ correctionsAfter: reportData.avgCorrectionsSecondHalf,
16643
+ praiseRatio: reportData.praiseRatio,
16644
+ insightsApplied: reportData.totalInsightsApplied,
16645
+ pendingCount: reportData.pendingCount,
16646
+ topInsight: reportData.topInsightId != null ? {
16647
+ insight: `#${reportData.topInsightId}`,
16648
+ effectiveness: reportData.topInsightEffectiveness ?? 0
16649
+ } : null
16650
+ };
16651
+ let report = formatGrowthReport2(metricsForReport, modelData);
16528
16652
  const drift = calculateDrift2(chatId);
16529
16653
  if (drift && (drift.soulDrift > 0.5 || drift.userDrift > 0.5)) {
16530
16654
  report += "\n\nSOUL.md has changed significantly since reflection started.\nTap History in /evolve to review all applied changes.";
@@ -16569,7 +16693,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16569
16693
  logActivity2(getDb2(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
16570
16694
  await channel.sendText(chatId, "\u2705 Self-learning enabled. Signal detection is now active.\nCreate a nightly cron job with /schedule to enable automatic analysis.", { parseMode: "plain" });
16571
16695
  } else {
16572
- setReflectionStatus2(getDb2(), chatId, "frozen", null, null);
16696
+ setReflectionStatus2(getDb2(), chatId, "frozen", void 0, void 0);
16573
16697
  const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
16574
16698
  logActivity2(getDb2(), { chatId, source: "telegram", eventType: "reflection_frozen", summary: "Reflection disabled" });
16575
16699
  await channel.sendText(chatId, "\u26D4 Self-learning disabled. No signals will be collected.", { parseMode: "plain" });
@@ -17157,7 +17281,7 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
17157
17281
  const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
17158
17282
  await channel.sendKeyboard(chatId, header2, buttons);
17159
17283
  }
17160
- var PERM_MODES, VERBOSE_LEVELS, HELP_CATEGORIES, USAGE_WINDOW_MAP, MEDIA_INCOMING_PATH, TONE_PATTERNS, pendingInterrupts, bypassBusyCheck, activeSideQuests, MAX_SIDE_QUESTS, pendingFallbackMessages, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
17284
+ var PERM_MODES, VERBOSE_LEVELS, HELP_CATEGORIES, USAGE_WINDOW_MAP, MEDIA_INCOMING_PATH, TONE_PATTERNS, pendingInterrupts, bypassBusyCheck, activeSideQuests, MAX_SIDE_QUESTS, activeProposalDiscussion, pendingFallbackMessages, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
17161
17285
  var init_router = __esm({
17162
17286
  "src/router.ts"() {
17163
17287
  "use strict";
@@ -17298,6 +17422,7 @@ var init_router = __esm({
17298
17422
  bypassBusyCheck = /* @__PURE__ */ new Set();
17299
17423
  activeSideQuests = /* @__PURE__ */ new Map();
17300
17424
  MAX_SIDE_QUESTS = 2;
17425
+ activeProposalDiscussion = /* @__PURE__ */ new Map();
17301
17426
  pendingFallbackMessages = /* @__PURE__ */ new Map();
17302
17427
  dashboardClawWarnings = /* @__PURE__ */ new Map();
17303
17428
  pendingSummaryUndo = /* @__PURE__ */ new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-claw",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "CC-Claw: Personal AI assistant on Telegram — multi-backend (Claude, Gemini, Codex, Cursor), sub-agent orchestration, MCP management",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",