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.
- package/dist/index.js +3 -3
- package/dist/runtime/api/.tsbuildinfo +1 -1
- package/dist/runtime/api/agent/agent-service.d.ts +11 -6
- package/dist/runtime/api/agent/agent-service.js +97 -29
- package/dist/runtime/api/agent/ai-providers/amp-cli-provider.d.ts +38 -0
- package/dist/runtime/api/agent/ai-providers/amp-cli-provider.js +268 -0
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +2 -0
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +92 -32
- package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.d.ts +24 -0
- package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.js +291 -0
- package/dist/runtime/api/agent/ai-providers/index.d.ts +3 -3
- package/dist/runtime/api/agent/ai-providers/index.js +3 -1
- package/dist/runtime/api/agent/ai-providers/types.d.ts +5 -2
- package/dist/runtime/api/agent/amp-cli-provider.test.js +99 -0
- package/dist/runtime/api/agent/codex-cli-provider.test.js +54 -7
- package/dist/runtime/api/agent/prompt-service.js +108 -105
- package/dist/runtime/api/agent/prompt-service.test.js +35 -0
- package/dist/runtime/api/agent/routing-policy.d.ts +13 -30
- package/dist/runtime/api/agent/routing-policy.js +82 -132
- package/dist/runtime/api/agent/routing-policy.test.js +63 -0
- package/dist/runtime/api/api/routers/ai.d.ts +15 -3
- package/dist/runtime/api/api/routers/ai.js +7 -6
- package/dist/runtime/api/api/routers/executions.d.ts +3 -8
- package/dist/runtime/api/api/routers/executions.js +2 -2
- package/dist/runtime/api/api/routers/provider-config.d.ts +34 -0
- package/dist/runtime/api/api/routers/settings.d.ts +19 -0
- package/dist/runtime/api/api/routers/settings.js +16 -0
- package/dist/runtime/api/api/routers/tasks.d.ts +10 -10
- package/dist/runtime/api/api/routers/workflows.d.ts +20 -12
- package/dist/runtime/api/api/routers/workflows.js +2 -1
- package/dist/runtime/api/api/routers/worktrees.d.ts +2 -2
- package/dist/runtime/api/api/trpc.d.ts +18 -18
- package/dist/runtime/api/lib/local-config.d.ts +94 -4
- package/dist/runtime/api/lib/local-config.js +16 -0
- package/dist/runtime/api/lib/provider-detection.d.ts +2 -0
- package/dist/runtime/api/lib/provider-detection.js +83 -1
- package/dist/runtime/api/lib/server/vibeman-info.d.ts +5 -0
- package/dist/runtime/api/lib/server/vibeman-info.js +85 -0
- package/dist/runtime/api/lib/trpc/server.d.ts +85 -35
- package/dist/runtime/api/persistence/execution-log-persistence.d.ts +1 -1
- package/dist/runtime/api/persistence/execution-log-persistence.js +19 -3
- package/dist/runtime/api/router.d.ts +85 -35
- package/dist/runtime/api/settings-service.js +70 -5
- package/dist/runtime/api/tasks/task-file-parser.d.ts +1 -0
- package/dist/runtime/api/tasks/task-file-parser.js +20 -1
- package/dist/runtime/api/tasks/task-updater.d.ts +62 -0
- package/dist/runtime/api/tasks/task-updater.js +260 -0
- package/dist/runtime/api/tasks/task-updater.test.d.ts +1 -0
- package/dist/runtime/api/tasks/task-updater.test.js +303 -0
- package/dist/runtime/api/types/index.d.ts +9 -2
- package/dist/runtime/api/types/settings.d.ts +29 -5
- package/dist/runtime/api/vcs/git-service.d.ts +9 -0
- package/dist/runtime/api/vcs/git-service.js +23 -0
- package/dist/runtime/api/vcs/worktree-service.d.ts +1 -1
- package/dist/runtime/api/vcs/worktree-service.js +22 -10
- package/dist/runtime/api/workflows/quality-pipeline.js +2 -1
- package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +93 -5
- package/dist/runtime/api/workflows/vibing-orchestrator.js +806 -204
- package/dist/runtime/api/workflows/workflow-effects.d.ts +45 -0
- package/dist/runtime/api/workflows/workflow-effects.js +49 -0
- package/dist/runtime/api/workflows/workflow-reconciler.d.ts +65 -0
- package/dist/runtime/api/workflows/workflow-reconciler.js +226 -0
- package/dist/runtime/api/workflows/workflow-reducer.d.ts +26 -0
- package/dist/runtime/api/workflows/workflow-reducer.js +288 -0
- package/dist/runtime/api/workflows/workflow-reducer.test.d.ts +1 -0
- package/dist/runtime/api/workflows/workflow-reducer.test.js +247 -0
- package/dist/runtime/api/workflows/workflow-schema.d.ts +546 -0
- package/dist/runtime/api/workflows/workflow-schema.js +256 -0
- package/dist/runtime/web/.next/BUILD_ID +1 -1
- package/dist/runtime/web/.next/app-build-manifest.json +51 -44
- package/dist/runtime/web/.next/app-path-routes-manifest.json +2 -1
- package/dist/runtime/web/.next/build-manifest.json +14 -14
- package/dist/runtime/web/.next/prerender-manifest.json +10 -10
- package/dist/runtime/web/.next/react-loadable-manifest.json +2 -33
- package/dist/runtime/web/.next/required-server-files.json +5 -5
- package/dist/runtime/web/.next/routes-manifest.json +8 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +1 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +1 -0
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +1 -0
- package/dist/runtime/web/.next/server/app/_not-found/page.js +2 -2
- package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/_not-found.html +2 -2
- package/dist/runtime/web/.next/server/app/_not-found.rsc +12 -12
- package/dist/runtime/web/.next/server/app/api/health/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app/index.html +2 -2
- package/dist/runtime/web/.next/server/app/index.rsc +15 -15
- package/dist/runtime/web/.next/server/app/page.js +27 -62
- package/dist/runtime/web/.next/server/app/page.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/runtime/web/.next/server/app-paths-manifest.json +2 -1
- package/dist/runtime/web/.next/server/chunks/210.js +1 -0
- package/dist/runtime/web/.next/server/chunks/291.js +18 -0
- package/dist/runtime/web/.next/server/chunks/552.js +22 -0
- package/dist/runtime/web/.next/server/chunks/780.js +1 -0
- package/dist/runtime/web/.next/server/chunks/905.js +6 -0
- package/dist/runtime/web/.next/server/chunks/98.js +1 -0
- package/dist/runtime/web/.next/server/middleware-build-manifest.js +1 -1
- package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/runtime/web/.next/server/pages/404.html +2 -2
- package/dist/runtime/web/.next/server/pages/500.html +1 -1
- package/dist/runtime/web/.next/server/pages/_app.js +1 -1
- package/dist/runtime/web/.next/server/pages/_app.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/pages/_document.js +1 -1
- package/dist/runtime/web/.next/server/pages/_document.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/pages/_error.js +9 -9
- package/dist/runtime/web/.next/server/pages/_error.js.nft.json +1 -1
- package/dist/runtime/web/.next/server/pages-manifest.json +1 -1
- package/dist/runtime/web/.next/server/server-reference-manifest.json +1 -1
- package/dist/runtime/web/.next/server/webpack-runtime.js +1 -1
- package/dist/runtime/web/.next/static/LJFZk_8tvKFN_Ee4HqUuM/_buildManifest.js +1 -0
- package/dist/runtime/web/.next/static/chunks/05c91ade-7d09b2b280adffd1.js +1 -0
- package/dist/runtime/web/.next/static/chunks/201-51bef3fa8c832e2e.js +1 -0
- package/dist/runtime/web/.next/static/chunks/524-89747ed9b0294f8a.js +1 -0
- package/dist/runtime/web/.next/static/chunks/554-8bec6e9cca6acc67.js +1 -0
- package/dist/runtime/web/.next/static/chunks/764.86e9503a69d45a85.js +1 -0
- package/dist/runtime/web/.next/static/chunks/{87c73c54-09e1ba5c70e60a51.js → 7ab4dc20-239138e0ae7af24a.js} +1 -1
- package/dist/runtime/web/.next/static/chunks/905-342391e3d3a3678f.js +20 -0
- package/dist/runtime/web/.next/static/chunks/a8a5ce16-4edea7df2d9b544a.js +79 -0
- package/dist/runtime/web/.next/static/chunks/{8bb4d8db-3e2aa02b0a2384b9.js → ad74d572-4c1b162e2c15acaa.js} +1 -1
- package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-7b752a8641f96c1f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/_not-found/page-34e66b251c2b5044.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-7b752a8641f96c1f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-7b752a8641f96c1f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-7b752a8641f96c1f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/layout-df9ac93cb02b2385.js +1 -0
- package/dist/runtime/web/.next/static/chunks/app/page-6610743f7de5f92a.js +1 -0
- package/dist/runtime/web/.next/static/chunks/c25e0690-e9b798b8de667da1.js +1 -0
- package/dist/runtime/web/.next/static/chunks/framework-57157ec4d37f64aa.js +1 -0
- package/dist/runtime/web/.next/static/chunks/main-app-156cc0c60371bd78.js +1 -0
- package/dist/runtime/web/.next/static/chunks/main-df25d367c47b1fec.js +1 -0
- package/dist/runtime/web/.next/static/chunks/pages/_app-9f629a5e1131d19f.js +1 -0
- package/dist/runtime/web/.next/static/chunks/pages/_error-9238238274c7efcd.js +1 -0
- package/dist/runtime/web/.next/static/chunks/webpack-cd50e39b423d1808.js +1 -0
- package/dist/runtime/web/.next/static/css/4fbf378a264bd4ea.css +1 -0
- package/dist/runtime/web/package.json +8 -8
- package/dist/runtime/web/server.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -37
- package/dist/runtime/api/lib/image-paste-drop-extension.d.ts +0 -26
- package/dist/runtime/api/lib/image-paste-drop-extension.js +0 -125
- package/dist/runtime/api/lib/markdown-utils.d.ts +0 -8
- package/dist/runtime/api/lib/markdown-utils.js +0 -282
- package/dist/runtime/api/lib/markdown-utils.test.js +0 -348
- package/dist/runtime/api/lib/tiptap-utils.clamp-selection.test.js +0 -27
- package/dist/runtime/api/lib/tiptap-utils.d.ts +0 -130
- package/dist/runtime/api/lib/tiptap-utils.js +0 -327
- package/dist/runtime/api/lib/trpc/client.d.ts +0 -1
- package/dist/runtime/api/lib/trpc/client.js +0 -5
- package/dist/runtime/web/.next/server/chunks/217.js +0 -1
- package/dist/runtime/web/.next/server/chunks/383.js +0 -6
- package/dist/runtime/web/.next/server/chunks/458.js +0 -1
- package/dist/runtime/web/.next/server/chunks/576.js +0 -18
- package/dist/runtime/web/.next/server/chunks/635.js +0 -22
- package/dist/runtime/web/.next/server/chunks/761.js +0 -1
- package/dist/runtime/web/.next/server/chunks/777.js +0 -3
- package/dist/runtime/web/.next/server/chunks/825.js +0 -1
- package/dist/runtime/web/.next/server/chunks/838.js +0 -1
- package/dist/runtime/web/.next/server/chunks/973.js +0 -15
- package/dist/runtime/web/.next/static/chunks/18-15c10d3288afef2e.js +0 -1
- package/dist/runtime/web/.next/static/chunks/1c0ca389.537bbe362e3ffbd9.js +0 -3
- package/dist/runtime/web/.next/static/chunks/22747d63-ad5da0c19f4cfe41.js +0 -71
- package/dist/runtime/web/.next/static/chunks/277-0142a939f08738c3.js +0 -63
- package/dist/runtime/web/.next/static/chunks/355.056c2645878a799a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/420.a5ccf151c9e2b2f1.js +0 -1
- package/dist/runtime/web/.next/static/chunks/439.1be0c6242fd248d5.js +0 -15
- package/dist/runtime/web/.next/static/chunks/440.c52e7c0f797e22b2.js +0 -1
- package/dist/runtime/web/.next/static/chunks/575-e2478287c27da87b.js +0 -1
- package/dist/runtime/web/.next/static/chunks/691.920d88c115087314.js +0 -1
- package/dist/runtime/web/.next/static/chunks/765-e838910065b50c3d.js +0 -1
- package/dist/runtime/web/.next/static/chunks/891cff7f.0f71fc028f87e683.js +0 -1
- package/dist/runtime/web/.next/static/chunks/9af238c7-271a911d4e99ab18.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/_not-found/page-1cb74d1cba27d0ab.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-105a61ae865ba536.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/layout-8435322f09fd0975.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/page-8c3ba579efc6f918.js +0 -1
- package/dist/runtime/web/.next/static/chunks/cac567b0-5b77dd12911823cd.js +0 -1
- package/dist/runtime/web/.next/static/chunks/framework-2518f1345b5b2806.js +0 -1
- package/dist/runtime/web/.next/static/chunks/main-17665e5e39de9a8a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/main-app-c0b0f5ba4f7f9d75.js +0 -1
- package/dist/runtime/web/.next/static/chunks/pages/_app-d6f6b3bbc3d81ee1.js +0 -1
- package/dist/runtime/web/.next/static/chunks/pages/_error-75a96cf1997cc3b9.js +0 -1
- package/dist/runtime/web/.next/static/chunks/webpack-c8de37305b4635cf.js +0 -1
- package/dist/runtime/web/.next/static/css/08c950681f1a9a92.css +0 -1
- package/dist/runtime/web/.next/static/mRpNgPfbYR_0wrODzlg_4/_buildManifest.js +0 -1
- /package/dist/runtime/api/{lib/markdown-utils.test.d.ts → agent/amp-cli-provider.test.d.ts} +0 -0
- /package/dist/runtime/api/{lib/tiptap-utils.clamp-selection.test.d.ts → agent/routing-policy.test.d.ts} +0 -0
- /package/dist/runtime/web/.next/static/{mRpNgPfbYR_0wrODzlg_4 → LJFZk_8tvKFN_Ee4HqUuM}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { describe, beforeEach, afterEach, test, expect, vi } from 'vitest';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { TaskUpdater } from './task-updater.js';
|
|
5
|
+
// Mock fs and path
|
|
6
|
+
vi.mock('fs/promises');
|
|
7
|
+
vi.mock('../lib/logger.js', () => ({
|
|
8
|
+
log: {
|
|
9
|
+
info: vi.fn(),
|
|
10
|
+
error: vi.fn(),
|
|
11
|
+
debug: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
vi.mock('../lib/server/project-root.js', () => ({
|
|
15
|
+
getVibeDir: vi.fn(() => '/test/vibe'),
|
|
16
|
+
}));
|
|
17
|
+
const mockFs = fs;
|
|
18
|
+
describe('TaskUpdater', () => {
|
|
19
|
+
let taskUpdater;
|
|
20
|
+
const testTasksDir = '/test/vibe/tasks';
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.clearAllMocks();
|
|
23
|
+
taskUpdater = new TaskUpdater('/test/vibe');
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
vi.restoreAllMocks();
|
|
27
|
+
});
|
|
28
|
+
describe('updateTaskForReviewReadiness', () => {
|
|
29
|
+
const sampleTaskContent = `---
|
|
30
|
+
id: TEST-TASK-123
|
|
31
|
+
title: Test Task
|
|
32
|
+
type: feature
|
|
33
|
+
status: in-progress
|
|
34
|
+
tags: ''
|
|
35
|
+
priority: medium
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Description
|
|
39
|
+
This is a test task.
|
|
40
|
+
|
|
41
|
+
## Implementation Tasks
|
|
42
|
+
- [ ] Implement feature A
|
|
43
|
+
- [ ] Add tests for feature A
|
|
44
|
+
- [x] Already completed task
|
|
45
|
+
|
|
46
|
+
## Acceptance Criteria
|
|
47
|
+
- [ ] Feature works correctly
|
|
48
|
+
- [ ] Tests pass
|
|
49
|
+
`;
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
mockFs.access = vi.fn().mockResolvedValue(undefined);
|
|
52
|
+
mockFs.readFile = vi.fn().mockResolvedValue(sampleTaskContent);
|
|
53
|
+
mockFs.writeFile = vi.fn().mockResolvedValue(undefined);
|
|
54
|
+
mockFs.rename = vi.fn().mockResolvedValue(undefined);
|
|
55
|
+
mockFs.unlink = vi.fn().mockResolvedValue(undefined);
|
|
56
|
+
});
|
|
57
|
+
test('should update task status to review', async () => {
|
|
58
|
+
const context = {
|
|
59
|
+
filesModified: ['src/feature.ts', 'tests/feature.test.ts'],
|
|
60
|
+
keyChanges: ['Added new feature A', 'Implemented core logic'],
|
|
61
|
+
testsPassed: true,
|
|
62
|
+
qualityChecksPassed: true,
|
|
63
|
+
};
|
|
64
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-123', context);
|
|
65
|
+
expect(mockFs.access).toHaveBeenCalledWith(path.join(testTasksDir, 'TEST-TASK-123.md'));
|
|
66
|
+
expect(mockFs.readFile).toHaveBeenCalledWith(path.join(testTasksDir, 'TEST-TASK-123.md'), 'utf-8');
|
|
67
|
+
expect(mockFs.writeFile).toHaveBeenCalled();
|
|
68
|
+
expect(mockFs.rename).toHaveBeenCalled();
|
|
69
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
70
|
+
const writtenContent = writeCall[1];
|
|
71
|
+
// Verify status changed to Review
|
|
72
|
+
expect(writtenContent).toContain('status: Review');
|
|
73
|
+
// Verify todos are marked as completed
|
|
74
|
+
expect(writtenContent).toContain('- [x] Implement feature A');
|
|
75
|
+
expect(writtenContent).toContain('- [x] Add tests for feature A');
|
|
76
|
+
// Verify acceptance criteria unchanged
|
|
77
|
+
expect(writtenContent).toContain('- [ ] Feature works correctly');
|
|
78
|
+
expect(writtenContent).toContain('- [ ] Tests pass');
|
|
79
|
+
// Verify implementation summary added
|
|
80
|
+
expect(writtenContent).toContain('## Implementation Summary');
|
|
81
|
+
expect(writtenContent).toContain('IMPLEMENTATION_SUMMARY_START');
|
|
82
|
+
expect(writtenContent).toContain('IMPLEMENTATION_SUMMARY_END');
|
|
83
|
+
});
|
|
84
|
+
test('should not change status if already done', async () => {
|
|
85
|
+
const doneTaskContent = sampleTaskContent.replace('status: in-progress', 'status: done');
|
|
86
|
+
mockFs.readFile = vi.fn().mockResolvedValue(doneTaskContent);
|
|
87
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-123', {});
|
|
88
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
89
|
+
const writtenContent = writeCall[1];
|
|
90
|
+
expect(writtenContent).toContain('status: done');
|
|
91
|
+
expect(writtenContent).not.toContain('status: Review');
|
|
92
|
+
});
|
|
93
|
+
test('should not change status if already Done (case insensitive)', async () => {
|
|
94
|
+
const doneTaskContent = sampleTaskContent.replace('status: in-progress', 'status: Done');
|
|
95
|
+
mockFs.readFile = vi.fn().mockResolvedValue(doneTaskContent);
|
|
96
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-123', {});
|
|
97
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
98
|
+
const writtenContent = writeCall[1];
|
|
99
|
+
expect(writtenContent).toContain('status: Done');
|
|
100
|
+
expect(writtenContent).not.toContain('status: Review');
|
|
101
|
+
});
|
|
102
|
+
test('should handle missing task file', async () => {
|
|
103
|
+
mockFs.access = vi.fn().mockRejectedValue(new Error('File not found'));
|
|
104
|
+
await expect(taskUpdater.updateTaskForReviewReadiness('NONEXISTENT-TASK', {})).rejects.toThrow('Task file not found: NONEXISTENT-TASK');
|
|
105
|
+
});
|
|
106
|
+
test('should mark only implementation todos, not acceptance criteria', async () => {
|
|
107
|
+
const taskWithMixedTodos = `---
|
|
108
|
+
id: TEST-TASK-456
|
|
109
|
+
title: Mixed Todos Task
|
|
110
|
+
type: feature
|
|
111
|
+
status: in-progress
|
|
112
|
+
tags: ''
|
|
113
|
+
priority: medium
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Implementation
|
|
117
|
+
- [ ] Task 1
|
|
118
|
+
- [ ] Task 2
|
|
119
|
+
|
|
120
|
+
## Implementation Tasks
|
|
121
|
+
- [ ] Task 3
|
|
122
|
+
- [ ] Task 4
|
|
123
|
+
|
|
124
|
+
## Acceptance Criteria
|
|
125
|
+
- [ ] AC 1
|
|
126
|
+
- [ ] AC 2
|
|
127
|
+
|
|
128
|
+
## Random Section
|
|
129
|
+
- [ ] Should not be marked
|
|
130
|
+
`;
|
|
131
|
+
mockFs.readFile = vi.fn().mockResolvedValue(taskWithMixedTodos);
|
|
132
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-456', {});
|
|
133
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
134
|
+
const writtenContent = writeCall[1];
|
|
135
|
+
// Implementation todos should be marked
|
|
136
|
+
expect(writtenContent).toContain('- [x] Task 1');
|
|
137
|
+
expect(writtenContent).toContain('- [x] Task 2');
|
|
138
|
+
expect(writtenContent).toContain('- [x] Task 3');
|
|
139
|
+
expect(writtenContent).toContain('- [x] Task 4');
|
|
140
|
+
// Acceptance criteria should remain unchanged
|
|
141
|
+
expect(writtenContent).toContain('- [ ] AC 1');
|
|
142
|
+
expect(writtenContent).toContain('- [ ] AC 2');
|
|
143
|
+
// Random section should remain unchanged
|
|
144
|
+
expect(writtenContent).toContain('- [ ] Should not be marked');
|
|
145
|
+
});
|
|
146
|
+
test('should update existing implementation summary', async () => {
|
|
147
|
+
const taskWithExistingSummary = `---
|
|
148
|
+
id: TEST-TASK-789
|
|
149
|
+
title: Task with Existing Summary
|
|
150
|
+
type: feature
|
|
151
|
+
status: in-progress
|
|
152
|
+
tags: ''
|
|
153
|
+
priority: medium
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Description
|
|
157
|
+
Task description
|
|
158
|
+
|
|
159
|
+
<!-- IMPLEMENTATION_SUMMARY_START -->
|
|
160
|
+
## Implementation Summary
|
|
161
|
+
Old summary content
|
|
162
|
+
<!-- IMPLEMENTATION_SUMMARY_END -->
|
|
163
|
+
|
|
164
|
+
## Implementation
|
|
165
|
+
- [ ] Task 1
|
|
166
|
+
`;
|
|
167
|
+
mockFs.readFile = vi.fn().mockResolvedValue(taskWithExistingSummary);
|
|
168
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-789', {
|
|
169
|
+
filesModified: ['newfile.ts'],
|
|
170
|
+
keyChanges: ['New implementation'],
|
|
171
|
+
});
|
|
172
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
173
|
+
const writtenContent = writeCall[1];
|
|
174
|
+
// Should have new summary, not old
|
|
175
|
+
expect(writtenContent).not.toContain('Old summary content');
|
|
176
|
+
expect(writtenContent).toContain('**Files Modified:** 1 files updated');
|
|
177
|
+
expect(writtenContent).toContain('newfile.ts');
|
|
178
|
+
expect(writtenContent).toContain('New implementation');
|
|
179
|
+
});
|
|
180
|
+
test('should handle atomic write failure by cleaning up temp file', async () => {
|
|
181
|
+
mockFs.writeFile = vi.fn().mockRejectedValue(new Error('Write failed'));
|
|
182
|
+
mockFs.unlink = vi.fn().mockResolvedValue(undefined);
|
|
183
|
+
await expect(taskUpdater.updateTaskForReviewReadiness('TEST-TASK-123', {})).rejects.toThrow('Write failed');
|
|
184
|
+
expect(mockFs.unlink).toHaveBeenCalledWith(path.join(testTasksDir, 'TEST-TASK-123.md.tmp'));
|
|
185
|
+
});
|
|
186
|
+
test('should generate comprehensive implementation summary', async () => {
|
|
187
|
+
const context = {
|
|
188
|
+
filesModified: ['src/feature.ts', 'tests/feature.test.ts', 'docs/feature.md'],
|
|
189
|
+
keyChanges: ['Added feature A', 'Improved performance', 'Fixed edge case'],
|
|
190
|
+
testsAdded: true,
|
|
191
|
+
testsPassed: true,
|
|
192
|
+
buildPassed: true,
|
|
193
|
+
qualityChecksPassed: true,
|
|
194
|
+
aiReviewScore: 85,
|
|
195
|
+
workflowId: 'workflow-123',
|
|
196
|
+
};
|
|
197
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-123', context);
|
|
198
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
199
|
+
const writtenContent = writeCall[1];
|
|
200
|
+
// Verify summary content
|
|
201
|
+
expect(writtenContent).toContain('**Files Modified:** 3 files updated');
|
|
202
|
+
expect(writtenContent).toContain('src/feature.ts');
|
|
203
|
+
expect(writtenContent).toContain('tests/feature.test.ts');
|
|
204
|
+
expect(writtenContent).toContain('docs/feature.md');
|
|
205
|
+
expect(writtenContent).toContain('**Key Changes:**');
|
|
206
|
+
expect(writtenContent).toContain('Added feature A');
|
|
207
|
+
expect(writtenContent).toContain('Improved performance');
|
|
208
|
+
expect(writtenContent).toContain('Fixed edge case');
|
|
209
|
+
expect(writtenContent).toContain('**Tests:** New tests added and passing');
|
|
210
|
+
expect(writtenContent).toContain('**Quality Checks:** All quality checks passed');
|
|
211
|
+
expect(writtenContent).toContain('AI review score: 85');
|
|
212
|
+
expect(writtenContent).toContain('Ready for manual review and approval');
|
|
213
|
+
});
|
|
214
|
+
test('should identify review risks when quality checks fail', async () => {
|
|
215
|
+
const context = {
|
|
216
|
+
filesModified: ['src/feature.ts'],
|
|
217
|
+
testsPassed: false,
|
|
218
|
+
buildPassed: false,
|
|
219
|
+
qualityChecksPassed: false,
|
|
220
|
+
aiReviewScore: 45,
|
|
221
|
+
};
|
|
222
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-123', context);
|
|
223
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
224
|
+
const writtenContent = writeCall[1];
|
|
225
|
+
expect(writtenContent).toContain('**Review Notes:** Issues requiring attention:');
|
|
226
|
+
expect(writtenContent).toContain('⚠️ Test failures need investigation');
|
|
227
|
+
expect(writtenContent).toContain('⚠️ Quality check issues need resolution');
|
|
228
|
+
expect(writtenContent).toContain('⚠️ Build issues need fixing');
|
|
229
|
+
expect(writtenContent).toContain('⚠️ AI review score below threshold');
|
|
230
|
+
});
|
|
231
|
+
test('should identify AI review score of 0 as risk', async () => {
|
|
232
|
+
const context = {
|
|
233
|
+
filesModified: ['src/feature.ts'],
|
|
234
|
+
testsPassed: true,
|
|
235
|
+
buildPassed: true,
|
|
236
|
+
qualityChecksPassed: true,
|
|
237
|
+
aiReviewScore: 0,
|
|
238
|
+
};
|
|
239
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-123', context);
|
|
240
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
241
|
+
const writtenContent = writeCall[1];
|
|
242
|
+
expect(writtenContent).toContain('**Review Notes:** Issues requiring attention:');
|
|
243
|
+
expect(writtenContent).toContain('⚠️ AI review score below threshold');
|
|
244
|
+
});
|
|
245
|
+
test('should handle edge case with no eligible todo sections', async () => {
|
|
246
|
+
const taskWithoutTodos = `---
|
|
247
|
+
id: TEST-TASK-NO-TODOS
|
|
248
|
+
title: Task Without Implementation Todos
|
|
249
|
+
type: feature
|
|
250
|
+
status: in-progress
|
|
251
|
+
tags: ''
|
|
252
|
+
priority: medium
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Description
|
|
256
|
+
This task has no implementation todos.
|
|
257
|
+
|
|
258
|
+
## Acceptance Criteria
|
|
259
|
+
- [ ] Should work
|
|
260
|
+
- [ ] Should be tested
|
|
261
|
+
`;
|
|
262
|
+
mockFs.readFile = vi.fn().mockResolvedValue(taskWithoutTodos);
|
|
263
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-TASK-NO-TODOS', {});
|
|
264
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
265
|
+
const writtenContent = writeCall[1];
|
|
266
|
+
// Status should still be updated
|
|
267
|
+
expect(writtenContent).toContain('status: Review');
|
|
268
|
+
// Implementation summary should still be added
|
|
269
|
+
expect(writtenContent).toContain('## Implementation Summary');
|
|
270
|
+
// Acceptance criteria should remain unchanged
|
|
271
|
+
expect(writtenContent).toContain('- [ ] Should work');
|
|
272
|
+
expect(writtenContent).toContain('- [ ] Should be tested');
|
|
273
|
+
});
|
|
274
|
+
test('should handle nested todo lists in implementation sections', async () => {
|
|
275
|
+
const taskWithNestedTodos = `---
|
|
276
|
+
id: TEST-NESTED
|
|
277
|
+
title: Task with Nested Todos
|
|
278
|
+
type: feature
|
|
279
|
+
status: in-progress
|
|
280
|
+
tags: ''
|
|
281
|
+
priority: medium
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Implementation Tasks
|
|
285
|
+
- [ ] Main task 1
|
|
286
|
+
- [ ] Subtask 1.1
|
|
287
|
+
- [ ] Subtask 1.2
|
|
288
|
+
- [ ] Main task 2
|
|
289
|
+
- [ ] Subtask 2.1
|
|
290
|
+
`;
|
|
291
|
+
mockFs.readFile = vi.fn().mockResolvedValue(taskWithNestedTodos);
|
|
292
|
+
await taskUpdater.updateTaskForReviewReadiness('TEST-NESTED', {});
|
|
293
|
+
const writeCall = mockFs.writeFile.mock.calls[0];
|
|
294
|
+
const writtenContent = writeCall[1];
|
|
295
|
+
// All todos should be marked, preserving indentation
|
|
296
|
+
expect(writtenContent).toContain('- [x] Main task 1');
|
|
297
|
+
expect(writtenContent).toContain(' - [x] Subtask 1.1');
|
|
298
|
+
expect(writtenContent).toContain(' - [x] Subtask 1.2');
|
|
299
|
+
expect(writtenContent).toContain('- [x] Main task 2');
|
|
300
|
+
expect(writtenContent).toContain(' - [x] Subtask 2.1');
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
});
|
|
@@ -109,6 +109,13 @@ export interface VibingExecution {
|
|
|
109
109
|
previousExecutionId?: string;
|
|
110
110
|
}>;
|
|
111
111
|
qualityResults?: QualityResults;
|
|
112
|
+
aiReviewResult?: {
|
|
113
|
+
executionId: string;
|
|
114
|
+
reviewSummary: string;
|
|
115
|
+
recommendations: string[];
|
|
116
|
+
qualityScore: number;
|
|
117
|
+
timestamp?: string;
|
|
118
|
+
};
|
|
112
119
|
links?: {
|
|
113
120
|
prUrl?: string;
|
|
114
121
|
prNumber?: number;
|
|
@@ -164,7 +171,7 @@ export interface VibingConfig {
|
|
|
164
171
|
timeout?: number;
|
|
165
172
|
enabled?: boolean;
|
|
166
173
|
}>;
|
|
167
|
-
aiRoutingOverrides?: Partial<Record<'execute_task' | '
|
|
174
|
+
aiRoutingOverrides?: Partial<Record<'execute_task' | 'quality_checks' | 'ai_codereview' | 'ai_merge' | 'improve_task', {
|
|
168
175
|
provider?: string;
|
|
169
176
|
model?: string;
|
|
170
177
|
}>>;
|
|
@@ -176,4 +183,4 @@ export interface VibingConfig {
|
|
|
176
183
|
timestamp?: string;
|
|
177
184
|
};
|
|
178
185
|
}
|
|
179
|
-
export type { VibingStage, VibingStatus,
|
|
186
|
+
export type { VibingStage, VibingStatus, VibingWorkflowV2, StageToStatusMapping, } from '@vibeman/types';
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
type ClaudeModel = 'claude-sonnet-4-20250514' | 'claude-opus-4-1-20250805' | 'claude-3-5-haiku-20241022';
|
|
2
|
+
type CodexModel = 'gpt-5.1-codex-max' | 'gpt-5.1-codex-max-low' | 'gpt-5.1-codex-max-medium' | 'gpt-5.1-codex-max-high' | 'gpt-5.1-codex-max-extra-high' | 'gpt-5.1-codex' | 'gpt-5.1-codex-low' | 'gpt-5.1-codex-medium' | 'gpt-5.1-codex-high' | 'gpt-5.1-codex-extra-high' | 'gpt-5.1-codex-mini' | 'gpt-5.1-codex-mini-low' | 'gpt-5.1-codex-mini-medium' | 'gpt-5.1-codex-mini-high' | 'gpt-5.1-codex-mini-extra-high' | 'gpt-5.2' | 'gpt-5.2-low' | 'gpt-5.2-medium' | 'gpt-5.2-high' | 'gpt-5.2-extra-high' | 'gpt-5.1' | 'gpt-5.1-low' | 'gpt-5.1-medium' | 'gpt-5.1-high' | 'gpt-5.1-extra-high';
|
|
3
|
+
type GeminiModel = 'gemini-3-pro-preview' | 'gemini-3-flash-preview' | 'gemini-2.5-pro' | 'gemini-2.5-flash' | 'gemini-2.5-flash-lite';
|
|
4
|
+
type AllowedModel = ClaudeModel | CodexModel | GeminiModel | 'auto';
|
|
1
5
|
export interface VibemanSettings {
|
|
2
6
|
agents: {
|
|
3
|
-
defaultProvider: 'claude-code' | 'codex';
|
|
7
|
+
defaultProvider: 'claude-code' | 'codex' | 'gemini' | 'amp';
|
|
4
8
|
codingAgent: {
|
|
5
|
-
provider: 'claude-code' | 'codex';
|
|
6
|
-
model:
|
|
9
|
+
provider: 'claude-code' | 'codex' | 'gemini' | 'amp';
|
|
10
|
+
model: AllowedModel;
|
|
7
11
|
maxTokens?: number;
|
|
8
12
|
};
|
|
9
13
|
judgeAgent: {
|
|
10
|
-
provider: 'claude-code' | 'codex';
|
|
11
|
-
model:
|
|
14
|
+
provider: 'claude-code' | 'codex' | 'gemini' | 'amp';
|
|
15
|
+
model: AllowedModel;
|
|
12
16
|
maxTokens?: number;
|
|
13
17
|
reviewThresholdScore: number;
|
|
14
18
|
};
|
|
@@ -23,6 +27,25 @@ export interface VibemanSettings {
|
|
|
23
27
|
codex?: {
|
|
24
28
|
binPath?: string;
|
|
25
29
|
};
|
|
30
|
+
gemini?: {
|
|
31
|
+
binPath?: string;
|
|
32
|
+
};
|
|
33
|
+
amp?: {
|
|
34
|
+
binPath?: string;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
routingPolicy?: {
|
|
38
|
+
defaultProvider?: 'claude-code' | 'codex' | 'gemini' | 'amp';
|
|
39
|
+
operations?: Partial<Record<'execute_task' | 'quality_checks' | 'ai_codereview' | 'ai_merge' | 'improve_task', {
|
|
40
|
+
provider: string;
|
|
41
|
+
model?: string;
|
|
42
|
+
options?: {
|
|
43
|
+
temperature?: number;
|
|
44
|
+
maxTokens?: number;
|
|
45
|
+
tools?: string[];
|
|
46
|
+
};
|
|
47
|
+
fallback?: string[];
|
|
48
|
+
}>>;
|
|
26
49
|
};
|
|
27
50
|
};
|
|
28
51
|
defaultWorkflow: {
|
|
@@ -79,3 +102,4 @@ export interface SettingsValidationError {
|
|
|
79
102
|
expected?: string;
|
|
80
103
|
received?: string;
|
|
81
104
|
}
|
|
105
|
+
export {};
|
|
@@ -25,6 +25,7 @@ export declare class GitService {
|
|
|
25
25
|
private config;
|
|
26
26
|
private projectRoot;
|
|
27
27
|
constructor(projectRoot?: string, config?: GitConfig);
|
|
28
|
+
getProjectRoot(): string;
|
|
28
29
|
/**
|
|
29
30
|
* Validate Git repository exists and is properly configured
|
|
30
31
|
*/
|
|
@@ -64,6 +65,14 @@ export declare class GitService {
|
|
|
64
65
|
* Get Git version information
|
|
65
66
|
*/
|
|
66
67
|
getGitVersion(): Promise<VersionResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Check whether a path is ignored by git.
|
|
70
|
+
*/
|
|
71
|
+
isIgnored(relativePath: string): Promise<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* Get the current HEAD commit hash
|
|
74
|
+
*/
|
|
75
|
+
getHeadCommitHash(short?: boolean): Promise<string>;
|
|
67
76
|
/**
|
|
68
77
|
* List all worktrees in the repository
|
|
69
78
|
*/
|
|
@@ -7,6 +7,9 @@ export class GitService {
|
|
|
7
7
|
this.config = config;
|
|
8
8
|
this.git = simpleGit(projectRoot);
|
|
9
9
|
}
|
|
10
|
+
getProjectRoot() {
|
|
11
|
+
return this.projectRoot;
|
|
12
|
+
}
|
|
10
13
|
/**
|
|
11
14
|
* Validate Git repository exists and is properly configured
|
|
12
15
|
*/
|
|
@@ -145,6 +148,26 @@ export class GitService {
|
|
|
145
148
|
async getGitVersion() {
|
|
146
149
|
return await this.git.version();
|
|
147
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Check whether a path is ignored by git.
|
|
153
|
+
*/
|
|
154
|
+
async isIgnored(relativePath) {
|
|
155
|
+
try {
|
|
156
|
+
await this.git.raw(['check-ignore', '-q', relativePath]);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get the current HEAD commit hash
|
|
165
|
+
*/
|
|
166
|
+
async getHeadCommitHash(short = true) {
|
|
167
|
+
const args = short ? ['--short', 'HEAD'] : ['HEAD'];
|
|
168
|
+
const hash = await this.git.revparse(args);
|
|
169
|
+
return hash.trim();
|
|
170
|
+
}
|
|
148
171
|
/**
|
|
149
172
|
* List all worktrees in the repository
|
|
150
173
|
*/
|
|
@@ -45,7 +45,7 @@ export declare class WorktreeService extends EventEmitter {
|
|
|
45
45
|
isConnectedToTask: boolean;
|
|
46
46
|
}>>;
|
|
47
47
|
/**
|
|
48
|
-
* Unified cleanup: remove
|
|
48
|
+
* Unified cleanup: remove worktree first, then branch
|
|
49
49
|
* For connected worktrees, use taskId; for unconnected, use worktreePath and branchName
|
|
50
50
|
*/
|
|
51
51
|
cleanupWorktree(params: {
|
|
@@ -252,6 +252,11 @@ export class WorktreeService extends EventEmitter {
|
|
|
252
252
|
entries = [];
|
|
253
253
|
}
|
|
254
254
|
for (const name of entries) {
|
|
255
|
+
const isIgnored = await this.gitService.isIgnored(name);
|
|
256
|
+
if (!isIgnored) {
|
|
257
|
+
log.debug('Skipped non-ignored env file', { name }, 'worktree-service');
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
255
260
|
const sourcePath = path.join(this.projectRoot, name);
|
|
256
261
|
const targetPath = path.join(worktreePath, name);
|
|
257
262
|
try {
|
|
@@ -269,8 +274,11 @@ export class WorktreeService extends EventEmitter {
|
|
|
269
274
|
}
|
|
270
275
|
}
|
|
271
276
|
}
|
|
272
|
-
catch {
|
|
273
|
-
|
|
277
|
+
catch (error) {
|
|
278
|
+
const err = error;
|
|
279
|
+
if (err?.code !== 'ENOENT') {
|
|
280
|
+
log.debug('Failed to inspect existing worktree env link', { name, error }, 'worktree-service');
|
|
281
|
+
}
|
|
274
282
|
}
|
|
275
283
|
if (shouldCreate) {
|
|
276
284
|
// Remove any existing file/dir and create a symlink
|
|
@@ -280,8 +288,8 @@ export class WorktreeService extends EventEmitter {
|
|
|
280
288
|
log.debug('Symlinked to worktree', { name }, 'worktree-service');
|
|
281
289
|
}
|
|
282
290
|
}
|
|
283
|
-
catch {
|
|
284
|
-
|
|
291
|
+
catch (error) {
|
|
292
|
+
log.debug('Failed to symlink env file', { name, error }, 'worktree-service');
|
|
285
293
|
}
|
|
286
294
|
}
|
|
287
295
|
}
|
|
@@ -323,7 +331,7 @@ export class WorktreeService extends EventEmitter {
|
|
|
323
331
|
}
|
|
324
332
|
}
|
|
325
333
|
/**
|
|
326
|
-
* Unified cleanup: remove
|
|
334
|
+
* Unified cleanup: remove worktree first, then branch
|
|
327
335
|
* For connected worktrees, use taskId; for unconnected, use worktreePath and branchName
|
|
328
336
|
*/
|
|
329
337
|
async cleanupWorktree(params) {
|
|
@@ -355,7 +363,11 @@ export class WorktreeService extends EventEmitter {
|
|
|
355
363
|
actualWorktreePath = worktreePath;
|
|
356
364
|
actualBranchName = branchName;
|
|
357
365
|
}
|
|
358
|
-
// Step 1:
|
|
366
|
+
// Step 1: Remove the worktree
|
|
367
|
+
await this.gitService.removeWorktree(actualWorktreePath, force);
|
|
368
|
+
// Step 2: Prune worktrees to ensure git state is clean before branch deletion
|
|
369
|
+
await this.gitService.pruneWorktrees();
|
|
370
|
+
// Step 3: Try to remove the branch (if it's not main/master)
|
|
359
371
|
if (actualBranchName !== 'main' && actualBranchName !== 'master') {
|
|
360
372
|
try {
|
|
361
373
|
await this.gitService.deleteLocalBranch(actualBranchName, true);
|
|
@@ -363,15 +375,13 @@ export class WorktreeService extends EventEmitter {
|
|
|
363
375
|
}
|
|
364
376
|
catch (error) {
|
|
365
377
|
// Log but continue - branch might not exist or already deleted
|
|
366
|
-
log.warn('Could not delete branch
|
|
378
|
+
log.warn('Could not delete branch after worktree removal', {
|
|
367
379
|
branchName: actualBranchName,
|
|
368
380
|
error: error instanceof Error ? error.message : String(error),
|
|
369
381
|
}, 'worktree-service');
|
|
370
382
|
}
|
|
371
383
|
}
|
|
372
|
-
// Step
|
|
373
|
-
await this.gitService.removeWorktree(actualWorktreePath, force);
|
|
374
|
-
// Step 3: Clean up tracking if it was a connected worktree
|
|
384
|
+
// Step 4: Clean up tracking if it was a connected worktree
|
|
375
385
|
if (isConnectedToTask && taskId) {
|
|
376
386
|
this.worktrees.delete(taskId);
|
|
377
387
|
this.emit('worktreeDeleted', taskId);
|
|
@@ -406,6 +416,8 @@ export class WorktreeService extends EventEmitter {
|
|
|
406
416
|
}
|
|
407
417
|
// Remove worktree using git service
|
|
408
418
|
await this.gitService.removeWorktree(worktree.path, force);
|
|
419
|
+
// Prune worktrees to ensure git state is clean before branch deletion
|
|
420
|
+
await this.gitService.pruneWorktrees();
|
|
409
421
|
// Delete local branch if it exists and is not merged
|
|
410
422
|
try {
|
|
411
423
|
await this.gitService.deleteLocalBranch(worktree.branchName, force);
|
|
@@ -3,7 +3,7 @@ import { stripNextInjectedEnv } from '../utils/stripNextEnv.js';
|
|
|
3
3
|
import { log } from '../lib/logger.js';
|
|
4
4
|
import { getSettingsService } from '../settings-service.js';
|
|
5
5
|
import { CoreAgentService } from '../agent/core-agent-service.js';
|
|
6
|
-
import { ClaudeCodeAdapter, CodexCliProvider } from '../agent/ai-providers/index.js';
|
|
6
|
+
import { ClaudeCodeAdapter, CodexCliProvider, GeminiCliProvider, } from '../agent/ai-providers/index.js';
|
|
7
7
|
import { getQualityChecksDetectionPrompt } from '../agent/prompt-service.js';
|
|
8
8
|
export class QualityPipeline {
|
|
9
9
|
// Expose configured checks for external orchestration/streaming
|
|
@@ -48,6 +48,7 @@ export class QualityPipeline {
|
|
|
48
48
|
defaultModel: 'claude-sonnet-4-20250514',
|
|
49
49
|
}));
|
|
50
50
|
core.registerProvider('codex', new CodexCliProvider({ defaultWorkingDirectory: cwd }));
|
|
51
|
+
core.registerProvider('gemini', new GeminiCliProvider({ defaultWorkingDirectory: cwd }));
|
|
51
52
|
const tools = ['Read', 'Grep', 'Glob'];
|
|
52
53
|
const result = await core.execute({
|
|
53
54
|
prompt,
|