vibeman 0.0.0 → 0.0.2

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 (230) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +114 -0
  3. package/dist/runtime/api/.tsbuildinfo +1 -0
  4. package/dist/runtime/api/agent/agent-service.d.ts +224 -0
  5. package/dist/runtime/api/agent/agent-service.js +895 -0
  6. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +61 -0
  7. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +362 -0
  8. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +34 -0
  9. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +315 -0
  10. package/dist/runtime/api/agent/ai-providers/index.d.ts +9 -0
  11. package/dist/runtime/api/agent/ai-providers/index.js +7 -0
  12. package/dist/runtime/api/agent/ai-providers/types.d.ts +182 -0
  13. package/dist/runtime/api/agent/ai-providers/types.js +5 -0
  14. package/dist/runtime/api/agent/codex-cli-provider.test.d.ts +1 -0
  15. package/dist/runtime/api/agent/codex-cli-provider.test.js +125 -0
  16. package/dist/runtime/api/agent/core-agent-service.d.ts +119 -0
  17. package/dist/runtime/api/agent/core-agent-service.js +267 -0
  18. package/dist/runtime/api/agent/parsers.d.ts +16 -0
  19. package/dist/runtime/api/agent/parsers.js +308 -0
  20. package/dist/runtime/api/agent/prompt-service.d.ts +30 -0
  21. package/dist/runtime/api/agent/prompt-service.js +449 -0
  22. package/dist/runtime/api/agent/prompt-service.test.d.ts +1 -0
  23. package/dist/runtime/api/agent/prompt-service.test.js +230 -0
  24. package/dist/runtime/api/agent/routing-policy.d.ts +188 -0
  25. package/dist/runtime/api/agent/routing-policy.js +246 -0
  26. package/dist/runtime/api/api/router-helpers.d.ts +32 -0
  27. package/dist/runtime/api/api/router-helpers.js +31 -0
  28. package/dist/runtime/api/api/routers/ai.d.ts +188 -0
  29. package/dist/runtime/api/api/routers/ai.js +395 -0
  30. package/dist/runtime/api/api/routers/executions.d.ts +98 -0
  31. package/dist/runtime/api/api/routers/executions.js +94 -0
  32. package/dist/runtime/api/api/routers/git.d.ts +45 -0
  33. package/dist/runtime/api/api/routers/git.js +35 -0
  34. package/dist/runtime/api/api/routers/provider-config.d.ts +165 -0
  35. package/dist/runtime/api/api/routers/provider-config.js +252 -0
  36. package/dist/runtime/api/api/routers/settings.d.ts +139 -0
  37. package/dist/runtime/api/api/routers/settings.js +113 -0
  38. package/dist/runtime/api/api/routers/tasks.d.ts +141 -0
  39. package/dist/runtime/api/api/routers/tasks.js +238 -0
  40. package/dist/runtime/api/api/routers/workflows.d.ts +267 -0
  41. package/dist/runtime/api/api/routers/workflows.js +310 -0
  42. package/dist/runtime/api/api/routers/worktrees.d.ts +101 -0
  43. package/dist/runtime/api/api/routers/worktrees.js +80 -0
  44. package/dist/runtime/api/api/trpc.d.ts +118 -0
  45. package/dist/runtime/api/api/trpc.js +34 -0
  46. package/dist/runtime/api/index.d.ts +9 -0
  47. package/dist/runtime/api/index.js +117 -0
  48. package/dist/runtime/api/lib/id-generator.d.ts +70 -0
  49. package/dist/runtime/api/lib/id-generator.js +123 -0
  50. package/dist/runtime/api/lib/image-paste-drop-extension.d.ts +26 -0
  51. package/dist/runtime/api/lib/image-paste-drop-extension.js +125 -0
  52. package/dist/runtime/api/lib/local-config.d.ts +245 -0
  53. package/dist/runtime/api/lib/local-config.js +288 -0
  54. package/dist/runtime/api/lib/logger.d.ts +11 -0
  55. package/dist/runtime/api/lib/logger.js +188 -0
  56. package/dist/runtime/api/lib/markdown-utils.d.ts +8 -0
  57. package/dist/runtime/api/lib/markdown-utils.js +282 -0
  58. package/dist/runtime/api/lib/markdown-utils.test.d.ts +1 -0
  59. package/dist/runtime/api/lib/markdown-utils.test.js +348 -0
  60. package/dist/runtime/api/lib/provider-detection.d.ts +59 -0
  61. package/dist/runtime/api/lib/provider-detection.js +244 -0
  62. package/dist/runtime/api/lib/server/agent-service-singleton.d.ts +6 -0
  63. package/dist/runtime/api/lib/server/agent-service-singleton.js +27 -0
  64. package/dist/runtime/api/lib/server/bootstrap.d.ts +38 -0
  65. package/dist/runtime/api/lib/server/bootstrap.js +197 -0
  66. package/dist/runtime/api/lib/server/git-service-singleton.d.ts +6 -0
  67. package/dist/runtime/api/lib/server/git-service-singleton.js +47 -0
  68. package/dist/runtime/api/lib/server/project-root.d.ts +2 -0
  69. package/dist/runtime/api/lib/server/project-root.js +61 -0
  70. package/dist/runtime/api/lib/server/task-service-singleton.d.ts +7 -0
  71. package/dist/runtime/api/lib/server/task-service-singleton.js +58 -0
  72. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.d.ts +7 -0
  73. package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.js +57 -0
  74. package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.d.ts +1 -0
  75. package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.js +27 -0
  76. package/dist/runtime/api/lib/tiptap-utils.d.ts +130 -0
  77. package/dist/runtime/api/lib/tiptap-utils.js +327 -0
  78. package/dist/runtime/api/lib/trpc/client.d.ts +1 -0
  79. package/dist/runtime/api/lib/trpc/client.js +5 -0
  80. package/dist/runtime/api/lib/trpc/server.d.ts +915 -0
  81. package/dist/runtime/api/lib/trpc/server.js +11 -0
  82. package/dist/runtime/api/lib/trpc/ws-server.d.ts +8 -0
  83. package/dist/runtime/api/lib/trpc/ws-server.js +33 -0
  84. package/dist/runtime/api/persistence/database-service.d.ts +14 -0
  85. package/dist/runtime/api/persistence/database-service.js +74 -0
  86. package/dist/runtime/api/persistence/execution-log-persistence.d.ts +90 -0
  87. package/dist/runtime/api/persistence/execution-log-persistence.js +410 -0
  88. package/dist/runtime/api/persistence/execution-log-persistence.test.d.ts +1 -0
  89. package/dist/runtime/api/persistence/execution-log-persistence.test.js +170 -0
  90. package/dist/runtime/api/router.d.ts +918 -0
  91. package/dist/runtime/api/router.js +34 -0
  92. package/dist/runtime/api/settings-service.d.ts +110 -0
  93. package/dist/runtime/api/settings-service.js +613 -0
  94. package/dist/runtime/api/tasks/file-watcher.d.ts +23 -0
  95. package/dist/runtime/api/tasks/file-watcher.js +88 -0
  96. package/dist/runtime/api/tasks/task-file-parser.d.ts +13 -0
  97. package/dist/runtime/api/tasks/task-file-parser.js +161 -0
  98. package/dist/runtime/api/tasks/task-service.d.ts +36 -0
  99. package/dist/runtime/api/tasks/task-service.js +173 -0
  100. package/dist/runtime/api/types/index.d.ts +179 -0
  101. package/dist/runtime/api/types/index.js +1 -0
  102. package/dist/runtime/api/types/settings.d.ts +81 -0
  103. package/dist/runtime/api/types/settings.js +2 -0
  104. package/dist/runtime/api/types.d.ts +2 -0
  105. package/dist/runtime/api/types.js +1 -0
  106. package/dist/runtime/api/utils/env.d.ts +6 -0
  107. package/dist/runtime/api/utils/env.js +12 -0
  108. package/dist/runtime/api/utils/stripNextEnv.d.ts +7 -0
  109. package/dist/runtime/api/utils/stripNextEnv.js +22 -0
  110. package/dist/runtime/api/utils/title-slug.d.ts +6 -0
  111. package/dist/runtime/api/utils/title-slug.js +77 -0
  112. package/dist/runtime/api/utils/url.d.ts +2 -0
  113. package/dist/runtime/api/utils/url.js +19 -0
  114. package/dist/runtime/api/vcs/git-history-service.d.ts +57 -0
  115. package/dist/runtime/api/vcs/git-history-service.js +228 -0
  116. package/dist/runtime/api/vcs/git-service.d.ts +127 -0
  117. package/dist/runtime/api/vcs/git-service.js +284 -0
  118. package/dist/runtime/api/vcs/worktree-service.d.ts +93 -0
  119. package/dist/runtime/api/vcs/worktree-service.js +506 -0
  120. package/dist/runtime/api/vcs/worktree-service.test.d.ts +1 -0
  121. package/dist/runtime/api/vcs/worktree-service.test.js +20 -0
  122. package/dist/runtime/api/workflows/quality-pipeline.d.ts +58 -0
  123. package/dist/runtime/api/workflows/quality-pipeline.js +400 -0
  124. package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +318 -0
  125. package/dist/runtime/api/workflows/vibing-orchestrator.js +1860 -0
  126. package/dist/runtime/web/.next/BUILD_ID +1 -0
  127. package/dist/runtime/web/.next/app-build-manifest.json +59 -0
  128. package/dist/runtime/web/.next/app-path-routes-manifest.json +7 -0
  129. package/dist/runtime/web/.next/build-manifest.json +33 -0
  130. package/dist/runtime/web/.next/package.json +1 -0
  131. package/dist/runtime/web/.next/prerender-manifest.json +61 -0
  132. package/dist/runtime/web/.next/react-loadable-manifest.json +39 -0
  133. package/dist/runtime/web/.next/required-server-files.json +334 -0
  134. package/dist/runtime/web/.next/routes-manifest.json +62 -0
  135. package/dist/runtime/web/.next/server/app/_not-found/page.js +2 -0
  136. package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +1 -0
  137. package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  138. package/dist/runtime/web/.next/server/app/_not-found.html +7 -0
  139. package/dist/runtime/web/.next/server/app/_not-found.meta +8 -0
  140. package/dist/runtime/web/.next/server/app/_not-found.rsc +22 -0
  141. package/dist/runtime/web/.next/server/app/api/health/route.js +1 -0
  142. package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +1 -0
  143. package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -0
  144. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -0
  145. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +1 -0
  146. package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -0
  147. package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -0
  148. package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +1 -0
  149. package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -0
  150. package/dist/runtime/web/.next/server/app/index.html +7 -0
  151. package/dist/runtime/web/.next/server/app/index.meta +7 -0
  152. package/dist/runtime/web/.next/server/app/index.rsc +27 -0
  153. package/dist/runtime/web/.next/server/app/page.js +147 -0
  154. package/dist/runtime/web/.next/server/app/page.js.nft.json +1 -0
  155. package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -0
  156. package/dist/runtime/web/.next/server/app-paths-manifest.json +7 -0
  157. package/dist/runtime/web/.next/server/chunks/217.js +1 -0
  158. package/dist/runtime/web/.next/server/chunks/383.js +6 -0
  159. package/dist/runtime/web/.next/server/chunks/458.js +1 -0
  160. package/dist/runtime/web/.next/server/chunks/576.js +18 -0
  161. package/dist/runtime/web/.next/server/chunks/635.js +22 -0
  162. package/dist/runtime/web/.next/server/chunks/761.js +1 -0
  163. package/dist/runtime/web/.next/server/chunks/777.js +3 -0
  164. package/dist/runtime/web/.next/server/chunks/825.js +1 -0
  165. package/dist/runtime/web/.next/server/chunks/838.js +1 -0
  166. package/dist/runtime/web/.next/server/chunks/973.js +15 -0
  167. package/dist/runtime/web/.next/server/functions-config-manifest.json +4 -0
  168. package/dist/runtime/web/.next/server/middleware-build-manifest.js +1 -0
  169. package/dist/runtime/web/.next/server/middleware-manifest.json +6 -0
  170. package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +1 -0
  171. package/dist/runtime/web/.next/server/next-font-manifest.js +1 -0
  172. package/dist/runtime/web/.next/server/next-font-manifest.json +1 -0
  173. package/dist/runtime/web/.next/server/pages/404.html +7 -0
  174. package/dist/runtime/web/.next/server/pages/500.html +1 -0
  175. package/dist/runtime/web/.next/server/pages/_app.js +1 -0
  176. package/dist/runtime/web/.next/server/pages/_app.js.nft.json +1 -0
  177. package/dist/runtime/web/.next/server/pages/_document.js +1 -0
  178. package/dist/runtime/web/.next/server/pages/_document.js.nft.json +1 -0
  179. package/dist/runtime/web/.next/server/pages/_error.js +19 -0
  180. package/dist/runtime/web/.next/server/pages/_error.js.nft.json +1 -0
  181. package/dist/runtime/web/.next/server/pages-manifest.json +6 -0
  182. package/dist/runtime/web/.next/server/server-reference-manifest.js +1 -0
  183. package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -0
  184. package/dist/runtime/web/.next/server/webpack-runtime.js +1 -0
  185. package/dist/runtime/web/.next/static/chunks/18-15c10d3288afef2e.js +1 -0
  186. package/dist/runtime/web/.next/static/chunks/1c0ca389.537bbe362e3ffbd9.js +3 -0
  187. package/dist/runtime/web/.next/static/chunks/22747d63-ad5da0c19f4cfe41.js +71 -0
  188. package/dist/runtime/web/.next/static/chunks/277-0142a939f08738c3.js +63 -0
  189. package/dist/runtime/web/.next/static/chunks/355.056c2645878a799a.js +1 -0
  190. package/dist/runtime/web/.next/static/chunks/420.a5ccf151c9e2b2f1.js +1 -0
  191. package/dist/runtime/web/.next/static/chunks/439.1be0c6242fd248d5.js +15 -0
  192. package/dist/runtime/web/.next/static/chunks/440.c52e7c0f797e22b2.js +1 -0
  193. package/dist/runtime/web/.next/static/chunks/575-e2478287c27da87b.js +1 -0
  194. package/dist/runtime/web/.next/static/chunks/691.920d88c115087314.js +1 -0
  195. package/dist/runtime/web/.next/static/chunks/765-e838910065b50c3d.js +1 -0
  196. package/dist/runtime/web/.next/static/chunks/87c73c54-09e1ba5c70e60a51.js +1 -0
  197. package/dist/runtime/web/.next/static/chunks/891cff7f.0f71fc028f87e683.js +1 -0
  198. package/dist/runtime/web/.next/static/chunks/8bb4d8db-3e2aa02b0a2384b9.js +1 -0
  199. package/dist/runtime/web/.next/static/chunks/9af238c7-271a911d4e99ab18.js +1 -0
  200. package/dist/runtime/web/.next/static/chunks/app/_not-found/page-1cb74d1cba27d0ab.js +1 -0
  201. package/dist/runtime/web/.next/static/chunks/app/api/health/route-105a61ae865ba536.js +1 -0
  202. package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-105a61ae865ba536.js +1 -0
  203. package/dist/runtime/web/.next/static/chunks/app/api/upload/route-105a61ae865ba536.js +1 -0
  204. package/dist/runtime/web/.next/static/chunks/app/layout-8435322f09fd0975.js +1 -0
  205. package/dist/runtime/web/.next/static/chunks/app/page-8c3ba579efc6f918.js +1 -0
  206. package/dist/runtime/web/.next/static/chunks/cac567b0-5b77dd12911823cd.js +1 -0
  207. package/dist/runtime/web/.next/static/chunks/framework-2518f1345b5b2806.js +1 -0
  208. package/dist/runtime/web/.next/static/chunks/main-17665e5e39de9a8a.js +1 -0
  209. package/dist/runtime/web/.next/static/chunks/main-app-c0b0f5ba4f7f9d75.js +1 -0
  210. package/dist/runtime/web/.next/static/chunks/pages/_app-d6f6b3bbc3d81ee1.js +1 -0
  211. package/dist/runtime/web/.next/static/chunks/pages/_error-75a96cf1997cc3b9.js +1 -0
  212. package/dist/runtime/web/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  213. package/dist/runtime/web/.next/static/chunks/webpack-c8de37305b4635cf.js +1 -0
  214. package/dist/runtime/web/.next/static/css/08c950681f1a9a92.css +1 -0
  215. package/dist/runtime/web/.next/static/css/2728291c68f99cb1.css +3 -0
  216. package/dist/runtime/web/.next/static/css/521bd69cc298cd1a.css +1 -0
  217. package/dist/runtime/web/.next/static/css/537e22821e101b87.css +1 -0
  218. package/dist/runtime/web/.next/static/mRpNgPfbYR_0wrODzlg_4/_buildManifest.js +1 -0
  219. package/dist/runtime/web/.next/static/mRpNgPfbYR_0wrODzlg_4/_ssgManifest.js +1 -0
  220. package/dist/runtime/web/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
  221. package/dist/runtime/web/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
  222. package/dist/runtime/web/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
  223. package/dist/runtime/web/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
  224. package/dist/runtime/web/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
  225. package/dist/runtime/web/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
  226. package/dist/runtime/web/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
  227. package/dist/runtime/web/package.json +65 -0
  228. package/dist/runtime/web/server.js +44 -0
  229. package/dist/tsconfig.tsbuildinfo +1 -0
  230. package/package.json +84 -7
