workplane 0.1.2 → 0.3.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/README.md +1 -0
- package/dist/cli.js +466 -3
- package/dist/cli.js.map +1 -1
- package/dist/node.js +201 -24
- package/dist/node.js.map +1 -1
- package/dist/schema.sql +13 -0
- package/dist/server.js +285 -75
- package/dist/server.js.map +1 -1
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# workplane
|
|
2
2
|
|
|
3
|
+
[](https://mindstaq.github.io/workplane/)
|
|
3
4
|
[](https://www.npmjs.com/package/workplane)
|
|
4
5
|
[](https://github.com/MindStaq/workplane)
|
|
5
6
|
[](https://github.com/MindStaq/workplane/blob/main/packages/workplane/LICENSE)
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,110 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// ../workplans/src/providers/anthropic.ts
|
|
13
|
+
var anthropic_exports = {};
|
|
14
|
+
__export(anthropic_exports, {
|
|
15
|
+
runAnthropicStep: () => runAnthropicStep
|
|
16
|
+
});
|
|
17
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
18
|
+
async function runAnthropicStep(opts) {
|
|
19
|
+
const client = new Anthropic({ apiKey: opts.apiKey ?? process.env.ANTHROPIC_API_KEY });
|
|
20
|
+
const message = await client.messages.create({
|
|
21
|
+
model: opts.model,
|
|
22
|
+
max_tokens: 4096,
|
|
23
|
+
messages: [{ role: "user", content: opts.prompt }]
|
|
24
|
+
});
|
|
25
|
+
const block = message.content[0];
|
|
26
|
+
return block.type === "text" ? block.text : "";
|
|
27
|
+
}
|
|
28
|
+
var init_anthropic = __esm({
|
|
29
|
+
"../workplans/src/providers/anthropic.ts"() {
|
|
30
|
+
"use strict";
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// ../workplans/src/providers/openai.ts
|
|
35
|
+
var openai_exports = {};
|
|
36
|
+
__export(openai_exports, {
|
|
37
|
+
runOpenAIStep: () => runOpenAIStep
|
|
38
|
+
});
|
|
39
|
+
import OpenAI from "openai";
|
|
40
|
+
async function runOpenAIStep(opts) {
|
|
41
|
+
const client = new OpenAI({ apiKey: opts.apiKey ?? process.env.OPENAI_API_KEY });
|
|
42
|
+
const completion = await client.chat.completions.create({
|
|
43
|
+
model: opts.model,
|
|
44
|
+
messages: [{ role: "user", content: opts.prompt }]
|
|
45
|
+
});
|
|
46
|
+
return completion.choices[0]?.message.content ?? "";
|
|
47
|
+
}
|
|
48
|
+
var init_openai = __esm({
|
|
49
|
+
"../workplans/src/providers/openai.ts"() {
|
|
50
|
+
"use strict";
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ../workplans/src/providers/ollama.ts
|
|
55
|
+
var ollama_exports = {};
|
|
56
|
+
__export(ollama_exports, {
|
|
57
|
+
runOllamaStep: () => runOllamaStep
|
|
58
|
+
});
|
|
59
|
+
import { Ollama } from "ollama";
|
|
60
|
+
async function runOllamaStep(opts) {
|
|
61
|
+
const host = opts.host ?? process.env.OLLAMA_HOST ?? "http://localhost:11434";
|
|
62
|
+
const client = new Ollama({ host });
|
|
63
|
+
const response = await client.generate({ model: opts.model, prompt: opts.prompt });
|
|
64
|
+
return response.response;
|
|
65
|
+
}
|
|
66
|
+
var init_ollama = __esm({
|
|
67
|
+
"../workplans/src/providers/ollama.ts"() {
|
|
68
|
+
"use strict";
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ../workplans/src/providers/shell.ts
|
|
73
|
+
var shell_exports = {};
|
|
74
|
+
__export(shell_exports, {
|
|
75
|
+
runShellStep: () => runShellStep
|
|
76
|
+
});
|
|
77
|
+
import { exec } from "child_process";
|
|
78
|
+
import { promisify } from "util";
|
|
79
|
+
async function runShellStep(opts) {
|
|
80
|
+
const { stdout } = await execAsync(opts.command, {
|
|
81
|
+
cwd: opts.cwd,
|
|
82
|
+
maxBuffer: 10 * 1024 * 1024
|
|
83
|
+
});
|
|
84
|
+
return stdout;
|
|
85
|
+
}
|
|
86
|
+
var execAsync;
|
|
87
|
+
var init_shell = __esm({
|
|
88
|
+
"../workplans/src/providers/shell.ts"() {
|
|
89
|
+
"use strict";
|
|
90
|
+
execAsync = promisify(exec);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ../workplans/src/providers/file.ts
|
|
95
|
+
var file_exports = {};
|
|
96
|
+
__export(file_exports, {
|
|
97
|
+
runFileStep: () => runFileStep
|
|
98
|
+
});
|
|
99
|
+
import { readFile } from "fs/promises";
|
|
100
|
+
async function runFileStep(opts) {
|
|
101
|
+
return readFile(opts.path, opts.encoding ?? "utf8");
|
|
102
|
+
}
|
|
103
|
+
var init_file = __esm({
|
|
104
|
+
"../workplans/src/providers/file.ts"() {
|
|
105
|
+
"use strict";
|
|
106
|
+
}
|
|
107
|
+
});
|
|
2
108
|
|
|
3
109
|
// ../core/src/env.ts
|
|
4
110
|
import { config as dotenvConfig } from "dotenv";
|
|
@@ -64,6 +170,292 @@ async function workplaneFetch(serverUrl2, path, options = {}) {
|
|
|
64
170
|
return await response.json();
|
|
65
171
|
}
|
|
66
172
|
|
|
173
|
+
// ../agent-skills/src/skills/code-review.ts
|
|
174
|
+
function codeReviewPlan(opts = {}) {
|
|
175
|
+
const { repoPath = ".", model = "claude-haiku-4-5-20251001", branch } = opts;
|
|
176
|
+
const diffCmd = branch ? `git diff ${branch}..HEAD` : "git diff HEAD~1";
|
|
177
|
+
return {
|
|
178
|
+
id: "code-review",
|
|
179
|
+
name: "Code Review",
|
|
180
|
+
steps: [
|
|
181
|
+
{
|
|
182
|
+
id: "diff",
|
|
183
|
+
name: "Git Diff",
|
|
184
|
+
adapter: "shell",
|
|
185
|
+
provider: "shell",
|
|
186
|
+
payload: { command: diffCmd, cwd: repoPath },
|
|
187
|
+
output: { dest: "next" }
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "summarize",
|
|
191
|
+
name: "Summarize",
|
|
192
|
+
adapter: "ollama",
|
|
193
|
+
provider: "ollama",
|
|
194
|
+
model: "llama3",
|
|
195
|
+
payload: { prompt: "Summarize these code changes concisely:\n{{prevOutput}}" },
|
|
196
|
+
output: { dest: "next" }
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: "critique",
|
|
200
|
+
name: "Critique",
|
|
201
|
+
adapter: "anthropic",
|
|
202
|
+
provider: "anthropic",
|
|
203
|
+
model,
|
|
204
|
+
payload: { prompt: "Review this summary for correctness, security issues, and code quality:\n{{prevOutput}}" }
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ../agent-skills/src/skills/summarize-file.ts
|
|
211
|
+
function summarizeFilePlan(opts) {
|
|
212
|
+
const { filePath, provider = "ollama", model = "llama3" } = opts;
|
|
213
|
+
return {
|
|
214
|
+
id: "summarize-file",
|
|
215
|
+
name: "Summarize File",
|
|
216
|
+
steps: [
|
|
217
|
+
{
|
|
218
|
+
id: "read",
|
|
219
|
+
name: "Read File",
|
|
220
|
+
adapter: "file",
|
|
221
|
+
provider: "file",
|
|
222
|
+
payload: { path: filePath },
|
|
223
|
+
output: { dest: "next" }
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: "summarize",
|
|
227
|
+
name: "Summarize",
|
|
228
|
+
adapter: provider,
|
|
229
|
+
provider,
|
|
230
|
+
model,
|
|
231
|
+
payload: { prompt: "Summarize the following file content concisely:\n{{prevOutput}}" }
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ../agent-skills/src/skills/index.ts
|
|
238
|
+
var SkillRegistry = class {
|
|
239
|
+
skills = /* @__PURE__ */ new Map();
|
|
240
|
+
register(entry) {
|
|
241
|
+
this.skills.set(entry.name, entry);
|
|
242
|
+
}
|
|
243
|
+
get(name) {
|
|
244
|
+
return this.skills.get(name);
|
|
245
|
+
}
|
|
246
|
+
list() {
|
|
247
|
+
return Array.from(this.skills.values());
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
function createDefaultRegistry() {
|
|
251
|
+
const registry = new SkillRegistry();
|
|
252
|
+
registry.register({
|
|
253
|
+
name: "code-review",
|
|
254
|
+
description: "Git diff \u2192 local summarize (ollama) \u2192 frontier critique (anthropic)",
|
|
255
|
+
buildPlan: (opts) => codeReviewPlan({
|
|
256
|
+
repoPath: typeof opts.repo === "string" ? opts.repo : void 0,
|
|
257
|
+
model: typeof opts.model === "string" ? opts.model : void 0,
|
|
258
|
+
branch: typeof opts.branch === "string" ? opts.branch : void 0
|
|
259
|
+
})
|
|
260
|
+
});
|
|
261
|
+
registry.register({
|
|
262
|
+
name: "summarize-file",
|
|
263
|
+
description: "Read file \u2192 summarize with configurable provider (default: ollama)",
|
|
264
|
+
buildPlan: (opts) => {
|
|
265
|
+
const filePath = typeof opts.file === "string" ? opts.file : "";
|
|
266
|
+
if (!filePath) throw new Error("--file is required for summarize-file");
|
|
267
|
+
return summarizeFilePlan({
|
|
268
|
+
filePath,
|
|
269
|
+
provider: typeof opts.provider === "string" ? opts.provider : void 0,
|
|
270
|
+
model: typeof opts.model === "string" ? opts.model : void 0
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
return registry;
|
|
275
|
+
}
|
|
276
|
+
function listSkills(registry) {
|
|
277
|
+
for (const skill of registry.list()) {
|
|
278
|
+
process.stdout.write(` ${skill.name.padEnd(20)} ${skill.description}
|
|
279
|
+
`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ../workplans/src/template.ts
|
|
284
|
+
function applyTemplate(payload, prevOutput) {
|
|
285
|
+
return Object.fromEntries(
|
|
286
|
+
Object.entries(payload).map(([k, v]) => [
|
|
287
|
+
k,
|
|
288
|
+
typeof v === "string" ? v.replaceAll("{{prevOutput}}", prevOutput) : v
|
|
289
|
+
])
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ../workplans/src/runner.ts
|
|
294
|
+
var INLINE_PROVIDERS = /* @__PURE__ */ new Set(["anthropic", "openai", "ollama", "shell", "file"]);
|
|
295
|
+
async function runInlineStep(step, prompt) {
|
|
296
|
+
const provider = step.provider;
|
|
297
|
+
if (provider === "anthropic") {
|
|
298
|
+
const { runAnthropicStep: runAnthropicStep2 } = await Promise.resolve().then(() => (init_anthropic(), anthropic_exports));
|
|
299
|
+
return runAnthropicStep2({ model: step.model ?? "claude-haiku-4-5-20251001", prompt });
|
|
300
|
+
}
|
|
301
|
+
if (provider === "openai") {
|
|
302
|
+
const { runOpenAIStep: runOpenAIStep2 } = await Promise.resolve().then(() => (init_openai(), openai_exports));
|
|
303
|
+
return runOpenAIStep2({ model: step.model ?? "gpt-4o-mini", prompt });
|
|
304
|
+
}
|
|
305
|
+
if (provider === "ollama") {
|
|
306
|
+
const { runOllamaStep: runOllamaStep2 } = await Promise.resolve().then(() => (init_ollama(), ollama_exports));
|
|
307
|
+
return runOllamaStep2({ model: step.model ?? "llama3", prompt });
|
|
308
|
+
}
|
|
309
|
+
if (provider === "shell") {
|
|
310
|
+
const { runShellStep: runShellStep2 } = await Promise.resolve().then(() => (init_shell(), shell_exports));
|
|
311
|
+
return runShellStep2({
|
|
312
|
+
command: String(step.payload.command ?? ""),
|
|
313
|
+
cwd: typeof step.payload.cwd === "string" ? step.payload.cwd : void 0
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
if (provider === "file") {
|
|
317
|
+
const { runFileStep: runFileStep2 } = await Promise.resolve().then(() => (init_file(), file_exports));
|
|
318
|
+
return runFileStep2({ path: String(step.payload.path ?? "") });
|
|
319
|
+
}
|
|
320
|
+
throw new Error(`unknown inline provider: ${provider}`);
|
|
321
|
+
}
|
|
322
|
+
var SequentialWorkplanRunner = class {
|
|
323
|
+
async run(plan, ctx) {
|
|
324
|
+
const results = [];
|
|
325
|
+
let prevOutput = "";
|
|
326
|
+
let planSucceeded = true;
|
|
327
|
+
for (const step of plan.steps) {
|
|
328
|
+
const payload = prevOutput ? applyTemplate(step.payload, prevOutput) : step.payload;
|
|
329
|
+
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
330
|
+
const start = Date.now();
|
|
331
|
+
let output = "";
|
|
332
|
+
let exitCode = 0;
|
|
333
|
+
try {
|
|
334
|
+
if (step.provider && INLINE_PROVIDERS.has(step.provider)) {
|
|
335
|
+
output = await runInlineStep({ ...step, payload }, prompt);
|
|
336
|
+
} else {
|
|
337
|
+
const node = await ctx.resolveNode(step.requires ?? [], step.provider);
|
|
338
|
+
const result = await node.execute({ ...step, payload });
|
|
339
|
+
output = result.output;
|
|
340
|
+
exitCode = result.exitCode;
|
|
341
|
+
}
|
|
342
|
+
} catch (err) {
|
|
343
|
+
exitCode = 1;
|
|
344
|
+
output = err instanceof Error ? err.message : String(err);
|
|
345
|
+
planSucceeded = false;
|
|
346
|
+
const stepResult2 = {
|
|
347
|
+
stepId: step.id,
|
|
348
|
+
output,
|
|
349
|
+
exitCode,
|
|
350
|
+
durationMs: Date.now() - start
|
|
351
|
+
};
|
|
352
|
+
results.push(stepResult2);
|
|
353
|
+
ctx.emitResult(step.id, step.name, output);
|
|
354
|
+
if (!step.continueOnError) {
|
|
355
|
+
return {
|
|
356
|
+
planId: plan.id,
|
|
357
|
+
status: "step_failed",
|
|
358
|
+
steps: results,
|
|
359
|
+
succeeded: false
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
prevOutput = "";
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const dest = step.output?.dest ?? "stdout";
|
|
366
|
+
const stepResult = {
|
|
367
|
+
stepId: step.id,
|
|
368
|
+
output,
|
|
369
|
+
exitCode,
|
|
370
|
+
durationMs: Date.now() - start
|
|
371
|
+
};
|
|
372
|
+
results.push(stepResult);
|
|
373
|
+
if (dest === "stdout" || dest === "artifact") {
|
|
374
|
+
ctx.emitResult(step.id, step.name, output);
|
|
375
|
+
prevOutput = "";
|
|
376
|
+
} else if (dest === "next") {
|
|
377
|
+
prevOutput = output;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
planId: plan.id,
|
|
382
|
+
status: planSucceeded ? "completed" : "failed",
|
|
383
|
+
steps: results,
|
|
384
|
+
succeeded: planSucceeded
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// ../workplans/src/sinks/stdout.ts
|
|
390
|
+
function printStepResult(stepName, output) {
|
|
391
|
+
process.stdout.write(`=== step: ${stepName} ===
|
|
392
|
+
${output}
|
|
393
|
+
|
|
394
|
+
`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// ../workplans/src/context.ts
|
|
398
|
+
var WorkplaneNodeHandle = class {
|
|
399
|
+
constructor(serverUrl2, nodeToken) {
|
|
400
|
+
this.serverUrl = serverUrl2;
|
|
401
|
+
this.nodeToken = nodeToken;
|
|
402
|
+
}
|
|
403
|
+
serverUrl;
|
|
404
|
+
nodeToken;
|
|
405
|
+
async execute(step) {
|
|
406
|
+
const start = Date.now();
|
|
407
|
+
const taskRes = await fetch(`${this.serverUrl}/tasks`, {
|
|
408
|
+
method: "POST",
|
|
409
|
+
headers: {
|
|
410
|
+
"content-type": "application/json",
|
|
411
|
+
authorization: `Bearer ${this.nodeToken}`
|
|
412
|
+
},
|
|
413
|
+
body: JSON.stringify({
|
|
414
|
+
kind: "workplan.step",
|
|
415
|
+
adapter: step.adapter,
|
|
416
|
+
payload: step.payload,
|
|
417
|
+
requires: step.requires ?? []
|
|
418
|
+
})
|
|
419
|
+
});
|
|
420
|
+
if (!taskRes.ok) {
|
|
421
|
+
throw new Error(`failed to submit step ${step.id}: ${taskRes.status}`);
|
|
422
|
+
}
|
|
423
|
+
const task = await taskRes.json();
|
|
424
|
+
const output = await this.pollUntilDone(task.id);
|
|
425
|
+
return { stepId: step.id, output, exitCode: 0, durationMs: Date.now() - start };
|
|
426
|
+
}
|
|
427
|
+
async pollUntilDone(taskId) {
|
|
428
|
+
const terminalStatuses = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled"]);
|
|
429
|
+
for (; ; ) {
|
|
430
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
431
|
+
const res = await fetch(`${this.serverUrl}/tasks/${taskId}`, {
|
|
432
|
+
headers: { authorization: `Bearer ${this.nodeToken}` }
|
|
433
|
+
});
|
|
434
|
+
const task = await res.json();
|
|
435
|
+
if (terminalStatuses.has(task.status)) {
|
|
436
|
+
if (task.status !== "succeeded") {
|
|
437
|
+
throw new Error(`step task ${taskId} ended with status: ${task.status}`);
|
|
438
|
+
}
|
|
439
|
+
return typeof task.payload?.output === "string" ? task.payload.output : "";
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
var LocalWorkplanContext = class {
|
|
445
|
+
serverUrl;
|
|
446
|
+
nodeToken;
|
|
447
|
+
constructor(opts = {}) {
|
|
448
|
+
this.serverUrl = opts.serverUrl ?? process.env.WORKPLANE_SERVER_URL ?? "http://localhost:8787";
|
|
449
|
+
this.nodeToken = opts.nodeToken ?? process.env.WORKPLANE_NODE_TOKEN ?? "";
|
|
450
|
+
}
|
|
451
|
+
async resolveNode(_requires, _provider) {
|
|
452
|
+
return new WorkplaneNodeHandle(this.serverUrl, this.nodeToken);
|
|
453
|
+
}
|
|
454
|
+
emitResult(_stepId, stepName, output) {
|
|
455
|
+
printStepResult(stepName, output);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
67
459
|
// ../cli/src/index.ts
|
|
68
460
|
loadLocalEnv();
|
|
69
461
|
var serverUrl = process.env.WORKPLANE_SERVER_URL ?? "http://localhost:8787";
|
|
@@ -111,13 +503,18 @@ function usage() {
|
|
|
111
503
|
" task submit shell --command <cmd> [--repo <url>] [--branch <b>] [--cwd <path>]",
|
|
112
504
|
" task submit aider --prompt <text> --repo <url> [--model <m>] [--test-command <cmd>]",
|
|
113
505
|
" task submit inference --model <m> --prompt <text> [--requires ollama]",
|
|
114
|
-
" task submit harness --harness <codex|claude-code> --repo <url> --prompt <text>",
|
|
506
|
+
" task submit harness --harness <codex|claude-code> --repo <url> --prompt <text> [--interactive]",
|
|
115
507
|
" tasks [--status <status>]",
|
|
116
508
|
" task show|logs|retry|cancel <taskId>",
|
|
117
509
|
" runs [--task-id <id>] [--status <status>]",
|
|
118
510
|
" run show <runId>",
|
|
511
|
+
" run input <runId> --stdin <text>",
|
|
512
|
+
" run input <runId> --signal <SIGTERM|SIGKILL|SIGINT>",
|
|
513
|
+
" run input <runId> --resize <COLSxROWS>",
|
|
119
514
|
" logs <runId|taskId>",
|
|
120
|
-
" artifacts <runId>"
|
|
515
|
+
" artifacts <runId>",
|
|
516
|
+
" skill list",
|
|
517
|
+
" skill run <name> [--key value ...]"
|
|
121
518
|
].join("\n")
|
|
122
519
|
);
|
|
123
520
|
}
|
|
@@ -201,6 +598,7 @@ async function submitHarness(args) {
|
|
|
201
598
|
const model = valueArg(args, "--model");
|
|
202
599
|
const branch = valueArg(args, "--branch");
|
|
203
600
|
const testCommand = valueArg(args, "--test-command");
|
|
601
|
+
const interactive = args.includes("--interactive") || void 0;
|
|
204
602
|
const requires = parseCsv(valueArg(args, "--requires") ?? `${harness},git`);
|
|
205
603
|
const task = await httpJson("/tasks", {
|
|
206
604
|
method: "POST",
|
|
@@ -209,7 +607,7 @@ async function submitHarness(args) {
|
|
|
209
607
|
kind: "agent.run",
|
|
210
608
|
adapter: harness,
|
|
211
609
|
requires,
|
|
212
|
-
payload: { prompt, repo, model, branch, testCommand }
|
|
610
|
+
payload: { prompt, repo, model, branch, testCommand, interactive }
|
|
213
611
|
}
|
|
214
612
|
});
|
|
215
613
|
printJson(task);
|
|
@@ -304,6 +702,36 @@ async function main() {
|
|
|
304
702
|
printJson(runs);
|
|
305
703
|
return;
|
|
306
704
|
}
|
|
705
|
+
if (command === "run" && subcommand === "input") {
|
|
706
|
+
const runId = rest[0];
|
|
707
|
+
if (!runId) {
|
|
708
|
+
throw new Error("run id is required");
|
|
709
|
+
}
|
|
710
|
+
const inputArgs = rest.slice(1);
|
|
711
|
+
const stdinData = valueArg(inputArgs, "--stdin");
|
|
712
|
+
const signal = valueArg(inputArgs, "--signal");
|
|
713
|
+
const resize = valueArg(inputArgs, "--resize");
|
|
714
|
+
let body;
|
|
715
|
+
if (stdinData !== void 0) {
|
|
716
|
+
body = { kind: "stdin", payload: { data: stdinData } };
|
|
717
|
+
} else if (signal !== void 0) {
|
|
718
|
+
if (signal !== "SIGTERM" && signal !== "SIGKILL" && signal !== "SIGINT") {
|
|
719
|
+
throw new Error("--signal must be SIGTERM, SIGKILL, or SIGINT");
|
|
720
|
+
}
|
|
721
|
+
body = { kind: "signal", payload: { signal } };
|
|
722
|
+
} else if (resize !== void 0) {
|
|
723
|
+
const match = /^(\d+)x(\d+)$/.exec(resize);
|
|
724
|
+
if (!match) {
|
|
725
|
+
throw new Error("--resize must be in COLSxROWS format, e.g. 220x50");
|
|
726
|
+
}
|
|
727
|
+
body = { kind: "resize", payload: { cols: Number(match[1]), rows: Number(match[2]) } };
|
|
728
|
+
} else {
|
|
729
|
+
throw new Error("one of --stdin, --signal, or --resize is required");
|
|
730
|
+
}
|
|
731
|
+
const result = await httpJson(`/runs/${runId}/input`, { method: "POST", operator: true, body });
|
|
732
|
+
printJson(result);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
307
735
|
if (command === "run" && subcommand === "show") {
|
|
308
736
|
const runId = rest[0];
|
|
309
737
|
if (!runId) {
|
|
@@ -330,6 +758,41 @@ async function main() {
|
|
|
330
758
|
printJson(artifacts);
|
|
331
759
|
return;
|
|
332
760
|
}
|
|
761
|
+
if (command === "skill" && subcommand === "list") {
|
|
762
|
+
const registry = createDefaultRegistry();
|
|
763
|
+
process.stdout.write("Available skills:\n");
|
|
764
|
+
listSkills(registry);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (command === "skill" && subcommand === "run") {
|
|
768
|
+
const skillName = rest[0];
|
|
769
|
+
if (!skillName) {
|
|
770
|
+
throw new Error("skill name is required");
|
|
771
|
+
}
|
|
772
|
+
const registry = createDefaultRegistry();
|
|
773
|
+
const skill = registry.get(skillName);
|
|
774
|
+
if (!skill) {
|
|
775
|
+
throw new Error(`unknown skill: ${skillName}. Run 'workplane skill list' to see available skills.`);
|
|
776
|
+
}
|
|
777
|
+
const optArgs = rest.slice(1);
|
|
778
|
+
const options = {};
|
|
779
|
+
for (let i = 0; i < optArgs.length - 1; i++) {
|
|
780
|
+
if (optArgs[i].startsWith("--")) {
|
|
781
|
+
options[optArgs[i].slice(2)] = optArgs[i + 1];
|
|
782
|
+
i++;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
const plan = skill.buildPlan(options);
|
|
786
|
+
const runner = new SequentialWorkplanRunner();
|
|
787
|
+
const ctx = new LocalWorkplanContext();
|
|
788
|
+
const result = await runner.run(plan, ctx);
|
|
789
|
+
if (!result.succeeded) {
|
|
790
|
+
process.stderr.write(`skill '${skillName}' failed at step '${result.steps.at(-1)?.stepId ?? "unknown"}'
|
|
791
|
+
`);
|
|
792
|
+
process.exit(1);
|
|
793
|
+
}
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
333
796
|
usage();
|
|
334
797
|
}
|
|
335
798
|
main().catch((error) => {
|