@narumitw/pi-sync 0.50.3 → 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.
Files changed (218) hide show
  1. package/README.md +4 -1
  2. package/dist/chunks/{chunk-6LP3MCR2.ts → chunk-2JZFHNLM.ts} +3 -5
  3. package/dist/chunks/chunk-2JZFHNLM.ts.map +7 -0
  4. package/dist/chunks/{chunk-LCRI3ABW.ts → chunk-2RZSQWBH.ts} +8 -19
  5. package/dist/chunks/chunk-2RZSQWBH.ts.map +7 -0
  6. package/dist/chunks/{chunk-IJ6FJFA5.ts → chunk-4AIHXRKE.ts} +11 -17
  7. package/dist/chunks/chunk-4AIHXRKE.ts.map +7 -0
  8. package/dist/chunks/{chunk-C7XY5NF5.ts → chunk-4MPIH46N.ts} +29 -51
  9. package/dist/chunks/chunk-4MPIH46N.ts.map +7 -0
  10. package/dist/chunks/{chunk-5B3FXBLE.ts → chunk-6546TS54.ts} +16 -20
  11. package/dist/chunks/chunk-6546TS54.ts.map +7 -0
  12. package/dist/chunks/{chunk-Q2TUM5X6.ts → chunk-6NT3YFGY.ts} +2 -2
  13. package/dist/chunks/chunk-6NT3YFGY.ts.map +7 -0
  14. package/dist/chunks/{chunk-2B2ELUF5.ts → chunk-7D5SF7P3.ts} +1 -1
  15. package/dist/chunks/chunk-7D5SF7P3.ts.map +7 -0
  16. package/dist/chunks/{chunk-YDWUZVPN.ts → chunk-7KHKWL3Q.ts} +28 -47
  17. package/dist/chunks/chunk-7KHKWL3Q.ts.map +7 -0
  18. package/dist/chunks/{chunk-ZI5MXMEX.ts → chunk-BVVP33U5.ts} +2 -4
  19. package/dist/chunks/chunk-BVVP33U5.ts.map +7 -0
  20. package/dist/chunks/{chunk-SZWBGPID.ts → chunk-DM4X4FIA.ts} +8 -13
  21. package/dist/chunks/chunk-DM4X4FIA.ts.map +7 -0
  22. package/dist/chunks/{chunk-SL64RLXR.ts → chunk-DZUSTGQV.ts} +8 -16
  23. package/dist/chunks/chunk-DZUSTGQV.ts.map +7 -0
  24. package/dist/chunks/{chunk-YHEMENKV.ts → chunk-E42GXD6D.ts} +13 -19
  25. package/dist/chunks/chunk-E42GXD6D.ts.map +7 -0
  26. package/dist/chunks/{chunk-Z4DQF6L3.ts → chunk-ECYCNZOE.ts} +1 -1
  27. package/dist/chunks/chunk-ECYCNZOE.ts.map +7 -0
  28. package/dist/chunks/{chunk-OEX4ABY6.ts → chunk-EEJPYTWE.ts} +11 -21
  29. package/dist/chunks/chunk-EEJPYTWE.ts.map +7 -0
  30. package/dist/chunks/{chunk-CPEHGXJV.ts → chunk-G4RUXRWZ.ts} +3 -5
  31. package/dist/chunks/chunk-G4RUXRWZ.ts.map +7 -0
  32. package/dist/chunks/{chunk-T2B5IU74.ts → chunk-HGJH56KR.ts} +4 -4
  33. package/dist/chunks/chunk-HGJH56KR.ts.map +7 -0
  34. package/dist/chunks/{chunk-JAAKLL6M.ts → chunk-IVXEDPAD.ts} +10 -34
  35. package/dist/chunks/chunk-IVXEDPAD.ts.map +7 -0
  36. package/dist/chunks/chunk-K3RIEL6J.ts +20 -0
  37. package/dist/chunks/chunk-K3RIEL6J.ts.map +7 -0
  38. package/dist/chunks/{chunk-LTBWGSBV.ts → chunk-MSSW6F3R.ts} +1 -1
  39. package/dist/chunks/chunk-MSSW6F3R.ts.map +7 -0
  40. package/dist/chunks/{chunk-T3HH3UEJ.ts → chunk-OVDTF5CH.ts} +4 -10
  41. package/dist/chunks/chunk-OVDTF5CH.ts.map +7 -0
  42. package/dist/chunks/{chunk-NJFUIVYL.ts → chunk-PWMWOJP4.ts} +1 -1
  43. package/dist/chunks/chunk-PWMWOJP4.ts.map +7 -0
  44. package/dist/chunks/{chunk-HUDQATCL.ts → chunk-Q32CQWPM.ts} +6 -8
  45. package/dist/chunks/chunk-Q32CQWPM.ts.map +7 -0
  46. package/dist/chunks/{chunk-ETTRJDNP.ts → chunk-QLSN7AGX.ts} +6 -13
  47. package/dist/chunks/chunk-QLSN7AGX.ts.map +7 -0
  48. package/dist/chunks/{chunk-THMF4BVL.ts → chunk-SWUXA3MH.ts} +1 -1
  49. package/dist/chunks/chunk-SWUXA3MH.ts.map +7 -0
  50. package/dist/chunks/{chunk-TEN2ZBJV.ts → chunk-T2SJD3N7.ts} +2 -4
  51. package/dist/chunks/chunk-T2SJD3N7.ts.map +7 -0
  52. package/dist/chunks/{chunk-AUSDLD6F.ts → chunk-T56RNRZJ.ts} +10 -26
  53. package/dist/chunks/chunk-T56RNRZJ.ts.map +7 -0
  54. package/dist/chunks/{chunk-DHVFS4QK.ts → chunk-UA7RXOIT.ts} +37 -90
  55. package/dist/chunks/chunk-UA7RXOIT.ts.map +7 -0
  56. package/dist/chunks/{chunk-D5X27LCA.ts → chunk-UETBKTDI.ts} +1 -1
  57. package/dist/chunks/chunk-UETBKTDI.ts.map +7 -0
  58. package/dist/chunks/{chunk-CHH26D4F.ts → chunk-YFADF5ZQ.ts} +7 -6
  59. package/dist/chunks/chunk-YFADF5ZQ.ts.map +7 -0
  60. package/dist/chunks/{chunk-CCSN3F47.ts → chunk-YPXGU5OR.ts} +4 -4
  61. package/dist/chunks/chunk-YPXGU5OR.ts.map +7 -0
  62. package/dist/chunks/{file-selection-FTTKAOYX.ts → file-selection-RAVHRYFG.ts} +14 -26
  63. package/dist/chunks/file-selection-RAVHRYFG.ts.map +7 -0
  64. package/dist/chunks/{git-backend-GMVS4PD4.ts → git-backend-IAANDL3K.ts} +41 -104
  65. package/dist/chunks/git-backend-IAANDL3K.ts.map +7 -0
  66. package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +23 -0
  67. package/dist/chunks/{manager-ui-IPAOAWZA.ts → manager-ui-763NEKJY.ts} +124 -251
  68. package/dist/chunks/manager-ui-763NEKJY.ts.map +7 -0
  69. package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +22 -0
  70. package/dist/chunks/{s3-backend-66O4FGBH.ts → s3-backend-XGXUOPUN.ts} +23 -54
  71. package/dist/chunks/s3-backend-XGXUOPUN.ts.map +7 -0
  72. package/dist/chunks/{setup-switch-IS2PEZ3J.ts → setup-switch-5G2VNJHL.ts} +9 -9
  73. package/dist/chunks/{setup-wizard-JUJZYNNM.ts → setup-wizard-YIEWGX32.ts} +12 -12
  74. package/dist/chunks/setup-wizard-YIEWGX32.ts.map +7 -0
  75. package/dist/chunks/{snapshot-HKEWCVK6.ts → snapshot-JL5DFSXI.ts} +5 -5
  76. package/dist/chunks/sync-inspection-P3ENJLNF.ts +20 -0
  77. package/dist/chunks/{sync-operations-LBX3GKCM.ts → sync-operations-AR76EB6C.ts} +85 -201
  78. package/dist/chunks/sync-operations-AR76EB6C.ts.map +7 -0
  79. package/dist/chunks/{sync-state-Q2FTXTW6.ts → sync-state-VHJUZ4YG.ts} +6 -6
  80. package/dist/chunks/{webdav-backend-ECIKD7SO.ts → webdav-backend-3EGUONAT.ts} +20 -51
  81. package/dist/chunks/webdav-backend-3EGUONAT.ts.map +7 -0
  82. package/dist/index.ts +49 -74
  83. package/dist/index.ts.map +3 -3
  84. package/docs/settings.md +9 -0
  85. package/package.json +61 -62
  86. package/src/backends/backend-factory.ts +15 -15
  87. package/src/backends/backend-identity.ts +31 -31
  88. package/src/backends/backend-types.ts +42 -42
  89. package/src/backends/git/git-backend.ts +714 -788
  90. package/src/backends/git/git-config.ts +104 -111
  91. package/src/backends/git/git-runner.ts +230 -247
  92. package/src/backends/git/git-storage.ts +228 -245
  93. package/src/backends/s3/s3-backend.ts +408 -441
  94. package/src/backends/s3/s3-client.ts +288 -333
  95. package/src/backends/sync-backend.ts +65 -65
  96. package/src/backends/webdav/webdav-backend.ts +542 -589
  97. package/src/backends/webdav/webdav-client.ts +292 -316
  98. package/src/backends/webdav/webdav-config.ts +57 -68
  99. package/src/commands/command-execution.ts +270 -289
  100. package/src/commands/command-handler.ts +160 -163
  101. package/src/commands/command-types.ts +13 -13
  102. package/src/commands/command.ts +190 -196
  103. package/src/commands/setup-completions.ts +1 -1
  104. package/src/paths.ts +47 -47
  105. package/src/settings/config-errors.ts +4 -5
  106. package/src/settings/config-file.ts +473 -506
  107. package/src/settings/config.ts +171 -174
  108. package/src/settings/settings-management.ts +191 -216
  109. package/src/settings/settings-store.ts +63 -77
  110. package/src/settings/settings-types.ts +78 -75
  111. package/src/settings/settings-validation.ts +328 -364
  112. package/src/snapshot/session-paths.ts +41 -55
  113. package/src/snapshot/snapshot-apply.ts +197 -229
  114. package/src/snapshot/snapshot-codec.ts +29 -31
  115. package/src/snapshot/snapshot-paths.ts +4 -6
  116. package/src/snapshot/snapshot-transaction.ts +161 -170
  117. package/src/snapshot/snapshot-types.ts +24 -24
  118. package/src/snapshot/snapshot.ts +310 -343
  119. package/src/state/json-file.ts +11 -11
  120. package/src/state/lock.ts +226 -242
  121. package/src/state/lockfile-fs.ts +20 -20
  122. package/src/state/operation-availability.ts +36 -41
  123. package/src/state/state-directory.ts +171 -173
  124. package/src/state/state-types.ts +15 -15
  125. package/src/state/sync-state-store.ts +23 -23
  126. package/src/sync/automatic-sync.ts +75 -80
  127. package/src/sync/remote-snapshot.ts +52 -60
  128. package/src/sync/setup-switch.ts +99 -114
  129. package/src/sync/signals.ts +3 -5
  130. package/src/sync/startup-check.ts +97 -111
  131. package/src/sync/sync-decision.ts +76 -80
  132. package/src/sync/sync-error-guidance.ts +41 -48
  133. package/src/sync/sync-errors.ts +10 -10
  134. package/src/sync/sync-inspection.ts +29 -33
  135. package/src/sync/sync-loaders.ts +28 -36
  136. package/src/sync/sync-mutations.ts +510 -622
  137. package/src/sync/sync-operations.ts +7 -7
  138. package/src/sync/sync-policy.ts +296 -326
  139. package/src/sync/sync-queries.ts +175 -194
  140. package/src/sync/sync-state.ts +74 -100
  141. package/src/sync-extension.ts +89 -101
  142. package/src/sync.ts +26 -26
  143. package/src/types.ts +37 -37
  144. package/src/ui/automatic-sync-summary.ts +1 -3
  145. package/src/ui/cancellable-operation.ts +89 -101
  146. package/src/ui/file-selection.ts +250 -274
  147. package/src/ui/manager-attention.ts +51 -63
  148. package/src/ui/manager-recovery.ts +48 -57
  149. package/src/ui/manager-result-dispatcher.ts +62 -76
  150. package/src/ui/manager-state.ts +200 -232
  151. package/src/ui/manager-ui.ts +264 -362
  152. package/src/ui/remote-selection-ui.ts +547 -588
  153. package/src/ui/secret-input.ts +197 -205
  154. package/src/ui/settings-ui.ts +175 -172
  155. package/src/ui/setup/git-ui.ts +232 -261
  156. package/src/ui/setup/s3-credentials-ui.ts +58 -65
  157. package/src/ui/setup/s3-ui.ts +171 -195
  158. package/src/ui/setup/setup-actions.ts +91 -101
  159. package/src/ui/setup/setup-location-ui.ts +121 -137
  160. package/src/ui/setup/setup-name-ui.ts +27 -27
  161. package/src/ui/setup/setup-prompts.ts +65 -65
  162. package/src/ui/setup/setup-review.ts +58 -59
  163. package/src/ui/setup/setup-switcher.ts +156 -160
  164. package/src/ui/setup/setup-wizard.ts +28 -28
  165. package/src/ui/setup/text-input.ts +54 -58
  166. package/src/ui/setup/webdav-ui.ts +246 -293
  167. package/src/ui/storage-connections-ui.ts +317 -369
  168. package/src/ui/sync-attention.ts +148 -164
  169. package/src/ui/sync-format.ts +110 -121
  170. package/src/ui/sync-resolution-ui.ts +147 -153
  171. package/src/ui/sync-setups-ui.ts +218 -238
  172. package/src/ui/sync-status.ts +17 -0
  173. package/src/ui/terminal-text.ts +2 -2
  174. package/dist/chunks/chunk-2B2ELUF5.ts.map +0 -7
  175. package/dist/chunks/chunk-5B3FXBLE.ts.map +0 -7
  176. package/dist/chunks/chunk-6LP3MCR2.ts.map +0 -7
  177. package/dist/chunks/chunk-AUSDLD6F.ts.map +0 -7
  178. package/dist/chunks/chunk-C7XY5NF5.ts.map +0 -7
  179. package/dist/chunks/chunk-CCSN3F47.ts.map +0 -7
  180. package/dist/chunks/chunk-CHH26D4F.ts.map +0 -7
  181. package/dist/chunks/chunk-CPEHGXJV.ts.map +0 -7
  182. package/dist/chunks/chunk-D5X27LCA.ts.map +0 -7
  183. package/dist/chunks/chunk-DHVFS4QK.ts.map +0 -7
  184. package/dist/chunks/chunk-ETTRJDNP.ts.map +0 -7
  185. package/dist/chunks/chunk-HUDQATCL.ts.map +0 -7
  186. package/dist/chunks/chunk-IJ6FJFA5.ts.map +0 -7
  187. package/dist/chunks/chunk-JAAKLL6M.ts.map +0 -7
  188. package/dist/chunks/chunk-LCRI3ABW.ts.map +0 -7
  189. package/dist/chunks/chunk-LTBWGSBV.ts.map +0 -7
  190. package/dist/chunks/chunk-NJFUIVYL.ts.map +0 -7
  191. package/dist/chunks/chunk-OEX4ABY6.ts.map +0 -7
  192. package/dist/chunks/chunk-Q2TUM5X6.ts.map +0 -7
  193. package/dist/chunks/chunk-SL64RLXR.ts.map +0 -7
  194. package/dist/chunks/chunk-SZWBGPID.ts.map +0 -7
  195. package/dist/chunks/chunk-T2B5IU74.ts.map +0 -7
  196. package/dist/chunks/chunk-T3HH3UEJ.ts.map +0 -7
  197. package/dist/chunks/chunk-TEN2ZBJV.ts.map +0 -7
  198. package/dist/chunks/chunk-THMF4BVL.ts.map +0 -7
  199. package/dist/chunks/chunk-YDWUZVPN.ts.map +0 -7
  200. package/dist/chunks/chunk-YHEMENKV.ts.map +0 -7
  201. package/dist/chunks/chunk-Z4DQF6L3.ts.map +0 -7
  202. package/dist/chunks/chunk-ZI5MXMEX.ts.map +0 -7
  203. package/dist/chunks/file-selection-FTTKAOYX.ts.map +0 -7
  204. package/dist/chunks/git-backend-GMVS4PD4.ts.map +0 -7
  205. package/dist/chunks/manager-result-dispatcher-ASKGOSW5.ts +0 -22
  206. package/dist/chunks/manager-ui-IPAOAWZA.ts.map +0 -7
  207. package/dist/chunks/remote-selection-ui-QDNR7WEI.ts +0 -21
  208. package/dist/chunks/s3-backend-66O4FGBH.ts.map +0 -7
  209. package/dist/chunks/setup-wizard-JUJZYNNM.ts.map +0 -7
  210. package/dist/chunks/sync-inspection-CST2KL65.ts +0 -20
  211. package/dist/chunks/sync-operations-LBX3GKCM.ts.map +0 -7
  212. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +0 -7
  213. /package/dist/chunks/{manager-result-dispatcher-ASKGOSW5.ts.map → manager-result-dispatcher-OTUMQM26.ts.map} +0 -0
  214. /package/dist/chunks/{remote-selection-ui-QDNR7WEI.ts.map → remote-selection-ui-TZTJF32Y.ts.map} +0 -0
  215. /package/dist/chunks/{setup-switch-IS2PEZ3J.ts.map → setup-switch-5G2VNJHL.ts.map} +0 -0
  216. /package/dist/chunks/{snapshot-HKEWCVK6.ts.map → snapshot-JL5DFSXI.ts.map} +0 -0
  217. /package/dist/chunks/{sync-inspection-CST2KL65.ts.map → sync-inspection-P3ENJLNF.ts.map} +0 -0
  218. /package/dist/chunks/{sync-state-Q2FTXTW6.ts.map → sync-state-VHJUZ4YG.ts.map} +0 -0
