A CLI tool for combining markdown fragments based on tags. Split your documentation, rules, or context files into multiple fragments and splice them together based on supplied tags.
- Tag-based fragment selection: Use
ctx-tagsin frontmatter to categorize fragments - Interactive TUI: Select tags using an intuitive terminal interface
- Non-interactive mode: Automate builds with command-line flags
- Configurable: JSON configuration with schema validation
- Multiple output formats: Support for different AI tools (opencode, gemini, etc.)
- Project-specific fragments: Local
.ctx/fragmentsdirectory support with override logic - Reproducible builds: Generate command files for replication
# Install directly from the repository
nix profile install github:Lewenhaupt/ctx
# Or run without installing
nix run github:Lewenhaupt/ctx -- build --help
# For faster builds, you can also use the binary cache (if available)
# nix profile install github:Lewenhaupt/ctx --extra-substituters https://cache.nixos.orgAdd to your flake.nix:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
ctx.url = "github:Lewenhaupt/ctx";
};
outputs = { self, nixpkgs, ctx, ... }:
let
system = "x86_64-linux"; # or your system
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.${system}.default = pkgs.mkShell {
packages = [
ctx.packages.${system}.default
# other packages...
];
};
};
}Then use in your project:
# Enter development shell with ctx available
nix develop
# Or run directly
nix run .#ctx -- build --helpAdd to your NixOS configuration:
# configuration.nix or flake.nix
{
inputs.ctx.url = "github:Lewenhaupt/ctx";
# In your system configuration:
environment.systemPackages = [
inputs.ctx.packages.${system}.default
];
}# Clone the repository
git clone <repository-url>
cd ctx
# Enter development shell
direnv allow
# or
nix develop
# Build the binary
go build -o ctx ./cmd/ctxgo install github.com/Lewenhaupt/ctx/cmd/ctx@latestThe easiest way to get started is to use the interactive init command to scaffold your configuration:
ctx initThis will guide you through:
- Setting up your configuration file (
~/.config/.ctx/config.json) - Choosing output formats (opencode, gemini, or custom formats)
- Configuring your fragments directory location
- Optionally creating a sample fragment to get started
The init command will:
- Show you the default output formats available
- Allow you to add custom output formats if needed
- Let you specify where to store your fragments (defaults to
~/.config/.ctx/fragments) - Optionally create a hello-world sample fragment
- Create all necessary directories and configuration files
If you prefer to set up manually:
-
Create fragments directory:
mkdir -p ~/.config/.ctx/fragments -
Create a fragment (
~/.config/.ctx/fragments/typescript.md):--- ctx-tags: typescript, frontend, web --- # TypeScript Guidelines ## Type Safety - Always use strict mode - Prefer interfaces over types for object shapes
-
Build fragments:
# Interactive mode ctx build # Non-interactive mode ctx build --tags typescript,frontend # Output to file ctx build --tags typescript --non-interactive > AGENTS.md
After running ctx init, you can immediately start building:
# Interactive mode - select tags and output formats
ctx build
# Non-interactive mode with specific tags
ctx build --tags hello,world --non-interactiveConfiguration is stored in ~/.config/.ctx/config.json (or $XDG_CONFIG_HOME/.ctx/config.json).
{
"defaultTags": ["general", "coding"],
"outputFormats": {
"opencode": "AGENTS.md",
"gemini": "GEMINI.md",
"custom": "CUSTOM.md"
},
"fragmentsDir": "/custom/path/to/fragments",
"customSettings": {
"max_fragments": 50
}
}The configuration follows the JSON schema defined in config.schema.json:
defaultTags: Array of tags to pre-select in interactive modeoutputFormats: Mapping of format names to output filenamesfragmentsDir: Custom path to fragments directory (optional)customSettings: Additional settings for specific workflows
Fragments are markdown files with optional frontmatter containing ctx-tags:
---
ctx-tags: tag1, tag2, tag3
---
# Fragment Content
Your markdown content here.- Tags are comma-separated in the
ctx-tagsfield - Frontmatter is optional
- Only
.mdand.markdownfiles are processed - Fragments are combined in the order they're found
In addition to global fragments stored in your configuration directory, ctx supports project-specific fragments stored in a local .ctx/fragments directory within your project.
Create a .ctx/fragments directory in your project root:
mkdir -p .ctx/fragmentsAdd project-specific fragments:
# .ctx/fragments/api-guidelines.md
---
ctx-tags: api, guidelines, backend
---
# API Guidelines
These guidelines apply specifically to this project's API.By default, local fragments override global fragments with the same filename:
- Global:
~/.config/.ctx/fragments/common.md - Local:
./.ctx/fragments/common.md - Result: Local
common.mdis used, global is ignored
Use the --no-local-override flag to include both local and global fragments:
# Include both local and global fragments with the same name
ctx build --no-local-override --tags common# Build using both global and local fragments (default override behavior)
ctx build --tags api,backend
# Build including both local and global fragments with same names
ctx build --tags common --no-local-override
# Build with tags that might exist in both global and local fragments
ctx build --tags guidelines,typescript- Project documentation: Store project-specific context in
.ctx/fragments - Team overrides: Override global rules with project-specific ones
- Environment-specific configs: Different fragments for different projects
- Version control: Commit local fragments with your project
ctx init [flags]
Flags:
--config-file string Config file path (default: XDG_CONFIG_HOME/.ctx/config.json)
-h, --help Help for initctx build [flags]
Flags:
--tags strings Comma-separated list of tags to include
--non-interactive Run in non-interactive mode
--output-format strings Output format(s) to use (e.g., opencode, gemini, custom)
--output-file string Output file path (overrides format-based naming)
--stdout Output to stdout instead of files
--no-local-override Include both local and global fragments even if they have the same name
--config-file string Config file path (default: XDG_CONFIG_HOME/.ctx/config.json)
-h, --help Help for build# Interactive mode (select tags and output formats)
ctx build
# Build with specific tags and output to stdout
ctx build --tags typescript,rust --stdout
# Non-interactive build with specific output format
ctx build --tags frontend --non-interactive --output-format opencode
# Build with multiple output formats
ctx build --tags general,coding --output-format opencode,gemini --non-interactive
# Build to custom file
ctx build --tags typescript --output-file custom-output.md --non-interactive
# Pipe output to another command
ctx build --tags general --stdout --non-interactive | grep "function"- Go 1.21+
- Nix (optional, for development environment)
# Clone repository
git clone <repository-url>
cd ctx
# Setup development environment
direnv allow # if using Nix
# or
nix develop # if using Nix without direnv
# or
go mod download # if using Go directly
# Setup git hooks for commit message validation
./scripts/setup-git-hooks.sh
# Run tests
go test ./...
# Build using Go
go build -o ctx ./cmd/ctx
# Or build using Nix
nix build .#default# Run directly with go run
go run ./cmd/ctx build
# Run with specific flags and output to stdout
go run ./cmd/ctx build --tags typescript,rust --stdout
# Run non-interactively with specific output format
go run ./cmd/ctx build --tags general --non-interactive --output-format opencode
# Use direnv to ensure correct environment
direnv exec . go run ./cmd/ctx build
# Build and run the binary
go build -o ctx ./cmd/ctx
./ctx build --stdout --non-interactive --tags general# Unit tests
go test ./internal/...
# Integration tests
go test .
# All tests with coverage
go test -cover ./....
├── cmd/ctx/ # Main CLI application
├── internal/
│ ├── config/ # Configuration management
│ ├── parser/ # Fragment parsing and splicing
│ └── tui/ # Terminal UI components
├── config.schema.json # JSON schema for configuration
├── flake.nix # Nix development environment
└── integration_test.go # Integration tests
- Fork the repository
- Create a feature branch
- Set up git hooks:
./scripts/setup-git-hooks.sh - Add tests for new functionality
- Ensure all tests pass:
go test ./... - Follow commit message format:
type: description(enforced by git hooks)- Allowed types:
feat,fix,docs,style,refactor,test,chore - Example:
feat: add new fragment parsing feature
- Allowed types:
- Submit a pull request
[Add your license here]# Test commit hook test change