@syntesseraai/opencode-feature-factory 0.10.13 → 0.10.15
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.md +4 -3
- package/README.md +18 -2
- package/agents/building.md +25 -11
- package/agents/documenting.md +25 -7
- package/agents/feature-factory.md +34 -0
- package/agents/ff-research.md +15 -4
- package/agents/planning.md +16 -8
- package/agents/reviewing.md +39 -9
- package/dist/auto-handoff.d.ts +22 -0
- package/dist/auto-handoff.js +195 -0
- package/dist/index.js +16 -1
- package/dist/mcp-config.d.ts +4 -0
- package/dist/mcp-config.js +4 -0
- package/package.json +2 -4
package/AGENTS.md
CHANGED
|
@@ -5,11 +5,11 @@ This file is installed to `~/.config/opencode/AGENTS.md` by `@syntesseraai/openc
|
|
|
5
5
|
## What Feature Factory Provides
|
|
6
6
|
|
|
7
7
|
- Native workflow orchestration through the `feature-factory` agent.
|
|
8
|
-
- Default orchestrator model: `feature-factory` -> `
|
|
8
|
+
- Default orchestrator model: `feature-factory` -> `github-copilot/gpt-5.4-mini`
|
|
9
9
|
- Stage sub-agents with default model routing:
|
|
10
10
|
- `planning` -> `openai/gpt-5.4`
|
|
11
11
|
- `building` -> `openai/gpt-5.3-codex`
|
|
12
|
-
- `reviewing` -> `opencode/
|
|
12
|
+
- `reviewing` -> `opencode/glm-5.1`
|
|
13
13
|
- `documenting` -> `opencode/gemini-3.1-pro`
|
|
14
14
|
- Specialized agents: `feature-factory`, `planning`, `building`, `reviewing`, `documenting`, and `ff-research`.
|
|
15
15
|
- Quick commands: `/ff-review [optional prompt]`, `/ff-document [optional prompt]`, and `/ff-rework [optional prompt]` (all run as subtasks on the relevant stage agent).
|
|
@@ -36,7 +36,8 @@ When work changes behavior, workflows, configuration, operational guidance, or r
|
|
|
36
36
|
|
|
37
37
|
## Preferred Tooling Pattern
|
|
38
38
|
|
|
39
|
-
- Use `
|
|
39
|
+
- Use codebase-memory MCP graph tools first in stage agents (`codebase-memory-mcp_search_graph`, `codebase-memory-mcp_get_architecture`, `codebase-memory-mcp_trace_call_path`, `codebase-memory-mcp_get_code_snippet`) for discovery, architecture context, and call-path impact analysis.
|
|
40
|
+
- Keep `morph-mcp_codebase_search` as fallback when codebase-memory MCP is unavailable or the repository has not been indexed.
|
|
40
41
|
- Use `read`, `glob`, and `grep` for targeted file inspection.
|
|
41
42
|
- Writable agents should prefer `morph-mcp` `edit_file`, then fallback to native `edit` when needed.
|
|
42
43
|
- Keep `edit` restricted on read-only agents (`planning`, `reviewing`, `ff-research`).
|
package/README.md
CHANGED
|
@@ -56,10 +56,10 @@ The plugin no longer exposes `ff_pipeline`, `ff_mini_loop`, or `ff_list_models`
|
|
|
56
56
|
|
|
57
57
|
Instead, the `feature-factory` primary agent orchestrates workflows natively by delegating to stage sub-agents:
|
|
58
58
|
|
|
59
|
-
- `feature-factory` (orchestrator) -> default model `
|
|
59
|
+
- `feature-factory` (orchestrator) -> default model `github-copilot/gpt-5.4-mini`
|
|
60
60
|
- `planning` -> default model `openai/gpt-5.4`
|
|
61
61
|
- `building` -> default model `openai/gpt-5.3-codex`
|
|
62
|
-
- `reviewing` -> default model `opencode/
|
|
62
|
+
- `reviewing` -> default model `opencode/glm-5.1`
|
|
63
63
|
- `documenting` -> default model `opencode/gemini-3.1-pro`
|
|
64
64
|
|
|
65
65
|
### Fixed execution path
|
|
@@ -76,6 +76,17 @@ Each transition carries forward prior-stage context (summary, gate/verdict, acti
|
|
|
76
76
|
|
|
77
77
|
Writable stage agents (`building`, `documenting`) prefer Morph MCP `edit_file` and can fallback to native `edit` (and `write` for new files) when needed. Read-only agents keep `edit` disabled.
|
|
78
78
|
|
|
79
|
+
Stage-agent code discovery is graph-first: planning/building/reviewing/documenting prefer codebase-memory MCP tools (`codebase-memory-mcp_search_graph`, `codebase-memory-mcp_get_architecture`, `codebase-memory-mcp_trace_call_path`, `codebase-memory-mcp_get_code_snippet`) before semantic warp_grep/warpgrep-style fallbacks.
|
|
80
|
+
|
|
81
|
+
### Plugin auto-handoff safety net
|
|
82
|
+
|
|
83
|
+
The plugin now includes an auto-handoff hook that can continue deterministic next steps by reading explicit machine-readable fields from assistant output and dispatching `client.session.prompt(...)`.
|
|
84
|
+
|
|
85
|
+
- The orchestrator (`@feature-factory`) remains the source of truth for workflow progression.
|
|
86
|
+
- Auto-handoff is a continuation safety net, not a replacement for orchestrator logic.
|
|
87
|
+
- Multi-stage continuation should target `feature-factory`; one-stage follow-up can target a stage agent.
|
|
88
|
+
- Dispatch is performed through direct API prompt calls (not slash-command execution).
|
|
89
|
+
|
|
79
90
|
## Quick Commands
|
|
80
91
|
|
|
81
92
|
The plugin installs global custom slash commands in `~/.config/opencode/commands/`:
|
|
@@ -83,14 +94,17 @@ The plugin installs global custom slash commands in `~/.config/opencode/commands
|
|
|
83
94
|
- `/ff-review [optional prompt]`
|
|
84
95
|
- Agent: `reviewing`
|
|
85
96
|
- Subtask: `true` (always runs as a subtask)
|
|
97
|
+
- Mode: manual standalone helper (does not continue the full pipeline automatically)
|
|
86
98
|
- Default prompt when no argument is provided: `Review the changes so far`
|
|
87
99
|
- `/ff-document [optional prompt]`
|
|
88
100
|
- Agent: `documenting`
|
|
89
101
|
- Subtask: `true` (always runs as a subtask)
|
|
102
|
+
- Mode: manual standalone helper (does not continue the full pipeline automatically)
|
|
90
103
|
- Default prompt when no argument is provided: `Document all the changes so far`
|
|
91
104
|
- `/ff-rework [optional prompt]`
|
|
92
105
|
- Agent: `building`
|
|
93
106
|
- Subtask: `true` (always runs as a subtask)
|
|
107
|
+
- Mode: manual standalone helper (does not continue the full pipeline automatically)
|
|
94
108
|
- Default prompt when no argument is provided: `apply the rework recommendations`
|
|
95
109
|
|
|
96
110
|
Examples:
|
|
@@ -111,6 +125,8 @@ The plugin merges the following MCP servers into global OpenCode config when mis
|
|
|
111
125
|
- `context7`
|
|
112
126
|
- `morph-mcp`
|
|
113
127
|
|
|
128
|
+
`codebase-memory-mcp` is currently treated as an optional prerequisite (not auto-provisioned by this plugin) because it commonly requires repository-specific indexing/setup. Stage agents still include morph semantic fallback guidance when codebase-memory MCP is unavailable.
|
|
129
|
+
|
|
114
130
|
`morph-mcp` is configured as:
|
|
115
131
|
|
|
116
132
|
```json
|
package/agents/building.md
CHANGED
|
@@ -11,6 +11,11 @@ tools:
|
|
|
11
11
|
bash: true
|
|
12
12
|
skill: true
|
|
13
13
|
task: true
|
|
14
|
+
'codebase-memory-mcp_search_graph': true
|
|
15
|
+
'codebase-memory-mcp_get_architecture': true
|
|
16
|
+
'codebase-memory-mcp_trace_call_path': true
|
|
17
|
+
'codebase-memory-mcp_get_code_snippet': true
|
|
18
|
+
'codebase-memory-mcp_search_code': true
|
|
14
19
|
'morph-mcp_codebase_search': true
|
|
15
20
|
permission:
|
|
16
21
|
skill:
|
|
@@ -43,29 +48,38 @@ You are the building specialist.
|
|
|
43
48
|
|
|
44
49
|
## Editing Workflow (Required)
|
|
45
50
|
|
|
46
|
-
Prefer
|
|
51
|
+
Prefer the OpenCode `edit_file` tool (Fast Apply) for all implementation edits.
|
|
52
|
+
|
|
53
|
+
Fallback policy:
|
|
54
|
+
|
|
55
|
+
- **Codex models:** use `patch` or `apply_patch`.
|
|
56
|
+
- **Non-Codex models:** use `edit`.
|
|
57
|
+
- **New files (all models):** `write` or `patch` are acceptable.
|
|
47
58
|
|
|
48
59
|
1. **Read before editing** to confirm current behavior and scope.
|
|
49
60
|
2. **Apply changes with `edit_file`** for targeted replacements/insertions whenever available.
|
|
50
61
|
3. **Re-read after edits** to verify the applied diff and avoid stale assumptions.
|
|
51
|
-
4. If `edit_file` is unavailable or cannot express the required change, fallback
|
|
52
|
-
5.
|
|
62
|
+
4. If `edit_file` is unavailable or cannot express the required change, choose fallback by model family: Codex → `patch`/`apply_patch`; non-Codex → `edit`.
|
|
63
|
+
5. For brand-new files, use `write` or `patch`.
|
|
53
64
|
|
|
54
65
|
## Semantic Code Search
|
|
55
66
|
|
|
56
|
-
Use
|
|
67
|
+
Use codebase-memory MCP tools first for semantic/structural code search when you need to:
|
|
57
68
|
|
|
58
|
-
- Find implementations by meaning
|
|
59
|
-
-
|
|
60
|
-
-
|
|
69
|
+
- Find implementations by meaning with `codebase-memory-mcp_search_graph`
|
|
70
|
+
- Understand package/service boundaries via `codebase-memory-mcp_get_architecture`
|
|
71
|
+
- Trace caller/callee impact with `codebase-memory-mcp_trace_call_path`
|
|
72
|
+
- Read exact source around graph hits with `codebase-memory-mcp_get_code_snippet`
|
|
61
73
|
|
|
62
|
-
Prefer this over
|
|
74
|
+
Prefer this over warp_grep/warpgrep-style search because graph context reduces false positives and improves dependency-aware edits.
|
|
63
75
|
|
|
64
76
|
Search fallback order:
|
|
65
77
|
|
|
66
|
-
1. `
|
|
67
|
-
2. `
|
|
68
|
-
3. `
|
|
78
|
+
1. `codebase-memory-mcp_search_graph` + `codebase-memory-mcp_get_architecture` for discovery.
|
|
79
|
+
2. `codebase-memory-mcp_trace_call_path` / `codebase-memory-mcp_get_code_snippet` for impact and code verification.
|
|
80
|
+
3. `morph-mcp_codebase_search` if codebase-memory MCP is unavailable or unindexed.
|
|
81
|
+
4. `read` to inspect exact implementation details before editing.
|
|
82
|
+
5. `grep` for exact symbol/literal checks when needed.
|
|
69
83
|
|
|
70
84
|
## GitHub Workflow Guidance
|
|
71
85
|
|
package/agents/documenting.md
CHANGED
|
@@ -11,6 +11,11 @@ tools:
|
|
|
11
11
|
bash: false
|
|
12
12
|
skill: true
|
|
13
13
|
task: true
|
|
14
|
+
'codebase-memory-mcp_search_graph': true
|
|
15
|
+
'codebase-memory-mcp_get_architecture': true
|
|
16
|
+
'codebase-memory-mcp_trace_call_path': true
|
|
17
|
+
'codebase-memory-mcp_get_code_snippet': true
|
|
18
|
+
'codebase-memory-mcp_search_code': true
|
|
14
19
|
'morph-mcp_codebase_search': true
|
|
15
20
|
permission:
|
|
16
21
|
skill:
|
|
@@ -47,25 +52,38 @@ Always load `ff-documentation-rules` before scoping or editing documentation. Tr
|
|
|
47
52
|
|
|
48
53
|
## Editing Workflow (Required)
|
|
49
54
|
|
|
50
|
-
Prefer
|
|
55
|
+
Prefer the OpenCode `edit_file` tool (Fast Apply) for documentation updates.
|
|
56
|
+
|
|
57
|
+
Fallback policy:
|
|
58
|
+
|
|
59
|
+
- **Codex models:** use `patch` or `apply_patch`.
|
|
60
|
+
- **Non-Codex models:** use `edit`.
|
|
61
|
+
- **New files (all models):** `write` or `patch` are acceptable.
|
|
51
62
|
|
|
52
63
|
1. **Read the current navigation chain first** — inspect the root `README.md`, `docs/INDEX.md`, relevant nested `docs/**/INDEX.md` files, and affected leaf docs before editing.
|
|
53
64
|
2. **Verify behavior from code** — use semantic search and targeted reads so the documentation matches what actually ships.
|
|
54
65
|
3. **Update canonical docs and navigation together** — change the affected leaf docs, every impacted `INDEX.md`, and the root `README.md` when primary documentation entry points or doc structure change.
|
|
55
66
|
4. **Apply changes with `edit_file`** for precise doc updates whenever available.
|
|
56
67
|
5. **Re-read after edits** to validate wording, relative links, and cross-doc consistency.
|
|
57
|
-
6. If `edit_file` is unavailable or cannot express the required change, fallback
|
|
58
|
-
7.
|
|
68
|
+
6. If `edit_file` is unavailable or cannot express the required change, choose fallback by model family: Codex → `patch`/`apply_patch`; non-Codex → `edit`.
|
|
69
|
+
7. For brand-new documentation files, use `write` or `patch`.
|
|
59
70
|
|
|
60
71
|
## Semantic Code Search
|
|
61
72
|
|
|
62
|
-
Use
|
|
73
|
+
Use codebase-memory MCP tools first to understand how code actually works before documenting it.
|
|
74
|
+
|
|
75
|
+
- `codebase-memory-mcp_search_graph` to locate relevant functions/classes/routes
|
|
76
|
+
- `codebase-memory-mcp_get_architecture` to capture subsystem context accurately
|
|
77
|
+
- `codebase-memory-mcp_trace_call_path` for workflow and dependency narratives
|
|
78
|
+
- `codebase-memory-mcp_get_code_snippet` to verify exact behavior before writing docs
|
|
63
79
|
|
|
64
80
|
Search fallback order:
|
|
65
81
|
|
|
66
|
-
1. `
|
|
67
|
-
2. `
|
|
68
|
-
3. `
|
|
82
|
+
1. `codebase-memory-mcp_search_graph` + `codebase-memory-mcp_get_architecture` for graph-first discovery.
|
|
83
|
+
2. `codebase-memory-mcp_trace_call_path` / `codebase-memory-mcp_get_code_snippet` for behavior confirmation.
|
|
84
|
+
3. `morph-mcp_codebase_search` if codebase-memory MCP is unavailable or the repo is not indexed.
|
|
85
|
+
4. `read` to verify exact behavior before writing docs.
|
|
86
|
+
5. `grep` for exact symbol/literal checks when needed.
|
|
69
87
|
|
|
70
88
|
## GitHub Workflow Guidance
|
|
71
89
|
|
|
@@ -191,6 +191,13 @@ For each iteration `n`:
|
|
|
191
191
|
7. If either gate is not `APPROVED`, route back to Build with consolidated action items from both review outputs.
|
|
192
192
|
8. If 10 iterations are exhausted, stop and escalate to the user.
|
|
193
193
|
|
|
194
|
+
### Autonomous continuation rule
|
|
195
|
+
|
|
196
|
+
- After the required user confirmation checkpoint is satisfied, do not pause between required stages.
|
|
197
|
+
- Do not ask the user what to do next while required workflow stages remain unfinished.
|
|
198
|
+
- Do not use optional phrasing like "If you want, I can..." for unfinished required workflow steps.
|
|
199
|
+
- If a stage reports partial progress (for example "waiting for tests"), treat it as non-terminal and issue a focused same-stage follow-up immediately.
|
|
200
|
+
|
|
194
201
|
### Direct-execution prohibition
|
|
195
202
|
|
|
196
203
|
- Do not substitute manual/orchestrator-only answers for work that should be performed by stage agents.
|
|
@@ -208,3 +215,30 @@ When a workflow ends (success or escalation), return:
|
|
|
208
215
|
4. `TEST_RESULTS` (if reported)
|
|
209
216
|
5. `OPEN_ISSUES`
|
|
210
217
|
6. `RECOMMENDED_NEXT_STEP`
|
|
218
|
+
|
|
219
|
+
`RECOMMENDED_NEXT_STEP` is reserved for true end-of-workflow recommendations only; do not use it to defer unfinished required stages.
|
|
220
|
+
|
|
221
|
+
## Auto-handoff contract (plugin-facing)
|
|
222
|
+
|
|
223
|
+
When the next step is deterministic and does not require user input, append this exact block at the end of the response:
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
RECOMMENDED_NEXT_STEP=<ACTION> "<human-readable next step>"
|
|
227
|
+
|
|
228
|
+
AUTO_HANDOFF=YES|NO
|
|
229
|
+
AUTO_HANDOFF_TARGET=feature-factory|building|documenting|reviewing
|
|
230
|
+
AUTO_HANDOFF_REASON=CONTINUE_WORKFLOW|REWORK|DOCUMENT|REVIEW
|
|
231
|
+
AUTO_HANDOFF_PROMPT<<FF_PROMPT
|
|
232
|
+
<exact prompt text for next turn>
|
|
233
|
+
FF_PROMPT
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Rules:
|
|
237
|
+
|
|
238
|
+
- Use `AUTO_HANDOFF=YES` only when the next step is deterministic and should run automatically.
|
|
239
|
+
- Use `AUTO_HANDOFF_TARGET=feature-factory` when the workflow should continue across multiple stages.
|
|
240
|
+
- Use stage targets (`building`, `documenting`, `reviewing`) only for focused one-stage follow-up.
|
|
241
|
+
- Never use `AUTO_HANDOFF=YES` before the required user confirmation checkpoint.
|
|
242
|
+
- If complete, blocked, escalated, or waiting on the user, output `AUTO_HANDOFF=NO`.
|
|
243
|
+
- `RECOMMENDED_NEXT_STEP` must match the machine-readable handoff block semantically.
|
|
244
|
+
- For normal workflow progression, continue by delegating to stage agents directly (`@building`, `@documenting`, `@reviewing`), not by invoking slash commands such as `/ff-review`, `/ff-document`, or `/ff-rework`.
|
package/agents/ff-research.md
CHANGED
|
@@ -9,6 +9,11 @@ tools:
|
|
|
9
9
|
bash: false
|
|
10
10
|
skill: true
|
|
11
11
|
task: false
|
|
12
|
+
'codebase-memory-mcp_search_graph': true
|
|
13
|
+
'codebase-memory-mcp_get_architecture': true
|
|
14
|
+
'codebase-memory-mcp_trace_call_path': true
|
|
15
|
+
'codebase-memory-mcp_get_code_snippet': true
|
|
16
|
+
'codebase-memory-mcp_search_code': true
|
|
12
17
|
'morph-mcp_codebase_search': true
|
|
13
18
|
'morph-mcp_github_codebase_search': true
|
|
14
19
|
permission:
|
|
@@ -53,13 +58,19 @@ You have access to powerful external research tools. **Use them proactively:**
|
|
|
53
58
|
|
|
54
59
|
### Semantic Code Search
|
|
55
60
|
|
|
56
|
-
- **`
|
|
61
|
+
- **`codebase-memory-mcp_search_graph`** — Graph-first local discovery for functions/classes/routes and their relationships.
|
|
62
|
+
- **`codebase-memory-mcp_get_architecture`** — High-level package/service map for architectural context.
|
|
63
|
+
- **`codebase-memory-mcp_trace_call_path`** — Call-chain tracing for impact analysis and behavior verification.
|
|
64
|
+
- **`codebase-memory-mcp_get_code_snippet`** — Read exact symbol source after graph discovery.
|
|
65
|
+
- **`morph-mcp_codebase_search`** — Semantic local fallback when graph indexing is unavailable.
|
|
57
66
|
|
|
58
67
|
Search fallback order for code understanding:
|
|
59
68
|
|
|
60
|
-
1. `
|
|
61
|
-
2. `
|
|
62
|
-
3. `
|
|
69
|
+
1. `codebase-memory-mcp_search_graph` + `codebase-memory-mcp_get_architecture` for local graph-first discovery.
|
|
70
|
+
2. `codebase-memory-mcp_trace_call_path` / `codebase-memory-mcp_get_code_snippet` for local behavior verification.
|
|
71
|
+
3. `morph-mcp_codebase_search` for local semantic fallback when graph search is unavailable.
|
|
72
|
+
4. `morph-mcp_github_codebase_search` for upstream/public GitHub internals when needed.
|
|
73
|
+
5. `gh_grep_searchGitHub` for exact code pattern matching across public repos.
|
|
63
74
|
|
|
64
75
|
## GitHub Workflow Guidance
|
|
65
76
|
|
package/agents/planning.md
CHANGED
|
@@ -10,6 +10,11 @@ tools:
|
|
|
10
10
|
bash: false
|
|
11
11
|
skill: true
|
|
12
12
|
task: true
|
|
13
|
+
'codebase-memory-mcp_search_graph': true
|
|
14
|
+
'codebase-memory-mcp_get_architecture': true
|
|
15
|
+
'codebase-memory-mcp_trace_call_path': true
|
|
16
|
+
'codebase-memory-mcp_get_code_snippet': true
|
|
17
|
+
'codebase-memory-mcp_search_code': true
|
|
13
18
|
'morph-mcp_codebase_search': true
|
|
14
19
|
permission:
|
|
15
20
|
skill:
|
|
@@ -40,19 +45,22 @@ You are a read-only agent — you do not write or edit files.
|
|
|
40
45
|
|
|
41
46
|
## Semantic Code Search
|
|
42
47
|
|
|
43
|
-
Use
|
|
48
|
+
Use codebase-memory MCP tools first when planning:
|
|
44
49
|
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
50
|
+
- `codebase-memory-mcp_search_graph` to find candidate functions, classes, and routes
|
|
51
|
+
- `codebase-memory-mcp_get_architecture` for package/service-level orientation
|
|
52
|
+
- `codebase-memory-mcp_trace_call_path` for dependency and impact analysis
|
|
53
|
+
- `codebase-memory-mcp_get_code_snippet` to inspect exact implementation context
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
This is preferred over warp_grep/warpgrep-style semantic discovery because graph-aware results surface structural context directly.
|
|
50
56
|
|
|
51
57
|
Search fallback order:
|
|
52
58
|
|
|
53
|
-
1. `
|
|
54
|
-
2. `
|
|
55
|
-
3. `
|
|
59
|
+
1. `codebase-memory-mcp_search_graph` + `codebase-memory-mcp_get_architecture` for graph-first discovery.
|
|
60
|
+
2. `codebase-memory-mcp_trace_call_path` / `codebase-memory-mcp_get_code_snippet` for call-flow and source verification.
|
|
61
|
+
3. `morph-mcp_codebase_search` if codebase-memory MCP is unavailable or the repository is not indexed yet.
|
|
62
|
+
4. `read` to verify relevant source context.
|
|
63
|
+
5. `grep` for exact symbol/literal checks when needed.
|
|
56
64
|
|
|
57
65
|
## GitHub Workflow Guidance
|
|
58
66
|
|
package/agents/reviewing.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
description: Unified validation agent for code and documentation. Performs acceptance, quality, security, and architecture review with context-driven scope.
|
|
3
3
|
mode: primary
|
|
4
4
|
color: '#8b5cf6'
|
|
5
|
-
model: opencode/
|
|
5
|
+
model: opencode/glm-5.1
|
|
6
6
|
tools:
|
|
7
7
|
read: true
|
|
8
8
|
write: false
|
|
@@ -10,6 +10,11 @@ tools:
|
|
|
10
10
|
bash: false
|
|
11
11
|
skill: true
|
|
12
12
|
task: true
|
|
13
|
+
'codebase-memory-mcp_search_graph': true
|
|
14
|
+
'codebase-memory-mcp_get_architecture': true
|
|
15
|
+
'codebase-memory-mcp_trace_call_path': true
|
|
16
|
+
'codebase-memory-mcp_get_code_snippet': true
|
|
17
|
+
'codebase-memory-mcp_search_code': true
|
|
13
18
|
'morph-mcp_codebase_search': true
|
|
14
19
|
permission:
|
|
15
20
|
skill:
|
|
@@ -40,19 +45,22 @@ You are a read-only agent — you do not write or edit files.
|
|
|
40
45
|
|
|
41
46
|
## Semantic Code Search
|
|
42
47
|
|
|
43
|
-
Use
|
|
48
|
+
Use codebase-memory MCP tools first when reviewing:
|
|
44
49
|
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
50
|
+
- `codebase-memory-mcp_search_graph` to locate related implementations by structure + name pattern
|
|
51
|
+
- `codebase-memory-mcp_get_architecture` to validate subsystem boundaries and coupling
|
|
52
|
+
- `codebase-memory-mcp_trace_call_path` to verify security/validation coverage across call paths
|
|
53
|
+
- `codebase-memory-mcp_get_code_snippet` to inspect concrete evidence for findings
|
|
48
54
|
|
|
49
|
-
Prefer this over
|
|
55
|
+
Prefer this over warp_grep/warpgrep-style search so findings are anchored to graph-aware dependencies, not isolated text matches.
|
|
50
56
|
|
|
51
57
|
Search fallback order:
|
|
52
58
|
|
|
53
|
-
1. `
|
|
54
|
-
2. `
|
|
55
|
-
3. `
|
|
59
|
+
1. `codebase-memory-mcp_search_graph` + `codebase-memory-mcp_get_architecture` for discovery and context.
|
|
60
|
+
2. `codebase-memory-mcp_trace_call_path` / `codebase-memory-mcp_get_code_snippet` for evidence collection.
|
|
61
|
+
3. `morph-mcp_codebase_search` if codebase-memory MCP is unavailable or not indexed.
|
|
62
|
+
4. `read` to verify concrete evidence and line references.
|
|
63
|
+
5. `grep` for exact symbol/literal checks when needed.
|
|
56
64
|
|
|
57
65
|
## GitHub Workflow Guidance
|
|
58
66
|
|
|
@@ -96,6 +104,28 @@ When acting as gate reviewer, output status line exactly:
|
|
|
96
104
|
- `REVIEW_GATE=APPROVED|REWORK|ESCALATE`
|
|
97
105
|
- `DOCUMENTATION_GATE=APPROVED|REWORK|ESCALATE`
|
|
98
106
|
|
|
107
|
+
## Auto-handoff output contract (for standalone review runs)
|
|
108
|
+
|
|
109
|
+
When a standalone review clearly requires rework and the next action is deterministic, append:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
RECOMMENDED_NEXT_STEP=REWORK "<specific rework summary>"
|
|
113
|
+
AUTO_HANDOFF=YES
|
|
114
|
+
AUTO_HANDOFF_TARGET=building
|
|
115
|
+
AUTO_HANDOFF_REASON=REWORK
|
|
116
|
+
AUTO_HANDOFF_PROMPT<<FF_PROMPT
|
|
117
|
+
apply the rework recommendations
|
|
118
|
+
|
|
119
|
+
- <specific action item>
|
|
120
|
+
FF_PROMPT
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Otherwise emit:
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
AUTO_HANDOFF=NO
|
|
127
|
+
```
|
|
128
|
+
|
|
99
129
|
## Operating Mode
|
|
100
130
|
|
|
101
131
|
- Use result-based handoff (`$RESULT[...]`) rather than file-based artifacts.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Plugin, type PluginInput } from '@opencode-ai/plugin';
|
|
2
|
+
type AutoHandoffTarget = 'feature-factory' | 'building' | 'documenting' | 'reviewing';
|
|
3
|
+
type AutoHandoffReason = 'CONTINUE_WORKFLOW' | 'REWORK' | 'DOCUMENT' | 'REVIEW';
|
|
4
|
+
export type ParsedAutoHandoff = {
|
|
5
|
+
enabled: false;
|
|
6
|
+
} | {
|
|
7
|
+
enabled: true;
|
|
8
|
+
target: AutoHandoffTarget;
|
|
9
|
+
reason: AutoHandoffReason;
|
|
10
|
+
prompt: string;
|
|
11
|
+
};
|
|
12
|
+
type Client = PluginInput['client'];
|
|
13
|
+
type SessionMetadata = {
|
|
14
|
+
parentID?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function parseAutoHandoff(text: string): ParsedAutoHandoff | null;
|
|
17
|
+
export declare function fingerprint(sessionId: string, messageId: string, handoff: Extract<ParsedAutoHandoff, {
|
|
18
|
+
enabled: true;
|
|
19
|
+
}>): string;
|
|
20
|
+
export declare function getSessionMetadata(client: Client, sessionId: string): Promise<SessionMetadata>;
|
|
21
|
+
export declare const AutoHandoffHooksPlugin: Plugin;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
const SERVICE_NAME = 'feature-factory';
|
|
2
|
+
const MAX_AUTO_HANDOFFS_PER_SESSION = 5;
|
|
3
|
+
const AUTO_HANDOFF_FLAG_RE = /^AUTO_HANDOFF=(YES|NO)$/m;
|
|
4
|
+
const AUTO_HANDOFF_TARGET_RE = /^AUTO_HANDOFF_TARGET=(feature-factory|building|documenting|reviewing)$/m;
|
|
5
|
+
const AUTO_HANDOFF_REASON_RE = /^AUTO_HANDOFF_REASON=(CONTINUE_WORKFLOW|REWORK|DOCUMENT|REVIEW)$/m;
|
|
6
|
+
const AUTO_HANDOFF_PROMPT_RE = /AUTO_HANDOFF_PROMPT<<FF_PROMPT\r?\n([\s\S]*?)\r?\nFF_PROMPT/m;
|
|
7
|
+
async function log(client, level, message, extra) {
|
|
8
|
+
try {
|
|
9
|
+
await client.app.log({
|
|
10
|
+
body: {
|
|
11
|
+
service: SERVICE_NAME,
|
|
12
|
+
level,
|
|
13
|
+
message,
|
|
14
|
+
extra,
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function extractMessageText(info) {
|
|
23
|
+
if (!info || typeof info !== 'object') {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
const parts = info.parts;
|
|
27
|
+
if (!Array.isArray(parts)) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
return parts
|
|
31
|
+
.map((part) => {
|
|
32
|
+
if (!part || typeof part !== 'object') {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
const typedPart = part;
|
|
36
|
+
return typedPart.type === 'text' && typeof typedPart.text === 'string' ? typedPart.text : '';
|
|
37
|
+
})
|
|
38
|
+
.join('');
|
|
39
|
+
}
|
|
40
|
+
export function parseAutoHandoff(text) {
|
|
41
|
+
const enabledMatch = text.match(AUTO_HANDOFF_FLAG_RE);
|
|
42
|
+
if (!enabledMatch) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const enabled = enabledMatch[1];
|
|
46
|
+
if (enabled === 'NO') {
|
|
47
|
+
return { enabled: false };
|
|
48
|
+
}
|
|
49
|
+
const targetMatch = text.match(AUTO_HANDOFF_TARGET_RE);
|
|
50
|
+
const reasonMatch = text.match(AUTO_HANDOFF_REASON_RE);
|
|
51
|
+
const promptMatch = text.match(AUTO_HANDOFF_PROMPT_RE);
|
|
52
|
+
const target = targetMatch?.[1];
|
|
53
|
+
const reason = reasonMatch?.[1];
|
|
54
|
+
const prompt = promptMatch?.[1]?.trim();
|
|
55
|
+
if (!target || !reason || !prompt) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
enabled: true,
|
|
60
|
+
target: target,
|
|
61
|
+
reason: reason,
|
|
62
|
+
prompt,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function fingerprint(sessionId, messageId, handoff) {
|
|
66
|
+
return [sessionId, messageId, handoff.target, handoff.reason, handoff.prompt].join('::');
|
|
67
|
+
}
|
|
68
|
+
export async function getSessionMetadata(client, sessionId) {
|
|
69
|
+
try {
|
|
70
|
+
const response = await client.session.get({ path: { id: sessionId } });
|
|
71
|
+
return {
|
|
72
|
+
parentID: response.data?.parentID,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
await log(client, 'warn', 'auto-handoff.session-metadata-fetch-failed', { sessionId });
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export const AutoHandoffHooksPlugin = async ({ client }) => {
|
|
81
|
+
const buffers = new Map();
|
|
82
|
+
const sessionState = new Map();
|
|
83
|
+
return {
|
|
84
|
+
event: async ({ event }) => {
|
|
85
|
+
const properties = event
|
|
86
|
+
.properties;
|
|
87
|
+
const sessionId = properties?.sessionID ?? properties?.sessionId;
|
|
88
|
+
if (!sessionId) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (event.type === 'session.deleted') {
|
|
92
|
+
buffers.delete(sessionId);
|
|
93
|
+
sessionState.delete(sessionId);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (event.type === 'message.updated') {
|
|
97
|
+
const info = event
|
|
98
|
+
.properties?.info;
|
|
99
|
+
if (!info || info.role !== 'assistant' || !info.id) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
buffers.set(sessionId, {
|
|
103
|
+
messageId: info.id,
|
|
104
|
+
text: extractMessageText(info),
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (event.type === 'message.part.updated') {
|
|
109
|
+
const state = buffers.get(sessionId);
|
|
110
|
+
if (!state) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const props = event.properties;
|
|
114
|
+
const part = props?.part;
|
|
115
|
+
if (part?.type !== 'text') {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (typeof props?.delta === 'string') {
|
|
119
|
+
state.text += props.delta;
|
|
120
|
+
}
|
|
121
|
+
else if (typeof part.text === 'string') {
|
|
122
|
+
state.text = part.text;
|
|
123
|
+
}
|
|
124
|
+
buffers.set(sessionId, state);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (event.type !== 'session.idle') {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const buffer = buffers.get(sessionId);
|
|
131
|
+
if (!buffer?.text) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const parsed = parseAutoHandoff(buffer.text);
|
|
135
|
+
if (!parsed || !parsed.enabled) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const meta = await getSessionMetadata(client, sessionId);
|
|
139
|
+
if (meta.parentID) {
|
|
140
|
+
await log(client, 'debug', 'auto-handoff.skipped-sub-agent-session', {
|
|
141
|
+
sessionId,
|
|
142
|
+
parentID: meta.parentID,
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const state = sessionState.get(sessionId) ?? {
|
|
147
|
+
count: 0,
|
|
148
|
+
fingerprints: new Set(),
|
|
149
|
+
};
|
|
150
|
+
if (state.count >= MAX_AUTO_HANDOFFS_PER_SESSION) {
|
|
151
|
+
await log(client, 'warn', 'auto-handoff.skipped-cap-reached', {
|
|
152
|
+
sessionId,
|
|
153
|
+
max: MAX_AUTO_HANDOFFS_PER_SESSION,
|
|
154
|
+
});
|
|
155
|
+
sessionState.set(sessionId, state);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const key = fingerprint(sessionId, buffer.messageId, parsed);
|
|
159
|
+
if (state.fingerprints.has(key)) {
|
|
160
|
+
await log(client, 'debug', 'auto-handoff.skipped-duplicate', { sessionId });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
state.fingerprints.add(key);
|
|
164
|
+
state.count += 1;
|
|
165
|
+
sessionState.set(sessionId, state);
|
|
166
|
+
await log(client, 'info', 'auto-handoff.dispatching', {
|
|
167
|
+
sessionId,
|
|
168
|
+
target: parsed.target,
|
|
169
|
+
reason: parsed.reason,
|
|
170
|
+
});
|
|
171
|
+
try {
|
|
172
|
+
await client.session.prompt({
|
|
173
|
+
path: { id: sessionId },
|
|
174
|
+
body: {
|
|
175
|
+
agent: parsed.target,
|
|
176
|
+
parts: [
|
|
177
|
+
{
|
|
178
|
+
type: 'text',
|
|
179
|
+
text: parsed.prompt,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
await log(client, 'error', 'auto-handoff.dispatch-failed', {
|
|
187
|
+
sessionId,
|
|
188
|
+
target: parsed.target,
|
|
189
|
+
reason: parsed.reason,
|
|
190
|
+
error: String(error),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { StopQualityGateHooksPlugin } from './stop-quality-gate.js';
|
|
2
|
+
import { AutoHandoffHooksPlugin } from './auto-handoff.js';
|
|
2
3
|
import { updateMCPConfig } from './mcp-config.js';
|
|
3
4
|
import { updateAgentConfig } from './agent-config.js';
|
|
4
5
|
import { updatePluginConfig } from './plugin-config.js';
|
|
5
6
|
import { $ } from 'bun';
|
|
7
|
+
function composeAsyncHandlers(...handlers) {
|
|
8
|
+
const activeHandlers = handlers.filter((handler) => Boolean(handler));
|
|
9
|
+
if (activeHandlers.length === 0) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return (async (...args) => {
|
|
13
|
+
for (const handler of activeHandlers) {
|
|
14
|
+
await handler(...args);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
6
18
|
/**
|
|
7
19
|
* Feature Factory Plugin
|
|
8
20
|
*
|
|
@@ -42,10 +54,13 @@ export const FeatureFactoryPlugin = async (input) => {
|
|
|
42
54
|
}
|
|
43
55
|
// Load hooks from the quality gate plugin
|
|
44
56
|
const qualityGateHooks = await StopQualityGateHooksPlugin(input).catch(() => ({}));
|
|
57
|
+
const autoHandoffHooks = await AutoHandoffHooksPlugin(input).catch(() => ({}));
|
|
45
58
|
// Feature Factory orchestration now runs via native sub-agent handoff in
|
|
46
59
|
// the feature-factory agent prompt (LLM-driven workflow). ff_* workflow MCP
|
|
47
60
|
// tools are no longer registered from this plugin.
|
|
48
61
|
return {
|
|
49
|
-
|
|
62
|
+
event: composeAsyncHandlers(qualityGateHooks.event, autoHandoffHooks.event),
|
|
63
|
+
'tool.execute.before': composeAsyncHandlers(qualityGateHooks['tool.execute.before'], autoHandoffHooks['tool.execute.before']),
|
|
64
|
+
'tool.execute.after': composeAsyncHandlers(qualityGateHooks['tool.execute.after'], autoHandoffHooks['tool.execute.after']),
|
|
50
65
|
};
|
|
51
66
|
};
|
package/dist/mcp-config.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ type BunShell = any;
|
|
|
2
2
|
/**
|
|
3
3
|
* Default MCP server configuration to be added by the plugin
|
|
4
4
|
* These servers will be merged into the global OpenCode config.
|
|
5
|
+
*
|
|
6
|
+
* Note: codebase-memory-mcp is intentionally not auto-provisioned here.
|
|
7
|
+
* It is treated as an optional prerequisite because it commonly needs
|
|
8
|
+
* repository-specific indexing/setup before graph queries are useful.
|
|
5
9
|
*/
|
|
6
10
|
export declare const DEFAULT_MCP_SERVERS: {
|
|
7
11
|
readonly 'jina-ai': {
|
package/dist/mcp-config.js
CHANGED
|
@@ -2,6 +2,10 @@ import { isRecord, updateGlobalOpenCodeConfigBlock } from './opencode-global-con
|
|
|
2
2
|
/**
|
|
3
3
|
* Default MCP server configuration to be added by the plugin
|
|
4
4
|
* These servers will be merged into the global OpenCode config.
|
|
5
|
+
*
|
|
6
|
+
* Note: codebase-memory-mcp is intentionally not auto-provisioned here.
|
|
7
|
+
* It is treated as an optional prerequisite because it commonly needs
|
|
8
|
+
* repository-specific indexing/setup before graph queries are useful.
|
|
5
9
|
*/
|
|
6
10
|
export const DEFAULT_MCP_SERVERS = {
|
|
7
11
|
'jina-ai': {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@syntesseraai/opencode-feature-factory",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.15",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "OpenCode plugin for Feature Factory agents - provides sub-agents and skills for validation, review, security, and architecture assessment",
|
|
7
7
|
"license": "MIT",
|
|
@@ -34,9 +34,7 @@
|
|
|
34
34
|
],
|
|
35
35
|
"scripts": {},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@
|
|
38
|
-
"@opencode-ai/plugin": "^1.1.48",
|
|
39
|
-
"glob": "^10.0.0"
|
|
37
|
+
"@opencode-ai/plugin": "^1.1.48"
|
|
40
38
|
},
|
|
41
39
|
"devDependencies": {
|
|
42
40
|
"@types/bun": "^1.2.6",
|