@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,126 @@
|
|
|
1
|
+
// The PostHog error-tracking twin of a sanitized review_client_error. It reuses
|
|
2
|
+
// the already cleaned message, the digest and the bundle-relative frames, so it
|
|
3
|
+
// adds nothing the allowlist has not checked.
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type JsonObject,
|
|
10
|
+
jsonObject,
|
|
11
|
+
jsonString,
|
|
12
|
+
parseJsonText,
|
|
13
|
+
} from "@dev.fast/json";
|
|
14
|
+
|
|
15
|
+
import { BUNDLE_FRAME_SEPARATOR } from "./ui-telemetry-events";
|
|
16
|
+
|
|
17
|
+
const FRAME_PATTERN = /^(.+):(\d+):(\d+)$/;
|
|
18
|
+
|
|
19
|
+
/** Processes that run on Node rather than in a browser renderer. */
|
|
20
|
+
const NODE_PROCESSES = new Set(["main", "server"]);
|
|
21
|
+
|
|
22
|
+
export type ChunkIds = ReadonlyMap<string, string>;
|
|
23
|
+
|
|
24
|
+
/** Written by apps/review-desktop/scripts/upload-source-maps.mjs. */
|
|
25
|
+
const CHUNK_ID_MANIFEST = "review-chunk-ids.json";
|
|
26
|
+
|
|
27
|
+
const CHUNK_ID_PATTERN = /^[0-9a-f-]{36}$/;
|
|
28
|
+
|
|
29
|
+
/** The entry is `<app>/review-runtime/dist/server/desktop-host.js`. */
|
|
30
|
+
export function readChunkIds(serverEntry: string | undefined): ChunkIds {
|
|
31
|
+
if (!serverEntry) return new Map();
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const manifest = jsonObject(
|
|
35
|
+
parseJsonText(
|
|
36
|
+
readFileSync(
|
|
37
|
+
path.resolve(
|
|
38
|
+
path.dirname(serverEntry),
|
|
39
|
+
"../../../out",
|
|
40
|
+
CHUNK_ID_MANIFEST,
|
|
41
|
+
),
|
|
42
|
+
"utf8",
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return new Map(
|
|
48
|
+
Object.entries(manifest ?? {}).flatMap(([file, id]) => {
|
|
49
|
+
const chunkId = jsonString(id);
|
|
50
|
+
|
|
51
|
+
return chunkId && CHUNK_ID_PATTERN.test(chunkId)
|
|
52
|
+
? [[file, chunkId] as const]
|
|
53
|
+
: [];
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
} catch {
|
|
57
|
+
return new Map();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function exceptionProperties(
|
|
62
|
+
properties: JsonObject,
|
|
63
|
+
chunkIds: ChunkIds = new Map(),
|
|
64
|
+
): JsonObject | undefined {
|
|
65
|
+
const type = jsonString(properties.error_name);
|
|
66
|
+
const hash = jsonString(properties.message_hash);
|
|
67
|
+
|
|
68
|
+
if (type === undefined && hash === undefined) return undefined;
|
|
69
|
+
|
|
70
|
+
const platform = NODE_PROCESSES.has(
|
|
71
|
+
jsonString(properties.error_process) ?? "",
|
|
72
|
+
)
|
|
73
|
+
? "node:javascript"
|
|
74
|
+
: "web:javascript";
|
|
75
|
+
|
|
76
|
+
const frames = (jsonString(properties.frames) ?? "")
|
|
77
|
+
.split(BUNDLE_FRAME_SEPARATOR)
|
|
78
|
+
.flatMap((frame) => parseFrame(frame, platform, chunkIds));
|
|
79
|
+
|
|
80
|
+
const exception = {
|
|
81
|
+
$exception_level: "error",
|
|
82
|
+
$exception_list: [
|
|
83
|
+
{
|
|
84
|
+
type: type ?? "Error",
|
|
85
|
+
value:
|
|
86
|
+
jsonString(properties.message) ??
|
|
87
|
+
(hash ? `[message withheld] ${hash}` : "[message withheld]"),
|
|
88
|
+
mechanism: { handled: true, synthetic: false },
|
|
89
|
+
stacktrace: { type: "raw", frames },
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
} satisfies JsonObject;
|
|
93
|
+
|
|
94
|
+
// Let PostHog group resolvable stacks by frame, not by message.
|
|
95
|
+
const resolvable =
|
|
96
|
+
frames.length > 0 && frames.every((frame) => frame.chunk_id);
|
|
97
|
+
|
|
98
|
+
return hash === undefined || resolvable
|
|
99
|
+
? exception
|
|
100
|
+
: { ...exception, $exception_fingerprint: hash };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function parseFrame(
|
|
104
|
+
frame: string,
|
|
105
|
+
platform: string,
|
|
106
|
+
chunkIds: ChunkIds,
|
|
107
|
+
): JsonObject[] {
|
|
108
|
+
const match = FRAME_PATTERN.exec(frame);
|
|
109
|
+
|
|
110
|
+
if (!match) return [];
|
|
111
|
+
|
|
112
|
+
const parsed: JsonObject = {
|
|
113
|
+
platform,
|
|
114
|
+
filename: match[1],
|
|
115
|
+
lineno: Number(match[2]),
|
|
116
|
+
colno: Number(match[3]),
|
|
117
|
+
function: "?",
|
|
118
|
+
in_app: true,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const chunkId = chunkIds.get(match[1]);
|
|
122
|
+
|
|
123
|
+
if (chunkId) parsed.chunk_id = chunkId;
|
|
124
|
+
|
|
125
|
+
return [parsed];
|
|
126
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "block-1",
|
|
4
|
+
"type": "call_stack_diff",
|
|
5
|
+
"title": "Status path",
|
|
6
|
+
"base": [
|
|
7
|
+
{
|
|
8
|
+
"id": "frame-1",
|
|
9
|
+
"key": "status",
|
|
10
|
+
"source": "base/order.ts#L1",
|
|
11
|
+
"label": "status = draft"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"head": [
|
|
15
|
+
{
|
|
16
|
+
"id": "frame-2",
|
|
17
|
+
"key": "status",
|
|
18
|
+
"source": "head/order.ts#L1",
|
|
19
|
+
"label": "status = queued",
|
|
20
|
+
"via": {
|
|
21
|
+
"kind": "call",
|
|
22
|
+
"reason": "direct"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "block-1",
|
|
4
|
+
"type": "database_lens",
|
|
5
|
+
"title": "Orders",
|
|
6
|
+
"actors": {
|
|
7
|
+
"service": "Order service"
|
|
8
|
+
},
|
|
9
|
+
"stores": {
|
|
10
|
+
"orders": {
|
|
11
|
+
"label": "Orders DB",
|
|
12
|
+
"storage": "relational",
|
|
13
|
+
"collections": {
|
|
14
|
+
"orders": {
|
|
15
|
+
"label": "orders",
|
|
16
|
+
"key": "id",
|
|
17
|
+
"fields": {
|
|
18
|
+
"id": {
|
|
19
|
+
"label": "id",
|
|
20
|
+
"dataType": "text",
|
|
21
|
+
"primaryKey": true
|
|
22
|
+
},
|
|
23
|
+
"status": {
|
|
24
|
+
"label": "status",
|
|
25
|
+
"dataType": "text"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"useCases": [
|
|
33
|
+
{
|
|
34
|
+
"id": "case-1",
|
|
35
|
+
"label": "Queue an order",
|
|
36
|
+
"operations": [
|
|
37
|
+
{
|
|
38
|
+
"id": "operation-1",
|
|
39
|
+
"kind": "write",
|
|
40
|
+
"store": "orders",
|
|
41
|
+
"collection": "orders",
|
|
42
|
+
"field": "status",
|
|
43
|
+
"actor": "service",
|
|
44
|
+
"label": "write queued",
|
|
45
|
+
"source": "head/order.ts#L1"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{ "id": "block-1", "type": "divider" }]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { type JsonValue, parseJsonText } from "@dev.fast/review-protocol";
|
|
6
|
+
|
|
7
|
+
export const BLOCK_FIXTURES_ROOT = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
FIXTURE_IMAGE_ID,
|
|
11
|
+
FIXTURE_MAP_ID,
|
|
12
|
+
FIXTURE_TRACE_EVENT_ID,
|
|
13
|
+
FIXTURE_TRACE_ID,
|
|
14
|
+
} from "./ids.js";
|
|
15
|
+
|
|
16
|
+
/** One entry per `<type>.json`: the block type and its array of valid blocks. */
|
|
17
|
+
export async function readBlockFixtures(): Promise<Map<string, JsonValue[]>> {
|
|
18
|
+
const fixtures = new Map<string, JsonValue[]>();
|
|
19
|
+
|
|
20
|
+
for (const name of (await readdir(BLOCK_FIXTURES_ROOT)).sort()) {
|
|
21
|
+
if (!name.endsWith(".json")) continue;
|
|
22
|
+
|
|
23
|
+
const parsed = parseJsonText(
|
|
24
|
+
await readFile(path.join(BLOCK_FIXTURES_ROOT, name), "utf8"),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (!Array.isArray(parsed))
|
|
28
|
+
throw new Error(`${name} must be an array of blocks.`);
|
|
29
|
+
fixtures.set(name.slice(0, -".json".length), parsed);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return fixtures;
|
|
33
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"type": "flow_diagram",
|
|
4
|
+
"title": "Queue an order",
|
|
5
|
+
"nodes": [
|
|
6
|
+
{
|
|
7
|
+
"key": "request",
|
|
8
|
+
"label": "Queue order",
|
|
9
|
+
"attachments": [
|
|
10
|
+
{
|
|
11
|
+
"label": "Handler",
|
|
12
|
+
"sources": ["head/order.ts#L1"]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"label": "Validation",
|
|
16
|
+
"sources": ["base/order.ts#L1"]
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"key": "saved",
|
|
22
|
+
"label": "Saved",
|
|
23
|
+
"kind": "terminal",
|
|
24
|
+
"attachments": []
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"edges": [
|
|
28
|
+
{
|
|
29
|
+
"from": "request",
|
|
30
|
+
"to": "saved",
|
|
31
|
+
"label": "persist"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"id": "flow-1"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Resource ids the block fixtures reference; tests map them to real uploads or stubs. */
|
|
2
|
+
export const FIXTURE_IMAGE_ID = "11111111-1111-4111-8111-111111111111";
|
|
3
|
+
|
|
4
|
+
export const FIXTURE_TRACE_ID = "22222222-2222-4222-8222-222222222222";
|
|
5
|
+
|
|
6
|
+
export const FIXTURE_TRACE_EVENT_ID = "0";
|
|
7
|
+
|
|
8
|
+
export const FIXTURE_MAP_ID = "33333333-3333-4333-8333-333333333333";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "diagram-1",
|
|
4
|
+
"type": "sequence",
|
|
5
|
+
"title": "Order status",
|
|
6
|
+
"actors": {
|
|
7
|
+
"api": "API",
|
|
8
|
+
"store": "Order store"
|
|
9
|
+
},
|
|
10
|
+
"steps": [
|
|
11
|
+
{
|
|
12
|
+
"id": "step-1",
|
|
13
|
+
"type": "step",
|
|
14
|
+
"from": "api",
|
|
15
|
+
"to": "store",
|
|
16
|
+
"label": "set status",
|
|
17
|
+
"style": "call",
|
|
18
|
+
"source": "head/order.ts#L1"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "step-2",
|
|
22
|
+
"type": "step",
|
|
23
|
+
"from": "store",
|
|
24
|
+
"to": "api",
|
|
25
|
+
"label": "queued",
|
|
26
|
+
"style": "return",
|
|
27
|
+
"explanation": "The stored status"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"type": "tutorial",
|
|
4
|
+
"kind": "keymap",
|
|
5
|
+
"id": "block-1"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"type": "tutorial",
|
|
9
|
+
"kind": "conversation",
|
|
10
|
+
"conversation": {
|
|
11
|
+
"version": 1,
|
|
12
|
+
"title": "How an agent authors a review",
|
|
13
|
+
"messages": [
|
|
14
|
+
{
|
|
15
|
+
"role": "user",
|
|
16
|
+
"body": "Explain this change."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"role": "assistant",
|
|
20
|
+
"body": "Here is a review."
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"id": "block-2"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"type": "tutorial",
|
|
28
|
+
"kind": "view",
|
|
29
|
+
"view": "commits",
|
|
30
|
+
"label": "Explore commits",
|
|
31
|
+
"id": "block-3"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "tutorial",
|
|
35
|
+
"kind": "feature",
|
|
36
|
+
"feature": "softwareMap",
|
|
37
|
+
"children": [
|
|
38
|
+
{
|
|
39
|
+
"type": "markdown",
|
|
40
|
+
"markdown": "Explore the map.",
|
|
41
|
+
"id": "block-5"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "block-6",
|
|
45
|
+
"type": "software_map",
|
|
46
|
+
"mapVersionId": "33333333-3333-4333-8333-333333333333"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"id": "block-4"
|
|
50
|
+
}
|
|
51
|
+
]
|
package/src/fs-utils.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
/** Whether a thrown filesystem error reports a missing file (ENOENT). */
|
|
4
|
+
export function isMissingFileError(cause: unknown): boolean {
|
|
5
|
+
return cause instanceof Error && "code" in cause && cause.code === "ENOENT";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Entries of a directory that may not exist yet. */
|
|
9
|
+
export async function readDirectory(directory: string) {
|
|
10
|
+
try {
|
|
11
|
+
return await readdir(directory, { withFileTypes: true });
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (isMissingFileError(error)) return [];
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function isDirectory(target: string): Promise<boolean> {
|
|
19
|
+
try {
|
|
20
|
+
return (await stat(target)).isDirectory();
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function isFile(target: string): Promise<boolean> {
|
|
27
|
+
try {
|
|
28
|
+
return (await stat(target)).isFile();
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fuzzysort from "fuzzysort";
|
|
2
|
+
|
|
3
|
+
/* Below this score the query only matched as scattered letters — "dev" landing
|
|
4
|
+
on the d, e and v of "Speed up Review Desktop". Those hits are noise, and a
|
|
5
|
+
plain subsequence matcher cannot tell them from real ones. */
|
|
6
|
+
const SCORE_THRESHOLD = 0.5;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The single fuzzy matcher for this package. Each label is scored on its own,
|
|
10
|
+
* never joined: a match across a join could pair a letter from one label with
|
|
11
|
+
* a letter from the next and keep an item that shows neither word.
|
|
12
|
+
*/
|
|
13
|
+
function bestScore(needle: string, labels: readonly string[]): number {
|
|
14
|
+
let best = 0;
|
|
15
|
+
|
|
16
|
+
for (const label of labels) {
|
|
17
|
+
const score = fuzzysort.single(needle, label)?.score ?? 0;
|
|
18
|
+
|
|
19
|
+
if (score > best) best = score;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return best;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** True when the query hits any label. An empty query matches everything. */
|
|
26
|
+
export function fuzzyMatches(query: string, ...labels: string[]): boolean {
|
|
27
|
+
const needle = query.trim();
|
|
28
|
+
|
|
29
|
+
if (!needle) return true;
|
|
30
|
+
|
|
31
|
+
return bestScore(needle, labels) >= SCORE_THRESHOLD;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface FuzzySegment {
|
|
35
|
+
text: string;
|
|
36
|
+
matched: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The label split into matched and unmatched runs, in order, for callers that
|
|
41
|
+
* want to mark the hit. Returns the whole label as one unmatched segment when
|
|
42
|
+
* the label itself did not match — a review found by its worktree name shows a
|
|
43
|
+
* plain title, not a scatter of marked letters.
|
|
44
|
+
*
|
|
45
|
+
* Segments, not markup: this module must not depend on a UI framework.
|
|
46
|
+
*/
|
|
47
|
+
export function fuzzySegments(query: string, label: string): FuzzySegment[] {
|
|
48
|
+
const whole = [{ text: label, matched: false }];
|
|
49
|
+
const needle = query.trim();
|
|
50
|
+
|
|
51
|
+
if (!needle) return whole;
|
|
52
|
+
const result = fuzzysort.single(needle, label);
|
|
53
|
+
|
|
54
|
+
if (!result || result.score < SCORE_THRESHOLD) return whole;
|
|
55
|
+
|
|
56
|
+
// Indexed by code unit, the way fuzzysort reports its matches.
|
|
57
|
+
const hit = new Set<number>(result.indexes);
|
|
58
|
+
const segments: FuzzySegment[] = [];
|
|
59
|
+
|
|
60
|
+
for (let index = 0; index < label.length; index++) {
|
|
61
|
+
const matched = hit.has(index);
|
|
62
|
+
const last = segments.at(-1);
|
|
63
|
+
|
|
64
|
+
if (last && last.matched === matched) {
|
|
65
|
+
last.text += label[index];
|
|
66
|
+
} else {
|
|
67
|
+
segments.push({ text: label[index], matched });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return segments;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The items whose labels hit the query, best match first. An empty query keeps
|
|
76
|
+
* every item in its original order.
|
|
77
|
+
*/
|
|
78
|
+
export function fuzzyRank<T>(
|
|
79
|
+
query: string,
|
|
80
|
+
items: readonly T[],
|
|
81
|
+
labelsOf: (item: T) => string[],
|
|
82
|
+
): T[] {
|
|
83
|
+
const needle = query.trim();
|
|
84
|
+
|
|
85
|
+
if (!needle) return [...items];
|
|
86
|
+
|
|
87
|
+
return items
|
|
88
|
+
.values()
|
|
89
|
+
.map((item) => ({ item, score: bestScore(needle, labelsOf(item)) }))
|
|
90
|
+
.filter((hit) => hit.score >= SCORE_THRESHOLD)
|
|
91
|
+
.toArray()
|
|
92
|
+
.sort((left, right) => right.score - left.score)
|
|
93
|
+
.map((hit) => hit.item);
|
|
94
|
+
}
|
package/src/install.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
export type InstallTarget =
|
|
5
|
+
| "claude"
|
|
6
|
+
| "codex"
|
|
7
|
+
| "cursor"
|
|
8
|
+
| "opencode"
|
|
9
|
+
| "pi"
|
|
10
|
+
| "omp"
|
|
11
|
+
| "copilot";
|
|
12
|
+
|
|
13
|
+
export const ALL_INSTALL_TARGETS: InstallTarget[] = [
|
|
14
|
+
"claude",
|
|
15
|
+
"codex",
|
|
16
|
+
"cursor",
|
|
17
|
+
"opencode",
|
|
18
|
+
"pi",
|
|
19
|
+
"omp",
|
|
20
|
+
"copilot",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export function isInstallTarget(value: string): value is InstallTarget {
|
|
24
|
+
return ALL_INSTALL_TARGETS.some((target) => target === value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function defaultPackageRoot(): string {
|
|
28
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
29
|
+
}
|