vibeman 0.0.3 → 0.0.6

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 (231) hide show
  1. package/dist/api.js +49 -0
  2. package/dist/cli.js +135 -0
  3. package/dist/ui/index-gnk6rhxs.js +9 -0
  4. package/dist/ui/index.html +10 -0
  5. package/dist/ui/index.js +2 -0
  6. package/package.json +10 -80
  7. package/README.md +0 -12
  8. package/dist/index.js +0 -114
  9. package/dist/runtime/api/.tsbuildinfo +0 -1
  10. package/dist/runtime/api/agent/agent-service.d.ts +0 -225
  11. package/dist/runtime/api/agent/agent-service.js +0 -904
  12. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +0 -61
  13. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +0 -362
  14. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +0 -36
  15. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +0 -347
  16. package/dist/runtime/api/agent/ai-providers/index.d.ts +0 -9
  17. package/dist/runtime/api/agent/ai-providers/index.js +0 -7
  18. package/dist/runtime/api/agent/ai-providers/types.d.ts +0 -182
  19. package/dist/runtime/api/agent/ai-providers/types.js +0 -5
  20. package/dist/runtime/api/agent/codex-cli-provider.test.d.ts +0 -1
  21. package/dist/runtime/api/agent/codex-cli-provider.test.js +0 -170
  22. package/dist/runtime/api/agent/core-agent-service.d.ts +0 -119
  23. package/dist/runtime/api/agent/core-agent-service.js +0 -267
  24. package/dist/runtime/api/agent/parsers.d.ts +0 -16
  25. package/dist/runtime/api/agent/parsers.js +0 -308
  26. package/dist/runtime/api/agent/prompt-service.d.ts +0 -30
  27. package/dist/runtime/api/agent/prompt-service.js +0 -449
  28. package/dist/runtime/api/agent/prompt-service.test.d.ts +0 -1
  29. package/dist/runtime/api/agent/prompt-service.test.js +0 -230
  30. package/dist/runtime/api/agent/routing-policy.d.ts +0 -171
  31. package/dist/runtime/api/agent/routing-policy.js +0 -196
  32. package/dist/runtime/api/agent/routing-policy.test.d.ts +0 -1
  33. package/dist/runtime/api/agent/routing-policy.test.js +0 -63
  34. package/dist/runtime/api/api/router-helpers.d.ts +0 -32
  35. package/dist/runtime/api/api/router-helpers.js +0 -31
  36. package/dist/runtime/api/api/routers/ai.d.ts +0 -200
  37. package/dist/runtime/api/api/routers/ai.js +0 -396
  38. package/dist/runtime/api/api/routers/executions.d.ts +0 -98
  39. package/dist/runtime/api/api/routers/executions.js +0 -94
  40. package/dist/runtime/api/api/routers/git.d.ts +0 -45
  41. package/dist/runtime/api/api/routers/git.js +0 -35
  42. package/dist/runtime/api/api/routers/provider-config.d.ts +0 -165
  43. package/dist/runtime/api/api/routers/provider-config.js +0 -252
  44. package/dist/runtime/api/api/routers/settings.d.ts +0 -139
  45. package/dist/runtime/api/api/routers/settings.js +0 -113
  46. package/dist/runtime/api/api/routers/tasks.d.ts +0 -141
  47. package/dist/runtime/api/api/routers/tasks.js +0 -238
  48. package/dist/runtime/api/api/routers/workflows.d.ts +0 -275
  49. package/dist/runtime/api/api/routers/workflows.js +0 -311
  50. package/dist/runtime/api/api/routers/worktrees.d.ts +0 -101
  51. package/dist/runtime/api/api/routers/worktrees.js +0 -80
  52. package/dist/runtime/api/api/trpc.d.ts +0 -118
  53. package/dist/runtime/api/api/trpc.js +0 -34
  54. package/dist/runtime/api/index.d.ts +0 -9
  55. package/dist/runtime/api/index.js +0 -117
  56. package/dist/runtime/api/lib/id-generator.d.ts +0 -70
  57. package/dist/runtime/api/lib/id-generator.js +0 -123
  58. package/dist/runtime/api/lib/local-config.d.ts +0 -245
  59. package/dist/runtime/api/lib/local-config.js +0 -288
  60. package/dist/runtime/api/lib/logger.d.ts +0 -11
  61. package/dist/runtime/api/lib/logger.js +0 -188
  62. package/dist/runtime/api/lib/provider-detection.d.ts +0 -59
  63. package/dist/runtime/api/lib/provider-detection.js +0 -244
  64. package/dist/runtime/api/lib/server/agent-service-singleton.d.ts +0 -6
  65. package/dist/runtime/api/lib/server/agent-service-singleton.js +0 -27
  66. package/dist/runtime/api/lib/server/bootstrap.d.ts +0 -38
  67. package/dist/runtime/api/lib/server/bootstrap.js +0 -197
  68. package/dist/runtime/api/lib/server/git-service-singleton.d.ts +0 -6
  69. package/dist/runtime/api/lib/server/git-service-singleton.js +0 -47
  70. package/dist/runtime/api/lib/server/project-root.d.ts +0 -2
  71. package/dist/runtime/api/lib/server/project-root.js +0 -61
  72. package/dist/runtime/api/lib/server/task-service-singleton.d.ts +0 -7
  73. package/dist/runtime/api/lib/server/task-service-singleton.js +0 -58
  74. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.d.ts +0 -7
  75. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.js +0 -57
  76. package/dist/runtime/api/lib/trpc/client.d.ts +0 -1
  77. package/dist/runtime/api/lib/trpc/client.js +0 -5
  78. package/dist/runtime/api/lib/trpc/server.d.ts +0 -935
  79. package/dist/runtime/api/lib/trpc/server.js +0 -11
  80. package/dist/runtime/api/lib/trpc/ws-server.d.ts +0 -8
  81. package/dist/runtime/api/lib/trpc/ws-server.js +0 -33
  82. package/dist/runtime/api/persistence/database-service.d.ts +0 -14
  83. package/dist/runtime/api/persistence/database-service.js +0 -74
  84. package/dist/runtime/api/persistence/execution-log-persistence.d.ts +0 -90
  85. package/dist/runtime/api/persistence/execution-log-persistence.js +0 -410
  86. package/dist/runtime/api/persistence/execution-log-persistence.test.d.ts +0 -1
  87. package/dist/runtime/api/persistence/execution-log-persistence.test.js +0 -170
  88. package/dist/runtime/api/router.d.ts +0 -938
  89. package/dist/runtime/api/router.js +0 -34
  90. package/dist/runtime/api/settings-service.d.ts +0 -110
  91. package/dist/runtime/api/settings-service.js +0 -661
  92. package/dist/runtime/api/tasks/file-watcher.d.ts +0 -23
  93. package/dist/runtime/api/tasks/file-watcher.js +0 -88
  94. package/dist/runtime/api/tasks/task-file-parser.d.ts +0 -13
  95. package/dist/runtime/api/tasks/task-file-parser.js +0 -161
  96. package/dist/runtime/api/tasks/task-service.d.ts +0 -36
  97. package/dist/runtime/api/tasks/task-service.js +0 -173
  98. package/dist/runtime/api/types/index.d.ts +0 -186
  99. package/dist/runtime/api/types/index.js +0 -1
  100. package/dist/runtime/api/types/settings.d.ts +0 -94
  101. package/dist/runtime/api/types/settings.js +0 -2
  102. package/dist/runtime/api/types.d.ts +0 -2
  103. package/dist/runtime/api/types.js +0 -1
  104. package/dist/runtime/api/utils/env.d.ts +0 -6
  105. package/dist/runtime/api/utils/env.js +0 -12
  106. package/dist/runtime/api/utils/stripNextEnv.d.ts +0 -7
  107. package/dist/runtime/api/utils/stripNextEnv.js +0 -22
  108. package/dist/runtime/api/utils/title-slug.d.ts +0 -6
  109. package/dist/runtime/api/utils/title-slug.js +0 -77
  110. package/dist/runtime/api/utils/url.d.ts +0 -2
  111. package/dist/runtime/api/utils/url.js +0 -19
  112. package/dist/runtime/api/vcs/git-history-service.d.ts +0 -57
  113. package/dist/runtime/api/vcs/git-history-service.js +0 -228
  114. package/dist/runtime/api/vcs/git-service.d.ts +0 -127
  115. package/dist/runtime/api/vcs/git-service.js +0 -284
  116. package/dist/runtime/api/vcs/worktree-service.d.ts +0 -93
  117. package/dist/runtime/api/vcs/worktree-service.js +0 -506
  118. package/dist/runtime/api/vcs/worktree-service.test.d.ts +0 -1
  119. package/dist/runtime/api/vcs/worktree-service.test.js +0 -20
  120. package/dist/runtime/api/workflows/quality-pipeline.d.ts +0 -58
  121. package/dist/runtime/api/workflows/quality-pipeline.js +0 -400
  122. package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +0 -318
  123. package/dist/runtime/api/workflows/vibing-orchestrator.js +0 -1891
  124. package/dist/runtime/web/.next/BUILD_ID +0 -1
  125. package/dist/runtime/web/.next/app-build-manifest.json +0 -66
  126. package/dist/runtime/web/.next/app-path-routes-manifest.json +0 -8
  127. package/dist/runtime/web/.next/build-manifest.json +0 -33
  128. package/dist/runtime/web/.next/package.json +0 -1
  129. package/dist/runtime/web/.next/prerender-manifest.json +0 -61
  130. package/dist/runtime/web/.next/react-loadable-manifest.json +0 -39
  131. package/dist/runtime/web/.next/required-server-files.json +0 -334
  132. package/dist/runtime/web/.next/routes-manifest.json +0 -70
  133. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +0 -1
  134. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +0 -1
  135. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +0 -1
  136. package/dist/runtime/web/.next/server/app/_not-found/page.js +0 -2
  137. package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +0 -1
  138. package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  139. package/dist/runtime/web/.next/server/app/_not-found.html +0 -7
  140. package/dist/runtime/web/.next/server/app/_not-found.meta +0 -8
  141. package/dist/runtime/web/.next/server/app/_not-found.rsc +0 -22
  142. package/dist/runtime/web/.next/server/app/api/health/route.js +0 -1
  143. package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +0 -1
  144. package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +0 -1
  145. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +0 -1
  146. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +0 -1
  147. package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +0 -1
  148. package/dist/runtime/web/.next/server/app/api/upload/route.js +0 -1
  149. package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +0 -1
  150. package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +0 -1
  151. package/dist/runtime/web/.next/server/app/index.html +0 -7
  152. package/dist/runtime/web/.next/server/app/index.meta +0 -7
  153. package/dist/runtime/web/.next/server/app/index.rsc +0 -27
  154. package/dist/runtime/web/.next/server/app/page.js +0 -147
  155. package/dist/runtime/web/.next/server/app/page.js.nft.json +0 -1
  156. package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +0 -1
  157. package/dist/runtime/web/.next/server/app-paths-manifest.json +0 -8
  158. package/dist/runtime/web/.next/server/chunks/217.js +0 -1
  159. package/dist/runtime/web/.next/server/chunks/383.js +0 -6
  160. package/dist/runtime/web/.next/server/chunks/458.js +0 -1
  161. package/dist/runtime/web/.next/server/chunks/576.js +0 -18
  162. package/dist/runtime/web/.next/server/chunks/635.js +0 -22
  163. package/dist/runtime/web/.next/server/chunks/761.js +0 -1
  164. package/dist/runtime/web/.next/server/chunks/777.js +0 -3
  165. package/dist/runtime/web/.next/server/chunks/825.js +0 -1
  166. package/dist/runtime/web/.next/server/chunks/838.js +0 -1
  167. package/dist/runtime/web/.next/server/chunks/973.js +0 -15
  168. package/dist/runtime/web/.next/server/functions-config-manifest.json +0 -4
  169. package/dist/runtime/web/.next/server/middleware-build-manifest.js +0 -1
  170. package/dist/runtime/web/.next/server/middleware-manifest.json +0 -6
  171. package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +0 -1
  172. package/dist/runtime/web/.next/server/next-font-manifest.js +0 -1
  173. package/dist/runtime/web/.next/server/next-font-manifest.json +0 -1
  174. package/dist/runtime/web/.next/server/pages/404.html +0 -7
  175. package/dist/runtime/web/.next/server/pages/500.html +0 -1
  176. package/dist/runtime/web/.next/server/pages/_app.js +0 -1
  177. package/dist/runtime/web/.next/server/pages/_app.js.nft.json +0 -1
  178. package/dist/runtime/web/.next/server/pages/_document.js +0 -1
  179. package/dist/runtime/web/.next/server/pages/_document.js.nft.json +0 -1
  180. package/dist/runtime/web/.next/server/pages/_error.js +0 -19
  181. package/dist/runtime/web/.next/server/pages/_error.js.nft.json +0 -1
  182. package/dist/runtime/web/.next/server/pages-manifest.json +0 -6
  183. package/dist/runtime/web/.next/server/server-reference-manifest.js +0 -1
  184. package/dist/runtime/web/.next/server/server-reference-manifest.json +0 -1
  185. package/dist/runtime/web/.next/server/webpack-runtime.js +0 -1
  186. package/dist/runtime/web/.next/static/5_15u1WQCxN1_eHZpldCv/_buildManifest.js +0 -1
  187. package/dist/runtime/web/.next/static/5_15u1WQCxN1_eHZpldCv/_ssgManifest.js +0 -1
  188. package/dist/runtime/web/.next/static/chunks/18-15c10d3288afef2e.js +0 -1
  189. package/dist/runtime/web/.next/static/chunks/1c0ca389.537bbe362e3ffbd9.js +0 -3
  190. package/dist/runtime/web/.next/static/chunks/22747d63-ad5da0c19f4cfe41.js +0 -71
  191. package/dist/runtime/web/.next/static/chunks/355.056c2645878a799a.js +0 -1
  192. package/dist/runtime/web/.next/static/chunks/420.a5ccf151c9e2b2f1.js +0 -1
  193. package/dist/runtime/web/.next/static/chunks/439.1be0c6242fd248d5.js +0 -15
  194. package/dist/runtime/web/.next/static/chunks/440.c52e7c0f797e22b2.js +0 -1
  195. package/dist/runtime/web/.next/static/chunks/575-e2478287c27da87b.js +0 -1
  196. package/dist/runtime/web/.next/static/chunks/691.920d88c115087314.js +0 -1
  197. package/dist/runtime/web/.next/static/chunks/765-e838910065b50c3d.js +0 -1
  198. package/dist/runtime/web/.next/static/chunks/823-6f371a6e829adbba.js +0 -63
  199. package/dist/runtime/web/.next/static/chunks/87c73c54-09e1ba5c70e60a51.js +0 -1
  200. package/dist/runtime/web/.next/static/chunks/891cff7f.0f71fc028f87e683.js +0 -1
  201. package/dist/runtime/web/.next/static/chunks/8bb4d8db-3e2aa02b0a2384b9.js +0 -1
  202. package/dist/runtime/web/.next/static/chunks/9af238c7-271a911d4e99ab18.js +0 -1
  203. package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-751c9265a65409e5.js +0 -1
  204. package/dist/runtime/web/.next/static/chunks/app/_not-found/page-1cb74d1cba27d0ab.js +0 -1
  205. package/dist/runtime/web/.next/static/chunks/app/api/health/route-751c9265a65409e5.js +0 -1
  206. package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-751c9265a65409e5.js +0 -1
  207. package/dist/runtime/web/.next/static/chunks/app/api/upload/route-751c9265a65409e5.js +0 -1
  208. package/dist/runtime/web/.next/static/chunks/app/layout-8435322f09fd0975.js +0 -1
  209. package/dist/runtime/web/.next/static/chunks/app/page-9fe7d75095b4ccec.js +0 -1
  210. package/dist/runtime/web/.next/static/chunks/cac567b0-5b77dd12911823cd.js +0 -1
  211. package/dist/runtime/web/.next/static/chunks/framework-2518f1345b5b2806.js +0 -1
  212. package/dist/runtime/web/.next/static/chunks/main-17665e5e39de9a8a.js +0 -1
  213. package/dist/runtime/web/.next/static/chunks/main-app-c0b0f5ba4f7f9d75.js +0 -1
  214. package/dist/runtime/web/.next/static/chunks/pages/_app-d6f6b3bbc3d81ee1.js +0 -1
  215. package/dist/runtime/web/.next/static/chunks/pages/_error-75a96cf1997cc3b9.js +0 -1
  216. package/dist/runtime/web/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  217. package/dist/runtime/web/.next/static/chunks/webpack-c8de37305b4635cf.js +0 -1
  218. package/dist/runtime/web/.next/static/css/08c950681f1a9a92.css +0 -1
  219. package/dist/runtime/web/.next/static/css/2728291c68f99cb1.css +0 -3
  220. package/dist/runtime/web/.next/static/css/521bd69cc298cd1a.css +0 -1
  221. package/dist/runtime/web/.next/static/css/537e22821e101b87.css +0 -1
  222. package/dist/runtime/web/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  223. package/dist/runtime/web/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  224. package/dist/runtime/web/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  225. package/dist/runtime/web/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  226. package/dist/runtime/web/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  227. package/dist/runtime/web/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  228. package/dist/runtime/web/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  229. package/dist/runtime/web/package.json +0 -65
  230. package/dist/runtime/web/server.js +0 -44
  231. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,506 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { GitService } from './git-service.js';
