@pmelab/gtd 7.3.0 → 7.4.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
@@ -167,11 +167,11 @@ through two `vars` tiers — `plannerModel` (heavier planning and review) and
167
167
  place (a `vars:` edit or a `GTD_VAR_plannerModel` override) instead of per
168
168
  state.
169
169
 
170
- `gtd-loop`, installed alongside `gtd`, is a ready-to-run driver for the whole
171
- protocol — point it at a repo and it runs the loop until it's your turn. It is
172
- the only command you run: at a gate you edit files (answer a plan question, tick
173
- a review box, fix code) and re-launch it, and it captures your edit as its
174
- opening move before driving on — so you never run `gtd step human` by hand. See
170
+ Bare `gtd` (or `gtd loop`) is a ready-to-run driver for the whole protocol —
171
+ point it at a repo and it runs the loop until it's your turn. It is the only
172
+ command you run: at a gate you edit files (answer a plan question, tick a review
173
+ box, fix code) and re-launch it, and it captures your edit as its opening move
174
+ before driving on — so you never run `gtd step human` by hand. See
175
175
  [Driving the loop](docs/loop.md).
176
176
 
177
177
  Before wiring gtd into a repo, note the
@@ -196,7 +196,7 @@ Both halves are enforced by `gtd validate` and the `gtd step` gate.
196
196
 
197
197
  Herdr integration: a workflow state can declare an optional `label:` — a
198
198
  human-readable display name surfaced in `gtd next --json`/`gtd status`. The
