@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.
Files changed (34) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +18 -8
  3. package/agents/sw-product-manager.md +9 -3
  4. package/agents/sw-shopware-architect.md +5 -1
  5. package/agents/sw-tender-editor.md +34 -19
  6. package/bin/cli.mjs +10 -4
  7. package/codex/agents/sw-product-manager.toml +9 -3
  8. package/codex/agents/sw-shopware-architect.toml +6 -2
  9. package/codex/agents/sw-tender-editor.toml +34 -19
  10. package/content/AGENTS.md +3 -3
  11. package/copilot/agents/sw-product-manager.agent.md +9 -3
  12. package/copilot/agents/sw-shopware-architect.agent.md +6 -2
  13. package/copilot/agents/sw-tender-editor.agent.md +35 -20
  14. package/lib/commands.mjs +80 -18
  15. package/lib/guide.mjs +14 -6
  16. package/lib/settings.mjs +77 -0
  17. package/package.json +1 -1
  18. package/plugin.json +1 -1
  19. package/skills/sw-discover-tender/SKILL.md +39 -43
  20. package/skills/sw-discover-tender/reference/agent-briefs.md +65 -110
  21. package/skills/sw-discover-tender/reference/analysis-template.md +41 -208
  22. package/skills/sw-discover-tender/reference/assumption-catalogue.md +69 -104
  23. package/skills/sw-discover-tender/reference/client-question-rules.md +16 -35
  24. package/skills/sw-discover-tender/reference/context-parameters.md +14 -107
  25. package/skills/sw-discover-tender/reference/coverage-mapping.md +33 -56
  26. package/skills/sw-discover-tender/reference/editor-session.md +32 -73
  27. package/skills/sw-discover-tender/reference/estimation-model.md +43 -79
  28. package/skills/sw-discover-tender/reference/procedure.md +36 -72
  29. package/skills/sw-discover-tender/reference/project-information.md +31 -0
  30. package/skills/sw-discover-tender/reference/response-rules.md +11 -10
  31. package/skills/sw-setup/SKILL.md +6 -7
  32. package/skills/sw-setup/reference/rows.md +27 -17
  33. package/skills/sw-discover-tender/reference/confidence-rubric.md +0 -80
  34. 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 — 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
