@stringsync/splates 0.0.53 → 0.0.54

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.
Files changed (60) hide show
  1. package/artifact.d.ts +3 -0
  2. package/budget.d.ts +3 -0
  3. package/creds.d.ts +3 -0
  4. package/flags.d.ts +37 -0
  5. package/hook.d.ts +3 -0
  6. package/index.d.ts +2 -0
  7. package/index.js +1802 -0
  8. package/infra.d.ts +3 -0
  9. package/lane.d.ts +3 -0
  10. package/notes.d.ts +4 -0
  11. package/package.json +12 -14
  12. package/projects.d.ts +3 -0
  13. package/queue.d.ts +3 -0
  14. package/server.d.ts +7 -0
  15. package/setup.d.ts +22 -0
  16. package/slots.d.ts +3 -0
  17. package/splates.d.ts +10 -0
  18. package/task.d.ts +3 -0
  19. package/tunnel.d.ts +3 -0
  20. package/waiting.d.ts +3 -0
  21. package/worker.d.ts +3 -0
  22. package/artifact.ts +0 -120
  23. package/budget.ts +0 -117
  24. package/checkout.ts +0 -8
  25. package/creds.test.ts +0 -336
  26. package/creds.ts +0 -144
  27. package/dev/dev.ts +0 -86
  28. package/dev/eval.ts +0 -21
  29. package/dev/fix.ts +0 -63
  30. package/dev/index.ts +0 -1
  31. package/dev/prove.ts +0 -38
  32. package/dev/root.ts +0 -7
  33. package/dev/ship.ts +0 -46
  34. package/dev/test.ts +0 -154
  35. package/e2e.test.ts +0 -472
  36. package/flags.ts +0 -157
  37. package/hook.ts +0 -328
  38. package/index.ts +0 -18
  39. package/infra.ts +0 -107
  40. package/lane.test.ts +0 -29
  41. package/lane.ts +0 -172
  42. package/notes.ts +0 -19
  43. package/projects.test.ts +0 -191
  44. package/projects.ts +0 -37
  45. package/queue.ts +0 -82
  46. package/screenshots/annotate.png +0 -0
  47. package/screenshots/demo.png +0 -0
  48. package/screenshots/parallel.png +0 -0
  49. package/screenshots/questions.png +0 -0
  50. package/server.ts +0 -287
  51. package/setup.ts +0 -233
  52. package/slots.ts +0 -65
  53. package/splates.test.ts +0 -338
  54. package/splates.ts +0 -567
  55. package/task.ts +0 -354
  56. package/tunnel.ts +0 -106
  57. package/wait-journal.test.ts +0 -96
  58. package/waiting.ts +0 -59
  59. package/worker-relay.test.ts +0 -233
  60. package/worker.ts +0 -300
