@popoverai/dotrequirements 0.33.0 → 0.34.1

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 (119) hide show
  1. package/README.md +2 -2
  2. package/dist/cli.js +13 -1
  3. package/dist/cloud.d.ts +5 -1
  4. package/dist/cloud.js +6 -2
  5. package/dist/commands/acceptance-test.js +1 -1
  6. package/dist/commands/board/index.d.ts +13 -0
  7. package/dist/commands/board/index.js +96 -0
  8. package/dist/commands/branch-merge.d.ts +78 -0
  9. package/dist/commands/branch-merge.js +129 -0
  10. package/dist/commands/diff.js +15 -5
  11. package/dist/commands/mockup/index.d.ts +11 -0
  12. package/dist/commands/mockup/index.js +82 -0
  13. package/dist/commands/scores/index.d.ts +50 -0
  14. package/dist/commands/scores/index.js +190 -0
  15. package/dist/commands/solution/ask.d.ts +9 -0
  16. package/dist/commands/solution/ask.js +27 -0
  17. package/dist/commands/solution/author.d.ts +19 -0
  18. package/dist/commands/solution/author.js +202 -0
  19. package/dist/commands/solution/branch.d.ts +103 -0
  20. package/dist/commands/solution/branch.js +341 -0
  21. package/dist/commands/solution/brief.d.ts +81 -0
  22. package/dist/commands/solution/brief.js +47 -0
  23. package/dist/commands/solution/checkout.d.ts +27 -0
  24. package/dist/commands/solution/checkout.js +66 -0
  25. package/dist/commands/solution/client.d.ts +16 -0
  26. package/dist/commands/solution/client.js +46 -0
  27. package/dist/commands/solution/contract.d.ts +9 -0
  28. package/dist/commands/solution/contract.js +23 -0
  29. package/dist/commands/solution/create.d.ts +14 -0
  30. package/dist/commands/solution/create.js +52 -0
  31. package/dist/commands/solution/demo-script.d.ts +28 -0
  32. package/dist/commands/solution/demo-script.js +43 -0
  33. package/dist/commands/solution/image.d.ts +60 -0
  34. package/dist/commands/solution/image.js +148 -0
  35. package/dist/commands/solution/index.d.ts +12 -0
  36. package/dist/commands/solution/index.js +282 -0
  37. package/dist/commands/solution/look-up.d.ts +9 -0
  38. package/dist/commands/solution/look-up.js +69 -0
  39. package/dist/commands/solution/next.d.ts +12 -0
  40. package/dist/commands/solution/next.js +48 -0
  41. package/dist/commands/solution/plan-file.d.ts +22 -0
  42. package/dist/commands/solution/plan-file.js +33 -0
  43. package/dist/commands/solution/preview-screenshot.d.ts +21 -0
  44. package/dist/commands/solution/preview-screenshot.js +56 -0
  45. package/dist/commands/solution/pull-request.d.ts +25 -0
  46. package/dist/commands/solution/pull-request.js +117 -0
  47. package/dist/commands/solution/pushing.d.ts +66 -0
  48. package/dist/commands/solution/pushing.js +185 -0
  49. package/dist/commands/solution/rehearse.d.ts +10 -0
  50. package/dist/commands/solution/rehearse.js +24 -0
  51. package/dist/commands/solution/request.d.ts +3 -0
  52. package/dist/commands/solution/request.js +13 -0
  53. package/dist/commands/solution/review.d.ts +27 -0
  54. package/dist/commands/solution/review.js +104 -0
  55. package/dist/commands/solution/run-file.d.ts +43 -0
  56. package/dist/commands/solution/run-file.js +69 -0
  57. package/dist/commands/solution/share.d.ts +62 -0
  58. package/dist/commands/solution/share.js +346 -0
  59. package/dist/commands/solution/stall.d.ts +14 -0
  60. package/dist/commands/solution/stall.js +41 -0
  61. package/dist/commands/solution/start.d.ts +14 -0
  62. package/dist/commands/solution/start.js +31 -0
  63. package/dist/commands/solution/status.d.ts +39 -0
  64. package/dist/commands/solution/status.js +145 -0
  65. package/dist/commands/solution/summary.d.ts +13 -0
  66. package/dist/commands/solution/summary.js +5 -0
  67. package/dist/commands/solution/things.d.ts +118 -0
  68. package/dist/commands/solution/things.js +78 -0
  69. package/dist/commands/solution/thread.d.ts +13 -0
  70. package/dist/commands/solution/thread.js +53 -0
  71. package/dist/commands/solution/upload.d.ts +24 -0
  72. package/dist/commands/solution/upload.js +27 -0
  73. package/dist/commands/solution/video.d.ts +88 -0
  74. package/dist/commands/solution/video.js +364 -0
  75. package/dist/commands/solution/working-folder.d.ts +44 -0
  76. package/dist/commands/solution/working-folder.js +64 -0
  77. package/dist/commands/sync.d.ts +2 -1
  78. package/dist/commands/sync.js +21 -5
  79. package/dist/convex.d.ts +54 -0
  80. package/dist/convex.js +64 -0
  81. package/dist/push/core.js +7 -2
  82. package/dist/rehearsal/derive.d.ts +23 -0
  83. package/dist/rehearsal/derive.js +112 -0
  84. package/dist/rehearsal/driver.d.ts +44 -0
  85. package/dist/rehearsal/driver.js +337 -0
  86. package/dist/rehearsal/earlier.d.ts +25 -0
  87. package/dist/rehearsal/earlier.js +138 -0
  88. package/dist/rehearsal/index.d.ts +5 -0
  89. package/dist/rehearsal/index.js +4 -0
  90. package/dist/rehearsal/live.d.ts +72 -0
  91. package/dist/rehearsal/live.js +301 -0
  92. package/dist/rehearsal/report.d.ts +4 -0
  93. package/dist/rehearsal/report.js +69 -0
  94. package/dist/rehearsal/types.d.ts +377 -0
  95. package/dist/rehearsal/types.js +8 -0
  96. package/dist/requirements/index.d.ts +6 -0
  97. package/dist/requirements/index.js +24 -0
  98. package/dist/schema/browser.d.ts +2 -1
  99. package/dist/schema/browser.js +6 -1
  100. package/dist/schema/file-writer.d.ts +5 -0
  101. package/dist/schema/file-writer.js +4 -0
  102. package/dist/schema/parser.d.ts +11 -2
  103. package/dist/schema/parser.js +11 -7
  104. package/dist/schema/provenance.d.ts +27 -0
  105. package/dist/schema/provenance.js +66 -0
  106. package/dist/schema/schemas.d.ts +40 -0
  107. package/dist/schema/schemas.js +4 -0
  108. package/dist/sync/execute.d.ts +10 -1
  109. package/dist/sync/execute.js +68 -5
  110. package/dist/sync/index.d.ts +1 -1
  111. package/dist/sync/index.js +1 -1
  112. package/dist/sync/publish.d.ts +7 -0
  113. package/dist/sync/segment.js +4 -1
  114. package/dist/sync/snapshot.d.ts +23 -1
  115. package/dist/sync/snapshot.js +75 -3
  116. package/dist/sync/solution-documents.d.ts +74 -0
  117. package/dist/sync/solution-documents.js +135 -0
  118. package/dist/sync/types.d.ts +20 -0
  119. package/package.json +3 -2
