opencode-hive 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/agents/architect.d.ts +1 -1
- package/dist/agents/hive.d.ts +1 -1
- package/dist/agents/swarm.d.ts +1 -1
- package/dist/background/manager.d.ts +17 -2
- package/dist/index.js +518 -111
- package/package.json +1 -1
- package/skills/dispatching-parallel-agents/SKILL.md +20 -0
- package/skills/executing-plans/SKILL.md +18 -7
- package/skills/parallel-exploration/SKILL.md +74 -5
package/dist/index.js
CHANGED
|
@@ -12608,6 +12608,19 @@ When you have multiple unrelated failures (different test files, different subsy
|
|
|
12608
12608
|
|
|
12609
12609
|
**Core principle:** Dispatch one agent per independent problem domain. Let them work concurrently.
|
|
12610
12610
|
|
|
12611
|
+
## Prerequisite: Check Runnable Tasks
|
|
12612
|
+
|
|
12613
|
+
Before dispatching, use \`hive_status()\` to get the **runnable** list — tasks whose dependencies are all satisfied.
|
|
12614
|
+
|
|
12615
|
+
**Only dispatch tasks that are runnable.** Never start tasks with unmet dependencies.
|
|
12616
|
+
|
|
12617
|
+
Only \`done\` satisfies dependencies (not \`blocked\`, \`failed\`, \`partial\`, \`cancelled\`).
|
|
12618
|
+
|
|
12619
|
+
**Ask the operator first:**
|
|
12620
|
+
- Use \`question()\`: "These tasks are runnable and independent: [list]. Execute in parallel?"
|
|
12621
|
+
- Record the decision with \`hive_context_write({ name: "execution-decisions", content: "..." })\`
|
|
12622
|
+
- Proceed only after operator approval
|
|
12623
|
+
|
|
12611
12624
|
## When to Use
|
|
12612
12625
|
|
|
12613
12626
|
\`\`\`dot
|
|
@@ -12668,6 +12681,13 @@ hive_exec_start({ task: "03-fix-race-condition-tests" })
|
|
|
12668
12681
|
// All three run concurrently in isolated worktrees
|
|
12669
12682
|
\`\`\`
|
|
12670
12683
|
|
|
12684
|
+
Parallelize by issuing multiple task() calls in the same assistant message.
|
|
12685
|
+
|
|
12686
|
+
\`\`\`typescript
|
|
12687
|
+
task({ subagent_type: 'scout-researcher', prompt: 'Investigate failure A' })
|
|
12688
|
+
task({ subagent_type: 'scout-researcher', prompt: 'Investigate failure B' })
|
|
12689
|
+
\`\`\`
|
|
12690
|
+
|
|
12671
12691
|
### 4. Review and Integrate
|
|
12672
12692
|
|
|
12673
12693
|
When agents return:
|
|
@@ -12797,28 +12817,39 @@ Load plan, review critically, execute tasks in batches, report for review betwee
|
|
|
12797
12817
|
3. If concerns: Raise them with your human partner before starting
|
|
12798
12818
|
4. If no concerns: Create TodoWrite and proceed
|
|
12799
12819
|
|
|
12800
|
-
### Step 2:
|
|
12801
|
-
|
|
12820
|
+
### Step 2: Identify Runnable Tasks
|
|
12821
|
+
|
|
12822
|
+
Use \`hive_status()\` to get the **runnable** list — tasks with all dependencies satisfied.
|
|
12823
|
+
|
|
12824
|
+
Only \`done\` satisfies dependencies (not \`blocked\`, \`failed\`, \`partial\`, \`cancelled\`).
|
|
12825
|
+
|
|
12826
|
+
**When 2+ tasks are runnable:**
|
|
12827
|
+
- **Ask the operator** via \`question()\`: "Multiple tasks are runnable: [list]. Run in parallel, sequential, or a specific subset?"
|
|
12828
|
+
- Record the decision with \`hive_context_write({ name: "execution-decisions", content: "..." })\` for future reference
|
|
12829
|
+
|
|
12830
|
+
**When 1 task is runnable:** Proceed directly.
|
|
12831
|
+
|
|
12832
|
+
### Step 3: Execute Batch
|
|
12802
12833
|
|
|
12803
|
-
For each task:
|
|
12804
|
-
1. Mark as in_progress
|
|
12834
|
+
For each task in the batch:
|
|
12835
|
+
1. Mark as in_progress via \`hive_exec_start()\`
|
|
12805
12836
|
2. Follow each step exactly (plan has bite-sized steps)
|
|
12806
12837
|
3. Run verifications as specified
|
|
12807
12838
|
4. Mark as completed
|
|
12808
12839
|
|
|
12809
|
-
### Step
|
|
12840
|
+
### Step 4: Report
|
|
12810
12841
|
When batch complete:
|
|
12811
12842
|
- Show what was implemented
|
|
12812
12843
|
- Show verification output
|
|
12813
12844
|
- Say: "Ready for feedback."
|
|
12814
12845
|
|
|
12815
|
-
### Step
|
|
12846
|
+
### Step 5: Continue
|
|
12816
12847
|
Based on feedback:
|
|
12817
12848
|
- Apply changes if needed
|
|
12818
12849
|
- Execute next batch
|
|
12819
12850
|
- Repeat until complete
|
|
12820
12851
|
|
|
12821
|
-
### Step
|
|
12852
|
+
### Step 6: Complete Development
|
|
12822
12853
|
|
|
12823
12854
|
After all tasks complete and verified:
|
|
12824
12855
|
- Announce: "I'm using the finishing-a-development-branch skill to complete this work."
|
|
@@ -12913,19 +12944,23 @@ Ask one at a time, with the provided options. Store the answers in \`.hive/conte
|
|
|
12913
12944
|
},
|
|
12914
12945
|
{
|
|
12915
12946
|
name: "parallel-exploration",
|
|
12916
|
-
description: "Use when you need parallel, read-only exploration
|
|
12917
|
-
template: `# Parallel Exploration (
|
|
12947
|
+
description: "Use when you need parallel, read-only exploration with hive_background_* or task() (Scout fan-out)",
|
|
12948
|
+
template: `# Parallel Exploration (Scout Fan-Out)
|
|
12918
12949
|
|
|
12919
12950
|
## Overview
|
|
12920
12951
|
|
|
12921
12952
|
When you need to answer "where/how does X work?" across multiple domains (codebase, tests, docs, OSS), investigating sequentially wastes time. Each investigation is independent and can happen in parallel.
|
|
12922
12953
|
|
|
12923
|
-
**Core principle:** Decompose into independent sub-questions, spawn one
|
|
12954
|
+
**Core principle:** Decompose into independent sub-questions, spawn one task per sub-question, collect results asynchronously.
|
|
12924
12955
|
|
|
12925
12956
|
**Safe in Planning mode:** This is read-only exploration. It is OK to use during exploratory research even when there is no feature, no plan, and no approved tasks.
|
|
12926
12957
|
|
|
12927
12958
|
**This skill is for read-only research.** For parallel implementation work, use \`hive_skill("dispatching-parallel-agents")\` with \`hive_exec_start\`.
|
|
12928
12959
|
|
|
12960
|
+
**Two valid execution paths:**
|
|
12961
|
+
- **Path A (Hive background tools):** Use \`hive_background_task\`, \`hive_background_output\`, \`hive_background_cancel\` when available.
|
|
12962
|
+
- **Path B (Task mode):** Use native \`task()\` for delegation when background tools are not registered.
|
|
12963
|
+
|
|
12929
12964
|
## When to Use
|
|
12930
12965
|
|
|
12931
12966
|
**Default to this skill when:**
|
|
@@ -12968,6 +13003,7 @@ Split your investigation into 2-4 independent sub-questions. Good decomposition:
|
|
|
12968
13003
|
Launch all tasks before waiting for any results:
|
|
12969
13004
|
|
|
12970
13005
|
\`\`\`typescript
|
|
13006
|
+
// Path A: Hive background tools (when available)
|
|
12971
13007
|
// Fan-out: spawn all tasks first
|
|
12972
13008
|
hive_background_task({
|
|
12973
13009
|
agent: "scout-researcher",
|
|
@@ -13000,6 +13036,37 @@ hive_background_task({
|
|
|
13000
13036
|
})
|
|
13001
13037
|
\`\`\`
|
|
13002
13038
|
|
|
13039
|
+
\`\`\`typescript
|
|
13040
|
+
// Path B: Task mode (native task tool)
|
|
13041
|
+
// Parallelize by issuing multiple task() calls in the same assistant message.
|
|
13042
|
+
task({
|
|
13043
|
+
subagent_type: 'scout-researcher',
|
|
13044
|
+
description: 'Find hive_background_task implementation',
|
|
13045
|
+
prompt: \`Where is hive_background_task implemented and registered?
|
|
13046
|
+
- Find the tool definition
|
|
13047
|
+
- Find the plugin registration
|
|
13048
|
+
- Return file paths with line numbers\`,
|
|
13049
|
+
});
|
|
13050
|
+
|
|
13051
|
+
task({
|
|
13052
|
+
subagent_type: 'scout-researcher',
|
|
13053
|
+
description: 'Analyze background task concurrency',
|
|
13054
|
+
prompt: \`How does background task concurrency/queueing work?
|
|
13055
|
+
- Find the manager/scheduler code
|
|
13056
|
+
- Document the concurrency model
|
|
13057
|
+
- Return file paths with evidence\`,
|
|
13058
|
+
});
|
|
13059
|
+
|
|
13060
|
+
task({
|
|
13061
|
+
subagent_type: 'scout-researcher',
|
|
13062
|
+
description: 'Find parent notification mechanism',
|
|
13063
|
+
prompt: \`How does parent notification work for background tasks?
|
|
13064
|
+
- Where is the notification built?
|
|
13065
|
+
- How is it sent to the parent session?
|
|
13066
|
+
- Return file paths with evidence\`,
|
|
13067
|
+
});
|
|
13068
|
+
\`\`\`
|
|
13069
|
+
|
|
13003
13070
|
**Key points:**
|
|
13004
13071
|
- Use \`agent: "scout-researcher"\` for read-only exploration
|
|
13005
13072
|
- Use \`sync: false\` to return immediately (non-blocking)
|
|
@@ -13020,6 +13087,7 @@ You'll receive a \`<system-reminder>\` notification when each task completes.
|
|
|
13020
13087
|
When notified of completion, retrieve results:
|
|
13021
13088
|
|
|
13022
13089
|
\`\`\`typescript
|
|
13090
|
+
// Path A: Hive background tools
|
|
13023
13091
|
// Get output from completed task
|
|
13024
13092
|
hive_background_output({
|
|
13025
13093
|
task_id: "task-abc123",
|
|
@@ -13058,6 +13126,7 @@ Combine results from all tasks:
|
|
|
13058
13126
|
Cancel tasks that are no longer needed:
|
|
13059
13127
|
|
|
13060
13128
|
\`\`\`typescript
|
|
13129
|
+
// Path A: Hive background tools
|
|
13061
13130
|
// Cancel specific task
|
|
13062
13131
|
hive_background_cancel({ task_id: "task-abc123" })
|
|
13063
13132
|
|
|
@@ -13120,6 +13189,7 @@ Return:
|
|
|
13120
13189
|
|
|
13121
13190
|
**Fan-out:**
|
|
13122
13191
|
\`\`\`typescript
|
|
13192
|
+
// Path A: Hive background tools
|
|
13123
13193
|
// Task 1: Implementation
|
|
13124
13194
|
hive_background_task({
|
|
13125
13195
|
agent: "scout-researcher",
|
|
@@ -13145,6 +13215,28 @@ hive_background_task({
|
|
|
13145
13215
|
})
|
|
13146
13216
|
\`\`\`
|
|
13147
13217
|
|
|
13218
|
+
\`\`\`typescript
|
|
13219
|
+
// Path B: Task mode (native task tool)
|
|
13220
|
+
// Parallelize by issuing multiple task() calls in the same assistant message.
|
|
13221
|
+
task({
|
|
13222
|
+
subagent_type: 'scout-researcher',
|
|
13223
|
+
description: 'Find hive_background_task implementation',
|
|
13224
|
+
prompt: 'Where is hive_background_task implemented? Find tool definition and registration.',
|
|
13225
|
+
});
|
|
13226
|
+
|
|
13227
|
+
task({
|
|
13228
|
+
subagent_type: 'scout-researcher',
|
|
13229
|
+
description: 'Analyze concurrency model',
|
|
13230
|
+
prompt: 'How does background task concurrency work? Find the manager/scheduler.',
|
|
13231
|
+
});
|
|
13232
|
+
|
|
13233
|
+
task({
|
|
13234
|
+
subagent_type: 'scout-researcher',
|
|
13235
|
+
description: 'Find notification mechanism',
|
|
13236
|
+
prompt: 'How are parent sessions notified of task completion?',
|
|
13237
|
+
});
|
|
13238
|
+
\`\`\`
|
|
13239
|
+
|
|
13148
13240
|
**Results:**
|
|
13149
13241
|
- Task 1: Found \`background-tools.ts\` (tool definition), \`index.ts\` (registration)
|
|
13150
13242
|
- Task 2: Found \`manager.ts\` with concurrency=3 default, queue-based scheduling
|
|
@@ -13169,6 +13261,13 @@ hive_background_task({ ..., sync: false }) // Returns immediately
|
|
|
13169
13261
|
// ... later, collect results with hive_background_output
|
|
13170
13262
|
\`\`\`
|
|
13171
13263
|
|
|
13264
|
+
\`\`\`typescript
|
|
13265
|
+
// GOOD (task mode): Spawn all in the same assistant message
|
|
13266
|
+
task({ ... });
|
|
13267
|
+
task({ ... });
|
|
13268
|
+
task({ ... });
|
|
13269
|
+
\`\`\`
|
|
13270
|
+
|
|
13172
13271
|
**Too many tasks (diminishing returns):**
|
|
13173
13272
|
- 2-4 tasks: Good parallelization
|
|
13174
13273
|
- 5+ tasks: Overhead exceeds benefit, harder to synthesize
|
|
@@ -13192,8 +13291,9 @@ hive_background_task({ ..., sync: false }) // Returns immediately
|
|
|
13192
13291
|
|
|
13193
13292
|
After using this pattern, verify:
|
|
13194
13293
|
- [ ] All tasks spawned before collecting any results (true fan-out)
|
|
13195
|
-
- [ ] Received notifications for completed tasks
|
|
13196
|
-
- [ ] Successfully retrieved output with \`hive_background_output\`
|
|
13294
|
+
- [ ] Received notifications for completed tasks (Path A)
|
|
13295
|
+
- [ ] Successfully retrieved output with \`hive_background_output\` (Path A)
|
|
13296
|
+
- [ ] Verified \`task()\` fan-out pattern used when in task mode (Path B)
|
|
13197
13297
|
- [ ] Synthesized findings into coherent answer`
|
|
13198
13298
|
},
|
|
13199
13299
|
{
|
|
@@ -14281,9 +14381,11 @@ Run \`hive_status()\` or \`hive_feature_list()\` to detect phase:
|
|
|
14281
14381
|
### Delegation
|
|
14282
14382
|
|
|
14283
14383
|
- Single-scout research → \`hive_background_task(agent: "scout-researcher", sync: true, ...)\` (blocks until complete, simpler flow)
|
|
14284
|
-
- Parallel exploration → Load \`hive_skill("parallel-exploration")\` and
|
|
14384
|
+
- Parallel exploration → Load \`hive_skill("parallel-exploration")\` and follow the task vs hive mode delegation guidance.
|
|
14285
14385
|
- Implementation → \`hive_exec_start(task)\` (spawns Forager)
|
|
14286
14386
|
|
|
14387
|
+
In task mode, use task() for research fan-out; in hive mode, use hive_background_task.
|
|
14388
|
+
|
|
14287
14389
|
During Planning, default to synchronous exploration (\`sync: true\`). If async/parallel exploration would help, ask the user via \`question()\`.
|
|
14288
14390
|
|
|
14289
14391
|
### Context Persistence
|
|
@@ -14306,7 +14408,7 @@ Load when detailed guidance needed:
|
|
|
14306
14408
|
- \`hive_skill("brainstorming")\` - exploring ideas and requirements
|
|
14307
14409
|
- \`hive_skill("writing-plans")\` - structuring implementation plans
|
|
14308
14410
|
- \`hive_skill("dispatching-parallel-agents")\` - parallel task delegation
|
|
14309
|
-
- \`hive_skill("parallel-exploration")\` - parallel read-only research via hive_background_task (Scout fan-out)
|
|
14411
|
+
- \`hive_skill("parallel-exploration")\` - parallel read-only research via task() or hive_background_task (Scout fan-out)
|
|
14310
14412
|
- \`hive_skill("executing-plans")\` - step-by-step plan execution
|
|
14311
14413
|
|
|
14312
14414
|
Load ONE skill at a time. Only when you need guidance beyond this prompt.
|
|
@@ -14365,6 +14467,13 @@ If yes → \`task({ subagent_type: "hygienic", prompt: "Review plan..." })\`
|
|
|
14365
14467
|
|
|
14366
14468
|
*Active when: plan approved, tasks exist*
|
|
14367
14469
|
|
|
14470
|
+
### Task Dependencies (Always Check)
|
|
14471
|
+
|
|
14472
|
+
Use \`hive_status()\` to see **runnable** tasks (dependencies satisfied) and **blockedBy** info.
|
|
14473
|
+
- Only start tasks from the runnable list
|
|
14474
|
+
- When 2+ tasks are runnable: ask operator via \`question()\` before parallelizing
|
|
14475
|
+
- Record execution decisions with \`hive_context_write({ name: "execution-decisions", ... })\`
|
|
14476
|
+
|
|
14368
14477
|
### When to Load Skills
|
|
14369
14478
|
|
|
14370
14479
|
- Multiple independent tasks → \`hive_skill("dispatching-parallel-agents")\`
|
|
@@ -14500,7 +14609,9 @@ Plan MUST include:
|
|
|
14500
14609
|
**Never:**
|
|
14501
14610
|
- Execute code (you plan, not implement)
|
|
14502
14611
|
- Spawn implementation/coding workers (Swarm (Orchestrator) does this); read-only research delegation to Scout is allowed
|
|
14503
|
-
-
|
|
14612
|
+
- You may use task() to delegate read-only research to Scout and plan review to Hygienic.
|
|
14613
|
+
- Never use task() to delegate implementation or coding work.
|
|
14614
|
+
- Tool availability depends on delegateMode.
|
|
14504
14615
|
- Skip discovery for complex tasks
|
|
14505
14616
|
- Assume when uncertain - ASK
|
|
14506
14617
|
|
|
@@ -14530,16 +14641,26 @@ Delegate by default. Work yourself only when trivial.
|
|
|
14530
14641
|
|------|--------|--------|
|
|
14531
14642
|
| Trivial | Single file, known location | Direct tools only |
|
|
14532
14643
|
| Explicit | Specific file/line, clear command | Execute directly |
|
|
14533
|
-
| Exploratory | "How does X work?" | Delegate to Scout via
|
|
14644
|
+
| Exploratory | "How does X work?" | Delegate to Scout via the parallel-exploration playbook. |
|
|
14534
14645
|
| Open-ended | "Improve", "Refactor" | Assess first, then delegate |
|
|
14535
14646
|
| Ambiguous | Unclear scope | Ask ONE clarifying question |
|
|
14536
14647
|
|
|
14537
14648
|
## Delegation Check (Before Acting)
|
|
14538
14649
|
|
|
14650
|
+
### Task Dependencies (Always Check)
|
|
14651
|
+
|
|
14652
|
+
Use \`hive_status()\` to see **runnable** tasks (dependencies satisfied) and **blockedBy** info.
|
|
14653
|
+
- Only start tasks from the runnable list
|
|
14654
|
+
- When 2+ tasks are runnable: ask operator via \`question()\` before parallelizing
|
|
14655
|
+
- Record execution decisions with \`hive_context_write({ name: "execution-decisions", ... })\`
|
|
14656
|
+
|
|
14657
|
+
### Standard Checks
|
|
14658
|
+
|
|
14539
14659
|
1. Is there a specialized agent that matches?
|
|
14540
14660
|
2. Can I do it myself FOR SURE? REALLY?
|
|
14541
14661
|
3. Does this require external system data (DBs/APIs/3rd-party tools)?
|
|
14542
14662
|
→ If external data needed: Load \`hive_skill("parallel-exploration")\` for parallel Scout fan-out
|
|
14663
|
+
In task mode, use task() for research fan-out; in hive mode, use hive_background_task.
|
|
14543
14664
|
During Planning, default to synchronous exploration. If async exploration would help, ask the user via \`question()\` and follow the onboarding preferences.
|
|
14544
14665
|
→ Default: DELEGATE
|
|
14545
14666
|
|
|
@@ -14562,7 +14683,7 @@ hive_exec_start({ task: "01-task-name" })
|
|
|
14562
14683
|
hive_background_task({ agent: "forager-worker", prompt: "...", sync: false })
|
|
14563
14684
|
// If external system data is needed (parallel exploration):
|
|
14564
14685
|
// Load hive_skill("parallel-exploration") for the full playbook, then:
|
|
14565
|
-
|
|
14686
|
+
// In task mode, use task() for research fan-out; in hive mode, use hive_background_task.
|
|
14566
14687
|
\`\`\`
|
|
14567
14688
|
|
|
14568
14689
|
**Sync Mode Guidance:**
|
|
@@ -15860,7 +15981,7 @@ var DEFAULT_HIVE_CONFIG = {
|
|
|
15860
15981
|
model: DEFAULT_AGENT_MODELS["scout-researcher"],
|
|
15861
15982
|
temperature: 0.5,
|
|
15862
15983
|
skills: [],
|
|
15863
|
-
autoLoadSkills: [
|
|
15984
|
+
autoLoadSkills: []
|
|
15864
15985
|
},
|
|
15865
15986
|
"forager-worker": {
|
|
15866
15987
|
model: DEFAULT_AGENT_MODELS["forager-worker"],
|
|
@@ -16356,6 +16477,7 @@ class TaskService {
|
|
|
16356
16477
|
throw new Error(`No plan.md found for feature '${featureName}'`);
|
|
16357
16478
|
}
|
|
16358
16479
|
const planTasks = this.parseTasksFromPlan(planContent);
|
|
16480
|
+
this.validateDependencyGraph(planTasks, featureName);
|
|
16359
16481
|
const existingTasks = this.list(featureName);
|
|
16360
16482
|
const result = {
|
|
16361
16483
|
created: [],
|
|
@@ -16388,7 +16510,7 @@ class TaskService {
|
|
|
16388
16510
|
}
|
|
16389
16511
|
for (const planTask of planTasks) {
|
|
16390
16512
|
if (!existingByName.has(planTask.folder)) {
|
|
16391
|
-
this.createFromPlan(featureName, planTask, planTasks);
|
|
16513
|
+
this.createFromPlan(featureName, planTask, planTasks, planContent);
|
|
16392
16514
|
result.created.push(planTask.folder);
|
|
16393
16515
|
}
|
|
16394
16516
|
}
|
|
@@ -16409,49 +16531,156 @@ class TaskService {
|
|
|
16409
16531
|
writeJson(getTaskStatusPath(this.projectRoot, featureName, folder), status);
|
|
16410
16532
|
return folder;
|
|
16411
16533
|
}
|
|
16412
|
-
createFromPlan(featureName, task, allTasks) {
|
|
16534
|
+
createFromPlan(featureName, task, allTasks, planContent) {
|
|
16413
16535
|
const taskPath = getTaskPath(this.projectRoot, featureName, task.folder);
|
|
16414
16536
|
ensureDir(taskPath);
|
|
16537
|
+
const dependsOn = this.resolveDependencies(task, allTasks);
|
|
16415
16538
|
const status = {
|
|
16416
16539
|
status: "pending",
|
|
16417
16540
|
origin: "plan",
|
|
16418
|
-
planTitle: task.name
|
|
16541
|
+
planTitle: task.name,
|
|
16542
|
+
dependsOn
|
|
16419
16543
|
};
|
|
16420
16544
|
writeJson(getTaskStatusPath(this.projectRoot, featureName, task.folder), status);
|
|
16545
|
+
const specContent = this.buildSpecContent({
|
|
16546
|
+
featureName,
|
|
16547
|
+
task,
|
|
16548
|
+
dependsOn,
|
|
16549
|
+
allTasks,
|
|
16550
|
+
planContent
|
|
16551
|
+
});
|
|
16552
|
+
writeText(getTaskSpecPath(this.projectRoot, featureName, task.folder), specContent);
|
|
16553
|
+
}
|
|
16554
|
+
buildSpecContent(params) {
|
|
16555
|
+
const { featureName, task, dependsOn, allTasks, planContent, contextFiles = [], completedTasks = [] } = params;
|
|
16421
16556
|
const specLines = [
|
|
16422
|
-
`# Task
|
|
16557
|
+
`# Task: ${task.folder}`,
|
|
16423
16558
|
"",
|
|
16424
|
-
|
|
16425
|
-
`**Folder:** ${task.folder}`,
|
|
16426
|
-
`**Status:** pending`,
|
|
16559
|
+
`## Feature: ${featureName}`,
|
|
16427
16560
|
"",
|
|
16428
|
-
"
|
|
16429
|
-
"",
|
|
16430
|
-
"## Description",
|
|
16431
|
-
"",
|
|
16432
|
-
task.description || "_No description provided in plan_",
|
|
16561
|
+
"## Dependencies",
|
|
16433
16562
|
""
|
|
16434
16563
|
];
|
|
16435
|
-
if (
|
|
16436
|
-
const
|
|
16437
|
-
|
|
16438
|
-
|
|
16439
|
-
|
|
16440
|
-
|
|
16564
|
+
if (dependsOn.length > 0) {
|
|
16565
|
+
for (const dep of dependsOn) {
|
|
16566
|
+
const depTask = allTasks.find((t) => t.folder === dep);
|
|
16567
|
+
if (depTask) {
|
|
16568
|
+
specLines.push(`- **${depTask.order}. ${depTask.name}** (${dep})`);
|
|
16569
|
+
} else {
|
|
16570
|
+
specLines.push(`- ${dep}`);
|
|
16441
16571
|
}
|
|
16442
|
-
specLines.push("");
|
|
16443
16572
|
}
|
|
16573
|
+
} else {
|
|
16574
|
+
specLines.push("_None_");
|
|
16575
|
+
}
|
|
16576
|
+
specLines.push("", "## Plan Section", "");
|
|
16577
|
+
const planSection = this.extractPlanSection(planContent ?? null, task);
|
|
16578
|
+
if (planSection) {
|
|
16579
|
+
specLines.push(planSection.trim());
|
|
16580
|
+
} else {
|
|
16581
|
+
specLines.push("_No plan section available._");
|
|
16582
|
+
}
|
|
16583
|
+
specLines.push("");
|
|
16584
|
+
if (contextFiles.length > 0) {
|
|
16585
|
+
const contextCompiled = contextFiles.map((f) => `## ${f.name}
|
|
16586
|
+
|
|
16587
|
+
${f.content}`).join(`
|
|
16588
|
+
|
|
16589
|
+
---
|
|
16590
|
+
|
|
16591
|
+
`);
|
|
16592
|
+
specLines.push("## Context", "", contextCompiled, "");
|
|
16593
|
+
}
|
|
16594
|
+
if (completedTasks.length > 0) {
|
|
16595
|
+
const completedLines = completedTasks.map((t) => `- ${t.name}: ${t.summary}`);
|
|
16596
|
+
specLines.push("## Completed Tasks", "", ...completedLines, "");
|
|
16597
|
+
}
|
|
16598
|
+
return specLines.join(`
|
|
16599
|
+
`);
|
|
16600
|
+
}
|
|
16601
|
+
extractPlanSection(planContent, task) {
|
|
16602
|
+
if (!planContent)
|
|
16603
|
+
return null;
|
|
16604
|
+
const escapedTitle = task.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16605
|
+
const titleRegex = new RegExp(`###\\s*\\d+\\.\\s*${escapedTitle}[\\s\\S]*?(?=###|$)`, "i");
|
|
16606
|
+
let taskMatch = planContent.match(titleRegex);
|
|
16607
|
+
if (!taskMatch && task.order > 0) {
|
|
16608
|
+
const orderRegex = new RegExp(`###\\s*${task.order}\\.\\s*[^\\n]+[\\s\\S]*?(?=###|$)`, "i");
|
|
16609
|
+
taskMatch = planContent.match(orderRegex);
|
|
16610
|
+
}
|
|
16611
|
+
return taskMatch ? taskMatch[0].trim() : null;
|
|
16612
|
+
}
|
|
16613
|
+
resolveDependencies(task, allTasks) {
|
|
16614
|
+
if (task.dependsOnNumbers !== null && task.dependsOnNumbers.length === 0) {
|
|
16615
|
+
return [];
|
|
16444
16616
|
}
|
|
16445
|
-
|
|
16446
|
-
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
|
|
16617
|
+
if (task.dependsOnNumbers !== null) {
|
|
16618
|
+
return task.dependsOnNumbers.map((num) => allTasks.find((t) => t.order === num)?.folder).filter((folder) => folder !== undefined);
|
|
16619
|
+
}
|
|
16620
|
+
if (task.order === 1) {
|
|
16621
|
+
return [];
|
|
16622
|
+
}
|
|
16623
|
+
const previousTask = allTasks.find((t) => t.order === task.order - 1);
|
|
16624
|
+
return previousTask ? [previousTask.folder] : [];
|
|
16625
|
+
}
|
|
16626
|
+
validateDependencyGraph(tasks, featureName) {
|
|
16627
|
+
const taskNumbers = new Set(tasks.map((t) => t.order));
|
|
16628
|
+
for (const task of tasks) {
|
|
16629
|
+
if (task.dependsOnNumbers === null) {
|
|
16630
|
+
continue;
|
|
16631
|
+
}
|
|
16632
|
+
for (const depNum of task.dependsOnNumbers) {
|
|
16633
|
+
if (depNum === task.order) {
|
|
16634
|
+
throw new Error(`Invalid dependency graph in plan.md: Self-dependency detected for task ${task.order} ("${task.name}"). ` + `A task cannot depend on itself. Please fix the "Depends on:" line in plan.md.`);
|
|
16635
|
+
}
|
|
16636
|
+
if (!taskNumbers.has(depNum)) {
|
|
16637
|
+
throw new Error(`Invalid dependency graph in plan.md: Unknown task number ${depNum} referenced in dependencies for task ${task.order} ("${task.name}"). ` + `Available task numbers are: ${Array.from(taskNumbers).sort((a, b) => a - b).join(", ")}. ` + `Please fix the "Depends on:" line in plan.md.`);
|
|
16638
|
+
}
|
|
16639
|
+
}
|
|
16640
|
+
}
|
|
16641
|
+
this.detectCycles(tasks);
|
|
16642
|
+
}
|
|
16643
|
+
detectCycles(tasks) {
|
|
16644
|
+
const taskByOrder = new Map(tasks.map((t) => [t.order, t]));
|
|
16645
|
+
const getDependencies = (task) => {
|
|
16646
|
+
if (task.dependsOnNumbers !== null) {
|
|
16647
|
+
return task.dependsOnNumbers;
|
|
16648
|
+
}
|
|
16649
|
+
if (task.order === 1) {
|
|
16650
|
+
return [];
|
|
16651
|
+
}
|
|
16652
|
+
return [task.order - 1];
|
|
16653
|
+
};
|
|
16654
|
+
const visited = new Map;
|
|
16655
|
+
const path32 = [];
|
|
16656
|
+
const dfs = (taskOrder) => {
|
|
16657
|
+
const state = visited.get(taskOrder);
|
|
16658
|
+
if (state === 2) {
|
|
16659
|
+
return;
|
|
16660
|
+
}
|
|
16661
|
+
if (state === 1) {
|
|
16662
|
+
const cycleStart = path32.indexOf(taskOrder);
|
|
16663
|
+
const cyclePath = [...path32.slice(cycleStart), taskOrder];
|
|
16664
|
+
const cycleDesc = cyclePath.join(" -> ");
|
|
16665
|
+
throw new Error(`Invalid dependency graph in plan.md: Cycle detected in task dependencies: ${cycleDesc}. ` + `Tasks cannot have circular dependencies. Please fix the "Depends on:" lines in plan.md.`);
|
|
16666
|
+
}
|
|
16667
|
+
visited.set(taskOrder, 1);
|
|
16668
|
+
path32.push(taskOrder);
|
|
16669
|
+
const task = taskByOrder.get(taskOrder);
|
|
16670
|
+
if (task) {
|
|
16671
|
+
const deps = getDependencies(task);
|
|
16672
|
+
for (const depOrder of deps) {
|
|
16673
|
+
dfs(depOrder);
|
|
16674
|
+
}
|
|
16675
|
+
}
|
|
16676
|
+
path32.pop();
|
|
16677
|
+
visited.set(taskOrder, 2);
|
|
16678
|
+
};
|
|
16679
|
+
for (const task of tasks) {
|
|
16680
|
+
if (!visited.has(task.order)) {
|
|
16681
|
+
dfs(task.order);
|
|
16450
16682
|
}
|
|
16451
|
-
specLines.push("");
|
|
16452
16683
|
}
|
|
16453
|
-
writeText(getTaskSpecPath(this.projectRoot, featureName, task.folder), specLines.join(`
|
|
16454
|
-
`));
|
|
16455
16684
|
}
|
|
16456
16685
|
writeSpec(featureName, taskFolder, content) {
|
|
16457
16686
|
const specPath = getTaskSpecPath(this.projectRoot, featureName, taskFolder);
|
|
@@ -16542,6 +16771,7 @@ class TaskService {
|
|
|
16542
16771
|
`);
|
|
16543
16772
|
let currentTask = null;
|
|
16544
16773
|
let descriptionLines = [];
|
|
16774
|
+
const dependsOnRegex = /^\s*\*{0,2}Depends\s+on\*{0,2}\s*:\s*(.+)$/i;
|
|
16545
16775
|
for (const line of lines) {
|
|
16546
16776
|
const taskMatch = line.match(/^###\s+(\d+)\.\s+(.+)$/);
|
|
16547
16777
|
if (taskMatch) {
|
|
@@ -16558,7 +16788,8 @@ class TaskService {
|
|
|
16558
16788
|
folder,
|
|
16559
16789
|
order,
|
|
16560
16790
|
name: rawName,
|
|
16561
|
-
description: ""
|
|
16791
|
+
description: "",
|
|
16792
|
+
dependsOnNumbers: null
|
|
16562
16793
|
};
|
|
16563
16794
|
descriptionLines = [];
|
|
16564
16795
|
} else if (currentTask) {
|
|
@@ -16569,6 +16800,16 @@ class TaskService {
|
|
|
16569
16800
|
currentTask = null;
|
|
16570
16801
|
descriptionLines = [];
|
|
16571
16802
|
} else {
|
|
16803
|
+
const dependsMatch = line.match(dependsOnRegex);
|
|
16804
|
+
if (dependsMatch) {
|
|
16805
|
+
const value = dependsMatch[1].trim().toLowerCase();
|
|
16806
|
+
if (value === "none") {
|
|
16807
|
+
currentTask.dependsOnNumbers = [];
|
|
16808
|
+
} else {
|
|
16809
|
+
const numbers = value.split(/[,\s]+/).map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n));
|
|
16810
|
+
currentTask.dependsOnNumbers = numbers;
|
|
16811
|
+
}
|
|
16812
|
+
}
|
|
16572
16813
|
descriptionLines.push(line);
|
|
16573
16814
|
}
|
|
16574
16815
|
}
|
|
@@ -21356,6 +21597,62 @@ class ConfigService {
|
|
|
21356
21597
|
return this.getDelegateMode() === "hive";
|
|
21357
21598
|
}
|
|
21358
21599
|
}
|
|
21600
|
+
function computeRunnableAndBlocked(tasks) {
|
|
21601
|
+
const statusByFolder = new Map;
|
|
21602
|
+
for (const task of tasks) {
|
|
21603
|
+
statusByFolder.set(task.folder, task.status);
|
|
21604
|
+
}
|
|
21605
|
+
const runnable = [];
|
|
21606
|
+
const blocked = {};
|
|
21607
|
+
const effectiveDepsByFolder = buildEffectiveDependencies(tasks);
|
|
21608
|
+
for (const task of tasks) {
|
|
21609
|
+
if (task.status !== "pending") {
|
|
21610
|
+
continue;
|
|
21611
|
+
}
|
|
21612
|
+
const deps = effectiveDepsByFolder.get(task.folder) ?? [];
|
|
21613
|
+
const unmetDeps = deps.filter((dep) => {
|
|
21614
|
+
const depStatus = statusByFolder.get(dep);
|
|
21615
|
+
return depStatus !== "done";
|
|
21616
|
+
});
|
|
21617
|
+
if (unmetDeps.length === 0) {
|
|
21618
|
+
runnable.push(task.folder);
|
|
21619
|
+
} else {
|
|
21620
|
+
blocked[task.folder] = unmetDeps;
|
|
21621
|
+
}
|
|
21622
|
+
}
|
|
21623
|
+
return { runnable, blocked };
|
|
21624
|
+
}
|
|
21625
|
+
function buildEffectiveDependencies(tasks) {
|
|
21626
|
+
const orderByFolder = new Map;
|
|
21627
|
+
const folderByOrder = new Map;
|
|
21628
|
+
for (const task of tasks) {
|
|
21629
|
+
const match = task.folder.match(/^(\d+)-/);
|
|
21630
|
+
if (!match) {
|
|
21631
|
+
orderByFolder.set(task.folder, null);
|
|
21632
|
+
continue;
|
|
21633
|
+
}
|
|
21634
|
+
const order = parseInt(match[1], 10);
|
|
21635
|
+
orderByFolder.set(task.folder, order);
|
|
21636
|
+
if (!folderByOrder.has(order)) {
|
|
21637
|
+
folderByOrder.set(order, task.folder);
|
|
21638
|
+
}
|
|
21639
|
+
}
|
|
21640
|
+
const effectiveDeps = new Map;
|
|
21641
|
+
for (const task of tasks) {
|
|
21642
|
+
if (task.dependsOn !== undefined) {
|
|
21643
|
+
effectiveDeps.set(task.folder, task.dependsOn);
|
|
21644
|
+
continue;
|
|
21645
|
+
}
|
|
21646
|
+
const order = orderByFolder.get(task.folder);
|
|
21647
|
+
if (!order || order <= 1) {
|
|
21648
|
+
effectiveDeps.set(task.folder, []);
|
|
21649
|
+
continue;
|
|
21650
|
+
}
|
|
21651
|
+
const previousFolder = folderByOrder.get(order - 1);
|
|
21652
|
+
effectiveDeps.set(task.folder, previousFolder ? [previousFolder] : []);
|
|
21653
|
+
}
|
|
21654
|
+
return effectiveDeps;
|
|
21655
|
+
}
|
|
21359
21656
|
|
|
21360
21657
|
// src/utils/worker-prompt.ts
|
|
21361
21658
|
function buildWorkerPrompt(params) {
|
|
@@ -22634,7 +22931,11 @@ class BackgroundManager {
|
|
|
22634
22931
|
parts: [{ type: "text", text: options.prompt }],
|
|
22635
22932
|
tools: {
|
|
22636
22933
|
background_task: false,
|
|
22637
|
-
delegate: false
|
|
22934
|
+
delegate: false,
|
|
22935
|
+
hive_background_task: false,
|
|
22936
|
+
hive_background_output: false,
|
|
22937
|
+
hive_background_cancel: false,
|
|
22938
|
+
task: false
|
|
22638
22939
|
},
|
|
22639
22940
|
...normalizedVariant !== undefined && { variant: normalizedVariant }
|
|
22640
22941
|
}
|
|
@@ -22649,6 +22950,36 @@ class BackgroundManager {
|
|
|
22649
22950
|
};
|
|
22650
22951
|
}
|
|
22651
22952
|
checkHiveTaskOrdering(feature, taskFolder) {
|
|
22953
|
+
const taskStatus = this.taskService.getRawStatus(feature, taskFolder);
|
|
22954
|
+
if (taskStatus?.dependsOn !== undefined) {
|
|
22955
|
+
return this.checkDependencies(feature, taskFolder, taskStatus.dependsOn);
|
|
22956
|
+
}
|
|
22957
|
+
return this.checkNumericOrdering(feature, taskFolder);
|
|
22958
|
+
}
|
|
22959
|
+
checkDependencies(feature, taskFolder, dependsOn) {
|
|
22960
|
+
if (dependsOn.length === 0) {
|
|
22961
|
+
return { allowed: true };
|
|
22962
|
+
}
|
|
22963
|
+
const unmetDeps = [];
|
|
22964
|
+
for (const depFolder of dependsOn) {
|
|
22965
|
+
const depStatus = this.taskService.getRawStatus(feature, depFolder);
|
|
22966
|
+
if (!depStatus || depStatus.status !== "done") {
|
|
22967
|
+
unmetDeps.push({
|
|
22968
|
+
folder: depFolder,
|
|
22969
|
+
status: depStatus?.status ?? "unknown"
|
|
22970
|
+
});
|
|
22971
|
+
}
|
|
22972
|
+
}
|
|
22973
|
+
if (unmetDeps.length > 0) {
|
|
22974
|
+
const depList = unmetDeps.map((d) => `"${d.folder}" (${d.status})`).join(", ");
|
|
22975
|
+
return {
|
|
22976
|
+
allowed: false,
|
|
22977
|
+
error: `Dependency constraint: Task "${taskFolder}" cannot start - dependencies not done: ${depList}. ` + `Only tasks with status 'done' satisfy dependencies.`
|
|
22978
|
+
};
|
|
22979
|
+
}
|
|
22980
|
+
return { allowed: true };
|
|
22981
|
+
}
|
|
22982
|
+
checkNumericOrdering(feature, taskFolder) {
|
|
22652
22983
|
const orderMatch = taskFolder.match(/^(\d+)-/);
|
|
22653
22984
|
if (!orderMatch) {
|
|
22654
22985
|
return { allowed: true };
|
|
@@ -22716,7 +23047,7 @@ class BackgroundManager {
|
|
|
22716
23047
|
**Description:** ${task.description}
|
|
22717
23048
|
**Agent:** ${task.agent}${errorLine}
|
|
22718
23049
|
|
|
22719
|
-
Use \`
|
|
23050
|
+
Use \`hive_background_output({ task_id: "${task.taskId}" })\` to retrieve the result.
|
|
22720
23051
|
</system-reminder>`;
|
|
22721
23052
|
try {
|
|
22722
23053
|
await this.client.session.prompt({
|
|
@@ -22913,6 +23244,22 @@ function createBackgroundTools(manager, client, configService) {
|
|
|
22913
23244
|
attempt
|
|
22914
23245
|
}, toolContext) {
|
|
22915
23246
|
const ctx = toolContext;
|
|
23247
|
+
const ALLOWED_CALLERS = new Set([
|
|
23248
|
+
"hive-master",
|
|
23249
|
+
"architect-planner",
|
|
23250
|
+
"swarm-orchestrator"
|
|
23251
|
+
]);
|
|
23252
|
+
const callerAgent = ctx?.agent;
|
|
23253
|
+
if (!callerAgent || !ALLOWED_CALLERS.has(callerAgent)) {
|
|
23254
|
+
const output = {
|
|
23255
|
+
provider: "hive",
|
|
23256
|
+
task_id: "",
|
|
23257
|
+
session_id: "",
|
|
23258
|
+
status: "error",
|
|
23259
|
+
error: `Agent "${callerAgent ?? "unknown"}" is not allowed to spawn background tasks. Only orchestrator agents (${[...ALLOWED_CALLERS].join(", ")}) can delegate.`
|
|
23260
|
+
};
|
|
23261
|
+
return JSON.stringify(output, null, 2);
|
|
23262
|
+
}
|
|
22916
23263
|
let resolvedPrompt = prompt;
|
|
22917
23264
|
if (promptFile) {
|
|
22918
23265
|
const baseDir = workdir || process.cwd();
|
|
@@ -23424,6 +23771,43 @@ To unblock: Remove .hive/features/${feature}/BLOCKED`;
|
|
|
23424
23771
|
}
|
|
23425
23772
|
return null;
|
|
23426
23773
|
};
|
|
23774
|
+
const checkDependencies = (feature, taskFolder) => {
|
|
23775
|
+
const taskStatus = taskService.getRawStatus(feature, taskFolder);
|
|
23776
|
+
if (!taskStatus) {
|
|
23777
|
+
return { allowed: true };
|
|
23778
|
+
}
|
|
23779
|
+
const tasks = taskService.list(feature).map((task) => {
|
|
23780
|
+
const status = taskService.getRawStatus(feature, task.folder);
|
|
23781
|
+
return {
|
|
23782
|
+
folder: task.folder,
|
|
23783
|
+
status: task.status,
|
|
23784
|
+
dependsOn: status?.dependsOn
|
|
23785
|
+
};
|
|
23786
|
+
});
|
|
23787
|
+
const effectiveDeps = buildEffectiveDependencies(tasks);
|
|
23788
|
+
const deps = effectiveDeps.get(taskFolder) ?? [];
|
|
23789
|
+
if (deps.length === 0) {
|
|
23790
|
+
return { allowed: true };
|
|
23791
|
+
}
|
|
23792
|
+
const unmetDeps = [];
|
|
23793
|
+
for (const depFolder of deps) {
|
|
23794
|
+
const depStatus = taskService.getRawStatus(feature, depFolder);
|
|
23795
|
+
if (!depStatus || depStatus.status !== "done") {
|
|
23796
|
+
unmetDeps.push({
|
|
23797
|
+
folder: depFolder,
|
|
23798
|
+
status: depStatus?.status ?? "unknown"
|
|
23799
|
+
});
|
|
23800
|
+
}
|
|
23801
|
+
}
|
|
23802
|
+
if (unmetDeps.length > 0) {
|
|
23803
|
+
const depList = unmetDeps.map((d) => `"${d.folder}" (${d.status})`).join(", ");
|
|
23804
|
+
return {
|
|
23805
|
+
allowed: false,
|
|
23806
|
+
error: `Dependency constraint: Task "${taskFolder}" cannot start - dependencies not done: ${depList}. ` + `Only tasks with status 'done' satisfy dependencies.`
|
|
23807
|
+
};
|
|
23808
|
+
}
|
|
23809
|
+
return { allowed: true };
|
|
23810
|
+
};
|
|
23427
23811
|
return {
|
|
23428
23812
|
"experimental.chat.system.transform": async (input, output) => {
|
|
23429
23813
|
output.system.push(HIVE_SYSTEM_PROMPT);
|
|
@@ -23694,9 +24078,9 @@ Reminder: start work with hive_exec_start to use its worktree, and ensure any su
|
|
|
23694
24078
|
const feature = resolveFeature(explicitFeature);
|
|
23695
24079
|
if (!feature)
|
|
23696
24080
|
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
23697
|
-
const
|
|
23698
|
-
if (
|
|
23699
|
-
return
|
|
24081
|
+
const blockedMessage = checkBlocked(feature);
|
|
24082
|
+
if (blockedMessage)
|
|
24083
|
+
return blockedMessage;
|
|
23700
24084
|
const taskInfo = taskService.get(feature, task);
|
|
23701
24085
|
if (!taskInfo)
|
|
23702
24086
|
return `Error: Task "${task}" not found`;
|
|
@@ -23705,6 +24089,19 @@ Reminder: start work with hive_exec_start to use its worktree, and ensure any su
|
|
|
23705
24089
|
if (continueFrom === "blocked" && taskInfo.status !== "blocked") {
|
|
23706
24090
|
return "Error: Task is not in blocked state. Use without continueFrom.";
|
|
23707
24091
|
}
|
|
24092
|
+
if (continueFrom !== "blocked") {
|
|
24093
|
+
const depCheck = checkDependencies(feature, task);
|
|
24094
|
+
if (!depCheck.allowed) {
|
|
24095
|
+
return JSON.stringify({
|
|
24096
|
+
success: false,
|
|
24097
|
+
error: depCheck.error,
|
|
24098
|
+
hints: [
|
|
24099
|
+
"Complete the required dependencies before starting this task.",
|
|
24100
|
+
"Use hive_status to see current task states."
|
|
24101
|
+
]
|
|
24102
|
+
});
|
|
24103
|
+
}
|
|
24104
|
+
}
|
|
23708
24105
|
let worktree;
|
|
23709
24106
|
if (continueFrom === "blocked") {
|
|
23710
24107
|
worktree = await worktreeService.get(feature, task);
|
|
@@ -23738,56 +24135,28 @@ Reminder: start work with hive_exec_start to use its worktree, and ensure any su
|
|
|
23738
24135
|
...taskBudgetResult.truncationEvents,
|
|
23739
24136
|
...contextBudgetResult.truncationEvents
|
|
23740
24137
|
];
|
|
23741
|
-
const priorTasksFormatted = previousTasks.map((t) => `- ${t.name}: ${t.summary}`).join(`
|
|
23742
|
-
`);
|
|
23743
24138
|
const droppedTasksHint = taskBudgetResult.droppedTasksHint;
|
|
23744
|
-
|
|
23745
|
-
|
|
23746
|
-
|
|
23747
|
-
specContent
|
|
23748
|
-
|
|
23749
|
-
|
|
23750
|
-
|
|
23751
|
-
|
|
23752
|
-
|
|
23753
|
-
|
|
23754
|
-
|
|
23755
|
-
|
|
23756
|
-
|
|
23757
|
-
|
|
23758
|
-
|
|
23759
|
-
|
|
23760
|
-
|
|
23761
|
-
|
|
23762
|
-
|
|
23763
|
-
|
|
23764
|
-
|
|
23765
|
-
${taskMatch[0].trim()}
|
|
23766
|
-
|
|
23767
|
-
`;
|
|
23768
|
-
}
|
|
23769
|
-
}
|
|
23770
|
-
if (contextFiles.length > 0) {
|
|
23771
|
-
const contextCompiled = contextFiles.map((f) => `## ${f.name}
|
|
23772
|
-
|
|
23773
|
-
${f.content}`).join(`
|
|
23774
|
-
|
|
23775
|
-
---
|
|
23776
|
-
|
|
23777
|
-
`);
|
|
23778
|
-
specContent += `## Context
|
|
23779
|
-
|
|
23780
|
-
${contextCompiled}
|
|
23781
|
-
|
|
23782
|
-
`;
|
|
23783
|
-
}
|
|
23784
|
-
if (priorTasksFormatted) {
|
|
23785
|
-
specContent += `## Completed Tasks
|
|
23786
|
-
|
|
23787
|
-
${priorTasksFormatted}
|
|
23788
|
-
|
|
23789
|
-
`;
|
|
23790
|
-
}
|
|
24139
|
+
const taskOrder = parseInt(taskInfo.folder.match(/^(\d+)/)?.[1] || "0", 10);
|
|
24140
|
+
const status = taskService.getRawStatus(feature, task);
|
|
24141
|
+
const dependsOn = status?.dependsOn ?? [];
|
|
24142
|
+
const specContent = taskService.buildSpecContent({
|
|
24143
|
+
featureName: feature,
|
|
24144
|
+
task: {
|
|
24145
|
+
folder: task,
|
|
24146
|
+
name: taskInfo.planTitle ?? taskInfo.name,
|
|
24147
|
+
order: taskOrder,
|
|
24148
|
+
description: undefined
|
|
24149
|
+
},
|
|
24150
|
+
dependsOn,
|
|
24151
|
+
allTasks: allTasks.map((t) => ({
|
|
24152
|
+
folder: t.folder,
|
|
24153
|
+
name: t.name,
|
|
24154
|
+
order: parseInt(t.folder.match(/^(\d+)/)?.[1] || "0", 10)
|
|
24155
|
+
})),
|
|
24156
|
+
planContent: planResult?.content ?? null,
|
|
24157
|
+
contextFiles,
|
|
24158
|
+
completedTasks: previousTasks
|
|
24159
|
+
});
|
|
23791
24160
|
taskService.writeSpec(feature, task, specContent);
|
|
23792
24161
|
const workerPrompt = buildWorkerPrompt({
|
|
23793
24162
|
feature,
|
|
@@ -24214,12 +24583,16 @@ Files changed: ${result.filesChanged?.length || 0}`;
|
|
|
24214
24583
|
const plan = planService.read(feature);
|
|
24215
24584
|
const tasks = taskService.list(feature);
|
|
24216
24585
|
const contextFiles = contextService.list(feature);
|
|
24217
|
-
const tasksSummary = tasks.map((t) =>
|
|
24218
|
-
|
|
24219
|
-
|
|
24220
|
-
|
|
24221
|
-
|
|
24222
|
-
|
|
24586
|
+
const tasksSummary = tasks.map((t) => {
|
|
24587
|
+
const rawStatus = taskService.getRawStatus(feature, t.folder);
|
|
24588
|
+
return {
|
|
24589
|
+
folder: t.folder,
|
|
24590
|
+
name: t.name,
|
|
24591
|
+
status: t.status,
|
|
24592
|
+
origin: t.origin || "plan",
|
|
24593
|
+
dependsOn: rawStatus?.dependsOn ?? null
|
|
24594
|
+
};
|
|
24595
|
+
});
|
|
24223
24596
|
const contextSummary = contextFiles.map((c) => ({
|
|
24224
24597
|
name: c.name,
|
|
24225
24598
|
chars: c.content.length,
|
|
@@ -24228,7 +24601,18 @@ Files changed: ${result.filesChanged?.length || 0}`;
|
|
|
24228
24601
|
const pendingTasks = tasksSummary.filter((t) => t.status === "pending");
|
|
24229
24602
|
const inProgressTasks = tasksSummary.filter((t) => t.status === "in_progress");
|
|
24230
24603
|
const doneTasks = tasksSummary.filter((t) => t.status === "done");
|
|
24231
|
-
const
|
|
24604
|
+
const tasksWithDeps = tasksSummary.map((t) => ({
|
|
24605
|
+
folder: t.folder,
|
|
24606
|
+
status: t.status,
|
|
24607
|
+
dependsOn: t.dependsOn ?? undefined
|
|
24608
|
+
}));
|
|
24609
|
+
const effectiveDeps = buildEffectiveDependencies(tasksWithDeps);
|
|
24610
|
+
const normalizedTasks = tasksWithDeps.map((task) => ({
|
|
24611
|
+
...task,
|
|
24612
|
+
dependsOn: effectiveDeps.get(task.folder)
|
|
24613
|
+
}));
|
|
24614
|
+
const { runnable, blocked: blockedBy } = computeRunnableAndBlocked(normalizedTasks);
|
|
24615
|
+
const getNextAction = (planStatus2, tasks2, runnableTasks) => {
|
|
24232
24616
|
if (!planStatus2 || planStatus2 === "draft") {
|
|
24233
24617
|
return "Write or revise plan with hive_plan_write, then get approval";
|
|
24234
24618
|
}
|
|
@@ -24242,9 +24626,15 @@ Files changed: ${result.filesChanged?.length || 0}`;
|
|
|
24242
24626
|
if (inProgress) {
|
|
24243
24627
|
return `Continue work on task: ${inProgress.folder}`;
|
|
24244
24628
|
}
|
|
24629
|
+
if (runnableTasks.length > 1) {
|
|
24630
|
+
return `${runnableTasks.length} tasks are ready to start in parallel: ${runnableTasks.join(", ")}`;
|
|
24631
|
+
}
|
|
24632
|
+
if (runnableTasks.length === 1) {
|
|
24633
|
+
return `Start next task with hive_exec_start: ${runnableTasks[0]}`;
|
|
24634
|
+
}
|
|
24245
24635
|
const pending = tasks2.find((t) => t.status === "pending");
|
|
24246
24636
|
if (pending) {
|
|
24247
|
-
return `
|
|
24637
|
+
return `Pending tasks exist but are blocked by dependencies. Check blockedBy for details.`;
|
|
24248
24638
|
}
|
|
24249
24639
|
return "All tasks complete. Review and merge or complete feature.";
|
|
24250
24640
|
};
|
|
@@ -24266,13 +24656,15 @@ Files changed: ${result.filesChanged?.length || 0}`;
|
|
|
24266
24656
|
pending: pendingTasks.length,
|
|
24267
24657
|
inProgress: inProgressTasks.length,
|
|
24268
24658
|
done: doneTasks.length,
|
|
24269
|
-
list: tasksSummary
|
|
24659
|
+
list: tasksSummary,
|
|
24660
|
+
runnable,
|
|
24661
|
+
blockedBy
|
|
24270
24662
|
},
|
|
24271
24663
|
context: {
|
|
24272
24664
|
fileCount: contextFiles.length,
|
|
24273
24665
|
files: contextSummary
|
|
24274
24666
|
},
|
|
24275
|
-
nextAction: getNextAction(planStatus, tasksSummary)
|
|
24667
|
+
nextAction: getNextAction(planStatus, tasksSummary, runnable)
|
|
24276
24668
|
});
|
|
24277
24669
|
}
|
|
24278
24670
|
}),
|
|
@@ -24406,7 +24798,7 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24406
24798
|
prompt: ARCHITECT_BEE_PROMPT + architectAutoLoadedSkills,
|
|
24407
24799
|
permission: {
|
|
24408
24800
|
edit: "deny",
|
|
24409
|
-
task: "
|
|
24801
|
+
task: "allow",
|
|
24410
24802
|
question: "allow",
|
|
24411
24803
|
skill: "allow",
|
|
24412
24804
|
todowrite: "allow",
|
|
@@ -24444,6 +24836,11 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24444
24836
|
prompt: SCOUT_BEE_PROMPT + scoutAutoLoadedSkills,
|
|
24445
24837
|
permission: {
|
|
24446
24838
|
edit: "deny",
|
|
24839
|
+
hive_background_task: "deny",
|
|
24840
|
+
hive_background_output: "deny",
|
|
24841
|
+
hive_background_cancel: "deny",
|
|
24842
|
+
task: "deny",
|
|
24843
|
+
delegate: "deny",
|
|
24447
24844
|
skill: "allow",
|
|
24448
24845
|
webfetch: "allow"
|
|
24449
24846
|
}
|
|
@@ -24457,6 +24854,11 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24457
24854
|
description: "Forager (Worker/Coder) - Executes tasks directly in isolated worktrees. Never delegates.",
|
|
24458
24855
|
prompt: FORAGER_BEE_PROMPT + foragerAutoLoadedSkills,
|
|
24459
24856
|
permission: {
|
|
24857
|
+
hive_background_task: "deny",
|
|
24858
|
+
hive_background_output: "deny",
|
|
24859
|
+
hive_background_cancel: "deny",
|
|
24860
|
+
task: "deny",
|
|
24861
|
+
delegate: "deny",
|
|
24460
24862
|
skill: "allow"
|
|
24461
24863
|
}
|
|
24462
24864
|
};
|
|
@@ -24470,6 +24872,11 @@ Make the requested changes, then call hive_request_review again.`;
|
|
|
24470
24872
|
prompt: HYGIENIC_BEE_PROMPT + hygienicAutoLoadedSkills,
|
|
24471
24873
|
permission: {
|
|
24472
24874
|
edit: "deny",
|
|
24875
|
+
hive_background_task: "deny",
|
|
24876
|
+
hive_background_output: "deny",
|
|
24877
|
+
hive_background_cancel: "deny",
|
|
24878
|
+
task: "deny",
|
|
24879
|
+
delegate: "deny",
|
|
24473
24880
|
skill: "allow"
|
|
24474
24881
|
}
|
|
24475
24882
|
};
|