@@ -0,0 +1,348 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { markdownToHtml, htmlToMarkdown } from './markdown-utils.js';
3
+ describe('markdown-utils', () => {
4
+ describe('markdownToHtml', () => {
5
+ it('should return empty string for empty input', () => {
6
+ expect(markdownToHtml('')).toBe('');
7
+ expect(markdownToHtml(' ')).toBe('');
8
+ expect(markdownToHtml(null)).toBe('');
9
+ expect(markdownToHtml(undefined)).toBe('');
10
+ });
11
+ it('should convert task item to HTML', () => {
12
+ const markdown = `- [ ] Task item
13
+ - [x] Checked task`;
14
+ const html = markdownToHtml(markdown);
15
+ // Check for proper TipTap TaskList structure
16
+ expect(html).toContain('data-type="taskList"');
17
+ expect(html).toContain('data-type="taskItem"');
18
+ expect(html).toContain('data-checked="false"');
19
+ expect(html).toContain('data-checked="true"');
20
+ expect(html).toContain('<label contenteditable="false">');
21
+ expect(html).toContain('<input type="checkbox" >');
22
+ expect(html).toContain('<input type="checkbox" checked>');
23
+ expect(html).toContain('<p>Task item</p>');
24
+ expect(html).toContain('<p>Checked task</p>');
25
+ });
26
+ it('should convert basic markdown to HTML', () => {
27
+ const markdown = '# Heading 1\n\nThis is a paragraph.\n\n**Bold text** and *italic text*.';
28
+ const html = markdownToHtml(markdown);
29
+ expect(html).toContain('<h1>Heading 1</h1>');
30
+ expect(html).toContain('<p>This is a paragraph.</p>');
31
+ expect(html).toContain('<strong>Bold text</strong>');
32
+ expect(html).toContain('<em>italic text</em>');
33
+ });
34
+ // it('should convert simple task lists to HTML with data-type attribute', () => {
35
+ // const markdown = '- [ ] Unchecked task\n- [x] Checked task';
36
+ // const html = markdownToHtml(markdown);
37
+ // expect(html).toContain('data-type="taskItem"');
38
+ // expect(html).toContain('<input type="checkbox" disabled>');
39
+ // expect(html).toContain('<input type="checkbox" checked disabled>');
40
+ // expect(html).toContain('Unchecked task');
41
+ // expect(html).toContain('Checked task');
42
+ // });
43
+ // it('should handle two-level nested task lists', () => {
44
+ // const markdown = `- [ ] Parent task
45
+ // - [ ] Child task 1
46
+ // - [x] Child task 2`;
47
+ // const html = markdownToHtml(markdown);
48
+ // expect(html).toContain('Parent task');
49
+ // expect(html).toContain('Child task 1');
50
+ // expect(html).toContain('Child task 2');
51
+ // expect(html).toMatch(/<ul>[\s\S]*<ul>/); // Nested ul elements
52
+ // });
53
+ // it('should handle three-level nested task lists', () => {
54
+ // const markdown = `- [ ] Level 1
55
+ // - [ ] Level 2
56
+ // - [x] Level 3`;
57
+ // const html = markdownToHtml(markdown);
58
+ // expect(html).toContain('Level 1');
59
+ // expect(html).toContain('Level 2');
60
+ // expect(html).toContain('Level 3');
61
+ // // Should have at least 3 ul elements for 3 levels
62
+ // const ulCount = (html.match(/<ul>/g) || []).length;
63
+ // expect(ulCount).toBeGreaterThanOrEqual(3);
64
+ // });
65
+ // it('should handle deeply nested task lists (5 levels)', () => {
66
+ // const markdown = `- [ ] Level 1
67
+ // - [ ] Level 2
68
+ // - [ ] Level 3
69
+ // - [ ] Level 4
70
+ // - [x] Level 5`;
71
+ // const html = markdownToHtml(markdown);
72
+ // expect(html).toContain('Level 1');
73
+ // expect(html).toContain('Level 5');
74
+ // const ulCount = (html.match(/<ul>/g) || []).length;
75
+ // expect(ulCount).toBeGreaterThanOrEqual(5);
76
+ // });
77
+ // it('should handle mixed task lists and regular lists', () => {
78
+ // const markdown = `- [ ] Task item
79
+ // - Regular nested item
80
+ // - [ ] Nested task
81
+ // - Another regular item`;
82
+ // const html = markdownToHtml(markdown);
83
+ // expect(html).toContain('Task item');
84
+ // expect(html).toContain('Regular nested item');
85
+ // expect(html).toContain('Nested task');
86
+ // expect(html).toContain('Another regular item');
87
+ // });
88
+ // it('should handle task lists with complex content', () => {
89
+ // const markdown = `- [ ] Task with **bold** and *italic*
90
+ // - [ ] Task with \`code\`
91
+ // - [ ] Task with [link](https://example.com)`;
92
+ // const html = markdownToHtml(markdown);
93
+ // // Complex markdown inside task items may not be parsed yet
94
+ // // This is a known limitation - task item text is not re-parsed for markdown
95
+ // expect(html).toContain('Task with **bold** and *italic*');
96
+ // expect(html).toContain('Task with `code`');
97
+ // expect(html).toContain('Task with [link](https://example.com)');
98
+ // });
99
+ it('should handle numbered lists', () => {
100
+ const markdown = '1. First item\n2. Second item\n3. Third item';
101
+ const html = markdownToHtml(markdown);
102
+ expect(html).toContain('<ol>');
103
+ expect(html).toContain('First item');
104
+ expect(html).toContain('Second item');
105
+ });
106
+ it('should handle code blocks', () => {
107
+ const markdown = '```javascript\nconst hello = "world";\n```';
108
+ const html = markdownToHtml(markdown);
109
+ expect(html).toContain('<pre>');
110
+ expect(html).toContain('<code');
111
+ expect(html).toContain('hello');
112
+ });
113
+ it('should handle blockquotes', () => {
114
+ const markdown = '> This is a quote';
115
+ const html = markdownToHtml(markdown);
116
+ expect(html).toContain('<blockquote>');
117
+ expect(html).toContain('This is a quote');
118
+ });
119
+ });
120
+ describe('htmlToMarkdown', () => {
121
+ it('should return empty string for empty input', () => {
122
+ expect(htmlToMarkdown('')).toBe('');
123
+ expect(htmlToMarkdown(' ')).toBe('');
124
+ expect(htmlToMarkdown(null)).toBe('');
125
+ expect(htmlToMarkdown(undefined)).toBe('');
126
+ });
127
+ it('should convert basic HTML to markdown', () => {
128
+ const html = '<h1>Heading 1</h1><p>This is a paragraph.</p><p><strong>Bold text</strong> and <em>italic text</em>.</p>';
129
+ const markdown = htmlToMarkdown(html);
130
+ expect(markdown).toContain('# Heading 1');
131
+ expect(markdown).toContain('This is a paragraph.');
132
+ expect(markdown).toContain('**Bold text**');
133
+ // Turndown uses underscore for italic by default
134
+ expect(markdown).toContain('_italic text_');
135
+ });
136
+ it('should convert task list HTML back to markdown', () => {
137
+ const html = `<ul>
138
+ <li data-type="taskItem"><input type="checkbox" disabled> Unchecked task</li>
139
+ <li data-type="taskItem"><input type="checkbox" checked disabled> Checked task</li>
140
+ </ul>`;
141
+ const markdown = htmlToMarkdown(html);
142
+ expect(markdown).toContain('- [ ] Unchecked task');
143
+ expect(markdown).toContain('- [x] Checked task');
144
+ });
145
+ it('should preserve two-level nested task list indentation', () => {
146
+ const html = `<ul>
147
+ <li data-type="taskItem"><input type="checkbox" disabled> Parent task
148
+ <ul>
149
+ <li data-type="taskItem"><input type="checkbox" disabled> Child task 1</li>
150
+ <li data-type="taskItem"><input type="checkbox" checked disabled> Child task 2</li>
151
+ </ul>
152
+ </li>
153
+ </ul>`;
154
+ const markdown = htmlToMarkdown(html);
155
+ expect(markdown).toContain('- [ ] Parent task');
156
+ expect(markdown).toContain(' - [ ] Child task 1');
157
+ expect(markdown).toContain(' - [x] Child task 2');
158
+ });
159
+ it('should preserve three-level nested task list indentation', () => {
160
+ const html = `<ul>
161
+ <li data-type="taskItem"><input type="checkbox" disabled> Level 1
162
+ <ul>
163
+ <li data-type="taskItem"><input type="checkbox" disabled> Level 2
164
+ <ul>
165
+ <li data-type="taskItem"><input type="checkbox" checked disabled> Level 3</li>
166
+ </ul>
167
+ </li>
168
+ </ul>
169
+ </li>
170
+ </ul>`;
171
+ const markdown = htmlToMarkdown(html);
172
+ expect(markdown).toContain('- [ ] Level 1');
173
+ expect(markdown).toContain(' - [ ] Level 2');
174
+ expect(markdown).toContain(' - [x] Level 3');
175
+ });
176
+ it('should handle regular nested lists', () => {
177
+ const html = `<ul>
178
+ <li>Parent item
179
+ <ul>
180
+ <li>Child item 1</li>
181
+ <li>Child item 2</li>
182
+ </ul>
183
+ </li>
184
+ </ul>`;
185
+ const markdown = htmlToMarkdown(html);
186
+ // Turndown may add extra spacing in lists
187
+ expect(markdown).toContain('Parent item');
188
+ expect(markdown).toContain('Child item 1');
189
+ expect(markdown).toContain('Child item 2');
190
+ // Check for proper nesting with indentation
191
+ expect(markdown).toMatch(/.*Child item 1/);
192
+ });
193
+ it('should handle numbered lists', () => {
194
+ const html = '<ol><li>First</li><li>Second</li><li>Third</li></ol>';
195
+ const markdown = htmlToMarkdown(html);
196
+ // Turndown may add extra spacing after the period
197
+ expect(markdown).toMatch(/1\.\s+First/);
198
+ expect(markdown).toMatch(/2\.\s+Second/);
199
+ expect(markdown).toMatch(/3\.\s+Third/);
200
+ });
201
+ it('should handle code blocks', () => {
202
+ const html = '<pre><code class="language-javascript">const hello = "world";</code></pre>';
203
+ const markdown = htmlToMarkdown(html);
204
+ expect(markdown).toContain('```');
205
+ expect(markdown).toContain('const hello = "world";');
206
+ });
207
+ it('should handle inline code', () => {
208
+ const html = '<p>Use <code>npm install</code> to install.</p>';
209
+ const markdown = htmlToMarkdown(html);
210
+ expect(markdown).toContain('`npm install`');
211
+ });
212
+ it('should handle links', () => {
213
+ const html = '<p>Visit <a href="https://example.com">example</a></p>';
214
+ const markdown = htmlToMarkdown(html);
215
+ expect(markdown).toContain('[example](https://example.com)');
216
+ });
217
+ it('should handle blockquotes', () => {
218
+ const html = '<blockquote><p>This is a quote</p></blockquote>';
219
+ const markdown = htmlToMarkdown(html);
220
+ expect(markdown).toContain('> This is a quote');
221
+ });
222
+ it('should handle horizontal rules', () => {
223
+ const html = '<hr>';
224
+ const markdown = htmlToMarkdown(html);
225
+ expect(markdown).toContain('---');
226
+ });
227
+ it('should render list item', () => {
228
+ const html = '<li>Test</li>';
229
+ const markdown = htmlToMarkdown(html);
230
+ expect(markdown).toContain('- Test');
231
+ });
232
+ });
233
+ // describe('Round-trip conversion', () => {
234
+ // it('should preserve simple task lists through round-trip conversion', () => {
235
+ // const original = '- [ ] Task 1\n- [x] Task 2';
236
+ // const html = markdownToHtml(original);
237
+ // const result = htmlToMarkdown(html);
238
+ // expect(result).toContain('- [ ] Task 1');
239
+ // expect(result).toContain('- [x] Task 2');
240
+ // });
241
+ // it('should preserve two-level nested task lists through round-trip conversion', () => {
242
+ // const original = `- [ ] Parent task
243
+ // - [ ] Child task 1
244
+ // - [x] Child task 2`;
245
+ // const html = markdownToHtml(original);
246
+ // const result = htmlToMarkdown(html);
247
+ // expect(result).toContain('- [ ] Parent task');
248
+ // expect(result).toContain(' - [ ] Child task 1');
249
+ // expect(result).toContain(' - [x] Child task 2');
250
+ // });
251
+ // it('should preserve three-level nested task lists through round-trip conversion', () => {
252
+ // const original = `- [ ] Level 1
253
+ // - [ ] Level 2
254
+ // - [x] Level 3`;
255
+ // const html = markdownToHtml(original);
256
+ // const result = htmlToMarkdown(html);
257
+ // expect(result).toContain('- [ ] Level 1');
258
+ // expect(result).toContain(' - [ ] Level 2');
259
+ // expect(result).toContain(' - [x] Level 3');
260
+ // });
261
+ // it('should preserve complex nested task list structure', () => {
262
+ // const original = `- [ ] Task 1
263
+ // - [ ] Task 1.1
264
+ // - [x] Task 1.2
265
+ // - [ ] Task 1.2.1
266
+ // - [ ] Task 1.2.2
267
+ // - [ ] Task 2
268
+ // - [ ] Task 2.1`;
269
+ // const html = markdownToHtml(original);
270
+ // const result = htmlToMarkdown(html);
271
+ // // Check all items are present
272
+ // expect(result).toContain('- [ ] Task 1');
273
+ // expect(result).toContain(' - [ ] Task 1.1');
274
+ // expect(result).toContain(' - [x] Task 1.2');
275
+ // expect(result).toContain(' - [ ] Task 1.2.1');
276
+ // expect(result).toContain(' - [ ] Task 1.2.2');
277
+ // expect(result).toContain('- [ ] Task 2');
278
+ // expect(result).toContain(' - [ ] Task 2.1');
279
+ // });
280
+ // it('should handle task list with description formatting', () => {
281
+ // const original = `- [ ] Add button to trigger task refinement
282
+ // - [ ] **Description**: Clear explanation
283
+ // - [ ] *Acceptance Criteria*: Tests pass
284
+ // - [ ] \`Technical Details\`: Use React`;
285
+ // const html = markdownToHtml(original);
286
+ // const result = htmlToMarkdown(html);
287
+ // expect(result).toContain('- [ ] Add button to trigger task refinement');
288
+ // // Markdown inside task items may not be preserved through round-trip
289
+ // // This is a known limitation of the current implementation
290
+ // expect(result).toContain('Description');
291
+ // expect(result).toContain('Acceptance Criteria');
292
+ // expect(result).toContain('Technical Details');
293
+ // });
294
+ // });
295
+ describe('Edge cases', () => {
296
+ it('should handle malformed task list syntax gracefully', () => {
297
+ const markdown = '- [] Invalid task\n-[x] Another invalid\n- [ ] Valid task';
298
+ const html = markdownToHtml(markdown);
299
+ // Should still parse what it can
300
+ expect(html).toContain('Valid task');
301
+ });
302
+ // it('should handle empty task descriptions', () => {
303
+ // const markdown = '- [ ] \n- [x] ';
304
+ // const html = markdownToHtml(markdown);
305
+ // expect(html).toContain('data-type="taskItem"');
306
+ // });
307
+ // it('should handle very deeply nested lists (10 levels)', () => {
308
+ // let markdown = '- [ ] L1';
309
+ // for (let i = 2; i <= 10; i++) {
310
+ // markdown += '\n' + ' '.repeat(i - 1) + `- [ ] L${i}`;
311
+ // }
312
+ // const html = markdownToHtml(markdown);
313
+ // const result = htmlToMarkdown(html);
314
+ // expect(result).toContain('- [ ] L1');
315
+ // expect(result).toContain(' '.repeat(9) + '- [ ] L10');
316
+ // });
317
+ it('should handle mixed content in list items', () => {
318
+ const html = `<ul>
319
+ <li data-type="taskItem"><input type="checkbox" disabled> Task with <strong>bold</strong> and <a href="#">link</a></li>
320
+ </ul>`;
321
+ const markdown = htmlToMarkdown(html);
322
+ expect(markdown).toContain('- [ ] Task with **bold** and [link](#)');
323
+ });
324
+ it('should strip extra whitespace properly', () => {
325
+ const html = `<ul>
326
+ <li data-type="taskItem"> <input type="checkbox" disabled> Task with spaces </li>
327
+ </ul>`;
328
+ const markdown = htmlToMarkdown(html);
329
+ expect(markdown.trim()).toBe('- [ ] Task with spaces');
330
+ });
331
+ it('should handle nested task lists properly', () => {
332
+ const nestedMarkdown = `- [ ] Parent task with nested items:
333
+ - [ ] Child task 1
334
+ - [x] Child task 2`;
335
+ const html = markdownToHtml(nestedMarkdown);
336
+ // Check that HTML contains proper nesting structure
337
+ expect(html).toContain('data-type="taskList"');
338
+ expect(html).toContain('data-type="taskItem"');
339
+ expect(html).toContain('data-checked="false"');
340
+ expect(html).toContain('data-checked="true"');
341
+ expect(html).toContain('Parent task with nested items:');
342
+ expect(html).toContain('Child task 1');
343
+ expect(html).toContain('Child task 2');
344
+ // Verify the nested structure is present in HTML
345
+ expect(html).toMatch(/<li[^>]*data-type="taskItem"[^>]*>.*<ul>.*<li[^>]*data-type="taskItem"/s);
346
+ });
347
+ });
348
+ });
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Enhanced Provider Detection Service
3
+ * Robust detection logic for AI provider CLI tools with multiple fallback strategies
4
+ */
5
+ export interface DetectionResult {
6
+ found: boolean;
7
+ path?: string;
8
+ version?: string;
9
+ method?: 'explicit' | 'PATH' | 'common-location';
10
+ error?: string;
11
+ }
12
+ export interface ProviderInfo {
13
+ name: string;
14
+ command: string;
15
+ versionArgsList?: string[][];
16
+ versionRegex: RegExp;
17
+ }
18
+ /**
19
+ * Provider definitions for detection
20
+ */
21
+ export declare const PROVIDER_DEFINITIONS: Record<string, ProviderInfo>;
22
+ /**
23
+ * Enhanced Provider Detection Service
24
+ */
25
+ export declare class ProviderDetectionService {
26
+ private localConfig;
27
+ constructor();
28
+ /**
29
+ * Detect a specific provider with all fallback strategies
30
+ */
31
+ detectProvider(providerId: string): Promise<DetectionResult>;
32
+ /**
33
+ * Detect all configured providers
34
+ */
35
+ detectAllProviders(): Promise<Record<string, DetectionResult>>;
36
+ /**
37
+ * Validate a specific binary path
38
+ */
39
+ validateBinaryPath(binPath: string, provider: ProviderInfo, method?: DetectionResult['method']): Promise<DetectionResult>;
40
+ /**
41
+ * Set explicit provider path and cache it
42
+ */
43
+ setProviderPath(providerId: string, binPath: string): Promise<DetectionResult>;
44
+ /**
45
+ * Clear cached provider configuration
46
+ */
47
+ clearProviderPath(providerId: string): Promise<void>;
48
+ /**
49
+ * Get cached detection results if still fresh
50
+ */
51
+ getCachedResult(providerId: string): Promise<DetectionResult | null>;
52
+ private checkExplicitConfig;
53
+ private checkSystemCommand;
54
+ private getVersion;
55
+ private resolveCommand;
56
+ private toLocalConfigKey;
57
+ private cacheDetectionResult;
58
+ }
59
+ export declare function getProviderDetectionService(): ProviderDetectionService;
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Enhanced Provider Detection Service
3
+ * Robust detection logic for AI provider CLI tools with multiple fallback strategies
4
+ */
5
+ import { exec, execFile } from 'child_process';
6
+ import { promisify } from 'util';
7
+ import fs from 'fs/promises';
8
+ import which from 'which';
9
+ import { log } from './logger.js';
10
+ import { getLocalConfigService } from './local-config.js';
11
+ const execAsync = promisify(exec);
12
+ const execFileAsync = promisify(execFile);
13
+ /**
14
+ * Provider definitions for detection
15
+ */
16
+ export const PROVIDER_DEFINITIONS = {
17
+ 'claude-code': {
18
+ name: 'Claude Code',
19
+ command: 'claude',
20
+ versionArgsList: [['--version'], ['-v']],
21
+ versionRegex: /(\d+\.\d+\.\d+)/,
22
+ },
23
+ codex: {
24
+ name: 'Codex CLI',
25
+ command: 'codex',
26
+ versionArgsList: [['--version'], ['-v']],
27
+ versionRegex: /(\d+\.\d+\.\d+)/,
28
+ },
29
+ };
30
+ /**
31
+ * Enhanced Provider Detection Service
32
+ */
33
+ export class ProviderDetectionService {
34
+ constructor() {
35
+ this.localConfig = getLocalConfigService();
36
+ // Ensure local config is initialized
37
+ this.localConfig.initialize().catch((error) => {
38
+ log.error('Failed to initialize local config for provider detection', error, 'provider-detection');
39
+ });
40
+ }
41
+ /**
42
+ * Detect a specific provider with all fallback strategies
43
+ */
44
+ async detectProvider(providerId) {
45
+ const provider = PROVIDER_DEFINITIONS[providerId];
46
+ if (!provider) {
47
+ return {
48
+ found: false,
49
+ error: `Unknown provider: ${providerId}`,
50
+ };
51
+ }
52
+ log.debug(`Detecting provider: ${provider.name}`, { providerId }, 'provider-detection');
53
+ // Strategy 1: Check explicit configuration from local config
54
+ const explicitResult = await this.checkExplicitConfig(providerId, provider);
55
+ if (explicitResult.found) {
56
+ log.info(`Provider found via explicit config: ${provider.name}`, explicitResult, 'provider-detection');
57
+ return explicitResult;
58
+ }
59
+ // Strategy 2: Resolve via system command lookup
60
+ const systemResult = await this.checkSystemCommand(provider);
61
+ if (systemResult.found) {
62
+ log.info(`Provider found on system PATH: ${provider.name}`, systemResult, 'provider-detection');
63
+ await this.cacheDetectionResult(providerId, systemResult);
64
+ return systemResult;
65
+ }
66
+ // All strategies failed
67
+ const result = {
68
+ found: false,
69
+ error: `${provider.name} not found via explicit config or system PATH`,
70
+ };
71
+ log.warn(`Provider not found: ${provider.name}`, result, 'provider-detection');
72
+ return result;
73
+ }
74
+ /**
75
+ * Detect all configured providers
76
+ */
77
+ async detectAllProviders() {
78
+ const results = {};
79
+ for (const providerId of Object.keys(PROVIDER_DEFINITIONS)) {
80
+ try {
81
+ results[providerId] = await this.detectProvider(providerId);
82
+ }
83
+ catch (error) {
84
+ results[providerId] = {
85
+ found: false,
86
+ error: `Detection failed: ${error instanceof Error ? error.message : String(error)}`,
87
+ };
88
+ }
89
+ }
90
+ return results;
91
+ }
92
+ /**
93
+ * Validate a specific binary path
94
+ */
95
+ async validateBinaryPath(binPath, provider, method = 'explicit') {
96
+ try {
97
+ // Check if file exists and is executable
98
+ const stats = await fs.stat(binPath);
99
+ if (!stats.isFile()) {
100
+ return {
101
+ found: false,
102
+ error: `Path is not a file: ${binPath}`,
103
+ };
104
+ }
105
+ // Try to execute version command
106
+ const version = await this.getVersion(binPath, provider);
107
+ return {
108
+ found: true,
109
+ path: binPath,
110
+ version,
111
+ method,
112
+ };
113
+ }
114
+ catch (error) {
115
+ return {
116
+ found: false,
117
+ path: binPath,
118
+ error: `Validation failed: ${error instanceof Error ? error.message : String(error)}`,
119
+ };
120
+ }
121
+ }
122
+ /**
123
+ * Set explicit provider path and cache it
124
+ */
125
+ async setProviderPath(providerId, binPath) {
126
+ const provider = PROVIDER_DEFINITIONS[providerId];
127
+ if (!provider) {
128
+ return {
129
+ found: false,
130
+ error: `Unknown provider: ${providerId}`,
131
+ };
132
+ }
133
+ const result = await this.validateBinaryPath(binPath, provider);
134
+ if (result.found) {
135
+ const localId = this.toLocalConfigKey(providerId);
136
+ await this.localConfig.setProviderPath(localId, binPath, 'explicit');
137
+ }
138
+ return result;
139
+ }
140
+ /**
141
+ * Clear cached provider configuration
142
+ */
143
+ async clearProviderPath(providerId) {
144
+ const localId = this.toLocalConfigKey(providerId);
145
+ await this.localConfig.clearProviderPath(localId);
146
+ }
147
+ /**
148
+ * Get cached detection results if still fresh
149
+ */
150
+ async getCachedResult(providerId) {
151
+ if (this.localConfig.isCachedValidationFresh(providerId)) {
152
+ const cached = this.localConfig.getCachedValidationResult(providerId);
153
+ if (cached) {
154
+ const localId = this.toLocalConfigKey(providerId);
155
+ return {
156
+ found: cached.available,
157
+ path: cached.available
158
+ ? this.localConfig.getProviderConfig(localId)?.detectedPath
159
+ : undefined,
160
+ version: cached.version,
161
+ error: cached.error,
162
+ };
163
+ }
164
+ }
165
+ return null;
166
+ }
167
+ // Private detection strategies
168
+ async checkExplicitConfig(providerId, provider) {
169
+ const localId = this.toLocalConfigKey(providerId);
170
+ const config = this.localConfig.getProviderConfig(localId);
171
+ if (!config?.binPath) {
172
+ return { found: false };
173
+ }
174
+ return this.validateBinaryPath(config.binPath, provider, 'explicit');
175
+ }
176
+ async checkSystemCommand(provider) {
177
+ const resolvedPath = await this.resolveCommand(provider.command);
178
+ if (!resolvedPath) {
179
+ return { found: false };
180
+ }
181
+ return this.validateBinaryPath(resolvedPath, provider, 'PATH');
182
+ }
183
+ async getVersion(binPath, provider) {
184
+ const argSets = provider.versionArgsList ?? [['--version']];
185
+ for (const args of argSets) {
186
+ try {
187
+ const { stdout, stderr } = await execFileAsync(binPath, args, { timeout: 10000 });
188
+ const output = (stdout + stderr).trim();
189
+ const match = output.match(provider.versionRegex);
190
+ if (match?.[1]) {
191
+ return match[1];
192
+ }
193
+ }
194
+ catch (error) {
195
+ log.debug(`Version command failed for ${binPath}`, { args, error }, 'provider-detection');
196
+ }
197
+ }
198
+ return undefined;
199
+ }
200
+ async resolveCommand(command) {
201
+ try {
202
+ return await which(command);
203
+ }
204
+ catch (error) {
205
+ log.debug(`which lookup failed for ${command}`, error, 'provider-detection');
206
+ }
207
+ if (process.platform !== 'win32') {
208
+ try {
209
+ const { stdout } = await execAsync(`command -v ${command}`, { timeout: 5000 });
210
+ const resolved = stdout.trim().split('\n')[0];
211
+ return resolved ? resolved : null;
212
+ }
213
+ catch (error) {
214
+ log.debug(`command -v lookup failed for ${command}`, error, 'provider-detection');
215
+ }
216
+ }
217
+ return null;
218
+ }
219
+ toLocalConfigKey(providerId) {
220
+ return providerId === 'claude-code' ? 'claude-code' : 'codex';
221
+ }
222
+ async cacheDetectionResult(providerId, result) {
223
+ if (result.found && result.path) {
224
+ const localId = this.toLocalConfigKey(providerId);
225
+ if (result.method === 'explicit') {
226
+ await this.localConfig.setProviderPath(localId, result.path, 'explicit');
227
+ }
228
+ else {
229
+ await this.localConfig.updateDetectedProvider(localId, result.path, result.method || 'PATH');
230
+ }
231
+ }
232
+ await this.localConfig.cacheValidationResult(providerId, {
233
+ available: result.found,
234
+ error: result.error,
235
+ version: result.version,
236
+ });
237
+ }
238
+ }
239
+ // Singleton instance
240
+ let providerDetectionService = null;
241
+ export function getProviderDetectionService() {
242
+ providerDetectionService ?? (providerDetectionService = new ProviderDetectionService());
243
+ return providerDetectionService;
244
+ }
@@ -0,0 +1,6 @@
1
+ import { AgentService } from '../../agent/agent-service.js';
2
+ declare global {
3
+ var __vibeman_agent_service__: unknown;
4
+ }
5
+ export declare function getAgentService(): AgentService;
6
+ export declare function resetAgentService(): void;