@voidlabs.gg/voidshift-agents 0.5.0 → 0.5.1
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/control.d.ts +8 -0
- package/dist/control.js +25 -14
- package/dist/runner.js +2 -5
- package/dist/workflow.d.ts +4 -0
- package/dist/workflow.js +15 -0
- package/pack/vs-plan/SKILL.md +4 -1
- package/pack/vs-plan-review/SKILL.md +3 -1
- package/package.json +2 -2
package/dist/control.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AgentsConfig, type Tracker as Kind } from './config.ts';
|
|
2
2
|
import { type Current, type RunEntry } from './state.ts';
|
|
3
|
+
import { type Trackers } from './tracker.ts';
|
|
3
4
|
import type { Check, ToolCommand, ToolContext } from './types.ts';
|
|
4
5
|
export { parsePlistArgs } from './launchd.ts';
|
|
5
6
|
export declare const trackersInUse: (config: Pick<AgentsConfig, "workflows">) => Kind[];
|
|
@@ -23,5 +24,12 @@ export interface CancelDeps {
|
|
|
23
24
|
cancelCard: (card: string, label: string, source: Kind) => Promise<boolean>;
|
|
24
25
|
}
|
|
25
26
|
export declare function cancelCurrent(ctx: ToolContext, c: Current, deps: CancelDeps): Promise<void>;
|
|
27
|
+
export declare function trackerWarnings(kinds: Kind[], trackers: Trackers): Promise<{
|
|
28
|
+
kind: Kind;
|
|
29
|
+
message: string;
|
|
30
|
+
}[]>;
|
|
31
|
+
export declare function workingLine(current: (Current & {
|
|
32
|
+
elapsedMinutes: number;
|
|
33
|
+
}) | null): string;
|
|
26
34
|
export declare const controlCommands: Record<string, ToolCommand>;
|
|
27
35
|
export declare function controlChecks(ctx: ToolContext): Promise<Check[]>;
|
package/dist/control.js
CHANGED
|
@@ -230,23 +230,30 @@ const realDeps = (ctx) => ({
|
|
|
230
230
|
}
|
|
231
231
|
},
|
|
232
232
|
});
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const trackers = trackersFor(ctx.configDir);
|
|
239
|
-
for (const kind of trackersInUse(readAgentsConfig(ctx.configDir))) {
|
|
233
|
+
// One message per tracker whose key is missing or refused. The tick skips a
|
|
234
|
+
// tracker it cannot open, so these warn rather than stop the runner.
|
|
235
|
+
export async function trackerWarnings(kinds, trackers) {
|
|
236
|
+
const out = [];
|
|
237
|
+
for (const kind of kinds) {
|
|
240
238
|
try {
|
|
241
239
|
await trackers(kind).viewer();
|
|
242
240
|
}
|
|
243
241
|
catch (err) {
|
|
244
242
|
const msg = err.message;
|
|
245
243
|
const flag = kind === 'linear' ? '' : ` --${kind}`;
|
|
246
|
-
|
|
247
|
-
return 1;
|
|
244
|
+
out.push({ kind, message: /^No .* API key/.test(msg) ? msg : `${TRACKERS[kind].name} did not accept the key: ${msg}. Run vsf agents key${flag}.` });
|
|
248
245
|
}
|
|
249
246
|
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
async function startCmd(ctx) {
|
|
250
|
+
if (!Object.keys(readAgentsConfig(ctx.configDir).workflows).length) {
|
|
251
|
+
ctx.log('No workflows yet. Create one first: vsf agents workflow add.');
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
254
|
+
for (const w of await trackerWarnings(trackersInUse(readAgentsConfig(ctx.configDir)), trackersFor(ctx.configDir))) {
|
|
255
|
+
ctx.log(`Warning: ${w.message.replace(/\.?$/, '.')} Workflows on ${TRACKERS[w.kind].name} are skipped until then.`);
|
|
256
|
+
}
|
|
250
257
|
setPaused(ctx.configDir, false);
|
|
251
258
|
if (!runnerService().start(ctx.home, program(ctx), logPath(ctx.configDir))) {
|
|
252
259
|
ctx.log('Could not start the runner. Check vsf agents logs.');
|
|
@@ -263,6 +270,14 @@ async function stopCmd(ctx) {
|
|
|
263
270
|
ctx.log('Runner stopped.');
|
|
264
271
|
return 0;
|
|
265
272
|
}
|
|
273
|
+
// The lock holds the round as the card's vs-vN label shows it; 0 before any round.
|
|
274
|
+
export function workingLine(current) {
|
|
275
|
+
if (!current)
|
|
276
|
+
return '';
|
|
277
|
+
if (!current.card)
|
|
278
|
+
return ' Checking now.';
|
|
279
|
+
return ` Working on ${current.card}${current.title ? ` "${current.title}"` : ''} (${[current.workflow, current.step, current.round ? `round ${current.round}` : '', `${current.elapsedMinutes}m`].filter(Boolean).join(', ')}).`;
|
|
280
|
+
}
|
|
266
281
|
const status = guard(async (args, ctx) => {
|
|
267
282
|
const p = parse('status', args, { json: { type: 'boolean' } }, ctx);
|
|
268
283
|
if (typeof p === 'number')
|
|
@@ -291,11 +306,7 @@ const status = guard(async (args, ctx) => {
|
|
|
291
306
|
ctx.log(JSON.stringify({ engine: config.engine, runner, lastTick, current, parked, interrupted, workflows, projects }, null, 2));
|
|
292
307
|
return 0;
|
|
293
308
|
}
|
|
294
|
-
const working = current
|
|
295
|
-
? ' Checking now.'
|
|
296
|
-
: current
|
|
297
|
-
? ` Working on ${current.card ?? 'a card'}${current.title ? ` "${current.title}"` : ''} (${[current.workflow, current.step, current.round !== undefined ? `round ${current.round + 1}` : '', `${current.elapsedMinutes}m`].filter(Boolean).join(', ')}).`
|
|
298
|
-
: '';
|
|
309
|
+
const working = workingLine(current);
|
|
299
310
|
if (runner === 'running')
|
|
300
311
|
ctx.log(`Runner: running.${working}`);
|
|
301
312
|
else if (runner === 'paused')
|
package/dist/runner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { basename, join } from 'node:path';
|
|
3
|
-
import { LABELS, PLAN_HEADING, buildPrompt, findingsFor, fixTarget, isStatusLabel, latestPlan, parseDevResult, parseVerdict, planBody, qaPassed, stageLabels, verdictBlock, } from "./workflow.js";
|
|
3
|
+
import { LABELS, PLAN_HEADING, buildPrompt, findingsFor, fixTarget, isStatusLabel, latestPlan, needsPlan, parseDevResult, parseVerdict, planBody, qaPassed, stageLabels, verdictBlock, } from "./workflow.js";
|
|
4
4
|
import { agentsDir, hasWork, isCardTrigger, resolveStep, stepId, stepIdLabel } from "./config.js";
|
|
5
5
|
import { lastDue, parseCron } from "./cron.js";
|
|
6
6
|
import { runAgent, stopAgents } from "./engine.js";
|
|
@@ -138,10 +138,7 @@ export async function runCard(card, trigger, workflowName, workflow, project, rc
|
|
|
138
138
|
throw new Park(`Workflow ${workflowName} has no ${stepIdLabel(opts.from)} step.`);
|
|
139
139
|
// A scheduled run has no card to plan on, so its task is the plan unless a Plan step writes one.
|
|
140
140
|
let plan = isCardTrigger(trigger) ? latestPlan(await linear.comments(card.identifier)) : card.description ?? null;
|
|
141
|
-
|
|
142
|
-
// A check before any commits is a check on the plan, like Plan review.
|
|
143
|
-
const needsPlan = steps.findIndex((d, n) => n >= from && (d.output === 'commits' || (d.output === 'verdict' && d.builtin !== 'vs-qa')));
|
|
144
|
-
if (!plan && needsPlan >= 0 && (firstPlan < 0 || firstPlan > needsPlan)) {
|
|
141
|
+
if (!plan && needsPlan(steps, from)) {
|
|
145
142
|
throw new Park('No plan comment (starting with "## Plan") was found. Publish a plan first, or start the workflow with Plan.');
|
|
146
143
|
}
|
|
147
144
|
await stage(stageOf(steps[from]));
|
package/dist/workflow.d.ts
CHANGED
|
@@ -49,4 +49,8 @@ export interface PromptInput {
|
|
|
49
49
|
export declare function buildPrompt(p: PromptInput): string;
|
|
50
50
|
export declare function verdictBlock<T>(steps: T[], i: number, isVerdict: (s: T) => boolean): [number, number];
|
|
51
51
|
export declare function fixTarget<T>(steps: T[], blockStart: number, isProducer: (s: T) => boolean): number;
|
|
52
|
+
export declare function needsPlan(steps: {
|
|
53
|
+
output: string;
|
|
54
|
+
builtin?: string;
|
|
55
|
+
}[], from: number): boolean;
|
|
52
56
|
export declare function planBody(output: string): string;
|
package/dist/workflow.js
CHANGED
|
@@ -105,6 +105,21 @@ export function fixTarget(steps, blockStart, isProducer) {
|
|
|
105
105
|
return j;
|
|
106
106
|
return -1;
|
|
107
107
|
}
|
|
108
|
+
// Whether a run starting at `from` needs a plan comment: some step before any
|
|
109
|
+
// Plan step writes commits or checks the plan. A check counts as a plan check
|
|
110
|
+
// only when a Plan step comes before it, so a review-only workflow runs bare.
|
|
111
|
+
export function needsPlan(steps, from) {
|
|
112
|
+
for (let n = from; n < steps.length; n++) {
|
|
113
|
+
const s = steps[n];
|
|
114
|
+
if (s.output === 'plan')
|
|
115
|
+
return false;
|
|
116
|
+
if (s.output === 'commits')
|
|
117
|
+
return true;
|
|
118
|
+
if (s.output === 'verdict' && s.builtin !== 'vs-qa' && (s.builtin === 'vs-plan-review' || steps.slice(0, n).some((d) => d.output === 'plan')))
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
108
123
|
// Plan output minus the trailing RESULT line, ready to post.
|
|
109
124
|
export function planBody(output) {
|
|
110
125
|
return output.replace(/^\s*RESULT:.*$/gim, '').trim();
|
package/pack/vs-plan/SKILL.md
CHANGED
|
@@ -94,7 +94,9 @@ The plan file holds the detail. In the chat, keep it short and plain:
|
|
|
94
94
|
Start straight at Understanding. Do not repeat the card id or title; the card
|
|
95
95
|
already shows them.
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
Outcome first. Under 350 words for a small change, never over 700 for any
|
|
98
|
+
change. File and line detail only in Surfaces; no code blocks over 5 lines;
|
|
99
|
+
Six angles lists only angles that apply.
|
|
98
100
|
|
|
99
101
|
**Understanding.** Two to four sentences: what the card asks, what it got
|
|
100
102
|
wrong, which reading you chose.
|
|
@@ -123,6 +125,7 @@ anything a human must click.
|
|
|
123
125
|
- No ticket ids in code comments.
|
|
124
126
|
- No em dashes or en dashes.
|
|
125
127
|
- No narration of process. State findings.
|
|
128
|
+
- Do not quote code the dev agent can read.
|
|
126
129
|
|
|
127
130
|
## When you run inside a workflow
|
|
128
131
|
|
|
@@ -41,7 +41,8 @@ main points.
|
|
|
41
41
|
2. The findings that change what gets built, most important first, at most 5,
|
|
42
42
|
one short plain line each: what is wrong and what to do about it.
|
|
43
43
|
3. Everything smaller in one line: `Minor: ...`.
|
|
44
|
-
4. A short `Details` section:
|
|
44
|
+
4. A short `Details` section: one line per finding, the file and line that
|
|
45
|
+
proves it.
|
|
45
46
|
5. Last line, exactly one of:
|
|
46
47
|
|
|
47
48
|
```
|
|
@@ -56,6 +57,7 @@ fixes to the plan file.
|
|
|
56
57
|
|
|
57
58
|
- No em dashes or en dashes.
|
|
58
59
|
- No narration of process. State findings.
|
|
60
|
+
- Whole review under 300 words.
|
|
59
61
|
|
|
60
62
|
## When you run inside a workflow
|
|
61
63
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voidlabs.gg/voidshift-agents",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Agents for Claude Code and Codex that plan, build and QA Linear cards, chained and run in the background.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
42
42
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
43
|
-
"test": "node --test test/*.test.ts",
|
|
43
|
+
"test": "node --test --test-force-exit --test-timeout=60000 test/*.test.ts",
|
|
44
44
|
"prepublishOnly": "rm -rf dist && tsc -p tsconfig.json"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|