chore: implement requested changes

This commit is contained in:
Ralph Khreish
2025-10-02 11:54:19 +02:00
parent 0aaa105021
commit 7660a29a1a
5 changed files with 213 additions and 15 deletions

View File

@@ -79,9 +79,7 @@ function generateExampleFromSchema(schema) {
if (def.checks) {
const minCheck = def.checks.find((c) => c.kind === 'min');
const maxCheck = def.checks.find((c) => c.kind === 'max');
if (minCheck && minCheck.value >= 20) {
return '<string with at least ' + minCheck.value + ' characters>';
} else if (minCheck && maxCheck) {
if (minCheck && maxCheck) {
return (
'<string between ' +
minCheck.value +
@@ -89,6 +87,8 @@ function generateExampleFromSchema(schema) {
maxCheck.value +
' characters>'
);
} else if (minCheck) {
return '<string with at least ' + minCheck.value + ' characters>';
} else if (maxCheck) {
return '<string up to ' + maxCheck.value + ' characters>';
}

View File

@@ -24,8 +24,6 @@ import {
hasCodebaseAnalysis
} from '../config-manager.js';
import { getPromptManager } from '../prompt-manager.js';
import generateTaskFiles from './generate-task-files.js';
import { COMPLEXITY_REPORT_FILE } from '../../../src/constants/paths.js';
import { getDebugFlag, getDefaultSubtasks } from '../config-manager.js';
import { getPromptManager } from '../prompt-manager.js';
import { findProjectRoot, flattenTasksWithSubtasks } from '../utils.js';

View File

@@ -1,8 +1,8 @@
import { z } from 'zod';
import { BaseTaskSchema } from './base-schemas.js';
import { BaseTaskSchema, SubtaskSchema } from './base-schemas.js';
export const UpdatedTaskSchema = BaseTaskSchema.extend({
subtasks: z.array(z.any()).nullable().default(null)
subtasks: z.array(SubtaskSchema).nullable().default(null)
});
export const UpdateTasksResponseSchema = z.object({

View File

@@ -38,17 +38,16 @@ describe('Prompt Migration Validation', () => {
if (lowerContent.includes(lowerPhrase)) {
// Check if this phrase is allowed in its context
const allowedInContext = allowedContexts[lowerPhrase];
if (allowedInContext) {
const isAllowed = allowedInContext.some((context) =>
const isAllowed =
allowedInContext &&
allowedInContext.some((context) =>
lowerContent.includes(context.toLowerCase())
);
if (isAllowed) {
return; // Skip this phrase - it's allowed in this context
}
}
// If we get here, the phrase is not allowed
expect(lowerContent).not.toContain(lowerPhrase);
expect(isAllowed).toBe(
true,
`File ${file} contains banned phrase "${phrase}" without allowed context`
);
}
});
});

View File

@@ -136,3 +136,204 @@ describe('updateTaskById validation', () => {
expect(log).toHaveBeenCalled();
});
});
describe('updateTaskById success path with generateObjectService', () => {
let fs;
let generateObjectService;
beforeEach(async () => {
jest.clearAllMocks();
jest.spyOn(process, 'exit').mockImplementation(() => {
throw new Error('process.exit called');
});
fs = await import('fs');
const aiServices = await import(
'../../../../../scripts/modules/ai-services-unified.js'
);
generateObjectService = aiServices.generateObjectService;
});
test('successfully updates task with all fields from generateObjectService', async () => {
fs.existsSync.mockReturnValue(true);
readJSON.mockReturnValue({
tag: 'master',
tasks: [
{
id: 1,
title: 'Original Task',
description: 'Original description',
status: 'pending',
dependencies: [],
priority: 'low',
details: null,
testStrategy: null,
subtasks: []
}
]
});
const updatedTaskData = {
id: 1,
title: 'Updated Task',
description: 'Updated description',
status: 'pending',
dependencies: [2],
priority: 'high',
details: 'New implementation details',
testStrategy: 'Unit tests required',
subtasks: [
{
id: 1,
title: 'Subtask 1',
description: 'First subtask',
status: 'pending',
dependencies: []
}
]
};
generateObjectService.mockResolvedValue({
mainResult: {
task: updatedTaskData
},
telemetryData: {
model: 'claude-3-5-sonnet-20241022',
inputTokens: 100,
outputTokens: 200
}
});
const result = await updateTaskById(
'tasks/tasks.json',
1,
'Update task with new requirements',
false,
{ tag: 'master' },
'json'
);
// Verify generateObjectService was called (not generateTextService)
expect(generateObjectService).toHaveBeenCalled();
const callArgs = generateObjectService.mock.calls[0][0];
// Verify correct arguments were passed
expect(callArgs).toMatchObject({
role: 'main',
commandName: 'update-task',
objectName: 'task'
});
expect(callArgs.schema).toBeDefined();
expect(callArgs.systemPrompt).toContain('update a software development task');
expect(callArgs.prompt).toContain('Update task with new requirements');
// Verify the returned task contains all expected fields
expect(result).toEqual({
updatedTask: expect.objectContaining({
id: 1,
title: 'Updated Task',
description: 'Updated description',
status: 'pending',
dependencies: [2],
priority: 'high',
details: 'New implementation details',
testStrategy: 'Unit tests required',
subtasks: expect.arrayContaining([
expect.objectContaining({
id: 1,
title: 'Subtask 1',
description: 'First subtask',
status: 'pending'
})
])
}),
telemetryData: expect.objectContaining({
model: 'claude-3-5-sonnet-20241022',
inputTokens: 100,
outputTokens: 200
}),
tagInfo: undefined
});
});
test('handles generateObjectService with malformed mainResult', async () => {
fs.existsSync.mockReturnValue(true);
readJSON.mockReturnValue({
tag: 'master',
tasks: [
{
id: 1,
title: 'Task',
description: 'Description',
status: 'pending',
dependencies: [],
priority: 'medium',
details: null,
testStrategy: null,
subtasks: []
}
]
});
generateObjectService.mockResolvedValue({
mainResult: {
task: null // Malformed: task is null
},
telemetryData: {}
});
await expect(
updateTaskById(
'tasks/tasks.json',
1,
'Update task',
false,
{ tag: 'master' },
'json'
)
).rejects.toThrow('Received invalid task object from AI');
});
test('handles generateObjectService with missing required fields', async () => {
fs.existsSync.mockReturnValue(true);
readJSON.mockReturnValue({
tag: 'master',
tasks: [
{
id: 1,
title: 'Task',
description: 'Description',
status: 'pending',
dependencies: [],
priority: 'medium',
details: null,
testStrategy: null,
subtasks: []
}
]
});
generateObjectService.mockResolvedValue({
mainResult: {
task: {
id: 1,
// Missing title and description
status: 'pending',
dependencies: [],
priority: 'medium'
}
},
telemetryData: {}
});
await expect(
updateTaskById(
'tasks/tasks.json',
1,
'Update task',
false,
{ tag: 'master' },
'json'
)
).rejects.toThrow('Updated task missing required fields');
});
});