archgen-skill 0.0.1
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/bin/archgen.mjs +89 -0
- package/lib/block.js +71 -0
- package/lib/init.js +56 -0
- package/lib/install.js +85 -0
- package/package.json +18 -0
- package/vendor/skills/archgen/SKILL.md +230 -0
- package/vendor/skills/archgen/assets/mcp-template.json +42 -0
- package/vendor/skills/archgen/assets/skill-registry.json +23 -0
- package/vendor/skills/archgen/references/artifact-templates.md +105 -0
- package/vendor/skills/archgen/references/code-standards.md +70 -0
- package/vendor/skills/archgen/references/interview.md +66 -0
- package/vendor/skills/archgen/references/mcp-and-skills.md +46 -0
- package/vendor/skills/archgen/references/orchestration.md +106 -0
- package/vendor/skills/archgen/references/platforms.md +79 -0
- package/vendor/skills/archgen/scripts/impact.mjs +64 -0
- package/vendor/skills/archgen/scripts/lib/graph.mjs +128 -0
- package/vendor/skills/archgen/scripts/lib/plan-coverage.mjs +32 -0
- package/vendor/skills/archgen/scripts/lib/yaml.mjs +476 -0
- package/vendor/skills/archgen/scripts/next-tasks.mjs +55 -0
- package/vendor/skills/archgen/scripts/set-status.mjs +40 -0
- package/vendor/skills/archgen/scripts/validate.mjs +70 -0
- package/vendor/skills/archgen/scripts/verify-plan.mjs +65 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Artifact templates — copy, fill, never invent structure
|
|
2
|
+
|
|
3
|
+
## architecture.yaml (LARGE scope)
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
name: Acme Shop
|
|
7
|
+
slug: acme-shop # becomes .archgen/acme-shop/
|
|
8
|
+
stack:
|
|
9
|
+
language: typescript
|
|
10
|
+
runtime: node20
|
|
11
|
+
framework: nextjs
|
|
12
|
+
database: postgres
|
|
13
|
+
structure: # planned folder tree — planned BEFORE code exists
|
|
14
|
+
- path: src/features/<feature>/
|
|
15
|
+
purpose: feature modules (api, internal, tests colocated)
|
|
16
|
+
- path: src/shared/
|
|
17
|
+
purpose: cross-feature utilities, no feature imports
|
|
18
|
+
- path: src/config/
|
|
19
|
+
purpose: env parsing + constants
|
|
20
|
+
modules:
|
|
21
|
+
- name: catalog
|
|
22
|
+
responsibility: product browsing + search
|
|
23
|
+
owns: ["src/features/catalog/**"]
|
|
24
|
+
- name: checkout
|
|
25
|
+
responsibility: cart → payment orchestration
|
|
26
|
+
owns: ["src/features/checkout/**"]
|
|
27
|
+
decisions: # ADR-lite; full ADRs live in decisions/
|
|
28
|
+
- id: D1
|
|
29
|
+
title: Postgres over MongoDB
|
|
30
|
+
context: transactional orders + reporting queries
|
|
31
|
+
decision: Postgres 16 with Prisma
|
|
32
|
+
consequences: schema migrations required per release
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## tasks.yaml (canonical shape)
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
# comment lines are preserved by set-status.mjs — annotate freely
|
|
39
|
+
tasks:
|
|
40
|
+
# Core pipeline — do first
|
|
41
|
+
- id: C
|
|
42
|
+
title: Scaffold project skeleton + config loading
|
|
43
|
+
depends_on: []
|
|
44
|
+
file_ownership: ["src/config/**", "package.json"]
|
|
45
|
+
acceptance:
|
|
46
|
+
- "npm run build exits 0"
|
|
47
|
+
- "config loads from env with typed schema"
|
|
48
|
+
- id: B
|
|
49
|
+
title: Catalog API endpoints
|
|
50
|
+
depends_on: [C]
|
|
51
|
+
file_ownership: ["src/features/catalog/**"]
|
|
52
|
+
acceptance:
|
|
53
|
+
- "GET /products returns seeded list"
|
|
54
|
+
- id: A
|
|
55
|
+
title: Checkout flow wired to catalog
|
|
56
|
+
depends_on: [B]
|
|
57
|
+
parallel_group: wave-final
|
|
58
|
+
file_ownership: ["src/features/checkout/**"]
|
|
59
|
+
artifacts: ["docs/checkout-flow.md"]
|
|
60
|
+
acceptance:
|
|
61
|
+
- "e2e test completes purchase with stub payment"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Rules encoded above: depends_on = prerequisites; ownership globs disjoint
|
|
65
|
+
within a wave; acceptance = objectively checkable statements.
|
|
66
|
+
|
|
67
|
+
## SMALL-scope plan note (plans/<feature>.md)
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
# <feature> — plan note (SMALL)
|
|
71
|
+
Intent: <one paragraph>
|
|
72
|
+
Tasks: see tasks.yaml entries <ids>
|
|
73
|
+
Risks: <bullets or none>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## ADR template (decisions/NNNN-title.md)
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
# NNNN. <decision title>
|
|
80
|
+
Date: YYYY-MM-DD · Status: accepted
|
|
81
|
+
## Context
|
|
82
|
+
<forces at play>
|
|
83
|
+
## Decision
|
|
84
|
+
<what we chose>
|
|
85
|
+
## Consequences
|
|
86
|
+
<positive / negative / neutral>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Mermaid snippets
|
|
90
|
+
|
|
91
|
+
C4 context:
|
|
92
|
+
```mermaid
|
|
93
|
+
graph TB
|
|
94
|
+
user([Customer]) --> sys[Acme Shop]
|
|
95
|
+
sys --> pay[Stripe]
|
|
96
|
+
sys --> mail[Email Service]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Container:
|
|
100
|
+
```mermaid
|
|
101
|
+
graph LR
|
|
102
|
+
web[Next.js App] --> api[API Routes]
|
|
103
|
+
api --> db[(Postgres)]
|
|
104
|
+
api --> cache[(Redis)]
|
|
105
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Code standards — contractual, verifier-enforced
|
|
2
|
+
|
|
3
|
+
Workers and planners follow these. The verifier rejects tasks whose plan or
|
|
4
|
+
output violates them. Every rule exists because its absence produced real
|
|
5
|
+
production incidents somewhere.
|
|
6
|
+
|
|
7
|
+
## Typing (TypeScript projects)
|
|
8
|
+
|
|
9
|
+
- NO ANY: `any` is BANNED — including implicit anys and `as any` casts. Enable `"strict": true` plus
|
|
10
|
+
`noImplicitAny`, `strictNullChecks`, `noUncheckedIndexedAccess`.
|
|
11
|
+
- No unhandled `undefined`/null: validate at boundaries (env parsing, API
|
|
12
|
+
input) with a schema; inside the codebase use non-optional types + explicit
|
|
13
|
+
result objects for fallible operations.
|
|
14
|
+
- Prefer discriminated unions over boolean-flag parameters.
|
|
15
|
+
- Generated Python/Rust/Go equivalents: type hints mandatory / no `unwrap()` on
|
|
16
|
+
fallibles / handle errors explicitly — same discipline per language.
|
|
17
|
+
|
|
18
|
+
## File & module limits
|
|
19
|
+
|
|
20
|
+
- HARD CEILING: files < 1000 LOC. Split well before — when a file gains a
|
|
21
|
+
second responsibility, that IS the split signal.
|
|
22
|
+
- One public purpose per module. If the docstring needs "and", split.
|
|
23
|
+
|
|
24
|
+
## Folder structure (planned in architecture.yaml BEFORE coding)
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
src/
|
|
28
|
+
features/<name>/ # feature-first: everything a feature owns
|
|
29
|
+
api/ # inbound contracts (routes/handlers)
|
|
30
|
+
internal/ # private implementation
|
|
31
|
+
tests/ # colocated tests mirror structure
|
|
32
|
+
shared/ # cross-feature ONLY; may not import features/
|
|
33
|
+
config/ # typed env + constants
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- Feature modules may import `shared/`; `shared/` imports NOTHING from features.
|
|
37
|
+
- Tests live beside code (mirrored path), never a distant `/tests` desert.
|
|
38
|
+
|
|
39
|
+
## File naming
|
|
40
|
+
|
|
41
|
+
Per-language conventions (professional, descriptive, consistent):
|
|
42
|
+
|
|
43
|
+
| Language | Source files | Components/classes | Tests |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| TypeScript | `kebab-case.ts` | `PascalCase.tsx` components, `PascalCase` classes | `<subject>.test.ts` colocated |
|
|
46
|
+
| Python | `snake_case.py` modules | `PascalCase` classes, `snake_case` functions | `test_<subject>.py` mirrored |
|
|
47
|
+
| Rust | `snake_case.rs` modules | `PascalCase` types/traits | `#[cfg(test)] mod tests` inline or `tests/` |
|
|
48
|
+
| Go | `snake_case.go` packages | `PascalCase` exported / `camelCase` internal | `<subject>_test.go` same dir |
|
|
49
|
+
|
|
50
|
+
- Descriptive > clever: `rate-limiter.ts` not `rl.ts`; `parse-config.ts` not
|
|
51
|
+
`utils2.ts`.
|
|
52
|
+
- BANNED names: `utils.ts`, `misc.ts`, `helpers.ts`, `temp*`, `final*`,
|
|
53
|
+
`copy*`, numbered duplicates (`auth2.ts`) — split by responsibility instead.
|
|
54
|
+
|
|
55
|
+
## Comments
|
|
56
|
+
|
|
57
|
+
- WHY at decision points: tradeoffs taken, non-obvious constraints, links to
|
|
58
|
+
decisions ("see ADR D3").
|
|
59
|
+
- Public APIs get doc comments: purpose, params, failure modes.
|
|
60
|
+
- NO noise: no `// increment i`, no commented-out code, no change-log comments
|
|
61
|
+
(git remembers).
|
|
62
|
+
|
|
63
|
+
## Enforcement hooks
|
|
64
|
+
|
|
65
|
+
- tasks.yaml acceptance criteria MUST include at least one objectively
|
|
66
|
+
checkable statement (command exit, observable output).
|
|
67
|
+
- The verifier runs verify-plan.mjs AND reads plans against this file;
|
|
68
|
+
violations = ISSUES, not warnings.
|
|
69
|
+
- Workers receive this reference in their prompt contract; "I didn't know" is
|
|
70
|
+
not available to them.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Interview playbook — requirement elicitation
|
|
2
|
+
|
|
3
|
+
Goal: complete, conflict-free requirements in the FEWEST round-trips.
|
|
4
|
+
Batch 3–5 questions per message. Never ask what you can infer.
|
|
5
|
+
|
|
6
|
+
## Round 1 — business frame (ask the user)
|
|
7
|
+
|
|
8
|
+
1. What problem does this solve, and for whom? (one sentence is enough)
|
|
9
|
+
2. What does success look like — measurable if possible?
|
|
10
|
+
3. Hard constraints: deadline, budget, must-keep systems?
|
|
11
|
+
|
|
12
|
+
## Round 2 — technical frame (infer first, confirm second)
|
|
13
|
+
|
|
14
|
+
Draft answers from context (existing repo, stated stack), then confirm:
|
|
15
|
+
4. Stack + hosting target correct? (state your inference)
|
|
16
|
+
5. Scale expectation: users/reqs/day, data growth?
|
|
17
|
+
6. Integration surfaces: auth, payments, email, existing APIs?
|
|
18
|
+
|
|
19
|
+
## Round 3 — constraints & risks
|
|
20
|
+
|
|
21
|
+
7. Compliance needs? (GDPR/HIPAA/PCI — checklist level only)
|
|
22
|
+
8. What must NOT change?
|
|
23
|
+
9. Top 2 risks the user already knows about?
|
|
24
|
+
|
|
25
|
+
## Conflict detection
|
|
26
|
+
|
|
27
|
+
While collecting, watch for and surface immediately:
|
|
28
|
+
- Scale vs budget contradictions ("1M users" + "cheapest VPS")
|
|
29
|
+
- Feature vs constraint collisions ("real-time" + "no websockets allowed")
|
|
30
|
+
- Requirement vs existing-architecture conflicts (brownfield: check codebase-map)
|
|
31
|
+
|
|
32
|
+
Format: "I see a tension between X and Y. Which wins?" — one per message,
|
|
33
|
+
resolve before proceeding. [wait for user]
|
|
34
|
+
|
|
35
|
+
## Answers-file format (machine-readable, enables non-interactive runs)
|
|
36
|
+
|
|
37
|
+
For scripted E2E or re-runs, requirements may come from a file instead of
|
|
38
|
+
conversation — `.archgen/<slug>/answers.yaml`:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
scope_class: MEDIUM # SMALL | MEDIUM | LARGE
|
|
42
|
+
problem: one-line problem statement
|
|
43
|
+
success_criteria:
|
|
44
|
+
- measurable outcome
|
|
45
|
+
constraints:
|
|
46
|
+
- must-not-change items
|
|
47
|
+
stack:
|
|
48
|
+
language: typescript
|
|
49
|
+
runtime: node
|
|
50
|
+
hosting: vercel
|
|
51
|
+
integrations: [auth0, stripe]
|
|
52
|
+
compliance: [] # checklist placeholders only
|
|
53
|
+
risks:
|
|
54
|
+
- known risk text
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The orchestrator treats answers.yaml as authoritative user input and skips
|
|
58
|
+
the conversational rounds when it exists and parses cleanly.
|
|
59
|
+
|
|
60
|
+
## Completion test
|
|
61
|
+
|
|
62
|
+
Requirements are done when ALL hold:
|
|
63
|
+
- Every ROUND-1 question answered
|
|
64
|
+
- All drafted inferences confirmed or corrected
|
|
65
|
+
- Zero unresolved conflicts
|
|
66
|
+
- Scope class agreed (SMALL/MEDIUM/LARGE) — this gates artifact generation
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# MCP installation and skill fetching
|
|
2
|
+
|
|
3
|
+
## INSTALL-MCP procedure (approval-gated)
|
|
4
|
+
|
|
5
|
+
1. **Identify** the platform (SKILL.md Step 0), load its row from
|
|
6
|
+
references/platforms.md section MCP: config file, top-level key, quirks.
|
|
7
|
+
2. **Prefer the platform CLI** where the table lists one. It handles scope and
|
|
8
|
+
file selection correctly:
|
|
9
|
+
- `claude mcp add --scope project --transport http <name> <url>`
|
|
10
|
+
- `codex mcp add <name> --url <url>` or `codex mcp add <n> -- cmd args`
|
|
11
|
+
- `gemini mcp add -s project -t http <name> <url>`
|
|
12
|
+
3. No CLI available: draft the exact config diff against the platform's real
|
|
13
|
+
shape (see assets/mcp-template.json for annotated canonical forms) and SHOW
|
|
14
|
+
it to the user. **USER APPROVAL GATE** — wait for an explicit yes before any
|
|
15
|
+
write; approval is never inferred from earlier steps.
|
|
16
|
+
4. Apply, then apply the restart semantics from the platforms table.
|
|
17
|
+
5. Verify the tool is visible after reload; report failure modes honestly.
|
|
18
|
+
|
|
19
|
+
Never write MCP configs silently. Never invent config shapes — the four known
|
|
20
|
+
top-level keys are mcpServers / servers / mcp / context_servers; remote URL
|
|
21
|
+
fields vary (url vs serverUrl vs httpUrl) per the platforms table.
|
|
22
|
+
|
|
23
|
+
## FETCH-SKILL procedure
|
|
24
|
+
|
|
25
|
+
1. Read assets/skill-registry.json. Match the current task domain against
|
|
26
|
+
entry `matches` patterns (e.g. frontend/UI work matches ui-skills).
|
|
27
|
+
2. Install into the CURRENT platform's skill dir (platforms.md paths):
|
|
28
|
+
- Generic: `npx skills add <owner>/<repo>` (writes correct path for 75+
|
|
29
|
+
harnesses; `-g` for global).
|
|
30
|
+
- Entry-specific command when present (e.g. `npx ui-skills get baseline-ui`).
|
|
31
|
+
3. Confirm files landed where expected; then FOLLOW the fetched skill's own
|
|
32
|
+
instructions for the task domain.
|
|
33
|
+
4. Missing capability: web-search reputable skill repos, propose to user,
|
|
34
|
+
on approval append an entry to the registry (PR or local edit).
|
|
35
|
+
|
|
36
|
+
## Registry entry shape
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"name": "short-name",
|
|
41
|
+
"purpose": "when to reach for it",
|
|
42
|
+
"repo": "owner/repo",
|
|
43
|
+
"install": "npx skills add owner/repo",
|
|
44
|
+
"matches": ["frontend", "ui", "design"]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Orchestration playbook — survey, verify, dispatch
|
|
2
|
+
|
|
3
|
+
Sub-agent briefs and the wave-execution mechanics. The orchestrator (SKILL.md)
|
|
4
|
+
calls into these sections; workers never read this file.
|
|
5
|
+
|
|
6
|
+
## §survey — brownfield codebase analysis brief
|
|
7
|
+
|
|
8
|
+
Dispatch 1–2 sub-agents with this prompt shape:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
TASK: Survey this repository to prepare a feature plan for: <feature request>
|
|
12
|
+
DELIVERABLE: write .archgen/<slug>/codebase-map.md containing EXACTLY these
|
|
13
|
+
sections:
|
|
14
|
+
## Structure — top-2-levels folder tree w/ one-line purpose each
|
|
15
|
+
## Modules — table: module | responsibility | key entry points
|
|
16
|
+
## Conventions — naming, error-handling, test layout actually observed
|
|
17
|
+
## Affected — files/modules the feature will touch, with evidence paths
|
|
18
|
+
SCOPE: read-only. Use codegraph MCP tools when available
|
|
19
|
+
(codegraph_explore / codegraph_files); otherwise Glob+Grep systematically.
|
|
20
|
+
VERIFY: every claim cites a real path; no guessed APIs.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Accept the map only if every section is present and non-empty.
|
|
24
|
+
|
|
25
|
+
## §verifier — plan review brief
|
|
26
|
+
|
|
27
|
+
Dispatch AFTER artifacts exist and validate.mjs passes. The verifier:
|
|
28
|
+
|
|
29
|
+
1. Runs `node <skill>/scripts/verify-plan.mjs .archgen/<slug>/tasks.yaml --plan .archgen/<slug>/plans`
|
|
30
|
+
— deterministic floor: cycles, dangling refs, same-wave ownership overlap,
|
|
31
|
+
missing acceptance criteria, plan↔task coverage.
|
|
32
|
+
2. Reads plans + tasks.yaml and judges what scripts cannot:
|
|
33
|
+
- Are wave boundaries sensible (no artificial serialization)?
|
|
34
|
+
- Do acceptance criteria objectively verify (command or observable)?
|
|
35
|
+
- Edge cases covered per SKILL.md right-sizing class?
|
|
36
|
+
- Code-standards compliance plausible for the planned structure?
|
|
37
|
+
3. Returns EXACTLY one line verdict: `APPROVE` or `ISSUES:` + numbered list.
|
|
38
|
+
|
|
39
|
+
Orchestrator fixes issues (edits artifacts) and re-dispatches. Loop until
|
|
40
|
+
APPROVE — never present to the user before verifier approval.
|
|
41
|
+
|
|
42
|
+
## §dispatch — wave execution
|
|
43
|
+
|
|
44
|
+
### Tier 1: native sub-agents (preferred where strong)
|
|
45
|
+
|
|
46
|
+
- Claude Code: background Agent tool per task; add worktree isolation for
|
|
47
|
+
tasks whose ownership might brush shared files.
|
|
48
|
+
- Cursor: background subagents; request isolation when >2 parallel workers.
|
|
49
|
+
- Antigravity: invoke_subagent async.
|
|
50
|
+
- Codex: spawn_agent works but children can't nest — flatten the plan instead.
|
|
51
|
+
- Gemini CLI: parallel @agents are hub-and-spoke only AND have no isolation →
|
|
52
|
+
use them ONLY for read-only/research waves unless wrapping in worktrees.
|
|
53
|
+
|
|
54
|
+
### Tier 2: bash fallback workers (universal floor)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
#!/usr/bin/env bash
|
|
58
|
+
set -uo pipefail
|
|
59
|
+
SLUG="<slug>"; ROOT="$(pwd)"; SKILL="<abs-path-to-skills/archgen>"
|
|
60
|
+
run_task() { # $1=id $2=prompt-file
|
|
61
|
+
local id="$1" pf="$2" log=".archgen/$SLUG/results/$id.log"
|
|
62
|
+
mkdir -p "$(dirname "$log")"
|
|
63
|
+
local body; body="$(cat "$pf")
|
|
64
|
+
|
|
65
|
+
You own exactly the file_ownership globs listed for this task in tasks.yaml. Follow archgen code-standards. On success run: node \"$SKILL/scripts/set-status.mjs\" .archgen/$SLUG/tasks.yaml \"$id\" done ; on failure set-status failed."
|
|
66
|
+
case "$ARCHGEN_HARNESS" in
|
|
67
|
+
claude) claude -p "$body" --output-format json --permission-mode acceptEdits > "$log" 2>&1 ;;
|
|
68
|
+
opencode) opencode run "$body" --auto --format json > "$log" 2>&1 ;;
|
|
69
|
+
codex) codex exec --sandbox workspace-write --json -o "$log.final" "$body" > "$log" 2>&1 ;;
|
|
70
|
+
gemini) gemini --output-format json "$body" > "$log" 2>&1 ;;
|
|
71
|
+
agy) agy -p --output-format json --print-timeout 15m "$body" > "$log" 2>&1 ;;
|
|
72
|
+
*) echo "unknown harness: $ARCHGEN_HARNESS" >&2; return 9 ;;
|
|
73
|
+
esac
|
|
74
|
+
|
|
75
|
+
You own exactly: $(node "$SKILL/scripts/impact.mjs" .archgen/$SLUG/tasks.yaml "$id" >/dev/null 2>&1 && echo 'see tasks.yaml file_ownership'). Follow archgen code-standards. On success run: node "$SKILL/scripts/set-status.mjs" .archgen/$SLUG/tasks.yaml "$id" done ; on failure set-status failed." \
|
|
76
|
+
}
|
|
77
|
+
# Wave N: launch all frontier tasks in parallel…
|
|
78
|
+
for t in $(node "$SKILL/scripts/next-tasks.mjs" .archgen/$SLUG/tasks.yaml \
|
|
79
|
+
| node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>console.log(JSON.parse(d).waves[0].map(t=>t.id).join(" ")))'); do
|
|
80
|
+
run_task "$t" ".archgen/$SLUG/results/$t.prompt" & pids="$pids $!"
|
|
81
|
+
done
|
|
82
|
+
fail=0; for p in $pids; do wait "$p" || fail=1; done
|
|
83
|
+
exit $fail # orchestrator inspects statuses before next wave
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Isolation upgrade for Tier 2 when ownership borders are tight:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git worktree add ".worktrees/$id" -b "task-$id"
|
|
90
|
+
# run worker with cwd=.worktrees/$id, then merge task-$id on success
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Worker prompt contract (both tiers)
|
|
94
|
+
|
|
95
|
+
Every worker receives: task id+title, its acceptance criteria verbatim, its
|
|
96
|
+
file_ownership globs ("edit NOTHING outside these"), the code-standards
|
|
97
|
+
reference, and the done/failed status-update command. Workers do NOT talk to
|
|
98
|
+
each other; coordination happens only through tasks.yaml state.
|
|
99
|
+
|
|
100
|
+
## Failure policy
|
|
101
|
+
|
|
102
|
+
- Worker exit ≠ 0 OR status=failed → leave it failed. NEVER auto-retry.
|
|
103
|
+
- Downstream tasks stay blocked by the resolver (blockedByFailure).
|
|
104
|
+
- Report to user: failed id, log path, proposed fix-task description.
|
|
105
|
+
[wait for user] A fix task gets fresh id, depends_on=[], ownership of the
|
|
106
|
+
failed task's globs.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Platform capability matrix (August 2026)
|
|
2
|
+
|
|
3
|
+
Facts verified against official vendor docs; cells marked UNVERIFIED could not be
|
|
4
|
+
confirmed. The orchestrator loads ONLY the row matching Step-0 detection.
|
|
5
|
+
|
|
6
|
+
## Skill install paths
|
|
7
|
+
|
|
8
|
+
| Platform | Project path | Global path | Notes |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| Claude Code | `.claude/skills/<name>/` | `~/.claude/skills/<name>/` | Plugin marketplace: `/plugin install x@mkt`; skills hot-reload |
|
|
11
|
+
| OpenCode | `.opencode/skills/` (+ reads `.claude/`, `.agents/`) | `~/.config/opencode/skills/` (+ `~/.claude/`, `~/.agents/`) | Walks up to git root |
|
|
12
|
+
| Cursor | `.agents/skills/`, `.cursor/skills/` | `~/.agents/skills/`, `~/.cursor/skills/` | ≥2.4 native; also scans `.claude/`, `.codex/`; nesting OK |
|
|
13
|
+
| Windsurf | `.windsurf/skills/` | `~/.codeium/windsurf/skills/` | Enterprise system paths also read |
|
|
14
|
+
| VS Code + Copilot | `.github/skills/` (+ `.claude/`, `.agents/`) | `~/.copilot/skills/` (+ `~/.claude/`, `~/.agents/`) | Extra dirs via chat.agentSkillsLocations |
|
|
15
|
+
| Codex CLI | `.agents/skills/` (crawls up to repo root) | `~/.agents/skills/` (legacy `~/.codex/skills/`) | `$skill-installer` built in |
|
|
16
|
+
| Gemini CLI | `.gemini/skills/` or `.agents/skills/` | `~/.gemini/skills/` or `~/.agents/skills/` | `gemini skills install <git-url> --consent` |
|
|
17
|
+
| Antigravity | `<ws>/.agents/skills/` | `~/.gemini/config/skills/` | Backward-supports `.agent/skills`; name frontmatter OPTIONAL here |
|
|
18
|
+
| Zed | `<project>/.agents/skills/` ONLY | `~/.agents/skills/` ONLY | FLAT layout — no nesting; worktree-trust gate on project skills |
|
|
19
|
+
| Kiro | `.kiro/skills/` | `~/.kiro/skills/` | Custom agents need explicit `resources: ["skill://.kiro/skills/**/SKILL.md"]` |
|
|
20
|
+
| Trae | `.trae/skills/` (opt-in `.agents/`) | `~/.trae/skills/` | `.trae` wins collisions; CN variant separate |
|
|
21
|
+
| Claude Desktop | — (account-scoped) | GUI ZIP upload only | Scripts run sandboxed, NOT local shell |
|
|
22
|
+
|
|
23
|
+
Universal fallback: `.agents/skills/` project + `~/.agents/skills/` user works
|
|
24
|
+
across most harnesses. Primary distribution: `npx skills add <repo>` (75+ agents).
|
|
25
|
+
|
|
26
|
+
## MCP configuration
|
|
27
|
+
|
|
28
|
+
Four different top-level keys exist — NEVER assume `mcpServers`:
|
|
29
|
+
|
|
30
|
+
| Platform | File(s) | Key | stdio fields | Remote URL field | CLI add? |
|
|
31
|
+
|---|---|---|---|---|---|
|
|
32
|
+
| Claude Code | `.mcp.json` (project) / `~/.claude.json` (local/user) | `mcpServers` | command,args[,env] type:"stdio" | `url`+headers type:"http"/"sse" | `claude mcp add --scope project\|local\|user --transport stdio\|http\|sse` |
|
|
33
|
+
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) / `%APPDATA%\Claude\` (Win) / `~/.config/Claude/` (Linux) | `mcpServers` | command,args,env | UI Connectors preferred; file `url` UNVERIFIED | none |
|
|
34
|
+
| OpenCode | `opencode.json(c)` / `~/.config/opencode/opencode.json(c)` | **`mcp`** | type:"local", **command:[array]**, environment{} | type:"remote", `url`, headers | `opencode mcp add` (interactive only) |
|
|
35
|
+
| Cursor | `.cursor/mcp.json` / `~/.cursor/mcp.json` | `mcpServers` | command,args,env,cwd | `url`+headers (`${env:VAR}` interp.) | none (deep-links UNVERIFIED) |
|
|
36
|
+
| VS Code Copilot | `.vscode/mcp.json` / profile mcp.json / portable `.mcp.json` | **`servers`** (+inputs[]) | type:"stdio",command,args,env | type:"http"/"sse", url, headers | `copilot mcp add`; palette "MCP: Add Server" |
|
|
37
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` (global only) | `mcpServers` | command,args,env | **`serverUrl`** OR `url` + headers | none (Marketplace UI/deeplink) |
|
|
38
|
+
| Codex CLI | `~/.codex/config.toml` / project `.codex/config.toml` (trust-gated) | `[mcp_servers.<id>]` TOML | command,args,env,cwd | `url`, bearer_token_env_var, http_headers | `codex mcp add <n> --url \| -- cmd args` |
|
|
39
|
+
| Gemini CLI | `~/.gemini/settings.json` / `.gemini/settings.json` | `mcpServers` | command,args,env,cwd,trust | `httpUrl`(HTTP)/`url`(SSE)+headers | `gemini mcp add -s user\|project -t stdio\|sse\|http` |
|
|
40
|
+
| Antigravity | `~/.gemini/config/mcp_config.json` / `.agents/mcp_config.json` | `mcpServers` | command,args,env,cwd | **`serverUrl` MANDATORY — `url` rejected** + headers/oauth | none (`/mcp` overlay manager) |
|
|
41
|
+
| Zed | `~/.config/zed/settings.json` / `.zed/settings.json` | **`context_servers`** | command,args,env | `url`+headers (OAuth auto-prompt) | none (Settings UI writes file) |
|
|
42
|
+
| Kiro | `.kiro/settings/mcp.json` / `~/.kiro/settings/mcp.json` | `mcpServers` | command,args,env,disabled,autoApprove | `url`+headers+oauth | `/mcp add` (CLI session) |
|
|
43
|
+
| Trae IDE | `.trae/mcp.json` | `mcpServers` | command,args,env | url + type:"sse"/"http" | none (UI manual add) |
|
|
44
|
+
|
|
45
|
+
Restart semantics: full restart needed = Claude Desktop, Windsurf, Zed.
|
|
46
|
+
Hot-reload = Kiro, Gemini, Codex. Approval prompt on first detection =
|
|
47
|
+
Claude Code project-scope servers.
|
|
48
|
+
|
|
49
|
+
## Sub-agent dispatch capability
|
|
50
|
+
|
|
51
|
+
| Harness | Native mechanism | Parallel background | Isolation for parallel edits |
|
|
52
|
+
|---|---|---|---|
|
|
53
|
+
| Claude Code | `Agent` tool + subagent_type; `.claude/agents/*.md` | Yes (default since v2.1.198; cap ~20) | BEST: `isolation: worktree` frontmatter; `/batch` |
|
|
54
|
+
| OpenCode | task/subagent tool + subagent_type | Yes (fg + background) | None in core → use file-ownership discipline or team-mode plugin worktrees |
|
|
55
|
+
| Codex CLI | model-mediated `spawn_agent`; max_depth=1 | Yes but children can't nest | None → prefer top-level parallel `codex exec` + manual git worktrees |
|
|
56
|
+
| Gemini CLI | subagents-as-tools; `@agent-name` | Yes BUT strictly hub-and-spoke (no nested calls) | None → restrict parallel workers to read-only roles unless given external worktrees |
|
|
57
|
+
| Cursor | Subagents w/ `is_background` frontmatter; `/multitask` | Yes | Opt-in per-subagent worktree or cloud VM |
|
|
58
|
+
| Antigravity | `invoke_subagent` (async by design); `/fork` | Yes | `/fork` workspace separation; per-subagent worktrees UNVERIFIED |
|
|
59
|
+
|
|
60
|
+
## Headless invocation templates (fallback workers)
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
claude claude -p "<prompt>" --output-format json --permission-mode acceptEdits
|
|
64
|
+
opencode opencode run "<prompt>" --auto --format json
|
|
65
|
+
codex codex exec --sandbox workspace-write --json -o <out-file> "<prompt>"
|
|
66
|
+
gemini gemini --output-format json "<prompt>"
|
|
67
|
+
agy agy -p --output-format json --print-timeout 15m "<prompt>"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Completion signal = process exit (Claude documents 0/non-zero contract).
|
|
71
|
+
NEVER pass secrets as arguments. Feature-detect old binaries via `--version`.
|
|
72
|
+
|
|
73
|
+
## Unknown platform procedure
|
|
74
|
+
|
|
75
|
+
1. WEB SEARCH: `<platform> official docs MCP configuration` and
|
|
76
|
+
`<platform> agent skills install path`.
|
|
77
|
+
2. Prefer official vendor domains over blog posts; note the date of sources.
|
|
78
|
+
3. Present findings + proposed config diff to the user. [approval gate]
|
|
79
|
+
4. Apply, verify, and offer to append findings to this table upstream.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// impact.mjs — reverse-dependency query: what is affected if <target> changes?
|
|
3
|
+
// Usage: node impact.mjs <tasks.yaml> <taskId-or-artifactPath>
|
|
4
|
+
// stdout: JSON {direct:[taskIds], transitive:[taskIds], artifacts:[paths]}
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { parseYaml } from './lib/yaml.mjs';
|
|
7
|
+
|
|
8
|
+
const [, , file, target] = process.argv;
|
|
9
|
+
if (!file || !target) { console.error('usage: impact.mjs <tasks.yaml> <taskId-or-artifactPath>'); process.exit(4); }
|
|
10
|
+
|
|
11
|
+
const { data } = parseYaml(readFileSync(file, 'utf8'), { filename: file });
|
|
12
|
+
const tasks = data?.tasks;
|
|
13
|
+
if (!Array.isArray(tasks)) { console.error(`${file}: missing 'tasks:' sequence`); process.exit(4); }
|
|
14
|
+
|
|
15
|
+
// Resolve target to a seed set of task ids.
|
|
16
|
+
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
17
|
+
let seeds;
|
|
18
|
+
if (byId.has(target)) {
|
|
19
|
+
seeds = [target];
|
|
20
|
+
} else {
|
|
21
|
+
// Artifact-path mode: any task whose artifacts[] or file_ownership[] covers it.
|
|
22
|
+
seeds = tasks
|
|
23
|
+
.filter((t) => (t.artifacts ?? []).includes(target) || (t.file_ownership ?? []).includes(target))
|
|
24
|
+
.map((t) => t.id);
|
|
25
|
+
if (seeds.length === 0) { console.error(`no task claims '${target}' (not an id, artifact, or ownership glob)`); process.exit(4); }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Reverse edges: dependents[prereq] = tasks that depend on it.
|
|
29
|
+
const dependents = new Map();
|
|
30
|
+
for (const t of tasks) {
|
|
31
|
+
for (const dep of t.depends_on ?? []) {
|
|
32
|
+
if (!dependents.has(dep)) dependents.set(dep, new Set());
|
|
33
|
+
dependents.get(dep).add(t.id);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Direct = immediate dependents of seeds; transitive = everything beyond.
|
|
38
|
+
const direct = new Set();
|
|
39
|
+
const transitive = new Set();
|
|
40
|
+
for (const s of seeds) for (const d of dependents.get(s) ?? []) direct.add(d);
|
|
41
|
+
transitive.clear();
|
|
42
|
+
{
|
|
43
|
+
const seen = new Set([...seeds, ...direct]);
|
|
44
|
+
const f = [...direct];
|
|
45
|
+
while (f.length) {
|
|
46
|
+
const cur = f.pop();
|
|
47
|
+
for (const d of dependents.get(cur) ?? []) {
|
|
48
|
+
if (seen.has(d)) continue;
|
|
49
|
+
seen.add(d); transitive.add(d); f.push(d);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Artifacts owned by affected tasks (what files the ripple touches).
|
|
55
|
+
const artifacts = new Set();
|
|
56
|
+
for (const id of [...direct, ...transitive]) {
|
|
57
|
+
for (const a of byId.get(id)?.artifacts ?? []) artifacts.add(a);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log(JSON.stringify({
|
|
61
|
+
direct: [...direct].sort(),
|
|
62
|
+
transitive: [...transitive].sort(),
|
|
63
|
+
artifacts: [...artifacts].sort(),
|
|
64
|
+
}, null, 2));
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// graph.mjs — shared dependency-graph utilities for archgen scripts.
|
|
2
|
+
// WHY a lib: validate.mjs, next-tasks.mjs and verify-plan.mjs must agree on
|
|
3
|
+
// cycle detection and wave semantics; three copies would drift (Metis finding).
|
|
4
|
+
|
|
5
|
+
/** Build adjacency (prerequisite → dependents) + reverse maps from tasks.
|
|
6
|
+
* Normative semantics: depends_on lists PREREQUISITE ids; a task is ready iff
|
|
7
|
+
* every prerequisite is done; wave 1 = empty depends_on. */
|
|
8
|
+
export function buildGraph(tasks) {
|
|
9
|
+
/** @type {Map<string, string[]>} prereq -> [dependent ids] */
|
|
10
|
+
const dependents = new Map();
|
|
11
|
+
/** @type {Map<string, string[]>} id -> prerequisite ids */
|
|
12
|
+
const prerequisites = new Map();
|
|
13
|
+
const byId = new Map();
|
|
14
|
+
for (const t of tasks) {
|
|
15
|
+
if (byId.has(t.id)) throw new GraphError(`duplicate task id: ${t.id}`);
|
|
16
|
+
byId.set(t.id, t);
|
|
17
|
+
prerequisites.set(t.id, Array.isArray(t.depends_on) ? t.depends_on : []);
|
|
18
|
+
}
|
|
19
|
+
for (const t of tasks) {
|
|
20
|
+
for (const dep of prerequisites.get(t.id)) {
|
|
21
|
+
if (!byId.has(dep)) throw new GraphError(`dangling depends_on reference: '${t.id}' depends on unknown task '${dep}'`);
|
|
22
|
+
if (!dependents.has(dep)) dependents.set(dep, []);
|
|
23
|
+
dependents.get(dep).push(t.id);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { byId, prerequisites, dependents };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class GraphError extends Error {}
|
|
30
|
+
|
|
31
|
+
/** Return an array of ids forming a cycle (empty if acyclic). Iterative DFS. */
|
|
32
|
+
export function findCycle(prerequisites) {
|
|
33
|
+
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
34
|
+
const color = new Map();
|
|
35
|
+
const parent = new Map();
|
|
36
|
+
for (const id of prerequisites.keys()) color.set(id, WHITE);
|
|
37
|
+
|
|
38
|
+
for (const start of prerequisites.keys()) {
|
|
39
|
+
if (color.get(start) !== WHITE) continue;
|
|
40
|
+
const stack = [[start, 0]];
|
|
41
|
+
color.set(start, GRAY);
|
|
42
|
+
while (stack.length) {
|
|
43
|
+
const [id, pi] = stack[stack.length - 1];
|
|
44
|
+
const deps = prerequisites.get(id);
|
|
45
|
+
if (pi < deps.length) {
|
|
46
|
+
stack[stack.length - 1] = [id, pi + 1];
|
|
47
|
+
const d = deps[pi];
|
|
48
|
+
if (!color.has(d)) continue; // dangling handled elsewhere
|
|
49
|
+
if (color.get(d) === GRAY) {
|
|
50
|
+
// Found a back-edge: reconstruct the cycle path d ← id ← … ← d
|
|
51
|
+
const cycle = [d];
|
|
52
|
+
let cur = id;
|
|
53
|
+
while (cur !== d) { cycle.push(cur); cur = parent.get(cur); }
|
|
54
|
+
cycle.push(d);
|
|
55
|
+
return cycle.reverse();
|
|
56
|
+
}
|
|
57
|
+
if (color.get(d) === WHITE) { color.set(d, GRAY); parent.set(d, id); stack.push([d, 0]); }
|
|
58
|
+
} else {
|
|
59
|
+
color.set(id, BLACK);
|
|
60
|
+
stack.pop();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Topological waves over NOT-DONE tasks.
|
|
68
|
+
* @param {Map<string,any>} byId
|
|
69
|
+
* @param {Map<string,string[]>} prerequisites
|
|
70
|
+
* @returns {{waves: Array<Array<any>>, blockedByFailure: string[]}}
|
|
71
|
+
* Wave membership: every prerequisite is done. Tasks whose TRANSITIVE
|
|
72
|
+
* prerequisites include any failed task are excluded entirely and reported in
|
|
73
|
+
* blockedByFailure (no silent cascades — orchestrator re-plans around them). */
|
|
74
|
+
export function computeWaves(byId, prerequisites) {
|
|
75
|
+
const done = new Set();
|
|
76
|
+
const failed = new Set();
|
|
77
|
+
for (const [id, t] of byId) {
|
|
78
|
+
if (t.status === 'done') done.add(id);
|
|
79
|
+
if (t.status === 'failed') failed.add(id);
|
|
80
|
+
}
|
|
81
|
+
// Propagate failure transitively.
|
|
82
|
+
const blockedByFailure = new Set();
|
|
83
|
+
let changed = true;
|
|
84
|
+
while (changed) {
|
|
85
|
+
changed = false;
|
|
86
|
+
for (const [id, deps] of prerequisites) {
|
|
87
|
+
if (blockedByFailure.has(id) || failed.has(id)) continue;
|
|
88
|
+
if (deps.some((d) => failed.has(d) || blockedByFailure.has(d))) {
|
|
89
|
+
blockedByFailure.add(id); changed = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const remaining = [...byId.keys()].filter((id) =>
|
|
95
|
+
!done.has(id) && !failed.has(id) && !blockedByFailure.has(id));
|
|
96
|
+
const waves = [];
|
|
97
|
+
const satisfied = new Set(done);
|
|
98
|
+
let frontier = remaining.filter((id) => prerequisites.get(id).every((d) => satisfied.has(d)));
|
|
99
|
+
const queued = new Set();
|
|
100
|
+
|
|
101
|
+
while (frontier.length) {
|
|
102
|
+
frontier.sort(); // deterministic output for identical inputs
|
|
103
|
+
waves.push(frontier.map((id) => byId.get(id)));
|
|
104
|
+
for (const id of frontier) { satisfied.add(id); queued.add(id); }
|
|
105
|
+
const next = [];
|
|
106
|
+
for (const id of remaining) {
|
|
107
|
+
if (queued.has(id) || satisfied.has(id)) continue;
|
|
108
|
+
if (prerequisites.get(id).every((d) => satisfied.has(d))) next.push(id);
|
|
109
|
+
}
|
|
110
|
+
frontier = next;
|
|
111
|
+
}
|
|
112
|
+
return { waves, blockedByFailure: [...blockedByFailure].sort() };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** First ownership conflict inside a single wave, or null.
|
|
116
|
+
* Ownership globs are treated as opaque strings for equality here — two tasks
|
|
117
|
+
* in the same wave must not claim the SAME literal glob (conservative rule that
|
|
118
|
+
* needs no glob-engine dependency). */
|
|
119
|
+
export function findOwnershipConflict(wave) {
|
|
120
|
+
const seen = new Map();
|
|
121
|
+
for (const t of wave) {
|
|
122
|
+
for (const g of t.file_ownership ?? []) {
|
|
123
|
+
if (seen.has(g)) return { glob: g, a: seen.get(g), b: t.id };
|
|
124
|
+
seen.set(g, t.id);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|