@execuro-sw-ecosystem/sw-ecosystem-agentic-harness 0.1.3 → 0.1.5

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
@@ -7,6 +7,32 @@ release.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.5] - 2026-09-17
11
+
12
+ ### Fixed
13
+
14
+ - **`install`/`apply` no longer write into every detected host when
15
+ `--host` is omitted.** They now resolve which host(s) to use in order:
16
+ `--host` if given; else the hosts a lock file already records; else an
17
+ interactive picker when a terminal is attached; else exit 2 naming the
18
+ four `install --host <h>` commands as `help`, instead of silently
19
+ defaulting to Claude Code, Codex, Copilot *and* Cursor. `status` and
20
+ `uninstall` keep covering every host by default, and `plan` previews all
21
+ four and says so when nothing was selected.
22
+
23
+ ## [0.1.4] - 2026-09-17
24
+
25
+ ### Added
26
+
27
+ - **`sw-setup` gained a twelfth readiness row, "Package updates".** It checks
28
+ whether the harness, the Specs Editor and the Tender Discovery Tool are at
29
+ their target versions (the harness at npm's latest, each editor at its own
30
+ `extra_components[].version` pin, never npm's latest) via three
31
+ `npm view` lookups, each with a 10-second timeout — a failed or timed-out
32
+ lookup reports "could not check" rather than failing the row. Folds into
33
+ step 3's single question; never blocks readiness. The KB MCP keeps its
34
+ existing row (registered as `@latest`, nothing to update).
35
+
10
36
  ## [0.1.3] - 2026-09-17
11
37
 
12
38
  ### Changed
package/README.md CHANGED
@@ -49,18 +49,26 @@ declines is fully set up without them, and `--editor` on `sw-design-requirements
49
49
 
50
50
  | Command | What it does |
51
51
  | --- | --- |
52
- | `install` | installs into every detected host; the same as `apply --yes` |
52
+ | `install` | installs into the resolved host(s) (see Flags below); the same as `apply --yes` |
53
53
  | `status` | read-only; what is installed, what drifted, which optional extra components are present |
54
54
  | `plan` | the exact changes, written as a diff; writes nothing |
55
55
  | `apply --yes` | performs them |
56
56
  | `uninstall --yes` | removes what the lock file records |
57
57
  | `guide` | the install protocol, the single source of truth |
58
58
 
59
- Flags: `--host claude-code|codex|copilot|cursor` (repeatable; omitting it
60
- selects every host that is present), `--scope project|user` (default
61
- `project`), `--root <path>` (default the current directory), `--yes` (required
62
- by `apply` and `uninstall`, never prompts — `install` implies it), and
63
- `--no-extra-components` (skip detection of the optional editor packages).
59
+ Flags: `--host claude-code|codex|copilot|cursor` (repeatable), `--scope
60
+ project|user` (default `project`), `--root <path>` (default the current
61
+ directory), `--yes` (required by `apply` and `uninstall` — `install` implies
62
+ it), and `--no-extra-components` (skip detection of the optional editor
63
+ packages).
64
+
65
+ `install`/`apply` resolve which host(s) to write into, in order: `--host` if
66
+ given; else the hosts a lock file already records (a re-run needs no
67
+ question); else, with a terminal attached, an interactive picker; else exit 2
68
+ with the four `install --host <h>` commands as `help` — it never guesses and
69
+ never hangs. `status` and `uninstall` are unaffected by this and keep
70
+ covering every host by default; `plan` writes nothing, so with no `--host` and
71
+ no lock it previews all four and says so.
64
72
 
65
73
  ## Install
66
74
 
@@ -74,11 +82,14 @@ writes anything.
74
82
  Then restart your coding agent — every host reads its configuration at startup.
75
83
  The command's output tells you which ones and how.
76
84
 
77
- `apply` refuses without `--yes` and never prompts: Codex's question tool does
78
- not block outside Plan mode, and CI has no terminal. Add `--host claude-code`
79
- (repeatable) to install into one agent, and `--scope user` to install into your
80
- home directory instead of this repository. The default scope is the project —
81
- nothing is written under `~` unless you ask.
85
+ `apply` refuses without `--yes`. Neither `apply` nor `install` prompts for
86
+ consent — typing the verb, or passing `--yes`, is the consent — but with no
87
+ `--host`, no existing lock and no terminal (Codex's question tool does not
88
+ block outside Plan mode, and CI has no terminal either), it exits 2 rather
89
+ than guess. Add `--host claude-code` (repeatable) to install into one agent,
90
+ and `--scope user` to install into your home directory instead of this
91
+ repository. The default scope is the project — nothing is written under `~`
92
+ unless you ask.
82
93
 
83
94
  ### What it writes
84
95
 
package/bin/cli.mjs CHANGED
@@ -36,10 +36,15 @@ const USAGE = `usage: sw-ecosystem-agentic-harness <command> [options]
36
36
  guide print the install protocol
37
37
 
38
38
  install is apply --yes under a friendlier name: the whole install in one
39
- command, no --yes needed — typing the verb is the consent.
39
+ command, no --yes needed — typing the verb is the consent for that write;
40
+ it can still ask which host(s), see below.
40
41
 
41
42
  <h> is one of: claude-code, codex, copilot, cursor. Repeat --host to select
42
- several; omitting it selects every host that is present.
43
+ several. install/apply resolve which host(s) to use, in order: --host if
44
+ given; else the hosts already recorded in the lock file, if one exists;
45
+ else, with a terminal attached, an interactive picker; else exit 2 with
46
+ the four "... install --host <h>" commands as help. status and uninstall
47
+ keep covering every host by default.
43
48
 
44
49
  --scope defaults to project: nothing is written under your home directory
45
50
  unless you ask for it. --root defaults to the current directory.
package/content/AGENTS.md CHANGED
@@ -16,7 +16,7 @@ procedure, and the detail lives in `reference/*.md` files beside its
16
16
  | `sw-discover-tender` | Analyse a client tender (RFP, RFI or RFQ) for a Shopware 6 project into one markdown sidecar, <source basename>-analysis.md. |
17
17
  | `sw-document-feature` | Document one Shopware project feature into the LLM-wiki under docs/project-wiki/ — one feature page per surface (administration/ and/or storefront/), the "why" row in the domain index, ADR pages only promoted from the WIP ADR files sw-design-solution extracted into specs/ once they are accepted and built (with an organised table of contents) or provided by the user (never invented). |
18
18
  | `sw-implement-feature` | Implement a feature from its technical spec (specs/NNNN-slug-spec.md, written by sw-design-solution) following TDD — tests first per acceptance criterion, then the implementation, fixing until every relevant test passes (including that AC's own acceptance/e2e test, run immediately, not deferred), running Shopware's own shopware-cli fixers/static-analysis per AC to conform to Shopware's coding standards as code is written rather than after the fact. |
19
- | `sw-setup` | Print one environment-readiness table for the project's development/test setup — vendor/, Node, the editor CLIs, shopware-cli, the KB MCP, the acceptance-test project, Playwright browsers, its .env, per-plugin test scaffolding, the project wiki and `.gitignore`, plus the installer CLI's own configuration/drift status. |
19
+ | `sw-setup` | Print one environment-readiness table for the project's development/test setup — vendor/, Node, the editor CLIs, shopware-cli, the KB MCP, the acceptance-test project, Playwright browsers, its .env, per-plugin test scaffolding, the project wiki, `.gitignore` and whether the SW AH npm packages are current, plus the installer CLI's own configuration/drift status. |
20
20
  | `sw-verify-feature` | Verify a feature implementation against its technical spec (specs/NNNN-slug-spec.md) by running three independent verifiers in parallel — AC test coverage/health, architecture/guideline compliance, and code quality/static analysis — then cross-checking their verdicts into one final per-AC pass/not pass/partly report. |
21
21
  | `sw-verify-feature-ac-tests` | Verify that a feature's acceptance criteria are backed by tests that actually exist, actually pass, and actually test what their name/comment/PHPDoc claims. |
22
22
  | `sw-verify-feature-architecture` | Verify that a feature's implementation matches the architectural decisions its spec made, and complies with Shopware's own Core conventions plus any written Project guidelines this repo defines. |
package/lib/commands.mjs CHANGED
@@ -15,6 +15,7 @@ import { HOSTS, buildPlan, detectAll, installedCounts } from './hosts.mjs';
15
15
  import * as lockfile from './lock.mjs';
16
16
  import { dedupe, execute, isWritableState, preview, pruneDirs, revert } from './actions.mjs';
17
17
  import { probeAll } from './extra-components.mjs';
18
+ import { askHosts } from './prompt.mjs';
18
19
 
19
20
  const RUN = 'sw-ecosystem-agentic-harness';
20
21
 
@@ -65,13 +66,49 @@ export function buildContext(argv, command) {
65
66
 
66
67
  return {
67
68
  root, scope, yes, projectWiki, extraComponents,
68
- hosts: hosts.length ? [...new Set(hosts)] : [...HOSTS],
69
+ // Empty means "nothing chosen yet" — resolveHosts() applies the
70
+ // per-command fallback once the lock (and, for apply/install, a TTY)
71
+ // is available to consult.
72
+ hosts: [...new Set(hosts)],
69
73
  platform: process.platform,
70
74
  version: PKG_VERSION,
71
75
  lock: null,
72
76
  };
73
77
  }
74
78
 
79
+ /**
80
+ * The host-resolution chain every write command shares:
81
+ * 1. `--host` given → use exactly those. No question.
82
+ * 2. else a lock already records hosts → reuse them. This is an update;
83
+ * the choice was already made. No question.
84
+ * 3. else a TTY is attached (apply/install only) → ask, via lib/prompt.mjs.
85
+ * 4. else → UsageError, exit 2, `help` lists the four runnable commands.
86
+ *
87
+ * `status` and `uninstall` keep their old, ungated default of every host —
88
+ * reporting and removal are not consent points. `plan` writes nothing, so it
89
+ * never reaches step 3: steps 1-2, then it previews all four and says so.
90
+ *
91
+ * Call after `loadLock(ctx)` so `ctx.lock` is populated for step 2.
92
+ */
93
+ async function resolveHosts(ctx, command) {
94
+ if (ctx.hosts.length) return { hosts: [...new Set(ctx.hosts)], chosen: true };
95
+ if (command === 'status' || command === 'uninstall') return { hosts: [...HOSTS], chosen: true };
96
+
97
+ const lockHosts = ctx.lock?.hosts ? Object.keys(ctx.lock.hosts) : [];
98
+ if (lockHosts.length) return { hosts: lockHosts, chosen: true };
99
+
100
+ if (command === 'plan') return { hosts: [...HOSTS], chosen: false };
101
+
102
+ if (process.stdin.isTTY) {
103
+ const chosen = await askHosts(HOSTS);
104
+ if (chosen) return { hosts: chosen, chosen: true };
105
+ }
106
+ throw new UsageError(
107
+ 'no --host given, this repository has no recorded install, and there is no terminal to ask which host(s) to use',
108
+ HOSTS.map((h) => `${RUN} ${command} --host ${h}`),
109
+ );
110
+ }
111
+
75
112
  function head(ctx, extra = {}) {
76
113
  return {
77
114
  version: PKG_VERSION, content_version: CONTENT_VERSION,
@@ -138,6 +175,7 @@ export async function status(argv) {
138
175
  }
139
176
  throw e;
140
177
  }
178
+ ({ hosts: ctx.hosts } = await resolveHosts(ctx, 'status'));
141
179
 
142
180
  const detections = await detectAll(ctx);
143
181
  const { actions, errors } = previewAll(ctx);
@@ -213,11 +251,14 @@ export async function plan(argv) {
213
251
  }
214
252
  throw e;
215
253
  }
254
+ const { hosts, chosen } = await resolveHosts(ctx, 'plan');
255
+ ctx.hosts = hosts;
216
256
  const { actions, errors } = previewAll(ctx);
217
257
  const summary = summarise(actions);
218
258
  const changes = summary.create + summary.update;
219
259
 
220
- let next = changes
260
+ let next = chosen ? '' : 'No --host given and no existing install found, so this previews all four hosts. ';
261
+ next += changes
221
262
  ? `Nothing was written. Run \`${RUN} apply --yes\` to perform the ${changes} change(s).`
222
263
  + (summary.conflict || summary.manual
223
264
  ? ` The ${summary.conflict} conflict(s) and ${summary.manual} manual step(s) will be reported again and left to you.`
@@ -262,6 +303,7 @@ export async function apply(argv, label = 'apply') {
262
303
  }
263
304
  throw e;
264
305
  }
306
+ ({ hosts: ctx.hosts } = await resolveHosts(ctx, label));
265
307
 
266
308
  // An apply we cannot record is an apply we cannot undo. Refuse up front
267
309
  // rather than leave the user with files and no way back.
@@ -370,6 +412,7 @@ export async function uninstall(argv) {
370
412
  }
371
413
  throw e;
372
414
  }
415
+ ({ hosts: ctx.hosts } = await resolveHosts(ctx, 'uninstall'));
373
416
 
374
417
  if (!lock) {
375
418
  // No record means no evidence of what we own. Guessing here would delete
package/lib/guide.mjs CHANGED
@@ -20,9 +20,14 @@ registers the MCP servers and permission rules they depend on.
20
20
  ## The loop
21
21
 
22
22
  1. \`${RUN} install\`
23
- The whole install in one command. It refuses to prompt — Codex's question
24
- tool does not block outside Plan mode, and CI has no terminal — so the verb
25
- itself is the consent. Same as \`apply --yes\`.
23
+ The whole install in one command. It never prompts for consent — typing
24
+ the verb is the consent — but it does still need to know **which** host(s)
25
+ to write into, resolved in order: \`--host\`, if given; else the hosts a
26
+ lock file already records, if this is a re-run; else, with a real
27
+ terminal attached, an interactive picker; else it exits 2 with the four
28
+ \`${RUN} install --host <h>\` commands as \`help\` rather than guess or
29
+ hang — Codex's question tool does not block outside Plan mode, and CI has
30
+ no terminal, so a skill invoking this must always pass \`--host\`.
26
31
 
27
32
  2. \`${RUN} status\`
28
33
  Read-only, optional. Reports which hosts are present, what is already
@@ -34,16 +39,19 @@ registers the MCP servers and permission rules they depend on.
34
39
 
35
40
  4. \`${RUN} apply --yes\`
36
41
  The scriptable name for the same write \`install\` performs — use it for
37
- re-runs and repairs. It refuses without \`--yes\`; there is no prompt, because
38
- Codex's question tool does not block outside Plan mode and CI has no
39
- terminal.
42
+ re-runs and repairs. It refuses without \`--yes\`, and resolves which
43
+ host(s) to touch the same way \`install\` does (see step 1).
40
44
 
41
45
  5. \`${RUN} uninstall --yes\`
42
46
  Removes what the lock file records and reverts the managed config blocks.
43
47
 
44
48
  ## Flags
45
49
 
46
- --host claude-code|codex|copilot|cursor restrict to one host, repeatable
50
+ --host claude-code|codex|copilot|cursor select one host, repeatable —
51
+ see the host-resolution order in
52
+ step 1 above; a skill invoking
53
+ \`install\`/\`apply\` must always
54
+ pass it, since it has no terminal
47
55
  --scope project|user default: project
48
56
  --root <path> default: the current directory
49
57
  --no-extra-components skip the optional extra-component skills
@@ -68,8 +76,9 @@ The exact command is also in a \`skipped\` action's \`remedy\`, and in
68
76
  - **stdout is exactly one JSON object.** Every successful result carries a
69
77
  \`next_step\` string: the literal command to run next. Errors carry a
70
78
  \`help\` array of runnable commands.
71
- - **Exit codes:** 0 success, 2 usage error (your invocation was wrong),
72
- 1 the work could not be done.
79
+ - **Exit codes:** 0 success, 2 usage error (your invocation was wrong,
80
+ including \`install\`/\`apply\` run with no \`--host\`, no existing lock and
81
+ no terminal to ask on), 1 the work could not be done.
73
82
  - **Nothing is silently overwritten.** A file we did not install, or one you
74
83
  edited after we installed it, is reported as \`conflict\` or \`drift\` and
75
84
  left exactly as it is. Its \`remedy\` field tells the user what to do.
package/lib/hosts.mjs CHANGED
@@ -2,9 +2,10 @@
2
2
  // so adding a host or following a schema change is one table to edit.
3
3
  //
4
4
  // Detection is marker-directory existence only — no network, no process scan,
5
- // no `which`. It is allowed to be wrong in the harmless direction: a host that
6
- // is installed but has no marker directory simply is not written to, and
7
- // `--host` forces it.
5
+ // no `which`. It feeds `status` reporting only; it never selects which hosts
6
+ // a write command touches. Which hosts get written to is decided by
7
+ // commands.mjs's `resolveHosts()` chain (`--host`, then a recorded lock,
8
+ // then a TTY prompt, then a usage error) — detection has no vote there.
8
9
 
9
10
  import { homedir } from 'node:os';
10
11
  import { join } from 'node:path';
package/lib/prompt.mjs ADDED
@@ -0,0 +1,72 @@
1
+ // The interactive host picker. Used only when `apply`/`install` reach step 3
2
+ // of the host-resolution chain: no `--host`, no lock to reuse, but a real
3
+ // terminal to ask on.
4
+ //
5
+ // node:readline only — the package has zero dependencies and that must not
6
+ // change. Every prompt and echo goes to STDERR, never stdout: stdout carries
7
+ // exactly one JSON object per run and every skill parses it, so a menu on
8
+ // stdout would corrupt that contract. This module never inspects
9
+ // `process.stdin.isTTY` itself — the caller decides whether to invoke it.
10
+
11
+ import { createInterface } from 'node:readline';
12
+
13
+ const LABELS = {
14
+ 'claude-code': 'Claude Code',
15
+ codex: 'Codex',
16
+ copilot: 'GitHub Copilot',
17
+ cursor: 'Cursor',
18
+ };
19
+
20
+ /**
21
+ * Pure parsing, no I/O — so it can be unit-tested without a TTY.
22
+ *
23
+ * Accepts comma-separated 1-based indexes into `hosts` (e.g. "1,3"), the
24
+ * word "all", or an empty string — both of the latter two mean every host,
25
+ * made explicit rather than left as a silent default. Throws a short,
26
+ * human-readable `Error` on anything else (out of range, not a number,
27
+ * nothing at all).
28
+ */
29
+ export function parseSelection(input, hosts) {
30
+ const trimmed = (input ?? '').trim();
31
+ if (trimmed === '' || trimmed.toLowerCase() === 'all') return [...hosts];
32
+ const parts = trimmed.split(',').map((p) => p.trim()).filter(Boolean);
33
+ if (!parts.length) throw new Error('no selection given');
34
+ const chosen = [];
35
+ for (const part of parts) {
36
+ if (!/^\d+$/.test(part)) throw new Error(`not a number: "${part}"`);
37
+ const n = Number(part);
38
+ if (n < 1 || n > hosts.length) throw new Error(`out of range 1-${hosts.length}: ${part}`);
39
+ const id = hosts[n - 1];
40
+ if (!chosen.includes(id)) chosen.push(id);
41
+ }
42
+ return chosen;
43
+ }
44
+
45
+ function question(rl, text) {
46
+ return new Promise((resolve) => rl.question(text, resolve));
47
+ }
48
+
49
+ /**
50
+ * Ask which host(s) to install into. Re-asks once on unparseable input, then
51
+ * gives up and returns `null` — the caller turns that into the same exit-2
52
+ * usage error the no-TTY path uses, so the output contract stays one shape.
53
+ */
54
+ export async function askHosts(hosts) {
55
+ const rl = createInterface({ input: process.stdin, output: process.stderr, terminal: true });
56
+ const menu = hosts.map((h, i) => ` ${i + 1}. ${LABELS[h] ?? h}`).join('\n');
57
+ const prompt = `Which coding agent(s) should this install into?\n${menu}\n`
58
+ + 'Enter comma-separated numbers (e.g. "1,3"), "all", or press Enter for all: ';
59
+ try {
60
+ for (let attempt = 0; attempt < 2; attempt += 1) {
61
+ const answer = await question(rl, attempt === 0 ? prompt : 'Not understood — try again: ');
62
+ try {
63
+ return parseSelection(answer, hosts);
64
+ } catch {
65
+ // Re-ask once; the loop's second pass falls through to null below.
66
+ }
67
+ }
68
+ return null;
69
+ } finally {
70
+ rl.close();
71
+ }
72
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@execuro-sw-ecosystem/sw-ecosystem-agentic-harness",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Shopware 6 agentic harness: sw-* skills and sub-agents, plus an installer that writes them into Claude Code, Codex, GitHub Copilot and Cursor.",
5
5
  "license": "MIT",
6
6
  "author": "Execuro UG (haftungsbeschränkt)",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "sw-ecosystem-agentic-harness",
4
- "version": "0.1.3",
4
+ "version": "0.1.5",
5
5
  "description": "Shopware 6 agentic harness: sw-* skills and sub-agents for requirements, specs, implementation, verification and documentation, with an installer CLI that writes them into Claude Code, Codex, GitHub Copilot and Cursor. The knowledge-base MCP and the editor tools are separate npm packages.",
6
6
  "author": {
7
7
  "name": "Execuro UG (haftungsbeschränkt)",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sw-setup
3
- description: Print one environment-readiness table for the project's development/test setup — vendor/, Node, the editor CLIs, shopware-cli, the KB MCP, the acceptance-test project, Playwright browsers, its .env, per-plugin test scaffolding, the project wiki and `.gitignore`, plus the installer CLI's own configuration/drift status. Stops when every row is ticked. When rows are missing, asks one yes/no question and delegates the fix to the installer CLI. Use when asked to set up the project, check if the environment is ready, or bootstrap the tests. Never designs, implements or verifies a feature itself — that's sw-design-solution, sw-implement-feature and sw-verify-feature, which call this skill first. Never writes a host configuration file itself — that's the installer CLI.
3
+ description: Print one environment-readiness table for the project's development/test setup — vendor/, Node, the editor CLIs, shopware-cli, the KB MCP, the acceptance-test project, Playwright browsers, its .env, per-plugin test scaffolding, the project wiki, `.gitignore` and whether the SW AH npm packages are current, plus the installer CLI's own configuration/drift status. Stops when every row is ticked. When rows are missing, asks one yes/no question and delegates the fix to the installer CLI. Use when asked to set up the project, check if the environment is ready, or bootstrap the tests. Never designs, implements or verifies a feature itself — that's sw-design-solution, sw-implement-feature and sw-verify-feature, which call this skill first. Never writes a host configuration file itself — that's the installer CLI.
4
4
  when_to_use: Trigger phrases — "set up the project", "is the environment ready", "bootstrap the tests", "sw-setup".
5
5
  allowed-tools: Read Glob Grep AskUserQuestion Skill Bash mcp__ShopwareDevKnowledgeBase__kb_status Bash(npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest *)
6
6
  ---
@@ -47,8 +47,9 @@ It prints one JSON object (`ok`, `next_step`, `help`, and its own
47
47
  `hosts[]`/`drift[]` configuration state). Alongside it, run every
48
48
  environment row's check from `reference/rows.md` in this skill's directory —
49
49
  read-only, seconds each: file/directory existence, one version command, one
50
- `kb_status` call. Never write, never install, never ask a question in this
51
- step.
50
+ `kb_status` call, three `npm view` registry lookups (Package updates row) —
51
+ the one part of step 1 that touches the network, so it is the slow part.
52
+ Never write, never install, never ask a question in this step.
52
53
 
53
54
  Print one checkbox list, one line per row — the environment rows first, then
54
55
  one line per configuration item the CLI's `hosts[]`/`drift[]` reports:
@@ -65,6 +66,7 @@ one line per configuration item the CLI's `hosts[]`/`drift[]` reports:
65
66
  - [ ] ATS env — tests/acceptance/.env missing
66
67
  - [x] Plugin tests — AhCheckout: phpunit + jest ok
67
68
  - [ ] Project wiki — docs/project-wiki/ missing
69
+ - [ ] Package updates — harness 0.1.0 → 0.1.3, Specs Editor not installed
68
70
  - [ ] Installer config — .mcp.json entry: drift (from CLI status)
69
71
  ```
70
72
 
@@ -91,8 +93,10 @@ npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest plan
91
93
 
92
94
  List the concrete steps in plain words: environment-row fixes in order
93
95
  (vendor/ → Visual editors → KB MCP → Acceptance-test project →
94
- Playwright browsers → ATS env → Plugin tests → Project wiki), then the CLI's
95
- planned configuration changes last. Skip any row already ticked.
96
+ Playwright browsers → ATS env → Plugin tests → Project wiki → Package
97
+ updates), then the CLI's planned configuration changes last — Package
98
+ updates comes immediately before those, since updating the harness is what
99
+ may change them. Skip any row already ticked.
96
100
 
97
101
  Ask exactly one question: "Do this and that?" with the steps spelled out in
98
102
  plain words (no command dump). Ask the user with a structured question tool
@@ -138,7 +142,7 @@ line:
138
142
 
139
143
  ## Reference files
140
144
 
141
- `reference/rows.md` in this skill's directory — the eleven environment rows:
145
+ `reference/rows.md` in this skill's directory — the twelve environment rows:
142
146
  check command(s), what "ticked" means, the fix steps verbatim from the
143
147
  guideline, and which skills the row blocks. Host configuration state (what
144
148
  gets installed into `.mcp.json`, permissions, `.gitignore`) is not in this
@@ -1,11 +1,21 @@
1
1
  # Readiness rows
2
2
 
3
- Eleven environment rows. Each: check (read-only), what "ticked" means, the fix
3
+ Twelve environment rows. Each: check (read-only), what "ticked" means, the fix
4
4
  (only run on approval, in this order), and which skills the row blocks when
5
5
  unticked. Host configuration files (`.mcp.json` entries, `.gitignore` lines,
6
6
  permission grants) are not rows here — the installer CLI owns them and
7
7
  reports their state in its own `status`/`plan` output; see `SKILL.md`.
8
8
 
9
+ When that `status` call reports no install at all (no lock file, so no
10
+ recorded hosts), the installer CLI's `apply --yes`/`install` has no `--host`
11
+ to reuse and, run from this skill, no terminal to ask on — it would exit 2.
12
+ Do not pass a guessed `--host`. Report this to the user instead: the first
13
+ install must be run by them directly in a terminal, e.g.
14
+ `npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest install`,
15
+ which then either takes their `--host` or prompts them interactively. Re-run
16
+ this skill's `status` check afterwards — every later `apply --yes` reuses the
17
+ hosts that first run recorded, and this skill can call it directly again.
18
+
9
19
  ## 1. vendor/
10
20
 
11
21
  - **Check:** `vendor/shopware/core` exists, `composer.lock` exists,
@@ -218,3 +228,42 @@ So this row never blocks readiness. It asks.
218
228
  project, verify-ac-tests screenshots). Idempotent — safe to re-run.
