@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,597 +0,0 @@
1
- #!/usr/bin/env node
2
- // glassware[type="implementation", id="cli-init-command--da781f68", requirements="requirement-cli-init-ux-1--1e5666e7,requirement-cli-init-ux-2--b69b045f,requirement-cli-init-ux-3--3dde4846,requirement-cli-init-ux-4--140d9249,requirement-cli-init-ux-5a--09a5bdab,requirement-cli-init-ux-5b--4cb7bb13,requirement-cli-init-ux-6--1627332e,requirement-cli-init-ux-7--97fe5eff,requirement-cli-init-app-1--1c2b11b4,requirement-cli-init-app-5--74a2ea93"]
3
- import path from 'path';
4
- import fs from 'fs';
5
- import { createModWorkspace, createModUser, setTextContent, detectMimeType, mimeTypeToCanvasType } from '@mod/mod-core';
6
- import { readConfig, readWorkspaceConnection, writeWorkspaceConnection, ensureModDir, } from '../lib/storage.js';
7
- import { select, input, validateWorkspaceName } from '../lib/prompts.js';
8
- import { addWorkspaceToSharePolicy, addFilesToSharePolicy } from '../daemon/share-policy.js';
9
- import { FileImportService } from '../services/file-import-service.js';
10
- export async function initCommand(args, repo) {
11
- const isForce = args.includes('--force');
12
- try {
13
- ensureModDir();
14
- // Check for existing workspace connection
15
- const currentDir = process.cwd();
16
- const existingConnection = readWorkspaceConnection(currentDir);
17
- if (existingConnection && !isForce) {
18
- console.log('Already initialized');
19
- console.log(`Workspace: ${existingConnection.workspaceName}`);
20
- console.log('');
21
- // Offer to resume import if there are files not yet synced
22
- const resumeChoice = await select('What would you like to do?', [
23
- { label: 'Resume file import (if interrupted)', value: 'resume' },
24
- { label: 'Start syncing', value: 'sync' },
25
- { label: 'Reinitialize with different workspace', value: 'force' },
26
- ]);
27
- if (resumeChoice === 'resume') {
28
- console.log('Checking for files to import...');
29
- try {
30
- await importDirectoryFiles(repo, { id: existingConnection.workspaceId });
31
- console.log('Resume complete');
32
- }
33
- catch (error) {
34
- console.error('Resume failed:', error instanceof Error ? error.message : error);
35
- process.exit(1);
36
- }
37
- }
38
- else if (resumeChoice === 'sync') {
39
- console.log('Run `mod sync start` to begin syncing');
40
- }
41
- else if (resumeChoice === 'force') {
42
- console.log('Reinitializing...');
43
- // Continue with force init below
44
- }
45
- if (resumeChoice !== 'force') {
46
- process.exit(0);
47
- }
48
- }
49
- // Check auth state
50
- const config = readConfig();
51
- const isAuthenticated = !!config.auth;
52
- let workspaceConnection;
53
- if (isAuthenticated) {
54
- workspaceConnection = await handleAuthenticatedInit(repo, config.auth.email);
55
- }
56
- else {
57
- workspaceConnection = await handleUnauthenticatedInit(repo);
58
- }
59
- // Workspace connection already saved during creation
60
- // Update last synced time
61
- workspaceConnection.lastSyncedAt = new Date().toISOString();
62
- writeWorkspaceConnection(currentDir, workspaceConnection);
63
- // Install the /mod agent skill for spec-driven development
64
- installModSkill();
65
- console.log('Installed /mod skill to .claude/skills/mod/');
66
- // Display success message
67
- displayInitializationSuccess(workspaceConnection, isAuthenticated);
68
- process.exit(0);
69
- }
70
- catch (error) {
71
- console.error('Initialization failed:', error.message);
72
- process.exit(1);
73
- }
74
- }
75
- // glassware[type="implementation", id="impl-init-authenticated--1fe6c419", requirements="requirement-cli-init-app-2--51f0306f,requirement-cli-init-app-3--76844b98,requirement-cli-init-qual-1--ca672702"]
76
- async function handleAuthenticatedInit(repo, email) {
77
- console.log(`Signed in as ${email}`);
78
- // Get workspace list from user document (app-2)
79
- const config = readConfig();
80
- let workspaces = [];
81
- try {
82
- const userDocId = config.auth?.userDocId;
83
- if (!userDocId) {
84
- console.warn('User document not found. Creating local workspace.');
85
- }
86
- else {
87
- // Fetch workspaces from user document (app-3)
88
- const userHandle = await repo.find(userDocId);
89
- await userHandle.whenReady();
90
- const userDoc = userHandle.doc();
91
- if (userDoc) {
92
- const workspaceIds = userDoc.workspaceIds || [];
93
- // Load workspace metadata for each workspace
94
- for (const wsId of workspaceIds) {
95
- try {
96
- const wsHandle = await repo.find(wsId);
97
- await wsHandle.whenReady();
98
- const ws = wsHandle.doc();
99
- workspaces.push({
100
- id: wsId,
101
- name: ws?.title || ws?.name || 'Untitled',
102
- });
103
- }
104
- catch {
105
- // Workspace might not be available, add with just ID
106
- workspaces.push({
107
- id: wsId,
108
- name: `Workspace ${String(wsId).slice(0, 8)}`,
109
- });
110
- }
111
- }
112
- }
113
- }
114
- }
115
- catch (error) {
116
- // Handle network errors gracefully (qual-1)
117
- console.warn('Could not load cloud workspaces. Creating local workspace.');
118
- }
119
- // Build options
120
- const options = [
121
- ...workspaces.map((w) => ({
122
- label: w.name,
123
- value: { type: 'existing', id: w.id, name: w.name },
124
- })),
125
- {
126
- label: '+ Create new workspace',
127
- value: { type: 'create', id: '', name: '' },
128
- },
129
- ];
130
- const choice = await select('Select workspace:', options);
131
- if (choice.type === 'create') {
132
- return await createNewWorkspace(repo);
133
- }
134
- // Add existing workspace to share policy so it can sync
135
- addWorkspaceToSharePolicy(choice.id);
136
- // Save workspace connection immediately
137
- const currentDir = process.cwd();
138
- const workspaceConnection = {
139
- path: currentDir,
140
- workspaceId: choice.id,
141
- workspaceName: choice.name,
142
- connectedAt: new Date().toISOString(),
143
- lastSyncedAt: new Date().toISOString(),
144
- };
145
- writeWorkspaceConnection(currentDir, workspaceConnection);
146
- console.log('Workspace connection saved');
147
- // Register existing workspace on UserDoc (in case it wasn't registered before)
148
- if (config.auth?.userDocId) {
149
- try {
150
- const modUser = createModUser(repo);
151
- await modUser.addWorkspace(config.auth.userDocId, choice.id);
152
- }
153
- catch (error) {
154
- // Silently ignore - workspace is already connected
155
- }
156
- }
157
- // Import files from directory if this is a new connection
158
- await importDirectoryFiles(repo, { id: choice.id });
159
- return workspaceConnection;
160
- }
161
- async function handleUnauthenticatedInit(repo) {
162
- const choice = await select('Select option:', [
163
- {
164
- label: 'Create local workspace',
165
- value: 'create',
166
- description: 'Work offline, sync later',
167
- },
168
- {
169
- label: 'Sign in to sync with team',
170
- value: 'signin',
171
- description: 'Access cloud workspaces',
172
- },
173
- ]);
174
- if (choice === 'signin') {
175
- // Trigger auth flow
176
- console.log('');
177
- console.log('Please run `mod auth login` to sign in, then run `mod init` again.');
178
- process.exit(0);
179
- }
180
- return await createNewWorkspace(repo);
181
- }
182
- // glassware[type="implementation", id="impl-init-create-workspace--4ff92c7c", requirements="requirement-cli-init-app-4--761a37a6,requirement-cli-init-int-1--e3f9f2b6"]
183
- async function createNewWorkspace(repo) {
184
- const currentDirName = path.basename(process.cwd());
185
- const currentDir = process.cwd();
186
- const defaultName = currentDirName.charAt(0).toUpperCase() + currentDirName.slice(1);
187
- const name = await input('Workspace name', {
188
- default: defaultName,
189
- validate: validateWorkspaceName,
190
- });
191
- console.log('Creating workspace...');
192
- // Create new workspace - works with or without auth (app-4)
193
- const modWorkspace = createModWorkspace(repo);
194
- // No branching by default (enableBranching: true to opt-in)
195
- // The CLI stores workspace IDs in its local storage instead
196
- const workspace = await modWorkspace.createWorkspace({ name });
197
- // Add workspace to share policy so it can sync
198
- addWorkspaceToSharePolicy(workspace.id);
199
- // CRITICAL: Save workspace connection immediately before import
200
- // This allows resuming if import is interrupted
201
- const workspaceConnection = {
202
- path: currentDir,
203
- workspaceId: workspace.id,
204
- workspaceName: workspace.name,
205
- connectedAt: new Date().toISOString(),
206
- lastSyncedAt: new Date().toISOString(),
207
- };
208
- writeWorkspaceConnection(currentDir, workspaceConnection);
209
- console.log('Workspace connection saved');
210
- // Wait for workspace document to sync to server
211
- console.log('Syncing workspace...');
212
- await new Promise(resolve => setTimeout(resolve, 2000));
213
- // Register workspace on UserDoc for cross-device discovery (do this before import)
214
- const config = readConfig();
215
- if (config.auth?.userDocId) {
216
- try {
217
- const modUser = createModUser(repo);
218
- await modUser.addWorkspace(config.auth.userDocId, workspace.id);
219
- // Wait for user doc to sync
220
- await new Promise(resolve => setTimeout(resolve, 1000));
221
- console.log('Workspace registered for sync');
222
- }
223
- catch (error) {
224
- // Don't fail the init if registration fails - workspace still created
225
- console.warn('Note: Could not register workspace for cross-device sync');
226
- }
227
- }
228
- // Import existing files from the directory
229
- await importDirectoryFiles(repo, workspace);
230
- console.log('Workspace synced');
231
- return workspaceConnection;
232
- }
233
- // glassware[type="implementation", id="impl-cli-init-import--74b5c00f", requirements="requirement-cli-add-init-integration--e953a345"]
234
- // TODO: Migrate to use AddService instead of FileImportService for consistency with `mod add`
235
- async function importDirectoryFiles(repo, workspace) {
236
- const currentDir = process.cwd();
237
- // Check if directory has any files to import
238
- const entries = fs.readdirSync(currentDir);
239
- const hasFiles = entries.some(entry => {
240
- const fullPath = path.join(currentDir, entry);
241
- const stats = fs.statSync(fullPath);
242
- return stats.isFile() && entry !== '.modconfig';
243
- });
244
- if (!hasFiles) {
245
- console.log('No files found to import');
246
- return;
247
- }
248
- console.log('Importing files from directory...');
249
- const importService = new FileImportService(repo);
250
- try {
251
- // Scan for files to import
252
- const preview = await importService.previewImport({
253
- workingDirectory: currentDir,
254
- verbose: false
255
- });
256
- if (preview.filteredFiles.length === 0) {
257
- console.log('No trackable files found to import');
258
- return;
259
- }
260
- console.log(`Found ${preview.filteredFiles.length} files to scan`);
261
- // Import the files with batching to avoid memory issues
262
- const modWorkspace = createModWorkspace(repo);
263
- const workspaceHandle = await modWorkspace.openWorkspace(workspace.id);
264
- // Query existing files to enable resume capability
265
- console.log('Checking for existing files...');
266
- const existingFiles = await workspaceHandle.file.list();
267
- const existingPaths = new Set(existingFiles
268
- .map(f => f.metadata?.typeData?.path || f.metadata?.typeData?.relativePath)
269
- .filter(Boolean));
270
- // Filter out files that already exist
271
- const filesToImport = preview.filteredFiles.filter(filePath => {
272
- const relativePath = path.relative(currentDir, filePath);
273
- return !existingPaths.has(relativePath);
274
- });
275
- if (filesToImport.length === 0) {
276
- console.log('All files already imported, nothing to do');
277
- return;
278
- }
279
- const skippedCount = preview.filteredFiles.length - filesToImport.length;
280
- if (skippedCount > 0) {
281
- console.log(`Skipping ${skippedCount} files that already exist`);
282
- }
283
- console.log(`Importing ${filesToImport.length} new files`);
284
- const importedFileIds = [];
285
- let importedCount = 0;
286
- const BATCH_SIZE = 20; // Reduced batch size to avoid Automerge concurrency issues
287
- // Split files into batches
288
- const batches = [];
289
- for (let i = 0; i < filesToImport.length; i += BATCH_SIZE) {
290
- batches.push(filesToImport.slice(i, i + BATCH_SIZE));
291
- }
292
- // Process each batch
293
- for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
294
- const batch = batches[batchIndex];
295
- // Process files sequentially to avoid Automerge concurrency issues
296
- // Concurrent modifications cause "recursive use of an object" errors
297
- for (const filePath of batch) {
298
- try {
299
- const content = fs.readFileSync(filePath, 'utf-8');
300
- const relativePath = path.relative(currentDir, filePath);
301
- const pathParts = relativePath.split(path.sep);
302
- const fileName = pathParts.pop();
303
- // Detect proper mime type instead of hardcoding markdown
304
- const mimeType = detectMimeType(fileName);
305
- const canvasType = mimeTypeToCanvasType(mimeType);
306
- const isCodeFile = canvasType === 'code';
307
- const isTextFile = mimeType.startsWith('text/') ||
308
- mimeType === 'application/json' ||
309
- mimeType === 'application/javascript';
310
- // Create file in workspace
311
- // Code files: Store content directly (no prosemirror formatting)
312
- // Text/markdown files: Create empty first, then apply setTextContent for proper formatting
313
- const fileDoc = await workspaceHandle.file.create({
314
- text: isCodeFile ? content : '',
315
- metadata: {
316
- type: 'text',
317
- path: relativePath,
318
- name: fileName,
319
- mimeType,
320
- createdAt: new Date().toISOString(),
321
- updatedAt: new Date().toISOString(),
322
- createdBy: 'cli'
323
- }
324
- }, {
325
- name: fileName,
326
- mimeType
327
- });
328
- // Apply proper richtext formatting using setTextContent for text/markdown files only
329
- // Code files should NOT use setTextContent - they need plain text for CodeMirror
330
- if (isTextFile && !isCodeFile && content.length > 0) {
331
- await setTextContent(fileDoc, ['text'], content);
332
- }
333
- importedFileIds.push(fileDoc.documentId);
334
- importedCount++;
335
- // Longer delay to ensure Automerge completes save cycle
336
- // The "recursive use" error happens when save is triggered while another save is in progress
337
- await new Promise(resolve => setTimeout(resolve, 100));
338
- }
339
- catch (error) {
340
- console.warn(` Failed to import ${filePath}:`, error instanceof Error ? error.message : error);
341
- // On Automerge error, wait longer before continuing
342
- if (error instanceof Error && error.message.includes('recursive')) {
343
- console.warn(' Automerge concurrency detected, waiting 2s...');
344
- await new Promise(resolve => setTimeout(resolve, 2000));
345
- }
346
- }
347
- }
348
- console.log(` Imported ${importedCount}/${filesToImport.length} files...`);
349
- // Trigger garbage collection hint between batches to free memory
350
- if (global.gc && batchIndex < batches.length - 1) {
351
- global.gc();
352
- }
353
- // Small delay between batches to allow memory cleanup
354
- if (batchIndex < batches.length - 1) {
355
- await new Promise(resolve => setTimeout(resolve, 100));
356
- }
357
- }
358
- // Add imported files to share policy in batches
359
- if (importedFileIds.length > 0) {
360
- const SHARE_POLICY_BATCH_SIZE = 100;
361
- for (let i = 0; i < importedFileIds.length; i += SHARE_POLICY_BATCH_SIZE) {
362
- const batch = importedFileIds.slice(i, i + SHARE_POLICY_BATCH_SIZE);
363
- addFilesToSharePolicy(batch);
364
- }
365
- }
366
- console.log(`✓ Imported ${importedCount} files (${skippedCount} already existed)`);
367
- }
368
- catch (error) {
369
- console.warn('Failed to import files:', error instanceof Error ? error.message : error);
370
- throw error; // Re-throw so caller knows import failed
371
- }
372
- }
373
- /**
374
- * Install the /mod agent skill to .claude/skills/mod/
375
- * This enables coding agents to use spec-driven development workflows
376
- */
377
- function installModSkill() {
378
- const currentDir = process.cwd();
379
- const skillDir = path.join(currentDir, '.claude', 'skills', 'mod');
380
- const referencesDir = path.join(skillDir, 'references');
381
- // Create directories if they don't exist
382
- if (!fs.existsSync(skillDir)) {
383
- fs.mkdirSync(skillDir, { recursive: true });
384
- }
385
- if (!fs.existsSync(referencesDir)) {
386
- fs.mkdirSync(referencesDir, { recursive: true });
387
- }
388
- const skillMdPath = path.join(skillDir, 'SKILL.md');
389
- const commandsMdPath = path.join(referencesDir, 'commands.md');
390
- // Only write if files don't exist (don't overwrite user modifications)
391
- if (!fs.existsSync(skillMdPath)) {
392
- fs.writeFileSync(skillMdPath, SKILL_MD_CONTENT);
393
- }
394
- if (!fs.existsSync(commandsMdPath)) {
395
- fs.writeFileSync(commandsMdPath, COMMANDS_MD_CONTENT);
396
- }
397
- }
398
- const SKILL_MD_CONTENT = `---
399
- name: mod
400
- description: Spec-driven development with traceability. Implements specs, adds traces, verifies coverage. Use when implementing from specifications, adding requirement traceability, or checking trace coverage before merging.
401
- ---
402
-
403
- # Mod: Spec-Driven Development
404
-
405
- Use this skill when the user asks to implement from specs, add traceability, or verify coverage.
406
-
407
- ## Triggers
408
-
409
- - "implement this spec"
410
- - "implement specs/*.md"
411
- - "/mod implement <file>"
412
- - "add tests for this spec"
413
- - "check trace coverage"
414
-
415
- ## Workflow
416
-
417
- ### 1. Setup (if needed)
418
-
419
- Check if workspace exists:
420
- \`\`\`bash
421
- mod status
422
- \`\`\`
423
-
424
- If not initialized:
425
- \`\`\`bash
426
- mod init
427
- mod auth login
428
- \`\`\`
429
-
430
- ### 2. Read the Spec
431
-
432
- Parse the spec file for requirements. Requirements may be marked with glassware annotations or be plain markdown sections.
433
-
434
- ### 3. Implement with Traces
435
-
436
- For each requirement:
437
-
438
- 1. Write the implementation
439
- 2. Add a trace connecting implementation to requirement:
440
- \`\`\`bash
441
- mod trace add <file>:<line> --type=implementation --link=<requirement-id>
442
- \`\`\`
443
-
444
- 3. Verify the trace was added:
445
- \`\`\`bash
446
- mod trace report <spec-file>
447
- \`\`\`
448
-
449
- ### 4. Add Tests with Traces
450
-
451
- For each implementation:
452
-
453
- 1. Write tests
454
- 2. Add traces connecting tests to implementations:
455
- \`\`\`bash
456
- mod trace add <test-file>:<line> --type=test --link=<implementation-id>
457
- \`\`\`
458
-
459
- ### 5. Verify Coverage
460
-
461
- Before completing, always run:
462
- \`\`\`bash
463
- mod trace report <spec-file>
464
- mod trace unmet
465
- mod trace diff
466
- \`\`\`
467
-
468
- Address any gaps:
469
- - Unmet requirements -> implement them
470
- - Untraced files -> add traces or mark as utility
471
-
472
- ### 6. Final Validation
473
-
474
- \`\`\`bash
475
- mod trace diff && mod trace unmet
476
- \`\`\`
477
-
478
- Only report completion when both commands exit 0 (all files traced, all requirements implemented).
479
-
480
- ## Commands Reference
481
-
482
- | Command | Purpose |
483
- |---------|---------|
484
- | \`mod init\` | Initialize workspace |
485
- | \`mod auth login\` | Authenticate |
486
- | \`mod trace add <file>:<line> --type=<type> [--link=<id>]\` | Add trace |
487
- | \`mod trace link <source> <target>\` | Link two traces |
488
- | \`mod trace report <file>\` | Show trace coverage for spec |
489
- | \`mod trace coverage\` | Workspace-wide stats |
490
- | \`mod trace unmet\` | Requirements without implementations |
491
- | \`mod trace diff\` | Untraced files on branch (auto-detects base) |
492
-
493
- ## Types
494
-
495
- - \`requirement\` - Spec requirement
496
- - \`specification\` - Technical spec detail
497
- - \`implementation\` - Code that builds
498
- - \`test\` - Code that verifies
499
- - \`utility\` - Intentionally untraced helpers
500
-
501
- ## Important
502
-
503
- - Always verify coverage before completing
504
- - If \`mod trace diff\` or \`mod trace unmet\` fails, address gaps before finishing
505
- - Use \`mod trace unmet\` to find requirements that still need implementation
506
- - Mark helper files as \`utility\` type to avoid false positives in diff
507
- `;
508
- const COMMANDS_MD_CONTENT = `# Mod CLI Commands Reference
509
-
510
- ## Authentication
511
-
512
- \`\`\`bash
513
- mod auth login # OAuth login (opens browser)
514
- mod auth login --dev # Dev mode (no OAuth)
515
- mod auth logout
516
- mod auth status
517
- \`\`\`
518
-
519
- ## Workspace
520
-
521
- \`\`\`bash
522
- mod init # Initialize workspace in current directory
523
- mod status # Show workspace status
524
- \`\`\`
525
-
526
- ## Traces
527
-
528
- \`\`\`bash
529
- # Add traces
530
- mod trace add <file>:<line> --type=<type> ["description"]
531
- mod trace add <file>:<line> --type=<type> --link=<trace-id>
532
-
533
- # Link traces
534
- mod trace link <source-id> <target-id>
535
-
536
- # View traces
537
- mod trace list # All traces
538
- mod trace list --type=requirement # Filter by type
539
- mod trace list --file=<path> # Filter by file
540
- mod trace get <trace-id> # Get trace details
541
-
542
- # Reports
543
- mod trace report <file> # Per-document coverage
544
- mod trace coverage # Workspace-wide stats
545
-
546
- # Find gaps
547
- mod trace diff # Untraced files on branch (auto-detects base)
548
- mod trace diff main..HEAD # Explicit range
549
- mod trace unmet # Requirements without implementations
550
- \`\`\`
551
-
552
- ## Trace Types
553
-
554
- | Type | Use For |
555
- |------|---------|
556
- | \`requirement\` | Specs, user stories, acceptance criteria |
557
- | \`specification\` | Detailed technical specs |
558
- | \`implementation\` | Code that builds something |
559
- | \`test\` | Code that verifies something |
560
- | \`design\` | Design docs, architecture notes |
561
- | \`decision\` | ADRs, decision records |
562
- | \`utility\` | Helpers that don't need tracing |
563
-
564
- ## Comments
565
-
566
- \`\`\`bash
567
- mod comment add <file>:<line> "text"
568
- mod comment list [file]
569
- \`\`\`
570
-
571
- ## Exit Codes for CI
572
-
573
- Commands exit non-zero when issues exist:
574
-
575
- | Command | Exit 0 | Exit 1 |
576
- |---------|--------|--------|
577
- | \`mod trace diff\` | All changed files traced | Untraced files exist |
578
- | \`mod trace unmet\` | All requirements implemented | Unmet requirements |
579
- | \`mod trace report\` | Always (informational) | - |
580
-
581
- Pre-merge validation:
582
- \`\`\`bash
583
- mod trace diff && mod trace unmet && git push
584
- \`\`\`
585
- `;
586
- function displayInitializationSuccess(connection, isAuthenticated) {
587
- console.log('');
588
- console.log('Initialized Mod in ' + connection.path);
589
- console.log(`Workspace: ${connection.workspaceName}`);
590
- console.log('');
591
- if (isAuthenticated) {
592
- console.log('Run `mod sync start` to begin syncing');
593
- }
594
- else {
595
- console.log('Run `mod auth login` to enable sync with collaborators');
596
- }
597
- }