| name | tixgraft |
| description | Guide for using tixgraft (also called "graft") to pull or graft reusable components from Git repositories (via sparse checkout) or local filesystem paths, using YAML config, CLI arguments, text replacements, and context-driven templating. |
| user-invocable | true |
tixgraft (graft)
tixgraft is a CLI tool for fetching reusable components from Git repositories (using sparse checkout) or from local filesystem paths (a file: source). When users say "graft" they mean tixgraft โ e.g., "graft a component from github", "I want to graft this into my project", "pull this scaffold using graft".
When to Use
Use tixgraft when the user wants to:
- Pull specific files or directories from a remote Git repository (or a local folder) into their project
- Scaffold or template new components from a shared repository
- Apply text replacements (placeholders) on pulled content
- Run post-processing commands after pulling files
- Reuse infrastructure configs, service templates, or any shareable code from Git
CLI Reference
Core Flags
tixgraft # Run with ./tixgraft.yaml config
tixgraft --config <path> # Use a specific config file
tixgraft --repository <repo> # Git repo (overrides config)
tixgraft --tag <ref> # Branch, tag, or commit (overrides config)
tixgraft --dry-run # Preview without executing
tixgraft --verbose / -v # Debug logging
tixgraft --to-command-line # Convert config to CLI command
tixgraft --to-config # Convert CLI args to YAML config
tixgraft --output-format <fmt> # "shell" (default) or "json" for --to-command-line
Per-Pull Flags (repeatable, index-aligned)
Each --pull-* flag at index N pairs with other --pull-* flags at the same index:
--pull-source <path> # Source path within the repository or local folder (required)
--pull-target <path> # Target path in local workspace (required)
--pull-type <type> # "file" or "directory" (default: "directory")
--pull-repository <repo> # Override repository for this pull
--pull-tag <ref> # Override tag for this pull
--pull-reset # rm -rf target before copying
--pull-require-clean-target # require clean git target (default: true)
--pull-must-succeed # whether failure is fatal (default: true)
--pull-commands <cmds> # Post-copy commands (comma-separated)
--pull-replacement <SRC=TGT> # Text replacement: "{{PLACEHOLDER}}=value" or "{{VAR}}=env:ENV_NAME"
Note on --pull-replacement: unlike the other --pull-* flags, replacements are not index-aligned. Every --pull-replacement given on the command line is applied to all CLI pulls (a known CLI limitation). Use a YAML config when you need per-pull replacements.
Context Flags
--context <KEY=VALUE> # Simple context value (repeatable, same key creates array)
--context-json <KEY=JSON> # Complex context as JSON (arrays, objects)
Skill Management Flags
--skill-install # Install the tixgraft Claude Code skill (project-scoped)
--skill-install -g # Install globally (~/.claude/skills/tixgraft/)
--skill-uninstall # Remove the skill (project-scoped)
--skill-uninstall -g # Remove globally
--skill-test # Check if skill is installed and up to date (interactive)
--skill-test -y # Auto-confirm prompts
--skill-test -g # Check global installation
Repository URL Formats
tixgraft accepts these repository formats:
| Format | Example | Expands To |
|---|
| Short | my_org/repo | https://github.com/my_org/repo.git |
| HTTPS | https://github.com/my_org/repo.git | (used as-is) |
| SSH | git@github.com:my_org/repo.git | (used as-is) |
| Local | file:///abs/path, file:~/rel/to/home, ~/rel/to/home, /abs/path, ./rel, ../rel | local folder (no Git โ see below) |
Enterprise Git hosts work with full HTTPS/SSH URLs.
Local (Filesystem) Sources
Besides Git, tixgraft can pull from a local folder on the same machine. A repository given as a file: URL or a plain filesystem path is treated as a local source: tixgraft skips Git entirely (no clone, no sparse checkout) and copies straight from the local path.
Accepted forms
| Form | Meaning |
|---|
file:///abs/path | Absolute path (file:// stripped โ /abs/path) |
file:/abs/path | Absolute path (file: stripped โ /abs/path) |
file:~/rel/to/home | Leading ~ expands to $HOME (or $USERPROFILE) |
file:relative/path | Resolved against the current working directory |
~/rel/to/home | Bare tilde path (~ expands to $HOME / $USERPROFILE) |
/abs/path | Bare absolute path |
./rel, ../rel | Bare relative path, resolved against the current working directory |
A source is treated as local when it starts with file:, ~, /, ./, or ../. Everything else โ the short org/repo form and HTTP(S)/SSH URLs โ is treated as Git. (A plain relative path with no leading ./ โ e.g. foo/bar โ is the short Git form, not a local path; prefix it with ./ to force local.)
Semantics
- No Git: local sources never clone or sparse-checkout. The
tag / --tag value is ignored for a local source.
- Validation: the resolved path must exist and be a directory, otherwise tixgraft fails with a source error (exit code 2). The per-pull
type (file or directory) is still checked against the resolved source inside that folder.
~ expansion: a leading ~ (with or without the file: prefix) expands to the home directory before resolving.
- Copy is ignore-aware: directory copies walk the source with the Rust
ignore crate (the same traversal ripgrep uses). Files matched by .gitignore rules (applied when the source lives inside a Git repository) or by .ignore files are skipped, not copied. Dotfiles are copied; symlinks are not followed. This is the same copy path used for Git sources, so there is no ignore-behavior difference between local and Git sources.
Example
repository: "file:~/src/tixena/scaffold"
pulls:
- source: "vigil/templates/developer"
target: "./dev-template"
tixgraft \
--repository file:~/src/tixena/scaffold \
--pull-source vigil/templates/developer \
--pull-target ./dev-template
YAML Configuration
The default config file is ./tixgraft.yaml. Structure:
repository: "my_org/scaffolds"
tag: "main"
context:
organization: "mycompany"
environment: "production"
pulls:
- source: "path/in/repo"
target: "./local/path"
type: "directory"
repository: "other/repo"
tag: "v1.0.0"
reset: true
requireCleanTarget: false
mustSucceed: false
context:
serviceName: "my-api"
port: 8080
replacements:
Config hierarchy: CLI arguments > per-pull config > global config.
Target paths must be relative (no leading /, no ..); they resolve against the config file's directory, so the same config produces the same result regardless of the working directory.
Children (Cascading Execution)
A config can reference child tixgraft.yaml files for cascading execution:
children:
- "./services/api/tixgraft.yaml"
- "./services/web/tixgraft.yaml"
repository: "my_org/scaffolds"
tag: "main"
children:
- "./services/api/tixgraft.yaml"
pulls:
- source: "shared/config"
target: "./shared-config"
Rules:
- Children must be in subdirectories (no
.., no absolute paths)
- Children are fully independent -- no inheritance of
repository, tag, or context
- Max nesting depth: 11
- A config must have
pulls or children (or both)
- Default execution order: parent pulls first, then children
processChildrenFirst: true reverses the order
Text Replacements
Replacements find-and-replace text in all non-binary files after copying.
In YAML
replacements:
- source: "{{APP_NAME}}"
target: "my-app"
- source: "{{NAMESPACE}}"
valueFromEnv: "K8S_NAMESPACE"
Via CLI
--pull-replacement "{{APP_NAME}}=my-app"
--pull-replacement "{{NAMESPACE}}=env:K8S_NAMESPACE"
The env: prefix tells tixgraft to read from an environment variable.
Context System and .graft.yaml
Components in the source repository can include .graft.yaml files that define required properties, replacements, and post-commands. This enables parameterized, reusable components.
How .graft.yaml Works
A .graft.yaml file in the source repository defines:
context:
- name: serviceName
description: "Name of the service"
dataType: string
- name: port
description: "Service port"
dataType: number
defaultValue: 8080
replacements:
- source: "{{SERVICE_NAME}}"
valueFromContext: serviceName
- source: "{{PORT}}"
valueFromContext: port
postCommands:
- command: echo
args: ["Service configured"]
Each .graft.yaml replacement must specify exactly one value source:
target: โ a static literal value
valueFromEnv: โ read from an environment variable
valueFromContext: โ read from a context property (shown above)
postCommands entries also accept an optional cwd: (working directory relative to the graft) and support a conditional choice form (type: choice with options, each running a test command and executing onMatch when the output matches expectedOutput). A postCommand with no type defaults to type: command.
Providing Context
Context values can come from three sources (in priority order):
- CLI:
--context serviceName=my-api --context port=8080
- Per-pull config:
context: block under a pull
- Global config:
context: block at root level
For complex values use --context-json:
--context-json 'services=[{"name":"api","port":8080}]'
Type Coercion
String values are automatically coerced to the declared type:
"true", "yes", "1" -> boolean true; "false", "no", "0" -> boolean false
"8080" -> number 8080
An empty-string context value removes that property from the context (used to unset a value inherited from global/per-pull context).
Validation
- Missing required properties (no default) -> exit code 1
- Invalid types that can't be coerced -> exit code 1
- Extra properties not in the context definition -> ignored
Processing Flow
- Files are copied to target
.graft.yaml files are discovered recursively in target
- Context is validated against requirements
- Replacements are applied using context values
- Post-commands execute
.graft.yaml files are cleaned up (removed from target)
Config-to-CLI Conversion
Convert any YAML config to a shareable CLI command:
tixgraft --to-command-line
tixgraft --to-command-line --output-format json
tixgraft --to-command-line --repository override/repo
Convert CLI args to a YAML config:
tixgraft --to-config --repository my_org/repo --pull-source src --pull-target ./dest
CLI-Only Usage (No Config File)
You can use tixgraft entirely from the command line without a YAML file:
tixgraft --repository my_org/templates --pull-source kubernetes/app --pull-target ./k8s
tixgraft --repository my_org/configs --pull-source docker/Dockerfile --pull-target ./Dockerfile --pull-type file
tixgraft \
--pull-repository my_org/configs --pull-source nginx/default.conf --pull-target ./nginx.conf --pull-type file \
--pull-repository my_org/scripts --pull-source ci/deploy.sh --pull-target ./deploy.sh --pull-type file
tixgraft \
--repository my_org/templates \
--pull-source kubernetes/app \
--pull-target ./k8s \
--pull-replacement "{{APP_NAME}}=my-app" \
--pull-replacement "{{NAMESPACE}}=env:K8S_NAMESPACE" \
--pull-commands "kubectl apply -f ."
tixgraft \
--repository my_org/service-templates \
--pull-source microservices/api \
--pull-target ./services/my-api \
--context serviceName=my-api \
--context port=8080
Exit Codes
| Code | Meaning |
|---|
| 0 | Success |
| 1 | Configuration error |
| 2 | Source error (path not found in repo) |
| 3 | Command error (post-processing command failed) |
| 4 | Git error (clone/checkout failed) |
| 5 | Filesystem error |
| 6 | Skill error |
Common Workflows
Scaffold a new service from a template repo
tixgraft --repository my_org/service-templates \
--pull-source microservices/api-service \
--pull-target ./services/user-api \
--context serviceName=user-api --context port=8080
Pull shared infrastructure configs
repository: "devops/k8s-scaffolds"
tag: "production"
pulls:
- source: "base/namespace"
target: "./k8s/namespace"
replacements:
- source: "{{NAMESPACE}}"
valueFromEnv: "K8S_NAMESPACE"
- source: "apps/mongodb"
target: "./k8s/mongodb"
reset: true
commands:
- "kubectl apply -f ."
- source: "extras/monitoring"
target: "./k8s/monitoring"
mustSucceed: false
Pull a single file
tixgraft --repository my_org/configs \
--pull-source docker/Dockerfile.node \
--pull-target ./Dockerfile \
--pull-type file \
--pull-replacement "{{NODE_VERSION}}=20"
Orchestrate multiple services with children
children:
- "./services/api/tixgraft.yaml"
- "./services/web/tixgraft.yaml"
- "./services/worker/tixgraft.yaml"
Generate a shareable command from config
tixgraft --to-command-line