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/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 path6 from "path";
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 fs6 from "fs/promises";
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 path5 from "path";
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 __require = /* @__PURE__ */ createRequire(import.meta.url);
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 = __require("os");
12821
- var tty = __require("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 = __require("tty");
12918
- var util = __require("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 = __require("fs");
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: feature?.status || "planning",
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
- feature.status = "approved";
13449
- feature.approvedAt = new Date().toISOString();
13450
- writeJson(featurePath, feature);
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 fs4.readdirSync(tasksPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
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
- fs4.rmSync(taskPath, { recursive: true });
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
- fs4.rmSync(subtaskPath, { recursive: true });
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 fs4.readdirSync(subtasksPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
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
- class SubtaskService {
13850
- projectRoot;
13851
- constructor(projectRoot) {
13852
- this.projectRoot = projectRoot;
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
- create(featureName, taskFolder, name, type) {
13855
- const subtasksPath = getSubtasksPath(this.projectRoot, featureName, taskFolder);
13856
- ensureDir(subtasksPath);
13857
- const existingFolders = this.listFolders(featureName, taskFolder);
13858
- const taskOrder = parseInt(taskFolder.split("-")[0], 10);
13859
- const nextOrder = existingFolders.length + 1;
13860
- const subtaskId = `${taskOrder}.${nextOrder}`;
13861
- const folderName = `${nextOrder}-${this.slugify(name)}`;
13862
- const subtaskPath = getSubtaskPath(this.projectRoot, featureName, taskFolder, folderName);
13863
- ensureDir(subtaskPath);
13864
- const subtaskStatus = {
13865
- status: "pending",
13866
- type,
13867
- createdAt: new Date().toISOString()
13868
- };
13869
- writeJson(getSubtaskStatusPath(this.projectRoot, featureName, taskFolder, folderName), subtaskStatus);
13870
- const specContent = `# Subtask: ${name}
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 fs6.access(tasksPath);
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 fs6.mkdir(path3.dirname(worktreePath), { recursive: true });
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 fs6.access(worktreePath);
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 fs6.readFile(statusPath, "utf-8"));
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 fs6.writeFile(patchPath, diff);
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 fs6.writeFile(patchPath, diffContent);
18392
+ await fs7.writeFile(patchPath, diffContent);
18153
18393
  const git = this.getGit();
18154
18394
  await git.applyPatch(patchPath);
18155
- await fs6.unlink(patchPath).catch(() => {});
18395
+ await fs7.unlink(patchPath).catch(() => {});
18156
18396
  return { success: true, filesAffected: filesChanged };
18157
18397
  } catch (error45) {
18158
- await fs6.unlink(patchPath).catch(() => {});
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 fs6.writeFile(patchPath, diffContent);
18414
+ await fs7.writeFile(patchPath, diffContent);
18175
18415
  const git = this.getGit();
18176
18416
  await git.applyPatch(patchPath, ["-R"]);
18177
- await fs6.unlink(patchPath).catch(() => {});
18417
+ await fs7.unlink(patchPath).catch(() => {});
18178
18418
  return { success: true, filesAffected: filesChanged };
18179
18419
  } catch (error45) {
18180
- await fs6.unlink(patchPath).catch(() => {});
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 fs6.readFile(diffPath, "utf-8");
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 fs6.rm(worktreePath, { recursive: true, force: true });
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 fs6.readdir(worktreesDir);
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 fs6.stat(featurePath).catch(() => null);
18482
+ const stat2 = await fs7.stat(featurePath).catch(() => null);
18243
18483
  if (!stat2?.isDirectory())
18244
18484
  continue;
18245
- const steps = await fs6.readdir(featurePath).catch(() => []);
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 fs6.readdir(worktreesDir).catch(() => []);
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 fs6.stat(featurePath).catch(() => null);
18506
+ const stat2 = await fs7.stat(featurePath).catch(() => null);
18267
18507
  if (!stat2?.isDirectory())
18268
18508
  continue;
18269
- const steps = await fs6.readdir(featurePath).catch(() => []);
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 fs6.stat(worktreePath).catch(() => null);
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 fs6.writeFile(patchPath, diffContent);
18533
+ await fs7.writeFile(patchPath, diffContent);
18294
18534
  const git = this.getGit();
18295
18535
  await git.applyPatch(patchPath, ["--check"]);
18296
- await fs6.unlink(patchPath).catch(() => {});
18536
+ await fs7.unlink(patchPath).catch(() => {});
18297
18537
  return [];
18298
18538
  } catch (error45) {
18299
- await fs6.unlink(patchPath).catch(() => {});
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 fs6.access(diffPath);
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 fs6.access(worktreePath);
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 = fs7.readdirSync(contextPath, { withFileTypes: true }).filter((f) => f.isFile() && f.name.endsWith(".md")).map((f) => f.name);
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 = fs7.statSync(filePath);
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
- fs7.unlinkSync(filePath);
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
- class SessionService {
18516
- projectRoot;
18517
- constructor(projectRoot) {
18518
- this.projectRoot = projectRoot;
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
- getSessionsPath(featureName) {
18521
- return path5.join(getFeaturePath(this.projectRoot, featureName), "sessions.json");
18522
- }
18523
- getSessions(featureName) {
18524
- const sessionsPath = this.getSessionsPath(featureName);
18525
- return readJson(sessionsPath) || { sessions: [] };
18526
- }
18527
- saveSessions(featureName, data) {
18528
- const sessionsPath = this.getSessionsPath(featureName);
18529
- ensureDir(path5.dirname(sessionsPath));
18530
- writeJson(sessionsPath, data);
18531
- }
18532
- track(featureName, sessionId, taskFolder) {
18533
- const data = this.getSessions(featureName);
18534
- const now = new Date().toISOString();
18535
- let session = data.sessions.find((s) => s.sessionId === sessionId);
18536
- if (session) {
18537
- session.lastActiveAt = now;
18538
- if (taskFolder)
18539
- session.taskFolder = taskFolder;
18540
- } else {
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
- data.sessions.push(session);
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
- getMaster(featureName) {
18561
- return this.getSessions(featureName).master;
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
- list(featureName) {
18564
- return this.getSessions(featureName).sessions;
18808
+ exists() {
18809
+ return fs10.existsSync(this.configPath);
18565
18810
  }
18566
- get(featureName, sessionId) {
18567
- return this.getSessions(featureName).sessions.find((s) => s.sessionId === sessionId);
18811
+ init() {
18812
+ if (!this.exists()) {
18813
+ return this.set(DEFAULT_HIVE_CONFIG);
18814
+ }
18815
+ return this.get();
18568
18816
  }
18569
- getByTask(featureName, taskFolder) {
18570
- return this.getSessions(featureName).sessions.find((s) => s.taskFolder === taskFolder);
18817
+ isOmoSlimEnabled() {
18818
+ return this.get().omoSlim?.enabled ?? false;
18571
18819
  }
18572
- remove(featureName, sessionId) {
18573
- const data = this.getSessions(featureName);
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
- findFeatureBySession(sessionId) {
18585
- const featuresPath = path5.join(this.projectRoot, ".hive", "features");
18586
- if (!fs8.existsSync(featuresPath))
18587
- return null;
18588
- const features = fs8.readdirSync(featuresPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
18589
- for (const feature of features) {
18590
- const sessions = this.getSessions(feature);
18591
- if (sessions.sessions.some((s) => s.sessionId === sessionId)) {
18592
- return feature;
18593
- }
18594
- if (sessions.master === sessionId) {
18595
- return feature;
18596
- }
18597
- }
18598
- return null;
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
- fork(featureName, fromSessionId) {
18601
- const data = this.getSessions(featureName);
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 (24 total)
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, hive_context_read, hive_context_list |
18647
- | Session | hive_session_open, hive_session_list |
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
- ### Subtasks & TDD
19264
+ ### Delegated Execution (OMO-Slim Integration)
18663
19265
 
18664
- For complex tasks, break work into subtasks:
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
- ## Overview
18683
- What we're building and why.
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
- ## Tasks
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
- ### 1. Task Name
18688
- Description of what to do.
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
- ### 2. Another Task
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 sessionService = new SessionService(directory);
19316
+ const configService = new ConfigService;
18717
19317
  const worktreeService = new WorktreeService({
18718
19318
  baseDir: directory,
18719
- hiveDir: path6.join(directory, ".hive")
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. Status: ${feature.status}. Write a plan with hive_plan_write.`;
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
- const worktree = await worktreeService.create(feature, task);
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(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?(?=###|$)`, "i"));
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 (does NOT merge or cleanup)",
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:** success`,
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
- taskService.update(feature, task, { status: "done", summary });
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}" completed. Changes committed to branch ${worktree?.branch || "unknown"}.
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
- hive_context_read: tool({
19104
- description: "Read a specific context file or all context for the feature",
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 "Error: No feature specified. Create a feature or provide feature param.";
19134
- const files = contextService.list(feature);
19135
- if (files.length === 0)
19136
- return "No context files";
19137
- return files.map((f) => `${f.name} (${f.content.length} chars, updated ${f.updatedAt})`).join(`
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 `Error: Feature '${feature}' not found`;
19154
- const ctx2 = toolContext;
19155
- if (ctx2?.sessionID) {
19156
- sessionService.track(feature, ctx2.sessionID, task);
19973
+ if (!featureData) {
19974
+ return JSON.stringify({
19975
+ error: `Feature '${feature}' not found`,
19976
+ availableFeatures: featureService.list()
19977
+ });
19157
19978
  }
19158
- const planResult = planService.read(feature);
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 contextCompiled = contextService.compile(feature);
19161
- const sessions = sessionService.list(feature);
19162
- let output = `## Feature: ${feature} [${featureData.status}]
19163
-
19164
- `;
19165
- if (planResult) {
19166
- output += `### Plan
19167
- ${planResult.content.substring(0, 500)}...
19168
-
19169
- `;
19170
- }
19171
- output += `### Tasks (${tasks.length})
19172
- `;
19173
- tasks.forEach((t) => {
19174
- output += `- ${t.folder}: ${t.name} [${t.status}]
19175
- `;
19176
- });
19177
- if (contextCompiled) {
19178
- output += `
19179
- ### Context
19180
- ${contextCompiled.substring(0, 500)}...
19181
- `;
19182
- }
19183
- output += `
19184
- ### Sessions (${sessions.length})
19185
- `;
19186
- sessions.forEach((s) => {
19187
- output += `- ${s.sessionId} (${s.taskFolder || "no task"})
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
- hive_subtask_update: tool({
19234
- description: "Update subtask status",
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
- subtask: tool.schema.string().describe('Subtask ID (e.g., "1.1")'),
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, subtask, status, feature: explicitFeature }) {
20054
+ async execute({ task, summary, feature: explicitFeature }) {
19242
20055
  const feature = resolveFeature(explicitFeature);
19243
20056
  if (!feature)
19244
- return "Error: No feature specified. Create a feature or provide feature param.";
19245
- try {
19246
- const updated = subtaskService.update(feature, task, subtask, status);
19247
- return `Subtask ${updated.id} updated: ${updated.status}`;
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
- hive_subtask_list: tool({
19254
- description: "List all subtasks for a task",
19255
- args: {
19256
- task: tool.schema.string().describe("Task folder name"),
19257
- feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
19258
- },
19259
- async execute({ task, feature: explicitFeature }) {
19260
- const feature = resolveFeature(explicitFeature);
19261
- if (!feature)
19262
- return "Error: No feature specified. Create a feature or provide feature param.";
19263
- try {
19264
- const subtasks = subtaskService.list(feature, task);
19265
- if (subtasks.length === 0)
19266
- return "No subtasks for this task.";
19267
- return subtasks.map((s) => {
19268
- const typeTag = s.type ? ` [${s.type}]` : "";
19269
- const statusIcon = s.status === "done" ? "✓" : s.status === "in_progress" ? "→" : "○";
19270
- return `${statusIcon} ${s.id}: ${s.name}${typeTag}`;
19271
- }).join(`
19272
- `);
19273
- } catch (e) {
19274
- return `Error: ${e.message}`;
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
- hive_subtask_spec_write: tool({
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
- hive_subtask_report_write: tool({
19299
- description: "Write report.md for a subtask (what was done)",
19300
- args: {
19301
- task: tool.schema.string().describe("Task folder name"),
19302
- subtask: tool.schema.string().describe('Subtask ID (e.g., "1.1")'),
19303
- content: tool.schema.string().describe("Report content (markdown)"),
19304
- feature: tool.schema.string().optional().describe("Feature name (defaults to active)")
19305
- },
19306
- async execute({ task, subtask, content, feature: explicitFeature }) {
19307
- const feature = resolveFeature(explicitFeature);
19308
- if (!feature)
19309
- return "Error: No feature specified. Create a feature or provide feature param.";
19310
- try {
19311
- const reportPath = subtaskService.writeReport(feature, task, subtask, content);
19312
- return `Subtask report written: ${reportPath}`;
19313
- } catch (e) {
19314
- return `Error: ${e.message}`;
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
  };