npm.io
1.0.1 • Published 16h agoCLI

@coder-dao/contextforge

Licence
Version
1.0.1
Deps
3
Size
67 kB
Vulns
0
Weekly
0

Context Forge

Context Forge (contextforge) turns a code repository into one AI-ready Markdown file named repository-context.md.

It is built for quickly giving an AI assistant the shape and source content of a repository without copying files one by one. The generated Markdown starts with a repository structure tree, then lists each included file as a ## File: path/to/file section with fenced code blocks.

Highlights

  • Generates repository-context.md by default.
  • Uses gpt-tokenizer with the o200k_base tokenizer for token counts.
  • Keeps the config simple: include files, exclude files, exclude directories, exclude extensions, and directory-structure behavior.
  • Lets excluded files still appear in the repository tree while omitting their content to save AI context tokens.
  • Lets includeFiles override content exclusions for specific files or subdirectories.
  • Removes empty lines by default to reduce output size.

Development setup

pnpm install
pnpm build
pnpm link --global
contextforge

During development, you can also run the CLI directly without linking:

pnpm dev

CLI usage

Create a repository context file in the current directory:

contextforge

Create a context file for another directory:

contextforge ./apps/web

Use a custom config path:

contextforge --config ./contextforge.config.json

Override the output path for one run:

contextforge --output ./ai/repository-context.md

Suppress the success summary:

contextforge --quiet

Initialize config

Create a default contextforge.config.json:

contextforge init

Overwrite an existing config:

contextforge init --force

CLI output

A successful run prints a readable summary like this:

Context Forge
Forged repository context successfully.
──────────────────────────────────────
Total Files:      128
Total Tokens:     1,35,342
Total Characters: 1,71,600
File Extensions:  .json (8), .md (5), .ts (64), .tsx (51)
Output Markdown:  /path/to/repository-context.md
Time Taken:       1.24s

Generated Markdown format

The output is intentionally close to Repomix's Markdown structure: summary, repository structure, then file sections.

# Repository Structure

```text
.
├── README.md
├── package.json
└── src/
    └── index.ts
```

# Repository Files

## File: README.md

```markdown
# Example
```

## File: src/index.ts

```typescript
export function hello() {
  return "world";
}
```

Default config

contextforge init creates this default shape:

{
  "output": "repository-context.md",
  "removeEmptyLines": true,
  "includeFiles": [".env.example", "**/.env.example"],
  "excludeFiles": [".DS_Store", "**/.DS_Store"],
  "excludeDirectories": ["node_modules", ".git", ".next", "dist", "coverage"],
  "excludeFileExtensions": [".png", ".jpg", ".pdf", ".zip", ".woff2"],
  "directoryStructure": {
    "includeIgnored": true,
    "excludePatterns": ["node_modules/**", ".git/**", "dist/**"]
  }
}

The real generated config includes the full default exclude lists.

Config options

