@popoverai/dotrequirements 0.33.0 → 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 (115) hide show
  1. package/dist/cli.js +13 -1
  2. package/dist/cloud.d.ts +5 -1
  3. package/dist/cloud.js +6 -2
  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/diff.js +15 -5
  9. package/dist/commands/mockup/index.d.ts +11 -0
  10. package/dist/commands/mockup/index.js +82 -0
  11. package/dist/commands/scores/index.d.ts +50 -0
  12. package/dist/commands/scores/index.js +190 -0
  13. package/dist/commands/solution/ask.d.ts +9 -0
  14. package/dist/commands/solution/ask.js +27 -0
  15. package/dist/commands/solution/author.d.ts +19 -0
  16. package/dist/commands/solution/author.js +202 -0
  17. package/dist/commands/solution/branch.d.ts +103 -0
  18. package/dist/commands/solution/branch.js +341 -0
  19. package/dist/commands/solution/brief.d.ts +81 -0
  20. package/dist/commands/solution/brief.js +47 -0
  21. package/dist/commands/solution/checkout.d.ts +27 -0
  22. package/dist/commands/solution/checkout.js +66 -0
  23. package/dist/commands/solution/client.d.ts +16 -0
  24. package/dist/commands/solution/client.js +46 -0
  25. package/dist/commands/solution/contract.d.ts +9 -0
  26. package/dist/commands/solution/contract.js +23 -0
  27. package/dist/commands/solution/create.d.ts +14 -0
  28. package/dist/commands/solution/create.js +52 -0
  29. package/dist/commands/solution/demo-script.d.ts +28 -0
  30. package/dist/commands/solution/demo-script.js +43 -0
  31. package/dist/commands/solution/image.d.ts +60 -0
  32. package/dist/commands/solution/image.js +148 -0
  33. package/dist/commands/solution/index.d.ts +12 -0
  34. package/dist/commands/solution/index.js +281 -0
  35. package/dist/commands/solution/look-up.d.ts +9 -0
  36. package/dist/commands/solution/look-up.js +69 -0
  37. package/dist/commands/solution/next.d.ts +12 -0
  38. package/dist/commands/solution/next.js +48 -0
  39. package/dist/commands/solution/plan-file.d.ts +22 -0
  40. package/dist/commands/solution/plan-file.js +33 -0
  41. package/dist/commands/solution/preview-screenshot.d.ts +21 -0
  42. package/dist/commands/solution/preview-screenshot.js +56 -0
  43. package/dist/commands/solution/pull-request.d.ts +25 -0
  44. package/dist/commands/solution/pull-request.js +117 -0
  45. package/dist/commands/solution/pushing.d.ts +66 -0
  46. package/dist/commands/solution/pushing.js +185 -0
  47. package/dist/commands/solution/rehearse.d.ts +10 -0
  48. package/dist/commands/solution/rehearse.js +24 -0
  49. package/dist/commands/solution/request.d.ts +3 -0
  50. package/dist/commands/solution/request.js +13 -0
  51. package/dist/commands/solution/review.d.ts +27 -0
  52. package/dist/commands/solution/review.js +104 -0
  53. package/dist/commands/solution/run-file.d.ts +42 -0
  54. package/dist/commands/solution/run-file.js +63 -0
  55. package/dist/commands/solution/share.d.ts +62 -0
  56. package/dist/commands/solution/share.js +346 -0
  57. package/dist/commands/solution/stall.d.ts +14 -0
  58. package/dist/commands/solution/stall.js +41 -0
  59. package/dist/commands/solution/start.d.ts +11 -0
  60. package/dist/commands/solution/start.js +20 -0
  61. package/dist/commands/solution/status.d.ts +39 -0
  62. package/dist/commands/solution/status.js +145 -0
  63. package/dist/commands/solution/summary.d.ts +13 -0
  64. package/dist/commands/solution/summary.js +5 -0
  65. package/dist/commands/solution/things.d.ts +118 -0
  66. package/dist/commands/solution/things.js +78 -0
  67. package/dist/commands/solution/thread.d.ts +13 -0
  68. package/dist/commands/solution/thread.js +53 -0
  69. package/dist/commands/solution/upload.d.ts +24 -0
  70. package/dist/commands/solution/upload.js +27 -0
  71. package/dist/commands/solution/video.d.ts +88 -0
  72. package/dist/commands/solution/video.js +364 -0
  73. package/dist/commands/solution/working-folder.d.ts +44 -0
  74. package/dist/commands/solution/working-folder.js +64 -0
  75. package/dist/commands/sync.d.ts +2 -1
  76. package/dist/commands/sync.js +21 -5
  77. package/dist/convex.d.ts +54 -0
  78. package/dist/convex.js +64 -0
  79. package/dist/push/core.js +7 -2
  80. package/dist/rehearsal/derive.d.ts +23 -0
  81. package/dist/rehearsal/derive.js +112 -0
  82. package/dist/rehearsal/driver.d.ts +44 -0
  83. package/dist/rehearsal/driver.js +337 -0
  84. package/dist/rehearsal/earlier.d.ts +25 -0
  85. package/dist/rehearsal/earlier.js +138 -0
  86. package/dist/rehearsal/index.d.ts +5 -0
  87. package/dist/rehearsal/index.js +4 -0
  88. package/dist/rehearsal/live.d.ts +72 -0
  89. package/dist/rehearsal/live.js +301 -0
  90. package/dist/rehearsal/report.d.ts +4 -0
  91. package/dist/rehearsal/report.js +69 -0
  92. package/dist/rehearsal/types.d.ts +377 -0
  93. package/dist/rehearsal/types.js +8 -0
  94. package/dist/requirements/index.d.ts +6 -0
  95. package/dist/requirements/index.js +24 -0
  96. package/dist/schema/browser.d.ts +1 -1
  97. package/dist/schema/browser.js +3 -1
  98. package/dist/schema/file-writer.d.ts +5 -0
  99. package/dist/schema/file-writer.js +4 -0
  100. package/dist/schema/parser.d.ts +11 -2
  101. package/dist/schema/parser.js +11 -7
  102. package/dist/schema/schemas.d.ts +40 -0
  103. package/dist/schema/schemas.js +4 -0
  104. package/dist/sync/execute.d.ts +10 -1
  105. package/dist/sync/execute.js +69 -5
  106. package/dist/sync/index.d.ts +1 -1
  107. package/dist/sync/index.js +1 -1
  108. package/dist/sync/publish.d.ts +7 -0
  109. package/dist/sync/segment.js +4 -1
  110. package/dist/sync/snapshot.d.ts +23 -1
  111. package/dist/sync/snapshot.js +75 -3
  112. package/dist/sync/solution-documents.d.ts +64 -0
  113. package/dist/sync/solution-documents.js +97 -0
  114. package/dist/sync/types.d.ts +18 -0
  115. package/package.json +3 -2
