@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
package/CONTRIBUTING.md
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
# Contributing to dev-workflow-mcp-server
|
|
2
|
-
|
|
3
|
-
Thanks for your interest in contributing! Follow the guidelines below to keep the development process smooth and aligned with the workflow enforced by this project.
|
|
4
|
-
|
|
5
|
-
## Development Workflow
|
|
6
|
-
|
|
7
|
-
1. **Start a task** using the MCP server's `start_task` tool with a clear description and task type.
|
|
8
|
-
2. **Implement the change** following the single flow rules documented in the README.
|
|
9
|
-
3. **Write or update tests**. All code changes must include tests when applicable.
|
|
10
|
-
4. **Run tests** with `npm test` and ensure they pass.
|
|
11
|
-
5. **Document changes**. Update `README.md` or other docs as needed.
|
|
12
|
-
6. **Commit** following conventional commits (e.g., `feat: add new tool`).
|
|
13
|
-
7. **Push** your branch and open a pull request using the provided template.
|
|
14
|
-
|
|
15
|
-
## Getting Started
|
|
16
|
-
|
|
17
|
-
- Fork the repository and clone it locally.
|
|
18
|
-
- Install dependencies:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm install
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
- Run the test suite to verify your setup:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm test
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Pull Requests
|
|
31
|
-
|
|
32
|
-
- Use a descriptive branch name (e.g., `feat/new-tool` or `fix/workflow-reset`).
|
|
33
|
-
- Fill out the pull request template completely, including tests and documentation details.
|
|
34
|
-
- Link related issues in the PR description.
|
|
35
|
-
- Ensure CI checks pass before requesting review.
|
|
36
|
-
|
|
37
|
-
## Coding Standards
|
|
38
|
-
|
|
39
|
-
- Use TypeScript-friendly patterns even in JavaScript (avoid dynamic typing pitfalls).
|
|
40
|
-
- Remove unused imports and variables before committing.
|
|
41
|
-
- Follow existing code style and formatting.
|
|
42
|
-
|
|
43
|
-
## Commit Messages
|
|
44
|
-
|
|
45
|
-
- Follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
46
|
-
- Example: `feat: enforce documentation reminder before commit`.
|
|
47
|
-
|
|
48
|
-
## Reporting Issues
|
|
49
|
-
|
|
50
|
-
- Search open issues to avoid duplicates.
|
|
51
|
-
- When filing a bug, include reproduction steps, expected behavior, and environment details.
|
|
52
|
-
- Feature requests should explain the problem being solved and potential benefits.
|
|
53
|
-
|
|
54
|
-
## Code of Conduct
|
|
55
|
-
|
|
56
|
-
By participating, you agree to abide by the [Code of Conduct](./CODE_OF_CONDUCT.md).
|
|
57
|
-
|
|
58
|
-
## Contact
|
|
59
|
-
|
|
60
|
-
Questions? Reach out at `opensource@programinglive.com`.
|
|
1
|
+
# Contributing to dev-workflow-mcp-server
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing! Follow the guidelines below to keep the development process smooth and aligned with the workflow enforced by this project.
|
|
4
|
+
|
|
5
|
+
## Development Workflow
|
|
6
|
+
|
|
7
|
+
1. **Start a task** using the MCP server's `start_task` tool with a clear description and task type.
|
|
8
|
+
2. **Implement the change** following the single flow rules documented in the README.
|
|
9
|
+
3. **Write or update tests**. All code changes must include tests when applicable.
|
|
10
|
+
4. **Run tests** with `npm test` and ensure they pass.
|
|
11
|
+
5. **Document changes**. Update `README.md` or other docs as needed.
|
|
12
|
+
6. **Commit** following conventional commits (e.g., `feat: add new tool`).
|
|
13
|
+
7. **Push** your branch and open a pull request using the provided template.
|
|
14
|
+
|
|
15
|
+
## Getting Started
|
|
16
|
+
|
|
17
|
+
- Fork the repository and clone it locally.
|
|
18
|
+
- Install dependencies:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- Run the test suite to verify your setup:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm test
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Pull Requests
|
|
31
|
+
|
|
32
|
+
- Use a descriptive branch name (e.g., `feat/new-tool` or `fix/workflow-reset`).
|
|
33
|
+
- Fill out the pull request template completely, including tests and documentation details.
|
|
34
|
+
- Link related issues in the PR description.
|
|
35
|
+
- Ensure CI checks pass before requesting review.
|
|
36
|
+
|
|
37
|
+
## Coding Standards
|
|
38
|
+
|
|
39
|
+
- Use TypeScript-friendly patterns even in JavaScript (avoid dynamic typing pitfalls).
|
|
40
|
+
- Remove unused imports and variables before committing.
|
|
41
|
+
- Follow existing code style and formatting.
|
|
42
|
+
|
|
43
|
+
## Commit Messages
|
|
44
|
+
|
|
45
|
+
- Follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
|
46
|
+
- Example: `feat: enforce documentation reminder before commit`.
|
|
47
|
+
|
|
48
|
+
## Reporting Issues
|
|
49
|
+
|
|
50
|
+
- Search open issues to avoid duplicates.
|
|
51
|
+
- When filing a bug, include reproduction steps, expected behavior, and environment details.
|
|
52
|
+
- Feature requests should explain the problem being solved and potential benefits.
|
|
53
|
+
|
|
54
|
+
## Code of Conduct
|
|
55
|
+
|
|
56
|
+
By participating, you agree to abide by the [Code of Conduct](./CODE_OF_CONDUCT.md).
|
|
57
|
+
|
|
58
|
+
## Contact
|
|
59
|
+
|
|
60
|
+
Questions? Reach out at `opensource@programinglive.com`.
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 programinglive
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 programinglive
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
# Development Workflow MCP Server
|
|
2
|
-
|
|
3
|
-
An MCP (Model Context Protocol) server that helps enforce development discipline and workflow best practices. This server acts as your coding conscience, reminding you to follow proper development workflows.
|
|
4
|
-
|
|
5
|
-
## 🎯 Purpose
|
|
6
|
-
|
|
7
|
-
This MCP server guides you through a disciplined development workflow:
|
|
8
|
-
|
|
9
|
-
1. **Start Conscious** - Be clear about what you're coding
|
|
10
|
-
2. **Fix/Implement** - Write your code
|
|
11
|
-
3. **Create Tests** - Always test your changes
|
|
12
|
-
4. **Run Tests** - Tests must pass (GREEN)
|
|
13
|
-
5. **Document** - Update documentation
|
|
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
|
|
16
|
-
|
|
17
|
-
## 🚀 Installation
|
|
18
|
-
|
|
19
|
-
### 1. Install Dependencies
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
npm install
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
### 2. Configure in Windsurf/Claude Desktop
|
|
26
|
-
|
|
27
|
-
Point your MCP client to the server entry point. Replace `<PROJECT_ROOT>` with the absolute path to this repository on your machine.
|
|
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": {
|
|
1
|
+
# Development Workflow MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that helps enforce development discipline and workflow best practices. This server acts as your coding conscience, reminding you to follow proper development workflows.
|
|
4
|
+
|
|
5
|
+
## 🎯 Purpose
|
|
6
|
+
|
|
7
|
+
This MCP server guides you through a disciplined development workflow:
|
|
8
|
+
|
|
9
|
+
1. **Start Conscious** - Be clear about what you're coding
|
|
10
|
+
2. **Fix/Implement** - Write your code
|
|
11
|
+
3. **Create Tests** - Always test your changes
|
|
12
|
+
4. **Run Tests** - Tests must pass (GREEN)
|
|
13
|
+
5. **Document** - Update documentation
|
|
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
|
|
16
|
+
|
|
17
|
+
## 🚀 Installation
|
|
18
|
+
|
|
19
|
+
### 1. Install Dependencies
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Configure in Windsurf/Claude Desktop
|
|
26
|
+
|
|
27
|
+
Point your MCP client to the server entry point. Replace `<PROJECT_ROOT>` with the absolute path to this repository on your machine.
|
|
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
64
|
"dev-workflow": {
|
|
65
65
|
"command": "node",
|
|
66
66
|
"args": ["<PROJECT_ROOT>\\index.js"]
|
|
@@ -333,6 +333,7 @@ The server maintains state in `.workflow-state.json`:
|
|
|
333
333
|
- History of completed tasks
|
|
334
334
|
|
|
335
335
|
This file is automatically created and managed by the server.
|
|
336
|
+
It contains local, machine-specific progress and is ignored by git so each environment can manage its own workflow history without cross-contamination.
|
|
336
337
|
|
|
337
338
|
## 🤝 Integration with Your Rules
|
|
338
339
|
|
package/SECURITY.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
# Security Policy
|
|
2
|
-
|
|
3
|
-
## Supported Versions
|
|
4
|
-
|
|
5
|
-
We release patches for security vulnerabilities under new minor or patch versions. Always use the latest release.
|
|
6
|
-
|
|
7
|
-
## Reporting a Vulnerability
|
|
8
|
-
|
|
9
|
-
If you discover a vulnerability, please email `security@programinglive.com` with:
|
|
10
|
-
|
|
11
|
-
- Description of the issue
|
|
12
|
-
- Steps to reproduce
|
|
13
|
-
- Potential impact
|
|
14
|
-
- Suggested remediation (if known)
|
|
15
|
-
|
|
16
|
-
We aim to acknowledge reports within 72 hours and will keep you informed as we work on a fix. Please do not disclose the vulnerability publicly until we have released an update.
|
|
17
|
-
|
|
18
|
-
## Disclosure Policy
|
|
19
|
-
|
|
20
|
-
Once a fix is available, we will publish a security advisory in the repository releases and notify contributors via the README.
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
We release patches for security vulnerabilities under new minor or patch versions. Always use the latest release.
|
|
6
|
+
|
|
7
|
+
## Reporting a Vulnerability
|
|
8
|
+
|
|
9
|
+
If you discover a vulnerability, please email `security@programinglive.com` with:
|
|
10
|
+
|
|
11
|
+
- Description of the issue
|
|
12
|
+
- Steps to reproduce
|
|
13
|
+
- Potential impact
|
|
14
|
+
- Suggested remediation (if known)
|
|
15
|
+
|
|
16
|
+
We aim to acknowledge reports within 72 hours and will keep you informed as we work on a fix. Please do not disclose the vulnerability publicly until we have released an update.
|
|
17
|
+
|
|
18
|
+
## Disclosure Policy
|
|
19
|
+
|
|
20
|
+
Once a fix is available, we will publish a security advisory in the repository releases and notify contributors via the README.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
function formatFileList(paths) {
|
|
2
|
+
const uniquePaths = [...new Set(paths.filter(Boolean))];
|
|
3
|
+
|
|
4
|
+
if (uniquePaths.length === 0) {
|
|
5
|
+
return "updates";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (uniquePaths.length === 1) {
|
|
9
|
+
return uniquePaths[0];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (uniquePaths.length === 2) {
|
|
13
|
+
return `${uniquePaths[0]} and ${uniquePaths[1]}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (uniquePaths.length === 3) {
|
|
17
|
+
return `${uniquePaths[0]}, ${uniquePaths[1]}, and ${uniquePaths[2]}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const remaining = uniquePaths.length - 3;
|
|
21
|
+
return `${uniquePaths[0]}, ${uniquePaths[1]}, ${uniquePaths[2]} +${remaining} more`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function determineCommitType(changes) {
|
|
25
|
+
const hasTestChange = changes.some((change) =>
|
|
26
|
+
[change.path, change.originalPath].some((p) => isTestFilePath(p))
|
|
27
|
+
);
|
|
28
|
+
const hasDocsChange = changes.some((change) =>
|
|
29
|
+
[change.path, change.originalPath].some((p) => isDocumentationFilePath(p))
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (hasTestChange) {
|
|
33
|
+
return "test";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (hasDocsChange) {
|
|
37
|
+
return "docs";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return "chore";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isTestFilePath(filePath) {
|
|
44
|
+
if (!filePath) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (/\.(test|spec)\.[^.]+$/i.test(filePath)) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return /(\/|^)(__tests__|tests?|spec)\//i.test(filePath);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isDocumentationFilePath(filePath) {
|
|
56
|
+
if (!filePath) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const normalized = filePath.toLowerCase();
|
|
61
|
+
return (
|
|
62
|
+
normalized.startsWith("docs/") ||
|
|
63
|
+
normalized.startsWith("documentation/") ||
|
|
64
|
+
normalized.endsWith(".md") ||
|
|
65
|
+
normalized.endsWith(".rst") ||
|
|
66
|
+
normalized.endsWith(".adoc") ||
|
|
67
|
+
normalized.includes("readme")
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function describeChange(change) {
|
|
72
|
+
const statusCode = change.status || "";
|
|
73
|
+
const path = change.path || change.originalPath || "";
|
|
74
|
+
|
|
75
|
+
if (statusCode.startsWith("R")) {
|
|
76
|
+
return `renamed ${change.originalPath} -> ${change.path}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (statusCode.startsWith("C")) {
|
|
80
|
+
return `copied ${change.originalPath} -> ${change.path}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
switch (statusCode[0]) {
|
|
84
|
+
case "A":
|
|
85
|
+
return `added ${path}`;
|
|
86
|
+
case "M":
|
|
87
|
+
return `modified ${path}`;
|
|
88
|
+
case "D":
|
|
89
|
+
return `removed ${path}`;
|
|
90
|
+
case "U":
|
|
91
|
+
return `updated ${path}`;
|
|
92
|
+
default:
|
|
93
|
+
return `${statusCode} ${path}`.trim();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createCommitMessageParts(changes, providedSummary = "") {
|
|
98
|
+
const summaryInput = typeof providedSummary === "string" ? providedSummary.trim() : "";
|
|
99
|
+
|
|
100
|
+
if (!Array.isArray(changes) || changes.length === 0) {
|
|
101
|
+
return {
|
|
102
|
+
summary: summaryInput,
|
|
103
|
+
body: "",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const commitType = determineCommitType(changes);
|
|
108
|
+
const summaryPaths = changes
|
|
109
|
+
.map((change) => change.path || change.originalPath)
|
|
110
|
+
.filter(Boolean);
|
|
111
|
+
const generatedSummary = `${commitType}: update ${formatFileList(summaryPaths)}`;
|
|
112
|
+
const summary = summaryInput || generatedSummary;
|
|
113
|
+
|
|
114
|
+
const detailLines = changes
|
|
115
|
+
.map((change) => describeChange(change))
|
|
116
|
+
.filter((line) => line && line.trim().length > 0)
|
|
117
|
+
.map((line) => `- ${line}`);
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
summary,
|
|
121
|
+
body: detailLines.join("\n"),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function normalizeCommitHeader(message) {
|
|
126
|
+
if (typeof message !== "string") {
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const header = message.split("\n")[0] || "";
|
|
131
|
+
return header.trim();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function containsBreakingChange(message) {
|
|
135
|
+
if (typeof message !== "string") {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return /breaking change/i.test(message);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function determineReleaseTypeFromCommit(message) {
|
|
143
|
+
const header = normalizeCommitHeader(message).toLowerCase();
|
|
144
|
+
const hasBreaking = containsBreakingChange(message) || /!/.test(header.split(":")[0] || "");
|
|
145
|
+
|
|
146
|
+
if (hasBreaking) {
|
|
147
|
+
return "major";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const typeMatch = header.match(/^([a-z]+)(\(|:)/i);
|
|
151
|
+
const commitType = typeMatch ? typeMatch[1] : "";
|
|
152
|
+
|
|
153
|
+
if (commitType === "feat") {
|
|
154
|
+
return "minor";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (commitType === "fix" || commitType === "perf") {
|
|
158
|
+
return "patch";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!commitType && containsBreakingChange(message)) {
|
|
162
|
+
return "major";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return "patch";
|
|
166
|
+
}
|
package/commitlint.config.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
extends: ["@commitlint/config-conventional"],
|
|
3
|
-
};
|
|
1
|
+
export default {
|
|
2
|
+
extends: ["@commitlint/config-conventional"],
|
|
3
|
+
};
|
package/exec.js
ADDED
package/git-helpers.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { exec } from "./exec.js";
|
|
2
|
+
|
|
3
|
+
export async function hasStagedChanges() {
|
|
4
|
+
try {
|
|
5
|
+
const { stdout } = await exec("git status --porcelain");
|
|
6
|
+
return stdout
|
|
7
|
+
.split("\n")
|
|
8
|
+
.filter(Boolean)
|
|
9
|
+
.some((line) => {
|
|
10
|
+
const indexStatus = line[0];
|
|
11
|
+
return indexStatus && indexStatus !== " " && indexStatus !== "?";
|
|
12
|
+
});
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function hasWorkingChanges() {
|
|
19
|
+
try {
|
|
20
|
+
const { stdout } = await exec("git status --porcelain");
|
|
21
|
+
return stdout.split("\n").some((line) => line && line.trim().length > 0);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function workingTreeSummary(statusOutput) {
|
|
28
|
+
if (typeof statusOutput !== "string" || statusOutput.trim() === "") {
|
|
29
|
+
return {
|
|
30
|
+
hasChanges: false,
|
|
31
|
+
lines: [],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const lines = statusOutput
|
|
36
|
+
.split("\n")
|
|
37
|
+
.map((line) => line.replace(/\s+/g, " ").trim())
|
|
38
|
+
.filter(Boolean);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
hasChanges: lines.length > 0,
|
|
42
|
+
lines,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isTestFilePath(filePath) {
|
|
47
|
+
if (!filePath) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (/\.(test|spec)\.[^.]+$/i.test(filePath)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return /(\/|^)(__tests__|tests?|spec)\//i.test(filePath);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function extractPathFromStatusLine(line) {
|
|
59
|
+
if (!line || line.length < 4) {
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const rawPath = line.slice(3).trim();
|
|
64
|
+
if (!rawPath) {
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (rawPath.includes(" -> ")) {
|
|
69
|
+
const parts = rawPath.split(" -> ");
|
|
70
|
+
return parts[parts.length - 1].trim();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return rawPath;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function containsTestFilesInStatus(statusOutput) {
|
|
77
|
+
if (typeof statusOutput !== "string" || statusOutput.trim() === "") {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return statusOutput
|
|
82
|
+
.split("\n")
|
|
83
|
+
.map((line) => extractPathFromStatusLine(line))
|
|
84
|
+
.some((path) => isTestFilePath(path));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parseNameStatusLine(line) {
|
|
88
|
+
if (!line) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const parts = line.split("\t").map((part) => part.trim());
|
|
93
|
+
const status = parts[0];
|
|
94
|
+
if (!status) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (status.startsWith("R") || status.startsWith("C")) {
|
|
99
|
+
const originalPath = parts[1] || "";
|
|
100
|
+
const newPath = parts[2] || originalPath;
|
|
101
|
+
return {
|
|
102
|
+
status,
|
|
103
|
+
path: newPath,
|
|
104
|
+
originalPath,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
status,
|
|
110
|
+
path: parts[1] || "",
|
|
111
|
+
originalPath: "",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function getStagedChanges() {
|
|
116
|
+
try {
|
|
117
|
+
const { stdout } = await exec("git diff --cached --name-status");
|
|
118
|
+
return stdout
|
|
119
|
+
.split("\n")
|
|
120
|
+
.map((line) => parseNameStatusLine(line))
|
|
121
|
+
.filter(Boolean);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function hasTestChanges() {
|
|
128
|
+
try {
|
|
129
|
+
const { stdout } = await exec("git status --porcelain");
|
|
130
|
+
return containsTestFilesInStatus(stdout);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function getLastCommitMessage() {
|
|
137
|
+
try {
|
|
138
|
+
const { stdout } = await exec("git log -1 --pretty=%B");
|
|
139
|
+
return stdout.trim();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return "";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function getCurrentBranch() {
|
|
146
|
+
try {
|
|
147
|
+
const { stdout } = await exec("git rev-parse --abbrev-ref HEAD");
|
|
148
|
+
return stdout.trim();
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return "";
|
|
151
|
+
}
|
|
152
|
+
}
|