@mindfoldhq/trellis 0.1.0 → 0.1.2
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/README.md +58 -11
- package/dist/.claude/agents/check.md +98 -0
- package/dist/.claude/agents/debug.md +109 -0
- package/dist/{templates/agents/dispatch.txt → .claude/agents/dispatch.md} +20 -12
- package/dist/.claude/agents/implement.md +101 -0
- package/dist/.claude/agents/plan.md +396 -0
- package/dist/.claude/agents/research.md +120 -0
- package/dist/{templates/commands/common/check-cross-layer.txt → .claude/commands/check-cross-layer.md} +29 -29
- package/dist/{templates/commands/common/onboard-developer.txt → .claude/commands/onboard-developer.md} +2 -2
- package/dist/.claude/commands/parallel.md +199 -0
- package/dist/{templates/commands/common/record-agent-flow.txt → .claude/commands/record-agent-flow.md} +1 -1
- package/dist/.claude/commands/start.md +192 -0
- package/dist/{templates → .claude}/hooks/inject-subagent-context.py +63 -0
- package/dist/.cursor/commands/before-backend-dev.md +13 -0
- package/dist/.cursor/commands/before-frontend-dev.md +13 -0
- package/dist/.cursor/commands/break-loop.md +107 -0
- package/dist/.cursor/commands/check-backend.md +13 -0
- package/dist/.cursor/commands/check-cross-layer.md +153 -0
- package/dist/.cursor/commands/check-frontend.md +13 -0
- package/dist/.cursor/commands/create-command.md +154 -0
- package/dist/.cursor/commands/finish-work.md +129 -0
- package/dist/.cursor/commands/integrate-skill.md +219 -0
- package/dist/.cursor/commands/onboard-developer.md +355 -0
- package/dist/.cursor/commands/record-agent-flow.md +62 -0
- package/dist/.cursor/commands/start.md +157 -0
- package/dist/{templates/markdown/agent-traces-index.md.txt → .trellis/agent-traces/index.md} +8 -9
- package/dist/{templates/scripts/add-session.sh.txt → .trellis/scripts/add-session.sh} +14 -14
- package/dist/{templates/scripts/common/developer.sh.txt → .trellis/scripts/common/developer.sh} +13 -13
- package/dist/{templates/scripts/common/git-context.sh.txt → .trellis/scripts/common/git-context.sh} +8 -8
- package/dist/{templates/scripts/common/paths.sh.txt → .trellis/scripts/common/paths.sh} +4 -4
- package/dist/.trellis/scripts/common/phase.sh +150 -0
- package/dist/.trellis/scripts/common/worktree.sh +138 -0
- package/dist/{templates/scripts/feature.sh.txt → .trellis/scripts/feature.sh} +297 -0
- package/dist/.trellis/scripts/multi-agent/cleanup.sh +416 -0
- package/dist/.trellis/scripts/multi-agent/create-pr.sh +241 -0
- package/dist/.trellis/scripts/multi-agent/plan.sh +232 -0
- package/dist/.trellis/scripts/multi-agent/start.sh +344 -0
- package/dist/.trellis/scripts/multi-agent/status.sh +695 -0
- package/dist/.trellis/structure/backend/database-guidelines.md +51 -0
- package/dist/.trellis/structure/backend/directory-structure.md +209 -0
- package/dist/.trellis/structure/backend/error-handling.md +278 -0
- package/dist/.trellis/structure/backend/index.md +38 -0
- package/dist/.trellis/structure/backend/logging-guidelines.md +266 -0
- package/dist/.trellis/structure/backend/quality-guidelines.md +313 -0
- package/dist/.trellis/structure/frontend/component-guidelines.md +59 -0
- package/dist/.trellis/structure/frontend/directory-structure.md +54 -0
- package/dist/.trellis/structure/frontend/hook-guidelines.md +51 -0
- package/dist/.trellis/structure/frontend/index.md +39 -0
- package/dist/.trellis/structure/frontend/quality-guidelines.md +51 -0
- package/dist/.trellis/structure/frontend/state-management.md +51 -0
- package/dist/.trellis/structure/frontend/type-safety.md +51 -0
- package/dist/.trellis/structure/guides/code-reuse-thinking-guide.md +92 -0
- package/dist/.trellis/structure/guides/cross-layer-thinking-guide.md +94 -0
- package/dist/.trellis/structure/guides/index.md +79 -0
- package/dist/{templates/markdown/workflow.md.txt → .trellis/workflow.md} +6 -6
- package/dist/.trellis/worktree.yaml +49 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +29 -15
- package/dist/commands/init.js.map +1 -1
- package/dist/configurators/claude.d.ts +17 -17
- package/dist/configurators/claude.d.ts.map +1 -1
- package/dist/configurators/claude.js +29 -59
- package/dist/configurators/claude.js.map +1 -1
- package/dist/configurators/cursor.d.ts +3 -3
- package/dist/configurators/cursor.d.ts.map +1 -1
- package/dist/configurators/cursor.js +11 -15
- package/dist/configurators/cursor.js.map +1 -1
- package/dist/configurators/opencode.d.ts +25 -0
- package/dist/configurators/opencode.d.ts.map +1 -0
- package/dist/configurators/opencode.js +54 -0
- package/dist/configurators/opencode.js.map +1 -0
- package/dist/configurators/workflow.d.ts +9 -0
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +32 -72
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/templates/extract.d.ts +73 -8
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +149 -10
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/markdown/{agents.md.txt → agents.md} +1 -1
- package/dist/templates/markdown/index.d.ts +5 -1
- package/dist/templates/markdown/index.d.ts.map +1 -1
- package/dist/templates/markdown/index.js +54 -26
- package/dist/templates/markdown/index.js.map +1 -1
- package/dist/templates/markdown/{init-agent.md.txt → init-agent.md} +8 -8
- package/dist/templates/markdown/structure/backend/directory-structure.md.txt +6 -6
- package/dist/templates/markdown/structure/backend/error-handling.md.txt +8 -8
- package/dist/templates/markdown/structure/backend/index.md.txt +5 -5
- package/dist/templates/markdown/structure/backend/logging-guidelines.md.txt +8 -8
- package/dist/templates/markdown/structure/frontend/index.md.txt +6 -6
- package/dist/templates/markdown/structure/guides/cross-layer-thinking-guide.md.txt +5 -5
- package/dist/templates/markdown/structure/guides/index.md.txt +7 -7
- package/dist/templates/markdown/worktree.yaml.txt +58 -0
- package/dist/types/ai-tools.d.ts +2 -2
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +4 -0
- package/dist/types/ai-tools.js.map +1 -1
- package/package.json +1 -1
- package/dist/configurators/templates.d.ts +0 -40
- package/dist/configurators/templates.d.ts.map +0 -1
- package/dist/configurators/templates.js +0 -67
- package/dist/configurators/templates.js.map +0 -1
- package/dist/templates/agents/check.txt +0 -120
- package/dist/templates/agents/debug.txt +0 -121
- package/dist/templates/agents/implement.txt +0 -114
- package/dist/templates/agents/index.d.ts +0 -35
- package/dist/templates/agents/index.d.ts.map +0 -1
- package/dist/templates/agents/index.js +0 -71
- package/dist/templates/agents/index.js.map +0 -1
- package/dist/templates/agents/research.txt +0 -258
- package/dist/templates/commands/claude/start.md.txt +0 -127
- package/dist/templates/commands/cursor/start.md.txt +0 -94
- package/dist/templates/commands/index.d.ts +0 -46
- package/dist/templates/commands/index.d.ts.map +0 -1
- package/dist/templates/commands/index.js +0 -151
- package/dist/templates/commands/index.js.map +0 -1
- package/dist/templates/hooks/index.d.ts +0 -33
- package/dist/templates/hooks/index.d.ts.map +0 -1
- package/dist/templates/hooks/index.js +0 -53
- package/dist/templates/hooks/index.js.map +0 -1
- package/dist/templates/markdown/gitignore.txt +0 -3
- package/dist/templates/scripts/index.d.ts +0 -25
- package/dist/templates/scripts/index.d.ts.map +0 -1
- package/dist/templates/scripts/index.js +0 -28
- package/dist/templates/scripts/index.js.map +0 -1
- /package/dist/{templates/commands/common/before-backend-dev.txt → .claude/commands/before-backend-dev.md} +0 -0
- /package/dist/{templates/commands/common/before-frontend-dev.txt → .claude/commands/before-frontend-dev.md} +0 -0
- /package/dist/{templates/commands/common/break-loop.txt → .claude/commands/break-loop.md} +0 -0
- /package/dist/{templates/commands/common/check-backend.txt → .claude/commands/check-backend.md} +0 -0
- /package/dist/{templates/commands/common/check-frontend.txt → .claude/commands/check-frontend.md} +0 -0
- /package/dist/{templates/commands/common/create-command.txt → .claude/commands/create-command.md} +0 -0
- /package/dist/{templates/commands/common/finish-work.txt → .claude/commands/finish-work.md} +0 -0
- /package/dist/{templates/commands/common/integrate-skill.txt → .claude/commands/integrate-skill.md} +0 -0
- /package/dist/{templates/hooks → .claude}/settings.json +0 -0
- /package/dist/{templates/scripts/create-bootstrap.sh.txt → .trellis/scripts/create-bootstrap.sh} +0 -0
- /package/dist/{templates/scripts/get-context.sh.txt → .trellis/scripts/get-context.sh} +0 -0
- /package/dist/{templates/scripts/get-developer.sh.txt → .trellis/scripts/get-developer.sh} +0 -0
- /package/dist/{templates/scripts/init-developer.sh.txt → .trellis/scripts/init-developer.sh} +0 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Multi-Agent Pipeline: Status Monitor
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./status.sh Show summary of all features (default)
|
|
7
|
+
# ./status.sh --list List all worktrees and agents
|
|
8
|
+
# ./status.sh --detail <feature> Detailed feature status
|
|
9
|
+
# ./status.sh --watch <feature> Watch agent log in real-time
|
|
10
|
+
# ./status.sh --log <feature> Show recent log entries
|
|
11
|
+
# ./status.sh --registry Show agent registry
|
|
12
|
+
# =============================================================================
|
|
13
|
+
|
|
14
|
+
set -e
|
|
15
|
+
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
source "$SCRIPT_DIR/../common/paths.sh"
|
|
18
|
+
source "$SCRIPT_DIR/../common/worktree.sh"
|
|
19
|
+
source "$SCRIPT_DIR/../common/developer.sh"
|
|
20
|
+
source "$SCRIPT_DIR/../common/phase.sh"
|
|
21
|
+
|
|
22
|
+
# Colors
|
|
23
|
+
RED='\033[0;31m'
|
|
24
|
+
GREEN='\033[0;32m'
|
|
25
|
+
YELLOW='\033[1;33m'
|
|
26
|
+
BLUE='\033[0;34m'
|
|
27
|
+
CYAN='\033[0;36m'
|
|
28
|
+
DIM='\033[2m'
|
|
29
|
+
NC='\033[0m'
|
|
30
|
+
|
|
31
|
+
PROJECT_ROOT=$(get_repo_root)
|
|
32
|
+
|
|
33
|
+
# =============================================================================
|
|
34
|
+
# Parse Arguments
|
|
35
|
+
# =============================================================================
|
|
36
|
+
ACTION="summary"
|
|
37
|
+
TARGET=""
|
|
38
|
+
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case $1 in
|
|
41
|
+
--list)
|
|
42
|
+
ACTION="list"
|
|
43
|
+
shift
|
|
44
|
+
;;
|
|
45
|
+
--detail)
|
|
46
|
+
ACTION="detail"
|
|
47
|
+
TARGET="$2"
|
|
48
|
+
shift 2
|
|
49
|
+
;;
|
|
50
|
+
--watch)
|
|
51
|
+
ACTION="watch"
|
|
52
|
+
TARGET="$2"
|
|
53
|
+
shift 2
|
|
54
|
+
;;
|
|
55
|
+
--log)
|
|
56
|
+
ACTION="log"
|
|
57
|
+
TARGET="$2"
|
|
58
|
+
shift 2
|
|
59
|
+
;;
|
|
60
|
+
--progress)
|
|
61
|
+
ACTION="progress"
|
|
62
|
+
TARGET="$2"
|
|
63
|
+
shift 2
|
|
64
|
+
;;
|
|
65
|
+
--registry)
|
|
66
|
+
ACTION="registry"
|
|
67
|
+
shift
|
|
68
|
+
;;
|
|
69
|
+
-h|--help)
|
|
70
|
+
ACTION="help"
|
|
71
|
+
shift
|
|
72
|
+
;;
|
|
73
|
+
*)
|
|
74
|
+
TARGET="$1"
|
|
75
|
+
shift
|
|
76
|
+
;;
|
|
77
|
+
esac
|
|
78
|
+
done
|
|
79
|
+
|
|
80
|
+
# =============================================================================
|
|
81
|
+
# Helper Functions
|
|
82
|
+
# =============================================================================
|
|
83
|
+
|
|
84
|
+
# Check if PID is running
|
|
85
|
+
is_running() {
|
|
86
|
+
local pid="$1"
|
|
87
|
+
[ -n "$pid" ] && kill -0 "$pid" 2>/dev/null
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Get status color
|
|
91
|
+
status_color() {
|
|
92
|
+
local status="$1"
|
|
93
|
+
case "$status" in
|
|
94
|
+
completed) echo "${GREEN}" ;;
|
|
95
|
+
in_progress) echo "${BLUE}" ;;
|
|
96
|
+
planning) echo "${YELLOW}" ;;
|
|
97
|
+
*) echo "${DIM}" ;;
|
|
98
|
+
esac
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# Find agent by feature name or ID
|
|
102
|
+
find_agent() {
|
|
103
|
+
local search="$1"
|
|
104
|
+
AGENTS_DIR=$(get_agents_dir)
|
|
105
|
+
REGISTRY_FILE="${AGENTS_DIR}/registry.json"
|
|
106
|
+
|
|
107
|
+
if [ ! -f "$REGISTRY_FILE" ]; then
|
|
108
|
+
return 1
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Try exact ID match first
|
|
112
|
+
local agent=$(jq -r --arg id "$search" '.agents[] | select(.id == $id)' "$REGISTRY_FILE" 2>/dev/null)
|
|
113
|
+
|
|
114
|
+
# Try partial match on feature_dir
|
|
115
|
+
if [ -z "$agent" ] || [ "$agent" = "null" ]; then
|
|
116
|
+
agent=$(jq -r --arg search "$search" '.agents[] | select(.feature_dir | contains($search))' "$REGISTRY_FILE" 2>/dev/null | head -1)
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
echo "$agent"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Get the last tool call from agent log
|
|
123
|
+
get_last_tool() {
|
|
124
|
+
local log_file="$1"
|
|
125
|
+
if [ ! -f "$log_file" ]; then
|
|
126
|
+
echo ""
|
|
127
|
+
return
|
|
128
|
+
fi
|
|
129
|
+
# Use tail -r on macOS, tac on Linux
|
|
130
|
+
if command -v tac &>/dev/null; then
|
|
131
|
+
tac "$log_file" 2>/dev/null | head -100 | jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | .name' 2>/dev/null | head -1
|
|
132
|
+
else
|
|
133
|
+
tail -r "$log_file" 2>/dev/null | head -100 | jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | .name' 2>/dev/null | head -1
|
|
134
|
+
fi
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Get the last assistant text from agent log
|
|
138
|
+
get_last_message() {
|
|
139
|
+
local log_file="$1"
|
|
140
|
+
local max_len="${2:-100}"
|
|
141
|
+
if [ ! -f "$log_file" ]; then
|
|
142
|
+
echo ""
|
|
143
|
+
return
|
|
144
|
+
fi
|
|
145
|
+
local text
|
|
146
|
+
# Use tail -r on macOS, tac on Linux
|
|
147
|
+
if command -v tac &>/dev/null; then
|
|
148
|
+
text=$(tac "$log_file" 2>/dev/null | head -100 | jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="text") | .text' 2>/dev/null | head -1)
|
|
149
|
+
else
|
|
150
|
+
text=$(tail -r "$log_file" 2>/dev/null | head -100 | jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="text") | .text' 2>/dev/null | head -1)
|
|
151
|
+
fi
|
|
152
|
+
if [ -n "$text" ] && [ "$text" != "null" ]; then
|
|
153
|
+
echo "${text:0:$max_len}"
|
|
154
|
+
fi
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# Get recent task notifications from agent log
|
|
158
|
+
# Looks for async_launched tasks and infers completion from current_phase
|
|
159
|
+
get_recent_tasks() {
|
|
160
|
+
local log_file="$1"
|
|
161
|
+
local count="${2:-5}"
|
|
162
|
+
local current_phase="${3:-0}"
|
|
163
|
+
if [ ! -f "$log_file" ]; then
|
|
164
|
+
return
|
|
165
|
+
fi
|
|
166
|
+
# Get async_launched tasks with phase number extracted from description
|
|
167
|
+
tail -500 "$log_file" 2>/dev/null | jq -r --argjson current_phase "$current_phase" '
|
|
168
|
+
select(.type=="user" and .tool_use_result.status == "async_launched" and .tool_use_result.description != null) |
|
|
169
|
+
.tool_use_result.description as $desc |
|
|
170
|
+
# Extract phase number from "Phase N:" pattern
|
|
171
|
+
($desc | capture("Phase (?<num>[0-9]+)") | .num | tonumber) as $phase_num |
|
|
172
|
+
# If current_phase > this phase, it is completed
|
|
173
|
+
(if $phase_num < $current_phase then "completed" else "async_launched" end) as $status |
|
|
174
|
+
"\($status)|\($desc)"
|
|
175
|
+
' 2>/dev/null | tail -"$count"
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# =============================================================================
|
|
179
|
+
# Commands
|
|
180
|
+
# =============================================================================
|
|
181
|
+
|
|
182
|
+
cmd_help() {
|
|
183
|
+
cat << EOF
|
|
184
|
+
Multi-Agent Pipeline: Status Monitor
|
|
185
|
+
|
|
186
|
+
Usage:
|
|
187
|
+
$0 Show summary of all features
|
|
188
|
+
$0 --list List all worktrees and agents
|
|
189
|
+
$0 --detail <feature> Detailed feature status
|
|
190
|
+
$0 --progress <feature> Quick progress view with recent activity
|
|
191
|
+
$0 --watch <feature> Watch agent log in real-time
|
|
192
|
+
$0 --log <feature> Show recent log entries
|
|
193
|
+
$0 --registry Show agent registry
|
|
194
|
+
|
|
195
|
+
Examples:
|
|
196
|
+
$0 --detail my-feature
|
|
197
|
+
$0 --progress my-feature
|
|
198
|
+
$0 --watch 16-worktree-support
|
|
199
|
+
$0 --log worktree-support
|
|
200
|
+
EOF
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
cmd_list() {
|
|
204
|
+
echo -e "${BLUE}=== Git Worktrees ===${NC}"
|
|
205
|
+
echo ""
|
|
206
|
+
cd "$PROJECT_ROOT"
|
|
207
|
+
git worktree list
|
|
208
|
+
echo ""
|
|
209
|
+
|
|
210
|
+
echo -e "${BLUE}=== Registered Agents ===${NC}"
|
|
211
|
+
echo ""
|
|
212
|
+
|
|
213
|
+
AGENTS_DIR=$(get_agents_dir)
|
|
214
|
+
REGISTRY_FILE="${AGENTS_DIR}/registry.json"
|
|
215
|
+
|
|
216
|
+
if [ ! -f "$REGISTRY_FILE" ]; then
|
|
217
|
+
echo " (no registry found)"
|
|
218
|
+
return
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
local agents=$(jq -r '.agents[]' "$REGISTRY_FILE" 2>/dev/null)
|
|
222
|
+
if [ -z "$agents" ]; then
|
|
223
|
+
echo " (no agents registered)"
|
|
224
|
+
return
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
jq -r '.agents[] | "\(.id)|\(.pid)|\(.worktree_path)|\(.started_at)"' "$REGISTRY_FILE" 2>/dev/null | while IFS='|' read -r id pid wt started; do
|
|
228
|
+
local status_icon
|
|
229
|
+
if is_running "$pid"; then
|
|
230
|
+
status_icon="${GREEN}●${NC}"
|
|
231
|
+
else
|
|
232
|
+
status_icon="${RED}○${NC}"
|
|
233
|
+
fi
|
|
234
|
+
echo -e " $status_icon $id (PID: $pid)"
|
|
235
|
+
echo -e " ${DIM}Worktree: $wt${NC}"
|
|
236
|
+
echo -e " ${DIM}Started: $started${NC}"
|
|
237
|
+
echo ""
|
|
238
|
+
done
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# Calculate elapsed time from ISO timestamp
|
|
242
|
+
calc_elapsed() {
|
|
243
|
+
local started="$1"
|
|
244
|
+
if [ -z "$started" ] || [ "$started" = "null" ]; then
|
|
245
|
+
echo "N/A"
|
|
246
|
+
return
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
# Parse started time (handle both formats: with and without timezone)
|
|
250
|
+
local start_epoch
|
|
251
|
+
if command -v gdate &>/dev/null; then
|
|
252
|
+
start_epoch=$(gdate -d "$started" +%s 2>/dev/null)
|
|
253
|
+
else
|
|
254
|
+
# Try to parse ISO format
|
|
255
|
+
start_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%S" "${started%%+*}" +%s 2>/dev/null || date -d "$started" +%s 2>/dev/null)
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
if [ -z "$start_epoch" ]; then
|
|
259
|
+
echo "N/A"
|
|
260
|
+
return
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
local now_epoch=$(date +%s)
|
|
264
|
+
local elapsed=$((now_epoch - start_epoch))
|
|
265
|
+
|
|
266
|
+
if [ $elapsed -lt 60 ]; then
|
|
267
|
+
echo "${elapsed}s"
|
|
268
|
+
elif [ $elapsed -lt 3600 ]; then
|
|
269
|
+
echo "$((elapsed / 60))m $((elapsed % 60))s"
|
|
270
|
+
else
|
|
271
|
+
echo "$((elapsed / 3600))h $((elapsed % 3600 / 60))m"
|
|
272
|
+
fi
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
# Note: get_phase_info is now in common/phase.sh
|
|
276
|
+
|
|
277
|
+
# Count modified files in worktree
|
|
278
|
+
count_modified_files() {
|
|
279
|
+
local worktree="$1"
|
|
280
|
+
if [ -d "$worktree" ]; then
|
|
281
|
+
cd "$worktree" && git status --short 2>/dev/null | wc -l | tr -d ' '
|
|
282
|
+
else
|
|
283
|
+
echo "0"
|
|
284
|
+
fi
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
cmd_summary() {
|
|
288
|
+
ensure_developer
|
|
289
|
+
|
|
290
|
+
local features_dir=$(get_features_dir)
|
|
291
|
+
if [ ! -d "$features_dir" ]; then
|
|
292
|
+
echo "No features directory found"
|
|
293
|
+
exit 0
|
|
294
|
+
fi
|
|
295
|
+
|
|
296
|
+
AGENTS_DIR=$(get_agents_dir)
|
|
297
|
+
REGISTRY_FILE="${AGENTS_DIR}/registry.json"
|
|
298
|
+
|
|
299
|
+
# Count running agents
|
|
300
|
+
local running_count=0
|
|
301
|
+
local total_agents=0
|
|
302
|
+
if [ -f "$REGISTRY_FILE" ]; then
|
|
303
|
+
total_agents=$(jq -r '.agents | length' "$REGISTRY_FILE" 2>/dev/null || echo "0")
|
|
304
|
+
while read -r pid; do
|
|
305
|
+
is_running "$pid" && ((running_count++))
|
|
306
|
+
done < <(jq -r '.agents[].pid' "$REGISTRY_FILE" 2>/dev/null)
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
echo -e "${BLUE}=== Multi-Agent Status ===${NC}"
|
|
310
|
+
echo -e " Agents: ${GREEN}${running_count}${NC} running / ${total_agents} registered"
|
|
311
|
+
echo ""
|
|
312
|
+
|
|
313
|
+
# Check if any agents are running and show detailed view
|
|
314
|
+
local has_running_agent=false
|
|
315
|
+
|
|
316
|
+
for d in "$features_dir"/*/; do
|
|
317
|
+
[ ! -d "$d" ] && continue
|
|
318
|
+
[[ "$(basename "$d")" == "archive" ]] && continue
|
|
319
|
+
|
|
320
|
+
local name=$(basename "$d")
|
|
321
|
+
local feature_json="$d/feature.json"
|
|
322
|
+
local status="unknown"
|
|
323
|
+
|
|
324
|
+
if [ -f "$feature_json" ]; then
|
|
325
|
+
status=$(jq -r '.status // "unknown"' "$feature_json")
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
# Check agent status
|
|
329
|
+
local agent_info=""
|
|
330
|
+
local pid=""
|
|
331
|
+
local worktree=""
|
|
332
|
+
local started=""
|
|
333
|
+
local is_agent_running=false
|
|
334
|
+
|
|
335
|
+
if [ -f "$REGISTRY_FILE" ]; then
|
|
336
|
+
agent_info=$(jq -r --arg name "$name" '.agents[] | select(.feature_dir | contains($name))' "$REGISTRY_FILE" 2>/dev/null)
|
|
337
|
+
if [ -n "$agent_info" ] && [ "$agent_info" != "null" ]; then
|
|
338
|
+
pid=$(echo "$agent_info" | jq -r '.pid')
|
|
339
|
+
worktree=$(echo "$agent_info" | jq -r '.worktree_path')
|
|
340
|
+
started=$(echo "$agent_info" | jq -r '.started_at')
|
|
341
|
+
if is_running "$pid"; then
|
|
342
|
+
is_agent_running=true
|
|
343
|
+
has_running_agent=true
|
|
344
|
+
fi
|
|
345
|
+
fi
|
|
346
|
+
fi
|
|
347
|
+
|
|
348
|
+
local color=$(status_color "$status")
|
|
349
|
+
|
|
350
|
+
if [ "$is_agent_running" = true ]; then
|
|
351
|
+
# Detailed view for running agents
|
|
352
|
+
# Read feature.json from worktree (has live phase info)
|
|
353
|
+
local feature_dir_rel=$(echo "$agent_info" | jq -r '.feature_dir')
|
|
354
|
+
local worktree_feature_json="$worktree/$feature_dir_rel/feature.json"
|
|
355
|
+
local phase_source="$feature_json"
|
|
356
|
+
[ -f "$worktree_feature_json" ] && phase_source="$worktree_feature_json"
|
|
357
|
+
|
|
358
|
+
local phase_info=$(get_phase_info "$phase_source")
|
|
359
|
+
local elapsed=$(calc_elapsed "$started")
|
|
360
|
+
local modified=$(count_modified_files "$worktree")
|
|
361
|
+
local branch=$(jq -r '.branch // "N/A"' "$phase_source" 2>/dev/null)
|
|
362
|
+
|
|
363
|
+
# Get recent activity from log
|
|
364
|
+
local log_file="$worktree/.agent-log"
|
|
365
|
+
local last_tool=$(get_last_tool "$log_file")
|
|
366
|
+
|
|
367
|
+
echo -e "${GREEN}▶${NC} ${CYAN}${name}${NC} ${GREEN}[running]${NC}"
|
|
368
|
+
echo -e " Phase: ${phase_info}"
|
|
369
|
+
echo -e " Elapsed: ${elapsed}"
|
|
370
|
+
echo -e " Branch: ${DIM}${branch}${NC}"
|
|
371
|
+
echo -e " Modified: ${modified} file(s)"
|
|
372
|
+
if [ -n "$last_tool" ]; then
|
|
373
|
+
echo -e " Activity: ${YELLOW}${last_tool}${NC}"
|
|
374
|
+
fi
|
|
375
|
+
echo -e " PID: ${DIM}${pid}${NC}"
|
|
376
|
+
echo ""
|
|
377
|
+
elif [ -n "$agent_info" ] && [ "$agent_info" != "null" ]; then
|
|
378
|
+
# Stopped agent
|
|
379
|
+
echo -e "${RED}○${NC} ${name} ${RED}[stopped]${NC}"
|
|
380
|
+
echo -e " ${DIM}PID ${pid} is no longer running${NC}"
|
|
381
|
+
echo ""
|
|
382
|
+
else
|
|
383
|
+
# No agent, just show status
|
|
384
|
+
echo -e " ${color}●${NC} ${name} (${status})"
|
|
385
|
+
fi
|
|
386
|
+
done
|
|
387
|
+
|
|
388
|
+
if [ "$has_running_agent" = true ]; then
|
|
389
|
+
echo -e "${DIM}─────────────────────────────────────${NC}"
|
|
390
|
+
echo -e "${DIM}Use --progress <name> for quick activity view${NC}"
|
|
391
|
+
echo -e "${DIM}Use --detail <name> for more info${NC}"
|
|
392
|
+
fi
|
|
393
|
+
echo ""
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
cmd_progress() {
|
|
397
|
+
if [ -z "$TARGET" ]; then
|
|
398
|
+
echo "Usage: $0 --progress <feature>"
|
|
399
|
+
exit 1
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
local agent=$(find_agent "$TARGET")
|
|
403
|
+
if [ -z "$agent" ] || [ "$agent" = "null" ]; then
|
|
404
|
+
echo "Agent not found: $TARGET"
|
|
405
|
+
exit 1
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
local id=$(echo "$agent" | jq -r '.id')
|
|
409
|
+
local pid=$(echo "$agent" | jq -r '.pid')
|
|
410
|
+
local worktree=$(echo "$agent" | jq -r '.worktree_path')
|
|
411
|
+
local feature_dir=$(echo "$agent" | jq -r '.feature_dir')
|
|
412
|
+
local started=$(echo "$agent" | jq -r '.started_at')
|
|
413
|
+
local log_file="$worktree/.agent-log"
|
|
414
|
+
|
|
415
|
+
if [ ! -f "$log_file" ]; then
|
|
416
|
+
echo "Log file not found: $log_file"
|
|
417
|
+
exit 1
|
|
418
|
+
fi
|
|
419
|
+
|
|
420
|
+
# Get phase info from worktree's feature.json
|
|
421
|
+
local worktree_feature_json="$worktree/$feature_dir/feature.json"
|
|
422
|
+
local phase_info="N/A"
|
|
423
|
+
local current_phase=0
|
|
424
|
+
if [ -f "$worktree_feature_json" ]; then
|
|
425
|
+
phase_info=$(get_phase_info "$worktree_feature_json")
|
|
426
|
+
current_phase=$(jq -r '.current_phase // 0' "$worktree_feature_json")
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
local elapsed=$(calc_elapsed "$started")
|
|
430
|
+
local modified=$(count_modified_files "$worktree")
|
|
431
|
+
|
|
432
|
+
# Check if running
|
|
433
|
+
local status_str
|
|
434
|
+
if is_running "$pid"; then
|
|
435
|
+
status_str="${GREEN}running${NC}"
|
|
436
|
+
else
|
|
437
|
+
status_str="${RED}stopped${NC}"
|
|
438
|
+
fi
|
|
439
|
+
|
|
440
|
+
echo ""
|
|
441
|
+
echo -e "${BLUE}=== Progress: ${id} ===${NC}"
|
|
442
|
+
echo ""
|
|
443
|
+
|
|
444
|
+
# Basic info (like summary)
|
|
445
|
+
echo -e "${CYAN}Status:${NC}"
|
|
446
|
+
echo -e " State: ${status_str}"
|
|
447
|
+
echo -e " Phase: ${phase_info}"
|
|
448
|
+
echo -e " Elapsed: ${elapsed}"
|
|
449
|
+
echo -e " Modified: ${modified} file(s)"
|
|
450
|
+
echo ""
|
|
451
|
+
|
|
452
|
+
# Recent task notifications
|
|
453
|
+
echo -e "${CYAN}Recent Tasks:${NC}"
|
|
454
|
+
local has_tasks=false
|
|
455
|
+
while IFS='|' read -r status summary; do
|
|
456
|
+
[ -z "$status" ] && continue
|
|
457
|
+
has_tasks=true
|
|
458
|
+
local icon
|
|
459
|
+
case "$status" in
|
|
460
|
+
completed) icon="${GREEN}✓${NC}" ;;
|
|
461
|
+
failed) icon="${RED}✗${NC}" ;;
|
|
462
|
+
async_launched) icon="${BLUE}▶${NC}" ;;
|
|
463
|
+
*) icon="${YELLOW}○${NC}" ;;
|
|
464
|
+
esac
|
|
465
|
+
echo -e " ${icon} ${summary}"
|
|
466
|
+
done < <(get_recent_tasks "$log_file" 5 "$current_phase")
|
|
467
|
+
|
|
468
|
+
if [ "$has_tasks" = false ]; then
|
|
469
|
+
echo -e " ${DIM}(no task notifications yet)${NC}"
|
|
470
|
+
fi
|
|
471
|
+
echo ""
|
|
472
|
+
|
|
473
|
+
# Current activity
|
|
474
|
+
echo -e "${CYAN}Current Activity:${NC}"
|
|
475
|
+
local last_tool=$(get_last_tool "$log_file")
|
|
476
|
+
if [ -n "$last_tool" ]; then
|
|
477
|
+
echo -e " Tool: ${YELLOW}${last_tool}${NC}"
|
|
478
|
+
else
|
|
479
|
+
echo -e " ${DIM}(no recent tool calls)${NC}"
|
|
480
|
+
fi
|
|
481
|
+
echo ""
|
|
482
|
+
|
|
483
|
+
# Last message
|
|
484
|
+
echo -e "${CYAN}Last Message:${NC}"
|
|
485
|
+
local last_msg=$(get_last_message "$log_file" 200)
|
|
486
|
+
if [ -n "$last_msg" ]; then
|
|
487
|
+
echo -e " \"${last_msg}...\""
|
|
488
|
+
else
|
|
489
|
+
echo -e " ${DIM}(no recent messages)${NC}"
|
|
490
|
+
fi
|
|
491
|
+
echo ""
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
cmd_detail() {
|
|
495
|
+
if [ -z "$TARGET" ]; then
|
|
496
|
+
echo "Usage: $0 --detail <feature>"
|
|
497
|
+
exit 1
|
|
498
|
+
fi
|
|
499
|
+
|
|
500
|
+
local agent=$(find_agent "$TARGET")
|
|
501
|
+
if [ -z "$agent" ] || [ "$agent" = "null" ]; then
|
|
502
|
+
echo "Agent not found: $TARGET"
|
|
503
|
+
exit 1
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
local id=$(echo "$agent" | jq -r '.id')
|
|
507
|
+
local pid=$(echo "$agent" | jq -r '.pid')
|
|
508
|
+
local worktree=$(echo "$agent" | jq -r '.worktree_path')
|
|
509
|
+
local feature_dir=$(echo "$agent" | jq -r '.feature_dir')
|
|
510
|
+
local started=$(echo "$agent" | jq -r '.started_at')
|
|
511
|
+
|
|
512
|
+
echo -e "${BLUE}=== Agent Detail: $id ===${NC}"
|
|
513
|
+
echo ""
|
|
514
|
+
echo " ID: $id"
|
|
515
|
+
echo " PID: $pid"
|
|
516
|
+
echo " Worktree: $worktree"
|
|
517
|
+
echo " Feature Dir: $feature_dir"
|
|
518
|
+
echo " Started: $started"
|
|
519
|
+
echo ""
|
|
520
|
+
|
|
521
|
+
# Status
|
|
522
|
+
if is_running "$pid"; then
|
|
523
|
+
echo -e " Status: ${GREEN}Running${NC}"
|
|
524
|
+
else
|
|
525
|
+
echo -e " Status: ${RED}Stopped${NC}"
|
|
526
|
+
fi
|
|
527
|
+
|
|
528
|
+
# Feature info
|
|
529
|
+
local feature_json="$PROJECT_ROOT/$feature_dir/feature.json"
|
|
530
|
+
if [ -f "$feature_json" ]; then
|
|
531
|
+
echo ""
|
|
532
|
+
echo -e "${BLUE}=== Feature Info ===${NC}"
|
|
533
|
+
echo ""
|
|
534
|
+
local status=$(jq -r '.status // "unknown"' "$feature_json")
|
|
535
|
+
local branch=$(jq -r '.branch // "N/A"' "$feature_json")
|
|
536
|
+
local base=$(jq -r '.base_branch // "N/A"' "$feature_json")
|
|
537
|
+
echo " Status: $status"
|
|
538
|
+
echo " Branch: $branch"
|
|
539
|
+
echo " Base Branch: $base"
|
|
540
|
+
fi
|
|
541
|
+
|
|
542
|
+
# Git changes
|
|
543
|
+
if [ -d "$worktree" ]; then
|
|
544
|
+
echo ""
|
|
545
|
+
echo -e "${BLUE}=== Git Changes ===${NC}"
|
|
546
|
+
echo ""
|
|
547
|
+
cd "$worktree"
|
|
548
|
+
local changes=$(git status --short 2>/dev/null | head -10)
|
|
549
|
+
if [ -n "$changes" ]; then
|
|
550
|
+
echo "$changes" | sed 's/^/ /'
|
|
551
|
+
local total=$(git status --short 2>/dev/null | wc -l | tr -d ' ')
|
|
552
|
+
if [ "$total" -gt 10 ]; then
|
|
553
|
+
echo " ... and $((total - 10)) more"
|
|
554
|
+
fi
|
|
555
|
+
else
|
|
556
|
+
echo " (no changes)"
|
|
557
|
+
fi
|
|
558
|
+
fi
|
|
559
|
+
|
|
560
|
+
echo ""
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
cmd_watch() {
|
|
564
|
+
if [ -z "$TARGET" ]; then
|
|
565
|
+
echo "Usage: $0 --watch <feature>"
|
|
566
|
+
exit 1
|
|
567
|
+
fi
|
|
568
|
+
|
|
569
|
+
local agent=$(find_agent "$TARGET")
|
|
570
|
+
if [ -z "$agent" ] || [ "$agent" = "null" ]; then
|
|
571
|
+
echo "Agent not found: $TARGET"
|
|
572
|
+
exit 1
|
|
573
|
+
fi
|
|
574
|
+
|
|
575
|
+
local worktree=$(echo "$agent" | jq -r '.worktree_path')
|
|
576
|
+
local log_file="$worktree/.agent-log"
|
|
577
|
+
|
|
578
|
+
if [ ! -f "$log_file" ]; then
|
|
579
|
+
echo "Log file not found: $log_file"
|
|
580
|
+
exit 1
|
|
581
|
+
fi
|
|
582
|
+
|
|
583
|
+
echo -e "${BLUE}Watching:${NC} $log_file"
|
|
584
|
+
echo -e "${DIM}Press Ctrl+C to stop${NC}"
|
|
585
|
+
echo ""
|
|
586
|
+
|
|
587
|
+
tail -f "$log_file"
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
cmd_log() {
|
|
591
|
+
if [ -z "$TARGET" ]; then
|
|
592
|
+
echo "Usage: $0 --log <feature>"
|
|
593
|
+
exit 1
|
|
594
|
+
fi
|
|
595
|
+
|
|
596
|
+
local agent=$(find_agent "$TARGET")
|
|
597
|
+
if [ -z "$agent" ] || [ "$agent" = "null" ]; then
|
|
598
|
+
echo "Agent not found: $TARGET"
|
|
599
|
+
exit 1
|
|
600
|
+
fi
|
|
601
|
+
|
|
602
|
+
local worktree=$(echo "$agent" | jq -r '.worktree_path')
|
|
603
|
+
local log_file="$worktree/.agent-log"
|
|
604
|
+
|
|
605
|
+
if [ ! -f "$log_file" ]; then
|
|
606
|
+
echo "Log file not found: $log_file"
|
|
607
|
+
exit 1
|
|
608
|
+
fi
|
|
609
|
+
|
|
610
|
+
echo -e "${BLUE}=== Recent Log: $TARGET ===${NC}"
|
|
611
|
+
echo ""
|
|
612
|
+
|
|
613
|
+
# Parse and format JSON log entries
|
|
614
|
+
tail -50 "$log_file" | while IFS= read -r line; do
|
|
615
|
+
local type=$(echo "$line" | jq -r '.type // empty' 2>/dev/null)
|
|
616
|
+
[ -z "$type" ] && continue
|
|
617
|
+
|
|
618
|
+
case "$type" in
|
|
619
|
+
system)
|
|
620
|
+
local subtype=$(echo "$line" | jq -r '.subtype // ""' 2>/dev/null)
|
|
621
|
+
echo -e "${CYAN}[SYSTEM]${NC} $subtype"
|
|
622
|
+
;;
|
|
623
|
+
user)
|
|
624
|
+
local content=$(echo "$line" | jq -r '.message.content // empty' 2>/dev/null)
|
|
625
|
+
if [ -n "$content" ] && [ "$content" != "null" ]; then
|
|
626
|
+
echo -e "${GREEN}[USER]${NC} ${content:0:200}"
|
|
627
|
+
fi
|
|
628
|
+
;;
|
|
629
|
+
assistant)
|
|
630
|
+
# Extract text or tool use
|
|
631
|
+
local text=$(echo "$line" | jq -r '.message.content[0].text // empty' 2>/dev/null)
|
|
632
|
+
local tool=$(echo "$line" | jq -r '.message.content[0].name // empty' 2>/dev/null)
|
|
633
|
+
|
|
634
|
+
if [ -n "$text" ] && [ "$text" != "null" ]; then
|
|
635
|
+
# Truncate long text
|
|
636
|
+
local display="${text:0:300}"
|
|
637
|
+
[ ${#text} -gt 300 ] && display="$display..."
|
|
638
|
+
echo -e "${BLUE}[ASSISTANT]${NC} $display"
|
|
639
|
+
elif [ -n "$tool" ] && [ "$tool" != "null" ]; then
|
|
640
|
+
echo -e "${YELLOW}[TOOL]${NC} $tool"
|
|
641
|
+
fi
|
|
642
|
+
;;
|
|
643
|
+
result)
|
|
644
|
+
local tool_name=$(echo "$line" | jq -r '.tool // "unknown"' 2>/dev/null)
|
|
645
|
+
echo -e "${DIM}[RESULT]${NC} $tool_name completed"
|
|
646
|
+
;;
|
|
647
|
+
esac
|
|
648
|
+
done
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
cmd_registry() {
|
|
652
|
+
AGENTS_DIR=$(get_agents_dir)
|
|
653
|
+
REGISTRY_FILE="${AGENTS_DIR}/registry.json"
|
|
654
|
+
|
|
655
|
+
echo -e "${BLUE}=== Agent Registry ===${NC}"
|
|
656
|
+
echo ""
|
|
657
|
+
echo "File: $REGISTRY_FILE"
|
|
658
|
+
echo ""
|
|
659
|
+
|
|
660
|
+
if [ -f "$REGISTRY_FILE" ]; then
|
|
661
|
+
jq '.' "$REGISTRY_FILE"
|
|
662
|
+
else
|
|
663
|
+
echo "(registry not found)"
|
|
664
|
+
fi
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
# =============================================================================
|
|
668
|
+
# Main
|
|
669
|
+
# =============================================================================
|
|
670
|
+
case "$ACTION" in
|
|
671
|
+
help)
|
|
672
|
+
cmd_help
|
|
673
|
+
;;
|
|
674
|
+
list)
|
|
675
|
+
cmd_list
|
|
676
|
+
;;
|
|
677
|
+
summary)
|
|
678
|
+
cmd_summary
|
|
679
|
+
;;
|
|
680
|
+
progress)
|
|
681
|
+
cmd_progress
|
|
682
|
+
;;
|
|
683
|
+
detail)
|
|
684
|
+
cmd_detail
|
|
685
|
+
;;
|
|
686
|
+
watch)
|
|
687
|
+
cmd_watch
|
|
688
|
+
;;
|
|
689
|
+
log)
|
|
690
|
+
cmd_log
|
|
691
|
+
;;
|
|
692
|
+
registry)
|
|
693
|
+
cmd_registry
|
|
694
|
+
;;
|
|
695
|
+
esac
|