@pmelab/gtd 3.4.0 → 3.6.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 +58 -25
- package/dist/gtd.bundle.mjs +314 -63
- package/docs/cli.md +89 -17
- package/docs/configuration.md +60 -11
- package/docs/design/state-file-association.md +9 -8
- package/docs/design/state-machine-validation.md +2 -2
- package/docs/design/steering-file-loops.md +11 -0
- package/docs/design/steering-file-validation-command.md +386 -0
- package/docs/development.md +8 -6
- package/docs/examples/advanced-workflow.md +17 -1
- package/docs/loop.md +4 -1
- package/docs/upgrading.md +12 -9
- package/package.json +1 -1
package/bin/gtd-loop
CHANGED
|
@@ -7,9 +7,10 @@ set -euo pipefail
|
|
|
7
7
|
# - "script" (a check rest) -> `gtd run` executes it and steps the check
|
|
8
8
|
# actor in one command; zero new commits = green with nothing owed (the
|
|
9
9
|
# terminal signal at idle).
|
|
10
|
-
# - "prompt" (an agent rest) -> feed `content` to the agent,
|
|
11
|
-
# `gtd
|
|
12
|
-
#
|
|
10
|
+
# - "prompt" (an agent rest) -> feed `content` to the agent, run the
|
|
11
|
+
# self-validation gate (`gtd validate`, re-prompting on findings — see the
|
|
12
|
+
# gate's own comment below), then `gtd step <actor>` ourselves — the
|
|
13
|
+
# harness owns ending the turn, not the prompt text.
|
|
13
14
|
# `.model` (optional, opaque) is exported as $GTD_LOOP_MODEL for the agent
|
|
14
15
|
# adapter invoked at a "prompt" rest; the default adapter appends `--model
|
|
15
16
|
# "$GTD_LOOP_MODEL"` only when it's non-empty.
|
|
@@ -34,6 +35,34 @@ prev_content=""
|
|
|
34
35
|
# across iterations and across restarts. In the git dir, never the work tree.
|
|
35
36
|
memory_marker="$(git rev-parse --git-dir 2>/dev/null || echo .git)/gtd-loop-memory"
|
|
36
37
|
|
|
38
|
+
# Swappable agent adapter: GTD_LOOP_AGENT_CMD lets any coding agent CLI stand
|
|
39
|
+
# in for the default, receiving the prompt via $GTD_LOOP_PROMPT, the state's
|
|
40
|
+
# opaque model hint (if any) via $GTD_LOOP_MODEL, and the memory scope via
|
|
41
|
+
# $GTD_LOOP_MEMORY / $GTD_LOOP_SESSION_ID / $GTD_LOOP_MEMORY_RESUME ("1" when
|
|
42
|
+
# this turn should continue the prior session). An adapter that ignores any of
|
|
43
|
+
# these keeps working — they're exported alongside $GTD_LOOP_PROMPT, never
|
|
44
|
+
# required. The default adapter maps the memory signal onto claude's own
|
|
45
|
+
# session flags: --resume to continue the scope's session, --session-id to pin
|
|
46
|
+
# a fresh one so the next same-scope turn can resume it. $1 is the prompt, $2
|
|
47
|
+
# the resume flag; the memory scope ($memory/$session_id) is read from the
|
|
48
|
+
# enclosing iteration, so a same-turn re-prompt (the validate gate) resumes the
|
|
49
|
+
# same session.
|
|
50
|
+
run_agent_turn() {
|
|
51
|
+
local prompt="$1" do_resume="$2" cmd="${GTD_LOOP_AGENT_CMD:-}"
|
|
52
|
+
if [[ -z "$cmd" ]]; then
|
|
53
|
+
cmd='claude -p "$GTD_LOOP_PROMPT"'
|
|
54
|
+
[[ -n "$model" ]] && cmd="$cmd"' --model "$GTD_LOOP_MODEL"'
|
|
55
|
+
if [[ "$do_resume" == "1" ]]; then
|
|
56
|
+
cmd="$cmd"' --resume "$GTD_LOOP_SESSION_ID"'
|
|
57
|
+
elif [[ -n "$session_id" ]]; then
|
|
58
|
+
cmd="$cmd"' --session-id "$GTD_LOOP_SESSION_ID"'
|
|
59
|
+
fi
|
|
60
|
+
cmd="$cmd"' --dangerously-skip-permissions'
|
|
61
|
+
fi
|
|
62
|
+
GTD_LOOP_PROMPT="$prompt" GTD_LOOP_MODEL="$model" GTD_LOOP_MEMORY="$memory" \
|
|
63
|
+
GTD_LOOP_SESSION_ID="$session_id" GTD_LOOP_MEMORY_RESUME="$do_resume" bash -c "$cmd"
|
|
64
|
+
}
|
|
65
|
+
|
|
37
66
|
while true; do
|
|
38
67
|
if ! next_json="$(gtd next --json 2>&1)"; then
|
|
39
68
|
echo "gtd-loop: could not determine the next step:" >&2
|
|
@@ -104,28 +133,32 @@ while true; do
|
|
|
104
133
|
rm -f "$memory_marker"
|
|
105
134
|
fi
|
|
106
135
|
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
136
|
+
# Run the agent turn (see run_agent_turn's own comment for the adapter
|
|
137
|
+
# contract). Then the self-validation gate: a producing state may declare a
|
|
138
|
+
# `file:`/`mode:` pair (e.g. the default workflow's grilling/reviewing),
|
|
139
|
+
# making its output's format checkable. `gtd validate` is a no-op (exit 0)
|
|
140
|
+
# when the resolved rest has nothing to validate, so run it after EVERY agent
|
|
141
|
+
# turn; on findings, re-prompt the SAME agent session to fix them (up to a
|
|
142
|
+
# cap) before stepping — the plain-`gtd next` self-validation instruction's
|
|
143
|
+
# driver-side equivalent, so the next rest is only ever handed a well-formed
|
|
144
|
+
# file. gtd itself never validates at step time — this is the loop's job.
|
|
145
|
+
run_agent_turn "$content" "$resume"
|
|
146
|
+
|
|
147
|
+
fix_attempt=0
|
|
148
|
+
while ! validate_out="$(gtd validate 2>&1)"; do
|
|
149
|
+
fix_attempt=$((fix_attempt + 1))
|
|
150
|
+
if ((fix_attempt > 3)); then
|
|
151
|
+
echo "gtd-loop: '$state' still fails \`gtd validate\` after 3 fix attempts:" >&2
|
|
152
|
+
echo "$validate_out" >&2
|
|
153
|
+
echo "Stopping rather than stepping with a malformed steering file." >&2
|
|
154
|
+
exit 1
|
|
125
155
|
fi
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
156
|
+
# Resume the same session when there is one (the agent just wrote the file);
|
|
157
|
+
# otherwise a fresh invocation still gets the findings via the prompt.
|
|
158
|
+
fix_resume=0
|
|
159
|
+
[[ -n "$session_id" ]] && fix_resume=1
|
|
160
|
+
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"
|
|
161
|
+
done
|
|
162
|
+
|
|
130
163
|
gtd step "$actor" >/dev/null
|
|
131
164
|
done
|