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
@@ -3,11 +3,18 @@
3
3
  * Handles provider selection per operation with fallbacks and configuration
4
4
  */
5
5
  import { z } from 'zod';
6
- import fs from 'fs/promises';
7
- import path from 'path';
8
6
  import { log } from '../lib/logger.js';
9
7
  import { getSettingsService } from '../settings-service.js';
10
- import { getVibeDir } from '../lib/server/project-root.js';
8
+ /**
9
+ * Operation types that support AI routing
10
+ */
11
+ const ROUTABLE_OPERATIONS = [
12
+ 'execute_task',
13
+ 'quality_checks',
14
+ 'ai_codereview',
15
+ 'ai_merge',
16
+ 'improve_task',
17
+ ];
11
18
  /**
12
19
  * Generation options for AI execution
13
20
  */
@@ -30,67 +37,83 @@ export const OperationConfigSchema = z.object({
30
37
  */
31
38
  export const RoutingPolicySchema = z.object({
32
39
  defaultProvider: z.string(),
33
- operations: z
34
- .record(z.enum(['execute_task', 'improve_task', 'ai_codereview', 'ai_merge']), OperationConfigSchema)
35
- .optional(),
40
+ operations: z.record(z.enum(ROUTABLE_OPERATIONS), OperationConfigSchema).optional(),
36
41
  });
42
+ export const ROUTABLE_OPERATION_LIST = ROUTABLE_OPERATIONS;
37
43
  /**
38
44
  * Routing Policy Manager
39
- * Manages AI provider routing policies with hot-reload capability
45
+ * Manages AI provider routing policies stored inside settings.json
40
46
  */
41
47
  export class RoutingPolicyManager {
42
48
  constructor() {
43
49
  this.policy = null;
44
- this.lastModified = 0;
45
- this.policyFilePath = path.join(getVibeDir(), 'ai-routing.json');
50
+ this.settingsService = getSettingsService();
51
+ // Invalidate cached policy when settings change
52
+ this.settingsService.on('settingsUpdated', () => {
53
+ this.policy = null;
54
+ });
46
55
  }
47
56
  /**
48
- * Get current effective policy with hot-reload
57
+ * Get current effective policy (lazy loads from settings)
49
58
  */
50
59
  async getPolicy() {
51
- await this.loadPolicyIfChanged();
52
- // Return default policy if none exists
53
60
  if (!this.policy) {
54
- const settings = getSettingsService().getSettings();
55
- return {
56
- defaultProvider: settings.agents.defaultProvider || 'claude-code',
57
- operations: {},
58
- };
61
+ this.policy = this.buildPolicyFromSettings();
59
62
  }
60
63
  return this.policy;
61
64
  }
62
65
  /**
63
- * Update routing policy and persist to disk
66
+ * Update routing policy and persist via settings service
64
67
  */
65
68
  async updatePolicy(updates) {
66
69
  const currentPolicy = await this.getPolicy();
67
- const newPolicy = {
68
- defaultProvider: updates.defaultProvider || currentPolicy.defaultProvider,
69
- operations: {
70
- ...currentPolicy.operations,
71
- ...updates.operations,
72
- },
70
+ const mergedOperations = {
71
+ ...(currentPolicy.operations ?? {}),
72
+ ...(updates.operations ?? {}),
73
+ };
74
+ const operationsKeys = Object.keys(mergedOperations);
75
+ const nextPolicy = {
76
+ defaultProvider: updates.defaultProvider ?? currentPolicy.defaultProvider,
77
+ ...(operationsKeys.length > 0 ? { operations: mergedOperations } : {}),
73
78
  };
74
- // Validate the new policy
75
- const validated = RoutingPolicySchema.parse(newPolicy);
76
- // Ensure .vibeman directory exists
77
- const vibemanDir = path.dirname(this.policyFilePath);
78
- await fs.mkdir(vibemanDir, { recursive: true });
79
- // Write to file
80
- await fs.writeFile(this.policyFilePath, JSON.stringify(validated, null, 2), 'utf-8');
81
- // Update in-memory policy
79
+ const validated = RoutingPolicySchema.parse(nextPolicy);
80
+ await this.settingsService.updateSettings([
81
+ { path: ['agents', 'routingPolicy'], value: validated },
82
+ ]);
82
83
  this.policy = validated;
83
- this.lastModified = Date.now();
84
84
  log.info('Updated AI routing policy', { policy: validated }, 'routing-policy');
85
85
  }
86
86
  /**
87
87
  * Resolve provider for a specific operation
88
88
  */
89
+ async getEffectivePolicy() {
90
+ const base = await this.getPolicy();
91
+ const operations = {
92
+ ...base.operations,
93
+ };
94
+ for (const op of ROUTABLE_OPERATIONS) {
95
+ if (!operations[op]) {
96
+ const resolved = await this.resolveProviderForOperation(op);
97
+ operations[op] = {
98
+ provider: resolved.provider,
99
+ ...(resolved.model ? { model: resolved.model } : {}),
100
+ ...(resolved.options ? { options: resolved.options } : {}),
101
+ ...(resolved.fallbacks && resolved.fallbacks.length
102
+ ? { fallback: resolved.fallbacks }
103
+ : {}),
104
+ };
105
+ }
106
+ }
107
+ return {
108
+ defaultProvider: base.defaultProvider,
109
+ operations,
110
+ };
111
+ }
89
112
  async resolveProviderForOperation(operation, overrides) {
90
113
  const policy = await this.getPolicy();
91
114
  const operationConfig = policy.operations?.[operation];
92
115
  // Pull provider/model defaults from settings per operation
93
- const settings = getSettingsService().getSettings();
116
+ const settings = this.settingsService.getSettings();
94
117
  const coding = settings.agents?.codingAgent;
95
118
  const judge = settings.agents?.judgeAgent;
96
119
  const settingsProviderModel = (() => {
@@ -98,61 +121,54 @@ export class RoutingPolicyManager {
98
121
  case 'ai_codereview':
99
122
  return { provider: judge?.provider, model: judge?.model };
100
123
  case 'execute_task':
101
- case 'improve_task':
124
+ case 'quality_checks':
102
125
  case 'ai_merge':
126
+ case 'improve_task':
103
127
  default:
104
128
  return { provider: coding?.provider, model: coding?.model };
105
129
  }
106
130
  })();
107
131
  // Start with operation-specific config or fall back to settings, then default policy
108
132
  const baseProvider = operationConfig?.provider || settingsProviderModel.provider || policy.defaultProvider;
109
- const baseModel = operationConfig?.model || settingsProviderModel.model;
133
+ const baseModel = operationConfig?.model
134
+ ? operationConfig.model
135
+ : operationConfig?.provider
136
+ ? undefined
137
+ : settingsProviderModel.model;
110
138
  const baseOptions = operationConfig?.options;
111
139
  const baseFallbacks = operationConfig?.fallback || [];
112
140
  // Apply overrides
141
+ const overrideProviderOnly = overrides?.provider && overrides.model === undefined;
113
142
  const resolved = {
114
143
  provider: overrides?.provider || baseProvider,
115
- model: overrides?.model || baseModel,
144
+ model: overrideProviderOnly ? undefined : overrides?.model || baseModel,
116
145
  options: overrides?.options || baseOptions,
117
146
  fallbacks: overrides?.fallbacks || baseFallbacks,
118
147
  };
119
148
  log.debug(`Resolved provider for ${operation}`, { operation, resolved }, 'routing-policy');
120
149
  return resolved;
121
150
  }
122
- /**
123
- * Set default provider
124
- */
125
151
  async setDefaultProvider(provider) {
126
152
  await this.updatePolicy({ defaultProvider: provider });
127
153
  }
128
- /**
129
- * Set operation-specific routing
130
- */
131
154
  async setOperationConfig(operation, config) {
132
155
  const currentPolicy = await this.getPolicy();
133
- await this.updatePolicy({
134
- operations: {
135
- ...currentPolicy.operations,
136
- [operation]: config,
137
- },
138
- });
156
+ const operations = {
157
+ ...(currentPolicy.operations ?? {}),
158
+ [operation]: config,
159
+ };
160
+ await this.updatePolicy({ operations });
139
161
  }
140
- /**
141
- * Validate policy against available providers
142
- */
143
162
  validatePolicy(policy, availableProviders) {
144
163
  const errors = [];
145
- // Check default provider
146
164
  if (!availableProviders.has(policy.defaultProvider)) {
147
165
  errors.push(`Default provider '${policy.defaultProvider}' is not available`);
148
166
  }
149
- // Check operation providers
150
167
  if (policy.operations) {
151
168
  for (const [operation, config] of Object.entries(policy.operations)) {
152
169
  if (!availableProviders.has(config.provider)) {
153
170
  errors.push(`Provider '${config.provider}' for operation '${operation}' is not available`);
154
171
  }
155
- // Check fallback providers
156
172
  if (config.fallback) {
157
173
  for (const fallbackProvider of config.fallback) {
158
174
  if (!availableProviders.has(fallbackProvider)) {
@@ -164,83 +180,17 @@ export class RoutingPolicyManager {
164
180
  }
165
181
  return errors;
166
182
  }
167
- /**
168
- * Load policy from file if it has changed
169
- */
170
- async loadPolicyIfChanged() {
171
- try {
172
- const stats = await fs.stat(this.policyFilePath);
173
- const fileModified = stats.mtime.getTime();
174
- // Only reload if file has changed
175
- if (fileModified <= this.lastModified) {
176
- return;
177
- }
178
- const content = await fs.readFile(this.policyFilePath, 'utf-8');
179
- const parsed = JSON.parse(content);
180
- const validated = RoutingPolicySchema.parse(parsed);
181
- this.policy = validated;
182
- this.lastModified = fileModified;
183
- log.info('Loaded AI routing policy', { policy: validated }, 'routing-policy');
184
- }
185
- catch (error) {
186
- if (error.code === 'ENOENT') {
187
- // File doesn't exist yet, use default
188
- this.policy = null;
189
- return;
190
- }
191
- log.warn('Failed to load routing policy, using defaults', error, 'routing-policy');
192
- this.policy = null;
193
- }
194
- }
195
- /**
196
- * Create example policy file if it doesn't exist
197
- */
198
- async createExamplePolicy() {
199
- const examplePath = path.join(getVibeDir(), 'ai-routing.example.json');
200
- // Do not recreate if it already exists
201
- try {
202
- await fs.stat(examplePath);
203
- return; // already exists; avoid noisy logs
204
- }
205
- catch (err) {
206
- if (err?.code && err.code !== 'ENOENT') {
207
- log.warn('Unable to stat example AI routing policy', err, 'routing-policy');
208
- return;
209
- }
210
- }
211
- const settings = getSettingsService().getSettings();
212
- const example = {
213
- defaultProvider: settings.agents.defaultProvider || 'claude-code',
214
- operations: {
215
- execute_task: {
216
- provider: settings.agents.defaultProvider || 'claude-code',
217
- model: 'claude-sonnet-4-20250514',
218
- fallback: ['codex'],
219
- },
220
- improve_task: {
221
- provider: settings.agents.defaultProvider || 'claude-code',
222
- model: 'claude-sonnet-4-20250514',
223
- },
224
- ai_codereview: {
225
- provider: 'codex',
226
- model: 'gpt-5',
227
- },
228
- ai_merge: {
229
- provider: settings.agents.defaultProvider || 'claude-code',
230
- model: 'claude-sonnet-4-20250514',
231
- },
232
- },
183
+ buildPolicyFromSettings() {
184
+ const settings = this.settingsService.getSettings();
185
+ const settingsPolicy = settings.agents?.routingPolicy;
186
+ const defaultProvider = settingsPolicy?.defaultProvider || settings.agents.defaultProvider || 'claude-code';
187
+ const operations = settingsPolicy?.operations
188
+ ? { ...settingsPolicy.operations }
189
+ : undefined;
190
+ const policy = {
191
+ defaultProvider,
192
+ ...(operations ? { operations } : {}),
233
193
  };
234
- // Ensure .vibeman directory exists
235
- const vibemanDir = path.dirname(examplePath);
236
- await fs.mkdir(vibemanDir, { recursive: true });
237
- await fs.writeFile(examplePath, JSON.stringify(example, null, 2), 'utf-8');
238
- log.info('Created example AI routing policy', { path: examplePath }, 'routing-policy');
239
- }
240
- /**
241
- * Get policy file path for external access
242
- */
243
- getPolicyFilePath() {
244
- return this.policyFilePath;
194
+ return RoutingPolicySchema.parse(policy);
245
195
  }
246
196
  }
@@ -0,0 +1,63 @@
1
+ import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
2
+ import { RoutingPolicyManager, } from './routing-policy.js';
3
+ import { getSettingsService } from '../settings-service.js';
4
+ const settingsService = getSettingsService();
5
+ const BASE_POLICY = {
6
+ defaultProvider: 'claude-code',
7
+ operations: {},
8
+ };
9
+ let originalRoutingPolicy;
10
+ async function setRoutingPolicy(value) {
11
+ const result = await settingsService.updateSettings([
12
+ { path: ['agents', 'routingPolicy'], value },
13
+ ]);
14
+ if (!result.success) {
15
+ throw new Error(`Failed to set routing policy: ${JSON.stringify(result.errors)}`);
16
+ }
17
+ }
18
+ describe('RoutingPolicyManager resolve precedence', () => {
19
+ beforeAll(async () => {
20
+ await settingsService.initialize();
21
+ const manager = new RoutingPolicyManager();
22
+ originalRoutingPolicy = await manager.getPolicy();
23
+ await setRoutingPolicy(BASE_POLICY);
24
+ });
25
+ afterEach(async () => {
26
+ await setRoutingPolicy(BASE_POLICY);
27
+ });
28
+ afterAll(async () => {
29
+ await setRoutingPolicy(originalRoutingPolicy);
30
+ });
31
+ it('falls back to settings when no operation config or overrides', async () => {
32
+ const rpm = new RoutingPolicyManager();
33
+ const resolved = await rpm.resolveProviderForOperation('execute_task');
34
+ expect(resolved.provider).toBe('claude-code');
35
+ expect(resolved.model).toBe('claude-sonnet-4-20250514');
36
+ });
37
+ it('uses operation-specific policy when provided', async () => {
38
+ const rpm = new RoutingPolicyManager();
39
+ await rpm.updatePolicy({
40
+ operations: {
41
+ ai_merge: { provider: 'codex', model: 'gpt-5.1-codex' },
42
+ },
43
+ });
44
+ const resolved = await rpm.resolveProviderForOperation('ai_merge');
45
+ expect(resolved.provider).toBe('codex');
46
+ expect(resolved.model).toBe('gpt-5.1-codex');
47
+ });
48
+ it('when override sets provider without model, does not inherit mismatched model', async () => {
49
+ const rpm = new RoutingPolicyManager();
50
+ await rpm.updatePolicy({ operations: {} });
51
+ const override = { provider: 'codex' };
52
+ const resolved = await rpm.resolveProviderForOperation('execute_task', override);
53
+ expect(resolved.provider).toBe('codex');
54
+ expect(resolved.model).toBeUndefined();
55
+ });
56
+ it('override with provider+model takes full precedence', async () => {
57
+ const rpm = new RoutingPolicyManager();
58
+ const override = { provider: 'codex', model: 'gpt-5.1-codex' };
59
+ const resolved = await rpm.resolveProviderForOperation('execute_task', override);
60
+ expect(resolved.provider).toBe('codex');
61
+ expect(resolved.model).toBe('gpt-5.1-codex');
62
+ });
63
+ });
@@ -40,7 +40,19 @@ export declare function buildAIRoutes(options: {
40
40
  */
41
41
  getRoutingPolicy: import("@trpc/server").TRPCQueryProcedure<{
42
42
  input: void;
43
- output: any;
43
+ output: {
44
+ defaultProvider: string;
45
+ operations?: Partial<Record<"execute_task" | "quality_checks" | "ai_codereview" | "ai_merge" | "improve_task", {
46
+ provider: string;
47
+ options?: {
48
+ temperature?: number | undefined;
49
+ maxTokens?: number | undefined;
50
+ tools?: string[] | undefined;
51
+ } | undefined;
52
+ model?: string | undefined;
53
+ fallback?: string[] | undefined;
54
+ }>> | undefined;
55
+ };
44
56
  meta: object;
45
57
  }>;
46
58
  /**
@@ -50,7 +62,7 @@ export declare function buildAIRoutes(options: {
50
62
  input: {
51
63
  policy: {
52
64
  defaultProvider?: string | undefined;
53
- operations?: Partial<Record<"execute_task" | "improve_task" | "ai_merge" | "ai_codereview", {
65
+ operations?: Partial<Record<"execute_task" | "quality_checks" | "ai_codereview" | "ai_merge" | "improve_task", {
54
66
  provider: string;
55
67
  options?: {
56
68
  temperature?: number | undefined;
@@ -86,7 +98,6 @@ export declare function buildAIRoutes(options: {
86
98
  */
87
99
  setOperationConfig: import("@trpc/server").TRPCMutationProcedure<{
88
100
  input: {
89
- operation: "execute_task" | "improve_task" | "ai_merge" | "ai_codereview";
90
101
  config: {
91
102
  provider: string;
92
103
  options?: {
@@ -97,6 +108,7 @@ export declare function buildAIRoutes(options: {
97
108
  model?: string | undefined;
98
109
  fallback?: string[] | undefined;
99
110
  };
111
+ operation: "execute_task" | "quality_checks" | "ai_codereview" | "ai_merge" | "improve_task";
100
112
  };
101
113
  output: {
102
114
  success: boolean;
@@ -20,7 +20,7 @@ const SetDefaultProviderInputSchema = z.object({
20
20
  provider: z.string(),
21
21
  });
22
22
  const SetOperationConfigInputSchema = z.object({
23
- operation: z.enum(['execute_task', 'improve_task', 'ai_codereview', 'ai_merge']),
23
+ operation: z.enum(['execute_task', 'quality_checks', 'ai_codereview', 'ai_merge', 'improve_task']),
24
24
  config: OperationConfigSchema,
25
25
  });
26
26
  const ValidateProvidersInputSchema = z.object({
@@ -92,16 +92,17 @@ export function buildAIRoutes(options) {
92
92
  */
93
93
  getRoutingPolicy: publicProcedure.query(async () => {
94
94
  try {
95
- const policy = await agentService.getRoutingPolicyManager()?.getPolicy();
96
- if (!policy) {
97
- // Return default policy if none exists
95
+ const rpm = agentService.getRoutingPolicyManager?.();
96
+ if (!rpm) {
98
97
  const settings = getSettingsService().getSettings();
99
- const defaultPolicy = {
98
+ return {
100
99
  defaultProvider: settings.agents.defaultProvider || 'claude-code',
101
100
  operations: {},
102
101
  };
103
- return defaultPolicy;
104
102
  }
103
+ const policy = (rpm.getEffectivePolicy
104
+ ? await rpm.getEffectivePolicy()
105
+ : await rpm.getPolicy());
105
106
  log.debug('Retrieved routing policy', { policy }, 'ai-router');
106
107
  return policy;
107
108
  }
@@ -65,20 +65,15 @@ export declare function buildExecutionRoutes({ taskService, vibingOrchestrator }
65
65
  }>;
66
66
  readonly improveTask: import("@trpc/server").TRPCMutationProcedure<{
67
67
  input: {
68
- title: string;
69
68
  type: "feature" | "bug" | "chore" | "refactor" | "test" | "doc";
70
- priority: "high" | "medium" | "low";
69
+ priority: "low" | "medium" | "high";
70
+ title: string;
71
71
  content: string;
72
72
  taskId: string;
73
73
  executionId?: string | undefined;
74
74
  };
75
75
  output: {
76
- type: string;
77
- priority: string;
78
- content: string;
79
- title?: string;
80
- executionId: string;
81
- selectedModel?: string;
76
+ readonly executionId: string;
82
77
  };
83
78
  meta: object;
84
79
  }>;
@@ -53,13 +53,13 @@ export function buildExecutionRoutes({ taskService, vibingOrchestrator }) {
53
53
  }))
54
54
  .mutation(async ({ input }) => {
55
55
  assertTaskActive(input.taskId);
56
- const improvement = await vibingOrchestrator.improveTaskContent(input.taskId, {
56
+ const { executionId } = await vibingOrchestrator.startTaskImprovement(input.taskId, {
57
57
  title: input.title,
58
58
  type: input.type,
59
59
  priority: input.priority,
60
60
  content: input.content,
61
61
  }, { executionId: input.executionId });
62
- return improvement;
62
+ return { executionId };
63
63
  }),
64
64
  onExecutionUpdate: publicProcedure
65
65
  .input(z.object({ executionId: z.string() }))
@@ -69,6 +69,18 @@ export declare function buildProviderConfigRoutes(): {
69
69
  lastDetected?: string | undefined;
70
70
  detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
71
71
  } | undefined;
72
+ gemini?: {
73
+ binPath?: string | undefined;
74
+ detectedPath?: string | undefined;
75
+ lastDetected?: string | undefined;
76
+ detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
77
+ } | undefined;
78
+ amp?: {
79
+ binPath?: string | undefined;
80
+ detectedPath?: string | undefined;
81
+ lastDetected?: string | undefined;
82
+ detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
83
+ } | undefined;
72
84
  };
73
85
  path: string;
74
86
  exists: boolean;
@@ -107,6 +119,16 @@ export declare function buildProviderConfigRoutes(): {
107
119
  detectedPath?: string | undefined;
108
120
  lastDetected?: string | undefined;
109
121
  detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
122
+ } | {
123
+ binPath?: string | undefined;
124
+ detectedPath?: string | undefined;
125
+ lastDetected?: string | undefined;
126
+ detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
127
+ } | {
128
+ binPath?: string | undefined;
129
+ detectedPath?: string | undefined;
130
+ lastDetected?: string | undefined;
131
+ detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
110
132
  } | undefined;
111
133
  cached: {
112
134
  available: boolean;
@@ -154,6 +176,18 @@ export declare function buildProviderConfigRouteHandlers(): {
154
176
  lastDetected?: string | undefined;
155
177
  detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
156
178
  } | undefined;
179
+ gemini?: {
180
+ binPath?: string | undefined;
181
+ detectedPath?: string | undefined;
182
+ lastDetected?: string | undefined;
183
+ detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
184
+ } | undefined;
185
+ amp?: {
186
+ binPath?: string | undefined;
187
+ detectedPath?: string | undefined;
188
+ lastDetected?: string | undefined;
189
+ detectionMethod?: "explicit" | "PATH" | "common-location" | undefined;
190
+ } | undefined;
157
191
  };
158
192
  path: string;
159
193
  exists: boolean;
@@ -2,6 +2,25 @@
2
2
  * tRPC routes for settings management
3
3
  */
4
4
  export declare function buildSettingsRoutes(): {
5
+ /**
6
+ * Get current project information
7
+ */
8
+ getProjectInfo: import("@trpc/server").TRPCQueryProcedure<{
9
+ input: void;
10
+ output: {
11
+ name: string;
12
+ path: string;
13
+ };
14
+ meta: object;
15
+ }>;
16
+ /**
17
+ * Get Vibeman application information
18
+ */
19
+ getVibemanInfo: import("@trpc/server").TRPCQueryProcedure<{
20
+ input: void;
21
+ output: import("../../lib/server/vibeman-info.js").VibemanInfo;
22
+ meta: object;
23
+ }>;
5
24
  /**
6
25
  * Get current settings
7
26
  */
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * tRPC routes for settings management
3
3
  */
4
+ import path from 'path';
4
5
  import { z } from 'zod';
5
6
  import { publicProcedure } from '../trpc.js';
6
7
  import { getSettingsService } from '../../settings-service.js';
7
8
  import { QualityPipeline } from '../../workflows/quality-pipeline.js';
8
9
  import { getProjectRoot } from '../../lib/server/project-root.js';
10
+ import { getVibemanInfo } from '../../lib/server/vibeman-info.js';
9
11
  // Input validation schemas
10
12
  const UpdateSettingsInputSchema = z.array(z.object({
11
13
  path: z.array(z.string().min(1)),
@@ -16,6 +18,20 @@ export function buildSettingsRoutes() {
16
18
  // Initialize settings service on first router build
17
19
  settingsService.initialize().catch(console.error);
18
20
  return {
21
+ /**
22
+ * Get current project information
23
+ */
24
+ getProjectInfo: publicProcedure.query(async () => {
25
+ const root = getProjectRoot();
26
+ const name = path.basename(root);
27
+ return { name, path: root };
28
+ }),
29
+ /**
30
+ * Get Vibeman application information
31
+ */
32
+ getVibemanInfo: publicProcedure.query(async () => {
33
+ return getVibemanInfo();
34
+ }),
19
35
  /**
20
36
  * Get current settings
21
37
  */
@@ -3,11 +3,11 @@ import type { GitService } from '../../vcs/git-service.js';
3
3
  export declare function buildTaskRoutes(taskService: TaskService, gitService?: GitService): {
4
4
  readonly listTasks: import("@trpc/server").TRPCQueryProcedure<{
5
5
  input: {
6
- status?: "review" | "in-progress" | "backlog" | "done" | undefined;
7
6
  type?: "feature" | "bug" | "chore" | "refactor" | "test" | "doc" | undefined;
8
- tags?: string[] | undefined;
9
- priority?: "high" | "medium" | "low" | undefined;
7
+ status?: "backlog" | "in-progress" | "review" | "done" | undefined;
8
+ priority?: "low" | "medium" | "high" | undefined;
10
9
  search?: string | undefined;
10
+ tags?: string[] | undefined;
11
11
  includeDeleted?: boolean | undefined;
12
12
  onlyDeleted?: boolean | undefined;
13
13
  } | undefined;
@@ -23,15 +23,15 @@ export declare function buildTaskRoutes(taskService: TaskService, gitService?: G
23
23
  }>;
24
24
  readonly createTask: import("@trpc/server").TRPCMutationProcedure<{
25
25
  input: {
26
- title: string;
27
26
  type: "feature" | "bug" | "chore" | "refactor" | "test" | "doc";
27
+ title: string;
28
28
  content: string;
29
- status?: "review" | "in-progress" | "backlog" | "done" | undefined;
29
+ status?: "backlog" | "in-progress" | "review" | "done" | undefined;
30
+ priority?: "low" | "medium" | "high" | undefined;
30
31
  id?: string | undefined;
31
32
  tags?: string[] | undefined;
32
33
  due_date?: string | undefined;
33
34
  assignee?: string[] | undefined;
34
- priority?: "high" | "medium" | "low" | undefined;
35
35
  comments?: string[] | undefined;
36
36
  };
37
37
  output: import("../../types/index.js").Task;
@@ -40,13 +40,13 @@ export declare function buildTaskRoutes(taskService: TaskService, gitService?: G
40
40
  readonly updateTask: import("@trpc/server").TRPCMutationProcedure<{
41
41
  input: {
42
42
  id: string;
43
- status?: "review" | "in-progress" | "backlog" | "done" | undefined;
44
- title?: string | undefined;
45
43
  type?: "feature" | "bug" | "chore" | "refactor" | "test" | "doc" | undefined;
44
+ status?: "backlog" | "in-progress" | "review" | "done" | undefined;
45
+ priority?: "low" | "medium" | "high" | undefined;
46
+ title?: string | undefined;
46
47
  tags?: string[] | undefined;
47
48
  due_date?: string | undefined;
48
49
  assignee?: string[] | undefined;
49
- priority?: "high" | "medium" | "low" | undefined;
50
50
  comments?: string[] | undefined;
51
51
  content?: string | undefined;
52
52
  deleted_at?: string | undefined;
@@ -65,7 +65,7 @@ export declare function buildTaskRoutes(taskService: TaskService, gitService?: G
65
65
  }>;
66
66
  readonly moveTask: import("@trpc/server").TRPCMutationProcedure<{
67
67
  input: {
68
- status: "review" | "in-progress" | "backlog" | "done";
68
+ status: "backlog" | "in-progress" | "review" | "done";
69
69
  id: string;
70
70
  };
71
71
  output: import("../../types/index.js").Task;