@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,14 @@
1
+ /**
2
+ * `dotreq solution create [file]`: with no file, a solution from the
3
+ * project's default plan, titled with `--title`; with a plan file (YAML or
4
+ * JSON), its title, its description, its plan in the stored shape, and the
5
+ * inputs that already exist. `--description` says what the solution is
6
+ * beyond its title, in Markdown, and wins over the file's. The backend validates the plan and refuses an invalid one
7
+ * with the rules it broke.
8
+ */
9
+ export declare function createCommand(file: string | undefined, options?: {
10
+ title?: string;
11
+ description?: string;
12
+ idea?: string[];
13
+ }): Promise<void>;
14
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1,52 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { api } from "../../convex.js";
4
+ import { solutionClient } from "./client.js";
5
+ import { planPayload, readPlanFile } from "./plan-file.js";
6
+ import { parseThing } from "./things.js";
7
+ /**
8
+ * `dotreq solution create [file]`: with no file, a solution from the
9
+ * project's default plan, titled with `--title`; with a plan file (YAML or
10
+ * JSON), its title, its description, its plan in the stored shape, and the
11
+ * inputs that already exist. `--description` says what the solution is
12
+ * beyond its title, in Markdown, and wins over the file's. The backend validates the plan and refuses an invalid one
13
+ * with the rules it broke.
14
+ */
15
+ export async function createCommand(file, options = {}) {
16
+ const idea = options.idea?.map(parseThing);
17
+ const described = options.description?.trim() || undefined;
18
+ if (!file) {
19
+ const title = options.title?.trim();
20
+ if (!title) {
21
+ throw new Error("A solution from the project's default plan needs a title: --title <title>.");
22
+ }
23
+ const client = solutionClient();
24
+ const id = await client.mutation(api.solutions.mutations.create, {
25
+ project: client.project,
26
+ title,
27
+ description: described,
28
+ idea,
29
+ });
30
+ console.log(`Created "${title}" from the project's default plan: ${id}`);
31
+ return;
32
+ }
33
+ const spec = readPlanFile(readFileSync(resolve(process.cwd(), file), "utf8"), file);
34
+ const title = options.title?.trim() ||
35
+ (typeof spec?.title === "string" ? spec.title : undefined);
36
+ if (!spec?.plan || !title) {
37
+ throw new Error(`${file} needs a plan, and a title in the file or as --title (docs/working/plan-surfaces.md).`);
38
+ }
39
+ const client = solutionClient();
40
+ const id = await client.mutation(api.solutions.mutations.create, {
41
+ project: client.project,
42
+ title,
43
+ description: described ??
44
+ (typeof spec.description === "string" ? spec.description : undefined),
45
+ // PLAN-1: the plan as the file writes it; the backend reads it.
46
+ plan: planPayload(spec),
47
+ inputs: Array.isArray(spec.inputs) ? spec.inputs : [],
48
+ idea,
49
+ });
50
+ console.log(`Created "${title}": ${id}`);
51
+ }
52
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Where a demo's script is saved in the checkout of the agent reviewing it
3
+ * (SIGHT-13.1), relative to the repository's root: the folder git ignores,
4
+ * under the script's file name. Kept in step with the backend's
5
+ * `demoScriptCopyPath` (convex/solutions/brief.ts), which is where the brief
6
+ * says the copy is.
7
+ */
8
+ export declare function demoScriptCopyPath(name: string): string;
9
+ /** An outcome as `dotreq solution next` returns it, as far as this reads it. */
10
+ type BriefOutcome = {
11
+ things?: Array<{
12
+ kind: string;
13
+ name?: string;
14
+ script?: string;
15
+ }>;
16
+ };
17
+ /**
18
+ * Save a copy of each demo's script among the outcomes a reviewer reads in
19
+ * its checkout, where its brief says it is, so it records the video from
20
+ * that file and fixes anything small there first (SIGHT-13.1, 13.3). The
21
+ * drafter's latest script replaces any earlier copy, as when the drafter
22
+ * revised it. The folder ignores itself, so nothing saved there is ever
23
+ * committed (13.0). The review is already started, so a copy that can't be
24
+ * saved is said on stderr rather than failing the run.
25
+ */
26
+ export declare function saveDemoScriptCopies(outcomes: readonly BriefOutcome[], cwd?: string): void;
27
+ export {};
28
+ //# sourceMappingURL=demo-script.d.ts.map
@@ -0,0 +1,43 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { basename, dirname, join } from "node:path";
3
+ import { repositoryRoot } from "./branch.js";
4
+ /**
5
+ * Where a demo's script is saved in the checkout of the agent reviewing it
6
+ * (SIGHT-13.1), relative to the repository's root: the folder git ignores,
7
+ * under the script's file name. Kept in step with the backend's
8
+ * `demoScriptCopyPath` (convex/solutions/brief.ts), which is where the brief
9
+ * says the copy is.
10
+ */
11
+ export function demoScriptCopyPath(name) {
12
+ return `.dotreq/demo-scripts/${name}`;
13
+ }
14
+ /**
15
+ * Save a copy of each demo's script among the outcomes a reviewer reads in
16
+ * its checkout, where its brief says it is, so it records the video from
17
+ * that file and fixes anything small there first (SIGHT-13.1, 13.3). The
18
+ * drafter's latest script replaces any earlier copy, as when the drafter
19
+ * revised it. The folder ignores itself, so nothing saved there is ever
20
+ * committed (13.0). The review is already started, so a copy that can't be
21
+ * saved is said on stderr rather than failing the run.
22
+ */
23
+ export function saveDemoScriptCopies(outcomes, cwd = process.cwd()) {
24
+ const scripts = outcomes.flatMap((outcome) => (outcome.things ?? []).flatMap((thing) => thing.kind === "demo script" && thing.name && thing.script !== undefined
25
+ ? [{ name: basename(thing.name), script: thing.script }]
26
+ : []));
27
+ if (scripts.length === 0)
28
+ return;
29
+ const root = repositoryRoot(cwd) ?? cwd;
30
+ try {
31
+ mkdirSync(join(root, ".dotreq"), { recursive: true });
32
+ writeFileSync(join(root, ".dotreq", ".gitignore"), "*\n");
33
+ for (const { name, script } of scripts) {
34
+ const path = join(root, demoScriptCopyPath(name));
35
+ mkdirSync(dirname(path), { recursive: true });
36
+ writeFileSync(path, script);
37
+ }
38
+ }
39
+ catch (error) {
40
+ console.error(`Couldn't save a copy of the demo's script: ${error instanceof Error ? error.message : String(error)}. Its text is in your brief.`);
41
+ }
42
+ }
43
+ //# sourceMappingURL=demo-script.js.map
@@ -0,0 +1,60 @@
1
+ import type { solutionClient } from "./client.js";
2
+ /**
3
+ * Images on an outcome (SIGHT-9), such as a screenshot: the drafter submits an
4
+ * image file from its checkout or anywhere it can read, and the app keeps a
5
+ * copy; nothing is committed.
6
+ *
7
+ * Every browser we support (Chrome, Edge, Firefox, Safari on macOS and iOS)
8
+ * shows PNG, JPEG, GIF, WebP and AVIF (MDN's image type guide; AVIF from
9
+ * Safari 16.4). HEIC and TIFF show only in Safari, and JPEG XL not in Edge,
10
+ * so they are refused. SVG is refused too: opened at full size in its own
11
+ * tab, an SVG runs whatever script it carries. The format is read from the
12
+ * file's first bytes (the WHATWG MIME Sniffing signatures; AVIF's brand in
13
+ * its `ftyp` box), since an extension says nothing about it. Kept in step
14
+ * with the backend's `ACCEPTED_IMAGE_FORMATS` (convex/solutions/images.ts).
15
+ */
16
+ export declare const ACCEPTED_IMAGE_FORMATS = "a PNG, JPEG, GIF, WebP or AVIF image";
17
+ export type ImageContentType = "image/png" | "image/jpeg" | "image/gif" | "image/webp" | "image/avif";
18
+ /** What the file is: a format a browser shows, or why it isn't one. */
19
+ export type ImageFormat = {
20
+ contentType: ImageContentType;
21
+ } | {
22
+ refused: string;
23
+ };
24
+ /** An image read from the checkout, ready to upload. */
25
+ export type Image = {
26
+ name: string;
27
+ contentType: ImageContentType;
28
+ bytes: Uint8Array<ArrayBuffer>;
29
+ };
30
+ /** The image at a path, or a refusal naming the accepted formats (SIGHT-9.0.0). */
31
+ export declare function readImage(path: string): Image;
32
+ /** What image format the bytes hold, read from the file's signature. */
33
+ export declare function imageFormat(bytes: Uint8Array): ImageFormat;
34
+ /**
35
+ * Where an image's copy is saved in the checkout, relative to the
36
+ * repository's root (SIGHT-9.3): a folder git ignores, then one folder per
37
+ * stored copy, so two images of one name stay apart. Kept in step with the
38
+ * backend's `imageCopyPath` (convex/solutions/brief.ts), which is where
39
+ * the brief says the copy is.
40
+ */
41
+ export declare function imageCopyPath(storageId: string, name: string): string;
42
+ /** An outcome as `dotreq solution next` returns it, as far as this reads it. */
43
+ type BriefOutcome = {
44
+ things?: Array<{
45
+ kind: string;
46
+ storageId?: string;
47
+ }>;
48
+ };
49
+ /**
50
+ * Save a copy of each image among the outcomes an agent works on in the
51
+ * checkout, where its brief says it is (SIGHT-9.3), in place of the copies
52
+ * an earlier session saved. The folder ignores itself, so nothing saved
53
+ * there is ever committed (9.0). The work is already started, so a copy
54
+ * that can't be saved is said on stderr rather than failing the run.
55
+ */
56
+ export declare function saveImageCopies(client: ReturnType<typeof solutionClient>, solutionId: string, outcomes: readonly BriefOutcome[], cwd?: string): Promise<void>;
57
+ /** Upload an image to the app (SIGHT-9.0), and the stored copy's id back. */
58
+ export declare function uploadImage(client: ReturnType<typeof solutionClient>, solutionId: string, image: Image): Promise<string>;
59
+ export {};
60
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1,148 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
+ import { api } from "../../convex.js";
4
+ import { repositoryRoot } from "./branch.js";
5
+ import { uploadToApp } from "./upload.js";
6
+ /**
7
+ * Images on an outcome (SIGHT-9), such as a screenshot: the drafter submits an
8
+ * image file from its checkout or anywhere it can read, and the app keeps a
9
+ * copy; nothing is committed.
10
+ *
11
+ * Every browser we support (Chrome, Edge, Firefox, Safari on macOS and iOS)
12
+ * shows PNG, JPEG, GIF, WebP and AVIF (MDN's image type guide; AVIF from
13
+ * Safari 16.4). HEIC and TIFF show only in Safari, and JPEG XL not in Edge,
14
+ * so they are refused. SVG is refused too: opened at full size in its own
15
+ * tab, an SVG runs whatever script it carries. The format is read from the
16
+ * file's first bytes (the WHATWG MIME Sniffing signatures; AVIF's brand in
17
+ * its `ftyp` box), since an extension says nothing about it. Kept in step
18
+ * with the backend's `ACCEPTED_IMAGE_FORMATS` (convex/solutions/images.ts).
19
+ */
20
+ export const ACCEPTED_IMAGE_FORMATS = "a PNG, JPEG, GIF, WebP or AVIF image";
21
+ /** The image at a path, or a refusal naming the accepted formats (SIGHT-9.0.0). */
22
+ export function readImage(path) {
23
+ const full = resolve(path);
24
+ if (!existsSync(full) || !statSync(full).isFile()) {
25
+ throw new Error(`There is no file at ${path}, so nothing was submitted.`);
26
+ }
27
+ const bytes = new Uint8Array(readFileSync(full));
28
+ const format = imageFormat(bytes);
29
+ if ("refused" in format) {
30
+ throw new Error(`${path} isn't an image a browser can show (${format.refused}), so nothing was submitted. Submit ${ACCEPTED_IMAGE_FORMATS}.`);
31
+ }
32
+ return { name: basename(full), contentType: format.contentType, bytes };
33
+ }
34
+ const AVIF_BRANDS = new Set(["avif", "avis"]);
35
+ const HEIF_BRANDS = new Set(["heic", "heix", "heim", "heis", "hevc", "hevx"]);
36
+ /** What image format the bytes hold, read from the file's signature. */
37
+ export function imageFormat(bytes) {
38
+ const starts = (...signature) => signature.every((byte, at) => bytes[at] === byte);
39
+ if (starts(0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a)) {
40
+ return { contentType: "image/png" };
41
+ }
42
+ if (starts(0xff, 0xd8, 0xff))
43
+ return { contentType: "image/jpeg" };
44
+ if (ascii(bytes, 0, 6) === "GIF87a" || ascii(bytes, 0, 6) === "GIF89a") {
45
+ return { contentType: "image/gif" };
46
+ }
47
+ if (ascii(bytes, 0, 4) === "RIFF" && ascii(bytes, 8, 6) === "WEBPVP") {
48
+ return { contentType: "image/webp" };
49
+ }
50
+ if (ascii(bytes, 4, 4) === "ftyp") {
51
+ // ISO-BMFF: the major brand, then compatible brands to the box's end.
52
+ // AVIF lists avif or avis; HEIC shares the box and lists its own.
53
+ const size = readUint32(bytes, 0);
54
+ const end = Math.min(bytes.length, size);
55
+ const brands = [ascii(bytes, 8, 4)];
56
+ for (let at = 16; at + 4 <= end; at += 4)
57
+ brands.push(ascii(bytes, at, 4));
58
+ if (brands.some((brand) => AVIF_BRANDS.has(brand))) {
59
+ return { contentType: "image/avif" };
60
+ }
61
+ if (brands.some((brand) => HEIF_BRANDS.has(brand))) {
62
+ return { refused: "it is a HEIC image, which only Safari shows" };
63
+ }
64
+ return { refused: "it isn't an image file" };
65
+ }
66
+ if (starts(0x49, 0x49, 0x2a, 0x00) || starts(0x4d, 0x4d, 0x00, 0x2a)) {
67
+ return { refused: "it is a TIFF image, which only Safari shows" };
68
+ }
69
+ if (/^\s*(<\?xml[^>]*>\s*)?(<!--[\s\S]*?-->\s*)*<svg[\s>]/i.test(head(bytes))) {
70
+ return {
71
+ refused: "it is an SVG, which runs its scripts when opened at full size",
72
+ };
73
+ }
74
+ return { refused: "it isn't an image file" };
75
+ }
76
+ function ascii(bytes, at, length) {
77
+ return at + length <= bytes.length
78
+ ? String.fromCharCode(...bytes.subarray(at, at + length))
79
+ : "";
80
+ }
81
+ function readUint32(bytes, at) {
82
+ let value = 0;
83
+ for (let i = 0; i < 4; i++)
84
+ value = value * 256 + (bytes[at + i] ?? 0);
85
+ return value;
86
+ }
87
+ /** The file's start as text, for telling an SVG by its root element. */
88
+ function head(bytes) {
89
+ return new TextDecoder("utf-8", { fatal: false })
90
+ .decode(bytes.subarray(0, 1024))
91
+ .replace(/^/, "");
92
+ }
93
+ /**
94
+ * Where an image's copy is saved in the checkout, relative to the
95
+ * repository's root (SIGHT-9.3): a folder git ignores, then one folder per
96
+ * stored copy, so two images of one name stay apart. Kept in step with the
97
+ * backend's `imageCopyPath` (convex/solutions/brief.ts), which is where
98
+ * the brief says the copy is.
99
+ */
100
+ export function imageCopyPath(storageId, name) {
101
+ return `${COPIES_FOLDER}/images/${storageId}/${name}`;
102
+ }
103
+ const COPIES_FOLDER = ".dotreq";
104
+ /**
105
+ * Save a copy of each image among the outcomes an agent works on in the
106
+ * checkout, where its brief says it is (SIGHT-9.3), in place of the copies
107
+ * an earlier session saved. The folder ignores itself, so nothing saved
108
+ * there is ever committed (9.0). The work is already started, so a copy
109
+ * that can't be saved is said on stderr rather than failing the run.
110
+ */
111
+ export async function saveImageCopies(client, solutionId, outcomes, cwd = process.cwd()) {
112
+ const storageIds = [
113
+ ...new Set(outcomes.flatMap((outcome) => (outcome.things ?? []).flatMap((thing) => thing.kind === "image" && thing.storageId ? [thing.storageId] : []))),
114
+ ];
115
+ if (storageIds.length === 0)
116
+ return;
117
+ const root = repositoryRoot(cwd) ?? cwd;
118
+ const folder = join(root, COPIES_FOLDER);
119
+ try {
120
+ rmSync(join(folder, "images"), { recursive: true, force: true });
121
+ mkdirSync(folder, { recursive: true });
122
+ writeFileSync(join(folder, ".gitignore"), "*\n");
123
+ const copies = await client.query(api.solutions.images.copies, { solutionId, storageIds });
124
+ for (const storageId of storageIds) {
125
+ const copy = copies.find((found) => found.storageId === storageId);
126
+ if (!copy) {
127
+ console.error(`An image submitted (${storageId}) has no copy left.`);
128
+ continue;
129
+ }
130
+ const response = await fetch(copy.url);
131
+ if (!response.ok) {
132
+ console.error(`Couldn't save a copy of ${copy.name} (${response.status} ${response.statusText}).`);
133
+ continue;
134
+ }
135
+ const path = join(root, imageCopyPath(storageId, basename(copy.name)));
136
+ mkdirSync(dirname(path), { recursive: true });
137
+ writeFileSync(path, new Uint8Array(await response.arrayBuffer()));
138
+ }
139
+ }
140
+ catch (error) {
141
+ console.error(`Couldn't save copies of the images submitted: ${error instanceof Error ? error.message : String(error)}`);
142
+ }
143
+ }
144
+ /** Upload an image to the app (SIGHT-9.0), and the stored copy's id back. */
145
+ export function uploadImage(client, solutionId, image) {
146
+ return uploadToApp(client, api.solutions.images.uploadUrl, { solutionId }, image);
147
+ }
148
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1,12 @@
1
+ import { type Command } from "commander";
2
+ /**
3
+ * `dotreq solution`: work a solution through its plan of outcomes
4
+ * (docs/working/plan-surfaces.md). A person creates a solution and can
5
+ * start an agent's work by hand; an agent runs `next` when invoked, which
6
+ * gives it an outcome to draft or to review, and records what came of it
7
+ * with one verb per act — `author`, `review`, `ask`, `stall` — each naming
8
+ * its object and no ids (VERB-4), and reads the outcome's thread with
9
+ * `thread`.
10
+ */
11
+ export declare function registerSolution(program: Command): void;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,281 @@
1
+ import { Option } from "commander";
2
+ import { askCommand } from "./ask.js";
3
+ import { authorCommand } from "./author.js";
4
+ import { checkoutCommand, pushCommand } from "./checkout.js";
5
+ import { REQUEST_CONTRACT } from "./contract.js";
6
+ import { createCommand } from "./create.js";
7
+ import { thingsCommand } from "./look-up.js";
8
+ import { nextCommand } from "./next.js";
9
+ import { pullRequestCommand } from "./pull-request.js";
10
+ import { rehearseCommand } from "./rehearse.js";
11
+ import { reviewCommand } from "./review.js";
12
+ import { doneCommand } from "./share.js";
13
+ import { stallCommand } from "./stall.js";
14
+ import { startCommand } from "./start.js";
15
+ import { statusCommand } from "./status.js";
16
+ import { threadCommand } from "./thread.js";
17
+ /**
18
+ * Verbs only an agent runs, kept out of `--help`: an agent gets them from
19
+ * `next`'s output, and a person has no reason to type them.
20
+ */
21
+ const HIDDEN = { hidden: true };
22
+ /**
23
+ * Where a verb's solution, agent and outcome come from when the run does
24
+ * not say (VERB-4.1). The same options on every verb an agent runs;
25
+ * `--step` is `--outcome` under its old name, kept out of `--help`.
26
+ */
27
+ function whereOptions(command, outcome = "Which outcome, when you are at more than one; otherwise the one you are at") {
28
+ return command
29
+ .option("--solution <id>", "Which solution, when the run does not say (inside a runner it does)")
30
+ .option("--agent <name>", "Which agent you are, when the run does not say (inside a runner it does)")
31
+ .option("--outcome <name>", outcome)
32
+ .addOption(new Option("--step <name>").hideHelp());
33
+ }
34
+ /** The forms a reference takes: one source of truth, on `author --help` (VERB-5.5). */
35
+ const REFERENCE_FORMS = `
36
+ Where what you made is, one or more:
37
+ document:<id> a document in dotrequirements
38
+ file:<path>[@<branch>] a file in this repository
39
+ link:<url> anything that lives elsewhere
40
+ board:<id> a discovery board in dotrequirements
41
+ mockup:<id> a mockup in dotrequirements
42
+ scores:<id> scores in dotrequirements, made with dotreq scores create
43
+ pr:<number> the solution's pull request, opened or found with
44
+ dotreq solution pull-request. An outcome kept on a pull
45
+ request is submitted with it, with the rest of its work
46
+ image:<path> an image, such as a screenshot: a PNG, JPEG, GIF, WebP or
47
+ AVIF. The app keeps a copy; don't commit it
48
+ "video:<path> <script>" a video file here, such as a screen recording of a fix,
49
+ and the steps file it was recorded from, as one argument,
50
+ e.g. "video:final.mp4 steps.json". The video is an MP4
51
+ with H.264, or a WebM with VP8 or VP9. The app keeps a copy of
52
+ both; don't commit either. Where an agent reviews your
53
+ outcome, it records the video: submit the demo-script
54
+ instead
55
+ demo-script:<path> a demo's steps file: the browser commands for each step
56
+ and the sentence said over it. Don't record it: the agent
57
+ reviewing it records the video once it would approve the
58
+ script. The app keeps the script; don't commit it
59
+ "preview:<port>[/<page>] <cmd>"
60
+ something running here — Storybook, a dev server — as one
61
+ argument: the port it listens on (6006, 5173 or 8080;
62
+ never 3000, the runner's), the page to open when it isn't
63
+ the first one, and the command that starts it from the
64
+ repository root. A screenshot of the page is taken as it
65
+ is submitted
66
+
67
+ Each reference can have a title, its name for the owner on the outcome's tab, and
68
+ a preview's button names it: give one --title per reference, in the order the
69
+ references are given. Without titles, each is named for what it is.
70
+
71
+ A spec goes under .requirements/ as <name>.requirements.md, any other written
72
+ outcome at .requirements/solutions/<solution>/<outcome>.md. With a repository
73
+ connected, a file kept on the pull request is committed and submitted as
74
+ file:<path>, unsynced, since it reaches the project's requirements when the
75
+ pull request merges; one kept in the app is synced alone with
76
+ \`dotreq sync --repo-contributes <path>\`, which writes the document id into its
77
+ frontmatter, and submitted as document:<id>. With none, submit file:<path>: it
78
+ is saved on the solution's branch, and the document id is written into its
79
+ frontmatter.
80
+
81
+ When your work moves on before anyone reviews it, the owner may read it next, so write
82
+ an executive summary for them with --executive-summary (or --summary). If they haven't
83
+ seen this work before, open with one sentence on what it is. If they sent it back, open
84
+ with what changed since, and how their points were handled. Then list the assumptions
85
+ worth their attention and what would benefit from their eyes. On an outcome no agent
86
+ reviews, your work is refused without one.
87
+
88
+ Examples:
89
+ dotreq solution author document:jd7abc --comment "Named the customer."
90
+ dotreq solution author file:src/checkout.ts@guest pr:12 image:checkout.png --comment "Guest path, behind the flag."
91
+ dotreq solution author "preview:8080/design/roll-count pnpm dev" file:src/RollCount.tsx --title "Roll count after three dice" --title "Roll count component" --comment "The roll count, as a dev-only page."
92
+ `;
93
+ /** What `pull-request` does, on its `--help`, where `author --help` sends the agent (PULL-2). */
94
+ const PULL_REQUEST_HELP = `
95
+ Pushes your commits to the solution's branch, then opens a pull request from it
96
+ into the branch it was made from, ready for review, or finds the one already open.
97
+ Prints its reference and its address:
98
+
99
+ The solution's pull request is pr:12: https://github.com/<owner>/<repository>/pull/12
100
+
101
+ Submit it with the rest of your work, by its reference:
102
+
103
+ dotreq solution author pr:12 <reference>... --comment "..."
104
+
105
+ Asking again gives the same pull request while it is open, with your new commits
106
+ pushed to it; once it is merged or closed, a new one is opened.
107
+ `;
108
+ /**
109
+ * How to stall and what to say: on `stall --help`, where the brief sends the
110
+ * agent for it (VERB-5.4), in the form the Stop hook prints (VERB-5.6).
111
+ */
112
+ const STALL_HELP = `
113
+ Stall when something outside your reach has to change before your work can go
114
+ on, such as a host you can't reach or access you don't have, and no decision
115
+ would fix it. Say exactly what has to change:
116
+
117
+ dotreq solution stall "<what has to change>"
118
+
119
+ The outcome waits until that is fixed and you are started again.
120
+ `;
121
+ /** The command as the Stop hook prints it, after the request contract on `ask --help`. */
122
+ const ASK_EXAMPLES = `
123
+ Examples:
124
+ dotreq solution ask question.json
125
+
126
+ A reviewer doesn't ask: it escalates, with dotreq solution review escalate --comment "...".
127
+ `;
128
+ /**
129
+ * What each result does, on `review --help` (STEP-REVIEW-1, 2), and the
130
+ * executive summary an approval carries, in the words the shared reviewer
131
+ * block gives it (VERB-2.5, `defaultInstructions/everyStep.ts`).
132
+ */
133
+ const REVIEW_RESULTS = `
134
+ Results:
135
+ approved The work moves on.
136
+ approved-with-revisions What remains is small: the drafter fixes it once, and it moves on without another review.
137
+ needs-revision The drafter revises and you read it again. Not recorded from the twelfth round.
138
+ escalate The owner decides: approve it, or send it back. Your comment is the note they read.
139
+
140
+ When you approve, write an executive summary for the owner with --executive-summary (or
141
+ --summary), describing the work as it will stand once any fixes you asked for are made. If
142
+ the owner hasn't seen this work before, open with one sentence on what it is. If they sent
143
+ it back, open with what changed since, and how their points were handled. Then two short
144
+ lists, one item per line:
145
+ Assumptions: the ones the work rests on that are worth the owner's attention, not all of them.
146
+ Look at: what would benefit from the owner's eyes.
147
+ Don't recount the review; your comment carries that, for the drafter, who revises from it.
148
+ Only an approval carries an executive summary.
149
+
150
+ A demo's script is recorded by the agent reviewing it. Once you would approve the script,
151
+ record the video from the copy in your checkout (.dotreq/demo-scripts/<file>) with
152
+ agentic-demo record, fixing anything small first, such as a selector, a wait or a word.
153
+ Then approve with the video and the script as you left it, as one argument after the
154
+ result, and say in your comment what you changed. If the script needs more than that, give
155
+ needs-revision, with no video. Only a plain approval carries a video.
156
+
157
+ Examples:
158
+ dotreq solution review approved --comment "Checked the pitch against the board; the customer is named." --executive-summary "A pitch for guest checkout, for first-time buyers. Look at the appetite: two weeks."
159
+ dotreq solution review escalate --comment "Whether to name a segment is your call: the drafter and I disagree."
160
+ dotreq solution review needs-revision --comment "The risk score gives no reason."
161
+ dotreq solution review approved "video:demo/final.mp4 .dotreq/demo-scripts/steps.json" --comment "Recorded it; step 3 now waits for the receipt." --executive-summary "A two-minute demo of guest checkout. Look at step 4: the receipt."
162
+ `;
163
+ function wrap(fn) {
164
+ return async (...args) => {
165
+ try {
166
+ await fn(...args);
167
+ }
168
+ catch (error) {
169
+ console.error(`\nError: ${error instanceof Error ? error.message : String(error)}\n`);
170
+ process.exit(1);
171
+ }
172
+ };
173
+ }
174
+ /**
175
+ * `dotreq solution`: work a solution through its plan of outcomes
176
+ * (docs/working/plan-surfaces.md). A person creates a solution and can
177
+ * start an agent's work by hand; an agent runs `next` when invoked, which
178
+ * gives it an outcome to draft or to review, and records what came of it
179
+ * with one verb per act — `author`, `review`, `ask`, `stall` — each naming
180
+ * its object and no ids (VERB-4), and reads the outcome's thread with
181
+ * `thread`.
182
+ */
183
+ export function registerSolution(program) {
184
+ const solution = program
185
+ .command("solution", HIDDEN) // until solutions launch (SOLACCESS-2)
186
+ .description("Work a solution through its plan");
187
+ solution
188
+ .command("create [file]")
189
+ .description("Create a solution from the project's default plan, or from a plan file (YAML or JSON: title, plan, inputs)")
190
+ .option("--title <title>", "The solution's title: required without a file, else when the file has none")
191
+ .option("--description <text>", "What the solution is beyond its title, in Markdown: wins over the file's")
192
+ .option("--idea <reference...>", "Where the idea is written down, for the outcome that makes the solution exist: document:<id>, file:<path>[@<branch>], link:<url>")
193
+ .action(wrap(createCommand));
194
+ whereOptions(solution
195
+ .command("status")
196
+ .description("Where the outcome you are at stands, as recorded"))
197
+ .option("--json", "Print as JSON")
198
+ .action(wrap(statusCommand));
199
+ solution
200
+ .command("start <solution-id> <agent>")
201
+ .description("Start an agent's work by hand: a stalled agent again, on the part of an outcome it has due")
202
+ .option("--outcome <name>", "The outcome to start it on, when it has more than one due; otherwise its first")
203
+ .action(wrap(startCommand));
204
+ whereOptions(solution
205
+ .command("next", HIDDEN)
206
+ .description("Pick up your next outcome to draft or review: starts it and prints its instructions"))
207
+ .option("--json", "Print the kind, the outcome and the text as JSON")
208
+ .action(wrap(nextCommand));
209
+ whereOptions(solution
210
+ .command("author <reference...>", HIDDEN)
211
+ .description("Record what you made, and what changed and why"))
212
+ .option("--comment <text>", "What changed and why: the next reader sees it first (required)")
213
+ .option("--executive-summary <text>", "For the owner, when your work moves on before anyone reviews it (required there)")
214
+ .option("--summary <text>", "Short for --executive-summary")
215
+ .option("--title <text>", "A reference's name on the outcome's tab, for the owner: one per reference, in the order the references are given, or none", (value, previous = []) => [...previous, value])
216
+ .addHelpText("after", REFERENCE_FORMS)
217
+ .action(wrap(authorCommand));
218
+ whereOptions(solution
219
+ .command("pull-request", HIDDEN)
220
+ .description("Push the solution's branch and open its pull request, or find the one open; prints its reference, pr:<number>, to submit"))
221
+ .addHelpText("after", PULL_REQUEST_HELP)
222
+ .action(wrap(pullRequestCommand));
223
+ whereOptions(solution
224
+ .command("review <result> [video...]", HIDDEN)
225
+ .description("Record the review's result: approved, approved-with-revisions, needs-revision or escalate"))
226
+ .option("--comment <text>", "What should change, what you checked, or what the owner is to decide: required for every result")
227
+ .option("--executive-summary <text>", "For the owner, with every approval (required there)")
228
+ .option("--summary <text>", "Short for --executive-summary")
229
+ .addHelpText("after", REVIEW_RESULTS)
230
+ .action(wrap((result, videos, options) => reviewCommand(result, { ...options, videos })));
231
+ solution
232
+ .command("rehearse <scenario>")
233
+ .description("Run a scenario — a plan or some of its outcomes, its inputs, a real agent or a script for each agent — against the linked project, and report what happened. Its earlier runs are removed first, with what they made")
234
+ .option("--keep", "Leave the scenario's earlier runs in place, to review the new run beside them")
235
+ .action(wrap(rehearseCommand));
236
+ whereOptions(solution
237
+ .command("ask <request-file>", HIDDEN)
238
+ .description("Put a decision to the owner, as JSON in the named file; the drafter's, since a reviewer escalates"))
239
+ .addHelpText("after", `\n${REQUEST_CONTRACT}\n${ASK_EXAMPLES}`)
240
+ .action(wrap(askCommand));
241
+ whereOptions(solution
242
+ .command("thread", HIDDEN)
243
+ .description("The outcome's thread, oldest first: every submission, review, decision, stall and restart on it"))
244
+ .option("--latest [n]", "Only the newest n submission notes, review results, escalations and decisions, still oldest first; alone, the newest one. Stalls, restarts and redrafts are left out")
245
+ .action(wrap(threadCommand));
246
+ whereOptions(solution
247
+ .command("things <name>", HIDDEN)
248
+ .description("What an outcome or an input holds now, or the outcomes a checkpoint presents: one reference a line")).action(wrap(thingsCommand));
249
+ whereOptions(solution
250
+ .command("stall <reason>", HIDDEN)
251
+ .description("Your work cannot go on, and no decision would fix that: what has to change before it can be started again"))
252
+ // The runner's own stall, after its push: the reason already says why
253
+ // the work couldn't be pushed.
254
+ .addOption(new Option("--no-push").hideHelp())
255
+ .addHelpText("after", STALL_HELP)
256
+ .action(wrap(stallCommand));
257
+ // The runner's, around each session (SOLBRANCH-1, 2, 4): an agent never
258
+ // types them.
259
+ whereOptions(solution
260
+ .command("checkout", HIDDEN)
261
+ .description("Put the checkout on the solution's branch, made on its first session, with what other agents pushed to it"), "With --share: the outcome handed over, as the command you were given names it")
262
+ .option("--json", "Print the branch and what to tell the agent as JSON")
263
+ .option("--share <token>", "Check a solution's work out into this repository, from the share command you were given")
264
+ .option("--branch-wins", "With --share: where the branch has a newer version of a document you changed and didn't push, take the branch's")
265
+ .action(wrap(checkoutCommand));
266
+ whereOptions(solution
267
+ .command("push", HIDDEN)
268
+ .description("Send the documents you changed to the solution's branch, in a repository its share command checked out; in a runner, push the agent's commits to the run's branch"))
269
+ .option("--json", "Print whether it pushed, and why not, as JSON")
270
+ .option("--repo-wins", "Where the branch has a newer version of a document you changed, save yours over it; the branch keeps the one replaced")
271
+ .action(wrap(pushCommand));
272
+ // SOLSHARE-4: whoever an outcome waits on says it's done, from the
273
+ // repository the solution was checked out into.
274
+ solution
275
+ .command("done [where...]")
276
+ .description("Hand the outcome you checked out back: where what you made is, such as a pull request, and what you did")
277
+ .option("--comment <text>", "What you did")
278
+ .option("--solution <solution>", "Which solution, when more than one is checked out here")
279
+ .action(wrap(doneCommand));
280
+ }
281
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ import { type WhereOptions } from "./run-file.js";
2
+ /**
3
+ * `dotreq solution things <name>`: what an outcome, an input, or the
4
+ * outcomes a checkpoint presents hold now, one reference a line, under
5
+ * each outcome's name. Where a brief says what else the solution has made,
6
+ * to look up if it matters, it names this command.
7
+ */
8
+ export declare function thingsCommand(name: string, options: WhereOptions): Promise<void>;
9
+ //# sourceMappingURL=look-up.d.ts.map