- tools: [read, edit, search]
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: 288b5d684f40ea62aa2b6021cfa3109a7f664fc2365f32109bcd9d1f94eb8a2c -->
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 bulk file job at a time: extract, rows, splice or assemble, per the brief in `reference/agent-briefs.md` of the `sw-discover-tender` skill (`§EXTRACT`, `§ROWS`, `§SPLICE`, `§ASSEMBLE`). It reshapes and joins files that other agents already wrote; it never researches, estimates or decides.
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. **Write only the files named in the brief.** Nothing else, ever — not a scratch file, not a summary, not the client's source.
19
- 2. **Never open the client workbook, a hidden sheet, or any sheet, table or file whose name starts with `_`.** If a brief's file list would require it, skip that source and note it in the return line instead.
20
- 3. **Never invent a figure, a compliance token or a requirement row.** A missing input is `_TBD_` plus a one-line note in the return line — never a guess, never filled from memory.
21
- 4. **PD only.** Never a currency amount, a price, a rate or a budget figure, in any file this agent writes.
22
- 5. **Never edit the client's source file.** Source exports and the original workbook are read-only inputs.
23
- 6. **Every write leaves its target valid and complete for its format.** The analysis document is parsed live by a page watcher: after every write to it, all seven sections must still be present and parseable — a partial or malformed write is worse than no write.
24
- 7. **One line back, nothing pasted.** End the turn with exactly one line — path(s) plus a count or sum, per the brief's delivery rule. Never paste the file's content into the response.
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 section named in the spawn message (`§EXTRACT`, `§ROWS`, `§SPLICE` or `§ASSEMBLE`) in `agent-briefs.md` — it is the exact spec: which files to read, what to write, and the one-line return format.
29
- 2. Read only the files that brief names. Nothing else, no exploring the rest of `specs/.rfp/<slug>/`.
30
- 3. Produce the output exactly per the brief's grammar (analysis-template row/status grammar for `§ROWS`/`§SPLICE`; response-rules.md for `§ASSEMBLE`).
31
- 4. Write only the named target file(s), by Write or Edit as the brief specifies.
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 do the PM's, architect's or QA's job: no verdicts, no verification, no estimates, no mechanism judgment. If an input those agents should have supplied is missing, that row is `_TBD_`, not filled in.
37
- - Never touch a row, section, frozen line, human `[x]`/`[-]` tick, or `partner`-marked line outside what the brief names.
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
- Exactly one line, as specified by the brief that spawned this agent — typically a path (or paths) plus a row count or PD sum, or the single failing check when a check fails.
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 questions, or writing the analysis prose in §1/§2/§5/§6/§7 — those are `sw-product-manager`, `sw-shopware-architect`, `sw-qa-engineer` and the orchestrating skill's work. This agent only moves already-produced content between files.
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 a lock already records agents → reuse them. This is an update;
90
- * the choice was already made. No question. A lock written before the
91
- * rename records them under `hosts`; lib/lock.mjs reads either.
92
- * 3. else a TTY is attached (apply/install only) → ask, via lib/prompt.mjs.
93
- * 4. else → UsageError, exit 2, `help` lists the four runnable commands.
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
- * `status` and `uninstall` keep their old, ungated default of every agent —
96
- * reporting and removal are not consent points. `plan` writes nothing, so it
97
- * never reaches step 3: steps 1-2, then it previews all four and says so.
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 === 'status' || command === 'uninstall') return { agents: [...AGENTS], chosen: true };
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
- ({ agents: ctx.agents } = await resolveAgents(ctx, 'status'));
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 (d.detected) {
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
- ({ agents: ctx.agents } = await resolveAgents(ctx, label));
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
- agents a lock file already records, if this is a re-run; else, with a real
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} status\`
31
- Read-only, optional. Reports which agents are present, what is already
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
- 3. \`${RUN} plan\`
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
- 4. \`${RUN} apply --yes\`
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
- 5. \`${RUN} uninstall --yes\`
51
+ 6. \`${RUN} uninstall --yes\`
44
52
  Removes what the lock file records and reverts the managed config blocks.
45
53
 
46
54
  ## Flags
@@ -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.11",
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.11",
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: Analyse a client tender (RFP, RFI or RFQ) for a Shopware 6 project into one markdown sidecar, <source basename>-analysis.md. Every requirement row gets a class against the installed Shopware version (stock / config / plugin / custom), a three-point person-day estimate, one line of mechanism and proposed scope-lock assumptions the partner accepts by ticking checkboxes — in the file or on the Tender Discovery Tool page (`--editor`); the next run reconciles the ticks, dissolves answered questions and re-estimates. At Ready to submit it exports one CSV per client table with compliance, comment and effort filled in the client's columns. Scope and PD only — never costs, prices, licence fees or budgets. Never designs the solution — that's sw-design-solution.
4
- when_to_use: Trigger phrases — "scope this RFP", "analyse the tender", "answer the RFI", "discover RFP 0001", "continue RFP 0001", "reconcile RFP 0001", "export the response for RFP 0001", "is RFP 0001 ready to submit", "open RFP 0001 in the browser", "open the tender tool", "sw-discover-tender specs/rfp-0001-x-analysis.md --editor".
5
- argument-hint: '[tender file or folder of exports (.xlsx|.md|.txt|.csv|.pdf) | specs/rfp-NNNN-slug-analysis.md to continue] [--editor] [--batch <n|all>] [--export]'
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 markdown sidecar per tender, `<source basename>-analysis.md`: class, three-point PD, mechanism and proposed assumptions per requirement row. The partner ticks, the next run reconciles. At Ready to submit: one response CSV per client table.
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
- - PD only. No costs, prices, rates, licence, hosting, PSP or CMP fees, no budget comparison. A stated budget is quoted in §1.1 as context.
16
- - Never edit the client's file. Never read a hidden sheet or anything whose name starts with `_`; name it in §1.3 as ignored. Pass this to every agent.
17
- - Assumption lines are written as `[ ]` only. `accepted` exists solely as the result of a human `[x]`.
18
- - A vague row gets a client question (`CQ-n`) or a proposed assumption, never a silent guess.
19
- - One line of mechanism per row. Design belongs to `sw-design-solution`.
20
- - Markdown and CSV out, plus the response workbook the tender tool writes as a **copy** of the client's file. The skill owns no executable: the workbook is read and written by the tender tool's own zero-dependency reader. The client's original is never touched, no docx is written, nothing is sent.
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
- | `.md` / `.txt` / `.csv` / `.pdf`, or a folder of such exports | New tender: extract, analyse, write `<basename>-analysis.md` next to the source |
27
- | `.xlsx` | Import first, per `reference/procedure.md` step 1 ".xlsx import": `npx -y @execuro-sw-ecosystem/sw-tender-discovery-tool@latest import` proposes a mapping (install gate: `reference/editor-session.md` §0), confirmed via `--editor` or a one-line yes/no, before the normalised source and the analysis are set. A refusal (encrypted, `.xls`, `.xlsb`) is relayed verbatim and the run stops |
28
- | `.docx` | Stop. One line: export every requirement table as CSV UTF-8 (or paste it as markdown tables, header row kept) and rerun with that file or folder |
29
- | `*-analysis.md` | Continue: reconcile, advance, rewrite in place |
30
- | `--export`, or "export the response for RFP NNNN" | Continue, then run step 9 if the gate holds |
31
- | `--editor` | Hand off to `reference/editor-session.md` in this skill's directory as the very first action and stop when it returns; nothing is read, reconciled or analysed before the page is open |
32
- | `--editor-session <url> --batch-file <file>` / `--analyze` | Editor mode (section below), spawned by the editor session |
33
- | `--batch <n>` / `--batch all` | How many requirement rows one estimate run covers. Default 50. `all` = the whole document in a single run (no estimate groups). Stored in the frontmatter as `batch:` and reused by every later run on that analysis until the flag is given again |
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
- - **Analysis** `<source basename>-analysis.md`, next to the source. Recommended: `specs/rfp-NNNN-slug.<ext>` → `specs/rfp-NNNN-slug-analysis.md`. Committed. Structure: `reference/analysis-template.md` in this skill's directory; the nine section numbers, the four §1 subsection numbers and every title are fixed.
39
- - **Source export** `<source dir>/<basename>/<pos>-<sheet-slug>.csv` plus `import-map.json`, written by the tender tool's `import` from the confirmed mapping. Committed; they are what the analysis reads, hashes and exports from.
40
- - **Response** `<source basename>-response-<n>-<table-slug>.csv`, one per client table. Committed, generated, never hand-edited.
41
- - **Work** `specs/.rfp/<slug>/`: `map-<t>.md`, `context-<t>.md`, `context.md`, `meta.md`, `params.md`, `migration.md`, `integrations.md`, `glossary.md`, `C<n>.md`, `pm-C<n>.md`, `arch-common.md`, `arch-C<n>.md`, `qa.md`, `rows-C<n>.md`, `tail-C<n>.md`, `resp-common.md`, `resp-C<n>.csv`. Gitignored.
42
- - **Editor session** `specs/.editor/<slug>/`, written by the Tender Discovery Tool. Gitignored; never hand-edited.
43
- - **Profile** `specs/rfp-partner-profile.md`. Missing: write the template from `estimation-model.md` with `_TBD_` and add a `high` `Q-n` to fill it. `_TBD_` overhead or buffer: PD final is stated before both, §2 says so.
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. Work through them, in order, for a run:
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 1–9: target/source, reconcile, extract, ground truth, fan out, questions, estimate/score/write, the 8-line report, export; delegation, batch size, staged writes |
56
- | `agent-briefs.md` | step 5 (fan out), step 9 (export) | PM, architect, QA, extract, rows, splice, response, assemble briefs; parallelism, caps, delivery rules |
57
- | `editor-session.md` | `--editor` / `--editor-session` in the arguments | the session loop; its "Editor mode" section: batch kinds, tick grammar, notes handling, preserve rules |
58
- | `analysis-template.md` | writing or reading the analysis file | frontmatter, the nine sections, row and status grammar |
59
- | `context-parameters.md` | steps 4 and 6, §1.2 | the P-01…P-50 key-parameter template, estimate-driving flags, missing-parameter question stubs |
60
- | `confidence-rubric.md` | step 7 | dimensions, bands, status rule, honesty rules |
61
- | `estimation-model.md` | steps 2, 5, 7 | three-point, levels, assumption deltas, folding, partner-profile template |
62
- | `client-question-rules.md` | step 6 | vague-row detectors, blocking, wording, cap, answers |
63
- | `assumption-catalogue.md` | step 5 (architect brief) | reusable scope-lock statements by theme |
64
- | `coverage-mapping.md` | step 5 (architect brief), step 9 | class → client token, synonyms, edition tiers |
65
- | `response-rules.md` | step 9 (export) | the response CSV rules (`RSP-1` … `RSP-7`), verbatim for the sub agent brief |
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
- No scripts: the skill ships no executable. The tender tool's `import --source <file>` and `export --xlsx --source <file>` commands read and write the workbook.
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.