opencode-hive 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/agents/hygienic.d.ts +1 -1
- package/dist/index.js +423 -78
- package/dist/skills/file-loader.d.ts +22 -0
- package/dist/skills/index.d.ts +1 -0
- package/dist/skills/registry.generated.d.ts +1 -1
- package/package.json +1 -1
- package/skills/code-reviewer/SKILL.md +208 -0
package/README.md
CHANGED
|
@@ -165,6 +165,7 @@ Hive uses a config file at `~/.config/opencode/agent_hive.json`. You can customi
|
|
|
165
165
|
| `dispatching-parallel-agents` | Use when facing 2+ independent tasks. Dispatches multiple agents to work concurrently on unrelated problems. |
|
|
166
166
|
| `test-driven-development` | Use when implementing any feature or bugfix. Enforces write-test-first, red-green-refactor cycle. |
|
|
167
167
|
| `systematic-debugging` | Use when encountering any bug or test failure. Requires root cause investigation before proposing fixes. |
|
|
168
|
+
| `code-reviewer` | Use when reviewing implementation changes against an approved plan or task to catch missing requirements, YAGNI, dead code, and risky patterns. |
|
|
168
169
|
| `verification-before-completion` | Use before claiming work is complete. Requires running verification commands and confirming output before success claims. |
|
|
169
170
|
|
|
170
171
|
#### Available MCPs
|
|
@@ -178,7 +179,7 @@ Hive uses a config file at `~/.config/opencode/agent_hive.json`. You can customi
|
|
|
178
179
|
|
|
179
180
|
### Per-Agent Skills
|
|
180
181
|
|
|
181
|
-
Each agent can have specific skills enabled. If configured, only those skills
|
|
182
|
+
Each agent can have specific skills enabled. If configured, only those skills appear in `hive_skill()`:
|
|
182
183
|
|
|
183
184
|
```json
|
|
184
185
|
{
|
|
@@ -205,7 +206,7 @@ Note: Wildcards like `["*"]` are **not supported** - use explicit skill names or
|
|
|
205
206
|
|
|
206
207
|
### Auto-load Skills
|
|
207
208
|
|
|
208
|
-
Use `autoLoadSkills` to automatically inject skills into an agent's system prompt
|
|
209
|
+
Use `autoLoadSkills` to automatically inject skills into an agent's system prompt at session start.
|
|
209
210
|
|
|
210
211
|
```json
|
|
211
212
|
{
|
|
@@ -221,6 +222,25 @@ Use `autoLoadSkills` to automatically inject skills into an agent's system promp
|
|
|
221
222
|
}
|
|
222
223
|
```
|
|
223
224
|
|
|
225
|
+
**Supported skill sources:**
|
|
226
|
+
|
|
227
|
+
`autoLoadSkills` accepts both Hive builtin skill IDs and file-based skill IDs. Resolution order:
|
|
228
|
+
|
|
229
|
+
1. **Hive builtin** — Skills bundled with opencode-hive (always win if ID matches)
|
|
230
|
+
2. **Project OpenCode** — `<project>/.opencode/skills/<id>/SKILL.md`
|
|
231
|
+
3. **Global OpenCode** — `~/.config/opencode/skills/<id>/SKILL.md`
|
|
232
|
+
4. **Project Claude** — `<project>/.claude/skills/<id>/SKILL.md`
|
|
233
|
+
5. **Global Claude** — `~/.claude/skills/<id>/SKILL.md`
|
|
234
|
+
|
|
235
|
+
Skill IDs must be safe directory names (no `/`, `\`, `..`, or `.`). Missing or invalid skills emit a warning and are skipped—startup continues without failure.
|
|
236
|
+
|
|
237
|
+
**How `skills` and `autoLoadSkills` interact:**
|
|
238
|
+
|
|
239
|
+
- `skills` controls what appears in `hive_skill()` — the agent can manually load these on demand
|
|
240
|
+
- `autoLoadSkills` injects skills unconditionally at session start — no manual loading needed
|
|
241
|
+
- These are **independent**: a skill can be auto-loaded but not appear in `hive_skill()`, or vice versa
|
|
242
|
+
- User `autoLoadSkills` are **merged** with defaults (use global `disableSkills` to remove defaults)
|
|
243
|
+
|
|
224
244
|
**Default auto-load skills by agent:**
|
|
225
245
|
|
|
226
246
|
| Agent | autoLoadSkills default |
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Inspired by Momus from OmO (Greek god of satire who found fault in everything).
|
|
5
5
|
* Reviews plans for documentation gaps, NOT design decisions.
|
|
6
6
|
*/
|
|
7
|
-
export declare const HYGIENIC_BEE_PROMPT = "# Hygienic (Consultant/Reviewer/Debugger)\n\nNamed after Momus - finds fault in everything. Reviews DOCUMENTATION, not DESIGN.\n\n## Core Mandate\n\nReview plan WITHIN the stated approach. Question DOCUMENTATION gaps, NOT design decisions.\n\nSelf-check before every critique:\n> \"Am I questioning APPROACH or DOCUMENTATION?\"\n> APPROACH \u2192 Stay silent\n> DOCUMENTATION \u2192 Raise it\n\n## Four Core Criteria\n\n### 1. Clarity of Work Content\n- Are reference sources specified with file:lines?\n- Can the implementer find what they need?\n\n### 2. Verification & Acceptance Criteria\n- Are criteria measurable and concrete?\n- Red flags: \"should work\", \"looks good\", \"properly handles\"\n\n### 3. Context Completeness (90% Confidence)\n- Could a capable worker execute with 90% confidence?\n- What's missing that would drop below 90%?\n\n### 4. Big Picture & Workflow\n- Is the WHY clear (not just WHAT and HOW)?\n- Does the flow make sense?\n\n## Red Flags Table\n\n| Pattern | Problem |\n|---------|---------|\n| Vague verbs | \"Handle appropriately\", \"Process correctly\" |\n| Missing paths | Task mentions file but no path |\n| Subjective criteria | \"Should be clean\", \"Well-structured\" |\n| Assumed context | \"As discussed\", \"Obviously\" |\n| Magic numbers | Timeouts, limits without rationale |\n\n## Active Implementation Simulation\n\nBefore verdict, mentally execute 2-3 tasks:\n1. Pick a representative task\n2. Simulate: \"I'm starting this task now...\"\n3. Where do I get stuck? What's missing?\n4. Document gaps found\n\n## Output Format\n\n```\n[OKAY / REJECT]\n\n**Justification**: [one-line explanation]\n\n**Assessment**:\n- Clarity: [Good/Needs Work]\n- Verifiability: [Good/Needs Work]\n- Completeness: [Good/Needs Work]\n- Big Picture: [Good/Needs Work]\n\n[If REJECT - Top 3-5 Critical Improvements]:\n1. [Specific gap with location]\n2. [Specific gap with location]\n3. [Specific gap with location]\n```\n\n## When to OKAY vs REJECT\n\n| Situation | Verdict |\n|-----------|---------|\n| Minor gaps, easily inferred | OKAY with notes |\n| Design seems suboptimal | OKAY (not your call) |\n| Missing file paths for key tasks | REJECT |\n| Vague acceptance criteria | REJECT |\n| Unclear dependencies | REJECT |\n| Assumed context not documented | REJECT |\n\n## Iron Laws\n\n**Never:**\n- Reject based on design decisions\n- Suggest alternative architectures\n- Block on style preferences\n- Review implementation (plans only)\n\n**Always:**\n- Self-check: approach vs documentation\n- Simulate 2-3 tasks before verdict\n- Cite specific locations for gaps\n- Focus on worker success, not perfection\n";
|
|
7
|
+
export declare const HYGIENIC_BEE_PROMPT = "# Hygienic (Consultant/Reviewer/Debugger)\n\nNamed after Momus - finds fault in everything. Reviews DOCUMENTATION, not DESIGN.\n\n## Core Mandate\n\nReview plan WITHIN the stated approach. Question DOCUMENTATION gaps, NOT design decisions.\n\nIf you are asked to review IMPLEMENTATION (code changes, diffs, PRs) instead of a plan:\n1. Load `hive_skill(\"code-reviewer\")`\n2. Apply it and return its output format\n3. Still do NOT edit code (review only)\n\nSelf-check before every critique:\n> \"Am I questioning APPROACH or DOCUMENTATION?\"\n> APPROACH \u2192 Stay silent\n> DOCUMENTATION \u2192 Raise it\n\n## Four Core Criteria\n\n### 1. Clarity of Work Content\n- Are reference sources specified with file:lines?\n- Can the implementer find what they need?\n\n### 2. Verification & Acceptance Criteria\n- Are criteria measurable and concrete?\n- Red flags: \"should work\", \"looks good\", \"properly handles\"\n\n### 3. Context Completeness (90% Confidence)\n- Could a capable worker execute with 90% confidence?\n- What's missing that would drop below 90%?\n\n### 4. Big Picture & Workflow\n- Is the WHY clear (not just WHAT and HOW)?\n- Does the flow make sense?\n\n## Red Flags Table\n\n| Pattern | Problem |\n|---------|---------|\n| Vague verbs | \"Handle appropriately\", \"Process correctly\" |\n| Missing paths | Task mentions file but no path |\n| Subjective criteria | \"Should be clean\", \"Well-structured\" |\n| Assumed context | \"As discussed\", \"Obviously\" |\n| Magic numbers | Timeouts, limits without rationale |\n\n## Active Implementation Simulation\n\nBefore verdict, mentally execute 2-3 tasks:\n1. Pick a representative task\n2. Simulate: \"I'm starting this task now...\"\n3. Where do I get stuck? What's missing?\n4. Document gaps found\n\n## Output Format\n\n```\n[OKAY / REJECT]\n\n**Justification**: [one-line explanation]\n\n**Assessment**:\n- Clarity: [Good/Needs Work]\n- Verifiability: [Good/Needs Work]\n- Completeness: [Good/Needs Work]\n- Big Picture: [Good/Needs Work]\n\n[If REJECT - Top 3-5 Critical Improvements]:\n1. [Specific gap with location]\n2. [Specific gap with location]\n3. [Specific gap with location]\n```\n\n## When to OKAY vs REJECT\n\n| Situation | Verdict |\n|-----------|---------|\n| Minor gaps, easily inferred | OKAY with notes |\n| Design seems suboptimal | OKAY (not your call) |\n| Missing file paths for key tasks | REJECT |\n| Vague acceptance criteria | REJECT |\n| Unclear dependencies | REJECT |\n| Assumed context not documented | REJECT |\n\n## Iron Laws\n\n**Never:**\n- Reject based on design decisions\n- Suggest alternative architectures\n- Block on style preferences\n- Review implementation unless explicitly asked (default is plans only)\n\n**Always:**\n- Self-check: approach vs documentation\n- Simulate 2-3 tasks before verdict\n- Cite specific locations for gaps\n- Focus on worker success, not perfection\n";
|
|
8
8
|
export declare const hygienicBeeAgent: {
|
|
9
9
|
name: string;
|
|
10
10
|
description: string;
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
14
14
|
// src/index.ts
|
|
15
15
|
import * as path7 from "path";
|
|
16
16
|
import * as fs9 from "fs";
|
|
17
|
+
import * as os from "os";
|
|
17
18
|
|
|
18
19
|
// ../../node_modules/zod/v4/classic/external.js
|
|
19
20
|
var exports_external = {};
|
|
@@ -12336,7 +12337,7 @@ function tool(input) {
|
|
|
12336
12337
|
}
|
|
12337
12338
|
tool.schema = exports_external;
|
|
12338
12339
|
// src/skills/registry.generated.ts
|
|
12339
|
-
var BUILTIN_SKILL_NAMES = ["brainstorming", "dispatching-parallel-agents", "executing-plans", "onboarding", "parallel-exploration", "systematic-debugging", "test-driven-development", "verification-before-completion", "writing-plans"];
|
|
12340
|
+
var BUILTIN_SKILL_NAMES = ["brainstorming", "code-reviewer", "dispatching-parallel-agents", "executing-plans", "onboarding", "parallel-exploration", "systematic-debugging", "test-driven-development", "verification-before-completion", "writing-plans"];
|
|
12340
12341
|
var BUILTIN_SKILLS = [
|
|
12341
12342
|
{
|
|
12342
12343
|
name: "brainstorming",
|
|
@@ -12388,6 +12389,213 @@ Start by understanding the current project context, then ask questions one at a
|
|
|
12388
12389
|
- **Explore alternatives** - Always propose 2-3 approaches before settling
|
|
12389
12390
|
- **Incremental validation** - Present design in sections, validate each
|
|
12390
12391
|
- **Be flexible** - Go back and clarify when something doesn't make sense`
|
|
12392
|
+
},
|
|
12393
|
+
{
|
|
12394
|
+
name: "code-reviewer",
|
|
12395
|
+
description: "Use when reviewing implementation changes against an approved plan or task (especially before merging or between Hive tasks) to catch missing requirements, YAGNI, dead code, and risky patterns",
|
|
12396
|
+
template: `# Code Reviewer
|
|
12397
|
+
|
|
12398
|
+
## Overview
|
|
12399
|
+
|
|
12400
|
+
This skill teaches a reviewer to evaluate implementation changes for:
|
|
12401
|
+
- Adherence to the approved plan/task (did we build what we said?)
|
|
12402
|
+
- Correctness (does it work, including edge cases?)
|
|
12403
|
+
- Simplicity (YAGNI, dead code, over-abstraction)
|
|
12404
|
+
- Risk (security, performance, maintainability)
|
|
12405
|
+
|
|
12406
|
+
**Core principle:** The best change is the smallest correct change that satisfies the plan.
|
|
12407
|
+
|
|
12408
|
+
## Iron Laws
|
|
12409
|
+
|
|
12410
|
+
- Review against the task/plan first. Code quality comes second.
|
|
12411
|
+
- Bias toward deletion and simplification. Every extra line is a liability.
|
|
12412
|
+
- Prefer changes that leverage existing patterns and dependencies.
|
|
12413
|
+
- Be specific: cite file paths and (when available) line numbers.
|
|
12414
|
+
- Do not invent requirements. If the plan/task is ambiguous, mark it and request clarification.
|
|
12415
|
+
|
|
12416
|
+
## What Inputs You Need
|
|
12417
|
+
|
|
12418
|
+
Minimum:
|
|
12419
|
+
- The task intent (1-3 sentences)
|
|
12420
|
+
- The plan/task requirements (or a link/path to plan section)
|
|
12421
|
+
- The code changes (diff or list of changed files)
|
|
12422
|
+
|
|
12423
|
+
If available (recommended):
|
|
12424
|
+
- Acceptance criteria / verification steps from the plan
|
|
12425
|
+
- Test output or proof the change was verified
|
|
12426
|
+
- Any relevant context files (design decisions, constraints)
|
|
12427
|
+
|
|
12428
|
+
## Review Process (In Order)
|
|
12429
|
+
|
|
12430
|
+
### 1) Identify Scope
|
|
12431
|
+
|
|
12432
|
+
1. List all files changed.
|
|
12433
|
+
2. For each file, state why it changed (what requirement it serves).
|
|
12434
|
+
3. Flag any changes that do not map to the task/plan.
|
|
12435
|
+
|
|
12436
|
+
**Rule:** If you cannot map a change to a requirement, treat it as suspicious until justified.
|
|
12437
|
+
|
|
12438
|
+
### 2) Plan/Task Adherence (Non-Negotiable)
|
|
12439
|
+
|
|
12440
|
+
Create a simple checklist:
|
|
12441
|
+
- What the task says must happen
|
|
12442
|
+
- Evidence in code/tests that it happens
|
|
12443
|
+
|
|
12444
|
+
Flag as issues:
|
|
12445
|
+
- Missing requirements (implemented behavior does not match intent)
|
|
12446
|
+
- Partial implementation with no follow-up task (TODO-driven shipping)
|
|
12447
|
+
- Behavior changes that are not in the plan/task
|
|
12448
|
+
|
|
12449
|
+
### 3) Correctness Layer
|
|
12450
|
+
|
|
12451
|
+
Review for:
|
|
12452
|
+
- Edge cases and error paths
|
|
12453
|
+
- Incorrect assumptions about inputs/types
|
|
12454
|
+
- Inconsistent behavior across platforms/environments
|
|
12455
|
+
- Broken invariants (e.g., state can become invalid)
|
|
12456
|
+
|
|
12457
|
+
Prefer "fail fast, fail loud": invalid states should become clear errors, not silent fallbacks.
|
|
12458
|
+
|
|
12459
|
+
### 4) Simplicity / YAGNI Layer
|
|
12460
|
+
|
|
12461
|
+
Be ruthless and concrete:
|
|
12462
|
+
- Remove dead branches, unused flags/options, unreachable code
|
|
12463
|
+
- Remove speculative TODOs and "reserved for future" scaffolding
|
|
12464
|
+
- Remove comments that restate the code or narrate obvious steps
|
|
12465
|
+
- Inline one-off abstractions (helpers/classes/interfaces used once)
|
|
12466
|
+
- Replace cleverness with obvious code
|
|
12467
|
+
- Reduce nesting with guard clauses / early returns
|
|
12468
|
+
|
|
12469
|
+
Prefer clarity over brevity:
|
|
12470
|
+
- Avoid nested ternary operators; use \`if/else\` or \`switch\` when branches matter
|
|
12471
|
+
- Avoid dense one-liners that hide intent or make debugging harder
|
|
12472
|
+
|
|
12473
|
+
### 4b) De-Slop Pass (AI Artifacts / Style Drift)
|
|
12474
|
+
|
|
12475
|
+
Scan the diff (not just the final code) for AI-generated slop introduced in this branch:
|
|
12476
|
+
- Extra comments that a human would not add, or that do not match the file's tone
|
|
12477
|
+
- Defensive checks or try/catch blocks that are abnormal for that area of the codebase
|
|
12478
|
+
- Especially swallowed errors ("ignore and continue") and silent fallbacks
|
|
12479
|
+
- Especially redundant validation in trusted internal codepaths
|
|
12480
|
+
- TypeScript escape hatches used to dodge type errors (\`as any\`, \`as unknown as X\`) without necessity
|
|
12481
|
+
- Style drift: naming, error handling patterns, logging style, and structure inconsistent with nearby code
|
|
12482
|
+
|
|
12483
|
+
Default stance:
|
|
12484
|
+
- Prefer deletion over justification.
|
|
12485
|
+
- If validation is needed, do it at boundaries; keep internals trusting parsed inputs.
|
|
12486
|
+
- If a cast is truly unavoidable, localize it and keep the justification to a single short note.
|
|
12487
|
+
|
|
12488
|
+
When recommending simplifications, do not accidentally change behavior. If the current behavior is unclear, request clarification or ask for a test that pins it down.
|
|
12489
|
+
|
|
12490
|
+
**Default stance:** Do not add extensibility points without an explicit current requirement.
|
|
12491
|
+
|
|
12492
|
+
### 5) Risk Layer (Security / Performance / Maintainability)
|
|
12493
|
+
|
|
12494
|
+
Only report what you are confident about.
|
|
12495
|
+
|
|
12496
|
+
Security checks (examples):
|
|
12497
|
+
- No secrets in code/logs
|
|
12498
|
+
- No injection vectors (shell/SQL/HTML) introduced
|
|
12499
|
+
- Authz/authn checks preserved
|
|
12500
|
+
- Sensitive data not leaked
|
|
12501
|
+
|
|
12502
|
+
Performance checks (examples):
|
|
12503
|
+
- Avoid unnecessary repeated work (N+1 queries, repeated parsing, repeated filesystem hits)
|
|
12504
|
+
- Avoid obvious hot-path allocations or large sync operations
|
|
12505
|
+
|
|
12506
|
+
Maintainability checks:
|
|
12507
|
+
- Clear naming and intent
|
|
12508
|
+
- Consistent error handling
|
|
12509
|
+
- API boundaries not blurred
|
|
12510
|
+
- Consistent with local file patterns (imports, export style, function style)
|
|
12511
|
+
|
|
12512
|
+
### 6) Make One Primary Recommendation
|
|
12513
|
+
|
|
12514
|
+
Provide one clear path to reach approval.
|
|
12515
|
+
Mention alternatives only when they have materially different trade-offs.
|
|
12516
|
+
|
|
12517
|
+
### 7) Signal the Investment
|
|
12518
|
+
|
|
12519
|
+
Tag the required follow-up effort using:
|
|
12520
|
+
- Quick (<1h)
|
|
12521
|
+
- Short (1-4h)
|
|
12522
|
+
- Medium (1-2d)
|
|
12523
|
+
- Large (3d+)
|
|
12524
|
+
|
|
12525
|
+
## Confidence Filter
|
|
12526
|
+
|
|
12527
|
+
Only report findings you believe are >=80% likely to be correct.
|
|
12528
|
+
If you are unsure, explicitly label it as "Uncertain" and explain what evidence would confirm it.
|
|
12529
|
+
|
|
12530
|
+
## Output Format (Use This Exactly)
|
|
12531
|
+
|
|
12532
|
+
---
|
|
12533
|
+
|
|
12534
|
+
**Files Reviewed:** [list]
|
|
12535
|
+
|
|
12536
|
+
**Plan/Task Reference:** [task name + link/path to plan section if known]
|
|
12537
|
+
|
|
12538
|
+
**Overall Assessment:** [APPROVE | REQUEST_CHANGES | NEEDS_DISCUSSION]
|
|
12539
|
+
|
|
12540
|
+
**Bottom Line:** 2-3 sentences describing whether it matches the task/plan and what must change.
|
|
12541
|
+
|
|
12542
|
+
### Critical Issues
|
|
12543
|
+
- None | [file:line] - [issue] (why it blocks approval) + (recommended fix)
|
|
12544
|
+
|
|
12545
|
+
### Major Issues
|
|
12546
|
+
- None | [file:line] - [issue] + (recommended fix)
|
|
12547
|
+
|
|
12548
|
+
### Minor Issues
|
|
12549
|
+
- None | [file:line] - [issue] + (suggested fix)
|
|
12550
|
+
|
|
12551
|
+
### YAGNI / Dead Code
|
|
12552
|
+
- None | [file:line] - [what to remove/simplify] + (why it is unnecessary)
|
|
12553
|
+
|
|
12554
|
+
### Positive Observations
|
|
12555
|
+
- [at least one concrete good thing]
|
|
12556
|
+
|
|
12557
|
+
### Action Plan
|
|
12558
|
+
1. [highest priority change]
|
|
12559
|
+
2. [next]
|
|
12560
|
+
3. [next]
|
|
12561
|
+
|
|
12562
|
+
### Effort Estimate
|
|
12563
|
+
[Quick | Short | Medium | Large]
|
|
12564
|
+
|
|
12565
|
+
---
|
|
12566
|
+
|
|
12567
|
+
## Common Review Smells (Fast Scan)
|
|
12568
|
+
|
|
12569
|
+
Task/plan adherence:
|
|
12570
|
+
- Adds features not mentioned in the plan/task
|
|
12571
|
+
- Leaves TODOs as the mechanism for correctness
|
|
12572
|
+
- Introduces new configuration modes/flags "for future"
|
|
12573
|
+
|
|
12574
|
+
YAGNI / dead code:
|
|
12575
|
+
- Options/config that are parsed but not used
|
|
12576
|
+
- Branches that do the same thing on both sides
|
|
12577
|
+
- Comments like "reserved for future" or "we might need this"
|
|
12578
|
+
|
|
12579
|
+
AI slop / inconsistency:
|
|
12580
|
+
- Commentary that restates code, narrates obvious steps, or adds process noise
|
|
12581
|
+
- try/catch that swallows errors or returns defaults without a requirement
|
|
12582
|
+
- \`as any\` used to silence type errors instead of fixing types
|
|
12583
|
+
- New helpers/abstractions with a single call site
|
|
12584
|
+
|
|
12585
|
+
Correctness:
|
|
12586
|
+
- Silent fallbacks to defaults on error when the task expects a hard failure
|
|
12587
|
+
- Unhandled error paths, missing cleanup, missing returns
|
|
12588
|
+
|
|
12589
|
+
Maintainability:
|
|
12590
|
+
- Abstractions used once
|
|
12591
|
+
- Unclear naming, "utility" grab-bags
|
|
12592
|
+
|
|
12593
|
+
## When to Escalate
|
|
12594
|
+
|
|
12595
|
+
Use NEEDS_DISCUSSION (instead of REQUEST_CHANGES) when:
|
|
12596
|
+
- The plan/task is ambiguous and multiple implementations could be correct
|
|
12597
|
+
- The change implies a product/architecture decision not documented
|
|
12598
|
+
- Fixing issues requires changing scope, dependencies, or public API`
|
|
12391
12599
|
},
|
|
12392
12600
|
{
|
|
12393
12601
|
name: "dispatching-parallel-agents",
|
|
@@ -13933,6 +14141,107 @@ function getFilteredSkills(disabledSkills = [], agentSkills) {
|
|
|
13933
14141
|
return filtered;
|
|
13934
14142
|
}
|
|
13935
14143
|
|
|
14144
|
+
// src/skills/file-loader.ts
|
|
14145
|
+
import * as fs from "node:fs/promises";
|
|
14146
|
+
import * as path from "node:path";
|
|
14147
|
+
function validateSkillId(skillId) {
|
|
14148
|
+
if (!skillId || skillId.trim() === "") {
|
|
14149
|
+
return "Skill ID cannot be empty";
|
|
14150
|
+
}
|
|
14151
|
+
if (skillId.includes("/")) {
|
|
14152
|
+
return `Invalid skill ID "${skillId}": contains path traversal character "/"`;
|
|
14153
|
+
}
|
|
14154
|
+
if (skillId.includes("\\")) {
|
|
14155
|
+
return `Invalid skill ID "${skillId}": contains path traversal character "\\"`;
|
|
14156
|
+
}
|
|
14157
|
+
if (skillId.includes("..")) {
|
|
14158
|
+
return `Invalid skill ID "${skillId}": contains path traversal sequence ".."`;
|
|
14159
|
+
}
|
|
14160
|
+
if (skillId === "." || skillId.includes(".")) {
|
|
14161
|
+
return `Invalid skill ID "${skillId}": contains path traversal character "."`;
|
|
14162
|
+
}
|
|
14163
|
+
return;
|
|
14164
|
+
}
|
|
14165
|
+
function stripQuotes(value) {
|
|
14166
|
+
const trimmed = value.trim();
|
|
14167
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
14168
|
+
return trimmed.slice(1, -1);
|
|
14169
|
+
}
|
|
14170
|
+
return trimmed;
|
|
14171
|
+
}
|
|
14172
|
+
function parseFrontmatter(content) {
|
|
14173
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
14174
|
+
if (!match)
|
|
14175
|
+
return null;
|
|
14176
|
+
const frontmatter = match[1];
|
|
14177
|
+
const body = match[2];
|
|
14178
|
+
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
14179
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
14180
|
+
if (!nameMatch || !descMatch)
|
|
14181
|
+
return null;
|
|
14182
|
+
return {
|
|
14183
|
+
name: stripQuotes(nameMatch[1]),
|
|
14184
|
+
description: stripQuotes(descMatch[1]),
|
|
14185
|
+
body
|
|
14186
|
+
};
|
|
14187
|
+
}
|
|
14188
|
+
function getSearchPaths(skillId, projectRoot, homeDir) {
|
|
14189
|
+
return [
|
|
14190
|
+
path.join(projectRoot, ".opencode", "skills", skillId, "SKILL.md"),
|
|
14191
|
+
path.join(homeDir, ".config", "opencode", "skills", skillId, "SKILL.md"),
|
|
14192
|
+
path.join(projectRoot, ".claude", "skills", skillId, "SKILL.md"),
|
|
14193
|
+
path.join(homeDir, ".claude", "skills", skillId, "SKILL.md")
|
|
14194
|
+
];
|
|
14195
|
+
}
|
|
14196
|
+
async function tryReadFile(filePath) {
|
|
14197
|
+
try {
|
|
14198
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
14199
|
+
return content;
|
|
14200
|
+
} catch {
|
|
14201
|
+
return null;
|
|
14202
|
+
}
|
|
14203
|
+
}
|
|
14204
|
+
async function loadFileSkill(skillId, projectRoot, homeDir) {
|
|
14205
|
+
const validationError = validateSkillId(skillId);
|
|
14206
|
+
if (validationError) {
|
|
14207
|
+
return { found: false, error: validationError };
|
|
14208
|
+
}
|
|
14209
|
+
const searchPaths = getSearchPaths(skillId, projectRoot, homeDir);
|
|
14210
|
+
for (const skillPath of searchPaths) {
|
|
14211
|
+
const content = await tryReadFile(skillPath);
|
|
14212
|
+
if (content === null) {
|
|
14213
|
+
continue;
|
|
14214
|
+
}
|
|
14215
|
+
const parsed = parseFrontmatter(content);
|
|
14216
|
+
if (!parsed) {
|
|
14217
|
+
return {
|
|
14218
|
+
found: false,
|
|
14219
|
+
error: `Invalid frontmatter in skill file: ${skillPath}. Expected YAML frontmatter with "name" and "description" fields.`
|
|
14220
|
+
};
|
|
14221
|
+
}
|
|
14222
|
+
if (parsed.name !== skillId) {
|
|
14223
|
+
return {
|
|
14224
|
+
found: false,
|
|
14225
|
+
error: `Skill name mismatch in ${skillPath}: frontmatter name "${parsed.name}" does not match skill ID "${skillId}"`
|
|
14226
|
+
};
|
|
14227
|
+
}
|
|
14228
|
+
const skill = {
|
|
14229
|
+
name: parsed.name,
|
|
14230
|
+
description: parsed.description,
|
|
14231
|
+
template: parsed.body
|
|
14232
|
+
};
|
|
14233
|
+
return {
|
|
14234
|
+
found: true,
|
|
14235
|
+
skill,
|
|
14236
|
+
source: skillPath
|
|
14237
|
+
};
|
|
14238
|
+
}
|
|
14239
|
+
return {
|
|
14240
|
+
found: false,
|
|
14241
|
+
error: `Skill "${skillId}" not found in any of the search paths`
|
|
14242
|
+
};
|
|
14243
|
+
}
|
|
14244
|
+
|
|
13936
14245
|
// src/agents/hive.ts
|
|
13937
14246
|
var QUEEN_BEE_PROMPT = `# Hive (Hybrid)
|
|
13938
14247
|
|
|
@@ -14535,6 +14844,11 @@ Named after Momus - finds fault in everything. Reviews DOCUMENTATION, not DESIGN
|
|
|
14535
14844
|
|
|
14536
14845
|
Review plan WITHIN the stated approach. Question DOCUMENTATION gaps, NOT design decisions.
|
|
14537
14846
|
|
|
14847
|
+
If you are asked to review IMPLEMENTATION (code changes, diffs, PRs) instead of a plan:
|
|
14848
|
+
1. Load \`hive_skill("code-reviewer")\`
|
|
14849
|
+
2. Apply it and return its output format
|
|
14850
|
+
3. Still do NOT edit code (review only)
|
|
14851
|
+
|
|
14538
14852
|
Self-check before every critique:
|
|
14539
14853
|
> "Am I questioning APPROACH or DOCUMENTATION?"
|
|
14540
14854
|
> APPROACH → Stay silent
|
|
@@ -14612,7 +14926,7 @@ Before verdict, mentally execute 2-3 tasks:
|
|
|
14612
14926
|
- Reject based on design decisions
|
|
14613
14927
|
- Suggest alternative architectures
|
|
14614
14928
|
- Block on style preferences
|
|
14615
|
-
- Review implementation (plans only)
|
|
14929
|
+
- Review implementation unless explicitly asked (default is plans only)
|
|
14616
14930
|
|
|
14617
14931
|
**Always:**
|
|
14618
14932
|
- Self-check: approach vs documentation
|
|
@@ -14663,10 +14977,10 @@ var createBuiltinMcps = (disabledMcps = []) => {
|
|
|
14663
14977
|
|
|
14664
14978
|
// ../hive-core/dist/index.js
|
|
14665
14979
|
import { createRequire as createRequire2 } from "node:module";
|
|
14666
|
-
import * as path from "path";
|
|
14667
|
-
import * as fs from "fs";
|
|
14668
14980
|
import * as path2 from "path";
|
|
14669
14981
|
import * as fs2 from "fs";
|
|
14982
|
+
import * as path22 from "path";
|
|
14983
|
+
import * as fs22 from "fs";
|
|
14670
14984
|
import * as fs3 from "fs";
|
|
14671
14985
|
import * as fs4 from "fs";
|
|
14672
14986
|
import * as fs5 from "fs";
|
|
@@ -15556,7 +15870,7 @@ var DEFAULT_HIVE_CONFIG = {
|
|
|
15556
15870
|
"hygienic-reviewer": {
|
|
15557
15871
|
model: DEFAULT_AGENT_MODELS["hygienic-reviewer"],
|
|
15558
15872
|
temperature: 0.3,
|
|
15559
|
-
skills: ["systematic-debugging"],
|
|
15873
|
+
skills: ["systematic-debugging", "code-reviewer"],
|
|
15560
15874
|
autoLoadSkills: []
|
|
15561
15875
|
}
|
|
15562
15876
|
}
|
|
@@ -15572,82 +15886,85 @@ var STATUS_FILE = "status.json";
|
|
|
15572
15886
|
var REPORT_FILE = "report.md";
|
|
15573
15887
|
var APPROVED_FILE = "APPROVED";
|
|
15574
15888
|
var JOURNAL_FILE = "journal.md";
|
|
15889
|
+
function normalizePath(filePath) {
|
|
15890
|
+
return filePath.replace(/\\/g, "/");
|
|
15891
|
+
}
|
|
15575
15892
|
function getHivePath(projectRoot) {
|
|
15576
|
-
return
|
|
15893
|
+
return path2.join(projectRoot, HIVE_DIR);
|
|
15577
15894
|
}
|
|
15578
15895
|
function getJournalPath(projectRoot) {
|
|
15579
|
-
return
|
|
15896
|
+
return path2.join(getHivePath(projectRoot), JOURNAL_FILE);
|
|
15580
15897
|
}
|
|
15581
15898
|
function getFeaturesPath(projectRoot) {
|
|
15582
|
-
return
|
|
15899
|
+
return path2.join(getHivePath(projectRoot), FEATURES_DIR);
|
|
15583
15900
|
}
|
|
15584
15901
|
function getFeaturePath(projectRoot, featureName) {
|
|
15585
|
-
return
|
|
15902
|
+
return path2.join(getFeaturesPath(projectRoot), featureName);
|
|
15586
15903
|
}
|
|
15587
15904
|
function getPlanPath(projectRoot, featureName) {
|
|
15588
|
-
return
|
|
15905
|
+
return path2.join(getFeaturePath(projectRoot, featureName), PLAN_FILE);
|
|
15589
15906
|
}
|
|
15590
15907
|
function getCommentsPath(projectRoot, featureName) {
|
|
15591
|
-
return
|
|
15908
|
+
return path2.join(getFeaturePath(projectRoot, featureName), COMMENTS_FILE);
|
|
15592
15909
|
}
|
|
15593
15910
|
function getFeatureJsonPath(projectRoot, featureName) {
|
|
15594
|
-
return
|
|
15911
|
+
return path2.join(getFeaturePath(projectRoot, featureName), FEATURE_FILE);
|
|
15595
15912
|
}
|
|
15596
15913
|
function getContextPath(projectRoot, featureName) {
|
|
15597
|
-
return
|
|
15914
|
+
return path2.join(getFeaturePath(projectRoot, featureName), CONTEXT_DIR);
|
|
15598
15915
|
}
|
|
15599
15916
|
function getTasksPath(projectRoot, featureName) {
|
|
15600
|
-
return
|
|
15917
|
+
return path2.join(getFeaturePath(projectRoot, featureName), TASKS_DIR);
|
|
15601
15918
|
}
|
|
15602
15919
|
function getTaskPath(projectRoot, featureName, taskFolder) {
|
|
15603
|
-
return
|
|
15920
|
+
return path2.join(getTasksPath(projectRoot, featureName), taskFolder);
|
|
15604
15921
|
}
|
|
15605
15922
|
function getTaskStatusPath(projectRoot, featureName, taskFolder) {
|
|
15606
|
-
return
|
|
15923
|
+
return path2.join(getTaskPath(projectRoot, featureName, taskFolder), STATUS_FILE);
|
|
15607
15924
|
}
|
|
15608
15925
|
function getTaskReportPath(projectRoot, featureName, taskFolder) {
|
|
15609
|
-
return
|
|
15926
|
+
return path2.join(getTaskPath(projectRoot, featureName, taskFolder), REPORT_FILE);
|
|
15610
15927
|
}
|
|
15611
15928
|
function getTaskSpecPath(projectRoot, featureName, taskFolder) {
|
|
15612
|
-
return
|
|
15929
|
+
return path2.join(getTaskPath(projectRoot, featureName, taskFolder), "spec.md");
|
|
15613
15930
|
}
|
|
15614
15931
|
function getApprovedPath(projectRoot, featureName) {
|
|
15615
|
-
return
|
|
15932
|
+
return path2.join(getFeaturePath(projectRoot, featureName), APPROVED_FILE);
|
|
15616
15933
|
}
|
|
15617
15934
|
var SUBTASKS_DIR = "subtasks";
|
|
15618
15935
|
var SPEC_FILE = "spec.md";
|
|
15619
15936
|
function getSubtasksPath(projectRoot, featureName, taskFolder) {
|
|
15620
|
-
return
|
|
15937
|
+
return path2.join(getTaskPath(projectRoot, featureName, taskFolder), SUBTASKS_DIR);
|
|
15621
15938
|
}
|
|
15622
15939
|
function getSubtaskPath(projectRoot, featureName, taskFolder, subtaskFolder) {
|
|
15623
|
-
return
|
|
15940
|
+
return path2.join(getSubtasksPath(projectRoot, featureName, taskFolder), subtaskFolder);
|
|
15624
15941
|
}
|
|
15625
15942
|
function getSubtaskStatusPath(projectRoot, featureName, taskFolder, subtaskFolder) {
|
|
15626
|
-
return
|
|
15943
|
+
return path2.join(getSubtaskPath(projectRoot, featureName, taskFolder, subtaskFolder), STATUS_FILE);
|
|
15627
15944
|
}
|
|
15628
15945
|
function getSubtaskSpecPath(projectRoot, featureName, taskFolder, subtaskFolder) {
|
|
15629
|
-
return
|
|
15946
|
+
return path2.join(getSubtaskPath(projectRoot, featureName, taskFolder, subtaskFolder), SPEC_FILE);
|
|
15630
15947
|
}
|
|
15631
15948
|
function getSubtaskReportPath(projectRoot, featureName, taskFolder, subtaskFolder) {
|
|
15632
|
-
return
|
|
15949
|
+
return path2.join(getSubtaskPath(projectRoot, featureName, taskFolder, subtaskFolder), REPORT_FILE);
|
|
15633
15950
|
}
|
|
15634
15951
|
function ensureDir(dirPath) {
|
|
15635
|
-
if (!
|
|
15636
|
-
|
|
15952
|
+
if (!fs2.existsSync(dirPath)) {
|
|
15953
|
+
fs2.mkdirSync(dirPath, { recursive: true });
|
|
15637
15954
|
}
|
|
15638
15955
|
}
|
|
15639
15956
|
function fileExists(filePath) {
|
|
15640
|
-
return
|
|
15957
|
+
return fs2.existsSync(filePath);
|
|
15641
15958
|
}
|
|
15642
15959
|
function readJson(filePath) {
|
|
15643
|
-
if (!
|
|
15960
|
+
if (!fs2.existsSync(filePath))
|
|
15644
15961
|
return null;
|
|
15645
|
-
const content =
|
|
15962
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
15646
15963
|
return JSON.parse(content);
|
|
15647
15964
|
}
|
|
15648
15965
|
function writeJson(filePath, data) {
|
|
15649
|
-
ensureDir(
|
|
15650
|
-
|
|
15966
|
+
ensureDir(path2.dirname(filePath));
|
|
15967
|
+
fs2.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
15651
15968
|
}
|
|
15652
15969
|
var DEFAULT_LOCK_OPTIONS = {
|
|
15653
15970
|
timeout: 5000,
|
|
@@ -15659,7 +15976,7 @@ function getLockPath(filePath) {
|
|
|
15659
15976
|
}
|
|
15660
15977
|
function isLockStale(lockPath, staleTTL) {
|
|
15661
15978
|
try {
|
|
15662
|
-
const stat2 =
|
|
15979
|
+
const stat2 = fs2.statSync(lockPath);
|
|
15663
15980
|
const age = Date.now() - stat2.mtimeMs;
|
|
15664
15981
|
return age > staleTTL;
|
|
15665
15982
|
} catch {
|
|
@@ -15677,12 +15994,12 @@ function acquireLockSync(filePath, options = {}) {
|
|
|
15677
15994
|
});
|
|
15678
15995
|
while (true) {
|
|
15679
15996
|
try {
|
|
15680
|
-
const fd =
|
|
15681
|
-
|
|
15682
|
-
|
|
15997
|
+
const fd = fs2.openSync(lockPath, fs2.constants.O_CREAT | fs2.constants.O_EXCL | fs2.constants.O_WRONLY);
|
|
15998
|
+
fs2.writeSync(fd, lockContent);
|
|
15999
|
+
fs2.closeSync(fd);
|
|
15683
16000
|
return () => {
|
|
15684
16001
|
try {
|
|
15685
|
-
|
|
16002
|
+
fs2.unlinkSync(lockPath);
|
|
15686
16003
|
} catch {}
|
|
15687
16004
|
};
|
|
15688
16005
|
} catch (err) {
|
|
@@ -15692,7 +16009,7 @@ function acquireLockSync(filePath, options = {}) {
|
|
|
15692
16009
|
}
|
|
15693
16010
|
if (isLockStale(lockPath, opts.staleLockTTL)) {
|
|
15694
16011
|
try {
|
|
15695
|
-
|
|
16012
|
+
fs2.unlinkSync(lockPath);
|
|
15696
16013
|
continue;
|
|
15697
16014
|
} catch {}
|
|
15698
16015
|
}
|
|
@@ -15705,14 +16022,14 @@ function acquireLockSync(filePath, options = {}) {
|
|
|
15705
16022
|
}
|
|
15706
16023
|
}
|
|
15707
16024
|
function writeAtomic(filePath, content) {
|
|
15708
|
-
ensureDir(
|
|
16025
|
+
ensureDir(path2.dirname(filePath));
|
|
15709
16026
|
const tempPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
15710
16027
|
try {
|
|
15711
|
-
|
|
15712
|
-
|
|
16028
|
+
fs2.writeFileSync(tempPath, content);
|
|
16029
|
+
fs2.renameSync(tempPath, filePath);
|
|
15713
16030
|
} catch (error45) {
|
|
15714
16031
|
try {
|
|
15715
|
-
|
|
16032
|
+
fs2.unlinkSync(tempPath);
|
|
15716
16033
|
} catch {}
|
|
15717
16034
|
throw error45;
|
|
15718
16035
|
}
|
|
@@ -15755,13 +16072,13 @@ function patchJsonLockedSync(filePath, patch, options = {}) {
|
|
|
15755
16072
|
}
|
|
15756
16073
|
}
|
|
15757
16074
|
function readText(filePath) {
|
|
15758
|
-
if (!
|
|
16075
|
+
if (!fs2.existsSync(filePath))
|
|
15759
16076
|
return null;
|
|
15760
|
-
return
|
|
16077
|
+
return fs2.readFileSync(filePath, "utf-8");
|
|
15761
16078
|
}
|
|
15762
16079
|
function writeText(filePath, content) {
|
|
15763
|
-
ensureDir(
|
|
15764
|
-
|
|
16080
|
+
ensureDir(path2.dirname(filePath));
|
|
16081
|
+
fs2.writeFileSync(filePath, content);
|
|
15765
16082
|
}
|
|
15766
16083
|
function detectContext(cwd) {
|
|
15767
16084
|
const result = {
|
|
@@ -15771,7 +16088,8 @@ function detectContext(cwd) {
|
|
|
15771
16088
|
isWorktree: false,
|
|
15772
16089
|
mainProjectRoot: null
|
|
15773
16090
|
};
|
|
15774
|
-
const
|
|
16091
|
+
const normalizedCwd = normalizePath(cwd);
|
|
16092
|
+
const worktreeMatch = normalizedCwd.match(/(.+)\/\.hive\/\.worktrees\/([^/]+)\/([^/]+)/);
|
|
15775
16093
|
if (worktreeMatch) {
|
|
15776
16094
|
result.mainProjectRoot = worktreeMatch[1];
|
|
15777
16095
|
result.feature = worktreeMatch[2];
|
|
@@ -15780,18 +16098,19 @@ function detectContext(cwd) {
|
|
|
15780
16098
|
result.projectRoot = worktreeMatch[1];
|
|
15781
16099
|
return result;
|
|
15782
16100
|
}
|
|
15783
|
-
const gitPath =
|
|
15784
|
-
if (
|
|
15785
|
-
const stat2 =
|
|
16101
|
+
const gitPath = path22.join(cwd, ".git");
|
|
16102
|
+
if (fs22.existsSync(gitPath)) {
|
|
16103
|
+
const stat2 = fs22.statSync(gitPath);
|
|
15786
16104
|
if (stat2.isFile()) {
|
|
15787
|
-
const gitContent =
|
|
16105
|
+
const gitContent = fs22.readFileSync(gitPath, "utf-8").trim();
|
|
15788
16106
|
const gitdirMatch = gitContent.match(/gitdir:\s*(.+)/);
|
|
15789
16107
|
if (gitdirMatch) {
|
|
15790
16108
|
const gitdir = gitdirMatch[1];
|
|
15791
|
-
const
|
|
16109
|
+
const normalizedGitdir = normalizePath(gitdir);
|
|
16110
|
+
const worktreePathMatch = normalizedGitdir.match(/(.+)\/\.git\/worktrees\/(.+)/);
|
|
15792
16111
|
if (worktreePathMatch) {
|
|
15793
16112
|
const mainRepo = worktreePathMatch[1];
|
|
15794
|
-
const cwdWorktreeMatch =
|
|
16113
|
+
const cwdWorktreeMatch = normalizedCwd.match(/\.hive\/\.worktrees\/([^/]+)\/([^/]+)/);
|
|
15795
16114
|
if (cwdWorktreeMatch) {
|
|
15796
16115
|
result.mainProjectRoot = mainRepo;
|
|
15797
16116
|
result.feature = cwdWorktreeMatch[1];
|
|
@@ -15808,9 +16127,9 @@ function detectContext(cwd) {
|
|
|
15808
16127
|
}
|
|
15809
16128
|
function listFeatures(projectRoot) {
|
|
15810
16129
|
const featuresPath = getFeaturesPath(projectRoot);
|
|
15811
|
-
if (!
|
|
16130
|
+
if (!fs22.existsSync(featuresPath))
|
|
15812
16131
|
return [];
|
|
15813
|
-
return
|
|
16132
|
+
return fs22.readdirSync(featuresPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
15814
16133
|
}
|
|
15815
16134
|
var JOURNAL_TEMPLATE = `# Hive Journal
|
|
15816
16135
|
|
|
@@ -21470,7 +21789,13 @@ function isValidPromptFilePath(filePath, workspaceRoot) {
|
|
|
21470
21789
|
try {
|
|
21471
21790
|
const normalizedFilePath = path5.resolve(filePath);
|
|
21472
21791
|
const normalizedWorkspace = path5.resolve(workspaceRoot);
|
|
21473
|
-
|
|
21792
|
+
let normalizedFilePathForCompare = normalizePath(normalizedFilePath);
|
|
21793
|
+
let normalizedWorkspaceForCompare = normalizePath(normalizedWorkspace);
|
|
21794
|
+
if (process.platform === "win32") {
|
|
21795
|
+
normalizedFilePathForCompare = normalizedFilePathForCompare.toLowerCase();
|
|
21796
|
+
normalizedWorkspaceForCompare = normalizedWorkspaceForCompare.toLowerCase();
|
|
21797
|
+
}
|
|
21798
|
+
if (!normalizedFilePathForCompare.startsWith(normalizedWorkspaceForCompare + "/") && normalizedFilePathForCompare !== normalizedWorkspaceForCompare) {
|
|
21474
21799
|
return false;
|
|
21475
21800
|
}
|
|
21476
21801
|
return true;
|
|
@@ -22891,6 +23216,36 @@ function formatSkillsXml(skills) {
|
|
|
22891
23216
|
${skillsXml}
|
|
22892
23217
|
</available_skills>`;
|
|
22893
23218
|
}
|
|
23219
|
+
async function buildAutoLoadedSkillsContent(agentName, configService, projectRoot) {
|
|
23220
|
+
const agentConfig = configService.getAgentConfig(agentName);
|
|
23221
|
+
const autoLoadSkills = agentConfig.autoLoadSkills ?? [];
|
|
23222
|
+
if (autoLoadSkills.length === 0) {
|
|
23223
|
+
return "";
|
|
23224
|
+
}
|
|
23225
|
+
const homeDir = process.env.HOME || os.homedir();
|
|
23226
|
+
const skillTemplates = [];
|
|
23227
|
+
for (const skillId of autoLoadSkills) {
|
|
23228
|
+
const builtinSkill = BUILTIN_SKILLS.find((entry) => entry.name === skillId);
|
|
23229
|
+
if (builtinSkill) {
|
|
23230
|
+
skillTemplates.push(builtinSkill.template);
|
|
23231
|
+
continue;
|
|
23232
|
+
}
|
|
23233
|
+
const fileResult = await loadFileSkill(skillId, projectRoot, homeDir);
|
|
23234
|
+
if (fileResult.found && fileResult.skill) {
|
|
23235
|
+
skillTemplates.push(fileResult.skill.template);
|
|
23236
|
+
continue;
|
|
23237
|
+
}
|
|
23238
|
+
console.warn(`[hive] Unknown skill id "${skillId}" for agent "${agentName}"`);
|
|
23239
|
+
}
|
|
23240
|
+
if (skillTemplates.length === 0) {
|
|
23241
|
+
return "";
|
|
23242
|
+
}
|
|
23243
|
+
return `
|
|
23244
|
+
|
|
23245
|
+
` + skillTemplates.join(`
|
|
23246
|
+
|
|
23247
|
+
`);
|
|
23248
|
+
}
|
|
22894
23249
|
function createHiveSkillTool(filteredSkills) {
|
|
22895
23250
|
const base = `Load a Hive skill to get detailed instructions for a specific workflow.
|
|
22896
23251
|
|
|
@@ -23072,22 +23427,6 @@ To unblock: Remove .hive/features/${feature}/BLOCKED`;
|
|
|
23072
23427
|
return {
|
|
23073
23428
|
"experimental.chat.system.transform": async (input, output) => {
|
|
23074
23429
|
output.system.push(HIVE_SYSTEM_PROMPT);
|
|
23075
|
-
const agentInput = input;
|
|
23076
|
-
const agentName = agentInput?.agent;
|
|
23077
|
-
if (agentName && isHiveAgent(agentName)) {
|
|
23078
|
-
const agentConfig = configService.getAgentConfig(agentName);
|
|
23079
|
-
const autoLoadSkills = agentConfig.autoLoadSkills ?? [];
|
|
23080
|
-
if (autoLoadSkills.length > 0) {
|
|
23081
|
-
for (const skillId of autoLoadSkills) {
|
|
23082
|
-
const skill = BUILTIN_SKILLS.find((entry) => entry.name === skillId);
|
|
23083
|
-
if (!skill) {
|
|
23084
|
-
console.warn("Unknown skill id", skillId);
|
|
23085
|
-
continue;
|
|
23086
|
-
}
|
|
23087
|
-
output.system.push(skill.template);
|
|
23088
|
-
}
|
|
23089
|
-
}
|
|
23090
|
-
}
|
|
23091
23430
|
const activeFeature = resolveFeature();
|
|
23092
23431
|
if (activeFeature) {
|
|
23093
23432
|
const info = featureService.getInfo(activeFeature);
|
|
@@ -23485,7 +23824,7 @@ ${priorTasksFormatted}
|
|
|
23485
23824
|
});
|
|
23486
23825
|
const hiveDir = path7.join(directory, ".hive");
|
|
23487
23826
|
const workerPromptPath = writeWorkerPromptFile(feature, task, workerPrompt, hiveDir);
|
|
23488
|
-
const relativePromptPath = path7.relative(directory, workerPromptPath);
|
|
23827
|
+
const relativePromptPath = normalizePath(path7.relative(directory, workerPromptPath));
|
|
23489
23828
|
const PREVIEW_MAX_LENGTH = 200;
|
|
23490
23829
|
const workerPromptPreview = workerPrompt.length > PREVIEW_MAX_LENGTH ? workerPrompt.slice(0, PREVIEW_MAX_LENGTH) + "..." : workerPrompt;
|
|
23491
23830
|
const hiveBackgroundInstructions = `## Delegation Required
|
|
@@ -24042,11 +24381,12 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24042
24381
|
config: async (opencodeConfig) => {
|
|
24043
24382
|
configService.init();
|
|
24044
24383
|
const hiveUserConfig = configService.getAgentConfig("hive-master");
|
|
24384
|
+
const hiveAutoLoadedSkills = await buildAutoLoadedSkillsContent("hive-master", configService, directory);
|
|
24045
24385
|
const hiveConfig = {
|
|
24046
24386
|
model: hiveUserConfig.model,
|
|
24047
24387
|
temperature: hiveUserConfig.temperature ?? 0.5,
|
|
24048
24388
|
description: "Hive (Hybrid) - Plans + orchestrates. Detects phase, loads skills on-demand.",
|
|
24049
|
-
prompt: QUEEN_BEE_PROMPT,
|
|
24389
|
+
prompt: QUEEN_BEE_PROMPT + hiveAutoLoadedSkills,
|
|
24050
24390
|
permission: {
|
|
24051
24391
|
question: "allow",
|
|
24052
24392
|
skill: "allow",
|
|
@@ -24058,11 +24398,12 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24058
24398
|
}
|
|
24059
24399
|
};
|
|
24060
24400
|
const architectUserConfig = configService.getAgentConfig("architect-planner");
|
|
24401
|
+
const architectAutoLoadedSkills = await buildAutoLoadedSkillsContent("architect-planner", configService, directory);
|
|
24061
24402
|
const architectConfig = {
|
|
24062
24403
|
model: architectUserConfig.model,
|
|
24063
24404
|
temperature: architectUserConfig.temperature ?? 0.7,
|
|
24064
24405
|
description: "Architect (Planner) - Plans features, interviews, writes plans. NEVER executes.",
|
|
24065
|
-
prompt: ARCHITECT_BEE_PROMPT,
|
|
24406
|
+
prompt: ARCHITECT_BEE_PROMPT + architectAutoLoadedSkills,
|
|
24066
24407
|
permission: {
|
|
24067
24408
|
edit: "deny",
|
|
24068
24409
|
task: "deny",
|
|
@@ -24077,11 +24418,12 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24077
24418
|
}
|
|
24078
24419
|
};
|
|
24079
24420
|
const swarmUserConfig = configService.getAgentConfig("swarm-orchestrator");
|
|
24421
|
+
const swarmAutoLoadedSkills = await buildAutoLoadedSkillsContent("swarm-orchestrator", configService, directory);
|
|
24080
24422
|
const swarmConfig = {
|
|
24081
24423
|
model: swarmUserConfig.model,
|
|
24082
24424
|
temperature: swarmUserConfig.temperature ?? 0.5,
|
|
24083
24425
|
description: "Swarm (Orchestrator) - Orchestrates execution. Delegates, spawns workers, verifies, merges.",
|
|
24084
|
-
prompt: SWARM_BEE_PROMPT,
|
|
24426
|
+
prompt: SWARM_BEE_PROMPT + swarmAutoLoadedSkills,
|
|
24085
24427
|
permission: {
|
|
24086
24428
|
question: "allow",
|
|
24087
24429
|
skill: "allow",
|
|
@@ -24093,12 +24435,13 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24093
24435
|
}
|
|
24094
24436
|
};
|
|
24095
24437
|
const scoutUserConfig = configService.getAgentConfig("scout-researcher");
|
|
24438
|
+
const scoutAutoLoadedSkills = await buildAutoLoadedSkillsContent("scout-researcher", configService, directory);
|
|
24096
24439
|
const scoutConfig = {
|
|
24097
24440
|
model: scoutUserConfig.model,
|
|
24098
24441
|
temperature: scoutUserConfig.temperature ?? 0.5,
|
|
24099
24442
|
mode: "subagent",
|
|
24100
24443
|
description: "Scout (Explorer/Researcher/Retrieval) - Researches codebase + external docs/data.",
|
|
24101
|
-
prompt: SCOUT_BEE_PROMPT,
|
|
24444
|
+
prompt: SCOUT_BEE_PROMPT + scoutAutoLoadedSkills,
|
|
24102
24445
|
permission: {
|
|
24103
24446
|
edit: "deny",
|
|
24104
24447
|
skill: "allow",
|
|
@@ -24106,23 +24449,25 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24106
24449
|
}
|
|
24107
24450
|
};
|
|
24108
24451
|
const foragerUserConfig = configService.getAgentConfig("forager-worker");
|
|
24452
|
+
const foragerAutoLoadedSkills = await buildAutoLoadedSkillsContent("forager-worker", configService, directory);
|
|
24109
24453
|
const foragerConfig = {
|
|
24110
24454
|
model: foragerUserConfig.model,
|
|
24111
24455
|
temperature: foragerUserConfig.temperature ?? 0.3,
|
|
24112
24456
|
mode: "subagent",
|
|
24113
24457
|
description: "Forager (Worker/Coder) - Executes tasks directly in isolated worktrees. Never delegates.",
|
|
24114
|
-
prompt: FORAGER_BEE_PROMPT,
|
|
24458
|
+
prompt: FORAGER_BEE_PROMPT + foragerAutoLoadedSkills,
|
|
24115
24459
|
permission: {
|
|
24116
24460
|
skill: "allow"
|
|
24117
24461
|
}
|
|
24118
24462
|
};
|
|
24119
24463
|
const hygienicUserConfig = configService.getAgentConfig("hygienic-reviewer");
|
|
24464
|
+
const hygienicAutoLoadedSkills = await buildAutoLoadedSkillsContent("hygienic-reviewer", configService, directory);
|
|
24120
24465
|
const hygienicConfig = {
|
|
24121
24466
|
model: hygienicUserConfig.model,
|
|
24122
24467
|
temperature: hygienicUserConfig.temperature ?? 0.3,
|
|
24123
24468
|
mode: "subagent",
|
|
24124
24469
|
description: "Hygienic (Consultant/Reviewer/Debugger) - Reviews plan documentation quality. OKAY/REJECT verdict.",
|
|
24125
|
-
prompt: HYGIENIC_BEE_PROMPT,
|
|
24470
|
+
prompt: HYGIENIC_BEE_PROMPT + hygienicAutoLoadedSkills,
|
|
24126
24471
|
permission: {
|
|
24127
24472
|
edit: "deny",
|
|
24128
24473
|
skill: "allow"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based Skill Loader
|
|
3
|
+
*
|
|
4
|
+
* Resolves and loads skill files from OpenCode and Claude-compatible paths.
|
|
5
|
+
* Implements strict skill ID validation and deterministic search order.
|
|
6
|
+
*/
|
|
7
|
+
import type { SkillLoadResult } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Load a skill from file-based locations.
|
|
10
|
+
*
|
|
11
|
+
* Searches for skill files in the following order:
|
|
12
|
+
* 1. Project OpenCode: `<projectRoot>/.opencode/skills/<skillId>/SKILL.md`
|
|
13
|
+
* 2. Global OpenCode: `~/.config/opencode/skills/<skillId>/SKILL.md`
|
|
14
|
+
* 3. Project Claude-compatible: `<projectRoot>/.claude/skills/<skillId>/SKILL.md`
|
|
15
|
+
* 4. Global Claude-compatible: `~/.claude/skills/<skillId>/SKILL.md`
|
|
16
|
+
*
|
|
17
|
+
* @param skillId - The skill ID to load
|
|
18
|
+
* @param projectRoot - The project root directory
|
|
19
|
+
* @param homeDir - The user's home directory
|
|
20
|
+
* @returns The skill load result
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadFileSkill(skillId: string, projectRoot: string, homeDir: string): Promise<SkillLoadResult>;
|
package/dist/skills/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { SkillDefinition } from './types.js';
|
|
|
7
7
|
/**
|
|
8
8
|
* List of builtin skill names.
|
|
9
9
|
*/
|
|
10
|
-
export declare const BUILTIN_SKILL_NAMES: readonly ["brainstorming", "dispatching-parallel-agents", "executing-plans", "onboarding", "parallel-exploration", "systematic-debugging", "test-driven-development", "verification-before-completion", "writing-plans"];
|
|
10
|
+
export declare const BUILTIN_SKILL_NAMES: readonly ["brainstorming", "code-reviewer", "dispatching-parallel-agents", "executing-plans", "onboarding", "parallel-exploration", "systematic-debugging", "test-driven-development", "verification-before-completion", "writing-plans"];
|
|
11
11
|
/**
|
|
12
12
|
* All builtin skill definitions.
|
|
13
13
|
*/
|
package/package.json
CHANGED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-reviewer
|
|
3
|
+
description: Use when reviewing implementation changes against an approved plan or task (especially before merging or between Hive tasks) to catch missing requirements, YAGNI, dead code, and risky patterns
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Reviewer
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
This skill teaches a reviewer to evaluate implementation changes for:
|
|
11
|
+
- Adherence to the approved plan/task (did we build what we said?)
|
|
12
|
+
- Correctness (does it work, including edge cases?)
|
|
13
|
+
- Simplicity (YAGNI, dead code, over-abstraction)
|
|
14
|
+
- Risk (security, performance, maintainability)
|
|
15
|
+
|
|
16
|
+
**Core principle:** The best change is the smallest correct change that satisfies the plan.
|
|
17
|
+
|
|
18
|
+
## Iron Laws
|
|
19
|
+
|
|
20
|
+
- Review against the task/plan first. Code quality comes second.
|
|
21
|
+
- Bias toward deletion and simplification. Every extra line is a liability.
|
|
22
|
+
- Prefer changes that leverage existing patterns and dependencies.
|
|
23
|
+
- Be specific: cite file paths and (when available) line numbers.
|
|
24
|
+
- Do not invent requirements. If the plan/task is ambiguous, mark it and request clarification.
|
|
25
|
+
|
|
26
|
+
## What Inputs You Need
|
|
27
|
+
|
|
28
|
+
Minimum:
|
|
29
|
+
- The task intent (1-3 sentences)
|
|
30
|
+
- The plan/task requirements (or a link/path to plan section)
|
|
31
|
+
- The code changes (diff or list of changed files)
|
|
32
|
+
|
|
33
|
+
If available (recommended):
|
|
34
|
+
- Acceptance criteria / verification steps from the plan
|
|
35
|
+
- Test output or proof the change was verified
|
|
36
|
+
- Any relevant context files (design decisions, constraints)
|
|
37
|
+
|
|
38
|
+
## Review Process (In Order)
|
|
39
|
+
|
|
40
|
+
### 1) Identify Scope
|
|
41
|
+
|
|
42
|
+
1. List all files changed.
|
|
43
|
+
2. For each file, state why it changed (what requirement it serves).
|
|
44
|
+
3. Flag any changes that do not map to the task/plan.
|
|
45
|
+
|
|
46
|
+
**Rule:** If you cannot map a change to a requirement, treat it as suspicious until justified.
|
|
47
|
+
|
|
48
|
+
### 2) Plan/Task Adherence (Non-Negotiable)
|
|
49
|
+
|
|
50
|
+
Create a simple checklist:
|
|
51
|
+
- What the task says must happen
|
|
52
|
+
- Evidence in code/tests that it happens
|
|
53
|
+
|
|
54
|
+
Flag as issues:
|
|
55
|
+
- Missing requirements (implemented behavior does not match intent)
|
|
56
|
+
- Partial implementation with no follow-up task (TODO-driven shipping)
|
|
57
|
+
- Behavior changes that are not in the plan/task
|
|
58
|
+
|
|
59
|
+
### 3) Correctness Layer
|
|
60
|
+
|
|
61
|
+
Review for:
|
|
62
|
+
- Edge cases and error paths
|
|
63
|
+
- Incorrect assumptions about inputs/types
|
|
64
|
+
- Inconsistent behavior across platforms/environments
|
|
65
|
+
- Broken invariants (e.g., state can become invalid)
|
|
66
|
+
|
|
67
|
+
Prefer "fail fast, fail loud": invalid states should become clear errors, not silent fallbacks.
|
|
68
|
+
|
|
69
|
+
### 4) Simplicity / YAGNI Layer
|
|
70
|
+
|
|
71
|
+
Be ruthless and concrete:
|
|
72
|
+
- Remove dead branches, unused flags/options, unreachable code
|
|
73
|
+
- Remove speculative TODOs and "reserved for future" scaffolding
|
|
74
|
+
- Remove comments that restate the code or narrate obvious steps
|
|
75
|
+
- Inline one-off abstractions (helpers/classes/interfaces used once)
|
|
76
|
+
- Replace cleverness with obvious code
|
|
77
|
+
- Reduce nesting with guard clauses / early returns
|
|
78
|
+
|
|
79
|
+
Prefer clarity over brevity:
|
|
80
|
+
- Avoid nested ternary operators; use `if/else` or `switch` when branches matter
|
|
81
|
+
- Avoid dense one-liners that hide intent or make debugging harder
|
|
82
|
+
|
|
83
|
+
### 4b) De-Slop Pass (AI Artifacts / Style Drift)
|
|
84
|
+
|
|
85
|
+
Scan the diff (not just the final code) for AI-generated slop introduced in this branch:
|
|
86
|
+
- Extra comments that a human would not add, or that do not match the file's tone
|
|
87
|
+
- Defensive checks or try/catch blocks that are abnormal for that area of the codebase
|
|
88
|
+
- Especially swallowed errors ("ignore and continue") and silent fallbacks
|
|
89
|
+
- Especially redundant validation in trusted internal codepaths
|
|
90
|
+
- TypeScript escape hatches used to dodge type errors (`as any`, `as unknown as X`) without necessity
|
|
91
|
+
- Style drift: naming, error handling patterns, logging style, and structure inconsistent with nearby code
|
|
92
|
+
|
|
93
|
+
Default stance:
|
|
94
|
+
- Prefer deletion over justification.
|
|
95
|
+
- If validation is needed, do it at boundaries; keep internals trusting parsed inputs.
|
|
96
|
+
- If a cast is truly unavoidable, localize it and keep the justification to a single short note.
|
|
97
|
+
|
|
98
|
+
When recommending simplifications, do not accidentally change behavior. If the current behavior is unclear, request clarification or ask for a test that pins it down.
|
|
99
|
+
|
|
100
|
+
**Default stance:** Do not add extensibility points without an explicit current requirement.
|
|
101
|
+
|
|
102
|
+
### 5) Risk Layer (Security / Performance / Maintainability)
|
|
103
|
+
|
|
104
|
+
Only report what you are confident about.
|
|
105
|
+
|
|
106
|
+
Security checks (examples):
|
|
107
|
+
- No secrets in code/logs
|
|
108
|
+
- No injection vectors (shell/SQL/HTML) introduced
|
|
109
|
+
- Authz/authn checks preserved
|
|
110
|
+
- Sensitive data not leaked
|
|
111
|
+
|
|
112
|
+
Performance checks (examples):
|
|
113
|
+
- Avoid unnecessary repeated work (N+1 queries, repeated parsing, repeated filesystem hits)
|
|
114
|
+
- Avoid obvious hot-path allocations or large sync operations
|
|
115
|
+
|
|
116
|
+
Maintainability checks:
|
|
117
|
+
- Clear naming and intent
|
|
118
|
+
- Consistent error handling
|
|
119
|
+
- API boundaries not blurred
|
|
120
|
+
- Consistent with local file patterns (imports, export style, function style)
|
|
121
|
+
|
|
122
|
+
### 6) Make One Primary Recommendation
|
|
123
|
+
|
|
124
|
+
Provide one clear path to reach approval.
|
|
125
|
+
Mention alternatives only when they have materially different trade-offs.
|
|
126
|
+
|
|
127
|
+
### 7) Signal the Investment
|
|
128
|
+
|
|
129
|
+
Tag the required follow-up effort using:
|
|
130
|
+
- Quick (<1h)
|
|
131
|
+
- Short (1-4h)
|
|
132
|
+
- Medium (1-2d)
|
|
133
|
+
- Large (3d+)
|
|
134
|
+
|
|
135
|
+
## Confidence Filter
|
|
136
|
+
|
|
137
|
+
Only report findings you believe are >=80% likely to be correct.
|
|
138
|
+
If you are unsure, explicitly label it as "Uncertain" and explain what evidence would confirm it.
|
|
139
|
+
|
|
140
|
+
## Output Format (Use This Exactly)
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
**Files Reviewed:** [list]
|
|
145
|
+
|
|
146
|
+
**Plan/Task Reference:** [task name + link/path to plan section if known]
|
|
147
|
+
|
|
148
|
+
**Overall Assessment:** [APPROVE | REQUEST_CHANGES | NEEDS_DISCUSSION]
|
|
149
|
+
|
|
150
|
+
**Bottom Line:** 2-3 sentences describing whether it matches the task/plan and what must change.
|
|
151
|
+
|
|
152
|
+
### Critical Issues
|
|
153
|
+
- None | [file:line] - [issue] (why it blocks approval) + (recommended fix)
|
|
154
|
+
|
|
155
|
+
### Major Issues
|
|
156
|
+
- None | [file:line] - [issue] + (recommended fix)
|
|
157
|
+
|
|
158
|
+
### Minor Issues
|
|
159
|
+
- None | [file:line] - [issue] + (suggested fix)
|
|
160
|
+
|
|
161
|
+
### YAGNI / Dead Code
|
|
162
|
+
- None | [file:line] - [what to remove/simplify] + (why it is unnecessary)
|
|
163
|
+
|
|
164
|
+
### Positive Observations
|
|
165
|
+
- [at least one concrete good thing]
|
|
166
|
+
|
|
167
|
+
### Action Plan
|
|
168
|
+
1. [highest priority change]
|
|
169
|
+
2. [next]
|
|
170
|
+
3. [next]
|
|
171
|
+
|
|
172
|
+
### Effort Estimate
|
|
173
|
+
[Quick | Short | Medium | Large]
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Common Review Smells (Fast Scan)
|
|
178
|
+
|
|
179
|
+
Task/plan adherence:
|
|
180
|
+
- Adds features not mentioned in the plan/task
|
|
181
|
+
- Leaves TODOs as the mechanism for correctness
|
|
182
|
+
- Introduces new configuration modes/flags "for future"
|
|
183
|
+
|
|
184
|
+
YAGNI / dead code:
|
|
185
|
+
- Options/config that are parsed but not used
|
|
186
|
+
- Branches that do the same thing on both sides
|
|
187
|
+
- Comments like "reserved for future" or "we might need this"
|
|
188
|
+
|
|
189
|
+
AI slop / inconsistency:
|
|
190
|
+
- Commentary that restates code, narrates obvious steps, or adds process noise
|
|
191
|
+
- try/catch that swallows errors or returns defaults without a requirement
|
|
192
|
+
- `as any` used to silence type errors instead of fixing types
|
|
193
|
+
- New helpers/abstractions with a single call site
|
|
194
|
+
|
|
195
|
+
Correctness:
|
|
196
|
+
- Silent fallbacks to defaults on error when the task expects a hard failure
|
|
197
|
+
- Unhandled error paths, missing cleanup, missing returns
|
|
198
|
+
|
|
199
|
+
Maintainability:
|
|
200
|
+
- Abstractions used once
|
|
201
|
+
- Unclear naming, "utility" grab-bags
|
|
202
|
+
|
|
203
|
+
## When to Escalate
|
|
204
|
+
|
|
205
|
+
Use NEEDS_DISCUSSION (instead of REQUEST_CHANGES) when:
|
|
206
|
+
- The plan/task is ambiguous and multiple implementations could be correct
|
|
207
|
+
- The change implies a product/architecture decision not documented
|
|
208
|
+
- Fixing issues requires changing scope, dependencies, or public API
|