cc-claw 0.19.0 → 0.19.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 +122 -34
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var VERSION;
|
|
|
33
33
|
var init_version = __esm({
|
|
34
34
|
"src/version.ts"() {
|
|
35
35
|
"use strict";
|
|
36
|
-
VERSION = true ? "0.19.
|
|
36
|
+
VERSION = true ? "0.19.2" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -623,7 +623,7 @@ function listMcpServers(db3) {
|
|
|
623
623
|
}
|
|
624
624
|
function addPropagation(db3, mcpName, runnerId, scope) {
|
|
625
625
|
db3.prepare(`
|
|
626
|
-
INSERT INTO mcp_propagation (mcpName, runnerId, scope, addedAt) VALUES (?, ?, ?, datetime('now'))
|
|
626
|
+
INSERT OR IGNORE INTO mcp_propagation (mcpName, runnerId, scope, addedAt) VALUES (?, ?, ?, datetime('now'))
|
|
627
627
|
`).run(mcpName, runnerId, scope);
|
|
628
628
|
}
|
|
629
629
|
function removePropagation(db3, mcpName, runnerId, scope) {
|
|
@@ -13122,7 +13122,14 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
|
|
|
13122
13122
|
const elapsed = () => `${((Date.now() - t0) / 1e3).toFixed(1)}s`;
|
|
13123
13123
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
13124
13124
|
const stderrChunks = [];
|
|
13125
|
-
|
|
13125
|
+
let stderrBytes = 0;
|
|
13126
|
+
const STDERR_CAP = 1e6;
|
|
13127
|
+
proc.stderr?.on("data", (chunk) => {
|
|
13128
|
+
if (stderrBytes < STDERR_CAP) {
|
|
13129
|
+
stderrChunks.push(chunk);
|
|
13130
|
+
stderrBytes += chunk.length;
|
|
13131
|
+
}
|
|
13132
|
+
});
|
|
13126
13133
|
const rl2 = createInterface5({ input: proc.stdout });
|
|
13127
13134
|
let firstLine = true;
|
|
13128
13135
|
const frTimeoutMs = adapter.id === BACKEND.GEMINI ? opts?.firstResponseTimeoutMs ?? FIRST_RESPONSE_TIMEOUT_MS : 0;
|
|
@@ -15624,7 +15631,7 @@ var init_live_status = __esm({
|
|
|
15624
15631
|
globalLastFlushAt = 0;
|
|
15625
15632
|
TRIM_THRESHOLD = 3500;
|
|
15626
15633
|
MAX_ENTRIES = 200;
|
|
15627
|
-
LiveStatusMessage = class {
|
|
15634
|
+
LiveStatusMessage = class _LiveStatusMessage {
|
|
15628
15635
|
constructor(chatId, channel, modelLabel, verboseLevel, showThinking = false) {
|
|
15629
15636
|
this.chatId = chatId;
|
|
15630
15637
|
this.channel = channel;
|
|
@@ -15642,6 +15649,9 @@ var init_live_status = __esm({
|
|
|
15642
15649
|
nextFlushAllowedAt = 0;
|
|
15643
15650
|
/** Tracks whether entries have been trimmed at least once (for display hint). */
|
|
15644
15651
|
hasTrimmed = false;
|
|
15652
|
+
/** Consecutive non-429 edit failures. After 3, stop trying (message likely deleted). */
|
|
15653
|
+
consecutiveEditFailures = 0;
|
|
15654
|
+
static MAX_EDIT_FAILURES = 3;
|
|
15645
15655
|
/** Resolve flush interval based on chat type (group chats are rate-limited more aggressively). */
|
|
15646
15656
|
get flushIntervalMs() {
|
|
15647
15657
|
const numericId = parseInt(this.chatId);
|
|
@@ -15720,6 +15730,7 @@ var init_live_status = __esm({
|
|
|
15720
15730
|
// ── Internal ──────────────────────────────────────────────────────────
|
|
15721
15731
|
async flush() {
|
|
15722
15732
|
if (this.finalized || !this.messageId || !this.channel.editText) return;
|
|
15733
|
+
if (this.consecutiveEditFailures >= _LiveStatusMessage.MAX_EDIT_FAILURES) return;
|
|
15723
15734
|
if (Date.now() < this.nextFlushAllowedAt) return;
|
|
15724
15735
|
if (!canFlushGlobally()) return;
|
|
15725
15736
|
const deduped = dedupThinking(this.entries);
|
|
@@ -15729,6 +15740,7 @@ var init_live_status = __esm({
|
|
|
15729
15740
|
markGlobalFlush();
|
|
15730
15741
|
try {
|
|
15731
15742
|
await this.channel.editText(this.chatId, this.messageId, body, "plain");
|
|
15743
|
+
this.consecutiveEditFailures = 0;
|
|
15732
15744
|
} catch (err) {
|
|
15733
15745
|
this.handleRateLimit(err);
|
|
15734
15746
|
}
|
|
@@ -15762,7 +15774,12 @@ var init_live_status = __esm({
|
|
|
15762
15774
|
this.nextFlushAllowedAt = Date.now() + retrySec * 1e3;
|
|
15763
15775
|
log(`[live-status] 429 rate-limited, backing off ${retrySec}s`);
|
|
15764
15776
|
} else {
|
|
15765
|
-
|
|
15777
|
+
this.consecutiveEditFailures++;
|
|
15778
|
+
if (this.consecutiveEditFailures >= _LiveStatusMessage.MAX_EDIT_FAILURES) {
|
|
15779
|
+
log(`[live-status] ${this.consecutiveEditFailures} consecutive edit failures \u2014 abandoning status updates (message likely deleted)`);
|
|
15780
|
+
} else {
|
|
15781
|
+
log(`[live-status] edit failed (${this.consecutiveEditFailures}/${_LiveStatusMessage.MAX_EDIT_FAILURES}): ${msg}`);
|
|
15782
|
+
}
|
|
15766
15783
|
}
|
|
15767
15784
|
}
|
|
15768
15785
|
};
|
|
@@ -16319,7 +16336,11 @@ async function sendResponse(chatId, channel, text, messageId, replyToMessageId)
|
|
|
16319
16336
|
error("[router] TTS failed, falling back to text:", err);
|
|
16320
16337
|
}
|
|
16321
16338
|
}
|
|
16322
|
-
|
|
16339
|
+
try {
|
|
16340
|
+
await channel.sendText(chatId, cleanText, replyToMessageId ? { replyToMessageId } : void 0);
|
|
16341
|
+
} catch (err) {
|
|
16342
|
+
error("[response] Final sendText failed \u2014 response lost:", err instanceof Error ? err.message : err);
|
|
16343
|
+
}
|
|
16323
16344
|
}
|
|
16324
16345
|
async function handleResponseExhaustion(responseText, chatId, msg, channel) {
|
|
16325
16346
|
const raw = responseText.replace(/\n\n🧠 \[.+$/, "").trim();
|
|
@@ -24669,10 +24690,16 @@ import { marked } from "marked";
|
|
|
24669
24690
|
function escapeHtml2(text) {
|
|
24670
24691
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
24671
24692
|
}
|
|
24693
|
+
function stripNonTelegramTags(html) {
|
|
24694
|
+
return html.replace(/<\/?([a-zA-Z][a-zA-Z0-9_-]*)\b[^>]*>/g, (fullMatch, tagName) => {
|
|
24695
|
+
if (TELEGRAM_ALLOWED_TAGS.has(tagName.toLowerCase())) return fullMatch;
|
|
24696
|
+
return fullMatch.replace(/</g, "<").replace(/>/g, ">");
|
|
24697
|
+
});
|
|
24698
|
+
}
|
|
24672
24699
|
function formatForTelegram(markdown) {
|
|
24673
24700
|
try {
|
|
24674
24701
|
const html = marked(markdown);
|
|
24675
|
-
return html.trimEnd();
|
|
24702
|
+
return stripNonTelegramTags(html).trimEnd();
|
|
24676
24703
|
} catch {
|
|
24677
24704
|
return formatForTelegramFallback(markdown);
|
|
24678
24705
|
}
|
|
@@ -24759,7 +24786,7 @@ function findSafeSplitPoint(text, maxLen) {
|
|
|
24759
24786
|
}
|
|
24760
24787
|
return safePositions[safePositions.length - 1];
|
|
24761
24788
|
}
|
|
24762
|
-
var MAX_MESSAGE_LENGTH;
|
|
24789
|
+
var MAX_MESSAGE_LENGTH, TELEGRAM_ALLOWED_TAGS;
|
|
24763
24790
|
var init_telegram = __esm({
|
|
24764
24791
|
"src/formatter/telegram.ts"() {
|
|
24765
24792
|
"use strict";
|
|
@@ -24823,6 +24850,35 @@ ${body.replace(/<[^>]*>/g, "").trim()}</code>
|
|
|
24823
24850
|
}
|
|
24824
24851
|
}
|
|
24825
24852
|
});
|
|
24853
|
+
TELEGRAM_ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
24854
|
+
"b",
|
|
24855
|
+
"strong",
|
|
24856
|
+
// bold
|
|
24857
|
+
"i",
|
|
24858
|
+
"em",
|
|
24859
|
+
// italic
|
|
24860
|
+
"u",
|
|
24861
|
+
"ins",
|
|
24862
|
+
// underline
|
|
24863
|
+
"s",
|
|
24864
|
+
"del",
|
|
24865
|
+
"strike",
|
|
24866
|
+
// strikethrough
|
|
24867
|
+
"code",
|
|
24868
|
+
"pre",
|
|
24869
|
+
// code
|
|
24870
|
+
"a",
|
|
24871
|
+
// link
|
|
24872
|
+
"span",
|
|
24873
|
+
// used for <span class="tg-spoiler">
|
|
24874
|
+
"tg-spoiler",
|
|
24875
|
+
"tg-emoji",
|
|
24876
|
+
// Telegram custom tags
|
|
24877
|
+
"blockquote",
|
|
24878
|
+
// block quote
|
|
24879
|
+
"expandable-blockquote"
|
|
24880
|
+
// expandable block quote
|
|
24881
|
+
]);
|
|
24826
24882
|
}
|
|
24827
24883
|
});
|
|
24828
24884
|
|
|
@@ -24832,22 +24888,24 @@ function tripCircuitBreaker(retrySec) {
|
|
|
24832
24888
|
const until = Date.now() + retrySec * 1e3;
|
|
24833
24889
|
if (until > circuitBreakerUntil) {
|
|
24834
24890
|
circuitBreakerUntil = until;
|
|
24835
|
-
warn(`[telegram] Circuit breaker tripped \u2014
|
|
24891
|
+
warn(`[telegram] Circuit breaker tripped \u2014 blocking ALL Telegram API calls for ${retrySec}s`);
|
|
24836
24892
|
}
|
|
24837
24893
|
}
|
|
24838
|
-
|
|
24839
|
-
|
|
24840
|
-
|
|
24841
|
-
|
|
24842
|
-
|
|
24843
|
-
return false;
|
|
24894
|
+
function isCircuitBreakerActive() {
|
|
24895
|
+
return Date.now() < circuitBreakerUntil;
|
|
24896
|
+
}
|
|
24897
|
+
function isRateLimitError(err) {
|
|
24898
|
+
return err instanceof GrammyError && err.error_code === 429;
|
|
24844
24899
|
}
|
|
24845
24900
|
async function withRetry(label2, fn) {
|
|
24846
24901
|
for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
|
|
24847
|
-
|
|
24848
|
-
|
|
24849
|
-
|
|
24850
|
-
|
|
24902
|
+
if (isCircuitBreakerActive()) {
|
|
24903
|
+
throw new GrammyError(
|
|
24904
|
+
`Circuit breaker active \u2014 skipping ${label2}`,
|
|
24905
|
+
{ ok: false, error_code: 429, description: "Rate limited (circuit breaker)" },
|
|
24906
|
+
label2,
|
|
24907
|
+
{}
|
|
24908
|
+
);
|
|
24851
24909
|
}
|
|
24852
24910
|
try {
|
|
24853
24911
|
return await fn();
|
|
@@ -24883,6 +24941,9 @@ function isFastPathMessage(msg) {
|
|
|
24883
24941
|
}
|
|
24884
24942
|
return false;
|
|
24885
24943
|
}
|
|
24944
|
+
function sanitizeForTelegram(text) {
|
|
24945
|
+
return text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFFFD\uFFFE\uFFFF]/g, "");
|
|
24946
|
+
}
|
|
24886
24947
|
function numericChatId(chatId) {
|
|
24887
24948
|
if (chatId.startsWith("sq:") || chatId.startsWith("cron:")) {
|
|
24888
24949
|
throw new Error(`Synthetic chatId "${chatId}" passed to Telegram API`);
|
|
@@ -24897,9 +24958,9 @@ var init_telegram2 = __esm({
|
|
|
24897
24958
|
init_telegram();
|
|
24898
24959
|
init_log();
|
|
24899
24960
|
init_store5();
|
|
24900
|
-
MAX_RETRIES2 =
|
|
24961
|
+
MAX_RETRIES2 = 2;
|
|
24901
24962
|
FALLBACK_RETRY_SEC = 3;
|
|
24902
|
-
GIVE_UP_THRESHOLD_SEC =
|
|
24963
|
+
GIVE_UP_THRESHOLD_SEC = 30;
|
|
24903
24964
|
circuitBreakerUntil = 0;
|
|
24904
24965
|
FAST_PATH_COMMANDS = /* @__PURE__ */ new Set(["stop", "status", "new", "newchat"]);
|
|
24905
24966
|
TelegramChannel = class {
|
|
@@ -25107,14 +25168,17 @@ var init_telegram2 = __esm({
|
|
|
25107
25168
|
await this.bot.stop();
|
|
25108
25169
|
}
|
|
25109
25170
|
async sendTyping(chatId) {
|
|
25110
|
-
|
|
25171
|
+
try {
|
|
25172
|
+
await this.bot.api.sendChatAction(numericChatId(chatId), "typing");
|
|
25173
|
+
} catch {
|
|
25174
|
+
}
|
|
25111
25175
|
}
|
|
25112
25176
|
async sendText(chatId, text, opts) {
|
|
25113
25177
|
const { parseMode, threadId, replyToMessageId } = opts ?? {};
|
|
25114
25178
|
const threadOpts = threadId ? { message_thread_id: threadId } : {};
|
|
25115
25179
|
const replyOpts = replyToMessageId ? { reply_parameters: { message_id: replyToMessageId } } : {};
|
|
25116
25180
|
if (parseMode === "plain") {
|
|
25117
|
-
const plainChunks = splitMessage(text);
|
|
25181
|
+
const plainChunks = splitMessage(sanitizeForTelegram(text));
|
|
25118
25182
|
for (const chunk of plainChunks) {
|
|
25119
25183
|
const sent = await withRetry(
|
|
25120
25184
|
"sendText:plain",
|
|
@@ -25124,7 +25188,7 @@ var init_telegram2 = __esm({
|
|
|
25124
25188
|
}
|
|
25125
25189
|
return;
|
|
25126
25190
|
}
|
|
25127
|
-
const formatted = parseMode === "html" ? text : formatForTelegram(text);
|
|
25191
|
+
const formatted = sanitizeForTelegram(parseMode === "html" ? text : formatForTelegram(text));
|
|
25128
25192
|
const chunks = splitMessage(formatted);
|
|
25129
25193
|
for (const chunk of chunks) {
|
|
25130
25194
|
try {
|
|
@@ -25138,6 +25202,7 @@ var init_telegram2 = __esm({
|
|
|
25138
25202
|
);
|
|
25139
25203
|
this.trackAgentMessage(sent.message_id, chatId);
|
|
25140
25204
|
} catch (err) {
|
|
25205
|
+
if (isRateLimitError(err)) throw err;
|
|
25141
25206
|
warn("[telegram] sendText HTML failed, falling back to plain:", err instanceof Error ? err.message : err);
|
|
25142
25207
|
const sent = await withRetry(
|
|
25143
25208
|
"sendText:fallback",
|
|
@@ -25177,7 +25242,7 @@ var init_telegram2 = __esm({
|
|
|
25177
25242
|
}
|
|
25178
25243
|
async sendTextReturningId(chatId, text, parseMode) {
|
|
25179
25244
|
try {
|
|
25180
|
-
const formatted = parseMode === "html" ? text : parseMode === "plain" ? text : formatForTelegram(text);
|
|
25245
|
+
const formatted = sanitizeForTelegram(parseMode === "html" ? text : parseMode === "plain" ? text : formatForTelegram(text));
|
|
25181
25246
|
const opts = parseMode === "plain" ? {} : { parse_mode: "HTML" };
|
|
25182
25247
|
const msg = await withRetry(
|
|
25183
25248
|
"sendTextReturningId",
|
|
@@ -25190,7 +25255,7 @@ var init_telegram2 = __esm({
|
|
|
25190
25255
|
}
|
|
25191
25256
|
}
|
|
25192
25257
|
async editText(chatId, messageId, text, parseMode) {
|
|
25193
|
-
const formatted = parseMode === "html" ? text : formatForTelegram(text);
|
|
25258
|
+
const formatted = sanitizeForTelegram(parseMode === "html" ? text : formatForTelegram(text));
|
|
25194
25259
|
try {
|
|
25195
25260
|
await withRetry(
|
|
25196
25261
|
"editText:html",
|
|
@@ -25200,6 +25265,7 @@ var init_telegram2 = __esm({
|
|
|
25200
25265
|
);
|
|
25201
25266
|
return true;
|
|
25202
25267
|
} catch (err) {
|
|
25268
|
+
if (isRateLimitError(err)) return false;
|
|
25203
25269
|
warn("[telegram] editText HTML failed, trying plain fallback:", err instanceof Error ? err.message : err);
|
|
25204
25270
|
try {
|
|
25205
25271
|
await withRetry(
|
|
@@ -25238,7 +25304,7 @@ var init_telegram2 = __esm({
|
|
|
25238
25304
|
}
|
|
25239
25305
|
const MAX_KEYBOARD_TEXT = 4e3;
|
|
25240
25306
|
const safeText = text.length > MAX_KEYBOARD_TEXT ? text.slice(0, MAX_KEYBOARD_TEXT) + "\n\n\u2026(truncated)" : text;
|
|
25241
|
-
const formatted = formatForTelegram(safeText);
|
|
25307
|
+
const formatted = sanitizeForTelegram(formatForTelegram(safeText));
|
|
25242
25308
|
try {
|
|
25243
25309
|
const msg = await withRetry(
|
|
25244
25310
|
"sendKeyboard",
|
|
@@ -25249,6 +25315,10 @@ var init_telegram2 = __esm({
|
|
|
25249
25315
|
);
|
|
25250
25316
|
return msg.message_id.toString();
|
|
25251
25317
|
} catch (err) {
|
|
25318
|
+
if (isRateLimitError(err)) {
|
|
25319
|
+
warn(`[telegram] sendKeyboard rate-limited, skipping all fallbacks`);
|
|
25320
|
+
return void 0;
|
|
25321
|
+
}
|
|
25252
25322
|
error(`[telegram] sendKeyboard failed (chat=${chatId}, textLen=${text.length}):`, err);
|
|
25253
25323
|
try {
|
|
25254
25324
|
const escaped = safeText.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -25260,6 +25330,7 @@ var init_telegram2 = __esm({
|
|
|
25260
25330
|
);
|
|
25261
25331
|
return retryMsg.message_id.toString();
|
|
25262
25332
|
} catch (plainErr) {
|
|
25333
|
+
if (isRateLimitError(plainErr)) return void 0;
|
|
25263
25334
|
error(`[telegram] sendKeyboard plain retry also failed:`, plainErr);
|
|
25264
25335
|
}
|
|
25265
25336
|
try {
|
|
@@ -27405,19 +27476,20 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
27405
27476
|
else if (/spawn timeout|timeout after \d+s/i.test(line)) spawnTimeout++;
|
|
27406
27477
|
else other++;
|
|
27407
27478
|
}
|
|
27479
|
+
const logFix = "cc-claw doctor --fix to clear stale errors";
|
|
27408
27480
|
if (rate429 > 10) {
|
|
27409
|
-
checks.push({ name: "Telegram rate limits", status: "error", message: `${rate429} rate-limit (429) errors in last 24h \u2014 message delivery blocked`, fix:
|
|
27481
|
+
checks.push({ name: "Telegram rate limits", status: "error", message: `${rate429} rate-limit (429) errors in last 24h \u2014 message delivery blocked`, fix: logFix });
|
|
27410
27482
|
} else if (rate429 > 0) {
|
|
27411
|
-
checks.push({ name: "Telegram rate limits", status: "warning", message: `${rate429} rate-limit (429) errors in last 24h`, fix:
|
|
27483
|
+
checks.push({ name: "Telegram rate limits", status: "warning", message: `${rate429} rate-limit (429) errors in last 24h`, fix: logFix });
|
|
27412
27484
|
}
|
|
27413
27485
|
if (contentSilence > 0) {
|
|
27414
|
-
checks.push({ name: "Content silence", status: "warning", message: `${contentSilence} agent silence timeout(s) in last 24h \u2014 API went unresponsive`, fix:
|
|
27486
|
+
checks.push({ name: "Content silence", status: "warning", message: `${contentSilence} agent silence timeout(s) in last 24h \u2014 API went unresponsive`, fix: logFix });
|
|
27415
27487
|
}
|
|
27416
27488
|
if (spawnTimeout > 0) {
|
|
27417
|
-
checks.push({ name: "Spawn timeouts", status: "warning", message: `${spawnTimeout} backend timeout(s) in last 24h`, fix:
|
|
27489
|
+
checks.push({ name: "Spawn timeouts", status: "warning", message: `${spawnTimeout} backend timeout(s) in last 24h`, fix: logFix });
|
|
27418
27490
|
}
|
|
27419
27491
|
if (other > 0) {
|
|
27420
|
-
checks.push({ name: "Other errors", status: "warning", message: `${other} other error(s) in last 24h`, fix:
|
|
27492
|
+
checks.push({ name: "Other errors", status: "warning", message: `${other} other error(s) in last 24h`, fix: logFix });
|
|
27421
27493
|
}
|
|
27422
27494
|
if (rate429 === 0 && contentSilence === 0 && spawnTimeout === 0 && other === 0) {
|
|
27423
27495
|
checks.push({ name: "Recent errors", status: "ok", message: "none in last 24h" });
|
|
@@ -27508,6 +27580,21 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
27508
27580
|
}
|
|
27509
27581
|
}
|
|
27510
27582
|
}
|
|
27583
|
+
const errorChecks = checks.filter(
|
|
27584
|
+
(c) => ["Telegram rate limits", "Content silence", "Spawn timeouts", "Other errors"].includes(c.name) && c.status !== "ok"
|
|
27585
|
+
);
|
|
27586
|
+
if (errorChecks.length > 0 && existsSync30(ERROR_LOG_PATH)) {
|
|
27587
|
+
try {
|
|
27588
|
+
const { writeFileSync: writeFileSync13 } = await import("fs");
|
|
27589
|
+
writeFileSync13(ERROR_LOG_PATH, "");
|
|
27590
|
+
for (const c of errorChecks) {
|
|
27591
|
+
c.status = "ok";
|
|
27592
|
+
c.message = "cleared (log truncated)";
|
|
27593
|
+
delete c.fix;
|
|
27594
|
+
}
|
|
27595
|
+
} catch {
|
|
27596
|
+
}
|
|
27597
|
+
}
|
|
27511
27598
|
}
|
|
27512
27599
|
const errors = checks.filter((c) => c.status === "error").length;
|
|
27513
27600
|
const warnings = checks.filter((c) => c.status === "warning").length;
|
|
@@ -27533,8 +27620,9 @@ async function doctorCommand(globalOpts, localOpts) {
|
|
|
27533
27620
|
lines.push(` ${parts.join(", ")}`);
|
|
27534
27621
|
const fixable = r.checks.filter((c) => c.fix);
|
|
27535
27622
|
if (fixable.length > 0 && !localOpts.fix) {
|
|
27536
|
-
|
|
27537
|
-
|
|
27623
|
+
const uniqueFixes = [...new Set(fixable.map((f) => f.fix))];
|
|
27624
|
+
for (const fix of uniqueFixes) {
|
|
27625
|
+
lines.push(muted(` Fix: ${fix}`));
|
|
27538
27626
|
}
|
|
27539
27627
|
}
|
|
27540
27628
|
}
|
package/package.json
CHANGED