opencode-task 0.1.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +576 -5
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -12329,22 +12329,593 @@ function tool(input) {
|
|
|
12329
12329
|
return input;
|
|
12330
12330
|
}
|
|
12331
12331
|
tool.schema = exports_external;
|
|
12332
|
+
// utils.ts
|
|
12333
|
+
import { appendFile } from "node:fs/promises";
|
|
12334
|
+
var AGENT_CONFIGS = {
|
|
12335
|
+
Explore: {
|
|
12336
|
+
description: "Fast agent specialized for exploring codebases. Use when you need to quickly find files by patterns, search code for keywords, or answer questions about the codebase.",
|
|
12337
|
+
prompt: "You are an exploration specialist. Search thoroughly using Glob, Grep, and Read. Report findings concisely. Do not modify any files.",
|
|
12338
|
+
tools: [
|
|
12339
|
+
"Glob",
|
|
12340
|
+
"Grep",
|
|
12341
|
+
"Read",
|
|
12342
|
+
"Bash",
|
|
12343
|
+
"LSP",
|
|
12344
|
+
"WebFetch",
|
|
12345
|
+
"WebSearch",
|
|
12346
|
+
"AskUserQuestion"
|
|
12347
|
+
]
|
|
12348
|
+
},
|
|
12349
|
+
Plan: {
|
|
12350
|
+
description: "Software architect agent for designing implementation plans. Use when you need to plan implementation strategy. Returns step-by-step plans, identifies critical files, considers architectural trade-offs.",
|
|
12351
|
+
prompt: "You are a software architect. Analyze requirements, explore the codebase thoroughly, and design clear implementation plans with specific steps. Do not modify any files.",
|
|
12352
|
+
tools: [
|
|
12353
|
+
"Glob",
|
|
12354
|
+
"Grep",
|
|
12355
|
+
"Read",
|
|
12356
|
+
"Bash",
|
|
12357
|
+
"LSP",
|
|
12358
|
+
"WebFetch",
|
|
12359
|
+
"WebSearch",
|
|
12360
|
+
"AskUserQuestion"
|
|
12361
|
+
]
|
|
12362
|
+
},
|
|
12363
|
+
"general-purpose": {
|
|
12364
|
+
description: "General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. Use for heavy lifting when simpler agents are insufficient.",
|
|
12365
|
+
prompt: "You are a general-purpose assistant. Complete the assigned task thoroughly and report results.",
|
|
12366
|
+
tools: null
|
|
12367
|
+
},
|
|
12368
|
+
"claude-code-guide": {
|
|
12369
|
+
description: "Expert on Claude Code CLI features, hooks, slash commands, MCP servers, settings, IDE integrations. Also covers Claude Agent SDK and Claude API usage.",
|
|
12370
|
+
prompt: "You are a Claude Code expert. Answer questions about Claude Code features, Agent SDK, and API usage accurately. Search documentation and web resources as needed.",
|
|
12371
|
+
tools: ["Glob", "Grep", "Read", "WebFetch", "WebSearch"]
|
|
12372
|
+
},
|
|
12373
|
+
"web-search": {
|
|
12374
|
+
description: "Web researcher for external information not in the codebase. Use for: library docs, API references, error lookups, technology research, best practices. Do NOT use for codebase questions (use Explore) or Claude Code questions (use claude-code-guide).",
|
|
12375
|
+
prompt: `Research specialist. Find accurate information, cite sources, flag uncertainty.
|
|
12376
|
+
|
|
12377
|
+
TOOLS:
|
|
12378
|
+
- WebSearch: Broad queries, discover sources
|
|
12379
|
+
- WebFetch: Extract content from specific URLs
|
|
12380
|
+
- Read/Grep/Glob: Check local files for context before searching
|
|
12381
|
+
- Bash: Use curl, gh, or CLI tools for direct API access
|
|
12382
|
+
- AskUserQuestion: Clarify ambiguous requests BEFORE searching
|
|
12383
|
+
|
|
12384
|
+
PROCEDURE:
|
|
12385
|
+
1. Parse request: factual lookup | how-to | comparison | troubleshooting
|
|
12386
|
+
2. Search: Start specific, broaden if <3 relevant results
|
|
12387
|
+
3. Verify: Single authoritative source for facts. Cross-reference for disputed topics.
|
|
12388
|
+
4. Synthesize: Answer directly, then evidence.
|
|
12389
|
+
|
|
12390
|
+
FAILURE HANDLING:
|
|
12391
|
+
- Limit to 5 source pages unless requested otherwise
|
|
12392
|
+
- If page inaccessible (paywall/JS-only), note and try alternatives
|
|
12393
|
+
- After 3 failed query reformulations, report partial findings and unknowns
|
|
12394
|
+
|
|
12395
|
+
STOP CONDITIONS:
|
|
12396
|
+
- Factual: Official source found → done
|
|
12397
|
+
- How-to: Working solution with docs link → done
|
|
12398
|
+
- Troubleshooting: Root cause found OR 3 searches no progress → report gaps
|
|
12399
|
+
|
|
12400
|
+
QUALITY:
|
|
12401
|
+
- Reject sources >2yr old for fast-moving topics
|
|
12402
|
+
- Priority: official docs > GitHub issues > Stack Overflow > blogs
|
|
12403
|
+
- On conflict, report both positions with provenance
|
|
12404
|
+
|
|
12405
|
+
OUTPUT:
|
|
12406
|
+
[Direct answer or "Unable to determine"]
|
|
12407
|
+
[Evidence with inline [source](url) citations]
|
|
12408
|
+
[Confidence: HIGH (multiple authoritative) | MEDIUM (single authoritative) | LOW (conflicting/unofficial)]`,
|
|
12409
|
+
tools: [
|
|
12410
|
+
"WebSearch",
|
|
12411
|
+
"WebFetch",
|
|
12412
|
+
"Bash",
|
|
12413
|
+
"Grep",
|
|
12414
|
+
"Glob",
|
|
12415
|
+
"Read",
|
|
12416
|
+
"AskUserQuestion"
|
|
12417
|
+
],
|
|
12418
|
+
model: "sonnet"
|
|
12419
|
+
}
|
|
12420
|
+
};
|
|
12421
|
+
|
|
12422
|
+
class Semaphore {
|
|
12423
|
+
maxConcurrent;
|
|
12424
|
+
permits;
|
|
12425
|
+
waiting = [];
|
|
12426
|
+
acquired = 0;
|
|
12427
|
+
draining = false;
|
|
12428
|
+
constructor(maxConcurrent) {
|
|
12429
|
+
this.maxConcurrent = maxConcurrent;
|
|
12430
|
+
this.permits = maxConcurrent;
|
|
12431
|
+
}
|
|
12432
|
+
async acquire() {
|
|
12433
|
+
if (this.draining)
|
|
12434
|
+
throw new Error("Semaphore is draining");
|
|
12435
|
+
if (this.permits > 0) {
|
|
12436
|
+
this.permits--;
|
|
12437
|
+
this.acquired++;
|
|
12438
|
+
return;
|
|
12439
|
+
}
|
|
12440
|
+
return new Promise((resolve, reject) => {
|
|
12441
|
+
this.waiting.push({ resolve, reject });
|
|
12442
|
+
});
|
|
12443
|
+
}
|
|
12444
|
+
release() {
|
|
12445
|
+
this.acquired--;
|
|
12446
|
+
const next = this.waiting.shift();
|
|
12447
|
+
if (next) {
|
|
12448
|
+
this.acquired++;
|
|
12449
|
+
next.resolve();
|
|
12450
|
+
} else {
|
|
12451
|
+
this.permits++;
|
|
12452
|
+
}
|
|
12453
|
+
}
|
|
12454
|
+
drain() {
|
|
12455
|
+
this.draining = true;
|
|
12456
|
+
for (const waiter of this.waiting) {
|
|
12457
|
+
waiter.reject(new Error("Semaphore drained"));
|
|
12458
|
+
}
|
|
12459
|
+
this.waiting = [];
|
|
12460
|
+
}
|
|
12461
|
+
get activeCount() {
|
|
12462
|
+
return this.acquired;
|
|
12463
|
+
}
|
|
12464
|
+
get waitingCount() {
|
|
12465
|
+
return this.waiting.length;
|
|
12466
|
+
}
|
|
12467
|
+
}
|
|
12468
|
+
function createTask(id, command, cwd, timeout, sessionID, metadata) {
|
|
12469
|
+
let resolveCompletion;
|
|
12470
|
+
const completionPromise = new Promise((r) => {
|
|
12471
|
+
resolveCompletion = r;
|
|
12472
|
+
});
|
|
12473
|
+
return {
|
|
12474
|
+
id,
|
|
12475
|
+
sessionID,
|
|
12476
|
+
command,
|
|
12477
|
+
cwd,
|
|
12478
|
+
status: "pending",
|
|
12479
|
+
stdout: `/tmp/${id}-stdout`,
|
|
12480
|
+
stderr: `/tmp/${id}-stderr`,
|
|
12481
|
+
metadata,
|
|
12482
|
+
createdAt: Date.now(),
|
|
12483
|
+
timeout,
|
|
12484
|
+
abortController: new AbortController,
|
|
12485
|
+
completionPromise,
|
|
12486
|
+
resolveCompletion
|
|
12487
|
+
};
|
|
12488
|
+
}
|
|
12489
|
+
function matchMetadata(taskMeta, filter) {
|
|
12490
|
+
if (!taskMeta)
|
|
12491
|
+
return false;
|
|
12492
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
12493
|
+
if (taskMeta[key] !== value)
|
|
12494
|
+
return false;
|
|
12495
|
+
}
|
|
12496
|
+
return true;
|
|
12497
|
+
}
|
|
12498
|
+
|
|
12499
|
+
class TaskHistory {
|
|
12500
|
+
capacity;
|
|
12501
|
+
buffer;
|
|
12502
|
+
head = 0;
|
|
12503
|
+
count = 0;
|
|
12504
|
+
constructor(capacity) {
|
|
12505
|
+
this.capacity = capacity;
|
|
12506
|
+
this.buffer = new Array(capacity);
|
|
12507
|
+
}
|
|
12508
|
+
push(entry) {
|
|
12509
|
+
this.buffer[this.head] = entry;
|
|
12510
|
+
this.head = (this.head + 1) % this.capacity;
|
|
12511
|
+
if (this.count < this.capacity)
|
|
12512
|
+
this.count++;
|
|
12513
|
+
}
|
|
12514
|
+
entries(statusFilter, metadataFilter) {
|
|
12515
|
+
const result = [];
|
|
12516
|
+
const start = this.count < this.capacity ? 0 : this.head;
|
|
12517
|
+
for (let i = 0;i < this.count; i++) {
|
|
12518
|
+
const idx = (start + i) % this.capacity;
|
|
12519
|
+
const entry = this.buffer[idx];
|
|
12520
|
+
if (!entry)
|
|
12521
|
+
continue;
|
|
12522
|
+
if (statusFilter && entry.status !== statusFilter)
|
|
12523
|
+
continue;
|
|
12524
|
+
if (metadataFilter && !matchMetadata(entry.metadata, metadataFilter))
|
|
12525
|
+
continue;
|
|
12526
|
+
result.push({ ...entry });
|
|
12527
|
+
}
|
|
12528
|
+
return result;
|
|
12529
|
+
}
|
|
12530
|
+
get length() {
|
|
12531
|
+
return this.count;
|
|
12532
|
+
}
|
|
12533
|
+
clear() {
|
|
12534
|
+
this.buffer = new Array(this.capacity);
|
|
12535
|
+
this.head = 0;
|
|
12536
|
+
this.count = 0;
|
|
12537
|
+
}
|
|
12538
|
+
}
|
|
12539
|
+
|
|
12540
|
+
class TaskRegistry {
|
|
12541
|
+
tasks = new Map;
|
|
12542
|
+
semaphore;
|
|
12543
|
+
nextId = 0;
|
|
12544
|
+
evictionTimers = new Map;
|
|
12545
|
+
accepting = true;
|
|
12546
|
+
options;
|
|
12547
|
+
client;
|
|
12548
|
+
taskHistory;
|
|
12549
|
+
constructor(options = {}) {
|
|
12550
|
+
this.options = {
|
|
12551
|
+
maxConcurrent: options.maxConcurrent ?? 50,
|
|
12552
|
+
taskTTL: options.taskTTL ?? 30000,
|
|
12553
|
+
maxOutputSize: options.maxOutputSize ?? 5 * 1024 * 1024,
|
|
12554
|
+
maxHistory: options.maxHistory ?? 200
|
|
12555
|
+
};
|
|
12556
|
+
this.client = options.client;
|
|
12557
|
+
this.semaphore = new Semaphore(this.options.maxConcurrent);
|
|
12558
|
+
this.taskHistory = new TaskHistory(this.options.maxHistory);
|
|
12559
|
+
}
|
|
12560
|
+
async spawn(command, options = {}) {
|
|
12561
|
+
if (!this.accepting) {
|
|
12562
|
+
throw new Error("Registry is shutting down");
|
|
12563
|
+
}
|
|
12564
|
+
const id = `task-${this.nextId++}`;
|
|
12565
|
+
const cwd = options.cwd ?? process.cwd();
|
|
12566
|
+
const task = createTask(id, command, cwd, options.timeout, options.sessionID, options.metadata);
|
|
12567
|
+
this.tasks.set(id, task);
|
|
12568
|
+
this.executeTask(task);
|
|
12569
|
+
return id;
|
|
12570
|
+
}
|
|
12571
|
+
async executeTask(task) {
|
|
12572
|
+
try {
|
|
12573
|
+
await this.semaphore.acquire();
|
|
12574
|
+
} catch (e) {
|
|
12575
|
+
task.status = "cancelled";
|
|
12576
|
+
task.error = String(e);
|
|
12577
|
+
task.resolveCompletion();
|
|
12578
|
+
return;
|
|
12579
|
+
}
|
|
12580
|
+
try {
|
|
12581
|
+
task.status = "running";
|
|
12582
|
+
task.startedAt = Date.now();
|
|
12583
|
+
const timeoutId = task.timeout ? setTimeout(() => task.abortController.abort(), task.timeout) : null;
|
|
12584
|
+
try {
|
|
12585
|
+
const proc = Bun.spawn(["sh", "-c", task.command], {
|
|
12586
|
+
cwd: task.cwd,
|
|
12587
|
+
stdout: Bun.file(task.stdout),
|
|
12588
|
+
stderr: Bun.file(task.stderr)
|
|
12589
|
+
});
|
|
12590
|
+
task.proc = proc;
|
|
12591
|
+
task.abortController.signal.addEventListener("abort", () => {
|
|
12592
|
+
proc.kill();
|
|
12593
|
+
});
|
|
12594
|
+
const exitCode = await proc.exited;
|
|
12595
|
+
task.exitCode = exitCode;
|
|
12596
|
+
if (task.abortController.signal.aborted) {
|
|
12597
|
+
task.status = "cancelled";
|
|
12598
|
+
} else {
|
|
12599
|
+
task.status = exitCode === 0 ? "completed" : "failed";
|
|
12600
|
+
}
|
|
12601
|
+
} catch (error45) {
|
|
12602
|
+
const errorMsg = `
|
|
12603
|
+
[Task execution error: ${String(error45)}]
|
|
12604
|
+
`;
|
|
12605
|
+
await appendFile(task.stderr, errorMsg);
|
|
12606
|
+
if (task.abortController.signal.aborted) {
|
|
12607
|
+
task.status = "cancelled";
|
|
12608
|
+
} else {
|
|
12609
|
+
task.status = "failed";
|
|
12610
|
+
task.error = String(error45);
|
|
12611
|
+
}
|
|
12612
|
+
} finally {
|
|
12613
|
+
if (timeoutId)
|
|
12614
|
+
clearTimeout(timeoutId);
|
|
12615
|
+
task.completedAt = Date.now();
|
|
12616
|
+
}
|
|
12617
|
+
} finally {
|
|
12618
|
+
this.semaphore.release();
|
|
12619
|
+
task.resolveCompletion();
|
|
12620
|
+
this.scheduleEviction(task.id);
|
|
12621
|
+
this.notifyCompletion(task);
|
|
12622
|
+
}
|
|
12623
|
+
}
|
|
12624
|
+
notifyCompletion(task) {
|
|
12625
|
+
if (!this.client || !task.sessionID)
|
|
12626
|
+
return;
|
|
12627
|
+
this.client.session.promptAsync({
|
|
12628
|
+
path: { id: task.sessionID },
|
|
12629
|
+
body: {
|
|
12630
|
+
parts: [
|
|
12631
|
+
{
|
|
12632
|
+
type: "text",
|
|
12633
|
+
text: `<task-notification>
|
|
12634
|
+
<task-id>${task.id}</task-id>
|
|
12635
|
+
<status>${task.status}</status>
|
|
12636
|
+
<exit-code>${task.exitCode ?? "N/A"}</exit-code>
|
|
12637
|
+
<stdout-path>${task.stdout}</stdout-path>
|
|
12638
|
+
<stderr-path>${task.stderr}</stderr-path>
|
|
12639
|
+
</task-notification>`
|
|
12640
|
+
}
|
|
12641
|
+
]
|
|
12642
|
+
}
|
|
12643
|
+
});
|
|
12644
|
+
}
|
|
12645
|
+
scheduleEviction(id) {
|
|
12646
|
+
const existing = this.evictionTimers.get(id);
|
|
12647
|
+
if (existing)
|
|
12648
|
+
clearTimeout(existing);
|
|
12649
|
+
const timer = setTimeout(() => {
|
|
12650
|
+
const task = this.tasks.get(id);
|
|
12651
|
+
if (task) {
|
|
12652
|
+
this.taskHistory.push({
|
|
12653
|
+
id: task.id,
|
|
12654
|
+
command: task.command.slice(0, 200),
|
|
12655
|
+
status: task.status,
|
|
12656
|
+
exitCode: task.exitCode,
|
|
12657
|
+
metadata: task.metadata,
|
|
12658
|
+
stdout: task.stdout,
|
|
12659
|
+
stderr: task.stderr,
|
|
12660
|
+
createdAt: task.createdAt,
|
|
12661
|
+
startedAt: task.startedAt,
|
|
12662
|
+
completedAt: task.completedAt
|
|
12663
|
+
});
|
|
12664
|
+
}
|
|
12665
|
+
this.tasks.delete(id);
|
|
12666
|
+
this.evictionTimers.delete(id);
|
|
12667
|
+
}, this.options.taskTTL);
|
|
12668
|
+
this.evictionTimers.set(id, timer);
|
|
12669
|
+
}
|
|
12670
|
+
get(id) {
|
|
12671
|
+
const task = this.tasks.get(id);
|
|
12672
|
+
if (task?.completedAt) {
|
|
12673
|
+
this.scheduleEviction(id);
|
|
12674
|
+
}
|
|
12675
|
+
return task;
|
|
12676
|
+
}
|
|
12677
|
+
async wait(id, timeoutMs = 30000) {
|
|
12678
|
+
const task = this.tasks.get(id);
|
|
12679
|
+
if (!task)
|
|
12680
|
+
throw new Error(`Task ${id} not found`);
|
|
12681
|
+
if (task.status !== "pending" && task.status !== "running") {
|
|
12682
|
+
return task;
|
|
12683
|
+
}
|
|
12684
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
12685
|
+
setTimeout(() => reject(new Error(`Task ${id} wait timed out`)), timeoutMs);
|
|
12686
|
+
});
|
|
12687
|
+
await Promise.race([task.completionPromise, timeoutPromise]);
|
|
12688
|
+
return task;
|
|
12689
|
+
}
|
|
12690
|
+
stop(id, signal) {
|
|
12691
|
+
const task = this.tasks.get(id);
|
|
12692
|
+
if (!task)
|
|
12693
|
+
return false;
|
|
12694
|
+
if (task.status === "running" && task.proc) {
|
|
12695
|
+
if (signal === "SIGKILL") {
|
|
12696
|
+
task.proc.kill(9);
|
|
12697
|
+
} else {
|
|
12698
|
+
task.proc.kill();
|
|
12699
|
+
}
|
|
12700
|
+
task.abortController.abort();
|
|
12701
|
+
return true;
|
|
12702
|
+
}
|
|
12703
|
+
if (task.status === "pending") {
|
|
12704
|
+
task.abortController.abort();
|
|
12705
|
+
task.status = "cancelled";
|
|
12706
|
+
task.resolveCompletion();
|
|
12707
|
+
return true;
|
|
12708
|
+
}
|
|
12709
|
+
return false;
|
|
12710
|
+
}
|
|
12711
|
+
list(statusFilter, metadataFilter) {
|
|
12712
|
+
let tasks = Array.from(this.tasks.values());
|
|
12713
|
+
if (statusFilter && statusFilter !== "all") {
|
|
12714
|
+
tasks = tasks.filter((t) => t.status === statusFilter);
|
|
12715
|
+
}
|
|
12716
|
+
if (metadataFilter) {
|
|
12717
|
+
tasks = tasks.filter((t) => matchMetadata(t.metadata, metadataFilter));
|
|
12718
|
+
}
|
|
12719
|
+
return tasks;
|
|
12720
|
+
}
|
|
12721
|
+
async shutdown(gracePeriodMs = 3000) {
|
|
12722
|
+
this.accepting = false;
|
|
12723
|
+
this.semaphore.drain();
|
|
12724
|
+
for (const timer of this.evictionTimers.values()) {
|
|
12725
|
+
clearTimeout(timer);
|
|
12726
|
+
}
|
|
12727
|
+
this.evictionTimers.clear();
|
|
12728
|
+
const runningTasks = [];
|
|
12729
|
+
for (const task of this.tasks.values()) {
|
|
12730
|
+
if (task.status === "running" && task.proc) {
|
|
12731
|
+
task.proc.kill();
|
|
12732
|
+
task.abortController.abort();
|
|
12733
|
+
runningTasks.push(task);
|
|
12734
|
+
}
|
|
12735
|
+
}
|
|
12736
|
+
if (runningTasks.length === 0)
|
|
12737
|
+
return;
|
|
12738
|
+
const graceDeadline = Date.now() + gracePeriodMs;
|
|
12739
|
+
for (const task of runningTasks) {
|
|
12740
|
+
const remaining = graceDeadline - Date.now();
|
|
12741
|
+
if (remaining > 0) {
|
|
12742
|
+
try {
|
|
12743
|
+
await Promise.race([
|
|
12744
|
+
task.completionPromise,
|
|
12745
|
+
new Promise((r) => setTimeout(r, remaining))
|
|
12746
|
+
]);
|
|
12747
|
+
} catch {}
|
|
12748
|
+
}
|
|
12749
|
+
if (task.status === "running" && task.proc) {
|
|
12750
|
+
task.proc.kill(9);
|
|
12751
|
+
}
|
|
12752
|
+
}
|
|
12753
|
+
}
|
|
12754
|
+
get size() {
|
|
12755
|
+
return this.tasks.size;
|
|
12756
|
+
}
|
|
12757
|
+
get activeCount() {
|
|
12758
|
+
return this.semaphore.activeCount;
|
|
12759
|
+
}
|
|
12760
|
+
get waitingCount() {
|
|
12761
|
+
return this.semaphore.waitingCount;
|
|
12762
|
+
}
|
|
12763
|
+
history(statusFilter, metadataFilter) {
|
|
12764
|
+
return this.taskHistory.entries(statusFilter, metadataFilter);
|
|
12765
|
+
}
|
|
12766
|
+
clear() {
|
|
12767
|
+
for (const timer of this.evictionTimers.values()) {
|
|
12768
|
+
clearTimeout(timer);
|
|
12769
|
+
}
|
|
12770
|
+
this.evictionTimers.clear();
|
|
12771
|
+
this.tasks.clear();
|
|
12772
|
+
this.taskHistory.clear();
|
|
12773
|
+
}
|
|
12774
|
+
}
|
|
12775
|
+
|
|
12332
12776
|
// index.ts
|
|
12333
|
-
var CustomToolPlugin = async () => {
|
|
12777
|
+
var CustomToolPlugin = async (input) => {
|
|
12778
|
+
const registry2 = new TaskRegistry({
|
|
12779
|
+
maxConcurrent: 50,
|
|
12780
|
+
client: input.client
|
|
12781
|
+
});
|
|
12334
12782
|
return {
|
|
12335
12783
|
tool: {
|
|
12336
|
-
|
|
12337
|
-
description:
|
|
12784
|
+
Task: tool({
|
|
12785
|
+
description: `Spawn background task.
|
|
12786
|
+
|
|
12787
|
+
Returns:
|
|
12788
|
+
- taskId: unique identifier
|
|
12789
|
+
- stdout/stderr: file paths (LIVE - readable during execution, not just after)
|
|
12790
|
+
|
|
12791
|
+
You will receive a notification when it completes.`,
|
|
12338
12792
|
args: {
|
|
12339
|
-
|
|
12793
|
+
command: tool.schema.string().describe("Shell command to execute"),
|
|
12794
|
+
cwd: tool.schema.string().optional().describe("Working directory"),
|
|
12795
|
+
timeout: tool.schema.number().optional().describe("Timeout in milliseconds"),
|
|
12796
|
+
metadata: tool.schema.record(tool.schema.string(), tool.schema.string()).optional().describe("Key-value metadata tags for filtering")
|
|
12797
|
+
},
|
|
12798
|
+
async execute(args, context) {
|
|
12799
|
+
const id = await registry2.spawn(args.command, {
|
|
12800
|
+
cwd: args.cwd,
|
|
12801
|
+
timeout: args.timeout,
|
|
12802
|
+
sessionID: context.sessionID,
|
|
12803
|
+
metadata: args.metadata
|
|
12804
|
+
});
|
|
12805
|
+
const task = registry2.get(id);
|
|
12806
|
+
return JSON.stringify({
|
|
12807
|
+
taskId: id,
|
|
12808
|
+
stdout: task?.stdout,
|
|
12809
|
+
stderr: task?.stderr,
|
|
12810
|
+
message: `Task ${id} spawned. You will receive a notification when complete.`
|
|
12811
|
+
});
|
|
12812
|
+
}
|
|
12813
|
+
}),
|
|
12814
|
+
TaskStop: tool({
|
|
12815
|
+
description: "Stop/cancel a running background task",
|
|
12816
|
+
args: {
|
|
12817
|
+
taskId: tool.schema.string().describe("Task ID to stop"),
|
|
12818
|
+
signal: tool.schema.enum(["SIGTERM", "SIGKILL"]).optional().describe("Signal to send (default SIGTERM)")
|
|
12340
12819
|
},
|
|
12341
12820
|
async execute(args) {
|
|
12342
|
-
|
|
12821
|
+
const stopped = registry2.stop(args.taskId, args.signal);
|
|
12822
|
+
return JSON.stringify({ success: stopped, taskId: args.taskId });
|
|
12823
|
+
}
|
|
12824
|
+
}),
|
|
12825
|
+
TaskList: tool({
|
|
12826
|
+
description: "List background tasks with status, output paths, and metadata. Supports filtering by status and metadata. Use includeHistory to see evicted tasks.",
|
|
12827
|
+
args: {
|
|
12828
|
+
status: tool.schema.enum([
|
|
12829
|
+
"all",
|
|
12830
|
+
"running",
|
|
12831
|
+
"completed",
|
|
12832
|
+
"failed",
|
|
12833
|
+
"pending",
|
|
12834
|
+
"cancelled"
|
|
12835
|
+
]).optional().describe("Filter by status"),
|
|
12836
|
+
metadata: tool.schema.record(tool.schema.string(), tool.schema.string()).optional().describe("Filter by metadata key-value pairs (all must match)"),
|
|
12837
|
+
includeHistory: tool.schema.boolean().optional().describe("Include evicted tasks from history buffer (default false)")
|
|
12838
|
+
},
|
|
12839
|
+
async execute(args) {
|
|
12840
|
+
const tasks = registry2.list(args.status, args.metadata);
|
|
12841
|
+
const result = {
|
|
12842
|
+
count: tasks.length,
|
|
12843
|
+
tasks: tasks.map((t) => ({
|
|
12844
|
+
id: t.id,
|
|
12845
|
+
status: t.status,
|
|
12846
|
+
command: t.command.slice(0, 100),
|
|
12847
|
+
stdout: t.stdout,
|
|
12848
|
+
stderr: t.stderr,
|
|
12849
|
+
metadata: t.metadata,
|
|
12850
|
+
createdAt: t.createdAt,
|
|
12851
|
+
duration: t.completedAt && t.startedAt ? t.completedAt - t.startedAt : undefined
|
|
12852
|
+
}))
|
|
12853
|
+
};
|
|
12854
|
+
if (args.includeHistory) {
|
|
12855
|
+
const history = registry2.history(args.status, args.metadata);
|
|
12856
|
+
result.history = history;
|
|
12857
|
+
result.historyCount = history.length;
|
|
12858
|
+
}
|
|
12859
|
+
return JSON.stringify(result);
|
|
12860
|
+
}
|
|
12861
|
+
}),
|
|
12862
|
+
AgentTask: tool({
|
|
12863
|
+
description: "Spawn a Claude agent to work on a subtask in the background. Available agents: Explore (codebase search), Plan (architecture design), general-purpose (complex multi-step tasks), claude-code-guide (Claude Code/SDK questions), web-search (research external information). You will receive a notification when complete.",
|
|
12864
|
+
args: {
|
|
12865
|
+
agent: tool.schema.enum([
|
|
12866
|
+
"Explore",
|
|
12867
|
+
"Plan",
|
|
12868
|
+
"general-purpose",
|
|
12869
|
+
"claude-code-guide",
|
|
12870
|
+
"web-search"
|
|
12871
|
+
]).describe("Agent type to spawn"),
|
|
12872
|
+
prompt: tool.schema.string().describe("Task prompt for the agent"),
|
|
12873
|
+
cwd: tool.schema.string().optional().describe("Working directory"),
|
|
12874
|
+
timeout: tool.schema.number().optional().describe("Timeout in milliseconds")
|
|
12875
|
+
},
|
|
12876
|
+
async execute(args, context) {
|
|
12877
|
+
const agentType = args.agent;
|
|
12878
|
+
const config2 = AGENT_CONFIGS[agentType];
|
|
12879
|
+
const agentDef = {
|
|
12880
|
+
[agentType]: {
|
|
12881
|
+
description: config2.description,
|
|
12882
|
+
prompt: config2.prompt
|
|
12883
|
+
}
|
|
12884
|
+
};
|
|
12885
|
+
const cmdParts = [
|
|
12886
|
+
"claude",
|
|
12887
|
+
"--verbose",
|
|
12888
|
+
"--strict-mcp-config",
|
|
12889
|
+
"--mcp-config",
|
|
12890
|
+
'{"mcpServers":{}}',
|
|
12891
|
+
"--allow-dangerously-skip-permissions",
|
|
12892
|
+
"--agents",
|
|
12893
|
+
JSON.stringify(agentDef),
|
|
12894
|
+
"--agent",
|
|
12895
|
+
agentType
|
|
12896
|
+
];
|
|
12897
|
+
if (config2.tools !== null) {
|
|
12898
|
+
cmdParts.push("--tools", config2.tools.join(","));
|
|
12899
|
+
}
|
|
12900
|
+
cmdParts.push("--output-format", "stream-json", "--print", "-p", args.prompt);
|
|
12901
|
+
const command = cmdParts.map((p) => p.includes(" ") || p.includes('"') ? `'${p}'` : p).join(" ");
|
|
12902
|
+
const id = await registry2.spawn(command, {
|
|
12903
|
+
cwd: args.cwd,
|
|
12904
|
+
timeout: args.timeout,
|
|
12905
|
+
sessionID: context.sessionID
|
|
12906
|
+
});
|
|
12907
|
+
return JSON.stringify({
|
|
12908
|
+
taskId: id,
|
|
12909
|
+
agent: agentType,
|
|
12910
|
+
message: `Agent ${agentType} spawned as ${id}. You will receive a notification when complete.`
|
|
12911
|
+
});
|
|
12343
12912
|
}
|
|
12344
12913
|
})
|
|
12345
12914
|
}
|
|
12346
12915
|
};
|
|
12347
12916
|
};
|
|
12917
|
+
var opencode_task_default = CustomToolPlugin;
|
|
12348
12918
|
export {
|
|
12919
|
+
opencode_task_default as default,
|
|
12349
12920
|
CustomToolPlugin
|
|
12350
12921
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-task",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": ["dist"],
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@biomejs/biome": "^1.9.0",
|
|
19
19
|
"@opencode-ai/plugin": "latest",
|
|
20
|
+
"@types/bun": "latest",
|
|
20
21
|
"typescript": "^5.0.0"
|
|
21
22
|
}
|
|
22
23
|
}
|