@pmelab/gtd 2.7.0 → 3.0.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/bin/gtd-loop CHANGED
@@ -1,18 +1,22 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- # Drives the two-beat protocol (README.md "The reference loop driver") to a
5
- # human gate. Both mutators are attempted every iteration, refusal-tolerant:
6
- # whichever actor is actually awaited claims the turn (captures the human's
7
- # uncommitted edit after a halt, or the agent's own edit from the previous
8
- # iteration), the other refuses harmlessly.
4
+ # Drives the v3 pattern-machine loop (see docs/loop.md): each iteration asks
5
+ # `gtd next --json` who's up and what they should do, dispatching on `kind`:
6
+ # - "message" (a human rest) -> halt, tell the user what gtd is waiting on.
7
+ # - "script" (a check rest) -> `gtd run` executes it and steps the check
8
+ # actor in one command; zero new commits = green with nothing owed (the
9
+ # terminal signal at idle).
10
+ # - "prompt" (an agent rest) -> feed `content` to the agent, then
11
+ # `gtd step <actor>` ourselves — the harness owns ending the turn, not
12
+ # the prompt text.
13
+ # `.model` (optional, opaque) is exported as $GTD_LOOP_MODEL for the agent
14
+ # adapter invoked at a "prompt" rest; the default adapter appends `--model
15
+ # "$GTD_LOOP_MODEL"` only when it's non-empty.
9
16
  prev_state=""
10
- prev_prompt=""
17
+ prev_content=""
11
18
 
12
19
  while true; do
13
- gtd step --json >/dev/null 2>&1 || true
14
- gtd step-agent --json >/dev/null 2>&1 || true
15
-
16
20
  if ! next_json="$(gtd next --json 2>&1)"; then
17
21
  echo "gtd-loop: could not determine the next step:" >&2
18
22
  echo "$next_json" >&2
@@ -20,39 +24,55 @@ while true; do
20
24
  exit 1
21
25
  fi
22
26
 
23
- actor="$(jq -r .actor <<<"$next_json")"
24
- pending="$(jq -r .pending <<<"$next_json")"
25
27
  state="$(jq -r .state <<<"$next_json")"
26
- prompt="$(jq -r .prompt <<<"$next_json")"
28
+ actor="$(jq -r .actor <<<"$next_json")"
29
+ kind="$(jq -r .kind <<<"$next_json")"
30
+ content="$(jq -r .content <<<"$next_json")"
31
+ model="$(jq -r '.model // empty' <<<"$next_json")"
27
32
 
28
- if [[ "$actor" != "agent" ]]; then
33
+ if [[ "$kind" == "message" ]]; then
29
34
  echo "--- Your turn ($state) ---"
30
35
  gtd next
31
36
  exit 0
32
37
  fi
33
38
 
34
- if [[ "$pending" == "true" ]]; then
39
+ if [[ "$kind" == "script" ]]; then
40
+ head_before="$(git rev-parse HEAD 2>/dev/null || echo none)"
41
+ gtd run
42
+ head_after="$(git rev-parse HEAD 2>/dev/null || echo none)"
43
+ if [[ "$head_before" == "$head_after" ]]; then
44
+ echo "--- Settled ($state: check passed, nothing to do) ---"
45
+ exit 0
46
+ fi
35
47
  prev_state=""
36
- prev_prompt=""
48
+ prev_content=""
37
49
  continue
38
50
  fi
39
51
 
40
- # Stall detection (skills/loop/SKILL.md): the same agent-owned state and
41
- # prompt repeating means the last dispatch made no progress — stop rather
42
- # than spin on it.
43
- if [[ "$state" == "$prev_state" && "$prompt" == "$prev_prompt" ]]; then
52
+ # kind == "prompt": stall detection (skills/loop/SKILL.md) — the same
53
+ # agent-owned state and content repeating means the last dispatch made no
54
+ # progress; stop rather than spin on it.
55
+ if [[ "$state" == "$prev_state" && "$content" == "$prev_content" ]]; then
44
56
  echo "gtd-loop: no progress at '$state' — the agent's last turn changed nothing. Stopping to avoid spinning." >&2
45
57
  gtd next >&2
46
58
  exit 1
47
59
  fi
48
60
  prev_state="$state"
49
- prev_prompt="$prompt"
61
+ prev_content="$content"
50
62
 
51
63
  # Swappable agent adapter: GTD_LOOP_AGENT_CMD lets any coding agent CLI
52
- # stand in for the default, receiving the prompt via $GTD_LOOP_PROMPT.
64
+ # stand in for the default, receiving the prompt via $GTD_LOOP_PROMPT and
65
+ # the state's opaque model hint (if any) via $GTD_LOOP_MODEL. An adapter
66
+ # that ignores $GTD_LOOP_MODEL keeps working — it's exported alongside
67
+ # $GTD_LOOP_PROMPT, never required.
53
68
  cmd="${GTD_LOOP_AGENT_CMD:-}"
54
69
  if [[ -z "$cmd" ]]; then
55
- cmd='claude -p "$GTD_LOOP_PROMPT" --dangerously-skip-permissions'
70
+ if [[ -n "$model" ]]; then
71
+ cmd='claude -p "$GTD_LOOP_PROMPT" --model "$GTD_LOOP_MODEL" --dangerously-skip-permissions'
72
+ else
73
+ cmd='claude -p "$GTD_LOOP_PROMPT" --dangerously-skip-permissions'
74
+ fi
56
75
  fi
57
- GTD_LOOP_PROMPT="$prompt" bash -c "$cmd"
76
+ GTD_LOOP_PROMPT="$content" GTD_LOOP_MODEL="$model" bash -c "$cmd"
77
+ gtd step "$actor" >/dev/null
58
78
  done