@programinglive/dev-workflow-mcp-server 1.0.8 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,620 @@
1
+ const textResponse = (text) => ({
2
+ content: [
3
+ {
4
+ type: "text",
5
+ text,
6
+ },
7
+ ],
8
+ });
9
+
10
+ function formatChecklist(checks) {
11
+ return checks.map((c) => `${c.done ? "✅" : "❌"} ${c.name}`).join("\n");
12
+ }
13
+
14
+ function summarizeHistory(history, limit) {
15
+ const recent = history.slice(-limit).reverse();
16
+ if (recent.length === 0) {
17
+ return null;
18
+ }
19
+
20
+ const historyText = recent
21
+ .map(
22
+ (entry, index) =>
23
+ `${index + 1}. [${new Date(entry.timestamp).toLocaleString()}]\n Task: ${entry.taskDescription}\n Type: ${entry.taskType}\n Commit: ${entry.commitMessage}\n`
24
+ )
25
+ .join("\n");
26
+
27
+ return `📜 Workflow History (last ${recent.length} tasks)\n\n${historyText}`;
28
+ }
29
+
30
+ function getContinueGuidance(status) {
31
+ switch (status.currentPhase) {
32
+ case "coding": {
33
+ const task = status.taskDescription ? ` "${status.taskDescription}"` : "";
34
+ return `Finish implementing${task} and then run 'mark_bug_fixed' with a brief summary of the change.`;
35
+ }
36
+ case "testing":
37
+ if (!status.testsCreated) {
38
+ return "Create automated coverage now using 'create_tests' before recording any results.";
39
+ }
40
+
41
+ if (status.testsPassed) {
42
+ return "With tests green, move on to 'create_documentation' and capture what changed.";
43
+ }
44
+
45
+ if (status.testsSkipped) {
46
+ return "Document why tests were skipped using 'create_documentation' so the workflow can proceed.";
47
+ }
48
+
49
+ return status.testCommand
50
+ ? `Fix any failing tests and re-run '${status.testCommand}'. Once they pass, record the results with 'run_tests'.`
51
+ : "Run your automated tests and record the outcome with 'run_tests'. Fix any failures before moving forward.";
52
+ case "documentation":
53
+ return status.testsSkipped
54
+ ? `Document the change (including manual QA for skipped tests) with 'create_documentation'. Reason recorded: ${
55
+ status.testsSkippedReason || "(none provided)"
56
+ }.`
57
+ : "Capture your updates with 'create_documentation' (provide the documentation type and a short summary).";
58
+ case "ready":
59
+ return "Run 'check_ready_to_commit' to verify all prerequisite steps before committing.";
60
+ case "commit":
61
+ return "All checks passed—run 'commit_and_push' with your commit message (and optional branch).";
62
+ case "release":
63
+ return "Commit recorded. Execute 'perform_release' with the release command you ran to publish changes.";
64
+ case "ready_to_complete":
65
+ return "Release recorded. Finish up by calling 'complete_task' with the commit message you used.";
66
+ default:
67
+ return "Review your workflow progress and complete the next required step.";
68
+ }
69
+ }
70
+
71
+ async function handleContinueWorkflow(workflowState) {
72
+ const status = workflowState.state;
73
+
74
+ if (!status || status.currentPhase === "idle") {
75
+ return textResponse("⚠️ No active workflow. Use 'start_task' to kick off a new task before continuing.");
76
+ }
77
+
78
+ if (status.currentPhase === "ready") {
79
+ return handleReadyCheck(workflowState);
80
+ }
81
+
82
+ const sections = [
83
+ `📊 Current Phase: ${status.currentPhase}`,
84
+ `📝 Task: ${status.taskDescription || "(none)"}`,
85
+ ];
86
+
87
+ if (status.testsSkipped) {
88
+ sections.push(`⚠️ Tests skipped: ${status.testsSkippedReason || "(no reason provided)"}`);
89
+ }
90
+
91
+ if (
92
+ status.currentPhase === "testing" &&
93
+ status.testsCreated &&
94
+ !status.testsPassed &&
95
+ typeof status.testCommand === "string" &&
96
+ status.testCommand.trim() !== ""
97
+ ) {
98
+ sections.push(`🧪 Last test command: ${status.testCommand}`);
99
+ }
100
+
101
+ sections.push(`➡️ ${getContinueGuidance(status)}`);
102
+
103
+ return textResponse(sections.join("\n\n"));
104
+ }
105
+
106
+ async function handleStartTask(args, workflowState) {
107
+ workflowState.reset();
108
+ workflowState.state.currentPhase = "coding";
109
+ workflowState.state.taskDescription = args.description;
110
+ workflowState.state.taskType = args.type;
111
+ workflowState.state.testsSkipped = false;
112
+ workflowState.state.testsSkippedReason = "";
113
+ workflowState.state.commitAndPushCompleted = false;
114
+ workflowState.state.lastCommitMessage = "";
115
+ workflowState.state.lastPushBranch = "";
116
+ await workflowState.save();
117
+
118
+ return textResponse(
119
+ `✅ Task Started: ${args.description}\n\n🎯 Be conscious about what you're coding!\n\nWorkflow Steps:\n1. ✓ Start task (current)\n2. ⏳ Fix/implement the feature\n3. ⏳ Create tests\n4. ⏳ Run tests (must pass!)\n5. ⏳ Create documentation\n6. ⏳ Run 'check_ready_to_commit'\n7. ⏳ Run 'commit_and_push' (commits and pushes)\n8. ⏳ Run 'perform_release' (handles versioning and tags)\n9. ⏳ Complete task\n\nReminder: Focus on writing clean, maintainable code!`
120
+ );
121
+ }
122
+
123
+ async function handleMarkBugFixed(args, workflowState) {
124
+ if (workflowState.state.currentPhase === "idle") {
125
+ return textResponse("⚠️ Please start a task first using 'start_task'!");
126
+ }
127
+
128
+ workflowState.state.bugFixed = true;
129
+ workflowState.state.testsCreated = false;
130
+ workflowState.state.currentPhase = "testing";
131
+ workflowState.state.testsSkipped = false;
132
+ workflowState.state.testsSkippedReason = "";
133
+ workflowState.state.fixSummary = args.summary;
134
+ workflowState.state.readyCheckCompleted = false;
135
+ workflowState.state.released = false;
136
+ workflowState.state.releaseCommand = "";
137
+ workflowState.state.releaseNotes = "";
138
+ workflowState.state.commitAndPushCompleted = false;
139
+ workflowState.state.lastCommitMessage = "";
140
+ workflowState.state.lastPushBranch = "";
141
+ await workflowState.save();
142
+
143
+ return textResponse(
144
+ `✅ Feature/Bug marked as fixed!\n\n⚠️ CRITICAL REMINDER: You MUST create tests now!\n\nNext Steps:\n1. ✓ Fix/implement feature\n2. ⏳ Create tests for: ${args.summary}\n3. ⏳ Run tests (must be green!)\n4. ⏳ Create documentation\n5. ⏳ Run 'check_ready_to_commit'\n6. ⏳ Run 'commit_and_push'\n7. ⏳ Run 'perform_release'\n8. ⏳ Complete task\n\n🚫 DO NOT SKIP TESTING!`
145
+ );
146
+ }
147
+
148
+ async function handleCreateTests(workflowState) {
149
+ if (!workflowState.state.bugFixed) {
150
+ return textResponse("⚠️ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!");
151
+ }
152
+
153
+ workflowState.state.testsCreated = true;
154
+ workflowState.state.testsSkipped = false;
155
+ workflowState.state.testsSkippedReason = "";
156
+ workflowState.state.readyCheckCompleted = false;
157
+ workflowState.state.released = false;
158
+ workflowState.state.commitAndPushCompleted = false;
159
+ workflowState.state.lastCommitMessage = "";
160
+ workflowState.state.lastPushBranch = "";
161
+ await workflowState.save();
162
+
163
+ return textResponse(
164
+ "✅ Tests recorded!\n\nNext Steps:\n1. ✓ Fix/implement feature\n2. ✓ Create tests\n3. ⏳ Run tests (must be green!)\n4. ⏳ Create documentation\n5. ⏳ Run 'check_ready_to_commit'\n6. ⏳ Run 'commit_and_push'\n7. ⏳ Run 'perform_release'\n8. ⏳ Complete task\n\n🧪 Run your test command and record the results using 'run_tests'."
165
+ );
166
+ }
167
+
168
+ async function handleSkipTests(args, workflowState) {
169
+ if (workflowState.state.currentPhase === "idle") {
170
+ return textResponse("⚠️ No active task. Use 'start_task' before skipping tests.");
171
+ }
172
+
173
+ if (!workflowState.state.bugFixed) {
174
+ return textResponse("⚠️ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!");
175
+ }
176
+
177
+ const reason = typeof args.reason === "string" ? args.reason.trim() : "";
178
+
179
+ if (!reason) {
180
+ return textResponse("⚠️ Provide a non-empty justification when skipping tests.");
181
+ }
182
+
183
+ workflowState.state.testsCreated = true;
184
+ workflowState.state.testsPassed = true;
185
+ workflowState.state.testsSkipped = true;
186
+ workflowState.state.testsSkippedReason = reason;
187
+ workflowState.state.currentPhase = "documentation";
188
+ workflowState.state.readyToCommit = false;
189
+ workflowState.state.readyCheckCompleted = false;
190
+ workflowState.state.released = false;
191
+ workflowState.state.commitAndPushCompleted = false;
192
+ workflowState.state.lastCommitMessage = "";
193
+ workflowState.state.lastPushBranch = "";
194
+ await workflowState.save();
195
+
196
+ return textResponse(
197
+ `⚠️ Tests skipped for this task.
198
+ Reason: ${reason}
199
+
200
+ Manual verification is now required.
201
+
202
+ Next Steps:
203
+ 1. ✓ Fix/implement feature
204
+ 2. ⚠️ Tests skipped (ensure manual QA)
205
+ 3. ⏳ Create/update documentation
206
+ 4. ⏳ Run 'check_ready_to_commit'
207
+ 5. ⏳ Run 'commit_and_push'
208
+ 6. ⏳ Run 'perform_release'
209
+ 7. ⏳ Complete task`
210
+ );
211
+ }
212
+
213
+ async function handleRunTests(args, workflowState) {
214
+ if (!workflowState.state.bugFixed) {
215
+ return textResponse("⚠️ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!");
216
+ }
217
+
218
+ if (!workflowState.state.testsCreated) {
219
+ return textResponse("⚠️ Please create tests first using 'create_tests' before recording test results!");
220
+ }
221
+
222
+ if (
223
+ typeof args.passed !== "boolean" ||
224
+ typeof args.testCommand !== "string" ||
225
+ args.testCommand.trim() === ""
226
+ ) {
227
+ return textResponse(
228
+ "⚠️ Please provide both 'passed' (boolean) and 'testCommand' (non-empty string) when recording test results."
229
+ );
230
+ }
231
+
232
+ workflowState.state.testsPassed = args.passed;
233
+ workflowState.state.testCommand = args.testCommand;
234
+ workflowState.state.testDetails = typeof args.details === "string" ? args.details : "";
235
+ workflowState.state.testsSkipped = false;
236
+ workflowState.state.testsSkippedReason = "";
237
+ workflowState.state.readyCheckCompleted = false;
238
+ workflowState.state.released = false;
239
+ workflowState.state.commitAndPushCompleted = false;
240
+ workflowState.state.lastCommitMessage = "";
241
+ workflowState.state.lastPushBranch = "";
242
+
243
+ if (!args.passed) {
244
+ workflowState.state.currentPhase = "testing";
245
+ await workflowState.save();
246
+
247
+ return textResponse(
248
+ `❌ TESTS FAILED!\n\n🚫 STOP! DO NOT COMMIT OR PUSH!\n\nYou must:\n1. Fix the failing tests\n2. Run tests again until they pass\n\nTest command: ${args.testCommand}\n\nNever skip or ignore failing tests!`
249
+ );
250
+ }
251
+
252
+ workflowState.state.currentPhase = "documentation";
253
+ await workflowState.save();
254
+
255
+ return textResponse(
256
+ `✅ All tests passed! 🎉\n\nTest command: ${args.testCommand}\n\nNext Steps:\n1. ✓ Fix/implement feature\n2. ✓ Create tests\n3. ✓ Run tests (GREEN!)\n4. ⏳ Create/update documentation\n5. ⏳ Run 'check_ready_to_commit'\n6. ⏳ Run 'commit_and_push'\n7. ⏳ Run 'perform_release'\n8. ⏳ Complete task\n\nReminder: Document what you did before committing!`
257
+ );
258
+ }
259
+
260
+ async function handleCreateDocumentation(args, workflowState) {
261
+ if (!workflowState.state.testsPassed) {
262
+ return textResponse("⚠️ Please ensure tests are passing first! Run 'run_tests' with passed=true.");
263
+ }
264
+
265
+ workflowState.state.documentationCreated = true;
266
+ workflowState.state.documentationType = args.documentationType;
267
+ workflowState.state.documentationSummary = args.summary;
268
+ workflowState.state.currentPhase = "ready";
269
+ workflowState.state.readyToCommit = true;
270
+ workflowState.state.readyCheckCompleted = false;
271
+ workflowState.state.released = false;
272
+ workflowState.state.commitAndPushCompleted = false;
273
+ workflowState.state.lastCommitMessage = "";
274
+ workflowState.state.lastPushBranch = "";
275
+ await workflowState.save();
276
+
277
+ return textResponse(
278
+ `✅ Documentation created!\n\nType: ${args.documentationType}\nSummary: ${args.summary}\n\n🎉 You're ready to verify your work!\n\nNext Steps:\n1. ✓ Fix/implement feature\n2. ✓ Create tests\n3. ✓ Run tests (GREEN!)\n4. ✓ Create documentation\n5. ⏳ Run 'check_ready_to_commit' to verify\n6. ⏳ Run 'commit_and_push'\n7. ⏳ Run 'perform_release'\n8. ⏳ Mark as complete with 'complete_task'`
279
+ );
280
+ }
281
+
282
+ async function handleReadyCheck(workflowState) {
283
+ const status = workflowState.state;
284
+ const testsCreatedDone = status.testsSkipped || status.testsCreated;
285
+ const testsPassedDone = status.testsSkipped || status.testsPassed;
286
+ const checks = [
287
+ { name: "Task started", done: status.currentPhase !== "idle" },
288
+ { name: "Feature/bug fixed", done: status.bugFixed },
289
+ {
290
+ name: status.testsSkipped ? "Tests skipped (manual QA documented)" : "Tests created",
291
+ done: testsCreatedDone,
292
+ },
293
+ {
294
+ name: status.testsSkipped ? "Tests skipped acknowledged" : "Tests passed",
295
+ done: testsPassedDone,
296
+ },
297
+ { name: "Documentation created", done: status.documentationCreated },
298
+ ];
299
+
300
+ const allDone = checks.every((c) => c.done);
301
+ const checkList = formatChecklist(checks);
302
+
303
+ workflowState.state.readyCheckCompleted = allDone;
304
+ if (allDone) {
305
+ workflowState.state.currentPhase = workflowState.state.commitAndPushCompleted
306
+ ? workflowState.state.released
307
+ ? "ready_to_complete"
308
+ : "release"
309
+ : "commit";
310
+ }
311
+ await workflowState.save();
312
+
313
+ if (allDone) {
314
+ return textResponse(
315
+ `🎉 ALL CHECKS PASSED!\n\n${checkList}\n\n✅ Next actions:\n1. Run 'commit_and_push' (commits and pushes your changes)\n2. Run 'perform_release' (handles versioning, tags, and final push)\n3. Finish with 'complete_task'\n\nTip: Provide the optional 'branch' argument to 'commit_and_push' to push to a non-default branch.\n\nTask: ${status.taskDescription}`
316
+ );
317
+ }
318
+
319
+ return textResponse(`⚠️ NOT READY TO COMMIT!\n\n${checkList}\n\nPlease complete all steps before committing.`);
320
+ }
321
+
322
+ async function handleCommitAndPush(args, context) {
323
+ const { workflowState, exec, git, utils } = context;
324
+ if (!workflowState.state.readyToCommit) {
325
+ return textResponse("⚠️ Not ready yet! Run 'check_ready_to_commit' and complete all steps first.");
326
+ }
327
+
328
+ if (!workflowState.state.readyCheckCompleted) {
329
+ return textResponse(
330
+ "⚠️ Please run 'check_ready_to_commit' and resolve any outstanding items before committing!"
331
+ );
332
+ }
333
+
334
+ const providedCommitMessage =
335
+ typeof args.commitMessage === "string" ? args.commitMessage.trim() : "";
336
+
337
+ if (!(await git.hasWorkingChanges())) {
338
+ return textResponse(
339
+ "⚠️ No changes detected. Make sure you have modifications to commit before running 'commit_and_push'."
340
+ );
341
+ }
342
+
343
+ if (!(await git.hasTestChanges()) && !workflowState.state.testsSkipped) {
344
+ return textResponse(
345
+ "⚠️ Please include test updates in your changes before running 'commit_and_push'. Ensure at least one test file is modified."
346
+ );
347
+ }
348
+
349
+ try {
350
+ await exec("git add .");
351
+ } catch (error) {
352
+ return textResponse(
353
+ `❌ Failed to stage files:\n\n${error.stderr || error.stdout || error.message}`
354
+ );
355
+ }
356
+
357
+ const stagedChanges = await git.getStagedChanges();
358
+ const { summary: generatedSummary, body: commitBody } = utils.createCommitMessageParts(
359
+ stagedChanges,
360
+ providedCommitMessage
361
+ );
362
+
363
+ if (!generatedSummary) {
364
+ return textResponse("⚠️ Unable to generate commit message. Please provide a 'commitMessage' manually.");
365
+ }
366
+
367
+ try {
368
+ if (commitBody) {
369
+ await exec(
370
+ `git commit -m ${utils.shellEscape(generatedSummary)} -m ${utils.shellEscape(commitBody)}`
371
+ );
372
+ } else {
373
+ await exec(`git commit -m ${utils.shellEscape(generatedSummary)}`);
374
+ }
375
+ } catch (error) {
376
+ const output = error.stderr || error.stdout || error.message || "Unknown error";
377
+ if (output.includes("nothing to commit")) {
378
+ return textResponse(
379
+ "⚠️ Nothing to commit. Make additional changes before running 'commit_and_push'."
380
+ );
381
+ }
382
+
383
+ return textResponse(`❌ git commit failed:\n\n${output}`);
384
+ }
385
+
386
+ const requestedBranch = typeof args.branch === "string" ? args.branch.trim() : "";
387
+ const currentBranch = await git.getCurrentBranch();
388
+ const branchForPush = requestedBranch || currentBranch || "";
389
+
390
+ try {
391
+ const pushCommand = branchForPush
392
+ ? `git push origin ${utils.shellEscape(branchForPush)}`
393
+ : "git push";
394
+ await exec(pushCommand);
395
+ } catch (error) {
396
+ return textResponse(
397
+ `❌ git push failed:\n\n${error.stderr || error.stdout || error.message}`
398
+ );
399
+ }
400
+
401
+ workflowState.state.commitAndPushCompleted = true;
402
+ workflowState.state.lastCommitMessage = generatedSummary;
403
+ workflowState.state.lastPushBranch = branchForPush;
404
+ workflowState.state.currentPhase = workflowState.state.released ? "ready_to_complete" : "release";
405
+ await workflowState.save();
406
+
407
+ return textResponse(
408
+ `✅ Commit and push completed!\n\nCommit message: ${generatedSummary}\nPushed to: ${
409
+ branchForPush || "(default upstream)"
410
+ }\n\nNext: Run 'perform_release' to handle the release and push tags.`
411
+ );
412
+ }
413
+
414
+ async function handlePerformRelease(args, context) {
415
+ const { workflowState, exec, git, utils } = context;
416
+
417
+ if (workflowState.state.currentPhase === "idle") {
418
+ return textResponse("⚠️ Please start a task first using 'start_task'!");
419
+ }
420
+
421
+ if (!workflowState.state.commitAndPushCompleted) {
422
+ return textResponse(
423
+ "⚠️ Please run 'commit_and_push' after the ready check before recording a release!"
424
+ );
425
+ }
426
+
427
+ if (!workflowState.state.readyCheckCompleted) {
428
+ return textResponse(
429
+ "⚠️ Please run 'check_ready_to_commit' and ensure all checks pass before releasing!"
430
+ );
431
+ }
432
+
433
+ const releaseCommand = typeof args.command === "string" ? args.command.trim() : "";
434
+
435
+ if (!releaseCommand) {
436
+ return textResponse("⚠️ Please provide a non-empty 'command' when using 'perform_release'.");
437
+ }
438
+
439
+ let statusOutput;
440
+ try {
441
+ const { stdout } = await exec("git status --porcelain");
442
+ statusOutput = stdout;
443
+ } catch (error) {
444
+ const details = error.stderr || error.stdout || error.message || "Unknown error";
445
+ return textResponse(
446
+ `⚠️ Unable to verify Git status. Refusing to run the release command until the repository state can be checked.\n\nDetails:\n${details}`
447
+ );
448
+ }
449
+
450
+ const statusSummary = git.workingTreeSummary(statusOutput);
451
+ if (statusSummary.hasChanges) {
452
+ const changeDetails =
453
+ statusSummary.lines.length > 0
454
+ ? statusSummary.lines.map((line) => `• ${line}`).join("\n")
455
+ : "• (unable to list changes)";
456
+
457
+ return textResponse(
458
+ `⚠️ Working tree not clean! Please commit or stash all changes before running 'perform_release'.\n\nDetected changes:\n${changeDetails}`
459
+ );
460
+ }
461
+
462
+ const lastCommitMessage = await git.getLastCommitMessage();
463
+ const releaseType = utils.determineReleaseTypeFromCommit(
464
+ lastCommitMessage || workflowState.state.lastCommitMessage
465
+ );
466
+
467
+ const finalCommand = releaseCommand.includes("--release-as")
468
+ ? releaseCommand
469
+ : `${releaseCommand} -- --release-as ${releaseType}`;
470
+
471
+ try {
472
+ await exec(finalCommand);
473
+ } catch (error) {
474
+ return textResponse(
475
+ `❌ Release command failed:\n\n${error.stderr || error.stdout || error.message}`
476
+ );
477
+ }
478
+
479
+ const branchForPush = workflowState.state.lastPushBranch || (await git.getCurrentBranch()) || "";
480
+ const pushCommand = branchForPush
481
+ ? `git push --follow-tags origin ${utils.shellEscape(branchForPush)}`
482
+ : "git push --follow-tags";
483
+
484
+ try {
485
+ await exec(pushCommand);
486
+ } catch (error) {
487
+ return textResponse(
488
+ `❌ git push (with tags) failed:\n\n${error.stderr || error.stdout || error.message}`
489
+ );
490
+ }
491
+
492
+ workflowState.state.released = true;
493
+ workflowState.state.releaseCommand = finalCommand;
494
+ workflowState.state.releaseNotes = typeof args.notes === "string" ? args.notes : "";
495
+ workflowState.state.currentPhase = "ready_to_complete";
496
+ await workflowState.save();
497
+
498
+ const message = summarizeHistory(workflowState.state.history, args.limit || 10);
499
+ if (!message) {
500
+ return textResponse("📜 No workflow history yet.");
501
+ }
502
+
503
+ return textResponse(message);
504
+ }
505
+
506
+ async function handleCompleteTask(args, workflowState) {
507
+ const entry = {
508
+ taskDescription: workflowState.state.taskDescription,
509
+ taskType: workflowState.state.taskType,
510
+ commitMessage: args.commitMessage,
511
+ };
512
+
513
+ workflowState.addToHistory(entry);
514
+ workflowState.reset();
515
+ await workflowState.save();
516
+
517
+ return textResponse(
518
+ `✅ Task completed!\n\nCommit message: ${args.commitMessage}\nHistory recorded.`
519
+ );
520
+ }
521
+
522
+ async function handleDropTask(args, workflowState) {
523
+ workflowState.addToHistory({
524
+ taskDescription: workflowState.state.taskDescription,
525
+ taskType: workflowState.state.taskType,
526
+ commitMessage: workflowState.state.lastCommitMessage,
527
+ dropped: true,
528
+ dropReason: typeof args.reason === "string" ? args.reason : "",
529
+ });
530
+ workflowState.reset();
531
+ await workflowState.save();
532
+
533
+ return textResponse("⚠️ Task dropped. Workflow state reset.");
534
+ }
535
+
536
+ function handleGetWorkflowStatus(workflowState) {
537
+ const status = workflowState.state;
538
+ const nextStep = status ? status.currentPhase : "idle";
539
+
540
+ return textResponse(
541
+ `📊 Current Workflow Status:
542
+
543
+ Phase: ${status.currentPhase}
544
+ Task: ${status.taskDescription || "(none)"}
545
+ Bug fixed: ${status.bugFixed ? "✅" : "❌"}
546
+ Tests created: ${status.testsCreated ? "✅" : "❌"}
547
+ Tests passed: ${status.testsPassed ? "✅" : "❌"}
548
+ Documentation: ${status.documentationCreated ? "✅" : "❌"}
549
+ Ready check: ${status.readyCheckCompleted ? "✅" : "❌"}
550
+ Commit & push: ${status.commitAndPushCompleted ? "✅" : "❌"}
551
+ Released: ${status.released ? "✅" : "❌"}
552
+
553
+ Next step: ${nextStep}`
554
+ );
555
+ }
556
+
557
+ function handleViewHistory(args, workflowState) {
558
+ const message = summarizeHistory(workflowState.state.history, args.limit || 10);
559
+ if (!message) {
560
+ return textResponse("📜 No workflow history yet.");
561
+ }
562
+
563
+ return textResponse(message);
564
+ }
565
+
566
+ export async function handleToolCall({
567
+ request,
568
+ normalizeRequestArgs,
569
+ workflowState,
570
+ exec,
571
+ git,
572
+ utils,
573
+ }) {
574
+ try {
575
+ const { name } = request.params;
576
+ const { args, error: argumentsError } = normalizeRequestArgs(request.params.arguments);
577
+
578
+ if (argumentsError) {
579
+ return textResponse(argumentsError);
580
+ }
581
+
582
+ switch (name) {
583
+ case "start_task":
584
+ return handleStartTask(args, workflowState);
585
+ case "mark_bug_fixed":
586
+ return handleMarkBugFixed(args, workflowState);
587
+ case "create_tests":
588
+ return handleCreateTests(workflowState);
589
+ case "skip_tests":
590
+ return handleSkipTests(args, workflowState);
591
+ case "run_tests":
592
+ return handleRunTests(args, workflowState);
593
+ case "create_documentation":
594
+ return handleCreateDocumentation(args, workflowState);
595
+ case "check_ready_to_commit":
596
+ return handleReadyCheck(workflowState);
597
+ case "commit_and_push":
598
+ return handleCommitAndPush(args, { workflowState, exec, git, utils });
599
+ case "perform_release":
600
+ return handlePerformRelease(args, { workflowState, exec, git, utils });
601
+ case "complete_task":
602
+ return handleCompleteTask(args, workflowState);
603
+ case "drop_task":
604
+ return handleDropTask(args, workflowState);
605
+ case "get_workflow_status":
606
+ return handleGetWorkflowStatus(workflowState);
607
+ case "view_history":
608
+ return handleViewHistory(args, workflowState);
609
+ case "continue_workflow":
610
+ return handleContinueWorkflow(workflowState);
611
+ default:
612
+ throw new Error(`Unknown tool: ${name}`);
613
+ }
614
+ } catch (error) {
615
+ console.error("Tool handler error:", error);
616
+ return textResponse(
617
+ "⚠️ Internal server error while processing the tool request. Please try again."
618
+ );
619
+ }
620
+ }
package/utils.js ADDED
@@ -0,0 +1,45 @@
1
+ const isWindows = process.platform === "win32";
2
+
3
+ export function shellEscape(value) {
4
+ const stringValue = String(value);
5
+ if (isWindows) {
6
+ // Use PowerShell-friendly single quotes, escape embedded single quotes by doubling
7
+ return `'${stringValue.replace(/'/g, "''")}'`;
8
+ }
9
+
10
+ return `'${stringValue.replace(/'/g, "'\\''")}'`;
11
+ }
12
+
13
+ export function normalizeRequestArgs(rawArgs) {
14
+ if (rawArgs === undefined || rawArgs === null) {
15
+ return { args: {}, error: null };
16
+ }
17
+
18
+ if (typeof rawArgs === "string") {
19
+ try {
20
+ const parsed = JSON.parse(rawArgs);
21
+ if (parsed && typeof parsed === "object") {
22
+ return { args: parsed, error: null };
23
+ }
24
+
25
+ return {
26
+ args: {},
27
+ error: "⚠️ Tool arguments must be a JSON object. Please provide key/value pairs.",
28
+ };
29
+ } catch (parseError) {
30
+ return {
31
+ args: {},
32
+ error: "⚠️ Unable to parse tool arguments. Please provide valid JSON-formatted data.",
33
+ };
34
+ }
35
+ }
36
+
37
+ if (typeof rawArgs === "object") {
38
+ return { args: rawArgs, error: null };
39
+ }
40
+
41
+ return {
42
+ args: {},
43
+ error: "⚠️ Unsupported tool arguments format. Expected a JSON object.",
44
+ };
45
+ }