Initial commit
This commit is contained in:
758
commands/adopt.md
Normal file
758
commands/adopt.md
Normal file
@@ -0,0 +1,758 @@
|
||||
---
|
||||
description: Add .ai-context.json metadata to an existing worktree
|
||||
argument-hint: [--mode <mode>] [--description "<text>"]
|
||||
allowed-tools: Bash, Write, Read
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
# /adopt:working-tree
|
||||
|
||||
Generate `.ai-context.json` and `README.working-tree.md` for existing worktree lacking metadata.
|
||||
|
||||
## ARGUMENT SPECIFICATION
|
||||
|
||||
```
|
||||
SYNTAX: /adopt:working-tree [--mode <mode>] [--description "<text>"]
|
||||
|
||||
OPTIONAL:
|
||||
--mode <mode>
|
||||
Type: enum[main, feature, bugfix, experiment, review]
|
||||
Default: inferred from branch-name (see MODE_INFERENCE_ALGORITHM)
|
||||
Validation: must match exactly one of the enum values
|
||||
|
||||
--description "<text>"
|
||||
Type: string (quoted if contains spaces)
|
||||
Default: "" (empty string)
|
||||
Validation: any string, no restrictions
|
||||
```
|
||||
|
||||
## MODE_INFERENCE_ALGORITHM
|
||||
|
||||
APPLY rules sequentially, first match wins:
|
||||
|
||||
```python
|
||||
def infer_mode(branch_name: str) -> str:
|
||||
if branch_name.startswith("feature/"):
|
||||
return "feature"
|
||||
elif branch_name.startswith(("bugfix/", "fix/")):
|
||||
return "bugfix"
|
||||
elif branch_name.startswith(("exp/", "experiment/")):
|
||||
return "experiment"
|
||||
elif branch_name.startswith("review/"):
|
||||
return "review"
|
||||
elif branch_name in ("main", "master"):
|
||||
return "main"
|
||||
else:
|
||||
return "feature" # DEFAULT
|
||||
```
|
||||
|
||||
DETERMINISTIC: Given same branch_name, always produces same mode.
|
||||
|
||||
## EXECUTION PROTOCOL
|
||||
|
||||
Execute steps sequentially. Each step must complete successfully before proceeding.
|
||||
|
||||
### STEP 1: DETECT REPOSITORY INFO
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel 2>&1)
|
||||
EXIT_CODE_ROOT=$?
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>&1)
|
||||
EXIT_CODE_BRANCH=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE_ROOT != 0 → ERROR PATTERN "not-in-git-repo"
|
||||
- IF EXIT_CODE_BRANCH != 0 → ERROR PATTERN "git-command-failed"
|
||||
- REPO_ROOT must be absolute path starting with /
|
||||
|
||||
DATA EXTRACTION:
|
||||
```bash
|
||||
WORKTREE_NAME=$(basename "$REPO_ROOT")
|
||||
```
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 2
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 2: CHECK EXISTING METADATA
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
METADATA_PATH="$REPO_ROOT/.ai-context.json"
|
||||
test -f "$METADATA_PATH"
|
||||
EXISTS=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- EXISTS is 0 (exists) or 1 (does not exist)
|
||||
|
||||
ACTION:
|
||||
- IF EXISTS == 0 → STEP 3 (check for overwrite)
|
||||
- IF EXISTS == 1 → STEP 4 (proceed with creation)
|
||||
|
||||
NEXT:
|
||||
- Conditional based on EXISTS value
|
||||
|
||||
### STEP 3: HANDLE EXISTING METADATA
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
EXISTING_JSON=$(cat "$METADATA_PATH" 2>&1)
|
||||
EXISTING_MODE=$(echo "$EXISTING_JSON" | jq -r '.mode // "unknown"' 2>&1)
|
||||
EXISTING_DESC=$(echo "$EXISTING_JSON" | jq -r '.description // ""' 2>&1)
|
||||
EXISTING_CREATED=$(echo "$EXISTING_JSON" | jq -r '.created // ""' 2>&1)
|
||||
```
|
||||
|
||||
DISPLAY TO USER:
|
||||
```
|
||||
Current worktree already has metadata:
|
||||
|
||||
Directory: {WORKTREE_NAME}
|
||||
Branch: {CURRENT_BRANCH}
|
||||
Mode: {EXISTING_MODE}
|
||||
Created: {EXISTING_CREATED}
|
||||
Description: {EXISTING_DESC or "None"}
|
||||
|
||||
Do you want to overwrite this metadata?
|
||||
```
|
||||
|
||||
USER DECISION (use AskUserQuestion):
|
||||
- Option 1: "Keep existing metadata" → TERMINATE (no changes)
|
||||
- Option 2: "Overwrite with new metadata" → STEP 4 (proceed)
|
||||
- Option 3: "Cancel" → TERMINATE (no changes)
|
||||
|
||||
NEXT:
|
||||
- IF user selects overwrite → STEP 4
|
||||
- IF user selects keep/cancel → TERMINATE
|
||||
|
||||
### STEP 4: PARSE AND VALIDATE ARGUMENTS
|
||||
|
||||
PARSE:
|
||||
```bash
|
||||
# Parse --mode flag (if present)
|
||||
if [[ "$@" =~ --mode[[:space:]]+([a-z]+) ]]; then
|
||||
MODE_ARG="${BASH_REMATCH[1]}"
|
||||
else
|
||||
MODE_ARG=""
|
||||
fi
|
||||
|
||||
# Parse --description flag (if present)
|
||||
if [[ "$@" =~ --description[[:space:]]+\"([^\"]+)\" ]]; then
|
||||
DESCRIPTION="${BASH_REMATCH[1]}"
|
||||
elif [[ "$@" =~ --description[[:space:]]+([^[:space:]]+) ]]; then
|
||||
DESCRIPTION="${BASH_REMATCH[1]}"
|
||||
else
|
||||
DESCRIPTION=""
|
||||
fi
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF MODE_ARG not empty AND MODE_ARG not in [main, feature, bugfix, experiment, review] → ERROR PATTERN "invalid-mode"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 5
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 5: DETERMINE MODE
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
if [ -n "$MODE_ARG" ]; then
|
||||
# Explicit mode provided
|
||||
MODE="$MODE_ARG"
|
||||
else
|
||||
# Infer from branch name using MODE_INFERENCE_ALGORITHM
|
||||
case "$CURRENT_BRANCH" in
|
||||
feature/*)
|
||||
MODE="feature"
|
||||
;;
|
||||
bugfix/*|fix/*)
|
||||
MODE="bugfix"
|
||||
;;
|
||||
exp/*|experiment/*)
|
||||
MODE="experiment"
|
||||
;;
|
||||
review/*)
|
||||
MODE="review"
|
||||
;;
|
||||
main|master)
|
||||
MODE="main"
|
||||
;;
|
||||
*)
|
||||
MODE="feature"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- MODE must be exactly one of: main, feature, bugfix, experiment, review
|
||||
- MODE must not be empty
|
||||
|
||||
DATA:
|
||||
- MODE = final determined mode (explicit or inferred)
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 6
|
||||
- On failure → ABORT (should not occur if STEP 4 validation passed)
|
||||
|
||||
### STEP 6: GENERATE TIMESTAMP
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
CREATED_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
```
|
||||
|
||||
FORMAT: ISO 8601 UTC (example: 2025-11-23T12:34:56Z)
|
||||
|
||||
VALIDATION:
|
||||
- CREATED_TIMESTAMP must match pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 7
|
||||
- No failure case
|
||||
|
||||
### STEP 7: GENERATE .ai-context.json
|
||||
|
||||
CONTENT TEMPLATE:
|
||||
```json
|
||||
{
|
||||
"worktree": "{WORKTREE_NAME}",
|
||||
"branch": "{CURRENT_BRANCH}",
|
||||
"mode": "{MODE}",
|
||||
"created": "{CREATED_TIMESTAMP}",
|
||||
"description": "{DESCRIPTION}"
|
||||
}
|
||||
```
|
||||
|
||||
SUBSTITUTIONS:
|
||||
- {WORKTREE_NAME} = from STEP 1
|
||||
- {CURRENT_BRANCH} = from STEP 1
|
||||
- {MODE} = from STEP 5
|
||||
- {CREATED_TIMESTAMP} = from STEP 6
|
||||
- {DESCRIPTION} = from STEP 4 (empty string if not provided)
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
cat > "$REPO_ROOT/.ai-context.json" <<EOF
|
||||
{
|
||||
"worktree": "$WORKTREE_NAME",
|
||||
"branch": "$CURRENT_BRANCH",
|
||||
"mode": "$MODE",
|
||||
"created": "$CREATED_TIMESTAMP",
|
||||
"description": "$DESCRIPTION"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- File must be created at exact path: $REPO_ROOT/.ai-context.json
|
||||
- File must contain valid JSON
|
||||
- Verify with: `jq empty "$REPO_ROOT/.ai-context.json"`
|
||||
- IF jq fails → ERROR PATTERN "metadata-write-failed"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 8
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 8: GENERATE README.working-tree.md (IF MISSING)
|
||||
|
||||
CHECK:
|
||||
```bash
|
||||
README_PATH="$REPO_ROOT/README.working-tree.md"
|
||||
test -f "$README_PATH"
|
||||
README_EXISTS=$?
|
||||
```
|
||||
|
||||
ACTION:
|
||||
- IF README_EXISTS == 0 (exists) → SKIP to STEP 9 (don't overwrite)
|
||||
- IF README_EXISTS == 1 (missing) → Create README
|
||||
|
||||
CONTENT TEMPLATE:
|
||||
```markdown
|
||||
# Worktree: {WORKTREE_NAME}
|
||||
|
||||
**Branch:** `{CURRENT_BRANCH}`
|
||||
**Mode:** `{MODE}`
|
||||
**Created:** {CREATED_TIMESTAMP}
|
||||
|
||||
## Purpose
|
||||
|
||||
{DESCRIPTION_OR_DEFAULT}
|
||||
|
||||
## Mode Semantics
|
||||
|
||||
- **main**: Minimal changes, stable work only
|
||||
- **feature**: Active development, larger changes allowed
|
||||
- **bugfix**: Isolated, surgical fixes only
|
||||
- **experiment**: Prototypes, large swings, unsafe changes allowed
|
||||
- **review**: Documentation, analysis, audits
|
||||
|
||||
## About This Worktree
|
||||
|
||||
This directory is an independent Git worktree attached to the main repository.
|
||||
|
||||
- Main repo: {REPO_ROOT}
|
||||
- Worktree path: {REPO_ROOT}
|
||||
- Branch: {CURRENT_BRANCH}
|
||||
|
||||
See `.ai-context.json` for machine-readable metadata.
|
||||
```
|
||||
|
||||
SUBSTITUTIONS:
|
||||
- {WORKTREE_NAME} = from STEP 1
|
||||
- {CURRENT_BRANCH} = from STEP 1
|
||||
- {MODE} = from STEP 5
|
||||
- {CREATED_TIMESTAMP} = from STEP 6
|
||||
- {DESCRIPTION_OR_DEFAULT} = DESCRIPTION if not empty, else "No description provided"
|
||||
- {REPO_ROOT} = from STEP 1
|
||||
|
||||
EXECUTE (only if README_EXISTS == 1):
|
||||
```bash
|
||||
DESCRIPTION_TEXT="${DESCRIPTION:-No description provided}"
|
||||
cat > "$REPO_ROOT/README.working-tree.md" <<EOF
|
||||
# Worktree: $WORKTREE_NAME
|
||||
|
||||
**Branch:** \`$CURRENT_BRANCH\`
|
||||
**Mode:** \`$MODE\`
|
||||
**Created:** $CREATED_TIMESTAMP
|
||||
|
||||
## Purpose
|
||||
|
||||
$DESCRIPTION_TEXT
|
||||
|
||||
## Mode Semantics
|
||||
|
||||
- **main**: Minimal changes, stable work only
|
||||
- **feature**: Active development, larger changes allowed
|
||||
- **bugfix**: Isolated, surgical fixes only
|
||||
- **experiment**: Prototypes, large swings, unsafe changes allowed
|
||||
- **review**: Documentation, analysis, audits
|
||||
|
||||
## About This Worktree
|
||||
|
||||
This directory is an independent Git worktree attached to the main repository.
|
||||
|
||||
- Main repo: $REPO_ROOT
|
||||
- Worktree path: $REPO_ROOT
|
||||
- Branch: $CURRENT_BRANCH
|
||||
|
||||
See \`.ai-context.json\` for machine-readable metadata.
|
||||
EOF
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF created, file must exist at: $REPO_ROOT/README.working-tree.md
|
||||
- IF file creation failed → Warning (not fatal)
|
||||
|
||||
NEXT:
|
||||
- On success or skip → STEP 9
|
||||
- On failure → Warning (continue)
|
||||
|
||||
### STEP 9: OUTPUT SUCCESS SUMMARY
|
||||
|
||||
OUTPUT FORMAT (exact):
|
||||
```
|
||||
Adopted worktree successfully!
|
||||
|
||||
Directory: {WORKTREE_NAME}
|
||||
Branch: {CURRENT_BRANCH}
|
||||
Mode: {MODE}
|
||||
Description: {DESCRIPTION_OR_NONE}
|
||||
|
||||
Metadata files created:
|
||||
✓ .ai-context.json
|
||||
{README_STATUS}
|
||||
|
||||
Use /status:working-tree to view metadata anytime.
|
||||
```
|
||||
|
||||
SUBSTITUTIONS:
|
||||
- {WORKTREE_NAME} = from STEP 1
|
||||
- {CURRENT_BRANCH} = from STEP 1
|
||||
- {MODE} = from STEP 5
|
||||
- {DESCRIPTION_OR_NONE} = DESCRIPTION if not empty, else "None"
|
||||
- {README_STATUS} = "✓ README.working-tree.md (created)" if created, "- README.working-tree.md (already exists)" if skipped
|
||||
|
||||
NEXT:
|
||||
- TERMINATE (success)
|
||||
|
||||
## ERROR PATTERNS
|
||||
|
||||
### PATTERN: not-in-git-repo
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git rev-parse --show-toplevel exit code != 0
|
||||
- INDICATORS: stderr contains "not a git repository"
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Not in a git repository
|
||||
|
||||
Run this command from within a git repository.
|
||||
|
||||
To create a new worktree with metadata:
|
||||
/create:working-tree <branch-name>
|
||||
```
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: git-command-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: Any git command (except rev-parse --show-toplevel) returns non-zero exit code
|
||||
- CAPTURE: stderr from git command
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to read git information
|
||||
|
||||
Git error: {GIT_STDERR}
|
||||
|
||||
Check that:
|
||||
- You're in a git repository
|
||||
- Git is installed and working
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {GIT_STDERR} = captured stderr
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: invalid-mode
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: MODE_ARG provided but not in [main, feature, bugfix, experiment, review]
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Invalid mode '{MODE_ARG}'
|
||||
|
||||
Valid modes: main, feature, bugfix, experiment, review
|
||||
|
||||
Example:
|
||||
/adopt:working-tree --mode feature --description "new feature work"
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {MODE_ARG} = the invalid mode provided
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: metadata-write-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: .ai-context.json write fails or invalid JSON (STEP 7)
|
||||
- CHECK: jq validation fails
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to write .ai-context.json
|
||||
|
||||
Write error: {ERROR_MESSAGE}
|
||||
|
||||
Check that:
|
||||
- You have write permission in this directory
|
||||
- Disk space is available
|
||||
- No file conflicts exist
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {ERROR_MESSAGE} = error message from write operation
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: readme-write-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: README.working-tree.md write fails (STEP 8)
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Warning: Failed to write README.working-tree.md
|
||||
|
||||
The .ai-context.json was created successfully.
|
||||
|
||||
You can manually create the README if needed.
|
||||
```
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: false (warning, not critical)
|
||||
- CLEANUP: none
|
||||
- FALLBACK: Continue without README
|
||||
|
||||
## TOOL PERMISSION MATRIX
|
||||
|
||||
| Tool | Pattern | Permission | Pre-Check | Post-Check | On-Deny-Action |
|
||||
|------|---------|------------|-----------|------------|----------------|
|
||||
| Bash | git:* | ALLOW | command_safe | validate_output | N/A |
|
||||
| Bash | date:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | test:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | basename:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | cat > *.json | ALLOW | dir_writable | valid_json | N/A |
|
||||
| Bash | cat > *.md | ALLOW | dir_writable | N/A | N/A |
|
||||
| Bash | jq:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | rm:* | DENY | N/A | N/A | ABORT "Destructive operation" |
|
||||
| Bash | sudo:* | DENY | N/A | N/A | ABORT "Elevated privileges" |
|
||||
| Write | $REPO_ROOT/.ai-context.json | ALLOW | dir_exists | valid_json | N/A |
|
||||
| Write | $REPO_ROOT/README.working-tree.md | ALLOW | dir_exists | N/A | N/A |
|
||||
| Write | **/.env* | DENY | N/A | N/A | ABORT "Secrets file" |
|
||||
| Read | $REPO_ROOT/.ai-context.json | ALLOW | file_exists | N/A | N/A |
|
||||
|
||||
SECURITY CONSTRAINTS:
|
||||
- Can only write to current worktree directory (REPO_ROOT)
|
||||
- Cannot modify files outside current worktree
|
||||
- Cannot execute destructive operations
|
||||
- All file writes must be to metadata files only
|
||||
|
||||
## TEST CASES
|
||||
|
||||
### TC001: Adopt worktree without metadata
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository at /Users/dev/myapp
|
||||
- Current branch: feature/login
|
||||
- File does NOT exist: /Users/dev/myapp/.ai-context.json
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/adopt:working-tree
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → REPO_ROOT="/Users/dev/myapp", WORKTREE_NAME="myapp", CURRENT_BRANCH="feature/login"
|
||||
2. STEP 2 → EXISTS=1 (no metadata)
|
||||
3. STEP 4 → MODE_ARG="", DESCRIPTION=""
|
||||
4. STEP 5 → MODE="feature" (inferred from "feature/" prefix)
|
||||
5. STEP 6 → Generate timestamp
|
||||
6. STEP 7 → Write .ai-context.json
|
||||
7. STEP 8 → README_EXISTS=1, create README
|
||||
8. STEP 9 → Output summary
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Adopted worktree successfully!
|
||||
|
||||
Directory: myapp
|
||||
Branch: feature/login
|
||||
Mode: feature
|
||||
Description: None
|
||||
|
||||
Metadata files created:
|
||||
✓ .ai-context.json
|
||||
✓ README.working-tree.md (created)
|
||||
|
||||
Use /status:working-tree to view metadata anytime.
|
||||
```
|
||||
|
||||
VALIDATION COMMANDS:
|
||||
```bash
|
||||
# Verify .ai-context.json created
|
||||
test -f /Users/dev/myapp/.ai-context.json && echo "PASS" || echo "FAIL"
|
||||
jq -r '.mode' /Users/dev/myapp/.ai-context.json | grep "feature" && echo "PASS" || echo "FAIL"
|
||||
|
||||
# Verify README created
|
||||
test -f /Users/dev/myapp/README.working-tree.md && echo "PASS" || echo "FAIL"
|
||||
```
|
||||
|
||||
### TC002: Adopt with explicit mode and description
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository at /Users/dev/myapp
|
||||
- Current branch: main
|
||||
- No existing metadata
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/adopt:working-tree --mode main --description "Primary development branch"
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-2. Standard detection
|
||||
3. STEP 4 → MODE_ARG="main", DESCRIPTION="Primary development branch"
|
||||
4. STEP 5 → MODE="main" (explicit, not inferred)
|
||||
5-8. Standard flow
|
||||
|
||||
EXPECTED .ai-context.json:
|
||||
```json
|
||||
{
|
||||
"worktree": "myapp",
|
||||
"branch": "main",
|
||||
"mode": "main",
|
||||
"created": "2025-11-23T12:34:56Z",
|
||||
"description": "Primary development branch"
|
||||
}
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
```bash
|
||||
jq -r '.mode' .ai-context.json | grep "main" && echo "PASS" || echo "FAIL"
|
||||
jq -r '.description' .ai-context.json | grep "Primary development branch" && echo "PASS" || echo "FAIL"
|
||||
```
|
||||
|
||||
### TC003: Metadata already exists - user keeps existing
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository at /Users/dev/myapp
|
||||
- File exists: /Users/dev/myapp/.ai-context.json with valid data
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/adopt:working-tree --mode experiment
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → Detect repo
|
||||
2. STEP 2 → EXISTS=0 (metadata exists)
|
||||
3. STEP 3 → Display existing metadata, ask user
|
||||
4. USER SELECTS "Keep existing metadata"
|
||||
5. TERMINATE (no changes)
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Current worktree already has metadata:
|
||||
|
||||
Directory: myapp
|
||||
Branch: feature/login
|
||||
Mode: feature
|
||||
Created: 2025-11-20T10:00:00Z
|
||||
Description: Original description
|
||||
|
||||
Do you want to overwrite this metadata?
|
||||
```
|
||||
|
||||
POSTCONDITIONS:
|
||||
- .ai-context.json unchanged
|
||||
- No files modified
|
||||
|
||||
### TC004: Metadata already exists - user overwrites
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository with existing .ai-context.json
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/adopt:working-tree --mode experiment --description "New description"
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-2. Detect repo, metadata exists
|
||||
3. STEP 3 → Display existing, ask user
|
||||
4. USER SELECTS "Overwrite with new metadata"
|
||||
5-9. Continue with creation (new timestamp, new mode, new description)
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Current worktree already has metadata:
|
||||
[existing data shown]
|
||||
|
||||
Do you want to overwrite this metadata?
|
||||
|
||||
[User confirms overwrite]
|
||||
|
||||
Adopted worktree successfully!
|
||||
|
||||
Directory: myapp
|
||||
Branch: feature/login
|
||||
Mode: experiment
|
||||
Description: New description
|
||||
|
||||
Metadata files created:
|
||||
✓ .ai-context.json
|
||||
- README.working-tree.md (already exists)
|
||||
|
||||
Use /status:working-tree to view metadata anytime.
|
||||
```
|
||||
|
||||
### TC005: README already exists
|
||||
|
||||
PRECONDITIONS:
|
||||
- No .ai-context.json
|
||||
- README.working-tree.md already exists
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/adopt:working-tree
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-7. Standard flow, create .ai-context.json
|
||||
8. STEP 8 → README_EXISTS=0 (exists), skip creation
|
||||
9. STEP 9 → Output shows README skipped
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Adopted worktree successfully!
|
||||
|
||||
Directory: myapp
|
||||
Branch: feature/login
|
||||
Mode: feature
|
||||
Description: None
|
||||
|
||||
Metadata files created:
|
||||
✓ .ai-context.json
|
||||
- README.working-tree.md (already exists)
|
||||
|
||||
Use /status:working-tree to view metadata anytime.
|
||||
```
|
||||
|
||||
## USE CASES
|
||||
|
||||
### UC001: Adopting main repository
|
||||
When working in main repo without worktrees:
|
||||
```bash
|
||||
cd ~/myapp
|
||||
/adopt:working-tree --mode main --description "Primary development branch"
|
||||
```
|
||||
|
||||
### UC002: Adopting manually created worktree
|
||||
If worktree created without /create:working-tree:
|
||||
```bash
|
||||
cd ../myapp-feature-something
|
||||
/adopt:working-tree
|
||||
```
|
||||
|
||||
### UC003: Adding description later
|
||||
If worktree created without description:
|
||||
```bash
|
||||
/adopt:working-tree --description "Working on user authentication refactor"
|
||||
```
|
||||
(Will prompt to overwrite existing metadata)
|
||||
|
||||
### UC004: Correcting mode
|
||||
If mode was inferred incorrectly:
|
||||
```bash
|
||||
/adopt:working-tree --mode experiment
|
||||
```
|
||||
(Will prompt to overwrite existing metadata)
|
||||
|
||||
## RELATED COMMANDS
|
||||
|
||||
- /status:working-tree - View current metadata after adoption
|
||||
- /create:working-tree - Create new worktree with metadata from start
|
||||
- /list:working-tree - See all worktrees and their metadata status
|
||||
|
||||
## DELEGATION
|
||||
|
||||
For organizing worktree adoption strategy:
|
||||
```
|
||||
Task(
|
||||
subagent_type='working-tree-consultant',
|
||||
description='Worktree adoption strategy',
|
||||
prompt='[question about when/how to adopt worktrees]'
|
||||
)
|
||||
```
|
||||
673
commands/destroy.md
Normal file
673
commands/destroy.md
Normal file
@@ -0,0 +1,673 @@
|
||||
---
|
||||
description: Safely remove a git worktree and its metadata (preserves branch)
|
||||
argument-hint: <worktree-path>
|
||||
allowed-tools: Bash, Read
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
# /destroy:working-tree
|
||||
|
||||
Safely remove git worktree directory and metadata files. Branch is preserved.
|
||||
|
||||
## ARGUMENT SPECIFICATION
|
||||
|
||||
```
|
||||
SYNTAX: /destroy:working-tree <worktree-path>
|
||||
|
||||
REQUIRED:
|
||||
<worktree-path>
|
||||
Type: path (absolute or relative)
|
||||
Validation: Must be registered git worktree
|
||||
Examples: "../myapp-feature-login", "/Users/dev/myapp-feature-login"
|
||||
```
|
||||
|
||||
## EXECUTION PROTOCOL
|
||||
|
||||
Execute steps sequentially. Each step must complete successfully before proceeding.
|
||||
|
||||
### STEP 1: VALIDATE AND RESOLVE WORKTREE PATH
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
# Resolve to absolute path
|
||||
if [[ "$WORKTREE_PATH_ARG" = /* ]]; then
|
||||
WORKTREE_PATH="$WORKTREE_PATH_ARG"
|
||||
else
|
||||
WORKTREE_PATH=$(cd "$(dirname "$WORKTREE_PATH_ARG")" && pwd)/$(basename "$WORKTREE_PATH_ARG")
|
||||
fi
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF WORKTREE_PATH_ARG is empty → ERROR PATTERN "missing-path"
|
||||
- WORKTREE_PATH must be absolute after resolution
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 2
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 2: CHECK PATH EXISTS
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
test -e "$WORKTREE_PATH"
|
||||
EXISTS=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXISTS != 0 → ERROR PATTERN "path-not-exist"
|
||||
|
||||
NEXT:
|
||||
- On EXISTS == 0 → STEP 3
|
||||
- On EXISTS != 0 → ABORT
|
||||
|
||||
### STEP 3: GET ALL WORKTREES AND VALIDATE
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
WORKTREE_LIST=$(git worktree list --porcelain 2>&1)
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → ERROR PATTERN "git-command-failed"
|
||||
|
||||
PARSE WORKTREE_LIST:
|
||||
```bash
|
||||
# Extract all worktree paths and branches
|
||||
# Format: worktree /path\nHEAD hash\nbranch refs/heads/name\n\n
|
||||
CURRENT_MAIN=$(echo "$WORKTREE_LIST" | head -1 | cut -d' ' -f2)
|
||||
IS_MAIN_REPO=false
|
||||
|
||||
if [ "$WORKTREE_PATH" = "$CURRENT_MAIN" ]; then
|
||||
IS_MAIN_REPO=true
|
||||
fi
|
||||
|
||||
# Find worktree entry for target path
|
||||
WORKTREE_ENTRY=$(echo "$WORKTREE_LIST" | grep -A 3 "^worktree $WORKTREE_PATH$")
|
||||
IS_REGISTERED=$(echo "$WORKTREE_ENTRY" | wc -l)
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF IS_MAIN_REPO == true → ERROR PATTERN "cannot-destroy-main"
|
||||
- IF IS_REGISTERED == 0 → ERROR PATTERN "not-a-worktree"
|
||||
|
||||
DATA EXTRACTION:
|
||||
```bash
|
||||
BRANCH_REF=$(echo "$WORKTREE_ENTRY" | grep "^branch " | cut -d' ' -f2)
|
||||
BRANCH_NAME=$(echo "$BRANCH_REF" | sed 's|refs/heads/||')
|
||||
```
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 4
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 4: CHECK FOR UNCOMMITTED CHANGES
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
cd "$WORKTREE_PATH"
|
||||
GIT_STATUS=$(git status --porcelain 2>&1)
|
||||
STATUS_EXIT=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF STATUS_EXIT != 0 → Warning (worktree may be corrupted, allow removal)
|
||||
|
||||
DETECTION:
|
||||
```bash
|
||||
if [ -n "$GIT_STATUS" ]; then
|
||||
HAS_CHANGES=true
|
||||
else
|
||||
HAS_CHANGES=false
|
||||
fi
|
||||
```
|
||||
|
||||
ACTION:
|
||||
- IF HAS_CHANGES == false → STEP 5 (proceed directly)
|
||||
- IF HAS_CHANGES == true → Display warning, ask user confirmation
|
||||
|
||||
USER DECISION (if HAS_CHANGES == true):
|
||||
```
|
||||
⚠ Warning: Uncommitted changes detected
|
||||
|
||||
Modified files:
|
||||
{GIT_STATUS output}
|
||||
|
||||
These changes will be lost if you proceed.
|
||||
|
||||
Recommendations:
|
||||
1. Commit changes: cd {WORKTREE_PATH} && git commit -am "message"
|
||||
2. Stash changes: cd {WORKTREE_PATH} && git stash
|
||||
3. Proceed anyway (changes will be lost)
|
||||
|
||||
Proceed with removal? (This will permanently delete uncommitted work)
|
||||
```
|
||||
|
||||
Use AskUserQuestion:
|
||||
- Option 1: "Cancel removal" → TERMINATE (no changes)
|
||||
- Option 2: "Proceed with removal (discard changes)" → STEP 5
|
||||
|
||||
NEXT:
|
||||
- IF user cancels → TERMINATE
|
||||
- IF user proceeds OR no changes → STEP 5
|
||||
|
||||
### STEP 5: REMOVE WORKTREE
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
git worktree remove --force "$WORKTREE_PATH" 2>&1
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → ERROR PATTERN "worktree-removal-failed"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 6
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 6: PRUNE STALE REFERENCES
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
git worktree prune 2>&1
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → Warning (not fatal, removal succeeded)
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 7
|
||||
- On warning → STEP 7 (continue)
|
||||
|
||||
### STEP 7: OUTPUT SUCCESS SUMMARY
|
||||
|
||||
OUTPUT FORMAT (exact):
|
||||
```
|
||||
✓ Worktree removed successfully
|
||||
|
||||
Path: {WORKTREE_PATH}
|
||||
Branch: {BRANCH_NAME}
|
||||
|
||||
Branch '{BRANCH_NAME}' has been preserved.
|
||||
|
||||
To delete the branch as well:
|
||||
git branch -d {BRANCH_NAME} # Safe delete (only if merged)
|
||||
git branch -D {BRANCH_NAME} # Force delete (even if unmerged)
|
||||
|
||||
To delete remote branch:
|
||||
git push origin --delete {BRANCH_NAME}
|
||||
```
|
||||
|
||||
SUBSTITUTIONS:
|
||||
- {WORKTREE_PATH} = from STEP 1
|
||||
- {BRANCH_NAME} = from STEP 3
|
||||
|
||||
NEXT:
|
||||
- TERMINATE (success)
|
||||
|
||||
## ERROR PATTERNS
|
||||
|
||||
### PATTERN: missing-path
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: WORKTREE_PATH_ARG is empty (no argument provided)
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Missing worktree path
|
||||
|
||||
Usage:
|
||||
/destroy:working-tree <worktree-path>
|
||||
|
||||
Example:
|
||||
/destroy:working-tree ../myapp-feature-login
|
||||
|
||||
To see all worktrees:
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: path-not-exist
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: Provided path does not exist (STEP 2)
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Path does not exist
|
||||
|
||||
Path: {WORKTREE_PATH}
|
||||
|
||||
The specified path doesn't exist. Check for typos.
|
||||
|
||||
To list existing worktrees:
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {WORKTREE_PATH} = provided path
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: git-command-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git worktree list command fails (STEP 3)
|
||||
- CAPTURE: stderr from git command
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to list worktrees
|
||||
|
||||
Git error: {GIT_STDERR}
|
||||
|
||||
Check that:
|
||||
- You're in a git repository
|
||||
- Git is installed and working
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {GIT_STDERR} = captured stderr
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: cannot-destroy-main
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: Target path matches main repository path (STEP 3)
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Cannot destroy main repository
|
||||
|
||||
The path '{WORKTREE_PATH}' is the main repository, not a worktree.
|
||||
|
||||
To remove worktrees, use paths like:
|
||||
/destroy:working-tree ../myapp-feature-branch
|
||||
/destroy:working-tree ../myapp-bugfix-something
|
||||
|
||||
To see all worktrees:
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {WORKTREE_PATH} = main repository path
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: not-a-worktree
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: Path exists but is not registered as git worktree (STEP 3)
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Not a registered git worktree
|
||||
|
||||
Path: {WORKTREE_PATH}
|
||||
|
||||
This path is not a git worktree. To see all worktrees:
|
||||
/list:working-tree
|
||||
|
||||
Valid worktree paths look like:
|
||||
/Users/dev/myapp-feature-login
|
||||
../myapp-bugfix-auth
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {WORKTREE_PATH} = provided path
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: worktree-removal-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git worktree remove fails (STEP 5)
|
||||
- CAPTURE: stderr from git worktree remove
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to remove worktree
|
||||
|
||||
Git error: {GIT_STDERR}
|
||||
|
||||
This can happen if:
|
||||
- Worktree is locked
|
||||
- Permission issues
|
||||
- Worktree is corrupted
|
||||
|
||||
Try:
|
||||
- Check file permissions
|
||||
- Run: git worktree prune
|
||||
- Manually remove directory and run: git worktree prune
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {GIT_STDERR} = captured stderr
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
## TOOL PERMISSION MATRIX
|
||||
|
||||
| Tool | Pattern | Permission | Pre-Check | Post-Check | On-Deny-Action |
|
||||
|------|---------|------------|-----------|------------|----------------|
|
||||
| Bash | git worktree:* | ALLOW | command_safe | validate_output | N/A |
|
||||
| Bash | git status:* | ALLOW | command_safe | N/A | N/A |
|
||||
| Bash | git branch:* | DENY | N/A | N/A | ABORT "Cannot delete branches automatically" |
|
||||
| Bash | cd:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | test:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | pwd:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | dirname:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | basename:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | grep:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | sed:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | cut:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | wc:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | head:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | rm:* | DENY | N/A | N/A | ABORT "Use git worktree remove" |
|
||||
| Bash | sudo:* | DENY | N/A | N/A | ABORT "Elevated privileges" |
|
||||
| Read | * | DENY | N/A | N/A | ABORT "Destroy is read-only except git" |
|
||||
| Write | * | DENY | N/A | N/A | ABORT "Destroy does not write files" |
|
||||
|
||||
SECURITY CONSTRAINTS:
|
||||
- Can ONLY remove worktrees via git worktree remove
|
||||
- CANNOT delete branches (user must do manually)
|
||||
- CANNOT use rm/rmdir (git manages removal)
|
||||
- MUST check for uncommitted changes
|
||||
- MUST prevent main repository deletion
|
||||
|
||||
## TEST CASES
|
||||
|
||||
### TC001: Remove worktree with no uncommitted changes
|
||||
|
||||
PRECONDITIONS:
|
||||
- Worktree exists at /Users/dev/myapp-feature-login
|
||||
- Branch: feature/login
|
||||
- No uncommitted changes
|
||||
- Not the main repository
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/destroy:working-tree /Users/dev/myapp-feature-login
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → WORKTREE_PATH="/Users/dev/myapp-feature-login"
|
||||
2. STEP 2 → EXISTS=0 (path exists)
|
||||
3. STEP 3 → IS_MAIN_REPO=false, IS_REGISTERED>0, BRANCH_NAME="feature/login"
|
||||
4. STEP 4 → HAS_CHANGES=false (no uncommitted changes)
|
||||
5. STEP 5 → git worktree remove succeeds
|
||||
6. STEP 6 → git worktree prune succeeds
|
||||
7. STEP 7 → Output summary
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
✓ Worktree removed successfully
|
||||
|
||||
Path: /Users/dev/myapp-feature-login
|
||||
Branch: feature/login
|
||||
|
||||
Branch 'feature/login' has been preserved.
|
||||
|
||||
To delete the branch as well:
|
||||
git branch -d feature/login # Safe delete (only if merged)
|
||||
git branch -D feature/login # Force delete (even if unmerged)
|
||||
|
||||
To delete remote branch:
|
||||
git push origin --delete feature/login
|
||||
```
|
||||
|
||||
VALIDATION COMMANDS:
|
||||
```bash
|
||||
# Verify worktree no longer exists
|
||||
test ! -e /Users/dev/myapp-feature-login && echo "PASS" || echo "FAIL"
|
||||
|
||||
# Verify branch still exists
|
||||
git show-ref --verify refs/heads/feature/login && echo "PASS" || echo "FAIL"
|
||||
|
||||
# Verify not in worktree list
|
||||
git worktree list | grep -v "feature-login" && echo "PASS" || echo "FAIL"
|
||||
```
|
||||
|
||||
### TC002: Remove worktree with uncommitted changes - user proceeds
|
||||
|
||||
PRECONDITIONS:
|
||||
- Worktree exists at /Users/dev/myapp-bugfix-auth
|
||||
- Has uncommitted changes: Modified src/auth.ts
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/destroy:working-tree /Users/dev/myapp-bugfix-auth
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-3. Standard detection
|
||||
4. STEP 4 → HAS_CHANGES=true
|
||||
5. Display warning with git status output
|
||||
6. USER SELECTS "Proceed with removal (discard changes)"
|
||||
7. STEP 5 → git worktree remove --force succeeds
|
||||
8. STEP 6-7 → Standard cleanup and output
|
||||
|
||||
EXPECTED OUTPUT (includes warning):
|
||||
```
|
||||
⚠ Warning: Uncommitted changes detected
|
||||
|
||||
Modified files:
|
||||
M src/auth.ts
|
||||
|
||||
These changes will be lost if you proceed.
|
||||
|
||||
Recommendations:
|
||||
1. Commit changes: cd /Users/dev/myapp-bugfix-auth && git commit -am "message"
|
||||
2. Stash changes: cd /Users/dev/myapp-bugfix-auth && git stash
|
||||
3. Proceed anyway (changes will be lost)
|
||||
|
||||
Proceed with removal? (This will permanently delete uncommitted work)
|
||||
|
||||
[User confirms]
|
||||
|
||||
✓ Worktree removed successfully
|
||||
|
||||
Path: /Users/dev/myapp-bugfix-auth
|
||||
Branch: bugfix/auth
|
||||
|
||||
Branch 'bugfix/auth' has been preserved.
|
||||
|
||||
To delete the branch as well:
|
||||
git branch -d bugfix/auth # Safe delete (only if merged)
|
||||
git branch -D bugfix/auth # Force delete (even if unmerged)
|
||||
|
||||
To delete remote branch:
|
||||
git push origin --delete bugfix/auth
|
||||
```
|
||||
|
||||
### TC003: Remove worktree with uncommitted changes - user cancels
|
||||
|
||||
PRECONDITIONS:
|
||||
- Worktree with uncommitted changes
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/destroy:working-tree /Users/dev/myapp-feature-test
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-4. Detect changes
|
||||
5. Display warning
|
||||
6. USER SELECTS "Cancel removal"
|
||||
7. TERMINATE (no changes)
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
⚠ Warning: Uncommitted changes detected
|
||||
|
||||
[warning displayed]
|
||||
|
||||
Proceed with removal?
|
||||
|
||||
[User cancels]
|
||||
|
||||
Removal cancelled. No changes made.
|
||||
```
|
||||
|
||||
POSTCONDITIONS:
|
||||
- Worktree still exists
|
||||
- No files modified
|
||||
|
||||
### TC004: Attempt to destroy main repository
|
||||
|
||||
PRECONDITIONS:
|
||||
- Main repository at /Users/dev/myapp
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/destroy:working-tree /Users/dev/myapp
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → Resolve path
|
||||
2. STEP 2 → Path exists
|
||||
3. STEP 3 → IS_MAIN_REPO=true
|
||||
4. ERROR PATTERN "cannot-destroy-main"
|
||||
5. ABORT
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Error: Cannot destroy main repository
|
||||
|
||||
The path '/Users/dev/myapp' is the main repository, not a worktree.
|
||||
|
||||
To remove worktrees, use paths like:
|
||||
/destroy:working-tree ../myapp-feature-branch
|
||||
/destroy:working-tree ../myapp-bugfix-something
|
||||
|
||||
To see all worktrees:
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
POSTCONDITIONS:
|
||||
- Main repository untouched
|
||||
- No changes made
|
||||
|
||||
### TC005: Path does not exist
|
||||
|
||||
PRECONDITIONS:
|
||||
- Path /Users/dev/nonexistent does not exist
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/destroy:working-tree /Users/dev/nonexistent
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → Resolve path
|
||||
2. STEP 2 → EXISTS=1 (does not exist)
|
||||
3. ERROR PATTERN "path-not-exist"
|
||||
4. ABORT
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Error: Path does not exist
|
||||
|
||||
Path: /Users/dev/nonexistent
|
||||
|
||||
The specified path doesn't exist. Check for typos.
|
||||
|
||||
To list existing worktrees:
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
### TC006: Not a git worktree
|
||||
|
||||
PRECONDITIONS:
|
||||
- Directory /Users/dev/random-dir exists but is not a git worktree
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/destroy:working-tree /Users/dev/random-dir
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-2. Resolve and verify path exists
|
||||
3. STEP 3 → IS_REGISTERED=0 (not in worktree list)
|
||||
4. ERROR PATTERN "not-a-worktree"
|
||||
5. ABORT
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Error: Not a registered git worktree
|
||||
|
||||
Path: /Users/dev/random-dir
|
||||
|
||||
This path is not a git worktree. To see all worktrees:
|
||||
/list:working-tree
|
||||
|
||||
Valid worktree paths look like:
|
||||
/Users/dev/myapp-feature-login
|
||||
../myapp-bugfix-auth
|
||||
```
|
||||
|
||||
## SAFETY FEATURES
|
||||
|
||||
### PROTECTED OPERATIONS
|
||||
- CANNOT remove main repository
|
||||
- WARNS about uncommitted changes
|
||||
- REQUIRES confirmation for destructive operations
|
||||
- PRESERVES branch by default
|
||||
|
||||
### BRANCH DELETION GUIDANCE
|
||||
|
||||
Command provides guidance but NEVER auto-deletes branches:
|
||||
|
||||
**Safe delete** (only if merged):
|
||||
```bash
|
||||
git branch -d <branch-name>
|
||||
```
|
||||
|
||||
**Force delete** (even if unmerged):
|
||||
```bash
|
||||
git branch -D <branch-name>
|
||||
```
|
||||
|
||||
**Remote branch delete**:
|
||||
```bash
|
||||
git push origin --delete <branch-name>
|
||||
```
|
||||
|
||||
## RELATED COMMANDS
|
||||
|
||||
- /list:working-tree - See all worktrees before deciding what to remove
|
||||
- /status:working-tree - Check current worktree before removing
|
||||
- /create:working-tree - Create new worktree after removal
|
||||
|
||||
## DELEGATION
|
||||
|
||||
For guidance on when to remove worktrees:
|
||||
```
|
||||
Task(
|
||||
subagent_type='working-tree-consultant',
|
||||
description='Worktree removal strategy',
|
||||
prompt='[question about when/how to safely remove worktrees]'
|
||||
)
|
||||
```
|
||||
514
commands/list.md
Normal file
514
commands/list.md
Normal file
@@ -0,0 +1,514 @@
|
||||
---
|
||||
description: List all git worktrees with their associated .ai-context metadata
|
||||
allowed-tools: Bash, Read, Glob
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
# /list:working-tree
|
||||
|
||||
Enumerate all git worktrees and display metadata from `.ai-context.json`.
|
||||
|
||||
## ARGUMENT SPECIFICATION
|
||||
|
||||
```
|
||||
SYNTAX: /list:working-tree
|
||||
|
||||
NO ARGUMENTS
|
||||
```
|
||||
|
||||
## EXECUTION PROTOCOL
|
||||
|
||||
Execute steps sequentially. Each step must complete successfully before proceeding.
|
||||
|
||||
### STEP 1: GET CURRENT WORKTREE
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
CURRENT_ROOT=$(git rev-parse --show-toplevel 2>&1)
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → ERROR PATTERN "not-in-git-repo"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 2
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 2: LIST ALL WORKTREES
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
WORKTREE_LIST=$(git worktree list --porcelain 2>&1)
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → ERROR PATTERN "git-command-failed"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 3
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 3: PARSE WORKTREES
|
||||
|
||||
PARSE FORMAT:
|
||||
```
|
||||
worktree /path/to/worktree
|
||||
HEAD commit-hash
|
||||
branch refs/heads/branch-name
|
||||
|
||||
worktree /another/path
|
||||
HEAD commit-hash
|
||||
branch refs/heads/another-branch
|
||||
```
|
||||
|
||||
PARSING ALGORITHM:
|
||||
```python
|
||||
worktrees = []
|
||||
current_entry = {}
|
||||
|
||||
for line in WORKTREE_LIST.split("\n"):
|
||||
if line.startswith("worktree "):
|
||||
if current_entry:
|
||||
worktrees.append(current_entry)
|
||||
current_entry = {"path": line.split(" ", 1)[1]}
|
||||
elif line.startswith("branch refs/heads/"):
|
||||
current_entry["branch"] = line.split("/")[-1]
|
||||
elif line == "":
|
||||
if current_entry:
|
||||
worktrees.append(current_entry)
|
||||
current_entry = {}
|
||||
|
||||
if current_entry:
|
||||
worktrees.append(current_entry)
|
||||
```
|
||||
|
||||
DATA EXTRACTED:
|
||||
- List of worktree entries, each with:
|
||||
- path: absolute path to worktree
|
||||
- branch: branch name (without refs/heads/)
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 4
|
||||
- No failure case (empty list is valid)
|
||||
|
||||
### STEP 4: READ METADATA FOR EACH WORKTREE
|
||||
|
||||
FOR EACH worktree in worktrees:
|
||||
```bash
|
||||
METADATA_PATH="$worktree_path/.ai-context.json"
|
||||
test -f "$METADATA_PATH"
|
||||
HAS_METADATA=$?
|
||||
|
||||
if [ $HAS_METADATA -eq 0 ]; then
|
||||
METADATA=$(cat "$METADATA_PATH" 2>&1)
|
||||
MODE=$(echo "$METADATA" | jq -r '.mode // "(invalid)"' 2>&1)
|
||||
DESCRIPTION=$(echo "$METADATA" | jq -r '.description // ""' 2>&1)
|
||||
|
||||
# Validate JSON
|
||||
if [ $? -ne 0 ]; then
|
||||
MODE="(invalid json)"
|
||||
DESCRIPTION=""
|
||||
fi
|
||||
else
|
||||
MODE="(no metadata)"
|
||||
DESCRIPTION="-"
|
||||
fi
|
||||
```
|
||||
|
||||
DATA STRUCTURE:
|
||||
```
|
||||
worktree_data = [
|
||||
{
|
||||
"path": "/path/to/worktree",
|
||||
"branch": "feature/login",
|
||||
"mode": "feature",
|
||||
"description": "OAuth refactor",
|
||||
"is_current": true/false,
|
||||
"has_metadata": true/false
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
CURRENT DETECTION:
|
||||
- IF worktree_path == CURRENT_ROOT → is_current = true
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 5
|
||||
- Continue even if individual metadata reads fail
|
||||
|
||||
### STEP 5: FORMAT AND DISPLAY TABLE
|
||||
|
||||
TABLE FORMAT:
|
||||
```
|
||||
Git Worktrees
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Path Branch Mode Description
|
||||
───────────────────────────────────────────────────────────────────────────────
|
||||
{INDICATOR} {PATH} {BRANCH} {MODE} {DESCRIPTION}
|
||||
...
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
Total: {COUNT} worktrees ({WITH_META} with metadata, {WITHOUT_META} without)
|
||||
|
||||
{TIP_IF_MISSING_METADATA}
|
||||
```
|
||||
|
||||
FORMATTING RULES:
|
||||
- INDICATOR: "→" if is_current, else " " (2 spaces)
|
||||
- PATH: Truncate to 30 chars if longer, add "..." suffix
|
||||
- BRANCH: Truncate to 20 chars if longer
|
||||
- MODE: Truncate to 12 chars
|
||||
- DESCRIPTION: Truncate to 40 chars if longer, add "..."
|
||||
- Align columns with padding
|
||||
|
||||
PADDING ALGORITHM:
|
||||
```python
|
||||
def pad_column(text, width):
|
||||
if len(text) > width:
|
||||
return text[:width-3] + "..."
|
||||
return text + " " * (width - len(text))
|
||||
```
|
||||
|
||||
TIP LOGIC:
|
||||
- IF any worktree has has_metadata == false:
|
||||
- Display: "Tip: Use /adopt:working-tree to add metadata to worktrees that lack it"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 6
|
||||
- No failure case
|
||||
|
||||
### STEP 6: COUNT AND SUMMARIZE
|
||||
|
||||
COUNTS:
|
||||
```bash
|
||||
TOTAL=$(echo "$worktrees" | wc -l)
|
||||
WITH_META=$(count worktrees where has_metadata == true)
|
||||
WITHOUT_META=$(count worktrees where has_metadata == false)
|
||||
```
|
||||
|
||||
SUMMARY LINE:
|
||||
```
|
||||
Total: {TOTAL} worktrees ({WITH_META} with metadata, {WITHOUT_META} without)
|
||||
```
|
||||
|
||||
SPECIAL CASES:
|
||||
- IF TOTAL == 1 AND WITHOUT_META == 1:
|
||||
- Summary: "Total: 1 worktree (main repository only)"
|
||||
- Tip: "To create a new worktree: /create:working-tree <branch-name>"
|
||||
|
||||
NEXT:
|
||||
- TERMINATE (success)
|
||||
|
||||
## ERROR PATTERNS
|
||||
|
||||
### PATTERN: not-in-git-repo
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git rev-parse --show-toplevel exit code != 0
|
||||
- INDICATORS: stderr contains "not a git repository"
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Not in a git repository
|
||||
|
||||
Run this command from within a git repository.
|
||||
```
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: git-command-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git worktree list command fails (STEP 2)
|
||||
- CAPTURE: stderr from git command
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to list worktrees
|
||||
|
||||
Git error: {GIT_STDERR}
|
||||
|
||||
Check that:
|
||||
- You're in a git repository
|
||||
- Git is installed and working
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {GIT_STDERR} = captured stderr
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: invalid-metadata-json
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: jq fails to parse .ai-context.json (STEP 4)
|
||||
- OCCURS: per-worktree, not fatal
|
||||
|
||||
RESPONSE:
|
||||
```
|
||||
Warning: Invalid .ai-context.json in {WORKTREE_PATH}
|
||||
|
||||
Displaying worktree without metadata. Consider:
|
||||
- Fixing the JSON manually
|
||||
- Running /adopt:working-tree in that worktree to regenerate
|
||||
```
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: false (warning only)
|
||||
- DISPLAY: Show worktree with MODE="(invalid json)"
|
||||
- CONTINUE: Process remaining worktrees
|
||||
|
||||
## TOOL PERMISSION MATRIX
|
||||
|
||||
| Tool | Pattern | Permission | Pre-Check | Post-Check | On-Deny-Action |
|
||||
|------|---------|------------|-----------|------------|----------------|
|
||||
| Bash | git worktree:* | ALLOW | command_safe | N/A | N/A |
|
||||
| Bash | git rev-parse:* | ALLOW | command_safe | N/A | N/A |
|
||||
| Bash | test:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | cat:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | jq:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | wc:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | grep:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | cut:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | sed:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | head:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | sudo:* | DENY | N/A | N/A | ABORT "Elevated privileges" |
|
||||
| Read | */.ai-context.json | ALLOW | N/A | N/A | N/A |
|
||||
| Write | * | DENY | N/A | N/A | ABORT "List is read-only" |
|
||||
| Edit | * | DENY | N/A | N/A | ABORT "List is read-only" |
|
||||
|
||||
SECURITY CONSTRAINTS:
|
||||
- Command is READ-ONLY
|
||||
- Cannot modify any files
|
||||
- Cannot execute destructive operations
|
||||
- Safe to run multiple times
|
||||
|
||||
## TEST CASES
|
||||
|
||||
### TC001: Multiple worktrees with metadata
|
||||
|
||||
PRECONDITIONS:
|
||||
- Main repository at /Users/dev/myapp
|
||||
- Worktree at /Users/dev/myapp-feature-api with metadata (mode: feature, description: "New API")
|
||||
- Worktree at /Users/dev/myapp-bugfix-auth with metadata (mode: bugfix, description: "Fix auth")
|
||||
- Currently in /Users/dev/myapp
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → CURRENT_ROOT="/Users/dev/myapp"
|
||||
2. STEP 2 → Get worktree list
|
||||
3. STEP 3 → Parse 3 worktrees
|
||||
4. STEP 4 → Read metadata for each
|
||||
5. STEP 5-6 → Format and display
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Git Worktrees
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Path Branch Mode Description
|
||||
───────────────────────────────────────────────────────────────────────────────
|
||||
→ /Users/dev/myapp main main Main development
|
||||
/Users/dev/myapp-feature... feature/api feature New API
|
||||
/Users/dev/myapp-bugfix-... bugfix/auth bugfix Fix auth
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
Total: 3 worktrees (3 with metadata)
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- Current worktree marked with →
|
||||
- All columns aligned
|
||||
- Counts correct
|
||||
|
||||
### TC002: Mixed metadata status
|
||||
|
||||
PRECONDITIONS:
|
||||
- Main repository without metadata
|
||||
- One worktree with metadata
|
||||
- One worktree without metadata
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Git Worktrees
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Path Branch Mode Description
|
||||
───────────────────────────────────────────────────────────────────────────────
|
||||
/Users/dev/myapp main (no metadata) -
|
||||
→ /Users/dev/myapp-feature... feature/new feature New feature work
|
||||
/Users/dev/old-checkout feature/abandoned (no metadata) -
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
Total: 3 worktrees (1 with metadata, 2 without)
|
||||
|
||||
Tip: Use /adopt:working-tree to add metadata to worktrees that lack it
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- Tip displayed because some worktrees lack metadata
|
||||
- (no metadata) shown for worktrees without .ai-context.json
|
||||
|
||||
### TC003: Only main repository
|
||||
|
||||
PRECONDITIONS:
|
||||
- Main repository only, no additional worktrees
|
||||
- No metadata file
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Git Worktrees
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Path Branch Mode Description
|
||||
───────────────────────────────────────────────────────────────────────────────
|
||||
→ /Users/dev/myapp main (no metadata) -
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
Total: 1 worktree (main repository only)
|
||||
|
||||
To create a new worktree: /create:working-tree <branch-name>
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- Special message for single worktree
|
||||
- Tip suggests creating worktree
|
||||
|
||||
### TC004: Invalid JSON in metadata file
|
||||
|
||||
PRECONDITIONS:
|
||||
- Worktree at /Users/dev/myapp-test
|
||||
- File exists: /Users/dev/myapp-test/.ai-context.json
|
||||
- File contains invalid JSON: `{invalid`
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-3. Standard flow
|
||||
4. STEP 4 → jq fails on invalid JSON
|
||||
5. Display warning
|
||||
6. Show worktree with MODE="(invalid json)"
|
||||
7. Continue with other worktrees
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Warning: Invalid .ai-context.json in /Users/dev/myapp-test
|
||||
|
||||
Displaying worktree without metadata. Consider:
|
||||
- Fixing the JSON manually
|
||||
- Running /adopt:working-tree in that worktree to regenerate
|
||||
|
||||
Git Worktrees
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Path Branch Mode Description
|
||||
───────────────────────────────────────────────────────────────────────────────
|
||||
→ /Users/dev/myapp-test feature/test (invalid json) -
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
Total: 1 worktree (0 with metadata, 1 without)
|
||||
|
||||
Tip: Use /adopt:working-tree to add metadata to worktrees that lack it
|
||||
```
|
||||
|
||||
### TC005: Long paths and descriptions
|
||||
|
||||
PRECONDITIONS:
|
||||
- Worktree with very long path: /Users/dev/myapp-feature-really-long-branch-name-that-exceeds-column-width
|
||||
- Description: "This is a very long description that exceeds the maximum column width and should be truncated"
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/list:working-tree
|
||||
```
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Git Worktrees
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
Path Branch Mode Description
|
||||
───────────────────────────────────────────────────────────────────────────────
|
||||
→ /Users/dev/myapp-feature... feature/really-lo... feature This is a very long description that...
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
Total: 1 worktree (1 with metadata)
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- Long path truncated with "..."
|
||||
- Long branch truncated with "..."
|
||||
- Long description truncated with "..."
|
||||
- Columns remain aligned
|
||||
|
||||
## DISPLAY NOTES
|
||||
|
||||
### COLUMN WIDTHS
|
||||
|
||||
Fixed widths for consistent alignment:
|
||||
- Indicator: 2 chars ("→ " or " ")
|
||||
- Path: 30 chars
|
||||
- Branch: 20 chars
|
||||
- Mode: 13 chars
|
||||
- Description: remaining width (or 40 chars minimum)
|
||||
|
||||
### TRUNCATION STRATEGY
|
||||
|
||||
```python
|
||||
def truncate(text, max_width):
|
||||
if len(text) <= max_width:
|
||||
return text
|
||||
return text[:max_width-3] + "..."
|
||||
```
|
||||
|
||||
### BOX DRAWING
|
||||
|
||||
Use consistent box-drawing characters:
|
||||
- Top/bottom: `═` (double horizontal)
|
||||
- Separator: `─` (single horizontal)
|
||||
- No vertical bars (cleaner appearance)
|
||||
|
||||
## RELATED COMMANDS
|
||||
|
||||
- /status:working-tree - Show current worktree details
|
||||
- /create:working-tree - Create new worktree with metadata
|
||||
- /adopt:working-tree - Add metadata to existing worktree
|
||||
- /destroy:working-tree - Remove worktree
|
||||
|
||||
## DELEGATION
|
||||
|
||||
For organizing multiple worktrees strategically:
|
||||
```
|
||||
Task(
|
||||
subagent_type='working-tree-consultant',
|
||||
description='Worktree organization strategy',
|
||||
prompt='[question about managing multiple worktrees]'
|
||||
)
|
||||
```
|
||||
947
commands/new.md
Normal file
947
commands/new.md
Normal file
@@ -0,0 +1,947 @@
|
||||
---
|
||||
description: Create a new git worktree with branch and .ai-context.json metadata
|
||||
argument-hint: <branch-name> [--mode <mode>] [--description "<text>"]
|
||||
allowed-tools: Bash, Write, Read
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
# /working-tree:new
|
||||
|
||||
Create new branch (if needed), attach git worktree, generate AI metadata files for isolated development.
|
||||
|
||||
## ARGUMENT SPECIFICATION
|
||||
|
||||
```
|
||||
SYNTAX: /working-tree:new <branch-name> [--mode <mode>] [--description "<text>"]
|
||||
|
||||
REQUIRED:
|
||||
<branch-name>
|
||||
Type: string
|
||||
Position: 1
|
||||
Validation: ^[a-zA-Z0-9/_-]+$
|
||||
Examples: "feature/login", "bugfix/timeout", "main"
|
||||
|
||||
OPTIONAL:
|
||||
--mode <mode>
|
||||
Type: enum[main, feature, bugfix, experiment, review]
|
||||
Default: inferred from branch-name (see MODE_INFERENCE_ALGORITHM)
|
||||
Validation: must match exactly one of the enum values
|
||||
|
||||
--description "<text>"
|
||||
Type: string (quoted if contains spaces)
|
||||
Default: "" (empty string)
|
||||
Validation: any string, no restrictions
|
||||
```
|
||||
|
||||
## MODE_INFERENCE_ALGORITHM
|
||||
|
||||
APPLY rules sequentially, first match wins:
|
||||
|
||||
```python
|
||||
def infer_mode(branch_name: str) -> str:
|
||||
if branch_name.startswith("feature/"):
|
||||
return "feature"
|
||||
elif branch_name.startswith(("bugfix/", "fix/")):
|
||||
return "bugfix"
|
||||
elif branch_name.startswith(("exp/", "experiment/")):
|
||||
return "experiment"
|
||||
elif branch_name.startswith("review/"):
|
||||
return "review"
|
||||
elif branch_name in ("main", "master"):
|
||||
return "main"
|
||||
else:
|
||||
return "feature" # DEFAULT
|
||||
```
|
||||
|
||||
DETERMINISTIC: Given same branch_name, always produces same mode.
|
||||
|
||||
## EXECUTION PROTOCOL
|
||||
|
||||
Execute steps sequentially. Each step must complete successfully before proceeding.
|
||||
|
||||
### STEP 1: DETECT REPOSITORY INFO
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel 2>&1)
|
||||
EXIT_CODE_ROOT=$?
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>&1)
|
||||
EXIT_CODE_BRANCH=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE_ROOT != 0 → ERROR PATTERN "not-in-git-repo"
|
||||
- IF EXIT_CODE_BRANCH != 0 → ERROR PATTERN "git-command-failed"
|
||||
- REPO_ROOT must be absolute path starting with /
|
||||
|
||||
DATA EXTRACTION:
|
||||
```bash
|
||||
REPO_NAME=$(basename "$REPO_ROOT")
|
||||
PARENT_DIR=$(dirname "$REPO_ROOT")
|
||||
```
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 2
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 2: PARSE AND VALIDATE ARGUMENTS
|
||||
|
||||
PARSE:
|
||||
```bash
|
||||
# Extract branch name (first positional argument)
|
||||
BRANCH_NAME="$1"
|
||||
|
||||
# Parse --mode flag (if present)
|
||||
if [[ "$@" =~ --mode[[:space:]]+([a-z]+) ]]; then
|
||||
MODE_ARG="${BASH_REMATCH[1]}"
|
||||
else
|
||||
MODE_ARG=""
|
||||
fi
|
||||
|
||||
# Parse --description flag (if present)
|
||||
if [[ "$@" =~ --description[[:space:]]+\"([^\"]+)\" ]]; then
|
||||
DESCRIPTION="${BASH_REMATCH[1]}"
|
||||
elif [[ "$@" =~ --description[[:space:]]+([^[:space:]]+) ]]; then
|
||||
DESCRIPTION="${BASH_REMATCH[1]}"
|
||||
else
|
||||
DESCRIPTION=""
|
||||
fi
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF BRANCH_NAME is empty → ERROR PATTERN "missing-branch-name"
|
||||
- IF BRANCH_NAME !~ ^[a-zA-Z0-9/_-]+$ → ERROR PATTERN "invalid-branch-name"
|
||||
- IF MODE_ARG not empty AND MODE_ARG not in [main, feature, bugfix, experiment, review] → ERROR PATTERN "invalid-mode"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 3
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 3: DETERMINE MODE
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
if [ -n "$MODE_ARG" ]; then
|
||||
# Explicit mode provided
|
||||
MODE="$MODE_ARG"
|
||||
else
|
||||
# Infer from branch name using MODE_INFERENCE_ALGORITHM
|
||||
case "$BRANCH_NAME" in
|
||||
feature/*)
|
||||
MODE="feature"
|
||||
;;
|
||||
bugfix/*|fix/*)
|
||||
MODE="bugfix"
|
||||
;;
|
||||
exp/*|experiment/*)
|
||||
MODE="experiment"
|
||||
;;
|
||||
review/*)
|
||||
MODE="review"
|
||||
;;
|
||||
main|master)
|
||||
MODE="main"
|
||||
;;
|
||||
*)
|
||||
MODE="feature"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- MODE must be exactly one of: main, feature, bugfix, experiment, review
|
||||
- MODE must not be empty
|
||||
|
||||
DATA:
|
||||
- MODE = final determined mode (explicit or inferred)
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 4
|
||||
- On failure → ABORT (should not occur if STEP 2 validation passed)
|
||||
|
||||
### STEP 4: CHECK BRANCH EXISTENCE
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
git show-ref --verify --quiet refs/heads/$BRANCH_NAME
|
||||
BRANCH_EXISTS=$?
|
||||
```
|
||||
|
||||
BRANCH_EXISTS values:
|
||||
- 0 = branch exists
|
||||
- 1 = branch does not exist
|
||||
- Other = error
|
||||
|
||||
VALIDATION:
|
||||
- IF BRANCH_EXISTS not in [0, 1] → ERROR PATTERN "git-command-failed"
|
||||
|
||||
ACTION:
|
||||
- IF BRANCH_EXISTS == 1 (does not exist) → Create branch in STEP 5
|
||||
- IF BRANCH_EXISTS == 0 (exists) → Use existing branch, skip to STEP 6
|
||||
|
||||
NEXT:
|
||||
- On BRANCH_EXISTS == 0 → STEP 6
|
||||
- On BRANCH_EXISTS == 1 → STEP 5
|
||||
- On error → ABORT
|
||||
|
||||
### STEP 5: CREATE NEW BRANCH
|
||||
|
||||
EXECUTE (only if BRANCH_EXISTS == 1):
|
||||
```bash
|
||||
git branch "$BRANCH_NAME" 2>&1
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → ERROR PATTERN "branch-creation-failed"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 6
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 6: DERIVE WORKTREE DIRECTORY NAME
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
# Normalize branch name: replace / and _ with -, convert to lowercase
|
||||
NORMALIZED_BRANCH=$(echo "$BRANCH_NAME" | tr '/_' '-' | tr '[:upper:]' '[:lower:]')
|
||||
WORKTREE_NAME="${REPO_NAME}-${NORMALIZED_BRANCH}"
|
||||
WORKTREE_PATH="${PARENT_DIR}/${WORKTREE_NAME}"
|
||||
```
|
||||
|
||||
NORMALIZATION RULES:
|
||||
- Replace `/` with `-`
|
||||
- Replace `_` with `-`
|
||||
- Convert to lowercase
|
||||
|
||||
EXAMPLES:
|
||||
- myapp + feature/login → myapp-feature-login
|
||||
- api-server + bugfix/SESSION_timeout → api-server-bugfix-session-timeout
|
||||
|
||||
DATA:
|
||||
- WORKTREE_NAME = derived directory name
|
||||
- WORKTREE_PATH = absolute path to worktree location
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 7
|
||||
- No validation needed (pure transformation)
|
||||
|
||||
### STEP 7: CHECK FOR EXISTING WORKTREE ON BRANCH
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
EXISTING_WORKTREE=$(git worktree list --porcelain | grep -A 3 "^branch refs/heads/$BRANCH_NAME$" | grep "^worktree " | cut -d' ' -f2)
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXISTING_WORKTREE not empty → ERROR PATTERN "branch-has-worktree" with path=$EXISTING_WORKTREE
|
||||
|
||||
NEXT:
|
||||
- On EXISTING_WORKTREE empty → STEP 8
|
||||
- On EXISTING_WORKTREE not empty → ABORT
|
||||
|
||||
### STEP 8: CHECK TARGET DIRECTORY DOESN'T EXIST
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
test -e "$WORKTREE_PATH"
|
||||
DIR_EXISTS=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF DIR_EXISTS == 0 (directory exists) → ERROR PATTERN "directory-exists"
|
||||
|
||||
NEXT:
|
||||
- On DIR_EXISTS != 0 (does not exist) → STEP 9
|
||||
- On DIR_EXISTS == 0 (exists) → ABORT
|
||||
|
||||
### STEP 9: CREATE WORKTREE
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
git worktree add "$WORKTREE_PATH" "$BRANCH_NAME" 2>&1
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → ERROR PATTERN "worktree-creation-failed"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 10
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 10: GENERATE TIMESTAMP
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
CREATED_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
```
|
||||
|
||||
FORMAT: ISO 8601 UTC (example: 2025-11-23T12:34:56Z)
|
||||
|
||||
VALIDATION:
|
||||
- CREATED_TIMESTAMP must match pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 11
|
||||
- No failure case
|
||||
|
||||
### STEP 11: GENERATE .ai-context.json
|
||||
|
||||
CONTENT TEMPLATE:
|
||||
```json
|
||||
{
|
||||
"worktree": "{WORKTREE_NAME}",
|
||||
"branch": "{BRANCH_NAME}",
|
||||
"mode": "{MODE}",
|
||||
"created": "{CREATED_TIMESTAMP}",
|
||||
"description": "{DESCRIPTION}"
|
||||
}
|
||||
```
|
||||
|
||||
SUBSTITUTIONS:
|
||||
- {WORKTREE_NAME} = from STEP 6
|
||||
- {BRANCH_NAME} = from STEP 2
|
||||
- {MODE} = from STEP 3
|
||||
- {CREATED_TIMESTAMP} = from STEP 10
|
||||
- {DESCRIPTION} = from STEP 2 (empty string if not provided)
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
cat > "$WORKTREE_PATH/.ai-context.json" <<EOF
|
||||
{
|
||||
"worktree": "$WORKTREE_NAME",
|
||||
"branch": "$BRANCH_NAME",
|
||||
"mode": "$MODE",
|
||||
"created": "$CREATED_TIMESTAMP",
|
||||
"description": "$DESCRIPTION"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- File must be created at exact path: $WORKTREE_PATH/.ai-context.json
|
||||
- File must contain valid JSON
|
||||
- Verify with: `jq empty "$WORKTREE_PATH/.ai-context.json"`
|
||||
- IF jq fails → ERROR PATTERN "metadata-write-failed"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 12
|
||||
- On failure → ABORT (and cleanup worktree)
|
||||
|
||||
### STEP 12: GENERATE README.working-tree.md
|
||||
|
||||
CONTENT TEMPLATE:
|
||||
```markdown
|
||||
# Worktree: {WORKTREE_NAME}
|
||||
|
||||
**Branch:** `{BRANCH_NAME}`
|
||||
**Mode:** `{MODE}`
|
||||
**Created:** {CREATED_TIMESTAMP}
|
||||
|
||||
## Purpose
|
||||
|
||||
{DESCRIPTION_OR_DEFAULT}
|
||||
|
||||
## Mode Semantics
|
||||
|
||||
- **main**: Minimal changes, stable work only
|
||||
- **feature**: Active development, larger changes allowed
|
||||
- **bugfix**: Isolated, surgical fixes only
|
||||
- **experiment**: Prototypes, large swings, unsafe changes allowed
|
||||
- **review**: Documentation, analysis, audits
|
||||
|
||||
## About This Worktree
|
||||
|
||||
This directory is an independent Git worktree attached to the main repository.
|
||||
|
||||
- Main repo: {REPO_ROOT}
|
||||
- Worktree path: {WORKTREE_PATH}
|
||||
- Branch: {BRANCH_NAME}
|
||||
|
||||
See `.ai-context.json` for machine-readable metadata.
|
||||
```
|
||||
|
||||
SUBSTITUTIONS:
|
||||
- {WORKTREE_NAME} = from STEP 6
|
||||
- {BRANCH_NAME} = from STEP 2
|
||||
- {MODE} = from STEP 3
|
||||
- {CREATED_TIMESTAMP} = from STEP 10
|
||||
- {DESCRIPTION_OR_DEFAULT} = DESCRIPTION if not empty, else "No description provided"
|
||||
- {REPO_ROOT} = from STEP 1
|
||||
- {WORKTREE_PATH} = from STEP 6
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
DESCRIPTION_TEXT="${DESCRIPTION:-No description provided}"
|
||||
cat > "$WORKTREE_PATH/README.working-tree.md" <<EOF
|
||||
# Worktree: $WORKTREE_NAME
|
||||
|
||||
**Branch:** \`$BRANCH_NAME\`
|
||||
**Mode:** \`$MODE\`
|
||||
**Created:** $CREATED_TIMESTAMP
|
||||
|
||||
## Purpose
|
||||
|
||||
$DESCRIPTION_TEXT
|
||||
|
||||
## Mode Semantics
|
||||
|
||||
- **main**: Minimal changes, stable work only
|
||||
- **feature**: Active development, larger changes allowed
|
||||
- **bugfix**: Isolated, surgical fixes only
|
||||
- **experiment**: Prototypes, large swings, unsafe changes allowed
|
||||
- **review**: Documentation, analysis, audits
|
||||
|
||||
## About This Worktree
|
||||
|
||||
This directory is an independent Git worktree attached to the main repository.
|
||||
|
||||
- Main repo: $REPO_ROOT
|
||||
- Worktree path: $WORKTREE_PATH
|
||||
- Branch: $BRANCH_NAME
|
||||
|
||||
See \`.ai-context.json\` for machine-readable metadata.
|
||||
EOF
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- File must exist at: $WORKTREE_PATH/README.working-tree.md
|
||||
- IF file creation failed → ERROR PATTERN "readme-write-failed"
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 13
|
||||
- On failure → ABORT (and cleanup)
|
||||
|
||||
### STEP 13: OUTPUT SUCCESS SUMMARY
|
||||
|
||||
OUTPUT FORMAT (exact):
|
||||
```
|
||||
Created worktree successfully!
|
||||
|
||||
Path: {WORKTREE_PATH}
|
||||
Branch: {BRANCH_NAME}
|
||||
Mode: {MODE}
|
||||
Description: {DESCRIPTION_OR_NONE}
|
||||
|
||||
Metadata files created:
|
||||
✓ .ai-context.json
|
||||
✓ README.working-tree.md
|
||||
|
||||
To switch to this worktree:
|
||||
cd {WORKTREE_PATH}
|
||||
```
|
||||
|
||||
SUBSTITUTIONS:
|
||||
- {WORKTREE_PATH} = from STEP 6
|
||||
- {BRANCH_NAME} = from STEP 2
|
||||
- {MODE} = from STEP 3
|
||||
- {DESCRIPTION_OR_NONE} = DESCRIPTION if not empty, else "None"
|
||||
|
||||
NEXT:
|
||||
- TERMINATE (success)
|
||||
|
||||
## ERROR PATTERNS
|
||||
|
||||
### PATTERN: not-in-git-repo
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git rev-parse --show-toplevel exit code != 0
|
||||
- INDICATORS: stderr contains "not a git repository"
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Not in a git repository
|
||||
|
||||
Run this command from within a git repository.
|
||||
```
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none needed
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: missing-branch-name
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: BRANCH_NAME is empty string after parsing
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Missing required argument <branch-name>
|
||||
|
||||
Usage:
|
||||
/working-tree:new <branch-name> [--mode <mode>] [--description "<text>"]
|
||||
|
||||
Example:
|
||||
/working-tree:new feature/my-feature
|
||||
```
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: invalid-branch-name
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: BRANCH_NAME does not match ^[a-zA-Z0-9/_-]+$
|
||||
- INDICATORS: Contains invalid characters
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Invalid branch name '{BRANCH_NAME}'
|
||||
|
||||
Branch names must contain only:
|
||||
- Letters (a-z, A-Z)
|
||||
- Numbers (0-9)
|
||||
- Forward slashes (/)
|
||||
- Hyphens (-)
|
||||
- Underscores (_)
|
||||
|
||||
Example valid names:
|
||||
feature/login
|
||||
bugfix/timeout-issue
|
||||
exp/ai_integration
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {BRANCH_NAME} = the invalid branch name provided
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: invalid-mode
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: MODE_ARG provided but not in [main, feature, bugfix, experiment, review]
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Invalid mode '{MODE_ARG}'
|
||||
|
||||
Valid modes: main, feature, bugfix, experiment, review
|
||||
|
||||
Example:
|
||||
/working-tree:new my-branch --mode feature
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {MODE_ARG} = the invalid mode provided
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: git-command-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: Any git command (except specific patterns) returns non-zero exit code
|
||||
- CAPTURE: stderr from git command
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Git command failed
|
||||
|
||||
Git error: {GIT_STDERR}
|
||||
|
||||
Check that:
|
||||
- You're in a git repository
|
||||
- Git is installed and working
|
||||
- You have necessary permissions
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {GIT_STDERR} = captured stderr
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: Remove worktree if created
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: branch-creation-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git branch command fails (STEP 5)
|
||||
- CAPTURE: stderr from git branch
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to create branch '{BRANCH_NAME}'
|
||||
|
||||
Git error: {GIT_STDERR}
|
||||
|
||||
Check that:
|
||||
- Branch name is valid
|
||||
- You're not in detached HEAD state
|
||||
- You have permission to create branches
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {BRANCH_NAME} = attempted branch name
|
||||
- {GIT_STDERR} = captured stderr
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: branch-has-worktree
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git worktree list shows branch already has attached worktree (STEP 7)
|
||||
- CAPTURE: EXISTING_WORKTREE path
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Branch '{BRANCH_NAME}' already has a worktree at {EXISTING_WORKTREE}
|
||||
|
||||
Use one of:
|
||||
- /working-tree:list to see all worktrees
|
||||
- cd {EXISTING_WORKTREE} to use the existing worktree
|
||||
- /working-tree:destroy {EXISTING_WORKTREE} to remove it first
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {BRANCH_NAME} = branch with existing worktree
|
||||
- {EXISTING_WORKTREE} = path to existing worktree
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: directory-exists
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: Target directory already exists (STEP 8)
|
||||
- CHECK: test -e "$WORKTREE_PATH" returns 0
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Directory '{WORKTREE_PATH}' already exists
|
||||
|
||||
Choose a different branch name or remove the existing directory.
|
||||
|
||||
To remove:
|
||||
rm -rf {WORKTREE_PATH}
|
||||
|
||||
(Be careful - this will delete all contents)
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {WORKTREE_PATH} = path that already exists
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: none
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: worktree-creation-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git worktree add fails (STEP 9)
|
||||
- CAPTURE: stderr from git worktree add
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to create worktree
|
||||
|
||||
Git error: {GIT_STDERR}
|
||||
|
||||
Check that:
|
||||
- Parent directory is writable
|
||||
- Branch name is valid
|
||||
- No permission issues
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {GIT_STDERR} = captured stderr
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- CLEANUP: Attempt to remove partially created worktree
|
||||
- RETRY: false
|
||||
|
||||
### PATTERN: metadata-write-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: .ai-context.json write fails or invalid JSON (STEP 11)
|
||||
- CHECK: jq validation fails
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to write .ai-context.json
|
||||
|
||||
The worktree was created but metadata file generation failed.
|
||||
|
||||
Worktree location: {WORKTREE_PATH}
|
||||
|
||||
You can:
|
||||
1. Manually create .ai-context.json
|
||||
2. Use /working-tree:adopt to regenerate metadata
|
||||
3. Remove worktree with /working-tree:destroy {WORKTREE_PATH}
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {WORKTREE_PATH} = worktree path
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: false (worktree exists, metadata failed)
|
||||
- CLEANUP: none (leave worktree intact)
|
||||
- FALLBACK: User can manually fix or adopt
|
||||
|
||||
### PATTERN: readme-write-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: README.working-tree.md write fails (STEP 12)
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Warning: Failed to write README.working-tree.md
|
||||
|
||||
The worktree and .ai-context.json were created successfully.
|
||||
|
||||
Worktree location: {WORKTREE_PATH}
|
||||
|
||||
You can manually create the README if needed.
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {WORKTREE_PATH} = worktree path
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: false (warning, not critical)
|
||||
- CLEANUP: none
|
||||
- FALLBACK: Continue without README
|
||||
|
||||
## TOOL PERMISSION MATRIX
|
||||
|
||||
| Tool | Pattern | Permission | Pre-Check | Post-Check | On-Deny-Action |
|
||||
|------|---------|------------|-----------|------------|----------------|
|
||||
| Bash | git:* | ALLOW | command_safe | validate_output | N/A |
|
||||
| Bash | date:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | test:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | basename:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | dirname:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | tr:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | grep:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | cat > *.json | ALLOW | parent_dir_writable | valid_json | N/A |
|
||||
| Bash | cat > *.md | ALLOW | parent_dir_writable | N/A | N/A |
|
||||
| Bash | jq:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | rm -rf:* | DENY | N/A | N/A | ABORT "Destructive operation not allowed" |
|
||||
| Bash | sudo:* | DENY | N/A | N/A | ABORT "Elevated privileges" |
|
||||
| Write | $WORKTREE_PATH/.ai-context.json | ALLOW | dir_exists | valid_json | N/A |
|
||||
| Write | $WORKTREE_PATH/README.working-tree.md | ALLOW | dir_exists | N/A | N/A |
|
||||
| Write | **/.env* | DENY | N/A | N/A | ABORT "Secrets file" |
|
||||
| Read | * | DENY | N/A | N/A | ABORT "Command is write-only" |
|
||||
|
||||
SECURITY CONSTRAINTS:
|
||||
- Can only write to newly created worktree directory
|
||||
- Cannot modify existing files
|
||||
- Cannot remove directories (even on cleanup)
|
||||
- Git worktree add is safe (git manages cleanup)
|
||||
|
||||
## TEST CASES
|
||||
|
||||
### TC001: Create new feature branch worktree
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository at /Users/dev/myapp
|
||||
- Current branch: main
|
||||
- Branch "feature/login-refactor" does not exist
|
||||
- Directory /Users/dev/myapp-feature-login-refactor does not exist
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/working-tree:new feature/login-refactor
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → REPO_ROOT="/Users/dev/myapp", REPO_NAME="myapp", PARENT_DIR="/Users/dev"
|
||||
2. STEP 2 → BRANCH_NAME="feature/login-refactor", MODE_ARG="", DESCRIPTION=""
|
||||
3. STEP 3 → MODE="feature" (inferred from "feature/" prefix)
|
||||
4. STEP 4 → BRANCH_EXISTS=1 (does not exist)
|
||||
5. STEP 5 → Create branch "feature/login-refactor"
|
||||
6. STEP 6 → WORKTREE_NAME="myapp-feature-login-refactor", WORKTREE_PATH="/Users/dev/myapp-feature-login-refactor"
|
||||
7. STEP 7 → No existing worktree
|
||||
8. STEP 8 → Directory does not exist
|
||||
9. STEP 9 → Create worktree
|
||||
10. STEP 10 → Generate timestamp
|
||||
11. STEP 11 → Write .ai-context.json
|
||||
12. STEP 12 → Write README.working-tree.md
|
||||
13. STEP 13 → Output summary
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Created worktree successfully!
|
||||
|
||||
Path: /Users/dev/myapp-feature-login-refactor
|
||||
Branch: feature/login-refactor
|
||||
Mode: feature
|
||||
Description: None
|
||||
|
||||
Metadata files created:
|
||||
✓ .ai-context.json
|
||||
✓ README.working-tree.md
|
||||
|
||||
To switch to this worktree:
|
||||
cd /Users/dev/myapp-feature-login-refactor
|
||||
```
|
||||
|
||||
VALIDATION COMMANDS:
|
||||
```bash
|
||||
# Verify worktree created
|
||||
test -d /Users/dev/myapp-feature-login-refactor && echo "PASS" || echo "FAIL"
|
||||
|
||||
# Verify branch created
|
||||
git show-ref --verify refs/heads/feature/login-refactor && echo "PASS" || echo "FAIL"
|
||||
|
||||
# Verify .ai-context.json
|
||||
test -f /Users/dev/myapp-feature-login-refactor/.ai-context.json && echo "PASS" || echo "FAIL"
|
||||
jq -r '.mode' /Users/dev/myapp-feature-login-refactor/.ai-context.json | grep "feature" && echo "PASS" || echo "FAIL"
|
||||
|
||||
# Verify README
|
||||
test -f /Users/dev/myapp-feature-login-refactor/README.working-tree.md && echo "PASS" || echo "FAIL"
|
||||
```
|
||||
|
||||
### TC002: Create with explicit mode and description
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository at /Users/dev/myapp
|
||||
- Branch "my-experiment" does not exist
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/working-tree:new my-experiment --mode experiment --description "Testing new architecture"
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-2. Parse arguments → BRANCH_NAME="my-experiment", MODE_ARG="experiment", DESCRIPTION="Testing new architecture"
|
||||
3. STEP 3 → MODE="experiment" (explicit, not inferred)
|
||||
4-13. Standard flow
|
||||
|
||||
EXPECTED .ai-context.json:
|
||||
```json
|
||||
{
|
||||
"worktree": "myapp-my-experiment",
|
||||
"branch": "my-experiment",
|
||||
"mode": "experiment",
|
||||
"created": "2025-11-23T12:34:56Z",
|
||||
"description": "Testing new architecture"
|
||||
}
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
```bash
|
||||
jq -r '.mode' .ai-context.json | grep "experiment" && echo "PASS" || echo "FAIL"
|
||||
jq -r '.description' .ai-context.json | grep "Testing new architecture" && echo "PASS" || echo "FAIL"
|
||||
```
|
||||
|
||||
### TC003: Branch already has worktree
|
||||
|
||||
PRECONDITIONS:
|
||||
- Branch "feature/existing" already has worktree at /Users/dev/myapp-feature-existing
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/working-tree:new feature/existing
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-6. Standard detection and parsing
|
||||
7. STEP 7 → EXISTING_WORKTREE="/Users/dev/myapp-feature-existing"
|
||||
8. ERROR PATTERN "branch-has-worktree"
|
||||
9. ABORT
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Error: Branch 'feature/existing' already has a worktree at /Users/dev/myapp-feature-existing
|
||||
|
||||
Use one of:
|
||||
- /working-tree:list to see all worktrees
|
||||
- cd /Users/dev/myapp-feature-existing to use the existing worktree
|
||||
- /working-tree:destroy /Users/dev/myapp-feature-existing to remove it first
|
||||
```
|
||||
|
||||
POSTCONDITIONS:
|
||||
- No new worktree created
|
||||
- No new branch created
|
||||
- Existing worktree unchanged
|
||||
|
||||
### TC004: Invalid mode specified
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/working-tree:new test-branch --mode production
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → Detect repo
|
||||
2. STEP 2 → MODE_ARG="production"
|
||||
3. Validation fails: "production" not in [main, feature, bugfix, experiment, review]
|
||||
4. ERROR PATTERN "invalid-mode"
|
||||
5. ABORT
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Error: Invalid mode 'production'
|
||||
|
||||
Valid modes: main, feature, bugfix, experiment, review
|
||||
|
||||
Example:
|
||||
/working-tree:new my-branch --mode feature
|
||||
```
|
||||
|
||||
### TC005: Directory already exists
|
||||
|
||||
PRECONDITIONS:
|
||||
- Directory /Users/dev/myapp-feature-test already exists (not a worktree)
|
||||
|
||||
INPUT:
|
||||
```
|
||||
/working-tree:new feature/test
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1-7. Standard flow
|
||||
8. STEP 8 → DIR_EXISTS=0 (directory exists)
|
||||
9. ERROR PATTERN "directory-exists"
|
||||
10. ABORT
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Error: Directory '/Users/dev/myapp-feature-test' already exists
|
||||
|
||||
Choose a different branch name or remove the existing directory.
|
||||
|
||||
To remove:
|
||||
rm -rf /Users/dev/myapp-feature-test
|
||||
|
||||
(Be careful - this will delete all contents)
|
||||
```
|
||||
|
||||
## RELATED COMMANDS
|
||||
|
||||
- /working-tree:status - Show current worktree metadata
|
||||
- /working-tree:list - List all worktrees with metadata
|
||||
- /working-tree:adopt - Add metadata to existing worktree
|
||||
- /working-tree:destroy - Remove worktree safely
|
||||
|
||||
## DELEGATION
|
||||
|
||||
For complex worktree strategy or organization questions:
|
||||
```
|
||||
Task(
|
||||
subagent_type='working-tree-consultant',
|
||||
description='Worktree strategy consultation',
|
||||
prompt='[question about worktree organization, naming, or workflow]'
|
||||
)
|
||||
```
|
||||
466
commands/status.md
Normal file
466
commands/status.md
Normal file
@@ -0,0 +1,466 @@
|
||||
---
|
||||
description: Show metadata for the current git worktree from .ai-context.json
|
||||
allowed-tools: Bash, Read
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
# /working-tree:status
|
||||
|
||||
Display current worktree AI context metadata and git information.
|
||||
|
||||
## EXECUTION PROTOCOL
|
||||
|
||||
Execute steps sequentially. Each step must complete successfully before proceeding.
|
||||
|
||||
### STEP 1: DETECT REPOSITORY ROOT
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel 2>&1)
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → ERROR PATTERN "not-in-git-repo"
|
||||
- REPO_ROOT must be absolute path starting with /
|
||||
- REPO_ROOT directory must exist
|
||||
|
||||
DATA EXTRACTION:
|
||||
- REPO_NAME = basename of REPO_ROOT
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 2
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 2: DETECT CURRENT BRANCH
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD 2>&1)
|
||||
EXIT_CODE=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF EXIT_CODE != 0 → ERROR PATTERN "git-command-failed"
|
||||
- BRANCH_NAME must not be empty string
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 3
|
||||
- On failure → ABORT
|
||||
|
||||
### STEP 3: CHECK METADATA FILE
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
METADATA_PATH="$REPO_ROOT/.ai-context.json"
|
||||
test -f "$METADATA_PATH"
|
||||
EXISTS=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- EXISTS is 0 (true) or 1 (false), no other values
|
||||
|
||||
NEXT:
|
||||
- IF EXISTS == 0 → STEP 4 (metadata exists)
|
||||
- IF EXISTS == 1 → STEP 5 (no metadata)
|
||||
|
||||
### STEP 4: READ AND PARSE METADATA
|
||||
|
||||
EXECUTE:
|
||||
```bash
|
||||
METADATA_JSON=$(cat "$METADATA_PATH" 2>&1)
|
||||
CAT_EXIT=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF CAT_EXIT != 0 → ERROR PATTERN "file-read-failed"
|
||||
|
||||
DATA EXTRACTION:
|
||||
```bash
|
||||
MODE=$(echo "$METADATA_JSON" | jq -r '.mode // "unknown"' 2>&1)
|
||||
JQ_EXIT_MODE=$?
|
||||
DESCRIPTION=$(echo "$METADATA_JSON" | jq -r '.description // ""' 2>&1)
|
||||
JQ_EXIT_DESC=$?
|
||||
CREATED=$(echo "$METADATA_JSON" | jq -r '.created // ""' 2>&1)
|
||||
JQ_EXIT_CREATED=$?
|
||||
```
|
||||
|
||||
VALIDATION:
|
||||
- IF JQ_EXIT_MODE != 0 → ERROR PATTERN "invalid-json"
|
||||
- MODE must be one of: main, feature, bugfix, experiment, review, unknown
|
||||
- DESCRIPTION can be empty string
|
||||
- CREATED should be ISO8601 format or empty
|
||||
|
||||
NEXT:
|
||||
- On success → STEP 6 (display with metadata)
|
||||
- On jq failure → ERROR PATTERN "invalid-json"
|
||||
|
||||
### STEP 5: DISPLAY NO METADATA
|
||||
|
||||
OUTPUT FORMAT (exact):
|
||||
```
|
||||
Worktree Status
|
||||
═══════════════════════════════════════════════════════════
|
||||
|
||||
Directory: {REPO_NAME}
|
||||
Branch: {BRANCH_NAME}
|
||||
Mode: (no metadata)
|
||||
|
||||
⚠ No .ai-context.json found
|
||||
|
||||
This worktree doesn't have AI context metadata.
|
||||
|
||||
To add metadata to this worktree:
|
||||
/working-tree:adopt [--mode <mode>] [--description "<text>"]
|
||||
|
||||
To create a new worktree with metadata:
|
||||
/working-tree:new <branch-name>
|
||||
```
|
||||
|
||||
NEXT:
|
||||
- TERMINATE (success)
|
||||
|
||||
### STEP 6: DISPLAY WITH METADATA
|
||||
|
||||
OUTPUT FORMAT (exact):
|
||||
```
|
||||
Worktree Status
|
||||
═══════════════════════════════════════════════════════════
|
||||
|
||||
Directory: {REPO_NAME}
|
||||
Branch: {BRANCH_NAME}
|
||||
Mode: {MODE}
|
||||
Created: {CREATED}
|
||||
|
||||
Purpose:
|
||||
{DESCRIPTION or "No description provided"}
|
||||
|
||||
───────────────────────────────────────────────────────────
|
||||
|
||||
Mode Semantics:
|
||||
main → Minimal changes, stable work only
|
||||
feature → Active development, larger changes allowed
|
||||
bugfix → Isolated, surgical fixes only
|
||||
experiment → Prototypes, large swings, unsafe changes allowed
|
||||
review → Documentation, analysis, audits
|
||||
|
||||
Metadata file: .ai-context.json
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {REPO_NAME} = extracted from STEP 1
|
||||
- {BRANCH_NAME} = extracted from STEP 2
|
||||
- {MODE} = extracted from STEP 4
|
||||
- {CREATED} = extracted from STEP 4
|
||||
- {DESCRIPTION} = extracted from STEP 4, if empty use "No description provided"
|
||||
|
||||
NEXT:
|
||||
- TERMINATE (success)
|
||||
|
||||
## ERROR PATTERNS
|
||||
|
||||
### PATTERN: not-in-git-repo
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git rev-parse --show-toplevel exit code != 0
|
||||
- INDICATORS: stderr contains "not a git repository" OR "not inside a work tree"
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Not in a git repository
|
||||
|
||||
Run this command from within a git repository.
|
||||
```
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- RETRY: false
|
||||
- FALLBACK: None
|
||||
|
||||
### PATTERN: git-command-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: git command exit code != 0 (excluding rev-parse --show-toplevel which uses "not-in-git-repo")
|
||||
- CAPTURE: stderr from failed git command
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to read git information
|
||||
|
||||
Git error: {GIT_STDERR}
|
||||
|
||||
Check that:
|
||||
- You're in a git repository
|
||||
- Git is installed and working
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {GIT_STDERR} = captured stderr from failed command
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- RETRY: false
|
||||
- FALLBACK: None
|
||||
|
||||
### PATTERN: invalid-json
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: jq command exit code != 0 when parsing .ai-context.json
|
||||
- CAPTURE: jq error message from stderr
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Warning: .ai-context.json exists but is invalid
|
||||
|
||||
JSON error: {JQ_ERROR}
|
||||
|
||||
The metadata file may be corrupted. Consider:
|
||||
- Fixing the JSON manually
|
||||
- Running /working-tree:adopt to regenerate
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {JQ_ERROR} = captured stderr from jq
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: false (warning, not error)
|
||||
- RETRY: false
|
||||
- FALLBACK: STEP 5 (display as if no metadata)
|
||||
|
||||
### PATTERN: file-read-failed
|
||||
|
||||
DETECTION:
|
||||
- TRIGGER: cat command on .ai-context.json fails despite file existence check passing
|
||||
- CAPTURE: stderr from cat command
|
||||
|
||||
RESPONSE (exact):
|
||||
```
|
||||
Error: Failed to read .ai-context.json
|
||||
|
||||
Read error: {CAT_ERROR}
|
||||
|
||||
Check file permissions on .ai-context.json
|
||||
```
|
||||
|
||||
TEMPLATE SUBSTITUTIONS:
|
||||
- {CAT_ERROR} = captured stderr from cat
|
||||
|
||||
CONTROL FLOW:
|
||||
- ABORT: true
|
||||
- RETRY: false
|
||||
- FALLBACK: None
|
||||
|
||||
## TOOL PERMISSION MATRIX
|
||||
|
||||
| Tool | Pattern | Permission | Pre-Check | Post-Check | On-Deny-Action |
|
||||
|------|---------|------------|-----------|------------|----------------|
|
||||
| Bash | git:* | ALLOW | command_safe | validate_output | N/A |
|
||||
| Bash | jq:* | ALLOW | command_safe | validate_json | N/A |
|
||||
| Bash | cat .ai-context.json | ALLOW | file_exists | validate_output | N/A |
|
||||
| Bash | test:* | ALLOW | N/A | N/A | N/A |
|
||||
| Bash | rm:* | DENY | N/A | N/A | ABORT "Destructive operation" |
|
||||
| Bash | sudo:* | DENY | N/A | N/A | ABORT "Elevated privileges" |
|
||||
| Read | .ai-context.json | ALLOW | file_exists | valid_json | N/A |
|
||||
| Write | ** | DENY | N/A | N/A | ABORT "Status is read-only" |
|
||||
| Edit | ** | DENY | N/A | N/A | ABORT "Status is read-only" |
|
||||
|
||||
SECURITY CONSTRAINTS:
|
||||
- This command is READ-ONLY
|
||||
- NO file modifications allowed
|
||||
- NO destructive operations allowed
|
||||
- Git commands limited to read operations (rev-parse, status, etc.)
|
||||
|
||||
## TEST CASES
|
||||
|
||||
### TC001: Worktree with valid metadata
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository at /path/to/myapp
|
||||
- Current branch: feature/login-refactor
|
||||
- File exists: /path/to/myapp/.ai-context.json
|
||||
- File contains valid JSON:
|
||||
```json
|
||||
{
|
||||
"mode": "feature",
|
||||
"description": "Refactor authentication flow to support OAuth2",
|
||||
"created": "2025-11-23T10:30:00Z",
|
||||
"branch": "feature/login-refactor"
|
||||
}
|
||||
```
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → REPO_ROOT="/path/to/myapp", REPO_NAME="myapp"
|
||||
2. STEP 2 → BRANCH_NAME="feature/login-refactor"
|
||||
3. STEP 3 → EXISTS=0 (metadata exists)
|
||||
4. STEP 4 → MODE="feature", DESCRIPTION="Refactor authentication flow to support OAuth2", CREATED="2025-11-23T10:30:00Z"
|
||||
5. STEP 6 → Display formatted output
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Worktree Status
|
||||
═══════════════════════════════════════════════════════════
|
||||
|
||||
Directory: myapp
|
||||
Branch: feature/login-refactor
|
||||
Mode: feature
|
||||
Created: 2025-11-23T10:30:00Z
|
||||
|
||||
Purpose:
|
||||
Refactor authentication flow to support OAuth2
|
||||
|
||||
───────────────────────────────────────────────────────────
|
||||
|
||||
Mode Semantics:
|
||||
main → Minimal changes, stable work only
|
||||
feature → Active development, larger changes allowed
|
||||
bugfix → Isolated, surgical fixes only
|
||||
experiment → Prototypes, large swings, unsafe changes allowed
|
||||
review → Documentation, analysis, audits
|
||||
|
||||
Metadata file: .ai-context.json
|
||||
```
|
||||
|
||||
VALIDATION COMMANDS:
|
||||
```bash
|
||||
# Verify file exists
|
||||
test -f /path/to/myapp/.ai-context.json && echo "PASS" || echo "FAIL"
|
||||
|
||||
# Verify valid JSON
|
||||
jq empty /path/to/myapp/.ai-context.json && echo "PASS" || echo "FAIL"
|
||||
|
||||
# Verify mode field
|
||||
test "$(jq -r '.mode' /path/to/myapp/.ai-context.json)" = "feature" && echo "PASS" || echo "FAIL"
|
||||
```
|
||||
|
||||
### TC002: Worktree without metadata
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository at /path/to/myapp
|
||||
- Current branch: main
|
||||
- File does NOT exist: /path/to/myapp/.ai-context.json
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → REPO_ROOT="/path/to/myapp", REPO_NAME="myapp"
|
||||
2. STEP 2 → BRANCH_NAME="main"
|
||||
3. STEP 3 → EXISTS=1 (no metadata)
|
||||
4. STEP 5 → Display no-metadata output
|
||||
5. TERMINATE
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Worktree Status
|
||||
═══════════════════════════════════════════════════════════
|
||||
|
||||
Directory: myapp
|
||||
Branch: main
|
||||
Mode: (no metadata)
|
||||
|
||||
⚠ No .ai-context.json found
|
||||
|
||||
This worktree doesn't have AI context metadata.
|
||||
|
||||
To add metadata to this worktree:
|
||||
/working-tree:adopt [--mode <mode>] [--description "<text>"]
|
||||
|
||||
To create a new worktree with metadata:
|
||||
/working-tree:new <branch-name>
|
||||
```
|
||||
|
||||
VALIDATION COMMANDS:
|
||||
```bash
|
||||
# Verify file does not exist
|
||||
test ! -f /path/to/myapp/.ai-context.json && echo "PASS" || echo "FAIL"
|
||||
```
|
||||
|
||||
### TC003: Invalid JSON in metadata file
|
||||
|
||||
PRECONDITIONS:
|
||||
- In git repository at /path/to/myapp
|
||||
- Current branch: feature/test
|
||||
- File exists: /path/to/myapp/.ai-context.json
|
||||
- File contains invalid JSON: `{invalid json}`
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → REPO_ROOT="/path/to/myapp"
|
||||
2. STEP 2 → BRANCH_NAME="feature/test"
|
||||
3. STEP 3 → EXISTS=0
|
||||
4. STEP 4 → jq fails with parse error
|
||||
5. ERROR PATTERN "invalid-json" → Warning displayed
|
||||
6. FALLBACK → STEP 5 (display as no metadata)
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Warning: .ai-context.json exists but is invalid
|
||||
|
||||
JSON error: parse error: Invalid numeric literal at line 1, column 10
|
||||
|
||||
The metadata file may be corrupted. Consider:
|
||||
- Fixing the JSON manually
|
||||
- Running /working-tree:adopt to regenerate
|
||||
|
||||
Worktree Status
|
||||
═══════════════════════════════════════════════════════════
|
||||
|
||||
Directory: myapp
|
||||
Branch: feature/test
|
||||
Mode: (no metadata)
|
||||
|
||||
⚠ No .ai-context.json found
|
||||
|
||||
This worktree doesn't have AI context metadata.
|
||||
|
||||
To add metadata to this worktree:
|
||||
/working-tree:adopt [--mode <mode>] [--description "<text>"]
|
||||
|
||||
To create a new worktree with metadata:
|
||||
/working-tree:new <branch-name>
|
||||
```
|
||||
|
||||
VALIDATION COMMANDS:
|
||||
```bash
|
||||
# Verify file exists but is invalid
|
||||
test -f /path/to/myapp/.ai-context.json && echo "EXISTS"
|
||||
jq empty /path/to/myapp/.ai-context.json 2>&1 | grep -q "parse error" && echo "INVALID"
|
||||
```
|
||||
|
||||
### TC004: Not in git repository
|
||||
|
||||
PRECONDITIONS:
|
||||
- Current directory: /tmp (not a git repository)
|
||||
|
||||
EXPECTED EXECUTION FLOW:
|
||||
1. STEP 1 → git rev-parse fails with exit code 128
|
||||
2. ERROR PATTERN "not-in-git-repo" triggered
|
||||
3. ABORT
|
||||
|
||||
EXPECTED OUTPUT:
|
||||
```
|
||||
Error: Not in a git repository
|
||||
|
||||
Run this command from within a git repository.
|
||||
```
|
||||
|
||||
VALIDATION COMMANDS:
|
||||
```bash
|
||||
# Verify not in git repo
|
||||
cd /tmp
|
||||
git rev-parse --show-toplevel 2>&1 | grep -q "not a git repository" && echo "PASS" || echo "FAIL"
|
||||
```
|
||||
|
||||
## RELATED COMMANDS
|
||||
|
||||
- /working-tree:adopt - Add metadata to current worktree
|
||||
- /working-tree:new - Create new worktree with metadata
|
||||
- /working-tree:list - List all worktrees with metadata
|
||||
- /working-tree:destroy - Remove worktree safely
|
||||
|
||||
## DELEGATION
|
||||
|
||||
For complex worktree strategy questions or multi-worktree workflows:
|
||||
```
|
||||
Task(
|
||||
subagent_type='working-tree-consultant',
|
||||
description='Worktree strategy consultation',
|
||||
prompt='[detailed question about worktree organization]'
|
||||
)
|
||||
```
|
||||
Reference in New Issue
Block a user