@mod-computer/cli 0.1.0

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 (56) hide show
  1. package/README.md +125 -0
  2. package/commands/execute.md +156 -0
  3. package/commands/overview.md +233 -0
  4. package/commands/review.md +151 -0
  5. package/commands/spec.md +169 -0
  6. package/dist/app.js +227 -0
  7. package/dist/cli.bundle.js +25824 -0
  8. package/dist/cli.bundle.js.map +7 -0
  9. package/dist/cli.js +121 -0
  10. package/dist/commands/agents-run.js +71 -0
  11. package/dist/commands/auth.js +151 -0
  12. package/dist/commands/branch.js +1411 -0
  13. package/dist/commands/claude-sync.js +772 -0
  14. package/dist/commands/index.js +43 -0
  15. package/dist/commands/init.js +378 -0
  16. package/dist/commands/recover.js +207 -0
  17. package/dist/commands/spec.js +386 -0
  18. package/dist/commands/status.js +329 -0
  19. package/dist/commands/sync.js +95 -0
  20. package/dist/commands/workspace.js +423 -0
  21. package/dist/components/conflict-resolution-ui.js +120 -0
  22. package/dist/components/messages.js +5 -0
  23. package/dist/components/thread.js +8 -0
  24. package/dist/config/features.js +72 -0
  25. package/dist/config/release-profiles/development.json +11 -0
  26. package/dist/config/release-profiles/mvp.json +12 -0
  27. package/dist/config/release-profiles/v0.1.json +11 -0
  28. package/dist/config/release-profiles/v0.2.json +11 -0
  29. package/dist/containers/branches-container.js +140 -0
  30. package/dist/containers/directory-container.js +92 -0
  31. package/dist/containers/thread-container.js +214 -0
  32. package/dist/containers/threads-container.js +27 -0
  33. package/dist/containers/workspaces-container.js +27 -0
  34. package/dist/daemon-worker.js +257 -0
  35. package/dist/lib/auth-server.js +153 -0
  36. package/dist/lib/browser.js +35 -0
  37. package/dist/lib/storage.js +203 -0
  38. package/dist/services/automatic-file-tracker.js +303 -0
  39. package/dist/services/cli-orchestrator.js +227 -0
  40. package/dist/services/feature-flags.js +187 -0
  41. package/dist/services/file-import-service.js +283 -0
  42. package/dist/services/file-transformation-service.js +218 -0
  43. package/dist/services/logger.js +44 -0
  44. package/dist/services/mod-config.js +61 -0
  45. package/dist/services/modignore-service.js +326 -0
  46. package/dist/services/sync-daemon.js +244 -0
  47. package/dist/services/thread-notification-service.js +50 -0
  48. package/dist/services/thread-service.js +147 -0
  49. package/dist/stores/use-directory-store.js +96 -0
  50. package/dist/stores/use-threads-store.js +46 -0
  51. package/dist/stores/use-workspaces-store.js +32 -0
  52. package/dist/types/config.js +16 -0
  53. package/dist/types/index.js +2 -0
  54. package/dist/types/workspace-connection.js +2 -0
  55. package/dist/types.js +1 -0
  56. package/package.json +67 -0
