pushwork 1.0.15 → 1.0.16

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 (66) hide show
  1. package/CLAUDE.md +114 -0
  2. package/README.md +1 -1
  3. package/dist/cli.js +4 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands.d.ts.map +1 -1
  6. package/dist/commands.js +38 -11
  7. package/dist/commands.js.map +1 -1
  8. package/dist/core/change-detection.d.ts.map +1 -1
  9. package/dist/core/change-detection.js +4 -12
  10. package/dist/core/change-detection.js.map +1 -1
  11. package/dist/core/config.d.ts +1 -1
  12. package/dist/core/config.d.ts.map +1 -1
  13. package/dist/core/config.js +3 -3
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/sync-engine.d.ts +25 -40
  16. package/dist/core/sync-engine.d.ts.map +1 -1
  17. package/dist/core/sync-engine.js +277 -317
  18. package/dist/core/sync-engine.js.map +1 -1
  19. package/dist/types/config.d.ts +1 -0
  20. package/dist/types/config.d.ts.map +1 -1
  21. package/dist/types/documents.d.ts +1 -2
  22. package/dist/types/documents.d.ts.map +1 -1
  23. package/dist/types/documents.js.map +1 -1
  24. package/dist/utils/fs.d.ts +2 -3
  25. package/dist/utils/fs.d.ts.map +1 -1
  26. package/dist/utils/fs.js +1 -11
  27. package/dist/utils/fs.js.map +1 -1
  28. package/dist/utils/index.d.ts +1 -0
  29. package/dist/utils/index.d.ts.map +1 -1
  30. package/dist/utils/index.js +1 -0
  31. package/dist/utils/index.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/cli.ts +12 -0
  34. package/src/commands.ts +41 -11
  35. package/src/core/change-detection.ts +557 -564
  36. package/src/core/config.ts +3 -3
  37. package/src/core/sync-engine.ts +1399 -1427
  38. package/src/types/config.ts +1 -0
  39. package/src/types/documents.ts +38 -39
  40. package/src/utils/fs.ts +170 -178
  41. package/src/utils/index.ts +4 -3
  42. package/src/utils/text-diff.ts +101 -0
  43. package/dist/cli/commands.d.ts +0 -61
  44. package/dist/cli/commands.d.ts.map +0 -1
  45. package/dist/cli/commands.js +0 -661
  46. package/dist/cli/commands.js.map +0 -1
  47. package/dist/cli/index.d.ts +0 -2
  48. package/dist/cli/index.d.ts.map +0 -1
  49. package/dist/cli/index.js +0 -19
  50. package/dist/cli/index.js.map +0 -1
  51. package/dist/cli/output.d.ts +0 -61
  52. package/dist/cli/output.d.ts.map +0 -1
  53. package/dist/cli/output.js +0 -176
  54. package/dist/cli/output.js.map +0 -1
  55. package/dist/config/index.d.ts +0 -71
  56. package/dist/config/index.d.ts.map +0 -1
  57. package/dist/config/index.js +0 -314
  58. package/dist/config/index.js.map +0 -1
  59. package/dist/utils/content-similarity.d.ts +0 -53
  60. package/dist/utils/content-similarity.d.ts.map +0 -1
  61. package/dist/utils/content-similarity.js +0 -155
  62. package/dist/utils/content-similarity.js.map +0 -1
  63. package/dist/utils/keyhive.d.ts +0 -9
  64. package/dist/utils/keyhive.d.ts.map +0 -1
  65. package/dist/utils/keyhive.js +0 -26
  66. package/dist/utils/keyhive.js.map +0 -1
