@pmelab/gtd 7.5.0 → 7.6.1

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 CHANGED
@@ -181,7 +181,11 @@ code), waits for you to save and exit, then captures your edit itself and keeps
181
181
  driving — or halts if you left nothing changed — so you never run
182
182
  `gtd step human` by hand. Pass `--no-edit` (or set `GTD_NO_EDIT`) to fall back
183
183
  to halting and printing the gate instead, if you'd rather edit and re-launch it
184
- yourself. See [Driving the loop](docs/loop.md).
184
+ yourself. Bare `gtd` prints one line per event — colored and emoji on a real
185
+ terminal, plain ASCII under `NO_COLOR` or when piped — and redirects the noisier
186
+ agent/check/step subprocess output to a per-repo/per-worktree log file.
187
+ `gtd log` opens that logfile in your editor. See
188
+ [Driving the loop](docs/loop.md).
185
189
 
186
190
  Before wiring gtd into a repo, note the
187
191
  [repository requirements](docs/cli.md#repository-requirements) — most
package/bin/gtd CHANGED
@@ -10,6 +10,10 @@ set -euo pipefail
10
10
  # - `edit [path]` -> handled entirely here in bash, never forwarded to the
11
11
  # bundle: opens `${VISUAL:-$EDITOR}` on `path`, or (no `path` given) on the
12
12
  # resolved rest's `.file` (or `.` when absent).
13
+ # - `log` -> handled entirely here in bash, never forwarded to the bundle:
14
+ # opens `${VISUAL:-$EDITOR}` on the current repo/worktree's loop logfile
15
+ # (see `resolve_log_path`), refusing rather than opening an empty buffer
16
+ # when no loop has produced one yet.
13
17
  # - no arguments, or exactly `loop` with no further arguments -> fall through
14
18
  # into the loop body below (the loop protocol comment further down).
15
19
  # - `loop` with additional arguments (other than a lone `--no-edit`) ->
@@ -51,6 +55,157 @@ launch_editor() {
51
55
  bash -c "$editor \"\$1\"" gtd-edit "$target"
52
56
  }
53
57
 
58
+ # ── Rendering primitives (capability detection, palette/markers, emitters) ──
59
+ # Additive: nothing below calls these yet. `FANCY=1` only when stdout (fd 1)
60
+ # is a real terminal AND `NO_COLOR` is unset — any pipe/redirect/non-tty
61
+ # stdout, or `NO_COLOR` set to any value, gives plain ASCII with no escape
62
+ # codes, per the NO_COLOR convention (https://no-color.org).
63
+ if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then FANCY=1; else FANCY=0; fi
64
+
65
+ if [[ "$FANCY" == 1 ]]; then
66
+ C_RESET=$'\e[0m'; C_BOLD=$'\e[1m'; C_DIM=$'\e[2m'
67
+ C_CYAN=$'\e[36m'; C_GREEN=$'\e[32m'; C_YELLOW=$'\e[33m'; C_RED=$'\e[31m'
68
+ M_TRANS="➡️"; M_AGENT="🤖"; M_CHECK="🔧"; M_HUMAN="✍️"
69
+ M_FIX="🔁"; M_COMMIT="✅"; M_SETTLE="🎉"; M_ERROR="❌"
70
+ else
71
+ C_RESET=""; C_BOLD=""; C_DIM=""
72
+ C_CYAN=""; C_GREEN=""; C_YELLOW=""; C_RED=""
73
+ M_TRANS="->"; M_AGENT="[agent]"; M_CHECK="[check]"; M_HUMAN="[you]"
74
+ M_FIX="[fix]"; M_COMMIT="[commit]"; M_SETTLE="[done]"; M_ERROR="[err]"
75
+ fi
76
+
77
+ # emit_transition <from> <to> <files> — a captured state-to-state move. The
78
+ # "→" separator is literal (not FANCY-dependent) — the same separator the
79
+ # engine's commit grammar itself uses, per the package spec.
80
+ emit_transition() {
81
+ local from="$1" to="$2" files="$3"
82
+ if [[ -n "$files" ]]; then
83
+ printf '%s %s%s → %s%s (%s%s%s)\n' \
84
+ "$M_TRANS" "$C_BOLD$C_CYAN" "$from" "$to" "$C_RESET" \
85
+ "$C_DIM" "$files" "$C_RESET"
86
+ else
87
+ printf '%s %s%s → %s%s\n' \
88
+ "$M_TRANS" "$C_BOLD$C_CYAN" "$from" "$to" "$C_RESET"
89
+ fi
90
+ }
91
+
92
+ # emit_action <marker> <label> — the heartbeat printed before a turn runs.
93
+ emit_action() {
94
+ local marker="$1" label="$2"
95
+ printf '%s %s%s\n' "$marker" "$label" "$C_RESET"
96
+ }
97
+
98
+ # emit_commit <subject> <files> — a non-transition capture (a bare-subject
99
+ # self-loop, or the squash's agent-authored final commit).
100
+ emit_commit() {
101
+ local subject="$1" files="$2"
102
+ if [[ -n "$files" ]]; then
103
+ printf '%s %s%s%s (%s%s%s)\n' "$M_COMMIT" "$C_GREEN" "$subject" "$C_RESET" \
104
+ "$C_DIM" "$files" "$C_RESET"
105
+ else
106
+ printf '%s %s%s%s\n' "$M_COMMIT" "$C_GREEN" "$subject" "$C_RESET"
107
+ fi
108
+ }
109
+
110
+ # emit_gate <state> <message> — the human-instruction line at a message rest.
111
+ emit_gate() {
112
+ local message="$2"
113
+ printf '%s %s%s%s\n' "$M_HUMAN" "$C_BOLD$C_YELLOW" "$message" "$C_RESET"
114
+ }
115
+
116
+ # emit_settled <state> — the terminal "nothing left to do" signal.
117
+ emit_settled() {
118
+ printf '%s %ssettled — nothing left to do%s\n' "$M_SETTLE" "$C_GREEN" "$C_RESET"
119
+ }
120
+
121
+ # emit_error <message> — refusals/stalls/failures, to stderr; <message> may be
122
+ # multi-line and is passed through verbatim.
123
+ emit_error() {
124
+ local message="$1"
125
+ printf '%s %s%s%s\n' "$M_ERROR" "$C_RED" "$message" "$C_RESET" >&2
126
+ }
127
+
128
+ # emit_log_pointer — the "see the log" line printed right after every
129
+ # emit_error call, to stderr: suppression must never hide a failure, so every
130
+ # error points at where the redirected subprocess output actually went.
131
+ emit_log_pointer() {
132
+ if [[ "$FANCY" == 1 ]]; then
133
+ printf '%s📄 see %s%s\n' "$C_DIM" "$GTD_LOOP_LOG" "$C_RESET" >&2
134
+ else
135
+ printf '%ssee %s%s\n' "$C_DIM" "$GTD_LOOP_LOG" "$C_RESET" >&2
136
+ fi
137
+ }
138
+
139
+ # ── Git-derivation helpers (parse_subject, report_commits) ──────────────────
140
+ # Additive: nothing below calls these yet. Replaces today's
141
+ # head_before/head_after string comparisons in a later change.
142
+
143
+ # parse_subject <subject> — sets S_ACTOR / S_FROM / S_TO / S_KIND. Patterns are
144
+ # held in variables (rather than inlined) so bash's [[ =~ ]] doesn't need the
145
+ # literal parens/spaces quoted inline.
146
+ parse_subject() {
147
+ local subject="$1"
148
+ local transition_re='^gtd\(([^)]+)\): (.+) → (.+)$'
149
+ local capture_re='^gtd\(([^)]+)\): (.+)$'
150
+ S_ACTOR="" S_FROM="" S_TO="" S_KIND=""
151
+ if [[ "$subject" =~ $transition_re ]]; then
152
+ S_KIND=transition
153
+ S_ACTOR="${BASH_REMATCH[1]}"
154
+ S_FROM="${BASH_REMATCH[2]}"
155
+ S_TO="${BASH_REMATCH[3]}"
156
+ elif [[ "$subject" =~ $capture_re ]]; then
157
+ S_KIND=capture
158
+ S_ACTOR="${BASH_REMATCH[1]}"
159
+ S_TO="${BASH_REMATCH[2]}"
160
+ else
161
+ S_KIND=squash
162
+ S_TO="$subject"
163
+ fi
164
+ }
165
+
166
+ # report_commits <head_before> — walks <head_before>..HEAD in order, emitting
167
+ # a transition/commit line per new commit; returns non-zero (having emitted
168
+ # nothing) when there were no new commits.
169
+ report_commits() {
170
+ local head_before="$1" sha subject files reported=0
171
+ while IFS= read -r sha; do
172
+ [[ -z "$sha" ]] && continue
173
+ reported=1
174
+ subject="$(git log -1 --format=%s "$sha")"
175
+ files="$(git diff-tree --no-commit-id --name-only -r "$sha" | paste -sd, - | sed 's/,/, /g')"
176
+ parse_subject "$subject"
177
+ case "$S_KIND" in
178
+ transition) emit_transition "$S_FROM" "$S_TO" "$files" ;;
179
+ capture | squash) emit_commit "$subject" "$files" ;;
180
+ esac
181
+ done < <(git rev-list --reverse "$head_before"..HEAD)
182
+ [[ "$reported" == 1 ]]
183
+ }
184
+
185
+ # ── Log file (resolve_log_path, gtd log) ────────────────────────────────────
186
+
187
+ # worktree_git_dir — the git dir of the working tree gtd is operating in,
188
+ # derived purely from the cwd. GIT_DIR/GIT_WORK_TREE are scrubbed for this one
189
+ # call so an inherited GIT_DIR (e.g. leaked from a parent git process, a hook,
190
+ # or another worktree's shell) can never point our per-worktree state at a
191
+ # DIFFERENT worktree — that would let two concurrently looping worktrees
192
+ # collide on one log/memory file. A linked worktree's git-dir is its own
193
+ # private .git/worktrees/<name>, so this is per-repository AND per-worktree.
194
+ worktree_git_dir() {
195
+ env -u GIT_DIR -u GIT_WORK_TREE -u GIT_COMMON_DIR -u GIT_INDEX_FILE \
196
+ git rev-parse --git-dir
197
+ }
198
+
199
+ # resolve_log_path — echoes the loop's logfile path: $GTD_LOOP_LOG verbatim if
200
+ # set, else "$(worktree_git_dir)/gtd-loop.log".
201
+ resolve_log_path() {
202
+ if [[ -n "${GTD_LOOP_LOG:-}" ]]; then
203
+ echo "$GTD_LOOP_LOG"
204
+ else
205
+ echo "$(worktree_git_dir)/gtd-loop.log"
206
+ fi
207
+ }
208
+
54
209
  # --no-edit / GTD_NO_EDIT govern only the loop's automatic editor launching at
