A Go-powered workspace manager that enforces a single, machine-readable project structure under
~/Code, provides a fast TUI for navigating and operating on projects, and supports safe one-command syncing to remote servers.
co solves the problem of disorganized code directories by enforcing a strict, predictable filesystem layout. Every project follows the same structure, making it trivial for both humans and automation (agents, scripts, tools) to locate and operate on projects.
- Unified workspace structure — Every project lives under
~/Codewith a consistent<owner>--<project>naming convention - Machine-readable metadata —
project.jsonin every workspace; globalindex.jsonlfor fast querying - Interactive TUI — Browse, search, and manage projects with a polished terminal interface
- Remote sync — One-command sync that copies metadata and clones repos on the target
- Safe archiving — Git-bundle based archives that preserve full history
- Semantic code search — Find code by meaning using AST-aware chunking and vector embeddings (via Ollama)
# Clone the repository
git clone https://github.com/youruser/co.git
cd co
# Build
make build
# Install to $GOPATH/bin
make install- Go 1.21+
- Git (for repo operations)
- rsync (for remote sync, optional)
# Launch the TUI (default action)
co
# Create a new workspace
co new personal my-project
# Create a workspace with repos
co new acme webapp https://github.com/acme/frontend.git https://github.com/acme/backend.git
# Create a workspace using a template
co new acme dashboard -t fullstack
# List available templates
co new --list-templates
# Regenerate the global index
co index
# Sync a workspace to a remote server
co sync acme--webapp prodAll workspaces live under a single root (default: ~/Code):
~/Code/
├── _system/ # Reserved for co internals
│ ├── archive/ # Archived workspaces
│ ├── cache/ # Temporary data
│ ├── index.jsonl # Global project index
│ └── logs/ # Debug logs
├── acme--dashboard/ # Workspace: owner=acme, project=dashboard
├── acme--api/ # Workspace: owner=acme, project=api
├── personal--dotfiles/ # Workspace: owner=personal, project=dotfiles
└── oss--contrib-rails/ # Workspace: owner=oss, project=contrib-rails
Every workspace follows this exact layout—no exceptions:
<workspace>/
├── project.json # Canonical metadata
└── repos/ # Git repositories
├── frontend/
├── backend/
└── ...
Workspace slugs follow the format: <owner>--<project>
| Owner Type | Examples |
|---|---|
| Client/org | acme--dashboard, bigcorp--analytics |
| Personal | personal--dotfiles, personal--blog |
| Open source | oss--neovim-config, oss--contrib-rails |
| Company | mycompany--platform, startup--mvp |
Variants use a controlled qualifier suffix: owner--project--qualifier
- Allowed:
poc,demo,legacy,migration,infra - Forbidden:
-old,-new,-2,final-final
Each workspace contains a project.json file as the source of truth:
{
"schema": 1,
"slug": "acme--dashboard",
"owner": "acme",
"name": "dashboard",
"state": "active",
"tags": ["client", "web"],
"created": "2025-12-13",
"updated": "2025-12-13",
"repos": [
{
"name": "frontend",
"path": "repos/frontend",
"remote": "[email protected]:acme/dashboard-frontend.git"
},
{
"name": "backend",
"path": "repos/backend",
"remote": "[email protected]:acme/dashboard-backend.git"
}
],
"notes": "Main dashboard application"
}State vocabulary: active | paused | archived | scratch
The global index at ~/Code/_system/index.jsonl is computed from disk and provides fast access for the TUI and CLI:
{
"schema": 1,
"slug": "acme--dashboard",
"path": "/Users/you/Code/acme--dashboard",
"owner": "acme",
"state": "active",
"tags": ["client", "web"],
"repo_count": 2,
"last_commit_at": "2025-12-10T12:34:56Z",
"last_fs_change_at": "2025-12-13T09:21:00Z",
"dirty_repos": 1,
"size_bytes": 128731293,
"repos": [
{ "name": "frontend", "path": "repos/frontend", "head": "a1b2c3d", "branch": "main", "dirty": true },
{ "name": "backend", "path": "repos/backend", "head": "d4e5f6g", "branch": "main", "dirty": false }
]
}Running co with no arguments launches the TUI.
Launch the interactive TUI dashboard.
co # Same as `co tui`
co tuiCreate a new workspace with the standard structure.
# Empty workspace
co new personal my-project
# With cloned repos
co new acme webapp \
https://github.com/acme/frontend.git \
https://github.com/acme/backend.git
# Using a template
co new acme dashboard -t fullstack
# With template variables
co new acme api -t backend -v port=8080 -v db_name=app_db
# Interactive mode (prompts for owner, project, and template)
co new
# Preview template creation without making changes
co new acme app -t fullstack --dry-runTemplate flags:
| Flag | Description |
|---|---|
-t, --template <name> |
Use a template for workspace creation |
-v, --var <key=value> |
Set template variable (can be repeated) |
--no-hooks |
Skip running lifecycle hooks |
--dry-run |
Preview creation without making changes |
--list-templates |
List available templates |
--show-template <name> |
Show template details |
Scan ~/Code and regenerate _system/index.jsonl. Computes:
- Last commit date across repos
- Last filesystem change
- Repo dirty flags
- Workspace size
- Syncs
project.jsonrepo entries fromrepos/(use--no-project-syncto skip)
co indexScan workspaces for missing project.json files and optionally create them.
co doctor
co doctor --dry-run
co doctor --yesList workspaces with optional filters.
co ls # All workspaces
co ls --owner acme # Filter by owner
co ls --state active # Filter by state
co ls --tag client # Filter by tag
co ls --json # JSON outputDisplay detailed workspace information.
co show acme--dashboard
co show acme--dashboard --jsonLaunch the Template Explorer TUI to browse, inspect, and create workspaces from templates.
co template # Launch Template Explorer TUI
co template list # List templates (non-interactive)
co template show <name> # Show template details
co template validate [name] # Validate one or all templatesThe Template Explorer provides an interactive interface for:
- Browsing available templates across all configured directories
- Viewing template details, variables, repos, and hooks
- Creating new workspaces with variable prompting
- Validating template manifests
See the Template Explorer TUI section for keybindings.
Launch an interactive TUI for browsing folders and importing them as workspaces. This is useful for organizing existing codebases into the co workspace structure.
co import-tui # Browse current directory
co import-tui ~/projects # Browse ~/projects
co import-tui ./legacy-code # Browse ./legacy-codeThe import browser provides:
- Tree view of the folder structure with git repository detection
- Import folders as new workspaces with owner/project configuration
- Add repos to existing workspaces
- Apply templates during import
- Stash (archive) folders for later
- Batch operations on multiple selected folders
See the Import Browser TUI section for the full workflow and keybindings.
Open a workspace in your configured editor.
co open acme--dashboardArchive a workspace to _system/archive/.
co archive old--project # Archive only
co archive old--project --delete # Archive and delete local
co archive old--project --reason "EOL" # Add reason to metadataSync a workspace to a remote server by copying metadata and non-repo files, then cloning repos on the target machine.
co sync acme--dashboard prod # Safe: fails if remote exists
co sync acme--dashboard prod --force # Overwrite remote
co sync acme--dashboard prod --dry-run # Preview only
co sync acme--dashboard prod --no-git # Exclude .git directoriesInteractively select multiple workspaces to sync to a remote server.
co sync-batch prod| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Invalid arguments |
| 10 | Sync skipped (remote exists) |
All listing/show commands support --json or --jsonl for scripting:
co ls --json | jq '.[] | select(.dirty_repos > 0)'
co show my--project --jsonConfiguration is discovered in order:
--config <path>flag$XDG_CONFIG_HOME/co/config.jsonor~/.config/co/config.json~/Code/_system/config.json(optional)
{
"schema": 1,
"code_root": "~/Code",
"editor": "code",
"servers": {
"prod": {
"ssh": "prod",
"code_root": "~/Code"
},
"devbox": {
"ssh": "devbox",
"code_root": "/home/ubuntu/Code"
}
}
}Server definitions:
ssh— SSH alias or host (uses~/.ssh/config)code_root— Remote code root (defaults to~/Code)
Templates provide reusable workspace scaffolding with pre-configured files, repos, and setup scripts.
Tip: Use
co templateto launch the Template Explorer TUI for interactive template browsing and workspace creation.
Templates are searched in multiple directories with the following precedence:
- Primary:
<code_root>/_system/templates/(e.g.,~/Code/_system/templates/) - Fallback:
~/.config/co/templates/(or$XDG_CONFIG_HOME/co/templates/)
When templates with the same name exist in multiple locations, the primary location takes precedence. Each template is a directory containing a template.json manifest.
~/Code/_system/templates/
├── fullstack/
│ ├── template.json
│ ├── .claude/
│ │ └── AGENTS.md
│ └── docker-compose.yml
├── backend/
│ └── template.json
└── _global/ # Files copied to ALL template-based workspaces
└── .editorconfig
Each template has a template.json file:
{
"name": "fullstack",
"description": "Full-stack web application with frontend and backend",
"variables": [
{
"name": "port",
"type": "integer",
"default": 3000,
"description": "Development server port"
},
{
"name": "db_type",
"type": "choice",
"choices": ["postgres", "mysql", "sqlite"],
"default": "postgres",
"required": true
}
],
"repos": [
{
"name": "frontend",
"clone_url": "https://github.com/example/frontend-template.git",
"tags": ["web", "react"]
},
{
"name": "backend",
"init": true,
"tags": ["api"]
}
],
"files": {
"include": ["**/*"],
"exclude": ["template.json", "*.md"]
},
"hooks": {
"post_create": "echo 'Workspace created!'",
"post_clone": {
"command": "./setup.sh",
"env": {"PORT": "{{port}}"}
}
},
"tags": ["web", "fullstack"],
"state": "active"
}These variables are automatically available in all templates:
| Variable | Description | Example |
|---|---|---|
owner |
Workspace owner | acme |
project |
Project name | dashboard |
slug |
Full workspace slug | acme--dashboard |
workspace_path |
Absolute path | /Users/you/Code/acme--dashboard |
code_root |
Code root directory | /Users/you/Code |
Template files support {{variable}} substitution:
# docker-compose.yml
services:
app:
ports:
- "{{port}}:{{port}}"
environment:
DB_TYPE: {{db_type}}
PROJECT: {{project}}| Type | Description |
|---|---|
string |
Free-form text |
integer |
Whole numbers |
boolean |
true/false (prompted as yes/no toggle) |
choice |
Selection from predefined list |
Templates support hooks that run at different stages:
| Hook | When it runs |
|---|---|
pre_create |
Before workspace directory is created |
post_create |
After workspace structure is created |
post_clone |
After each repo is cloned |
post_complete |
After all setup is complete |
post_migrate |
After applying template to existing workspace |
Hooks can be a simple command string or an object with command, workdir, and env fields.
Files in ~/Code/_system/templates/_global/ are copied to every workspace created with any template. Use this for shared configuration like .editorconfig, .gitattributes, or shared scripts.
-
Create a directory in
~/Code/_system/templates/:mkdir -p ~/Code/_system/templates/my-template -
Add a
template.jsonmanifest:{ "name": "my-template", "description": "My custom workspace template" } -
Add any files you want copied to new workspaces.
-
Use the template:
co new acme project -t my-template
Partials are reusable file sets that can be applied to any directory at any time. Unlike templates, partials do not create a workspace; they add or update files in an existing directory.
- Primary:
~/Code/_system/partials/ - Fallback:
~/.config/co/partials/
These ship with co and live under ~/Code/_system/partials/:
| Name | Description | Example |
|---|---|---|
agent-setup |
Agent workflow scaffolding (AGENTS.md, docs, hooks) | co partial apply agent-setup -v primary_stack=go |
python-tooling |
Python tooling with ruff/pytest (optional mypy) | co partial apply python-tooling -v python_version=3.12 |
eslint |
ESLint config for JS/TS with optional Prettier | co partial apply eslint -v typescript=true -v prettier=true |
github-actions |
CI/release workflows for node/python/go/rust | co partial apply github-actions -v language=node |
co partial list
co partial list --tag agentco partial show agent-setup
co partial show agent-setup --filesco partial apply agent-setup
co partial apply agent-setup ./repos/backend
co partial apply agent-setup -v primary_stack=go
co partial apply agent-setup --dry-run
co partial apply agent-setup --conflict skip -yco partial launches the interactive Partial Explorer TUI (browse, apply, validate).
Non-interactive commands:
co partial list
co partial show <name>
co partial show <name> --files
co partial apply <name> [path]
co partial validate [name]- Variables are provided with
-v key=value(repeatable). Required variables are prompted when missing. - Boolean variables should be
trueorfalse. - Conflict strategies:
prompt,skip,overwrite,backup,merge.mergeonly applies to.gitignore-style files, JSON, and YAML.
- Use
--dry-runto preview. - Use
--no-hooksto skippre_apply/post_applyscripts. - Use
--forceto apply even when prerequisites are missing (otherwise apply fails).
| Strategy | Behavior |
|---|---|
| prompt | Ask for each conflicting file |
| skip | Keep existing files |
| overwrite | Replace existing files |
| backup | Create .bak file, then overwrite |
| merge | Merge supported formats (gitignore/json/yaml) when available |
Preserve patterns (in partial.json) are never overwritten, regardless of strategy.
These partials are shipped in ~/Code/_system/partials/ and can be applied as-is:
agent-setup— AddsAGENTS.md,.claude/settings.json, andagent_docs/(optional Beads init viause_beads)python-tooling— Addspyproject.toml,ruff.toml,.python-version, and Python.gitignoreentrieseslint— Adds ESLint config with optional TypeScript + Prettier integrationgithub-actions— Adds CI workflows with language-specific steps and optional release workflow
Examples:
co partial apply agent-setup -v primary_stack=go -v use_beads=true
co partial apply python-tooling -v python_version=3.12 -v use_mypy=true
co partial apply eslint -v typescript=true -v prettier=true
co partial apply github-actions -v language=go -v run_tests=true -v build_artifact=falseDirectory structure:
partials/
└── my-partial/
├── partial.json
├── files/
│ └── README.md.tmpl
└── hooks/
└── post-apply.sh
Example partial.json:
{
"schema": 1,
"name": "agent-setup",
"description": "AI agent configuration",
"version": "1.0.0",
"variables": [
{ "name": "project_name", "type": "string", "default": "{{DIRNAME}}" },
{ "name": "primary_stack", "type": "choice", "choices": ["go", "python", "node"], "required": true }
],
"files": { "include": ["**/*"], "exclude": ["partial.json", "hooks/**"] },
"conflicts": { "strategy": "prompt", "preserve": [".beads/*.log"] },
"hooks": { "post_apply": "hooks/post-apply.sh" },
"tags": ["agent", "workflow"]
}Notes:
- Put all output files under
files/. Files ending in.tmplare processed with variable substitution. hooks/is optional. Hook paths are relative to the partial directory.partial.jsonmust match the directory name.
| Variable | Description |
|---|---|
DIRNAME |
Name of target directory |
DIRPATH |
Absolute path to target directory |
PARENT_DIRNAME |
Parent directory name |
IS_GIT_REPO |
true if target is a git repo |
GIT_REMOTE_URL |
Remote URL (if git repo) |
GIT_BRANCH |
Current branch (if git repo) |
Standard variables are also available (for example: GIT_USER_NAME, GIT_USER_EMAIL, DATE, YEAR, TIMESTAMP).
Hooks are optional scripts defined in partial.json:
pre_apply: Runs before any files are writtenpost_apply: Runs after all files are written
Environment variables available to hooks (when enabled):
CO_PARTIAL_NAME,CO_PARTIAL_PATHCO_TARGET_PATH,CO_TARGET_DIRNAMECO_DRY_RUN,CO_VERBOSE,CO_IS_GIT_REPO,CO_GIT_REMOTE_URL,CO_GIT_BRANCHCO_VAR_<name>for each resolved variableCO_FILES_CREATED,CO_FILES_SKIPPED,CO_FILES_OVERWRITTEN,CO_FILES_MERGED,CO_FILES_BACKED_UP(post-apply only)
Templates can reference partials via a partials array in template.json:
{
"partials": [
{ "name": "agent-setup", "target": ".", "variables": { "project_name": "{{PROJECT}}" } },
{ "name": "eslint", "target": "repos/frontend", "when": "{{frontend_stack}} == 'node'" }
]
}Planned application order:
- Workspace structure created
- Global files applied
- Template files applied
post_createhook- Repos cloned/initialized
post_clonehook- Partials applied (in order)
post_completehook
- Check if remote path exists
- If exists: stop (exit code 10) unless
--force - If not exists: create directory
- Sync metadata and non-repo files
- Clone repos on the target machine from
project.json(or local git remotes if missing)
- Metadata sync: rsync over SSH (
rsync -az --partial --progress) - Fallback: tar streaming over SSH
- Repos: cloned with
git cloneon the target machine
Common build artifacts, dependency caches, and sensitive files are excluded by default:
| Category | Patterns |
|---|---|
| Dependencies | node_modules/, vendor/, .pnpm-store/, bower_components/ |
| Build outputs | target/, dist/, build/, out/, bin/, obj/, _build/ |
| Frameworks | .next/, .nuxt/, .output/, .svelte-kit/, .vercel/, .netlify/ |
| Caches | .cache/, __pycache__/, .pytest_cache/, .mypy_cache/, .turbo/ |
| Virtual envs | .venv/, venv/, .virtualenv/ |
| Coverage | coverage/, .nyc_output/, htmlcov/, .tox/, .nox/ |
| IDE | .idea/, *.swp, *.swo, *~, .settings/ |
| OS | .DS_Store, Thumbs.db, Desktop.ini |
| Logs | *.log, logs/, npm-debug.log*, yarn-*.log* |
| Secrets | .env, .env.*, secrets/, *.pem, *.key |
| Terraform | .terraform/, *.tfstate, *.tfstate.* |
Use co sync --list-excludes to see the full list.
repos/ is always excluded from file transfer and cloned separately on the target.
| Flag | Effect |
|---|---|
--force |
Sync even if remote exists |
--dry-run |
Preview without changes |
--no-git |
Exclude .git/ directories |
-i, --interactive |
Launch TUI to select excludes before syncing |
--exclude <pattern> |
Add pattern to exclude (repeatable) |
--exclude-from <file> |
Read patterns from file (one per line, # comments) |
--include-env |
Include .env files (override default exclude) |
--list-excludes |
Print effective exclude list and exit |
Launch with co sync <workspace> <server> --interactive to select files/directories to exclude:
co sync acme--backend prod -iKeybindings:
| Key | Action |
|---|---|
j/k or ↑/↓ |
Navigate list |
space |
Toggle exclude (red ✗ = excluded) |
h/l or ←/→ |
Collapse/expand directory |
g/G |
Jump to top/bottom |
c |
Clear all exclusions |
r |
Reset to defaults |
enter |
Confirm and sync |
S |
Save selections to project.json and sync |
q or esc |
Cancel |
Default excludes start pre-selected. Excluding a directory excludes its entire subtree.
Exclude patterns can be persisted in project.json so they apply automatically on future syncs:
{
"sync": {
"excludes": {
"add": ["data/raw/", "*.sqlite"],
"remove": ["vendor/"]
},
"include_env": false
}
}add: Patterns to add to default excludesremove: Patterns to remove from default excludesinclude_env: Set totrueto include.envfiles
Use S in interactive mode to save your current selections to project.json.
Archives use git bundles to preserve full history without copying build artifacts.
~/Code/_system/archive/2025/acme--dashboard--20251213-143022.tar.gz
├── project.json
├── archive-meta.json
├── repos__frontend.bundle
└── repos__backend.bundle
- Avoids copying
node_modules,target/, etc. - Preserves all git history and unpushed work
- Restores without network access
co includes a semantic code search feature that lets you find code by meaning rather than exact text matching. For example, searching for "authentication middleware" can find auth-related functions even if they don't contain those exact words.
- AST-aware chunking — Uses tree-sitter to extract semantic units (functions, classes, methods) rather than arbitrary line splits
- Local embeddings — Generates vector embeddings via Ollama (runs locally, free)
- Vector similarity — Stores embeddings in SQLite with sqlite-vec for fast similarity search
Install and run Ollama:
# Install Ollama (macOS)
brew install ollama
# Start the server
ollama serve
# Pull the embedding model (768 dimensions, ~270MB)
ollama pull nomic-embed-textIndex codebases for semantic search.
# Index a specific codebase
co vector index acme--backend
# Index multiple codebases
co vector index acme--api acme--web
# Index all active codebases
co vector index --all
# Index with verbose output
co vector index acme--backend -vIndexing is incremental — unchanged files are skipped based on content hash.
Search for similar code across indexed codebases.
# Natural language query
co vector search "authentication middleware"
# Code pattern query
co vector search "func handleError(err error)"
# With code content preview
co vector search "database connection" --content
# Limit results
co vector search "api endpoint" -n 5
# Filter by codebase
co vector search "user model" --codebase acme--backend
# Use a file as query
co vector search --file example.go
# JSON output for scripting
co vector search "config parsing" --jsonShow index statistics.
co vector stats # All codebases
co vector stats --codebase acme # Specific codebase
co vector stats --json # JSON outputOutput includes:
- Total files and chunks indexed
- Per-codebase breakdown
- Language distribution
- Index freshness
Clear index data.
co vector clear acme--backend # Clear one codebase
co vector clear --all # Clear entire indexAdd to your config.json:
{
"embeddings": {
"backend": "ollama",
"ollama_url": "http://localhost:11434",
"ollama_model": "nomic-embed-text"
},
"indexing": {
"chunk_max_lines": 100,
"chunk_min_lines": 5,
"max_file_size_bytes": 1048576,
"batch_size": 50,
"workers": 4,
"exclude_patterns": [
"**/node_modules/**",
"**/vendor/**",
"**/.git/**"
]
}
}Tree-sitter parsing extracts semantic chunks from:
- Go
- Python
- JavaScript / TypeScript
- Rust
- Java
- C / C++
Other file types fall back to line-based chunking.
Search results include a similarity score (0-1):
- 0.15-0.20 — Strong semantic match
- 0.10-0.15 — Good match
- 0.05-0.10 — Weak match
- < 0.05 — Marginal relevance
Use --min-score to filter results:
co vector search "error handling" --min-score 0.1Vector data is stored in ~/Code/_system/vectors.db (SQLite with sqlite-vec extension).
The TUI provides:
- Project list — Browse all workspaces with status indicators
- Search — Fuzzy-find projects by name, owner, or tags
- Details panel — View repos, last activity, dirty state
- Quick actions — Open in editor, archive, sync
| Key | Action |
|---|---|
j/k or ↑/↓ |
Navigate list |
/ |
Search |
Enter |
Open workspace in editor |
a |
Archive workspace |
s |
Sync to server |
r |
Refresh index |
q |
Quit |
The Template Explorer (co template) provides a dedicated interface for working with workspace templates.
| Tab | Purpose |
|---|---|
| Browse | View all templates with details pane |
| Files | Browse template source files (coming soon) |
| Create | Create new workspace from selected template |
| Validate | Validate template manifests |
| Key | Action |
|---|---|
Tab / Shift+Tab |
Next / previous tab |
1-4 |
Jump to tab by number |
q / Ctrl+C |
Quit |
| Key | Action |
|---|---|
j/k or ↑/↓ |
Navigate template list |
/ |
Search templates |
l / → |
Switch to details pane |
h / ← |
Switch to list pane |
o |
Open template directory in editor |
v |
Validate selected template |
| Key | Action |
|---|---|
Tab / ↓ |
Next field |
Shift+Tab / ↑ |
Previous field |
Space |
Toggle checkbox (dry-run, no-hooks) |
Enter |
Submit / confirm |
Esc |
Cancel variable prompt or creation |
When a template has variables, you'll be prompted for each:
| Key | Action |
|---|---|
Enter |
Confirm value |
y/n |
Toggle boolean |
j/k |
Navigate choice list |
Esc |
Cancel and return to Create tab |
| Key | Action |
|---|---|
o |
Open created workspace in editor |
Enter / Esc |
Return to Create tab |
q |
Quit |
| Key | Action |
|---|---|
v |
Validate selected template |
V |
Validate all templates |
j/k or ↑/↓ |
Navigate results |
l/h |
Switch panes |
Templates are searched in multiple directories with the following precedence:
- Primary:
<code_root>/_system/templates/(e.g.,~/Code/_system/templates/) - Fallback:
~/.config/co/templates/(or$XDG_CONFIG_HOME/co/templates/)
When templates with the same name exist in multiple locations, the primary location takes precedence. The Template Explorer shows the source directory for each template.
The special _global directory contains files copied to ALL template-based workspaces:
~/Code/_system/templates/_global/
├── .editorconfig
├── .gitattributes
└── .claude/
└── settings.json
Global files from all template directories are merged, with primary taking precedence over fallback.
The Import Browser (co import-tui) provides an interactive interface for migrating existing folders into the co workspace structure. It's ideal for organizing legacy codebases, downloaded projects, or any folder structure that needs to be converted into proper workspaces.
The import process follows this flow:
Browse → Select → Configure → (Template) → (Extra Files) → Preview → Execute → Post-Import
- Browse — Navigate the folder tree to find projects to import
- Select — Choose a folder (single) or multiple folders (batch mode)
- Configure — Enter owner and project name for the workspace slug
- Template (optional) — Select a template to apply to the new workspace
- Extra Files (optional) — Select non-git files to include in the import
- Preview — Review the import operation before execution
- Execute — Create the workspace and move repositories
- Post-Import — Choose what to do with the source folder (keep/stash/delete)
| Key | Action |
|---|---|
j/k or ↑/↓ |
Navigate up/down |
h/l or ←/→ |
Collapse/expand directory |
g |
Jump to top |
G |
Jump to bottom |
Enter |
Toggle expand/collapse |
Space |
Toggle selection (for batch operations) |
/ |
Enter filter mode |
. |
Toggle hidden files |
r |
Refresh tree |
Tab |
Switch between tree and details pane |
i |
Import selected folder(s) |
s |
Stash selected folder(s) (keep source) |
S |
Stash selected folder(s) (delete source) |
d |
Delete selected folder (permanent, with confirmation) |
t |
Move selected folder to trash |
a |
Add to existing workspace |
q |
Quit |
| Key | Action |
|---|---|
Tab / ↓ |
Next field |
Shift+Tab / ↑ |
Previous field |
Enter |
Continue to next step |
Esc |
Cancel and return to browse |
| Key | Action |
|---|---|
j/k or ↑/↓ |
Navigate template list |
g/G |
Jump to top/bottom |
Enter |
Select template |
Esc |
Skip template selection |
| Key | Action |
|---|---|
j/k or ↑/↓ |
Navigate file list |
Space |
Toggle file selection |
a |
Select all |
n |
Select none |
Enter |
Confirm selection |
Esc |
Skip extra files |
| Key | Action |
|---|---|
Enter |
Execute import |
d |
Toggle dry-run mode |
Esc |
Go back |
| Key | Action |
|---|---|
j/k or ↑/↓ |
Navigate options |
1/2/3 |
Quick-select option |
Enter |
Confirm selection |
Options:
- Keep — Leave source folder in place
- Stash — Archive source folder to
_system/archive/ - Delete — Remove source folder (with confirmation)
The import browser automatically detects git repositories and displays:
- Repository status (clean/dirty)
- Current branch
- Nested repositories within folders
Folders containing git repos are highlighted with a special indicator.
Select multiple folders using Space, then:
- Press
ito batch import all selected folders - Press
sorSto batch stash all selected folders
Batch import prompts for a common owner, then creates separate workspaces using each folder's name as the project.
When importing, you can optionally apply a template to the new workspace. The template's files and hooks are applied after the repositories are moved into place.
Press a to add the selected folder's contents to an existing workspace instead of creating a new one. This is useful for consolidating related repositories.
| Indicator | Meaning |
|---|---|
📁 |
Regular directory |
📦 |
Git repository |
📂 |
Expanded directory |
✓ |
Selected for batch operation |
* |
Dirty git repository |
Import a single project:
1. co import-tui ~/old-projects
2. Navigate to the project folder
3. Press 'i' to import
4. Enter owner: "personal"
5. Confirm project name (auto-filled from folder name)
6. Press Enter to continue through preview
7. Choose to keep or stash the source
Batch import multiple projects:
1. co import-tui ~/client-work
2. Use j/k to navigate and Space to select folders
3. Press 'i' to start batch import
4. Enter common owner: "acme"
5. Review and confirm the batch
Add repos to existing workspace:
1. co import-tui ~/downloads
2. Navigate to a cloned repository
3. Press 'a' to add to existing workspace
4. Select the target workspace
5. Confirm the operation
.
├── cmd/
│ └── co/
│ ├── main.go # Entry point
│ └── cmd/ # Cobra commands
├── internal/
│ ├── archive/ # Git bundle archive builder
│ ├── chunker/ # AST-aware code chunking (tree-sitter)
│ ├── config/ # Config parsing + server resolution
│ ├── embedder/ # Embedding generation (Ollama client)
│ ├── fs/ # Workspace scanning, directory walking
│ ├── git/ # Git inspection (head, branch, dirty)
│ ├── index/ # Index generation + atomic write
│ ├── model/ # Data structures (project, index)
│ ├── search/ # Vector search indexing + querying
│ ├── sync/ # Remote sync (rsync/tar transport)
│ ├── template/ # Workspace templates + variable substitution
│ ├── tui/ # Bubble Tea models/views
│ └── vectordb/ # SQLite + sqlite-vec database
├── build/ # Build output
├── go.mod
├── go.sum
├── Makefile
└── README.md
Add this function to your ~/.zshrc or ~/.bashrc for instant workspace navigation:
ccd() { cd "$(co cd "$1")" }The co cd command supports fuzzy matching, so you can type partial names:
ccd dashboard # matches acme--dashboard
ccd api # matches acme--api-server
ccd acme # matches first acme--* workspaceAfter adding the function, reload your shell:
source ~/.zshrc # or source ~/.bashrc# Format code
make fmt
# Run linter
make lint
# Run tests
make test
# Build and run
make run
# Full check (fmt + lint + test)
make check- Single root — All workspaces under one directory
- Uniform structure — Every workspace has
project.json+repos/ - Metadata is truth —
project.jsonis canonical; index is derived - Index is generated — System can regenerate state from disk
- Safe by default — Destructive actions require
--forceor--delete
- Machine readability — Stable JSON schemas, predictable paths
- Searchability — Fast filtering/sorting via index
- Lifecycle — Archiving and syncing are first-class operations
- Speed — TUI feels instantaneous with hundreds of projects
MIT