@mod-computer/cli 0.2.4 → 0.2.5

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 (74) hide show
  1. package/package.json +3 -3
  2. package/dist/app.js +0 -227
  3. package/dist/cli.bundle.js.map +0 -7
  4. package/dist/cli.js +0 -132
  5. package/dist/commands/add.js +0 -245
  6. package/dist/commands/agents-run.js +0 -71
  7. package/dist/commands/auth.js +0 -259
  8. package/dist/commands/branch.js +0 -1411
  9. package/dist/commands/claude-sync.js +0 -772
  10. package/dist/commands/comment.js +0 -568
  11. package/dist/commands/diff.js +0 -182
  12. package/dist/commands/index.js +0 -73
  13. package/dist/commands/init.js +0 -597
  14. package/dist/commands/ls.js +0 -135
  15. package/dist/commands/members.js +0 -687
  16. package/dist/commands/mv.js +0 -282
  17. package/dist/commands/recover.js +0 -207
  18. package/dist/commands/rm.js +0 -257
  19. package/dist/commands/spec.js +0 -386
  20. package/dist/commands/status.js +0 -296
  21. package/dist/commands/sync.js +0 -119
  22. package/dist/commands/trace.js +0 -1752
  23. package/dist/commands/workspace.js +0 -447
  24. package/dist/components/conflict-resolution-ui.js +0 -120
  25. package/dist/components/messages.js +0 -5
  26. package/dist/components/thread.js +0 -8
  27. package/dist/config/features.js +0 -83
  28. package/dist/containers/branches-container.js +0 -140
  29. package/dist/containers/directory-container.js +0 -92
  30. package/dist/containers/thread-container.js +0 -214
  31. package/dist/containers/threads-container.js +0 -27
  32. package/dist/containers/workspaces-container.js +0 -27
  33. package/dist/daemon/conflict-resolution.js +0 -172
  34. package/dist/daemon/content-hash.js +0 -31
  35. package/dist/daemon/file-sync.js +0 -985
  36. package/dist/daemon/index.js +0 -203
  37. package/dist/daemon/mime-types.js +0 -166
  38. package/dist/daemon/offline-queue.js +0 -211
  39. package/dist/daemon/path-utils.js +0 -64
  40. package/dist/daemon/share-policy.js +0 -83
  41. package/dist/daemon/wasm-errors.js +0 -189
  42. package/dist/daemon/worker.js +0 -557
  43. package/dist/daemon-worker.js +0 -258
  44. package/dist/errors/workspace-errors.js +0 -48
  45. package/dist/lib/auth-server.js +0 -216
  46. package/dist/lib/browser.js +0 -35
  47. package/dist/lib/diff.js +0 -284
  48. package/dist/lib/formatters.js +0 -204
  49. package/dist/lib/git.js +0 -137
  50. package/dist/lib/local-fs.js +0 -201
  51. package/dist/lib/prompts.js +0 -56
  52. package/dist/lib/storage.js +0 -213
  53. package/dist/lib/trace-formatters.js +0 -314
  54. package/dist/services/add-service.js +0 -554
  55. package/dist/services/add-validation.js +0 -124
  56. package/dist/services/automatic-file-tracker.js +0 -303
  57. package/dist/services/cli-orchestrator.js +0 -227
  58. package/dist/services/feature-flags.js +0 -187
  59. package/dist/services/file-import-service.js +0 -283
  60. package/dist/services/file-transformation-service.js +0 -218
  61. package/dist/services/logger.js +0 -44
  62. package/dist/services/mod-config.js +0 -67
  63. package/dist/services/modignore-service.js +0 -328
  64. package/dist/services/sync-daemon.js +0 -244
  65. package/dist/services/thread-notification-service.js +0 -50
  66. package/dist/services/thread-service.js +0 -147
  67. package/dist/stores/use-directory-store.js +0 -96
  68. package/dist/stores/use-threads-store.js +0 -46
  69. package/dist/stores/use-workspaces-store.js +0 -54
  70. package/dist/types/add-types.js +0 -99
  71. package/dist/types/config.js +0 -16
  72. package/dist/types/index.js +0 -2
  73. package/dist/types/workspace-connection.js +0 -53
  74. package/dist/types.js +0 -1
