pushwork 1.0.4 → 1.0.7

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 (195) hide show
  1. package/README.md +87 -328
  2. package/dist/.pushwork/automerge/3P/Dm3ekE2pmjGnWvDaG3vSR7ww98/snapshot/aa2349c94955ea561f698720142f9d884a6872d9f82dc332d578c216beb0df0e +0 -0
  3. package/dist/.pushwork/automerge/st/orage-adapter-id +1 -0
  4. package/dist/.pushwork/config.json +15 -0
  5. package/dist/.pushwork/snapshot.json +7 -0
  6. package/dist/cli.js +231 -170
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands.d.ts +51 -0
  9. package/dist/commands.d.ts.map +1 -0
  10. package/dist/commands.js +799 -0
  11. package/dist/commands.js.map +1 -0
  12. package/dist/core/change-detection.d.ts +6 -19
  13. package/dist/core/change-detection.d.ts.map +1 -1
  14. package/dist/core/change-detection.js +101 -80
  15. package/dist/core/change-detection.js.map +1 -1
  16. package/dist/{config/index.d.ts → core/config.d.ts} +13 -3
  17. package/dist/core/config.d.ts.map +1 -0
  18. package/dist/{config/index.js → core/config.js} +55 -73
  19. package/dist/core/config.js.map +1 -0
  20. package/dist/core/index.d.ts +1 -0
  21. package/dist/core/index.d.ts.map +1 -1
  22. package/dist/core/index.js +1 -1
  23. package/dist/core/index.js.map +1 -1
  24. package/dist/core/move-detection.d.ts +12 -50
  25. package/dist/core/move-detection.d.ts.map +1 -1
  26. package/dist/core/move-detection.js +58 -139
  27. package/dist/core/move-detection.js.map +1 -1
  28. package/dist/core/snapshot.d.ts +0 -4
  29. package/dist/core/snapshot.d.ts.map +1 -1
  30. package/dist/core/snapshot.js +2 -11
  31. package/dist/core/snapshot.js.map +1 -1
  32. package/dist/core/sync-engine.d.ts +5 -11
  33. package/dist/core/sync-engine.d.ts.map +1 -1
  34. package/dist/core/sync-engine.js +220 -362
  35. package/dist/core/sync-engine.js.map +1 -1
  36. package/dist/index.d.ts +0 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +0 -6
  39. package/dist/index.js.map +1 -1
  40. package/dist/types/config.d.ts +43 -67
  41. package/dist/types/config.d.ts.map +1 -1
  42. package/dist/types/config.js +6 -0
  43. package/dist/types/config.js.map +1 -1
  44. package/dist/types/documents.d.ts +15 -3
  45. package/dist/types/documents.d.ts.map +1 -1
  46. package/dist/types/documents.js.map +1 -1
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/types/index.js +0 -3
  49. package/dist/types/index.js.map +1 -1
  50. package/dist/types/snapshot.d.ts +3 -21
  51. package/dist/types/snapshot.d.ts.map +1 -1
  52. package/dist/types/snapshot.js +0 -14
  53. package/dist/types/snapshot.js.map +1 -1
  54. package/dist/utils/content.d.ts.map +1 -1
  55. package/dist/utils/content.js +2 -6
  56. package/dist/utils/content.js.map +1 -1
  57. package/dist/utils/directory.d.ts +10 -0
  58. package/dist/utils/directory.d.ts.map +1 -0
  59. package/dist/utils/directory.js +37 -0
  60. package/dist/utils/directory.js.map +1 -0
  61. package/dist/utils/fs.d.ts +15 -2
  62. package/dist/utils/fs.d.ts.map +1 -1
  63. package/dist/utils/fs.js +63 -53
  64. package/dist/utils/fs.js.map +1 -1
  65. package/dist/utils/index.d.ts +1 -1
  66. package/dist/utils/index.d.ts.map +1 -1
  67. package/dist/utils/index.js +1 -4
  68. package/dist/utils/index.js.map +1 -1
  69. package/dist/utils/mime-types.d.ts.map +1 -1
  70. package/dist/utils/mime-types.js +11 -4
  71. package/dist/utils/mime-types.js.map +1 -1
  72. package/dist/utils/network-sync.d.ts +0 -6
  73. package/dist/utils/network-sync.d.ts.map +1 -1
  74. package/dist/utils/network-sync.js +55 -99
  75. package/dist/utils/network-sync.js.map +1 -1
  76. package/dist/utils/output.d.ts +129 -0
  77. package/dist/utils/output.d.ts.map +1 -0
  78. package/dist/utils/output.js +375 -0
  79. package/dist/utils/output.js.map +1 -0
  80. package/dist/utils/repo-factory.d.ts +2 -6
  81. package/dist/utils/repo-factory.d.ts.map +1 -1
  82. package/dist/utils/repo-factory.js +8 -22
  83. package/dist/utils/repo-factory.js.map +1 -1
  84. package/dist/utils/string-similarity.d.ts +14 -0
  85. package/dist/utils/string-similarity.d.ts.map +1 -0
  86. package/dist/utils/string-similarity.js +43 -0
  87. package/dist/utils/string-similarity.js.map +1 -0
  88. package/dist/utils/trace.d.ts +19 -0
  89. package/dist/utils/trace.d.ts.map +1 -0
  90. package/dist/utils/trace.js +68 -0
  91. package/dist/utils/trace.js.map +1 -0
  92. package/package.json +17 -12
  93. package/src/cli.ts +326 -252
  94. package/src/commands.ts +988 -0
  95. package/src/core/change-detection.ts +199 -162
  96. package/src/{config/index.ts → core/config.ts} +65 -82
  97. package/src/core/index.ts +1 -1
  98. package/src/core/move-detection.ts +74 -180
  99. package/src/core/snapshot.ts +2 -12
  100. package/src/core/sync-engine.ts +248 -499
  101. package/src/index.ts +0 -10
  102. package/src/types/config.ts +50 -72
  103. package/src/types/documents.ts +16 -3
  104. package/src/types/index.ts +0 -5
  105. package/src/types/snapshot.ts +1 -23
  106. package/src/utils/content.ts +2 -6
  107. package/src/utils/directory.ts +50 -0
  108. package/src/utils/fs.ts +67 -56
  109. package/src/utils/index.ts +1 -6
  110. package/src/utils/mime-types.ts +12 -4
  111. package/src/utils/network-sync.ts +79 -137
  112. package/src/utils/output.ts +450 -0
  113. package/src/utils/repo-factory.ts +13 -31
  114. package/src/utils/string-similarity.ts +54 -0
  115. package/src/utils/trace.ts +70 -0
  116. package/test/integration/exclude-patterns.test.ts +6 -15
  117. package/test/integration/fuzzer.test.ts +308 -391
  118. package/test/integration/init-sync.test.ts +89 -0
  119. package/test/integration/sync-deletion.test.ts +2 -61
  120. package/test/integration/sync-flow.test.ts +4 -24
  121. package/test/jest.setup.ts +34 -0
  122. package/test/unit/deletion-behavior.test.ts +3 -14
  123. package/test/unit/enhanced-mime-detection.test.ts +0 -22
  124. package/test/unit/snapshot.test.ts +2 -29
  125. package/test/unit/sync-convergence.test.ts +3 -198
  126. package/test/unit/sync-timing.test.ts +0 -44
  127. package/test/unit/utils.test.ts +0 -2
  128. package/tsconfig.json +3 -3
  129. package/dist/browser/browser-sync-engine.d.ts +0 -64
  130. package/dist/browser/browser-sync-engine.d.ts.map +0 -1
  131. package/dist/browser/browser-sync-engine.js +0 -303
  132. package/dist/browser/browser-sync-engine.js.map +0 -1
  133. package/dist/browser/filesystem-adapter.d.ts +0 -84
  134. package/dist/browser/filesystem-adapter.d.ts.map +0 -1
  135. package/dist/browser/filesystem-adapter.js +0 -413
  136. package/dist/browser/filesystem-adapter.js.map +0 -1
  137. package/dist/browser/index.d.ts +0 -36
  138. package/dist/browser/index.d.ts.map +0 -1
  139. package/dist/browser/index.js +0 -90
  140. package/dist/browser/index.js.map +0 -1
  141. package/dist/browser/types.d.ts +0 -70
  142. package/dist/browser/types.d.ts.map +0 -1
  143. package/dist/browser/types.js +0 -6
  144. package/dist/browser/types.js.map +0 -1
  145. package/dist/cli/commands.d.ts +0 -77
  146. package/dist/cli/commands.d.ts.map +0 -1
  147. package/dist/cli/commands.js +0 -904
  148. package/dist/cli/commands.js.map +0 -1
  149. package/dist/cli/index.d.ts +0 -2
  150. package/dist/cli/index.d.ts.map +0 -1
  151. package/dist/cli/index.js +0 -19
  152. package/dist/cli/index.js.map +0 -1
  153. package/dist/config/index.d.ts.map +0 -1
  154. package/dist/config/index.js.map +0 -1
  155. package/dist/core/isomorphic-snapshot.d.ts +0 -58
  156. package/dist/core/isomorphic-snapshot.d.ts.map +0 -1
  157. package/dist/core/isomorphic-snapshot.js +0 -204
  158. package/dist/core/isomorphic-snapshot.js.map +0 -1
  159. package/dist/platform/browser-filesystem.d.ts +0 -26
  160. package/dist/platform/browser-filesystem.d.ts.map +0 -1
  161. package/dist/platform/browser-filesystem.js +0 -91
  162. package/dist/platform/browser-filesystem.js.map +0 -1
  163. package/dist/platform/filesystem.d.ts +0 -29
  164. package/dist/platform/filesystem.d.ts.map +0 -1
  165. package/dist/platform/filesystem.js +0 -65
  166. package/dist/platform/filesystem.js.map +0 -1
  167. package/dist/platform/node-filesystem.d.ts +0 -21
  168. package/dist/platform/node-filesystem.d.ts.map +0 -1
  169. package/dist/platform/node-filesystem.js +0 -93
  170. package/dist/platform/node-filesystem.js.map +0 -1
  171. package/dist/utils/content-similarity.d.ts +0 -53
  172. package/dist/utils/content-similarity.d.ts.map +0 -1
  173. package/dist/utils/content-similarity.js +0 -155
  174. package/dist/utils/content-similarity.js.map +0 -1
  175. package/dist/utils/fs-browser.d.ts +0 -57
  176. package/dist/utils/fs-browser.d.ts.map +0 -1
  177. package/dist/utils/fs-browser.js +0 -311
  178. package/dist/utils/fs-browser.js.map +0 -1
  179. package/dist/utils/fs-node.d.ts +0 -53
  180. package/dist/utils/fs-node.d.ts.map +0 -1
  181. package/dist/utils/fs-node.js +0 -220
  182. package/dist/utils/fs-node.js.map +0 -1
  183. package/dist/utils/isomorphic.d.ts +0 -29
  184. package/dist/utils/isomorphic.d.ts.map +0 -1
  185. package/dist/utils/isomorphic.js +0 -139
  186. package/dist/utils/isomorphic.js.map +0 -1
  187. package/dist/utils/pure.d.ts +0 -25
  188. package/dist/utils/pure.d.ts.map +0 -1
  189. package/dist/utils/pure.js +0 -112
  190. package/dist/utils/pure.js.map +0 -1
  191. package/src/cli/commands.ts +0 -1207
  192. package/src/cli/index.ts +0 -2
  193. package/src/utils/content-similarity.ts +0 -194
  194. package/test/README-TESTING-GAPS.md +0 -174
  195. package/test/unit/content-similarity.test.ts +0 -236
