@sabaiway/agent-workflow-kit 1.9.0 → 1.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 CHANGED
@@ -4,6 +4,53 @@ 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
+ ## 1.10.0 — Hidden mode covers the full AI/agent footprint, project-local
8
+
9
+ Hidden visibility now hides the **full AI/agent footprint** — the kit's own artifacts **and** every
10
+ known foreign tool's files (Claude skills, Cursor, Windsurf, Gemini, Copilot, Aider, Continue, …) — in
11
+ **one managed block in the project-local `.git/info/exclude`**, never the machine-global
12
+ `core.excludesFile` (which leaked the same rules to every repo on the host). **AD-014** amends
13
+ **AD-006** and generalizes the `.claude/skills/` one-off (AD-013). The deployment-lineage head stays
14
+ **`1.3.0`** — this is a stamp-independent reconcile wired into bootstrap + the upgrade flow (the AD-010
15
+ methodology-slot precedent), so there is **no migration file**.
16
+
17
+ ### Added
18
+ - `tools/known-footprint.mjs` — the `KIT_OWN_PATHS` + `KNOWN_FOOTPRINT` registry (+ `patternToProbe` /
19
+ `expandGlob` / `matchesKnownGlob`), guarded by a frozen-snapshot + count-sentinel drift test.
20
+ - `tools/hide-footprint.mjs` — the single hide-writer. Classifies each path (tracked → **ASK** with the
21
+ printed `git rm --cached`; present generic-name → **ASK**; else **hide**), writes one re-derived
22
+ managed fence (a clean re-run is byte-identical / zero-diff), `migrateFromGlobal` (detect + report the
23
+ residual machine-global block by default; `--remove-global` removes it with a printed backup),
24
+ `--reconcile` (upgrade-time visibility inference: visible → zero bytes, ambiguous → ASK),
25
+ `--unhide`, `--include`. Unit + real-`git` integration tests (worktree, precedence, delegated memory).
26
+
27
+ ### Changed
28
+ - `references/contracts.md` Visibility contract rewritten (project-local; full footprint table);
29
+ `SKILL.md` bootstrap step 9 + the upgrade reconcile now drive the tool; root + kit READMEs corrected.
30
+ - A **tracked** file is never silently un-tracked — the tool prints the `git rm --cached` it will not run.
31
+
32
+ ## 1.9.1 — Front-door value framing for the optional bridges; kit flow-pointer
33
+
34
+ The optional execution-backends (the `codex` / `agy` bridges) were **listed** but never **sold**: a
35
+ reader couldn't tell what they add to the workflow or why they'd want them. Promoted per **AD-009**
36
+ altitude — value lives at the **family front door**, the per-package page stays a manual.
37
+
38
+ - **Root README** — the existing `## 🔌 Optional delegated execution (the bridges)` section now
39
+ frames what the bridges add to **plan → execute → review**: an *independent reviewer* (a second
40
+ opinion in the **review** phase) and a *delegated executor / parallel hand* (a bounded task to
41
+ `codex exec` in the **execute** phase), under your own subscription (no pay-as-you-go billing,
42
+ subject to each provider's quotas). The honesty caveats are unchanged (`init` bundles but never
43
+ places a bridge; link-only `setup`; third-party services; context-file priority).
44
+ - **Kit README** — one manual-altitude flow-pointer in the composition-root bridge bullet: the
45
+ bridges plug into the **execute** and **review** phases, routing **up** to the front door for the
46
+ *why*. No value pitch duplicated into the manual (AD-009 anti-drift).
47
+
48
+ Documentation change only — no code, detector, or `init`/npx behaviour change, no `docs/ai`
49
+ structural change, deployment-lineage head stays **`1.3.0`**, `agent-workflow-memory` untouched, no
50
+ migration. The **kit** README + metadata ship in the kit tarball (the root README is the GitHub
51
+ family front door, outside the package), so the kit README edit rides a patch bump — three version
52
+ sources in sync.
53
+
7
54
  ## 1.9.0 — `upgrade` surfaces the optional backends at every successful exit
8
55
 
