feat(CF-314): Add planning_mode_required field and smart planning mode support
Adds DB column, TypeScript types, MCP tool schemas, and CRUD handlers for planning_mode_required (NULL=auto-detect, true=always, false=never). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
3
migrations/029_add_planning_mode_required.sql
Normal file
3
migrations/029_add_planning_mode_required.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
-- CF-314: Add planning_mode_required flag for smart planning mode auto-detection
|
||||
-- NULL = auto-detect (scoring algorithm), true = always plan, false = never plan
|
||||
ALTER TABLE tasks ADD COLUMN IF NOT EXISTS planning_mode_required BOOLEAN DEFAULT NULL;
|
||||
@@ -120,6 +120,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
type: a.type,
|
||||
priority: a.priority,
|
||||
description: a.description,
|
||||
planning_mode_required: a.planning_mode_required,
|
||||
});
|
||||
break;
|
||||
case 'task_list':
|
||||
@@ -144,6 +145,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
priority: a.priority,
|
||||
type: a.type,
|
||||
title: a.title,
|
||||
planning_mode_required: a.planning_mode_required,
|
||||
});
|
||||
break;
|
||||
case 'task_investigate':
|
||||
|
||||
@@ -63,6 +63,7 @@ interface TaskAddArgs {
|
||||
type?: string;
|
||||
priority?: string;
|
||||
description?: string;
|
||||
planning_mode_required?: boolean | null;
|
||||
}
|
||||
|
||||
interface TaskListArgs {
|
||||
@@ -79,13 +80,14 @@ interface TaskUpdateArgs {
|
||||
priority?: string;
|
||||
type?: string;
|
||||
title?: string;
|
||||
planning_mode_required?: boolean | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new task
|
||||
*/
|
||||
export async function taskAdd(args: TaskAddArgs): Promise<string> {
|
||||
const { title, project = 'Unknown', type = 'task', priority = 'P2', description = '' } = args;
|
||||
const { title, project = 'Unknown', type = 'task', priority = 'P2', description = '', planning_mode_required } = args;
|
||||
|
||||
// Get project key
|
||||
const projectKey = await getProjectKey(project);
|
||||
@@ -151,17 +153,19 @@ export async function taskAdd(args: TaskAddArgs): Promise<string> {
|
||||
const session_id = getSessionId();
|
||||
|
||||
// Insert task with session_id
|
||||
const planningValue = planning_mode_required !== undefined ? planning_mode_required : null;
|
||||
|
||||
if (embeddingValue) {
|
||||
await execute(
|
||||
`INSERT INTO tasks (id, project, title, description, type, status, priority, session_id, embedding)
|
||||
VALUES ($1, $2, $3, $4, $5, 'open', $6, $7, $8)`,
|
||||
[taskId, projectKey, title, description, type, priority, session_id, embeddingValue]
|
||||
`INSERT INTO tasks (id, project, title, description, type, status, priority, session_id, embedding, planning_mode_required)
|
||||
VALUES ($1, $2, $3, $4, $5, 'open', $6, $7, $8, $9)`,
|
||||
[taskId, projectKey, title, description, type, priority, session_id, embeddingValue, planningValue]
|
||||
);
|
||||
} else {
|
||||
await execute(
|
||||
`INSERT INTO tasks (id, project, title, description, type, status, priority, session_id)
|
||||
VALUES ($1, $2, $3, $4, $5, 'open', $6, $7)`,
|
||||
[taskId, projectKey, title, description, type, priority, session_id]
|
||||
`INSERT INTO tasks (id, project, title, description, type, status, priority, session_id, planning_mode_required)
|
||||
VALUES ($1, $2, $3, $4, $5, 'open', $6, $7, $8)`,
|
||||
[taskId, projectKey, title, description, type, priority, session_id, planningValue]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -326,7 +330,7 @@ export async function taskList(args: TaskListArgs): Promise<string> {
|
||||
*/
|
||||
export async function taskShow(id: string): Promise<string> {
|
||||
const task = await queryOne<Task & { session_id?: string }>(
|
||||
`SELECT id, project, title, description, type, status, priority, session_id,
|
||||
`SELECT id, project, title, description, type, status, priority, session_id, planning_mode_required,
|
||||
to_char(created_at, 'YYYY-MM-DD HH24:MI') as created,
|
||||
to_char(updated_at, 'YYYY-MM-DD HH24:MI') as updated,
|
||||
to_char(completed_at, 'YYYY-MM-DD HH24:MI') as completed
|
||||
@@ -355,6 +359,9 @@ export async function taskShow(id: string): Promise<string> {
|
||||
output += `**Created in session:** ${task.session_id}\n`;
|
||||
}
|
||||
|
||||
const planningLabel = task.planning_mode_required === true ? 'required' : task.planning_mode_required === false ? 'skipped' : 'auto-detect';
|
||||
output += `**Planning:** ${planningLabel}\n`;
|
||||
|
||||
if (task.description) {
|
||||
output += `\n**Description:**\n${task.description}\n`;
|
||||
}
|
||||
@@ -476,7 +483,7 @@ export async function taskClose(id: string): Promise<string> {
|
||||
* Update a task
|
||||
*/
|
||||
export async function taskUpdate(args: TaskUpdateArgs): Promise<string> {
|
||||
const { id, status, priority, type, title } = args;
|
||||
const { id, status, priority, type, title, planning_mode_required } = args;
|
||||
|
||||
// Get current values for activity tracking
|
||||
const task = await queryOne<{ status: string }>(`SELECT status FROM tasks WHERE id = $1`, [id]);
|
||||
@@ -507,6 +514,10 @@ export async function taskUpdate(args: TaskUpdateArgs): Promise<string> {
|
||||
updates.push(`title = $${paramIndex++}`);
|
||||
params.push(title);
|
||||
}
|
||||
if (planning_mode_required !== undefined) {
|
||||
updates.push(`planning_mode_required = $${paramIndex++}`);
|
||||
params.push(planning_mode_required);
|
||||
}
|
||||
|
||||
if (updates.length === 0) {
|
||||
return 'No updates specified';
|
||||
|
||||
@@ -13,6 +13,7 @@ export const toolDefinitions = [
|
||||
type: { type: 'string', enum: ['task', 'bug', 'feature', 'debt', 'investigation'], description: 'Task type (default: task)' },
|
||||
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'], description: 'Priority level (default: P2)' },
|
||||
description: { type: 'string', description: 'Optional description' },
|
||||
planning_mode_required: { type: 'boolean', description: 'Override planning mode: true=always plan, false=never plan, omit=auto-detect' },
|
||||
},
|
||||
required: ['title'],
|
||||
},
|
||||
@@ -24,7 +25,7 @@ export const toolDefinitions = [
|
||||
type: 'object',
|
||||
properties: {
|
||||
project: { type: 'string', description: 'Filter by project key' },
|
||||
status: { type: 'string', enum: ['open', 'in_progress', 'testing', 'blocked', 'completed'], description: 'Filter by status. Omit to show all non-completed tasks.' },
|
||||
status: { type: 'string', enum: ['open', 'in_progress', 'testing', 'blocked', 'completed'], description: 'Filter by exact status value. IMPORTANT: "open" only matches status=open, NOT in_progress/blocked/testing. Omit this parameter entirely to show ALL non-completed tasks.' },
|
||||
type: { type: 'string', enum: ['task', 'bug', 'feature', 'debt', 'investigation'], description: 'Filter by type' },
|
||||
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'], description: 'Filter by priority' },
|
||||
limit: { type: 'number', description: 'Max results (default: 20)' },
|
||||
@@ -64,6 +65,7 @@ export const toolDefinitions = [
|
||||
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'], description: 'New priority' },
|
||||
type: { type: 'string', enum: ['task', 'bug', 'feature', 'debt', 'investigation'], description: 'New type' },
|
||||
title: { type: 'string', description: 'New title' },
|
||||
planning_mode_required: { type: 'boolean', description: 'Override planning mode: true=always plan, false=never plan, null=auto-detect' },
|
||||
},
|
||||
required: ['id'],
|
||||
},
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface Task {
|
||||
priority: 'P0' | 'P1' | 'P2' | 'P3';
|
||||
version_id?: string;
|
||||
epic_id?: string;
|
||||
planning_mode_required?: boolean | null;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
completed_at?: Date;
|
||||
|
||||
Reference in New Issue
Block a user