@execuro-sw-ecosystem/sw-ecosystem-agentic-harness 0.1.0 → 0.1.3
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 +63 -0
- package/README.md +53 -14
- package/SECURITY.md +1 -1
- package/agents/sw-php-backend-developer.md +1 -1
- package/agents/sw-storefront-developer.md +5 -5
- package/agents/sw-tender-editor.md +1 -1
- package/bin/cli.mjs +8 -3
- package/codex/agents/sw-php-backend-developer.toml +2 -2
- package/codex/agents/sw-storefront-developer.toml +6 -6
- package/codex/agents/sw-tender-editor.toml +2 -2
- package/content/AGENTS.md +2 -2
- package/copilot/agents/sw-php-backend-developer.agent.md +2 -2
- package/copilot/agents/sw-storefront-developer.agent.md +6 -6
- package/copilot/agents/sw-tender-editor.agent.md +2 -2
- package/lib/actions.mjs +11 -10
- package/lib/commands.mjs +17 -14
- package/lib/{companions.mjs → extra-components.mjs} +16 -16
- package/lib/guide.mjs +39 -18
- package/lib/hosts.mjs +10 -10
- package/lib/merge.mjs +10 -0
- package/package.json +2 -2
- package/plugin.json +1 -1
- package/skills/sw-design-requirements/SKILL.md +1 -1
- package/skills/sw-design-solution/SKILL.md +1 -1
- package/skills/sw-discover-tender/SKILL.md +1 -1
- package/skills/sw-document-feature/SKILL.md +1 -1
- package/skills/sw-document-feature/reference/setup-wiki.md +7 -2
- package/skills/sw-implement-feature/SKILL.md +1 -1
- package/skills/sw-setup/SKILL.md +15 -14
- package/skills/sw-setup/reference/rows.md +31 -34
- package/skills/sw-verify-feature/SKILL.md +1 -1
- package/skills/sw-verify-feature-ac-tests/SKILL.md +1 -1
- package/skills/sw-verify-feature-architecture/SKILL.md +1 -1
- package/skills/sw-verify-feature-code-quality/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,69 @@ release.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.3] - 2026-09-17
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **The `vendor/` row now names the specific missing file when unticked.**
|
|
15
|
+
`sw-setup`'s printed line says which of `vendor/shopware/core`,
|
|
16
|
+
`composer.lock` or `vendor/bin/phpunit` is missing instead of just marking
|
|
17
|
+
the row unticked, so a `--no-dev` vendor tree (which has the first two but
|
|
18
|
+
not `vendor/bin/phpunit`) is diagnosable at a glance. Same fix as before,
|
|
19
|
+
`composer install --no-interaction`, offered the same way.
|
|
20
|
+
- **The update documentation now covers every component.** `README.md`'s
|
|
21
|
+
**Keep up to date** section became **Keeping it up to date** and says how
|
|
22
|
+
each of the three parts updates on its own schedule: this package through
|
|
23
|
+
`install`, the MCP servers by themselves (they are registered as `@latest`,
|
|
24
|
+
so a host fetches the newest build when it next starts one), and the Specs
|
|
25
|
+
Editor and Tender Discovery Tool through their own `npm i -D` line followed
|
|
26
|
+
by a re-run of `install`. The section also moved below Install's own
|
|
27
|
+
subsections, which it had been separating from their heading.
|
|
28
|
+
- **The two extra components are now named individually, with the exact
|
|
29
|
+
pinned `npm i -D` command for each**, in both `README.md`'s new **The
|
|
30
|
+
optional editors** subsection and `guide`'s output — replacing the earlier
|
|
31
|
+
`<package>@<version>` placeholder — because the probe accepts only the
|
|
32
|
+
exact version `EXTRA_COMPONENTS` pins and skips the skill for anything else,
|
|
33
|
+
including `@latest`.
|
|
34
|
+
- **Breaking: the "companion" terminology is renamed to "extra component".**
|
|
35
|
+
The CLI flag `--no-companions` is now `--no-extra-components`, and the
|
|
36
|
+
`status` JSON output key `companions` is now `extra_components`. Anything
|
|
37
|
+
scripting against `0.1.2` or earlier that reads `companions` or passes
|
|
38
|
+
`--no-companions` must update to the new names — there is no backwards-
|
|
39
|
+
compatible alias.
|
|
40
|
+
- **`sw-storefront-developer`'s locate proofs now check for deprecated
|
|
41
|
+
forwarder files and `feature()` branches, and read the active theme's
|
|
42
|
+
`views` chain.** Proof 1 rejects a template whose header names it a removed
|
|
43
|
+
forwarder; proof 2 notes the `feature()` branch a block sits in; proof 3
|
|
44
|
+
also greps the theme's `theme.json` `views` order. The chain order and
|
|
45
|
+
where an override belongs are now deferred to the storefront guideline's
|
|
46
|
+
expert template-inheritance and override-placement sections instead of
|
|
47
|
+
being restated in the agent body.
|
|
48
|
+
|
|
49
|
+
## [0.1.2] - 2026-09-16
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- **Install/update instructions now anchor to `@latest`.** Every `npx`
|
|
54
|
+
invocation of this package in `README.md`, `sw-setup`'s `SKILL.md` and
|
|
55
|
+
`reference/rows.md` uses `@execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest`
|
|
56
|
+
instead of a bare package name or a frozen version — a bare invocation can
|
|
57
|
+
resolve to a stale copy already in the `npx` cache or `node_modules`. Added
|
|
58
|
+
a **Keep up to date** section to `README.md` and a **Keeping it current**
|
|
59
|
+
section to `guide`'s output.
|
|
60
|
+
|
|
61
|
+
## [0.1.1] - 2026-09-16
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **An `install` command** — `apply --yes` under a friendlier one-step name.
|
|
66
|
+
Typing the verb is the consent, so it never prompts and needs no `--yes`; the
|
|
67
|
+
scriptable `apply --yes` is unchanged and stays what skills and CI call.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- CI and the GitHub Pages setup for the plugin marketplace.
|
|
72
|
+
|
|
10
73
|
## [0.1.0] - 2026-09-16
|
|
11
74
|
|
|
12
75
|
### Added
|
package/README.md
CHANGED
|
@@ -49,7 +49,8 @@ declines is fully set up without them, and `--editor` on `sw-design-requirements
|
|
|
49
49
|
|
|
50
50
|
| Command | What it does |
|
|
51
51
|
| --- | --- |
|
|
52
|
-
| `
|
|
52
|
+
| `install` | installs into every detected host; the same as `apply --yes` |
|
|
53
|
+
| `status` | read-only; what is installed, what drifted, which optional extra components are present |
|
|
53
54
|
| `plan` | the exact changes, written as a diff; writes nothing |
|
|
54
55
|
| `apply --yes` | performs them |
|
|
55
56
|
| `uninstall --yes` | removes what the lock file records |
|
|
@@ -58,22 +59,18 @@ declines is fully set up without them, and `--editor` on `sw-design-requirements
|
|
|
58
59
|
Flags: `--host claude-code|codex|copilot|cursor` (repeatable; omitting it
|
|
59
60
|
selects every host that is present), `--scope project|user` (default
|
|
60
61
|
`project`), `--root <path>` (default the current directory), `--yes` (required
|
|
61
|
-
by `apply` and `uninstall`, never prompts), and
|
|
62
|
-
detection of the optional editor packages).
|
|
62
|
+
by `apply` and `uninstall`, never prompts — `install` implies it), and
|
|
63
|
+
`--no-extra-components` (skip detection of the optional editor packages).
|
|
63
64
|
|
|
64
65
|
## Install
|
|
65
66
|
|
|
66
67
|
```bash
|
|
67
|
-
|
|
68
|
-
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness status
|
|
69
|
-
|
|
70
|
-
# see the exact changes, written as a diff. Writes nothing.
|
|
71
|
-
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness plan
|
|
72
|
-
|
|
73
|
-
# perform them
|
|
74
|
-
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness apply --yes
|
|
68
|
+
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest install
|
|
75
69
|
```
|
|
76
70
|
|
|
71
|
+
Want to look first? `status` is read-only and `plan` prints the diff; neither
|
|
72
|
+
writes anything.
|
|
73
|
+
|
|
77
74
|
Then restart your coding agent — every host reads its configuration at startup.
|
|
78
75
|
The command's output tells you which ones and how.
|
|
79
76
|
|
|
@@ -111,7 +108,7 @@ Codex never reads `CLAUDE.md`.
|
|
|
111
108
|
Undo everything with:
|
|
112
109
|
|
|
113
110
|
```bash
|
|
114
|
-
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness uninstall --yes
|
|
111
|
+
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest uninstall --yes
|
|
115
112
|
```
|
|
116
113
|
|
|
117
114
|
It removes exactly what the lock file at `.sw-ai-sdk/harness.lock.json` records,
|
|
@@ -125,9 +122,51 @@ installed skill file goes stale and the CLI does not. Anything that needs the
|
|
|
125
122
|
protocol runs:
|
|
126
123
|
|
|
127
124
|
```bash
|
|
128
|
-
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness guide
|
|
125
|
+
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest guide
|
|
129
126
|
```
|
|
130
127
|
|
|
128
|
+
## Keeping it up to date
|
|
129
|
+
|
|
130
|
+
Three things update on different schedules.
|
|
131
|
+
|
|
132
|
+
| What | How it updates |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| This package — skills, sub-agents, permission rules, MCP registrations | `npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest install` |
|
|
135
|
+
| The MCP servers (`ShopwareDevKnowledgeBase`, `playwright`) | By themselves. They are registered as `@latest`, so your agent fetches the newest build the next time it starts one. Nothing to run. |
|
|
136
|
+
| Specs Editor, Tender Discovery Tool | You update them. Each has its own line — see below. |
|
|
137
|
+
|
|
138
|
+
### The optional editors
|
|
139
|
+
|
|
140
|
+
Install or update each at the exact version this release pins — the CLI
|
|
141
|
+
probes for that version and skips the skill if it finds another:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm i -D @execuro-sw-ecosystem/sw-specs-editor@0.1.0
|
|
145
|
+
npm i -D @execuro-sw-ecosystem/sw-tender-discovery-tool@0.1.0
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- `sw-specs-editor` backs the `--editor` flag on `sw-design-requirements` and
|
|
149
|
+
`sw-design-solution`.
|
|
150
|
+
- `sw-tender-discovery-tool` backs the `--editor` flag and the `.xlsx` import
|
|
151
|
+
on `sw-discover-tender`.
|
|
152
|
+
|
|
153
|
+
Re-run `install` afterwards so the new skill lands in each host. `status`'s
|
|
154
|
+
`extra_components[]` array reports the pinned `version` and the exact `install`
|
|
155
|
+
command for whichever one is missing.
|
|
156
|
+
|
|
157
|
+
Re-running `install` is safe at any time: it rewrites only what it owns,
|
|
158
|
+
leaves anything you hand-edited alone (reported as drift), and records the new
|
|
159
|
+
version in the lock file. Restart your coding agent afterwards — every host
|
|
160
|
+
reads its configuration at startup.
|
|
161
|
+
|
|
162
|
+
`@latest` matters. Without it, `npx` can reuse a copy already in its cache or
|
|
163
|
+
in this project's `node_modules`, so you would silently keep running the
|
|
164
|
+
version you first installed.
|
|
165
|
+
|
|
166
|
+
To look before you change anything: `... @latest status` reports
|
|
167
|
+
`installed_version`, any drift, and which extra components are present. It
|
|
168
|
+
writes nothing.
|
|
169
|
+
|
|
131
170
|
## Prerequisites
|
|
132
171
|
|
|
133
172
|
| Requirement | Why | Check |
|
|
@@ -210,7 +249,7 @@ instead — it then applies across every repo without per-repo commits.
|
|
|
210
249
|
|
|
211
250
|
```bash
|
|
212
251
|
# 1. the install is complete and undrifted
|
|
213
|
-
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness status
|
|
252
|
+
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest status
|
|
214
253
|
|
|
215
254
|
# 2. the toolchain the agents call actually exists
|
|
216
255
|
shopware-cli project console --help
|
package/SECURITY.md
CHANGED
|
@@ -41,7 +41,7 @@ scripts** on install, which removes a large part of the usual npm attack
|
|
|
41
41
|
surface.
|
|
42
42
|
|
|
43
43
|
Out of scope: vulnerabilities in the coding agents themselves, in the
|
|
44
|
-
separately published
|
|
44
|
+
separately published extra components (they have their own repositories
|
|
45
45
|
and their own reporting), or in a third-party MCP server this package merely
|
|
46
46
|
registers, such as Playwright's (report those upstream).
|
|
47
47
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sw-php-backend-developer
|
|
3
|
-
description: Shopware 6 PHP backend specialist. Use PROACTIVELY for any Shopware backend work — plugin/app PHP, DAL entities & migrations, Store/Admin API routes, services & decorators, event subscribers, flow actions, message handlers, caching, ACL. Triggers
|
|
3
|
+
description: Shopware 6 PHP backend specialist. Use PROACTIVELY for any Shopware backend work — plugin/app PHP, DAL entities & migrations, Store/Admin API routes, services & decorators, event subscribers, flow actions, message handlers, caching, ACL. Triggers — "add an entity", "create an API endpoint", "extend checkout/order/product logic", "write a migration", "implement backend AC". Not for Administration (Vue) or Storefront (Twig/JS) work.
|
|
4
4
|
model: inherit
|
|
5
5
|
tools: Read, Write, Edit, Glob, Grep, Bash, mcp__ShopwareDevKnowledgeBase__list_docs, mcp__ShopwareDevKnowledgeBase__grep_docs, mcp__ShopwareDevKnowledgeBase__read_doc, mcp__ShopwareDevKnowledgeBase__kb_status
|
|
6
6
|
color: blue
|
|
@@ -41,7 +41,7 @@ An edit is not done until `lint:twig` passes on it and the changed markup is obs
|
|
|
41
41
|
- Lighthouse on touched pages; perf/a11y/SEO regressions are failures.
|
|
42
42
|
- Playwright MCP (`mcp__playwright__*`) for click-through and screenshots. Shopware docs MCP (`mcp__ShopwareDevKnowledgeBase__*`) for the exact core template, block, route or plugin before extending; never guess a block name.
|
|
43
43
|
- Test the targeted state: logged-out, logged-in, guest checkout, plus a non-default currency or language when prices or text change.
|
|
44
|
-
- "Does not show up" checklist, in order: template path, block name, theme assigned, theme compiled, cache cleared, browser cache.
|
|
44
|
+
- "Does not show up" checklist, in order: template path (not a deprecated forwarder file), block name in that file (and its `feature()` branch), theme assigned, theme compiled, cache cleared, browser cache.
|
|
45
45
|
|
|
46
46
|
### Small, reversible
|
|
47
47
|
- One concern per template, subscriber or plugin; header + footer + checkout is three changes.
|
|
@@ -56,14 +56,14 @@ An edit is not done until `lint:twig` passes on it and the changed markup is obs
|
|
|
56
56
|
|
|
57
57
|
1. **Classify.** Confirm storefront work (see Boundary). If mixed, split and hand off the non-storefront part first.
|
|
58
58
|
2. **Locate — prove it, never assume.** Docs MCP answers *what an extension point is for*; only the installed source answers *whether it exists here*. Produce all four proofs before writing a single `{% block %}`:
|
|
59
|
-
1. The target template exists at the exact relative path: `ls vendor/shopware/storefront/Resources/views/storefront/<rel
|
|
60
|
-
2. The block name exists verbatim **in that file**: `grep -n "{% block <name>" vendor/shopware/storefront/Resources/views/storefront/<rel>`. Block names are not unique across the storefront (~2200 block tags, ~2035 distinct names — every override re-declares its parent's name), so a repo-wide grep for a name is not proof. If it is not in that file, it lives in an included sub-template: find that file and override it instead.
|
|
61
|
-
3. Every other bundle already providing the same relative path: `grep -rl "<rel>" custom/ vendor/*/*/Resources/views
|
|
59
|
+
1. The target template exists at the exact relative path: `ls vendor/shopware/storefront/Resources/views/storefront/<rel>` — and its first lines carry no `@deprecated … File will be removed` header: such a forwarder is never loaded, extend the file it names instead.
|
|
60
|
+
2. The block name exists verbatim **in that file**: `grep -n "{% block <name>" vendor/shopware/storefront/Resources/views/storefront/<rel>`. Block names are not unique across the storefront (~2200 block tags, ~2035 distinct names — every override re-declares its parent's name), so a repo-wide grep for a name is not proof. If it is not in that file, it lives in an included sub-template: find that file and override it instead. Note the `feature()` branch the block sits in, if any — the guideline's template-inheritance section says which anchor to target.
|
|
61
|
+
3. Every other bundle already providing the same relative path: `grep -rl "<rel>" custom/ vendor/*/*/Resources/views/`, plus the active theme's chain: `grep -A4 '"views"' custom/*/*/src/Resources/theme.json`. Name who wins under the chain order in the guideline's template-inheritance section, and whether they call `{{ parent() }}`.
|
|
62
62
|
4. When the dev env is up, `shopware-cli project console debug:twig @Storefront/storefront/<rel>` prints "Matched File" and "Overridden Files" — the real resolution, and it overrides 1–3. Caveat: the theme slot is filled per storefront request, so a CLI run may show only the `@Storefront`/`@Plugins` part of the chain; the browser is the final word on which theme override wins.
|
|
63
63
|
A block name recalled from memory or from an older Shopware version is a defect, not a starting point.
|
|
64
64
|
3. **Place.** Theme (look, Bootstrap variables) vs plugin (features).
|
|
65
65
|
4. **Test first** where tooling supports it: Jest for JS plugins, functional/e2e for rendered HTML. Failing test before the extension.
|
|
66
|
-
5. **Implement** the smallest extension that passes: one block, one subscriber, one plugin.
|
|
66
|
+
5. **Implement** the smallest extension that passes: one block, one subscriber, one plugin — placed per the guideline's override-placement ladder (edit an existing project override rather than adding a second file for the same path; go later in the chain than a marketplace override).
|
|
67
67
|
6. **Lint and build.** `lint:twig` on every touched Twig file first, then `extension validate` for SCSS/JS, then the cache/theme/build sequence — all per "Verify: lint, build, render".
|
|
68
68
|
7. **Verify.** Render assertion first (the changed markup is actually in the page), then browser pass (Playwright MCP), console/network clean, breakpoints, keyboard, Lighthouse.
|
|
69
69
|
8. **Report** in the format below.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sw-tender-editor
|
|
3
|
-
description: Mechanical file worker for the sw-discover-tender skill — extracts requirement tables from a tender's source export into cluster files, merges PM/architect/QA cluster reports into finished §4 row blocks, splices those blocks into the analysis sidecar, and assembles and checks the final response CSVs. Deterministic, no judgment
|
|
3
|
+
description: Mechanical file worker for the sw-discover-tender skill — extracts requirement tables from a tender's source export into cluster files, merges PM/architect/QA cluster reports into finished §4 row blocks, splices those blocks into the analysis sidecar, and assembles and checks the final response CSVs. Deterministic, no judgment — every value it writes comes from a named input file, never from memory or estimation. Not for research, feasibility checks, effort estimation or any judgment call — that is `sw-product-manager`, `sw-shopware-architect` and `sw-qa-engineer`'s work; this agent only reshapes what they already produced into the file the skill needs next.
|
|
4
4
|
tools: Read, Write, Edit, Glob, Grep
|
|
5
5
|
---
|
|
6
6
|
|
package/bin/cli.mjs
CHANGED
|
@@ -21,25 +21,30 @@ const COMMANDS = {
|
|
|
21
21
|
status: async () => (await import('../lib/commands.mjs')).status,
|
|
22
22
|
plan: async () => (await import('../lib/commands.mjs')).plan,
|
|
23
23
|
apply: async () => (await import('../lib/commands.mjs')).apply,
|
|
24
|
+
install: async () => (await import('../lib/commands.mjs')).install,
|
|
24
25
|
uninstall: async () => (await import('../lib/commands.mjs')).uninstall,
|
|
25
26
|
guide: async () => (await import('../lib/guide.mjs')).main,
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
const USAGE = `usage: sw-ecosystem-agentic-harness <command> [options]
|
|
29
30
|
|
|
31
|
+
install [--host <h>] [--scope project|user] [--root <path>] [--no-extra-components]
|
|
30
32
|
status [--host <h>] [--scope project|user] [--root <path>]
|
|
31
|
-
plan [--host <h>] [--scope project|user] [--root <path>] [--no-
|
|
32
|
-
apply --yes [--host <h>] [--scope project|user] [--root <path>] [--no-
|
|
33
|
+
plan [--host <h>] [--scope project|user] [--root <path>] [--no-extra-components]
|
|
34
|
+
apply --yes [--host <h>] [--scope project|user] [--root <path>] [--no-extra-components]
|
|
33
35
|
uninstall --yes [--host <h>] [--scope project|user] [--root <path>]
|
|
34
36
|
guide print the install protocol
|
|
35
37
|
|
|
38
|
+
install is apply --yes under a friendlier name: the whole install in one
|
|
39
|
+
command, no --yes needed — typing the verb is the consent.
|
|
40
|
+
|
|
36
41
|
<h> is one of: claude-code, codex, copilot, cursor. Repeat --host to select
|
|
37
42
|
several; omitting it selects every host that is present.
|
|
38
43
|
|
|
39
44
|
--scope defaults to project: nothing is written under your home directory
|
|
40
45
|
unless you ask for it. --root defaults to the current directory.
|
|
41
46
|
|
|
42
|
-
--no-
|
|
47
|
+
--no-extra-components skips the two optional extra-component skills (Specs
|
|
43
48
|
Editor, Tender Discovery Tool) even when the package is present locally.
|
|
44
49
|
|
|
45
50
|
Every command prints one JSON object on stdout carrying a next_step. Run
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Generated by scripts/gen-codex-agents.mjs — do not edit.
|
|
2
2
|
# source: agents/sw-php-backend-developer.md
|
|
3
|
-
# source-sha256:
|
|
3
|
+
# source-sha256: ea82533b10d08e276f3c82da3555fd49f66b4141850990bca3e7e878fe07df61
|
|
4
4
|
|
|
5
5
|
name = "sw-php-backend-developer"
|
|
6
|
-
description = "Shopware 6 PHP backend specialist. Use PROACTIVELY for any Shopware backend work — plugin/app PHP, DAL entities & migrations, Store/Admin API routes, services & decorators, event subscribers, flow actions, message handlers, caching, ACL. Triggers
|
|
6
|
+
description = "Shopware 6 PHP backend specialist. Use PROACTIVELY for any Shopware backend work — plugin/app PHP, DAL entities & migrations, Store/Admin API routes, services & decorators, event subscribers, flow actions, message handlers, caching, ACL. Triggers — \"add an entity\", \"create an API endpoint\", \"extend checkout/order/product logic\", \"write a migration\", \"implement backend AC\". Not for Administration (Vue) or Storefront (Twig/JS) work."
|
|
7
7
|
|
|
8
8
|
developer_instructions = '''
|
|
9
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Generated by scripts/gen-codex-agents.mjs — do not edit.
|
|
2
2
|
# source: agents/sw-storefront-developer.md
|
|
3
|
-
# source-sha256:
|
|
3
|
+
# source-sha256: 9afbeda4ca95afe1d74bf0268c3551031a519524fd2d1c4d2c4ece874cd6c054
|
|
4
4
|
|
|
5
5
|
name = "sw-storefront-developer"
|
|
6
6
|
description = "Use PROACTIVELY for any Shopware 6 Storefront work - Twig templates, theme/SCSS, storefront controllers/pages/pagelets, storefront JS plugins, checkout/account/product-page customizations."
|
|
@@ -43,7 +43,7 @@ An edit is not done until `lint:twig` passes on it and the changed markup is obs
|
|
|
43
43
|
- Lighthouse on touched pages; perf/a11y/SEO regressions are failures.
|
|
44
44
|
- Playwright MCP (`mcp__playwright__*`) for click-through and screenshots. Shopware docs MCP (`mcp__ShopwareDevKnowledgeBase__*`) for the exact core template, block, route or plugin before extending; never guess a block name.
|
|
45
45
|
- Test the targeted state: logged-out, logged-in, guest checkout, plus a non-default currency or language when prices or text change.
|
|
46
|
-
- "Does not show up" checklist, in order: template path, block name, theme assigned, theme compiled, cache cleared, browser cache.
|
|
46
|
+
- "Does not show up" checklist, in order: template path (not a deprecated forwarder file), block name in that file (and its `feature()` branch), theme assigned, theme compiled, cache cleared, browser cache.
|
|
47
47
|
|
|
48
48
|
### Small, reversible
|
|
49
49
|
- One concern per template, subscriber or plugin; header + footer + checkout is three changes.
|
|
@@ -58,14 +58,14 @@ An edit is not done until `lint:twig` passes on it and the changed markup is obs
|
|
|
58
58
|
|
|
59
59
|
1. **Classify.** Confirm storefront work (see Boundary). If mixed, split and hand off the non-storefront part first.
|
|
60
60
|
2. **Locate — prove it, never assume.** Docs MCP answers *what an extension point is for*; only the installed source answers *whether it exists here*. Produce all four proofs before writing a single `{% block %}`:
|
|
61
|
-
1. The target template exists at the exact relative path: `ls vendor/shopware/storefront/Resources/views/storefront/<rel
|
|
62
|
-
2. The block name exists verbatim **in that file**: `grep -n "{% block <name>" vendor/shopware/storefront/Resources/views/storefront/<rel>`. Block names are not unique across the storefront (~2200 block tags, ~2035 distinct names — every override re-declares its parent's name), so a repo-wide grep for a name is not proof. If it is not in that file, it lives in an included sub-template: find that file and override it instead.
|
|
63
|
-
3. Every other bundle already providing the same relative path: `grep -rl "<rel>" custom/ vendor/*/*/Resources/views
|
|
61
|
+
1. The target template exists at the exact relative path: `ls vendor/shopware/storefront/Resources/views/storefront/<rel>` — and its first lines carry no `@deprecated … File will be removed` header: such a forwarder is never loaded, extend the file it names instead.
|
|
62
|
+
2. The block name exists verbatim **in that file**: `grep -n "{% block <name>" vendor/shopware/storefront/Resources/views/storefront/<rel>`. Block names are not unique across the storefront (~2200 block tags, ~2035 distinct names — every override re-declares its parent's name), so a repo-wide grep for a name is not proof. If it is not in that file, it lives in an included sub-template: find that file and override it instead. Note the `feature()` branch the block sits in, if any — the guideline's template-inheritance section says which anchor to target.
|
|
63
|
+
3. Every other bundle already providing the same relative path: `grep -rl "<rel>" custom/ vendor/*/*/Resources/views/`, plus the active theme's chain: `grep -A4 '"views"' custom/*/*/src/Resources/theme.json`. Name who wins under the chain order in the guideline's template-inheritance section, and whether they call `{{ parent() }}`.
|
|
64
64
|
4. When the dev env is up, `shopware-cli project console debug:twig @Storefront/storefront/<rel>` prints "Matched File" and "Overridden Files" — the real resolution, and it overrides 1–3. Caveat: the theme slot is filled per storefront request, so a CLI run may show only the `@Storefront`/`@Plugins` part of the chain; the browser is the final word on which theme override wins.
|
|
65
65
|
A block name recalled from memory or from an older Shopware version is a defect, not a starting point.
|
|
66
66
|
3. **Place.** Theme (look, Bootstrap variables) vs plugin (features).
|
|
67
67
|
4. **Test first** where tooling supports it: Jest for JS plugins, functional/e2e for rendered HTML. Failing test before the extension.
|
|
68
|
-
5. **Implement** the smallest extension that passes: one block, one subscriber, one plugin.
|
|
68
|
+
5. **Implement** the smallest extension that passes: one block, one subscriber, one plugin — placed per the guideline's override-placement ladder (edit an existing project override rather than adding a second file for the same path; go later in the chain than a marketplace override).
|
|
69
69
|
6. **Lint and build.** `lint:twig` on every touched Twig file first, then `extension validate` for SCSS/JS, then the cache/theme/build sequence — all per "Verify: lint, build, render".
|
|
70
70
|
7. **Verify.** Render assertion first (the changed markup is actually in the page), then browser pass (Playwright MCP), console/network clean, breakpoints, keyboard, Lighthouse.
|
|
71
71
|
8. **Report** in the format below.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Generated by scripts/gen-codex-agents.mjs — do not edit.
|
|
2
2
|
# source: agents/sw-tender-editor.md
|
|
3
|
-
# source-sha256:
|
|
3
|
+
# source-sha256: 288b5d684f40ea62aa2b6021cfa3109a7f664fc2365f32109bcd9d1f94eb8a2c
|
|
4
4
|
|
|
5
5
|
name = "sw-tender-editor"
|
|
6
|
-
description = "Mechanical file worker for the sw-discover-tender skill — extracts requirement tables from a tender's source export into cluster files, merges PM/architect/QA cluster reports into finished §4 row blocks, splices those blocks into the analysis sidecar, and assembles and checks the final response CSVs. Deterministic, no judgment
|
|
6
|
+
description = "Mechanical file worker for the sw-discover-tender skill — extracts requirement tables from a tender's source export into cluster files, merges PM/architect/QA cluster reports into finished §4 row blocks, splices those blocks into the analysis sidecar, and assembles and checks the final response CSVs. Deterministic, no judgment — every value it writes comes from a named input file, never from memory or estimation. Not for research, feasibility checks, effort estimation or any judgment call — that is `sw-product-manager`, `sw-shopware-architect` and `sw-qa-engineer`'s work; this agent only reshapes what they already produced into the file the skill needs next."
|
|
7
7
|
|
|
8
8
|
developer_instructions = '''
|
|
9
9
|
|
package/content/AGENTS.md
CHANGED
|
@@ -16,7 +16,7 @@ procedure, and the detail lives in `reference/*.md` files beside its
|
|
|
16
16
|
| `sw-discover-tender` | Analyse a client tender (RFP, RFI or RFQ) for a Shopware 6 project into one markdown sidecar, <source basename>-analysis.md. |
|
|
17
17
|
| `sw-document-feature` | Document one Shopware project feature into the LLM-wiki under docs/project-wiki/ — one feature page per surface (administration/ and/or storefront/), the "why" row in the domain index, ADR pages only promoted from the WIP ADR files sw-design-solution extracted into specs/ once they are accepted and built (with an organised table of contents) or provided by the user (never invented). |
|
|
18
18
|
| `sw-implement-feature` | Implement a feature from its technical spec (specs/NNNN-slug-spec.md, written by sw-design-solution) following TDD — tests first per acceptance criterion, then the implementation, fixing until every relevant test passes (including that AC's own acceptance/e2e test, run immediately, not deferred), running Shopware's own shopware-cli fixers/static-analysis per AC to conform to Shopware's coding standards as code is written rather than after the fact. |
|
|
19
|
-
| `sw-setup` | Print one environment-readiness table for the project's development/test setup —
|
|
19
|
+
| `sw-setup` | Print one environment-readiness table for the project's development/test setup — vendor/, Node, the editor CLIs, shopware-cli, the KB MCP, the acceptance-test project, Playwright browsers, its .env, per-plugin test scaffolding, the project wiki and `.gitignore`, plus the installer CLI's own configuration/drift status. |
|
|
20
20
|
| `sw-verify-feature` | Verify a feature implementation against its technical spec (specs/NNNN-slug-spec.md) by running three independent verifiers in parallel — AC test coverage/health, architecture/guideline compliance, and code quality/static analysis — then cross-checking their verdicts into one final per-AC pass/not pass/partly report. |
|
|
21
21
|
| `sw-verify-feature-ac-tests` | Verify that a feature's acceptance criteria are backed by tests that actually exist, actually pass, and actually test what their name/comment/PHPDoc claims. |
|
|
22
22
|
| `sw-verify-feature-architecture` | Verify that a feature's implementation matches the architectural decisions its spec made, and complies with Shopware's own Core conventions plus any written Project guidelines this repo defines. |
|
|
@@ -45,7 +45,7 @@ and installs the harness configuration — permission rules and the MCP
|
|
|
45
45
|
server registrations — through the installer CLI:
|
|
46
46
|
|
|
47
47
|
```
|
|
48
|
-
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@
|
|
48
|
+
npx -y @execuro-sw-ecosystem/sw-ecosystem-agentic-harness@latest status
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
The Shopware knowledge base is served by the `ShopwareDevKnowledgeBase`
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "sw-php-backend-developer"
|
|
3
|
-
description: "Shopware 6 PHP backend specialist. Use PROACTIVELY for any Shopware backend work — plugin/app PHP, DAL entities & migrations, Store/Admin API routes, services & decorators, event subscribers, flow actions, message handlers, caching, ACL. Triggers
|
|
3
|
+
description: "Shopware 6 PHP backend specialist. Use PROACTIVELY for any Shopware backend work — plugin/app PHP, DAL entities & migrations, Store/Admin API routes, services & decorators, event subscribers, flow actions, message handlers, caching, ACL. Triggers — \"add an entity\", \"create an API endpoint\", \"extend checkout/order/product logic\", \"write a migration\", \"implement backend AC\". Not for Administration (Vue) or Storefront (Twig/JS) work."
|
|
4
4
|
tools: [read, edit, search, execute]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by scripts/gen-copilot-agents.mjs — do not edit. -->
|
|
8
8
|
<!-- source: agents/sw-php-backend-developer.md -->
|
|
9
|
-
<!-- source-sha256:
|
|
9
|
+
<!-- source-sha256: ea82533b10d08e276f3c82da3555fd49f66b4141850990bca3e7e878fe07df61 -->
|
|
10
10
|
# PHP Backend Developer (Shopware 6)
|
|
11
11
|
|
|
12
12
|
## Identity
|
|
@@ -5,7 +5,7 @@ description: "Use PROACTIVELY for any Shopware 6 Storefront work - Twig template
|
|
|
5
5
|
|
|
6
6
|
<!-- Generated by scripts/gen-copilot-agents.mjs — do not edit. -->
|
|
7
7
|
<!-- source: agents/sw-storefront-developer.md -->
|
|
8
|
-
<!-- source-sha256:
|
|
8
|
+
<!-- source-sha256: 9afbeda4ca95afe1d74bf0268c3551031a519524fd2d1c4d2c4ece874cd6c054 -->
|
|
9
9
|
# Shopware Storefront Developer
|
|
10
10
|
|
|
11
11
|
## Role
|
|
@@ -42,7 +42,7 @@ An edit is not done until `lint:twig` passes on it and the changed markup is obs
|
|
|
42
42
|
- Lighthouse on touched pages; perf/a11y/SEO regressions are failures.
|
|
43
43
|
- Playwright MCP (`mcp__playwright__*`) for click-through and screenshots. Shopware docs MCP (`mcp__ShopwareDevKnowledgeBase__*`) for the exact core template, block, route or plugin before extending; never guess a block name.
|
|
44
44
|
- Test the targeted state: logged-out, logged-in, guest checkout, plus a non-default currency or language when prices or text change.
|
|
45
|
-
- "Does not show up" checklist, in order: template path, block name, theme assigned, theme compiled, cache cleared, browser cache.
|
|
45
|
+
- "Does not show up" checklist, in order: template path (not a deprecated forwarder file), block name in that file (and its `feature()` branch), theme assigned, theme compiled, cache cleared, browser cache.
|
|
46
46
|
|
|
47
47
|
### Small, reversible
|
|
48
48
|
- One concern per template, subscriber or plugin; header + footer + checkout is three changes.
|
|
@@ -57,14 +57,14 @@ An edit is not done until `lint:twig` passes on it and the changed markup is obs
|
|
|
57
57
|
|
|
58
58
|
1. **Classify.** Confirm storefront work (see Boundary). If mixed, split and hand off the non-storefront part first.
|
|
59
59
|
2. **Locate — prove it, never assume.** Docs MCP answers *what an extension point is for*; only the installed source answers *whether it exists here*. Produce all four proofs before writing a single `{% block %}`:
|
|
60
|
-
1. The target template exists at the exact relative path: `ls vendor/shopware/storefront/Resources/views/storefront/<rel
|
|
61
|
-
2. The block name exists verbatim **in that file**: `grep -n "{% block <name>" vendor/shopware/storefront/Resources/views/storefront/<rel>`. Block names are not unique across the storefront (~2200 block tags, ~2035 distinct names — every override re-declares its parent's name), so a repo-wide grep for a name is not proof. If it is not in that file, it lives in an included sub-template: find that file and override it instead.
|
|
62
|
-
3. Every other bundle already providing the same relative path: `grep -rl "<rel>" custom/ vendor/*/*/Resources/views
|
|
60
|
+
1. The target template exists at the exact relative path: `ls vendor/shopware/storefront/Resources/views/storefront/<rel>` — and its first lines carry no `@deprecated … File will be removed` header: such a forwarder is never loaded, extend the file it names instead.
|
|
61
|
+
2. The block name exists verbatim **in that file**: `grep -n "{% block <name>" vendor/shopware/storefront/Resources/views/storefront/<rel>`. Block names are not unique across the storefront (~2200 block tags, ~2035 distinct names — every override re-declares its parent's name), so a repo-wide grep for a name is not proof. If it is not in that file, it lives in an included sub-template: find that file and override it instead. Note the `feature()` branch the block sits in, if any — the guideline's template-inheritance section says which anchor to target.
|
|
62
|
+
3. Every other bundle already providing the same relative path: `grep -rl "<rel>" custom/ vendor/*/*/Resources/views/`, plus the active theme's chain: `grep -A4 '"views"' custom/*/*/src/Resources/theme.json`. Name who wins under the chain order in the guideline's template-inheritance section, and whether they call `{{ parent() }}`.
|
|
63
63
|
4. When the dev env is up, `shopware-cli project console debug:twig @Storefront/storefront/<rel>` prints "Matched File" and "Overridden Files" — the real resolution, and it overrides 1–3. Caveat: the theme slot is filled per storefront request, so a CLI run may show only the `@Storefront`/`@Plugins` part of the chain; the browser is the final word on which theme override wins.
|
|
64
64
|
A block name recalled from memory or from an older Shopware version is a defect, not a starting point.
|
|
65
65
|
3. **Place.** Theme (look, Bootstrap variables) vs plugin (features).
|
|
66
66
|
4. **Test first** where tooling supports it: Jest for JS plugins, functional/e2e for rendered HTML. Failing test before the extension.
|
|
67
|
-
5. **Implement** the smallest extension that passes: one block, one subscriber, one plugin.
|
|
67
|
+
5. **Implement** the smallest extension that passes: one block, one subscriber, one plugin — placed per the guideline's override-placement ladder (edit an existing project override rather than adding a second file for the same path; go later in the chain than a marketplace override).
|
|
68
68
|
6. **Lint and build.** `lint:twig` on every touched Twig file first, then `extension validate` for SCSS/JS, then the cache/theme/build sequence — all per "Verify: lint, build, render".
|
|
69
69
|
7. **Verify.** Render assertion first (the changed markup is actually in the page), then browser pass (Playwright MCP), console/network clean, breakpoints, keyboard, Lighthouse.
|
|
70
70
|
8. **Report** in the format below.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "sw-tender-editor"
|
|
3
|
-
description: "Mechanical file worker for the sw-discover-tender skill — extracts requirement tables from a tender's source export into cluster files, merges PM/architect/QA cluster reports into finished §4 row blocks, splices those blocks into the analysis sidecar, and assembles and checks the final response CSVs. Deterministic, no judgment
|
|
3
|
+
description: "Mechanical file worker for the sw-discover-tender skill — extracts requirement tables from a tender's source export into cluster files, merges PM/architect/QA cluster reports into finished §4 row blocks, splices those blocks into the analysis sidecar, and assembles and checks the final response CSVs. Deterministic, no judgment — every value it writes comes from a named input file, never from memory or estimation. Not for research, feasibility checks, effort estimation or any judgment call — that is `sw-product-manager`, `sw-shopware-architect` and `sw-qa-engineer`'s work; this agent only reshapes what they already produced into the file the skill needs next."
|
|
4
4
|
tools: [read, edit, search]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- Generated by scripts/gen-copilot-agents.mjs — do not edit. -->
|
|
8
8
|
<!-- source: agents/sw-tender-editor.md -->
|
|
9
|
-
<!-- source-sha256:
|
|
9
|
+
<!-- source-sha256: 288b5d684f40ea62aa2b6021cfa3109a7f664fc2365f32109bcd9d1f94eb8a2c -->
|
|
10
10
|
# sw-tender-editor
|
|
11
11
|
|
|
12
12
|
## Identity
|
package/lib/actions.mjs
CHANGED
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
abs, ensureDir, readMaybe, readTextMaybe, removeDirIfEmpty, removeFile, rel, sha256, writeAtomic,
|
|
15
15
|
} from './fsx.mjs';
|
|
16
16
|
import {
|
|
17
|
-
canonical, deletePointer, fenceInner, findFence, getPointer, mergeArrayUnique,
|
|
18
|
-
removeArrayValues, removeFence, serializeJson, setPointer, upsertFence,
|
|
17
|
+
canonical, deletePointer, fenceInner, findFence, getPointer, mergeArrayUnique, normalizeEol,
|
|
18
|
+
parseJsonLoose, removeArrayValues, removeFence, serializeJson, setPointer, upsertFence,
|
|
19
19
|
} from './merge.mjs';
|
|
20
20
|
import { PKG_ROOT } from './content.mjs';
|
|
21
21
|
import { isDeclined } from './lock.mjs';
|
|
22
22
|
import { readFileSync } from 'node:fs';
|
|
23
|
-
import {
|
|
23
|
+
import { EXTRA_COMPONENTS, probe } from './extra-components.mjs';
|
|
24
24
|
|
|
25
25
|
/** Preview one action against the filesystem. Reads only. */
|
|
26
26
|
export function preview(action, ctx) {
|
|
@@ -83,8 +83,8 @@ function stripProbeBody({ body, ...rest }) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
function previewSkill(action, ctx) {
|
|
86
|
-
const
|
|
87
|
-
const probed = probe(
|
|
86
|
+
const component = EXTRA_COMPONENTS.find((c) => c.id === action.component);
|
|
87
|
+
const probed = probe(component, { platform: ctx.platform });
|
|
88
88
|
const probeSummary = stripProbeBody(probed);
|
|
89
89
|
|
|
90
90
|
if (!probed.available) {
|
|
@@ -226,8 +226,9 @@ function previewFence(action, ctx) {
|
|
|
226
226
|
const found = readTextMaybe(target);
|
|
227
227
|
const createdFile = found === null;
|
|
228
228
|
// Hash what will actually sit inside the fence, warning line included, so it
|
|
229
|
-
// can be compared with what `findFence` reads back.
|
|
230
|
-
|
|
229
|
+
// can be compared with what `findFence` reads back. Normalised to LF first —
|
|
230
|
+
// a fence's identity does not depend on the target file's line-ending style.
|
|
231
|
+
const hash = sha256(normalizeEol(fenceInner(action.body, action.syntax)));
|
|
231
232
|
const locked = ctx.lock?.fences?.find(
|
|
232
233
|
(f) => f.file === action.target && f.block === action.block,
|
|
233
234
|
);
|
|
@@ -243,7 +244,7 @@ function previewFence(action, ctx) {
|
|
|
243
244
|
return { ...base, prior: null, state: 'create',
|
|
244
245
|
detail: createdFile ? 'creates the file with a managed block' : 'appends a managed block' };
|
|
245
246
|
}
|
|
246
|
-
const priorHash = sha256(existing.inner);
|
|
247
|
+
const priorHash = sha256(normalizeEol(existing.inner));
|
|
247
248
|
if (priorHash === hash) return { ...base, prior: priorHash, state: 'unchanged', created_file: locked?.created_file ?? false, detail: 'up to date' };
|
|
248
249
|
if (locked && locked.hash !== priorHash) {
|
|
249
250
|
return { ...base, prior: priorHash, state: 'drift', created_file: locked.created_file,
|
|
@@ -276,7 +277,7 @@ export function execute(action, ctx) {
|
|
|
276
277
|
}
|
|
277
278
|
case 'skill-copy': {
|
|
278
279
|
// Collapses to a `file-copy` lock entry on purpose: `lock.record`,
|
|
279
|
-
// `lock.entries`, `revert` and `rebuild` need no
|
|
280
|
+
// `lock.entries`, `revert` and `rebuild` need no extra-component-specific
|
|
280
281
|
// case, and `uninstall` needs no probe. `source` is provenance only —
|
|
281
282
|
// `revert` never reads it.
|
|
282
283
|
ensureDir(dirname(target));
|
|
@@ -401,7 +402,7 @@ export function revert(entry, ctx) {
|
|
|
401
402
|
if (found === null) return { state: 'missing', target: entry.target, reason: 'already gone' };
|
|
402
403
|
const fence = findFence(found.text, entry.block);
|
|
403
404
|
if (!fence) return { state: 'missing', target: entry.target, reason: 'our block is already gone' };
|
|
404
|
-
if (sha256(fence.inner) !== entry.hash) {
|
|
405
|
+
if (sha256(normalizeEol(fence.inner)) !== entry.hash) {
|
|
405
406
|
return { state: 'kept', target: entry.target, reason: 'content inside the managed fence was hand-edited' };
|
|
406
407
|
}
|
|
407
408
|
const { next } = removeFence(found.text, entry.block);
|
package/lib/commands.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { exists, isWritable, abs } from './fsx.mjs';
|
|
|
14
14
|
import { HOSTS, buildPlan, detectAll, installedCounts } from './hosts.mjs';
|
|
15
15
|
import * as lockfile from './lock.mjs';
|
|
16
16
|
import { dedupe, execute, isWritableState, preview, pruneDirs, revert } from './actions.mjs';
|
|
17
|
-
import { probeAll } from './
|
|
17
|
+
import { probeAll } from './extra-components.mjs';
|
|
18
18
|
|
|
19
19
|
const RUN = 'sw-ecosystem-agentic-harness';
|
|
20
20
|
|
|
@@ -29,12 +29,12 @@ export function buildContext(argv, command) {
|
|
|
29
29
|
let root = process.cwd();
|
|
30
30
|
let yes = false;
|
|
31
31
|
let projectWiki;
|
|
32
|
-
let
|
|
32
|
+
let extraComponents = true;
|
|
33
33
|
|
|
34
34
|
for (let i = 0; i < argv.length; i += 1) {
|
|
35
35
|
const arg = argv[i];
|
|
36
36
|
if (arg === '--yes' || arg === '-y') { yes = true; continue; }
|
|
37
|
-
if (arg === '--no-
|
|
37
|
+
if (arg === '--no-extra-components') { extraComponents = false; continue; }
|
|
38
38
|
if (arg === '--host') {
|
|
39
39
|
const value = argv[++i];
|
|
40
40
|
if (!HOSTS.includes(value)) {
|
|
@@ -64,7 +64,7 @@ export function buildContext(argv, command) {
|
|
|
64
64
|
if (!exists(root)) throw new UsageError(`--root does not exist: ${root}`, [`${RUN} ${command} --root .`]);
|
|
65
65
|
|
|
66
66
|
return {
|
|
67
|
-
root, scope, yes, projectWiki,
|
|
67
|
+
root, scope, yes, projectWiki, extraComponents,
|
|
68
68
|
hosts: hosts.length ? [...new Set(hosts)] : [...HOSTS],
|
|
69
69
|
platform: process.platform,
|
|
70
70
|
version: PKG_VERSION,
|
|
@@ -168,7 +168,7 @@ export async function status(argv) {
|
|
|
168
168
|
const worst = ['conflict', 'drift', 'not-installed', 'ok', 'absent']
|
|
169
169
|
.find((s) => hosts.some((h) => h.state === s)) ?? 'absent';
|
|
170
170
|
|
|
171
|
-
const
|
|
171
|
+
const extraComponents = ctx.extraComponents === false ? [] : probeAll({ platform: ctx.platform }).map((p) => ({
|
|
172
172
|
id: p.id, package: p.pkg, version: p.version, label: p.label,
|
|
173
173
|
available: p.available, probe: p.state, reason: p.reason ?? null,
|
|
174
174
|
installed_for: Object.entries(lock?.files ?? {})
|
|
@@ -180,10 +180,10 @@ export async function status(argv) {
|
|
|
180
180
|
let next = pending
|
|
181
181
|
? `Run \`${RUN} plan\` to see the ${pending} change(s) needed, then \`${RUN} apply --yes\`.`
|
|
182
182
|
: `Nothing to do — the harness is installed and up to date. Run \`${RUN} plan\` any time to re-check.`;
|
|
183
|
-
if (ctx.
|
|
184
|
-
const unavailable =
|
|
183
|
+
if (ctx.extraComponents !== false) {
|
|
184
|
+
const unavailable = extraComponents.filter((c) => !c.available);
|
|
185
185
|
if (unavailable.length) {
|
|
186
|
-
next += ` ${unavailable.length} optional
|
|
186
|
+
next += ` ${unavailable.length} optional extra component(s) are not on this machine — install with ${unavailable.map((c) => `\`${c.install}\``).join(', ')} to enable their skills.`;
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
@@ -193,7 +193,7 @@ export async function status(argv) {
|
|
|
193
193
|
next_step: next,
|
|
194
194
|
help: [`${RUN} plan`, `${RUN} apply --yes`, `${RUN} guide`],
|
|
195
195
|
inventory: { skills: skills().length, agents: agents().length },
|
|
196
|
-
hosts,
|
|
196
|
+
hosts, extra_components: extraComponents, drift,
|
|
197
197
|
declined: lock?.declined ?? [],
|
|
198
198
|
manual: lock?.manual ?? [],
|
|
199
199
|
errors,
|
|
@@ -245,17 +245,17 @@ const RESTART_HELP = {
|
|
|
245
245
|
cursor: 'Restart Cursor so it re-reads .cursor/mcp.json',
|
|
246
246
|
};
|
|
247
247
|
|
|
248
|
-
export async function apply(argv) {
|
|
248
|
+
export async function apply(argv, label = 'apply') {
|
|
249
249
|
const ctx = buildContext(argv, 'apply');
|
|
250
250
|
if (!ctx.yes) {
|
|
251
|
-
return out.usage(
|
|
251
|
+
return out.usage(label, 'apply refuses to write without --yes',
|
|
252
252
|
[`${RUN} plan`, `${RUN} apply --yes`]);
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
let lock;
|
|
256
256
|
try { lock = loadLock(ctx) ?? lockfile.empty(ctx.scope); } catch (e) {
|
|
257
257
|
if (e instanceof lockfile.LockVersionError) {
|
|
258
|
-
return out.failure(
|
|
258
|
+
return out.failure(label, e.message, {
|
|
259
259
|
next_step: `Upgrade the package: npm i -D ${PKG_NAME}@latest`,
|
|
260
260
|
help: [`npm i -D ${PKG_NAME}@latest`],
|
|
261
261
|
});
|
|
@@ -266,7 +266,7 @@ export async function apply(argv) {
|
|
|
266
266
|
// An apply we cannot record is an apply we cannot undo. Refuse up front
|
|
267
267
|
// rather than leave the user with files and no way back.
|
|
268
268
|
if (!isWritable(abs(ctx.root, LOCK_FILE))) {
|
|
269
|
-
return out.failure(
|
|
269
|
+
return out.failure(label, `cannot write the lock file at ${LOCK_FILE}`, {
|
|
270
270
|
next_step: `Make ${ctx.root} writable, then re-run \`${RUN} apply --yes\`.`,
|
|
271
271
|
help: [`${RUN} status`],
|
|
272
272
|
});
|
|
@@ -345,9 +345,12 @@ export async function apply(argv) {
|
|
|
345
345
|
summary, lock_file: LOCK_FILE, next_step: next, help,
|
|
346
346
|
hosts: [...perHost.values()], actions: results, manual, errors,
|
|
347
347
|
});
|
|
348
|
-
return summary.failed ? out.failure(
|
|
348
|
+
return summary.failed ? out.failure(label, `${summary.failed} change(s) failed`, body) : out.ok(label, body);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
/** `install` is `apply --yes`: typing the verb is the consent. */
|
|
352
|
+
export const install = (argv) => apply(['--yes', ...argv], 'install');
|
|
353
|
+
|
|
351
354
|
// --------------------------------------------------------------- uninstall
|
|
352
355
|
|
|
353
356
|
export async function uninstall(argv) {
|