9
56
  `/agent-workflow-kit upgrade` said **nothing** about the optional execution-backends (the `codex` /
package/README.md CHANGED
@@ -124,8 +124,9 @@ The Markdown memory is **stack-agnostic**; the `scripts/` + pre-commit hook are
124
124
  (dependency-free, `node --test`). Non-Node projects keep the same policy by hand.
125
125
 
126
126
  Two **visibility** modes, chosen at deploy time: **visible** (committed with the repo) or **hidden**
127
- (same files in-tree but git-ignored via the global `core.excludesFile`, so the repo "looks normal").
128
- Hidden changes how the files are *tracked*, not where agents find them.
127
+ (same files in-tree but git-ignored via the project-local `.git/info/exclude`, so the repo "looks
128
+ normal" one managed block covering the full AI/agent footprint, scoped to this repo, never
129
+ machine-wide). Hidden changes how the files are *tracked*, not where agents find them.
129
130
 
130
131
  ---
131
132
 
@@ -258,7 +259,10 @@ agent-workflow-kit — the composition root (installed via npx … init)
258
259
  - **Injects** the bounded workflow methodology into the deployed `AGENTS.md`. Its *future* home is
259
260
  **`agent-workflow-engine`** — today an `available: false` stub, never one of the shipped backends.
260
261
  - **Detects & (opt-in) sets up** the optional `codex` / `agy` **bridges** — agent skills (not npm, not
261
- installed by `init`). `/agent-workflow-kit backends` reports readiness **read-only**;
262
+ installed by `init`). They plug into the workflow's **execute** and **review** phases — for *what
263
+ each adds and why*, see the
264
+ [family front door](https://github.com/sabaiway/agent-workflow#readme). `/agent-workflow-kit backends`
265
+ reports readiness **read-only**;
262
266
  `/agent-workflow-kit setup` does the **link-only** part (place the bundled bridge skill + link its
263
267
  wrappers), while the binary install + the subscription login stay manual. A bridge reads the deployed
264
268
  memory only if it wins that tool's context-file priority, and the bridges call third-party services
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: '1.9.0'
6
+ version: '1.10.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-kit
@@ -51,7 +51,11 @@ made: a partial/broken memory install discovered mid-flow must not disable the w
51
51
  answers** (visibility / language / attribution) to `agent-workflow-memory`, which writes
52
52
  `docs/ai/` + `AGENTS.md` (with the empty slot) + **`.memory-version`**. The kit then
53
53
  **reconciles the bounded methodology slot** (below) and writes the kit-fallback
54
- **`.workflow-version`**. → **both stamps** present.
54
+ **`.workflow-version`**. → **both stamps** present. In **hidden** mode the kit is the **single
55
+ hide authority**: after the hand-off it runs `tools/hide-footprint.mjs` (step 9), which **absorbs
56
+ memory's project-local footprint lines** into the one canonical `.git/info/exclude` block and adds
57
+ the external footprint — so there is **no machine-global write** at any step (a stale memory's
58
+ residual global block is cleaned via the upgrade reconcile, below).
55
59
  - **Fallback** (memory absent/invalid): the kit runs the bootstrap procedure below from its own
56
60
  bundled assets — whose entry-point template now ships the **empty methodology slot** the kit
57
61
  reconciles + fills — and writes **`.workflow-version`** only. Softly suggest installing
@@ -91,7 +95,7 @@ Before acting, read `docs/ai/.workflow-version` (the project's stamp), state a o
91
95
 
92
96
  - **absent** → bootstrap (a fresh deployment).
93
97
  - **stamp < `1.3.0`** (the deployment-lineage head) → `upgrade`.
94
- - **stamp == `1.3.0`** → already current; only the stamp-independent methodology-slot reconcile may run.
98
+ - **stamp == `1.3.0`** → already current; only the stamp-independent reconciles may run (the methodology slot **and** the hidden-mode footprint, *Mode: upgrade* step 3).
95
99
  - **stamp > head / unparseable** → STOP — never-downgrade gate (see *Mode: upgrade* step 2).
96
100
 
97
101
  **Two independent version axes — never conflate them:**
@@ -133,14 +137,21 @@ readiness. Canonical format:
133
137
  - `src/` (or equivalent) 2–3 levels deep → modules, routes/pages, components, services, types.
134
138
  - Tests (framework, location, E2E?) and linter rules.
135
139
  - Record: stack, package manager, daily commands (`dev`/`test`/`lint`/`type-check`), routes/pages, architecture layers.
136
- 2. **Choose visibility — ASK the user explicitly and wait for the answer, before writing anything.** This decides what gets tracked and is hard to reverse after a commit, so never assume the default silently: `visible` (committed — canonical, recommended) or `hidden` (in-tree, hidden via `~/.gitignore_global`). See [Visibility contract](references/contracts.md#visibility-contract).
140
+ 2. **Choose visibility — ASK the user explicitly and wait for the answer, before writing anything.** This decides what gets tracked and is hard to reverse after a commit, so never assume the default silently: `visible` (committed — canonical, recommended) or `hidden` (in-tree, git-ignored via the **project-local** `.git/info/exclude` — one managed block covering the full AI/agent footprint, never the machine-global excludes). See [Visibility contract](references/contracts.md#visibility-contract).
137
141
  3. **Choose conversational language — ASK the user explicitly and wait for the answer.** Which language should the agent *talk to them* in — questions, explanations, summaries, status updates? Offer the language they're already writing in as the default. Carry the answer into the `{{COMM_LANGUAGE}}` slot of the *Communication language* block when `AGENTS.md` is created (step 5). See [Communication contract](references/contracts.md#communication-contract). This sets the **dialogue** language only — never the files.
138
142
  4. **Choose agent attribution — ASK the user explicitly and wait for the answer.** 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](references/contracts.md#attribution-contract).
139
143
  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).
140
144
  6. **Deploy `docs/ai/`.** Create the 11 files + `pages/` from `${CLAUDE_SKILL_DIR}/references/templates/`. Keep each file's frontmatter (`type / lastUpdated / scope / staleAfter / owner / maxLines`).
141
145
  7. **Fill templates** per the table below.
142
146
  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.
143
- 9. **Wire / hide** per visibility (see 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.
147
+ 9. **Wire / hide** per visibility (see [Visibility contract](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.
148
+ - **visible** — wire the `package.json` scripts + add the minimal `.gitignore`. **Do not run the hide tool.**
149
+ - **hidden** — run the kit's hide writer (one managed block in the **project-local** `.git/info/exclude`, covering the kit's own artifacts **and** every known AI/agent tool's footprint — never the machine-global excludes): `node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --dry-run` **first** (changes nothing, prints the plan), then the same without `--dry-run`. Handle what it surfaces, in plain language (never the tool's internal terms):
150
+ - A path it reports as **already committed** can't be hidden by ignoring it and is **never un-tracked silently** — show the user the printed `git rm --cached <path>`, let them decide, then opt it in with `--include=<path>`.
151
+ - A **present file with a generic name** it flags (e.g. `GEMINI.md`) → ask before `--include=<path>`.
152
+ - 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).
153
+ - 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.**
154
+ - **Do not edit `package.json`** in hidden mode — a tracked change leaks the whole system.
144
155
  10. **Stamp the deployment lineage.** Write the **deployment-lineage head** into
145
156
  `docs/ai/.workflow-version` (one semver line). The lineage head is **`1.3.0`** — the shared
146
157
  `agent-workflow` deployment lineage, **NOT** this kit's npm package version (see
@@ -169,8 +180,10 @@ Fill strategy:
169
180
  **Cap-refusal is a soft, reported skip — not a STOP.** If — and ONLY if — `reconcile` exits non-zero because filling the slot would exceed the deployed `AGENTS.md` 100-line cap (the entry point is already at / over budget), leave the file byte-for-byte unchanged (the tool already did) and **continue** the upgrade without the slot. A malformed slot or a missing / duplicate anchor is a *different* non-zero exit and still **STOPs** (above) — never soft-skip those. This is **not** a silent skip (Hard Constraint — no silent failures): report it explicitly in the successful-exit report (**step 4** on an equal-head deployment, else **step 8**), in plain language, e.g. *"The workflow-methodology pointer wasn't added — `AGENTS.md` is N lines, over its 100-line limit. The methodology is already documented in `docs/ai/agent_rules.md`; to add the pointer, trim the entry point (move detail into `docs/ai/`) and re-run upgrade."* N is the file's **current** line count — never the tool's number (that is the would-be post-injection size). Because the entry point is already over cap, ensure the rest of the upgrade does not push it further: any mandatory `AGENTS.md` edit must keep it ≤100 lines or pause for an explicit trim — never bust the cap to land a migration.
170
181
 
171
182
  **No-Node project:** open `AGENTS.md`; if there is no `<!-- workflow:methodology:start/end -->` pair, **count the lines first** — if pasting the pair + the fragment from `tools/methodology-slot.md` would take the file over 100 lines, **skip it and report the skip** (as above — the methodology is already in `docs/ai/agent_rules.md`; trim to add the pointer). Otherwise paste the pair right after the *Read it before any code change.* line and fill it from `tools/methodology-slot.md`.
172
- 4. **Equal-head exit — a real successful-exit report, not a bare stop.** If the stamp **equals** the head, the lineage is up to date — but step 3 (slot reconcile) ran first and may have changed the file, so this is a proper exit report, not a no-op:
173
- - **Report step 3's outcome in plain language** — the workflow-methodology pointer was *added*, was *already present* (nothing changed), or was *skipped because the entry point is over its line limit* (the cap-refusal soft-skip from step 3, with its reason). Plain wording only never the reconcile/slot/anchor/marker terms (Gotcha: never leak kit internals).
183
+
184
+ **Hidden-mode footprint reconcile — stamp-independent, same gate, BEFORE the equal-head short-circuit (D9 / AD-014).** A deployment does not record whether it chose `hidden`, so first **infer visibility**: `node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --reconcile --dry-run` (writes **zero bytes**). It reports one of **visible** (the entry point is tracked) nothing to do; **ambiguous** (untracked but not ignored — could be a fresh uncommitted repo, or a hide that broke) → **ASK** the user which it is, never guess; **hidden** re-run without `--dry-run` to migrate any older **machine-global** hide to the **project-local** `.git/info/exclude` (one managed block; folds in the legacy `.claude/skills/` line), idempotently (a clean re-run is zero-diff). Handle its surfaced paths exactly as bootstrap step 9 (already-committed show `git rm --cached`, ask before `--include`; generic-name present file → ask; **leftover machine-wide ignore block → ASK before `--remove-global`**, default keep + report). No Node on the agent host / Windows → as step 9. This runs on **every** hidden upgrade, like the methodology slot no lineage-head bump, no migration file.
185
+ 4. **Equal-head exit — a real successful-exit report, not a bare stop.** If the stamp **equals** the head, the lineage is up to date — but step 3 (the methodology-slot **and** hidden-mode footprint reconciles) ran first and may have changed things, so this is a proper exit report, not a no-op:
186
+ - **Report step 3's outcome in plain language** — the workflow-methodology pointer was *added*, was *already present* (nothing changed), or was *skipped because the entry point is over its line limit* (the cap-refusal soft-skip from step 3, with its reason); and, for a hidden deployment, whether the hidden-mode footprint was *moved to project-local*, was *already project-local* (nothing changed), or needed a question (ambiguous visibility / a leftover machine-wide block). Plain wording only — never the reconcile/slot/anchor/marker terms (Gotcha: never leak kit internals).
174
187
  - **Print the one-line backend-status line** — the shared contract above (run `node ${CLAUDE_SKILL_DIR}/tools/detect-backends.mjs`; same format, invariants, and detector-unavailable skip-with-reason).
175
188
  - **Then ask before committing — never auto-commit.** If step 3 added the slot (or anything else changed), report it and ask. If step 3 was a pure zero-diff no-op and nothing else changed, say **"already up to date"** and still print the read-only backend line.
176
189
  5. Show the relevant `${CLAUDE_SKILL_DIR}/CHANGELOG.md` diff (entries newer than the project's stamp).
@@ -217,6 +230,7 @@ The non-obvious traps — scan these before bootstrapping or upgrading. Each is
217
230
  - **Source vs target directory.** Templates and scripts are read from the skill's own dir (`${CLAUDE_SKILL_DIR}/` in Claude Code, the `SKILL.md` folder elsewhere). The **working directory is the target project** — never write kernel files back into the skill.
218
231
  - **The `Co-Authored-By` trailer is added by the harness, not by prose.** When attribution is `off`, a doc directive alone won't stop it — for Claude Code you **must** also set `"includeCoAuthoredBy": false` in the project's `.claude/settings.json` (create it if absent). Other tools: disable their equivalent co-author/footer setting.
219
232
  - **Hidden mode must never touch `package.json`.** Editing it is a *tracked* change and leaks the whole system. Hidden mode wires nothing into `package.json`; the pre-commit hook (untracked in `.git/hooks/`) calls `node scripts/<x>.mjs` directly. After hiding, **verify `git status` shows the artifacts as ignored**.
233
+ - **Hidden mode is project-local, and the hide tool owns the known footprint.** `tools/hide-footprint.mjs` writes **one managed block** in the **project-local `.git/info/exclude`** — never the machine-global `core.excludesFile` (which would silently affect every repo on the host; **AD-014** amends **AD-006**). It hides the kit's own artifacts **and** the known external AI/agent footprint (the `KNOWN_FOOTPRINT` table in [contracts](references/contracts.md#visibility-contract)). A **tracked** file is **never silently un-tracked** — the tool prints the `git rm --cached` it will not run. Never leak its internal marker / asks terms to the user; translate every outcome to plain language.
220
234
  - **`CLAUDE.md` is a symlink, not a copy.** `ln -s AGENTS.md CLAUDE.md` — single source, no duplication. A copy drifts; a symlink can't.
221
235
  - **Never overwrite an existing entry point or hook.** If `AGENTS.md` / `CLAUDE.md` already exist, or the installer reports a pre-existing non-marker git hook, **stop and ask** the user to merge vs replace — don't clobber.
222
236
  - **No Node runtime → skip enforcement.** If the project has no Node (recon step 1), skip bootstrap steps 8–9 (scripts + hook) and follow the cap/archive/index policy manually, or port the scripts to the project's language.
@@ -278,6 +292,6 @@ Deploy these into `AGENTS.md`; remove rows that don't apply to the stack.
278
292
  - [`references/scripts/`](references/scripts/) — the Node enforcement scripts (caps + staleness + index-freshness gate, 3-tier archive, hook installer) and their unit tests.
279
293
  - [`migrations/`](migrations/) — per-version upgrade steps; see `migrations/README.md`.
280
294
  - [`launchers/`](launchers/) — run the bootstrapper from non-Claude agents (`SKILL.md` is a native Codex skill; a Devin Desktop workflow launcher + install script). See `launchers/README.md`.
281
- - [`tools/`](tools/) — the family-wide tooling the kit **owns and ships**: `manifest/{schema.md,validate.mjs}` (the `capability.json` schema + the validator the kit runs as the memory detector, and root CI invokes), `delegation.mjs` (the executable delegate/fallback decision + hand-off plan), `inject-methodology.mjs` + `methodology-slot.md` (the bounded slot reconciliation — ensure-slot / inject-if-empty / cap; the fragment is a byte-identical mirror of the `agent-workflow-engine` canon, pinned by `methodology-mirror.test.mjs`), `detect-backends.mjs` (the read-only **backend detector** behind `/agent-workflow-kit backends`, plus the axis-aware `guideFor`), `setup-backends.mjs` (the **link-only** backend setup behind `/agent-workflow-kit setup` — place the bundled bridge + link wrappers), `fs-safe.mjs` (the shared symlink-traversal-safe copy/link primitives both `setup-backends` and the npx installer use), and `release-scan.mjs` (the attribution-off release gate). The bundled bridge skill mirrors live under [`bridges/`](bridges/) (byte-identical to the repo-root bridges, pinned by `test/bridges-mirror.test.mjs`). See [`tools/manifest/schema.md`](tools/manifest/schema.md).
295
+ - [`tools/`](tools/) — the family-wide tooling the kit **owns and ships**: `manifest/{schema.md,validate.mjs}` (the `capability.json` schema + the validator the kit runs as the memory detector, and root CI invokes), `delegation.mjs` (the executable delegate/fallback decision + hand-off plan), `inject-methodology.mjs` + `methodology-slot.md` (the bounded slot reconciliation — ensure-slot / inject-if-empty / cap; the fragment is a byte-identical mirror of the `agent-workflow-engine` canon, pinned by `methodology-mirror.test.mjs`), `detect-backends.mjs` (the read-only **backend detector** behind `/agent-workflow-kit backends`, plus the axis-aware `guideFor`), `setup-backends.mjs` (the **link-only** backend setup behind `/agent-workflow-kit setup` — place the bundled bridge + link wrappers), `fs-safe.mjs` (the shared symlink-traversal-safe copy/link primitives both `setup-backends` and the npx installer use), `known-footprint.mjs` + `hide-footprint.mjs` (the **hidden-mode** registry + the single hide-writer behind step 9 / the upgrade reconcile — one managed block in the **project-local** `.git/info/exclude` covering the full AI/agent footprint; pinned by `known-footprint.test.mjs` drift-guard + `hide-footprint.test.mjs` / `.integration.test.mjs`), and `release-scan.mjs` (the attribution-off release gate). The bundled bridge skill mirrors live under [`bridges/`](bridges/) (byte-identical to the repo-root bridges, pinned by `test/bridges-mirror.test.mjs`). See [`tools/manifest/schema.md`](tools/manifest/schema.md).
282
296
  - [`capability.json`](capability.json) — the kit's own `agent-workflow` family manifest (`kind: composition-root`).
283
297
  - [`CHANGELOG.md`](CHANGELOG.md) — version history of this kernel.
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-kit",
5
5
  "kind": "composition-root",
6
- "version": "1.9.0",
6
+ "version": "1.10.0",
7
7
  "provides": [],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-kit",
3
- "version": "1.9.0",
3
+ "version": "1.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",
@@ -17,7 +17,27 @@ The user chooses at bootstrap whether the AI artifacts are visible in the repo o
17
17
  diverge:
18
18
 
19
19
  - **visible** — artifacts are committed. Wire the project's `package.json` scripts (`docs:check` / `docs:index` / `docs:index:check` / `docs:archive` / `docs:archive:check` / `docs:archive:issues` / `docs:archive:issues:check` / `prepare: node scripts/install-git-hooks.mjs`) and add a minimal `.gitignore` (`docs/plans/`, `.claude/settings.local.json`). This is the canonical model.
20
- - **hidden** (in-tree) — same files on disk, but the repo "looks normal": append the artifact paths (`AGENTS.md`, `CLAUDE.md`, `docs/ai/`, `docs/plans/`, `scripts/*.mjs` you added, `docs/ai/.workflow-version`) to the global excludes file git **already uses** (`git config --get core.excludesFile`); if none is set, point it at `~/.gitignore_global` (`git config --global core.excludesFile ~/.gitignore_global`) and append there. **Verify `git status` shows the artifacts as ignored** afterwards. **Do not edit `package.json`** — that is a tracked change and would leak; the pre-commit hook (always untracked in `.git/hooks/`) calls the scripts via `node scripts/<x>.mjs` directly.
20
+ - **hidden** (in-tree) — same files on disk, but the repo "looks normal": the AI/agent footprint is git-ignored via **one managed block in the project-local `.git/info/exclude`** (resolved with `git rev-parse --git-path info/exclude` — never the machine-global `core.excludesFile`, which would affect every repo on the host; **AD-014** amends **AD-006**). The kit's `tools/hide-footprint.mjs` is the single writer: it covers `KIT_OWN_PATHS ∪ the present subset of KNOWN_FOOTPRINT` (the full footprint table below — the kit's own artifacts **and** every other AI/agent tool's files). Per path: a **tracked** file → **ASK** (an exclude does nothing for it; only `git rm --cached` un-tracks it — the tool prints that command and never runs it); an untracked path already covered by a **tracked `.gitignore`** → dropped (redundant); a **present** file whose name is generic enough to be ambiguous (`falsePositiveRisk`) → **ASK**; everything else → **hidden**. `asks` are excluded from the block unless explicitly opted in. **Verify** treats a path as hidden only when it is **untracked AND ignored by our project-local block** (or a tracked `.gitignore`) being ignored by the global excludes does **not** count. Re-running re-derives the block wholesale (sorted/deduped) a clean re-run is a **zero-diff** no-op. On an existing global-excludes deployment the tool **detects + reports the residual legacy machine-global block and keeps it by default** (a harmless double-ignore; the local block wins precedence); removal is the explicit `--remove-global` (it prints the removed lines as a restorable backup), which **the agent only runs after asking** — another of the user's hidden repos on the same host may rely on the same root-anchored global lines. **Do not edit `package.json`** — that is a tracked change and would leak; the pre-commit hook (always untracked in `.git/hooks/`) calls the scripts via `node scripts/<x>.mjs` directly. Windows is supported (text edit, no symlinks; CRLF preserved).
21
+
22
+ **Known AI/agent footprint** (the `KNOWN_FOOTPRINT` registry in `tools/known-footprint.mjs`; this table is its human mirror, kept in sync by review — D11):
23
+
24
+ | Pattern | Owner | Kind | Commit-risk name? | Note |
25
+ |---|---|---|---|---|
26
+ | `/.claude/skills/` | Claude Code | dir | no | local-dev skills; absorbs the AD-013 one-off |
27
+ | `/.cursor/rules/` | Cursor | dir | no | project rule files |
28
+ | `/.cursorrules` | Cursor (legacy) | file | **yes** | legacy single-file rules |
29
+ | `/.codeium/` | Codeium/Windsurf | dir | no | home-scoped launchers live under `~/`, out of scope |
30
+ | `/.windsurf/` | Windsurf (Devin) | dir | no | project config dir |
31
+ | `/.windsurfrules` | Windsurf | file | **yes** | legacy single-file rules |
32
+ | `/GEMINI.md` | Gemini/Antigravity | file | **yes** | context file; generic name |
33
+ | `/.antigravity.md` | Antigravity | file | **yes** | context file |
34
+ | `/.github/copilot-*` | GitHub Copilot | file | **yes** | one reviewed glob; covers `copilot-instructions.md` |
35
+ | `/.aider.conf.yml` | Aider | file | no | config |
36
+ | `/.aider.chat.history.md` | Aider | file | no | chat history |
37
+ | `/.aider.input.history` | Aider | file | no | input history |
38
+ | `/.continue/` | Continue | dir | no | project config dir |
39
+
40
+ The kit's OWN footprint (`KIT_OWN_PATHS`) — `AGENTS.md`, `CLAUDE.md`, `docs/ai/` (subsumes the stamp), the added `scripts/*.mjs`, `docs/plans/`, `.claude/settings.local.json`, and `.claude/settings.json` (hidden-only — visible mode commits it) — is always a candidate in hidden mode.
21
41
 
22
42
  Not in this version: a fully-external hidden mode (artifacts relocated outside the repo tree).
23
43
  Deferred to a later release + migration.
@@ -0,0 +1,168 @@
1
+ // Integration acceptance for hide-footprint against a REAL `git` — what injected-git mocks cannot
2
+ // prove: real `git rev-parse --git-path info/exclude` (incl. a linked worktree), real precedence
3
+ // (tracked .gitignore > .git/info/exclude > global core.excludesFile), and the delegated-memory
4
+ // hand-off (fresh + stale). Every test ISOLATES the git environment (per-test HOME + GIT_CONFIG_GLOBAL
5
+ // + GIT_CONFIG_NOSYSTEM) so the host's real ~/.gitignore_global — which already hides /AGENTS.md,
6
+ // /docs/ai/ — cannot make a real check-ignore pass/fail for host-state reasons (Review fold R2 codex#8).
7
+
8
+ import { describe, it, before, beforeEach, afterEach } from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import { mkdtempSync, rmSync, writeFileSync, mkdirSync, readFileSync, existsSync } from 'node:fs';
11
+ import { tmpdir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ import { execFileSync } from 'node:child_process';
14
+ import { hideFootprint, excludePath, START_MARKER } from './hide-footprint.mjs';
15
+
16
+ let gitOk = true;
17
+ before(() => {
18
+ try { execFileSync('git', ['--version'], { encoding: 'utf8' }); } catch { gitOk = false; }
19
+ });
20
+
21
+ const made = [];
22
+ const mkdtemp = (tag) => { const d = mkdtempSync(join(tmpdir(), tag)); made.push(d); return d; };
23
+ afterEach(() => { while (made.length) { try { rmSync(made.pop(), { recursive: true, force: true }); } catch { /* best effort */ } } });
24
+
25
+ // An isolated repo: empty global config (no host excludesFile), committer identity in env.
26
+ const setup = () => {
27
+ const home = mkdtemp('aw-home-');
28
+ const dir = mkdtemp('aw-repo-');
29
+ const gcfg = join(home, '.gitconfig');
30
+ writeFileSync(gcfg, '');
31
+ const env = {
32
+ ...process.env, HOME: home, GIT_CONFIG_GLOBAL: gcfg, GIT_CONFIG_NOSYSTEM: '1',
33
+ GIT_AUTHOR_NAME: 'T', GIT_AUTHOR_EMAIL: 't@e', GIT_COMMITTER_NAME: 'T', GIT_COMMITTER_EMAIL: 't@e',
34
+ };
35
+ const git = (args, cwd = dir) => execFileSync('git', args, { cwd, env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
36
+ git(['init', '-q', '-b', 'main']);
37
+ return { home, dir, gcfg, env, git, deps: { env, home } };
38
+ };
39
+ const readExclude = (dir) => { const p = join(dir, '.git/info/exclude'); return existsSync(p) ? readFileSync(p, 'utf8') : ''; };
40
+ const count = (s, sub) => s.split(sub).length - 1;
41
+ const checkIgnoreSource = (git, dir, probe) => {
42
+ try { return git(['check-ignore', '-v', '--', probe], dir).split('\t')[0]; } catch { return null; }
43
+ };
44
+
45
+ describe('hide-footprint integration (real git)', { skip: !gitOk }, () => {
46
+ let env;
47
+ beforeEach(() => { env = setup(); });
48
+
49
+ it('resolves info/exclude, writes ONE managed block, and real check-ignore reports it', () => {
50
+ const { dir, git, deps } = env;
51
+ writeFileSync(join(dir, 'AGENTS.md'), '# entry\n');
52
+ const ep = excludePath(deps, dir);
53
+ assert.ok(ep.endsWith('.git/info/exclude'));
54
+ const r = hideFootprint({ dir }, deps);
55
+ assert.equal(r.action, 'created');
56
+ const content = readExclude(dir);
57
+ assert.equal(count(content, START_MARKER), 1, 'exactly one managed block');
58
+ assert.match(checkIgnoreSource(git, dir, 'AGENTS.md') ?? '', /info\/exclude/);
59
+ // Idempotent: a second run changes nothing.
60
+ const r2 = hideFootprint({ dir }, deps);
61
+ assert.equal(r2.action, 'noop');
62
+ assert.equal(readExclude(dir), content);
63
+ });
64
+
65
+ it('works inside a linked git worktree', () => {
66
+ const { dir, git, deps, env: e } = env;
67
+ git(['commit', '-q', '--allow-empty', '-m', 'init']);
68
+ const wt = mkdtemp('aw-wt-');
69
+ rmSync(wt, { recursive: true, force: true }); // git worktree add wants a non-existent path
70
+ git(['worktree', 'add', '-q', wt]);
71
+ writeFileSync(join(wt, 'AGENTS.md'), '# entry\n');
72
+ const r = hideFootprint({ dir: wt }, deps);
73
+ assert.equal(r.visibility, 'hidden');
74
+ assert.equal(count(r.wrote.join('\n'), '/AGENTS.md'), 1);
75
+ assert.match(checkIgnoreSource((a, c) => execFileSync('git', a, { cwd: c, env: e, encoding: 'utf8' }), wt, 'AGENTS.md') ?? '', /exclude/);
76
+ });
77
+
78
+ it('precedence: a path in a TRACKED .gitignore is dropped (redundant), reported by .gitignore', () => {
79
+ const { dir, git, deps } = env;
80
+ writeFileSync(join(dir, '.gitignore'), '/docs/ai/\n');
81
+ git(['add', '.gitignore']);
82
+ const r = hideFootprint({ dir }, deps);
83
+ assert.ok(r.dropped.includes('/docs/ai/'), 'covered by tracked .gitignore → dropped');
84
+ assert.ok(!r.wrote.includes('/docs/ai/'));
85
+ assert.match(checkIgnoreSource(git, dir, 'docs/ai/') ?? '', /\.gitignore/);
86
+ });
87
+
88
+ it('precedence: a path in BOTH global excludes and the local block is reported by the LOCAL block', () => {
89
+ const { dir, git, deps } = env;
90
+ const globalExcludes = join(env.home, 'gitignore_global');
91
+ writeFileSync(globalExcludes, '/AGENTS.md\n');
92
+ git(['config', 'core.excludesFile', globalExcludes]);
93
+ writeFileSync(join(dir, 'AGENTS.md'), '# entry\n');
94
+ const r = hideFootprint({ dir }, deps); // default keeps the global block
95
+ assert.ok(r.wrote.includes('/AGENTS.md'));
96
+ assert.match(checkIgnoreSource(git, dir, 'AGENTS.md') ?? '', /info\/exclude/, 'local exclude wins precedence');
97
+ assert.equal(r.global.action, 'kept');
98
+ });
99
+
100
+ it('a machine-global core.excludesFile NAMED .gitignore is not mistaken for a project .gitignore (no spurious STOP)', () => {
101
+ const { dir, git, deps } = env;
102
+ const globalGitignore = join(env.home, '.gitignore'); // basename collides with a project .gitignore
103
+ writeFileSync(globalGitignore, '/AGENTS.md\n');
104
+ git(['config', 'core.excludesFile', globalGitignore]);
105
+ writeFileSync(join(dir, 'AGENTS.md'), '# entry\n');
106
+ const r = hideFootprint({ dir }, deps); // must not STOP probing an outside-repo path
107
+ assert.ok(r.wrote.includes('/AGENTS.md'), 'AGENTS.md hidden project-local, not dropped as a "tracked .gitignore"');
108
+ assert.match(checkIgnoreSource(git, dir, 'AGENTS.md') ?? '', /info\/exclude/);
109
+ });
110
+
111
+ it('delegated-hidden, FRESH memory: bare project-local lines are absorbed into ONE canonical fence', () => {
112
+ const { dir, deps } = env;
113
+ // What a 1.1.0 memory writes project-local (bare, no fence):
114
+ writeFileSync(join(dir, '.git/info/exclude'), '/AGENTS.md\n/CLAUDE.md\n/docs/ai/\n');
115
+ const r = hideFootprint({ dir }, deps);
116
+ const content = readExclude(dir);
117
+ assert.equal(count(content, START_MARKER), 1, 'one fence');
118
+ assert.equal(count(content, '/AGENTS.md\n'), 1, 'no duplicate AGENTS.md rule');
119
+ // the bare lines are now INSIDE the fence, not loose above it.
120
+ assert.ok(content.indexOf('/AGENTS.md') > content.indexOf(START_MARKER));
121
+ assert.ok(r.wrote.includes('/AGENTS.md') && r.wrote.includes('/docs/ai/'));
122
+ });
123
+
124
+ it('delegated-hidden, STALE memory: a memory-old GLOBAL block is migrated away with --remove-global', () => {
125
+ const { dir, git, deps } = env;
126
+ const globalExcludes = join(env.home, 'gitignore_global');
127
+ writeFileSync(globalExcludes, '# agent-workflow-kit hidden mode (machine-local; remove these lines to un-hide)\n/AGENTS.md\n/docs/ai/\n/docs/ai/.memory-version\n');
128
+ git(['config', 'core.excludesFile', globalExcludes]);
129
+ writeFileSync(join(dir, 'AGENTS.md'), '# entry\n');
130
+ const r = hideFootprint({ dir, removeGlobal: true }, deps);
131
+ assert.equal(r.global.action, 'removed');
132
+ assert.ok(!readFileSync(globalExcludes, 'utf8').includes('/AGENTS.md'), 'global block removed');
133
+ assert.match(checkIgnoreSource(git, dir, 'AGENTS.md') ?? '', /info\/exclude/, 'now hidden project-local');
134
+ });
135
+
136
+ describe('visibility inference (D16)', () => {
137
+ it('tracked anchor → VISIBLE → --reconcile writes zero bytes', () => {
138
+ const { dir, git, deps } = env;
139
+ writeFileSync(join(dir, 'AGENTS.md'), '# entry\n');
140
+ git(['add', 'AGENTS.md']);
141
+ const before = readExclude(dir);
142
+ const r = hideFootprint({ dir, reconcile: true }, deps);
143
+ assert.equal(r.visibility, 'visible');
144
+ assert.equal(readExclude(dir), before, 'no bytes written in visible mode');
145
+ });
146
+
147
+ it('untracked + ignored anchor → HIDDEN → --reconcile runs the hide', () => {
148
+ const { dir, git, deps } = env;
149
+ const globalExcludes = join(env.home, 'gitignore_global');
150
+ writeFileSync(globalExcludes, '/AGENTS.md\n');
151
+ git(['config', 'core.excludesFile', globalExcludes]);
152
+ writeFileSync(join(dir, 'AGENTS.md'), '# entry\n');
153
+ const r = hideFootprint({ dir, reconcile: true }, deps);
154
+ assert.equal(r.visibility, 'hidden');
155
+ assert.ok(r.wrote.includes('/AGENTS.md'));
156
+ });
157
+
158
+ it('untracked + not ignored anchor → AMBIGUOUS → --reconcile surfaces it, writes nothing', () => {
159
+ const { dir, deps } = env;
160
+ writeFileSync(join(dir, 'AGENTS.md'), '# entry\n');
161
+ const before = readExclude(dir);
162
+ const r = hideFootprint({ dir, reconcile: true }, deps);
163
+ assert.equal(r.ambiguous, true);
164
+ assert.equal(r.visibility, 'ambiguous');
165
+ assert.equal(readExclude(dir), before, 'ambiguous → no write, agent ASKs');
166
+ });
167
+ });
168
+ });