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:
Christian Gick
2026-02-04 20:30:36 +02:00
parent baec42810c
commit 6cbb5ce6cb
5 changed files with 29 additions and 10 deletions

View 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;

View File

@@ -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':

View File

@@ -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';

View File

@@ -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'],
},

View File

@@ -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;