Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions plugins/worktrunk/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ end-to-end against codex-cli 0.144.1 (scratch marketplaces through

## Known Limitations

### Status stays on the launch worktree (Claude)

Claude marker hooks resolve through `-C "$CLAUDE_PROJECT_DIR"`. `EnterWorktree` does not change that directory, so the marker stays on the launch worktree for the session. Sessions launched outside a repository have no activity marker.

### Status persists after user interrupt (Claude)

The Claude hooks track activity via git config (`worktrunk.state.{branch}.marker`):
Expand Down Expand Up @@ -150,6 +154,8 @@ The events (Codex's `HookEventsToml` vocabulary, verified against `codex-rs/conf

`Stop` fires at turn-end, so 🤖 returns to 💬 when a turn completes. `SessionEnd` clears the marker when the main thread ends.

Codex and Gemini marker commands still resolve from the hook process's cwd. When either harness exposes a stable session project directory, pass that directory to Worktrunk with global `-C`, as Claude does with `$CLAUDE_PROJECT_DIR`.
Comment thread
max-sixty marked this conversation as resolved.

### Accepted tradeoff: shared `skills/` exposes `wt-switch-create`

Codex's mirrored `skills/` and Gemini's `${extensionPath}/skills/` both carry the entire skill set, including `wt-switch-create`, which depends on Claude session-cwd switching (`EnterWorktree`) that neither provides. Accepted: a tool loading a skill it can't act on is harmless, and a single authored `skills/` keeps the `worktrunk` skill single-source across all three tools and the docs sync. Don't add per-tool skills subtrees to exclude it.
12 changes: 6 additions & 6 deletions plugins/worktrunk/hooks/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" config state marker set 🤖 || true"
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" -C \"$CLAUDE_PROJECT_DIR\" config state marker set 🤖 || true"
Comment thread
max-sixty marked this conversation as resolved.
}
]
}
Expand All @@ -16,7 +16,7 @@
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" config state marker set 💬 || true"
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" -C \"$CLAUDE_PROJECT_DIR\" config state marker set 💬 || true"
}
]
}
Expand All @@ -27,7 +27,7 @@
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" config state marker set 💬 || true"
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" -C \"$CLAUDE_PROJECT_DIR\" config state marker set 💬 || true"
}
]
}
Expand All @@ -38,7 +38,7 @@
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" config state marker set 💬 || true"
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" -C \"$CLAUDE_PROJECT_DIR\" config state marker set 💬 || true"
}
]
}
Expand All @@ -48,7 +48,7 @@
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" config state marker set 💬 || true"
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" -C \"$CLAUDE_PROJECT_DIR\" config state marker set 💬 || true"
}
]
}
Expand All @@ -59,7 +59,7 @@
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" config state marker clear || true"
"command": "bash \"$CLAUDE_PLUGIN_ROOT/hooks/wt.sh\" -C \"$CLAUDE_PROJECT_DIR\" config state marker clear || true"
}
]
}
Expand Down
19 changes: 19 additions & 0 deletions tests/integration_tests/config_show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3962,6 +3962,25 @@ fn test_plugin_layout_is_consolidated() {
);
}

let marker_commands = all_commands
.iter()
.filter(|command| command.contains("config state marker"))
.collect::<Vec<_>>();
assert_eq!(
marker_commands.len(),
6,
"expected all 6 Claude marker hooks (UserPromptSubmit, Notification, \
PreToolUse, PermissionRequest, Stop, SessionEnd); a newly added one \
must be pinned too. hooks.json:\n{hooks}"
);
for command in marker_commands {
assert!(
command.contains(r#"-C "$CLAUDE_PROJECT_DIR""#),
"marker hook must pin the directory it resolves against, or a shell \
`cd` during a turn retargets it to another repository (#3921). \
command:\n{command}"
);
}
let worktree_remove_cmd = hooks_json["hooks"]["WorktreeRemove"][0]["hooks"][0]["command"]
.as_str()
.expect("WorktreeRemove hook must define a command");
Expand Down
Loading