Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
8eb975e
ai(rules[check:*,implement]) Add tmuxinator parity commands
tony Feb 8, 2026
b68d09d
docs(comparison) Add feature comparison table for tmuxp/tmuxinator/te…
tony Feb 8, 2026
bd157ee
notes(parity) Add tmuxinator parity analysis
tony Feb 8, 2026
01544e1
notes(parity) Add teamocil parity analysis
tony Feb 8, 2026
aca877e
notes(import) Add tmuxinator import behavior analysis
tony Feb 8, 2026
0efa4b7
notes(import) Add teamocil import behavior analysis
tony Feb 8, 2026
61abc11
fix(docs): Correct parity docs from verification pass
tony Feb 8, 2026
a4b2ed3
fix(comparison): Correct tmuxinator min tmux and detach flag
tony Feb 8, 2026
db1811b
fix(import-tmuxinator): Add missing socket_path entry
tony Feb 8, 2026
c48ed72
fix(import-teamocil): Reclassify with_env_var and cmd_separator
tony Feb 8, 2026
42f5605
fix(comparison): Correct tmuxinator version ref and clarify details
tony Feb 8, 2026
c584bb2
fix(comparison): Annotate startup_window/startup_pane with tmuxp focu…
tony Feb 8, 2026
5e7b498
fix(parity-docs): Correct before_script hook mapping and --here details
tony Feb 8, 2026
a672022
fix(parity-docs): Correct line number references in teamocil notes
tony Feb 8, 2026
3282a2e
fix(comparison): Correct tmuxinator min tmux, add session rename note…
tony Feb 8, 2026
cd50db2
fix(parity-tmuxinator): Fix startup_window/pane semantics, pre_window…
tony Feb 8, 2026
b945c3c
fix(parity-teamocil): Add session rename behavior, fix with_env_var/c…
tony Feb 8, 2026
f0d929a
fix(import-tmuxinator): Correct pre/pre_window semantics and cli_args…
tony Feb 8, 2026
f7a75a3
fix(import-teamocil): Mark with_env_var/cmd_separator as unverified s…
tony Feb 8, 2026
481fde8
docs(plan): Add parity implementation plan with API blockers
tony Feb 8, 2026
db14a85
fix(plan): Correct line refs, add isinstance bug, expand details
tony Feb 8, 2026
e2b59be
fix(plan): Correct details from tmux source verification
tony Feb 8, 2026
a93b64a
fix(plan): Correct pane-title insertion points and teamocil line refe…
tony Feb 8, 2026
d06c900
fix(plan): Add tmux 3.2 sync scope detail and before_script limitation
tony Feb 8, 2026
0665e48
fix(plan): Add missing width drop note and importer update dependencies
tony Feb 8, 2026
88b5a4a
fix(plan): Fix phase numbering and add missing L3 to phase listing
tony Feb 8, 2026
e4c7ccd
fix(plan): Correct logging description and shlex bug example
tony Feb 8, 2026
6e21bd3
docs(comparison): Update version, fix hook descriptions, add auto-det…
tony Mar 6, 2026
79ee7d5
docs(parity-tmuxinator): Update version, add template execution order
tony Mar 6, 2026
0613488
docs(parity-teamocil): Update version and timestamp
tony Mar 6, 2026
3f0004e
docs(import): Update timestamps for import behavior notes
tony Mar 6, 2026
8fb29bb
docs(teamocil): Verify v0.x features from 0.4-stable branch
tony Mar 6, 2026
6030cc0
docs(comparison): Add teamocil v0.x pane sizing keys
tony Mar 6, 2026
47bbc44
docs(comparison): Refine auto-detection algorithm with v0.x fallback
tony Mar 6, 2026
95fa5ab
docs(import-teamocil): Document accidental focus/target passthrough
tony Mar 6, 2026
c048105
docs(parity-teamocil): Add accidental v0.x focus/target passthrough
tony Mar 6, 2026
2ca93c9
docs(comparison): Fix pre-build script and deprecated hook mappings
tony Mar 6, 2026
2e82f05
docs(import-tmuxinator): Document pre→before_script semantic bug
tony Mar 6, 2026
f0dd7c1
docs(parity-tmuxinator): Add pre→before_script scope bug to importer …
tony Mar 6, 2026
0b4d6f0
docs(plan): Add solo pre-to-before_script scope bug and update analysis
tony Mar 7, 2026
11c5e9d
docs(plan): Expand available libtmux APIs, fix stale teamocil claim, …
tony Mar 7, 2026
55e04ce
docs(plan): Add missing post and target keys from parity cross-reference
tony Mar 7, 2026
26d81ec
docs(comparison): Add synchronize deprecation, pane shell_command_bef…
tony Mar 7, 2026
20f5e5f
docs(parity-tmuxinator): Add synchronize deprecation and pane_title_f…
tony Mar 7, 2026
cc8df5b
docs(parity-teamocil): Add v1.0 rewrite context from README
tony Mar 7, 2026
a70491d
docs(import-tmuxinator): Note synchronize deprecation in summary table
tony Mar 7, 2026
6258093
docs(import-teamocil): Update date after source verification
tony Mar 7, 2026
f6364a4
docs(plan): Add synchronize deprecation context
tony Mar 7, 2026
4c878b6
docs(plan): Fix accuracy issues from issue #1016 review
tony Mar 7, 2026
1f26c6e
docs(parity-tmuxinator): Fix fallback order, add missing CLI details
tony Mar 7, 2026
dac633b
docs(parity-teamocil): Add layout-per-pane behavior and path expansion
tony Mar 7, 2026
aa37195
docs(plan): Add test coverage gaps section from fixture analysis
tony Mar 7, 2026
8a9c163
docs(import-tmuxinator): Add YAML aliases and numeric/emoji name find…
tony Mar 7, 2026
368ec52
docs(notes[plan]): Mark L1/L2/L3 resolved, fix stale line numbers, re…
tony Mar 15, 2026
a14574b
fix(import[teamocil]): Replace redundant filter loops with direct ass…
tony May 9, 2026
7c66184
fix(import[tmuxinator]): Map pre to before_script, warn on shell cons…
tony May 9, 2026
0037967
tests(import[tmuxinator]): Cover pre-to-before_script fix and shell-m…
tony May 9, 2026
ce87be3
fix(import[tmuxinator]): Use shlex to parse cli_args/tmux_options
tony May 9, 2026
3d4df50
tests(import[tmuxinator]): Cover cli_args/tmux_options shlex parsing
tony May 9, 2026
b48483c
feat(import[tmuxinator]): Add rvm, pre_tab, startup_*, socket_path, a…
tony May 9, 2026
4a74f79
tests(import[tmuxinator]): Cover rvm, startup_*, socket_path, attach
tony May 9, 2026
8c2e6bc
feat(import[teamocil]): Implement with_env_var, warn on cmd_separator…
tony May 9, 2026
ae13a59
tests(import[teamocil]): Cover with_env_var and TODO triage warnings
tony May 9, 2026
9aae4db
fix(import[teamocil]): Detect v0.x by markers when session wrapper om…
tony May 9, 2026
5a9265c
tests(import[teamocil]): Cover v1.x format dispatch and pane shorthand
tony May 9, 2026
4e9dbac
fix(import[teamocil]): Pop v0.x pane width/height/target with warning
tony May 9, 2026
23481f4
tests(import[teamocil]): Cover v0.x pane geometry-key drop warnings
tony May 9, 2026
f8472c8
docs(notes[plan]): Mark importer fixes resolved with implementation s…
tony May 9, 2026
b583588
docs(CHANGES): Note tmuxinator/teamocil import behavior changes
tony May 9, 2026
13e89e4
feat(builder): Handle synchronize config key with before/true/after
tony May 9, 2026
ac2dea8
tests(builder): Cover synchronize-panes config key
tony May 9, 2026
2625d28
feat(builder): Read window-level shell_command_after and send to each…
tony May 9, 2026
7803872
tests(builder): Cover window-level shell_command_after
tony May 9, 2026
e9304dc
feat(cli[load]): Add --no-shell-command-before flag
tony May 9, 2026
ea2542e
fix(loader): Apply no_shell_command_before guard to trickle propagation
tony May 9, 2026
e54330f
feat(builder): Pane title config keys + per-pane title
tony May 9, 2026
0bc9309
tests(builder): Cover pane titles + fix global_=True for window scope
tony May 9, 2026
d2ca89b
feat(builder): Wire on_project_start/first_start/restart lifecycle hooks
tony May 9, 2026
7ae5354
tests(util): Cover run_lifecycle_hook helper
tony May 9, 2026
69d21b0
feat(cli[manage]): Add new/copy/delete/implode workspace commands
tony May 9, 2026
948645a
tests(cli[manage]): Cover new/copy/delete/implode
tony May 9, 2026
df27abf
feat(template): In-house ${var} engine + load -D KEY=VALUE plumbing
tony May 9, 2026
6b754bb
tests(_internal/template): Cover render + parse_cli_vars
tony May 9, 2026
9fdeb20
feat(cli[stop]): Add tmuxp stop with on_project_stop hook
tony May 9, 2026
2224dcf
tests(cli[stop]): Cover tmuxp stop session kill + hook order
tony May 9, 2026
01e173d
feat(cli[load]): Add --here flag (load workspace into current session)
tony May 9, 2026
a2ac0c3
tests(cli[load]): Cover --here precondition errors
tony May 9, 2026
7603756
feat(cli[load]): Add --dry-run via isolated tmux socket
tony May 9, 2026
1268774
docs(CHANGES,notes[plan]): Document parity features and mark limitati…
tony May 9, 2026
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
80 changes: 80 additions & 0 deletions .claude/commands/check/parity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# /check:parity — Feature Parity Analysis

