diff --git a/README.md b/README.md
index 7d2a889..b47c5fa 100644
--- a/README.md
+++ b/README.md
@@ -4,273 +4,38 @@ Generate markdown documentation from directory structure. Optimized for LLM inge
-## Features
+## Install
-- Recursive directory traversal
-- ASCII tree visualization (LLM-friendly, minimal token overhead)
-- Full file contents with explicit delimiters and metadata
-- Filters dotfiles, binaries, and blacklisted extensions
-- Vertical slices mode: root file + one markdown per top-level directory
-- Append mode for single files or directories
-- Force overwrite of existing output files
-- Configurable depth, file size limits, ignore patterns
-- Optional upload to Proton Drive after generating output
-- Skip frontend files (`.html`, `.css`, `.js`, etc.)
-- Frontend only mode (include only frontend files and `README.md`)
-- Omit test files and inline test blocks (Go, Java, Rust)
+ go install git.flo.fo/FLO/dirmd@latest
-## Installation
-Only for linux.
-Download binary from release page or install with go
-```bash
-go install git.flo.fo/FLO/dirmd@latest
-```
+Linux only. Run `dirmd -h` for all flags.
-## Use with Git Post Hook
-combine with git post hook to sync with proton drive on each commit
+## Why
-add something like this in ./.git/hooks/post-commit
+LLMs don't need 40k tokens of preamble. dirmd dumps a repo as markdown: tree + file contents, minus the noise. Everything filtered out stays visible in the tree (`~ tests omitted`, `~ ignored`) so nothing vanishes silently — the model knows what exists and can ask for it.
-```bash
-#!/bin/bash
+## Three ways to use it
-set -euo pipefail
+**One file — small repos.**
+For small projects that fit in a context window comfortably:
-if ! command -v dirmd &>/dev/null; then
- exit 0
-fi
+ dirmd -o out.md ~/repos/myproject
-REPO_ROOT=$(git rev-parse --show-toplevel)
-REPO_NAME=$(basename "$REPO_ROOT")
+Everything lands in a single markdown file. Simplest, works fine until it doesn't.
-dirmd -o "/tmp/${REPO_NAME}" "$REPO_ROOT" -f --vertical-slices --proton-drive
-```
+**Vertical slices — large repos.**
+Once a single file gets too big to paste repeatedly, split it:
+ dirmd -o docs ~/repos/myproject --vertical-slices
-## Build
+This produces an index file (metadata + full tree + slice table) and one file per top-level directory. The full tree exists only in the index; every other file carries a one-line breadcrumb pointing back to it. Workflow: paste the index, then paste only the slice you need.
- go build -o dirmd .
+**Slices + picks — you already know which files matter.**
+Sometimes you need one specific file without its whole directory:
-## Usage
+ dirmd -o docs ~/repos/myproject --vertical-slices \
+ --pick common/src/config.rs --pick init/src/main.rs
- # Create new documentation
- dirmd -o output.md ~/repos/myproject
+Picks are individual files written alongside the slices, in the same directory layout (`common/config.rs.md` next to `common/common_myproject.md`). Use this when you're mid-conversation, already know the relevant files, and want a single consistent snapshot containing exactly those.
- # Overwrite existing output file
- dirmd -o output.md ~/repos/myproject -f
-
- # Append a single file
- dirmd -a -i README.md -o existing.md
-
- # Append another directory
- dirmd -a ~/repos/anotherproject -o existing.md
-
- # Generate with vertical slices
- dirmd -o output.md ~/repos/myproject --vertical-slices
-
- # Skip frontend files (.html, .css, .js, etc.)
- dirmd -o output.md ~/repos/myproject --skip-frontend
-
- # Frontend only (include only frontend files and README.md)
- dirmd -o output.md ~/repos/myproject --frontend-only
-
- # Omit test files and inline test blocks
- # Go: excludes *_test.go
- # Java: excludes *Test.java, *Tests.java, src/test/java/*
- # Rust: strips #[cfg(test)] mod tests { ... } blocks
- dirmd -o output.md ~/repos/myproject --omit-test
-
- # Combine filters
- dirmd -o output.md ~/repos/myproject --frontend-only --omit-test
-
- # Generate and upload to Proton Drive (default: /my-files/md/)
- dirmd -o output.md ~/repos/myproject --proton-drive
-
- # Generate and upload to a specific remote path
- dirmd -o output.md ~/repos/myproject --proton-drive --drive-path /my-files/docs/api.md
-
-## Test File Omission
-
-The `--omit-test` flag removes test files and inline test blocks to reduce token overhead in LLM context windows. Behavior varies by language:
-
-| Language | Exclusion Strategy | What Gets Removed |
-|----------|-------------------|-------------------|
-| **Go** | File exclusion | All `*_test.go` files |
-| **Java** | File exclusion | `*Test.java`, `*Tests.java`, files under `src/test/java/` |
-| **Rust** | Content stripping | Entire `#[cfg(test)] mod tests { ... }` blocks removed from `.rs` files |
-
-The Rust stripping works by detecting the `#[cfg(test)]` attribute, then tracking brace depth until the module closes. Nested braces in string literals may occasionally throw off the count, but this is acceptable for RAG use cases.
-
-## Frontend Filtering
-
-Two complementary flags control frontend file inclusion:
-
-| Flag | Effect |
-|------|--------|
-| `--skip-frontend` | Exclude frontend files, keep everything else |
-| `--frontend-only` | Include only frontend files and `README.md`, exclude everything else |
-
-Frontend extensions covered:
-
-| Type | Extensions |
-|------|------------|
-| Templates | `.html`, `.htm`, `.gohtml`, `.tmpl` |
-| Stylesheets | `.css`, `.scss`, `.sass`, `.less` |
-| JavaScript | `.js`, `.jsx`, `.mjs`, `.cjs` |
-| TypeScript | `.ts`, `.tsx` |
-| Frameworks | `.vue`, `.svelte` |
-
-`--skip-frontend` and `--frontend-only` are mutually exclusive. Both can be combined with `--omit-test`.
-
-## Vertical Slices Mode
-
-When `--vertical-slices` is passed, dirmd splits output into multiple files instead of one monolithic document:
-
-- **Root file** (`-o output.md`): Full directory tree with cross-references to slice files, a vertical slices index table (file counts, total bytes per directory), and full contents of all root-level files.
-- **Slice files** (`cmd.md`, `internal.md`, etc.): One per top-level directory, placed alongside the root file. Each contains its own tree, a back-reference to the root document, and full recursive file contents for that directory.
-
-This mode cannot be combined with `--append` or `--input-file`.
-
-Example output with `dirmd -o docs.md ~/repos/myproject --vertical-slices`:
-
- docs/
- ├── docs.md ← root: tree + slices index + root file contents
- ├── cmd.md ← slice: tree + contents of cmd/
- └── internal.md ← slice: tree + contents of internal/
-
-## Instructions File (`.dirmd`)
-
-If a file named `.dirmd` exists in the root of the input directory, you can include its contents as metadata in the generated output by passing the `--instructions` flag.
-
-This is useful for injecting specific context, constraints, or instructions for LLMs processing the snapshot.
-
-- **File Name**: `.dirmd`
-- **Location**: Root of the input directory.
-- **Format**: Plain text or Markdown.
-- **Output**: Rendered as a quoted block (`> Instructions: ...`) immediately after the timestamp and commit info.
-- **Opt-in**: The file is **ignored** unless `--instructions` is explicitly passed.
-
-Example `.dirmd`:
-
-```text
-Focus on the `internal/` package for business logic.
-Ignore `cmd/` unless explicitly asked.
-Prioritize Go files over Python.
-```
-
-## LLM-Optimized Output
-
-Output is designed for RAG ingestion and LLM context windows, not human presentation:
-
-- **ISO 8601 Timestamp**: Added as a metadata line (`> Generated: ...`) immediately after the H1 header for every new file created.
-- **Git Commit Info**: If the source directory is a Git repository, a second metadata line (`> Commit: ()`) is added below the timestamp.
-- **ASCII tree format** (`+-` instead of Unicode box-drawing) to reduce token overhead
-- **Explicit file delimiters**: `--- FILE: ./path (bytes, lines) ---` and `--- END FILE ---`
-- **Document type markers**: `[ROOT]` and `[SLICE]` in H1 headers
-- **Cross-references**: Directory entries in root tree show `-> see cmd.md`; slice files reference their root document
-- **Slices index table**: File counts and total bytes per slice for informed retrieval decisions
-- **Tilde fences** (`~~~`) to avoid conflicts with backticks in source files
-
-## Flags
-
-| Flag | Description | Default |
-|------|-------------|---------|
-| `-o`, `--output` | Output markdown file path (required) | |
-| `-a`, `--append` | Append to existing output file | false |
-| `-f`, `--force` | Overwrite existing output file (non-append mode) | false |
-| `-i`, `--input-file` | Single file input (append mode only) | |
-| `--vertical-slices` | Split output into root file + one markdown per top-level directory | false |
-| `--max-size` | Max file size in bytes | 524288 |
-| `--max-depth` | Max directory recursion depth | 20 |
-| `--ignore` | Additional ignore patterns (glob) | |
-| `--extensions` | Additional file extensions to skip | |
-| `--proton-drive` | Upload output to Proton Drive after writing locally | false |
-| `--drive-path` | Full remote path on Proton Drive (e.g. /my-files/md/report.md) | /my-files/md/ |
-| `--skip-frontend` | Omit frontend file types (.html, .css, .js, .ts, .vue, .svelte, etc.) | false |
-| `--frontend-only` | Include only frontend file types and README.md | false |
-| `--omit-test` | Exclude test files (Go *_test.go, Java *Test.java) and strip #[cfg(test)] blocks from Rust | false |
-
-## Proton Drive Integration
-
-dirmd can optionally upload the generated markdown file(s) to Proton Drive using the [Proton Drive CLI](https://proton.me/support/drive-cli). The upload is performed only when `--proton-drive` is passed. Without it, dirmd behaves exactly as before — no Proton Drive dependency is required.
-
-In vertical slices mode, all generated files (root + slices) are uploaded to `/my-files/md/`. The `--drive-path` flag is ignored in slices mode ; each file is uploaded by its basename.
-
-### Prerequisites
-
-1. Install the Proton Drive CLI — see the [official guide](https://proton.me/support/drive-cli) for download and installation instructions.
-2. Authenticate by running `proton-drive auth login`.
-3. Ensure `proton-drive` is in your PATH.
-
-If the CLI is not installed or you are not logged in, dirmd will report the error to stderr and exit with a non-zero code. Local files are always written successfully before the upload is attempted.
-
-For full documentation on the Proton Drive CLI, including installation, authentication, and troubleshooting, refer to the [official Proton Drive CLI support page](https://proton.me/support/drive-cli).
-
-## Examples
-
- # Custom depth and size limit
- dirmd -o output.md ~/repos/project --max-depth 5 --max-size 262144
-
- # Ignore specific directories
- dirmd -o output.md ~/repos/project --ignore __pycache__ --ignore .venv
-
- # Skip additional file extensions
- dirmd -o output.md ~/repos/project --extensions .log --extensions .tmp
-
- # Vertical slices with overwrite
- dirmd -o docs.md ~/repos/project -f --vertical-slices
-
- # Skip frontend and omit tests
- dirmd -o docs.md ~/repos/project -f --skip-frontend --omit-test
-
- # Frontend only with test omission
- dirmd -o docs.md ~/repos/project -f --frontend-only --omit-test
-
- # Generate, then upload to default remote location
- dirmd -o docs.md ~/repos/project --proton-drive
-
- # Generate, then upload to a custom remote location
- dirmd -o docs.md ~/repos/project --proton-drive --drive-path /my-files/projects/docs.md
-
- # Overwrite existing local file and upload to Drive
- dirmd -o docs.md ~/repos/project -f --proton-drive
-
-## Output Format
-
-Produces markdown with:
-
-1. Absolute path as H1 header with `[ROOT]` or `[SLICE]` marker
-2. **Timestamp metadata line** (ISO 8601) immediately after the H1 header (only for new files)
-3. ASCII directory tree (indented with `+-` and `|` characters)
-4. Vertical slices index table (sliced mode only)
-5. All text files with content in fenced code blocks
-6. Explicit `--- FILE: ---` and `--- END FILE ---` delimiters with byte and line counts
-7. Language detection from file extensions
-
-Example output structure (non-sliced mode):
-
- # /home/user/repos/myproject [ROOT]
- > Generated: 2026-09-02T14:30:00Z
- > Commit: a1b2c3d (Fix login bug)
-
- ## Directory Tree
-
- ~~~
- myproject/
- +- cmd/
- | +- main.go
- +- internal/
- | +- config.go
- ~~~
-
- ## Contents
-
- --- FILE: ./cmd/main.go (45 bytes, 3 lines) ---
- ~~~go
- package main
-
- func main() {}
- ~~~
- --- END FILE ---
-
-Note: Uses tilde fences (~~~) internally to avoid conflicts with nested backticks in source files.
+Rule of thumb: one file for tiny repos, slices when you need to browse, picks when you already know the answer.
diff --git a/Taskfile.yml b/Taskfile.yml
new file mode 100644
index 0000000..ec9d1a1
--- /dev/null
+++ b/Taskfile.yml
@@ -0,0 +1,21 @@
+version: "3"
+
+tasks:
+ build:
+ desc: Build dirmd with injected semantic version (task build VERSION=v0.1.4)
+ deps: [vet]
+ preconditions:
+ - sh: printf '%s' '{{.VERSION}}' | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'
+ msg: "VERSION is required and must be a semantic tag like v0.1.4 — task build VERSION=v0.1.4"
+ cmds:
+ - go build -trimpath -ldflags "-s -w -X git.flo.fo/FLO/dirmd/internal/config.Version={{.VERSION}}" -o bin/dirmd .
+
+ vet:
+ desc: Run go vet
+ cmds:
+ - go vet ./...
+
+ test:
+ desc: Run all tests
+ cmds:
+ - go test ./...
diff --git a/cmd/root.go b/cmd/root.go
index a9cdc1d..63caa4c 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -16,8 +16,9 @@ import (
var (
rootCmd = &cobra.Command{
- Use: "dirmd [flags] ",
- Short: "Generate markdown documentation from directory structure",
+ Use: "dirmd [flags] ",
+ Short: "Generate markdown documentation from directory structure",
+ Version: config.Version,
Long: `dirmd walks a directory and generates markdown files with:
- Directory tree structure
@@ -46,7 +47,10 @@ Examples:
dirmd -o ./docs ~/repos/myproject --proton-drive
# Interactive TUI mode
- dirmd --tui`,
+ dirmd -i
+
+ # Pick specific files in vertical slices mode
+ dirmd -o ./docs ~/repos/myproject --vertical-slices --pick common/src/lib.rs --pick mssql-extractor/src/processor.rs`,
RunE: run,
}
@@ -67,6 +71,7 @@ Examples:
omitMd bool
tuiMode bool
instructions bool
+ pickPaths []string
)
func Execute() {
@@ -93,6 +98,7 @@ func init() {
rootCmd.Flags().BoolVar(&omitTest, "omit-test", false, "exclude test files (Go *_test.go, Java *Test.java) and strip #[cfg(test)] blocks from Rust")
rootCmd.Flags().BoolVar(&omitMd, "omit-md", false, "omit all .md files except README.md")
rootCmd.Flags().BoolVar(&instructions, "instructions", false, "include .dirmd instructions file if present in root")
+ rootCmd.Flags().StringArrayVar(&pickPaths, "pick", nil, "repeatable: relative path to file to include as pick (requires --vertical-slices)")
}
func run(cmd *cobra.Command, args []string) error {
@@ -101,7 +107,7 @@ func run(cmd *cobra.Command, args []string) error {
}
if outputPath == "" {
- return fmt.Errorf("-o is required (or use --tui for interactive mode)")
+ return fmt.Errorf("-o is required (or use --interactive for interactive mode)")
}
if drivePath != "" && !protonDrive {
@@ -120,6 +126,10 @@ func run(cmd *cobra.Command, args []string) error {
return fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
}
+ if len(pickPaths) > 0 && !verticalSlices {
+ return fmt.Errorf("--pick requires --vertical-slices")
+ }
+
excludedExts := append(config.DefaultExts, extensions...)
if skipFrontend {
excludedExts = append(excludedExts, config.FrontendExts...)
@@ -141,6 +151,7 @@ func run(cmd *cobra.Command, args []string) error {
OmitTest: omitTest,
OmitMd: omitMd,
IncludeInstructions: instructions,
+ PickPaths: pickPaths,
}
if singleFile != "" {
diff --git a/internal/config/config.go b/internal/config/config.go
index 5dae068..947dba5 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -1,19 +1,12 @@
package config
-import (
- "flag"
- "fmt"
- "os"
- "path/filepath"
- "strings"
-)
-
const (
DefaultMaxSize = 512 * 1024
DefaultMaxDepth = 20
- Version = "0.1.0"
)
+var Version = "dev"
+
var DefaultIgnores = []string{
".git", "node_modules", "vendor", "bin", "go.mod", "go.sum",
"target", "build", "dist", "Cargo.lock", ".idea", ".vscode",
@@ -41,11 +34,6 @@ var FrontendExts = []string{
".vue", ".svelte",
}
-type stringSlice []string
-
-func (s *stringSlice) String() string { return strings.Join(*s, ", ") }
-func (s *stringSlice) Set(v string) error { *s = append(*s, v); return nil }
-
type Config struct {
InputPath string
OutputPath string
@@ -65,127 +53,5 @@ type Config struct {
OmitTest bool
OmitMd bool
IncludeInstructions bool
-}
-
-func PrintUsage() {
- fmt.Fprintf(os.Stderr, "dirmd v%s\n\n", Version)
- fmt.Fprintf(os.Stderr, "Usage: dirmd [flags] \n")
- fmt.Fprintf(os.Stderr, " dirmd -a -i -o