opencode-hive 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/hive.d.ts +30 -0
- package/dist/index.js +1463 -603
- package/dist/skills/builtin.d.ts +26 -0
- package/dist/skills/index.d.ts +7 -0
- package/dist/skills/registry.generated.d.ts +14 -0
- package/dist/skills/types.d.ts +29 -0
- package/dist/utils/agent-selector.d.ts +29 -0
- package/dist/utils/worker-prompt.d.ts +42 -0
- package/package.json +8 -3
- package/skills/hive-execution/SKILL.md +60 -0
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __export = (target, all) => {
|
|
3
4
|
for (var name in all)
|
|
@@ -8,9 +9,11 @@ var __export = (target, all) => {
|
|
|
8
9
|
set: (newValue) => all[name] = () => newValue
|
|
9
10
|
});
|
|
10
11
|
};
|
|
12
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
11
13
|
|
|
12
14
|
// src/index.ts
|
|
13
|
-
import * as
|
|
15
|
+
import * as path5 from "path";
|
|
16
|
+
import * as fs6 from "fs";
|
|
14
17
|
|
|
15
18
|
// ../../node_modules/.bun/zod@4.1.8/node_modules/zod/v4/classic/external.js
|
|
16
19
|
var exports_external = {};
|
|
@@ -12332,8 +12335,339 @@ function tool(input) {
|
|
|
12332
12335
|
return input;
|
|
12333
12336
|
}
|
|
12334
12337
|
tool.schema = exports_external;
|
|
12338
|
+
// src/skills/registry.generated.ts
|
|
12339
|
+
var BUILTIN_SKILL_NAMES = ["hive", "hive-execution"];
|
|
12340
|
+
var BUILTIN_SKILLS = [
|
|
12341
|
+
{
|
|
12342
|
+
name: "hive",
|
|
12343
|
+
description: "Plan-first AI development with isolated git worktrees and human review. Use for any feature development.",
|
|
12344
|
+
template: `# Hive Workflow
|
|
12345
|
+
|
|
12346
|
+
You are working in a Hive-enabled repository. Follow this plan-first workflow.
|
|
12347
|
+
|
|
12348
|
+
## Lifecycle
|
|
12349
|
+
|
|
12350
|
+
\`\`\`
|
|
12351
|
+
Feature -> Plan -> Review -> Approve -> Execute -> Merge -> Complete
|
|
12352
|
+
\`\`\`
|
|
12353
|
+
|
|
12354
|
+
---
|
|
12355
|
+
|
|
12356
|
+
## Phase 1: Planning
|
|
12357
|
+
|
|
12358
|
+
### Start Feature
|
|
12359
|
+
|
|
12360
|
+
\`\`\`
|
|
12361
|
+
hive_feature_create({ name: "feature-name" })
|
|
12362
|
+
\`\`\`
|
|
12363
|
+
|
|
12364
|
+
### Discovery Phase (Required)
|
|
12365
|
+
|
|
12366
|
+
BEFORE writing a plan, you MUST:
|
|
12367
|
+
1. Ask clarifying questions about the feature
|
|
12368
|
+
2. Document Q&A in plan.md with a \`## Discovery\` section
|
|
12369
|
+
3. Research the codebase (grep, read existing code)
|
|
12370
|
+
4. Save findings with hive_context_write
|
|
12371
|
+
|
|
12372
|
+
Your plan MUST include a \`## Discovery\` section or hive_plan_write will be BLOCKED.
|
|
12373
|
+
|
|
12374
|
+
Example discovery section:
|
|
12375
|
+
\`\`\`markdown
|
|
12376
|
+
## Discovery
|
|
12377
|
+
|
|
12378
|
+
**Q: What authentication system do we use?**
|
|
12379
|
+
A: JWT with refresh tokens, see src/auth/
|
|
12380
|
+
|
|
12381
|
+
**Q: Should this work offline?**
|
|
12382
|
+
A: No, online-only is fine
|
|
12383
|
+
|
|
12384
|
+
**Research:**
|
|
12385
|
+
- Found existing theme system in src/theme/
|
|
12386
|
+
- Uses CSS variables pattern
|
|
12387
|
+
\`\`\`
|
|
12388
|
+
|
|
12389
|
+
### Research First
|
|
12390
|
+
|
|
12391
|
+
Before writing anything:
|
|
12392
|
+
1. Search for relevant files (grep, explore)
|
|
12393
|
+
2. Read existing implementations
|
|
12394
|
+
3. Identify patterns and conventions
|
|
12395
|
+
|
|
12396
|
+
Save all findings:
|
|
12397
|
+
\`\`\`
|
|
12398
|
+
hive_context_write({
|
|
12399
|
+
name: "research",
|
|
12400
|
+
content: \`# Research Findings
|
|
12401
|
+
|
|
12402
|
+
## Existing Patterns
|
|
12403
|
+
- Theme system uses CSS variables in src/theme/
|
|
12404
|
+
- Components follow atomic design
|
|
12405
|
+
|
|
12406
|
+
## Files to Modify
|
|
12407
|
+
- src/theme/colors.ts
|
|
12408
|
+
- src/components/ThemeProvider.tsx
|
|
12409
|
+
\`
|
|
12410
|
+
})
|
|
12411
|
+
\`\`\`
|
|
12412
|
+
|
|
12413
|
+
### Write the Plan
|
|
12414
|
+
|
|
12415
|
+
Your plan should include these sections:
|
|
12416
|
+
|
|
12417
|
+
| Section | Required | Purpose |
|
|
12418
|
+
|---------|----------|---------|
|
|
12419
|
+
| \`## Discovery\` | Yes (gate enforced) | Q&A and research before planning |
|
|
12420
|
+
| \`## Problem\` | Yes | What we're solving |
|
|
12421
|
+
| \`## Non-Goals\` | Recommended | What we're NOT building (scope boundaries) |
|
|
12422
|
+
| \`## Tasks\` | Yes | Implementation steps with \`### N. Task Name\` format |
|
|
12423
|
+
| \`## Ghost Diffs\` | Recommended | Rejected alternatives (prevents re-proposing) |
|
|
12424
|
+
| \`## Success Criteria\` | Optional | How we know we're done |
|
|
12425
|
+
|
|
12426
|
+
Format for task parsing:
|
|
12427
|
+
|
|
12428
|
+
\`\`\`markdown
|
|
12429
|
+
# Feature Name
|
|
12430
|
+
|
|
12431
|
+
## Overview
|
|
12432
|
+
One paragraph explaining what and why.
|
|
12433
|
+
|
|
12434
|
+
## Tasks
|
|
12435
|
+
|
|
12436
|
+
### 1. Task Name
|
|
12437
|
+
Description of what this task accomplishes.
|
|
12438
|
+
- Specific files to modify
|
|
12439
|
+
- Expected outcome
|
|
12440
|
+
|
|
12441
|
+
### 2. Another Task
|
|
12442
|
+
Description...
|
|
12443
|
+
|
|
12444
|
+
### 3. Final Task
|
|
12445
|
+
Description...
|
|
12446
|
+
\`\`\`
|
|
12447
|
+
|
|
12448
|
+
Write with:
|
|
12449
|
+
\`\`\`
|
|
12450
|
+
hive_plan_write({ content: \`...\` })
|
|
12451
|
+
\`\`\`
|
|
12452
|
+
|
|
12453
|
+
**STOP** and tell user: "Plan written. Please review."
|
|
12454
|
+
|
|
12455
|
+
---
|
|
12456
|
+
|
|
12457
|
+
## Phase 2: Review (Human)
|
|
12458
|
+
|
|
12459
|
+
- User reviews plan.md in VS Code sidebar
|
|
12460
|
+
- User can add comments
|
|
12461
|
+
- Use \`hive_plan_read()\` to see user comments
|
|
12462
|
+
- Revise plan based on feedback
|
|
12463
|
+
- User clicks "Approve" or runs \`hive_plan_approve()\`
|
|
12464
|
+
|
|
12465
|
+
---
|
|
12466
|
+
|
|
12467
|
+
## Phase 3: Execution
|
|
12468
|
+
|
|
12469
|
+
### Generate Tasks
|
|
12470
|
+
|
|
12471
|
+
\`\`\`
|
|
12472
|
+
hive_tasks_sync()
|
|
12473
|
+
\`\`\`
|
|
12474
|
+
|
|
12475
|
+
Parses \`### N. Task Name\` headers into task folders.
|
|
12476
|
+
|
|
12477
|
+
### Execute Each Task
|
|
12478
|
+
|
|
12479
|
+
For each task in order:
|
|
12480
|
+
|
|
12481
|
+
#### 1. Start (creates worktree)
|
|
12482
|
+
\`\`\`
|
|
12483
|
+
hive_exec_start({ task: "01-task-name" })
|
|
12484
|
+
\`\`\`
|
|
12485
|
+
|
|
12486
|
+
#### 2. Implement
|
|
12487
|
+
Work in the isolated worktree path. Read \`spec.md\` for context.
|
|
12488
|
+
|
|
12489
|
+
#### 3. Complete (commits to branch)
|
|
12490
|
+
\`\`\`
|
|
12491
|
+
hive_exec_complete({ task: "01-task-name", summary: "What was done. Tests pass." })
|
|
12492
|
+
\`\`\`
|
|
12493
|
+
|
|
12494
|
+
**Note**: Summary must mention verification (tests/build) or completion will be BLOCKED.
|
|
12495
|
+
|
|
12496
|
+
#### 4. Merge (integrates to main)
|
|
12497
|
+
\`\`\`
|
|
12498
|
+
hive_merge({ task: "01-task-name", strategy: "squash" })
|
|
12499
|
+
\`\`\`
|
|
12500
|
+
|
|
12501
|
+
---
|
|
12502
|
+
|
|
12503
|
+
## Phase 4: Completion
|
|
12504
|
+
|
|
12505
|
+
After all tasks merged:
|
|
12506
|
+
\`\`\`
|
|
12507
|
+
hive_feature_complete({ name: "feature-name" })
|
|
12508
|
+
\`\`\`
|
|
12509
|
+
|
|
12510
|
+
---
|
|
12511
|
+
|
|
12512
|
+
## Tool Quick Reference
|
|
12513
|
+
|
|
12514
|
+
| Phase | Tool | Purpose |
|
|
12515
|
+
|-------|------|---------|
|
|
12516
|
+
| Plan | \`hive_feature_create\` | Start new feature |
|
|
12517
|
+
| Plan | \`hive_context_write\` | Save research findings |
|
|
12518
|
+
| Plan | \`hive_plan_write\` | Write the plan |
|
|
12519
|
+
| Plan | \`hive_plan_read\` | Check for user comments |
|
|
12520
|
+
| Plan | \`hive_plan_approve\` | Approve plan |
|
|
12521
|
+
| Execute | \`hive_tasks_sync\` | Generate tasks from plan |
|
|
12522
|
+
| Execute | \`hive_exec_start\` | Start task (creates worktree) |
|
|
12523
|
+
| Execute | \`hive_exec_complete\` | Finish task (commits changes) |
|
|
12524
|
+
| Execute | \`hive_merge\` | Integrate task to main |
|
|
12525
|
+
| Complete | \`hive_feature_complete\` | Mark feature done |
|
|
12526
|
+
|
|
12527
|
+
---
|
|
12528
|
+
|
|
12529
|
+
## Task Design Guidelines
|
|
12530
|
+
|
|
12531
|
+
### Good Tasks
|
|
12532
|
+
|
|
12533
|
+
| Characteristic | Example |
|
|
12534
|
+
|---------------|---------|
|
|
12535
|
+
| **Atomic** | "Add ThemeContext provider" not "Add theming" |
|
|
12536
|
+
| **Testable** | "Toggle switches between light/dark" |
|
|
12537
|
+
| **Independent** | Can be completed without other tasks (where possible) |
|
|
12538
|
+
| **Ordered** | Dependencies come first |
|
|
12539
|
+
|
|
12540
|
+
### Task Sizing
|
|
12541
|
+
|
|
12542
|
+
- **Too small**: "Add import statement" - combine with related work
|
|
12543
|
+
- **Too large**: "Implement entire feature" - break into logical units
|
|
12544
|
+
- **Just right**: "Create theme context with light/dark values"
|
|
12545
|
+
|
|
12546
|
+
---
|
|
12547
|
+
|
|
12548
|
+
## Rules
|
|
12549
|
+
|
|
12550
|
+
1. **Never skip planning** - Always write plan first
|
|
12551
|
+
2. **Context is critical** - Save all research with \`hive_context_write\`
|
|
12552
|
+
3. **Wait for approval** - Don't execute until user approves
|
|
12553
|
+
4. **One task at a time** - Complete and merge before starting next
|
|
12554
|
+
5. **Squash merges** - Keep history clean with single commit per task
|
|
12555
|
+
|
|
12556
|
+
---
|
|
12557
|
+
|
|
12558
|
+
## Error Recovery
|
|
12559
|
+
|
|
12560
|
+
### Task Failed
|
|
12561
|
+
\`\`\`
|
|
12562
|
+
hive_exec_abort(task="<task>") # Discards changes
|
|
12563
|
+
hive_exec_start(task="<task>") # Fresh start
|
|
12564
|
+
\`\`\`
|
|
12565
|
+
|
|
12566
|
+
### Merge Conflicts
|
|
12567
|
+
1. Resolve conflicts in the worktree
|
|
12568
|
+
2. Commit the resolution
|
|
12569
|
+
3. Run \`hive_merge\` again
|
|
12570
|
+
|
|
12571
|
+
---
|
|
12572
|
+
|
|
12573
|
+
## Example
|
|
12574
|
+
|
|
12575
|
+
User: "Add dark mode support"
|
|
12576
|
+
|
|
12577
|
+
\`\`\`
|
|
12578
|
+
1. hive_feature_create({ name: "dark-mode" })
|
|
12579
|
+
2. Research: grep for theme, colors, CSS variables
|
|
12580
|
+
3. hive_context_write({ name: "research", content: "Found theme in src/theme/..." })
|
|
12581
|
+
4. hive_plan_write({ content: "# Dark Mode\\n\\n## Tasks\\n\\n### 1. Add theme context..." })
|
|
12582
|
+
5. Tell user: "Plan ready for review"
|
|
12583
|
+
6. [User reviews and approves]
|
|
12584
|
+
7. hive_tasks_sync()
|
|
12585
|
+
8. For each task: exec_start -> implement -> exec_complete -> merge
|
|
12586
|
+
9. hive_feature_complete({ name: "dark-mode" })
|
|
12587
|
+
\`\`\``
|
|
12588
|
+
},
|
|
12589
|
+
{
|
|
12590
|
+
name: "hive-execution",
|
|
12591
|
+
description: "Execute Hive feature tasks with worktree isolation, parallel orchestration, and clean git history. Use when running synced Hive tasks.",
|
|
12592
|
+
template: `# Hive Execution
|
|
12593
|
+
|
|
12594
|
+
Quick reference for executing Hive tasks.
|
|
12595
|
+
|
|
12596
|
+
## Workflow Summary
|
|
12597
|
+
|
|
12598
|
+
1. **Feature create** → Discovery guide injected
|
|
12599
|
+
2. **Discovery** → Q&A documented in plan.md
|
|
12600
|
+
3. **Plan write** → GATE: requires ## Discovery section
|
|
12601
|
+
4. **Approval** → User reviews in VS Code
|
|
12602
|
+
5. **Exec start** → Delegation guide (Master), TDD+debugging (Worker)
|
|
12603
|
+
6. **Complete** → GATE: requires verification mention
|
|
12604
|
+
7. **Merge** → Squash into feature branch
|
|
12605
|
+
|
|
12606
|
+
## Gates
|
|
12607
|
+
|
|
12608
|
+
| Tool | Gate | Error |
|
|
12609
|
+
|------|------|-------|
|
|
12610
|
+
| hive_plan_write | ## Discovery section | "BLOCKED: Discovery required" |
|
|
12611
|
+
| hive_exec_complete | Verification in summary | "BLOCKED: No verification" |
|
|
12612
|
+
|
|
12613
|
+
## Task Lifecycle
|
|
12614
|
+
|
|
12615
|
+
\`\`\`
|
|
12616
|
+
hive_exec_start(task) # Creates worktree
|
|
12617
|
+
↓
|
|
12618
|
+
[implement in worktree]
|
|
12619
|
+
↓
|
|
12620
|
+
hive_exec_complete(task, summary) # Commits to branch
|
|
12621
|
+
↓
|
|
12622
|
+
hive_merge(task, strategy: "squash") # Integrates to main
|
|
12623
|
+
\`\`\`
|
|
12624
|
+
|
|
12625
|
+
## Quick Reference
|
|
12626
|
+
|
|
12627
|
+
| Tool | Purpose |
|
|
12628
|
+
|------|---------|
|
|
12629
|
+
| hive_status | Check overall progress |
|
|
12630
|
+
| hive_worker_status | Check delegated workers |
|
|
12631
|
+
| hive_exec_abort | Discard changes, restart |
|
|
12632
|
+
| hive_merge | Integrate completed task |
|
|
12633
|
+
| hive_worktree_list | See active worktrees |
|
|
12634
|
+
|
|
12635
|
+
## Error Recovery
|
|
12636
|
+
|
|
12637
|
+
### Task Failed
|
|
12638
|
+
\`\`\`
|
|
12639
|
+
hive_exec_abort(task) # Discards changes
|
|
12640
|
+
hive_exec_start(task) # Fresh start
|
|
12641
|
+
\`\`\`
|
|
12642
|
+
|
|
12643
|
+
### Merge Conflicts
|
|
12644
|
+
1. Resolve conflicts in worktree
|
|
12645
|
+
2. Commit resolution
|
|
12646
|
+
3. Run hive_merge again`
|
|
12647
|
+
}
|
|
12648
|
+
];
|
|
12649
|
+
|
|
12650
|
+
// src/skills/builtin.ts
|
|
12651
|
+
function loadBuiltinSkill(name) {
|
|
12652
|
+
const skill = BUILTIN_SKILLS.find((s) => s.name === name);
|
|
12653
|
+
if (!skill) {
|
|
12654
|
+
return {
|
|
12655
|
+
found: false,
|
|
12656
|
+
error: `Unknown builtin skill: ${name}. Available: ${BUILTIN_SKILL_NAMES.join(", ")}`
|
|
12657
|
+
};
|
|
12658
|
+
}
|
|
12659
|
+
return {
|
|
12660
|
+
found: true,
|
|
12661
|
+
skill,
|
|
12662
|
+
source: "builtin"
|
|
12663
|
+
};
|
|
12664
|
+
}
|
|
12665
|
+
function getBuiltinSkills() {
|
|
12666
|
+
return BUILTIN_SKILLS;
|
|
12667
|
+
}
|
|
12668
|
+
|
|
12335
12669
|
// ../hive-core/dist/index.js
|
|
12336
|
-
import { createRequire } from "node:module";
|
|
12670
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
12337
12671
|
import * as path from "path";
|
|
12338
12672
|
import * as fs from "fs";
|
|
12339
12673
|
import * as path2 from "path";
|
|
@@ -12341,16 +12675,16 @@ import * as fs2 from "fs";
|
|
|
12341
12675
|
import * as fs3 from "fs";
|
|
12342
12676
|
import * as fs4 from "fs";
|
|
12343
12677
|
import * as fs5 from "fs";
|
|
12344
|
-
import * as
|
|
12678
|
+
import * as fs7 from "fs/promises";
|
|
12345
12679
|
import * as path3 from "path";
|
|
12346
12680
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
12347
12681
|
import { spawn } from "child_process";
|
|
12348
12682
|
import { normalize } from "node:path";
|
|
12349
12683
|
import { EventEmitter } from "node:events";
|
|
12350
|
-
import * as fs7 from "fs";
|
|
12351
|
-
import * as path4 from "path";
|
|
12352
12684
|
import * as fs8 from "fs";
|
|
12353
|
-
import * as
|
|
12685
|
+
import * as path4 from "path";
|
|
12686
|
+
import * as fs10 from "fs";
|
|
12687
|
+
import * as path6 from "path";
|
|
12354
12688
|
var __create = Object.create;
|
|
12355
12689
|
var __getProtoOf = Object.getPrototypeOf;
|
|
12356
12690
|
var __defProp2 = Object.defineProperty;
|
|
@@ -12368,7 +12702,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
12368
12702
|
return to;
|
|
12369
12703
|
};
|
|
12370
12704
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
12371
|
-
var
|
|
12705
|
+
var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
|
|
12372
12706
|
var require_ms = __commonJS((exports, module) => {
|
|
12373
12707
|
var s = 1000;
|
|
12374
12708
|
var m = s * 60;
|
|
@@ -12817,8 +13151,8 @@ var require_has_flag = __commonJS((exports, module) => {
|
|
|
12817
13151
|
};
|
|
12818
13152
|
});
|
|
12819
13153
|
var require_supports_color = __commonJS((exports, module) => {
|
|
12820
|
-
var os =
|
|
12821
|
-
var tty =
|
|
13154
|
+
var os = __require2("os");
|
|
13155
|
+
var tty = __require2("tty");
|
|
12822
13156
|
var hasFlag = require_has_flag();
|
|
12823
13157
|
var { env } = process;
|
|
12824
13158
|
var forceColor;
|
|
@@ -12914,8 +13248,8 @@ var require_supports_color = __commonJS((exports, module) => {
|
|
|
12914
13248
|
};
|
|
12915
13249
|
});
|
|
12916
13250
|
var require_node = __commonJS((exports, module) => {
|
|
12917
|
-
var tty =
|
|
12918
|
-
var util =
|
|
13251
|
+
var tty = __require2("tty");
|
|
13252
|
+
var util = __require2("util");
|
|
12919
13253
|
exports.init = init;
|
|
12920
13254
|
exports.log = log;
|
|
12921
13255
|
exports.formatArgs = formatArgs;
|
|
@@ -13094,7 +13428,7 @@ var require_src2 = __commonJS((exports) => {
|
|
|
13094
13428
|
return mod && mod.__esModule ? mod : { default: mod };
|
|
13095
13429
|
};
|
|
13096
13430
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13097
|
-
var fs_1 =
|
|
13431
|
+
var fs_1 = __require2("fs");
|
|
13098
13432
|
var debug_1 = __importDefault(require_src());
|
|
13099
13433
|
var log = debug_1.default("@kwsites/file-exists");
|
|
13100
13434
|
function check2(path32, isFile, isDirectory) {
|
|
@@ -13174,6 +13508,17 @@ var require_dist2 = __commonJS((exports) => {
|
|
|
13174
13508
|
exports.createDeferred = deferred;
|
|
13175
13509
|
exports.default = deferred;
|
|
13176
13510
|
});
|
|
13511
|
+
var DEFAULT_HIVE_CONFIG = {
|
|
13512
|
+
enableToolsFor: [],
|
|
13513
|
+
agents: {
|
|
13514
|
+
worker: {
|
|
13515
|
+
visible: true
|
|
13516
|
+
}
|
|
13517
|
+
},
|
|
13518
|
+
omoSlim: {
|
|
13519
|
+
enabled: false
|
|
13520
|
+
}
|
|
13521
|
+
};
|
|
13177
13522
|
var HIVE_DIR = ".hive";
|
|
13178
13523
|
var FEATURES_DIR = "features";
|
|
13179
13524
|
var TASKS_DIR = "tasks";
|
|
@@ -13183,9 +13528,14 @@ var COMMENTS_FILE = "comments.json";
|
|
|
13183
13528
|
var FEATURE_FILE = "feature.json";
|
|
13184
13529
|
var STATUS_FILE = "status.json";
|
|
13185
13530
|
var REPORT_FILE = "report.md";
|
|
13531
|
+
var APPROVED_FILE = "APPROVED";
|
|
13532
|
+
var JOURNAL_FILE = "journal.md";
|
|
13186
13533
|
function getHivePath(projectRoot) {
|
|
13187
13534
|
return path.join(projectRoot, HIVE_DIR);
|
|
13188
13535
|
}
|
|
13536
|
+
function getJournalPath(projectRoot) {
|
|
13537
|
+
return path.join(getHivePath(projectRoot), JOURNAL_FILE);
|
|
13538
|
+
}
|
|
13189
13539
|
function getFeaturesPath(projectRoot) {
|
|
13190
13540
|
return path.join(getHivePath(projectRoot), FEATURES_DIR);
|
|
13191
13541
|
}
|
|
@@ -13219,6 +13569,9 @@ function getTaskReportPath(projectRoot, featureName, taskFolder) {
|
|
|
13219
13569
|
function getTaskSpecPath(projectRoot, featureName, taskFolder) {
|
|
13220
13570
|
return path.join(getTaskPath(projectRoot, featureName, taskFolder), "spec.md");
|
|
13221
13571
|
}
|
|
13572
|
+
function getApprovedPath(projectRoot, featureName) {
|
|
13573
|
+
return path.join(getFeaturePath(projectRoot, featureName), APPROVED_FILE);
|
|
13574
|
+
}
|
|
13222
13575
|
var SUBTASKS_DIR = "subtasks";
|
|
13223
13576
|
var SPEC_FILE = "spec.md";
|
|
13224
13577
|
function getSubtasksPath(projectRoot, featureName, taskFolder) {
|
|
@@ -13312,6 +13665,22 @@ function listFeatures(projectRoot) {
|
|
|
13312
13665
|
return [];
|
|
13313
13666
|
return fs2.readdirSync(featuresPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
13314
13667
|
}
|
|
13668
|
+
var JOURNAL_TEMPLATE = `# Hive Journal
|
|
13669
|
+
|
|
13670
|
+
Audit trail of project learnings. Updated when trouble is resolved.
|
|
13671
|
+
|
|
13672
|
+
---
|
|
13673
|
+
|
|
13674
|
+
<!-- Entry template:
|
|
13675
|
+
### YYYY-MM-DD: feature-name
|
|
13676
|
+
|
|
13677
|
+
**Trouble**: What went wrong
|
|
13678
|
+
**Resolution**: How it was fixed
|
|
13679
|
+
**Constraint**: Never/Always rule derived (add to Iron Laws if recurring)
|
|
13680
|
+
**See**: .hive/features/feature-name/plan.md
|
|
13681
|
+
-->
|
|
13682
|
+
`;
|
|
13683
|
+
|
|
13315
13684
|
class FeatureService {
|
|
13316
13685
|
projectRoot;
|
|
13317
13686
|
constructor(projectRoot) {
|
|
@@ -13325,6 +13694,10 @@ class FeatureService {
|
|
|
13325
13694
|
ensureDir(featurePath);
|
|
13326
13695
|
ensureDir(getContextPath(this.projectRoot, name));
|
|
13327
13696
|
ensureDir(getTasksPath(this.projectRoot, name));
|
|
13697
|
+
const journalPath = getJournalPath(this.projectRoot);
|
|
13698
|
+
if (!fileExists(journalPath)) {
|
|
13699
|
+
fs3.writeFileSync(journalPath, JOURNAL_TEMPLATE);
|
|
13700
|
+
}
|
|
13328
13701
|
const feature = {
|
|
13329
13702
|
name,
|
|
13330
13703
|
status: "planning",
|
|
@@ -13343,6 +13716,16 @@ class FeatureService {
|
|
|
13343
13716
|
return [];
|
|
13344
13717
|
return fs3.readdirSync(featuresPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
13345
13718
|
}
|
|
13719
|
+
getActive() {
|
|
13720
|
+
const features = this.list();
|
|
13721
|
+
for (const name of features) {
|
|
13722
|
+
const feature = this.get(name);
|
|
13723
|
+
if (feature && feature.status !== "completed") {
|
|
13724
|
+
return feature;
|
|
13725
|
+
}
|
|
13726
|
+
}
|
|
13727
|
+
return null;
|
|
13728
|
+
}
|
|
13346
13729
|
updateStatus(name, status) {
|
|
13347
13730
|
const feature = this.get(name);
|
|
13348
13731
|
if (!feature)
|
|
@@ -13422,6 +13805,7 @@ class PlanService {
|
|
|
13422
13805
|
const planPath = getPlanPath(this.projectRoot, featureName);
|
|
13423
13806
|
writeText(planPath, content);
|
|
13424
13807
|
this.clearComments(featureName);
|
|
13808
|
+
this.revokeApproval(featureName);
|
|
13425
13809
|
return planPath;
|
|
13426
13810
|
}
|
|
13427
13811
|
read(featureName) {
|
|
@@ -13429,25 +13813,45 @@ class PlanService {
|
|
|
13429
13813
|
const content = readText(planPath);
|
|
13430
13814
|
if (content === null)
|
|
13431
13815
|
return null;
|
|
13432
|
-
const feature = readJson(getFeatureJsonPath(this.projectRoot, featureName));
|
|
13433
13816
|
const comments = this.getComments(featureName);
|
|
13817
|
+
const isApproved = this.isApproved(featureName);
|
|
13434
13818
|
return {
|
|
13435
13819
|
content,
|
|
13436
|
-
status:
|
|
13820
|
+
status: isApproved ? "approved" : "planning",
|
|
13437
13821
|
comments
|
|
13438
13822
|
};
|
|
13439
13823
|
}
|
|
13440
13824
|
approve(featureName) {
|
|
13441
|
-
const featurePath = getFeatureJsonPath(this.projectRoot, featureName);
|
|
13442
|
-
const feature = readJson(featurePath);
|
|
13443
|
-
if (!feature)
|
|
13444
|
-
throw new Error(`Feature '${featureName}' not found`);
|
|
13445
13825
|
if (!fileExists(getPlanPath(this.projectRoot, featureName))) {
|
|
13446
13826
|
throw new Error(`No plan.md found for feature '${featureName}'`);
|
|
13447
13827
|
}
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13828
|
+
const approvedPath = getApprovedPath(this.projectRoot, featureName);
|
|
13829
|
+
const timestamp = new Date().toISOString();
|
|
13830
|
+
fs4.writeFileSync(approvedPath, `Approved at ${timestamp}
|
|
13831
|
+
`);
|
|
13832
|
+
const featurePath = getFeatureJsonPath(this.projectRoot, featureName);
|
|
13833
|
+
const feature = readJson(featurePath);
|
|
13834
|
+
if (feature) {
|
|
13835
|
+
feature.status = "approved";
|
|
13836
|
+
feature.approvedAt = timestamp;
|
|
13837
|
+
writeJson(featurePath, feature);
|
|
13838
|
+
}
|
|
13839
|
+
}
|
|
13840
|
+
isApproved(featureName) {
|
|
13841
|
+
return fileExists(getApprovedPath(this.projectRoot, featureName));
|
|
13842
|
+
}
|
|
13843
|
+
revokeApproval(featureName) {
|
|
13844
|
+
const approvedPath = getApprovedPath(this.projectRoot, featureName);
|
|
13845
|
+
if (fileExists(approvedPath)) {
|
|
13846
|
+
fs4.unlinkSync(approvedPath);
|
|
13847
|
+
}
|
|
13848
|
+
const featurePath = getFeatureJsonPath(this.projectRoot, featureName);
|
|
13849
|
+
const feature = readJson(featurePath);
|
|
13850
|
+
if (feature && feature.status === "approved") {
|
|
13851
|
+
feature.status = "planning";
|
|
13852
|
+
delete feature.approvedAt;
|
|
13853
|
+
writeJson(featurePath, feature);
|
|
13854
|
+
}
|
|
13451
13855
|
}
|
|
13452
13856
|
getComments(featureName) {
|
|
13453
13857
|
const commentsPath = getCommentsPath(this.projectRoot, featureName);
|
|
@@ -13629,12 +14033,12 @@ class TaskService {
|
|
|
13629
14033
|
const tasksPath = getTasksPath(this.projectRoot, featureName);
|
|
13630
14034
|
if (!fileExists(tasksPath))
|
|
13631
14035
|
return [];
|
|
13632
|
-
return
|
|
14036
|
+
return fs5.readdirSync(tasksPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
13633
14037
|
}
|
|
13634
14038
|
deleteTask(featureName, taskFolder) {
|
|
13635
14039
|
const taskPath = getTaskPath(this.projectRoot, featureName, taskFolder);
|
|
13636
14040
|
if (fileExists(taskPath)) {
|
|
13637
|
-
|
|
14041
|
+
fs5.rmSync(taskPath, { recursive: true });
|
|
13638
14042
|
}
|
|
13639
14043
|
}
|
|
13640
14044
|
getNextOrder(existingFolders) {
|
|
@@ -13774,7 +14178,7 @@ _Add detailed instructions here_
|
|
|
13774
14178
|
}
|
|
13775
14179
|
const subtaskPath = getSubtaskPath(this.projectRoot, featureName, taskFolder, subtaskFolder);
|
|
13776
14180
|
if (fileExists(subtaskPath)) {
|
|
13777
|
-
|
|
14181
|
+
fs5.rmSync(subtaskPath, { recursive: true });
|
|
13778
14182
|
}
|
|
13779
14183
|
}
|
|
13780
14184
|
getSubtask(featureName, taskFolder, subtaskId) {
|
|
@@ -13834,7 +14238,7 @@ _Add detailed instructions here_
|
|
|
13834
14238
|
const subtasksPath = getSubtasksPath(this.projectRoot, featureName, taskFolder);
|
|
13835
14239
|
if (!fileExists(subtasksPath))
|
|
13836
14240
|
return [];
|
|
13837
|
-
return
|
|
14241
|
+
return fs5.readdirSync(subtasksPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
13838
14242
|
}
|
|
13839
14243
|
findSubtaskFolder(featureName, taskFolder, subtaskId) {
|
|
13840
14244
|
const folders = this.listSubtaskFolders(featureName, taskFolder);
|
|
@@ -13845,212 +14249,48 @@ _Add detailed instructions here_
|
|
|
13845
14249
|
return name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
13846
14250
|
}
|
|
13847
14251
|
}
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
13851
|
-
|
|
13852
|
-
|
|
14252
|
+
var import_file_exists = __toESM(require_dist(), 1);
|
|
14253
|
+
var import_debug = __toESM(require_src(), 1);
|
|
14254
|
+
var import_promise_deferred = __toESM(require_dist2(), 1);
|
|
14255
|
+
var import_promise_deferred2 = __toESM(require_dist2(), 1);
|
|
14256
|
+
var __defProp22 = Object.defineProperty;
|
|
14257
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
14258
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
14259
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
14260
|
+
var __esm = (fn, res) => function __init() {
|
|
14261
|
+
return fn && (res = (0, fn[__getOwnPropNames2(fn)[0]])(fn = 0)), res;
|
|
14262
|
+
};
|
|
14263
|
+
var __commonJS2 = (cb, mod) => function __require() {
|
|
14264
|
+
return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
14265
|
+
};
|
|
14266
|
+
var __export2 = (target, all) => {
|
|
14267
|
+
for (var name in all)
|
|
14268
|
+
__defProp22(target, name, { get: all[name], enumerable: true });
|
|
14269
|
+
};
|
|
14270
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14271
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14272
|
+
for (let key of __getOwnPropNames2(from))
|
|
14273
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
14274
|
+
__defProp22(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13853
14275
|
}
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
13870
|
-
|
|
13871
|
-
|
|
13872
|
-
**Type:** ${type || "custom"}
|
|
13873
|
-
**ID:** ${subtaskId}
|
|
13874
|
-
|
|
13875
|
-
## Instructions
|
|
13876
|
-
|
|
13877
|
-
_Add detailed instructions here_
|
|
13878
|
-
`;
|
|
13879
|
-
writeText(getSubtaskSpecPath(this.projectRoot, featureName, taskFolder, folderName), specContent);
|
|
13880
|
-
return {
|
|
13881
|
-
id: subtaskId,
|
|
13882
|
-
name,
|
|
13883
|
-
folder: folderName,
|
|
13884
|
-
status: "pending",
|
|
13885
|
-
type,
|
|
13886
|
-
createdAt: subtaskStatus.createdAt
|
|
13887
|
-
};
|
|
13888
|
-
}
|
|
13889
|
-
update(featureName, taskFolder, subtaskId, status) {
|
|
13890
|
-
const subtaskFolder = this.findFolder(featureName, taskFolder, subtaskId);
|
|
13891
|
-
if (!subtaskFolder) {
|
|
13892
|
-
throw new Error(`Subtask '${subtaskId}' not found in task '${taskFolder}'`);
|
|
13893
|
-
}
|
|
13894
|
-
const statusPath = getSubtaskStatusPath(this.projectRoot, featureName, taskFolder, subtaskFolder);
|
|
13895
|
-
const current = readJson(statusPath);
|
|
13896
|
-
if (!current) {
|
|
13897
|
-
throw new Error(`Subtask status not found for '${subtaskId}'`);
|
|
13898
|
-
}
|
|
13899
|
-
const updated = { ...current, status };
|
|
13900
|
-
if (status === "done" && !current.completedAt) {
|
|
13901
|
-
updated.completedAt = new Date().toISOString();
|
|
13902
|
-
}
|
|
13903
|
-
writeJson(statusPath, updated);
|
|
13904
|
-
const name = subtaskFolder.replace(/^\d+-/, "");
|
|
13905
|
-
return {
|
|
13906
|
-
id: subtaskId,
|
|
13907
|
-
name,
|
|
13908
|
-
folder: subtaskFolder,
|
|
13909
|
-
status,
|
|
13910
|
-
type: current.type,
|
|
13911
|
-
createdAt: current.createdAt,
|
|
13912
|
-
completedAt: updated.completedAt
|
|
13913
|
-
};
|
|
13914
|
-
}
|
|
13915
|
-
list(featureName, taskFolder) {
|
|
13916
|
-
const folders = this.listFolders(featureName, taskFolder);
|
|
13917
|
-
const taskOrder = parseInt(taskFolder.split("-")[0], 10);
|
|
13918
|
-
return folders.map((folder, index) => {
|
|
13919
|
-
const statusPath = getSubtaskStatusPath(this.projectRoot, featureName, taskFolder, folder);
|
|
13920
|
-
const status = readJson(statusPath);
|
|
13921
|
-
const name = folder.replace(/^\d+-/, "");
|
|
13922
|
-
const subtaskOrder = parseInt(folder.split("-")[0], 10) || index + 1;
|
|
13923
|
-
return {
|
|
13924
|
-
id: `${taskOrder}.${subtaskOrder}`,
|
|
13925
|
-
name,
|
|
13926
|
-
folder,
|
|
13927
|
-
status: status?.status || "pending",
|
|
13928
|
-
type: status?.type,
|
|
13929
|
-
createdAt: status?.createdAt,
|
|
13930
|
-
completedAt: status?.completedAt
|
|
13931
|
-
};
|
|
13932
|
-
});
|
|
13933
|
-
}
|
|
13934
|
-
get(featureName, taskFolder, subtaskId) {
|
|
13935
|
-
const subtaskFolder = this.findFolder(featureName, taskFolder, subtaskId);
|
|
13936
|
-
if (!subtaskFolder)
|
|
13937
|
-
return null;
|
|
13938
|
-
const statusPath = getSubtaskStatusPath(this.projectRoot, featureName, taskFolder, subtaskFolder);
|
|
13939
|
-
const status = readJson(statusPath);
|
|
13940
|
-
if (!status)
|
|
13941
|
-
return null;
|
|
13942
|
-
const taskOrder = parseInt(taskFolder.split("-")[0], 10);
|
|
13943
|
-
const subtaskOrder = parseInt(subtaskFolder.split("-")[0], 10);
|
|
13944
|
-
const name = subtaskFolder.replace(/^\d+-/, "");
|
|
13945
|
-
return {
|
|
13946
|
-
id: `${taskOrder}.${subtaskOrder}`,
|
|
13947
|
-
name,
|
|
13948
|
-
folder: subtaskFolder,
|
|
13949
|
-
status: status.status,
|
|
13950
|
-
type: status.type,
|
|
13951
|
-
createdAt: status.createdAt,
|
|
13952
|
-
completedAt: status.completedAt
|
|
13953
|
-
};
|
|
13954
|
-
}
|
|
13955
|
-
writeSpec(featureName, taskFolder, subtaskId, content) {
|
|
13956
|
-
const subtaskFolder = this.findFolder(featureName, taskFolder, subtaskId);
|
|
13957
|
-
if (!subtaskFolder) {
|
|
13958
|
-
throw new Error(`Subtask '${subtaskId}' not found in task '${taskFolder}'`);
|
|
13959
|
-
}
|
|
13960
|
-
const specPath = getSubtaskSpecPath(this.projectRoot, featureName, taskFolder, subtaskFolder);
|
|
13961
|
-
writeText(specPath, content);
|
|
13962
|
-
return specPath;
|
|
13963
|
-
}
|
|
13964
|
-
writeReport(featureName, taskFolder, subtaskId, content) {
|
|
13965
|
-
const subtaskFolder = this.findFolder(featureName, taskFolder, subtaskId);
|
|
13966
|
-
if (!subtaskFolder) {
|
|
13967
|
-
throw new Error(`Subtask '${subtaskId}' not found in task '${taskFolder}'`);
|
|
13968
|
-
}
|
|
13969
|
-
const reportPath = getSubtaskReportPath(this.projectRoot, featureName, taskFolder, subtaskFolder);
|
|
13970
|
-
writeText(reportPath, content);
|
|
13971
|
-
return reportPath;
|
|
13972
|
-
}
|
|
13973
|
-
readSpec(featureName, taskFolder, subtaskId) {
|
|
13974
|
-
const subtaskFolder = this.findFolder(featureName, taskFolder, subtaskId);
|
|
13975
|
-
if (!subtaskFolder)
|
|
13976
|
-
return null;
|
|
13977
|
-
const specPath = getSubtaskSpecPath(this.projectRoot, featureName, taskFolder, subtaskFolder);
|
|
13978
|
-
return readText(specPath);
|
|
13979
|
-
}
|
|
13980
|
-
readReport(featureName, taskFolder, subtaskId) {
|
|
13981
|
-
const subtaskFolder = this.findFolder(featureName, taskFolder, subtaskId);
|
|
13982
|
-
if (!subtaskFolder)
|
|
13983
|
-
return null;
|
|
13984
|
-
const reportPath = getSubtaskReportPath(this.projectRoot, featureName, taskFolder, subtaskFolder);
|
|
13985
|
-
return readText(reportPath);
|
|
13986
|
-
}
|
|
13987
|
-
delete(featureName, taskFolder, subtaskId) {
|
|
13988
|
-
const subtaskFolder = this.findFolder(featureName, taskFolder, subtaskId);
|
|
13989
|
-
if (!subtaskFolder) {
|
|
13990
|
-
throw new Error(`Subtask '${subtaskId}' not found in task '${taskFolder}'`);
|
|
13991
|
-
}
|
|
13992
|
-
const subtaskPath = getSubtaskPath(this.projectRoot, featureName, taskFolder, subtaskFolder);
|
|
13993
|
-
if (fileExists(subtaskPath)) {
|
|
13994
|
-
fs5.rmSync(subtaskPath, { recursive: true });
|
|
13995
|
-
}
|
|
13996
|
-
}
|
|
13997
|
-
listFolders(featureName, taskFolder) {
|
|
13998
|
-
const subtasksPath = getSubtasksPath(this.projectRoot, featureName, taskFolder);
|
|
13999
|
-
if (!fileExists(subtasksPath))
|
|
14000
|
-
return [];
|
|
14001
|
-
return fs5.readdirSync(subtasksPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
14002
|
-
}
|
|
14003
|
-
findFolder(featureName, taskFolder, subtaskId) {
|
|
14004
|
-
const folders = this.listFolders(featureName, taskFolder);
|
|
14005
|
-
const subtaskOrder = subtaskId.split(".")[1];
|
|
14006
|
-
return folders.find((f) => f.startsWith(`${subtaskOrder}-`)) || null;
|
|
14007
|
-
}
|
|
14008
|
-
slugify(name) {
|
|
14009
|
-
return name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
14010
|
-
}
|
|
14011
|
-
}
|
|
14012
|
-
var import_file_exists = __toESM(require_dist(), 1);
|
|
14013
|
-
var import_debug = __toESM(require_src(), 1);
|
|
14014
|
-
var import_promise_deferred = __toESM(require_dist2(), 1);
|
|
14015
|
-
var import_promise_deferred2 = __toESM(require_dist2(), 1);
|
|
14016
|
-
var __defProp22 = Object.defineProperty;
|
|
14017
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
14018
|
-
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
14019
|
-
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
14020
|
-
var __esm = (fn, res) => function __init() {
|
|
14021
|
-
return fn && (res = (0, fn[__getOwnPropNames2(fn)[0]])(fn = 0)), res;
|
|
14022
|
-
};
|
|
14023
|
-
var __commonJS2 = (cb, mod) => function __require() {
|
|
14024
|
-
return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
14025
|
-
};
|
|
14026
|
-
var __export2 = (target, all) => {
|
|
14027
|
-
for (var name in all)
|
|
14028
|
-
__defProp22(target, name, { get: all[name], enumerable: true });
|
|
14029
|
-
};
|
|
14030
|
-
var __copyProps = (to, from, except, desc) => {
|
|
14031
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14032
|
-
for (let key of __getOwnPropNames2(from))
|
|
14033
|
-
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
14034
|
-
__defProp22(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14035
|
-
}
|
|
14036
|
-
return to;
|
|
14037
|
-
};
|
|
14038
|
-
var __toCommonJS = (mod) => __copyProps(__defProp22({}, "__esModule", { value: true }), mod);
|
|
14039
|
-
function pathspec(...paths) {
|
|
14040
|
-
const key = new String(paths);
|
|
14041
|
-
cache.set(key, paths);
|
|
14042
|
-
return key;
|
|
14043
|
-
}
|
|
14044
|
-
function isPathSpec(path32) {
|
|
14045
|
-
return path32 instanceof String && cache.has(path32);
|
|
14046
|
-
}
|
|
14047
|
-
function toPaths(pathSpec) {
|
|
14048
|
-
return cache.get(pathSpec) || [];
|
|
14049
|
-
}
|
|
14050
|
-
var cache;
|
|
14051
|
-
var init_pathspec = __esm({
|
|
14052
|
-
"src/lib/args/pathspec.ts"() {
|
|
14053
|
-
cache = /* @__PURE__ */ new WeakMap;
|
|
14276
|
+
return to;
|
|
14277
|
+
};
|
|
14278
|
+
var __toCommonJS = (mod) => __copyProps(__defProp22({}, "__esModule", { value: true }), mod);
|
|
14279
|
+
function pathspec(...paths) {
|
|
14280
|
+
const key = new String(paths);
|
|
14281
|
+
cache.set(key, paths);
|
|
14282
|
+
return key;
|
|
14283
|
+
}
|
|
14284
|
+
function isPathSpec(path32) {
|
|
14285
|
+
return path32 instanceof String && cache.has(path32);
|
|
14286
|
+
}
|
|
14287
|
+
function toPaths(pathSpec) {
|
|
14288
|
+
return cache.get(pathSpec) || [];
|
|
14289
|
+
}
|
|
14290
|
+
var cache;
|
|
14291
|
+
var init_pathspec = __esm({
|
|
14292
|
+
"src/lib/args/pathspec.ts"() {
|
|
14293
|
+
cache = /* @__PURE__ */ new WeakMap;
|
|
14054
14294
|
}
|
|
14055
14295
|
});
|
|
14056
14296
|
var GitError;
|
|
@@ -18028,7 +18268,7 @@ class WorktreeService {
|
|
|
18028
18268
|
const featurePath = path3.join(this.config.hiveDir, "features", feature);
|
|
18029
18269
|
const tasksPath = path3.join(featurePath, "tasks", step, "status.json");
|
|
18030
18270
|
try {
|
|
18031
|
-
await
|
|
18271
|
+
await fs7.access(tasksPath);
|
|
18032
18272
|
return tasksPath;
|
|
18033
18273
|
} catch {}
|
|
18034
18274
|
return path3.join(featurePath, "execution", step, "status.json");
|
|
@@ -18040,7 +18280,7 @@ class WorktreeService {
|
|
|
18040
18280
|
const worktreePath = this.getWorktreePath(feature, step);
|
|
18041
18281
|
const branchName = this.getBranchName(feature, step);
|
|
18042
18282
|
const git = this.getGit();
|
|
18043
|
-
await
|
|
18283
|
+
await fs7.mkdir(path3.dirname(worktreePath), { recursive: true });
|
|
18044
18284
|
const base = baseBranch || (await git.revparse(["HEAD"])).trim();
|
|
18045
18285
|
const existing = await this.get(feature, step);
|
|
18046
18286
|
if (existing) {
|
|
@@ -18069,7 +18309,7 @@ class WorktreeService {
|
|
|
18069
18309
|
const worktreePath = this.getWorktreePath(feature, step);
|
|
18070
18310
|
const branchName = this.getBranchName(feature, step);
|
|
18071
18311
|
try {
|
|
18072
|
-
await
|
|
18312
|
+
await fs7.access(worktreePath);
|
|
18073
18313
|
const worktreeGit = this.getGit(worktreePath);
|
|
18074
18314
|
const commit = (await worktreeGit.revparse(["HEAD"])).trim();
|
|
18075
18315
|
return {
|
|
@@ -18089,7 +18329,7 @@ class WorktreeService {
|
|
|
18089
18329
|
let base = baseCommit;
|
|
18090
18330
|
if (!base) {
|
|
18091
18331
|
try {
|
|
18092
|
-
const status = JSON.parse(await
|
|
18332
|
+
const status = JSON.parse(await fs7.readFile(statusPath, "utf-8"));
|
|
18093
18333
|
base = status.baseCommit;
|
|
18094
18334
|
} catch {}
|
|
18095
18335
|
}
|
|
@@ -18139,7 +18379,7 @@ class WorktreeService {
|
|
|
18139
18379
|
const base = baseBranch || "HEAD~1";
|
|
18140
18380
|
const worktreeGit = this.getGit(worktreePath);
|
|
18141
18381
|
const diff = await worktreeGit.diff([`${base}...HEAD`]);
|
|
18142
|
-
await
|
|
18382
|
+
await fs7.writeFile(patchPath, diff);
|
|
18143
18383
|
return patchPath;
|
|
18144
18384
|
}
|
|
18145
18385
|
async applyDiff(feature, step, baseBranch) {
|
|
@@ -18149,13 +18389,13 @@ class WorktreeService {
|
|
|
18149
18389
|
}
|
|
18150
18390
|
const patchPath = path3.join(this.config.hiveDir, ".worktrees", feature, `${step}.patch`);
|
|
18151
18391
|
try {
|
|
18152
|
-
await
|
|
18392
|
+
await fs7.writeFile(patchPath, diffContent);
|
|
18153
18393
|
const git = this.getGit();
|
|
18154
18394
|
await git.applyPatch(patchPath);
|
|
18155
|
-
await
|
|
18395
|
+
await fs7.unlink(patchPath).catch(() => {});
|
|
18156
18396
|
return { success: true, filesAffected: filesChanged };
|
|
18157
18397
|
} catch (error45) {
|
|
18158
|
-
await
|
|
18398
|
+
await fs7.unlink(patchPath).catch(() => {});
|
|
18159
18399
|
const err = error45;
|
|
18160
18400
|
return {
|
|
18161
18401
|
success: false,
|
|
@@ -18171,13 +18411,13 @@ class WorktreeService {
|
|
|
18171
18411
|
}
|
|
18172
18412
|
const patchPath = path3.join(this.config.hiveDir, ".worktrees", feature, `${step}.patch`);
|
|
18173
18413
|
try {
|
|
18174
|
-
await
|
|
18414
|
+
await fs7.writeFile(patchPath, diffContent);
|
|
18175
18415
|
const git = this.getGit();
|
|
18176
18416
|
await git.applyPatch(patchPath, ["-R"]);
|
|
18177
|
-
await
|
|
18417
|
+
await fs7.unlink(patchPath).catch(() => {});
|
|
18178
18418
|
return { success: true, filesAffected: filesChanged };
|
|
18179
18419
|
} catch (error45) {
|
|
18180
|
-
await
|
|
18420
|
+
await fs7.unlink(patchPath).catch(() => {});
|
|
18181
18421
|
const err = error45;
|
|
18182
18422
|
return {
|
|
18183
18423
|
success: false,
|
|
@@ -18196,7 +18436,7 @@ class WorktreeService {
|
|
|
18196
18436
|
return [...new Set(files)];
|
|
18197
18437
|
}
|
|
18198
18438
|
async revertFromSavedDiff(diffPath) {
|
|
18199
|
-
const diffContent = await
|
|
18439
|
+
const diffContent = await fs7.readFile(diffPath, "utf-8");
|
|
18200
18440
|
if (!diffContent.trim()) {
|
|
18201
18441
|
return { success: true, filesAffected: [] };
|
|
18202
18442
|
}
|
|
@@ -18221,7 +18461,7 @@ class WorktreeService {
|
|
|
18221
18461
|
try {
|
|
18222
18462
|
await git.raw(["worktree", "remove", worktreePath, "--force"]);
|
|
18223
18463
|
} catch {
|
|
18224
|
-
await
|
|
18464
|
+
await fs7.rm(worktreePath, { recursive: true, force: true });
|
|
18225
18465
|
}
|
|
18226
18466
|
try {
|
|
18227
18467
|
await git.raw(["worktree", "prune"]);
|
|
@@ -18236,13 +18476,13 @@ class WorktreeService {
|
|
|
18236
18476
|
const worktreesDir = this.getWorktreesDir();
|
|
18237
18477
|
const results = [];
|
|
18238
18478
|
try {
|
|
18239
|
-
const features = feature ? [feature] : await
|
|
18479
|
+
const features = feature ? [feature] : await fs7.readdir(worktreesDir);
|
|
18240
18480
|
for (const feat of features) {
|
|
18241
18481
|
const featurePath = path3.join(worktreesDir, feat);
|
|
18242
|
-
const stat2 = await
|
|
18482
|
+
const stat2 = await fs7.stat(featurePath).catch(() => null);
|
|
18243
18483
|
if (!stat2?.isDirectory())
|
|
18244
18484
|
continue;
|
|
18245
|
-
const steps = await
|
|
18485
|
+
const steps = await fs7.readdir(featurePath).catch(() => []);
|
|
18246
18486
|
for (const step of steps) {
|
|
18247
18487
|
const info = await this.get(feat, step);
|
|
18248
18488
|
if (info) {
|
|
@@ -18260,16 +18500,16 @@ class WorktreeService {
|
|
|
18260
18500
|
await git.raw(["worktree", "prune"]);
|
|
18261
18501
|
} catch {}
|
|
18262
18502
|
const worktreesDir = this.getWorktreesDir();
|
|
18263
|
-
const features = feature ? [feature] : await
|
|
18503
|
+
const features = feature ? [feature] : await fs7.readdir(worktreesDir).catch(() => []);
|
|
18264
18504
|
for (const feat of features) {
|
|
18265
18505
|
const featurePath = path3.join(worktreesDir, feat);
|
|
18266
|
-
const stat2 = await
|
|
18506
|
+
const stat2 = await fs7.stat(featurePath).catch(() => null);
|
|
18267
18507
|
if (!stat2?.isDirectory())
|
|
18268
18508
|
continue;
|
|
18269
|
-
const steps = await
|
|
18509
|
+
const steps = await fs7.readdir(featurePath).catch(() => []);
|
|
18270
18510
|
for (const step of steps) {
|
|
18271
18511
|
const worktreePath = path3.join(featurePath, step);
|
|
18272
|
-
const stepStat = await
|
|
18512
|
+
const stepStat = await fs7.stat(worktreePath).catch(() => null);
|
|
18273
18513
|
if (!stepStat?.isDirectory())
|
|
18274
18514
|
continue;
|
|
18275
18515
|
try {
|
|
@@ -18290,13 +18530,13 @@ class WorktreeService {
|
|
|
18290
18530
|
}
|
|
18291
18531
|
const patchPath = path3.join(this.config.hiveDir, ".worktrees", feature, `${step}-check.patch`);
|
|
18292
18532
|
try {
|
|
18293
|
-
await
|
|
18533
|
+
await fs7.writeFile(patchPath, diffContent);
|
|
18294
18534
|
const git = this.getGit();
|
|
18295
18535
|
await git.applyPatch(patchPath, ["--check"]);
|
|
18296
|
-
await
|
|
18536
|
+
await fs7.unlink(patchPath).catch(() => {});
|
|
18297
18537
|
return [];
|
|
18298
18538
|
} catch (error45) {
|
|
18299
|
-
await
|
|
18539
|
+
await fs7.unlink(patchPath).catch(() => {});
|
|
18300
18540
|
const err = error45;
|
|
18301
18541
|
const stderr = err.message || "";
|
|
18302
18542
|
const conflicts2 = stderr.split(`
|
|
@@ -18309,7 +18549,7 @@ class WorktreeService {
|
|
|
18309
18549
|
}
|
|
18310
18550
|
async checkConflictsFromSavedDiff(diffPath, reverse = false) {
|
|
18311
18551
|
try {
|
|
18312
|
-
await
|
|
18552
|
+
await fs7.access(diffPath);
|
|
18313
18553
|
} catch {
|
|
18314
18554
|
return [];
|
|
18315
18555
|
}
|
|
@@ -18332,7 +18572,7 @@ class WorktreeService {
|
|
|
18332
18572
|
async commitChanges(feature, step, message) {
|
|
18333
18573
|
const worktreePath = this.getWorktreePath(feature, step);
|
|
18334
18574
|
try {
|
|
18335
|
-
await
|
|
18575
|
+
await fs7.access(worktreePath);
|
|
18336
18576
|
} catch {
|
|
18337
18577
|
return { committed: false, sha: "", message: "Worktree not found" };
|
|
18338
18578
|
}
|
|
@@ -18472,10 +18712,10 @@ class ContextService {
|
|
|
18472
18712
|
const contextPath = getContextPath(this.projectRoot, featureName);
|
|
18473
18713
|
if (!fileExists(contextPath))
|
|
18474
18714
|
return [];
|
|
18475
|
-
const files =
|
|
18715
|
+
const files = fs8.readdirSync(contextPath, { withFileTypes: true }).filter((f) => f.isFile() && f.name.endsWith(".md")).map((f) => f.name);
|
|
18476
18716
|
return files.map((name) => {
|
|
18477
18717
|
const filePath = path4.join(contextPath, name);
|
|
18478
|
-
const stat2 =
|
|
18718
|
+
const stat2 = fs8.statSync(filePath);
|
|
18479
18719
|
const content = readText(filePath) || "";
|
|
18480
18720
|
return {
|
|
18481
18721
|
name: name.replace(/\.md$/, ""),
|
|
@@ -18488,7 +18728,7 @@ class ContextService {
|
|
|
18488
18728
|
const contextPath = getContextPath(this.projectRoot, featureName);
|
|
18489
18729
|
const filePath = path4.join(contextPath, this.normalizeFileName(fileName));
|
|
18490
18730
|
if (fileExists(filePath)) {
|
|
18491
|
-
|
|
18731
|
+
fs8.unlinkSync(filePath);
|
|
18492
18732
|
return true;
|
|
18493
18733
|
}
|
|
18494
18734
|
return false;
|
|
@@ -18511,140 +18751,502 @@ ${f.content}`);
|
|
|
18511
18751
|
return `${normalized}.md`;
|
|
18512
18752
|
}
|
|
18513
18753
|
}
|
|
18514
|
-
|
|
18515
|
-
|
|
18516
|
-
|
|
18517
|
-
|
|
18518
|
-
|
|
18754
|
+
class ConfigService {
|
|
18755
|
+
configPath;
|
|
18756
|
+
constructor() {
|
|
18757
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
18758
|
+
const configDir = path6.join(homeDir, ".config", "opencode");
|
|
18759
|
+
this.configPath = path6.join(configDir, "agent_hive.json");
|
|
18519
18760
|
}
|
|
18520
|
-
|
|
18521
|
-
return
|
|
18522
|
-
}
|
|
18523
|
-
|
|
18524
|
-
|
|
18525
|
-
|
|
18526
|
-
|
|
18527
|
-
|
|
18528
|
-
|
|
18529
|
-
|
|
18530
|
-
|
|
18531
|
-
|
|
18532
|
-
|
|
18533
|
-
|
|
18534
|
-
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
18538
|
-
|
|
18539
|
-
|
|
18540
|
-
|
|
18541
|
-
session = {
|
|
18542
|
-
sessionId,
|
|
18543
|
-
taskFolder,
|
|
18544
|
-
startedAt: now,
|
|
18545
|
-
lastActiveAt: now
|
|
18761
|
+
getPath() {
|
|
18762
|
+
return this.configPath;
|
|
18763
|
+
}
|
|
18764
|
+
get() {
|
|
18765
|
+
try {
|
|
18766
|
+
if (!fs10.existsSync(this.configPath)) {
|
|
18767
|
+
return { ...DEFAULT_HIVE_CONFIG };
|
|
18768
|
+
}
|
|
18769
|
+
const raw = fs10.readFileSync(this.configPath, "utf-8");
|
|
18770
|
+
const stored = JSON.parse(raw);
|
|
18771
|
+
return {
|
|
18772
|
+
...DEFAULT_HIVE_CONFIG,
|
|
18773
|
+
...stored,
|
|
18774
|
+
agents: {
|
|
18775
|
+
...DEFAULT_HIVE_CONFIG.agents,
|
|
18776
|
+
...stored.agents
|
|
18777
|
+
},
|
|
18778
|
+
omoSlim: {
|
|
18779
|
+
...DEFAULT_HIVE_CONFIG.omoSlim,
|
|
18780
|
+
...stored.omoSlim
|
|
18781
|
+
}
|
|
18546
18782
|
};
|
|
18547
|
-
|
|
18548
|
-
|
|
18549
|
-
if (!data.master) {
|
|
18550
|
-
data.master = sessionId;
|
|
18783
|
+
} catch {
|
|
18784
|
+
return { ...DEFAULT_HIVE_CONFIG };
|
|
18551
18785
|
}
|
|
18552
|
-
this.saveSessions(featureName, data);
|
|
18553
|
-
return session;
|
|
18554
|
-
}
|
|
18555
|
-
setMaster(featureName, sessionId) {
|
|
18556
|
-
const data = this.getSessions(featureName);
|
|
18557
|
-
data.master = sessionId;
|
|
18558
|
-
this.saveSessions(featureName, data);
|
|
18559
18786
|
}
|
|
18560
|
-
|
|
18561
|
-
|
|
18787
|
+
set(updates) {
|
|
18788
|
+
const current = this.get();
|
|
18789
|
+
const merged = {
|
|
18790
|
+
...current,
|
|
18791
|
+
...updates,
|
|
18792
|
+
agents: updates.agents ? {
|
|
18793
|
+
...current.agents,
|
|
18794
|
+
...updates.agents
|
|
18795
|
+
} : current.agents,
|
|
18796
|
+
omoSlim: updates.omoSlim ? {
|
|
18797
|
+
...current.omoSlim,
|
|
18798
|
+
...updates.omoSlim
|
|
18799
|
+
} : current.omoSlim
|
|
18800
|
+
};
|
|
18801
|
+
const configDir = path6.dirname(this.configPath);
|
|
18802
|
+
if (!fs10.existsSync(configDir)) {
|
|
18803
|
+
fs10.mkdirSync(configDir, { recursive: true });
|
|
18804
|
+
}
|
|
18805
|
+
fs10.writeFileSync(this.configPath, JSON.stringify(merged, null, 2));
|
|
18806
|
+
return merged;
|
|
18562
18807
|
}
|
|
18563
|
-
|
|
18564
|
-
return this.
|
|
18808
|
+
exists() {
|
|
18809
|
+
return fs10.existsSync(this.configPath);
|
|
18565
18810
|
}
|
|
18566
|
-
|
|
18567
|
-
|
|
18811
|
+
init() {
|
|
18812
|
+
if (!this.exists()) {
|
|
18813
|
+
return this.set(DEFAULT_HIVE_CONFIG);
|
|
18814
|
+
}
|
|
18815
|
+
return this.get();
|
|
18568
18816
|
}
|
|
18569
|
-
|
|
18570
|
-
return this.
|
|
18817
|
+
isOmoSlimEnabled() {
|
|
18818
|
+
return this.get().omoSlim?.enabled ?? false;
|
|
18571
18819
|
}
|
|
18572
|
-
|
|
18573
|
-
|
|
18574
|
-
const index = data.sessions.findIndex((s) => s.sessionId === sessionId);
|
|
18575
|
-
if (index === -1)
|
|
18576
|
-
return false;
|
|
18577
|
-
data.sessions.splice(index, 1);
|
|
18578
|
-
if (data.master === sessionId) {
|
|
18579
|
-
data.master = data.sessions[0]?.sessionId;
|
|
18580
|
-
}
|
|
18581
|
-
this.saveSessions(featureName, data);
|
|
18582
|
-
return true;
|
|
18820
|
+
setOmoSlim(enabled) {
|
|
18821
|
+
return this.set({ omoSlim: { enabled } });
|
|
18583
18822
|
}
|
|
18584
|
-
|
|
18585
|
-
|
|
18586
|
-
|
|
18587
|
-
|
|
18588
|
-
|
|
18589
|
-
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
|
|
18593
|
-
|
|
18594
|
-
|
|
18595
|
-
|
|
18596
|
-
|
|
18597
|
-
|
|
18598
|
-
|
|
18823
|
+
}
|
|
18824
|
+
|
|
18825
|
+
// src/utils/agent-selector.ts
|
|
18826
|
+
var AGENT_PATTERNS = [
|
|
18827
|
+
{ pattern: /\b(find|search|locate|where|grep|explore|scan|codebase)\b/i, agent: "explorer" },
|
|
18828
|
+
{ pattern: /\b(research|investigate|learn|docs?|documentation|library|api|external|github)\b/i, agent: "librarian" },
|
|
18829
|
+
{ pattern: /\b(ui|ux|component|frontend|react|vue|svelte|css|style|layout|design|button|form|modal|visual|responsive)\b/i, agent: "designer" },
|
|
18830
|
+
{ pattern: /\b(architect|decision|tradeoff|approach|strategy|choose|decide|compare|review|debug|complex)\b/i, agent: "oracle" }
|
|
18831
|
+
];
|
|
18832
|
+
function selectAgent(taskName, spec) {
|
|
18833
|
+
const content = `${taskName} ${spec}`.toLowerCase();
|
|
18834
|
+
for (const { pattern, agent } of AGENT_PATTERNS) {
|
|
18835
|
+
if (pattern.test(content)) {
|
|
18836
|
+
return agent;
|
|
18837
|
+
}
|
|
18838
|
+
}
|
|
18839
|
+
return "fixer";
|
|
18840
|
+
}
|
|
18841
|
+
|
|
18842
|
+
// src/utils/worker-prompt.ts
|
|
18843
|
+
function buildWorkerPrompt(params) {
|
|
18844
|
+
const {
|
|
18845
|
+
feature,
|
|
18846
|
+
task,
|
|
18847
|
+
taskOrder,
|
|
18848
|
+
worktreePath,
|
|
18849
|
+
branch,
|
|
18850
|
+
plan,
|
|
18851
|
+
contextFiles,
|
|
18852
|
+
spec,
|
|
18853
|
+
previousTasks,
|
|
18854
|
+
continueFrom
|
|
18855
|
+
} = params;
|
|
18856
|
+
const contextSection = contextFiles.length > 0 ? contextFiles.map((f) => `### ${f.name}
|
|
18857
|
+
${f.content}`).join(`
|
|
18858
|
+
|
|
18859
|
+
`) : "_No context files available._";
|
|
18860
|
+
const previousSection = previousTasks?.length ? previousTasks.map((t) => `- **${t.name}**: ${t.summary}`).join(`
|
|
18861
|
+
`) : "_This is the first task._";
|
|
18862
|
+
const continuationSection = continueFrom ? `
|
|
18863
|
+
## Continuation from Blocked State
|
|
18864
|
+
|
|
18865
|
+
Previous worker was blocked and exited. Here's the context:
|
|
18866
|
+
|
|
18867
|
+
**Previous Progress**: ${continueFrom.previousSummary}
|
|
18868
|
+
|
|
18869
|
+
**User Decision**: ${continueFrom.decision}
|
|
18870
|
+
|
|
18871
|
+
Continue from where the previous worker left off, incorporating the user's decision.
|
|
18872
|
+
The worktree already contains the previous worker's progress.
|
|
18873
|
+
` : "";
|
|
18874
|
+
return `# Hive Worker Assignment
|
|
18875
|
+
|
|
18876
|
+
You are a worker agent executing a task in an isolated git worktree.
|
|
18877
|
+
|
|
18878
|
+
## Assignment Details
|
|
18879
|
+
|
|
18880
|
+
| Field | Value |
|
|
18881
|
+
|-------|-------|
|
|
18882
|
+
| Feature | ${feature} |
|
|
18883
|
+
| Task | ${task} |
|
|
18884
|
+
| Task # | ${taskOrder} |
|
|
18885
|
+
| Branch | ${branch} |
|
|
18886
|
+
| Worktree | ${worktreePath} |
|
|
18887
|
+
|
|
18888
|
+
**CRITICAL**: All file operations MUST be within this worktree path:
|
|
18889
|
+
\`${worktreePath}\`
|
|
18890
|
+
|
|
18891
|
+
Do NOT modify files outside this directory.
|
|
18892
|
+
${continuationSection}
|
|
18893
|
+
---
|
|
18894
|
+
|
|
18895
|
+
## Plan Context
|
|
18896
|
+
|
|
18897
|
+
${plan}
|
|
18898
|
+
|
|
18899
|
+
---
|
|
18900
|
+
|
|
18901
|
+
## Context Files (Royal Jelly)
|
|
18902
|
+
|
|
18903
|
+
${contextSection}
|
|
18904
|
+
|
|
18905
|
+
---
|
|
18906
|
+
|
|
18907
|
+
## Previous Tasks Completed
|
|
18908
|
+
|
|
18909
|
+
${previousSection}
|
|
18910
|
+
|
|
18911
|
+
---
|
|
18912
|
+
|
|
18913
|
+
## Your Mission
|
|
18914
|
+
|
|
18915
|
+
${spec}
|
|
18916
|
+
|
|
18917
|
+
---
|
|
18918
|
+
|
|
18919
|
+
## Blocker Protocol
|
|
18920
|
+
|
|
18921
|
+
If you hit a blocker requiring human decision, **DO NOT** use the question tool directly.
|
|
18922
|
+
Instead, escalate via the blocker protocol:
|
|
18923
|
+
|
|
18924
|
+
1. **Save your progress** to the worktree (commit if appropriate)
|
|
18925
|
+
2. **Call hive_exec_complete** with blocker info:
|
|
18926
|
+
|
|
18927
|
+
\`\`\`
|
|
18928
|
+
hive_exec_complete({
|
|
18929
|
+
task: "${task}",
|
|
18930
|
+
feature: "${feature}",
|
|
18931
|
+
status: "blocked",
|
|
18932
|
+
summary: "What you accomplished so far",
|
|
18933
|
+
blocker: {
|
|
18934
|
+
reason: "Why you're blocked - be specific",
|
|
18935
|
+
options: ["Option A", "Option B", "Option C"],
|
|
18936
|
+
recommendation: "Your suggested choice with reasoning",
|
|
18937
|
+
context: "Relevant background the user needs to decide"
|
|
18938
|
+
}
|
|
18939
|
+
})
|
|
18940
|
+
\`\`\`
|
|
18941
|
+
|
|
18942
|
+
**After calling hive_exec_complete with blocked status, STOP IMMEDIATELY.**
|
|
18943
|
+
|
|
18944
|
+
The Hive Master will:
|
|
18945
|
+
1. Receive your blocker info
|
|
18946
|
+
2. Ask the user via question()
|
|
18947
|
+
3. Spawn a NEW worker to continue with the decision
|
|
18948
|
+
|
|
18949
|
+
This keeps the user focused on ONE conversation (Hive Master) instead of multiple worker panes.
|
|
18950
|
+
|
|
18951
|
+
---
|
|
18952
|
+
|
|
18953
|
+
## Completion Protocol
|
|
18954
|
+
|
|
18955
|
+
When your task is **fully complete**:
|
|
18956
|
+
|
|
18957
|
+
\`\`\`
|
|
18958
|
+
hive_exec_complete({
|
|
18959
|
+
task: "${task}",
|
|
18960
|
+
feature: "${feature}",
|
|
18961
|
+
status: "completed",
|
|
18962
|
+
summary: "Concise summary of what you accomplished"
|
|
18963
|
+
})
|
|
18964
|
+
\`\`\`
|
|
18965
|
+
|
|
18966
|
+
**CRITICAL: After calling hive_exec_complete, you MUST STOP IMMEDIATELY.**
|
|
18967
|
+
Do NOT continue working. Do NOT respond further. Your session is DONE.
|
|
18968
|
+
The Hive Master will take over from here.
|
|
18969
|
+
|
|
18970
|
+
If you encounter an **unrecoverable error**:
|
|
18971
|
+
|
|
18972
|
+
\`\`\`
|
|
18973
|
+
hive_exec_complete({
|
|
18974
|
+
task: "${task}",
|
|
18975
|
+
feature: "${feature}",
|
|
18976
|
+
status: "failed",
|
|
18977
|
+
summary: "What went wrong and what was attempted"
|
|
18978
|
+
})
|
|
18979
|
+
\`\`\`
|
|
18980
|
+
|
|
18981
|
+
If you made **partial progress** but can't continue:
|
|
18982
|
+
|
|
18983
|
+
\`\`\`
|
|
18984
|
+
hive_exec_complete({
|
|
18985
|
+
task: "${task}",
|
|
18986
|
+
feature: "${feature}",
|
|
18987
|
+
status: "partial",
|
|
18988
|
+
summary: "What was completed and what remains"
|
|
18989
|
+
})
|
|
18990
|
+
\`\`\`
|
|
18991
|
+
|
|
18992
|
+
---
|
|
18993
|
+
|
|
18994
|
+
## TDD Protocol (Required)
|
|
18995
|
+
|
|
18996
|
+
1. **Red**: Write failing test first
|
|
18997
|
+
2. **Green**: Minimal code to pass
|
|
18998
|
+
3. **Refactor**: Clean up, keep tests green
|
|
18999
|
+
|
|
19000
|
+
Never write implementation before test exists.
|
|
19001
|
+
Exception: Pure refactoring of existing tested code.
|
|
19002
|
+
|
|
19003
|
+
## Debugging Protocol (When stuck)
|
|
19004
|
+
|
|
19005
|
+
1. **Reproduce**: Get consistent failure
|
|
19006
|
+
2. **Isolate**: Binary search to find cause
|
|
19007
|
+
3. **Hypothesize**: Form theory, test it
|
|
19008
|
+
4. **Fix**: Minimal change that resolves
|
|
19009
|
+
|
|
19010
|
+
After 3 failed attempts at same fix: STOP and report blocker.
|
|
19011
|
+
|
|
19012
|
+
---
|
|
19013
|
+
|
|
19014
|
+
## Tool Access
|
|
19015
|
+
|
|
19016
|
+
**You have access to:**
|
|
19017
|
+
- All standard tools (read, write, edit, bash, glob, grep)
|
|
19018
|
+
- \`hive_exec_complete\` - Signal task done/blocked/failed
|
|
19019
|
+
- \`hive_exec_abort\` - Abort and discard changes
|
|
19020
|
+
- \`hive_plan_read\` - Re-read plan if needed
|
|
19021
|
+
- \`hive_context_write\` - Save learnings for future tasks
|
|
19022
|
+
|
|
19023
|
+
**You do NOT have access to (or should not use):**
|
|
19024
|
+
- \`question\` - Escalate via blocker protocol instead
|
|
19025
|
+
- \`hive_exec_start\` - No spawning sub-workers
|
|
19026
|
+
- \`hive_merge\` - Only Hive Master merges
|
|
19027
|
+
- \`background_task\` - No recursive delegation
|
|
19028
|
+
|
|
19029
|
+
---
|
|
19030
|
+
|
|
19031
|
+
## Guidelines
|
|
19032
|
+
|
|
19033
|
+
1. **Work methodically** - Break down the mission into steps
|
|
19034
|
+
2. **Stay in scope** - Only do what the spec asks
|
|
19035
|
+
3. **Escalate blockers** - Don't guess on important decisions
|
|
19036
|
+
4. **Save context** - Use hive_context_write for discoveries
|
|
19037
|
+
5. **Complete cleanly** - Always call hive_exec_complete when done
|
|
19038
|
+
|
|
19039
|
+
---
|
|
19040
|
+
|
|
19041
|
+
Begin your task now.
|
|
19042
|
+
`;
|
|
19043
|
+
}
|
|
19044
|
+
|
|
19045
|
+
// src/agents/hive.ts
|
|
19046
|
+
var HIVE_AGENT_BASE = `# Hive Agent
|
|
19047
|
+
|
|
19048
|
+
You are the Hive Master - a hybrid planner-orchestrator for structured feature development.
|
|
19049
|
+
|
|
19050
|
+
## Your Role
|
|
19051
|
+
|
|
19052
|
+
- **Plan** features via hive_plan_write (explicit, reviewable)
|
|
19053
|
+
- **Delegate** execution via hive_exec_start (workers in tmux panes)
|
|
19054
|
+
- **Ask questions** on behalf of blocked workers (single point of contact)
|
|
19055
|
+
- **Do simple work** directly if user explicitly asks
|
|
19056
|
+
|
|
19057
|
+
## Core Workflow
|
|
19058
|
+
|
|
19059
|
+
1. **Plan** - Create feature, write plan, get user approval
|
|
19060
|
+
2. **Execute** - Spawn workers for each task via hive_exec_start
|
|
19061
|
+
3. **Monitor** - Check progress with hive_worker_status
|
|
19062
|
+
4. **Handle blockers** - Workers exit with blocker info, you ask user and resume
|
|
19063
|
+
5. **Merge** - Integrate completed work via hive_merge
|
|
19064
|
+
|
|
19065
|
+
## When No Feature is Active
|
|
19066
|
+
|
|
19067
|
+
Work directly on user requests. You're a capable coding agent.
|
|
19068
|
+
Use hive_feature_create when the task is complex enough to benefit from structure.
|
|
19069
|
+
|
|
19070
|
+
Signs you should create a feature:
|
|
19071
|
+
- Multiple files to change
|
|
19072
|
+
- Task requires planning
|
|
19073
|
+
- Work should be reviewed before merging
|
|
19074
|
+
- User mentions "feature", "implement", or describes multi-step work
|
|
19075
|
+
|
|
19076
|
+
## When Feature is Active
|
|
19077
|
+
|
|
19078
|
+
Follow Hive workflow strictly:
|
|
19079
|
+
1. Write plan via hive_plan_write
|
|
19080
|
+
2. Wait for user to review and add comments
|
|
19081
|
+
3. Read comments via hive_plan_read, revise if needed
|
|
19082
|
+
4. Get approval (explicit or via hive_plan_approve)
|
|
19083
|
+
5. Generate tasks via hive_tasks_sync
|
|
19084
|
+
6. Execute tasks via hive_exec_start (spawns workers)
|
|
19085
|
+
7. Monitor workers via hive_worker_status
|
|
19086
|
+
8. Handle any blocked workers
|
|
19087
|
+
9. Merge completed work via hive_merge
|
|
19088
|
+
|
|
19089
|
+
## Blocker Handling Protocol
|
|
19090
|
+
|
|
19091
|
+
When a worker returns status: 'blocked':
|
|
19092
|
+
|
|
19093
|
+
1. **Read** the blocker info from hive_worker_status:
|
|
19094
|
+
- reason: Why they're blocked
|
|
19095
|
+
- options: Available choices
|
|
19096
|
+
- recommendation: Worker's suggestion
|
|
19097
|
+
|
|
19098
|
+
2. **Ask** the user via question():
|
|
19099
|
+
\`\`\`
|
|
19100
|
+
question({
|
|
19101
|
+
questions: [{
|
|
19102
|
+
header: "Decision Needed",
|
|
19103
|
+
question: "Worker blocked: {reason}. {recommendation}",
|
|
19104
|
+
options: [
|
|
19105
|
+
{ label: "Option A", description: "..." },
|
|
19106
|
+
{ label: "Option B", description: "..." }
|
|
19107
|
+
]
|
|
19108
|
+
}]
|
|
19109
|
+
})
|
|
19110
|
+
\`\`\`
|
|
19111
|
+
|
|
19112
|
+
3. **Resume** with the decision:
|
|
19113
|
+
\`\`\`
|
|
19114
|
+
hive_exec_start({
|
|
19115
|
+
task: "the-task",
|
|
19116
|
+
continueFrom: "blocked",
|
|
19117
|
+
decision: "User chose Option A because..."
|
|
19118
|
+
})
|
|
19119
|
+
\`\`\`
|
|
19120
|
+
|
|
19121
|
+
This keeps the user focused on ONE conversation (you) instead of multiple worker panes.
|
|
19122
|
+
|
|
19123
|
+
## Iron Laws
|
|
19124
|
+
|
|
19125
|
+
### Never
|
|
19126
|
+
- Plan without asking questions first (discovery required)
|
|
19127
|
+
- Code without failing test first
|
|
19128
|
+
- Complete without running verification
|
|
19129
|
+
- Assume when uncertain - ASK
|
|
19130
|
+
- Put raw data in Master context - DELEGATE
|
|
19131
|
+
- Attempt same fix more than 3 times
|
|
19132
|
+
|
|
19133
|
+
### Always
|
|
19134
|
+
- One question at a time (discovery)
|
|
19135
|
+
- Test -> Code -> Verify (TDD)
|
|
19136
|
+
- Delegate data queries to subagents
|
|
19137
|
+
- Stop and ask when blocked
|
|
19138
|
+
|
|
19139
|
+
## Communication Style
|
|
19140
|
+
|
|
19141
|
+
- Be concise, no preamble
|
|
19142
|
+
- Start work immediately
|
|
19143
|
+
- Challenge wrong approaches professionally
|
|
19144
|
+
- Don't summarize unless asked
|
|
19145
|
+
- Use hive tools proactively when in feature context
|
|
19146
|
+
`;
|
|
19147
|
+
function buildFeatureContextSection(ctx) {
|
|
19148
|
+
return `
|
|
19149
|
+
## Active Feature: ${ctx.name}
|
|
19150
|
+
|
|
19151
|
+
**Plan Status:** ${ctx.planStatus}
|
|
19152
|
+
**Tasks:** ${ctx.tasksSummary}
|
|
19153
|
+
**Context Files:** ${ctx.contextList.length > 0 ? ctx.contextList.join(", ") : "none"}
|
|
19154
|
+
|
|
19155
|
+
You are in feature context. Use Hive workflow.
|
|
19156
|
+
`;
|
|
19157
|
+
}
|
|
19158
|
+
var OOM_SLIM_SECTION = `
|
|
19159
|
+
## OMO-Slim Detected
|
|
19160
|
+
|
|
19161
|
+
Workers spawn in tmux panes with specialized agents:
|
|
19162
|
+
- **explorer** - Codebase search and pattern matching
|
|
19163
|
+
- **librarian** - External docs and library research
|
|
19164
|
+
- **oracle** - Architecture decisions and guidance
|
|
19165
|
+
- **designer** - UI/UX implementation
|
|
19166
|
+
- **fixer** - Default implementation (code changes)
|
|
19167
|
+
|
|
19168
|
+
Agent is auto-selected based on task content.
|
|
19169
|
+
Watch workers in tmux panes for real-time progress.
|
|
19170
|
+
`;
|
|
19171
|
+
function buildHiveAgentPrompt(featureContext, omoSlimDetected) {
|
|
19172
|
+
let prompt = HIVE_AGENT_BASE;
|
|
19173
|
+
if (featureContext) {
|
|
19174
|
+
prompt += buildFeatureContextSection(featureContext);
|
|
18599
19175
|
}
|
|
18600
|
-
|
|
18601
|
-
|
|
18602
|
-
const now = new Date().toISOString();
|
|
18603
|
-
const sourceSession = fromSessionId ? data.sessions.find((s) => s.sessionId === fromSessionId) : data.sessions.find((s) => s.sessionId === data.master);
|
|
18604
|
-
const newSessionId = `ses_fork_${Date.now()}`;
|
|
18605
|
-
const newSession = {
|
|
18606
|
-
sessionId: newSessionId,
|
|
18607
|
-
taskFolder: sourceSession?.taskFolder,
|
|
18608
|
-
startedAt: now,
|
|
18609
|
-
lastActiveAt: now
|
|
18610
|
-
};
|
|
18611
|
-
data.sessions.push(newSession);
|
|
18612
|
-
this.saveSessions(featureName, data);
|
|
18613
|
-
return newSession;
|
|
18614
|
-
}
|
|
18615
|
-
fresh(featureName, title) {
|
|
18616
|
-
const data = this.getSessions(featureName);
|
|
18617
|
-
const now = new Date().toISOString();
|
|
18618
|
-
const newSessionId = `ses_${title ? title.replace(/\s+/g, "_").toLowerCase() : Date.now()}`;
|
|
18619
|
-
const newSession = {
|
|
18620
|
-
sessionId: newSessionId,
|
|
18621
|
-
startedAt: now,
|
|
18622
|
-
lastActiveAt: now
|
|
18623
|
-
};
|
|
18624
|
-
data.sessions.push(newSession);
|
|
18625
|
-
this.saveSessions(featureName, data);
|
|
18626
|
-
return newSession;
|
|
19176
|
+
if (omoSlimDetected) {
|
|
19177
|
+
prompt += OOM_SLIM_SECTION;
|
|
18627
19178
|
}
|
|
19179
|
+
return prompt;
|
|
18628
19180
|
}
|
|
18629
19181
|
|
|
18630
19182
|
// src/index.ts
|
|
19183
|
+
function formatSkillsXml() {
|
|
19184
|
+
const skills = getBuiltinSkills();
|
|
19185
|
+
if (skills.length === 0)
|
|
19186
|
+
return "";
|
|
19187
|
+
const skillsXml = skills.map((skill) => {
|
|
19188
|
+
return [
|
|
19189
|
+
" <skill>",
|
|
19190
|
+
` <name>${skill.name}</name>`,
|
|
19191
|
+
` <description>(hive - Skill) ${skill.description}</description>`,
|
|
19192
|
+
" </skill>"
|
|
19193
|
+
].join(`
|
|
19194
|
+
`);
|
|
19195
|
+
}).join(`
|
|
19196
|
+
`);
|
|
19197
|
+
return `
|
|
19198
|
+
|
|
19199
|
+
<available_skills>
|
|
19200
|
+
${skillsXml}
|
|
19201
|
+
</available_skills>`;
|
|
19202
|
+
}
|
|
19203
|
+
function createHiveSkillTool() {
|
|
19204
|
+
const base = "Load a Hive skill to get detailed instructions for a specific workflow.";
|
|
19205
|
+
const skills = getBuiltinSkills();
|
|
19206
|
+
const description = skills.length === 0 ? base + `
|
|
19207
|
+
|
|
19208
|
+
No Hive skills available.` : base + formatSkillsXml();
|
|
19209
|
+
return tool({
|
|
19210
|
+
description,
|
|
19211
|
+
args: {
|
|
19212
|
+
name: tool.schema.string().describe("The skill name from available_skills")
|
|
19213
|
+
},
|
|
19214
|
+
async execute({ name }) {
|
|
19215
|
+
const result = loadBuiltinSkill(name);
|
|
19216
|
+
if (!result.found || !result.skill) {
|
|
19217
|
+
const available = skills.map((s) => s.name).join(", ");
|
|
19218
|
+
throw new Error(`Skill "${name}" not found. Available Hive skills: ${available || "none"}`);
|
|
19219
|
+
}
|
|
19220
|
+
const skill = result.skill;
|
|
19221
|
+
return [
|
|
19222
|
+
`## Hive Skill: ${skill.name}`,
|
|
19223
|
+
"",
|
|
19224
|
+
`**Description**: ${skill.description}`,
|
|
19225
|
+
"",
|
|
19226
|
+
skill.template
|
|
19227
|
+
].join(`
|
|
19228
|
+
`);
|
|
19229
|
+
}
|
|
19230
|
+
});
|
|
19231
|
+
}
|
|
18631
19232
|
var HIVE_SYSTEM_PROMPT = `
|
|
18632
19233
|
## Hive - Feature Development System
|
|
18633
19234
|
|
|
18634
19235
|
Plan-first development: Write plan → User reviews → Approve → Execute tasks
|
|
18635
19236
|
|
|
18636
|
-
### Tools (
|
|
19237
|
+
### Tools (19 total)
|
|
18637
19238
|
|
|
18638
19239
|
| Domain | Tools |
|
|
18639
19240
|
|--------|-------|
|
|
18640
19241
|
| Feature | hive_feature_create, hive_feature_list, hive_feature_complete |
|
|
18641
19242
|
| Plan | hive_plan_write, hive_plan_read, hive_plan_approve |
|
|
18642
19243
|
| Task | hive_tasks_sync, hive_task_create, hive_task_update |
|
|
18643
|
-
| Subtask | hive_subtask_create, hive_subtask_update, hive_subtask_list, hive_subtask_spec_write, hive_subtask_report_write |
|
|
18644
19244
|
| Exec | hive_exec_start, hive_exec_complete, hive_exec_abort |
|
|
19245
|
+
| Worker | hive_worker_status |
|
|
18645
19246
|
| Merge | hive_merge, hive_worktree_list |
|
|
18646
|
-
| Context | hive_context_write
|
|
18647
|
-
|
|
|
19247
|
+
| Context | hive_context_write |
|
|
19248
|
+
| Status | hive_status |
|
|
19249
|
+
| Skill | hive_skill |
|
|
18648
19250
|
|
|
18649
19251
|
### Workflow
|
|
18650
19252
|
|
|
@@ -18659,37 +19261,31 @@ Plan-first development: Write plan → User reviews → Approve → Execute task
|
|
|
18659
19261
|
**Important:** \`hive_exec_complete\` commits changes to task branch but does NOT merge.
|
|
18660
19262
|
Use \`hive_merge\` to explicitly integrate changes. Worktrees persist until manually removed.
|
|
18661
19263
|
|
|
18662
|
-
###
|
|
19264
|
+
### Delegated Execution (OMO-Slim Integration)
|
|
18663
19265
|
|
|
18664
|
-
|
|
18665
|
-
|
|
18666
|
-
\`\`\`
|
|
18667
|
-
hive_subtask_create(task, "Write failing tests", "test")
|
|
18668
|
-
hive_subtask_create(task, "Implement until green", "implement")
|
|
18669
|
-
hive_subtask_create(task, "Run test suite", "verify")
|
|
18670
|
-
\`\`\`
|
|
18671
|
-
|
|
18672
|
-
Subtask types: test, implement, review, verify, research, debug, custom
|
|
18673
|
-
|
|
18674
|
-
**Test-Driven Development**: For implementation tasks, consider writing tests first.
|
|
18675
|
-
Tests define "done" and provide feedback loops that improve quality.
|
|
18676
|
-
|
|
18677
|
-
### Plan Format
|
|
18678
|
-
|
|
18679
|
-
\`\`\`markdown
|
|
18680
|
-
# Feature Name
|
|
19266
|
+
When OMO-Slim is installed, \`hive_exec_start\` spawns worker agents in tmux panes:
|
|
18681
19267
|
|
|
18682
|
-
|
|
18683
|
-
|
|
19268
|
+
1. \`hive_exec_start(task)\` → Creates worktree + spawns worker via \`background_task\`
|
|
19269
|
+
2. Worker appears in tmux pane - watch it work in real-time
|
|
19270
|
+
3. Worker completes → calls \`hive_exec_complete(status: "completed")\`
|
|
19271
|
+
4. Worker blocked → calls \`hive_exec_complete(status: "blocked", blocker: {...})\`
|
|
18684
19272
|
|
|
18685
|
-
|
|
19273
|
+
**Handling blocked workers:**
|
|
19274
|
+
1. Check blockers with \`hive_worker_status()\`
|
|
19275
|
+
2. Read the blocker info (reason, options, recommendation)
|
|
19276
|
+
3. Ask user via \`question()\` tool
|
|
19277
|
+
4. Resume with \`hive_exec_start(task, continueFrom: "blocked", decision: answer)\`
|
|
18686
19278
|
|
|
18687
|
-
|
|
18688
|
-
|
|
19279
|
+
**Agent auto-selection** based on task content:
|
|
19280
|
+
| Pattern | Agent |
|
|
19281
|
+
|---------|-------|
|
|
19282
|
+
| find, search, explore | explorer |
|
|
19283
|
+
| research, docs | librarian |
|
|
19284
|
+
| ui, component, react | designer |
|
|
19285
|
+
| architect, decision | oracle |
|
|
19286
|
+
| (default) | general |
|
|
18689
19287
|
|
|
18690
|
-
|
|
18691
|
-
Description.
|
|
18692
|
-
\`\`\`
|
|
19288
|
+
Without OMO-Slim: \`hive_exec_start\` falls back to inline mode (work in same session).
|
|
18693
19289
|
|
|
18694
19290
|
### Planning Phase - Context Management REQUIRED
|
|
18695
19291
|
|
|
@@ -18702,22 +19298,29 @@ As you research and plan, CONTINUOUSLY save findings using \`hive_context_write\
|
|
|
18702
19298
|
**Update existing context files** when new info emerges - dont create duplicates.
|
|
18703
19299
|
Workers depend on context for background. Without it, they work blind.
|
|
18704
19300
|
|
|
18705
|
-
Save context BEFORE writing the plan, and UPDATE it as planning iterates.
|
|
18706
|
-
|
|
18707
19301
|
\`hive_tasks_sync\` parses \`### N. Task Name\` headers.
|
|
19302
|
+
|
|
19303
|
+
### Execution Phase - Stay Aligned
|
|
19304
|
+
|
|
19305
|
+
During execution, call \`hive_status\` periodically to:
|
|
19306
|
+
- Check current progress and pending work
|
|
19307
|
+
- See context files to read
|
|
19308
|
+
- Get reminded of next actions
|
|
18708
19309
|
`;
|
|
18709
19310
|
var plugin = async (ctx) => {
|
|
18710
|
-
const { directory } = ctx;
|
|
19311
|
+
const { directory, client } = ctx;
|
|
18711
19312
|
const featureService = new FeatureService(directory);
|
|
18712
19313
|
const planService = new PlanService(directory);
|
|
18713
19314
|
const taskService = new TaskService(directory);
|
|
18714
|
-
const subtaskService = new SubtaskService(directory);
|
|
18715
19315
|
const contextService = new ContextService(directory);
|
|
18716
|
-
const
|
|
19316
|
+
const configService = new ConfigService;
|
|
18717
19317
|
const worktreeService = new WorktreeService({
|
|
18718
19318
|
baseDir: directory,
|
|
18719
|
-
hiveDir:
|
|
19319
|
+
hiveDir: path5.join(directory, ".hive")
|
|
18720
19320
|
});
|
|
19321
|
+
const isOmoSlimEnabled = () => {
|
|
19322
|
+
return configService.isOmoSlimEnabled();
|
|
19323
|
+
};
|
|
18721
19324
|
const resolveFeature = (explicit) => {
|
|
18722
19325
|
if (explicit)
|
|
18723
19326
|
return explicit;
|
|
@@ -18738,6 +19341,20 @@ var plugin = async (ctx) => {
|
|
|
18738
19341
|
}
|
|
18739
19342
|
}
|
|
18740
19343
|
};
|
|
19344
|
+
const checkBlocked = (feature) => {
|
|
19345
|
+
const fs9 = __require("fs");
|
|
19346
|
+
const blockedPath = path5.join(directory, ".hive", "features", feature, "BLOCKED");
|
|
19347
|
+
if (fs9.existsSync(blockedPath)) {
|
|
19348
|
+
const reason = fs9.readFileSync(blockedPath, "utf-8").trim();
|
|
19349
|
+
return `⛔ BLOCKED by Beekeeper
|
|
19350
|
+
|
|
19351
|
+
${reason || "(No reason provided)"}
|
|
19352
|
+
|
|
19353
|
+
The human has blocked this feature. Wait for them to unblock it.
|
|
19354
|
+
To unblock: Remove .hive/features/${feature}/BLOCKED`;
|
|
19355
|
+
}
|
|
19356
|
+
return null;
|
|
19357
|
+
};
|
|
18741
19358
|
return {
|
|
18742
19359
|
"experimental.chat.system.transform": async (_input, output) => {
|
|
18743
19360
|
output.system.push(HIVE_SYSTEM_PROMPT);
|
|
@@ -18761,6 +19378,7 @@ var plugin = async (ctx) => {
|
|
|
18761
19378
|
}
|
|
18762
19379
|
},
|
|
18763
19380
|
tool: {
|
|
19381
|
+
hive_skill: createHiveSkillTool(),
|
|
18764
19382
|
hive_feature_create: tool({
|
|
18765
19383
|
description: "Create a new feature and set it as active",
|
|
18766
19384
|
args: {
|
|
@@ -18769,7 +19387,40 @@ var plugin = async (ctx) => {
|
|
|
18769
19387
|
},
|
|
18770
19388
|
async execute({ name, ticket }) {
|
|
18771
19389
|
const feature = featureService.create(name, ticket);
|
|
18772
|
-
return `Feature "${name}" created.
|
|
19390
|
+
return `Feature "${name}" created.
|
|
19391
|
+
|
|
19392
|
+
## Discovery Phase Required
|
|
19393
|
+
|
|
19394
|
+
Before writing a plan, you MUST:
|
|
19395
|
+
1. Ask clarifying questions about the feature
|
|
19396
|
+
2. Document Q&A in plan.md with a \`## Discovery\` section
|
|
19397
|
+
3. Research the codebase (grep, read existing code)
|
|
19398
|
+
4. Save findings with hive_context_write
|
|
19399
|
+
|
|
19400
|
+
Example discovery section:
|
|
19401
|
+
\`\`\`markdown
|
|
19402
|
+
## Discovery
|
|
19403
|
+
|
|
19404
|
+
**Q: What authentication system do we use?**
|
|
19405
|
+
A: JWT with refresh tokens, see src/auth/
|
|
19406
|
+
|
|
19407
|
+
**Q: Should this work offline?**
|
|
19408
|
+
A: No, online-only is fine
|
|
19409
|
+
|
|
19410
|
+
**Research:**
|
|
19411
|
+
- Found existing theme system in src/theme/
|
|
19412
|
+
- Uses CSS variables pattern
|
|
19413
|
+
\`\`\`
|
|
19414
|
+
|
|
19415
|
+
## Planning Guidelines
|
|
19416
|
+
|
|
19417
|
+
When writing your plan, include:
|
|
19418
|
+
- \`## Non-Goals\` - What we're explicitly NOT building (scope boundaries)
|
|
19419
|
+
- \`## Ghost Diffs\` - Alternatives you considered but rejected
|
|
19420
|
+
|
|
19421
|
+
These prevent scope creep and re-proposing rejected solutions.
|
|
19422
|
+
|
|
19423
|
+
NEXT: Ask your first clarifying question about this feature.`;
|
|
18773
19424
|
}
|
|
18774
19425
|
}),
|
|
18775
19426
|
hive_feature_list: tool({
|
|
@@ -18799,6 +19450,34 @@ var plugin = async (ctx) => {
|
|
|
18799
19450
|
return `Feature "${feature}" marked as completed`;
|
|
18800
19451
|
}
|
|
18801
19452
|
}),
|
|
19453
|
+
hive_journal_append: tool({
|
|
19454
|
+
description: "Append entry to .hive/journal.md for audit trail",
|
|
19455
|
+
args: {
|
|
19456
|
+
feature: tool.schema.string().describe("Feature name for context"),
|
|
19457
|
+
trouble: tool.schema.string().describe("What went wrong"),
|
|
19458
|
+
resolution: tool.schema.string().describe("How it was fixed"),
|
|
19459
|
+
constraint: tool.schema.string().optional().describe("Never/Always rule derived")
|
|
19460
|
+
},
|
|
19461
|
+
async execute({ feature, trouble, resolution, constraint }) {
|
|
19462
|
+
const journalPath = path5.join(directory, ".hive", "journal.md");
|
|
19463
|
+
if (!fs6.existsSync(journalPath)) {
|
|
19464
|
+
return `Error: journal.md not found. Create a feature first to initialize the journal.`;
|
|
19465
|
+
}
|
|
19466
|
+
const date5 = new Date().toISOString().split("T")[0];
|
|
19467
|
+
const entry = `
|
|
19468
|
+
### ${date5}: ${feature}
|
|
19469
|
+
|
|
19470
|
+
**Trouble**: ${trouble}
|
|
19471
|
+
**Resolution**: ${resolution}
|
|
19472
|
+
${constraint ? `**Constraint**: ${constraint}` : ""}
|
|
19473
|
+
**See**: .hive/features/${feature}/plan.md
|
|
19474
|
+
|
|
19475
|
+
---
|
|
19476
|
+
`;
|
|
19477
|
+
fs6.appendFileSync(journalPath, entry);
|
|
19478
|
+
return `Journal entry added for ${feature}. ${constraint ? `Constraint: "${constraint}"` : ""}`;
|
|
19479
|
+
}
|
|
19480
|
+
}),
|
|
18802
19481
|
hive_plan_write: tool({
|
|
18803
19482
|
description: "Write plan.md (clears existing comments)",
|
|
18804
19483
|
args: {
|
|
@@ -18809,6 +19488,17 @@ var plugin = async (ctx) => {
|
|
|
18809
19488
|
const feature = resolveFeature(explicitFeature);
|
|
18810
19489
|
if (!feature)
|
|
18811
19490
|
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
19491
|
+
const hasDiscovery = content.toLowerCase().includes("## discovery");
|
|
19492
|
+
if (!hasDiscovery) {
|
|
19493
|
+
return `BLOCKED: Discovery section required before planning.
|
|
19494
|
+
|
|
19495
|
+
Your plan must include a \`## Discovery\` section documenting:
|
|
19496
|
+
- Questions you asked and answers received
|
|
19497
|
+
- Research findings from codebase exploration
|
|
19498
|
+
- Key decisions made
|
|
19499
|
+
|
|
19500
|
+
Add this section to your plan content and try again.`;
|
|
19501
|
+
}
|
|
18812
19502
|
captureSession(feature, toolContext);
|
|
18813
19503
|
const planPath = planService.write(feature, content);
|
|
18814
19504
|
return `Plan written to ${planPath}. Comments cleared for fresh review.`;
|
|
@@ -18880,7 +19570,8 @@ var plugin = async (ctx) => {
|
|
|
18880
19570
|
if (!feature)
|
|
18881
19571
|
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
18882
19572
|
const folder = taskService.create(feature, name, order);
|
|
18883
|
-
return `Manual task created: ${folder}
|
|
19573
|
+
return `Manual task created: ${folder}
|
|
19574
|
+
Reminder: start work with hive_exec_start to use its worktree, and ensure any subagents work in that worktree too.`;
|
|
18884
19575
|
}
|
|
18885
19576
|
}),
|
|
18886
19577
|
hive_task_update: tool({
|
|
@@ -18903,21 +19594,36 @@ var plugin = async (ctx) => {
|
|
|
18903
19594
|
}
|
|
18904
19595
|
}),
|
|
18905
19596
|
hive_exec_start: tool({
|
|
18906
|
-
description: "Create worktree and begin work on task",
|
|
19597
|
+
description: "Create worktree and begin work on task. When OMO-Slim is installed, spawns worker agent in tmux pane.",
|
|
18907
19598
|
args: {
|
|
18908
19599
|
task: tool.schema.string().describe("Task folder name"),
|
|
18909
|
-
feature: tool.schema.string().optional().describe("Feature name (defaults to detection or single feature)")
|
|
19600
|
+
feature: tool.schema.string().optional().describe("Feature name (defaults to detection or single feature)"),
|
|
19601
|
+
continueFrom: tool.schema.enum(["blocked"]).optional().describe("Resume a blocked task"),
|
|
19602
|
+
decision: tool.schema.string().optional().describe("Answer to blocker question when continuing")
|
|
18910
19603
|
},
|
|
18911
|
-
async execute({ task, feature: explicitFeature }) {
|
|
19604
|
+
async execute({ task, feature: explicitFeature, continueFrom, decision }, toolContext) {
|
|
18912
19605
|
const feature = resolveFeature(explicitFeature);
|
|
18913
19606
|
if (!feature)
|
|
18914
19607
|
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
19608
|
+
const blocked = checkBlocked(feature);
|
|
19609
|
+
if (blocked)
|
|
19610
|
+
return blocked;
|
|
18915
19611
|
const taskInfo = taskService.get(feature, task);
|
|
18916
19612
|
if (!taskInfo)
|
|
18917
19613
|
return `Error: Task "${task}" not found`;
|
|
18918
19614
|
if (taskInfo.status === "done")
|
|
18919
19615
|
return "Error: Task already completed";
|
|
18920
|
-
|
|
19616
|
+
if (continueFrom === "blocked" && taskInfo.status !== "blocked") {
|
|
19617
|
+
return "Error: Task is not in blocked state. Use without continueFrom.";
|
|
19618
|
+
}
|
|
19619
|
+
let worktree;
|
|
19620
|
+
if (continueFrom === "blocked") {
|
|
19621
|
+
worktree = await worktreeService.get(feature, task);
|
|
19622
|
+
if (!worktree)
|
|
19623
|
+
return "Error: No worktree found for blocked task";
|
|
19624
|
+
} else {
|
|
19625
|
+
worktree = await worktreeService.create(feature, task);
|
|
19626
|
+
}
|
|
18921
19627
|
taskService.update(feature, task, {
|
|
18922
19628
|
status: "in_progress",
|
|
18923
19629
|
baseCommit: worktree.commit
|
|
@@ -18933,7 +19639,7 @@ var plugin = async (ctx) => {
|
|
|
18933
19639
|
|
|
18934
19640
|
`;
|
|
18935
19641
|
if (planResult) {
|
|
18936
|
-
const taskMatch = planResult.content.match(new RegExp(`###\\s*\\d+\\.\\s*${taskInfo.name.replace(/[.*+?^${}()|[
|
|
19642
|
+
const taskMatch = planResult.content.match(new RegExp(`###\\s*\\d+\\.\\s*${taskInfo.name.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&")}[\\s\\S]*?(?=###|$)`, "i"));
|
|
18937
19643
|
if (taskMatch) {
|
|
18938
19644
|
specContent += `## Plan Section
|
|
18939
19645
|
|
|
@@ -18958,36 +19664,140 @@ ${priorTasks.join(`
|
|
|
18958
19664
|
`;
|
|
18959
19665
|
}
|
|
18960
19666
|
taskService.writeSpec(feature, task, specContent);
|
|
19667
|
+
if (isOmoSlimEnabled()) {
|
|
19668
|
+
const contextFiles = [];
|
|
19669
|
+
const contextDir = path5.join(directory, ".hive", "features", feature, "context");
|
|
19670
|
+
if (fs6.existsSync(contextDir)) {
|
|
19671
|
+
const files = fs6.readdirSync(contextDir).filter((f) => f.endsWith(".md"));
|
|
19672
|
+
for (const file2 of files) {
|
|
19673
|
+
const content = fs6.readFileSync(path5.join(contextDir, file2), "utf-8");
|
|
19674
|
+
contextFiles.push({ name: file2, content });
|
|
19675
|
+
}
|
|
19676
|
+
}
|
|
19677
|
+
const previousTasks = allTasks.filter((t) => t.status === "done" && t.summary).map((t) => ({ name: t.folder, summary: t.summary }));
|
|
19678
|
+
const workerPrompt = buildWorkerPrompt({
|
|
19679
|
+
feature,
|
|
19680
|
+
task,
|
|
19681
|
+
taskOrder: parseInt(taskInfo.folder.match(/^(\d+)/)?.[1] || "0", 10),
|
|
19682
|
+
worktreePath: worktree.path,
|
|
19683
|
+
branch: worktree.branch,
|
|
19684
|
+
plan: planResult?.content || "No plan available",
|
|
19685
|
+
contextFiles,
|
|
19686
|
+
spec: specContent,
|
|
19687
|
+
previousTasks,
|
|
19688
|
+
continueFrom: continueFrom === "blocked" ? {
|
|
19689
|
+
status: "blocked",
|
|
19690
|
+
previousSummary: taskInfo.summary || "No previous summary",
|
|
19691
|
+
decision: decision || "No decision provided"
|
|
19692
|
+
} : undefined
|
|
19693
|
+
});
|
|
19694
|
+
const agent = selectAgent(taskInfo.name, specContent);
|
|
19695
|
+
return JSON.stringify({
|
|
19696
|
+
worktreePath: worktree.path,
|
|
19697
|
+
branch: worktree.branch,
|
|
19698
|
+
mode: "delegate",
|
|
19699
|
+
agent,
|
|
19700
|
+
delegationRequired: true,
|
|
19701
|
+
backgroundTaskCall: {
|
|
19702
|
+
agent,
|
|
19703
|
+
prompt: workerPrompt,
|
|
19704
|
+
description: `Hive: ${task}`,
|
|
19705
|
+
sync: false
|
|
19706
|
+
},
|
|
19707
|
+
instructions: `## Delegation Required
|
|
19708
|
+
|
|
19709
|
+
You MUST now call background_task to spawn a worker:
|
|
19710
|
+
|
|
19711
|
+
\`\`\`
|
|
19712
|
+
background_task({
|
|
19713
|
+
agent: "${agent}",
|
|
19714
|
+
prompt: <the workerPrompt below>,
|
|
19715
|
+
description: "Hive: ${task}",
|
|
19716
|
+
sync: false
|
|
19717
|
+
})
|
|
19718
|
+
\`\`\`
|
|
19719
|
+
|
|
19720
|
+
After spawning:
|
|
19721
|
+
- Monitor with hive_worker_status
|
|
19722
|
+
- Handle blockers when worker exits
|
|
19723
|
+
- Merge completed work with hive_merge
|
|
19724
|
+
|
|
19725
|
+
DO NOT do the work yourself. Delegate it.`,
|
|
19726
|
+
workerPrompt
|
|
19727
|
+
}, null, 2);
|
|
19728
|
+
}
|
|
18961
19729
|
return `Worktree created at ${worktree.path}
|
|
18962
19730
|
Branch: ${worktree.branch}
|
|
18963
19731
|
Base commit: ${worktree.commit}
|
|
18964
|
-
Spec: ${task}/spec.md generated
|
|
19732
|
+
Spec: ${task}/spec.md generated
|
|
19733
|
+
Reminder: do all work inside this worktree and ensure any subagents do the same.`;
|
|
18965
19734
|
}
|
|
18966
19735
|
}),
|
|
18967
19736
|
hive_exec_complete: tool({
|
|
18968
|
-
description: "Complete task: commit changes to branch, write report
|
|
19737
|
+
description: "Complete task: commit changes to branch, write report. Supports blocked/failed/partial status for worker communication.",
|
|
18969
19738
|
args: {
|
|
18970
19739
|
task: tool.schema.string().describe("Task folder name"),
|
|
18971
19740
|
summary: tool.schema.string().describe("Summary of what was done"),
|
|
19741
|
+
status: tool.schema.enum(["completed", "blocked", "failed", "partial"]).optional().default("completed").describe("Task completion status"),
|
|
19742
|
+
blocker: tool.schema.object({
|
|
19743
|
+
reason: tool.schema.string().describe("Why the task is blocked"),
|
|
19744
|
+
options: tool.schema.array(tool.schema.string()).optional().describe("Available options for the user"),
|
|
19745
|
+
recommendation: tool.schema.string().optional().describe("Your recommended choice"),
|
|
19746
|
+
context: tool.schema.string().optional().describe("Additional context for the decision")
|
|
19747
|
+
}).optional().describe("Blocker info when status is blocked"),
|
|
18972
19748
|
feature: tool.schema.string().optional().describe("Feature name (defaults to detection or single feature)")
|
|
18973
19749
|
},
|
|
18974
|
-
async execute({ task, summary, feature: explicitFeature }) {
|
|
19750
|
+
async execute({ task, summary, status = "completed", blocker, feature: explicitFeature }) {
|
|
18975
19751
|
const feature = resolveFeature(explicitFeature);
|
|
18976
19752
|
if (!feature)
|
|
18977
19753
|
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
18978
19754
|
const taskInfo = taskService.get(feature, task);
|
|
18979
19755
|
if (!taskInfo)
|
|
18980
19756
|
return `Error: Task "${task}" not found`;
|
|
18981
|
-
if (taskInfo.status !== "in_progress")
|
|
19757
|
+
if (taskInfo.status !== "in_progress" && taskInfo.status !== "blocked")
|
|
18982
19758
|
return "Error: Task not in progress";
|
|
19759
|
+
if (status === "completed") {
|
|
19760
|
+
const verificationKeywords = ["test", "build", "lint", "vitest", "jest", "npm run", "pnpm", "cargo", "pytest", "verified", "passes", "succeeds"];
|
|
19761
|
+
const summaryLower = summary.toLowerCase();
|
|
19762
|
+
const hasVerificationMention = verificationKeywords.some((kw) => summaryLower.includes(kw));
|
|
19763
|
+
if (!hasVerificationMention) {
|
|
19764
|
+
return `BLOCKED: No verification detected in summary.
|
|
19765
|
+
|
|
19766
|
+
Before claiming completion, you must:
|
|
19767
|
+
1. Run tests (vitest, jest, pytest, etc.)
|
|
19768
|
+
2. Run build (npm run build, cargo build, etc.)
|
|
19769
|
+
3. Include verification results in summary
|
|
19770
|
+
|
|
19771
|
+
Example summary: "Implemented auth flow. Tests pass (vitest). Build succeeds."
|
|
19772
|
+
|
|
19773
|
+
Re-run with updated summary showing verification results.`;
|
|
19774
|
+
}
|
|
19775
|
+
}
|
|
19776
|
+
if (status === "blocked") {
|
|
19777
|
+
taskService.update(feature, task, {
|
|
19778
|
+
status: "blocked",
|
|
19779
|
+
summary,
|
|
19780
|
+
blocker
|
|
19781
|
+
});
|
|
19782
|
+
const worktree2 = await worktreeService.get(feature, task);
|
|
19783
|
+
return JSON.stringify({
|
|
19784
|
+
status: "blocked",
|
|
19785
|
+
task,
|
|
19786
|
+
summary,
|
|
19787
|
+
blocker,
|
|
19788
|
+
worktreePath: worktree2?.path,
|
|
19789
|
+
message: 'Task blocked. Hive Master will ask user and resume with hive_exec_start(continueFrom: "blocked", decision: answer)'
|
|
19790
|
+
}, null, 2);
|
|
19791
|
+
}
|
|
18983
19792
|
const commitResult = await worktreeService.commitChanges(feature, task, `hive(${task}): ${summary.slice(0, 50)}`);
|
|
18984
19793
|
const diff = await worktreeService.getDiff(feature, task);
|
|
19794
|
+
const statusLabel = status === "completed" ? "success" : status;
|
|
18985
19795
|
const reportLines = [
|
|
18986
19796
|
`# Task Report: ${task}`,
|
|
18987
19797
|
"",
|
|
18988
19798
|
`**Feature:** ${feature}`,
|
|
18989
19799
|
`**Completed:** ${new Date().toISOString()}`,
|
|
18990
|
-
`**Status:**
|
|
19800
|
+
`**Status:** ${statusLabel}`,
|
|
18991
19801
|
`**Commit:** ${commitResult.sha || "none"}`,
|
|
18992
19802
|
"",
|
|
18993
19803
|
"---",
|
|
@@ -19011,9 +19821,10 @@ Spec: ${task}/spec.md generated`;
|
|
|
19011
19821
|
}
|
|
19012
19822
|
taskService.writeReport(feature, task, reportLines.join(`
|
|
19013
19823
|
`));
|
|
19014
|
-
|
|
19824
|
+
const finalStatus = status === "completed" ? "done" : status;
|
|
19825
|
+
taskService.update(feature, task, { status: finalStatus, summary });
|
|
19015
19826
|
const worktree = await worktreeService.get(feature, task);
|
|
19016
|
-
return `Task "${task}"
|
|
19827
|
+
return `Task "${task}" ${status}. Changes committed to branch ${worktree?.branch || "unknown"}.
|
|
19017
19828
|
Use hive_merge to integrate changes. Worktree preserved at ${worktree?.path || "unknown"}.`;
|
|
19018
19829
|
}
|
|
19019
19830
|
}),
|
|
@@ -19032,6 +19843,51 @@ Use hive_merge to integrate changes. Worktree preserved at ${worktree?.path || "
|
|
|
19032
19843
|
return `Task "${task}" aborted. Status reset to pending.`;
|
|
19033
19844
|
}
|
|
19034
19845
|
}),
|
|
19846
|
+
hive_worker_status: tool({
|
|
19847
|
+
description: "Check status of delegated workers. Shows running workers, blockers, and progress.",
|
|
19848
|
+
args: {
|
|
19849
|
+
task: tool.schema.string().optional().describe("Specific task to check, or omit for all"),
|
|
19850
|
+
feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
|
|
19851
|
+
},
|
|
19852
|
+
async execute({ task: specificTask, feature: explicitFeature }) {
|
|
19853
|
+
const feature = resolveFeature(explicitFeature);
|
|
19854
|
+
if (!feature)
|
|
19855
|
+
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
19856
|
+
const STUCK_THRESHOLD = 10 * 60 * 1000;
|
|
19857
|
+
const now = Date.now();
|
|
19858
|
+
const tasks = taskService.list(feature);
|
|
19859
|
+
const inProgressTasks = tasks.filter((t) => (t.status === "in_progress" || t.status === "blocked") && (!specificTask || t.folder === specificTask));
|
|
19860
|
+
if (inProgressTasks.length === 0) {
|
|
19861
|
+
return specificTask ? `No active worker for task "${specificTask}"` : "No active workers.";
|
|
19862
|
+
}
|
|
19863
|
+
const workers = await Promise.all(inProgressTasks.map(async (t) => {
|
|
19864
|
+
const worktree = await worktreeService.get(feature, t.folder);
|
|
19865
|
+
const taskData = t;
|
|
19866
|
+
const startedAt = taskData.startedAt ? new Date(taskData.startedAt).getTime() : now;
|
|
19867
|
+
const maybeStuck = now - startedAt > STUCK_THRESHOLD && t.status === "in_progress";
|
|
19868
|
+
return {
|
|
19869
|
+
task: t.folder,
|
|
19870
|
+
name: t.name,
|
|
19871
|
+
status: t.status,
|
|
19872
|
+
agent: taskData.agent || "inline",
|
|
19873
|
+
mode: taskData.mode || "inline",
|
|
19874
|
+
workerId: taskData.workerId || null,
|
|
19875
|
+
worktreePath: worktree?.path || null,
|
|
19876
|
+
branch: worktree?.branch || null,
|
|
19877
|
+
maybeStuck,
|
|
19878
|
+
blocker: taskData.blocker || null,
|
|
19879
|
+
summary: t.summary || null
|
|
19880
|
+
};
|
|
19881
|
+
}));
|
|
19882
|
+
const omoSlimEnabled = isOmoSlimEnabled();
|
|
19883
|
+
return JSON.stringify({
|
|
19884
|
+
feature,
|
|
19885
|
+
omoSlimEnabled,
|
|
19886
|
+
workers,
|
|
19887
|
+
hint: workers.some((w) => w.status === "blocked") ? 'Use hive_exec_start(task, continueFrom: "blocked", decision: answer) to resume blocked workers' : workers.some((w) => w.maybeStuck) ? "Some workers may be stuck. Check tmux panes or abort with hive_exec_abort." : "Workers in progress. Watch tmux panes for live updates."
|
|
19888
|
+
}, null, 2);
|
|
19889
|
+
}
|
|
19890
|
+
}),
|
|
19035
19891
|
hive_merge: tool({
|
|
19036
19892
|
description: "Merge completed task branch into current branch (explicit integration)",
|
|
19037
19893
|
args: {
|
|
@@ -19100,218 +19956,166 @@ Files changed: ${result.filesChanged?.length || 0}`;
|
|
|
19100
19956
|
return `Context file written: ${filePath}`;
|
|
19101
19957
|
}
|
|
19102
19958
|
}),
|
|
19103
|
-
|
|
19104
|
-
description: "
|
|
19105
|
-
args: {
|
|
19106
|
-
name: tool.schema.string().optional().describe("Context file name. If omitted, returns all context compiled."),
|
|
19107
|
-
feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
|
|
19108
|
-
},
|
|
19109
|
-
async execute({ name, feature: explicitFeature }) {
|
|
19110
|
-
const feature = resolveFeature(explicitFeature);
|
|
19111
|
-
if (!feature)
|
|
19112
|
-
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
19113
|
-
if (name) {
|
|
19114
|
-
const content = contextService.read(feature, name);
|
|
19115
|
-
if (!content)
|
|
19116
|
-
return `Error: Context file '${name}' not found`;
|
|
19117
|
-
return content;
|
|
19118
|
-
}
|
|
19119
|
-
const compiled = contextService.compile(feature);
|
|
19120
|
-
if (!compiled)
|
|
19121
|
-
return "No context files found";
|
|
19122
|
-
return compiled;
|
|
19123
|
-
}
|
|
19124
|
-
}),
|
|
19125
|
-
hive_context_list: tool({
|
|
19126
|
-
description: "List all context files for the feature",
|
|
19959
|
+
hive_status: tool({
|
|
19960
|
+
description: "Get comprehensive status of a feature including plan, tasks, and context. Returns JSON with all relevant state for resuming work.",
|
|
19127
19961
|
args: {
|
|
19128
19962
|
feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
|
|
19129
19963
|
},
|
|
19130
19964
|
async execute({ feature: explicitFeature }) {
|
|
19131
19965
|
const feature = resolveFeature(explicitFeature);
|
|
19132
|
-
if (!feature)
|
|
19133
|
-
return
|
|
19134
|
-
|
|
19135
|
-
|
|
19136
|
-
|
|
19137
|
-
|
|
19138
|
-
`);
|
|
19139
|
-
}
|
|
19140
|
-
}),
|
|
19141
|
-
hive_session_open: tool({
|
|
19142
|
-
description: "Open session, return full context for a feature",
|
|
19143
|
-
args: {
|
|
19144
|
-
feature: tool.schema.string().optional().describe("Feature name (defaults to active)"),
|
|
19145
|
-
task: tool.schema.string().optional().describe("Task folder to focus on")
|
|
19146
|
-
},
|
|
19147
|
-
async execute({ feature: explicitFeature, task }, toolContext) {
|
|
19148
|
-
const feature = resolveFeature(explicitFeature);
|
|
19149
|
-
if (!feature)
|
|
19150
|
-
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
19966
|
+
if (!feature) {
|
|
19967
|
+
return JSON.stringify({
|
|
19968
|
+
error: "No feature specified and no active feature found",
|
|
19969
|
+
hint: "Use hive_feature_create to create a new feature"
|
|
19970
|
+
});
|
|
19971
|
+
}
|
|
19151
19972
|
const featureData = featureService.get(feature);
|
|
19152
|
-
if (!featureData)
|
|
19153
|
-
return
|
|
19154
|
-
|
|
19155
|
-
|
|
19156
|
-
|
|
19973
|
+
if (!featureData) {
|
|
19974
|
+
return JSON.stringify({
|
|
19975
|
+
error: `Feature '${feature}' not found`,
|
|
19976
|
+
availableFeatures: featureService.list()
|
|
19977
|
+
});
|
|
19157
19978
|
}
|
|
19158
|
-
const
|
|
19979
|
+
const blocked = checkBlocked(feature);
|
|
19980
|
+
if (blocked)
|
|
19981
|
+
return blocked;
|
|
19982
|
+
const plan = planService.read(feature);
|
|
19159
19983
|
const tasks = taskService.list(feature);
|
|
19160
|
-
const
|
|
19161
|
-
const
|
|
19162
|
-
|
|
19163
|
-
|
|
19164
|
-
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
|
|
19168
|
-
|
|
19169
|
-
|
|
19170
|
-
|
|
19171
|
-
|
|
19172
|
-
|
|
19173
|
-
|
|
19174
|
-
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
|
|
19182
|
-
|
|
19183
|
-
|
|
19184
|
-
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19984
|
+
const contextFiles = contextService.list(feature);
|
|
19985
|
+
const tasksSummary = tasks.map((t) => ({
|
|
19986
|
+
folder: t.folder,
|
|
19987
|
+
name: t.name,
|
|
19988
|
+
status: t.status,
|
|
19989
|
+
origin: t.origin || "plan"
|
|
19990
|
+
}));
|
|
19991
|
+
const contextSummary = contextFiles.map((c) => ({
|
|
19992
|
+
name: c.name,
|
|
19993
|
+
chars: c.content.length,
|
|
19994
|
+
updatedAt: c.updatedAt
|
|
19995
|
+
}));
|
|
19996
|
+
const pendingTasks = tasksSummary.filter((t) => t.status === "pending");
|
|
19997
|
+
const inProgressTasks = tasksSummary.filter((t) => t.status === "in_progress");
|
|
19998
|
+
const doneTasks = tasksSummary.filter((t) => t.status === "done");
|
|
19999
|
+
const getNextAction = (planStatus2, tasks2) => {
|
|
20000
|
+
if (!planStatus2 || planStatus2 === "draft") {
|
|
20001
|
+
return "Write or revise plan with hive_plan_write, then get approval";
|
|
20002
|
+
}
|
|
20003
|
+
if (planStatus2 === "review") {
|
|
20004
|
+
return "Wait for plan approval or revise based on comments";
|
|
20005
|
+
}
|
|
20006
|
+
if (tasks2.length === 0) {
|
|
20007
|
+
return "Generate tasks from plan with hive_tasks_sync";
|
|
20008
|
+
}
|
|
20009
|
+
const inProgress = tasks2.find((t) => t.status === "in_progress");
|
|
20010
|
+
if (inProgress) {
|
|
20011
|
+
return `Continue work on task: ${inProgress.folder}`;
|
|
20012
|
+
}
|
|
20013
|
+
const pending = tasks2.find((t) => t.status === "pending");
|
|
20014
|
+
if (pending) {
|
|
20015
|
+
return `Start next task with hive_exec_start: ${pending.folder}`;
|
|
20016
|
+
}
|
|
20017
|
+
return "All tasks complete. Review and merge or complete feature.";
|
|
20018
|
+
};
|
|
20019
|
+
const planStatus = featureData.status === "planning" ? "draft" : featureData.status === "approved" ? "approved" : featureData.status === "executing" ? "locked" : "none";
|
|
20020
|
+
return JSON.stringify({
|
|
20021
|
+
feature: {
|
|
20022
|
+
name: feature,
|
|
20023
|
+
status: featureData.status,
|
|
20024
|
+
ticket: featureData.ticket || null,
|
|
20025
|
+
createdAt: featureData.createdAt
|
|
20026
|
+
},
|
|
20027
|
+
plan: {
|
|
20028
|
+
exists: !!plan,
|
|
20029
|
+
status: planStatus,
|
|
20030
|
+
approved: planStatus === "approved" || planStatus === "locked"
|
|
20031
|
+
},
|
|
20032
|
+
tasks: {
|
|
20033
|
+
total: tasks.length,
|
|
20034
|
+
pending: pendingTasks.length,
|
|
20035
|
+
inProgress: inProgressTasks.length,
|
|
20036
|
+
done: doneTasks.length,
|
|
20037
|
+
list: tasksSummary
|
|
20038
|
+
},
|
|
20039
|
+
context: {
|
|
20040
|
+
fileCount: contextFiles.length,
|
|
20041
|
+
files: contextSummary
|
|
20042
|
+
},
|
|
20043
|
+
nextAction: getNextAction(planStatus, tasksSummary)
|
|
19189
20044
|
});
|
|
19190
|
-
return output;
|
|
19191
|
-
}
|
|
19192
|
-
}),
|
|
19193
|
-
hive_session_list: tool({
|
|
19194
|
-
description: "List all sessions for the feature",
|
|
19195
|
-
args: {
|
|
19196
|
-
feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
|
|
19197
|
-
},
|
|
19198
|
-
async execute({ feature: explicitFeature }) {
|
|
19199
|
-
const feature = resolveFeature(explicitFeature);
|
|
19200
|
-
if (!feature)
|
|
19201
|
-
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
19202
|
-
const sessions = sessionService.list(feature);
|
|
19203
|
-
const master = sessionService.getMaster(feature);
|
|
19204
|
-
if (sessions.length === 0)
|
|
19205
|
-
return "No sessions";
|
|
19206
|
-
return sessions.map((s) => {
|
|
19207
|
-
const masterMark = s.sessionId === master ? " (master)" : "";
|
|
19208
|
-
return `${s.sessionId}${masterMark} - ${s.taskFolder || "no task"} - ${s.lastActiveAt}`;
|
|
19209
|
-
}).join(`
|
|
19210
|
-
`);
|
|
19211
|
-
}
|
|
19212
|
-
}),
|
|
19213
|
-
hive_subtask_create: tool({
|
|
19214
|
-
description: "Create a subtask within a task. Use for TDD: create test/implement/verify subtasks.",
|
|
19215
|
-
args: {
|
|
19216
|
-
task: tool.schema.string().describe("Task folder name"),
|
|
19217
|
-
name: tool.schema.string().describe("Subtask description"),
|
|
19218
|
-
type: tool.schema.enum(["test", "implement", "review", "verify", "research", "debug", "custom"]).optional().describe("Subtask type"),
|
|
19219
|
-
feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
|
|
19220
|
-
},
|
|
19221
|
-
async execute({ task, name, type, feature: explicitFeature }) {
|
|
19222
|
-
const feature = resolveFeature(explicitFeature);
|
|
19223
|
-
if (!feature)
|
|
19224
|
-
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
19225
|
-
try {
|
|
19226
|
-
const subtask = subtaskService.create(feature, task, name, type);
|
|
19227
|
-
return `Subtask created: ${subtask.id} - ${subtask.name} [${subtask.type || "custom"}]`;
|
|
19228
|
-
} catch (e) {
|
|
19229
|
-
return `Error: ${e.message}`;
|
|
19230
|
-
}
|
|
19231
20045
|
}
|
|
19232
20046
|
}),
|
|
19233
|
-
|
|
19234
|
-
description: "
|
|
20047
|
+
hive_request_review: tool({
|
|
20048
|
+
description: "Request human review of completed task. BLOCKS until human approves or requests changes. Call after completing work, before merging.",
|
|
19235
20049
|
args: {
|
|
19236
20050
|
task: tool.schema.string().describe("Task folder name"),
|
|
19237
|
-
|
|
19238
|
-
status: tool.schema.enum(["pending", "in_progress", "done", "cancelled"]).describe("New status"),
|
|
20051
|
+
summary: tool.schema.string().describe("Summary of what you did for human to review"),
|
|
19239
20052
|
feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
|
|
19240
20053
|
},
|
|
19241
|
-
async execute({ task,
|
|
20054
|
+
async execute({ task, summary, feature: explicitFeature }) {
|
|
19242
20055
|
const feature = resolveFeature(explicitFeature);
|
|
19243
20056
|
if (!feature)
|
|
19244
|
-
return "Error: No feature specified.
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
return `
|
|
19248
|
-
} catch (e) {
|
|
19249
|
-
return `Error: ${e.message}`;
|
|
20057
|
+
return "Error: No feature specified.";
|
|
20058
|
+
const taskDir = path5.join(directory, ".hive", "features", feature, "tasks", task);
|
|
20059
|
+
if (!fs6.existsSync(taskDir)) {
|
|
20060
|
+
return `Error: Task '${task}' not found in feature '${feature}'`;
|
|
19250
20061
|
}
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19254
|
-
|
|
19255
|
-
|
|
19256
|
-
|
|
19257
|
-
|
|
19258
|
-
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
|
|
19262
|
-
|
|
19263
|
-
|
|
19264
|
-
|
|
19265
|
-
|
|
19266
|
-
|
|
19267
|
-
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
20062
|
+
const reportPath = path5.join(taskDir, "report.md");
|
|
20063
|
+
const existingReport = fs6.existsSync(reportPath) ? fs6.readFileSync(reportPath, "utf-8") : `# Task Report
|
|
20064
|
+
`;
|
|
20065
|
+
const attemptCount = (existingReport.match(/## Attempt \d+/g) || []).length + 1;
|
|
20066
|
+
const timestamp = new Date().toISOString();
|
|
20067
|
+
const newContent = existingReport + `
|
|
20068
|
+
## Attempt ${attemptCount}
|
|
20069
|
+
|
|
20070
|
+
**Requested**: ${timestamp}
|
|
20071
|
+
|
|
20072
|
+
### Summary
|
|
20073
|
+
|
|
20074
|
+
${summary}
|
|
20075
|
+
|
|
20076
|
+
`;
|
|
20077
|
+
fs6.writeFileSync(reportPath, newContent);
|
|
20078
|
+
const pendingPath = path5.join(taskDir, "PENDING_REVIEW");
|
|
20079
|
+
fs6.writeFileSync(pendingPath, JSON.stringify({
|
|
20080
|
+
attempt: attemptCount,
|
|
20081
|
+
requestedAt: timestamp,
|
|
20082
|
+
summary: summary.substring(0, 200) + (summary.length > 200 ? "..." : "")
|
|
20083
|
+
}, null, 2));
|
|
20084
|
+
const pollInterval = 2000;
|
|
20085
|
+
const maxWait = 30 * 60 * 1000;
|
|
20086
|
+
const startTime = Date.now();
|
|
20087
|
+
while (fs6.existsSync(pendingPath)) {
|
|
20088
|
+
if (Date.now() - startTime > maxWait) {
|
|
20089
|
+
return "Review timed out after 30 minutes. Human did not respond.";
|
|
20090
|
+
}
|
|
20091
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
19275
20092
|
}
|
|
19276
|
-
|
|
19277
|
-
|
|
19278
|
-
|
|
19279
|
-
description: "Write spec.md for a subtask (detailed instructions)",
|
|
19280
|
-
args: {
|
|
19281
|
-
task: tool.schema.string().describe("Task folder name"),
|
|
19282
|
-
subtask: tool.schema.string().describe('Subtask ID (e.g., "1.1")'),
|
|
19283
|
-
content: tool.schema.string().describe("Spec content (markdown)"),
|
|
19284
|
-
feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
|
|
19285
|
-
},
|
|
19286
|
-
async execute({ task, subtask, content, feature: explicitFeature }) {
|
|
19287
|
-
const feature = resolveFeature(explicitFeature);
|
|
19288
|
-
if (!feature)
|
|
19289
|
-
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
19290
|
-
try {
|
|
19291
|
-
const specPath = subtaskService.writeSpec(feature, task, subtask, content);
|
|
19292
|
-
return `Subtask spec written: ${specPath}`;
|
|
19293
|
-
} catch (e) {
|
|
19294
|
-
return `Error: ${e.message}`;
|
|
20093
|
+
const resultPath = path5.join(taskDir, "REVIEW_RESULT");
|
|
20094
|
+
if (!fs6.existsSync(resultPath)) {
|
|
20095
|
+
return "Review cancelled (PENDING_REVIEW removed but no REVIEW_RESULT).";
|
|
19295
20096
|
}
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19313
|
-
|
|
19314
|
-
|
|
20097
|
+
const result = fs6.readFileSync(resultPath, "utf-8").trim();
|
|
20098
|
+
fs6.appendFileSync(reportPath, `### Review Result
|
|
20099
|
+
|
|
20100
|
+
${result}
|
|
20101
|
+
|
|
20102
|
+
---
|
|
20103
|
+
|
|
20104
|
+
`);
|
|
20105
|
+
if (result.toUpperCase() === "APPROVED") {
|
|
20106
|
+
return `✅ APPROVED
|
|
20107
|
+
|
|
20108
|
+
Your work has been approved. You may now merge:
|
|
20109
|
+
|
|
20110
|
+
hive_merge(task="${task}")
|
|
20111
|
+
|
|
20112
|
+
After merging, proceed to the next task.`;
|
|
20113
|
+
} else {
|
|
20114
|
+
return `\uD83D\uDD04 Changes Requested
|
|
20115
|
+
|
|
20116
|
+
${result}
|
|
20117
|
+
|
|
20118
|
+
Make the requested changes, then call hive_request_review again.`;
|
|
19315
20119
|
}
|
|
19316
20120
|
}
|
|
19317
20121
|
})
|
|
@@ -19326,6 +20130,62 @@ ${contextCompiled.substring(0, 500)}...
|
|
|
19326
20130
|
return `Create feature "${name}" using hive_feature_create tool.`;
|
|
19327
20131
|
}
|
|
19328
20132
|
}
|
|
20133
|
+
},
|
|
20134
|
+
agent: {
|
|
20135
|
+
hive: {
|
|
20136
|
+
model: undefined,
|
|
20137
|
+
temperature: 0.7,
|
|
20138
|
+
description: "Hive Master - plan-first development with structured workflow and worker delegation",
|
|
20139
|
+
prompt: buildHiveAgentPrompt(undefined, false)
|
|
20140
|
+
}
|
|
20141
|
+
},
|
|
20142
|
+
systemPrompt: (existingPrompt) => {
|
|
20143
|
+
if (!existingPrompt.includes("Hive Master")) {
|
|
20144
|
+
return existingPrompt;
|
|
20145
|
+
}
|
|
20146
|
+
const featureNames = listFeatures(directory);
|
|
20147
|
+
let featureContext;
|
|
20148
|
+
for (const featureName of featureNames) {
|
|
20149
|
+
const feature = featureService.get(featureName);
|
|
20150
|
+
if (feature && ["planning", "executing"].includes(feature.status)) {
|
|
20151
|
+
const tasks = taskService.list(featureName);
|
|
20152
|
+
const pendingCount = tasks.filter((t) => t.status === "pending").length;
|
|
20153
|
+
const inProgressCount = tasks.filter((t) => t.status === "in_progress").length;
|
|
20154
|
+
const doneCount = tasks.filter((t) => t.status === "done").length;
|
|
20155
|
+
const contextDir = path5.join(directory, ".hive", "features", featureName, "context");
|
|
20156
|
+
const contextList = fs6.existsSync(contextDir) ? fs6.readdirSync(contextDir).filter((f) => f.endsWith(".md")) : [];
|
|
20157
|
+
const planResult = planService.read(featureName);
|
|
20158
|
+
let planStatus = "none";
|
|
20159
|
+
if (planResult) {
|
|
20160
|
+
planStatus = planResult.status === "approved" || planResult.status === "executing" ? "approved" : "draft";
|
|
20161
|
+
}
|
|
20162
|
+
featureContext = {
|
|
20163
|
+
name: featureName,
|
|
20164
|
+
planStatus,
|
|
20165
|
+
tasksSummary: `${doneCount} done, ${inProgressCount} in progress, ${pendingCount} pending`,
|
|
20166
|
+
contextList
|
|
20167
|
+
};
|
|
20168
|
+
break;
|
|
20169
|
+
}
|
|
20170
|
+
}
|
|
20171
|
+
if (featureContext || isOmoSlimEnabled()) {
|
|
20172
|
+
return buildHiveAgentPrompt(featureContext, isOmoSlimEnabled());
|
|
20173
|
+
}
|
|
20174
|
+
return existingPrompt;
|
|
20175
|
+
},
|
|
20176
|
+
config: async (opencodeConfig) => {
|
|
20177
|
+
const hiveAgentConfig = {
|
|
20178
|
+
model: undefined,
|
|
20179
|
+
temperature: 0.7,
|
|
20180
|
+
description: "Hive Master - plan-first development with structured workflow and worker delegation",
|
|
20181
|
+
prompt: buildHiveAgentPrompt(undefined, false)
|
|
20182
|
+
};
|
|
20183
|
+
const configAgent = opencodeConfig.agent;
|
|
20184
|
+
if (!configAgent) {
|
|
20185
|
+
opencodeConfig.agent = { hive: hiveAgentConfig };
|
|
20186
|
+
} else {
|
|
20187
|
+
configAgent.hive = hiveAgentConfig;
|
|
20188
|
+
}
|
|
19329
20189
|
}
|
|
19330
20190
|
};
|
|
19331
20191
|
};
|