@popoverai/dotrequirements 0.32.1 → 0.34.0

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 (135) hide show
  1. package/dist/cli.js +32 -7
  2. package/dist/cloud.d.ts +18 -0
  3. package/dist/cloud.js +38 -0
  4. package/dist/commands/board/index.d.ts +13 -0
  5. package/dist/commands/board/index.js +96 -0
  6. package/dist/commands/branch-merge.d.ts +78 -0
  7. package/dist/commands/branch-merge.js +129 -0
  8. package/dist/commands/create-requirement-document.js +2 -2
  9. package/dist/commands/diff.js +15 -5
  10. package/dist/commands/init.js +7 -6
  11. package/dist/commands/link.js +27 -21
  12. package/dist/commands/mockup/index.d.ts +11 -0
  13. package/dist/commands/mockup/index.js +82 -0
  14. package/dist/commands/report.js +22 -9
  15. package/dist/commands/review-test.js +3 -2
  16. package/dist/commands/scores/index.d.ts +50 -0
  17. package/dist/commands/scores/index.js +190 -0
  18. package/dist/commands/solution/ask.d.ts +9 -0
  19. package/dist/commands/solution/ask.js +27 -0
  20. package/dist/commands/solution/author.d.ts +19 -0
  21. package/dist/commands/solution/author.js +202 -0
  22. package/dist/commands/solution/branch.d.ts +103 -0
  23. package/dist/commands/solution/branch.js +341 -0
  24. package/dist/commands/solution/brief.d.ts +81 -0
  25. package/dist/commands/solution/brief.js +47 -0
  26. package/dist/commands/solution/checkout.d.ts +27 -0
  27. package/dist/commands/solution/checkout.js +66 -0
  28. package/dist/commands/solution/client.d.ts +16 -0
  29. package/dist/commands/solution/client.js +46 -0
  30. package/dist/commands/solution/contract.d.ts +9 -0
  31. package/dist/commands/solution/contract.js +23 -0
  32. package/dist/commands/solution/create.d.ts +14 -0
  33. package/dist/commands/solution/create.js +52 -0
  34. package/dist/commands/solution/demo-script.d.ts +28 -0
  35. package/dist/commands/solution/demo-script.js +43 -0
  36. package/dist/commands/solution/image.d.ts +60 -0
  37. package/dist/commands/solution/image.js +148 -0
  38. package/dist/commands/solution/index.d.ts +12 -0
  39. package/dist/commands/solution/index.js +281 -0
  40. package/dist/commands/solution/look-up.d.ts +9 -0
  41. package/dist/commands/solution/look-up.js +69 -0
  42. package/dist/commands/solution/next.d.ts +12 -0
  43. package/dist/commands/solution/next.js +48 -0
  44. package/dist/commands/solution/plan-file.d.ts +22 -0
  45. package/dist/commands/solution/plan-file.js +33 -0
  46. package/dist/commands/solution/preview-screenshot.d.ts +21 -0
  47. package/dist/commands/solution/preview-screenshot.js +56 -0
  48. package/dist/commands/solution/pull-request.d.ts +25 -0
  49. package/dist/commands/solution/pull-request.js +117 -0
  50. package/dist/commands/solution/pushing.d.ts +66 -0
  51. package/dist/commands/solution/pushing.js +185 -0
  52. package/dist/commands/solution/rehearse.d.ts +10 -0
  53. package/dist/commands/solution/rehearse.js +24 -0
  54. package/dist/commands/solution/request.d.ts +3 -0
  55. package/dist/commands/solution/request.js +13 -0
  56. package/dist/commands/solution/review.d.ts +27 -0
  57. package/dist/commands/solution/review.js +104 -0
  58. package/dist/commands/solution/run-file.d.ts +42 -0
  59. package/dist/commands/solution/run-file.js +63 -0
  60. package/dist/commands/solution/share.d.ts +62 -0
  61. package/dist/commands/solution/share.js +346 -0
  62. package/dist/commands/solution/stall.d.ts +14 -0
  63. package/dist/commands/solution/stall.js +41 -0
  64. package/dist/commands/solution/start.d.ts +11 -0
  65. package/dist/commands/solution/start.js +20 -0
  66. package/dist/commands/solution/status.d.ts +39 -0
  67. package/dist/commands/solution/status.js +145 -0
  68. package/dist/commands/solution/summary.d.ts +13 -0
  69. package/dist/commands/solution/summary.js +5 -0
  70. package/dist/commands/solution/things.d.ts +118 -0
  71. package/dist/commands/solution/things.js +78 -0
  72. package/dist/commands/solution/thread.d.ts +13 -0
  73. package/dist/commands/solution/thread.js +53 -0
  74. package/dist/commands/solution/upload.d.ts +24 -0
  75. package/dist/commands/solution/upload.js +27 -0
  76. package/dist/commands/solution/video.d.ts +88 -0
  77. package/dist/commands/solution/video.js +364 -0
  78. package/dist/commands/solution/working-folder.d.ts +44 -0
  79. package/dist/commands/solution/working-folder.js +64 -0
  80. package/dist/commands/style-check.js +4 -4
  81. package/dist/commands/sync.d.ts +2 -1
  82. package/dist/commands/sync.js +21 -5
  83. package/dist/convex.d.ts +64 -10
  84. package/dist/convex.js +75 -10
  85. package/dist/harness/finalize.js +16 -8
  86. package/dist/harness/reportingStatus.js +2 -11
  87. package/dist/push/core.js +7 -2
  88. package/dist/rehearsal/derive.d.ts +23 -0
  89. package/dist/rehearsal/derive.js +112 -0
  90. package/dist/rehearsal/driver.d.ts +44 -0
  91. package/dist/rehearsal/driver.js +337 -0
  92. package/dist/rehearsal/earlier.d.ts +25 -0
  93. package/dist/rehearsal/earlier.js +138 -0
  94. package/dist/rehearsal/index.d.ts +5 -0
  95. package/dist/rehearsal/index.js +4 -0
  96. package/dist/rehearsal/live.d.ts +72 -0
  97. package/dist/rehearsal/live.js +301 -0
  98. package/dist/rehearsal/report.d.ts +4 -0
  99. package/dist/rehearsal/report.js +69 -0
  100. package/dist/rehearsal/types.d.ts +377 -0
  101. package/dist/rehearsal/types.js +8 -0
  102. package/dist/requirements/cloud-ai.d.ts +0 -6
  103. package/dist/requirements/cloud-ai.js +2 -11
  104. package/dist/requirements/cloud-coverage.js +4 -4
  105. package/dist/requirements/index.d.ts +6 -0
  106. package/dist/requirements/index.js +24 -0
  107. package/dist/schema/browser.d.ts +1 -1
  108. package/dist/schema/browser.js +3 -1
  109. package/dist/schema/file-writer.d.ts +5 -0
  110. package/dist/schema/file-writer.js +4 -0
  111. package/dist/schema/parser.d.ts +11 -2
  112. package/dist/schema/parser.js +11 -7
  113. package/dist/schema/schemas.d.ts +40 -0
  114. package/dist/schema/schemas.js +4 -0
  115. package/dist/sync/execute.d.ts +10 -1
  116. package/dist/sync/execute.js +76 -24
  117. package/dist/sync/index.d.ts +1 -1
  118. package/dist/sync/index.js +1 -1
  119. package/dist/sync/publish.d.ts +7 -0
  120. package/dist/sync/publish.js +2 -2
  121. package/dist/sync/segment.js +4 -1
  122. package/dist/sync/snapshot.d.ts +27 -5
  123. package/dist/sync/snapshot.js +92 -15
  124. package/dist/sync/solution-documents.d.ts +64 -0
  125. package/dist/sync/solution-documents.js +97 -0
  126. package/dist/sync/types.d.ts +19 -1
  127. package/dist/utils/command-error.d.ts +19 -0
  128. package/dist/utils/command-error.js +44 -0
  129. package/dist/utils/oauth-flow.js +2 -2
  130. package/dist/utils/project-selector.js +4 -4
  131. package/dist/version-messages.d.ts +28 -0
  132. package/dist/version-messages.js +87 -0
  133. package/package.json +3 -2
  134. package/dist/config.d.ts +0 -5
  135. package/dist/config.js +0 -12
