@narumitw/pi-sync 0.49.15 → 0.50.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.
Files changed (209) hide show
  1. package/README.md +49 -16
  2. package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
  3. package/dist/chunks/chunk-2B2ELUF5.ts.map +7 -0
  4. package/dist/chunks/chunk-6LP3MCR2.ts +115 -0
  5. package/dist/chunks/chunk-6LP3MCR2.ts.map +7 -0
  6. package/dist/chunks/chunk-6UHKGALE.ts +134 -0
  7. package/dist/chunks/chunk-6UHKGALE.ts.map +7 -0
  8. package/dist/chunks/{chunk-JF6MZCT4.ts → chunk-AUSDLD6F.ts} +4 -4
  9. package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
  10. package/dist/chunks/{chunk-X7SQ2EQM.ts → chunk-AXXS2BPA.ts} +9 -9
  11. package/dist/chunks/chunk-AXXS2BPA.ts.map +7 -0
  12. package/dist/chunks/{chunk-5T442OP3.ts → chunk-C7XY5NF5.ts} +210 -373
  13. package/dist/chunks/chunk-C7XY5NF5.ts.map +7 -0
  14. package/dist/chunks/{chunk-4U6AFLJB.ts → chunk-CHH26D4F.ts} +3 -2
  15. package/dist/chunks/chunk-CHH26D4F.ts.map +7 -0
  16. package/dist/chunks/chunk-CPEHGXJV.ts +50 -0
  17. package/dist/chunks/chunk-CPEHGXJV.ts.map +7 -0
  18. package/dist/chunks/{chunk-5MQ76BK5.ts → chunk-D5X27LCA.ts} +2 -2
  19. package/dist/chunks/chunk-D5X27LCA.ts.map +7 -0
  20. package/dist/chunks/{chunk-WSOBYHIZ.ts → chunk-GWMHAQG4.ts} +4 -4
  21. package/dist/chunks/chunk-GWMHAQG4.ts.map +7 -0
  22. package/dist/chunks/{chunk-5LZALPCD.ts → chunk-HUDQATCL.ts} +25 -25
  23. package/dist/chunks/chunk-HUDQATCL.ts.map +7 -0
  24. package/dist/chunks/{chunk-YQ6UW7IF.ts → chunk-JAAKLL6M.ts} +2 -2
  25. package/dist/chunks/chunk-JAAKLL6M.ts.map +7 -0
  26. package/dist/chunks/chunk-JSYMGJQQ.ts +51 -0
  27. package/dist/chunks/chunk-JSYMGJQQ.ts.map +7 -0
  28. package/dist/chunks/chunk-L7TBO32M.ts +25 -0
  29. package/dist/chunks/chunk-L7TBO32M.ts.map +7 -0
  30. package/dist/chunks/{chunk-DFXBKHGN.ts → chunk-LCRI3ABW.ts} +21 -8
  31. package/dist/chunks/chunk-LCRI3ABW.ts.map +7 -0
  32. package/dist/chunks/chunk-LTBWGSBV.ts +12 -0
  33. package/dist/chunks/chunk-LTBWGSBV.ts.map +7 -0
  34. package/dist/chunks/chunk-NJFUIVYL.ts +17 -0
  35. package/dist/chunks/chunk-NJFUIVYL.ts.map +7 -0
  36. package/dist/chunks/{chunk-HVUSHMVL.ts → chunk-OEX4ABY6.ts} +39 -37
  37. package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
  38. package/dist/chunks/chunk-OUHGBFYF.ts +1255 -0
  39. package/dist/chunks/chunk-OUHGBFYF.ts.map +7 -0
  40. package/dist/chunks/{chunk-HGV24P4T.ts → chunk-Q2TUM5X6.ts} +3 -3
  41. package/dist/chunks/chunk-Q2TUM5X6.ts.map +7 -0
  42. package/dist/chunks/{chunk-GLQLN5GV.ts → chunk-SL64RLXR.ts} +8 -6
  43. package/dist/chunks/chunk-SL64RLXR.ts.map +7 -0
  44. package/dist/chunks/chunk-T3HH3UEJ.ts +96 -0
  45. package/dist/chunks/chunk-T3HH3UEJ.ts.map +7 -0
  46. package/dist/chunks/chunk-TEN2ZBJV.ts +101 -0
  47. package/dist/chunks/chunk-TEN2ZBJV.ts.map +7 -0
  48. package/dist/chunks/chunk-THMF4BVL.ts +18 -0
  49. package/dist/chunks/chunk-THMF4BVL.ts.map +7 -0
  50. package/dist/chunks/{chunk-44LPGQTI.ts → chunk-X5OM6GXT.ts} +9 -7
  51. package/dist/chunks/chunk-X5OM6GXT.ts.map +7 -0
  52. package/dist/chunks/chunk-XBX4HBLG.ts +57 -0
  53. package/dist/chunks/chunk-XBX4HBLG.ts.map +7 -0
  54. package/dist/chunks/{chunk-ARN2672F.ts → chunk-YKJHPTGO.ts} +23 -55
  55. package/dist/chunks/chunk-YKJHPTGO.ts.map +7 -0
  56. package/dist/chunks/chunk-Z4DQF6L3.ts +12 -0
  57. package/dist/chunks/chunk-Z4DQF6L3.ts.map +7 -0
  58. package/dist/chunks/{chunk-F5QL2GPY.ts → chunk-ZI5MXMEX.ts} +5 -99
  59. package/dist/chunks/chunk-ZI5MXMEX.ts.map +7 -0
  60. package/dist/chunks/{file-selection-RKEHALNQ.ts → file-selection-FTTKAOYX.ts} +10 -7
  61. package/dist/chunks/file-selection-FTTKAOYX.ts.map +7 -0
  62. package/dist/chunks/{git-backend-E3UOUUOM.ts → git-backend-OQW46G2X.ts} +42 -17
  63. package/dist/chunks/git-backend-OQW46G2X.ts.map +7 -0
  64. package/dist/chunks/manager-result-dispatcher-U23ODD32.ts +22 -0
  65. package/dist/chunks/manager-ui-ABATHC36.ts +1680 -0
  66. package/dist/chunks/manager-ui-ABATHC36.ts.map +7 -0
  67. package/dist/chunks/remote-selection-ui-ZE3TY67S.ts +21 -0
  68. package/dist/chunks/{s3-backend-552LXHE4.ts → s3-backend-3P33LQHU.ts} +96 -18
  69. package/dist/chunks/s3-backend-3P33LQHU.ts.map +7 -0
  70. package/dist/chunks/{setup-switch-5YDG5YGJ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
  71. package/dist/chunks/setup-wizard-VB3VDOV5.ts +89 -0
  72. package/dist/chunks/setup-wizard-VB3VDOV5.ts.map +7 -0
  73. package/dist/chunks/{snapshot-2RYBUDRA.ts → snapshot-HKEWCVK6.ts} +7 -6
  74. package/dist/chunks/sync-inspection-FEP4L5TR.ts +20 -0
  75. package/dist/chunks/{sync-operations-3LLIHWZO.ts → sync-operations-ONJ27QRL.ts} +374 -294
  76. package/dist/chunks/sync-operations-ONJ27QRL.ts.map +7 -0
  77. package/dist/chunks/{sync-state-SRN6YRBK.ts → sync-state-Q2FTXTW6.ts} +6 -6
  78. package/dist/chunks/sync-state-Q2FTXTW6.ts.map +7 -0
  79. package/dist/chunks/{webdav-backend-ZD5RHRIU.ts → webdav-backend-ECIKD7SO.ts} +26 -13
  80. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +7 -0
  81. package/dist/index.ts +477 -342
  82. package/dist/index.ts.map +4 -4
  83. package/docs/settings.md +30 -6
  84. package/package.json +1 -1
  85. package/src/{backend-factory.ts → backends/backend-factory.ts} +4 -4
  86. package/src/backends/backend-identity.ts +40 -0
  87. package/src/backends/backend-types.ts +81 -0
  88. package/src/{git-backend.ts → backends/git/git-backend.ts} +43 -19
  89. package/src/{git-config.ts → backends/git/git-config.ts} +1 -0
  90. package/src/{git-storage.ts → backends/git/git-storage.ts} +2 -2
  91. package/src/{s3-backend.ts → backends/s3/s3-backend.ts} +45 -9
  92. package/src/{s3-client.ts → backends/s3/s3-client.ts} +56 -8
  93. package/src/{sync-backend.ts → backends/sync-backend.ts} +1 -1
  94. package/src/{webdav-backend.ts → backends/webdav/webdav-backend.ts} +22 -10
  95. package/src/{webdav-client.ts → backends/webdav/webdav-client.ts} +1 -1
  96. package/src/{webdav-config.ts → backends/webdav/webdav-config.ts} +1 -0
  97. package/src/commands/command-execution.ts +330 -0
  98. package/src/commands/command-handler.ts +201 -0
  99. package/src/commands/command-types.ts +18 -0
  100. package/src/{command.ts → commands/command.ts} +2 -1
  101. package/src/commands/setup-completions.ts +6 -0
  102. package/src/settings/config.ts +238 -0
  103. package/src/{settings-management.ts → settings/settings-management.ts} +39 -14
  104. package/src/settings/settings-store.ts +109 -0
  105. package/src/settings/settings-types.ts +128 -0
  106. package/src/settings/settings-validation.ts +443 -0
  107. package/src/snapshot/session-paths.ts +84 -0
  108. package/src/{snapshot-apply.ts → snapshot/snapshot-apply.ts} +3 -3
  109. package/src/{snapshot-codec.ts → snapshot/snapshot-codec.ts} +2 -2
  110. package/src/{snapshot-transaction.ts → snapshot/snapshot-transaction.ts} +5 -4
  111. package/src/snapshot/snapshot-types.ts +38 -0
  112. package/src/{snapshot.ts → snapshot/snapshot.ts} +39 -20
  113. package/src/state/json-file.ts +20 -0
  114. package/src/{lock.ts → state/lock.ts} +3 -2
  115. package/src/{operation-availability.ts → state/operation-availability.ts} +2 -2
  116. package/src/state/state-types.ts +20 -0
  117. package/src/state/sync-state-store.ts +62 -0
  118. package/src/sync/automatic-sync.ts +103 -0
  119. package/src/{remote-snapshot.ts → sync/remote-snapshot.ts} +27 -4
  120. package/src/{setup-switch.ts → sync/setup-switch.ts} +6 -7
  121. package/src/sync/signals.ts +10 -0
  122. package/src/sync/startup-check.ts +128 -0
  123. package/src/{sync-decision.ts → sync/sync-decision.ts} +6 -3
  124. package/src/sync/sync-error-guidance.ts +53 -0
  125. package/src/sync/sync-inspection.ts +51 -0
  126. package/src/sync/sync-loaders.ts +54 -0
  127. package/src/{sync-operations.ts → sync/sync-mutations.ts} +34 -325
  128. package/src/sync/sync-operations.ts +12 -0
  129. package/src/{sync-policy.ts → sync/sync-policy.ts} +2 -2
  130. package/src/sync/sync-queries.ts +235 -0
  131. package/src/{sync-state.ts → sync/sync-state.ts} +5 -4
  132. package/src/sync-extension.ts +64 -708
  133. package/src/sync.ts +8 -8
  134. package/src/types.ts +47 -286
  135. package/src/ui/automatic-sync-summary.ts +6 -0
  136. package/src/{cancellable-operation.ts → ui/cancellable-operation.ts} +4 -4
  137. package/src/{file-selection.ts → ui/file-selection.ts} +5 -3
  138. package/src/{manager-attention.ts → ui/manager-attention.ts} +5 -1
  139. package/src/{manager-recovery.ts → ui/manager-recovery.ts} +6 -2
  140. package/src/{manager-state.ts → ui/manager-state.ts} +35 -14
  141. package/src/ui/manager-ui.ts +386 -0
  142. package/src/{remote-selection-ui.ts → ui/remote-selection-ui.ts} +21 -13
  143. package/src/{secret-input.ts → ui/secret-input.ts} +22 -4
  144. package/src/{settings-ui.ts → ui/settings-ui.ts} +17 -13
  145. package/src/ui/setup/git-ui.ts +293 -0
  146. package/src/{s3-credentials-ui.ts → ui/setup/s3-credentials-ui.ts} +13 -21
  147. package/src/ui/setup/s3-ui.ts +217 -0
  148. package/src/ui/setup/setup-actions.ts +127 -0
  149. package/src/ui/setup/setup-location-ui.ts +167 -0
  150. package/src/ui/setup/setup-name-ui.ts +34 -0
  151. package/src/ui/setup/setup-prompts.ts +92 -0
  152. package/src/ui/setup/setup-review.ts +70 -0
  153. package/src/ui/setup/setup-switcher.ts +181 -0
  154. package/src/ui/setup/setup-wizard.ts +39 -0
  155. package/src/ui/setup/text-input.ts +78 -0
  156. package/src/ui/setup/webdav-ui.ts +330 -0
  157. package/src/{storage-connections-ui.ts → ui/storage-connections-ui.ts} +99 -69
  158. package/src/{sync-attention.ts → ui/sync-attention.ts} +75 -6
  159. package/src/{sync-format.ts → ui/sync-format.ts} +7 -13
  160. package/src/{sync-resolution-ui.ts → ui/sync-resolution-ui.ts} +4 -3
  161. package/src/{sync-setups-ui.ts → ui/sync-setups-ui.ts} +23 -10
  162. package/src/ui/terminal-text.ts +4 -0
  163. package/dist/chunks/chunk-44LPGQTI.ts.map +0 -7
  164. package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
  165. package/dist/chunks/chunk-5LZALPCD.ts.map +0 -7
  166. package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
  167. package/dist/chunks/chunk-5T442OP3.ts.map +0 -7
  168. package/dist/chunks/chunk-ARN2672F.ts.map +0 -7
  169. package/dist/chunks/chunk-C7LXZ3UH.ts +0 -167
  170. package/dist/chunks/chunk-C7LXZ3UH.ts.map +0 -7
  171. package/dist/chunks/chunk-DFXBKHGN.ts.map +0 -7
  172. package/dist/chunks/chunk-E4COSV32.ts +0 -82
  173. package/dist/chunks/chunk-E4COSV32.ts.map +0 -7
  174. package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
  175. package/dist/chunks/chunk-GLQLN5GV.ts.map +0 -7
  176. package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
  177. package/dist/chunks/chunk-HVUSHMVL.ts.map +0 -7
  178. package/dist/chunks/chunk-JF6MZCT4.ts.map +0 -7
  179. package/dist/chunks/chunk-WSOBYHIZ.ts.map +0 -7
  180. package/dist/chunks/chunk-X7SQ2EQM.ts.map +0 -7
  181. package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
  182. package/dist/chunks/file-selection-RKEHALNQ.ts.map +0 -7
  183. package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
  184. package/dist/chunks/manager-result-dispatcher-LWPYJAD5.ts +0 -18
  185. package/dist/chunks/manager-ui-NF4RUHBY.ts +0 -2785
  186. package/dist/chunks/manager-ui-NF4RUHBY.ts.map +0 -7
  187. package/dist/chunks/remote-selection-ui-NUF5P4ZJ.ts +0 -17
  188. package/dist/chunks/s3-backend-552LXHE4.ts.map +0 -7
  189. package/dist/chunks/sync-operations-3LLIHWZO.ts.map +0 -7
  190. package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
  191. package/src/config.ts +0 -923
  192. package/src/git-ui.ts +0 -307
  193. package/src/manager-helpers.ts +0 -64
  194. package/src/manager-ui.ts +0 -997
  195. package/src/webdav-ui.ts +0 -421
  196. /package/dist/chunks/{manager-result-dispatcher-LWPYJAD5.ts.map → manager-result-dispatcher-U23ODD32.ts.map} +0 -0
  197. /package/dist/chunks/{remote-selection-ui-NUF5P4ZJ.ts.map → remote-selection-ui-ZE3TY67S.ts.map} +0 -0
  198. /package/dist/chunks/{setup-switch-5YDG5YGJ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
  199. /package/dist/chunks/{snapshot-2RYBUDRA.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
  200. /package/dist/chunks/{sync-state-SRN6YRBK.ts.map → sync-inspection-FEP4L5TR.ts.map} +0 -0
  201. /package/src/{git-runner.ts → backends/git/git-runner.ts} +0 -0
  202. /package/src/{config-errors.ts → settings/config-errors.ts} +0 -0
  203. /package/src/{config-file.ts → settings/config-file.ts} +0 -0
  204. /package/src/{snapshot-paths.ts → snapshot/snapshot-paths.ts} +0 -0
  205. /package/src/{lock-policy.ts → state/lock-policy.ts} +0 -0
  206. /package/src/{lockfile-fs.ts → state/lockfile-fs.ts} +0 -0
  207. /package/src/{state-directory.ts → state/state-directory.ts} +0 -0
  208. /package/src/{sync-errors.ts → sync/sync-errors.ts} +0 -0
  209. /package/src/{manager-result-dispatcher.ts → ui/manager-result-dispatcher.ts} +0 -0
@@ -0,0 +1,235 @@
1
+ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
+ import { createSyncBackend, type SyncBackendFactory } from "../backends/backend-factory.js";
3
+ import type { SyncBackend } from "../backends/sync-backend.js";
4
+ import type { CommandOptions } from "../commands/command-types.js";
5
+ import { loadConfig, syncSessionsWarnings } from "../settings/config.js";
6
+ import { snapshotOptionsForContext } from "../snapshot/session-paths.js";
7
+ import { createSnapshot, scanSnapshot } from "../snapshot/snapshot.js";
8
+ import type { SnapshotOptions } from "../snapshot/snapshot-types.js";
9
+ import { inspectLock, isLockGuardHeld, isStaleLock, withLock } from "../state/lock.js";
10
+ import {
11
+ formatDiff,
12
+ formatSnapshotOnlyDiff,
13
+ publicationCapabilityDescription,
14
+ } from "../ui/sync-format.js";
15
+ import { safeTerminalText } from "../ui/terminal-text.js";
16
+ import { formatRemoteSelectionStatus, readRemoteSnapshot } from "./remote-snapshot.js";
17
+ import { throwIfAborted } from "./signals.js";
18
+ import { errorMessage } from "./sync-errors.js";
19
+ import { inspectSync } from "./sync-inspection.js";
20
+ import { rollback } from "./sync-mutations.js";
21
+
22
+ const STATUS_KEY = "sync";
23
+ const DEFAULT_PROFILE = "default";
24
+
25
+ export async function status(
26
+ ctx: ExtensionCommandContext,
27
+ options: CommandOptions,
28
+ factory: SyncBackendFactory = createSyncBackend,
29
+ ) {
30
+ const config = await loadConfig(options.setup);
31
+ throwIfAborted(options.signal);
32
+ ctx.ui.setStatus(STATUS_KEY, `checking ${config.setupName}`);
33
+ const { head, selectionState, localChanged, remoteChanged, localFiles, destination, capability } =
34
+ await inspectSync(config, snapshotOptionsForContext(ctx, config), options.signal, factory);
35
+ throwIfAborted(options.signal);
36
+
37
+ const remoteText = head
38
+ ? `remote: ${head.snapshotId} from ${head.machine} at ${head.createdAt}`
39
+ : "remote: empty";
40
+ const warnings = syncSessionsWarnings(config);
41
+ ctx.ui.setStatus(STATUS_KEY, undefined);
42
+ ctx.ui.notify(
43
+ [
44
+ `sync setup: ${config.setupName}`,
45
+ `storage connection: ${config.connectionName}`,
46
+ `storage location: ${safeTerminalText(destination)}`,
47
+ `publication safety: ${publicationCapabilityDescription(capability)}`,
48
+ `included content: ${config.include.join(", ") || "none"}`,
49
+ `sessions: ${config.include.includes("sessions") ? "included" : "excluded"}`,
50
+ remoteText,
51
+ formatRemoteSelectionStatus(selectionState),
52
+ `local files: ${localFiles}`,
53
+ `local changed since last sync: ${localChanged ? "yes" : "no"}`,
54
+ `remote changed since last sync: ${remoteChanged ? "yes" : "no"}`,
55
+ ...warnings,
56
+ ].join("\n"),
57
+ localChanged || remoteChanged || selectionState?.kind === "different" || warnings.length > 0
58
+ ? "warning"
59
+ : "info",
60
+ );
61
+ }
62
+
63
+ export async function diff(
64
+ ctx: ExtensionCommandContext,
65
+ options: CommandOptions,
66
+ factory: SyncBackendFactory = createSyncBackend,
67
+ ) {
68
+ const config = await loadConfig(options.setup);
69
+ throwIfAborted(options.signal);
70
+ ctx.ui.setStatus(STATUS_KEY, `checking ${config.setupName}`);
71
+ const backend = await factory(config);
72
+ const local = await createSnapshot(
73
+ config.snapshotIdentity,
74
+ snapshotOptionsForContext(ctx, config),
75
+ );
76
+ throwIfAborted(options.signal);
77
+ const { snapshot: remote, selectionState } = await readRemoteSnapshot(
78
+ backend,
79
+ config,
80
+ options.signal,
81
+ { allowSelectionDifference: true },
82
+ );
83
+ throwIfAborted(options.signal);
84
+ ctx.ui.setStatus(STATUS_KEY, undefined);
85
+
86
+ const warnings = syncSessionsWarnings(config);
87
+ const header = [
88
+ `sync setup: ${config.setupName}`,
89
+ `storage connection: ${config.connectionName}`,
90
+ `storage location: ${safeTerminalText(backend.destination)}`,
91
+ `included content: ${config.include.join(", ") || "none"}`,
92
+ `sessions: ${config.include.includes("sessions") ? "included" : "excluded"}`,
93
+ formatRemoteSelectionStatus(selectionState),
94
+ ...warnings,
95
+ ].join("\n");
96
+ const level = warnings.length > 0 || selectionState?.kind === "different" ? "warning" : "info";
97
+ if (!remote) {
98
+ ctx.ui.notify(
99
+ `${header}\n\n${formatSnapshotOnlyDiff("Remote is empty. Local push would upload", local)}`,
100
+ level,
101
+ );
102
+ return;
103
+ }
104
+
105
+ ctx.ui.notify(`${header}\n\n${formatDiff(local, remote)}`, level);
106
+ }
107
+
108
+ export async function doctor(
109
+ ctx: ExtensionCommandContext,
110
+ options: CommandOptions,
111
+ factory: SyncBackendFactory = createSyncBackend,
112
+ ) {
113
+ const messages: string[] = [];
114
+ let level: "info" | "warning" = "info";
115
+ let snapshotOptions: SnapshotOptions = {};
116
+ let profile = DEFAULT_PROFILE;
117
+ let backend: SyncBackend | undefined;
118
+ let backendSummary: string[] = [];
119
+
120
+ try {
121
+ const config = await loadConfig(options.setup);
122
+ throwIfAborted(options.signal);
123
+ backend = await factory(config);
124
+ profile = config.snapshotIdentity;
125
+ snapshotOptions = snapshotOptionsForContext(ctx, config);
126
+ messages.push(
127
+ `config: ok (sync setup ${config.setupName})`,
128
+ `included content: ${config.include.join(", ") || "none"}`,
129
+ `sessions: ${config.include.includes("sessions") ? "included" : "excluded"}`,
130
+ );
131
+ backendSummary = [
132
+ `storage location: ${safeTerminalText(backend.destination)}`,
133
+ `publication safety: ${publicationCapabilityDescription(backend.capability)}`,
134
+ ];
135
+ const warnings = syncSessionsWarnings(config);
136
+ if (warnings.length > 0) {
137
+ level = "warning";
138
+ messages.push(...warnings);
139
+ }
140
+ } catch (error) {
141
+ throwIfAborted(options.signal);
142
+ level = "warning";
143
+ messages.push(`config: ${errorMessage(error)}`);
144
+ }
145
+
146
+ const local = await createSnapshot(profile, snapshotOptions);
147
+ throwIfAborted(options.signal);
148
+ const secrets = scanSnapshot(local);
149
+ if (secrets.length > 0) {
150
+ level = "warning";
151
+ messages.push("secret scan: possible secrets found:");
152
+ messages.push(...secrets.map((secret) => `- ${secret}`));
153
+ } else {
154
+ messages.push(`secret scan: ok (${local.files.length} files checked)`);
155
+ }
156
+
157
+ const lock = await inspectLock();
158
+ throwIfAborted(options.signal);
159
+ if (lock.status === "valid" && isStaleLock(lock.lock)) {
160
+ level = "warning";
161
+ messages.push(
162
+ `lock: stale (pid ${lock.lock.pid}); run /sync unlock --stale after verifying no sync is running`,
163
+ );
164
+ } else if (lock.status === "valid") {
165
+ messages.push(`lock: held by pid ${lock.lock.pid} since ${lock.lock.startedAt}`);
166
+ } else if (lock.status === "unreadable") {
167
+ level = "warning";
168
+ messages.push(
169
+ "lock: unreadable; use /sync unlock --stale only after verifying no sync is running",
170
+ );
171
+ } else if (await isLockGuardHeld()) {
172
+ throwIfAborted(options.signal);
173
+ level = "warning";
174
+ messages.push("lock: guard active while metadata is missing or still being initialized");
175
+ } else {
176
+ messages.push("lock: free");
177
+ }
178
+
179
+ if (backend) {
180
+ messages.push(...backendSummary);
181
+ const diagnostics = await backend.diagnose(options.signal);
182
+ throwIfAborted(options.signal);
183
+ for (const diagnostic of diagnostics) {
184
+ messages.push(diagnostic.message);
185
+ if (diagnostic.level !== "info") level = "warning";
186
+ }
187
+ }
188
+ ctx.ui.notify(messages.join("\n"), level);
189
+ }
190
+
191
+ export async function history(
192
+ ctx: ExtensionCommandContext,
193
+ options: CommandOptions,
194
+ factory: SyncBackendFactory = createSyncBackend,
195
+ ) {
196
+ const config = await loadConfig(options.setup);
197
+ throwIfAborted(options.signal);
198
+ const backend = await factory(config);
199
+ const snapshots = (await backend.listHistory(options.signal)).slice(-20).reverse();
200
+ throwIfAborted(options.signal);
201
+ if (snapshots.length === 0) {
202
+ ctx.ui.notify("No remote pi-sync history found.", "info");
203
+ return;
204
+ }
205
+
206
+ const currentSnapshot = snapshots[0]?.snapshotId;
207
+ if (ctx.mode === "tui") {
208
+ const labels = snapshots.map(
209
+ (item, index) =>
210
+ `${index + 1}. ${item.createdAt} · ${safeTerminalText(item.machine)} · ${item.snapshotId}${item.snapshotId === currentSnapshot ? " (current)" : ""}${item.syncSessions ? " · sessions" : ""}`,
211
+ );
212
+ const selected = await ctx.ui.select(
213
+ `History for sync setup “${safeTerminalText(config.setupName)}”\n\nChoose a snapshot to preview rollback.`,
214
+ [...labels, "Back"],
215
+ );
216
+ if (!selected || selected === "Back") return;
217
+ throwIfAborted(options.signal);
218
+ const index = labels.indexOf(selected);
219
+ const snapshot = snapshots[index];
220
+ if (!snapshot) return;
221
+ await withLock("rollback", () =>
222
+ rollback(ctx, { ...options, args: [snapshot.snapshotRef], yes: false }, factory, {
223
+ backendIdentity: backend.identity,
224
+ setup: config.setupName,
225
+ }),
226
+ );
227
+ return;
228
+ }
229
+ ctx.ui.notify(
230
+ snapshots
231
+ .map((item) => `${item.snapshotRef} ${item.createdAt} ${safeTerminalText(item.machine)}`)
232
+ .join("\n"),
233
+ "info",
234
+ );
235
+ }
@@ -1,18 +1,19 @@
1
- import { toPosix } from "./paths.js";
1
+ import type { RemoteHead } from "../backends/sync-backend.js";
2
+ import { toPosix } from "../paths.js";
2
3
  import {
3
4
  canonicalSnapshotPathForConfig,
4
5
  filterSnapshotForConfigPolicy,
5
6
  isConfiguredSnapshotPath,
6
7
  isSessionPath,
7
- } from "./snapshot.js";
8
- import type { RemoteHead } from "./sync-backend.js";
8
+ } from "../snapshot/snapshot.js";
9
+ import type { Snapshot } from "../snapshot/snapshot-types.js";
10
+ import type { SyncState } from "../state/state-types.js";
9
11
  import {
10
12
  customIncludePathsByLower,
11
13
  includeFromSelectionConfig,
12
14
  normalizeSyncInclude,
13
15
  type SyncSelectionConfig,
14
16
  } from "./sync-policy.js";
15
- import type { Snapshot, SyncState } from "./types.js";
16
17
 
17
18
  type SyncPolicyConfig = SyncSelectionConfig;
18
19