@pmelab/gtd 8.0.0 → 8.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.
- package/README.md +490 -110
- package/bin/gtd +104 -20
- package/dist/gtd.bundle.mjs +214 -102
- package/package.json +1 -2
- package/schema.json +33 -9
- package/docs/cli.md +0 -675
- package/docs/configuration.md +0 -994
- package/docs/design/configurable-state-machine.md +0 -1439
- package/docs/design/delta-purity-plan.md +0 -283
- package/docs/design/pattern-machine-plan.md +0 -164
- package/docs/design/pluggable-steering-modes.md +0 -154
- package/docs/design/scripted-actor.md +0 -289
- package/docs/design/state-file-association.md +0 -120
- package/docs/design/state-machine-validation.md +0 -437
- package/docs/design/steering-file-loops.md +0 -182
- package/docs/design/steering-file-validation-command.md +0 -385
- package/docs/design/work-packages.md +0 -193
- package/docs/development.md +0 -109
- package/docs/loop.md +0 -219
- package/docs/upgrading.md +0 -293
package/bin/gtd
CHANGED
|
@@ -44,12 +44,12 @@ if [[ "$FANCY" == 1 ]]; then
|
|
|
44
44
|
C_RESET=$'\e[0m'; C_BOLD=$'\e[1m'; C_DIM=$'\e[2m'
|
|
45
45
|
C_CYAN=$'\e[36m'; C_GREEN=$'\e[32m'; C_YELLOW=$'\e[33m'; C_RED=$'\e[31m'
|
|
46
46
|
M_TRANS="➡️"; M_AGENT="🤖"; M_CHECK="🔧"; M_HUMAN="✍️"
|
|
47
|
-
M_FIX="🔁"; M_COMMIT="✅"; M_SETTLE="🎉"; M_ERROR="❌"; M_ELLIPSIS="…"
|
|
47
|
+
M_FIX="🔁"; M_COMMIT="✅"; M_SETTLE="🎉"; M_ERROR="❌"; M_WARN="⚠️"; M_ELLIPSIS="…"
|
|
48
48
|
else
|
|
49
49
|
C_RESET=""; C_BOLD=""; C_DIM=""
|
|
50
50
|
C_CYAN=""; C_GREEN=""; C_YELLOW=""; C_RED=""
|
|
51
51
|
M_TRANS="->"; M_AGENT="[agent]"; M_CHECK="[check]"; M_HUMAN="[you]"
|
|
52
|
-
M_FIX="[fix]"; M_COMMIT="[commit]"; M_SETTLE="[done]"; M_ERROR="[err]"; M_ELLIPSIS="..."
|
|
52
|
+
M_FIX="[fix]"; M_COMMIT="[commit]"; M_SETTLE="[done]"; M_ERROR="[err]"; M_WARN="[warn]"; M_ELLIPSIS="..."
|
|
53
53
|
fi
|
|
54
54
|
|
|
55
55
|
# emit_file_rows <files> — render a commit's changed files as dim, indented
|
|
@@ -114,8 +114,10 @@ emit_error() {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
# emit_log_pointer — the "see the log" line printed right after every
|
|
117
|
-
# emit_error call, to stderr:
|
|
118
|
-
#
|
|
117
|
+
# emit_error call, to stderr: it names the log as the OVERFLOW channel — the
|
|
118
|
+
# failing execution's own output is already replayed inline (see emit_block/
|
|
119
|
+
# emit_output_since below); the log is where the rest of it, and the full
|
|
120
|
+
# run's other output, lives.
|
|
119
121
|
emit_log_pointer() {
|
|
120
122
|
if [[ "$FANCY" == 1 ]]; then
|
|
121
123
|
printf '%s📄 see %s%s\n' "$C_DIM" "$GTD_LOOP_LOG" "$C_RESET" >&2
|
|
@@ -124,6 +126,58 @@ emit_log_pointer() {
|
|
|
124
126
|
fi
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
# Suppressed subprocess output goes to $GTD_LOOP_LOG in full, but a FAILING
|
|
130
|
+
# execution also replays inline: mark the log's size before the call, print
|
|
131
|
+
# what was appended since. Bounded to the last $ERR_TAIL_LINES lines — the log
|
|
132
|
+
# holds the rest, and the overflow row says where.
|
|
133
|
+
ERR_TAIL_LINES=20
|
|
134
|
+
|
|
135
|
+
# log_offset — the log's current size in bytes (0 when it doesn't exist yet).
|
|
136
|
+
log_offset() {
|
|
137
|
+
local n=0
|
|
138
|
+
if [[ -n "${GTD_LOOP_LOG:-}" && -f "$GTD_LOOP_LOG" ]]; then n="$(wc -c <"$GTD_LOOP_LOG")"; fi
|
|
139
|
+
printf '%s' "${n// /}"
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# emit_block <text> — replay captured subprocess output to stderr, dim and
|
|
143
|
+
# indented like emit_file_rows, capped at the LAST $ERR_TAIL_LINES lines with
|
|
144
|
+
# an overflow row naming the log. Empty <text> prints nothing. CSI escapes are
|
|
145
|
+
# stripped under FANCY=0 (the plain-ASCII promise), using a sed form (\e, not
|
|
146
|
+
# \x1b) that both BSD and GNU sed accept.
|
|
147
|
+
emit_block() {
|
|
148
|
+
local text="$1" total overflow
|
|
149
|
+
[[ -z "$text" ]] && return 0
|
|
150
|
+
if [[ "$FANCY" == 0 ]]; then
|
|
151
|
+
text="$(sed $'s/\e\\[[0-9;]*[a-zA-Z]//g' <<<"$text")"
|
|
152
|
+
fi
|
|
153
|
+
total="$(wc -l <<<"$text")"
|
|
154
|
+
total="${total// /}"
|
|
155
|
+
if ((total > ERR_TAIL_LINES)); then
|
|
156
|
+
overflow=$((total - ERR_TAIL_LINES))
|
|
157
|
+
printf ' %s%s (%d earlier lines in %s)%s\n' "$C_DIM" "$M_ELLIPSIS" "$overflow" "$GTD_LOOP_LOG" "$C_RESET" >&2
|
|
158
|
+
text="$(tail -n "$ERR_TAIL_LINES" <<<"$text")"
|
|
159
|
+
fi
|
|
160
|
+
while IFS= read -r line; do
|
|
161
|
+
printf ' %s%s%s\n' "$C_DIM" "$line" "$C_RESET" >&2
|
|
162
|
+
done <<<"$text"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# emit_output_since <offset> — emit_block over the log bytes appended since
|
|
166
|
+
# <offset>.
|
|
167
|
+
emit_output_since() {
|
|
168
|
+
local offset="$1" bytes=""
|
|
169
|
+
if [[ -n "${GTD_LOOP_LOG:-}" && -f "$GTD_LOOP_LOG" ]]; then
|
|
170
|
+
bytes="$(tail -c "+$((offset + 1))" "$GTD_LOOP_LOG" 2>/dev/null || true)"
|
|
171
|
+
fi
|
|
172
|
+
emit_block "$bytes"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# emit_warn <message> — a surfaced but NON-fatal failure (a check script that
|
|
176
|
+
# exited non-zero).
|
|
177
|
+
emit_warn() {
|
|
178
|
+
printf '%s %s%s%s\n' "$M_WARN" "$C_YELLOW" "$1" "$C_RESET" >&2
|
|
179
|
+
}
|
|
180
|
+
|
|
127
181
|
# ── Git-derivation helpers (parse_subject, report_commits) ──────────────────
|
|
128
182
|
|
|
129
183
|
# parse_subject <subject> — sets S_ACTOR / S_FROM / S_TO / S_KIND. Patterns are
|
|
@@ -247,7 +301,7 @@ fi
|
|
|
247
301
|
# otherwise ($# -eq 0, or exactly "loop" with no further args, --once already
|
|
248
302
|
# stripped above): fall through into the loop body below.
|
|
249
303
|
|
|
250
|
-
# Drives the v3 pattern-machine loop
|
|
304
|
+
# Drives the v3 pattern-machine loop: each iteration asks
|
|
251
305
|
# `gtd next --json` who's up and what they should do, dispatching on `kind`:
|
|
252
306
|
# - "message" (a human rest) -> the non-autonomous halt: print the gate and
|
|
253
307
|
# exit 0. Acting on it (opening an editor, notifying, whatever) is an
|
|
@@ -264,8 +318,7 @@ fi
|
|
|
264
318
|
# adapter invoked at a "prompt" rest; the default adapter appends `--model
|
|
265
319
|
# "$GTD_LOOP_MODEL"` only when it's non-empty.
|
|
266
320
|
#
|
|
267
|
-
# `.memory` (optional, opaque
|
|
268
|
-
# is the agent-memory scope label. gtd never interprets it; the driver does:
|
|
321
|
+
# `.memory` (optional, opaque) is the agent-memory scope label. gtd never interprets it; the driver does:
|
|
269
322
|
# consecutive "prompt" turns emitting the SAME label continue ONE agent
|
|
270
323
|
# session (so the agent still remembers its previous turn), and a changed
|
|
271
324
|
# label — or the first turn, or an unset label — starts fresh. That is what
|
|
@@ -376,9 +429,10 @@ fi
|
|
|
376
429
|
|
|
377
430
|
while true; do
|
|
378
431
|
if ! next_json="$(run_gtd next --json 2>&1)"; then
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
432
|
+
emit_error "could not determine the next step:
|
|
433
|
+
$next_json
|
|
434
|
+
Run \`gtd status\` to inspect the repo."
|
|
435
|
+
emit_log_pointer
|
|
382
436
|
exit 1
|
|
383
437
|
fi
|
|
384
438
|
|
|
@@ -395,17 +449,29 @@ while true; do
|
|
|
395
449
|
# (or an outer wrapper) edits and re-launches; the opening move above
|
|
396
450
|
# captures what they left.
|
|
397
451
|
emit_gate "$state" "$label"
|
|
398
|
-
run_gtd next
|
|
452
|
+
if ! gate_out="$(run_gtd next 2>&1)"; then
|
|
453
|
+
emit_error "$gate_out"
|
|
454
|
+
emit_log_pointer
|
|
455
|
+
exit 1
|
|
456
|
+
fi
|
|
457
|
+
printf '%s\n' "$gate_out"
|
|
399
458
|
exit 0
|
|
400
459
|
fi
|
|
401
460
|
|
|
402
461
|
if [[ "$kind" == "script" ]]; then
|
|
403
462
|
emit_action "$M_CHECK" "$label"
|
|
404
|
-
# Execute the emitted wrapper verbatim; its exit code
|
|
405
|
-
#
|
|
406
|
-
#
|
|
407
|
-
#
|
|
408
|
-
|
|
463
|
+
# Execute the emitted wrapper verbatim; its exit code decides nothing
|
|
464
|
+
# (the script encodes its outcome in the tree, and `gtd step` captures
|
|
465
|
+
# whatever it left there via the state's own `on` patterns) but a non-zero
|
|
466
|
+
# exit is surfaced as a warning — its own output goes to the log, and to
|
|
467
|
+
# the terminal too when it exits non-zero.
|
|
468
|
+
script_offset="$(log_offset)"
|
|
469
|
+
script_status=0
|
|
470
|
+
bash -c "$content" >>"$GTD_LOOP_LOG" 2>&1 || script_status=$?
|
|
471
|
+
if ((script_status != 0)); then
|
|
472
|
+
emit_warn "the check script at '$state' exited $script_status — continuing; the outcome is whatever it left in the tree:"
|
|
473
|
+
emit_output_since "$script_offset"
|
|
474
|
+
fi
|
|
409
475
|
if ! step_out="$(run_gtd step "$actor" 2>&1)"; then
|
|
410
476
|
printf '%s\n' "$step_out" >>"$GTD_LOOP_LOG"
|
|
411
477
|
emit_error "$step_out"
|
|
@@ -425,7 +491,7 @@ while true; do
|
|
|
425
491
|
continue
|
|
426
492
|
fi
|
|
427
493
|
|
|
428
|
-
# kind == "prompt": stall detection
|
|
494
|
+
# kind == "prompt": stall detection — the same
|
|
429
495
|
# agent-owned state and content repeating, WITH NO COMMIT authored since the
|
|
430
496
|
# last time we saw this beat, means the last dispatch made no progress; stop
|
|
431
497
|
# rather than spin on it. The HEAD check is what distinguishes a genuine
|
|
@@ -479,24 +545,42 @@ $next_out"
|
|
|
479
545
|
# driver-side equivalent, so the next rest is only ever handed a well-formed
|
|
480
546
|
# file. gtd itself never validates at step time — this is the loop's job.
|
|
481
547
|
emit_action "$M_AGENT" "$label"
|
|
482
|
-
|
|
548
|
+
turn_offset="$(log_offset)"
|
|
549
|
+
agent_status=0
|
|
550
|
+
run_agent_turn "$content" "$resume" || agent_status=$?
|
|
551
|
+
if ((agent_status != 0)); then
|
|
552
|
+
emit_error "the agent turn at '$state' failed (exit $agent_status):"
|
|
553
|
+
emit_output_since "$turn_offset"
|
|
554
|
+
emit_log_pointer
|
|
555
|
+
exit 1
|
|
556
|
+
fi
|
|
483
557
|
|
|
484
558
|
fix_attempt=0
|
|
485
559
|
while ! validate_out="$(run_gtd validate 2>&1)"; do
|
|
486
560
|
fix_attempt=$((fix_attempt + 1))
|
|
487
561
|
if ((fix_attempt > 3)); then
|
|
562
|
+
printf '%s\n' "$validate_out" >>"$GTD_LOOP_LOG"
|
|
488
563
|
emit_error "'$state' still fails \`gtd validate\` after 3 fix attempts:
|
|
489
564
|
$validate_out
|
|
490
565
|
Stopping rather than stepping with a malformed steering file."
|
|
491
566
|
emit_log_pointer
|
|
492
567
|
exit 1
|
|
493
568
|
fi
|
|
569
|
+
printf '%s\n' "$validate_out" >>"$GTD_LOOP_LOG"
|
|
570
|
+
emit_action "$M_FIX" "attempt $fix_attempt"
|
|
494
571
|
# Resume the same session when there is one (the agent just wrote the file);
|
|
495
572
|
# otherwise a fresh invocation still gets the findings via the prompt.
|
|
496
573
|
fix_resume=0
|
|
497
574
|
[[ -n "$session_id" ]] && fix_resume=1
|
|
498
|
-
|
|
499
|
-
|
|
575
|
+
turn_offset="$(log_offset)"
|
|
576
|
+
agent_status=0
|
|
577
|
+
run_agent_turn "$content"$'\n\n'"Your last turn does not pass \`gtd validate\`. Fix these format violations, then finish:"$'\n'"$validate_out" "$fix_resume" || agent_status=$?
|
|
578
|
+
if ((agent_status != 0)); then
|
|
579
|
+
emit_error "the agent's fix attempt $fix_attempt at '$state' failed (exit $agent_status):"
|
|
580
|
+
emit_output_since "$turn_offset"
|
|
581
|
+
emit_log_pointer
|
|
582
|
+
exit 1
|
|
583
|
+
fi
|
|
500
584
|
done
|
|
501
585
|
|
|
502
586
|
if ! step_out="$(run_gtd step "$actor" 2>&1)"; then
|