@@ -0,0 +1,169 @@
1
+ ---
2
+ version: 1.0.0
3
+ updated: 2025-12-13
4
+ checksum: d4e5f6g7h8i9j0k1
5
+ description: Create comprehensive spec from feature description
6
+ ---
7
+
8
+ # ModSpec Creation Guide
9
+
10
+ Given this high-level feature description: "$ARGUMENTS"
11
+
12
+ ## Branch Creation and Setup
13
+
14
+ **BEFORE writing the specification:**
15
+
16
+ **Automatic Specification Branch Creation:**
17
+ 1. **Extract spec title** from the feature description arguments
18
+ 2. **Check for existing branch** that matches the specification:
19
+ ```bash
20
+ # Check if specification branch already exists
21
+ mod branch list | grep "feature-[package-]spec-name"
22
+ ```
23
+ 3. **Create or switch to specification branch**:
24
+ ```bash
25
+ # If branch exists, switch to it
26
+ mod branch switch feature-mod-cli-branching-cli
27
+
28
+ # If branch doesn't exist, create it with package prefix
29
+ mod branch create feature-mod-cli-branching-cli --spec mod-cli/branching-cli.spec.md
30
+ ```
31
+ 4. **Verify branch setup**:
32
+ ```bash
33
+ mod branch status
34
+ # Should show: Active branch: feature-mod-cli-branching-cli [spec] (specification)
35
+ # Should show: ✓ Linked to specification: .mod/specs/mod-cli/branching-cli.spec.md
36
+ ```
37
+
38
+ **Package-Prefixed Branch Naming:**
39
+ - **Pattern**: `feature-{package}-{spec-name}`
40
+ - **Examples**:
41
+ - `feature-mod-cli-branching-cli`
42
+ - `feature-mod-core-workspace-management`
43
+ - `feature-mod-ui-component-library`
44
+ - **Benefits**: Clear package ownership, avoids name collisions
45
+
46
+ **Background Sync Integration:**
47
+ - The mod sync daemon automatically tracks specification file changes
48
+ - Branch switching preserves working directory state
49
+ - Real-time change monitoring enables seamless development flow
50
+
51
+ ## Pre-Specification Research
52
+
53
+ **BEFORE writing the specification:**
54
+ 1. Research existing codebase for related functionality and patterns
55
+ 2. Check `.mod/specs/` for similar specifications
56
+ 3. Identify reusable code, services, and integration points
57
+ 4. Understand the target package's architecture and conventions
58
+
59
+ ## ModSpec Structure
60
+
61
+ Include:
62
+
63
+ **Required Header:**
64
+ ```markdown
65
+ ---
66
+ title: [feature-name]
67
+ type: specification
68
+ ---
69
+ # [Feature Name] Specification
70
+ ```
71
+
72
+ **Core Sections:**
73
+ 1. **Abstract** - One concise sentence describing key capabilities
74
+ 2. **Motivation** - Problems and impact in bullet points
75
+ 3. **Approach** - Technical decisions and integration points
76
+ 4. **UX Requirements (UX-*)** - UI components, user flows, and accessibility
77
+ 5. **Business Logic Requirements (BUS-*)** - Business logic, workflows, and validation rules
78
+ 6. **Data Model Requirements (DATA-*)** - TypeScript interfaces, data structures, and service extensions (be conservative - prefer extending existing over creating new)
79
+ 7. **Integration Requirements (INT-*)** - APIs, external services, and data exchange
80
+ 8. **Infrastructure Requirements (INFRA-*)** - System architecture, deployment, and scaling
81
+ 9. **Quality Requirements (QUAL-*)** - Testing, performance, and security criteria
82
+
83
+ ### Quality Requirements Playbook
84
+ Default to four numbered blocks so every spec documents the same test tiers; add REQ-QUAL-5+ if the feature needs specialty coverage (security, privacy, chaos, compliance, etc.). Keep every entry succinct and include the test type + success metric in the requirement text. Under each block, add sub-numbered requirements (REQ-QUAL-X.Y.Z) that describe the specific test case so it can be traced back to automation or manual checklists.
85
+
86
+ 1. **REQ-QUAL-1: Unit Quality** — Core modules and invariants guarded by unit tests (coverage targets, fixtures, failure injection).
87
+ 2. **REQ-QUAL-2: Integration Quality** — Extension/service interactions, persistence, external APIs validated through integration harnesses.
88
+ 3. **REQ-QUAL-3: End-to-End / Scenario Quality** — Real workflows (happy + negative), manual UX steps, plus the evidence reviewers must archive.
89
+ 4. **REQ-QUAL-4: Performance & Telemetry** — Bench methodology, latency/throughput budgets, soak expectations, telemetry metrics + alert thresholds.
90
+ 5. **REQ-QUAL-5+: Optional Specialty** — Security, resilience, localization, etc., only when needed.
91
+
92
+ Reference `mod-ide-plugin/12-spec-branch-creation-and-change-tracking.spec.md` for tone and structure when in doubt.
93
+
94
+ **Critical Requirements for Agent Execution:**
95
+ - Use hierarchical numbered requirements with concise, clear descriptions:
96
+ - REQ-{CATEGORY}: Top-level category (REQ-UX, REQ-BUS, REQ-DATA, REQ-INT, REQ-INFRA, REQ-QUAL)
97
+ - REQ-{CATEGORY}-N: User story-level groupings (REQ-UX-1: Dashboard access with recent activity)
98
+ - REQ-{CATEGORY}-N.N: Feature sections within user story (REQ-UX-1.1: Default State)
99
+ - REQ-{CATEGORY}-N.N.N: Granular implementation details (REQ-UX-1.1.1: Available dashboard view)
100
+ - Write concisely: remove redundant words, use bullet points, avoid verbose descriptions
101
+ - Include exact implementation details (bcrypt cost=12, not "hash securely")
102
+ - Specify concrete behavior that agents can implement directly
103
+ - Be direct and scannable - eliminate wordy, repetitive language
104
+
105
+ ## Specification Output Location and Chronological Ordering
106
+
107
+ **IMPORTANT**: Create the specification file at:
108
+ `.mod/specs/<appropriate-folder>/<feature-name>.spec.md`
109
+
110
+ **Chronological Ordering System:**
111
+ 1. **Use Creation Date**: Specs are ordered by creation date for stable, conflict-free sequencing
112
+ 2. **Add Created Frontmatter**: Include `created: YYYY-MM-DD` in the YAML frontmatter for explicit ordering
113
+ 3. **Fallback to File Stats**: If no `created` field exists, use file system creation time as backup
114
+ 4. **Display Numbers**: Show position numbers (1., 2., 3.) in navigation UI based on chronological order
115
+
116
+ **Directory Selection:**
117
+ Choose the appropriate folder based on the feature's primary domain. If unsure, use the package name that will contain the primary implementation.
118
+
119
+ **Frontmatter Template:**
120
+ ```markdown
121
+ ---
122
+ title: feature-name
123
+ type: specification
124
+ created: 2024-01-15
125
+ ---
126
+ ```
127
+
128
+ **Benefits of Date-Based Ordering:**
129
+ - **No file renaming**: New specs don't affect existing filenames
130
+ - **Merge conflict free**: Multiple developers can create specs simultaneously
131
+ - **Stable references**: Spec filenames never change after creation
132
+ - **Natural chronology**: Order reflects development timeline
133
+
134
+ ## Quality Checklist
135
+
136
+ Before finalizing your specification, ensure:
137
+
138
+ ✅ **Concise**: Abstract is one sentence, motivation uses bullet points, requirements are direct
139
+ ✅ **Traceability**: Every requirement maps to specific implementation files
140
+ ✅ **Testability**: Every requirement can be validated through testing
141
+ ✅ **Measurability**: Performance targets include specific metrics
142
+ ✅ **Completeness**: All user workflows are covered by requirements
143
+ ✅ **Clarity**: Each requirement has unambiguous acceptance criteria
144
+ ✅ **Implementation Ready**: Developers know exactly what to build and where
145
+ ✅ **File Location**: Spec is saved to `.mod/specs/<appropriate-folder>/<feature-name>.spec.md`
146
+
147
+ ## Examples
148
+
149
+ ### Hierarchical Requirements Structure:
150
+ ```markdown
151
+ ## REQ-UX: User Experience Requirements
152
+
153
+ ### REQ-UX-1: Real-time email validation feedback for error correction
154
+
155
+ **REQ-UX-1.1: Form Components**
156
+ - REQ-UX-1.1.1: Email input with real-time validation feedback
157
+ - REQ-UX-1.1.2: Password input with strength indicator
158
+ - REQ-UX-1.1.3: Submit button with loading/disabled states
159
+ - REQ-UX-1.1.4: Error display areas with ARIA attributes
160
+
161
+ **REQ-UX-1.2: Validation Logic**
162
+ - REQ-UX-1.2.1: Email format validation on blur event
163
+ - REQ-UX-1.2.2: Password strength calculation with visual feedback
164
+ - REQ-UX-1.2.3: Form submission validation with error handling
165
+ - REQ-UX-1.2.4: Success state with redirect to verification
166
+
167
+ ```
168
+
169
+ Now generate your specification following this template, ensuring every requirement is traceable from business value to implementation code and can be validated through testing.
package/dist/app.js ADDED
@@ -0,0 +1,227 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
+ import { Box, Text } from 'ink';
4
+ import TextInput from 'ink-text-input';
5
+ import WorkspacesContainer from './containers/workspaces-container.js';
6
+ import ThreadContainer from './containers/thread-container.js';
7
+ import DirectoryContainer from './containers/directory-container.js';
8
+ import BranchesContainer from './containers/branches-container.js';
9
+ import { useInput } from 'ink';
10
+ import { readModConfig, writeModConfig } from './services/mod-config.js';
11
+ // Removed legacy sync commands and services
12
+ import { BranchService } from '@mod/mod-core/services/branch-service';
13
+ import { getFeatureFlags, shouldEnableBackgroundWatch } from './services/feature-flags.js';
14
+ export default function App({ repo, featureFlags: providedFeatureFlags, }) {
15
+ const featureFlags = useMemo(() => providedFeatureFlags ?? getFeatureFlags(), [providedFeatureFlags]);
16
+ const tasksEnabled = featureFlags['tasks-panel'];
17
+ const directoryEnabled = featureFlags['directory-view'];
18
+ // Routing: 'workspaces' | 'thread' | 'directory' | 'branches' | 'tasks'
19
+ const [route, setRoute] = useState('workspaces');
20
+ const [selectedWorkspace, setSelectedWorkspace] = useState(null);
21
+ const [selectedThread, setSelectedThread] = useState(null);
22
+ const [inputValue, setInputValue] = useState('');
23
+ const [pendingChatInput, setPendingChatInput] = useState('');
24
+ // const watcherRef = useRef<FileWatcherService | null>(null); // Removed
25
+ const initialUploadDoneRef = useRef(false); // reserved (disabled)
26
+ // Keyboard shortcuts for navigation (only Esc to reset)
27
+ useInput((input, key) => {
28
+ if (key.escape) {
29
+ setRoute('workspaces');
30
+ setSelectedWorkspace(null);
31
+ setSelectedThread(null);
32
+ setInputValue('');
33
+ }
34
+ });
35
+ // Command prompt handler
36
+ const handleInputSubmit = (value) => {
37
+ const trimmed = value.trim();
38
+ if (trimmed === '/workspaces') {
39
+ setRoute('workspaces');
40
+ setSelectedWorkspace(null);
41
+ setSelectedThread(null);
42
+ }
43
+ else if ((trimmed === '/threads' || trimmed === '/branches') && selectedWorkspace) {
44
+ setRoute('branches');
45
+ setSelectedThread(null);
46
+ }
47
+ else if (trimmed === '/files' && directoryEnabled) {
48
+ setRoute('directory');
49
+ }
50
+ else if (trimmed === '/tasks' && tasksEnabled && selectedWorkspace) {
51
+ setRoute('tasks');
52
+ }
53
+ else if (trimmed === '/thread' && selectedWorkspace && selectedThread) {
54
+ setRoute('thread');
55
+ }
56
+ else if (trimmed === '/exit') {
57
+ process.exit(0);
58
+ }
59
+ else if (trimmed === '/upload') {
60
+ // Upload all files in current working directory to active workspace/thread
61
+ // uploadWorkspaceCommand([], repo).catch(err => console.error('[upload] Failed:', err)); // Removed
62
+ }
63
+ else if (trimmed === '/download') {
64
+ // Download all workspace files to current working directory
65
+ // downloadWorkspaceCommand([], repo).catch(err => console.error('[download] Failed:', err)); // Removed
66
+ }
67
+ else if (route === 'thread' && selectedThread) {
68
+ // Send message to thread
69
+ setPendingChatInput(value);
70
+ }
71
+ setInputValue('');
72
+ };
73
+ // Bootstrap: if .mod/config.json has workspace/branch, jump to thread view (branch-first)
74
+ useEffect(() => {
75
+ let cancelled = false;
76
+ (async () => {
77
+ try {
78
+ const cfg = readModConfig();
79
+ if (!cfg?.workspaceId)
80
+ return;
81
+ const wsHandle = await repo.find(cfg.workspaceId);
82
+ const wsDoc = await wsHandle.doc();
83
+ // Branch-first: read branch.threadId
84
+ const activeBranchId = (cfg.activeBranchId || wsDoc?.activeBranchId);
85
+ let thread = null;
86
+ const branchesDocId = wsDoc?.branchesDocId;
87
+ if (activeBranchId && branchesDocId) {
88
+ try {
89
+ const branchService = new BranchService(repo);
90
+ const branch = await branchService.getBranch(activeBranchId, branchesDocId);
91
+ const tid = branch?.threadId;
92
+ if (tid) {
93
+ try {
94
+ const tHandle = await repo.find(tid);
95
+ const tDoc = await tHandle.doc();
96
+ thread = { id: tDoc?.id || tid, name: tDoc?.name || 'Thread' };
97
+ }
98
+ catch {
99
+ thread = { id: tid, name: 'Thread' };
100
+ }
101
+ }
102
+ }
103
+ catch { }
104
+ }
105
+ // No implicit thread creation on startup; prefer fast load and let user pick
106
+ if (!cancelled) {
107
+ setSelectedWorkspace({ id: wsDoc.id, name: wsDoc.name });
108
+ if (thread) {
109
+ setSelectedThread(thread);
110
+ setRoute('thread');
111
+ }
112
+ else {
113
+ setRoute('branches');
114
+ }
115
+ }
116
+ }
117
+ catch {
118
+ // ignore
119
+ }
120
+ })();
121
+ return () => { cancelled = true; };
122
+ }, [repo]);
123
+ // Background sync: start file watcher when workspace/thread are active (initial bulk upload disabled)
124
+ useEffect(() => {
125
+ let cancelled = false;
126
+ (async () => {
127
+ try {
128
+ const workspaceId = selectedWorkspace?.id || readModConfig()?.workspaceId;
129
+ const threadId = selectedThread?.id;
130
+ if (!repo || !workspaceId || !threadId)
131
+ return;
132
+ const autoWatch = shouldEnableBackgroundWatch();
133
+ if (!autoWatch)
134
+ return;
135
+ // Initial bulk upload is intentionally disabled to avoid heavy concurrent writes
136
+ // File watching removed - using AutomaticFileTracker instead
137
+ /*
138
+ if (!watcherRef.current) {
139
+ try {
140
+ const watcher = new FileWatcherService(repo);
141
+ await watcher.startWatching({
142
+ workspaceId: workspaceId as any,
143
+ threadId: threadId as any,
144
+ watchDirectory: process.cwd(),
145
+ debounceMs: 300,
146
+ verbose: false,
147
+ });
148
+ if (!cancelled) watcherRef.current = watcher;
149
+ } catch (err) {
150
+ // Non-blocking
151
+ }
152
+ }
153
+ */
154
+ }
155
+ catch {
156
+ // ignore
157
+ }
158
+ })();
159
+ return () => {
160
+ cancelled = true;
161
+ };
162
+ }, [repo, selectedWorkspace, selectedThread]);
163
+ let content = null;
164
+ if (route === 'workspaces') {
165
+ content = (_jsx(WorkspacesContainer, { repo: repo, onSelect: ws => {
166
+ setSelectedWorkspace(ws);
167
+ try {
168
+ writeModConfig({ workspaceId: ws.id });
169
+ }
170
+ catch { }
171
+ setRoute('branches');
172
+ } }));
173
+ }
174
+ else if (route === 'thread' && selectedWorkspace && selectedThread) {
175
+ content = (_jsx(ThreadContainer, { repo: repo, activeThread: selectedThread, workspace: selectedWorkspace, pendingChatInput: pendingChatInput, onChatInputHandled: () => setPendingChatInput(''), handleInputSubmit: handleInputSubmit }));
176
+ }
177
+ else if (route === 'branches' && selectedWorkspace) {
178
+ content = (_jsx(BranchesContainer, { repo: repo, workspace: selectedWorkspace, onSelect: async (thread) => {
179
+ setSelectedThread(thread);
180
+ setRoute('thread');
181
+ // Persist activeBranchId is handled inside BranchesContainer on select
182
+ // Legacy sync and watcher removed - using AutomaticFileTracker instead
183
+ /*
184
+ // Stop watcher while syncing
185
+ try { watcherRef.current?.stopWatching(); } catch {}
186
+ // Perform web -> local sync for the selected branch/thread
187
+ try {
188
+ const wsId = (selectedWorkspace as any).id;
189
+ const syncSvc = new WorkspaceSyncService(repo as any);
190
+ await syncSvc.syncWorkspaceFiles(wsId as any, (thread as any).id as any, { force: true, verbose: true });
191
+ } catch (err) {
192
+ console.error('[branches] Sync after selection failed:', err);
193
+ }
194
+ // Restart watcher on the selected thread
195
+ try {
196
+ const watcher = watcherRef.current || new FileWatcherService(repo);
197
+ await watcher.startWatching({
198
+ workspaceId: (selectedWorkspace as any).id as any,
199
+ threadId: (thread as any).id as any,
200
+ watchDirectory: process.cwd(),
201
+ debounceMs: 300,
202
+ verbose: false,
203
+ });
204
+ watcherRef.current = watcher;
205
+ } catch {}
206
+ */
207
+ } }));
208
+ }
209
+ else if (route === 'tasks' && tasksEnabled && selectedWorkspace) {
210
+ content = ({ /* TasksContainer removed - legacy task management */});
211
+ }
212
+ else if (route === 'directory' && directoryEnabled) {
213
+ content = (_jsx(DirectoryContainer, { repo: repo, selectedWorkspace: selectedWorkspace, selectedThread: selectedThread }));
214
+ }
215
+ else {
216
+ content = _jsx(Text, { children: "Unknown route." });
217
+ }
218
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { flexDirection: "column", flexGrow: 1, children: content }), _jsxs(Box, { borderStyle: "single", paddingX: 1, borderColor: "gray", children: [_jsxs(Text, { color: "gray", children: ['>', " "] }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, focus: true, showCursor: true, placeholder: `Type ${[
219
+ '/workspaces',
220
+ '/branches',
221
+ directoryEnabled ? '/files' : null,
222
+ tasksEnabled ? '/tasks' : null,
223
+ '/upload',
224
+ '/download',
225
+ '/exit',
226
+ ].filter(Boolean).join(', ')}` })] })] }));
227
+ }