Deep-dive analysis of tmuxp vs tmuxinator and teamocil. Updates comparison docs and parity notes.

This command produces three artifacts:
- Feature parity notes (`notes/parity-*.md`)
- Import behavior notes (`notes/import-*.md`)
- Auto-detection heuristics (in `docs/comparison.md`)

## Workflow

1. **Read source code** of all three projects (see `.claude/commands/implement.md` for shared reference codebase paths). Focus on `src/tmuxp/workspace/`, `tmuxinator/lib/tmuxinator/{project,window,pane}.rb` + `hooks/` + `assets/template.erb`, and `teamocil/lib/teamocil/tmux/{session,window,pane}.rb`.

2. **Read existing docs** for baseline:
- `docs/about.md` — tmuxp's own feature description
- `docs/comparison.md` — feature comparison table (create if missing)
- `notes/parity-tmuxinator.md` — tmuxinator parity analysis (create if missing)
- `notes/parity-teamocil.md` — teamocil parity analysis (create if missing)

3. **Update `docs/comparison.md`** with tabular feature comparison:
- Overview table (language, min tmux, config format, architecture)
- Configuration keys table (every key across all three, with ✓/✗)
- CLI commands table (side-by-side)
- Architecture comparison (ORM vs script generation vs command objects)
- Include version numbers for each project

