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,
|
type: a.type,
|
||||||
priority: a.priority,
|
priority: a.priority,
|
||||||
description: a.description,
|
description: a.description,
|
||||||
|
planning_mode_required: a.planning_mode_required,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'task_list':
|
case 'task_list':
|
||||||
@@ -144,6 +145,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|||||||
priority: a.priority,
|
priority: a.priority,
|
||||||
type: a.type,
|
type: a.type,
|
||||||
title: a.title,
|
title: a.title,
|
||||||
|
planning_mode_required: a.planning_mode_required,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'task_investigate':
|
case 'task_investigate':
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ interface TaskAddArgs {
|
|||||||
type?: string;
|
type?: string;
|
||||||
priority?: string;
|
priority?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
planning_mode_required?: boolean | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TaskListArgs {
|
interface TaskListArgs {
|
||||||
@@ -79,13 +80,14 @@ interface TaskUpdateArgs {
|
|||||||
priority?: string;
|
priority?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
planning_mode_required?: boolean | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new task
|
* Create a new task
|
||||||
*/
|
*/
|
||||||
export async function taskAdd(args: TaskAddArgs): Promise<string> {
|
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
|
// Get project key
|
||||||
const projectKey = await getProjectKey(project);
|
const projectKey = await getProjectKey(project);
|
||||||
@@ -151,17 +153,19 @@ export async function taskAdd(args: TaskAddArgs): Promise<string> {
|
|||||||
const session_id = getSessionId();
|
const session_id = getSessionId();
|
||||||
|
|
||||||
// Insert task with session_id
|
// Insert task with session_id
|
||||||
|
const planningValue = planning_mode_required !== undefined ? planning_mode_required : null;
|
||||||
|
|
||||||
if (embeddingValue) {
|
if (embeddingValue) {
|
||||||
await execute(
|
await execute(
|
||||||
`INSERT INTO tasks (id, project, title, description, type, status, priority, session_id, embedding)
|
`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)`,
|
VALUES ($1, $2, $3, $4, $5, 'open', $6, $7, $8, $9)`,
|
||||||
[taskId, projectKey, title, description, type, priority, session_id, embeddingValue]
|
[taskId, projectKey, title, description, type, priority, session_id, embeddingValue, planningValue]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await execute(
|
await execute(
|
||||||
`INSERT INTO tasks (id, project, title, description, type, status, 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)`,
|
VALUES ($1, $2, $3, $4, $5, 'open', $6, $7, $8)`,
|
||||||
[taskId, projectKey, title, description, type, priority, session_id]
|
[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> {
|
export async function taskShow(id: string): Promise<string> {
|
||||||
const task = await queryOne<Task & { session_id?: 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(created_at, 'YYYY-MM-DD HH24:MI') as created,
|
||||||
to_char(updated_at, 'YYYY-MM-DD HH24:MI') as updated,
|
to_char(updated_at, 'YYYY-MM-DD HH24:MI') as updated,
|
||||||
to_char(completed_at, 'YYYY-MM-DD HH24:MI') as completed
|
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`;
|
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) {
|
if (task.description) {
|
||||||
output += `\n**Description:**\n${task.description}\n`;
|
output += `\n**Description:**\n${task.description}\n`;
|
||||||
}
|
}
|
||||||
@@ -476,7 +483,7 @@ export async function taskClose(id: string): Promise<string> {
|
|||||||
* Update a task
|
* Update a task
|
||||||
*/
|
*/
|
||||||
export async function taskUpdate(args: TaskUpdateArgs): Promise<string> {
|
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
|
// Get current values for activity tracking
|
||||||
const task = await queryOne<{ status: string }>(`SELECT status FROM tasks WHERE id = $1`, [id]);
|
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++}`);
|
updates.push(`title = $${paramIndex++}`);
|
||||||
params.push(title);
|
params.push(title);
|
||||||
}
|
}
|
||||||
|
if (planning_mode_required !== undefined) {
|
||||||
|
updates.push(`planning_mode_required = $${paramIndex++}`);
|
||||||
|
params.push(planning_mode_required);
|
||||||
|
}
|
||||||
|
|
||||||
if (updates.length === 0) {
|
if (updates.length === 0) {
|
||||||
return 'No updates specified';
|
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)' },
|
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)' },
|
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'], description: 'Priority level (default: P2)' },
|
||||||
description: { type: 'string', description: 'Optional description' },
|
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'],
|
required: ['title'],
|
||||||
},
|
},
|
||||||
@@ -24,7 +25,7 @@ export const toolDefinitions = [
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
project: { type: 'string', description: 'Filter by project key' },
|
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' },
|
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' },
|
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'], description: 'Filter by priority' },
|
||||||
limit: { type: 'number', description: 'Max results (default: 20)' },
|
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' },
|
priority: { type: 'string', enum: ['P0', 'P1', 'P2', 'P3'], description: 'New priority' },
|
||||||
type: { type: 'string', enum: ['task', 'bug', 'feature', 'debt', 'investigation'], description: 'New type' },
|
type: { type: 'string', enum: ['task', 'bug', 'feature', 'debt', 'investigation'], description: 'New type' },
|
||||||
title: { type: 'string', description: 'New title' },
|
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'],
|
required: ['id'],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface Task {
|
|||||||
priority: 'P0' | 'P1' | 'P2' | 'P3';
|
priority: 'P0' | 'P1' | 'P2' | 'P3';
|
||||||
version_id?: string;
|
version_id?: string;
|
||||||
epic_id?: string;
|
epic_id?: string;
|
||||||
|
planning_mode_required?: boolean | null;
|
||||||
created_at: Date;
|
created_at: Date;
|
||||||
updated_at: Date;
|
updated_at: Date;
|
||||||
completed_at?: Date;
|
completed_at?: Date;
|
||||||
|
|||||||
Reference in New Issue
Block a user