55
210
  # human gates (see handle_human_gate further down) — `gtd edit` itself never
56
211
  # reads either. `--no-edit` is recognised in exactly the two sanctioned
@@ -82,6 +237,21 @@ if [[ "${1:-}" == "edit" ]]; then
82
237
  exit 0
83
238
  fi
84
239
 
240
+ if [[ "${1:-}" == "log" ]]; then
241
+ shift
242
+ if [[ $# -gt 0 ]]; then
243
+ echo "gtd log: takes no arguments" >&2
244
+ exit 2
245
+ fi
246
+ log_path="$(resolve_log_path)"
247
+ if [[ ! -f "$log_path" ]]; then
248
+ echo "gtd log: no loop log yet at $log_path" >&2
249
+ exit 1
250
+ fi
251
+ launch_editor "$log_path"
252
+ exit 0
253
+ fi
254
+
85
255
  if [[ $# -gt 0 && "$1" != "loop" ]]; then
86
256
  run_gtd "$@"
87
257
  exit $?
@@ -166,8 +336,8 @@ herdr_release() { # hand the pane back to Herdr's normal detection
166
336
  # opening move (when editing is on) and by every "message" rest the main loop
167
337
  # encounters. $1 is the rest's `next --json` blob.
168
338
  #
169
- # Editing OFF ($edit_disabled): today's behavior, unchanged — print the gate
170
- # and exit 0 without touching an editor.
339
+ # Editing OFF ($edit_disabled): today's behavior, unchanged — render the gate
340
+ # via emit_gate and exit 0 without touching an editor.
171
341
  #
172
342
  # Editing ON (the default): open the editor on the rest's `.file` (or `.` when
173
343
  # absent), wait for it to exit, then run `gtd step human`:
@@ -187,7 +357,7 @@ handle_human_gate() {
187
357
  if [[ "$edit_disabled" == 1 ]]; then
188
358
  herdr_report blocked "$label"
189
359
  herdr_notify "gtd needs you — $(basename "$PWD")" "$label"
190
- echo "--- Your turn ($state) ---"
360
+ emit_gate "$state" "$label"
191
361
  run_gtd next
192
362
  exit 0
193
363
  fi
@@ -198,19 +368,22 @@ handle_human_gate() {
198
368
 
199
369
  head_before="$(git rev-parse HEAD 2>/dev/null || echo none)"
200
370
  if ! step_out="$(run_gtd step human 2>&1)"; then
201
- echo "gtd-loop: could not capture your changes at the human gate:" >&2
202
- echo "$step_out" >&2
203
- echo "Fix the reported issue and re-run gtd-loop." >&2
371
+ emit_error "could not capture your changes at the human gate:
372
+ $step_out
373
+ Fix the reported issue and re-run gtd-loop."
374
+ emit_log_pointer
204
375
  exit 1
205
376
  fi
206
377
  head_after="$(git rev-parse HEAD 2>/dev/null || echo none)"
207
378
  if [[ "$head_before" == "$head_after" ]]; then
208
379
  herdr_report idle "$label"
209
380
  herdr_release
210
- echo "--- Done for now ($state: nothing captured) ---"
381
+ emit_action "$M_HUMAN" "done for now (nothing captured)"
211
382
  exit 0
212
383
  fi
213
- # else: a new commit was captured — return normally, caller keeps driving.
384
+ # A new commit was captured — render it, then return normally so the
385
+ # caller keeps driving.
386
+ report_commits "$head_before"
214
387
  }
215
388
 
216
389
  trap 'code=$?; if [ "$code" -ne 0 ]; then
@@ -222,7 +395,18 @@ prev_state=""
222
395
  prev_content=""
223
396
  # Where the last "prompt" turn's `memory` scope + session id are remembered
224
397
  # across iterations and across restarts. In the git dir, never the work tree.
225
- memory_marker="$(git rev-parse --git-dir 2>/dev/null || echo .git)/gtd-loop-memory"
398
+ memory_marker="$(worktree_git_dir 2>/dev/null || echo .git)/gtd-loop-memory"
399
+
400
+ # Truncated once here (not every iteration), so the file always reflects the
401
+ # current run but survives after the loop exits for a later `gtd log`.
402
+ log_path="$(resolve_log_path)"
403
+ : >"$log_path"
404
+ export GTD_LOOP_LOG="$log_path"
405
+ if [[ "$FANCY" == 1 ]]; then
406
+ printf '%s📄 log: %s (tail -f to follow)%s\n' "$C_DIM" "$log_path" "$C_RESET"
407
+ else
408
+ printf '%slog: %s (tail -f to follow)%s\n' "$C_DIM" "$log_path" "$C_RESET"
409
+ fi
226
410
 
227
411
  # Swappable agent adapter: GTD_LOOP_AGENT_CMD lets any coding agent CLI stand
228
412
  # in for the default, receiving the prompt via $GTD_LOOP_PROMPT, the state's
@@ -249,7 +433,8 @@ run_agent_turn() {
249
433
  cmd="$cmd"' --dangerously-skip-permissions'
250
434
  fi
251
435
  GTD_LOOP_PROMPT="$prompt" GTD_LOOP_MODEL="$model" GTD_LOOP_MEMORY="$memory" \
252
- GTD_LOOP_SESSION_ID="$session_id" GTD_LOOP_MEMORY_RESUME="$do_resume" bash -c "$cmd"
436
+ GTD_LOOP_SESSION_ID="$session_id" GTD_LOOP_MEMORY_RESUME="$do_resume" bash -c "$cmd" \
437
+ >>"${GTD_LOOP_LOG:-/dev/null}" 2>&1
253
438
  }
254
439
 
255
440
  # Opening move: capture the human's pending edit so gtd-loop is the ONLY command
@@ -273,17 +458,19 @@ run_agent_turn() {
273
458
  # step silently, refusal surfaced and halts, a clean/committed gate just falls
274
459
  # into the loop below.
275
460
  if ! peek_json="$(run_gtd next --json 2>&1)"; then
276
- echo "gtd-loop: could not determine the next step:" >&2
277
- echo "$peek_json" >&2
278
- echo "Run \`gtd status\` to inspect the repo." >&2
461
+ emit_error "could not determine the next step:
462
+ $peek_json
463
+ Run \`gtd status\` to inspect the repo."
464
+ emit_log_pointer
279
465
  exit 1
280
466
  fi
281
467
  if [[ "$(jq -r .kind <<<"$peek_json")" == "message" ]]; then
282
468
  if [[ "$edit_disabled" == 1 ]]; then
283
469
  if ! step_out="$(run_gtd step human 2>&1)"; then
284
- echo "gtd-loop: could not capture your changes at the human gate:" >&2
285
- echo "$step_out" >&2
286
- echo "Fix the reported issue and re-run gtd-loop." >&2
470
+ emit_error "could not capture your changes at the human gate:
471
+ $step_out
472
+ Fix the reported issue and re-run gtd-loop."
473
+ emit_log_pointer
287
474
  exit 1
288
475
  fi
289
476
  else
@@ -318,18 +505,24 @@ while true; do
318
505
  herdr_report working "$label"
319
506
 
320
507
  if [[ "$kind" == "script" ]]; then
508
+ emit_action "$M_CHECK" "$label"
321
509
  head_before="$(git rev-parse HEAD 2>/dev/null || echo none)"
322
510
  # Execute the emitted wrapper verbatim; its exit code is deliberately
323
511
  # ignored (`|| true` under `set -e`) — the script encodes its outcome in
324
512
  # the tree, and `gtd step` captures whatever it left there via the state's
325
- # own `on` patterns.
326
- bash -c "$content" || true
327
- run_gtd step "$actor" >/dev/null
328
- head_after="$(git rev-parse HEAD 2>/dev/null || echo none)"
329
- if [[ "$head_before" == "$head_after" ]]; then
513
+ # own `on` patterns. Its own output goes to the log, not the terminal.
514
+ bash -c "$content" >>"$GTD_LOOP_LOG" 2>&1 || true
515
+ if ! step_out="$(run_gtd step "$actor" 2>&1)"; then
516
+ printf '%s\n' "$step_out" >>"$GTD_LOOP_LOG"
517
+ emit_error "$step_out"
518
+ emit_log_pointer
519
+ exit 1
520
+ fi
521
+ printf '%s\n' "$step_out" >>"$GTD_LOOP_LOG"
522
+ if ! report_commits "$head_before"; then
330
523
  herdr_report idle "$label"
331
524
  herdr_release
332
- echo "--- Settled ($state: check passed, nothing to do) ---"
525
+ emit_settled "$state"
333
526
  exit 0
334
527
  fi
335
528
  prev_state=""
@@ -341,8 +534,10 @@ while true; do
341
534
  # agent-owned state and content repeating means the last dispatch made no
342
535
  # progress; stop rather than spin on it.
343
536
  if [[ "$state" == "$prev_state" && "$content" == "$prev_content" ]]; then
344
- echo "gtd-loop: no progress at '$state' — the agent's last turn changed nothing. Stopping to avoid spinning." >&2
345
- run_gtd next >&2
537
+ next_out="$(run_gtd next 2>&1)" || true
538
+ emit_error "no progress at '$state' — the agent's last turn changed nothing. Stopping to avoid spinning.
539
+ $next_out"
540
+ emit_log_pointer
346
541
  exit 1
347
542
  fi
348
543
  prev_state="$state"
@@ -382,23 +577,37 @@ while true; do
382
577
  # cap) before stepping — the plain-`gtd next` self-validation instruction's
383
578
  # driver-side equivalent, so the next rest is only ever handed a well-formed
384
579
  # file. gtd itself never validates at step time — this is the loop's job.
580
+ emit_action "$M_AGENT" "$label"
581
+ head_before="$(git rev-parse HEAD 2>/dev/null || echo none)"
385
582
  run_agent_turn "$content" "$resume"
386
583
 
387
584
  fix_attempt=0
388
585
  while ! validate_out="$(run_gtd validate 2>&1)"; do
389
586
  fix_attempt=$((fix_attempt + 1))
390
587
  if ((fix_attempt > 3)); then
391
- echo "gtd-loop: '$state' still fails \`gtd validate\` after 3 fix attempts:" >&2
392
- echo "$validate_out" >&2
393
- echo "Stopping rather than stepping with a malformed steering file." >&2
588
+ emit_error "'$state' still fails \`gtd validate\` after 3 fix attempts:
589
+ $validate_out
590
+ Stopping rather than stepping with a malformed steering file."
591
+ emit_log_pointer
394
592
  exit 1
395
593
  fi
396
594
  # Resume the same session when there is one (the agent just wrote the file);
397
595
  # otherwise a fresh invocation still gets the findings via the prompt.
398
596
  fix_resume=0
399
597
  [[ -n "$session_id" ]] && fix_resume=1
598
+ emit_action "$M_FIX" "attempt $fix_attempt"
400
599
  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"
401
600
  done
402
601
 
403
- run_gtd step "$actor" >/dev/null
602
+ if ! step_out="$(run_gtd step "$actor" 2>&1)"; then
603
+ printf '%s\n' "$step_out" >>"$GTD_LOOP_LOG"
604
+ emit_error "$step_out"
605
+ emit_log_pointer
606
+ exit 1
607
+ fi
608
+ printf '%s\n' "$step_out" >>"$GTD_LOOP_LOG"
609
+ # A no-op step (nothing matched a declared pattern on a clean tree) reports
610
+ # zero commits here — report_commits returns non-zero, and that's fine: the
611
+ # NEXT iteration's stall check is what catches unchanged state/content.
612
+ report_commits "$head_before" || true
404
613
  done
package/docs/loop.md CHANGED
@@ -137,7 +137,7 @@ while true; do
137
137
  content="$(jq -r .content <<<"$next_json")"
138
138
 
139
139
  if [[ "$kind" == "message" ]]; then
140
- echo "--- Your turn ($state) ---"
140
+ echo "your turn ($state)" # bin/gtd renders this via emit_gate instead
141
141
  gtd next
142
142
  exit 0
143
143
  fi
@@ -148,7 +148,7 @@ while true; do
148
148
  gtd step "$actor" >/dev/null # capture whatever it left in the tree
149
149
  head_after="$(git rev-parse HEAD 2>/dev/null || echo none)"
150
150
  if [[ "$head_before" == "$head_after" ]]; then
151
- echo "--- Settled ($state: check passed, nothing to do) ---"
151
+ echo "settled ($state: check passed, nothing to do)" # bin/gtd renders this via emit_settled instead
152
152
  exit 0
153
153
  fi
154
154
  continue
@@ -164,6 +164,33 @@ The driver — not the prompt text — owns ending the agent's turn
164
164
  (`gtd step "$actor"` right after the agent acts): every default-workflow agent
165
165
  prompt says explicitly not to run `gtd step agent` itself.
166
166
 
167
+ `bin/gtd log` opens the current repo/worktree's loop logfile —
168
+ `"$(worktree_git_dir)/gtd-loop.log"` by default, or `$GTD_LOOP_LOG` verbatim
169
+ when set — in `${VISUAL:-$EDITOR}`, the same editor resolution `bin/gtd edit`
170
+ uses. It takes no arguments and errors out (rather than opening an empty buffer)
171
+ if no loop has produced a log yet. `worktree_git_dir` derives the git dir from
172
+ the cwd with `GIT_DIR`/`GIT_WORK_TREE` scrubbed, so an inherited `GIT_DIR`
173
+ (leaked from a parent git process, a hook, or another worktree's shell) can
174
+ never key the log — or the loop's memory marker — to a different worktree; two
175
+ worktrees looping at once each keep their own log.
176
+
177
+ ## Rendered output and logging
178
+
179
+ `bin/gtd` detects its output capability once at startup (`FANCY`, true when
180
+ stdout is a real terminal AND `NO_COLOR` is unset): on a real terminal it prints
181
+ one colored/emoji line per event, and under `NO_COLOR` or a piped/redirected
182
+ stdout it prints the same events as plain ASCII with no escape codes, per the
183
+ [NO_COLOR convention](https://no-color.org). Normal events (a state transition,
184
+ a captured commit, an agent/check turn starting, settling at idle) render to
185
+ stdout; refusals, stalls, and validate-cap halts render to stderr, each followed
186
+ by a `📄 see <log>` pointer (plain: `see <log>`).
187
+
188
+ The three previously terminal-visible subprocess streams — the agent turn's own
189
+ output, the check script's own output, and `gtd step`'s own output — are no
190
+ longer printed directly: they're appended to the per-repo/per-worktree log file
191
+ (truncated once at the start of the run) instead, and surfaced with
192
+ `bin/gtd log` above.
193
+
167
194
  ## Agent memory scope
168
195
 
169
196
  A `"prompt"` beat may carry an optional `memory` key — an opaque scope label the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "7.5.0",
3
+ "version": "7.6.1",
4
4
  "private": false,
5
5
  "description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
6
6
  "bin": {