@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.
- package/dist/cli.js +13 -1
- package/dist/cloud.d.ts +5 -1
- package/dist/cloud.js +6 -2
- package/dist/commands/board/index.d.ts +13 -0
- package/dist/commands/board/index.js +96 -0
- package/dist/commands/branch-merge.d.ts +78 -0
- package/dist/commands/branch-merge.js +129 -0
- package/dist/commands/diff.js +15 -5
- package/dist/commands/mockup/index.d.ts +11 -0
- package/dist/commands/mockup/index.js +82 -0
- package/dist/commands/scores/index.d.ts +50 -0
- package/dist/commands/scores/index.js +190 -0
- package/dist/commands/solution/ask.d.ts +9 -0
- package/dist/commands/solution/ask.js +27 -0
- package/dist/commands/solution/author.d.ts +19 -0
- package/dist/commands/solution/author.js +202 -0
- package/dist/commands/solution/branch.d.ts +103 -0
- package/dist/commands/solution/branch.js +341 -0
- package/dist/commands/solution/brief.d.ts +81 -0
- package/dist/commands/solution/brief.js +47 -0
- package/dist/commands/solution/checkout.d.ts +27 -0
- package/dist/commands/solution/checkout.js +66 -0
- package/dist/commands/solution/client.d.ts +16 -0
- package/dist/commands/solution/client.js +46 -0
- package/dist/commands/solution/contract.d.ts +9 -0
- package/dist/commands/solution/contract.js +23 -0
- package/dist/commands/solution/create.d.ts +14 -0
- package/dist/commands/solution/create.js +52 -0
- package/dist/commands/solution/demo-script.d.ts +28 -0
- package/dist/commands/solution/demo-script.js +43 -0
- package/dist/commands/solution/image.d.ts +60 -0
- package/dist/commands/solution/image.js +148 -0
- package/dist/commands/solution/index.d.ts +12 -0
- package/dist/commands/solution/index.js +281 -0
- package/dist/commands/solution/look-up.d.ts +9 -0
- package/dist/commands/solution/look-up.js +69 -0
- package/dist/commands/solution/next.d.ts +12 -0
- package/dist/commands/solution/next.js +48 -0
- package/dist/commands/solution/plan-file.d.ts +22 -0
- package/dist/commands/solution/plan-file.js +33 -0
- package/dist/commands/solution/preview-screenshot.d.ts +21 -0
- package/dist/commands/solution/preview-screenshot.js +56 -0
- package/dist/commands/solution/pull-request.d.ts +25 -0
- package/dist/commands/solution/pull-request.js +117 -0
- package/dist/commands/solution/pushing.d.ts +66 -0
- package/dist/commands/solution/pushing.js +185 -0
- package/dist/commands/solution/rehearse.d.ts +10 -0
- package/dist/commands/solution/rehearse.js +24 -0
- package/dist/commands/solution/request.d.ts +3 -0
- package/dist/commands/solution/request.js +13 -0
- package/dist/commands/solution/review.d.ts +27 -0
- package/dist/commands/solution/review.js +104 -0
- package/dist/commands/solution/run-file.d.ts +42 -0
- package/dist/commands/solution/run-file.js +63 -0
- package/dist/commands/solution/share.d.ts +62 -0
- package/dist/commands/solution/share.js +346 -0
- package/dist/commands/solution/stall.d.ts +14 -0
- package/dist/commands/solution/stall.js +41 -0
- package/dist/commands/solution/start.d.ts +11 -0
- package/dist/commands/solution/start.js +20 -0
- package/dist/commands/solution/status.d.ts +39 -0
- package/dist/commands/solution/status.js +145 -0
- package/dist/commands/solution/summary.d.ts +13 -0
- package/dist/commands/solution/summary.js +5 -0
- package/dist/commands/solution/things.d.ts +118 -0
- package/dist/commands/solution/things.js +78 -0
- package/dist/commands/solution/thread.d.ts +13 -0
- package/dist/commands/solution/thread.js +53 -0
- package/dist/commands/solution/upload.d.ts +24 -0
- package/dist/commands/solution/upload.js +27 -0
- package/dist/commands/solution/video.d.ts +88 -0
- package/dist/commands/solution/video.js +364 -0
- package/dist/commands/solution/working-folder.d.ts +44 -0
- package/dist/commands/solution/working-folder.js +64 -0
- package/dist/commands/sync.d.ts +2 -1
- package/dist/commands/sync.js +21 -5
- package/dist/convex.d.ts +54 -0
- package/dist/convex.js +64 -0
- package/dist/push/core.js +7 -2
- package/dist/rehearsal/derive.d.ts +23 -0
- package/dist/rehearsal/derive.js +112 -0
- package/dist/rehearsal/driver.d.ts +44 -0
- package/dist/rehearsal/driver.js +337 -0
- package/dist/rehearsal/earlier.d.ts +25 -0
- package/dist/rehearsal/earlier.js +138 -0
- package/dist/rehearsal/index.d.ts +5 -0
- package/dist/rehearsal/index.js +4 -0
- package/dist/rehearsal/live.d.ts +72 -0
- package/dist/rehearsal/live.js +301 -0
- package/dist/rehearsal/report.d.ts +4 -0
- package/dist/rehearsal/report.js +69 -0
- package/dist/rehearsal/types.d.ts +377 -0
- package/dist/rehearsal/types.js +8 -0
- package/dist/requirements/index.d.ts +6 -0
- package/dist/requirements/index.js +24 -0
- package/dist/schema/browser.d.ts +1 -1
- package/dist/schema/browser.js +3 -1
- package/dist/schema/file-writer.d.ts +5 -0
- package/dist/schema/file-writer.js +4 -0
- package/dist/schema/parser.d.ts +11 -2
- package/dist/schema/parser.js +11 -7
- package/dist/schema/schemas.d.ts +40 -0
- package/dist/schema/schemas.js +4 -0
- package/dist/sync/execute.d.ts +10 -1
- package/dist/sync/execute.js +69 -5
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.js +1 -1
- package/dist/sync/publish.d.ts +7 -0
- package/dist/sync/segment.js +4 -1
- package/dist/sync/snapshot.d.ts +23 -1
- package/dist/sync/snapshot.js +75 -3
- package/dist/sync/solution-documents.d.ts +64 -0
- package/dist/sync/solution-documents.js +97 -0
- package/dist/sync/types.d.ts +18 -0
- 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
|