@cyperx/clawforge 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +312 -0
  3. package/VERSION +1 -0
  4. package/bin/attach.sh +98 -0
  5. package/bin/check-agents.sh +343 -0
  6. package/bin/clawforge +257 -0
  7. package/bin/clawforge-dashboard +0 -0
  8. package/bin/clean.sh +257 -0
  9. package/bin/config.sh +111 -0
  10. package/bin/conflicts.sh +224 -0
  11. package/bin/cost.sh +273 -0
  12. package/bin/dashboard.sh +557 -0
  13. package/bin/diff.sh +109 -0
  14. package/bin/doctor.sh +196 -0
  15. package/bin/eval.sh +217 -0
  16. package/bin/history.sh +91 -0
  17. package/bin/init.sh +182 -0
  18. package/bin/learn.sh +230 -0
  19. package/bin/logs.sh +126 -0
  20. package/bin/memory.sh +207 -0
  21. package/bin/merge-helper.sh +174 -0
  22. package/bin/multi-review.sh +215 -0
  23. package/bin/notify.sh +93 -0
  24. package/bin/on-complete.sh +149 -0
  25. package/bin/parse-cost.sh +205 -0
  26. package/bin/pr.sh +167 -0
  27. package/bin/resume.sh +183 -0
  28. package/bin/review-mode.sh +163 -0
  29. package/bin/review-pr.sh +145 -0
  30. package/bin/routing.sh +88 -0
  31. package/bin/scope-task.sh +169 -0
  32. package/bin/spawn-agent.sh +190 -0
  33. package/bin/sprint.sh +320 -0
  34. package/bin/steer.sh +107 -0
  35. package/bin/stop.sh +136 -0
  36. package/bin/summary.sh +182 -0
  37. package/bin/swarm.sh +525 -0
  38. package/bin/templates.sh +244 -0
  39. package/lib/common.sh +302 -0
  40. package/lib/templates/bugfix.json +6 -0
  41. package/lib/templates/migration.json +7 -0
  42. package/lib/templates/refactor.json +6 -0
  43. package/lib/templates/security-audit.json +5 -0
  44. package/lib/templates/test-coverage.json +6 -0
  45. package/package.json +31 -0
  46. package/registry/conflicts.jsonl +0 -0
  47. package/registry/costs.jsonl +0 -0
  48. package/tui/PRD.md +106 -0
  49. package/tui/agent.go +266 -0
  50. package/tui/animation.go +192 -0
  51. package/tui/dashboard.go +219 -0
  52. package/tui/filter.go +68 -0
  53. package/tui/go.mod +25 -0
  54. package/tui/go.sum +46 -0
  55. package/tui/keybindings.go +229 -0
  56. package/tui/main.go +61 -0
  57. package/tui/model.go +166 -0
  58. package/tui/steer.go +69 -0
  59. package/tui/styles.go +69 -0
