chore(lint): enable more readability rules

This commit is contained in:
Peter Steinberger
2026-05-31 07:38:24 +01:00
parent 9c3cf35e08
commit 4eba3e5d7d
48 changed files with 152 additions and 200 deletions

View File

@@ -32,11 +32,13 @@
"eslint/no-useless-computed-key": "error",
"eslint/no-useless-concat": "error",
"eslint/no-useless-constructor": "error",
"eslint/no-useless-rename": "error",
"eslint/no-unused-vars": "off",
"eslint/no-warning-comments": "error",
"eslint/no-unmodified-loop-condition": "error",
"eslint/no-new-wrappers": "error",
"eslint/no-else-return": "error",
"eslint/no-lonely-if": "error",
"eslint/no-case-declarations": "error",
"eslint/default-case-last": "error",
"eslint/default-param-last": "error",
@@ -89,6 +91,7 @@
"typescript/prefer-namespace-keyword": "error",
"typescript/prefer-return-this-type": "error",
"typescript/prefer-find": "error",
"typescript/prefer-for-of": "error",
"typescript/prefer-function-type": "error",
"typescript/prefer-includes": "error",
"typescript/prefer-reduce-type-parameter": "error",
@@ -118,6 +121,7 @@
"unicorn/prefer-date-now": "error",
"unicorn/prefer-dom-node-text-content": "error",
"unicorn/prefer-keyboard-event-key": "error",
"unicorn/prefer-array-flat": "error",
"unicorn/prefer-array-some": "error",
"unicorn/prefer-math-min-max": "error",
"unicorn/prefer-node-protocol": "error",

View File

