@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,447 +0,0 @@
1
- // glassware[type="implementation", id="cli-workspace-command--7ef7e657", requirements="requirement-cli-init-ux-2--b69b045f,requirement-cli-init-ux-3--3dde4846,requirement-cli-init-app-1--1c2b11b4"]
2
- // spec: packages/mod-cli/specs/workspaces.md
3
- import { createModWorkspace, createModUser } from '@mod/mod-core';
4
- import { listWorkspaceConnections, readWorkspaceConnection, writeWorkspaceConnection, deleteWorkspaceConnection, readConfig, } from '../lib/storage.js';
5
- import { input, select, validateWorkspaceName } from '../lib/prompts.js';
6
- export async function workspaceCommand(args, repo) {
7
- const [subcommand, ...rest] = args;
8
- switch (subcommand) {
9
- case 'create':
10
- await handleCreateWorkspace(rest, repo);
11
- break;
12
- case 'list':
13
- await handleListWorkspaces();
14
- break;
15
- case 'list-remote':
16
- await handleListRemoteWorkspaces(repo);
17
- break;
18
- case 'connect':
19
- await handleConnectWorkspace(rest, repo);
20
- break;
21
- case 'info':
22
- await handleWorkspaceInfo();
23
- break;
24
- case 'disconnect':
25
- await handleDisconnectWorkspace();
26
- break;
27
- case 'register':
28
- await handleRegisterWorkspaces(repo);
29
- break;
30
- case 'clear':
31
- await handleClearWorkspace(rest, repo);
32
- break;
33
- default:
34
- if (!subcommand) {
35
- await handleListWorkspaces();
36
- }
37
- else {
38
- console.error('Usage: mod workspace <create|list|list-remote|connect|info|disconnect|register|clear>');
39
- console.error('');
40
- console.error('Commands:');
41
- console.error(' create [name] Create new workspace in current directory');
42
- console.error(' list List all locally connected workspaces');
43
- console.error(' list-remote List all workspaces from your user account');
44
- console.error(' connect <id> [--name ...] Connect current directory to existing workspace');
45
- console.error(' info Show current directory workspace info');
46
- console.error(' disconnect Disconnect current directory from workspace');
47
- console.error(' register Register connected workspaces to your account');
48
- console.error(' clear [id] Delete all files from a workspace (DESTRUCTIVE)');
49
- process.exit(1);
50
- }
51
- }
52
- process.exit(0);
53
- }
54
- async function handleCreateWorkspace(args, repo) {
55
- const currentDir = process.cwd();
56
- // Check if already connected
57
- const existing = readWorkspaceConnection(currentDir);
58
- if (existing) {
59
- console.log(`Already connected to workspace: ${existing.workspaceName}`);
60
- console.log('');
61
- console.log('To create a new workspace, first disconnect:');
62
- console.log(' mod workspace disconnect');
63
- process.exit(1);
64
- }
65
- // Get workspace name
66
- let name = args[0];
67
- if (!name) {
68
- const dirName = currentDir.split('/').pop() || 'workspace';
69
- const defaultName = dirName.charAt(0).toUpperCase() + dirName.slice(1);
70
- name = await input('Workspace name', {
71
- default: defaultName,
72
- validate: validateWorkspaceName,
73
- });
74
- }
75
- else {
76
- const validationError = validateWorkspaceName(name);
77
- if (validationError) {
78
- console.error(`Invalid workspace name: ${validationError}`);
79
- process.exit(1);
80
- }
81
- }
82
- console.log('Creating workspace...');
83
- try {
84
- const modWorkspace = createModWorkspace(repo);
85
- // No branching by default (enableBranching: true to opt-in)
86
- const workspace = await modWorkspace.createWorkspace({ name });
87
- // Wait for workspace document to sync to server
88
- console.log('Syncing to server...');
89
- await new Promise(resolve => setTimeout(resolve, 3000));
90
- const connection = {
91
- path: currentDir,
92
- workspaceId: workspace.id,
93
- workspaceName: workspace.name,
94
- connectedAt: new Date().toISOString(),
95
- lastSyncedAt: new Date().toISOString(),
96
- };
97
- writeWorkspaceConnection(currentDir, connection);
98
- console.log('');
99
- console.log(`Created workspace: ${workspace.name}`);
100
- console.log(`ID: ${workspace.id}`);
101
- console.log(`Path: ${currentDir}`);
102
- console.log('');
103
- console.log('Run `mod sync start` to begin tracking changes');
104
- }
105
- catch (error) {
106
- console.error('Failed to create workspace:', error.message);
107
- process.exit(1);
108
- }
109
- }
110
- async function handleListWorkspaces() {
111
- const connections = listWorkspaceConnections();
112
- if (connections.length === 0) {
113
- console.log('No workspaces connected.');
114
- console.log('');
115
- console.log('To connect a directory to a workspace:');
116
- console.log(' cd /path/to/project');
117
- console.log(' mod init');
118
- return;
119
- }
120
- const currentDir = process.cwd();
121
- const currentConnection = readWorkspaceConnection(currentDir);
122
- console.log('Connected workspaces:');
123
- console.log('');
124
- for (const conn of connections) {
125
- const isCurrent = currentConnection?.workspaceId === conn.workspaceId &&
126
- currentConnection?.path === conn.path;
127
- const marker = isCurrent ? '* ' : ' ';
128
- console.log(`${marker}${conn.workspaceName}`);
129
- console.log(` Path: ${conn.path}`);
130
- console.log(` ID: ${conn.workspaceId}`);
131
- console.log(` Connected: ${formatDate(conn.connectedAt)}`);
132
- console.log('');
133
- }
134
- if (currentConnection) {
135
- console.log(`Current directory is connected to: ${currentConnection.workspaceName}`);
136
- }
137
- else {
138
- console.log('Current directory is not connected to any workspace.');
139
- console.log('Run `mod init` to connect.');
140
- }
141
- }
142
- async function handleWorkspaceInfo() {
143
- const currentDir = process.cwd();
144
- const connection = readWorkspaceConnection(currentDir);
145
- if (!connection) {
146
- console.log('Current directory is not connected to a workspace.');
147
- console.log('');
148
- console.log('To connect:');
149
- console.log(' mod init');
150
- process.exit(1);
151
- }
152
- console.log(`Workspace: ${connection.workspaceName}`);
153
- console.log(`ID: ${connection.workspaceId}`);
154
- console.log(`Path: ${connection.path}`);
155
- console.log(`Connected: ${formatDate(connection.connectedAt)}`);
156
- console.log(`Last synced: ${formatDate(connection.lastSyncedAt)}`);
157
- }
158
- async function handleDisconnectWorkspace() {
159
- const currentDir = process.cwd();
160
- const connection = readWorkspaceConnection(currentDir);
161
- if (!connection) {
162
- console.log('Current directory is not connected to a workspace.');
163
- process.exit(1);
164
- }
165
- const choice = await select('Disconnect from workspace?', [
166
- { label: 'Yes, disconnect', value: 'yes' },
167
- { label: 'Cancel', value: 'no' },
168
- ]);
169
- if (choice === 'no') {
170
- console.log('Cancelled.');
171
- return;
172
- }
173
- const deleted = deleteWorkspaceConnection(currentDir);
174
- if (deleted) {
175
- console.log(`Disconnected from workspace: ${connection.workspaceName}`);
176
- console.log('');
177
- console.log('The workspace data is preserved. To reconnect:');
178
- console.log(' mod init');
179
- }
180
- else {
181
- console.error('Failed to disconnect.');
182
- process.exit(1);
183
- }
184
- }
185
- async function handleRegisterWorkspaces(repo) {
186
- const config = readConfig();
187
- if (!config.auth) {
188
- console.log('Not signed in.');
189
- console.log('');
190
- console.log('Sign in first with: mod auth login');
191
- process.exit(1);
192
- }
193
- if (!config.auth.userDocId) {
194
- console.log('User document ID not found.');
195
- console.log('');
196
- console.log('Try logging in again: mod auth login');
197
- process.exit(1);
198
- }
199
- const connections = listWorkspaceConnections();
200
- if (connections.length === 0) {
201
- console.log('No connected workspaces found.');
202
- console.log('');
203
- console.log('Connect a directory to a workspace with: mod init');
204
- return;
205
- }
206
- console.log(`Registering ${connections.length} workspace(s) to your user account...`);
207
- console.log('');
208
- const modUser = createModUser(repo);
209
- const userDocId = config.auth.userDocId;
210
- console.log('Using user document:', userDocId);
211
- let registered = 0;
212
- for (const conn of connections) {
213
- try {
214
- await modUser.addWorkspace(userDocId, conn.workspaceId);
215
- console.log(`✓ Registered: ${conn.workspaceName}`);
216
- registered++;
217
- }
218
- catch (error) {
219
- console.warn(` Failed to register ${conn.workspaceName}: ${error.message}`);
220
- }
221
- }
222
- console.log('');
223
- console.log(`✓ Registered ${registered} of ${connections.length} workspaces`);
224
- console.log('');
225
- // Wait for changes to sync to server
226
- console.log('Syncing changes to server...');
227
- await new Promise(resolve => setTimeout(resolve, 2000));
228
- console.log('');
229
- console.log('These workspaces should now appear in the web app.');
230
- }
231
- async function handleListRemoteWorkspaces(repo) {
232
- const config = readConfig();
233
- if (!config.auth) {
234
- console.log('Not signed in.');
235
- console.log('');
236
- console.log('Sign in first with: mod auth login');
237
- process.exit(1);
238
- }
239
- if (!config.auth.userDocId) {
240
- console.log('User document ID not found.');
241
- console.log('');
242
- console.log('Try logging in again: mod auth login');
243
- process.exit(1);
244
- }
245
- console.log('Fetching workspaces from your user account...');
246
- console.log('');
247
- try {
248
- const modUser = createModUser(repo);
249
- const userDocId = config.auth.userDocId;
250
- // Get the user doc
251
- const userHandle = await repo.find(userDocId);
252
- await userHandle.whenReady();
253
- const userDoc = userHandle.doc();
254
- if (!userDoc) {
255
- console.log('Could not load user document.');
256
- process.exit(1);
257
- }
258
- // Access workspaces from user doc
259
- const workspaceRefs = userDoc.workspaceIds || [];
260
- if (workspaceRefs.length === 0) {
261
- console.log('No workspaces found in your account.');
262
- console.log('');
263
- console.log('To register your local workspaces:');
264
- console.log(' mod workspace register');
265
- process.exit(0);
266
- }
267
- console.log(`Found ${workspaceRefs.length} workspace(s):\n`);
268
- for (const wsId of workspaceRefs) {
269
- console.log(` ${wsId}`);
270
- // Try to load workspace name
271
- try {
272
- const wsHandle = await repo.find(wsId);
273
- await wsHandle.whenReady();
274
- const ws = wsHandle.doc();
275
- if (ws && ws.name) {
276
- console.log(` Name: ${ws.name}`);
277
- }
278
- }
279
- catch (e) {
280
- // Workspace might not be available
281
- }
282
- console.log('');
283
- }
284
- console.log('To connect to a workspace:');
285
- console.log(' mod workspace connect <workspace-id> <workspace-name>');
286
- }
287
- catch (error) {
288
- console.error('Failed to fetch workspaces:', error.message);
289
- process.exit(1);
290
- }
291
- }
292
- async function handleConnectWorkspace(args, repo) {
293
- const currentDir = process.cwd();
294
- // Check if already connected
295
- const existing = readWorkspaceConnection(currentDir);
296
- if (existing) {
297
- console.log(`Already connected to workspace: ${existing.workspaceName}`);
298
- console.log('');
299
- console.log('To connect to a different workspace, first disconnect:');
300
- console.log(' mod workspace disconnect');
301
- process.exit(1);
302
- }
303
- // Parse args: first positional is workspace ID, --name is optional override
304
- let workspaceId;
305
- let nameOverride;
306
- for (let i = 0; i < args.length; i++) {
307
- if (args[i] === '--name' && args[i + 1]) {
308
- nameOverride = args[i + 1];
309
- i++; // Skip the next arg
310
- }
311
- else if (!workspaceId && !args[i].startsWith('--')) {
312
- workspaceId = args[i];
313
- }
314
- }
315
- if (!workspaceId) {
316
- console.error('Usage: mod workspace connect <workspace-id> [--name <display-name>]');
317
- console.error('');
318
- console.error('To see available workspaces:');
319
- console.error(' mod workspace list-remote');
320
- process.exit(1);
321
- }
322
- console.log(`Connecting to workspace...`);
323
- console.log(`ID: ${workspaceId}`);
324
- console.log('');
325
- try {
326
- // Fetch workspace to verify it exists and get name
327
- const modWorkspace = createModWorkspace(repo);
328
- const workspaceHandle = await modWorkspace.openWorkspace(workspaceId);
329
- // Use override name if provided, otherwise fetch from workspace
330
- const workspaceName = nameOverride || workspaceHandle.name || 'Untitled Workspace';
331
- console.log(`Workspace: ${workspaceName}`);
332
- // Create connection
333
- const connection = {
334
- path: currentDir,
335
- workspaceId: workspaceId,
336
- workspaceName: workspaceName,
337
- connectedAt: new Date().toISOString(),
338
- lastSyncedAt: new Date().toISOString(),
339
- };
340
- writeWorkspaceConnection(currentDir, connection);
341
- // Register workspace to user document so it shows in web app
342
- const config = readConfig();
343
- if (config.auth?.userDocId) {
344
- try {
345
- console.log('Registering workspace to your account...');
346
- const modUser = createModUser(repo);
347
- await modUser.addWorkspace(config.auth.userDocId, workspaceId);
348
- // Wait for sync
349
- await new Promise(resolve => setTimeout(resolve, 1000));
350
- console.log('✓ Workspace registered to your account');
351
- }
352
- catch (error) {
353
- console.warn('Note: Could not register workspace to account:', error.message);
354
- }
355
- }
356
- console.log('✓ Connected successfully');
357
- console.log('');
358
- console.log('To import files from this directory:');
359
- console.log(' mod init');
360
- console.log(' > Select "Resume file import"');
361
- }
362
- catch (error) {
363
- console.error('Failed to connect to workspace:', error.message);
364
- process.exit(1);
365
- }
366
- }
367
- async function handleClearWorkspace(args, repo) {
368
- const [workspaceId] = args;
369
- if (!workspaceId) {
370
- const currentDir = process.cwd();
371
- const connection = readWorkspaceConnection(currentDir);
372
- if (!connection) {
373
- console.error('No workspace specified and current directory is not connected.');
374
- console.error('');
375
- console.error('Usage: mod workspace clear <workspace-id>');
376
- console.error(' or: mod workspace clear (when in a connected directory)');
377
- process.exit(1);
378
- }
379
- console.log(`Current workspace: ${connection.workspaceName}`);
380
- console.log(`ID: ${connection.workspaceId}`);
381
- }
382
- else {
383
- console.log(`Workspace ID: ${workspaceId}`);
384
- }
385
- console.log('');
386
- console.warn('⚠️ WARNING: This will delete ALL files from the workspace.');
387
- console.warn('⚠️ This action CANNOT be undone.');
388
- console.log('');
389
- const confirmation = await select('Are you absolutely sure?', [
390
- { label: 'No, cancel', value: 'no' },
391
- { label: 'Yes, delete all files', value: 'yes' },
392
- ]);
393
- if (confirmation === 'no') {
394
- console.log('Cancelled.');
395
- return;
396
- }
397
- const targetWorkspaceId = workspaceId || readWorkspaceConnection(process.cwd()).workspaceId;
398
- try {
399
- console.log('Loading workspace...');
400
- const modWorkspace = createModWorkspace(repo);
401
- const workspaceHandle = await modWorkspace.openWorkspace(targetWorkspaceId);
402
- console.log('Fetching files...');
403
- const files = await workspaceHandle.file.list();
404
- console.log(`Found ${files.length} files to delete`);
405
- if (files.length === 0) {
406
- console.log('Workspace is already empty.');
407
- return;
408
- }
409
- console.log('Deleting files...');
410
- let deleted = 0;
411
- for (const file of files) {
412
- try {
413
- await workspaceHandle.file.delete(file.id);
414
- deleted++;
415
- if (deleted % 50 === 0) {
416
- console.log(` Deleted ${deleted}/${files.length} files...`);
417
- }
418
- }
419
- catch (error) {
420
- console.warn(` Failed to delete ${file.name}:`, error instanceof Error ? error.message : error);
421
- }
422
- }
423
- console.log('');
424
- console.log(`✓ Deleted ${deleted} files`);
425
- console.log('');
426
- console.log('The workspace is now empty. You can import files again with:');
427
- console.log(' mod init');
428
- console.log(' > Select "Resume file import"');
429
- }
430
- catch (error) {
431
- console.error('Failed to clear workspace:', error.message);
432
- process.exit(1);
433
- }
434
- }
435
- function formatDate(isoString) {
436
- const date = new Date(isoString);
437
- const now = new Date();
438
- const diffMs = now.getTime() - date.getTime();
439
- const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
440
- if (diffHours < 1)
441
- return 'just now';
442
- if (diffHours < 24)
443
- return `${diffHours}h ago`;
444
- if (diffHours < 24 * 7)
445
- return `${Math.floor(diffHours / 24)}d ago`;
446
- return date.toLocaleDateString();
447
- }
@@ -1,120 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Box, Text, useInput } from 'ink';
4
- const options = [
5
- {
6
- key: 'local',
7
- label: 'Keep Local',
8
- description: 'Keep the local version and skip remote',
9
- color: 'blue',
10
- },
11
- {
12
- key: 'remote',
13
- label: 'Keep Remote',
14
- description: 'Overwrite local with remote version',
15
- color: 'green',
16
- },
17
- {
18
- key: 'both',
19
- label: 'Keep Both',
20
- description: 'Keep local and save remote with suffix',
21
- color: 'yellow',
22
- },
23
- {
24
- key: 'skip',
25
- label: 'Skip',
26
- description: 'Skip this file for now',
27
- color: 'gray',
28
- },
29
- ];
30
- export default function ConflictResolutionUI({ conflict, onResolve, onCancel, }) {
31
- const [selectedIndex, setSelectedIndex] = useState(0);
32
- useInput((input, key) => {
33
- if (key.downArrow) {
34
- setSelectedIndex((prev) => (prev + 1) % options.length);
35
- }
36
- else if (key.upArrow) {
37
- setSelectedIndex((prev) => (prev - 1 + options.length) % options.length);
38
- }
39
- else if (key.return) {
40
- onResolve(options[selectedIndex].key);
41
- }
42
- else if (key.escape && onCancel) {
43
- onCancel();
44
- }
45
- });
46
- const formatFileSize = (bytes) => {
47
- if (!bytes)
48
- return 'Unknown size';
49
- if (bytes < 1024)
50
- return `${bytes} B`;
51
- if (bytes < 1024 * 1024)
52
- return `${(bytes / 1024).toFixed(1)} KB`;
53
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
54
- };
55
- const formatDate = (dateString) => {
56
- if (!dateString)
57
- return 'Unknown date';
58
- try {
59
- return new Date(dateString).toLocaleString();
60
- }
61
- catch {
62
- return dateString;
63
- }
64
- };
65
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "red", children: "\uD83D\uDD25 File Conflict Detected" }) }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, paddingX: 2, children: [_jsxs(Text, { bold: true, color: "white", children: ["File: ", conflict.fileName] }), _jsxs(Text, { color: "gray", children: ["Path: ", conflict.localPath] })] }), _jsxs(Box, { flexDirection: "row", marginBottom: 1, paddingX: 2, children: [_jsxs(Box, { flexDirection: "column", width: "50%", children: [_jsx(Text, { bold: true, color: "blue", children: "\uD83D\uDCC1 Local Version" }), _jsxs(Text, { color: "gray", children: ["Size: ", formatFileSize(conflict.localSize)] }), _jsxs(Text, { color: "gray", children: ["Modified: ", formatDate(conflict.localModified)] })] }), _jsxs(Box, { flexDirection: "column", width: "50%", children: [_jsx(Text, { bold: true, color: "green", children: "\u2601\uFE0F Remote Version" }), _jsxs(Text, { color: "gray", children: ["Size: ", formatFileSize(conflict.remoteSize)] }), _jsxs(Text, { color: "gray", children: ["Modified: ", formatDate(conflict.remoteModified)] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "white", children: "How would you like to resolve this conflict?" }) }), options.map((option, index) => (_jsxs(Box, { marginLeft: 2, children: [_jsxs(Text, { color: selectedIndex === index ? 'white' : 'gray', backgroundColor: selectedIndex === index ? option.color : undefined, bold: selectedIndex === index, children: [selectedIndex === index ? '▶ ' : ' ', option.label] }), _jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: "gray", children: ["- ", option.description] }) })] }, option.key)))] }), _jsx(Box, { marginTop: 1, paddingX: 2, children: _jsxs(Text, { color: "gray", dimColor: true, children: ["Use \u2191\u2193 arrow keys to navigate, Enter to select", onCancel ? ', Esc to cancel' : ''] }) })] }));
66
- }
67
- export function BatchConflictResolutionUI({ conflicts, onResolveAll, onResolveOne, onCancel, }) {
68
- const [currentIndex, setCurrentIndex] = useState(0);
69
- const [resolutions, setResolutions] = useState({});
70
- const [showBatchOptions, setShowBatchOptions] = useState(false);
71
- const [selectedBatchOption, setSelectedBatchOption] = useState(0);
72
- const batchOptions = [
73
- { key: 'local', label: 'Keep All Local', description: 'Keep all local versions' },
74
- { key: 'remote', label: 'Keep All Remote', description: 'Overwrite all with remote versions' },
75
- { key: 'both', label: 'Keep All Both', description: 'Keep all local and save remote with suffix' },
76
- ];
77
- useInput((input, key) => {
78
- if (showBatchOptions) {
79
- if (key.downArrow) {
80
- setSelectedBatchOption((prev) => (prev + 1) % batchOptions.length);
81
- }
82
- else if (key.upArrow) {
83
- setSelectedBatchOption((prev) => (prev - 1 + batchOptions.length) % batchOptions.length);
84
- }
85
- else if (key.return) {
86
- const resolution = batchOptions[selectedBatchOption].key;
87
- const allResolutions = {};
88
- conflicts.forEach(conflict => {
89
- allResolutions[conflict.fileName] = resolution;
90
- });
91
- onResolveAll(allResolutions);
92
- }
93
- else if (key.escape) {
94
- setShowBatchOptions(false);
95
- }
96
- }
97
- else {
98
- if (input === 'a') {
99
- setShowBatchOptions(true);
100
- }
101
- else if (key.escape && onCancel) {
102
- onCancel();
103
- }
104
- }
105
- });
106
- const handleResolveOne = (resolution) => {
107
- const conflict = conflicts[currentIndex];
108
- setResolutions(prev => ({ ...prev, [conflict.fileName]: resolution }));
109
- onResolveOne(conflict.fileName, resolution);
110
- if (currentIndex < conflicts.length - 1) {
111
- setCurrentIndex(prev => prev + 1);
112
- }
113
- };
114
- if (showBatchOptions) {
115
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "yellow", children: "\uD83D\uDD25 Batch Conflict Resolution" }) }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { children: ["Apply the same resolution to all ", conflicts.length, " conflicts:"] }) }), batchOptions.map((option, index) => (_jsxs(Box, { marginLeft: 2, marginBottom: 1, children: [_jsxs(Text, { color: selectedBatchOption === index ? 'white' : 'gray', backgroundColor: selectedBatchOption === index ? 'blue' : undefined, bold: selectedBatchOption === index, children: [selectedBatchOption === index ? '▶ ' : ' ', option.label] }), _jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: "gray", children: ["- ", option.description] }) })] }, option.key))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", dimColor: true, children: "Use \u2191\u2193 arrow keys to navigate, Enter to apply to all, Esc to go back" }) })] }));
116
- }
117
- const currentConflict = conflicts[currentIndex];
118
- const resolvedCount = Object.keys(resolutions).length;
119
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, paddingX: 1, children: _jsxs(Text, { color: "yellow", children: ["Conflict ", currentIndex + 1, " of ", conflicts.length, " (", resolvedCount, " resolved)"] }) }), _jsx(ConflictResolutionUI, { conflict: currentConflict, onResolve: handleResolveOne }), _jsx(Box, { marginTop: 1, paddingX: 2, children: _jsx(Text, { color: "gray", dimColor: true, children: "Press 'a' for batch resolution options, Esc to cancel" }) })] }));
120
- }
@@ -1,5 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React from 'react';
3
- import { Box, Text } from 'ink';
4
- const MessageList = React.memo(({ messages, parseContentSegments, messageKeyProp }) => (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: messages.length === 0 ? (_jsx(Text, { color: "gray", children: "No messages yet. Type to start the thread." })) : (messages.map((m, i) => (_jsxs(Text, { children: [_jsxs(Text, { color: "yellow", children: [new Date(m.timestamp).toLocaleTimeString(), " "] }), _jsxs(Text, { color: m.userType === 'user' ? 'cyan' : 'magenta', children: [m.userType === 'user' ? (m.user?.name || 'You') : 'Assistant', ":"] }), ' ', parseContentSegments(m.text).map((seg, idx) => seg.isBold ? (_jsx(Text, { bold: true, color: "whiteBright", children: seg.text }, idx)) : (_jsx(Text, { children: seg.text }, idx)))] }, `${m.id || i}-${m._contentHash || m.text.length}`)))) })));
5
- export default MessageList;
@@ -1,8 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React from 'react';
3
- import { Box, Text } from 'ink';
4
- import MessageList from './messages.js';
5
- const ThreadView = React.memo(({ activeThread, messages, parseContentSegments, messageKeyProp }) => {
6
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Thread: ", _jsx(Text, { color: "green", children: activeThread.name })] }), _jsx(MessageList, { messages: messages, parseContentSegments: parseContentSegments, messageKeyProp: messageKeyProp }), _jsxs(Text, { color: "cyan", children: ["Type ", _jsx(Text, { bold: true, children: "/branch" }), " to switch threads."] })] }));
7
- });
8
- export default ThreadView;
@@ -1,83 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- export const FEATURES = {
5
- STATUS: 'status',
6
- WORKSPACE_MANAGEMENT: 'workspace-management',
7
- WORKSPACE_BRANCHING: 'workspace-branching',
8
- TASK_MANAGEMENT: 'task-management',
9
- FILE_OPERATIONS: 'file-operations',
10
- AGENT_INTEGRATIONS: 'agent-integrations',
11
- SYNC_OPERATIONS: 'sync-operations',
12
- WATCH_OPERATIONS: 'watch-operations',
13
- CONNECTOR_INTEGRATIONS: 'connector-integrations',
14
- AUTH: 'auth',
15
- MEMBER_MANAGEMENT: 'member-management',
16
- TRACING: 'tracing'
17
- };
18
- let releaseProfile = null;
19
- export function isFeatureEnabled(feature) {
20
- if (process.env.NODE_ENV === 'development') {
21
- const envVar = `MOD_FEATURE_${feature.toUpperCase().replace('-', '_')}`;
22
- if (process.env[envVar] === 'true')
23
- return true;
24
- if (process.env[envVar] === 'false')
25
- return false;
26
- }
27
- if (!releaseProfile) {
28
- releaseProfile = loadReleaseProfile();
29
- }
30
- return releaseProfile[feature] ?? false;
31
- }
32
- function loadReleaseProfile() {
33
- const profileName = process.env.MOD_RELEASE_PROFILE || 'mvp';
34
- try {
35
- const __filename = fileURLToPath(import.meta.url);
36
- const __dirname = path.dirname(__filename);
37
- // Try multiple paths to support both bundled and unbundled modes
38
- const candidates = [
39
- path.join(__dirname, 'release-profiles', `${profileName}.json`), // unbundled: dist/config/
40
- path.join(__dirname, 'config', 'release-profiles', `${profileName}.json`), // bundled: dist/
41
- ];
42
- for (const profilePath of candidates) {
43
- if (fs.existsSync(profilePath)) {
44
- const profileData = fs.readFileSync(profilePath, 'utf8');
45
- return JSON.parse(profileData);
46
- }
47
- }
48
- throw new Error(`Profile ${profileName} not found`);
49
- }
50
- catch (error) {
51
- if (profileName === 'development') {
52
- return {
53
- [FEATURES.STATUS]: true,
54
- [FEATURES.WORKSPACE_MANAGEMENT]: true,
55
- [FEATURES.WORKSPACE_BRANCHING]: true,
56
- [FEATURES.TASK_MANAGEMENT]: true,
57
- [FEATURES.FILE_OPERATIONS]: true,
58
- [FEATURES.AGENT_INTEGRATIONS]: true,
59
- [FEATURES.SYNC_OPERATIONS]: true,
60
- [FEATURES.WATCH_OPERATIONS]: true,
61
- [FEATURES.CONNECTOR_INTEGRATIONS]: true,
62
- [FEATURES.AUTH]: true,
63
- [FEATURES.MEMBER_MANAGEMENT]: true,
64
- [FEATURES.TRACING]: true
65
- };
66
- }
67
- // Fallback to minimal profile for unknown profiles
68
- return {
69
- [FEATURES.STATUS]: true,
70
- [FEATURES.WORKSPACE_MANAGEMENT]: false,
71
- [FEATURES.WORKSPACE_BRANCHING]: false,
72
- [FEATURES.TASK_MANAGEMENT]: false,
73
- [FEATURES.FILE_OPERATIONS]: false,
74
- [FEATURES.AGENT_INTEGRATIONS]: false,
75
- [FEATURES.SYNC_OPERATIONS]: false,
76
- [FEATURES.WATCH_OPERATIONS]: false,
77
- [FEATURES.CONNECTOR_INTEGRATIONS]: false
78
- };
79
- }
80
- }
81
- export function resetFeatureCache() {
82
- releaseProfile = null;
83
- }