@flumecode/runner 0.3.1 → 0.3.2
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/cli.js
CHANGED
|
@@ -40,6 +40,7 @@ function readVersion() {
|
|
|
40
40
|
var RUNNER_VERSION = readVersion();
|
|
41
41
|
var RUNNER_VERSION_HEADER = "x-flumecode-runner-version";
|
|
42
42
|
var RUNNER_MIN_VERSION_HEADER = "x-flumecode-min-runner-version";
|
|
43
|
+
var RUNNER_LATEST_VERSION_HEADER = "x-flumecode-latest-runner-version";
|
|
43
44
|
function compareVersions(a, b) {
|
|
44
45
|
const parse = (v) => (v.split("-")[0] ?? v).split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
45
46
|
const pa = parse(a);
|
|
@@ -55,16 +56,29 @@ function compareVersions(a, b) {
|
|
|
55
56
|
// src/api.ts
|
|
56
57
|
var OUTDATED_WARN_INTERVAL_MS = 60 * 6e4;
|
|
57
58
|
var lastOutdatedWarnAt = 0;
|
|
59
|
+
var UPDATE_NUDGE_INTERVAL_MS = 60 * 6e4;
|
|
60
|
+
var lastUpdateNudgeAt = 0;
|
|
58
61
|
function noteServerVersion(res) {
|
|
59
62
|
const min = res.headers.get(RUNNER_MIN_VERSION_HEADER)?.trim();
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
`\u26A0\uFE0F This runner (v${RUNNER_VERSION}) is outdated \u2014 the server expects v${min} or newer.
|
|
63
|
+
if (min && RUNNER_VERSION !== "unknown" && compareVersions(RUNNER_VERSION, min) < 0) {
|
|
64
|
+
const now2 = Date.now();
|
|
65
|
+
if (now2 - lastOutdatedWarnAt >= OUTDATED_WARN_INTERVAL_MS) {
|
|
66
|
+
lastOutdatedWarnAt = now2;
|
|
67
|
+
console.warn(
|
|
68
|
+
`\u26A0\uFE0F This runner (v${RUNNER_VERSION}) is outdated \u2014 the server expects v${min} or newer.
|
|
67
69
|
Update with: npm install -g @flumecode/runner@latest`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const latest = res.headers.get(RUNNER_LATEST_VERSION_HEADER)?.trim();
|
|
75
|
+
if (!latest || RUNNER_VERSION === "unknown") return;
|
|
76
|
+
if (compareVersions(RUNNER_VERSION, latest) >= 0) return;
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
if (now - lastUpdateNudgeAt < UPDATE_NUDGE_INTERVAL_MS) return;
|
|
79
|
+
lastUpdateNudgeAt = now;
|
|
80
|
+
console.log(
|
|
81
|
+
`\u2139\uFE0F A newer runner is available (v${latest}; you have v${RUNNER_VERSION}). Update with: npm install -g @flumecode/runner@latest`
|
|
68
82
|
);
|
|
69
83
|
}
|
|
70
84
|
async function claimJob(config) {
|
|
@@ -265,19 +279,30 @@ function renderPlan(plan) {
|
|
|
265
279
|
lines.push(PLAN_MARKER);
|
|
266
280
|
return lines.join("\n");
|
|
267
281
|
}
|
|
282
|
+
var submitPlanInputSchema = {
|
|
283
|
+
plans: z2.array(z2.object(planInputSchema)).min(1).refine(
|
|
284
|
+
(arr) => {
|
|
285
|
+
const titles = arr.map((p) => p.title.trim()).filter((t) => t.length > 0);
|
|
286
|
+
return new Set(titles).size === titles.length;
|
|
287
|
+
},
|
|
288
|
+
{ message: "Each plan must have a distinct non-empty title" }
|
|
289
|
+
)
|
|
290
|
+
};
|
|
291
|
+
var submitPlanSchema = z2.object(submitPlanInputSchema);
|
|
268
292
|
function createPlanTooling() {
|
|
269
|
-
let
|
|
293
|
+
let renderedPlans = null;
|
|
270
294
|
const submitPlan = tool2(
|
|
271
295
|
SUBMIT_PLAN,
|
|
272
|
-
"Submit
|
|
273
|
-
|
|
296
|
+
"Submit ALL your plans in a single call \u2014 one entry per plan; each becomes its own independently-acceptable Accept-as-plan draft. Do NOT call submit_plan more than once. acceptanceCriteria is required in each plan and must contain at least 2 observable, verifiable conditions. The 'title' field names each specific plan \u2014 make it concise and distinct from the request title and from sibling plan titles.",
|
|
297
|
+
submitPlanInputSchema,
|
|
274
298
|
async (args) => {
|
|
275
|
-
|
|
299
|
+
const parsed = submitPlanSchema.parse(args);
|
|
300
|
+
renderedPlans = parsed.plans.map(renderPlan);
|
|
276
301
|
return {
|
|
277
302
|
content: [
|
|
278
303
|
{
|
|
279
304
|
type: "text",
|
|
280
|
-
text: "Plan submitted. The runner will render and post
|
|
305
|
+
text: "Plan(s) submitted. The runner will render and post them as your comment(s). End your turn now."
|
|
281
306
|
}
|
|
282
307
|
]
|
|
283
308
|
};
|
|
@@ -287,7 +312,7 @@ function createPlanTooling() {
|
|
|
287
312
|
name: SERVER_NAME2,
|
|
288
313
|
tools: [submitPlan]
|
|
289
314
|
});
|
|
290
|
-
return { mcpServer,
|
|
315
|
+
return { mcpServer, getPlans: () => renderedPlans };
|
|
291
316
|
}
|
|
292
317
|
|
|
293
318
|
// src/executor.ts
|
|
@@ -295,7 +320,7 @@ var FLUME_PLUGIN_DIR = fileURLToPath2(new URL("../skills-plugin", import.meta.ur
|
|
|
295
320
|
async function runClaudeCode(opts) {
|
|
296
321
|
let finalText = "";
|
|
297
322
|
const { mcpServer, collected } = createWidgetTooling();
|
|
298
|
-
const { mcpServer: planServer,
|
|
323
|
+
const { mcpServer: planServer, getPlans } = createPlanTooling();
|
|
299
324
|
for await (const message of query({
|
|
300
325
|
prompt: opts.prompt,
|
|
301
326
|
options: {
|
|
@@ -333,7 +358,7 @@ async function runClaudeCode(opts) {
|
|
|
333
358
|
}
|
|
334
359
|
}
|
|
335
360
|
process.stdout.write("\n");
|
|
336
|
-
return { text: finalText, widgets: collected,
|
|
361
|
+
return { text: finalText, widgets: collected, plans: getPlans() };
|
|
337
362
|
}
|
|
338
363
|
|
|
339
364
|
// src/health.ts
|
|
@@ -857,14 +882,14 @@ async function processChatJob(ctx, dir) {
|
|
|
857
882
|
});
|
|
858
883
|
const summary = result.text.trim();
|
|
859
884
|
let reply = summary || "(the agent produced no summary)";
|
|
860
|
-
if (result.plan) {
|
|
861
|
-
reply = result.plan;
|
|
862
|
-
}
|
|
863
885
|
if (installResult?.status === "failed") {
|
|
864
886
|
reply += `
|
|
865
887
|
|
|
866
888
|
> \u26A0\uFE0F Dependencies failed to install (\`${installResult.manager}\`); tests may not have run.`;
|
|
867
889
|
}
|
|
890
|
+
if (result.plans?.length) {
|
|
891
|
+
return { text: result.text.trim(), widgets: [], plans: result.plans };
|
|
892
|
+
}
|
|
868
893
|
if (result.widgets.length > 0) {
|
|
869
894
|
console.log(` \u2026job ${ctx.jobId} posted ${result.widgets.length} widget(s); awaiting reply`);
|
|
870
895
|
return { text: reply, widgets: result.widgets };
|
|
@@ -937,7 +962,7 @@ async function processReviseJob(ctx, dir, resumed) {
|
|
|
937
962
|
});
|
|
938
963
|
const summary = result.text.trim();
|
|
939
964
|
let reply = summary || "(the agent produced no reply)";
|
|
940
|
-
if (result.
|
|
965
|
+
if (result.plans?.length) reply = result.plans[0] ?? reply;
|
|
941
966
|
if (installResult.status === "failed") {
|
|
942
967
|
reply += `
|
|
943
968
|
|
|
@@ -966,7 +991,12 @@ async function processReviseJob(ctx, dir, resumed) {
|
|
|
966
991
|
if (outcome.kind !== "none") {
|
|
967
992
|
reply += outcomeBanner(outcome, { branch: ctx.repo.checkoutBranch, documented });
|
|
968
993
|
}
|
|
969
|
-
return {
|
|
994
|
+
return {
|
|
995
|
+
text: reply,
|
|
996
|
+
widgets: [],
|
|
997
|
+
...outcome.kind === "pr" ? { pr: outcome.pr } : {},
|
|
998
|
+
...result.plans?.length ? { plans: result.plans } : {}
|
|
999
|
+
};
|
|
970
1000
|
}
|
|
971
1001
|
async function heartbeat(config) {
|
|
972
1002
|
const health = await checkClaudeCode();
|
|
@@ -1005,8 +1035,14 @@ async function pollLoop(config) {
|
|
|
1005
1035
|
continue;
|
|
1006
1036
|
}
|
|
1007
1037
|
try {
|
|
1008
|
-
const { text, widgets, pr } = await processJob(ctx);
|
|
1009
|
-
await reportJob(config, ctx.jobId, {
|
|
1038
|
+
const { text, widgets, pr, plans } = await processJob(ctx);
|
|
1039
|
+
await reportJob(config, ctx.jobId, {
|
|
1040
|
+
status: "done",
|
|
1041
|
+
text,
|
|
1042
|
+
widgets,
|
|
1043
|
+
pr,
|
|
1044
|
+
...plans?.length ? { plans } : {}
|
|
1045
|
+
});
|
|
1010
1046
|
console.log(`\u2713 Job ${ctx.jobId} done`);
|
|
1011
1047
|
} catch (err) {
|
|
1012
1048
|
const message = errorMessage2(err);
|
package/package.json
CHANGED
|
@@ -84,13 +84,13 @@ plan without re-deriving it.
|
|
|
84
84
|
|
|
85
85
|
### Multiple plans per request
|
|
86
86
|
|
|
87
|
-
A single request can yield **several** plans — one thread can be accepted into
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
commenting to refine it; treat a later turn as a fresh **Plan** phase and call
|
|
93
|
-
`submit_plan` again with the revised fields.
|
|
87
|
+
A single request can yield **several** plans — one thread can be accepted into many. If the
|
|
88
|
+
work naturally splits into independent pieces, or the user asks for more than one plan, make
|
|
89
|
+
**ONE `submit_plan` call** with all of them in the `plans[]` array (one entry per plan, each
|
|
90
|
+
with a distinct title). Do **not** call `submit_plan` more than once. Each entry becomes its
|
|
91
|
+
own independently-acceptable "Accept as plan" draft. After a plan is accepted the user may
|
|
92
|
+
keep commenting to refine it; treat a later turn as a fresh **Plan** phase and call
|
|
93
|
+
`submit_plan` again with a `plans[]` array containing the revised fields.
|
|
94
94
|
|
|
95
95
|
## Always
|
|
96
96
|
|
|
@@ -39,11 +39,11 @@ actual code. Pick exactly one:
|
|
|
39
39
|
Explain why in plain prose, offer an alternative if you have one, and end your
|
|
40
40
|
turn. Make no code changes.
|
|
41
41
|
- **Re-plan** — the request meaningfully changes scope or direction, enough that a
|
|
42
|
-
fresh plan should be agreed before building. Call **`submit_plan`** with
|
|
43
|
-
revised structured fields (same shape as the request-to-plan skill:
|
|
44
|
-
`goal`, `assumptions`, `steps`, `acceptanceCriteria` — at least 2 —, `risks`,
|
|
45
|
-
`outOfScope`). The runner posts it as a revision
|
|
46
|
-
code changes this turn.
|
|
42
|
+
fresh plan should be agreed before building. Call **`submit_plan`** with a `plans[]` array
|
|
43
|
+
containing the revised structured fields (same per-plan shape as the request-to-plan skill:
|
|
44
|
+
`scope`, `goal`, `assumptions`, `steps`, `acceptanceCriteria` — at least 2 —, `risks`,
|
|
45
|
+
`outOfScope`). Include only one entry for a revise turn. The runner posts it as a revision
|
|
46
|
+
the user can accept; make no code changes this turn.
|
|
47
47
|
- **Implement** — the request is clear and reasonable. Make the change (via
|
|
48
48
|
subagents — see Step 2). This is the common case for small fine-tuning.
|
|
49
49
|
|