4. **Update `notes/parity-tmuxinator.md`** with:
- Features tmuxinator has that tmuxp lacks (with source locations)
- Import behavior analysis (what the current importer handles vs misses)
- WorkspaceBuilder requirements for 100% feature support
- Code quality issues in current importer

5. **Update `notes/parity-teamocil.md`** with:
- Features teamocil has that tmuxp lacks (with source locations)
- v0.x vs v1.4.2 format differences (current importer targets v0.x only)
- Import behavior analysis
- WorkspaceBuilder requirements for full parity

6. **Commit each file separately**

## Key areas to verify

- Check `importers.py` line-by-line against actual tmuxinator/teamocil config keys
- Verify `load_workspace()` actually reads config keys it claims to support
- Cross-reference CHANGELOGs for version-specific features
- Check test fixtures match real-world configs

---

# Import Behavior

Study tmuxp, teamocil, and tmuxinator source code. Find any syntax they support that tmuxp's native syntax doesn't.

Create/update:
- `notes/import-teamocil.md`
- `notes/import-tmuxinator.md`

## Syntax Level Differences / Limitations

For each config key and syntax pattern discovered, classify as:

### Differences (Translatable)

Syntax that differs but can be automatically converted during import. Document the mapping.

### Limitations (tmuxp needs to add support)

