@dev.fast/whiteboard 0.0.0-stage → 0.2.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 (247) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -2
  3. package/THIRD_PARTY_NOTICES.md +93 -0
  4. package/dist/account-alias-DOHN1RSH.js +973 -0
  5. package/dist/agent-cli-ChOq0ZuO.js +117 -0
  6. package/dist/agent-cli-DzWfZr6G.js +2 -0
  7. package/dist/agent-client-CPQo7iTI.js +305 -0
  8. package/dist/authoring-tools-Cr6Kpjsx.js +234 -0
  9. package/dist/build-info.json +1 -0
  10. package/dist/cli-hVgHcdsv.js +47 -0
  11. package/dist/cli-runner-D8y2_luU.js +5095 -0
  12. package/dist/cli.d.ts +1 -0
  13. package/dist/cli.js +128 -0
  14. package/dist/client-DFoh50W3.js +950 -0
  15. package/dist/desktop-discovery-DBO-PM2V.js +164 -0
  16. package/dist/error-message-OtiDonty.js +6 -0
  17. package/dist/fs-utils-BMPLt0cr.js +22 -0
  18. package/dist/fuzzy-match-BoAmcyak.js +30 -0
  19. package/dist/headless-host-BJxf4dUU.js +94 -0
  20. package/dist/input-error-OLgB_h31.js +11 -0
  21. package/dist/local-data-CxLk75rx.js +4491 -0
  22. package/dist/mcp-QeF8vkdf.js +140 -0
  23. package/dist/package-paths-B6-zxvIO.js +47 -0
  24. package/dist/process-error-telemetry-v9e5h6D7.js +3952 -0
  25. package/dist/profile-4ry2f3AM.js +76 -0
  26. package/dist/profile-Bh5FIrmf.js +2 -0
  27. package/dist/request-origin-D_4QIdoN.js +811 -0
  28. package/dist/review-agent-traces-DNJFYzc8.js +3866 -0
  29. package/dist/review-home-paths-6zZH-1c9.js +2560 -0
  30. package/dist/review-telemetry-DEFnQsQ2.js +1195 -0
  31. package/dist/runtime-CKiWhGrf.js +52 -0
  32. package/dist/runtime.d.ts +9 -0
  33. package/dist/runtime.js +2 -0
  34. package/dist/s3-SzLpeCG6.js +345 -0
  35. package/dist/s3-config-BgxkSoOy.js +2 -0
  36. package/dist/s3-config-CAxhO9u_.js +676 -0
  37. package/dist/server/desktop-host.d.ts +4 -0
  38. package/dist/server/desktop-host.js +1702 -0
  39. package/dist/server-discovery-Dbzn3w6Z.js +66 -0
  40. package/dist/sharing/index.d.ts +1977 -0
  41. package/dist/sharing/index.js +2 -0
  42. package/dist/src-CGP5ytbV.js +283 -0
  43. package/dist/src-CmdiBL20.js +3676 -0
  44. package/dist/src-DnwdaQ2r.js +865 -0
  45. package/dist/src-X9phtB2j.js +68 -0
  46. package/dist/stored-document-migration-DHeMpLMb.js +2671 -0
  47. package/dist/tool-failure-C8zB73HV.js +98 -0
  48. package/dist/tutorial-trace-DrBvkaeL.js +43 -0
  49. package/instructions/authoring.md +36 -0
  50. package/instructions/file-lenses.md +13 -0
  51. package/instructions/scratchpad.md +23 -0
  52. package/instructions/trace-archaeology.md +113 -0
  53. package/onboarding.md +8 -0
  54. package/package.json +106 -3
  55. package/src/agent-selection.ts +100 -0
  56. package/src/agent-session-ref.ts +80 -0
  57. package/src/ask/agents.ts +351 -0
  58. package/src/ask/checkout-files.ts +47 -0
  59. package/src/ask/file-refs.ts +111 -0
  60. package/src/ask/pi-mcp.ts +154 -0
  61. package/src/ask/protocol.ts +244 -0
  62. package/src/ask/thread-state.ts +350 -0
  63. package/src/ask/thread.ts +1400 -0
  64. package/src/ask/threads.ts +219 -0
  65. package/src/ask/watch.ts +79 -0
  66. package/src/cli-install.ts +1041 -0
  67. package/src/cli-runner.ts +1461 -0
  68. package/src/cli-runtime-info.ts +35 -0
  69. package/src/cli.ts +222 -0
  70. package/src/connect-prompts.ts +248 -0
  71. package/src/cursor-deeplink.ts +11 -0
  72. package/src/desktop-discovery.ts +355 -0
  73. package/src/diff-selection-migration.ts +63 -0
  74. package/src/embedded-posthog-key.ts +6 -0
  75. package/src/error-telemetry.ts +247 -0
  76. package/src/evidence.ts +14 -0
  77. package/src/exception-telemetry.ts +126 -0
  78. package/src/fixtures/blocks/call_stack_diff.json +27 -0
  79. package/src/fixtures/blocks/callout.json +11 -0
  80. package/src/fixtures/blocks/code.json +9 -0
  81. package/src/fixtures/blocks/code_peek.json +8 -0
  82. package/src/fixtures/blocks/database_lens.json +51 -0
  83. package/src/fixtures/blocks/divider.json +1 -0
  84. package/src/fixtures/blocks/fixtures.ts +33 -0
  85. package/src/fixtures/blocks/flow_diagram.json +36 -0
  86. package/src/fixtures/blocks/ids.ts +8 -0
  87. package/src/fixtures/blocks/image.json +9 -0
  88. package/src/fixtures/blocks/markdown.json +7 -0
  89. package/src/fixtures/blocks/section.json +11 -0
  90. package/src/fixtures/blocks/sequence.json +31 -0
  91. package/src/fixtures/blocks/software_map.json +7 -0
  92. package/src/fixtures/blocks/trace_quote.json +9 -0
  93. package/src/fixtures/blocks/tutorial.json +51 -0
  94. package/src/fs-utils.ts +32 -0
  95. package/src/fuzzy-match.ts +94 -0
  96. package/src/install.ts +29 -0
  97. package/src/legacy-skills.ts +133 -0
  98. package/src/lens-selection.ts +230 -0
  99. package/src/markdown-latex-math.ts +230 -0
  100. package/src/markdown.ts +81 -0
  101. package/src/package-paths.ts +53 -0
  102. package/src/posthog-capture-client.ts +610 -0
  103. package/src/review-api/README.md +295 -0
  104. package/src/review-api/activity.ts +340 -0
  105. package/src/review-api/agent-cli.ts +219 -0
  106. package/src/review-api/agent-client.ts +188 -0
  107. package/src/review-api/anchor-quotes.ts +88 -0
  108. package/src/review-api/ask-history.ts +242 -0
  109. package/src/review-api/authoring-tools.ts +211 -0
  110. package/src/review-api/blocks/call_stack_diff.ts +75 -0
  111. package/src/review-api/blocks/callout.ts +24 -0
  112. package/src/review-api/blocks/code.ts +9 -0
  113. package/src/review-api/blocks/code_peek.ts +13 -0
  114. package/src/review-api/blocks/database_lens.ts +150 -0
  115. package/src/review-api/blocks/definition.ts +40 -0
  116. package/src/review-api/blocks/divider.ts +6 -0
  117. package/src/review-api/blocks/flow_diagram.ts +111 -0
  118. package/src/review-api/blocks/image.ts +10 -0
  119. package/src/review-api/blocks/index.ts +90 -0
  120. package/src/review-api/blocks/markdown.ts +17 -0
  121. package/src/review-api/blocks/section.ts +24 -0
  122. package/src/review-api/blocks/sequence.ts +56 -0
  123. package/src/review-api/blocks/software_map.ts +9 -0
  124. package/src/review-api/blocks/trace_quote.ts +10 -0
  125. package/src/review-api/blocks/tutorial.ts +39 -0
  126. package/src/review-api/checkout-fs.ts +14 -0
  127. package/src/review-api/client.ts +1 -0
  128. package/src/review-api/comparison-coverage.ts +304 -0
  129. package/src/review-api/component-reference.ts +30 -0
  130. package/src/review-api/diff-lenses.ts +175 -0
  131. package/src/review-api/document-headings.ts +51 -0
  132. package/src/review-api/document-text.ts +200 -0
  133. package/src/review-api/document.ts +906 -0
  134. package/src/review-api/file-lenses.ts +100 -0
  135. package/src/review-api/http.ts +1877 -0
  136. package/src/review-api/image-decode.ts +29 -0
  137. package/src/review-api/input-error.ts +9 -0
  138. package/src/review-api/instructions.ts +89 -0
  139. package/src/review-api/lens-alignment.ts +52 -0
  140. package/src/review-api/local-data.ts +1837 -0
  141. package/src/review-api/map-input.ts +154 -0
  142. package/src/review-api/mcp-client-agent.ts +32 -0
  143. package/src/review-api/mcp.ts +240 -0
  144. package/src/review-api/origin.ts +43 -0
  145. package/src/review-api/profile.ts +146 -0
  146. package/src/review-api/public-tools.ts +102 -0
  147. package/src/review-api/pull-request.ts +389 -0
  148. package/src/review-api/read-schemas.ts +85 -0
  149. package/src/review-api/recovery.ts +2 -0
  150. package/src/review-api/request-origin.ts +33 -0
  151. package/src/review-api/review-progress.ts +382 -0
  152. package/src/review-api/status-tool.ts +10 -0
  153. package/src/review-api/store-schema.ts +22 -0
  154. package/src/review-api/store.ts +1647 -0
  155. package/src/review-api/tool-failure.ts +64 -0
  156. package/src/review-api/trace-schema.ts +13 -0
  157. package/src/review-api/traces.ts +119 -0
  158. package/src/review-api/unsupported-files.integration.ts +91 -0
  159. package/src/review-api/workspaces.ts +692 -0
  160. package/src/review-api/worktree-source.ts +170 -0
  161. package/src/review-api/worktree-structural.integration.ts +320 -0
  162. package/src/review-app-launcher.ts +432 -0
  163. package/src/review-app-picker.ts +168 -0
  164. package/src/review-app.ts +134 -0
  165. package/src/review-bundled-tools.ts +249 -0
  166. package/src/review-checkout-paths.ts +37 -0
  167. package/src/review-diff-files.ts +89 -0
  168. package/src/review-head-checkout.ts +300 -0
  169. package/src/review-home-paths.ts +73 -0
  170. package/src/review-info.ts +59 -0
  171. package/src/review-instances.ts +113 -0
  172. package/src/review-logger.ts +183 -0
  173. package/src/review-preferences.ts +88 -0
  174. package/src/review-prepare.ts +291 -0
  175. package/src/review-stack.ts +112 -0
  176. package/src/review-telemetry.ts +1066 -0
  177. package/src/runtime.ts +74 -0
  178. package/src/server/account-alias.ts +30 -0
  179. package/src/server/bounded-stream.ts +34 -0
  180. package/src/server/bug-report.ts +301 -0
  181. package/src/server/client-error-budget.ts +45 -0
  182. package/src/server/crash-report.ts +273 -0
  183. package/src/server/desktop-host-shutdown.ts +64 -0
  184. package/src/server/desktop-host.ts +190 -0
  185. package/src/server/desktop-server.ts +703 -0
  186. package/src/server/diffr-config.ts +509 -0
  187. package/src/server/diffr-languages.ts +97 -0
  188. package/src/server/global-verb-relay.ts +190 -0
  189. package/src/server/headless-host.ts +149 -0
  190. package/src/server/hono-http.ts +162 -0
  191. package/src/server/http-json.ts +24 -0
  192. package/src/server/json-review-reporting.ts +174 -0
  193. package/src/server/process-error-telemetry.ts +174 -0
  194. package/src/server/review-api-parsers.ts +90 -0
  195. package/src/server/review-lifecycle-telemetry.ts +73 -0
  196. package/src/server/review-open-watchdog.ts +46 -0
  197. package/src/server/review-server-core.ts +290 -0
  198. package/src/server/structural-comparisons.ts +153 -0
  199. package/src/server/structural-diff.ts +186 -0
  200. package/src/server/tutorial-service.ts +241 -0
  201. package/src/server/ui-telemetry.ts +198 -0
  202. package/src/server-discovery.ts +95 -0
  203. package/src/session-markers.ts +132 -0
  204. package/src/sharing/auth.ts +41 -0
  205. package/src/sharing/cli.ts +83 -0
  206. package/src/sharing/client.ts +334 -0
  207. package/src/sharing/export.ts +195 -0
  208. package/src/sharing/host.ts +316 -0
  209. package/src/sharing/import.ts +787 -0
  210. package/src/sharing/index.ts +16 -0
  211. package/src/sharing/repository.ts +145 -0
  212. package/src/sharing/routes.ts +34 -0
  213. package/src/slug.ts +23 -0
  214. package/src/software-map-diff-counts.ts +517 -0
  215. package/src/software-map-model.ts +1147 -0
  216. package/src/software-map-topology-diff.ts +260 -0
  217. package/src/source.ts +92 -0
  218. package/src/startup-trace.ts +232 -0
  219. package/src/stored-document-migration.ts +151 -0
  220. package/src/telemetry-clean-text.ts +257 -0
  221. package/src/telemetry-config.ts +298 -0
  222. package/src/telemetry-debug-sink.ts +38 -0
  223. package/src/telemetry.ts +13 -0
  224. package/src/trace-cli.ts +156 -0
  225. package/src/trace-storage-cli.ts +509 -0
  226. package/src/tutorial-conversation.ts +18 -0
  227. package/src/ui-telemetry-events.ts +765 -0
  228. package/src/unified-diff.ts +71 -0
  229. package/src/viewed-coverage.ts +259 -0
  230. package/src/windows-cli.ts +125 -0
  231. package/tutorial/document.json +275 -0
  232. package/tutorial/runtime-manifest.json +12 -0
  233. package/tutorial/sample-service/package.json +9 -0
  234. package/tutorial/sample-service/src/api/checkout-api.ts +17 -0
  235. package/tutorial/sample-service/src/app.ts +26 -0
  236. package/tutorial/sample-service/src/database/schema.ts +10 -0
  237. package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +15 -0
  238. package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +24 -0
  239. package/tutorial/sample-service/src/inventory/inventory-service.ts +11 -0
  240. package/tutorial/sample-service/src/orders/order-service.ts +34 -0
  241. package/tutorial/sample-service/src/orders/order.ts +21 -0
  242. package/tutorial/sample-service/src/orders/orders-repository.ts +24 -0
  243. package/tutorial/sample-service/src/payments/payment-gateway.ts +12 -0
  244. package/tutorial/sample-service/src/shipping/shipping-gateway.ts +15 -0
  245. package/tutorial/sample-service/tsconfig.json +12 -0
  246. package/tutorial/software-map.json +139 -0
  247. package/tutorial/trace.json +20 -0
