opencode-hive 1.3.6 → 1.4.3

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 CHANGED
@@ -24,7 +24,7 @@ npm install opencode-hive
24
24
 
25
25
  1. Create `.opencode/mcp-servers.json` using the template:
26
26
  - From this repo: `packages/opencode-hive/templates/mcp-servers.json`
27
- - Or from npm: `node_modules/opencode-hive/templates/mcp-servers.json`
27
+ - Or from the installed npm package: `node_modules/opencode-hive/templates/mcp-servers.json`
28
28
  2. Set `EXA_API_KEY` to enable `websearch_exa` (optional).
29
29
  3. Restart OpenCode.
30
30
 
@@ -34,7 +34,7 @@ This enables tools like `grep_app_searchGitHub`, `context7_query-docs`, `websear
34
34
 
35
35
  1. **Create Feature** — `hive_feature_create("dark-mode")`
36
36
  2. **Write Plan** — AI generates structured plan
37
- 3. **Review** — You review in VS Code, add comments
37
+ 3. **Review** — Optional `vscode-hive` companion for overview/plan review and comments
38
38
  4. **Approve** — `hive_plan_approve()`
39
39
  5. **Execute** — Tasks run in isolated git worktrees
40
40
  6. **Ship** — Clean commits, full audit trail
@@ -43,6 +43,17 @@ This enables tools like `grep_app_searchGitHub`, `context7_query-docs`, `websear
43
43
 
44
44
  During planning, "don't execute" means "don't implement" (no code edits, no worktrees). Read-only exploration is explicitly allowed and encouraged, both via local tools and by delegating to Scout.
45
45
 
46
+ When delegation is warranted, synthesize the task before handing it off: name the file paths or search target, state the expected result, and say what done looks like. Workers do not inherit planner context.
47
+
48
+ For execution work, treat worker output as evidence to inspect, not proof to trust blindly. OpenCode is the supported execution harness in `1.4.0`; if you use `vscode-hive`, treat it as a review/sidebar companion. Read changed files yourself and run the shared verification commands on the main branch before claiming the batch is complete.
49
+
50
+ \`hive_network_query\` is an optional lookup, not a default step. There is no startup lookup: first orient on the live request and live repo state. planning, orchestration, and review roles get network access first. live-file verification still required even when network results look relevant.
51
+
52
+ ### Local skill and model use cases
53
+
54
+ - **Local skill experiments:** keep a skill in `<project>/.opencode/skills/<id>/SKILL.md` or `<project>/.claude/skills/<id>/SKILL.md` and add it to `skills` or `autoLoadSkills` for that repo only.
55
+ - **Local model tuning:** set per-agent models or variants in `<project>/.hive/agent-hive.json` when you want a repository-specific routing setup without changing your global OpenCode defaults.
56
+
46
57
  #### Canonical Delegation Threshold
47
58
 
48
59
  - Delegate to Scout when you cannot name the file path upfront, expect to inspect 2+ files, or the question is open-ended ("how/where does X work?").
@@ -106,22 +117,40 @@ When using Dynamic Context Pruning (DCP), use a Hive-safe config in `~/.config/o
106
117
  - `strategies.supersedeWrites.enabled: false`
107
118
  - `strategies.purgeErrors.enabled: false`
108
119
 
109
- For local plugin testing, keep OpenCode plugin entry as `"opencode-hive"` (not `"opencode-hive@latest"`).
120
+ For normal usage, set the OpenCode plugin entry to `"opencode-hive@latest"`. Keep `"opencode-hive"` only for local contributor testing with a symlinked checkout.
121
+
122
+ #### OpenCode alignment: honest hook contract and bounded recovery
110
123
 
111
- #### Compaction recovery and session re-anchoring
124
+ Agent Hive aligns to the OpenCode surfaces that exist today. It does **not** depend on a first-class upstream Hive orchestration API, a native checkpoint API, or a hidden todo-sync surface.
112
125
 
113
- OpenCode can compact long sessions. When that happens mid-orchestration or mid-task, Hive needs the session to recover its role and task boundaries without re-reading the whole repository.
126
+ At the current plugin/runtime layer, Hive relies on these supported hooks:
114
127
 
