@proletariat/cli 0.3.35 → 0.3.40

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 (148) hide show
  1. package/README.md +37 -2
  2. package/bin/dev.js +0 -0
  3. package/dist/commands/agent/auth.d.ts +12 -2
  4. package/dist/commands/agent/auth.js +128 -4
  5. package/dist/commands/agent/list.js +16 -7
  6. package/dist/commands/agent/status.js +32 -4
  7. package/dist/commands/board/watch.js +6 -0
  8. package/dist/commands/branch/list.d.ts +1 -0
  9. package/dist/commands/branch/list.js +43 -12
  10. package/dist/commands/branch/where.js +9 -19
  11. package/dist/commands/category/list.d.ts +2 -1
  12. package/dist/commands/category/list.js +38 -13
  13. package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
  14. package/dist/commands/{claude.js → claude/index.js} +12 -12
  15. package/dist/commands/claude/open.d.ts +13 -0
  16. package/dist/commands/claude/open.js +175 -0
  17. package/dist/commands/diet.js +18 -2
  18. package/dist/commands/docker/logs.js +7 -3
  19. package/dist/commands/docker/shell.js +6 -0
  20. package/dist/commands/docker/start.js +20 -4
  21. package/dist/commands/docker/sync.d.ts +4 -0
  22. package/dist/commands/docker/sync.js +30 -2
  23. package/dist/commands/epic/show.d.ts +13 -0
  24. package/dist/commands/epic/show.js +16 -0
  25. package/dist/commands/epic/ticket.js +7 -24
  26. package/dist/commands/epic/view.js +27 -0
  27. package/dist/commands/execution/config.d.ts +0 -4
  28. package/dist/commands/execution/config.js +14 -46
  29. package/dist/commands/execution/index.js +2 -1
  30. package/dist/commands/execution/logs.js +7 -1
  31. package/dist/commands/execution/stop.js +2 -1
  32. package/dist/commands/execution/view.js +30 -26
  33. package/dist/commands/init.js +2 -19
  34. package/dist/commands/label/create.js +2 -1
  35. package/dist/commands/label/delete.js +2 -1
  36. package/dist/commands/label/group/create.js +2 -1
  37. package/dist/commands/label/group/list.js +2 -1
  38. package/dist/commands/label/list.js +2 -1
  39. package/dist/commands/mcp-server.js +27 -1
  40. package/dist/commands/phase/template/list.js +2 -1
  41. package/dist/commands/pmo/init.js +12 -40
  42. package/dist/commands/project/create.js +3 -4
  43. package/dist/commands/project/update.js +5 -6
  44. package/dist/commands/pull.js +24 -0
  45. package/dist/commands/qa/index.d.ts +54 -0
  46. package/dist/commands/qa/index.js +762 -0
  47. package/dist/commands/repo/view.js +2 -8
  48. package/dist/commands/session/attach.js +4 -4
  49. package/dist/commands/session/create.d.ts +19 -0
  50. package/dist/commands/session/create.js +102 -0
  51. package/dist/commands/session/health.js +4 -23
  52. package/dist/commands/session/index.js +14 -1
  53. package/dist/commands/session/list.js +9 -8
  54. package/dist/commands/session/peek.d.ts +38 -0
  55. package/dist/commands/session/peek.js +316 -0
  56. package/dist/commands/session/poke.d.ts +27 -0
  57. package/dist/commands/session/poke.js +219 -0
  58. package/dist/commands/spec/view.js +29 -0
  59. package/dist/commands/template/list.js +2 -1
  60. package/dist/commands/theme/add-names.d.ts +4 -0
  61. package/dist/commands/theme/add-names.js +11 -1
  62. package/dist/commands/theme/create.d.ts +2 -0
  63. package/dist/commands/theme/create.js +8 -0
  64. package/dist/commands/ticket/bulk.js +2 -2
  65. package/dist/commands/ticket/complete.js +2 -2
  66. package/dist/commands/ticket/create.js +21 -0
  67. package/dist/commands/ticket/delete.js +8 -0
  68. package/dist/commands/ticket/edit.js +25 -0
  69. package/dist/commands/ticket/epic.js +17 -43
  70. package/dist/commands/ticket/index.js +2 -2
  71. package/dist/commands/ticket/move.js +25 -2
  72. package/dist/commands/ticket/resolve.js +3 -4
  73. package/dist/commands/ticket/show.d.ts +13 -0
  74. package/dist/commands/ticket/show.js +16 -0
  75. package/dist/commands/ticket/template/list.js +2 -1
  76. package/dist/commands/ticket/view.d.ts +0 -1
  77. package/dist/commands/ticket/view.js +30 -1
  78. package/dist/commands/work/index.js +4 -0
  79. package/dist/commands/work/spawn-all.js +1 -1
  80. package/dist/commands/work/spawn.js +15 -4
  81. package/dist/commands/work/start.js +186 -103
  82. package/dist/commands/work/status.d.ts +14 -0
  83. package/dist/commands/work/status.js +60 -0
  84. package/dist/commands/work/watch.js +1 -1
  85. package/dist/commands/workflow/index.js +2 -1
  86. package/dist/commands/workflow/show.d.ts +13 -0
  87. package/dist/commands/workflow/show.js +16 -0
  88. package/dist/commands/workspace/add.js +15 -0
  89. package/dist/commands/workspace/list.js +2 -1
  90. package/dist/commands/workspace/prune.js +7 -7
  91. package/dist/hooks/init.js +10 -2
  92. package/dist/lib/agents/commands.d.ts +5 -0
  93. package/dist/lib/agents/commands.js +143 -97
  94. package/dist/lib/branch/index.d.ts +1 -0
  95. package/dist/lib/database/drizzle-schema.d.ts +465 -0
  96. package/dist/lib/database/drizzle-schema.js +53 -0
  97. package/dist/lib/database/index.d.ts +47 -1
  98. package/dist/lib/database/index.js +138 -20
  99. package/dist/lib/execution/config.d.ts +15 -1
  100. package/dist/lib/execution/config.js +28 -0
  101. package/dist/lib/execution/runners.d.ts +45 -0
  102. package/dist/lib/execution/runners.js +187 -26
  103. package/dist/lib/execution/session-utils.d.ts +16 -1
  104. package/dist/lib/execution/session-utils.js +71 -4
  105. package/dist/lib/execution/spawner.js +15 -2
  106. package/dist/lib/execution/storage.d.ts +6 -1
  107. package/dist/lib/execution/storage.js +35 -5
  108. package/dist/lib/execution/types.d.ts +3 -0
  109. package/dist/lib/mcp/tools/board.js +4 -6
  110. package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
  111. package/dist/lib/mcp/tools/epic.js +8 -3
  112. package/dist/lib/mcp/tools/index.d.ts +1 -0
  113. package/dist/lib/mcp/tools/index.js +1 -0
  114. package/dist/lib/mcp/tools/spec.js +1 -1
  115. package/dist/lib/mcp/tools/ticket.js +11 -9
  116. package/dist/lib/mcp/tools/tmux.d.ts +16 -0
  117. package/dist/lib/mcp/tools/tmux.js +182 -0
  118. package/dist/lib/mcp/tools/work.js +148 -6
  119. package/dist/lib/mcp/types.d.ts +10 -0
  120. package/dist/lib/multiline-input.js +2 -1
  121. package/dist/lib/pmo/base-command.js +4 -4
  122. package/dist/lib/pmo/schema.d.ts +1 -1
  123. package/dist/lib/pmo/schema.js +1 -0
  124. package/dist/lib/pmo/storage/actions.js +1 -1
  125. package/dist/lib/pmo/storage/base.js +402 -50
  126. package/dist/lib/pmo/storage/dependencies.d.ts +1 -0
  127. package/dist/lib/pmo/storage/dependencies.js +11 -3
  128. package/dist/lib/pmo/storage/epics.js +1 -1
  129. package/dist/lib/pmo/storage/helpers.d.ts +4 -4
  130. package/dist/lib/pmo/storage/helpers.js +36 -26
  131. package/dist/lib/pmo/storage/projects.d.ts +2 -0
  132. package/dist/lib/pmo/storage/projects.js +207 -119
  133. package/dist/lib/pmo/storage/specs.d.ts +2 -0
  134. package/dist/lib/pmo/storage/specs.js +274 -188
  135. package/dist/lib/pmo/storage/tickets.d.ts +2 -0
  136. package/dist/lib/pmo/storage/tickets.js +350 -290
  137. package/dist/lib/pmo/storage/types.d.ts +1 -0
  138. package/dist/lib/pmo/storage/views.d.ts +2 -0
  139. package/dist/lib/pmo/storage/views.js +183 -130
  140. package/dist/lib/prompt-command.d.ts +20 -0
  141. package/dist/lib/prompt-command.js +38 -2
  142. package/dist/lib/prompt-json.d.ts +41 -4
  143. package/dist/lib/prompt-json.js +138 -7
  144. package/dist/lib/styles.d.ts +37 -0
  145. package/dist/lib/styles.js +73 -0
  146. package/oclif.manifest.json +4046 -3385
  147. package/package.json +11 -6
  148. package/LICENSE +0 -190