@@ -0,0 +1,90 @@
1
+ import { z } from "zod";
2
+
3
+ import { call_stack_diff } from "./call_stack_diff.js";
4
+ import { type CalloutBlock, callout, calloutSchema } from "./callout.js";
5
+ import { code } from "./code.js";
6
+ import { code_peek } from "./code_peek.js";
7
+ import { database_lens } from "./database_lens.js";
8
+ import type { BlockDefinition } from "./definition.js";
9
+ import { divider } from "./divider.js";
10
+ import { flow_diagram } from "./flow_diagram.js";
11
+ import { image } from "./image.js";
12
+ import { markdown } from "./markdown.js";
13
+ import { type SectionBlock, section, sectionSchema } from "./section.js";
14
+ import { sequence } from "./sequence.js";
15
+ import { software_map } from "./software_map.js";
16
+ import { trace_quote } from "./trace_quote.js";
17
+ import { type TutorialBlock, tutorial, tutorialSchema } from "./tutorial.js";
18
+
19
+ /** Leaf kinds share one discriminated union so unknown types read as they always have. */
20
+ export const leafSchema = z.discriminatedUnion("type", [
21
+ markdown.schema,
22
+ code.schema,
23
+ divider.schema,
24
+ code_peek.schema,
25
+ sequence.schema,
26
+ call_stack_diff.schema,
27
+ database_lens.schema,
28
+ image.schema,
29
+ trace_quote.schema,
30
+ software_map.schema,
31
+ flow_diagram.schema,
32
+ ]);
33
+
34
+ export type LeafBlock = z.infer<typeof leafSchema>;
35
+
36
+ export type Block = LeafBlock | SectionBlock | CalloutBlock | TutorialBlock;
37
+
38
+ export type BlockType = Block["type"];
39
+
40
+ export type Definitions = {
41
+ [K in BlockType]: BlockDefinition<Extract<Block, { type: K }>>;
42
+ };
43
+
44
+ /** Every block kind, keyed by type. A kind without a definition is a compile error. */
45
+ export const blocks = {
46
+ markdown,
47
+ code,
48
+ divider,
49
+ code_peek,
50
+ sequence,
51
+ call_stack_diff,
52
+ database_lens,
53
+ image,
54
+ trace_quote,
55
+ software_map,
56
+ flow_diagram,
57
+ section,
58
+ callout,
59
+ tutorial,
60
+ } satisfies Definitions;
61
+
62
+ /** Discriminated by type, so a malformed block reports its own kind's
63
+ * issues rather than every kind's. */
64
+ export const blockSchema: z.ZodType<Block> = z.lazy(() =>
65
+ z.discriminatedUnion("type", blockKindSchemas()),
66
+ );
67
+
68
+ /** Each block kind's schema, for unions that add kinds of their own. Read
69
+ * lazily: the container modules import this one. */
70
+ export const blockKindSchemas = () =>
71
+ [leafSchema, sectionSchema, calloutSchema, tutorialSchema] as const;
72
+
73
+ function checkBlock<K extends BlockType>(
74
+ type: K,
75
+ block: Extract<Block, { type: K }>,
76
+ ): void {
77
+ const definition: Definitions[K] = blocks[type];
78
+ definition.check?.(block);
79
+ }
80
+
81
+ /** Relationships not expressible in a field schema. Sources/resources are checked by host providers. */
82
+ export function checkReferences(document: Block[]): void {
83
+ const visit = (block: Block) => {
84
+ checkBlock(block.type, block);
85
+
86
+ if ("children" in block) block.children.forEach(visit);
87
+ };
88
+
89
+ document.forEach(visit);
90
+ }
@@ -0,0 +1,17 @@
1
+ import { sourcePinsSchema } from "@review/source.js";
2
+
3
+ import { defineBlock, text } from "./definition.js";
4
+
5
+ export const markdown = {
6
+ type: "markdown",
7
+ schema: defineBlock("markdown", {
8
+ markdown: text.describe(
9
+ "Safe Markdown. Repository file links must use [label](review-source:head/path#L10-L24) or review-source:base/path#L10-L24, with a repository-relative path and verified line numbers. Relative paths, absolute filesystem paths, and file/editor URLs are rejected. External links use https://, http://, or mailto:; document anchors use #heading.",
10
+ ),
11
+ pins: sourcePinsSchema
12
+ .optional()
13
+ .describe(
14
+ "Repository and commits this block's review-source links resolve against, instead of the document's pins.",
15
+ ),
16
+ }),
17
+ } as const;
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+
3
+ import { type BlockDefinition, defineBlock, label } from "./definition.js";
4
+ import { type Block, blockSchema } from "./index.js";
5
+
6
+ export interface SectionBlock {
7
+ id?: string;
8
+ type: "section";
9
+ title: string;
10
+ defaultCollapsed?: boolean;
11
+ children: Block[];
12
+ }
13
+
14
+ // blockSchema is read inside z.lazy, after every module in the cycle has evaluated.
15
+ export const sectionSchema = defineBlock("section", {
16
+ title: label,
17
+ defaultCollapsed: z.boolean().optional(),
18
+ children: z.array(z.lazy((): z.ZodType<Block> => blockSchema)),
19
+ });
20
+
21
+ export const section: BlockDefinition<SectionBlock> = {
22
+ type: "section",
23
+ schema: sectionSchema,
24
+ };
@@ -0,0 +1,56 @@
1
+ import { anchorSchema } from "@review/lens-selection.js";
2
+ import { z } from "zod";
3
+
4
+ import { codeFields } from "./code.js";
5
+ import {
6
+ type BlockDefinition,
7
+ defineBlock,
8
+ elementPins,
9
+ identity,
10
+ label,
11
+ requireKey,
12
+ text,
13
+ } from "./definition.js";
14
+
15
+ export const stepSchema = z
16
+ .strictObject({
17
+ ...identity,
18
+ type: z.literal("step").default("step"),
19
+ from: label,
20
+ to: label,
21
+ label,
22
+ style: z.enum(["call", "return", "async"]).default("call"),
23
+ source: anchorSchema.optional(),
24
+ pins: elementPins,
25
+ explanation: label.optional(),
26
+ code: z.strictObject(codeFields).optional(),
27
+ })
28
+ .refine(
29
+ (s) =>
30
+ [s.source, s.explanation, s.code].filter((v) => v !== undefined)
31
+ .length === 1,
32
+ "A step needs exactly one of source, explanation, or code.",
33
+ );
34
+
35
+ export type Step = z.infer<typeof stepSchema>;
36
+
37
+ /** Kept for callers that parsed sequences directly. */
38
+ export const sequenceSchema = defineBlock("sequence", {
39
+ title: label,
40
+ actors: z.record(text, label),
41
+ steps: z.array(stepSchema),
42
+ pins: elementPins,
43
+ });
44
+
45
+ export type SequenceBlock = z.infer<typeof sequenceSchema>;
46
+
47
+ export const sequence = {
48
+ type: "sequence",
49
+ schema: sequenceSchema,
50
+ check(block: SequenceBlock) {
51
+ for (const step of block.steps) {
52
+ requireKey(block.actors, step.from);
53
+ requireKey(block.actors, step.to);
54
+ }
55
+ },
56
+ } satisfies BlockDefinition<SequenceBlock>;
@@ -0,0 +1,9 @@
1
+ import { defineBlock, label } from "./definition.js";
2
+
3
+ export const software_map = {
4
+ type: "software_map",
5
+ schema: defineBlock("software_map", {
6
+ mapVersionId: label,
7
+ focusElementId: label.optional(),
8
+ }),
9
+ } as const;
@@ -0,0 +1,10 @@
1
+ import { defineBlock, label } from "./definition.js";
2
+
3
+ export const trace_quote = {
4
+ type: "trace_quote",
5
+ schema: defineBlock("trace_quote", {
6
+ traceId: label,
7
+ eventId: label,
8
+ text: label,
9
+ }),
10
+ } as const;
@@ -0,0 +1,39 @@
1
+ import {
2
+ type TutorialAuthoringConversation,
3
+ tutorialAuthoringConversationSchema,
4
+ } from "@review/tutorial-conversation.js";
5
+ import { z } from "zod";
6
+
7
+ import { type BlockDefinition, defineBlock } from "./definition.js";
8
+ import { type Block, blockSchema } from "./index.js";
9
+
10
+ type TutorialContent =
11
+ | { kind: "keymap" }
12
+ | { kind: "conversation"; conversation: TutorialAuthoringConversation }
13
+ | { kind: "view"; view: "review" | "commits" | "diff" | "map"; label: string }
14
+ | { kind: "feature"; feature: "softwareMap"; children: Block[] };
15
+
16
+ export type TutorialBlock = { id?: string; type: "tutorial" } & TutorialContent;
17
+
18
+ export const tutorialSchema = z.discriminatedUnion("kind", [
19
+ defineBlock("tutorial", { kind: z.literal("keymap") }),
20
+ defineBlock("tutorial", {
21
+ kind: z.literal("conversation"),
22
+ conversation: tutorialAuthoringConversationSchema,
23
+ }),
24
+ defineBlock("tutorial", {
25
+ kind: z.literal("view"),
26
+ view: z.enum(["review", "commits", "diff", "map"]),
27
+ label: z.string(),
28
+ }),
29
+ defineBlock("tutorial", {
30
+ kind: z.literal("feature"),
31
+ feature: z.literal("softwareMap"),
32
+ children: z.array(z.lazy((): z.ZodType<Block> => blockSchema)),
33
+ }),
34
+ ]);
35
+
36
+ export const tutorial: BlockDefinition<TutorialBlock> = {
37
+ type: "tutorial",
38
+ schema: tutorialSchema,
39
+ };
@@ -0,0 +1,14 @@
1
+ import * as fs from "node:fs/promises";
2
+ import { createRequire } from "node:module";
3
+
4
+ /**
5
+ * File system calls for paths inside a user's checkout. Electron's `fs`
6
+ * treats every `*.asar` path as an archive, so a checkout that merely
7
+ * contains one fails with "Invalid package" or shows it as a directory.
8
+ * Electron's unpatched `original-fs` reads it as the plain file it is.
9
+ */
10
+ export const checkoutFs: typeof fs = process.versions.electron
11
+ ? // SAFETY: Electron's original-fs is Node's fs module without the asar patch.
12
+ (createRequire(import.meta.url)("original-fs") as typeof import("node:fs"))
13
+ .promises
14
+ : fs;
@@ -0,0 +1 @@
1
+ export { ReviewApiClient, ReviewApiError } from "@dev.fast/review-protocol";
@@ -0,0 +1,304 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import {
4
+ type StructuralDiff,
5
+ type StructuralRegion,
6
+ type StructuralSource,
7
+ type StructuralVisibility,
8
+ structuralRows,
9
+ } from "@dev.fast/review-protocol";
10
+ import type { AlignmentRow } from "@review/lens-selection.js";
11
+ import type { FileLineRange } from "@review/source.js";
12
+ import { parseUnifiedPatch } from "@review/unified-diff.js";
13
+ import {
14
+ type Coverage,
15
+ type CoverageFile,
16
+ type LineInterval,
17
+ emptyCoverage,
18
+ intersectIntervals,
19
+ subtractIntervals,
20
+ unionIntervals,
21
+ } from "@review/viewed-coverage.js";
22
+
23
+ import type { Pins } from "./document.js";
24
+ import { textualRows } from "./lens-alignment.js";
25
+ import type { LocalReviewData } from "./local-data.js";
26
+
27
+ export type CoverageMode = "structural" | "textual";
28
+
29
+ const hash = (parts: (string | null)[]) =>
30
+ createHash("sha256").update(JSON.stringify(parts)).digest("hex");
31
+
32
+ export interface ComparisonCoverage {
33
+ files: CoverageFile[];
34
+ fileSources: Map<string, FileLineRange[]>;
35
+ alignments: Map<string, readonly AlignmentRow[]>;
36
+ }
37
+
38
+ /** Immutable comparison facts shared by catalog totals and review progress. */
39
+ export async function comparisonCoverage(
40
+ data: LocalReviewData,
41
+ reviewId: string,
42
+ pins: Pins,
43
+ mode: CoverageMode,
44
+ signal: AbortSignal,
45
+ publish?: (coverage: ComparisonCoverage) => void,
46
+ ) {
47
+ const fileSources = new Map<string, FileLineRange[]>();
48
+ const alignments = new Map<string, readonly AlignmentRow[]>();
49
+
50
+ const files: CoverageFile[] = [];
51
+
52
+ if (mode === "textual") {
53
+ for (const file of await data.changes(pins)) {
54
+ signal.throwIfAborted();
55
+ const patch = await data.changes(pins, file.path);
56
+ const changed = emptyCoverage();
57
+
58
+ for (const hunk of parseUnifiedPatch(file.path, patch))
59
+ for (const line of hunk.lines) {
60
+ if (line.kind === "add")
61
+ changed.head.push([line.newLine! - 1, line.newLine!]);
62
+
63
+ if (line.kind === "remove")
64
+ changed.base.push([line.oldLine! - 1, line.oldLine!]);
65
+ }
66
+
67
+ changed.base = unionIntervals(changed.base);
68
+ changed.head = unionIntervals(changed.head);
69
+
70
+ const [base, head] = await Promise.all([
71
+ file.status === "added"
72
+ ? null
73
+ : data
74
+ .file(pins, "base", file.previousPath ?? file.path)
75
+ .then((value) => value.text)
76
+ .catch(() => null),
77
+ file.status === "deleted"
78
+ ? null
79
+ : data
80
+ .file(pins, "head", file.path)
81
+ .then((value) => value.text)
82
+ .catch(() => null),
83
+ ]);
84
+
85
+ alignments.set(
86
+ file.path,
87
+ textualRows(
88
+ file.path,
89
+ patch,
90
+ base === null ? 0 : base.split("\n").length,
91
+ head === null ? 0 : head.split("\n").length,
92
+ ),
93
+ );
94
+
95
+ const readable =
96
+ (file.status === "added" || base !== null) &&
97
+ (file.status === "deleted" || head !== null);
98
+
99
+ const fingerprint = hash([
100
+ pins.repositoryId,
101
+ base,
102
+ head,
103
+ ...(readable ? [] : [pins.base, pins.head, patch]),
104
+ ]);
105
+
106
+ fileSources.set(file.path, [
107
+ ...(base !== null
108
+ ? [
109
+ {
110
+ side: "base" as const,
111
+ file: file.previousPath ?? file.path,
112
+ fromLine: 1,
113
+ toLine: base.split("\n").length,
114
+ },
115
+ ]
116
+ : []),
117
+ ...(head !== null
118
+ ? [
119
+ {
120
+ side: "head" as const,
121
+ file: file.path,
122
+ fromLine: 1,
123
+ toLine: head.split("\n").length,
124
+ },
125
+ ]
126
+ : []),
127
+ ]);
128
+ files.push({
129
+ path: file.path,
130
+ previousPath: file.previousPath,
131
+ fingerprint,
132
+ changed,
133
+ viewed: emptyCoverage(),
134
+ });
135
+ publish?.({
136
+ files: [...files],
137
+ fileSources: new Map(fileSources),
138
+ alignments: new Map(alignments),
139
+ });
140
+ }
141
+ } else {
142
+ const remaining = new Set<string>();
143
+
144
+ for await (const event of data.structuralChanges({
145
+ reviewId,
146
+ pins,
147
+ signal,
148
+ })) {
149
+ if (event.type === "start") {
150
+ for (const entry of event.files)
151
+ remaining.add((entry.file.rhs ?? entry.file.lhs)!.path);
152
+
153
+ if (!remaining.size) break;
154
+ continue;
155
+ }
156
+
157
+ if (event.type === "complete" && (event.failed || event.aborted))
158
+ throw new Error(
159
+ event.aborted?.message ?? "Structural coverage is incomplete.",
160
+ );
161
+
162
+ if (event.type !== "file") continue;
163
+
164
+ const path = (event.file.rhs ?? event.file.lhs)!.path;
165
+
166
+ if (!remaining.delete(path))
167
+ throw new Error(`Unexpected structural result: ${path}`);
168
+
169
+ if (event.error) {
170
+ if (event.error.code !== "unsupported_file_type")
171
+ throw new Error(`Cannot count ${path}: ${event.error.message}`);
172
+
173
+ if (!remaining.size) break;
174
+ continue;
175
+ }
176
+
177
+ const diff = event.diff;
178
+
179
+ const previousPath =
180
+ event.file.lhs?.path !== path ? event.file.lhs?.path : undefined;
181
+
182
+ const base = diff.type === "text" ? (diff.lhs?.text ?? null) : null;
183
+ const head = diff.type === "text" ? (diff.rhs?.text ?? null) : null;
184
+
185
+ const fingerprint = hash([
186
+ pins.repositoryId,
187
+ base,
188
+ head,
189
+ ...(diff.type === "binary"
190
+ ? [event.file.lhs?.oid ?? null, event.file.rhs?.oid ?? null]
191
+ : []),
192
+ ]);
193
+
194
+ const sources: FileLineRange[] = [];
195
+
196
+ if (base !== null)
197
+ sources.push({
198
+ side: "base",
199
+ file: event.file.lhs!.path,
200
+ fromLine: 1,
201
+ toLine: base.split("\n").length,
202
+ });
203
+
204
+ if (head !== null)
205
+ sources.push({
206
+ side: "head",
207
+ file: path,
208
+ fromLine: 1,
209
+ toLine: head.split("\n").length,
210
+ });
211
+ fileSources.set(path, sources);
212
+
213
+ if (diff.type === "text") alignments.set(path, structuralRows(diff));
214
+ files.push({
215
+ path,
216
+ previousPath,
217
+ fingerprint,
218
+ changed:
219
+ diff.type === "text" ? diff.structural_changes : emptyCoverage(),
220
+ folded: foldedChanges(event.visibility, diff),
221
+ viewed: emptyCoverage(),
222
+ });
223
+ publish?.({
224
+ files: [...files],
225
+ fileSources: new Map(fileSources),
226
+ alignments: new Map(alignments),
227
+ });
228
+
229
+ if (!remaining.size) break;
230
+ }
231
+
232
+ if (remaining.size) throw new Error("Structural coverage is incomplete.");
233
+ }
234
+
235
+ return { files, fileSources, alignments };
236
+ }
237
+
238
+ /**
239
+ * The changed lines diffr folds by default. A file it hides (lockfiles,
240
+ * generated, vendored and test files, per its plugins) folds all of them.
241
+ * Otherwise they are the changed lines under regions that start collapsed
242
+ * and in no visible leaf: `structural_changes` less what diffr counts in
243
+ * `stats.visible`, recomputed the way diffr's `change_coverage` does so that
244
+ * the lines, not just the counts, are known. A paired leaf contributes its
245
+ * changed spans' lines; an unpaired leaf, all of its lines.
246
+ */
247
+ export function foldedChanges(
248
+ visibility: StructuralVisibility | undefined,
249
+ diff: StructuralDiff,
250
+ ): Coverage {
251
+ if (diff.type !== "text") return emptyCoverage();
252
+ const all = diff.structural_changes;
253
+
254
+ if (visibility?.collapsed)
255
+ return { base: unionIntervals(all.base), head: unionIntervals(all.head) };
256
+
257
+ const side = (
258
+ changed: readonly LineInterval[],
259
+ source: StructuralSource | undefined,
260
+ other: StructuralSource | undefined,
261
+ ): LineInterval[] => {
262
+ const paired = new Set<number>();
263
+
264
+ const pair = (region: StructuralRegion) => {
265
+ if (region.kind === "leaf") paired.add(region.alignment_id);
266
+ else region.children.forEach(pair);
267
+ };
268
+
269
+ other?.regions?.forEach(pair);
270
+
271
+ const hidden: LineInterval[] = [],
272
+ visible: LineInterval[] = [];
273
+
274
+ const collect = (region: StructuralRegion, folded: boolean) => {
275
+ folded ||= region.visibility?.collapsed === true;
276
+
277
+ if (region.kind === "fold") {
278
+ for (const child of region.children) collect(child, folded);
279
+
280
+ return;
281
+ }
282
+
283
+ const lines = folded ? hidden : visible;
284
+
285
+ if (paired.has(region.alignment_id))
286
+ for (const span of region.changed ?? [])
287
+ lines.push([span.line, span.line + 1]);
288
+ else
289
+ lines.push([
290
+ region.start.line,
291
+ region.end.column === 0 ? region.end.line : region.end.line + 1,
292
+ ]);
293
+ };
294
+
295
+ for (const region of source?.regions ?? []) collect(region, false);
296
+
297
+ return subtractIntervals(intersectIntervals(changed, hidden), visible);
298
+ };
299
+
300
+ return {
301
+ base: side(all.base, diff.lhs, diff.rhs),
302
+ head: side(all.head, diff.rhs, diff.lhs),
303
+ };
304
+ }
@@ -0,0 +1,30 @@
1
+ /** Every component's fields, for session_get_instructions: the edit tool's
2
+ * description must stay short enough that clients don't cut it off. */
3
+ export const COMPONENT_REFERENCE = [
4
+ "## Components",
5
+ "",
6
+ "session_edit's content is one component, named by its type. Fields, with ? for optional and anchor for a source anchor string:",
7
+ "",
8
+ "- markdown {markdown, pins?}",
9
+ "- code {text, language?, caption?}",
10
+ "- divider {}",
11
+ "- code_peek {source: anchor, caption?, pins?}",
12
+ "- section {title, children: component[], defaultCollapsed?}",
13
+ "- callout {children: component[], title?, tone?: info|warning|danger|success}",
14
+ "- image {assetId from session_upload, alt, caption?}",
15
+ "- trace_quote {traceId, eventId, text}",
16
+ "- software_map {mapVersionId, focusElementId?}",
17
+ "- sequence {title, actors: {key: label}, steps: step[], pins?}",
18
+ "- step {from, to (actor keys), label, style?: call|return|async, and exactly one of source: anchor, explanation, or code: {text, language?}; pins?}",
19
+ "- flow_diagram {title, nodes: flow_node[] (1 to 100), edges: flow_edge[], description?, direction?: right|down, pins?}",
20
+ "- flow_node {key, label, description?, kind?: process|decision|terminal, attachments?: {label, sources: anchor[], pins?}[], link?: {from or to, label?, style?}}",
21
+ "- flow_edge {from, to (node keys), label?, style?: solid|dashed}",
22
+ "- call_stack_diff {title, base: frame[], head: frame[], pins?}; either side may be empty",
23
+ "- frame {source: anchor, label?, key?, parentKey? (an earlier frame on that side), callSite?: anchor, contextSources?: anchor[], via?: {kind: call|queue|callback|rpc, reason}, pins?}",
24
+ "- database_lens {title, actors: {key: label}, stores: {key: store}, useCases: useCase[] (1 or more), pins?}",
25
+ "- store {label, storage: relational|document, collections: {key: {label, fields: {key: field}}}, dataStoreKind?: database|objectStore|bucket|artifactStore|fileStore}",
26
+ "- field {label, dataType, nullable?, primaryKey?, references?: {store, collection, field}, fields?: {key: field}}",
27
+ "- useCase {label, summary?, operations: operation[] (1 or more)}; operation {kind: read|write, actor, store, collection, label, source: anchor, field?, detail?, pins?}, naming declared keys",
28
+ "",
29
+ 'Example (replace its path and lines with verified ones): {"type":"sequence","title":"Request","actors":{"a":"Client","b":"Server"},"steps":[{"from":"a","to":"b","label":"Send","source":"head/src/app.ts#L10-L20"}]}',
30
+ ].join("\n");