115
- The plugin now persists durable session metadata and uses it during `experimental.session.compacting` to rebuild a compact re-anchor prompt.
128
+ - `event`
129
+ - `config`
130
+ - `chat.message`
131
+ - `experimental.chat.messages.transform`
132
+ - `tool.execute.before`
116
133
 
117
- Where:
134
+ That contract matters because some older wording implied deeper integration than OpenCode currently exposes. The recovery path in this branch is hook-timed and file-backed, not storage-level magic.
135
+
136
+ Todo ownership is intentionally modest:
137
+
138
+ - OpenCode todos remain **session-scoped** and **replace-all**.
139
+ - Hive does not add a new upstream todo API.
140
+ - This plugin does not expose a derived projected-todo field or stale refresh hints as part of its runtime contract.
141
+ - Worker and subagent sessions still follow normal OpenCode limits; they should not be described as independently syncing Hive task state.
142
+ - `.hive` remains the durable source of truth for plan/task/worktree state.
143
+
144
+ Compaction recovery uses durable Hive artifacts instead of transcript dumps:
118
145
 
119
146
  - Global session state is written to `.hive/sessions.json`.
120
147
  - Feature-local mirrors are written to `.hive/features/<feature>/sessions.json`.
121
148
  - Session classification distinguishes `primary`, `subagent`, `task-worker`, and `unknown`.
122
- - Primary and subagent recovery can replay the stored user directive once after compaction.
123
- - Task-worker recovery uses the strict re-anchor plus one bounded worker-specific synthetic replay after compaction.
124
- - The task-worker replay can reference `.hive/features/<feature>/tasks/<task>/worker-prompt.md`.
149
+ - Primary and subagent recovery can replay the stored user directive once after compaction, with `directiveRecoveryState` enforcing the one-replay-then-escalate contract.
150
+ - Task-worker recovery does **not** replay the whole user directive. Workers re-read `worker-prompt.md` and continue the current task from the existing worktree state.
151
+ - Recovery uses durable session metadata plus `worker-prompt.md` instead of an extra checkpoint artifact, idle child-session replay, or parent-scoped task rehydration.
152
+
153
+ In practice, the durable task-level recovery surface is the semantic `.hive` artifact set for that task, with `worker-prompt.md` plus bound feature/task/session metadata as the re-entry surface. Hive does **not** persist raw transcript dumps as its recovery contract.
125
154
 
126
155
  Task-worker recovery is intentionally strict:
127
156
 
@@ -135,15 +164,20 @@ Task-worker recovery is intentionally strict:
135
164
  - do not use orchestration tools unless the worker prompt explicitly says so
136
165
  - continue from the last known point
137
166
 
138
- This split is deliberate: primary and subagent sessions replay the stored user directive once after compaction, while task-workers also receive one worker-specific synthetic replay after compaction that restates the active task identity and worker boundaries.
167
+ This split is deliberate: primary and subagent sessions replay the stored user directive once after compaction, while task workers recover from their own bounded task contract instead of relying on parent-session replay.
139
168
 
140
169
  Manual tasks follow the same DAG model as plan-backed tasks:
141
170
 
142
171
  - `hive_task_create()` stores manual tasks with explicit `dependsOn` metadata instead of inferring sequential order.
172
+ - manual tasks are append-only.
173
+ - intermediate insertion requires plan amendment.
174
+ - dependencies on unfinished work require plan amendment.
143
175
  - Structured manual-task fields such as `goal`, `description`, `acceptanceCriteria`, `files`, and `references` are turned into worker-facing `spec.md` content.
144
176
  - If review feedback changes downstream sequencing or scope, update `plan.md` and run `hive_tasks_sync({ refreshPending: true })` so pending plan tasks pick up the new `dependsOn` graph and regenerated specs.
145
177
 