package/lane.ts DELETED
@@ -1,172 +0,0 @@
1
- import {
2
- APPROVAL,
3
- BACKLOG_LANE,
4
- laneAdd,
5
- laneList,
6
- laneRemove,
7
- laneShow,
8
- laneUpdate,
9
- laneUpdated,
10
- recordLaneRemove,
11
- } from "@stringsync/splates-server/board";
12
- import { refuseGroomer } from "@stringsync/splates-server/conversations";
13
- import { UNTRIAGED } from "@stringsync/splates-server/plans";
14
- import { refuseAgentQueue } from "@stringsync/splates-server/queue";
15
- import { repository } from "@stringsync/splates-server/records";
16
- import type { SplatesDeps } from "@stringsync/splates-server/server";
17
- import { cli } from "webappwiz/cmd";
18
- import { z } from "zod";
19
- import { position } from "./flags";
20
-
21
- /** Backlog is lane zero, also addressable by its permanent name. */
22
- const laneNumber = z.union([
23
- z.literal("backlog").transform(() => BACKLOG_LANE),
24
- z.coerce.number().int().nonnegative(),
25
- ]);
26
-
27
- /** `splates lane`, which the root cli mounts after `splates task`. */
28
- export const splatesLane = cli<SplatesDeps>("lane").description(
29
- "lanes: named groups of tasks, for you to see related work together on the board; queue one whole with `splates lane queue`. Backlog (lane 0) is permanent and holds unmatched deferred work. Untriaged is groomed into lanes and cannot be queued whole",
30
- );
31
-
32
- splatesLane
33
- .command("list")
34
- .use(repository({ needs: "read" }))
35
- .description(
36
- "every lane: its name, its queued tasks in order, the started tasks on it, its next task, and what blocks that in other lanes",
37
- )
38
- .option("json", z.boolean(), { default: false, description: "emit JSON" })
39
- .action((opts, ctx) => laneList(ctx, { json: opts.json }));
40
-
41
- splatesLane
42
- .command("show")
43
- .use(repository({ needs: "read" }))
44
- .description(
45
- "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",
46
- )
47
- .arg(
48
- "lane",
49
- z.union([z.literal(UNTRIAGED).transform(() => null), laneNumber], {
50
- error: "pass a lane's number, backlog, or untriaged",
51
- }),
52
- { description: "lane number, `backlog`, or `untriaged`" },
53
- )
54
- .option("json", z.boolean(), { default: false, description: "emit JSON" })
55
- .action((opts, ctx) => laneShow(ctx, opts.lane, { json: opts.json }));
56
-
57
- splatesLane
58
- .command("add")
59
- .use(repository({ needs: "tasks" }))
60
- .description("start an empty lane, after the others")
61
- .arg("name", z.string(), {
62
- description: "what its tasks are for, like `Licensing`",
63
- })
64
- .action(async (opts, ctx) => {
65
- await ctx.journal.record(
66
- "lane add",
67
- null,
68
- () => laneAdd(ctx, opts.name),
69
- (lane) => ({ lane: lane.id, name: lane.name }),
70
- );
71
- });
72
-
73
- splatesLane
74
- .command("update")
75
- .use(repository({ needs: "tasks" }))
76
- .description("rename a lane, or move it among the others")
77
- .arg("lane", laneNumber, {
78
- description: "lane number",
79
- })
80
- .option("name", z.string().optional(), {
81
- description: "what it is called from now on",
82
- })
83
- .option("position", position, {
84
- description:
85
- "where to move it among the lanes, 1 the first column on the board; the others close up around it",
86
- })
87
- .action(async (opts, ctx) => {
88
- const change = { name: opts.name, position: opts.position };
89
- await ctx.journal.record(
90
- "lane update",
91
- null,
92
- () => laneUpdate(ctx, opts.lane, change),
93
- (record) => laneUpdated(opts.lane, record, change),
94
- );
95
- });
96
-
97
- splatesLane
98
- .command("queue")
99
- .use(repository({ needs: "tasks" }))
100
- .description(
101
- "queue a lane whole: each of its tasks is queued as soon as nothing blocks it, now and as tasks land, until the lane is empty; a task you take out with `splates queue remove` stays out until you queue it again. Untriaged cannot be queued whole",
102
- )
103
- .arg("lane", laneNumber, {
104
- description: "lane number",
105
- })
106
- .action(async (opts, ctx) => {
107
- refuseAgentQueue(ctx.service, "lane queue");
108
- await refuseGroomer(ctx.service, "lane queue");
109
- const change = { queued: true };
110
- await ctx.journal.record(
111
- "lane update",
112
- null,
113
- () => laneUpdate(ctx, opts.lane, change),
114
- (record) => laneUpdated(opts.lane, record, change),
115
- );
116
- });
117
-
118
- splatesLane
119
- .command("approve")
120
- .use(repository({ needs: "tasks" }))
121
- .description(
122
- "let a queued lane queue more tasks on its own: it stops when its budget is spent, which starts at its tasks then plus 5, and this adds 10 to what is left; refused from an agent",
123
- )
124
- .arg("lane", laneNumber, {
125
- description: "lane number",
126
- })
127
- .action(async (opts, ctx) => {
128
- refuseAgentQueue(ctx.service, "lane approve");
129
- const change = { approve: APPROVAL };
130
- await ctx.journal.record(
131
- "lane update",
132
- null,
133
- () => laneUpdate(ctx, opts.lane, change),
134
- (record) => laneUpdated(opts.lane, record, change),
135
- );
136
- });
137
-
138
- splatesLane
139
- .command("unqueue")
140
- .use(repository({ needs: "tasks" }))
141
- .description(
142
- "stop queueing a lane whole, taking out of the queue each of its tasks that has yet to start; those under way go on",
143
- )
144
- .arg("lane", laneNumber, {
145
- description: "lane number",
146
- })
147
- .action(async (opts, ctx) => {
148
- refuseAgentQueue(ctx.service, "lane unqueue");
149
- const change = { queued: false };
150
- await ctx.journal.record(
151
- "lane update",
152
- null,
153
- () => laneUpdate(ctx, opts.lane, change),
154
- (record) => laneUpdated(opts.lane, record, change),
155
- );
156
- });
157
-
158
- splatesLane
159
- .command("remove")
160
- .use(repository({ needs: "tasks" }))
161
- .description(
162
- "remove a lane and its tasks, kept to restore; a lane whose last task merges goes on its own; refused from an agent, which asks with a permission card",
163
- )
164
- .arg("lane", laneNumber, {
165
- description: "lane number",
166
- })
167
- .action((opts, ctx) => {
168
- refuseAgentQueue(ctx.service, "lane remove");
169
- return recordLaneRemove(ctx.journal, ctx.tasks, opts.lane, () =>
170
- laneRemove(ctx, opts.lane),
171
- );
172
- });
package/notes.ts DELETED
@@ -1,19 +0,0 @@
1
- import { loadConfig } from "@stringsync/splates-server/config";
2
- import { Notes } from "@stringsync/splates-server/notes";
3
- import type { Repository } from "@stringsync/splates-server/records";
4
- import type { SplatesDeps } from "@stringsync/splates-server/server";
5
-
6
- /** Prints the descriptions in this checkout, or the project explicitly named. */
7
- export async function notes(ctx: SplatesDeps & Repository): Promise<void> {
8
- const root =
9
- ctx.project === undefined
10
- ? (
11
- await ctx.git.run(ctx.ps.cwd(), "rev-parse", "--show-toplevel")
12
- ).stdout.trim()
13
- : ctx.git.root;
14
- const config = await loadConfig(root, { fs: ctx.fs, git: ctx.git });
15
- const found = await new Notes(ctx.fs).list(root, config);
16
- for (const note of found) {
17
- ctx.log.info(note.description);
18
- }
19
- }
package/projects.test.ts DELETED
@@ -1,191 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
- import { basename, join } from "node:path";
3
- import { Testing } from "@stringsync/splates-server/testing";
4
- import { projectArgs, splates } from "./splates";
5
-
6
- /**
7
- * Two projects on one machine, the first's registry naming both, its agent
8
- * running `splates` on the second with `--project`.
9
- */
10
- describe("splates across projects", () => {
11
- let here: Testing;
12
- let there: Testing;
13
- let deps: Parameters<typeof splates.run>[0];
14
- let hereSlug: string;
15
- let thereSlug: string;
16
-
17
- beforeEach(async () => {
18
- here = await Testing.open();
19
- there = await Testing.open();
20
- await here.projects.register(there.root);
21
- const listed = await here.projects.list();
22
- const slug = (root: string) =>
23
- listed.find((each) => each.root === root)?.slug ?? "";
24
- hereSlug = slug(here.root);
25
- thereSlug = slug(there.root);
26
- here.ps.cd(here.root);
27
- deps = {
28
- log: here.log,
29
- fs: here.fs,
30
- ps: here.ps,
31
- assets: here.assets,
32
- secrets: here.secrets,
33
- };
34
- });
35
-
36
- afterEach(async () => {
37
- await here.disposeAsync();
38
- await there.disposeAsync();
39
- });
40
-
41
- /** Runs `splates --project <there> ...args` from here. */
42
- const onThere = (...args: string[]) =>
43
- splates.run({ ...deps, project: thereSlug }, args);
44
-
45
- it("splits a leading --project off the arguments", () => {
46
- expect(projectArgs(["--project", "shop", "list"])).toEqual({
47
- project: "shop",
48
- argv: ["list"],
49
- });
50
- expect(projectArgs(["--project=shop", "list"])).toEqual({
51
- project: "shop",
52
- argv: ["list"],
53
- });
54
- expect(projectArgs(["list", "--project", "shop"])).toEqual({
55
- argv: ["list", "--project", "shop"],
56
- });
57
- });
58
-
59
- it("queues a task in its untriaged lane, saying where it came from", async () => {
60
- await splates.run(deps, ["add", "alpha", "--goal", "Land it."]);
61
- here.ps.cd((await here.service.find("alpha")).path);
62
-
63
- await onThere(
64
- "task",
65
- "add",
66
- "Export the parser",
67
- "--slug",
68
- "parser-export",
69
- );
70
-
71
- const thereSlug = (await here.projects.list()).find(
72
- (each) => each.root === there.root,
73
- )?.slug;
74
- expect(String(here.log.entries.at(-1)?.message)).toContain(
75
- `${thereSlug} #1 at`,
76
- );
77
-
78
- const [queued] = await there.tasks.all();
79
- expect(queued?.state).toMatchObject({
80
- subject: "Export the parser",
81
- from: null,
82
- lane: null,
83
- origin: { project: here.root, task: "alpha" },
84
- });
85
- expect(await here.tasks.all()).toEqual([]);
86
-
87
- await onThere("task", "show", String(queued?.id));
88
- expect(String(here.log.entries.at(-1)?.message)).toContain(
89
- `${thereSlug} #${queued?.id}`,
90
- );
91
- expect(String(here.log.entries.at(-1)?.message)).toContain(
92
- `from: ${basename(here.root)}/alpha`,
93
- );
94
- });
95
-
96
- it("refuses a lane for a queued task from another project", async () => {
97
- await onThere(
98
- "task",
99
- "add",
100
- "Export the parser",
101
- "--slug",
102
- "parser-export",
103
- "--lane",
104
- "new",
105
- );
106
-
107
- expect(here.out()).toContain('"reason":"not_granted"');
108
- expect(await there.tasks.all()).toEqual([]);
109
- });
110
-
111
- it("refuses what the other project has not granted, until it does", async () => {
112
- await onThere(
113
- "task",
114
- "add",
115
- "Export the parser",
116
- "--slug",
117
- "parser-export",
118
- );
119
- await onThere("task", "remove", "1");
120
- expect(here.out()).toContain('"reason":"not_granted"');
121
- expect(here.out()).toContain(`splates projects grant ${hereSlug} tasks`);
122
- expect(await there.tasks.all()).toHaveLength(1);
123
-
124
- // The person, in the other project's main tree.
125
- here.ps.cd(there.root);
126
- await splates.run(deps, ["projects", "grant", hereSlug, "tasks"]);
127
- expect(await here.projects.reach(here.root, there.root)).toBe("tasks");
128
-
129
- here.ps.cd(here.root);
130
- await onThere("task", "remove", "1");
131
- expect(await there.tasks.all()).toEqual([]);
132
- });
133
-
134
- it("starts no task in another project, granted or not, so it is queued there", async () => {
135
- await here.projects.grant(there.root, here.root, "tasks");
136
-
137
- await onThere("add", "beta", "--goal", "Do it.", "--agent", "claude");
138
-
139
- expect(here.out()).toContain('"reason":"not_granted"');
140
- expect(here.out()).toContain("task add");
141
- expect((await there.service.find("beta")).gone).toBe(true);
142
- });
143
-
144
- it("refuses a command only a project runs on itself", async () => {
145
- await here.projects.grant(there.root, here.root, "tasks");
146
-
147
- await onThere("projects", "grant", hereSlug, "tasks");
148
-
149
- expect(here.out()).toContain('"reason":"not_granted"');
150
- });
151
-
152
- it("checks an agent of this project's page that changes into the other", async () => {
153
- await here.projects.grant(there.root, here.root, "read");
154
- await here.fs.mkdir(join(here.root, ".git/splates/conversations"));
155
- await here.fs.write(
156
- join(here.root, ".git/splates/conversations/c1.jsonl"),
157
- "",
158
- );
159
- here.proc.env.SPLATES_CONVERSATION = "c1";
160
- here.ps.cd(there.root);
161
-
162
- await splates.run(deps, [
163
- "task",
164
- "add",
165
- "Export the parser",
166
- "--slug",
167
- "parser-export",
168
- ]);
169
- expect(here.out()).toContain('"reason":"not_granted"');
170
- expect(await there.tasks.all()).toEqual([]);
171
-
172
- await splates.run(deps, ["task", "list", "--json"]);
173
- expect(JSON.parse(String(here.log.entries.at(-1)?.message))).toEqual([]);
174
- });
175
-
176
- it("lists what each project may do in the other", async () => {
177
- await here.projects.grant(there.root, here.root, "tasks");
178
-
179
- await splates.run(deps, ["projects", "list", "--json"]);
180
-
181
- expect(JSON.parse(String(here.log.entries.at(-1)?.message))).toEqual([
182
- {
183
- slug: thereSlug,
184
- root: there.root,
185
- gone: false,
186
- here: "queue",
187
- there: "tasks",
188
- },
189
- ]);
190
- });
191
- });
package/projects.ts DELETED
@@ -1,37 +0,0 @@
1
- import {
2
- grantReach,
3
- listReaches,
4
- REACHES,
5
- } from "@stringsync/splates-server/projects";
6
- import { repository } from "@stringsync/splates-server/records";
7
- import type { SplatesDeps } from "@stringsync/splates-server/server";
8
- import { cli } from "webappwiz/cmd";
9
- import { z } from "zod";
10
-
11
- /** `splates projects`, which the root cli mounts after `splates lane`. */
12
- export const splatesProjects = cli<SplatesDeps>("projects")
13
- .description(
14
- "the other splates projects on this machine, and what agents of each may do in the others through splates: `read` their tasks and lanes; add tasks to their untriaged lane with `queue`, as every project may until granted otherwise; or run their queue, lanes and hooks with `tasks`. Any repo command runs on another project as `splates --project <slug> <command>`, refused where it needs more than that project granted",
15
- )
16
- .fallback("list");
17
-
18
- splatesProjects
19
- .command("list")
20
- .use(repository({ needs: "read" }))
21
- .description(
22
- "print each other project: its slug, its root, what it may do here, and what this one may do there",
23
- )
24
- .option("json", z.boolean(), { default: false, description: "emit JSON" })
25
- .action((opts, ctx) => listReaches(ctx, { json: opts.json }));
26
-
27
- splatesProjects
28
- .command("grant")
29
- .use(repository({ needs: "own" }))
30
- .description(
31
- "let agents of another project do as much as `read`, `queue` or `tasks` here, each including those before it; the user's call, so an agent runs it only when the user asks it to",
32
- )
33
- .arg("project", z.string(), { description: "the other project's slug" })
34
- .arg("reach", z.enum(REACHES), {
35
- description: "read, queue or tasks",
36
- })
37
- .action((opts, ctx) => grantReach(ctx, opts.project, opts.reach));
package/queue.ts DELETED
@@ -1,82 +0,0 @@
1
- import { tasksNamed } from "@stringsync/splates-server/board";
2
- import { refuseGroomer } from "@stringsync/splates-server/conversations";
3
- import {
4
- queueAdd,
5
- queueList,
6
- queueMove,
7
- queueRemove,
8
- refuseAgentQueue,
9
- } from "@stringsync/splates-server/queue";
10
- import { repository } from "@stringsync/splates-server/records";
11
- import type { SplatesDeps } from "@stringsync/splates-server/server";
12
- import { cli } from "webappwiz/cmd";
13
- import { z } from "zod";
14
- import { here } from "./flags";
15
-
16
- /** `splates queue`, which the root cli mounts beside `splates task`. */
17
- export const splatesQueue = cli<SplatesDeps>("queue").description(
18
- "the queue: the tasks a worker of `splates server` starts as soon as it has a slot free, each after every task it is blocked by, otherwise in the order you set. Only you queue a task, from the backlog or groomed, and an agent asks you to with a queue card; one that names no model runs on the roster's first agent",
19
- );
20
-
21
- splatesQueue
22
- .command("list")
23
- .use(repository({ needs: "read" }))
24
- .description(
25
- "the queue in the order workers start it, with each task's model and what it waits on: the open tasks it is blocked by, or those removed without merging",
26
- )
27
- .option("json", z.boolean(), { default: false, description: "emit JSON" })
28
- .action((opts, ctx) => queueList(ctx, { json: opts.json }));
29
-
30
- splatesQueue
31
- .command("add")
32
- .use(repository({ needs: "tasks" }))
33
- .description(
34
- "queue tasks for a worker to start once nothing they are blocked by is open; refused for one already under way",
35
- )
36
- .rest("ids", z.coerce.number().int().positive(), {
37
- description: "task numbers",
38
- })
39
- .action(async (opts, ctx) => {
40
- refuseAgentQueue(ctx.service, "queue add");
41
- await refuseGroomer(ctx.service, "queue add");
42
- await ctx.journal.record(
43
- "queue add",
44
- await here(ctx),
45
- () => queueAdd(ctx, opts.ids),
46
- (queued) => ({ tasks: tasksNamed(queued) }),
47
- );
48
- });
49
-
50
- splatesQueue
51
- .command("remove")
52
- .use(repository({ needs: "tasks" }))
53
- .description(
54
- "take tasks out of the queue, back to ready when they name a model, else the backlog; the tasks stay",
55
- )
56
- .rest("ids", z.coerce.number().int().positive(), {
57
- description: "task numbers",
58
- })
59
- .action(async (opts, ctx) => {
60
- refuseAgentQueue(ctx.service, "queue remove");
61
- await ctx.journal.record(
62
- "queue remove",
63
- await here(ctx),
64
- () => queueRemove(ctx, opts.ids),
65
- (removed) => ({ tasks: tasksNamed(removed) }),
66
- );
67
- });
68
-
69
- splatesQueue
70
- .command("move")
71
- .use(repository({ needs: "tasks" }))
72
- .description(
73
- "move a queued task to another place in the queue, from 1 for the next to start; what it is blocked by still goes first. Then lists the queue",
74
- )
75
- .arg("id", z.coerce.number().int().positive(), { description: "task number" })
76
- .arg("position", z.coerce.number().int().positive(), {
77
- description: "where in the queue to put it, from 1",
78
- })
79
- .action(async (opts, ctx) => {
80
- refuseAgentQueue(ctx.service, "queue move");
81
- return queueMove(ctx, opts.id, opts.position);
82
- });
Binary file
Binary file
Binary file
Binary file