@@ -373,8 +373,7 @@ function splitExecLine(line: string): string[] {
let current = "";
let inQuotes = false;
let quoteChar = "";
for (let i = 0; i < line.length; i += 1) {
const ch = line[i];
for (const ch of line) {
if ((ch === '"' || ch === "'") && (!inQuotes || ch === quoteChar)) {
if (inQuotes) {
inQuotes = false;

View File

@@ -2051,10 +2051,8 @@ export class MatrixClient {
this.emitter.emit("room.event", roomId, raw);
if (isEncryptedEvent) {
this.emitter.emit("room.encrypted_event", roomId, raw);
} else {
if (decryptBridge.shouldEmitUnencryptedMessage(roomId, raw.event_id)) {
this.emitter.emit("room.message", roomId, raw);
}
} else if (decryptBridge.shouldEmitUnencryptedMessage(roomId, raw.event_id)) {
this.emitter.emit("room.message", roomId, raw);
}
const stateKey = raw.state_key ?? "";

View File

@@ -231,7 +231,7 @@ const LANGUAGE_STOP_WORDS = {
const CONCEPT_STOP_WORDS = new Set(
Object.values(LANGUAGE_STOP_WORDS)
.flatMap((words) => words)
.flat()
.map((word) => normalizeLowercaseStringOrEmpty(word)),
);

View File

@@ -255,28 +255,26 @@ export async function handleNextcloudTalkInbound(params: {
runtime.log?.(`nextcloud-talk: drop group sender ${senderId} (reason=${accessReason})`);
return;
}
} else {
if (access.senderAccess.decision !== "allow") {
if (access.senderAccess.decision === "pairing") {
await pairing.issueChallenge({
senderId,
senderIdLine: `Your Nextcloud user id: ${senderId}`,
meta: { name: senderName || undefined },
sendPairingReply: async (text) => {
await sendMessageNextcloudTalk(roomToken, text, {
cfg: config,
accountId: account.accountId,
});
statusSink?.({ lastOutboundAt: Date.now() });
},
onReplyError: (err) => {
runtime.error?.(`nextcloud-talk: pairing reply failed for ${senderId}: ${String(err)}`);
},
});
}
runtime.log?.(`nextcloud-talk: drop DM sender ${senderId} (reason=${accessReason})`);
return;
} else if (access.senderAccess.decision !== "allow") {
if (access.senderAccess.decision === "pairing") {
await pairing.issueChallenge({
senderId,
senderIdLine: `Your Nextcloud user id: ${senderId}`,
meta: { name: senderName || undefined },
sendPairingReply: async (text) => {
await sendMessageNextcloudTalk(roomToken, text, {
cfg: config,
accountId: account.accountId,
});
statusSink?.({ lastOutboundAt: Date.now() });
},
onReplyError: (err) => {
runtime.error?.(`nextcloud-talk: pairing reply failed for ${senderId}: ${String(err)}`);
},
});
}
runtime.log?.(`nextcloud-talk: drop DM sender ${senderId} (reason=${accessReason})`);
return;
}
if (access.commandAccess.shouldBlockControlCommand) {

View File

@@ -312,13 +312,11 @@ export async function waitForFile(
stableCount = 0;
}
lastSize = stat.size;
} else {
if (Date.now() - fileAppearedAt > emptyGiveUpMs) {
debugError(
`[audio-convert] waitForFile: file still empty after ${emptyGiveUpMs}ms, giving up: ${path.basename(filePath)}`,
);
return 0;
}
} else if (Date.now() - fileAppearedAt > emptyGiveUpMs) {
debugError(
`[audio-convert] waitForFile: file still empty after ${emptyGiveUpMs}ms, giving up: ${path.basename(filePath)}`,
);
return 0;
}
} catch {
if (!fileExists && Date.now() - start > noFileGiveUpMs) {

View File

@@ -107,17 +107,13 @@ function expectRpcCall(params: {
expect(method).toBe(params.method);
if (params.rpcParams) {
expectFields(requireRecord(rpcParams, "rpc params"), params.rpcParams);
} else {
if (rpcParams === undefined) {
throw new Error("expected rpc params argument");
}
} else if (rpcParams === undefined) {
throw new Error("expected rpc params argument");
}
if (params.options) {
expectFields(requireRecord(options, "rpc options"), params.options);
} else {
if (options === undefined) {
throw new Error("expected rpc options argument");
}
} else if (options === undefined) {
throw new Error("expected rpc options argument");
}
}

View File

@@ -68,7 +68,7 @@ class GroupFairQueue {
}
private takeNext(): QueuedApiRequest<unknown> | undefined {
for (let scanned = 0; scanned < this.laneOrder.length; scanned += 1) {
for (const ignoredLaneKey of this.laneOrder) {
this.nextLaneIndex %= this.laneOrder.length;
const laneKey = this.laneOrder[this.nextLaneIndex];
const queue = this.lanes.get(laneKey);

View File

@@ -294,8 +294,7 @@ async function sendTelegramVoiceFallbackText(opts: {
let firstDeliveredMessageId: number | undefined;
const chunks = filterEmptyTelegramTextChunks(opts.chunkText(opts.text));
let appliedReplyTo = false;
for (let i = 0; i < chunks.length; i += 1) {
const chunk = chunks[i];
for (const chunk of chunks) {
// Only apply reply reference, quote text, and buttons to the first chunk.
const replyToForChunk = !appliedReplyTo ? opts.replyToId : undefined;
const applyQuoteForChunk = !appliedReplyTo;

View File

@@ -216,8 +216,7 @@ function shouldBypassEnvProxyForTelegramApi(env: NodeJS.ProcessEnv = process.env
const targetHostname = normalizeLowercaseStringOrEmpty(TELEGRAM_API_HOSTNAME);
const targetPort = 443;
const noProxyEntries = noProxyValue.split(/[,\s]/);
for (let i = 0; i < noProxyEntries.length; i++) {
const entry = noProxyEntries[i];
for (const entry of noProxyEntries) {
if (!entry) {
continue;
}

View File

@@ -149,8 +149,8 @@ export function calculateMulawRms(muLaw: Buffer): number {
return 0;
}
let sum = 0;
for (let i = 0; i < muLaw.length; i += 1) {
const normalized = (MULAW_LINEAR_SAMPLES[muLaw[i] ?? 0] ?? 0) / PCM16_MAX_AMPLITUDE;
for (const sample of muLaw) {
const normalized = (MULAW_LINEAR_SAMPLES[sample] ?? 0) / PCM16_MAX_AMPLITUDE;
sum += normalized * normalized;
}
return Math.sqrt(sum / muLaw.length);

View File

@@ -287,14 +287,12 @@ export function createWebOnMessageHandler(params: {
if (!gating.shouldProcess) {
return;
}
} else {
} else if (!msg.sender?.e164 && !msg.senderE164 && peerId && peerId.startsWith("+")) {
// Ensure `peerId` for DMs is stable and stored as E.164 when possible.
if (!msg.sender?.e164 && !msg.senderE164 && peerId && peerId.startsWith("+")) {
const normalized = normalizeE164(peerId);
if (normalized) {
msg.sender = { ...msg.sender, e164: normalized };
msg.senderE164 = normalized;
}
const normalized = normalizeE164(peerId);
if (normalized) {
msg.sender = { ...msg.sender, e164: normalized };
msg.senderE164 = normalized;
}
}

View File

@@ -401,9 +401,9 @@ export function findCutPoint(
const messageTokens = estimateTokens(entry.message);
accumulatedTokens += messageTokens;
if (accumulatedTokens >= keepRecentTokens) {
for (let c = 0; c < cutPoints.length; c++) {
if (cutPoints[c] >= i) {
cutIndex = cutPoints[c];
for (const cutPoint of cutPoints) {
if (cutPoint >= i) {
cutIndex = cutPoint;
break;
}
}

View File

@@ -4,8 +4,7 @@ export function parseCommandArgs(argsString: string): string[] {
let current = "";
let inQuote: string | null = null;
for (let i = 0; i < argsString.length; i++) {
const char = argsString[i];
for (const char of argsString) {
if (inQuote) {
if (char === inQuote) {
inQuote = null;

View File

@@ -67,8 +67,7 @@ function walkStaticImportGraph(params) {
const visited = new Set();
const errors = [];
for (let index = 0; index < queue.length; index += 1) {
const filePath = queue[index];
for (const filePath of queue) {
if (!filePath || visited.has(filePath)) {
continue;
}

View File

@@ -101,8 +101,7 @@ function splitTopLevelArguments(source: string): string[] {
let bracketDepth = 0;
let braceDepth = 0;
const quoteState: QuoteScanState = { quote: null, escaped: false };
for (let i = 0; i < source.length; i += 1) {
const ch = source[i];
for (const ch of source) {
if (quoteState.quote) {
current += ch;
consumeQuotedChar(quoteState, ch);

View File

@@ -85,8 +85,8 @@ function descendantsOf(rootPid, stats) {
}
const seen = new Set([rootPid]);
const queue = [rootPid];
for (let index = 0; index < queue.length; index += 1) {
for (const child of children.get(queue[index]) ?? []) {
for (const queuedPid of queue) {
for (const child of children.get(queuedPid) ?? []) {
if (!seen.has(child)) {
seen.add(child);
queue.push(child);

View File

@@ -157,8 +157,7 @@ export function expandPackageDistImportClosure(params) {
}
const queue = [...expectedSet].filter((file) => fileSet.has(file));
for (let index = 0; index < queue.length; index += 1) {
const importerPath = queue[index];
for (const importerPath of queue) {
for (const importedPath of importsByImporter.get(importerPath) ?? []) {
if (fileSet.has(importedPath) && !expectedSet.has(importedPath)) {
expectedSet.add(importedPath);

View File

@@ -391,8 +391,7 @@ function hasExplicitDisabledRunFlag(argv) {
}
function hasSeparateVitestOptionValueArg(argv) {
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
for (const arg of argv) {
if (arg === "--") {
return false;
}

View File

@@ -1338,8 +1338,7 @@ function resolveAffectedTestsFromTargetedImportScan(changedPath, cwd) {
const seen = new Set(queue);
const targets = [];
for (let index = 0; index < queue.length; index += 1) {
const current = queue[index];
for (const current of queue) {
const importers = findDirectImportersWithGitGrep(cwd, current, fileSet);
if (importers === null) {
return null;
@@ -1409,8 +1408,7 @@ function resolveAffectedTestsFromImportGraph(changedPath, cwd, options = {}) {
const seen = new Set(queue);
const targets = [];
for (let index = 0; index < queue.length; index += 1) {
const current = queue[index];
for (const current of queue) {
for (const importer of reverseImports.get(current) ?? []) {
if (seen.has(importer)) {
continue;

View File

@@ -422,8 +422,7 @@ function extractScriptTargetFromCommand(
}
};
for (let i = 0; i < value.length; i += 1) {
const ch = value[i];
for (const ch of value) {
if (inSingle) {
if (ch === "'") {
inSingle = false;
@@ -669,8 +668,7 @@ function hasUnquotedScriptHint(raw: string): boolean {
return false;
};
for (let i = 0; i < raw.length; i += 1) {
const ch = raw[i];
for (const ch of raw) {
if (escaped) {
if (!inSingle && !inDouble) {
token += ch;

View File

@@ -286,8 +286,7 @@ export async function writeCliImages(params: {
await fs.mkdir(imageRoot, { recursive: true, mode: 0o700 });
const store = privateFileStore(imageRoot);
const paths: string[] = [];
for (let i = 0; i < params.images.length; i += 1) {
const image = params.images[i];
for (const image of params.images) {
const fileName = path.basename(resolveCliImagePath(image));
const buffer = Buffer.from(image.data, "base64");
await store.writeText(fileName, buffer);

View File

@@ -2221,11 +2221,11 @@ function isJsonValue(value: unknown): value is JsonValue {
continue;
}
if (Array.isArray(current.value)) {
for (let index = 0; index < current.value.length; index += 1) {
for (const value of current.value) {
if (nodes + stack.length + 1 > MAX_NATIVE_HOOK_RELAY_JSON_NODES) {
return false;
}
stack.push({ value: current.value[index], depth: current.depth + 1 });
stack.push({ value, depth: current.depth + 1 });
}
continue;
}

View File

@@ -196,8 +196,7 @@ export function buildSubagentRunReadIndexFromRuns(params: {
let count = 0;
const pending = [root];
const visited = new Set<string>([root]);
for (let index = 0; index < pending.length; index += 1) {
const requester = pending[index];
for (const requester of pending) {
if (!requester) {
continue;
}
@@ -377,8 +376,7 @@ function forEachDescendantRun(
}
const pending = [root];
const visited = new Set<string>([root]);
for (let index = 0; index < pending.length; index += 1) {
const requester = pending[index];
for (const requester of pending) {
if (!requester) {
continue;
}

View File

@@ -30,9 +30,7 @@ export function splitShellWords(input: string | undefined, maxWords = 48): strin
let quote: '"' | "'" | undefined;
let escaped = false;
for (let i = 0; i < input.length; i += 1) {
const char = input[i];
for (const char of input) {
if (escaped) {
current += char;
escaped = false;

View File

@@ -589,10 +589,8 @@ export function registerLogsCli(program: Command) {
if (!emitJsonLine({ type: "log", ...parsed })) {
return;
}
} else {
if (!emitJsonLine({ type: "raw", raw: line })) {
return;
}
} else if (!emitJsonLine({ type: "raw", raw: line })) {
return;
}
}
if (payload.truncated) {

View File

@@ -218,8 +218,8 @@ function countJsonlLines(filePath: string): number {
return 0;
}
let count = 0;
for (let i = 0; i < raw.length; i += 1) {
if (raw[i] === "\n") {
for (const char of raw) {
if (char === "\n") {
count += 1;
}
}

View File

@@ -454,7 +454,7 @@ export async function getHealthSnapshot(params?: {
boundAccounts,
});
const boundAccountIdsAll = Array.from(
new Set(Array.from(channelBindings.get(plugin.id)?.values() ?? []).flatMap((ids) => ids)),
new Set(Array.from(channelBindings.get(plugin.id)?.values() ?? []).flat()),
);
const accountIdsToProbe = Array.from(
new Set(

View File

@@ -86,8 +86,7 @@ function parseFlagName(arg: string): string | null {
export function redactConfigAuditArgv(argv: readonly string[]): string[] {
const result: string[] = [];
let redactNext = false;
for (let i = 0; i < argv.length; i++) {
const current = argv[i];
for (const current of argv) {
if (typeof current !== "string") {
result.push(current);
redactNext = false;

View File

@@ -91,18 +91,16 @@ function coerceSchedule(schedule: UnknownRecord) {
if (kind) {
next.kind = kind;
} else {
if (
typeof schedule.atMs === "number" ||
typeof schedule.at === "string" ||
typeof schedule.atMs === "string"
) {
next.kind = "at";
} else if (everyMs !== undefined) {
next.kind = "every";
} else if (normalizedExpr) {
next.kind = "cron";
}
} else if (
typeof schedule.atMs === "number" ||
typeof schedule.at === "string" ||
typeof schedule.atMs === "string"
) {
next.kind = "at";
} else if (everyMs !== undefined) {
next.kind = "every";
} else if (normalizedExpr) {
next.kind = "cron";
}
const parsedAtIso = parsedAtMs !== null ? timestampMsToIsoString(parsedAtMs) : undefined;

View File

@@ -1,6 +1,6 @@
import { normalizeOptionalString } from "../../packages/normalization-core/src/string-coerce.js";
import type { FailoverReason } from "../agents/embedded-agent-helpers/types.js";
import { resolveFailoverReasonFromError } from "../agents/failover-error.js";
import { normalizeOptionalString } from "../../packages/normalization-core/src/string-coerce.js";
import { normalizeCronRunDiagnostics } from "./run-diagnostics.js";
import type { CronRunLogEntry } from "./run-log-types.js";
import type { CronDeliveryStatus } from "./types.js";
@@ -38,8 +38,8 @@ export function parseCronRunLogEntriesFromJsonl(
}
const parsed: CronRunLogEntry[] = [];
const lines = raw.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i]?.trim();
for (const rawLine of lines) {
const line = rawLine.trim();
if (!line) {
continue;
}

View File

@@ -784,8 +784,7 @@ function mirrorMessageToolVisibleReplies(messages: unknown[]): unknown[] {
clearPending();
};
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
for (const message of messages) {
const record = readRecord(message);
if (!record) {
next.push(message);

View File

@@ -164,8 +164,7 @@ function estimateBase64Size(value: string | undefined): number | undefined {
}
let encodedLength = 0;
let padding = 0;
for (let index = 0; index < value.length; index += 1) {
const char = value[index];
for (const char of value) {
if (!char || isBase64Whitespace(char)) {
continue;
}

View File

@@ -262,9 +262,7 @@ function appendDecodedText(
sourceEndOffset: number,
): void {
decoded.value += value;
for (let index = 0; index < value.length; index += 1) {
decoded.sourceOffsets.push(sourceEndOffset);
}
decoded.sourceOffsets.push(...Array.from({ length: value.length }, () => sourceEndOffset));
}
function identityDecodedShellText(text: string, sourceOffset = 0): DecodedShellText {

View File

@@ -225,40 +225,38 @@ function splitShellPipeline(command: string): { ok: boolean; reason?: string; se
if (line === current.delimiter) {
pendingHeredocs.shift();
}
} else {
} else if (line === current.delimiter && unquotedHeredocLogicalChunks.length === 0) {
// An unquoted heredoc body whose previous physical line ended with
// `\<newline>` is spliced into the next line at runtime. In that
// case bash does not treat the next physical line as the delimiter,
// even if it matches literally — the splice wins and the body
// continues. Only recognize the delimiter when no continuation is
// pending.
if (line === current.delimiter && unquotedHeredocLogicalChunks.length === 0) {
pendingHeredocs.shift();
} else {
const continued = stripUnquotedHeredocLineContinuation(line);
unquotedHeredocLogicalChunks.push(continued.line);
if (unquotedHeredocLogicalChunks.length > MAX_UNQUOTED_HEREDOC_CONTINUATION_LINES) {
return {
ok: false,
reason: "heredoc continuation too long",
segments: [],
};
}
unquotedHeredocLogicalLength += continued.line.length;
if (unquotedHeredocLogicalLength > MAX_UNQUOTED_HEREDOC_LOGICAL_LINE_LENGTH) {
return {
ok: false,
reason: "heredoc logical line too large",
segments: [],
};
}
if (!continued.continues) {
if (hasUnquotedHeredocExpansionToken(unquotedHeredocLogicalChunks.join(""))) {
return { ok: false, reason: "shell expansion in unquoted heredoc", segments: [] };
}
unquotedHeredocLogicalChunks = [];
unquotedHeredocLogicalLength = 0;
pendingHeredocs.shift();
} else {
const continued = stripUnquotedHeredocLineContinuation(line);
unquotedHeredocLogicalChunks.push(continued.line);
if (unquotedHeredocLogicalChunks.length > MAX_UNQUOTED_HEREDOC_CONTINUATION_LINES) {
return {
ok: false,
reason: "heredoc continuation too long",
segments: [],
};
}
unquotedHeredocLogicalLength += continued.line.length;
if (unquotedHeredocLogicalLength > MAX_UNQUOTED_HEREDOC_LOGICAL_LINE_LENGTH) {
return {
ok: false,
reason: "heredoc logical line too large",
segments: [],
};
}
if (!continued.continues) {
if (hasUnquotedHeredocExpansionToken(unquotedHeredocLogicalChunks.join(""))) {
return { ok: false, reason: "shell expansion in unquoted heredoc", segments: [] };
}
unquotedHeredocLogicalChunks = [];
unquotedHeredocLogicalLength = 0;
}
}
}

View File

@@ -187,9 +187,7 @@ export function transformMessages<TApi extends Api>(
}
};
for (let i = 0; i < transformed.length; i++) {
const msg = transformed[i];
for (const msg of transformed) {
if (msg.role === "assistant") {
// If we have pending orphaned tool calls from a previous assistant, insert synthetic results now
insertSyntheticToolResults();

View File

@@ -19,8 +19,8 @@ const CRC_TABLE = (() => {
/** Compute CRC32 checksum for a buffer (used in PNG chunk encoding). */
function crc32(buf: Buffer): number {
let crc = 0xffffffff;
for (let i = 0; i < buf.length; i += 1) {
crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
for (const byte of buf) {
crc = CRC_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);
}
return (crc ^ 0xffffffff) >>> 0;
}

View File

@@ -1277,10 +1277,8 @@ export function describeTtsAutoApplyContract() {
expect(fetchMock).toHaveBeenCalledTimes(params.expectedFetchCalls);
if (params.expectSamePayload) {
expect(result).toBe(params.payload);
} else {
if (typeof result.mediaUrl !== "string" || result.mediaUrl.length === 0) {
throw new Error("expected auto TTS to attach mediaUrl");
}
} else if (typeof result.mediaUrl !== "string" || result.mediaUrl.length === 0) {
throw new Error("expected auto TTS to attach mediaUrl");
}
});
}

View File

@@ -59,5 +59,5 @@ export function getChannelEnvVars(channelId: string, params?: ChannelEnvVarLooku
}
export function listKnownChannelEnvVarNames(params?: ChannelEnvVarLookupParams): string[] {
return uniqueStrings(Object.values(resolveChannelEnvVars(params)).flatMap((keys) => keys));
return uniqueStrings(Object.values(resolveChannelEnvVars(params)).flat());
}

View File

@@ -438,13 +438,13 @@ export function getProviderEnvVars(
// remain available to child bridge/runtime processes.
export function listKnownProviderAuthEnvVarNames(params?: ProviderEnvVarLookupParams): string[] {
return uniqueStrings([
...Object.values(resolveProviderAuthEnvVarCandidates(params)).flatMap((keys) => keys),
...Object.values(resolveProviderEnvVars(params)).flatMap((keys) => keys),
...Object.values(resolveProviderAuthEnvVarCandidates(params)).flat(),
...Object.values(resolveProviderEnvVars(params)).flat(),
]);
}
export function listKnownSecretEnvVarNames(params?: ProviderEnvVarLookupParams): string[] {
return uniqueStrings(Object.values(resolveProviderEnvVars(params)).flatMap((keys) => keys));
return uniqueStrings(Object.values(resolveProviderEnvVars(params)).flat());
}
export function omitEnvKeysCaseInsensitive(

View File

@@ -291,8 +291,7 @@ function containsDiscoverableSkill(
): boolean {
const discoveryBudget = createSkillDiscoveryBudget(opts.maxCandidateDirs);
const queue: Array<{ dir: string; depth: number }> = [{ dir, depth: 0 }];
for (let index = 0; index < queue.length; index += 1) {
const candidate = queue[index];
for (const candidate of queue) {
if (!candidate) {
continue;
}
@@ -520,8 +519,7 @@ export function resolveNestedSkillsRoot(
// child-directory filter as discovery so ignored folders cannot re-root.
const discoveryBudget = createSkillDiscoveryBudget(scanLimit);
const queue: Array<{ dir: string; depth: number }> = [{ dir: nested, depth: 0 }];
for (let index = 0; index < queue.length; index += 1) {
const candidate = queue[index];
for (const candidate of queue) {
if (!candidate) {
continue;
}
@@ -999,8 +997,7 @@ function loadSkillEntries(
}),
);
for (let queueIndex = 0; queueIndex < scanQueue.length; queueIndex += 1) {
const candidate = scanQueue[queueIndex];
for (const candidate of scanQueue) {
if (!candidate) {
continue;
}

View File

@@ -243,7 +243,7 @@ function addTrustedSymlinkSkillWatchTargets(
let watched = 0;
let directoryScans = 0;
let rawEntries = 0;
for (let queueIndex = 0; queueIndex < queue.length; queueIndex += 1) {
for (const queued of queue) {
if (
watched >= MAX_SYMLINK_WATCH_TARGETS_PER_ROOT ||
directoryScans >= MAX_SYMLINK_WATCH_DIRECTORY_SCANS_PER_ROOT ||
@@ -251,7 +251,7 @@ function addTrustedSymlinkSkillWatchTargets(
) {
break;
}
const current = queue[queueIndex];
const current = queued;
if (!current) {
continue;
}

View File

@@ -350,10 +350,8 @@ export function createEventHandlers(context: EventHandlerContext) {
if (params.wasActiveRun) {
setActivityStatus(params.status);
clearStreamingWatchdog();
} else {
if (streamingWatchdogRunId === params.runId) {
clearStreamingWatchdog();
}
} else if (streamingWatchdogRunId === params.runId) {
clearStreamingWatchdog();
}
void refreshSessionInfo?.();
};

View File

@@ -23,8 +23,10 @@ const ZERO_BASELINE_RULES = [
"eslint/no-self-compare",
"eslint/no-var",
"eslint/no-implicit-coercion",
"eslint/no-useless-rename",
"eslint/no-new-wrappers",
"eslint/no-else-return",
"eslint/no-lonely-if",
"eslint/no-case-declarations",
"eslint/prefer-exponentiation-operator",
"eslint/prefer-numeric-literals",
@@ -43,6 +45,7 @@ const ZERO_BASELINE_RULES = [
"typescript/no-non-null-asserted-nullish-coalescing",
"typescript/no-unnecessary-qualifier",
"typescript/prefer-find",
"typescript/prefer-for-of",
"typescript/prefer-function-type",
"typescript/prefer-includes",
"typescript/prefer-reduce-type-parameter",
@@ -57,6 +60,7 @@ const ZERO_BASELINE_RULES = [
"unicorn/no-typeof-undefined",
"unicorn/no-useless-error-capture-stack-trace",
"unicorn/no-zero-fractions",
"unicorn/prefer-array-flat",
"unicorn/prefer-array-some",
"unicorn/prefer-dom-node-text-content",
"unicorn/prefer-keyboard-event-key",

View File

@@ -2672,17 +2672,15 @@ export function renderApp(state: AppViewState) {
const { basePath, existing } = modelEntry;
if (!modelId) {
removeConfigFormValue(state, basePath);
} else if (existing && typeof existing === "object" && !Array.isArray(existing)) {
const fallbacks = (existing as { fallbacks?: unknown }).fallbacks;
const next = {
primary: modelId,
...(Array.isArray(fallbacks) ? { fallbacks } : {}),
};
updateConfigFormValue(state, basePath, next);
} else {
if (existing && typeof existing === "object" && !Array.isArray(existing)) {
const fallbacks = (existing as { fallbacks?: unknown }).fallbacks;
const next = {
primary: modelId,
...(Array.isArray(fallbacks) ? { fallbacks } : {}),
};
updateConfigFormValue(state, basePath, next);
} else {
updateConfigFormValue(state, basePath, modelId);
}
updateConfigFormValue(state, basePath, modelId);
}
void refreshVisibleToolsEffectiveForCurrentSession(state);
},

View File

@@ -349,13 +349,11 @@ md.linkify.add("www", {
if (c === open) {
balance[close] = balance[close] === 0 ? 1 : 0;
}
} else {
} else if (c === open) {
// Distinct open/close (e.g., ())
if (c === open) {
balance[close]++;
} else if (c === close) {
balance[close]--;
}
balance[close]++;
} else if (c === close) {
balance[close]--;
}
}
}
@@ -394,13 +392,11 @@ md.linkify.add("www", {
len--;
continue;
}
} else {
} else if (balance[ch] < 0) {
// Distinct pair: strip if more closes than opens
if (balance[ch] < 0) {
balance[ch]++;
len--;
continue;
}
balance[ch]++;
len--;
continue;
}
}
break;

View File

@@ -10,8 +10,8 @@ function uuidFromBytes(bytes: Uint8Array): string {
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1
let hex = "";
for (let i = 0; i < bytes.length; i++) {
hex += bytes[i].toString(16).padStart(2, "0");
for (const byte of bytes) {
hex += byte.toString(16).padStart(2, "0");
}
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(

View File

@@ -503,8 +503,7 @@ function handlePaste(e: ClipboardEvent, props: ChatProps) {
return;
}
const imageItems: DataTransferItem[] = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
for (const item of items) {
if (item.type.startsWith("image/")) {
imageItems.push(item);
}