Syntax/features that cannot be imported because tmuxp lacks the underlying capability. For each, note:
1. What the feature does in the source tool
2. Why it can't be imported
3. What tmuxp would need to add

---

# WorkspaceBuilder

Analyze what WorkspaceBuilder needs to:

1. **Auto-detect config format** — Determine heuristics to identify tmuxinator vs teamocil vs tmuxp configs transparently
2. **100% feature support** — List every feature/behavior needed for complete compatibility, including behavioral idiosyncrasies
51 changes: 51 additions & 0 deletions .claude/commands/check/shortcomings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# /check:shortcomings — API Limitations Analysis

Second-step command that reads parity analysis and outputs API blockers to `notes/plan.md`.

## Input Files (from /check:parity)

- `notes/parity-tmuxinator.md`
- `notes/parity-teamocil.md`
- `notes/import-tmuxinator.md`
- `notes/import-teamocil.md`

## Workflow

1. **Read parity analysis files** to understand feature gaps

2. **Explore libtmux** at `~/work/python/libtmux/`:
- What APIs are missing? (e.g., no `pane.set_title()`)
- What's hardcoded? (e.g., `shutil.which("tmux")`)

3. **Explore tmuxp** at `~/work/python/tmuxp/`:
- What config keys are dead data?
- What keys are missing from loader/builder?
- What CLI flags are missing?

