- name
- project-session-manager
- description
- Worktree-first dev environment manager for issues, PRs, and features with optional tmux sessions
- aliases
- ["psm"]
- level
- 2
# Project Session Manager (PSM) Skill
`psm` is the compatibility alias for this canonical skill entrypoint.
> **Quick Start (worktree-first):** Start with `omc teleport` when you want an isolated issue/PR/feature worktree before adding any tmux/session orchestration:
> ```bash
> omc teleport #123 # Create worktree for issue/PR
> omc teleport my-feature # Create worktree for feature
> omc teleport list # List worktrees
> ```
> See [Teleport Command](#teleport-command) below for details.
Automate isolated development environments using git worktrees and tmux sessions with Claude Code. Enables parallel work across multiple tasks, projects, and repositories.
Canonical slash command: `/oh-my-claudecode:project-session-manager` (alias: `/oh-my-claudecode:psm`).
## Commands
| Command | Description | Example |
|---------|-------------|---------|
| `review <ref>` | PR review session | `/psm review omc#123` |
| `fix <ref>` | Issue fix session | `/psm fix omc#42` |
| `feature <proj> <name>` | Feature development | `/psm feature omc add-webhooks` |
| `list [project]` | List active sessions | `/psm list` |
| `attach <session>` | Attach to session | `/psm attach omc:pr-123` |
| `kill <session>` | Kill session | `/psm kill omc:pr-123` |
| `cleanup` | Clean merged/closed | `/psm cleanup` |
| `status` | Current session info | `/psm status` |
## Project References
Supported formats:
- **Alias**: `omc#123` (requires `~/.psm/projects.json`)
- **Full**: `owner/repo#123`
- **URL**: `https://github.com/owner/repo/pull/123`
- **Current**: `#123` (uses current directory's repo)
## Configuration
### Project Aliases (`~/.psm/projects.json`)
```json
{
"aliases": {
"omc": {
"repo": "Yeachan-Heo/oh-my-claudecode",
"local": "~/Workspace/oh-my-claudecode",
"default_base": "main"
}
},
"defaults": {
"worktree_root": "~/.psm/worktrees",
"cleanup_after_days": 14
}
}
```
## Providers
PSM supports multiple issue tracking providers:
| Provider | CLI Required | Reference Formats | Commands |
|----------|--------------|-------------------|----------|
| GitHub (default) | `gh` | `owner/repo#123`, `alias#123`, GitHub URLs | review, fix, feature |
| Jira | `jira` | `PROJ-123` (if PROJ configured), `alias#123` | fix, feature |
### Jira Configuration
To use Jira, add an alias with `jira_project` and `provider: "jira"`:
```json
{
"aliases": {
"mywork": {
"jira_project": "MYPROJ",
"repo": "mycompany/my-project",
"local": "~/Workspace/my-project",
"default_base": "develop",
"provider": "jira"
}
}
}
```
**Important:** The `repo` field is still required for cloning the git repository. Jira tracks issues, but you work in a git repo.
For non-GitHub repos, use `clone_url` instead:
```json
{
"aliases": {
"private": {
"jira_project": "PRIV",
"clone_url": "git@gitlab.internal:team/repo.git",
"local": "~/Workspace/repo",
"provider": "jira"
}
}
}
```
### Jira Reference Detection
PSM only recognizes `PROJ-123` format as Jira when `PROJ` is explicitly configured as a `jira_project` in your aliases. This prevents false positives from branch names like `FIX-123`.
### Jira Examples
```bash
# Fix a Jira issue (MYPROJ must be configured)
psm fix MYPROJ-123
# Fix using alias (recommended)
psm fix mywork#123
# Feature development (works same as GitHub)
psm feature mywork add-webhooks
# Note: 'psm review' is not supported for Jira (no PR concept)
# Use 'psm fix' for Jira issues
```
### Jira CLI Setup
Install the Jira CLI:
```bash
# macOS
brew install ankitpokhrel/jira-cli/jira-cli
# Linux
# See: https://github.com/ankitpokhrel/jira-cli#installation
# Configure (interactive)
jira init
```
The Jira CLI handles authentication separately from PSM.
## Directory Structure
```
~/.psm/
├── projects.json # Project aliases
├── sessions.json # Active session registry
└── worktrees/ # Worktree storage
└── <project>/
└── <type>-<id>/
```
## Session Naming
The **public session ID** (colon form, e.g. `omc:pr-123`) is the human-facing
identifier stored in `sessions.json` and used with `psm attach`/`psm kill`. tmux
reserves `:` and `.` for its `session:window.pane` target syntax and silently
rewrites them, so the **actual tmux session name** uses a tmux-safe form where
those characters become `_` (issue #3528). PSM translates the public ID to the
tmux-safe name at every tmux boundary; attach directly with the tmux-safe name.
| Type | Public ID (`psm attach`/`kill`) | Tmux Session (`tmux attach -t`) | Worktree Dir |
|------|---------------------------------|---------------------------------|--------------|
| PR Review | `omc:pr-123` | `psm_omc_pr-123` | `~/.psm/worktrees/omc/pr-123` |
| Issue Fix | `omc:issue-42` | `psm_omc_issue-42` | `~/.psm/worktrees/omc/issue-42` |
| Feature | `omc:feat-auth` | `psm_omc_feat-auth` | `~/.psm/worktrees/omc/feat-auth` |
---
## Implementation Protocol
When the user invokes a PSM command, follow this protocol:
### Parse Arguments
Parse `{{ARGUMENTS}}` to determine:
1. **Subcommand**: review, fix, feature, list, attach, kill, cleanup, status
2. **Reference**: project#number, URL, or session ID
3. **Options**: --branch, --base, --no-claude, --no-tmux, etc.
### Subcommand: `review <ref>`
**Purpose**: Create PR review session
**Steps**:
1. **Resolve reference**:
```bash
# Read project aliases
cat ~/.psm/projects.json 2>/dev/null || echo '{"aliases":{}}'
# Parse ref format: alias#num, owner/repo#num, or URL
# Extract: project_alias, repo (owner/repo), pr_number, local_path
```
2. **Fetch PR info**:
```bash
gh pr view <pr_number> --repo <repo> --json number,title,author,headRefName,baseRefName,body,files,url
```
3. **Ensure local repo exists**:
```bash
# If local path doesn't exist, clone
if [[ ! -d "$local_path" ]]; then
git clone "https://github.com/$repo.git" "$local_path"
fi
```
4. **Create worktree**:
```bash
worktree_path="$HOME/.psm/worktrees/$project_alias/pr-$pr_number"
# Fetch PR branch
cd "$local_path"
git fetch origin "pull/$pr_number/head:pr-$pr_number-review"
# Create worktree
git worktree add "$worktree_path" "pr-$pr_number-review"
```
5. **Create session metadata**:
```bash
cat > "$worktree_path/.psm-session.json" << EOF
{
"id": "$project_alias:pr-$pr_number",
"type": "review",
"project": "$project_alias",
"ref": "pr-$pr_number",
"branch": "<head_branch>",
"base": "<base_branch>",
"created_at": "$(date -Iseconds)",
"tmux_session": "psm_${project_alias}_pr-$pr_number",
"worktree_path": "$worktree_path",
"source_repo": "$local_path",
"github": {
"pr_number": $pr_number,
"pr_title": "<title>",
"pr_author": "<author>",
"pr_url": "<url>"
},
"state": "active"
}
EOF
```
6. **Update sessions registry**:
```bash
# Add to ~/.psm/sessions.json
```
7. **Create tmux session** (tmux-safe name; `:`/`.` are translated to `_`):
```bash
tmux new-session -d -s "psm_${project_alias}_pr-$pr_number" -c "$worktree_path"
```
8. **Launch Claude Code** (unless --no-claude):
```bash
# --dangerously-skip-permissions prevents the "Do you trust this directory?" prompt
# and repeated tool-approval prompts from stalling the session (issue #2508).
tmux send-keys -t "psm_${project_alias}_pr-$pr_number" "claude --dangerously-skip-permissions" Enter
# After claude boots (PSM_CLAUDE_STARTUP_DELAY, default 5s), deliver the task.
# Use -l (literal) so special characters are not misinterpreted by tmux.
sleep "${PSM_CLAUDE_STARTUP_DELAY:-5}"
tmux send-keys -t "psm_${project_alias}_pr-$pr_number" -l \
"Review PR #$pr_number: \"$pr_title\" by @$pr_author ($head_branch → $base_branch). URL: $pr_url." Enter
```
9. **Output session info**:
```
Session ready!
ID: omc:pr-123
Worktree: ~/.psm/worktrees/omc/pr-123
Tmux: psm_omc_pr-123
To attach: tmux attach -t psm_omc_pr-123 (or: psm attach omc:pr-123)
```
### Subcommand: `fix <ref>`
**Purpose**: Create issue fix session
**Steps**:
1. **Resolve reference** (same as review)
2. **Fetch issue info**:
```bash
gh issue view <issue_number> --repo <repo> --json number,title,body,labels,url
```
3. **Create feature branch**:
```bash
cd "$local_path"
git fetch origin main
branch_name="fix/$issue_number-$(echo "$title" | tr ' ' '-' | tr '[:upper:]' '[:lower:]' | head -c 30)"
git checkout -b "$branch_name" origin/main
```
4. **Create worktree**:
```bash
worktree_path="$HOME/.psm/worktrees/$project_alias/issue-$issue_number"
git worktree add "$worktree_path" "$branch_name"
```
在 GitHub 查看