@programinglive/dev-workflow-mcp-server 1.0.2 โ 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 +24 -0
- package/README.md +94 -11
- package/index.js +423 -11
- package/package.json +1 -1
- package/tests/workflowState.test.js +42 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
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
|
+
|
|
5
29
|
### [1.0.2](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.0.1...v1.0.2) (2025-10-19)
|
|
6
30
|
|
|
7
31
|
|
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,34 +24,94 @@ 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`):
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"dev-workflow": {
|
|
65
|
+
"command": "node",
|
|
66
|
+
"args": ["<PROJECT_ROOT>\\index.js"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **Claude Desktop** (`%APPDATA%\Claude\claude_desktop_config.json`):
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"mcpServers": {
|
|
77
|
+
"dev-workflow": {
|
|
78
|
+
"command": "node",
|
|
79
|
+
"args": ["<PROJECT_ROOT>\\index.js"]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Linux
|
|
86
|
+
|
|
87
|
+
- **Windsurf** (`~/.config/windsurf/config.json`):
|
|
29
88
|
|
|
30
89
|
```json
|
|
31
90
|
{
|
|
32
91
|
"mcpServers": {
|
|
33
92
|
"dev-workflow": {
|
|
34
93
|
"command": "node",
|
|
35
|
-
"args": ["
|
|
94
|
+
"args": ["<PROJECT_ROOT>/index.js"]
|
|
36
95
|
}
|
|
37
96
|
}
|
|
38
97
|
}
|
|
39
98
|
```
|
|
40
99
|
|
|
41
|
-
**
|
|
100
|
+
- **Claude Desktop** (`~/.config/claude/claude_desktop_config.json`):
|
|
42
101
|
|
|
43
102
|
```json
|
|
44
103
|
{
|
|
45
104
|
"mcpServers": {
|
|
46
105
|
"dev-workflow": {
|
|
47
106
|
"command": "node",
|
|
48
|
-
"args": ["
|
|
107
|
+
"args": ["<PROJECT_ROOT>/index.js"]
|
|
49
108
|
}
|
|
50
109
|
}
|
|
51
110
|
}
|
|
52
111
|
```
|
|
53
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.
|
|
@@ -92,6 +153,11 @@ Mark that the bug/feature is fixed. **Reminder: Now you MUST create tests!**
|
|
|
92
153
|
**Parameters:**
|
|
93
154
|
- `summary` (string, required): Brief summary of what was fixed/implemented
|
|
94
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
|
+
|
|
95
161
|
### `run_tests`
|
|
96
162
|
Record test results. **NEVER commit if tests fail!** Only proceed if all tests are green.
|
|
97
163
|
|
|
@@ -118,6 +184,20 @@ Mark that documentation has been created/updated. This is required before commit
|
|
|
118
184
|
### `check_ready_to_commit`
|
|
119
185
|
Check if all workflow steps are completed and you're ready to commit & push.
|
|
120
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
|
+
|
|
121
201
|
### `complete_task`
|
|
122
202
|
Mark the task as complete after successful commit & push. Resets workflow for next task.
|
|
123
203
|
|
|
@@ -180,13 +260,16 @@ Here's how you'd use this MCP server in a typical coding session:
|
|
|
180
260
|
```
|
|
181
261
|
|
|
182
262
|
8. **Commit & Push:**
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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'"
|
|
187
270
|
```
|
|
188
271
|
|
|
189
|
-
|
|
272
|
+
10. **Complete:**
|
|
190
273
|
```
|
|
191
274
|
"Complete the task with commit message: 'feat: add user profile page'"
|
|
192
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,18 @@ 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
|
+
|
|
73
129
|
function normalizeRequestArgs(rawArgs) {
|
|
74
130
|
if (rawArgs === undefined || rawArgs === null) {
|
|
75
131
|
return { args: {}, error: null };
|
|
@@ -121,6 +177,24 @@ async function hasStagedChanges() {
|
|
|
121
177
|
}
|
|
122
178
|
}
|
|
123
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
|
+
|
|
124
198
|
// Create MCP server
|
|
125
199
|
export const server = new Server(
|
|
126
200
|
{
|
|
@@ -174,6 +248,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
174
248
|
required: ["summary"],
|
|
175
249
|
},
|
|
176
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
|
+
},
|
|
177
259
|
{
|
|
178
260
|
name: "run_tests",
|
|
179
261
|
description:
|
|
@@ -226,6 +308,45 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
226
308
|
properties: {},
|
|
227
309
|
},
|
|
228
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
|
+
},
|
|
229
350
|
{
|
|
230
351
|
name: "complete_task",
|
|
231
352
|
description:
|
|
@@ -291,13 +412,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
291
412
|
workflowState.state.currentPhase = "coding";
|
|
292
413
|
workflowState.state.taskDescription = args.description;
|
|
293
414
|
workflowState.state.taskType = args.type;
|
|
415
|
+
workflowState.state.commitAndPushCompleted = false;
|
|
416
|
+
workflowState.state.lastCommitMessage = "";
|
|
417
|
+
workflowState.state.lastPushBranch = "";
|
|
294
418
|
await workflowState.save();
|
|
295
419
|
|
|
296
420
|
return {
|
|
297
421
|
content: [
|
|
298
422
|
{
|
|
299
423
|
type: "text",
|
|
300
|
-
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. โณ Commit & push\n\nReminder: Focus on writing clean, maintainable code!`,
|
|
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!`,
|
|
301
425
|
},
|
|
302
426
|
],
|
|
303
427
|
};
|
|
@@ -316,15 +440,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
316
440
|
}
|
|
317
441
|
|
|
318
442
|
workflowState.state.bugFixed = true;
|
|
443
|
+
workflowState.state.testsCreated = false;
|
|
319
444
|
workflowState.state.currentPhase = "testing";
|
|
320
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();
|
|
455
|
+
|
|
456
|
+
return {
|
|
457
|
+
content: [
|
|
458
|
+
{
|
|
459
|
+
type: "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!`,
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
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
|
+
}
|
|
477
|
+
|
|
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 = "";
|
|
321
484
|
await workflowState.save();
|
|
322
485
|
|
|
323
486
|
return {
|
|
324
487
|
content: [
|
|
325
488
|
{
|
|
326
489
|
type: "text",
|
|
327
|
-
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'.",
|
|
328
491
|
},
|
|
329
492
|
],
|
|
330
493
|
};
|
|
@@ -342,6 +505,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
342
505
|
};
|
|
343
506
|
}
|
|
344
507
|
|
|
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
|
+
|
|
345
519
|
if (
|
|
346
520
|
typeof args.passed !== "boolean" ||
|
|
347
521
|
typeof args.testCommand !== "string" ||
|
|
@@ -361,6 +535,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
361
535
|
workflowState.state.testCommand = args.testCommand;
|
|
362
536
|
workflowState.state.testDetails =
|
|
363
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 = "";
|
|
364
543
|
|
|
365
544
|
if (!args.passed) {
|
|
366
545
|
workflowState.state.currentPhase = "testing";
|
|
@@ -383,7 +562,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
383
562
|
content: [
|
|
384
563
|
{
|
|
385
564
|
type: "text",
|
|
386
|
-
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. โณ Commit & push\n\nReminder: Document what you did before committing!`,
|
|
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!`,
|
|
387
566
|
},
|
|
388
567
|
],
|
|
389
568
|
};
|
|
@@ -406,13 +585,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
406
585
|
workflowState.state.documentationSummary = args.summary;
|
|
407
586
|
workflowState.state.currentPhase = "ready";
|
|
408
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 = "";
|
|
409
593
|
await workflowState.save();
|
|
410
594
|
|
|
411
595
|
return {
|
|
412
596
|
content: [
|
|
413
597
|
{
|
|
414
598
|
type: "text",
|
|
415
|
-
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`,
|
|
416
600
|
},
|
|
417
601
|
],
|
|
418
602
|
};
|
|
@@ -423,6 +607,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
423
607
|
const checks = [
|
|
424
608
|
{ name: "Task started", done: status.currentPhase !== "idle" },
|
|
425
609
|
{ name: "Feature/bug fixed", done: status.bugFixed },
|
|
610
|
+
{ name: "Tests created", done: status.testsCreated },
|
|
426
611
|
{ name: "Tests created and passed", done: status.testsPassed },
|
|
427
612
|
{ name: "Documentation created", done: status.documentationCreated },
|
|
428
613
|
];
|
|
@@ -432,12 +617,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
432
617
|
.map((c) => `${c.done ? "โ
" : "โ"} ${c.name}`)
|
|
433
618
|
.join("\n");
|
|
434
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
|
+
|
|
435
630
|
if (allDone) {
|
|
436
631
|
return {
|
|
437
632
|
content: [
|
|
438
633
|
{
|
|
439
634
|
type: "text",
|
|
440
|
-
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}`,
|
|
441
636
|
},
|
|
442
637
|
],
|
|
443
638
|
};
|
|
@@ -453,6 +648,182 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
453
648
|
}
|
|
454
649
|
}
|
|
455
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
|
+
|
|
456
827
|
case "complete_task": {
|
|
457
828
|
if (!workflowState.state.readyToCommit) {
|
|
458
829
|
return {
|
|
@@ -465,6 +836,39 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
465
836
|
};
|
|
466
837
|
}
|
|
467
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
|
+
|
|
468
872
|
if (await hasStagedChanges()) {
|
|
469
873
|
return {
|
|
470
874
|
content: [
|
|
@@ -483,6 +887,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
483
887
|
testCommand: workflowState.state.testCommand,
|
|
484
888
|
documentationType: workflowState.state.documentationType,
|
|
485
889
|
commitMessage: args.commitMessage,
|
|
890
|
+
releaseCommand: workflowState.state.releaseCommand,
|
|
891
|
+
releaseNotes: workflowState.state.releaseNotes,
|
|
486
892
|
});
|
|
487
893
|
|
|
488
894
|
const completedTask = workflowState.state.taskDescription;
|
|
@@ -518,14 +924,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
518
924
|
coding: "๐จ Currently coding",
|
|
519
925
|
testing: "๐งช Ready for testing",
|
|
520
926
|
documentation: "๐ Ready for documentation",
|
|
521
|
-
ready: "โ
Ready
|
|
927
|
+
ready: "โ
Ready for verification",
|
|
928
|
+
commit: "๐ฆ Ready to commit & push",
|
|
929
|
+
release: "๐ Ready to release",
|
|
930
|
+
ready_to_complete: "โ
Release completed",
|
|
522
931
|
};
|
|
523
932
|
|
|
524
933
|
return {
|
|
525
934
|
content: [
|
|
526
935
|
{
|
|
527
936
|
type: "text",
|
|
528
|
-
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)}`,
|
|
529
938
|
},
|
|
530
939
|
],
|
|
531
940
|
};
|
|
@@ -582,10 +991,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
582
991
|
// Helper function
|
|
583
992
|
export function getNextStep(status) {
|
|
584
993
|
if (!status.bugFixed) return "Mark feature/bug as fixed";
|
|
585
|
-
if (!status.
|
|
994
|
+
if (!status.testsCreated) return "Create tests";
|
|
995
|
+
if (!status.testsPassed) return "Run tests";
|
|
586
996
|
if (!status.documentationCreated) return "Create documentation";
|
|
587
|
-
if (!status.
|
|
588
|
-
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";
|
|
589
1001
|
}
|
|
590
1002
|
|
|
591
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
|
});
|