@narumitw/pi-sync 0.54.2 → 0.56.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 +40 -15
- package/dist/chunks/agent-candidate-3JN6DJKT.ts +26 -0
- package/dist/chunks/agent-plan-QCUJWBP4.ts +99 -0
- package/dist/chunks/agent-plan-QCUJWBP4.ts.map +7 -0
- package/dist/chunks/agent-runner-E2JU2VZ6.ts +182 -0
- package/dist/chunks/agent-runner-E2JU2VZ6.ts.map +7 -0
- package/dist/chunks/agent-store-A3F6IYWZ.ts +152 -0
- package/dist/chunks/agent-store-A3F6IYWZ.ts.map +7 -0
- package/dist/chunks/chunk-5Q5SSKNG.ts +128 -0
- package/dist/chunks/chunk-5Q5SSKNG.ts.map +7 -0
- package/dist/chunks/chunk-5XZOC7FD.ts +215 -0
- package/dist/chunks/chunk-5XZOC7FD.ts.map +7 -0
- package/dist/chunks/{chunk-IJ6VV6FN.ts → chunk-AAEEHR7H.ts} +31 -87
- package/dist/chunks/chunk-AAEEHR7H.ts.map +7 -0
- package/dist/chunks/{chunk-CHSIAC4N.ts → chunk-B4QZBDBT.ts} +2 -2
- package/dist/chunks/{chunk-CL7BZRRQ.ts → chunk-CRKTAPSL.ts} +8 -8
- package/dist/chunks/chunk-DT7M4VQS.ts +69 -0
- package/dist/chunks/chunk-DT7M4VQS.ts.map +7 -0
- package/dist/chunks/chunk-ED3TYIVW.ts +229 -0
- package/dist/chunks/chunk-ED3TYIVW.ts.map +7 -0
- package/dist/chunks/{chunk-VHXBCEVF.ts → chunk-EUMINX7L.ts} +6 -6
- package/dist/chunks/chunk-FQOSHGJ7.ts +215 -0
- package/dist/chunks/chunk-FQOSHGJ7.ts.map +7 -0
- package/dist/chunks/chunk-GNH73BJ5.ts +153 -0
- package/dist/chunks/chunk-GNH73BJ5.ts.map +7 -0
- package/dist/chunks/{chunk-UQ3ALBIB.ts → chunk-GP5DU62N.ts} +8 -9
- package/dist/chunks/chunk-GP5DU62N.ts.map +7 -0
- package/dist/chunks/{chunk-5RZPINUQ.ts → chunk-GSNPASKB.ts} +2 -2
- package/dist/chunks/{chunk-QE7NZZWT.ts → chunk-IWZH5KYS.ts} +23 -7
- package/dist/chunks/chunk-IWZH5KYS.ts.map +7 -0
- package/dist/chunks/{chunk-5ZWQ33XL.ts → chunk-JJTARUSO.ts} +15 -14
- package/dist/chunks/chunk-JJTARUSO.ts.map +7 -0
- package/dist/chunks/{chunk-2DI6PDZV.ts → chunk-NLWS55B6.ts} +15 -2
- package/dist/chunks/chunk-NLWS55B6.ts.map +7 -0
- package/dist/chunks/{chunk-SDZCTCYQ.ts → chunk-OEWQVWLC.ts} +4 -4
- package/dist/chunks/{chunk-MYPSMJHJ.ts → chunk-OTZRCKYP.ts} +380 -510
- package/dist/chunks/chunk-OTZRCKYP.ts.map +7 -0
- package/dist/chunks/{chunk-WFKUKVCP.ts → chunk-OWBAZA6J.ts} +24 -12
- package/dist/chunks/chunk-OWBAZA6J.ts.map +7 -0
- package/dist/chunks/chunk-PRUEIIK7.ts +24 -0
- package/dist/chunks/chunk-PRUEIIK7.ts.map +7 -0
- package/dist/chunks/chunk-QGK445K7.ts +21 -0
- package/dist/chunks/chunk-QGK445K7.ts.map +7 -0
- package/dist/chunks/{chunk-3B4Z2SKV.ts → chunk-QO333NM4.ts} +49 -21
- package/dist/chunks/chunk-QO333NM4.ts.map +7 -0
- package/dist/chunks/{chunk-4SBTAVFL.ts → chunk-QSHMOL3I.ts} +22 -24
- package/dist/chunks/chunk-QSHMOL3I.ts.map +7 -0
- package/dist/chunks/chunk-S2FMH5FT.ts +97 -0
- package/dist/chunks/chunk-S2FMH5FT.ts.map +7 -0
- package/dist/chunks/{chunk-BVVP33U5.ts → chunk-SWRVXVFD.ts} +12 -9
- package/dist/chunks/chunk-SWRVXVFD.ts.map +7 -0
- package/dist/chunks/{chunk-ZX2Q6THB.ts → chunk-UBV24CG4.ts} +4 -5
- package/dist/chunks/chunk-UBV24CG4.ts.map +7 -0
- package/dist/chunks/chunk-UOHR7LRO.ts +70 -0
- package/dist/chunks/chunk-UOHR7LRO.ts.map +7 -0
- package/dist/chunks/{chunk-4QVPTIZB.ts → chunk-UPNVIUBN.ts} +3 -3
- package/dist/chunks/chunk-UZPMDLTL.ts +54 -0
- package/dist/chunks/chunk-UZPMDLTL.ts.map +7 -0
- package/dist/chunks/chunk-VJN7KU4J.ts +350 -0
- package/dist/chunks/chunk-VJN7KU4J.ts.map +7 -0
- package/dist/chunks/chunk-YHT4YTYG.ts +31 -0
- package/dist/chunks/chunk-YHT4YTYG.ts.map +7 -0
- package/dist/chunks/{chunk-V6VZ5TLG.ts → chunk-YK52VDXG.ts} +6 -5
- package/dist/chunks/chunk-YK52VDXG.ts.map +7 -0
- package/dist/chunks/config-KGIYTTXQ.ts +37 -0
- package/dist/chunks/{conflict-artifacts-BB2FZMIE.ts → conflict-artifacts-FXFIIBCB.ts} +8 -7
- package/dist/chunks/{conflict-review-4PLVLMRO.ts → conflict-review-6WIEQLXW.ts} +31 -24
- package/dist/chunks/{conflict-review-4PLVLMRO.ts.map → conflict-review-6WIEQLXW.ts.map} +1 -1
- package/dist/chunks/continuous-record-LFRVXQBD.ts +30 -0
- package/dist/chunks/continuous-sync-AWAAOXK2.ts +545 -0
- package/dist/chunks/continuous-sync-AWAAOXK2.ts.map +7 -0
- package/dist/chunks/{file-selection-WZX7HAY2.ts → file-selection-3JJYOVNR.ts} +5 -5
- package/dist/chunks/{git-backend-FD7AVSK6.ts → git-backend-KYDZ673X.ts} +28 -32
- package/dist/chunks/git-backend-KYDZ673X.ts.map +7 -0
- package/dist/chunks/{manager-result-dispatcher-6MQKFCWB.ts → manager-result-dispatcher-AGBBVDDS.ts} +10 -9
- package/dist/chunks/{manager-ui-IURS4WXY.ts → manager-ui-KRAW3NE2.ts} +464 -164
- package/dist/chunks/manager-ui-KRAW3NE2.ts.map +7 -0
- package/dist/chunks/{merge-baseline-store-EVQAAML6.ts → merge-baseline-store-RFCAH7KL.ts} +7 -5
- package/dist/chunks/{remote-selection-ui-6LJJ4IMQ.ts → remote-selection-ui-5725JDNC.ts} +9 -8
- package/dist/chunks/remote-selection-ui-5725JDNC.ts.map +7 -0
- package/dist/chunks/{s3-backend-AQGV6LFT.ts → s3-backend-Y7WSCLJI.ts} +38 -38
- package/dist/chunks/s3-backend-Y7WSCLJI.ts.map +7 -0
- package/dist/chunks/{setup-switch-KTUWL6DE.ts → setup-switch-E7PHKI6J.ts} +7 -6
- package/dist/chunks/setup-switch-E7PHKI6J.ts.map +7 -0
- package/dist/chunks/{setup-wizard-GSDPDFC4.ts → setup-wizard-65DROCIJ.ts} +14 -13
- package/dist/chunks/setup-wizard-65DROCIJ.ts.map +7 -0
- package/dist/chunks/{sync-inspection-K45QVLHB.ts → sync-inspection-XFJOAMJD.ts} +9 -8
- package/dist/chunks/sync-inspection-XFJOAMJD.ts.map +7 -0
- package/dist/chunks/{sync-operations-47OUX76C.ts → sync-operations-HDUDT5PD.ts} +188 -73
- package/dist/chunks/sync-operations-HDUDT5PD.ts.map +7 -0
- package/dist/chunks/{webdav-backend-TBILVEQK.ts → webdav-backend-6K26F6LT.ts} +39 -48
- package/dist/chunks/webdav-backend-6K26F6LT.ts.map +7 -0
- package/dist/index.ts +246 -131
- package/dist/index.ts.map +3 -3
- package/docs/agent-mode-verification.md +99 -0
- package/docs/continuous-sync-verification.md +87 -0
- package/docs/settings.md +131 -11
- package/docs/tui-contract.md +61 -0
- package/package.json +3 -2
- package/src/backends/git/git-backend.ts +1 -4
- package/src/backends/git/git-runner.ts +1 -5
- package/src/backends/s3/s3-backend.ts +3 -25
- package/src/backends/s3/s3-client.ts +14 -2
- package/src/backends/snapshot-pointer.ts +27 -0
- package/src/backends/webdav/webdav-backend.ts +3 -29
- package/src/backends/webdav/webdav-client.ts +11 -7
- package/src/commands/command-execution.ts +7 -3
- package/src/commands/command-handler.ts +15 -3
- package/src/commands/command-types.ts +10 -0
- package/src/commands/command.ts +4 -1
- package/src/settings/config.ts +20 -10
- package/src/settings/settings-store.ts +1 -34
- package/src/settings/settings-types.ts +10 -0
- package/src/settings/settings-validation.ts +4 -1
- package/src/snapshot/snapshot-codec.ts +1 -5
- package/src/state/lock.ts +13 -10
- package/src/state/merge-baseline-store.ts +19 -1
- package/src/state/state-directory.ts +8 -1
- package/src/sync/agent-candidate.ts +251 -0
- package/src/sync/agent-plan.ts +87 -0
- package/src/sync/agent-policy.ts +13 -0
- package/src/sync/agent-request.ts +17 -0
- package/src/sync/agent-runner.ts +188 -0
- package/src/sync/agent-store.ts +151 -0
- package/src/sync/agent-sync.ts +91 -0
- package/src/sync/apply-review.ts +63 -0
- package/src/sync/automatic-sync.ts +46 -33
- package/src/sync/continuous-errors.ts +85 -0
- package/src/sync/continuous-lease.ts +221 -0
- package/src/sync/continuous-record.ts +179 -0
- package/src/sync/continuous-storage.ts +55 -0
- package/src/sync/continuous-sync.ts +516 -0
- package/src/sync/continuous-watch.ts +59 -0
- package/src/sync/merged-sync.ts +268 -112
- package/src/sync/remote-snapshot.ts +2 -3
- package/src/sync/settings-merge.ts +19 -3
- package/src/sync/setup-switch.ts +1 -5
- package/src/sync/startup-check.ts +51 -29
- package/src/sync/startup-timings.ts +90 -0
- package/src/sync/sync-inspection.ts +16 -5
- package/src/sync/sync-mutations.ts +55 -28
- package/src/sync/sync-queries.ts +19 -3
- package/src/sync/text-merge.ts +3 -3
- package/src/sync-extension.ts +75 -19
- package/src/ui/agent-mode-setting.ts +91 -0
- package/src/ui/manager-state.ts +12 -6
- package/src/ui/manager-ui.ts +57 -7
- package/src/ui/merge-review.ts +3 -3
- package/src/ui/merged-sync-summary.ts +2 -18
- package/src/ui/remote-selection-ui.ts +5 -5
- package/src/ui/settings-frame.ts +55 -0
- package/src/ui/settings-ui.ts +223 -43
- package/src/ui/setup/s3-credentials-ui.ts +1 -5
- package/src/ui/setup/setup-name-ui.ts +2 -5
- package/src/ui/setup/setup-prompts.ts +4 -3
- package/src/ui/setup/setup-switcher.ts +2 -0
- package/src/ui/sync-behavior.ts +15 -0
- package/src/ui/sync-format.ts +69 -38
- package/src/ui/sync-setups-ui.ts +3 -5
- package/src/ui/transfer-changes.ts +33 -0
- package/src/ui/transfer-review.ts +27 -0
- package/dist/chunks/chunk-2DI6PDZV.ts.map +0 -7
- package/dist/chunks/chunk-3B4Z2SKV.ts.map +0 -7
- package/dist/chunks/chunk-4SBTAVFL.ts.map +0 -7
- package/dist/chunks/chunk-5ZWQ33XL.ts.map +0 -7
- package/dist/chunks/chunk-BVVP33U5.ts.map +0 -7
- package/dist/chunks/chunk-IJ6VV6FN.ts.map +0 -7
- package/dist/chunks/chunk-MYPSMJHJ.ts.map +0 -7
- package/dist/chunks/chunk-ORQRHEEO.ts +0 -362
- package/dist/chunks/chunk-ORQRHEEO.ts.map +0 -7
- package/dist/chunks/chunk-QE7NZZWT.ts.map +0 -7
- package/dist/chunks/chunk-UQ3ALBIB.ts.map +0 -7
- package/dist/chunks/chunk-V6VZ5TLG.ts.map +0 -7
- package/dist/chunks/chunk-WFKUKVCP.ts.map +0 -7
- package/dist/chunks/chunk-ZX2Q6THB.ts.map +0 -7
- package/dist/chunks/git-backend-FD7AVSK6.ts.map +0 -7
- package/dist/chunks/manager-ui-IURS4WXY.ts.map +0 -7
- package/dist/chunks/s3-backend-AQGV6LFT.ts.map +0 -7
- package/dist/chunks/setup-wizard-GSDPDFC4.ts.map +0 -7
- package/dist/chunks/sync-operations-47OUX76C.ts.map +0 -7
- package/dist/chunks/webdav-backend-TBILVEQK.ts.map +0 -7
- /package/dist/chunks/{conflict-artifacts-BB2FZMIE.ts.map → agent-candidate-3JN6DJKT.ts.map} +0 -0
- /package/dist/chunks/{chunk-CHSIAC4N.ts.map → chunk-B4QZBDBT.ts.map} +0 -0
- /package/dist/chunks/{chunk-CL7BZRRQ.ts.map → chunk-CRKTAPSL.ts.map} +0 -0
- /package/dist/chunks/{chunk-VHXBCEVF.ts.map → chunk-EUMINX7L.ts.map} +0 -0
- /package/dist/chunks/{chunk-5RZPINUQ.ts.map → chunk-GSNPASKB.ts.map} +0 -0
- /package/dist/chunks/{chunk-SDZCTCYQ.ts.map → chunk-OEWQVWLC.ts.map} +0 -0
- /package/dist/chunks/{chunk-4QVPTIZB.ts.map → chunk-UPNVIUBN.ts.map} +0 -0
- /package/dist/chunks/{manager-result-dispatcher-6MQKFCWB.ts.map → config-KGIYTTXQ.ts.map} +0 -0
- /package/dist/chunks/{merge-baseline-store-EVQAAML6.ts.map → conflict-artifacts-FXFIIBCB.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-6LJJ4IMQ.ts.map → continuous-record-LFRVXQBD.ts.map} +0 -0
- /package/dist/chunks/{file-selection-WZX7HAY2.ts.map → file-selection-3JJYOVNR.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-KTUWL6DE.ts.map → manager-result-dispatcher-AGBBVDDS.ts.map} +0 -0
- /package/dist/chunks/{sync-inspection-K45QVLHB.ts.map → merge-baseline-store-RFCAH7KL.ts.map} +0 -0
package/src/ui/settings-ui.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { defineMenu, runMenu } from "@narumitw/pi-tui-kit";
|
|
2
|
+
import { defineMenu, type MenuSettingItem, runMenu } from "@narumitw/pi-tui-kit";
|
|
3
3
|
import { loadConfig } from "../settings/config.js";
|
|
4
4
|
import { localConfigPath } from "../settings/config-file.js";
|
|
5
5
|
import { updateSyncSetup } from "../settings/settings-management.js";
|
|
@@ -12,11 +12,18 @@ import {
|
|
|
12
12
|
setupSwitchActionLabel,
|
|
13
13
|
} from "../sync/setup-switch.js";
|
|
14
14
|
import { captureMutationOwner } from "../sync/sync-local.js";
|
|
15
|
+
import {
|
|
16
|
+
AGENT_MODE_DESCRIPTION,
|
|
17
|
+
CONTINUOUS_DESCRIPTION,
|
|
18
|
+
saveAgentMode,
|
|
19
|
+
saveContinuousMode,
|
|
20
|
+
} from "./agent-mode-setting.js";
|
|
15
21
|
import type { RunRoute } from "./cancellable-operation.js";
|
|
16
22
|
import { dispatchManagerResult } from "./manager-result-dispatcher.js";
|
|
23
|
+
import { boundedSettingsUI } from "./settings-frame.js";
|
|
17
24
|
import { showEditSyncSetupStorage, showSyncSetupManager } from "./setup/setup-actions.js";
|
|
18
|
-
import { AUTOMATIC_SYNC_DESCRIPTION } from "./setup/setup-prompts.js";
|
|
19
25
|
import { showStorageConnections } from "./storage-connections-ui.js";
|
|
26
|
+
import { AUTOMATIC_SYNC_DESCRIPTION, STARTUP_TRANSFER_DESCRIPTION, startupTransferSummary } from "./sync-behavior.js";
|
|
20
27
|
import { configureSyncStatus } from "./sync-status.js";
|
|
21
28
|
import { safeTerminalText } from "./terminal-text.js";
|
|
22
29
|
|
|
@@ -70,55 +77,74 @@ export async function showSyncSettings(
|
|
|
70
77
|
type Action =
|
|
71
78
|
| "automatic"
|
|
72
79
|
| "automatic-transfer"
|
|
80
|
+
| "agent-mode"
|
|
81
|
+
| "continuous"
|
|
73
82
|
| "merge-settings"
|
|
74
83
|
| "local-fields"
|
|
75
84
|
| "content-policy"
|
|
76
85
|
| "skip-secret-scan"
|
|
77
86
|
| "show-status"
|
|
87
|
+
| "debug"
|
|
78
88
|
| "on-switch"
|
|
79
89
|
| "include"
|
|
80
90
|
| "remote-include"
|
|
81
91
|
| "storage-location"
|
|
82
92
|
| "setups"
|
|
83
93
|
| "connections";
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
type Group = "content" | "behavior" | "storage" | "advanced" | "all";
|
|
95
|
+
type Screen = "categories" | Group;
|
|
96
|
+
const groups: Record<Group, { label: string; ids: readonly string[] }> = {
|
|
97
|
+
content: { label: "Sync content", ids: ["include", "remoteInclude", "localFields"] },
|
|
98
|
+
behavior: {
|
|
99
|
+
label: "Sync behavior",
|
|
100
|
+
ids: ["automatic", "automaticTransfer", "agentMode", "continuous", "onSwitch"],
|
|
101
|
+
},
|
|
102
|
+
storage: { label: "Storage & setups", ids: ["storageLocation", "setups", "connections"] },
|
|
103
|
+
advanced: { label: "Advanced", ids: ["mergeSettings", "contentPolicy", "skipSecretScan", "showStatus", "debug"] },
|
|
104
|
+
all: { label: "All settings (search)", ids: [] },
|
|
105
|
+
};
|
|
106
|
+
let saveStatus = "Simple choices save immediately. Back does not undo saved changes.";
|
|
107
|
+
// Keep Kit's existing settings adapter: native SettingsList has no async save/rollback
|
|
108
|
+
// contract or injected keybindings. Kit preserves these safety and accessibility behaviors.
|
|
109
|
+
// Categories use public navigation; All settings retains cross-category search.
|
|
110
|
+
const settingsScreen = (state: Awaited<ReturnType<typeof loadConfig>>, group: Group) =>
|
|
111
|
+
({
|
|
112
|
+
kind: "settings",
|
|
113
|
+
title: `Pi Sync Settings · ${groups[group].label}`,
|
|
114
|
+
lines: [
|
|
115
|
+
`Setup: ${safeTerminalText(state.setupName)} · Connection: ${safeTerminalText(state.connectionName)}`,
|
|
116
|
+
saveStatus,
|
|
117
|
+
],
|
|
118
|
+
items: (
|
|
119
|
+
[
|
|
96
120
|
{
|
|
97
121
|
id: "include",
|
|
98
|
-
label: "Included
|
|
99
|
-
description:
|
|
100
|
-
|
|
122
|
+
label: "Included paths",
|
|
123
|
+
description:
|
|
124
|
+
"This setup: choose paths in a draft, then review and Save or Discard. No transfer is started.",
|
|
125
|
+
currentValue: `${state.include.length} paths · Edit`,
|
|
101
126
|
action: "include",
|
|
102
127
|
},
|
|
103
128
|
{
|
|
104
129
|
id: "remoteInclude",
|
|
105
|
-
label: "Compare
|
|
106
|
-
description:
|
|
130
|
+
label: "Compare included paths",
|
|
131
|
+
description:
|
|
132
|
+
"This setup: compare included-path policies, not file contents. Review before saving either list; no files transfer unless you explicitly Continue.",
|
|
107
133
|
currentValue: "Review",
|
|
108
134
|
action: "remote-include",
|
|
109
135
|
},
|
|
110
136
|
{
|
|
111
137
|
id: "localFields",
|
|
112
|
-
label: "Machine-local
|
|
138
|
+
label: "Machine-local fields",
|
|
113
139
|
description:
|
|
114
|
-
"
|
|
140
|
+
"This setup: settings.json root fields excluded from future portable snapshots, not project overrides. Policy changes require reviewed directional migration; old history remains.",
|
|
115
141
|
currentValue: `${state.localFields?.length ?? 0} fields · Edit`,
|
|
116
142
|
action: "local-fields",
|
|
117
143
|
},
|
|
118
144
|
{
|
|
119
145
|
id: "storageLocation",
|
|
120
146
|
label: "Storage location",
|
|
121
|
-
description: "
|
|
147
|
+
description: "This setup: review and save bucket, branch, or path changes. Remote history is not moved.",
|
|
122
148
|
currentValue:
|
|
123
149
|
state.backend.type === "s3"
|
|
124
150
|
? `${safeTerminalText(state.backend.destination.bucket)} · ${safeTerminalText(state.backend.destination.prefix)}`
|
|
@@ -130,7 +156,8 @@ export async function showSyncSettings(
|
|
|
130
156
|
{
|
|
131
157
|
id: "setups",
|
|
132
158
|
label: "Manage sync setups",
|
|
133
|
-
description:
|
|
159
|
+
description:
|
|
160
|
+
"This device: add, switch, edit, or remove sync setups. Each edit identifies its owner before saving.",
|
|
134
161
|
currentValue: "Open",
|
|
135
162
|
action: "setups",
|
|
136
163
|
},
|
|
@@ -138,7 +165,7 @@ export async function showSyncSettings(
|
|
|
138
165
|
id: "connections",
|
|
139
166
|
label: "Manage storage connections",
|
|
140
167
|
description:
|
|
141
|
-
"
|
|
168
|
+
"Shared connections on this device: review affected setups before saving server or credential changes.",
|
|
142
169
|
currentValue: "Open",
|
|
143
170
|
action: "connections",
|
|
144
171
|
},
|
|
@@ -152,63 +179,114 @@ export async function showSyncSettings(
|
|
|
152
179
|
},
|
|
153
180
|
{
|
|
154
181
|
id: "automaticTransfer",
|
|
155
|
-
label: "
|
|
156
|
-
description:
|
|
157
|
-
"May upload, replace, or delete selected files once at idle startup in TUI/RPC. Requires a baseline and conditional/lease publication; never reloads resources. Turning off cancels pending work.",
|
|
182
|
+
label: "Startup transfer",
|
|
183
|
+
description: `${STARTUP_TRANSFER_DESCRIPTION} Saved preference and requirements: ${startupTransferSummary(state)}.`,
|
|
158
184
|
currentValue: state.automaticTransfer ? "On" : "Off",
|
|
159
185
|
values: ["On", "Off"],
|
|
160
186
|
action: "automatic-transfer",
|
|
161
187
|
},
|
|
188
|
+
{
|
|
189
|
+
id: "agentMode",
|
|
190
|
+
label: "Agent mode (experimental)",
|
|
191
|
+
description: AGENT_MODE_DESCRIPTION,
|
|
192
|
+
currentValue: state.agentMode ? "On" : "Off",
|
|
193
|
+
values: ["On", "Off"],
|
|
194
|
+
action: "agent-mode",
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: "continuous",
|
|
198
|
+
label: "Continuous sync (experimental)",
|
|
199
|
+
description: `${CONTINUOUS_DESCRIPTION}${state.continuous && !state.agentMode ? " Saved On, but paused until Agent mode is enabled." : ""}`,
|
|
200
|
+
currentValue: state.continuous ? "On" : "Off",
|
|
201
|
+
values: ["On", "Off"],
|
|
202
|
+
action: "continuous",
|
|
203
|
+
},
|
|
162
204
|
{
|
|
163
205
|
id: "onSwitch",
|
|
164
|
-
label: "
|
|
165
|
-
description:
|
|
206
|
+
label: "On setup switch",
|
|
207
|
+
description:
|
|
208
|
+
"All setups on this device: ask before a pull review, open it automatically, or switch without pulling. Applies to subsequent switches.",
|
|
166
209
|
currentValue: setupSwitchActionLabel(state.onSwitch),
|
|
167
210
|
values: SETUP_SWITCH_ACTION_OPTIONS.map(({ label }) => label),
|
|
168
211
|
action: "on-switch",
|
|
169
212
|
},
|
|
170
213
|
{
|
|
171
214
|
id: "mergeSettings",
|
|
172
|
-
label: "
|
|
215
|
+
label: "Merge settings fields",
|
|
173
216
|
description:
|
|
174
|
-
"
|
|
217
|
+
"This setup, experimental opt-in: merge independent global settings.json root fields with a verified private ancestor. Arrays and nested objects remain atomic. Applies to subsequent sync; no reload.",
|
|
175
218
|
currentValue: state.mergeSettings ? "On" : "Off",
|
|
176
219
|
values: ["On", "Off"],
|
|
177
220
|
action: "merge-settings",
|
|
178
221
|
},
|
|
179
222
|
{
|
|
180
223
|
id: "contentPolicy",
|
|
181
|
-
label: "Content / partial
|
|
224
|
+
label: "Content merge / partial progress",
|
|
182
225
|
description:
|
|
183
|
-
"
|
|
226
|
+
"This setup, experimental opt-in: review the version-5 upgrade before saving. Merge bounded text and prefix-only sessions; partial progress keeps withheld versions and baselines. Applies to subsequent sync; disabling does not clear recovery evidence.",
|
|
184
227
|
currentValue: state.mergeContent
|
|
185
228
|
? state.partialSync
|
|
186
|
-
? "
|
|
187
|
-
: "
|
|
229
|
+
? "Merge + partial"
|
|
230
|
+
: "Merge only"
|
|
188
231
|
: state.partialSync
|
|
189
232
|
? "Partial only"
|
|
190
233
|
: "Off",
|
|
191
|
-
values: ["Off", "
|
|
234
|
+
values: ["Off", "Merge only", "Merge + partial", "Partial only"],
|
|
192
235
|
action: "content-policy",
|
|
193
236
|
},
|
|
194
237
|
{
|
|
195
238
|
id: "skipSecretScan",
|
|
196
|
-
label: "Skip secret scan
|
|
197
|
-
description:
|
|
239
|
+
label: "Skip secret scan",
|
|
240
|
+
description:
|
|
241
|
+
"All setups on this device: skip possible-secret checks on subsequent pushes, including automatic pushes. Other safety checks remain. Diagnostics still scans.",
|
|
198
242
|
currentValue: state.skipSecretScan ? "On" : "Off",
|
|
199
243
|
values: ["On", "Off"],
|
|
200
244
|
action: "skip-secret-scan",
|
|
201
245
|
},
|
|
202
246
|
{
|
|
203
247
|
id: "showStatus",
|
|
204
|
-
label: "Show status
|
|
205
|
-
description:
|
|
248
|
+
label: "Show sync status",
|
|
249
|
+
description:
|
|
250
|
+
"All setups on this device: saved and applied immediately to Pi status. Review widgets and notifications remain available.",
|
|
206
251
|
currentValue: state.showStatus ? "On" : "Off",
|
|
207
252
|
values: ["On", "Off"],
|
|
208
253
|
action: "show-status",
|
|
209
254
|
},
|
|
255
|
+
{
|
|
256
|
+
id: "debug",
|
|
257
|
+
label: "Debug startup timings",
|
|
258
|
+
description:
|
|
259
|
+
"All setups on this device: show startup durations in TUI/RPC notifications. Applies next session start; no credentials or file contents.",
|
|
260
|
+
currentValue: state.debug ? "On" : "Off",
|
|
261
|
+
values: ["On", "Off"],
|
|
262
|
+
action: "debug",
|
|
263
|
+
},
|
|
264
|
+
] satisfies MenuSettingItem<Action>[]
|
|
265
|
+
).filter((item) => group === "all" || groups[group].ids.includes(item.id)),
|
|
266
|
+
}) as const;
|
|
267
|
+
const menu = defineMenu<Awaited<ReturnType<typeof loadConfig>>, Screen, Action, ExtensionCommandContext>({
|
|
268
|
+
start: "categories",
|
|
269
|
+
screens: {
|
|
270
|
+
categories: ({ state }) => ({
|
|
271
|
+
kind: "actions",
|
|
272
|
+
title: "Pi Sync Settings",
|
|
273
|
+
lines: [
|
|
274
|
+
`Setup: ${safeTerminalText(state.setupName)} · Connection: ${safeTerminalText(state.connectionName)}`,
|
|
275
|
+
"Choose a task, or search all settings. Scope is shown in each setting's description.",
|
|
276
|
+
saveStatus,
|
|
210
277
|
],
|
|
278
|
+
items: (Object.entries(groups) as [Group, (typeof groups)[Group]][]).map(([id, group]) => ({
|
|
279
|
+
id,
|
|
280
|
+
label: group.label,
|
|
281
|
+
to: id,
|
|
282
|
+
})),
|
|
283
|
+
hint: "back",
|
|
211
284
|
}),
|
|
285
|
+
content: ({ state }) => settingsScreen(state, "content"),
|
|
286
|
+
behavior: ({ state }) => settingsScreen(state, "behavior"),
|
|
287
|
+
storage: ({ state }) => settingsScreen(state, "storage"),
|
|
288
|
+
advanced: ({ state }) => settingsScreen(state, "advanced"),
|
|
289
|
+
all: ({ state }) => settingsScreen(state, "all"),
|
|
212
290
|
},
|
|
213
291
|
actions: {
|
|
214
292
|
"storage-location": async ({ signal: actionSignal }) => {
|
|
@@ -232,8 +310,8 @@ export async function showSyncSettings(
|
|
|
232
310
|
try {
|
|
233
311
|
const previous = await loadConfig(setupName);
|
|
234
312
|
validate();
|
|
235
|
-
const mergeContent = value === "
|
|
236
|
-
const partialSync = value === "Partial only" || value === "
|
|
313
|
+
const mergeContent = value === "Merge only" || value === "Merge + partial";
|
|
314
|
+
const partialSync = value === "Partial only" || value === "Merge + partial";
|
|
237
315
|
if (
|
|
238
316
|
!(await ctx.ui.confirm(
|
|
239
317
|
"Save experimental content policy?",
|
|
@@ -243,6 +321,7 @@ export async function showSyncSettings(
|
|
|
243
321
|
)
|
|
244
322
|
return { kind: "rejected" };
|
|
245
323
|
validate();
|
|
324
|
+
ctx.ui.notify("Saving reviewed Pi Sync policy…", "info");
|
|
246
325
|
await updateLocalConfig((current) => {
|
|
247
326
|
validate();
|
|
248
327
|
const setup = current.syncSetups[setupName];
|
|
@@ -262,6 +341,9 @@ export async function showSyncSettings(
|
|
|
262
341
|
};
|
|
263
342
|
}, mutationSignal);
|
|
264
343
|
validate();
|
|
344
|
+
saveStatus =
|
|
345
|
+
"Saved reviewed policy for this setup. Applies to subsequent sync operations; recovery evidence is retained.";
|
|
346
|
+
ctx.ui.notify(saveStatus, "info");
|
|
265
347
|
return { kind: "stay" };
|
|
266
348
|
} catch (error) {
|
|
267
349
|
if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
|
|
@@ -312,6 +394,7 @@ export async function showSyncSettings(
|
|
|
312
394
|
);
|
|
313
395
|
validate();
|
|
314
396
|
if (!confirmed) return { kind: "rejected" };
|
|
397
|
+
ctx.ui.notify("Saving reviewed Pi Sync policy…", "info");
|
|
315
398
|
await updateLocalConfig((current) => {
|
|
316
399
|
validate();
|
|
317
400
|
const setup = current.syncSetups[setupName];
|
|
@@ -327,6 +410,9 @@ export async function showSyncSettings(
|
|
|
327
410
|
};
|
|
328
411
|
}, mutationSignal);
|
|
329
412
|
validate();
|
|
413
|
+
saveStatus =
|
|
414
|
+
"Saved reviewed policy for this setup. Applies to subsequent sync operations; recovery evidence is retained.";
|
|
415
|
+
ctx.ui.notify(saveStatus, "info");
|
|
330
416
|
return { kind: "stay" };
|
|
331
417
|
} catch (error) {
|
|
332
418
|
if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
|
|
@@ -354,6 +440,61 @@ export async function showSyncSettings(
|
|
|
354
440
|
return { kind: "rejected" };
|
|
355
441
|
}
|
|
356
442
|
},
|
|
443
|
+
"agent-mode": async ({ value, signal: actionSignal }) => {
|
|
444
|
+
const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
|
|
445
|
+
const validate = captureMutationOwner(ctx, mutationSignal);
|
|
446
|
+
try {
|
|
447
|
+
const saved = await saveAgentMode(ctx, setupName, value === "On", mutationSignal);
|
|
448
|
+
if (!saved) return { kind: "rejected" };
|
|
449
|
+
try {
|
|
450
|
+
validate();
|
|
451
|
+
ctx.ui.notify(
|
|
452
|
+
"Agent mode saved. Applies to subsequent sync and the next session start; no transfer started.",
|
|
453
|
+
"info",
|
|
454
|
+
);
|
|
455
|
+
} catch {
|
|
456
|
+
// The save committed. A closed or replaced UI must not report a failed save.
|
|
457
|
+
// The next Settings read shows the persisted value.
|
|
458
|
+
}
|
|
459
|
+
return { kind: "stay" };
|
|
460
|
+
} catch (error) {
|
|
461
|
+
try {
|
|
462
|
+
validate();
|
|
463
|
+
} catch {
|
|
464
|
+
return { kind: "rejected" };
|
|
465
|
+
}
|
|
466
|
+
notifySaveFailure(ctx, error);
|
|
467
|
+
return { kind: "rejected" };
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
continuous: async ({ value, signal: actionSignal }) => {
|
|
471
|
+
const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
|
|
472
|
+
const validate = captureMutationOwner(ctx, mutationSignal);
|
|
473
|
+
try {
|
|
474
|
+
const saved = await saveContinuousMode(ctx, setupName, value === "On", mutationSignal);
|
|
475
|
+
if (!saved) return { kind: "rejected" };
|
|
476
|
+
try {
|
|
477
|
+
validate();
|
|
478
|
+
ctx.ui.notify(
|
|
479
|
+
value === "On"
|
|
480
|
+
? "Continuous sync enabled. Monitoring starts after the /sync command or manager closes; completed work is not undone."
|
|
481
|
+
: "Continuous sync disabled. Completed work is not undone.",
|
|
482
|
+
"info",
|
|
483
|
+
);
|
|
484
|
+
} catch {
|
|
485
|
+
/* committed save; do not use stale UI */
|
|
486
|
+
}
|
|
487
|
+
return { kind: "stay" };
|
|
488
|
+
} catch (error) {
|
|
489
|
+
try {
|
|
490
|
+
validate();
|
|
491
|
+
} catch {
|
|
492
|
+
return { kind: "rejected" };
|
|
493
|
+
}
|
|
494
|
+
notifySaveFailure(ctx, error);
|
|
495
|
+
return { kind: "rejected" };
|
|
496
|
+
}
|
|
497
|
+
},
|
|
357
498
|
"automatic-transfer": async ({ value, signal: actionSignal }) => {
|
|
358
499
|
const automaticTransfer = value === "On";
|
|
359
500
|
const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
|
|
@@ -405,6 +546,18 @@ export async function showSyncSettings(
|
|
|
405
546
|
return { kind: "rejected" };
|
|
406
547
|
}
|
|
407
548
|
},
|
|
549
|
+
debug: async ({ value, signal: actionSignal }) => {
|
|
550
|
+
const debug = value === "On";
|
|
551
|
+
const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
|
|
552
|
+
try {
|
|
553
|
+
await updateLocalConfig((settings) => ({ ...settings, debug }), mutationSignal);
|
|
554
|
+
if (mutationSignal.aborted) return { kind: "rejected" };
|
|
555
|
+
return { kind: "stay" };
|
|
556
|
+
} catch (error) {
|
|
557
|
+
if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
|
|
558
|
+
return { kind: "rejected" };
|
|
559
|
+
}
|
|
560
|
+
},
|
|
408
561
|
"on-switch": async ({ value, signal: actionSignal }) => {
|
|
409
562
|
const action = value ? setupSwitchActionFromLabel(value) : undefined;
|
|
410
563
|
if (!action) return { kind: "rejected" };
|
|
@@ -444,6 +597,33 @@ export async function showSyncSettings(
|
|
|
444
597
|
},
|
|
445
598
|
},
|
|
446
599
|
});
|
|
600
|
+
for (const id of [
|
|
601
|
+
"automatic",
|
|
602
|
+
"automatic-transfer",
|
|
603
|
+
"merge-settings",
|
|
604
|
+
"skip-secret-scan",
|
|
605
|
+
"show-status",
|
|
606
|
+
"debug",
|
|
607
|
+
"on-switch",
|
|
608
|
+
] as const) {
|
|
609
|
+
const save = menu.actions[id];
|
|
610
|
+
menu.actions[id] = async (action) => {
|
|
611
|
+
const validate = captureMutationOwner(ctx, action.signal);
|
|
612
|
+
validate();
|
|
613
|
+
ctx.ui.notify("Saving Pi Sync setting…", "info");
|
|
614
|
+
const result = await save(action);
|
|
615
|
+
if (action.signal.aborted || signal?.aborted) return { kind: "rejected" };
|
|
616
|
+
validate();
|
|
617
|
+
saveStatus =
|
|
618
|
+
result?.kind === "rejected"
|
|
619
|
+
? "Save failed or rejected. Previous value retained; retry is available."
|
|
620
|
+
: id === "automatic-transfer" || id === "automatic" || id === "debug"
|
|
621
|
+
? "Saved. Enabling startup work applies next session start; completed transfers are not undone."
|
|
622
|
+
: "Saved. Applies to subsequent operations; status visibility applies immediately.";
|
|
623
|
+
ctx.ui.notify(saveStatus, result?.kind === "rejected" ? "warning" : "info");
|
|
624
|
+
return result;
|
|
625
|
+
};
|
|
626
|
+
}
|
|
447
627
|
async function resumeSettings(editorSignal: AbortSignal) {
|
|
448
628
|
if (editorSignal.aborted) return { kind: "close" as const };
|
|
449
629
|
try {
|
|
@@ -455,7 +635,7 @@ export async function showSyncSettings(
|
|
|
455
635
|
return { kind: "close" as const };
|
|
456
636
|
}
|
|
457
637
|
}
|
|
458
|
-
await runMenu(ctx, menu, {
|
|
638
|
+
await runMenu({ ...ctx, ui: boundedSettingsUI(ctx) }, menu, {
|
|
459
639
|
getState: () => loadConfig(setupName),
|
|
460
640
|
signal,
|
|
461
641
|
isCurrent: () => !signal?.aborted,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { throwIfAborted } from "../../sync/signals.js";
|
|
2
3
|
import { promptSecret } from "../secret-input.js";
|
|
3
4
|
import { requiredValueInput } from "./text-input.js";
|
|
4
5
|
|
|
@@ -70,8 +71,3 @@ export async function chooseS3Credentials(
|
|
|
70
71
|
ready: true,
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
-
function throwIfAborted(signal?: AbortSignal) {
|
|
75
|
-
if (!signal?.aborted) return;
|
|
76
|
-
throw signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError");
|
|
77
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { validateConfigName } from "../../settings/settings-validation.js";
|
|
3
|
+
import { throwIfAborted } from "../../sync/signals.js";
|
|
3
4
|
import { errorMessage } from "../../sync/sync-errors.js";
|
|
4
5
|
import { safeTerminalText } from "../terminal-text.js";
|
|
5
6
|
|
|
@@ -10,11 +11,7 @@ export async function promptInitialSetupName(ctx: ExtensionCommandContext, signa
|
|
|
10
11
|
const guidance = ctx.mode === "tui" ? ctx.ui.theme.fg("muted", hint) : hint;
|
|
11
12
|
// This compact prompt owns its default hint; the general helper would repeat it.
|
|
12
13
|
const value = await ctx.ui.input(`Sync setup name\n${guidance}`, undefined, { signal });
|
|
13
|
-
|
|
14
|
-
throw signal.reason instanceof Error
|
|
15
|
-
? signal.reason
|
|
16
|
-
: new DOMException("The operation was aborted", "AbortError");
|
|
17
|
-
}
|
|
14
|
+
throwIfAborted(signal);
|
|
18
15
|
if (value === undefined) return undefined;
|
|
19
16
|
const name = value.trim() || "default";
|
|
20
17
|
try {
|
|
@@ -2,7 +2,11 @@ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import { readLocalConfigObject } from "../../settings/settings-store.js";
|
|
3
3
|
import { validateConfigName } from "../../settings/settings-validation.js";
|
|
4
4
|
import { DEFAULT_SYNC_INCLUDE } from "../../sync/sync-policy.js";
|
|
5
|
+
import { AUTOMATIC_SYNC_DESCRIPTION } from "../sync-behavior.js";
|
|
5
6
|
import { safeTerminalText } from "../terminal-text.js";
|
|
7
|
+
|
|
8
|
+
export { AUTOMATIC_SYNC_DESCRIPTION } from "../sync-behavior.js";
|
|
9
|
+
|
|
6
10
|
import { promptTextInput } from "./text-input.js";
|
|
7
11
|
|
|
8
12
|
export async function readSetupConnection(name: string, signal?: AbortSignal) {
|
|
@@ -12,9 +16,6 @@ export async function readSetupConnection(name: string, signal?: AbortSignal) {
|
|
|
12
16
|
return connection;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
export const AUTOMATIC_SYNC_DESCRIPTION =
|
|
16
|
-
"Check the current setup in the background at startup; review changes in /sync before transferring. At shutdown, automatically push selected content only when sessions are included. No startup dialogs.";
|
|
17
|
-
|
|
18
19
|
export async function chooseSetupContent(ctx: ExtensionCommandContext, signal?: AbortSignal) {
|
|
19
20
|
const choice = await ctx.ui.select(
|
|
20
21
|
"Choose included content\n\nRecommended: Pi settings, instructions, skills, prompts, themes, and extensions.\nMinimal: settings.json and AGENTS.md.\nReview the exact paths before saving.",
|
|
@@ -129,6 +129,8 @@ export async function showSetupSwitcher(
|
|
|
129
129
|
`Included content: ${config.include.length} paths`,
|
|
130
130
|
`Automatic sync: ${automaticSyncSummary(config.automatic)} · Sessions: ${config.include.includes("sessions") ? "On" : "Off"}`,
|
|
131
131
|
`Automatic transfer at startup: ${config.automaticTransfer ? "On — may upload, replace, or delete selected files; no reload" : "Off"}`,
|
|
132
|
+
`Agent mode: ${config.agentMode ? "On (experimental; autonomous merged sync)" : "Off"}`,
|
|
133
|
+
`Continuous sync: ${config.continuous ? (config.agentMode ? "On (experimental; recurring requests and transfers)" : "Saved On; paused until Agent mode is enabled") : "Off"}`,
|
|
132
134
|
"",
|
|
133
135
|
switchEffect,
|
|
134
136
|
].join("\n"),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const AUTOMATIC_SYNC_DESCRIPTION =
|
|
2
|
+
"This setup: check remote state once at startup in TUI/RPC. At shutdown, push all selected content only when sessions are included; reload skips that push. Saves apply to subsequent operations; a new check waits for the next session start. This does not enable startup transfer.";
|
|
3
|
+
|
|
4
|
+
export const STARTUP_TRANSFER_DESCRIPTION =
|
|
5
|
+
"This setup: separate opt-in for one idle startup transfer in TUI/RPC. Requires an accepted baseline, matching remote included paths, and conditional or lease-protected publication (Git, verified WebDAV, or R2/S3 with verified conditional writes). May upload, replace, or remove selected files. Never reloads resources. Enabling applies next session start; opening Settings cancels pending work. Completed transfers are not undone.";
|
|
6
|
+
|
|
7
|
+
export function startupTransferSummary(config: {
|
|
8
|
+
automaticTransfer?: boolean;
|
|
9
|
+
backend: { type: "s3" | "git" | "webdav" };
|
|
10
|
+
}) {
|
|
11
|
+
if (!config.automaticTransfer) return "Off";
|
|
12
|
+
return config.backend.type === "s3"
|
|
13
|
+
? "On (next idle startup; R2/S3 conditional-write verification required)"
|
|
14
|
+
: "On (next idle startup; baseline and remote policy checks required)";
|
|
15
|
+
}
|