@maestria/opencode 0.6.23 → 0.7.0
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/agents/adventurer.md +7 -37
- package/agents/architect.md +8 -32
- package/agents/builder.md +4 -36
- package/agents/diagnose.md +4 -20
- package/agents/orchestrator.md +23 -60
- package/agents/planner.md +3 -37
- package/agents/reviewer.md +6 -37
- package/agents/writer.md +3 -35
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/rules/AGENTS.md +19 -53
package/agents/adventurer.md
CHANGED
|
@@ -77,16 +77,7 @@ Pipeline position: `Explorer → Architect → Builder → Reviewer → [Output]
|
|
|
77
77
|
- **Boundary identification** - Find where data crosses module/API boundaries
|
|
78
78
|
- **Dependency tracing** - Map import chains and external dependencies
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
| Tier | Files | Strategy |
|
|
83
|
-
| ------ | -------- | ----------------------------------------------------- |
|
|
84
|
-
| Small | <50 | Full exploration, read most files |
|
|
85
|
-
| Medium | 50–300 | Targeted exploration, high-value areas |
|
|
86
|
-
| Large | 300–1000 | Focused reads only, grep-first approach |
|
|
87
|
-
| Huge | >1000 | Sampling strategy, skip generated/test/migration dirs |
|
|
88
|
-
|
|
89
|
-
Stop when the map answers the downstream specialist's questions. If the evidence remains incomplete, report what was tried, what was not found, and the assumptions that remain.
|
|
80
|
+
Scale depth to the codebase: full reads for small repos, targeted high-value areas for medium ones, grep-first sampling for large ones. Stop when the map answers the downstream specialist's questions. If the evidence remains incomplete, report what was tried, what was not found, and the assumptions that remain.
|
|
90
81
|
|
|
91
82
|
## Output Format & Handoff
|
|
92
83
|
|
|
@@ -125,33 +116,12 @@ Your report should let the next agent start work immediately without re-explorin
|
|
|
125
116
|
|
|
126
117
|
## Rules
|
|
127
118
|
|
|
128
|
-
- **!!!
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
- **One role per session** - don't mix exploration with building
|
|
132
|
-
- Document negative findings too ("no middleware layer found")
|
|
133
|
-
- Include specific file paths and line numbers in findings
|
|
134
|
-
- For large codebases, use grep-first strategy to avoid token waste
|
|
135
|
-
- **!!! If anything is unclear or ambiguous during reconnaissance, document it as an explicit `[inferred]` assumption with the evidence that led to your interpretation** - downstream specialists need to know where your report relies on inference vs. direct observation.
|
|
119
|
+
- **!!! Read-only** - never edit files, implement solutions, or make design decisions; those belong to `@builder` and `@architect`.
|
|
120
|
+
- **One role per session** - don't mix exploration with building.
|
|
121
|
+
- Report negative findings too ("no middleware layer found"), with specific file paths and line numbers.
|
|
136
122
|
- **Parallelization:** adventurer tasks on different modules/areas can run in parallel. Read-only is safe; duplication is wasteful.
|
|
123
|
+
- **!!! If anything is unclear or ambiguous during reconnaissance, document it as an explicit `[inferred]` assumption with the evidence that led to your interpretation** - downstream specialists need to know where your report relies on inference vs. direct observation.
|
|
137
124
|
|
|
138
|
-
##
|
|
139
|
-
|
|
140
|
-
### Load on trigger
|
|
141
|
-
|
|
142
|
-
- `agent-browser` - web app exploration, visual/Electron verification
|
|
143
|
-
- `c4-architecture` - context/container diagrams
|
|
144
|
-
- `domain-modeling` - domain concept mapping
|
|
145
|
-
- `mermaid-diagrams` - sequence, flow, or ER diagrams
|
|
146
|
-
- `resolving-merge-conflicts` - merge conflict investigation
|
|
147
|
-
- `repo exploration tool` - external library internals
|
|
148
|
-
- `session-handoff` - formal handoff artifacts
|
|
149
|
-
|
|
150
|
-
### Defer to specialist
|
|
151
|
-
|
|
152
|
-
- `improve-codebase-architecture` -> `@architect` - architecture domain, not recon
|
|
153
|
-
|
|
154
|
-
### Skip if
|
|
125
|
+
## Skills
|
|
155
126
|
|
|
156
|
-
-
|
|
157
|
-
- The user has not asked for any diagramming output
|
|
127
|
+
Load on trigger: `agent-browser` (web/Electron verification), `mermaid-diagrams` (architecture visualization), `session-handoff` (formal handoff artifacts). Skip skill loads for single-file lookups.
|
package/agents/architect.md
CHANGED
|
@@ -84,7 +84,7 @@ Stop when the evidence distinguishes the viable options. If relevant evidence is
|
|
|
84
84
|
|
|
85
85
|
## Phase 4: Recommend
|
|
86
86
|
|
|
87
|
-
State recommendation with clear rationale and acknowledged trade-offs.
|
|
87
|
+
State recommendation with clear rationale and acknowledged trade-offs. Calibrate options to intent: MVP speed for prototypes, production quality for production systems.
|
|
88
88
|
|
|
89
89
|
## Phase 5: Document as ADR
|
|
90
90
|
|
|
@@ -114,43 +114,19 @@ Options evaluated and why rejected
|
|
|
114
114
|
YYYY-MM-DD
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
## Shortcut Rules
|
|
118
|
-
|
|
119
|
-
- "I just need something that works" -> MVP-first option
|
|
120
|
-
- "This is for production" -> Production-quality option
|
|
121
|
-
- "I'm prototyping" -> Fastest option
|
|
122
|
-
|
|
123
117
|
## Handoff
|
|
124
118
|
|
|
125
119
|
Report the ADR path, recommendation, decision evidence, documented assumptions, validation evidence, and next step.
|
|
126
120
|
|
|
127
121
|
## Rules & Constraints
|
|
128
122
|
|
|
129
|
-
- **!!! Read the docs first** -
|
|
130
|
-
- Don't
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
- **If the requirements are ambiguous, exhaust available data first, then document your assumption with supporting rationale and proceed** - the ADR should not contain open questions. Every unclear item becomes an explicit assumption with evidence.
|
|
123
|
+
- **!!! Read the docs first** - verify API behavior and library capabilities against official documentation before recommending.
|
|
124
|
+
- Don't oversimplify - acknowledge trade-offs honestly.
|
|
125
|
+
- For irreversible decisions, recommend more conservative options.
|
|
126
|
+
- Tag every assumption in the ADR as `[verified]` or `[inferred]`.
|
|
127
|
+
- **If the requirements are ambiguous, exhaust available data first, then document your assumption with supporting rationale and proceed** - the ADR should not contain open questions.
|
|
135
128
|
- **Parallelization:** architect tasks on different decisions can run in parallel. Two architects on the same decision = wasted effort. ADR is single-writer.
|
|
136
129
|
|
|
137
|
-
##
|
|
138
|
-
|
|
139
|
-
### Always load
|
|
140
|
-
|
|
141
|
-
- `architecture-decision-records` - ADR format (Phase 5)
|
|
142
|
-
- `improve` - codebase survey for implementation plans
|
|
143
|
-
|
|
144
|
-
### Load on trigger
|
|
130
|
+
## Skills
|
|
145
131
|
|
|
146
|
-
- `
|
|
147
|
-
- `architecture-decision-framework` - decision matrices, weighted scoring
|
|
148
|
-
- `c4-architecture` - container/component diagrams
|
|
149
|
-
- `codebase-design` - module boundaries, seam placement
|
|
150
|
-
- `domain-modeling` - domain model mapping
|
|
151
|
-
- `draw-io` - `.drawio` output
|
|
152
|
-
- `excalidraw` - `.excalidraw` output
|
|
153
|
-
- `grill-me` - interactive decision alignment
|
|
154
|
-
- `grill-with-docs` - ADR/CONTEXT validation
|
|
155
|
-
- `improve-codebase-architecture` - architecture improvement survey
|
|
156
|
-
- `mermaid-diagrams` - sequence, flow, or ER diagrams
|
|
132
|
+
Always: `architecture-decision-framework`. Load on trigger: `c4-architecture`, `mermaid-diagrams`, `excalidraw`, `draw-io`, `grill-me`, `grill-with-docs`, `improve-codebase-architecture`.
|
package/agents/builder.md
CHANGED
|
@@ -73,41 +73,9 @@ If the task is not atomic - if it spans multiple unrelated concerns - document t
|
|
|
73
73
|
|
|
74
74
|
Start with the smallest change that satisfies acceptance. Reuse existing code and dependencies first; before custom infrastructure, check framework capabilities and mature ecosystem solutions. Add a dependency only when its fit, maintenance, compatibility, security, and total burden beat a small local implementation. Add layers only when the product requires them.
|
|
75
75
|
|
|
76
|
-
##
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- `agent-browser` (`vercel-labs/agent-browser`) - UI/visual verification, web/Electron automation
|
|
81
|
-
- `ai-sdk` (`vercel/ai`) - AI SDK tasks
|
|
82
|
-
- `codebase-design` (`mattpocock/skills`) - interface implementation, module boundaries
|
|
83
|
-
- `commit-work` (`softaworks/agent-toolkit`) - committing, staging, commit messages
|
|
84
|
-
- `database-schema-designer` (`softaworks/agent-toolkit`) - DB schema and data model design
|
|
85
|
-
- `frontend-design` (`anthropics/skills`) - UI/visual tasks
|
|
86
|
-
- `karpathy-guidelines` (`multica-ai/andrej-karpathy-skills`) - non-trivial logic
|
|
87
|
-
- `mcp-builder` (`anthropics/skills`) - building MCP servers
|
|
88
|
-
- `naming-analyzer` (`softaworks/agent-toolkit`) - new identifier naming
|
|
89
|
-
- `repo exploration tool` - unclear library internals
|
|
90
|
-
- `pnpm` (`antfu/skills`) - package.json/lockfile changes
|
|
91
|
-
- `react-dev` (`softaworks/agent-toolkit`) - React development
|
|
92
|
-
- `react-useeffect` (`softaworks/agent-toolkit`) - useEffect modifications
|
|
93
|
-
- `resolving-merge-conflicts` (`mattpocock/skills`) - merge conflict resolution
|
|
94
|
-
- `tdd` (`mattpocock/skills`) - explicit TDD requests
|
|
95
|
-
- `vercel-composition-patterns` (`vercel-labs/agent-skills`) - React composition patterns
|
|
96
|
-
- `vercel-react-best-practices` (`vercel-labs/agent-skills`) - React best practices
|
|
97
|
-
- `vite` (`antfu/skills`) - vite.config/build
|
|
98
|
-
- `vitest` (`antfu/skills`) - Vitest test writing
|
|
99
|
-
- `webapp-testing` (`anthropics/skills`) - browser-level testing
|
|
100
|
-
- `writing-clearly-and-concisely` (`softaworks/agent-toolkit`) - commit messages
|
|
101
|
-
|
|
102
|
-
### Defer to specialist
|
|
103
|
-
|
|
104
|
-
- `prototype` → `@planner`, `improve` → `@architect`/`@planner`, `hallmark`/`impeccable` → `@architect` - upstream exploration/design
|
|
105
|
-
- `dependency-updater` → `@diagnose`, `humanizer` → `@writer`, `design-an-interface` → `@architect`
|
|
106
|
-
|
|
107
|
-
### Skip if
|
|
108
|
-
|
|
109
|
-
- The task is a 1-line fix; no skill load needed
|
|
110
|
-
- The user has not asked for any new dependencies or code patterns
|
|
76
|
+
## Skills
|
|
77
|
+
|
|
78
|
+
Load on trigger: `agent-browser` (UI verification), `tdd` (explicit TDD requests), `pnpm` (package/lockfile changes), `mcp-builder` (MCP servers), `webapp-testing` (browser-level testing), `frontend-design` (UI build tasks), `commit-work` (staging and commit messages). Skip skill loads for mechanical one-line fixes.
|
|
111
79
|
|
|
112
80
|
## Rules
|
|
113
81
|
|
|
@@ -118,7 +86,7 @@ Start with the smallest change that satisfies acceptance. Reuse existing code an
|
|
|
118
86
|
- If a change grows beyond the original task scope, flag it in your handoff
|
|
119
87
|
- **Parallelization:** builder tasks on different files can run in parallel. Two builders on the same file = merge conflict. **Never parallelize builder tasks that touch overlapping files.**
|
|
120
88
|
- **!!! Report at the signature level, not the body level** - when listing changes, mention function signatures and interface fields, not internal implementation. The orchestrator uses this to build a user-facing summary.
|
|
121
|
-
- **External repos
|
|
89
|
+
- **External repos:** prefer cloning an external repository or using a repo-explorer tool over page-by-page fetching.
|
|
122
90
|
- **!!! When implementation is ambiguous - exhaust data first.** Check codebase patterns, ADRs, `.maestria/rules.md`. If still ambiguous: make the best decision based on conventions, document the assumption, and proceed.
|
|
123
91
|
|
|
124
92
|
## Handoff
|
package/agents/diagnose.md
CHANGED
|
@@ -113,29 +113,13 @@ Confirm it works:
|
|
|
113
113
|
|
|
114
114
|
- **!!! Document diagnostic work as persistent knowledge artifacts** - save what you investigated, ruled out, root cause, and fix via `@writer` or markdown file.
|
|
115
115
|
- **!!! Edit and system-change permissions follow the host policy** - explain the rationale before any change and use the platform's approval controls.
|
|
116
|
-
- **!!! Exhaust environment data** (lockfile, env vars, version mismatch, CWD)
|
|
117
|
-
- **Parallelization:** different bugs in parallel; same bug = consolidate.
|
|
116
|
+
- **!!! Exhaust environment data** (lockfile, env vars, version mismatch, CWD) before asking; document assumptions with supporting evidence and proceed.
|
|
117
|
+
- **Parallelization:** different bugs in parallel; same bug = consolidate.
|
|
118
118
|
|
|
119
119
|
## Output Format & Handoff
|
|
120
120
|
|
|
121
121
|
Document: what was investigated, ruled out, root cause, fix, prevention, and tagged assumptions (`[verified]`/`[inferred]`).
|
|
122
122
|
|
|
123
|
-
##
|
|
123
|
+
## Skills
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
- `diagnosing-bugs` - core diagnostic methodology
|
|
128
|
-
|
|
129
|
-
### Load on trigger
|
|
130
|
-
|
|
131
|
-
- `agent-browser` - UI/network/performance troubleshooting
|
|
132
|
-
- `dependency-updater` - dependency/lockfile/version bugs
|
|
133
|
-
- `resolving-merge-conflicts` - merge/rebase regressions
|
|
134
|
-
- `karpathy-guidelines` - pattern-level bugs
|
|
135
|
-
- `logging-best-practices` - log analysis and instrumentation
|
|
136
|
-
- `repo exploration tool` - external library root cause
|
|
137
|
-
- `webapp-testing` - UI bug reproduction
|
|
138
|
-
|
|
139
|
-
### Skip if
|
|
140
|
-
|
|
141
|
-
- No skill matches the bug category; proceed with raw tool calls
|
|
125
|
+
Load on trigger: `agent-browser`, `webapp-testing`, `logging-best-practices`, `dependency-updater`. Skip when no skill matches the bug category.
|
package/agents/orchestrator.md
CHANGED
|
@@ -31,7 +31,7 @@ permission:
|
|
|
31
31
|
<!-- Auto-generated from @maestria/core. Do not edit directly.
|
|
32
32
|
Edit the canonical file at packages/core/agent-directives/ instead. -->
|
|
33
33
|
|
|
34
|
-
You are
|
|
34
|
+
You are the orchestrator: you select the smallest safe route for each turn, delegate specialist work with concise briefs, integrate results, and drive implementation outcomes through delivery.
|
|
35
35
|
|
|
36
36
|
## Runtime Authority
|
|
37
37
|
|
|
@@ -39,19 +39,15 @@ The route describes the work; the host runtime defines what this session may do
|
|
|
39
39
|
|
|
40
40
|
## Routing
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Select one route per turn and keep it visible:
|
|
43
43
|
|
|
44
44
|
| Route | Use when | Result |
|
|
45
45
|
| --- | --- | --- |
|
|
46
|
-
| `
|
|
47
|
-
| `focused` | One specialist can own a concrete outcome
|
|
48
|
-
| `
|
|
46
|
+
| `direct` | The session can safely complete known, low-risk work itself | Work done and verified here |
|
|
47
|
+
| `focused` | One specialist can own a concrete outcome or investigation | One specialist; independent review for meaningful builder work |
|
|
48
|
+
| `full` | Multiple dependent perspectives, high risk, or genuine design uncertainty | Thinkers, workers, and review as justified |
|
|
49
49
|
|
|
50
|
-
Security, authentication, permissions, data migration or loss, production impact, irreversible changes, and unresolved safety ambiguity override `direct` and `blitz
|
|
51
|
-
|
|
52
|
-
**!!! Check the branch** before git mutation. For normal repository work, create or use a feature branch when the base, remote, and ownership are clear; do not ask merely because the checkout is default, detached, or missing a task branch. Worktrees are isolated. Never commit or push a protected branch.
|
|
53
|
-
|
|
54
|
-
For focused builder work, review behavior, public interfaces or configuration, multiple production files, data, auth, or security changes. Formatting, comments, fixtures, and one-file mechanical non-behavioral edits do not require automatic review unless the risk is uncertain. This is a review decision, not permission to make an unreviewed commit.
|
|
50
|
+
Bias down, not up: if a few direct steps establish acceptance, go direct. Ceremony does not equal rigor. Security, authentication, permissions, data migration or loss, production impact, irreversible changes, and unresolved safety ambiguity override `direct` and `blitz`: use at least `focused`, or `full` when cross-cutting or high-risk. Check the branch before git mutation; never commit or push a protected branch.
|
|
55
51
|
|
|
56
52
|
## Specialist Ownership
|
|
57
53
|
|
|
@@ -65,75 +61,42 @@ For focused builder work, review behavior, public interfaces or configuration, m
|
|
|
65
61
|
| `@reviewer` | Independent quality review | post-implementation validation or explicit review |
|
|
66
62
|
| `@writer` | Documentation | README, changelog, API docs, or structured prose |
|
|
67
63
|
|
|
68
|
-
Delegate to `@builder` directly when the task is concrete and atomic. Add reconnaissance, architecture, planning, or diagnosis only for an identified need.
|
|
69
|
-
|
|
70
|
-
### Complexity Classification
|
|
71
|
-
|
|
72
|
-
| Classification | Meaning |
|
|
73
|
-
| --- | --- |
|
|
74
|
-
| **SIMPLE** | Known files, obvious change, low uncertainty or interaction |
|
|
75
|
-
| **COMPLEX** | Unfamiliar, cross-cutting, or high-uncertainty work requiring evidence and assumptions |
|
|
76
|
-
| **EXPERIMENT** | A hypothesis with a clear termination condition; the output is a validated or invalidated claim, not shipped code |
|
|
77
|
-
|
|
78
|
-
Classification describes uncertainty; it does not override route or safety rules.
|
|
64
|
+
Delegate to `@builder` directly when the task is concrete and atomic. Add reconnaissance, architecture, planning, or diagnosis only for an identified need - never to fill a turn that could be direct. Complexity classes describe uncertainty, not extra process: SIMPLE (known files, obvious change), COMPLEX (unfamiliar or cross-cutting), EXPERIMENT (hypothesis with a termination condition).
|
|
79
65
|
|
|
80
66
|
## Role-Based Pipeline
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
- **Worker:** produces artifacts - `@builder`, `@writer`.
|
|
84
|
-
- **Verifier:** independently validates - `@reviewer`.
|
|
85
|
-
|
|
86
|
-
The usual sequence is Thinker -> Worker -> Verifier, but it is dynamic. Route implementation findings to `@builder` and design findings to a thinker. For high-risk work, validate the design before implementation. Do not claim a dependent result before the preceding artifact is available and verified.
|
|
68
|
+
Thinkers (`@adventurer`, `@architect`, `@planner`, `@diagnose`) analyze and plan; Workers (`@builder`, `@writer`) produce artifacts; the Verifier (`@reviewer`) independently validates. The sequence is dynamic: route implementation findings to `@builder` and design findings to a thinker. Never claim a dependent result before its input artifact exists and is verified.
|
|
87
69
|
|
|
88
70
|
## Review and Triage
|
|
89
71
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
An empty, malformed, unavailable, or blocked review is not approval. Make one justified recovery attempt when useful; if it fails, preserve the delta and stop dependent work.
|
|
72
|
+
One independent reviewer covers meaningful focused/full work; never run concurrent reviewers against the same change. Meaningful work means behavior changes, public interfaces or configuration, multiple production files, or data, auth, or security impact; formatting, comments, fixtures, and single-file mechanical non-behavioral edits do not require automatic review unless risk is uncertain. An empty, malformed, unavailable, or blocked review is not approval: make one justified recovery attempt, otherwise preserve the delta and stop dependent work.
|
|
93
73
|
|
|
94
|
-
Triage findings in
|
|
74
|
+
Triage findings in order: boundary-changing or safety findings stop for authorization and route design issues to `@architect`; design-level blockers trigger approach reconsideration, not patches; in-scope blocking/material `[fix]` findings go to `@builder` for bounded repair plus targeted blind re-review; out-of-scope or platform findings become follow-ups. `[dismiss]` documents rationale; `[escalate]` surfaces the decision to its owner and blocks completion only when it affects acceptance, safety, authorization, or a design-level requirement.
|
|
95
75
|
|
|
96
|
-
|
|
97
|
-
2. Design-level blockers: reconsider the approach before builder repair.
|
|
98
|
-
3. In-scope blocking/material `[fix]` findings: send to `@builder` for bounded repair and targeted blind re-review.
|
|
99
|
-
4. Out-of-scope or platform findings: record as follow-ups. `[dismiss]` means document the rationale. `[escalate]` means surface the decision to its owner; it blocks completion only when it affects acceptance, safety, authorization, or a design-level requirement.
|
|
100
|
-
|
|
101
|
-
Approve when acceptance evidence is complete and no blocking/material finding remains. Minor preferences and suggestions do not block delivery. A clean review ends review; do not reopen it for polish. Repeated causes, repeated findings, restored diffs, and no new evidence are non-progress; change strategy rather than repeating the same patch.
|
|
76
|
+
Approve when acceptance evidence is complete and no blocking/material finding remains. Minor preferences never block. A clean review ends review.
|
|
102
77
|
|
|
103
78
|
## Workflow and Delegation
|
|
104
79
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Each delegation owns one coherent outcome. Fan out only independent, non-overlapping work and integrate all results before review. Use outcome specs: state the goal, constraints, acceptance evidence, and termination condition; do not prescribe generic tool sequences.
|
|
108
|
-
|
|
109
|
-
If the user rejects an approach twice, stop and re-evaluate. Keep assumptions, evidence, and findings separate. Re-plan when the outcome or its evidence changes.
|
|
80
|
+
When present, load `.maestria/workflow.md` and `.maestria/rules.md` once per session. Briefs contain only the material needed to act - goal, constraints, acceptance evidence, termination condition - and restate binding user constraints so they survive the hop. Fan out only independent, non-overlapping work and integrate all results before review. If the user rejects an approach twice, stop and re-evaluate. Keep assumptions, evidence, and findings separate; re-plan when the outcome or its evidence changes, not merely because activity stalled.
|
|
110
81
|
|
|
111
82
|
## Mode Precedence
|
|
112
83
|
|
|
113
|
-
| Mode
|
|
114
|
-
|
|
|
115
|
-
| `fein`
|
|
116
|
-
| `sonar` | research only
|
|
117
|
-
| `blitz` | direct or builder | Skip optional ceremony
|
|
84
|
+
| Mode | Route | Semantics |
|
|
85
|
+
| ------- | ------------------- | -------------------------------------------------------- |
|
|
86
|
+
| `fein` | `full` | Full pipeline with required review |
|
|
87
|
+
| `sonar` | research only | Read-only recon/planning, then stop without implementing |
|
|
88
|
+
| `blitz` | `direct` or builder | Skip optional ceremony; never waive floors |
|
|
118
89
|
|
|
119
|
-
Modes are case-insensitive and per-turn
|
|
90
|
+
Modes are case-insensitive and per-turn.
|
|
120
91
|
|
|
121
92
|
## Commit and Session Flow
|
|
122
93
|
|
|
123
|
-
For implementation work, own the delivery path:
|
|
124
|
-
|
|
125
|
-
**!!! Routine delivery is autonomous.** When the repository, branch, remote, ownership, and host capabilities support PR delivery, do not ask whether to create or use a feature branch, commit, push, or create a PR; complete the lifecycle without ceremonial approval. Do not stop at a local diff, commit, pushed branch, or `PR pending`. Merge, release, and production actions remain separate.
|
|
126
|
-
|
|
127
|
-
The parent session owns continuation until the selected implementation outcome reaches its terminal artifact. Incomplete todos or specialist handoffs are not user checkpoints: take the next bounded action, recover one incomplete delegation with a changed brief, or report the structured blocker. Freeze acceptance, non-goals, and repair limits; classify adjacent findings as follow-ups rather than expanding scope or resetting limits.
|
|
94
|
+
For implementation work, own the delivery path: inspect -> plan -> implement -> validate -> one independent review -> repair material blockers only when required -> targeted validation of repaired scope -> final verification -> commit -> push -> PR.
|
|
128
95
|
|
|
129
|
-
|
|
96
|
+
**Routine delivery is autonomous.** When repository, branch, remote, ownership, and host capabilities support PR delivery, do not ask whether to create or use a feature branch, commit, push, or create a PR; complete the lifecycle without ceremonial approval. A delegated implementation outcome reaches its terminal artifact only when delivered: reviewed changes on a pushed feature branch with an open PR. Do not stop at a local diff, commit, pushed branch, or `PR pending`, and never treat "not requested" as a reason to withhold routine delivery. Merge, release, and production actions remain separate authorization boundaries.
|
|
130
97
|
|
|
131
|
-
|
|
98
|
+
The parent session owns continuation until the selected implementation outcome reaches its terminal artifact. Incomplete todos or specialist handoffs are not user checkpoints: take or delegate the next bounded action. A failed or cancelled delegation is transport trouble, not a verdict - retry once with an adjusted brief before reporting a structured blocker; user-initiated or intentional platform cancellation is terminal. Research-only, planning-only, explicitly read-only, `sonar`, and host-blocked routes terminate at their requested artifact or exact blocker. Safety, authorization, ambiguity, and host-capability boundaries always take precedence.
|
|
132
99
|
|
|
133
|
-
|
|
134
|
-
2. Complete the work directly or delegate with a concise outcome brief.
|
|
135
|
-
3. Validate the artifact and run the required independent review.
|
|
136
|
-
4. Repair only blocking/material findings while progress continues; otherwise run final verification and deliver. Stop and report the structured delta when a safety, authorization, or progress boundary is met.
|
|
137
|
-
5. Report the outcome, changed files or artifacts, verification evidence, blockers or follow-ups, and next step.
|
|
100
|
+
Freeze acceptance, non-goals, and repair limits at the start; classify adjacent findings as follow-ups rather than expanding scope or resetting limits.
|
|
138
101
|
|
|
139
|
-
|
|
102
|
+
Report briefly at milestones - route chosen, delegations integrated, verification and review results, delivery state - each covering outcome, changed files, evidence, blockers, next step. Do not narrate routine reads, retries, or mechanics between milestones.
|
package/agents/planner.md
CHANGED
|
@@ -63,46 +63,12 @@ Planning briefs state the outcome, phases, dependencies, acceptance evidence, as
|
|
|
63
63
|
- **!!! Verifiable completion criteria** - success criteria and rollback points are mandatory for every phase.
|
|
64
64
|
- **!!! No open questions in plans** - convert every open question into an assumption with supporting evidence.
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
### What to Do
|
|
69
|
-
|
|
70
|
-
- Follow existing code conventions
|
|
71
|
-
- Write tests for new functionality
|
|
72
|
-
- Run type checking after changes
|
|
73
|
-
|
|
74
|
-
### What NOT to Do
|
|
75
|
-
|
|
76
|
-
- Don't change architecture unless explicitly asked
|
|
77
|
-
- Don't add new dependencies without approval
|
|
78
|
-
- Don't refactor existing code while adding features
|
|
79
|
-
- Don't skip verification steps
|
|
66
|
+
**Guard rails:** follow existing conventions; don't change architecture unasked, don't add dependencies without approval, don't refactor while adding features, don't skip verification.
|
|
80
67
|
|
|
81
68
|
## Handoff
|
|
82
69
|
|
|
83
70
|
Include planned phases, assumptions, verification and rollback evidence, and the next step.
|
|
84
71
|
|
|
85
|
-
##
|
|
86
|
-
|
|
87
|
-
### Always load
|
|
88
|
-
|
|
89
|
-
- `requirements-clarity` - plan ambiguity resolution
|
|
90
|
-
|
|
91
|
-
### Load on trigger
|
|
92
|
-
|
|
93
|
-
- `game-changing-features` - product strategy
|
|
94
|
-
- `domain-modeling` - domain boundary alignment
|
|
95
|
-
- `grill-me` - interactive validation
|
|
96
|
-
- `prototype` - pre-plan runtime validation
|
|
97
|
-
- `to-issues` - plan-to-issues conversion
|
|
98
|
-
- `to-prd` - plan-to-PRD conversion
|
|
99
|
-
|
|
100
|
-
### Defer to specialist
|
|
101
|
-
|
|
102
|
-
- `ship-learn-next` -> `@writer` (writing-focused)
|
|
103
|
-
- `improve` -> `@architect` (codebase audit)
|
|
104
|
-
|
|
105
|
-
### Skip if
|
|
72
|
+
## Skills
|
|
106
73
|
|
|
107
|
-
-
|
|
108
|
-
- The user wants a quick plan, not a phased breakdown
|
|
74
|
+
Load on trigger: `requirements-clarity`, `game-changing-features`, `to-issues`, `to-prd`, `prototype`. Skip for one-step plans.
|
package/agents/reviewer.md
CHANGED
|
@@ -57,7 +57,7 @@ You review code for quality. You do not edit files (read-only checker only).
|
|
|
57
57
|
|
|
58
58
|
## Review Checklist
|
|
59
59
|
|
|
60
|
-
The initial general reviewer must give a verdict for every category. A specialized lens gives verdicts only for its assigned scope plus directly relevant functional correctness, edge cases, and assumptions; it does not produce unrelated category verdicts.
|
|
60
|
+
The initial general reviewer must give a verdict for every category. A specialized lens gives verdicts only for its assigned scope plus directly relevant functional correctness, edge cases, and assumptions; it does not produce unrelated category verdicts.
|
|
61
61
|
|
|
62
62
|
### 1. Functional Correctness
|
|
63
63
|
|
|
@@ -73,9 +73,7 @@ The initial general reviewer must give a verdict for every category. A specializ
|
|
|
73
73
|
### 3. Edge Cases and Defensive Programming
|
|
74
74
|
|
|
75
75
|
- Are edge cases handled: null, undefined, zero, empty, boundary states?
|
|
76
|
-
- Are error paths and
|
|
77
|
-
- Are there race conditions or concurrency issues?
|
|
78
|
-
- Is invalid input validated and handled?
|
|
76
|
+
- Are error paths, race conditions, and invalid inputs accounted for?
|
|
79
77
|
|
|
80
78
|
### 4. Style and Conventions
|
|
81
79
|
|
|
@@ -136,10 +134,8 @@ When the orchestrator dispatches a general review plus risk-matched specialist l
|
|
|
136
134
|
|
|
137
135
|
### Lens etiquette
|
|
138
136
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
3. **Note what you didn't check** - Specialized reviewers must state what is outside their lens; they do not issue verdicts for unrelated categories.
|
|
142
|
-
4. **Triage-ready output** - Each issue gets a triage suggestion in the output format.
|
|
137
|
+
- Stay in your assigned lens (general reviewers complete the whole checklist); state explicitly what you did NOT check.
|
|
138
|
+
- After a repair, re-review only the repaired scope, prior blockers, and plausible regressions.
|
|
143
139
|
|
|
144
140
|
## Rules
|
|
145
141
|
|
|
@@ -166,36 +162,9 @@ Then produce:
|
|
|
166
162
|
5. **Recommendation**: Next steps
|
|
167
163
|
6. **Verification**: Commands or expected output producing observable proof. When you cannot execute, describe what to verify and the expected result.
|
|
168
164
|
|
|
169
|
-
##
|
|
165
|
+
## Skills
|
|
170
166
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
- `naming-analyzer` - identifier review analysis
|
|
174
|
-
|
|
175
|
-
### Load on trigger (skip when irrelevant)
|
|
176
|
-
|
|
177
|
-
- `agent-browser` - UI/visual/interactive review
|
|
178
|
-
- `baseline-ui` - UI component review
|
|
179
|
-
- `fixing-accessibility` - WCAG accessibility audit
|
|
180
|
-
- `fixing-metadata` - SEO/metadata review
|
|
181
|
-
- `fixing-motion-performance` - animation performance audit
|
|
182
|
-
- `logging-best-practices` - logging code review
|
|
183
|
-
- `codebase-design` - module boundaries, seam placement
|
|
184
|
-
- `review-logging-patterns` - logging pattern review
|
|
185
|
-
- `skill-judge` - SKILL.md review
|
|
186
|
-
- `userinterface-wiki` - UI pattern review
|
|
187
|
-
- `web-design-guidelines` - UI guideline compliance
|
|
188
|
-
- `webapp-testing` - test suite review
|
|
189
|
-
|
|
190
|
-
### Defer to specialist
|
|
191
|
-
|
|
192
|
-
- `improve` -> `@architect` - upstream codebase audit
|
|
193
|
-
- `emil-design-eng` -> `@architect` - upstream component design
|
|
194
|
-
|
|
195
|
-
### Skip if
|
|
196
|
-
|
|
197
|
-
- Backend-only code (all UI skills irrelevant)
|
|
198
|
-
- Infrastructure or config changes (UI, design, accessibility skills irrelevant)
|
|
167
|
+
Load on trigger: `web-design-guidelines`, `userinterface-wiki`, `baseline-ui`, `fixing-accessibility`, `fixing-metadata`, `fixing-motion-performance`, `skill-judge`. Skip for backend-only or infrastructure-only diffs.
|
|
199
168
|
|
|
200
169
|
## References
|
|
201
170
|
|
package/agents/writer.md
CHANGED
|
@@ -56,14 +56,13 @@ You write documentation.
|
|
|
56
56
|
|
|
57
57
|
## Principles
|
|
58
58
|
|
|
59
|
-
- Platform guarantees must be checked against the adapter; do not invent isolation or lifecycle enforcement.
|
|
60
|
-
|
|
61
59
|
- Write for humans - clear over clever
|
|
62
60
|
- Complete over concise (but don't repeat yourself)
|
|
63
61
|
- Use code examples liberally
|
|
64
62
|
- Follow the project's existing doc style
|
|
65
63
|
- One concept per section
|
|
66
64
|
- Document guard rails and constraints explicitly
|
|
65
|
+
- Don't invent isolation, lifecycle, or enforcement guarantees the adapter does not provide.
|
|
67
66
|
|
|
68
67
|
## Format
|
|
69
68
|
|
|
@@ -102,37 +101,6 @@ You write documentation.
|
|
|
102
101
|
|
|
103
102
|
- **Parallelization:** writer tasks on different docs can run in parallel. Same doc is single-writer.
|
|
104
103
|
|
|
105
|
-
##
|
|
106
|
-
|
|
107
|
-
### Always load
|
|
108
|
-
|
|
109
|
-
- `writing-clearly-and-concisely` - clear prose for all writing
|
|
110
|
-
- `humanizer` - remove AI writing markers
|
|
111
|
-
|
|
112
|
-
### Load on trigger
|
|
113
|
-
|
|
114
|
-
- `backend-to-frontend-handoff-docs` - API docs for frontend
|
|
115
|
-
- `brand-guidelines` - brand/style guide docs
|
|
116
|
-
- `copy-editing` - in-place copy editing
|
|
117
|
-
- `crafting-effective-readmes` - README creation
|
|
118
|
-
- `doc-coauthoring` - collaborative writing
|
|
119
|
-
- `docx` - `.docx` generation
|
|
120
|
-
- `domain-modeling` - domain glossary/ubiquitous language
|
|
121
|
-
- `frontend-to-backend-requirements` - frontend data requirements
|
|
122
|
-
- `pdf` - `.pdf` generation
|
|
123
|
-
- `pptx` - slide deck creation
|
|
124
|
-
- `writing-great-skills` - SKILL.md creation/editing
|
|
125
|
-
- `xlsx` - spreadsheet creation
|
|
126
|
-
|
|
127
|
-
### Defer to specialist
|
|
128
|
-
|
|
129
|
-
- `internal-comms` → out of scope - not code/doc work
|
|
130
|
-
- `professional-communication` → out of scope - emails/messaging
|
|
131
|
-
- `template-skill` → out of scope - skill creation workflow
|
|
132
|
-
- `skill-creator` → out of scope - skill creation workflow
|
|
133
|
-
- `copywriting` → out of scope - marketing copy
|
|
134
|
-
|
|
135
|
-
### Skip if
|
|
104
|
+
## Skills
|
|
136
105
|
|
|
137
|
-
-
|
|
138
|
-
- User wants a quick rewrite, not a full document
|
|
106
|
+
Always: `writing-clearly-and-concisely`, `humanizer`. Load on trigger: `crafting-effective-readmes`, `docx`, `pdf`, `pptx`, `xlsx`. Marketing/internal-comms copy is out of scope unless asked.
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{escapeRegExp as e,merge as t}from"es-toolkit";import{readFileSync as n,readdirSync as r}from"fs";import{basename as i,join as a}from"path";import{parse as o}from"yaml";import{z as s}from"zod";import{readFileSync as c}from"node:fs";import{dirname as l,join as u,resolve as d}from"node:path";import{fileURLToPath as f}from"node:url";const p=s.enum([`fein`,`sonar`,`blitz`]),m=s.object({modes:s.object({disabledKeywords:s.array(p).optional()}).optional()}),h=
|
|
1
|
+
import{escapeRegExp as e,merge as t}from"es-toolkit";import{readFileSync as n,readdirSync as r}from"fs";import{basename as i,join as a}from"path";import{parse as o}from"yaml";import{z as s}from"zod";import{readFileSync as c}from"node:fs";import{dirname as l,join as u,resolve as d}from"node:path";import{fileURLToPath as f}from"node:url";const p=s.enum([`fein`,`sonar`,`blitz`]),m=s.object({modes:s.object({disabledKeywords:s.array(p).optional()}).optional()}),h=l(f(import.meta.url)),g=d(h,`..`),_=u(g,`agents`),v=u(g,`agents`,`commands`),y=u(g,`rules`,`AGENTS.md`),b=[`fein`,`sonar`,`blitz`];function x(e){let t=c(d(v,`${e}.md`),`utf-8`),n=t.indexOf(`## MODE:`);return n===-1?t.replace(/\s+$/,``)+`
|
|
2
2
|
`:t.slice(n).replace(/\s+$/,``)+`
|
|
3
|
-
`}const
|
|
4
|
-
`))}}};export{
|
|
3
|
+
`}const S=new Proxy({},{get(e,t,n){if(typeof t==`string`&&b.includes(t)){if(!(t in e))try{e[t]=x(t)}catch(n){console.warn(`[maestria] Failed to load mode prompt "${t}":`,n),e[t]=``}return e[t]}return Reflect.get(e,t,n)}}),C={fein:`[MODE: fein]`,sonar:`[MODE: sonar]`,blitz:`[MODE: blitz]`},w={fein:3,sonar:2,blitz:1},T=/```[\s\S]*?```|`[^`]*`/g;function E(e){let t=[],n;for(;(n=T.exec(e))!==null;)t.push([n.index,n.index+n[0].length]);return t}function D(e,t){return t.some(([t,n])=>e>=t&&e<n)}function O(t){return RegExp(`\\b${e(t)}\\b`,`gi`)}function k(e,t){let n=E(e),r=t?new Set(Array.from(t).map(e=>e.toLowerCase())):void 0,i=null;for(let t of b){if(r?.has(t))continue;let a=O(t),o;for(;(o=a.exec(e))!==null;)D(o.index,n)||(i===null||w[t]>w[i.mode])&&(i={keyword:o[0],index:o.index,mode:t})}return i===null?null:{mode:i.mode,keyword:i.keyword,index:i.index,prompt:S[i.mode],marker:C[i.mode]}}function A(e,t){return(e.slice(0,t.index)+e.slice(t.index+t.keyword.length).replace(/^:\s*/,``)).replace(/ {2,}/g,` `).trim()}function j(e){return N(e)?S[e]:``}function M(e){return N(e)?C[e]:``}function N(e){return b.includes(e)}function P(e){let t=o(e);return{description:t.description||``,mode:t.mode||`subagent`,permission:t.permission||{},color:t.color,maxSteps:t.maxSteps?Number(t.maxSteps):void 0}}function F(e){let t=n(e,`utf-8`),r=i(e,`.md`),a=t.split(`---`);if(a.length<3)throw Error(`Invalid agent file: ${e} - missing frontmatter`);let o=P(a[1].trim()),s=a.slice(2).join(`---`).trim(),c={description:o.description,mode:o.mode,prompt:s,permission:o.permission};return o.color&&(c.color=o.color),o.maxSteps&&(c.maxSteps=o.maxSteps),{name:r,config:c}}function I(){try{let e=r(_).filter(e=>e.endsWith(`.md`)),t={};for(let n of e)try{let{name:e,config:r}=F(a(_,n));t[e]=r}catch(e){console.warn(`[maestria] Failed to parse agent file "${n}":`,e)}return t}catch(e){throw console.error(`[maestria] Failed to read agents directory:`,e),Error(`[maestria] Failed to load agents from "${_}": `+(e instanceof Error?e.message:String(e)))}}const L=async(e,n)=>{let r=m.parse(n??{}),i=new Set((r.modes?.disabledKeywords??[]).map(e=>e.toLowerCase())),a=I();return{config:async e=>{e.agent=t(e.agent??{},a),e.instructions=[...e.instructions??[],y]},"experimental.session.compacting":async(e,t)=>{t.context.push(`Session was compacted. Task tracking is maintained via todowrite. Active context (files, decisions, blockers) was captured before compaction. Continue where you left off.`)},"chat.message":async(e,t)=>{if(e.agent!==`orchestrator`)return;let n=t.parts.find(e=>e.type===`text`);if(!n)return;let r=k(n.text,i);r&&(n.text=[M(r.mode),``,j(r.mode),``,A(n.text,r)].join(`
|
|
4
|
+
`))}}};export{L as MaestriaPlugin,L as default};
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["join","readFileSync","parseYaml"],"sources":["../src/modes/types.ts","../src/root.ts","../src/modes/prompts.ts","../src/modes/index.ts","../src/index.ts"],"sourcesContent":["/**\n * Types for keyword-triggered workflow modes.\n *\n * @see ADR-OC-003 for full design context.\n */\n\nimport { z } from 'zod';\n\n/**\n * Valid mode keywords.\n *\n * - `\"fein\"` -- Full pipeline (recon -> design -> build -> review)\n * - `\"sonar\"` -- Research only (recon + design, stop before build)\n * - `\"blitz\"` -- Fast implementation (builder direct, skip optional recon/design; required review remains)\n */\nexport const modeKeywordSchema = z.enum(['fein', 'sonar', 'blitz']);\nexport type ModeKeyword = z.infer<typeof modeKeywordSchema>;\n\n/**\n * Plugin-level options for @maestria/opencode.\n */\nexport const maestriaOptionsSchema = z.object({\n modes: z\n .object({\n disabledKeywords: z.array(modeKeywordSchema).optional(),\n })\n .optional(),\n});\nexport type MaestriaPluginOptions = z.infer<typeof maestriaOptionsSchema>;\n\n/**\n * Result returned when a mode keyword is detected in a message.\n */\nexport interface ModeResult {\n /** The resolved mode keyword (lowercase). */\n mode: ModeKeyword;\n /** The keyword string as matched in the original text. */\n keyword: string;\n /** The character index where the keyword starts in the original text. */\n index: number;\n /** The mode prompt text to inject. */\n prompt: string;\n /** The mode marker string like `[MODE: fein]`. */\n marker: string;\n}\n","import { fileURLToPath } from 'node:url';\nimport { dirname, resolve, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nexport const PACKAGE_ROOT = resolve(__dirname, '..');\nexport const AGENTS_DIR = join(PACKAGE_ROOT, 'agents');\nexport const COMMANDS_DIR = join(PACKAGE_ROOT, 'agents', 'commands');\nexport const RULES_PATH = join(PACKAGE_ROOT, 'rules', 'AGENTS.md');\n","import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { COMMANDS_DIR } from '@/root.js';\nimport type { ModeKeyword } from '@/modes/types.js';\n\nconst VALID_KEYWORDS: readonly ModeKeyword[] = ['fein', 'sonar', 'blitz'];\n\nfunction loadModePrompt(name: string): string {\n const content = readFileSync(resolve(COMMANDS_DIR, `${name}.md`), 'utf-8');\n // Find the `## MODE:` heading which marks the start of the actual prompt text.\n // The synced command files start with an HTML comment (`<!-- Auto-generated... -->`),\n // not YAML frontmatter (`---`), so a frontmatter regex would never match.\n const modeIdx = content.indexOf('## MODE:');\n if (modeIdx !== -1) {\n return content.slice(modeIdx).replace(/\\s+$/, '') + '\\n';\n }\n return content.replace(/\\s+$/, '') + '\\n';\n}\n\n/**\n * Mode prompt text for each keyword, lazily loaded on first access.\n * If a prompt file is missing or unreadable, logs a warning and caches\n * an empty string — never throws at module evaluation time.\n *\n * @see ADR-OC-003 (section \"Mode Prompts\")\n */\nexport const MODE_PROMPTS: Record<ModeKeyword, string> = new Proxy(\n {} as Record<ModeKeyword, string>,\n {\n get(target, key, receiver) {\n if (typeof key === 'string' && (VALID_KEYWORDS as readonly string[]).includes(key)) {\n if (!(key in target)) {\n try {\n (target as Record<string, string>)[key] = loadModePrompt(key);\n } catch (e) {\n console.warn(`[maestria] Failed to load mode prompt \"${key}\":`, e);\n (target as Record<string, string>)[key] = '';\n }\n }\n return (target as Record<string, string>)[key as string];\n }\n return Reflect.get(target, key, receiver);\n },\n },\n);\n\n/**\n * Marker strings for each mode keyword, used to signal the active mode.\n * Format: `[MODE: <keyword>]`\n */\nexport const MODE_MARKERS: Record<ModeKeyword, string> = {\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\n blitz: '[MODE: blitz]',\n};\n\n/**\n * Array of all valid mode keywords for runtime iteration.\n */\nexport { VALID_KEYWORDS };\n","import { escapeRegExp } from 'es-toolkit';\nimport { MODE_PROMPTS, MODE_MARKERS, VALID_KEYWORDS } from '@/modes/prompts.js';\nimport type { ModeKeyword, ModeResult } from '@/modes/types.js';\n\n/**\n * Priority mapping for mode keyword restrictiveness.\n * Higher number = more restrictive = wins when multiple keywords are present.\n * fein (3): full pipeline with mandatory gates\n * sonar (2): research only, no code\n * blitz (1): fast implementation, skip optional ceremony; required review remains\n */\nconst MODE_PRIORITY: Record<ModeKeyword, number> = {\n fein: 3,\n sonar: 2,\n blitz: 1,\n};\n\n/**\n * Regex matching fenced code blocks (```) and inline backtick spans (`).\n * Used to exclude keyword matches inside code spans.\n */\n// Note: Unclosed fenced code blocks (``` without closing ```) are not\n// excluded - the regex requires matching fences. This is an accepted\n// false-positive risk (see ADR-OC-003 consequences).\nconst CODE_BLOCK_RE = /```[\\s\\S]*?```|`[^`]*`/g;\n\n/**\n * Find ranges of code blocks and inline code spans in text.\n * Returns [start, end) positions. Keywords inside these ranges\n * are ignored during detection.\n */\nfunction findAllCodeBlockRanges(text: string): Array<[number, number]> {\n const ranges: Array<[number, number]> = [];\n let match: RegExpExecArray | null;\n while ((match = CODE_BLOCK_RE.exec(text)) !== null) {\n ranges.push([match.index, match.index + match[0].length]);\n }\n return ranges;\n}\n\nfunction isInRanges(index: number, ranges: Array<[number, number]>): boolean {\n return ranges.some(([start, end]) => index >= start && index < end);\n}\n\n/**\n * Build a regex pattern for word-boundary matching of the given keyword.\n *\n * The pattern uses `\\b` word boundaries to ensure we match whole words only,\n * and is case-insensitive so `Fein`, `FEIN`, `fein` all match.\n */\nfunction buildKeywordRegex(keyword: string): RegExp {\n return new RegExp(`\\\\b${escapeRegExp(keyword)}\\\\b`, 'gi');\n}\n\n/**\n * Detect a workflow mode keyword in the given text.\n *\n * Detection rules (per ADR-OC-003):\n * - Word-boundary regex matching (`\\bfein\\b`, `\\bsonar\\b`, `\\bblitz\\b`)\n * - Most restrictive match wins (fein > sonar > blitz)\n * - Case-insensitive\n * - Disabled keywords are ignored\n * - Matches inside fenced code blocks (```) and inline backticks (`) are ignored\n *\n * @param text The user message to scan.\n * @param disabled Optional set of disabled mode keywords (lowercase).\n * @returns A `ModeResult` if a keyword was detected, or `null`.\n */\nexport function detectMode(text: string, disabled?: Set<string>): ModeResult | null {\n const codeRanges = findAllCodeBlockRanges(text);\n // Normalize disabled keywords to lowercase for case-insensitive comparison\n const normalizedDisabled = disabled\n ? new Set(Array.from(disabled).map((k) => k.toLowerCase()))\n : undefined;\n let bestMatch: { keyword: string; index: number; mode: ModeKeyword } | null = null;\n\n for (const keyword of VALID_KEYWORDS) {\n if (normalizedDisabled?.has(keyword)) continue;\n\n const regex = buildKeywordRegex(keyword);\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(text)) !== null) {\n if (isInRanges(match.index, codeRanges)) continue;\n // Most-restrictive wins: prefer higher-priority mode over position\n if (bestMatch === null || MODE_PRIORITY[keyword] > MODE_PRIORITY[bestMatch.mode]) {\n bestMatch = {\n keyword: match[0],\n index: match.index,\n mode: keyword,\n };\n }\n }\n }\n\n if (bestMatch === null) return null;\n\n return {\n mode: bestMatch.mode,\n keyword: bestMatch.keyword,\n index: bestMatch.index,\n prompt: MODE_PROMPTS[bestMatch.mode],\n marker: MODE_MARKERS[bestMatch.mode],\n };\n}\n\n/**\n * Remove the matched keyword from the text, cleaning up any trailing colon\n * or whitespace that may follow it.\n *\n * @param text The original message text.\n * @param result The `ModeResult` from `detectMode()`.\n * @returns The text with the keyword stripped.\n */\nexport function stripKeyword(text: string, result: ModeResult): string {\n const before = text.slice(0, result.index);\n const after = text.slice(result.index + result.keyword.length);\n\n // Remove any colon + optional whitespace after the keyword\n // (e.g. \"fein: do this\" -> \"do this\")\n const cleaned = after.replace(/^:\\s*/, '');\n\n // Collapse double spaces and trim both ends (handles keyword at start,\n // end, or middle of text, plus extra whitespace around colon)\n return (before + cleaned).replace(/ {2,}/g, ' ').trim();\n}\n\n/**\n * Get the mode prompt text for a given mode name.\n *\n * @param mode The mode keyword (e.g. \"fein\", \"sonar\", \"blitz\").\n * @returns The prompt string, or empty string if mode is unknown.\n */\nexport function getModePrompt(mode: string): string {\n if (isModeKeyword(mode)) {\n return MODE_PROMPTS[mode];\n }\n return '';\n}\n\n/**\n * Get the mode marker string for a given mode name.\n *\n * @param mode The mode keyword (e.g. \"fein\", \"sonar\", \"blitz\").\n * @returns The marker string (e.g. `[MODE: fein]`), or empty string if unknown.\n */\nexport function getModeMarker(mode: string): string {\n if (isModeKeyword(mode)) {\n return MODE_MARKERS[mode];\n }\n return '';\n}\n\n/**\n * Type guard to check if a string is a valid ModeKeyword.\n */\nfunction isModeKeyword(value: string): value is ModeKeyword {\n return (VALID_KEYWORDS as readonly string[]).includes(value);\n}\n","import type { Plugin } from '@opencode-ai/plugin';\nimport { merge } from 'es-toolkit';\nimport { readFileSync, readdirSync } from 'fs';\nimport { join, basename } from 'path';\nimport { parse as parseYaml } from 'yaml';\nimport { type MaestriaPluginOptions, maestriaOptionsSchema } from '@/modes/types.js';\nimport { detectMode, stripKeyword, getModeMarker, getModePrompt } from '@/modes/index.js';\nimport { AGENTS_DIR, RULES_PATH } from '@/root.js';\n\ninterface AgentFrontmatter {\n description: string;\n mode: string;\n permission: Record<string, unknown>;\n color?: string;\n maxSteps?: number;\n}\n\nfunction parseFrontmatter(yamlStr: string): AgentFrontmatter {\n const result = parseYaml(yamlStr) as Record<string, unknown>;\n return {\n description: (result.description as string) || '',\n mode: (result.mode as string) || 'subagent',\n permission: (result.permission as Record<string, unknown>) || {},\n color: result.color as string | undefined,\n maxSteps: result.maxSteps ? Number(result.maxSteps) : undefined,\n };\n}\n\n/**\n * Read an agent markdown file and split into frontmatter + prompt.\n */\nfunction parseAgentFile(filePath: string): { name: string; config: Record<string, unknown> } {\n const content = readFileSync(filePath, 'utf-8');\n const name = basename(filePath, '.md');\n\n // Split on ---\n const parts = content.split('---');\n if (parts.length < 3) {\n throw new Error(`Invalid agent file: ${filePath} - missing frontmatter`);\n }\n\n const frontmatter = parseFrontmatter(parts[1].trim());\n const prompt = parts.slice(2).join('---').trim();\n\n const config: Record<string, unknown> = {\n description: frontmatter.description,\n mode: frontmatter.mode,\n prompt,\n permission: frontmatter.permission,\n };\n\n if (frontmatter.color) config.color = frontmatter.color;\n if (frontmatter.maxSteps) config.maxSteps = frontmatter.maxSteps;\n\n return { name, config };\n}\n\n/**\n * Load all agent configs from the bundled agents/ directory.\n * Returns partial results if some agent files fail to load.\n */\nfunction loadAgents(): Record<string, Record<string, unknown>> {\n try {\n const files = readdirSync(AGENTS_DIR).filter((f) => f.endsWith('.md'));\n const agents: Record<string, Record<string, unknown>> = {};\n\n for (const file of files) {\n try {\n const { name, config } = parseAgentFile(join(AGENTS_DIR, file));\n agents[name] = config;\n } catch (err) {\n console.warn(`[maestria] Failed to parse agent file \"${file}\":`, err);\n }\n }\n\n return agents;\n } catch (err) {\n console.error(`[maestria] Failed to read agents directory:`, err);\n throw new Error(\n `[maestria] Failed to load agents from \"${AGENTS_DIR}\": ` +\n (err instanceof Error ? err.message : String(err)),\n );\n }\n}\n\nexport const MaestriaPlugin: Plugin = async (_input, options?: MaestriaPluginOptions) => {\n // Validate and parse options with zod\n const parsed = maestriaOptionsSchema.parse(options ?? {});\n const disabledKeywords = new Set<string>(\n (parsed.modes?.disabledKeywords ?? []).map((k) => k.toLowerCase()),\n );\n const agents = loadAgents();\n\n return {\n config: async (input) => {\n // Deep-merge plugin agent defaults over the user's agent entries. A\n // shallow `{ ...input.agent, ...agents }` would replace each entry\n // wholesale, dropping user-set keys (model, variant, temperature) for\n // the 8 maestria agent names. Plugin defaults win on conflict; user\n // keys the plugin does not set survive.\n input.agent = merge(input.agent ?? {}, agents);\n input.instructions = [...(input.instructions ?? []), RULES_PATH];\n },\n 'experimental.session.compacting': async (_input, output) => {\n output.context.push(\n 'Session was compacted. Task tracking is maintained via todowrite. ' +\n 'Active context (files, decisions, blockers) was captured before compaction. ' +\n 'Continue where you left off.',\n );\n },\n 'chat.message': async (hookInput, hookOutput) => {\n // Only fire for the orchestrator agent\n if (hookInput.agent !== 'orchestrator') return;\n\n // Find the first text part with user content\n const textPart = hookOutput.parts.find((p) => p.type === 'text') as\n | { text: string; type: 'text' }\n | undefined;\n if (!textPart) return;\n\n // Detect keyword in the text\n const result = detectMode(textPart.text, disabledKeywords);\n if (!result) return;\n\n // Strip keyword from text and prepend mode marker + prompt inline.\n // We embed everything in the existing text part rather than injecting\n // a second text part into `parts`, because the OpenCode runtime does\n // not handle multiple text parts per message (causes a hang).\n textPart.text = [\n getModeMarker(result.mode),\n '',\n getModePrompt(result.mode),\n '',\n stripKeyword(textPart.text, result),\n ].join('\\n');\n },\n };\n};\n\nexport default MaestriaPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["join","readFileSync","parseYaml"],"sources":["../src/modes/types.ts","../src/root.ts","../src/modes/prompts.ts","../src/modes/index.ts","../src/index.ts"],"sourcesContent":["/**\n * Types for keyword-triggered workflow modes.\n *\n * @see ADR-OC-003 for full design context.\n */\n\nimport { z } from 'zod';\n\n/**\n * Valid mode keywords.\n *\n * - `\"fein\"` -- Full pipeline (recon -> design -> build -> review)\n * - `\"sonar\"` -- Research only (recon + design, stop before build)\n * - `\"blitz\"` -- Fast implementation (builder direct, skip optional recon/design; required review remains)\n */\nexport const modeKeywordSchema = z.enum(['fein', 'sonar', 'blitz']);\nexport type ModeKeyword = z.infer<typeof modeKeywordSchema>;\n\n/**\n * Plugin-level options for @maestria/opencode.\n */\nexport const maestriaOptionsSchema = z.object({\n modes: z\n .object({\n disabledKeywords: z.array(modeKeywordSchema).optional(),\n })\n .optional(),\n});\nexport type MaestriaPluginOptions = z.infer<typeof maestriaOptionsSchema>;\n\n/**\n * Result returned when a mode keyword is detected in a message.\n */\nexport interface ModeResult {\n /** The resolved mode keyword (lowercase). */\n mode: ModeKeyword;\n /** The keyword string as matched in the original text. */\n keyword: string;\n /** The character index where the keyword starts in the original text. */\n index: number;\n /** The mode prompt text to inject. */\n prompt: string;\n /** The mode marker string like `[MODE: fein]`. */\n marker: string;\n}\n","import { fileURLToPath } from 'node:url';\nimport { dirname, resolve, join } from 'node:path';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nexport const PACKAGE_ROOT = resolve(__dirname, '..');\nexport const AGENTS_DIR = join(PACKAGE_ROOT, 'agents');\nexport const COMMANDS_DIR = join(PACKAGE_ROOT, 'agents', 'commands');\nexport const RULES_PATH = join(PACKAGE_ROOT, 'rules', 'AGENTS.md');\n","import { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { COMMANDS_DIR } from '@/root.js';\nimport type { ModeKeyword } from '@/modes/types.js';\n\nconst VALID_KEYWORDS: readonly ModeKeyword[] = ['fein', 'sonar', 'blitz'];\n\nfunction loadModePrompt(name: string): string {\n const content = readFileSync(resolve(COMMANDS_DIR, `${name}.md`), 'utf-8');\n // Find the `## MODE:` heading which marks the start of the actual prompt text.\n // The synced command files start with an HTML comment (`<!-- Auto-generated... -->`),\n // not YAML frontmatter (`---`), so a frontmatter regex would never match.\n const modeIdx = content.indexOf('## MODE:');\n if (modeIdx !== -1) {\n return content.slice(modeIdx).replace(/\\s+$/, '') + '\\n';\n }\n return content.replace(/\\s+$/, '') + '\\n';\n}\n\n/**\n * Mode prompt text for each keyword, lazily loaded on first access.\n * If a prompt file is missing or unreadable, logs a warning and caches\n * an empty string — never throws at module evaluation time.\n *\n * @see ADR-OC-003 (section \"Mode Prompts\")\n */\nexport const MODE_PROMPTS: Record<ModeKeyword, string> = new Proxy(\n {} as Record<ModeKeyword, string>,\n {\n get(target, key, receiver) {\n if (typeof key === 'string' && (VALID_KEYWORDS as readonly string[]).includes(key)) {\n if (!(key in target)) {\n try {\n (target as Record<string, string>)[key] = loadModePrompt(key);\n } catch (e) {\n console.warn(`[maestria] Failed to load mode prompt \"${key}\":`, e);\n (target as Record<string, string>)[key] = '';\n }\n }\n return (target as Record<string, string>)[key as string];\n }\n return Reflect.get(target, key, receiver);\n },\n },\n);\n\n/**\n * Marker strings for each mode keyword, used to signal the active mode.\n * Format: `[MODE: <keyword>]`\n */\nexport const MODE_MARKERS: Record<ModeKeyword, string> = {\n fein: '[MODE: fein]',\n sonar: '[MODE: sonar]',\n blitz: '[MODE: blitz]',\n};\n\n/**\n * Array of all valid mode keywords for runtime iteration.\n */\nexport { VALID_KEYWORDS };\n","import { escapeRegExp } from 'es-toolkit';\nimport { MODE_PROMPTS, MODE_MARKERS, VALID_KEYWORDS } from '@/modes/prompts.js';\nimport type { ModeKeyword, ModeResult } from '@/modes/types.js';\n\n/**\n * Priority mapping for mode keyword restrictiveness.\n * Higher number = more restrictive = wins when multiple keywords are present.\n * fein (3): full pipeline with mandatory gates\n * sonar (2): research only, no code\n * blitz (1): fast implementation, skip optional ceremony; required review remains\n */\nconst MODE_PRIORITY: Record<ModeKeyword, number> = {\n fein: 3,\n sonar: 2,\n blitz: 1,\n};\n\n/**\n * Regex matching fenced code blocks (```) and inline backtick spans (`).\n * Used to exclude keyword matches inside code spans.\n */\n// Note: Unclosed fenced code blocks (``` without closing ```) are not\n// excluded - the regex requires matching fences. This is an accepted\n// false-positive risk (see ADR-OC-003 consequences).\nconst CODE_BLOCK_RE = /```[\\s\\S]*?```|`[^`]*`/g;\n\n/**\n * Find ranges of code blocks and inline code spans in text.\n * Returns [start, end) positions. Keywords inside these ranges\n * are ignored during detection.\n */\nfunction findAllCodeBlockRanges(text: string): Array<[number, number]> {\n const ranges: Array<[number, number]> = [];\n let match: RegExpExecArray | null;\n while ((match = CODE_BLOCK_RE.exec(text)) !== null) {\n ranges.push([match.index, match.index + match[0].length]);\n }\n return ranges;\n}\n\nfunction isInRanges(index: number, ranges: Array<[number, number]>): boolean {\n return ranges.some(([start, end]) => index >= start && index < end);\n}\n\n/**\n * Build a regex pattern for word-boundary matching of the given keyword.\n *\n * The pattern uses `\\b` word boundaries to ensure we match whole words only,\n * and is case-insensitive so `Fein`, `FEIN`, `fein` all match.\n */\nfunction buildKeywordRegex(keyword: string): RegExp {\n return new RegExp(`\\\\b${escapeRegExp(keyword)}\\\\b`, 'gi');\n}\n\n/**\n * Detect a workflow mode keyword in the given text.\n *\n * Detection rules (per ADR-OC-003):\n * - Word-boundary regex matching (`\\bfein\\b`, `\\bsonar\\b`, `\\bblitz\\b`)\n * - Most restrictive match wins (fein > sonar > blitz)\n * - Case-insensitive\n * - Disabled keywords are ignored\n * - Matches inside fenced code blocks (```) and inline backticks (`) are ignored\n *\n * @param text The user message to scan.\n * @param disabled Optional set of disabled mode keywords (lowercase).\n * @returns A `ModeResult` if a keyword was detected, or `null`.\n */\nexport function detectMode(text: string, disabled?: Set<string>): ModeResult | null {\n const codeRanges = findAllCodeBlockRanges(text);\n // Normalize disabled keywords to lowercase for case-insensitive comparison\n const normalizedDisabled = disabled\n ? new Set(Array.from(disabled).map((k) => k.toLowerCase()))\n : undefined;\n let bestMatch: { keyword: string; index: number; mode: ModeKeyword } | null = null;\n\n for (const keyword of VALID_KEYWORDS) {\n if (normalizedDisabled?.has(keyword)) continue;\n\n const regex = buildKeywordRegex(keyword);\n let match: RegExpExecArray | null;\n\n while ((match = regex.exec(text)) !== null) {\n if (isInRanges(match.index, codeRanges)) continue;\n // Most-restrictive wins: prefer higher-priority mode over position\n if (bestMatch === null || MODE_PRIORITY[keyword] > MODE_PRIORITY[bestMatch.mode]) {\n bestMatch = {\n keyword: match[0],\n index: match.index,\n mode: keyword,\n };\n }\n }\n }\n\n if (bestMatch === null) return null;\n\n return {\n mode: bestMatch.mode,\n keyword: bestMatch.keyword,\n index: bestMatch.index,\n prompt: MODE_PROMPTS[bestMatch.mode],\n marker: MODE_MARKERS[bestMatch.mode],\n };\n}\n\n/**\n * Remove the matched keyword from the text, cleaning up any trailing colon\n * or whitespace that may follow it.\n *\n * @param text The original message text.\n * @param result The `ModeResult` from `detectMode()`.\n * @returns The text with the keyword stripped.\n */\nexport function stripKeyword(text: string, result: ModeResult): string {\n const before = text.slice(0, result.index);\n const after = text.slice(result.index + result.keyword.length);\n\n // Remove any colon + optional whitespace after the keyword\n // (e.g. \"fein: do this\" -> \"do this\")\n const cleaned = after.replace(/^:\\s*/, '');\n\n // Collapse double spaces and trim both ends (handles keyword at start,\n // end, or middle of text, plus extra whitespace around colon)\n return (before + cleaned).replace(/ {2,}/g, ' ').trim();\n}\n\n/**\n * Get the mode prompt text for a given mode name.\n *\n * @param mode The mode keyword (e.g. \"fein\", \"sonar\", \"blitz\").\n * @returns The prompt string, or empty string if mode is unknown.\n */\nexport function getModePrompt(mode: string): string {\n if (isModeKeyword(mode)) {\n return MODE_PROMPTS[mode];\n }\n return '';\n}\n\n/**\n * Get the mode marker string for a given mode name.\n *\n * @param mode The mode keyword (e.g. \"fein\", \"sonar\", \"blitz\").\n * @returns The marker string (e.g. `[MODE: fein]`), or empty string if unknown.\n */\nexport function getModeMarker(mode: string): string {\n if (isModeKeyword(mode)) {\n return MODE_MARKERS[mode];\n }\n return '';\n}\n\n/**\n * Type guard to check if a string is a valid ModeKeyword.\n */\nfunction isModeKeyword(value: string): value is ModeKeyword {\n return (VALID_KEYWORDS as readonly string[]).includes(value);\n}\n","import type { Plugin } from '@opencode-ai/plugin';\nimport { merge } from 'es-toolkit';\nimport { readFileSync, readdirSync } from 'fs';\nimport { join, basename } from 'path';\nimport { parse as parseYaml } from 'yaml';\nimport { type MaestriaPluginOptions, maestriaOptionsSchema } from '@/modes/types.js';\nimport { detectMode, stripKeyword, getModeMarker, getModePrompt } from '@/modes/index.js';\nimport { AGENTS_DIR, RULES_PATH } from '@/root.js';\n\ninterface AgentFrontmatter {\n description: string;\n mode: string;\n permission: Record<string, unknown>;\n color?: string;\n maxSteps?: number;\n}\n\nfunction parseFrontmatter(yamlStr: string): AgentFrontmatter {\n const result = parseYaml(yamlStr) as Record<string, unknown>;\n return {\n description: (result.description as string) || '',\n mode: (result.mode as string) || 'subagent',\n permission: (result.permission as Record<string, unknown>) || {},\n color: result.color as string | undefined,\n maxSteps: result.maxSteps ? Number(result.maxSteps) : undefined,\n };\n}\n\n/**\n * Read an agent markdown file and split into frontmatter + prompt.\n */\nfunction parseAgentFile(filePath: string): { name: string; config: Record<string, unknown> } {\n const content = readFileSync(filePath, 'utf-8');\n const name = basename(filePath, '.md');\n\n // Split on ---\n const parts = content.split('---');\n if (parts.length < 3) {\n throw new Error(`Invalid agent file: ${filePath} - missing frontmatter`);\n }\n\n const frontmatter = parseFrontmatter(parts[1].trim());\n const prompt = parts.slice(2).join('---').trim();\n\n const config: Record<string, unknown> = {\n description: frontmatter.description,\n mode: frontmatter.mode,\n prompt,\n permission: frontmatter.permission,\n };\n\n if (frontmatter.color) config.color = frontmatter.color;\n if (frontmatter.maxSteps) config.maxSteps = frontmatter.maxSteps;\n\n return { name, config };\n}\n\n/**\n * Load all agent configs from the bundled agents/ directory.\n * Returns partial results if some agent files fail to load.\n */\nfunction loadAgents(): Record<string, Record<string, unknown>> {\n try {\n const files = readdirSync(AGENTS_DIR).filter((f) => f.endsWith('.md'));\n const agents: Record<string, Record<string, unknown>> = {};\n\n for (const file of files) {\n try {\n const { name, config } = parseAgentFile(join(AGENTS_DIR, file));\n agents[name] = config;\n } catch (err) {\n console.warn(`[maestria] Failed to parse agent file \"${file}\":`, err);\n }\n }\n\n return agents;\n } catch (err) {\n console.error(`[maestria] Failed to read agents directory:`, err);\n throw new Error(\n `[maestria] Failed to load agents from \"${AGENTS_DIR}\": ` +\n (err instanceof Error ? err.message : String(err)),\n );\n }\n}\n\nexport const MaestriaPlugin: Plugin = async (_input, options?: MaestriaPluginOptions) => {\n // Validate and parse options with zod\n const parsed = maestriaOptionsSchema.parse(options ?? {});\n const disabledKeywords = new Set<string>(\n (parsed.modes?.disabledKeywords ?? []).map((k) => k.toLowerCase()),\n );\n const agents = loadAgents();\n\n return {\n config: async (input) => {\n // Deep-merge plugin agent defaults over the user's agent entries. A\n // shallow `{ ...input.agent, ...agents }` would replace each entry\n // wholesale, dropping user-set keys (model, variant, temperature) for\n // the 8 maestria agent names. Plugin defaults win on conflict; user\n // keys the plugin does not set survive.\n input.agent = merge(input.agent ?? {}, agents);\n input.instructions = [...(input.instructions ?? []), RULES_PATH];\n },\n 'experimental.session.compacting': async (_input, output) => {\n output.context.push(\n 'Session was compacted. Task tracking is maintained via todowrite. ' +\n 'Active context (files, decisions, blockers) was captured before compaction. ' +\n 'Continue where you left off.',\n );\n },\n 'chat.message': async (hookInput, hookOutput) => {\n // Only fire for the orchestrator agent\n if (hookInput.agent !== 'orchestrator') return;\n\n // Find the first text part with user content\n const textPart = hookOutput.parts.find((p) => p.type === 'text') as\n | { text: string; type: 'text' }\n | undefined;\n if (!textPart) return;\n\n // Detect keyword in the text\n const result = detectMode(textPart.text, disabledKeywords);\n if (!result) return;\n\n // Strip keyword from text and prepend mode marker + prompt inline.\n // We embed everything in the existing text part rather than injecting\n // a second text part into `parts`, because the OpenCode runtime does\n // not handle multiple text parts per message (causes a hang).\n textPart.text = [\n getModeMarker(result.mode),\n '',\n getModePrompt(result.mode),\n '',\n stripKeyword(textPart.text, result),\n ].join('\\n');\n },\n };\n};\n\nexport default MaestriaPlugin;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maestria/opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "OpenCode plugin encoding AI engineering praxis: rules, agents, and workflow discipline.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"provenance": true
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"es-toolkit": "^1.
|
|
42
|
+
"es-toolkit": "^1.51.0",
|
|
43
43
|
"yaml": "^2.9.0",
|
|
44
44
|
"zod": "^4.4.3"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@opencode-ai/plugin": "^1.18.
|
|
48
|
-
"@types/node": "^26",
|
|
49
|
-
"typescript": "^
|
|
50
|
-
"vitest": "4.1.
|
|
47
|
+
"@opencode-ai/plugin": "^1.18.19",
|
|
48
|
+
"@types/node": "^26.2.0",
|
|
49
|
+
"typescript": "^7.0.2",
|
|
50
|
+
"vitest": "4.1.11"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@opencode-ai/plugin": "^1.18.4"
|
package/rules/AGENTS.md
CHANGED
|
@@ -3,82 +3,48 @@
|
|
|
3
3
|
|
|
4
4
|
# Global Agent Rules
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Cross-platform behavior contract: outcomes, evidence, safety, delegation, review, and bounded repair. The host runtime defines tool authority and lifecycle; specialists own their role methodology. Project rules constrain sequencing but cannot waive these floors.
|
|
7
7
|
|
|
8
8
|
## Universal Floors
|
|
9
9
|
|
|
10
10
|
`!!!` marks a non-negotiable default-path rule. Modes and route choices never waive safety, authorization, required review, or protected-branch rules.
|
|
11
11
|
|
|
12
|
-
- **!!! Verify important claims** against
|
|
13
|
-
- **!!! Match effort to stakes.** Use the smallest route, investigation, test set, and review depth that
|
|
14
|
-
- **!!! Prefer reuse over reinvention.** Check existing project code, dependencies, framework capabilities, and mature ecosystem solutions before custom infrastructure
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- **!!! Exhaust available evidence before asking.** Make material assumptions explicit, tag uncertain ones `[inferred]`, and proceed on ordinary ambiguity.
|
|
18
|
-
- **!!! Keep public output self-contained and professional.** Do not leak internal context, and understand existing systems before adapting or deleting them.
|
|
19
|
-
- State what the host guarantees versus what is only advisory. Never claim tool isolation, context isolation, lifecycle control, or maker/checker enforcement that the runtime does not provide.
|
|
12
|
+
- **!!! Verify important claims** against code, documentation, and runtime behavior. Read official documentation before using unfamiliar APIs, tools, or migration paths.
|
|
13
|
+
- **!!! Match effort to stakes.** Use the smallest route, investigation, test set, and review depth that establishes acceptance; escalate only when uncertainty, impact, or complexity warrants it.
|
|
14
|
+
- **!!! Prefer reuse over reinvention.** Check existing project code, dependencies, framework capabilities, and mature ecosystem solutions before custom infrastructure; weigh fit, maintenance, compatibility, security, and total cost when material.
|
|
15
|
+
- **!!! Exhaust available evidence before asking.** Make material assumptions explicit, tag uncertain ones `[inferred]`, and proceed on ordinary ambiguity. Ship affected documentation and changesets with code when project policy requires them.
|
|
16
|
+
- **!!! Keep output self-contained and professional.** Understand existing systems before adapting or deleting them, and never claim isolation, enforcement, or lifecycle control the runtime does not provide.
|
|
20
17
|
|
|
21
|
-
##
|
|
18
|
+
## Modes
|
|
22
19
|
|
|
23
|
-
-
|
|
24
|
-
- When relevant, load `.maestria/workflow.md` and `.maestria/rules.md` once per session. Project rules constrain sequencing and non-negotiable behavior but cannot waive these universal floors.
|
|
25
|
-
- Modes are per-turn when the host supports them: `fein` requests the full route with review, `sonar` is research-only, and `blitz` skips optional ceremony only. Persisted modes must expose a clear/reset path.
|
|
20
|
+
Per-turn keywords when the host supports them: `fein` requests the full route with required review, `sonar` is research-only and stops without implementing, `blitz` skips optional ceremony for familiar low-risk work. Modes are case-insensitive and per-turn unless the platform documents another lifetime.
|
|
26
21
|
|
|
27
22
|
## Outcome and Scope
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
- Compare progress with the outcome and acceptance evidence, not activity or process completion.
|
|
31
|
-
- Keep file, package, and runtime scope explicit. Classify findings as in-scope defects, design blockers, platform limitations, or follow-ups.
|
|
32
|
-
- Adjacent findings do not expand the current task automatically. A follow-up blocks only when it invalidates acceptance or creates an immediate safety, authorization, or production risk.
|
|
33
|
-
- Changes that alter security, authentication, authorization, or permission boundaries are mandatory stops. Ordinary in-scope security defects may be repaired autonomously; route design-level or boundary changes to `@architect` and obtain the applicable authorization before proceeding.
|
|
24
|
+
Define the primary user outcome, acceptance evidence, and meaningful non-goals before substantial implementation or delegation; measure progress against them, not activity. Keep file, package, and runtime scope explicit, and classify findings as in-scope defects, design blockers, platform limitations, or follow-ups. Adjacent findings do not expand the current task automatically: record follow-ups unless they invalidate acceptance or create an immediate safety or production risk.
|
|
34
25
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- **!!! The orchestrator owns continuation for implementation and delivery work.** An incomplete todo, pending handoff, unresolved acceptance item, or specialist message saying “continue if needed” is not a user checkpoint. Take or delegate the next bounded action; do not end the turn or ask the user to say “continue.” Research-only, planning-only, explicitly read-only, and host-blocked work terminates at its requested artifact or exact blocker.
|
|
38
|
-
- A specialist's read-only or no-edit result ends that delegation, not the parent work unit. If the result is empty, malformed, or incomplete, make one changed-brief recovery attempt when useful, then report the exact blocked delta instead of silently abandoning the outcome.
|
|
39
|
-
- Freeze the outcome, acceptance criteria, non-goals, and review budget at the start of the work unit. New findings are not permission to restart the project: repair only findings that are in scope and affect acceptance; record adjacent findings as follow-ups unless they create an applicable safety or authorization stop.
|
|
40
|
-
- Do not reset a review or repair budget by splitting the same outcome into more delegations, changing specialist names, or relabelling the finding. A new scope requires a new outcome and acceptance criteria.
|
|
41
|
-
- For implementation work, continue through validation and the project's normal delivery artifact. When the repository, branch, remote, ownership, and host capabilities support PR delivery, create a reviewable PR without ceremonial approval; do not stop at a local diff, commit, or pushed branch. Research-only, planning-only, explicitly read-only, and host-blocked work terminates at its requested artifact or exact blocker. Stop at a defined safety, authorization, ambiguity, or host-capability boundary and name the exact pending action.
|
|
26
|
+
Changes altering security, authentication, or permission boundaries are mandatory stops; ordinary in-scope security defects may be repaired autonomously.
|
|
42
27
|
|
|
43
28
|
## Delegation and Context
|
|
44
29
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- Delegate only when another context, expertise, independent check, or parallel workstream materially improves the outcome. A delegation owns one coherent outcome.
|
|
48
|
-
- A useful handoff contains only the material needed to act: outcome, relevant context and constraints, acceptance or expected evidence, material assumptions or known problems, and the next step or blocker.
|
|
49
|
-
- A specialist reports what it produced, changed files or artifacts, evidence of validation, blockers or follow-ups, and the next step. Empty, malformed, unavailable, or blocked output is not success.
|
|
50
|
-
- When delegation fails, preserve useful state and make one justified recovery attempt when the cause is identifiable or transport can be retried. User or intentional platform cancellation is terminal. If recovery fails, stop dependent work, report the delta, and never mutate directly as a fallback.
|
|
51
|
-
- Parallelize only independent work with non-overlapping writers. Integrate results before reviewing the combined change.
|
|
52
|
-
- Before handoff or compaction, preserve the outcome, decisions, assumptions and evidence, changed files, validation, blockers, and next step.
|
|
30
|
+
Delegate only when another context, expertise, independent check, or parallel workstream materially improves the outcome. Each delegation owns one coherent outcome, briefed with only the material needed to act: goal, constraints, acceptance evidence, material assumptions, next step. Restate binding user constraints inside every brief whose work they affect, and check them again at final verification. Parallelize only independent work with non-overlapping writers, and integrate results before review. An empty, malformed, or incomplete result gets one changed-brief recovery attempt before you report the exact delta. Before handoff or compaction, preserve the outcome, decisions, assumptions and evidence, changed files, validation, blockers, and next step.
|
|
53
31
|
|
|
54
32
|
## Acceptance and Blind Review
|
|
55
33
|
|
|
56
|
-
|
|
57
|
-
- The checker independently inspects the requirements, acceptance criteria, relevant diff, and available validation or behavior evidence; maker claims and maker-authored narrative are not approval.
|
|
58
|
-
- Review against acceptance, correctness, safety, and the diff. Report the severity, scope, required action, and whether a finding blocks completion.
|
|
59
|
-
- The checker labels `[fix]` only for a concrete blocker: a security-boundary, acceptance, correctness/regression, or material in-scope design/maintainability failure. Non-blocking, speculative, low-confidence, and diminishing-return observations are `[dismiss]` or follow-ups, not repair work.
|
|
60
|
-
- In-scope blockers may be repaired autonomously. Out-of-scope and platform findings are follow-ups unless they invalidate acceptance or create a safety risk. Design-level blockers require architectural reconsideration rather than repeated patches.
|
|
61
|
-
- Completion requires observable evidence for the acceptance criteria. Never claim an unverified result.
|
|
34
|
+
Maker/checker split: the implementer must not approve its own work. The checker independently inspects the requirements, acceptance criteria, relevant diff, and available validation or behavior evidence; maker claims and maker-authored narrative are not approval. Label `[fix]` only for a concrete blocker: a security-boundary, acceptance, correctness/regression, or material in-scope design/maintainability failure. Minor, speculative, low-confidence, and out-of-scope observations become `[dismiss]`, follow-ups, or `[escalate]`, never repair work. Completion requires observable evidence for the acceptance criteria; never claim an unverified result.
|
|
62
35
|
|
|
63
36
|
## Bounded Repair and Fail-Loud Behavior
|
|
64
37
|
|
|
65
|
-
|
|
66
|
-
- Review is a convergence gate, not an invitation to polish indefinitely. Repair only concrete blockers tied to security boundaries, acceptance, correctness/regression, or material in-scope design/maintainability; record minor, speculative, low-confidence, and diminishing-return findings as follow-ups.
|
|
67
|
-
- Default to one independent review and, only when blockers exist, one repair/re-review pass. Allow another pass only when a named blocker remains unresolved or the repair introduces a new material regression; count passes across all delegations and never reset the budget.
|
|
68
|
-
- Repeated causes, repeated findings, restored diffs, or no new evidence are non-progress. Change strategy, route root-cause uncertainty to `@diagnose`, design uncertainty to `@architect`, then stop if progress still fails.
|
|
69
|
-
- Do not loop silently. Report: `Tried X, Y, Z. Blocked by [cause]. Need [input] to proceed.` Preserve the last diff and finding provenance.
|
|
38
|
+
Default to one independent review and, only when blockers exist, one repair/re-review pass; allow another pass only when a named blocker remains unresolved or the repair introduced a new material regression. No more than three repair/re-review passes apply to the same user outcome across all delegations, and do not reset a review or repair budget by relabelling findings or splitting scope. Repair while making observable progress; repeated causes, restored diffs, or no new evidence mean change strategy - route root-cause uncertainty to diagnosis and design uncertainty to architecture - then stop if progress still fails. Do not loop silently: report `Tried X, Y, Z. Blocked by [cause]. Need [input] to proceed.` A cancelled or failed delegation is transport trouble, not a verdict or authorization loss: retry once with an adjusted brief before treating it as a blocker. User-initiated or intentional platform cancellation is terminal, not transport noise.
|
|
70
39
|
|
|
71
40
|
## Authorization, Lifecycle, and Branches
|
|
72
41
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
- An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping.
|
|
42
|
+
Safety and authorization override user intent, methodology, and brevity. Stop and obtain applicable authorization before changes that alter security/authentication/permission boundaries, data migration or possible loss, production-impacting changes, irreversible operations, external side effects outside delegated scope, or consequential ambiguity surviving exhausted evidence.
|
|
43
|
+
|
|
44
|
+
The orchestrator owns continuation for implementation and delivery work until the outcome reaches its terminal artifact; incomplete todos, pending handoffs, or specialist messages saying "continue if needed" are not a user checkpoint. Routine delivery is autonomous. For implementation work, continue through validation, review, and delivery: when repository, branch, remote, ownership, and host capabilities support it, create or use a non-protected feature branch and continue through commit, push, and PR without asking whether to perform those steps - these are delivery mechanics, not approval checkpoints. Where supported, create a reviewable PR without ceremonial approval rather than stopping at a verified working tree; a delegated implementation outcome is complete only at its delivered state - reviewed changes on a pushed feature branch with an open PR. Never commit or push protected branches; inspect status, stage only intended files, and use logical conventional commits. Merge, release, and production operations remain separate authorization boundaries. Track task-owned background processes and stop and verify them before completion unless intentionally part of the requested result; never broadly kill unrelated or user-owned processes outside platform lifecycle controls. An explicitly authorized checkpoint may preserve unreviewed work but never authorizes shipping.
|
|
45
|
+
|
|
46
|
+
Freeze the outcome, acceptance criteria, non-goals, and repair limits at the start of a work unit; re-plan only when the outcome or its evidence changes. Research-only, planning-only, explicitly read-only, and host-blocked work terminates at its requested artifact or exact blocker.
|
|
79
47
|
|
|
80
48
|
## Canonical Source Invariant
|
|
81
49
|
|
|
82
|
-
|
|
83
|
-
- Generate platform projections with `scripts/sync-all`; never hand-edit them.
|
|
84
|
-
- Pass the sync check before handing off a canonical directive change.
|
|
50
|
+
Author agent directives only under `packages/core/agent-directives/`. Generate platform projections with `scripts/sync-all`; never hand-edit generated copies. Pass the sync check before handing off any canonical directive change.
|