Skip to content

Commit 05549dc

Browse files
Merge pull request #53 from chriscarrollsmith/49-optimize-cli-commands
Set autoApprove to true by default, and provide better CLI approval intructions
2 parents 94b788c + c2223d1 commit 05549dc

13 files changed

+86
-16
lines changed

.cursor/rules/general.mdc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ description:
33
globs:
44
alwaysApply: true
55
---
6-
Work step-by-step. If presented with an implementation plan, implement the plan exactly. If the plan presents more than one implementation option, consult with the human user to decide between options. If you are tempted to embellish or imporve upon the plan, consult with the human user. Always complete the current task and wait for human review before proceeding to the next task.
6+
Work step-by-step. If presented with an implementation plan, implement the plan exactly. If the plan presents more than one implementation option, consult with the human user to decide between options. If you are tempted to embellish or imporve upon the plan, consult with the human user. Always complete the current task and wait for human review before proceeding to the next task.
7+
8+
In developing this codebase, we are doing test-driven development with an integration testing (as opposed to a unit testing) verification strategy. Before writing any code (except perhaps for empty function or class signatures), we will write tests and run them to make sure they fail. The red phase is not complete until the tests are failing correctly.

.cursor/rules/tests.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ description: Writing unit tests with `jest`
33
globs: tests/**/*
44
alwaysApply: false
55
---
6-
Make use of the helpers in tests/mcp/test-helpers.ts.
6+
Make use of the helpers in tests/mcp/test-helpers.ts for writing tests.

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Usually you will set the tool configuration in Claude Desktop, Cursor, or anothe
2929
}
3030
```
3131

32-
To use the CLI utility, you can use the following command:
32+
To use the CLI utility, you can install the package globally and then use the following command:
3333

3434
```bash
3535
npx taskqueue --help
@@ -145,6 +145,8 @@ npx --package=taskqueue-mcp taskqueue --help
145145

146146
#### Task Approval
147147

148+
By default, all tasks and projects will be auto-approved when marked "done" by the AI agent. To require manual human task approval, set `autoApprove` to `false` when creating a project.
149+
148150
Task approval is controlled exclusively by the human user through the CLI:
149151

