@wairon/cli 5.1.1-dev.1 → 5.1.1-dev.11
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/README.md +13 -5
- package/dist/cli/index.js +21488 -17258
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +18582 -13104
- package/dist/index.js.map +1 -1
- package/dist/templates/skills/sdd-architect.md +2 -2
- package/dist/templates/skills/sdd-delegate.md +46 -0
- package/dist/templates/skills/sdd-narrative.md +1 -0
- package/package.json +3 -1
|
@@ -102,7 +102,7 @@ schemas:
|
|
|
102
102
|
- **targetLanguage**: set on L0 (`sdd_initialize_system`), override per L1 — enables language-aware validation (foreign builtins and flow constructs the language lacks are flagged). Extension packs (`.wai/project.yaml` → `extensions.packs`) may register additional languages/platforms and custom profiles.
|
|
103
103
|
- **technologies (L4)**: an external technology (database, vendor SDK, service) is abstracted as a component — wrapped by an Adapter behind an intent-language interface (inside a Repository for persistence, a Gateway for external APIs). Declare the binding on that component's L4: `technologies: [mysql]`. The ownership tree becomes the technology's home: references outside it are flagged (`TECH_LEAKAGE`), vendor names in ANY L3 identifiers are flagged (`VENDOR_NAME_IN_CONTRACT` — the contract is the swap seam, so `insertMySqlRow` is wrong even on the owning adapter), and binding tech on a logic stereotype is flagged (`TECH_ON_LOGIC_COMPONENT`). Swapping the technology then touches one L4.
|
|
104
104
|
- **lint.allow (per-spec suppression)**: any L1–L4/type spec may carry `lint: { allow: [{ code, reason }] }` — silences that WARNING code on that spec only (wairon's `#[allow]`). Errors are never locally suppressible; unknown codes and allows that no longer match anything are flagged. Prefer fixing — an allow is for a documented false positive or a deliberate, reviewable exception.
|
|
105
|
-
- **Methods (L3)**: prefer structured `params: [{name, type}]` — authoritative for type checking; the prose `signature` is then display-only. `guarantees` are open tokens — the builtins (idempotent | atomic | transactional | exactly-once) plus any tokens declared by loaded extension packs; an unknown token warns (`UNKNOWN_GUARANTEE`), and a method's guarantees must back any guarantee a narrative step asserts. Every Portal method needs an `endpoint` — bind with `sdd_set_endpoints` after `sdd_define_interface`.
|
|
106
|
-
- **Narratives (L5, inside L4)**: a FLAT numbered step list; order mimics the code lines. `type: local | call | dispatch | branch | switch | loop | try | parallel | jump | return | throw` — flow steps jump by step number (branch: `condition` + `onFalseStep`; loop/try/parallel: body = next step through `endStep`, a parallel step fans out into ≥2 `branches` arms with an implicit join after `endStep`; a call/dispatch step may set `detach: true` for fire-and-forget; see the sdd-narrative skill for full config). A `call` step names `targetComponent` + `targetMethod`, which must exist on a declared dependency's interface; a `dispatch` step routes a `capability` through a generic-dispatch Portal's dispatch table (validated — `UNSERVED_CAPABILITY`). Granular edits (insert/delete/update steps, reopen status) go through `sdd_update_spec`, which renumbers AND relocates jump fields automatically (inserting AT a jump target returns a NOTICE; `captureJumps: true` retargets those jumps onto the inserted step).
|
|
105
|
+
- **Methods (L3)**: prefer structured `params: [{name, type}]` — authoritative for type checking; the prose `signature` is then display-only. `guarantees` are open tokens — the builtins (idempotent | atomic | transactional | exactly-once) plus any tokens declared by loaded extension packs; an unknown token warns (`UNKNOWN_GUARANTEE`), and a method's guarantees must back any guarantee a narrative step asserts. Every Portal method needs an `endpoint` — bind with `sdd_set_endpoints` after `sdd_define_interface`. A method whose real caller lives OUTSIDE the modeled narrative graph (runtime timer/hook, external system, sibling subsystem) declares `invokedBy: { kind: runtime | external | sibling-subsystem, caller }` — unused-detection seeds it as an entrypoint and reachability propagates through its narrative (thin `caller` prose → `INVOKED_BY_UNDESCRIBED`; a method the internal walk already reaches → `INVOKED_BY_REDUNDANT`). Prefer a `register` step when the wiring is internal.
|
|
106
|
+
- **Narratives (L5, inside L4)**: a FLAT numbered step list; order mimics the code lines. `type: local | call | dispatch | register | branch | switch | loop | try | parallel | jump | return | throw` — flow steps jump by step number (branch: `condition` + `onFalseStep`; loop/try/parallel: body = next step through `endStep`, a parallel step fans out into ≥2 `branches` arms with an implicit join after `endStep`; a call/dispatch step may set `detach: true` for fire-and-forget; see the sdd-narrative skill for full config). A `call` step names `targetComponent` + `targetMethod`, which must exist on a declared dependency's interface; a `dispatch` step routes a `capability` through a generic-dispatch Portal's dispatch table (validated — `UNSERVED_CAPABILITY`); a `register` step (same target shape as `call`) hands the target method to the runtime as a callback — a reachability edge, never an invocation (exempt from call-graph conformance, call cycles, and the durability boot walk). Granular edits (insert/delete/update steps, reopen status) go through `sdd_update_spec`, which renumbers AND relocates jump fields automatically (inserting AT a jump target returns a NOTICE; `captureJumps: true` retargets those jumps onto the inserted step).
|
|
107
107
|
- **Semantic wiring (dispatch / lifecycle / durability)**: a generic-dispatch Portal declares a machine-readable `dispatch` table (`capability → component.method`, targets also under `dependsOn`) instead of hiding routing in prose — the reachability walker follows it, so "invisible to the static walker" lint-allows go stale and are flagged. Each L1 may declare `lifecycle` entrypoints (`{phase: init|shutdown|cyclic|interrupt|scheduled, component, method}`) — flow roots for reachability (PLC scan loops, ISRs, cron); only `init` flows feed hydration. A `Store` declares `durability: ram-projection | durable | read-through | cache`; only `durable` (persisted RAM projection) requires the hydration round-trip — its L3 methods carry `effect: read | write` tags and its writes need a read-back reachable from a lifecycle `init` flow (`MISSING_HYDRATION`); `read-through` = persisted with no RAM copy (every read is the read-back), `ram-projection` = rebuilt not restored, `cache` = evictable and loss-safe. Cross-subsystem method params/returns must be typed — bare `Json`/`any` on a published surface is flagged (`UNTYPED_SEAM`), and persistence claims that exist only in prose are flagged (`UNREALIZED_CLAIM`).
|
|
108
108
|
- **Narrative detail dial**: per method (or L4 spec-level) `detail: full | calls-only | intent`; omitted = stereotype default (Portal/Observer/Adapter → calls-only, Store/Index/Registry → intent, logic components → full). `intent` methods carry an `intent` paragraph instead of steps (what it does + how it fails — placeholder-thin prose is rejected). Levels are floors, not ceilings.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sdd-delegate
|
|
3
|
+
description: Delegate scoped implementation work by fetching a live agent brief (sdd_get_agent_brief / wairon-agent://) and spawning a generic subagent from it. Use when handing a component or subsystem task to a focused sub-session.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: sdd-delegate
|
|
7
|
+
|
|
8
|
+
## Trigger
|
|
9
|
+
- `/sdd delegate [agent]`
|
|
10
|
+
- "Delegate [component] to its implementer"
|
|
11
|
+
- "Hand this off to the [subsystem] owner"
|
|
12
|
+
|
|
13
|
+
## Role & Behavior
|
|
14
|
+
You are the **Delegation Orchestrator**. Your job is to hand scoped work to a focused subagent built from a LIVE agent brief — never from a generated per-component agent file. The flow is hierarchical: the main session delegates to owners, and an owner subagent may delegate further down using this same skill.
|
|
15
|
+
|
|
16
|
+
**Why live briefs**: a brief is composed from the CURRENT spec tree on every call. A re-lock changes the next fetch's result — sessions never restart to pick up topology changes. Component-level delegation therefore has NO generated files; per-subsystem owner files are the only generated artifacts, and they are an optional materialized view of the same briefs.
|
|
17
|
+
|
|
18
|
+
**Project guidance**: a project may carry user-owned per-agent guidance in `.wai/agents/<agent-id>.md` — it is folded into every brief under `## Project guidance` (scaffold one with `wairon agent customize <id>`).
|
|
19
|
+
|
|
20
|
+
## Workflow Rules
|
|
21
|
+
1. **Discover the target agent**:
|
|
22
|
+
- Call the `listAgents` MCP tool, or list MCP resources and look for `wairon-agent://<agentId>` entries.
|
|
23
|
+
- Pick the agent whose `ownedPaths`/domain matches the task. If no agent fits, stop and tell the user the topology has a gap.
|
|
24
|
+
2. **Fetch the LIVE brief**:
|
|
25
|
+
- Call `sdd_get_agent_brief(agentId)` (or read the `wairon-agent://<agentId>` resource).
|
|
26
|
+
- The brief carries: `agentId`, `name`, `template`, `domainRoot?`, `ownedPaths`, `readPaths?`, `instructions`, `variantGuidance?`, and — when the project opted into `execution.tier` — `profile` and `budget`.
|
|
27
|
+
- **Never reuse a brief across delegations or after a re-lock** — fetch fresh per delegation; the call is cheap and the brief is always current.
|
|
28
|
+
3. **Spawn a GENERIC subagent from the brief**:
|
|
29
|
+
- Prompt: `brief.instructions`, plus the concrete task description.
|
|
30
|
+
- Write fence: the subagent may only modify files matching `brief.ownedPaths` (within `brief.domainRoot` when set).
|
|
31
|
+
- Required first reading: `brief.readPaths` — the subagent reads these before any edit.
|
|
32
|
+
- Pass `brief.variantGuidance` along when present.
|
|
33
|
+
4. **Apply `brief.budget` when it is present** — constituting the subagent correctly is part of spawning it, not a separate concern. When the brief carries no budget the project has not opted in; spawn as you otherwise would.
|
|
34
|
+
- `modelTier` → your host's model families. On Claude Code: `small`→haiku, `standard`→sonnet, `large`→opus, `frontier`→fable. A host that cannot select models ignores this rather than approximating it.
|
|
35
|
+
- `effort`, `maxTurns` → pass through where the host supports them. The turn ceiling is a circuit breaker: hitting it means the task was scoped too big, so re-scope and re-delegate rather than raising it.
|
|
36
|
+
- `toolClass` → `read-only` grants read/search only; `implement` adds edit/write/shell; `orchestrate` is for a router that owns nothing and must not read bulk content.
|
|
37
|
+
- `allowNestedDelegation: false` → withhold the delegation tool entirely, so the subagent does its own work instead of spawning another layer.
|
|
38
|
+
- `mcp: none` → do not load MCP servers into the subagent; its brief already quotes the contract it needs.
|
|
39
|
+
- **`frontier` is never an owner tier.** Derivation never assigns it. Treat it as a sparring partner: when a subagent is genuinely stuck on something the specs do not settle, escalate that *question* to a frontier-tier helper and bring the answer back. A component whose owner truly needs frontier capability to operate is usually a component doing too much — raise that as a spec concern rather than spending the tier.
|
|
40
|
+
- Fan delegations out in ONE message when they are independent. Siblings spawned together share a cached prompt prefix; dispatched one at a time they each pay for it.
|
|
41
|
+
5. **The subagent does the work**:
|
|
42
|
+
- For component implementation it follows the `sdd-implement` skill (gating checks, AI-TDD, narrative coding) and reports back: what changed, test results, anything out of scope.
|
|
43
|
+
6. **Review & integrate**:
|
|
44
|
+
- Read the report, verify the write fence was respected, and continue orchestrating — or delegate the next scoped task.
|
|
45
|
+
|
|
46
|
+
This flow is transport-agnostic: it works identically over local stdio and the hosted data plane — the tools and `wairon-agent://` resources are the same surface.
|
|
@@ -31,6 +31,7 @@ You must read, respect, and update `.wai/phased_design.md` (specifically Stage 5
|
|
|
31
31
|
- `local`: internal logic (calculations, state mapping).
|
|
32
32
|
- `call`: call to another component (`targetComponent` + `targetMethod`).
|
|
33
33
|
- `dispatch`: a capability routed through a generic-dispatch Portal's dispatch table (`targetComponent` = the Portal, `capability` = the routed name). Use this instead of a bare `call` to the portal's generic handle — the gate validates the capability against the portal's table (`UNSERVED_CAPABILITY`) and reachability follows the bound server.
|
|
34
|
+
- `register`: a runtime-callback handoff (`targetComponent` + `targetMethod`, same shape as `call`): this method hands the target method to the runtime (timer, event listener, shutdown hook) to invoke LATER. Reachability follows the edge (the callback is reached wherever the registrar is), but it is never an invocation — exempt from call-graph conformance, call-cycle detection, and the durability boot walk. If the real caller lives entirely outside the modeled graph, declare `invokedBy` on the target's L3 method instead.
|
|
34
35
|
- `branch`: if/else — `condition` + `onFalseStep` (true continues at `onTrueStep` or the next step). Chain else-ifs by targeting another branch step.
|
|
35
36
|
- `switch`: `on` + `cases: [{value, step}]` + optional `defaultStep`.
|
|
36
37
|
- `loop`: header step; body = next step through `endStep`. `loopKind: forEach | for | while | doWhile` with `over` (forEach/for) or `condition` (while/doWhile).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wairon/cli",
|
|
3
|
-
"version": "5.1.1-dev.
|
|
3
|
+
"version": "5.1.1-dev.11",
|
|
4
4
|
"description": "SYW Waffle AIron — CLI for managing AI coding agent topology across projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -45,7 +45,9 @@
|
|
|
45
45
|
"build:watch": "tsup --watch",
|
|
46
46
|
"build:binary": "npm run build && pkg dist/cli/index.js --config package.json --target node20-linux-x64,node20-win-x64,node20-macos-x64 --output wairon --compress GZip",
|
|
47
47
|
"typecheck": "tsc --noEmit",
|
|
48
|
+
"typecheck:web": "npm --prefix web install --no-audit --no-fund && npm --prefix web run typecheck",
|
|
48
49
|
"test": "vitest run",
|
|
50
|
+
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
49
51
|
"test:watch": "vitest",
|
|
50
52
|
"test:coverage": "vitest run --coverage",
|
|
51
53
|
"clean": "rimraf dist"
|