@stringsync/splates 0.0.58 → 0.0.59
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 +10 -0
- package/blockers.d.ts +32 -0
- package/index.js +315 -86
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -281,6 +281,16 @@ better or worse, and say what to change when one stands out. Fixed rules over
|
|
|
281
281
|
the counts make those calls, not a model, so the same outcomes always read
|
|
282
282
|
the same.
|
|
283
283
|
|
|
284
|
+
### Kimi Code
|
|
285
|
+
|
|
286
|
+
Kimi Code is on the default roster beside Claude and Codex, started as
|
|
287
|
+
`kimi acp`, Kimi's own ACP server. Its installer leaves `kimi` off the PATH,
|
|
288
|
+
so splates runs `KIMI_PATH` when set, else `kimi` on the PATH, else
|
|
289
|
+
`~/.kimi-code/bin/kimi`. A roster written before Kimi joined gains it once,
|
|
290
|
+
when the server starts on a machine with Kimi installed; take it off and it
|
|
291
|
+
stays off. Its models go by their full ids, such as `kimi-code/k3.1-pro`.
|
|
292
|
+
Sign in first with `kimi login`: splates never signs in for it.
|
|
293
|
+
|
|
284
294
|
### Optional OpenCode ACP agent
|
|
285
295
|
|
|
286
296
|
Open **Settings → Setup** and visit the optional OpenCode steps. Check this
|
package/blockers.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ElsewhereBlocker, ElsewhereBoards } from "@stringsync/splates-server/board";
|
|
2
|
+
/**
|
|
3
|
+
* Reads `--blocked-by` and `--remove-blocked-by` in the project at `here`:
|
|
4
|
+
* a number names a task here, `webappwiz#404` or `webappwiz #404` one of
|
|
5
|
+
* another project, as `boards` finds it, and this project's own slug a task
|
|
6
|
+
* here. A task of another project that `--blocked-by` names has to be open
|
|
7
|
+
* there; one `--remove-blocked-by` names need not.
|
|
8
|
+
*/
|
|
9
|
+
export declare function blockerFlags({ boards, here, blockedBy, removeBlockedBy, }: BlockerFlagsOptions): Promise<BlockerFlags>;
|
|
10
|
+
/** What `blockerFlags` reads, and where. */
|
|
11
|
+
export interface BlockerFlagsOptions {
|
|
12
|
+
boards: ElsewhereBoards;
|
|
13
|
+
/** This project's main tree. */
|
|
14
|
+
here: string;
|
|
15
|
+
/** `--blocked-by`, as given. */
|
|
16
|
+
blockedBy?: readonly string[];
|
|
17
|
+
/** `--remove-blocked-by`, as given. */
|
|
18
|
+
removeBlockedBy?: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
/** The tasks the flags name, here and in other projects. */
|
|
21
|
+
export interface BlockerFlags {
|
|
22
|
+
blockedBy: number[];
|
|
23
|
+
blockedElsewhere: ElsewhereBlocker[];
|
|
24
|
+
/** Those no longer to block it. */
|
|
25
|
+
unblocked: {
|
|
26
|
+
ids: number[];
|
|
27
|
+
elsewhere: {
|
|
28
|
+
project: string;
|
|
29
|
+
id: number;
|
|
30
|
+
}[];
|
|
31
|
+
};
|
|
32
|
+
}
|
package/index.js
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
var __dispose = Symbol.dispose || /* @__PURE__ */ Symbol.for("Symbol.dispose");
|
|
4
|
+
var __asyncDispose = Symbol.asyncDispose || /* @__PURE__ */ Symbol.for("Symbol.asyncDispose");
|
|
5
|
+
var __using = (stack, value, async) => {
|
|
6
|
+
if (value != null) {
|
|
7
|
+
if (typeof value !== "object" && typeof value !== "function")
|
|
8
|
+
throw TypeError('Object expected to be assigned to "using" declaration');
|
|
9
|
+
var dispose;
|
|
10
|
+
if (async)
|
|
11
|
+
dispose = value[__asyncDispose];
|
|
12
|
+
if (dispose === undefined)
|
|
13
|
+
dispose = value[__dispose];
|
|
14
|
+
if (typeof dispose !== "function")
|
|
15
|
+
throw TypeError("Object not disposable");
|
|
16
|
+
stack.push([async, dispose, value]);
|
|
17
|
+
} else if (async) {
|
|
18
|
+
stack.push([async]);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
};
|
|
22
|
+
var __callDispose = (stack, error, hasError) => {
|
|
23
|
+
var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
|
|
24
|
+
return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
|
|
25
|
+
}, fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e), next = (it) => {
|
|
26
|
+
while (it = stack.pop()) {
|
|
27
|
+
try {
|
|
28
|
+
var result = it[1] && it[1].call(it[2]);
|
|
29
|
+
if (it[0])
|
|
30
|
+
return Promise.resolve(result).then(next, (e) => (fail(e), next()));
|
|
31
|
+
} catch (e) {
|
|
32
|
+
fail(e);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (hasError)
|
|
36
|
+
throw error;
|
|
37
|
+
};
|
|
38
|
+
return next();
|
|
39
|
+
};
|
|
3
40
|
|
|
4
41
|
// index.ts
|
|
5
42
|
import { basename as basename2 } from "path";
|
|
@@ -48,9 +85,9 @@ async function handOffDev(argv, cwd) {
|
|
|
48
85
|
// splates.ts
|
|
49
86
|
import { agents } from "@stringsync/splates-server/agents";
|
|
50
87
|
import { attach } from "@stringsync/splates-server/artifacts";
|
|
51
|
-
import { commaList as
|
|
88
|
+
import { commaList as commaList6 } from "@stringsync/splates-server/comma-list";
|
|
52
89
|
import { refuseGroomer as refuseGroomer4 } from "@stringsync/splates-server/conversations";
|
|
53
|
-
import { exits, fail as
|
|
90
|
+
import { exits, fail as fail10 } from "@stringsync/splates-server/exit";
|
|
54
91
|
import { DEFAULT_COUNT, log as showLog } from "@stringsync/splates-server/log";
|
|
55
92
|
import { merge } from "@stringsync/splates-server/merge";
|
|
56
93
|
import { portsLs } from "@stringsync/splates-server/ports";
|
|
@@ -59,15 +96,18 @@ import {
|
|
|
59
96
|
MIGRATING,
|
|
60
97
|
MIGRATION_LIMIT,
|
|
61
98
|
migrateRecords,
|
|
99
|
+
OpenBoards as OpenBoards2,
|
|
62
100
|
repository as repository10
|
|
63
101
|
} from "@stringsync/splates-server/records";
|
|
64
102
|
import {
|
|
65
103
|
newest,
|
|
66
104
|
recordsFirst,
|
|
105
|
+
refreshHere,
|
|
67
106
|
rosterOf as rosterOf2,
|
|
68
107
|
seeded as seeded2,
|
|
69
108
|
update
|
|
70
109
|
} from "@stringsync/splates-server/server";
|
|
110
|
+
import { ship } from "@stringsync/splates-server/ship";
|
|
71
111
|
import { addFactTasks } from "@stringsync/splates-server/stats";
|
|
72
112
|
import {
|
|
73
113
|
add,
|
|
@@ -244,9 +284,87 @@ splatesArtifact.command("remove").use(repository({ needs: "own" })).description(
|
|
|
244
284
|
splatesArtifact.command("list").use(repository({ needs: "own" })).description("the artifacts this conversation lists, and their current version").option(...conversation).action(async (opts, ctx) => listArtifacts(ctx, await conversationOf(ctx.service, opts.conversation)));
|
|
245
285
|
splatesArtifact.command("show").use(repository({ needs: "own" })).description("every version of an artifact, and which is current").arg("name", z2.string(), { description: "the artifact's name" }).option(...conversation).action(async (opts, ctx) => listArtifacts(ctx, await conversationOf(ctx.service, opts.conversation), opts.name));
|
|
246
286
|
|
|
287
|
+
// blockers.ts
|
|
288
|
+
import { commaList as commaList3 } from "@stringsync/splates-server/comma-list";
|
|
289
|
+
import { fail as fail3 } from "@stringsync/splates-server/exit";
|
|
290
|
+
async function blockerFlags({
|
|
291
|
+
boards,
|
|
292
|
+
here,
|
|
293
|
+
blockedBy = [],
|
|
294
|
+
removeBlockedBy = []
|
|
295
|
+
}) {
|
|
296
|
+
const ids = [];
|
|
297
|
+
const blockedElsewhere = [];
|
|
298
|
+
for (const named of await namedIn(boards, here, blockedBy)) {
|
|
299
|
+
if (named.project === null) {
|
|
300
|
+
ids.push(named.id);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const found = await boards.find(named.project, named.id);
|
|
304
|
+
if (found === null) {
|
|
305
|
+
fail3("not_found", `the tasks of ${named.written} cannot be read: is ${named.slug} set up with \`splates init\`?`, { blockedBy: named.written });
|
|
306
|
+
}
|
|
307
|
+
if (found.status === "merged" || found.status === "removed") {
|
|
308
|
+
fail3("not_found", `${named.written} is ${found.status === "merged" ? `merged, as ${found.commit}` : "gone, removed without merging"}: nothing waits on it any more`, { blockedBy: named.written });
|
|
309
|
+
}
|
|
310
|
+
blockedElsewhere.push({
|
|
311
|
+
project: named.project,
|
|
312
|
+
id: named.id,
|
|
313
|
+
subject: found.subject
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
const unblocked = { ids: [], elsewhere: [] };
|
|
317
|
+
for (const named of await namedIn(boards, here, removeBlockedBy)) {
|
|
318
|
+
if (named.project === null) {
|
|
319
|
+
unblocked.ids.push(named.id);
|
|
320
|
+
} else {
|
|
321
|
+
unblocked.elsewhere.push({ project: named.project, id: named.id });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return { blockedBy: ids, blockedElsewhere, unblocked };
|
|
325
|
+
}
|
|
326
|
+
function id(digits) {
|
|
327
|
+
const parsed = Number(digits);
|
|
328
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
329
|
+
fail3("usage", `'${digits}' is not a task number`, { number: digits });
|
|
330
|
+
}
|
|
331
|
+
return parsed;
|
|
332
|
+
}
|
|
333
|
+
async function namedIn(boards, here, raw) {
|
|
334
|
+
const named = [];
|
|
335
|
+
for (const value of commaList3(raw.join(","))) {
|
|
336
|
+
const local = /^#?(\d+)$/.exec(value);
|
|
337
|
+
if (local !== null) {
|
|
338
|
+
named.push({
|
|
339
|
+
project: null,
|
|
340
|
+
slug: "",
|
|
341
|
+
id: id(local[1]),
|
|
342
|
+
written: value
|
|
343
|
+
});
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const other = /^#?([\w.-]+)(?:\s+task)?\s*#(\d+)$/i.exec(value);
|
|
347
|
+
if (other === null) {
|
|
348
|
+
fail3("usage", `'${value}' is not a task: pass its number, or <project>#<number> for one of another project`, { blockedBy: value });
|
|
349
|
+
}
|
|
350
|
+
const slug = other[1];
|
|
351
|
+
const root = await boards.root(slug);
|
|
352
|
+
if (root === null) {
|
|
353
|
+
fail3("not_found", `no splates project '${slug}' on this machine: \`splates projects\` lists them`, { blockedBy: value });
|
|
354
|
+
}
|
|
355
|
+
named.push({
|
|
356
|
+
project: root === here ? null : root,
|
|
357
|
+
slug,
|
|
358
|
+
id: id(other[2]),
|
|
359
|
+
written: `${slug} #${other[2]}`
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return named;
|
|
363
|
+
}
|
|
364
|
+
|
|
247
365
|
// creds.ts
|
|
248
366
|
import { DEFAULT_TIMEOUT, PROVIDERS } from "@stringsync/splates-server/creds";
|
|
249
|
-
import { fail as
|
|
367
|
+
import { fail as fail4 } from "@stringsync/splates-server/exit";
|
|
250
368
|
import { repository as repository2 } from "@stringsync/splates-server/records";
|
|
251
369
|
import { untilAfter } from "@stringsync/splates-server/tasks";
|
|
252
370
|
import { cli as cli2 } from "webappwiz/cmd";
|
|
@@ -296,7 +414,7 @@ splatesCreds.command("wait").use(repository2({ needs: "own" })).description("blo
|
|
|
296
414
|
await ctx.journal.waiting("creds wait", null, { name: opts.name, until: untilAfter(timeout) }, async () => {
|
|
297
415
|
const ended = await ctx.creds.wait(opts.name, { timeout });
|
|
298
416
|
if (ended === "dismissed") {
|
|
299
|
-
|
|
417
|
+
fail4("dismissed", `${opts.name} was dismissed: carry on without it, or ask the person why`, { name: opts.name });
|
|
300
418
|
}
|
|
301
419
|
});
|
|
302
420
|
ctx.log.info(`${color.green("saved")} ${opts.name}`);
|
|
@@ -328,7 +446,7 @@ import {
|
|
|
328
446
|
inspectBatch,
|
|
329
447
|
writeImported
|
|
330
448
|
} from "@stringsync/splates-server/evals";
|
|
331
|
-
import { fail as
|
|
449
|
+
import { fail as fail5 } from "@stringsync/splates-server/exit";
|
|
332
450
|
import { cli as cli3 } from "webappwiz/cmd";
|
|
333
451
|
import { color as color2 } from "webappwiz/log";
|
|
334
452
|
import { z as z4 } from "zod";
|
|
@@ -344,7 +462,7 @@ splatesEvals.command("import").description("turn a framework's results into a sp
|
|
|
344
462
|
const file = resolve3(ctx.ps.cwd(), opts.file);
|
|
345
463
|
const dir = resolve3(ctx.ps.cwd(), opts["batch-dir"]);
|
|
346
464
|
if (!await ctx.fs.exists(file)) {
|
|
347
|
-
|
|
465
|
+
fail5("not_found", `no file at ${file}`, { file });
|
|
348
466
|
}
|
|
349
467
|
const imported = inspectBatch(await inspectLog(ctx, file), opts["pass-at"]);
|
|
350
468
|
await writeImported(dir, imported);
|
|
@@ -358,12 +476,12 @@ async function inspectLog(ctx, file) {
|
|
|
358
476
|
return log;
|
|
359
477
|
}
|
|
360
478
|
} catch {}
|
|
361
|
-
return
|
|
479
|
+
return fail5("usage", `${file} is no Inspect eval log`, { file });
|
|
362
480
|
}
|
|
363
481
|
async function dumped(ctx, file) {
|
|
364
|
-
const { exitCode, stdout, stderr } = await ctx.ps.spawnCapture(["inspect", "log", "dump", file]).catch(() =>
|
|
482
|
+
const { exitCode, stdout, stderr } = await ctx.ps.spawnCapture(["inspect", "log", "dump", file]).catch(() => fail5("usage", "`inspect` is not on PATH: activate the environment Inspect is installed in, or pass a log written with --log-format json"));
|
|
365
483
|
if (exitCode !== 0) {
|
|
366
|
-
|
|
484
|
+
fail5("usage", `inspect log dump ${file} failed: ${stderr.trim()}`, {
|
|
367
485
|
file
|
|
368
486
|
});
|
|
369
487
|
}
|
|
@@ -371,8 +489,8 @@ async function dumped(ctx, file) {
|
|
|
371
489
|
}
|
|
372
490
|
|
|
373
491
|
// hook.ts
|
|
374
|
-
import { commaList as
|
|
375
|
-
import { fail as
|
|
492
|
+
import { commaList as commaList4 } from "@stringsync/splates-server/comma-list";
|
|
493
|
+
import { fail as fail6 } from "@stringsync/splates-server/exit";
|
|
376
494
|
import {
|
|
377
495
|
HookStore,
|
|
378
496
|
hookAdd,
|
|
@@ -425,13 +543,13 @@ splatesHook.command("add").use(repository3({ needs: "tasks" })).description("run
|
|
|
425
543
|
let hook;
|
|
426
544
|
if (opts.run !== "") {
|
|
427
545
|
if (pick != null || opts.prompt !== "") {
|
|
428
|
-
|
|
546
|
+
fail6("usage", "--run goes alone: a shell command runs no agent", {});
|
|
429
547
|
}
|
|
430
548
|
hook = { kind: "run", command: opts.run };
|
|
431
549
|
} else if (pick == null) {
|
|
432
|
-
|
|
550
|
+
fail6("usage", "say what runs: --agent with --prompt, or --run", {});
|
|
433
551
|
} else if (opts.prompt === "") {
|
|
434
|
-
|
|
552
|
+
fail6("usage", "an agent hook needs --prompt: what to tell it", {});
|
|
435
553
|
} else {
|
|
436
554
|
hook = { kind: "agent", prompt: opts.prompt, ...pick };
|
|
437
555
|
}
|
|
@@ -471,7 +589,7 @@ splatesHook.command("edit").use(repository3({ needs: "tasks" })).description("ch
|
|
|
471
589
|
const { id, agent, model, effort, ...change } = opts;
|
|
472
590
|
const pick = await assigneeIn(ctx, agent, model, effort);
|
|
473
591
|
if (pick === null) {
|
|
474
|
-
|
|
592
|
+
fail6("usage", "an agent automation needs an agent: name one, or --run", {});
|
|
475
593
|
}
|
|
476
594
|
await hookEdit({ hooks: storeOf(ctx), log: ctx.log }, id, {
|
|
477
595
|
...change,
|
|
@@ -501,24 +619,24 @@ splatesHook.command("groomer").use(repository3({ needs: "tasks" })).description(
|
|
|
501
619
|
}).action(async (opts, ctx) => {
|
|
502
620
|
const pick = await assigneeIn(ctx, opts.agent, opts.model, opts.effort);
|
|
503
621
|
if (opts.off === (pick != null)) {
|
|
504
|
-
|
|
622
|
+
fail6("usage", "say how it runs with --agent, or turn it off with --off", {});
|
|
505
623
|
}
|
|
506
624
|
await hookGroomer({ hooks: storeOf(ctx), log: ctx.log }, pick ?? null);
|
|
507
625
|
});
|
|
508
626
|
function triggerOf(opts) {
|
|
509
627
|
const given = [opts.event, opts.every, opts.cron].filter((each) => each !== undefined && each !== "");
|
|
510
628
|
if (given.length !== 1) {
|
|
511
|
-
|
|
629
|
+
fail6("usage", "say what sets it off: a task event, --every or --cron, one of them", {});
|
|
512
630
|
}
|
|
513
631
|
if (opts.event === undefined || opts.event === "") {
|
|
514
632
|
if (opts.stage !== "" || opts.lane !== "") {
|
|
515
|
-
|
|
633
|
+
fail6("usage", "--stage and --lane narrow a task event, not a schedule", {});
|
|
516
634
|
}
|
|
517
635
|
return opts.every !== "" ? { every: opts.every } : { cron: opts.cron };
|
|
518
636
|
}
|
|
519
637
|
const filter = {};
|
|
520
638
|
if (opts.stage !== "") {
|
|
521
|
-
filter.stages = stagesNamed(
|
|
639
|
+
filter.stages = stagesNamed(commaList4(opts.stage));
|
|
522
640
|
}
|
|
523
641
|
if (opts.lane !== "") {
|
|
524
642
|
filter.lanes = lanesNamed(opts.lane);
|
|
@@ -672,7 +790,7 @@ for (const command of ["pause", "resume"]) {
|
|
|
672
790
|
import { dirname as dirname2 } from "node:path";
|
|
673
791
|
import { loadConfig } from "@stringsync/splates-server/config";
|
|
674
792
|
import { SYSTEM_STORES as SYSTEM_STORES2 } from "@stringsync/splates-server/creds";
|
|
675
|
-
import { fail as
|
|
793
|
+
import { fail as fail7 } from "@stringsync/splates-server/exit";
|
|
676
794
|
import { Projects } from "@stringsync/splates-server/projects";
|
|
677
795
|
import {
|
|
678
796
|
FORMAT,
|
|
@@ -714,7 +832,7 @@ splatesReplica.command("restore").description("with `splates server` stopped, re
|
|
|
714
832
|
]);
|
|
715
833
|
const config = await loadConfig(root, { fs: ctx.fs });
|
|
716
834
|
if (config.replica === null) {
|
|
717
|
-
|
|
835
|
+
fail7("usage", "no replica: name one as `replica` in splates.config.ts first");
|
|
718
836
|
}
|
|
719
837
|
const replica = openReplica(config.replica, {
|
|
720
838
|
root,
|
|
@@ -741,7 +859,7 @@ splatesReplica.command("restore").description("with `splates server` stopped, re
|
|
|
741
859
|
].join(`
|
|
742
860
|
`));
|
|
743
861
|
} catch (error) {
|
|
744
|
-
|
|
862
|
+
fail7("usage", error instanceof Error ? error.message : String(error));
|
|
745
863
|
}
|
|
746
864
|
});
|
|
747
865
|
splatesReplica.command("retire").use(repository7({ needs: "own" })).description("after the server moved elsewhere (stop it and check `splates replica status` says caught up first), leave this project's records aside as state.db.moved, so a splates run here finds no stale records and says where the server went").arg("to", z9.string(), {
|
|
@@ -762,7 +880,7 @@ async function mainTree(ctx) {
|
|
|
762
880
|
"--git-common-dir"
|
|
763
881
|
]);
|
|
764
882
|
if (exitCode !== 0) {
|
|
765
|
-
|
|
883
|
+
fail7("usage", `not a git repository: ${ctx.ps.cwd()}`);
|
|
766
884
|
}
|
|
767
885
|
return dirname2(stdout.trim());
|
|
768
886
|
}
|
|
@@ -807,7 +925,7 @@ splatesServer.command("run").description("serve every splates project on this ma
|
|
|
807
925
|
// setup.ts
|
|
808
926
|
import { dirname as dirname3 } from "node:path";
|
|
809
927
|
import { SYSTEM_STORES as SYSTEM_STORES3 } from "@stringsync/splates-server/creds";
|
|
810
|
-
import { fail as
|
|
928
|
+
import { fail as fail8 } from "@stringsync/splates-server/exit";
|
|
811
929
|
import { splatesHome as splatesHome2 } from "@stringsync/splates-server/models";
|
|
812
930
|
import { Projects as Projects2 } from "@stringsync/splates-server/projects";
|
|
813
931
|
import {
|
|
@@ -823,15 +941,15 @@ import { fetchWorkers } from "@stringsync/splates-server/workers";
|
|
|
823
941
|
import { color as color3 } from "webappwiz/log";
|
|
824
942
|
async function setup(deps, opts) {
|
|
825
943
|
if (opts.step === "" && (opts.skip || opts.unskip || opts.undo)) {
|
|
826
|
-
|
|
944
|
+
fail8("usage", "--skip and --unskip name a step, --undo an area", {});
|
|
827
945
|
}
|
|
828
946
|
if (opts.undo && (opts.skip || opts.unskip)) {
|
|
829
|
-
|
|
947
|
+
fail8("usage", "--undo names an area, not a step to skip", {
|
|
830
948
|
area: opts.step
|
|
831
949
|
});
|
|
832
950
|
}
|
|
833
951
|
if (opts.skip && opts.unskip) {
|
|
834
|
-
|
|
952
|
+
fail8("usage", "--skip or --unskip, not both", { step: opts.step });
|
|
835
953
|
}
|
|
836
954
|
const machine = await setupOf(deps);
|
|
837
955
|
const { log } = deps;
|
|
@@ -1024,10 +1142,10 @@ import {
|
|
|
1024
1142
|
tasksNamed as tasksNamed2,
|
|
1025
1143
|
taskUpdate
|
|
1026
1144
|
} from "@stringsync/splates-server/board";
|
|
1027
|
-
import { commaList as
|
|
1145
|
+
import { commaList as commaList5 } from "@stringsync/splates-server/comma-list";
|
|
1028
1146
|
import { refuseGroomer as refuseGroomer3 } from "@stringsync/splates-server/conversations";
|
|
1029
|
-
import { fail as
|
|
1030
|
-
import { repository as repository8 } from "@stringsync/splates-server/records";
|
|
1147
|
+
import { fail as fail9 } from "@stringsync/splates-server/exit";
|
|
1148
|
+
import { OpenBoards, repository as repository8 } from "@stringsync/splates-server/records";
|
|
1031
1149
|
import {
|
|
1032
1150
|
DEFAULT_TIMEOUT as DEFAULT_TIMEOUT2,
|
|
1033
1151
|
taskWait,
|
|
@@ -1056,7 +1174,7 @@ function adding(name, description, ready) {
|
|
|
1056
1174
|
}).repeated("file", z12.string(), {
|
|
1057
1175
|
description: "files to attach, comma separated or the flag repeated: copied under .git/splates/tasks/<n>/"
|
|
1058
1176
|
}).repeated("blocked-by", z12.string(), {
|
|
1059
|
-
description: "
|
|
1177
|
+
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"
|
|
1060
1178
|
}).option("lane", z12.string(), {
|
|
1061
1179
|
default: "",
|
|
1062
1180
|
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)"
|
|
@@ -1070,28 +1188,43 @@ function adding(name, description, ready) {
|
|
|
1070
1188
|
default: "",
|
|
1071
1189
|
description: "with --agent: the thinking level, one the agent offers (default: the agent's own)"
|
|
1072
1190
|
}).action(async (opts, ctx) => {
|
|
1073
|
-
|
|
1074
|
-
|
|
1191
|
+
let __stack = [];
|
|
1192
|
+
try {
|
|
1193
|
+
if (ctx.elsewhere !== null && opts.lane !== "") {
|
|
1194
|
+
fail9("not_granted", "a task from another project goes in its untriaged lane, for whoever orchestrates it there to place: leave --lane off", { subject: opts.subject });
|
|
1195
|
+
}
|
|
1196
|
+
const from = await here(ctx);
|
|
1197
|
+
const assignee = await assigneeIn(ctx, opts.agent, opts.model, opts.effort);
|
|
1198
|
+
const origin = ctx.elsewhere === null ? undefined : {
|
|
1199
|
+
project: ctx.elsewhere,
|
|
1200
|
+
task: ctx.project === undefined ? null : ctx.service.taskFor(await ctx.git.currentBranch(ctx.ps.cwd()))
|
|
1201
|
+
};
|
|
1202
|
+
const project = await projectFrom(ctx);
|
|
1203
|
+
const boards = __using(__stack, new OpenBoards(ctx, ctx.projects), 0);
|
|
1204
|
+
const { blockedBy, blockedElsewhere } = await blockerFlags({
|
|
1205
|
+
boards,
|
|
1206
|
+
here: ctx.git.root,
|
|
1207
|
+
blockedBy: opts["blocked-by"]
|
|
1208
|
+
});
|
|
1209
|
+
await ctx.journal.record("task add", from, () => taskAdd(ctx, opts.subject, from, {
|
|
1210
|
+
slug: opts.slug,
|
|
1211
|
+
text: opts.text,
|
|
1212
|
+
position: opts.position,
|
|
1213
|
+
files: commaList5(opts.file.join(",")),
|
|
1214
|
+
blockedBy,
|
|
1215
|
+
blockedElsewhere,
|
|
1216
|
+
lane: laneIn(opts.lane) ?? null,
|
|
1217
|
+
assignee,
|
|
1218
|
+
origin,
|
|
1219
|
+
ready,
|
|
1220
|
+
project,
|
|
1221
|
+
boards
|
|
1222
|
+
}), addedFacts);
|
|
1223
|
+
} catch (_catch) {
|
|
1224
|
+
var _err = _catch, _hasErr = 1;
|
|
1225
|
+
} finally {
|
|
1226
|
+
__callDispose(__stack, _err, _hasErr);
|
|
1075
1227
|
}
|
|
1076
|
-
const from = await here(ctx);
|
|
1077
|
-
const assignee = await assigneeIn(ctx, opts.agent, opts.model, opts.effort);
|
|
1078
|
-
const origin = ctx.elsewhere === null ? undefined : {
|
|
1079
|
-
project: ctx.elsewhere,
|
|
1080
|
-
task: ctx.project === undefined ? null : ctx.service.taskFor(await ctx.git.currentBranch(ctx.ps.cwd()))
|
|
1081
|
-
};
|
|
1082
|
-
const project = await projectFrom(ctx);
|
|
1083
|
-
await ctx.journal.record("task add", from, () => taskAdd(ctx, opts.subject, from, {
|
|
1084
|
-
slug: opts.slug,
|
|
1085
|
-
text: opts.text,
|
|
1086
|
-
position: opts.position,
|
|
1087
|
-
files: commaList4(opts.file.join(",")),
|
|
1088
|
-
blockedBy: taskIds(opts["blocked-by"].join(",")),
|
|
1089
|
-
lane: laneIn(opts.lane) ?? null,
|
|
1090
|
-
assignee,
|
|
1091
|
-
origin,
|
|
1092
|
-
ready,
|
|
1093
|
-
project
|
|
1094
|
-
}), addedFacts);
|
|
1095
1228
|
});
|
|
1096
1229
|
}
|
|
1097
1230
|
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);
|
|
@@ -1108,11 +1241,23 @@ splatesTask.command("list").use(repository8({ needs: "read" })).description("eve
|
|
|
1108
1241
|
ready: opts.ready,
|
|
1109
1242
|
project: await projectFrom(ctx)
|
|
1110
1243
|
}));
|
|
1111
|
-
splatesTask.command("show").use(repository8({ needs: "read" })).description("one task in full: where it stands, its files, and its detail").arg("id", z12.coerce.number().int().positive(), { description: "task number" }).option("json", z12.boolean(), { default: false, description: "emit JSON" }).action(async (opts, ctx) =>
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1244
|
+
splatesTask.command("show").use(repository8({ needs: "read" })).description("one task in full: where it stands, its files, and its detail").arg("id", z12.coerce.number().int().positive(), { description: "task number" }).option("json", z12.boolean(), { default: false, description: "emit JSON" }).action(async (opts, ctx) => {
|
|
1245
|
+
let __stack = [];
|
|
1246
|
+
try {
|
|
1247
|
+
const boards = __using(__stack, new OpenBoards(ctx, ctx.projects), 0);
|
|
1248
|
+
await taskShow(ctx, opts.id, {
|
|
1249
|
+
json: opts.json,
|
|
1250
|
+
project: await projectFrom(ctx),
|
|
1251
|
+
others: (await ctx.projects.list()).filter((project) => project.root !== ctx.git.root).map((project) => project.slug),
|
|
1252
|
+
boards,
|
|
1253
|
+
root: ctx.git.root
|
|
1254
|
+
});
|
|
1255
|
+
} catch (_catch) {
|
|
1256
|
+
var _err = _catch, _hasErr = 1;
|
|
1257
|
+
} finally {
|
|
1258
|
+
__callDispose(__stack, _err, _hasErr);
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1116
1261
|
splatesTask.command("update").use(repository8({ 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", z12.coerce.number().int().positive(), { description: "task number" }).arg("text", z12.string(), {
|
|
1117
1262
|
default: "",
|
|
1118
1263
|
description: "whatever more there is to say about it, in markdown; leave out to keep the detail"
|
|
@@ -1129,9 +1274,9 @@ splatesTask.command("update").use(repository8({ needs: "tasks" })).description("
|
|
|
1129
1274
|
}).repeated("remove-file", z12.string(), {
|
|
1130
1275
|
description: "attached files to drop, comma separated or the flag repeated, by path or stored name"
|
|
1131
1276
|
}).repeated("blocked-by", z12.string(), {
|
|
1132
|
-
description: "
|
|
1277
|
+
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"
|
|
1133
1278
|
}).repeated("remove-blocked-by", z12.string(), {
|
|
1134
|
-
description: "
|
|
1279
|
+
description: "tasks no longer blocking it, as --blocked-by names them, comma separated or the flag repeated"
|
|
1135
1280
|
}).option("lane", z12.string(), {
|
|
1136
1281
|
default: "",
|
|
1137
1282
|
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"
|
|
@@ -1154,25 +1299,42 @@ splatesTask.command("update").use(repository8({ needs: "tasks" })).description("
|
|
|
1154
1299
|
default: false,
|
|
1155
1300
|
description: "let a queued lane queue it again, undoing --manual"
|
|
1156
1301
|
}).action(async (opts, ctx) => {
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1302
|
+
let __stack = [];
|
|
1303
|
+
try {
|
|
1304
|
+
if (opts.manual && opts.auto) {
|
|
1305
|
+
fail9("usage", "pass --manual or --auto, not both", { number: opts.id });
|
|
1306
|
+
}
|
|
1307
|
+
await refuseGroomer3(ctx.service, "task update", await ctx.tasks.find(opts.id));
|
|
1308
|
+
const assignee = await assigneeIn(ctx, opts.agent, opts.model, opts.effort);
|
|
1309
|
+
const boards = __using(__stack, new OpenBoards(ctx, ctx.projects), 0);
|
|
1310
|
+
const { blockedBy, blockedElsewhere, unblocked } = await blockerFlags({
|
|
1311
|
+
boards,
|
|
1312
|
+
here: ctx.git.root,
|
|
1313
|
+
blockedBy: opts["blocked-by"],
|
|
1314
|
+
removeBlockedBy: opts["remove-blocked-by"]
|
|
1315
|
+
});
|
|
1316
|
+
await recordUpdate(ctx.journal, ctx.tasks, opts.id, () => taskUpdate(ctx, opts.id, {
|
|
1317
|
+
subject: opts.subject,
|
|
1318
|
+
slug: opts.slug,
|
|
1319
|
+
text: opts.text,
|
|
1320
|
+
position: opts.position,
|
|
1321
|
+
files: commaList5(opts.file.join(",")),
|
|
1322
|
+
removeFiles: commaList5(opts["remove-file"].join(",")),
|
|
1323
|
+
blockedBy,
|
|
1324
|
+
removeBlockedBy: unblocked.ids,
|
|
1325
|
+
blockedElsewhere,
|
|
1326
|
+
removeElsewhere: unblocked.elsewhere,
|
|
1327
|
+
lane: laneIn(opts.lane),
|
|
1328
|
+
assignee,
|
|
1329
|
+
ready: opts.ready,
|
|
1330
|
+
manual: opts.manual ? true : opts.auto ? false : undefined,
|
|
1331
|
+
boards
|
|
1332
|
+
}));
|
|
1333
|
+
} catch (_catch) {
|
|
1334
|
+
var _err = _catch, _hasErr = 1;
|
|
1335
|
+
} finally {
|
|
1336
|
+
__callDispose(__stack, _err, _hasErr);
|
|
1337
|
+
}
|
|
1176
1338
|
});
|
|
1177
1339
|
splatesTask.command("remove").use(repository8({ needs: "tasks" })).description("drop a queued task that was done some other way or no longer applies").arg("id", z12.coerce.number().int().positive(), { description: "task number" }).action(async (opts, ctx) => {
|
|
1178
1340
|
await refuseGroomer3(ctx.service, "task remove", await ctx.tasks.find(opts.id));
|
|
@@ -1407,6 +1569,7 @@ splates.command("update").use(recordsFirst()).use(repository10({ needs: "own" })
|
|
|
1407
1569
|
ctx.ps.exit(code);
|
|
1408
1570
|
}
|
|
1409
1571
|
});
|
|
1572
|
+
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));
|
|
1410
1573
|
splates.command("forget").use(repository10({ 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));
|
|
1411
1574
|
splates.command("add").use(repository10({ 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", z16.string(), {
|
|
1412
1575
|
description: "the task's name, for its branch and worktree (lowercase-with-dashes)"
|
|
@@ -1437,12 +1600,12 @@ splates.command("add").use(repository10({ needs: "tasks" })).description("start
|
|
|
1437
1600
|
}).action(async (opts, ctx) => {
|
|
1438
1601
|
await refuseGroomer4(ctx.service, "add");
|
|
1439
1602
|
if (opts.agent === "" && (opts.model !== "" || opts.effort !== "" || opts.prompt !== "" || opts.held !== "")) {
|
|
1440
|
-
|
|
1603
|
+
fail10("usage", "--model, --effort, --prompt and --held go with --agent", {
|
|
1441
1604
|
task: opts.slug
|
|
1442
1605
|
});
|
|
1443
1606
|
}
|
|
1444
1607
|
if (opts.goal.trim() === "" && opts.task === "") {
|
|
1445
|
-
|
|
1608
|
+
fail10("usage", `say what done means: \`splates add ${opts.slug} --goal "<one or two lines>"\`, or start queued tasks with --task`, { task: opts.slug });
|
|
1446
1609
|
}
|
|
1447
1610
|
const dispatch = opts.agent === "" ? undefined : {
|
|
1448
1611
|
agent: opts.agent,
|
|
@@ -1499,10 +1662,46 @@ splates.command("remove").use(repository10({ needs: "tasks" })).description("dis
|
|
|
1499
1662
|
default: false,
|
|
1500
1663
|
description: "discard even when another agent holds the lease"
|
|
1501
1664
|
}).option("reason", z16.optional(z16.enum(REMOVE_REASONS)), {
|
|
1502
|
-
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)"
|
|
1665
|
+
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", z16.string(), {
|
|
1667
|
+
default: "",
|
|
1668
|
+
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", z16.string(), {
|
|
1670
|
+
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", z16.string(), {
|
|
1672
|
+
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", z16.string(), {
|
|
1674
|
+
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", z16.string(), {
|
|
1676
|
+
default: "",
|
|
1677
|
+
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"
|
|
1503
1678
|
}).action(async (opts, ctx) => {
|
|
1504
|
-
|
|
1505
|
-
|
|
1679
|
+
let __stack = [];
|
|
1680
|
+
try {
|
|
1681
|
+
const boards = __using(__stack, new OpenBoards2(ctx, ctx.projects), 0);
|
|
1682
|
+
const { blockedBy, blockedElsewhere } = await blockerFlags({
|
|
1683
|
+
boards,
|
|
1684
|
+
here: ctx.git.root,
|
|
1685
|
+
blockedBy: opts["blocked-by"]
|
|
1686
|
+
});
|
|
1687
|
+
await ctx.journal.record("remove", opts.task, () => remove(ctx, opts.task, {
|
|
1688
|
+
force: opts.force,
|
|
1689
|
+
reason: opts.reason,
|
|
1690
|
+
deferral: {
|
|
1691
|
+
why: opts.why,
|
|
1692
|
+
blockedBy,
|
|
1693
|
+
blockedElsewhere,
|
|
1694
|
+
questions: opts.ask,
|
|
1695
|
+
outside: opts["waiting-on"],
|
|
1696
|
+
detail: opts.detail === "" ? undefined : opts.detail
|
|
1697
|
+
}
|
|
1698
|
+
}), ({ released }) => ({ why: opts.reason ?? null, released }));
|
|
1699
|
+
await follow(ctx, null, { from: opts.task });
|
|
1700
|
+
} catch (_catch) {
|
|
1701
|
+
var _err = _catch, _hasErr = 1;
|
|
1702
|
+
} finally {
|
|
1703
|
+
__callDispose(__stack, _err, _hasErr);
|
|
1704
|
+
}
|
|
1506
1705
|
});
|
|
1507
1706
|
splates.command("list").use(repository10({ 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", z16.boolean(), { default: false, description: "emit JSON" }).option("files", z16.boolean(), {
|
|
1508
1707
|
default: false,
|
|
@@ -1572,12 +1771,42 @@ splates.command("attach").use(repository10({ needs: "own" })).description("store
|
|
|
1572
1771
|
}).option("conversation", z16.string(), {
|
|
1573
1772
|
default: "",
|
|
1574
1773
|
description: "conversation to keep the files with, when $SPLATES_CONVERSATION is not set"
|
|
1575
|
-
}).action(async (opts, ctx) => ctx.journal.record("attach", opts.task || await here(ctx), () => attach(ctx,
|
|
1774
|
+
}).action(async (opts, ctx) => ctx.journal.record("attach", opts.task || await here(ctx), () => attach(ctx, commaList6(opts.files), ctx.ps.cwd(), {
|
|
1576
1775
|
task: opts.task || undefined,
|
|
1577
|
-
remove:
|
|
1776
|
+
remove: commaList6(opts.remove),
|
|
1578
1777
|
conversation: opts.conversation || undefined
|
|
1579
|
-
}), (stored) => ({ stored, removed:
|
|
1778
|
+
}), (stored) => ({ stored, removed: commaList6(opts.remove) })));
|
|
1580
1779
|
splates.command("retry").use(repository10({ 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", z16.string(), { description: "task name" }).action((opts, ctx) => recordRetry(ctx, opts.task));
|
|
1780
|
+
splates.command("ship").use(repository10({ needs: "read" })).description("ship a project and its cascade: `splates server` runs its `ship` command from splates.config.ts on its machine, under your own npm sign-in, signing in with `npm login --auth-type=web` first when npm knows no one, and shows npm's sign-in and 2FA links on the page for any device; this prints that ship's output until it ends. The server then carries the release through every registered project that depends on it, in dependency order: a bump task queued in each once all it takes from are shipped, and, once that merges, each public one with a `ship` command and dependents of its own ships in turn. A bump that asks you, or is removed, stops the cascade at that project until it merges or you retry or drop it, as does a failed ship. While a cascade is under way, this prints where it stands instead of starting another, or changes one of its pending publishes with `--bump`. `--plan` prints the cascade and runs nothing. The person's alone to start, refused an agent, which asks with a run-command card").arg("project", z16.string(), {
|
|
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
|
+
}));
|
|
1581
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);
|
|
1582
1811
|
|
|
1583
1812
|
// index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stringsync/splates",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.59",
|
|
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.59",
|
|
21
|
+
"webappwiz": "0.0.53",
|
|
22
22
|
"zod": "^4.4.3"
|
|
23
23
|
},
|
|
24
24
|
"main": "./index.js",
|