@execuro-sw-ecosystem/sw-ecosystem-agentic-harness 0.1.6 → 0.1.7

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/lib/render.mjs ADDED
@@ -0,0 +1,364 @@
1
+ // The human view of a result body.
2
+ //
3
+ // Every command still builds exactly the same JSON object it always did — that
4
+ // object is the model. This module is the view: pure `body -> string`
5
+ // functions, no I/O, no process state, so the whole renderer is testable
6
+ // without a terminal and without running an install.
7
+ //
8
+ // Design rules, in the order they matter:
9
+ //
10
+ // 1. Nothing changed is ONE line. A file count is not news to a human, and
11
+ // "274 unchanged" reads like a failure to someone who typed `install`.
12
+ // 2. Something changed is reported per agent, in the user's vocabulary —
13
+ // skills and sub-agents by name, config files by path.
14
+ // 3. Conflicts, drift, manual steps and failures are never collapsed into a
15
+ // count: those are the only cases the user has to act on, so each one
16
+ // names its file and its remedy.
17
+ // 4. Never name an agent this project has not installed. A Claude Code user
18
+ // is not told about Codex, Cursor or Copilot.
19
+ //
20
+ // Zero dependencies: bare ANSI, and only when the caller says the stream is a
21
+ // terminal.
22
+
23
+ /** Human labels for the four supported coding agents. */
24
+ export const LABELS = {
25
+ 'claude-code': 'Claude Code',
26
+ codex: 'Codex',
27
+ copilot: 'GitHub Copilot',
28
+ cursor: 'Cursor',
29
+ };
30
+
31
+ /** What each agent needs before it reads the configuration we just wrote. */
32
+ export const RESTART = {
33
+ 'claude-code': 'Restart Claude Code, or run `/mcp` in the running session',
34
+ codex: 'Restart `codex` — .codex/config.toml is read once at startup',
35
+ copilot: 'Reload the VS Code window (Developer: Reload Window) for .vscode/mcp.json',
36
+ cursor: 'Restart Cursor so it re-reads .cursor/mcp.json',
37
+ };
38
+
39
+ function label(id) {
40
+ return LABELS[id] ?? id;
41
+ }
42
+
43
+ // ------------------------------------------------------------------ styling
44
+
45
+ const CODES = { bold: '1', dim: '2', red: '31', green: '32', yellow: '33' };
46
+
47
+ /** A theme whose functions are the identity when colour is off. */
48
+ function theme(color) {
49
+ const wrap = (code) => (color ? (s) => `\x1b[${code}m${s}\x1b[0m` : (s) => s);
50
+ const t = {};
51
+ for (const [name, code] of Object.entries(CODES)) t[name] = wrap(code);
52
+ return t;
53
+ }
54
+
55
+ // --------------------------------------------------------------- primitives
56
+
57
+ const MAX_NAMES = 6;
58
+
59
+ /** "a, b, c and 4 more" — a list a person reads, not a dump. */
60
+ function names(list) {
61
+ const max = MAX_NAMES;
62
+ const all = [...list];
63
+ if (all.length <= max) return all.join(', ');
64
+ return `${all.slice(0, max).join(', ')} and ${all.length - max} more`;
65
+ }
66
+
67
+ function plural(n, one, many = `${one}s`) {
68
+ return `${n} ${n === 1 ? one : many}`;
69
+ }
70
+
71
+ /**
72
+ * What a target is, in the words a user thinks in. A skill is one thing to
73
+ * them even though it is a directory of files; a config file is its path.
74
+ */
75
+ export function classify(target) {
76
+ const skill = /(?:^|\/)skills\/([^/]+)\//.exec(target);
77
+ if (skill) return { kind: 'skill', name: skill[1] };
78
+ const agent = /(?:^|\/)agents\/([^/]+)$/.exec(target);
79
+ if (agent) return { kind: 'agent', name: agent[1].replace(/(\.agent)?\.(md|toml)$/, '') };
80
+ return { kind: 'file', name: target };
81
+ }
82
+
83
+ const VERBS = {
84
+ create: 'added', created: 'added',
85
+ update: 'updated', updated: 'updated',
86
+ removed: 'removed', reverted: 'reverted',
87
+ };
88
+
89
+ const SHARED = '__shared__';
90
+
91
+ /**
92
+ * Group the actions in `states` by agent, then by verb, then by what kind of
93
+ * thing they are. Returns `[{ id, buckets: Map<verb, {skills, agents, files}> }]`
94
+ * in the order the agents first appear.
95
+ */
96
+ export function groupChanges(actions = [], states) {
97
+ const wanted = new Set(states);
98
+ const order = [];
99
+ const groups = new Map();
100
+ for (const action of actions) {
101
+ if (!wanted.has(action.state)) continue;
102
+ const id = !action.host || action.host === 'shared' ? SHARED : action.host;
103
+ if (!groups.has(id)) { groups.set(id, new Map()); order.push(id); }
104
+ const buckets = groups.get(id);
105
+ const verb = VERBS[action.state] ?? action.state;
106
+ if (!buckets.has(verb)) buckets.set(verb, { skill: new Set(), agent: new Set(), file: new Set() });
107
+ const bucket = buckets.get(verb);
108
+ const { kind, name } = classify(action.target);
109
+ // Several actions can target one config file (one per JSON pointer);
110
+ // the user changed one file, so it is named once.
111
+ bucket[kind].add(name);
112
+ (bucket.targets ??= []).push(action.target);
113
+ }
114
+ return order.map((id) => ({ id, buckets: groups.get(id) }));
115
+ }
116
+
117
+ function bucketLines(verb, bucket, { verbose }, t) {
118
+ const lines = [];
119
+ if (verbose) {
120
+ for (const target of bucket.targets ?? []) lines.push(` ${t.dim(verb)} ${target}`);
121
+ return lines;
122
+ }
123
+ if (bucket.skill.size) lines.push(` ${verb} ${plural(bucket.skill.size, 'skill')}: ${names(bucket.skill)}`);
124
+ if (bucket.agent.size) lines.push(` ${verb} ${plural(bucket.agent.size, 'sub-agent')}: ${names(bucket.agent)}`);
125
+ if (bucket.file.size) lines.push(` ${verb} ${names(bucket.file)}`);
126
+ return lines;
127
+ }
128
+
129
+ function groupLines(groups, opts, t) {
130
+ const lines = [];
131
+ for (const { id, buckets } of groups) {
132
+ lines.push(` ${t.bold(id === SHARED ? 'Project files' : label(id))}`);
133
+ for (const [verb, bucket] of buckets) lines.push(...bucketLines(verb, bucket, opts, t));
134
+ }
135
+ return lines;
136
+ }
137
+
138
+ // ------------------------------------------------------------------- issues
139
+
140
+ /** Everything the user has to act on, flattened into one shape. */
141
+ function issues(body) {
142
+ const found = [];
143
+ for (const a of body.actions ?? []) {
144
+ if (a.state === 'conflict' || a.state === 'drift' || a.state === 'failed') {
145
+ found.push({ kind: a.state, target: a.target, reason: a.reason, remedy: a.remedy });
146
+ }
147
+ }
148
+ for (const d of body.drift ?? []) {
149
+ found.push({ kind: d.state ?? 'drift', target: d.target, reason: d.reason, remedy: d.remedy });
150
+ }
151
+ for (const m of body.manual ?? []) {
152
+ found.push({ kind: 'manual', target: m.target ?? m.file, reason: m.reason, remedy: m.remedy });
153
+ }
154
+ for (const e of body.errors ?? []) {
155
+ found.push({ kind: 'failed', target: e.target ?? label(e.host), reason: e.error });
156
+ }
157
+ // The same file can reach us from both `actions` and `drift`; say it once.
158
+ const seen = new Set();
159
+ return found.filter((i) => {
160
+ const key = `${i.kind}:${i.target}`;
161
+ if (seen.has(key)) return false;
162
+ seen.add(key);
163
+ return true;
164
+ });
165
+ }
166
+
167
+ const ISSUE_TITLE = {
168
+ conflict: 'conflict', drift: 'edited since install',
169
+ manual: 'manual step', failed: 'failed',
170
+ };
171
+
172
+ function issueLines(list, t) {
173
+ if (!list.length) return [];
174
+ const paint = (kind) => (kind === 'drift' || kind === 'manual' ? t.yellow : t.red);
175
+ const lines = [` ${t.bold('Needs your attention')}`];
176
+ for (const i of list) {
177
+ lines.push(` ${paint(i.kind)(ISSUE_TITLE[i.kind] ?? i.kind)} ${i.target}`);
178
+ if (i.reason) lines.push(` ${i.reason}`);
179
+ if (i.remedy) lines.push(` ${t.dim('fix:')} ${i.remedy}`);
180
+ }
181
+ return lines;
182
+ }
183
+
184
+ // ------------------------------------------------------------------ restart
185
+
186
+ function restartLines(body, changedIds, t) {
187
+ const ids = changedIds.length
188
+ ? changedIds
189
+ : (body.agents ?? []).map((a) => a.id).filter((id) => RESTART[id]);
190
+ const uniq = [...new Set(ids)].filter((id) => RESTART[id]);
191
+ return uniq.map((id) => ` ${t.dim('→')} ${RESTART[id]}`);
192
+ }
193
+
194
+ // ------------------------------------------------------------ apply/install
195
+
196
+ function renderApply(body, opts, t) {
197
+ const s = body.summary ?? {};
198
+ const changed = (s.created ?? 0) + (s.updated ?? 0);
199
+ const problems = issues(body);
200
+
201
+ if (changed === 0 && problems.length === 0) {
202
+ // ONE line. Nothing changed means nothing to restart and nothing to read.
203
+ return `${t.green('✓')} Shopware agentic harness: already up to date — nothing to change.\n`;
204
+ }
205
+
206
+ const groups = groupChanges(body.actions, ['created', 'updated']);
207
+ const changedIds = groups.map((g) => g.id).filter((id) => id !== SHARED);
208
+ const verb = (s.updated ?? 0) === 0 ? 'Installed' : 'Updated';
209
+ const lines = [];
210
+ lines.push(changed
211
+ ? `${t.green('✓')} ${verb} the Shopware agentic harness.`
212
+ : `${t.yellow('!')} Nothing was written — see below.`);
213
+ if (groups.length) {
214
+ lines.push('');
215
+ lines.push(...groupLines(groups, opts, t));
216
+ }
217
+ if (problems.length) {
218
+ lines.push('');
219
+ lines.push(...issueLines(problems, t));
220
+ }
221
+ if (changed) {
222
+ lines.push('');
223
+ lines.push(...restartLines(body, changedIds, t));
224
+ }
225
+ return `${lines.join('\n')}\n`;
226
+ }
227
+
228
+ // -------------------------------------------------------------------- plan
229
+
230
+ function renderPlan(body, opts, t) {
231
+ const s = body.summary ?? {};
232
+ const changes = (s.create ?? 0) + (s.update ?? 0);
233
+ const problems = issues(body);
234
+ if (changes === 0 && problems.length === 0) {
235
+ return `${t.green('✓')} Nothing to do — every target is already up to date.\n`;
236
+ }
237
+ const lines = [];
238
+ // `plan` with nothing selected previews every supported agent. Say so, or
239
+ // the list silently names agents this project never installed.
240
+ if (/previews all four/i.test(body.next_step ?? '')) {
241
+ lines.push(t.dim('No agent selected and no recorded install — previewing every supported agent.'));
242
+ }
243
+ lines.push(`${plural(changes, 'change')} would be made. Nothing was written.`);
244
+ const groups = groupChanges(body.actions, ['create', 'update']);
245
+ if (groups.length) {
246
+ lines.push('');
247
+ lines.push(...groupLines(groups, opts, t));
248
+ }
249
+ if (problems.length) {
250
+ lines.push('');
251
+ lines.push(...issueLines(problems, t));
252
+ }
253
+ if (changes) {
254
+ lines.push('');
255
+ lines.push(` ${t.dim('→')} Run \`sw-ecosystem-agentic-harness apply --yes\` to perform them.`);
256
+ }
257
+ return `${lines.join('\n')}\n`;
258
+ }
259
+
260
+ // ------------------------------------------------------------------ status
261
+
262
+ /** An agent is only ours to talk about once it has actually been installed. */
263
+ function installedAgents(body) {
264
+ return (body.agents ?? []).filter((a) => ['ok', 'drift', 'conflict'].includes(a.state));
265
+ }
266
+
267
+ function renderStatus(body, opts, t) {
268
+ const mine = installedAgents(body);
269
+ const problems = issues(body);
270
+
271
+ if (!mine.length) {
272
+ return `${t.yellow('!')} No coding agent is set up in this project yet.\n`
273
+ + ` ${t.dim('→')} ${body.help?.[0] ?? 'sw-ecosystem-agentic-harness install'}\n`;
274
+ }
275
+
276
+ const worst = ['conflict', 'drift'].find((state) => mine.some((a) => a.state === state));
277
+ const lines = [];
278
+ lines.push(worst
279
+ ? `${t.yellow('!')} Shopware agentic harness ${body.installed_version ?? body.version} — ${plural(problems.length, 'item')} ${problems.length === 1 ? 'needs' : 'need'} attention.`
280
+ : `${t.green('✓')} Shopware agentic harness ${body.installed_version ?? body.version} — installed and up to date.`);
281
+
282
+ for (const a of mine) {
283
+ const counts = [];
284
+ if (a.installed?.skills) counts.push(plural(a.installed.skills, 'skill'));
285
+ if (a.installed?.agents) counts.push(plural(a.installed.agents, 'sub-agent'));
286
+ lines.push(` ${t.bold(label(a.id))}${counts.length ? `: ${counts.join(', ')}` : ''}`);
287
+ for (const note of a.notes ?? []) lines.push(` ${t.dim(note)}`);
288
+ }
289
+
290
+ if (problems.length) {
291
+ lines.push('');
292
+ lines.push(...issueLines(problems, t));
293
+ }
294
+
295
+ const missing = (body.extra_components ?? []).filter((c) => !c.available && c.install);
296
+ if (missing.length) {
297
+ lines.push('');
298
+ lines.push(` ${t.dim('Optional add-ons not installed:')} ${missing.map((c) => c.install).join(', ')}`);
299
+ }
300
+ return `${lines.join('\n')}\n`;
301
+ }
302
+
303
+ // --------------------------------------------------------------- uninstall
304
+
305
+ function renderUninstall(body, opts, t) {
306
+ const s = body.summary ?? {};
307
+ const gone = (s.removed ?? 0) + (s.reverted ?? 0);
308
+ if (gone === 0 && !(body.kept ?? []).length) {
309
+ return `${t.green('✓')} Nothing to remove — this project has no recorded install.\n`;
310
+ }
311
+ const lines = [`${t.green('✓')} Removed the Shopware agentic harness — `
312
+ + `${plural(s.removed ?? 0, 'file')} removed, ${plural(s.reverted ?? 0, 'config block')} reverted.`];
313
+ if ((body.kept ?? []).length) {
314
+ lines.push('');
315
+ lines.push(` ${t.bold('Kept, because you edited them')}`);
316
+ for (const k of body.kept) lines.push(` ${k.target}`);
317
+ }
318
+ lines.push('');
319
+ lines.push(` ${t.dim('→')} Restart your coding agent(s) so they drop the removed configuration.`);
320
+ return `${lines.join('\n')}\n`;
321
+ }
322
+
323
+ // ------------------------------------------------------------------ errors
324
+
325
+ function renderProblem(body, opts, t) {
326
+ const lines = [`${t.red('✗')} ${body.error}`];
327
+ for (const e of body.errors ?? []) {
328
+ lines.push(` ${e.target ?? label(e.host)} — ${e.error}`);
329
+ }
330
+ const problems = issues({ ...body, errors: [] });
331
+ if (problems.length) {
332
+ lines.push('');
333
+ lines.push(...issueLines(problems, t));
334
+ }
335
+ if (body.help?.length) {
336
+ lines.push('');
337
+ lines.push(` ${t.bold('Try')}`);
338
+ for (const h of body.help) lines.push(` ${h}`);
339
+ }
340
+ return `${lines.join('\n')}\n`;
341
+ }
342
+
343
+ // ---------------------------------------------------------------- dispatch
344
+
345
+ const RENDERERS = {
346
+ install: renderApply,
347
+ apply: renderApply,
348
+ plan: renderPlan,
349
+ status: renderStatus,
350
+ uninstall: renderUninstall,
351
+ };
352
+
353
+ /**
354
+ * `body` is the exact object `--json` prints. `opts.verbose` prints the
355
+ * per-file detail the JSON carries; `opts.color` enables ANSI.
356
+ */
357
+ export function render(body, opts = {}) {
358
+ if (!body || typeof body !== 'object') return '';
359
+ const t = theme(opts.color === true);
360
+ if (body.ok === false) return renderProblem(body, opts, t);
361
+ const renderer = RENDERERS[body.command];
362
+ if (!renderer) return `${body.next_step ?? ''}\n`;
363
+ return renderer(body, opts, t);
364
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@execuro-sw-ecosystem/sw-ecosystem-agentic-harness",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
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.6",
4
+ "version": "0.1.7",
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, `.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.
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 coding agent's 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
  ---
@@ -40,11 +40,13 @@ table from this run's output — it does not re-run `sw-setup`'s checks.
40
40
  Run:
41
41
 
42
42
  ```
43
- npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest status
43
+ npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest status --json
44
44
  ```
45
45
 
46
- It prints one JSON object (`ok`, `next_step`, `help`, and its own
47
- `hosts[]`/`drift[]` configuration state). Alongside it, run every
46
+ `--json` is mandatory for every call this skill makes: without it the CLI
47
+ prints a human summary, not the object below. It prints one JSON object
48
+ (`ok`, `next_step`, `help`, and its own `agents[]`/`drift[]` configuration
49
+ state). Alongside it, run every
48
50
  environment row's check from `reference/rows.md` in this skill's directory —
49
51
  read-only, seconds each: file/directory existence, one version command, one
50
52
  `kb_status` call, three `npm view` registry lookups (Package updates row) —
@@ -52,7 +54,7 @@ the one part of step 1 that touches the network, so it is the slow part.
52
54
  Never write, never install, never ask a question in this step.
53
55
 
54
56
  Print one checkbox list, one line per row — the environment rows first, then
55
- one line per configuration item the CLI's `hosts[]`/`drift[]` reports:
57
+ one line per configuration item the CLI's `agents[]`/`drift[]` reports:
56
58
 
57
59
  ```
58
60
  - [x] vendor/ — shopware/core 6.7.13.0
@@ -88,7 +90,7 @@ For a missing/drifted configuration row, do not research or plan the fix
88
90
  yourself — the CLI's `plan` command already has it:
89
91
 
90
92
  ```
91
- npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest plan
93
+ npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest plan --json
92
94
  ```
93
95
 
94
96
  List the concrete steps in plain words: environment-row fixes in order
@@ -104,7 +106,7 @@ if you have one (Claude Code: `AskUserQuestion`; Codex: `request_user_input`,
104
106
  which is non-blocking outside Plan mode — so end the turn after asking).
105
107
  Options:
106
108
  - **Yes, run it** — run the environment-row fixes in order, then
107
- `npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest apply --yes`
109
+ `npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest apply --yes --json`
108
110
  for the configuration changes, then re-run step 1 and print the final table.
109
111
  - **No, stop** — stop; the table from step 1 is already on screen.
110
112
 
@@ -124,11 +126,11 @@ Options:
124
126
  the corpus ships already built inside the
125
127
  `@execuro-sw-ecosystem/sw-dev-knowledge-base-mcp` package. If `platform` is
126
128
  not `implemented`, the fix is to check the registration (re-run the
127
- installer CLI's `apply --yes`) or update that package — never build a
129
+ installer CLI's `apply --yes --json`) or update that package — never build a
128
130
  corpus here. Neither is replicated in this skill.
129
- - This skill never writes a host configuration file itself (`.mcp.json`,
130
- permission grants, `.gitignore` lines). Every such write goes through the
131
- installer CLI's `apply --yes`, run only after the user's yes.
131
+ - This skill never writes a coding agent's configuration file itself
132
+ (`.mcp.json`, permission grants, `.gitignore` lines). Every such write goes
133
+ through the installer CLI's `apply --yes --json`, after the user's yes.
132
134
 
133
135
  ### 5. Report
134
136
 
@@ -144,6 +146,6 @@ line:
144
146
 
145
147
  `reference/rows.md` in this skill's directory — the twelve environment rows:
146
148
  check command(s), what "ticked" means, the fix steps verbatim from the
147
- guideline, and which skills the row blocks. Host configuration state (what
149
+ guideline, and which skills the row blocks. Agent configuration state (what
148
150
  gets installed into `.mcp.json`, permissions, `.gitignore`) is not in this
149
151
  file — it comes from the installer CLI's `status`/`plan`/`apply` output.
@@ -2,19 +2,25 @@
2
2
 
3
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
- unticked. Host configuration files (`.mcp.json` entries, `.gitignore` lines,
6
- permission grants) are not rows here — the installer CLI owns them and
7
- reports their state in its own `status`/`plan` output; see `SKILL.md`.
5
+ unticked. Coding-agent configuration files (`.mcp.json` entries, `.gitignore`
6
+ lines, permission grants) are not rows here — the installer CLI owns them and
7
+ reports their state in its own `status --json`/`plan --json` output; see
8
+ `SKILL.md`.
8
9
 
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`
10
+ Every installer-CLI call this skill makes passes `--json`. Without it the CLI
11
+ prints a short human summary instead of the object these rows read — that
12
+ summary is for the person typing the command, never for a skill parsing it.
13
+
14
+ When that `status --json` call reports no install at all (no lock file, so no
15
+ recorded agents), the installer CLI's `apply --yes`/`install` has no `--agent`
11
16
  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
17
+ Do not pass a guessed `--agent`. Report this to the user instead: the first
13
18
  install must be run by them directly in a terminal, e.g.
14
19
  `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.
20
+ which then either takes their `--agent` or prompts them interactively. Re-run
21
+ this skill's `status` check afterwards — every later `apply --yes --json`
22
+ reuses the agents that first run recorded, and this skill can call it
23
+ directly again. Expect `agents[]` in every output this skill reads.
18
24
 
19
25
  ## 1. vendor/
20
26
 
@@ -60,11 +66,11 @@ row never runs an extra component's own install command.
60
66
  So this row never blocks readiness. It asks.
61
67
 
62
68
  - **Check:** read `extra_components[]` from the `status` output the skill already
63
- ran in step 1 — each entry's `available`, `installed_for` (the hosts it is
64
- placed in), and `install` (the npm command, when missing). Never probe a
69
+ ran in step 1 — each entry's `available`, `installed_for` (the coding agents
70
+ it is placed in), and `install` (the npm command, when missing). Never probe a
65
71
  path and never run an extra component by hand; the CLI owns both.
66
72
  - **Ticked:** `[x]` when an extra component's `available` is true and
67
- `installed_for` includes this host; `[-]` when the user has declined it.
73
+ `installed_for` includes this agent; `[-]` when the user has declined it.
68
74
  Only an unanswered offer shows `[ ]`, and even then it does not hold the
69
75
  table open — report it as optional and move on.
70
76
  - **Fix:** offer each missing extra component inside step 3's single question,
@@ -76,10 +82,10 @@ So this row never blocks readiness. It asks.
76
82
  `package` field from `extra_components[]`), then re-run
77
83
 
78
84
  ```
79
- npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest apply --yes
85
+ npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest apply --yes --json
80
86
  ```
81
87
 
82
- which places one `SKILL.md` per extra component into each selected host's own
88
+ which places one `SKILL.md` per extra component into each selected agent's own
83
89
  skills directory, records it, and removes it again on uninstall. **Never
84
90
  run the extra component's own `install-skill`** — with no `--target` it
85
91
  defaults to Claude Code's own skills directory, wrong for a Codex, Copilot