3
- import { log } from '../lib/logger.js';
4
- import { getSettingsService } from '../settings-service.js';
5
- import { getProjectRoot } from '../lib/server/project-root.js';
6
- import path from 'path';
7
- import fs from 'fs/promises';
8
- export class WorktreeService extends EventEmitter {
9
- constructor(projectRoot = getProjectRoot(), gitService) {
10
- super();
11
- this.worktrees = new Map();
12
- this.inFlightCreates = new Map();
13
- this.initialized = false;
14
- this.initPromise = null;
15
- this.projectRoot = projectRoot;
16
- this.gitService = gitService || new GitService(projectRoot);
17
- // Load configuration from settings, with fallback to environment then default
18
- const settingsWorktreePath = (() => {
19
- try {
20
- const svc = getSettingsService();
21
- const s = svc.getSettings();
22
- return s?.development?.git?.worktreePath;
23
- }
24
- catch {
25
- return undefined;
26
- }
27
- })();
28
- this.config = {
29
- // Default to parent directory of the repo
30
- worktreePath: settingsWorktreePath || '../',
31
- };
32
- }
33
- /**
34
- * Initialize the worktree service
35
- */
36
- async initialize() {
37
- if (this.initialized)
38
- return;
39
- if (this.initPromise)
40
- return this.initPromise;
41
- this.initPromise = (async () => {
42
- try {
43
- // Check if project root exists and is a Git repository
44
- await fs.access(this.projectRoot);
45
- // Ensure Git is available and version is compatible
46
- await this.validateGitVersion();
47
- // Ensure recovery of any existing worktrees
48
- await this.recoverExistingWorktrees();
49
- this.initialized = true;
50
- log.debug('WorktreeService initialized', undefined, 'worktree-service');
51
- }
52
- catch (error) {
53
- log.error('WorktreeService initialization failed, disabling worktrees', error, 'worktree-service');
54
- }
55
- })();
56
- try {
57
- await this.initPromise;
58
- }
59
- finally {
60
- this.initPromise = null;
61
- }
62
- }
63
- /**
64
- * Recover existing worktrees after restart
65
- */
66
- async recoverExistingWorktrees() {
67
- try {
68
- const gitWorktrees = await this.gitService.listWorktrees();
69
- const validTaskIds = new Set();
70
- let sawPrunable = false;
71
- // First, recover valid worktrees from Git
72
- const baseDir = path.resolve(this.projectRoot, this.config.worktreePath);
73
- for (const worktree of gitWorktrees) {
74
- if (worktree.prunable) {
75
- sawPrunable = true;
76
- continue;
77
- }
78
- // Only consider worktrees under the configured base directory
79
- const wtPath = path.resolve(worktree.path);
80
- if (wtPath.startsWith(baseDir + path.sep) || wtPath === baseDir) {
81
- const taskId = this.extractTaskIdFromPath(worktree.path);
82
- if (taskId) {
83
- try {
84
- // Ensure the path exists before creating a git instance
85
- await fs.stat(worktree.path);
86
- const worktreeInfo = {
87
- taskId,
88
- path: worktree.path,
89
- branchName: worktree.branch,
90
- git: new GitService(worktree.path),
91
- };
92
- this.worktrees.set(taskId, worktreeInfo);
93
- validTaskIds.add(taskId);
94
- log.debug('Recovered worktree for task', { taskId }, 'worktree-service');
95
- }
96
- catch {
97
- // Path no longer exists; mark for prune
98
- sawPrunable = true;
99
- }
100
- }
101
- }
102
- }
103
- if (sawPrunable) {
104
- try {
105
- await this.gitService.pruneWorktrees();
106
- log.info('Pruned stale worktree entries during recovery', undefined, 'worktree-service');
107
- }
108
- catch (e) {
109
- log.error('Failed to prune stale worktrees during recovery', e, 'worktree-service');
110
- }
111
- }
112
- // Remove any in-memory worktrees that no longer exist in Git
113
- const inMemoryTaskIds = Array.from(this.worktrees.keys());
114
- for (const taskId of inMemoryTaskIds) {
115
- if (!validTaskIds.has(taskId)) {
116
- this.worktrees.delete(taskId);
117
- log.info('Removed stale worktree reference', { taskId }, 'worktree-service');
118
- }
119
- }
120
- }
121
- catch (error) {
122
- log.error('Could not recover existing worktrees', error, 'worktree-service');
123
- }
124
- }
125
- /**
126
- * Extract task ID from worktree path
127
- * TODO: need to improve this to be more robust
128
- */
129
- extractTaskIdFromPath(worktreePath) {
130
- const basename = path.basename(worktreePath);
131
- // Worktree directory format: {taskId}
132
- // Accept IDs like FEATURE-001, FEAT-ABC12, BUG-4XYZ9, FEAT-EDITOR-EXP
133
- // Previously this only allowed a single hyphen; expand to multiple hyphen-separated segments.
134
- return /^[A-Z0-9]+(?:-[A-Z0-9]+)+$/i.test(basename) ? basename : null;
135
- }
136
- /**
137
- * Create a new worktree for a task
138
- */
139
- async createWorktree(task) {
140
- // Check if worktree already exists for this task
141
- if (this.worktrees.has(task.id)) {
142
- const existing = this.worktrees.get(task.id);
143
- log.info('Using existing worktree for task', { taskId: task.id }, 'worktree-service');
144
- return existing;
145
- }
146
- // Deduplicate concurrent create calls per task
147
- const existingPromise = this.inFlightCreates.get(task.id);
148
- if (existingPromise)
149
- return existingPromise;
150
- // Generate branch name and worktree path
151
- const branchName = this.gitService.generateBranchName(task);
152
- const baseDir = path.resolve(this.projectRoot, this.config.worktreePath);
153
- // Ensure base directory exists
154
- await fs.mkdir(baseDir, { recursive: true });
155
- const worktreePath = path.resolve(baseDir, task.id);
156
- const createPromise = (async () => {
157
- try {
158
- // Ensure we're in sync with remote
159
- await this.gitService.syncWithRemote();
160
- // Check existing worktrees and branches to allow reuse on reruns
161
- const gitWorktrees = await this.gitService.listWorktrees();
162
- // Try to find an existing worktree for this branch or path
163
- const existingByBranch = gitWorktrees.find((w) => w.branch === branchName);
164
- const existingByPath = gitWorktrees.find((w) => path.resolve(w.path) === path.resolve(worktreePath));
165
- if (existingByBranch && !existingByBranch.prunable) {
166
- // Reuse existing worktree by branch
167
- const resolvedPath = path.resolve(existingByBranch.path);
168
- await this.symlinkEssentialFiles(resolvedPath);
169
- const worktreeInfo = {
170
- taskId: task.id,
171
- path: resolvedPath,
172
- branchName,
173
- git: new GitService(resolvedPath),
174
- };
175
- this.worktrees.set(task.id, worktreeInfo);
176
- log.info('Reused existing worktree for branch', { taskId: task.id, path: resolvedPath, branchName }, 'worktree-service');
177
- this.emit('worktreeCreated', worktreeInfo);
178
- return worktreeInfo;
179
- }
180
- if (existingByPath && !existingByPath.prunable) {
181
- // Path already registered as a worktree; use it
182
- const resolvedPath = path.resolve(existingByPath.path);
183
- await this.symlinkEssentialFiles(resolvedPath);
184
- const worktreeInfo = {
185
- taskId: task.id,
186
- path: resolvedPath,
187
- branchName: existingByPath.branch || branchName,
188
- git: new GitService(resolvedPath),
189
- };
190
- this.worktrees.set(task.id, worktreeInfo);
191
- log.info('Reused existing worktree by path', { taskId: task.id, path: resolvedPath, branchName: worktreeInfo.branchName }, 'worktree-service');
192
- this.emit('worktreeCreated', worktreeInfo);
193
- return worktreeInfo;
194
- }
195
- // If we found prunable entries for this path or branch, prune before attempting to add
196
- if ((existingByBranch && existingByBranch.prunable) ||
197
- (existingByPath && existingByPath.prunable)) {
198
- try {
199
- await this.gitService.pruneWorktrees();
200
- log.info('Pruned stale worktree entries', { branchName, worktreePath }, 'worktree-service');
201
- }
202
- catch (e) {
203
- log.error('Failed to prune worktrees', e, 'worktree-service');
204
- }
205
- }
206
- // Determine if branch already exists locally
207
- const branchExists = await this.gitService.branchExists(branchName);
208
- // Create the worktree using git service
209
- await this.gitService.createWorktree(worktreePath, branchName, branchExists);
210
- // Copy essential files (like .env) to the worktree
211
- await this.symlinkEssentialFiles(worktreePath);
212
- const worktreeInfo = {
213
- taskId: task.id,
214
- path: worktreePath,
215
- branchName,
216
- git: new GitService(worktreePath),
217
- };
218
- this.worktrees.set(task.id, worktreeInfo);
219
- log.info('Created worktree', { taskId: task.id, path: worktreePath }, 'worktree-service');
220
- this.emit('worktreeCreated', worktreeInfo);
221
- return worktreeInfo;
222
- }
223
- catch (error) {
224
- log.error('Failed to create worktree', error, 'worktree-service');
225
- // Cleanup on failure
226
- try {
227
- await fs.rm(worktreePath, { recursive: true, force: true });
228
- }
229
- catch {
230
- // Ignore cleanup errors
231
- }
232
- throw new Error(`Failed to create worktree: ${error instanceof Error ? error.message : String(error)}`);
233
- }
234
- finally {
235
- // Clear in-flight marker
236
- this.inFlightCreates.delete(task.id);
237
- }
238
- })();
239
- this.inFlightCreates.set(task.id, createPromise);
240
- return createPromise;
241
- }
242
- /**
243
- * Symlink essential files to worktree
244
- */
245
- async symlinkEssentialFiles(worktreePath) {
246
- // Link only .env and .env* files in the project root.
247
- let entries = [];
248
- try {
249
- entries = (await fs.readdir(this.projectRoot)).filter((name) => name.startsWith('.env'));
250
- }
251
- catch {
252
- entries = [];
253
- }
254
- for (const name of entries) {
255
- const sourcePath = path.join(this.projectRoot, name);
256
- const targetPath = path.join(worktreePath, name);
257
- try {
258
- await fs.stat(sourcePath);
259
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
260
- // Ensure idempotency: if target exists and links to source, skip
261
- let shouldCreate = true;
262
- try {
263
- const lst = await fs.lstat(targetPath);
264
- if (lst.isSymbolicLink()) {
265
- const linkTarget = await fs.readlink(targetPath);
266
- const resolved = path.resolve(path.dirname(targetPath), linkTarget);
267
- if (resolved === sourcePath) {
268
- shouldCreate = false;
269
- }
270
- }
271
- }
272
- catch {
273
- // target missing, proceed
274
- }
275
- if (shouldCreate) {
276
- // Remove any existing file/dir and create a symlink
277
- await fs.rm(targetPath, { recursive: true, force: true });
278
- const relative = path.relative(path.dirname(targetPath), sourcePath);
279
- await fs.symlink(relative, targetPath);
280
- log.debug('Symlinked to worktree', { name }, 'worktree-service');
281
- }
282
- }
283
- catch {
284
- // Ignore missing source or failures
285
- }
286
- }
287
- }
288
- /**
289
- * Get worktree info for a task
290
- */
291
- getWorktree(taskId) {
292
- return this.worktrees.get(taskId) || null;
293
- }
294
- /**
295
- * List all Git worktrees with connection status to Vibeman tasks
296
- */
297
- async listWorktrees() {
298
- try {
299
- const gitWorktrees = await this.gitService.listWorktrees();
300
- const result = [];
301
- for (const gitWorktree of gitWorktrees) {
302
- if (gitWorktree.prunable) {
303
- continue; // Skip prunable worktrees
304
- }
305
- // Skip main branch worktree
306
- if (gitWorktree.branch === 'main' || gitWorktree.branch === 'master') {
307
- continue;
308
- }
309
- const taskId = this.extractTaskIdFromPath(gitWorktree.path);
310
- const isConnectedToTask = taskId ? this.worktrees.has(taskId) : false;
311
- result.push({
312
- taskId: taskId || path.basename(gitWorktree.path),
313
- path: gitWorktree.path,
314
- branchName: gitWorktree.branch,
315
- isConnectedToTask,
316
- });
317
- }
318
- return result;
319
- }
320
- catch (error) {
321
- log.error('Failed to list all worktrees', error, 'worktree-service');
322
- throw new Error(`Failed to list all worktrees: ${error instanceof Error ? error.message : String(error)}`);
323
- }
324
- }
325
- /**
326
- * Unified cleanup: remove branch first, then worktree
327
- * For connected worktrees, use taskId; for unconnected, use worktreePath and branchName
328
- */
329
- async cleanupWorktree(params) {
330
- const { taskId, worktreePath, branchName, isConnectedToTask, force = false } = params;
331
- try {
332
- let actualWorktreePath;
333
- let actualBranchName;
334
- if (isConnectedToTask && taskId) {
335
- // For connected worktrees, get info from tracked worktrees
336
- const worktree = this.worktrees.get(taskId);
337
- if (!worktree) {
338
- throw new Error(`Worktree for task ${taskId} not found`);
339
- }
340
- actualWorktreePath = worktree.path;
341
- actualBranchName = worktree.branchName;
342
- // Check for uncommitted changes unless forced
343
- if (!force) {
344
- const hasChanges = await this.gitService.hasUncommittedChanges(actualWorktreePath);
345
- if (hasChanges) {
346
- throw new Error(`Worktree has uncommitted changes. Use force=true to delete anyway.`);
347
- }
348
- }
349
- }
350
- else {
351
- // For unconnected worktrees, use provided paths
352
- if (!worktreePath || !branchName) {
353
- throw new Error('worktreePath and branchName are required for unconnected worktrees');
354
- }
355
- actualWorktreePath = worktreePath;
356
- actualBranchName = branchName;
357
- }
358
- // Step 1: Try to remove the branch first (if it's not main/master)
359
- if (actualBranchName !== 'main' && actualBranchName !== 'master') {
360
- try {
361
- await this.gitService.deleteLocalBranch(actualBranchName, true);
362
- log.info('Deleted branch', { branchName: actualBranchName }, 'worktree-service');
363
- }
364
- catch (error) {
365
- // Log but continue - branch might not exist or already deleted
366
- log.warn('Could not delete branch, continuing with worktree removal', {
367
- branchName: actualBranchName,
368
- error: error instanceof Error ? error.message : String(error),
369
- }, 'worktree-service');
370
- }
371
- }
372
- // Step 2: Remove the worktree
373
- await this.gitService.removeWorktree(actualWorktreePath, force);
374
- // Step 3: Clean up tracking if it was a connected worktree
375
- if (isConnectedToTask && taskId) {
376
- this.worktrees.delete(taskId);
377
- this.emit('worktreeDeleted', taskId);
378
- }
379
- log.info('Successfully cleaned up worktree', {
380
- taskId,
381
- worktreePath: actualWorktreePath,
382
- branchName: actualBranchName,
383
- isConnectedToTask,
384
- }, 'worktree-service');
385
- }
386
- catch (error) {
387
- log.error('Failed to cleanup worktree', error, 'worktree-service');
388
- throw new Error(`Failed to cleanup worktree: ${error instanceof Error ? error.message : String(error)}`);
389
- }
390
- }
391
- /**
392
- * Delete a worktree
393
- */
394
- async deleteWorktree(taskId, force = false) {
395
- const worktree = this.worktrees.get(taskId);
396
- if (!worktree) {
397
- throw new Error(`Worktree for task ${taskId} not found`);
398
- }
399
- try {
400
- // Check for uncommitted changes unless forced
401
- if (!force) {
402
- const hasChanges = await this.gitService.hasUncommittedChanges(worktree.path);
403
- if (hasChanges) {
404
- throw new Error(`Worktree has uncommitted changes. Use force=true to delete anyway.`);
405
- }
406
- }
407
- // Remove worktree using git service
408
- await this.gitService.removeWorktree(worktree.path, force);
409
- // Delete local branch if it exists and is not merged
410
- try {
411
- await this.gitService.deleteLocalBranch(worktree.branchName, force);
412
- log.info('Deleted branch', { branchName: worktree.branchName }, 'worktree-service');
413
- }
414
- catch (error) {
415
- log.warn('Could not delete branch', error, 'worktree-service');
416
- }
417
- // Remove from tracking
418
- this.worktrees.delete(taskId);
419
- log.info('Deleted worktree', { taskId }, 'worktree-service');
420
- this.emit('worktreeDeleted', { taskId, worktree });
421
- }
422
- catch (error) {
423
- log.error('Failed to delete worktree', error, 'worktree-service');
424
- throw new Error(`Failed to delete worktree: ${error instanceof Error ? error.message : String(error)}`);
425
- }
426
- }
427
- /**
428
- * Create pull request from worktree branch
429
- */
430
- async createPullRequest(taskId, _baseBranch = 'main') {
431
- const worktree = this.worktrees.get(taskId);
432
- if (!worktree) {
433
- return {
434
- success: false,
435
- error: `Worktree for task ${taskId} not found`,
436
- };
437
- }
438
- try {
439
- // Check if there are any commits in the worktree branch
440
- const gitLog = await this.gitService.getLog(worktree.path, ['-n', '1']);
441
- if (!gitLog.latest) {
442
- return {
443
- success: false,
444
- error: 'No commits found in worktree branch',
445
- };
446
- }
447
- // Push the branch to remote
448
- await this.gitService.pushFromDirectory(worktree.path, 'origin', worktree.branchName);
449
- log.info('Pushed branch for task - manual PR creation required', { taskId, branchName: worktree.branchName }, 'worktree-service');
450
- return {
451
- success: true,
452
- error: 'Branch pushed successfully. GitHub integration removed - please create PR manually.',
453
- };
454
- }
455
- catch (error) {
456
- log.error('Failed to push branch', error, 'worktree-service');
457
- return {
458
- success: false,
459
- error: error instanceof Error ? error.message : String(error),
460
- };
461
- }
462
- }
463
- /**
464
- * Sync worktree state with Git (public method for manual refresh)
465
- */
466
- async syncWorktreeState() {
467
- await this.recoverExistingWorktrees();
468
- log.info('Worktree state synchronized', undefined, 'worktree-service');
469
- }
470
- /**
471
- * Validate Git version compatibility for worktrees
472
- */
473
- async validateGitVersion() {
474
- try {
475
- const version = await this.gitService.getGitVersion();
476
- const versionNumber = parseFloat(String(version.major) + '.' + String(version.minor));
477
- if (versionNumber < 2.15) {
478
- log.warn('Git version not optimal for worktrees', { version: version.installed, recommended: '2.15+' }, 'worktree-service');
479
- }
480
- }
481
- catch {
482
- throw new Error('Git is not available. Please install Git 2.15+ to use worktree functionality.');
483
- }
484
- }
485
- /**
486
- * Get worktree counts (synchronous, from memory)
487
- */
488
- getWorktreeCounts() {
489
- return {
490
- active: this.worktrees.size,
491
- total: this.worktrees.size,
492
- };
493
- }
494
- /**
495
- * Get current configuration
496
- */
497
- getConfig() {
498
- return { ...this.config };
499
- }
500
- /**
501
- * Get GitService instance for advanced operations
502
- */
503
- getGitService() {
504
- return this.gitService;
505
- }
506
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,20 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { WorktreeService } from './worktree-service.js';
3
- describe('WorktreeService.extractTaskIdFromPath', () => {
4
- it('accepts simple hyphenated IDs', () => {
5
- const svc = new WorktreeService(process.cwd());
6
- const fn = svc.extractTaskIdFromPath.bind(svc);
7
- expect(fn('/tmp/FEAT-DFMXM')).toBe('FEAT-DFMXM');
8
- });
9
- it('accepts multi-hyphen IDs like FEAT-EDITOR-EXP', () => {
10
- const svc = new WorktreeService(process.cwd());
11
- const fn = svc.extractTaskIdFromPath.bind(svc);
12
- expect(fn('/any/path/FEAT-EDITOR-EXP')).toBe('FEAT-EDITOR-EXP');
13
- });
14
- it('rejects clearly non-matching names', () => {
15
- const svc = new WorktreeService(process.cwd());
16
- const fn = svc.extractTaskIdFromPath.bind(svc);
17
- expect(fn('/tmp/feat_editor_exp')).toBeNull();
18
- expect(fn('/tmp/feat editor exp')).toBeNull();
19
- });
20
- });
@@ -1,58 +0,0 @@
1
- import { QualityResults } from '../types/index.js';
2
- interface QualityCheck {
3
- name: string;
4
- command: string;
5
- args: string[];
6
- timeout: number;
7
- }
8
- export declare class QualityPipeline {
9
- getChecksFor(): Promise<QualityCheck[]>;
10
- private getChecks;
11
- private detectQualityChecks;
12
- private aiDetectQualityChecks;
13
- private getDefaultProviderName;
14
- /**
15
- * Public: Detect quality checks (AI-only) and persist to settings if found.
16
- * Does not execute any checks.
17
- */
18
- detectAndPersistChecks(workingDirectory: string): Promise<Array<{
19
- name: string;
20
- command: string;
21
- args: string[];
22
- timeout: number;
23
- enabled: boolean;
24
- }>>;
25
- /**
26
- * Run all quality checks in the specified directory
27
- */
28
- runChecks(workingDirectory: string): Promise<QualityResults>;
29
- /**
30
- * Run checks with streaming logs via callback.
31
- */
32
- runChecksStreaming(workingDirectory: string, onLog?: (line: string) => void | Promise<void>): Promise<QualityResults>;
33
- /**
34
- * Run a single quality check
35
- */
36
- private runCheck;
37
- /**
38
- * Run a command and return its output
39
- */
40
- private runCommand;
41
- private runCommandStreaming;
42
- private isMissingCommandError;
43
- /**
44
- * Run individual quality check by name
45
- */
46
- runSingleCheck(checkName: string, workingDirectory: string): Promise<{
47
- passed: boolean;
48
- output: string;
49
- }>;
50
- /**
51
- * Get quality pipeline configuration
52
- */
53
- getConfig(): {
54
- checks: never[];
55
- parallel: boolean;
56
- };
57
- }
58
- export {};