Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions .cursor/commands/ask-codefetch-pro.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
---
description: Generate codebase with codefetch, copy to clipboard, and open AI chat
argument-hint: [PROMPT=<task-description>]
---

# Ask Codefetch Pro

Generate codebase, copy to clipboard, open AI chat.
Expand All @@ -17,18 +22,6 @@ npx codefetch open --max-tokens 50000 \
--project-tree 3 \
--include-files "src/**/*.ts"

# Gemini
npx codefetch open --chat-url gemini.google.com --chat-model gemini-3.0 \
--max-tokens 50000 --include-files "src/**/*.ts"

# Claude
npx codefetch open --chat-url claude.ai --chat-model claude-3.5-sonnet \
--max-tokens 50000 --include-files "src/**/*.ts"

# Copy only (no browser)
npx codefetch open --no-browser --max-tokens 50000 --include-files "src/**/*.ts"
```

## Options

Chat:
Expand Down
33 changes: 33 additions & 0 deletions .cursor/commands/ask-codefetch-quick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
description: Quick codefetch workflow - run help first, then generate and open AI chat
argument-hint: [PROMPT=<task-description>]
---

# Ask Codefetch Quick

Generate codebase, copy to clipboard, open AI chat.

## Workflow

1. Help: run `codefetch help` to see all available options
2. Research: identify relevant files/directories for PROMPT
3. Run: codefetch open with scoped files → copies to clipboard, opens browser
4. Paste: codebase in clipboard, paste into AI chat

## Step 1: Get Help

```bash
npx codefetch help
npx codefetch open --help
```

## Step 2: Run

```bash
npx codefetch open --max-tokens 50000 \
--project-tree 3 \
--include-files "src/**/*.ts" \
-p prompt.md
```

Adjust options based on PROMPT requirements.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 2.2.0

### Added
- **XML-structured output format** - Output now uses semantic XML tags for better AI parsing:
- `<task>...</task>` - Wraps the prompt/instructions
- `<filetree>...</filetree>` - Wraps the project tree structure
- `<source_code>...</source_code>` - Wraps all source code files
- **Additive `--include-dir` and `--include-files`** - These options now work together additively instead of being mutually exclusive. Use both to include specific directories PLUS specific files.
- **External prompt file support** - Prompt files with paths (e.g., `-p docs/arch/prompt.md`) are now correctly resolved from the project root instead of requiring them to be in `codefetch/prompts/`

### Fixed
- Fixed `--include-dir` and `--include-files` being mutually exclusive - now they combine additively
- Fixed external prompt file paths not being found when containing directory separators
- Fixed prompts without `{{CURRENT_CODEBASE}}` placeholder not including the codebase content

## 2.1.2

### Fixed
Expand Down
7 changes: 7 additions & 0 deletions HOW-TO-RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ Delete the GitHub Release if needed: `gh release delete vX.Y.Z`
- `npm ERR! code E403` or auth failures: run `npm login` and retry
- `gh` failures: `gh auth status`; ensure `repo` scope exists
- Tag push rejected: pull/rebase or fast-forward `main`, then rerun
- **CI fails with `ERR_PNPM_OUTDATED_LOCKFILE`**: The lockfile is out of sync with `package.json`. This happens when dependencies change (e.g., `workspace:*` → `^2.1.0`). Fix it locally:
```bash
pnpm install --no-frozen-lockfile
git add pnpm-lock.yaml
git commit -m "chore: update pnpm-lock.yaml"
git push
```

## Release Frequency Suggestions

Expand Down
55 changes: 52 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ npx codefetch --include-files "src/components/AgentPanel.tsx,src/lib/llm/**/*" -

# Include src directory, exclude test files
npx codefetch --include-dir src --exclude-files "*.test.ts" -o src-no-tests.md

# Combine --include-dir and --include-files (additive!)
# This includes ALL files from crates/core/src PLUS the specific lib.rs file
npx codefetch --include-dir crates/core/src --include-files "crates/engine/src/lib.rs" -o combined.md
```

Dry run (only output to console)
Expand Down Expand Up @@ -289,10 +293,20 @@ Inline prompts are automatically appended with the codebase content.

