@execuro-sw-ecosystem/sw-ecosystem-agentic-harness 0.1.7 → 0.1.9
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 +58 -0
- package/README.md +40 -17
- package/SECURITY.md +1 -1
- package/bin/cli.mjs +7 -5
- package/lib/actions.mjs +1 -45
- package/lib/commands.mjs +43 -45
- package/lib/content.mjs +31 -1
- package/lib/guide.mjs +9 -16
- package/lib/hosts.mjs +36 -34
- package/lib/lock.mjs +41 -9
- package/lib/render.mjs +10 -9
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/sw-design-requirements/SKILL.md +1 -1
- package/skills/sw-design-requirements/reference/editor-gate.md +1 -1
- package/skills/sw-design-requirements/reference/editor-mode.md +2 -2
- package/skills/sw-design-solution/SKILL.md +1 -1
- package/skills/sw-design-solution/reference/editor-mode.md +2 -2
- package/skills/sw-discover-tender/SKILL.md +2 -2
- package/skills/sw-discover-tender/reference/editor-session.md +3 -3
- package/skills/sw-discover-tender/reference/procedure.md +2 -2
- package/skills/sw-setup/SKILL.md +5 -5
- package/skills/sw-setup/reference/rows.md +105 -68
- package/lib/extra-components.mjs +0 -117
package/lib/hosts.mjs
CHANGED
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
import { homedir } from 'node:os';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
import {
|
|
13
|
-
agents, agentsMd, claudeSettings,
|
|
13
|
+
agents, agentsMd, claudeSettings, lockDir, lockNeedsGitignore, mcpServers, PKG_ROOT, skills,
|
|
14
14
|
} from './content.mjs';
|
|
15
15
|
import { abs, isDir, isWritable, walk } from './fsx.mjs';
|
|
16
16
|
import { renderTomlTable, tomlTableExists, findFence } from './merge.mjs';
|
|
17
17
|
import { readTextMaybe } from './fsx.mjs';
|
|
18
|
-
import { EXTRA_COMPONENTS } from './extra-components.mjs';
|
|
19
18
|
|
|
20
19
|
export const AGENTS = ['claude-code', 'codex', 'copilot', 'cursor'];
|
|
21
20
|
|
|
22
|
-
/** Where each host reads its skills from — the base every skill
|
|
23
|
-
*
|
|
21
|
+
/** Where each host reads its skills from — the base every skill copy is
|
|
22
|
+
* rooted at, and the directory `sw-setup` hands to an editor package's own
|
|
23
|
+
* `install-skill --target`. */
|
|
24
24
|
export const SKILL_DIR = {
|
|
25
25
|
'claude-code': '.claude/skills',
|
|
26
26
|
codex: '.agents/skills',
|
|
@@ -103,21 +103,6 @@ function skillActions(host, targetBase) {
|
|
|
103
103
|
return out;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
/**
|
|
107
|
-
* One `skill-copy` action per optional extra component. The `skill:`
|
|
108
|
-
* segment (not `file:`) keeps these ids from colliding, or deduping,
|
|
109
|
-
* against a shipped `file-copy` action at the same path.
|
|
110
|
-
*/
|
|
111
|
-
function extraComponentActions(host, targetBase) {
|
|
112
|
-
return EXTRA_COMPONENTS.map((c) => {
|
|
113
|
-
const target = `${targetBase}/${c.id}/SKILL.md`;
|
|
114
|
-
return {
|
|
115
|
-
id: `${host}:skill:${target}`, host, kind: 'skill-copy',
|
|
116
|
-
component: c.id, package: `${c.pkg}@${c.version}`, target,
|
|
117
|
-
};
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
106
|
function agentActions(host, targetBase, pick, ext) {
|
|
122
107
|
return agents().map((agent) => {
|
|
123
108
|
const target = `${targetBase}/${agent.name}${ext}`;
|
|
@@ -147,11 +132,17 @@ function agentsMdAction(host) {
|
|
|
147
132
|
};
|
|
148
133
|
}
|
|
149
134
|
|
|
150
|
-
/**
|
|
151
|
-
|
|
135
|
+
/**
|
|
136
|
+
* The lock describes one machine's install, so it must not be tracked. Under
|
|
137
|
+
* `var/` the `shopware/core` Flex recipe already ignores it (`/var/*`), so
|
|
138
|
+
* there is nothing to write and we do not touch the user's `.gitignore` at
|
|
139
|
+
* all. Only the fallback location needs the managed line.
|
|
140
|
+
*/
|
|
141
|
+
function gitignoreAction(host, ctx) {
|
|
142
|
+
if (!lockNeedsGitignore(ctx.root)) return null;
|
|
152
143
|
return {
|
|
153
144
|
id: 'shared:text:.gitignore#lock', host, kind: 'text-fence',
|
|
154
|
-
target: '.gitignore', block: 'lock', syntax: 'gitignore', body: `${
|
|
145
|
+
target: '.gitignore', block: 'lock', syntax: 'gitignore', body: `${lockDir(ctx.root)}/`,
|
|
155
146
|
};
|
|
156
147
|
}
|
|
157
148
|
|
|
@@ -159,7 +150,6 @@ function planClaude(ctx) {
|
|
|
159
150
|
const host = 'claude-code';
|
|
160
151
|
const out = [
|
|
161
152
|
...skillActions(host, SKILL_DIR[host]),
|
|
162
|
-
...(ctx.extraComponents === false ? [] : extraComponentActions(host, SKILL_DIR[host])),
|
|
163
153
|
...agentActions(host, '.claude/agents', 'claude', '.md'),
|
|
164
154
|
...mcpJsonActions(host, '.mcp.json', 'mcpServers', ctx),
|
|
165
155
|
];
|
|
@@ -176,8 +166,8 @@ function planClaude(ctx) {
|
|
|
176
166
|
target: '.claude/settings.json', pointer: setting.pointer, op: 'set', value: setting.value,
|
|
177
167
|
});
|
|
178
168
|
}
|
|
179
|
-
out.push(gitignoreAction(host));
|
|
180
|
-
return out;
|
|
169
|
+
out.push(gitignoreAction(host, ctx));
|
|
170
|
+
return out.filter(Boolean);
|
|
181
171
|
}
|
|
182
172
|
|
|
183
173
|
function planCodex(ctx) {
|
|
@@ -185,7 +175,6 @@ function planCodex(ctx) {
|
|
|
185
175
|
const out = [
|
|
186
176
|
// Codex discovers skills from `.agents/skills`, never `.claude/skills`.
|
|
187
177
|
...skillActions(host, SKILL_DIR[host]),
|
|
188
|
-
...(ctx.extraComponents === false ? [] : extraComponentActions(host, SKILL_DIR[host])),
|
|
189
178
|
...agentActions(host, '.codex/agents', 'codex', '.toml'),
|
|
190
179
|
];
|
|
191
180
|
|
|
@@ -222,15 +211,14 @@ function planCodex(ctx) {
|
|
|
222
211
|
},
|
|
223
212
|
});
|
|
224
213
|
|
|
225
|
-
out.push(agentsMdAction(host), gitignoreAction(host));
|
|
226
|
-
return out;
|
|
214
|
+
out.push(agentsMdAction(host), gitignoreAction(host, ctx));
|
|
215
|
+
return out.filter(Boolean);
|
|
227
216
|
}
|
|
228
217
|
|
|
229
218
|
function planCopilot(ctx) {
|
|
230
219
|
const host = 'copilot';
|
|
231
220
|
const out = [
|
|
232
221
|
...skillActions(host, SKILL_DIR[host]),
|
|
233
|
-
...(ctx.extraComponents === false ? [] : extraComponentActions(host, SKILL_DIR[host])),
|
|
234
222
|
...agentActions(host, '.github/agents', 'copilot', '.agent.md'),
|
|
235
223
|
// Copilot CLI reads `.mcp.json`; VS Code reads `.vscode/mcp.json` under the
|
|
236
224
|
// incompatible `servers` key. Both, or the two surfaces disagree.
|
|
@@ -239,9 +227,9 @@ function planCopilot(ctx) {
|
|
|
239
227
|
...a, id: a.id.replace('shared:', 'copilot:'),
|
|
240
228
|
})),
|
|
241
229
|
agentsMdAction(host),
|
|
242
|
-
gitignoreAction(host),
|
|
230
|
+
gitignoreAction(host, ctx),
|
|
243
231
|
];
|
|
244
|
-
return out;
|
|
232
|
+
return out.filter(Boolean);
|
|
245
233
|
}
|
|
246
234
|
|
|
247
235
|
function planCursor(ctx) {
|
|
@@ -257,12 +245,26 @@ function planCursor(ctx) {
|
|
|
257
245
|
|| (isDir(abs(ctx.root, '.claude/skills')) && isDir(abs(ctx.root, '.claude/agents')));
|
|
258
246
|
if (!claudeTrees) {
|
|
259
247
|
out.push(...skillActions(host, SKILL_DIR[host]));
|
|
260
|
-
if (ctx.extraComponents !== false) out.push(...extraComponentActions(host, SKILL_DIR[host]));
|
|
261
248
|
out.push(...agentActions(host, '.cursor/agents', 'claude', '.md'));
|
|
262
249
|
}
|
|
263
250
|
out.push(...mcpJsonActions(host, '.cursor/mcp.json', 'mcpServers', ctx));
|
|
264
|
-
out.push(agentsMdAction(host), gitignoreAction(host));
|
|
265
|
-
return out;
|
|
251
|
+
out.push(agentsMdAction(host), gitignoreAction(host, ctx));
|
|
252
|
+
return out.filter(Boolean);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The skills directory a host actually reads, for `status` to report — the
|
|
257
|
+
* one `sw-setup` passes to an editor package's `install-skill --target`.
|
|
258
|
+
* Cursor is the exception: when the Claude trees are present it reads those,
|
|
259
|
+
* so naming `.cursor/skills` would put an editor skill where Cursor never
|
|
260
|
+
* looks.
|
|
261
|
+
*/
|
|
262
|
+
export function skillsDir(id, ctx) {
|
|
263
|
+
if (id === 'cursor'
|
|
264
|
+
&& isDir(abs(ctx.root, '.claude/skills')) && isDir(abs(ctx.root, '.claude/agents'))) {
|
|
265
|
+
return SKILL_DIR['claude-code'];
|
|
266
|
+
}
|
|
267
|
+
return SKILL_DIR[id];
|
|
266
268
|
}
|
|
267
269
|
|
|
268
270
|
/** What is already installed for a host, for `status`. */
|
package/lib/lock.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
// The lock file:
|
|
1
|
+
// The lock file: `var/sw-ai-sdk/harness.lock.json` in the host repository, or
|
|
2
|
+
// `.sw-ai-sdk/harness.lock.json` in a root that has no `var/` (see
|
|
3
|
+
// `lockDir()` in content.mjs).
|
|
2
4
|
//
|
|
3
5
|
// This is the ownership record. JSON config files have no comments, so nothing
|
|
4
6
|
// in `.mcp.json` can tell us who wrote an entry — the lock does, by content
|
|
@@ -6,15 +8,29 @@
|
|
|
6
8
|
// removal is always narrower than addition, and which rules the user declined
|
|
7
9
|
// so we never ask twice.
|
|
8
10
|
//
|
|
9
|
-
// It is
|
|
10
|
-
//
|
|
11
|
+
// It is never tracked: it describes one machine's install, not the project.
|
|
12
|
+
// Under `var/` the Shopware recipe already ignores it; in the fallback
|
|
13
|
+
// location the installer writes the managed `.gitignore` line itself.
|
|
14
|
+
//
|
|
15
|
+
// A lock left at the old `.sw-ai-sdk/` path is read and rewritten to the new
|
|
16
|
+
// one on the next write, so an existing install never looks fresh.
|
|
11
17
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
18
|
+
import {
|
|
19
|
+
LOCK_FILE, SUPPORTED_CONTENT, CONTENT_VERSION, PKG_NAME, PKG_VERSION, lockFile,
|
|
20
|
+
} from './content.mjs';
|
|
21
|
+
import {
|
|
22
|
+
abs, ensureDir, exists, readTextMaybe, removeDirIfEmpty, removeFile, writeAtomic,
|
|
23
|
+
} from './fsx.mjs';
|
|
24
|
+
import { dirname } from 'node:path';
|
|
14
25
|
|
|
15
26
|
export class LockVersionError extends Error {}
|
|
16
27
|
|
|
17
28
|
export function lockPath(root) {
|
|
29
|
+
return abs(root, lockFile(root));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The pre-`var/` location, kept only so an existing install can be migrated. */
|
|
33
|
+
export function legacyLockPath(root) {
|
|
18
34
|
return abs(root, LOCK_FILE);
|
|
19
35
|
}
|
|
20
36
|
|
|
@@ -41,19 +57,26 @@ export function empty(scope) {
|
|
|
41
57
|
|
|
42
58
|
/** Read the lock, or null when absent. Throws on a layout we cannot undo. */
|
|
43
59
|
export function read(root, scope) {
|
|
44
|
-
|
|
60
|
+
// The new location first; a lock still at the old one is read here and
|
|
61
|
+
// rewritten by `write()`.
|
|
62
|
+
let where = lockFile(root);
|
|
63
|
+
let found = readTextMaybe(lockPath(root));
|
|
64
|
+
if (found === null) {
|
|
65
|
+
found = readTextMaybe(legacyLockPath(root));
|
|
66
|
+
where = LOCK_FILE;
|
|
67
|
+
}
|
|
45
68
|
if (found === null) return null;
|
|
46
69
|
let lock;
|
|
47
70
|
try {
|
|
48
71
|
lock = JSON.parse(found.text);
|
|
49
72
|
} catch {
|
|
50
73
|
throw new LockVersionError(
|
|
51
|
-
`${
|
|
74
|
+
`${where} is not valid JSON. Move it aside and re-run apply to rebuild it.`,
|
|
52
75
|
);
|
|
53
76
|
}
|
|
54
77
|
if (!SUPPORTED_CONTENT.includes(lock.content_version)) {
|
|
55
78
|
throw new LockVersionError(
|
|
56
|
-
`${
|
|
79
|
+
`${where} records content_version ${lock.content_version}, which this ` +
|
|
57
80
|
`version of ${PKG_NAME} (${PKG_VERSION}) does not understand.`,
|
|
58
81
|
);
|
|
59
82
|
}
|
|
@@ -68,7 +91,16 @@ export function read(root, scope) {
|
|
|
68
91
|
export function write(root, lock) {
|
|
69
92
|
const next = { ...lock, updated_at: new Date().toISOString() };
|
|
70
93
|
if (!next.installed_at) next.installed_at = next.updated_at;
|
|
71
|
-
|
|
94
|
+
const target = lockPath(root);
|
|
95
|
+
ensureDir(dirname(target));
|
|
96
|
+
writeAtomic(target, `${JSON.stringify(next, null, 2)}\n`);
|
|
97
|
+
// Migration: one lock, at one path. Writing the new one and leaving the old
|
|
98
|
+
// would make an older CLI, or our own `read()`, resurrect a stale record.
|
|
99
|
+
const legacy = legacyLockPath(root);
|
|
100
|
+
if (legacy !== target && exists(legacy)) {
|
|
101
|
+
removeFile(legacy);
|
|
102
|
+
removeDirIfEmpty(dirname(legacy));
|
|
103
|
+
}
|
|
72
104
|
return next;
|
|
73
105
|
}
|
|
74
106
|
|
package/lib/render.mjs
CHANGED
|
@@ -197,8 +197,9 @@ function renderApply(body, opts, t) {
|
|
|
197
197
|
const s = body.summary ?? {};
|
|
198
198
|
const changed = (s.created ?? 0) + (s.updated ?? 0);
|
|
199
199
|
const problems = issues(body);
|
|
200
|
+
const migrated = body.migrated ?? [];
|
|
200
201
|
|
|
201
|
-
if (changed === 0 && problems.length === 0) {
|
|
202
|
+
if (changed === 0 && problems.length === 0 && migrated.length === 0) {
|
|
202
203
|
// ONE line. Nothing changed means nothing to restart and nothing to read.
|
|
203
204
|
return `${t.green('✓')} Shopware agentic harness: already up to date — nothing to change.\n`;
|
|
204
205
|
}
|
|
@@ -207,13 +208,18 @@ function renderApply(body, opts, t) {
|
|
|
207
208
|
const changedIds = groups.map((g) => g.id).filter((id) => id !== SHARED);
|
|
208
209
|
const verb = (s.updated ?? 0) === 0 ? 'Installed' : 'Updated';
|
|
209
210
|
const lines = [];
|
|
210
|
-
lines.push(
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
if (changed) lines.push(`${t.green('✓')} ${verb} the Shopware agentic harness.`);
|
|
212
|
+
else if (problems.length) lines.push(`${t.yellow('!')} Nothing was written — see below.`);
|
|
213
|
+
// Only tidy-up ran: the install itself was already current.
|
|
214
|
+
else lines.push(`${t.green('✓')} Shopware agentic harness: already up to date.`);
|
|
213
215
|
if (groups.length) {
|
|
214
216
|
lines.push('');
|
|
215
217
|
lines.push(...groupLines(groups, opts, t));
|
|
216
218
|
}
|
|
219
|
+
if (migrated.length) {
|
|
220
|
+
lines.push('');
|
|
221
|
+
for (const m of migrated) lines.push(` ${t.dim('·')} ${m.target}: ${m.detail}`);
|
|
222
|
+
}
|
|
217
223
|
if (problems.length) {
|
|
218
224
|
lines.push('');
|
|
219
225
|
lines.push(...issueLines(problems, t));
|
|
@@ -292,11 +298,6 @@ function renderStatus(body, opts, t) {
|
|
|
292
298
|
lines.push(...issueLines(problems, t));
|
|
293
299
|
}
|
|
294
300
|
|
|
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
301
|
return `${lines.join('\n')}\n`;
|
|
301
302
|
}
|
|
302
303
|
|
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.9",
|
|
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.9",
|
|
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)",
|
|
@@ -3,7 +3,7 @@ name: sw-design-requirements
|
|
|
3
3
|
description: Turn a briefing — chat text or a file, covering one or more features — into a lean, business-only PRD stored as specs/NNNN-slug.md. Runs an interactive clarification loop for the requirement gaps and inconsistencies that actually change the requirements, then scores a weighted confidence %. Use when asked to write, draft, refine, continue, or score a PRD / requirements doc / feature brief for this Shopware project. Never writes a tech spec or answers architecture questions — that's sw-design-solution.
|
|
4
4
|
when_to_use: Trigger phrases — "write a PRD", "draft requirements", "turn this brief into a PRD", "continue PRD 0003", "is this PRD ready for specification", "raise the confidence on this PRD", "requirements for <feature>".
|
|
5
5
|
argument-hint: '[file-path | briefing text | specs/NNNN-slug.md to continue] [--editor]'
|
|
6
|
-
allowed-tools: Read Write Edit Glob Grep AskUserQuestion Agent Skill Bash(ls *) Bash(mkdir -p specs) Bash(npx -y @execuro-sw-ecosystem/sw-specs-editor@
|
|
6
|
+
allowed-tools: Read Write Edit Glob Grep AskUserQuestion Agent Skill Bash(ls *) Bash(mkdir -p specs) Bash(npx -y @execuro-sw-ecosystem/sw-specs-editor@latest *) mcp__ShopwareDevKnowledgeBase__list_docs mcp__ShopwareDevKnowledgeBase__grep_docs mcp__ShopwareDevKnowledgeBase__read_doc mcp__ShopwareDevKnowledgeBase__kb_status
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Runs before the `--editor` hand-off (step 0). The Specs Editor is an **optional** add-on: it is not part of this plugin, and a host that never installed it is a supported, normal setup. `sw-setup` offers to install it.
|
|
4
4
|
|
|
5
|
-
What is checked is the **skill**, `sw-specs-editor`, because that is what `sw-setup` installs and what the hand-off calls. The skill brings the CLI with it — it invokes `npx -y @execuro-sw-ecosystem/sw-specs-editor@
|
|
5
|
+
What is checked is the **skill**, `sw-specs-editor`, because that is what `sw-setup` installs and what the hand-off calls. The skill brings the CLI with it — it invokes `npx -y @execuro-sw-ecosystem/sw-specs-editor@latest`, which fetches the package on first use.
|
|
6
6
|
|
|
7
7
|
Check: is the `sw-specs-editor` skill available in this session?
|
|
8
8
|
|
|
@@ -8,8 +8,8 @@ Invoked as `<path> --editor-session <url> --batch <batch.json>` — one note bat
|
|
|
8
8
|
- **Full pass on request only.** A review request ("check the whole PRD", "reconcile") runs the full flow: step 2, §11 gap review, missing options/marks/notes filled. Otherwise touched questions get options.
|
|
9
9
|
- **Scoped agent runs.** Spawn `sw-product-manager`/`sw-shopware-architect` only when a note touches feasibility, §10, or a §11 block changes (one brief per run); else edit, anchor, log alone.
|
|
10
10
|
- **Preserve on every write:** status tags on item ids (`- **FR-3** [done] …`, also `AC-n`/`BR-n`), the `Diagram:` line, the §11 question blocks and ticks. Rewrite in place; never regenerate.
|
|
11
|
-
- **Diagrams.** Domain diagram: `specs/NNNN-slug.domain.graph.json` (business concepts only). Write/update, run `npx -y @execuro-sw-ecosystem/sw-specs-editor@
|
|
12
|
-
- **Progress.** Before each step, before/after each spawn: `npx -y @execuro-sw-ecosystem/sw-specs-editor@
|
|
11
|
+
- **Diagrams.** Domain diagram: `specs/NNNN-slug.domain.graph.json` (business concepts only). Write/update, run `npx -y @execuro-sw-ecosystem/sw-specs-editor@latest diagram specs/NNNN-slug.domain.graph.json specs/NNNN-slug.domain.excalidraw`, ensure §4 carries `Diagram: specs/NNNN-slug.domain.excalidraw`; never write `.excalidraw` directly, first request only.
|
|
12
|
+
- **Progress.** Before each step, before/after each spawn: `npx -y @execuro-sw-ecosystem/sw-specs-editor@latest emit progress "<step>" --batch <id> --doc prd`.
|
|
13
13
|
- **Report:** changed blocks, answers (Q ids → anchor), top remaining gap or `none`, confidence %, status, `Agents: <list or none>`.
|
|
14
14
|
|
|
15
15
|
Graph shape (`bin/cli.mjs diagram` input): `{ "title", "direction": "LR", "groups": [{id,label}], "nodes": [{id,label,type: entity|actor|service|external|event|store, group?, note?}], "edges": [{from,to,label?}] }`.
|
|
@@ -3,7 +3,7 @@ name: sw-design-solution
|
|
|
3
3
|
description: Turn a PRD (specs/NNNN-slug.md) into a technical spec at specs/NNNN-slug-spec.md. Applies TDD — every acceptance criterion gets an implementation plan and e2e coverage, with Shopware toolchain (shopware-cli/PHPStan/ESLint/Stylelint) conventions baked into decisions up front. Runs a clarification loop for the gaps that change the implementation plan, and on the user's say-so extracts project-wide architecture/stack decisions into ADR files (specs/NNNN-slug-adr-<topic>.md) that gate readiness while proposed. Pre-checks vendor/ and the KB before it starts. Use when asked to write, draft, refine, or continue a tech spec / implementation plan. Never edits the source PRD or answers business/requirements questions — that's sw-design-requirements.
|
|
4
4
|
when_to_use: Trigger phrases — "write the tech spec for PRD 0007", "create an implementation plan for this PRD", "draft the spec", "continue spec 0007", "is this spec ready for implementation".
|
|
5
5
|
argument-hint: '[specs/NNNN-slug.md | specs/NNNN-slug-spec.md] [--editor]'
|
|
6
|
-
allowed-tools: Read Write Edit Glob Grep AskUserQuestion Agent Skill Bash(ls *) Bash(npx -y @execuro-sw-ecosystem/sw-specs-editor@
|
|
6
|
+
allowed-tools: Read Write Edit Glob Grep AskUserQuestion Agent Skill Bash(ls *) Bash(npx -y @execuro-sw-ecosystem/sw-specs-editor@latest *) WebSearch WebFetch mcp__ShopwareDevKnowledgeBase__list_docs mcp__ShopwareDevKnowledgeBase__grep_docs mcp__ShopwareDevKnowledgeBase__read_doc mcp__ShopwareDevKnowledgeBase__kb_status
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# sw-design-solution
|
|
@@ -9,8 +9,8 @@ Same procedure/boundary/verification/ADR rules, plus:
|
|
|
9
9
|
- **Batch input.** Ticks first — a ticked question with no note is still an answer. `answer`/`[x]` tick → anchor, append Decision Log, delete block (`[adr]` sub-step 3, `[gate]` sub-step 5). `comment`/`free` → this run's briefing (block-bound `path`/`line`–`endLine`/`md`/`quote`/`hash`; verify `md` matches, fall back to `quote`; `selection` → rework only that text). `diagram` → update the graph file. *What*/*why* notes belong to the PRD. First write migrates a pre-template spec; blocks get missing options only when touched.
|
|
10
10
|
- **Scoped runs.** Re-spawn architect only for an overturned/new decision, QA only for a changed test plan, `sw-product-manager` only for feasibility — together still one message; else edit/verify/anchor/log alone, name agents.
|
|
11
11
|
- **Preserve:** status tags (`- **AC-2** [partly] (FR-3)`, `- **Decision:** [done] …`), `Diagram:` line, §6 blocks/ticks — rewrite in place, never regenerate.
|
|
12
|
-
- **Diagrams.** `specs/NNNN-slug-spec.architecture.graph.json`; run `npx -y @execuro-sw-ecosystem/sw-specs-editor@
|
|
13
|
-
- **Progress.** `npx -y @execuro-sw-ecosystem/sw-specs-editor@
|
|
12
|
+
- **Diagrams.** `specs/NNNN-slug-spec.architecture.graph.json`; run `npx -y @execuro-sw-ecosystem/sw-specs-editor@latest diagram specs/NNNN-slug-spec.architecture.graph.json specs/NNNN-slug-spec.architecture.excalidraw`, keep §2's `Diagram:` line — never write it directly.
|
|
13
|
+
- **Progress.** `npx -y @execuro-sw-ecosystem/sw-specs-editor@latest emit --since <step-0.5 start> progress "<step>" --batch <id> --doc spec` before/after each step/spawn; `--since` prefixes `+m:ss`.
|
|
14
14
|
- **Report:** changed blocks; answers (Q ids → anchor); first unmet §7 criterion or `none`; status/confidence/`Open ADRs`; `Agents: <list or none>`; `Timing:`.
|
|
15
15
|
|
|
16
16
|
Graph shape: `{ title, direction: "LR", groups: [{id,label}], nodes: [{id,label,type: entity|actor|service|external|event|store,group?,note?}], edges: [{from,to,label?}] }`.
|
|
@@ -3,7 +3,7 @@ name: sw-discover-tender
|
|
|
3
3
|
description: Analyse a client tender (RFP, RFI or RFQ) for a Shopware 6 project into one markdown sidecar, <source basename>-analysis.md. Every requirement row gets a class against the installed Shopware version (stock / config / plugin / custom), a three-point person-day estimate, one line of mechanism and proposed scope-lock assumptions the partner accepts by ticking checkboxes — in the file or on the Tender Discovery Tool page (`--editor`); the next run reconciles the ticks, dissolves answered questions and re-estimates. At Ready to submit it exports one CSV per client table with compliance, comment and effort filled in the client's columns. Scope and PD only — never costs, prices, licence fees or budgets. Never designs the solution — that's sw-design-solution.
|
|
4
4
|
when_to_use: Trigger phrases — "scope this RFP", "analyse the tender", "answer the RFI", "discover RFP 0001", "continue RFP 0001", "reconcile RFP 0001", "export the response for RFP 0001", "is RFP 0001 ready to submit", "open RFP 0001 in the browser", "open the tender tool", "sw-discover-tender specs/rfp-0001-x-analysis.md --editor".
|
|
5
5
|
argument-hint: '[tender file or folder of exports (.xlsx|.md|.txt|.csv|.pdf) | specs/rfp-NNNN-slug-analysis.md to continue] [--editor] [--batch <n|all>] [--export]'
|
|
6
|
-
allowed-tools: Read Write Edit Glob Grep Agent AskUserQuestion Bash(ls *) Bash(mkdir -p specs) Bash(mkdir -p specs/.rfp *) Bash(shasum -a 256 *) Bash(npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@
|
|
6
|
+
allowed-tools: Read Write Edit Glob Grep Agent AskUserQuestion Bash(ls *) Bash(mkdir -p specs) Bash(mkdir -p specs/.rfp *) Bash(shasum -a 256 *) Bash(npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest *) Bash(open *) Bash(xdg-open *) Bash(tail *) Bash(grep *) Bash(printf *) mcp__ShopwareDevKnowledgeBase__list_docs mcp__ShopwareDevKnowledgeBase__grep_docs mcp__ShopwareDevKnowledgeBase__read_doc mcp__ShopwareDevKnowledgeBase__kb_status
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# sw-discover-tender
|
|
@@ -24,7 +24,7 @@ One markdown sidecar per tender, `<source basename>-analysis.md`: class, three-p
|
|
|
24
24
|
| Argument | Behaviour |
|
|
25
25
|
| --- | --- |
|
|
26
26
|
| `.md` / `.txt` / `.csv` / `.pdf`, or a folder of such exports | New tender: extract, analyse, write `<basename>-analysis.md` next to the source |
|
|
27
|
-
| `.xlsx` | Import first, per `reference/procedure.md` step 1 ".xlsx import": `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@
|
|
27
|
+
| `.xlsx` | Import first, per `reference/procedure.md` step 1 ".xlsx import": `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest import` proposes a mapping (install gate: `reference/editor-session.md` §0), confirmed via `--editor` or a one-line yes/no, before the normalised source and the analysis are set. A refusal (encrypted, `.xls`, `.xlsb`) is relayed verbatim and the run stops |
|
|
28
28
|
| `.docx` | Stop. One line: export every requirement table as CSV UTF-8 (or paste it as markdown tables, header row kept) and rerun with that file or folder |
|
|
29
29
|
| `*-analysis.md` | Continue: reconcile, advance, rewrite in place |
|
|
30
30
|
| `--export`, or "export the response for RFP NNNN" | Continue, then run step 9 if the gate holds |
|
|
@@ -5,7 +5,7 @@ The Tender Discovery Tool: a local live page for one analysis document. The skil
|
|
|
5
5
|
**The protocol is not in this file.** Run
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@
|
|
8
|
+
npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest guide
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
and follow it: it is the single source for the xlsx import, start, the batch kinds, poll, emit, redelivery and close, and every command ends with a `next_step:` line. An installed copy of any of that goes stale against a newer CLI. This file adds only what `guide` cannot know — the install gate, the row-batch setting, which opening batch to post, the subagent brief, the follow-up queueing grammar and the close report.
|
|
@@ -58,7 +58,7 @@ Invoke it with the Skill tool: skill "sw-discover-tender", args "<analysis path>
|
|
|
58
58
|
Append the session's row-batch setting to the args when it is not the default: <--batch n | --batch all>.
|
|
59
59
|
If the Skill tool is not available to you, read the SKILL.md of the `sw-discover-tender` skill and follow it exactly, including its "Editor mode" section.
|
|
60
60
|
Rules: never ask the user a question with a structured question tool (Claude Code: `AskUserQuestion`; Codex: `request_user_input`); every gap becomes a §5 block with options; emit progress with
|
|
61
|
-
npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@
|
|
61
|
+
npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest emit progress "<step>" --batch <id>
|
|
62
62
|
before each procedure step and before/after each agent wave; never change a [x] or [-] except by reconciling it; keep every [ ] line whose
|
|
63
63
|
Evidence / risk or Risk to says "partner"; frozen accepted/rejected lines stay byte-identical; never edit the client's file.
|
|
64
64
|
Context — last chat entries (newest last):
|
|
@@ -111,5 +111,5 @@ Invoked by the editor session (`reference/editor-session.md` in this skill's dir
|
|
|
111
111
|
- **Notes.** `comment` notes carry `block`, `blockKind`, `path` (section breadcrumb), `line`–`endLine`, `md` (the block's source line), `quote`, `hash` and sometimes `selection` (the exact text the user highlighted). Open the file at that range under that path, confirm the text matches `md`, fall back to searching for `quote` when lines shifted. By `blockKind`: `req` → the note questions the class, mechanism or estimate: re-spawn the architect for that row only and rewrite the `req` line; `assume` / `global` → rewrite the statement as a new `[ ]` line (a frozen line is never edited; a proposed line may be replaced in place), or adjust `PD saved` when the note is about the figure; `clarify` → the decision text; `question` → reword or re-option the block (keep its id); `section` / `paragraph` / `row` → that text. `free` notes and `chat` are the briefing for the run. No PD figure ever comes from the page: a note asking for a different number is an architect question, not a hand edit.
|
|
112
112
|
- **A note on a row still `queued` or `analysing`** is not an immediate re-spawn: carry it into that row's own estimate batch and answer it there.
|
|
113
113
|
- **Preserve on every write.** Frozen `accepted <date>` / `rejected <date>` lines byte-identical; every `[x]` / `[-]` untouched except by reconciling it; partner-proposed `[ ]` lines; `X-n` exclusions the page added; §1.1, §1.2, §8, §9 and the nine section headings; the frontmatter block. Rewrite lines in place, never regenerate from the template. Every write must leave the document parseable — the page re-renders on each one.
|
|
114
|
-
- **Progress.** `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@
|
|
114
|
+
- **Progress.** `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest emit progress "<step>" --batch <id>` before each procedure step and before and after each spawn wave; the page shows it. Row status carries the rest: `queued` → `analysing` → `estimated`.
|
|
115
115
|
- **Report.** The 8-line report of step 8 with its `next:` line (plus the 3 export lines); the editor posts it to the page. Never paste the document into the report.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
`ls specs`. Keep `rfp-NNNN-` from the input name; else `NNNN` = highest existing + 1, zero-padded, slug = kebab-case `<client>-<project>`. An xlsx is imported and its mapping confirmed first (below). `shasum -a 256` every source file into frontmatter `source`; for an xlsx, the workbook, `import-map.json` and every CSV. Continue mode: keep path, number and slug; never renumber, never a second file for the same tender.
|
|
16
16
|
|
|
17
|
-
**.xlsx import.** Install gate first, per `reference/editor-session.md` §0: the `sw-tender-discovery-tool` skill must be installed; missing → stop, do not import. `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@
|
|
17
|
+
**.xlsx import.** Install gate first, per `reference/editor-session.md` §0: the `sw-tender-discovery-tool` skill must be installed; missing → stop, do not import. `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest import --source <file>` reads the workbook (hidden and `_` sheets never leave it) and proposes a mapping — which tabs are requirement tables, where each header sits, what each column means, which tokens the dropdowns allow. **With `--editor`** the user confirms it on the steering screen. **Without it**, print the proposed table the command listed, ask the user in one line whether it is right, and on a yes run `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest import --source <file> --map --accept-proposed`; if a tab or a column is wrong, say so and rerun with `--editor`. Either way the confirmed `import-map.json` and the normalised `<source dir>/<basename>/<pos>-<sheet-slug>.csv` are then the source, and the analysis lands next to the workbook. A refusal (encrypted, `.xls`, `.xlsb`) is relayed verbatim and the run stops.
|
|
18
18
|
|
|
19
19
|
### 2. Reconcile — first step of every continue run, before any spawn
|
|
20
20
|
|
|
@@ -93,6 +93,6 @@ When the gate below holds, one more line: "Generate the response CSV?" Then end.
|
|
|
93
93
|
- **Assemble and check, delegated**: one `sw-tender-editor` per client table, all in one message, cap 10, brief `§ASSEMBLE`. Each concatenates its cluster CSVs behind the client's header row into `<source basename>-response-<n>-<table-slug>.csv` (`<n>` and slug from the Source map) and runs the file checks: row count and id sequence equal the source table; header equals the client's; every compliance value in the allowed list; every effort cell a number, or empty with the blocked comment; no `A-`, `CQ-`, `Q-`, `RC-`, `.a`, `.c`, `PD saved`, `EUR`, `€`; no proposed or rejected assumption text; constant column count. Any failure → the agent deletes the file and returns the failing check. The orchestrator never reads the CSVs.
|
|
94
94
|
- **Check the sums** itself, from the returned per-table effort sums: they must equal §2 per priority. Mismatch → delete the files, report the check.
|
|
95
95
|
- **Write back.** Frontmatter `export: {date, files, source_sha256, stale: false}`; one `C-n`: `Response exported: <files> files, <rows> rows, Must <n> PD`.
|
|
96
|
-
- **Workbook copy.** After every response CSV passes its checks, run `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@
|
|
96
|
+
- **Workbook copy.** After every response CSV passes its checks, run `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest export --xlsx --source <file>`: it writes `<source basename>-response.xlsx`, a copy of the client's own workbook with compliance, comment and effort filled in from those CSVs, styles and dropdowns intact. The client's original is never written. Only for an xlsx tender with a confirmed mapping; a CSV/markdown tender stops at the CSVs.
|
|
97
97
|
- **Report** three lines: files with row counts · Must / Should / Could as exported · checks passed or the failing check.
|
|
98
98
|
- Same analysis and same source give byte-identical files.
|
package/skills/sw-setup/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: sw-setup
|
|
3
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
|
-
allowed-tools: Read Glob Grep AskUserQuestion Skill Bash mcp__ShopwareDevKnowledgeBase__kb_status Bash(npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest *)
|
|
5
|
+
allowed-tools: Read Glob Grep AskUserQuestion Skill Bash mcp__ShopwareDevKnowledgeBase__kb_status Bash(npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest *) Bash(npx -y @execuro-sw-ecosystem/sw-specs-editor@latest *) Bash(npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest *)
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# sw-setup
|
|
@@ -49,8 +49,8 @@ prints a human summary, not the object below. It prints one JSON object
|
|
|
49
49
|
state). Alongside it, run every
|
|
50
50
|
environment row's check from `reference/rows.md` in this skill's directory —
|
|
51
51
|
read-only, seconds each: file/directory existence, one version command, one
|
|
52
|
-
`kb_status` call,
|
|
53
|
-
|
|
52
|
+
`kb_status` call, one `npm view` registry lookup (Package updates row) — the
|
|
53
|
+
one part of step 1 that touches the network, so it is the slow part.
|
|
54
54
|
Never write, never install, never ask a question in this step.
|
|
55
55
|
|
|
56
56
|
Print one checkbox list, one line per row — the environment rows first, then
|
|
@@ -60,7 +60,7 @@ one line per configuration item the CLI's `agents[]`/`drift[]` reports:
|
|
|
60
60
|
- [x] vendor/ — shopware/core 6.7.13.0
|
|
61
61
|
- [ ] vendor/ — vendor/bin/phpunit missing (tree built --no-dev)
|
|
62
62
|
- [ ] Node — 18.x (need >= 20)
|
|
63
|
-
- [-] Visual editors (optional) — Specs Editor
|
|
63
|
+
- [-] Visual editors (optional) — Specs Editor installed, Tender tool declined
|
|
64
64
|
- [x] shopware-cli — 0.16.10
|
|
65
65
|
- [x] KB MCP — platform: implemented
|
|
66
66
|
- [ ] Acceptance-test project — tests/acceptance/ missing
|
|
@@ -68,7 +68,7 @@ one line per configuration item the CLI's `agents[]`/`drift[]` reports:
|
|
|
68
68
|
- [ ] ATS env — tests/acceptance/.env missing
|
|
69
69
|
- [x] Plugin tests — AhCheckout: phpunit + jest ok
|
|
70
70
|
- [ ] Project wiki — docs/project-wiki/ missing
|
|
71
|
-
- [ ] Package updates — harness 0.1.0 → 0.1.3
|
|
71
|
+
- [ ] Package updates — harness 0.1.0 → 0.1.3
|
|
72
72
|
- [ ] Installer config — .mcp.json entry: drift (from CLI status)
|
|
73
73
|
```
|
|
74
74
|
|