package/bin/stop.sh ADDED
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env bash
2
+ # stop.sh — Stop a running agent
3
+ # Usage: clawforge stop <id> [--yes] [--clean]
4
+ set -euo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ source "${SCRIPT_DIR}/../lib/common.sh"
8
+
9
+ # ── Help ───────────────────────────────────────────────────────────────
10
+ usage() {
11
+ cat <<EOF
12
+ Usage: clawforge stop <id> [flags]
13
+
14
+ Stop a running agent. Kills the tmux session and marks task as stopped.
15
+
16
+ Arguments:
17
+ <id> Task short ID (#1), full ID, or sub-agent ID (3.2)
18
+
19
+ Flags:
20
+ --yes Skip confirmation prompt
21
+ --clean Also remove the worktree
22
+ --help Show this help
23
+
24
+ Examples:
25
+ clawforge stop 1
26
+ clawforge stop 3 --yes --clean
27
+ EOF
28
+ }
29
+
30
+ # ── Parse args ────────────────────────────────────────────────────────
31
+ TASK_REF="" YES=false CLEAN=false
32
+
33
+ while [[ $# -gt 0 ]]; do
34
+ case "$1" in
35
+ --yes) YES=true; shift ;;
36
+ --clean) CLEAN=true; shift ;;
37
+ --help|-h) usage; exit 0 ;;
38
+ --*) log_error "Unknown option: $1"; usage; exit 1 ;;
39
+ *)
40
+ if [[ -z "$TASK_REF" ]]; then
41
+ TASK_REF="$1"
42
+ else
43
+ log_error "Unexpected argument: $1"
44
+ usage
45
+ exit 1
46
+ fi
47
+ shift
48
+ ;;
49
+ esac
50
+ done
51
+
52
+ if [[ -z "$TASK_REF" ]]; then
53
+ log_error "Task ID is required"
54
+ usage
55
+ exit 1
56
+ fi
57
+
58
+ # ── Resolve task ──────────────────────────────────────────────────────
59
+ TASK_ID=$(resolve_task_id "$TASK_REF")
60
+ if [[ -z "$TASK_ID" ]]; then
61
+ log_error "Could not resolve task: $TASK_REF"
62
+ exit 1
63
+ fi
64
+
65
+ TASK=$(registry_get "$TASK_ID")
66
+ if [[ -z "$TASK" ]]; then
67
+ log_error "Task not found: $TASK_ID"
68
+ exit 1
69
+ fi
70
+
71
+ STATUS=$(echo "$TASK" | jq -r '.status')
72
+ TMUX_SESSION=$(echo "$TASK" | jq -r '.tmuxSession')
73
+ WORKTREE=$(echo "$TASK" | jq -r '.worktree')
74
+ DESCRIPTION=$(echo "$TASK" | jq -r '.description' | head -c 50)
75
+
76
+ # ── Already stopped? ──────────────────────────────────────────────────
77
+ if [[ "$STATUS" == "stopped" || "$STATUS" == "archived" ]]; then
78
+ echo "Task $TASK_REF is already $STATUS."
79
+ exit 0
80
+ fi
81
+
82
+ # ── Confirm ───────────────────────────────────────────────────────────
83
+ if ! $YES; then
84
+ echo "Stop task $TASK_REF? [$STATUS] \"$DESCRIPTION\""
85
+ $CLEAN && echo " (will also remove worktree: $WORKTREE)"
86
+ echo -n " Confirm [y/N]: "
87
+ read -r confirm
88
+ if [[ ! "$confirm" =~ ^[yY] ]]; then
89
+ echo "Cancelled."
90
+ exit 0
91
+ fi
92
+ fi
93
+
94
+ # ── Kill tmux session ─────────────────────────────────────────────────
95
+ if [[ -n "$TMUX_SESSION" ]]; then
96
+ tmux kill-session -t "$TMUX_SESSION" 2>/dev/null || true
97
+ log_info "Killed tmux session: $TMUX_SESSION"
98
+ fi
99
+
100
+ # ── Update registry ───────────────────────────────────────────────────
101
+ NOW=$(epoch_ms)
102
+ registry_update "$TASK_ID" "status" '"stopped"'
103
+ registry_update "$TASK_ID" "completedAt" "$NOW"
104
+
105
+ # ── Clean worktree if requested ───────────────────────────────────────
106
+ if $CLEAN && [[ -n "$WORKTREE" ]] && [[ -d "$WORKTREE" ]]; then
107
+ REPO=$(echo "$TASK" | jq -r '.repo')
108
+ git -C "$REPO" worktree remove "$WORKTREE" --force 2>/dev/null || rm -rf "$WORKTREE"
109
+ log_info "Removed worktree: $WORKTREE"
110
+ fi
111
+
112
+ # ── Also stop sub-agents if this is a swarm parent ────────────────────
113
+ MODE=$(echo "$TASK" | jq -r '.mode // ""')
114
+ if [[ "$MODE" == "swarm" ]]; then
115
+ SUB_IDS=$(jq -r --arg pid "$TASK_ID" '.tasks[] | select(.parent_id == $pid) | .id' "$REGISTRY_FILE" 2>/dev/null || true)
116
+ if [[ -n "$SUB_IDS" ]]; then
117
+ while IFS= read -r sub_id; do
118
+ SUB_TMUX=$(registry_get "$sub_id" | jq -r '.tmuxSession')
119
+ if [[ -n "$SUB_TMUX" ]]; then
120
+ tmux kill-session -t "$SUB_TMUX" 2>/dev/null || true
121
+ fi
122
+ registry_update "$sub_id" "status" '"stopped"'
123
+ registry_update "$sub_id" "completedAt" "$NOW"
124
+ if $CLEAN; then
125
+ SUB_WT=$(registry_get "$sub_id" | jq -r '.worktree')
126
+ SUB_REPO=$(registry_get "$sub_id" | jq -r '.repo')
127
+ if [[ -n "$SUB_WT" ]] && [[ -d "$SUB_WT" ]]; then
128
+ git -C "$SUB_REPO" worktree remove "$SUB_WT" --force 2>/dev/null || rm -rf "$SUB_WT"
129
+ fi
130
+ fi
131
+ log_info "Stopped sub-agent: $sub_id"
132
+ done <<< "$SUB_IDS"
133
+ fi
134
+ fi
135
+
136
+ echo "Stopped: $TASK_REF ($DESCRIPTION)"
package/bin/summary.sh ADDED
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env bash
2
+ # summary.sh — AI-generated summary of what an agent did
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
+ source "${SCRIPT_DIR}/../lib/common.sh"
7
+
8
+ usage() {
9
+ cat <<EOF
10
+ Usage: clawforge summary <id> [options]
11
+
12
+ Generate an AI summary of what an agent did based on git diff and logs.
13
+
14
+ Arguments:
15
+ <id> Task ID or short ID
16
+
17
+ Options:
18
+ --model <model> Model to use for summary (default: from config)
19
+ --format <fmt> Output format: text, markdown, json (default: markdown)
20
+ --include-diff Include full diff in output (default: summary only)
21
+ --save <path> Save summary to file
22
+ --help Show this help
23
+
24
+ Examples:
25
+ clawforge summary 1
26
+ clawforge summary 1 --format json
27
+ clawforge summary sprint-jwt --save /tmp/summary.md
28
+ EOF
29
+ }
30
+
31
+ TASK_REF="" MODEL="" FORMAT="markdown" INCLUDE_DIFF=false SAVE_PATH=""
32
+
33
+ while [[ $# -gt 0 ]]; do
34
+ case "$1" in
35
+ --model) MODEL="$2"; shift 2 ;;
36
+ --format) FORMAT="$2"; shift 2 ;;
37
+ --include-diff) INCLUDE_DIFF=true; shift ;;
38
+ --save) SAVE_PATH="$2"; shift 2 ;;
39
+ --help|-h) usage; exit 0 ;;
40
+ --*) log_error "Unknown option: $1"; usage; exit 1 ;;
41
+ *) TASK_REF="$1"; shift ;;
42
+ esac
43
+ done
44
+
45
+ [[ -z "$TASK_REF" ]] && { log_error "Task ID required"; usage; exit 1; }
46
+
47
+ _ensure_registry
48
+
49
+ # Resolve task
50
+ TASK_DATA=""
51
+ if [[ "$TASK_REF" =~ ^[0-9]+$ ]]; then
52
+ TASK_DATA=$(jq -r --argjson sid "$TASK_REF" '.tasks[] | select(.short_id == $sid)' "$REGISTRY_FILE" 2>/dev/null || true)
53
+ fi
54
+ if [[ -z "$TASK_DATA" ]]; then
55
+ TASK_DATA=$(registry_get "$TASK_REF" 2>/dev/null || true)
56
+ fi
57
+ if [[ -z "$TASK_DATA" ]]; then
58
+ log_error "Task '$TASK_REF' not found"
59
+ exit 1
60
+ fi
61
+
62
+ TASK_ID=$(echo "$TASK_DATA" | jq -r '.id')
63
+ DESC=$(echo "$TASK_DATA" | jq -r '.description // "—"')
64
+ WORKTREE=$(echo "$TASK_DATA" | jq -r '.worktree // empty')
65
+ BRANCH=$(echo "$TASK_DATA" | jq -r '.branch // empty')
66
+ REPO=$(echo "$TASK_DATA" | jq -r '.repo // empty')
67
+ MODE=$(echo "$TASK_DATA" | jq -r '.mode // "sprint"')
68
+ STATUS=$(echo "$TASK_DATA" | jq -r '.status // "unknown"')
69
+ AGENT=$(echo "$TASK_DATA" | jq -r '.agent // "claude"')
70
+ SHORT_ID=$(echo "$TASK_DATA" | jq -r '.short_id // 0')
71
+ TMUX_SESSION=$(echo "$TASK_DATA" | jq -r '.tmuxSession // empty')
72
+
73
+ # Resolve model
74
+ if [[ -z "$MODEL" ]]; then
75
+ MODEL=$(config_get default_model_claude "claude-sonnet-4-5")
76
+ fi
77
+
78
+ # Gather context: git diff
79
+ GIT_DIR=""
80
+ if [[ -n "$WORKTREE" && -d "$WORKTREE" ]]; then
81
+ GIT_DIR="$WORKTREE"
82
+ elif [[ -n "$REPO" && -d "$REPO" ]]; then
83
+ GIT_DIR="$REPO"
84
+ fi
85
+
86
+ DIFF_TEXT=""
87
+ DIFF_STAT=""
88
+ if [[ -n "$GIT_DIR" ]]; then
89
+ DEFAULT_BRANCH=$(git -C "$GIT_DIR" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main")
90
+ DIFF_STAT=$(git -C "$GIT_DIR" diff --stat "${DEFAULT_BRANCH}...HEAD" 2>/dev/null || git -C "$GIT_DIR" diff --stat 2>/dev/null || true)
91
+ DIFF_TEXT=$(git -C "$GIT_DIR" diff "${DEFAULT_BRANCH}...HEAD" 2>/dev/null | head -300 || git -C "$GIT_DIR" diff 2>/dev/null | head -300 || true)
92
+ fi
93
+
94
+ # Gather context: recent tmux output
95
+ TMUX_OUTPUT=""
96
+ if [[ -n "$TMUX_SESSION" ]] && tmux has-session -t "$TMUX_SESSION" 2>/dev/null; then
97
+ TMUX_OUTPUT=$(tmux capture-pane -t "$TMUX_SESSION" -p -S -30 2>/dev/null || true)
98
+ fi
99
+
100
+ # Build summary prompt
101
+ SUMMARY_PROMPT="Summarize what this coding agent accomplished. Be concise and specific.
102
+
103
+ Task: ${DESC}
104
+ Mode: ${MODE}
105
+ Status: ${STATUS}
106
+ Agent: ${AGENT}
107
+ Branch: ${BRANCH}
108
+
109
+ ${DIFF_STAT:+Files changed:
110
+ ${DIFF_STAT}
111
+ }
112
+ ${DIFF_TEXT:+Diff (first 300 lines):
113
+ \`\`\`diff
114
+ ${DIFF_TEXT}
115
+ \`\`\`
116
+ }
117
+ ${TMUX_OUTPUT:+Recent agent output:
118
+ \`\`\`
119
+ ${TMUX_OUTPUT}
120
+ \`\`\`
121
+ }
122
+
123
+ Provide:
124
+ 1. One-line summary (what was done)
125
+ 2. Key changes (bullet points)
126
+ 3. Status assessment (complete/partial/blocked)
127
+ 4. Any notable decisions or trade-offs"
128
+
129
+ if [[ "$FORMAT" == "json" ]]; then
130
+ SUMMARY_PROMPT+="
131
+
132
+ Output as JSON: {\"oneLiner\": \"...\", \"changes\": [\"...\"], \"status\": \"...\", \"notes\": \"...\"}"
133
+ fi
134
+
135
+ # Generate summary
136
+ if ! command -v claude &>/dev/null; then
137
+ log_error "claude CLI not found — needed for summary generation"
138
+ exit 1
139
+ fi
140
+
141
+ log_info "Generating summary for #${SHORT_ID} with $MODEL..."
142
+
143
+ SUMMARY=$(claude --model "$MODEL" -p "$SUMMARY_PROMPT" 2>/dev/null || true)
144
+
145
+ if [[ -z "$SUMMARY" ]]; then
146
+ log_error "Summary generation failed"
147
+ exit 1
148
+ fi
149
+
150
+ # Output
151
+ OUTPUT=""
152
+ if [[ "$FORMAT" == "markdown" ]]; then
153
+ OUTPUT="# Summary: #${SHORT_ID} — ${DESC}
154
+ **Mode:** ${MODE} | **Status:** ${STATUS} | **Agent:** ${AGENT}
155
+ **Branch:** ${BRANCH}
156
+
157
+ ${SUMMARY}
158
+
159
+ ---
160
+ *Generated by ClawForge summary (${MODEL})*"
161
+ elif [[ "$FORMAT" == "json" ]]; then
162
+ OUTPUT="$SUMMARY"
163
+ else
164
+ OUTPUT="Summary #${SHORT_ID}: ${DESC}
165
+ ${SUMMARY}"
166
+ fi
167
+
168
+ if $INCLUDE_DIFF && [[ -n "$DIFF_STAT" ]]; then
169
+ OUTPUT+="
170
+
171
+ ## Diff Stats
172
+ \`\`\`
173
+ ${DIFF_STAT}
174
+ \`\`\`"
175
+ fi
176
+
177
+ if [[ -n "$SAVE_PATH" ]]; then
178
+ echo "$OUTPUT" > "$SAVE_PATH"
179
+ echo "Summary saved to $SAVE_PATH"
180
+ else
181
+ echo "$OUTPUT"
182
+ fi