feat: add deep-work automation system (#1644)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com>
This commit is contained in:
Cyrus Gray
2026-05-15 19:55:46 -07:00
committed by GitHub
co-authored by Claude Steven Enamakel
parent 574d40a40e
commit 98b579e6c7
9 changed files with 1474 additions and 0 deletions
+1
View File
@@ -30,6 +30,7 @@
"pr:checklist": "node scripts/check-pr-checklist.mjs",
"rabbit": "bash scripts/rabbit/cli.sh",
"reset": "bash scripts/shortcuts/ws-reset.sh",
"deep-work": "bash scripts/deep-work/cli.sh",
"review": "bash scripts/shortcuts/review/cli.sh",
"work": "bash scripts/shortcuts/work/cli.sh",
"agent-batch": "node scripts/agent-batch/cli.mjs",
+157
View File
@@ -0,0 +1,157 @@
#!/usr/bin/env bash
# cleanup.sh <issue-number>
#
# Clean up worktree for completed issue
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./lib.sh
source "$here/lib.sh"
if [ -z "${1:-}" ]; then
echo "Usage: pnpm deep-work cleanup <issue-number>" >&2
echo ""
echo "Active worktrees:"
list_deep_work_worktrees | while IFS='=' read -r key value; do
if [ "$key" = "issue" ]; then
issue_num="$value"
elif [ "$key" = "path" ]; then
echo " #$issue_num: $value"
fi
done
exit 1
fi
case "$1" in
''|*[!0-9]*)
echo "[deep-work] issue-number must be numeric, got: $1" >&2
exit 1
;;
esac
issue="$1"
echo "[deep-work] 🧹 Cleaning up worktree for issue #$issue"
# Check if worktree exists
if ! worktree_exists "$issue"; then
echo "[deep-work] ❌ no worktree found for issue #$issue"
exit 1
fi
worktree_dir=$(worktree_dir_for_issue "$issue")
echo "[deep-work] worktree location: $worktree_dir"
# Get branch info
branch=""
if [ -d "$worktree_dir" ]; then
branch=$(cd "$worktree_dir" && git branch --show-current 2>/dev/null || echo "unknown")
echo "[deep-work] branch: $branch"
fi
# Check for uncommitted changes
if [ -d "$worktree_dir/.git" ]; then
if ! (cd "$worktree_dir" && git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]); then
echo ""
echo "⚠️ WARNING: Worktree has uncommitted changes!"
echo ""
echo "Files with changes:"
(cd "$worktree_dir" && git status --short)
echo ""
echo "These changes will be LOST if you proceed with cleanup."
echo ""
echo "Options:"
echo "1) Cancel cleanup and commit changes first"
echo "2) Continue anyway and lose changes"
echo ""
read -p "What would you like to do? [1/2]: " -r choice
case "$choice" in
1)
echo "Cleanup cancelled. Please commit your changes first:"
echo " cd $worktree_dir"
echo " git add ."
echo " git commit -m 'message'"
echo " pnpm deep-work cleanup $issue"
exit 0
;;
2)
echo "Proceeding with cleanup, changes will be lost..."
;;
*)
echo "Invalid choice. Cancelling cleanup."
exit 1
;;
esac
fi
fi
# Check PR status
repo=$(resolve_deep_work_repo)
pr_status=""
pr_url=""
if [ -n "$branch" ] && [ "$branch" != "unknown" ]; then
if pr_json=$(gh pr view "$branch" -R "$repo" --json url,state,isDraft,mergeable 2>/dev/null); then
pr_url=$(echo "$pr_json" | jq -r '.url')
pr_state=$(echo "$pr_json" | jq -r '.state')
pr_draft=$(echo "$pr_json" | jq -r '.isDraft')
if [ "$pr_state" = "MERGED" ]; then
pr_status="merged"
elif [ "$pr_draft" = "true" ]; then
pr_status="draft"
elif [ "$pr_state" = "OPEN" ]; then
pr_status="open"
else
pr_status="$pr_state"
fi
echo ""
echo "📋 PR Status: $pr_status"
echo "🔗 PR URL: $pr_url"
else
echo ""
echo "📋 No PR found for branch $branch"
fi
fi
# Confirm cleanup
echo ""
echo "This will:"
echo " ✗ Remove worktree directory: $worktree_dir"
echo " ✗ Delete local branch: $branch"
echo " ✓ Keep remote branch and PR intact (if they exist)"
echo ""
if [ "$pr_status" = "draft" ] || [ "$pr_status" = "open" ]; then
echo "⚠️ Note: PR is still $pr_status. You may want to merge it first."
echo ""
fi
read -p "Continue with cleanup? [y/N]: " -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cleanup cancelled."
exit 0
fi
# Perform cleanup
cleanup_worktree "$issue" "true"
echo ""
echo "[deep-work] ✅ Cleanup complete!"
if [ -n "$pr_url" ]; then
echo ""
echo "📋 PR remains available: $pr_url"
if [ "$pr_status" = "merged" ]; then
echo ""
echo "🎉 Issue #$issue has been successfully completed and merged!"
fi
fi
echo ""
echo "🚀 Ready to start your next deep-work session:"
echo " pnpm deep-work pick"
echo " pnpm deep-work start <issue-number>"
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# Dispatcher for `pnpm deep-work <cmd> <args…>`.
# Commands: start, pick, continue, status, cleanup, list
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
usage() {
cat <<'EOF'
Usage: pnpm deep-work <command> [args...]
Full workflow automation for GitHub issues using worktrees and AI agents.
Commands:
start <issue-number> Start full workflow for an issue
pick Smart issue selection + start workflow
continue [issue-number] Resume workflow from current step
status Show all active worktrees and progress
list List all worktrees
cleanup <issue-number> Clean up specific worktree (with confirmation)
Examples:
pnpm deep-work start 1234 # Start working on issue #1234
pnpm deep-work pick # Let AI pick and start an issue
pnpm deep-work continue # Resume current work
pnpm deep-work status # Check progress on all issues
pnpm deep-work cleanup 1234 # Clean up worktree for issue #1234
EOF
}
cmd="${1:-}"
if [ -z "$cmd" ] || [ "$cmd" = "-h" ] || [ "$cmd" = "--help" ]; then
usage
exit 0
fi
case "$cmd" in
start)
shift
exec "$here/start.sh" "$@"
;;
pick)
shift
exec "$here/pick.sh" "$@"
;;
continue)
shift
exec "$here/continue.sh" "$@"
;;
status)
shift
exec "$here/status.sh" "$@"
;;
list)
shift
exec "$here/list.sh" "$@"
;;
cleanup)
shift
exec "$here/cleanup.sh" "$@"
;;
*)
echo "[deep-work] unknown command: $cmd" >&2
usage >&2
exit 1
;;
esac
+291
View File
@@ -0,0 +1,291 @@
#!/usr/bin/env bash
# continue.sh [issue-number]
#
# Resume deep-work workflow from current state
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./lib.sh
source "$here/lib.sh"
issue="${1:-}"
# If no issue provided, try to detect from current directory
if [ -z "$issue" ]; then
worktree_info=$(get_current_worktree_info)
if [ "$worktree_info" = "not_in_worktree" ]; then
echo "[deep-work] not currently in a worktree and no issue specified"
echo ""
echo "Usage: pnpm deep-work continue [issue-number]"
echo ""
echo "Active worktrees:"
list_deep_work_worktrees | while IFS='=' read -r key value; do
if [ "$key" = "issue" ]; then
issue_num="$value"
elif [ "$key" = "path" ]; then
echo " #$issue_num: $value"
fi
done
exit 1
fi
# Extract issue from worktree info
issue=$(echo "$worktree_info" | grep -o 'issue=[0-9]*' | cut -d= -f2)
echo "[deep-work] detected issue #$issue from current directory"
fi
# Validate issue number
case "$issue" in
''|*[!0-9]*)
echo "[deep-work] invalid issue number: $issue" >&2
exit 1
;;
esac
# Check if worktree exists
worktree_dir=$(worktree_dir_for_issue "$issue")
if ! worktree_exists "$issue"; then
echo "[deep-work] no worktree found for issue #$issue"
echo "[deep-work] use 'pnpm deep-work start $issue' to begin work"
exit 1
fi
echo "[deep-work] 🔄 Resuming work on issue #$issue"
# Change to worktree
cd "$worktree_dir"
echo "[deep-work] 📁 working in: $(pwd)"
echo "[deep-work] 🌿 branch: $(git branch --show-current)"
# Check git status to determine next step
git_status=$(git status --porcelain)
has_staged=$(git diff --cached --quiet && echo "false" || echo "true")
has_unstaged=$(git diff --quiet && echo "false" || echo "true")
has_untracked=$([ -z "$(git ls-files --others --exclude-standard)" ] && echo "false" || echo "true")
echo ""
echo "[deep-work] analyzing current state..."
# Determine what step to resume from
if [ "$has_staged" = "true" ] || [ "$has_unstaged" = "true" ] || [ "$has_untracked" = "true" ]; then
echo "[deep-work] 📝 detected uncommitted changes"
echo ""
echo "Git status:"
git status --short
echo ""
echo "Next steps:"
echo "1) Continue implementation - call codecrusher to finish work"
echo "2) Run quality checks - typecheck, lint, format, build"
echo "3) Commit changes - stage and commit current work"
echo "4) Manual review - inspect changes yourself"
echo ""
while true; do
read -p "What would you like to do? [1-4]: " -r choice
case "$choice" in
1)
echo "[deep-work] 💻 calling codecrusher to continue implementation..."
repo=$(resolve_deep_work_repo)
issue_json=$(gh issue view "$issue" -R "$repo" --json title,body,url)
title=$(jq -r '.title' <<<"$issue_json")
body=$(jq -r '.body // ""' <<<"$issue_json")
url=$(jq -r '.url' <<<"$issue_json")
implementation_prompt="Continue working on GitHub issue #${issue} from previous session.
Working branch: $(git branch --show-current)
Issue URL: ${url}
Issue title: ${title}
Current uncommitted changes detected. Please review the current state and continue implementation:
--- Issue body ---
${body}
--- end issue body ---
Complete the implementation following the existing patterns and ensure all requirements are met."
claude "$implementation_prompt" --task codecrusher
break
;;
2)
echo "[deep-work] 🔍 running quality checks..."
if run_quality_checks "continue"; then
echo "[deep-work] ✅ quality checks passed"
else
echo "[deep-work] ❌ quality checks failed - please fix issues"
fi
break
;;
3)
echo "[deep-work] 📝 preparing to commit..."
git add .
repo=$(resolve_deep_work_repo)
issue_json=$(gh issue view "$issue" -R "$repo" --json title,body)
title=$(jq -r '.title' <<<"$issue_json")
body=$(jq -r '.body // ""' <<<"$issue_json")
commit_message="fix(#${issue}): ${title}
${body}
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>"
git commit -m "$(cat <<EOF
$commit_message
EOF
)"
echo "[deep-work] ✅ changes committed"
break
;;
4)
echo "[deep-work] 👀 opening files for manual review..."
echo ""
echo "Modified files:"
git diff --name-only HEAD
git diff --name-only --cached
git ls-files --others --exclude-standard
echo ""
echo "Review complete. Run 'pnpm deep-work continue $issue' when ready to proceed."
exit 0
;;
*)
echo "Please enter 1, 2, 3, or 4"
continue
;;
esac
done
else
# Clean working tree - check if we need to push/PR
echo "[deep-work] 📋 working tree is clean"
# Check if we have unpushed commits
branch=$(git branch --show-current)
if git log origin/"$branch"..HEAD --oneline 2>/dev/null | grep -q .; then
echo "[deep-work] 📤 found unpushed commits"
echo ""
echo "Unpushed commits:"
git log origin/"$branch"..HEAD --oneline
echo ""
echo "Next steps:"
echo "1) Push and create PR"
echo "2) Run additional quality checks first"
echo "3) Update memory with learnings"
echo ""
while true; do
read -p "What would you like to do? [1-3]: " -r choice
case "$choice" in
1)
echo "[deep-work] 📤 pushing branch and creating PR..."
git push -u origin "$branch"
# Create PR if it doesn't exist
repo=$(resolve_deep_work_repo)
if ! gh pr view "$branch" -R "$repo" >/dev/null 2>&1; then
issue_json=$(gh issue view "$issue" -R "$repo" --json title,body)
title=$(jq -r '.title' <<<"$issue_json")
body=$(jq -r '.body // ""' <<<"$issue_json")
pr_body="## Summary
Closes #${issue}
${body}
🤖 Generated with [Claude Code](https://claude.ai/code)"
pr_url=$(gh pr create \
--title "fix(#${issue}): ${title}" \
--body "$pr_body" \
--draft \
--head "$(gh auth status 2>&1 | grep 'Logged in.*as' | sed 's/.*as //' | cut -d' ' -f1):$branch" \
--base main \
--repo "$repo")
echo "[deep-work] 📝 PR created: $pr_url"
else
echo "[deep-work] ✅ PR already exists"
fi
break
;;
2)
if run_quality_checks "continue"; then
echo "[deep-work] ✅ quality checks passed"
else
echo "[deep-work] ❌ quality checks failed - please fix issues"
fi
break
;;
3)
echo "[deep-work] 🧠 calling memory-keeper..."
repo=$(resolve_deep_work_repo)
issue_json=$(gh issue view "$issue" -R "$repo" --json title)
title=$(jq -r '.title' <<<"$issue_json")
memory_prompt="Update project memory with learnings from issue #${issue}: \"${title}\".
Review the recent commits and changes to extract useful insights for future development."
claude "$memory_prompt" --task memory-keeper
break
;;
*)
echo "Please enter 1, 2, or 3"
continue
;;
esac
done
else
echo "[deep-work] 📋 everything up to date"
echo ""
echo "Next steps:"
echo "1) Check PR status and reviews"
echo "2) Run final quality checks"
echo "3) Clean up worktree"
echo ""
while true; do
read -p "What would you like to do? [1-3]: " -r choice
case "$choice" in
1)
repo=$(resolve_deep_work_repo)
if gh pr view "$branch" -R "$repo" >/dev/null 2>&1; then
echo "[deep-work] 📋 PR status:"
gh pr view "$branch" -R "$repo"
else
echo "[deep-work] ❌ no PR found for this branch"
fi
break
;;
2)
if run_quality_checks "final"; then
echo "[deep-work] ✅ final quality checks passed"
else
echo "[deep-work] ❌ quality checks failed"
fi
break
;;
3)
echo "[deep-work] 🧹 cleaning up worktree..."
cd "$(git rev-parse --show-toplevel)/.."
cleanup_worktree "$issue"
break
;;
*)
echo "Please enter 1, 2, or 3"
continue
;;
esac
done
fi
fi
echo ""
echo "[deep-work] 🎯 Continue session complete!"
echo "[deep-work] Use 'pnpm deep-work continue $issue' to resume anytime."
+208
View File
@@ -0,0 +1,208 @@
#!/usr/bin/env bash
# Shared utilities for deep-work scripts
set -euo pipefail
# Source the existing review lib for repo resolution
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$here/../.." && pwd)"
# shellcheck source=../review/lib.sh
source "$repo_root/scripts/review/lib.sh"
# Worktree utilities
worktree_dir_for_issue() {
local issue="$1"
echo "../oh-$issue"
}
worktree_branch_for_issue() {
local issue="$1"
local title="$2"
local branch_prefix="${DEEP_WORK_BRANCH_PREFIX:-fix}"
# Create slug from title
local slug
slug=$(printf '%s' "$title" \
| tr '[:upper:]' '[:lower:]' \
| sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' \
| cut -c1-30 \
| sed -E 's/-+$//')
if [ -z "$slug" ]; then
slug="work"
fi
echo "${branch_prefix}/${issue}-${slug}"
}
# Check if worktree exists for issue
worktree_exists() {
local issue="$1"
local worktree_dir
worktree_dir=$(worktree_dir_for_issue "$issue")
[ -d "$worktree_dir" ]
}
# Get current worktree info
get_current_worktree_info() {
local current_dir
current_dir=$(pwd)
# Check if we're in a worktree by looking for oh-<number> pattern
if [[ "$current_dir" =~ /oh-([0-9]+)$ ]]; then
local issue="${BASH_REMATCH[1]}"
local branch
branch=$(git branch --show-current 2>/dev/null || echo "")
echo "issue=$issue branch=$branch dir=$current_dir"
else
echo "not_in_worktree"
fi
}
# Resolve repo (with DEEP_WORK_REPO override)
resolve_deep_work_repo() {
local repo="${DEEP_WORK_REPO:-${WORK_REPO:-${REVIEW_REPO:-}}}"
if [ -z "$repo" ]; then
repo=$(REVIEW_REPO= resolve_repo)
fi
echo "$repo"
}
# Create worktree for issue
create_worktree() {
local issue="$1"
local title="$2"
local worktree_dir
worktree_dir=$(worktree_dir_for_issue "$issue")
local branch
branch=$(worktree_branch_for_issue "$issue" "$title")
if worktree_exists "$issue"; then
echo "[deep-work] worktree for issue #$issue already exists at $worktree_dir"
return 1
fi
echo "[deep-work] creating worktree at $worktree_dir with branch $branch..."
git worktree add "$worktree_dir" -b "$branch"
echo "[deep-work] worktree created: $worktree_dir"
echo "[deep-work] branch: $branch"
}
# List all deep-work worktrees
list_deep_work_worktrees() {
git worktree list --porcelain | while IFS= read -r line; do
if [[ "$line" =~ ^worktree\ (.*/oh-([0-9]+))$ ]]; then
local path="${BASH_REMATCH[1]}"
local issue="${BASH_REMATCH[2]}"
echo "issue=$issue path=$path"
fi
done
}
# Clean up worktree
cleanup_worktree() {
local issue="$1"
local force="${2:-false}"
local worktree_dir
worktree_dir=$(worktree_dir_for_issue "$issue")
if ! worktree_exists "$issue"; then
echo "[deep-work] no worktree found for issue #$issue"
return 1
fi
if [ "$force" != "true" ]; then
echo "This will remove the worktree at $worktree_dir and delete the branch."
echo "Any uncommitted changes will be lost."
read -p "Are you sure? (y/N) " -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled."
return 1
fi
fi
# Get branch name before removing worktree
local branch
if [ -d "$worktree_dir" ]; then
branch=$(cd "$worktree_dir" && git branch --show-current 2>/dev/null || echo "")
fi
echo "[deep-work] removing worktree $worktree_dir..."
git worktree remove "$worktree_dir" --force
if [ -n "$branch" ] && git show-ref --verify --quiet "refs/heads/$branch"; then
echo "[deep-work] deleting branch $branch..."
git branch -D "$branch" || echo "[deep-work] failed to delete branch $branch"
fi
echo "[deep-work] cleanup completed for issue #$issue"
}
# Sync with upstream
sync_upstream() {
echo "[deep-work] syncing with upstream..."
# Ensure we're on main
if [ "$(git branch --show-current)" != "main" ]; then
echo "[deep-work] switching to main branch..."
git checkout main
fi
# Fetch and merge upstream
if git remote get-url upstream >/dev/null 2>&1; then
git fetch upstream
echo "[deep-work] merging upstream/main..."
git merge --ff-only upstream/main || git merge upstream/main
fi
# Update from origin
if git remote get-url origin >/dev/null 2>&1; then
echo "[deep-work] pulling from origin/main..."
git pull --ff-only origin main
fi
# Update submodules
git submodule update --init --recursive
echo "[deep-work] upstream sync completed"
}
# Run quality checks
run_quality_checks() {
local step="$1"
echo "[deep-work] running quality checks for $step..."
# Typecheck
echo "[deep-work] typechecking..."
if ! pnpm typecheck; then
echo "[deep-work] ❌ typecheck failed"
return 1
fi
# Lint
echo "[deep-work] linting..."
if ! pnpm lint; then
echo "[deep-work] ❌ lint failed"
return 1
fi
# Format check
echo "[deep-work] format check..."
if ! pnpm format:check; then
echo "[deep-work] running formatter..."
pnpm format
fi
# Build
echo "[deep-work] building..."
if ! pnpm build; then
echo "[deep-work] ❌ build failed"
return 1
fi
echo "[deep-work] ✅ all quality checks passed"
}
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# list.sh
#
# Simple list of all deep-work worktrees
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./lib.sh
source "$here/lib.sh"
echo "[deep-work] 📂 All deep-work worktrees:"
echo ""
worktrees_found=false
list_deep_work_worktrees | while IFS='=' read -r key value; do
if [ "$key" = "issue" ]; then
issue_num="$value"
worktrees_found=true
elif [ "$key" = "path" ]; then
path="$value"
branch=""
status=""
if [ -d "$path" ]; then
if [ -d "$path/.git" ]; then
branch=$(cd "$path" && git branch --show-current 2>/dev/null || echo "unknown")
# Quick status check
if cd "$path" && git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then
status="clean"
else
status="uncommitted changes"
fi
else
status="not a git repo"
fi
else
status="directory missing"
fi
printf " #%-4s %s\n" "$issue_num" "$path"
printf " branch: %s\n" "$branch"
printf " status: %s\n" "$status"
echo ""
fi
done
# Check if we found any worktrees (need to use a different approach since the while loop runs in a subshell)
if ! list_deep_work_worktrees | grep -q "issue="; then
echo " No deep-work worktrees found."
echo ""
echo " To create a new one:"
echo " pnpm deep-work start <issue-number>"
echo " pnpm deep-work pick"
fi
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/env bash
# pick.sh
#
# Smart issue selection based on workflow criteria:
# - Filter by complexity (easy to medium)
# - Filter by description quality (substantial content)
# - Prioritize bugs over features
# - Avoid blocked issues
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./lib.sh
source "$here/lib.sh"
require gh jq
repo=$(resolve_deep_work_repo)
echo "[deep-work] 🔍 Smart issue picking from $repo..."
# Get all open issues
echo "[deep-work] fetching open issues..."
issues_json=$(gh issue list -R "$repo" --state open --limit 100 \
--json number,title,body,labels,assignees,state,url)
if [ "$(echo "$issues_json" | jq 'length')" -eq 0 ]; then
echo "[deep-work] no open issues found in $repo"
exit 1
fi
echo "[deep-work] found $(echo "$issues_json" | jq 'length') open issues"
# Filter and score issues
echo "[deep-work] analyzing issues..."
candidates=$(echo "$issues_json" | jq -r '
map(select(
# Must be open
.state == "OPEN" and
# Must not be assigned (or ask user about assigned ones)
(.assignees | length == 0) and
# Must have substantial description (>500 chars)
(.body // "" | length > 500)
)) |
# Add scoring
map(
. + {
"score": (
# Prefer bugs (+3)
(if (.labels | map(.name) | contains(["bug"])) then 3 else 0 end) +
# Prefer enhancement (+2)
(if (.labels | map(.name) | contains(["enhancement"])) then 2 else 0 end) +
# Prefer good-first-issue (+1)
(if (.labels | map(.name) | contains(["good first issue"])) then 1 else 0 end) +
# Penalize large scope labels (-2)
(if (.labels | map(.name) | contains(["epic", "large", "major"])) then -2 else 0 end) +
# Description quality bonus (length / 1000)
((.body // "" | length) / 1000 | floor)
),
"complexity": (
if (.labels | map(.name) | contains(["epic", "large", "major"])) then "hard"
elif (.labels | map(.name) | contains(["good first issue", "easy"])) then "easy"
else "medium"
end
)
}
) |
# Filter out hard complexity
map(select(.complexity != "hard")) |
# Sort by score (descending)
sort_by(-.score) |
# Take top 10
.[0:10]
')
num_candidates=$(echo "$candidates" | jq 'length')
if [ "$num_candidates" -eq 0 ]; then
echo "[deep-work] no suitable issues found after filtering"
echo ""
echo "Criteria used:"
echo "- Open and unassigned"
echo "- Substantial description (>500 chars)"
echo "- Not labeled as epic/large/major complexity"
echo ""
echo "Try browsing issues manually: gh issue list -R $repo"
exit 1
fi
echo "[deep-work] found $num_candidates suitable candidates"
echo ""
# Display top candidates
echo "🏆 Top issue candidates:"
echo ""
echo "$candidates" | jq -r '.[] |
"\(.number). \(.title)
🏷️ Labels: \((.labels | map(.name) | join(", ")) // "none")
📊 Score: \(.score) | Complexity: \(.complexity)
📝 Description: \(.body // "no description" | .[0:100])...
🔗 \(.url)
"
'
echo ""
echo "Select an issue to work on:"
echo "1-$num_candidates) Pick by number from list above"
echo "0) Browse all issues manually"
echo "q) Quit"
echo ""
while true; do
read -p "Your choice [1-$num_candidates/0/q]: " -r choice
case "$choice" in
q|Q)
echo "Cancelled."
exit 0
;;
0)
echo "[deep-work] opening issue browser..."
gh issue list -R "$repo" --web
exit 0
;;
''|*[!0-9]*)
echo "Please enter a number between 1-$num_candidates, 0, or q"
continue
;;
*)
if [ "$choice" -ge 1 ] && [ "$choice" -le "$num_candidates" ]; then
selected_idx=$((choice - 1))
selected_issue=$(echo "$candidates" | jq -r ".[$selected_idx].number")
selected_title=$(echo "$candidates" | jq -r ".[$selected_idx].title")
echo ""
echo "[deep-work] 🎯 Selected issue #$selected_issue: $selected_title"
echo ""
echo "Starting full workflow..."
exec "$here/start.sh" "$selected_issue"
else
echo "Please enter a number between 1-$num_candidates, 0, or q"
continue
fi
;;
esac
done
+379
View File
@@ -0,0 +1,379 @@
#!/usr/bin/env bash
# start.sh <issue-number>
#
# Full workflow automation for a GitHub issue:
# 0. Worktree setup (create worktree → new branch)
# 1. Issue fetching and validation
# 1.5. Context gathering (CLAUDE.md, memory.md, recent commits)
# 2. Planning with architectobot agent
# 3. Implementation with codecrusher agent
# 4. Cross-checking (typecheck, lint, format, build)
# 5. Quality checks and test runs
# 6. Memory updates with memory-keeper agent
# 7. Commit with proper message
# 8. Merge main and resolve conflicts
# 9. Push and create draft PR
# 10. Review cycle with pr-reviewer agent
# 11. Mark ready for review (with user confirmation)
# 12. Cleanup (with user confirmation)
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$here/../.." && pwd)"
# shellcheck source=./lib.sh
source "$here/lib.sh"
require git gh jq
if [ -z "${1:-}" ]; then
echo "Usage: pnpm deep-work start <issue-number>" >&2
exit 1
fi
case "$1" in
''|*[!0-9]*)
echo "[deep-work] issue-number must be numeric, got: $1" >&2
exit 1
;;
esac
issue="$1"
shift
repo=$(resolve_deep_work_repo)
echo "[deep-work] 🚀 Starting full workflow for issue #$issue from $repo"
# Step 0: Ensure we're on main and synced
echo ""
echo "[deep-work] === Step 0: Upstream sync & worktree setup ==="
sync_upstream
# Step 1: Fetch issue details
echo ""
echo "[deep-work] === Step 1: Fetching issue details ==="
echo "[deep-work] fetching issue #$issue from $repo..."
issue_json=$(gh issue view "$issue" -R "$repo" \
--json number,title,body,labels,state,url,assignees)
state=$(jq -r '.state' <<<"$issue_json")
if [ "$state" != "OPEN" ]; then
echo "[deep-work] ⚠️ issue #$issue is $state — continuing anyway" >&2
fi
title=$(jq -r '.title' <<<"$issue_json")
body=$(jq -r '.body // ""' <<<"$issue_json")
url=$(jq -r '.url' <<<"$issue_json")
labels=$(jq -r '[.labels[].name] | join(", ")' <<<"$issue_json")
echo "[deep-work] 📋 Issue: $title"
echo "[deep-work] 🏷️ Labels: ${labels:-(none)}"
echo "[deep-work] 🔗 URL: $url"
# Create worktree
echo ""
echo "[deep-work] creating worktree..."
if ! create_worktree "$issue" "$title"; then
worktree_dir=$(worktree_dir_for_issue "$issue")
echo "[deep-work] using existing worktree at $worktree_dir"
fi
# Change to worktree
worktree_dir=$(worktree_dir_for_issue "$issue")
cd "$worktree_dir"
echo "[deep-work] 📁 working in: $(pwd)"
echo "[deep-work] 🌿 branch: $(git branch --show-current)"
# Step 1.5: Context gathering
echo ""
echo "[deep-work] === Step 1.5: Context gathering ==="
echo "[deep-work] reading project documentation..."
context_info=""
if [ -f "CLAUDE.md" ]; then
echo "[deep-work] ✅ found CLAUDE.md"
context_info+="✅ CLAUDE.md available\n"
else
echo "[deep-work] ⚠️ CLAUDE.md not found"
context_info+="⚠️ CLAUDE.md not found\n"
fi
if [ -f ".claude/memory.md" ]; then
echo "[deep-work] ✅ found .claude/memory.md"
context_info+="✅ .claude/memory.md available\n"
else
echo "[deep-work] ⚠️ .claude/memory.md not found"
context_info+="⚠️ .claude/memory.md not found\n"
fi
echo "[deep-work] checking recent commits..."
recent_commits=$(git log --oneline -10 main)
context_info+="📝 Recent commits:\n$recent_commits\n"
# Step 2: Planning phase
echo ""
echo "[deep-work] === Step 2: Planning with architectobot ==="
planning_prompt="You are starting work on GitHub issue #${issue} from ${repo}.
Working branch: $(git branch --show-current)
Issue URL: ${url}
Issue title: ${title}
Labels: ${labels:-(none)}
Context information:
${context_info}
--- Issue body ---
${body}
--- end issue body ---
Please analyze this issue and create a detailed implementation plan. Follow the workflow in CLAUDE.md and consider the existing codebase architecture. Break down the work into clear, manageable steps.
Focus on:
1. Understanding the requirements
2. Identifying affected components
3. Planning the implementation approach
4. Considering testing strategy
5. Potential edge cases or challenges
Output a structured plan that can guide the implementation phase."
echo "[deep-work] 🧠 calling architectobot for planning..."
# Call architectobot agent directly via claude with task flag
echo "$planning_prompt" | claude --task architectobot
echo ""
echo "[deep-work] 📋 Planning complete. Press Enter to continue to implementation..."
read -r
# Step 3: Implementation phase
echo ""
echo "[deep-work] === Step 3: Implementation with codecrusher ==="
implementation_prompt="Continue working on GitHub issue #${issue} from the planning phase.
Working branch: $(git branch --show-current)
Issue URL: ${url}
Issue title: ${title}
The architectobot has analyzed the requirements and created a plan. Now implement the solution:
--- Issue body ---
${body}
--- end issue body ---
Follow the implementation plan and:
1. Write clean, idiomatic code following existing patterns
2. Add appropriate tests for new functionality
3. Update documentation if needed
4. Ensure all changes are minimal and focused
5. Follow the project's coding conventions in CLAUDE.md
Keep the diff minimal and focused on the specific issue requirements."
echo "[deep-work] 💻 calling codecrusher for implementation..."
# Call codecrusher agent directly via claude with task flag
echo "$implementation_prompt" | claude --task codecrusher
echo ""
echo "[deep-work] ✅ Implementation complete. Press Enter to continue to quality checks..."
read -r
# Step 4 & 5: Quality checks
echo ""
echo "[deep-work] === Steps 4-5: Quality checks ==="
if ! run_quality_checks "implementation"; then
echo "[deep-work] ❌ quality checks failed. Please fix issues and re-run."
echo "[deep-work] to resume: pnpm deep-work continue $issue"
exit 1
fi
# Step 6: Memory updates
echo ""
echo "[deep-work] === Step 6: Memory updates ==="
echo "[deep-work] 🧠 calling memory-keeper to update project knowledge..."
memory_prompt="I've just completed implementation work on GitHub issue #${issue}: \"${title}\".
Please update the project memory (.claude/memory.md) with any important learnings, patterns, gotchas, or insights from this work that would help future development on this project.
Focus on:
- New patterns or conventions established
- Technical challenges overcome
- Important gotchas or edge cases discovered
- Useful debugging techniques
- Architecture insights
- Testing approaches that worked well
Only add genuinely useful information that would help someone working on similar issues in the future."
# Call memory-keeper agent directly via claude with task flag
echo "$memory_prompt" | claude --task memory-keeper
# Step 7: Commit
echo ""
echo "[deep-work] === Step 7: Commit ==="
echo "[deep-work] staging changes for commit..."
git add .
# Generate commit message
commit_message="fix(#${issue}): ${title}
${body}
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>"
echo "[deep-work] committing changes..."
git commit -m "$(cat <<EOF
$commit_message
EOF
)"
echo "[deep-work] ✅ changes committed"
# Step 8: Merge main and resolve conflicts
echo ""
echo "[deep-work] === Step 8: Merge main & resolve conflicts ==="
echo "[deep-work] fetching latest main..."
# Fetch main from main worktree location
cd "$repo_root"
sync_upstream
cd "$worktree_dir"
echo "[deep-work] merging main into working branch..."
if ! git merge main; then
echo "[deep-work] ⚠️ merge conflicts detected. Please resolve conflicts and continue."
echo "[deep-work] after resolving conflicts, run: pnpm deep-work continue $issue"
exit 1
fi
echo "[deep-work] ✅ main merged successfully"
# Step 9: Push and create draft PR
echo ""
echo "[deep-work] === Step 9: Push & create draft PR ==="
branch=$(git branch --show-current)
echo "[deep-work] pushing branch $branch to origin..."
# Push to origin (user's fork)
git push -u origin "$branch"
# Create draft PR
echo "[deep-work] creating draft PR..."
pr_body="## Summary
Closes #${issue}
${body}
## Changes Made
[Brief description of implementation approach]
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed
- [ ] Quality checks pass (typecheck, lint, format, build)
## Checklist
- [x] Code follows project conventions
- [x] Tests added for new functionality
- [x] Documentation updated if needed
- [x] Quality checks pass
- [x] Memory updated with learnings
🤖 Generated with [Claude Code](https://claude.ai/code)"
pr_url=$(gh pr create \
--title "fix(#${issue}): ${title}" \
--body "$pr_body" \
--draft \
--head "$(gh auth status 2>&1 | grep 'Logged in.*as' | sed 's/.*as //' | cut -d' ' -f1):$branch" \
--base main \
--repo "$repo")
echo "[deep-work] 📝 draft PR created: $pr_url"
# Step 10: Review cycle
echo ""
echo "[deep-work] === Step 10: Review cycle ==="
echo "[deep-work] 🔍 calling pr-reviewer for automated review..."
review_prompt="Please perform a thorough CodeRabbit-style review of this PR: $pr_url
This PR addresses issue #${issue}: \"${title}\"
Provide:
1. Walkthrough of changes
2. Change summary table
3. Per-file analysis
4. Inline comments with concrete suggestions
5. Identify any potential issues or improvements
After review, apply any approved suggestions, run quality checks, commit and push updates."
# Call pr-reviewer agent directly via claude with task flag
echo "$review_prompt" | claude --task pr-reviewer
# Step 11: Mark ready for review (user confirmation)
echo ""
echo "[deep-work] === Step 11: Ready for review ==="
echo "[deep-work] 🎉 Workflow complete! The PR has been created and auto-reviewed."
echo ""
echo "PR URL: $pr_url"
echo "Branch: $branch"
echo "Worktree: $worktree_dir"
echo ""
echo "Would you like to mark the PR as ready for review? (y/N)"
read -p "> " -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "[deep-work] marking PR as ready for review..."
gh pr ready "$pr_url"
echo "[deep-work] ✅ PR marked as ready for review"
else
echo "[deep-work] PR remains in draft. You can mark it ready later with:"
echo " gh pr ready $pr_url"
fi
# Step 12: Cleanup (user confirmation)
echo ""
echo "[deep-work] === Step 12: Cleanup ==="
echo ""
echo "The worktree for issue #$issue is at $worktree_dir (branch: $branch)."
echo ""
echo "Would you like me to clean up the worktree? This will:"
echo "- Remove the worktree directory"
echo "- Delete the local branch"
echo "- Keep the remote branch and PR intact"
echo ""
echo "Clean up worktree? (y/N)"
read -p "> " -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
cd "$repo_root"
cleanup_worktree "$issue" "true"
echo "[deep-work] ✅ worktree cleaned up"
else
echo "[deep-work] worktree preserved. You can clean it up later with:"
echo " pnpm deep-work cleanup $issue"
fi
echo ""
echo "[deep-work] 🎊 Deep work session complete!"
echo "PR: $pr_url"
echo ""
echo "Next steps:"
echo "- Monitor PR for reviewer feedback"
echo "- Address any review comments"
echo "- Merge when approved"
+156
View File
@@ -0,0 +1,156 @@
#!/usr/bin/env bash
# status.sh
#
# Show status of all deep-work worktrees and progress
set -euo pipefail
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./lib.sh
source "$here/lib.sh"
require git gh jq
repo=$(resolve_deep_work_repo)
echo "[deep-work] 📊 Deep Work Status Report"
echo "Repository: $repo"
echo ""
# Get current location info
current_info=$(get_current_worktree_info)
if [ "$current_info" != "not_in_worktree" ]; then
current_issue=$(echo "$current_info" | grep -o 'issue=[0-9]*' | cut -d= -f2)
current_branch=$(echo "$current_info" | grep -o 'branch=[^[:space:]]*' | cut -d= -f2)
current_dir=$(echo "$current_info" | grep -o 'dir=[^[:space:]]*' | cut -d= -f2)
echo "📍 Current location: Issue #$current_issue (branch: $current_branch)"
echo " Directory: $current_dir"
echo ""
fi
# List all deep-work worktrees
worktrees_data=""
while IFS='=' read -r key value; do
if [ "$key" = "issue" ]; then
current_issue_num="$value"
elif [ "$key" = "path" ]; then
current_path="$value"
worktrees_data+="$current_issue_num|$current_path\n"
fi
done < <(list_deep_work_worktrees)
if [ -z "$worktrees_data" ]; then
echo "🏜️ No active deep-work sessions found."
echo ""
echo "To start working on an issue:"
echo " pnpm deep-work start <issue-number>"
echo " pnpm deep-work pick"
exit 0
fi
echo "🚀 Active deep-work sessions:"
echo ""
printf "%-6s %-20s %-15s %-30s %s\n" "ISSUE" "STATUS" "BRANCH" "TITLE" "PROGRESS"
echo "────────────────────────────────────────────────────────────────────────────────────"
echo -e "$worktrees_data" | while IFS='|' read -r issue_num path; do
if [ -z "$issue_num" ] || [ -z "$path" ]; then
continue
fi
if [ ! -d "$path" ]; then
printf "%-6s %-20s %-15s %-30s %s\n" "#$issue_num" "❌ MISSING" "?" "?" "Worktree directory not found"
continue
fi
# Get branch and git status from worktree
branch=""
git_clean=""
has_commits=""
pr_exists=""
if [ -d "$path/.git" ]; then
branch=$(cd "$path" && git branch --show-current 2>/dev/null || echo "unknown")
# Check if working tree is clean
if cd "$path" && git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then
git_clean="clean"
else
git_clean="dirty"
fi
# Check for commits ahead of origin
if cd "$path" && git log origin/"$branch"..HEAD --oneline 2>/dev/null | grep -q .; then
has_commits="unpushed"
elif cd "$path" && git log --oneline -1 >/dev/null 2>&1; then
has_commits="pushed"
else
has_commits="no_commits"
fi
# Check if PR exists
if gh pr view "$branch" -R "$repo" >/dev/null 2>&1; then
pr_status=$(gh pr view "$branch" -R "$repo" --json state,isDraft | jq -r 'if .isDraft then "draft" else .state end')
pr_exists="$pr_status"
else
pr_exists="no_pr"
fi
fi
# Get issue title
issue_title=""
if issue_json=$(gh issue view "$issue_num" -R "$repo" --json title 2>/dev/null); then
issue_title=$(echo "$issue_json" | jq -r '.title' | cut -c1-30)
else
issue_title="(issue not found)"
fi
# Determine overall status
status=""
progress=""
if [ "$git_clean" = "dirty" ]; then
status="🛠️ WORKING"
progress="Implementation in progress"
elif [ "$has_commits" = "no_commits" ]; then
status="🆕 FRESH"
progress="Just started"
elif [ "$has_commits" = "unpushed" ]; then
status="📝 COMMITTED"
progress="Ready to push"
elif [ "$pr_exists" = "no_pr" ]; then
status="📤 PUSHED"
progress="Ready for PR"
elif [ "$pr_exists" = "draft" ]; then
status="📋 DRAFT_PR"
progress="PR created, needs review"
elif [ "$pr_exists" = "OPEN" ]; then
status="🔍 IN_REVIEW"
progress="Under review"
elif [ "$pr_exists" = "MERGED" ]; then
status="✅ MERGED"
progress="Complete, ready for cleanup"
else
status="❓ UNKNOWN"
progress="Unknown state"
fi
printf "%-6s %-20s %-15s %-30s %s\n" "#$issue_num" "$status" "$branch" "$issue_title" "$progress"
done
echo ""
echo "📋 Legend:"
echo " 🆕 FRESH - Just started, no commits yet"
echo " 🛠️ WORKING - Implementation in progress (uncommitted changes)"
echo " 📝 COMMITTED - Work committed locally, ready to push"
echo " 📤 PUSHED - Pushed to remote, ready to create PR"
echo " 📋 DRAFT_PR - Draft PR created, under development"
echo " 🔍 IN_REVIEW - PR open and ready for review"
echo " ✅ MERGED - PR merged, ready for cleanup"
echo ""
echo "🛠️ Commands:"
echo " pnpm deep-work continue <issue> - Resume work on specific issue"
echo " pnpm deep-work cleanup <issue> - Clean up completed worktree"
echo " pnpm deep-work start <issue> - Start new issue"
echo " pnpm deep-work pick - Pick and start new issue"