vibeman 0.0.1 → 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 (71) hide show
  1. package/dist/index.js +5 -7
  2. package/dist/runtime/api/.tsbuildinfo +1 -1
  3. package/dist/runtime/api/agent/agent-service.d.ts +11 -13
  4. package/dist/runtime/api/agent/agent-service.js +25 -31
  5. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +2 -2
  6. package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +25 -36
  7. package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +48 -14
  8. package/dist/runtime/api/agent/ai-providers/types.d.ts +2 -0
  9. package/dist/runtime/api/agent/codex-cli-provider.test.js +37 -0
  10. package/dist/runtime/api/agent/parsers.d.ts +1 -0
  11. package/dist/runtime/api/agent/parsers.js +75 -8
  12. package/dist/runtime/api/agent/prompt-service.d.ts +14 -1
  13. package/dist/runtime/api/agent/prompt-service.js +123 -14
  14. package/dist/runtime/api/agent/prompt-service.test.d.ts +1 -0
  15. package/dist/runtime/api/agent/prompt-service.test.js +230 -0
  16. package/dist/runtime/api/agent/routing-policy.d.ts +14 -14
  17. package/dist/runtime/api/api/routers/ai.d.ts +6 -6
  18. package/dist/runtime/api/api/routers/ai.js +2 -17
  19. package/dist/runtime/api/api/routers/executions.d.ts +5 -5
  20. package/dist/runtime/api/api/routers/executions.js +12 -21
  21. package/dist/runtime/api/api/routers/provider-config.d.ts +165 -0
  22. package/dist/runtime/api/api/routers/provider-config.js +252 -0
  23. package/dist/runtime/api/api/routers/tasks.d.ts +10 -10
  24. package/dist/runtime/api/api/routers/workflows.d.ts +15 -16
  25. package/dist/runtime/api/api/routers/workflows.js +28 -26
  26. package/dist/runtime/api/api/routers/worktrees.d.ts +4 -5
  27. package/dist/runtime/api/api/routers/worktrees.js +11 -11
  28. package/dist/runtime/api/api/trpc.d.ts +18 -18
  29. package/dist/runtime/api/index.js +2 -10
  30. package/dist/runtime/api/lib/local-config.d.ts +245 -0
  31. package/dist/runtime/api/lib/local-config.js +288 -0
  32. package/dist/runtime/api/lib/provider-detection.d.ts +59 -0
  33. package/dist/runtime/api/lib/provider-detection.js +244 -0
  34. package/dist/runtime/api/lib/server/bootstrap.d.ts +38 -0
  35. package/dist/runtime/api/lib/server/bootstrap.js +197 -0
  36. package/dist/runtime/api/lib/server/project-root.js +24 -1
  37. package/dist/runtime/api/lib/trpc/server.d.ts +124 -31
  38. package/dist/runtime/api/lib/trpc/server.js +8 -8
  39. package/dist/runtime/api/lib/trpc/ws-server.js +2 -2
  40. package/dist/runtime/api/router.d.ts +125 -32
  41. package/dist/runtime/api/router.js +9 -31
  42. package/dist/runtime/api/settings-service.js +2 -0
  43. package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +8 -3
  44. package/dist/runtime/api/workflows/vibing-orchestrator.js +182 -183
  45. package/dist/runtime/web/.next/BUILD_ID +1 -1
  46. package/dist/runtime/web/.next/app-build-manifest.json +2 -2
  47. package/dist/runtime/web/.next/build-manifest.json +2 -2
  48. package/dist/runtime/web/.next/prerender-manifest.json +3 -3
  49. package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  50. package/dist/runtime/web/.next/server/app/_not-found.html +2 -2
  51. package/dist/runtime/web/.next/server/app/_not-found.rsc +5 -5
  52. package/dist/runtime/web/.next/server/app/api/health/route.js +1 -1
  53. package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  54. package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -1
  55. package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -1
  56. package/dist/runtime/web/.next/server/app/index.html +2 -2
  57. package/dist/runtime/web/.next/server/app/index.rsc +6 -6
  58. package/dist/runtime/web/.next/server/app/page.js +3 -3
  59. package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -1
  60. package/dist/runtime/web/.next/server/chunks/458.js +1 -1
  61. package/dist/runtime/web/.next/server/pages/404.html +2 -2
  62. package/dist/runtime/web/.next/server/pages/500.html +1 -1
  63. package/dist/runtime/web/.next/server/pages-manifest.json +1 -1
  64. package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -1
  65. package/dist/runtime/web/.next/static/chunks/app/{layout-dc0cfd29075b2160.js → layout-8435322f09fd0975.js} +1 -1
  66. package/dist/runtime/web/.next/static/chunks/app/page-8c3ba579efc6f918.js +1 -0
  67. package/dist/tsconfig.tsbuildinfo +1 -1
  68. package/package.json +5 -1
  69. package/dist/runtime/web/.next/static/chunks/app/page-f34a8b196b18850b.js +0 -1
  70. /package/dist/runtime/web/.next/static/{1HR8N0rJkCvFRtbTPJMyH → mRpNgPfbYR_0wrODzlg_4}/_buildManifest.js +0 -0
  71. /package/dist/runtime/web/.next/static/{1HR8N0rJkCvFRtbTPJMyH → mRpNgPfbYR_0wrODzlg_4}/_ssgManifest.js +0 -0