150152
```bash
@@ -154,7 +156,7 @@ npx taskqueue approve-task -- <projectId> <taskId>
154156
Options:
155157
- `-f, --force`: Force approval even if the task is not marked as done
156158

157-
Note: Tasks must be marked as "done" with completed details before they can be approved (unless using --force).
159+
Note: Tasks must be marked as "done" with completed details by the AI agent before they can be approved (unless using --force).
158160

159161
#### Listing Tasks and Projects
160162

@@ -221,6 +223,7 @@ TaskManagerFile
221223
├── initialPrompt: string # Original user request text
222224
├── projectPlan: string # Additional project details
223225
├── completed: boolean # Project completion status
226+
├── autoApprove: boolean # Set `false` to require manual user approval
224227
└── tasks: Task[] # Array of tasks
225228
├── id: string # Format: "task-{number}"
226229
├── title: string # Short task title

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "taskqueue-mcp",
3-
"version": "1.4.0",
3+
"version": "1.4.1",
44
"description": "Task Queue MCP Server",
55
"author": "Christopher C. Smith ([email protected])",
66
"main": "dist/src/server/index.js",

src/client/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const program = new Command();
1414
program
1515
.name("taskqueue")
1616
.description("CLI for the Task Manager MCP Server")
17-
.version("1.4.0")
17+
.version("1.4.1")
1818
.option(
1919
'-f, --file-path <path>',
2020
'Specify the path to the tasks JSON file. Overrides TASK_MANAGER_FILE_PATH env var.'

src/server/TaskManager.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
AddTasksSuccessData,
1616
DeleteTaskSuccessData,
1717
ReadProjectSuccessData,
18+
UpdateTaskSuccessData
1819
} from "../types/response.js";
1920
import { AppError, AppErrorCode } from "../types/errors.js";
2021
import { FileSystemService } from "./FileSystemService.js";
@@ -134,7 +135,7 @@ export class TaskManager {
134135
projectPlan: projectPlan || initialPrompt,
135136
tasks: newTasks,
136137
completed: false,
137-
autoApprove: autoApprove === true ? true : false,
138+
autoApprove: autoApprove === false ? false : true,
138139
};
139140

140141
this.data.projects.push(newProject);
@@ -519,7 +520,7 @@ export class TaskManager {
519520
status?: "not started" | "in progress" | "done";
520521
completedDetails?: string;
521522
}
522-
): Promise<Task> {
523+
): Promise<UpdateTaskSuccessData> {
523524
await this.ensureInitialized();
524525
await this.reloadFromDisk();
525526

@@ -544,8 +545,14 @@ export class TaskManager {
544545
// Apply updates
545546
Object.assign(task, updates);
546547

548+
// Generate message if needed
549+
let message: string | undefined = undefined;
550+
if (updates.status === 'done' && proj.autoApprove === false) {
551+
message = `Task marked as done but requires human approval.\nTo approve, user should run: npx taskqueue approve-task -- ${projectId} ${taskId}`;
552+
}
553+
547554
await this.saveTasks();
548-
return task;
555+
return { task, message };
549556
}
550557

551558
public async deleteTask(projectId: string, taskId: string): Promise<DeleteTaskSuccessData> {

src/server/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprot
1010
const server = new Server(
1111
{
1212
name: "task-manager-server",
13-
version: "1.4.0"
13+
version: "1.4.1"
1414
},
1515
{
1616
capabilities: {

src/server/toolExecutors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ const createProjectToolExecutor: ToolExecutor = {
152152
const initialPrompt = validateRequiredStringParam(args.initialPrompt, "initialPrompt");
153153
const validatedTasks = validateTaskObjects(args.tasks);
154154
const projectPlan = args.projectPlan !== undefined ? String(args.projectPlan) : undefined;
155-
const autoApprove = args.autoApprove === true;
155+
const autoApprove = args.autoApprove as boolean | undefined;
156156

157157
if (args.projectPlan !== undefined && typeof args.projectPlan !== 'string') {
158158
throw new AppError(

src/server/tools.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { TaskManager } from "./TaskManager.js";
33
import { toolExecutorMap } from "./toolExecutors.js";
44
import { AppError, AppErrorCode } from "../types/errors.js";
55
import { McpError, CallToolResult, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
6+
import { UpdateTaskSuccessData } from '../types/response.js';
67

78
// ---------------------- PROJECT TOOLS ----------------------
89

@@ -468,9 +469,11 @@ export async function executeToolAndHandleErrors(
468469
// 2. Execute the tool - Validation errors (protocol) or TaskManager errors (execution) might be thrown
469470
const resultData = await executor.execute(taskManager, args);
470471

471-
// 3. Format successful execution result
472+
// 3. Format successful execution result using standard stringify
473+
const responseText = JSON.stringify(resultData, null, 2);
474+
472475
return {
473-
content: [{ type: "text", text: JSON.stringify(resultData, null, 2) }]
476+
content: [{ type: "text", text: responseText }]
474477
};
475478

476479
} catch (error: AppError | unknown) {

src/types/response.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export interface ProjectCreationSuccessData {
5555
export interface DeleteTaskSuccessData {
5656
message: string;
5757
}
58-
58+
5959
export interface ReadProjectSuccessData {
6060
projectId: string;
6161
initialPrompt: string;
@@ -64,4 +64,10 @@ export interface ProjectCreationSuccessData {
6464
autoApprove?: boolean;
6565
tasks: Task[];
6666
}
67+
68+
// Add the new interface for update_task success
69+
export interface UpdateTaskSuccessData {
70+
task: Task; // The updated task object
71+
message?: string; // Optional message (e.g., approval reminder)
72+
}
6773

tests/mcp/tools/create-project.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ describe('create_project Tool', () => {
111111
"Task 2",
112112
"Task 3"
113113
]);
114+
expect(project).toHaveProperty('autoApprove', true);
114115
});
115116

116117
it('should create a project with auto-approve enabled', async () => {

tests/mcp/tools/update-task.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,54 @@ describe('update_task Tool', () => {
8282
});
8383
});
8484

85+
it('should return reminder message when marking task done in a project requiring approval', async () => {
86+
// Create a project that requires approval
87+
const project = await createTestProjectInFile(context.testFilePath, {
88+
initialPrompt: "Project Requiring Approval",
89+
autoApprove: false // Explicitly set for clarity
90+
});
91+
const task = await createTestTaskInFile(context.testFilePath, project.projectId, {
92+
title: "Task to be Approved",
93+
status: "in progress"
94+
});
95+
96+
// Mark the task as done
97+
const result = await context.client.callTool({
98+
name: "update_task",
99+
arguments: {
100+
projectId: project.projectId,
101+
taskId: task.id,
102+
status: "done",
103+
completedDetails: "Task finished, awaiting approval."
104+
}
105+
}) as CallToolResult;
106+
107+
// Verify the response includes the approval reminder within the JSON structure
108+
verifyCallToolResult(result); // Basic verification
109+
expect(result.isError).toBeFalsy();
110+
const responseText = (result.content[0] as { text: string }).text;
111+
// Parse the JSON response
112+
const responseData = JSON.parse(responseText);
113+
114+
// Check the message property
115+
expect(responseData).toHaveProperty('message');
116+
const expectedMessage = `Task marked as done but requires human approval.\nTo approve, user should run: npx taskqueue approve-task -- ${project.projectId} ${task.id}`;
117+
expect(responseData.message).toBe(expectedMessage);
118+
119+
// Check that the core task data is present under the 'task' key
120+
expect(responseData).toHaveProperty('task');
121+
expect(responseData.task.id).toBe(task.id);
122+
expect(responseData.task.status).toBe('done');
123+
expect(responseData.task.completedDetails).toBe("Task finished, awaiting approval.");
124+
125+
// Also verify the task state in the file
126+
await verifyTaskInFile(context.testFilePath, project.projectId, task.id, {
127+
status: "done",
128+
completedDetails: "Task finished, awaiting approval.",
129+
approved: false // Should not be approved yet
130+
});
131+
});
132+
85133
it('should update task title and description', async () => {
86134
const project = await createTestProjectInFile(context.testFilePath, {
87135
initialPrompt: "Test Project"

0 commit comments

Comments
 (0)