mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
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:
co-authored by
Claude
Steven Enamakel
parent
574d40a40e
commit
98b579e6c7
@@ -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",
|
||||
|
||||
Executable
+157
@@ -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>"
|
||||
Executable
+67
@@ -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
|
||||
Executable
+291
@@ -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."
|
||||
Executable
+208
@@ -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"
|
||||
}
|
||||
Executable
+57
@@ -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
|
||||
Executable
+158
@@ -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
|
||||
Executable
+379
@@ -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"
|
||||
Executable
+156
@@ -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"
|
||||
Reference in New Issue
Block a user