@narumitw/pi-sync 0.50.2 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/chunks/{chunk-6LP3MCR2.ts → chunk-2JZFHNLM.ts} +3 -5
- package/dist/chunks/chunk-2JZFHNLM.ts.map +7 -0
- package/dist/chunks/{chunk-LCRI3ABW.ts → chunk-2RZSQWBH.ts} +8 -19
- package/dist/chunks/chunk-2RZSQWBH.ts.map +7 -0
- package/dist/chunks/{chunk-IJ6FJFA5.ts → chunk-4AIHXRKE.ts} +11 -17
- package/dist/chunks/chunk-4AIHXRKE.ts.map +7 -0
- package/dist/chunks/{chunk-C7XY5NF5.ts → chunk-4MPIH46N.ts} +29 -51
- package/dist/chunks/chunk-4MPIH46N.ts.map +7 -0
- package/dist/chunks/{chunk-5B3FXBLE.ts → chunk-6546TS54.ts} +16 -20
- package/dist/chunks/chunk-6546TS54.ts.map +7 -0
- package/dist/chunks/{chunk-Q2TUM5X6.ts → chunk-6NT3YFGY.ts} +2 -2
- package/dist/chunks/chunk-6NT3YFGY.ts.map +7 -0
- package/dist/chunks/{chunk-2B2ELUF5.ts → chunk-7D5SF7P3.ts} +1 -1
- package/dist/chunks/chunk-7D5SF7P3.ts.map +7 -0
- package/dist/chunks/{chunk-4CHTZMEO.ts → chunk-7KHKWL3Q.ts} +37 -55
- package/dist/chunks/chunk-7KHKWL3Q.ts.map +7 -0
- package/dist/chunks/{chunk-ZI5MXMEX.ts → chunk-BVVP33U5.ts} +2 -4
- package/dist/chunks/chunk-BVVP33U5.ts.map +7 -0
- package/dist/chunks/{chunk-SZWBGPID.ts → chunk-DM4X4FIA.ts} +8 -13
- package/dist/chunks/chunk-DM4X4FIA.ts.map +7 -0
- package/dist/chunks/{chunk-SL64RLXR.ts → chunk-DZUSTGQV.ts} +8 -16
- package/dist/chunks/chunk-DZUSTGQV.ts.map +7 -0
- package/dist/chunks/{chunk-G4RNQQZS.ts → chunk-E42GXD6D.ts} +13 -19
- package/dist/chunks/chunk-E42GXD6D.ts.map +7 -0
- package/dist/chunks/{chunk-Z4DQF6L3.ts → chunk-ECYCNZOE.ts} +1 -1
- package/dist/chunks/chunk-ECYCNZOE.ts.map +7 -0
- package/dist/chunks/{chunk-OEX4ABY6.ts → chunk-EEJPYTWE.ts} +11 -21
- package/dist/chunks/chunk-EEJPYTWE.ts.map +7 -0
- package/dist/chunks/{chunk-CPEHGXJV.ts → chunk-G4RUXRWZ.ts} +3 -5
- package/dist/chunks/chunk-G4RUXRWZ.ts.map +7 -0
- package/dist/chunks/{chunk-T2B5IU74.ts → chunk-HGJH56KR.ts} +4 -4
- package/dist/chunks/chunk-HGJH56KR.ts.map +7 -0
- package/dist/chunks/{chunk-JAAKLL6M.ts → chunk-IVXEDPAD.ts} +10 -34
- package/dist/chunks/chunk-IVXEDPAD.ts.map +7 -0
- package/dist/chunks/chunk-K3RIEL6J.ts +20 -0
- package/dist/chunks/chunk-K3RIEL6J.ts.map +7 -0
- package/dist/chunks/{chunk-LTBWGSBV.ts → chunk-MSSW6F3R.ts} +1 -1
- package/dist/chunks/chunk-MSSW6F3R.ts.map +7 -0
- package/dist/chunks/{chunk-T3HH3UEJ.ts → chunk-OVDTF5CH.ts} +4 -10
- package/dist/chunks/chunk-OVDTF5CH.ts.map +7 -0
- package/dist/chunks/{chunk-NJFUIVYL.ts → chunk-PWMWOJP4.ts} +1 -1
- package/dist/chunks/chunk-PWMWOJP4.ts.map +7 -0
- package/dist/chunks/{chunk-HUDQATCL.ts → chunk-Q32CQWPM.ts} +6 -8
- package/dist/chunks/chunk-Q32CQWPM.ts.map +7 -0
- package/dist/chunks/{chunk-ETTRJDNP.ts → chunk-QLSN7AGX.ts} +6 -13
- package/dist/chunks/chunk-QLSN7AGX.ts.map +7 -0
- package/dist/chunks/{chunk-THMF4BVL.ts → chunk-SWUXA3MH.ts} +1 -1
- package/dist/chunks/chunk-SWUXA3MH.ts.map +7 -0
- package/dist/chunks/{chunk-TEN2ZBJV.ts → chunk-T2SJD3N7.ts} +2 -4
- package/dist/chunks/chunk-T2SJD3N7.ts.map +7 -0
- package/dist/chunks/{chunk-AUSDLD6F.ts → chunk-T56RNRZJ.ts} +10 -26
- package/dist/chunks/chunk-T56RNRZJ.ts.map +7 -0
- package/dist/chunks/{chunk-KW7YQEZN.ts → chunk-UA7RXOIT.ts} +48 -100
- package/dist/chunks/chunk-UA7RXOIT.ts.map +7 -0
- package/dist/chunks/{chunk-D5X27LCA.ts → chunk-UETBKTDI.ts} +1 -1
- package/dist/chunks/chunk-UETBKTDI.ts.map +7 -0
- package/dist/chunks/{chunk-CHH26D4F.ts → chunk-YFADF5ZQ.ts} +7 -6
- package/dist/chunks/chunk-YFADF5ZQ.ts.map +7 -0
- package/dist/chunks/{chunk-CCSN3F47.ts → chunk-YPXGU5OR.ts} +4 -4
- package/dist/chunks/chunk-YPXGU5OR.ts.map +7 -0
- package/dist/chunks/{file-selection-FTTKAOYX.ts → file-selection-RAVHRYFG.ts} +14 -26
- package/dist/chunks/file-selection-RAVHRYFG.ts.map +7 -0
- package/dist/chunks/{git-backend-GMVS4PD4.ts → git-backend-IAANDL3K.ts} +41 -104
- package/dist/chunks/git-backend-IAANDL3K.ts.map +7 -0
- package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +23 -0
- package/dist/chunks/{manager-ui-5KLKVMRC.ts → manager-ui-763NEKJY.ts} +124 -251
- package/dist/chunks/manager-ui-763NEKJY.ts.map +7 -0
- package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +22 -0
- package/dist/chunks/{s3-backend-66O4FGBH.ts → s3-backend-XGXUOPUN.ts} +23 -54
- package/dist/chunks/s3-backend-XGXUOPUN.ts.map +7 -0
- package/dist/chunks/{setup-switch-IS2PEZ3J.ts → setup-switch-5G2VNJHL.ts} +9 -9
- package/dist/chunks/{setup-wizard-IUB6ESEU.ts → setup-wizard-YIEWGX32.ts} +12 -12
- package/dist/chunks/setup-wizard-YIEWGX32.ts.map +7 -0
- package/dist/chunks/{snapshot-HKEWCVK6.ts → snapshot-JL5DFSXI.ts} +5 -5
- package/dist/chunks/sync-inspection-P3ENJLNF.ts +20 -0
- package/dist/chunks/{sync-operations-LBX3GKCM.ts → sync-operations-AR76EB6C.ts} +85 -201
- package/dist/chunks/sync-operations-AR76EB6C.ts.map +7 -0
- package/dist/chunks/{sync-state-Q2FTXTW6.ts → sync-state-VHJUZ4YG.ts} +6 -6
- package/dist/chunks/{webdav-backend-ECIKD7SO.ts → webdav-backend-3EGUONAT.ts} +20 -51
- package/dist/chunks/webdav-backend-3EGUONAT.ts.map +7 -0
- package/dist/index.ts +49 -74
- package/dist/index.ts.map +3 -3
- package/docs/settings.md +9 -0
- package/package.json +61 -62
- package/src/backends/backend-factory.ts +15 -15
- package/src/backends/backend-identity.ts +31 -31
- package/src/backends/backend-types.ts +42 -42
- package/src/backends/git/git-backend.ts +714 -788
- package/src/backends/git/git-config.ts +104 -111
- package/src/backends/git/git-runner.ts +230 -247
- package/src/backends/git/git-storage.ts +228 -245
- package/src/backends/s3/s3-backend.ts +408 -441
- package/src/backends/s3/s3-client.ts +288 -333
- package/src/backends/sync-backend.ts +65 -65
- package/src/backends/webdav/webdav-backend.ts +542 -589
- package/src/backends/webdav/webdav-client.ts +292 -316
- package/src/backends/webdav/webdav-config.ts +57 -68
- package/src/commands/command-execution.ts +270 -289
- package/src/commands/command-handler.ts +160 -163
- package/src/commands/command-types.ts +13 -13
- package/src/commands/command.ts +190 -196
- package/src/commands/setup-completions.ts +1 -1
- package/src/paths.ts +47 -47
- package/src/settings/config-errors.ts +4 -5
- package/src/settings/config-file.ts +473 -506
- package/src/settings/config.ts +171 -174
- package/src/settings/settings-management.ts +191 -216
- package/src/settings/settings-store.ts +63 -77
- package/src/settings/settings-types.ts +78 -75
- package/src/settings/settings-validation.ts +328 -364
- package/src/snapshot/session-paths.ts +41 -55
- package/src/snapshot/snapshot-apply.ts +197 -229
- package/src/snapshot/snapshot-codec.ts +29 -31
- package/src/snapshot/snapshot-paths.ts +4 -6
- package/src/snapshot/snapshot-transaction.ts +161 -170
- package/src/snapshot/snapshot-types.ts +24 -24
- package/src/snapshot/snapshot.ts +310 -343
- package/src/state/json-file.ts +11 -11
- package/src/state/lock.ts +226 -242
- package/src/state/lockfile-fs.ts +20 -20
- package/src/state/operation-availability.ts +36 -41
- package/src/state/state-directory.ts +171 -173
- package/src/state/state-types.ts +15 -15
- package/src/state/sync-state-store.ts +23 -23
- package/src/sync/automatic-sync.ts +75 -80
- package/src/sync/remote-snapshot.ts +52 -60
- package/src/sync/setup-switch.ts +99 -114
- package/src/sync/signals.ts +3 -5
- package/src/sync/startup-check.ts +97 -111
- package/src/sync/sync-decision.ts +76 -80
- package/src/sync/sync-error-guidance.ts +41 -48
- package/src/sync/sync-errors.ts +10 -10
- package/src/sync/sync-inspection.ts +29 -33
- package/src/sync/sync-loaders.ts +28 -36
- package/src/sync/sync-mutations.ts +510 -622
- package/src/sync/sync-operations.ts +7 -7
- package/src/sync/sync-policy.ts +296 -326
- package/src/sync/sync-queries.ts +175 -194
- package/src/sync/sync-state.ts +74 -100
- package/src/sync-extension.ts +89 -101
- package/src/sync.ts +26 -26
- package/src/types.ts +37 -37
- package/src/ui/automatic-sync-summary.ts +1 -3
- package/src/ui/cancellable-operation.ts +92 -112
- package/src/ui/file-selection.ts +250 -274
- package/src/ui/manager-attention.ts +51 -63
- package/src/ui/manager-recovery.ts +48 -57
- package/src/ui/manager-result-dispatcher.ts +62 -76
- package/src/ui/manager-state.ts +200 -232
- package/src/ui/manager-ui.ts +264 -362
- package/src/ui/remote-selection-ui.ts +547 -588
- package/src/ui/secret-input.ts +198 -214
- package/src/ui/settings-ui.ts +175 -172
- package/src/ui/setup/git-ui.ts +232 -261
- package/src/ui/setup/s3-credentials-ui.ts +58 -65
- package/src/ui/setup/s3-ui.ts +171 -195
- package/src/ui/setup/setup-actions.ts +91 -101
- package/src/ui/setup/setup-location-ui.ts +121 -137
- package/src/ui/setup/setup-name-ui.ts +27 -27
- package/src/ui/setup/setup-prompts.ts +65 -65
- package/src/ui/setup/setup-review.ts +58 -59
- package/src/ui/setup/setup-switcher.ts +156 -160
- package/src/ui/setup/setup-wizard.ts +28 -28
- package/src/ui/setup/text-input.ts +54 -58
- package/src/ui/setup/webdav-ui.ts +246 -293
- package/src/ui/storage-connections-ui.ts +317 -369
- package/src/ui/sync-attention.ts +148 -164
- package/src/ui/sync-format.ts +110 -121
- package/src/ui/sync-resolution-ui.ts +147 -153
- package/src/ui/sync-setups-ui.ts +218 -238
- package/src/ui/sync-status.ts +17 -0
- package/src/ui/terminal-text.ts +2 -2
- package/dist/chunks/chunk-2B2ELUF5.ts.map +0 -7
- package/dist/chunks/chunk-4CHTZMEO.ts.map +0 -7
- package/dist/chunks/chunk-5B3FXBLE.ts.map +0 -7
- package/dist/chunks/chunk-6LP3MCR2.ts.map +0 -7
- package/dist/chunks/chunk-AUSDLD6F.ts.map +0 -7
- package/dist/chunks/chunk-C7XY5NF5.ts.map +0 -7
- package/dist/chunks/chunk-CCSN3F47.ts.map +0 -7
- package/dist/chunks/chunk-CHH26D4F.ts.map +0 -7
- package/dist/chunks/chunk-CPEHGXJV.ts.map +0 -7
- package/dist/chunks/chunk-D5X27LCA.ts.map +0 -7
- package/dist/chunks/chunk-ETTRJDNP.ts.map +0 -7
- package/dist/chunks/chunk-G4RNQQZS.ts.map +0 -7
- package/dist/chunks/chunk-HUDQATCL.ts.map +0 -7
- package/dist/chunks/chunk-IJ6FJFA5.ts.map +0 -7
- package/dist/chunks/chunk-JAAKLL6M.ts.map +0 -7
- package/dist/chunks/chunk-KW7YQEZN.ts.map +0 -7
- package/dist/chunks/chunk-LCRI3ABW.ts.map +0 -7
- package/dist/chunks/chunk-LTBWGSBV.ts.map +0 -7
- package/dist/chunks/chunk-NJFUIVYL.ts.map +0 -7
- package/dist/chunks/chunk-OEX4ABY6.ts.map +0 -7
- package/dist/chunks/chunk-Q2TUM5X6.ts.map +0 -7
- package/dist/chunks/chunk-SL64RLXR.ts.map +0 -7
- package/dist/chunks/chunk-SZWBGPID.ts.map +0 -7
- package/dist/chunks/chunk-T2B5IU74.ts.map +0 -7
- package/dist/chunks/chunk-T3HH3UEJ.ts.map +0 -7
- package/dist/chunks/chunk-TEN2ZBJV.ts.map +0 -7
- package/dist/chunks/chunk-THMF4BVL.ts.map +0 -7
- package/dist/chunks/chunk-Z4DQF6L3.ts.map +0 -7
- package/dist/chunks/chunk-ZI5MXMEX.ts.map +0 -7
- package/dist/chunks/file-selection-FTTKAOYX.ts.map +0 -7
- package/dist/chunks/git-backend-GMVS4PD4.ts.map +0 -7
- package/dist/chunks/manager-result-dispatcher-FKIBM3GO.ts +0 -22
- package/dist/chunks/manager-ui-5KLKVMRC.ts.map +0 -7
- package/dist/chunks/remote-selection-ui-D7RBRJNL.ts +0 -21
- package/dist/chunks/s3-backend-66O4FGBH.ts.map +0 -7
- package/dist/chunks/setup-wizard-IUB6ESEU.ts.map +0 -7
- package/dist/chunks/sync-inspection-CST2KL65.ts +0 -20
- package/dist/chunks/sync-operations-LBX3GKCM.ts.map +0 -7
- package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +0 -7
- /package/dist/chunks/{manager-result-dispatcher-FKIBM3GO.ts.map → manager-result-dispatcher-OTUMQM26.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-D7RBRJNL.ts.map → remote-selection-ui-TZTJF32Y.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-IS2PEZ3J.ts.map → setup-switch-5G2VNJHL.ts.map} +0 -0
- /package/dist/chunks/{snapshot-HKEWCVK6.ts.map → snapshot-JL5DFSXI.ts.map} +0 -0
- /package/dist/chunks/{sync-inspection-CST2KL65.ts.map → sync-inspection-P3ENJLNF.ts.map} +0 -0
- /package/dist/chunks/{sync-state-Q2FTXTW6.ts.map → sync-state-VHJUZ4YG.ts.map} +0 -0
package/src/ui/file-selection.ts
CHANGED
|
@@ -6,10 +6,10 @@ import { localConfigPath } from "../settings/config-file.js";
|
|
|
6
6
|
import { updateSyncSetup } from "../settings/settings-management.js";
|
|
7
7
|
import { agentDir } from "../snapshot/session-paths.js";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
BUILT_IN_SYNC_ROOTS,
|
|
10
|
+
isSafeCustomIncludePath,
|
|
11
|
+
normalizeSyncInclude,
|
|
12
|
+
syncIncludeSelection,
|
|
13
13
|
} from "../sync/sync-policy.js";
|
|
14
14
|
|
|
15
15
|
const BUILT_IN_PREFIX = "builtin:";
|
|
@@ -18,310 +18,286 @@ const SESSIONS_ID = "sessions";
|
|
|
18
18
|
const ADD_CUSTOM_ID = "add-custom";
|
|
19
19
|
|
|
20
20
|
interface SelectionDraft {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
builtIns: Set<string>;
|
|
22
|
+
custom: Set<string>;
|
|
23
|
+
sessions: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export async function showFileSelection(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
sessions: selection.sessions,
|
|
38
|
-
};
|
|
39
|
-
const draft = cloneDraft(original);
|
|
40
|
-
const customCandidates = await listCustomCandidates(draft.custom);
|
|
26
|
+
export async function showFileSelection(ctx: ExtensionCommandContext, setupName?: string, signal?: AbortSignal) {
|
|
27
|
+
const config = await loadConfig(setupName);
|
|
28
|
+
if (signal?.aborted) return;
|
|
29
|
+
const selection = syncIncludeSelection(config.include);
|
|
30
|
+
const original: SelectionDraft = {
|
|
31
|
+
builtIns: new Set(selection.builtIns),
|
|
32
|
+
custom: new Set(selection.custom),
|
|
33
|
+
sessions: selection.sessions,
|
|
34
|
+
};
|
|
35
|
+
const draft = cloneDraft(original);
|
|
36
|
+
const customCandidates = await listCustomCandidates(draft.custom);
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
if (ctx.mode !== "tui") {
|
|
39
|
+
ctx.ui.notify(
|
|
40
|
+
[
|
|
41
|
+
`pi-sync included content for sync setup ${safeTerminalText(config.setupName)}:`,
|
|
42
|
+
`include: ${config.include.map(safeTerminalText).join(", ") || "none"}`,
|
|
43
|
+
`Edit sync.include in ${safeTerminalText(localConfigPath())}.`,
|
|
44
|
+
].join("\n"),
|
|
45
|
+
"info",
|
|
46
|
+
);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
50
|
+
while (!signal?.aborted) {
|
|
51
|
+
await showDraftEditor(ctx, config.setupName, draft, customCandidates, signal);
|
|
52
|
+
if (signal?.aborted || sameDraft(original, draft)) return;
|
|
53
|
+
const choice = await showDraftReview(ctx, original, draft, signal);
|
|
54
|
+
if (signal?.aborted) return;
|
|
55
|
+
if (choice === "Continue editing") continue;
|
|
56
|
+
if (choice !== "Save changes") {
|
|
57
|
+
ctx.ui.notify("Included-content changes discarded.", "info");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
if (!original.sessions && draft.sessions) {
|
|
62
|
+
const acknowledged = await ctx.ui.confirm(
|
|
63
|
+
"Include session conversations?",
|
|
64
|
+
"Session JSONL may contain prompts, tool output, file paths, images, and secrets. Continue only with storage you trust.",
|
|
65
|
+
{ signal },
|
|
66
|
+
);
|
|
67
|
+
if (signal?.aborted) return;
|
|
68
|
+
if (!acknowledged) {
|
|
69
|
+
ctx.ui.notify("Session inclusion was not saved.", "info");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const include = [
|
|
74
|
+
...BUILT_IN_SYNC_ROOTS.filter((candidate) => draft.builtIns.has(candidate)),
|
|
75
|
+
...draft.custom,
|
|
76
|
+
...(draft.sessions ? ["sessions"] : []),
|
|
77
|
+
];
|
|
78
|
+
if (signal?.aborted) return;
|
|
79
|
+
await updateSyncSetup(
|
|
80
|
+
config.setupName,
|
|
81
|
+
(setup) => ({
|
|
82
|
+
...setup,
|
|
83
|
+
sync: { ...setup.sync, include },
|
|
84
|
+
}),
|
|
85
|
+
{ expectedInclude: config.include, signal },
|
|
86
|
+
);
|
|
87
|
+
if (signal?.aborted) return;
|
|
88
|
+
ctx.ui.notify(
|
|
89
|
+
`Saved included content for sync setup “${safeTerminalText(config.setupName)}”. It applies to the next manual or automatic sync.`,
|
|
90
|
+
"info",
|
|
91
|
+
);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (signal?.aborted) return;
|
|
94
|
+
ctx.ui.notify(
|
|
95
|
+
`Could not save pi-sync file selection: ${safeTerminalText(error instanceof Error ? error.message : String(error))}`,
|
|
96
|
+
"error",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
105
101
|
}
|
|
106
102
|
|
|
107
103
|
async function showDraftEditor(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
104
|
+
ctx: ExtensionCommandContext,
|
|
105
|
+
setupName: string,
|
|
106
|
+
draft: SelectionDraft,
|
|
107
|
+
customCandidates: string[],
|
|
108
|
+
signal?: AbortSignal,
|
|
113
109
|
) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
signal,
|
|
208
|
-
isCurrent: () => !signal?.aborted,
|
|
209
|
-
});
|
|
110
|
+
const menu = defineMenu<undefined, "editor", "toggle" | "addCustom", ExtensionCommandContext>({
|
|
111
|
+
start: "editor",
|
|
112
|
+
screens: {
|
|
113
|
+
editor: () => ({
|
|
114
|
+
kind: "multiSelect",
|
|
115
|
+
title: `Included Content · ${safeTerminalText(setupName)}`,
|
|
116
|
+
lines: ["Draft only · leaving this screen opens Save, Discard, or Continue editing."],
|
|
117
|
+
viewportSize: 12,
|
|
118
|
+
items: [
|
|
119
|
+
...BUILT_IN_SYNC_ROOTS.map((relativePath) => ({
|
|
120
|
+
id: `${BUILT_IN_PREFIX}${relativePath}`,
|
|
121
|
+
label: relativePath,
|
|
122
|
+
description: relativePath.includes(".")
|
|
123
|
+
? `Sync the top-level ${relativePath} file when present.`
|
|
124
|
+
: `Recursively sync every safe file under ${relativePath}/.`,
|
|
125
|
+
selected: draft.builtIns.has(relativePath),
|
|
126
|
+
})),
|
|
127
|
+
...customCandidates.map((relativePath) => ({
|
|
128
|
+
id: `${CUSTOM_PREFIX}${relativePath}`,
|
|
129
|
+
label: safeTerminalText(relativePath),
|
|
130
|
+
description: "Additional safe agent-relative file or directory.",
|
|
131
|
+
selected: draft.custom.has(relativePath),
|
|
132
|
+
})),
|
|
133
|
+
{
|
|
134
|
+
id: SESSIONS_ID,
|
|
135
|
+
label: "sessions",
|
|
136
|
+
description:
|
|
137
|
+
"Session JSONL may contain prompts, tool output, paths, images, and secrets. Sync only to storage you trust.",
|
|
138
|
+
selected: draft.sessions,
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
action: "toggle",
|
|
142
|
+
actions: [
|
|
143
|
+
{
|
|
144
|
+
id: ADD_CUSTOM_ID,
|
|
145
|
+
label: "Add custom path…",
|
|
146
|
+
description: "Include an agent-relative file or directory even when it exists only remotely.",
|
|
147
|
+
action: "addCustom",
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
hint: "close",
|
|
151
|
+
doneLabel: "Review changes",
|
|
152
|
+
}),
|
|
153
|
+
},
|
|
154
|
+
actions: {
|
|
155
|
+
toggle: async ({ itemId, selected }) => {
|
|
156
|
+
updateDraft(draft, itemId, selected === true);
|
|
157
|
+
return { kind: "stay" };
|
|
158
|
+
},
|
|
159
|
+
addCustom: async ({ ctx: actionCtx, signal: actionSignal }) => {
|
|
160
|
+
const entered = await actionCtx.ui.input(
|
|
161
|
+
"Add included content",
|
|
162
|
+
"Agent-relative path, for example custom.toml or snippets",
|
|
163
|
+
{ signal: actionSignal },
|
|
164
|
+
);
|
|
165
|
+
if (actionSignal.aborted) return { kind: "stay" };
|
|
166
|
+
const requested = entered?.trim();
|
|
167
|
+
if (!requested) return { kind: "stay" };
|
|
168
|
+
const existing = customCandidates.find((candidate) => candidate.toLowerCase() === requested.toLowerCase());
|
|
169
|
+
const relativePath = existing ?? requested;
|
|
170
|
+
if (draft.custom.has(relativePath)) return { kind: "stay" };
|
|
171
|
+
try {
|
|
172
|
+
if (!isSafeCustomIncludePath(relativePath)) {
|
|
173
|
+
throw new Error("Enter a safe agent-relative file or directory path.");
|
|
174
|
+
}
|
|
175
|
+
normalizeSyncInclude([
|
|
176
|
+
...draft.builtIns,
|
|
177
|
+
...draft.custom,
|
|
178
|
+
...(draft.sessions ? ["sessions"] : []),
|
|
179
|
+
relativePath,
|
|
180
|
+
]);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (actionSignal.aborted) return { kind: "stay" };
|
|
183
|
+
actionCtx.ui.notify(
|
|
184
|
+
`Could not add included content: ${safeTerminalText(error instanceof Error ? error.message : String(error))}`,
|
|
185
|
+
"error",
|
|
186
|
+
);
|
|
187
|
+
return { kind: "stay" };
|
|
188
|
+
}
|
|
189
|
+
if (!existing) {
|
|
190
|
+
customCandidates.push(relativePath);
|
|
191
|
+
customCandidates.sort((left, right) => left.localeCompare(right));
|
|
192
|
+
}
|
|
193
|
+
draft.custom.add(relativePath);
|
|
194
|
+
return { kind: "stay" };
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
await runMenu(ctx, menu, {
|
|
199
|
+
getState: () => undefined,
|
|
200
|
+
signal,
|
|
201
|
+
isCurrent: () => !signal?.aborted,
|
|
202
|
+
});
|
|
210
203
|
}
|
|
211
204
|
|
|
212
205
|
async function showDraftReview(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
206
|
+
ctx: ExtensionCommandContext,
|
|
207
|
+
original: SelectionDraft,
|
|
208
|
+
draft: SelectionDraft,
|
|
209
|
+
signal?: AbortSignal,
|
|
217
210
|
) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
getState: () => undefined,
|
|
248
|
-
signal,
|
|
249
|
-
isCurrent: () => !signal?.aborted,
|
|
250
|
-
});
|
|
251
|
-
return choice;
|
|
211
|
+
let choice: "Save changes" | "Discard changes" | "Continue editing" | undefined;
|
|
212
|
+
const menu = defineMenu<undefined, "review", "choose", ExtensionCommandContext>({
|
|
213
|
+
start: "review",
|
|
214
|
+
screens: {
|
|
215
|
+
review: () => ({
|
|
216
|
+
kind: "actions",
|
|
217
|
+
title: "Review included-content changes",
|
|
218
|
+
lines: formatDraftPreview(original, draft).split("\n").slice(2),
|
|
219
|
+
items: [
|
|
220
|
+
{ id: "save", label: "Save changes", action: "choose" },
|
|
221
|
+
{ id: "discard", label: "Discard changes", action: "choose" },
|
|
222
|
+
{ id: "continue", label: "Continue editing", action: "choose" },
|
|
223
|
+
],
|
|
224
|
+
hint: "close",
|
|
225
|
+
}),
|
|
226
|
+
},
|
|
227
|
+
actions: {
|
|
228
|
+
choose: async ({ itemId }) => {
|
|
229
|
+
choice = itemId === "save" ? "Save changes" : itemId === "continue" ? "Continue editing" : "Discard changes";
|
|
230
|
+
return { kind: "close" };
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
await runMenu(ctx, menu, {
|
|
235
|
+
getState: () => undefined,
|
|
236
|
+
signal,
|
|
237
|
+
isCurrent: () => !signal?.aborted,
|
|
238
|
+
});
|
|
239
|
+
return choice;
|
|
252
240
|
}
|
|
253
241
|
|
|
254
242
|
function updateDraft(draft: SelectionDraft, id: string, included: boolean) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
throw new Error(`Unknown file selection: ${id}`);
|
|
243
|
+
if (id.startsWith(BUILT_IN_PREFIX)) return updateSet(draft.builtIns, id.slice(BUILT_IN_PREFIX.length), included);
|
|
244
|
+
if (id.startsWith(CUSTOM_PREFIX)) return updateSet(draft.custom, id.slice(CUSTOM_PREFIX.length), included);
|
|
245
|
+
if (id === SESSIONS_ID) {
|
|
246
|
+
draft.sessions = included;
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
throw new Error(`Unknown file selection: ${id}`);
|
|
264
250
|
}
|
|
265
251
|
|
|
266
252
|
function updateSet(set: Set<string>, value: string, included: boolean) {
|
|
267
|
-
|
|
268
|
-
|
|
253
|
+
if (included) set.add(value);
|
|
254
|
+
else set.delete(value);
|
|
269
255
|
}
|
|
270
256
|
|
|
271
257
|
function formatDraftPreview(original: SelectionDraft, draft: SelectionDraft) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (before !== after) lines.push(`${after ? "Include" : "Exclude"}: ${safeTerminalText(item)}`);
|
|
282
|
-
}
|
|
283
|
-
if (original.sessions !== draft.sessions)
|
|
284
|
-
lines.push(`${draft.sessions ? "Include" : "Exclude"}: sessions`);
|
|
285
|
-
lines.push("", "Saving does not start a network sync.");
|
|
286
|
-
return lines.join("\n");
|
|
258
|
+
const lines = ["Review included-content changes", ""];
|
|
259
|
+
for (const item of new Set([...original.builtIns, ...draft.builtIns, ...original.custom, ...draft.custom])) {
|
|
260
|
+
const before = original.builtIns.has(item) || original.custom.has(item);
|
|
261
|
+
const after = draft.builtIns.has(item) || draft.custom.has(item);
|
|
262
|
+
if (before !== after) lines.push(`${after ? "Include" : "Exclude"}: ${safeTerminalText(item)}`);
|
|
263
|
+
}
|
|
264
|
+
if (original.sessions !== draft.sessions) lines.push(`${draft.sessions ? "Include" : "Exclude"}: sessions`);
|
|
265
|
+
lines.push("", "Saving does not start a network sync.");
|
|
266
|
+
return lines.join("\n");
|
|
287
267
|
}
|
|
288
268
|
|
|
289
269
|
async function listCustomCandidates(configured: Set<string>) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
return [...candidates.values()].sort((left, right) => left.localeCompare(right));
|
|
270
|
+
const candidates = new Map([...configured].map((item) => [item.toLowerCase(), item]));
|
|
271
|
+
try {
|
|
272
|
+
for (const entry of await fs.readdir(agentDir(), { withFileTypes: true })) {
|
|
273
|
+
if ((!entry.isFile() && !entry.isDirectory()) || !isSafeCustomIncludePath(entry.name)) continue;
|
|
274
|
+
if (!candidates.has(entry.name.toLowerCase())) candidates.set(entry.name.toLowerCase(), entry.name);
|
|
275
|
+
}
|
|
276
|
+
} catch (error) {
|
|
277
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
278
|
+
}
|
|
279
|
+
return [...candidates.values()].sort((left, right) => left.localeCompare(right));
|
|
302
280
|
}
|
|
303
281
|
|
|
304
282
|
function cloneDraft(value: SelectionDraft): SelectionDraft {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
283
|
+
return {
|
|
284
|
+
builtIns: new Set(value.builtIns),
|
|
285
|
+
custom: new Set(value.custom),
|
|
286
|
+
sessions: value.sessions,
|
|
287
|
+
};
|
|
310
288
|
}
|
|
311
289
|
|
|
312
290
|
function sameDraft(left: SelectionDraft, right: SelectionDraft) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
sameSet(left.custom, right.custom)
|
|
317
|
-
);
|
|
291
|
+
return (
|
|
292
|
+
left.sessions === right.sessions && sameSet(left.builtIns, right.builtIns) && sameSet(left.custom, right.custom)
|
|
293
|
+
);
|
|
318
294
|
}
|
|
319
295
|
|
|
320
296
|
function sameSet(left: Set<string>, right: Set<string>) {
|
|
321
|
-
|
|
297
|
+
return left.size === right.size && [...left].every((item) => right.has(item));
|
|
322
298
|
}
|
|
323
299
|
|
|
324
300
|
function safeTerminalText(value: string) {
|
|
325
|
-
|
|
326
|
-
|
|
301
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.
|
|
302
|
+
return value.replace(/[\u0000-\u001f\u007f-\u009f]/gu, "?");
|
|
327
303
|
}
|