@@ -1,904 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.ProgressMessages = void 0;
40
- exports.setupCommandContext = setupCommandContext;
41
- exports.safeRepoShutdown = safeRepoShutdown;
42
- exports.init = init;
43
- exports.sync = sync;
44
- exports.diff = diff;
45
- exports.status = status;
46
- exports.log = log;
47
- exports.checkout = checkout;
48
- exports.clone = clone;
49
- exports.url = url;
50
- exports.commit = commit;
51
- exports.debug = debug;
52
- const path = __importStar(require("path"));
53
- const fs = __importStar(require("fs/promises"));
54
- const chalk_1 = __importDefault(require("chalk"));
55
- const ora_1 = __importDefault(require("ora"));
56
- const diffLib = __importStar(require("diff"));
57
- const core_1 = require("../core");
58
- const utils_1 = require("../utils");
59
- const config_1 = require("../config");
60
- const repo_factory_1 = require("../utils/repo-factory");
61
- /**
62
- * Shared pre-action that ensures repository and sync engine are properly initialized
63
- * This function always works, with or without network connectivity
64
- */
65
- async function setupCommandContext(workingDir = process.cwd(), customSyncServer, customStorageId, enableNetwork = true) {
66
- const resolvedPath = path.resolve(workingDir);
67
- // Check if initialized
68
- const syncToolDir = path.join(resolvedPath, ".pushwork");
69
- if (!(await (0, utils_1.pathExists)(syncToolDir))) {
70
- throw new Error('Directory not initialized for sync. Run "pushwork init" first.');
71
- }
72
- // Load configuration
73
- const configManager = new config_1.ConfigManager(resolvedPath);
74
- const config = await configManager.getMerged();
75
- // Create repo with configurable network setting
76
- const repo = await (0, repo_factory_1.createRepo)(resolvedPath, {
77
- enableNetwork,
78
- syncServer: customSyncServer,
79
- syncServerStorageId: customStorageId,
80
- });
81
- // Create sync engine with configurable network sync
82
- const syncEngine = new core_1.SyncEngine(repo, resolvedPath, config.defaults.exclude_patterns, enableNetwork, config.sync_server_storage_id);
83
- return {
84
- repo,
85
- syncEngine,
86
- config,
87
- workingDir: resolvedPath,
88
- };
89
- }
90
- /**
91
- * Safely shutdown a repository with proper error handling
92
- */
93
- async function safeRepoShutdown(repo, context) {
94
- try {
95
- await repo.shutdown();
96
- }
97
- catch (shutdownError) {
98
- // WebSocket errors during shutdown are common and non-critical
99
- // Silently ignore them - they don't affect data integrity
100
- const errorMessage = shutdownError instanceof Error
101
- ? shutdownError.message
102
- : String(shutdownError);
103
- // Ignore WebSocket-related errors entirely
104
- if (errorMessage.includes("WebSocket") ||
105
- errorMessage.includes("connection was established") ||
106
- errorMessage.includes("was closed")) {
107
- // Silently ignore WebSocket shutdown errors
108
- return;
109
- }
110
- // Only warn about truly unexpected shutdown errors
111
- console.warn(`Warning: Repository shutdown failed${context ? ` (${context})` : ""}: ${shutdownError}`);
112
- }
113
- }
114
- /**
115
- * Common progress message helpers
116
- */
117
- exports.ProgressMessages = {
118
- // Setup messages
119
- directoryFound: () => console.log(chalk_1.default.gray(" ✓ Sync directory found")),
120
- configLoaded: () => console.log(chalk_1.default.gray(" ✓ Configuration loaded")),
121
- repoConnected: () => console.log(chalk_1.default.gray(" ✓ Connected to repository")),
122
- // Configuration display
123
- syncServer: (server) => console.log(chalk_1.default.gray(` ✓ Sync server: ${server}`)),
124
- storageId: (id) => console.log(chalk_1.default.gray(` ✓ Storage ID: ${id}`)),
125
- rootUrl: (url) => console.log(chalk_1.default.gray(` ✓ Root URL: ${url}`)),
126
- // Operation completion
127
- changesWritten: () => console.log(chalk_1.default.gray(" ✓ All changes written to disk")),
128
- syncCompleted: (duration) => console.log(chalk_1.default.gray(` ✓ Initial sync completed in ${duration}ms`)),
129
- directoryStructureCreated: () => console.log(chalk_1.default.gray(" ✓ Created sync directory structure")),
130
- configSaved: () => console.log(chalk_1.default.gray(" ✓ Saved configuration")),
131
- repoCreated: () => console.log(chalk_1.default.gray(" ✓ Created Automerge repository")),
132
- };
133
- /**
134
- * Show actual content diff for a changed file
135
- */
136
- async function showContentDiff(change) {
137
- try {
138
- // Get old content (from snapshot/remote)
139
- const oldContent = change.remoteContent || "";
140
- // Get new content (current local)
141
- const newContent = change.localContent || "";
142
- // Convert binary content to string representation if needed
143
- const oldText = typeof oldContent === "string"
144
- ? oldContent
145
- : `<binary content: ${oldContent.length} bytes>`;
146
- const newText = typeof newContent === "string"
147
- ? newContent
148
- : `<binary content: ${newContent.length} bytes>`;
149
- // Generate unified diff
150
- const diffResult = diffLib.createPatch(change.path, oldText, newText, "previous", "current");
151
- // Skip the header lines and process the diff
152
- const lines = diffResult.split("\n").slice(4); // Skip index, ===, ---, +++ lines
153
- if (lines.length === 0 || (lines.length === 1 && lines[0] === "")) {
154
- console.log(chalk_1.default.gray(" (content identical)"));
155
- return;
156
- }
157
- for (const line of lines) {
158
- if (line.startsWith("@@")) {
159
- // Hunk header
160
- console.log(chalk_1.default.cyan(line));
161
- }
162
- else if (line.startsWith("+")) {
163
- // Added line
164
- console.log(chalk_1.default.green(line));
165
- }
166
- else if (line.startsWith("-")) {
167
- // Removed line
168
- console.log(chalk_1.default.red(line));
169
- }
170
- else if (line.startsWith(" ")) {
171
- // Context line
172
- console.log(chalk_1.default.gray(line));
173
- }
174
- else if (line === "") {
175
- // Empty line
176
- console.log("");
177
- }
178
- }
179
- }
180
- catch (error) {
181
- console.log(chalk_1.default.gray(` (diff error: ${error})`));
182
- }
183
- }
184
- /**
185
- * Initialize sync in a directory
186
- */
187
- async function init(targetPath, syncServer, syncServerStorageId) {
188
- const spinner = (0, ora_1.default)("Starting initialization...").start();
189
- try {
190
- const resolvedPath = path.resolve(targetPath);
191
- // Step 1: Directory setup
192
- spinner.text = "Setting up directory structure...";
193
- await (0, utils_1.ensureDirectoryExists)(resolvedPath);
194
- // Check if already initialized
195
- const syncToolDir = path.join(resolvedPath, ".pushwork");
196
- if (await (0, utils_1.pathExists)(syncToolDir)) {
197
- spinner.fail("Directory already initialized for sync");
198
- return;
199
- }
200
- // Step 2: Create sync directories
201
- spinner.text = "Creating .pushwork directory...";
202
- await (0, utils_1.ensureDirectoryExists)(syncToolDir);
203
- await (0, utils_1.ensureDirectoryExists)(path.join(syncToolDir, "automerge"));
204
- exports.ProgressMessages.directoryStructureCreated();
205
- // Step 3: Configuration setup
206
- spinner.text = "Setting up configuration...";
207
- const configManager = new config_1.ConfigManager(resolvedPath);
208
- const defaultSyncServer = syncServer || "wss://sync3.automerge.org";
209
- const defaultStorageId = syncServerStorageId || "3760df37-a4c6-4f66-9ecd-732039a9385d";
210
- const config = {
211
- sync_server: defaultSyncServer,
212
- sync_server_storage_id: defaultStorageId,
213
- sync_enabled: true,
214
- defaults: {
215
- exclude_patterns: [".git", "node_modules", "*.tmp", ".pushwork"],
216
- large_file_threshold: "100MB",
217
- },
218
- diff: {
219
- show_binary: false,
220
- },
221
- sync: {
222
- move_detection_threshold: 0.8,
223
- prompt_threshold: 0.5,
224
- auto_sync: false,
225
- parallel_operations: 4,
226
- },
227
- };
228
- await configManager.save(config);
229
- exports.ProgressMessages.configSaved();
230
- exports.ProgressMessages.syncServer(defaultSyncServer);
231
- exports.ProgressMessages.storageId(defaultStorageId);
232
- // Step 4: Initialize Automerge repo and create root directory document
233
- spinner.text = "Creating root directory document...";
234
- const repo = await (0, repo_factory_1.createRepo)(resolvedPath, {
235
- enableNetwork: true,
236
- syncServer: syncServer,
237
- syncServerStorageId: syncServerStorageId,
238
- });
239
- // Create the root directory document
240
- const rootDoc = {
241
- "@patchwork": { type: "folder" },
242
- docs: [],
243
- };
244
- const rootHandle = repo.create(rootDoc);
245
- exports.ProgressMessages.repoCreated();
246
- exports.ProgressMessages.rootUrl(rootHandle.url);
247
- // Step 5: Scan existing files
248
- spinner.text = "Scanning existing files...";
249
- const syncEngine = new core_1.SyncEngine(repo, resolvedPath, config.defaults.exclude_patterns, true, // Network sync enabled for init
250
- config.sync_server_storage_id);
251
- // Get file count for progress
252
- const dirEntries = await fs.readdir(resolvedPath, { withFileTypes: true });
253
- const fileCount = dirEntries.filter((dirent) => dirent.isFile()).length;
254
- if (fileCount > 0) {
255
- console.log(chalk_1.default.gray(` ✓ Found ${fileCount} existing files`));
256
- spinner.text = `Creating initial snapshot with ${fileCount} files...`;
257
- }
258
- else {
259
- spinner.text = "Creating initial empty snapshot...";
260
- }
261
- // Step 6: Set the root directory URL before creating initial snapshot
262
- await syncEngine.setRootDirectoryUrl(rootHandle.url);
263
- // Step 7: Create initial snapshot
264
- spinner.text = "Creating initial snapshot...";
265
- const startTime = Date.now();
266
- await syncEngine.sync(false);
267
- const duration = Date.now() - startTime;
268
- exports.ProgressMessages.syncCompleted(duration);
269
- // Step 8: Ensure all Automerge operations are flushed to disk
270
- spinner.text = "Flushing changes to disk...";
271
- await safeRepoShutdown(repo, "init");
272
- exports.ProgressMessages.changesWritten();
273
- spinner.succeed(`Initialized sync in ${chalk_1.default.green(resolvedPath)}`);
274
- console.log(`\n${chalk_1.default.bold("🎉 Sync Directory Created!")}`);
275
- console.log(` 📁 Directory: ${chalk_1.default.blue(resolvedPath)}`);
276
- console.log(` 🔗 Sync server: ${chalk_1.default.blue(defaultSyncServer)}`);
277
- console.log(`\n${chalk_1.default.green("Initialization complete!")} Run ${chalk_1.default.cyan("pushwork sync")} to start syncing.`);
278
- }
279
- catch (error) {
280
- spinner.fail(`Failed to initialize: ${error}`);
281
- throw error;
282
- }
283
- }
284
- /**
285
- * Run bidirectional sync
286
- */
287
- async function sync(options) {
288
- const spinner = (0, ora_1.default)("Starting sync operation...").start();
289
- try {
290
- // Step 1: Setup shared context
291
- spinner.text = "Setting up sync context...";
292
- const { repo, syncEngine, config, workingDir } = await setupCommandContext();
293
- exports.ProgressMessages.directoryFound();
294
- exports.ProgressMessages.configLoaded();
295
- exports.ProgressMessages.syncServer(config?.sync_server || "wss://sync3.automerge.org");
296
- exports.ProgressMessages.repoConnected();
297
- // Show root directory URL for context
298
- const syncStatus = await syncEngine.getStatus();
299
- if (syncStatus.snapshot?.rootDirectoryUrl) {
300
- exports.ProgressMessages.rootUrl(syncStatus.snapshot.rootDirectoryUrl);
301
- }
302
- if (options.dryRun) {
303
- // Dry run mode - detailed preview
304
- spinner.text = "Analyzing changes (dry run)...";
305
- const startTime = Date.now();
306
- const preview = await syncEngine.previewChanges();
307
- const analysisTime = Date.now() - startTime;
308
- spinner.succeed("Change analysis completed");
309
- console.log(`\n${chalk_1.default.bold("📊 Change Analysis")} (${analysisTime}ms):`);
310
- console.log(chalk_1.default.gray(` Directory: ${workingDir}`));
311
- console.log(chalk_1.default.gray(` Analysis time: ${analysisTime}ms`));
312
- if (preview.changes.length === 0 && preview.moves.length === 0) {
313
- console.log(`\n${chalk_1.default.green("✨ No changes detected")} - everything is in sync!`);
314
- return;
315
- }
316
- console.log(`\n${chalk_1.default.bold("📋 Summary:")}`);
317
- console.log(` ${preview.summary}`);
318
- if (preview.changes.length > 0) {
319
- const localChanges = preview.changes.filter((c) => c.changeType === "local_only" || c.changeType === "both_changed").length;
320
- const remoteChanges = preview.changes.filter((c) => c.changeType === "remote_only" || c.changeType === "both_changed").length;
321
- const conflicts = preview.changes.filter((c) => c.changeType === "both_changed").length;
322
- console.log(`\n${chalk_1.default.bold("📁 File Changes:")} (${preview.changes.length} total)`);
323
- if (localChanges > 0) {
324
- console.log(` ${chalk_1.default.green("📤")} Local changes: ${localChanges}`);
325
- }
326
- if (remoteChanges > 0) {
327
- console.log(` ${chalk_1.default.blue("📥")} Remote changes: ${remoteChanges}`);
328
- }
329
- if (conflicts > 0) {
330
- console.log(` ${chalk_1.default.yellow("⚠️")} Conflicts: ${conflicts}`);
331
- }
332
- console.log(`\n${chalk_1.default.bold("📄 Changed Files:")}`);
333
- for (const change of preview.changes.slice(0, 10)) {
334
- // Show first 10
335
- const typeIcon = change.changeType === "local_only"
336
- ? chalk_1.default.green("📤")
337
- : change.changeType === "remote_only"
338
- ? chalk_1.default.blue("📥")
339
- : change.changeType === "both_changed"
340
- ? chalk_1.default.yellow("⚠️")
341
- : chalk_1.default.gray("➖");
342
- console.log(` ${typeIcon} ${change.path}`);
343
- }
344
- if (preview.changes.length > 10) {
345
- console.log(` ${chalk_1.default.gray(`... and ${preview.changes.length - 10} more files`)}`);
346
- }
347
- }
348
- if (preview.moves.length > 0) {
349
- console.log(`\n${chalk_1.default.bold("🔄 Potential Moves:")} (${preview.moves.length})`);
350
- for (const move of preview.moves.slice(0, 5)) {
351
- // Show first 5
352
- const confidence = move.confidence === "auto"
353
- ? chalk_1.default.green("Auto")
354
- : move.confidence === "prompt"
355
- ? chalk_1.default.yellow("Prompt")
356
- : chalk_1.default.red("Low");
357
- console.log(` 🔄 ${move.fromPath} → ${move.toPath} (${confidence})`);
358
- }
359
- if (preview.moves.length > 5) {
360
- console.log(` ${chalk_1.default.gray(`... and ${preview.moves.length - 5} more moves`)}`);
361
- }
362
- }
363
- console.log(`\n${chalk_1.default.cyan("ℹ️ Run without --dry-run to apply these changes")}`);
364
- }
365
- else {
366
- // Actual sync operation
367
- spinner.text = "Detecting changes...";
368
- const startTime = Date.now();
369
- const result = await syncEngine.sync(false);
370
- const totalTime = Date.now() - startTime;
371
- if (result.success) {
372
- spinner.succeed(`Sync completed in ${totalTime}ms`);
373
- console.log(`\n${chalk_1.default.bold("✅ Sync Results:")}`);
374
- console.log(` 📄 Files changed: ${chalk_1.default.yellow(result.filesChanged)}`);
375
- console.log(` 📁 Directories changed: ${chalk_1.default.yellow(result.directoriesChanged)}`);
376
- console.log(` ⏱️ Total time: ${chalk_1.default.gray(totalTime + "ms")}`);
377
- if (result.warnings.length > 0) {
378
- console.log(`\n${chalk_1.default.yellow("⚠️ Warnings:")} (${result.warnings.length})`);
379
- for (const warning of result.warnings.slice(0, 5)) {
380
- console.log(` ${chalk_1.default.yellow("⚠️")} ${warning}`);
381
- }
382
- if (result.warnings.length > 5) {
383
- console.log(` ${chalk_1.default.gray(`... and ${result.warnings.length - 5} more warnings`)}`);
384
- }
385
- }
386
- if (result.filesChanged === 0 && result.directoriesChanged === 0) {
387
- console.log(`\n${chalk_1.default.green("✨ Everything already in sync!")}`);
388
- }
389
- // Ensure all changes are flushed to disk
390
- spinner.text = "Flushing changes to disk...";
391
- await safeRepoShutdown(repo, "sync");
392
- exports.ProgressMessages.changesWritten();
393
- }
394
- else {
395
- spinner.fail("Sync completed with errors");
396
- console.log(`\n${chalk_1.default.red("❌ Sync Errors:")} (${result.errors.length})`);
397
- for (const error of result.errors.slice(0, 5)) {
398
- console.log(` ${chalk_1.default.red("❌")} ${error.path}: ${error.error.message}`);
399
- }
400
- if (result.errors.length > 5) {
401
- console.log(` ${chalk_1.default.gray(`... and ${result.errors.length - 5} more errors`)}`);
402
- }
403
- if (result.filesChanged > 0 || result.directoriesChanged > 0) {
404
- console.log(`\n${chalk_1.default.yellow("⚠️ Partial sync completed:")}`);
405
- console.log(` 📄 Files changed: ${result.filesChanged}`);
406
- console.log(` 📁 Directories changed: ${result.directoriesChanged}`);
407
- }
408
- // Still try to flush any partial changes
409
- await safeRepoShutdown(repo, "sync-error");
410
- }
411
- }
412
- }
413
- catch (error) {
414
- spinner.fail(`Sync failed: ${error}`);
415
- throw error;
416
- }
417
- }
418
- /**
419
- * Show differences between local and remote
420
- */
421
- async function diff(targetPath = ".", options) {
422
- try {
423
- // Setup shared context with network disabled for diff check
424
- const { repo, syncEngine } = await setupCommandContext(targetPath, undefined, undefined, false);
425
- const preview = await syncEngine.previewChanges();
426
- if (options.nameOnly) {
427
- // Show only file names
428
- for (const change of preview.changes) {
429
- console.log(change.path);
430
- }
431
- return;
432
- }
433
- // Show root directory URL for context
434
- const diffStatus = await syncEngine.getStatus();
435
- if (diffStatus.snapshot?.rootDirectoryUrl) {
436
- console.log(chalk_1.default.gray(`Root URL: ${diffStatus.snapshot.rootDirectoryUrl}`));
437
- console.log("");
438
- }
439
- if (preview.changes.length === 0) {
440
- console.log(chalk_1.default.green("No changes detected"));
441
- return;
442
- }
443
- console.log(chalk_1.default.bold("Differences:"));
444
- for (const change of preview.changes) {
445
- const typeLabel = change.changeType === "local_only"
446
- ? chalk_1.default.green("[LOCAL]")
447
- : change.changeType === "remote_only"
448
- ? chalk_1.default.blue("[REMOTE]")
449
- : change.changeType === "both_changed"
450
- ? chalk_1.default.yellow("[CONFLICT]")
451
- : chalk_1.default.gray("[NO CHANGE]");
452
- console.log(`\n${typeLabel} ${change.path}`);
453
- if (options.tool) {
454
- console.log(` Use "${options.tool}" to view detailed diff`);
455
- }
456
- else {
457
- // Show actual diff content
458
- await showContentDiff(change);
459
- }
460
- }
461
- // Cleanup repo resources
462
- await safeRepoShutdown(repo, "diff");
463
- }
464
- catch (error) {
465
- console.error(chalk_1.default.red(`Diff failed: ${error}`));
466
- throw error;
467
- }
468
- }
469
- /**
470
- * Show sync status
471
- */
472
- async function status() {
473
- try {
474
- const spinner = (0, ora_1.default)("Loading sync status...").start();
475
- // Setup shared context with network disabled for status check
476
- const { repo, syncEngine, workingDir } = await setupCommandContext(process.cwd(), undefined, undefined, false);
477
- const syncStatus = await syncEngine.getStatus();
478
- spinner.stop();
479
- console.log(chalk_1.default.bold("📊 Sync Status Report"));
480
- console.log(`${"=".repeat(50)}`);
481
- // Directory information
482
- console.log(`\n${chalk_1.default.bold("📁 Directory Information:")}`);
483
- console.log(` 📂 Path: ${chalk_1.default.blue(workingDir)}`);
484
- console.log(` 🔧 Config: ${path.join(workingDir, ".pushwork")}`);
485
- // Show root directory URL if available
486
- if (syncStatus.snapshot?.rootDirectoryUrl) {
487
- console.log(` 🔗 Root URL: ${chalk_1.default.cyan(syncStatus.snapshot.rootDirectoryUrl)}`);
488
- // Try to show lastSyncAt from root directory document
489
- try {
490
- const rootHandle = await repo.find(syncStatus.snapshot.rootDirectoryUrl);
491
- const rootDoc = await rootHandle.doc();
492
- if (rootDoc?.lastSyncAt) {
493
- const lastSyncDate = new Date(rootDoc.lastSyncAt);
494
- const timeSince = Date.now() - rootDoc.lastSyncAt;
495
- const timeAgo = timeSince < 60000
496
- ? `${Math.floor(timeSince / 1000)}s ago`
497
- : timeSince < 3600000
498
- ? `${Math.floor(timeSince / 60000)}m ago`
499
- : `${Math.floor(timeSince / 3600000)}h ago`;
500
- console.log(` 🕒 Root last touched: ${chalk_1.default.green(lastSyncDate.toLocaleString())} (${chalk_1.default.gray(timeAgo)})`);
501
- }
502
- else {
503
- console.log(` 🕒 Root last touched: ${chalk_1.default.yellow("Never")}`);
504
- }
505
- }
506
- catch (error) {
507
- console.log(` 🕒 Root last touched: ${chalk_1.default.gray("Unable to determine")}`);
508
- }
509
- }
510
- else {
511
- console.log(` 🔗 Root URL: ${chalk_1.default.yellow("Not set")}`);
512
- }
513
- // Sync timing
514
- if (syncStatus.lastSync) {
515
- const timeSince = Date.now() - syncStatus.lastSync.getTime();
516
- const timeAgo = timeSince < 60000
517
- ? `${Math.floor(timeSince / 1000)}s ago`
518
- : timeSince < 3600000
519
- ? `${Math.floor(timeSince / 60000)}m ago`
520
- : `${Math.floor(timeSince / 3600000)}h ago`;
521
- console.log(`\n${chalk_1.default.bold("⏱️ Sync Timing:")}`);
522
- console.log(` 🕐 Last sync: ${chalk_1.default.green(syncStatus.lastSync.toLocaleString())}`);
523
- console.log(` ⏳ Time since: ${chalk_1.default.gray(timeAgo)}`);
524
- }
525
- else {
526
- console.log(`\n${chalk_1.default.bold("⏱️ Sync Timing:")}`);
527
- console.log(` 🕐 Last sync: ${chalk_1.default.yellow("Never synced")}`);
528
- console.log(` 💡 Run ${chalk_1.default.cyan("pushwork sync")} to perform initial sync`);
529
- }
530
- // Change status
531
- console.log(`\n${chalk_1.default.bold("📝 Change Status:")}`);
532
- if (syncStatus.hasChanges) {
533
- console.log(` 📄 Pending changes: ${chalk_1.default.yellow(syncStatus.changeCount)}`);
534
- console.log(` 🔄 Status: ${chalk_1.default.yellow("Sync needed")}`);
535
- console.log(` 💡 Run ${chalk_1.default.cyan("pushwork diff")} to see details`);
536
- }
537
- else {
538
- console.log(` 📄 Pending changes: ${chalk_1.default.green("None")}`);
539
- console.log(` ✅ Status: ${chalk_1.default.green("Up to date")}`);
540
- }
541
- // Configuration
542
- console.log(`\n${chalk_1.default.bold("⚙️ Configuration:")}`);
543
- const statusConfigManager2 = new config_1.ConfigManager(workingDir);
544
- const statusConfig2 = await statusConfigManager2.load();
545
- if (statusConfig2?.sync_server) {
546
- console.log(` 🔗 Sync server: ${chalk_1.default.blue(statusConfig2.sync_server)}`);
547
- }
548
- else {
549
- console.log(` 🔗 Sync server: ${chalk_1.default.blue("wss://sync3.automerge.org")} (default)`);
550
- }
551
- console.log(` ⚡ Auto sync: ${statusConfig2?.sync?.auto_sync
552
- ? chalk_1.default.green("Enabled")
553
- : chalk_1.default.gray("Disabled")}`);
554
- // Snapshot information
555
- if (syncStatus.snapshot) {
556
- const fileCount = syncStatus.snapshot.files.size;
557
- const dirCount = syncStatus.snapshot.directories.size;
558
- console.log(`\n${chalk_1.default.bold("📊 Repository Statistics:")}`);
559
- console.log(` 📄 Tracked files: ${chalk_1.default.yellow(fileCount)}`);
560
- console.log(` 📁 Tracked directories: ${chalk_1.default.yellow(dirCount)}`);
561
- console.log(` 🏷️ Snapshot timestamp: ${chalk_1.default.gray(new Date(syncStatus.snapshot.timestamp).toLocaleString())}`);
562
- }
563
- // Quick actions
564
- console.log(`\n${chalk_1.default.bold("🚀 Quick Actions:")}`);
565
- if (syncStatus.hasChanges) {
566
- console.log(` ${chalk_1.default.cyan("pushwork diff")} - View pending changes`);
567
- console.log(` ${chalk_1.default.cyan("pushwork sync")} - Apply changes`);
568
- }
569
- else {
570
- console.log(` ${chalk_1.default.cyan("pushwork sync")} - Check for remote changes`);
571
- }
572
- console.log(` ${chalk_1.default.cyan("pushwork log")} - View sync history`);
573
- // Cleanup repo resources
574
- await safeRepoShutdown(repo, "status");
575
- }
576
- catch (error) {
577
- console.error(chalk_1.default.red(`❌ Status check failed: ${error}`));
578
- throw error;
579
- }
580
- }
581
- /**
582
- * Show sync history
583
- */
584
- async function log(targetPath = ".", options) {
585
- try {
586
- // Setup shared context with network disabled for log check
587
- const { repo: logRepo, syncEngine: logSyncEngine, workingDir, } = await setupCommandContext(targetPath, undefined, undefined, false);
588
- const logStatus = await logSyncEngine.getStatus();
589
- if (logStatus.snapshot?.rootDirectoryUrl) {
590
- console.log(chalk_1.default.gray(`Root URL: ${logStatus.snapshot.rootDirectoryUrl}`));
591
- console.log("");
592
- }
593
- // TODO: Implement history tracking and display
594
- // For now, show basic information
595
- console.log(chalk_1.default.bold("Sync History:"));
596
- // Check for snapshot files
597
- const snapshotPath = path.join(workingDir, ".pushwork", "snapshot.json");
598
- if (await (0, utils_1.pathExists)(snapshotPath)) {
599
- const stats = await fs.stat(snapshotPath);
600
- if (options.oneline) {
601
- console.log(`${stats.mtime.toISOString()} - Last sync`);
602
- }
603
- else {
604
- console.log(`Last sync: ${chalk_1.default.green(stats.mtime.toISOString())}`);
605
- console.log(`Snapshot size: ${stats.size} bytes`);
606
- }
607
- }
608
- else {
609
- console.log(chalk_1.default.yellow("No sync history found"));
610
- }
611
- // Cleanup repo resources
612
- await safeRepoShutdown(logRepo, "log");
613
- }
614
- catch (error) {
615
- console.error(chalk_1.default.red(`Log failed: ${error}`));
616
- throw error;
617
- }
618
- }
619
- /**
620
- * Checkout/restore from previous sync
621
- */
622
- async function checkout(syncId, targetPath = ".", options) {
623
- try {
624
- // Setup shared context
625
- const { workingDir } = await setupCommandContext(targetPath);
626
- // TODO: Implement checkout functionality
627
- // This would involve:
628
- // 1. Finding the sync with the given ID
629
- // 2. Restoring file states from that sync
630
- // 3. Updating the snapshot
631
- console.log(chalk_1.default.yellow(`Checkout functionality not yet implemented`));
632
- console.log(`Would restore to sync: ${syncId}`);
633
- console.log(`Target path: ${workingDir}`);
634
- }
635
- catch (error) {
636
- console.error(chalk_1.default.red(`Checkout failed: ${error}`));
637
- throw error;
638
- }
639
- }
640
- /**
641
- * Clone an existing synced directory from an AutomergeUrl
642
- */
643
- async function clone(rootUrl, targetPath, options) {
644
- const spinner = (0, ora_1.default)("Starting clone operation...").start();
645
- try {
646
- const resolvedPath = path.resolve(targetPath);
647
- // Step 1: Directory setup
648
- spinner.text = "Setting up target directory...";
649
- // Check if directory exists and handle --force
650
- if (await (0, utils_1.pathExists)(resolvedPath)) {
651
- const files = await fs.readdir(resolvedPath);
652
- if (files.length > 0 && !options.force) {
653
- spinner.fail("Target directory is not empty. Use --force to overwrite.");
654
- return;
655
- }
656
- }
657
- else {
658
- await (0, utils_1.ensureDirectoryExists)(resolvedPath);
659
- }
660
- // Check if already initialized
661
- const syncToolDir = path.join(resolvedPath, ".pushwork");
662
- if (await (0, utils_1.pathExists)(syncToolDir)) {
663
- if (!options.force) {
664
- spinner.fail("Directory already initialized for sync. Use --force to overwrite.");
665
- return;
666
- }
667
- // Clean up existing sync directory
668
- await fs.rm(syncToolDir, { recursive: true, force: true });
669
- }
670
- console.log(chalk_1.default.gray(" ✓ Target directory prepared"));
671
- // Step 2: Create sync directories
672
- spinner.text = "Creating .pushwork directory...";
673
- await (0, utils_1.ensureDirectoryExists)(syncToolDir);
674
- await (0, utils_1.ensureDirectoryExists)(path.join(syncToolDir, "automerge"));
675
- exports.ProgressMessages.directoryStructureCreated();
676
- // Step 3: Configuration setup
677
- spinner.text = "Setting up configuration...";
678
- const configManager = new config_1.ConfigManager(resolvedPath);
679
- const defaultSyncServer = options.syncServer || "wss://sync3.automerge.org";
680
- const defaultStorageId = options.syncServerStorageId || "3760df37-a4c6-4f66-9ecd-732039a9385d";
681
- const config = {
682
- sync_server: defaultSyncServer,
683
- sync_server_storage_id: defaultStorageId,
684
- sync_enabled: true,
685
- defaults: {
686
- exclude_patterns: [".git", "node_modules", "*.tmp", ".pushwork"],
687
- large_file_threshold: "100MB",
688
- },
689
- diff: {
690
- show_binary: false,
691
- },
692
- sync: {
693
- move_detection_threshold: 0.8,
694
- prompt_threshold: 0.5,
695
- auto_sync: false,
696
- parallel_operations: 4,
697
- },
698
- };
699
- await configManager.save(config);
700
- exports.ProgressMessages.configSaved();
701
- exports.ProgressMessages.syncServer(defaultSyncServer);
702
- exports.ProgressMessages.storageId(defaultStorageId);
703
- // Step 4: Initialize Automerge repo and connect to root directory
704
- spinner.text = "Connecting to root directory document...";
705
- const repo = await (0, repo_factory_1.createRepo)(resolvedPath, {
706
- enableNetwork: true,
707
- syncServer: options.syncServer,
708
- syncServerStorageId: options.syncServerStorageId,
709
- });
710
- exports.ProgressMessages.repoCreated();
711
- exports.ProgressMessages.rootUrl(rootUrl);
712
- // Step 5: Initialize sync engine and pull existing structure
713
- spinner.text = "Downloading directory structure...";
714
- const syncEngine = new core_1.SyncEngine(repo, resolvedPath, config.defaults.exclude_patterns, true, // Network sync enabled for clone
715
- defaultStorageId);
716
- // Set the root directory URL to connect to the cloned repository
717
- await syncEngine.setRootDirectoryUrl(rootUrl);
718
- // Sync to pull the existing directory structure and files
719
- const startTime = Date.now();
720
- await syncEngine.sync(false);
721
- const duration = Date.now() - startTime;
722
- console.log(chalk_1.default.gray(` ✓ Directory sync completed in ${duration}ms`));
723
- // Ensure all changes are flushed to disk
724
- spinner.text = "Flushing changes to disk...";
725
- await safeRepoShutdown(repo, "clone");
726
- exports.ProgressMessages.changesWritten();
727
- spinner.succeed(`Cloned sync directory to ${chalk_1.default.green(resolvedPath)}`);
728
- console.log(`\n${chalk_1.default.bold("📂 Directory Cloned!")}`);
729
- console.log(` 📁 Directory: ${chalk_1.default.blue(resolvedPath)}`);
730
- console.log(` 🔗 Root URL: ${chalk_1.default.cyan(rootUrl)}`);
731
- console.log(` 🔗 Sync server: ${chalk_1.default.blue(defaultSyncServer)}`);
732
- console.log(`\n${chalk_1.default.green("Clone complete!")} Run ${chalk_1.default.cyan("pushwork sync")} to stay in sync.`);
733
- }
734
- catch (error) {
735
- spinner.fail(`Failed to clone: ${error}`);
736
- throw error;
737
- }
738
- }
739
- /**
740
- * Get the root URL for the current pushwork repository
741
- */
742
- async function url(targetPath = ".") {
743
- try {
744
- const resolvedPath = path.resolve(targetPath);
745
- // Check if initialized
746
- const syncToolDir = path.join(resolvedPath, ".pushwork");
747
- if (!(await (0, utils_1.pathExists)(syncToolDir))) {
748
- console.error(chalk_1.default.red("Directory not initialized for sync"));
749
- console.error(`Run ${chalk_1.default.cyan("pushwork init .")} to get started`);
750
- process.exit(1);
751
- }
752
- // Load the snapshot directly to get the URL without all the verbose output
753
- const snapshotPath = path.join(syncToolDir, "snapshot.json");
754
- if (!(await (0, utils_1.pathExists)(snapshotPath))) {
755
- console.error(chalk_1.default.red("No snapshot found"));
756
- console.error(chalk_1.default.gray("The repository may not be properly initialized"));
757
- process.exit(1);
758
- }
759
- const snapshotData = await fs.readFile(snapshotPath, "utf-8");
760
- const snapshot = JSON.parse(snapshotData);
761
- if (snapshot.rootDirectoryUrl) {
762
- // Output just the URL for easy use in scripts
763
- console.log(snapshot.rootDirectoryUrl);
764
- }
765
- else {
766
- console.error(chalk_1.default.red("No root URL found in snapshot"));
767
- console.error(chalk_1.default.gray("The repository may not be properly initialized"));
768
- process.exit(1);
769
- }
770
- }
771
- catch (error) {
772
- console.error(chalk_1.default.red(`Failed to get URL: ${error}`));
773
- process.exit(1);
774
- }
775
- }
776
- async function commit(targetPath, dryRun = false) {
777
- const spinner = (0, ora_1.default)("Starting commit operation...").start();
778
- let repo;
779
- try {
780
- // Setup shared context with network disabled for local-only commit
781
- spinner.text = "Setting up commit context...";
782
- const context = await setupCommandContext(targetPath, undefined, undefined, false);
783
- repo = context.repo;
784
- const syncEngine = context.syncEngine;
785
- spinner.succeed("Connected to repository");
786
- // Run local commit only
787
- spinner.text = "Committing local changes...";
788
- const startTime = Date.now();
789
- const result = await syncEngine.commitLocal(dryRun);
790
- const duration = Date.now() - startTime;
791
- if (repo) {
792
- await safeRepoShutdown(repo, "commit");
793
- }
794
- spinner.succeed(`Commit completed in ${duration}ms`);
795
- // Display results
796
- console.log(chalk_1.default.green("\n✅ Commit Results:"));
797
- console.log(` 📄 Files committed: ${result.filesChanged}`);
798
- console.log(` 📁 Directories committed: ${result.directoriesChanged}`);
799
- console.log(` ⏱️ Total time: ${duration}ms`);
800
- if (result.warnings.length > 0) {
801
- console.log(chalk_1.default.yellow("\n⚠️ Warnings:"));
802
- result.warnings.forEach((warning) => console.log(chalk_1.default.yellow(` • ${warning}`)));
803
- }
804
- if (result.errors.length > 0) {
805
- console.log(chalk_1.default.red("\n❌ Errors:"));
806
- result.errors.forEach((error) => console.log(chalk_1.default.red(` • ${error.operation} at ${error.path}: ${error.error.message}`)));
807
- process.exit(1);
808
- }
809
- console.log(chalk_1.default.gray("\n💡 Run 'pushwork push' to upload to sync server"));
810
- }
811
- catch (error) {
812
- if (repo) {
813
- await safeRepoShutdown(repo, "commit-error");
814
- }
815
- spinner.fail(`Commit failed: ${error}`);
816
- console.error(chalk_1.default.red(`Error: ${error}`));
817
- process.exit(1);
818
- }
819
- }
820
- /**
821
- * Debug command to inspect internal document state
822
- */
823
- async function debug(targetPath = ".", options = {}) {
824
- try {
825
- const spinner = (0, ora_1.default)("Loading debug information...").start();
826
- // Setup shared context with network disabled for debug check
827
- const { repo, syncEngine, workingDir } = await setupCommandContext(targetPath, undefined, undefined, false);
828
- const debugStatus = await syncEngine.getStatus();
829
- spinner.stop();
830
- console.log(chalk_1.default.bold("🔍 Debug Information"));
831
- console.log(`${"=".repeat(50)}`);
832
- // Directory information
833
- console.log(`\n${chalk_1.default.bold("📁 Directory Information:")}`);
834
- console.log(` 📂 Path: ${chalk_1.default.blue(workingDir)}`);
835
- console.log(` 🔧 Config: ${path.join(workingDir, ".pushwork")}`);
836
- if (debugStatus.snapshot?.rootDirectoryUrl) {
837
- console.log(`\n${chalk_1.default.bold("🗂️ Root Directory Document:")}`);
838
- console.log(` 🔗 URL: ${chalk_1.default.cyan(debugStatus.snapshot.rootDirectoryUrl)}`);
839
- try {
840
- const rootHandle = await repo.find(debugStatus.snapshot.rootDirectoryUrl);
841
- const rootDoc = await rootHandle.doc();
842
- if (rootDoc) {
843
- console.log(` 📊 Document Structure:`);
844
- console.log(` 📄 Entries: ${rootDoc.docs.length}`);
845
- console.log(` 🏷️ Type: ${rootDoc["@patchwork"].type}`);
846
- if (rootDoc.lastSyncAt) {
847
- const lastSyncDate = new Date(rootDoc.lastSyncAt);
848
- console.log(` 🕒 Last Sync At: ${chalk_1.default.green(lastSyncDate.toISOString())}`);
849
- console.log(` 🕒 Last Sync Timestamp: ${chalk_1.default.gray(rootDoc.lastSyncAt)}`);
850
- }
851
- else {
852
- console.log(` 🕒 Last Sync At: ${chalk_1.default.yellow("Never set")}`);
853
- }
854
- if (options.verbose) {
855
- console.log(`\n 📋 Full Document Content:`);
856
- console.log(JSON.stringify(rootDoc, null, 2));
857
- console.log(`\n 🏷️ Document Heads:`);
858
- console.log(JSON.stringify(rootHandle.heads(), null, 2));
859
- }
860
- console.log(`\n 📁 Directory Entries:`);
861
- rootDoc.docs.forEach((entry, index) => {
862
- console.log(` ${index + 1}. ${entry.name} (${entry.type}) -> ${entry.url}`);
863
- });
864
- }
865
- else {
866
- console.log(` ❌ Unable to load root document`);
867
- }
868
- }
869
- catch (error) {
870
- console.log(` ❌ Error loading root document: ${error}`);
871
- }
872
- }
873
- else {
874
- console.log(`\n${chalk_1.default.bold("🗂️ Root Directory Document:")}`);
875
- console.log(` ❌ No root directory URL set`);
876
- }
877
- // Snapshot information
878
- if (debugStatus.snapshot) {
879
- console.log(`\n${chalk_1.default.bold("📸 Snapshot Information:")}`);
880
- console.log(` 📄 Tracked files: ${debugStatus.snapshot.files.size}`);
881
- console.log(` 📁 Tracked directories: ${debugStatus.snapshot.directories.size}`);
882
- console.log(` 🏷️ Timestamp: ${new Date(debugStatus.snapshot.timestamp).toISOString()}`);
883
- console.log(` 📂 Root path: ${debugStatus.snapshot.rootPath}`);
884
- if (options.verbose) {
885
- console.log(`\n 📋 All Tracked Files:`);
886
- debugStatus.snapshot.files.forEach((entry, path) => {
887
- console.log(` ${path} -> ${entry.url}`);
888
- });
889
- console.log(`\n 📋 All Tracked Directories:`);
890
- debugStatus.snapshot.directories.forEach((entry, path) => {
891
- console.log(` ${path} -> ${entry.url}`);
892
- });
893
- }
894
- }
895
- // Cleanup repo resources
896
- await safeRepoShutdown(repo, "debug");
897
- }
898
- catch (error) {
899
- console.error(chalk_1.default.red(`Debug failed: ${error}`));
900
- throw error;
901
- }
902
- }
903
- // TODO: Add push and pull commands later
904
- //# sourceMappingURL=commands.js.map