@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.
- package/README.md +125 -0
- package/commands/execute.md +156 -0
- package/commands/overview.md +233 -0
- package/commands/review.md +151 -0
- package/commands/spec.md +169 -0
- package/dist/app.js +227 -0
- package/dist/cli.bundle.js +25824 -0
- package/dist/cli.bundle.js.map +7 -0
- package/dist/cli.js +121 -0
- package/dist/commands/agents-run.js +71 -0
- package/dist/commands/auth.js +151 -0
- package/dist/commands/branch.js +1411 -0
- package/dist/commands/claude-sync.js +772 -0
- package/dist/commands/index.js +43 -0
- package/dist/commands/init.js +378 -0
- package/dist/commands/recover.js +207 -0
- package/dist/commands/spec.js +386 -0
- package/dist/commands/status.js +329 -0
- package/dist/commands/sync.js +95 -0
- package/dist/commands/workspace.js +423 -0
- package/dist/components/conflict-resolution-ui.js +120 -0
- package/dist/components/messages.js +5 -0
- package/dist/components/thread.js +8 -0
- package/dist/config/features.js +72 -0
- package/dist/config/release-profiles/development.json +11 -0
- package/dist/config/release-profiles/mvp.json +12 -0
- package/dist/config/release-profiles/v0.1.json +11 -0
- package/dist/config/release-profiles/v0.2.json +11 -0
- package/dist/containers/branches-container.js +140 -0
- package/dist/containers/directory-container.js +92 -0
- package/dist/containers/thread-container.js +214 -0
- package/dist/containers/threads-container.js +27 -0
- package/dist/containers/workspaces-container.js +27 -0
- package/dist/daemon-worker.js +257 -0
- package/dist/lib/auth-server.js +153 -0
- package/dist/lib/browser.js +35 -0
- package/dist/lib/storage.js +203 -0
- package/dist/services/automatic-file-tracker.js +303 -0
- package/dist/services/cli-orchestrator.js +227 -0
- package/dist/services/feature-flags.js +187 -0
- package/dist/services/file-import-service.js +283 -0
- package/dist/services/file-transformation-service.js +218 -0
- package/dist/services/logger.js +44 -0
- package/dist/services/mod-config.js +61 -0
- package/dist/services/modignore-service.js +326 -0
- package/dist/services/sync-daemon.js +244 -0
- package/dist/services/thread-notification-service.js +50 -0
- package/dist/services/thread-service.js +147 -0
- package/dist/stores/use-directory-store.js +96 -0
- package/dist/stores/use-threads-store.js +46 -0
- package/dist/stores/use-workspaces-store.js +32 -0
- package/dist/types/config.js +16 -0
- package/dist/types/index.js +2 -0
- package/dist/types/workspace-connection.js +2 -0
- package/dist/types.js +1 -0
- package/package.json +67 -0
package/commands/spec.md
ADDED
|
@@ -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
|
+
}
|