@stringsync/splates 0.0.59 → 0.0.61
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/index.js +150 -170
- package/package.json +3 -3
- package/ship.d.ts +3 -0
package/index.js
CHANGED
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
MIGRATION_LIMIT,
|
|
98
98
|
migrateRecords,
|
|
99
99
|
OpenBoards as OpenBoards2,
|
|
100
|
-
repository as
|
|
100
|
+
repository as repository11
|
|
101
101
|
} from "@stringsync/splates-server/records";
|
|
102
102
|
import {
|
|
103
103
|
newest,
|
|
@@ -107,7 +107,6 @@ import {
|
|
|
107
107
|
seeded as seeded2,
|
|
108
108
|
update
|
|
109
109
|
} from "@stringsync/splates-server/server";
|
|
110
|
-
import { ship } from "@stringsync/splates-server/ship";
|
|
111
110
|
import { addFactTasks } from "@stringsync/splates-server/stats";
|
|
112
111
|
import {
|
|
113
112
|
add,
|
|
@@ -125,9 +124,9 @@ import {
|
|
|
125
124
|
untilAfter as untilAfter3,
|
|
126
125
|
wait
|
|
127
126
|
} from "@stringsync/splates-server/tasks";
|
|
128
|
-
import { cli as
|
|
127
|
+
import { cli as cli16 } from "webappwiz/cmd";
|
|
129
128
|
import { Duration as Duration3 } from "webappwiz/time";
|
|
130
|
-
import { z as
|
|
129
|
+
import { z as z17 } from "zod";
|
|
131
130
|
|
|
132
131
|
// artifact.ts
|
|
133
132
|
import { basename, resolve as resolve2 } from "node:path";
|
|
@@ -650,10 +649,10 @@ function storeOf(ctx) {
|
|
|
650
649
|
|
|
651
650
|
// lane.ts
|
|
652
651
|
import {
|
|
653
|
-
APPROVAL,
|
|
654
652
|
BACKLOG_LANE as BACKLOG_LANE2,
|
|
655
653
|
laneAdd,
|
|
656
654
|
laneList,
|
|
655
|
+
laneQueue,
|
|
657
656
|
laneRemove,
|
|
658
657
|
laneShow,
|
|
659
658
|
laneUpdate,
|
|
@@ -662,7 +661,10 @@ import {
|
|
|
662
661
|
} from "@stringsync/splates-server/board";
|
|
663
662
|
import { refuseGroomer } from "@stringsync/splates-server/conversations";
|
|
664
663
|
import { UNTRIAGED } from "@stringsync/splates-server/plans";
|
|
665
|
-
import {
|
|
664
|
+
import {
|
|
665
|
+
refuseAgentQueue,
|
|
666
|
+
sayUnqueuedBlockers
|
|
667
|
+
} from "@stringsync/splates-server/queue";
|
|
666
668
|
import { repository as repository4 } from "@stringsync/splates-server/records";
|
|
667
669
|
import { cli as cli5 } from "webappwiz/cmd";
|
|
668
670
|
import { z as z6 } from "zod";
|
|
@@ -670,7 +672,7 @@ var laneNumber = z6.union([
|
|
|
670
672
|
z6.literal("backlog").transform(() => BACKLOG_LANE2),
|
|
671
673
|
z6.coerce.number().int().nonnegative()
|
|
672
674
|
]);
|
|
673
|
-
var splatesLane = cli5("lane").description("lanes: named groups of tasks, for you to see related work together on the board; queue
|
|
675
|
+
var splatesLane = cli5("lane").description("lanes: named groups of tasks, for you to see related work together on the board; queue all its tasks at once with `splates lane queue`. Backlog (lane 0) is permanent and holds unmatched deferred work. Untriaged is groomed into lanes and cannot be queued as a lane");
|
|
674
676
|
splatesLane.command("list").use(repository4({ needs: "read" })).description("every lane: its name, its queued tasks in order, the started tasks on it, its next task, and what blocks that in other lanes").option("json", z6.boolean(), { default: false, description: "emit JSON" }).action((opts, ctx) => laneList(ctx, { json: opts.json }));
|
|
675
677
|
splatesLane.command("show").use(repository4({ needs: "read" })).description("one lane step by step, and what its agent does next: start the next task, or wait for the one blocking it in another lane to land first; `untriaged` lists the tasks in no lane, to place in lanes").arg("lane", z6.union([z6.literal(UNTRIAGED).transform(() => null), laneNumber], {
|
|
676
678
|
error: "pass a lane's number, backlog, or untriaged"
|
|
@@ -690,27 +692,19 @@ splatesLane.command("update").use(repository4({ needs: "tasks" })).description("
|
|
|
690
692
|
const change = { name: opts.name, position: opts.position };
|
|
691
693
|
await ctx.journal.record("lane update", null, () => laneUpdate(ctx, opts.lane, change), (record) => laneUpdated(opts.lane, record, change));
|
|
692
694
|
});
|
|
693
|
-
splatesLane.command("queue").use(repository4({ needs: "tasks" })).description("queue a lane
|
|
695
|
+
splatesLane.command("queue").use(repository4({ needs: "tasks" })).description("queue every task in a lane now, once: the queue starts each once nothing it is blocked by is open; one blocked by a task outside the queue waits queued until that is queued too, as the command names; a task put in the lane later is queued on its own. Untriaged cannot be queued as a lane").arg("lane", laneNumber, {
|
|
694
696
|
description: "lane number"
|
|
695
697
|
}).action(async (opts, ctx) => {
|
|
696
698
|
refuseAgentQueue(ctx.service, "lane queue");
|
|
697
699
|
await refuseGroomer(ctx.service, "lane queue");
|
|
698
|
-
const
|
|
699
|
-
await
|
|
700
|
+
const queued = await laneQueue(ctx, opts.lane, true);
|
|
701
|
+
await sayUnqueuedBlockers(ctx, queued.map((task) => task.id));
|
|
700
702
|
});
|
|
701
|
-
splatesLane.command("
|
|
702
|
-
description: "lane number"
|
|
703
|
-
}).action(async (opts, ctx) => {
|
|
704
|
-
refuseAgentQueue(ctx.service, "lane approve");
|
|
705
|
-
const change = { approve: APPROVAL };
|
|
706
|
-
await ctx.journal.record("lane update", null, () => laneUpdate(ctx, opts.lane, change), (record) => laneUpdated(opts.lane, record, change));
|
|
707
|
-
});
|
|
708
|
-
splatesLane.command("unqueue").use(repository4({ needs: "tasks" })).description("stop queueing a lane whole, taking out of the queue each of its tasks that has yet to start; those under way go on").arg("lane", laneNumber, {
|
|
703
|
+
splatesLane.command("unqueue").use(repository4({ needs: "tasks" })).description("take each of a lane's queued tasks out of the queue; those under way go on").arg("lane", laneNumber, {
|
|
709
704
|
description: "lane number"
|
|
710
705
|
}).action(async (opts, ctx) => {
|
|
711
706
|
refuseAgentQueue(ctx.service, "lane unqueue");
|
|
712
|
-
|
|
713
|
-
await ctx.journal.record("lane update", null, () => laneUpdate(ctx, opts.lane, change), (record) => laneUpdated(opts.lane, record, change));
|
|
707
|
+
await laneQueue(ctx, opts.lane, false);
|
|
714
708
|
});
|
|
715
709
|
splatesLane.command("remove").use(repository4({ needs: "tasks" })).description("remove a lane and its tasks, kept to restore; a lane whose last task merges goes on its own; agents may remove an empty lane; for one containing tasks, ask with a permission card").arg("lane", laneNumber, {
|
|
716
710
|
description: "lane number"
|
|
@@ -1094,12 +1088,38 @@ function printUndo(log, plan) {
|
|
|
1094
1088
|
log.info(color3.dim("the Setup page's Undo does it, after the user confirms"));
|
|
1095
1089
|
}
|
|
1096
1090
|
|
|
1097
|
-
//
|
|
1098
|
-
import {
|
|
1091
|
+
// ship.ts
|
|
1092
|
+
import { repository as repository8 } from "@stringsync/splates-server/records";
|
|
1093
|
+
import { DEFAULT_PORT as DEFAULT_PORT2 } from "@stringsync/splates-server/server";
|
|
1099
1094
|
import { cli as cli10 } from "webappwiz/cmd";
|
|
1100
1095
|
import { z as z11 } from "zod";
|
|
1101
|
-
var
|
|
1102
|
-
|
|
1096
|
+
var splatesShip = cli10("ship").description("open the Ships page with a project picked").fallback("run");
|
|
1097
|
+
splatesShip.command("run").use(repository8({ needs: "read" })).arg("project", z11.string(), {
|
|
1098
|
+
default: "",
|
|
1099
|
+
description: "the registered project to show"
|
|
1100
|
+
}).option("dev", z11.string(), {
|
|
1101
|
+
default: "",
|
|
1102
|
+
description: "the splates server URL"
|
|
1103
|
+
}).action((opts, ctx) => {
|
|
1104
|
+
const page = new URL("/", opts.dev || ctx.ps.env("SPLATES_DEV") || `http://localhost:${DEFAULT_PORT2}`);
|
|
1105
|
+
page.hash = "ships";
|
|
1106
|
+
if (opts.project !== "") {
|
|
1107
|
+
page.searchParams.set("ship", opts.project);
|
|
1108
|
+
}
|
|
1109
|
+
ctx.log.info(`Open ship plan: ${page}`);
|
|
1110
|
+
const local = new URL(page);
|
|
1111
|
+
local.hostname = "localhost";
|
|
1112
|
+
local.protocol = "http:";
|
|
1113
|
+
local.port = page.hostname === "localhost" || page.hostname === "127.0.0.1" ? page.port : String(DEFAULT_PORT2);
|
|
1114
|
+
ctx.log.info(`Local: ${local}`);
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
// slots.ts
|
|
1118
|
+
import { Slots } from "@stringsync/splates-server/ports";
|
|
1119
|
+
import { cli as cli11 } from "webappwiz/cmd";
|
|
1120
|
+
import { z as z12 } from "zod";
|
|
1121
|
+
var splatesSlots = cli11("slots").description("the words `splates server --slot` hands out on this machine, each held by the tree that claimed it: a task's worktree holds its word until the task merges or is removed, which stops its server and gives the word back");
|
|
1122
|
+
splatesSlots.command("list").description("print each claimed word, the tree holding it, its server's pid while one runs, and whether it is orphaned: its tree gone, or a splates repository's main tree, which every agent shares").option("json", z12.boolean(), { default: false, description: "emit JSON" }).action(async (opts, ctx) => {
|
|
1103
1123
|
const held = await Slots.ofMachine(ctx).status();
|
|
1104
1124
|
if (opts.json) {
|
|
1105
1125
|
ctx.log.info(JSON.stringify(held.map(({ slot, tree, pid, orphaned }) => ({
|
|
@@ -1145,16 +1165,16 @@ import {
|
|
|
1145
1165
|
import { commaList as commaList5 } from "@stringsync/splates-server/comma-list";
|
|
1146
1166
|
import { refuseGroomer as refuseGroomer3 } from "@stringsync/splates-server/conversations";
|
|
1147
1167
|
import { fail as fail9 } from "@stringsync/splates-server/exit";
|
|
1148
|
-
import { OpenBoards, repository as
|
|
1168
|
+
import { OpenBoards, repository as repository9 } from "@stringsync/splates-server/records";
|
|
1149
1169
|
import {
|
|
1150
1170
|
DEFAULT_TIMEOUT as DEFAULT_TIMEOUT2,
|
|
1151
1171
|
taskWait,
|
|
1152
1172
|
untilAfter as untilAfter2
|
|
1153
1173
|
} from "@stringsync/splates-server/tasks";
|
|
1154
|
-
import { cli as
|
|
1174
|
+
import { cli as cli12 } from "webappwiz/cmd";
|
|
1155
1175
|
import { Duration as Duration2 } from "webappwiz/time";
|
|
1156
|
-
import { z as
|
|
1157
|
-
var splatesTask =
|
|
1176
|
+
import { z as z13 } from "zod";
|
|
1177
|
+
var splatesTask = cli12("task").description("the numbered tasks: defer work for later instead of growing the task you are in, and `splates add <slug> --task <n>` starts one; `merge` recommends the next one when a task lands. The person puts tasks in `splates queue`, for workers to start");
|
|
1158
1178
|
async function projectFrom(ctx) {
|
|
1159
1179
|
if (ctx.elsewhere === null) {
|
|
1160
1180
|
return;
|
|
@@ -1162,29 +1182,29 @@ async function projectFrom(ctx) {
|
|
|
1162
1182
|
return (await ctx.projects.list()).find((each) => each.root === ctx.git.root)?.slug;
|
|
1163
1183
|
}
|
|
1164
1184
|
function adding(name, description, ready) {
|
|
1165
|
-
splatesTask.command(name).use(
|
|
1185
|
+
splatesTask.command(name).use(repository9({ needs: "queue" })).description(description).arg("subject", z13.string(), {
|
|
1166
1186
|
description: "what is left to do, in a line"
|
|
1167
|
-
}).arg("text",
|
|
1187
|
+
}).arg("text", z13.string(), {
|
|
1168
1188
|
default: "",
|
|
1169
1189
|
description: "whatever more there is to say about it, in markdown"
|
|
1170
|
-
}).option("slug",
|
|
1190
|
+
}).option("slug", z13.string(), {
|
|
1171
1191
|
description: "a short name for the task that takes it (lowercase-with-dashes), naming its branch, worktree and conversation: a few words for what it changes, not the subject cut short"
|
|
1172
1192
|
}).option("position", position, {
|
|
1173
1193
|
description: "where it goes in the list, 1 at the top, pushing those from there down (default: the bottom)"
|
|
1174
|
-
}).repeated("file",
|
|
1194
|
+
}).repeated("file", z13.string(), {
|
|
1175
1195
|
description: "files to attach, comma separated or the flag repeated: copied under .git/splates/tasks/<n>/"
|
|
1176
|
-
}).repeated("blocked-by",
|
|
1196
|
+
}).repeated("blocked-by", z13.string(), {
|
|
1177
1197
|
description: "the tasks blocking it, comma separated or the flag repeated: a number for one here, <project>#<number> for one of another project (`webappwiz#404`); each has to merge before this one starts"
|
|
1178
|
-
}).option("lane",
|
|
1198
|
+
}).option("lane", z13.string(), {
|
|
1179
1199
|
default: "",
|
|
1180
1200
|
description: "the lane it goes in, at the bottom: a lane's number, `backlog` for the permanent catch-all, or `new` for one of its own (default: none, untriaged)"
|
|
1181
|
-
}).option("agent",
|
|
1201
|
+
}).option("agent", z13.string(), {
|
|
1182
1202
|
default: "",
|
|
1183
1203
|
description: "the agent on the roster (`splates agents`) that should work it, which Start on the `splates server` board offers first"
|
|
1184
|
-
}).option("model",
|
|
1204
|
+
}).option("model", z13.string(), {
|
|
1185
1205
|
default: "",
|
|
1186
1206
|
description: "with --agent: the model, by its id or name on the roster"
|
|
1187
|
-
}).option("effort",
|
|
1207
|
+
}).option("effort", z13.string(), {
|
|
1188
1208
|
default: "",
|
|
1189
1209
|
description: "with --agent: the thinking level, one the agent offers (default: the agent's own)"
|
|
1190
1210
|
}).action(async (opts, ctx) => {
|
|
@@ -1229,10 +1249,10 @@ function adding(name, description, ready) {
|
|
|
1229
1249
|
}
|
|
1230
1250
|
adding("add", "queue a task to do later and move on, groomed, so it goes in ready: you checked no task already asks it, and linked what blocks it; run from a worktree, it records the task it came up in", true);
|
|
1231
1251
|
adding("defer", "queue a task as it is, rough, in the backlog for whoever grooms it: to check for one that already asks it, link what blocks it, and mark it ready; run from a worktree, it records the task it came up in", false);
|
|
1232
|
-
splatesTask.command("list").use(
|
|
1252
|
+
splatesTask.command("list").use(repository9({ needs: "read" })).description("every queued task, top of the list first, with the task it came from and the started one that took it up").option("json", z13.boolean(), { default: false, description: "emit JSON" }).option("open", z13.boolean(), {
|
|
1233
1253
|
default: false,
|
|
1234
1254
|
description: "only tasks no started one has taken up: the ones free to pick up"
|
|
1235
|
-
}).option("ready",
|
|
1255
|
+
}).option("ready", z13.boolean(), {
|
|
1236
1256
|
default: false,
|
|
1237
1257
|
description: "only tasks free to start now: none has taken them, and nothing blocking them is still open"
|
|
1238
1258
|
}).action(async (opts, ctx) => taskList(ctx, {
|
|
@@ -1241,7 +1261,7 @@ splatesTask.command("list").use(repository8({ needs: "read" })).description("eve
|
|
|
1241
1261
|
ready: opts.ready,
|
|
1242
1262
|
project: await projectFrom(ctx)
|
|
1243
1263
|
}));
|
|
1244
|
-
splatesTask.command("show").use(
|
|
1264
|
+
splatesTask.command("show").use(repository9({ needs: "read" })).description("one task in full: where it stands, its files, and its detail").arg("id", z13.coerce.number().int().positive(), { description: "task number" }).option("json", z13.boolean(), { default: false, description: "emit JSON" }).action(async (opts, ctx) => {
|
|
1245
1265
|
let __stack = [];
|
|
1246
1266
|
try {
|
|
1247
1267
|
const boards = __using(__stack, new OpenBoards(ctx, ctx.projects), 0);
|
|
@@ -1258,52 +1278,43 @@ splatesTask.command("show").use(repository8({ needs: "read" })).description("one
|
|
|
1258
1278
|
__callDispose(__stack, _err, _hasErr);
|
|
1259
1279
|
}
|
|
1260
1280
|
});
|
|
1261
|
-
splatesTask.command("update").use(
|
|
1281
|
+
splatesTask.command("update").use(repository9({ needs: "tasks" })).description("say what a queued task is in other words, move it up or down the list, or attach and drop files; it keeps its number").arg("id", z13.coerce.number().int().positive(), { description: "task number" }).arg("text", z13.string(), {
|
|
1262
1282
|
default: "",
|
|
1263
1283
|
description: "whatever more there is to say about it, in markdown; leave out to keep the detail"
|
|
1264
|
-
}).option("subject",
|
|
1284
|
+
}).option("subject", z13.string(), {
|
|
1265
1285
|
default: "",
|
|
1266
1286
|
description: "what is left to do, in a line; leave out to keep it"
|
|
1267
|
-
}).option("slug",
|
|
1287
|
+
}).option("slug", z13.string(), {
|
|
1268
1288
|
default: "",
|
|
1269
1289
|
description: "a new name for the task that takes it (lowercase-with-dashes); leave out to keep it"
|
|
1270
1290
|
}).option("position", position, {
|
|
1271
1291
|
description: "where to move it in the list, 1 at the top; the others close up around it"
|
|
1272
|
-
}).repeated("file",
|
|
1292
|
+
}).repeated("file", z13.string(), {
|
|
1273
1293
|
description: "files to attach, comma separated or the flag repeated"
|
|
1274
|
-
}).repeated("remove-file",
|
|
1294
|
+
}).repeated("remove-file", z13.string(), {
|
|
1275
1295
|
description: "attached files to drop, comma separated or the flag repeated, by path or stored name"
|
|
1276
|
-
}).repeated("blocked-by",
|
|
1296
|
+
}).repeated("blocked-by", z13.string(), {
|
|
1277
1297
|
description: "more tasks blocking it, comma separated or the flag repeated: a number for one here, <project>#<number> for one of another project (`webappwiz#404`); each has to merge before this one starts; refused when that makes a loop"
|
|
1278
|
-
}).repeated("remove-blocked-by",
|
|
1298
|
+
}).repeated("remove-blocked-by", z13.string(), {
|
|
1279
1299
|
description: "tasks no longer blocking it, as --blocked-by names them, comma separated or the flag repeated"
|
|
1280
|
-
}).option("lane",
|
|
1300
|
+
}).option("lane", z13.string(), {
|
|
1281
1301
|
default: "",
|
|
1282
1302
|
description: "move it to a lane, at the bottom: a lane's number, `backlog` for the permanent catch-all, `new` for one of its own, or `none` for untriaged"
|
|
1283
|
-
}).option("agent",
|
|
1303
|
+
}).option("agent", z13.string(), {
|
|
1284
1304
|
default: "",
|
|
1285
1305
|
description: "the agent on the roster (`splates agents`) that should work it, which Start on the `splates server` board offers first, or `none`"
|
|
1286
|
-
}).option("model",
|
|
1306
|
+
}).option("model", z13.string(), {
|
|
1287
1307
|
default: "",
|
|
1288
1308
|
description: "with --agent: the model, by its id or name on the roster"
|
|
1289
|
-
}).option("effort",
|
|
1309
|
+
}).option("effort", z13.string(), {
|
|
1290
1310
|
default: "",
|
|
1291
1311
|
description: "with --agent: the thinking level, one the agent offers (default: the agent's own)"
|
|
1292
|
-
}).option("ready",
|
|
1312
|
+
}).option("ready", z13.boolean(), {
|
|
1293
1313
|
default: false,
|
|
1294
1314
|
description: "mark it groomed, moving it from the backlog to ready: no task already asks it, and what blocks it is linked"
|
|
1295
|
-
}).option("manual", z12.boolean(), {
|
|
1296
|
-
default: false,
|
|
1297
|
-
description: "queue it by hand only: a queued lane passes over it, and it starts once you `splates queue add` it"
|
|
1298
|
-
}).option("auto", z12.boolean(), {
|
|
1299
|
-
default: false,
|
|
1300
|
-
description: "let a queued lane queue it again, undoing --manual"
|
|
1301
1315
|
}).action(async (opts, ctx) => {
|
|
1302
1316
|
let __stack = [];
|
|
1303
1317
|
try {
|
|
1304
|
-
if (opts.manual && opts.auto) {
|
|
1305
|
-
fail9("usage", "pass --manual or --auto, not both", { number: opts.id });
|
|
1306
|
-
}
|
|
1307
1318
|
await refuseGroomer3(ctx.service, "task update", await ctx.tasks.find(opts.id));
|
|
1308
1319
|
const assignee = await assigneeIn(ctx, opts.agent, opts.model, opts.effort);
|
|
1309
1320
|
const boards = __using(__stack, new OpenBoards(ctx, ctx.projects), 0);
|
|
@@ -1327,7 +1338,6 @@ splatesTask.command("update").use(repository8({ needs: "tasks" })).description("
|
|
|
1327
1338
|
lane: laneIn(opts.lane),
|
|
1328
1339
|
assignee,
|
|
1329
1340
|
ready: opts.ready,
|
|
1330
|
-
manual: opts.manual ? true : opts.auto ? false : undefined,
|
|
1331
1341
|
boards
|
|
1332
1342
|
}));
|
|
1333
1343
|
} catch (_catch) {
|
|
@@ -1336,11 +1346,11 @@ splatesTask.command("update").use(repository8({ needs: "tasks" })).description("
|
|
|
1336
1346
|
__callDispose(__stack, _err, _hasErr);
|
|
1337
1347
|
}
|
|
1338
1348
|
});
|
|
1339
|
-
splatesTask.command("remove").use(
|
|
1349
|
+
splatesTask.command("remove").use(repository9({ needs: "tasks" })).description("drop a queued task that was done some other way or no longer applies").arg("id", z13.coerce.number().int().positive(), { description: "task number" }).action(async (opts, ctx) => {
|
|
1340
1350
|
await refuseGroomer3(ctx.service, "task remove", await ctx.tasks.find(opts.id));
|
|
1341
1351
|
await ctx.journal.record("task remove", null, () => taskRemove(ctx, opts.id), (removed) => ({ tasks: tasksNamed2([removed]) }));
|
|
1342
1352
|
});
|
|
1343
|
-
splatesTask.command("wait").use(
|
|
1353
|
+
splatesTask.command("wait").use(repository9({ needs: "read" })).description("block until a task leaves the queue, which it does when the task that took it merges: for a task blocked by one another lane is on; ends early when that task waits on a person to answer its questions, and gives up with `timeout`").arg("id", z13.coerce.number().int().positive(), { description: "task number" }).option("timeout-secs", z13.coerce.number(), {
|
|
1344
1354
|
default: DEFAULT_TIMEOUT2.secs,
|
|
1345
1355
|
description: "how long to wait before giving up"
|
|
1346
1356
|
}).action(async (opts, ctx) => {
|
|
@@ -1354,25 +1364,25 @@ splatesTask.command("wait").use(repository8({ needs: "read" })).description("blo
|
|
|
1354
1364
|
});
|
|
1355
1365
|
|
|
1356
1366
|
// tunnel.ts
|
|
1357
|
-
import { repository as
|
|
1367
|
+
import { repository as repository10 } from "@stringsync/splates-server/records";
|
|
1358
1368
|
import { savedTunnelOf } from "@stringsync/splates-server/server";
|
|
1359
1369
|
import {
|
|
1360
1370
|
API_TOKEN,
|
|
1361
1371
|
tunnelRemove,
|
|
1362
1372
|
tunnelSetup
|
|
1363
1373
|
} from "@stringsync/splates-server/tunnel";
|
|
1364
|
-
import { cli as
|
|
1365
|
-
import { z as
|
|
1366
|
-
var splatesTunnel =
|
|
1367
|
-
splatesTunnel.command("setup").use(
|
|
1374
|
+
import { cli as cli13 } from "webappwiz/cmd";
|
|
1375
|
+
import { z as z14 } from "zod";
|
|
1376
|
+
var splatesTunnel = cli13("tunnel").description("reach `splates server` from elsewhere: a Cloudflare tunnel with Cloudflare Access in front, set up once per machine");
|
|
1377
|
+
splatesTunnel.command("setup").use(repository10({ needs: "own" })).description(`make a Cloudflare tunnel to \`splates server\`, the DNS record routing <host> to it, and an Access application letting in only --email, through the API with the token kept as the cred ${API_TOKEN}, then save them for this machine, so a bare \`splates server\` comes up tunneled; run again, it changes only what is not so already. Without that token it requests it on the Creds page of \`splates server\`, with how to make it, and exits \`requested\`: \`splates creds wait ${API_TOKEN}\`, then run it again`).arg("host", z14.string(), {
|
|
1368
1378
|
description: "the public host name, in a zone of the account, as in splates.example.com"
|
|
1369
|
-
}).option("email",
|
|
1379
|
+
}).option("email", z14.string(), {
|
|
1370
1380
|
default: "",
|
|
1371
1381
|
description: "comma-separated email addresses Access lets in"
|
|
1372
|
-
}).option("name",
|
|
1382
|
+
}).option("name", z14.string(), {
|
|
1373
1383
|
default: "splates",
|
|
1374
1384
|
description: "what the tunnel, the Access application and its policy are called"
|
|
1375
|
-
}).option("team",
|
|
1385
|
+
}).option("team", z14.string(), {
|
|
1376
1386
|
default: "",
|
|
1377
1387
|
description: "the Zero Trust team to make, as in <team>.cloudflareaccess.com, when the account has none yet"
|
|
1378
1388
|
}).action(async (opts, ctx) => ctx.journal.record("tunnel setup", null, async () => tunnelSetup({
|
|
@@ -1383,7 +1393,7 @@ splatesTunnel.command("setup").use(repository9({ needs: "own" })).description(`m
|
|
|
1383
1393
|
log: ctx.log,
|
|
1384
1394
|
fetch: ctx.fetch
|
|
1385
1395
|
})));
|
|
1386
|
-
splatesTunnel.command("status").description("print the tunnel `splates tunnel setup` saved for this machine, which a bare `splates server` runs, and whether its token is in the system's secret store").option("json",
|
|
1396
|
+
splatesTunnel.command("status").description("print the tunnel `splates tunnel setup` saved for this machine, which a bare `splates server` runs, and whether its token is in the system's secret store").option("json", z14.boolean(), { default: false, description: "emit JSON" }).action(async (opts, ctx) => {
|
|
1387
1397
|
const saved = savedTunnelOf(ctx);
|
|
1388
1398
|
const record = await saved.read();
|
|
1389
1399
|
const token = record === null ? false : await saved.token() !== undefined;
|
|
@@ -1404,7 +1414,7 @@ splatesTunnel.command("status").description("print the tunnel `splates tunnel se
|
|
|
1404
1414
|
].join(`
|
|
1405
1415
|
`));
|
|
1406
1416
|
});
|
|
1407
|
-
splatesTunnel.command("remove").use(
|
|
1417
|
+
splatesTunnel.command("remove").use(repository10({ needs: "own" })).description(`delete what \`splates tunnel setup\` and \`splates server --slot\` made in Cloudflare by what they saved: each tunnel, the DNS record routing to it, the Access applications and their policy, then forget the saved files and the tunnels' tokens; the API token kept as ${API_TOKEN} stays. Run again after a failure, it finishes what is left`).action(async (_opts, ctx) => ctx.journal.record("tunnel remove", null, async () => tunnelRemove({
|
|
1408
1418
|
saved: savedTunnelOf(ctx),
|
|
1409
1419
|
slots: slotTunnelsOf(ctx),
|
|
1410
1420
|
creds: ctx.creds,
|
|
@@ -1423,20 +1433,20 @@ import {
|
|
|
1423
1433
|
removeWorker,
|
|
1424
1434
|
workerRun
|
|
1425
1435
|
} from "@stringsync/splates-server/workers";
|
|
1426
|
-
import { cli as
|
|
1436
|
+
import { cli as cli15 } from "webappwiz/cmd";
|
|
1427
1437
|
import { CompositeLogger } from "webappwiz/log";
|
|
1428
|
-
import { z as
|
|
1438
|
+
import { z as z16 } from "zod";
|
|
1429
1439
|
|
|
1430
1440
|
// waiting.ts
|
|
1431
1441
|
import { listQueue, moveQueued } from "@stringsync/splates-server/workers";
|
|
1432
|
-
import { cli as
|
|
1433
|
-
import { z as
|
|
1442
|
+
import { cli as cli14 } from "webappwiz/cmd";
|
|
1443
|
+
import { z as z15 } from "zod";
|
|
1434
1444
|
var DEV = {
|
|
1435
1445
|
default: "",
|
|
1436
1446
|
description: "the `splates server` to ask, a URL such as http://localhost:4310; by default the one an agent runs under, from SPLATES_DEV"
|
|
1437
1447
|
};
|
|
1438
|
-
var splatesWaiting =
|
|
1439
|
-
splatesWaiting.command("list").description("list the agents waiting to start, first in line first: position, project, task, conversation, how long each has waited and why (paused, no worker up, or worker draining)").option("dev",
|
|
1448
|
+
var splatesWaiting = cli14("queue").description("the agents waiting to start on a worker of the running `splates server`: nothing caps how many agents run, so one waits only while what splates starts on its own (the queue, automations, hooks, the groomer) is paused, or while no worker it can run on is up, first come, first served; none is ever failed for waiting. An agent you start waits only while no worker is up");
|
|
1449
|
+
splatesWaiting.command("list").description("list the agents waiting to start, first in line first: position, project, task, conversation, how long each has waited and why (paused, no worker up, or worker draining)").option("dev", z15.string(), DEV).option("json", z15.boolean(), { default: false, description: "emit JSON" }).action((opts, ctx) => listQueue({
|
|
1440
1450
|
dev: opts.dev,
|
|
1441
1451
|
json: opts.json,
|
|
1442
1452
|
log: ctx.log,
|
|
@@ -1444,11 +1454,11 @@ splatesWaiting.command("list").description("list the agents waiting to start, fi
|
|
|
1444
1454
|
ps: ctx.ps,
|
|
1445
1455
|
send: ctx.fetch
|
|
1446
1456
|
}));
|
|
1447
|
-
splatesWaiting.command("move").description("move an agent waiting to start to another place in line, from 1 for the next to start, then list the queue").arg("which",
|
|
1457
|
+
splatesWaiting.command("move").description("move an agent waiting to start to another place in line, from 1 for the next to start, then list the queue").arg("which", z15.string(), {
|
|
1448
1458
|
description: "the task it works, or its conversation's id"
|
|
1449
|
-
}).arg("position",
|
|
1459
|
+
}).arg("position", z15.coerce.number().int(), {
|
|
1450
1460
|
description: "where in line to put it, from 1"
|
|
1451
|
-
}).option("dev",
|
|
1461
|
+
}).option("dev", z15.string(), DEV).option("json", z15.boolean(), { default: false, description: "emit JSON" }).action((opts, ctx) => moveQueued({
|
|
1452
1462
|
which: opts.which,
|
|
1453
1463
|
position: opts.position,
|
|
1454
1464
|
dev: opts.dev,
|
|
@@ -1460,20 +1470,20 @@ splatesWaiting.command("move").description("move an agent waiting to start to an
|
|
|
1460
1470
|
}));
|
|
1461
1471
|
|
|
1462
1472
|
// worker.ts
|
|
1463
|
-
var splatesWorker =
|
|
1464
|
-
splatesWorker.command("run").description("run this machine’s worker until stopped, connecting over its private Unix socket and reconnecting when the server restarts").option("server",
|
|
1473
|
+
var splatesWorker = cli15("worker").description("this machine’s worker, which runs agents, terminals and process reads for `splates server` over its Unix socket");
|
|
1474
|
+
splatesWorker.command("run").description("run this machine’s worker until stopped, connecting over its private Unix socket and reconnecting when the server restarts").option("server", z16.string(), {
|
|
1465
1475
|
default: "",
|
|
1466
1476
|
description: "the `splates server` to dial: unix:<path>; by default this machine's"
|
|
1467
|
-
}).option("coordinator",
|
|
1477
|
+
}).option("coordinator", z16.string(), {
|
|
1468
1478
|
default: "",
|
|
1469
1479
|
description: "the old name for --server, still read for workers installed before it, and passed to a build older than it"
|
|
1470
|
-
}).option("slots",
|
|
1480
|
+
}).option("slots", z16.coerce.number().int().min(0), {
|
|
1471
1481
|
default: 0,
|
|
1472
1482
|
description: "ignored: nothing caps how many agents a worker runs; still read, since a `splates server` started before it was dropped passes it"
|
|
1473
|
-
}).option("id",
|
|
1483
|
+
}).option("id", z16.literal("local"), {
|
|
1474
1484
|
default: "local",
|
|
1475
1485
|
description: "what the server knows this worker by, kept across restarts so it picks up where it was; by default local"
|
|
1476
|
-
}).option("quiet",
|
|
1486
|
+
}).option("quiet", z16.boolean(), {
|
|
1477
1487
|
default: false,
|
|
1478
1488
|
description: "say only what went wrong, not each time it joins or loses its server, as the worker `splates server` starts beside itself does; all of it still goes to worker.log in ~/.splates, the one before it kept as worker.log.1"
|
|
1479
1489
|
}).action((opts, ctx) => workerRun({
|
|
@@ -1483,10 +1493,10 @@ splatesWorker.command("run").description("run this machine’s worker until stop
|
|
|
1483
1493
|
ps: ctx.ps,
|
|
1484
1494
|
fs: ctx.fs
|
|
1485
1495
|
}));
|
|
1486
|
-
splatesWorker.command("list").description("list the workers the running `splates server` has: platform, build, protocol, health with when it was last heard, and tasks, then any it refused for speaking another protocol").option("dev",
|
|
1496
|
+
splatesWorker.command("list").description("list the workers the running `splates server` has: platform, build, protocol, health with when it was last heard, and tasks, then any it refused for speaking another protocol").option("dev", z16.string(), {
|
|
1487
1497
|
default: "",
|
|
1488
1498
|
description: "the `splates server` to ask, a URL such as http://localhost:4310; by default the one an agent runs under, from SPLATES_DEV"
|
|
1489
|
-
}).option("json",
|
|
1499
|
+
}).option("json", z16.boolean(), { default: false, description: "emit JSON" }).action((opts, ctx) => listWorkers({
|
|
1490
1500
|
dev: opts.dev,
|
|
1491
1501
|
json: opts.json,
|
|
1492
1502
|
log: ctx.log,
|
|
@@ -1498,12 +1508,12 @@ var dev = {
|
|
|
1498
1508
|
default: "",
|
|
1499
1509
|
description: "the `splates server` to ask, a URL such as http://localhost:4310; by default the one an agent runs under, from SPLATES_DEV"
|
|
1500
1510
|
};
|
|
1501
|
-
splatesWorker.command("drain").description("take no new agents on a worker, letting those on it run to their end, but for one asking you something in a task, which saves its task and lets its slot go, its question kept for you to answer and carry it on elsewhere; --undo takes new ones again").arg("id",
|
|
1511
|
+
splatesWorker.command("drain").description("take no new agents on a worker, letting those on it run to their end, but for one asking you something in a task, which saves its task and lets its slot go, its question kept for you to answer and carry it on elsewhere; --undo takes new ones again").arg("id", z16.string(), {
|
|
1502
1512
|
description: "the worker, as `splates worker list` names it"
|
|
1503
|
-
}).option("undo",
|
|
1513
|
+
}).option("undo", z16.boolean(), {
|
|
1504
1514
|
default: false,
|
|
1505
1515
|
description: "take new agents again"
|
|
1506
|
-
}).option("dev",
|
|
1516
|
+
}).option("dev", z16.string(), dev).action((opts, ctx) => drainWorker({
|
|
1507
1517
|
id: opts.id,
|
|
1508
1518
|
undo: opts.undo,
|
|
1509
1519
|
dev: opts.dev,
|
|
@@ -1512,12 +1522,12 @@ splatesWorker.command("drain").description("take no new agents on a worker, lett
|
|
|
1512
1522
|
ps: ctx.ps,
|
|
1513
1523
|
send: ctx.fetch
|
|
1514
1524
|
}));
|
|
1515
|
-
splatesWorker.command("remove").description("stop this machine’s worker taking agents, letting its agents run to their end; resume it with `splates worker drain local --undo`").arg("id",
|
|
1525
|
+
splatesWorker.command("remove").description("stop this machine’s worker taking agents, letting its agents run to their end; resume it with `splates worker drain local --undo`").arg("id", z16.string(), {
|
|
1516
1526
|
description: "the worker, as `splates worker list` names it"
|
|
1517
|
-
}).option("force",
|
|
1527
|
+
}).option("force", z16.boolean(), {
|
|
1518
1528
|
default: false,
|
|
1519
1529
|
description: "end the agents on it now rather than wait for them"
|
|
1520
|
-
}).option("dev",
|
|
1530
|
+
}).option("dev", z16.string(), dev).action((opts, ctx) => removeWorker({
|
|
1521
1531
|
id: opts.id,
|
|
1522
1532
|
force: opts.force,
|
|
1523
1533
|
dev: opts.dev,
|
|
@@ -1549,15 +1559,15 @@ function projectArgs(argv) {
|
|
|
1549
1559
|
}
|
|
1550
1560
|
return { argv };
|
|
1551
1561
|
}
|
|
1552
|
-
var splates =
|
|
1553
|
-
splates.command("init").use(
|
|
1562
|
+
var splates = cli16("splates").use(exits());
|
|
1563
|
+
splates.command("init").use(repository11({ creates: true, needs: "own" })).description("make this repository a splates project: set up its records under .git/splates when it has none, install the splates skill of this release with `bunx skills add`, and register it in ~/.splates/projects.json, so `splates server` serves it beside every other; run again, it changes only what is not so already").action((_opts, ctx) => init(ctx));
|
|
1554
1564
|
splates.command("migrate").description(`move this repository's records under .git/splates to this splates's format, as \`splates server\` does as it starts: backed up first, the steps run with ${MIGRATION_LIMIT.human()} to finish, and the backup put back if one fails or runs over, with why kept in .git/splates-migration.json; it tries again past a failure kept there, which the server and every other command leave alone. Run it only while no older \`splates server\` serves the repository, which would refuse the records once moved; \`splates update\` restarts one first`).action(async (_opts, ctx) => {
|
|
1555
1565
|
const format = await migrateRecords(ctx);
|
|
1556
1566
|
if (ctx.ps.env(MIGRATING) === undefined) {
|
|
1557
1567
|
ctx.log.info(`splates's records are in format ${format}`);
|
|
1558
1568
|
}
|
|
1559
1569
|
});
|
|
1560
|
-
splates.command("update").use(recordsFirst()).use(
|
|
1570
|
+
splates.command("update").use(recordsFirst()).use(repository11({ needs: "own" })).description("bring this repository up to the newest splates on npm: run `update` on that release when it is newer than this one, which moves the records under .git/splates to its format, as `splates migrate` does, unless an older `splates server` serves the repository, which it asks to restart onto that release once its agents have checkpointed, migrating them as it starts; then installs its splates skill with `bunx skills add` over the copy there, leaving one that links to your own; commit what changed").action(async (_opts, ctx) => {
|
|
1561
1571
|
const code = await update({
|
|
1562
1572
|
root: ctx.git.root,
|
|
1563
1573
|
fs: ctx.fs,
|
|
@@ -1570,31 +1580,31 @@ splates.command("update").use(recordsFirst()).use(repository10({ needs: "own" })
|
|
|
1570
1580
|
}
|
|
1571
1581
|
});
|
|
1572
1582
|
splates.command("refresh").description("bring the tree you stand in, a task's worktree included, up to this splates: install its splates skill with `bunx skills add` over the copy there and move what an older config holds to where it reads it, leaving the records and any `splates server` alone; what a project runs once a ship's bump task has installed a newer splates, as splates's `afterBump` names; commit what changed").action((_opts, ctx) => refreshHere(ctx));
|
|
1573
|
-
splates.command("forget").use(
|
|
1574
|
-
splates.command("add").use(
|
|
1583
|
+
splates.command("forget").use(repository11({ needs: "own" })).description("take this repository off ~/.splates/projects.json, so `splates server` no longer offers it; its records stay, and `splates init` brings it back").action((_opts, ctx) => forget(ctx));
|
|
1584
|
+
splates.command("add").use(repository11({ needs: "tasks" })).description("start a task: a new one, or with --task one from the queue; create branch task/<slug>, a worktree at ../<repo>-splates.noindex/<slug> and a state record").arg("slug", z17.string(), {
|
|
1575
1585
|
description: "the task's name, for its branch and worktree (lowercase-with-dashes)"
|
|
1576
|
-
}).option("base",
|
|
1586
|
+
}).option("base", z17.string(), {
|
|
1577
1587
|
default: "",
|
|
1578
1588
|
description: "branch this task starts from and merges onto (default: trunk); `task/<other>` stacks this task on that one and lands the work in its worktree"
|
|
1579
|
-
}).option("task",
|
|
1589
|
+
}).option("task", z17.string(), {
|
|
1580
1590
|
default: "",
|
|
1581
1591
|
description: "start this queued task by number: its words become the plan's Goal, merging removes it, and removing this task puts it back where it was"
|
|
1582
|
-
}).option("goal",
|
|
1592
|
+
}).option("goal", z17.string(), {
|
|
1583
1593
|
default: "",
|
|
1584
1594
|
description: "one or two lines on what done means, written under `## Goal` in the task's SPLATES.md; required unless --task seeds the Goal"
|
|
1585
|
-
}).option("agent",
|
|
1595
|
+
}).option("agent", z17.string(), {
|
|
1586
1596
|
default: "",
|
|
1587
1597
|
description: "how `splates server`'s queue hands the task to an agent on the roster (`splates agents`), which it starts in the worktree; refused from an agent, which starts no agents and queues work with `splates task add` instead"
|
|
1588
|
-
}).option("model",
|
|
1598
|
+
}).option("model", z17.string(), {
|
|
1589
1599
|
default: "",
|
|
1590
1600
|
description: "with --agent: the model it works on, by its id or name on the roster (default: the agent's own)"
|
|
1591
|
-
}).option("effort",
|
|
1601
|
+
}).option("effort", z17.string(), {
|
|
1592
1602
|
default: "",
|
|
1593
1603
|
description: "with --agent: the thinking level it works at, one the agent offers (default: the agent's own)"
|
|
1594
|
-
}).option("prompt",
|
|
1604
|
+
}).option("prompt", z17.string(), {
|
|
1595
1605
|
default: "",
|
|
1596
1606
|
description: "with --agent: what to tell the agent beyond claiming the task"
|
|
1597
|
-
}).option("held",
|
|
1607
|
+
}).option("held", z17.string(), {
|
|
1598
1608
|
default: "",
|
|
1599
1609
|
description: "with --agent: the conversation `splates server`'s queue held for the queued task, which takes the task rather than a new one"
|
|
1600
1610
|
}).action(async (opts, ctx) => {
|
|
@@ -1639,11 +1649,11 @@ splates.command("add").use(repository10({ needs: "tasks" })).description("start
|
|
|
1639
1649
|
await serveProject(ctx);
|
|
1640
1650
|
}
|
|
1641
1651
|
});
|
|
1642
|
-
splates.command("claim").use(
|
|
1652
|
+
splates.command("claim").use(repository11({ needs: "own" })).description("resume an existing task: take ownership of its worktree and print its path, status and any half-finished rebase; refuses while another agent holds the lease, but takes a stale one silently, so `splates show` first if the tree may not be abandoned").arg("task", z17.string(), { description: "task name" }).action(async (opts, ctx) => {
|
|
1643
1653
|
await ctx.journal.record("claim", opts.task, () => claim(ctx, opts.task), (claimed) => claimed);
|
|
1644
1654
|
await follow(ctx, opts.task);
|
|
1645
1655
|
});
|
|
1646
|
-
splates.command("merge").use(
|
|
1656
|
+
splates.command("merge").use(repository11({ needs: "own" })).description("land this worktree's branch on its base (trunk unless created with --base): rebase onto it, run tests on the rebased code, fast-forward it in whichever worktree has it checked out, then discard the worktree, branch and record (linear history, never a merge commit, no flag to skip tests); requires committed work, refusing a dirty worktree").option("message", z17.string(), {
|
|
1647
1657
|
default: "",
|
|
1648
1658
|
description: "squash the task's commits into one with this message before landing, saying what the base gains in at least 5 characters"
|
|
1649
1659
|
}).action(async (opts, ctx) => {
|
|
@@ -1658,21 +1668,21 @@ splates.command("merge").use(repository10({ needs: "own" })).description("land t
|
|
|
1658
1668
|
await follow(ctx, null, { from: task });
|
|
1659
1669
|
}
|
|
1660
1670
|
});
|
|
1661
|
-
splates.command("remove").use(
|
|
1671
|
+
splates.command("remove").use(repository11({ needs: "tasks" })).description("discard a task: worktree, branch and board tasks; cheap and encouraged, since redoing a task against current trunk often beats a hard rebase").arg("task", z17.string(), { description: "task name" }).option("force", z17.boolean(), {
|
|
1662
1672
|
default: false,
|
|
1663
1673
|
description: "discard even when another agent holds the lease"
|
|
1664
|
-
}).option("reason",
|
|
1674
|
+
}).option("reason", z17.optional(z17.enum(REMOVE_REASONS)), {
|
|
1665
1675
|
description: "why, for the Stats page: nothing (nothing to change), moot (no longer needed), superseded (done some other way), redo (better redone against current trunk), blocked (set aside until what it waits on lands, so back on the list, which takes --why and at least one blocker)"
|
|
1666
|
-
}).option("why",
|
|
1676
|
+
}).option("why", z17.string(), {
|
|
1667
1677
|
default: "",
|
|
1668
1678
|
description: "with --reason blocked: what you found that stops the work, in a sentence or two, for the person and whoever takes it next"
|
|
1669
|
-
}).repeated("blocked-by",
|
|
1679
|
+
}).repeated("blocked-by", z17.string(), {
|
|
1670
1680
|
description: "with --reason blocked: the tasks it waits on to merge, comma separated or the flag repeated: a number for one here, <project>#<number> for one of another project"
|
|
1671
|
-
}).repeated("ask",
|
|
1681
|
+
}).repeated("ask", z17.string(), {
|
|
1672
1682
|
description: "with --reason blocked: a question only the person can answer, in the skill's question format; repeat the flag for each"
|
|
1673
|
-
}).repeated("waiting-on",
|
|
1683
|
+
}).repeated("waiting-on", z17.string(), {
|
|
1674
1684
|
description: "with --reason blocked: something outside splates it waits on, such as a key or an upstream release; repeat the flag for each"
|
|
1675
|
-
}).option("detail",
|
|
1685
|
+
}).option("detail", z17.string(), {
|
|
1676
1686
|
default: "",
|
|
1677
1687
|
description: "with --reason blocked: the task's detail rewritten for whoever takes it next: why it stopped, what you found, the steps done and left, and where any pushed work is"
|
|
1678
1688
|
}).action(async (opts, ctx) => {
|
|
@@ -1703,30 +1713,30 @@ splates.command("remove").use(repository10({ needs: "tasks" })).description("dis
|
|
|
1703
1713
|
__callDispose(__stack, _err, _hasErr);
|
|
1704
1714
|
}
|
|
1705
1715
|
});
|
|
1706
|
-
splates.command("list").use(
|
|
1716
|
+
splates.command("list").use(repository11({ needs: "read" })).description("list every started task: its number, name, status, lease (held: an agent is on it now; stale: gone quiet, normal for a task mid-edit; none), commits ahead of trunk, age; `splates task list` lists the queue").option("json", z17.boolean(), { default: false, description: "emit JSON" }).option("files", z17.boolean(), {
|
|
1707
1717
|
default: false,
|
|
1708
1718
|
description: "add each task's Goal and changed files (committed or not), to check for overlap before starting"
|
|
1709
1719
|
}).action((opts, ctx) => list(ctx, { json: opts.json, files: opts.files }));
|
|
1710
|
-
splates.command("show").use(
|
|
1711
|
-
splates.command("wait").use(
|
|
1720
|
+
splates.command("show").use(repository11({ needs: "read" })).description("read one task without touching it: everything `list` shows for it, plus the SPLATES.md its agent left at the worktree root; takes no lease, so it cannot knock that agent off its own tree; for a task no longer live, the newest finished one of that name: how it ended, its base, the commit it landed as, its conversation and the SPLATES.md it ended on").arg("task", z17.string(), { description: "task name" }).option("json", z17.boolean(), { default: false, description: "emit JSON" }).action((opts, ctx) => show(ctx, opts.task, { json: opts.json }));
|
|
1721
|
+
splates.command("wait").use(repository11({ needs: "read" })).description("block until a task stops moving: waiting on a person to answer its questions, or gone (merged or removed), or broken; takes no lease, and gives up with `timeout` rather than waiting forever").arg("task", z17.string(), { description: "task name" }).option("timeout-secs", z17.coerce.number(), {
|
|
1712
1722
|
default: DEFAULT_TIMEOUT3.secs,
|
|
1713
1723
|
description: "how long to wait before giving up"
|
|
1714
1724
|
}).action((opts, ctx) => {
|
|
1715
1725
|
const timeout = Duration3.secs(opts["timeout-secs"]);
|
|
1716
1726
|
return ctx.journal.waiting("wait", opts.task, { until: untilAfter3(timeout) }, () => wait(ctx, opts.task, { timeout }));
|
|
1717
1727
|
});
|
|
1718
|
-
splates.command("log").use(
|
|
1728
|
+
splates.command("log").use(repository11({ needs: "read" })).description("show what has been done here recently: one line per add, claim, merge, remove, retry, queued task and more, with how it ended; outlives the tasks themselves").option("count", z17.coerce.number(), {
|
|
1719
1729
|
default: DEFAULT_COUNT,
|
|
1720
1730
|
description: "how many entries to show"
|
|
1721
|
-
}).option("json",
|
|
1731
|
+
}).option("json", z17.boolean(), { default: false, description: "emit JSON" }).action((opts, ctx) => showLog(ctx, { count: opts.count, json: opts.json }));
|
|
1722
1732
|
splates.mount("server", splatesServer);
|
|
1723
|
-
splates.command("ports").description("list the ports tasks serve, found by the running `splates server` as an editor finds ports to forward: each port a process in a task's worktree, or under its agent, listens on, with its link through the ports host `splates server --ports-host` saved, behind Cloudflare Access, and its link on this machine; give the user both").arg("task",
|
|
1733
|
+
splates.command("ports").description("list the ports tasks serve, found by the running `splates server` as an editor finds ports to forward: each port a process in a task's worktree, or under its agent, listens on, with its link through the ports host `splates server --ports-host` saved, behind Cloudflare Access, and its link on this machine; give the user both").arg("task", z17.string(), {
|
|
1724
1734
|
default: "",
|
|
1725
1735
|
description: "only this task's ports; every task's by default"
|
|
1726
|
-
}).option("dev",
|
|
1736
|
+
}).option("dev", z17.string(), {
|
|
1727
1737
|
default: "",
|
|
1728
1738
|
description: "the `splates server` to ask, a URL such as http://localhost:4310; by default the one an agent runs under, from SPLATES_DEV, else this machine's on its default port"
|
|
1729
|
-
}).option("json",
|
|
1739
|
+
}).option("json", z17.boolean(), { default: false, description: "emit JSON" }).action((opts, ctx) => portsLs({
|
|
1730
1740
|
task: opts.task,
|
|
1731
1741
|
dev: opts.dev,
|
|
1732
1742
|
json: opts.json,
|
|
@@ -1734,41 +1744,41 @@ splates.command("ports").description("list the ports tasks serve, found by the r
|
|
|
1734
1744
|
ps: ctx.ps,
|
|
1735
1745
|
send: ctx.fetch
|
|
1736
1746
|
}));
|
|
1737
|
-
splates.command("agents").description("print the roster of agents splates can start, kept for every project in ~/.splates/agents.json: each agent's launch command and models, with what each model is good at and what it costs, and the thinking levels `--effort` takes, as the agent offered them when last started, for choosing whom to delegate to; writes the defaults (Claude, Codex) there first when there is none, and `splates server` starts conversations with those it lists").option("json",
|
|
1747
|
+
splates.command("agents").description("print the roster of agents splates can start, kept for every project in ~/.splates/agents.json: each agent's launch command and models, with what each model is good at and what it costs, and the thinking levels `--effort` takes, as the agent offered them when last started, for choosing whom to delegate to; writes the defaults (Claude, Codex) there first when there is none, and `splates server` starts conversations with those it lists").option("json", z17.boolean(), { default: false, description: "emit JSON" }).action(async (opts, ctx) => agents({
|
|
1738
1748
|
roster: rosterOf2(ctx),
|
|
1739
1749
|
offered: await offeredOf(ctx).all(),
|
|
1740
1750
|
log: ctx.log
|
|
1741
1751
|
}, { json: opts.json }));
|
|
1742
|
-
splates.command("setup").description("set splates up on this machine, one step at a time: with no step, each area's steps, checked from real state, and the one to do next; with a step, why it matters, the one thing the user does and what the agent does. Skips are kept in ~/.splates/setup.json, so a skipped step stops asking").arg("step",
|
|
1752
|
+
splates.command("setup").description("set splates up on this machine, one step at a time: with no step, each area's steps, checked from real state, and the one to do next; with a step, why it matters, the one thing the user does and what the agent does. Skips are kept in ~/.splates/setup.json, so a skipped step stops asking").arg("step", z17.string(), {
|
|
1743
1753
|
default: "",
|
|
1744
1754
|
description: "a step's id; omit for every area's progress"
|
|
1745
|
-
}).option("json",
|
|
1755
|
+
}).option("json", z17.boolean(), { default: false, description: "emit JSON" }).option("skip", z17.boolean(), {
|
|
1746
1756
|
default: false,
|
|
1747
1757
|
description: "with a step: stop asking for it"
|
|
1748
|
-
}).option("unskip",
|
|
1758
|
+
}).option("unskip", z17.boolean(), {
|
|
1749
1759
|
default: false,
|
|
1750
1760
|
description: "with a step: ask for it again"
|
|
1751
|
-
}).option("undo",
|
|
1761
|
+
}).option("undo", z17.boolean(), {
|
|
1752
1762
|
default: false,
|
|
1753
1763
|
description: "with an area's id in place of a step: print what undoing it would remove, the creds it forgets and keeps, and what only the user can remove; the Setup page's Undo does it, after the user confirms"
|
|
1754
1764
|
}).action((opts, ctx) => setup(ctx, opts));
|
|
1755
|
-
splates.command("path").use(
|
|
1765
|
+
splates.command("path").use(repository11({ needs: "read" })).description("print a task's worktree path, or the main tree with no task; names another agent's tree without taking its lease").arg("task", z17.string(), {
|
|
1756
1766
|
default: "",
|
|
1757
1767
|
description: "task name; omit for the main tree"
|
|
1758
1768
|
}).action((opts, ctx) => path(ctx, opts.task || undefined));
|
|
1759
|
-
splates.command("checkpoint").use(
|
|
1769
|
+
splates.command("checkpoint").use(repository11({ needs: "own" })).description("save this task to the central remote (`remote` in splates.config.ts) before a stop: commit what is uncommitted as a `WIP: checkpoint` commit, to squash before merging, and push the branch as `splates/<task>`, so a worker that dies loses nothing; with no remote configured, leaves the tree as it is").action(async (_opts, ctx) => {
|
|
1760
1770
|
await checkpoint(ctx, ctx.ps.cwd());
|
|
1761
1771
|
});
|
|
1762
|
-
splates.command("attach").use(
|
|
1772
|
+
splates.command("attach").use(repository11({ needs: "own" })).description("store screenshots and recordings for this task's questions to show: prints the path each was stored at, under .git/splates, which the `splates server` page shows inline; from a conversation (its $SPLATES_CONVERSATION, or --conversation) they stay with it past the task, otherwise they go when the task merges or is removed; those the task's final SPLATES.md shows are copied to keep with its finished record. A task holds at most attachmentCapacity bytes; with no files, lists what it holds. A file the user will come back for on its own is an artifact instead (`splates artifact add`)").arg("files", z17.string(), {
|
|
1763
1773
|
default: "",
|
|
1764
1774
|
description: "files to store, comma separated"
|
|
1765
|
-
}).option("task",
|
|
1775
|
+
}).option("task", z17.string(), {
|
|
1766
1776
|
default: "",
|
|
1767
1777
|
description: "task name, when run outside its worktree"
|
|
1768
|
-
}).option("remove",
|
|
1778
|
+
}).option("remove", z17.string(), {
|
|
1769
1779
|
default: "",
|
|
1770
1780
|
description: "files it holds to drop first, comma separated, by path or stored name"
|
|
1771
|
-
}).option("conversation",
|
|
1781
|
+
}).option("conversation", z17.string(), {
|
|
1772
1782
|
default: "",
|
|
1773
1783
|
description: "conversation to keep the files with, when $SPLATES_CONVERSATION is not set"
|
|
1774
1784
|
}).action(async (opts, ctx) => ctx.journal.record("attach", opts.task || await here(ctx), () => attach(ctx, commaList6(opts.files), ctx.ps.cwd(), {
|
|
@@ -1776,38 +1786,8 @@ splates.command("attach").use(repository10({ needs: "own" })).description("store
|
|
|
1776
1786
|
remove: commaList6(opts.remove),
|
|
1777
1787
|
conversation: opts.conversation || undefined
|
|
1778
1788
|
}), (stored) => ({ stored, removed: commaList6(opts.remove) })));
|
|
1779
|
-
splates.command("retry").use(
|
|
1780
|
-
splates.
|
|
1781
|
-
default: "",
|
|
1782
|
-
description: "the registered project's slug; this one by default"
|
|
1783
|
-
}).option("plan", z16.boolean(), {
|
|
1784
|
-
default: false,
|
|
1785
|
-
description: "print the plan without running anything"
|
|
1786
|
-
}).option("json", z16.boolean(), { default: false, description: "emit JSON" }).option("dev", z16.string(), {
|
|
1787
|
-
default: "",
|
|
1788
|
-
description: "the `splates server` to run the ship on, a URL such as http://localhost:4310; by default the one from SPLATES_DEV, else this machine's on its default port"
|
|
1789
|
-
}).option("bump", z16.optional(z16.enum(["patch", "minor", "major"])), {
|
|
1790
|
-
description: "how far to move the starting project's version, patch by default; while its cascade runs, change this pending publish instead"
|
|
1791
|
-
}).option("drop", z16.string(), {
|
|
1792
|
-
default: "",
|
|
1793
|
-
description: "take this registered project out of the cascade under way, so those after it go on without its new version"
|
|
1794
|
-
}).option("retry", z16.string(), {
|
|
1795
|
-
default: "",
|
|
1796
|
-
description: "run this registered project's part of the cascade under way again: its ship again when that failed, or a new bump task when its last was removed"
|
|
1797
|
-
}).option("abandon", z16.boolean(), {
|
|
1798
|
-
default: false,
|
|
1799
|
-
description: "end the cascade under way: what shipped stays published, a running ship is stopped, its open bump tasks are removed, and history files it as abandoned"
|
|
1800
|
-
}).action((opts, ctx) => ship(ctx, {
|
|
1801
|
-
project: opts.project || undefined,
|
|
1802
|
-
plan: opts.plan,
|
|
1803
|
-
json: opts.json,
|
|
1804
|
-
dev: opts.dev,
|
|
1805
|
-
bump: opts.bump,
|
|
1806
|
-
drop: opts.drop || undefined,
|
|
1807
|
-
retry: opts.retry || undefined,
|
|
1808
|
-
abandon: opts.abandon
|
|
1809
|
-
}));
|
|
1810
|
-
splates.mount("artifact", splatesArtifact).mount("task", splatesTask).mount("lane", splatesLane).mount("projects", splatesProjects).mount("creds", splatesCreds).mount("tunnel", splatesTunnel).mount("slots", splatesSlots).mount("worker", splatesWorker).mount("queue", splatesQueue).mount("hook", splatesHook).mount("replica", splatesReplica).mount("evals", splatesEvals);
|
|
1789
|
+
splates.command("retry").use(repository11({ needs: "own" })).description("give a task that spent its merge attempts another mergeRetryCount of them; the way out of `budget_exhausted` that is not remove and redo, for a person to run once they have seen the tree").arg("task", z17.string(), { description: "task name" }).action((opts, ctx) => recordRetry(ctx, opts.task));
|
|
1790
|
+
splates.mount("artifact", splatesArtifact).mount("task", splatesTask).mount("lane", splatesLane).mount("ship", splatesShip).mount("projects", splatesProjects).mount("creds", splatesCreds).mount("tunnel", splatesTunnel).mount("slots", splatesSlots).mount("worker", splatesWorker).mount("queue", splatesQueue).mount("hook", splatesHook).mount("replica", splatesReplica).mount("evals", splatesEvals);
|
|
1811
1791
|
|
|
1812
1792
|
// index.ts
|
|
1813
1793
|
if (basename2(process.argv[1] ?? "") === LEGACY) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stringsync/splates",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.61",
|
|
4
4
|
"description": "Runs several AI coding agents on one repository at once, each in its own git worktree",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "stringsync LLC",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@stringsync/splates-server": "^0.0.
|
|
21
|
-
"webappwiz": "0.0.
|
|
20
|
+
"@stringsync/splates-server": "^0.0.61",
|
|
21
|
+
"webappwiz": "0.0.58",
|
|
22
22
|
"zod": "^4.4.3"
|
|
23
23
|
},
|
|
24
24
|
"main": "./index.js",
|
package/ship.d.ts
ADDED