@@ -1,296 +0,0 @@
1
- // glassware[type="implementation", id="impl-cli-status-cmd--a59c0eef", requirements="requirement-cli-status-cmd--73a24f60,requirement-cli-status-requires-workspace--99699152,requirement-cli-status-local-modified--1e5f495a,requirement-cli-status-workspace-modified--10b85f32,requirement-cli-status-local-new--31131959,requirement-cli-status-workspace-new--47a5c7b1,requirement-cli-status-conflicts--8b6fb820,requirement-cli-status-short--81ed94d8,requirement-cli-status-json--9f708de6"]
2
- // spec: packages/mod-cli/specs/file-directory.md
3
- import { createModWorkspace } from '@mod/mod-core';
4
- import { readWorkspaceConnection } from '../lib/storage.js';
5
- import { readLocalFile, getLocalFileStats, listLocalFiles } from '../lib/local-fs.js';
6
- import { getWorkspaceContent } from '../lib/diff.js';
7
- import { detectGitRepo } from '../lib/git.js';
8
- import path from 'path';
9
- import crypto from 'crypto';
10
- // glassware[type="implementation", id="impl-cli-status-requires-workspace--7061acd4", requirements="requirement-cli-status-requires-workspace--99699152,requirement-cli-fd-error-no-workspace--1919b4fb"]
11
- function requireWorkspaceConnection() {
12
- const currentDir = process.cwd();
13
- const connection = readWorkspaceConnection(currentDir);
14
- if (!connection) {
15
- console.error('Error: Not connected to a workspace.');
16
- console.error('Run `mod init` to connect this directory first.');
17
- process.exit(1);
18
- }
19
- return {
20
- workspaceId: connection.workspaceId,
21
- workspaceName: connection.workspaceName,
22
- workspacePath: connection.path,
23
- };
24
- }
25
- function parseArgs(args) {
26
- const options = {
27
- short: false,
28
- json: false,
29
- quiet: false,
30
- };
31
- let pathFilter;
32
- for (let i = 0; i < args.length; i++) {
33
- const arg = args[i];
34
- if (arg === '--short' || arg === '-s') {
35
- options.short = true;
36
- }
37
- else if (arg === '--json') {
38
- options.json = true;
39
- }
40
- else if (arg === '--quiet' || arg === '-q') {
41
- options.quiet = true;
42
- }
43
- else if (!arg.startsWith('-') && !pathFilter) {
44
- pathFilter = arg;
45
- }
46
- }
47
- return { pathFilter, options };
48
- }
49
- /**
50
- * Quick hash for content comparison
51
- */
52
- function hashContent(content) {
53
- return crypto.createHash('md5').update(content).digest('hex');
54
- }
55
- // glassware[type="implementation", id="impl-cli-fd-perf-status--15f2ce0f", requirements="requirement-cli-fd-perf-status--248b3313"]
56
- // glassware[type="implementation", id="impl-cli-status-branch--542da302", specifications="specification-spec-status-branch--c6241388,specification-spec-status-no-git--615a5917"]
57
- export async function statusCommand(args, repo) {
58
- const { workspaceId, workspacePath } = requireWorkspaceConnection();
59
- const { pathFilter, options } = parseArgs(args);
60
- // Detect git repo and branch
61
- const gitInfo = detectGitRepo(workspacePath);
62
- try {
63
- const modWorkspace = createModWorkspace(repo);
64
- const workspaceHandle = await modWorkspace.openWorkspace(workspaceId);
65
- // Get workspace files
66
- const workspaceFiles = await workspaceHandle.file.list();
67
- // Build workspace file map
68
- const wsFileMap = new Map();
69
- for (const file of workspaceFiles) {
70
- const filePath = file.metadata?.path || file.name;
71
- if (pathFilter && !filePath.startsWith(pathFilter))
72
- continue;
73
- wsFileMap.set(filePath, {
74
- id: file.id,
75
- size: file.size,
76
- updatedAt: file.updatedAt,
77
- });
78
- }
79
- // Get local files
80
- const localFiles = await listLocalFiles(workspacePath);
81
- const localFileMap = new Map();
82
- for (const relativePath of localFiles) {
83
- if (pathFilter && !relativePath.startsWith(pathFilter))
84
- continue;
85
- // Skip common ignore patterns
86
- if (relativePath.includes('node_modules/') ||
87
- relativePath.includes('.git/') ||
88
- relativePath.includes('.mod/') ||
89
- relativePath.startsWith('.')) {
90
- continue;
91
- }
92
- const stats = await getLocalFileStats(path.join(workspacePath, relativePath));
93
- if (stats) {
94
- localFileMap.set(relativePath, stats);
95
- }
96
- }
97
- // Compare files
98
- const statusList = [];
99
- const processedPaths = new Set();
100
- // Check workspace files against local
101
- for (const [filePath, wsFile] of wsFileMap) {
102
- processedPaths.add(filePath);
103
- const localFile = localFileMap.get(filePath);
104
- if (!localFile) {
105
- // File in workspace but not local
106
- statusList.push({
107
- path: filePath,
108
- status: 'added-workspace',
109
- workspaceSize: wsFile.size,
110
- });
111
- }
112
- else {
113
- // File exists in both - compare content
114
- const localContent = await readLocalFile(path.join(workspacePath, filePath));
115
- if (localContent !== null) {
116
- // Get workspace content
117
- try {
118
- const handle = await workspaceHandle.file.get(wsFile.id);
119
- if (handle) {
120
- const doc = handle.doc();
121
- const wsContent = getWorkspaceContent(doc?.content);
122
- const localHash = hashContent(localContent);
123
- const wsHash = hashContent(wsContent);
124
- if (localHash !== wsHash) {
125
- // Content differs - check timestamps to determine direction
126
- const localTime = localFile.mtime.getTime();
127
- const wsTime = new Date(wsFile.updatedAt).getTime();
128
- // If both modified recently (within 1 second of each other), it's a conflict
129
- const timeDiff = Math.abs(localTime - wsTime);
130
- if (timeDiff < 1000 && localTime !== wsTime) {
131
- // Conflict - modified in both places
132
- statusList.push({
133
- path: filePath,
134
- status: 'conflict',
135
- localSize: localFile.size,
136
- workspaceSize: wsFile.size,
137
- });
138
- }
139
- else if (localTime > wsTime) {
140
- // Local is newer
141
- statusList.push({
142
- path: filePath,
143
- status: 'modified-local',
144
- localSize: localFile.size,
145
- workspaceSize: wsFile.size,
146
- });
147
- }
148
- else {
149
- // Workspace is newer
150
- statusList.push({
151
- path: filePath,
152
- status: 'modified-workspace',
153
- localSize: localFile.size,
154
- workspaceSize: wsFile.size,
155
- });
156
- }
157
- }
158
- }
159
- }
160
- catch {
161
- // If we can't load the file, assume local is newer
162
- statusList.push({
163
- path: filePath,
164
- status: 'modified-local',
165
- localSize: localFile.size,
166
- });
167
- }
168
- }
169
- }
170
- }
171
- // Check local files not in workspace
172
- for (const [filePath, localFile] of localFileMap) {
173
- if (processedPaths.has(filePath))
174
- continue;
175
- statusList.push({
176
- path: filePath,
177
- status: 'added-local',
178
- localSize: localFile.size,
179
- });
180
- }
181
- // Sort by path
182
- statusList.sort((a, b) => a.path.localeCompare(b.path));
183
- // Format output
184
- if (options.json) {
185
- // JSON output format with git info
186
- const output = {
187
- git: {
188
- isGitRepo: gitInfo.isGitRepo,
189
- branch: gitInfo.branch,
190
- isDetached: gitInfo.isDetached,
191
- },
192
- localChanges: statusList.filter(f => f.status === 'modified-local' || f.status === 'added-local' || f.status === 'deleted-workspace'),
193
- workspaceChanges: statusList.filter(f => f.status === 'modified-workspace' || f.status === 'added-workspace' || f.status === 'deleted-local'),
194
- conflicts: statusList.filter(f => f.status === 'conflict'),
195
- };
196
- console.log(JSON.stringify(output, null, 2));
197
- return;
198
- }
199
- if (options.quiet) {
200
- const localCount = statusList.filter(f => f.status === 'modified-local' || f.status === 'added-local').length;
201
- const wsCount = statusList.filter(f => f.status === 'modified-workspace' || f.status === 'added-workspace').length;
202
- const conflictCount = statusList.filter(f => f.status === 'conflict').length;
203
- console.log(`${localCount} local, ${wsCount} workspace, ${conflictCount} conflicts`);
204
- return;
205
- }
206
- if (options.short) {
207
- // Short format output
208
- for (const file of statusList) {
209
- const code = getStatusCode(file.status);
210
- console.log(`${code} ${file.path}`);
211
- }
212
- if (statusList.length === 0) {
213
- console.log('No changes.');
214
- }
215
- return;
216
- }
217
- // Default output - show git branch info first
218
- if (!options.json && !options.short && !options.quiet) {
219
- if (gitInfo.isGitRepo) {
220
- const branchDisplay = gitInfo.isDetached
221
- ? `${gitInfo.branch} (detached HEAD)`
222
- : gitInfo.branch;
223
- console.log(`On git branch: ${branchDisplay}`);
224
- }
225
- else {
226
- console.log('On git branch: (no git)');
227
- }
228
- console.log('');
229
- }
230
- if (statusList.length === 0) {
231
- console.log('Everything up to date.');
232
- return;
233
- }
234
- // Group by category
235
- const localModified = statusList.filter(f => f.status === 'modified-local');
236
- const localAdded = statusList.filter(f => f.status === 'added-local');
237
- const wsModified = statusList.filter(f => f.status === 'modified-workspace');
238
- const wsAdded = statusList.filter(f => f.status === 'added-workspace');
239
- const conflicts = statusList.filter(f => f.status === 'conflict');
240
- if (localModified.length > 0 || localAdded.length > 0) {
241
- console.log('Changes not synced to workspace:');
242
- for (const file of localModified) {
243
- console.log(` modified: ${file.path}`);
244
- }
245
- for (const file of localAdded) {
246
- console.log(` new file: ${file.path}`);
247
- }
248
- console.log('');
249
- }
250
- if (wsModified.length > 0 || wsAdded.length > 0) {
251
- console.log('Changes not synced to local:');
252
- for (const file of wsModified) {
253
- console.log(` modified: ${file.path}`);
254
- }
255
- for (const file of wsAdded) {
256
- console.log(` new file: ${file.path}`);
257
- }
258
- console.log('');
259
- }
260
- if (conflicts.length > 0) {
261
- console.log('Conflicts (modified in both):');
262
- for (const file of conflicts) {
263
- console.log(` !! ${file.path}`);
264
- }
265
- console.log('');
266
- console.log('Use `mod diff <file>` to view differences.');
267
- console.log('');
268
- }
269
- // Summary
270
- const localCount = localModified.length + localAdded.length;
271
- const wsCount = wsModified.length + wsAdded.length;
272
- console.log(`${localCount} file${localCount === 1 ? '' : 's'} changed locally, ${wsCount} file${wsCount === 1 ? '' : 's'} changed in workspace`);
273
- }
274
- catch (error) {
275
- console.error('Error checking status:', error.message);
276
- process.exit(1);
277
- }
278
- }
279
- function getStatusCode(status) {
280
- switch (status) {
281
- case 'modified-local':
282
- return 'M ';
283
- case 'modified-workspace':
284
- return ' M';
285
- case 'added-local':
286
- return 'A ';
287
- case 'added-workspace':
288
- return ' A';
289
- case 'deleted-local':
290
- return 'D ';
291
- case 'deleted-workspace':
292
- return ' D';
293
- case 'conflict':
294
- return '!!';
295
- }
296
- }
@@ -1,119 +0,0 @@
1
- // glassware[type="implementation", id="cli-sync-command--4fa01713", requirements="requirement-cli-sync-ux-1a--55a635db,requirement-cli-sync-ux-1b--eedac360,requirement-cli-sync-ux-2--fe764dca,requirement-cli-sync-ux-3--fbb89737,requirement-cli-sync-ux-4a--f2e42b98,requirement-cli-sync-ux-4b--00ec3d9c,requirement-cli-sync-ux-5--480ee59f"]
2
- import { startDaemon, stopDaemon, getDaemonStatus, } from '../daemon/index.js';
3
- export async function syncCommand(args, repo) {
4
- const command = args[0] || 'start';
5
- try {
6
- switch (command) {
7
- case 'start':
8
- await handleStart(args.includes('--verbose'));
9
- break;
10
- case 'stop':
11
- await handleStop(args.includes('--force'));
12
- break;
13
- case 'restart':
14
- await handleStop(args.includes('--force'));
15
- await handleStart(args.includes('--verbose'));
16
- break;
17
- case 'status':
18
- handleStatus();
19
- break;
20
- default:
21
- showUsage();
22
- break;
23
- }
24
- }
25
- catch (error) {
26
- console.error(`Sync command failed: ${error.message}`);
27
- process.exit(1);
28
- }
29
- }
30
- async function handleStart(verbose) {
31
- const result = await startDaemon({ verbose });
32
- if (result.success) {
33
- console.log(result.message);
34
- }
35
- else {
36
- console.error(result.message);
37
- process.exit(1);
38
- }
39
- }
40
- async function handleStop(force) {
41
- const result = await stopDaemon({ force });
42
- if (result.success) {
43
- console.log(result.message);
44
- }
45
- else {
46
- console.error(result.message);
47
- process.exit(1);
48
- }
49
- }
50
- function handleStatus() {
51
- const status = getDaemonStatus();
52
- if (!status.running) {
53
- console.log('Sync daemon: not running');
54
- console.log('Run `mod sync start` to begin tracking changes');
55
- return;
56
- }
57
- console.log(`Sync daemon: running (pid ${status.pid})`);
58
- console.log('');
59
- if (status.connections.length > 0) {
60
- console.log('Connected directories:');
61
- for (const conn of status.connections) {
62
- const branchInfo = conn.gitBranch ? ` (${conn.gitBranch})` : ' (no git)';
63
- console.log(` ${conn.path} → ${conn.workspaceName}${branchInfo}`);
64
- }
65
- console.log('');
66
- }
67
- switch (status.cloudSync) {
68
- case 'connected':
69
- console.log('Cloud sync: connected');
70
- if (status.lastSync) {
71
- const ago = formatTimeAgo(status.lastSync);
72
- console.log(`Last sync: ${ago}`);
73
- }
74
- console.log(`Pending changes: ${status.pendingChanges}`);
75
- break;
76
- case 'not_signed_in':
77
- console.log('Cloud sync: not signed in');
78
- console.log('Tracking changes locally');
79
- console.log('');
80
- console.log('Sign in to sync with collaborators: mod auth login');
81
- break;
82
- case 'disconnected':
83
- console.log('Cloud sync: disconnected');
84
- console.log('Changes will sync when connection is restored');
85
- break;
86
- }
87
- }
88
- function showUsage() {
89
- console.log('Usage: mod sync <command>');
90
- console.log('');
91
- console.log('Commands:');
92
- console.log(' start Start the sync daemon in the background');
93
- console.log(' stop Stop the sync daemon');
94
- console.log(' restart Restart the sync daemon');
95
- console.log(' status Show daemon status');
96
- console.log('');
97
- console.log('Options:');
98
- console.log(' --force Force stop operations');
99
- console.log(' --verbose Show daemon output in terminal');
100
- }
101
- function formatTimeAgo(isoString) {
102
- const date = new Date(isoString);
103
- const now = new Date();
104
- const diffMs = now.getTime() - date.getTime();
105
- const diffSeconds = Math.floor(diffMs / 1000);
106
- if (diffSeconds < 60) {
107
- return `${diffSeconds} seconds ago`;
108
- }
109
- const diffMinutes = Math.floor(diffSeconds / 60);
110
- if (diffMinutes < 60) {
111
- return `${diffMinutes} minute${diffMinutes === 1 ? '' : 's'} ago`;
112
- }
113
- const diffHours = Math.floor(diffMinutes / 60);
114
- if (diffHours < 24) {
115
- return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;
116
- }
117
- const diffDays = Math.floor(diffHours / 24);
118
- return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
119
- }