146
- This recovery path applies to the built-in `forager-worker` and custom agents derived from it, because they are the sessions most likely to be compacted mid-implementation.
178
+ This recovery path applies to the built-in `forager-worker`, the runtime-only `hive-helper` bounded hard-task operational assistant, and custom agents derived from `forager-worker`. `hive-helper` handles merge recovery, state clarification, interrupted-state wrap-up, and safe manual-follow-up assistance while staying inside the current approved DAG boundary. It may summarize observable state, including `helperStatus`, and create safe append-only manual tasks, but it may never update plan-backed task state and must escalate DAG-changing requests for plan amendment. For the issue-72 style "task 3 is locally testable but task 4 has not started" situation, ask Helper to clarify the locally testable state and wrap-up surface first; ask it to create a safe manual follow-up only when the work can append after the current DAG; if you think you need `3b` / `3c` inserted before later plan-backed work, amend `plan.md` instead. `hive-helper` is intentionally OpenCode runtime-only in v1, not a custom base agent, and it does not appear in `.github/agents/` or `packages/vscode-hive/src/generators/`.
179
+
180
+ `hive-helper` also remains not a network consumer. It benefits indirectly from better upstream planning/orchestration/review decisions, but it does not call `hive_network_query` itself.
147
181
 
148
182
  ## Prompt Budgeting & Observability
149
183
 
@@ -194,7 +228,27 @@ Description.
194
228
 
195
229
  ## Configuration
196
230
 
197
- Hive uses a config file at `~/.config/opencode/agent_hive.json`. You can customize agent models, variants, disable skills, and disable MCP servers.
231
+ Hive reads config from these locations, in order:
232
+
233
+ 1. `<project>/.hive/agent-hive.json` (preferred)
234
+ 2. `<project>/.opencode/agent_hive.json` (legacy fallback, used only when the new file is missing)
235
+ 3. `~/.config/opencode/agent_hive.json` (global fallback)
236
+
237
+ If `.hive/agent-hive.json` exists but is invalid JSON or an invalid shape, Hive warns, skips the legacy project file, and falls back to the global config and defaults.
238
+
239
+ You can customize agent models, variants, disable skills, and disable MCP servers.
240
+
241
+ ### Project-local config example
242
+
243
+ Create `.hive/agent-hive.json`:
244
+
245
+ ```json
246
+ {
247
+ "$schema": "https://raw.githubusercontent.com/tctinh/agent-hive/main/packages/opencode-hive/schema/agent_hive.schema.json",
248
+ "agentMode": "unified",
249
+ "disableSkills": []
250
+ }
251
+ ```
198
252
 
199
253
  ### Disable Skills or MCPs
200
254
 