Option Type Default What it does
output string repository-context.md Path for the generated Markdown file. Relative paths are resolved from the target repository directory.
removeEmptyLines boolean true Removes blank lines from included file contents before writing Markdown. This reduces token usage while preserving non-empty source lines.
includeFiles string[] [".env.example", "**/.env.example"] Glob patterns that force file contents to be included. These patterns override excludeFiles, excludeDirectories, and excludeFileExtensions.
excludeFiles string[] Common env, lock, log, OS, and generated file patterns File glob patterns whose contents should not be included. Files may still appear in the repository tree when directoryStructure.includeIgnored is true.
excludeDirectories string[] Common dependency, build, cache, coverage, and metadata folders Directory names or paths whose file contents should not be included. A value like tests matches any folder named tests; a value like drizzle/meta matches that path and everything below it.
excludeFileExtensions string[] Common image, archive, audio, video, font, and PDF extensions File extensions whose contents should not be included. These files can still appear in the repository tree.
directoryStructure.includeIgnored boolean true When true, files excluded from content can still be listed in the repository tree. This is useful for test files, generated files, or binary assets where paths are useful but contents are too noisy.
directoryStructure.excludePatterns string[] Noisy folders like node_modules/**, .git/**, dist/** Hard skips for the repository tree traversal. Use this for directories you do not want scanned or shown at all.

Glob behavior

  • Patterns use forward slashes, even on Windows.
  • Dotfiles are supported.
  • File patterns without slashes, like *.log, match by basename.
  • Directory patterns without slashes, like tests, match any path segment named tests.
  • Inclusion wins over content exclusion. includeFiles is the override mechanism.
  • directoryStructure.excludePatterns is a hard skip and is meant for very large or noisy paths.

Example: keep tests in the tree, omit test contents

Use this when you want the AI to know that tests exist, but you do not want to spend tokens on test content.

{
  "output": "repository-context.md",
  "removeEmptyLines": true,
  "includeFiles": [".env.example", "**/.env.example"],
  "excludeFiles": [
    "**/*.test.ts",
    "**/*.test.tsx",
    "**/*.spec.ts",
    "**/*.spec.tsx"
  ],
  "excludeDirectories": ["node_modules", ".git", "dist", "coverage"],
  "excludeFileExtensions": [
    ".png",
    ".jpg",
    ".jpeg",
    ".gif",
    ".webp",
    ".pdf",
    ".zip"
  ],
  "directoryStructure": {
    "includeIgnored": true,
    "excludePatterns": ["node_modules/**", ".git/**", "dist/**", "coverage/**"]
  }
}

With this config, src/button.test.ts appears under # Repository Structure, but it does not get a ## File: src/button.test.ts content section.

Example: exclude a whole directory, but include one file inside it

This is useful when a directory is mostly generated or noisy, but one file is important.

{
  "excludeDirectories": ["legacy"],
  "includeFiles": ["legacy/README.md", "legacy/src/public-api.ts"],
  "directoryStructure": {
    "includeIgnored": true,
    "excludePatterns": ["node_modules/**", ".git/**", "dist/**"]
  }
}

legacy/README.md and legacy/src/public-api.ts are included with content because includeFiles overrides excludeDirectories.

Example: include .env.example, but never include real env files

The default config already does this:

{
  "includeFiles": [".env.example", "**/.env.example"],
  "excludeFiles": [".env", ".env.*", "**/.env", "**/.env.*"]
}

.env.example is included, but .env, .env.local, and nested env files are omitted from content.

Example: hide noisy folders from both the tree and file contents

Add noisy paths to directoryStructure.excludePatterns when you do not even want them scanned or listed.

{
  "excludeDirectories": [
    "node_modules",
    ".git",
    "dist",
    "coverage",
    "storybook-static"
  ],
  "directoryStructure": {
    "includeIgnored": true,
    "excludePatterns": [
      "node_modules/**",
      ".git/**",
      "dist/**",
      "coverage/**",
      "storybook-static/**"
    ]
  }
}

Programmatic usage

import { generateContext } from "contextforge";

const result = await generateContext({
  targetDirectory: process.cwd(),
});

console.log(result.stats.totalTokens);
console.log(result.outputPath);

Notes on binary files

Binary-looking files are never written as content blocks. If they are not hidden by directoryStructure.excludePatterns, they can still appear in the repository tree so the AI can understand that assets exist without consuming binary data.

Troubleshooting

contextforge command not found after linking

Run the build first, then link globally:

pnpm build
pnpm link --global
A file is excluded even though I need it

Add it to includeFiles:

{
  "includeFiles": ["path/to/important-file.ts"]
}
A folder is not visible in the tree

Check directoryStructure.excludePatterns. Those patterns are hard skips.

The output includes too many files

Add specific patterns to excludeFiles, excludeDirectories, or excludeFileExtensions. For very large folders, also add the path to directoryStructure.excludePatterns.