Split output: every part's Directory Structure shows the whole repo tree, not the part's files
Description
In split-output mode (output.splitOutput), every generated part renders the full repository tree in its Directory Structure section, even though each part only contains a subset of the files. The tree and the Files section are inconsistent within every part except the last-assembled grouping.
Verified on repomix 1.18.0 (published npm CLI) and on main (72807df).
Why it matters: split output exists so each part can be fed to an LLM independently. When part N's tree lists files that are not in part N, the model sees "listed in the directory structure but missing from Files" — it will either report files as missing, or (worse) hallucinate contents for paths it was told exist. This silently degrades the core use case of the feature.
Reproduction
Fixture — 9 small files in a/, b/, c/ plus the config itself:
mkdir -p a b c
for d in a b c; do for i in 1 2 3; do printf 'content-%s-%s padding padding padding\n' "$d" "$i" > $d/file$i.txt; done; donerepomix.config.json:
{
"output": {
"filePath": "out.txt",
"style": "plain",
"splitOutput": 3000,
"fileSummary": true,
"directoryStructure": true,
"files": true
}
}Run repomix --quiet → produces out.1.txt and out.2.txt.
Actual (v1.18.0):
out.2.txt Directory Structure:
a/
file1.txt
file2.txt
file3.txt
b/
file1.txt
file2.txt
file3.txt
c/
file1.txt
file2.txt
file3.txt
repomix.config.json…while its Files section contains only c/file1.txt … c/file3.txt and repomix.config.json. Same inversion on out.1.txt (tree lists c/, files don't include it).
Expected: each part's Directory Structure covers exactly the files included in that part (part 1: a/, b/; part 2: c/, repomix.config.json).
Root cause
src/core/packager.ts:288buildsfilePathsByRootfrom all repository files (with the defaultfilePathStyle: 'target-relative',usesRootLabels()is always true, so this mapping is always constructed — single root included).src/core/output/outputSplit.ts(renderGroups) passes that whole-repofilePathsByRootunchanged intogenerateOutputfor every chunk.src/core/output/outputGenerate.ts:428prefersfilePathsByRootover the chunk-scopedfilePathsForTreewhen generating the tree:
let treeString: string;
if (filePathsByRoot) {
treeString = generateTreeStringWithRoots(filePathsByRoot, directoryPathsForTree);
} else {
treeString = generateTreeString(filePathsForTree, directoryPathsForTree);
}So the per-chunk scoping computed in buildOutputGeneratorContext is bypassed whenever filePathsByRoot is present — which is always, under the default config.
Fix
Scope filePathsByRoot to the chunk's own files in renderGroups before calling generateOutput:
--- a/src/core/output/outputSplit.ts
+++ b/src/core/output/outputSplit.ts
@@ -152,6 +152,18 @@ const renderGroups = async (
const chunkAllFilePaths = groupsToRender.flatMap((g) => g.allFilePaths);
const chunkConfig = makeChunkConfig(baseConfig, partIndex);
+ // Scope filePathsByRoot (used for the multi-root tree) to this chunk's files.
+ // The caller passes the whole-repo mapping, and generateOutput prefers it over
+ // filePathsForTree when building the Directory Structure — unscoped, every
+ // split part would render the full repository tree while only containing a
+ // subset of the files.
+ const chunkFilePathSet = new Set(chunkAllFilePaths);
+ const chunkFilePathsByRoot = filePathsByRoot
+ ? filePathsByRoot
+ .map(({ rootLabel, files }) => ({ rootLabel, files: files.filter((file) => chunkFilePathSet.has(file)) }))
+ .filter(({ files }) => files.length > 0)
+ : undefined;
+
return await generateOutput(
rootDirs,
chunkConfig,
@@ -159,7 +171,7 @@ const renderGroups = async (
chunkAllFilePaths,
partIndex === 1 ? gitDiffResult : undefined,
partIndex === 1 ? gitLogResult : undefined,
- filePathsByRoot,
+ chunkFilePathsByRoot,
emptyDirPaths,
);
};Verification
- Reproduced on stock v1.18.0 CLI (npx) — wrong tree in both parts.
- Applied the patch, rebuilt (
npm run build), re-ran the same fixture:out.1.txttree =a/,b/only; Files =a/*,b/*(0 references toc/).out.2.txttree =c/,repomix.config.json; Files =c/*,repomix.config.json.
npx vitest run tests/core tests/config→ 101 files, 1114 tests, all passing (includestests/core/packager/splitOutput.test.ts; note the existing test mocksproduceOutput, so it does not cover per-chunk tree contents — the above fixture does).tsc --noEmitclean.- Single-file groups still hit the "cannot split further" error path correctly (subdivide → per-file groups unchanged).
- Multi-root labeled trees keep their labels: each root's file list is filtered, not re-derived, so
rootLabelhandling ingenerateTreeStringWithRootsis untouched.
One behavior note: filePathsByRoot mapping is per-root; roots whose files all land in other parts are dropped entirely (.filter(({ files }) => files.length > 0)), so no empty labeled sections appear.
Found while doing a docs-vs-behavior drift check on context-packing tools (I run a small $5 "FreshContext Pack" service that does exactly this kind of repo-vs-claims verification: https://deploy-foorge-team.vercel.app). Happy to open a PR if the fix direction looks right.
Source: yamadashy/repomix