@@ -0,0 +1,190 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { api } from "../../convex.js";
3
+ import { solutionClient } from "../solution/client.js";
4
+ /**
5
+ * Hidden from help until solutions launch: today's users see the CLI they
6
+ * have now, and the server refuses these commands for a team without the
7
+ * solutions switch (SOLACCESS-2).
8
+ */
9
+ const HIDDEN_UNTIL_LAUNCH = { hidden: true };
10
+ /**
11
+ * Scores from the shell (VERB-7, SCORES-1): a solution's price on CREATED's
12
+ * seven axes, made with `create` and revised with `update`, each axis a
13
+ * whole number from 1 to 5 with a reason. Like a board or a mockup, scores
14
+ * are a thing: `create` prints the `scores:<id>` a writer submits with
15
+ * `dotreq solution author`, whose comment says what changed and why.
16
+ * Nothing reaches the backend unless every axis is whole, and the backend
17
+ * refuses the same again.
18
+ */
19
+ /**
20
+ * The seven axes as `dotreq scores` takes them (VERB-7): the key the backend stores,
21
+ * the name a person reads, and the option, which is also how a line of the
22
+ * reasons file names the axis. The backend's own list is
23
+ * packages/convex/solutions/scores.ts; the CLI can't import it, so a test
24
+ * keeps the two equal.
25
+ */
26
+ export const AXES = [
27
+ { key: "costOfDelay", label: "Cost of delay", option: "cost-of-delay" },
28
+ { key: "relevance", label: "Relevance", option: "relevance" },
29
+ { key: "entities", label: "Entities", option: "entities" },
30
+ { key: "api", label: "API", option: "api" },
31
+ { key: "testing", label: "Testing", option: "testing" },
32
+ { key: "experience", label: "Experience", option: "experience" },
33
+ { key: "doubt", label: "Doubt", option: "doubt" },
34
+ ];
35
+ const AXIS_NAMES = `The axes are ${AXES.map((axis) => axis.option).join(", ")}.`;
36
+ /** Each axis's score from its option, or a refusal naming the axis (VERB-7.2). */
37
+ function scoresFrom(options) {
38
+ const scores = {};
39
+ for (const axis of AXES) {
40
+ const given = options[axis.key]?.trim();
41
+ if (!given) {
42
+ throw new Error(`${axis.label} has no score: give it with --${axis.option} <1-5>.`);
43
+ }
44
+ const score = Number(given);
45
+ if (!/^\d+$/.test(given) || score < 1 || score > 5) {
46
+ throw new Error(`${axis.label} is scored "${given}": a score is a whole number from 1 to 5.`);
47
+ }
48
+ scores[axis.key] = score;
49
+ }
50
+ return scores;
51
+ }
52
+ /**
53
+ * Each axis's reason from the reasons file (VERB-7.0): one line an axis,
54
+ * `<axis>: <reason>`, the axis spelled as its option is, in any case; a
55
+ * leading "- " and blank lines are passed over. Refuses a line in another
56
+ * form, an axis that isn't one of the seven or is named twice (VERB-7.3),
57
+ * and an axis with no reason (VERB-7.2).
58
+ */
59
+ export function reasonsFrom(text, file) {
60
+ const reasons = {};
61
+ const lines = text.split(/\r?\n/);
62
+ lines.forEach((raw, index) => {
63
+ const line = raw.trim().replace(/^-\s+/, "");
64
+ if (!line)
65
+ return;
66
+ const colon = line.indexOf(":");
67
+ if (colon === -1) {
68
+ throw new Error(`Line ${index + 1} of ${file} isn't "<axis>: <reason>": "${raw.trim()}". ${AXIS_NAMES}`);
69
+ }
70
+ const name = line.slice(0, colon).trim().toLowerCase();
71
+ const axis = AXES.find((a) => a.option === name);
72
+ if (!axis) {
73
+ throw new Error(`"${name}" in ${file} isn't an axis. ${AXIS_NAMES}`);
74
+ }
75
+ if (reasons[axis.key] !== undefined) {
76
+ throw new Error(`"${name}" is named twice in ${file}. ${AXIS_NAMES}`);
77
+ }
78
+ reasons[axis.key] = line.slice(colon + 1).trim();
79
+ });
80
+ for (const axis of AXES) {
81
+ if (!reasons[axis.key]) {
82
+ throw new Error(`${axis.label} has no reason: add the line "${axis.option}: <why>" to ${file}.`);
83
+ }
84
+ }
85
+ return reasons;
86
+ }
87
+ /** The seven axes, checked, from the options and the reasons file. */
88
+ function axesFrom(options) {
89
+ const scores = scoresFrom(options);
90
+ const file = options.reasons?.trim();
91
+ if (!file) {
92
+ throw new Error(`Give each axis's reason in a file: --reasons <file>, one line per axis, "<axis>: <reason>". ${AXIS_NAMES}`);
93
+ }
94
+ let text;
95
+ try {
96
+ text = readFileSync(file, "utf8");
97
+ }
98
+ catch {
99
+ throw new Error(`Can't read the reasons file ${file}.`);
100
+ }
101
+ const reasons = reasonsFrom(text, file);
102
+ return Object.fromEntries(AXES.map((axis) => [
103
+ axis.key,
104
+ { score: scores[axis.key], reason: reasons[axis.key] },
105
+ ]));
106
+ }
107
+ /** `dotreq scores create`: new scores, and the reference to record them by (VERB-7.0). */
108
+ export async function createCommand(options) {
109
+ const axes = axesFrom(options);
110
+ const client = solutionClient();
111
+ const { scoresId } = await client.mutation(api.scores.mutations.create, { project: client.project, ...axes });
112
+ console.log(`Created scores ${scoresId}. Record them with: dotreq solution author scores:${scoresId} --comment "..."`);
113
+ }
114
+ /** `dotreq scores update <id>`: all seven axes again, for a revision (VERB-7.1). */
115
+ export async function updateCommand(scoresId, options) {
116
+ const axes = axesFrom(options);
117
+ // Named by its id or by the reference `create` printed.
118
+ const id = scoresId.replace(/^scores:/, "");
119
+ await solutionClient().mutation(api.scores.mutations.update, {
120
+ scoresId: id,
121
+ ...axes,
122
+ });
123
+ console.log(`Updated scores ${id}. Record the revision with: dotreq solution author scores:${id} --comment "..."`);
124
+ }
125
+ /** The axes, the reasons file and an example, on `scores --help` (VERB-7.4). */
126
+ export const SCORES_HELP = `
127
+ Scores are seven axes, each a whole number from 1 to 5 with one line of reasoning:
128
+ suitability, whether to do this now:
129
+ --cost-of-delay, --relevance
130
+ appetite, what it takes to build and how likely the other six are wrong:
131
+ --entities, --api, --testing, --experience, --doubt
132
+
133
+ The reasons file has one line per axis, the axis named as its option is,
134
+ without the dashes:
135
+ cost-of-delay: <why this score>
136
+ relevance: <why>
137
+ entities: <why>
138
+ api: <why>
139
+ testing: <why>
140
+ experience: <why>
141
+ doubt: <why>
142
+
143
+ create prints the scores' reference; record them with it, as any work is
144
+ recorded. To revise them, update them with all seven axes again and record
145
+ them again.
146
+
147
+ Examples:
148
+ dotreq scores create --cost-of-delay 4 --relevance 3 --entities 2 --api 2 \\
149
+ --testing 2 --experience 3 --doubt 2 --reasons reasons.txt
150
+ dotreq solution author scores:<id> --comment "Scored against the pitch's draft and the discovery board."
151
+ dotreq scores update <id> --cost-of-delay 4 --relevance 3 --entities 3 --api 2 \\
152
+ --testing 2 --experience 3 --doubt 3 --reasons reasons.txt
153
+ `;
154
+ /** The seven axis options and the reasons file, on both subcommands. */
155
+ function axisOptions(command) {
156
+ return command
157
+ .option("--cost-of-delay <n>", "Cost of delay, 1 to 5")
158
+ .option("--relevance <n>", "Relevance, 1 to 5")
159
+ .option("--entities <n>", "Entities, 1 to 5")
160
+ .option("--api <n>", "API, 1 to 5")
161
+ .option("--testing <n>", "Testing, 1 to 5")
162
+ .option("--experience <n>", "Experience, 1 to 5")
163
+ .option("--doubt <n>", "Doubt, 1 to 5")
164
+ .option("--reasons <file>", "A file with one line per axis, <axis>: <reason> (required)")
165
+ .addHelpText("after", SCORES_HELP);
166
+ }
167
+ export function registerScores(program) {
168
+ const scores = program
169
+ .command("scores", HIDDEN_UNTIL_LAUNCH)
170
+ .description("Make and revise a solution's scores: seven axes, each a whole number from 1 to 5 with a reason")
171
+ .addHelpText("after", SCORES_HELP);
172
+ axisOptions(scores
173
+ .command("create")
174
+ .description("Make scores from the seven axis options and a reasons file, and print the reference to record them by")).action(wrap(createCommand));
175
+ axisOptions(scores
176
+ .command("update <scores>")
177
+ .description("Revise scores, named by their id or scores:<id>, with all seven axes again; record them again with author")).action(wrap(updateCommand));
178
+ }
179
+ function wrap(fn) {
180
+ return async (...args) => {
181
+ try {
182
+ await fn(...args);
183
+ }
184
+ catch (error) {
185
+ console.error(`\nError: ${error instanceof Error ? error.message : String(error)}\n`);
186
+ process.exitCode = 1;
187
+ }
188
+ };
189
+ }
190
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ import { type WhereOptions } from "./run-file.js";
2
+ /**
3
+ * `dotreq solution ask <request-file>`: the drafter puts a decision to the
4
+ * owner, on the outcome it is drafting or the one `--outcome` names. A
5
+ * reviewer escalates instead (`dotreq solution review escalate`), and the
6
+ * backend refuses its question (VERB-3.5).
7
+ */
8
+ export declare function askCommand(file: string, options: WhereOptions): Promise<void>;
9
+ //# sourceMappingURL=ask.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { api } from "../../convex.js";
2
+ import { solutionClient } from "./client.js";
3
+ import { pushBeforeRecord } from "./pushing.js";
4
+ import { readRequest } from "./request.js";
5
+ import { onOutcome, requireWhere } from "./run-file.js";
6
+ /**
7
+ * `dotreq solution ask <request-file>`: the drafter puts a decision to the
8
+ * owner, on the outcome it is drafting or the one `--outcome` names. A
9
+ * reviewer escalates instead (`dotreq solution review escalate`), and the
10
+ * backend refuses its question (VERB-3.5).
11
+ */
12
+ export async function askCommand(file, options) {
13
+ const request = readRequest(file);
14
+ const where = requireWhere(options);
15
+ const { solutionId, agent } = where;
16
+ const client = solutionClient();
17
+ // SOLBRANCH-3.0: the drafter's commits reach the branch before Quinn is asked.
18
+ await pushBeforeRecord(client, where);
19
+ await client.mutation(api.solutions.mutations.ask, {
20
+ solutionId,
21
+ agent,
22
+ ...(where.outcome ? { outcome: where.outcome } : {}),
23
+ request,
24
+ });
25
+ console.log(`Asked${onOutcome(where.outcome)}. It waits on the owner's decision.`);
26
+ }
27
+ //# sourceMappingURL=ask.js.map
@@ -0,0 +1,19 @@
1
+ import { type WhereOptions } from "./run-file.js";
2
+ import { type SummaryOptions } from "./summary.js";
3
+ export type AuthorOptions = WhereOptions & SummaryOptions & {
4
+ /** What changed and why, for the outcome's thread (VERB-1.1, STEP-REVIEW-5.0). */
5
+ comment?: string;
6
+ /** Each reference's name on the outcome's tab: one per reference, in their order, or none (SIGHT-12.3). */
7
+ title?: string[];
8
+ };
9
+ /**
10
+ * `dotreq solution author <reference>... --comment "<what changed and why>"`:
11
+ * the drafter's submission of the outcome it is drafting, or of the one
12
+ * `--outcome` names. The references say where what it made is; the comment
13
+ * is what the next reader of the thread sees first, so nothing is recorded
14
+ * without it (VERB-1.2). On an outcome no agent reviews, the work goes to
15
+ * the owner with the executive summary, `--executive-summary "..."`, which
16
+ * the backend asks for there (VERB-1.6).
17
+ */
18
+ export declare function authorCommand(references: string[], options: AuthorOptions): Promise<void>;
19
+ //# sourceMappingURL=author.d.ts.map
@@ -0,0 +1,202 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { isAbsolute, relative, resolve } from "node:path";
3
+ import { api } from "../../convex.js";
4
+ import { currentBranch, headCommit, repositoryRoot } from "./branch.js";
5
+ import { solutionClient } from "./client.js";
6
+ import { readImage, uploadImage } from "./image.js";
7
+ import { screenshotPreview } from "./preview-screenshot.js";
8
+ import { checkPullRequest } from "./pull-request.js";
9
+ import { pushBeforeRecord, refuseUncommitted } from "./pushing.js";
10
+ import { onOutcome, requireWhere } from "./run-file.js";
11
+ import { summaryGiven } from "./summary.js";
12
+ import { parseThing, REFERENCE_FORMS } from "./things.js";
13
+ import { readDemoScript, readRecording, submittedRecording, } from "./video.js";
14
+ import { readSubmittedFiles, recordSavedIds, } from "./working-folder.js";
15
+ /**
16
+ * `dotreq solution author <reference>... --comment "<what changed and why>"`:
17
+ * the drafter's submission of the outcome it is drafting, or of the one
18
+ * `--outcome` names. The references say where what it made is; the comment
19
+ * is what the next reader of the thread sees first, so nothing is recorded
20
+ * without it (VERB-1.2). On an outcome no agent reviews, the work goes to
21
+ * the owner with the executive summary, `--executive-summary "..."`, which
22
+ * the backend asks for there (VERB-1.6).
23
+ */
24
+ export async function authorCommand(references, options) {
25
+ if (references.length === 0) {
26
+ throw new Error("Name where what you made is: dotreq solution author <reference>... — `dotreq solution author --help` gives the forms.");
27
+ }
28
+ const comment = options.comment?.trim();
29
+ if (!comment) {
30
+ throw new Error("Say what changed and why: your comment is what the next reader sees first.");
31
+ }
32
+ const where = requireWhere(options);
33
+ const made = references.map(parseThing);
34
+ // SIGHT-12.3, 12.3.0: each reference's title, matched to it by order
35
+ // before anything is read, pushed, uploaded or recorded.
36
+ const titles = titleReferences(made, options.title ?? []);
37
+ // SIGHT-7.0.0, 7.0.1, 9.0.0, 13.0: a video and its script, a demo's
38
+ // script, and an image, are read first, so one a browser can't show, or
39
+ // a script that isn't one, is refused before anything is pushed,
40
+ // uploaded or recorded.
41
+ const videos = new Map();
42
+ const demoScripts = new Map();
43
+ const images = new Map();
44
+ for (const thing of made) {
45
+ if (thing.kind === "video") {
46
+ videos.set(thing, readRecording(thing.path, thing.scriptPath));
47
+ }
48
+ if (thing.kind === "demo script") {
49
+ demoScripts.set(thing, readDemoScript(thing.path));
50
+ }
51
+ if (thing.kind === "image")
52
+ images.set(thing, readImage(thing.path));
53
+ }
54
+ // VERB-1.4, 1.5: a file named without its branch is on the branch the
55
+ // checkout is on, or on none.
56
+ const cwd = process.cwd();
57
+ refuseOutsideCheckout(made, cwd);
58
+ const checkout = currentBranch(cwd);
59
+ const fromCheckout = (thing) => thing.kind === "file" &&
60
+ (thing.branch === undefined || thing.branch === checkout);
61
+ // PULL-3.0, 3.2: the pull request named is the solution's, open from its
62
+ // branch, checked before anything is pushed or recorded.
63
+ const pullRequests = new Map();
64
+ for (const thing of made) {
65
+ if (thing.kind === "pull request") {
66
+ pullRequests.set(thing, await checkPullRequest(where, thing.number));
67
+ }
68
+ }
69
+ const client = solutionClient();
70
+ if (where.branch) {
71
+ // Inside a runner: only what is committed is submitted (SOLBRANCH-5),
72
+ // and it reaches the solution's branch before the record is kept (3.0).
73
+ refuseUncommitted({ ...where, branch: where.branch }, made.flatMap((thing) => fromCheckout(thing) && thing.kind === "file" ? [thing.path] : []));
74
+ await pushBeforeRecord(client, where);
75
+ }
76
+ // SOLDOC-12.2.0: a file from the checkout's own branch keeps the commit
77
+ // it was submitted at, so the page can tell when it changes after.
78
+ const commit = checkout ? headCommit(cwd) : null;
79
+ /** A reference as it is submitted: where the thing is, as the backend keeps it. */
80
+ const submissionOf = async (thing) => {
81
+ const recording = videos.get(thing);
82
+ if (recording) {
83
+ // SIGHT-7.0, 7.4: the app keeps a copy of the video, the script's
84
+ // text and where each of its steps begins; nothing goes on the
85
+ // branch.
86
+ return submittedRecording(client, where.solutionId, recording);
87
+ }
88
+ const demoScript = demoScripts.get(thing);
89
+ if (demoScript) {
90
+ // SIGHT-13.0: the app keeps the script's text for the agent
91
+ // reviewing it to record; nothing goes on the branch.
92
+ return { kind: "demo script", ...demoScript };
93
+ }
94
+ const image = images.get(thing);
95
+ if (image) {
96
+ // SIGHT-9.0: the app keeps a copy of the image; nothing goes on the
97
+ // branch.
98
+ const storageId = await uploadImage(client, where.solutionId, image);
99
+ return { kind: "image", storageId, name: image.name };
100
+ }
101
+ const pullRequest = pullRequests.get(thing);
102
+ if (pullRequest)
103
+ return { kind: "pull request", ...pullRequest };
104
+ if (thing.kind === "preview") {
105
+ // SIGHT-12.0, 12.2: a screenshot of the page it opens on, kept as an
106
+ // image is; without one, it is submitted all the same.
107
+ const shot = await screenshotPreview(thing);
108
+ if ("image" in shot) {
109
+ const screenshot = await uploadImage(client, where.solutionId, shot.image);
110
+ return { ...thing, screenshot };
111
+ }
112
+ console.error(`No screenshot of preview:${thing.port}${thing.page ?? ""} was taken (${shot.missed}); its tab says so. Submit it again once it answers to add one.`);
113
+ return thing;
114
+ }
115
+ return checkout && fromCheckout(thing)
116
+ ? { ...thing, branch: checkout, ...(commit ? { commit } : {}) }
117
+ : thing;
118
+ };
119
+ const submitted = [];
120
+ for (const thing of made) {
121
+ // SIGHT-12.3: the title given for it goes with it, whatever its kind.
122
+ const title = titles.get(thing);
123
+ submitted.push({
124
+ ...(await submissionOf(thing)),
125
+ ...(title ? { title } : {}),
126
+ });
127
+ }
128
+ // A document submitted by its file goes with its text when there is no
129
+ // git checkout: on a project with no repository connected, the backend
130
+ // saves it on the solution's branch (BRANCH-1.0).
131
+ const files = checkout ? [] : readSubmittedFiles(made);
132
+ // VERB-1.6: on an outcome no agent reviews, the owner reads the executive
133
+ // summary first. Only the backend knows whether one does, so it is the
134
+ // backend that refuses the work without one.
135
+ const summary = summaryGiven(options);
136
+ const args = {
137
+ solutionId: where.solutionId,
138
+ agent: where.agent,
139
+ ...(where.outcome ? { outcome: where.outcome } : {}),
140
+ made: submitted,
141
+ comment,
142
+ ...(summary ? { summary } : {}),
143
+ ...(files.length ? { files } : {}),
144
+ };
145
+ const result = await client.mutation(api.solutions.mutations.complete, args);
146
+ const saved = result?.saved ?? [];
147
+ recordSavedIds(files, saved);
148
+ for (const entry of saved) {
149
+ console.log(`Saved ${entry.path} on the solution's branch as version ${entry.version}.`);
150
+ }
151
+ // CHECKPOINT-6.3.2.3: work begun before the outcome was set to be
152
+ // drafted again is kept, but isn't its new draft; the agent is told so.
153
+ if (result?.setAside) {
154
+ console.log(result.setAside);
155
+ return;
156
+ }
157
+ console.log(`Recorded your work${onOutcome(where.outcome)}.`);
158
+ }
159
+ /**
160
+ * SOLBRANCH-5.3: a file is submitted by where it is in the repository, so
161
+ * one from outside the checkout, such as a video written under /tmp, would
162
+ * be recorded at a path nobody can open. Refused, with the other forms, so
163
+ * the agent finds the one it meant.
164
+ */
165
+ function refuseOutsideCheckout(made, cwd) {
166
+ const root = repositoryRoot(cwd);
167
+ if (!root)
168
+ return;
169
+ const top = realpathSync(root);
170
+ const here = realpathSync(cwd);
171
+ const outside = made.flatMap((thing) => {
172
+ if (thing.kind !== "file")
173
+ return [];
174
+ const within = relative(top, resolve(here, thing.path));
175
+ return within.startsWith("..") || isAbsolute(within) ? [thing.path] : [];
176
+ });
177
+ if (outside.length === 0)
178
+ return;
179
+ throw new Error(`Nothing was recorded: ${outside.join(", ")} ${outside.length === 1 ? "is" : "are"} outside your checkout, and only files in the repository are submitted as files. ${REFERENCE_FORMS}`);
180
+ }
181
+ /**
182
+ * SIGHT-12.3, 12.3.0, 12.3.1: the title each reference is submitted with,
183
+ * its name on the outcome's tab, given as one `--title` per reference in the
184
+ * order the references are named, as `git commit -m` takes one per
185
+ * paragraph. None given, each keeps the name the tab gives it; a count that
186
+ * doesn't match submits nothing. A blank title is none, for that reference
187
+ * alone.
188
+ */
189
+ function titleReferences(made, given) {
190
+ if (given.length > 0 && given.length !== made.length) {
191
+ const count = (n, one, many) => `${n} ${n === 1 ? one : many}`;
192
+ throw new Error(`You named ${count(made.length, "reference", "references")} and gave ${count(given.length, "--title", "--titles")}, so nothing was submitted. Give one --title per reference, in the order you name them, or none.`);
193
+ }
194
+ const titles = new Map();
195
+ given.forEach((text, at) => {
196
+ const title = text.trim();
197
+ if (title)
198
+ titles.set(made[at], title);
199
+ });
200
+ return titles;
201
+ }
202
+ //# sourceMappingURL=author.js.map
@@ -0,0 +1,103 @@
1
+ /**
2
+ * The git a solution's branch needs (SOLBRANCH): which branch a checkout
3
+ * is on, what it has not committed, bringing the branch's new commits in
4
+ * under the agent's own, and pushing. Every function runs git in the
5
+ * directory it is given and never commits: what an agent has not committed
6
+ * stays uncommitted, in its checkout (SOLBRANCH-6).
7
+ */
8
+ type Ran = {
9
+ code: number;
10
+ stdout: string;
11
+ stderr: string;
12
+ };
13
+ export declare function git(cwd: string, args: string[]): Ran;
14
+ /** Git's name for no commit: a push's `before` when the branch wasn't in the repository. */
15
+ export declare const NO_COMMIT = "0000000000000000000000000000000000000000";
16
+ /**
17
+ * The branch the checkout is on, or null when it is on no branch — a
18
+ * single commit checked out, or no git repository at all (VERB-1.4, 1.5).
19
+ */
20
+ export declare function currentBranch(cwd: string): string | null;
21
+ /** The top of the repository the checkout is in, or null outside one. */
22
+ export declare function repositoryRoot(cwd: string): string | null;
23
+ /** The commit the checkout is at, or null outside a git repository. */
24
+ export declare function headCommit(cwd: string): string | null;
25
+ /**
26
+ * What the checkout has not committed: files git tracks with changes,
27
+ * staged or not, and new files it does not ignore, one by one.
28
+ */
29
+ export declare function uncommitted(cwd: string): {
30
+ changed: string[];
31
+ untracked: string[];
32
+ };
33
+ export type BroughtIn = {
34
+ ok: true;
35
+ } | {
36
+ ok: false;
37
+ conflicts: string[];
38
+ } | {
39
+ ok: false;
40
+ problem: string;
41
+ };
42
+ /**
43
+ * Bring the branch's new commits into the checkout, the agent's own
44
+ * commits placed after them (SOLBRANCH-2, 3.1). When they conflict — with
45
+ * a commit of the agent's, or with a change it has not committed — the
46
+ * checkout is left exactly as it was, and the files that conflict are
47
+ * named (SOLBRANCH-2.3, 3.2). What it has not committed stays uncommitted.
48
+ */
49
+ export declare function bringIn(cwd: string, branch: string): BroughtIn;
50
+ export type Pushed = {
51
+ kind: "pushed";
52
+ before: string;
53
+ after: string;
54
+ } | {
55
+ kind: "nothing to push";
56
+ } | {
57
+ kind: "conflict";
58
+ files: string[];
59
+ } | {
60
+ kind: "refused";
61
+ reason: string;
62
+ };
63
+ /**
64
+ * Push the checkout's commits to the solution's branch, after bringing in
65
+ * what other agents pushed since (SOLBRANCH-3.1). A push that loses a race
66
+ * with another agent's tries again; one the repository refuses comes back
67
+ * with the reason it gave (3.3).
68
+ */
69
+ export declare function pushTo(cwd: string, branch: string): Pushed;
70
+ /** The names of the repository's branches, as it has them now. */
71
+ export declare function remoteBranches(cwd: string): Set<string>;
72
+ /**
73
+ * The name for a solution's new branch: `dotreq/<directory>`, or, when a
74
+ * branch already has that name, the lowest suffix from -2 up that none has
75
+ * (SOLBRANCH-1.0, 1.1).
76
+ */
77
+ export declare function freeName(directory: string, taken: Set<string>): string;
78
+ /** The default branch's commit, as the repository has it now, fetched into the checkout. */
79
+ export declare function defaultBranchCommit(cwd: string): string;
80
+ /**
81
+ * The commit a named branch is at, as the repository has it now, fetched
82
+ * into the checkout: what a solution's branch is made from when a
83
+ * rehearsal's scenario names the branch to start from (REHEARSE-4.5).
84
+ */
85
+ export declare function branchCommit(cwd: string, branch: string): string;
86
+ /**
87
+ * The commit the repository's branch `name` is at, making the branch at
88
+ * `base` first when the repository has none of that name (SOLBRANCH-1.0,
89
+ * 1.3). A branch of that name that appears first — another of the
90
+ * solution's first sessions making it a moment before, or anyone's — is
91
+ * never moved: its commit is read and returned, for the caller to judge
92
+ * whose it is.
93
+ */
94
+ export declare function branchHead(cwd: string, name: string, base: string): string;
95
+ /**
96
+ * Put the checkout on the solution's branch, and bring in what other steps
97
+ * pushed to it (SOLBRANCH-1.2, 2). Returns what the agent should be told,
98
+ * or null when there is nothing to tell: its checkout is on the branch and
99
+ * holds everything pushed to it.
100
+ */
101
+ export declare function checkOut(cwd: string, branch: string): string | null;
102
+ export {};
103
+ //# sourceMappingURL=branch.d.ts.map