vibeman 0.0.2 → 0.0.5

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 (198) hide show
  1. package/dist/index.js +3 -3
  2. package/dist/runtime/api/.tsbuildinfo +1 -1
  3. package/dist/runtime/api/agent/agent-service.d.ts +11 -6
  4. package/dist/runtime/api/agent/agent-service.js +97 -29
  5. package/dist/runtime/api/agent/ai-providers/amp-cli-provider.d.ts +38 -0
  6. package/dist/runtime/api/agent/ai-providers/amp-cli-provider.js +268 -0
  7. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +2 -0
  8. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +92 -32
  9. package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.d.ts +24 -0
  10. package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.js +291 -0
  11. package/dist/runtime/api/agent/ai-providers/index.d.ts +3 -3
  12. package/dist/runtime/api/agent/ai-providers/index.js +3 -1
  13. package/dist/runtime/api/agent/ai-providers/types.d.ts +5 -2
  14. package/dist/runtime/api/agent/amp-cli-provider.test.js +99 -0
  15. package/dist/runtime/api/agent/codex-cli-provider.test.js +54 -7
  16. package/dist/runtime/api/agent/prompt-service.js +108 -105
  17. package/dist/runtime/api/agent/prompt-service.test.js +35 -0
  18. package/dist/runtime/api/agent/routing-policy.d.ts +13 -30
  19. package/dist/runtime/api/agent/routing-policy.js +82 -132
  20. package/dist/runtime/api/agent/routing-policy.test.js +63 -0
  21. package/dist/runtime/api/api/routers/ai.d.ts +15 -3
  22. package/dist/runtime/api/api/routers/ai.js +7 -6
  23. package/dist/runtime/api/api/routers/executions.d.ts +3 -8
  24. package/dist/runtime/api/api/routers/executions.js +2 -2
  25. package/dist/runtime/api/api/routers/provider-config.d.ts +34 -0
  26. package/dist/runtime/api/api/routers/settings.d.ts +19 -0
  27. package/dist/runtime/api/api/routers/settings.js +16 -0
  28. package/dist/runtime/api/api/routers/tasks.d.ts +10 -10
  29. package/dist/runtime/api/api/routers/workflows.d.ts +20 -12
  30. package/dist/runtime/api/api/routers/workflows.js +2 -1
  31. package/dist/runtime/api/api/routers/worktrees.d.ts +2 -2
  32. package/dist/runtime/api/api/trpc.d.ts +18 -18
  33. package/dist/runtime/api/lib/local-config.d.ts +94 -4
  34. package/dist/runtime/api/lib/local-config.js +16 -0
  35. package/dist/runtime/api/lib/provider-detection.d.ts +2 -0
  36. package/dist/runtime/api/lib/provider-detection.js +83 -1
  37. package/dist/runtime/api/lib/server/vibeman-info.d.ts +5 -0
  38. package/dist/runtime/api/lib/server/vibeman-info.js +85 -0
  39. package/dist/runtime/api/lib/trpc/server.d.ts +85 -35
  40. package/dist/runtime/api/persistence/execution-log-persistence.d.ts +1 -1
  41. package/dist/runtime/api/persistence/execution-log-persistence.js +19 -3
  42. package/dist/runtime/api/router.d.ts +85 -35
  43. package/dist/runtime/api/settings-service.js +70 -5
  44. package/dist/runtime/api/tasks/task-file-parser.d.ts +1 -0
  45. package/dist/runtime/api/tasks/task-file-parser.js +20 -1
  46. package/dist/runtime/api/tasks/task-updater.d.ts +62 -0
  47. package/dist/runtime/api/tasks/task-updater.js +260 -0
  48. package/dist/runtime/api/tasks/task-updater.test.d.ts +1 -0
  49. package/dist/runtime/api/tasks/task-updater.test.js +303 -0
  50. package/dist/runtime/api/types/index.d.ts +9 -2
  51. package/dist/runtime/api/types/settings.d.ts +29 -5
  52. package/dist/runtime/api/vcs/git-service.d.ts +9 -0
  53. package/dist/runtime/api/vcs/git-service.js +23 -0
  54. package/dist/runtime/api/vcs/worktree-service.d.ts +1 -1
  55. package/dist/runtime/api/vcs/worktree-service.js +22 -10
  56. package/dist/runtime/api/workflows/quality-pipeline.js +2 -1
  57. package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +93 -5
  58. package/dist/runtime/api/workflows/vibing-orchestrator.js +806 -204
  59. package/dist/runtime/api/workflows/workflow-effects.d.ts +45 -0
  60. package/dist/runtime/api/workflows/workflow-effects.js +49 -0
  61. package/dist/runtime/api/workflows/workflow-reconciler.d.ts +65 -0
  62. package/dist/runtime/api/workflows/workflow-reconciler.js +226 -0
  63. package/dist/runtime/api/workflows/workflow-reducer.d.ts +26 -0
  64. package/dist/runtime/api/workflows/workflow-reducer.js +288 -0
  65. package/dist/runtime/api/workflows/workflow-reducer.test.d.ts +1 -0
  66. package/dist/runtime/api/workflows/workflow-reducer.test.js +247 -0
  67. package/dist/runtime/api/workflows/workflow-schema.d.ts +546 -0
  68. package/dist/runtime/api/workflows/workflow-schema.js +256 -0
  69. package/dist/runtime/web/.next/BUILD_ID +1 -1
  70. package/dist/runtime/web/.next/app-build-manifest.json +51 -44
  71. package/dist/runtime/web/.next/app-path-routes-manifest.json +2 -1
  72. package/dist/runtime/web/.next/build-manifest.json +14 -14
  73. package/dist/runtime/web/.next/prerender-manifest.json +10 -10
  74. package/dist/runtime/web/.next/react-loadable-manifest.json +2 -33
  75. package/dist/runtime/web/.next/required-server-files.json +5 -5
  76. package/dist/runtime/web/.next/routes-manifest.json +8 -0
  77. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +1 -0
  78. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +1 -0
  79. package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +1 -0
  80. package/dist/runtime/web/.next/server/app/_not-found/page.js +2 -2
  81. package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  82. package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  83. package/dist/runtime/web/.next/server/app/_not-found.html +2 -2
  84. package/dist/runtime/web/.next/server/app/_not-found.rsc +12 -12
  85. package/dist/runtime/web/.next/server/app/api/health/route.js +1 -1
  86. package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +1 -1
  87. package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  88. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -1
  89. package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +1 -1
  90. package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -1
  91. package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -1
  92. package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +1 -1
  93. package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -1
  94. package/dist/runtime/web/.next/server/app/index.html +2 -2
  95. package/dist/runtime/web/.next/server/app/index.rsc +15 -15
  96. package/dist/runtime/web/.next/server/app/page.js +27 -62
  97. package/dist/runtime/web/.next/server/app/page.js.nft.json +1 -1
  98. package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -1
  99. package/dist/runtime/web/.next/server/app-paths-manifest.json +2 -1
  100. package/dist/runtime/web/.next/server/chunks/210.js +1 -0
  101. package/dist/runtime/web/.next/server/chunks/291.js +18 -0
  102. package/dist/runtime/web/.next/server/chunks/552.js +22 -0
  103. package/dist/runtime/web/.next/server/chunks/780.js +1 -0
  104. package/dist/runtime/web/.next/server/chunks/905.js +6 -0
  105. package/dist/runtime/web/.next/server/chunks/98.js +1 -0
  106. package/dist/runtime/web/.next/server/middleware-build-manifest.js +1 -1
  107. package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +1 -1
  108. package/dist/runtime/web/.next/server/pages/404.html +2 -2
  109. package/dist/runtime/web/.next/server/pages/500.html +1 -1
  110. package/dist/runtime/web/.next/server/pages/_app.js +1 -1
  111. package/dist/runtime/web/.next/server/pages/_app.js.nft.json +1 -1
  112. package/dist/runtime/web/.next/server/pages/_document.js +1 -1
  113. package/dist/runtime/web/.next/server/pages/_document.js.nft.json +1 -1
  114. package/dist/runtime/web/.next/server/pages/_error.js +9 -9
  115. package/dist/runtime/web/.next/server/pages/_error.js.nft.json +1 -1
  116. package/dist/runtime/web/.next/server/pages-manifest.json +1 -1
  117. package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -1
  118. package/dist/runtime/web/.next/server/webpack-runtime.js +1 -1
  119. package/dist/runtime/web/.next/static/LJFZk_8tvKFN_Ee4HqUuM/_buildManifest.js +1 -0
  120. package/dist/runtime/web/.next/static/chunks/05c91ade-7d09b2b280adffd1.js +1 -0
  121. package/dist/runtime/web/.next/static/chunks/201-51bef3fa8c832e2e.js +1 -0
  122. package/dist/runtime/web/.next/static/chunks/524-89747ed9b0294f8a.js +1 -0
  123. package/dist/runtime/web/.next/static/chunks/554-8bec6e9cca6acc67.js +1 -0
  124. package/dist/runtime/web/.next/static/chunks/764.86e9503a69d45a85.js +1 -0
  125. package/dist/runtime/web/.next/static/chunks/{87c73c54-09e1ba5c70e60a51.js → 7ab4dc20-239138e0ae7af24a.js} +1 -1
  126. package/dist/runtime/web/.next/static/chunks/905-342391e3d3a3678f.js +20 -0
  127. package/dist/runtime/web/.next/static/chunks/a8a5ce16-4edea7df2d9b544a.js +79 -0
  128. package/dist/runtime/web/.next/static/chunks/{8bb4d8db-3e2aa02b0a2384b9.js → ad74d572-4c1b162e2c15acaa.js} +1 -1
  129. package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-7b752a8641f96c1f.js +1 -0
  130. package/dist/runtime/web/.next/static/chunks/app/_not-found/page-34e66b251c2b5044.js +1 -0
  131. package/dist/runtime/web/.next/static/chunks/app/api/health/route-7b752a8641f96c1f.js +1 -0
  132. package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-7b752a8641f96c1f.js +1 -0
  133. package/dist/runtime/web/.next/static/chunks/app/api/upload/route-7b752a8641f96c1f.js +1 -0
  134. package/dist/runtime/web/.next/static/chunks/app/layout-df9ac93cb02b2385.js +1 -0
  135. package/dist/runtime/web/.next/static/chunks/app/page-6610743f7de5f92a.js +1 -0
  136. package/dist/runtime/web/.next/static/chunks/c25e0690-e9b798b8de667da1.js +1 -0
  137. package/dist/runtime/web/.next/static/chunks/framework-57157ec4d37f64aa.js +1 -0
  138. package/dist/runtime/web/.next/static/chunks/main-app-156cc0c60371bd78.js +1 -0
  139. package/dist/runtime/web/.next/static/chunks/main-df25d367c47b1fec.js +1 -0
  140. package/dist/runtime/web/.next/static/chunks/pages/_app-9f629a5e1131d19f.js +1 -0
  141. package/dist/runtime/web/.next/static/chunks/pages/_error-9238238274c7efcd.js +1 -0
  142. package/dist/runtime/web/.next/static/chunks/webpack-cd50e39b423d1808.js +1 -0
  143. package/dist/runtime/web/.next/static/css/4fbf378a264bd4ea.css +1 -0
  144. package/dist/runtime/web/package.json +8 -8
  145. package/dist/runtime/web/server.js +1 -1
  146. package/dist/tsconfig.tsbuildinfo +1 -1
  147. package/package.json +3 -37
  148. package/dist/runtime/api/lib/image-paste-drop-extension.d.ts +0 -26
  149. package/dist/runtime/api/lib/image-paste-drop-extension.js +0 -125
  150. package/dist/runtime/api/lib/markdown-utils.d.ts +0 -8
  151. package/dist/runtime/api/lib/markdown-utils.js +0 -282
  152. package/dist/runtime/api/lib/markdown-utils.test.js +0 -348
  153. package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.js +0 -27
  154. package/dist/runtime/api/lib/tiptap-utils.d.ts +0 -130
  155. package/dist/runtime/api/lib/tiptap-utils.js +0 -327
  156. package/dist/runtime/api/lib/trpc/client.d.ts +0 -1
  157. package/dist/runtime/api/lib/trpc/client.js +0 -5
  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/static/chunks/18-15c10d3288afef2e.js +0 -1
  169. package/dist/runtime/web/.next/static/chunks/1c0ca389.537bbe362e3ffbd9.js +0 -3
  170. package/dist/runtime/web/.next/static/chunks/22747d63-ad5da0c19f4cfe41.js +0 -71
  171. package/dist/runtime/web/.next/static/chunks/277-0142a939f08738c3.js +0 -63
  172. package/dist/runtime/web/.next/static/chunks/355.056c2645878a799a.js +0 -1
  173. package/dist/runtime/web/.next/static/chunks/420.a5ccf151c9e2b2f1.js +0 -1
  174. package/dist/runtime/web/.next/static/chunks/439.1be0c6242fd248d5.js +0 -15
  175. package/dist/runtime/web/.next/static/chunks/440.c52e7c0f797e22b2.js +0 -1
  176. package/dist/runtime/web/.next/static/chunks/575-e2478287c27da87b.js +0 -1
  177. package/dist/runtime/web/.next/static/chunks/691.920d88c115087314.js +0 -1
  178. package/dist/runtime/web/.next/static/chunks/765-e838910065b50c3d.js +0 -1
  179. package/dist/runtime/web/.next/static/chunks/891cff7f.0f71fc028f87e683.js +0 -1
  180. package/dist/runtime/web/.next/static/chunks/9af238c7-271a911d4e99ab18.js +0 -1
  181. package/dist/runtime/web/.next/static/chunks/app/_not-found/page-1cb74d1cba27d0ab.js +0 -1
  182. package/dist/runtime/web/.next/static/chunks/app/api/health/route-105a61ae865ba536.js +0 -1
  183. package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-105a61ae865ba536.js +0 -1
  184. package/dist/runtime/web/.next/static/chunks/app/api/upload/route-105a61ae865ba536.js +0 -1
  185. package/dist/runtime/web/.next/static/chunks/app/layout-8435322f09fd0975.js +0 -1
  186. package/dist/runtime/web/.next/static/chunks/app/page-8c3ba579efc6f918.js +0 -1
  187. package/dist/runtime/web/.next/static/chunks/cac567b0-5b77dd12911823cd.js +0 -1
  188. package/dist/runtime/web/.next/static/chunks/framework-2518f1345b5b2806.js +0 -1
  189. package/dist/runtime/web/.next/static/chunks/main-17665e5e39de9a8a.js +0 -1
  190. package/dist/runtime/web/.next/static/chunks/main-app-c0b0f5ba4f7f9d75.js +0 -1
  191. package/dist/runtime/web/.next/static/chunks/pages/_app-d6f6b3bbc3d81ee1.js +0 -1
  192. package/dist/runtime/web/.next/static/chunks/pages/_error-75a96cf1997cc3b9.js +0 -1
  193. package/dist/runtime/web/.next/static/chunks/webpack-c8de37305b4635cf.js +0 -1
  194. package/dist/runtime/web/.next/static/css/08c950681f1a9a92.css +0 -1
  195. package/dist/runtime/web/.next/static/mRpNgPfbYR_0wrODzlg_4/_buildManifest.js +0 -1
  196. /package/dist/runtime/api/{lib/markdown-utils.test.d.ts → agent/amp-cli-provider.test.d.ts} +0 -0
  197. /package/dist/runtime/api/{lib/tiptap-utils.clamp-selection.test.d.ts → agent/routing-policy.test.d.ts} +0 -0
  198. /package/dist/runtime/web/.next/static/{mRpNgPfbYR_0wrODzlg_4 → LJFZk_8tvKFN_Ee4HqUuM}/_ssgManifest.js +0 -0