#### Custom Prompt Files

Create custom prompts in `codefetch/prompts/` directory:
You can use custom prompt files in two ways:

1. Create a markdown file (e.g., `codefetch/prompts/my-prompt.md`)
2. Use it with `--prompt my-prompt.md`
**1. External prompt files (anywhere in your project):**
```bash
# Use a prompt file from anywhere in your project
npx codefetch -p docs/arch/review-prompt.md
npx codefetch --prompt ./prompts/security-audit.txt
```

**2. Prompt files in `codefetch/prompts/` directory:**
```bash
# Create codefetch/prompts/my-prompt.md, then use:
npx codefetch --prompt my-prompt.md
```

You can also set a default prompt in your `codefetch.config.mjs`:

Expand Down Expand Up @@ -344,6 +358,41 @@ Codefetch uses a set of default ignore patterns to exclude common files and dire

You can view the complete list of default patterns in [default-ignore.ts](packages/sdk/src/default-ignore.ts).

## Output Format

Codefetch generates structured output using semantic XML tags to help AI models better understand the different sections:

```xml
<task>
Your prompt or instructions here...
</task>

<filetree>
Project Structure:
└── src
├── index.ts
└── utils
└── helpers.ts
</filetree>

<source_code>
src/index.ts
```typescript
// Your code here
```

src/utils/helpers.ts
```typescript
// More code here
```
</source_code>
```

The XML structure provides:
- `<task>` - Contains your prompt/instructions (from `-p` flag)
- `<filetree>` - Contains the project tree visualization (from `-t` flag)
- `<source_code>` - Contains all the source code files with their paths

## Token Counting

Codefetch supports different token counting methods to match various AI models:
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## 2.2.0

### Added
- **XML-structured output format** - Output now uses semantic XML tags for better AI parsing:
- `<task>...</task>` - Wraps the prompt/instructions
- `<filetree>...</filetree>` - Wraps the project tree structure
- `<source_code>...</source_code>` - Wraps all source code files
- **External prompt file support** - Prompt files with paths (e.g., `-p docs/arch/prompt.md`) are now correctly resolved from the project root

### Fixed
- Fixed `getPromptFile` not resolving external file paths correctly when they contain directory separators

