@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.
- package/package.json +3 -3
- package/dist/app.js +0 -227
- package/dist/cli.bundle.js.map +0 -7
- package/dist/cli.js +0 -132
- package/dist/commands/add.js +0 -245
- package/dist/commands/agents-run.js +0 -71
- package/dist/commands/auth.js +0 -259
- package/dist/commands/branch.js +0 -1411
- package/dist/commands/claude-sync.js +0 -772
- package/dist/commands/comment.js +0 -568
- package/dist/commands/diff.js +0 -182
- package/dist/commands/index.js +0 -73
- package/dist/commands/init.js +0 -597
- package/dist/commands/ls.js +0 -135
- package/dist/commands/members.js +0 -687
- package/dist/commands/mv.js +0 -282
- package/dist/commands/recover.js +0 -207
- package/dist/commands/rm.js +0 -257
- package/dist/commands/spec.js +0 -386
- package/dist/commands/status.js +0 -296
- package/dist/commands/sync.js +0 -119
- package/dist/commands/trace.js +0 -1752
- package/dist/commands/workspace.js +0 -447
- package/dist/components/conflict-resolution-ui.js +0 -120
- package/dist/components/messages.js +0 -5
- package/dist/components/thread.js +0 -8
- package/dist/config/features.js +0 -83
- package/dist/containers/branches-container.js +0 -140
- package/dist/containers/directory-container.js +0 -92
- package/dist/containers/thread-container.js +0 -214
- package/dist/containers/threads-container.js +0 -27
- package/dist/containers/workspaces-container.js +0 -27
- package/dist/daemon/conflict-resolution.js +0 -172
- package/dist/daemon/content-hash.js +0 -31
- package/dist/daemon/file-sync.js +0 -985
- package/dist/daemon/index.js +0 -203
- package/dist/daemon/mime-types.js +0 -166
- package/dist/daemon/offline-queue.js +0 -211
- package/dist/daemon/path-utils.js +0 -64
- package/dist/daemon/share-policy.js +0 -83
- package/dist/daemon/wasm-errors.js +0 -189
- package/dist/daemon/worker.js +0 -557
- package/dist/daemon-worker.js +0 -258
- package/dist/errors/workspace-errors.js +0 -48
- package/dist/lib/auth-server.js +0 -216
- package/dist/lib/browser.js +0 -35
- package/dist/lib/diff.js +0 -284
- package/dist/lib/formatters.js +0 -204
- package/dist/lib/git.js +0 -137
- package/dist/lib/local-fs.js +0 -201
- package/dist/lib/prompts.js +0 -56
- package/dist/lib/storage.js +0 -213
- package/dist/lib/trace-formatters.js +0 -314
- package/dist/services/add-service.js +0 -554
- package/dist/services/add-validation.js +0 -124
- package/dist/services/automatic-file-tracker.js +0 -303
- package/dist/services/cli-orchestrator.js +0 -227
- package/dist/services/feature-flags.js +0 -187
- package/dist/services/file-import-service.js +0 -283
- package/dist/services/file-transformation-service.js +0 -218
- package/dist/services/logger.js +0 -44
- package/dist/services/mod-config.js +0 -67
- package/dist/services/modignore-service.js +0 -328
- package/dist/services/sync-daemon.js +0 -244
- package/dist/services/thread-notification-service.js +0 -50
- package/dist/services/thread-service.js +0 -147
- package/dist/stores/use-directory-store.js +0 -96
- package/dist/stores/use-threads-store.js +0 -46
- package/dist/stores/use-workspaces-store.js +0 -54
- package/dist/types/add-types.js +0 -99
- package/dist/types/config.js +0 -16
- package/dist/types/index.js +0 -2
- package/dist/types/workspace-connection.js +0 -53
- 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;
|
package/dist/config/features.js
DELETED
|
@@ -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
|
-
}
|