@sabaiway/agent-workflow-kit 5.8.0 → 5.10.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 +83 -0
- package/SKILL.md +1 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +32 -11
- package/bridges/antigravity-cli-bridge/bin/agy-envelope.mjs +160 -0
- package/bridges/antigravity-cli-bridge/bin/agy-envelope.test.mjs +235 -0
- package/bridges/antigravity-cli-bridge/bin/agy-review-honesty.test.mjs +23 -1
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +242 -38
- package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +482 -38
- package/bridges/antigravity-cli-bridge/capability.json +3 -2
- package/bridges/antigravity-cli-bridge/references/models-and-flags.md +45 -12
- package/bridges/antigravity-cli-bridge/references/review-prompt.md +6 -3
- package/bridges/antigravity-cli-bridge/setup/README.md +18 -5
- package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/hooks/state-block-guard.mjs +107 -45
- package/references/modes/bootstrap.md +6 -2
- package/references/modes/set-recipe.md +8 -5
- package/references/modes/state-block-guard.md +39 -31
- package/references/modes/upgrade.md +8 -5
- package/references/scripts/check-docs-size-cli.test.mjs +7 -6
- package/references/scripts/check-docs-size-ensure.test.mjs +332 -0
- package/references/scripts/check-docs-size.mjs +181 -30
- package/references/shared/composition-handoff.md +10 -0
- package/references/shared/report-footer.md +2 -2
- package/references/templates/agent_rules.md +1 -0
- package/tools/detect-backends.mjs +1 -0
- package/tools/doc-parity.mjs +5 -1
- package/tools/ensure-configs.mjs +37 -19
- package/tools/ensure-ops.mjs +79 -1
- package/tools/ensure-vocabulary.mjs +17 -3
- package/tools/known-footprint.mjs +10 -0
- package/tools/lens-region.mjs +13 -1
- package/tools/source-size-scope.mjs +3 -1
- package/tools/upgrade-runlist.mjs +1 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"schema": 1,
|
|
4
4
|
"name": "antigravity-cli-bridge",
|
|
5
5
|
"kind": "execution-backend",
|
|
6
|
-
"version": "5.
|
|
6
|
+
"version": "5.3.0",
|
|
7
7
|
"provides": ["review", "probe"],
|
|
8
8
|
"posture": { "model": "Gemini 3.7 Flash (High)" },
|
|
9
9
|
"roles": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
],
|
|
34
34
|
"receipt": "side effect — a successful review appends one JSON receipt line to <git dir>/agent-workflow-review-receipts.jsonl (AW_REVIEW_RECEIPTS overrides; plan/diff outside a git tree: warn + skip unless overridden): fingerprint = sha256 over the canonical uncommitted-state payload (staged diff + unstaged diff + untracked-not-ignored contents — the review-payload domain; never-committable untracked paths — character/block devices, FIFOs, sockets — are excluded from the domain entirely, untracked symlinks/directories ride as name-only notes) in code mode, the artifact-file sha256 in plan/diff mode; verdict recorded verbatim from the mandated '### Verdict' section (SHIP / SHIP WITH NITS / REWORK); grounded = whether a NON-EMPTY --facts payload was supplied (code mode refuses pre-spend without one — no run, no receipt — unless --ungrounded/AGY_PROBE=1; in plan/diff an empty payload records grounded:false — fail-closed, the state gate rejects it), factsHash = sha256 of the facts payload; a continuation receipt is fresh:false (informational-only — it cannot attest the folded tree); probe = whether the run relaxed the quality guards (AGY_PROBE=1), written on EVERY receipt so it self-declares — the kit's review-state gate rejects a probe-marked receipt (a probe review never attests) and equally rejects an unmarked one (silence is not a declaration); posture = the ACTUAL run posture {model} (agy has no tier), written on EVERY receipt (D5) — the gate rejects a receipt with an absent/invalid posture (a pre-D5 wrapper minted it; re-run the review), one stderr banner line states the same posture, an ATTESTING review with AGY_MODEL explicitly emptied refuses pre-spend, and a model string carrying control bytes refuses pre-spend in every mode; delivery = how the change set REACHED the model, currently emitted as 'inline' (the whole set rode one prompt — proven by construction) or 'fed' (a chunked feed whose per-part echo proof verified); REQUIRED on every agy code receipt and its ABSENCE is what stops a pre-fed-lane receipt attesting, while the gate accepts any well-formed declaration rather than a particular value; absent by construction on plan/diff/continuation receipts, which carry no change set; a run whose output carries NO recognized '### Verdict' section — empty output included — exits 4 with NO receipt (D4: a FAILED review to RE-RUN, never a fatal session error); when the dispatch nonce seam is supplied — the AW_REVIEW_NONCE environment value or its plain-argument equivalent --nonce <n> (one seam: the flag assigns the same value; supplying both with different values refuses pre-spend) — under the safe grammar [A-Za-z0-9._-]{1,64} (anything else refuses pre-spend), the wrapper first mints the finding MANIFEST {schema, backend, nonce, fingerprint, findings} beside the receipts file (agent-workflow-finding-manifest-<backend>-<nonce>.json; atomic, no-clobber — a byte-identical rewrite is an idempotent no-op, different bytes refuse loudly) ORDERED before the receipt append — a failed manifest write EXCLUDES the receipt append, so a nonce-supplied dispatch can never land a receipt without its readable manifest; a nonce-less invocation adds NO nonce field and mints NO finding manifest (the existing wrapperVersion field still changes with each bridge release); a write failure warns, never fails the review",
|
|
35
35
|
"notes": [
|
|
36
|
+
"transport: every review dispatch drives the CLI in --output-format json (plus --disable-slash-commands) and the returned envelope is parsed in node (bin/agy-envelope.mjs) — the operator-facing invocations and flags above do NOT change, and on a ZERO exit the wrapper still PRINTS the review text, never JSON. A missing or unreadable envelope on a zero exit is a loud failure with NO receipt, never a downgraded verdict and never a fallback to raw-stdout parsing; a non-zero CLI exit keeps its own code and message, and publishes the captured stdout unchanged from the SINGLE dispatch or the FINAL fed turn (which may therefore be a JSON or partial payload — the envelope is parsed only on a zero exit); an INTERMEDIATE feed turn is the exception, its output stays private (Invariant E) and its failure prints only a named error. Enforced by a PRE-SPEND capability probe, not a version floor: agy --help must advertise --output-format and --disable-slash-commands, node must be >= 22, and bin/agy-envelope.mjs must be present — otherwise the review refuses before any run is spent and names the missing capability",
|
|
36
37
|
"pre-dispatch host-diff: before the FIRST dispatch of this bridge, diff its declared networkHosts against the live sandbox allow-list — a missing host is surfaced to the maintainer BEFORE dispatching, never fired into a known prompt",
|
|
37
38
|
"the review posture banner appends a banner-only timeout=<duration> field — exactly the duration agy-run hands to timeout(1); the hard-timeout preflight fails CLOSED when no timeout/gtimeout binary exists (the wrapper refuses by name before any CLI run, so an uncapped review run can no longer happen), and the field never enters the receipt posture or the D5 banner↔receipt parity",
|
|
38
39
|
"quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated posture; a prose re-type drifts"
|
|
@@ -229,7 +230,7 @@
|
|
|
229
230
|
"max": 100000000,
|
|
230
231
|
"default": "240000",
|
|
231
232
|
"appliesTo": ["agy-review"],
|
|
232
|
-
"effect": "agy-review code: the ceiling on the SUM of all outgoing prompt bytes the chunked feed may send (every
|
|
233
|
+
"effect": "agy-review code: the ceiling on the SUM of all outgoing prompt bytes the chunked feed may send (every feed-turn framing block, every body, the final turn). Checked BEFORE the first turn is spent; past it the fed review refuses. An economy guard, not the correctness guard — correctness is the per-part delivery echo."
|
|
233
234
|
}
|
|
234
235
|
],
|
|
235
236
|
"networkHosts": ["*.googleapis.com", "accounts.google.com", "antigravity-unleash.goog", "lh3.googleusercontent.com"],
|
|
@@ -6,13 +6,25 @@ were captured from **v1.1.13**; if the binary disagrees, the binary wins. The wr
|
|
|
6
6
|
|
|
7
7
|
## Headless behaviour
|
|
8
8
|
|
|
9
|
-
Use `-p`, `--print`, or `--prompt` to run one non-interactive prompt and print the text response.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
Use `-p`, `--print`, or `--prompt` to run one non-interactive prompt and print the text response.
|
|
10
|
+
Both wrappers run headless `-p`, and they differ in **transport**:
|
|
11
|
+
|
|
12
|
+
- **`agy-run` stays text.** It prints the CLI's text response unchanged. JSON can ride passthrough
|
|
13
|
+
(`agy-run "…" -- --output-format json`) with NO parsing or validation — if you need structure here,
|
|
14
|
+
ask for Markdown, bullets, tables or fenced blocks and validate the text yourself.
|
|
15
|
+
- **`agy-review` drives every dispatch in `--output-format json`** and reads the returned envelope
|
|
16
|
+
(`bin/agy-envelope.mjs`), so the answer TEXT and the conversation id come from **named fields**
|
|
17
|
+
instead of a guess at raw stdout. The **review contract stays prose-shaped**: the verdict is still
|
|
18
|
+
parsed out of that text's `### Verdict` section, exactly as before. It also passes
|
|
19
|
+
`--disable-slash-commands`, so a delivered change-set line that begins with a slash command stays
|
|
20
|
+
BODY rather than being expanded by the CLI. **On a zero exit** what it PRINTS is the review text,
|
|
21
|
+
never JSON — the envelope is wire format, not output format. When the **single dispatch or the
|
|
22
|
+
FINAL fed turn** exits non-zero the captured stdout is published as-is (the CLI's own failure wins,
|
|
23
|
+
and the envelope is parsed only on a zero exit), so a failing run may print a JSON or partial
|
|
24
|
+
payload. An **intermediate** feed turn is the exception: its output stays private and its failure
|
|
25
|
+
prints only a named error.
|
|
26
|
+
|
|
27
|
+
`--json-schema` is **not** adopted and was measured and rejected; see the flags table below.
|
|
16
28
|
|
|
17
29
|
## Wrapper contract
|
|
18
30
|
|
|
@@ -70,6 +82,15 @@ agy-review --continue | --conversation <id> [--decided @f] [--focus "…"] #
|
|
|
70
82
|
passthrough (it owns the posture). The service can still **stall on large/substantive prompts**
|
|
71
83
|
(Issue-001), so keep reviews **focused**; the hard timeout is the guard.
|
|
72
84
|
|
|
85
|
+
**Host requirements, enforced BEFORE any run is spent.** Because the review reads agy's JSON
|
|
86
|
+
envelope in node, `agy-review` needs **Node ≥ 22** on `PATH`; and because it passes flags the CLI
|
|
87
|
+
must honour, it probes `agy --help` for **`--output-format`** and **`--disable-slash-commands`**
|
|
88
|
+
first. A missing flag, an unreadable `agy --help`, a missing or too-old node, or a missing
|
|
89
|
+
`bin/agy-envelope.mjs` each refuse with a named cause and spend nothing. This is a **capability
|
|
90
|
+
probe, not a version floor** — the release that introduced `--output-format` is not measurable from
|
|
91
|
+
one installed build, so a guessed floor would refuse working installs. A failed probe is never read
|
|
92
|
+
as "capability present".
|
|
93
|
+
|
|
73
94
|
## Models
|
|
74
95
|
|
|
75
96
|
Pass the **exact display string** from `agy models`, or set `AGY_MODEL`.
|
|
@@ -112,19 +133,31 @@ AGY_MODEL="Claude Sonnet 4.6 (Thinking)" AGY_TIMEOUT=10m agy-run @review-prompt.
|
|
|
112
133
|
| `--dangerously-skip-permissions` | auto-approve all tool permissions | avoid by default; use only with explicit user approval |
|
|
113
134
|
| `--sandbox` | run with terminal restrictions enabled | prefer when delegating a prompt that might trigger tool/terminal work |
|
|
114
135
|
| `--log-file <path>` | override the CLI log-file path | keep logs secret-free and out of committed artifacts |
|
|
115
|
-
| `--output-format <fmt>` | print-mode output: `text` (default), `json`, `stream-json` |
|
|
116
|
-
| `--json-schema <s\|path>` | enforce structured output (stream-json final result) |
|
|
136
|
+
| `--output-format <fmt>` | print-mode output: `text` (default), `json`, `stream-json` | **ADOPTED by `agy-review`** — every dispatch runs `json` and the envelope is parsed (`bin/agy-envelope.mjs`). `agy-run` stays `text`; JSON there rides passthrough, unparsed. `stream-json` is adopted by neither (see below) |
|
|
137
|
+
| `--json-schema <s\|path>` | enforce structured output (stream-json final result) | **REJECTED, and the reason is a measurement.** It is not a constrained decode: the model answers in prose and the CLI then spends a **second turn** asking it to restate that answer in schema shape (`num_turns: 2`). Matched control — same prompt, same model, schema off vs on: **16,585 vs 33,446 total tokens**. So a schema does not remove prose parsing; it replaces a free, deterministic, anchored regex with a billed non-deterministic re-read (the structured `reason` came back reworded, not quoted) and adds a failure mode — a run dying between the two turns has prose but no `structured_output` |
|
|
117
138
|
| `--effort <low\|medium\|high>` | reasoning effort for the session | NEW in 1.1.x; the display strings already carry an effort tier — the wrapper keeps model selection in ONE place (`AGY_MODEL`) |
|
|
118
139
|
| `--mode <m>` | agent execution mode (`accept-edits`, `plan`) | NEW in 1.1.x; not used by the wrapper |
|
|
119
140
|
| `--agent` / `--project <id>` / `--new-project` | agent + project selection for the session | NEW in 1.1.x; not used by the wrapper |
|
|
120
|
-
| `--disable-slash-commands` | disable slash command/skill expansion in print mode |
|
|
141
|
+
| `--disable-slash-commands` | disable slash command/skill expansion in print mode | **ADOPTED by `agy-review`** on every dispatch — a delivered change-set line beginning with a slash command stays BODY instead of being expanded, so the model reviews the delivered bytes. Not used by `agy-run` |
|
|
121
142
|
|
|
122
143
|
## Subcommands (v1.1.13)
|
|
123
144
|
|
|
124
145
|
`agent` / `agents`, `changelog`, `help`, `install`, `models`, `plugin` / `plugins`, `update`.
|
|
125
146
|
|
|
126
|
-
**Still not available in v1.1.13:** any `agy inspect`.
|
|
127
|
-
|
|
147
|
+
**Still not available in v1.1.13:** any `agy inspect`. On output format the two wrappers differ:
|
|
148
|
+
`agy-run` is a **text-by-default passthrough** — it hands back the CLI's stdout unchanged, so
|
|
149
|
+
`agy-run "…" -- --output-format json` really does print JSON; `agy-review` prints the review text it
|
|
150
|
+
read out of the JSON envelope on a **successful** run. (When its single dispatch or FINAL fed turn
|
|
151
|
+
exits non-zero, `agy-review` publishes that captured stdout unchanged — never an intermediate feed
|
|
152
|
+
turn's; see the transport bullet above.)
|
|
153
|
+
|
|
154
|
+
**`--output-format stream-json` is deliberately NOT adopted.** Its two measured gains are queued as
|
|
155
|
+
follow-ups rather than built: its `init` event names the **resolved** model (neither the plain `json`
|
|
156
|
+
envelope nor the stream's own `result` event carries one), which would let the receipt record what
|
|
157
|
+
actually ran; and its live `step_update` events would make a stalled run visible (Issue-001). The
|
|
158
|
+
seam built here makes the FLAG easy to switch, but neither gain is free: `bin/agy-envelope.mjs`
|
|
159
|
+
parses ONE JSON object and refuses anything else, so NDJSON needs a stream-aware reader, and live
|
|
160
|
+
progress needs an event relay rather than a captured file.
|
|
128
161
|
|
|
129
162
|
## Project-context flags
|
|
130
163
|
|
|
@@ -89,6 +89,12 @@ itself can never reveal the very text it asks for, and the wrapper needs no seco
|
|
|
89
89
|
construction) or `fed` (proven by echo). The kit's review-state gate requires the field present and
|
|
90
90
|
well-formed, never a particular value, so a receipt minted before this lane existed no longer
|
|
91
91
|
attests. The recovery is stated: re-run the review.
|
|
92
|
+
- **Later turns are routed by a NAMED field.** Turn 1's `--output-format json` envelope carries
|
|
93
|
+
`conversation_id`; the wrapper validates it against the UUID grammar and dispatches every later
|
|
94
|
+
turn with `--conversation <id>`. There is no `--continue` fallback in this lane: an id that is
|
|
95
|
+
absent, wrong-typed or malformed stops the run **before turn 2 is spent**, with NO receipt. (It was
|
|
96
|
+
formerly scraped from `agy`'s own run log — a format that is `agy`'s to change, so the pin could
|
|
97
|
+
rot silently; a named field fails loudly instead.)
|
|
92
98
|
|
|
93
99
|
### Honest residuals (recorded, not engineered away)
|
|
94
100
|
|
|
@@ -99,9 +105,6 @@ itself can never reveal the very text it asks for, and the wrapper needs no seco
|
|
|
99
105
|
- A change set whose parts carry **no unique interior line** in the 24..200-byte window (a single
|
|
100
106
|
huge minified line, for instance) cannot be proven delivered, so the wrapper **refuses** rather
|
|
101
107
|
than reviewing unprovably. Split the review, or exclude the blob.
|
|
102
|
-
- The conversation id is parsed from `agy`'s own run log, whose format is `agy`'s to change. An
|
|
103
|
-
unparseable log **degrades loudly** to `--continue`; correctness still rests on the echo proof,
|
|
104
|
-
which fails closed when the wrong conversation answers.
|
|
105
108
|
|
|
106
109
|
## agy's own permission ask — surfaced, never applied
|
|
107
110
|
|
|
@@ -9,11 +9,18 @@ works in any project.
|
|
|
9
9
|
```bash
|
|
10
10
|
curl -fsSL https://antigravity.google/cli/install.sh | bash
|
|
11
11
|
export PATH="$HOME/.local/bin:$PATH" # add to ~/.bashrc / ~/.zshrc to persist
|
|
12
|
-
agy --version #
|
|
12
|
+
agy --version # captured here from 1.1.13 — see the capability note below
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
- The binary is **`agy`** (not `antigravity`); it installs to `~/.local/bin/agy`.
|
|
16
16
|
- Keep `$HOME/.local/bin` on `PATH` (the wrapper also prepends it defensively).
|
|
17
|
+
- **There is no version floor.** The docs in this skill were captured from **v1.1.13**, but the
|
|
18
|
+
release that introduced each flag is not measurable from one installed build, so a guessed floor
|
|
19
|
+
would refuse working installs. What actually gates a review is a **capability probe** —
|
|
20
|
+
`agy-review` reads `agy --help` and refuses BEFORE spending a run unless the install advertises
|
|
21
|
+
both **`--output-format`** and **`--disable-slash-commands`**, plus **Node ≥ 22** on `PATH` (the
|
|
22
|
+
review parses agy's JSON envelope in node) and the skill's own `bin/agy-envelope.mjs`. Each
|
|
23
|
+
refusal names the missing capability and the recovery. `agy-run` needs none of these.
|
|
17
24
|
|
|
18
25
|
## 2. Sign in once (subscription only)
|
|
19
26
|
|
|
@@ -59,13 +66,19 @@ agy --version
|
|
|
59
66
|
echo "say OK" | agy-run -
|
|
60
67
|
```
|
|
61
68
|
|
|
62
|
-
Expected:
|
|
63
|
-
|
|
69
|
+
Expected: a version prints (any — `agy-review` probes its required capabilities, not a version
|
|
70
|
+
floor; `agy-run` probes nothing), then a short reply containing `OK`. If `agy-run` reports
|
|
71
|
+
`'agy' not found`, fix your `PATH` (step 1). If it asks you to sign in, complete step 2. If
|
|
72
|
+
`agy-review` later refuses naming a missing flag or Node, that is the capability probe from step 1,
|
|
73
|
+
not a broken install — follow the recovery it prints.
|
|
64
74
|
|
|
65
75
|
## Notes
|
|
66
76
|
|
|
67
|
-
- `agy-run` is headless and text
|
|
68
|
-
|
|
77
|
+
- `agy-run` is headless and text: `--output-format json` can ride passthrough
|
|
78
|
+
(`-- --output-format json`) but that wrapper adds no parsing/validation. `agy-review` is the one
|
|
79
|
+
that drives `--output-format json` first-class and reads the envelope (a successful run still
|
|
80
|
+
prints text; when its single dispatch or FINAL fed turn exits non-zero it publishes that captured
|
|
81
|
+
stdout unchanged, never an intermediate feed turn's).
|
|
69
82
|
- `AGY_MODEL` selects the exact model display string; `AGY_TIMEOUT` controls `--print-timeout`.
|
|
70
83
|
- Extra `agy` flags go after `--`, e.g. `agy-run @prompt.md -- --add-dir .`.
|
|
71
84
|
- Re-run interactive `agy` only when the OAuth token expires or the account changes.
|
|
@@ -1198,7 +1198,7 @@ describe('codex-review.sh — review receipts (AD-038)', () => {
|
|
|
1198
1198
|
// The grammar ENUMERATES the ASCII set (no ranges): a locale-collated [A-Za-z] could admit a
|
|
1199
1199
|
// non-ASCII letter the kit's JS reader then refuses, breaking correlation after a paid run.
|
|
1200
1200
|
const utf8 = makeSandbox();
|
|
1201
|
-
const r2 = run(utf8, { env: { AW_REVIEW_NONCE: '
|
|
1201
|
+
const r2 = run(utf8, { env: { AW_REVIEW_NONCE: 'r\u00e91', LC_ALL: 'en_US.UTF-8', LANG: 'en_US.UTF-8' } });
|
|
1202
1202
|
rmSync(utf8.root, { recursive: true, force: true });
|
|
1203
1203
|
assert.equal(r2.status, 2, 'a non-ASCII nonce letter refuses whatever the locale collation says');
|
|
1204
1204
|
assert.match(r2.stderr, /safe nonce grammar/);
|
package/capability.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-kit",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.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",
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
//
|
|
8
8
|
// At Stop time the turn is ENDING. That single fact turns two closing shapes from "discouraged" into
|
|
9
9
|
// FALSE, so they can be judged mechanically:
|
|
10
|
-
//
|
|
10
|
+
// * the "what I need from you" slot answering "nothing" — a stopped turn always needs a resume, so
|
|
11
11
|
// the slot must NAME it. "Nothing needed" is honest only while work is actually running;
|
|
12
|
-
//
|
|
12
|
+
// * a first-person promise of imminent work in the closing block — the turn is over, so the work
|
|
13
13
|
// is not starting. This is the announce-and-stop shape.
|
|
14
14
|
// A promise GATED on something named (after your yes, when CI finishes) is honest and passes.
|
|
15
15
|
//
|
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
// required a second classifier to decide what counts as a real ask, the rule was DELETED and its
|
|
20
20
|
// residual written down instead — twice. The mode contract lists every residual by name.
|
|
21
21
|
//
|
|
22
|
+
// ONE VOCABULARY, ENGLISH. The kit ships English markers and enumerates no other language: a
|
|
23
|
+
// language list would be a shipped guess about somebody else's dialogue, and it never stops growing.
|
|
24
|
+
// A deployment whose dialogue language is not English extends the arrays in ITS OWN placed copy —
|
|
25
|
+
// this file is copied into the project and self-contained, so that edit is local and survives an
|
|
26
|
+
// uninstalled kit. Until then such a deployment gets no detection, which the mode contract states.
|
|
27
|
+
//
|
|
22
28
|
// HONEST LIMIT, stated because it bounds what this can claim: a Stop hook cannot un-send the turn it
|
|
23
29
|
// judges. This is DETECTION — it converts a silent recurrence into a loud one. It never blocks, and
|
|
24
30
|
// every anomaly path exits 0: a hook that fires on every turn must not become the blocker or the
|
|
@@ -39,18 +45,17 @@ const EXIT_OK = 0;
|
|
|
39
45
|
// The three slots, matched on their LABEL and ANCHORED TO A LINE START. The anchor is not cosmetic:
|
|
40
46
|
// a message that DISCUSSES this contract — which the sessions this guard was built for do constantly
|
|
41
47
|
// — mentions the labels inline, and an unanchored match would judge that prose instead of the real
|
|
42
|
-
// closing block.
|
|
43
|
-
// keep the guard usable in an English-dialogue deployment.
|
|
48
|
+
// closing block.
|
|
44
49
|
const LINE_LEAD = '^[ \\t\\-•]*';
|
|
45
50
|
const SLOT_PATTERNS = Object.freeze({
|
|
46
|
-
now: new RegExp(`${LINE_LEAD}(
|
|
47
|
-
fromYou: new RegExp(`${LINE_LEAD}(
|
|
48
|
-
next: new RegExp(`${LINE_LEAD}(
|
|
51
|
+
now: new RegExp(`${LINE_LEAD}(?:now|where we are|state now)\\s*[::]`, 'gim'),
|
|
52
|
+
fromYou: new RegExp(`${LINE_LEAD}(?:what i need from you|from you)\\s*[::]`, 'gim'),
|
|
53
|
+
next: new RegExp(`${LINE_LEAD}(?:what(?:'s| is)? next|next)\\s*[::]`, 'gim'),
|
|
49
54
|
});
|
|
50
55
|
|
|
51
56
|
// Quoted and fenced material is EXAMPLE text, not the turn's own closing block. Stripped before any
|
|
52
57
|
// label search — otherwise pasting the very specimen this guard catches would make the guard judge
|
|
53
|
-
// the paste. Emphasis marks are stripped after, so
|
|
58
|
+
// the paste. Emphasis marks are stripped after, so `**Now:**` still parses.
|
|
54
59
|
// Scanned line by line rather than matched as one regex: an UNCLOSED fence must run to the end of
|
|
55
60
|
// the message. A pattern that only removes CLOSED fences would take an unfinished example's labels
|
|
56
61
|
// for a real block — judging a demonstration as if it were the turn, which is precisely the mistake
|
|
@@ -84,9 +89,11 @@ const stripFences = (text) => {
|
|
|
84
89
|
|
|
85
90
|
const BLOCKQUOTE_LINE = /^[ \t]*>.*$/gm;
|
|
86
91
|
const EMPHASIS_MARKS = /[*_`]/g;
|
|
87
|
-
// A model writes
|
|
88
|
-
// ASCII one, so matching only ASCII made
|
|
89
|
-
|
|
92
|
+
// A model writes "What's next" and "I'll" with a typographic apostrophe far more often than with the
|
|
93
|
+
// ASCII one, so matching only ASCII made every label and every promise marker fail silently on
|
|
94
|
+
// ordinary output. U+02BC rides as an escape because it is a modifier LETTER, and this family's
|
|
95
|
+
// sources carry no non-ASCII letter; the escaped class matches exactly the same characters.
|
|
96
|
+
const APOSTROPHES = /[’‘\u02bc‛]/g;
|
|
90
97
|
const toProse = (text) => stripFences(text)
|
|
91
98
|
.replace(BLOCKQUOTE_LINE, '')
|
|
92
99
|
.replace(EMPHASIS_MARKS, '')
|
|
@@ -103,69 +110,93 @@ const openingAnswer = (slot) => {
|
|
|
103
110
|
const at = slot.search(CLAUSE_END);
|
|
104
111
|
return at === -1 ? slot : slot.slice(0, at);
|
|
105
112
|
};
|
|
106
|
-
// A rule excusing
|
|
113
|
+
// A rule excusing "…, nothing else is needed" was built, tightened twice, and then DELETED. Every
|
|
107
114
|
// version let a banned answer through behind some harmless prefix, and the next version would have
|
|
108
115
|
// needed an "is this a real ask" recogniser — a second lexical classifier with its own edge set. The
|
|
109
116
|
// residual is stated instead of coded: a comma-joined qualifier IS flagged, and the writer's fix is
|
|
110
117
|
// a clause break, which the first-clause rule already passes.
|
|
111
118
|
|
|
112
119
|
const NOTHING_FORMS = Object.freeze([
|
|
113
|
-
'ничего',
|
|
114
|
-
'ничем',
|
|
115
|
-
'не требуется',
|
|
116
|
-
'не нужно',
|
|
117
|
-
'не нужен',
|
|
118
|
-
'нет необходимости',
|
|
119
120
|
'nothing',
|
|
120
121
|
'none',
|
|
121
122
|
'n/a',
|
|
122
123
|
'no action',
|
|
124
|
+
'no need',
|
|
125
|
+
'not needed',
|
|
126
|
+
'not required',
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
// -- the "what is happening NOW" slot is not a report of what was DONE ----------------------------
|
|
130
|
+
// The third judgeable shape. The three slots answer three different questions; when the now-slot
|
|
131
|
+
// opens with finished work, the reader's one question — what is running — is buried behind a
|
|
132
|
+
// summary they can already read in the message body above.
|
|
133
|
+
// Judged by TOKEN ORDER, the technique the promise/gate rule uses: a COMPLETION marker standing
|
|
134
|
+
// BEFORE the first CURRENCY marker means the running fact was buried. Two consequences are
|
|
135
|
+
// deliberate — a slot with NO currency marker is never judged here (nothing running is a legitimate
|
|
136
|
+
// state, and whether that is honest is the from-you slot's question), and a slot that LEADS with
|
|
137
|
+
// what is running passes whatever it adds afterwards.
|
|
138
|
+
// Stated residual, in this file's own style: a CURRENT state phrased with a COMPLETION MARKER
|
|
139
|
+
// ("the work is closed, waiting on your call" — `closed` is in the list below) is flagged, and the
|
|
140
|
+
// writer's fix is to phrase the state rather than the transition. Telling that from a real
|
|
141
|
+
// completion report needs a classifier this layer lacks. The specimen is deliberately one the list
|
|
142
|
+
// really carries: a residual written with a word the code does not match would document a false
|
|
143
|
+
// positive that cannot happen, which is worse than no residual at all.
|
|
144
|
+
const COMPLETION_FORMS = Object.freeze([
|
|
145
|
+
'done', 'completed', 'finished', 'closed', 'landed', 'shipped', 'merged', 'published',
|
|
146
|
+
'committed', 'fixed', 'added', 'removed', 'updated', 'recorded', 'measured', 'marked',
|
|
147
|
+
'verified', 'ran', 'wrote',
|
|
148
|
+
"i've", 'i have',
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
// Running STATE only, never a time-deictic. "now" is the slot's own label, so accepting it as a
|
|
152
|
+
// currency marker would put one at position 0 of nearly every slot and every completion after it
|
|
153
|
+
// would read as honest — one word defeating the whole rule. A marker that names WHEN rather than
|
|
154
|
+
// WHAT-IS-HAPPENING cannot carry this judgement.
|
|
155
|
+
const CURRENCY_FORMS = Object.freeze([
|
|
156
|
+
'running', 'in progress', 'waiting', 'awaiting', 'pending', 'is up', 'under way', 'underway',
|
|
157
|
+
'in flight', 'blocked on',
|
|
123
158
|
]);
|
|
124
159
|
|
|
125
160
|
// First-person promises of imminent work. Present tense counts: at Stop it describes work that is
|
|
126
161
|
// NOT running.
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
// a
|
|
130
|
-
//
|
|
131
|
-
// «I'll wait». Waiting is not work: it is what a turn that ENDS actually does, and it needs no gate to
|
|
132
|
-
// be honest. So a modal followed by a waiting verb is excluded rather than the modal being dropped —
|
|
133
|
-
// dropping it would need an open-ended list of every action verb English can put after it.
|
|
162
|
+
// The markers are pronoun+modal or pronoun+verb, so they cannot tell "I'll start" from "I'll wait".
|
|
163
|
+
// Waiting is not work: it is what a turn that ENDS actually does, and it needs no gate to be honest.
|
|
164
|
+
// So a modal followed by a waiting verb is excluded rather than the modal being dropped — dropping
|
|
165
|
+
// it would need an open-ended list of every action verb English can put after it.
|
|
134
166
|
const WAITING_CONTINUATIONS = Object.freeze([
|
|
135
167
|
"i'll wait", 'i will wait', "i'll be waiting", "i'll stand by", 'i will stand by',
|
|
136
168
|
"i'll hold", 'i will hold', "i'll stay", 'i will stay', "i'll remain", 'i will remain',
|
|
137
169
|
]);
|
|
138
170
|
|
|
139
171
|
const PROMISE_FORMS = Object.freeze([
|
|
140
|
-
'
|
|
141
|
-
'
|
|
142
|
-
"i'll", 'i will', "i'm going to", 'i am going to', 'i start', 'i begin', 'next i',
|
|
172
|
+
"i'll", 'i will', "i'm going to", 'i am going to', "i'm starting", "i'm taking",
|
|
173
|
+
'i start', 'i begin', 'i take', 'i move on', 'next i',
|
|
143
174
|
]);
|
|
144
175
|
|
|
145
176
|
// A gate the promise may depend on. What is actually checked is TOKEN ORDER inside one segment: a
|
|
146
177
|
// gate is treated as excusing a promise when it appears in the same segment and earlier in it. That
|
|
147
178
|
// is a lexical approximation of dependency, not dependency itself, and it carries two named
|
|
148
|
-
// residuals — an honest TRAILING gate (
|
|
149
|
-
// an earlier comma-clause (
|
|
150
|
-
// that follows it. Both are accepted limits of a lexical layer,
|
|
179
|
+
// residuals — an honest TRAILING gate ("I'll take it when you say so") is flagged, and a gate
|
|
180
|
+
// belonging to an earlier comma-clause ("if the test fails I'll report, and now I start the next
|
|
181
|
+
// class") wrongly excuses a promise that follows it. Both are accepted limits of a lexical layer,
|
|
182
|
+
// documented in the mode contract.
|
|
151
183
|
const CONDITIONAL_FORMS = Object.freeze([
|
|
152
|
-
'
|
|
153
|
-
'after', 'when', 'if', 'as soon as', 'once', 'pending',
|
|
184
|
+
'after', 'when', 'if', 'as soon as', 'once', 'upon', 'pending',
|
|
154
185
|
]);
|
|
155
186
|
|
|
156
|
-
// Segment boundaries for the promise/gate rule. Deliberately NOT the comma:
|
|
157
|
-
//
|
|
187
|
+
// Segment boundaries for the promise/gate rule. Deliberately NOT the comma: "if you agree, I'll take
|
|
188
|
+
// the class" is one honest thought, and splitting it would flag the shape the rule exists to permit.
|
|
158
189
|
const PROMISE_SEGMENT_BREAK = /;|\n|[.!?](?=\s|$)/;
|
|
159
190
|
|
|
160
|
-
// Word-bounded matching, Unicode-aware.
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
191
|
+
// Word-bounded matching, Unicode-aware. The markers are English but the TEXT is the project's
|
|
192
|
+
// dialogue language, which need not be ASCII, and JavaScript's `\b` is ASCII-only: under it every
|
|
193
|
+
// non-ASCII letter reads as a word break, so an English marker sitting inside a non-ASCII word would
|
|
194
|
+
// match as if it stood alone. The Unicode class costs nothing and keeps that door shut; on the
|
|
195
|
+
// English side it is what stops `none` matching inside `nonexistent`.
|
|
165
196
|
const WORD_CHAR = '\\p{L}\\p{N}';
|
|
166
197
|
const escapeForRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
|
|
167
198
|
const boundedPattern = (phrase) => new RegExp(
|
|
168
|
-
`(?<![${WORD_CHAR}])${escapeForRegExp(phrase)}(?![${WORD_CHAR}])
|
|
199
|
+
`(?<![${WORD_CHAR}])${escapeForRegExp(phrase)}(?![${WORD_CHAR}])`,
|
|
169
200
|
'iu',
|
|
170
201
|
);
|
|
171
202
|
const firstMatchAt = (haystack, needles) => needles.reduce((best, needle) => {
|
|
@@ -175,6 +206,25 @@ const firstMatchAt = (haystack, needles) => needles.reduce((best, needle) => {
|
|
|
175
206
|
}, -1);
|
|
176
207
|
const containsAny = (haystack, needles) => firstMatchAt(haystack, needles) !== -1;
|
|
177
208
|
|
|
209
|
+
// Nearly every English completion marker doubles as an ADJECTIVE, which the Russian verbs this rule
|
|
210
|
+
// was first written against did not: "the updated test suite is running" is an honest now-slot that
|
|
211
|
+
// leads with the running fact, and reading `updated` there as a completion report would flag it. A
|
|
212
|
+
// marker sitting directly after a DETERMINER is attributive — it modifies the noun after it instead
|
|
213
|
+
// of reporting a transition — so it is not counted.
|
|
214
|
+
// Residual, stated: this also loses a completion phrased attributively ("the finished council, the
|
|
215
|
+
// matrix is running" passes). That trade is deliberate. A false flag on a correct slot is noise from
|
|
216
|
+
// a hook that fires on EVERY turn, and noise is the failure this file is most careful to avoid; a
|
|
217
|
+
// missed marginal shape is not.
|
|
218
|
+
const DETERMINERS = Object.freeze(['the', 'a', 'an', 'this', 'that', 'these', 'those', 'its', 'my', 'our', 'your', 'their']);
|
|
219
|
+
const AFTER_DETERMINER = new RegExp(`(?:^|[^${WORD_CHAR}])(?:${DETERMINERS.join('|')})[ \\t]+$`, 'iu');
|
|
220
|
+
const matchesIn = (haystack, needle) => [...haystack.matchAll(new RegExp(boundedPattern(needle).source, 'giu'))]
|
|
221
|
+
.map((match) => match.index);
|
|
222
|
+
const firstReportedAt = (haystack, needles) => needles.reduce((best, needle) => {
|
|
223
|
+
const at = matchesIn(haystack, needle).find((index) => !AFTER_DETERMINER.test(haystack.slice(0, index)));
|
|
224
|
+
if (at === undefined) return best;
|
|
225
|
+
return best === -1 ? at : Math.min(best, at);
|
|
226
|
+
}, -1);
|
|
227
|
+
|
|
178
228
|
const normalise = (value) => (typeof value === 'string'
|
|
179
229
|
? value.replace(EMPHASIS_MARKS, '').replace(APOSTROPHES, "'").toLowerCase()
|
|
180
230
|
: '');
|
|
@@ -197,7 +247,7 @@ const groupLabels = (labels) => labels.reduce((state, label) => {
|
|
|
197
247
|
: { current: candidate, completed: state.completed };
|
|
198
248
|
}, { current: [], completed: null });
|
|
199
249
|
|
|
200
|
-
// findStateBlock(text)
|
|
250
|
+
// findStateBlock(text) -> { now, fromYou, next } or null. The LAST STARTED candidate decides: if the
|
|
201
251
|
// message ends mid-block, the turn did not end on a block at all, and falling back to an earlier
|
|
202
252
|
// complete one would judge text the turn already moved past.
|
|
203
253
|
export const findStateBlock = (text) => {
|
|
@@ -213,7 +263,7 @@ export const findStateBlock = (text) => {
|
|
|
213
263
|
return block;
|
|
214
264
|
};
|
|
215
265
|
|
|
216
|
-
// decideStop({ closingText, requireBlock })
|
|
266
|
+
// decideStop({ closingText, requireBlock }) -> { ok, reasons }. Never throws: junk input decides
|
|
217
267
|
// "no block", which is a reportable state rather than a crash. An ABSENT block is reported only
|
|
218
268
|
// under `requireBlock`: this kit does not mandate the three-part block, so warning about its absence
|
|
219
269
|
// by default would fire on nearly every turn of a project that never adopted it — noise, from a hook
|
|
@@ -225,7 +275,7 @@ export const decideStop = (options) => {
|
|
|
225
275
|
return requireBlock
|
|
226
276
|
? {
|
|
227
277
|
ok: false,
|
|
228
|
-
reasons: ['no state block: the closing message must end with
|
|
278
|
+
reasons: ['no state block: the closing message must end with "now / what I need from you / what\'s next"'],
|
|
229
279
|
}
|
|
230
280
|
: { ok: true, reasons: [] };
|
|
231
281
|
}
|
|
@@ -236,6 +286,11 @@ export const decideStop = (options) => {
|
|
|
236
286
|
'from-you slot answers "nothing": the turn is ENDING, so a resume from the maintainer IS required — name the real unblocker instead',
|
|
237
287
|
);
|
|
238
288
|
}
|
|
289
|
+
if (buriesTheRunningFact(normalise(block.now))) {
|
|
290
|
+
reasons.push(
|
|
291
|
+
'the now-slot reports finished work before it names what is running — "now" is the state at this instant; move what you completed into the message body above the block',
|
|
292
|
+
);
|
|
293
|
+
}
|
|
239
294
|
if (hasUngatedPromise(normalise(block.next))) {
|
|
240
295
|
reasons.push(
|
|
241
296
|
'announce-and-stop: the what-next slot promises imminent first-person work while the turn ends — either do it in this turn, or state what the work waits on',
|
|
@@ -244,6 +299,13 @@ export const decideStop = (options) => {
|
|
|
244
299
|
return { ok: reasons.length === 0, reasons };
|
|
245
300
|
};
|
|
246
301
|
|
|
302
|
+
const buriesTheRunningFact = (nowSlot) => {
|
|
303
|
+
const currencyAt = firstMatchAt(nowSlot, CURRENCY_FORMS);
|
|
304
|
+
if (currencyAt === -1) return false;
|
|
305
|
+
const completionAt = firstReportedAt(nowSlot, COMPLETION_FORMS);
|
|
306
|
+
return completionAt !== -1 && completionAt < currencyAt;
|
|
307
|
+
};
|
|
308
|
+
|
|
247
309
|
const hasUngatedPromise = (nextSlot) => nextSlot.split(PROMISE_SEGMENT_BREAK).some((segment) => {
|
|
248
310
|
const promiseAt = firstMatchAt(segment, PROMISE_FORMS);
|
|
249
311
|
if (promiseAt === -1) return false;
|
|
@@ -285,7 +347,7 @@ const parsePayload = (rawInput) => {
|
|
|
285
347
|
//
|
|
286
348
|
// An EMPTY string is delivered text, not an absent one: a turn that ended with no prose really did
|
|
287
349
|
// end without a closing block, and `--require-block` should be able to say so.
|
|
288
|
-
// runHook(raw, {requireBlock})
|
|
350
|
+
// runHook(raw, {requireBlock}) -> a decision, or null when there is genuinely nothing to judge.
|
|
289
351
|
// Never throws.
|
|
290
352
|
export const runHook = (rawInput, deps) => {
|
|
291
353
|
const requireBlock = (deps ?? {}).requireBlock === true;
|
|
@@ -22,7 +22,7 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
|
|
|
22
22
|
4. **Choose agent attribution — answered in the step-2 batch.** May the agent attribute work to itself / to AI — `Co-Authored-By` trailers, "Generated with …" footers, "AI"/agent/model mentions in code, comments, commit messages, PR titles/bodies, or docs? **Default to `off`** (no agent/AI mention anywhere) unless they opt in — people are routinely surprised to find an AI listed as a repo contributor. Carry the answer into the `{{AGENT_ATTRIBUTION}}` slot of the *Attribution* block when `AGENTS.md` is created (step 5). **If `off` and the project uses Claude Code**, also set `"includeCoAuthoredBy": false` in the project's `.claude/settings.json` (create it if absent) — the trailer is added by the harness, so a doc directive alone won't stop it. See [Attribution contract](${CLAUDE_SKILL_DIR}/references/contracts.md#attribution-contract).
|
|
23
23
|
5. **Entry-point doc.** If `AGENTS.md` / `CLAUDE.md` already exist (step-1 recon), do **not** overwrite — show the user and ask whether to merge or replace. Otherwise create `AGENTS.md` (the cross-agent standard — Codex / Cursor / Devin Desktop / Copilot read it natively) from `${CLAUDE_SKILL_DIR}/references/templates/AGENTS.md`, and symlink `CLAUDE.md -> AGENTS.md` (`ln -s AGENTS.md CLAUDE.md`) for Claude Code — single source, no duplication. For nested context, add a subdir `AGENTS.md` (+ a `CLAUDE.md` symlink beside it for Claude Code).
|
|
24
24
|
6. **Deploy `docs/ai/`.** Create every `docs/ai/` file + `pages/` + the seed `adr/` store from `${CLAUDE_SKILL_DIR}/references/templates/` (the template loop deploys each non-`AGENTS.md`, non-`adr-record.md` template — `adr-record.md` is a skill-home ADR authoring reference, never deployed). This deploys the HOT ADR window **`docs/ai/decisions.md`** and the seed navigator **`docs/ai/adr/log.md`** (its `adr/` subdir copies in wholesale), the `.md` docs, **and** the three seeded, user-editable strict-JSON configs: **`docs/ai/orchestration.json`** (the per-project recipe defaults the `procedures` advisor reads), **`docs/ai/gates.json`** (the project's gate declaration — seeded as the no-consent EMPTY list; FILLING it is the consented `gates-init` preview, `${CLAUDE_SKILL_DIR}/references/modes/gates.md`), and **`docs/ai/autonomy.json`** (the per-project autonomy policy — seeded SPARSE with the onboarding note only, so it is exactly the computed defaults until the user declares levels via `/agent-workflow-kit set-autonomy` or by hand). Keep each `.md` file's frontmatter (`type / lastUpdated / scope / staleAfter / owner / maxLines`); the `.json` seeds carry no frontmatter (the docs cap-validator globs `*.md` only, so they are inherently skipped).
|
|
25
|
-
7. **Fill templates** per the table below.
|
|
25
|
+
7. **Fill templates** per the table below. **Then materialize the navigator, as the last `docs/ai` step:** the entry point declares `docs/ai/index.md` always-loaded and it is a GENERATED artifact with no template, so run `node ${CLAUDE_SKILL_DIR}/references/scripts/check-docs-size.mjs --ensure-index --root=<project>` and relay its one outcome line (`ensure-index: regenerated` / `already-current`, or a `write-refused` / `probe-failed` line naming the offending path — either is a STOP: resolve it before reporting the deploy, since the navigator the entry point declares does not exist yet). The generator runs from **this skill's own directory**, so a project with **no Node runtime** is covered too: the agent host runs it, and step 8's copy is not a precondition.
|
|
26
26
|
8. **Install enforcement (Node projects).** Copy `${CLAUDE_SKILL_DIR}/references/scripts/*.mjs` (+ `*.test.mjs`) into the project's `scripts/`. They self-configure (project name from `package.json`, hierarchical/on-demand sections auto-discovered). **If the project has no Node runtime** (step-1 recon), skip this step and the hook in step 9 — follow the cap/archive/index policy manually, or port the scripts to the project's language.
|
|
27
27
|
9. **Wire / hide** per visibility (see [Visibility contract](${CLAUDE_SKILL_DIR}/references/contracts.md#visibility-contract)). Install the pre-commit hook (Node projects): `node scripts/install-git-hooks.mjs`. If the installer reports a pre-existing non-marker hook, stop and ask the user to merge it manually rather than overwriting. Once the gate declaration is final-run-capable, the installer's optional consented `--commit-guard <kit commit-guard.mjs path>` arm binds commits to the green `run-gates --final` receipt (the Recommendations advisor surfaces the exact one-liner).
|
|
28
28
|
- **visible** — wire the `package.json` scripts + add the minimal `.gitignore`. **Do not run the hide tool.**
|
|
@@ -32,7 +32,11 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
|
|
|
32
32
|
- If it reports a **leftover machine-wide ignore block** from an older deployment, **ask before removing it** — it could affect another of the user's repos that relies on the same machine-wide rules; on a yes, re-run with `--remove-global` (prints a restorable backup). Otherwise it is kept (harmless — the project-local rules win).
|
|
33
33
|
- Report the result plainly (what is now hidden). **No Node on the agent host** → write the one managed block into `.git/info/exclude` by hand from the contract's path list, and report the manual step. **Windows is supported.**
|
|
34
34
|
- **Do not edit `package.json`** in hidden mode — a tracked change leaks the whole system.
|
|
35
|
-
10. **Stamp the deployment lineage.**
|
|
35
|
+
10. **Stamp the deployment lineage.** **First re-run the step-7 navigator finalizer**
|
|
36
|
+
(`--ensure-index`) whenever a later step touched `docs/ai` — the reconciles that follow the
|
|
37
|
+
template copy do (the agent-rules lens,
|
|
38
|
+
`${CLAUDE_SKILL_DIR}/references/shared/composition-handoff.md`); it is idempotent, so an
|
|
39
|
+
untouched tree simply reports `already-current`. Then write the **deployment-lineage head** into
|
|
36
40
|
`docs/ai/.workflow-version` (one semver line). The lineage head is **`3.0.0`** — the shared
|
|
37
41
|
`agent-workflow` deployment lineage, **NOT** this kit's npm package version (see
|
|
38
42
|
`package.json` / `CHANGELOG.md`). The two are
|
|
@@ -7,12 +7,15 @@ The **config writer** for `docs/ai/orchestration.json` — the answer to *"set m
|
|
|
7
7
|
|
|
8
8
|
**Map the user's plain language → explicit ops** (the kit ships no NL parser; it performs no `all`-magic, so you expand scope explicitly, asking when unclear):
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
The intents are listed in English; a user speaking any other language expresses the same four, and
|
|
11
|
+
mapping their wording onto them is your job, not a shipped phrase list.
|
|
12
|
+
|
|
13
|
+
| user means | op | scope |
|
|
11
14
|
|---|---|---|
|
|
12
|
-
| "
|
|
13
|
-
| "
|
|
14
|
-
| "
|
|
15
|
-
| "
|
|
15
|
+
| "both review" | `--set <activity>.review=council` | **disambiguate**: which activity? If both, pass `--set plan-authoring.review=council --set plan-execution.review=council`. |
|
|
16
|
+
| "one reviewer" | `--set <activity>.review=reviewed` | per the named activity, else ask |
|
|
17
|
+
| "delegate execution" | `--set plan-execution.execute=delegated` | execution only |
|
|
18
|
+
| "revert / do it myself" | `--unset <activity>.<slot>` | the named slot → its computed default |
|
|
16
19
|
|
|
17
20
|
Run **`node ${CLAUDE_SKILL_DIR}/tools/set-recipe.mjs [--set <activity>.<slot>=<recipe>]… [--unset <activity>.<slot>]… [--write] [--json]`**:
|
|
18
21
|
|