@@ -1,661 +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
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.setupCommandContext = setupCommandContext;
37
- exports.safeRepoShutdown = safeRepoShutdown;
38
- exports.init = init;
39
- exports.sync = sync;
40
- exports.diff = diff;
41
- exports.status = status;
42
- exports.log = log;
43
- exports.checkout = checkout;
44
- exports.clone = clone;
45
- exports.url = url;
46
- exports.commit = commit;
47
- exports.debug = debug;
48
- const path = __importStar(require("path"));
49
- const fs = __importStar(require("fs/promises"));
50
- const diffLib = __importStar(require("diff"));
51
- const core_1 = require("../core");
52
- const utils_1 = require("../utils");
53
- const config_1 = require("../config");
54
- const repo_factory_1 = require("../utils/repo-factory");
55
- const output_1 = require("./output");
56
- /**
57
- * Validate that sync server options are used together
58
- */
59
- function validateSyncServerOptions(syncServer, syncServerStorageId) {
60
- const hasSyncServer = !!syncServer;
61
- const hasSyncServerStorageId = !!syncServerStorageId;
62
- if (hasSyncServer && !hasSyncServerStorageId) {
63
- throw new Error("--sync-server requires --sync-server-storage-id\nBoth arguments must be provided together.");
64
- }
65
- if (hasSyncServerStorageId && !hasSyncServer) {
66
- throw new Error("--sync-server-storage-id requires --sync-server\nBoth arguments must be provided together.");
67
- }
68
- }
69
- /**
70
- * Shared pre-action that ensures repository and sync engine are properly initialized
71
- * This function always works, with or without network connectivity
72
- */
73
- async function setupCommandContext(workingDir = process.cwd(), customSyncServer, customStorageId, enableNetwork = true) {
74
- const resolvedPath = path.resolve(workingDir);
75
- // Check if initialized
76
- const syncToolDir = path.join(resolvedPath, ".pushwork");
77
- if (!(await (0, utils_1.pathExists)(syncToolDir))) {
78
- throw new Error('Directory not initialized for sync. Run "pushwork init" first.');
79
- }
80
- // Load configuration
81
- const configManager = new config_1.ConfigManager(resolvedPath);
82
- const config = await configManager.getMerged();
83
- // Create repo with configurable network setting
84
- const repo = await (0, repo_factory_1.createRepo)(resolvedPath, {
85
- enableNetwork,
86
- syncServer: customSyncServer,
87
- syncServerStorageId: customStorageId,
88
- });
89
- // Create sync engine with configurable network sync
90
- const syncEngine = new core_1.SyncEngine(repo, resolvedPath, config.defaults.exclude_patterns, enableNetwork, config.sync_server_storage_id);
91
- return {
92
- repo,
93
- syncEngine,
94
- config,
95
- workingDir: resolvedPath,
96
- };
97
- }
98
- /**
99
- * Safely shutdown a repository with proper error handling
100
- */
101
- async function safeRepoShutdown(repo, context) {
102
- try {
103
- await repo.shutdown();
104
- }
105
- catch (shutdownError) {
106
- // WebSocket errors during shutdown are common and non-critical
107
- // Silently ignore them - they don't affect data integrity
108
- const errorMessage = shutdownError instanceof Error
109
- ? shutdownError.message
110
- : String(shutdownError);
111
- // Ignore WebSocket-related errors entirely
112
- if (errorMessage.includes("WebSocket") ||
113
- errorMessage.includes("connection was established") ||
114
- errorMessage.includes("was closed")) {
115
- // Silently ignore WebSocket shutdown errors
116
- return;
117
- }
118
- // Only warn about truly unexpected shutdown errors
119
- console.warn(`Warning: Repository shutdown failed${context ? ` (${context})` : ""}: ${shutdownError}`);
120
- }
121
- }
122
- /**
123
- * Initialize sync in a directory
124
- */
125
- async function init(targetPath, syncServer, syncServerStorageId) {
126
- // Validate sync server options
127
- validateSyncServerOptions(syncServer, syncServerStorageId);
128
- const out = new output_1.Output();
129
- try {
130
- const resolvedPath = path.resolve(targetPath);
131
- out.task(`Initializing ${resolvedPath}`);
132
- await (0, utils_1.ensureDirectoryExists)(resolvedPath);
133
- // Check if already initialized
134
- const syncToolDir = path.join(resolvedPath, ".pushwork");
135
- if (await (0, utils_1.pathExists)(syncToolDir)) {
136
- out.error("Directory already initialized for sync");
137
- out.exit(1);
138
- }
139
- out.update("Creating sync directory");
140
- await (0, utils_1.ensureDirectoryExists)(syncToolDir);
141
- await (0, utils_1.ensureDirectoryExists)(path.join(syncToolDir, "automerge"));
142
- out.update("Setting up configuration");
143
- const configManager = new config_1.ConfigManager(resolvedPath);
144
- const defaultSyncServer = syncServer || "wss://sync3.automerge.org";
145
- const defaultStorageId = syncServerStorageId || "3760df37-a4c6-4f66-9ecd-732039a9385d";
146
- const config = {
147
- sync_server: defaultSyncServer,
148
- sync_server_storage_id: defaultStorageId,
149
- sync_enabled: true,
150
- defaults: {
151
- exclude_patterns: [".git", "node_modules", "*.tmp", ".pushwork"],
152
- large_file_threshold: "100MB",
153
- },
154
- diff: {
155
- show_binary: false,
156
- },
157
- sync: {
158
- move_detection_threshold: 0.8,
159
- prompt_threshold: 0.5,
160
- auto_sync: false,
161
- parallel_operations: 4,
162
- },
163
- };
164
- await configManager.save(config);
165
- out.update("Creating root directory");
166
- const repo = await (0, repo_factory_1.createRepo)(resolvedPath, {
167
- enableNetwork: true,
168
- syncServer: syncServer,
169
- syncServerStorageId: syncServerStorageId,
170
- });
171
- const rootDoc = {
172
- "@patchwork": { type: "folder" },
173
- docs: [],
174
- };
175
- const rootHandle = repo.create(rootDoc);
176
- out.update("Scanning existing files");
177
- const syncEngine = new core_1.SyncEngine(repo, resolvedPath, config.defaults.exclude_patterns, true, config.sync_server_storage_id);
178
- await syncEngine.setRootDirectoryUrl(rootHandle.url);
179
- const result = await syncEngine.sync(false);
180
- out.update("Writing to disk");
181
- await safeRepoShutdown(repo, "init");
182
- out.done();
183
- out.banner("success", "Repository initialized");
184
- out.pair("URL", rootHandle.url);
185
- out.pair("Sync", defaultSyncServer);
186
- if (result.filesChanged > 0) {
187
- out.pair("Files", `${result.filesChanged} added`);
188
- }
189
- out.log("");
190
- out.log(`Run 'pushwork sync' to start synchronizing`);
191
- }
192
- catch (error) {
193
- out.banner("error", "Initialization failed");
194
- out.log(` ${error}`);
195
- out.exit(1);
196
- }
197
- }
198
- /**
199
- * Run bidirectional sync
200
- */
201
- async function sync(options) {
202
- const out = new output_1.Output();
203
- try {
204
- out.task("Syncing");
205
- const { repo, syncEngine, workingDir } = await setupCommandContext();
206
- if (options.dryRun) {
207
- out.update("Analyzing changes");
208
- const preview = await syncEngine.previewChanges();
209
- out.done();
210
- if (preview.changes.length === 0 && preview.moves.length === 0) {
211
- out.banner("info", "No changes detected");
212
- out.log("Everything is already in sync");
213
- return;
214
- }
215
- out.banner("info", "Changes pending");
216
- out.pair("Directory", workingDir);
217
- out.pair("Changes", preview.changes.length.toString());
218
- if (preview.moves.length > 0) {
219
- out.pair("Moves", preview.moves.length.toString());
220
- }
221
- out.log("");
222
- out.log("Files:");
223
- for (const change of preview.changes.slice(0, 10)) {
224
- const prefix = change.changeType === "local_only"
225
- ? "[local] "
226
- : change.changeType === "remote_only"
227
- ? "[remote] "
228
- : "[conflict]";
229
- out.log(` ${prefix} ${change.path}`);
230
- }
231
- if (preview.changes.length > 10) {
232
- out.log(` ... and ${preview.changes.length - 10} more`);
233
- }
234
- if (preview.moves.length > 0) {
235
- out.log("");
236
- out.log("Moves:");
237
- for (const move of preview.moves.slice(0, 5)) {
238
- out.log(` ${move.fromPath} → ${move.toPath}`);
239
- }
240
- if (preview.moves.length > 5) {
241
- out.log(` ... and ${preview.moves.length - 5} more`);
242
- }
243
- }
244
- out.log("");
245
- out.log("Run without --dry-run to apply these changes");
246
- }
247
- else {
248
- out.update("Synchronizing");
249
- const result = await syncEngine.sync(false);
250
- out.update("Writing to disk");
251
- await safeRepoShutdown(repo, "sync");
252
- if (result.success) {
253
- if (result.filesChanged === 0 && result.directoriesChanged === 0) {
254
- out.done();
255
- out.banner("success", "Already in sync");
256
- }
257
- else {
258
- out.done();
259
- out.banner("success", `${result.filesChanged} file${result.filesChanged !== 1 ? "s" : ""} updated`);
260
- out.pair("Files", result.filesChanged.toString());
261
- }
262
- if (result.warnings.length > 0) {
263
- out.log("");
264
- out.banner("warning", `${result.warnings.length} warnings`);
265
- for (const warning of result.warnings.slice(0, 5)) {
266
- out.log(` ${warning}`);
267
- }
268
- if (result.warnings.length > 5) {
269
- out.log(` ... and ${result.warnings.length - 5} more`);
270
- }
271
- }
272
- }
273
- else {
274
- out.done("partial", false);
275
- out.banner("warning", `${result.filesChanged} updated, ${result.errors.length} errors`);
276
- out.pair("Files", result.filesChanged.toString());
277
- out.pair("Errors", result.errors.length.toString());
278
- out.log("");
279
- for (const error of result.errors.slice(0, 5)) {
280
- out.banner("error", error.path);
281
- out.log(` ${error.error.message}`);
282
- out.log("");
283
- }
284
- if (result.errors.length > 5) {
285
- out.log(`... and ${result.errors.length - 5} more errors`);
286
- }
287
- }
288
- }
289
- }
290
- catch (error) {
291
- out.banner("error", "Sync failed");
292
- out.log(` ${error}`);
293
- out.exit(1);
294
- }
295
- }
296
- /**
297
- * Show differences between local and remote
298
- */
299
- async function diff(targetPath = ".", options) {
300
- const out = new output_1.Output();
301
- try {
302
- out.task("Analyzing changes");
303
- const { repo, syncEngine } = await setupCommandContext(targetPath, undefined, undefined, false);
304
- const preview = await syncEngine.previewChanges();
305
- out.done();
306
- if (options.nameOnly) {
307
- for (const change of preview.changes) {
308
- console.log(change.path);
309
- }
310
- return;
311
- }
312
- if (preview.changes.length === 0) {
313
- out.success("No changes detected");
314
- await safeRepoShutdown(repo, "diff");
315
- out.exit();
316
- return;
317
- }
318
- out.warn(`${preview.changes.length} changes detected`);
319
- for (const change of preview.changes) {
320
- const prefix = change.changeType === "local_only"
321
- ? "[local] "
322
- : change.changeType === "remote_only"
323
- ? "[remote] "
324
- : "[conflict]";
325
- if (!options.tool) {
326
- try {
327
- // Get old content (from snapshot/remote)
328
- const oldContent = change.remoteContent || "";
329
- // Get new content (current local)
330
- const newContent = change.localContent || "";
331
- // Convert binary content to string representation if needed
332
- const oldText = typeof oldContent === "string"
333
- ? oldContent
334
- : `<binary content: ${oldContent.length} bytes>`;
335
- const newText = typeof newContent === "string"
336
- ? newContent
337
- : `<binary content: ${newContent.length} bytes>`;
338
- // Generate unified diff
339
- const diffResult = diffLib.createPatch(change.path, oldText, newText, "previous", "current");
340
- // Skip the header lines and process the diff
341
- const lines = diffResult.split("\n").slice(4); // Skip index, ===, ---, +++ lines
342
- if (lines.length === 0 || (lines.length === 1 && lines[0] === "")) {
343
- out.log(`${prefix}${change.path} (content identical)`, "cyan");
344
- continue;
345
- }
346
- // Extract first hunk header and show inline with path
347
- let firstHunk = "";
348
- let diffLines = lines;
349
- if (lines[0]?.startsWith("@@")) {
350
- firstHunk = ` ${lines[0]}`;
351
- diffLines = lines.slice(1);
352
- }
353
- out.log(`${prefix}${change.path}${firstHunk}`, "cyan");
354
- for (const line of diffLines) {
355
- if (line.startsWith("@@")) {
356
- // Additional hunk headers
357
- out.log(line, "dim");
358
- }
359
- else if (line.startsWith("+")) {
360
- // Added line
361
- out.log(line, "green");
362
- }
363
- else if (line.startsWith("-")) {
364
- // Removed line
365
- out.log(line, "red");
366
- }
367
- else if (line.startsWith(" ") || line === "") {
368
- // Context line or empty
369
- out.log(line, "dim");
370
- }
371
- }
372
- }
373
- catch (error) {
374
- out.log(`${prefix}${change.path} (diff error: ${error})`, "cyan");
375
- }
376
- }
377
- else {
378
- out.log(`${prefix} ${change.path}`);
379
- }
380
- }
381
- await safeRepoShutdown(repo, "diff");
382
- }
383
- catch (error) {
384
- out.error(`Diff failed: ${error}`);
385
- out.exit(1);
386
- }
387
- }
388
- /**
389
- * Show sync status
390
- */
391
- async function status() {
392
- const out = new output_1.Output();
393
- try {
394
- out.task("Loading status");
395
- const { repo, syncEngine, workingDir, config } = await setupCommandContext(process.cwd(), undefined, undefined, false);
396
- const syncStatus = await syncEngine.getStatus();
397
- out.done();
398
- out.banner("info", workingDir);
399
- if (syncStatus.snapshot?.rootDirectoryUrl) {
400
- out.pair("URL", syncStatus.snapshot.rootDirectoryUrl);
401
- }
402
- if (syncStatus.snapshot) {
403
- const fileCount = syncStatus.snapshot.files.size;
404
- out.pair("Files", `${fileCount} tracked`);
405
- }
406
- out.pair("Sync", config?.sync_server || "wss://sync3.automerge.org");
407
- if (syncStatus.hasChanges) {
408
- out.pair("Changes", `${syncStatus.changeCount} pending`);
409
- out.log("");
410
- out.log("Run 'pushwork diff' to see pairs");
411
- }
412
- else {
413
- out.pair("Status", "up to date");
414
- }
415
- await safeRepoShutdown(repo, "status");
416
- }
417
- catch (error) {
418
- out.error(`Status check failed: ${error}`);
419
- out.exit(1);
420
- }
421
- }
422
- /**
423
- * Show sync history
424
- */
425
- async function log(targetPath = ".", options) {
426
- const out = new output_1.Output();
427
- try {
428
- const { repo: logRepo, workingDir } = await setupCommandContext(targetPath, undefined, undefined, false);
429
- // TODO: Implement history tracking
430
- const snapshotPath = path.join(workingDir, ".pushwork", "snapshot.json");
431
- if (await (0, utils_1.pathExists)(snapshotPath)) {
432
- const stats = await fs.stat(snapshotPath);
433
- out.banner("info", "Sync history (stub)");
434
- out.pair("Last sync", stats.mtime.toISOString());
435
- }
436
- else {
437
- out.banner("info", "No sync history found");
438
- }
439
- await safeRepoShutdown(logRepo, "log");
440
- }
441
- catch (error) {
442
- out.error(`Log failed: ${error}`);
443
- out.exit(1);
444
- }
445
- }
446
- /**
447
- * Checkout/restore from previous sync
448
- */
449
- async function checkout(syncId, targetPath = ".", options) {
450
- const out = new output_1.Output();
451
- try {
452
- const { workingDir } = await setupCommandContext(targetPath);
453
- // TODO: Implement checkout functionality
454
- out.banner("warning", "Checkout not yet implemented");
455
- out.pair("Sync ID", syncId);
456
- out.pair("Path", workingDir);
457
- }
458
- catch (error) {
459
- out.error(`Checkout failed: ${error}`);
460
- out.exit(1);
461
- }
462
- }
463
- /**
464
- * Clone an existing synced directory from an AutomergeUrl
465
- */
466
- async function clone(rootUrl, targetPath, options) {
467
- // Validate sync server options
468
- validateSyncServerOptions(options.syncServer, options.syncServerStorageId);
469
- const out = new output_1.Output();
470
- try {
471
- const resolvedPath = path.resolve(targetPath);
472
- out.task(`Cloning ${rootUrl}`);
473
- // Check if directory exists and handle --force
474
- if (await (0, utils_1.pathExists)(resolvedPath)) {
475
- const files = await fs.readdir(resolvedPath);
476
- if (files.length > 0 && !options.force) {
477
- out.error("Target directory is not empty. Use --force to overwrite");
478
- out.exit(1);
479
- }
480
- }
481
- else {
482
- await (0, utils_1.ensureDirectoryExists)(resolvedPath);
483
- }
484
- // Check if already initialized
485
- const syncToolDir = path.join(resolvedPath, ".pushwork");
486
- if (await (0, utils_1.pathExists)(syncToolDir)) {
487
- if (!options.force) {
488
- out.error("Directory already initialized. Use --force to overwrite");
489
- out.exit(1);
490
- }
491
- await fs.rm(syncToolDir, { recursive: true, force: true });
492
- }
493
- out.update("Creating sync directory");
494
- await (0, utils_1.ensureDirectoryExists)(syncToolDir);
495
- await (0, utils_1.ensureDirectoryExists)(path.join(syncToolDir, "automerge"));
496
- out.update("Setting up configuration");
497
- const configManager = new config_1.ConfigManager(resolvedPath);
498
- const defaultSyncServer = options.syncServer || "wss://sync3.automerge.org";
499
- const defaultStorageId = options.syncServerStorageId || "3760df37-a4c6-4f66-9ecd-732039a9385d";
500
- const config = {
501
- sync_server: defaultSyncServer,
502
- sync_server_storage_id: defaultStorageId,
503
- sync_enabled: true,
504
- defaults: {
505
- exclude_patterns: [".git", "node_modules", "*.tmp", ".pushwork"],
506
- large_file_threshold: "100MB",
507
- },
508
- diff: {
509
- show_binary: false,
510
- },
511
- sync: {
512
- move_detection_threshold: 0.8,
513
- prompt_threshold: 0.5,
514
- auto_sync: false,
515
- parallel_operations: 4,
516
- },
517
- };
518
- await configManager.save(config);
519
- out.update("Connecting to sync server");
520
- const repo = await (0, repo_factory_1.createRepo)(resolvedPath, {
521
- enableNetwork: true,
522
- syncServer: options.syncServer,
523
- syncServerStorageId: options.syncServerStorageId,
524
- });
525
- out.update("Downloading files");
526
- const syncEngine = new core_1.SyncEngine(repo, resolvedPath, config.defaults.exclude_patterns, true, defaultStorageId);
527
- await syncEngine.setRootDirectoryUrl(rootUrl);
528
- const result = await syncEngine.sync(false);
529
- out.update("Writing to disk");
530
- await safeRepoShutdown(repo, "clone");
531
- out.done();
532
- out.banner("success", `Cloned to ${resolvedPath}`);
533
- out.pair("Files", `${result.filesChanged} downloaded`);
534
- out.pair("Sync", defaultSyncServer);
535
- out.pair("URL", rootUrl);
536
- }
537
- catch (error) {
538
- out.banner("error", "Clone failed");
539
- out.log(` ${error}`);
540
- out.exit(1);
541
- }
542
- }
543
- /**
544
- * Get the root URL for the current pushwork repository
545
- */
546
- async function url(targetPath = ".") {
547
- const out = new output_1.Output();
548
- try {
549
- const resolvedPath = path.resolve(targetPath);
550
- const syncToolDir = path.join(resolvedPath, ".pushwork");
551
- if (!(await (0, utils_1.pathExists)(syncToolDir))) {
552
- out.error("Directory not initialized for sync");
553
- out.exit(1);
554
- }
555
- const snapshotPath = path.join(syncToolDir, "snapshot.json");
556
- if (!(await (0, utils_1.pathExists)(snapshotPath))) {
557
- out.error("No snapshot found");
558
- out.exit(1);
559
- }
560
- const snapshotData = await fs.readFile(snapshotPath, "utf-8");
561
- const snapshot = JSON.parse(snapshotData);
562
- if (snapshot.rootDirectoryUrl) {
563
- // Output just the URL for easy use in scripts
564
- console.log(snapshot.rootDirectoryUrl);
565
- }
566
- else {
567
- out.error("No root URL found in snapshot");
568
- out.exit(1);
569
- }
570
- }
571
- catch (error) {
572
- out.error(`Failed to get URL: ${error}`);
573
- out.exit(1);
574
- }
575
- }
576
- async function commit(targetPath, dryRun = false) {
577
- const out = new output_1.Output();
578
- try {
579
- out.task("Committing local changes");
580
- const { repo, syncEngine } = await setupCommandContext(targetPath, undefined, undefined, false);
581
- const result = await syncEngine.commitLocal(dryRun);
582
- await safeRepoShutdown(repo, "commit");
583
- out.done();
584
- if (result.errors.length > 0) {
585
- out.banner("error", `${result.errors.length} errors`);
586
- result.errors.forEach((error) => {
587
- out.log(` ${error.path}: ${error.error.message}`);
588
- });
589
- out.exit(1);
590
- }
591
- out.banner("success", `${result.filesChanged} files committed`);
592
- out.pair("Files", result.filesChanged.toString());
593
- out.pair("Directories", result.directoriesChanged.toString());
594
- if (result.warnings.length > 0) {
595
- out.log("");
596
- out.banner("warning", `${result.warnings.length} warnings`);
597
- result.warnings.forEach((warning) => out.log(` ${warning}`));
598
- }
599
- }
600
- catch (error) {
601
- out.error(`Commit failed: ${error}`);
602
- out.exit(1);
603
- }
604
- }
605
- /**
606
- * Debug command to inspect internal document state
607
- */
608
- async function debug(targetPath = ".", options = {}) {
609
- const out = new output_1.Output();
610
- try {
611
- out.task("Loading debug info");
612
- const { repo, syncEngine, workingDir } = await setupCommandContext(targetPath, undefined, undefined, false);
613
- const debugStatus = await syncEngine.getStatus();
614
- out.done("done");
615
- out.banner("info", "Debug Information");
616
- out.pair("Path", workingDir);
617
- if (debugStatus.snapshot?.rootDirectoryUrl) {
618
- out.pair("URL", debugStatus.snapshot.rootDirectoryUrl);
619
- try {
620
- const rootHandle = await repo.find(debugStatus.snapshot.rootDirectoryUrl);
621
- const rootDoc = await rootHandle.doc();
622
- if (rootDoc) {
623
- out.pair("Entries", rootDoc.docs.length.toString());
624
- if (rootDoc.lastSyncAt) {
625
- const lastSyncDate = new Date(rootDoc.lastSyncAt);
626
- out.pair("Last sync", lastSyncDate.toISOString());
627
- }
628
- if (options.verbose) {
629
- out.log("");
630
- out.log("Document:");
631
- out.log(JSON.stringify(rootDoc, null, 2));
632
- out.log("");
633
- out.log("Heads:");
634
- out.log(JSON.stringify(rootHandle.heads(), null, 2));
635
- }
636
- }
637
- }
638
- catch (error) {
639
- out.warn(`Error loading root document: ${error}`);
640
- }
641
- }
642
- if (debugStatus.snapshot) {
643
- out.pair("Files", debugStatus.snapshot.files.size.toString());
644
- out.pair("Directories", debugStatus.snapshot.directories.size.toString());
645
- if (options.verbose) {
646
- out.log("");
647
- out.log("All tracked files:");
648
- debugStatus.snapshot.files.forEach((entry, filePath) => {
649
- out.log(` ${filePath} -> ${entry.url}`);
650
- });
651
- }
652
- }
653
- await safeRepoShutdown(repo, "debug");
654
- }
655
- catch (error) {
656
- out.error(`Debug failed: ${error}`);
657
- out.exit(1);
658
- }
659
- }
660
- // TODO: Add push and pull commands later
661
- //# sourceMappingURL=commands.js.map