@programinglive/dev-workflow-mcp-server 1.0.8 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +34 -34
- package/.github/ISSUE_TEMPLATE/feature_request.md +25 -25
- package/.github/pull_request_template.md +22 -22
- package/.husky/commit-msg +3 -3
- package/.husky/pre-commit +1 -1
- package/CHANGELOG.md +128 -97
- package/CODE_OF_CONDUCT.md +79 -79
- package/CONTRIBUTING.md +60 -60
- package/LICENSE +21 -21
- package/README.md +64 -63
- package/SECURITY.md +20 -20
- package/commit-helpers.js +166 -0
- package/commitlint.config.js +3 -3
- package/exec.js +4 -0
- package/git-helpers.js +152 -0
- package/index.js +45 -1300
- package/package.json +91 -91
- package/tests/handlers.test.js +188 -0
- package/tests/workflowState.test.js +231 -231
- package/tools/definitions.js +211 -0
- package/tools/handlers.js +620 -0
- package/utils.js +45 -0
- package/workflow-state.js +107 -0
|
@@ -1,231 +1,231 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
WorkflowState,
|
|
8
|
-
getNextStep,
|
|
9
|
-
containsTestFilesInStatus,
|
|
10
|
-
createCommitMessageParts,
|
|
11
|
-
determineReleaseTypeFromCommit,
|
|
12
|
-
workingTreeSummary,
|
|
13
|
-
} from "../index.js";
|
|
14
|
-
|
|
15
|
-
async function withWorkflowState(callback) {
|
|
16
|
-
const tempDir = await mkdtemp(path.join(tmpdir(), "workflow-state-"));
|
|
17
|
-
const stateFile = path.join(tempDir, "state.json");
|
|
18
|
-
const state = new WorkflowState(stateFile);
|
|
19
|
-
await state.load();
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
await callback(state);
|
|
23
|
-
} finally {
|
|
24
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
test("WorkflowState persists data across save/load", async () => {
|
|
29
|
-
await withWorkflowState(async (state) => {
|
|
30
|
-
state.state.currentPhase = "testing";
|
|
31
|
-
state.state.taskDescription = "Test persistence";
|
|
32
|
-
state.state.bugFixed = true;
|
|
33
|
-
state.state.testsCreated = true;
|
|
34
|
-
state.state.readyCheckCompleted = true;
|
|
35
|
-
state.state.released = true;
|
|
36
|
-
state.state.releaseCommand = "npm run release";
|
|
37
|
-
state.state.releaseNotes = "Automated release";
|
|
38
|
-
state.state.commitAndPushCompleted = true;
|
|
39
|
-
state.state.lastCommitMessage = "test: persistence";
|
|
40
|
-
state.state.lastPushBranch = "main";
|
|
41
|
-
state.state.testsSkipped = true;
|
|
42
|
-
state.state.testsSkippedReason = "Legacy app uses manual QA";
|
|
43
|
-
await state.save();
|
|
44
|
-
|
|
45
|
-
const reloaded = new WorkflowState(state.stateFile);
|
|
46
|
-
await reloaded.load();
|
|
47
|
-
|
|
48
|
-
assert.equal(reloaded.state.currentPhase, "testing");
|
|
49
|
-
assert.equal(reloaded.state.taskDescription, "Test persistence");
|
|
50
|
-
assert.equal(reloaded.state.bugFixed, true);
|
|
51
|
-
assert.equal(reloaded.state.testsCreated, true);
|
|
52
|
-
assert.equal(reloaded.state.readyCheckCompleted, true);
|
|
53
|
-
assert.equal(reloaded.state.released, true);
|
|
54
|
-
assert.equal(reloaded.state.releaseCommand, "npm run release");
|
|
55
|
-
assert.equal(reloaded.state.releaseNotes, "Automated release");
|
|
56
|
-
assert.equal(reloaded.state.commitAndPushCompleted, true);
|
|
57
|
-
assert.equal(reloaded.state.lastCommitMessage, "test: persistence");
|
|
58
|
-
assert.equal(reloaded.state.lastPushBranch, "main");
|
|
59
|
-
assert.equal(reloaded.state.testsSkipped, true);
|
|
60
|
-
assert.equal(
|
|
61
|
-
reloaded.state.testsSkippedReason,
|
|
62
|
-
"Legacy app uses manual QA"
|
|
63
|
-
);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("WorkflowState reset keeps history but clears progress", async () => {
|
|
68
|
-
await withWorkflowState(async (state) => {
|
|
69
|
-
state.state.currentPhase = "ready";
|
|
70
|
-
state.state.testsCreated = true;
|
|
71
|
-
state.state.testsPassed = true;
|
|
72
|
-
state.state.documentationCreated = true;
|
|
73
|
-
state.state.readyCheckCompleted = true;
|
|
74
|
-
state.state.released = true;
|
|
75
|
-
state.state.commitAndPushCompleted = true;
|
|
76
|
-
state.state.testsSkipped = true;
|
|
77
|
-
state.state.testsSkippedReason = "Manual QA";
|
|
78
|
-
state.addToHistory({ taskDescription: "Sample task" });
|
|
79
|
-
|
|
80
|
-
state.reset();
|
|
81
|
-
|
|
82
|
-
assert.equal(state.state.currentPhase, "idle");
|
|
83
|
-
assert.equal(state.state.testsCreated, false);
|
|
84
|
-
assert.equal(state.state.testsPassed, false);
|
|
85
|
-
assert.equal(state.state.documentationCreated, false);
|
|
86
|
-
assert.equal(state.state.readyCheckCompleted, false);
|
|
87
|
-
assert.equal(state.state.released, false);
|
|
88
|
-
assert.equal(state.state.commitAndPushCompleted, false);
|
|
89
|
-
assert.equal(state.state.testsSkipped, false);
|
|
90
|
-
assert.equal(state.state.testsSkippedReason, "");
|
|
91
|
-
assert.equal(state.state.history.length, 1);
|
|
92
|
-
assert.equal(state.state.history[0].taskDescription, "Sample task");
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("getNextStep treats skipped tests as satisfied", () => {
|
|
97
|
-
const skippedState = {
|
|
98
|
-
bugFixed: true,
|
|
99
|
-
testsCreated: true,
|
|
100
|
-
testsPassed: true,
|
|
101
|
-
testsSkipped: true,
|
|
102
|
-
testsSkippedReason: "Legacy system",
|
|
103
|
-
documentationCreated: false,
|
|
104
|
-
readyCheckCompleted: false,
|
|
105
|
-
commitAndPushCompleted: false,
|
|
106
|
-
released: false,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
assert.equal(getNextStep(skippedState), "Create documentation");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("getNextStep guides workflow progression", () => {
|
|
113
|
-
const baseState = {
|
|
114
|
-
bugFixed: false,
|
|
115
|
-
testsCreated: false,
|
|
116
|
-
testsPassed: false,
|
|
117
|
-
documentationCreated: false,
|
|
118
|
-
readyCheckCompleted: false,
|
|
119
|
-
commitAndPushCompleted: false,
|
|
120
|
-
released: false,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
assert.equal(
|
|
124
|
-
getNextStep(baseState),
|
|
125
|
-
"Mark feature/bug as fixed"
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const afterFix = { ...baseState, bugFixed: true };
|
|
129
|
-
assert.equal(
|
|
130
|
-
getNextStep(afterFix),
|
|
131
|
-
"Create tests"
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
const afterTestsCreated = { ...afterFix, testsCreated: true };
|
|
135
|
-
assert.equal(getNextStep(afterTestsCreated), "Run tests");
|
|
136
|
-
|
|
137
|
-
const afterTests = { ...afterTestsCreated, testsPassed: true };
|
|
138
|
-
assert.equal(
|
|
139
|
-
getNextStep(afterTests),
|
|
140
|
-
"Create documentation"
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const afterDocs = { ...afterTests, documentationCreated: true };
|
|
144
|
-
assert.equal(getNextStep(afterDocs), "Run 'check_ready_to_commit'");
|
|
145
|
-
|
|
146
|
-
const afterReadyCheck = { ...afterDocs, readyCheckCompleted: true };
|
|
147
|
-
assert.equal(getNextStep(afterReadyCheck), "Run 'commit_and_push'");
|
|
148
|
-
|
|
149
|
-
const afterCommit = { ...afterReadyCheck, commitAndPushCompleted: true };
|
|
150
|
-
assert.equal(getNextStep(afterCommit), "Run 'perform_release'");
|
|
151
|
-
|
|
152
|
-
const afterRelease = { ...afterCommit, released: true };
|
|
153
|
-
assert.equal(getNextStep(afterRelease), "Complete the task");
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
test("containsTestFilesInStatus detects modified test files", () => {
|
|
157
|
-
const statusOutput = [
|
|
158
|
-
" M src/app.test.js",
|
|
159
|
-
"A tests/helpers/util.js",
|
|
160
|
-
"R src/old.js -> src/new.spec.ts",
|
|
161
|
-
].join("\n");
|
|
162
|
-
|
|
163
|
-
assert.equal(containsTestFilesInStatus(statusOutput), true);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test("containsTestFilesInStatus returns false when no test files present", () => {
|
|
167
|
-
const statusOutput = [
|
|
168
|
-
" M src/app.js",
|
|
169
|
-
"A docs/README.md",
|
|
170
|
-
"?? assets/logo.png",
|
|
171
|
-
].join("\n");
|
|
172
|
-
|
|
173
|
-
assert.equal(containsTestFilesInStatus(statusOutput), false);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
test("createCommitMessageParts generates test prefixed summary with details", () => {
|
|
177
|
-
const changes = [
|
|
178
|
-
{ status: "M", path: "src/app.test.js" },
|
|
179
|
-
{ status: "A", path: "tests/helpers/util.js" },
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
const { summary, body } = createCommitMessageParts(changes);
|
|
183
|
-
|
|
184
|
-
assert.equal(summary.startsWith("test:"), true);
|
|
185
|
-
assert.equal(body.includes("- modified src/app.test.js"), true);
|
|
186
|
-
assert.equal(body.includes("- added tests/helpers/util.js"), true);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test("createCommitMessageParts respects provided summary when present", () => {
|
|
190
|
-
const changes = [{ status: "M", path: "docs/guide.md" }];
|
|
191
|
-
const provided = "docs: update guide";
|
|
192
|
-
|
|
193
|
-
const { summary, body } = createCommitMessageParts(changes, provided);
|
|
194
|
-
|
|
195
|
-
assert.equal(summary, provided);
|
|
196
|
-
assert.equal(body.includes("- modified docs/guide.md"), true);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test("determineReleaseTypeFromCommit returns major for breaking change", () => {
|
|
200
|
-
const message = "feat!: overhaul API\n\nBREAKING CHANGE: new behavior";
|
|
201
|
-
assert.equal(determineReleaseTypeFromCommit(message), "major");
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test("determineReleaseTypeFromCommit returns minor for feat", () => {
|
|
205
|
-
const message = "feat: add dashboard";
|
|
206
|
-
assert.equal(determineReleaseTypeFromCommit(message), "minor");
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("determineReleaseTypeFromCommit returns patch for fix", () => {
|
|
210
|
-
const message = "fix: resolve login issue";
|
|
211
|
-
assert.equal(determineReleaseTypeFromCommit(message), "patch");
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("determineReleaseTypeFromCommit defaults to patch for other commit types", () => {
|
|
215
|
-
const message = "chore: update dependencies";
|
|
216
|
-
assert.equal(determineReleaseTypeFromCommit(message), "patch");
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test("workingTreeSummary reports no changes for empty output", () => {
|
|
220
|
-
const summary = workingTreeSummary("\n \n");
|
|
221
|
-
assert.equal(summary.hasChanges, false);
|
|
222
|
-
assert.deepEqual(summary.lines, []);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("workingTreeSummary lists trimmed status lines", () => {
|
|
226
|
-
const statusOutput = " M src/app.js\nA tests/new.test.js\n";
|
|
227
|
-
const summary = workingTreeSummary(statusOutput);
|
|
228
|
-
|
|
229
|
-
assert.equal(summary.hasChanges, true);
|
|
230
|
-
assert.deepEqual(summary.lines, ["M src/app.js", "A tests/new.test.js"]);
|
|
231
|
-
});
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
WorkflowState,
|
|
8
|
+
getNextStep,
|
|
9
|
+
containsTestFilesInStatus,
|
|
10
|
+
createCommitMessageParts,
|
|
11
|
+
determineReleaseTypeFromCommit,
|
|
12
|
+
workingTreeSummary,
|
|
13
|
+
} from "../index.js";
|
|
14
|
+
|
|
15
|
+
async function withWorkflowState(callback) {
|
|
16
|
+
const tempDir = await mkdtemp(path.join(tmpdir(), "workflow-state-"));
|
|
17
|
+
const stateFile = path.join(tempDir, "state.json");
|
|
18
|
+
const state = new WorkflowState(stateFile);
|
|
19
|
+
await state.load();
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
await callback(state);
|
|
23
|
+
} finally {
|
|
24
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
test("WorkflowState persists data across save/load", async () => {
|
|
29
|
+
await withWorkflowState(async (state) => {
|
|
30
|
+
state.state.currentPhase = "testing";
|
|
31
|
+
state.state.taskDescription = "Test persistence";
|
|
32
|
+
state.state.bugFixed = true;
|
|
33
|
+
state.state.testsCreated = true;
|
|
34
|
+
state.state.readyCheckCompleted = true;
|
|
35
|
+
state.state.released = true;
|
|
36
|
+
state.state.releaseCommand = "npm run release";
|
|
37
|
+
state.state.releaseNotes = "Automated release";
|
|
38
|
+
state.state.commitAndPushCompleted = true;
|
|
39
|
+
state.state.lastCommitMessage = "test: persistence";
|
|
40
|
+
state.state.lastPushBranch = "main";
|
|
41
|
+
state.state.testsSkipped = true;
|
|
42
|
+
state.state.testsSkippedReason = "Legacy app uses manual QA";
|
|
43
|
+
await state.save();
|
|
44
|
+
|
|
45
|
+
const reloaded = new WorkflowState(state.stateFile);
|
|
46
|
+
await reloaded.load();
|
|
47
|
+
|
|
48
|
+
assert.equal(reloaded.state.currentPhase, "testing");
|
|
49
|
+
assert.equal(reloaded.state.taskDescription, "Test persistence");
|
|
50
|
+
assert.equal(reloaded.state.bugFixed, true);
|
|
51
|
+
assert.equal(reloaded.state.testsCreated, true);
|
|
52
|
+
assert.equal(reloaded.state.readyCheckCompleted, true);
|
|
53
|
+
assert.equal(reloaded.state.released, true);
|
|
54
|
+
assert.equal(reloaded.state.releaseCommand, "npm run release");
|
|
55
|
+
assert.equal(reloaded.state.releaseNotes, "Automated release");
|
|
56
|
+
assert.equal(reloaded.state.commitAndPushCompleted, true);
|
|
57
|
+
assert.equal(reloaded.state.lastCommitMessage, "test: persistence");
|
|
58
|
+
assert.equal(reloaded.state.lastPushBranch, "main");
|
|
59
|
+
assert.equal(reloaded.state.testsSkipped, true);
|
|
60
|
+
assert.equal(
|
|
61
|
+
reloaded.state.testsSkippedReason,
|
|
62
|
+
"Legacy app uses manual QA"
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("WorkflowState reset keeps history but clears progress", async () => {
|
|
68
|
+
await withWorkflowState(async (state) => {
|
|
69
|
+
state.state.currentPhase = "ready";
|
|
70
|
+
state.state.testsCreated = true;
|
|
71
|
+
state.state.testsPassed = true;
|
|
72
|
+
state.state.documentationCreated = true;
|
|
73
|
+
state.state.readyCheckCompleted = true;
|
|
74
|
+
state.state.released = true;
|
|
75
|
+
state.state.commitAndPushCompleted = true;
|
|
76
|
+
state.state.testsSkipped = true;
|
|
77
|
+
state.state.testsSkippedReason = "Manual QA";
|
|
78
|
+
state.addToHistory({ taskDescription: "Sample task" });
|
|
79
|
+
|
|
80
|
+
state.reset();
|
|
81
|
+
|
|
82
|
+
assert.equal(state.state.currentPhase, "idle");
|
|
83
|
+
assert.equal(state.state.testsCreated, false);
|
|
84
|
+
assert.equal(state.state.testsPassed, false);
|
|
85
|
+
assert.equal(state.state.documentationCreated, false);
|
|
86
|
+
assert.equal(state.state.readyCheckCompleted, false);
|
|
87
|
+
assert.equal(state.state.released, false);
|
|
88
|
+
assert.equal(state.state.commitAndPushCompleted, false);
|
|
89
|
+
assert.equal(state.state.testsSkipped, false);
|
|
90
|
+
assert.equal(state.state.testsSkippedReason, "");
|
|
91
|
+
assert.equal(state.state.history.length, 1);
|
|
92
|
+
assert.equal(state.state.history[0].taskDescription, "Sample task");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("getNextStep treats skipped tests as satisfied", () => {
|
|
97
|
+
const skippedState = {
|
|
98
|
+
bugFixed: true,
|
|
99
|
+
testsCreated: true,
|
|
100
|
+
testsPassed: true,
|
|
101
|
+
testsSkipped: true,
|
|
102
|
+
testsSkippedReason: "Legacy system",
|
|
103
|
+
documentationCreated: false,
|
|
104
|
+
readyCheckCompleted: false,
|
|
105
|
+
commitAndPushCompleted: false,
|
|
106
|
+
released: false,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
assert.equal(getNextStep(skippedState), "Create documentation");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("getNextStep guides workflow progression", () => {
|
|
113
|
+
const baseState = {
|
|
114
|
+
bugFixed: false,
|
|
115
|
+
testsCreated: false,
|
|
116
|
+
testsPassed: false,
|
|
117
|
+
documentationCreated: false,
|
|
118
|
+
readyCheckCompleted: false,
|
|
119
|
+
commitAndPushCompleted: false,
|
|
120
|
+
released: false,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
assert.equal(
|
|
124
|
+
getNextStep(baseState),
|
|
125
|
+
"Mark feature/bug as fixed"
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const afterFix = { ...baseState, bugFixed: true };
|
|
129
|
+
assert.equal(
|
|
130
|
+
getNextStep(afterFix),
|
|
131
|
+
"Create tests"
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const afterTestsCreated = { ...afterFix, testsCreated: true };
|
|
135
|
+
assert.equal(getNextStep(afterTestsCreated), "Run tests");
|
|
136
|
+
|
|
137
|
+
const afterTests = { ...afterTestsCreated, testsPassed: true };
|
|
138
|
+
assert.equal(
|
|
139
|
+
getNextStep(afterTests),
|
|
140
|
+
"Create documentation"
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const afterDocs = { ...afterTests, documentationCreated: true };
|
|
144
|
+
assert.equal(getNextStep(afterDocs), "Run 'check_ready_to_commit'");
|
|
145
|
+
|
|
146
|
+
const afterReadyCheck = { ...afterDocs, readyCheckCompleted: true };
|
|
147
|
+
assert.equal(getNextStep(afterReadyCheck), "Run 'commit_and_push'");
|
|
148
|
+
|
|
149
|
+
const afterCommit = { ...afterReadyCheck, commitAndPushCompleted: true };
|
|
150
|
+
assert.equal(getNextStep(afterCommit), "Run 'perform_release'");
|
|
151
|
+
|
|
152
|
+
const afterRelease = { ...afterCommit, released: true };
|
|
153
|
+
assert.equal(getNextStep(afterRelease), "Complete the task");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("containsTestFilesInStatus detects modified test files", () => {
|
|
157
|
+
const statusOutput = [
|
|
158
|
+
" M src/app.test.js",
|
|
159
|
+
"A tests/helpers/util.js",
|
|
160
|
+
"R src/old.js -> src/new.spec.ts",
|
|
161
|
+
].join("\n");
|
|
162
|
+
|
|
163
|
+
assert.equal(containsTestFilesInStatus(statusOutput), true);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("containsTestFilesInStatus returns false when no test files present", () => {
|
|
167
|
+
const statusOutput = [
|
|
168
|
+
" M src/app.js",
|
|
169
|
+
"A docs/README.md",
|
|
170
|
+
"?? assets/logo.png",
|
|
171
|
+
].join("\n");
|
|
172
|
+
|
|
173
|
+
assert.equal(containsTestFilesInStatus(statusOutput), false);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("createCommitMessageParts generates test prefixed summary with details", () => {
|
|
177
|
+
const changes = [
|
|
178
|
+
{ status: "M", path: "src/app.test.js" },
|
|
179
|
+
{ status: "A", path: "tests/helpers/util.js" },
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
const { summary, body } = createCommitMessageParts(changes);
|
|
183
|
+
|
|
184
|
+
assert.equal(summary.startsWith("test:"), true);
|
|
185
|
+
assert.equal(body.includes("- modified src/app.test.js"), true);
|
|
186
|
+
assert.equal(body.includes("- added tests/helpers/util.js"), true);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("createCommitMessageParts respects provided summary when present", () => {
|
|
190
|
+
const changes = [{ status: "M", path: "docs/guide.md" }];
|
|
191
|
+
const provided = "docs: update guide";
|
|
192
|
+
|
|
193
|
+
const { summary, body } = createCommitMessageParts(changes, provided);
|
|
194
|
+
|
|
195
|
+
assert.equal(summary, provided);
|
|
196
|
+
assert.equal(body.includes("- modified docs/guide.md"), true);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("determineReleaseTypeFromCommit returns major for breaking change", () => {
|
|
200
|
+
const message = "feat!: overhaul API\n\nBREAKING CHANGE: new behavior";
|
|
201
|
+
assert.equal(determineReleaseTypeFromCommit(message), "major");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("determineReleaseTypeFromCommit returns minor for feat", () => {
|
|
205
|
+
const message = "feat: add dashboard";
|
|
206
|
+
assert.equal(determineReleaseTypeFromCommit(message), "minor");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("determineReleaseTypeFromCommit returns patch for fix", () => {
|
|
210
|
+
const message = "fix: resolve login issue";
|
|
211
|
+
assert.equal(determineReleaseTypeFromCommit(message), "patch");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("determineReleaseTypeFromCommit defaults to patch for other commit types", () => {
|
|
215
|
+
const message = "chore: update dependencies";
|
|
216
|
+
assert.equal(determineReleaseTypeFromCommit(message), "patch");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("workingTreeSummary reports no changes for empty output", () => {
|
|
220
|
+
const summary = workingTreeSummary("\n \n");
|
|
221
|
+
assert.equal(summary.hasChanges, false);
|
|
222
|
+
assert.deepEqual(summary.lines, []);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("workingTreeSummary lists trimmed status lines", () => {
|
|
226
|
+
const statusOutput = " M src/app.js\nA tests/new.test.js\n";
|
|
227
|
+
const summary = workingTreeSummary(statusOutput);
|
|
228
|
+
|
|
229
|
+
assert.equal(summary.hasChanges, true);
|
|
230
|
+
assert.deepEqual(summary.lines, ["M src/app.js", "A tests/new.test.js"]);
|
|
231
|
+
});
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
export function getToolList() {
|
|
2
|
+
return [
|
|
3
|
+
{
|
|
4
|
+
name: "start_task",
|
|
5
|
+
description:
|
|
6
|
+
"Start a new coding task. This is the first step - be conscious about what you're coding.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
description: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Clear description of what you're going to code",
|
|
13
|
+
},
|
|
14
|
+
type: {
|
|
15
|
+
type: "string",
|
|
16
|
+
enum: ["feature", "bugfix", "refactor", "other"],
|
|
17
|
+
description: "Type of task",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ["description", "type"],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "mark_bug_fixed",
|
|
25
|
+
description:
|
|
26
|
+
"Mark that the bug/feature is fixed. Reminder: Now you MUST create tests!",
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
summary: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Brief summary of what was fixed/implemented",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: ["summary"],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "create_tests",
|
|
40
|
+
description: "Mark that tests covering the change have been created.",
|
|
41
|
+
inputSchema: {
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "skip_tests",
|
|
48
|
+
description:
|
|
49
|
+
"Record that tests were intentionally skipped, with a justification.",
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
reason: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Why tests cannot be created or executed",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ["reason"],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "run_tests",
|
|
63
|
+
description:
|
|
64
|
+
"Record test results. NEVER commit if tests fail! Only proceed if all tests are green.",
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
passed: {
|
|
69
|
+
type: "boolean",
|
|
70
|
+
description: "Did all tests pass?",
|
|
71
|
+
},
|
|
72
|
+
testCommand: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "The test command that was run",
|
|
75
|
+
},
|
|
76
|
+
details: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "Test results details",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
required: ["passed", "testCommand"],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "create_documentation",
|
|
86
|
+
description:
|
|
87
|
+
"Mark that documentation has been created/updated. This is required before committing.",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
documentationType: {
|
|
92
|
+
type: "string",
|
|
93
|
+
enum: ["README", "inline-comments", "API-docs", "changelog", "other"],
|
|
94
|
+
description: "Type of documentation created",
|
|
95
|
+
},
|
|
96
|
+
summary: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "What was documented",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
required: ["documentationType", "summary"],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "check_ready_to_commit",
|
|
106
|
+
description:
|
|
107
|
+
"Check if all workflow steps are completed and you're ready to commit & push.",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: {},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "commit_and_push",
|
|
115
|
+
description:
|
|
116
|
+
"Automatically run git add, commit, and push once the ready check passes.",
|
|
117
|
+
inputSchema: {
|
|
118
|
+
type: "object",
|
|
119
|
+
properties: {
|
|
120
|
+
commitMessage: {
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "Commit message to use",
|
|
123
|
+
},
|
|
124
|
+
branch: {
|
|
125
|
+
type: "string",
|
|
126
|
+
description: "Optional branch name to push to (defaults to current branch)",
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
required: [],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "perform_release",
|
|
134
|
+
description:
|
|
135
|
+
"Record release details after committing and pushing your changes.",
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
command: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "Release command that was executed",
|
|
142
|
+
},
|
|
143
|
+
notes: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "Optional release notes",
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
required: ["command"],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "complete_task",
|
|
153
|
+
description:
|
|
154
|
+
"Mark the task as complete after successful commit & push. Resets workflow for next task.",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: "object",
|
|
157
|
+
properties: {
|
|
158
|
+
commitMessage: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description: "The commit message used",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
required: ["commitMessage"],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "drop_task",
|
|
168
|
+
description:
|
|
169
|
+
"Abandon the current task and reset the workflow without completing it.",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
type: "object",
|
|
172
|
+
properties: {
|
|
173
|
+
reason: {
|
|
174
|
+
type: "string",
|
|
175
|
+
description: "Optional explanation for dropping the task",
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "get_workflow_status",
|
|
182
|
+
description: "Get current workflow status and what needs to be done next.",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: "object",
|
|
185
|
+
properties: {},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: "view_history",
|
|
190
|
+
description: "View workflow history of completed tasks.",
|
|
191
|
+
inputSchema: {
|
|
192
|
+
type: "object",
|
|
193
|
+
properties: {
|
|
194
|
+
limit: {
|
|
195
|
+
type: "number",
|
|
196
|
+
description: "Number of recent tasks to show (default: 10)",
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "continue_workflow",
|
|
203
|
+
description:
|
|
204
|
+
"Get guidance on the next workflow step. Automatically performs the ready check when appropriate.",
|
|
205
|
+
inputSchema: {
|
|
206
|
+
type: "object",
|
|
207
|
+
properties: {},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
}
|