chiefwiggum 1.2.9 → 1.3.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.
- package/dist/cli.cjs +904 -0
- package/package.json +23 -2
- package/chiefwiggum +0 -903
- /package/templates/{CLAUDE.md → CLAUDE-template.md} +0 -0
- /package/templates/{TODO.md → TODO-template.md} +0 -0
- /package/templates/{specs/prd.md → prd-template.md} +0 -0
- /package/templates/{specs/technical.md → technical-template.md} +0 -0
package/chiefwiggum
DELETED
|
@@ -1,903 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
###########################################
|
|
5
|
-
# ░█▀▀░█░█░▀█▀░█▀▀░█▀▀░░░█░█░▀█▀░█▀▀░█▀▀░█░█░█▄█
|
|
6
|
-
# ░█░░░█▀█░░█░░█▀▀░█▀▀░░░█▄█░░█░░█░█░█░█░█░█░█░█
|
|
7
|
-
# ░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀░░░░░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀
|
|
8
|
-
#
|
|
9
|
-
# Autonomous coding agent CLI
|
|
10
|
-
###########################################
|
|
11
|
-
|
|
12
|
-
VERSION="$(grep '"version"' "$(dirname "$(realpath "$0")")/package.json" 2>/dev/null | sed 's/.*"version": "\([^"]*\)".*/\1/' || echo "dev")"
|
|
13
|
-
|
|
14
|
-
# =========================================
|
|
15
|
-
# COLORS
|
|
16
|
-
# =========================================
|
|
17
|
-
GREEN='\033[0;32m'
|
|
18
|
-
YELLOW='\033[1;33m'
|
|
19
|
-
RED='\033[0;31m'
|
|
20
|
-
BLUE='\033[0;34m'
|
|
21
|
-
CYAN='\033[0;36m'
|
|
22
|
-
BOLD='\033[1m'
|
|
23
|
-
NC='\033[0m'
|
|
24
|
-
|
|
25
|
-
# =========================================
|
|
26
|
-
# CONFIGURATION
|
|
27
|
-
# =========================================
|
|
28
|
-
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
|
29
|
-
CHIEFWIGGUM_HOME="${CHIEFWIGGUM_HOME:-$HOME/.chiefwiggum}"
|
|
30
|
-
TEMPLATES_DIR="${CHIEFWIGGUM_HOME}/templates"
|
|
31
|
-
BUNDLED_TEMPLATES="${SCRIPT_DIR}/templates"
|
|
32
|
-
TODO_FILE="${TODO_FILE:-TODO.md}"
|
|
33
|
-
COOLDOWN_SECONDS="${COOLDOWN_SECONDS:-5}"
|
|
34
|
-
|
|
35
|
-
# Guardrails
|
|
36
|
-
ITERATION_TIMEOUT_MINUTES="${ITERATION_TIMEOUT_MINUTES:-60}"
|
|
37
|
-
MAX_CONSECUTIVE_FAILURES="${MAX_CONSECUTIVE_FAILURES:-3}"
|
|
38
|
-
MAX_NO_COMMIT_CYCLES="${MAX_NO_COMMIT_CYCLES:-5}"
|
|
39
|
-
MAX_CLAUDE_ATTEMPTS="${MAX_CLAUDE_ATTEMPTS:-3}"
|
|
40
|
-
|
|
41
|
-
# Claude Code reliability hardening
|
|
42
|
-
export CLAUDE_CODE_EXIT_AFTER_STOP_DELAY="${CLAUDE_CODE_EXIT_AFTER_STOP_DELAY:-30000}"
|
|
43
|
-
export NODE_OPTIONS="--unhandled-rejections=strict${NODE_OPTIONS:+ $NODE_OPTIONS}"
|
|
44
|
-
|
|
45
|
-
# =========================================
|
|
46
|
-
# BANNER
|
|
47
|
-
# =========================================
|
|
48
|
-
show_banner() {
|
|
49
|
-
echo ""
|
|
50
|
-
echo -e "${YELLOW}░█▀▀░█░█░▀█▀░█▀▀░█▀▀░░░█░█░▀█▀░█▀▀░█▀▀░█░█░█▄█${NC}"
|
|
51
|
-
echo -e "${YELLOW}░█░░░█▀█░░█░░█▀▀░█▀▀░░░█▄█░░█░░█░█░█░█░█░█░█░█${NC}"
|
|
52
|
-
echo -e "${YELLOW}░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀░░░░░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀${NC}"
|
|
53
|
-
echo -e "${YELLOW} Autonomous Coding Agent v${VERSION}${NC}"
|
|
54
|
-
echo ""
|
|
55
|
-
|
|
56
|
-
# Check for updates (non-blocking, 2s timeout)
|
|
57
|
-
check_for_updates
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
check_for_updates() {
|
|
61
|
-
local latest
|
|
62
|
-
latest=$(timeout 2s npm view chiefwiggum version 2>/dev/null || echo "")
|
|
63
|
-
|
|
64
|
-
if [ -n "$latest" ] && [ "$latest" != "$VERSION" ]; then
|
|
65
|
-
echo -e "${CYAN} ✨ Update available: v${VERSION} → v${latest}${NC}"
|
|
66
|
-
echo -e "${CYAN} Run: npx chiefwiggum@latest${NC}"
|
|
67
|
-
echo ""
|
|
68
|
-
fi
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
# =========================================
|
|
72
|
-
# HELP
|
|
73
|
-
# =========================================
|
|
74
|
-
show_help() {
|
|
75
|
-
show_banner
|
|
76
|
-
echo "Usage: chiefwiggum [command] [options]"
|
|
77
|
-
echo ""
|
|
78
|
-
echo "Commands:"
|
|
79
|
-
echo " new [plan.md] Initialize project (interactive or from plan file)"
|
|
80
|
-
echo " loop Run the build loop on existing TODO.md"
|
|
81
|
-
echo " status Show current project status"
|
|
82
|
-
echo " help Show this help message"
|
|
83
|
-
echo ""
|
|
84
|
-
echo "Examples:"
|
|
85
|
-
echo " chiefwiggum new # Interactive setup"
|
|
86
|
-
echo " chiefwiggum new plans/myplan.md # Setup from plan file"
|
|
87
|
-
echo " chiefwiggum loop # Start build loop"
|
|
88
|
-
echo " chiefwiggum # Same as 'loop'"
|
|
89
|
-
echo ""
|
|
90
|
-
echo "Templates:"
|
|
91
|
-
echo -e " ${CYAN}${TEMPLATES_DIR}${NC}"
|
|
92
|
-
echo " Edit these to customize how specs are generated."
|
|
93
|
-
echo ""
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
# =========================================
|
|
97
|
-
# PROMPT HELPERS
|
|
98
|
-
# =========================================
|
|
99
|
-
ask_choice() {
|
|
100
|
-
local prompt="$1"
|
|
101
|
-
shift
|
|
102
|
-
local options=("$@")
|
|
103
|
-
local selected=0
|
|
104
|
-
local key
|
|
105
|
-
|
|
106
|
-
echo ""
|
|
107
|
-
echo -e "${BOLD}${prompt}${NC}"
|
|
108
|
-
echo ""
|
|
109
|
-
|
|
110
|
-
# Hide cursor
|
|
111
|
-
tput civis 2>/dev/null
|
|
112
|
-
|
|
113
|
-
# Draw menu with current selection highlighted
|
|
114
|
-
draw_menu() {
|
|
115
|
-
for i in "${!options[@]}"; do
|
|
116
|
-
tput el 2>/dev/null # Clear line
|
|
117
|
-
if [ "$i" -eq "$selected" ]; then
|
|
118
|
-
echo -e " ${CYAN}▸${NC} ${BOLD}${options[$i]}${NC}"
|
|
119
|
-
else
|
|
120
|
-
echo -e " ${options[$i]}"
|
|
121
|
-
fi
|
|
122
|
-
done
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
draw_menu
|
|
126
|
-
|
|
127
|
-
while true; do
|
|
128
|
-
read -rsn1 key
|
|
129
|
-
|
|
130
|
-
case "$key" in
|
|
131
|
-
$'\x1b') # Escape sequence (arrow keys)
|
|
132
|
-
read -rsn2 key
|
|
133
|
-
case "$key" in
|
|
134
|
-
'[A'|'OA') # Up arrow
|
|
135
|
-
((selected > 0)) && ((selected--))
|
|
136
|
-
;;
|
|
137
|
-
'[B'|'OB') # Down arrow
|
|
138
|
-
((selected < ${#options[@]} - 1)) && ((selected++))
|
|
139
|
-
;;
|
|
140
|
-
esac
|
|
141
|
-
;;
|
|
142
|
-
'k') # Vim up
|
|
143
|
-
((selected > 0)) && ((selected--))
|
|
144
|
-
;;
|
|
145
|
-
'j') # Vim down
|
|
146
|
-
((selected < ${#options[@]} - 1)) && ((selected++))
|
|
147
|
-
;;
|
|
148
|
-
'') # Enter key
|
|
149
|
-
break
|
|
150
|
-
;;
|
|
151
|
-
esac
|
|
152
|
-
|
|
153
|
-
# Move cursor back up and redraw
|
|
154
|
-
tput cuu ${#options[@]} 2>/dev/null
|
|
155
|
-
draw_menu
|
|
156
|
-
done
|
|
157
|
-
|
|
158
|
-
# Show cursor again
|
|
159
|
-
tput cnorm 2>/dev/null
|
|
160
|
-
echo ""
|
|
161
|
-
|
|
162
|
-
return $selected
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
ask_text() {
|
|
166
|
-
local prompt="$1"
|
|
167
|
-
local default="${2:-}"
|
|
168
|
-
|
|
169
|
-
echo ""
|
|
170
|
-
if [ -n "$default" ]; then
|
|
171
|
-
read -rp "${prompt} [${default}]: " response
|
|
172
|
-
echo "${response:-$default}"
|
|
173
|
-
else
|
|
174
|
-
read -rp "${prompt}: " response
|
|
175
|
-
echo "$response"
|
|
176
|
-
fi
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
ask_confirm() {
|
|
180
|
-
local prompt="$1"
|
|
181
|
-
local default="${2:-y}"
|
|
182
|
-
|
|
183
|
-
if [ "$default" = "y" ]; then
|
|
184
|
-
read -rp "${prompt} [Y/n]: " response
|
|
185
|
-
[[ ! "$response" =~ ^[Nn] ]]
|
|
186
|
-
else
|
|
187
|
-
read -rp "${prompt} [y/N]: " response
|
|
188
|
-
[[ "$response" =~ ^[Yy] ]]
|
|
189
|
-
fi
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
# =========================================
|
|
193
|
-
# STATUS COMMAND
|
|
194
|
-
# =========================================
|
|
195
|
-
cmd_status() {
|
|
196
|
-
show_banner
|
|
197
|
-
|
|
198
|
-
echo -e "${BOLD}Project Status${NC}"
|
|
199
|
-
echo ""
|
|
200
|
-
|
|
201
|
-
# Check for key files
|
|
202
|
-
local has_todo=false
|
|
203
|
-
local has_specs=false
|
|
204
|
-
local has_claude_md=false
|
|
205
|
-
|
|
206
|
-
[ -f "$TODO_FILE" ] && has_todo=true
|
|
207
|
-
[ -d "specs" ] && has_specs=true
|
|
208
|
-
[ -f "CLAUDE.md" ] && has_claude_md=true
|
|
209
|
-
|
|
210
|
-
echo -e " TODO.md: $([ "$has_todo" = true ] && echo -e "${GREEN}✓${NC}" || echo -e "${RED}✗${NC}")"
|
|
211
|
-
echo -e " specs/: $([ "$has_specs" = true ] && echo -e "${GREEN}✓${NC}" || echo -e "${RED}✗${NC}")"
|
|
212
|
-
echo -e " CLAUDE.md: $([ "$has_claude_md" = true ] && echo -e "${GREEN}✓${NC}" || echo -e "${RED}✗${NC}")"
|
|
213
|
-
echo ""
|
|
214
|
-
|
|
215
|
-
if [ "$has_todo" = true ]; then
|
|
216
|
-
local total=$(grep -c "^\s*- \[" "$TODO_FILE" 2>/dev/null || echo "0")
|
|
217
|
-
local done=$(grep -c "^\s*- \[x\]" "$TODO_FILE" 2>/dev/null || echo "0")
|
|
218
|
-
local remaining=$((total - done))
|
|
219
|
-
|
|
220
|
-
echo -e "${BOLD}Tasks${NC}"
|
|
221
|
-
echo -e " Total: ${total}"
|
|
222
|
-
echo -e " Completed: ${GREEN}${done}${NC}"
|
|
223
|
-
echo -e " Remaining: ${YELLOW}${remaining}${NC}"
|
|
224
|
-
echo ""
|
|
225
|
-
fi
|
|
226
|
-
|
|
227
|
-
# Git status
|
|
228
|
-
if git rev-parse --git-dir &>/dev/null; then
|
|
229
|
-
local branch=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
230
|
-
local commits=$(git rev-list --count HEAD 2>/dev/null || echo "0")
|
|
231
|
-
echo -e "${BOLD}Git${NC}"
|
|
232
|
-
echo -e " Branch: ${branch}"
|
|
233
|
-
echo -e " Commits: ${commits}"
|
|
234
|
-
fi
|
|
235
|
-
echo ""
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
# =========================================
|
|
239
|
-
# NEW COMMAND - INTERACTIVE SETUP
|
|
240
|
-
# =========================================
|
|
241
|
-
cmd_new() {
|
|
242
|
-
local plan_file="${1:-}"
|
|
243
|
-
|
|
244
|
-
show_banner
|
|
245
|
-
|
|
246
|
-
echo -e "${BOLD}Project Setup${NC}"
|
|
247
|
-
echo ""
|
|
248
|
-
|
|
249
|
-
# Show current directory
|
|
250
|
-
echo -e "Working directory: ${CYAN}$(pwd)${NC}"
|
|
251
|
-
echo ""
|
|
252
|
-
|
|
253
|
-
if ! ask_confirm "Initialize Chief Wiggum in this directory?"; then
|
|
254
|
-
echo -e "${YELLOW}Aborted.${NC}"
|
|
255
|
-
exit 0
|
|
256
|
-
fi
|
|
257
|
-
|
|
258
|
-
# Check for existing chiefwiggum files (not CLAUDE.md - that's project-specific)
|
|
259
|
-
local existing_files=()
|
|
260
|
-
[ -f "$TODO_FILE" ] && existing_files+=("TODO.md")
|
|
261
|
-
[ -d "specs" ] && existing_files+=("specs/")
|
|
262
|
-
|
|
263
|
-
if [ ${#existing_files[@]} -gt 0 ]; then
|
|
264
|
-
echo ""
|
|
265
|
-
echo -e "${YELLOW}Existing files found:${NC}"
|
|
266
|
-
for f in "${existing_files[@]}"; do
|
|
267
|
-
echo " - $f"
|
|
268
|
-
done
|
|
269
|
-
echo ""
|
|
270
|
-
if ! ask_confirm "Overwrite these files?" "n"; then
|
|
271
|
-
echo -e "${YELLOW}Aborted.${NC}"
|
|
272
|
-
exit 0
|
|
273
|
-
fi
|
|
274
|
-
fi
|
|
275
|
-
|
|
276
|
-
# Note if CLAUDE.md exists (we'll preserve it)
|
|
277
|
-
local has_claude_md=false
|
|
278
|
-
[ -f "CLAUDE.md" ] && has_claude_md=true
|
|
279
|
-
|
|
280
|
-
# If plan file provided, use it directly
|
|
281
|
-
if [ -n "$plan_file" ]; then
|
|
282
|
-
if [ ! -f "$plan_file" ]; then
|
|
283
|
-
echo -e "${RED}Plan file not found: ${plan_file}${NC}"
|
|
284
|
-
exit 1
|
|
285
|
-
fi
|
|
286
|
-
generate_from_plan "$plan_file"
|
|
287
|
-
start_loop
|
|
288
|
-
return
|
|
289
|
-
fi
|
|
290
|
-
|
|
291
|
-
# Interactive setup
|
|
292
|
-
ask_choice "How would you like to set up this project?" \
|
|
293
|
-
"From a plan file - I have a plan.md ready" \
|
|
294
|
-
"Describe it - I'll tell you what to build" \
|
|
295
|
-
"Existing TODO - Just start the loop with current TODO.md"
|
|
296
|
-
local setup_choice=$?
|
|
297
|
-
|
|
298
|
-
case $setup_choice in
|
|
299
|
-
0)
|
|
300
|
-
# From plan file
|
|
301
|
-
echo ""
|
|
302
|
-
# Show available plans
|
|
303
|
-
if [ -d "plans" ] && [ "$(ls -A plans/*.md 2>/dev/null)" ]; then
|
|
304
|
-
echo -e "${BOLD}Available plans:${NC}"
|
|
305
|
-
for f in plans/*.md; do
|
|
306
|
-
echo -e " ${CYAN}${f}${NC}"
|
|
307
|
-
done
|
|
308
|
-
echo ""
|
|
309
|
-
fi
|
|
310
|
-
# Use read -e for tab completion
|
|
311
|
-
local plan_path
|
|
312
|
-
read -e -p "Path to plan file [plans/plan.md]: " plan_path
|
|
313
|
-
plan_path="${plan_path:-plans/plan.md}"
|
|
314
|
-
if [ ! -f "$plan_path" ]; then
|
|
315
|
-
echo -e "${RED}Plan file not found: ${plan_path}${NC}"
|
|
316
|
-
exit 1
|
|
317
|
-
fi
|
|
318
|
-
generate_from_plan "$plan_path"
|
|
319
|
-
;;
|
|
320
|
-
1)
|
|
321
|
-
# Describe project
|
|
322
|
-
interactive_describe
|
|
323
|
-
;;
|
|
324
|
-
2)
|
|
325
|
-
# Existing TODO
|
|
326
|
-
if [ ! -f "$TODO_FILE" ]; then
|
|
327
|
-
echo -e "${RED}No TODO.md found. Run 'chiefwiggum new' to set up first.${NC}"
|
|
328
|
-
exit 1
|
|
329
|
-
fi
|
|
330
|
-
;;
|
|
331
|
-
esac
|
|
332
|
-
|
|
333
|
-
# Ask about starting the loop
|
|
334
|
-
echo ""
|
|
335
|
-
ask_choice "Ready to start. What would you like to do?" \
|
|
336
|
-
"Start the build loop now" \
|
|
337
|
-
"Review files first, then start manually with 'chiefwiggum loop'" \
|
|
338
|
-
"Exit"
|
|
339
|
-
local start_choice=$?
|
|
340
|
-
|
|
341
|
-
case $start_choice in
|
|
342
|
-
0) start_loop ;;
|
|
343
|
-
1)
|
|
344
|
-
echo ""
|
|
345
|
-
echo -e "${GREEN}Setup complete!${NC} Review your files, then run:"
|
|
346
|
-
echo -e " ${CYAN}chiefwiggum loop${NC}"
|
|
347
|
-
;;
|
|
348
|
-
2)
|
|
349
|
-
echo -e "${YELLOW}Exiting.${NC}"
|
|
350
|
-
;;
|
|
351
|
-
esac
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
# =========================================
|
|
355
|
-
# INTERACTIVE PROJECT DESCRIPTION
|
|
356
|
-
# =========================================
|
|
357
|
-
interactive_describe() {
|
|
358
|
-
echo ""
|
|
359
|
-
echo -e "${BOLD}Tell me about your project${NC}"
|
|
360
|
-
echo ""
|
|
361
|
-
|
|
362
|
-
# Project name
|
|
363
|
-
local project_name
|
|
364
|
-
project_name=$(ask_text "Project name")
|
|
365
|
-
|
|
366
|
-
# Project type
|
|
367
|
-
ask_choice "What type of project is this?" \
|
|
368
|
-
"Web application (Next.js, React, etc.)" \
|
|
369
|
-
"API / Backend service" \
|
|
370
|
-
"CLI tool" \
|
|
371
|
-
"Library / Package" \
|
|
372
|
-
"Mobile app" \
|
|
373
|
-
"Other"
|
|
374
|
-
local project_type=$?
|
|
375
|
-
local project_type_names=("web" "api" "cli" "library" "mobile" "other")
|
|
376
|
-
local project_type_name="${project_type_names[$project_type]}"
|
|
377
|
-
|
|
378
|
-
# Tech stack
|
|
379
|
-
local tech_stack
|
|
380
|
-
tech_stack=$(ask_text "Tech stack (e.g., Next.js, TypeScript, Convex)")
|
|
381
|
-
|
|
382
|
-
# Description
|
|
383
|
-
echo ""
|
|
384
|
-
echo -e "${BOLD}Describe what you're building:${NC}"
|
|
385
|
-
echo "(Enter description, then press Enter twice to finish)"
|
|
386
|
-
echo ""
|
|
387
|
-
local description=""
|
|
388
|
-
local empty_lines=0
|
|
389
|
-
while IFS= read -r line; do
|
|
390
|
-
if [ -z "$line" ]; then
|
|
391
|
-
((empty_lines++))
|
|
392
|
-
[ $empty_lines -ge 1 ] && break
|
|
393
|
-
else
|
|
394
|
-
empty_lines=0
|
|
395
|
-
fi
|
|
396
|
-
description+="$line"$'\n'
|
|
397
|
-
done
|
|
398
|
-
|
|
399
|
-
# Key features
|
|
400
|
-
echo ""
|
|
401
|
-
echo -e "${BOLD}List key features (one per line, empty line to finish):${NC}"
|
|
402
|
-
local features=""
|
|
403
|
-
while IFS= read -r line; do
|
|
404
|
-
[ -z "$line" ] && break
|
|
405
|
-
features+="- $line"$'\n'
|
|
406
|
-
done
|
|
407
|
-
|
|
408
|
-
# Generate plan content
|
|
409
|
-
local plan_content="# ${project_name}
|
|
410
|
-
|
|
411
|
-
## Overview
|
|
412
|
-
${description}
|
|
413
|
-
|
|
414
|
-
## Project Type
|
|
415
|
-
${project_type_name}
|
|
416
|
-
|
|
417
|
-
## Tech Stack
|
|
418
|
-
${tech_stack}
|
|
419
|
-
|
|
420
|
-
## Key Features
|
|
421
|
-
${features}
|
|
422
|
-
"
|
|
423
|
-
|
|
424
|
-
# Save plan
|
|
425
|
-
mkdir -p plans
|
|
426
|
-
local plan_file="plans/plan.md"
|
|
427
|
-
echo "$plan_content" > "$plan_file"
|
|
428
|
-
echo ""
|
|
429
|
-
echo -e "${GREEN}Plan saved to ${plan_file}${NC}"
|
|
430
|
-
|
|
431
|
-
# Generate specs from plan
|
|
432
|
-
generate_from_plan "$plan_file"
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
# =========================================
|
|
436
|
-
# GENERATE SPECS FROM PLAN
|
|
437
|
-
# =========================================
|
|
438
|
-
generate_from_plan() {
|
|
439
|
-
local plan_file="$1"
|
|
440
|
-
local plan_content
|
|
441
|
-
plan_content="$(cat "$plan_file")"
|
|
442
|
-
|
|
443
|
-
echo ""
|
|
444
|
-
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
445
|
-
echo -e "${GREEN} Generating specs from: ${plan_file}${NC}"
|
|
446
|
-
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
447
|
-
|
|
448
|
-
# Check templates exist
|
|
449
|
-
if [ ! -d "$TEMPLATES_DIR" ]; then
|
|
450
|
-
echo -e "${RED}Templates not found at: ${TEMPLATES_DIR}${NC}"
|
|
451
|
-
echo ""
|
|
452
|
-
echo "Run this to set up templates:"
|
|
453
|
-
echo -e " ${CYAN}npx chiefwiggum@latest${NC}"
|
|
454
|
-
exit 1
|
|
455
|
-
fi
|
|
456
|
-
|
|
457
|
-
mkdir -p specs
|
|
458
|
-
|
|
459
|
-
# Load templates
|
|
460
|
-
local prd_template=$(cat "$TEMPLATES_DIR/specs/prd.md")
|
|
461
|
-
local tech_template=$(cat "$TEMPLATES_DIR/specs/technical.md")
|
|
462
|
-
local todo_template=$(cat "$TEMPLATES_DIR/TODO.md")
|
|
463
|
-
local claude_template=$(cat "$TEMPLATES_DIR/CLAUDE.md")
|
|
464
|
-
|
|
465
|
-
# Step 1: Generate PRD
|
|
466
|
-
echo ""
|
|
467
|
-
echo -e "${YELLOW}[1/4] Generating specs/prd.md...${NC}"
|
|
468
|
-
|
|
469
|
-
local prd_prompt="You are filling in a PRD template based on a project plan.
|
|
470
|
-
|
|
471
|
-
Here is the plan:
|
|
472
|
-
<plan>
|
|
473
|
-
${plan_content}
|
|
474
|
-
</plan>
|
|
475
|
-
|
|
476
|
-
Here is the template to fill in:
|
|
477
|
-
<template>
|
|
478
|
-
${prd_template}
|
|
479
|
-
</template>
|
|
480
|
-
|
|
481
|
-
Fill in the template with specific details from the plan.
|
|
482
|
-
Replace all placeholder text in [brackets] with real content.
|
|
483
|
-
Write the completed PRD directly to specs/prd.md.
|
|
484
|
-
Do NOT ask questions — infer everything from the plan."
|
|
485
|
-
|
|
486
|
-
echo "$prd_prompt" | claude -p --dangerously-skip-permissions 2>/dev/null
|
|
487
|
-
echo -e "${GREEN} ✓ specs/prd.md${NC}"
|
|
488
|
-
|
|
489
|
-
# Step 2: Generate Technical Spec
|
|
490
|
-
echo ""
|
|
491
|
-
echo -e "${YELLOW}[2/4] Generating specs/technical.md...${NC}"
|
|
492
|
-
local prd_generated
|
|
493
|
-
prd_generated="$(cat specs/prd.md 2>/dev/null || echo "")"
|
|
494
|
-
|
|
495
|
-
local tech_prompt="You are filling in a Technical Specification template based on a PRD.
|
|
496
|
-
|
|
497
|
-
Here is the PRD:
|
|
498
|
-
<prd>
|
|
499
|
-
${prd_generated}
|
|
500
|
-
</prd>
|
|
501
|
-
|
|
502
|
-
Here is the template to fill in:
|
|
503
|
-
<template>
|
|
504
|
-
${tech_template}
|
|
505
|
-
</template>
|
|
506
|
-
|
|
507
|
-
Fill in the template with specific technical details.
|
|
508
|
-
Replace all placeholder text in [brackets] with real content.
|
|
509
|
-
Write the completed spec directly to specs/technical.md.
|
|
510
|
-
Do NOT ask questions — infer everything from the PRD."
|
|
511
|
-
|
|
512
|
-
echo "$tech_prompt" | claude -p --dangerously-skip-permissions 2>/dev/null
|
|
513
|
-
echo -e "${GREEN} ✓ specs/technical.md${NC}"
|
|
514
|
-
|
|
515
|
-
# Step 3: Generate CLAUDE.md (skip if exists)
|
|
516
|
-
echo ""
|
|
517
|
-
if [ -f "CLAUDE.md" ]; then
|
|
518
|
-
echo -e "${YELLOW}[3/4] Skipping CLAUDE.md (already exists)${NC}"
|
|
519
|
-
else
|
|
520
|
-
echo -e "${YELLOW}[3/4] Generating CLAUDE.md...${NC}"
|
|
521
|
-
|
|
522
|
-
local claude_prompt="You are filling in a CLAUDE.md template for a project.
|
|
523
|
-
|
|
524
|
-
Here is the PRD:
|
|
525
|
-
<prd>
|
|
526
|
-
${prd_generated}
|
|
527
|
-
</prd>
|
|
528
|
-
|
|
529
|
-
Here is the Technical Spec:
|
|
530
|
-
<technical>
|
|
531
|
-
$(cat specs/technical.md 2>/dev/null || echo "")
|
|
532
|
-
</technical>
|
|
533
|
-
|
|
534
|
-
Here is the template to fill in:
|
|
535
|
-
<template>
|
|
536
|
-
${claude_template}
|
|
537
|
-
</template>
|
|
538
|
-
|
|
539
|
-
Fill in the template with project-specific details.
|
|
540
|
-
Replace all placeholder text in [brackets] with real content.
|
|
541
|
-
Keep it concise - this is a quick reference for AI agents.
|
|
542
|
-
Write directly to CLAUDE.md."
|
|
543
|
-
|
|
544
|
-
echo "$claude_prompt" | claude -p --dangerously-skip-permissions 2>/dev/null
|
|
545
|
-
echo -e "${GREEN} ✓ CLAUDE.md${NC}"
|
|
546
|
-
fi
|
|
547
|
-
|
|
548
|
-
# Step 4: Generate TODO
|
|
549
|
-
echo ""
|
|
550
|
-
echo -e "${YELLOW}[4/4] Generating TODO.md...${NC}"
|
|
551
|
-
|
|
552
|
-
local todo_prompt="You are filling in a TODO template based on specs.
|
|
553
|
-
|
|
554
|
-
Here is the PRD:
|
|
555
|
-
<prd>
|
|
556
|
-
${prd_generated}
|
|
557
|
-
</prd>
|
|
558
|
-
|
|
559
|
-
Here is the Technical Specification:
|
|
560
|
-
<technical>
|
|
561
|
-
$(cat specs/technical.md 2>/dev/null || echo "")
|
|
562
|
-
</technical>
|
|
563
|
-
|
|
564
|
-
Here is the template to follow:
|
|
565
|
-
<template>
|
|
566
|
-
${todo_template}
|
|
567
|
-
</template>
|
|
568
|
-
|
|
569
|
-
Create a phased TODO.md following the template structure.
|
|
570
|
-
Use checkbox format: - [ ] Task description
|
|
571
|
-
Keep tasks granular (1-2 hours max each).
|
|
572
|
-
End each phase with: - [ ] Phase N review
|
|
573
|
-
Write directly to TODO.md."
|
|
574
|
-
|
|
575
|
-
echo "$todo_prompt" | claude -p --dangerously-skip-permissions 2>/dev/null
|
|
576
|
-
echo -e "${GREEN} ✓ TODO.md${NC}"
|
|
577
|
-
|
|
578
|
-
echo ""
|
|
579
|
-
echo -e "${GREEN}Specs generated:${NC}"
|
|
580
|
-
echo " - specs/prd.md"
|
|
581
|
-
echo " - specs/technical.md"
|
|
582
|
-
if [ ! -f "CLAUDE.md" ]; then
|
|
583
|
-
echo " - CLAUDE.md"
|
|
584
|
-
fi
|
|
585
|
-
echo " - TODO.md"
|
|
586
|
-
|
|
587
|
-
# Commit specs
|
|
588
|
-
if git rev-parse --git-dir &>/dev/null; then
|
|
589
|
-
git add specs/ TODO.md CLAUDE.md 2>/dev/null || true
|
|
590
|
-
git commit -m "chore: generate specs from plan" 2>/dev/null || true
|
|
591
|
-
fi
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
# =========================================
|
|
595
|
-
# BUILD LOOP
|
|
596
|
-
# =========================================
|
|
597
|
-
start_loop() {
|
|
598
|
-
[ -f "$TODO_FILE" ] || { echo -e "${RED}Missing $TODO_FILE${NC}"; exit 1; }
|
|
599
|
-
|
|
600
|
-
local max_iterations
|
|
601
|
-
max_iterations=$(grep -c "\[ \]" "$TODO_FILE" 2>/dev/null) || max_iterations=0
|
|
602
|
-
|
|
603
|
-
if [ "$max_iterations" -eq 0 ]; then
|
|
604
|
-
echo -e "${GREEN}No unchecked tasks in $TODO_FILE. All done!${NC}"
|
|
605
|
-
exit 0
|
|
606
|
-
fi
|
|
607
|
-
|
|
608
|
-
echo ""
|
|
609
|
-
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
610
|
-
echo -e "${GREEN} Starting Build Loop${NC}"
|
|
611
|
-
echo -e "${GREEN} Tasks remaining: ${max_iterations}${NC}"
|
|
612
|
-
echo -e "${GREEN} Timeout: ${ITERATION_TIMEOUT_MINUTES}m per task${NC}"
|
|
613
|
-
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
614
|
-
|
|
615
|
-
local consecutive_failures=0
|
|
616
|
-
local no_commit_cycles=0
|
|
617
|
-
local last_commit
|
|
618
|
-
last_commit="$(git rev-parse HEAD 2>/dev/null || echo "none")"
|
|
619
|
-
|
|
620
|
-
for i in $(seq 1 "$max_iterations"); do
|
|
621
|
-
echo ""
|
|
622
|
-
echo -e "${YELLOW}══════════ Task $i of $max_iterations ══════════${NC}"
|
|
623
|
-
|
|
624
|
-
cleanup_stale_processes
|
|
625
|
-
|
|
626
|
-
local start_time
|
|
627
|
-
start_time="$(date +%s)"
|
|
628
|
-
|
|
629
|
-
set +e
|
|
630
|
-
run_task_iteration "$i"
|
|
631
|
-
local claude_exit=$?
|
|
632
|
-
set -e
|
|
633
|
-
|
|
634
|
-
local duration=$(($(date +%s) - start_time))
|
|
635
|
-
echo ""
|
|
636
|
-
echo -e "Completed in ${duration}s (exit: ${claude_exit})"
|
|
637
|
-
|
|
638
|
-
# Track failures
|
|
639
|
-
if [ "$claude_exit" -ne 0 ]; then
|
|
640
|
-
consecutive_failures=$((consecutive_failures + 1))
|
|
641
|
-
echo -e "${RED}Failure ${consecutive_failures}/${MAX_CONSECUTIVE_FAILURES}${NC}"
|
|
642
|
-
sleep 30
|
|
643
|
-
else
|
|
644
|
-
consecutive_failures=0
|
|
645
|
-
fi
|
|
646
|
-
|
|
647
|
-
# Track commits
|
|
648
|
-
local current_commit
|
|
649
|
-
current_commit="$(git rev-parse HEAD 2>/dev/null || echo "none")"
|
|
650
|
-
if [ "$current_commit" = "$last_commit" ]; then
|
|
651
|
-
if [ "$claude_exit" -eq 0 ]; then
|
|
652
|
-
no_commit_cycles=$((no_commit_cycles + 1))
|
|
653
|
-
echo -e "${YELLOW}No commit (${no_commit_cycles}/${MAX_NO_COMMIT_CYCLES})${NC}"
|
|
654
|
-
fi
|
|
655
|
-
else
|
|
656
|
-
no_commit_cycles=0
|
|
657
|
-
last_commit="$current_commit"
|
|
658
|
-
echo -e "${GREEN}New commit: ${current_commit:0:7}${NC}"
|
|
659
|
-
git push origin HEAD 2>/dev/null && echo "Pushed to origin" || true
|
|
660
|
-
fi
|
|
661
|
-
|
|
662
|
-
# Guardrails
|
|
663
|
-
if [ "$consecutive_failures" -ge "$MAX_CONSECUTIVE_FAILURES" ]; then
|
|
664
|
-
echo -e "${RED}Stopping: ${MAX_CONSECUTIVE_FAILURES} consecutive failures${NC}"
|
|
665
|
-
exit 1
|
|
666
|
-
fi
|
|
667
|
-
|
|
668
|
-
if [ "$no_commit_cycles" -ge "$MAX_NO_COMMIT_CYCLES" ]; then
|
|
669
|
-
echo -e "${RED}Stopping: No commits for ${MAX_NO_COMMIT_CYCLES} cycles${NC}"
|
|
670
|
-
exit 1
|
|
671
|
-
fi
|
|
672
|
-
|
|
673
|
-
# Check if done
|
|
674
|
-
if ! grep -q "\[ \]" "$TODO_FILE"; then
|
|
675
|
-
echo ""
|
|
676
|
-
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
677
|
-
echo -e "${GREEN} All tasks completed!${NC}"
|
|
678
|
-
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
679
|
-
git push origin HEAD 2>/dev/null || true
|
|
680
|
-
exit 0
|
|
681
|
-
fi
|
|
682
|
-
|
|
683
|
-
sleep "$COOLDOWN_SECONDS"
|
|
684
|
-
done
|
|
685
|
-
|
|
686
|
-
echo -e "${YELLOW}Reached max iterations (${max_iterations})${NC}"
|
|
687
|
-
git push origin HEAD 2>/dev/null || true
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
run_task_iteration() {
|
|
691
|
-
local iteration="$1"
|
|
692
|
-
local log_file="/tmp/chiefwiggum-iter${iteration}.log"
|
|
693
|
-
|
|
694
|
-
local task_prompt='You are an autonomous coding agent. Complete ONE task from TODO.md.
|
|
695
|
-
|
|
696
|
-
## Before Starting
|
|
697
|
-
Read these files to understand the project:
|
|
698
|
-
- CLAUDE.md — Project context and conventions
|
|
699
|
-
- specs/prd.md — What we are building
|
|
700
|
-
- specs/technical.md — How we are building it
|
|
701
|
-
- TODO.md — Task list
|
|
702
|
-
|
|
703
|
-
## Workflow
|
|
704
|
-
1. Find the FIRST unchecked task (- [ ]) in TODO.md
|
|
705
|
-
2. Plan: Understand what needs to be done
|
|
706
|
-
3. Implement: Write the code
|
|
707
|
-
4. Review: Verify it works
|
|
708
|
-
5. Mark complete: Change - [ ] to - [x] in TODO.md
|
|
709
|
-
6. Commit: git add . && git commit -m "type(scope): description"
|
|
710
|
-
|
|
711
|
-
## Rules
|
|
712
|
-
- ONE task per iteration
|
|
713
|
-
- Follow existing code patterns
|
|
714
|
-
- Use conventional commits (feat, fix, docs, refactor, test, chore)
|
|
715
|
-
- If blocked after 3 attempts, output RALPH_BLOCKED: <reason>
|
|
716
|
-
|
|
717
|
-
## When Done
|
|
718
|
-
Output: RALPH_COMPLETE
|
|
719
|
-
If no tasks remain: RALPH_ALL_DONE'
|
|
720
|
-
|
|
721
|
-
local claude_exit=1
|
|
722
|
-
|
|
723
|
-
for attempt in $(seq 1 "$MAX_CLAUDE_ATTEMPTS"); do
|
|
724
|
-
echo -e "${YELLOW}Attempt ${attempt}/${MAX_CLAUDE_ATTEMPTS}${NC}"
|
|
725
|
-
|
|
726
|
-
echo "$task_prompt" \
|
|
727
|
-
| timeout --kill-after=10s "${ITERATION_TIMEOUT_MINUTES}m" \
|
|
728
|
-
claude -p --dangerously-skip-permissions --no-session-persistence \
|
|
729
|
-
--verbose --output-format stream-json \
|
|
730
|
-
2>&1 \
|
|
731
|
-
| tee "$log_file" \
|
|
732
|
-
| jq -r '
|
|
733
|
-
if .type == "assistant" then
|
|
734
|
-
(.message.content[]? | select(.type=="text") | .text? // empty),
|
|
735
|
-
(.message.content[]? | select(.type=="tool_use") | "🔧 " + .name)
|
|
736
|
-
elif .type == "result" then
|
|
737
|
-
"✅ " + (.subtype // "done")
|
|
738
|
-
else empty end
|
|
739
|
-
' 2>/dev/null || true &
|
|
740
|
-
local claude_pid=$!
|
|
741
|
-
|
|
742
|
-
# Monitor for freeze
|
|
743
|
-
while kill -0 $claude_pid 2>/dev/null; do
|
|
744
|
-
if grep -q "No messages returned" "$log_file" 2>/dev/null; then
|
|
745
|
-
echo -e "${RED}Freeze detected, killing...${NC}"
|
|
746
|
-
kill -9 $claude_pid 2>/dev/null || true
|
|
747
|
-
sleep 2
|
|
748
|
-
break
|
|
749
|
-
fi
|
|
750
|
-
sleep 1
|
|
751
|
-
done
|
|
752
|
-
|
|
753
|
-
wait $claude_pid 2>/dev/null
|
|
754
|
-
claude_exit=$?
|
|
755
|
-
|
|
756
|
-
# Check for transient errors
|
|
757
|
-
if [ "$claude_exit" -ne 0 ]; then
|
|
758
|
-
if grep -qE "(No messages returned|ECONNRESET|ETIMEDOUT)" "$log_file" 2>/dev/null; then
|
|
759
|
-
echo -e "${YELLOW}Transient error, retrying...${NC}"
|
|
760
|
-
sleep $((attempt * 5))
|
|
761
|
-
continue
|
|
762
|
-
fi
|
|
763
|
-
fi
|
|
764
|
-
|
|
765
|
-
break
|
|
766
|
-
done
|
|
767
|
-
|
|
768
|
-
rm -f "$log_file"
|
|
769
|
-
return $claude_exit
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
cleanup_stale_processes() {
|
|
773
|
-
pkill -f playwright 2>/dev/null || true
|
|
774
|
-
pkill -f chromium 2>/dev/null || true
|
|
775
|
-
if command -v lsof &>/dev/null; then
|
|
776
|
-
lsof -ti:3000 | xargs kill -9 2>/dev/null || true
|
|
777
|
-
fi
|
|
778
|
-
sleep 1
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
# =========================================
|
|
782
|
-
# SETUP TEMPLATES
|
|
783
|
-
# =========================================
|
|
784
|
-
setup_templates() {
|
|
785
|
-
# Copy bundled templates to ~/.chiefwiggum/templates
|
|
786
|
-
if [ -d "$BUNDLED_TEMPLATES" ]; then
|
|
787
|
-
mkdir -p "$CHIEFWIGGUM_HOME"
|
|
788
|
-
cp -r "$BUNDLED_TEMPLATES" "$CHIEFWIGGUM_HOME/"
|
|
789
|
-
echo -e "${GREEN}✓ Templates installed to: ${TEMPLATES_DIR}${NC}"
|
|
790
|
-
fi
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
# =========================================
|
|
794
|
-
# INSTALL COMMAND
|
|
795
|
-
# =========================================
|
|
796
|
-
cmd_install() {
|
|
797
|
-
show_banner
|
|
798
|
-
|
|
799
|
-
echo -e "${BOLD}Installing Chief Wiggum...${NC}"
|
|
800
|
-
echo ""
|
|
801
|
-
|
|
802
|
-
# Check if already installed globally
|
|
803
|
-
if command -v chiefwiggum &>/dev/null; then
|
|
804
|
-
local installed_path=$(which chiefwiggum)
|
|
805
|
-
if [[ "$installed_path" != *"npx"* ]] && [[ "$installed_path" != *".npm/_npx"* ]]; then
|
|
806
|
-
echo -e "${GREEN}✓ Already installed at: ${installed_path}${NC}"
|
|
807
|
-
# Still set up templates in case they're missing
|
|
808
|
-
setup_templates
|
|
809
|
-
echo ""
|
|
810
|
-
echo "Run 'chiefwiggum new' to get started."
|
|
811
|
-
return 0
|
|
812
|
-
fi
|
|
813
|
-
fi
|
|
814
|
-
|
|
815
|
-
# Install globally
|
|
816
|
-
echo "Running: npm install -g chiefwiggum@latest"
|
|
817
|
-
echo ""
|
|
818
|
-
|
|
819
|
-
if npm install -g chiefwiggum@latest; then
|
|
820
|
-
echo ""
|
|
821
|
-
# Set up templates after install
|
|
822
|
-
# Need to get the installed location
|
|
823
|
-
local installed_dir=$(dirname "$(realpath "$(which chiefwiggum)")")
|
|
824
|
-
if [ -d "${installed_dir}/templates" ]; then
|
|
825
|
-
mkdir -p "$CHIEFWIGGUM_HOME"
|
|
826
|
-
cp -r "${installed_dir}/templates" "$CHIEFWIGGUM_HOME/"
|
|
827
|
-
echo -e "${GREEN}✓ Templates installed to: ${TEMPLATES_DIR}${NC}"
|
|
828
|
-
fi
|
|
829
|
-
|
|
830
|
-
echo ""
|
|
831
|
-
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
832
|
-
echo -e "${GREEN} ✓ Installation complete!${NC}"
|
|
833
|
-
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
834
|
-
echo ""
|
|
835
|
-
echo -e "Templates: ${CYAN}${TEMPLATES_DIR}${NC}"
|
|
836
|
-
echo ""
|
|
837
|
-
echo "Get started:"
|
|
838
|
-
echo -e " ${CYAN}chiefwiggum new${NC} # Set up a new project"
|
|
839
|
-
echo -e " ${CYAN}chiefwiggum loop${NC} # Run the build loop"
|
|
840
|
-
echo -e " ${CYAN}chiefwiggum help${NC} # See all commands"
|
|
841
|
-
echo ""
|
|
842
|
-
else
|
|
843
|
-
echo ""
|
|
844
|
-
echo -e "${RED}Installation failed.${NC}"
|
|
845
|
-
echo "Try running with sudo: sudo npm install -g chiefwiggum"
|
|
846
|
-
exit 1
|
|
847
|
-
fi
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
# =========================================
|
|
851
|
-
# MAIN
|
|
852
|
-
# =========================================
|
|
853
|
-
main() {
|
|
854
|
-
local cmd="${1:-}"
|
|
855
|
-
|
|
856
|
-
case "$cmd" in
|
|
857
|
-
install)
|
|
858
|
-
cmd_install
|
|
859
|
-
;;
|
|
860
|
-
new)
|
|
861
|
-
shift
|
|
862
|
-
cmd_new "$@"
|
|
863
|
-
;;
|
|
864
|
-
loop)
|
|
865
|
-
show_banner
|
|
866
|
-
start_loop
|
|
867
|
-
;;
|
|
868
|
-
status)
|
|
869
|
-
cmd_status
|
|
870
|
-
;;
|
|
871
|
-
help|--help|-h)
|
|
872
|
-
show_help
|
|
873
|
-
;;
|
|
874
|
-
"")
|
|
875
|
-
# No command: if running via npx, install. Otherwise run loop.
|
|
876
|
-
if [[ "${0}" == *"npx"* ]] || [[ "${0}" == *".npm/_npx"* ]]; then
|
|
877
|
-
cmd_install
|
|
878
|
-
else
|
|
879
|
-
show_banner
|
|
880
|
-
if [ -f "$TODO_FILE" ]; then
|
|
881
|
-
start_loop
|
|
882
|
-
else
|
|
883
|
-
echo -e "${YELLOW}No TODO.md found. Run 'chiefwiggum new' to get started.${NC}"
|
|
884
|
-
exit 1
|
|
885
|
-
fi
|
|
886
|
-
fi
|
|
887
|
-
;;
|
|
888
|
-
*)
|
|
889
|
-
# Check if it's a file path (for backward compat with ralph.sh)
|
|
890
|
-
if [ -f "$cmd" ]; then
|
|
891
|
-
show_banner
|
|
892
|
-
generate_from_plan "$cmd"
|
|
893
|
-
start_loop
|
|
894
|
-
else
|
|
895
|
-
echo -e "${RED}Unknown command: ${cmd}${NC}"
|
|
896
|
-
echo "Run 'chiefwiggum help' for usage."
|
|
897
|
-
exit 1
|
|
898
|
-
fi
|
|
899
|
-
;;
|
|
900
|
-
esac
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
main "$@"
|