Drop a folder. Get one file your AI can actually read.
Project tree at the top, every file in a labeled block. The model gets the structure before any file body.
- no upload
- no account
- no server
- no telemetry
Drop files or a folder
- Code, docs, configs
- Binaries, lockfiles, build output
What the model receives.
Files arrive wrapped, tagged, and ordered for parsing. The model gets the file tree first, then each file in a labeled block with its language and path. It navigates the bundle the way you'd navigate the repo.
- treeOne ASCII map at the top so the model knows what it's reading before any file body.
- tagsEach file wrapped in
<file path>so references stay unambiguous. - fencesLanguage-typed code fences. Syntax highlighting carries through to whatever renders the response.
# Project Structure
```
└── app/
├── hooks/use-theme.ts
├── components/button.tsx
└── README.md
```
# File Contents
<file path="app/hooks/use-theme.ts">
```typescript
import { useEffect, useState } from "react";
export function useTheme() {
const [theme, setTheme] = useState("light");
return { theme, setTheme };
}
```
</file>When you reach for it.
The moment looks the same across very different work.
You have files scattered across folders, a clear question for the model, and no patience for shuttle-copying one file at a time.
Whether that's a repo headed for code review, paper sections for a synthesis pass, markdown chapters you're editing across, or an assignment built from many files.
Same engine, in your terminal.
Install once and pipe context straight into the model. Stdout carries the artifact, stderr carries progress, and a one-line JSON summary is one flag away.
$ npm install -g @fileconcat/cli- pipeStdout is the artifact, stderr is progress. Drop it into
| claude -pwithout parsing noise. - parseOpt in with --parse and the CLI pulls plain text out of PDFs, Word, Excel, slides, and the ODF family.
- json
--jsonprints a single-line summary your wrapper canJSON.parseat end of run.
{"files":45,"parsed":3,"skipped":2,"skippedBreakdown":{"oversize":0,"binary":2,"readError":0,"parseFailed":0},"totalBytes":184320,"outputPath":"ctx.xml","elapsedSeconds":1.83,"style":"xml"}
With --parse, these documents enter the bundle as plain text.
- DOCX
- XLSX
- PPTX
- ODT
- ODS
- ODP
One run, two outputs.
The bundle goes to the model in structured XML the model can navigate. The summary goes to your script as JSON your wrapper can parse. They ride on stdout together with stderr carrying progress.
<codebase project="service" generator="fileconcat">
<directory_structure>
└── api/
├── handlers.ts
└── spec.pdf
</directory_structure>
<files>
<file path="api/handlers.ts" language="typescript">
// route definitions
export function GET() { … }
</file>
<file path="api/spec.pdf" language="text">
# Service API spec
extracted from spec.pdf via --parse
</file>
</files>
</codebase>{
"files": 45,
"parsed": 3,
"skipped": 2,
"skippedBreakdown": {
"oversize": 0,
"binary": 2,
"parseFailed": 0
},
"totalBytes": 184320,
"outputPath": "ctx.xml",
"elapsedSeconds": 1.83,
"style": "xml"
}Wrapping multi-file context in XML tags helps the model keep its bearings across long context, especially when extracted spec text and source code sit in the same prompt.
Pull the plug. It keeps working.
FileConcat loads once and then runs offline. Drop files with your WiFi off. The bundle still comes out the other side. Here's what a fresh session looks like in the network panel:
Open DevTools, watch the Network tab, then drop a folder. Four files at page load, then silence. That's the whole story.
Drop a folder. Pipe a directory. Same output.
Then: file-concat ./your-folder