@@ -298,6 +352,7 @@ Skill IDs must be safe directory names (no `/`, `\`, `..`, or `.`). Missing or i
298
352
  |-------|------------------------|
299
353
  | `hive-master` | `parallel-exploration` |
300
354
  | `forager-worker` | `test-driven-development`, `verification-before-completion` |
355
+ | `hive-helper` | (none) |
301
356
  | `scout-researcher` | (none) |
302
357
  | `architect-planner` | `parallel-exploration` |
303
358
  | `swarm-orchestrator` | (none) |
@@ -355,6 +410,10 @@ Define plugin-only custom subagents with `customAgents`. Freshly initialized `ag
355
410
  - `baseAgent`: one of `forager-worker` or `hygienic-reviewer`
356
411
  - `description`: delegation guidance injected into primary planner/orchestrator prompts
357
412
 
413
+ `hive-helper` is not a custom base agent. In v1 it stays runtime-only for isolated merge recovery and does not appear in `.github/agents/` and does not appear in `packages/vscode-hive/src/generators/`.
414
+
415
+ It is also not a network consumer; planning, orchestration, and review roles get network access first.
416
+
358
417
  Published example (validated by `src/e2e/custom-agent-docs-example.test.ts`):
359
418
 
360
419
  ```json
@@ -422,7 +481,7 @@ Override models for specific agents:
422
481
 
423
482
  ## Pair with VS Code
424
483
 
425
- For the full experience, install [vscode-hive](https://marketplace.visualstudio.com/items?itemName=tctinh.vscode-hive) to review plans inline with comments.
484
+ For the full OpenCode-first workflow, install [vscode-hive](https://marketplace.visualstudio.com/items?itemName=tctinh.vscode-hive) as an optional review/sidebar companion for inline comments and approvals.
426
485
 
427
486
  ## License
428
487
 
@@ -1,18 +1,23 @@
1
1
  import type { AgentModelConfig, HiveConfig, ResolvedCustomAgentConfig } from '../types.js';
2
2
  import type { SandboxConfig } from './dockerSandboxService.js';
3
3
  /**
4
- * ConfigService manages user config at ~/.config/opencode/agent_hive.json
4
+ * ConfigService manages Agent Hive config with read precedence:
5
+ * 1. <project>/.hive/agent-hive.json (preferred when present and valid)
6
+ * 2. <project>/.opencode/agent_hive.json (legacy fallback during migration)
7
+ * 3. ~/.config/opencode/agent_hive.json (fallback)
5
8
  *
6
- * This is USER config (not project-scoped):
7
- * - VSCode extension reads/writes this
8
- * - OpenCode plugin reads this to enable features
9
- * - Agent does NOT have tools to access this
9
+ * Writes remain global-only at ~/.config/opencode/agent_hive.json.
10
10
  */
11
11
  export declare class ConfigService {
12
12
  private configPath;
13
+ private projectConfigPath?;
14
+ private legacyProjectConfigPath?;
13
15
  private cachedConfig;
14
16
  private cachedCustomAgentConfigs;
15
- constructor();
17
+ private activeReadSourceType;
18
+ private activeReadPath;
19
+ private lastFallbackWarning;
20
+ constructor(projectRoot?: string);
16
21
  /**
17
22
  * Get config path
18
23
  */
@@ -21,6 +26,16 @@ export declare class ConfigService {
21
26
  * Get the full config, merged with defaults.
22
27
  */
23
28
  get(): HiveConfig;
29
+ getActiveReadSourceType(): 'project' | 'global';
30
+ getActiveReadPath(): string;
31
+ getLastFallbackWarning(): {
32
+ message: string;
33
+ sourceType: 'project' | 'global';
34
+ sourcePath: string;
35
+ fallbackType: 'global' | 'defaults';
36
+ fallbackPath?: string;
37
+ reason: 'parse_error' | 'validation_error' | 'read_error';
38
+ } | null;
24
39
  /**
25
40
  * Update config (partial merge).
26
41
  */
@@ -75,4 +90,11 @@ export declare class ConfigService {
75
90
  getHookCadence(hookName: string, options?: {
76
91
  safetyCritical?: boolean;
77
92
  }): number;
93
+ private readStoredConfig;
94
+ private mergeWithDefaults;
95
+ private createProjectFallbackWarning;
96
+ private isValidStoredConfig;
97
+ private isStringArray;
98
+ private isValidAgentConfigDeclaration;
99
+ private isHookCadenceRecord;
78
100
  }
@@ -1,5 +1,5 @@
1
- import type { ContextFile } from '../types.js';
2
- export type { ContextFile };
1
+ import type { ContextFile, ContextRole } from '../types.js';
2
+ export type { ContextFile, ContextRole };
3
3
  export declare const RESERVED_OVERVIEW_CONTEXT = "overview";
4
4
  export declare class ContextService {
5
5
  private projectRoot;
@@ -9,6 +9,8 @@ export declare class ContextService {
9
9
  list(featureName: string): ContextFile[];
10
10
  getOverview(featureName: string): ContextFile | null;
11
11
  listExecutionContext(featureName: string): ContextFile[];
12
+ listAgentsMdSyncContext(featureName: string): ContextFile[];
13
+ listNetworkContext(featureName: string): ContextFile[];
12
14
  delete(featureName: string, fileName: string): boolean;
13
15
  compile(featureName: string): string;
14
16
  archive(featureName: string): {
@@ -22,4 +24,5 @@ export declare class ContextService {
22
24
  newest?: string;
23
25
  };
24
26
  private normalizeFileName;
27
+ private classifyContextName;
25
28
  }
@@ -6,6 +6,7 @@ export { SubtaskService } from './subtaskService.js';
6
6
  export { WorktreeService, createWorktreeService } from './worktreeService.js';
7
7
  export type { WorktreeInfo, DiffResult, ApplyResult, CommitResult, MergeResult, WorktreeConfig } from './worktreeService.js';
8
8
  export { ContextService } from './contextService.js';
9
+ export { NetworkService } from './networkService.js';
9
10
  export { ReviewService } from './reviewService.js';
10
11
  export { SessionService } from './sessionService.js';
11
12
  export { ConfigService } from './configService.js';
@@ -0,0 +1,25 @@
1
+ export interface NetworkQueryOptions {
2
+ currentFeature?: string;
3
+ query: string;
4
+ maxFeatures: number;
5
+ maxSnippetsPerFeature: number;
6
+ maxSnippetChars: number;
7
+ }
8
+ export interface NetworkQueryResult {
9
+ feature: string;
10
+ sourceType: 'plan' | 'context';
11
+ sourceName: string;
12
+ path: string;
13
+ updatedAt: string;
14
+ snippet: string;
15
+ }
16
+ export declare class NetworkService {
17
+ private readonly projectRoot;
18
+ private readonly contextService;
19
+ constructor(projectRoot: string);
20
+ query(options: NetworkQueryOptions): NetworkQueryResult[];
21
+ private collectMatches;
22
+ private matchPlan;
23
+ private matchContext;
24
+ private resolveDirectoryName;
25
+ }
@@ -113,6 +113,7 @@ export declare class TaskService {
113
113
  private listFolders;
114
114
  private deleteTask;
115
115
  private getNextOrder;
116
+ private validateManualTaskDependsOn;
116
117
  private parseTasksFromPlan;
117
118
  createSubtask(featureName: string, taskFolder: string, name: string, type?: SubtaskType): Subtask;
118
119
  updateSubtask(featureName: string, taskFolder: string, subtaskId: string, status: TaskStatusType): Subtask;
@@ -22,12 +22,23 @@ export interface CommitResult {
22
22
  sha: string;
23
23
  message?: string;
24
24
  }
25
+ export interface MergeOptions {
26
+ preserveConflicts?: boolean;
27
+ cleanup?: 'none' | 'worktree' | 'worktree+branch';
28
+ }
25
29
  export interface MergeResult {
26
30
  success: boolean;
27
31
  merged: boolean;
32
+ strategy: 'merge' | 'squash' | 'rebase';
28
33
  sha?: string;
29
- filesChanged?: string[];
30
- conflicts?: string[];
34
+ filesChanged: string[];
35
+ conflicts: string[];
36
+ conflictState: 'none' | 'aborted' | 'preserved';
37
+ cleanup: {
38
+ worktreeRemoved: boolean;
39
+ branchDeleted: boolean;
40
+ pruned: boolean;
41
+ };
31
42
  error?: string;
32
43
  }
33
44
  export interface WorktreeConfig {
@@ -50,7 +61,11 @@ export declare class WorktreeService {
50
61
  revertDiff(feature: string, step: string, baseBranch?: string): Promise<ApplyResult>;
51
62
  private parseFilesFromDiff;
52
63
  revertFromSavedDiff(diffPath: string): Promise<ApplyResult>;
53
- remove(feature: string, step: string, deleteBranch?: boolean): Promise<void>;
64
+ remove(feature: string, step: string, deleteBranch?: boolean): Promise<{
65
+ worktreeRemoved: boolean;
66
+ branchDeleted: boolean;
67
+ pruned: boolean;
68
+ }>;
54
69
  list(feature?: string): Promise<WorktreeInfo[]>;
55
70
  cleanup(feature?: string): Promise<{
56
71
  removed: string[];
@@ -59,8 +74,9 @@ export declare class WorktreeService {
59
74
  checkConflicts(feature: string, step: string, baseBranch?: string): Promise<string[]>;
60
75
  checkConflictsFromSavedDiff(diffPath: string, reverse?: boolean): Promise<string[]>;
61
76
  commitChanges(feature: string, step: string, message?: string): Promise<CommitResult>;
62
- merge(feature: string, step: string, strategy?: "merge" | "squash" | "rebase", message?: string): Promise<MergeResult>;
77
+ merge(feature: string, step: string, strategy?: "merge" | "squash" | "rebase", message?: string, options?: MergeOptions): Promise<MergeResult>;
63
78
  hasUncommittedChanges(feature: string, step: string): Promise<boolean>;
64
79
  private parseConflictsFromError;
80
+ private getActiveConflictFiles;
65
81
  }
66
82
  export declare function createWorktreeService(projectDir: string): WorktreeService;
@@ -127,12 +127,18 @@ export interface FeatureInfo {
127
127
  commentCount: number;
128
128
  reviewCounts: ReviewCounts;
129
129
  }
130
+ export type ContextRole = 'human' | 'scratchpad' | 'operational' | 'durable';
130
131
  export interface ContextFile {
131
132
  name: string;
132
133
  content: string;
133
134
  updatedAt: string;
135
+ role: ContextRole;
136
+ includeInExecution: boolean;
137
+ includeInAgentsMdSync: boolean;
138
+ includeInNetwork: boolean;
134
139
  }
135
140
  export type SessionKind = 'primary' | 'subagent' | 'task-worker' | 'unknown';
141
+ export type DirectiveRecoveryState = 'available' | 'consumed' | 'escalated';
136
142
  export interface SessionInfo {
137
143
  sessionId: string;
138
144
  featureName?: string;
@@ -142,6 +148,7 @@ export interface SessionInfo {
142
148
  sessionKind?: SessionKind;
143
149
  workerPromptPath?: string;
144
150
  directivePrompt?: string;
151
+ directiveRecoveryState?: DirectiveRecoveryState;
145
152
  replayDirectivePending?: boolean;
146
153
  startedAt: string;
147
154
  lastActiveAt: string;
@@ -174,11 +181,11 @@ export interface AgentModelConfig {
174
181
  /** Variant key for model reasoning/effort level (e.g., 'low', 'medium', 'high', 'max') */
175
182
  variant?: string;
176
183
  }
177
- export declare const BUILT_IN_AGENT_NAMES: readonly ["hive-master", "architect-planner", "swarm-orchestrator", "scout-researcher", "forager-worker", "hygienic-reviewer"];
184
+ export declare const BUILT_IN_AGENT_NAMES: readonly ["hive-master", "architect-planner", "swarm-orchestrator", "scout-researcher", "forager-worker", "hive-helper", "hygienic-reviewer"];
178
185
  export type BuiltInAgentName = (typeof BUILT_IN_AGENT_NAMES)[number];
179
186
  export declare const CUSTOM_AGENT_BASES: readonly ["forager-worker", "hygienic-reviewer"];
180
187
  export type CustomAgentBase = (typeof CUSTOM_AGENT_BASES)[number];
181
- export declare const CUSTOM_AGENT_RESERVED_NAMES: readonly ["hive-master", "architect-planner", "swarm-orchestrator", "scout-researcher", "forager-worker", "hygienic-reviewer", "hive", "architect", "swarm", "scout", "forager", "hygienic", "receiver", "build", "plan", "code"];
188
+ export declare const CUSTOM_AGENT_RESERVED_NAMES: readonly ["hive-master", "architect-planner", "swarm-orchestrator", "scout-researcher", "forager-worker", "hive-helper", "hygienic-reviewer", "hive", "architect", "swarm", "scout", "forager", "hygienic", "receiver", "build", "plan", "code"];
182
189
  export interface CustomAgentConfig {
183
190
  baseAgent: CustomAgentBase;
184
191
  description: string;
@@ -216,6 +223,8 @@ export interface HiveConfig {
216
223
  'scout-researcher'?: AgentModelConfig;
217
224
  /** Forager Worker */
218
225
  'forager-worker'?: AgentModelConfig;
226
+ /** Hive Helper */
227
+ 'hive-helper'?: AgentModelConfig;
219
228
  /** Hygienic Reviewer */
220
229
  'hygienic-reviewer'?: AgentModelConfig;
221
230
  };
@@ -236,6 +245,7 @@ export declare const DEFAULT_AGENT_MODELS: {
236
245
  readonly 'swarm-orchestrator': "github-copilot/claude-opus-4.5";
237
246
  readonly 'scout-researcher': "zai-coding-plan/glm-4.7";
238
247
  readonly 'forager-worker': "github-copilot/gpt-5.2-codex";
248
+ readonly 'hive-helper': "github-copilot/gpt-5.2-codex";
239
249
  readonly 'hygienic-reviewer': "github-copilot/gpt-5.2-codex";
240
250
  };
241
251
  export declare const DEFAULT_HIVE_CONFIG: HiveConfig;