@dev.fast/whiteboard 0.0.0-stage → 0.2.1-preview.20261005.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +146 -2
- package/THIRD_PARTY_NOTICES.md +93 -0
- package/dist/account-alias-DOHN1RSH.js +973 -0
- package/dist/agent-cli-ChOq0ZuO.js +117 -0
- package/dist/agent-cli-DzWfZr6G.js +2 -0
- package/dist/agent-client-CPQo7iTI.js +305 -0
- package/dist/authoring-tools-Cr6Kpjsx.js +234 -0
- package/dist/build-info.json +1 -0
- package/dist/cli-hVgHcdsv.js +47 -0
- package/dist/cli-runner-D8y2_luU.js +5095 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +128 -0
- package/dist/client-DFoh50W3.js +950 -0
- package/dist/desktop-discovery-DBO-PM2V.js +164 -0
- package/dist/error-message-OtiDonty.js +6 -0
- package/dist/fs-utils-BMPLt0cr.js +22 -0
- package/dist/fuzzy-match-BoAmcyak.js +30 -0
- package/dist/headless-host-BJxf4dUU.js +94 -0
- package/dist/input-error-OLgB_h31.js +11 -0
- package/dist/local-data-CxLk75rx.js +4491 -0
- package/dist/mcp-QeF8vkdf.js +140 -0
- package/dist/package-paths-B6-zxvIO.js +47 -0
- package/dist/process-error-telemetry-v9e5h6D7.js +3952 -0
- package/dist/profile-4ry2f3AM.js +76 -0
- package/dist/profile-Bh5FIrmf.js +2 -0
- package/dist/request-origin-D_4QIdoN.js +811 -0
- package/dist/review-agent-traces-DNJFYzc8.js +3866 -0
- package/dist/review-home-paths-6zZH-1c9.js +2560 -0
- package/dist/review-telemetry-DEFnQsQ2.js +1195 -0
- package/dist/runtime-CKiWhGrf.js +52 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.js +2 -0
- package/dist/s3-SzLpeCG6.js +345 -0
- package/dist/s3-config-BgxkSoOy.js +2 -0
- package/dist/s3-config-CAxhO9u_.js +676 -0
- package/dist/server/desktop-host.d.ts +4 -0
- package/dist/server/desktop-host.js +1702 -0
- package/dist/server-discovery-Dbzn3w6Z.js +66 -0
- package/dist/sharing/index.d.ts +1977 -0
- package/dist/sharing/index.js +2 -0
- package/dist/src-CGP5ytbV.js +283 -0
- package/dist/src-CmdiBL20.js +3676 -0
- package/dist/src-DnwdaQ2r.js +865 -0
- package/dist/src-X9phtB2j.js +68 -0
- package/dist/stored-document-migration-DHeMpLMb.js +2671 -0
- package/dist/tool-failure-C8zB73HV.js +98 -0
- package/dist/tutorial-trace-DrBvkaeL.js +43 -0
- package/instructions/authoring.md +36 -0
- package/instructions/file-lenses.md +13 -0
- package/instructions/scratchpad.md +23 -0
- package/instructions/trace-archaeology.md +113 -0
- package/onboarding.md +8 -0
- package/package.json +106 -3
- package/src/agent-selection.ts +100 -0
- package/src/agent-session-ref.ts +80 -0
- package/src/ask/agents.ts +351 -0
- package/src/ask/checkout-files.ts +47 -0
- package/src/ask/file-refs.ts +111 -0
- package/src/ask/pi-mcp.ts +154 -0
- package/src/ask/protocol.ts +244 -0
- package/src/ask/thread-state.ts +350 -0
- package/src/ask/thread.ts +1400 -0
- package/src/ask/threads.ts +219 -0
- package/src/ask/watch.ts +79 -0
- package/src/cli-install.ts +1041 -0
- package/src/cli-runner.ts +1461 -0
- package/src/cli-runtime-info.ts +35 -0
- package/src/cli.ts +222 -0
- package/src/connect-prompts.ts +248 -0
- package/src/cursor-deeplink.ts +11 -0
- package/src/desktop-discovery.ts +355 -0
- package/src/diff-selection-migration.ts +63 -0
- package/src/embedded-posthog-key.ts +6 -0
- package/src/error-telemetry.ts +247 -0
- package/src/evidence.ts +14 -0
- package/src/exception-telemetry.ts +126 -0
- package/src/fixtures/blocks/call_stack_diff.json +27 -0
- package/src/fixtures/blocks/callout.json +11 -0
- package/src/fixtures/blocks/code.json +9 -0
- package/src/fixtures/blocks/code_peek.json +8 -0
- package/src/fixtures/blocks/database_lens.json +51 -0
- package/src/fixtures/blocks/divider.json +1 -0
- package/src/fixtures/blocks/fixtures.ts +33 -0
- package/src/fixtures/blocks/flow_diagram.json +36 -0
- package/src/fixtures/blocks/ids.ts +8 -0
- package/src/fixtures/blocks/image.json +9 -0
- package/src/fixtures/blocks/markdown.json +7 -0
- package/src/fixtures/blocks/section.json +11 -0
- package/src/fixtures/blocks/sequence.json +31 -0
- package/src/fixtures/blocks/software_map.json +7 -0
- package/src/fixtures/blocks/trace_quote.json +9 -0
- package/src/fixtures/blocks/tutorial.json +51 -0
- package/src/fs-utils.ts +32 -0
- package/src/fuzzy-match.ts +94 -0
- package/src/install.ts +29 -0
- package/src/legacy-skills.ts +133 -0
- package/src/lens-selection.ts +230 -0
- package/src/markdown-latex-math.ts +230 -0
- package/src/markdown.ts +81 -0
- package/src/package-paths.ts +53 -0
- package/src/posthog-capture-client.ts +610 -0
- package/src/review-api/README.md +295 -0
- package/src/review-api/activity.ts +340 -0
- package/src/review-api/agent-cli.ts +219 -0
- package/src/review-api/agent-client.ts +188 -0
- package/src/review-api/anchor-quotes.ts +88 -0
- package/src/review-api/ask-history.ts +242 -0
- package/src/review-api/authoring-tools.ts +211 -0
- package/src/review-api/blocks/call_stack_diff.ts +75 -0
- package/src/review-api/blocks/callout.ts +24 -0
- package/src/review-api/blocks/code.ts +9 -0
- package/src/review-api/blocks/code_peek.ts +13 -0
- package/src/review-api/blocks/database_lens.ts +150 -0
- package/src/review-api/blocks/definition.ts +40 -0
- package/src/review-api/blocks/divider.ts +6 -0
- package/src/review-api/blocks/flow_diagram.ts +111 -0
- package/src/review-api/blocks/image.ts +10 -0
- package/src/review-api/blocks/index.ts +90 -0
- package/src/review-api/blocks/markdown.ts +17 -0
- package/src/review-api/blocks/section.ts +24 -0
- package/src/review-api/blocks/sequence.ts +56 -0
- package/src/review-api/blocks/software_map.ts +9 -0
- package/src/review-api/blocks/trace_quote.ts +10 -0
- package/src/review-api/blocks/tutorial.ts +39 -0
- package/src/review-api/checkout-fs.ts +14 -0
- package/src/review-api/client.ts +1 -0
- package/src/review-api/comparison-coverage.ts +304 -0
- package/src/review-api/component-reference.ts +30 -0
- package/src/review-api/diff-lenses.ts +175 -0
- package/src/review-api/document-headings.ts +51 -0
- package/src/review-api/document-text.ts +200 -0
- package/src/review-api/document.ts +906 -0
- package/src/review-api/file-lenses.ts +100 -0
- package/src/review-api/http.ts +1877 -0
- package/src/review-api/image-decode.ts +29 -0
- package/src/review-api/input-error.ts +9 -0
- package/src/review-api/instructions.ts +89 -0
- package/src/review-api/lens-alignment.ts +52 -0
- package/src/review-api/local-data.ts +1837 -0
- package/src/review-api/map-input.ts +154 -0
- package/src/review-api/mcp-client-agent.ts +32 -0
- package/src/review-api/mcp.ts +240 -0
- package/src/review-api/origin.ts +43 -0
- package/src/review-api/profile.ts +146 -0
- package/src/review-api/public-tools.ts +102 -0
- package/src/review-api/pull-request.ts +389 -0
- package/src/review-api/read-schemas.ts +85 -0
- package/src/review-api/recovery.ts +2 -0
- package/src/review-api/request-origin.ts +33 -0
- package/src/review-api/review-progress.ts +382 -0
- package/src/review-api/status-tool.ts +10 -0
- package/src/review-api/store-schema.ts +22 -0
- package/src/review-api/store.ts +1647 -0
- package/src/review-api/tool-failure.ts +64 -0
- package/src/review-api/trace-schema.ts +13 -0
- package/src/review-api/traces.ts +119 -0
- package/src/review-api/unsupported-files.integration.ts +91 -0
- package/src/review-api/workspaces.ts +692 -0
- package/src/review-api/worktree-source.ts +170 -0
- package/src/review-api/worktree-structural.integration.ts +320 -0
- package/src/review-app-launcher.ts +432 -0
- package/src/review-app-picker.ts +168 -0
- package/src/review-app.ts +134 -0
- package/src/review-bundled-tools.ts +249 -0
- package/src/review-checkout-paths.ts +37 -0
- package/src/review-diff-files.ts +89 -0
- package/src/review-head-checkout.ts +300 -0
- package/src/review-home-paths.ts +73 -0
- package/src/review-info.ts +59 -0
- package/src/review-instances.ts +113 -0
- package/src/review-logger.ts +183 -0
- package/src/review-preferences.ts +88 -0
- package/src/review-prepare.ts +291 -0
- package/src/review-stack.ts +112 -0
- package/src/review-telemetry.ts +1066 -0
- package/src/runtime.ts +74 -0
- package/src/server/account-alias.ts +30 -0
- package/src/server/bounded-stream.ts +34 -0
- package/src/server/bug-report.ts +301 -0
- package/src/server/client-error-budget.ts +45 -0
- package/src/server/crash-report.ts +273 -0
- package/src/server/desktop-host-shutdown.ts +64 -0
- package/src/server/desktop-host.ts +190 -0
- package/src/server/desktop-server.ts +703 -0
- package/src/server/diffr-config.ts +509 -0
- package/src/server/diffr-languages.ts +97 -0
- package/src/server/global-verb-relay.ts +190 -0
- package/src/server/headless-host.ts +149 -0
- package/src/server/hono-http.ts +162 -0
- package/src/server/http-json.ts +24 -0
- package/src/server/json-review-reporting.ts +174 -0
- package/src/server/process-error-telemetry.ts +174 -0
- package/src/server/review-api-parsers.ts +90 -0
- package/src/server/review-lifecycle-telemetry.ts +73 -0
- package/src/server/review-open-watchdog.ts +46 -0
- package/src/server/review-server-core.ts +290 -0
- package/src/server/structural-comparisons.ts +153 -0
- package/src/server/structural-diff.ts +186 -0
- package/src/server/tutorial-service.ts +241 -0
- package/src/server/ui-telemetry.ts +198 -0
- package/src/server-discovery.ts +95 -0
- package/src/session-markers.ts +132 -0
- package/src/sharing/auth.ts +41 -0
- package/src/sharing/cli.ts +83 -0
- package/src/sharing/client.ts +334 -0
- package/src/sharing/export.ts +195 -0
- package/src/sharing/host.ts +316 -0
- package/src/sharing/import.ts +787 -0
- package/src/sharing/index.ts +16 -0
- package/src/sharing/repository.ts +145 -0
- package/src/sharing/routes.ts +34 -0
- package/src/slug.ts +23 -0
- package/src/software-map-diff-counts.ts +517 -0
- package/src/software-map-model.ts +1147 -0
- package/src/software-map-topology-diff.ts +260 -0
- package/src/source.ts +92 -0
- package/src/startup-trace.ts +232 -0
- package/src/stored-document-migration.ts +151 -0
- package/src/telemetry-clean-text.ts +257 -0
- package/src/telemetry-config.ts +298 -0
- package/src/telemetry-debug-sink.ts +38 -0
- package/src/telemetry.ts +13 -0
- package/src/trace-cli.ts +156 -0
- package/src/trace-storage-cli.ts +509 -0
- package/src/tutorial-conversation.ts +18 -0
- package/src/ui-telemetry-events.ts +765 -0
- package/src/unified-diff.ts +71 -0
- package/src/viewed-coverage.ts +259 -0
- package/src/windows-cli.ts +125 -0
- package/tutorial/document.json +275 -0
- package/tutorial/runtime-manifest.json +12 -0
- package/tutorial/sample-service/package.json +9 -0
- package/tutorial/sample-service/src/api/checkout-api.ts +17 -0
- package/tutorial/sample-service/src/app.ts +26 -0
- package/tutorial/sample-service/src/database/schema.ts +10 -0
- package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +15 -0
- package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +24 -0
- package/tutorial/sample-service/src/inventory/inventory-service.ts +11 -0
- package/tutorial/sample-service/src/orders/order-service.ts +34 -0
- package/tutorial/sample-service/src/orders/order.ts +21 -0
- package/tutorial/sample-service/src/orders/orders-repository.ts +24 -0
- package/tutorial/sample-service/src/payments/payment-gateway.ts +12 -0
- package/tutorial/sample-service/src/shipping/shipping-gateway.ts +15 -0
- package/tutorial/sample-service/tsconfig.json +12 -0
- package/tutorial/software-map.json +139 -0
- package/tutorial/trace.json +20 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { m as isStringValue, n as isJsonObject } from "./src-X9phtB2j.js";
|
|
2
|
+
import { n as callAuthoringTool, s as ReviewApiError, t as ToolText } from "./agent-client-CPQo7iTI.js";
|
|
3
|
+
import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
|
|
4
|
+
//#region src/review-api/public-tools.ts
|
|
5
|
+
function publicDescription(text) {
|
|
6
|
+
return text.replace(/\breview_(\w+)/g, "session_$1").replace(/\breviewId\b/g, "sessionId").replace(/\bReview\b/g, "Whiteboard").replace("The result carries review,", "The result carries session,");
|
|
7
|
+
}
|
|
8
|
+
/** Rewrite schema prose without changing property names, enum values or examples. */
|
|
9
|
+
function translateSchemaDescriptions(value) {
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
value.forEach(translateSchemaDescriptions);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (!isJsonObject(value)) return;
|
|
15
|
+
for (const [key, item] of Object.entries(value)) if (key === "description" && isStringValue(item)) value[key] = publicDescription(item);
|
|
16
|
+
else if (![
|
|
17
|
+
"examples",
|
|
18
|
+
"default",
|
|
19
|
+
"const",
|
|
20
|
+
"enum"
|
|
21
|
+
].includes(key)) translateSchemaDescriptions(item);
|
|
22
|
+
}
|
|
23
|
+
/** Public vocabulary belongs to the agent boundary, not stored documents. */
|
|
24
|
+
function publicTool(tool) {
|
|
25
|
+
const inputSchema = structuredClone(tool.inputSchema);
|
|
26
|
+
if (isJsonObject(inputSchema)) translateSchemaDescriptions(inputSchema);
|
|
27
|
+
inputSchema.properties = Object.fromEntries(Object.entries(inputSchema.properties ?? {}).map(([key, value]) => [key === "reviewId" ? "sessionId" : key, value]));
|
|
28
|
+
if (inputSchema.required) inputSchema.required = inputSchema.required.map((key) => key === "reviewId" ? "sessionId" : key);
|
|
29
|
+
return {
|
|
30
|
+
...tool,
|
|
31
|
+
name: tool.name.replace(/^review_/, "session_"),
|
|
32
|
+
description: publicDescription(tool.description),
|
|
33
|
+
inputSchema
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Translate known response envelopes only; authored content is opaque. */
|
|
37
|
+
function publicResult(value) {
|
|
38
|
+
if (Array.isArray(value)) return value.map(publicResult);
|
|
39
|
+
if (!isJsonObject(value)) return value;
|
|
40
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => {
|
|
41
|
+
return [key === "reviewId" ? "sessionId" : key === "review" ? "session" : key === "reviews" ? "sessions" : key, key === "review" || key === "reviews" ? publicResult(item) : item];
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
async function callPublicTool(client, tool, input, signal) {
|
|
45
|
+
if ("reviewId" in input) throw new Error("Use sessionId with session tools.");
|
|
46
|
+
const { sessionId, ...rest } = input;
|
|
47
|
+
const fields = { ...rest };
|
|
48
|
+
if (sessionId !== void 0) fields.reviewId = sessionId;
|
|
49
|
+
const result = await callAuthoringTool(client, tool, fields, signal);
|
|
50
|
+
return result instanceof ToolText ? result : publicResult(result);
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/review-api/recovery.ts
|
|
54
|
+
const RECOVERY = "Whiteboard is not running, so its tools and guidance are unavailable. Start Whiteboard Desktop (or `whiteboard server start` for headless use). If Whiteboard's tools still do not appear, reconnect the Whiteboard MCP server or start a new agent session.";
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/review-api/tool-failure.ts
|
|
57
|
+
/**
|
|
58
|
+
* Sort a failed agent tool call into the CLI's closed error vocabulary, so
|
|
59
|
+
* telemetry can tell an agent's bad input from a broken host. `connect`
|
|
60
|
+
* covers reaching the host and reading its catalog; `call` is the tool itself.
|
|
61
|
+
*/
|
|
62
|
+
function toolFailure(error, phase) {
|
|
63
|
+
const status = error instanceof ReviewApiError || error instanceof ReviewInputError ? error.status : void 0;
|
|
64
|
+
if (status === 400) return {
|
|
65
|
+
errorName: "usage_error",
|
|
66
|
+
errorCategory: "user_input"
|
|
67
|
+
};
|
|
68
|
+
if (status === 401) return {
|
|
69
|
+
errorName: "desktop_connection_error",
|
|
70
|
+
errorCategory: "dependency"
|
|
71
|
+
};
|
|
72
|
+
if (status === 404) return {
|
|
73
|
+
errorName: "review_not_found",
|
|
74
|
+
errorCategory: "local_state"
|
|
75
|
+
};
|
|
76
|
+
if (status === 409) return {
|
|
77
|
+
errorName: "review_state_error",
|
|
78
|
+
errorCategory: "local_state"
|
|
79
|
+
};
|
|
80
|
+
if (status !== void 0) return {
|
|
81
|
+
errorName: "unexpected_error",
|
|
82
|
+
errorCategory: "internal"
|
|
83
|
+
};
|
|
84
|
+
if (phase === "connect") return {
|
|
85
|
+
errorName: "desktop_connection_error",
|
|
86
|
+
errorCategory: "dependency"
|
|
87
|
+
};
|
|
88
|
+
if (error instanceof Error && (error instanceof TypeError || error.name === "AbortError" || error.cause !== void 0)) return {
|
|
89
|
+
errorName: "network_error",
|
|
90
|
+
errorCategory: "transport"
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
errorName: "unexpected_error",
|
|
94
|
+
errorCategory: "internal"
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { publicTool as i, RECOVERY as n, callPublicTool as r, toolFailure as t };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { r as TUTORIAL_TRACE_SESSION_ID } from "./review-agent-traces-DNJFYzc8.js";
|
|
2
|
+
//#region ../trace-core/src/tutorial-trace.ts
|
|
3
|
+
function loadTutorialTrace() {
|
|
4
|
+
return {
|
|
5
|
+
parserVersion: "1",
|
|
6
|
+
descriptor: {
|
|
7
|
+
sessionId: TUTORIAL_TRACE_SESSION_ID,
|
|
8
|
+
harness: "codex",
|
|
9
|
+
available: true,
|
|
10
|
+
source: null,
|
|
11
|
+
subagents: [],
|
|
12
|
+
commits: []
|
|
13
|
+
},
|
|
14
|
+
traceName: null,
|
|
15
|
+
cacheStatus: "current",
|
|
16
|
+
subagents: [],
|
|
17
|
+
trace: {
|
|
18
|
+
harness: "codex",
|
|
19
|
+
title: "Shared agent chat server — illustrative tutorial session",
|
|
20
|
+
startedAt: null,
|
|
21
|
+
endedAt: null,
|
|
22
|
+
activeMs: null,
|
|
23
|
+
userTurns: 1,
|
|
24
|
+
toolCalls: 0,
|
|
25
|
+
events: [
|
|
26
|
+
{
|
|
27
|
+
kind: "user",
|
|
28
|
+
text: "Right now, each Review window manages its own agent chats, so opening the same thread in another window can show stale messages or start a duplicate session. I think we want to have one shared server / source of truth for all agent chats. Can you move session ownership and message history into the server, and have each window subscribe to updates? Closing a window should not end the conversation. Keep the existing chat UI, and make sure reconnecting shows the messages that arrived while the window was closed."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
kind: "assistant",
|
|
32
|
+
markdown: "I'll make the server own each thread's agent session and message history. Windows will read that history and subscribe to new messages, so opening a thread in two windows attaches both to the same conversation."
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
kind: "assistant",
|
|
36
|
+
markdown: "Closing a window will detach its subscription without stopping the agent session. On reconnect, the window will catch up from the server's stored history before displaying live updates. I'll also check that simultaneous requests to open the same thread cannot create duplicate sessions."
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { loadTutorialTrace };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
you are writing an interactive rfc-style whiteboard, for consumption by a staff engineer.
|
|
2
|
+
|
|
3
|
+
**flow**
|
|
4
|
+
follow these first five steps in order, with no tool calls beyond what they need: loading the tools they use (`session_create`, `session_activity_begin`, `session_edit`, `session_get`, and later `session_activity_update` and `session_activity_end`) and, for a PR, reading its description (`gh pr view`).
|
|
5
|
+
- create the whiteboard, pinned to the commits/pr the user describes, naming the checkout by its `repositoryPath`. if they name none, use a `worktree` target with no `base`: it reviews the checkout, uncommitted work included, against the default branch. for uncommitted work only, pass `base: "HEAD"`. untracked files are left out: run `git add -N <file>` on new files you want reviewed
|
|
6
|
+
- trigger a subagent with this exact instruction: "Call `session_get_instructions({topic:\"file-lenses\"})` and follow it for whiteboard <sessionId>, whose change is `<base>..<head>` in `<repositoryPath>`."
|
|
7
|
+
- call `session_activity_begin` and pass its `activityId` on every `session_edit`
|
|
8
|
+
- read the diff with git (or jj) in `repositoryPath`, at the commits `session_create` returned: `git diff <base> <head>`, or for a worktree target `git diff <base>` plus `git status` for untracked files. if it shows no changes, the target is wrong: fix it with `session_set_target` before writing
|
|
9
|
+
- immediately after reading the diff (and the PR description) - put down a first pass at the what/why section.
|
|
10
|
+
|
|
11
|
+
- whiteboard structure - each of these should be written as a top-level `section`, in this order:
|
|
12
|
+
- what/why: succinct description of what the change is, + why the change was made (if this context is available to you.)
|
|
13
|
+
- requirements: as given by the user, in their own words, if this context is available to you. otherwise, omit. write these as short bullet points
|
|
14
|
+
- design: how the solution works at the level of components, data and control flow, not functions.
|
|
15
|
+
- pick one diagram that best shows the shape of the change:
|
|
16
|
+
- `sequence` if participants interact over time (who calls whom, async handoffs)
|
|
17
|
+
- `flow_diagram` if the interesting part is branches, retries or state transitions
|
|
18
|
+
- `database_lens` if the interesting part is a schema change to the data stores, and/or who reads/writes it
|
|
19
|
+
- if the change is mostly a new/changed contract, show the key types / interfaces as `code_peek`(s)
|
|
20
|
+
plus the main decisions and tradeoffs, and alternatives considered if you have evidence for them (trace, PR discussion). skip for small changes whose design is self-evident.
|
|
21
|
+
- implementation: how the code delivers the design, at the level of functions and files. walk the changed code in the order a reader should follow it, starting with the entry point.
|
|
22
|
+
- `call_stack_diff` for the old vs. new path through user flows. always root the flows in the user/agent entry point (eg a button click, CLI command, etc.), including unchanged nodes along the way.
|
|
23
|
+
- `code_peek` for the few spots that carry the mechanism or an invariant; link everything else inline
|
|
24
|
+
|
|
25
|
+
- before finishing, read the whole whiteboard back with `session_get({sessionId, full: true})` (without `full`, prose is cut short) and fix any contradictions/unverified claims.
|
|
26
|
+
|
|
27
|
+
**updating existing whiteboard**
|
|
28
|
+
- move the whiteboard to the new commits with `session_set_target`
|
|
29
|
+
- read the existing whiteboard (if you haven't already,) read the diff since last whiteboard, make any necessary updates to the whiteboard.
|
|
30
|
+
|
|
31
|
+
**guidelines**
|
|
32
|
+
- IMPORTANT: Write incrementally. The user sees you write on the canvas in real-time. Show them visual progress every few seconds.
|
|
33
|
+
- use `session_activity_update` to provide regular status updates on your area of focus.
|
|
34
|
+
- keep whiteboards short and sweet when possible (esp. for small changes.) feel free to omit sections.
|
|
35
|
+
- when something (a phrase in the prose, diagram node, etc.) describes actual code in the codebase, always default to attaching/hyperlink code.
|
|
36
|
+
- Link repository code as `[label](review-source:head/src/file.ts#L10-L24)`; use `base` for old code. Use repository-relative paths and verified line numbers.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
you are grouping a code whiteboard's changed files into file lenses for the Diff view.
|
|
2
|
+
|
|
3
|
+
**input:** a whiteboard id, and its change as `<base>..<head>` in a repository path.
|
|
4
|
+
|
|
5
|
+
**flow**
|
|
6
|
+
- call `session_activity_begin` before writing, pass its `activityId` on every `session_lens_edit`, and call `session_activity_end` when done.
|
|
7
|
+
- call `session_lens_get`: before any lens exists, its `uncategorized` lists every changed file
|
|
8
|
+
- categorize all the changes into buckets - leaving nothing in uncategorized changes by the end
|
|
9
|
+
- first, categorize away non-implementation code: tests, docs, generated files and lockfiles, config and build, fixtures and snapshots, pure renames and moves, formatting-only changes, imports — all might be reasonable.
|
|
10
|
+
- then, when left with only implementation code, split it by the part of the design each file serves (e.g. the data model, an API, a UI surface), in the order a reader should take them. keep each lens small enough to read in one sitting, and don't split a file across lenses unless it holds two unrelated changes.
|
|
11
|
+
|
|
12
|
+
**guidelines**
|
|
13
|
+
- to see a file's changes, run `git diff <base> <head> -- <path>` in the repository. otherwise, rely on the file listing.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Scratchpad
|
|
2
|
+
|
|
3
|
+
The scratchpad is one document in Whiteboard that is not a session for any change. It has no target, base, head or lifecycle. You draw on it the way you would sketch an explanation on a whiteboard: a short paragraph, a sequence or flow diagram, a call tree, a code peek, and the smallest view that makes the point. The user reads it in the app and copies blocks back to you.
|
|
4
|
+
|
|
5
|
+
Its id is fixed: `scratchpad`. While it is turned on in Whiteboard Settings it always exists; never create, rename, delete, dismiss or share it.
|
|
6
|
+
|
|
7
|
+
## When to draw here
|
|
8
|
+
|
|
9
|
+
- The user asks to be shown how something works, wants a picture of a flow, a call path, a data shape or a layout, or is thinking out loud with you before there is a concrete plan.
|
|
10
|
+
- The explanation spans more than one file or more than one repository, or would be an ASCII diagram in chat.
|
|
11
|
+
|
|
12
|
+
Answer in chat when one sentence or one code line does it. Do not use the scratchpad to review a change; author a session instead (`session_get_instructions({})`).
|
|
13
|
+
|
|
14
|
+
## How
|
|
15
|
+
|
|
16
|
+
1. `session_capabilities({})`. Draw only when `desktopAvailable` and `scratchpadEnabled` are true. Otherwise answer in chat; if the pad is off, say once that it can be turned on in Whiteboard Settings.
|
|
17
|
+
2. Every source reference needs pins. For each repository you will quote, `session_register_repository({path})` for its `repositoryId`, and `git rev-parse HEAD` (or the commit the user is looking at) for a commit id. Put `pins: {repositoryId, head}` on each block that quotes code (`code_peek`, `sequence`, `flow_diagram`, `call_stack_diff`, `database_lens` and `markdown` with `review-source:` links); a step, frame, attachment or operation that quotes other commits than its block takes its own `pins`. Add `base` only when an anchor reads the base side. A reference without pins is rejected, since the scratchpad has none to lend.
|
|
18
|
+
3. Read what you cite at those commits, for example `git show <head>:<path>` in the repository, so line numbers match the pins.
|
|
19
|
+
4. Call `session_activity_begin` and pass the `activityId` it returns on each `session_edit` as you append blocks. Omitted placement puts the block at the top of the pad, and the response's `targetId` names it. A thought that spans several blocks reads top-down only if you insert it bottom-up (last block first) or give each following block `afterId` of the block you just inserted. Draw a diagram whole in one insert; fix one you drew earlier by patching its nodes, edges or steps by ID, and `replace` a block only when the whole picture was wrong. Each block's fields are in the Components section below; read `session_get_instructions({topic:"authoring"})` for choosing components. End the activity when finished.
|
|
20
|
+
5. After your first insert in a session, `session_open({sessionId: "scratchpad"})` once so the pad is showing. Do not call it again for later edits; they appear live.
|
|
21
|
+
6. In chat, say in one line what you drew. Do not repeat the diagram there.
|
|
22
|
+
|
|
23
|
+
Keep it a log, not a document: the newest thought sits at the top, above older ones. Do not build sections, outlines or status markers unless the user asks for structure. When the user asks to clear the pad, `replace` or `remove` its blocks; history keeps what was there.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Trace archaeology
|
|
2
|
+
|
|
3
|
+
Agent-written commits record `Agent-Session: <id>` trailers. Use the `whiteboard trace` CLI to resolve and pull those sessions. Use FFF to find candidate events. Use `whiteboard trace show` for exact evidence.
|
|
4
|
+
|
|
5
|
+
The `whiteboard trace` commands also work without Whiteboard installed or running.
|
|
6
|
+
|
|
7
|
+
## Configuration
|
|
8
|
+
|
|
9
|
+
Before hosted setup, explain: full transcripts go to the chosen origin. Writers can upload and check their own status; only admins can read transcripts.
|
|
10
|
+
|
|
11
|
+
- Enable only user-authorized repositories and origins. Existing authorization is sufficient unless later revoked, including by `whiteboard trace deny`.
|
|
12
|
+
- Check `whiteboard trace status`; `--agent-session <id>` narrows uploads. If authorized, run `whiteboard trace store create` when needed, then `whiteboard trace allow .`. Check status again.
|
|
13
|
+
- Without authorization, leave capture off and continue read-only investigation. Trace lookup does not require publication.
|
|
14
|
+
|
|
15
|
+
FFF setup is human-owned. If FFF is unavailable, report the setup gap. Do not replace or reconfigure it.
|
|
16
|
+
|
|
17
|
+
Local commit trailers and blame resolution work without trace storage access.
|
|
18
|
+
Read commands use the machine's selected trace store. When both an S3/R2
|
|
19
|
+
bucket and the hosted store are configured, add `--storage s3|hosted` to
|
|
20
|
+
read the other one; it changes nothing about capture or consent.
|
|
21
|
+
|
|
22
|
+
## Explain code provenance
|
|
23
|
+
|
|
24
|
+
When asked why code exists, who wrote it, or what decisions produced it:
|
|
25
|
+
|
|
26
|
+
1. Identify the commits behind the target lines:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
whiteboard trace blame <file> -L <start,end> --json
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This command identifies the last commit that touched each line. Add `--history` only when the current provenance does not explain the decision:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
whiteboard trace blame <file> -L <start,end> --history --json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. Pull each relevant session into the local corpus:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
whiteboard trace pull --agent-session <session-id> --json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Read the absolute normalized file paths from the response's `paths` array. Do not derive them from the corpus layout.
|
|
45
|
+
|
|
46
|
+
3. Use FFF to search the normalized files returned in `paths`.
|
|
47
|
+
|
|
48
|
+
Treat each result only as a candidate locator. Each trace file has this shape:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
<owner>/<repo>/<session>/main.jsonl
|
|
52
|
+
<owner>/<repo>/<session>/<subagent>.jsonl
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Ignore physical line 1 because it is trace metadata. For a match on line `<L>`, use event index `<L> - 2`. Use the record's `index` when the excerpt shows it.
|
|
56
|
+
|
|
57
|
+
4. Inspect each relevant event:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
whiteboard trace show <session-id> --trace <trace> --event <event> --json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Pass the trace name from the result, including `main`. Search results are not final evidence.
|
|
64
|
+
|
|
65
|
+
5. Check the current code before you explain the result. The trace can describe a decision that the author later reversed.
|
|
66
|
+
|
|
67
|
+
This flow is complete when you inspected the relevant events and checked every historical claim against the current code.
|
|
68
|
+
|
|
69
|
+
## Research a topic
|
|
70
|
+
|
|
71
|
+
When asked if an agent has previously solved a problem or handled a topic:
|
|
72
|
+
|
|
73
|
+
1. Pull the current repository traces:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
whiteboard trace pull --json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Add `--main-only` only when subagent work is not relevant.
|
|
80
|
+
Read the absolute normalized file paths from the response's `paths` array.
|
|
81
|
+
|
|
82
|
+
2. Use FFF to search the normalized files returned in `paths`. Read the session and trace from the result path. Ignore physical line 1. For a match on line `<L>`, use event index `<L> - 2`.
|
|
83
|
+
|
|
84
|
+
3. Inspect the relevant events:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
whiteboard trace show <session-id> --trace <trace> --event <event> --json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
When the investigation starts from one commit, list its sessions first:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
whiteboard trace list --commit <rev> --json
|
|
94
|
+
whiteboard trace pull --commit <rev> --json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
When no commit anchors the investigation, page through every published session of the hosted store with `--cursor`. This command needs the hosted store; on a machine that selects s3, pass `--storage hosted`.
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
whiteboard trace sessions --json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use `whiteboard trace show <session-id>` when the full session timeline helps explain the result.
|
|
104
|
+
|
|
105
|
+
This flow is complete when you inspected the source events and checked the result against current code.
|
|
106
|
+
|
|
107
|
+
## Evidence Rules
|
|
108
|
+
|
|
109
|
+
- Treat traces as historical evidence, not current specifications.
|
|
110
|
+
- Prefer `--json` when you parse command output.
|
|
111
|
+
- Cite session, trace, and event locators for event evidence.
|
|
112
|
+
- Cite commit IDs and source lines for current code evidence.
|
|
113
|
+
- Report missing or uncertain provenance. Never invent an explanation.
|
package/onboarding.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Whiteboard workflow
|
|
2
|
+
|
|
3
|
+
1. Install Whiteboard Desktop; it installs the `whiteboard` CLI.
|
|
4
|
+
2. Open Whiteboard Desktop. It hosts the JSON review server for this workflow.
|
|
5
|
+
3. Run `whiteboard connect <agent>` and paste its prompt into the agent; the
|
|
6
|
+
agent adds the Whiteboard MCP server, which serves the authoring instructions.
|
|
7
|
+
4. Ask the agent for a review. It authors the review through the Whiteboard MCP
|
|
8
|
+
tools or `whiteboard api`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,109 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev.fast/whiteboard",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.1-preview.20261005.98",
|
|
4
|
+
"private": false,
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/devdotfast/whiteboard.git",
|
|
9
|
+
"directory": "packages/review"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"review": "./dist/cli.js",
|
|
13
|
+
"whiteboard": "./dist/cli.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"README.md",
|
|
18
|
+
"onboarding.md",
|
|
19
|
+
"dist",
|
|
20
|
+
"instructions",
|
|
21
|
+
"tutorial",
|
|
22
|
+
"!tutorial/.bundle",
|
|
23
|
+
"src",
|
|
24
|
+
"!src/**/*.test.ts",
|
|
25
|
+
"!src/**/*test-utils*",
|
|
26
|
+
"THIRD_PARTY_NOTICES.md"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"exports": {
|
|
30
|
+
"./runtime": {
|
|
31
|
+
"types": "./dist/runtime.d.ts",
|
|
32
|
+
"import": "./dist/runtime.js"
|
|
33
|
+
},
|
|
34
|
+
"./sharing": {
|
|
35
|
+
"types": "./dist/sharing/index.d.ts",
|
|
36
|
+
"import": "./dist/sharing/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@agentclientprotocol/claude-agent-acp": "0.84.0",
|
|
44
|
+
"@agentclientprotocol/codex-acp": "2.0.0",
|
|
45
|
+
"@agentclientprotocol/sdk": "1.5.1",
|
|
46
|
+
"@hono/node-server": "2.0.12",
|
|
47
|
+
"@img/sharp-wasm32": "0.35.4",
|
|
48
|
+
"@modelcontextprotocol/sdk": "1.30.0",
|
|
49
|
+
"commander": "14.0.3",
|
|
50
|
+
"fuzzysort": "4.0.2",
|
|
51
|
+
"hono": "4.13.7",
|
|
52
|
+
"mdast-util-from-markdown": "2.0.3",
|
|
53
|
+
"mdast-util-gfm": "3.1.0",
|
|
54
|
+
"mdast-util-math": "3.0.0",
|
|
55
|
+
"micromark-extension-gfm": "3.0.0",
|
|
56
|
+
"pi-acp": "0.0.34",
|
|
57
|
+
"pino": "10.3.1",
|
|
58
|
+
"semver": "7.8.5",
|
|
59
|
+
"sharp": "0.35.4",
|
|
60
|
+
"smol-toml": "1.8.0",
|
|
61
|
+
"zod": "4.6.5"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@dev.fast/diffr": "0.1.10",
|
|
65
|
+
"@dev.fast/local-vcs": "^0.1.0",
|
|
66
|
+
"@types/node": "24.12.2",
|
|
67
|
+
"@types/semver": "7.7.1",
|
|
68
|
+
"micromark-extension-math": "3.1.0",
|
|
69
|
+
"pino-pretty": "13.1.3",
|
|
70
|
+
"tsdown": "0.22.14",
|
|
71
|
+
"tsx": "4.21.0",
|
|
72
|
+
"typescript": "6.0.3",
|
|
73
|
+
"vitest": "4.1.11",
|
|
74
|
+
"@dev.fast/json": "0.0.0",
|
|
75
|
+
"@dev.fast/review-protocol": "0.0.0",
|
|
76
|
+
"@dev.fast/review-share-protocol": "0.1.2",
|
|
77
|
+
"@dev.fast/trace-core": "0.0.0",
|
|
78
|
+
"@dev.fast/trace-protocol": "0.5.0"
|
|
79
|
+
},
|
|
80
|
+
"engines": {
|
|
81
|
+
"node": ">=24 <25"
|
|
82
|
+
},
|
|
83
|
+
"intent": {
|
|
84
|
+
"version": 1,
|
|
85
|
+
"repo": "devdotfast/review",
|
|
86
|
+
"docs": "packages/review/instructions/authoring.md"
|
|
87
|
+
},
|
|
88
|
+
"gitHead": "2c6b8b5b8fe7c3435cf223f6a12a87e507ccf6f1",
|
|
89
|
+
"scripts": {
|
|
90
|
+
"prebuild": "pnpm run build:workspace-deps",
|
|
91
|
+
"ensure:diffr": "diffr-fetch --into bin",
|
|
92
|
+
"build": "node --max-old-space-size=8192 node_modules/tsdown/dist/run.mjs",
|
|
93
|
+
"build:tutorial-assets": "tsx scripts/build-tutorial-assets.ts",
|
|
94
|
+
"ensure:tutorial-assets": "tsx scripts/ensure-tutorial-assets.ts",
|
|
95
|
+
"check:tutorial": "tsx scripts/check-tutorial.ts",
|
|
96
|
+
"build:workspace-deps": "pnpm --filter @dev.fast/json --filter @dev.fast/local-vcs --filter @dev.fast/review-protocol --filter @dev.fast/review-share-protocol --filter @dev.fast/trace-protocol --filter @dev.fast/trace-core build",
|
|
97
|
+
"pretypecheck": "pnpm run ensure:tutorial-assets",
|
|
98
|
+
"pretest": "pnpm run ensure:tutorial-assets",
|
|
99
|
+
"pretest:node": "pnpm run ensure:tutorial-assets",
|
|
100
|
+
"start": "tsx src/cli.ts",
|
|
101
|
+
"review": "tsx src/cli.ts",
|
|
102
|
+
"test": "vitest run --config vitest.config.ts",
|
|
103
|
+
"test:node": "vitest run --config vitest.config.ts --project shared-module-graph",
|
|
104
|
+
"test:telemetry-privacy": "vitest run --config vitest.config.ts src/ui-telemetry-events.test.ts src/error-telemetry.test.ts",
|
|
105
|
+
"typecheck": "pnpm -w exec tsc --noEmit -p packages/review",
|
|
106
|
+
"test:integration:diffr": "pnpm run build:workspace-deps && pnpm run ensure:diffr --required && vitest run --config vitest.integration.config.ts",
|
|
107
|
+
"whiteboard": "tsx src/cli.ts"
|
|
108
|
+
}
|
|
6
109
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReviewApiSelectionSourceSchema,
|
|
3
|
+
ReviewSelectedDiffSchema,
|
|
4
|
+
} from "@dev.fast/review-protocol";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
/** The most block text an anchor keeps; a selection in a longer block is
|
|
8
|
+
* not anchored. */
|
|
9
|
+
export const ASK_ANCHOR_TEXT_LIMIT = 20_000;
|
|
10
|
+
|
|
11
|
+
/** A semantic selection, independent of comment/thread creation. */
|
|
12
|
+
export const AgentSelectionSchema = z.strictObject({
|
|
13
|
+
target: z.discriminatedUnion("kind", [
|
|
14
|
+
z.strictObject({
|
|
15
|
+
kind: z.literal("text"),
|
|
16
|
+
quote: z.string(),
|
|
17
|
+
/** Where the quote is: characters `start` to `end` of the text of the
|
|
18
|
+
* review block `blockId`, which was `text` then. The block keeps its
|
|
19
|
+
* id across versions; comparing its text then and now finds the quote
|
|
20
|
+
* again, or tells that it changed. */
|
|
21
|
+
anchor: z
|
|
22
|
+
.strictObject({
|
|
23
|
+
blockId: z.string().min(1).max(200),
|
|
24
|
+
start: z.number().int().nonnegative(),
|
|
25
|
+
end: z.number().int().positive(),
|
|
26
|
+
text: z.string().max(ASK_ANCHOR_TEXT_LIMIT),
|
|
27
|
+
})
|
|
28
|
+
.refine((anchor) => anchor.start < anchor.end, "an empty anchor")
|
|
29
|
+
.optional(),
|
|
30
|
+
}),
|
|
31
|
+
z.strictObject({
|
|
32
|
+
kind: z.literal("code"),
|
|
33
|
+
path: z.string(),
|
|
34
|
+
side: z.enum(["base", "head"]),
|
|
35
|
+
startLine: z.number().int().positive(),
|
|
36
|
+
endLine: z.number().int().positive(),
|
|
37
|
+
}),
|
|
38
|
+
]),
|
|
39
|
+
title: z.string().max(500),
|
|
40
|
+
detail: z.string().max(24000).optional(),
|
|
41
|
+
revision: z.string().max(200).optional(),
|
|
42
|
+
selectedDiff: ReviewSelectedDiffSchema.optional(),
|
|
43
|
+
apiSource: ReviewApiSelectionSourceSchema.optional(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export type AgentSelection = z.infer<typeof AgentSelectionSchema>;
|
|
47
|
+
|
|
48
|
+
export function selectionMarkdown(
|
|
49
|
+
selection: AgentSelection,
|
|
50
|
+
sourceExcerpt = "",
|
|
51
|
+
diffPaths?: { base: string; head: string },
|
|
52
|
+
): string {
|
|
53
|
+
const target = selection.target;
|
|
54
|
+
|
|
55
|
+
if (selection.selectedDiff)
|
|
56
|
+
return selectedDiffMarkdown(selection.selectedDiff, diffPaths);
|
|
57
|
+
|
|
58
|
+
if (target.kind === "text")
|
|
59
|
+
return target.quote
|
|
60
|
+
.split(/\r?\n/)
|
|
61
|
+
.map((line) => `> ${line}`)
|
|
62
|
+
.join("\n");
|
|
63
|
+
|
|
64
|
+
return [`## ${selection.title}`, selection.detail ?? "", sourceExcerpt]
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.join("\n\n");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function selectedDiffMarkdown(
|
|
70
|
+
diff: z.infer<typeof ReviewSelectedDiffSchema>,
|
|
71
|
+
paths?: { base: string; head: string },
|
|
72
|
+
): string {
|
|
73
|
+
const oldCount = diff.rows.filter((row) => row.kind !== "added").length;
|
|
74
|
+
const newCount = diff.rows.filter((row) => row.kind !== "deleted").length;
|
|
75
|
+
|
|
76
|
+
const lines = diff.rows.map(
|
|
77
|
+
(row) =>
|
|
78
|
+
`${row.kind === "added" ? "+" : row.kind === "deleted" ? "-" : " "}${row.text}`,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Keep Markdown source containing backtick fences inside this diff fence.
|
|
82
|
+
const fence = "`".repeat(
|
|
83
|
+
Math.max(
|
|
84
|
+
3,
|
|
85
|
+
...lines.flatMap((line) =>
|
|
86
|
+
[...line.matchAll(/`+/g)].map((match) => match[0].length + 1),
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return [
|
|
92
|
+
`Base: ${paths?.base ?? "a/"}`,
|
|
93
|
+
`Head: ${paths?.head ?? "b/"}`,
|
|
94
|
+
`Range: -${diff.oldStart},${oldCount} +${diff.newStart},${newCount}`,
|
|
95
|
+
"",
|
|
96
|
+
`${fence}diff`,
|
|
97
|
+
...lines,
|
|
98
|
+
fence,
|
|
99
|
+
].join("\n");
|
|
100
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type ReviewAgentHarness = "claude-code" | "codex" | "opencode" | "pi";
|
|
2
|
+
|
|
3
|
+
export interface SessionRef {
|
|
4
|
+
harness: ReviewAgentHarness;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type EnvironmentValues = Readonly<Record<string, string | undefined>>;
|
|
9
|
+
|
|
10
|
+
export const AUTHORING_AGENT_SESSION_ENV = "DEV_FAST_AGENT_SESSION";
|
|
11
|
+
|
|
12
|
+
export function authoringSessionKey(ref: SessionRef): string {
|
|
13
|
+
return `${ref.harness}:${ref.sessionId}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function parseAuthoringSessionKey(
|
|
17
|
+
value: string | null | undefined,
|
|
18
|
+
): SessionRef | undefined {
|
|
19
|
+
const separator = value?.indexOf(":") ?? -1;
|
|
20
|
+
|
|
21
|
+
if (!value || separator < 1 || separator === value.length - 1) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const harness = value.slice(0, separator);
|
|
26
|
+
const sessionId = value.slice(separator + 1);
|
|
27
|
+
|
|
28
|
+
return isReviewAgentHarness(harness) ? { harness, sessionId } : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveAuthoringSessionRef(
|
|
32
|
+
env: EnvironmentValues,
|
|
33
|
+
): SessionRef | undefined {
|
|
34
|
+
const hosted = readEnvValue(env[AUTHORING_AGENT_SESSION_ENV]);
|
|
35
|
+
|
|
36
|
+
if (hosted) {
|
|
37
|
+
const ref = parseAuthoringSessionKey(hosted);
|
|
38
|
+
|
|
39
|
+
if (!ref) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`${AUTHORING_AGENT_SESSION_ENV} has an invalid authoring session reference.`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return ref;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const codexThreadId = readEnvValue(env.CODEX_THREAD_ID);
|
|
49
|
+
|
|
50
|
+
if (codexThreadId) return { harness: "codex", sessionId: codexThreadId };
|
|
51
|
+
|
|
52
|
+
const claudeSessionId =
|
|
53
|
+
readEnvValue(env.CLAUDE_CODE_SESSION_ID) ??
|
|
54
|
+
readEnvValue(env.CLAUDE_SESSION_ID);
|
|
55
|
+
|
|
56
|
+
if (claudeSessionId) {
|
|
57
|
+
return { harness: "claude-code", sessionId: claudeSessionId };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const piSessionId = readEnvValue(env.PI_SESSION_ID);
|
|
61
|
+
|
|
62
|
+
if (piSessionId) return { harness: "pi", sessionId: piSessionId };
|
|
63
|
+
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isReviewAgentHarness(value: string): value is ReviewAgentHarness {
|
|
68
|
+
return (
|
|
69
|
+
value === "claude-code" ||
|
|
70
|
+
value === "codex" ||
|
|
71
|
+
value === "opencode" ||
|
|
72
|
+
value === "pi"
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readEnvValue(value: string | undefined): string | undefined {
|
|
77
|
+
const trimmed = value?.trim();
|
|
78
|
+
|
|
79
|
+
return trimmed ? trimmed : undefined;
|
|
80
|
+
}
|