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