perf(core): Automated performance tuning by Claude#1615
Conversation
…on-markdown output intent(perf): automated perf tuning targeting >=2% of a ~1.2s default CLI run, behavior-preserving decision(render-context): gate calculateFileLineCounts behind config.skillGenerate, since only the skill-generation path (skillSectionGenerators / skillStatistics) consumes fileLineCounts; the xml/markdown/plain Handlebars templates never reference it decision(render-context): gate calculateMarkdownDelimiter behind style==='markdown' || skillGenerate, since only the Markdown template and skill sections render Markdown-style code fences; xml/plain ignore markdownCodeBlockDelimiter, so fall back to the default '```' fence learned(critical-path): in warm-cache runs the output branch (produceOutput) is the wall-clock critical path — it overlaps with the cheap metrics branch — so removing these two full-content scans from createRenderContext maps ~1:1 to wall-clock; both scans walk every file's entire content (newline regex + backtick regex), which the profile attributed to ~17ms of main-thread CPU constraint(behavior): output verified byte-identical across xml/markdown/plain (the only diff was the edited source file itself appearing in repomix's self-pack); full suite 1320/1320 passing Benchmark (repomix self-pack, warm cache, paired interleaved A/B, 60 pairs, node spawn wall-clock): base mean 1242.3ms paired delta 23.8ms = 1.91% (95% CI [0.94%, 2.88%]); changed faster in 43/60 pairs
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
⚡ Performance Benchmark
Details
|
There was a problem hiding this comment.
Code Review
This pull request optimizes createRenderContext by conditionally calculating fileLineCounts and markdownCodeBlockDelimiter only when specific configuration flags are active, avoiding unnecessary full-content scans. The reviewer suggests using lazy getters on the returned RenderContext object instead of hardcoded conditional checks. This approach decouples the context creation from specific templates, ensures safety for programmatic users, and maintains efficiency by executing the expensive calculations on-demand and caching the results.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const { config, processedFiles } = outputGeneratorContext; | ||
|
|
||
| // `fileLineCounts` is only consumed by the skill-generation path | ||
| // (skillSectionGenerators / skillStatistics). For the standard xml/markdown/plain | ||
| // output templates it is never referenced, so skip the full-content newline scan | ||
| // over every file unless skill generation is active. | ||
| const skillGenerateActive = config.skillGenerate !== undefined; | ||
| const fileLineCounts = skillGenerateActive ? calculateFileLineCounts(processedFiles) : {}; | ||
|
|
||
| // `markdownCodeBlockDelimiter` is only referenced by the Markdown template and the | ||
| // skill sections (which also render Markdown-style code fences). For xml/plain output | ||
| // it is unused, so skip scanning every file for backtick runs and fall back to the | ||
| // default fence. | ||
| const markdownCodeBlockDelimiter = | ||
| config.output.style === 'markdown' || skillGenerateActive ? calculateMarkdownDelimiter(processedFiles) : '```'; | ||
|
|
There was a problem hiding this comment.
Instead of hardcoding conditional checks based on specific configuration flags (like config.skillGenerate or config.output.style), we can use lazy getters on the returned RenderContext object.
This approach is more robust because:
- Decoupling: It decouples context creation from the specific templates/styles that consume these fields. If a new style is added or an existing template is modified to use these fields, it will work automatically without needing to update the gating logic here.
- Safety: It is 100% safe for programmatic users of the library who might call
createRenderContextdirectly and expect these fields to be populated. - Efficiency: The expensive calculations (
calculateFileLineCountsandcalculateMarkdownDelimiter) are only executed on-demand when they are actually accessed, and the results are cached for subsequent accesses.
const { processedFiles } = outputGeneratorContext;
let cachedFileLineCounts: Record<string, number> | null = null;
let cachedMarkdownDelimiter: string | null = null;
const getFileLineCounts = () => {
if (cachedFileLineCounts === null) {
cachedFileLineCounts = calculateFileLineCounts(processedFiles);
}
return cachedFileLineCounts;
};
const getMarkdownDelimiter = () => {
if (cachedMarkdownDelimiter === null) {
cachedMarkdownDelimiter = calculateMarkdownDelimiter(processedFiles);
}
return cachedMarkdownDelimiter;
};| fileLineCounts, | ||
| fileSummaryEnabled: outputGeneratorContext.config.output.fileSummary, | ||
| directoryStructureEnabled: outputGeneratorContext.config.output.directoryStructure, | ||
| filesEnabled: outputGeneratorContext.config.output.files, | ||
| escapeFileContent: outputGeneratorContext.config.output.parsableStyle, | ||
| markdownCodeBlockDelimiter: calculateMarkdownDelimiter(outputGeneratorContext.processedFiles), | ||
| markdownCodeBlockDelimiter, |
There was a problem hiding this comment.
Use the lazy getters defined above to populate fileLineCounts and markdownCodeBlockDelimiter on-demand.
| fileLineCounts, | |
| fileSummaryEnabled: outputGeneratorContext.config.output.fileSummary, | |
| directoryStructureEnabled: outputGeneratorContext.config.output.directoryStructure, | |
| filesEnabled: outputGeneratorContext.config.output.files, | |
| escapeFileContent: outputGeneratorContext.config.output.parsableStyle, | |
| markdownCodeBlockDelimiter: calculateMarkdownDelimiter(outputGeneratorContext.processedFiles), | |
| markdownCodeBlockDelimiter, | |
| get fileLineCounts() { | |
| return getFileLineCounts(); | |
| }, | |
| fileSummaryEnabled: outputGeneratorContext.config.output.fileSummary, | |
| directoryStructureEnabled: outputGeneratorContext.config.output.directoryStructure, | |
| filesEnabled: outputGeneratorContext.config.output.files, | |
| escapeFileContent: outputGeneratorContext.config.output.parsableStyle, | |
| get markdownCodeBlockDelimiter() { | |
| return getMarkdownDelimiter(); | |
| }, |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1615 +/- ##
=======================================
Coverage 90.86% 90.87%
=======================================
Files 121 121
Lines 4698 4702 +4
Branches 1096 1098 +2
=======================================
+ Hits 4269 4273 +4
Misses 429 429 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Deploying repomix with
|
| Latest commit: |
57a0041
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://926c754d.repomix.pages.dev |
| Branch Preview URL: | https://claude-optimistic-goodall-z9.repomix.pages.dev |
Skip two full-content scans in
createRenderContext(src/core/output/outputGenerate.ts) that are computed on every run but only consumed by output paths that aren't active in the default run.Summary
createRenderContextunconditionally computed two values by scanning the entire content of every file:calculateFileLineCounts— a/\n/gregex over every file. Only ever read by the skill-generation path (skillSectionGenerators/skillStatistics). The xml / markdown / plain / json output templates never reference it.calculateMarkdownDelimiter— a backtick-run regex over every file. Only read by the Markdown template and the skill sections (which render Markdown-style code fences). For xml / plain / json it is unused.This PR gates both behind the paths that actually consume them:
In warm-cache runs the output branch (
produceOutput) is the wall-clock critical path — it overlaps with the much cheaper metrics branch — so eliminating these two scans maps almost 1:1 to wall-clock time.Behavior preservation
This is a pure performance change; output is unchanged.
config.skillGenerate !== undefinedin production (guarded atpackager.tsbeforepackSkill, and set by the MCPgenerateSkillTool), sofileLineCountsis still fully computed wherever it's consumed.style === 'markdown'). For xml/plain/json — which never read the field — the default'```'fence is substituted (and equals whatcalculateMarkdownDelimiterreturns for zero-backtick content anyway).--style json, parsable variants, MCP) — no production behavior change.Benchmark
repomix self-pack, warm token-count cache, paired interleaved A/B (base vs changed
lib, alternating order to cancel drift),nodeprocess spawn measuring full CLI wall-clock, 60 pairs:The point estimate lands just under the 2% target and within measurement noise of it (the container is shared/noisy; per-sample SD ≈ 47 ms). The win is a genuine ~24 ms of eliminated dead work with zero behavioral risk, so it's offered here as a safe, low-risk improvement for review.
Checklist
npm run test— 1320/1320 passingnpm run lint— biome / oxlint / tsgo cleanGenerated by Claude Code