vibeman 0.0.3 → 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.
- package/dist/index.js +3 -3
- package/dist/runtime/api/.tsbuildinfo +1 -1
- package/dist/runtime/api/agent/agent-service.d.ts +4 -0
- package/dist/runtime/api/agent/agent-service.js +62 -3
- package/dist/runtime/api/agent/ai-providers/amp-cli-provider.d.ts +38 -0
- package/dist/runtime/api/agent/ai-providers/amp-cli-provider.js +268 -0
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +40 -12
- package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.d.ts +24 -0
- package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.js +291 -0
- package/dist/runtime/api/agent/ai-providers/index.d.ts +3 -3
- package/dist/runtime/api/agent/ai-providers/index.js +3 -1
- package/dist/runtime/api/agent/ai-providers/types.d.ts +5 -2
- package/dist/runtime/api/agent/amp-cli-provider.test.d.ts +1 -0
- package/dist/runtime/api/agent/amp-cli-provider.test.js +99 -0
- package/dist/runtime/api/agent/codex-cli-provider.test.js +10 -8
- package/dist/runtime/api/agent/prompt-service.js +108 -105
- package/dist/runtime/api/agent/prompt-service.test.js +35 -0
- package/dist/runtime/api/agent/routing-policy.d.ts +2 -2
- package/dist/runtime/api/agent/routing-policy.test.js +4 -4
- package/dist/runtime/api/api/routers/ai.d.ts +3 -3
- package/dist/runtime/api/api/routers/executions.d.ts +2 -7
- package/dist/runtime/api/api/routers/executions.js +2 -2
- package/dist/runtime/api/api/routers/provider-config.d.ts +34 -0
- package/dist/runtime/api/api/routers/settings.d.ts +19 -0
- package/dist/runtime/api/api/routers/settings.js +16 -0
- package/dist/runtime/api/api/routers/tasks.d.ts +9 -9
- package/dist/runtime/api/api/routers/workflows.d.ts +12 -12
- package/dist/runtime/api/api/routers/worktrees.d.ts +2 -2
- package/dist/runtime/api/api/trpc.d.ts +16 -16
- package/dist/runtime/api/lib/local-config.d.ts +94 -4
- package/dist/runtime/api/lib/local-config.js +16 -0
- package/dist/runtime/api/lib/provider-detection.d.ts +2 -0
- package/dist/runtime/api/lib/provider-detection.js +83 -1
- package/dist/runtime/api/lib/server/vibeman-info.d.ts +5 -0
- package/dist/runtime/api/lib/server/vibeman-info.js +85 -0
- package/dist/runtime/api/lib/trpc/server.d.ts +63 -33
- package/dist/runtime/api/persistence/execution-log-persistence.d.ts +1 -1
- package/dist/runtime/api/persistence/execution-log-persistence.js +19 -3
- package/dist/runtime/api/router.d.ts +63 -33
- package/dist/runtime/api/settings-service.js +31 -14
- package/dist/runtime/api/tasks/task-file-parser.d.ts +1 -0
- package/dist/runtime/api/tasks/task-file-parser.js +20 -1
- package/dist/runtime/api/tasks/task-updater.d.ts +62 -0
- package/dist/runtime/api/tasks/task-updater.js +260 -0
- package/dist/runtime/api/tasks/task-updater.test.d.ts +1 -0
- package/dist/runtime/api/tasks/task-updater.test.js +303 -0
- package/dist/runtime/api/types/index.d.ts +1 -1
- package/dist/runtime/api/types/settings.d.ts +17 -6
- package/dist/runtime/api/vcs/git-service.d.ts +9 -0
- package/dist/runtime/api/vcs/git-service.js +23 -0
- package/dist/runtime/api/vcs/worktree-service.d.ts +1 -1
- package/dist/runtime/api/vcs/worktree-service.js +22 -10
- package/dist/runtime/api/workflows/quality-pipeline.js +2 -1
- package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +93 -5
- package/dist/runtime/api/workflows/vibing-orchestrator.js +774 -203
- package/dist/runtime/api/workflows/workflow-effects.d.ts +45 -0
- package/dist/runtime/api/workflows/workflow-effects.js +49 -0
- package/dist/runtime/api/workflows/workflow-reconciler.d.ts +65 -0
- package/dist/runtime/api/workflows/workflow-reconciler.js +226 -0
- package/dist/runtime/api/workflows/workflow-reducer.d.ts +26 -0
- package/dist/runtime/api/workflows/workflow-reducer.js +288 -0
- package/dist/runtime/api/workflows/workflow-reducer.test.d.ts +1 -0
- package/dist/runtime/api/workflows/workflow-reducer.test.js +247 -0
- package/dist/runtime/api/workflows/workflow-schema.d.ts +546 -0
- package/dist/runtime/api/workflows/workflow-schema.js +256 -0
- package/dist/runtime/web/.next/BUILD_ID +1 -1
- package/dist/runtime/web/.next/app-build-manifest.json +50 -50
- package/dist/runtime/web/.next/app-path-routes-manifest.json +1 -1
- package/dist/runtime/web/.next/build-manifest.json +14 -14
- package/dist/runtime/web/.next/prerender-manifest.json +3 -3
- package/dist/runtime/web/.next/react-loadable-manifest.json +2 -33
- package/dist/runtime/web/.next/required-server-files.json +5 -5
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +1 -1
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/_not-found/page.js +2 -2
- package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/_not-found.html +2 -2
- package/dist/runtime/web/.next/server/app/_not-found.rsc +12 -12
- package/dist/runtime/web/.next/server/app/api/health/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/index.html +2 -2
- package/dist/runtime/web/.next/server/app/index.rsc +15 -15
- package/dist/runtime/web/.next/server/app/page.js +27 -62
- package/dist/runtime/web/.next/server/app/page.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app-paths-manifest.json +1 -1
- package/dist/runtime/web/.next/server/chunks/210.js +1 -0
- package/dist/runtime/web/.next/server/chunks/291.js +18 -0
- package/dist/runtime/web/.next/server/chunks/552.js +22 -0
- package/dist/runtime/web/.next/server/chunks/780.js +1 -0
- package/dist/runtime/web/.next/server/chunks/905.js +6 -0
- package/dist/runtime/web/.next/server/chunks/98.js +1 -0
- package/dist/runtime/web/.next/server/middleware-build-manifest.js +1 -1
- package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/runtime/web/.next/server/pages/404.html +2 -2
- package/dist/runtime/web/.next/server/pages/500.html +1 -1
- package/dist/runtime/web/.next/server/pages/_app.js +1 -1
- package/dist/runtime/web/.next/server/pages/_app.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/pages/_document.js +1 -1
- package/dist/runtime/web/.next/server/pages/_document.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/pages/_error.js +9 -9
- package/dist/runtime/web/.next/server/pages/_error.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/pages-manifest.json +1 -1
- package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -1
- package/dist/runtime/web/.next/server/webpack-runtime.js +1 -1
- package/dist/runtime/web/.next/static/{5_15u1WQCxN1_eHZpldCv → LJFZk_8tvKFN_Ee4HqUuM}/_buildManifest.js +1 -1
- package/dist/runtime/web/.next/static/chunks/05c91ade-7d09b2b280adffd1.js +1 -0
- package/dist/runtime/web/.next/static/chunks/201-51bef3fa8c832e2e.js +1 -0
- package/dist/runtime/web/.next/static/chunks/524-89747ed9b0294f8a.js +1 -0
- package/dist/runtime/web/.next/static/chunks/554-8bec6e9cca6acc67.js +1 -0
- package/dist/runtime/web/.next/static/chunks/764.86e9503a69d45a85.js +1 -0
- package/dist/runtime/web/.next/static/chunks/{87c73c54-09e1ba5c70e60a51.js → 7ab4dc20-239138e0ae7af24a.js} +1 -1
- package/dist/runtime/web/.next/static/chunks/905-342391e3d3a3678f.js +20 -0
- package/dist/runtime/web/.next/static/chunks/a8a5ce16-4edea7df2d9b544a.js +79 -0
- package/dist/runtime/web/.next/static/chunks/{8bb4d8db-3e2aa02b0a2384b9.js → ad74d572-4c1b162e2c15acaa.js} +1 -1
- package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-7b752a8641f96c1f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/_not-found/page-34e66b251c2b5044.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-7b752a8641f96c1f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-7b752a8641f96c1f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-7b752a8641f96c1f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/layout-df9ac93cb02b2385.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/page-6610743f7de5f92a.js +1 -0
- package/dist/runtime/web/.next/static/chunks/c25e0690-e9b798b8de667da1.js +1 -0
- package/dist/runtime/web/.next/static/chunks/framework-57157ec4d37f64aa.js +1 -0
- package/dist/runtime/web/.next/static/chunks/main-app-156cc0c60371bd78.js +1 -0
- package/dist/runtime/web/.next/static/chunks/main-df25d367c47b1fec.js +1 -0
- package/dist/runtime/web/.next/static/chunks/pages/_app-9f629a5e1131d19f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/pages/_error-9238238274c7efcd.js +1 -0
- package/dist/runtime/web/.next/static/chunks/webpack-cd50e39b423d1808.js +1 -0
- package/dist/runtime/web/.next/static/css/4fbf378a264bd4ea.css +1 -0
- package/dist/runtime/web/package.json +8 -8
- package/dist/runtime/web/server.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -37
- package/dist/runtime/api/lib/trpc/client.d.ts +0 -1
- package/dist/runtime/api/lib/trpc/client.js +0 -5
- package/dist/runtime/web/.next/server/chunks/217.js +0 -1
- package/dist/runtime/web/.next/server/chunks/383.js +0 -6
- package/dist/runtime/web/.next/server/chunks/458.js +0 -1
- package/dist/runtime/web/.next/server/chunks/576.js +0 -18
- package/dist/runtime/web/.next/server/chunks/635.js +0 -22
- package/dist/runtime/web/.next/server/chunks/761.js +0 -1
- package/dist/runtime/web/.next/server/chunks/777.js +0 -3
- package/dist/runtime/web/.next/server/chunks/825.js +0 -1
- package/dist/runtime/web/.next/server/chunks/838.js +0 -1
- package/dist/runtime/web/.next/server/chunks/973.js +0 -15
- package/dist/runtime/web/.next/static/chunks/18-15c10d3288afef2e.js +0 -1
- package/dist/runtime/web/.next/static/chunks/1c0ca389.537bbe362e3ffbd9.js +0 -3
- package/dist/runtime/web/.next/static/chunks/22747d63-ad5da0c19f4cfe41.js +0 -71
- package/dist/runtime/web/.next/static/chunks/355.056c2645878a799a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/420.a5ccf151c9e2b2f1.js +0 -1
- package/dist/runtime/web/.next/static/chunks/439.1be0c6242fd248d5.js +0 -15
- package/dist/runtime/web/.next/static/chunks/440.c52e7c0f797e22b2.js +0 -1
- package/dist/runtime/web/.next/static/chunks/575-e2478287c27da87b.js +0 -1
- package/dist/runtime/web/.next/static/chunks/691.920d88c115087314.js +0 -1
- package/dist/runtime/web/.next/static/chunks/765-e838910065b50c3d.js +0 -1
- package/dist/runtime/web/.next/static/chunks/823-6f371a6e829adbba.js +0 -63
- package/dist/runtime/web/.next/static/chunks/891cff7f.0f71fc028f87e683.js +0 -1
- package/dist/runtime/web/.next/static/chunks/9af238c7-271a911d4e99ab18.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-751c9265a65409e5.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/_not-found/page-1cb74d1cba27d0ab.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-751c9265a65409e5.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-751c9265a65409e5.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-751c9265a65409e5.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/layout-8435322f09fd0975.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/page-9fe7d75095b4ccec.js +0 -1
- package/dist/runtime/web/.next/static/chunks/cac567b0-5b77dd12911823cd.js +0 -1
- package/dist/runtime/web/.next/static/chunks/framework-2518f1345b5b2806.js +0 -1
- package/dist/runtime/web/.next/static/chunks/main-17665e5e39de9a8a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/main-app-c0b0f5ba4f7f9d75.js +0 -1
- package/dist/runtime/web/.next/static/chunks/pages/_app-d6f6b3bbc3d81ee1.js +0 -1
- package/dist/runtime/web/.next/static/chunks/pages/_error-75a96cf1997cc3b9.js +0 -1
- package/dist/runtime/web/.next/static/chunks/webpack-c8de37305b4635cf.js +0 -1
- package/dist/runtime/web/.next/static/css/08c950681f1a9a92.css +0 -1
- /package/dist/runtime/web/.next/static/{5_15u1WQCxN1_eHZpldCv → LJFZk_8tvKFN_Ee4HqUuM}/_ssgManifest.js +0 -0
|
@@ -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
|
|
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
|
-
/**
|
|
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
+
});
|
|
@@ -35,7 +35,7 @@ describe('CodexCliProvider (mocked)', () => {
|
|
|
35
35
|
workingDirectory: process.cwd(),
|
|
36
36
|
images: ['a.png', 'b.jpg'],
|
|
37
37
|
effort: 'minimal',
|
|
38
|
-
model: 'gpt-5',
|
|
38
|
+
model: 'gpt-5.2',
|
|
39
39
|
});
|
|
40
40
|
for await (const m of iter)
|
|
41
41
|
messages.push(m);
|
|
@@ -55,7 +55,7 @@ describe('CodexCliProvider (mocked)', () => {
|
|
|
55
55
|
expect(captured[1]).toContain('a.png,b.jpg');
|
|
56
56
|
const modelIdx = captured[1].indexOf('--model');
|
|
57
57
|
expect(modelIdx).toBeGreaterThan(-1);
|
|
58
|
-
expect(captured[1][modelIdx + 1]).toBe('gpt-5');
|
|
58
|
+
expect(captured[1][modelIdx + 1]).toBe('gpt-5.2');
|
|
59
59
|
const configIdx = captured[1].indexOf('-c');
|
|
60
60
|
expect(configIdx).toBeGreaterThan(-1);
|
|
61
61
|
expect(captured[1][configIdx + 1]).toBe('model_reasoning_effort=low');
|
|
@@ -85,7 +85,7 @@ describe('CodexCliProvider (mocked)', () => {
|
|
|
85
85
|
const messages = [];
|
|
86
86
|
const iter = provider.execute('Solve with depth', {
|
|
87
87
|
workingDirectory: process.cwd(),
|
|
88
|
-
model: 'gpt-5-codex-high',
|
|
88
|
+
model: 'gpt-5.1-codex-max-extra-high',
|
|
89
89
|
});
|
|
90
90
|
for await (const m of iter)
|
|
91
91
|
messages.push(m);
|
|
@@ -93,10 +93,10 @@ describe('CodexCliProvider (mocked)', () => {
|
|
|
93
93
|
const captured = mockSpawn.__captured();
|
|
94
94
|
const modelIdx = captured[1].indexOf('--model');
|
|
95
95
|
expect(modelIdx).toBeGreaterThan(-1);
|
|
96
|
-
expect(captured[1][modelIdx + 1]).toBe('gpt-5-codex');
|
|
96
|
+
expect(captured[1][modelIdx + 1]).toBe('gpt-5.1-codex-max');
|
|
97
97
|
const configIdx = captured[1].indexOf('-c');
|
|
98
98
|
expect(configIdx).toBeGreaterThan(-1);
|
|
99
|
-
expect(captured[1][configIdx + 1]).toBe('model_reasoning_effort=
|
|
99
|
+
expect(captured[1][configIdx + 1]).toBe('model_reasoning_effort=extra_high');
|
|
100
100
|
});
|
|
101
101
|
it('prepends system prompts and toggles sandbox bypass flag', async () => {
|
|
102
102
|
vi.resetModules();
|
|
@@ -133,9 +133,11 @@ describe('CodexCliProvider (mocked)', () => {
|
|
|
133
133
|
const captured = mockSpawn.__captured();
|
|
134
134
|
expect(captured[1][1]).toContain('Base system prompt');
|
|
135
135
|
expect(captured[1][1]).toContain('Additional guidance');
|
|
136
|
-
const
|
|
137
|
-
expect(
|
|
138
|
-
|
|
136
|
+
const fullAutoIdx = captured[1].indexOf('--full-auto');
|
|
137
|
+
expect(fullAutoIdx).toBeGreaterThan(-1);
|
|
138
|
+
const sandboxIdx = captured[1].indexOf('--sandbox');
|
|
139
|
+
expect(sandboxIdx).toBeGreaterThan(-1);
|
|
140
|
+
expect(captured[1][sandboxIdx + 1]).toBe('danger-full-access');
|
|
139
141
|
});
|
|
140
142
|
});
|
|
141
143
|
// Real integration (opt-in)
|