@dev.fast/whiteboard 0.3.4-preview.20261019.126 → 0.3.4
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/dist/{account-alias-B5-HYi1D.js → account-alias-CprZojXd.js} +8 -13
- package/dist/agent-cli-C6SPSII0.js +2 -0
- package/dist/{agent-cli-CohHf6Ua.js → agent-cli-CThN7Q59.js} +10 -15
- package/dist/{agent-client-Dtc-Kn_w.js → agent-client-BOOi7EUR.js} +3 -13
- package/dist/{authoring-tools-B7KU04f7.js → authoring-tools-BNwuMnz6.js} +27 -34
- package/dist/{background-server-C03RKxKS.js → background-server-CkvxA-6O.js} +5 -5
- package/dist/{background-server-CB9C4uoc.js → background-server-DLSDogkI.js} +1 -1
- package/dist/build-info.json +1 -1
- package/dist/{cli-CJaEb7zC.js → cli-R4yfa5DT.js} +1 -1
- package/dist/{cli-runner-DGDIqh3V.js → cli-runner-Cuc9FSzu.js} +26 -26
- package/dist/cli.js +3 -4
- package/dist/{client-lKVF_Elo.js → client-BN_FDxFr.js} +47 -253
- package/dist/{fuzzy-match-BmVJJm9I.js → fuzzy-match-BO9bK37N.js} +4 -6
- package/dist/{headless-host-DRtjf9WA.js → headless-host-CVBOQyRS.js} +1 -1
- package/dist/{headless-host-XhL1X4vX.js → headless-host-Cog6SpS9.js} +140 -513
- package/dist/{local-data-Cpl4ENbJ.js → local-data-D_9tvdsp.js} +323 -2419
- package/dist/{mcp-ovmiUE_x.js → mcp-D1hziANa.js} +9 -5
- package/dist/{package-paths-BWxO-UEL.js → package-paths-C--__jEv.js} +1 -1
- package/dist/profile-CYPmvBoA.js +2 -0
- package/dist/{profile-BadSkCkI.js → profile-DokFw782.js} +8 -20
- package/dist/{remote-attach-Dd-YobM6.js → remote-attach-CEJ-kvDY.js} +4 -4
- package/dist/{remote-extensions-CNXwqK0D.js → remote-extensions-Blcjr9Ze.js} +3 -3
- package/dist/{remote-extensions-VP-kfq0C.js → remote-extensions-GN8Gf_qG.js} +1 -1
- package/dist/{remote-language-server-BXk87KCr.js → remote-language-server-BTKBQ7x4.js} +5 -5
- package/dist/{remote-uninstall-DuOzsvaD.js → remote-uninstall-DW7Gdryh.js} +6 -6
- package/dist/{request-origin-B9qCPiVB.js → request-origin-Zcn_CjX4.js} +12 -45
- package/dist/{review-app-launcher-C7el2Uoy.js → review-app-launcher-CkwLclzR.js} +2 -2
- package/dist/{review-home-paths-D8AoCdd2.js → review-home-paths-j2BRAaNv.js} +17 -19
- package/dist/{review-telemetry-BsoRMC2w.js → review-telemetry-BLNwYnLV.js} +5 -5
- package/dist/runtime-CKiWhGrf.js +52 -0
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +1 -1
- package/dist/{s3-CsG2EWED.js → s3-DtJFWC0G.js} +1 -1
- package/dist/{s3-config-CjcLyFV5.js → s3-config-BTO12tCC.js} +2 -4
- package/dist/{s3-config-zz4GWh2T.js → s3-config-CSy3fx8H.js} +1 -1
- package/dist/server/desktop-host.js +76 -29
- package/dist/{server-discovery-CKjw3-6h.js → server-discovery-Bey8IqXH.js} +3 -3
- package/dist/sharing/index.d.ts +70 -484
- package/dist/sharing/index.js +1 -1
- package/dist/{src-WPaQn75m.js → src-BMPuxWv4.js} +73 -208
- package/dist/{src-BiWNbcF_.js → src-DsM3jO8G.js} +12 -89
- package/dist/{stored-document-migration-dFbewRTG.js → stored-document-migration-CChxtFAy.js} +269 -270
- package/dist/{thread-state-Do5LX10I.js → thread-state-5pYr6_zM.js} +2 -7
- package/dist/{tool-failure-DvI-gys2.js → tool-failure-C9MpbXJJ.js} +3 -10
- package/instructions/authoring.md +2 -3
- package/instructions/scratchpad.md +23 -0
- package/package.json +4 -4
- package/src/ask/agents.ts +1 -13
- package/src/ask/checkout-files.ts +5 -2
- package/src/ask/thread-state.ts +0 -4
- package/src/ask/thread.ts +0 -16
- package/src/ask/threads.ts +3 -24
- package/src/cli-install.ts +2 -13
- package/src/cli-runner.ts +1 -4
- package/src/cli.ts +1 -4
- package/src/diff-selection-migration.ts +34 -48
- package/src/lens-selection.ts +2 -38
- package/src/review-api/README.md +13 -52
- package/src/review-api/activity.ts +4 -1
- package/src/review-api/agent-cli.ts +1 -9
- package/src/review-api/anchor-quotes.ts +7 -14
- package/src/review-api/ask-history.ts +51 -137
- package/src/review-api/authoring-tools.ts +30 -45
- package/src/review-api/blocks/definition.ts +1 -1
- package/src/review-api/blocks/markdown.ts +2 -2
- package/src/review-api/document-headings.ts +27 -58
- package/src/review-api/document-text.ts +2 -31
- package/src/review-api/document.ts +34 -281
- package/src/review-api/github-provider.ts +13 -51
- package/src/review-api/http.ts +123 -702
- package/src/review-api/instructions.ts +19 -0
- package/src/review-api/local-data.ts +46 -310
- package/src/review-api/mcp.ts +11 -3
- package/src/review-api/onboarding.ts +1 -1
- package/src/review-api/profile.ts +6 -37
- package/src/review-api/public-tools.ts +2 -24
- package/src/review-api/pull-request.ts +49 -82
- package/src/review-api/request-origin.ts +0 -10
- package/src/review-api/review-progress.ts +27 -61
- package/src/review-api/store-schema.ts +29 -38
- package/src/review-api/store.ts +478 -2142
- package/src/review-api/worktree-events.ts +5 -3
- package/src/review-api/worktree-source.ts +7 -3
- package/src/review-api/worktree-structural.integration.ts +0 -72
- package/src/review-preferences.ts +88 -0
- package/src/review-stack.ts +23 -15
- package/src/runtime.ts +30 -4
- package/src/server/desktop-host.ts +1 -6
- package/src/server/desktop-server.ts +31 -1
- package/src/server/headless-host.ts +2 -0
- package/src/server/hono-http.ts +1 -2
- package/src/server/legacy-tutorial-cleanup.ts +3 -2
- package/src/server/review-gateway-list.ts +5 -3
- package/src/server/review-gateway-memory.ts +1 -1
- package/src/server/review-lifecycle-telemetry.ts +2 -2
- package/src/server/review-server-core.ts +5 -1
- package/src/server/structural-comparisons.ts +0 -47
- package/src/sharing/export.ts +7 -36
- package/src/sharing/host.ts +14 -34
- package/src/sharing/import.ts +24 -156
- package/src/sharing/repository.ts +20 -128
- package/src/startup-trace.ts +3 -3
- package/src/stored-document-migration.ts +19 -110
- package/src/ui-telemetry-events.ts +11 -33
- package/dist/agent-cli-_YyMIMRY.js +0 -2
- package/dist/lens-selection-CyJapcsl.js +0 -214
- package/dist/profile-DJvkBJNQ.js +0 -2
- package/dist/runtime-Bj2tD33R.js +0 -27
- package/src/heading-anchors.ts +0 -42
- package/src/review-api/board-order.ts +0 -144
- package/src/review-api/checkout-groups.ts +0 -560
- package/src/review-api/foreign-references.ts +0 -328
- package/src/review-api/link-preview.ts +0 -113
- package/src/review-api/scratchpad-plan-upgrade.ts +0 -250
- package/src/review-api/store-migrations.ts +0 -357
|
@@ -2,25 +2,12 @@ import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { gitAt } from "@dev.fast/local-vcs";
|
|
6
6
|
import { normalizeGitHubRemote } from "@dev.fast/review-share-protocol";
|
|
7
7
|
import { type Pins, ReviewInputError } from "@review/review-api/document.js";
|
|
8
8
|
|
|
9
|
-
/** A full commit id, SHA-1 or SHA-256. */
|
|
10
|
-
export const COMMIT_ID = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
|
|
11
|
-
|
|
12
|
-
/** Refuses anything but full commit ids, before any of them reaches git:
|
|
13
|
-
* a shared board's commits come from someone else, and one spelled as an
|
|
14
|
-
* option or a refspec would make git do something else. */
|
|
15
|
-
export function assertCommitIds(commits: readonly string[]) {
|
|
16
|
-
if (!commits.every((commit) => COMMIT_ID.test(commit)))
|
|
17
|
-
throw new ReviewInputError(
|
|
18
|
-
"The shared whiteboard names a commit that isn't a full commit id.",
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
9
|
/** Keep credential helpers but prevent the calling shell from redirecting Git's object store. */
|
|
23
|
-
export function sharedGit(cwd: string, args:
|
|
10
|
+
export function sharedGit(cwd: string, args: string[]) {
|
|
24
11
|
const env = Object.fromEntries(
|
|
25
12
|
Object.entries(process.env).filter(
|
|
26
13
|
([key]) =>
|
|
@@ -38,7 +25,7 @@ export function sharedGit(cwd: string, args: VcsArg[]) {
|
|
|
38
25
|
cwd,
|
|
39
26
|
["-c", "core.hooksPath=/dev/null", "-c", "core.fsmonitor=false", ...args],
|
|
40
27
|
{
|
|
41
|
-
env,
|
|
28
|
+
env: { ...env, GIT_TERMINAL_PROMPT: "0" },
|
|
42
29
|
timeout: 300_000,
|
|
43
30
|
},
|
|
44
31
|
);
|
|
@@ -59,28 +46,18 @@ function fetchError(stderr: string, publishing: boolean): ReviewInputError {
|
|
|
59
46
|
);
|
|
60
47
|
}
|
|
61
48
|
|
|
62
|
-
/** Recipients need complete history; publication only checks that the commits exist.
|
|
63
|
-
* `commits` are other commits the board's references read at; a plan has
|
|
64
|
-
* only those. */
|
|
49
|
+
/** Recipients need complete history; publication only checks that the commits exist. */
|
|
65
50
|
export async function fetchPinnedRepository(
|
|
66
51
|
root: string,
|
|
67
52
|
url: string,
|
|
68
|
-
pins: Pick<Pins, "base" | "head"
|
|
53
|
+
pins: Pick<Pins, "base" | "head">,
|
|
69
54
|
publishing = false,
|
|
70
|
-
commits: readonly string[] = [],
|
|
71
55
|
) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
{ commit: pins.head, ref: "head" },
|
|
78
|
-
]
|
|
79
|
-
: []),
|
|
80
|
-
...commits.map((commit) => ({ commit, ref: `commits/${commit}` })),
|
|
81
|
-
];
|
|
82
|
-
|
|
83
|
-
if (!named.every(({ commit }) => COMMIT_ID.test(commit)))
|
|
56
|
+
if (
|
|
57
|
+
![pins.base, pins.head].every((commit) =>
|
|
58
|
+
/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(commit),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
84
61
|
throw new ReviewInputError(
|
|
85
62
|
"Sharing requires immutable base and head commits.",
|
|
86
63
|
);
|
|
@@ -90,7 +67,7 @@ export async function fetchPinnedRepository(
|
|
|
90
67
|
if (!publishing) {
|
|
91
68
|
await sharedGit(root, ["config", "core.hooksPath", "/dev/null"]);
|
|
92
69
|
await sharedGit(root, ["config", "core.fsmonitor", "false"]);
|
|
93
|
-
await sharedGit(root, ["remote", "add", "origin",
|
|
70
|
+
await sharedGit(root, ["remote", "add", "origin", url]);
|
|
94
71
|
}
|
|
95
72
|
|
|
96
73
|
// Branch refs make Git reject non-commit objects during the fetch itself.
|
|
@@ -102,10 +79,9 @@ export async function fetchPinnedRepository(
|
|
|
102
79
|
"--no-tags",
|
|
103
80
|
"--force",
|
|
104
81
|
...(publishing ? ["--depth=1", "--filter=tree:0"] : []),
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
})),
|
|
82
|
+
publishing ? url : "origin",
|
|
83
|
+
`${pins.base}:${refs}/base`,
|
|
84
|
+
`${pins.head}:${refs}/head`,
|
|
109
85
|
]);
|
|
110
86
|
} catch (error) {
|
|
111
87
|
throw fetchError(
|
|
@@ -115,104 +91,21 @@ export async function fetchPinnedRepository(
|
|
|
115
91
|
}
|
|
116
92
|
|
|
117
93
|
if (!publishing)
|
|
118
|
-
for (const
|
|
119
|
-
await sharedGit(root, ["cat-file", "-e",
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Fetches the commits a shared board's links read at into a checkout of
|
|
123
|
-
* its repository, from its origin unless it has them, and keeps each under
|
|
124
|
-
* `refs/review/shared/<share>/<commit>`, so `git gc` can't prune one the
|
|
125
|
-
* board still quotes. */
|
|
126
|
-
export async function fetchSharedCommits(
|
|
127
|
-
root: string,
|
|
128
|
-
commits: readonly string[],
|
|
129
|
-
share: string,
|
|
130
|
-
) {
|
|
131
|
-
assertCommitIds(commits);
|
|
132
|
-
|
|
133
|
-
const ref = (commit: string) => `refs/review/shared/${share}/${commit}`;
|
|
134
|
-
const missing: string[] = [];
|
|
135
|
-
|
|
136
|
-
for (const commit of commits)
|
|
137
|
-
try {
|
|
138
|
-
await sharedGit(root, ["cat-file", "-e", { rev: `${commit}^{commit}` }]);
|
|
139
|
-
await sharedGit(root, ["update-ref", { ref: ref(commit) }, { commit }]);
|
|
140
|
-
} catch {
|
|
141
|
-
missing.push(commit);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (!missing.length) return;
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
await sharedGit(root, [
|
|
148
|
-
"fetch",
|
|
149
|
-
"--no-tags",
|
|
150
|
-
"origin",
|
|
151
|
-
...missing.map((commit) => ({
|
|
152
|
-
refspec: { from: commit, to: ref(commit), force: true },
|
|
153
|
-
})),
|
|
154
|
-
]);
|
|
155
|
-
} catch (error) {
|
|
156
|
-
throw fetchError(
|
|
157
|
-
error instanceof Error && "stderr" in error ? String(error.stderr) : "",
|
|
158
|
-
false,
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** Drops the refs `fetchSharedCommits` kept for a share, once the share is
|
|
164
|
-
* gone, so `git gc` can prune what only it quoted. */
|
|
165
|
-
export async function forgetSharedCommits(root: string, share: string) {
|
|
166
|
-
const { stdout } = await sharedGit(root, [
|
|
167
|
-
"for-each-ref",
|
|
168
|
-
"--format=%(refname)",
|
|
169
|
-
{ ref: `refs/review/shared/${share}/` },
|
|
170
|
-
]);
|
|
171
|
-
|
|
172
|
-
for (const ref of stdout.split("\n").filter(Boolean))
|
|
173
|
-
await sharedGit(root, ["update-ref", "-d", { ref }]);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** A managed clone of a repository's default branch, for a shared board
|
|
177
|
-
* with no pins whose repository the receiver has no checkout of. */
|
|
178
|
-
export async function cloneSharedRepository(root: string, url: string) {
|
|
179
|
-
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
180
|
-
await sharedGit(root, ["init", "--quiet"]);
|
|
181
|
-
await sharedGit(root, ["config", "core.hooksPath", "/dev/null"]);
|
|
182
|
-
await sharedGit(root, ["config", "core.fsmonitor", "false"]);
|
|
183
|
-
await sharedGit(root, ["remote", "add", "origin", { remote: url }]);
|
|
184
|
-
|
|
185
|
-
try {
|
|
186
|
-
await sharedGit(root, ["fetch", "--no-tags", "origin", "HEAD"]);
|
|
187
|
-
} catch (error) {
|
|
188
|
-
throw fetchError(
|
|
189
|
-
error instanceof Error && "stderr" in error ? String(error.stderr) : "",
|
|
190
|
-
false,
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
await sharedGit(root, ["checkout", "--detach", "FETCH_HEAD"]);
|
|
94
|
+
for (const commit of [pins.base, pins.head])
|
|
95
|
+
await sharedGit(root, ["cat-file", "-e", `${commit}^{commit}`]);
|
|
195
96
|
}
|
|
196
97
|
|
|
197
98
|
export async function repositoryReady(
|
|
198
99
|
root: string,
|
|
199
100
|
pins: Pick<Pins, "base" | "head">,
|
|
200
|
-
commits: readonly string[] = [],
|
|
201
101
|
): Promise<boolean> {
|
|
202
|
-
if (
|
|
203
|
-
![pins.base, pins.head, ...commits].every((commit) =>
|
|
204
|
-
COMMIT_ID.test(commit),
|
|
205
|
-
)
|
|
206
|
-
)
|
|
207
|
-
return false;
|
|
208
|
-
|
|
209
102
|
try {
|
|
210
103
|
const head = await sharedGit(root, ["rev-parse", "HEAD"]);
|
|
211
104
|
|
|
212
105
|
if (head.stdout.trim() !== pins.head) return false;
|
|
213
106
|
|
|
214
|
-
for (const commit of [pins.base, pins.head
|
|
215
|
-
await sharedGit(root, ["cat-file", "-e",
|
|
107
|
+
for (const commit of [pins.base, pins.head])
|
|
108
|
+
await sharedGit(root, ["cat-file", "-e", `${commit}^{commit}`]);
|
|
216
109
|
|
|
217
110
|
return true;
|
|
218
111
|
} catch {
|
|
@@ -236,15 +129,14 @@ export async function readShareRepository(root: string) {
|
|
|
236
129
|
|
|
237
130
|
export async function verifyShareRepository(
|
|
238
131
|
root: string,
|
|
239
|
-
pins: Pins
|
|
132
|
+
pins: Pins,
|
|
240
133
|
repository?: { cloneUrl: string },
|
|
241
|
-
commits: readonly string[] = [],
|
|
242
134
|
) {
|
|
243
135
|
const { cloneUrl } = repository ?? (await readShareRepository(root));
|
|
244
136
|
const temporary = await mkdtemp(path.join(tmpdir(), "review-share-check-"));
|
|
245
137
|
|
|
246
138
|
try {
|
|
247
|
-
await fetchPinnedRepository(temporary, cloneUrl, pins, true
|
|
139
|
+
await fetchPinnedRepository(temporary, cloneUrl, pins, true);
|
|
248
140
|
} finally {
|
|
249
141
|
await rm(temporary, { recursive: true, force: true });
|
|
250
142
|
}
|
package/src/startup-trace.ts
CHANGED
|
@@ -221,9 +221,9 @@ if (traceEnabled) {
|
|
|
221
221
|
// Every git/jj spawn inside @dev.fast/local-vcs becomes a `$ …` span under
|
|
222
222
|
// whatever span is active in the caller's async context.
|
|
223
223
|
setLocalVcsCommandObserver({
|
|
224
|
-
start({
|
|
225
|
-
const handle = startSpan(commandSpanName(
|
|
226
|
-
detail: commandDetail(
|
|
224
|
+
start({ file, args, cwd }) {
|
|
225
|
+
const handle = startSpan(commandSpanName(file, args), {
|
|
226
|
+
detail: commandDetail(file, args, cwd),
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
return ({ ok }) => (ok ? handle.end() : handle.fail());
|
|
@@ -1,124 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type JsonObject,
|
|
3
|
-
type JsonValue,
|
|
4
|
-
isBooleanValue,
|
|
5
|
-
isJsonObject,
|
|
6
|
-
isNumberValue,
|
|
7
|
-
isStringValue,
|
|
8
|
-
} from "@dev.fast/review-protocol";
|
|
1
|
+
import { type JsonValue, isJsonObject } from "@dev.fast/review-protocol";
|
|
9
2
|
import { z } from "zod";
|
|
10
3
|
|
|
11
|
-
import {
|
|
12
|
-
ATTACHMENT_KEYS,
|
|
13
|
-
migrateDiffSelections,
|
|
14
|
-
retiredSelection,
|
|
15
|
-
} from "./diff-selection-migration.js";
|
|
4
|
+
import { migrateDiffSelections } from "./diff-selection-migration.js";
|
|
16
5
|
import { formatAnchor } from "./lens-selection.js";
|
|
17
6
|
import { label } from "./review-api/blocks/definition.js";
|
|
18
7
|
import { type Lens, lensTargetsSchema } from "./review-api/diff-lenses.js";
|
|
19
|
-
import { respellLinks } from "./review-api/document.js";
|
|
20
8
|
|
|
21
9
|
/** Upgrade a saved or shared Review document to the current block schema at the
|
|
22
10
|
* read/import boundary. New edits use the strict schema and never accept these
|
|
23
|
-
* retired forms.
|
|
24
|
-
* selections, anchors become strings, sections lose their status, and
|
|
25
|
-
* Markdown links are spelled the current way. */
|
|
11
|
+
* retired forms. */
|
|
26
12
|
// This is the decoder boundary for stored documents in retired wire formats.
|
|
27
13
|
// oxlint-disable-next-line anti-slop/no-unknown-parameters
|
|
28
14
|
export function migrateStoredDocument(input: unknown): JsonValue {
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Where a node sits: under a key that held a retired attachment, and
|
|
33
|
-
* whether its anchors still need converting (not inside an anchor). */
|
|
34
|
-
interface NodePlace {
|
|
35
|
-
attachment: boolean;
|
|
36
|
-
anchors: boolean;
|
|
37
|
-
blockPins?: JsonValue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Decoder boundary: every value is checked to be JSON as it is copied.
|
|
41
|
-
function upgradeNode(value: unknown, place: NodePlace): JsonValue {
|
|
42
|
-
if (isStringValue(value) || isBooleanValue(value) || value === null)
|
|
43
|
-
return value;
|
|
44
|
-
|
|
45
|
-
if (isNumberValue(value) && Number.isFinite(value)) return value;
|
|
46
|
-
|
|
47
|
-
if (Array.isArray(value))
|
|
48
|
-
return value.map((child) => upgradeNode(child, place));
|
|
49
|
-
|
|
50
|
-
if (!isJsonObject(value)) throw new TypeError("A document must be JSON.");
|
|
51
|
-
|
|
52
|
-
const selection = place.attachment ? retiredSelection(value) : undefined;
|
|
53
|
-
|
|
54
|
-
if (selection)
|
|
55
|
-
return upgradeNode(selection, { attachment: false, anchors: false });
|
|
56
|
-
|
|
57
|
-
const inherited = place.anchors ? (value.pins ?? place.blockPins) : undefined;
|
|
58
|
-
let pins: JsonValue | undefined;
|
|
59
|
-
|
|
60
|
-
const convert = (anchor: JsonValue): JsonValue => {
|
|
61
|
-
// Most anchors are strings already.
|
|
62
|
-
if (isStringValue(anchor)) return anchor;
|
|
63
|
-
|
|
64
|
-
const parsed = selectionObjectSchema.safeParse(anchor);
|
|
65
|
-
|
|
66
|
-
if (!parsed.success) return anchor;
|
|
67
|
-
|
|
68
|
-
const { pins: own, ...anchorAt } = parsed.data;
|
|
69
|
-
|
|
70
|
-
if (own) pins = own;
|
|
71
|
-
|
|
72
|
-
return formatAnchor(anchorAt);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const entries: [string, JsonValue][] = [];
|
|
76
|
-
|
|
77
|
-
for (const [key, child] of Object.entries(value)) {
|
|
78
|
-
if (key === "status" && value.type === "section") continue;
|
|
79
|
-
|
|
80
|
-
if (key === "markdown" && isStringValue(child)) {
|
|
81
|
-
entries.push([key, respellLinks(child)]);
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const attachment = ATTACHMENT_KEYS.includes(key);
|
|
86
|
-
|
|
87
|
-
if (place.anchors && ANCHOR_KEYS.includes(key)) {
|
|
88
|
-
const anchors = upgradeNode(child, { attachment, anchors: false });
|
|
89
|
-
|
|
90
|
-
entries.push([
|
|
91
|
-
key,
|
|
92
|
-
Array.isArray(anchors) ? anchors.map(convert) : convert(anchors),
|
|
93
|
-
]);
|
|
94
|
-
} else
|
|
95
|
-
entries.push([
|
|
96
|
-
key,
|
|
97
|
-
upgradeNode(child, {
|
|
98
|
-
attachment,
|
|
99
|
-
anchors: place.anchors,
|
|
100
|
-
blockPins: inherited,
|
|
101
|
-
}),
|
|
102
|
-
]);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const upgraded: JsonObject = Object.fromEntries(entries);
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
pins !== undefined &&
|
|
109
|
-
value.kind !== "ranges" &&
|
|
110
|
-
JSON.stringify(pins) !== JSON.stringify(place.blockPins)
|
|
111
|
-
)
|
|
112
|
-
upgraded.pins = pins;
|
|
113
|
-
|
|
114
|
-
return upgraded;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** A stored document as the store reads it: retired forms upgraded, and
|
|
118
|
-
* lens blocks lifted out to the snapshot's own lenses. */
|
|
119
|
-
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Decoder boundary for stored documents in retired formats.
|
|
120
|
-
export function readStoredDocument(input: unknown): LiftedLenses {
|
|
121
|
-
return liftFileLenses(migrateStoredDocument(input));
|
|
15
|
+
return anchorStrings(dropSectionStatus(migrateDiffSelections(input)));
|
|
122
16
|
}
|
|
123
17
|
|
|
124
18
|
/** Lenses saved before ranges were anchor strings. */
|
|
@@ -188,6 +82,21 @@ export function anchorStrings(
|
|
|
188
82
|
return migrated;
|
|
189
83
|
}
|
|
190
84
|
|
|
85
|
+
/** Sections once carried an optional `status` (pending, in_progress or
|
|
86
|
+
* complete). The authoring lease is now the only signal of work in progress,
|
|
87
|
+
* so versions saved before it was retired drop the field when read. */
|
|
88
|
+
function dropSectionStatus(value: JsonValue): JsonValue {
|
|
89
|
+
if (Array.isArray(value)) return value.map(dropSectionStatus);
|
|
90
|
+
|
|
91
|
+
if (!isJsonObject(value)) return value;
|
|
92
|
+
|
|
93
|
+
return Object.fromEntries(
|
|
94
|
+
Object.entries(value)
|
|
95
|
+
.filter(([key]) => !(value.type === "section" && key === "status"))
|
|
96
|
+
.map(([key, child]) => [key, dropSectionStatus(child)]),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
191
100
|
/** A saved `file_lens` block, from before lenses left the document. */
|
|
192
101
|
const legacyFileLensSchema = z.object({
|
|
193
102
|
type: z.literal("file_lens"),
|
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
import { z } from "zod";
|
|
30
30
|
|
|
31
31
|
import { askAgentIds } from "./ask/thread-state";
|
|
32
|
-
import { SELECTION_UNAVAILABLE_REASONS } from "./lens-selection";
|
|
33
32
|
import { containsFilePath, hasPossibleUserInfo } from "./telemetry-clean-text";
|
|
34
33
|
|
|
35
34
|
/** Property value validators: an enum of allowed strings, or a scalar type. */
|
|
@@ -71,13 +70,6 @@ const PEEK_VIA = [
|
|
|
71
70
|
|
|
72
71
|
export const PEEK_ROOT_KIND = ["range"] as const;
|
|
73
72
|
|
|
74
|
-
/** `unresolved`: progress finished without this selection; `error`: progress failed. */
|
|
75
|
-
export const PEEK_FAILURE_REASON = [
|
|
76
|
-
...SELECTION_UNAVAILABLE_REASONS,
|
|
77
|
-
"error",
|
|
78
|
-
"unresolved",
|
|
79
|
-
] as const;
|
|
80
|
-
|
|
81
73
|
export const LSP_FEATURE = [
|
|
82
74
|
"hover",
|
|
83
75
|
"goto_definition",
|
|
@@ -145,6 +137,7 @@ export const SETTING_NAME = [
|
|
|
145
137
|
"telemetry_enabled",
|
|
146
138
|
"keymap",
|
|
147
139
|
"software_map_enabled",
|
|
140
|
+
"scratchpad_enabled",
|
|
148
141
|
"diffr_config",
|
|
149
142
|
"theme",
|
|
150
143
|
"ctrl_tab",
|
|
@@ -156,8 +149,12 @@ export const SETTING_NAME = [
|
|
|
156
149
|
|
|
157
150
|
export const REVIEW_OPENED_VIA = ["home", "cli", "other"] as const;
|
|
158
151
|
|
|
159
|
-
/** Where a review's source comes from: its target's kind. */
|
|
160
|
-
export const SESSION_SOURCE_KIND = [
|
|
152
|
+
/** Where a review's source comes from: its target's kind, or the one scratchpad. */
|
|
153
|
+
export const SESSION_SOURCE_KIND = [
|
|
154
|
+
"worktree",
|
|
155
|
+
"commits",
|
|
156
|
+
"scratchpad",
|
|
157
|
+
] as const;
|
|
161
158
|
|
|
162
159
|
/**
|
|
163
160
|
* The authoring agent a review session is attributed to. This is the one
|
|
@@ -249,7 +246,6 @@ export const INSTALL_LOCATION = [
|
|
|
249
246
|
|
|
250
247
|
export const DIFF_OPENED_VIA = [
|
|
251
248
|
"topbar",
|
|
252
|
-
"sidebar",
|
|
253
249
|
"lens",
|
|
254
250
|
"locate",
|
|
255
251
|
"file_picker",
|
|
@@ -258,15 +254,6 @@ export const DIFF_OPENED_VIA = [
|
|
|
258
254
|
/** Which diff opened: a commit's, or a file diff drawn plain or structural. */
|
|
259
255
|
export const DIFF_OPENED_KIND = ["commit", "structural"] as const;
|
|
260
256
|
|
|
261
|
-
export const DIFF_FAILED_REASON = [
|
|
262
|
-
"missing_binary",
|
|
263
|
-
"process_exit",
|
|
264
|
-
"stream_ended",
|
|
265
|
-
"aborted",
|
|
266
|
-
"file_error",
|
|
267
|
-
"other",
|
|
268
|
-
] as const;
|
|
269
|
-
|
|
270
257
|
/** GitHub Enterprise is any GitHub host but github.com. */
|
|
271
258
|
export const PR_PROVIDER = [
|
|
272
259
|
"github",
|
|
@@ -290,9 +277,7 @@ export const ONBOARDING_STEP = ["welcome", "install", "run"] as const;
|
|
|
290
277
|
|
|
291
278
|
export const RUN_MODE = ["draw", "copy"] as const;
|
|
292
279
|
|
|
293
|
-
export const NEW_WHITEBOARD_VIA = ["button", "command"
|
|
294
|
-
|
|
295
|
-
export const NEW_WHITEBOARD_MODE = [...RUN_MODE, "empty"] as const;
|
|
280
|
+
export const NEW_WHITEBOARD_VIA = ["button", "command"] as const;
|
|
296
281
|
|
|
297
282
|
export const FIND_SURFACE = ["review", "diff"] as const;
|
|
298
283
|
|
|
@@ -609,10 +594,7 @@ export const UI_TELEMETRY_EVENTS = {
|
|
|
609
594
|
event: "review_diff_opened",
|
|
610
595
|
properties: { kind: DIFF_OPENED_KIND, via: DIFF_OPENED_VIA },
|
|
611
596
|
},
|
|
612
|
-
|
|
613
|
-
event: "review_diff_failed",
|
|
614
|
-
properties: { reason: DIFF_FAILED_REASON, files: "number" },
|
|
615
|
-
},
|
|
597
|
+
scratchpad_opened: { event: "review_scratchpad_opened", properties: {} },
|
|
616
598
|
discord_clicked: {
|
|
617
599
|
event: "review_discord_clicked",
|
|
618
600
|
properties: { via: DISCORD_VIA },
|
|
@@ -631,7 +613,7 @@ export const UI_TELEMETRY_EVENTS = {
|
|
|
631
613
|
},
|
|
632
614
|
peek_resolve_failed: {
|
|
633
615
|
event: "review_peek_resolve_failed",
|
|
634
|
-
properties: { root_kind: PEEK_ROOT_KIND
|
|
616
|
+
properties: { root_kind: PEEK_ROOT_KIND },
|
|
635
617
|
},
|
|
636
618
|
review_deleted: {
|
|
637
619
|
event: "review_review_deleted",
|
|
@@ -670,11 +652,7 @@ export const UI_TELEMETRY_EVENTS = {
|
|
|
670
652
|
},
|
|
671
653
|
new_whiteboard_started: {
|
|
672
654
|
event: "review_new_whiteboard_started",
|
|
673
|
-
properties: {
|
|
674
|
-
mode: NEW_WHITEBOARD_MODE,
|
|
675
|
-
embedded: "boolean",
|
|
676
|
-
agent: askAgentIds,
|
|
677
|
-
},
|
|
655
|
+
properties: { mode: RUN_MODE, embedded: "boolean", agent: askAgentIds },
|
|
678
656
|
},
|
|
679
657
|
remote_host_installed: {
|
|
680
658
|
event: "review_remote_host_installed",
|