@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
package/README.md CHANGED
@@ -251,13 +251,13 @@ Browser testing needs a Stagehand model and the API key for that model's provide
251
251
  {
252
252
  "defaultURL": "https://your-app.com",
253
253
  "browserTest": {
254
- "modelName": "gateway/anthropic/claude-haiku-4-5",
254
+ "modelName": "gateway/anthropic/claude-haiku-5.5",
255
255
  "modelApiKey": "your-api-key"
256
256
  }
257
257
  }
258
258
  ```
259
259
 
260
- Pick any Stagehand-supported model. Example values: `gateway/anthropic/claude-haiku-4-5` (Vercel AI Gateway key), `google/gemini-3-flash-preview` (Gemini key).
260
+ Pick any Stagehand-supported model. Example values: `gateway/anthropic/claude-haiku-5.5` (Vercel AI Gateway key), `google/gemini-3-flash-preview` (Gemini key).
261
261
 
262
262
  Optional settings: `vercelBypassSecret`, `browserbaseApiKey`, `browserbaseProjectId`. Setting both Browserbase fields switches runs from LOCAL (spawns Playwright on your machine) to BROWSERBASE (managed cloud browsers).
263
263
 
package/dist/cli.js CHANGED
@@ -2,10 +2,11 @@
2
2
  import { readFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { Command } from "commander";
5
+ import { Command, Option } from "commander";
6
6
  import { acceptanceTestCommand } from "./commands/acceptance-test.js";
7
7
  import { aiSetupCommand } from "./commands/ai-setup.js";
8
8
  import { pullAlias, pushAlias } from "./commands/aliases.js";
9
+ import { registerBoard } from "./commands/board/index.js";
9
10
  import { registerCodebaseToSpec } from "./commands/codebase-to-spec/index.js";
10
11
  import { createRequirementDocumentCommand } from "./commands/create-requirement-document.js";
11
12
  import { diffCommand } from "./commands/diff.js";
@@ -16,11 +17,14 @@ import { initCommand } from "./commands/init.js";
16
17
  import { linkCommand } from "./commands/link.js";
17
18
  import { listCommand } from "./commands/list.js";
18
19
  import { mcpCommand } from "./commands/mcp.js";
20
+ import { registerMockup } from "./commands/mockup/index.js";
19
21
  import { prepareCommand } from "./commands/prepare.js";
20
22
  import { reportCommand } from "./commands/report.js";
21
23
  import { requirementsForCommand } from "./commands/requirements-for.js";
22
24
  import { reviewTestCommand } from "./commands/review-test.js";
25
+ import { registerScores } from "./commands/scores/index.js";
23
26
  import { searchCommand } from "./commands/search.js";
27
+ import { registerSolution } from "./commands/solution/index.js";
24
28
  import { styleCheckCommand } from "./commands/style-check.js";
25
29
  import { syncCliAction } from "./commands/sync.js";
26
30
  import { testsForCommand } from "./commands/tests-for.js";
@@ -115,6 +119,10 @@ program
115
119
  .option("-s, --share <token>", "Read-only share token (no setup required)")
116
120
  .option("-p, --project <id>", "Override the project slug")
117
121
  .option("-y, --yes", "Skip the deletion confirmation prompt")
122
+ // BRANCH-8: merging a solution's branch into main. Kept out of --help, as
123
+ // the solution verbs are, until the solution flow is offered.
124
+ .addOption(new Option("--branch-contributes <solution-id>", "Merge the solution's branch into main: its additions only").hideHelp())
125
+ .addOption(new Option("--branch-wins <solution-id>", "Merge the solution's branch into main: the branch overwrites what differs").hideHelp())
118
126
  .action(wrapCommand(syncCliAction));
119
127
  // Deprecated aliases into `sync`, kept for commands already in the wild
120
128
  // (SYNC-ALIAS-1). Each prints a deprecation notice naming its new spelling.
@@ -225,5 +233,9 @@ program
225
233
  .description("Print the greenfield interview protocol — for working out what something new should do before writing its spec")
226
234
  .action(wrapCommand(greenfieldDiscoveryCommand));
227
235
  registerCodebaseToSpec(program);
236
+ registerSolution(program);
237
+ registerMockup(program);
238
+ registerBoard(program);
239
+ registerScores(program);
228
240
  program.parse();
229
241
  //# sourceMappingURL=cli.js.map
package/dist/cloud.d.ts CHANGED
@@ -5,7 +5,11 @@
5
5
  */
6
6
  /** The web app, home of the hosted AI endpoints and publishing. */
7
7
  export declare const DEFAULT_API_BASE_URL = "https://app.dotrequirements.io";
8
- /** The Convex backend's address, for a ConvexHttpClient or its HTTP API. */
8
+ /**
9
+ * The Convex backend's address, for a ConvexHttpClient or its HTTP API:
10
+ * production, unless DOTREQ_CONVEX_URL points somewhere else, such as a dev
11
+ * or preview deployment.
12
+ */
9
13
  export declare function convexUrl(): string;
10
14
  /** The web app's address; `DOTREQUIREMENTS_API_URL` overrides it. */
11
15
  export declare function webApiUrl(): string;
package/dist/cloud.js CHANGED
@@ -15,10 +15,14 @@ function noteUse() {
15
15
  used = true;
16
16
  onFirstUse?.();
17
17
  }
18
- /** The Convex backend's address, for a ConvexHttpClient or its HTTP API. */
18
+ /**
19
+ * The Convex backend's address, for a ConvexHttpClient or its HTTP API:
20
+ * production, unless DOTREQ_CONVEX_URL points somewhere else, such as a dev
21
+ * or preview deployment.
22
+ */
19
23
  export function convexUrl() {
20
24
  noteUse();
21
- return CONVEX_URL;
25
+ return process.env.DOTREQ_CONVEX_URL?.trim() || CONVEX_URL;
22
26
  }
23
27
  /** The web app's address; `DOTREQUIREMENTS_API_URL` overrides it. */
24
28
  export function webApiUrl() {
@@ -52,7 +52,7 @@ export async function acceptanceTestCommand(requirementKey, url, options) {
52
52
  console.error("Error: no browser-test model configured in project-settings.json.");
53
53
  console.log("Add a block like:\n" +
54
54
  ' "browserTest": {\n' +
55
- ' "modelName": "gateway/anthropic/claude-haiku-4-5",\n' +
55
+ ' "modelName": "gateway/anthropic/claude-haiku-5.5",\n' +
56
56
  ' "modelApiKey": "..."\n' +
57
57
  " }\n" +
58
58
  "to .requirements/project-settings.json. See the docs for supported models.");
@@ -0,0 +1,13 @@
1
+ import type { Command } from "commander";
2
+ export declare function guideCommand(): Promise<void>;
3
+ export declare function createCommand(options: {
4
+ title: string;
5
+ description?: string;
6
+ file: string;
7
+ }): Promise<void>;
8
+ export declare function updateCommand(boardId: string, options: {
9
+ file: string;
10
+ }): Promise<void>;
11
+ export declare function showCommand(boardId: string): Promise<void>;
12
+ export declare function registerBoard(program: Command): void;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,96 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { webApiUrl } from "../../cloud.js";
4
+ import { getProjectCredentials } from "../../utils/project-settings.js";
5
+ /**
6
+ * Hidden from help until solutions launch: today's users see the CLI they
7
+ * have now, and the server refuses these commands for a team without the
8
+ * solutions switch (SOLACCESS-2).
9
+ */
10
+ const HIDDEN_UNTIL_LAUNCH = { hidden: true };
11
+ async function throughApp(path, extra) {
12
+ const { projectId, projectSecret } = getProjectCredentials();
13
+ const base = webApiUrl();
14
+ const response = await fetch(`${base}${path}`, {
15
+ method: "POST",
16
+ headers: { "Content-Type": "application/json" },
17
+ body: JSON.stringify({ projectId, projectSecret, ...extra }),
18
+ });
19
+ if (!response.ok) {
20
+ const body = (await response.json().catch(() => ({})));
21
+ // BOARD-CLI-3: every problem at once, each on its line — an agent that
22
+ // gets one problem per attempt spends a turn per typo.
23
+ const problems = (body.problems ?? [])
24
+ .map((problem) => problem.line === undefined
25
+ ? ` ${problem.message}`
26
+ : ` Line ${problem.line}: ${problem.message}`)
27
+ .join("\n");
28
+ throw new Error([body.error || response.statusText, problems]
29
+ .filter(Boolean)
30
+ .join("\n"));
31
+ }
32
+ return response;
33
+ }
34
+ export async function guideCommand() {
35
+ const response = await throughApp("/api/boards/guide", {});
36
+ const { guide } = (await response.json());
37
+ process.stdout.write(`${guide}\n`);
38
+ }
39
+ export async function createCommand(options) {
40
+ const yaml = readFileSync(resolve(options.file), "utf8");
41
+ const response = await throughApp("/api/boards/create", {
42
+ title: options.title,
43
+ description: options.description,
44
+ yaml,
45
+ });
46
+ const { boardId, message } = (await response.json());
47
+ console.log(`Created board ${boardId}. ${message} Submit it as board:${boardId}; read it back with: dotreq board show ${boardId}`);
48
+ }
49
+ export async function updateCommand(boardId, options) {
50
+ const yaml = readFileSync(resolve(options.file), "utf8");
51
+ const response = await throughApp("/api/boards/update", { boardId, yaml });
52
+ const { message } = (await response.json());
53
+ console.log(message);
54
+ }
55
+ export async function showCommand(boardId) {
56
+ const response = await throughApp("/api/boards/show", { boardId });
57
+ const { yaml } = (await response.json());
58
+ process.stdout.write(yaml.endsWith("\n") ? yaml : `${yaml}\n`);
59
+ }
60
+ export function registerBoard(program) {
61
+ const board = program
62
+ .command("board", HIDDEN_UNTIL_LAUNCH)
63
+ .description("Make and read discovery boards");
64
+ board
65
+ .command("guide")
66
+ .description("Print the board authoring guide: the YAML a board is written in, what each verb does, a complete example")
67
+ .action(wrap(guideCommand));
68
+ board
69
+ .command("create")
70
+ .description("Create a board in the project from a YAML file")
71
+ .requiredOption("--title <title>", "The board's title")
72
+ .option("--description <text>", "What the board explores")
73
+ .requiredOption("--file <path>", "The board file")
74
+ .action(wrap(createCommand));
75
+ board
76
+ .command("update <board-id>")
77
+ .description("Make a board say what a YAML file says")
78
+ .requiredOption("--file <path>", "The board file")
79
+ .action(wrap(updateCommand));
80
+ board
81
+ .command("show <board-id>")
82
+ .description("Print a board as the YAML it is written in")
83
+ .action(wrap(showCommand));
84
+ }
85
+ function wrap(fn) {
86
+ return async (...args) => {
87
+ try {
88
+ await fn(...args);
89
+ }
90
+ catch (error) {
91
+ console.error(`\nError: ${error instanceof Error ? error.message : String(error)}\n`);
92
+ process.exitCode = 1;
93
+ }
94
+ };
95
+ }
96
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * `dotreq sync --branch-contributes <solution-id>` and `--branch-wins
3
+ * <solution-id>`: merge a solution's branch into main (BRANCH-8). Both sides
4
+ * are in the cloud, so no local file is read or written; the web app's merge
5
+ * route does it, as it does for the solution page's Merge action.
6
+ */
7
+ type MergeDocument = {
8
+ title: string;
9
+ action: string;
10
+ because?: string;
11
+ };
12
+ type MergeRefusal = {
13
+ kind: "conflicts";
14
+ documents: string[];
15
+ } | {
16
+ kind: "key";
17
+ key: string;
18
+ holder: string;
19
+ document: string;
20
+ } | {
21
+ kind: "limit";
22
+ message: string;
23
+ } | {
24
+ kind: "swap";
25
+ documents: string[];
26
+ } | {
27
+ kind: "nothing";
28
+ };
29
+ type MergePlan = {
30
+ solution: {
31
+ title: string;
32
+ };
33
+ documents: MergeDocument[];
34
+ refusal: MergeRefusal | null;
35
+ };
36
+ type MergeOutcome = {
37
+ kind: "merged";
38
+ landed: string[];
39
+ plan: MergePlan;
40
+ } | {
41
+ kind: "refused";
42
+ plan: MergePlan;
43
+ } | {
44
+ kind: "no branch";
45
+ } | {
46
+ kind: "stopped";
47
+ landed: string[];
48
+ at: string;
49
+ reason: string;
50
+ plan: MergePlan;
51
+ };
52
+ export type BranchMergeFlags = {
53
+ branchContributes?: string;
54
+ branchWins?: string;
55
+ };
56
+ /** The flags a branch merge can't be combined with (BRANCH-8.3). */
57
+ declare const OTHER_FLAGS: readonly ["cloudContributes", "repoContributes", "cloudWins", "repoWins", "share", "project"];
58
+ /**
59
+ * The merge a sync's flags ask for, or null when they ask for none. Throws,
60
+ * saying so, when they combine a merge with anything else (BRANCH-8.3).
61
+ */
62
+ export declare function branchMergeOf(scope: string[], options: BranchMergeFlags & {
63
+ [K in (typeof OTHER_FLAGS)[number]]?: unknown;
64
+ }): {
65
+ solutionId: string;
66
+ mode: "contributes" | "wins";
67
+ } | null;
68
+ /** What a merge says it did, line by line; and whether it did it. */
69
+ export declare function describeMerge(outcome: MergeOutcome): {
70
+ lines: string[];
71
+ ok: boolean;
72
+ };
73
+ export declare function branchMergeCommand(merge: {
74
+ solutionId: string;
75
+ mode: "contributes" | "wins";
76
+ }): Promise<void>;
77
+ export {};
78
+ //# sourceMappingURL=branch-merge.d.ts.map
@@ -0,0 +1,129 @@
1
+ import { webApiUrl } from "../cloud.js";
2
+ import { getProjectCredentials } from "../utils/project-settings.js";
3
+ import { checkedOut } from "./solution/share.js";
4
+ /** The flags a branch merge can't be combined with (BRANCH-8.3). */
5
+ const OTHER_FLAGS = [
6
+ "cloudContributes",
7
+ "repoContributes",
8
+ "cloudWins",
9
+ "repoWins",
10
+ "share",
11
+ // The merge runs on the linked project's credential; a project named here
12
+ // would be quietly ignored, so it is refused instead.
13
+ "project",
14
+ ];
15
+ /**
16
+ * The merge a sync's flags ask for, or null when they ask for none. Throws,
17
+ * saying so, when they combine a merge with anything else (BRANCH-8.3).
18
+ */
19
+ export function branchMergeOf(scope, options) {
20
+ const contributes = options.branchContributes;
21
+ const wins = options.branchWins;
22
+ if (contributes === undefined && wins === undefined)
23
+ return null;
24
+ const combined = (contributes !== undefined && wins !== undefined) ||
25
+ scope.length > 0 ||
26
+ OTHER_FLAGS.some((flag) => options[flag]);
27
+ if (combined) {
28
+ throw new Error("A branch merge can't be combined with other sync flags or a scope: run `dotreq sync --branch-contributes <solution-id>` or `dotreq sync --branch-wins <solution-id>` on its own.");
29
+ }
30
+ return contributes !== undefined
31
+ ? { solutionId: contributes, mode: "contributes" }
32
+ : { solutionId: wins, mode: "wins" };
33
+ }
34
+ /** What a merge says it did, line by line; and whether it did it. */
35
+ export function describeMerge(outcome) {
36
+ if (outcome.kind === "no branch") {
37
+ return {
38
+ lines: ["That solution has nothing on a branch to merge."],
39
+ ok: false,
40
+ };
41
+ }
42
+ const title = outcome.plan.solution.title;
43
+ if (outcome.kind === "merged") {
44
+ return {
45
+ lines: [
46
+ `Merged the "${title}" branch into main.`,
47
+ ...outcome.landed.map((doc) => ` landed: ${doc}`),
48
+ ],
49
+ ok: true,
50
+ };
51
+ }
52
+ if (outcome.kind === "stopped") {
53
+ return {
54
+ lines: [
55
+ `The "${title}" merge stopped at ${outcome.at}: ${outcome.reason}`,
56
+ ...outcome.landed.map((doc) => ` landed: ${doc}`),
57
+ ],
58
+ ok: false,
59
+ };
60
+ }
61
+ const refusal = outcome.plan.refusal;
62
+ switch (refusal.kind) {
63
+ case "conflicts":
64
+ return {
65
+ lines: [
66
+ `Nothing merged: these documents on the "${title}" branch conflict with main.`,
67
+ ...refusal.documents.map((doc) => ` conflict: ${doc}`),
68
+ "Resolve them on the branch and merge again, or let the branch win with `dotreq sync --branch-wins <solution-id>`.",
69
+ ],
70
+ ok: false,
71
+ };
72
+ case "key":
73
+ return {
74
+ lines: [
75
+ `Nothing merged: ${refusal.document} uses the requirement key ${refusal.key}, which "${refusal.holder}" on main has.`,
76
+ ],
77
+ ok: false,
78
+ };
79
+ case "limit":
80
+ return { lines: [`Nothing merged: ${refusal.message}`], ok: false };
81
+ case "swap":
82
+ return {
83
+ lines: [
84
+ `Nothing merged: ${refusal.documents.join(" and ")} take each other's requirement keys, which can't land one at a time. Give one of them new keys on the branch.`,
85
+ ],
86
+ ok: false,
87
+ };
88
+ case "nothing":
89
+ return {
90
+ lines: [
91
+ `Nothing to merge: every document on the "${title}" branch is already on main.`,
92
+ ],
93
+ ok: true,
94
+ };
95
+ }
96
+ }
97
+ export async function branchMergeCommand(merge) {
98
+ // SOLSHARE-5.0: a repository a solution's share command checked out holds
99
+ // no credential that merges; say so rather than asking for a link.
100
+ let credentials;
101
+ try {
102
+ credentials = getProjectCredentials();
103
+ }
104
+ catch (error) {
105
+ if (checkedOut().length > 0) {
106
+ throw new Error("Nothing merged: the share command you checked out with can't merge a solution's branch. Whoever runs the solution merges it, from the app.");
107
+ }
108
+ throw error;
109
+ }
110
+ const { projectId, projectSecret } = credentials;
111
+ const apiBaseUrl = webApiUrl();
112
+ const response = await fetch(`${apiBaseUrl}/api/branches/merge`, {
113
+ method: "POST",
114
+ headers: { "Content-Type": "application/json" },
115
+ body: JSON.stringify({ ...merge, projectId, projectSecret }),
116
+ });
117
+ const body = (await response.json().catch(() => null));
118
+ if (!body || !("kind" in body)) {
119
+ throw new Error(body?.error ??
120
+ `The merge failed (${response.status}).`);
121
+ }
122
+ const { lines, ok } = describeMerge(body);
123
+ for (const line of lines)
124
+ console.log(line);
125
+ // BRANCH-8.4: a merge that finds conflicts exits non-zero.
126
+ if (!ok)
127
+ process.exitCode = 1;
128
+ }
129
+ //# sourceMappingURL=branch-merge.js.map
@@ -5,16 +5,17 @@
5
5
  * the detail one level. Exits 0 regardless of verdicts unless `--exit-code` is
6
6
  * passed, which makes any non-in-sync document exit 1 (the CI drift gate).
7
7
  */
8
- import { acquireCloudSnapshot, acquireLocalSnapshot, compareSnapshots, filterByScope, formatUnitDetail, formatVerdictLine, resolutionHint, } from "../sync/index.js";
8
+ import { acquireCloudSnapshot, acquireLocalSnapshot, compareSnapshots, filterByScope, foreignDocumentNotes, formatUnitDetail, formatVerdictLine, resolutionHint, } from "../sync/index.js";
9
9
  import { brand } from "../utils/brand.js";
10
10
  import { resolveCloudAuth } from "./sync-common.js";
11
11
  export async function diffCommand(scope, options) {
12
12
  const workspaceRoot = process.cwd();
13
13
  const auth = resolveCloudAuth(options);
14
- const [local, cloud] = await Promise.all([
15
- acquireLocalSnapshot(workspaceRoot),
16
- acquireCloudSnapshot(auth),
17
- ]);
14
+ // SOLACCESS-2.2: as the sync carries them.
15
+ const cloud = await acquireCloudSnapshot(auth);
16
+ const local = await acquireLocalSnapshot(workspaceRoot, undefined, {
17
+ solutions: cloud.solutions === true,
18
+ });
18
19
  const comparison = compareSnapshots(local, cloud);
19
20
  const { documents, unmatched } = filterByScope(comparison, scope, workspaceRoot);
20
21
  for (const token of unmatched) {
@@ -38,12 +39,21 @@ export async function diffCommand(scope, options) {
38
39
  console.log("No documents to compare.");
39
40
  return;
40
41
  }
42
+ // SYNC-FAIL-5: a file whose id names another project's document is only
43
+ // in the repo, and a sync will refuse it; say so here as the sync will.
44
+ const foreign = await foreignDocumentNotes(auth, documents.flatMap((d) => d.verdict === "only_in_repo" && d.documentId ? [d.documentId] : []));
41
45
  console.log(`\n=== Repo vs ${brand} cloud ===\n`);
42
46
  for (const doc of documents) {
43
47
  console.log(formatVerdictLine(doc));
44
48
  if (doc.verdict === "invalid_file" && doc.error) {
45
49
  console.log(` ${doc.error}`);
46
50
  }
51
+ const foreignNote = doc.verdict === "only_in_repo" && doc.documentId
52
+ ? foreign.get(doc.documentId)
53
+ : undefined;
54
+ if (foreignNote) {
55
+ console.log(` ${foreignNote}`);
56
+ }
47
57
  if (verbose && doc.details && doc.details.length > 0) {
48
58
  for (const detail of doc.details) {
49
59
  console.log(formatUnitDetail(detail));
@@ -0,0 +1,11 @@
1
+ import type { Command } from "commander";
2
+ export declare function guideCommand(): Promise<void>;
3
+ export declare function createCommand(options: {
4
+ title: string;
5
+ file: string;
6
+ }): Promise<void>;
7
+ export declare function previewCommand(mockupId: string, options: {
8
+ out?: string;
9
+ }): Promise<void>;
10
+ export declare function registerMockup(program: Command): void;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,82 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { webApiUrl } from "../../cloud.js";
4
+ import { api } from "../../convex.js";
5
+ import { getProjectCredentials } from "../../utils/project-settings.js";
6
+ import { solutionClient } from "../solution/client.js";
7
+ /**
8
+ * Hidden from help until solutions launch: today's users see the CLI they
9
+ * have now, and the server refuses these commands for a team without the
10
+ * solutions switch (SOLACCESS-2).
11
+ */
12
+ const HIDDEN_UNTIL_LAUNCH = { hidden: true };
13
+ /**
14
+ * Mockups from the shell (SIGHT-5): the guide, a mockup made from a file,
15
+ * and a mockup rendered to a file an agent can open in its browser. The
16
+ * guide and the render come through the app, which owns the dialect's
17
+ * text and its renderer; the create goes to the backend.
18
+ */
19
+ async function throughApp(path, extra) {
20
+ const { projectId, projectSecret } = getProjectCredentials();
21
+ const base = webApiUrl();
22
+ const response = await fetch(`${base}${path}`, {
23
+ method: "POST",
24
+ headers: { "Content-Type": "application/json" },
25
+ body: JSON.stringify({ projectId, projectSecret, ...extra }),
26
+ });
27
+ if (!response.ok) {
28
+ const body = (await response.json().catch(() => ({})));
29
+ throw new Error(body.error || response.statusText);
30
+ }
31
+ return response;
32
+ }
33
+ export async function guideCommand() {
34
+ const response = await throughApp("/api/mockups/guide", {});
35
+ const { guide } = (await response.json());
36
+ process.stdout.write(`${guide}\n`);
37
+ }
38
+ export async function createCommand(options) {
39
+ const markup = readFileSync(resolve(options.file), "utf8");
40
+ const client = solutionClient();
41
+ const { mockupId } = await client.mutation(api.mockups.mutations.create, { project: client.project, title: options.title, markup });
42
+ console.log(`Created mockup ${mockupId}. Submit it as mockup:${mockupId}; render it with: dotreq mockup preview ${mockupId} --out mockup.html`);
43
+ }
44
+ export async function previewCommand(mockupId, options) {
45
+ const response = await throughApp("/api/mockups/frame", { mockupId });
46
+ const html = await response.text();
47
+ const out = resolve(options.out ?? `mockup-${mockupId}.html`);
48
+ writeFileSync(out, html);
49
+ console.log(`Wrote ${out}. Look at it: agent-browser open file://${out} && agent-browser screenshot mockup.png`);
50
+ }
51
+ export function registerMockup(program) {
52
+ const mockup = program
53
+ .command("mockup", HIDDEN_UNTIL_LAUNCH)
54
+ .description("Make and look at wireframe mockups");
55
+ mockup
56
+ .command("guide")
57
+ .description("Print the mockup authoring guide: the markup dialect, the components, a complete example")
58
+ .action(wrap(guideCommand));
59
+ mockup
60
+ .command("create")
61
+ .description("Create a mockup in the project from a markup file")
62
+ .requiredOption("--title <title>", "The mockup's title")
63
+ .requiredOption("--file <path>", "The markup file")
64
+ .action(wrap(createCommand));
65
+ mockup
66
+ .command("preview <mockup-id>")
67
+ .description("Render a mockup to a self-contained HTML file, to open in a browser")
68
+ .option("--out <path>", "Where to write it (default: mockup-<id>.html)")
69
+ .action(wrap(previewCommand));
70
+ }
71
+ function wrap(fn) {
72
+ return async (...args) => {
73
+ try {
74
+ await fn(...args);
75
+ }
76
+ catch (error) {
77
+ console.error(`\nError: ${error instanceof Error ? error.message : String(error)}\n`);
78
+ process.exitCode = 1;
79
+ }
80
+ };
81
+ }
82
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,50 @@
1
+ import type { Command } from "commander";
2
+ import type { Scores } from "../solution/things.js";
3
+ /**
4
+ * Scores from the shell (VERB-7, SCORES-1): a solution's price on CREATED's
5
+ * seven axes, made with `create` and revised with `update`, each axis a
6
+ * whole number from 1 to 5 with a reason. Like a board or a mockup, scores
7
+ * are a thing: `create` prints the `scores:<id>` a writer submits with
8
+ * `dotreq solution author`, whose comment says what changed and why.
9
+ * Nothing reaches the backend unless every axis is whole, and the backend
10
+ * refuses the same again.
11
+ */
12
+ /**
13
+ * The seven axes as `dotreq scores` takes them (VERB-7): the key the backend stores,
14
+ * the name a person reads, and the option, which is also how a line of the
15
+ * reasons file names the axis. The backend's own list is
16
+ * packages/convex/solutions/scores.ts; the CLI can't import it, so a test
17
+ * keeps the two equal.
18
+ */
19
+ export declare const AXES: ReadonlyArray<{
20
+ key: keyof Scores;
21
+ label: string;
22
+ option: string;
23
+ }>;
24
+ export type ScoresOptions = {
25
+ costOfDelay?: string;
26
+ relevance?: string;
27
+ entities?: string;
28
+ api?: string;
29
+ testing?: string;
30
+ experience?: string;
31
+ doubt?: string;
32
+ /** A file with one line per axis: `<axis>: <reason>`. */
33
+ reasons?: string;
34
+ };
35
+ /**
36
+ * Each axis's reason from the reasons file (VERB-7.0): one line an axis,
37
+ * `<axis>: <reason>`, the axis spelled as its option is, in any case; a
38
+ * leading "- " and blank lines are passed over. Refuses a line in another
39
+ * form, an axis that isn't one of the seven or is named twice (VERB-7.3),
40
+ * and an axis with no reason (VERB-7.2).
41
+ */
42
+ export declare function reasonsFrom(text: string, file: string): Record<keyof Scores, string>;
43
+ /** `dotreq scores create`: new scores, and the reference to record them by (VERB-7.0). */
44
+ export declare function createCommand(options: ScoresOptions): Promise<void>;
45
+ /** `dotreq scores update <id>`: all seven axes again, for a revision (VERB-7.1). */
46
+ export declare function updateCommand(scoresId: string, options: ScoresOptions): Promise<void>;
47
+ /** The axes, the reasons file and an example, on `scores --help` (VERB-7.4). */
48
+ export declare const SCORES_HELP = "\nScores are seven axes, each a whole number from 1 to 5 with one line of reasoning:\n suitability, whether to do this now:\n --cost-of-delay, --relevance\n appetite, what it takes to build and how likely the other six are wrong:\n --entities, --api, --testing, --experience, --doubt\n\nThe reasons file has one line per axis, the axis named as its option is,\nwithout the dashes:\n cost-of-delay: <why this score>\n relevance: <why>\n entities: <why>\n api: <why>\n testing: <why>\n experience: <why>\n doubt: <why>\n\ncreate prints the scores' reference; record them with it, as any work is\nrecorded. To revise them, update them with all seven axes again and record\nthem again.\n\nExamples:\n dotreq scores create --cost-of-delay 4 --relevance 3 --entities 2 --api 2 \\\n --testing 2 --experience 3 --doubt 2 --reasons reasons.txt\n dotreq solution author scores:<id> --comment \"Scored against the pitch's draft and the discovery board.\"\n dotreq scores update <id> --cost-of-delay 4 --relevance 3 --entities 3 --api 2 \\\n --testing 2 --experience 3 --doubt 3 --reasons reasons.txt\n";
49
+ export declare function registerScores(program: Command): void;
50
+ //# sourceMappingURL=index.d.ts.map