@sabaiway/agent-workflow-kit 14.14.0 → 14.14.1

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,20 @@ 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
+ ## 14.14.1 — the agents writer stops on a project folder given as a symlink, before it reads or writes anything (AD-161)
8
+
9
+ **`cheap-agents.mjs --cwd <dir>` now refuses a `<dir>` that is itself a symlink.** Until 14.14.1 an `--apply` there
10
+ wrote the vehicles through the link. With a hand-edited executor it placed `changelog-skeleton.md` and then failed on
11
+ the executor with an uncoded error, leaving `.claude/agents/` half placed; with a trailing `/` even that executor was
12
+ rewritten through the link.
13
+
14
+ - **The refusal.** `<link>`, `<link>/` and `<link>/.` stop with `CHEAP_AGENTS_SYMLINK`, exit 1, and a message naming
15
+ the project root as a symlink the writer refuses to write through. It is the first step of `--dry-run` and
16
+ `--apply` alike and of a direct `preflightCheapAgents` call: nothing under the root is read, nothing is written.
17
+ - **What stays.** A real directory, a regular file, an absent path and a project reached through a linked parent
18
+ directory get the same answers as in 14.14.0, and so does the default, the current directory. The mode doc's
19
+ symlink-safe line and the tool header name the project root.
20
+
7
21
  ## 14.14.0 — the Recommendations screen lists each step of the reference setup you have not taken or declined, with its preview command or the reason it has none (AD-160)
8
22
 
9
23
  **`/agent-workflow-kit recommendations` now shows what your project still lacks of the full flow.** Until 14.14.0
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". New to the epic, story and task tier? Run `/agent-workflow-kit tier`.
4
4
  disable-model-invocation: true
5
5
  metadata:
6
- version: '14.14.0'
6
+ version: '14.14.1'
7
7
  ---
8
8
 
9
9
  # agent-workflow-kit
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": "14.14.0",
6
+ "version": "14.14.1",
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": "14.14.0",
3
+ "version": "14.14.1",
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",
@@ -19,7 +19,7 @@ Writing code, running gates, and user-facing copy never move to these four vehic
19
19
  Run `node ${CLAUDE_SKILL_DIR}/tools/cheap-agents.mjs [--dry-run | --apply] [--cwd <dir>]`:
20
20
 
21
21
  1. **`--dry-run` first, always** (the default — changes nothing). It previews, per bundled vehicle, whether it **would place** the file, finds it **already current**, or finds a **customized** file (different content) that will be **preserved, never overwritten** (delete the file to reseed it from the bundle) — except the executor, whose differing bytes **would re-derive** from `docs/ai/vehicles.json` and are rewritten on `--apply` (a hand edit is replaced, never preserved).
22
- 2. **Only on an explicit yes**, re-run with `--apply`. It writes **only** under `.claude/agents/` — never `settings.json` / `settings.local.json`, never a commit. `--apply` is deployment-gated (the stamp must be at the lineage head) and symlink-safe (a symlinked `.claude` / `.claude/agents` / target file is a STOP).
22
+ 2. **Only on an explicit yes**, re-run with `--apply`. It writes **only** under `.claude/agents/` — never `settings.json` / `settings.local.json`, never a commit. `--apply` is deployment-gated (the stamp must be at the lineage head) and symlink-safe (a symlinked project root / `.claude` / `.claude/agents` / target file is a STOP, in a `--dry-run` too).
23
23
  3. **Hidden-mode deployments:** after apply, run the hide-footprint reconcile (`node ${CLAUDE_SKILL_DIR}/tools/hide-footprint.mjs --dir <project> --reconcile`) so the placed files stay invisible to `git status` — `/.claude/agents/` is in the known-footprint registry; the apply report reminds you.
24
24
 
25
25
  **Invariants:** writer (writes only `.claude/agents/`) · preview by default · a diverged existing file is reported and preserved, never clobbered — except the executor, re-derived from `docs/ai/vehicles.json` on apply · never touches settings · never commits · **no READ-ONLY vehicle grants `Bash`, and exactly one bundled vehicle, `executor`, grants a shell** · the cheap-lane vehicles are pinned to `model: haiku` + `effort: low`, and the review lens is pinned OFF the cheap model (all content-tested).
@@ -12,8 +12,8 @@
12
12
  // • deployment-gated — `--apply` STOPs unless docs/ai/.workflow-version equals the lineage
13
13
  // head (a dry-run stays usable whatever the stamp says; an unreadable orchestration config
14
14
  // STOPs both, since the derived lenses it names cannot be known);
15
- // • symlink-safe — a symlinked `.claude` / `.claude/agents` / target file is a STOP, never a
16
- // write-through;
15
+ // • symlink-safe — a symlinked project root, `.claude`, `.claude/agents` or target file is a
16
+ // STOP, in a dry-run too, never a write-through;
17
17
  // • preserves differing .claude/agents/ files (`customized — preserved`), except the executor:
18
18
  // on apply its body is re-derived from docs/ai/vehicles.json, replacing a hand edit;
19
19
  // an identical file is `already current` (idempotent re-run);
@@ -31,7 +31,7 @@
31
31
  // Dependency-free, Node >= 22. No side effects on import.
32
32
 
33
33
  import { mkdirSync, writeFileSync } from 'node:fs';
34
- import { join } from 'node:path';
34
+ import { join, resolve } from 'node:path';
35
35
  import { fileURLToPath } from 'node:url';
36
36
  import { isDirectRun } from './direct-run.mjs';
37
37
  import { shellQuoteArg } from './repo-lex.mjs';
@@ -117,9 +117,21 @@ const writeFsDeps = (deps = {}) => ({
117
117
  writeFile: deps.writeFile ?? writeFileSync,
118
118
  });
119
119
 
120
+ const refuseSymlinkedRoot = (projectDir, deps) => {
121
+ const root = resolve(projectDir);
122
+ let stat;
123
+ try {
124
+ stat = readFsDeps(deps).lstat(root);
125
+ } catch {
126
+ return;
127
+ }
128
+ if (stat.isSymbolicLink()) throw makeCheapAgentsError(CHEAP_AGENTS_SYMLINK, `the project root ${root} is a symlink — refusing to write through it`);
129
+ };
130
+
120
131
  export const preflightCheapAgents = ({ cwd, derived = [] }, deps = {}) => {
121
132
  const fs = readFsDeps(deps);
122
133
  const projectDir = cwd ?? process.cwd();
134
+ refuseSymlinkedRoot(projectDir, deps);
123
135
  const templatesByName = new Map(readBundledAgents(deps).map((template) => [template.name, template]));
124
136
  for (const template of derived) templatesByName.set(template.name, template);
125
137
  const templates = [...templatesByName.values()];
@@ -169,6 +181,7 @@ export const applyCheapAgentsCommand = (root) =>
169
181
  export const writeCheapAgents = ({ cwd, dryRun = true } = {}, deps = {}) => {
170
182
  const fs = writeFsDeps(deps);
171
183
  const projectDir = cwd ?? process.cwd();
184
+ refuseSymlinkedRoot(projectDir, deps);
172
185
  const posture = loadExecutorPostureOrStop(projectDir, deps);
173
186
  const derived = [executorTemplate(posture, deps), ...configuredDerivedTemplates(projectDir, deps)];
174
187
  const preflight = preflightCheapAgents({ cwd: projectDir, derived }, deps);