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
.ts.tsx.py.md.json.css.go.rs.sql+86

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.
fileconcat-output.txt
≈ 412 tokens
# 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 -p without parsing noise.
  • parseOpt in with --parse and the CLI pulls plain text out of PDFs, Word, Excel, slides, and the ODF family.
  • json--json prints a single-line summary your wrapper can JSON.parse at end of run.
zsh · file-concat
stdout + stderr
$ file-concat ./service --parse pdf,docx --json -o ctx.xml
stderr
Processing: /Users/em/service
Output: ctx.xml (xml)
Parse mode: pdf, docx
Found 47 files
Processing 45 files (parsed 3, skipped 2)
Done in 1.83s
Output written to: ctx.xml
stdout
{"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.

  • PDF
  • 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.

bundle for the modelctx.xml · what your prompt embeds
<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>
summary for your script--json on stdout, one line
{
  "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.
Paraphrasing Anthropic's prompt engineering guidance, use XML tags to structure your prompts. The CLI ships the XML wrapper by default so multi-file prompts stay well-tagged even when scripts assemble them.

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:

NetworkConsoleSourcesElementsrecording
NameStatusTypeSizeSource
fileconcat.com200document3.2 KBpage load
index.css200stylesheet18.4 KBpage load
main.js200script82.1 KBpage load
tiktoken.wasm200wasm421 KBpage load
14:02:31you drop 47 files
0 outbound requests
silent. processing happens locally.

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.

Open the tool

Then: file-concat ./your-folder