@sabaiway/agent-workflow-kit 3.14.0 → 3.15.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 +23 -0
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/state-block-guard.md +7 -5
- package/tools/recommendations.mjs +47 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ Semantically versioned ([semver](https://semver.org)), newest first. The `versio
|
|
|
4
4
|
is the current release. `upgrade` mode reads a project's `docs/ai/.workflow-version` and applies
|
|
5
5
|
every `migrations/<version>-<slug>.md` newer than it, in semver order.
|
|
6
6
|
|
|
7
|
+
## 3.15.0 — a shipped opt-in now advertises itself (AD-076)
|
|
8
|
+
|
|
9
|
+
`/agent-workflow-kit upgrade` and `recommendations` now offer the closing-block detector that 3.14.0
|
|
10
|
+
shipped.
|
|
11
|
+
|
|
12
|
+
They did not, and that was the whole defect. 3.14.0 added the detector with a mode doc, a catalog row
|
|
13
|
+
and a README row — every surface an agent reads — and no advisor entry. So a user who installed the
|
|
14
|
+
update and ran `upgrade` was told «nothing is broken — process is optimal» while the capability from
|
|
15
|
+
that very version sat unwired and unmentioned. Both statements were true alone. Together they meant
|
|
16
|
+
the only route to the new feature was reading this file or interrogating the agent.
|
|
17
|
+
|
|
18
|
+
The new item fires when no `Stop` hook runs the detector's runtime, states what goes unseen without
|
|
19
|
+
it, and carries a hand-apply pointer to the mode doc — there is still no writer for this hook, and the
|
|
20
|
+
doc carries the exact block plus the three merge cases. It matches on the runtime FILE NAME rather
|
|
21
|
+
than an exact command, because with no writer every user pastes their own path: an exact comparison
|
|
22
|
+
would keep nagging someone who already wired it.
|
|
23
|
+
|
|
24
|
+
The reason this was structurally invited is worth stating. Every other surface of a new mode is
|
|
25
|
+
drift-guarded — omit the `SKILL.md` row, the catalog entry or the mode doc and a test fails. The
|
|
26
|
+
advisor is the one surface with no such guard, and the only one a user receives without asking. The
|
|
27
|
+
guard that would close it needs an accurate claim for all 28 modes, so it is the next slice's first
|
|
28
|
+
item rather than a hurried addition here.
|
|
29
|
+
|
|
7
30
|
## 3.14.0 — the closing state block gets a checker (AD-075)
|
|
8
31
|
|
|
9
32
|
A new opt-in `Stop` hook reads the turn's final assistant message and warns when the closing state
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: agent-workflow-kit
|
|
|
3
3
|
description: Deploy or upgrade a portable AI-agent memory-and-workflow system in any project. Use when the user wants to bootstrap `docs/ai/` + an entry-point `AGENTS.md` (+ `CLAUDE.md` alias) + cap/archive/index enforcement in a new or existing repo, set up the Memory Map and session protocols, install the docs-rotation pre-commit hook, or run `/agent-workflow-kit` / `/agent-workflow-kit upgrade`. Triggers on phrases like "set up the memory system", "deploy the AI workflow here", "bootstrap docs/ai", "upgrade the workflow".
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
metadata:
|
|
6
|
-
version: '3.
|
|
6
|
+
version: '3.15.0'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-kit
|
package/capability.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-kit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -131,7 +131,7 @@ form this family already sanctions. When the detector has earned a writer in rea
|
|
|
131
131
|
"hooks": [
|
|
132
132
|
{
|
|
133
133
|
"type": "command",
|
|
134
|
-
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/state-block-guard.mjs\"
|
|
134
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/state-block-guard.mjs\"",
|
|
135
135
|
"timeout": 10
|
|
136
136
|
}
|
|
137
137
|
]
|
|
@@ -141,10 +141,12 @@ form this family already sanctions. When the detector has earned a writer in rea
|
|
|
141
141
|
}
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
**Add `--require-block` to that command IF your project requires the three-part block on every
|
|
145
|
+
message** — then a turn that drops the block entirely is reported too. It is left OFF above on
|
|
146
|
+
purpose: this kit does not mandate the block, so switching it on for a project that never adopted it
|
|
147
|
+
would warn after nearly every turn. `--require-block` is the **only** argument accepted: an
|
|
148
|
+
unrecognised one makes the guard refuse to judge the turn and say so, rather than quietly running in
|
|
149
|
+
the weaker mode you did not choose.
|
|
148
150
|
|
|
149
151
|
A `Stop` hook is read at session start, so it becomes live in the **next** session, not the current
|
|
150
152
|
one.
|
|
@@ -104,6 +104,7 @@ export const SEVERITIES = Object.freeze({
|
|
|
104
104
|
'read-lane': SEVERITY_OPTIONAL,
|
|
105
105
|
'read-lane.stale': SEVERITY_ATTENTION,
|
|
106
106
|
'read-lane.missing': SEVERITY_ATTENTION,
|
|
107
|
+
'state-block': SEVERITY_OPTIONAL,
|
|
107
108
|
'family-freshness': SEVERITY_ATTENTION,
|
|
108
109
|
'sandbox-masks': SEVERITY_OPTIONAL,
|
|
109
110
|
'agy-adddir': SEVERITY_OPTIONAL,
|
|
@@ -161,6 +162,7 @@ export const WHATS = Object.freeze({
|
|
|
161
162
|
'read-lane': 'the gate hook is wired but the read-only compound lane is off — pipes/chains of seeded reads still prompt one by one',
|
|
162
163
|
'read-lane.stale': 'the read-lane is ON but the placed gate hook is stale — an old hook never reads lanes.json, so the lane is silently dark; reseed it',
|
|
163
164
|
'read-lane.missing': 'the gate hook is wired but its placed file is missing — every Bash call errors and the read-lane is dark; re-place it',
|
|
165
|
+
'state-block': 'nothing checks the closing state block — a turn that ends on «nothing needed from you», or on a promise it never started, passes unseen',
|
|
164
166
|
'family-freshness': '{parts}',
|
|
165
167
|
'sandbox-masks': '{n} sandbox device mask(s) clutter git status — the managed exclude block is absent or stale',
|
|
166
168
|
'sandbox-masks.stale-real': '{n} sandbox device mask(s) clutter git status — the exclude block is stale; {m} fenced entr(ies) are REAL paths (a fresh apply drops them)',
|
|
@@ -214,6 +216,7 @@ export const BENEFITS = Object.freeze({
|
|
|
214
216
|
'gate-hook': 'velocity — your own declared gate commands auto-approve byte-exactly (opt-in PreToolUse hook)',
|
|
215
217
|
'commit-guard': 'integrity — commits require the ONE green --final receipt at the exact staged fingerprint (consented pre-commit arm)',
|
|
216
218
|
'read-lane': 'velocity — pipes/chains of your seeded read-only commands auto-approve instead of prompting (opt-in, conservatively classified)',
|
|
219
|
+
'state-block': 'no silent stalls — a turn ending on «you are not needed», or on work it never started, warns at once instead of waiting to be spotted',
|
|
217
220
|
'family-freshness': 'currency — placed family members carry the latest shipped fixes and features',
|
|
218
221
|
'sandbox-masks': 'zero clutter — git status shows only your changes (the review domain already ignores the masks by construction)',
|
|
219
222
|
'agy-adddir': 'large reviews — an oversized agy code review offloads to a staging dir instead of refusing',
|
|
@@ -439,6 +442,26 @@ const probeReadLane = ({ root, deps, add, skip }) => {
|
|
|
439
442
|
}
|
|
440
443
|
};
|
|
441
444
|
|
|
445
|
+
// The state-block-guard offer (AD-075 · OPT-IN-SHIPS-INVISIBLE). This item exists because its absence
|
|
446
|
+
// fired: 3.14.0 shipped the detector with a mode doc, a catalog row and a README row — and no advisor
|
|
447
|
+
// entry — so `upgrade` reported «nothing is broken» to a user who did not have it. There is no writer
|
|
448
|
+
// for this hook, so the apply is a HAND-APPLY pointer at the mode doc, which carries the exact block
|
|
449
|
+
// and the three merge cases.
|
|
450
|
+
const probeStateBlockHook = ({ root, deps, add, skip }) => {
|
|
451
|
+
try {
|
|
452
|
+
const project = readSettingsFile(join(root, SETTINGS_FILE), { ...deps, cwd: root });
|
|
453
|
+
const local = readSettingsFile(join(root, SETTINGS_LOCAL_FILE), { ...deps, cwd: root });
|
|
454
|
+
if (isStateBlockGuardWired(project.data) || isStateBlockGuardWired(local.data)) return; // converged
|
|
455
|
+
add(
|
|
456
|
+
'state-block',
|
|
457
|
+
fillTemplate(WHATS['state-block'], {}),
|
|
458
|
+
`HAND-APPLY: add a Stop hook running ${STATE_BLOCK_HOOK_COMMAND} to ${SETTINGS_FILE} — the exact block and the three merge cases are in references/modes/state-block-guard.md`,
|
|
459
|
+
);
|
|
460
|
+
} catch (err) {
|
|
461
|
+
skip('state-block', err);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
442
465
|
const probeFamilyFreshness = ({ deps, add, skip }) => {
|
|
443
466
|
try {
|
|
444
467
|
const survey = deps.surveyFamily ?? surveyFamily;
|
|
@@ -617,6 +640,29 @@ export const READ_LANE_KEY = 'readLane';
|
|
|
617
640
|
const GATE_HOOK_REL = '.claude/hooks/agent-workflow-gates.mjs';
|
|
618
641
|
const BUNDLED_HOOK_ABS = join(HERE, '..', 'references', 'hooks', 'gate-approve.mjs');
|
|
619
642
|
|
|
643
|
+
// The state-block-guard wiring the advisor looks for. Matched on the RUNTIME FILE NAME inside the
|
|
644
|
+
// command, not on an exact string: the hook has no writer, so every user pastes their own path —
|
|
645
|
+
// a copy under `.claude/hooks/`, a kit-source path, `--require-block` or not. Any Stop entry that
|
|
646
|
+
// runs this runtime counts as wired, which is the honest question ("is it watching?").
|
|
647
|
+
export const STATE_BLOCK_HOOK_RUNTIME = 'state-block-guard.mjs';
|
|
648
|
+
// NO `--require-block` in the offered command. That flag turns on the absent-block report, and this
|
|
649
|
+
// kit does not mandate the three-part closing block — recommending it to every project would hand
|
|
650
|
+
// them a hook that warns after nearly every turn. The mode doc explains when to add it. (Offering the
|
|
651
|
+
// strict flag by default would also contradict the very reason the report was made opt-in.)
|
|
652
|
+
export const STATE_BLOCK_HOOK_COMMAND = `node "$CLAUDE_PROJECT_DIR/.claude/hooks/${STATE_BLOCK_HOOK_RUNTIME}"`;
|
|
653
|
+
const runsStateBlockGuard = (hook) => typeof hook?.command === 'string' && hook.command.includes(STATE_BLOCK_HOOK_RUNTIME);
|
|
654
|
+
// Both entry shapes count. The matcher-group form (an object carrying its own `hooks` array) is what
|
|
655
|
+
// this family ships and what was observed firing; a flat entry is accepted too, because the question
|
|
656
|
+
// this probe answers is "is anything watching?" — and guessing NO for a wiring that works would nag
|
|
657
|
+
// someone who already did the work.
|
|
658
|
+
const isStateBlockGuardWired = (data) => {
|
|
659
|
+
const entries = data?.hooks?.Stop;
|
|
660
|
+
if (!Array.isArray(entries)) return false;
|
|
661
|
+
return entries.some((entry) => (Array.isArray(entry?.hooks)
|
|
662
|
+
? entry.hooks.some(runsStateBlockGuard)
|
|
663
|
+
: runsStateBlockGuard(entry)));
|
|
664
|
+
};
|
|
665
|
+
|
|
620
666
|
// Byte-compare the placed gate hook against the bundled runtime. A read error (an unreadable placed
|
|
621
667
|
// hook, a broken kit bundle) propagates → the probe states a skip, never a wrong currency verdict.
|
|
622
668
|
const isPlacedHookCurrent = (root, deps) => {
|
|
@@ -879,6 +925,7 @@ const PROBES = Object.freeze([
|
|
|
879
925
|
probeGates,
|
|
880
926
|
probeCommitGuard,
|
|
881
927
|
probeReadLane,
|
|
928
|
+
probeStateBlockHook,
|
|
882
929
|
probeFamilyFreshness,
|
|
883
930
|
probeMasksItem,
|
|
884
931
|
probeAgyAdddir,
|