devflow-kit 3.2.0 → 3.4.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/CHANGELOG.md +34 -0
- package/README.md +1 -1
- package/dist/agents/code.md +330 -0
- package/{src/assets → dist}/agents/design.md +11 -1
- package/{src/assets → dist}/agents/diagnose.md +17 -13
- package/dist/agents/git.md +29 -56
- package/{src/assets → dist}/agents/knowledge.md +8 -5
- package/{src/assets → dist}/agents/research.md +21 -2
- package/{src/assets → dist}/agents/review.md +18 -7
- package/{src/assets → dist}/agents/scrutinize.md +35 -11
- package/dist/agents/skim.md +148 -0
- package/{src/assets → dist}/agents/triage.md +8 -2
- package/dist/cli/commands/init.js +62 -0
- package/dist/cli/commands/learning.js +63 -8
- package/dist/cli/commands/uninstall.js +42 -1
- package/dist/commands/bug-analysis.md +30 -8
- package/dist/commands/code-review.md +142 -61
- package/dist/commands/debug.md +15 -13
- package/dist/commands/dynamic-build.md +176 -130
- package/dist/commands/dynamic-plan.md +31 -19
- package/dist/commands/dynamic-profile.md +27 -13
- package/dist/commands/dynamic-tickets.md +28 -14
- package/dist/commands/explore.md +16 -14
- package/dist/commands/implement.md +163 -158
- package/dist/commands/plan.md +37 -24
- package/dist/commands/release.md +69 -4
- package/dist/commands/research.md +33 -11
- package/dist/commands/resolve.md +61 -47
- package/dist/commands/self-review.md +50 -32
- package/dist/core/agent-models.js +53 -7
- package/dist/core/assets.js +55 -10
- package/dist/core/claude-md-audit.js +190 -0
- package/dist/core/feature-switch.js +20 -1
- package/dist/core/flags.js +28 -0
- package/dist/core/fs-atomic.js +8 -3
- package/dist/core/learning-tuning-config.js +8 -0
- package/dist/core/learning-variants.js +213 -0
- package/dist/core/manifest.js +62 -0
- package/dist/core/mds-variants.js +38 -1
- package/dist/core/plugins.js +78 -11
- package/{src/assets → dist/learning-off}/agents/code.md +77 -28
- package/dist/learning-off/agents/design.md +119 -0
- package/dist/learning-off/agents/diagnose.md +210 -0
- package/dist/learning-off/agents/knowledge.md +90 -0
- package/dist/learning-off/agents/research.md +149 -0
- package/dist/learning-off/agents/review.md +228 -0
- package/dist/learning-off/agents/scrutinize.md +117 -0
- package/{src/assets → dist/learning-off}/agents/skim.md +5 -10
- package/dist/learning-off/agents/triage.md +163 -0
- package/dist/learning-off/commands/bug-analysis.md +420 -0
- package/dist/learning-off/commands/code-review.md +525 -0
- package/dist/learning-off/commands/debug.md +294 -0
- package/dist/learning-off/commands/dynamic-build.md +1255 -0
- package/dist/learning-off/commands/dynamic-plan.md +424 -0
- package/dist/learning-off/commands/dynamic-profile.md +214 -0
- package/dist/learning-off/commands/dynamic-tickets.md +632 -0
- package/dist/learning-off/commands/explore.md +210 -0
- package/dist/learning-off/commands/implement.md +808 -0
- package/dist/learning-off/commands/plan.md +664 -0
- package/dist/learning-off/commands/release.md +310 -0
- package/dist/learning-off/commands/research.md +222 -0
- package/dist/learning-off/commands/resolve.md +837 -0
- package/dist/learning-off/commands/self-review.md +266 -0
- package/dist/skills/git/references/tracker/_contract.md +33 -0
- package/dist/skills/git/references/tracker/github/fetch-issue.md +2 -0
- package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +2 -0
- package/dist/skills/git/references/tracker/github/gather-release-evidence.md +5 -1
- package/dist/skills/git/references/tracker/github/post-wave-report.md +2 -0
- package/dist/skills/git/references/tracker/github/setup-task.md +12 -0
- package/dist/skills/git/references/tracker/jira/associate-release.md +1 -1
- package/dist/skills/git/references/tracker/jira/fetch-issue.md +2 -0
- package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +2 -0
- package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +5 -1
- package/dist/skills/git/references/tracker/jira/post-wave-report.md +2 -0
- package/dist/skills/git/references/tracker/jira/setup-task.md +14 -2
- package/dist/skills/git/references/tracker/linear/associate-release.md +1 -1
- package/dist/skills/git/references/tracker/linear/fetch-issue.md +2 -0
- package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +2 -0
- package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +5 -1
- package/dist/skills/git/references/tracker/linear/post-wave-report.md +2 -0
- package/dist/skills/git/references/tracker/linear/setup-task.md +14 -2
- package/dist/targets/claude-code/installer.js +72 -36
- package/dist/targets/claude-code/language-stamp.js +185 -0
- package/dist/targets/claude-code/learning-install.js +489 -0
- package/package.json +1 -1
- package/src/assets/agents/code.mds +339 -0
- package/src/assets/agents/design.mds +149 -0
- package/src/assets/agents/diagnose.mds +225 -0
- package/src/assets/agents/evaluate.md +12 -25
- package/src/assets/agents/git.mds +29 -56
- package/src/assets/agents/knowledge.mds +125 -0
- package/src/assets/agents/learning.md +4 -6
- package/src/assets/agents/research.mds +176 -0
- package/src/assets/agents/review.mds +286 -0
- package/src/assets/agents/scrutinize.mds +132 -0
- package/src/assets/agents/simplify.md +20 -0
- package/src/assets/agents/skim.mds +161 -0
- package/src/assets/agents/synthesize.md +12 -0
- package/src/assets/agents/test.md +2 -2
- package/src/assets/agents/triage.mds +194 -0
- package/src/assets/agents/validate.md +15 -8
- package/src/assets/commands/_partials/_compliance.mds +5 -4
- package/src/assets/commands/_partials/_decisions.mds +31 -0
- package/src/assets/commands/_partials/_engine.mds +29 -24
- package/src/assets/commands/_partials/_knowledge.mds +26 -13
- package/src/assets/commands/_partials/_plan_contract.mds +1 -1
- package/src/assets/commands/_partials/_preamble.mds +33 -9
- package/src/assets/commands/_partials/_publication.mds +5 -4
- package/src/assets/commands/_partials/_settings.mds +13 -5
- package/src/assets/commands/_partials/_wave.mds +16 -6
- package/src/assets/commands/bug-analysis.mds +24 -2
- package/src/assets/commands/code-review.mds +148 -45
- package/src/assets/commands/debug.mds +17 -1
- package/src/assets/commands/dynamic-build.mds +144 -65
- package/src/assets/commands/dynamic-plan.mds +36 -6
- package/src/assets/commands/dynamic-profile.mds +9 -1
- package/src/assets/commands/dynamic-tickets.mds +16 -2
- package/src/assets/commands/explore.mds +27 -1
- package/src/assets/commands/implement.mds +171 -138
- package/src/assets/commands/plan.mds +47 -8
- package/src/assets/commands/{release.md → release.mds} +27 -24
- package/src/assets/commands/research.mds +28 -4
- package/src/assets/commands/resolve.mds +69 -17
- package/src/assets/commands/self-review.mds +43 -13
- package/src/assets/mds/tracker/_common.mds +1 -1
- package/src/assets/mds/tracker/_contract.mds +72 -0
- package/src/assets/mds/tracker/_github.mds +13 -2
- package/src/assets/mds/tracker/_jira.mds +17 -5
- package/src/assets/mds/tracker/_linear.mds +17 -5
- package/src/assets/mds/tracker/_mcp.mds +2 -2
- package/src/assets/mds/tracker/_steps.mds +97 -0
- package/src/assets/rules/context-economy.md +10 -0
- package/src/assets/rules/go.md +1 -0
- package/src/assets/rules/java.md +1 -0
- package/src/assets/rules/python.md +1 -0
- package/src/assets/rules/rust.md +1 -0
- package/src/assets/rules/typescript.md +1 -0
- package/src/assets/scripts/ci-wait.cjs +636 -0
- package/src/assets/scripts/claude-md-audit.cjs +611 -0
- package/src/assets/scripts/hooks/assets/orchestrator-charter.md +2 -3
- package/src/assets/scripts/hooks/background-memory-update +290 -9
- package/src/assets/scripts/hooks/json-helper.cjs +13 -5
- package/src/assets/scripts/hooks/json-parse +34 -10
- package/src/assets/scripts/hooks/session-start-context +315 -7
- package/src/assets/scripts/release-trace.cjs +27 -10
- package/src/assets/skills/accessibility/SKILL.md +1 -1
- package/src/assets/skills/apply-decisions/SKILL.md +12 -82
- package/src/assets/skills/apply-feature-knowledge/SKILL.md +9 -43
- package/src/assets/skills/architecture/SKILL.md +1 -1
- package/src/assets/skills/boundary-validation/SKILL.md +1 -1
- package/src/assets/skills/complexity/SKILL.md +1 -1
- package/src/assets/skills/compliance/SKILL.md +1 -1
- package/src/assets/skills/consistency/SKILL.md +1 -1
- package/src/assets/skills/database/SKILL.md +1 -1
- package/src/assets/skills/dependencies/SKILL.md +1 -1
- package/src/assets/skills/dependency-research/SKILL.md +3 -6
- package/src/assets/skills/design-review/SKILL.md +1 -1
- package/src/assets/skills/docs-framework/SKILL.md +1 -1
- package/src/assets/skills/documentation/SKILL.md +1 -1
- package/src/assets/skills/feature-knowledge/SKILL.md +43 -12
- package/src/assets/skills/gap-analysis/SKILL.md +1 -1
- package/src/assets/skills/git/SKILL.md +1 -1
- package/src/assets/skills/go/SKILL.md +1 -1
- package/src/assets/skills/java/SKILL.md +1 -1
- package/src/assets/skills/patterns/SKILL.md +1 -1
- package/src/assets/skills/performance/SKILL.md +1 -1
- package/src/assets/skills/python/SKILL.md +1 -1
- package/src/assets/skills/qa/SKILL.md +1 -3
- package/src/assets/skills/quality-gates/SKILL.md +10 -13
- package/src/assets/skills/quality-gates/references/report-template.md +20 -20
- package/src/assets/skills/react/SKILL.md +1 -1
- package/src/assets/skills/regression/SKILL.md +1 -1
- package/src/assets/skills/reliability/SKILL.md +1 -1
- package/src/assets/skills/research-codebase/SKILL.md +1 -1
- package/src/assets/skills/research-competitor/SKILL.md +1 -1
- package/src/assets/skills/research-external/SKILL.md +1 -1
- package/src/assets/skills/research-technology/SKILL.md +1 -1
- package/src/assets/skills/review-methodology/SKILL.md +1 -1
- package/src/assets/skills/rust/SKILL.md +1 -1
- package/src/assets/skills/security/SKILL.md +1 -1
- package/src/assets/skills/software-design/SKILL.md +1 -1
- package/src/assets/skills/test-driven-development/SKILL.md +9 -29
- package/src/assets/skills/testing/SKILL.md +1 -1
- package/src/assets/skills/typescript/SKILL.md +1 -1
- package/src/assets/skills/ui-design/SKILL.md +1 -1
- package/src/assets/skills/worktree-support/SKILL.md +3 -55
- package/src/assets/skills/worktree-support/references/discovery.md +48 -0
- package/src/assets/skills/worktree-support/references/roots.md +2 -2
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
# background-memory-update — Eager working-memory refresh worker
|
|
4
4
|
# Spawned as a detached background process by memory-worker (Stop hook) after the
|
|
5
|
-
# 120s throttle expires. Drains .pending-turns.jsonl → calls `claude -p`
|
|
5
|
+
# 120s throttle expires. Drains .pending-turns.jsonl → calls `claude -p` on the model
|
|
6
|
+
# and effort the `agents.memory` entry of ~/.devflow/agent-models.json names, else the
|
|
7
|
+
# shipped worker default, in a lean session (D-MEMORY-WORKER-LEAN) →
|
|
6
8
|
# rewrites WORKING-MEMORY.md with a git-reconciliation stamp on line 1.
|
|
7
9
|
#
|
|
8
10
|
# Applies the LLM-vs-plumbing principle: artifact content (WORKING-MEMORY.md) is
|
|
@@ -118,6 +120,92 @@ if ! df_no_symlink_below "$PROJECT_ROOT" "$LOCK_DIR" "$QUEUE_FILE" "$PROCESSING_
|
|
|
118
120
|
exit 0
|
|
119
121
|
fi
|
|
120
122
|
|
|
123
|
+
# --- CLI version probe: choose the argv form (D-MEMORY-WORKER-LEAN) ---
|
|
124
|
+
# The lean argv (--effort, --safe-mode, --max-turns, ...) is verified from
|
|
125
|
+
# MEMORY_MIN_CLAUDE_VERSION up. A CLI older than that, or one whose version cannot be
|
|
126
|
+
# read, gets the legacy argv instead, because one flag a CLI does not know fails every
|
|
127
|
+
# run. The probe runs here, once $CLAUDE_BIN has resolved and the link check has
|
|
128
|
+
# passed, and before the lock is taken, so a slow or hung `--version` can never extend
|
|
129
|
+
# the lock's hold. It is bounded by DEVFLOW_BG_VERSION_PROBE_SECS (default 10, at most
|
|
130
|
+
# 60; tests override it as they do DEVFLOW_BG_WATCHDOG_SECS) with the watchdog's
|
|
131
|
+
# background-and-kill idiom. Its output is parsed, never executed.
|
|
132
|
+
#
|
|
133
|
+
# Errexit safety: the worker runs under `set -e`, and a helper's last non-zero status
|
|
134
|
+
# aborts the caller, so each helper below ends in an explicit return status and is
|
|
135
|
+
# called inside an `if` condition.
|
|
136
|
+
MEMORY_MIN_CLAUDE_VERSION="2.1.286"
|
|
137
|
+
|
|
138
|
+
# Sets CLAUDE_VERSION_TEXT to the first 512 bytes `claude --version` printed ("" on any
|
|
139
|
+
# failure). Returns 0 when it exited 0 inside the bound, else 1. A hung probe gets
|
|
140
|
+
# SIGTERM to its own process group (set -m, as for the spawn below) after the bound
|
|
141
|
+
# and SIGKILL 2s later; the watchdog subshell has its descriptors closed so a sleep it
|
|
142
|
+
# leaves behind holds no pipe open.
|
|
143
|
+
probe_claude_version() {
|
|
144
|
+
local _secs _out _pid _wd_pid _status=1
|
|
145
|
+
CLAUDE_VERSION_TEXT=""
|
|
146
|
+
_secs="${DEVFLOW_BG_VERSION_PROBE_SECS:-10}"
|
|
147
|
+
case "$_secs" in
|
|
148
|
+
''|*[!0123456789]*|????*) _secs=10 ;;
|
|
149
|
+
esac
|
|
150
|
+
if [ "$_secs" -lt 1 ] || [ "$_secs" -gt 60 ]; then _secs=10; fi
|
|
151
|
+
_out=$(mktemp "${TMPDIR:-/tmp}/devflow-claude-version.XXXXXX" 2>/dev/null) || return 1
|
|
152
|
+
set -m
|
|
153
|
+
"$CLAUDE_BIN" --version </dev/null >"$_out" 2>/dev/null &
|
|
154
|
+
_pid=$!
|
|
155
|
+
set +m
|
|
156
|
+
(
|
|
157
|
+
sleep "$_secs"
|
|
158
|
+
kill -TERM -"$_pid" 2>/dev/null || true
|
|
159
|
+
sleep 2
|
|
160
|
+
kill -KILL -"$_pid" 2>/dev/null || true
|
|
161
|
+
) </dev/null >/dev/null 2>&1 &
|
|
162
|
+
_wd_pid=$!
|
|
163
|
+
if wait "$_pid" 2>/dev/null; then _status=0; fi
|
|
164
|
+
kill "$_wd_pid" 2>/dev/null || true
|
|
165
|
+
wait "$_wd_pid" 2>/dev/null || true
|
|
166
|
+
if [ "$_status" -eq 0 ]; then
|
|
167
|
+
CLAUDE_VERSION_TEXT=$(head -c 512 "$_out" 2>/dev/null || true)
|
|
168
|
+
fi
|
|
169
|
+
rm -f "$_out" 2>/dev/null || true
|
|
170
|
+
return "$_status"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# Returns 0 when the first X.Y.Z in $1 is at or above the first X.Y.Z in $2, comparing
|
|
174
|
+
# the three components as base-10 numbers left to right (2.10.0 is above 2.1.286, and
|
|
175
|
+
# a leading zero is not octal). Returns 1 when it is below, or when either text holds
|
|
176
|
+
# no X.Y.Z or a component longer than six digits. The digit set is spelled out: a
|
|
177
|
+
# range such as 0-9 collates by locale on bash 3.2.
|
|
178
|
+
claude_version_at_least() {
|
|
179
|
+
local _text="$1" _floor="$2" _re='([0123456789]+)\.([0123456789]+)\.([0123456789]+)'
|
|
180
|
+
local _f1 _f2 _f3 _v1 _v2 _v3
|
|
181
|
+
[[ "$_floor" =~ $_re ]] || return 1
|
|
182
|
+
_f1=${BASH_REMATCH[1]}; _f2=${BASH_REMATCH[2]}; _f3=${BASH_REMATCH[3]}
|
|
183
|
+
[[ "$_text" =~ $_re ]] || return 1
|
|
184
|
+
_v1=${BASH_REMATCH[1]}; _v2=${BASH_REMATCH[2]}; _v3=${BASH_REMATCH[3]}
|
|
185
|
+
if [ "${#_v1}" -gt 6 ] || [ "${#_v2}" -gt 6 ] || [ "${#_v3}" -gt 6 ]; then return 1; fi
|
|
186
|
+
_v1=$((10#$_v1)); _v2=$((10#$_v2)); _v3=$((10#$_v3))
|
|
187
|
+
_f1=$((10#$_f1)); _f2=$((10#$_f2)); _f3=$((10#$_f3))
|
|
188
|
+
if [ "$_v1" -ne "$_f1" ]; then
|
|
189
|
+
if [ "$_v1" -gt "$_f1" ]; then return 0; fi
|
|
190
|
+
return 1
|
|
191
|
+
fi
|
|
192
|
+
if [ "$_v2" -ne "$_f2" ]; then
|
|
193
|
+
if [ "$_v2" -gt "$_f2" ]; then return 0; fi
|
|
194
|
+
return 1
|
|
195
|
+
fi
|
|
196
|
+
if [ "$_v3" -ge "$_f3" ]; then return 0; fi
|
|
197
|
+
return 1
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
CLAUDE_ARGV_FORM="legacy"
|
|
201
|
+
if ! probe_claude_version; then
|
|
202
|
+
log "claude --version probe failed or timed out — using the legacy argv"
|
|
203
|
+
elif claude_version_at_least "$CLAUDE_VERSION_TEXT" "$MEMORY_MIN_CLAUDE_VERSION"; then
|
|
204
|
+
CLAUDE_ARGV_FORM="lean"
|
|
205
|
+
else
|
|
206
|
+
log "claude --version is below ${MEMORY_MIN_CLAUDE_VERSION} or unparseable — using the legacy argv"
|
|
207
|
+
fi
|
|
208
|
+
|
|
121
209
|
# --- Worker-level lock (300s stale-break — much longer than learning-lock's 30s) ---
|
|
122
210
|
# This prevents a second worker (spawned 121s later) from double-writing WORKING-MEMORY.md
|
|
123
211
|
# while the first worker's claude -p call (up to 120s) is still in flight.
|
|
@@ -171,7 +259,20 @@ fi
|
|
|
171
259
|
# Clean up any staged file left by a watchdog-killed prior run.
|
|
172
260
|
# Without this, a stale staged file with a valid stamp would be mistakenly
|
|
173
261
|
# mv-ed to the real path on the NEXT run's CAS check (D-MEMORY-STAGED-CAS).
|
|
262
|
+
#
|
|
263
|
+
# D-MEMORY-WORKER-LEAN: the removal is also a CHECKED precondition. The model gets the
|
|
264
|
+
# Write tool alone, and Write refuses a file that exists and has not been read, which
|
|
265
|
+
# is how an earlier Write-only worker failed silently: every Write errored while the
|
|
266
|
+
# run exited 0. Once nothing stands at the staged path, the one Write is always a
|
|
267
|
+
# create, so Write-only is safe. A path `rm -f` cannot clear (a directory, say) would
|
|
268
|
+
# make every Write fail, so the run stops here instead: no LLM call, the batch and
|
|
269
|
+
# the queue untouched (nothing has been claimed yet), the lock released by the EXIT
|
|
270
|
+
# trap. `-e` or `-L`, so a link there counts as present.
|
|
174
271
|
rm -f "$STAGED_FILE" 2>/dev/null || true
|
|
272
|
+
if [ -e "$STAGED_FILE" ] || [ -L "$STAGED_FILE" ]; then
|
|
273
|
+
log "SKIP: $STAGED_FILE could not be removed — a Write to it would fail, so no LLM run; the batch and the queue are left as they are"
|
|
274
|
+
exit 0
|
|
275
|
+
fi
|
|
175
276
|
|
|
176
277
|
# --- Orphan-only skip: if queue has no assistant/qa turn, exit and leave the queue ---
|
|
177
278
|
# This prevents fabrication-prone LLM runs with only user turns in the queue.
|
|
@@ -500,7 +601,107 @@ Instructions:
|
|
|
500
601
|
EOF
|
|
501
602
|
)
|
|
502
603
|
|
|
503
|
-
|
|
604
|
+
# --- Model and effort (D-MEMORY-WORKER-LEAN) ---
|
|
605
|
+
# One authority: WORKER_AGENTS.memory in src/core/agent-models.ts, which `devflow
|
|
606
|
+
# agents` shows as the worker's default. The two defaults below are that row spelled
|
|
607
|
+
# out for bash, held equal to it by tests/agent-models-worker.test.ts, as are the
|
|
608
|
+
# alias and effort lists and the name rules. A personal `agents.memory` entry in
|
|
609
|
+
# $HOME/.devflow/agent-models.json (the machine root; nothing relocates it) overrides
|
|
610
|
+
# them. Each field is validated on its own against the worker domain, so a bad model
|
|
611
|
+
# keeps a good effort and the reverse:
|
|
612
|
+
# model = `default` (the shipped one), one of the aliases, or a `claude-` name;
|
|
613
|
+
# a name starts with a letter or digit, continues with those and . _ -,
|
|
614
|
+
# and is 64 characters at most (MODEL_NAME_RE in src/core/agent-frontmatter.ts)
|
|
615
|
+
# effort = `default` (the shipped one) or one of the levels
|
|
616
|
+
# `inherit` is outside both, since a background worker has no session to inherit from.
|
|
617
|
+
# Only a value that passed its match reaches argv. A value that is absent, empty or
|
|
618
|
+
# not a JSON string reads as absent and takes the shipped one silently; a string that
|
|
619
|
+
# fails its match takes it with one log line naming the field, never the value.
|
|
620
|
+
# The character sets are spelled out because a range such as A-Za-z matches accented
|
|
621
|
+
# letters in a UTF-8 locale on bash 3.2.
|
|
622
|
+
MEMORY_MODEL_DEFAULT="claude-sonnet-5-5"
|
|
623
|
+
MEMORY_EFFORT_DEFAULT="high"
|
|
624
|
+
MEMORY_MODEL_ALIASES="haiku sonnet opus fable"
|
|
625
|
+
MEMORY_MODEL_PREFIX="claude-"
|
|
626
|
+
MEMORY_MODEL_MAX_LEN=64
|
|
627
|
+
MEMORY_EFFORT_LEVELS="low medium high xhigh max"
|
|
628
|
+
MEMORY_NAME_HEAD="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
629
|
+
MEMORY_EFFORT_CHARS="abcdefghijklmnopqrstuvwxyz"
|
|
630
|
+
|
|
631
|
+
# Prints the string value of agents.memory.<field> followed by one `x` (the caller strips
|
|
632
|
+
# it, so a value's own trailing newline survives the command substitution). Prints just
|
|
633
|
+
# the `x` when the file, the field or a string there is missing. Always returns 0.
|
|
634
|
+
memory_agent_field() {
|
|
635
|
+
local _file="$HOME/.devflow/agent-models.json"
|
|
636
|
+
if [ "$_JSON_AVAILABLE" = "true" ] && [ -f "$_file" ]; then
|
|
637
|
+
json_string_field_file "$_file" "agents.memory.$1" 2>/dev/null || true
|
|
638
|
+
fi
|
|
639
|
+
printf x
|
|
640
|
+
return 0
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
# Sets MEMORY_MODEL. Always returns 0.
|
|
644
|
+
resolve_memory_model() {
|
|
645
|
+
local _raw _tail="${MEMORY_NAME_HEAD}._-" _ok=""
|
|
646
|
+
MEMORY_MODEL="$MEMORY_MODEL_DEFAULT"
|
|
647
|
+
_raw=$(memory_agent_field model)
|
|
648
|
+
_raw=${_raw%x}
|
|
649
|
+
case "$_raw" in
|
|
650
|
+
''|default) return 0 ;;
|
|
651
|
+
[!$MEMORY_NAME_HEAD]*|*[!$_tail]*) ;;
|
|
652
|
+
*)
|
|
653
|
+
if [ "${#_raw}" -le "$MEMORY_MODEL_MAX_LEN" ]; then
|
|
654
|
+
case " $MEMORY_MODEL_ALIASES " in
|
|
655
|
+
*" $_raw "*) _ok="yes" ;;
|
|
656
|
+
esac
|
|
657
|
+
case "$_raw" in
|
|
658
|
+
"$MEMORY_MODEL_PREFIX"?*) _ok="yes" ;;
|
|
659
|
+
esac
|
|
660
|
+
fi
|
|
661
|
+
;;
|
|
662
|
+
esac
|
|
663
|
+
if [ -n "$_ok" ]; then
|
|
664
|
+
MEMORY_MODEL="$_raw"
|
|
665
|
+
else
|
|
666
|
+
log "agents.memory.model: not a model the worker accepts (default, an alias or a ${MEMORY_MODEL_PREFIX} name) — using the shipped model"
|
|
667
|
+
fi
|
|
668
|
+
return 0
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
# Sets MEMORY_EFFORT. Always returns 0.
|
|
672
|
+
resolve_memory_effort() {
|
|
673
|
+
local _raw _ok=""
|
|
674
|
+
MEMORY_EFFORT="$MEMORY_EFFORT_DEFAULT"
|
|
675
|
+
_raw=$(memory_agent_field effort)
|
|
676
|
+
_raw=${_raw%x}
|
|
677
|
+
case "$_raw" in
|
|
678
|
+
''|default) return 0 ;;
|
|
679
|
+
*[!$MEMORY_EFFORT_CHARS]*) ;;
|
|
680
|
+
*)
|
|
681
|
+
case " $MEMORY_EFFORT_LEVELS " in
|
|
682
|
+
*" $_raw "*) _ok="yes" ;;
|
|
683
|
+
esac
|
|
684
|
+
;;
|
|
685
|
+
esac
|
|
686
|
+
if [ -n "$_ok" ]; then
|
|
687
|
+
MEMORY_EFFORT="$_raw"
|
|
688
|
+
else
|
|
689
|
+
log "agents.memory.effort: not an effort level the worker accepts (default or a level) — using the shipped effort"
|
|
690
|
+
fi
|
|
691
|
+
return 0
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
resolve_memory_model
|
|
695
|
+
resolve_memory_effort
|
|
696
|
+
|
|
697
|
+
if [ "$CLAUDE_ARGV_FORM" = "lean" ]; then
|
|
698
|
+
EFFORT_LOG="effort ${MEMORY_EFFORT}"
|
|
699
|
+
ARGV_LOG="lean argv"
|
|
700
|
+
else
|
|
701
|
+
EFFORT_LOG="no effort passed"
|
|
702
|
+
ARGV_LOG="legacy argv"
|
|
703
|
+
fi
|
|
704
|
+
log "Spawning claude -p (model ${MEMORY_MODEL}, ${EFFORT_LOG}, ${ARGV_LOG}, ${TURN_COUNT} turns)"
|
|
504
705
|
# SECURITY: never log PROMPT — it contains turn content which may include secrets
|
|
505
706
|
|
|
506
707
|
# --- Watchdog timeout (overridable for tests: DEVFLOW_BG_WATCHDOG_SECS=2) ---
|
|
@@ -537,14 +738,94 @@ fi
|
|
|
537
738
|
# Restore set +m immediately after capturing CLAUDE_PID so the watchdog subshell and
|
|
538
739
|
# other subsequent background operations stay in the worker's group.
|
|
539
740
|
# Job-control notifications go to stderr which is redirected to the log — suppress them.
|
|
741
|
+
#
|
|
742
|
+
# D-MEMORY-WORKER-LEAN — the spawn. The worker opens a headless session to make one
|
|
743
|
+
# Write, so the session is cut to what that Write needs. Prompt on stdin, output and
|
|
744
|
+
# errors to the log, as before. Two argv forms, chosen by the version probe above:
|
|
745
|
+
#
|
|
746
|
+
# lean (CLI at or above MEMORY_MIN_CLAUDE_VERSION):
|
|
747
|
+
# -p --model M --effort E --safe-mode --tools Write --disable-slash-commands \
|
|
748
|
+
# --strict-mcp-config --mcp-config '{"mcpServers":{}}' --max-turns 3 \
|
|
749
|
+
# --dangerously-skip-permissions --output-format text
|
|
750
|
+
# legacy (older, unreadable or timed-out probe):
|
|
751
|
+
# -p --model M --tools Write --dangerously-skip-permissions --output-format text
|
|
752
|
+
#
|
|
753
|
+
# What each flag is for:
|
|
754
|
+
# --model, --effort the resolved agents.memory values above. --effort is the only
|
|
755
|
+
# effort source: an inherited CLAUDE_CODE_EFFORT_LEVEL beats the
|
|
756
|
+
# flag (measured on 2026-10-09), so it is unset below, for both
|
|
757
|
+
# forms, and the log's "no effort passed" is then true.
|
|
758
|
+
# --safe-mode starts with the user's customisations off: instruction files,
|
|
759
|
+
# skills, plugins, hooks, MCP servers, custom commands and agents.
|
|
760
|
+
# Auth, model selection and built-in tools work as usual, and
|
|
761
|
+
# settings `env` (the provider URL and key) still applies under
|
|
762
|
+
# it (measured on 2026-10-09), so a routed or keyed install works.
|
|
763
|
+
# --tools Write Write is the only tool. `--allowedTools` only pre-approves, it
|
|
764
|
+
# leaves every tool, listing and MCP server loaded.
|
|
765
|
+
# --disable-slash-commands, --strict-mcp-config with an empty --mcp-config
|
|
766
|
+
# no slash-command or skill listing, no MCP server and none of the
|
|
767
|
+
# instructions they add to the context.
|
|
768
|
+
# --max-turns 3 the worker's own pattern is one Write and then the end of the
|
|
769
|
+
# turn, two requests; the third is margin. A run that reaches the
|
|
770
|
+
# cap exits non-zero, so its batch stays for the retry; the Write
|
|
771
|
+
# on the capped turn still runs. 3 was verified on claude 2.1.294
|
|
772
|
+
# on 2026-10-09 (cap 1 exits 1, caps 2 and 3 exit 0 on that
|
|
773
|
+
# pattern). `--max-turns` is not listed in `claude --help` there
|
|
774
|
+
# but is registered (`--max-turns abc` errors "must be a number"),
|
|
775
|
+
# which is why it was exercised rather than read off the help.
|
|
776
|
+
# --dangerously-skip-permissions headless: nothing could answer a prompt.
|
|
777
|
+
# --output-format text
|
|
778
|
+
# DEVFLOW_BG_UPDATER=1 the recursion guard stays on both forms, though --safe-mode
|
|
779
|
+
# also disables hooks. CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 and
|
|
780
|
+
# CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 keep the instruction files and
|
|
781
|
+
# auto-memory out on both forms, including the legacy one.
|
|
782
|
+
#
|
|
783
|
+
# Why Write-only is safe: the staged path is empty before every run (the removal and
|
|
784
|
+
# the checked precondition after the lock above), so the one Write is always a create.
|
|
785
|
+
# Write refuses an existing file that has not been read, and that is the whole history
|
|
786
|
+
# of this flag set:
|
|
787
|
+
# a472a490 passed `--tools "Write"` with `--allowedTools` for two memory files;
|
|
788
|
+
# e701cecc widened the tools to `Read,Write,Bash` after counting the silent
|
|
789
|
+
# read-before-write failures (the run exited 0 while every Write errored);
|
|
790
|
+
# baf02265 removed both flags, with no reason in its message;
|
|
791
|
+
# 6186645f raised the worker's model and kept `--allowedTools 'Read,Write'`.
|
|
792
|
+
#
|
|
793
|
+
# The version floor is 2.1.286, the lowest CLI on which the whole lean argv was run
|
|
794
|
+
# and its flags, the settings `env` under --safe-mode and the effort override were
|
|
795
|
+
# checked (2.1.286 and 2.1.292 to 2.1.294 behave identically; 2026-10-09, recorded in
|
|
796
|
+
# docs/reference/platform-assumptions.md). The legacy form carries no --effort,
|
|
797
|
+
# --safe-mode, --max-turns, --disable-slash-commands, --strict-mcp-config or
|
|
798
|
+
# --mcp-config, none of which is verified on older CLIs, so on it effort is not
|
|
799
|
+
# applied, MCP servers stay loaded and there is no turn cap.
|
|
800
|
+
#
|
|
801
|
+
# The turns input has no byte cap: the queue holds every turn whole by design, and the
|
|
802
|
+
# prompt is bounded by the newest-20-rows window above and the 64 KB existing-memory
|
|
803
|
+
# cap, not by cutting a turn.
|
|
804
|
+
unset CLAUDE_CODE_EFFORT_LEVEL
|
|
540
805
|
set -m
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
806
|
+
if [ "$CLAUDE_ARGV_FORM" = "lean" ]; then
|
|
807
|
+
DEVFLOW_BG_UPDATER=1 CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 "$CLAUDE_BIN" -p \
|
|
808
|
+
--model "$MEMORY_MODEL" \
|
|
809
|
+
--effort "$MEMORY_EFFORT" \
|
|
810
|
+
--safe-mode \
|
|
811
|
+
--tools Write \
|
|
812
|
+
--disable-slash-commands \
|
|
813
|
+
--strict-mcp-config \
|
|
814
|
+
--mcp-config '{"mcpServers":{}}' \
|
|
815
|
+
--max-turns 3 \
|
|
816
|
+
--dangerously-skip-permissions \
|
|
817
|
+
--output-format text \
|
|
818
|
+
<<< "$PROMPT" \
|
|
819
|
+
>> "$LOG_FILE" 2>&1 &
|
|
820
|
+
else
|
|
821
|
+
DEVFLOW_BG_UPDATER=1 CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 "$CLAUDE_BIN" -p \
|
|
822
|
+
--model "$MEMORY_MODEL" \
|
|
823
|
+
--tools Write \
|
|
824
|
+
--dangerously-skip-permissions \
|
|
825
|
+
--output-format text \
|
|
826
|
+
<<< "$PROMPT" \
|
|
827
|
+
>> "$LOG_FILE" 2>&1 &
|
|
828
|
+
fi
|
|
548
829
|
CLAUDE_PID=$!
|
|
549
830
|
set +m
|
|
550
831
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// get-field <field> [default] Read field from stdin JSON
|
|
14
14
|
// get-string-field <field> Read field from stdin JSON only when it is a string
|
|
15
15
|
// extract-cwd-field <field> Extract cwd + arbitrary field, SOH-byte delimited
|
|
16
|
-
// session-output <context>
|
|
16
|
+
// session-output <context> [message] Build SessionStart output envelope (+ systemMessage)
|
|
17
17
|
// prompt-output <context> Build UserPromptSubmit output envelope
|
|
18
18
|
// backup-construct Build pre-compact backup JSON from --arg pairs
|
|
19
19
|
// assign-anchor <decision|pitfall> <obs_id>
|
|
@@ -293,13 +293,21 @@ try {
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
case 'session-output': {
|
|
296
|
+
// D-SYSTEMMESSAGE-ENVELOPE (json-parse json_session_output): an optional second
|
|
297
|
+
// argument is a user-facing message, carried in the top-level `systemMessage` key.
|
|
298
|
+
// One argument, or an empty message, gives today's envelope; a message with an
|
|
299
|
+
// empty context gives `systemMessage` alone, with no hookSpecificOutput key.
|
|
296
300
|
const ctx = args[0];
|
|
297
|
-
|
|
298
|
-
|
|
301
|
+
const message = args[1] || '';
|
|
302
|
+
const envelope = {};
|
|
303
|
+
if (message === '' || ctx !== '') {
|
|
304
|
+
envelope.hookSpecificOutput = {
|
|
299
305
|
hookEventName: 'SessionStart',
|
|
300
306
|
additionalContext: ctx,
|
|
301
|
-
}
|
|
302
|
-
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
if (message !== '') envelope.systemMessage = message;
|
|
310
|
+
console.log(JSON.stringify(envelope));
|
|
303
311
|
break;
|
|
304
312
|
}
|
|
305
313
|
|
|
@@ -98,18 +98,42 @@ json_extract_cwd_prompt() {
|
|
|
98
98
|
|
|
99
99
|
# --- Hook output envelopes ---
|
|
100
100
|
|
|
101
|
-
# Build SessionStart output. Usage: json_session_output "$CONTEXT"
|
|
101
|
+
# Build SessionStart output. Usage: json_session_output "$CONTEXT" ["$SYSTEM_MESSAGE"]
|
|
102
|
+
#
|
|
103
|
+
# D-SYSTEMMESSAGE-ENVELOPE: the optional second argument is a message shown to the
|
|
104
|
+
# user, carried in the top-level `systemMessage` key beside (never inside)
|
|
105
|
+
# `hookSpecificOutput`; it does not enter the model's context. With ONE argument the
|
|
106
|
+
# envelope is exactly what every existing caller has always received, byte for byte
|
|
107
|
+
# (ensure-proxy, session-start-orchestrator, session-start-memory and Sections 1-5 of
|
|
108
|
+
# session-start-context pass one). With two:
|
|
109
|
+
# - a message and a context -> both keys, hookSpecificOutput first;
|
|
110
|
+
# - a message and no context -> `systemMessage` alone, with NO hookSpecificOutput key;
|
|
111
|
+
# - an empty message -> the one-argument envelope for that context.
|
|
112
|
+
# The node fallback (json-helper.cjs session-output) builds the same content, compact
|
|
113
|
+
# where jq pretty-prints.
|
|
102
114
|
json_session_output() {
|
|
103
|
-
local ctx="$1"
|
|
104
|
-
if [ "
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
local ctx="$1" msg="${2:-}"
|
|
116
|
+
if [ "$#" -lt 2 ] || [ -z "$msg" ]; then
|
|
117
|
+
if [ "$_HAS_JQ" = "true" ]; then
|
|
118
|
+
jq -n --arg ctx "$ctx" '{
|
|
119
|
+
"hookSpecificOutput": {
|
|
120
|
+
"hookEventName": "SessionStart",
|
|
121
|
+
"additionalContext": $ctx
|
|
122
|
+
}
|
|
123
|
+
}'
|
|
124
|
+
else
|
|
125
|
+
node "$_JSON_HELPER" session-output "$ctx"
|
|
126
|
+
fi
|
|
127
|
+
elif [ "$_HAS_JQ" = "true" ]; then
|
|
128
|
+
jq -n --arg ctx "$ctx" --arg msg "$msg" '
|
|
129
|
+
(if $ctx != "" then {
|
|
130
|
+
"hookSpecificOutput": {
|
|
131
|
+
"hookEventName": "SessionStart",
|
|
132
|
+
"additionalContext": $ctx
|
|
133
|
+
}
|
|
134
|
+
} else {} end) + {"systemMessage": $msg}'
|
|
111
135
|
else
|
|
112
|
-
node "$_JSON_HELPER" session-output "$ctx"
|
|
136
|
+
node "$_JSON_HELPER" session-output "$ctx" "$msg"
|
|
113
137
|
fi
|
|
114
138
|
}
|
|
115
139
|
|