@@ -8,13 +8,21 @@ import { findHQRoot } from '../lib/workspace.js';
8
8
  * - No workspaces are registered in machine config (~/.proletariat/config.json)
9
9
  * - AND they're not currently inside a valid HQ directory
10
10
  */
11
- const hook = async function ({ id, config }) {
11
+ const hook = async function ({ id, argv, config }) {
12
12
  // Skip for init command to avoid infinite loop
13
13
  if (id === 'init') {
14
14
  return;
15
15
  }
16
+ // Skip when running under oclif tooling (manifest, readme generation)
17
+ // These run commands to scan metadata and should not trigger the init flow
18
+ if (process.env.OCLIF_COMPILATION || process.argv[1]?.includes('oclif')) {
19
+ return;
20
+ }
16
21
  // Skip when --help flag is present - help should always be available
17
- if (process.argv.includes('--help') || process.argv.includes('-h')) {
22
+ // Check both process.argv (production CLI) and the oclif-provided argv
23
+ // (programmatic invocation via @oclif/test runCommand)
24
+ if (process.argv.includes('--help') || process.argv.includes('-h') ||
25
+ argv?.includes('--help') || argv?.includes('-h')) {
18
26
  return;
19
27
  }
20
28
  // Skip for help-related commands/flags
@@ -111,6 +111,11 @@ export interface EphemeralAgentResult {
111
111
  /**
112
112
  * Create an ephemeral agent on-demand for a spawn operation.
113
113
  * Creates worktree in agents/temp/{name}/
114
+ *
115
+ * Concurrency-safe: if the generated name collides at the DB level
116
+ * (e.g. a parallel process inserted the same name first), we clean up
117
+ * the on-disk artifacts, regenerate a name, and retry up to
118
+ * EPHEMERAL_CREATE_MAX_RETRIES times.
114
119
  */
115
120
  export declare function createEphemeralAgent(workspaceInfo: WorkspaceInfo, options?: EphemeralAgentOptions): Promise<EphemeralAgentResult>;
116
121
  /**
@@ -5,7 +5,7 @@ import * as fs from 'node:fs';
5
5
  import * as path from 'node:path';
6
6
  import { execSync } from 'node:child_process';
7
7
  import inquirer from 'inquirer';
8
- import { getWorkspaceConfig, getWorkspaceAgents, getWorkspaceRepositories, getAgentWorktrees, addAgentsToDatabase, removeAgentsFromDatabase, addEphemeralAgentToDatabase, getEphemeralAgentNames, getActiveTheme, markAgentCleaned, discoverAgentsOnDisk } from '../database/index.js';
8
+ import { getWorkspaceConfig, getWorkspaceAgents, getWorkspaceRepositories, getAgentWorktrees, addAgentsToDatabase, removeAgentsFromDatabase, tryAddEphemeralAgentToDatabase, getEphemeralAgentNames, getActiveTheme, markAgentCleaned, discoverAgentsOnDisk } from '../database/index.js';
9
9
  import { isValidAgentName, getSuggestedAgentNames, generateEphemeralAgentName, getThemePersistentDir, getThemeEphemeralDir, extractBaseName, getAgentBaseName, } from '../themes.js';
10
10
  import { createDevcontainerConfig } from '../execution/devcontainer.js';
11
11
  import { getGitIdentity } from '../pr/index.js';
@@ -395,16 +395,21 @@ export async function removeAgentsFromWorkspace(workspaceInfo, agentNames) {
395
395
  }
396
396
  return { removed, failed };
397
397
  }
398
+ /**
399
+ * Maximum number of retries when a name collision is detected at the DB level.
400
+ * Each retry generates a fresh name to avoid repeated collisions.
401
+ */
402
+ const EPHEMERAL_CREATE_MAX_RETRIES = 5;
398
403
  /**
399
404
  * Create an ephemeral agent on-demand for a spawn operation.
400
405
  * Creates worktree in agents/temp/{name}/
406
+ *
407
+ * Concurrency-safe: if the generated name collides at the DB level
408
+ * (e.g. a parallel process inserted the same name first), we clean up
409
+ * the on-disk artifacts, regenerate a name, and retry up to
410
+ * EPHEMERAL_CREATE_MAX_RETRIES times.
401
411
  */
402
412
  export async function createEphemeralAgent(workspaceInfo, options) {
403
- // Get existing agent names for uniqueness check
404
- const existingNames = new Set([
405
- ...Array.from(getEphemeralAgentNames(workspaceInfo.path)),
406
- ...workspaceInfo.agents.map(a => a.name.toLowerCase())
407
- ]);
408
413
  const log = options?.log;
409
414
  // Get theme: use provided themeId, or fall back to workspace's active theme
410
415
  let themeId = options?.themeId;
@@ -417,117 +422,158 @@ export async function createEphemeralAgent(workspaceInfo, options) {
417
422
  // Extract base names currently in use by active agents
418
423
  // This helps the generator prefer fresh base names
419
424
  const inUseBaseNames = new Set(workspaceInfo.agents.map(agent => getAgentBaseName(agent).toLowerCase()));
420
- // Create a conflict checker for external resources (tmux sessions, directories)
421
- const checkExternalConflict = (candidateName) => {
422
- // Check if a tmux session with this name already exists (could be from manual creation)
423
- if (tmuxSessionExists(candidateName)) {
424
- return { conflict: true, reason: `tmux session "${candidateName}" already exists` };
425
- }
426
- // Check if the directory already exists in agents/temp/
427
- const candidateDir = path.join(tempAgentsBasePath, candidateName);
428
- if (fs.existsSync(candidateDir)) {
429
- return { conflict: true, reason: `directory "${candidateDir}" already exists` };
430
- }
431
- return { conflict: false };
432
- };
433
- // Log when conflicts are skipped during name generation
434
- const onConflictSkipped = (name, reason) => {
435
- log?.(`⚠️ Skipping name "${name}": ${reason}`);
436
- };
437
- // Generate unique ephemeral name using workspace theme
438
- const nameOptions = {
439
- themeId,
440
- checkExternalConflict,
441
- onConflictSkipped,
442
- inUseBaseNames
443
- };
444
- const agentName = generateEphemeralAgentName(existingNames, nameOptions);
445
- // Extract base name from the generated name (e.g., "bezos" from "bold-bezos" or "bold-bezos-2")
446
- const baseName = extractBaseName(agentName);
447
- // Create temp agents directory if it doesn't exist
448
- if (!fs.existsSync(tempAgentsBasePath)) {
449
- fs.mkdirSync(tempAgentsBasePath, { recursive: true });
450
- }
451
- const agentDir = path.join(tempAgentsBasePath, agentName);
452
- // Create agent directory
453
- if (!fs.existsSync(agentDir)) {
454
- fs.mkdirSync(agentDir, { recursive: true });
455
- }
456
- // Create worktrees/clones for each repository
457
425
  const reposPath = path.join(workspaceInfo.path, 'repos');
458
426
  const mountMode = options?.mountMode || 'worktree';
459
- if (fs.existsSync(reposPath) && workspaceInfo.repositories.length > 0) {
460
- for (const repo of workspaceInfo.repositories) {
461
- const sourceRepoPath = path.join(reposPath, repo.name);
462
- const targetPath = path.join(agentDir, repo.name);
463
- if (fs.existsSync(sourceRepoPath) && !fs.existsSync(targetPath)) {
464
- if (mountMode === 'clone') {
465
- // CLONE MODE: Create independent git clone
466
- try {
467
- // Get remote URL from source repo
468
- const remoteUrl = execSync('git remote get-url origin', {
469
- cwd: sourceRepoPath,
470
- encoding: 'utf-8',
471
- stdio: ['pipe', 'pipe', 'pipe']
472
- }).trim();
473
- if (remoteUrl) {
474
- execSync(`git clone "${remoteUrl}" "${targetPath}"`, {
427
+ for (let attempt = 0; attempt <= EPHEMERAL_CREATE_MAX_RETRIES; attempt++) {
428
+ // Re-read existing names on every attempt so we see names that were
429
+ // inserted by concurrent processes since our last try
430
+ const existingNames = new Set([
431
+ ...Array.from(getEphemeralAgentNames(workspaceInfo.path)),
432
+ ...workspaceInfo.agents.map(a => a.name.toLowerCase())
433
+ ]);
434
+ // Create a conflict checker for external resources (tmux sessions, directories)
435
+ const checkExternalConflict = (candidateName) => {
436
+ if (tmuxSessionExists(candidateName)) {
437
+ return { conflict: true, reason: `tmux session "${candidateName}" already exists` };
438
+ }
439
+ const candidateDir = path.join(tempAgentsBasePath, candidateName);
440
+ if (fs.existsSync(candidateDir)) {
441
+ return { conflict: true, reason: `directory "${candidateDir}" already exists` };
442
+ }
443
+ return { conflict: false };
444
+ };
445
+ const onConflictSkipped = (name, reason) => {
446
+ log?.(`⚠️ Skipping name "${name}": ${reason}`);
447
+ };
448
+ const nameOptions = {
449
+ themeId,
450
+ checkExternalConflict,
451
+ onConflictSkipped,
452
+ inUseBaseNames
453
+ };
454
+ const agentName = generateEphemeralAgentName(existingNames, nameOptions);
455
+ const baseName = extractBaseName(agentName);
456
+ // Create temp agents directory if it doesn't exist
457
+ if (!fs.existsSync(tempAgentsBasePath)) {
458
+ fs.mkdirSync(tempAgentsBasePath, { recursive: true });
459
+ }
460
+ const agentDir = path.join(tempAgentsBasePath, agentName);
461
+ // Create agent directory
462
+ if (!fs.existsSync(agentDir)) {
463
+ fs.mkdirSync(agentDir, { recursive: true });
464
+ }
465
+ // Create worktrees/clones for each repository
466
+ if (fs.existsSync(reposPath) && workspaceInfo.repositories.length > 0) {
467
+ for (const repo of workspaceInfo.repositories) {
468
+ const sourceRepoPath = path.join(reposPath, repo.name);
469
+ const targetPath = path.join(agentDir, repo.name);
470
+ if (fs.existsSync(sourceRepoPath) && !fs.existsSync(targetPath)) {
471
+ if (mountMode === 'clone') {
472
+ // CLONE MODE: Create independent git clone
473
+ try {
474
+ const remoteUrl = execSync('git remote get-url origin', {
475
+ cwd: sourceRepoPath,
476
+ encoding: 'utf-8',
477
+ stdio: ['pipe', 'pipe', 'pipe']
478
+ }).trim();
479
+ if (remoteUrl) {
480
+ execSync(`git clone "${remoteUrl}" "${targetPath}"`, {
481
+ stdio: 'pipe'
482
+ });
483
+ }
484
+ }
485
+ catch {
486
+ if (!fs.existsSync(targetPath)) {
487
+ fs.mkdirSync(targetPath, { recursive: true });
488
+ }
489
+ }
490
+ }
491
+ else {
492
+ // WORKTREE MODE: Create git worktree
493
+ try {
494
+ execSync(`git worktree add --detach "${targetPath}"`, {
495
+ cwd: sourceRepoPath,
475
496
  stdio: 'pipe'
476
497
  });
477
498
  }
478
- }
479
- catch {
480
- // If clone fails, try to just create the directory
481
- if (!fs.existsSync(targetPath)) {
482
- fs.mkdirSync(targetPath, { recursive: true });
499
+ catch {
500
+ if (!fs.existsSync(targetPath)) {
501
+ fs.mkdirSync(targetPath, { recursive: true });
502
+ }
483
503
  }
484
504
  }
485
505
  }
486
- else {
487
- // WORKTREE MODE: Create git worktree
506
+ }
507
+ }
508
+ // Create devcontainer config if not skipped (uses shared devcontainer generator)
509
+ if (!options?.skipDevcontainer) {
510
+ const devcontainerDir = path.join(agentDir, '.devcontainer');
511
+ if (!fs.existsSync(devcontainerDir)) {
512
+ const gitIdentity = getGitIdentity();
513
+ createDevcontainerConfig({
514
+ agentName,
515
+ agentDir,
516
+ repoWorktrees: mountMode === 'worktree' ? workspaceInfo.repositories.map(r => r.name) : undefined,
517
+ mountMode,
518
+ gitUserName: gitIdentity.name || undefined,
519
+ gitUserEmail: gitIdentity.email || undefined,
520
+ });
521
+ }
522
+ }
523
+ // Attempt atomic DB insertion — returns null on name collision
524
+ const agent = tryAddEphemeralAgentToDatabase(workspaceInfo.path, agentName, baseName, options?.themeId, mountMode);
525
+ if (agent) {
526
+ return {
527
+ name: agentName,
528
+ baseName,
529
+ worktreePath: agentDir,
530
+ agent
531
+ };
532
+ }
533
+ // Name collision at DB level — clean up on-disk artifacts and retry
534
+ log?.(`⚠️ Name collision for "${agentName}" (attempt ${attempt + 1}/${EPHEMERAL_CREATE_MAX_RETRIES + 1}), retrying with a new name...`);
535
+ cleanupFailedEphemeralAgent(agentDir, workspaceInfo, mountMode);
536
+ }
537
+ // All retries exhausted
538
+ throw new Error(`Failed to create ephemeral agent after ${EPHEMERAL_CREATE_MAX_RETRIES + 1} attempts due to concurrent name collisions. ` +
539
+ `This can happen when many ephemeral agents are being created simultaneously. ` +
540
+ `Suggested remediation: wait a moment and retry, or specify a unique agent name with --agent.`);
541
+ }
542
+ /**
543
+ * Clean up on-disk artifacts (directories, worktrees) for a failed ephemeral
544
+ * agent creation attempt. Used when a DB name collision forces a retry.
545
+ */
546
+ function cleanupFailedEphemeralAgent(agentDir, workspaceInfo, mountMode) {
547
+ if (!fs.existsSync(agentDir))
548
+ return;
549
+ // Remove git worktrees first (if in worktree mode)
550
+ if (mountMode === 'worktree') {
551
+ const reposPath = path.join(workspaceInfo.path, 'repos');
552
+ if (fs.existsSync(reposPath)) {
553
+ for (const repo of workspaceInfo.repositories) {
554
+ const sourceRepoPath = path.join(reposPath, repo.name);
555
+ const worktreePath = path.join(agentDir, repo.name);
556
+ if (fs.existsSync(sourceRepoPath) && fs.existsSync(worktreePath)) {
488
557
  try {
489
- // Create git worktree for the repository
490
- // Don't create a branch yet - that happens in work:start
491
- // Use --detach to create without a branch reference
492
- execSync(`git worktree add --detach "${targetPath}"`, {
558
+ execSync(`git worktree remove "${worktreePath}" --force`, {
493
559
  cwd: sourceRepoPath,
494
560
  stdio: 'pipe'
495
561
  });
496
562
  }
497
563
  catch {
498
- // If worktree creation fails, try to just create the directory
499
- // The agent can still work without a worktree (e.g., for non-git projects)
500
- if (!fs.existsSync(targetPath)) {
501
- fs.mkdirSync(targetPath, { recursive: true });
502
- }
564
+ // Ignore rmSync below will clean up the directory
503
565
  }
504
566
  }
505
567
  }
506
568
  }
507
569
  }
508
- // Create devcontainer config if not skipped (uses shared devcontainer generator)
509
- if (!options?.skipDevcontainer) {
510
- const devcontainerDir = path.join(agentDir, '.devcontainer');
511
- if (!fs.existsSync(devcontainerDir)) {
512
- const gitIdentity = getGitIdentity();
513
- createDevcontainerConfig({
514
- agentName,
515
- agentDir,
516
- repoWorktrees: mountMode === 'worktree' ? workspaceInfo.repositories.map(r => r.name) : undefined,
517
- mountMode,
518
- gitUserName: gitIdentity.name || undefined,
519
- gitUserEmail: gitIdentity.email || undefined,
520
- });
521
- }
570
+ // Remove the agent directory
571
+ try {
572
+ fs.rmSync(agentDir, { recursive: true, force: true });
573
+ }
574
+ catch {
575
+ // Best-effort cleanup
522
576
  }
523
- // Add to database
524
- const agent = addEphemeralAgentToDatabase(workspaceInfo.path, agentName, baseName, options?.themeId, mountMode);
525
- return {
526
- name: agentName,
527
- baseName,
528
- worktreePath: agentDir,
529
- agent
530
- };
531
577
  }
532
578
  /**
533
579
  * Check if a tmux session exists for a given name
@@ -84,6 +84,7 @@ export interface BranchInfo {
84
84
  agent?: string;
85
85
  description?: string;
86
86
  tracking?: string;
87
+ repo?: string;
87
88
  }
88
89
  /**
89
90
  * Get current branch name.