opencode-hive 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/architect.d.ts +1 -1
- package/dist/agents/forager.d.ts +1 -1
- package/dist/agents/hive.d.ts +1 -1
- package/dist/agents/hygienic.d.ts +1 -1
- package/dist/agents/scout.d.ts +1 -1
- package/dist/agents/swarm.d.ts +1 -1
- package/dist/index.js +1189 -168
- package/dist/skills/registry.generated.d.ts +1 -1
- package/package.json +1 -1
- package/skills/agents-md-mastery/SKILL.md +253 -0
- package/skills/docker-mastery/SKILL.md +346 -0
- package/skills/executing-plans/SKILL.md +2 -2
- package/skills/test-driven-development/SKILL.md +1 -1
- package/skills/writing-plans/SKILL.md +7 -0
- package/skills/onboarding/SKILL.md +0 -61
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ var __export = (target, all) => {
|
|
|
12
12
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
|
-
import * as
|
|
15
|
+
import * as path8 from "path";
|
|
16
16
|
import * as os from "os";
|
|
17
17
|
|
|
18
18
|
// ../../node_modules/zod/v4/classic/external.js
|
|
@@ -12336,8 +12336,260 @@ function tool(input) {
|
|
|
12336
12336
|
}
|
|
12337
12337
|
tool.schema = exports_external;
|
|
12338
12338
|
// src/skills/registry.generated.ts
|
|
12339
|
-
var BUILTIN_SKILL_NAMES = ["brainstorming", "code-reviewer", "dispatching-parallel-agents", "
|
|
12339
|
+
var BUILTIN_SKILL_NAMES = ["agents-md-mastery", "brainstorming", "code-reviewer", "dispatching-parallel-agents", "docker-mastery", "executing-plans", "parallel-exploration", "systematic-debugging", "test-driven-development", "verification-before-completion", "writing-plans"];
|
|
12340
12340
|
var BUILTIN_SKILLS = [
|
|
12341
|
+
{
|
|
12342
|
+
name: "agents-md-mastery",
|
|
12343
|
+
description: "Use when bootstrapping, updating, or reviewing AGENTS.md — teaches what makes effective agent memory, how to structure sections, signal vs noise filtering, and when to prune stale entries",
|
|
12344
|
+
template: `# AGENTS.md Mastery
|
|
12345
|
+
|
|
12346
|
+
## Overview
|
|
12347
|
+
|
|
12348
|
+
**AGENTS.md is pseudo-memory loaded at session start.** Every line shapes agent behavior for the entire session. Quality beats quantity. Write for agents, not humans.
|
|
12349
|
+
|
|
12350
|
+
Unlike code comments or READMEs, AGENTS.md entries persist across all agent sessions. A bad entry misleads agents hundreds of times. A missing entry causes the same mistake repeatedly.
|
|
12351
|
+
|
|
12352
|
+
**Core principle:** Optimize for agent comprehension and behavioral change, not human readability.
|
|
12353
|
+
|
|
12354
|
+
## The Iron Law
|
|
12355
|
+
|
|
12356
|
+
\`\`\`
|
|
12357
|
+
EVERY ENTRY MUST CHANGE AGENT BEHAVIOR
|
|
12358
|
+
\`\`\`
|
|
12359
|
+
|
|
12360
|
+
If an entry doesn't:
|
|
12361
|
+
- Prevent a specific mistake
|
|
12362
|
+
- Enable a capability the agent would otherwise miss
|
|
12363
|
+
- Override a default assumption that breaks in this codebase
|
|
12364
|
+
|
|
12365
|
+
...then it doesn't belong in AGENTS.md.
|
|
12366
|
+
|
|
12367
|
+
**Test:** Would a fresh agent session make a mistake without this entry? If no → noise.
|
|
12368
|
+
|
|
12369
|
+
## When to Use
|
|
12370
|
+
|
|
12371
|
+
| Trigger | Action |
|
|
12372
|
+
|---------|--------|
|
|
12373
|
+
| New project bootstrap | Write initial AGENTS.md with build/test/style basics |
|
|
12374
|
+
| Feature completion | Sync new learnings via \`hive_agents_md\` tool |
|
|
12375
|
+
| Periodic review | Audit for stale/redundant entries (quarterly) |
|
|
12376
|
+
| Quality issues | Agent repeating mistakes? Check if AGENTS.md has the fix |
|
|
12377
|
+
|
|
12378
|
+
## What Makes Good Agent Memory
|
|
12379
|
+
|
|
12380
|
+
### Signal Entries (Keep)
|
|
12381
|
+
|
|
12382
|
+
✅ **Project-specific conventions:**
|
|
12383
|
+
- "We use Zustand, not Redux — never add Redux"
|
|
12384
|
+
- "Auth lives in \`/lib/auth\` — never create auth elsewhere"
|
|
12385
|
+
- "Run \`bun test\` not \`npm test\` (we don't use npm)"
|
|
12386
|
+
|
|
12387
|
+
✅ **Non-obvious patterns:**
|
|
12388
|
+
- "Use \`.js\` extension for local imports (ESM requirement)"
|
|
12389
|
+
- "Worktrees don't share \`node_modules\` — run \`bun install\` in each"
|
|
12390
|
+
- "SandboxConfig is in \`dockerSandboxService.ts\`, NOT \`types.ts\`"
|
|
12391
|
+
|
|
12392
|
+
✅ **Gotchas that break builds:**
|
|
12393
|
+
- "Never use \`ensureDirSync\` — doesn't exist. Use \`ensureDir\` (sync despite name)"
|
|
12394
|
+
- "Import from \`../utils/paths.js\` not \`./paths\` (ESM strict)"
|
|
12395
|
+
|
|
12396
|
+
### Noise Entries (Remove)
|
|
12397
|
+
|
|
12398
|
+
❌ **Agent already knows:**
|
|
12399
|
+
- "This project uses TypeScript" (agent detects from files)
|
|
12400
|
+
- "We follow semantic versioning" (universal convention)
|
|
12401
|
+
- "Use descriptive variable names" (generic advice)
|
|
12402
|
+
|
|
12403
|
+
❌ **Irrelevant metadata:**
|
|
12404
|
+
- "Created on January 2024"
|
|
12405
|
+
- "Originally written by X"
|
|
12406
|
+
- "License: MIT" (in LICENSE file already)
|
|
12407
|
+
|
|
12408
|
+
❌ **Describes what code does:**
|
|
12409
|
+
- "FeatureService manages features" (agent can read code)
|
|
12410
|
+
- "The system uses git worktrees" (observable from commands)
|
|
12411
|
+
|
|
12412
|
+
### Rule of Thumb
|
|
12413
|
+
|
|
12414
|
+
**Signal:** Changes how agent acts
|
|
12415
|
+
**Noise:** Documents what agent observes
|
|
12416
|
+
|
|
12417
|
+
## Section Structure for Fast Comprehension
|
|
12418
|
+
|
|
12419
|
+
Agents read AGENTS.md top-to-bottom once at session start. Put high-value info first:
|
|
12420
|
+
|
|
12421
|
+
\`\`\`markdown
|
|
12422
|
+
# Project Name
|
|
12423
|
+
|
|
12424
|
+
## Build & Test Commands
|
|
12425
|
+
# ← Agents need this IMMEDIATELY
|
|
12426
|
+
bun run build
|
|
12427
|
+
bun run test
|
|
12428
|
+
bun run release:check
|
|
12429
|
+
|
|
12430
|
+
## Code Style
|
|
12431
|
+
# ← Prevents syntax/import errors
|
|
12432
|
+
- Semicolons: Yes
|
|
12433
|
+
- Quotes: Single
|
|
12434
|
+
- Imports: Use \`.js\` extension
|
|
12435
|
+
|
|
12436
|
+
## Architecture
|
|
12437
|
+
# ← Key directories, where things live
|
|
12438
|
+
packages/
|
|
12439
|
+
├── hive-core/ # Shared logic
|
|
12440
|
+
├── opencode-hive/ # Plugin
|
|
12441
|
+
└── vscode-hive/ # Extension
|
|
12442
|
+
|
|
12443
|
+
## Important Patterns
|
|
12444
|
+
# ← How to do common tasks correctly
|
|
12445
|
+
Use \`readText\` from paths.ts, not fs.readFileSync
|
|
12446
|
+
|
|
12447
|
+
## Gotchas & Anti-Patterns
|
|
12448
|
+
# ← Things that break or mislead
|
|
12449
|
+
NEVER use \`ensureDirSync\` — doesn't exist
|
|
12450
|
+
\`\`\`
|
|
12451
|
+
|
|
12452
|
+
**Keep total under 500 lines.** Beyond that, agents lose focus and miss critical entries.
|
|
12453
|
+
|
|
12454
|
+
## The Sync Workflow
|
|
12455
|
+
|
|
12456
|
+
After completing a feature, sync learnings to AGENTS.md:
|
|
12457
|
+
|
|
12458
|
+
1. **Trigger sync:**
|
|
12459
|
+
\`\`\`typescript
|
|
12460
|
+
hive_agents_md({ action: 'sync', feature: 'feature-name' })
|
|
12461
|
+
\`\`\`
|
|
12462
|
+
|
|
12463
|
+
2. **Review each proposal:**
|
|
12464
|
+
- Read the proposed change
|
|
12465
|
+
- Ask: "Does this change agent behavior?"
|
|
12466
|
+
- Check: Is this already obvious from code/files?
|
|
12467
|
+
|
|
12468
|
+
3. **Accept signal, reject noise:**
|
|
12469
|
+
- ❌ "TypeScript is used" → Agent detects this
|
|
12470
|
+
- ✅ "Use \`.js\` extension for imports" → Prevents build failures
|
|
12471
|
+
|
|
12472
|
+
4. **Apply approved changes:**
|
|
12473
|
+
\`\`\`typescript
|
|
12474
|
+
hive_agents_md({ action: 'apply' })
|
|
12475
|
+
\`\`\`
|
|
12476
|
+
|
|
12477
|
+
**Warning:** Don't auto-approve all proposals. One bad entry pollutes all future sessions.
|
|
12478
|
+
|
|
12479
|
+
## When to Prune
|
|
12480
|
+
|
|
12481
|
+
Remove entries when they become:
|
|
12482
|
+
|
|
12483
|
+
**Outdated:**
|
|
12484
|
+
- "We use Redux" → Project migrated to Zustand
|
|
12485
|
+
- "Node 16 compatibility required" → Now on Node 22
|
|
12486
|
+
|
|
12487
|
+
**Redundant:**
|
|
12488
|
+
- "Use single quotes" + "Strings use single quotes" → Keep one
|
|
12489
|
+
- Near-duplicates in different sections
|
|
12490
|
+
|
|
12491
|
+
**Too generic:**
|
|
12492
|
+
- "Write clear code" → Applies to any project
|
|
12493
|
+
- "Test your changes" → Universal advice
|
|
12494
|
+
|
|
12495
|
+
**Describing code:**
|
|
12496
|
+
- "TaskService manages tasks" → Agent can read \`TaskService\` class
|
|
12497
|
+
- "Worktrees are in \`.hive/.worktrees/\`" → Observable from filesystem
|
|
12498
|
+
|
|
12499
|
+
**Proven unnecessary:**
|
|
12500
|
+
- Entry added 6 months ago, but agents haven't hit that issue since
|
|
12501
|
+
|
|
12502
|
+
## Red Flags
|
|
12503
|
+
|
|
12504
|
+
| Warning Sign | Why It's Bad | Fix |
|
|
12505
|
+
|-------------|-------------|-----|
|
|
12506
|
+
| AGENTS.md > 800 lines | Agents lose focus, miss critical info | Prune aggressively |
|
|
12507
|
+
| Describes what code does | Agent can read code | Remove descriptions |
|
|
12508
|
+
| Missing build/test commands | First thing agents need | Add at top |
|
|
12509
|
+
| No gotchas section | Agents repeat past mistakes | Document failure modes |
|
|
12510
|
+
| Generic best practices | Doesn't change behavior | Remove or make specific |
|
|
12511
|
+
| Outdated patterns | Misleads agents | Prune during sync |
|
|
12512
|
+
|
|
12513
|
+
## Anti-Patterns
|
|
12514
|
+
|
|
12515
|
+
| Anti-Pattern | Better Approach |
|
|
12516
|
+
|-------------|----------------|
|
|
12517
|
+
| "Document everything" | Document only what changes behavior |
|
|
12518
|
+
| "Keep for historical record" | Version control is history |
|
|
12519
|
+
| "Might be useful someday" | Add when proven necessary |
|
|
12520
|
+
| "Explains the system" | Agents read code for that |
|
|
12521
|
+
| "Comprehensive reference" | AGENTS.md is a filter, not docs |
|
|
12522
|
+
|
|
12523
|
+
## Good Examples
|
|
12524
|
+
|
|
12525
|
+
**Build Commands (High value, agents need immediately):**
|
|
12526
|
+
\`\`\`markdown
|
|
12527
|
+
## Build & Test Commands
|
|
12528
|
+
bun run build # Build all packages
|
|
12529
|
+
bun run test # Run all tests
|
|
12530
|
+
bun run release:check # Full CI check
|
|
12531
|
+
\`\`\`
|
|
12532
|
+
|
|
12533
|
+
**Project-Specific Convention (Prevents mistakes):**
|
|
12534
|
+
\`\`\`markdown
|
|
12535
|
+
## Code Style
|
|
12536
|
+
- Imports: Use \`.js\` extension for local imports (ESM requirement)
|
|
12537
|
+
- Paths: Import from \`../utils/paths.js\` never \`./paths\`
|
|
12538
|
+
\`\`\`
|
|
12539
|
+
|
|
12540
|
+
**Non-Obvious Gotcha (Prevents build failure):**
|
|
12541
|
+
\`\`\`markdown
|
|
12542
|
+
## Important Patterns
|
|
12543
|
+
Use \`ensureDir\` from paths.ts — sync despite name
|
|
12544
|
+
NEVER use \`ensureDirSync\` (doesn't exist)
|
|
12545
|
+
\`\`\`
|
|
12546
|
+
|
|
12547
|
+
## Bad Examples
|
|
12548
|
+
|
|
12549
|
+
**Generic advice (agent already knows):**
|
|
12550
|
+
\`\`\`markdown
|
|
12551
|
+
## Best Practices
|
|
12552
|
+
- Use meaningful variable names
|
|
12553
|
+
- Write unit tests
|
|
12554
|
+
- Follow DRY principle
|
|
12555
|
+
\`\`\`
|
|
12556
|
+
|
|
12557
|
+
**Describes code (agent can read it):**
|
|
12558
|
+
\`\`\`markdown
|
|
12559
|
+
## Architecture
|
|
12560
|
+
The FeatureService class manages features. It has methods
|
|
12561
|
+
for create, read, update, and delete operations.
|
|
12562
|
+
\`\`\`
|
|
12563
|
+
|
|
12564
|
+
**Irrelevant metadata:**
|
|
12565
|
+
\`\`\`markdown
|
|
12566
|
+
## Project History
|
|
12567
|
+
Created in January 2024 by the platform team.
|
|
12568
|
+
Originally built for internal use.
|
|
12569
|
+
\`\`\`
|
|
12570
|
+
|
|
12571
|
+
## Verification
|
|
12572
|
+
|
|
12573
|
+
Before finalizing AGENTS.md updates:
|
|
12574
|
+
|
|
12575
|
+
- [ ] Every entry answers: "What mistake does this prevent?"
|
|
12576
|
+
- [ ] No generic advice that applies to all projects
|
|
12577
|
+
- [ ] Build/test commands are first
|
|
12578
|
+
- [ ] Gotchas section exists and is populated
|
|
12579
|
+
- [ ] Total length under 500 lines (800 absolute max)
|
|
12580
|
+
- [ ] No entries describing what code does
|
|
12581
|
+
- [ ] Fresh agent session would benefit from each entry
|
|
12582
|
+
|
|
12583
|
+
## Summary
|
|
12584
|
+
|
|
12585
|
+
AGENTS.md is **behavioral memory**, not documentation:
|
|
12586
|
+
- Write for agents, optimize for behavior change
|
|
12587
|
+
- Signal = prevents mistakes, Noise = describes observables
|
|
12588
|
+
- Sync after features, prune quarterly
|
|
12589
|
+
- Test: Would agent make a mistake without this entry?
|
|
12590
|
+
|
|
12591
|
+
**Quality > quantity. Every line counts.**`
|
|
12592
|
+
},
|
|
12341
12593
|
{
|
|
12342
12594
|
name: "brainstorming",
|
|
12343
12595
|
description: "Use before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.",
|
|
@@ -12795,6 +13047,351 @@ From debugging session (2025-10-03):
|
|
|
12795
13047
|
- All investigations completed concurrently
|
|
12796
13048
|
- All fixes integrated successfully
|
|
12797
13049
|
- Zero conflicts between agent changes`
|
|
13050
|
+
},
|
|
13051
|
+
{
|
|
13052
|
+
name: "docker-mastery",
|
|
13053
|
+
description: "Use when working with Docker containers — debugging container failures, writing Dockerfiles, docker-compose for integration tests, image optimization, or deploying containerized applications",
|
|
13054
|
+
template: `# Docker Mastery
|
|
13055
|
+
|
|
13056
|
+
## Overview
|
|
13057
|
+
|
|
13058
|
+
Docker is a **platform for building, shipping, and running applications**, not just isolation.
|
|
13059
|
+
|
|
13060
|
+
Agents should think in containers: reproducible environments, declarative dependencies, isolated execution.
|
|
13061
|
+
|
|
13062
|
+
**Core principle:** Containers are not virtual machines. They share the kernel but isolate processes, filesystems, and networks.
|
|
13063
|
+
|
|
13064
|
+
**Violating the letter of these guidelines is violating the spirit of containerization.**
|
|
13065
|
+
|
|
13066
|
+
## The Iron Law
|
|
13067
|
+
|
|
13068
|
+
\`\`\`
|
|
13069
|
+
UNDERSTAND THE CONTAINER BEFORE DEBUGGING INSIDE IT
|
|
13070
|
+
\`\`\`
|
|
13071
|
+
|
|
13072
|
+
Before exec'ing into a container or adding debug commands:
|
|
13073
|
+
1. Check the image (what's installed?)
|
|
13074
|
+
2. Check mounts (what host files are visible?)
|
|
13075
|
+
3. Check environment variables (what config is passed?)
|
|
13076
|
+
4. Check the Dockerfile (how was it built?)
|
|
13077
|
+
|
|
13078
|
+
Random debugging inside containers wastes time. Context first, then debug.
|
|
13079
|
+
|
|
13080
|
+
## When to Use
|
|
13081
|
+
|
|
13082
|
+
Use this skill when working with:
|
|
13083
|
+
- **Container build failures** - Dockerfile errors, missing dependencies
|
|
13084
|
+
- **Test environment setup** - Reproducible test environments across machines
|
|
13085
|
+
- **Integration test orchestration** - Multi-service setups (DB + API + tests)
|
|
13086
|
+
- **Dockerfile authoring** - Writing efficient, maintainable Dockerfiles
|
|
13087
|
+
- **Image size optimization** - Reducing image size, layer caching
|
|
13088
|
+
- **Deployment** - Containerized application deployment
|
|
13089
|
+
- **Sandbox debugging** - Issues with Hive's Docker sandbox mode
|
|
13090
|
+
|
|
13091
|
+
**Use this ESPECIALLY when:**
|
|
13092
|
+
- Tests pass locally but fail in CI (environment mismatch)
|
|
13093
|
+
- "Works on my machine" problems
|
|
13094
|
+
- Need to test against specific dependency versions
|
|
13095
|
+
- Multiple services must coordinate (database + API)
|
|
13096
|
+
- Building for production deployment
|
|
13097
|
+
|
|
13098
|
+
## Core Concepts
|
|
13099
|
+
|
|
13100
|
+
### Images vs Containers
|
|
13101
|
+
|
|
13102
|
+
- **Image**: Read-only template (built from Dockerfile)
|
|
13103
|
+
- **Container**: Running instance of an image (ephemeral by default)
|
|
13104
|
+
|
|
13105
|
+
\`\`\`bash
|
|
13106
|
+
# Build once
|
|
13107
|
+
docker build -t myapp:latest .
|
|
13108
|
+
|
|
13109
|
+
# Run many times
|
|
13110
|
+
docker run --rm myapp:latest
|
|
13111
|
+
docker run --rm -e DEBUG=true myapp:latest
|
|
13112
|
+
\`\`\`
|
|
13113
|
+
|
|
13114
|
+
**Key insight:** Changes inside containers are lost unless committed or volumes are used.
|
|
13115
|
+
|
|
13116
|
+
### Volumes & Mounts
|
|
13117
|
+
|
|
13118
|
+
Mount host directories into containers for persistence and code sharing:
|
|
13119
|
+
|
|
13120
|
+
\`\`\`bash
|
|
13121
|
+
# Mount current directory to /app in container
|
|
13122
|
+
docker run -v $(pwd):/app myapp:latest
|
|
13123
|
+
|
|
13124
|
+
# Hive worktrees are mounted automatically
|
|
13125
|
+
# Your code edits (via Read/Write/Edit tools) affect the host
|
|
13126
|
+
# Container sees the same files at runtime
|
|
13127
|
+
\`\`\`
|
|
13128
|
+
|
|
13129
|
+
**How Hive uses this:** Worktree is mounted into container, so file tools work on host, bash commands run in container.
|
|
13130
|
+
|
|
13131
|
+
### Multi-Stage Builds
|
|
13132
|
+
|
|
13133
|
+
Minimize image size by using multiple FROM statements:
|
|
13134
|
+
|
|
13135
|
+
\`\`\`dockerfile
|
|
13136
|
+
# Build stage (large, has compilers)
|
|
13137
|
+
FROM node:22 AS builder
|
|
13138
|
+
WORKDIR /app
|
|
13139
|
+
COPY package.json bun.lockb ./
|
|
13140
|
+
RUN bun install
|
|
13141
|
+
COPY . .
|
|
13142
|
+
RUN bun run build
|
|
13143
|
+
|
|
13144
|
+
# Runtime stage (small, production only)
|
|
13145
|
+
FROM node:22-slim
|
|
13146
|
+
WORKDIR /app
|
|
13147
|
+
COPY --from=builder /app/dist ./dist
|
|
13148
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
13149
|
+
CMD ["node", "dist/index.js"]
|
|
13150
|
+
\`\`\`
|
|
13151
|
+
|
|
13152
|
+
**Result:** Builder tools (TypeScript, bundlers) not included in final image.
|
|
13153
|
+
|
|
13154
|
+
### Docker Compose for Multi-Service Setups
|
|
13155
|
+
|
|
13156
|
+
Define multiple services in \`docker-compose.yml\`:
|
|
13157
|
+
|
|
13158
|
+
\`\`\`yaml
|
|
13159
|
+
version: '3.8'
|
|
13160
|
+
services:
|
|
13161
|
+
db:
|
|
13162
|
+
image: postgres:15
|
|
13163
|
+
environment:
|
|
13164
|
+
POSTGRES_PASSWORD: testpass
|
|
13165
|
+
ports:
|
|
13166
|
+
- "5432:5432"
|
|
13167
|
+
|
|
13168
|
+
api:
|
|
13169
|
+
build: .
|
|
13170
|
+
environment:
|
|
13171
|
+
DATABASE_URL: postgres://db:5432/testdb
|
|
13172
|
+
depends_on:
|
|
13173
|
+
- db
|
|
13174
|
+
ports:
|
|
13175
|
+
- "3000:3000"
|
|
13176
|
+
\`\`\`
|
|
13177
|
+
|
|
13178
|
+
Run with: \`docker-compose up -d\`
|
|
13179
|
+
Teardown with: \`docker-compose down\`
|
|
13180
|
+
|
|
13181
|
+
### Network Modes
|
|
13182
|
+
|
|
13183
|
+
- **bridge** (default): Isolated network, containers can talk to each other by name
|
|
13184
|
+
- **host**: Container uses host's network directly (no isolation)
|
|
13185
|
+
- **none**: No network access
|
|
13186
|
+
|
|
13187
|
+
**When to use host mode:** Debugging network issues, accessing host services directly.
|
|
13188
|
+
|
|
13189
|
+
## Common Patterns
|
|
13190
|
+
|
|
13191
|
+
### Debug a Failing Container
|
|
13192
|
+
|
|
13193
|
+
**Problem:** Container exits immediately, logs unclear.
|
|
13194
|
+
|
|
13195
|
+
**Pattern:**
|
|
13196
|
+
1. Run interactively with shell:
|
|
13197
|
+
\`\`\`bash
|
|
13198
|
+
docker run -it --entrypoint sh myapp:latest
|
|
13199
|
+
\`\`\`
|
|
13200
|
+
2. Inspect filesystem, check if dependencies exist:
|
|
13201
|
+
\`\`\`bash
|
|
13202
|
+
ls /app
|
|
13203
|
+
which node
|
|
13204
|
+
cat /etc/os-release
|
|
13205
|
+
\`\`\`
|
|
13206
|
+
3. Run command manually to see full error:
|
|
13207
|
+
\`\`\`bash
|
|
13208
|
+
node dist/index.js
|
|
13209
|
+
\`\`\`
|
|
13210
|
+
|
|
13211
|
+
### Integration Tests with Docker Compose
|
|
13212
|
+
|
|
13213
|
+
**Pattern:**
|
|
13214
|
+
1. Define services in \`docker-compose.test.yml\`
|
|
13215
|
+
2. Add wait logic (wait for DB to be ready)
|
|
13216
|
+
3. Run tests
|
|
13217
|
+
4. Teardown
|
|
13218
|
+
|
|
13219
|
+
\`\`\`yaml
|
|
13220
|
+
# docker-compose.test.yml
|
|
13221
|
+
services:
|
|
13222
|
+
db:
|
|
13223
|
+
image: postgres:15
|
|
13224
|
+
environment:
|
|
13225
|
+
POSTGRES_PASSWORD: test
|
|
13226
|
+
test:
|
|
13227
|
+
build: .
|
|
13228
|
+
command: bun run test:integration
|
|
13229
|
+
depends_on:
|
|
13230
|
+
- db
|
|
13231
|
+
environment:
|
|
13232
|
+
DATABASE_URL: postgres://postgres:test@db:5432/testdb
|
|
13233
|
+
\`\`\`
|
|
13234
|
+
|
|
13235
|
+
\`\`\`bash
|
|
13236
|
+
docker-compose -f docker-compose.test.yml up --abort-on-container-exit
|
|
13237
|
+
docker-compose -f docker-compose.test.yml down
|
|
13238
|
+
\`\`\`
|
|
13239
|
+
|
|
13240
|
+
### Optimize Dockerfile
|
|
13241
|
+
|
|
13242
|
+
**Anti-pattern:**
|
|
13243
|
+
\`\`\`dockerfile
|
|
13244
|
+
FROM node:22
|
|
13245
|
+
WORKDIR /app
|
|
13246
|
+
COPY . . # Copies everything (including node_modules, .git)
|
|
13247
|
+
RUN bun install # Invalidates cache on any file change
|
|
13248
|
+
CMD ["bun", "run", "start"]
|
|
13249
|
+
\`\`\`
|
|
13250
|
+
|
|
13251
|
+
**Optimized:**
|
|
13252
|
+
\`\`\`dockerfile
|
|
13253
|
+
FROM node:22-slim # Use slim variant
|
|
13254
|
+
WORKDIR /app
|
|
13255
|
+
|
|
13256
|
+
# Copy dependency files first (cache layer)
|
|
13257
|
+
COPY package.json bun.lockb ./
|
|
13258
|
+
RUN bun install --production
|
|
13259
|
+
|
|
13260
|
+
# Copy source code (changes frequently)
|
|
13261
|
+
COPY src ./src
|
|
13262
|
+
COPY tsconfig.json ./
|
|
13263
|
+
|
|
13264
|
+
CMD ["bun", "run", "start"]
|
|
13265
|
+
\`\`\`
|
|
13266
|
+
|
|
13267
|
+
**Add \`.dockerignore\`:**
|
|
13268
|
+
\`\`\`
|
|
13269
|
+
node_modules
|
|
13270
|
+
.git
|
|
13271
|
+
.env
|
|
13272
|
+
*.log
|
|
13273
|
+
dist
|
|
13274
|
+
.DS_Store
|
|
13275
|
+
\`\`\`
|
|
13276
|
+
|
|
13277
|
+
### Handle Missing Dependencies
|
|
13278
|
+
|
|
13279
|
+
**Problem:** Command fails with "not found" in container.
|
|
13280
|
+
|
|
13281
|
+
**Pattern:**
|
|
13282
|
+
1. Check if dependency is in image:
|
|
13283
|
+
\`\`\`bash
|
|
13284
|
+
docker run -it myapp:latest which git
|
|
13285
|
+
\`\`\`
|
|
13286
|
+
2. If missing, add to Dockerfile:
|
|
13287
|
+
\`\`\`dockerfile
|
|
13288
|
+
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*
|
|
13289
|
+
\`\`\`
|
|
13290
|
+
3. Or use a richer base image (e.g., \`node:22\` instead of \`node:22-slim\`).
|
|
13291
|
+
|
|
13292
|
+
## Hive Sandbox Integration
|
|
13293
|
+
|
|
13294
|
+
### How Hive Wraps Commands
|
|
13295
|
+
|
|
13296
|
+
When sandbox mode is active (\`sandbox: 'docker'\` in config):
|
|
13297
|
+
1. Hive hook intercepts bash commands before execution
|
|
13298
|
+
2. Wraps with \`docker run --rm -v <worktree>:/workspace -w /workspace <image> sh -c "<command>"\`
|
|
13299
|
+
3. Command runs in container, but file edits (Read/Write/Edit) still affect host
|
|
13300
|
+
|
|
13301
|
+
**Workers are unaware** — they issue normal bash commands, Hive handles containerization.
|
|
13302
|
+
|
|
13303
|
+
### When Host Access is Needed
|
|
13304
|
+
|
|
13305
|
+
Some operations MUST run on host:
|
|
13306
|
+
- **Git operations** (commit, push, branch) — repo state is on host
|
|
13307
|
+
- **Host-level tools** (Docker itself, system config)
|
|
13308
|
+
- **Cross-worktree operations** (accessing main repo from worktree)
|
|
13309
|
+
|
|
13310
|
+
**Pattern:** Use \`HOST:\` prefix to escape sandbox:
|
|
13311
|
+
\`\`\`bash
|
|
13312
|
+
HOST: git status
|
|
13313
|
+
HOST: docker ps
|
|
13314
|
+
\`\`\`
|
|
13315
|
+
|
|
13316
|
+
**If you need host access frequently:** Report as blocked and ask user if sandbox should be disabled for this task.
|
|
13317
|
+
|
|
13318
|
+
### Persistent vs Ephemeral Containers
|
|
13319
|
+
|
|
13320
|
+
**Current (v1.2.0):** Each command runs \`docker run --rm\` (ephemeral). State does NOT persist.
|
|
13321
|
+
|
|
13322
|
+
Example: \`npm install lodash\` in one command → not available in next command.
|
|
13323
|
+
|
|
13324
|
+
**Workaround:** Install dependencies in Dockerfile, not at runtime.
|
|
13325
|
+
|
|
13326
|
+
**Future:** \`docker exec\` will reuse containers, persisting state across commands.
|
|
13327
|
+
|
|
13328
|
+
### Auto-Detected Images
|
|
13329
|
+
|
|
13330
|
+
Hive detects runtime from project files:
|
|
13331
|
+
- \`package.json\` → \`node:22-slim\`
|
|
13332
|
+
- \`requirements.txt\` / \`pyproject.toml\` → \`python:3.12-slim\`
|
|
13333
|
+
- \`go.mod\` → \`golang:1.22-slim\`
|
|
13334
|
+
- \`Cargo.toml\` → \`rust:1.77-slim\`
|
|
13335
|
+
- \`Dockerfile\` → Builds from project Dockerfile
|
|
13336
|
+
- Fallback → \`ubuntu:24.04\`
|
|
13337
|
+
|
|
13338
|
+
**Override:** Set \`dockerImage\` in config (\`~/.config/opencode/agent_hive.json\`).
|
|
13339
|
+
|
|
13340
|
+
## Red Flags - STOP
|
|
13341
|
+
|
|
13342
|
+
If you catch yourself:
|
|
13343
|
+
- Installing packages on host instead of in Dockerfile
|
|
13344
|
+
- Running \`docker build\` without \`.dockerignore\` (cache invalidation)
|
|
13345
|
+
- Using \`latest\` tag in production (non-reproducible)
|
|
13346
|
+
- Ignoring container exit codes (hides failures)
|
|
13347
|
+
- Assuming state persists between \`docker run --rm\` commands
|
|
13348
|
+
- Using absolute host paths in Dockerfile (not portable)
|
|
13349
|
+
- Copying secrets into image layers (leaks credentials)
|
|
13350
|
+
|
|
13351
|
+
**ALL of these mean: STOP. Review pattern.**
|
|
13352
|
+
|
|
13353
|
+
## Anti-Patterns
|
|
13354
|
+
|
|
13355
|
+
| Excuse | Reality |
|
|
13356
|
+
|--------|---------|
|
|
13357
|
+
| "I'll just run it on host" | Container mismatch bugs are worse to debug later. Build happens in container anyway. |
|
|
13358
|
+
| "Works in my container, don't need CI" | CI uses different cache state. Always test in CI-like environment. |
|
|
13359
|
+
| "I'll optimize the Dockerfile later" | Later never comes. Large images slow down deployments now. |
|
|
13360
|
+
| "latest tag is fine for dev" | Dev should match prod. Pin versions or face surprises. |
|
|
13361
|
+
| "Don't need .dockerignore, COPY is fast" | Invalidates cache on every file change. Wastes minutes per build. |
|
|
13362
|
+
| "Install at runtime, not in image" | Ephemeral containers lose state. Slows down every command. |
|
|
13363
|
+
| "Skip depends_on, services start fast" | Race conditions in integration tests. Use wait-for-it or health checks. |
|
|
13364
|
+
|
|
13365
|
+
## Verification Before Completion
|
|
13366
|
+
|
|
13367
|
+
Before marking Docker work complete:
|
|
13368
|
+
|
|
13369
|
+
- [ ] Container runs successfully: \`docker run --rm <image> <command>\` exits 0
|
|
13370
|
+
- [ ] Tests pass inside container (not just on host)
|
|
13371
|
+
- [ ] No host pollution (dependencies installed in container, not host)
|
|
13372
|
+
- [ ] \`.dockerignore\` exists if using \`COPY . .\`
|
|
13373
|
+
- [ ] Image tags are pinned (not \`latest\`) for production
|
|
13374
|
+
- [ ] Multi-stage build used if applicable (separate build/runtime)
|
|
13375
|
+
- [ ] Integration tests teardown properly (\`docker-compose down\`)
|
|
13376
|
+
|
|
13377
|
+
**If any fail:** Don't claim success. Fix or report blocker.
|
|
13378
|
+
|
|
13379
|
+
## Quick Reference
|
|
13380
|
+
|
|
13381
|
+
| Task | Command Pattern |
|
|
13382
|
+
|------|----------------|
|
|
13383
|
+
| **Debug container** | \`docker run -it --entrypoint sh <image>\` |
|
|
13384
|
+
| **Run with mounts** | \`docker run -v $(pwd):/app <image>\` |
|
|
13385
|
+
| **Multi-service tests** | \`docker-compose up --abort-on-container-exit\` |
|
|
13386
|
+
| **Check image contents** | \`docker run --rm <image> ls /app\` |
|
|
13387
|
+
| **Optimize build** | Add \`.dockerignore\`, use multi-stage, pin versions |
|
|
13388
|
+
| **Escape Hive sandbox** | Prefix with \`HOST:\` (e.g., \`HOST: git status\`) |
|
|
13389
|
+
|
|
13390
|
+
## Related Skills
|
|
13391
|
+
|
|
13392
|
+
- **hive_skill:systematic-debugging** - When container behavior is unexpected
|
|
13393
|
+
- **hive_skill:test-driven-development** - Write tests that run in containers
|
|
13394
|
+
- **hive_skill:verification-before-completion** - Verify tests pass in container before claiming done`
|
|
12798
13395
|
},
|
|
12799
13396
|
{
|
|
12800
13397
|
name: "executing-plans",
|
|
@@ -12857,8 +13454,8 @@ Based on feedback:
|
|
|
12857
13454
|
### Step 6: Complete Development
|
|
12858
13455
|
|
|
12859
13456
|
After all tasks complete and verified:
|
|
12860
|
-
- Announce: "I'm using the
|
|
12861
|
-
- **REQUIRED SUB-SKILL:** Use hive_skill:
|
|
13457
|
+
- Announce: "I'm using the verification-before-completion skill to complete this work."
|
|
13458
|
+
- **REQUIRED SUB-SKILL:** Use hive_skill:verification-before-completion
|
|
12862
13459
|
- Follow that skill to verify tests, present options, execute choice
|
|
12863
13460
|
|
|
12864
13461
|
## When to Stop and Ask for Help
|
|
@@ -12886,66 +13483,6 @@ After all tasks complete and verified:
|
|
|
12886
13483
|
- Reference skills when plan says to
|
|
12887
13484
|
- Between batches: just report and wait
|
|
12888
13485
|
- Stop when blocked, don't guess`
|
|
12889
|
-
},
|
|
12890
|
-
{
|
|
12891
|
-
name: "onboarding",
|
|
12892
|
-
description: "Ask about workflow preferences and store them in .hive/contexts/preferences.md before proceeding.",
|
|
12893
|
-
template: `# Onboarding Preferences
|
|
12894
|
-
|
|
12895
|
-
## Overview
|
|
12896
|
-
|
|
12897
|
-
Gather workflow preferences so the assistant can match the user's desired working style.
|
|
12898
|
-
|
|
12899
|
-
## When to Ask
|
|
12900
|
-
|
|
12901
|
-
- **Immediately when the skill is loaded**, before any other work.
|
|
12902
|
-
- If \`.hive/contexts/preferences.md\` does not exist, start onboarding.
|
|
12903
|
-
- If later a decision is ambiguous and preferences are missing, ask again.
|
|
12904
|
-
|
|
12905
|
-
## Preference Storage
|
|
12906
|
-
|
|
12907
|
-
Use \`hive_context_write\` to write \`.hive/contexts/preferences.md\` with this exact template:
|
|
12908
|
-
|
|
12909
|
-
\`\`\`
|
|
12910
|
-
# Preferences
|
|
12911
|
-
|
|
12912
|
-
## Exploration Style
|
|
12913
|
-
sync
|
|
12914
|
-
|
|
12915
|
-
## Research Depth
|
|
12916
|
-
medium
|
|
12917
|
-
|
|
12918
|
-
## Confirmation Level
|
|
12919
|
-
standard
|
|
12920
|
-
|
|
12921
|
-
## Commit Behavior
|
|
12922
|
-
ask-before-commit
|
|
12923
|
-
\`\`\`
|
|
12924
|
-
|
|
12925
|
-
## If Preferences Already Exist
|
|
12926
|
-
|
|
12927
|
-
Follow the same pattern used in \`packages/vscode-hive/src/tools/plan.ts\`:
|
|
12928
|
-
|
|
12929
|
-
1. Use \`contextService.list(feature)\` to detect existing contexts.
|
|
12930
|
-
2. Ask **"Preferences already exist. Keep or overwrite?"** using the \`question()\` tool.
|
|
12931
|
-
3. If keep → continue using existing preferences.
|
|
12932
|
-
4. If overwrite → collect new answers and write them with \`hive_context_write\`.
|
|
12933
|
-
|
|
12934
|
-
## Questions to Ask (Always use \`question()\`)
|
|
12935
|
-
|
|
12936
|
-
Ask one at a time, with the provided options. Store the answers in \`.hive/contexts/preferences.md\`.
|
|
12937
|
-
|
|
12938
|
-
1. **Exploration Style:** sync | async
|
|
12939
|
-
2. **Research Depth:** shallow | medium | deep
|
|
12940
|
-
3. **Confirmation Level:** minimal | standard | high
|
|
12941
|
-
4. **Commit Behavior:** ask-before-commit | auto-commit | never-commit
|
|
12942
|
-
|
|
12943
|
-
## Requirements
|
|
12944
|
-
|
|
12945
|
-
- Use the \`question()\` tool (no plain text questions).
|
|
12946
|
-
- Ask immediately when the skill loads if preferences are missing.
|
|
12947
|
-
- If later a decision is ambiguous and preferences are missing, ask again.
|
|
12948
|
-
- Always store answers using \`hive_context_write\` with the template above.`
|
|
12949
13486
|
},
|
|
12950
13487
|
{
|
|
12951
13488
|
name: "parallel-exploration",
|
|
@@ -13837,7 +14374,7 @@ Never fix bugs without a test.
|
|
|
13837
14374
|
|
|
13838
14375
|
## Testing Anti-Patterns
|
|
13839
14376
|
|
|
13840
|
-
When adding mocks or test utilities,
|
|
14377
|
+
When adding mocks or test utilities, avoid common pitfalls:
|
|
13841
14378
|
- Testing mock behavior instead of real behavior
|
|
13842
14379
|
- Adding test-only methods to production classes
|
|
13843
14380
|
- Mocking without understanding dependencies
|
|
@@ -14100,6 +14637,12 @@ Always include **Depends on** for each task. Use \`none\` to enable parallel sta
|
|
|
14100
14637
|
**Verify**:
|
|
14101
14638
|
- [ ] Run: \`{command}\` → {expected}
|
|
14102
14639
|
- [ ] {Additional acceptance criteria}
|
|
14640
|
+
|
|
14641
|
+
All verification MUST be agent-executable (no human intervention):
|
|
14642
|
+
✅ \`bun test\` → all pass
|
|
14643
|
+
✅ \`curl -X POST /api/x\` → 201
|
|
14644
|
+
❌ "User manually tests..."
|
|
14645
|
+
❌ "Visually confirm..."
|
|
14103
14646
|
\`\`\`\`
|
|
14104
14647
|
|
|
14105
14648
|
## Remember
|
|
@@ -14108,6 +14651,7 @@ Always include **Depends on** for each task. Use \`none\` to enable parallel sta
|
|
|
14108
14651
|
- Exact commands with expected output
|
|
14109
14652
|
- Reference relevant skills with @ syntax
|
|
14110
14653
|
- DRY, YAGNI, TDD, frequent commits
|
|
14654
|
+
- All acceptance criteria must be agent-executable (zero human intervention)
|
|
14111
14655
|
|
|
14112
14656
|
## Execution Handoff
|
|
14113
14657
|
|
|
@@ -14317,6 +14861,19 @@ Before major transitions, verify:
|
|
|
14317
14861
|
- [ ] Scope defined?
|
|
14318
14862
|
- [ ] No critical ambiguities?
|
|
14319
14863
|
|
|
14864
|
+
### Turn Termination
|
|
14865
|
+
|
|
14866
|
+
Valid endings:
|
|
14867
|
+
- Ask a concrete question
|
|
14868
|
+
- Update draft + ask a concrete question
|
|
14869
|
+
- Explicitly state you are waiting on background work (tool/task)
|
|
14870
|
+
- Auto-transition to the next required action
|
|
14871
|
+
|
|
14872
|
+
NEVER end with:
|
|
14873
|
+
- "Let me know if you have questions"
|
|
14874
|
+
- Summary without a follow-up action
|
|
14875
|
+
- "When you're ready..."
|
|
14876
|
+
|
|
14320
14877
|
### Loading Skills (On-Demand)
|
|
14321
14878
|
|
|
14322
14879
|
Load when detailed guidance needed:
|
|
@@ -14325,6 +14882,11 @@ Load when detailed guidance needed:
|
|
|
14325
14882
|
- \`hive_skill("dispatching-parallel-agents")\` - parallel task delegation
|
|
14326
14883
|
- \`hive_skill("parallel-exploration")\` - parallel read-only research via task() (Scout fan-out)
|
|
14327
14884
|
- \`hive_skill("executing-plans")\` - step-by-step plan execution
|
|
14885
|
+
- \`hive_skill("systematic-debugging")\` - encountering bugs, test failures, or unexpected behavior
|
|
14886
|
+
- \`hive_skill("test-driven-development")\` - implementing features with TDD approach
|
|
14887
|
+
- \`hive_skill("verification-before-completion")\` - before claiming work is complete or creating PRs
|
|
14888
|
+
- \`hive_skill("docker-mastery")\` - working with Docker containers, debugging, docker-compose
|
|
14889
|
+
- \`hive_skill("agents-md-mastery")\` - bootstrapping/updating AGENTS.md, quality review
|
|
14328
14890
|
|
|
14329
14891
|
Load ONE skill at a time. Only when you need guidance beyond this prompt.
|
|
14330
14892
|
|
|
@@ -14445,6 +15007,17 @@ After completing and merging a batch:
|
|
|
14445
15007
|
2. If yes, run \`task({ subagent_type: "hygienic", prompt: "Review implementation changes from the latest batch." })\`.
|
|
14446
15008
|
3. Apply feedback before starting the next batch.
|
|
14447
15009
|
|
|
15010
|
+
### AGENTS.md Maintenance
|
|
15011
|
+
|
|
15012
|
+
After feature completion (all tasks merged):
|
|
15013
|
+
1. Sync context findings to AGENTS.md: \`hive_agents_md({ action: "sync", feature: "feature-name" })\`
|
|
15014
|
+
2. Review the proposed diff with the user
|
|
15015
|
+
3. Apply approved changes to keep AGENTS.md current
|
|
15016
|
+
|
|
15017
|
+
For projects without AGENTS.md:
|
|
15018
|
+
- Bootstrap with \`hive_agents_md({ action: "init" })\`
|
|
15019
|
+
- Generates initial documentation from codebase analysis
|
|
15020
|
+
|
|
14448
15021
|
### Orchestration Iron Laws
|
|
14449
15022
|
|
|
14450
15023
|
- Delegate by default
|
|
@@ -14462,11 +15035,19 @@ After completing and merging a batch:
|
|
|
14462
15035
|
- Ask user before consulting Hygienic (Consultant/Reviewer/Debugger)
|
|
14463
15036
|
- Load skills on-demand, one at a time
|
|
14464
15037
|
|
|
14465
|
-
|
|
15038
|
+
### Hard Blocks
|
|
15039
|
+
|
|
15040
|
+
NEVER violate:
|
|
14466
15041
|
- Skip phase detection
|
|
14467
15042
|
- Mix planning and orchestration in same action
|
|
14468
15043
|
- Auto-load all skills at start
|
|
14469
15044
|
|
|
15045
|
+
### Anti-Patterns
|
|
15046
|
+
|
|
15047
|
+
BLOCKING violations:
|
|
15048
|
+
- Ending a turn without a next action
|
|
15049
|
+
- Asking for user input in plain text instead of question()
|
|
15050
|
+
|
|
14470
15051
|
**User Input:** ALWAYS use \`question()\` tool for any user input - NEVER ask questions via plain text. This ensures structured responses.
|
|
14471
15052
|
`;
|
|
14472
15053
|
|
|
@@ -14477,25 +15058,38 @@ PLANNER, NOT IMPLEMENTER. "Do X" means "create plan for X".
|
|
|
14477
15058
|
|
|
14478
15059
|
## Intent Classification (First)
|
|
14479
15060
|
|
|
14480
|
-
| Intent | Signals | Action |
|
|
14481
|
-
|
|
14482
|
-
| Trivial | Single file, <10 lines | Do directly. No plan needed. |
|
|
14483
|
-
| Simple | 1-2 files, <30 min | Light interview → quick plan |
|
|
14484
|
-
| Complex | 3+ files, review needed | Full discovery → detailed plan |
|
|
14485
|
-
| Refactor | Existing code changes | Safety:
|
|
14486
|
-
| Greenfield | New feature |
|
|
15061
|
+
| Intent | Signals | Strategy | Action |
|
|
15062
|
+
|--------|---------|----------|--------|
|
|
15063
|
+
| Trivial | Single file, <10 lines | N/A | Do directly. No plan needed. |
|
|
15064
|
+
| Simple | 1-2 files, <30 min | Quick assessment | Light interview → quick plan |
|
|
15065
|
+
| Complex | 3+ files, review needed | Full discovery | Full discovery → detailed plan |
|
|
15066
|
+
| Refactor | Existing code changes | Safety-first: behavior preservation | Tests → blast radius → plan |
|
|
15067
|
+
| Greenfield | New feature | Discovery-first: explore before asking | Research → interview → plan |
|
|
15068
|
+
| Architecture | Cross-cutting, multi-system | Strategic: consult Scout | Deep research → plan |
|
|
14487
15069
|
|
|
14488
15070
|
During Planning, use \`task({ subagent_type: "scout-researcher", ... })\` for exploration (BLOCKING — returns when done). For parallel exploration, issue multiple \`task()\` calls in the same message.
|
|
14489
15071
|
|
|
14490
15072
|
## Self-Clearance Check (After Every Exchange)
|
|
14491
15073
|
|
|
14492
|
-
□ Core objective
|
|
14493
|
-
□ Scope
|
|
14494
|
-
□ No critical ambiguities?
|
|
14495
|
-
□
|
|
15074
|
+
□ Core objective clearly defined?
|
|
15075
|
+
□ Scope boundaries established (IN/OUT)?
|
|
15076
|
+
□ No critical ambiguities remaining?
|
|
15077
|
+
□ Technical approach decided?
|
|
15078
|
+
□ Test strategy confirmed (TDD/tests-after/none)?
|
|
15079
|
+
□ No blocking questions outstanding?
|
|
15080
|
+
|
|
15081
|
+
ALL YES → Announce "Requirements clear. Generating plan." → Write plan
|
|
15082
|
+
ANY NO → Ask the specific unclear thing
|
|
15083
|
+
|
|
15084
|
+
## Test Strategy (Ask Before Planning)
|
|
14496
15085
|
|
|
14497
|
-
|
|
14498
|
-
|
|
15086
|
+
For Build and Refactor intents, ASK:
|
|
15087
|
+
"Should this include automated tests?"
|
|
15088
|
+
- TDD: Red-Green-Refactor per task
|
|
15089
|
+
- Tests after: Add test tasks after implementation
|
|
15090
|
+
- None: No unit/integration tests
|
|
15091
|
+
|
|
15092
|
+
Record decision in draft. Embed in plan tasks.
|
|
14499
15093
|
|
|
14500
15094
|
## AI-Slop Flags
|
|
14501
15095
|
|
|
@@ -14515,6 +15109,18 @@ ANY NO → Ask the unclear thing
|
|
|
14515
15109
|
| MINOR | FIX silently, note in summary |
|
|
14516
15110
|
| AMBIGUOUS | Apply default, DISCLOSE in summary |
|
|
14517
15111
|
|
|
15112
|
+
## Turn Termination
|
|
15113
|
+
|
|
15114
|
+
Valid endings:
|
|
15115
|
+
- Question to user (via question() tool)
|
|
15116
|
+
- Draft update + next question
|
|
15117
|
+
- Auto-transition to plan generation
|
|
15118
|
+
|
|
15119
|
+
NEVER end with:
|
|
15120
|
+
- "Let me know if you have questions"
|
|
15121
|
+
- Summary without follow-up action
|
|
15122
|
+
- "When you're ready..."
|
|
15123
|
+
|
|
14518
15124
|
## Draft as Working Memory
|
|
14519
15125
|
|
|
14520
15126
|
Create draft on first exchange. Update after EVERY user response:
|
|
@@ -14631,11 +15237,13 @@ hive_worktree_create({ task: "01-task-name" })
|
|
|
14631
15237
|
- Call \`hive_status()\` immediately after to check new state and find next runnable tasks
|
|
14632
15238
|
- For parallel fan-out, issue multiple \`task()\` calls in the same message
|
|
14633
15239
|
|
|
14634
|
-
## After Delegation -
|
|
15240
|
+
## After Delegation - VERIFY
|
|
14635
15241
|
|
|
15242
|
+
After every delegation, check:
|
|
14636
15243
|
- Does it work as expected?
|
|
14637
|
-
- Followed existing codebase
|
|
14638
|
-
-
|
|
15244
|
+
- Followed existing codebase patterns?
|
|
15245
|
+
- Met MUST DO and MUST NOT DO requirements?
|
|
15246
|
+
- No unintended side effects?
|
|
14639
15247
|
|
|
14640
15248
|
## Blocker Handling
|
|
14641
15249
|
|
|
@@ -14649,8 +15257,7 @@ When worker reports blocked:
|
|
|
14649
15257
|
1. STOP all further edits
|
|
14650
15258
|
2. REVERT to last known working state
|
|
14651
15259
|
3. DOCUMENT what was attempted
|
|
14652
|
-
4.
|
|
14653
|
-
5. If Oracle cannot resolve → ASK USER
|
|
15260
|
+
4. ASK USER via question() — present options and context
|
|
14654
15261
|
|
|
14655
15262
|
## Merge Strategy
|
|
14656
15263
|
|
|
@@ -14660,13 +15267,39 @@ hive_merge({ task: "01-task-name", strategy: "merge" })
|
|
|
14660
15267
|
|
|
14661
15268
|
Merge only after verification passes.
|
|
14662
15269
|
|
|
14663
|
-
|
|
15270
|
+
### Post-Batch Review (Hygienic)
|
|
14664
15271
|
|
|
14665
15272
|
After completing and merging a batch:
|
|
14666
15273
|
1. Ask the user via \`question()\` if they want a Hygienic code review for the batch.
|
|
14667
15274
|
2. If yes, run \`task({ subagent_type: "hygienic", prompt: "Review implementation changes from the latest batch." })\`.
|
|
14668
15275
|
3. Apply feedback before starting the next batch.
|
|
14669
15276
|
|
|
15277
|
+
### AGENTS.md Maintenance
|
|
15278
|
+
|
|
15279
|
+
After completing and merging a batch:
|
|
15280
|
+
1. Sync context findings to AGENTS.md: \`hive_agents_md({ action: "sync", feature: "feature-name" })\`
|
|
15281
|
+
2. Review the proposed diff with the user
|
|
15282
|
+
3. Apply approved changes to keep AGENTS.md current
|
|
15283
|
+
|
|
15284
|
+
For quality review of AGENTS.md content, load \`hive_skill("agents-md-mastery")\`.
|
|
15285
|
+
|
|
15286
|
+
For projects without AGENTS.md:
|
|
15287
|
+
- Bootstrap with \`hive_agents_md({ action: "init" })\`
|
|
15288
|
+
- Generates initial documentation from codebase analysis
|
|
15289
|
+
|
|
15290
|
+
## Turn Termination
|
|
15291
|
+
|
|
15292
|
+
Valid endings:
|
|
15293
|
+
- Worker delegation (hive_worktree_create)
|
|
15294
|
+
- Status check (hive_status)
|
|
15295
|
+
- User question (question())
|
|
15296
|
+
- Merge (hive_merge)
|
|
15297
|
+
|
|
15298
|
+
NEVER end with:
|
|
15299
|
+
- "Let me know when you're ready"
|
|
15300
|
+
- Summary without next action
|
|
15301
|
+
- Waiting for something unspecified
|
|
15302
|
+
|
|
14670
15303
|
## Iron Laws
|
|
14671
15304
|
|
|
14672
15305
|
**Never:**
|
|
@@ -14778,84 +15411,12 @@ When asked to retrieve raw data from external systems (MongoDB/Stripe/etc.):
|
|
|
14778
15411
|
|
|
14779
15412
|
## Persistence
|
|
14780
15413
|
|
|
14781
|
-
When operating within a feature context
|
|
14782
|
-
- If findings are substantial (3+ files
|
|
14783
|
-
Use \`hive_context_write\` to persist findings:
|
|
15414
|
+
When operating within a feature context:
|
|
15415
|
+
- If findings are substantial (3+ files, architecture patterns, or key decisions):
|
|
14784
15416
|
\`\`\`
|
|
14785
15417
|
hive_context_write({
|
|
14786
|
-
name: "research-{topic
|
|
14787
|
-
content: "##
|
|
14788
|
-
|
|
14789
|
-
Date: {date}
|
|
14790
|
-
|
|
14791
|
-
## Context
|
|
14792
|
-
|
|
14793
|
-
## research-findings
|
|
14794
|
-
|
|
14795
|
-
# Research Findings for Hive Improvements v2
|
|
14796
|
-
|
|
14797
|
-
## Worker Prompt Builder (\`worker-prompt.ts:48\`)
|
|
14798
|
-
- \`buildWorkerPrompt(params: WorkerPromptParams): string\`
|
|
14799
|
-
- Receives: feature, task, taskOrder, worktreePath, branch, plan, contextFiles, spec, previousTasks, continueFrom
|
|
14800
|
-
- Only uses: feature, task, taskOrder, worktreePath, branch, spec, continueFrom
|
|
14801
|
-
- plan/contextFiles/previousTasks passed but NOT used (already embedded in spec)
|
|
14802
|
-
- 10 sections: Assignment, Continuation(optional), Mission(=spec), Blocker Protocol, Completion Protocol, TDD, Debugging, Tools, Guidelines, User Input
|
|
14803
|
-
- **ZERO task-type awareness** — all workers get identical protocols
|
|
14804
|
-
- Budget: 100KB soft limit (advisory, not enforced)
|
|
14805
|
-
|
|
14806
|
-
## Task Completion Flow (\`index.ts:974-1088\`)
|
|
14807
|
-
- \`hive_exec_complete\` accepts: task, summary (string), status (completed|blocked|failed|partial), blocker (optional)
|
|
14808
|
-
- Summary stored in: status.json, report.md, commit message (first 50 chars)
|
|
14809
|
-
- **Summary is free-form string** — no structure enforced
|
|
14810
|
-
- Completed summaries collected for next task: \`allTasks.filter(t => t.status === 'done' && t.summary)\`
|
|
14811
|
-
- Injected into spec as \`## Completed Tasks\` → \`- taskName: summary\`
|
|
14812
|
-
|
|
14813
|
-
## TaskService (\`taskService.ts\`)
|
|
14814
|
-
- \`buildSpecContent()\` (lines 168-225): builds spec with Dependencies, Plan Section, Context, Completed Tasks
|
|
14815
|
-
- \`parseTasksFromPlan()\` (lines 532-602): regex \`/^###\\s+(\\d+)\\.\\s+(.+)$/\` for task headers
|
|
14816
|
-
- \`resolveDependencies()\` (lines 248-268): explicit deps or implicit sequential (N depends on N-1)
|
|
14817
|
-
- Types: TaskStatus has \`summary?: string\`, TaskInfo has \`summary?: string\`
|
|
14818
|
-
|
|
14819
|
-
## Forager Agent (\`forager.ts:8-117\`)
|
|
14820
|
-
- Execution flow: Understand → Implement → Verify → Report
|
|
14821
|
-
- **NO orient/pre-flight phase** — jumps straight to understanding task spec
|
|
14822
|
-
- Can read codebase, use research tools (grep_app, context7, ast_grep)
|
|
14823
|
-
- Cannot: delegate (task/hive_exec_start), modify plan, use hive_merge
|
|
14824
|
-
- Notepads: \`.hive/features/{feature}/notepads/{learnings,issues,decisions}.md\` (append-only)
|
|
14825
|
-
|
|
14826
|
-
## Hygienic Agent (\`hygienic.ts:8-105\`)
|
|
14827
|
-
- Reviews plan DOCUMENTATION quality, not design
|
|
14828
|
-
- 4 criteria: Clarity, Verifiability, Completeness, Big Picture
|
|
14829
|
-
- Verdict: OKAY or REJECT with 4-category assessment
|
|
14830
|
-
- When asked to review implementation → loads \`hive_skill("code-reviewer")\`
|
|
14831
|
-
- **Currently only invoked for plan review** (from Hive and Architect agents)
|
|
14832
|
-
- Cannot delegate/spawn workers
|
|
14833
|
-
|
|
14834
|
-
## Scout Agent (\`scout.ts:8-112\`)
|
|
14835
|
-
- Read-only research agent
|
|
14836
|
-
- Classifies requests: CONCEPTUAL, IMPLEMENTATION, CODEBASE, COMPREHENSIVE
|
|
14837
|
-
- Output format: \`<results><files>...<answer>...<next_steps>...</results>\`
|
|
14838
|
-
- **Does NOT persist findings** — returns to orchestrator only
|
|
14839
|
-
- Parallel execution by default (3+ tools simultaneously)
|
|
14840
|
-
|
|
14841
|
-
## Code-Reviewer Skill (\`skills/code-reviewer/SKILL.md\`)
|
|
14842
|
-
- Loaded by Hygienic when reviewing implementation
|
|
14843
|
-
- Output: APPROVE | REQUEST_CHANGES | NEEDS_DISCUSSION
|
|
14844
|
-
- Reviews: plan adherence, correctness, simplicity/YAGNI, risk
|
|
14845
|
-
- Already exists but underused (Hygienic only loads it when explicitly asked)
|
|
14846
|
-
|
|
14847
|
-
## Plan Format
|
|
14848
|
-
- Headers: \`### N. Task Name\`
|
|
14849
|
-
- Sections: Depends on, What to do, Must NOT do, References (file:lines), Acceptance Criteria
|
|
14850
|
-
- Dependencies: \`none\` | \`1\` | \`1,3\` | implicit sequential
|
|
14851
|
-
|
|
14852
|
-
## Skills (10 total)
|
|
14853
|
-
writing-plans, executing-plans, dispatching-parallel-agents, parallel-exploration, code-reviewer, onboarding, brainstorming, verification-before-completion, test-driven-development, systematic-debugging
|
|
14854
|
-
|
|
14855
|
-
## Notepad System
|
|
14856
|
-
- Location: \`.hive/features/{feature}/notepads/{learnings,issues,decisions}.md\`
|
|
14857
|
-
- Workers append-only
|
|
14858
|
-
- **NOT automatically injected into next batch** — context injection only reads from \`contexts/\` directory"
|
|
15418
|
+
name: "research-{topic}",
|
|
15419
|
+
content: "## {Topic}\\n\\nDate: {YYYY-MM-DD}\\n\\n## Context\\n\\n## Findings"
|
|
14859
15420
|
})
|
|
14860
15421
|
\`\`\`
|
|
14861
15422
|
|
|
@@ -14895,6 +15456,20 @@ CAN use for quick lookups:
|
|
|
14895
15456
|
- \`ast_grep_search\` — AST patterns
|
|
14896
15457
|
- \`glob\`, \`grep\`, \`read\` — Codebase exploration
|
|
14897
15458
|
|
|
15459
|
+
## Resolve Before Blocking
|
|
15460
|
+
|
|
15461
|
+
Default to exploration, questions are LAST resort:
|
|
15462
|
+
1. Read the referenced files and surrounding code
|
|
15463
|
+
2. Search for similar patterns in the codebase
|
|
15464
|
+
3. Try a reasonable approach based on conventions
|
|
15465
|
+
|
|
15466
|
+
Only report as blocked when:
|
|
15467
|
+
- Multiple approaches failed (tried 3+)
|
|
15468
|
+
- Decision requires business logic you can't infer
|
|
15469
|
+
- External dependency is missing or broken
|
|
15470
|
+
|
|
15471
|
+
Context inference: Before asking "what does X do?", READ X first.
|
|
15472
|
+
|
|
14898
15473
|
## Plan = READ ONLY
|
|
14899
15474
|
|
|
14900
15475
|
CRITICAL: NEVER MODIFY THE PLAN FILE
|
|
@@ -14919,8 +15494,11 @@ Read spec for:
|
|
|
14919
15494
|
### 2. Orient (Pre-flight Before Coding)
|
|
14920
15495
|
Before writing code:
|
|
14921
15496
|
- Confirm dependencies are satisfied and required context is present
|
|
15497
|
+
- Read the referenced files and surrounding code
|
|
15498
|
+
- Search for similar patterns in the codebase
|
|
14922
15499
|
- Identify the exact files/sections to touch (from references)
|
|
14923
15500
|
- Decide the first failing test you will write (TDD)
|
|
15501
|
+
- Identify the test command(s) and inputs you will run
|
|
14924
15502
|
- Plan the minimum change to reach green
|
|
14925
15503
|
|
|
14926
15504
|
### 3. Implement
|
|
@@ -14966,6 +15544,16 @@ hive_worktree_commit({
|
|
|
14966
15544
|
})
|
|
14967
15545
|
\`\`\`
|
|
14968
15546
|
|
|
15547
|
+
## Completion Checklist
|
|
15548
|
+
|
|
15549
|
+
Before calling hive_worktree_commit:
|
|
15550
|
+
- All tests in scope are run and passing (Record exact commands and results)
|
|
15551
|
+
- Build succeeds if required (Record exact command and result)
|
|
15552
|
+
- lsp_diagnostics clean on changed files (Record exact command and result)
|
|
15553
|
+
- Changes match the spec and references
|
|
15554
|
+
- No extra scope creep or unrelated edits
|
|
15555
|
+
- Summary includes what changed, why, and verification status
|
|
15556
|
+
|
|
14969
15557
|
## Failure Recovery
|
|
14970
15558
|
|
|
14971
15559
|
After 3 consecutive failures:
|
|
@@ -14975,6 +15563,15 @@ After 3 consecutive failures:
|
|
|
14975
15563
|
|
|
14976
15564
|
## Iron Laws
|
|
14977
15565
|
|
|
15566
|
+
### Docker Sandbox
|
|
15567
|
+
|
|
15568
|
+
When sandbox mode is active, ALL bash commands automatically run inside a Docker container.
|
|
15569
|
+
- Your commands are transparently wrapped — you don't need to do anything special
|
|
15570
|
+
- File edits (Read, Write, Edit tools) still work on the host filesystem (worktree is mounted)
|
|
15571
|
+
- If a command must run on the host (e.g., git operations), report as blocked and ask the user
|
|
15572
|
+
- If a command fails with "docker: command not found", report as blocked — the host needs Docker installed
|
|
15573
|
+
- For deeper Docker expertise, load \`hive_skill("docker-mastery")\`
|
|
15574
|
+
|
|
14978
15575
|
**Never:**
|
|
14979
15576
|
- Exceed task scope
|
|
14980
15577
|
- Modify plan file
|
|
@@ -15017,7 +15614,10 @@ Self-check before every critique:
|
|
|
15017
15614
|
|
|
15018
15615
|
### 2. Verification & Acceptance Criteria
|
|
15019
15616
|
- Are criteria measurable and concrete?
|
|
15020
|
-
-
|
|
15617
|
+
- Are they agent-executable (tool-runnable) without human judgment?
|
|
15618
|
+
- Do they specify exact commands + expected signals (exit code, output text, counts)?
|
|
15619
|
+
- Red flags: "should work", "looks good", "properly handles", "verify manually"
|
|
15620
|
+
- If manual checks are required, the plan must explain why automation is impossible
|
|
15021
15621
|
|
|
15022
15622
|
### 3. Context Completeness (90% Confidence)
|
|
15023
15623
|
- Could a capable worker execute with 90% confidence?
|
|
@@ -15149,6 +15749,11 @@ import * as fs8 from "fs";
|
|
|
15149
15749
|
import * as path4 from "path";
|
|
15150
15750
|
import * as fs10 from "fs";
|
|
15151
15751
|
import * as path6 from "path";
|
|
15752
|
+
import * as fs11 from "fs";
|
|
15753
|
+
import * as path7 from "path";
|
|
15754
|
+
import { existsSync as existsSync5 } from "fs";
|
|
15755
|
+
import { join as join8, sep } from "path";
|
|
15756
|
+
import { execSync } from "child_process";
|
|
15152
15757
|
var __create = Object.create;
|
|
15153
15758
|
var __getProtoOf = Object.getPrototypeOf;
|
|
15154
15759
|
var __defProp2 = Object.defineProperty;
|
|
@@ -15986,6 +16591,7 @@ var DEFAULT_HIVE_CONFIG = {
|
|
|
15986
16591
|
disableSkills: [],
|
|
15987
16592
|
disableMcps: [],
|
|
15988
16593
|
agentMode: "unified",
|
|
16594
|
+
sandbox: "none",
|
|
15989
16595
|
agents: {
|
|
15990
16596
|
"hive-master": {
|
|
15991
16597
|
model: DEFAULT_AGENT_MODELS["hive-master"],
|
|
@@ -21465,6 +22071,12 @@ class ContextService {
|
|
|
21465
22071
|
ensureDir(contextPath);
|
|
21466
22072
|
const filePath = path4.join(contextPath, this.normalizeFileName(fileName));
|
|
21467
22073
|
writeText(filePath, content);
|
|
22074
|
+
const totalChars = this.list(featureName).reduce((sum, c) => sum + c.content.length, 0);
|
|
22075
|
+
if (totalChars > 20000) {
|
|
22076
|
+
return `${filePath}
|
|
22077
|
+
|
|
22078
|
+
⚠️ Context total: ${totalChars} chars (exceeds 20,000). Consider archiving older contexts with contextService.archive().`;
|
|
22079
|
+
}
|
|
21468
22080
|
return filePath;
|
|
21469
22081
|
}
|
|
21470
22082
|
read(featureName, fileName) {
|
|
@@ -21510,6 +22122,37 @@ ${f.content}`);
|
|
|
21510
22122
|
|
|
21511
22123
|
`);
|
|
21512
22124
|
}
|
|
22125
|
+
archive(featureName) {
|
|
22126
|
+
const contexts = this.list(featureName);
|
|
22127
|
+
if (contexts.length === 0)
|
|
22128
|
+
return { archived: [], archivePath: "" };
|
|
22129
|
+
const contextPath = getContextPath(this.projectRoot, featureName);
|
|
22130
|
+
const archiveDir = path4.join(contextPath, "..", "archive");
|
|
22131
|
+
ensureDir(archiveDir);
|
|
22132
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
22133
|
+
const archived = [];
|
|
22134
|
+
for (const ctx of contexts) {
|
|
22135
|
+
const archiveName = `${timestamp}_${ctx.name}.md`;
|
|
22136
|
+
const src = path4.join(contextPath, `${ctx.name}.md`);
|
|
22137
|
+
const dest = path4.join(archiveDir, archiveName);
|
|
22138
|
+
fs8.copyFileSync(src, dest);
|
|
22139
|
+
fs8.unlinkSync(src);
|
|
22140
|
+
archived.push(ctx.name);
|
|
22141
|
+
}
|
|
22142
|
+
return { archived, archivePath: archiveDir };
|
|
22143
|
+
}
|
|
22144
|
+
stats(featureName) {
|
|
22145
|
+
const contexts = this.list(featureName);
|
|
22146
|
+
if (contexts.length === 0)
|
|
22147
|
+
return { count: 0, totalChars: 0 };
|
|
22148
|
+
const sorted2 = [...contexts].sort((a, b) => new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime());
|
|
22149
|
+
return {
|
|
22150
|
+
count: contexts.length,
|
|
22151
|
+
totalChars: contexts.reduce((sum, c) => sum + c.content.length, 0),
|
|
22152
|
+
oldest: sorted2[0].name,
|
|
22153
|
+
newest: sorted2[sorted2.length - 1].name
|
|
22154
|
+
};
|
|
22155
|
+
}
|
|
21513
22156
|
normalizeFileName(name) {
|
|
21514
22157
|
const normalized = name.replace(/\.md$/, "");
|
|
21515
22158
|
return `${normalized}.md`;
|
|
@@ -21623,6 +22266,304 @@ class ConfigService {
|
|
|
21623
22266
|
const config2 = this.get();
|
|
21624
22267
|
return config2.disableMcps ?? [];
|
|
21625
22268
|
}
|
|
22269
|
+
getSandboxConfig() {
|
|
22270
|
+
const config2 = this.get();
|
|
22271
|
+
const mode = config2.sandbox ?? "none";
|
|
22272
|
+
const image = config2.dockerImage;
|
|
22273
|
+
const persistent = config2.persistentContainers ?? mode === "docker";
|
|
22274
|
+
return { mode, ...image && { image }, persistent };
|
|
22275
|
+
}
|
|
22276
|
+
}
|
|
22277
|
+
|
|
22278
|
+
class AgentsMdService {
|
|
22279
|
+
rootDir;
|
|
22280
|
+
contextService;
|
|
22281
|
+
constructor(rootDir, contextService) {
|
|
22282
|
+
this.rootDir = rootDir;
|
|
22283
|
+
this.contextService = contextService;
|
|
22284
|
+
}
|
|
22285
|
+
async init() {
|
|
22286
|
+
const agentsMdPath = path7.join(this.rootDir, "AGENTS.md");
|
|
22287
|
+
const existed = fileExists(agentsMdPath);
|
|
22288
|
+
if (existed) {
|
|
22289
|
+
const existing = readText(agentsMdPath);
|
|
22290
|
+
return { content: existing || "", existed: true };
|
|
22291
|
+
}
|
|
22292
|
+
const content = await this.scanAndGenerate();
|
|
22293
|
+
return { content, existed: false };
|
|
22294
|
+
}
|
|
22295
|
+
async sync(featureName) {
|
|
22296
|
+
const contexts = this.contextService.list(featureName);
|
|
22297
|
+
const agentsMdPath = path7.join(this.rootDir, "AGENTS.md");
|
|
22298
|
+
const current = await fs11.promises.readFile(agentsMdPath, "utf-8").catch(() => "");
|
|
22299
|
+
const findings = this.extractFindings(contexts);
|
|
22300
|
+
const proposals = this.generateProposals(findings, current);
|
|
22301
|
+
return { proposals, diff: this.formatDiff(current, proposals) };
|
|
22302
|
+
}
|
|
22303
|
+
apply(content) {
|
|
22304
|
+
const agentsMdPath = path7.join(this.rootDir, "AGENTS.md");
|
|
22305
|
+
const isNew = !fileExists(agentsMdPath);
|
|
22306
|
+
writeText(agentsMdPath, content);
|
|
22307
|
+
return { path: agentsMdPath, chars: content.length, isNew };
|
|
22308
|
+
}
|
|
22309
|
+
extractFindings(contexts) {
|
|
22310
|
+
const findings = [];
|
|
22311
|
+
const patterns = [
|
|
22312
|
+
/we\s+use\s+[^.\n]+/gi,
|
|
22313
|
+
/prefer\s+[^.\n]+\s+over\s+[^.\n]+/gi,
|
|
22314
|
+
/don't\s+use\s+[^.\n]+/gi,
|
|
22315
|
+
/do\s+not\s+use\s+[^.\n]+/gi,
|
|
22316
|
+
/(?:build|test|dev)\s+command:\s*[^.\n]+/gi,
|
|
22317
|
+
/[a-zA-Z]+\s+lives?\s+in\s+\/[^\s.\n]+/gi
|
|
22318
|
+
];
|
|
22319
|
+
for (const context of contexts) {
|
|
22320
|
+
const lines = context.content.split(`
|
|
22321
|
+
`);
|
|
22322
|
+
for (const line of lines) {
|
|
22323
|
+
const trimmed2 = line.trim();
|
|
22324
|
+
if (!trimmed2 || trimmed2.startsWith("#"))
|
|
22325
|
+
continue;
|
|
22326
|
+
for (const pattern of patterns) {
|
|
22327
|
+
const matches = trimmed2.match(pattern);
|
|
22328
|
+
if (matches) {
|
|
22329
|
+
for (const match of matches) {
|
|
22330
|
+
const finding = match.trim();
|
|
22331
|
+
if (finding && !findings.includes(finding)) {
|
|
22332
|
+
findings.push(finding);
|
|
22333
|
+
}
|
|
22334
|
+
}
|
|
22335
|
+
}
|
|
22336
|
+
}
|
|
22337
|
+
}
|
|
22338
|
+
}
|
|
22339
|
+
return findings;
|
|
22340
|
+
}
|
|
22341
|
+
generateProposals(findings, current) {
|
|
22342
|
+
const proposals = [];
|
|
22343
|
+
const currentLower = current.toLowerCase();
|
|
22344
|
+
for (const finding of findings) {
|
|
22345
|
+
const findingLower = finding.toLowerCase();
|
|
22346
|
+
if (!currentLower.includes(findingLower)) {
|
|
22347
|
+
proposals.push(finding);
|
|
22348
|
+
}
|
|
22349
|
+
}
|
|
22350
|
+
return proposals;
|
|
22351
|
+
}
|
|
22352
|
+
formatDiff(current, proposals) {
|
|
22353
|
+
if (proposals.length === 0)
|
|
22354
|
+
return "";
|
|
22355
|
+
const lines = proposals.map((p) => `+ ${p}`);
|
|
22356
|
+
return lines.join(`
|
|
22357
|
+
`);
|
|
22358
|
+
}
|
|
22359
|
+
async scanAndGenerate() {
|
|
22360
|
+
const detections = await this.detectProjectInfo();
|
|
22361
|
+
return this.generateTemplate(detections);
|
|
22362
|
+
}
|
|
22363
|
+
async detectProjectInfo() {
|
|
22364
|
+
const packageJsonPath = path7.join(this.rootDir, "package.json");
|
|
22365
|
+
let packageJson = null;
|
|
22366
|
+
if (fileExists(packageJsonPath)) {
|
|
22367
|
+
try {
|
|
22368
|
+
const content = readText(packageJsonPath);
|
|
22369
|
+
packageJson = content ? JSON.parse(content) : null;
|
|
22370
|
+
} catch {}
|
|
22371
|
+
}
|
|
22372
|
+
const info = {
|
|
22373
|
+
packageManager: this.detectPackageManager(),
|
|
22374
|
+
language: this.detectLanguage(),
|
|
22375
|
+
testFramework: this.detectTestFramework(packageJson),
|
|
22376
|
+
buildCommand: packageJson?.scripts?.build || null,
|
|
22377
|
+
testCommand: packageJson?.scripts?.test || null,
|
|
22378
|
+
devCommand: packageJson?.scripts?.dev || null,
|
|
22379
|
+
isMonorepo: this.detectMonorepo(packageJson)
|
|
22380
|
+
};
|
|
22381
|
+
return info;
|
|
22382
|
+
}
|
|
22383
|
+
detectPackageManager() {
|
|
22384
|
+
if (fileExists(path7.join(this.rootDir, "bun.lockb")))
|
|
22385
|
+
return "bun";
|
|
22386
|
+
if (fileExists(path7.join(this.rootDir, "pnpm-lock.yaml")))
|
|
22387
|
+
return "pnpm";
|
|
22388
|
+
if (fileExists(path7.join(this.rootDir, "yarn.lock")))
|
|
22389
|
+
return "yarn";
|
|
22390
|
+
if (fileExists(path7.join(this.rootDir, "package-lock.json")))
|
|
22391
|
+
return "npm";
|
|
22392
|
+
return "npm";
|
|
22393
|
+
}
|
|
22394
|
+
detectLanguage() {
|
|
22395
|
+
if (fileExists(path7.join(this.rootDir, "tsconfig.json")))
|
|
22396
|
+
return "TypeScript";
|
|
22397
|
+
if (fileExists(path7.join(this.rootDir, "package.json")))
|
|
22398
|
+
return "JavaScript";
|
|
22399
|
+
if (fileExists(path7.join(this.rootDir, "requirements.txt")))
|
|
22400
|
+
return "Python";
|
|
22401
|
+
if (fileExists(path7.join(this.rootDir, "go.mod")))
|
|
22402
|
+
return "Go";
|
|
22403
|
+
if (fileExists(path7.join(this.rootDir, "Cargo.toml")))
|
|
22404
|
+
return "Rust";
|
|
22405
|
+
return "Unknown";
|
|
22406
|
+
}
|
|
22407
|
+
detectTestFramework(packageJson) {
|
|
22408
|
+
if (!packageJson)
|
|
22409
|
+
return null;
|
|
22410
|
+
const deps = {
|
|
22411
|
+
...packageJson.dependencies,
|
|
22412
|
+
...packageJson.devDependencies
|
|
22413
|
+
};
|
|
22414
|
+
if (deps?.vitest)
|
|
22415
|
+
return "vitest";
|
|
22416
|
+
if (deps?.jest)
|
|
22417
|
+
return "jest";
|
|
22418
|
+
if (this.detectPackageManager() === "bun")
|
|
22419
|
+
return "bun test";
|
|
22420
|
+
if (deps?.pytest)
|
|
22421
|
+
return "pytest";
|
|
22422
|
+
return null;
|
|
22423
|
+
}
|
|
22424
|
+
detectMonorepo(packageJson) {
|
|
22425
|
+
if (!packageJson)
|
|
22426
|
+
return false;
|
|
22427
|
+
return !!packageJson.workspaces;
|
|
22428
|
+
}
|
|
22429
|
+
generateTemplate(info) {
|
|
22430
|
+
const sections = [];
|
|
22431
|
+
sections.push(`# Agent Guidelines
|
|
22432
|
+
`);
|
|
22433
|
+
sections.push(`## Overview
|
|
22434
|
+
`);
|
|
22435
|
+
sections.push(`This project uses AI-assisted development. Follow these guidelines.
|
|
22436
|
+
`);
|
|
22437
|
+
sections.push(`## Build & Test Commands
|
|
22438
|
+
`);
|
|
22439
|
+
sections.push("```bash");
|
|
22440
|
+
if (info.isMonorepo) {
|
|
22441
|
+
sections.push("# This is a monorepo using bun workspaces");
|
|
22442
|
+
}
|
|
22443
|
+
if (info.buildCommand) {
|
|
22444
|
+
sections.push(`# Build`);
|
|
22445
|
+
sections.push(`${info.packageManager} run build`);
|
|
22446
|
+
sections.push("");
|
|
22447
|
+
}
|
|
22448
|
+
if (info.testCommand) {
|
|
22449
|
+
sections.push(`# Run tests`);
|
|
22450
|
+
sections.push(`${info.packageManager} ${info.testCommand === "bun test" ? "test" : "run test"}`);
|
|
22451
|
+
sections.push("");
|
|
22452
|
+
}
|
|
22453
|
+
if (info.devCommand) {
|
|
22454
|
+
sections.push(`# Development mode`);
|
|
22455
|
+
sections.push(`${info.packageManager} run dev`);
|
|
22456
|
+
}
|
|
22457
|
+
sections.push("```\n");
|
|
22458
|
+
sections.push(`## Technology Stack
|
|
22459
|
+
`);
|
|
22460
|
+
sections.push(`- **Language**: ${info.language}`);
|
|
22461
|
+
sections.push(`- **Package Manager**: ${info.packageManager}`);
|
|
22462
|
+
if (info.testFramework) {
|
|
22463
|
+
sections.push(`- **Test Framework**: ${info.testFramework}`);
|
|
22464
|
+
}
|
|
22465
|
+
if (info.isMonorepo) {
|
|
22466
|
+
sections.push(`- **Structure**: Monorepo with workspaces`);
|
|
22467
|
+
}
|
|
22468
|
+
sections.push("");
|
|
22469
|
+
sections.push(`## Code Style
|
|
22470
|
+
`);
|
|
22471
|
+
sections.push(`Follow existing patterns in the codebase.
|
|
22472
|
+
`);
|
|
22473
|
+
sections.push(`## Architecture Principles
|
|
22474
|
+
`);
|
|
22475
|
+
sections.push(`Document key architectural decisions here.
|
|
22476
|
+
`);
|
|
22477
|
+
return sections.join(`
|
|
22478
|
+
`);
|
|
22479
|
+
}
|
|
22480
|
+
}
|
|
22481
|
+
|
|
22482
|
+
class DockerSandboxService {
|
|
22483
|
+
static detectImage(worktreePath) {
|
|
22484
|
+
if (existsSync5(join8(worktreePath, "Dockerfile"))) {
|
|
22485
|
+
return null;
|
|
22486
|
+
}
|
|
22487
|
+
if (existsSync5(join8(worktreePath, "package.json"))) {
|
|
22488
|
+
return "node:22-slim";
|
|
22489
|
+
}
|
|
22490
|
+
if (existsSync5(join8(worktreePath, "requirements.txt")) || existsSync5(join8(worktreePath, "pyproject.toml"))) {
|
|
22491
|
+
return "python:3.12-slim";
|
|
22492
|
+
}
|
|
22493
|
+
if (existsSync5(join8(worktreePath, "go.mod"))) {
|
|
22494
|
+
return "golang:1.22-slim";
|
|
22495
|
+
}
|
|
22496
|
+
if (existsSync5(join8(worktreePath, "Cargo.toml"))) {
|
|
22497
|
+
return "rust:1.77-slim";
|
|
22498
|
+
}
|
|
22499
|
+
return "ubuntu:24.04";
|
|
22500
|
+
}
|
|
22501
|
+
static buildRunCommand(worktreePath, command, image) {
|
|
22502
|
+
const escapedCommand = command.replace(/'/g, "'\\''");
|
|
22503
|
+
return `docker run --rm -v ${worktreePath}:/app -w /app ${image} sh -c '${escapedCommand}'`;
|
|
22504
|
+
}
|
|
22505
|
+
static containerName(worktreePath) {
|
|
22506
|
+
const parts = worktreePath.split(sep);
|
|
22507
|
+
const worktreeIdx = parts.indexOf(".worktrees");
|
|
22508
|
+
if (worktreeIdx === -1 || worktreeIdx + 2 >= parts.length) {
|
|
22509
|
+
return `hive-sandbox-${Date.now()}`;
|
|
22510
|
+
}
|
|
22511
|
+
const feature = parts[worktreeIdx + 1];
|
|
22512
|
+
const task = parts[worktreeIdx + 2];
|
|
22513
|
+
const name = `hive-${feature}-${task}`.replace(/[^a-z0-9-]/gi, "-").toLowerCase();
|
|
22514
|
+
return name.slice(0, 63);
|
|
22515
|
+
}
|
|
22516
|
+
static ensureContainer(worktreePath, image) {
|
|
22517
|
+
const name = this.containerName(worktreePath);
|
|
22518
|
+
try {
|
|
22519
|
+
execSync(`docker inspect --format='{{.State.Running}}' ${name}`, { stdio: "pipe" });
|
|
22520
|
+
return name;
|
|
22521
|
+
} catch {
|
|
22522
|
+
execSync(`docker run -d --name ${name} -v ${worktreePath}:/app -w /app ${image} tail -f /dev/null`, { stdio: "pipe" });
|
|
22523
|
+
return name;
|
|
22524
|
+
}
|
|
22525
|
+
}
|
|
22526
|
+
static buildExecCommand(containerName, command) {
|
|
22527
|
+
const escapedCommand = command.replace(/'/g, "'\\''");
|
|
22528
|
+
return `docker exec ${containerName} sh -c '${escapedCommand}'`;
|
|
22529
|
+
}
|
|
22530
|
+
static stopContainer(worktreePath) {
|
|
22531
|
+
const name = this.containerName(worktreePath);
|
|
22532
|
+
try {
|
|
22533
|
+
execSync(`docker rm -f ${name}`, { stdio: "ignore" });
|
|
22534
|
+
} catch {}
|
|
22535
|
+
}
|
|
22536
|
+
static isDockerAvailable() {
|
|
22537
|
+
try {
|
|
22538
|
+
execSync("docker info", { stdio: "ignore" });
|
|
22539
|
+
return true;
|
|
22540
|
+
} catch {
|
|
22541
|
+
return false;
|
|
22542
|
+
}
|
|
22543
|
+
}
|
|
22544
|
+
static wrapCommand(worktreePath, command, config2) {
|
|
22545
|
+
if (command.startsWith("HOST: ")) {
|
|
22546
|
+
return command.substring(6);
|
|
22547
|
+
}
|
|
22548
|
+
if (config2.mode === "none") {
|
|
22549
|
+
return command;
|
|
22550
|
+
}
|
|
22551
|
+
let image;
|
|
22552
|
+
if (config2.image) {
|
|
22553
|
+
image = config2.image;
|
|
22554
|
+
} else {
|
|
22555
|
+
image = this.detectImage(worktreePath);
|
|
22556
|
+
if (image === null) {
|
|
22557
|
+
return command;
|
|
22558
|
+
}
|
|
22559
|
+
}
|
|
22560
|
+
if (config2.persistent) {
|
|
22561
|
+
const containerName = this.ensureContainer(worktreePath, image);
|
|
22562
|
+
return this.buildExecCommand(containerName, command);
|
|
22563
|
+
} else {
|
|
22564
|
+
return this.buildRunCommand(worktreePath, command, image);
|
|
22565
|
+
}
|
|
22566
|
+
}
|
|
21626
22567
|
}
|
|
21627
22568
|
function computeRunnableAndBlocked(tasks) {
|
|
21628
22569
|
const statusByFolder = new Map;
|
|
@@ -22306,6 +23247,7 @@ var plugin = async (ctx) => {
|
|
|
22306
23247
|
const planService = new PlanService(directory);
|
|
22307
23248
|
const taskService = new TaskService(directory);
|
|
22308
23249
|
const contextService = new ContextService(directory);
|
|
23250
|
+
const agentsMdService = new AgentsMdService(directory, contextService);
|
|
22309
23251
|
const configService = new ConfigService;
|
|
22310
23252
|
const disabledMcps = configService.getDisabledMcps();
|
|
22311
23253
|
const disabledSkills = configService.getDisabledSkills();
|
|
@@ -22314,7 +23256,7 @@ var plugin = async (ctx) => {
|
|
|
22314
23256
|
const effectiveAutoLoadSkills = configService.getAgentConfig("hive-master").autoLoadSkills ?? [];
|
|
22315
23257
|
const worktreeService = new WorktreeService({
|
|
22316
23258
|
baseDir: directory,
|
|
22317
|
-
hiveDir:
|
|
23259
|
+
hiveDir: path8.join(directory, ".hive")
|
|
22318
23260
|
});
|
|
22319
23261
|
const isOmoSlimEnabled = () => {
|
|
22320
23262
|
return configService.isOmoSlimEnabled();
|
|
@@ -22341,7 +23283,7 @@ var plugin = async (ctx) => {
|
|
|
22341
23283
|
};
|
|
22342
23284
|
const checkBlocked = (feature) => {
|
|
22343
23285
|
const fs9 = __require("fs");
|
|
22344
|
-
const blockedPath =
|
|
23286
|
+
const blockedPath = path8.join(directory, ".hive", "features", feature, "BLOCKED");
|
|
22345
23287
|
if (fs9.existsSync(blockedPath)) {
|
|
22346
23288
|
const reason = fs9.readFileSync(blockedPath, "utf-8").trim();
|
|
22347
23289
|
return `⛔ BLOCKED by Beekeeper
|
|
@@ -22426,6 +23368,31 @@ To unblock: Remove .hive/features/${feature}/BLOCKED`;
|
|
|
22426
23368
|
output.message.variant = configuredVariant;
|
|
22427
23369
|
}
|
|
22428
23370
|
},
|
|
23371
|
+
"tool.execute.before": async (input, output) => {
|
|
23372
|
+
if (input.tool !== "bash")
|
|
23373
|
+
return;
|
|
23374
|
+
const sandboxConfig = configService.getSandboxConfig();
|
|
23375
|
+
if (sandboxConfig.mode === "none")
|
|
23376
|
+
return;
|
|
23377
|
+
const command = output.args?.command?.trim();
|
|
23378
|
+
if (!command)
|
|
23379
|
+
return;
|
|
23380
|
+
if (/^HOST:\s*/i.test(command)) {
|
|
23381
|
+
const strippedCommand = command.replace(/^HOST:\s*/i, "");
|
|
23382
|
+
console.warn(`[hive:sandbox] HOST bypass: ${strippedCommand.slice(0, 80)}${strippedCommand.length > 80 ? "..." : ""}`);
|
|
23383
|
+
output.args.command = strippedCommand;
|
|
23384
|
+
return;
|
|
23385
|
+
}
|
|
23386
|
+
const workdir = output.args?.workdir;
|
|
23387
|
+
if (!workdir)
|
|
23388
|
+
return;
|
|
23389
|
+
const hiveWorktreeBase = path8.join(directory, ".hive", ".worktrees");
|
|
23390
|
+
if (!workdir.startsWith(hiveWorktreeBase))
|
|
23391
|
+
return;
|
|
23392
|
+
const wrapped = DockerSandboxService.wrapCommand(workdir, command, sandboxConfig);
|
|
23393
|
+
output.args.command = wrapped;
|
|
23394
|
+
output.args.workdir = undefined;
|
|
23395
|
+
},
|
|
22429
23396
|
mcp: builtinMcps,
|
|
22430
23397
|
tool: {
|
|
22431
23398
|
hive_skill: createHiveSkillTool(filteredSkills),
|
|
@@ -22494,8 +23461,8 @@ NEXT: Ask your first clarifying question about this feature.`;
|
|
|
22494
23461
|
const feature = resolveFeature(explicitFeature);
|
|
22495
23462
|
if (!feature)
|
|
22496
23463
|
return "Error: No feature specified. Create a feature or provide feature param.";
|
|
22497
|
-
const
|
|
22498
|
-
if (!
|
|
23464
|
+
const discoveryMatch = content.match(/^##\s+Discovery\s*$/im);
|
|
23465
|
+
if (!discoveryMatch) {
|
|
22499
23466
|
return `BLOCKED: Discovery section required before planning.
|
|
22500
23467
|
|
|
22501
23468
|
Your plan must include a \`## Discovery\` section documenting:
|
|
@@ -22504,6 +23471,19 @@ Your plan must include a \`## Discovery\` section documenting:
|
|
|
22504
23471
|
- Key decisions made
|
|
22505
23472
|
|
|
22506
23473
|
Add this section to your plan content and try again.`;
|
|
23474
|
+
}
|
|
23475
|
+
const afterDiscovery = content.slice(discoveryMatch.index + discoveryMatch[0].length);
|
|
23476
|
+
const nextHeading = afterDiscovery.search(/^##\s+/m);
|
|
23477
|
+
const discoveryContent = nextHeading > -1 ? afterDiscovery.slice(0, nextHeading).trim() : afterDiscovery.trim();
|
|
23478
|
+
if (discoveryContent.length < 100) {
|
|
23479
|
+
return `BLOCKED: Discovery section is too thin (${discoveryContent.length} chars, minimum 100).
|
|
23480
|
+
|
|
23481
|
+
A substantive Discovery section should include:
|
|
23482
|
+
- Original request quoted
|
|
23483
|
+
- Interview summary (key decisions)
|
|
23484
|
+
- Research findings with file:line references
|
|
23485
|
+
|
|
23486
|
+
Expand your Discovery section and try again.`;
|
|
22507
23487
|
}
|
|
22508
23488
|
captureSession(feature, toolContext);
|
|
22509
23489
|
const planPath = planService.write(feature, content);
|
|
@@ -22724,9 +23704,9 @@ Reminder: start work with hive_worktree_create to use its worktree, and ensure a
|
|
|
22724
23704
|
spec: specContent,
|
|
22725
23705
|
workerPrompt
|
|
22726
23706
|
});
|
|
22727
|
-
const hiveDir =
|
|
23707
|
+
const hiveDir = path8.join(directory, ".hive");
|
|
22728
23708
|
const workerPromptPath = writeWorkerPromptFile(feature, task, workerPrompt, hiveDir);
|
|
22729
|
-
const relativePromptPath = normalizePath(
|
|
23709
|
+
const relativePromptPath = normalizePath(path8.relative(directory, workerPromptPath));
|
|
22730
23710
|
const PREVIEW_MAX_LENGTH = 200;
|
|
22731
23711
|
const workerPromptPreview = workerPrompt.length > PREVIEW_MAX_LENGTH ? workerPrompt.slice(0, PREVIEW_MAX_LENGTH) + "..." : workerPrompt;
|
|
22732
23712
|
const taskToolPrompt = `Follow instructions in @${relativePromptPath}`;
|
|
@@ -23066,6 +24046,47 @@ Files changed: ${result.filesChanged?.length || 0}`;
|
|
|
23066
24046
|
nextAction: getNextAction(planStatus, tasksSummary, runnable)
|
|
23067
24047
|
});
|
|
23068
24048
|
}
|
|
24049
|
+
}),
|
|
24050
|
+
hive_agents_md: tool({
|
|
24051
|
+
description: "Initialize or sync AGENTS.md. init: scan codebase and generate (preview only). sync: propose updates from feature contexts. apply: write approved content to disk.",
|
|
24052
|
+
args: {
|
|
24053
|
+
action: tool.schema.enum(["init", "sync", "apply"]).describe("Action to perform"),
|
|
24054
|
+
feature: tool.schema.string().optional().describe("Feature name for sync action"),
|
|
24055
|
+
content: tool.schema.string().optional().describe("Content to write (required for apply action)")
|
|
24056
|
+
},
|
|
24057
|
+
async execute({ action, feature, content }) {
|
|
24058
|
+
if (action === "init") {
|
|
24059
|
+
const result = await agentsMdService.init();
|
|
24060
|
+
if (result.existed) {
|
|
24061
|
+
return `AGENTS.md already exists (${result.content.length} chars). Use 'sync' to propose updates.`;
|
|
24062
|
+
}
|
|
24063
|
+
return `Generated AGENTS.md from codebase scan (${result.content.length} chars):
|
|
24064
|
+
|
|
24065
|
+
${result.content}
|
|
24066
|
+
|
|
24067
|
+
⚠️ This has NOT been written to disk. Ask the user via question() whether to write it to AGENTS.md.`;
|
|
24068
|
+
}
|
|
24069
|
+
if (action === "sync") {
|
|
24070
|
+
if (!feature)
|
|
24071
|
+
return "Error: feature name required for sync action";
|
|
24072
|
+
const result = await agentsMdService.sync(feature);
|
|
24073
|
+
if (result.proposals.length === 0) {
|
|
24074
|
+
return "No new findings to sync to AGENTS.md.";
|
|
24075
|
+
}
|
|
24076
|
+
return `Proposed AGENTS.md updates from feature "${feature}":
|
|
24077
|
+
|
|
24078
|
+
${result.diff}
|
|
24079
|
+
|
|
24080
|
+
⚠️ These changes have NOT been applied. Ask the user via question() whether to apply them.`;
|
|
24081
|
+
}
|
|
24082
|
+
if (action === "apply") {
|
|
24083
|
+
if (!content)
|
|
24084
|
+
return "Error: content required for apply action. Use init or sync first to get content, then apply with the approved content.";
|
|
24085
|
+
const result = agentsMdService.apply(content);
|
|
24086
|
+
return `AGENTS.md ${result.isNew ? "created" : "updated"} (${result.chars} chars) at ${result.path}`;
|
|
24087
|
+
}
|
|
24088
|
+
return "Error: unknown action";
|
|
24089
|
+
}
|
|
23069
24090
|
})
|
|
23070
24091
|
},
|
|
23071
24092
|
command: {
|