recoder-code 2.5.2 โ 2.5.3
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/dist/index.js +0 -0
- package/dist/src/commands/context/index.js +2 -2
- package/dist/src/commands/mcp/marketplace.d.ts +6 -0
- package/dist/src/commands/mcp/marketplace.js +448 -0
- package/dist/src/commands/mcp.js +2 -0
- package/dist/src/commands/parallel.d.ts +20 -0
- package/dist/src/commands/parallel.js +133 -0
- package/dist/src/commands/recoderWeb.js +184 -5
- package/dist/src/commands/web/diff.d.ts +13 -0
- package/dist/src/commands/web/diff.js +235 -0
- package/dist/src/commands/web/link.d.ts +11 -0
- package/dist/src/commands/web/link.js +96 -0
- package/dist/src/commands/web/pull.d.ts +13 -0
- package/dist/src/commands/web/pull.js +203 -0
- package/dist/src/commands/web/status.d.ts +10 -0
- package/dist/src/commands/web/status.js +104 -0
- package/dist/src/commands/web/unlink.d.ts +10 -0
- package/dist/src/commands/web/unlink.js +45 -0
- package/dist/src/commands/web/watch.d.ts +14 -0
- package/dist/src/commands/web/watch.js +360 -0
- package/dist/src/commands/web.js +12 -0
- package/dist/src/config/config.js +6 -2
- package/dist/src/config/defaultMcpServers.d.ts +1 -0
- package/dist/src/config/defaultMcpServers.js +46 -0
- package/dist/src/gemini.js +10 -0
- package/dist/src/parallel/git-utils.d.ts +42 -0
- package/dist/src/parallel/git-utils.js +161 -0
- package/dist/src/parallel/index.d.ts +14 -0
- package/dist/src/parallel/index.js +14 -0
- package/dist/src/parallel/parallel-mode.d.ts +48 -0
- package/dist/src/parallel/parallel-mode.js +224 -0
- package/dist/src/services/AgentBridgeService.d.ts +61 -0
- package/dist/src/services/AgentBridgeService.js +253 -0
- package/dist/src/services/BuiltinCommandLoader.js +7 -0
- package/dist/src/services/PlatformSyncService.d.ts +154 -0
- package/dist/src/services/PlatformSyncService.js +588 -0
- package/dist/src/ui/commands/workflowCommands.d.ts +16 -0
- package/dist/src/ui/commands/workflowCommands.js +291 -0
- package/dist/src/ui/commands/workspaceCommand.d.ts +11 -0
- package/dist/src/ui/commands/workspaceCommand.js +329 -0
- package/dist/src/zed-integration/schema.d.ts +30 -30
- package/package.json +29 -10
- package/src/postinstall.cjs +3 -2
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git utilities for parallel mode
|
|
3
|
+
* Based on Kilo Code (Apache-2.0 License)
|
|
4
|
+
*/
|
|
5
|
+
import simpleGit from 'simple-git';
|
|
6
|
+
/**
|
|
7
|
+
* Get Git repository information for a given directory
|
|
8
|
+
*/
|
|
9
|
+
export async function getGitInfo(cwd) {
|
|
10
|
+
const defaultResult = {
|
|
11
|
+
branch: null,
|
|
12
|
+
isClean: true,
|
|
13
|
+
isRepo: false,
|
|
14
|
+
};
|
|
15
|
+
if (!cwd) {
|
|
16
|
+
return defaultResult;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const git = simpleGit(cwd);
|
|
20
|
+
// Check if it's a git repository
|
|
21
|
+
const isRepo = await git.checkIsRepo();
|
|
22
|
+
if (!isRepo) {
|
|
23
|
+
return defaultResult;
|
|
24
|
+
}
|
|
25
|
+
// Get current branch
|
|
26
|
+
const branch = await git.revparse(['--abbrev-ref', 'HEAD']);
|
|
27
|
+
// Check if working directory is clean
|
|
28
|
+
const status = await git.status();
|
|
29
|
+
const isClean = status.files.length === 0;
|
|
30
|
+
return {
|
|
31
|
+
branch: branch.trim() || null,
|
|
32
|
+
isClean,
|
|
33
|
+
isRepo: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.debug('Failed to get git info:', error);
|
|
38
|
+
return defaultResult;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get just the branch name (faster than full git info)
|
|
43
|
+
*/
|
|
44
|
+
export async function getGitBranch(cwd) {
|
|
45
|
+
if (!cwd) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const git = simpleGit(cwd);
|
|
50
|
+
const isRepo = await git.checkIsRepo();
|
|
51
|
+
if (!isRepo) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const branch = await git.revparse(['--abbrev-ref', 'HEAD']);
|
|
55
|
+
return branch.trim() || null;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.debug('Failed to get git branch:', error);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if a branch exists in the repository
|
|
64
|
+
*/
|
|
65
|
+
export async function branchExists(cwd, branchName) {
|
|
66
|
+
if (!cwd || !branchName) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const git = simpleGit(cwd);
|
|
71
|
+
const isRepo = await git.checkIsRepo();
|
|
72
|
+
if (!isRepo) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
// Get all branches (local and remote)
|
|
76
|
+
const branches = await git.branch();
|
|
77
|
+
// Check if branch exists in local branches
|
|
78
|
+
return branches.all.includes(branchName) || branches.all.includes(`remotes/origin/${branchName}`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.debug('Failed to check if branch exists:', error);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Generate a valid git branch name from a prompt
|
|
87
|
+
* Sanitizes the prompt to create a safe branch name
|
|
88
|
+
*/
|
|
89
|
+
export function generateBranchName(prompt) {
|
|
90
|
+
// Take first 50 chars, convert to lowercase, replace spaces and special chars with hyphens
|
|
91
|
+
const sanitized = prompt
|
|
92
|
+
.slice(0, 50)
|
|
93
|
+
.toLowerCase()
|
|
94
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
95
|
+
.replace(/^-+|-+$/g, '') // Remove leading/trailing hyphens
|
|
96
|
+
.replace(/-+/g, '-'); // Collapse multiple hyphens
|
|
97
|
+
// Add timestamp to ensure uniqueness
|
|
98
|
+
const timestamp = Date.now();
|
|
99
|
+
return `recoder/${sanitized || 'task'}-${timestamp}`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if a directory is a git worktree
|
|
103
|
+
*/
|
|
104
|
+
export async function isGitWorktree(cwd) {
|
|
105
|
+
if (!cwd) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const git = simpleGit(cwd);
|
|
110
|
+
const isRepo = await git.checkIsRepo();
|
|
111
|
+
if (!isRepo) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
// In a worktree, --git-dir points to .git/worktrees/<name>
|
|
115
|
+
// In a normal repo, --git-dir points to .git
|
|
116
|
+
const gitDir = await git.revparse(['--git-dir']);
|
|
117
|
+
return gitDir.trim().includes('worktrees');
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.debug('Failed to check if git worktree:', error);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create a git worktree
|
|
126
|
+
*/
|
|
127
|
+
export async function createWorktree(cwd, worktreePath, branchName, isNewBranch) {
|
|
128
|
+
const git = simpleGit(cwd);
|
|
129
|
+
const args = isNewBranch
|
|
130
|
+
? ['worktree', 'add', '-b', branchName, worktreePath]
|
|
131
|
+
: ['worktree', 'add', worktreePath, branchName];
|
|
132
|
+
await git.raw(args);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Remove a git worktree
|
|
136
|
+
*/
|
|
137
|
+
export async function removeWorktree(cwd, worktreePath) {
|
|
138
|
+
const git = simpleGit(cwd);
|
|
139
|
+
await git.raw(['worktree', 'remove', worktreePath]);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* List all worktrees for a repository
|
|
143
|
+
*/
|
|
144
|
+
export async function listWorktrees(cwd) {
|
|
145
|
+
try {
|
|
146
|
+
const git = simpleGit(cwd);
|
|
147
|
+
const result = await git.raw(['worktree', 'list', '--porcelain']);
|
|
148
|
+
const worktrees = [];
|
|
149
|
+
const lines = result.split('\n');
|
|
150
|
+
for (const line of lines) {
|
|
151
|
+
if (line.startsWith('worktree ')) {
|
|
152
|
+
worktrees.push(line.replace('worktree ', '').trim());
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return worktrees;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.debug('Failed to list worktrees:', error);
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel Mode - Git Worktree-based Parallel Execution
|
|
3
|
+
*
|
|
4
|
+
* Allows multiple Recoder instances to work on the same repository
|
|
5
|
+
* without conflicts by using separate git worktrees.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* recoder --parallel "implement feature X"
|
|
9
|
+
* recoder --parallel --existing-branch recoder/feature-x-123 "continue work"
|
|
10
|
+
*
|
|
11
|
+
* Based on Kilo Code (Apache-2.0 License)
|
|
12
|
+
*/
|
|
13
|
+
export { startParallelMode, finishParallelMode, getParallelModeParams, listParallelWorktrees, cleanupStaleWorktrees, type ParallelModeInput, type ParallelModeParams, type ParallelModeResult, } from './parallel-mode.js';
|
|
14
|
+
export { getGitInfo, getGitBranch, branchExists, generateBranchName, isGitWorktree, createWorktree, removeWorktree, listWorktrees, type GitInfo, } from './git-utils.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel Mode - Git Worktree-based Parallel Execution
|
|
3
|
+
*
|
|
4
|
+
* Allows multiple Recoder instances to work on the same repository
|
|
5
|
+
* without conflicts by using separate git worktrees.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* recoder --parallel "implement feature X"
|
|
9
|
+
* recoder --parallel --existing-branch recoder/feature-x-123 "continue work"
|
|
10
|
+
*
|
|
11
|
+
* Based on Kilo Code (Apache-2.0 License)
|
|
12
|
+
*/
|
|
13
|
+
export { startParallelMode, finishParallelMode, getParallelModeParams, listParallelWorktrees, cleanupStaleWorktrees, } from './parallel-mode.js';
|
|
14
|
+
export { getGitInfo, getGitBranch, branchExists, generateBranchName, isGitWorktree, createWorktree, removeWorktree, listWorktrees, } from './git-utils.js';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel Mode - Execute multiple tasks simultaneously using git worktrees
|
|
3
|
+
* Based on Kilo Code (Apache-2.0 License)
|
|
4
|
+
*
|
|
5
|
+
* Parallel mode allows multiple Recoder instances to work on the same repository
|
|
6
|
+
* without conflicts. Each instance operates in a separate git worktree.
|
|
7
|
+
*/
|
|
8
|
+
export declare const COMMIT_COMPLETION_TIMEOUT = 40000;
|
|
9
|
+
export interface ParallelModeInput {
|
|
10
|
+
cwd: string;
|
|
11
|
+
prompt: string;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
existingBranch?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ParallelModeParams {
|
|
16
|
+
worktreeBranch: string;
|
|
17
|
+
worktreePath: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ParallelModeResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
branch: string;
|
|
22
|
+
worktreePath: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Determine the branch and worktree path for parallel mode
|
|
27
|
+
*/
|
|
28
|
+
export declare function determineParallelBranch({ cwd, prompt, existingBranch, }: ParallelModeInput): Promise<ParallelModeParams>;
|
|
29
|
+
/**
|
|
30
|
+
* Get parameters for parallel mode execution
|
|
31
|
+
*/
|
|
32
|
+
export declare function getParallelModeParams({ cwd, prompt, existingBranch, }: ParallelModeInput): Promise<ParallelModeParams>;
|
|
33
|
+
/**
|
|
34
|
+
* Finish parallel mode by committing changes and cleaning up worktree
|
|
35
|
+
*/
|
|
36
|
+
export declare function finishParallelMode(worktreePath: string, worktreeBranch: string, originalCwd: string): Promise<ParallelModeResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Start parallel mode - creates worktree and returns the working directory
|
|
39
|
+
*/
|
|
40
|
+
export declare function startParallelMode(cwd: string, prompt: string, existingBranch?: string): Promise<ParallelModeParams>;
|
|
41
|
+
/**
|
|
42
|
+
* List active parallel mode worktrees
|
|
43
|
+
*/
|
|
44
|
+
export declare function listParallelWorktrees(cwd: string): Promise<string[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Clean up stale parallel mode worktrees
|
|
47
|
+
*/
|
|
48
|
+
export declare function cleanupStaleWorktrees(cwd: string): Promise<number>;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel Mode - Execute multiple tasks simultaneously using git worktrees
|
|
3
|
+
* Based on Kilo Code (Apache-2.0 License)
|
|
4
|
+
*
|
|
5
|
+
* Parallel mode allows multiple Recoder instances to work on the same repository
|
|
6
|
+
* without conflicts. Each instance operates in a separate git worktree.
|
|
7
|
+
*/
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
import simpleGit from 'simple-git';
|
|
11
|
+
import { getGitInfo, generateBranchName, branchExists, createWorktree, removeWorktree, } from './git-utils.js';
|
|
12
|
+
export const COMMIT_COMPLETION_TIMEOUT = 40000;
|
|
13
|
+
/**
|
|
14
|
+
* Determine the branch and worktree path for parallel mode
|
|
15
|
+
*/
|
|
16
|
+
export async function determineParallelBranch({ cwd, prompt, existingBranch, }) {
|
|
17
|
+
const { isRepo, branch } = await getGitInfo(cwd);
|
|
18
|
+
if (!isRepo) {
|
|
19
|
+
throw new Error('Parallel mode requires the current working directory to be a git repository');
|
|
20
|
+
}
|
|
21
|
+
if (!branch) {
|
|
22
|
+
throw new Error('Could not determine current git branch');
|
|
23
|
+
}
|
|
24
|
+
// Determine the branch to use
|
|
25
|
+
let worktreeBranch;
|
|
26
|
+
if (existingBranch) {
|
|
27
|
+
// Check if the existing branch exists
|
|
28
|
+
const exists = await branchExists(cwd, existingBranch);
|
|
29
|
+
if (!exists) {
|
|
30
|
+
throw new Error(`Branch "${existingBranch}" does not exist`);
|
|
31
|
+
}
|
|
32
|
+
worktreeBranch = existingBranch;
|
|
33
|
+
console.log(`๐ Using existing branch: ${worktreeBranch}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Generate branch name from prompt
|
|
37
|
+
worktreeBranch = generateBranchName(prompt);
|
|
38
|
+
console.log(`๐ฟ Creating new branch: ${worktreeBranch}`);
|
|
39
|
+
}
|
|
40
|
+
// Create worktree directory path in OS temp directory
|
|
41
|
+
const tempDir = os.tmpdir();
|
|
42
|
+
const worktreePath = path.join(tempDir, `recoder-worktree-${worktreeBranch.replace(/\//g, '-')}`);
|
|
43
|
+
// Create worktree
|
|
44
|
+
try {
|
|
45
|
+
await createWorktree(cwd, worktreePath, worktreeBranch, !existingBranch);
|
|
46
|
+
console.log(`๐ Created worktree at: ${worktreePath}`);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('Failed to create worktree:', error);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
return { worktreeBranch, worktreePath };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get parameters for parallel mode execution
|
|
56
|
+
*/
|
|
57
|
+
export async function getParallelModeParams({ cwd, prompt, existingBranch, }) {
|
|
58
|
+
try {
|
|
59
|
+
return await determineParallelBranch({ cwd, prompt, existingBranch });
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
63
|
+
console.error(`Failed to start parallel mode: ${message}`);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Helper function to commit changes with a fallback message
|
|
69
|
+
*/
|
|
70
|
+
async function commitWithFallback(cwd) {
|
|
71
|
+
const fallbackMessage = 'chore: recoder parallel mode task completion';
|
|
72
|
+
const git = simpleGit(cwd);
|
|
73
|
+
await git.commit(fallbackMessage);
|
|
74
|
+
console.log('๐ Changes committed with fallback message');
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Poll git status to check if commit is complete
|
|
78
|
+
* Returns true if commit was made, false if timeout reached
|
|
79
|
+
*/
|
|
80
|
+
async function waitForCommitCompletion(cwd) {
|
|
81
|
+
const pollIntervalMs = 1000;
|
|
82
|
+
const startTime = Date.now();
|
|
83
|
+
const git = simpleGit(cwd);
|
|
84
|
+
while (Date.now() - startTime < COMMIT_COMPLETION_TIMEOUT) {
|
|
85
|
+
try {
|
|
86
|
+
const stagedDiff = await git.diff(['--staged']);
|
|
87
|
+
// If no staged changes, commit was successful
|
|
88
|
+
if (!stagedDiff.trim()) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
// Wait before next poll
|
|
92
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('Error checking commit status:', error);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Finish parallel mode by committing changes and cleaning up worktree
|
|
103
|
+
*/
|
|
104
|
+
export async function finishParallelMode(worktreePath, worktreeBranch, originalCwd) {
|
|
105
|
+
const git = simpleGit(worktreePath);
|
|
106
|
+
try {
|
|
107
|
+
const status = await git.status();
|
|
108
|
+
if (!status.isClean()) {
|
|
109
|
+
console.log('๐ฆ Staging all changes...');
|
|
110
|
+
await git.add('-A');
|
|
111
|
+
const diff = await git.diff(['--staged']);
|
|
112
|
+
if (!diff.trim()) {
|
|
113
|
+
console.warn('โ ๏ธ No staged changes found after git add');
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.log('๐ Committing changes...');
|
|
117
|
+
await commitWithFallback(worktreePath);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
console.log('โจ No changes to commit');
|
|
122
|
+
}
|
|
123
|
+
// Print completion message
|
|
124
|
+
const green = '\x1b[32m';
|
|
125
|
+
const cyan = '\x1b[36m';
|
|
126
|
+
const yellow = '\x1b[33m';
|
|
127
|
+
const bold = '\x1b[1m';
|
|
128
|
+
const reset = '\x1b[0m';
|
|
129
|
+
console.log('\n' + cyan + 'โ'.repeat(80) + reset);
|
|
130
|
+
console.log(`${green}โ${reset} ${bold}Parallel mode complete!${reset} Changes committed to: ${cyan}${worktreeBranch}${reset}`);
|
|
131
|
+
console.log(`\n${bold}Review and merge changes:${reset}`);
|
|
132
|
+
console.log(` ${yellow}git diff ...${worktreeBranch}${reset}`);
|
|
133
|
+
console.log(` ${yellow}git merge ${worktreeBranch}${reset}`);
|
|
134
|
+
console.log(`\n${bold}๐ก Tip:${reset} Resume work with ${yellow}--existing-branch${reset}:`);
|
|
135
|
+
console.log(` ${yellow}recoder --parallel --existing-branch ${worktreeBranch} "<prompt>"${reset}`);
|
|
136
|
+
console.log(cyan + 'โ'.repeat(80) + reset + '\n');
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
branch: worktreeBranch,
|
|
140
|
+
worktreePath,
|
|
141
|
+
message: `Changes committed to branch: ${worktreeBranch}`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
146
|
+
console.error('Failed to commit changes:', errorMessage);
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
branch: worktreeBranch,
|
|
150
|
+
worktreePath,
|
|
151
|
+
message: `Failed to commit: ${errorMessage}`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
// Clean up worktree
|
|
156
|
+
try {
|
|
157
|
+
console.log(`๐งน Removing worktree at: ${worktreePath}`);
|
|
158
|
+
await removeWorktree(originalCwd, worktreePath);
|
|
159
|
+
console.log('โ
Worktree removed successfully');
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
163
|
+
console.warn('โ ๏ธ Failed to remove worktree:', errorMessage);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Start parallel mode - creates worktree and returns the working directory
|
|
169
|
+
*/
|
|
170
|
+
export async function startParallelMode(cwd, prompt, existingBranch) {
|
|
171
|
+
console.log('\n๐ Starting Recoder Parallel Mode...\n');
|
|
172
|
+
const params = await getParallelModeParams({
|
|
173
|
+
cwd,
|
|
174
|
+
prompt,
|
|
175
|
+
existingBranch,
|
|
176
|
+
});
|
|
177
|
+
console.log(`\nโ
Parallel mode ready!`);
|
|
178
|
+
console.log(`๐ Working in: ${params.worktreePath}`);
|
|
179
|
+
console.log(`๐ฟ Branch: ${params.worktreeBranch}\n`);
|
|
180
|
+
return params;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* List active parallel mode worktrees
|
|
184
|
+
*/
|
|
185
|
+
export async function listParallelWorktrees(cwd) {
|
|
186
|
+
const git = simpleGit(cwd);
|
|
187
|
+
try {
|
|
188
|
+
const result = await git.raw(['worktree', 'list', '--porcelain']);
|
|
189
|
+
const worktrees = [];
|
|
190
|
+
const lines = result.split('\n');
|
|
191
|
+
for (const line of lines) {
|
|
192
|
+
if (line.startsWith('worktree ')) {
|
|
193
|
+
const path = line.replace('worktree ', '').trim();
|
|
194
|
+
// Only include recoder worktrees
|
|
195
|
+
if (path.includes('recoder-worktree-')) {
|
|
196
|
+
worktrees.push(path);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return worktrees;
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
console.debug('Failed to list worktrees:', error);
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Clean up stale parallel mode worktrees
|
|
209
|
+
*/
|
|
210
|
+
export async function cleanupStaleWorktrees(cwd) {
|
|
211
|
+
const worktrees = await listParallelWorktrees(cwd);
|
|
212
|
+
let cleaned = 0;
|
|
213
|
+
for (const worktreePath of worktrees) {
|
|
214
|
+
try {
|
|
215
|
+
await removeWorktree(cwd, worktreePath);
|
|
216
|
+
cleaned++;
|
|
217
|
+
console.log(`๐งน Removed stale worktree: ${worktreePath}`);
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
console.debug(`Failed to remove worktree ${worktreePath}:`, error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return cleaned;
|
|
224
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Bridge Service
|
|
3
|
+
*
|
|
4
|
+
* Connects recoder-code CLI to the A2A workspace system on docker-backend
|
|
5
|
+
* via WebSocket. Enables local agents to:
|
|
6
|
+
* - Register as workspace members
|
|
7
|
+
* - Receive task assignments in real-time
|
|
8
|
+
* - Report task progress and results
|
|
9
|
+
* - Participate in channel conversations
|
|
10
|
+
*
|
|
11
|
+
* Uses existing RecoderAuthService for authentication tokens.
|
|
12
|
+
* Auto-reconnects with exponential backoff (same pattern as PlatformSyncService).
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'events';
|
|
15
|
+
export interface TaskAssignment {
|
|
16
|
+
taskId: string;
|
|
17
|
+
prompt: string;
|
|
18
|
+
context: Record<string, unknown>;
|
|
19
|
+
worktreeBranch?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ChannelMessageEvent {
|
|
22
|
+
channelId: string;
|
|
23
|
+
from: string;
|
|
24
|
+
content: string;
|
|
25
|
+
}
|
|
26
|
+
export interface WorkspaceAgent {
|
|
27
|
+
agentId: string;
|
|
28
|
+
status: string;
|
|
29
|
+
isLocal: boolean;
|
|
30
|
+
capabilities: string[];
|
|
31
|
+
}
|
|
32
|
+
export declare class AgentBridgeService extends EventEmitter {
|
|
33
|
+
private ws;
|
|
34
|
+
private reconnectTimeout;
|
|
35
|
+
private heartbeatInterval;
|
|
36
|
+
private connected;
|
|
37
|
+
private agentId;
|
|
38
|
+
private workspaceId;
|
|
39
|
+
private reconnectAttempts;
|
|
40
|
+
private readonly maxReconnectDelay;
|
|
41
|
+
private getToken;
|
|
42
|
+
constructor(getToken: () => Promise<string | null>, agentId?: string);
|
|
43
|
+
connect(workspaceId: string, capabilities?: string[]): Promise<void>;
|
|
44
|
+
private connectWebSocket;
|
|
45
|
+
disconnect(): void;
|
|
46
|
+
private scheduleReconnect;
|
|
47
|
+
private startHeartbeat;
|
|
48
|
+
private stopHeartbeat;
|
|
49
|
+
private send;
|
|
50
|
+
joinWorkspace(workspaceId: string): void;
|
|
51
|
+
leaveWorkspace(workspaceId: string): void;
|
|
52
|
+
sendProgress(taskId: string, progress: number, output: string): void;
|
|
53
|
+
completeTask(taskId: string, result: unknown): void;
|
|
54
|
+
failTask(taskId: string, error: string): void;
|
|
55
|
+
sendChannelMessage(channelId: string, content: string): void;
|
|
56
|
+
private handleMessage;
|
|
57
|
+
isConnected(): boolean;
|
|
58
|
+
getAgentId(): string;
|
|
59
|
+
getWorkspaceId(): string | null;
|
|
60
|
+
displayStatus(): void;
|
|
61
|
+
}
|