@programinglive/dev-workflow-mcp-server 1.0.1 โ 1.0.4
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.
- package/CHANGELOG.md +37 -0
- package/README.md +109 -11
- package/index.js +546 -56
- package/package.json +1 -1
- package/tests/workflowState.test.js +42 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.0.4](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.0.3...v1.0.4) (2025-10-20)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### โจ Features
|
|
9
|
+
|
|
10
|
+
* enforce release step after ready check ([0725496](https://github.com/programinglive/dev-workflow-mcp-server/commit/07254967becd515cb2a4261d62af01dfeb842175))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ๐ Bug Fixes
|
|
14
|
+
|
|
15
|
+
* support windows-friendly commit quoting ([47881fd](https://github.com/programinglive/dev-workflow-mcp-server/commit/47881fd0db54da3fabe368b1a135902e47ef873e))
|
|
16
|
+
|
|
17
|
+
### [1.0.3](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.0.2...v1.0.3) (2025-10-20)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### ๐งน Chores
|
|
21
|
+
|
|
22
|
+
* add gitignore ([5f695fb](https://github.com/programinglive/dev-workflow-mcp-server/commit/5f695fbc71515c7f1ffbc44fd74b52314c966ee6))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### ๐ Documentation
|
|
26
|
+
|
|
27
|
+
* clarify cross-platform installation ([52b5ee7](https://github.com/programinglive/dev-workflow-mcp-server/commit/52b5ee7061e8f968ff94e6c226586fc1b7e2a057))
|
|
28
|
+
|
|
29
|
+
### [1.0.2](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.0.1...v1.0.2) (2025-10-19)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### ๐ Bug Fixes
|
|
33
|
+
|
|
34
|
+
* guard run_tests arguments ([2004c59](https://github.com/programinglive/dev-workflow-mcp-server/commit/2004c590fabe359d068122cf240a731382f81ca0))
|
|
35
|
+
* normalize tool call arguments ([44d5a3d](https://github.com/programinglive/dev-workflow-mcp-server/commit/44d5a3d35f191012445fa02cee6254afbc993c98))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
### ๐ Documentation
|
|
39
|
+
|
|
40
|
+
* clarify tool argument requirements ([4528614](https://github.com/programinglive/dev-workflow-mcp-server/commit/45286146661ad850eccb04849c11819e91ddd580))
|
|
41
|
+
|
|
5
42
|
### [1.0.1](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.0.0...v1.0.1) (2025-10-18)
|
|
6
43
|
|
|
7
44
|
|
package/README.md
CHANGED
|
@@ -11,7 +11,8 @@ This MCP server guides you through a disciplined development workflow:
|
|
|
11
11
|
3. **Create Tests** - Always test your changes
|
|
12
12
|
4. **Run Tests** - Tests must pass (GREEN)
|
|
13
13
|
5. **Document** - Update documentation
|
|
14
|
-
6. **Commit & Push** -
|
|
14
|
+
6. **Commit & Push** - Let the server drive the git commands once verification passes
|
|
15
|
+
7. **Release** - Record the release details before closing out the task
|
|
15
16
|
|
|
16
17
|
## ๐ Installation
|
|
17
18
|
|
|
@@ -23,40 +24,115 @@ npm install
|
|
|
23
24
|
|
|
24
25
|
### 2. Configure in Windsurf/Claude Desktop
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
Point your MCP client to the server entry point. Replace `<PROJECT_ROOT>` with the absolute path to this repository on your machine.
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
#### macOS
|
|
30
|
+
|
|
31
|
+
- **Windsurf** (`~/Library/Application Support/Windsurf/config.json`):
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"dev-workflow": {
|
|
37
|
+
"command": "node",
|
|
38
|
+
"args": ["<PROJECT_ROOT>/index.js"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"dev-workflow": {
|
|
50
|
+
"command": "node",
|
|
51
|
+
"args": ["<PROJECT_ROOT>/index.js"]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Windows
|
|
58
|
+
|
|
59
|
+
- **Windsurf** (`%APPDATA%\Windsurf\config.json`):
|
|
29
60
|
|
|
30
61
|
```json
|
|
31
62
|
{
|
|
32
63
|
"mcpServers": {
|
|
33
64
|
"dev-workflow": {
|
|
34
65
|
"command": "node",
|
|
35
|
-
"args": ["
|
|
66
|
+
"args": ["<PROJECT_ROOT>\\index.js"]
|
|
36
67
|
}
|
|
37
68
|
}
|
|
38
69
|
}
|
|
39
70
|
```
|
|
40
71
|
|
|
41
|
-
**
|
|
72
|
+
- **Claude Desktop** (`%APPDATA%\Claude\claude_desktop_config.json`):
|
|
42
73
|
|
|
43
74
|
```json
|
|
44
75
|
{
|
|
45
76
|
"mcpServers": {
|
|
46
77
|
"dev-workflow": {
|
|
47
78
|
"command": "node",
|
|
48
|
-
"args": ["
|
|
79
|
+
"args": ["<PROJECT_ROOT>\\index.js"]
|
|
49
80
|
}
|
|
50
81
|
}
|
|
51
82
|
}
|
|
52
83
|
```
|
|
53
84
|
|
|
85
|
+
#### Linux
|
|
86
|
+
|
|
87
|
+
- **Windsurf** (`~/.config/windsurf/config.json`):
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"dev-workflow": {
|
|
93
|
+
"command": "node",
|
|
94
|
+
"args": ["<PROJECT_ROOT>/index.js"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- **Claude Desktop** (`~/.config/claude/claude_desktop_config.json`):
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"mcpServers": {
|
|
105
|
+
"dev-workflow": {
|
|
106
|
+
"command": "node",
|
|
107
|
+
"args": ["<PROJECT_ROOT>/index.js"]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> **Note:** On Windows paths in JSON require escaped backslashes (e.g., `"C:\\path\\to\\project"`).
|
|
114
|
+
|
|
54
115
|
### 3. Restart Windsurf/Claude Desktop
|
|
55
116
|
|
|
56
117
|
After adding the configuration, restart the application to load the MCP server.
|
|
57
118
|
|
|
58
119
|
## ๐ ๏ธ Available Tools
|
|
59
120
|
|
|
121
|
+
### Tool Argument Requirements
|
|
122
|
+
|
|
123
|
+
All tool invocations validate their `arguments` payload before running:
|
|
124
|
+
- Strings are parsed as JSON and must resolve to an object (key/value pairs).
|
|
125
|
+
- Passing non-object data (numbers, arrays, plain text) triggers a guidance error.
|
|
126
|
+
- Missing or malformed arguments safely default to empty input so the tool can respond with actionable reminders.
|
|
127
|
+
|
|
128
|
+
Example (stringified JSON object):
|
|
129
|
+
```
|
|
130
|
+
{
|
|
131
|
+
"name": "start_task",
|
|
132
|
+
"arguments": "{\"description\":\"Add reporting endpoint\",\"type\":\"feature\"}"
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
60
136
|
### `start_task`
|
|
61
137
|
Start a new coding task. This is your first step - be conscious about what you're coding.
|
|
62
138
|
|
|
@@ -77,6 +153,11 @@ Mark that the bug/feature is fixed. **Reminder: Now you MUST create tests!**
|
|
|
77
153
|
**Parameters:**
|
|
78
154
|
- `summary` (string, required): Brief summary of what was fixed/implemented
|
|
79
155
|
|
|
156
|
+
### `create_tests`
|
|
157
|
+
Confirm that you've created the necessary tests covering your change. Required before recording test results.
|
|
158
|
+
|
|
159
|
+
**Parameters:** _none_
|
|
160
|
+
|
|
80
161
|
### `run_tests`
|
|
81
162
|
Record test results. **NEVER commit if tests fail!** Only proceed if all tests are green.
|
|
82
163
|
|
|
@@ -103,6 +184,20 @@ Mark that documentation has been created/updated. This is required before commit
|
|
|
103
184
|
### `check_ready_to_commit`
|
|
104
185
|
Check if all workflow steps are completed and you're ready to commit & push.
|
|
105
186
|
|
|
187
|
+
### `commit_and_push`
|
|
188
|
+
Automatically run `git add`, `git commit`, and `git push` after the ready check passes.
|
|
189
|
+
|
|
190
|
+
**Parameters:**
|
|
191
|
+
- `commitMessage` (string, required): Conventional commit message to use
|
|
192
|
+
- `branch` (string, optional): Target branch to push (defaults to current branch)
|
|
193
|
+
|
|
194
|
+
### `perform_release`
|
|
195
|
+
Record the release after you've committed and pushed. Required before you can complete the task.
|
|
196
|
+
|
|
197
|
+
**Parameters:**
|
|
198
|
+
- `command` (string, required): Release command that was executed (e.g., `npm run release`)
|
|
199
|
+
- `notes` (string, optional): Additional release notes
|
|
200
|
+
|
|
106
201
|
### `complete_task`
|
|
107
202
|
Mark the task as complete after successful commit & push. Resets workflow for next task.
|
|
108
203
|
|
|
@@ -165,13 +260,16 @@ Here's how you'd use this MCP server in a typical coding session:
|
|
|
165
260
|
```
|
|
166
261
|
|
|
167
262
|
8. **Commit & Push:**
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
263
|
+
```
|
|
264
|
+
"Commit and push: commitMessage='feat: add user profile page with tests and docs'"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
9. **Record release:**
|
|
268
|
+
```
|
|
269
|
+
"Record release: command='npm run release', notes='v1.2.3'"
|
|
172
270
|
```
|
|
173
271
|
|
|
174
|
-
|
|
272
|
+
10. **Complete:**
|
|
175
273
|
```
|
|
176
274
|
"Complete the task with commit message: 'feat: add user profile page'"
|
|
177
275
|
```
|
package/index.js
CHANGED
|
@@ -25,9 +25,17 @@ export class WorkflowState {
|
|
|
25
25
|
currentPhase: "idle",
|
|
26
26
|
taskDescription: "",
|
|
27
27
|
bugFixed: false,
|
|
28
|
+
testsCreated: false,
|
|
28
29
|
testsPassed: false,
|
|
29
30
|
documentationCreated: false,
|
|
30
31
|
readyToCommit: false,
|
|
32
|
+
readyCheckCompleted: false,
|
|
33
|
+
released: false,
|
|
34
|
+
releaseCommand: "",
|
|
35
|
+
releaseNotes: "",
|
|
36
|
+
commitAndPushCompleted: false,
|
|
37
|
+
lastCommitMessage: "",
|
|
38
|
+
lastPushBranch: "",
|
|
31
39
|
history: [],
|
|
32
40
|
};
|
|
33
41
|
}
|
|
@@ -35,7 +43,35 @@ export class WorkflowState {
|
|
|
35
43
|
async load() {
|
|
36
44
|
try {
|
|
37
45
|
const data = await fs.readFile(this.stateFile, "utf-8");
|
|
38
|
-
|
|
46
|
+
const parsed = JSON.parse(data);
|
|
47
|
+
this.state = {
|
|
48
|
+
...this.state,
|
|
49
|
+
...parsed,
|
|
50
|
+
};
|
|
51
|
+
if (typeof this.state.testsCreated !== "boolean") {
|
|
52
|
+
this.state.testsCreated = false;
|
|
53
|
+
}
|
|
54
|
+
if (typeof this.state.readyCheckCompleted !== "boolean") {
|
|
55
|
+
this.state.readyCheckCompleted = false;
|
|
56
|
+
}
|
|
57
|
+
if (typeof this.state.released !== "boolean") {
|
|
58
|
+
this.state.released = false;
|
|
59
|
+
}
|
|
60
|
+
if (typeof this.state.commitAndPushCompleted !== "boolean") {
|
|
61
|
+
this.state.commitAndPushCompleted = false;
|
|
62
|
+
}
|
|
63
|
+
if (typeof this.state.releaseCommand !== "string") {
|
|
64
|
+
this.state.releaseCommand = "";
|
|
65
|
+
}
|
|
66
|
+
if (typeof this.state.releaseNotes !== "string") {
|
|
67
|
+
this.state.releaseNotes = "";
|
|
68
|
+
}
|
|
69
|
+
if (typeof this.state.lastCommitMessage !== "string") {
|
|
70
|
+
this.state.lastCommitMessage = "";
|
|
71
|
+
}
|
|
72
|
+
if (typeof this.state.lastPushBranch !== "string") {
|
|
73
|
+
this.state.lastPushBranch = "";
|
|
74
|
+
}
|
|
39
75
|
} catch (error) {
|
|
40
76
|
// File doesn't exist yet, use default state
|
|
41
77
|
}
|
|
@@ -50,9 +86,17 @@ export class WorkflowState {
|
|
|
50
86
|
currentPhase: "idle",
|
|
51
87
|
taskDescription: "",
|
|
52
88
|
bugFixed: false,
|
|
89
|
+
testsCreated: false,
|
|
53
90
|
testsPassed: false,
|
|
54
91
|
documentationCreated: false,
|
|
55
92
|
readyToCommit: false,
|
|
93
|
+
readyCheckCompleted: false,
|
|
94
|
+
released: false,
|
|
95
|
+
releaseCommand: "",
|
|
96
|
+
releaseNotes: "",
|
|
97
|
+
commitAndPushCompleted: false,
|
|
98
|
+
lastCommitMessage: "",
|
|
99
|
+
lastPushBranch: "",
|
|
56
100
|
history: this.state.history,
|
|
57
101
|
};
|
|
58
102
|
}
|
|
@@ -70,6 +114,54 @@ await workflowState.load();
|
|
|
70
114
|
|
|
71
115
|
const exec = promisify(execCallback);
|
|
72
116
|
|
|
117
|
+
const isWindows = process.platform === "win32";
|
|
118
|
+
|
|
119
|
+
function shellEscape(value) {
|
|
120
|
+
const stringValue = String(value);
|
|
121
|
+
if (isWindows) {
|
|
122
|
+
// Use PowerShell-friendly single quotes, escape embedded single quotes by doubling
|
|
123
|
+
return `'${stringValue.replace(/'/g, "''")}'`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return `'${stringValue.replace(/'/g, "'\\''")}'`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function normalizeRequestArgs(rawArgs) {
|
|
130
|
+
if (rawArgs === undefined || rawArgs === null) {
|
|
131
|
+
return { args: {}, error: null };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (typeof rawArgs === "string") {
|
|
135
|
+
try {
|
|
136
|
+
const parsed = JSON.parse(rawArgs);
|
|
137
|
+
if (parsed && typeof parsed === "object") {
|
|
138
|
+
return { args: parsed, error: null };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
args: {},
|
|
143
|
+
error:
|
|
144
|
+
"โ ๏ธ Tool arguments must be a JSON object. Please provide key/value pairs.",
|
|
145
|
+
};
|
|
146
|
+
} catch (parseError) {
|
|
147
|
+
return {
|
|
148
|
+
args: {},
|
|
149
|
+
error:
|
|
150
|
+
"โ ๏ธ Unable to parse tool arguments. Please provide valid JSON-formatted data.",
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (typeof rawArgs === "object") {
|
|
156
|
+
return { args: rawArgs, error: null };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
args: {},
|
|
161
|
+
error: "โ ๏ธ Unsupported tool arguments format. Expected a JSON object.",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
73
165
|
async function hasStagedChanges() {
|
|
74
166
|
try {
|
|
75
167
|
const { stdout } = await exec("git status --porcelain");
|
|
@@ -85,6 +177,24 @@ async function hasStagedChanges() {
|
|
|
85
177
|
}
|
|
86
178
|
}
|
|
87
179
|
|
|
180
|
+
async function hasWorkingChanges() {
|
|
181
|
+
try {
|
|
182
|
+
const { stdout } = await exec("git status --porcelain");
|
|
183
|
+
return stdout.split("\n").some((line) => line && line.trim().length > 0);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function getCurrentBranch() {
|
|
190
|
+
try {
|
|
191
|
+
const { stdout } = await exec("git rev-parse --abbrev-ref HEAD");
|
|
192
|
+
return stdout.trim();
|
|
193
|
+
} catch (error) {
|
|
194
|
+
return "";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
88
198
|
// Create MCP server
|
|
89
199
|
export const server = new Server(
|
|
90
200
|
{
|
|
@@ -138,6 +248,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
138
248
|
required: ["summary"],
|
|
139
249
|
},
|
|
140
250
|
},
|
|
251
|
+
{
|
|
252
|
+
name: "create_tests",
|
|
253
|
+
description: "Mark that tests covering the change have been created.",
|
|
254
|
+
inputSchema: {
|
|
255
|
+
type: "object",
|
|
256
|
+
properties: {},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
141
259
|
{
|
|
142
260
|
name: "run_tests",
|
|
143
261
|
description:
|
|
@@ -190,6 +308,45 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
190
308
|
properties: {},
|
|
191
309
|
},
|
|
192
310
|
},
|
|
311
|
+
{
|
|
312
|
+
name: "commit_and_push",
|
|
313
|
+
description:
|
|
314
|
+
"Automatically run git add, commit, and push once the ready check passes.",
|
|
315
|
+
inputSchema: {
|
|
316
|
+
type: "object",
|
|
317
|
+
properties: {
|
|
318
|
+
commitMessage: {
|
|
319
|
+
type: "string",
|
|
320
|
+
description: "Commit message to use",
|
|
321
|
+
},
|
|
322
|
+
branch: {
|
|
323
|
+
type: "string",
|
|
324
|
+
description:
|
|
325
|
+
"Optional branch name to push to (defaults to current branch)",
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
required: ["commitMessage"],
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "perform_release",
|
|
333
|
+
description:
|
|
334
|
+
"Record release details after committing and pushing your changes.",
|
|
335
|
+
inputSchema: {
|
|
336
|
+
type: "object",
|
|
337
|
+
properties: {
|
|
338
|
+
command: {
|
|
339
|
+
type: "string",
|
|
340
|
+
description: "Release command that was executed",
|
|
341
|
+
},
|
|
342
|
+
notes: {
|
|
343
|
+
type: "string",
|
|
344
|
+
description: "Optional release notes",
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
required: ["command"],
|
|
348
|
+
},
|
|
349
|
+
},
|
|
193
350
|
{
|
|
194
351
|
name: "complete_task",
|
|
195
352
|
description:
|
|
@@ -232,95 +389,184 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
232
389
|
|
|
233
390
|
// Handle tool calls
|
|
234
391
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
workflowState.state.currentPhase = "coding";
|
|
241
|
-
workflowState.state.taskDescription = args.description;
|
|
242
|
-
workflowState.state.taskType = args.type;
|
|
243
|
-
await workflowState.save();
|
|
392
|
+
try {
|
|
393
|
+
const { name } = request.params;
|
|
394
|
+
const { args, error: argumentsError } = normalizeRequestArgs(
|
|
395
|
+
request.params.arguments
|
|
396
|
+
);
|
|
244
397
|
|
|
398
|
+
if (argumentsError) {
|
|
245
399
|
return {
|
|
246
400
|
content: [
|
|
247
401
|
{
|
|
248
402
|
type: "text",
|
|
249
|
-
text:
|
|
403
|
+
text: argumentsError,
|
|
250
404
|
},
|
|
251
405
|
],
|
|
252
406
|
};
|
|
253
407
|
}
|
|
254
408
|
|
|
255
|
-
|
|
256
|
-
|
|
409
|
+
switch (name) {
|
|
410
|
+
case "start_task": {
|
|
411
|
+
workflowState.reset();
|
|
412
|
+
workflowState.state.currentPhase = "coding";
|
|
413
|
+
workflowState.state.taskDescription = args.description;
|
|
414
|
+
workflowState.state.taskType = args.type;
|
|
415
|
+
workflowState.state.commitAndPushCompleted = false;
|
|
416
|
+
workflowState.state.lastCommitMessage = "";
|
|
417
|
+
workflowState.state.lastPushBranch = "";
|
|
418
|
+
await workflowState.save();
|
|
419
|
+
|
|
257
420
|
return {
|
|
258
421
|
content: [
|
|
259
422
|
{
|
|
260
423
|
type: "text",
|
|
261
|
-
text:
|
|
424
|
+
text: `โ
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. โณ Commit & push, then run 'perform_release'\n8. โณ Complete task\n\nReminder: Focus on writing clean, maintainable code!`,
|
|
262
425
|
},
|
|
263
426
|
],
|
|
264
427
|
};
|
|
265
428
|
}
|
|
266
429
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
430
|
+
case "mark_bug_fixed": {
|
|
431
|
+
if (workflowState.state.currentPhase === "idle") {
|
|
432
|
+
return {
|
|
433
|
+
content: [
|
|
434
|
+
{
|
|
435
|
+
type: "text",
|
|
436
|
+
text: "โ ๏ธ Please start a task first using 'start_task'!",
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
};
|
|
440
|
+
}
|
|
271
441
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
442
|
+
workflowState.state.bugFixed = true;
|
|
443
|
+
workflowState.state.testsCreated = false;
|
|
444
|
+
workflowState.state.currentPhase = "testing";
|
|
445
|
+
workflowState.state.fixSummary = args.summary;
|
|
446
|
+
workflowState.state.readyCheckCompleted = false;
|
|
447
|
+
workflowState.state.released = false;
|
|
448
|
+
workflowState.state.releaseCommand = "";
|
|
449
|
+
workflowState.state.releaseNotes = "";
|
|
450
|
+
workflowState.state.readyToCommit = false;
|
|
451
|
+
workflowState.state.commitAndPushCompleted = false;
|
|
452
|
+
workflowState.state.lastCommitMessage = "";
|
|
453
|
+
workflowState.state.lastPushBranch = "";
|
|
454
|
+
await workflowState.save();
|
|
281
455
|
|
|
282
|
-
case "run_tests": {
|
|
283
|
-
if (!workflowState.state.bugFixed) {
|
|
284
456
|
return {
|
|
285
457
|
content: [
|
|
286
458
|
{
|
|
287
459
|
type: "text",
|
|
288
|
-
text:
|
|
460
|
+
text: `โ
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. โณ Commit & push, then run 'perform_release'\n7. โณ Complete task\n\n๐ซ DO NOT SKIP TESTING!`,
|
|
289
461
|
},
|
|
290
462
|
],
|
|
291
463
|
};
|
|
292
464
|
}
|
|
293
465
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
466
|
+
case "create_tests": {
|
|
467
|
+
if (!workflowState.state.bugFixed) {
|
|
468
|
+
return {
|
|
469
|
+
content: [
|
|
470
|
+
{
|
|
471
|
+
type: "text",
|
|
472
|
+
text: "โ ๏ธ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!",
|
|
473
|
+
},
|
|
474
|
+
],
|
|
475
|
+
};
|
|
476
|
+
}
|
|
297
477
|
|
|
298
|
-
|
|
299
|
-
workflowState.state.
|
|
478
|
+
workflowState.state.testsCreated = true;
|
|
479
|
+
workflowState.state.readyCheckCompleted = false;
|
|
480
|
+
workflowState.state.released = false;
|
|
481
|
+
workflowState.state.commitAndPushCompleted = false;
|
|
482
|
+
workflowState.state.lastCommitMessage = "";
|
|
483
|
+
workflowState.state.lastPushBranch = "";
|
|
300
484
|
await workflowState.save();
|
|
301
485
|
|
|
302
486
|
return {
|
|
303
487
|
content: [
|
|
304
488
|
{
|
|
305
489
|
type: "text",
|
|
306
|
-
text:
|
|
490
|
+
text: "โ
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. โณ Commit & push, then run 'perform_release'\n7. โณ Complete task\n\n๐งช Run your test command and record the results using 'run_tests'.",
|
|
307
491
|
},
|
|
308
492
|
],
|
|
309
493
|
};
|
|
310
494
|
}
|
|
311
495
|
|
|
312
|
-
|
|
313
|
-
|
|
496
|
+
case "run_tests": {
|
|
497
|
+
if (!workflowState.state.bugFixed) {
|
|
498
|
+
return {
|
|
499
|
+
content: [
|
|
500
|
+
{
|
|
501
|
+
type: "text",
|
|
502
|
+
text: "โ ๏ธ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!",
|
|
503
|
+
},
|
|
504
|
+
],
|
|
505
|
+
};
|
|
506
|
+
}
|
|
314
507
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
508
|
+
if (!workflowState.state.testsCreated) {
|
|
509
|
+
return {
|
|
510
|
+
content: [
|
|
511
|
+
{
|
|
512
|
+
type: "text",
|
|
513
|
+
text: "โ ๏ธ Please create tests first using 'create_tests' before recording test results!",
|
|
514
|
+
},
|
|
515
|
+
],
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (
|
|
520
|
+
typeof args.passed !== "boolean" ||
|
|
521
|
+
typeof args.testCommand !== "string" ||
|
|
522
|
+
args.testCommand.trim() === ""
|
|
523
|
+
) {
|
|
524
|
+
return {
|
|
525
|
+
content: [
|
|
526
|
+
{
|
|
527
|
+
type: "text",
|
|
528
|
+
text: "โ ๏ธ Please provide both 'passed' (boolean) and 'testCommand' (non-empty string) when recording test results.",
|
|
529
|
+
},
|
|
530
|
+
],
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
workflowState.state.testsPassed = args.passed;
|
|
535
|
+
workflowState.state.testCommand = args.testCommand;
|
|
536
|
+
workflowState.state.testDetails =
|
|
537
|
+
typeof args.details === "string" ? args.details : "";
|
|
538
|
+
workflowState.state.readyCheckCompleted = false;
|
|
539
|
+
workflowState.state.released = false;
|
|
540
|
+
workflowState.state.commitAndPushCompleted = false;
|
|
541
|
+
workflowState.state.lastCommitMessage = "";
|
|
542
|
+
workflowState.state.lastPushBranch = "";
|
|
543
|
+
|
|
544
|
+
if (!args.passed) {
|
|
545
|
+
workflowState.state.currentPhase = "testing";
|
|
546
|
+
await workflowState.save();
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
content: [
|
|
550
|
+
{
|
|
551
|
+
type: "text",
|
|
552
|
+
text: `โ 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!`,
|
|
553
|
+
},
|
|
554
|
+
],
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
workflowState.state.currentPhase = "documentation";
|
|
559
|
+
await workflowState.save();
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
content: [
|
|
563
|
+
{
|
|
564
|
+
type: "text",
|
|
565
|
+
text: `โ
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. โณ Commit & push, then run 'perform_release'\n7. โณ Complete task\n\nReminder: Document what you did before committing!`,
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
};
|
|
569
|
+
}
|
|
324
570
|
|
|
325
571
|
case "create_documentation": {
|
|
326
572
|
if (!workflowState.state.testsPassed) {
|
|
@@ -339,13 +585,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
339
585
|
workflowState.state.documentationSummary = args.summary;
|
|
340
586
|
workflowState.state.currentPhase = "ready";
|
|
341
587
|
workflowState.state.readyToCommit = true;
|
|
588
|
+
workflowState.state.readyCheckCompleted = false;
|
|
589
|
+
workflowState.state.released = false;
|
|
590
|
+
workflowState.state.commitAndPushCompleted = false;
|
|
591
|
+
workflowState.state.lastCommitMessage = "";
|
|
592
|
+
workflowState.state.lastPushBranch = "";
|
|
342
593
|
await workflowState.save();
|
|
343
594
|
|
|
344
595
|
return {
|
|
345
596
|
content: [
|
|
346
597
|
{
|
|
347
598
|
type: "text",
|
|
348
|
-
text: `โ
Documentation created!\n\nType: ${args.documentationType}\nSummary: ${args.summary}\n\n๐ You're ready to
|
|
599
|
+
text: `โ
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. โณ Commit & push, then run 'perform_release'\n7. โณ Mark as complete with 'complete_task'\n\nRemember: git add . && git commit && git push`,
|
|
349
600
|
},
|
|
350
601
|
],
|
|
351
602
|
};
|
|
@@ -356,6 +607,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
356
607
|
const checks = [
|
|
357
608
|
{ name: "Task started", done: status.currentPhase !== "idle" },
|
|
358
609
|
{ name: "Feature/bug fixed", done: status.bugFixed },
|
|
610
|
+
{ name: "Tests created", done: status.testsCreated },
|
|
359
611
|
{ name: "Tests created and passed", done: status.testsPassed },
|
|
360
612
|
{ name: "Documentation created", done: status.documentationCreated },
|
|
361
613
|
];
|
|
@@ -365,12 +617,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
365
617
|
.map((c) => `${c.done ? "โ
" : "โ"} ${c.name}`)
|
|
366
618
|
.join("\n");
|
|
367
619
|
|
|
620
|
+
workflowState.state.readyCheckCompleted = allDone;
|
|
621
|
+
if (allDone) {
|
|
622
|
+
workflowState.state.currentPhase = workflowState.state.commitAndPushCompleted
|
|
623
|
+
? workflowState.state.released
|
|
624
|
+
? "ready_to_complete"
|
|
625
|
+
: "release"
|
|
626
|
+
: "commit";
|
|
627
|
+
}
|
|
628
|
+
await workflowState.save();
|
|
629
|
+
|
|
368
630
|
if (allDone) {
|
|
369
631
|
return {
|
|
370
632
|
content: [
|
|
371
633
|
{
|
|
372
634
|
type: "text",
|
|
373
|
-
text: `๐ ALL CHECKS PASSED
|
|
635
|
+
text: `๐ ALL CHECKS PASSED!\n\n${checkList}\n\nโ
Next actions:\n1. Run 'commit_and_push' with your commit message\n2. Run 'perform_release' to record the release\n3. Finish with 'complete_task'\n\nTip: Provide the optional 'branch' argument to push to a non-default branch.\n\nTask: ${status.taskDescription}`,
|
|
374
636
|
},
|
|
375
637
|
],
|
|
376
638
|
};
|
|
@@ -386,6 +648,182 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
386
648
|
}
|
|
387
649
|
}
|
|
388
650
|
|
|
651
|
+
case "commit_and_push": {
|
|
652
|
+
if (!workflowState.state.readyToCommit) {
|
|
653
|
+
return {
|
|
654
|
+
content: [
|
|
655
|
+
{
|
|
656
|
+
type: "text",
|
|
657
|
+
text: "โ ๏ธ Not ready yet! Run 'check_ready_to_commit' and complete all steps first.",
|
|
658
|
+
},
|
|
659
|
+
],
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (!workflowState.state.readyCheckCompleted) {
|
|
664
|
+
return {
|
|
665
|
+
content: [
|
|
666
|
+
{
|
|
667
|
+
type: "text",
|
|
668
|
+
text: "โ ๏ธ Please run 'check_ready_to_commit' and resolve any outstanding items before committing!",
|
|
669
|
+
},
|
|
670
|
+
],
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const commitMessage =
|
|
675
|
+
typeof args.commitMessage === "string" ? args.commitMessage.trim() : "";
|
|
676
|
+
|
|
677
|
+
if (!commitMessage) {
|
|
678
|
+
return {
|
|
679
|
+
content: [
|
|
680
|
+
{
|
|
681
|
+
type: "text",
|
|
682
|
+
text: "โ ๏ธ Please provide a non-empty 'commitMessage' when using 'commit_and_push'.",
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (!(await hasWorkingChanges())) {
|
|
689
|
+
return {
|
|
690
|
+
content: [
|
|
691
|
+
{
|
|
692
|
+
type: "text",
|
|
693
|
+
text: "โ ๏ธ No changes detected. Make sure you have modifications to commit before running 'commit_and_push'.",
|
|
694
|
+
},
|
|
695
|
+
],
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
try {
|
|
700
|
+
await exec("git add .");
|
|
701
|
+
} catch (error) {
|
|
702
|
+
return {
|
|
703
|
+
content: [
|
|
704
|
+
{
|
|
705
|
+
type: "text",
|
|
706
|
+
text: `โ Failed to stage files:\n\n${error.stderr || error.stdout || error.message}`,
|
|
707
|
+
},
|
|
708
|
+
],
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
try {
|
|
713
|
+
await exec(`git commit -m ${shellEscape(commitMessage)}`);
|
|
714
|
+
} catch (error) {
|
|
715
|
+
const output = error.stderr || error.stdout || error.message || "Unknown error";
|
|
716
|
+
if (output.includes("nothing to commit")) {
|
|
717
|
+
return {
|
|
718
|
+
content: [
|
|
719
|
+
{
|
|
720
|
+
type: "text",
|
|
721
|
+
text: "โ ๏ธ Nothing to commit. Make additional changes before running 'commit_and_push'.",
|
|
722
|
+
},
|
|
723
|
+
],
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return {
|
|
728
|
+
content: [
|
|
729
|
+
{
|
|
730
|
+
type: "text",
|
|
731
|
+
text: `โ git commit failed:\n\n${output}`,
|
|
732
|
+
},
|
|
733
|
+
],
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const requestedBranch =
|
|
738
|
+
typeof args.branch === "string" ? args.branch.trim() : "";
|
|
739
|
+
const currentBranch = await getCurrentBranch();
|
|
740
|
+
const pushBranch = requestedBranch || currentBranch;
|
|
741
|
+
const pushCommand = pushBranch
|
|
742
|
+
? `git push origin ${shellEscape(pushBranch)}`
|
|
743
|
+
: "git push";
|
|
744
|
+
|
|
745
|
+
try {
|
|
746
|
+
await exec(pushCommand);
|
|
747
|
+
} catch (error) {
|
|
748
|
+
return {
|
|
749
|
+
content: [
|
|
750
|
+
{
|
|
751
|
+
type: "text",
|
|
752
|
+
text: `โ git push failed:\n\n${error.stderr || error.stdout || error.message}`,
|
|
753
|
+
},
|
|
754
|
+
],
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
workflowState.state.commitAndPushCompleted = true;
|
|
759
|
+
workflowState.state.lastCommitMessage = commitMessage;
|
|
760
|
+
workflowState.state.lastPushBranch = pushBranch || currentBranch || "";
|
|
761
|
+
workflowState.state.currentPhase = workflowState.state.released
|
|
762
|
+
? "ready_to_complete"
|
|
763
|
+
: "release";
|
|
764
|
+
await workflowState.save();
|
|
765
|
+
|
|
766
|
+
return {
|
|
767
|
+
content: [
|
|
768
|
+
{
|
|
769
|
+
type: "text",
|
|
770
|
+
text: `โ
Commit & push completed!\n\nCommit message: ${commitMessage}\nPushed to: ${pushBranch || currentBranch || "(default upstream)"}\n\nNext: Run 'perform_release' to record the release.`,
|
|
771
|
+
},
|
|
772
|
+
],
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
case "perform_release": {
|
|
777
|
+
if (workflowState.state.currentPhase === "idle") {
|
|
778
|
+
return {
|
|
779
|
+
content: [
|
|
780
|
+
{
|
|
781
|
+
type: "text",
|
|
782
|
+
text: "โ ๏ธ Please start a task first using 'start_task'!",
|
|
783
|
+
},
|
|
784
|
+
],
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (!workflowState.state.commitAndPushCompleted) {
|
|
789
|
+
return {
|
|
790
|
+
content: [
|
|
791
|
+
{
|
|
792
|
+
type: "text",
|
|
793
|
+
text: "โ ๏ธ Please run 'commit_and_push' after the ready check before recording a release!",
|
|
794
|
+
},
|
|
795
|
+
],
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
if (!workflowState.state.readyCheckCompleted) {
|
|
800
|
+
return {
|
|
801
|
+
content: [
|
|
802
|
+
{
|
|
803
|
+
type: "text",
|
|
804
|
+
text: "โ ๏ธ Please run 'check_ready_to_commit' and ensure all checks pass before releasing!",
|
|
805
|
+
},
|
|
806
|
+
],
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
workflowState.state.released = true;
|
|
811
|
+
workflowState.state.releaseCommand = args.command;
|
|
812
|
+
workflowState.state.releaseNotes =
|
|
813
|
+
typeof args.notes === "string" ? args.notes : "";
|
|
814
|
+
workflowState.state.currentPhase = "ready_to_complete";
|
|
815
|
+
await workflowState.save();
|
|
816
|
+
|
|
817
|
+
return {
|
|
818
|
+
content: [
|
|
819
|
+
{
|
|
820
|
+
type: "text",
|
|
821
|
+
text: `๐ Release recorded!\n\nCommand: ${args.command}\n${workflowState.state.releaseNotes ? `Notes: ${workflowState.state.releaseNotes}\n` : ""}\nโ
Next: Run 'complete_task' to wrap up.`,
|
|
822
|
+
},
|
|
823
|
+
],
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
389
827
|
case "complete_task": {
|
|
390
828
|
if (!workflowState.state.readyToCommit) {
|
|
391
829
|
return {
|
|
@@ -398,6 +836,39 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
398
836
|
};
|
|
399
837
|
}
|
|
400
838
|
|
|
839
|
+
if (!workflowState.state.readyCheckCompleted) {
|
|
840
|
+
return {
|
|
841
|
+
content: [
|
|
842
|
+
{
|
|
843
|
+
type: "text",
|
|
844
|
+
text: "โ ๏ธ Please run 'check_ready_to_commit' and ensure all checks pass before completing the task!",
|
|
845
|
+
},
|
|
846
|
+
],
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (!workflowState.state.commitAndPushCompleted) {
|
|
851
|
+
return {
|
|
852
|
+
content: [
|
|
853
|
+
{
|
|
854
|
+
type: "text",
|
|
855
|
+
text: "โ ๏ธ Commit and push not completed! Run 'commit_and_push' first.",
|
|
856
|
+
},
|
|
857
|
+
],
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
if (!workflowState.state.released) {
|
|
862
|
+
return {
|
|
863
|
+
content: [
|
|
864
|
+
{
|
|
865
|
+
type: "text",
|
|
866
|
+
text: "โ ๏ธ Release not recorded! Run 'perform_release' after committing and pushing.",
|
|
867
|
+
},
|
|
868
|
+
],
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
|
|
401
872
|
if (await hasStagedChanges()) {
|
|
402
873
|
return {
|
|
403
874
|
content: [
|
|
@@ -416,6 +887,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
416
887
|
testCommand: workflowState.state.testCommand,
|
|
417
888
|
documentationType: workflowState.state.documentationType,
|
|
418
889
|
commitMessage: args.commitMessage,
|
|
890
|
+
releaseCommand: workflowState.state.releaseCommand,
|
|
891
|
+
releaseNotes: workflowState.state.releaseNotes,
|
|
419
892
|
});
|
|
420
893
|
|
|
421
894
|
const completedTask = workflowState.state.taskDescription;
|
|
@@ -451,14 +924,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
451
924
|
coding: "๐จ Currently coding",
|
|
452
925
|
testing: "๐งช Ready for testing",
|
|
453
926
|
documentation: "๐ Ready for documentation",
|
|
454
|
-
ready: "โ
Ready
|
|
927
|
+
ready: "โ
Ready for verification",
|
|
928
|
+
commit: "๐ฆ Ready to commit & push",
|
|
929
|
+
release: "๐ Ready to release",
|
|
930
|
+
ready_to_complete: "โ
Release completed",
|
|
455
931
|
};
|
|
456
932
|
|
|
457
933
|
return {
|
|
458
934
|
content: [
|
|
459
935
|
{
|
|
460
936
|
type: "text",
|
|
461
|
-
text: `๐ Current Workflow Status\n\nTask: ${status.taskDescription}\nPhase: ${phaseMessages[status.currentPhase]}\n\nProgress:\n${status.bugFixed ? "โ
" : "โณ"} Feature/bug fixed\n${status.testsPassed ? "โ
" : "โณ"} Tests passed\n${status.documentationCreated ? "โ
" : "โณ"} Documentation created\n${status.
|
|
937
|
+
text: `๐ Current Workflow Status\n\nTask: ${status.taskDescription}\nPhase: ${phaseMessages[status.currentPhase]}\n\nProgress:\n${status.bugFixed ? "โ
" : "โณ"} Feature/bug fixed\n${status.testsCreated ? "โ
" : "โณ"} Tests created\n${status.testsPassed ? "โ
" : "โณ"} Tests passed\n${status.documentationCreated ? "โ
" : "โณ"} Documentation created\n${status.readyCheckCompleted ? "โ
" : "โณ"} Ready check completed\n${status.commitAndPushCompleted ? "โ
" : "โณ"} Commit & push completed\n${status.released ? "โ
" : "โณ"} Release recorded\n\nNext: ${getNextStep(status)}`,
|
|
462
938
|
},
|
|
463
939
|
],
|
|
464
940
|
};
|
|
@@ -496,18 +972,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
496
972
|
};
|
|
497
973
|
}
|
|
498
974
|
|
|
499
|
-
|
|
500
|
-
|
|
975
|
+
default:
|
|
976
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
977
|
+
}
|
|
978
|
+
} catch (error) {
|
|
979
|
+
console.error("Tool handler error:", error);
|
|
980
|
+
return {
|
|
981
|
+
content: [
|
|
982
|
+
{
|
|
983
|
+
type: "text",
|
|
984
|
+
text: "โ ๏ธ Internal server error while processing the tool request. Please try again.",
|
|
985
|
+
},
|
|
986
|
+
],
|
|
987
|
+
};
|
|
501
988
|
}
|
|
502
989
|
});
|
|
503
990
|
|
|
504
991
|
// Helper function
|
|
505
992
|
export function getNextStep(status) {
|
|
506
993
|
if (!status.bugFixed) return "Mark feature/bug as fixed";
|
|
507
|
-
if (!status.
|
|
994
|
+
if (!status.testsCreated) return "Create tests";
|
|
995
|
+
if (!status.testsPassed) return "Run tests";
|
|
508
996
|
if (!status.documentationCreated) return "Create documentation";
|
|
509
|
-
if (!status.
|
|
510
|
-
return "
|
|
997
|
+
if (!status.readyCheckCompleted) return "Run 'check_ready_to_commit'";
|
|
998
|
+
if (!status.commitAndPushCompleted) return "Run 'commit_and_push'";
|
|
999
|
+
if (!status.released) return "Run 'perform_release'";
|
|
1000
|
+
return "Complete the task";
|
|
511
1001
|
}
|
|
512
1002
|
|
|
513
1003
|
// Define prompts
|
package/package.json
CHANGED
|
@@ -23,6 +23,14 @@ test("WorkflowState persists data across save/load", async () => {
|
|
|
23
23
|
state.state.currentPhase = "testing";
|
|
24
24
|
state.state.taskDescription = "Test persistence";
|
|
25
25
|
state.state.bugFixed = true;
|
|
26
|
+
state.state.testsCreated = true;
|
|
27
|
+
state.state.readyCheckCompleted = true;
|
|
28
|
+
state.state.released = true;
|
|
29
|
+
state.state.releaseCommand = "npm run release";
|
|
30
|
+
state.state.releaseNotes = "Automated release";
|
|
31
|
+
state.state.commitAndPushCompleted = true;
|
|
32
|
+
state.state.lastCommitMessage = "test: persistence";
|
|
33
|
+
state.state.lastPushBranch = "main";
|
|
26
34
|
await state.save();
|
|
27
35
|
|
|
28
36
|
const reloaded = new WorkflowState(state.stateFile);
|
|
@@ -31,21 +39,37 @@ test("WorkflowState persists data across save/load", async () => {
|
|
|
31
39
|
assert.equal(reloaded.state.currentPhase, "testing");
|
|
32
40
|
assert.equal(reloaded.state.taskDescription, "Test persistence");
|
|
33
41
|
assert.equal(reloaded.state.bugFixed, true);
|
|
42
|
+
assert.equal(reloaded.state.testsCreated, true);
|
|
43
|
+
assert.equal(reloaded.state.readyCheckCompleted, true);
|
|
44
|
+
assert.equal(reloaded.state.released, true);
|
|
45
|
+
assert.equal(reloaded.state.releaseCommand, "npm run release");
|
|
46
|
+
assert.equal(reloaded.state.releaseNotes, "Automated release");
|
|
47
|
+
assert.equal(reloaded.state.commitAndPushCompleted, true);
|
|
48
|
+
assert.equal(reloaded.state.lastCommitMessage, "test: persistence");
|
|
49
|
+
assert.equal(reloaded.state.lastPushBranch, "main");
|
|
34
50
|
});
|
|
35
51
|
});
|
|
36
52
|
|
|
37
53
|
test("WorkflowState reset keeps history but clears progress", async () => {
|
|
38
54
|
await withWorkflowState(async (state) => {
|
|
39
55
|
state.state.currentPhase = "ready";
|
|
56
|
+
state.state.testsCreated = true;
|
|
40
57
|
state.state.testsPassed = true;
|
|
41
58
|
state.state.documentationCreated = true;
|
|
59
|
+
state.state.readyCheckCompleted = true;
|
|
60
|
+
state.state.released = true;
|
|
61
|
+
state.state.commitAndPushCompleted = true;
|
|
42
62
|
state.addToHistory({ taskDescription: "Sample task" });
|
|
43
63
|
|
|
44
64
|
state.reset();
|
|
45
65
|
|
|
46
66
|
assert.equal(state.state.currentPhase, "idle");
|
|
67
|
+
assert.equal(state.state.testsCreated, false);
|
|
47
68
|
assert.equal(state.state.testsPassed, false);
|
|
48
69
|
assert.equal(state.state.documentationCreated, false);
|
|
70
|
+
assert.equal(state.state.readyCheckCompleted, false);
|
|
71
|
+
assert.equal(state.state.released, false);
|
|
72
|
+
assert.equal(state.state.commitAndPushCompleted, false);
|
|
49
73
|
assert.equal(state.state.history.length, 1);
|
|
50
74
|
assert.equal(state.state.history[0].taskDescription, "Sample task");
|
|
51
75
|
});
|
|
@@ -54,9 +78,12 @@ test("WorkflowState reset keeps history but clears progress", async () => {
|
|
|
54
78
|
test("getNextStep guides workflow progression", () => {
|
|
55
79
|
const baseState = {
|
|
56
80
|
bugFixed: false,
|
|
81
|
+
testsCreated: false,
|
|
57
82
|
testsPassed: false,
|
|
58
83
|
documentationCreated: false,
|
|
59
|
-
|
|
84
|
+
readyCheckCompleted: false,
|
|
85
|
+
commitAndPushCompleted: false,
|
|
86
|
+
released: false,
|
|
60
87
|
};
|
|
61
88
|
|
|
62
89
|
assert.equal(
|
|
@@ -67,21 +94,27 @@ test("getNextStep guides workflow progression", () => {
|
|
|
67
94
|
const afterFix = { ...baseState, bugFixed: true };
|
|
68
95
|
assert.equal(
|
|
69
96
|
getNextStep(afterFix),
|
|
70
|
-
"Create
|
|
97
|
+
"Create tests"
|
|
71
98
|
);
|
|
72
99
|
|
|
73
|
-
const
|
|
100
|
+
const afterTestsCreated = { ...afterFix, testsCreated: true };
|
|
101
|
+
assert.equal(getNextStep(afterTestsCreated), "Run tests");
|
|
102
|
+
|
|
103
|
+
const afterTests = { ...afterTestsCreated, testsPassed: true };
|
|
74
104
|
assert.equal(
|
|
75
105
|
getNextStep(afterTests),
|
|
76
106
|
"Create documentation"
|
|
77
107
|
);
|
|
78
108
|
|
|
79
109
|
const afterDocs = { ...afterTests, documentationCreated: true };
|
|
80
|
-
assert.equal(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
);
|
|
110
|
+
assert.equal(getNextStep(afterDocs), "Run 'check_ready_to_commit'");
|
|
111
|
+
|
|
112
|
+
const afterReadyCheck = { ...afterDocs, readyCheckCompleted: true };
|
|
113
|
+
assert.equal(getNextStep(afterReadyCheck), "Run 'commit_and_push'");
|
|
114
|
+
|
|
115
|
+
const afterCommit = { ...afterReadyCheck, commitAndPushCompleted: true };
|
|
116
|
+
assert.equal(getNextStep(afterCommit), "Run 'perform_release'");
|
|
84
117
|
|
|
85
|
-
const
|
|
86
|
-
assert.equal(getNextStep(
|
|
118
|
+
const afterRelease = { ...afterCommit, released: true };
|
|
119
|
+
assert.equal(getNextStep(afterRelease), "Complete the task");
|
|
87
120
|
});
|