@@ -0,0 +1,337 @@
1
+ import { planOf } from "./derive.js";
2
+ /**
3
+ * The plan a scenario rehearses, refused when it names an outcome the plan
4
+ * doesn't have or leaves one of its inputs holding nothing: what a live
5
+ * rehearsal checks before it removes the scenario's earlier runs, so a
6
+ * mistake in the scenario never costs the run under review. An input the
7
+ * scenario doesn't name holds something when the earlier rehearsal it
8
+ * starts from holds it (REHEARSE-4.8).
9
+ */
10
+ export function checkScenario(scenario) {
11
+ const plan = planOf(scenario.plan);
12
+ for (const name of plan.inputs) {
13
+ if (scenario.inputs?.[name])
14
+ continue;
15
+ if (scenario.history?.outcomes.includes(name))
16
+ continue;
17
+ throw new Error(scenario.history
18
+ ? `The plan's input "${name}" holds nothing: solution ${scenario.history.solution} held no "${name}" when it made "${scenario.history.after}", so name its artifacts in the scenario.`
19
+ : `The plan's input "${name}" holds nothing: name its artifacts in the scenario.`);
20
+ }
21
+ return plan;
22
+ }
23
+ /**
24
+ * Who plays an agent at a part of an outcome's work in a scenario: the key
25
+ * naming that agent on that outcome (`Code/Code Reviewer`), then the key
26
+ * naming the part — the outcome's name for its drafter or whoever makes it
27
+ * from outside (`Pitch`), "<outcome>/reviewer" for its reviewers
28
+ * (`Pitch/reviewer`) — then the agent's own name (`Acceptance Tester`, or
29
+ * `Pitch/drafter` for one dedicated to its outcome), then whoever outside
30
+ * makes it (`release manager`). Unnamed, an agent is real. The key that
31
+ * matched, for counting turns.
32
+ */
33
+ export function partiesOf(parties) {
34
+ return (at) => {
35
+ const keys = [
36
+ ...(at.agent ? [`${at.outcome}/${at.agent}`] : []),
37
+ at.part === "review" ? `${at.outcome}/reviewer` : at.outcome,
38
+ ...(at.agent ? [at.agent] : []),
39
+ ...(at.party ? [at.party] : []),
40
+ ];
41
+ const key = keys.find((name) => parties[name] !== undefined);
42
+ return key ? { party: parties[key], key } : { party: "real", key: keys[0] };
43
+ };
44
+ }
45
+ /** Every outcome on the panel, in the plan's order. */
46
+ const outcomesOn = (panel) => (panel?.phases ?? []).flatMap((phase) => phase.outcomes);
47
+ /**
48
+ * The parts of the work an agent, or someone outside, could take now or is
49
+ * at, in the plan's order: a ready or drafting outcome's draft, a review of
50
+ * an outcome standing reviewing (each of its reviewers, since the panel
51
+ * doesn't say which have given their result), and an outcome waiting on
52
+ * whoever makes it from outside.
53
+ */
54
+ function partsDue(outcomes) {
55
+ return outcomes.flatMap((outcome) => {
56
+ if ((outcome.status === "ready" || outcome.status === "drafting") &&
57
+ outcome.drafter) {
58
+ return [{ outcome: outcome.name, part: "draft", agent: outcome.drafter }];
59
+ }
60
+ if (outcome.status === "reviewing") {
61
+ return (outcome.reviewers ?? []).map((agent) => ({
62
+ outcome: outcome.name,
63
+ part: "review",
64
+ agent,
65
+ }));
66
+ }
67
+ if (outcome.status === "awaiting external" ||
68
+ outcome.status === "awaiting collaboration") {
69
+ return [
70
+ {
71
+ outcome: outcome.name,
72
+ part: "outside",
73
+ ...(outcome.party ? { party: outcome.party } : {}),
74
+ },
75
+ ];
76
+ }
77
+ return [];
78
+ });
79
+ }
80
+ /**
81
+ * The driver (docs/working/rehearsing-steps.md): the orchestrator the
82
+ * product has as events, as a loop. Look at where every outcome stands;
83
+ * for each part of the work due, find who plays its agent; play a script
84
+ * through the same verbs an agent uses — `next` for the agent, then what
85
+ * its script records — or wait for a real one; answer decisions and
86
+ * checkpoints from the person's script; stop when nothing is ready, or
87
+ * when something needs a person nobody scripted, or an agent stalls.
88
+ */
89
+ export async function rehearse(client, scenario) {
90
+ const plan = checkScenario(scenario);
91
+ const inputs = Object.entries(scenario.inputs ?? {}).map(([name, things]) => ({ name, things }));
92
+ const solutionId = await client.create({
93
+ title: scenario.title,
94
+ scenario: scenario.file,
95
+ description: scenario.description,
96
+ plan,
97
+ inputs,
98
+ ...(scenario.branch ? { branch: scenario.branch } : {}),
99
+ ...(scenario.published?.length ? { published: scenario.published } : {}),
100
+ ...(scenario.history
101
+ ? {
102
+ history: {
103
+ solution: scenario.history.solution,
104
+ after: scenario.history.after,
105
+ },
106
+ }
107
+ : {}),
108
+ });
109
+ const partyFor = partiesOf(scenario.parties);
110
+ const played = {};
111
+ /** The script for this turn: the n-th of a list, the last repeating, counted by outcome and agent. */
112
+ const scriptFor = (at, party) => {
113
+ if (!Array.isArray(party))
114
+ return party;
115
+ const key = `${at.outcome}/${at.agent ?? at.part}`;
116
+ const n = played[key] ?? 0;
117
+ played[key] = n + 1;
118
+ return party[Math.min(n, party.length - 1)];
119
+ };
120
+ const asked = {};
121
+ /** The person's next scripted answer under a name, taken. */
122
+ const answerFor = (name) => {
123
+ const answers = scenario.person?.[name] ?? [];
124
+ const n = asked[name] ?? 0;
125
+ const answer = answers[n];
126
+ if (answer)
127
+ asked[name] = n + 1;
128
+ return answer;
129
+ };
130
+ const waited = {};
131
+ const waitMs = scenario.waitMs ?? 20 * 60 * 1000;
132
+ const pollMs = 15_000;
133
+ let ended = "nothing ready";
134
+ for (let turn = 0; turn < 500; turn++) {
135
+ const panel = await client.panel(solutionId);
136
+ const outcomes = outcomesOn(panel);
137
+ // A decision waiting: the person's script answers, or the rehearsal ends.
138
+ const deciding = outcomes.find((o) => o.status === "awaiting decision");
139
+ if (deciding) {
140
+ const decision = await client.openDecision?.(solutionId, deciding.name);
141
+ const answer = decision && client.answer ? answerFor(deciding.name) : undefined;
142
+ if (!answer || !decision || !client.answer) {
143
+ ended = `"${deciding.name}" waits on a decision nobody scripted`;
144
+ break;
145
+ }
146
+ await client.answer(decision.id, answer);
147
+ continue;
148
+ }
149
+ const stalled = outcomes.find((o) => o.status === "stalled");
150
+ if (stalled) {
151
+ ended = `"${stalled.name}" stalled: ${stalled.reason ?? "no reason given"}`;
152
+ break;
153
+ }
154
+ // A checkpoint waiting on the person: her scripted approval, or the end.
155
+ const checkpoint = panel?.checkpoints?.find((c) => c.stands === "deciding");
156
+ if (checkpoint) {
157
+ const answer = client.approveCheckpoint
158
+ ? answerFor(checkpoint.name)
159
+ : undefined;
160
+ if (!answer || !client.approveCheckpoint) {
161
+ ended = `the ${checkpoint.name} checkpoint waits on a decision nobody scripted`;
162
+ break;
163
+ }
164
+ if (answer.kind !== "approval" || answer.outcome !== "approved") {
165
+ throw new Error(`The person's script answers the ${checkpoint.name} checkpoint with an approval: { kind: approval, outcome: approved }, with any comment as her words.`);
166
+ }
167
+ await client.approveCheckpoint(solutionId, checkpoint.name, answer.comment);
168
+ continue;
169
+ }
170
+ // The parts due, scripted ones first: a script takes its agent's work
171
+ // through `next`, as the agent would.
172
+ const due = partsDue(outcomes).map((at) => ({ at, ...partyFor(at) }));
173
+ let acted = false;
174
+ for (const { at, party } of due) {
175
+ if (party === "real")
176
+ continue;
177
+ if (at.part === "outside") {
178
+ await playOutside(client, solutionId, at, scriptFor(at, party));
179
+ acted = true;
180
+ break;
181
+ }
182
+ const brief = await client.next({ solutionId, agent: at.agent });
183
+ if (brief.kind === "nothing ready")
184
+ continue;
185
+ const taken = takenPart(brief, at.agent);
186
+ const script = partyFor(taken);
187
+ if (script.party === "real") {
188
+ throw new Error(`${at.agent} is scripted for "${at.outcome}" but real for "${taken.outcome}", which it took first: script it for both, or neither.`);
189
+ }
190
+ await play(client, solutionId, taken, scriptFor(taken, script.party), brief);
191
+ acted = true;
192
+ break;
193
+ }
194
+ if (acted)
195
+ continue;
196
+ // Nothing a script could take: wait for a real agent at work or due.
197
+ const real = due
198
+ .filter(({ at, party }) => party === "real" && at.part !== "outside")
199
+ .map(({ at }) => ({ outcome: at.outcome, agent: at.agent }))[0];
200
+ if (!real) {
201
+ ended = "nothing ready";
202
+ break;
203
+ }
204
+ if (!client.sleep) {
205
+ throw new Error(`${real.agent} is real on "${real.outcome}", and only a live rehearsal can wait for a real agent.`);
206
+ }
207
+ const key = `${real.outcome}/${real.agent}`;
208
+ waited[key] = (waited[key] ?? 0) + pollMs;
209
+ if (waited[key] > waitMs) {
210
+ ended = `"${real.outcome}" waited ${Math.round(waitMs / 60000)} minutes for ${real.agent}`;
211
+ break;
212
+ }
213
+ await client.sleep(pollMs);
214
+ }
215
+ const reports = [];
216
+ for (const outcome of outcomesOn(await client.panel(solutionId))) {
217
+ const report = {
218
+ outcome: outcome.name,
219
+ status: outcome.status,
220
+ reason: outcome.reason,
221
+ things: await client.things(solutionId, outcome.name),
222
+ thread: await client.thread(solutionId, outcome.name),
223
+ };
224
+ if (client.agentLog) {
225
+ const agents = [
226
+ ...(outcome.drafter ? [outcome.drafter] : []),
227
+ ...(outcome.reviewers ?? []),
228
+ ];
229
+ const logs = {};
230
+ for (const agent of agents) {
231
+ const part = {
232
+ outcome: outcome.name,
233
+ part: agent === outcome.drafter ? "draft" : "review",
234
+ agent,
235
+ };
236
+ if (partyFor(part).party === "real") {
237
+ logs[agent] = await client.agentLog(solutionId, agent);
238
+ }
239
+ }
240
+ if (Object.keys(logs).length > 0)
241
+ report.agentLogs = logs;
242
+ }
243
+ reports.push(report);
244
+ }
245
+ return { solutionId, ended, outcomes: reports };
246
+ }
247
+ /** The part a brief hands the agent: the outcome it names, drafting or reviewing it. */
248
+ function takenPart(brief, agent) {
249
+ const step = brief.step;
250
+ const outcome = step?.outcome ?? step?.label;
251
+ if (!outcome) {
252
+ throw new Error(`${agent} was given a brief that names no outcome.`);
253
+ }
254
+ return { outcome, part: brief.kind === "review" ? "review" : "draft", agent };
255
+ }
256
+ /** A script for an agent: check the brief it was given as the agent would read it, then act. */
257
+ async function play(client, solutionId, at, script, brief) {
258
+ const agent = at.agent;
259
+ const { outcome } = at;
260
+ const where = `"${outcome}" (${agent}, ${at.part === "review" ? "reviewing" : "drafting"})`;
261
+ if (script.expect) {
262
+ try {
263
+ script.expect(brief);
264
+ }
265
+ catch (error) {
266
+ throw new Error(`${where}: the brief isn't what the script expected — ${error instanceof Error ? error.message : String(error)}`);
267
+ }
268
+ }
269
+ if (script.stall) {
270
+ await client.stall({ solutionId, agent, outcome, reason: script.stall });
271
+ return;
272
+ }
273
+ if (script.ask) {
274
+ await client.ask({ solutionId, agent, outcome, request: script.ask });
275
+ return;
276
+ }
277
+ // REHEARSE-1.1: a script records exactly as a real agent does, so an
278
+ // approval carries the summary the script gives (VERB-2.5) and none
279
+ // stands in for a missing one; the backend refuses it as it would the
280
+ // agent's.
281
+ if (at.part === "review") {
282
+ if (!script.review) {
283
+ throw new Error(`${where}: the script gives no review — give it review, ask or stall.`);
284
+ }
285
+ await client.complete({
286
+ solutionId,
287
+ agent,
288
+ outcome,
289
+ review: script.review,
290
+ });
291
+ return;
292
+ }
293
+ const made = await madeBy(client, script);
294
+ if (!made) {
295
+ throw new Error(`${where}: the script says nothing to do — give it made, scores, ask or stall.`);
296
+ }
297
+ await client.complete({
298
+ solutionId,
299
+ agent,
300
+ outcome,
301
+ made,
302
+ comment: script.comment,
303
+ summary: script.summary,
304
+ });
305
+ }
306
+ /** What a script submits: its things, or the scores it makes first, as `dotreq scores create` would. */
307
+ async function madeBy(client, script) {
308
+ if (script.made)
309
+ return script.made;
310
+ if (script.scores) {
311
+ return [
312
+ {
313
+ kind: "scores",
314
+ scoresId: await client.createScores(script.scores),
315
+ },
316
+ ];
317
+ }
318
+ return undefined;
319
+ }
320
+ /**
321
+ * A script for whoever makes an outcome from outside the product, or the
322
+ * owner with an agent: it closes the outcome with what it made, as their
323
+ * close would (AWAIT-7.1: a rehearsal's scripts play every party).
324
+ */
325
+ async function playOutside(client, solutionId, at, script) {
326
+ const made = await madeBy(client, script);
327
+ if (!made) {
328
+ throw new Error(`"${at.outcome}" (${at.party ?? "made outside"}): the script says nothing to do — give it made or scores.`);
329
+ }
330
+ await client.complete({
331
+ solutionId,
332
+ outcome: at.outcome,
333
+ made,
334
+ comment: script.comment,
335
+ });
336
+ }
337
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1,25 @@
1
+ import type { solutionClient } from "../commands/solution/client.js";
2
+ import type { EarlierRuns } from "./types.js";
3
+ /**
4
+ * A new rehearsal replaces the earlier runs of its scenario (REHEARSE-6):
5
+ * the rehearsal solutions in the project recorded as runs of the same
6
+ * scenario file are removed before it starts, with what they made. What any scenario in the
7
+ * same folder names as an input stays, so that scenario can still start
8
+ * from it; so does an earlier run whose history a scenario there starts
9
+ * from (6.2.0), whole.
10
+ */
11
+ /**
12
+ * What the scenarios in this scenario's folder name: the documents, boards
13
+ * and mockups they name as inputs, and the earlier rehearsals' solutions
14
+ * whose history they start from (REHEARSE-6.2.0), each by id, with the files
15
+ * that name it. Refuses when one of those files can't be read, since then
16
+ * what it names can't be known.
17
+ */
18
+ export declare function namedInputs(scenarioPath: string): Map<string, string[]>;
19
+ /**
20
+ * Remove the earlier runs of the scenario, keeping what a scenario names
21
+ * (REHEARSE-6.0, 6.2), and keeping whole an earlier run whose history a
22
+ * scenario starts from (6.2.0).
23
+ */
24
+ export declare function removeEarlierRuns(scenarioPath: string, client: ReturnType<typeof solutionClient>, pause?: (ms: number) => Promise<void>): Promise<EarlierRuns>;
25
+ //# sourceMappingURL=earlier.d.ts.map
@@ -0,0 +1,138 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
+ import { parse } from "yaml";
4
+ import { parseThing } from "../commands/solution/things.js";
5
+ import { api } from "../convex.js";
6
+ /**
7
+ * A new rehearsal replaces the earlier runs of its scenario (REHEARSE-6):
8
+ * the rehearsal solutions in the project recorded as runs of the same
9
+ * scenario file are removed before it starts, with what they made. What any scenario in the
10
+ * same folder names as an input stays, so that scenario can still start
11
+ * from it; so does an earlier run whose history a scenario there starts
12
+ * from (6.2.0), whole.
13
+ */
14
+ /**
15
+ * What the scenarios in this scenario's folder name: the documents, boards
16
+ * and mockups they name as inputs, and the earlier rehearsals' solutions
17
+ * whose history they start from (REHEARSE-6.2.0), each by id, with the files
18
+ * that name it. Refuses when one of those files can't be read, since then
19
+ * what it names can't be known.
20
+ */
21
+ export function namedInputs(scenarioPath) {
22
+ return namedInFolder(scenarioPath).inputs;
23
+ }
24
+ function namedInFolder(scenarioPath) {
25
+ const folder = dirname(resolve(scenarioPath));
26
+ const named = new Map();
27
+ const histories = new Map();
28
+ for (const name of readdirSync(folder).sort()) {
29
+ if (!name.endsWith(".yaml"))
30
+ continue;
31
+ let inputs;
32
+ try {
33
+ const file = parse(readFileSync(join(folder, name), "utf8"));
34
+ inputs = file?.inputs;
35
+ const source = file?.history?.solution;
36
+ if (typeof source === "string" && source.trim()) {
37
+ const id = source.trim();
38
+ histories.set(id, [...(histories.get(id) ?? []), name]);
39
+ }
40
+ for (const entries of Object.values((inputs ?? {}))) {
41
+ for (const entry of Array.isArray(entries) ? entries : []) {
42
+ if (typeof entry !== "string")
43
+ continue;
44
+ const thing = parseThing(entry);
45
+ const id = thing.kind === "document"
46
+ ? thing.documentId
47
+ : thing.kind === "board"
48
+ ? thing.boardId
49
+ : thing.kind === "mockup"
50
+ ? thing.mockupId
51
+ : null;
52
+ if (id)
53
+ named.set(id, [...(named.get(id) ?? []), name]);
54
+ }
55
+ }
56
+ }
57
+ catch (error) {
58
+ throw new Error(`Can't tell what ${name} names as its inputs, so no earlier run was removed: ${error instanceof Error ? error.message : String(error)} Fix it, or rehearse with --keep.`);
59
+ }
60
+ }
61
+ return { inputs: named, histories };
62
+ }
63
+ /**
64
+ * How long a rehearsal waits for an earlier run's branch to be deleted
65
+ * before it starts anyway: the deletion is the backend's, scheduled as the
66
+ * run is removed.
67
+ */
68
+ const BRANCH_WAIT_MS = 60_000;
69
+ /**
70
+ * Wait for the backend to delete an earlier run's branch (REHEARSE-6.7):
71
+ * it must be gone before the new run makes its own, or that one takes a
72
+ * suffixed name. What became of it, or pending when it took too long.
73
+ */
74
+ async function branchOutcome(client, removalId, pause) {
75
+ const deadline = Date.now() + BRANCH_WAIT_MS;
76
+ for (;;) {
77
+ const removal = await client.query(api.solutions.branches.removal, { removalId });
78
+ if (!removal)
79
+ return null;
80
+ if (removal.outcome) {
81
+ return {
82
+ name: removal.branch,
83
+ outcome: removal.outcome,
84
+ ...(removal.why ? { why: removal.why } : {}),
85
+ };
86
+ }
87
+ if (Date.now() >= deadline) {
88
+ return { name: removal.branch, outcome: "pending" };
89
+ }
90
+ await pause(1000);
91
+ }
92
+ }
93
+ /**
94
+ * Remove the earlier runs of the scenario, keeping what a scenario names
95
+ * (REHEARSE-6.0, 6.2), and keeping whole an earlier run whose history a
96
+ * scenario starts from (6.2.0).
97
+ */
98
+ export async function removeEarlierRuns(scenarioPath, client, pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
99
+ const { inputs: named, histories } = namedInFolder(scenarioPath);
100
+ const scenario = basename(scenarioPath);
101
+ const earlier = await client.query(api.solutions.queries.rehearsalsOf, { project: client.project, scenario });
102
+ const kept = [];
103
+ const keptRuns = [];
104
+ const branches = [];
105
+ let removedRuns = 0;
106
+ for (const solutionId of earlier) {
107
+ const startFromIt = histories.get(solutionId);
108
+ if (startFromIt) {
109
+ keptRuns.push({ solutionId, namedBy: startFromIt });
110
+ continue;
111
+ }
112
+ removedRuns++;
113
+ const removed = await client.mutation(api.solutions.mutations.removeRehearsal, {
114
+ solutionId,
115
+ keep: [...named.keys()],
116
+ });
117
+ // Each of its branches, one per pull-request surface (SOLBRANCH-1.4).
118
+ for (const removalId of removed.branchRemovals ?? []) {
119
+ const branch = await branchOutcome(client, removalId, pause);
120
+ if (branch)
121
+ branches.push(branch);
122
+ }
123
+ for (const thing of removed.kept) {
124
+ kept.push({
125
+ reference: `${thing.kind}:${thing.id}`,
126
+ namedBy: thing.because === "named" ? (named.get(thing.id) ?? []) : [],
127
+ });
128
+ }
129
+ }
130
+ return {
131
+ scenario,
132
+ removed: removedRuns,
133
+ kept,
134
+ ...(keptRuns.length > 0 ? { keptRuns } : {}),
135
+ ...(branches.length > 0 ? { branches } : {}),
136
+ };
137
+ }
138
+ //# sourceMappingURL=earlier.js.map
@@ -0,0 +1,5 @@
1
+ export { planForOutcomes, planOf } from "./derive.js";
2
+ export { partiesOf, rehearse } from "./driver.js";
3
+ export { renderReport } from "./report.js";
4
+ export type * from "./types.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ export { planForOutcomes, planOf } from "./derive.js";
2
+ export { partiesOf, rehearse } from "./driver.js";
3
+ export { renderReport } from "./report.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,72 @@
1
+ import { solutionClient } from "../commands/solution/client.js";
2
+ import type { Party, PersonScript, RehearsalClient, Scenario } from "./types.js";
3
+ /**
4
+ * The live mode (REHEARSE-4): the driver over the CLI's client against the
5
+ * linked project. Scripted agents run through the same verbs an agent
6
+ * runs; real agents are the runner's, awaited; the person's decisions are
7
+ * answered from the scenario's own script, and the rehearsal stops only at
8
+ * a decision nobody scripted.
9
+ */
10
+ /** The scenario as a file: plans and artifacts by name, scripts as data. */
11
+ export type ScenarioFile = {
12
+ title: string;
13
+ /** What the person wrote about the solution beyond its title, as Markdown. */
14
+ description?: string;
15
+ /**
16
+ * "default" for the project's plan; a path to a plan file; or some of
17
+ * either's outcomes, run on their own: `{ from: default, outcomes: [Code,
18
+ * Demo] }`, or `outcome: Pitch` for one. `step` and `steps` are read as
19
+ * `outcome` and `outcomes`.
20
+ */
21
+ plan: "default" | string | {
22
+ from?: "default" | string;
23
+ outcome?: string;
24
+ outcomes?: string[];
25
+ step?: string;
26
+ steps?: string[];
27
+ };
28
+ /**
29
+ * The branch the solution's branch is made from, rather than the
30
+ * repository's default branch (REHEARSE-4.5): `branch: rehearsal-fixtures/spec`.
31
+ */
32
+ branch?: string;
33
+ /**
34
+ * An earlier rehearsal's solution and an outcome it made, whose history
35
+ * the solution starts with (REHEARSE-4.8):
36
+ * `history: { solution: q97fe7s7…, after: Code }`. Each input `inputs`
37
+ * doesn't name is taken from that solution as it stood when that outcome
38
+ * was made, with its thread.
39
+ */
40
+ history?: {
41
+ solution?: unknown;
42
+ after?: unknown;
43
+ };
44
+ /**
45
+ * Artifacts by input name: references; `image:<path>` for an image, its
46
+ * path from the scenario's folder, to upload first (REHEARSE-4.7); or
47
+ * { file } to publish first.
48
+ */
49
+ inputs?: Record<string, Array<string | {
50
+ file: string;
51
+ title?: string;
52
+ }>>;
53
+ /**
54
+ * By agent (`Code Reviewer`, `Pitch/drafter`), by an outcome's name (its
55
+ * drafter), by "<outcome>/reviewer" (its reviewers), or by
56
+ * "<outcome>/<agent>": "real", or a script as data (`Scenario.parties`).
57
+ */
58
+ parties?: Record<string, "real" | Omit<Party, "expect">>;
59
+ /** What the person answers, by an outcome's name or a checkpoint's, in order asked. */
60
+ person?: PersonScript;
61
+ /** Minutes to wait for a real agent. */
62
+ wait?: number;
63
+ };
64
+ /** The scenario file read into a scenario the driver runs. */
65
+ export declare function scenarioFrom(path: string, client: ReturnType<typeof solutionClient>): Promise<Scenario>;
66
+ /**
67
+ * The driver's client over the CLI: the project secret for every call. The
68
+ * solution is created as a rehearsal's, which is what lets that same secret
69
+ * answer the decisions the scenario scripted.
70
+ */
71
+ export declare function liveClient(): RehearsalClient;
72
+ //# sourceMappingURL=live.d.ts.map