@@ -1,293 +1,264 @@
1
1
  import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
+ import { normalizeGitBranch, normalizeGitDirectory, normalizeGitRemote } from "../../backends/git/git-config.js";
2
3
  import {
3
- normalizeGitBranch,
4
- normalizeGitDirectory,
5
- normalizeGitRemote,
6
- } from "../../backends/git/git-config.js";
7
- import {
8
- addStorageConnection,
9
- addSyncSetup,
10
- saveNewV3Settings,
11
- updateStorageConnection,
12
- updateSyncSetup,
4
+ addStorageConnection,
5
+ addSyncSetup,
6
+ saveNewV3Settings,
7
+ updateStorageConnection,
8
+ updateSyncSetup,
13
9
  } from "../../settings/settings-management.js";
14
10
  import type { PartialConfig } from "../../settings/settings-types.js";
15
11
  import { automaticSyncSummary } from "../automatic-sync-summary.js";
16
12
  import { safeTerminalText } from "../terminal-text.js";
17
13
  import { promptAvailableSetupStorage } from "./setup-location-ui.js";
18
14
  import {
19
- chooseAutomaticSync,
20
- chooseSetupContent,
21
- includedContentLines,
22
- promptResourceName,
23
- readSetupConnection,
15
+ chooseAutomaticSync,
16
+ chooseSetupContent,
17
+ includedContentLines,
18
+ promptResourceName,
19
+ readSetupConnection,
24
20
  } from "./setup-prompts.js";
