From 90aa3a306c965109305ab13ea9aa09b127af0782 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 18 Mar 2026 14:19:12 +0900 Subject: [PATCH] perf(hooks,tools): optimize string operations and reduce redundant iterations - output-renderer, hashline-edit-diff: replace str += with array join (H2) - auto-slash-command: single-pass Map grouping instead of 6x filter (M1) - comment-checker: hoist Zod schema to module scope (M2) - session-last-agent: reverse iterate sorted array instead of sort+reverse (L2) --- src/cli/run/output-renderer.ts | 10 ++++---- src/hooks/atlas/session-last-agent.ts | 4 ++-- src/hooks/auto-slash-command/executor.ts | 22 ++++++++---------- src/hooks/comment-checker/hook.ts | 23 ++++++++++--------- src/tools/hashline-edit/hashline-edit-diff.ts | 12 +++++----- 5 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/cli/run/output-renderer.ts b/src/cli/run/output-renderer.ts index 1bea3b269..6c5782da4 100644 --- a/src/cli/run/output-renderer.ts +++ b/src/cli/run/output-renderer.ts @@ -45,26 +45,26 @@ export function writePaddedText( return { output: text, atLineStart: text.endsWith("\n") } } - let output = "" + const parts: string[] = [] let lineStart = atLineStart for (let i = 0; i < text.length; i++) { const ch = text[i] if (lineStart) { - output += " " + parts.push(" ") lineStart = false } if (ch === "\n") { - output += " \n" + parts.push(" \n") lineStart = true continue } - output += ch + parts.push(ch) } - return { output, atLineStart: lineStart } + return { output: parts.join(""), atLineStart: lineStart } } function colorizeWithProfileColor(text: string, hexColor?: string): string { diff --git a/src/hooks/atlas/session-last-agent.ts b/src/hooks/atlas/session-last-agent.ts index 5b8de6562..6c6b821db 100644 --- a/src/hooks/atlas/session-last-agent.ts +++ b/src/hooks/atlas/session-last-agent.ts @@ -18,9 +18,9 @@ function getLastAgentFromMessageDir(messageDir: string): string | null { const files = readdirSync(messageDir) .filter((fileName) => fileName.endsWith(".json")) .sort() - .reverse() - for (const fileName of files) { + for (let i = files.length - 1; i >= 0; i--) { + const fileName = files[i] try { const content = readFileSync(join(messageDir, fileName), "utf-8") const parsed = JSON.parse(content) as { agent?: unknown } diff --git a/src/hooks/auto-slash-command/executor.ts b/src/hooks/auto-slash-command/executor.ts index cea80eb2a..579da6d34 100644 --- a/src/hooks/auto-slash-command/executor.ts +++ b/src/hooks/auto-slash-command/executor.ts @@ -44,12 +44,6 @@ export interface ExecutorOptions { agent?: string } -function filterDiscoveredCommandsByScope( - commands: DiscoveredCommandInfo[], - scope: DiscoveredCommandInfo["scope"], -): DiscoveredCommandInfo[] { - return commands.filter(command => command.scope === scope) -} async function discoverAllCommands(options?: ExecutorOptions): Promise { const discoveredCommands = discoverCommandsSync(process.cwd(), { @@ -60,14 +54,18 @@ async function discoverAllCommands(options?: ExecutorOptions): Promise() + for (const cmd of discoveredCommands) { + const list = grouped.get(cmd.scope) ?? [] + list.push(cmd) + grouped.set(cmd.scope, list) + } + const orderedCommands = scopeOrder.flatMap((scope) => grouped.get(scope) ?? []) + return [ ...skillCommands, - ...filterDiscoveredCommandsByScope(discoveredCommands, "project"), - ...filterDiscoveredCommandsByScope(discoveredCommands, "user"), - ...filterDiscoveredCommandsByScope(discoveredCommands, "opencode-project"), - ...filterDiscoveredCommandsByScope(discoveredCommands, "opencode"), - ...filterDiscoveredCommandsByScope(discoveredCommands, "builtin"), - ...filterDiscoveredCommandsByScope(discoveredCommands, "plugin"), + ...orderedCommands, ] } diff --git a/src/hooks/comment-checker/hook.ts b/src/hooks/comment-checker/hook.ts index f72fe9c20..06ecc8bbf 100644 --- a/src/hooks/comment-checker/hook.ts +++ b/src/hooks/comment-checker/hook.ts @@ -3,6 +3,18 @@ import type { CommentCheckerConfig } from "../../config/schema" import z from "zod" +const ApplyPatchMetadataSchema = z.object({ + files: z.array( + z.object({ + filePath: z.string(), + movePath: z.string().optional(), + before: z.string(), + after: z.string(), + type: z.string().optional(), + }), + ), +}) + import { initializeCommentCheckerCli, getCommentCheckerCliPathPromise, @@ -104,17 +116,6 @@ export function createCommentCheckerHooks(config?: CommentCheckerConfig) { return } - const ApplyPatchMetadataSchema = z.object({ - files: z.array( - z.object({ - filePath: z.string(), - movePath: z.string().optional(), - before: z.string(), - after: z.string(), - type: z.string().optional(), - }), - ), - }) if (toolLower === "apply_patch") { const parsed = ApplyPatchMetadataSchema.safeParse(output.metadata) diff --git a/src/tools/hashline-edit/hashline-edit-diff.ts b/src/tools/hashline-edit/hashline-edit-diff.ts index 901828e79..04f53506d 100644 --- a/src/tools/hashline-edit/hashline-edit-diff.ts +++ b/src/tools/hashline-edit/hashline-edit-diff.ts @@ -4,7 +4,7 @@ export function generateHashlineDiff(oldContent: string, newContent: string, fil const oldLines = oldContent.split("\n") const newLines = newContent.split("\n") - let diff = `--- ${filePath}\n+++ ${filePath}\n` + const parts: string[] = [`--- ${filePath}\n+++ ${filePath}\n`] const maxLines = Math.max(oldLines.length, newLines.length) for (let i = 0; i < maxLines; i += 1) { @@ -14,18 +14,18 @@ export function generateHashlineDiff(oldContent: string, newContent: string, fil const hash = computeLineHash(lineNum, newLine) if (i >= oldLines.length) { - diff += `+ ${lineNum}#${hash}|${newLine}\n` + parts.push(`+ ${lineNum}#${hash}|${newLine}\n`) continue } if (i >= newLines.length) { - diff += `- ${lineNum}# |${oldLine}\n` + parts.push(`- ${lineNum}# |${oldLine}\n`) continue } if (oldLine !== newLine) { - diff += `- ${lineNum}# |${oldLine}\n` - diff += `+ ${lineNum}#${hash}|${newLine}\n` + parts.push(`- ${lineNum}# |${oldLine}\n`) + parts.push(`+ ${lineNum}#${hash}|${newLine}\n`) } } - return diff + return parts.join("") }