@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/CHANGELOG.md +35 -0
- package/CONTRIBUTING.md +13 -4
- package/README.md +38 -23
- package/bin/cli.mjs +17 -13
- package/lib/commands.mjs +71 -67
- package/lib/guide.mjs +28 -20
- package/lib/hosts.mjs +8 -8
- package/lib/lock.mjs +10 -5
- package/lib/out.mjs +27 -6
- package/lib/prompt.mjs +18 -23
- package/lib/render.mjs +364 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/sw-setup/SKILL.md +14 -12
- package/skills/sw-setup/reference/rows.md +20 -14
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.
|
|
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.
|
|
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)",
|
package/skills/sw-setup/SKILL.md
CHANGED
|
@@ -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
|
|
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
|
-
|
|
47
|
-
|
|
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 `
|
|
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
|
|
130
|
-
permission grants, `.gitignore` lines). Every such write goes
|
|
131
|
-
installer CLI's `apply --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.
|
|
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.
|
|
6
|
-
permission grants) are not rows here — the installer CLI owns them and
|
|
7
|
-
reports their state in its own `status`/`plan` output; see
|
|
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
|
-
|
|
10
|
-
|
|
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 `--
|
|
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 `--
|
|
16
|
-
this skill's `status` check afterwards — every later `apply --yes`
|
|
17
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|