@execuro-sw-ecosystem/sw-ecosystem-agentic-harness 0.1.11 → 0.1.13
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/README.md +18 -8
- package/agents/sw-product-manager.md +9 -3
- package/agents/sw-shopware-architect.md +5 -1
- package/agents/sw-tender-editor.md +34 -19
- package/bin/cli.mjs +10 -4
- package/codex/agents/sw-product-manager.toml +9 -3
- package/codex/agents/sw-shopware-architect.toml +6 -2
- package/codex/agents/sw-tender-editor.toml +34 -19
- package/content/AGENTS.md +3 -3
- package/copilot/agents/sw-product-manager.agent.md +9 -3
- package/copilot/agents/sw-shopware-architect.agent.md +6 -2
- package/copilot/agents/sw-tender-editor.agent.md +35 -20
- package/lib/commands.mjs +80 -18
- package/lib/guide.mjs +14 -6
- package/lib/settings.mjs +77 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/sw-discover-tender/SKILL.md +39 -43
- package/skills/sw-discover-tender/reference/agent-briefs.md +65 -110
- package/skills/sw-discover-tender/reference/analysis-template.md +41 -208
- package/skills/sw-discover-tender/reference/assumption-catalogue.md +69 -104
- package/skills/sw-discover-tender/reference/client-question-rules.md +16 -35
- package/skills/sw-discover-tender/reference/context-parameters.md +14 -107
- package/skills/sw-discover-tender/reference/coverage-mapping.md +33 -56
- package/skills/sw-discover-tender/reference/editor-session.md +32 -73
- package/skills/sw-discover-tender/reference/estimation-model.md +43 -79
- package/skills/sw-discover-tender/reference/procedure.md +36 -72
- package/skills/sw-discover-tender/reference/project-information.md +31 -0
- package/skills/sw-discover-tender/reference/response-rules.md +11 -10
- package/skills/sw-setup/SKILL.md +6 -7
- package/skills/sw-setup/reference/rows.md +27 -17
- package/skills/sw-discover-tender/reference/confidence-rubric.md +0 -80
- package/skills/sw-discover-tender/reference/editor-mode.md +0 -11
|
@@ -1,46 +1,61 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "sw-tender-editor"
|
|
3
|
-
description: "Mechanical file worker for the sw-discover-tender skill —
|
|
4
|
-
tools: [read, edit
|
|
3
|
+
description: "Mechanical file worker for the `sw-discover-tender` skill's two non-judgment jobs — reading ONE named file (the runtime's `import-digest.md`, or a client PDF) end to end and sorting every requirement row into the tool's own fixed tabs (Functional, Non-functional, Project & services) and the client's own topic, returning the extraction JSON for the skill to hand to the runtime's `intake`; and writing the working document's context prose — section 1's meta, section 3 Exclusions, section 6 Integrations, section 7 Glossary — strictly from the inputs it is given. Deterministic, no judgment — every value it writes or returns comes from a named input, never from memory or estimation; sorting a row into a tab is transcription-level classification, not a coverage or effort call. Not for Requirement Coverage, confidence, effort, Client Response drafting, or any section 4 scope-item row or section 2 Totals — those are `sw-product-manager`, `sw-shopware-architect` and the runtime's work; this agent only transcribes what the source already states."
|
|
4
|
+
tools: [read, edit]
|
|
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: d16ff0b3fd61e9170c0801c31adc5ef749feccdfc795f6dfa22650eed95f561c -->
|
|
10
10
|
# sw-tender-editor
|
|
11
11
|
|
|
12
12
|
## Identity
|
|
13
13
|
|
|
14
|
-
A mechanical editor spawned by `sw-discover-tender` for one
|
|
14
|
+
A mechanical editor spawned by `sw-discover-tender` for exactly one of two jobs at a time: `extract` (sort a digest's or a PDF's requirements into the tool's own tabs and topics, as JSON) or `context` (write the working document's context and framing sections from already-gathered inputs). It never researches, estimates, judges Requirement Coverage, or decides anything a reader would call an opinion.
|
|
15
15
|
|
|
16
16
|
## Rules
|
|
17
17
|
|
|
18
|
-
1. **
|
|
19
|
-
2. **
|
|
20
|
-
3. **
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
1. **One job per spawn.** `extract` returns JSON as its result; `context` writes named sections of the working document. Never both, never a third job.
|
|
19
|
+
2. **`extract` reads exactly one named tender file** — the runtime's `import-digest.md` (an xlsx or csv source) or the PDF itself — plus `reference/project-information.md` for the 18 `PROJECT_INFO` key definitions used by Rule 6. No Glob or Grep is available; a brief that names any other file is a defect, not an invitation to look further.
|
|
20
|
+
3. **The row's own content decides the tab; the topic is always the client's own grouping, unchanged.** `TABS = Functional, Non-functional, Project & services`.
|
|
21
|
+
- **Functional**: anything the shop, admin or an interface must do — including a feature required by law or compliance (hazard labelling, VAT-ID validation, newsletter double opt-in, a payment method), an integration, migration, or content.
|
|
22
|
+
- **Non-functional**: a quality attribute or operating constraint — performance, availability, scalability, security posture/access policy, accessibility conformance level, data-protection process, hosting, operations, monitoring, code quality.
|
|
23
|
+
- **Project & services**: how the project is delivered and supported — method, timeline, team, documentation, training, testing/acceptance, hypercare, warranty/maintenance, handover, references, commercial structure (scope and effort only, never a figure).
|
|
24
|
+
- A bidder questionnaire sheet (e.g. a "Company Overview" sheet asking the partner to describe org structure, headcount or references) is Project & services, its topic the client's own sheet/block name — answered like any other row.
|
|
25
|
+
- A client "Type" column is a hint, never the decider — the row's own content decides the tab.
|
|
26
|
+
- **topic**: the most specific grouping the client gives for that row — its own Area/Category column when the sheet has one, else the block heading, else the sheet name (PDF: the section heading); strip a leading chapter number ("1. ", "3.3 - ") and use the rest verbatim. Never invented, never paraphrased, never the tab name.
|
|
27
|
+
- This is transcription-level classification (which of three fixed buckets a row's own content already points to), never a Requirement Coverage or effort judgment.
|
|
28
|
+
4. **Confirm, correct, or add to the digest's proposal.** The digest names a proposed table (header row, data range, id/priority/requirement/topic/answer columns) as a starting point, not a fixed truth: correct a header row, a data range or a column the sheet's own cells contradict, and record every table actually used in `tables[]`. A sheet the digest called context whose cells are in fact a requirement or vendor-answer table gets its own `tables[]` entry the digest never proposed; a block within one sheet the digest's proposal did not separate out likewise gets its own entry, `topic` = that block's heading.
|
|
29
|
+
5. **`skip` for a non-requirement row inside a declared table.** A row inside a `tables[]` range that is a heading, a note, a total or otherwise not a requirement goes into `skip[]` with its sheet, row and why — never into the data range as if it were a requirement. A row outside every declared table's range is never listed, in `skip[]` or anywhere else.
|
|
30
|
+
6. **`projectInfo` for the 18 keys.** Read the tender's own context sheets or prose for each of the 18 `PROJECT_INFO` keys (`reference/project-information.md`); a key the source does not state is simply omitted (the runtime fills it as `not stated`) — never invented, never guessed. `source` may join several locations with "; " (`<sheet> r<row>; <sheet> r<row>`). State what the source actually gives, qualified when it is not exactly what the key asks (e.g. "up to 10 orders a day per buyer — no shop total stated"); never compute or invent a total the source itself does not state. **Amounts are kept verbatim** — a `value` may carry a figure the source states (EUR 380, €120k, 160,000 EUR); this is the client's or the operator's own fact, not a figure this agent invents, and the only place in this agent's output the money rule (Rule 9) does not apply.
|
|
31
|
+
7. **PDF: verbatim text, inferred priority, empty arrays elsewhere.** `tables`, `overrides` and `skip` are each returned as empty arrays — a PDF has no sheet to table. `items[]` reproduces each requirement's text and its section heading (`topic`) exactly, in the order they appear in the document — never paraphrased, merged, split or reordered. A descriptive paragraph that states no requirement is not an item — it may still feed `projectInfo`. `prio` is inferred from the prose: "must"/"shall"/"required" → Must, "should" → Should, "nice to have"/"could"/"optional" → Nice to have; empty only when the prose gives no such signal at all. Use the document's own word for that level, its first letter capitalised (Must, Should, Nice to have), dropping a trailing qualifier such as ", if time allows". A bullet list under a lead-in sentence that states the priority ("The following are required:") — every bullet inherits the lead-in's priority; the bullet's own text is still its own text verbatim, never the lead-in's wording.
|
|
32
|
+
8. **`context` writes only from stated inputs.** Meta, source files, the detected Shopware version/edition/plan, exclusions, integrations and glossary entries — every one is copied or summarised from a value the spawning brief hands it, never invented, never carried over from another tender. There is no Approach section: a platform, hosting, PSP or CMP recommendation lives in a scope item's Client Response, and the Shopware target lives in the Project information table, not written by this agent.
|
|
33
|
+
9. **Never a currency amount, a price, a rate, a budget figure, or a numeric compliance-legend rating**, in anything this agent writes or returns, except a `projectInfo` `value` (Rule 6) — the runtime writes the legend number at export.
|
|
34
|
+
10. **Never touch section 2 Totals, a section 4 Scope items row, the Project information table or the Not taken table.** Those are runtime-generated (`intake`, `check --write`) or agent-judged (`sw-shopware-architect`) respectively; this agent has no basis to write any of them.
|
|
35
|
+
11. **Every write leaves the working document valid and complete.** It is parsed live by a page watcher: after any `context` write, all 8 sections (`## 1. Context` … `## 8. Log`) must still be present and parseable — a partial or malformed write is worse than no write.
|
|
36
|
+
12. **One line back, nothing pasted.** `context` ends the turn with the path plus which sections were written; `extract` returns the JSON itself as its result, with no extra prose. Never paste a section's full content into the response when a summary line would do.
|
|
25
37
|
|
|
26
38
|
## Flow
|
|
27
39
|
|
|
28
|
-
1. Read the brief
|
|
29
|
-
2. Read only the
|
|
30
|
-
3.
|
|
31
|
-
4.
|
|
32
|
-
5. Return the one-line result. Stop.
|
|
40
|
+
1. Read the spawning brief to see which job it is (`extract` or `context`) and which file(s) it names.
|
|
41
|
+
2. Read only the file(s) the brief names — nothing else.
|
|
42
|
+
3. `extract`: read the digest or the PDF end to end; build `{ tables, overrides, skip, items, projectInfo }` per Rules 3-7 and the schema in `agent-briefs.md`; return it as the turn's result.
|
|
43
|
+
4. `context`: write the named sections (section 1 meta, section 3 Exclusions, section 6 Integrations, section 7 Glossary) into the working document by Write or Edit, using only the values the brief supplies; a value the brief does not supply is stated as not provided, never guessed.
|
|
44
|
+
5. Return the one-line result (`context`) or the JSON (`extract`). Stop.
|
|
33
45
|
|
|
34
46
|
## Never
|
|
35
47
|
|
|
36
|
-
- Never
|
|
37
|
-
- Never
|
|
48
|
+
- Never assess Requirement Coverage, confidence or effort, or draft a Client Response or Internal note — that is `sw-product-manager` and `sw-shopware-architect`'s work.
|
|
49
|
+
- Never write or edit section 2 Totals, a section 4 Scope items row, the Project information table, or the Not taken table.
|
|
50
|
+
- Never write an Approach section — none exists.
|
|
38
51
|
- Never run a shell command, fetch a URL, or call another agent — this agent has no Bash, WebFetch or Agent tool.
|
|
52
|
+
- Never open a file the spawning brief did not name.
|
|
39
53
|
|
|
40
54
|
## Report
|
|
41
55
|
|
|
42
|
-
|
|
56
|
+
`extract`: the extraction JSON, and nothing else, as the turn's result.
|
|
57
|
+
`context`: one line — the working document's path and which sections were written.
|
|
43
58
|
|
|
44
59
|
## Boundaries
|
|
45
60
|
|
|
46
|
-
Not for research, feasibility checks, effort estimation, drafting
|
|
61
|
+
Not for research, feasibility checks, effort estimation, drafting client-facing text, or any section 4/section 2/Project information content — that is `sw-product-manager`, `sw-shopware-architect` and the runtime's work. This agent only sorts a digest's or a PDF's requirements into the tool's own tabs and topics, and transcribes already-decided context values into the working document.
|
package/lib/commands.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import * as lockfile from './lock.mjs';
|
|
|
17
17
|
import { dedupe, execute, isWritableState, preview, pruneDirs, revert } from './actions.mjs';
|
|
18
18
|
import { removeDirIfEmpty, removeFile } from './fsx.mjs';
|
|
19
19
|
import { askAgents } from './prompt.mjs';
|
|
20
|
+
import * as settings from './settings.mjs';
|
|
20
21
|
import { RESTART } from './render.mjs';
|
|
21
22
|
|
|
22
23
|
const RUN = 'sw-ecosystem-agentic-harness';
|
|
@@ -86,33 +87,40 @@ export function buildContext(argv, command) {
|
|
|
86
87
|
/**
|
|
87
88
|
* The agent-resolution chain every write command shares:
|
|
88
89
|
* 1. `--agent` given → use exactly those. No question.
|
|
89
|
-
* 2. else
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
90
|
+
* 2. else agents are anchored (lib/settings.mjs) → use them. The choice was
|
|
91
|
+
* already made. No question.
|
|
92
|
+
* 3. else a lock already records agents → reuse them. A legacy install; a
|
|
93
|
+
* lock written before the rename records them under `hosts`, which
|
|
94
|
+
* lib/lock.mjs reads either way.
|
|
95
|
+
* 4. else a TTY is attached (apply/install only) → ask, via lib/prompt.mjs.
|
|
96
|
+
* 5. else → UsageError, exit 2, `help` lists the four runnable commands.
|
|
94
97
|
*
|
|
95
|
-
* `
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
+
* `source` says which step answered; apply anchors the answer of 1 and 4.
|
|
99
|
+
* `status` takes steps 1-2, then every agent; `uninstall` takes step 1, then
|
|
100
|
+
* every agent — reporting and removal are not consent points. `plan` writes
|
|
101
|
+
* nothing, so it never reaches step 4: steps 1-3, then it previews all four.
|
|
98
102
|
*
|
|
99
103
|
* Call after `loadLock(ctx)` so `ctx.lock` is populated for step 2.
|
|
100
104
|
*/
|
|
101
105
|
async function resolveAgents(ctx, command) {
|
|
102
|
-
if (ctx.agents.length) return { agents: [...new Set(ctx.agents)], chosen: true };
|
|
103
|
-
if (command === '
|
|
106
|
+
if (ctx.agents.length) return { agents: [...new Set(ctx.agents)], chosen: true, source: 'flag' };
|
|
107
|
+
if (command === 'uninstall') return { agents: [...AGENTS], chosen: true, source: 'all' };
|
|
108
|
+
|
|
109
|
+
const anchored = settings.read(ctx.root)?.agents ?? [];
|
|
110
|
+
if (anchored.length) return { agents: anchored, chosen: true, source: 'settings' };
|
|
111
|
+
if (command === 'status') return { agents: [...AGENTS], chosen: true, source: 'all' };
|
|
104
112
|
|
|
105
113
|
const recorded = ctx.lock?.agents ? Object.keys(ctx.lock.agents) : [];
|
|
106
|
-
if (recorded.length) return { agents: recorded, chosen: true };
|
|
114
|
+
if (recorded.length) return { agents: recorded, chosen: true, source: 'lock' };
|
|
107
115
|
|
|
108
|
-
if (command === 'plan') return { agents: [...AGENTS], chosen: false };
|
|
116
|
+
if (command === 'plan') return { agents: [...AGENTS], chosen: false, source: 'all' };
|
|
109
117
|
|
|
110
118
|
if (process.stdin.isTTY) {
|
|
111
119
|
const chosen = await askAgents(AGENTS);
|
|
112
|
-
if (chosen) return { agents: chosen, chosen: true };
|
|
120
|
+
if (chosen) return { agents: chosen, chosen: true, source: 'picker' };
|
|
113
121
|
}
|
|
114
122
|
throw new UsageError(
|
|
115
|
-
'no --agent given, this repository has no recorded install, and there is no terminal to ask which coding agent(s) to use',
|
|
123
|
+
'no --agent given, no anchored target agents, this repository has no recorded install, and there is no terminal to ask which coding agent(s) to use',
|
|
116
124
|
AGENTS.map((a) => `${RUN} ${command} --agent ${a}`),
|
|
117
125
|
);
|
|
118
126
|
}
|
|
@@ -162,6 +170,39 @@ function publicAction(a) {
|
|
|
162
170
|
return rest;
|
|
163
171
|
}
|
|
164
172
|
|
|
173
|
+
/** The anchored targets as every result reports them. */
|
|
174
|
+
function targetsOf(root, anchored) {
|
|
175
|
+
return {
|
|
176
|
+
agents: anchored?.agents ?? [],
|
|
177
|
+
source: anchored ? 'settings' : 'none',
|
|
178
|
+
settings_file: settings.settingsFile(root),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ------------------------------------------------------------------ agents
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Read or anchor the target agents. Detection only yields `candidates`; the
|
|
186
|
+
* write touches the settings file and nothing else.
|
|
187
|
+
*/
|
|
188
|
+
export async function agentsCommand(argv) {
|
|
189
|
+
const ctx = buildContext(argv, 'agents');
|
|
190
|
+
const candidates = (await detectAll(ctx)).filter((d) => d.detected).map((d) => d.id);
|
|
191
|
+
let anchored = settings.read(ctx.root);
|
|
192
|
+
if (ctx.agents.length) {
|
|
193
|
+
const same = ctx.agents.length === candidates.length && ctx.agents.every((a) => candidates.includes(a));
|
|
194
|
+
anchored = settings.write(ctx.root, ctx.agents, same ? 'detected' : 'chosen');
|
|
195
|
+
}
|
|
196
|
+
return out.ok('agents', head(ctx, {
|
|
197
|
+
next_step: anchored
|
|
198
|
+
? `Target agents: ${anchored.agents.join(', ')}. Run \`${RUN} status\` to check them.`
|
|
199
|
+
: `Nothing is anchored — run \`${RUN} agents --agent <id>\` (candidates: ${candidates.join(', ') || 'none'}).`,
|
|
200
|
+
help: [`${RUN} status`, ...AGENTS.map((a) => `${RUN} agents --agent ${a}`)],
|
|
201
|
+
targets: targetsOf(ctx.root, anchored),
|
|
202
|
+
candidates,
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
|
|
165
206
|
// ------------------------------------------------------------------ status
|
|
166
207
|
|
|
167
208
|
export async function status(argv) {
|
|
@@ -176,7 +217,11 @@ export async function status(argv) {
|
|
|
176
217
|
}
|
|
177
218
|
throw e;
|
|
178
219
|
}
|
|
179
|
-
|
|
220
|
+
const resolved = await resolveAgents(ctx, 'status');
|
|
221
|
+
ctx.agents = resolved.agents;
|
|
222
|
+
// Untargeted only means something once a choice exists (`--agent` or anchored).
|
|
223
|
+
const narrowed = resolved.source === 'flag' || resolved.source === 'settings';
|
|
224
|
+
const anchored = settings.read(ctx.root);
|
|
180
225
|
|
|
181
226
|
const detections = await detectAll(ctx);
|
|
182
227
|
const { actions, errors } = previewAll(ctx);
|
|
@@ -184,8 +229,10 @@ export async function status(argv) {
|
|
|
184
229
|
|
|
185
230
|
const agentStates = detections.map((d) => {
|
|
186
231
|
const mine = actions.filter((a) => a.host === d.id || a.host === 'shared');
|
|
232
|
+
const targeted = !narrowed || ctx.agents.includes(d.id);
|
|
187
233
|
let state = 'absent';
|
|
188
|
-
if (
|
|
234
|
+
if (!targeted) state = 'not-targeted';
|
|
235
|
+
else if (d.detected) {
|
|
189
236
|
const pending = mine.filter((a) => isWritableState(a.state));
|
|
190
237
|
const bad = mine.filter((a) => a.state === 'conflict' || a.state === 'drift');
|
|
191
238
|
if (bad.length) state = bad.some((a) => a.state === 'conflict') ? 'conflict' : 'drift';
|
|
@@ -193,7 +240,7 @@ export async function status(argv) {
|
|
|
193
240
|
else state = lock?.agents?.[d.id] ? 'drift' : 'not-installed';
|
|
194
241
|
}
|
|
195
242
|
return {
|
|
196
|
-
id: d.id, detected: d.detected, marker: d.marker, writable: d.writable,
|
|
243
|
+
id: d.id, detected: d.detected, targeted, marker: d.marker, writable: d.writable,
|
|
197
244
|
skills_dir: skillsDir(d.id, ctx),
|
|
198
245
|
installed: installedCounts(d.id, ctx), state, notes: d.notes,
|
|
199
246
|
};
|
|
@@ -209,15 +256,22 @@ export async function status(argv) {
|
|
|
209
256
|
const pending = actions.filter((a) => isWritableState(a.state)).length;
|
|
210
257
|
const worst = ['conflict', 'drift', 'not-installed', 'ok', 'absent']
|
|
211
258
|
.find((s) => agentStates.some((a) => a.state === s)) ?? 'absent';
|
|
259
|
+
const candidates = detections.filter((d) => d.detected).map((d) => d.id);
|
|
212
260
|
|
|
213
261
|
let next = pending
|
|
214
262
|
? `Run \`${RUN} plan\` to see the ${pending} change(s) needed, then \`${RUN} apply --yes\`.`
|
|
215
263
|
: `Nothing to do — the harness is installed and up to date. Run \`${RUN} plan\` any time to re-check.`;
|
|
264
|
+
if (!anchored) {
|
|
265
|
+
next = `No target agents are anchored yet — run \`${RUN} agents --agent <id>\` (candidates: ${candidates.join(', ') || 'none'}). ${next}`;
|
|
266
|
+
}
|
|
216
267
|
|
|
217
268
|
return out.ok('status', head(ctx, {
|
|
218
269
|
installed_version: lock?.version ?? null,
|
|
219
270
|
state: worst,
|
|
220
271
|
next_step: next,
|
|
272
|
+
targets: targetsOf(ctx.root, anchored),
|
|
273
|
+
candidates,
|
|
274
|
+
lock_file: lockFile(ctx.root),
|
|
221
275
|
help: [`${RUN} plan`, `${RUN} apply --yes`, `${RUN} guide`],
|
|
222
276
|
inventory: { skills: skills().length, agents: agents().length },
|
|
223
277
|
agents: agentStates, drift,
|
|
@@ -281,7 +335,8 @@ export async function apply(argv, label = 'apply') {
|
|
|
281
335
|
}
|
|
282
336
|
throw e;
|
|
283
337
|
}
|
|
284
|
-
|
|
338
|
+
const resolved = await resolveAgents(ctx, label);
|
|
339
|
+
ctx.agents = resolved.agents;
|
|
285
340
|
|
|
286
341
|
// An apply we cannot record is an apply we cannot undo. Refuse up front
|
|
287
342
|
// rather than leave the user with files and no way back.
|
|
@@ -359,6 +414,11 @@ export async function apply(argv, label = 'apply') {
|
|
|
359
414
|
lock = { ...lock, agents: { ...lock.agents, [agent]: { installed_at: new Date().toISOString(), version: PKG_VERSION } } };
|
|
360
415
|
}
|
|
361
416
|
lockfile.write(ctx.root, lock);
|
|
417
|
+
settings.migrate(ctx.root);
|
|
418
|
+
// The first explicit choice becomes the project's target agents.
|
|
419
|
+
if ((resolved.source === 'flag' && !settings.read(ctx.root)) || resolved.source === 'picker') {
|
|
420
|
+
settings.write(ctx.root, ctx.agents, 'chosen');
|
|
421
|
+
}
|
|
362
422
|
|
|
363
423
|
const summary = {
|
|
364
424
|
created: results.filter((r) => r.state === 'created').length,
|
|
@@ -411,6 +471,7 @@ export async function uninstall(argv) {
|
|
|
411
471
|
({ agents: ctx.agents } = await resolveAgents(ctx, 'uninstall'));
|
|
412
472
|
|
|
413
473
|
if (!lock) {
|
|
474
|
+
settings.prune(ctx.root, ctx.agents);
|
|
414
475
|
// No record means no evidence of what we own. Guessing here would delete
|
|
415
476
|
// someone else's files, so it does nothing and says so.
|
|
416
477
|
return out.ok('uninstall', head(ctx, {
|
|
@@ -447,6 +508,7 @@ export async function uninstall(argv) {
|
|
|
447
508
|
|
|
448
509
|
const prunedDirs = pruneDirs(lock.dirs ?? [], ctx);
|
|
449
510
|
const failed = errors.length;
|
|
511
|
+
if (!failed) settings.prune(ctx.root, [...selected]);
|
|
450
512
|
|
|
451
513
|
if (failed === 0 && survivors.length === 0 && selected.size === AGENTS.length) {
|
|
452
514
|
// Everything we recorded is gone; the lock has nothing left to describe.
|
package/lib/guide.mjs
CHANGED
|
@@ -21,26 +21,34 @@ registers the MCP servers and permission rules they depend on.
|
|
|
21
21
|
The whole install in one command. It never prompts for consent — typing
|
|
22
22
|
the verb is the consent — but it does still need to know **which** coding
|
|
23
23
|
agent(s) to write into, resolved in order: \`--agent\`, if given; else the
|
|
24
|
-
|
|
24
|
+
anchored target agents (step 2); else the agents a lock file already
|
|
25
|
+
records, if this is a re-run; else, with a real
|
|
25
26
|
terminal attached, an interactive picker; else it exits 2 with the four
|
|
26
27
|
\`${RUN} install --agent <a>\` commands as \`help\` rather than guess or
|
|
27
28
|
hang — Codex's question tool does not block outside Plan mode, and CI has
|
|
28
29
|
no terminal, so a skill invoking this must always pass \`--agent\`.
|
|
29
30
|
|
|
30
|
-
2. \`${RUN}
|
|
31
|
-
|
|
31
|
+
2. \`${RUN} agents [--agent <a> ...]\`
|
|
32
|
+
Reads, or with \`--agent\` anchors, the coding agents this project
|
|
33
|
+
targets, in \`sw-ecosystem-agentic-harness.json\` next to the lock file.
|
|
34
|
+
Marker directories only produce \`candidates\`; an explicit first
|
|
35
|
+
\`--agent\` choice on \`install\` is anchored too. Once anchored, \`status\`
|
|
36
|
+
judges only those agents and \`install\` needs no \`--agent\`.
|
|
37
|
+
|
|
38
|
+
3. \`${RUN} status\`
|
|
39
|
+
Read-only, optional. Reports the target agents, what is already
|
|
32
40
|
installed, and anything that has drifted since it was installed. Always safe.
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
4. \`${RUN} plan\`
|
|
35
43
|
Prints the exact set of changes and writes nothing. Every entry says what it
|
|
36
44
|
would do and why. Show this to the user before applying.
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
5. \`${RUN} apply --yes\`
|
|
39
47
|
The scriptable name for the same write \`install\` performs — use it for
|
|
40
48
|
re-runs and repairs. It refuses without \`--yes\`, and resolves which
|
|
41
49
|
agent(s) to touch the same way \`install\` does (see step 1).
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
6. \`${RUN} uninstall --yes\`
|
|
44
52
|
Removes what the lock file records and reverts the managed config blocks.
|
|
45
53
|
|
|
46
54
|
## Flags
|
package/lib/settings.mjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// The anchored target agents: `sw-ecosystem-agentic-harness.json`, next to the lock (see
|
|
2
|
+
// `lockDir()` in content.mjs).
|
|
3
|
+
//
|
|
4
|
+
// The lock records what was *installed*; this records what the project
|
|
5
|
+
// *targets*. Marker directories only ever produce candidates — a stray empty
|
|
6
|
+
// `.vscode/` must not make Copilot a target — so every command that has to
|
|
7
|
+
// pick agents reads this file first. Only the CLI writes it, per checkout: it
|
|
8
|
+
// lives in the ignored state directory, so each machine anchors once.
|
|
9
|
+
|
|
10
|
+
import { dirname } from 'node:path';
|
|
11
|
+
import { LOCK_DIR, lockDir } from './content.mjs';
|
|
12
|
+
import {
|
|
13
|
+
abs, ensureDir, exists, readTextMaybe, removeDirIfEmpty, removeFile, writeAtomic,
|
|
14
|
+
} from './fsx.mjs';
|
|
15
|
+
import { AGENTS } from './hosts.mjs';
|
|
16
|
+
|
|
17
|
+
const NAME = 'sw-ecosystem-agentic-harness.json';
|
|
18
|
+
|
|
19
|
+
export function settingsFile(root) {
|
|
20
|
+
return `${lockDir(root)}/${NAME}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The pre-`var/` location; read only so an anchored project survives the move. */
|
|
24
|
+
function legacyPath(root) {
|
|
25
|
+
return abs(root, `${LOCK_DIR}/${NAME}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The anchored agents, or null when nothing is anchored (or the file is unusable). */
|
|
29
|
+
export function read(root) {
|
|
30
|
+
const found = readTextMaybe(abs(root, settingsFile(root))) ?? readTextMaybe(legacyPath(root));
|
|
31
|
+
if (found === null) return null;
|
|
32
|
+
let parsed;
|
|
33
|
+
try { parsed = JSON.parse(found.text); } catch { return null; }
|
|
34
|
+
const agents = (Array.isArray(parsed?.agents) ? parsed.agents : []).filter((a) => AGENTS.includes(a));
|
|
35
|
+
return agents.length ? { ...parsed, agents: [...new Set(agents)] } : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function write(root, agents, source) {
|
|
39
|
+
const settings = {
|
|
40
|
+
schema: 1, agents: [...new Set(agents)], anchored_at: new Date().toISOString(), source,
|
|
41
|
+
};
|
|
42
|
+
const target = abs(root, settingsFile(root));
|
|
43
|
+
ensureDir(dirname(target));
|
|
44
|
+
writeAtomic(target, `${JSON.stringify(settings, null, 2)}\n`);
|
|
45
|
+
dropLegacy(root, target);
|
|
46
|
+
return settings;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function dropLegacy(root, target) {
|
|
50
|
+
const legacy = legacyPath(root);
|
|
51
|
+
if (legacy !== target && exists(legacy)) {
|
|
52
|
+
removeFile(legacy);
|
|
53
|
+
removeDirIfEmpty(dirname(legacy));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Move a settings file left at the old path to the current one. */
|
|
58
|
+
export function migrate(root) {
|
|
59
|
+
const current = read(root);
|
|
60
|
+
if (current && legacyPath(root) !== abs(root, settingsFile(root)) && exists(legacyPath(root))) write(root, current.agents, current.source ?? 'chosen');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Drop agents from the targets; the file goes when none are left. */
|
|
64
|
+
export function prune(root, removed) {
|
|
65
|
+
const current = read(root);
|
|
66
|
+
if (!current) return;
|
|
67
|
+
const left = current.agents.filter((a) => !removed.includes(a));
|
|
68
|
+
if (left.length === current.agents.length) return;
|
|
69
|
+
if (left.length) {
|
|
70
|
+
write(root, left, current.source ?? 'chosen');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const target = abs(root, settingsFile(root));
|
|
74
|
+
removeFile(target);
|
|
75
|
+
removeDirIfEmpty(dirname(target));
|
|
76
|
+
dropLegacy(root, target);
|
|
77
|
+
}
|
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.13",
|
|
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.13",
|
|
5
5
|
"description": "Shopware 6 agentic harness: sw-* skills and sub-agents for requirements, specs, implementation, verification and documentation, with an installer CLI that writes them into Claude Code, Codex, GitHub Copilot and Cursor. The knowledge-base MCP and the editor tools are separate npm packages.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Execuro UG (haftungsbeschränkt)",
|
|
@@ -1,68 +1,64 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sw-discover-tender
|
|
3
|
-
description:
|
|
4
|
-
when_to_use: Trigger phrases — "scope this RFP", "analyse the tender", "answer the RFI", "discover RFP 0001", "continue RFP 0001", "
|
|
5
|
-
argument-hint: '[tender file
|
|
3
|
+
description: Turn a client's RFI/RFP/RFQ or project plan into confirmed Shopware scope in one working document, `specs/rfp-NNNN-slug-analysis.md`. An agent extracts every requirement into the tool's own three fixed tabs — Functional, Non-functional, Project & services — and 18-row Project information table; intake confirms itself and analysis starts automatically, corrected any time via row notes (move, skip, restore); each scope item gets a Requirement Coverage (OOTB, Configuration, Extension, ISV, Custom, or `—`), a confidence, a T-shirt-size or partner-profile effort figure, assumptions the operator accepts, and a Client Response — without an engineer. Runs on the runtime's own CLI and page (`--editor`); confirm, answer and export happen anytime, and the document reaches Ready when every scope item is confirmed. Exports as XLSX only — a surgical fit-back into the client's own workbook, or the tool's own multi-tab workbook for a CSV or PDF source. Scope and effort only — never costs, prices, licence fees or budgets. Never designs the solution — that's sw-design-solution.
|
|
4
|
+
when_to_use: Trigger phrases — "scope this RFP", "analyse the tender", "answer the RFI", "discover RFP 0001", "continue RFP 0001", "export the response for RFP 0001", "is RFP 0001 ready", "open RFP 0001 in the browser", "open the tender tool", "sw-discover-tender specs/rfp-0001-x-analysis.md --editor".
|
|
5
|
+
argument-hint: '[tender file (.xlsx|.csv|.pdf) | specs/rfp-NNNN-slug-analysis.md to continue] [--editor] [--export]'
|
|
6
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
|
|
10
10
|
|
|
11
|
-
One
|
|
11
|
+
One working document per tender: Requirement Coverage, confidence, effort, assumptions and a Client Response per scope item. The operator confirms; the next run reconciles. Export to XLSX anytime.
|
|
12
|
+
|
|
13
|
+
Requires the runtime at `sw-tender-discovery-tool` >= 0.2.0 — the working-document grammar below is a 0.2.0 contract; `sw-setup` keeps it current with `@latest`, so a project on an older runtime should re-run `sw-setup` first.
|
|
12
14
|
|
|
13
15
|
## Boundaries
|
|
14
16
|
|
|
15
|
-
-
|
|
16
|
-
- Never edit the client's file.
|
|
17
|
-
-
|
|
18
|
-
- A vague
|
|
19
|
-
-
|
|
20
|
-
-
|
|
17
|
+
- Scope and effort only. The tool never produces a figure of its own — no cost, price, rate, licence, hosting or PSP fee, no budget — `reference/estimation-model.md`'s money rule. A client's or the operator's own figure in Project information is kept verbatim; a coverage choice with a cost consequence gets a one-line `cost:` reference, no amount. Elsewhere a client-stated budget is never recorded, not even as quoted context.
|
|
18
|
+
- Never edit the client's file. A hidden sheet or one whose name starts with `_` is never read and never mentioned anywhere — not in section 1, not in Not taken, not in the digest.
|
|
19
|
+
- Assumptions are proposed by agents, accepted or rejected only by the operator. A rejected proposal is never proposed again.
|
|
20
|
+
- A requirement too vague to assess gets a client question (`CQ-n`), never a silent guess.
|
|
21
|
+
- No QA agent anywhere in the flow. Three agents only: `sw-tender-editor`, `sw-product-manager`, `sw-shopware-architect`.
|
|
22
|
+
- XLSX out only, via the runtime's own writer: a surgical fit-back into the client's own workbook, or a multi-tab working-sheet export for csv/pdf. The client's original is never touched, nothing is sent. No per-requirement CSV of any kind is written.
|
|
21
23
|
|
|
22
24
|
## Inputs
|
|
23
25
|
|
|
24
26
|
| Argument | Behaviour |
|
|
25
27
|
| --- | --- |
|
|
26
|
-
| `.
|
|
27
|
-
| `.
|
|
28
|
-
| `.docx` | Stop. One line: export
|
|
29
|
-
| `*-analysis.md` |
|
|
30
|
-
| `--export`, or "export the response for RFP NNNN" | Continue, then
|
|
31
|
-
| `--editor` | Hand off to `reference/editor-session.md`
|
|
32
|
-
| `--editor-session <url> --batch-file <file>` / `--
|
|
33
|
-
|
|
|
34
|
-
| nothing | Ask for the path in one line and end |
|
|
28
|
+
| `.xlsx` / `.csv` | Import first (`reference/procedure.md` step 1): `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest import --source <file>` writes `import-digest.md` (install gate: `reference/editor-session.md` §0). `sw-tender-editor` runs its `extract` job on the digest and returns the extraction JSON; the skill saves it and runs `intake <doc> --source <file> --extraction <json>`. Every source runs the same extract-then-intake flow — xlsx, csv and pdf alike. A refusal (encrypted, `.xls`, `.xlsb`) is relayed verbatim and the run stops. |
|
|
29
|
+
| `.pdf` | New tender: `sw-tender-editor` runs its `extract` job on the PDF directly; the skill saves the JSON and runs `intake <doc> --source <pdf> --extraction <json>`. |
|
|
30
|
+
| `.docx` or another format | Stop. One line: export the requirement table as CSV/xlsx or the prose as PDF, and rerun with that file. |
|
|
31
|
+
| `*-analysis.md` | Intake already confirmed itself and queued analysis: work the chunks until none are left (`reference/procedure.md` step 2), then `check --write`, `report`. An old document still at `intake: review` needs `intake-confirm` first, on the operator's go; no separate Analyze step ever exists. |
|
|
32
|
+
| `--export`, or "export the response for RFP NNNN" | Continue, then `export`. |
|
|
33
|
+
| `--editor` | Hand off to `reference/editor-session.md` as the very first action and stop when it returns; nothing is read or assessed before the page is open. |
|
|
34
|
+
| `--editor-session <url> --batch-file <file>` / `--intake` | Editor mode, spawned by the editor session (`reference/editor-session.md`). |
|
|
35
|
+
| nothing | Ask for the path in one line and end. |
|
|
35
36
|
|
|
36
37
|
## Files
|
|
37
38
|
|
|
38
|
-
- **
|
|
39
|
-
- **
|
|
40
|
-
- **Response** `<source basename>-response
|
|
41
|
-
- **Work** `specs/.rfp/<slug>/`: `
|
|
42
|
-
- **Editor session** `specs/.editor/<slug
|
|
43
|
-
- **
|
|
44
|
-
|
|
45
|
-
## Procedure
|
|
46
|
-
|
|
47
|
-
**0. Mode.** `--editor` → `reference/editor-session.md`, per the Inputs row above, and stop there. `--editor-session` → work through `reference/procedure.md` under `reference/editor-session.md`'s *Editor mode* rules.
|
|
39
|
+
- **Working document** `specs/rfp-NNNN-slug-analysis.md`, next to the source. Committed. Grammar: `reference/analysis-template.md` — 8 fixed sections, section 4 header, cell lists, frontmatter.
|
|
40
|
+
- **Fit-back map** (xlsx sources only) `<srcdir>/<base>/import-map.json`, written by `intake`. Committed. No per-table CSVs are written.
|
|
41
|
+
- **Response** `<source basename>-response-v<n>.xlsx` (never overwritten). Committed, generated, never hand-edited.
|
|
42
|
+
- **Work** `specs/.rfp/<slug>/`: `extraction.json` (the `extract` job's result), `proposals.json`, `reestimate.json`, `last-run.json`, `reports/*.json` (saved architect reports before `apply`). Gitignored.
|
|
43
|
+
- **Editor session** `specs/.editor/<slug>/`: `import-snapshot.json`, `import-map.proposed.json`, `import-digest.md`, written by the Tender Discovery Tool. Gitignored.
|
|
44
|
+
- **Partner profile** `specs/rfp-partner-profile.md`. Missing: the page offers the wizard (skippable); without it the document runs in T-shirt regime (`reference/estimation-model.md`).
|
|
48
45
|
|
|
49
46
|
## Reference files
|
|
50
47
|
|
|
51
|
-
All under `reference/` in this skill's directory
|
|
48
|
+
All under `reference/` in this skill's directory:
|
|
52
49
|
|
|
53
50
|
| File | When | What it decides |
|
|
54
51
|
| --- | --- | --- |
|
|
55
|
-
| `procedure.md` | every run | steps
|
|
56
|
-
| `agent-briefs.md` |
|
|
57
|
-
| `editor-session.md` | `--editor` / `--editor-session` in the arguments | the session loop
|
|
58
|
-
| `analysis-template.md` | writing or reading the
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `response-rules.md` | step
|
|
66
|
-
| `editor-mode.md` | harness maintenance only | pending harness edits for this skill; not part of a run |
|
|
52
|
+
| `procedure.md` | every run | the seven steps: intake (extract + intake), assess, propose, estimate, confirm, answer, export; the three agents, applying a report, the run close |
|
|
53
|
+
| `agent-briefs.md` | steps 2-4 | one brief per agent, its tools, its delivery |
|
|
54
|
+
| `editor-session.md` | `--editor` / `--editor-session` in the arguments | the session loop, batch kinds, the subagent brief, close report |
|
|
55
|
+
| `analysis-template.md` | writing or reading the working document | frontmatter, the 8 sections, section 4 grammar, section 5 blocks |
|
|
56
|
+
| `project-information.md` | step 1, section 1 | the 18 Project information keys, what each captures, which estimate it informs |
|
|
57
|
+
| `context-parameters.md` | steps 1-2, section 1 | version/edition/plan detection from the Dev Knowledge Base only, what every agent receives |
|
|
58
|
+
| `coverage-mapping.md` | step 2 | the six Requirement Coverage values, the client-token map, ISV research |
|
|
59
|
+
| `estimation-model.md` | steps 3-4 | the two regimes, default scale, size-up drivers, not-decomposed rule, totals, money rule, the profile file |
|
|
60
|
+
| `client-question-rules.md` | step 3, step 6 | when a scope item gets a `CQ-n`, wording, answering |
|
|
61
|
+
| `assumption-catalogue.md` | step 3 (architect brief) | reusable assumption statements by key, with the effort each saves |
|
|
62
|
+
| `response-rules.md` | step 7 (export) | X-1…X-7, what the runtime's `export` writes |
|
|
67
63
|
|
|
68
|
-
|
|
64
|
+
The skill ships no executable of its own: the runtime's `import`, `intake`, `intake-confirm`, `move`, `skip`, `restore`, `info`, `apply`, `confirm`/`accept`/`reject`/`assume`/`unassume`/`answer`/`patch`/`profile`, `check`, `report` and `export` commands read and write the working document and the workbook.
|