vibeman 0.0.5 → 0.0.6
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/api.js +49 -0
- package/dist/cli.js +135 -0
- package/dist/ui/index-gnk6rhxs.js +9 -0
- package/dist/ui/index.html +10 -0
- package/dist/ui/index.js +2 -0
- package/package.json +10 -46
- package/README.md +0 -12
- package/dist/index.js +0 -114
- package/dist/runtime/api/.tsbuildinfo +0 -1
- package/dist/runtime/api/agent/agent-service.d.ts +0 -229
- package/dist/runtime/api/agent/agent-service.js +0 -963
- package/dist/runtime/api/agent/ai-providers/amp-cli-provider.d.ts +0 -38
- package/dist/runtime/api/agent/ai-providers/amp-cli-provider.js +0 -268
- package/dist/runtime/api/agent/ai-providers/claude-code-adapter.d.ts +0 -61
- package/dist/runtime/api/agent/ai-providers/claude-code-adapter.js +0 -362
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.d.ts +0 -36
- package/dist/runtime/api/agent/ai-providers/codex-cli-provider.js +0 -375
- package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.d.ts +0 -24
- package/dist/runtime/api/agent/ai-providers/gemini-cli-provider.js +0 -291
- package/dist/runtime/api/agent/ai-providers/index.d.ts +0 -9
- package/dist/runtime/api/agent/ai-providers/index.js +0 -9
- package/dist/runtime/api/agent/ai-providers/types.d.ts +0 -185
- package/dist/runtime/api/agent/ai-providers/types.js +0 -5
- package/dist/runtime/api/agent/amp-cli-provider.test.d.ts +0 -1
- package/dist/runtime/api/agent/amp-cli-provider.test.js +0 -99
- package/dist/runtime/api/agent/codex-cli-provider.test.d.ts +0 -1
- package/dist/runtime/api/agent/codex-cli-provider.test.js +0 -172
- package/dist/runtime/api/agent/core-agent-service.d.ts +0 -119
- package/dist/runtime/api/agent/core-agent-service.js +0 -267
- package/dist/runtime/api/agent/parsers.d.ts +0 -16
- package/dist/runtime/api/agent/parsers.js +0 -308
- package/dist/runtime/api/agent/prompt-service.d.ts +0 -30
- package/dist/runtime/api/agent/prompt-service.js +0 -452
- package/dist/runtime/api/agent/prompt-service.test.d.ts +0 -1
- package/dist/runtime/api/agent/prompt-service.test.js +0 -265
- package/dist/runtime/api/agent/routing-policy.d.ts +0 -171
- package/dist/runtime/api/agent/routing-policy.js +0 -196
- package/dist/runtime/api/agent/routing-policy.test.d.ts +0 -1
- package/dist/runtime/api/agent/routing-policy.test.js +0 -63
- package/dist/runtime/api/api/router-helpers.d.ts +0 -32
- package/dist/runtime/api/api/router-helpers.js +0 -31
- package/dist/runtime/api/api/routers/ai.d.ts +0 -200
- package/dist/runtime/api/api/routers/ai.js +0 -396
- package/dist/runtime/api/api/routers/executions.d.ts +0 -93
- package/dist/runtime/api/api/routers/executions.js +0 -94
- package/dist/runtime/api/api/routers/git.d.ts +0 -45
- package/dist/runtime/api/api/routers/git.js +0 -35
- package/dist/runtime/api/api/routers/provider-config.d.ts +0 -199
- package/dist/runtime/api/api/routers/provider-config.js +0 -252
- package/dist/runtime/api/api/routers/settings.d.ts +0 -158
- package/dist/runtime/api/api/routers/settings.js +0 -129
- package/dist/runtime/api/api/routers/tasks.d.ts +0 -141
- package/dist/runtime/api/api/routers/tasks.js +0 -238
- package/dist/runtime/api/api/routers/workflows.d.ts +0 -275
- package/dist/runtime/api/api/routers/workflows.js +0 -311
- package/dist/runtime/api/api/routers/worktrees.d.ts +0 -101
- package/dist/runtime/api/api/routers/worktrees.js +0 -80
- package/dist/runtime/api/api/trpc.d.ts +0 -118
- package/dist/runtime/api/api/trpc.js +0 -34
- package/dist/runtime/api/index.d.ts +0 -9
- package/dist/runtime/api/index.js +0 -117
- package/dist/runtime/api/lib/id-generator.d.ts +0 -70
- package/dist/runtime/api/lib/id-generator.js +0 -123
- package/dist/runtime/api/lib/local-config.d.ts +0 -335
- package/dist/runtime/api/lib/local-config.js +0 -304
- package/dist/runtime/api/lib/logger.d.ts +0 -11
- package/dist/runtime/api/lib/logger.js +0 -188
- package/dist/runtime/api/lib/provider-detection.d.ts +0 -61
- package/dist/runtime/api/lib/provider-detection.js +0 -326
- package/dist/runtime/api/lib/server/agent-service-singleton.d.ts +0 -6
- package/dist/runtime/api/lib/server/agent-service-singleton.js +0 -27
- package/dist/runtime/api/lib/server/bootstrap.d.ts +0 -38
- package/dist/runtime/api/lib/server/bootstrap.js +0 -197
- package/dist/runtime/api/lib/server/git-service-singleton.d.ts +0 -6
- package/dist/runtime/api/lib/server/git-service-singleton.js +0 -47
- package/dist/runtime/api/lib/server/project-root.d.ts +0 -2
- package/dist/runtime/api/lib/server/project-root.js +0 -61
- package/dist/runtime/api/lib/server/task-service-singleton.d.ts +0 -7
- package/dist/runtime/api/lib/server/task-service-singleton.js +0 -58
- package/dist/runtime/api/lib/server/vibeman-info.d.ts +0 -5
- package/dist/runtime/api/lib/server/vibeman-info.js +0 -85
- package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.d.ts +0 -7
- package/dist/runtime/api/lib/server/vibing-orchestrator-singleton.js +0 -57
- package/dist/runtime/api/lib/trpc/server.d.ts +0 -965
- package/dist/runtime/api/lib/trpc/server.js +0 -11
- package/dist/runtime/api/lib/trpc/ws-server.d.ts +0 -8
- package/dist/runtime/api/lib/trpc/ws-server.js +0 -33
- package/dist/runtime/api/persistence/database-service.d.ts +0 -14
- package/dist/runtime/api/persistence/database-service.js +0 -74
- package/dist/runtime/api/persistence/execution-log-persistence.d.ts +0 -90
- package/dist/runtime/api/persistence/execution-log-persistence.js +0 -426
- package/dist/runtime/api/persistence/execution-log-persistence.test.d.ts +0 -1
- package/dist/runtime/api/persistence/execution-log-persistence.test.js +0 -170
- package/dist/runtime/api/router.d.ts +0 -968
- package/dist/runtime/api/router.js +0 -34
- package/dist/runtime/api/settings-service.d.ts +0 -110
- package/dist/runtime/api/settings-service.js +0 -678
- package/dist/runtime/api/tasks/file-watcher.d.ts +0 -23
- package/dist/runtime/api/tasks/file-watcher.js +0 -88
- package/dist/runtime/api/tasks/task-file-parser.d.ts +0 -14
- package/dist/runtime/api/tasks/task-file-parser.js +0 -180
- package/dist/runtime/api/tasks/task-service.d.ts +0 -36
- package/dist/runtime/api/tasks/task-service.js +0 -173
- package/dist/runtime/api/tasks/task-updater.d.ts +0 -62
- package/dist/runtime/api/tasks/task-updater.js +0 -260
- package/dist/runtime/api/tasks/task-updater.test.d.ts +0 -1
- package/dist/runtime/api/tasks/task-updater.test.js +0 -303
- package/dist/runtime/api/types/index.d.ts +0 -186
- package/dist/runtime/api/types/index.js +0 -1
- package/dist/runtime/api/types/settings.d.ts +0 -105
- package/dist/runtime/api/types/settings.js +0 -2
- package/dist/runtime/api/types.d.ts +0 -2
- package/dist/runtime/api/types.js +0 -1
- package/dist/runtime/api/utils/env.d.ts +0 -6
- package/dist/runtime/api/utils/env.js +0 -12
- package/dist/runtime/api/utils/stripNextEnv.d.ts +0 -7
- package/dist/runtime/api/utils/stripNextEnv.js +0 -22
- package/dist/runtime/api/utils/title-slug.d.ts +0 -6
- package/dist/runtime/api/utils/title-slug.js +0 -77
- package/dist/runtime/api/utils/url.d.ts +0 -2
- package/dist/runtime/api/utils/url.js +0 -19
- package/dist/runtime/api/vcs/git-history-service.d.ts +0 -57
- package/dist/runtime/api/vcs/git-history-service.js +0 -228
- package/dist/runtime/api/vcs/git-service.d.ts +0 -136
- package/dist/runtime/api/vcs/git-service.js +0 -307
- package/dist/runtime/api/vcs/worktree-service.d.ts +0 -93
- package/dist/runtime/api/vcs/worktree-service.js +0 -518
- package/dist/runtime/api/vcs/worktree-service.test.d.ts +0 -1
- package/dist/runtime/api/vcs/worktree-service.test.js +0 -20
- package/dist/runtime/api/workflows/quality-pipeline.d.ts +0 -58
- package/dist/runtime/api/workflows/quality-pipeline.js +0 -401
- package/dist/runtime/api/workflows/vibing-orchestrator.d.ts +0 -406
- package/dist/runtime/api/workflows/vibing-orchestrator.js +0 -2462
- package/dist/runtime/api/workflows/workflow-effects.d.ts +0 -45
- package/dist/runtime/api/workflows/workflow-effects.js +0 -49
- package/dist/runtime/api/workflows/workflow-reconciler.d.ts +0 -65
- package/dist/runtime/api/workflows/workflow-reconciler.js +0 -226
- package/dist/runtime/api/workflows/workflow-reducer.d.ts +0 -26
- package/dist/runtime/api/workflows/workflow-reducer.js +0 -288
- package/dist/runtime/api/workflows/workflow-reducer.test.d.ts +0 -1
- package/dist/runtime/api/workflows/workflow-reducer.test.js +0 -247
- package/dist/runtime/api/workflows/workflow-schema.d.ts +0 -546
- package/dist/runtime/api/workflows/workflow-schema.js +0 -256
- package/dist/runtime/web/.next/BUILD_ID +0 -1
- package/dist/runtime/web/.next/app-build-manifest.json +0 -66
- package/dist/runtime/web/.next/app-path-routes-manifest.json +0 -8
- package/dist/runtime/web/.next/build-manifest.json +0 -33
- package/dist/runtime/web/.next/package.json +0 -1
- package/dist/runtime/web/.next/prerender-manifest.json +0 -61
- package/dist/runtime/web/.next/react-loadable-manifest.json +0 -8
- package/dist/runtime/web/.next/required-server-files.json +0 -334
- package/dist/runtime/web/.next/routes-manifest.json +0 -70
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js +0 -1
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/.vibeman/assets/images/[...path]/route_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/_not-found/page.js +0 -2
- package/dist/runtime/web/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/_not-found.html +0 -7
- package/dist/runtime/web/.next/server/app/_not-found.meta +0 -8
- package/dist/runtime/web/.next/server/app/_not-found.rsc +0 -22
- package/dist/runtime/web/.next/server/app/api/health/route.js +0 -1
- package/dist/runtime/web/.next/server/app/api/health/route.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/api/health/route_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js +0 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/api/images/[...path]/route_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js +0 -1
- package/dist/runtime/web/.next/server/app/api/upload/route.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/api/upload/route_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app/index.html +0 -7
- package/dist/runtime/web/.next/server/app/index.meta +0 -7
- package/dist/runtime/web/.next/server/app/index.rsc +0 -27
- package/dist/runtime/web/.next/server/app/page.js +0 -112
- package/dist/runtime/web/.next/server/app/page.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/app/page_client-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/app-paths-manifest.json +0 -8
- package/dist/runtime/web/.next/server/chunks/210.js +0 -1
- package/dist/runtime/web/.next/server/chunks/291.js +0 -18
- package/dist/runtime/web/.next/server/chunks/552.js +0 -22
- package/dist/runtime/web/.next/server/chunks/780.js +0 -1
- package/dist/runtime/web/.next/server/chunks/905.js +0 -6
- package/dist/runtime/web/.next/server/chunks/98.js +0 -1
- package/dist/runtime/web/.next/server/functions-config-manifest.json +0 -4
- package/dist/runtime/web/.next/server/middleware-build-manifest.js +0 -1
- package/dist/runtime/web/.next/server/middleware-manifest.json +0 -6
- package/dist/runtime/web/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/dist/runtime/web/.next/server/next-font-manifest.js +0 -1
- package/dist/runtime/web/.next/server/next-font-manifest.json +0 -1
- package/dist/runtime/web/.next/server/pages/404.html +0 -7
- package/dist/runtime/web/.next/server/pages/500.html +0 -1
- package/dist/runtime/web/.next/server/pages/_app.js +0 -1
- package/dist/runtime/web/.next/server/pages/_app.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/pages/_document.js +0 -1
- package/dist/runtime/web/.next/server/pages/_document.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/pages/_error.js +0 -19
- package/dist/runtime/web/.next/server/pages/_error.js.nft.json +0 -1
- package/dist/runtime/web/.next/server/pages-manifest.json +0 -6
- package/dist/runtime/web/.next/server/server-reference-manifest.js +0 -1
- package/dist/runtime/web/.next/server/server-reference-manifest.json +0 -1
- package/dist/runtime/web/.next/server/webpack-runtime.js +0 -1
- package/dist/runtime/web/.next/static/LJFZk_8tvKFN_Ee4HqUuM/_buildManifest.js +0 -1
- package/dist/runtime/web/.next/static/LJFZk_8tvKFN_Ee4HqUuM/_ssgManifest.js +0 -1
- package/dist/runtime/web/.next/static/chunks/05c91ade-7d09b2b280adffd1.js +0 -1
- package/dist/runtime/web/.next/static/chunks/201-51bef3fa8c832e2e.js +0 -1
- package/dist/runtime/web/.next/static/chunks/524-89747ed9b0294f8a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/554-8bec6e9cca6acc67.js +0 -1
- package/dist/runtime/web/.next/static/chunks/764.86e9503a69d45a85.js +0 -1
- package/dist/runtime/web/.next/static/chunks/7ab4dc20-239138e0ae7af24a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/905-342391e3d3a3678f.js +0 -20
- package/dist/runtime/web/.next/static/chunks/a8a5ce16-4edea7df2d9b544a.js +0 -79
- package/dist/runtime/web/.next/static/chunks/ad74d572-4c1b162e2c15acaa.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/.vibeman/assets/images/[...path]/route-7b752a8641f96c1f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/_not-found/page-34e66b251c2b5044.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/health/route-7b752a8641f96c1f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/images/[...path]/route-7b752a8641f96c1f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/api/upload/route-7b752a8641f96c1f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/layout-df9ac93cb02b2385.js +0 -1
- package/dist/runtime/web/.next/static/chunks/app/page-6610743f7de5f92a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/c25e0690-e9b798b8de667da1.js +0 -1
- package/dist/runtime/web/.next/static/chunks/framework-57157ec4d37f64aa.js +0 -1
- package/dist/runtime/web/.next/static/chunks/main-app-156cc0c60371bd78.js +0 -1
- package/dist/runtime/web/.next/static/chunks/main-df25d367c47b1fec.js +0 -1
- package/dist/runtime/web/.next/static/chunks/pages/_app-9f629a5e1131d19f.js +0 -1
- package/dist/runtime/web/.next/static/chunks/pages/_error-9238238274c7efcd.js +0 -1
- package/dist/runtime/web/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/dist/runtime/web/.next/static/chunks/webpack-cd50e39b423d1808.js +0 -1
- package/dist/runtime/web/.next/static/css/2728291c68f99cb1.css +0 -3
- package/dist/runtime/web/.next/static/css/4fbf378a264bd4ea.css +0 -1
- package/dist/runtime/web/.next/static/css/521bd69cc298cd1a.css +0 -1
- package/dist/runtime/web/.next/static/css/537e22821e101b87.css +0 -1
- package/dist/runtime/web/.next/static/media/19cfc7226ec3afaa-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/21350d82a1f187e9-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/8e9860b6e62d6359-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/ba9851c3c22cd980-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/c5fe6dc8356a8c31-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/df0a9ae256c0569c-s.woff2 +0 -0
- package/dist/runtime/web/.next/static/media/e4af272ccee01ff0-s.p.woff2 +0 -0
- package/dist/runtime/web/package.json +0 -65
- package/dist/runtime/web/server.js +0 -44
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -1,260 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,303 +0,0 @@
|
|
|
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
|
-
});
|