219
229
  - **Blocks:** nothing directly; unticked means the next `git status` will
220
230
  show generated/secret files as untracked.
231
+
232
+ ## 12. Package updates (optional)
233
+
234
+ The KB MCP is excluded — it is registered as `@latest` and re-resolves on
235
+ every host launch, so there is nothing to update (see row 5). This row covers
236
+ only the three versioned packages: the harness itself, the Specs Editor and
237
+ the Tender Discovery Tool.
238
+
239
+ This row never blocks readiness. It folds into step 3's single question.
240
+
241
+ - **Check:** three `npm view <pkg> version` calls, each with a 10-second
242
+ timeout — `npm view @execuro-sw-ecosystem/sw-ecosystem-agentic-harness
243
+ version`, `npm view @execuro-sw-ecosystem/sw-specs-editor version`,
244
+ `npm view @execuro-sw-ecosystem/sw-tender-discovery-tool version`. Installed
245
+ versions come from three different places: the harness's from
246
+ `installed_version` in the `status` JSON the skill already ran in step 1
247
+ (from the lock file); the Specs Editor's from `version` in
248
+ `node_modules/@execuro-sw-ecosystem/sw-specs-editor/package.json`; the
249
+ Tender Discovery Tool's the same way, in its own `node_modules` path. A
250
+ failed or timed-out `npm view` call reports "could not check" for that
251
+ package only — never an unticked row, never an error, never a blocked run.
252
+ No network is a normal condition, not a fault.
253
+ - **Ticked:** all three packages at their target version — the harness at
254
+ npm's latest, each editor at the pin its own `status` output names
255
+ (`extra_components[].version`), not at npm's latest.
256
+ - **Unticked:** one line per stale package, `installed → available` — e.g.
257
+ `harness 0.1.1 → 0.1.3`, `Specs Editor 0.1.0 → 0.1.0 (pinned)` when current.
258
+ An editor whose npm latest is ahead of the pin is a harness-release lag,
259
+ not a missing update: report it, but do not offer to install that newer
260
+ version — only a newer harness can raise the pin.
261
+ - **Fix:** update the harness first — a newer harness may carry newer pins:
262
+ `npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest install`.
263
+ That command rewrites the installed `sw-setup` skill itself, so the newer
264
+ skill takes effect on the next run — which step 3 already causes by
265
+ re-running step 1. Re-read the new `status` output, then for each stale
266
+ editor run the same `npm i -D <package>@<version>` shape row 3's Fix uses,
267
+ with `<package>` and `<version>` taken from that editor's own
268
+ `extra_components[]` entry in the re-read `status`.
269
+ - **Blocks:** nothing. Out of date is not broken.