4. **Update `notes/plan.md`** with:
- libtmux limitations (what Server/Pane/Window/Session can't do)
- tmuxp limitations (what WorkspaceBuilder/loader/cli can't do)
- Dead config keys (imported but ignored)
- Required API additions for each gap
- Non-breaking implementation notes

5. **Commit** `notes/plan.md`

## Output Structure

notes/plan.md should follow this format:

### libtmux Limitations
Per-limitation:
- **Blocker**: What API is missing/hardcoded
- **Blocks**: What parity feature this prevents
- **Required**: What API addition is needed

### tmuxp Limitations
Per-limitation:
- **Blocker**: What's missing/broken
- **Blocks**: What parity feature this prevents
- **Required**: What change is needed

### Implementation Notes
Non-breaking approach for each limitation.
155 changes: 155 additions & 0 deletions .claude/commands/implement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# /implement — Plan and Implement from notes/plan.md

Orchestrates the full implementation workflow: plan → implement → test → verify → commit → document.

## Reference Codebases

- **tmuxinator**: `~/study/ruby/tmuxinator/`
- **teamocil**: `~/study/ruby/teamocil/`
- **tmux**: `~/study/c/tmux/`
- **libtmux**: `~/work/python/libtmux/`
- **tmuxp**: `~/work/python/tmuxp/`

## Workflow

### Phase 1: Planning Mode

1. **Read the plan**: Load `notes/plan.md` to understand what needs to be implemented
2. **Select a task**: Pick the highest priority incomplete item from the plan
3. **Research**:
- Read relevant tmuxinator/teamocil Ruby source for behavior reference
- Read libtmux Python source for available APIs
- Read tmuxp source for integration points
- **Study existing tests** for similar functionality (see Testing Pattern below)
4. **Create implementation plan**: Design the specific changes needed
5. **Exit planning mode** with the finalized approach

### Phase 2: Implementation

1. **Make changes**: Edit the necessary files
2. **Follow conventions**: Match existing code style, use type hints, add docstrings

### Phase 3: Write Tests

**CRITICAL**: Before running verification, write tests for new functionality.

1. **Find similar tests**: Search `tests/` for existing tests of similar features
2. **Follow the project test pattern** (see Testing Pattern below)
3. **Add test cases**: Cover normal cases, edge cases, and error conditions

### Phase 4: Verification

Run the full QA suite:

```bash
uv run ruff check . --fix --show-fixes
uv run ruff format .
uv run mypy
uv run py.test --reruns 0 -vvv
```

All checks must pass before proceeding.

### Phase 5: Commit Implementation

**Source and tests must be in separate commits.**

1. **Commit source code first**: Implementation changes only (e.g., `fix(cli): Read socket_name/path and config from workspace config`)
2. **Commit tests second**: Test files only (e.g., `tests(cli): Add config key precedence tests for load_workspace`)

Follow the project's commit conventions (e.g., `feat:`, `fix:`, `refactor:` for source; `tests:` or `tests(<scope>):` for tests).

### Phase 6: Update Documentation

1. **Update `notes/completed.md`**: Add entry for what was implemented
- Date
- What was done
- Files changed
- Any notes or follow-ups

2. **Update `notes/plan.md`**: Mark the item as complete or remove it

3. **Commit notes separately**: Use message like `notes: Mark <feature> as complete`

---

## Testing Pattern

This project uses a consistent test pattern. **Always follow this pattern for new tests.**

### 1. NamedTuple Fixture Class

```python
import typing as t

class MyFeatureTestFixture(t.NamedTuple):
"""Test fixture for my feature tests."""

# pytest (internal): Test fixture name
test_id: str

# test params
input_value: str
expected_output: str
expected_error: str | None = None
```

### 2. Fixture List

```python
TEST_MY_FEATURE_FIXTURES: list[MyFeatureTestFixture] = [
MyFeatureTestFixture(
test_id="normal-case",
input_value="foo",
expected_output="bar",
),
MyFeatureTestFixture(
test_id="edge-case-empty",
input_value="",
expected_output="",
),
MyFeatureTestFixture(
test_id="error-case",
input_value="bad",
expected_output="",
expected_error="Invalid input",
),
]
```

### 3. Parametrized Test Function

```python
@pytest.mark.parametrize(
"test",
TEST_MY_FEATURE_FIXTURES,
ids=[test.test_id for test in TEST_MY_FEATURE_FIXTURES],
)
def test_my_feature(test: MyFeatureTestFixture) -> None:
"""Test my feature with various inputs."""
result = my_function(test.input_value)
assert result == test.expected_output

if test.expected_error:
# check error handling
pass
```

See `CLAUDE.md` "Testing Guidelines" for the project-wide test conventions (function tests only, real fixtures, `tmp_path`, `monkeypatch`).

---

## Output

After completion, report:
- What was implemented
- Files changed (including test files)
- Test results summary
- What remains in the plan

## Notes

- If tests fail, fix the issues before committing
- If libtmux changes are needed, note them but don't modify libtmux in this workflow
- One logical change per run — don't implement multiple unrelated items
- **Always write tests** — No implementation is complete without tests
90 changes: 90 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,96 @@ $ pipx install \
_Notes on the upcoming release will go here._
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->

### New features — tmuxinator/teamocil parity (#1014)

These additions close the remaining feature gaps with tmuxinator and
teamocil, so configs from either tool now have a tmuxp-native equivalent.

**New config keys**

- **`synchronize`** (window-level) — sync keystrokes across the panes of a
window. Accepts `before` / `true` (turn `synchronize-panes` on before
pane commands run) or `after` (turn it on once each pane has finished
setup). Mirrors tmuxinator.
- **`enable_pane_titles`**, **`pane_title_position`** (default `top`),
**`pane_title_format`** (default `"#{pane_index}: #{pane_title}"`)
(session-level) plus per-pane **`title`**. Renders pane titles in the
border bar. tmuxinator's named-pane shorthand (`{name: command}`) is
imported as both a title and the pane command.
- **`shell_command_after`** (window-level) — list of commands sent to
every pane after each pane's main `shell_command` has run. Mirrors
teamocil's `filters.after`.
- **`on_project_start`**, **`on_project_first_start`**,
**`on_project_restart`**, **`on_project_exit`**, **`on_project_stop`**
(session-level) — lifecycle hooks. Each accepts a string or list. Hooks
reuse tmuxp's existing script executor: no `shell=True`, so pipes and
redirects need a real script file. `start` fires on every load,
`first_start` only on a brand-new session, `restart` only when
attaching to an existing one, `exit` after attach, and `stop` runs from
`tmuxp stop`.

**New CLI flags on `tmuxp load`**

- **`--here`** — load the workspace into the current tmux session
instead of creating a new one. Mutually exclusive with `--append`;
refuses to run outside a tmux session.
- **`--no-shell-command-before`** — skip every `shell_command_before`
(session, window, and pane). Slightly broader than tmuxinator's
`--no-pre-window`, which only suppresses `pre_window`.
- **`--dry-run`** — execute the build on an isolated tmux socket so your
main tmux server is untouched. tmuxp surfaces libtmux's
`tmux command dispatched` log at INFO so you can see the exact tmux
call sequence; the temporary server is killed when the run finishes.
- **`-D KEY=VALUE` / `--var KEY=VALUE`** (repeatable) — substitute
`${var}` and `${var:-default}` placeholders in the workspace YAML
before parsing, matching tmuxinator's ERB-before-YAML order. Bare
`$name` is intentionally not handled here so `loader.expandshell` can
keep handling environment-variable expansion on specific config keys
after parsing. Stdlib-only — no Jinja2 dependency.

**New CLI commands**

- **`tmuxp stop <name>`** — fire `on_project_stop` and kill the matching
session. Idempotent: a no-op if the session isn't running. Mirrors
tmuxinator's `stop` command.
- **`tmuxp new <name>`** — create a starter workspace YAML in your
tmuxp config directory and open it in `$EDITOR`.
- **`tmuxp copy <src> <dst>`** — copy an existing workspace and open the
copy in `$EDITOR`. Prompts before overwriting.
- **`tmuxp delete <name>...`** — delete one or more workspaces, with a
per-name confirmation unless `-y` is supplied.
- **`tmuxp implode`** — delete every workspace in every tmuxp config
directory (legacy `~/.tmuxp/` and XDG `~/.config/tmuxp/`). Single
confirmation; `-y` skips it.

### Behavior changes — tmuxinator/teamocil import (#1014)

- **tmuxinator**: `pre` now correctly maps to `before_script` (was: per-pane
`shell_command_before`). Importer warns when `pre` contains shell
constructs like pipes/redirects since `before_script` runs without
`shell=True`. Combo `pre` + `pre_window` previously dropped `pre`
entirely; both keys now map independently.
- **tmuxinator**: `cli_args`/`tmux_options` now use `shlex` parsing.
`-L` (socket name) and `-S` (socket path) are extracted (were silently
dropped). Paths containing `-f` no longer corrupt the config value.
Unknown flags and missing-value flags warn.
- **tmuxinator**: new key translations — `rvm`, `pre_tab`,
`startup_window`, `startup_pane`, `on_project_first_start`,
`socket_path`. `attach: false` warns and directs to `tmuxp load -d`.
`startup_window`/`startup_pane` resolve by name OR integer index to
set `focus: true` on the matching window/pane.
- **teamocil**: v1.x format now supported. Importer auto-dispatches to
v0.x or v1.x based on `session:` wrapper, `splits`/`filters`/`cmd`
markers. v1.x handles bare-string panes, `commands`, per-window/pane
`focus`, window `options`. v1.x pane dicts with no recognizable keys
produce empty panes and warn.
- **teamocil**: v0.x configs now emit `environment: {TEAMOCIL: "1"}` by
default (matches `with_env_var: true` from teamocil 0.4-stable).
Suppress with `with_env_var: false` (also accepts the YAML-quoted
string `"false"`).
- **teamocil**: v0.x pane `width`/`height`/`target` keys now drop with a
WARNING (were silently dropped or passed through as dead data).

### Documentation

- Visual improvements to API docs from [gp-sphinx](https://gp-sphinx.git-pull.com)-based Sphinx packages (#1035)
Expand Down
Loading
Loading