@@ -6,6 +6,29 @@ import { spawn } from 'child_process';
6
6
  import { getSettingsService } from '../../settings-service.js';
7
7
  import { getProviderDetectionService } from '../../lib/provider-detection.js';
8
8
  import { logger } from '../../lib/logger.js';
9
+ const CODEX_BASE_MODELS = [
10
+ 'gpt-5.1-codex-max',
11
+ 'gpt-5.1-codex',
12
+ 'gpt-5.1-codex-mini',
13
+ 'gpt-5.2',
14
+ 'gpt-5.1',
15
+ ];
16
+ const CODEX_BASE_LABELS = {
17
+ 'gpt-5.1-codex-max': 'gpt-5.1 Codex Max (current)',
18
+ 'gpt-5.1-codex': 'gpt-5.1 Codex',
19
+ 'gpt-5.1-codex-mini': 'gpt-5.1 Codex Mini',
20
+ 'gpt-5.2': 'gpt-5.2',
21
+ 'gpt-5.1': 'gpt-5.1',
22
+ };
23
+ const REASONING_SUFFIXES = [
24
+ 'minimal',
25
+ 'low',
26
+ 'medium',
27
+ 'high',
28
+ 'extra',
29
+ 'extra-high',
30
+ 'extra_high',
31
+ ];
9
32
  export class CodexCliProvider {
10
33
  constructor(config = {}) {
11
34
  this.config = config;
@@ -37,26 +60,52 @@ export class CodexCliProvider {
37
60
  // Default to binary name (resolve via PATH)
38
61
  return 'codex';
39
62
  }
63
+ normalizeReasoningEffort(value) {
64
+ if (!value)
65
+ return undefined;
66
+ const normalized = value.toLowerCase().replace(/\s+/g, '-');
67
+ if (normalized === 'minimal')
68
+ return 'low';
69
+ if (normalized === 'extra-high' || normalized === 'extra_high' || normalized === 'extra') {
70
+ return 'extra_high';
71
+ }
72
+ const allowed = ['low', 'medium', 'high'];
73
+ return allowed.includes(normalized)
74
+ ? normalized
75
+ : undefined;
76
+ }
77
+ resolveModelSpec(model, fallbackEffort) {
78
+ const fallback = this.normalizeReasoningEffort(fallbackEffort);
79
+ if (!model) {
80
+ return { reasoning: fallback };
81
+ }
82
+ const trimmed = model.trim();
83
+ const normalized = trimmed.toLowerCase();
84
+ const basePattern = CODEX_BASE_MODELS.map((m) => m.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')).join('|');
85
+ const reasoningPattern = [...REASONING_SUFFIXES].join('|');
86
+ const match = normalized.match(new RegExp(`^(${basePattern})(?:-(${reasoningPattern}))?$`));
87
+ if (!match) {
88
+ return {
89
+ cliModel: trimmed,
90
+ requestedModel: trimmed,
91
+ reasoning: fallback,
92
+ };
93
+ }
94
+ const baseModel = match[1];
95
+ const reasoning = this.normalizeReasoningEffort(match[2]);
96
+ return {
97
+ cliModel: baseModel,
98
+ requestedModel: trimmed,
99
+ reasoning: reasoning ?? fallback,
100
+ };
101
+ }
40
102
  async *execute(prompt, options) {
41
103
  // Effective options
42
104
  const cwd = options?.workingDirectory || this.config.defaultWorkingDirectory || process.cwd();
43
- const mapModel = (m) => {
44
- if (!m)
45
- return m;
46
- // Normalize reasoning-effort variants to the canonical model id
47
- // Users reported only `gpt-5` is selectable; variants are effort levels in the TUI.
48
- const lower = m.toLowerCase();
49
- if (lower === 'gpt-5-minimal' ||
50
- lower === 'gpt-5-low' ||
51
- lower === 'gpt-5-medium' ||
52
- lower === 'gpt-5-high') {
53
- return 'gpt-5';
54
- }
55
- return m;
56
- };
57
- const model = mapModel(options?.model || this.config.defaultModel);
105
+ const resolvedModel = this.resolveModelSpec(options?.model || this.config.defaultModel, options?.effort);
106
+ const model = resolvedModel.cliModel;
58
107
  const images = (options?.images || []).filter(Boolean);
59
- const effort = options?.effort;
108
+ const reasoningEffort = resolvedModel.reasoning;
60
109
  const timeoutMs = options?.timeout ?? this.config.defaultTimeoutMs ?? 10 * 60 * 1000; // 10m
61
110
  const systemPrompt = options?.systemPrompt?.trim();
62
111
  const appendSystemPrompt = options?.appendSystemPrompt?.trim();
@@ -74,6 +123,9 @@ export class CodexCliProvider {
74
123
  if (model) {
75
124
  argv.push('--model', model);
76
125
  }
126
+ if (reasoningEffort) {
127
+ argv.push('-c', `model_reasoning_effort=${reasoningEffort}`);
128
+ }
77
129
  // Prefer spawning with cwd, but also set --cd to make Codex aware of root
78
130
  if (cwd) {
79
131
  argv.push('--cd', cwd);
@@ -82,7 +134,9 @@ export class CodexCliProvider {
82
134
  argv.push('--image', images.join(','));
83
135
  }
84
136
  if (options?.dangerouslyBypassApprovalsAndSandbox) {
85
- argv.push('--dangerously-bypass-approvals-and-sandbox');
137
+ // Codex CLI >= 0.73.0 replaces the legacy bypass flag with --full-auto + sandbox selection
138
+ argv.push('--full-auto');
139
+ argv.push('--sandbox', 'danger-full-access');
86
140
  }
87
141
  const cmd = await this.resolveExecutable();
88
142
  const child = spawn(cmd, argv, {
@@ -95,7 +149,7 @@ export class CodexCliProvider {
95
149
  type: 'init',
96
150
  timestamp: new Date().toISOString(),
97
151
  sessionId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
98
- model: model || 'default',
152
+ model: resolvedModel.requestedModel || model || 'default',
99
153
  provider: this.name,
100
154
  };
101
155
  yield init;
@@ -108,7 +162,7 @@ export class CodexCliProvider {
108
162
  metadata: {
109
163
  provider: this.name,
110
164
  images,
111
- effort,
165
+ effort: reasoningEffort || options?.effort,
112
166
  systemPrompt,
113
167
  appendSystemPrompt,
114
168
  dangerouslyBypassApprovalsAndSandbox: !!options?.dangerouslyBypassApprovalsAndSandbox,
@@ -242,6 +296,7 @@ export class CodexCliProvider {
242
296
  // Convenience: collect from execute()
243
297
  let content = '';
244
298
  const start = Date.now();
299
+ const resolvedModel = this.resolveModelSpec(options?.model || this.config.defaultModel, options?.effort);
245
300
  for await (const msg of this.execute(prompt, options)) {
246
301
  if (msg.type === 'assistant' && typeof msg.content === 'string') {
247
302
  content += (content ? '\n' : '') + msg.content;
@@ -250,26 +305,31 @@ export class CodexCliProvider {
250
305
  return {
251
306
  id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
252
307
  provider: this.name,
253
- model: options?.model || this.config.defaultModel || 'default',
308
+ model: resolvedModel.requestedModel || resolvedModel.cliModel || 'default',
254
309
  content,
255
310
  usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
256
311
  duration: Date.now() - start,
257
312
  };
258
313
  }
259
314
  async detectAvailableModels() {
260
- // Current public CLI exposes `--model gpt-5`; effort levels are chosen in-CLI.
261
- return [
262
- {
263
- id: 'gpt-5',
264
- name: 'gpt-5',
265
- displayName: 'gpt‑5',
266
- provider: this.name,
267
- contextWindow: 256000,
268
- maxOutputTokens: 16384,
269
- // Capabilities per Codex docs: supports images via --image and advanced reasoning
270
- capabilities: ['code', 'analysis', 'tools', 'vision', 'image', 'reasoning'],
271
- },
315
+ const capabilities = ['code', 'analysis', 'tools', 'vision', 'image', 'reasoning'];
316
+ const makeModel = (id, displayName) => ({
317
+ id,
318
+ name: id,
319
+ displayName,
320
+ provider: this.name,
321
+ contextWindow: 256000,
322
+ maxOutputTokens: 16384,
323
+ capabilities,
324
+ });
325
+ const addReasoningVariants = (base, label) => [
326
+ makeModel(base, label),
327
+ makeModel(`${base}-low`, `${label} · low reasoning`),
328
+ makeModel(`${base}-medium`, `${label} · medium reasoning`),
329
+ makeModel(`${base}-high`, `${label} · high reasoning`),
330
+ makeModel(`${base}-extra-high`, `${label} · extra-high reasoning`),
272
331
  ];
332
+ return Object.entries(CODEX_BASE_LABELS).flatMap(([id, label]) => addReasoningVariants(id, label));
273
333
  }
274
334
  async validateSetup() {
275
335
  try {
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Gemini CLI Provider
3
+ * Headless wrapper around the Gemini CLI binary (default command `gemini`).
4
+ */
5
+ import { AIProvider, ExecutionMessage, ExecutionOptions, ModelInfo, ProviderCapabilities, ProviderStatus } from './types.js';
6
+ export interface GeminiCliConfig {
7
+ geminiBinPath?: string;
8
+ defaultWorkingDirectory?: string;
9
+ defaultModel?: string;
10
+ defaultTimeoutMs?: number;
11
+ }
12
+ export declare class GeminiCliProvider implements AIProvider {
13
+ private config;
14
+ readonly name = "gemini";
15
+ readonly displayName = "Gemini CLI";
16
+ constructor(config?: GeminiCliConfig);
17
+ private resolveExecutable;
18
+ private normalizeReasoningEffort;
19
+ private resolveModelSpec;
20
+ execute(prompt: string, options?: ExecutionOptions): AsyncIterableIterator<ExecutionMessage>;
21
+ detectAvailableModels(): Promise<ModelInfo[]>;
22
+ validateSetup(): Promise<ProviderStatus>;
23
+ getCapabilities(): ProviderCapabilities;
24
+ }
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Gemini CLI Provider
3
+ * Headless wrapper around the Gemini CLI binary (default command `gemini`).
4
+ */
5
+ import { spawn } from 'child_process';
6
+ import { getSettingsService } from '../../settings-service.js';
7
+ import { getProviderDetectionService } from '../../lib/provider-detection.js';
8
+ export class GeminiCliProvider {
9
+ constructor(config = {}) {
10
+ this.config = config;
11
+ this.name = 'gemini';
12
+ this.displayName = 'Gemini CLI';
13
+ }
14
+ async resolveExecutable() {
15
+ if (this.config.geminiBinPath)
16
+ return this.config.geminiBinPath;
17
+ // Use enhanced detection service
18
+ const detectionService = getProviderDetectionService();
19
+ const result = await detectionService.detectProvider('gemini');
20
+ if (result.found && result.path) {
21
+ return result.path;
22
+ }
23
+ // Fallback: try old settings-based approach for backwards compatibility
24
+ const settingsBinPath = (() => {
25
+ try {
26
+ const svc = getSettingsService();
27
+ const s = svc.getSettings();
28
+ return s?.agents?.providers?.gemini?.binPath;
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ })();
34
+ if (settingsBinPath?.trim())
35
+ return settingsBinPath.trim();
36
+ // Default to binary name (resolve via PATH)
37
+ return 'gemini';
38
+ }
39
+ normalizeReasoningEffort(value) {
40
+ if (!value)
41
+ return undefined;
42
+ const normalized = value.toLowerCase();
43
+ if (normalized === 'minimal')
44
+ return 'low';
45
+ const allowed = ['low', 'medium', 'high'];
46
+ return allowed.includes(normalized)
47
+ ? normalized
48
+ : undefined;
49
+ }
50
+ resolveModelSpec(model, fallbackEffort) {
51
+ const fallback = this.normalizeReasoningEffort(fallbackEffort);
52
+ if (!model) {
53
+ return { reasoning: fallback };
54
+ }
55
+ const trimmed = model.trim();
56
+ const normalized = trimmed.toLowerCase();
57
+ // Recognize a few common Gemini model aliases; keep passthrough for others
58
+ const knownPrefixes = [
59
+ 'gemini-3-pro-preview',
60
+ 'gemini-3-flash-preview',
61
+ 'gemini-2.5-pro',
62
+ 'gemini-2.5-flash',
63
+ 'gemini-2.5-flash-lite',
64
+ 'gemini-3',
65
+ 'gemini-2.5',
66
+ 'gemini',
67
+ ];
68
+ const isKnown = knownPrefixes.some((p) => normalized.startsWith(p));
69
+ return {
70
+ cliModel: isKnown ? trimmed : trimmed,
71
+ requestedModel: trimmed,
72
+ reasoning: fallback,
73
+ };
74
+ }
75
+ async *execute(prompt, options) {
76
+ // Effective options
77
+ const cwd = options?.workingDirectory || this.config.defaultWorkingDirectory || process.cwd();
78
+ const resolvedModel = this.resolveModelSpec(options?.model || this.config.defaultModel, options?.effort);
79
+ const model = resolvedModel.cliModel;
80
+ const images = (options?.images || []).filter(Boolean);
81
+ const timeoutMs = options?.timeout ?? this.config.defaultTimeoutMs ?? 10 * 60 * 1000; // 10m
82
+ const temperature = options?.temperature;
83
+ const systemPrompt = options?.systemPrompt?.trim();
84
+ const appendSystemPrompt = options?.appendSystemPrompt?.trim();
85
+ const promptSegments = [];
86
+ if (systemPrompt)
87
+ promptSegments.push(systemPrompt);
88
+ if (appendSystemPrompt)
89
+ promptSegments.push(appendSystemPrompt);
90
+ promptSegments.push(prompt);
91
+ const effectivePrompt = promptSegments.join('\n\n');
92
+ // Build argv for `gemini` CLI. Use a simple subcommand 'chat' with flags; passthrough prompt as arg.
93
+ const argv = [];
94
+ // Prefer a subcommand if used by the CLI; otherwise just supply flags
95
+ // We keep it generic so detection/validation works even if CLI differs
96
+ if (model) {
97
+ argv.push('--model', model);
98
+ }
99
+ if (typeof temperature === 'number') {
100
+ argv.push('--temperature', String(temperature));
101
+ }
102
+ if (images.length) {
103
+ argv.push('--image', images.join(','));
104
+ }
105
+ // YOLO mode auto-approves tool calls to allow code edits without prompts
106
+ argv.push('--yolo');
107
+ // Push the prompt last (many CLIs accept final arg as the prompt)
108
+ argv.push(effectivePrompt);
109
+ const cmd = await this.resolveExecutable();
110
+ const child = spawn(cmd, argv, {
111
+ cwd,
112
+ env: { ...process.env },
113
+ stdio: ['ignore', 'pipe', 'pipe'],
114
+ });
115
+ // Emit an init/system message with effective command
116
+ const init = {
117
+ type: 'init',
118
+ timestamp: new Date().toISOString(),
119
+ sessionId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
120
+ model: resolvedModel.requestedModel || model || 'default',
121
+ provider: this.name,
122
+ };
123
+ yield init;
124
+ yield {
125
+ type: 'system',
126
+ timestamp: new Date().toISOString(),
127
+ content: `Running: ${cmd} ${argv
128
+ .map((a) => (a.includes(' ') ? '"' + a + '"' : a))
129
+ .join(' ')} (cwd=${cwd})`,
130
+ metadata: {
131
+ provider: this.name,
132
+ images,
133
+ systemPrompt,
134
+ appendSystemPrompt,
135
+ },
136
+ };
137
+ let stdoutBuf = '';
138
+ let finished = false;
139
+ let lastFlushedIndex = 0;
140
+ let lineCarry = '';
141
+ const flushNewOutput = () => {
142
+ const newChunk = stdoutBuf.slice(lastFlushedIndex);
143
+ if (!newChunk)
144
+ return;
145
+ lastFlushedIndex = stdoutBuf.length;
146
+ lineCarry += newChunk;
147
+ const parts = lineCarry.split(/\r?\n/);
148
+ lineCarry = parts.pop() || '';
149
+ return parts.map((l) => l.trimEnd());
150
+ };
151
+ const onAbort = () => {
152
+ try {
153
+ child.kill('SIGTERM');
154
+ }
155
+ catch (err) {
156
+ void err; // ignore if process already exited
157
+ }
158
+ setTimeout(() => {
159
+ if (!finished) {
160
+ try {
161
+ child.kill('SIGKILL');
162
+ }
163
+ catch (err) {
164
+ void err; // ignore if process already exited
165
+ }
166
+ }
167
+ }, 3000);
168
+ };
169
+ if (options?.abortSignal) {
170
+ if (options.abortSignal.aborted)
171
+ onAbort();
172
+ else
173
+ options.abortSignal.addEventListener('abort', onAbort, { once: true });
174
+ }
175
+ const guard = setTimeout(() => onAbort(), timeoutMs);
176
+ child.stdout?.on('data', (data) => {
177
+ stdoutBuf += data.toString('utf8');
178
+ });
179
+ const exitPromise = new Promise((resolve) => {
180
+ child.on('exit', (code, signal) => resolve({ code, signal }));
181
+ });
182
+ let exitStatus;
183
+ while (!finished) {
184
+ const tick = new Promise((r) => setTimeout(r, 150));
185
+ const exit = await Promise.race([exitPromise, tick]);
186
+ if (exit && typeof exit.code !== 'undefined') {
187
+ const lines = flushNewOutput();
188
+ if (lines && lines.length) {
189
+ for (const text of lines) {
190
+ if (!text)
191
+ continue;
192
+ yield { type: 'assistant', timestamp: new Date().toISOString(), content: text };
193
+ }
194
+ }
195
+ finished = true;
196
+ exitStatus = exit;
197
+ break;
198
+ }
199
+ const lines = flushNewOutput();
200
+ if (lines && lines.length) {
201
+ for (const text of lines) {
202
+ if (!text)
203
+ continue;
204
+ yield { type: 'assistant', timestamp: new Date().toISOString(), content: text };
205
+ }
206
+ }
207
+ }
208
+ const exit = exitStatus ?? (await exitPromise);
209
+ finished = true;
210
+ clearTimeout(guard);
211
+ if (lineCarry.trim()) {
212
+ yield {
213
+ type: 'assistant',
214
+ timestamp: new Date().toISOString(),
215
+ content: lineCarry.trimEnd(),
216
+ };
217
+ lineCarry = '';
218
+ }
219
+ const success = (exit.code ?? 1) === 0 && !exit.signal;
220
+ const remainingStdout = stdoutBuf.trim();
221
+ const resultMsg = {
222
+ type: 'result',
223
+ timestamp: new Date().toISOString(),
224
+ success,
225
+ result: success ? (remainingStdout || '').toString() : undefined,
226
+ error: success
227
+ ? undefined
228
+ : `gemini exited ${exit.signal ? 'by ' + exit.signal : 'with code ' + exit.code}`,
229
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
230
+ };
231
+ yield resultMsg;
232
+ }
233
+ async detectAvailableModels() {
234
+ const capabilities = ['code', 'analysis', 'tools', 'vision', 'image', 'reasoning'];
235
+ const makeModel = (id, displayName) => ({
236
+ id,
237
+ name: id,
238
+ displayName,
239
+ provider: this.name,
240
+ contextWindow: 128000,
241
+ maxOutputTokens: 8192,
242
+ capabilities,
243
+ });
244
+ return [
245
+ makeModel('gemini-3-pro-preview', 'Gemini 3 Pro Preview'),
246
+ makeModel('gemini-3-flash-preview', 'Gemini 3 Flash Preview'),
247
+ makeModel('gemini-2.5-pro', 'Gemini 2.5 Pro'),
248
+ makeModel('gemini-2.5-flash', 'Gemini 2.5 Flash'),
249
+ makeModel('gemini-2.5-flash-lite', 'Gemini 2.5 Flash Lite'),
250
+ ];
251
+ }
252
+ async validateSetup() {
253
+ try {
254
+ const detectionService = getProviderDetectionService();
255
+ const result = await detectionService.detectProvider('gemini');
256
+ if (!result.found) {
257
+ return {
258
+ available: false,
259
+ error: result.error ||
260
+ 'Gemini CLI not found. Install the Gemini CLI and ensure it is available on PATH as "gemini" or set VIBEMAN_GEMINI_BIN.',
261
+ models: [],
262
+ capabilities: this.getCapabilities(),
263
+ };
264
+ }
265
+ return {
266
+ available: true,
267
+ models: await this.detectAvailableModels(),
268
+ capabilities: this.getCapabilities(),
269
+ };
270
+ }
271
+ catch (error) {
272
+ return {
273
+ available: false,
274
+ error: error instanceof Error ? error.message : String(error),
275
+ models: [],
276
+ capabilities: this.getCapabilities(),
277
+ };
278
+ }
279
+ }
280
+ getCapabilities() {
281
+ return {
282
+ streaming: true,
283
+ tools: true,
284
+ functionCalling: false,
285
+ vision: true,
286
+ codeExecution: true,
287
+ maxContextWindow: 128000,
288
+ maxOutputTokens: 8192,
289
+ };
290
+ }
291
+ }
@@ -2,8 +2,8 @@
2
2
  * AI Providers Module
3
3
  * Export all provider types and implementations
4
4
  */
5
- export * from './types.js';
6
5
  export { ClaudeCodeAdapter } from './claude-code-adapter.js';
7
- export type { ClaudeCodeConfig } from './claude-code-adapter.js';
8
6
  export { CodexCliProvider } from './codex-cli-provider.js';
9
- export type { CodexCliConfig } from './codex-cli-provider.js';
7
+ export { GeminiCliProvider } from './gemini-cli-provider.js';
8
+ export { AmpCliProvider } from './amp-cli-provider.js';
9
+ export * from './types.js';
@@ -2,6 +2,8 @@
2
2
  * AI Providers Module
3
3
  * Export all provider types and implementations
4
4
  */
5
- export * from './types.js';
6
5
  export { ClaudeCodeAdapter } from './claude-code-adapter.js';
7
6
  export { CodexCliProvider } from './codex-cli-provider.js';
7
+ export { GeminiCliProvider } from './gemini-cli-provider.js';
8
+ export { AmpCliProvider } from './amp-cli-provider.js';
9
+ export * from './types.js';
@@ -43,9 +43,12 @@ export interface ExecutionOptions {
43
43
  permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions';
44
44
  /** Optional list of image file paths to attach (Codex CLI: --image) */
45
45
  images?: string[];
46
- /** Optional effort hint for reasoning presets (e.g., 'minimal'|'low'|'medium'|'high'); informational only */
46
+ /** Optional effort hint for reasoning presets (e.g., 'minimal'|'low'|'medium'|'high'|'extra_high'); informational only */
47
47
  effort?: string;
48
- /** Enable Codex CLI sandbox bypass flag when running automation that must edit files */
48
+ /**
49
+ * Enable Codex CLI full-auto + danger-full-access sandbox mode (>= v0.73) so automation
50
+ * can edit files and run privileged commands.
51
+ */
49
52
  dangerouslyBypassApprovalsAndSandbox?: boolean;
50
53
  }
51
54
  /**
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect, vi, afterEach } from 'vitest';
2
+ // Toggle real CLI integration via env
3
+ const USE_REAL = process.env.VITEST_USE_REAL_AMP === 'true';
4
+ describe('AmpCliProvider (mocked)', () => {
5
+ afterEach(() => {
6
+ vi.resetAllMocks();
7
+ });
8
+ it('streams stdout, passes flags, and returns result', async () => {
9
+ vi.resetModules();
10
+ vi.doMock('child_process', () => {
11
+ let captured = [];
12
+ return {
13
+ __captured: () => captured,
14
+ spawn: (cmd, args, opts) => {
15
+ captured = [cmd, args, opts];
16
+ const { EventEmitter } = require('events');
17
+ const stdout = new EventEmitter();
18
+ const stderr = new EventEmitter();
19
+ const proc = new EventEmitter();
20
+ proc.stdout = stdout;
21
+ proc.stderr = stderr;
22
+ setTimeout(() => stdout.emit('data', Buffer.from('Hello from Amp\n')), 10);
23
+ setTimeout(() => proc.emit('exit', 0, null), 20);
24
+ return proc;
25
+ },
26
+ exec: vi.fn(),
27
+ execFile: vi.fn(),
28
+ };
29
+ });
30
+ const { AmpCliProvider } = await import('./ai-providers/amp-cli-provider.js');
31
+ const provider = new AmpCliProvider({ defaultTimeoutMs: 1000, ampBinPath: 'amp' });
32
+ const messages = [];
33
+ const iter = provider.execute('What is this project about?', {
34
+ workingDirectory: process.cwd(),
35
+ model: 'auto',
36
+ dangerouslyBypassApprovalsAndSandbox: true,
37
+ });
38
+ for await (const m of iter)
39
+ messages.push(m);
40
+ const resultMsg = messages.find((m) => m.type === 'result');
41
+ expect(!!resultMsg).toBe(true);
42
+ const assistantMsg = messages.find((m) => m.type === 'assistant');
43
+ if (assistantMsg && typeof assistantMsg.content === 'string') {
44
+ expect(assistantMsg.content.length).toBeGreaterThan(0);
45
+ }
46
+ else if (resultMsg && typeof resultMsg.result === 'string') {
47
+ expect(resultMsg.result.length).toBeGreaterThan(0);
48
+ }
49
+ const mockSpawn = await import('child_process');
50
+ const captured = mockSpawn.__captured();
51
+ expect(captured[0]).toBe('amp');
52
+ expect(Array.isArray(captured[1])).toBe(true);
53
+ expect(captured[1]).toContain('-x');
54
+ // Amp integration runs in auto mode (no explicit model flag)
55
+ expect(captured[1]).not.toContain('--use-sonnet');
56
+ // Check bypass
57
+ expect(captured[1]).toContain('--dangerously-allow-all');
58
+ // Check prompt passed as arg
59
+ const promptArg = captured[1][1];
60
+ expect(promptArg).toContain('What is this project about?');
61
+ });
62
+ it('prepends system prompts', async () => {
63
+ vi.resetModules();
64
+ vi.doMock('child_process', () => {
65
+ let captured = [];
66
+ return {
67
+ __captured: () => captured,
68
+ spawn: (cmd, args, opts) => {
69
+ captured = [cmd, args, opts];
70
+ const { EventEmitter } = require('events');
71
+ const stdout = new EventEmitter();
72
+ const proc = new EventEmitter();
73
+ proc.stdout = stdout;
74
+ setTimeout(() => stdout.emit('data', Buffer.from('Done\n')), 5);
75
+ setTimeout(() => proc.emit('exit', 0, null), 10);
76
+ return proc;
77
+ },
78
+ exec: vi.fn(),
79
+ execFile: vi.fn(),
80
+ };
81
+ });
82
+ const { AmpCliProvider } = await import('./ai-providers/amp-cli-provider.js');
83
+ const provider = new AmpCliProvider({ ampBinPath: 'amp' });
84
+ const iter = provider.execute('Implement feature', {
85
+ workingDirectory: '/tmp/project',
86
+ systemPrompt: 'Base system prompt',
87
+ appendSystemPrompt: 'Additional guidance',
88
+ });
89
+ for await (const _ of iter) {
90
+ // drain iterator
91
+ }
92
+ const mockSpawn = await import('child_process');
93
+ const captured = mockSpawn.__captured();
94
+ const promptArg = captured[1][1];
95
+ expect(promptArg).toContain('Base system prompt');
96
+ expect(promptArg).toContain('Additional guidance');
97
+ expect(promptArg).toContain('Implement feature');
98
+ });
99
+ });