@@ -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,38 @@
1
+ /**
2
+ * Server Bootstrap - Async-first service initialization
3
+ *
4
+ * Provides ordered initialization of core services before router creation,
5
+ * ensuring all dependencies are ready for synchronous access during runtime.
6
+ */
7
+ import { getSettingsService } from '../../settings-service.js';
8
+ import { TaskService } from '../../tasks/task-service.js';
9
+ import { AgentService } from '../../agent/agent-service.js';
10
+ import { GitService } from '../../vcs/git-service.js';
11
+ import { WorktreeService } from '../../vcs/worktree-service.js';
12
+ import { VibingOrchestrator } from '../../workflows/vibing-orchestrator.js';
13
+ /**
14
+ * Initialize core services in proper dependency order.
15
+ * Must be called before any service getters or router creation.
16
+ */
17
+ export declare function bootstrap(): Promise<void>;
18
+ /**
19
+ * Ready-only sync getters - throw if called before bootstrap
20
+ */
21
+ export declare function getBootstrappedSettingsService(): ReturnType<typeof getSettingsService>;
22
+ export declare function getBootstrappedTaskService(): TaskService;
23
+ export declare function getBootstrappedAgentService(): AgentService;
24
+ export declare function getBootstrappedGitService(): GitService;
25
+ export declare function getBootstrappedWorktreeService(): WorktreeService;
26
+ export declare function getBootstrappedVibingOrchestrator(): VibingOrchestrator;
27
+ /**
28
+ * Get bootstrap completion promise for diagnostics/tests
29
+ */
30
+ export declare function getBootstrapReady(): Promise<void> | null;
31
+ /**
32
+ * Check if bootstrap has completed
33
+ */
34
+ export declare function isBootstrapComplete(): boolean;
35
+ /**
36
+ * Test bootstrap function that initializes against a temp workspace
37
+ */
38
+ export declare function testBootstrap(tempWorkspace?: string): Promise<void>;
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Server Bootstrap - Async-first service initialization
3
+ *
4
+ * Provides ordered initialization of core services before router creation,
5
+ * ensuring all dependencies are ready for synchronous access during runtime.
6
+ */
7
+ import { log } from '../logger.js';
8
+ import { getSettingsService } from '../../settings-service.js';
9
+ import { TaskService } from '../../tasks/task-service.js';
10
+ import { AgentService } from '../../agent/agent-service.js';
11
+ import { GitService } from '../../vcs/git-service.js';
12
+ import { VibingOrchestrator } from '../../workflows/vibing-orchestrator.js';
13
+ import { getProjectRoot, getVibeDir } from './project-root.js';
14
+ // Ready-only service instances - throw if accessed before bootstrap
15
+ let settingsService = null;
16
+ let taskService = null;
17
+ let agentService = null;
18
+ let gitService = null;
19
+ let worktreeService = null;
20
+ let vibingOrchestrator = null;
21
+ // Bootstrap completion promise for diagnostics/tests
22
+ let ready = null;
23
+ let isBootstrapped = false;
24
+ // Default config fallback for VibingOrchestrator
25
+ const getDefaultVibingConfig = () => ({
26
+ autoQualityChecks: true,
27
+ requireHumanApproval: true,
28
+ aiCodeReview: true,
29
+ retryPolicy: { maxImplementationAttempts: 3 },
30
+ autoCommit: false,
31
+ });
32
+ /**
33
+ * Initialize core services in proper dependency order.
34
+ * Must be called before any service getters or router creation.
35
+ */
36
+ export async function bootstrap() {
37
+ if (isBootstrapped) {
38
+ log.info('Bootstrap already completed, skipping');
39
+ return;
40
+ }
41
+ if (ready) {
42
+ log.info('Bootstrap already in progress, waiting...');
43
+ return ready;
44
+ }
45
+ const startTime = Date.now();
46
+ log.info('Starting server bootstrap', {}, 'bootstrap');
47
+ ready = (async () => {
48
+ try {
49
+ // Stage 1: Settings Service (foundation)
50
+ const settingsStart = Date.now();
51
+ settingsService = getSettingsService();
52
+ log.info('Settings service initialized', {
53
+ duration: Date.now() - settingsStart,
54
+ }, 'bootstrap');
55
+ // Stage 2: Task Service (depends on settings for vibeDir)
56
+ const taskStart = Date.now();
57
+ const settingsVibeDir = (() => {
58
+ try {
59
+ const s = settingsService.getSettings();
60
+ return s?.development?.vibeDir;
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
65
+ })();
66
+ const vibeDir = settingsVibeDir ?? getVibeDir();
67
+ taskService = new TaskService(vibeDir);
68
+ await taskService.ensureInitialized();
69
+ log.info('Task service initialized', {
70
+ vibeDir,
71
+ duration: Date.now() - taskStart,
72
+ }, 'bootstrap');
73
+ // Stage 3: Git Service (independent)
74
+ const gitStart = Date.now();
75
+ const projectRoot = getProjectRoot();
76
+ const settingsDefaultBranch = (() => {
77
+ try {
78
+ const s = settingsService.getSettings();
79
+ return s?.development?.git?.defaultBranch;
80
+ }
81
+ catch {
82
+ return undefined;
83
+ }
84
+ })();
85
+ const gitConfig = {
86
+ defaultBranch: settingsDefaultBranch || 'main',
87
+ };
88
+ gitService = new GitService(projectRoot, gitConfig);
89
+ log.info('Git service initialized', {
90
+ projectRoot,
91
+ defaultBranch: gitConfig.defaultBranch,
92
+ duration: Date.now() - gitStart,
93
+ }, 'bootstrap');
94
+ // Stage 4: Agent Service (depends on task service)
95
+ const agentStart = Date.now();
96
+ agentService = new AgentService(taskService, projectRoot);
97
+ log.info('Agent service initialized', {
98
+ duration: Date.now() - agentStart,
99
+ }, 'bootstrap');
100
+ // Stage 5: Worktree Service (depends on git service, settings)
101
+ const worktreeStart = Date.now();
102
+ worktreeService = agentService.getWorktreeService();
103
+ await worktreeService.initialize();
104
+ log.info('Worktree service initialized', {
105
+ duration: Date.now() - worktreeStart,
106
+ }, 'bootstrap');
107
+ // Stage 6: VibingOrchestrator (depends on all previous services)
108
+ const orchestratorStart = Date.now();
109
+ vibingOrchestrator = new VibingOrchestrator(taskService, agentService, worktreeService, getDefaultVibingConfig(), gitService);
110
+ await vibingOrchestrator.initialize();
111
+ log.info('VibingOrchestrator initialized', {
112
+ duration: Date.now() - orchestratorStart,
113
+ }, 'bootstrap');
114
+ isBootstrapped = true;
115
+ const totalDuration = Date.now() - startTime;
116
+ log.info('Server bootstrap completed', {
117
+ totalDuration,
118
+ stages: 6,
119
+ }, 'bootstrap');
120
+ }
121
+ catch (error) {
122
+ log.error('Server bootstrap failed', error, 'bootstrap');
123
+ throw error;
124
+ }
125
+ })();
126
+ return ready;
127
+ }
128
+ /**
129
+ * Ready-only sync getters - throw if called before bootstrap
130
+ */
131
+ export function getBootstrappedSettingsService() {
132
+ if (!settingsService) {
133
+ throw new Error('Settings service not available - bootstrap() must be called first');
134
+ }
135
+ return settingsService;
136
+ }
137
+ export function getBootstrappedTaskService() {
138
+ if (!taskService) {
139
+ throw new Error('Task service not available - bootstrap() must be called first');
140
+ }
141
+ return taskService;
142
+ }
143
+ export function getBootstrappedAgentService() {
144
+ if (!agentService) {
145
+ throw new Error('Agent service not available - bootstrap() must be called first');
146
+ }
147
+ return agentService;
148
+ }
149
+ export function getBootstrappedGitService() {
150
+ if (!gitService) {
151
+ throw new Error('Git service not available - bootstrap() must be called first');
152
+ }
153
+ return gitService;
154
+ }
155
+ export function getBootstrappedWorktreeService() {
156
+ if (!worktreeService) {
157
+ throw new Error('Worktree service not available - bootstrap() must be called first');
158
+ }
159
+ return worktreeService;
160
+ }
161
+ export function getBootstrappedVibingOrchestrator() {
162
+ if (!vibingOrchestrator) {
163
+ throw new Error('VibingOrchestrator not available - bootstrap() must be called first');
164
+ }
165
+ return vibingOrchestrator;
166
+ }
167
+ /**
168
+ * Get bootstrap completion promise for diagnostics/tests
169
+ */
170
+ export function getBootstrapReady() {
171
+ return ready;
172
+ }
173
+ /**
174
+ * Check if bootstrap has completed
175
+ */
176
+ export function isBootstrapComplete() {
177
+ return isBootstrapped;
178
+ }
179
+ /**
180
+ * Test bootstrap function that initializes against a temp workspace
181
+ */
182
+ export async function testBootstrap(tempWorkspace) {
183
+ // Reset bootstrap state for testing
184
+ settingsService = null;
185
+ taskService = null;
186
+ agentService = null;
187
+ gitService = null;
188
+ worktreeService = null;
189
+ vibingOrchestrator = null;
190
+ ready = null;
191
+ isBootstrapped = false;
192
+ // TODO: Implement temp workspace logic if needed
193
+ if (tempWorkspace) {
194
+ log.info('Test bootstrap with temp workspace', { tempWorkspace }, 'bootstrap');
195
+ }
196
+ return bootstrap();
197
+ }
@@ -8,7 +8,30 @@ import path from 'path';
8
8
  export function getProjectRoot() {
9
9
  const envRoot = process.env.VIBEMAN_PROJECT_ROOT;
10
10
  if (envRoot && envRoot.trim().length > 0) {
11
- return path.resolve(envRoot);
11
+ const trimmedRoot = envRoot.trim();
12
+ if (path.isAbsolute(trimmedRoot)) {
13
+ return path.normalize(trimmedRoot);
14
+ }
15
+ const baseCandidates = [
16
+ process.env.VIBEMAN_PROJECT_ROOT_BASE,
17
+ process.env.INIT_CWD,
18
+ process.env.PWD,
19
+ process.cwd(),
20
+ ].filter((candidate) => !!candidate && candidate.trim().length > 0);
21
+ let fallbackResolved = null;
22
+ for (const base of baseCandidates) {
23
+ const resolved = path.resolve(base, trimmedRoot);
24
+ if (!fallbackResolved) {
25
+ fallbackResolved = resolved;
26
+ }
27
+ if (fs.existsSync(resolved)) {
28
+ return resolved;
29
+ }
30
+ }
31
+ if (fallbackResolved) {
32
+ return fallbackResolved;
33
+ }
34
+ return path.resolve(trimmedRoot);
12
35
  }
13
36
  // First preference: directory that contains a .git directory/file
14
37
  let dir = process.cwd();