@@ -0,0 +1,341 @@
1
+ import { spawnSync } from "node:child_process";
2
+ export function git(cwd, args) {
3
+ const ran = spawnSync("git", args, {
4
+ cwd,
5
+ encoding: "utf8",
6
+ // A push that wants a password fails rather than waiting for one.
7
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
8
+ });
9
+ return {
10
+ code: ran.status ?? 1,
11
+ stdout: ran.stdout ?? "",
12
+ stderr: ran.stderr ?? "",
13
+ };
14
+ }
15
+ /** Git's name for no commit: a push's `before` when the branch wasn't in the repository. */
16
+ export const NO_COMMIT = "0000000000000000000000000000000000000000";
17
+ /**
18
+ * The branch the checkout is on, or null when it is on no branch — a
19
+ * single commit checked out, or no git repository at all (VERB-1.4, 1.5).
20
+ */
21
+ export function currentBranch(cwd) {
22
+ const ran = git(cwd, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
23
+ return ran.code === 0 ? ran.stdout.trim() || null : null;
24
+ }
25
+ /** The top of the repository the checkout is in, or null outside one. */
26
+ export function repositoryRoot(cwd) {
27
+ const ran = git(cwd, ["rev-parse", "--show-toplevel"]);
28
+ return ran.code === 0 ? ran.stdout.trim() || null : null;
29
+ }
30
+ /** The commit the checkout is at, or null outside a git repository. */
31
+ export function headCommit(cwd) {
32
+ const ran = git(cwd, ["rev-parse", "--verify", "--quiet", "HEAD"]);
33
+ return ran.code === 0 ? ran.stdout.trim() || null : null;
34
+ }
35
+ function commitOf(cwd, ref) {
36
+ const ran = git(cwd, ["rev-parse", "--verify", "--quiet", ref]);
37
+ return ran.code === 0 ? ran.stdout.trim() || null : null;
38
+ }
39
+ function isAncestor(cwd, a, b) {
40
+ return git(cwd, ["merge-base", "--is-ancestor", a, b]).code === 0;
41
+ }
42
+ /**
43
+ * What the checkout has not committed: files git tracks with changes,
44
+ * staged or not, and new files it does not ignore, one by one.
45
+ */
46
+ export function uncommitted(cwd) {
47
+ const ran = git(cwd, [
48
+ "status",
49
+ "--porcelain=v1",
50
+ "-z",
51
+ "--untracked-files=all",
52
+ ]);
53
+ const changed = [];
54
+ const untracked = [];
55
+ const entries = ran.stdout.split("\0");
56
+ for (let i = 0; i < entries.length; i++) {
57
+ const entry = entries[i];
58
+ if (entry.length < 4)
59
+ continue;
60
+ const status = entry.slice(0, 2);
61
+ const path = entry.slice(3);
62
+ if (status === "??") {
63
+ untracked.push(path);
64
+ continue;
65
+ }
66
+ changed.push(path);
67
+ // A rename or copy names where it came from next; that path changed too.
68
+ if (status[0] === "R" || status[0] === "C") {
69
+ const from = entries[++i];
70
+ if (from)
71
+ changed.push(from);
72
+ }
73
+ }
74
+ return { changed, untracked };
75
+ }
76
+ /** The remote-tracking ref a solution's branch is fetched into. */
77
+ function upstreamOf(branch) {
78
+ return `refs/remotes/origin/${branch}`;
79
+ }
80
+ /** Fetch the branch as the repository has it now; false when the repository has no such branch. */
81
+ function fetchBranch(cwd, branch) {
82
+ const ran = git(cwd, [
83
+ "fetch",
84
+ "--quiet",
85
+ "origin",
86
+ `+refs/heads/${branch}:${upstreamOf(branch)}`,
87
+ ]);
88
+ if (ran.code === 0)
89
+ return { ok: true, there: true };
90
+ if (/couldn't find remote ref/i.test(ran.stderr)) {
91
+ // Gone from the repository: so is what the checkout remembered of it.
92
+ git(cwd, ["update-ref", "-d", upstreamOf(branch)]);
93
+ return { ok: true, there: false };
94
+ }
95
+ return { ok: false, problem: lastLines(ran.stderr || ran.stdout) };
96
+ }
97
+ /**
98
+ * Bring the branch's new commits into the checkout, the agent's own
99
+ * commits placed after them (SOLBRANCH-2, 3.1). When they conflict — with
100
+ * a commit of the agent's, or with a change it has not committed — the
101
+ * checkout is left exactly as it was, and the files that conflict are
102
+ * named (SOLBRANCH-2.3, 3.2). What it has not committed stays uncommitted.
103
+ */
104
+ export function bringIn(cwd, branch) {
105
+ const fetched = fetchBranch(cwd, branch);
106
+ if (!fetched.ok)
107
+ return fetched;
108
+ if (!fetched.there)
109
+ return { ok: true };
110
+ const upstream = upstreamOf(branch);
111
+ if (isAncestor(cwd, upstream, "HEAD"))
112
+ return { ok: true };
113
+ // A change not committed can't be carried over a commit that touches the
114
+ // same file without conflicting, so that is a conflict before anything
115
+ // moves; nor can a new file the branch now adds.
116
+ const incoming = new Set(git(cwd, ["diff", "--name-only", "-z", `HEAD...${upstream}`])
117
+ .stdout.split("\0")
118
+ .filter(Boolean));
119
+ const { changed, untracked } = uncommitted(cwd);
120
+ const clashing = [...changed, ...untracked].filter((path) => incoming.has(path));
121
+ if (clashing.length > 0)
122
+ return { ok: false, conflicts: clashing.sort() };
123
+ if (isAncestor(cwd, "HEAD", upstream)) {
124
+ const merged = git(cwd, ["merge", "--ff-only", "--quiet", upstream]);
125
+ return merged.code === 0
126
+ ? { ok: true }
127
+ : { ok: false, problem: lastLines(merged.stderr || merged.stdout) };
128
+ }
129
+ const rebased = git(cwd, ["rebase", "--autostash", "--quiet", upstream]);
130
+ if (rebased.code === 0)
131
+ return { ok: true };
132
+ const conflicts = git(cwd, ["diff", "--name-only", "--diff-filter=U", "-z"])
133
+ .stdout.split("\0")
134
+ .filter(Boolean)
135
+ .sort();
136
+ // Undoing the rebase puts back the agent's commits and, from the
137
+ // autostash, what it had not committed.
138
+ git(cwd, ["rebase", "--abort"]);
139
+ return conflicts.length > 0
140
+ ? { ok: false, conflicts }
141
+ : { ok: false, problem: lastLines(rebased.stderr || rebased.stdout) };
142
+ }
143
+ /**
144
+ * Push the checkout's commits to the solution's branch, after bringing in
145
+ * what other agents pushed since (SOLBRANCH-3.1). A push that loses a race
146
+ * with another agent's tries again; one the repository refuses comes back
147
+ * with the reason it gave (3.3).
148
+ */
149
+ export function pushTo(cwd, branch) {
150
+ for (let attempt = 0; attempt < 3; attempt++) {
151
+ const brought = bringIn(cwd, branch);
152
+ if (!brought.ok) {
153
+ return "conflicts" in brought
154
+ ? { kind: "conflict", files: brought.conflicts }
155
+ : { kind: "refused", reason: brought.problem };
156
+ }
157
+ const before = commitOf(cwd, upstreamOf(branch));
158
+ const after = headCommit(cwd);
159
+ if (!after)
160
+ return { kind: "nothing to push" };
161
+ if (before && isAncestor(cwd, "HEAD", before)) {
162
+ return { kind: "nothing to push" };
163
+ }
164
+ const pushed = git(cwd, [
165
+ "push",
166
+ "--porcelain",
167
+ "origin",
168
+ `HEAD:refs/heads/${branch}`,
169
+ ]);
170
+ if (pushed.code === 0) {
171
+ return { kind: "pushed", before: before ?? NO_COMMIT, after };
172
+ }
173
+ if (/\[rejected\].*\((fetch first|non-fast-forward)\)/.test(pushed.stdout)) {
174
+ continue;
175
+ }
176
+ return { kind: "refused", reason: refusalOf(pushed) };
177
+ }
178
+ return {
179
+ kind: "refused",
180
+ reason: `other agents kept pushing to ${branch} while this one tried to`,
181
+ };
182
+ }
183
+ /** What the repository said when it refused a push: its own lines, else git's reason. */
184
+ function refusalOf(ran) {
185
+ const said = ran.stderr
186
+ .split("\n")
187
+ .filter((line) => line.startsWith("remote:"))
188
+ .map((line) => line
189
+ .replace(/^remote:\s*/, "")
190
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: the escape starts colour codes a remote sends
191
+ .replace(/\u001b\[[0-9;]*m/g, "")
192
+ .trim())
193
+ .filter(Boolean);
194
+ if (said.length > 0)
195
+ return said.join(" ");
196
+ const reason = ran.stdout.match(/\[(?:remote )?rejected\]\s*\(([^)]+)\)/);
197
+ return reason ? reason[1] : lastLines(ran.stderr || ran.stdout);
198
+ }
199
+ function lastLines(text) {
200
+ return text.trim().split("\n").slice(-3).join(" ").trim();
201
+ }
202
+ /** The names of the repository's branches, as it has them now. */
203
+ export function remoteBranches(cwd) {
204
+ const ran = git(cwd, ["ls-remote", "--heads", "origin"]);
205
+ if (ran.code !== 0) {
206
+ throw new Error(`Couldn't list the repository's branches: ${lastLines(ran.stderr)}`);
207
+ }
208
+ return new Set(ran.stdout
209
+ .split("\n")
210
+ .map((line) => line.split("\t")[1]?.replace(/^refs\/heads\//, ""))
211
+ .filter((name) => !!name));
212
+ }
213
+ /**
214
+ * The name for a solution's new branch: `dotreq/<directory>`, or, when a
215
+ * branch already has that name, the lowest suffix from -2 up that none has
216
+ * (SOLBRANCH-1.0, 1.1).
217
+ */
218
+ export function freeName(directory, taken) {
219
+ const base = `dotreq/${directory}`;
220
+ if (!taken.has(base))
221
+ return base;
222
+ for (let n = 2;; n++) {
223
+ if (!taken.has(`${base}-${n}`))
224
+ return `${base}-${n}`;
225
+ }
226
+ }
227
+ /** The default branch's commit, as the repository has it now, fetched into the checkout. */
228
+ export function defaultBranchCommit(cwd) {
229
+ const fetched = git(cwd, ["fetch", "--quiet", "origin", "HEAD"]);
230
+ const commit = fetched.code === 0 ? commitOf(cwd, "FETCH_HEAD") : null;
231
+ if (!commit) {
232
+ throw new Error(`Couldn't read the repository's default branch: ${lastLines(fetched.stderr)}`);
233
+ }
234
+ return commit;
235
+ }
236
+ /**
237
+ * The commit a named branch is at, as the repository has it now, fetched
238
+ * into the checkout: what a solution's branch is made from when a
239
+ * rehearsal's scenario names the branch to start from (REHEARSE-4.5).
240
+ */
241
+ export function branchCommit(cwd, branch) {
242
+ const fetched = fetchBranch(cwd, branch);
243
+ if (!fetched.ok) {
244
+ throw new Error(`Couldn't fetch ${branch}: ${fetched.problem}`);
245
+ }
246
+ const commit = fetched.there ? commitOf(cwd, upstreamOf(branch)) : null;
247
+ if (!commit) {
248
+ throw new Error(`The branch ${branch}, which the solution's branch is to be made from, isn't in the repository.`);
249
+ }
250
+ return commit;
251
+ }
252
+ /**
253
+ * The commit the repository's branch `name` is at, making the branch at
254
+ * `base` first when the repository has none of that name (SOLBRANCH-1.0,
255
+ * 1.3). A branch of that name that appears first — another of the
256
+ * solution's first sessions making it a moment before, or anyone's — is
257
+ * never moved: its commit is read and returned, for the caller to judge
258
+ * whose it is.
259
+ */
260
+ export function branchHead(cwd, name, base) {
261
+ for (let attempt = 0; attempt < 3; attempt++) {
262
+ const fetched = fetchBranch(cwd, name);
263
+ if (!fetched.ok) {
264
+ throw new Error(`Couldn't fetch ${name}: ${fetched.problem}`);
265
+ }
266
+ if (fetched.there) {
267
+ const head = commitOf(cwd, upstreamOf(name));
268
+ if (head)
269
+ return head;
270
+ }
271
+ // Another session may have fetched a newer default branch and claimed
272
+ // first; its commit is the one to make the branch from.
273
+ if (commitOf(cwd, `${base}^{commit}`) === null) {
274
+ const got = git(cwd, ["fetch", "--quiet", "origin", base]);
275
+ if (got.code !== 0) {
276
+ throw new Error(`Couldn't fetch ${base}, which the solution's branch is made from: ${lastLines(got.stderr)}`);
277
+ }
278
+ }
279
+ // Made only if no branch has the name; one that got there first is
280
+ // read on the next pass.
281
+ const pushed = git(cwd, [
282
+ "push",
283
+ "--porcelain",
284
+ `--force-with-lease=refs/heads/${name}:`,
285
+ "origin",
286
+ `${base}:refs/heads/${name}`,
287
+ ]);
288
+ if (pushed.code === 0)
289
+ return base;
290
+ if (!/\[rejected\]|already exists/.test(pushed.stdout + pushed.stderr)) {
291
+ throw new Error(`The repository refused to make the branch ${name}: ${refusalOf(pushed)}`);
292
+ }
293
+ }
294
+ throw new Error(`Couldn't make the branch ${name}: it kept changing while this session read it.`);
295
+ }
296
+ /**
297
+ * Put the checkout on the solution's branch, and bring in what other steps
298
+ * pushed to it (SOLBRANCH-1.2, 2). Returns what the agent should be told,
299
+ * or null when there is nothing to tell: its checkout is on the branch and
300
+ * holds everything pushed to it.
301
+ */
302
+ export function checkOut(cwd, branch) {
303
+ const fetched = fetchBranch(cwd, branch);
304
+ if (!fetched.ok) {
305
+ throw new Error(`Couldn't fetch ${branch}: ${fetched.problem}`);
306
+ }
307
+ if (!fetched.there) {
308
+ throw new Error(`The solution's branch ${branch} is no longer in the repository.`);
309
+ }
310
+ const current = currentBranch(cwd);
311
+ if (current !== branch) {
312
+ const local = commitOf(cwd, `refs/heads/${branch}`) !== null;
313
+ const switched = local
314
+ ? git(cwd, ["checkout", "--quiet", branch])
315
+ : git(cwd, [
316
+ "checkout",
317
+ "--quiet",
318
+ "-b",
319
+ branch,
320
+ "--no-track",
321
+ upstreamOf(branch),
322
+ ]);
323
+ if (switched.code !== 0) {
324
+ return [
325
+ `Your checkout is on ${current ?? "no branch"}, not the solution's branch ${branch}, and switching to it would overwrite changes you haven't committed: ${lastLines(switched.stderr)}`,
326
+ `Switch to ${branch} before you record anything: what you record is pushed to ${branch}.`,
327
+ ].join("\n");
328
+ }
329
+ }
330
+ const brought = bringIn(cwd, branch);
331
+ if (brought.ok)
332
+ return null;
333
+ if ("problem" in brought) {
334
+ throw new Error(`Couldn't bring in ${branch}'s new commits: ${brought.problem}`);
335
+ }
336
+ return [
337
+ `Other agents pushed commits to ${branch} that conflict with work you left in your checkout, in ${brought.conflicts.join(", ")}.`,
338
+ `Your checkout is as you left it. Bring the branch's commits in before you record anything (git pull --rebase --autostash origin ${branch}), and resolve the conflicts.`,
339
+ ].join("\n");
340
+ }
341
+ //# sourceMappingURL=branch.js.map
@@ -0,0 +1,81 @@
1
+ /**
2
+ * What `dotreq solution next` prints. The brief for an outcome is composed
3
+ * in the backend, entirely from blocks (docs/working/prompt-blocks.md), and
4
+ * arrives as `text`; the CLI prints it as given and renders nothing of its
5
+ * own. The shapes mirror what `solutions/mutations:next` and
6
+ * `solutions/queries:getPlan` return, as far as the CLI reads them (the CLI
7
+ * doesn't import the backend's types).
8
+ */
9
+ /**
10
+ * An outcome to draft (`step`) or to review (`review`): the outcome, the
11
+ * brief the agent reads, and the outcomes it reviews and works from, whose
12
+ * images and demo scripts `next` saves in the checkout (SIGHT-9.3, 13.1).
13
+ */
14
+ export type Brief = {
15
+ kind: "step" | "review";
16
+ /** The outcome the agent took; `label` is its name too. */
17
+ step: {
18
+ label: string;
19
+ outcome?: string;
20
+ };
21
+ text: string;
22
+ reviewing?: BriefOutcome[];
23
+ against?: BriefOutcome[];
24
+ };
25
+ /** An outcome the brief lists, as far as the CLI reads it. */
26
+ export type BriefOutcome = {
27
+ name: string;
28
+ things?: Array<{
29
+ kind: string;
30
+ storageId?: string;
31
+ name?: string;
32
+ script?: string;
33
+ }>;
34
+ };
35
+ /** An outcome on the plan panel, as far as the CLI reads it. */
36
+ export type PanelOutcome = {
37
+ name: string;
38
+ /** The one word it stands in: ready, drafting, reviewing, awaiting decision, stalled, done... */
39
+ status: string;
40
+ /** The agent that drafts it, when an agent of ours does. */
41
+ drafter?: string;
42
+ /** The agents that review it. */
43
+ reviewers?: string[];
44
+ /** Why one of its agents stalled. */
45
+ reason?: string;
46
+ /** Who it waits on, when someone outside creates it. */
47
+ party?: string;
48
+ /** For an outcome not started: what it can't begin without. */
49
+ waitingFor?: string[];
50
+ /** Its drafter's work was set aside and its new draft isn't taken yet: a session at it owes nothing more (CHECKPOINT-6.3.2.3). */
51
+ setAside?: boolean;
52
+ };
53
+ /** A checkpoint on the plan panel, as far as the CLI reads it. */
54
+ export type PanelCheckpoint = {
55
+ name: string;
56
+ /** waiting, deciding (on the owner now), fixing, deferred or approved. */
57
+ stands: string;
58
+ };
59
+ /** The plan panel (`getPlan`): its outcomes by phase, and its checkpoints. */
60
+ export type Panel = {
61
+ title: string;
62
+ phases: Array<{
63
+ name: string;
64
+ outcomes: PanelOutcome[];
65
+ }>;
66
+ checkpoints?: PanelCheckpoint[];
67
+ };
68
+ export type Standing = {
69
+ kind: "nothing ready";
70
+ plan: Panel;
71
+ };
72
+ export type NextResult = Brief | Standing;
73
+ export declare const EXIT: {
74
+ /** Nothing is ready: a loop of `next` can stop here. */
75
+ readonly nothingReady: 3;
76
+ };
77
+ /** Every outcome on the panel, in the plan's order. */
78
+ export declare function outcomesOf(panel: Pick<Panel, "phases">): PanelOutcome[];
79
+ /** Nothing ready: every outcome and what it waits on, then any checkpoint the owner is at. */
80
+ export declare function renderStanding({ plan }: Standing): string;
81
+ //# sourceMappingURL=brief.d.ts.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * What `dotreq solution next` prints. The brief for an outcome is composed
3
+ * in the backend, entirely from blocks (docs/working/prompt-blocks.md), and
4
+ * arrives as `text`; the CLI prints it as given and renders nothing of its
5
+ * own. The shapes mirror what `solutions/mutations:next` and
6
+ * `solutions/queries:getPlan` return, as far as the CLI reads them (the CLI
7
+ * doesn't import the backend's types).
8
+ */
9
+ export const EXIT = {
10
+ /** Nothing is ready: a loop of `next` can stop here. */
11
+ nothingReady: 3,
12
+ };
13
+ /** Every outcome on the panel, in the plan's order. */
14
+ export function outcomesOf(panel) {
15
+ return panel.phases.flatMap((phase) => phase.outcomes);
16
+ }
17
+ /** Nothing ready: every outcome and what it waits on, then any checkpoint the owner is at. */
18
+ export function renderStanding({ plan }) {
19
+ const lines = [`Nothing is ready on "${plan.title}".`, ""];
20
+ for (const outcome of outcomesOf(plan)) {
21
+ lines.push(`- ${outcome.name}: ${standing(outcome)}`);
22
+ }
23
+ for (const checkpoint of plan.checkpoints ?? []) {
24
+ const stands = CHECKPOINT_STANDS[checkpoint.stands];
25
+ if (stands)
26
+ lines.push(`- The ${checkpoint.name} checkpoint: ${stands}`);
27
+ }
28
+ return `${lines.join("\n")}\n`;
29
+ }
30
+ /** A checkpoint worth a line when nothing is ready: one that holds the work up. */
31
+ const CHECKPOINT_STANDS = {
32
+ deciding: "waiting on the owner's decision",
33
+ fixing: "a fix is running",
34
+ deferred: "deferred by the owner",
35
+ };
36
+ function standing(outcome) {
37
+ if (outcome.status === "stalled" && outcome.reason)
38
+ return `stalled — ${outcome.reason}`;
39
+ if (outcome.status === "awaiting external" && outcome.party) {
40
+ return `awaiting ${outcome.party}`;
41
+ }
42
+ if (outcome.status === "not started" && outcome.waitingFor?.length) {
43
+ return `not started — after ${outcome.waitingFor.join(", ")}`;
44
+ }
45
+ return outcome.status;
46
+ }
47
+ //# sourceMappingURL=brief.js.map
@@ -0,0 +1,27 @@
1
+ import { type WhereOptions } from "./run-file.js";
2
+ /**
3
+ * `dotreq solution checkout`: what the runner runs in its checkout before
4
+ * each session (SOLBRANCH-1, 2). Puts the checkout on the branch of the
5
+ * surface the outcome is kept on (1.5), making it on that surface's first
6
+ * session, and brings in what other agents pushed to it. Prints the branch and what the agent is to
7
+ * be told before its brief, if anything; with `--json`, as one line the
8
+ * runner reads.
9
+ */
10
+ export declare function checkoutCommand(options: WhereOptions & {
11
+ json?: boolean;
12
+ share?: string;
13
+ branchWins?: boolean;
14
+ }): Promise<void>;
15
+ /**
16
+ * `dotreq solution push`: what the runner runs when a session ends
17
+ * (SOLBRANCH-4.0). Pushes the commits the agent made after its last record
18
+ * to the run's branch, its commits after any other agents pushed since;
19
+ * never commits what it left uncommitted (SOLBRANCH-6.0). Prints whether
20
+ * it pushed and, when it couldn't, why; with `--json`, as one line.
21
+ * Outside a runner there is no branch to push to, and nothing happens.
22
+ */
23
+ export declare function pushCommand(options: WhereOptions & {
24
+ json?: boolean;
25
+ repoWins?: boolean;
26
+ }): Promise<void>;
27
+ //# sourceMappingURL=checkout.d.ts.map
@@ -0,0 +1,66 @@
1
+ import { solutionClient } from "./client.js";
2
+ import { checkOutSolution, pushWork } from "./pushing.js";
3
+ import { requireSolution, whereOf } from "./run-file.js";
4
+ import { shareCheckout, sharePush } from "./share.js";
5
+ /**
6
+ * `dotreq solution checkout`: what the runner runs in its checkout before
7
+ * each session (SOLBRANCH-1, 2). Puts the checkout on the branch of the
8
+ * surface the outcome is kept on (1.5), making it on that surface's first
9
+ * session, and brings in what other agents pushed to it. Prints the branch and what the agent is to
10
+ * be told before its brief, if anything; with `--json`, as one line the
11
+ * runner reads.
12
+ */
13
+ export async function checkoutCommand(options) {
14
+ // SOLSHARE-2: from any repository, on a solution's share command.
15
+ if (options.share) {
16
+ await shareCheckout(options.share, (options.outcome ?? options.step)?.trim() || undefined, options.branchWins);
17
+ return;
18
+ }
19
+ const { solutionId } = requireSolution(options);
20
+ // SOLBRANCH-1.5: the branch of the surface the outcome is kept on.
21
+ const outcome = (options.outcome ?? options.step)?.trim() || undefined;
22
+ const { branch, notice } = await checkOutSolution(solutionClient(), solutionId, process.cwd(), outcome);
23
+ if (options.json) {
24
+ process.stdout.write(`${JSON.stringify({ branch, notice })}\n`);
25
+ return;
26
+ }
27
+ console.log(`On ${branch}.`);
28
+ if (notice)
29
+ console.log(`\n${notice}`);
30
+ }
31
+ /**
32
+ * `dotreq solution push`: what the runner runs when a session ends
33
+ * (SOLBRANCH-4.0). Pushes the commits the agent made after its last record
34
+ * to the run's branch, its commits after any other agents pushed since;
35
+ * never commits what it left uncommitted (SOLBRANCH-6.0). Prints whether
36
+ * it pushed and, when it couldn't, why; with `--json`, as one line.
37
+ * Outside a runner there is no branch to push to, and nothing happens.
38
+ */
39
+ export async function pushCommand(options) {
40
+ const where = whereOf(options);
41
+ // SOLSHARE-3: a repository a solution was checked out into, with no run.
42
+ if (!where.branch && (await sharePush(options.solution, options.repoWins)))
43
+ return;
44
+ if (!where.branch && !options.json) {
45
+ throw new Error("No solution is checked out here: run the share command you were given first.");
46
+ }
47
+ const outcome = where.solutionId && where.branch
48
+ ? await pushWork(solutionClient(), {
49
+ solutionId: where.solutionId,
50
+ branch: where.branch,
51
+ })
52
+ : { ok: true, pushed: false };
53
+ const result = outcome.ok
54
+ ? { pushed: outcome.pushed, failure: null }
55
+ : { pushed: false, failure: outcome.why };
56
+ if (options.json) {
57
+ process.stdout.write(`${JSON.stringify(result)}\n`);
58
+ return;
59
+ }
60
+ console.log(result.failure
61
+ ? `Not pushed: ${result.failure}.`
62
+ : result.pushed
63
+ ? `Pushed to ${where.branch}.`
64
+ : "Nothing to push.");
65
+ }
66
+ //# sourceMappingURL=checkout.js.map
@@ -0,0 +1,16 @@
1
+ import type { FunctionReference } from "convex/server";
2
+ /**
3
+ * Calls against the linked project, for the solution verbs: the project's
4
+ * credentials on every call, and a refusal from the backend surfaced in its
5
+ * own words. A call to an endpoint that names the project passes `project`
6
+ * itself.
7
+ */
8
+ export declare function solutionClient(): {
9
+ /** The linked project, by its slug or id. */
10
+ project: string;
11
+ mutation: <R>(fn: FunctionReference<"mutation">, args: Record<string, unknown>) => Promise<R>;
12
+ query: <R>(fn: FunctionReference<"query">, args: Record<string, unknown>) => Promise<R>;
13
+ };
14
+ /** A backend refusal, in the words the backend gave: a plain message, or a plan's broken rules listed. */
15
+ export declare function refusal(data: unknown): string;
16
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,46 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ import { ConvexError } from "convex/values";
3
+ import { convexUrl } from "../../cloud.js";
4
+ import { getProjectCredentials } from "../../utils/project-settings.js";
5
+ /**
6
+ * Calls against the linked project, for the solution verbs: the project's
7
+ * credentials on every call, and a refusal from the backend surfaced in its
8
+ * own words. A call to an endpoint that names the project passes `project`
9
+ * itself.
10
+ */
11
+ export function solutionClient() {
12
+ const { projectId, projectSecret } = getProjectCredentials();
13
+ const client = new ConvexHttpClient(convexUrl());
14
+ const auth = { projectAuth: { projectSlug: projectId, projectSecret } };
15
+ return {
16
+ /** The linked project, by its slug or id. */
17
+ project: projectId,
18
+ mutation: (fn, args) => inWords(() => client.mutation(fn, { ...auth, ...args })),
19
+ query: (fn, args) => inWords(() => client.query(fn, { ...auth, ...args })),
20
+ };
21
+ }
22
+ async function inWords(call) {
23
+ try {
24
+ return await call();
25
+ }
26
+ catch (error) {
27
+ if (error instanceof ConvexError)
28
+ throw new Error(refusal(error.data));
29
+ throw error;
30
+ }
31
+ }
32
+ /** A backend refusal, in the words the backend gave: a plain message, or a plan's broken rules listed. */
33
+ export function refusal(data) {
34
+ if (typeof data === "string")
35
+ return data;
36
+ if (data && typeof data === "object") {
37
+ const { kind, rules, message } = data;
38
+ if (kind === "invalid plan" && rules?.length) {
39
+ return `The plan breaks these rules:\n- ${rules.join("\n- ")}`;
40
+ }
41
+ if (message)
42
+ return message;
43
+ }
44
+ return JSON.stringify(data);
45
+ }
46
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The request file `ask` reads, in words: what `dotreq solution ask --help`
3
+ * prints after its options, the way `author --help` carries the reference
4
+ * forms. The backend's `describeContract()` renders it from the schema the
5
+ * request is validated against; the CLI can't import the backend, so this
6
+ * is that text, and a test keeps the two the same.
7
+ */
8
+ export declare const REQUEST_CONTRACT = "Ask the person to decide something about the outcome you are drafting. A choice presents options with a recommendation. One decision per request: if two things need deciding, ask twice.\n\nA request is JSON with a \"kind\" and the fields of that kind:\n\nchoice:\n question (text, up to 200 characters): The question, phrased as a question in the words the person will read it, not as a topic.\n options (list, at least 2): The live alternatives. More than four makes deciding harder; give more only when each is a genuine choice. Leave out anything you have already ruled out.\n name (text, up to 200 characters): The option as you would say it to the person: a noun phrase they can repeat back, not a label.\n benefits (list of text, each up to 200 characters): What is better if this option is taken. One line each, in your own words.\n costs (list of text, each up to 200 characters): What is worse if this option is taken: what it would take to build, and what the person lives with afterwards. Effort is a cost like any other.\n note (text, up to 600 characters, optional): Anything you learned or checked about this option that is not a benefit or a cost. Leave it out when there is nothing to add.\n recommendation (object): Which option you would take, and the one reason why.\n option (text, up to 200 characters): The name of the option you recommend, exactly as it appears in options.\n because (text, up to 200 characters): The one reason that carries it, in one line. The full reasoning belongs in background.\n background (text, up to 4000 characters): What the person needs to know to decide well that the options do not already say. Include only context that would substantially change the quality of the decision. The person reads this after the options, so do not reiterate them.";
9
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The request file `ask` reads, in words: what `dotreq solution ask --help`
3
+ * prints after its options, the way `author --help` carries the reference
4
+ * forms. The backend's `describeContract()` renders it from the schema the
5
+ * request is validated against; the CLI can't import the backend, so this
6
+ * is that text, and a test keeps the two the same.
7
+ */
8
+ export const REQUEST_CONTRACT = `Ask the person to decide something about the outcome you are drafting. A choice presents options with a recommendation. One decision per request: if two things need deciding, ask twice.
9
+
10
+ A request is JSON with a "kind" and the fields of that kind:
11
+
12
+ choice:
13
+ question (text, up to 200 characters): The question, phrased as a question in the words the person will read it, not as a topic.
14
+ options (list, at least 2): The live alternatives. More than four makes deciding harder; give more only when each is a genuine choice. Leave out anything you have already ruled out.
15
+ name (text, up to 200 characters): The option as you would say it to the person: a noun phrase they can repeat back, not a label.
16
+ benefits (list of text, each up to 200 characters): What is better if this option is taken. One line each, in your own words.
17
+ costs (list of text, each up to 200 characters): What is worse if this option is taken: what it would take to build, and what the person lives with afterwards. Effort is a cost like any other.
18
+ note (text, up to 600 characters, optional): Anything you learned or checked about this option that is not a benefit or a cost. Leave it out when there is nothing to add.
19
+ recommendation (object): Which option you would take, and the one reason why.
20
+ option (text, up to 200 characters): The name of the option you recommend, exactly as it appears in options.
21
+ because (text, up to 200 characters): The one reason that carries it, in one line. The full reasoning belongs in background.
22
+ background (text, up to 4000 characters): What the person needs to know to decide well that the options do not already say. Include only context that would substantially change the quality of the decision. The person reads this after the options, so do not reiterate them.`;
23
+ //# sourceMappingURL=contract.js.map