-
-
Notifications
You must be signed in to change notification settings - Fork 941
feat(core): Introduce Intelligent Line Limiting Feature #935
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a comprehensive line-limiting feature enabling per-file line reduction through language-specific parsing and content selection. Includes 11 language-specific strategies, configuration/CLI integration, truncation tracking, performance benchmarking tool, and extensive test coverage. Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI Handler
participant Config as Config Loader
participant Pack as Packager
participant FileProc as File Processor
participant LineLim as LineLimitProcessor
participant Strategy as Language Strategy
participant Output as Output Generator
CLI->>Config: Load config with lineLimit
Config->>Config: Check env var REPOMIX_LINE_LIMIT
Config->>Config: Apply priority: CLI > Config > Env
CLI->>Pack: pack(files, config, verbose)
Pack->>FileProc: processFile(file)
FileProc->>FileProc: Remove comments/empty lines
alt lineLimit enabled
FileProc->>LineLim: applyLineLimit(content, path, limit)
LineLim->>Strategy: Initialize strategy for language
Strategy->>Strategy: Parse AST with tree-sitter
LineLim->>Strategy: identifyHeaderLines()
LineLim->>Strategy: identifyFooterLines()
LineLim->>Strategy: analyzeFunctions()
LineLim->>LineLim: Allocate lines (30% header, 60% core, 10% footer)
LineLim->>LineLim: Select important functions from core
LineLim->>LineLim: Build limited content + truncation metadata
LineLim-->>FileProc: {content, truncation, originalContent}
else lineLimit disabled
FileProc-->>FileProc: Return content only
end
FileProc-->>Pack: ProcessedFile with truncation data
Pack->>Pack: calculateTruncationMetrics()
Pack->>Output: generateOutput(files, truncationMetrics)
Output->>Output: Add truncation indicators to output
Output-->>CLI: Rendered output with truncation info
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes
Areas requiring extra attention:
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
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 |
Summary of ChangesHello @bramburn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances Repomix by introducing an intelligent line limiting feature. This functionality is designed to optimize output size, making it particularly useful for managing AI context windows and reducing processing costs in large codebases. It intelligently selects the most relevant code sections while preserving structural integrity across various programming languages. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces an impressive and well-documented line limiting feature. The implementation is thorough, covering CLI options, configuration files, environment variables, and detailed reporting. The addition of language-specific strategies and a comprehensive performance testing suite is particularly noteworthy. I've identified a few issues, primarily related to configuration priority and script execution, which should be addressed to ensure the feature behaves as specified and is fully robust. Overall, this is a high-quality contribution.
| if (require.main === module) { | ||
| program.parse(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script uses require.main === module to make it executable. However, this is a CommonJS pattern and will not work in an ES module context, which this file appears to be (using import/export). This will cause the script to fail when run directly from the command line.
You should use the ES module equivalent. You'll also need to add import { pathToFileURL } from 'node:url'; at the top of the file.
if (import.meta.url === (await import('node:url')).pathToFileURL(process.argv[1]).href) {
program.parse();
}| // Apply environment variable line limit if set and not already overridden by CLI | ||
| if (envLineLimitValue !== undefined && !cliConfig.output?.lineLimit) { | ||
| mergedOutput.lineLimit = envLineLimitValue; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current logic for merging the lineLimit configuration does not follow the specified priority order of CLI > file > env. The current implementation results in CLI > env > file, meaning the environment variable incorrectly overrides the value from the configuration file.
To fix this, you should apply the environment variable with the lowest precedence. You can achieve this by changing the merge order when creating mergedOutput around line 208 and removing this if block.
For example:
const mergedOutput = {
...baseConfig.output,
...(envLineLimitValue !== undefined && { lineLimit: envLineLimitValue }),
...fileConfig?.output,
...cliConfig.output,
};This ensures the correct CLI > file > env > default priority.
| # Use in package.json scripts | ||
| { | ||
| "scripts": { | ||
| "pack:small": "repomix --line 25 ./src", | ||
| "pack:medium": "repomix --line 100 ./src" | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The example for package.json scripts is incorrectly placed inside a bash code block, which is meant for shell commands. This should be in its own json code block for correct syntax highlighting and to avoid confusion. Additionally, the descriptive comment is inside the code block, which is not ideal.
Consider structuring it like this for better clarity:
...
repomix --line 25 ./src # Uses 25, not 100
```
And in `package.json` scripts:
```json
{
"scripts": {
"pack:small": "repomix --line 25 ./src",
"pack:medium": "repomix --line 100 ./src"
}
}
```| (v: string) => { | ||
| if (!/^\d+$/.test(v)) { | ||
| throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`); | ||
| } | ||
| return Number(v); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation for the --line option allows 0 as a valid value because /^\d+$/ matches '0'. However, the configuration schema requires a positive integer. This will lead to a less user-friendly Zod validation error later, instead of your custom error message. To provide a better user experience, the validation should also check if the value is greater than zero.
(v: string) => {
const num = Number(v);
if (!Number.isInteger(num) || num <= 0) {
throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`);
}
return num;
}| (v: string) => { | ||
| if (!/^\d+$/.test(v)) { | ||
| throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`); | ||
| } | ||
| return Number(v); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the --line option, the validation for the --line-limit alias allows 0 as a valid value, while the schema requires a positive integer. This should be updated to check for positive integers to provide a more immediate and specific error message.
(v: string) => {
const num = Number(v);
if (!Number.isInteger(num) || num <= 0) {
throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`);
}
return num;
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 32
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/config/configLoad.ts (1)
179-214: Fix environment variable precedence logic.The current implementation has a critical bug in configuration precedence. The environment variable
REPOMIX_LINE_LIMITwill override values set in the config file, which violates the expected precedence order.Current behavior: CLI > Env Var > File Config > Default
Expected behavior: CLI > File Config > Env Var > DefaultThe condition at line 212 only checks if the CLI set the value (
!cliConfig.output?.lineLimit), but after the spread operations, iffileConfighadlineLimitset, it's already inmergedOutput.lineLimit. The environment variable then unconditionally overwrites it.Apply this fix to respect file config precedence:
// Apply environment variable line limit if set and not already overridden by CLI - if (envLineLimitValue !== undefined && !cliConfig.output?.lineLimit) { + if (envLineLimitValue !== undefined && mergedOutput.lineLimit === undefined) { mergedOutput.lineLimit = envLineLimitValue; }This ensures the environment variable only applies when neither CLI nor file config has set a value.
src/core/file/fileProcessContent.ts (1)
60-108: Reorder line numbering relative to line limitingLine 60 onwards currently prepends line numbers before calling
applyLineLimit. That means Python, Ruby, etc. hit the line limiter with strings like1: import os, which tree-sitter cannot parse. The call throws, the catch block at Line 96 logs and markstruncated: false, and the feature silently skips truncation whenevershowLineNumbersis enabled—effectively disabling line limiting for a very common configuration. Please apply the limiter to the raw content first and only add numbering afterwards so we always feed valid source into the parser.[suggested fix]
@@ - if (config.output.compress) { + const shouldAddLineNumbers = config.output.showLineNumbers && !config.output.compress; + let lineNumbersApplied = false; + + if (config.output.compress) { @@ - } else if (config.output.showLineNumbers) { - const lines = processedContent.split('\n'); - const padding = lines.length.toString().length; - const numberedLines = lines.map((line, i) => `${(i + 1).toString().padStart(padding)}: ${line}`); - processedContent = numberedLines.join('\n'); - } + } @@ const lineLimitResult = await applyLineLimit(processedContent, rawFile.path, config.output.lineLimit, { preserveStructure: true, showTruncationIndicators: false, // We'll handle indicators in output styles enableCaching: true, }); processedContent = lineLimitResult.content; + + if (shouldAddLineNumbers) { + const lines = processedContent.split('\n'); + const padding = lines.length.toString().length; + const numberedLines = lines.map((line, i) => `${(i + 1).toString().padStart(padding)}: ${line}`); + processedContent = numberedLines.join('\n'); + lineNumbersApplied = true; + } @@ } + + if (shouldAddLineNumbers && !lineNumbersApplied) { + const lines = processedContent.split('\n'); + const padding = lines.length.toString().length; + const numberedLines = lines.map((line, i) => `${(i + 1).toString().padStart(padding)}: ${line}`); + processedContent = numberedLines.join('\n'); + }src/cli/cliReport.ts (1)
43-85: Avoid double-reporting truncation statsWe currently emit the truncation summary twice: first via the inline block (Lines 43-58) and then immediately via
reportTruncationSummary(Lines 76-85). That produces duplicate sections in the CLI output, which is confusing and breaks the “single source of truth” intent behind the helper. Please remove the ad-hoc block and let the helper own the rendering (augment the helper to log the token info if needed), e.g.:- if (packResult.truncationMetrics) { - const stats = getTruncationStats(packResult.truncationMetrics); - logger.log('📊 Truncation Statistics:'); - logger.log(pc.dim('────────────────────')); - logger.log(stats.summary); - if (stats.reductionInfo) { - logger.log(stats.reductionInfo); - } - if (stats.lineLimitInfo) { - logger.log(stats.lineLimitInfo); - } - if (stats.tokenInfo) { - logger.log(stats.tokenInfo); - } - logger.log(''); - } + if (packResult.truncationMetrics) { + reportTruncationSummary(packResult.truncationMetrics); + logger.log(''); + if (verbose && packResult.truncationMetrics.perFileTruncation?.length) { + reportVerboseTruncationDetails(packResult.truncationMetrics.perFileTruncation); + logger.log(''); + } + }…and extend
reportTruncationSummaryso it logstokenInfoalongside the other fields. This keeps the output clean and avoids diverging implementations.
🧹 Nitpick comments (2)
src/core/output/outputStyles/plainStyle.ts (1)
60-62: Consider adding explicit newline before truncation notice.The truncation notice is appended directly after
{{{this.content}}}without a guaranteed newline separator. If the file content doesn't end with a newline, the truncation message will appear on the same line as the last line of content, which may look awkward.Apply this diff to ensure proper spacing:
-{{{this.content}}}{{#if this.truncation.truncated}} - -... (truncated: showing {{this.truncation.truncatedLineCount}} of {{this.truncation.originalLineCount}} lines){{/if}} +{{{this.content}}}{{#if this.truncation.truncated}} + +... (truncated: showing {{this.truncation.truncatedLineCount}} of {{this.truncation.originalLineCount}} lines) +{{/if}}src/cli/cliRun.ts (1)
117-136: Reduce duplication: consolidate validation logic.Both
--lineand--line-limitoptions have identical validation logic. This creates a maintenance burden where fixes need to be applied in two places.Consider extracting the validation to a shared function:
+ const parseLineLimit = (v: string): number => { + if (!/^[1-9]\d*$/.test(v)) { + throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`); + } + return Number(v); + }; + .option( '-l, --line <number>', 'Maximum number of lines per file. Limits each file to specified number of lines using intelligent selection. Alias: --line-limit', - (v: string) => { - if (!/^\d+$/.test(v)) { - throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`); - } - return Number(v); - }, + parseLineLimit, ) .option( '--line-limit <number>', 'Alias for --line option. Maximum number of lines per file using intelligent selection', - (v: string) => { - if (!/^\d+$/.test(v)) { - throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`); - } - return Number(v); - }, + parseLineLimit, )
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (68)
README.md(5 hunks)docs/performance-testing.md(1 hunks)repomix-instruction.md(0 hunks)scripts/performance-benchmark.ts(1 hunks)src/cli/actions/defaultAction.ts(2 hunks)src/cli/actions/workers/defaultActionWorker.ts(1 hunks)src/cli/cliReport.ts(5 hunks)src/cli/cliRun.ts(1 hunks)src/cli/reporters/tokenCountTreeReporter.ts(2 hunks)src/cli/types.ts(1 hunks)src/config/configLoad.ts(2 hunks)src/config/configSchema.ts(2 hunks)src/core/file/fileProcess.ts(2 hunks)src/core/file/fileProcessContent.ts(3 hunks)src/core/file/fileTypes.ts(1 hunks)src/core/file/lineLimitProcessor.ts(1 hunks)src/core/file/lineLimitStrategies/CLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/CSharpLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/DartLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/GoLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/JavaLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/KotlinLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/LineLimitStrategyRegistry.ts(1 hunks)src/core/file/lineLimitStrategies/PhpLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/PythonLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/RubyLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/RustLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/SwiftLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitStrategies/TypeScriptLineLimitStrategy.ts(1 hunks)src/core/file/lineLimitTypes.ts(1 hunks)src/core/file/truncationMetrics.ts(1 hunks)src/core/file/workers/fileProcessWorker.ts(1 hunks)src/core/metrics/TokenCounter.ts(1 hunks)src/core/metrics/calculateMetrics.ts(6 hunks)src/core/metrics/calculateSelectiveFileMetrics.ts(2 hunks)src/core/metrics/workers/calculateMetricsWorker.ts(2 hunks)src/core/metrics/workers/types.ts(1 hunks)src/core/output/outputGenerate.ts(2 hunks)src/core/output/outputStyles/markdownStyle.ts(1 hunks)src/core/output/outputStyles/plainStyle.ts(1 hunks)src/core/output/outputStyles/xmlStyle.ts(1 hunks)src/core/packager.ts(6 hunks)src/core/tokenCount/buildTokenCountStructure.ts(2 hunks)src/core/tokenCount/types.ts(1 hunks)tests/core/file/fileProcess.test.ts(7 hunks)tests/core/file/fileProcessContent.test.ts(1 hunks)tests/core/file/lineLimitConfig.test.ts(1 hunks)tests/core/file/lineLimitEdgeCases.test.ts(1 hunks)tests/core/file/lineLimitIntegration.test.ts(1 hunks)tests/core/file/lineLimitProcessor.test.ts(1 hunks)tests/core/file/lineLimitStrategies/AllLineLimitStrategies.test.ts(1 hunks)tests/core/file/lineLimitStrategies/CLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitStrategies/CSharpLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitStrategies/GoLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitStrategies/JavaLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitStrategies/LineLimitStrategyRegistry.test.ts(1 hunks)tests/core/file/lineLimitStrategies/PhpLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitStrategies/PythonLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitStrategies/RubyLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitStrategies/RustLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitStrategies/TypeScriptLineLimitStrategy.test.ts(1 hunks)tests/core/file/lineLimitTypes.test.ts(1 hunks)tests/core/file/truncationMetrics.test.ts(1 hunks)tests/core/output/outputGenerate.test.ts(1 hunks)tests/core/output/outputStyles/jsonStyle.test.ts(2 hunks)tests/core/output/truncationIndicators.test.ts(1 hunks)tests/integration-tests/cliLineLimit.test.ts(1 hunks)tests/integration-tests/configLineLimit.test.ts(1 hunks)
💤 Files with no reviewable changes (1)
- repomix-instruction.md
🧰 Additional context used
🧬 Code graph analysis (50)
src/cli/actions/defaultAction.ts (1)
src/cli/cliReport.ts (1)
reportResults(15-88)
src/cli/actions/workers/defaultActionWorker.ts (1)
src/core/packager.ts (1)
pack(51-164)
tests/core/file/lineLimitStrategies/LineLimitStrategyRegistry.test.ts (2)
src/core/file/lineLimitStrategies/LineLimitStrategyRegistry.ts (1)
LineLimitStrategyRegistry(19-134)src/core/file/lineLimitTypes.ts (1)
LanguageStrategy(80-85)
src/core/file/workers/fileProcessWorker.ts (1)
src/core/file/fileProcessContent.ts (1)
processContent(22-113)
tests/core/file/lineLimitEdgeCases.test.ts (2)
src/core/file/lineLimitProcessor.ts (1)
LineLimitProcessor(19-412)src/core/file/lineLimitTypes.ts (3)
LineLimitTooSmallError(135-144)LineLimitError(120-133)LanguageStrategy(80-85)
tests/core/file/lineLimitConfig.test.ts (2)
src/core/file/lineLimitProcessor.ts (1)
LineLimitProcessor(19-412)src/core/file/lineLimitTypes.ts (1)
LineLimitTooSmallError(135-144)
tests/core/file/lineLimitStrategies/GoLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/GoLineLimitStrategy.ts (1)
GoLineLimitStrategy(8-385)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
src/config/configLoad.ts (1)
src/shared/logger.ts (1)
logger(89-89)
src/core/file/lineLimitStrategies/LineLimitStrategyRegistry.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategyRegistry(90-92)LanguageStrategy(80-85)src/core/treeSitter/lang2Query.ts (1)
SupportedLang(37-37)
src/core/file/lineLimitStrategies/RustLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryRust.ts (1)
queryRust(1-75)
tests/core/file/lineLimitStrategies/RubyLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/RubyLineLimitStrategy.ts (1)
RubyLineLimitStrategy(8-362)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
src/core/file/lineLimitStrategies/DartLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryDart.ts (1)
queryDart(1-29)
tests/core/file/lineLimitStrategies/JavaLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/JavaLineLimitStrategy.ts (1)
JavaLineLimitStrategy(8-356)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
tests/core/file/lineLimitTypes.test.ts (1)
src/core/file/lineLimitTypes.ts (3)
LineLimitError(120-133)LineLimitTooSmallError(135-144)LineLimitParseError(146-151)
tests/integration-tests/cliLineLimit.test.ts (3)
tests/testing/testUtils.ts (1)
isWindows(47-47)src/cli/cliRun.ts (1)
runCli(232-283)src/shared/errorHandle.ts (1)
RepomixError(6-11)
src/core/file/fileProcessContent.ts (2)
src/core/file/fileTypes.ts (2)
RawFile(1-4)TruncationInfo(13-18)src/core/file/lineLimitProcessor.ts (2)
applyLineLimit(52-118)applyLineLimit(417-473)
src/core/file/lineLimitStrategies/KotlinLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryJava.ts (1)
queryJava(1-33)
tests/core/file/lineLimitStrategies/TypeScriptLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/TypeScriptLineLimitStrategy.ts (1)
TypeScriptLineLimitStrategy(8-339)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
src/core/file/truncationMetrics.ts (2)
src/core/file/fileTypes.ts (3)
ProcessedFile(6-11)TruncationMetrics(20-30)PerFileTruncation(32-38)src/config/configSchema.ts (1)
RepomixConfigMerged(163-163)
src/core/file/lineLimitStrategies/PythonLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryPython.ts (1)
queryPython(1-32)
src/core/file/lineLimitStrategies/JavaLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryJava.ts (1)
queryJava(1-33)
src/core/file/lineLimitStrategies/CLineLimitStrategy.ts (3)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryCpp.ts (1)
queryCpp(1-19)src/core/treeSitter/queries/queryC.ts (1)
queryC(1-13)
tests/core/file/lineLimitStrategies/CSharpLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/CSharpLineLimitStrategy.ts (1)
CSharpLineLimitStrategy(8-385)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
tests/integration-tests/configLineLimit.test.ts (3)
tests/testing/testUtils.ts (1)
isWindows(47-47)src/config/configLoad.ts (2)
loadFileConfig(69-111)mergeConfigs(170-253)src/config/configSchema.ts (1)
repomixConfigFileSchema(137-137)
src/core/file/lineLimitStrategies/CSharpLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryCSharp.ts (1)
queryCSharp(1-50)
tests/core/file/lineLimitProcessor.test.ts (4)
src/core/file/lineLimitProcessor.ts (3)
LineLimitProcessor(19-412)applyLineLimit(52-118)applyLineLimit(417-473)src/core/file/lineLimitTypes.ts (3)
LineLimitError(120-133)LineLimitParseError(146-151)LineLimitConfig(97-102)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)src/core/file/lineLimitStrategies/LineLimitStrategyRegistry.ts (1)
LineLimitStrategyRegistry(19-134)
tests/core/file/lineLimitStrategies/PythonLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/PythonLineLimitStrategy.ts (1)
PythonLineLimitStrategy(8-359)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
scripts/performance-benchmark.ts (2)
tests/performance/performanceUtils.ts (3)
PerformanceThresholds(63-82)PerformanceMetrics(8-31)measurePerformance(161-228)tests/performance/testDataGenerators.ts (3)
generateLargeFile(1480-1495)generatePerformanceTestCases(1573-1599)PerformanceTestCase(7-13)
tests/core/file/lineLimitStrategies/CLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/CLineLimitStrategy.ts (1)
CLineLimitStrategy(9-398)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
src/core/file/lineLimitStrategies/SwiftLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/querySwift.ts (1)
querySwift(1-41)
src/core/packager.ts (2)
src/core/file/fileTypes.ts (1)
TruncationMetrics(20-30)src/core/file/truncationMetrics.ts (1)
calculateTruncationMetrics(7-91)
tests/core/file/lineLimitStrategies/AllLineLimitStrategies.test.ts (3)
src/core/file/lineLimitTypes.ts (1)
LineLimitConfig(97-102)src/core/file/lineLimitStrategies/LineLimitStrategyRegistry.ts (1)
LineLimitStrategyRegistry(19-134)src/core/file/lineLimitProcessor.ts (1)
LineLimitProcessor(19-412)
tests/core/file/lineLimitStrategies/RustLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/RustLineLimitStrategy.ts (1)
RustLineLimitStrategy(8-420)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
src/core/file/fileProcess.ts (1)
src/core/file/truncationMetrics.ts (1)
formatTruncationProgress(96-104)
tests/core/file/fileProcessContent.test.ts (5)
src/core/file/fileTypes.ts (1)
RawFile(1-4)src/core/file/fileProcessContent.ts (1)
processContent(22-113)src/core/file/truncateBase64.ts (1)
truncateBase64Content(15-45)src/core/file/fileManipulate.ts (1)
getFileManipulator(367-370)src/core/file/lineLimitProcessor.ts (2)
applyLineLimit(52-118)applyLineLimit(417-473)
src/cli/cliRun.ts (1)
src/shared/errorHandle.ts (1)
RepomixError(6-11)
tests/core/file/lineLimitIntegration.test.ts (1)
src/core/file/lineLimitProcessor.ts (1)
LineLimitProcessor(19-412)
tests/core/output/truncationIndicators.test.ts (4)
src/config/configSchema.ts (1)
RepomixConfigMerged(163-163)src/core/file/fileTypes.ts (1)
ProcessedFile(6-11)tests/testing/testUtils.ts (1)
createMockConfig(15-45)src/core/output/outputGenerate.ts (1)
generateOutput(243-284)
src/cli/cliReport.ts (4)
src/core/packager.ts (1)
PackResult(19-35)src/cli/reporters/tokenCountTreeReporter.ts (1)
reportTokenCountTree(11-43)src/core/file/truncationMetrics.ts (1)
getTruncationStats(126-160)src/core/file/fileTypes.ts (1)
PerFileTruncation(32-38)
src/core/file/lineLimitStrategies/GoLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryGo.ts (1)
queryGo(1-64)
src/core/file/lineLimitStrategies/PhpLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryPhp.ts (1)
queryPhp(1-48)
tests/core/file/truncationMetrics.test.ts (2)
src/core/file/fileTypes.ts (2)
ProcessedFile(6-11)TruncationMetrics(20-30)src/core/file/truncationMetrics.ts (4)
calculateTruncationMetrics(7-91)formatTruncationProgress(96-104)getTruncationSummary(109-121)getTruncationStats(126-160)
src/core/metrics/workers/calculateMetricsWorker.ts (3)
src/core/metrics/TokenCounter.ts (2)
countTokensPair(45-53)countTokens(19-43)src/core/metrics/tokenCounterFactory.ts (1)
getTokenCounter(12-19)src/shared/logger.ts (1)
error(34-38)
tests/core/file/lineLimitStrategies/PhpLineLimitStrategy.test.ts (2)
src/core/file/lineLimitStrategies/PhpLineLimitStrategy.ts (1)
PhpLineLimitStrategy(8-384)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)
src/core/file/lineLimitStrategies/RubyLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryRuby.ts (1)
queryRuby(1-72)
src/core/metrics/calculateSelectiveFileMetrics.ts (4)
src/core/file/fileTypes.ts (1)
ProcessedFile(6-11)src/shared/processConcurrency.ts (1)
TaskRunner(97-100)src/core/metrics/workers/calculateMetricsWorker.ts (2)
TokenCountTask(17-21)TokenCountPairTask(23-28)src/core/metrics/workers/types.ts (1)
FileMetrics(1-7)
src/core/metrics/calculateMetrics.ts (2)
src/shared/processConcurrency.ts (2)
TaskRunner(97-100)initTaskRunner(102-108)src/core/metrics/workers/calculateMetricsWorker.ts (1)
TokenCountPairTask(23-28)
src/core/file/lineLimitProcessor.ts (5)
src/core/file/lineLimitTypes.ts (8)
LanguageStrategy(80-85)LineLimitError(120-133)LineLimitParseError(146-151)LineLimitConfig(97-102)LineLimitResult(47-54)LineAllocation(38-42)SourceLine(6-12)TruncationIndicator(71-75)src/core/treeSitter/loadLanguage.ts (1)
loadLanguage(7-19)src/core/file/lineLimitStrategies/LineLimitStrategyRegistry.ts (1)
LineLimitStrategyRegistry(19-134)src/core/treeSitter/lang2Query.ts (1)
SupportedLang(37-37)src/core/treeSitter/ext2Lang.ts (1)
ext2Lang(4-31)
src/cli/reporters/tokenCountTreeReporter.ts (1)
src/core/tokenCount/buildTokenCountStructure.ts (1)
FileWithTokens(3-8)
src/core/file/lineLimitStrategies/TypeScriptLineLimitStrategy.ts (2)
src/core/file/lineLimitTypes.ts (2)
LanguageStrategy(80-85)FunctionAnalysis(26-33)src/core/treeSitter/queries/queryTypescript.ts (1)
queryTypescript(1-73)
🪛 LanguageTool
docs/performance-testing.md
[grammar] ~5-~5: Use a hyphen to join words.
Context: ...erformance testing approach for the line limiting feature in Repomix. The testing...
(QB_NEW_EN_HYPHEN)
[grammar] ~9-~9: Use a hyphen to join words.
Context: ... Based on the PRD requirements, the line limiting feature must meet these perform...
(QB_NEW_EN_HYPHEN)
[style] ~71-~71: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “CLI”.
Context: ...) Comprehensive benchmarking tool with CLI interface: - Automated Benchmarking: Run ful...
(ACRONYM_TAUTOLOGY)
[style] ~91-~91: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...2000-10000 lines) with scaling limits - Very large files (15000-50000 lines) with aggressi...
(EN_WEAK_ADJECTIVE)
[style] ~109-~109: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...nes): Should process in < 100 seconds - Very large files (> 10000 lines): Should process i...
(EN_WEAK_ADJECTIVE)
[style] ~115-~115: As an alternative to the over-used intensifier ‘extremely’, consider replacing this phrase.
Context: ...adation detection - Edge case handling (extremely large files) ### 3. Language-Specific Perfor...
(EN_WEAK_ADJECTIVE)
[style] ~144-~144: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...- Single Large File: Processing one very large file - Multiple Large Files: Proces...
(EN_WEAK_ADJECTIVE)
[grammar] ~483-~483: Use a hyphen to join words.
Context: ...ance testing suite ensures that the line limiting feature meets all PRD requireme...
(QB_NEW_EN_HYPHEN)
README.md
[grammar] ~437-~437: Use a hyphen to join words.
Context: ... #### How Line Limiting Works The line limiting feature uses an intelligent sel...
(QB_NEW_EN_HYPHEN)
🪛 markdownlint-cli2 (0.18.1)
docs/performance-testing.md
26-26: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
291-291: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (16)
src/cli/actions/defaultAction.ts (2)
93-93: LGTM: Verbose flag integration.The verbose flag is properly passed to enable detailed truncation reporting.
286-292: LGTM: Line limit CLI option handling.The line option is correctly wired to the output configuration with proper schema validation downstream.
src/cli/types.ts (1)
59-59: LGTM: CLI option type definition.The optional
lineproperty is correctly typed for the new line-limiting feature.src/config/configSchema.ts (1)
40-46: LGTM: Schema definition with clear documentation.The
lineLimitconfiguration option is properly validated as a positive integer with excellent inline documentation describing the 30/60/10 distribution strategy.Also applies to: 106-106
src/core/output/outputStyles/markdownStyle.ts (1)
44-46: LGTM: Truncation notice in Markdown output.The conditional truncation notice is properly formatted and provides clear information about the number of lines shown vs. original.
src/core/output/outputStyles/xmlStyle.ts (1)
49-51: LGTM: Truncation notice in XML output.The HTML comment format is appropriate for XML output and maintains consistency with other output styles.
tests/core/output/outputGenerate.test.ts (1)
274-279: LGTM: Test updated for new JSON output structure.The test correctly reflects the new JSON output format where file entries are objects with a
contentproperty. Note that this represents a breaking change in the JSON output format.src/core/metrics/TokenCounter.ts (1)
45-53: LGTM: Convenient paired token counting.The
countTokensPairmethod is a clean convenience wrapper that reuses existing token counting logic for both original and truncated content.src/config/configLoad.ts (1)
179-190: LGTM: Environment variable parsing and validation.The parsing and validation logic for
REPOMIX_LINE_LIMITis well-implemented with appropriate error handling and logging for invalid values.src/core/tokenCount/types.ts (1)
4-5: LGTM! Clean type extension.The optional fields
originalTokensandtruncatedappropriately extend the interface to support truncation metadata without breaking existing code.src/core/file/fileProcess.ts (1)
44-64: LGTM! Well-integrated truncation tracking.The truncation count tracking and conditional progress messaging are correctly implemented. The use of optional chaining (
result.truncation?.truncated) safely handles cases where truncation info is absent, and the conditional logic appropriately switches between enhanced and standard progress formats based on configuration.tests/core/file/fileProcess.test.ts (1)
73-167: LGTM! Consistent test updates.All test assertions have been correctly updated to access
result.contentinstead ofresultdirectly, reflecting the API change whereprocessContentnow returns an object with acontentproperty. The changes are consistent across all test cases.tests/core/output/outputStyles/jsonStyle.test.ts (1)
124-150: LGTM! Test expectations align with new JSON format.The test assertions have been correctly updated to expect file content as objects with a
contentproperty, consistent with the new JSON output structure that supports additional metadata like truncation information.src/core/tokenCount/buildTokenCountStructure.ts (1)
6-7: LGTM! Clean metadata propagation.The interface extension and field propagation are correctly implemented. The use of
file.truncated || falseon line 46 ensures thetruncatedfield is always a boolean value, consistent with the type definition.Also applies to: 45-46
src/core/file/fileTypes.ts (1)
9-38: LGTM! Comprehensive truncation type definitions.The new interfaces (
TruncationInfo,TruncationMetrics,PerFileTruncation) and the extensions toProcessedFileare well-structured and provide comprehensive metadata tracking for the line-limiting feature. The use of optional fields ensures backward compatibility.tests/core/file/lineLimitStrategies/AllLineLimitStrategies.test.ts (1)
421-428: Add missingendin Ruby sampleThe Ruby example lacks an
endto closedef save_user, so the class never terminates cleanly. Tree-sitter will flag the snippet as erroneous, weakening the test signal. Add the method-closingendbefore the class-closingend:rescue false end -end + end +endLikely an incorrect or invalid review comment.
| return await applyLineLimit(content, filePath, testCase.lineLimit); | ||
| }); | ||
|
|
||
| caseResults.push(metrics); | ||
| } | ||
|
|
||
| // Calculate average metrics | ||
| const avgMetrics = this.calculateAverageMetrics(caseResults); | ||
| this.currentResults.push(avgMetrics); | ||
|
|
||
| // Check against thresholds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix zero-throughput benchmark metrics
measurePerformance only fills linesProcessed, linesOutput, and lineLimit when the measured function returns originalLineCount/limitedLineCount (or metadata) at the top level. applyLineLimit exposes those counts under truncation, so every iteration records zeros. That drives metrics.throughputLinesPerSec to 0, passesThresholds always fails, and the summary/report show false negatives even when the limiter is fast. Please surface the counts (and limit) before returning from the measured callback—for example, spread the result and copy truncation.originalLineCount/truncation.truncatedLineCount (and metadata) into the shape measurePerformance expects—so the benchmarks emit real numbers.
🤖 Prompt for AI Agents
In scripts/performance-benchmark.ts around lines 150-160, the measured callback
returns counts nested under truncation so measurePerformance sees zeros; modify
the callback to return an object shaped like measurePerformance expects by
spreading the original result and copying truncation.originalLineCount ->
linesProcessed, truncation.truncatedLineCount -> linesOutput (and set lineLimit
from testCase.lineLimit or truncation.limit if present), and copy any metadata
up to the top level before returning so throughput and threshold checks use real
numbers.
| .option( | ||
| '-l, --line <number>', | ||
| 'Maximum number of lines per file. Limits each file to specified number of lines using intelligent selection. Alias: --line-limit', | ||
| (v: string) => { | ||
| if (!/^\d+$/.test(v)) { | ||
| throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`); | ||
| } | ||
| return Number(v); | ||
| }, | ||
| ) | ||
| .option( | ||
| '--line-limit <number>', | ||
| 'Alias for --line option. Maximum number of lines per file using intelligent selection', | ||
| (v: string) => { | ||
| if (!/^\d+$/.test(v)) { | ||
| throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`); | ||
| } | ||
| return Number(v); | ||
| }, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix validation to match "positive integer" requirement.
The regex /^\d+$/ accepts 0, but the error message states "Must be a positive integer." Positive integers are typically > 0, not >= 0. This creates a validation mismatch where --line 0 would pass regex validation but violate the semantic requirement.
Apply this diff to fix the validation:
'-l, --line <number>',
'Maximum number of lines per file. Limits each file to specified number of lines using intelligent selection. Alias: --line-limit',
(v: string) => {
- if (!/^\d+$/.test(v)) {
+ if (!/^[1-9]\d*$/.test(v)) {
throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`);
}
return Number(v);
},
)
.option(
'--line-limit <number>',
'Alias for --line option. Maximum number of lines per file using intelligent selection',
(v: string) => {
- if (!/^\d+$/.test(v)) {
+ if (!/^[1-9]\d*$/.test(v)) {
throw new RepomixError(`Invalid line limit: '${v}'. Must be a positive integer.`);
}
return Number(v);
},
)Alternatively, if 0 should be allowed, change the error message to "Must be a non-negative integer."
🤖 Prompt for AI Agents
In src/cli/cliRun.ts around lines 117 to 136, the validator uses /^\d+$/ which
allows "0" despite the message saying "Must be a positive integer"; update both
option validators (for -l/--line and --line-limit) to enforce >0 — either change
the regex to /^[1-9]\d*$/ or parse to Number and check Number(v) > 0, and keep
throwing RepomixError with the same message when the check fails so "0" is
rejected; ensure both validators return the numeric value on success.
| const selectedLines = [...headerLines, ...coreLines, ...footerLines] | ||
| .sort((a, b) => a.lineNumber - b.lineNumber) | ||
| .slice(0, config.lineLimit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deduplicate overlapping sections before slicing.
Header/core/footer selections can point to the same line numbers. Today those duplicates consume the quota, so we repeat the same lines while dropping unique ones later in the file. De‑duplicate on lineNumber (keeping the most important variant) before the final sort/slice.
- const selectedLines = [...headerLines, ...coreLines, ...footerLines]
- .sort((a, b) => a.lineNumber - b.lineNumber)
- .slice(0, config.lineLimit);
+ const merged = [...headerLines, ...coreLines, ...footerLines];
+ const deduped = Array.from(
+ merged
+ .reduce((acc, line) => {
+ const current = acc.get(line.lineNumber);
+ if (!current || current.importance < line.importance) {
+ acc.set(line.lineNumber, line);
+ }
+ return acc;
+ }, new Map<number, SourceLine>())
+ .values(),
+ )
+ .sort((a, b) => a.lineNumber - b.lineNumber)
+ .slice(0, config.lineLimit);
+
+ const selectedLines = deduped;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/core/file/lineLimitProcessor.ts around lines 81 to 83, the
header/core/footer arrays may contain overlapping lineNumber entries which
currently consume the lineLimit quota; deduplicate by lineNumber before sorting
and slicing by merging the three arrays with a defined priority (header highest,
then core, then footer), keeping the entry from the highest-priority section
when duplicate lineNumbers are found, then create the final array from the
deduplicated entries, sort by lineNumber and slice to config.lineLimit.
| private calculateLineAllocation(totalLimit: number): LineAllocation { | ||
| const headerLines = Math.floor(totalLimit * 0.3); | ||
| const coreLines = Math.floor(totalLimit * 0.6); | ||
| const footerLines = Math.floor(totalLimit * 0.1); | ||
|
|
||
| return { | ||
| headerLines, | ||
| coreLines, | ||
| footerLines, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honor the requested limit by distributing the remainder.
Flooring the 30/60/10 buckets drops the remainder, so for most limits we emit fewer lines than requested (e.g., a limit of 25 only returns 24 lines). That’s a correctness bug for the flagship feature. Please reallocate the leftover lines so the buckets always sum to the caller’s limit.
private calculateLineAllocation(totalLimit: number): LineAllocation {
- const headerLines = Math.floor(totalLimit * 0.3);
- const coreLines = Math.floor(totalLimit * 0.6);
- const footerLines = Math.floor(totalLimit * 0.1);
-
- return {
- headerLines,
- coreLines,
- footerLines,
- };
+ const allocation: LineAllocation = {
+ headerLines: Math.floor(totalLimit * 0.3),
+ coreLines: Math.floor(totalLimit * 0.6),
+ footerLines: Math.floor(totalLimit * 0.1),
+ };
+
+ let remainder = totalLimit - (allocation.headerLines + allocation.coreLines + allocation.footerLines);
+ const distributionOrder: Array<keyof LineAllocation> = ['coreLines', 'headerLines', 'footerLines'];
+
+ for (const key of distributionOrder) {
+ if (remainder === 0) {
+ break;
+ }
+ allocation[key] += 1;
+ remainder -= 1;
+ }
+
+ return allocation;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private calculateLineAllocation(totalLimit: number): LineAllocation { | |
| const headerLines = Math.floor(totalLimit * 0.3); | |
| const coreLines = Math.floor(totalLimit * 0.6); | |
| const footerLines = Math.floor(totalLimit * 0.1); | |
| return { | |
| headerLines, | |
| coreLines, | |
| footerLines, | |
| }; | |
| private calculateLineAllocation(totalLimit: number): LineAllocation { | |
| const allocation: LineAllocation = { | |
| headerLines: Math.floor(totalLimit * 0.3), | |
| coreLines: Math.floor(totalLimit * 0.6), | |
| footerLines: Math.floor(totalLimit * 0.1), | |
| }; | |
| let remainder = totalLimit - (allocation.headerLines + allocation.coreLines + allocation.footerLines); | |
| const distributionOrder: Array<keyof LineAllocation> = ['coreLines', 'headerLines', 'footerLines']; | |
| for (const key of distributionOrder) { | |
| if (remainder === 0) { | |
| break; | |
| } | |
| allocation[key] += 1; | |
| remainder -= 1; | |
| } | |
| return allocation; | |
| } |
🤖 Prompt for AI Agents
In src/core/file/lineLimitProcessor.ts around lines 202 to 211, the current
30/60/10 flooring drops the remainder so the returned buckets can sum to less
than totalLimit; compute headerLines = Math.floor(totalLimit * 0.3), coreLines =
Math.floor(totalLimit * 0.6), footerLines = Math.floor(totalLimit * 0.1), then
compute remainder = totalLimit - (headerLines + coreLines + footerLines) and
distribute that remainder back into the buckets (e.g., in priority order core ->
header -> footer, adding 1 to each until remainder is 0) so the returned
headerLines/coreLines/footerLines always sum to totalLimit.
| if (finalConfig.showTruncationIndicators && result.truncationIndicators.length > 0) { | ||
| const lines = limitedContent.split('\n'); | ||
| for (const indicator of result.truncationIndicators) { | ||
| if (indicator.position <= lines.length) { | ||
| lines.splice(indicator.position - 1, 0, `// ${indicator.description}`); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix truncation indicator insertion and match comment syntax to the language.
Because indicator.position is measured in the original file, the <= lines.length guard prevents indicators from ever being inserted, so showTruncationIndicators is currently a no-op. When we do insert, we always prepend //, which breaks Python, Ruby, SQL, etc. Please clamp the insertion index against the truncated output and derive an appropriate line comment prefix from the file extension.
- if (finalConfig.showTruncationIndicators && result.truncationIndicators.length > 0) {
- const lines = limitedContent.split('\n');
- for (const indicator of result.truncationIndicators) {
- if (indicator.position <= lines.length) {
- lines.splice(indicator.position - 1, 0, `// ${indicator.description}`);
- }
- }
+ if (finalConfig.showTruncationIndicators && result.truncationIndicators.length > 0) {
+ const lines = limitedContent.split('\n');
+ const commentPrefix = getTruncationCommentPrefix(filePath);
+ for (const indicator of result.truncationIndicators) {
+ const insertionIndex = Math.min(Math.max(indicator.position - 1, 0), lines.length);
+ lines.splice(insertionIndex, 0, `${commentPrefix}${indicator.description}`);
+ }
return {
content: lines.join('\n'),
truncation: {
truncated: result.originalLineCount > lineLimit,
@@
};
} finally {
processor.dispose();
}
};
+
+function getTruncationCommentPrefix(filePath: string): string {
+ const ext = filePath.split('.').pop()?.toLowerCase();
+ if (!ext) {
+ return '// ';
+ }
+
+ if (['py', 'rb', 'sh', 'bash', 'ps1'].includes(ext)) {
+ return '# ';
+ }
+
+ if (ext === 'sql') {
+ return '-- ';
+ }
+
+ return '// ';
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (finalConfig.showTruncationIndicators && result.truncationIndicators.length > 0) { | |
| const lines = limitedContent.split('\n'); | |
| for (const indicator of result.truncationIndicators) { | |
| if (indicator.position <= lines.length) { | |
| lines.splice(indicator.position - 1, 0, `// ${indicator.description}`); | |
| } | |
| if (finalConfig.showTruncationIndicators && result.truncationIndicators.length > 0) { | |
| const lines = limitedContent.split('\n'); | |
| const commentPrefix = getTruncationCommentPrefix(filePath); | |
| for (const indicator of result.truncationIndicators) { | |
| const insertionIndex = Math.min(Math.max(indicator.position - 1, 0), lines.length); | |
| lines.splice(insertionIndex, 0, `${commentPrefix}${indicator.description}`); | |
| } | |
| return { | |
| content: lines.join('\n'), | |
| truncation: { | |
| truncated: result.originalLineCount > lineLimit, | |
| } | |
| }; | |
| } finally { | |
| processor.dispose(); | |
| } | |
| }; | |
| function getTruncationCommentPrefix(filePath: string): string { | |
| const ext = filePath.split('.').pop()?.toLowerCase(); | |
| if (!ext) { | |
| return '// '; | |
| } | |
| if (['py', 'rb', 'sh', 'bash', 'ps1'].includes(ext)) { | |
| return '# '; | |
| } | |
| if (ext === 'sql') { | |
| return '-- '; | |
| } | |
| return '// '; | |
| } |
🤖 Prompt for AI Agents
In src/core/file/lineLimitProcessor.ts around lines 443 to 448, the current
guard uses indicator.position <= lines.length which prevents insertion when
positions are based on the original file and always uses '//' as the prefix;
change to clamp the insert index to the truncated output: compute insertIndex =
Math.max(1, Math.min(indicator.position, lines.length + 1)) and insert at
insertIndex - 1 so indicators at or beyond the end still append; derive a
language-appropriate line comment prefix from the file extension (map common
extensions: .py -> '#', .rb -> '#', .sql -> '--', .sh -> '#',
.js/.ts/.java/.c/.cpp -> '//', etc., with a sensible default) and use that
prefix instead of hardcoded '//' when inserting the indicator comment.
| const content = [ | ||
| 'import React from "react";', | ||
| 'function App() {', | ||
| ' return <div>Hello</div>;', | ||
| '}', | ||
| 'export default App;', | ||
| ].join('\n'); | ||
|
|
||
| // First call | ||
| const startTime1 = Date.now(); | ||
| const result1 = await processor.applyLineLimit(content, 'test.jsx', { | ||
| lineLimit: 3, | ||
| preserveStructure: true, | ||
| showTruncationIndicators: true, | ||
| enableCaching: true, | ||
| }); | ||
| const endTime1 = Date.now(); | ||
|
|
||
| // Second call (should use cache) | ||
| const startTime2 = Date.now(); | ||
| const result2 = await processor.applyLineLimit(content, 'test.jsx', { | ||
| lineLimit: 3, | ||
| preserveStructure: true, | ||
| showTruncationIndicators: true, | ||
| enableCaching: true, | ||
| }); | ||
| const endTime2 = Date.now(); | ||
|
|
||
| expect(result1.selectedLines).toEqual(result2.selectedLines); | ||
| expect(endTime2 - startTime2).toBeLessThan(endTime1 - startTime1); // Second call should be faster | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace timing-based cache assertion
Using wall-clock timings (endTime2 - startTime2) to prove caching is unreliable—noise from the event loop or GC can easily flip the comparison and turn this into a flaky test. Please swap the timing check for a deterministic assertion, e.g. spy on the underlying parser so the second call confirms no extra parse happens:
- const startTime1 = Date.now();
- const result1 = await processor.applyLineLimit(content, 'test.jsx', {
- lineLimit: 3,
- preserveStructure: true,
- showTruncationIndicators: true,
- enableCaching: true,
- });
- const endTime1 = Date.now();
-
- // Second call (should use cache)
- const startTime2 = Date.now();
- const result2 = await processor.applyLineLimit(content, 'test.jsx', {
- lineLimit: 3,
- preserveStructure: true,
- showTruncationIndicators: true,
- enableCaching: true,
- });
- const endTime2 = Date.now();
-
- expect(result1.selectedLines).toEqual(result2.selectedLines);
- expect(endTime2 - startTime2).toBeLessThan(endTime1 - startTime1); // Second call should be faster
+ await processor.applyLineLimit(content, 'test.jsx', {
+ lineLimit: 3,
+ preserveStructure: true,
+ showTruncationIndicators: true,
+ enableCaching: true,
+ });
+ const parser = (processor as any).languageParser;
+ const parseSpy = vi.spyOn(parser, 'parse');
+
+ const result2 = await processor.applyLineLimit(content, 'test.jsx', {
+ lineLimit: 3,
+ preserveStructure: true,
+ showTruncationIndicators: true,
+ enableCaching: true,
+ });
+
+ expect(result2.selectedLines).toHaveLength(3);
+ expect(parseSpy).not.toHaveBeenCalled();This keeps the intent while making the suite stable.
🤖 Prompt for AI Agents
In tests/core/file/lineLimitConfig.test.ts around lines 264 to 294, the test
uses wall-clock timing to assert caching which is flaky; replace the
timing-based assertion with a deterministic spy on the underlying parser/cache
entry so you can assert the parser is only invoked once on the two calls. Update
the test to import or access the parser (or processor.parse method), wrap it
with a spy/mock before the first call, call applyLineLimit twice with the same
args, assert result1.selectedLines equals result2.selectedLines, and then assert
the parser/mock was called exactly once (or that cache lookup was used) instead
of comparing endTime durations. Ensure the spy is restored/cleared after the
test.
| await expect(processor.initialize('test.ts')).resolves.not.toThrow(); | ||
| await expect(processor.initialize('test.js')).resolves.not.toThrow(); | ||
| await expect(processor.initialize('test.py')).resolves.not.toThrow(); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t use toThrow on a resolved promise
await expect(processor.initialize('test.ts')).resolves.not.toThrow() invokes the toThrow matcher on the resolved value (undefined), which immediately fails with “received value must be a function”. Assert the resolved value instead, e.g. await expect(processor.initialize('test.ts')).resolves.toBeUndefined();. Apply the same fix to the .js and .py variants.
🤖 Prompt for AI Agents
In tests/core/file/lineLimitProcessor.test.ts around lines 44 to 47, the test
uses await expect(...).resolves.not.toThrow(), which calls toThrow on the
resolved value (undefined) and fails; replace those three assertions with await
expect(processor.initialize('test.ts')).resolves.toBeUndefined(), await
expect(processor.initialize('test.js')).resolves.toBeUndefined(), and await
expect(processor.initialize('test.py')).resolves.toBeUndefined() so the resolved
promise value is asserted correctly.
| for (auto& user : users) { | ||
| if (user.getId() == id) { | ||
| return &user; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return nullptr; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove stray brace in C++ sample
The UserService::getUser sample has an extra closing brace before return nullptr;, making the snippet invalid C++. That introduces parse errors and defeats the purpose of exercising the strategy against well-formed code. Drop the stray brace so the function structure is valid. For example:
- }
- }
- return nullptr;
+ }
+ return nullptr;🤖 Prompt for AI Agents
In tests/core/file/lineLimitStrategies/AllLineLimitStrategies.test.ts around
lines 248 to 257, the C++ sample for UserService::getUser contains an extra
closing brace immediately before "return nullptr;" which makes the snippet
invalid; remove that stray closing brace so the function's braces match
correctly and the "return nullptr;" remains inside the function body (verify
surrounding braces still balance for the function and any enclosing
class/namespace).
| test('should identify headers when parsing fails', () => { | ||
| const lines = [ | ||
| 'require "json"', | ||
| 'require "net/http"', | ||
| 'class UserService', | ||
| ' def initialize', | ||
| ' @users = []', | ||
| ' end', | ||
| 'end', | ||
| ]; | ||
|
|
||
| // Mock parsing failure by using empty tree | ||
| const tree = parser.parse(''); | ||
| if (!tree) throw new Error('Failed to parse code'); | ||
| const headerLines = strategy.identifyHeaderLines(lines, tree); | ||
|
|
||
| expect(headerLines).toContain(0); // require | ||
| expect(headerLines).toContain(1); // require | ||
| expect(headerLines).toContain(2); // class UserService | ||
| expect(headerLines).toContain(3); // def initialize | ||
| }); | ||
|
|
||
| test('should analyze functions when parsing fails', () => { | ||
| const lines = [ | ||
| 'class TestClass', | ||
| ' def simple_method', | ||
| ' "simple"', | ||
| ' end', | ||
| ' ', | ||
| ' def complex_method(param)', | ||
| ' if param > 0', | ||
| ' (1..param).each { |i| puts i }', | ||
| ' end', | ||
| ' param', | ||
| ' end', | ||
| 'end', | ||
| ]; | ||
|
|
||
| // Mock parsing failure by using empty tree | ||
| const tree = parser.parse(''); | ||
| if (!tree) throw new Error('Failed to parse code'); | ||
| const functions = strategy.analyzeFunctions(lines, tree); | ||
|
|
||
| expect(functions).toHaveLength(2); | ||
|
|
||
| const simpleMethod = functions.find((f) => f.name === 'simple_method'); | ||
| expect(simpleMethod).toBeDefined(); | ||
|
|
||
| const complexMethod = functions.find((f) => f.name === 'complex_method'); | ||
| expect(complexMethod).toBeDefined(); | ||
| expect(complexMethod!.complexity).toBeGreaterThan(0.5); | ||
| }); | ||
|
|
||
| test('should identify footers when parsing fails', () => { | ||
| const lines = [ | ||
| 'require "json"', | ||
| 'class Application', | ||
| ' def run', | ||
| ' puts "Running..."', | ||
| ' end', | ||
| 'end', | ||
| '', | ||
| 'app = Application.new', | ||
| 'app.run', | ||
| ]; | ||
|
|
||
| // Mock parsing failure by using empty tree | ||
| const tree = parser.parse(''); | ||
| if (!tree) throw new Error('Failed to parse code'); | ||
| const footerLines = strategy.identifyFooterLines(lines, tree); | ||
|
|
||
| expect(footerLines).toContain(9); // app = Application.new | ||
| expect(footerLines).toContain(10); // app.run | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fallback tests never hit the heuristic path
These “Fallback Heuristics” tests still feed a perfectly valid (albeit empty) tree from parser.parse(''), so identifyHeaderLines/analyzeFunctions/identifyFooterLines never throw, the catch block is never entered, and the heuristic helpers aren’t exercised. As written the expectations will fail because each method returns []. Force the error path (e.g. by passing a mock tree whose language access throws or by spying on the helpers and calling them directly) before asserting heuristic behaviour.
| await expect(runCli([tempDir], tempDir, { line: 10 })).resolves.not.toThrow(); | ||
|
|
||
| // The test passes if no error is thrown during CLI processing | ||
| }); | ||
|
|
||
| test('should reject invalid line limit values', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");'); | ||
|
|
||
| // Test with negative line limit | ||
| await expect(runCli([tempDir], tempDir, { line: -5 })).rejects.toThrow(RepomixError); | ||
|
|
||
| // Test with zero line limit | ||
| await expect(runCli([tempDir], tempDir, { line: 0 })).rejects.toThrow(RepomixError); | ||
|
|
||
| // Test with non-integer line limit (this should be caught by Commander.js validation) | ||
| // We can't directly test this case here since Commander.js validates before our code runs | ||
| }); | ||
|
|
||
| test('should handle line limit with different output formats', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Line 1");\n'.repeat(15)); | ||
|
|
||
| // Test with XML output | ||
| await expect(runCli([tempDir], tempDir, { line: 10, style: 'xml' })).resolves.not.toThrow(); | ||
|
|
||
| // Test with Markdown output | ||
| await expect(runCli([tempDir], tempDir, { line: 10, style: 'markdown' })).resolves.not.toThrow(); | ||
|
|
||
| // Test with Plain output | ||
| await expect(runCli([tempDir], tempDir, { line: 10, style: 'plain' })).resolves.not.toThrow(); | ||
|
|
||
| // Test with JSON output | ||
| await expect(runCli([tempDir], tempDir, { line: 10, style: 'json' })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with other CLI options', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with verbose mode | ||
| await expect(runCli([tempDir], tempDir, { line: 10, verbose: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with copy to clipboard | ||
| await expect(runCli([tempDir], tempDir, { line: 10, copy: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with remove comments | ||
| await expect(runCli([tempDir], tempDir, { line: 10, removeComments: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with remove empty lines | ||
| await expect(runCli([tempDir], tempDir, { line: 10, removeEmptyLines: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with compress | ||
| await expect(runCli([tempDir], tempDir, { line: 10, compress: true })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with file selection options', async () => { | ||
| // Create multiple test files | ||
| await fs.writeFile(path.join(tempDir, 'test1.js'), 'console.log("Test 1");\n'.repeat(20)); | ||
| await fs.writeFile(path.join(tempDir, 'test2.ts'), 'console.log("Test 2");\n'.repeat(20)); | ||
| await fs.writeFile(path.join(tempDir, 'test3.md'), '# Test 3\n'.repeat(20)); | ||
|
|
||
| // Test with include pattern | ||
| await expect(runCli([tempDir], tempDir, { line: 10, include: '*.js' })).resolves.not.toThrow(); | ||
|
|
||
| // Test with ignore pattern | ||
| await expect(runCli([tempDir], tempDir, { line: 10, ignore: '*.md' })).resolves.not.toThrow(); | ||
|
|
||
| // Test with no gitignore | ||
| await expect(runCli([tempDir], tempDir, { line: 10, gitignore: false })).resolves.not.toThrow(); | ||
|
|
||
| // Test with no default patterns | ||
| await expect(runCli([tempDir], tempDir, { line: 10, defaultPatterns: false })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with output options', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with custom output file | ||
| const outputFile = path.join(tempDir, 'custom-output.xml'); | ||
| await expect(runCli([tempDir], tempDir, { line: 10, output: outputFile })).resolves.not.toThrow(); | ||
|
|
||
| // Test with stdout | ||
| await expect(runCli([tempDir], tempDir, { line: 10, stdout: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with show line numbers | ||
| await expect(runCli([tempDir], tempDir, { line: 10, outputShowLineNumbers: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with parsable style | ||
| await expect(runCli([tempDir], tempDir, { line: 10, parsableStyle: true })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with security options', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with security check disabled | ||
| await expect(runCli([tempDir], tempDir, { line: 10, securityCheck: false })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with git options', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with no git sort by changes | ||
| await expect(runCli([tempDir], tempDir, { line: 10, gitSortByChanges: false })).resolves.not.toThrow(); | ||
|
|
||
| // Test with include diffs | ||
| await expect(runCli([tempDir], tempDir, { line: 10, includeDiffs: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with include logs | ||
| await expect(runCli([tempDir], tempDir, { line: 10, includeLogs: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with include logs count | ||
| await expect(runCli([tempDir], tempDir, { line: 10, includeLogsCount: 25 })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with token count options', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with token count encoding | ||
| await expect(runCli([tempDir], tempDir, { line: 10, tokenCountEncoding: 'cl100k_base' })).resolves.not.toThrow(); | ||
|
|
||
| // Test with token count tree | ||
| await expect(runCli([tempDir], tempDir, { line: 10, tokenCountTree: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with token count tree threshold | ||
| await expect(runCli([tempDir], tempDir, { line: 10, tokenCountTree: 100 })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with top files length option', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with custom top files length | ||
| await expect(runCli([tempDir], tempDir, { line: 10, topFilesLen: 20 })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with header text option', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with custom header text | ||
| await expect(runCli([tempDir], tempDir, { line: 10, headerText: 'Custom Header Text' })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with instruction file path option', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| const instructionFile = path.join(tempDir, 'instructions.txt'); | ||
| await fs.writeFile(instructionFile, 'Custom instructions for AI'); | ||
|
|
||
| // Test with instruction file path | ||
| await expect(runCli([tempDir], tempDir, { line: 10, instructionFilePath: instructionFile })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with directory structure options', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with include empty directories | ||
| await expect(runCli([tempDir], tempDir, { line: 10, includeEmptyDirectories: true })).resolves.not.toThrow(); | ||
|
|
||
| // Test with include full directory structure | ||
| await expect(runCli([tempDir], tempDir, { line: 10, includeFullDirectoryStructure: true })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with truncate base64 option', async () => { | ||
| const testFile = path.join(tempDir, 'test.js'); | ||
| await fs.writeFile(testFile, 'console.log("Hello World");\n'.repeat(20)); | ||
|
|
||
| // Test with truncate base64 | ||
| await expect(runCli([tempDir], tempDir, { line: 10, truncateBase64: true })).resolves.not.toThrow(); | ||
| }); | ||
|
|
||
| test('should handle line limit with multiple directories', async () => { | ||
| // Create multiple directories with test files | ||
| const dir1 = path.join(tempDir, 'dir1'); | ||
| const dir2 = path.join(tempDir, 'dir2'); | ||
|
|
||
| await fs.mkdir(dir1, { recursive: true }); | ||
| await fs.mkdir(dir2, { recursive: true }); | ||
|
|
||
| await fs.writeFile(path.join(dir1, 'test1.js'), 'console.log("Dir 1");\n'.repeat(20)); | ||
| await fs.writeFile(path.join(dir2, 'test2.js'), 'console.log("Dir 2");\n'.repeat(20)); | ||
|
|
||
| // Test with multiple directories | ||
| await expect(runCli([dir1, dir2], tempDir, { line: 10 })).resolves.not.toThrow(); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix async assertion: resolves.not.toThrow() will always fail
Each await expect(runCli(...)).resolves.not.toThrow(); call is invalid because toThrow expects a function. When the promise resolves, Jest/Vitest passes the resolved value (undefined) into toThrow, which immediately errors with “received value must be a function,” so these tests will never pass. Swap to either asserting on the resolved value or wrapping the call:
- await expect(runCli([tempDir], tempDir, { line: 10 })).resolves.not.toThrow();
+ await expect(runCli([tempDir], tempDir, { line: 10 })).resolves.toBeUndefined();Apply the same pattern everywhere you’re using .resolves.not.toThrow().
🤖 Prompt for AI Agents
In tests/integration-tests/cliLineLimit.test.ts around lines 45-236, all uses of
await expect(runCli(...)).resolves.not.toThrow() are invalid because toThrow
expects a function; replace each .resolves.not.toThrow() with
.resolves.toBeUndefined() (or simply await runCli(...) directly) so the promise
resolution is asserted correctly; apply this change to every occurrence in the
file while leaving rejects.toThrow() checks untouched.
|
these reviews with AI are really good!! working on those changes |
|
I'll work on it when i get some time |
This pull request introduces a new line limiting feature to Repomix, allowing users to control the number of lines included from each file in the output. This is particularly useful for managing AI context windows, reducing processing costs, and focusing on the most relevant code sections in large repositories.
Key Features
--lineCLI option, therepomix.config.jsonfile, or theREPOMIX_LINE_LIMITenvironment variable.README.mdand website documentation have been updated with a comprehensive guide on how to use the new line limiting feature.How to Use
CLI:
# Limit each file to 100 lines repomix --line 100 ./srcConfiguration File (
repomix.config.json):{ "output": { "lineLimit": 100 } }Environment Variable:
export REPOMIX_LINE_LIMIT=100 repomix ./srcChecklist
npm run testnpm run lint