Skip to content

Split output: every part's Directory Structure shows the whole repo tree, not the part's files #1815

Description

@wallidsaydi-creator

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; done

repomix.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.txtc/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:288 builds filePathsByRoot from all repository files (with the default filePathStyle: 'target-relative', usesRootLabels() is always true, so this mapping is always constructed — single root included).
  • src/core/output/outputSplit.ts (renderGroups) passes that whole-repo filePathsByRoot unchanged into generateOutput for every chunk.
  • src/core/output/outputGenerate.ts:428 prefers filePathsByRoot over the chunk-scoped filePathsForTree when 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.txt tree = a/, b/ only; Files = a/*, b/* (0 references to c/).
    • out.2.txt tree = c/, repomix.config.json; Files = c/*, repomix.config.json.
  • npx vitest run tests/core tests/config101 files, 1114 tests, all passing (includes tests/core/packager/splitOutput.test.ts; note the existing test mocks produceOutput, so it does not cover per-chunk tree contents — the above fixture does).
  • tsc --noEmit clean.
  • 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 rootLabel handling in generateTreeStringWithRoots is 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions