@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
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env bash
2
+ # templates.sh — Task templates: pre-configured workflow settings
3
+ # Usage: clawforge templates [list|new|show] [name]
4
+ set -euo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+ source "${SCRIPT_DIR}/../lib/common.sh"
8
+
9
+ BUILTIN_DIR="${CLAWFORGE_DIR}/lib/templates"
10
+ USER_DIR="${HOME}/.clawforge/templates"
11
+
12
+ # ── Help ───────────────────────────────────────────────────────────────
13
+ usage() {
14
+ cat <<EOF
15
+ Usage: clawforge templates [command] [name]
16
+
17
+ Manage task templates for pre-configured workflows.
18
+
19
+ Commands:
20
+ clawforge templates List all available templates
21
+ clawforge templates show <name> Show template details
22
+ clawforge templates new <name> Create a new custom template
23
+
24
+ Template Usage:
25
+ clawforge sprint --template refactor "Refactor auth module"
26
+ clawforge swarm --template migration "Migrate to TypeScript"
27
+
28
+ Flags:
29
+ --json Output as JSON
30
+ --help Show this help
31
+ EOF
32
+ }
33
+
34
+ # ── Parse args ─────────────────────────────────────────────────────────
35
+ COMMAND="" NAME="" JSON_OUTPUT=false
36
+ POSITIONAL=()
37
+
38
+ while [[ $# -gt 0 ]]; do
39
+ case "$1" in
40
+ --json) JSON_OUTPUT=true; shift ;;
41
+ --help|-h) usage; exit 0 ;;
42
+ --*) log_error "Unknown option: $1"; usage; exit 1 ;;
43
+ *) POSITIONAL+=("$1"); shift ;;
44
+ esac
45
+ done
46
+
47
+ if [[ ${#POSITIONAL[@]} -gt 0 ]]; then
48
+ COMMAND="${POSITIONAL[0]}"
49
+ fi
50
+ if [[ ${#POSITIONAL[@]} -gt 1 ]]; then
51
+ NAME="${POSITIONAL[1]}"
52
+ fi
53
+
54
+ # ── List templates ─────────────────────────────────────────────────────
55
+ _list_templates() {
56
+ if $JSON_OUTPUT; then
57
+ local result="[]"
58
+ fi
59
+
60
+ if ! $JSON_OUTPUT; then
61
+ echo "=== Available Templates ==="
62
+ echo ""
63
+ echo " Built-in:"
64
+ fi
65
+
66
+ # Built-in templates
67
+ if [[ -d "$BUILTIN_DIR" ]]; then
68
+ for f in "$BUILTIN_DIR"/*.json; do
69
+ [[ -f "$f" ]] || continue
70
+ local name desc mode
71
+ name=$(basename "$f" .json)
72
+ desc=$(jq -r '.description // "No description"' "$f" 2>/dev/null)
73
+ mode=$(jq -r '.mode // "—"' "$f" 2>/dev/null)
74
+ if $JSON_OUTPUT; then
75
+ local entry
76
+ entry=$(jq -c --arg name "$name" --arg source "builtin" '. + {name: $name, source: $source}' "$f" 2>/dev/null)
77
+ result=$(echo "$result" | jq --argjson e "$entry" '. += [$e]')
78
+ else
79
+ printf " %-20s %-10s %s\n" "$name" "[$mode]" "$desc"
80
+ fi
81
+ done
82
+ fi
83
+
84
+ # User templates
85
+ if [[ -d "$USER_DIR" ]]; then
86
+ if ! $JSON_OUTPUT; then
87
+ echo ""
88
+ echo " Custom:"
89
+ fi
90
+ local found=false
91
+ for f in "$USER_DIR"/*.json; do
92
+ [[ -f "$f" ]] || continue
93
+ found=true
94
+ local name desc mode
95
+ name=$(basename "$f" .json)
96
+ desc=$(jq -r '.description // "No description"' "$f" 2>/dev/null)
97
+ mode=$(jq -r '.mode // "—"' "$f" 2>/dev/null)
98
+ if $JSON_OUTPUT; then
99
+ local entry
100
+ entry=$(jq -c --arg name "$name" --arg source "custom" '. + {name: $name, source: $source}' "$f" 2>/dev/null)
101
+ result=$(echo "$result" | jq --argjson e "$entry" '. += [$e]')
102
+ else
103
+ printf " %-20s %-10s %s\n" "$name" "[$mode]" "$desc"
104
+ fi
105
+ done
106
+ if ! $found && ! $JSON_OUTPUT; then
107
+ echo " (none — create with: clawforge templates new <name>)"
108
+ fi
109
+ elif ! $JSON_OUTPUT; then
110
+ echo ""
111
+ echo " Custom:"
112
+ echo " (none — create with: clawforge templates new <name>)"
113
+ fi
114
+
115
+ if $JSON_OUTPUT; then
116
+ echo "$result" | jq '.'
117
+ fi
118
+ }
119
+
120
+ # ── Show template ──────────────────────────────────────────────────────
121
+ _show_template() {
122
+ local name="$1"
123
+ local file=""
124
+
125
+ # Check user templates first, then builtin
126
+ if [[ -f "${USER_DIR}/${name}.json" ]]; then
127
+ file="${USER_DIR}/${name}.json"
128
+ elif [[ -f "${BUILTIN_DIR}/${name}.json" ]]; then
129
+ file="${BUILTIN_DIR}/${name}.json"
130
+ else
131
+ log_error "Template '$name' not found"
132
+ echo "Available templates:"
133
+ _list_templates
134
+ exit 1
135
+ fi
136
+
137
+ if $JSON_OUTPUT; then
138
+ jq --arg name "$name" '. + {name: $name}' "$file"
139
+ else
140
+ echo "=== Template: $name ==="
141
+ echo ""
142
+ jq -r 'to_entries[] | " \(.key): \(.value)"' "$file" 2>/dev/null
143
+ fi
144
+ }
145
+
146
+ # ── Create new template ───────────────────────────────────────────────
147
+ _new_template() {
148
+ local name="$1"
149
+ mkdir -p "$USER_DIR"
150
+
151
+ local target="${USER_DIR}/${name}.json"
152
+ if [[ -f "$target" ]]; then
153
+ log_error "Template '$name' already exists at $target"
154
+ exit 1
155
+ fi
156
+
157
+ echo "Creating template: $name"
158
+ echo ""
159
+
160
+ # Interactive prompts (or defaults if non-interactive)
161
+ local mode="sprint" max_agents=3 auto_merge=false ci_loop=false description=""
162
+
163
+ if [[ -t 0 ]]; then
164
+ printf " Mode [sprint/swarm/review] (sprint): "
165
+ read -r mode_input
166
+ [[ -n "$mode_input" ]] && mode="$mode_input"
167
+
168
+ if [[ "$mode" == "swarm" ]]; then
169
+ printf " Max agents (3): "
170
+ read -r agents_input
171
+ [[ -n "$agents_input" ]] && max_agents="$agents_input"
172
+ fi
173
+
174
+ printf " Auto-merge [true/false] (false): "
175
+ read -r merge_input
176
+ [[ "$merge_input" == "true" ]] && auto_merge=true
177
+
178
+ printf " CI loop [true/false] (false): "
179
+ read -r ci_input
180
+ [[ "$ci_input" == "true" ]] && ci_loop=true
181
+
182
+ printf " Description: "
183
+ read -r description
184
+ fi
185
+
186
+ # Build template JSON
187
+ local template
188
+ template=$(jq -cn \
189
+ --arg mode "$mode" \
190
+ --argjson maxAgents "$max_agents" \
191
+ --argjson autoMerge "$auto_merge" \
192
+ --argjson ciLoop "$ci_loop" \
193
+ --arg description "${description:-Custom template: $name}" \
194
+ '{
195
+ mode: $mode,
196
+ maxAgents: $maxAgents,
197
+ autoMerge: $autoMerge,
198
+ ciLoop: $ciLoop,
199
+ description: $description
200
+ }')
201
+
202
+ echo "$template" | jq '.' > "$target"
203
+ echo ""
204
+ echo "Template saved: $target"
205
+ echo "$template" | jq '.'
206
+ }
207
+
208
+ # ── Load template (called from sprint/swarm) ──────────────────────────
209
+ # Usage: source templates.sh && load_template <name>
210
+ # Returns JSON template to stdout
211
+ load_template() {
212
+ local name="$1"
213
+ local file=""
214
+
215
+ if [[ -f "${USER_DIR}/${name}.json" ]]; then
216
+ file="${USER_DIR}/${name}.json"
217
+ elif [[ -f "${BUILTIN_DIR}/${name}.json" ]]; then
218
+ file="${BUILTIN_DIR}/${name}.json"
219
+ else
220
+ log_error "Template '$name' not found"
221
+ return 1
222
+ fi
223
+
224
+ cat "$file"
225
+ }
226
+
227
+ # ── Route ──────────────────────────────────────────────────────────────
228
+ case "${COMMAND:-list}" in
229
+ list|"")
230
+ _list_templates
231
+ ;;
232
+ show)
233
+ [[ -z "$NAME" ]] && { log_error "Template name required"; usage; exit 1; }
234
+ _show_template "$NAME"
235
+ ;;
236
+ new)
237
+ [[ -z "$NAME" ]] && { log_error "Template name required"; usage; exit 1; }
238
+ _new_template "$NAME"
239
+ ;;
240
+ *)
241
+ # Treat as template name to show
242
+ _show_template "$COMMAND"
243
+ ;;
244
+ esac
package/lib/common.sh ADDED
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env bash
2
+ # common.sh — Shared functions for clawforge
3
+ # Registry helpers, logging, config
4
+
5
+ set -euo pipefail
6
+
7
+ # ── Paths ──────────────────────────────────────────────────────────────
8
+ CLAWFORGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
9
+ REGISTRY_FILE="${CLAWFORGE_DIR}/registry/active-tasks.json"
10
+ CONFIG_FILE="${CLAWFORGE_DIR}/config/defaults.json"
11
+
12
+ # ── Logging ────────────────────────────────────────────────────────────
13
+ log_info() { echo "[INFO] $(date +%H:%M:%S) $*" >&2; }
14
+ log_warn() { echo "[WARN] $(date +%H:%M:%S) $*" >&2; }
15
+ log_error() { echo "[ERROR] $(date +%H:%M:%S) $*" >&2; }
16
+ log_debug() { [[ "${CLAWFORGE_DEBUG:-0}" == "1" ]] && echo "[DEBUG] $(date +%H:%M:%S) $*" >&2 || true; }
17
+
18
+ # ── Config ─────────────────────────────────────────────────────────────
19
+ USER_CONFIG_FILE="${HOME}/.clawforge/config.json"
20
+
21
+ config_get() {
22
+ local key="$1"
23
+ local default="${2:-}"
24
+ # User config takes priority
25
+ if [[ -f "$USER_CONFIG_FILE" ]]; then
26
+ local val
27
+ val=$(jq -r ".$key // empty" "$USER_CONFIG_FILE" 2>/dev/null)
28
+ if [[ -n "$val" ]]; then
29
+ echo "$val"
30
+ return
31
+ fi
32
+ fi
33
+ # Fall back to project defaults
34
+ if [[ -f "$CONFIG_FILE" ]]; then
35
+ local val
36
+ val=$(jq -r ".$key // empty" "$CONFIG_FILE" 2>/dev/null)
37
+ if [[ -n "$val" ]]; then
38
+ echo "$val"
39
+ return
40
+ fi
41
+ fi
42
+ echo "$default"
43
+ }
44
+
45
+ config_set() {
46
+ local key="$1" value="$2"
47
+ mkdir -p "$(dirname "$USER_CONFIG_FILE")"
48
+ if [[ ! -f "$USER_CONFIG_FILE" ]]; then
49
+ echo '{}' > "$USER_CONFIG_FILE"
50
+ fi
51
+ local tmp
52
+ tmp=$(mktemp)
53
+ jq --arg k "$key" --arg v "$value" '.[$k] = $v' "$USER_CONFIG_FILE" > "$tmp" && mv "$tmp" "$USER_CONFIG_FILE"
54
+ }
55
+
56
+ config_list() {
57
+ echo "── User config (~/.clawforge/config.json) ──"
58
+ if [[ -f "$USER_CONFIG_FILE" ]]; then
59
+ jq '.' "$USER_CONFIG_FILE"
60
+ else
61
+ echo "(not created yet)"
62
+ fi
63
+ echo ""
64
+ echo "── Project defaults (config/defaults.json) ──"
65
+ if [[ -f "$CONFIG_FILE" ]]; then
66
+ jq '.' "$CONFIG_FILE"
67
+ else
68
+ echo "(not found)"
69
+ fi
70
+ }
71
+
72
+ # ── Registry helpers ───────────────────────────────────────────────────
73
+ _ensure_registry() {
74
+ mkdir -p "$(dirname "$REGISTRY_FILE")"
75
+ if [[ ! -f "$REGISTRY_FILE" ]]; then
76
+ echo '{"tasks":[]}' > "$REGISTRY_FILE"
77
+ fi
78
+ }
79
+
80
+
81
+ # ── Registry file locking ──────────────────────────────────────────────
82
+ REGISTRY_LOCK="${CLAWFORGE_DIR}/registry/.lock"
83
+
84
+ _with_lock() {
85
+ mkdir -p "$(dirname "$REGISTRY_LOCK")"
86
+ local fd=200
87
+ eval "exec ${fd}>"$REGISTRY_LOCK""
88
+ if ! flock -w 5 $fd 2>/dev/null; then
89
+ log_error "Registry lock timeout — another clawforge process may be writing"
90
+ return 1
91
+ fi
92
+ }
93
+
94
+ _unlock() {
95
+ local fd=200
96
+ flock -u $fd 2>/dev/null || true
97
+ }
98
+
99
+ registry_add() {
100
+ local task_json="$1"
101
+ _ensure_registry
102
+ local tmp
103
+ tmp=$(mktemp)
104
+ jq --argjson task "$task_json" '.tasks += [$task]' "$REGISTRY_FILE" > "$tmp" && mv "$tmp" "$REGISTRY_FILE"
105
+ log_info "Registry: added task $(echo "$task_json" | jq -r '.id')"
106
+ }
107
+
108
+ registry_update() {
109
+ local id="$1" field="$2" value="$3"
110
+ _ensure_registry
111
+ local tmp
112
+ tmp=$(mktemp)
113
+ # Try to parse value as JSON; if it fails, treat as string
114
+ if echo "$value" | jq . >/dev/null 2>&1; then
115
+ jq --arg id "$id" --arg field "$field" --argjson val "$value" \
116
+ '(.tasks[] | select(.id == $id))[$field] = $val' "$REGISTRY_FILE" > "$tmp" && mv "$tmp" "$REGISTRY_FILE"
117
+ else
118
+ jq --arg id "$id" --arg field "$field" --arg val "$value" \
119
+ '(.tasks[] | select(.id == $id))[$field] = $val' "$REGISTRY_FILE" > "$tmp" && mv "$tmp" "$REGISTRY_FILE"
120
+ fi
121
+ log_debug "Registry: updated $id.$field"
122
+ }
123
+
124
+ registry_get() {
125
+ local id="$1"
126
+ _ensure_registry
127
+ jq --arg id "$id" '.tasks[] | select(.id == $id)' "$REGISTRY_FILE"
128
+ }
129
+
130
+ registry_list() {
131
+ _ensure_registry
132
+ local status_filter=""
133
+ while [[ $# -gt 0 ]]; do
134
+ case "$1" in
135
+ --status) status_filter="$2"; shift 2 ;;
136
+ *) shift ;;
137
+ esac
138
+ done
139
+ if [[ -n "$status_filter" ]]; then
140
+ jq --arg s "$status_filter" '[.tasks[] | select(.status == $s)]' "$REGISTRY_FILE"
141
+ else
142
+ jq '.tasks' "$REGISTRY_FILE"
143
+ fi
144
+ }
145
+
146
+ registry_remove() {
147
+ local id="$1"
148
+ _ensure_registry
149
+ local tmp
150
+ tmp=$(mktemp)
151
+ jq --arg id "$id" '.tasks = [.tasks[] | select(.id != $id)]' "$REGISTRY_FILE" > "$tmp" && mv "$tmp" "$REGISTRY_FILE"
152
+ log_info "Registry: removed task $id"
153
+ }
154
+
155
+ # ── Agent detection ────────────────────────────────────────────────────
156
+ detect_agent() {
157
+ local preferred="${1:-}"
158
+ if [[ -n "$preferred" ]]; then
159
+ if command -v "$preferred" &>/dev/null; then
160
+ echo "$preferred"
161
+ return 0
162
+ else
163
+ log_error "Requested agent '$preferred' not found"
164
+ return 1
165
+ fi
166
+ fi
167
+ if command -v claude &>/dev/null; then
168
+ echo "claude"
169
+ elif command -v codex &>/dev/null; then
170
+ echo "codex"
171
+ else
172
+ log_error "No coding agent found (need claude or codex)"
173
+ return 1
174
+ fi
175
+ }
176
+
177
+ # ── Short ID management ───────────────────────────────────────────────
178
+ # Sequential short IDs (#1, #2, #3...) mapped in registry
179
+ _next_short_id() {
180
+ _ensure_registry
181
+ local max_id
182
+ max_id=$(jq '[.tasks[].short_id // 0] | max // 0' "$REGISTRY_FILE" 2>/dev/null || echo 0)
183
+ echo $((max_id + 1))
184
+ }
185
+
186
+ resolve_task_id() {
187
+ # Accept short ID (#1, 1), sub-agent ID (3.2), or full UUID/slug
188
+ local input="$1"
189
+ _ensure_registry
190
+
191
+ # Strip leading # if present
192
+ input="${input#\#}"
193
+
194
+ # Check if it's a sub-agent reference (e.g., 3.2)
195
+ if [[ "$input" =~ ^([0-9]+)\.([0-9]+)$ ]]; then
196
+ local parent_short="${BASH_REMATCH[1]}"
197
+ local sub_index="${BASH_REMATCH[2]}"
198
+ local parent_id
199
+ parent_id=$(jq -r --argjson sid "$parent_short" '.tasks[] | select(.short_id == $sid) | .id' "$REGISTRY_FILE" 2>/dev/null || true)
200
+ if [[ -n "$parent_id" ]]; then
201
+ # Find sub-agent by parent_id and sub_index
202
+ jq -r --arg pid "$parent_id" --argjson idx "$sub_index" \
203
+ '.tasks[] | select(.parent_id == $pid and .sub_index == $idx) | .id' "$REGISTRY_FILE" 2>/dev/null || true
204
+ return
205
+ fi
206
+ fi
207
+
208
+ # Check if it's a numeric short ID
209
+ if [[ "$input" =~ ^[0-9]+$ ]]; then
210
+ local resolved
211
+ resolved=$(jq -r --argjson sid "$input" '.tasks[] | select(.short_id == $sid) | .id' "$REGISTRY_FILE" 2>/dev/null || true)
212
+ if [[ -n "$resolved" ]]; then
213
+ echo "$resolved"
214
+ return
215
+ fi
216
+ fi
217
+
218
+ # Fall through: treat as full ID/slug
219
+ echo "$input"
220
+ }
221
+
222
+ # ── Auto-repo detection ──────────────────────────────────────────────
223
+ detect_repo() {
224
+ # Walk up from cwd (or given path) to find .git
225
+ local start="${1:-$(pwd)}"
226
+ local dir="$start"
227
+ while [[ "$dir" != "/" ]]; do
228
+ if [[ -d "$dir/.git" ]] || [[ -f "$dir/.git" ]]; then
229
+ echo "$dir"
230
+ return 0
231
+ fi
232
+ dir="$(dirname "$dir")"
233
+ done
234
+ log_error "No git repository found from $start"
235
+ return 1
236
+ }
237
+
238
+ # ── Auto-branch naming ───────────────────────────────────────────────
239
+ slugify_task() {
240
+ # Convert task description to a URL-safe branch slug
241
+ local task="$1"
242
+ local max_len="${2:-40}"
243
+ echo "$task" \
244
+ | tr '[:upper:]' '[:lower:]' \
245
+ | sed 's/[^a-z0-9 ]//g' \
246
+ | sed 's/^ *//;s/ *$//' \
247
+ | sed 's/ */ /g' \
248
+ | sed 's/ /-/g' \
249
+ | cut -c1-"$max_len" \
250
+ | sed 's/-$//'
251
+ }
252
+
253
+ auto_branch_name() {
254
+ # Generate branch name with mode prefix and collision detection
255
+ local mode="$1" # sprint, quick, swarm
256
+ local task="$2"
257
+ local repo="${3:-}"
258
+
259
+ local slug
260
+ slug=$(slugify_task "$task")
261
+ local prefix="${mode}/"
262
+ local candidate="${prefix}${slug}"
263
+
264
+ # Collision detection if repo is provided
265
+ if [[ -n "$repo" ]] && [[ -d "$repo/.git" ]]; then
266
+ local attempt=1
267
+ local base="$candidate"
268
+ while git -C "$repo" show-ref --verify --quiet "refs/heads/$candidate" 2>/dev/null; do
269
+ attempt=$((attempt + 1))
270
+ candidate="${base}-${attempt}"
271
+ done
272
+ fi
273
+
274
+ echo "$candidate"
275
+ }
276
+
277
+ # ── Utilities ──────────────────────────────────────────────────────────
278
+ sanitize_branch() {
279
+ echo "$1" | sed 's|/|-|g' | sed 's|[^a-zA-Z0-9_-]|-|g'
280
+ }
281
+
282
+ epoch_ms() {
283
+ python3 -c 'import time; print(int(time.time()*1000))' 2>/dev/null || echo "$(date +%s)000"
284
+ }
285
+
286
+ # ── Disk space check ──────────────────────────────────────────────────
287
+ disk_check() {
288
+ local dir="${1:-.}"
289
+ local warn_gb="${2:-5}"
290
+ local error_gb="${3:-1}"
291
+ local avail_kb
292
+ avail_kb=$(df -k "$dir" 2>/dev/null | awk 'NR==2{print $4}')
293
+ [[ -z "$avail_kb" ]] && return 0
294
+ local avail_gb=$((avail_kb / 1048576))
295
+ if [[ $avail_gb -lt $error_gb ]]; then
296
+ log_error "Disk space critically low: ${avail_gb}GB free (need ${error_gb}GB minimum)"
297
+ return 1
298
+ elif [[ $avail_gb -lt $warn_gb ]]; then
299
+ log_warn "Disk space low: ${avail_gb}GB free (warning threshold: ${warn_gb}GB)"
300
+ fi
301
+ return 0
302
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "mode": "sprint",
3
+ "quick": true,
4
+ "autoMerge": true,
5
+ "description": "Quick bugfix: sprint with auto-merge, skip review"
6
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "mode": "swarm",
3
+ "maxAgents": 4,
4
+ "autoMerge": true,
5
+ "ciLoop": true,
6
+ "description": "Large-scale migration: parallel agents with auto-merge and CI feedback"
7
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "mode": "sprint",
3
+ "autoMerge": false,
4
+ "ciLoop": true,
5
+ "description": "Code refactoring: single agent with CI loop, manual merge for safety"
6
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "mode": "review",
3
+ "depth": "deep",
4
+ "description": "Security audit: deep review mode for thorough vulnerability analysis"
5
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "mode": "swarm",
3
+ "maxAgents": 3,
4
+ "autoMerge": true,
5
+ "description": "Boost test coverage: parallel agents adding tests across modules"
6
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@cyperx/clawforge",
3
+ "version": "1.2.0",
4
+ "description": "Multi-mode coding workflow CLI for orchestrating AI coding agents",
5
+ "bin": {
6
+ "@cyperx/clawforge": "./bin/clawforge"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "echo 'ClawForge installed. Run: clawforge help'"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/cyperx84/clawforge.git"
14
+ },
15
+ "keywords": ["cli", "coding", "agents", "orchestration", "tmux", "worktree"],
16
+ "author": "cyperx84",
17
+ "license": "MIT",
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "os": ["darwin", "linux"],
22
+ "files": [
23
+ "bin/",
24
+ "lib/",
25
+ "tui/",
26
+ "registry/",
27
+ "VERSION",
28
+ "README.md",
29
+ "LICENSE"
30
+ ]
31
+ }
File without changes
File without changes