199
- reference `bin/gtd-loop` driver uses it to report its lifecycle
199
+ reference `bin/gtd` driver uses it to report its lifecycle
200
200
  (working/blocked/idle) to a [Herdr](https://herdr.dev) pane sidebar via the
201
201
  `herdr` CLI when running under Herdr (`HERDR_ENV=1`, a pane ID, and `herdr` on
202
202
  `$PATH`); outside Herdr this is a complete no-op. See
@@ -210,8 +210,8 @@ reporting contract.
210
210
  sub-machines, and the bundled workflow template
211
211
  - [CLI reference](docs/cli.md) — every command, exit codes, JSON schemas,
212
212
  repository requirements
213
- - [Driving the loop](docs/loop.md) — the reference loop driver, `gtd-loop`,
214
- custom agents
213
+ - [Driving the loop](docs/loop.md) — the reference loop driver,
214
+ `gtd`/`gtd loop`, custom agents
215
215
  - [Configuration](docs/configuration.md) — `gtd init`, the `.gtdrc` `workflow:`
216
216
  schema, lookup
217
217
  - [Upgrading](docs/upgrading.md) — breaking changes and migration
@@ -1,6 +1,41 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
+ # This file is the single published `bin` entry: `bin/gtd`. Before anything
5
+ # else, it dispatches on $1:
6
+ # - no arguments, or exactly `loop` with no further arguments -> fall through
7
+ # into the loop body below (the loop protocol comment further down).
8
+ # - `loop` with additional arguments -> usage error, exit 2.
9
+ # - anything else -> hand off to the bundle via `run_gtd`, forwarding all
10
+ # arguments unchanged (e.g. `bin/gtd status`, `bin/gtd step human`).
11
+ # `run_gtd` is the ONE place that knows how to invoke "the bundle": by default
12
+ # it resolves `dist/gtd.bundle.mjs` relative to this script's own location
13
+ # (not cwd), but `GTD_BIN` (a dev/test override for the whole invocation
14
+ # command, e.g. `GTD_BIN="node $PWD/dev/run.mjs"`) takes precedence when set.
15
+ # Every call the loop body makes below goes through `run_gtd` too, so the loop
16
+ # always drives the exact same build/source it was launched from — no
17
+ # dependence on a `gtd` binary being on PATH.
18
+ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
19
+
20
+ run_gtd() {
21
+ if [[ -n "${GTD_BIN:-}" ]]; then
22
+ eval "$GTD_BIN" '"$@"'
23
+ else
24
+ node "$SCRIPT_DIR/../dist/gtd.bundle.mjs" "$@"
25
+ fi
26
+ }
27
+
28
+ if [[ $# -gt 0 && "$1" != "loop" ]]; then
29
+ run_gtd "$@"
30
+ exit $?
31
+ fi
32
+ if [[ "${1:-}" == "loop" && $# -gt 1 ]]; then
33
+ echo "gtd: 'loop' takes no arguments" >&2
34
+ exit 2
35
+ fi
36
+ # otherwise ($# -eq 0, or exactly "loop" with no further args): fall through
37
+ # into the existing loop body below, unchanged in behavior.
38
+
4
39
  # Drives the v3 pattern-machine loop (see docs/loop.md): each iteration asks
5
40
  # `gtd next --json` who's up and what they should do, dispatching on `kind`:
6
41
  # - "message" (a human rest) -> halt, tell the user what gtd is waiting on.
@@ -32,20 +67,38 @@ set -euo pipefail
32
67
  # `--resume`/`--session-id`. An adapter that ignores them keeps working.
33
68
  herdr_ok() { [ "${HERDR_ENV:-}" = 1 ] && [ -n "${HERDR_PANE_ID:-}" ] && command -v herdr >/dev/null 2>&1; }
34
69
 
70
+ # Run a `herdr` invocation best-effort. Normally output is discarded and a
71
+ # failure is swallowed (`|| true`) so a missing/incompatible herdr never blocks
72
+ # the loop. Set GTD_HERDR_DEBUG=1 to instead surface every call and its exit
73
+ # code on stderr — the escape hatch that makes a silent CLI-signature mismatch
74
+ # (e.g. an arg herdr rejects) visible again.
75
+ herdr_run() {
76
+ if [ "${GTD_HERDR_DEBUG:-}" = 1 ]; then
77
+ printf 'gtd/herdr: %s\n' "$*" >&2
78
+ herdr "$@" >&2
79
+ printf 'gtd/herdr: exit=%s\n' "$?" >&2
80
+ else
81
+ herdr "$@" >/dev/null 2>&1 || true
82
+ fi
83
+ }
84
+
85
+ # NOTE: herdr requires the positional <PANE_ID> BEFORE the options for
86
+ # `pane` subcommands — passing it last makes herdr reject the first option as
87
+ # `unknown option` (exit 2). Keep the pane id first here.
35
88
  herdr_report() { # $1=state (working|blocked|idle) $2=message
36
89
  herdr_ok || return 0
37
- herdr pane report-agent --source herdr:gtd --agent gtd \
38
- --state "$1" --message "$2" "$HERDR_PANE_ID" >/dev/null 2>&1 || true
90
+ herdr_run pane report-agent "$HERDR_PANE_ID" \
91
+ --source herdr:gtd --agent gtd --state "$1" --message "$2"
39
92
  }
40
93
 
41
94
  herdr_notify() { # $1=title $2=body
42
95
  herdr_ok || return 0
43
- herdr notification show "$1" --body "$2" --sound request >/dev/null 2>&1 || true
96
+ herdr_run notification show "$1" --body "$2" --sound request
44
97
  }
45
98
 
46
99
  herdr_release() { # hand the pane back to Herdr's normal detection
47
100
  herdr_ok || return 0
48
- herdr pane release-agent --source herdr:gtd --agent gtd "$HERDR_PANE_ID" >/dev/null 2>&1 || true
101
+ herdr_run pane release-agent "$HERDR_PANE_ID" --source herdr:gtd --agent gtd
49
102
  }
50
103
 
51
104
  trap 'code=$?; if [ "$code" -ne 0 ]; then
@@ -101,14 +154,14 @@ run_agent_turn() {
101
154
  # a harmless no-op; a genuine refusal there (a malformed steering file, or an
102
155
  # edit no `on` pattern accepts) is surfaced and stops the loop rather than being
103
156
  # driven past.
104
- if ! peek_json="$(gtd next --json 2>&1)"; then
157
+ if ! peek_json="$(run_gtd next --json 2>&1)"; then
105
158
  echo "gtd-loop: could not determine the next step:" >&2
106
159
  echo "$peek_json" >&2
107
160
  echo "Run \`gtd status\` to inspect the repo." >&2
108
161
  exit 1
109
162
  fi
110
163
  if [[ "$(jq -r .kind <<<"$peek_json")" == "message" ]]; then
111
- if ! step_out="$(gtd step human 2>&1)"; then
164
+ if ! step_out="$(run_gtd step human 2>&1)"; then
112
165
  echo "gtd-loop: could not capture your changes at the human gate:" >&2
113
166
  echo "$step_out" >&2
114
167
  echo "Fix the reported issue and re-run gtd-loop." >&2
@@ -117,7 +170,7 @@ if [[ "$(jq -r .kind <<<"$peek_json")" == "message" ]]; then
117
170
  fi
118
171
 
119
172
  while true; do
120
- if ! next_json="$(gtd next --json 2>&1)"; then
173
+ if ! next_json="$(run_gtd next --json 2>&1)"; then
121
174
  echo "gtd-loop: could not determine the next step:" >&2
122
175
  echo "$next_json" >&2
123
176
  echo "Run \`gtd status\` to inspect the repo." >&2
@@ -141,7 +194,7 @@ while true; do
141
194
  herdr_report blocked "$label"
142
195
  herdr_notify "gtd needs you — $(basename "$PWD")" "$label"
143
196
  echo "--- Your turn ($state) ---"
144
- gtd next
197
+ run_gtd next
145
198
  exit 0
146
199
  fi
147
200
 
@@ -152,7 +205,7 @@ while true; do
152
205
  # the tree, and `gtd step` captures whatever it left there via the state's
153
206
  # own `on` patterns.
154
207
  bash -c "$content" || true
155
- gtd step "$actor" >/dev/null
208
+ run_gtd step "$actor" >/dev/null
156
209
  head_after="$(git rev-parse HEAD 2>/dev/null || echo none)"
157
210
  if [[ "$head_before" == "$head_after" ]]; then
158
211
  herdr_report idle "$label"
@@ -170,7 +223,7 @@ while true; do
170
223
  # progress; stop rather than spin on it.
171
224
  if [[ "$state" == "$prev_state" && "$content" == "$prev_content" ]]; then
172
225
  echo "gtd-loop: no progress at '$state' — the agent's last turn changed nothing. Stopping to avoid spinning." >&2
173
- gtd next >&2
226
+ run_gtd next >&2
174
227
  exit 1
175
228
  fi
176
229
  prev_state="$state"
@@ -213,7 +266,7 @@ while true; do
213
266
  run_agent_turn "$content" "$resume"
214
267
 
215
268
  fix_attempt=0
216
- while ! validate_out="$(gtd validate 2>&1)"; do
269
+ while ! validate_out="$(run_gtd validate 2>&1)"; do
217
270
  fix_attempt=$((fix_attempt + 1))
218
271
  if ((fix_attempt > 3)); then
219
272
  echo "gtd-loop: '$state' still fails \`gtd validate\` after 3 fix attempts:" >&2
@@ -228,5 +281,5 @@ while true; do
228
281
  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"
229
282
  done
230
283
 
231
- gtd step "$actor" >/dev/null
284
+ run_gtd step "$actor" >/dev/null
232
285
  done
@@ -205351,6 +205351,11 @@ const GTD_VERSION = createRequire(import.meta.url)("../package.json").version;
205351
205351
  const HELP_TEXT = `Usage: gtd [command] [options]
205352
205352
 
205353
205353
  Commands:
205354
+ (no command), loop
205355
+ Launch the loop driver (bin/gtd), which repeatedly drives
205356
+ an agent through gtd next/gtd step calls until the
205357
+ workflow returns to its initial state again. A bare gtd
205358
+ invocation and gtd loop both launch it identically
205354
205359
  init Scaffold a minimal .gtdrc.json for this repo, seeding the
205355
205360
  default variables you are most likely to change (the test
205356
205361
  command) and a Prettier formatting suggestion. gtd runs its
package/docs/cli.md CHANGED
@@ -4,12 +4,19 @@
4
4
  Usage: gtd [command] [options]
5
5
 
6
6
  Commands:
7
- init Scaffold a minimal .gtdrc.json for this repo, seeding a
8
- testCommand var (npm test) and a ready-to-edit Prettier
9
- formatting suggestion under modes:. Writes no workflow — gtd
10
- runs the bundled unified workflow as its built-in default.
11
- Takes no argument. Refuses if a gtd config already exists.
12
- Leaves the file uncommitted for you to review and commit
7
+ (no command), loop
8
+ Launch the loop driver (bin/gtd), which repeatedly drives
9
+ an agent through gtd next/gtd step calls until the
10
+ workflow returns to its initial state again. A bare gtd
11
+ invocation and gtd loop both launch it identically
12
+ init Scaffold a minimal .gtdrc.json for this repo, seeding the
13
+ default variables you are most likely to change (the test
14
+ command) and a Prettier formatting suggestion. gtd runs its
15
+ built-in workflow by default, so no workflow is written —
16
+ add a workflow: key only to customize the machine itself.
17
+ Takes no argument. Run once per repo; refuses if a gtd
18
+ config already exists. Leaves the file uncommitted for you
19
+ to review and commit
13
20
  step <actor> Authenticate as <actor>, match the resolved rest's
14
21
  declared patterns against the pending changes, and commit
15
22
  (or squash) the one resulting transition. Pass
@@ -44,7 +51,9 @@ Commands:
44
51
  declares, with its mode's commands (its file:/mode:);
45
52
  exits non-zero with the findings when it is invalid
46
53
  lsp Start the LSP server for .gtd/ steering files (stdio)
47
- visualize Serve an interactive diagram of the active workflow locally
54
+ visualize Serve an interactive diagram of the active workflow on a
55
+ local web server (--port <n>, --no-open; --json prints the
56
+ model and exits)
48
57
  version Print version and exit
49
58
  help Print this help and exit
50
59
 
@@ -61,9 +70,11 @@ Options:
61
70
  `--version` (`-v`) / `gtd version` and `--help` (`-h`) / `gtd help`
62
71
  short-circuit before any git or repository-state work — they run outside a repo
63
72
  and in any repo state (the bare subcommands are exact equivalents of their flag
64
- forms). Bare `gtd` (no subcommand) is a usage error: it prints the help text and
65
- exits 1 without touching the repository. Every other command must be run from
66
- the **repository root** — gtd derives the workflow, pending changes, and process
73
+ forms). Bare `gtd` (no subcommand) and `gtd loop` both launch the loop driver
74
+ (`bin/gtd`) immediately — neither is a usage error. Any other, truly unknown
75
+ subcommand remains a usage error: it prints the help text and exits 1 without
76
+ touching the repository. Every other (recognized) command must be run from the
77
+ **repository root** — gtd derives the workflow, pending changes, and process
67
78
  history relative to cwd, so it refuses with a clear error if invoked from a
68
79
  subdirectory.
69
80
 
@@ -401,7 +412,7 @@ which are JSON-only. `--json` emits
401
412
 
402
413
  There is no `gtd` subcommand that executes a workflow script — gtd never runs a
403
414
  workflow script itself. When `gtd next` resolves to a `script`-content rest, the
404
- **driver** (`bin/gtd-loop`, or any loop harness) executes the emitted `content`
415
+ **driver** (`bin/gtd`, or any loop harness) executes the emitted `content`
405
416
  verbatim via `bash -c` — foreground, inherited stdio, exit code deliberately
406
417
  ignored (a check script encodes its outcome in the tree, e.g. writing a findings
407
418
  file, never in its exit status) — then runs `gtd step <actor>` for that state's
@@ -493,7 +504,7 @@ agent or human:
493
504
  - **The producing agent self-validates.** Plain `gtd next` appends a "run
494
505
  `gtd validate` and fix all violations" instruction to a `prompt` rest that
495
506
  declares `file:`+`mode:`; `gtd next --json` withholds it and the driving loop
496
- runs `gtd validate` after the turn instead (see `bin/gtd-loop` /
507
+ runs `gtd validate` after the turn instead (see `bin/gtd` /
497
508
  [STATES.md §12](../STATES.md#12-steering-file-validation-gtd-validate)).
498
509
  - **`gtd step` enforces the same gate.** Capturing a turn out of a state that
499
510
  declares `file:`+`mode:` formats that file in place and validates it first
package/docs/loop.md CHANGED
@@ -57,9 +57,10 @@ gtd next --json # ask who's up and what they should do
57
57
  ```
58
58
 
59
59
  See [`skills/loop/SKILL.md`](../skills/loop/SKILL.md) for the agent-facing
60
- instructions that follow the same pinned contract. `gtd-loop`, installed
61
- alongside `gtd` (see below), is the packaged, ready-to-run implementation of
62
- that same script for anyone who doesn't want to drive the loop by hand.
60
+ instructions that follow the same pinned contract. `gtd` itself (bare, or with
61
+ `loop` as its first argument — see below) is the packaged, ready-to-run
62
+ implementation of that same script for anyone who doesn't want to drive the loop
63
+ by hand.
63
64
 
64
65
  ## The reference loop driver
65
66
 
@@ -68,17 +69,29 @@ A minimal bash implementation of the pinned protocol, driving an agent CLI (e.g.
68
69
  reference for what a loop driver must do; keep any other implementation
69
70
  (including `skills/loop/SKILL.md`) consistent with it rather than editing both
70
71
  independently. Both open with the same move — capture the human's pending edit
71
- when the machine rests at a `"message"` gate — so `gtd-loop` is the only command
72
- a human runs. `bin/gtd-loop` is this exact script, packaged as the `gtd-loop`
73
- binary, with four additions: it stops with a diagnostic if the same `"prompt"`
74
- state/content repeat with no progress (see `skills/loop/SKILL.md`'s "Stall
75
- detection"); it lets `GTD_LOOP_AGENT_CMD` swap in any coding agent CLI in place
76
- of the default `claude -p`, receiving the prompt via `$GTD_LOOP_PROMPT`; it
77
- exports the resolved state's optional `model` hint as `$GTD_LOOP_MODEL`,
78
- appending `--model "$GTD_LOOP_MODEL"` to the default `claude -p` invocation
79
- whenever it's non-empty; and it acts on the optional `memory` scope hint (see
80
- "Agent memory scope" below) — continuing one agent session across consecutive
81
- same-scope turns and starting fresh when the scope changes.
72
+ when the machine rests at a `"message"` gate — so `gtd` is the only command a
73
+ human runs. `bin/gtd` is the packaged entry point: invoked bare, or with `loop`
74
+ as its first argument, it runs this exact script; any other first argument (e.g.
75
+ `next`, `step`, `status`) hands off to `node dist/gtd.bundle.mjs` instead. The
76
+ loop body it runs adds four things on top of the reference script above: it
77
+ stops with a diagnostic if the same `"prompt"` state/content repeat with no
78
+ progress (see `skills/loop/SKILL.md`'s "Stall detection"); it lets
79
+ `GTD_LOOP_AGENT_CMD` swap in any coding agent CLI in place of the default
80
+ `claude -p`, receiving the prompt via `$GTD_LOOP_PROMPT`; it exports the
81
+ resolved state's optional `model` hint as `$GTD_LOOP_MODEL`, appending
82
+ `--model "$GTD_LOOP_MODEL"` to the default `claude -p` invocation whenever it's
83
+ non-empty; and it acts on the optional `memory` scope hint (see "Agent memory
84
+ scope" below) — continuing one agent session across consecutive same-scope turns
85
+ and starting fresh when the scope changes.
86
+
87
+ `bin/gtd` resolves both its bundle hand-off and the loop's own internal `gtd`
88
+ calls against `dist/gtd.bundle.mjs` next to its own location. Set `GTD_BIN` — a
89
+ full command, not just a path — to point both at source instead, e.g. for
90
+ development or testing:
91
+
92
+ ```bash
93
+ GTD_BIN="node $PWD/dev/run.mjs" bin/gtd
94
+ ```
82
95
 
83
96
  ```bash
84
97
  #!/usr/bin/env bash
@@ -144,9 +157,9 @@ turn, or either side has no `memory` — start fresh.
144
157
  This is what makes a loop retain memory while a phase boundary clears it: a loop
145
158
  that keeps re-entering one state emits the same label every lap, so the agent
146
159
  accumulates context across the loop; the move to the next phase's
147
- differently-labelled state resets it. `bin/gtd-loop` implements this by mapping
148
- the signal onto claude's session flags — `--session-id` to pin a fresh session
149
- when the scope changes, `--resume` to continue it while the scope holds — and
160
+ differently-labelled state resets it. `bin/gtd` implements this by mapping the
161
+ signal onto claude's session flags — `--session-id` to pin a fresh session when
162
+ the scope changes, `--resume` to continue it while the scope holds — and
150
163
  persists the current scope + session id in the git dir (never the working tree,
151
164
  so `gtd status` and the pending diff never see them) so retention survives even
152
165
  across the restarts a human gate forces mid-loop. See `skills/loop/SKILL.md`'s
@@ -154,7 +167,7 @@ across the restarts a human gate forces mid-loop. See `skills/loop/SKILL.md`'s
154
167
 
155
168
  ## Using a different agent
156
169
 
157
- `gtd-loop` defaults to
170
+ `gtd` (bare, or `gtd loop`) defaults to
158
171
  `claude -p "$GTD_LOOP_PROMPT" --dangerously-skip-permissions`, but the agent
159
172
  invocation is swappable: set `GTD_LOOP_AGENT_CMD` to any shell command, and it
160
173
  runs with the prompt available as `$GTD_LOOP_PROMPT` in its environment, along
@@ -165,23 +178,26 @@ session). An adapter that ignores any of these keeps working unchanged. For
165
178
  example, to drive a different agent CLI:
166
179
 
167
180
  ```bash
168
- GTD_LOOP_AGENT_CMD='my-agent-cli --prompt "$GTD_LOOP_PROMPT"' gtd-loop
181
+ GTD_LOOP_AGENT_CMD='my-agent-cli --prompt "$GTD_LOOP_PROMPT"' gtd
169
182
  ```
170
183
 
171
184
  ## Herdr integration (optional)
172
185
 
173
- `bin/gtd-loop` optionally reports its lifecycle to [Herdr](https://herdr.dev) (a
186
+ `bin/gtd` optionally reports its lifecycle to [Herdr](https://herdr.dev) (a
174
187
  terminal multiplexer for coding agents) via a `herdr` CLI binary, entirely at
175
188
  the driver's edge — gtd core never talks to Herdr. Every call is best-effort
176
189
  (guarded, output discarded, `|| true`), so a missing/failing `herdr` binary
177
- never blocks, slows, or fails the loop.
190
+ never blocks, slows, or fails the loop. Because that guard also hides a herdr
191
+ CLI-signature mismatch, set `GTD_HERDR_DEBUG=1` to surface every `herdr` call
192
+ and its exit code on stderr instead.
178
193
 
179
194
  The reporting is a no-op unless all three guard conditions hold: `HERDR_ENV=1`,
180
- a non-empty `$HERDR_PANE_ID`, and `herdr` on `$PATH`. When they do, `gtd-loop`
181
- makes three kinds of calls:
195
+ a non-empty `$HERDR_PANE_ID`, and `herdr` on `$PATH`. When they do, `gtd` makes
196
+ three kinds of calls (note the positional `<PANE_ID>` comes BEFORE the options —
197
+ herdr's `pane` subcommands reject a trailing pane id):
182
198
 
183
- - `herdr pane report-agent --source herdr:gtd --agent gtd --state <state> --message <label> "$HERDR_PANE_ID"`
184
- - `herdr pane release-agent --source herdr:gtd --agent gtd "$HERDR_PANE_ID"`
199
+ - `herdr pane report-agent "$HERDR_PANE_ID" --source herdr:gtd --agent gtd --state <state> --message <label>`
200
+ - `herdr pane release-agent "$HERDR_PANE_ID" --source herdr:gtd --agent gtd`
185
201
  - `herdr notification show <title> --body <label> --sound request`
186
202
 
187
203
  mapped onto the loop's states like this:
package/docs/upgrading.md CHANGED
@@ -159,6 +159,47 @@ loudly at load time rather than silently misinterpreting.
159
159
  or agent harness, upgrading the `gtd` binary also means re-copying that skill
160
160
  from this release.
161
161
 
162
+ ## 7.4: `gtd-loop` is gone — bare `gtd` and `gtd loop` run it directly
163
+
164
+ `gtd-loop`, the standalone loop-driver binary installed alongside `gtd`, no
165
+ longer exists. Its body now lives in `bin/gtd` itself, which is a bash entry
166
+ script that dispatches on its first argument:
167
+
168
+ | behavior | before (≤ 7.3) | from 7.4 |
169
+ | ------------------------------------------------------ | ---------------------------------------------- | ----------------------------------------------- |
170
+ | `gtd-loop` | separate installed binary | removed |
171
+ | bare `gtd` (no subcommand) | usage error, prints help, exits 1 | runs the loop driver immediately |
172
+ | `gtd loop` | not a recognized subcommand | runs the loop driver immediately |
173
+ | any other subcommand (`step`, `status`, `validate`, …) | handled by `node dist/gtd.bundle.mjs` directly | same, now reached via `bin/gtd`'s hand-off/exec |
174
+
175
+ **Bare `gtd` is a behavior flip, not merely an addition** — the same kind of
176
+ change as the v1 → v2 label grammar below: previously it printed the help text
177
+ and exited 1 without touching the repository; from 7.4 it launches the loop
178
+ driver immediately, identically to `gtd loop`. A script that relied on bare
179
+ `gtd` failing fast (e.g. to detect a missing subcommand) now gets a running loop
180
+ instead of an error.
181
+
182
+ Because `bin/gtd` is a bash script for every invocation now, **running `gtd` at
183
+ all — including any subcommand — requires bash**. Before 7.4, only the loop
184
+ driver (`gtd-loop`) needed bash; a subcommand could be invoked directly via the
185
+ bundle (e.g. `node node_modules/.bin/gtd`) with no bash in between. This is
186
+ intentional and accepted, not an oversight: gtd's loop, its `script`-content
187
+ checks, and a steering-file mode's own `format:`/`validate:` commands already
188
+ run via `bash -c` (see
189
+ [Configuration](configuration.md#modes--pluggable-steering-file-modes)) — gtd
190
+ already assumes a unix/bash agent environment everywhere else, so the entry
191
+ point catching up removes an inconsistency rather than introducing a new
192
+ dependency.
193
+
194
+ **Migration:**
195
+
196
+ - Anyone invoking `gtd-loop` directly — in scripts, CI, or docs — should invoke
197
+ `gtd` or `gtd loop` instead.
198
+ - Anyone relying on `node node_modules/.bin/gtd` resolving straight to the
199
+ bundle should note `gtd` is now a bash script that execs the bundle for
200
+ subcommands, so behavior for actual subcommands (`gtd step`, `gtd status`,
201
+ etc.) is unchanged — only the underlying file changed.
202
+
162
203
  ## 7.2: the review checkout window is now per worktree
163
204
 
164
205
  The review checkout window's two refs moved from the SHARED `refs/gtd/*`
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "7.3.0",
3
+ "version": "7.4.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": {
7
- "gtd": "dist/gtd.bundle.mjs",
8
- "gtd-loop": "bin/gtd-loop"
7
+ "gtd": "bin/gtd"
9
8
  },
10
9
  "files": [
11
10
  "dist/",