@proletariat/cli 0.3.13 → 0.3.15
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 +15 -8
- package/dist/commands/agent/staff/add.d.ts +1 -0
- package/dist/commands/agent/staff/add.js +5 -0
- package/dist/commands/work/revise.js +8 -14
- package/dist/commands/work/spawn.d.ts +1 -0
- package/dist/commands/work/spawn.js +7 -0
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +19 -33
- package/dist/lib/agents/commands.d.ts +5 -1
- package/dist/lib/agents/commands.js +48 -21
- package/dist/lib/agents/index.d.ts +5 -1
- package/dist/lib/agents/index.js +165 -107
- package/dist/lib/database/index.d.ts +4 -2
- package/dist/lib/database/index.js +29 -14
- package/dist/lib/execution/context.d.ts +24 -0
- package/dist/lib/execution/context.js +55 -0
- package/dist/lib/execution/devcontainer.d.ts +3 -0
- package/dist/lib/execution/devcontainer.js +38 -22
- package/dist/lib/execution/runners.js +9 -67
- package/dist/lib/execution/spawner.js +5 -18
- package/dist/lib/execution/types.d.ts +1 -0
- package/dist/lib/pmo/storage/base.js +56 -5
- package/oclif.manifest.json +4072 -4054
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
> ⚠️ **Beta Software** — Under active development. Commands and APIs may change between versions, and bugs are actively being squashed.
|
|
17
17
|
>
|
|
18
|
-
>
|
|
18
|
+
> **Let's get you shipping** [Book a call][cal-link] - I'm happy to help you get prlt running or chat feedback, ideas, multi-agent workflows, and the future of work/labor (and economic labor theory..)
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -55,6 +55,9 @@ prlt work spawn # Interactive: select tickets, environment, a
|
|
|
55
55
|
# Agent creates PR → You review → Merge → Done
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
<details>
|
|
59
|
+
<summary><b>Workflow Diagram</b> (click to expand)</summary>
|
|
60
|
+
|
|
58
61
|
```mermaid
|
|
59
62
|
sequenceDiagram
|
|
60
63
|
participant You
|
|
@@ -76,6 +79,8 @@ sequenceDiagram
|
|
|
76
79
|
You->>GitHub: Review & approve
|
|
77
80
|
```
|
|
78
81
|
|
|
82
|
+
</details>
|
|
83
|
+
|
|
79
84
|
Spawn agents to implement, groom, or review—not just write code.
|
|
80
85
|
|
|
81
86
|
### Interactive Menus
|
|
@@ -83,19 +88,19 @@ Spawn agents to implement, groom, or review—not just write code.
|
|
|
83
88
|
`prlt work` guides you through project and ticket selection:
|
|
84
89
|
|
|
85
90
|
<p align="center">
|
|
86
|
-
<img src="docs/images/work/work-project-select.png" alt="Project Selection" width="600">
|
|
91
|
+
<img src="https://raw.githubusercontent.com/chrismcdermut/proletariat/main/docs/images/work/work-project-select.png" alt="Project Selection" width="600">
|
|
87
92
|
</p>
|
|
88
93
|
|
|
89
94
|
Choose your operation—start a single agent, batch spawn, or watch a column:
|
|
90
95
|
|
|
91
96
|
<p align="center">
|
|
92
|
-
<img src="docs/images/work/work-operations-menu.png" alt="Work Operations Menu" width="600">
|
|
97
|
+
<img src="https://raw.githubusercontent.com/chrismcdermut/proletariat/main/docs/images/work/work-operations-menu.png" alt="Work Operations Menu" width="600">
|
|
93
98
|
</p>
|
|
94
99
|
|
|
95
100
|
Select tickets to spawn, grouped by priority:
|
|
96
101
|
|
|
97
102
|
<p align="center">
|
|
98
|
-
<img src="docs/images/work/work-ticket-select.png" alt="Ticket Selection" width="800">
|
|
103
|
+
<img src="https://raw.githubusercontent.com/chrismcdermut/proletariat/main/docs/images/work/work-ticket-select.png" alt="Ticket Selection" width="800">
|
|
99
104
|
</p>
|
|
100
105
|
|
|
101
106
|
---
|
|
@@ -249,7 +254,7 @@ $ prlt ticket create
|
|
|
249
254
|
View ticket details with `prlt ticket`:
|
|
250
255
|
|
|
251
256
|
<p align="center">
|
|
252
|
-
<img src="docs/images/ticket/ticket-view.png" alt="Ticket View" width="600">
|
|
257
|
+
<img src="https://raw.githubusercontent.com/chrismcdermut/proletariat/main/docs/images/ticket/ticket-view.png" alt="Ticket View" width="600">
|
|
253
258
|
</p>
|
|
254
259
|
|
|
255
260
|
#### 2. JSON Mode (AI Agents)
|
|
@@ -381,7 +386,7 @@ Each agent works in its own branch. No conflicts.
|
|
|
381
386
|
Monitor running agents with `prlt execution`:
|
|
382
387
|
|
|
383
388
|
<p align="center">
|
|
384
|
-
<img src="docs/images/execution/execution-list.png" alt="Execution List" width="800">
|
|
389
|
+
<img src="https://raw.githubusercontent.com/chrismcdermut/proletariat/main/docs/images/execution/execution-list.png" alt="Execution List" width="800">
|
|
385
390
|
</p>
|
|
386
391
|
|
|
387
392
|
```mermaid
|
|
@@ -414,7 +419,7 @@ flowchart LR
|
|
|
414
419
|
Agent-created PRs ready for review:
|
|
415
420
|
|
|
416
421
|
<p align="center">
|
|
417
|
-
<img src="docs/images/execution/github-prs.png" alt="GitHub Pull Requests" width="800">
|
|
422
|
+
<img src="https://raw.githubusercontent.com/chrismcdermut/proletariat/main/docs/images/execution/github-prs.png" alt="GitHub Pull Requests" width="800">
|
|
418
423
|
</p>
|
|
419
424
|
|
|
420
425
|
### Command Reference
|
|
@@ -637,7 +642,7 @@ Claude Code handles its own authentication via `claude login`.
|
|
|
637
642
|
|
|
638
643
|
- **Discord**: [discord.gg/tmZyjNNSvw](https://discord.gg/tmZyjNNSvw)
|
|
639
644
|
- **GitHub Issues**: [Report bugs or request features](https://github.com/chrismcdermut/proletariat-cli/issues)
|
|
640
|
-
- **
|
|
645
|
+
- **Setup Help**: [Book a call][cal-link] - I'll help you get things running
|
|
641
646
|
|
|
642
647
|
---
|
|
643
648
|
|
|
@@ -654,3 +659,5 @@ Apache 2.0
|
|
|
654
659
|
[Star on GitHub](https://github.com/chrismcdermut/proletariat) | [Install from NPM](https://www.npmjs.com/package/@proletariat/cli) | [Report Issues](https://github.com/chrismcdermut/proletariat/issues)
|
|
655
660
|
|
|
656
661
|
Made with ⚒️ by the proletariat.
|
|
662
|
+
|
|
663
|
+
[cal-link]: https://cal.com/chrismcdermut
|
|
@@ -9,6 +9,7 @@ export default class Add extends Command {
|
|
|
9
9
|
'no-container': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
10
|
theme: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
clone: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
13
|
};
|
|
13
14
|
static strict: boolean;
|
|
14
15
|
run(): Promise<void>;
|
|
@@ -32,6 +32,10 @@ export default class Add extends Command {
|
|
|
32
32
|
description: 'Output prompt configuration as JSON (for AI agents/scripts)',
|
|
33
33
|
default: false,
|
|
34
34
|
}),
|
|
35
|
+
clone: Flags.boolean({
|
|
36
|
+
description: 'Use independent git clone instead of worktree (more isolation, no real-time sync)',
|
|
37
|
+
default: false,
|
|
38
|
+
}),
|
|
35
39
|
};
|
|
36
40
|
static strict = false; // Allow multiple agent names
|
|
37
41
|
async run() {
|
|
@@ -249,6 +253,7 @@ export default class Add extends Command {
|
|
|
249
253
|
const addedAgents = await addAgentsToWorkspace(workspaceInfo, agentNames, {
|
|
250
254
|
skipDevcontainer: flags['no-container'],
|
|
251
255
|
themeId,
|
|
256
|
+
mountMode: flags.clone ? 'clone' : 'worktree',
|
|
252
257
|
});
|
|
253
258
|
if (addedAgents.length === 0) {
|
|
254
259
|
this.log(chalk.yellow('No new agents to add. All specified agents already exist.'));
|
|
@@ -13,6 +13,7 @@ import { runExecution, isDockerRunning, isDevcontainerCliInstalled } from '../..
|
|
|
13
13
|
import { ExecutionStorage } from '../../lib/execution/storage.js';
|
|
14
14
|
import { loadExecutionConfig, getTerminalApp, getShell, hasTerminalPreference, hasShellPreference } from '../../lib/execution/config.js';
|
|
15
15
|
import { hasDevcontainerConfig } from '../../lib/execution/devcontainer.js';
|
|
16
|
+
import { detectRepoWorktrees, resolveWorktreePath } from '../../lib/execution/context.js';
|
|
16
17
|
import { isGHInstalled, isGHAuthenticated, getPRFeedback, hasPendingFeedback, formatPRFeedbackForPrompt, } from '../../lib/pr/index.js';
|
|
17
18
|
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
18
19
|
export default class WorkRevise extends PMOCommand {
|
|
@@ -187,23 +188,14 @@ export default class WorkRevise extends PMOCommand {
|
|
|
187
188
|
db.close();
|
|
188
189
|
this.error(`Agent directory not found at ${agentDir}.`);
|
|
189
190
|
}
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
const gitPath = path.join(itemPath, '.git');
|
|
195
|
-
return fs.statSync(itemPath).isDirectory() && fs.existsSync(gitPath);
|
|
196
|
-
});
|
|
197
|
-
if (repoWorktrees.length === 1) {
|
|
198
|
-
worktreePath = path.join(agentDir, repoWorktrees[0]);
|
|
199
|
-
}
|
|
200
|
-
else if (repoWorktrees.length > 1) {
|
|
201
|
-
worktreePath = agentDir;
|
|
191
|
+
// Detect repository worktrees within agent directory
|
|
192
|
+
const repoWorktrees = detectRepoWorktrees(agentDir);
|
|
193
|
+
const worktreePath = resolveWorktreePath(agentDir, repoWorktrees);
|
|
194
|
+
if (repoWorktrees.length > 1) {
|
|
202
195
|
this.log(styles.muted(` Repos: ${repoWorktrees.join(', ')}`));
|
|
203
196
|
}
|
|
204
|
-
else {
|
|
197
|
+
else if (repoWorktrees.length === 0) {
|
|
205
198
|
this.log(styles.muted(` No git worktree found, using current directory`));
|
|
206
|
-
worktreePath = process.cwd();
|
|
207
199
|
}
|
|
208
200
|
// Get branch from ticket metadata or current branch
|
|
209
201
|
const branch = ticket.metadata?.pr_branch || this.getCurrentBranch(worktreePath);
|
|
@@ -221,6 +213,8 @@ export default class WorkRevise extends PMOCommand {
|
|
|
221
213
|
worktreePath,
|
|
222
214
|
branch,
|
|
223
215
|
hqPath,
|
|
216
|
+
pmoPath: this.pmoPath,
|
|
217
|
+
repoWorktrees,
|
|
224
218
|
// Revision-specific context
|
|
225
219
|
isRevision: true,
|
|
226
220
|
prFeedback: formattedFeedback,
|
|
@@ -24,6 +24,7 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
24
24
|
action: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
25
|
session: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
26
|
focus: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
|
+
clone: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
28
|
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
28
29
|
};
|
|
29
30
|
execute(): Promise<void>;
|
|
@@ -109,6 +109,10 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
109
109
|
description: 'Bring terminal to foreground when opening new tabs (default: opens in background)',
|
|
110
110
|
default: false,
|
|
111
111
|
}),
|
|
112
|
+
clone: Flags.boolean({
|
|
113
|
+
description: 'Use independent git clone instead of worktree (more isolation, no real-time sync)',
|
|
114
|
+
default: false,
|
|
115
|
+
}),
|
|
112
116
|
};
|
|
113
117
|
async execute() {
|
|
114
118
|
const { flags, argv } = await this.parse(WorkSpawn);
|
|
@@ -757,6 +761,9 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
757
761
|
// IMPORTANT: Pass --project to avoid re-prompting for project selection
|
|
758
762
|
// Pass --ephemeral to signal work:start should create an ephemeral agent
|
|
759
763
|
const startArgs = [ticket.id, '--project', projectId, '--ephemeral'];
|
|
764
|
+
// Pass clone flag if specified
|
|
765
|
+
if (flags.clone)
|
|
766
|
+
startArgs.push('--clone');
|
|
760
767
|
if (flags['per-ticket']) {
|
|
761
768
|
// Per-ticket mode: only pass display flag, let start prompt for the rest
|
|
762
769
|
// batchDisplayMode is for devcontainer, batchDisplay is for host
|
|
@@ -26,6 +26,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
26
26
|
agent: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
27
27
|
ephemeral: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
28
28
|
focus: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
29
|
+
clone: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
29
30
|
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
30
31
|
};
|
|
31
32
|
execute(): Promise<void>;
|
|
@@ -14,6 +14,7 @@ import { runExecution, isDockerRunning, isGitHubTokenAvailable, isDevcontainerCl
|
|
|
14
14
|
import { ExecutionStorage, ContainerStorage } from '../../lib/execution/storage.js';
|
|
15
15
|
import { loadExecutionConfig, getTerminalApp, promptTerminalPreference, getShell, promptShellPreference, hasTerminalPreference, hasShellPreference, getOrPromptCoderName } from '../../lib/execution/config.js';
|
|
16
16
|
import { hasDevcontainerConfig } from '../../lib/execution/devcontainer.js';
|
|
17
|
+
import { detectRepoWorktrees, resolveWorktreePath } from '../../lib/execution/context.js';
|
|
17
18
|
import { isGHInstalled, isGHAuthenticated } from '../../lib/pr/index.js';
|
|
18
19
|
/**
|
|
19
20
|
* Try to execute a git command, return true if successful
|
|
@@ -169,6 +170,10 @@ export default class WorkStart extends PMOCommand {
|
|
|
169
170
|
description: 'Bring terminal to foreground when opening new tabs (default: opens in background)',
|
|
170
171
|
default: false,
|
|
171
172
|
}),
|
|
173
|
+
clone: Flags.boolean({
|
|
174
|
+
description: 'Use independent git clone instead of worktree (more isolation, no real-time sync)',
|
|
175
|
+
default: false,
|
|
176
|
+
}),
|
|
172
177
|
};
|
|
173
178
|
async execute() {
|
|
174
179
|
const { args, flags } = await this.parse(WorkStart);
|
|
@@ -314,6 +319,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
314
319
|
const ephemeralResult = await createEphemeralAgent(workspaceInfo, {
|
|
315
320
|
skipDevcontainer: flags['run-on-host'],
|
|
316
321
|
log: (msg) => this.log(msg),
|
|
322
|
+
mountMode: flags.clone ? 'clone' : 'worktree',
|
|
317
323
|
});
|
|
318
324
|
agentName = ephemeralResult.name;
|
|
319
325
|
agentWorktreePath = ephemeralResult.worktreePath;
|
|
@@ -381,6 +387,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
381
387
|
const ephemeralResult = await createEphemeralAgent(workspaceInfo, {
|
|
382
388
|
skipDevcontainer: flags['run-on-host'],
|
|
383
389
|
log: (msg) => this.log(msg),
|
|
390
|
+
mountMode: flags.clone ? 'clone' : 'worktree',
|
|
384
391
|
});
|
|
385
392
|
agentName = ephemeralResult.name;
|
|
386
393
|
agentWorktreePath = ephemeralResult.worktreePath;
|
|
@@ -397,6 +404,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
397
404
|
const ephemeralResult = await createEphemeralAgent(workspaceInfo, {
|
|
398
405
|
skipDevcontainer: flags['run-on-host'],
|
|
399
406
|
log: (msg) => this.log(msg),
|
|
407
|
+
mountMode: flags.clone ? 'clone' : 'worktree',
|
|
400
408
|
});
|
|
401
409
|
agentName = ephemeralResult.name;
|
|
402
410
|
agentWorktreePath = ephemeralResult.worktreePath;
|
|
@@ -495,31 +503,14 @@ export default class WorkStart extends PMOCommand {
|
|
|
495
503
|
}
|
|
496
504
|
}
|
|
497
505
|
}
|
|
498
|
-
//
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
// Check if agent has repository worktrees (subdirectories with .git)
|
|
503
|
-
const agentContents = fs.readdirSync(agentDir);
|
|
504
|
-
const repoWorktrees = agentContents.filter(item => {
|
|
505
|
-
const itemPath = path.join(agentDir, item);
|
|
506
|
-
const gitPath = path.join(itemPath, '.git');
|
|
507
|
-
return fs.statSync(itemPath).isDirectory() && fs.existsSync(gitPath);
|
|
508
|
-
});
|
|
509
|
-
if (repoWorktrees.length === 1) {
|
|
510
|
-
// Single repo - open directly in the repo worktree
|
|
511
|
-
worktreePath = path.join(agentDir, repoWorktrees[0]);
|
|
512
|
-
}
|
|
513
|
-
else if (repoWorktrees.length > 1) {
|
|
514
|
-
// Multiple repos - open in agent directory, Claude can navigate between them
|
|
515
|
-
worktreePath = agentDir;
|
|
506
|
+
// Detect repository worktrees within agent directory
|
|
507
|
+
const repoWorktrees = detectRepoWorktrees(agentDir);
|
|
508
|
+
const worktreePath = resolveWorktreePath(agentDir, repoWorktrees);
|
|
509
|
+
if (repoWorktrees.length > 1) {
|
|
516
510
|
this.log(styles.muted(` Repos: ${repoWorktrees.join(', ')}`));
|
|
517
511
|
}
|
|
518
|
-
else {
|
|
519
|
-
// No git worktrees found - agent is a placeholder
|
|
520
|
-
// Fall back to the current working directory
|
|
512
|
+
else if (repoWorktrees.length === 0) {
|
|
521
513
|
this.log(styles.muted(` No git worktree found for agent, using current directory`));
|
|
522
|
-
worktreePath = process.cwd();
|
|
523
514
|
}
|
|
524
515
|
// Get coder name for branch naming (prompts on first use)
|
|
525
516
|
const coderName = await getOrPromptCoderName(db);
|
|
@@ -647,6 +638,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
647
638
|
branch,
|
|
648
639
|
hqPath,
|
|
649
640
|
pmoPath: this.pmoPath, // PMO path for container mounting
|
|
641
|
+
repoWorktrees,
|
|
650
642
|
// Action context
|
|
651
643
|
actionId: selectedAction?.id,
|
|
652
644
|
actionName: selectedAction?.name || (customPrompt ? 'Custom' : undefined),
|
|
@@ -1500,6 +1492,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
1500
1492
|
...(flags['run-on-host'] ? ['--run-on-host'] : []),
|
|
1501
1493
|
...(flags.force ? ['--force'] : []),
|
|
1502
1494
|
'--permission-mode', batchPermissionMode,
|
|
1495
|
+
...(flags.clone ? ['--clone'] : []),
|
|
1503
1496
|
]);
|
|
1504
1497
|
successCount++;
|
|
1505
1498
|
}
|
|
@@ -1527,17 +1520,9 @@ export default class WorkStart extends PMOCommand {
|
|
|
1527
1520
|
if (!fs.existsSync(agentDir)) {
|
|
1528
1521
|
throw new Error(`Agent directory not found: ${agentDir}`);
|
|
1529
1522
|
}
|
|
1530
|
-
//
|
|
1531
|
-
|
|
1532
|
-
const
|
|
1533
|
-
const repoWorktrees = agentContents.filter(item => {
|
|
1534
|
-
const itemPath = path.join(agentDir, item);
|
|
1535
|
-
const gitPath = path.join(itemPath, '.git');
|
|
1536
|
-
return fs.statSync(itemPath).isDirectory() && fs.existsSync(gitPath);
|
|
1537
|
-
});
|
|
1538
|
-
if (repoWorktrees.length === 1) {
|
|
1539
|
-
worktreePath = path.join(agentDir, repoWorktrees[0]);
|
|
1540
|
-
}
|
|
1523
|
+
// Detect repository worktrees within agent directory
|
|
1524
|
+
const repoWorktrees = detectRepoWorktrees(agentDir);
|
|
1525
|
+
const worktreePath = resolveWorktreePath(agentDir, repoWorktrees);
|
|
1541
1526
|
// Get coder name for branch naming (prompts on first use)
|
|
1542
1527
|
const coderName = await getOrPromptCoderName(db);
|
|
1543
1528
|
// Use ticket's existing branch or generate a new one
|
|
@@ -1583,6 +1568,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
1583
1568
|
branch,
|
|
1584
1569
|
hqPath: workspaceInfo.path,
|
|
1585
1570
|
pmoPath: this.pmoPath,
|
|
1571
|
+
repoWorktrees,
|
|
1586
1572
|
createPR: flags['create-pr'] || false,
|
|
1587
1573
|
// Use 'implement' action for batch mode
|
|
1588
1574
|
actionId: defaultAction?.id,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Agent, Repository } from '../database/index.js';
|
|
1
|
+
import { Agent, Repository, MountMode as DBMountMode } from '../database/index.js';
|
|
2
2
|
export interface AgentStatus {
|
|
3
3
|
name: string;
|
|
4
4
|
exists: boolean;
|
|
@@ -68,9 +68,11 @@ export declare function validateAgentNames(agentNames: string[]): {
|
|
|
68
68
|
valid: string[];
|
|
69
69
|
invalid: string[];
|
|
70
70
|
};
|
|
71
|
+
export type MountMode = DBMountMode;
|
|
71
72
|
export interface AddAgentOptions {
|
|
72
73
|
skipDevcontainer?: boolean;
|
|
73
74
|
themeId?: string;
|
|
75
|
+
mountMode?: DBMountMode;
|
|
74
76
|
}
|
|
75
77
|
/**
|
|
76
78
|
* Create agent worktrees and update database
|
|
@@ -90,6 +92,8 @@ export interface EphemeralAgentOptions {
|
|
|
90
92
|
* Optional logger for conflict messages (e.g., when a tmux session or directory already exists)
|
|
91
93
|
*/
|
|
92
94
|
log?: (message: string) => void;
|
|
95
|
+
/** Mount mode: 'worktree' = git worktree (default), 'clone' = independent clone */
|
|
96
|
+
mountMode?: DBMountMode;
|
|
93
97
|
}
|
|
94
98
|
export interface EphemeralAgentResult {
|
|
95
99
|
name: string;
|
|
@@ -283,15 +283,15 @@ export async function addAgentsToWorkspace(workspaceInfo, agentNames, options) {
|
|
|
283
283
|
if (newAgents.length === 0) {
|
|
284
284
|
return [];
|
|
285
285
|
}
|
|
286
|
-
// Create worktrees
|
|
286
|
+
// Create worktrees/clones
|
|
287
287
|
if (workspaceInfo.type === 'hq') {
|
|
288
288
|
await createAgentWorktrees(workspaceInfo.agentsPath, newAgents, workspaceInfo.path, options);
|
|
289
289
|
}
|
|
290
290
|
else {
|
|
291
291
|
await createAgentWorktrees(workspaceInfo.agentsPath, newAgents, undefined, options);
|
|
292
292
|
}
|
|
293
|
-
// Add to database (with optional theme ID)
|
|
294
|
-
addAgentsToDatabase(workspaceInfo.path, newAgents, options?.themeId);
|
|
293
|
+
// Add to database (with optional theme ID and mount mode)
|
|
294
|
+
addAgentsToDatabase(workspaceInfo.path, newAgents, options?.themeId, options?.mountMode || 'clone');
|
|
295
295
|
return newAgents;
|
|
296
296
|
}
|
|
297
297
|
/**
|
|
@@ -441,27 +441,53 @@ export async function createEphemeralAgent(workspaceInfo, options) {
|
|
|
441
441
|
if (!fs.existsSync(agentDir)) {
|
|
442
442
|
fs.mkdirSync(agentDir, { recursive: true });
|
|
443
443
|
}
|
|
444
|
-
// Create worktrees for each repository
|
|
444
|
+
// Create worktrees/clones for each repository
|
|
445
445
|
const reposPath = path.join(workspaceInfo.path, 'repos');
|
|
446
|
+
const mountMode = options?.mountMode || 'worktree';
|
|
446
447
|
if (fs.existsSync(reposPath) && workspaceInfo.repositories.length > 0) {
|
|
447
448
|
for (const repo of workspaceInfo.repositories) {
|
|
448
449
|
const sourceRepoPath = path.join(reposPath, repo.name);
|
|
449
|
-
const
|
|
450
|
-
if (fs.existsSync(sourceRepoPath) && !fs.existsSync(
|
|
451
|
-
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
450
|
+
const targetPath = path.join(agentDir, repo.name);
|
|
451
|
+
if (fs.existsSync(sourceRepoPath) && !fs.existsSync(targetPath)) {
|
|
452
|
+
if (mountMode === 'clone') {
|
|
453
|
+
// CLONE MODE: Create independent git clone
|
|
454
|
+
try {
|
|
455
|
+
// Get remote URL from source repo
|
|
456
|
+
const remoteUrl = execSync('git remote get-url origin', {
|
|
457
|
+
cwd: sourceRepoPath,
|
|
458
|
+
encoding: 'utf-8',
|
|
459
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
460
|
+
}).trim();
|
|
461
|
+
if (remoteUrl) {
|
|
462
|
+
execSync(`git clone "${remoteUrl}" "${targetPath}"`, {
|
|
463
|
+
stdio: 'pipe'
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
catch {
|
|
468
|
+
// If clone fails, try to just create the directory
|
|
469
|
+
if (!fs.existsSync(targetPath)) {
|
|
470
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
471
|
+
}
|
|
472
|
+
}
|
|
459
473
|
}
|
|
460
|
-
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
474
|
+
else {
|
|
475
|
+
// WORKTREE MODE: Create git worktree
|
|
476
|
+
try {
|
|
477
|
+
// Create git worktree for the repository
|
|
478
|
+
// Don't create a branch yet - that happens in work:start
|
|
479
|
+
// Use --detach to create without a branch reference
|
|
480
|
+
execSync(`git worktree add --detach "${targetPath}"`, {
|
|
481
|
+
cwd: sourceRepoPath,
|
|
482
|
+
stdio: 'pipe'
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
// If worktree creation fails, try to just create the directory
|
|
487
|
+
// The agent can still work without a worktree (e.g., for non-git projects)
|
|
488
|
+
if (!fs.existsSync(targetPath)) {
|
|
489
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
490
|
+
}
|
|
465
491
|
}
|
|
466
492
|
}
|
|
467
493
|
}
|
|
@@ -474,12 +500,13 @@ export async function createEphemeralAgent(workspaceInfo, options) {
|
|
|
474
500
|
createDevcontainerConfig({
|
|
475
501
|
agentName,
|
|
476
502
|
agentDir,
|
|
477
|
-
repoWorktrees: workspaceInfo.repositories.map(r => r.name)
|
|
503
|
+
repoWorktrees: mountMode === 'worktree' ? workspaceInfo.repositories.map(r => r.name) : undefined,
|
|
504
|
+
mountMode,
|
|
478
505
|
});
|
|
479
506
|
}
|
|
480
507
|
}
|
|
481
508
|
// Add to database
|
|
482
|
-
const agent = addEphemeralAgentToDatabase(workspaceInfo.path, agentName, baseName, options?.themeId);
|
|
509
|
+
const agent = addEphemeralAgentToDatabase(workspaceInfo.path, agentName, baseName, options?.themeId, mountMode);
|
|
483
510
|
return {
|
|
484
511
|
name: agentName,
|
|
485
512
|
baseName,
|
|
@@ -20,11 +20,15 @@ export { findHQRoot } from '../workspace.js';
|
|
|
20
20
|
* Prompt user to enter agent names
|
|
21
21
|
*/
|
|
22
22
|
export declare function promptAgentNames(existingAgents?: string[]): Promise<string[]>;
|
|
23
|
+
export type MountMode = 'worktree' | 'clone';
|
|
23
24
|
export interface CreateAgentOptions {
|
|
24
25
|
skipDevcontainer?: boolean;
|
|
26
|
+
mountMode?: MountMode;
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
|
-
* Create agent
|
|
29
|
+
* Create agent repos (worktree or clone mode)
|
|
30
|
+
* - worktree mode: Uses git worktree add (shared .git, requires parent repo mounts in container)
|
|
31
|
+
* - clone mode: Uses git clone (independent repo, no path translation needed)
|
|
28
32
|
*/
|
|
29
33
|
export declare function createAgentWorktrees(workspacePath: string, agents: string[], hqPath?: string, options?: CreateAgentOptions): Promise<void>;
|
|
30
34
|
/**
|