@mod-computer/cli 0.2.3 → 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 (75) hide show
  1. package/dist/cli.bundle.js +216 -36371
  2. package/package.json +3 -3
  3. package/dist/app.js +0 -227
  4. package/dist/cli.bundle.js.map +0 -7
  5. package/dist/cli.js +0 -132
  6. package/dist/commands/add.js +0 -245
  7. package/dist/commands/agents-run.js +0 -71
  8. package/dist/commands/auth.js +0 -259
  9. package/dist/commands/branch.js +0 -1411
  10. package/dist/commands/claude-sync.js +0 -772
  11. package/dist/commands/comment.js +0 -568
  12. package/dist/commands/diff.js +0 -182
  13. package/dist/commands/index.js +0 -73
  14. package/dist/commands/init.js +0 -597
  15. package/dist/commands/ls.js +0 -135
  16. package/dist/commands/members.js +0 -687
  17. package/dist/commands/mv.js +0 -282
  18. package/dist/commands/recover.js +0 -207
  19. package/dist/commands/rm.js +0 -257
  20. package/dist/commands/spec.js +0 -386
  21. package/dist/commands/status.js +0 -296
  22. package/dist/commands/sync.js +0 -119
  23. package/dist/commands/trace.js +0 -1752
  24. package/dist/commands/workspace.js +0 -447
  25. package/dist/components/conflict-resolution-ui.js +0 -120
  26. package/dist/components/messages.js +0 -5
  27. package/dist/components/thread.js +0 -8
  28. package/dist/config/features.js +0 -83
  29. package/dist/containers/branches-container.js +0 -140
  30. package/dist/containers/directory-container.js +0 -92
  31. package/dist/containers/thread-container.js +0 -214
  32. package/dist/containers/threads-container.js +0 -27
  33. package/dist/containers/workspaces-container.js +0 -27
  34. package/dist/daemon/conflict-resolution.js +0 -172
  35. package/dist/daemon/content-hash.js +0 -31
  36. package/dist/daemon/file-sync.js +0 -985
  37. package/dist/daemon/index.js +0 -203
  38. package/dist/daemon/mime-types.js +0 -166
  39. package/dist/daemon/offline-queue.js +0 -211
  40. package/dist/daemon/path-utils.js +0 -64
  41. package/dist/daemon/share-policy.js +0 -83
  42. package/dist/daemon/wasm-errors.js +0 -189
  43. package/dist/daemon/worker.js +0 -557
  44. package/dist/daemon-worker.js +0 -258
  45. package/dist/errors/workspace-errors.js +0 -48
  46. package/dist/lib/auth-server.js +0 -216
  47. package/dist/lib/browser.js +0 -35
  48. package/dist/lib/diff.js +0 -284
  49. package/dist/lib/formatters.js +0 -204
  50. package/dist/lib/git.js +0 -137
  51. package/dist/lib/local-fs.js +0 -201
  52. package/dist/lib/prompts.js +0 -56
  53. package/dist/lib/storage.js +0 -213
  54. package/dist/lib/trace-formatters.js +0 -314
  55. package/dist/services/add-service.js +0 -554
  56. package/dist/services/add-validation.js +0 -124
  57. package/dist/services/automatic-file-tracker.js +0 -303
  58. package/dist/services/cli-orchestrator.js +0 -227
  59. package/dist/services/feature-flags.js +0 -187
  60. package/dist/services/file-import-service.js +0 -283
  61. package/dist/services/file-transformation-service.js +0 -218
  62. package/dist/services/logger.js +0 -44
  63. package/dist/services/mod-config.js +0 -67
  64. package/dist/services/modignore-service.js +0 -328
  65. package/dist/services/sync-daemon.js +0 -244
  66. package/dist/services/thread-notification-service.js +0 -50
  67. package/dist/services/thread-service.js +0 -147
  68. package/dist/stores/use-directory-store.js +0 -96
  69. package/dist/stores/use-threads-store.js +0 -46
  70. package/dist/stores/use-workspaces-store.js +0 -54
  71. package/dist/types/add-types.js +0 -99
  72. package/dist/types/config.js +0 -16
  73. package/dist/types/index.js +0 -2
  74. package/dist/types/workspace-connection.js +0 -53
  75. 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
- }