@programinglive/dev-workflow-mcp-server 1.7.17 → 1.8.1

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,193 @@
1
+ import { textResponse, isReleaseSatisfied, resetToCommitIfWorkingChanges, summarizeHistory } from "./shared.js";
2
+
3
+ export async function handleSkipRelease(args, workflowState) {
4
+ if (workflowState.state.currentPhase === "idle") {
5
+ return textResponse("⚠️ Please start a task first using 'start_task' before skipping release.");
6
+ }
7
+
8
+ const reason = typeof args.reason === "string" ? args.reason.trim() : "";
9
+ if (!reason) {
10
+ return textResponse("⚠️ Provide a non-empty 'reason' explaining why the release step is being skipped.");
11
+ }
12
+
13
+ if (workflowState.state.releaseSkipped) {
14
+ workflowState.state.releaseSkippedReason = reason;
15
+ await workflowState.save();
16
+ return textResponse(
17
+ `ℹ️ Release step was already skipped for this task.\nUpdated reason: ${reason}\n\nNext: run 'complete_task' to wrap up.`
18
+ );
19
+ }
20
+
21
+ if (workflowState.state.released) {
22
+ return textResponse("⚠️ A release command has already been recorded. Continue with 'complete_task'.");
23
+ }
24
+
25
+ if (!workflowState.state.readyCheckCompleted) {
26
+ return textResponse("⚠️ Please run 'check_ready_to_commit' and ensure all prerequisites pass before skipping release.");
27
+ }
28
+
29
+ if (!workflowState.state.commitAndPushCompleted) {
30
+ return textResponse("⚠️ Please complete 'commit_and_push' before skipping release.");
31
+ }
32
+
33
+ workflowState.state.releaseSkipped = true;
34
+ workflowState.state.releaseSkippedReason = reason;
35
+ workflowState.state.released = false;
36
+ workflowState.state.releaseCommand = "(skipped)";
37
+ workflowState.state.releaseNotes = "";
38
+ workflowState.state.currentPhase = "ready_to_complete";
39
+ await workflowState.save();
40
+
41
+ return textResponse(
42
+ `⚠️ Release step skipped.\nReason: ${reason}\n\n✅ Next: run 'complete_task' with your final commit message to close out the workflow.`
43
+ );
44
+ }
45
+
46
+ export async function handlePerformRelease(args, context) {
47
+ const { workflowState, exec, git, utils } = context;
48
+
49
+ if (workflowState.state.currentPhase === "idle") {
50
+ return textResponse("⚠️ Please start a task first using 'start_task'!");
51
+ }
52
+
53
+ if (await resetToCommitIfWorkingChanges(workflowState, git)) {
54
+ return textResponse(
55
+ "⚠️ Detected new changes after the last commit. Please run 'commit_and_push' again before recording a release."
56
+ );
57
+ }
58
+
59
+ if (!workflowState.state.readyCheckCompleted) {
60
+ return textResponse(
61
+ "⚠️ Please run 'check_ready_to_commit' and ensure all checks pass before releasing!"
62
+ );
63
+ }
64
+
65
+ if (!workflowState.state.commitAndPushCompleted) {
66
+ const hasWorkingChanges = await git.hasWorkingChanges();
67
+
68
+ if (hasWorkingChanges) {
69
+ return textResponse(
70
+ "⚠️ Please run 'commit_and_push' after the ready check before recording a release!"
71
+ );
72
+ }
73
+
74
+ workflowState.state.currentPhase = "commit";
75
+ await workflowState.save();
76
+
77
+ return textResponse(
78
+ "⚠️ Please run 'commit_and_push' after the ready check before recording a release!"
79
+ );
80
+ }
81
+
82
+ const rawReleaseCommand = typeof args.command === "string" ? args.command.trim() : "";
83
+ const releaseTypeArg =
84
+ typeof args.releaseType === "string" ? args.releaseType.trim().toLowerCase() : "";
85
+ const presetArg = typeof args.preset === "string" ? args.preset.trim().toLowerCase() : "";
86
+ const recognizedTypes = new Set(["major", "minor", "patch"]);
87
+
88
+ const tokens = rawReleaseCommand
89
+ .toLowerCase()
90
+ .split(/\s+/)
91
+ .map((token) => token.trim())
92
+ .filter(Boolean);
93
+
94
+ let releaseType = "";
95
+ if (recognizedTypes.has(releaseTypeArg)) {
96
+ releaseType = releaseTypeArg;
97
+ } else if (recognizedTypes.has(presetArg)) {
98
+ releaseType = presetArg;
99
+ } else if (tokens.length > 0) {
100
+ const possibleType = tokens[tokens.length - 1];
101
+ if (recognizedTypes.has(possibleType)) {
102
+ releaseType = possibleType;
103
+ }
104
+ }
105
+
106
+ let releaseCommand = rawReleaseCommand;
107
+ if (releaseCommand && recognizedTypes.has(releaseCommand.toLowerCase())) {
108
+ releaseType = releaseCommand.toLowerCase();
109
+ releaseCommand = "";
110
+ }
111
+
112
+ const scriptTypeMatch = /release:(major|minor|patch)/i.exec(releaseCommand);
113
+ if (scriptTypeMatch) {
114
+ releaseType = scriptTypeMatch[1].toLowerCase();
115
+ }
116
+
117
+ if (!releaseCommand && releaseType) {
118
+ releaseCommand = `npm run release:${releaseType}`;
119
+ }
120
+
121
+ if (!releaseCommand) {
122
+ return textResponse("⚠️ Please provide a non-empty 'command' when using 'perform_release'.");
123
+ }
124
+
125
+ let statusOutput;
126
+ try {
127
+ const { stdout } = await exec("git status --porcelain");
128
+ statusOutput = stdout;
129
+ } catch (error) {
130
+ const details = error.stderr || error.stdout || error.message || "Unknown error";
131
+ return textResponse(
132
+ `⚠️ Unable to verify Git status. Refusing to run the release command until the repository state can be checked.\n\nDetails:\n${details}`
133
+ );
134
+ }
135
+
136
+ const statusSummary = git.workingTreeSummary(statusOutput);
137
+ if (statusSummary.hasChanges) {
138
+ const changeDetails =
139
+ statusSummary.lines.length > 0
140
+ ? statusSummary.lines.map((line) => `• ${line}`).join("\n")
141
+ : "• (unable to list changes)";
142
+
143
+ return textResponse(
144
+ `⚠️ Working tree not clean! Please commit or stash all changes before running 'perform_release'.\n\nDetected changes:\n${changeDetails}`
145
+ );
146
+ }
147
+
148
+ const lastCommitMessage = await git.getLastCommitMessage();
149
+ if (!releaseType) {
150
+ releaseType = utils.determineReleaseTypeFromCommit(
151
+ lastCommitMessage || workflowState.state.lastCommitMessage
152
+ );
153
+ }
154
+
155
+ const finalCommand =
156
+ releaseCommand.includes("--release-as") || /release:(major|minor|patch)/i.test(releaseCommand)
157
+ ? releaseCommand
158
+ : `${releaseCommand} -- --release-as ${releaseType}`;
159
+
160
+ try {
161
+ await exec(finalCommand);
162
+ } catch (error) {
163
+ return textResponse(
164
+ `❌ Release command failed:\n\n${error.stderr || error.stdout || error.message}`
165
+ );
166
+ }
167
+
168
+ const branchForPush = workflowState.state.lastPushBranch || (await git.getCurrentBranch()) || "";
169
+ const pushCommand = branchForPush
170
+ ? `git push --follow-tags origin ${utils.shellEscape(branchForPush)}`
171
+ : "git push --follow-tags";
172
+
173
+ try {
174
+ await exec(pushCommand);
175
+ } catch (error) {
176
+ return textResponse(
177
+ `❌ git push (with tags) failed:\n\n${error.stderr || error.stdout || error.message}`
178
+ );
179
+ }
180
+
181
+ workflowState.state.released = true;
182
+ workflowState.state.releaseCommand = finalCommand;
183
+ workflowState.state.releaseNotes = typeof args.notes === "string" ? args.notes : "";
184
+ workflowState.state.currentPhase = "ready_to_complete";
185
+ await workflowState.save();
186
+
187
+ const message = summarizeHistory(workflowState.state.history, args.limit || 10);
188
+ if (!message) {
189
+ return textResponse("📜 No workflow history yet.");
190
+ }
191
+
192
+ return textResponse(message);
193
+ }
@@ -0,0 +1,134 @@
1
+ import { textResponse, formatChecklist, isReleaseSatisfied, getContinueGuidance, resetToCommitIfWorkingChanges } from "./shared.js";
2
+ import { handleCommitAndPush } from "./commit-handlers.js";
3
+
4
+ export function handleGetWorkflowStatus(workflowState) {
5
+ const status = workflowState.state;
6
+ const nextStep = status ? status.currentPhase : "idle";
7
+
8
+ return textResponse(
9
+ `📊 Current Workflow Status:
10
+
11
+ Phase: ${status.currentPhase}
12
+ Task: ${status.taskDescription || "(none)"}
13
+ Bug fixed: ${status.bugFixed ? "✅" : "❌"}
14
+ Tests created: ${status.testsCreated ? "✅" : "❌"}
15
+ Tests passed: ${status.testsPassed ? "✅" : "❌"}
16
+ Documentation: ${status.documentationCreated ? "✅" : "❌"}
17
+ Ready check: ${status.readyCheckCompleted ? "✅" : "❌"}
18
+ Commit & push: ${status.commitAndPushCompleted ? "✅" : "❌"}
19
+ Released: ${status.released ? "✅" : "❌"}
20
+
21
+ Next step: ${nextStep}`
22
+ );
23
+ }
24
+
25
+ export async function handleReadyCheck(workflowState) {
26
+ const status = workflowState.state;
27
+ const testsCreatedDone = status.testsSkipped || status.testsCreated;
28
+ const testsPassedDone = status.testsSkipped || status.testsPassed;
29
+ const checks = [
30
+ { name: "Task started", done: status.currentPhase !== "idle" },
31
+ { name: "Feature/bug fixed", done: status.bugFixed },
32
+ {
33
+ name: status.testsSkipped ? "Tests skipped (manual QA documented)" : "Tests created",
34
+ done: testsCreatedDone,
35
+ },
36
+ {
37
+ name: status.testsSkipped ? "Tests skipped acknowledged" : "Tests passed",
38
+ done: testsPassedDone,
39
+ },
40
+ { name: "Documentation created", done: status.documentationCreated },
41
+ ];
42
+
43
+ const allDone = checks.every((c) => c.done);
44
+ const checkList = formatChecklist(checks);
45
+
46
+ workflowState.state.readyCheckCompleted = allDone;
47
+ if (allDone) {
48
+ if (workflowState.state.commitAndPushCompleted) {
49
+ workflowState.state.currentPhase = isReleaseSatisfied(workflowState.state) ? "ready_to_complete" : "release";
50
+ } else {
51
+ workflowState.state.currentPhase = "commit";
52
+ }
53
+ }
54
+ await workflowState.save();
55
+
56
+ if (allDone) {
57
+ return textResponse(
58
+ `🎉 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}`
59
+ );
60
+ }
61
+
62
+ return textResponse(`⚠️ NOT READY TO COMMIT!\n\n${checkList}\n\nPlease complete all steps before committing.`);
63
+ }
64
+
65
+ export async function handleContinueWorkflow(workflowState, context) {
66
+ const status = workflowState.state;
67
+
68
+ if (!status || status.currentPhase === "idle") {
69
+ return textResponse("⚠️ No active workflow. Use 'start_task' to kick off a new task before continuing.");
70
+ }
71
+
72
+ if (await resetToCommitIfWorkingChanges(workflowState, context.git)) {
73
+ return textResponse(
74
+ "⚠️ Detected new changes after the last commit. Workflow moved back to the commit phase. Run 'commit_and_push' to capture the latest updates before continuing."
75
+ );
76
+ }
77
+
78
+ if (status.currentPhase === "ready") {
79
+ return handleReadyCheck(workflowState);
80
+ }
81
+
82
+ if (status.currentPhase === "commit") {
83
+ return handleCommitAndPush({}, context);
84
+ }
85
+
86
+ if (status.currentPhase === "release") {
87
+ return textResponse(
88
+ "⚠️ Release phase requires explicit command. Run 'perform_release' with your release command (e.g., 'npm run release')."
89
+ );
90
+ }
91
+
92
+ const sections = [
93
+ `📊 Current Phase: ${status.currentPhase}`,
94
+ `📝 Task: ${status.taskDescription || "(none)"}`,
95
+ ];
96
+
97
+ if (status.testsSkipped) {
98
+ sections.push(`⚠️ Tests skipped: ${status.testsSkippedReason || "(no reason provided)"}`);
99
+ }
100
+
101
+ if (
102
+ status.currentPhase === "testing" &&
103
+ status.testsCreated &&
104
+ !status.testsPassed &&
105
+ typeof status.testCommand === "string" &&
106
+ status.testCommand.trim() !== ""
107
+ ) {
108
+ sections.push(`🧪 Last test command: ${status.testCommand}`);
109
+ }
110
+
111
+ sections.push(`➡️ ${getContinueGuidance(status)}`);
112
+
113
+ return textResponse(sections.join("\n\n"));
114
+ }
115
+
116
+ export async function handleRerunWorkflow(workflowState) {
117
+ const currentDescription = workflowState.state.taskDescription;
118
+ const currentType = workflowState.state.taskType;
119
+
120
+ if (!currentDescription) {
121
+ return textResponse("⚠️ No active task to rerun. Use 'start_task' to begin a new workflow.");
122
+ }
123
+
124
+ workflowState.reset();
125
+
126
+ workflowState.state.currentPhase = "coding";
127
+ workflowState.state.taskDescription = currentDescription;
128
+ workflowState.state.taskType = currentType;
129
+ await workflowState.save();
130
+
131
+ return textResponse(
132
+ `🔄 Rerunning workflow from the start!\n\nTask: ${currentDescription}\nType: ${currentType}\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\n🎯 Be conscious about what you're coding!`
133
+ );
134
+ }
@@ -0,0 +1,46 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ import { textResponse, generateProjectSummary, summarizeHistory, PROJECT_SUMMARY_FILENAME } from "./shared.js";
4
+
5
+ export function handleProjectSummary(workflowState) {
6
+ const summary = generateProjectSummary(workflowState.state.history);
7
+ return textResponse(summary);
8
+ }
9
+
10
+ export async function handleProjectSummaryData(workflowState) {
11
+ const summaryPath = path.join(path.dirname(workflowState.stateFile), PROJECT_SUMMARY_FILENAME);
12
+ try {
13
+ const data = await fs.readFile(summaryPath, "utf-8");
14
+ const parsed = JSON.parse(data);
15
+ const formatted = `🧠 Project Knowledge Summary (from persisted data)\n\n📊 Stats\n- Total tasks completed: ${parsed.totalTasks}\n- Recent task mix: ${Object.entries(parsed.taskTypes).map(([t, c]) => `${t}: ${c}`).join(", ")}\n- Last active: ${parsed.lastActive ? new Date(parsed.lastActive).toLocaleDateString() : "Never"}\n- Updated: ${new Date(parsed.updatedAt).toLocaleString()}\n\n📝 Recent tasks (last 5)\n${parsed.recentTasks.map((e, i) => `${i + 1}. ${e.description} (${e.type})`).join("\n")}`;
16
+ return textResponse(formatted);
17
+ } catch (error) {
18
+ return handleProjectSummary(workflowState);
19
+ }
20
+ }
21
+
22
+ export async function handleProjectSummaryDb(workflowState) {
23
+ try {
24
+ const { getSummaryForUser } = await import("../../db/index.js");
25
+ const userId = process.env.DEV_WORKFLOW_USER_ID || "default";
26
+ const projectPath = path.dirname(path.dirname(path.dirname(path.dirname(workflowState.stateFile))));
27
+
28
+ const summary = await getSummaryForUser(userId, projectPath);
29
+ if (!summary) {
30
+ return textResponse("📜 No project summary in database yet.");
31
+ }
32
+ const formatted = `🧠 Project Knowledge Summary (from SQLite)\n\n📊 Stats\n- Total tasks completed: ${summary.totalTasks}\n- Recent task mix: ${Object.entries(summary.taskTypes).map(([t, c]) => `${t}: ${c}`).join(", ")}\n- Last active: ${summary.lastActive ? new Date(summary.lastActive).toLocaleDateString() : "Never"}\n- Updated: ${new Date(summary.updatedAt).toLocaleString()}\n\n📝 Recent tasks (last 5)\n${summary.recentTasks.map((e, i) => `${i + 1}. ${e.description} (${e.type})`).join("\n")}`;
33
+ return textResponse(formatted);
34
+ } catch (error) {
35
+ return textResponse("📜 Database module not available. Use 'project_summary' or 'project_summary_data' instead.");
36
+ }
37
+ }
38
+
39
+ export function handleViewHistory(args, workflowState) {
40
+ const message = summarizeHistory(workflowState.state.history, args.limit || 10);
41
+ if (!message) {
42
+ return textResponse("📜 No workflow history yet.");
43
+ }
44
+
45
+ return textResponse(message);
46
+ }
@@ -0,0 +1,219 @@
1
+ import path from "path";
2
+ import { existsSync } from "fs";
3
+ import { textResponse, isReleaseSatisfied, resetToCommitIfWorkingChanges } from "./shared.js";
4
+
5
+ export async function handleStartTask(args, workflowState) {
6
+ workflowState.reset();
7
+ workflowState.state.currentPhase = "coding";
8
+ workflowState.state.taskDescription = args.description;
9
+ workflowState.state.taskType = args.type;
10
+ workflowState.state.testsSkipped = false;
11
+ workflowState.state.testsSkippedReason = "";
12
+ workflowState.state.commitAndPushCompleted = false;
13
+ workflowState.state.lastCommitMessage = "";
14
+ workflowState.state.lastPushBranch = "";
15
+ await workflowState.save();
16
+
17
+ return textResponse(
18
+ `✅ 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) or 'skip_release' (when no release is needed)\n9. ⏳ Complete task\n\nReminder: Focus on writing clean, maintainable code!`
19
+ );
20
+ }
21
+
22
+ export async function handleMarkBugFixed(args, workflowState) {
23
+ if (workflowState.state.currentPhase === "idle") {
24
+ return textResponse("⚠️ Please start a task first using 'start_task'!");
25
+ }
26
+
27
+ if (workflowState.state.bugFixed) {
28
+ const summary = typeof args.summary === "string" ? args.summary : "";
29
+ if (summary && summary.trim() !== "") {
30
+ workflowState.state.fixSummary = summary;
31
+ }
32
+
33
+ if (workflowState.state.testsPassed && workflowState.state.documentationCreated) {
34
+ workflowState.state.currentPhase = "ready";
35
+ workflowState.state.readyToCommit = true;
36
+ } else if (workflowState.state.testsCreated) {
37
+ workflowState.state.currentPhase = "testing";
38
+ }
39
+ workflowState.state.readyCheckCompleted = false;
40
+ workflowState.state.commitAndPushCompleted = false;
41
+ await workflowState.save();
42
+
43
+ return textResponse(
44
+ "ℹ️ Already marked as fixed; kept existing tests/documentation. Continue with the next step."
45
+ );
46
+ }
47
+
48
+ workflowState.state.bugFixed = true;
49
+ workflowState.state.testsCreated = false;
50
+ workflowState.state.currentPhase = "testing";
51
+ workflowState.state.testsSkipped = false;
52
+ workflowState.state.testsSkippedReason = "";
53
+ workflowState.state.fixSummary = args.summary;
54
+ workflowState.state.readyCheckCompleted = false;
55
+ workflowState.state.released = false;
56
+ workflowState.state.releaseSkipped = false;
57
+ workflowState.state.releaseSkippedReason = "";
58
+ workflowState.state.releaseCommand = "";
59
+ workflowState.state.releaseNotes = "";
60
+ workflowState.state.commitAndPushCompleted = false;
61
+ workflowState.state.lastCommitMessage = "";
62
+ workflowState.state.lastPushBranch = "";
63
+ await workflowState.save();
64
+
65
+ return textResponse(
66
+ `✅ 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' (or 'skip_release' if no release applies)\n8. ⏳ Complete task\n\n🚫 DO NOT SKIP TESTING!`
67
+ );
68
+ }
69
+
70
+ export async function handleCreateTests(workflowState) {
71
+ if (!workflowState.state.bugFixed) {
72
+ return textResponse("⚠️ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!");
73
+ }
74
+
75
+ workflowState.state.testsCreated = true;
76
+ workflowState.state.testsSkipped = false;
77
+ workflowState.state.testsSkippedReason = "";
78
+ workflowState.state.readyCheckCompleted = false;
79
+ workflowState.state.released = false;
80
+ workflowState.state.releaseSkipped = false;
81
+ workflowState.state.releaseSkippedReason = "";
82
+ workflowState.state.commitAndPushCompleted = false;
83
+ workflowState.state.lastCommitMessage = "";
84
+ workflowState.state.lastPushBranch = "";
85
+ await workflowState.save();
86
+
87
+ return textResponse(
88
+ "✅ 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' (or 'skip_release' if appropriate)\n8. ⏳ Complete task\n\n🧪 Run your test command and record the results using 'run_tests'."
89
+ );
90
+ }
91
+
92
+ export async function handleSkipTests(args, workflowState) {
93
+ if (workflowState.state.currentPhase === "idle") {
94
+ return textResponse("⚠️ No active task. Use 'start_task' before skipping tests.");
95
+ }
96
+
97
+ if (!workflowState.state.bugFixed) {
98
+ return textResponse("⚠️ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!");
99
+ }
100
+
101
+ const reason = typeof args.reason === "string" ? args.reason.trim() : "";
102
+
103
+ if (!reason) {
104
+ return textResponse("⚠️ Provide a non-empty justification when skipping tests.");
105
+ }
106
+
107
+ workflowState.state.testsCreated = true;
108
+ workflowState.state.testsPassed = true;
109
+ workflowState.state.testsSkipped = true;
110
+ workflowState.state.testsSkippedReason = reason;
111
+ workflowState.state.currentPhase = "documentation";
112
+ workflowState.state.readyToCommit = false;
113
+ workflowState.state.readyCheckCompleted = false;
114
+ workflowState.state.released = false;
115
+ workflowState.state.releaseSkipped = false;
116
+ workflowState.state.releaseSkippedReason = "";
117
+ workflowState.state.commitAndPushCompleted = false;
118
+ workflowState.state.lastCommitMessage = "";
119
+ workflowState.state.lastPushBranch = "";
120
+ await workflowState.save();
121
+
122
+ return textResponse(
123
+ `⚠️ Tests skipped for this task.
124
+ Reason: ${reason}
125
+
126
+ Manual verification is now required.
127
+
128
+ Next Steps:
129
+ 1. ✓ Fix/implement feature
130
+ 2. ⚠️ Tests skipped (ensure manual QA)
131
+ 3. ⏳ Create/update documentation
132
+ 4. ⏳ Run 'check_ready_to_commit'
133
+ 5. ⏳ Run 'commit_and_push'
134
+ 6. ⏳ Run 'perform_release' or 'skip_release'
135
+ 7. ⏳ Complete task`
136
+ );
137
+ }
138
+
139
+ export async function handleRunTests(args, workflowState) {
140
+ if (!workflowState.state.bugFixed) {
141
+ return textResponse("⚠️ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!");
142
+ }
143
+
144
+ if (!workflowState.state.testsCreated) {
145
+ return textResponse("⚠️ Please create tests first using 'create_tests' before recording test results!");
146
+ }
147
+
148
+ if (
149
+ typeof args.passed !== "boolean" ||
150
+ typeof args.testCommand !== "string" ||
151
+ args.testCommand.trim() === ""
152
+ ) {
153
+ return textResponse(
154
+ "⚠️ Please provide both 'passed' (boolean) and 'testCommand' (non-empty string) when recording test results."
155
+ );
156
+ }
157
+
158
+ workflowState.state.testsPassed = args.passed;
159
+ workflowState.state.testCommand = args.testCommand;
160
+ workflowState.state.testDetails = typeof args.details === "string" ? args.details : "";
161
+ workflowState.state.testsSkipped = false;
162
+ workflowState.state.testsSkippedReason = "";
163
+ workflowState.state.readyCheckCompleted = false;
164
+ workflowState.state.released = false;
165
+ workflowState.state.releaseSkipped = false;
166
+ workflowState.state.releaseSkippedReason = "";
167
+ workflowState.state.commitAndPushCompleted = false;
168
+ workflowState.state.lastCommitMessage = "";
169
+ workflowState.state.lastPushBranch = "";
170
+
171
+ if (!args.passed) {
172
+ workflowState.state.currentPhase = "testing";
173
+ await workflowState.save();
174
+
175
+ return textResponse(
176
+ `❌ 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!`
177
+ );
178
+ }
179
+
180
+ workflowState.state.currentPhase = "documentation";
181
+ await workflowState.save();
182
+
183
+ return textResponse(
184
+ `✅ All tests passed! 🎉\n\nTest command: ${args.testCommand}\n\n📝 Now create or update documentation using 'create_documentation' with:\n- documentationType: "PRD", "README", "RELEASE_NOTES", "inline-comments", "API-docs", "changelog", or "other"\n- summary: Brief description of what was documented\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' (or use 'skip_release' if no release is needed)\n8. ⏳ Complete task`
185
+ );
186
+ }
187
+
188
+ export async function handleCreateDocumentation(args, workflowState) {
189
+ if (!workflowState.state.testsPassed) {
190
+ return textResponse("⚠️ Please ensure tests are passing first! Run 'run_tests' with passed=true.");
191
+ }
192
+
193
+ const prdPath = path.join(process.cwd(), "docs", "PRD.md");
194
+ const prdExists = existsSync(prdPath);
195
+
196
+ if (!prdExists) {
197
+ return textResponse(
198
+ `⚠️ PRD file not found!\n\nExpected location: docs/PRD.md\n\nPlease create or update the PRD before marking documentation as complete.`
199
+ );
200
+ }
201
+
202
+ workflowState.state.documentationCreated = true;
203
+ workflowState.state.documentationType = args.documentationType;
204
+ workflowState.state.documentationSummary = args.summary;
205
+ workflowState.state.currentPhase = "ready";
206
+ workflowState.state.readyToCommit = true;
207
+ workflowState.state.readyCheckCompleted = false;
208
+ workflowState.state.released = false;
209
+ workflowState.state.releaseSkipped = false;
210
+ workflowState.state.releaseSkippedReason = "";
211
+ workflowState.state.commitAndPushCompleted = false;
212
+ workflowState.state.lastCommitMessage = "";
213
+ workflowState.state.lastPushBranch = "";
214
+ await workflowState.save();
215
+
216
+ return textResponse(
217
+ `✅ Documentation created/updated!\n\nType: ${args.documentationType}\nSummary: ${args.summary}\n✅ PRD verified: docs/product/PRD.md exists\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/update documentation\n5. ⏳ Run 'check_ready_to_commit' to verify\n6. ⏳ Run 'commit_and_push'\n7. ⏳ Run 'perform_release' (or 'skip_release' when applicable)\n8. ⏳ Mark as complete with 'complete_task'`
218
+ );
219
+ }
@@ -0,0 +1 @@
1
+ export { handleToolCall } from "./handler-modules/index.js";