## 2.1.2

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codefetch",
"version": "2.1.2",
"version": "2.2.0",
"description": "Fetches all files in the current directory and outputs them in a Markdown file.",
"repository": "regenrek/codefetch",
"license": "MIT",
Expand Down Expand Up @@ -41,7 +41,7 @@
"dependencies": {
"@clack/prompts": "^0.11.0",
"c12": "^2.0.1",
"codefetch-sdk": "^2.1.0",
"codefetch-sdk": "workspace:*",
"consola": "^3.3.3",
"ignore": "^7.0.0",
"mri": "^1.2.0",
Expand Down
62 changes: 62 additions & 0 deletions packages/cli/package.json.backup
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"name": "codefetch",
"version": "2.2.0",
"description": "Fetches all files in the current directory and outputs them in a Markdown file.",
"repository": "regenrek/codefetch",
"license": "MIT",
"type": "module",
"sideEffects": false,
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"bin": {
"codefetch": "./dist/cli.mjs"
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.cts",
"files": [
"dist"
],
"scripts": {
"build": "unbuild",
"dev": "vitest",
"lint": "eslint --cache . && prettier -c 'src/**/*.{ts,tsx,js,mjs,cjs}' 'test/**/*.{ts,js}' '!test/fixtures/**'",
"start": "tsx src/cli.ts",
"lint:fix": "eslint --cache . --fix && prettier -c 'src/**/*.{ts,tsx,js,mjs,cjs}' 'test/**/*.{ts,js}' '!test/fixtures/**' -w",
"test": "npm run lint && npm run test:types && vitest run --coverage",
"test:types": "tsc --noEmit --skipLibCheck",
"cli": "node ./dist/cli.mjs"
},
"dependencies": {
"@clack/prompts": "^0.11.0",
"c12": "^2.0.1",
"codefetch-sdk": "workspace:*",
"consola": "^3.3.3",
"ignore": "^7.0.0",
"mri": "^1.2.0",
"pathe": "^2.0.1"
},
"devDependencies": {
"@types/node": "^22.10.5",
"@vitest/coverage-v8": "^2.1.8",
"eslint": "^9.18.0",
"eslint-config-unjs": "^0.4.2",
"msw": "^2.10.4",
"prettier": "^3.4.2",
"tsx": "^4.19.2",
"typescript": "^5.7.3",
"unbuild": "3.2.0",
"vitest": "^2.1.8"
}
}
9 changes: 9 additions & 0 deletions packages/cli/src/commands/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ function getPromptFile(
if (VALID_PROMPTS.has(config.defaultPromptFile)) {
return config.defaultPromptFile;
}
// Check if it's an external file path (contains path separator or is absolute)
// External paths should be used as-is, not nested under codefetch/prompts/
if (
config.defaultPromptFile.includes("/") ||
config.defaultPromptFile.includes("\\") ||
config.defaultPromptFile.startsWith(".")
) {
return resolve(config.defaultPromptFile);
}
return resolve(config.outputPath, "prompts", config.defaultPromptFile);
}

Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/commands/open.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ function getPromptFile(
if (VALID_PROMPTS.has(config.defaultPromptFile)) {
return config.defaultPromptFile;
}
// Check if it's an external file path (contains path separator or is absolute)
// External paths should be used as-is, not nested under codefetch/prompts/
if (
config.defaultPromptFile.includes("/") ||
config.defaultPromptFile.includes("\\") ||
config.defaultPromptFile.startsWith(".")
) {
return resolve(config.defaultPromptFile);
}
return resolve(config.outputPath, "prompts", config.defaultPromptFile);
}

Expand Down
83 changes: 83 additions & 0 deletions packages/cli/test/integration/codebase-fixture.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,87 @@ describe("Integration: codebase-test fixture", () => {
expect(content).toContain("button.js");
expect(content).toContain("utils");
});

it("combines --include-dir and --include-files additively", () => {
const result = spawnSync(
"node",
[
cliPath,
"-o",
"combined-include.md",
"--include-dir",
"src/utils",
"--include-files",
"src/components/button.js",
"-t",
"3",
],
{
cwd: FIXTURE_DIR,
encoding: "utf8",
stdio: ["inherit", "pipe", "pipe"],
}
);

expect(result.stderr).toBe("");
expect(result.stdout).toContain("Output written to");

const outPath = join(CODEFETCH_DIR, "combined-include.md");
expect(fs.existsSync(outPath)).toBe(true);

const content = fs.readFileSync(outPath, "utf8");
// Should include files from utils directory (via --include-dir)
expect(content).toContain("test1.ts");
expect(content).toContain("test2.js");
// Should include specific file (via --include-files)
expect(content).toContain("button.js");
// Should NOT include other files not matching the patterns
expect(content).not.toContain("app.js");
expect(content).not.toContain("header.js");
expect(content).not.toContain("container.js");
// Project tree should be present
expect(content).toMatch(/Project Structure:/);
});

it("combines multiple --include-dir directories with --include-files", () => {
const result = spawnSync(
"node",
[
cliPath,
"-o",
"multi-dir-include.md",
"--include-dir",
"src/utils,src/components/base",
"--include-files",
"src/app.js",
"-t",
"3",
],
{
cwd: FIXTURE_DIR,
encoding: "utf8",
stdio: ["inherit", "pipe", "pipe"],
}
);

expect(result.stderr).toBe("");
expect(result.stdout).toContain("Output written to");

const outPath = join(CODEFETCH_DIR, "multi-dir-include.md");
expect(fs.existsSync(outPath)).toBe(true);

const content = fs.readFileSync(outPath, "utf8");
// Should include files from utils directory
expect(content).toContain("test1.ts");
expect(content).toContain("test2.js");
// Should include files from components/base directory
expect(content).toContain("container.js");
// Should include the specific file app.js
expect(content).toContain("app.js");
// Should NOT include files from other directories
expect(content).not.toContain("button.js");
expect(content).not.toContain("header.js");
// Project tree should be present
expect(content).toMatch(/Project Structure:/);
});
});
Loading
Loading