25
21
  import { saveReviewedDraft } from "./setup-review.js";
26
22
  import { requiredInput, requiredValueInput } from "./text-input.js";
27
23
 
28
- export async function showGitSetup(
29
- ctx: ExtensionCommandContext,
30
- targetName: string,
31
- signal?: AbortSignal,
32
- ) {
33
- const remote = await promptGitRemote(ctx, signal);
34
- if (!remote) return false;
35
- const destination = await promptGitDestination(ctx, signal);
36
- if (!destination) return false;
37
- const include = await chooseSetupContent(ctx, signal);
38
- if (!include) return false;
39
- const automatic = await chooseAutomaticSync(ctx, signal);
40
- if (automatic === undefined) return false;
41
- const saved = await saveReviewedDraft(
42
- ctx,
43
- "Review Git sync setup",
44
- [
45
- `Sync setup: ${safeTerminalText(targetName)}`,
46
- `Storage connection: ${safeTerminalText(targetName)} (Git)`,
47
- `Remote: ${safeTerminalText(remote)}`,
48
- `Sync branch: ${safeTerminalText(destination.branch)}`,
49
- `Storage location: ${safeTerminalText(destination.directory)}`,
50
- ...includedContentLines(include),
51
- `Automatic sync: ${automaticSyncSummary(automatic)}`,
52
- "Authentication: existing Git credential helper or SSH configuration (not stored by pi-sync).",
53
- "pi-sync manages the entire branch. The repository must already exist; a new branch is created on first push.",
54
- "Saving does not contact remote storage or start syncing.",
55
- ],
56
- "Save setup",
57
- (saveSignal) =>
58
- saveNewV3Settings(
59
- {
60
- setupName: targetName,
61
- connectionName: targetName,
62
- connection: { type: "git", remote },
63
- setup: {
64
- storage: {
65
- connection: targetName,
66
- branch: destination.branch,
67
- path: destination.directory,
68
- },
69
- sync: { include, automatic },
70
- },
71
- },
72
- saveSignal,
73
- ),
74
- signal,
75
- );
76
- if (saved && !signal?.aborted)
77
- ctx.ui.notify(
78
- `Saved Git sync setup “${safeTerminalText(targetName)}”. Choose Sync now to start.`,
79
- "info",
80
- );
81
- return saved;
24
+ export async function showGitSetup(ctx: ExtensionCommandContext, targetName: string, signal?: AbortSignal) {
25
+ const remote = await promptGitRemote(ctx, signal);
26
+ if (!remote) return false;
27
+ const destination = await promptGitDestination(ctx, signal);
28
+ if (!destination) return false;
29
+ const include = await chooseSetupContent(ctx, signal);
30
+ if (!include) return false;
31
+ const automatic = await chooseAutomaticSync(ctx, signal);
32
+ if (automatic === undefined) return false;
33
+ const saved = await saveReviewedDraft(
34
+ ctx,
35
+ "Review Git sync setup",
36
+ [
37
+ `Sync setup: ${safeTerminalText(targetName)}`,
38
+ `Storage connection: ${safeTerminalText(targetName)} (Git)`,
39
+ `Remote: ${safeTerminalText(remote)}`,
40
+ `Sync branch: ${safeTerminalText(destination.branch)}`,
41
+ `Storage location: ${safeTerminalText(destination.directory)}`,
42
+ ...includedContentLines(include),
43
+ `Automatic sync: ${automaticSyncSummary(automatic)}`,
44
+ "Authentication: existing Git credential helper or SSH configuration (not stored by pi-sync).",
45
+ "pi-sync manages the entire branch. The repository must already exist; a new branch is created on first push.",
46
+ "Saving does not contact remote storage or start syncing.",
47
+ ],
48
+ "Save setup",
49
+ (saveSignal) =>
50
+ saveNewV3Settings(
51
+ {
52
+ setupName: targetName,
53
+ connectionName: targetName,
54
+ connection: { type: "git", remote },
55
+ setup: {
56
+ storage: {
57
+ connection: targetName,
58
+ branch: destination.branch,
59
+ path: destination.directory,
60
+ },
61
+ sync: { include, automatic },
62
+ },
63
+ },
64
+ saveSignal,
65
+ ),
66
+ signal,
67
+ );
68
+ if (saved && !signal?.aborted)
69
+ ctx.ui.notify(`Saved Git sync setup “${safeTerminalText(targetName)}”. Choose Sync now to start.`, "info");
70
+ return saved;
82
71
  }
83
72
 
84
73
  export async function showAddGitStorageProfile(ctx: ExtensionCommandContext, signal?: AbortSignal) {
85
- const name = await promptResourceName(ctx, "storage connection", "git", signal);
86
- if (!name) return false;
87
- const remote = await promptGitRemote(ctx, signal);
88
- if (!remote) return false;
89
- const saved = await saveReviewedDraft(
90
- ctx,
91
- "Review storage connection",
92
- [
93
- `Name: ${safeTerminalText(name)}`,
94
- "Type: Git",
95
- `Remote: ${safeTerminalText(remote)}`,
96
- "Credentials: existing Git/SSH authentication (not stored)",
97
- "Adding a connection does not contact the remote or start syncing.",
98
- ],
99
- "Add storage connection",
100
- (saveSignal) => addStorageConnection(name, { type: "git", remote }, saveSignal),
101
- signal,
102
- );
103
- if (saved && !signal?.aborted)
104
- ctx.ui.notify(`Added storage connection “${safeTerminalText(name)}”.`, "info");
105
- return saved;
74
+ const name = await promptResourceName(ctx, "storage connection", "git", signal);
75
+ if (!name) return false;
76
+ const remote = await promptGitRemote(ctx, signal);
77
+ if (!remote) return false;
78
+ const saved = await saveReviewedDraft(
79
+ ctx,
80
+ "Review storage connection",
81
+ [
82
+ `Name: ${safeTerminalText(name)}`,
83
+ "Type: Git",
84
+ `Remote: ${safeTerminalText(remote)}`,
85
+ "Credentials: existing Git/SSH authentication (not stored)",
86
+ "Adding a connection does not contact the remote or start syncing.",
87
+ ],
88
+ "Add storage connection",
89
+ (saveSignal) => addStorageConnection(name, { type: "git", remote }, saveSignal),
90
+ signal,
91
+ );
92
+ if (saved && !signal?.aborted) ctx.ui.notify(`Added storage connection “${safeTerminalText(name)}”.`, "info");
93
+ return saved;
106
94
  }
107
95
 
108
96
  export async function showEditGitStorageProfile(
109
- ctx: ExtensionCommandContext,
110
- name: string,
111
- profile: Record<string, unknown>,
112
- signal?: AbortSignal,
113
- affectedSetups?: string[],
97
+ ctx: ExtensionCommandContext,
98
+ name: string,
99
+ profile: Record<string, unknown>,
100
+ signal?: AbortSignal,
101
+ affectedSetups?: string[],
114
102
  ) {
115
- const remote = await promptGitRemote(
116
- ctx,
117
- signal,
118
- typeof profile.remote === "string" ? profile.remote : undefined,
119
- );
120
- if (!remote) return false;
121
- const saved = await saveReviewedDraft(
122
- ctx,
123
- "Review storage connection",
124
- [
125
- `Storage connection: ${safeTerminalText(name)}`,
126
- `Remote: ${safeTerminalText(String(profile.remote ?? "missing"))} → ${safeTerminalText(remote)}`,
127
- `Affected sync setups: ${affectedSetups?.length ? affectedSetups.map(safeTerminalText).join(", ") : "None"}`,
128
- "Saving changes future storage access for every affected setup; it does not move or delete remote history.",
129
- ],
130
- "Save storage connection",
131
- (saveSignal) =>
132
- updateStorageConnection(
133
- name,
134
- (current) => {
135
- if (current.type !== "git" || current.remote !== profile.remote)
136
- throw new Error("Storage connection changed while it was open; reopen it.");
137
- return { ...current, remote };
138
- },
139
- affectedSetups,
140
- saveSignal,
141
- ),
142
- signal,
143
- );
144
- if (saved && !signal?.aborted)
145
- ctx.ui.notify(`Saved storage connection “${safeTerminalText(name)}”.`, "info");
146
- return saved;
103
+ const remote = await promptGitRemote(ctx, signal, typeof profile.remote === "string" ? profile.remote : undefined);
104
+ if (!remote) return false;
105
+ const saved = await saveReviewedDraft(
106
+ ctx,
107
+ "Review storage connection",
108
+ [
109
+ `Storage connection: ${safeTerminalText(name)}`,
110
+ `Remote: ${safeTerminalText(String(profile.remote ?? "missing"))} → ${safeTerminalText(remote)}`,
111
+ `Affected sync setups: ${affectedSetups?.length ? affectedSetups.map(safeTerminalText).join(", ") : "None"}`,
112
+ "Saving changes future storage access for every affected setup; it does not move or delete remote history.",
113
+ ],
114
+ "Save storage connection",
115
+ (saveSignal) =>
116
+ updateStorageConnection(
117
+ name,
118
+ (current) => {
119
+ if (current.type !== "git" || current.remote !== profile.remote)
120
+ throw new Error("Storage connection changed while it was open; reopen it.");
121
+ return { ...current, remote };
122
+ },
123
+ affectedSetups,
124
+ saveSignal,
125
+ ),
126
+ signal,
127
+ );
128
+ if (saved && !signal?.aborted) ctx.ui.notify(`Saved storage connection “${safeTerminalText(name)}”.`, "info");
129
+ return saved;
147
130
  }
148
131
 
149
132
  export async function showAddGitTarget(
150
- ctx: ExtensionCommandContext,
151
- name: string,
152
- profile: string,
153
- signal?: AbortSignal,
133
+ ctx: ExtensionCommandContext,
134
+ name: string,
135
+ profile: string,
136
+ signal?: AbortSignal,
154
137
  ) {
155
- const selected = await promptGitDestination(ctx, signal);
156
- if (!selected) return false;
157
- const storage = await promptAvailableSetupStorage(
158
- ctx,
159
- { connection: profile, branch: selected.branch, path: selected.directory },
160
- signal,
161
- );
162
- if (!storage) return false;
163
- const include = await chooseSetupContent(ctx, signal);
164
- if (!include) return false;
165
- const automatic = await chooseAutomaticSync(ctx, signal);
166
- if (automatic === undefined) return false;
167
- const connection = await readSetupConnection(profile, signal);
168
- if (connection?.type !== "git") throw new Error("Storage connection changed; reopen setup.");
169
- const saved = await saveReviewedDraft(
170
- ctx,
171
- "Review Git sync setup",
172
- [
173
- `Sync setup: ${safeTerminalText(name)}`,
174
- `Storage connection: ${safeTerminalText(profile)}`,
175
- `Remote: ${safeTerminalText(connection.remote)}`,
176
- `Sync branch: ${safeTerminalText(storage.branch)}`,
177
- `Storage location: ${safeTerminalText(storage.path)}`,
178
- ...includedContentLines(include),
179
- `Automatic sync: ${automaticSyncSummary(automatic)}`,
180
- "pi-sync manages the entire branch. Adding this setup does not sync or modify remote data.",
181
- ],
182
- "Add sync setup",
183
- (saveSignal) =>
184
- addSyncSetup(name, { storage, sync: { include, automatic } }, saveSignal, connection),
185
- signal,
186
- );
187
- if (saved && !signal?.aborted)
188
- ctx.ui.notify(`Added sync setup “${safeTerminalText(name)}”.`, "info");
189
- return saved;
138
+ const selected = await promptGitDestination(ctx, signal);
139
+ if (!selected) return false;
140
+ const storage = await promptAvailableSetupStorage(
141
+ ctx,
142
+ { connection: profile, branch: selected.branch, path: selected.directory },
143
+ signal,
144
+ );
145
+ if (!storage) return false;
146
+ const include = await chooseSetupContent(ctx, signal);
147
+ if (!include) return false;
148
+ const automatic = await chooseAutomaticSync(ctx, signal);
149
+ if (automatic === undefined) return false;
150
+ const connection = await readSetupConnection(profile, signal);
151
+ if (connection?.type !== "git") throw new Error("Storage connection changed; reopen setup.");
152
+ const saved = await saveReviewedDraft(
153
+ ctx,
154
+ "Review Git sync setup",
155
+ [
156
+ `Sync setup: ${safeTerminalText(name)}`,
157
+ `Storage connection: ${safeTerminalText(profile)}`,
158
+ `Remote: ${safeTerminalText(connection.remote)}`,
159
+ `Sync branch: ${safeTerminalText(storage.branch)}`,
160
+ `Storage location: ${safeTerminalText(storage.path)}`,
161
+ ...includedContentLines(include),
162
+ `Automatic sync: ${automaticSyncSummary(automatic)}`,
163
+ "pi-sync manages the entire branch. Adding this setup does not sync or modify remote data.",
164
+ ],
165
+ "Add sync setup",
166
+ (saveSignal) => addSyncSetup(name, { storage, sync: { include, automatic } }, saveSignal, connection),
167
+ signal,
168
+ );
169
+ if (saved && !signal?.aborted) ctx.ui.notify(`Added sync setup “${safeTerminalText(name)}”.`, "info");
170
+ return saved;
190
171
  }
191
172
 
192
- export async function showEditGitTarget(
193
- ctx: ExtensionCommandContext,
194
- partial: PartialConfig,
195
- signal?: AbortSignal,
196
- ) {
197
- let destination = await promptGitDestination(ctx, signal, partial);
198
- if (!destination) return false;
199
- while (destination.directory !== partial.storagePath && destination.branch === partial.branch) {
200
- ctx.ui.notify(
201
- "Changing a Git storage path requires a new Git branch so the existing branch remains readable.",
202
- "warning",
203
- );
204
- const branch = await requiredValueInput(
205
- ctx,
206
- "New Git branch\n\nKeep the previous branch unchanged; enter a new branch for this path.",
207
- "pi-sync/archive",
208
- signal,
209
- normalizeGitBranch,
210
- );
211
- if (!branch) return false;
212
- destination = { ...destination, branch };
213
- }
214
- const chosen = destination;
215
- const connection = await readSetupConnection(partial.connectionName, signal);
216
- if (connection.type !== "git") throw new Error("Storage connection changed; reopen setup.");
217
- const saved = await saveReviewedDraft(
218
- ctx,
219
- "Review sync setup",
220
- [
221
- `Sync setup: ${safeTerminalText(partial.setupName)}`,
222
- `Storage connection: ${safeTerminalText(partial.connectionName)}`,
223
- `Remote: ${safeTerminalText(connection.remote)}`,
224
- `Branch: ${safeTerminalText(partial.branch ?? "missing")} → ${safeTerminalText(chosen.branch)}`,
225
- `Storage path: ${safeTerminalText(partial.storagePath)} → ${safeTerminalText(chosen.directory)}`,
226
- "Saving changes the future storage location only; it does not move or delete remote history.",
227
- ],
228
- "Save sync setup",
229
- (saveSignal) =>
230
- updateSyncSetup(
231
- partial.setupName,
232
- (setup) => {
233
- if (typeof setup.storage.branch !== "string")
234
- throw new Error("Sync setup storage type changed; reopen it.");
235
- return {
236
- ...setup,
237
- storage: { ...setup.storage, branch: chosen.branch, path: chosen.directory },
238
- };
239
- },
240
- { expectedStorage: partial, expectedConnection: connection, signal: saveSignal },
241
- ),
242
- signal,
243
- );
244
- if (saved && !signal?.aborted)
245
- ctx.ui.notify(`Saved sync setup “${safeTerminalText(partial.setupName)}”.`, "info");
246
- return saved;
173
+ export async function showEditGitTarget(ctx: ExtensionCommandContext, partial: PartialConfig, signal?: AbortSignal) {
174
+ let destination = await promptGitDestination(ctx, signal, partial);
175
+ if (!destination) return false;
176
+ while (destination.directory !== partial.storagePath && destination.branch === partial.branch) {
177
+ ctx.ui.notify(
178
+ "Changing a Git storage path requires a new Git branch so the existing branch remains readable.",
179
+ "warning",
180
+ );
181
+ const branch = await requiredValueInput(
182
+ ctx,
183
+ "New Git branch\n\nKeep the previous branch unchanged; enter a new branch for this path.",
184
+ "pi-sync/archive",
185
+ signal,
186
+ normalizeGitBranch,
187
+ );
188
+ if (!branch) return false;
189
+ destination = { ...destination, branch };
190
+ }
191
+ const chosen = destination;
192
+ const connection = await readSetupConnection(partial.connectionName, signal);
193
+ if (connection.type !== "git") throw new Error("Storage connection changed; reopen setup.");
194
+ const saved = await saveReviewedDraft(
195
+ ctx,
196
+ "Review sync setup",
197
+ [
198
+ `Sync setup: ${safeTerminalText(partial.setupName)}`,
199
+ `Storage connection: ${safeTerminalText(partial.connectionName)}`,
200
+ `Remote: ${safeTerminalText(connection.remote)}`,
201
+ `Branch: ${safeTerminalText(partial.branch ?? "missing")} → ${safeTerminalText(chosen.branch)}`,
202
+ `Storage path: ${safeTerminalText(partial.storagePath)} → ${safeTerminalText(chosen.directory)}`,
203
+ "Saving changes the future storage location only; it does not move or delete remote history.",
204
+ ],
205
+ "Save sync setup",
206
+ (saveSignal) =>
207
+ updateSyncSetup(
208
+ partial.setupName,
209
+ (setup) => {
210
+ if (typeof setup.storage.branch !== "string") throw new Error("Sync setup storage type changed; reopen it.");
211
+ return {
212
+ ...setup,
213
+ storage: { ...setup.storage, branch: chosen.branch, path: chosen.directory },
214
+ };
215
+ },
216
+ { expectedStorage: partial, expectedConnection: connection, signal: saveSignal },
217
+ ),
218
+ signal,
219
+ );
220
+ if (saved && !signal?.aborted) ctx.ui.notify(`Saved sync setup “${safeTerminalText(partial.setupName)}”.`, "info");
221
+ return saved;
247
222
  }
248
223
 
249
- async function promptGitRemote(
250
- ctx: ExtensionCommandContext,
251
- signal?: AbortSignal,
252
- current?: string,
253
- ) {
254
- const title =
255
- "Git remote URL (SSH or HTTPS)\n\nUse an existing private repository with Git/SSH authentication already configured.";
256
- const validate = (value: string) => {
257
- const remote = normalizeGitRemote(value);
258
- if (!remote) throw new Error("Git remote URL is required.");
259
- return remote;
260
- };
261
- return current
262
- ? requiredInput(ctx, title, current, signal, validate)
263
- : requiredValueInput(
264
- ctx,
265
- title,
266
- "git@github.com:owner/private-pi-sync.git (SSH) or https://github.com/owner/private-pi-sync.git (HTTPS)",
267
- signal,
268
- validate,
269
- );
224
+ async function promptGitRemote(ctx: ExtensionCommandContext, signal?: AbortSignal, current?: string) {
225
+ const title =
226
+ "Git remote URL (SSH or HTTPS)\n\nUse an existing private repository with Git/SSH authentication already configured.";
227
+ const validate = (value: string) => {
228
+ const remote = normalizeGitRemote(value);
229
+ if (!remote) throw new Error("Git remote URL is required.");
230
+ return remote;
231
+ };
232
+ return current
233
+ ? requiredInput(ctx, title, current, signal, validate)
234
+ : requiredValueInput(
235
+ ctx,
236
+ title,
237
+ "git@github.com:owner/private-pi-sync.git (SSH) or https://github.com/owner/private-pi-sync.git (HTTPS)",
238
+ signal,
239
+ validate,
240
+ );
270
241
  }
271
242
 
272
243
  async function promptGitDestination(
273
- ctx: ExtensionCommandContext,
274
- signal?: AbortSignal,
275
- current: Partial<PartialConfig> = {},
244
+ ctx: ExtensionCommandContext,
245
+ signal?: AbortSignal,
246
+ current: Partial<PartialConfig> = {},
276
247
  ) {
277
- const branch = await requiredInput(
278
- ctx,
279
- "Git branch for sync snapshots\n\npi-sync manages this entire branch, not your local working branch.\nUse a new branch or one already used by pi-sync; unrelated content is rejected.",
280
- current.branch ?? "main",
281
- signal,
282
- normalizeGitBranch,
283
- );
284
- if (!branch) return undefined;
285
- const directory = await requiredInput(
286
- ctx,
287
- "Git storage path\n\nRelative to the repository root, not your local filesystem.\n./ stores manifest.json and files/ at the repository root.",
288
- current.storagePath ?? "./",
289
- signal,
290
- normalizeGitDirectory,
291
- );
292
- return directory ? { branch, directory } : undefined;
248
+ const branch = await requiredInput(
249
+ ctx,
250
+ "Git branch for sync snapshots\n\npi-sync manages this entire branch, not your local working branch.\nUse a new branch or one already used by pi-sync; unrelated content is rejected.",
251
+ current.branch ?? "main",
252
+ signal,
253
+ normalizeGitBranch,
254
+ );
255
+ if (!branch) return undefined;
256
+ const directory = await requiredInput(
257
+ ctx,
258
+ "Git storage path\n\nRelative to the repository root, not your local filesystem.\n./ stores manifest.json and files/ at the repository root.",
259
+ current.storagePath ?? "./",
260
+ signal,
261
+ normalizeGitDirectory,
262
+ );
263
+ return directory ? { branch, directory } : undefined;
293
264
  }