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
@@ -16,17 +16,67 @@ const CLAUDE_ALLOWED_MODELS = [
16
16
  'claude-opus-4-1-20250805',
17
17
  'claude-3-5-haiku-20241022',
18
18
  ];
19
- const MODEL_ENUM = [...CLAUDE_ALLOWED_MODELS, 'gpt-5'];
19
+ const CODEX_BASE_MODELS = [
20
+ 'gpt-5.1-codex-max',
21
+ 'gpt-5.1-codex',
22
+ 'gpt-5.1-codex-mini',
23
+ 'gpt-5.2',
24
+ 'gpt-5.1',
25
+ ];
26
+ const CODEX_REASONING_SUFFIXES = ['low', 'medium', 'high', 'extra-high'];
27
+ const CODEX_ALLOWED_MODELS = [
28
+ ...CODEX_BASE_MODELS,
29
+ ...CODEX_BASE_MODELS.flatMap((base) => CODEX_REASONING_SUFFIXES.map((suffix) => `${base}-${suffix}`)),
30
+ ];
31
+ const GEMINI_ALLOWED_MODELS = [
32
+ 'gemini-3-pro-preview',
33
+ 'gemini-3-flash-preview',
34
+ 'gemini-2.5-pro',
35
+ 'gemini-2.5-flash',
36
+ 'gemini-2.5-flash-lite',
37
+ ];
38
+ const AUTO_ALLOWED_MODELS = ['auto'];
39
+ const MODEL_ENUM = [
40
+ ...CLAUDE_ALLOWED_MODELS,
41
+ ...CODEX_ALLOWED_MODELS,
42
+ ...GEMINI_ALLOWED_MODELS,
43
+ ...AUTO_ALLOWED_MODELS,
44
+ ];
45
+ const ROUTABLE_OPERATIONS = [
46
+ 'execute_task',
47
+ 'quality_checks',
48
+ 'ai_codereview',
49
+ 'ai_merge',
50
+ 'improve_task',
51
+ ];
52
+ const RoutingOperationSchema = z.object({
53
+ provider: z.string(),
54
+ model: z.string().optional(),
55
+ options: z
56
+ .object({
57
+ temperature: z.number().min(0).max(2).optional(),
58
+ maxTokens: z.number().positive().optional(),
59
+ tools: z.array(z.string()).optional(),
60
+ })
61
+ .optional(),
62
+ fallback: z.array(z.string()).optional(),
63
+ });
64
+ const RoutingPolicySettingsSchema = z
65
+ .object({
66
+ defaultProvider: z.enum(['claude-code', 'codex', 'gemini', 'amp']).optional(),
67
+ operations: z.record(z.enum(ROUTABLE_OPERATIONS), RoutingOperationSchema).optional(),
68
+ })
69
+ .optional();
20
70
  const VibemanSettingsSchema = z.object({
21
71
  agents: z.object({
22
- defaultProvider: z.enum(['claude-code', 'codex']),
72
+ defaultProvider: z.enum(['claude-code', 'codex', 'gemini', 'amp']),
23
73
  codingAgent: z.object({
24
- provider: z.enum(['claude-code', 'codex']),
74
+ provider: z.enum(['claude-code', 'codex', 'gemini', 'amp']),
25
75
  model: z.enum(MODEL_ENUM),
26
76
  maxTokens: z.number().min(256).max(10000000000000).optional(),
27
77
  }),
28
78
  judgeAgent: z.object({
29
- provider: z.enum(['claude-code', 'codex']),
79
+ provider: z.enum(['claude-code', 'codex', 'gemini', 'amp']),
30
80
  model: z.enum(MODEL_ENUM),
31
81
  maxTokens: z.number().min(256).max(10000000000000).optional(),
32
82
  reviewThresholdScore: z.number().min(0).max(100),
@@ -41,8 +91,11 @@ const VibemanSettingsSchema = z.object({
41
91
  .object({
42
92
  claudeCode: z.object({ binPath: z.string().min(1).optional() }).optional(),
43
93
  codex: z.object({ binPath: z.string().min(1).optional() }).optional(),
94
+ gemini: z.object({ binPath: z.string().min(1).optional() }).optional(),
95
+ amp: z.object({ binPath: z.string().min(1).optional() }).optional(),
44
96
  })
45
97
  .optional(),
98
+ routingPolicy: RoutingPolicySettingsSchema,
46
99
  }),
47
100
  defaultWorkflow: z.object({
48
101
  autoQualityChecks: z.boolean(),
@@ -116,6 +169,10 @@ const DEFAULT_SETTINGS = {
116
169
  taskPrompt: 'Please implement the following task with attention to quality and testing.',
117
170
  },
118
171
  providers: {},
172
+ routingPolicy: {
173
+ defaultProvider: 'claude-code',
174
+ operations: {},
175
+ },
119
176
  },
120
177
  defaultWorkflow: {
121
178
  autoQualityChecks: true,
@@ -170,7 +227,7 @@ export class SettingsService extends EventEmitter {
170
227
  super();
171
228
  this.settings = { ...DEFAULT_SETTINGS };
172
229
  this.initialized = false;
173
- const settingsDir = config.settingsDir ?? '.vibeman';
230
+ const settingsDir = config.settingsDir ?? '.vibeman/.local';
174
231
  const settingsFileName = config.settingsFileName ?? 'settings.json';
175
232
  // Resolve settings path relative to the repo project root (not package CWD)
176
233
  const projectRoot = getProjectRoot();
@@ -558,6 +615,14 @@ export class SettingsService extends EventEmitter {
558
615
  ...DEFAULT_SETTINGS.agents.defaultPrompts,
559
616
  ...settings.agents?.defaultPrompts,
560
617
  },
618
+ routingPolicy: {
619
+ ...DEFAULT_SETTINGS.agents.routingPolicy,
620
+ ...settings.agents?.routingPolicy,
621
+ operations: {
622
+ ...(DEFAULT_SETTINGS.agents.routingPolicy?.operations ?? {}),
623
+ ...(settings.agents?.routingPolicy?.operations ?? {}),
624
+ },
625
+ },
561
626
  },
562
627
  defaultWorkflow: {
563
628
  ...DEFAULT_SETTINGS.defaultWorkflow,
@@ -6,6 +6,7 @@ export declare class TaskFileParser {
6
6
  parseDirectory(dirPath?: string): Promise<Task[]>;
7
7
  parseContent(content: string, filePath?: string): Promise<Task | null>;
8
8
  private getFileStats;
9
+ private normalizeStatus;
9
10
  writeTask(task: Task): Promise<void>;
10
11
  deleteTask(taskId: string): Promise<void>;
11
12
  generateFilePath(task: Task): string;
@@ -41,6 +41,10 @@ export class TaskFileParser {
41
41
  try {
42
42
  const parsed = matter(content);
43
43
  const metadata = parsed.data;
44
+ const normalizedStatus = this.normalizeStatus(metadata.status);
45
+ if (normalizedStatus !== null) {
46
+ metadata.status = normalizedStatus;
47
+ }
44
48
  // Validate required fields
45
49
  if (!metadata.id || !metadata.title || !metadata.type || !metadata.status) {
46
50
  log.error(`Invalid task metadata in ${filePath}`, { metadata, filePath }, 'task-file-parser:parseContent');
@@ -56,7 +60,7 @@ export class TaskFileParser {
56
60
  }
57
61
  if (!validStatuses.includes(metadata.status)) {
58
62
  log.error(`Invalid task status '${metadata.status}' in ${filePath}`, { status: metadata.status, filePath, validStatuses }, 'task-file-parser:parseContent');
59
- return null;
63
+ metadata.status = 'backlog';
60
64
  }
61
65
  if (metadata.priority && !validPriorities.includes(metadata.priority)) {
62
66
  log.error(`Invalid task priority '${metadata.priority}' in ${filePath}`, { priority: metadata.priority, filePath, validPriorities }, 'task-file-parser:parseContent');
@@ -108,6 +112,21 @@ export class TaskFileParser {
108
112
  return null;
109
113
  }
110
114
  }
115
+ normalizeStatus(status) {
116
+ if (typeof status !== 'string') {
117
+ return null;
118
+ }
119
+ const normalized = status
120
+ .trim()
121
+ .toLowerCase()
122
+ .replace(/[_\s]+/g, '-')
123
+ .replace(/-+/g, '-');
124
+ const validStatuses = ['backlog', 'in-progress', 'review', 'done'];
125
+ if (validStatuses.includes(normalized)) {
126
+ return normalized;
127
+ }
128
+ return null;
129
+ }
111
130
  async writeTask(task) {
112
131
  const filePath = this.generateFilePath(task);
113
132
  const frontMatter = {
@@ -0,0 +1,62 @@
1
+ export interface TaskUpdateContext {
2
+ filesModified?: string[];
3
+ keyChanges?: string[];
4
+ testsAdded?: boolean;
5
+ testsPassed?: boolean;
6
+ buildPassed?: boolean;
7
+ qualityChecksPassed?: boolean;
8
+ aiReviewScore?: number;
9
+ workflowId?: string;
10
+ phase?: string;
11
+ }
12
+ export interface TaskUpdateSummary {
13
+ filesChanged: string[];
14
+ keyLogic: string[];
15
+ testsInfo: string;
16
+ qualityNotes: string;
17
+ remainingRisks: string[];
18
+ }
19
+ /**
20
+ * Updates task markdown files when workflows reach review readiness.
21
+ * Handles status updates, todo marking, and implementation summary generation.
22
+ */
23
+ export declare class TaskUpdater {
24
+ private tasksDir;
25
+ constructor(vibeDir?: string);
26
+ /**
27
+ * Main entry point: Update task for review readiness
28
+ */
29
+ updateTaskForReviewReadiness(taskId: string, context?: TaskUpdateContext): Promise<void>;
30
+ /**
31
+ * Update YAML front-matter to set status to 'review'
32
+ */
33
+ private updateStatusToReview;
34
+ /**
35
+ * Process markdown content: mark todos and add/update implementation summary
36
+ */
37
+ private processMarkdownContent;
38
+ /**
39
+ * Mark implementation-related todos as completed
40
+ */
41
+ private markImplementationTodos;
42
+ /**
43
+ * Check if a section is eligible for todo marking
44
+ */
45
+ private isEligibleTodoSection;
46
+ /**
47
+ * Add or update the implementation summary section
48
+ */
49
+ private upsertImplementationSummary;
50
+ /**
51
+ * Generate implementation summary from context
52
+ */
53
+ private generateImplementationSummary;
54
+ /**
55
+ * Build the implementation summary markdown section
56
+ */
57
+ private buildImplementationSummarySection;
58
+ /**
59
+ * Write task file atomically using temporary file
60
+ */
61
+ private writeTaskFileAtomically;
62
+ }
@@ -0,0 +1,260 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import matter from 'gray-matter';
4
+ import { log } from '../lib/logger.js';
5
+ import { getVibeDir } from '../lib/server/project-root.js';
6
+ /**
7
+ * Updates task markdown files when workflows reach review readiness.
8
+ * Handles status updates, todo marking, and implementation summary generation.
9
+ */
10
+ export class TaskUpdater {
11
+ constructor(vibeDir) {
12
+ this.tasksDir = path.join(vibeDir || getVibeDir(), 'tasks');
13
+ }
14
+ /**
15
+ * Main entry point: Update task for review readiness
16
+ */
17
+ async updateTaskForReviewReadiness(taskId, context = {}) {
18
+ try {
19
+ const taskFilePath = path.join(this.tasksDir, `${taskId}.md`);
20
+ // Check if task file exists
21
+ try {
22
+ await fs.access(taskFilePath);
23
+ }
24
+ catch (error) {
25
+ log.error('Task file not found', { taskId, taskFilePath }, 'task-updater');
26
+ throw new Error(`Task file not found: ${taskId}`);
27
+ }
28
+ // Read and parse the task file
29
+ const fileContent = await fs.readFile(taskFilePath, 'utf-8');
30
+ const parsed = matter(fileContent);
31
+ // Update YAML front-matter status
32
+ const updatedFrontMatter = this.updateStatusToReview(parsed.data);
33
+ // Process markdown content
34
+ const updatedContent = this.processMarkdownContent(parsed.content, context);
35
+ // Write back atomically
36
+ await this.writeTaskFileAtomically(taskFilePath, updatedFrontMatter, updatedContent);
37
+ log.info('Task updated for review readiness', { taskId }, 'task-updater');
38
+ }
39
+ catch (error) {
40
+ log.error('Failed to update task for review readiness', error, 'task-updater');
41
+ throw error;
42
+ }
43
+ }
44
+ /**
45
+ * Update YAML front-matter to set status to 'review'
46
+ */
47
+ updateStatusToReview(frontMatter) {
48
+ const updated = { ...frontMatter };
49
+ // Only update if not already 'done' (preserve completed tasks) - case insensitive
50
+ if (updated.status?.toLowerCase() !== 'done') {
51
+ updated.status = 'Review';
52
+ }
53
+ return updated;
54
+ }
55
+ /**
56
+ * Process markdown content: mark todos and add/update implementation summary
57
+ */
58
+ processMarkdownContent(content, context) {
59
+ let processedContent = content;
60
+ // Step 1: Mark implementation todos as completed
61
+ processedContent = this.markImplementationTodos(processedContent);
62
+ // Step 2: Add or update implementation summary
63
+ processedContent = this.upsertImplementationSummary(processedContent, context);
64
+ return processedContent;
65
+ }
66
+ /**
67
+ * Mark implementation-related todos as completed
68
+ */
69
+ markImplementationTodos(content) {
70
+ const lines = content.split('\n');
71
+ let inEligibleSection = false;
72
+ let currentSection = '';
73
+ const processedLines = lines.map((line) => {
74
+ // Track current section
75
+ const headerMatch = line.match(/^##\s+(.+)$/i);
76
+ if (headerMatch) {
77
+ currentSection = headerMatch[1].toLowerCase().trim();
78
+ inEligibleSection = this.isEligibleTodoSection(currentSection);
79
+ return line;
80
+ }
81
+ // Reset section tracking on other headers
82
+ if (line.match(/^#\s+/)) {
83
+ inEligibleSection = false;
84
+ currentSection = '';
85
+ return line;
86
+ }
87
+ // Mark todos in eligible sections
88
+ if (inEligibleSection && line.match(/^[\s]*-\s+\[\s*\]\s+/)) {
89
+ return line.replace(/^([\s]*-\s+)\[\s*\](\s+.*)$/, '$1[x]$2');
90
+ }
91
+ return line;
92
+ });
93
+ return processedLines.join('\n');
94
+ }
95
+ /**
96
+ * Check if a section is eligible for todo marking
97
+ */
98
+ isEligibleTodoSection(sectionName) {
99
+ const eligibleSections = [
100
+ 'implementation',
101
+ 'implementation tasks',
102
+ 'tasks',
103
+ 'todo',
104
+ 'implementation notes',
105
+ ];
106
+ // Check for exact matches or partial matches
107
+ return eligibleSections.some((eligible) => sectionName.includes(eligible) || eligible.includes(sectionName));
108
+ }
109
+ /**
110
+ * Add or update the implementation summary section
111
+ */
112
+ upsertImplementationSummary(content, context) {
113
+ const summary = this.generateImplementationSummary(context);
114
+ const summarySection = this.buildImplementationSummarySection(summary);
115
+ // Check if implementation summary already exists
116
+ const startMarker = '<!-- IMPLEMENTATION_SUMMARY_START -->';
117
+ const endMarker = '<!-- IMPLEMENTATION_SUMMARY_END -->';
118
+ const startIndex = content.indexOf(startMarker);
119
+ const endIndex = content.indexOf(endMarker);
120
+ if (startIndex !== -1 && endIndex !== -1) {
121
+ // Replace existing summary
122
+ const before = content.substring(0, startIndex);
123
+ const after = content.substring(endIndex + endMarker.length);
124
+ return before + summarySection + after;
125
+ }
126
+ else {
127
+ // Add new summary at the beginning of content (after first header if any)
128
+ const lines = content.split('\n');
129
+ let insertIndex = 0;
130
+ // Find insertion point after description or first header
131
+ for (let i = 0; i < lines.length; i++) {
132
+ if (lines[i].match(/^##\s+/)) {
133
+ insertIndex = i;
134
+ break;
135
+ }
136
+ }
137
+ const before = lines.slice(0, insertIndex).join('\n');
138
+ const after = lines.slice(insertIndex).join('\n');
139
+ const separator = insertIndex > 0 && before.trim() ? '\n\n' : '';
140
+ return before + separator + summarySection + '\n\n' + after;
141
+ }
142
+ }
143
+ /**
144
+ * Generate implementation summary from context
145
+ */
146
+ generateImplementationSummary(context) {
147
+ const filesChanged = context.filesModified || [];
148
+ const keyLogic = context.keyChanges || [];
149
+ // Generate tests info
150
+ let testsInfo = 'No test information available';
151
+ if (context.testsAdded) {
152
+ testsInfo = context.testsPassed
153
+ ? 'New tests added and passing'
154
+ : 'New tests added (status unknown)';
155
+ }
156
+ else if (context.testsPassed !== undefined) {
157
+ testsInfo = context.testsPassed ? 'Existing tests passing' : 'Test failures detected';
158
+ }
159
+ // Generate quality notes
160
+ let qualityNotes = 'Quality checks not run';
161
+ if (context.qualityChecksPassed !== undefined) {
162
+ qualityNotes = context.qualityChecksPassed
163
+ ? 'All quality checks passed'
164
+ : 'Quality check failures';
165
+ }
166
+ if (context.buildPassed !== undefined) {
167
+ qualityNotes += context.buildPassed ? ', build successful' : ', build failures detected';
168
+ }
169
+ if (context.aiReviewScore !== undefined) {
170
+ qualityNotes += `, AI review score: ${context.aiReviewScore}`;
171
+ }
172
+ // Identify remaining risks (only for actual failures, not missing data)
173
+ const remainingRisks = [];
174
+ if (context.testsPassed === false)
175
+ remainingRisks.push('Test failures need investigation');
176
+ if (context.qualityChecksPassed === false)
177
+ remainingRisks.push('Quality check issues need resolution');
178
+ if (context.buildPassed === false)
179
+ remainingRisks.push('Build issues need fixing');
180
+ if (context.aiReviewScore !== undefined && context.aiReviewScore < 70) {
181
+ remainingRisks.push('AI review score below threshold');
182
+ }
183
+ return {
184
+ filesChanged: filesChanged.slice(0, 10), // Limit to 10 files for readability
185
+ keyLogic,
186
+ testsInfo,
187
+ qualityNotes,
188
+ remainingRisks,
189
+ };
190
+ }
191
+ /**
192
+ * Build the implementation summary markdown section
193
+ */
194
+ buildImplementationSummarySection(summary) {
195
+ const sections = [];
196
+ sections.push('<!-- IMPLEMENTATION_SUMMARY_START -->');
197
+ sections.push('## Implementation Summary');
198
+ sections.push('');
199
+ // Files changed
200
+ if (summary.filesChanged.length > 0) {
201
+ sections.push(`- **Files Modified:** ${summary.filesChanged.length} files updated`);
202
+ sections.push('- **Key Files:**');
203
+ summary.filesChanged.forEach((file) => {
204
+ sections.push(` - \`${file}\``);
205
+ });
206
+ }
207
+ else {
208
+ sections.push('- **Files Modified:** No file changes tracked');
209
+ }
210
+ // Key logic changes
211
+ if (summary.keyLogic.length > 0) {
212
+ sections.push('- **Key Changes:**');
213
+ summary.keyLogic.forEach((change) => {
214
+ sections.push(` - ${change}`);
215
+ });
216
+ }
217
+ // Tests and quality
218
+ sections.push(`- **Tests:** ${summary.testsInfo}`);
219
+ sections.push(`- **Quality Checks:** ${summary.qualityNotes}`);
220
+ // Remaining risks
221
+ if (summary.remainingRisks.length > 0) {
222
+ sections.push('- **Review Notes:** Issues requiring attention:');
223
+ summary.remainingRisks.forEach((risk) => {
224
+ sections.push(` - ⚠️ ${risk}`);
225
+ });
226
+ }
227
+ else {
228
+ sections.push('- **Review Notes:** Ready for manual review and approval');
229
+ }
230
+ sections.push('');
231
+ sections.push('<!-- IMPLEMENTATION_SUMMARY_END -->');
232
+ return sections.join('\n');
233
+ }
234
+ /**
235
+ * Write task file atomically using temporary file
236
+ */
237
+ async writeTaskFileAtomically(filePath, frontMatter, content) {
238
+ const tempPath = filePath + '.tmp';
239
+ try {
240
+ // Normalize content to ensure clean formatting
241
+ const normalizedContent = content.replace(/^[\r\n]+/, '').replace(/[\r\n]*$/, '\n');
242
+ // Build complete file content
243
+ const fileContent = matter.stringify(normalizedContent, frontMatter);
244
+ // Write to temporary file first
245
+ await fs.writeFile(tempPath, fileContent, 'utf-8');
246
+ // Atomic rename
247
+ await fs.rename(tempPath, filePath);
248
+ }
249
+ catch (error) {
250
+ // Clean up temp file if it exists
251
+ try {
252
+ await fs.unlink(tempPath);
253
+ }
254
+ catch {
255
+ // Ignore cleanup errors
256
+ }
257
+ throw error;
258
+ }
259
+ }
260
+ }
@@ -0,0 +1 @@
1
+ export {};