@programinglive/dev-workflow-mcp-server 1.1.11 → 1.1.16
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/workflows/npm-publish.yml +47 -0
- package/CHANGELOG.md +37 -0
- package/README.md +52 -0
- package/docs/product/PRD.md +64 -0
- package/docs/web-dashboard.md +163 -0
- package/index.js +69 -4
- package/package.json +5 -1
- package/release-wrapper.js +26 -1
- package/tests/exec.test.js +7 -4
- package/tests/release-wrapper.test.js +11 -3
- package/tests/workflowState.test.js +54 -2
- package/web/index.html +520 -80
- package/web/server.js +34 -1
- package/workflow-state.js +86 -23
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
name: Publish package
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
env:
|
|
17
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout repository
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Setup Node.js
|
|
24
|
+
uses: actions/setup-node@v4
|
|
25
|
+
with:
|
|
26
|
+
node-version: "20"
|
|
27
|
+
cache: "npm"
|
|
28
|
+
registry-url: https://registry.npmjs.org
|
|
29
|
+
scope: "@programinglive"
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: npm ci
|
|
33
|
+
|
|
34
|
+
- name: Build package
|
|
35
|
+
run: npm run build
|
|
36
|
+
|
|
37
|
+
- name: Verify tag matches package version
|
|
38
|
+
run: |
|
|
39
|
+
EXPECTED_TAG="${GITHUB_REF#refs/tags/}"
|
|
40
|
+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
41
|
+
if [ "v${PACKAGE_VERSION}" != "${EXPECTED_TAG}" ]; then
|
|
42
|
+
echo "Package version ${PACKAGE_VERSION} does not match tag ${EXPECTED_TAG}"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
- name: Publish to npm
|
|
47
|
+
run: npm publish --provenance
|
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.1.16](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.1.15...v1.1.16) (2025-10-31)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### 🧹 Chores
|
|
9
|
+
|
|
10
|
+
* align npm provenance metadata ([459572e](https://github.com/programinglive/dev-workflow-mcp-server/commit/459572e07efa340ef87cc679535fb24413d8c997))
|
|
11
|
+
|
|
12
|
+
### [1.1.15](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.1.14...v1.1.15) (2025-10-31)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### 📝 Documentation
|
|
16
|
+
|
|
17
|
+
* add npm publish workflow automation ([c923734](https://github.com/programinglive/dev-workflow-mcp-server/commit/c923734ab4399afbcb0166abcd16cd01c3e50c0e))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### 👷 Continuous Integration
|
|
21
|
+
|
|
22
|
+
* **npm:** build + provenance in npm-publish workflow ([5ae04db](https://github.com/programinglive/dev-workflow-mcp-server/commit/5ae04db33e33fe64c33d8eac08db70e98754ea64))
|
|
23
|
+
|
|
24
|
+
### [1.1.14](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.1.13...v1.1.14) (2025-10-31)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### 🐛 Bug Fixes
|
|
28
|
+
|
|
29
|
+
* improve workflow state project root resolution ([a575e24](https://github.com/programinglive/dev-workflow-mcp-server/commit/a575e2404d26e550c8e52e4a524a75a3fa64752d))
|
|
30
|
+
|
|
31
|
+
### [1.1.13](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.1.12...v1.1.13) (2025-10-30)
|
|
32
|
+
|
|
33
|
+
### [1.1.12](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.1.11...v1.1.12) (2025-10-30)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### ✨ Features
|
|
37
|
+
|
|
38
|
+
* **cli:** add 'call' subcommand to invoke MCP tools directly (e.g., node index.js call drop_task) ([c9b8fae](https://github.com/programinglive/dev-workflow-mcp-server/commit/c9b8fae7e24e0915d857135d03fa969b10dcac67))
|
|
39
|
+
* **web:** docs modal, docs API, navbar redesign ([902242b](https://github.com/programinglive/dev-workflow-mcp-server/commit/902242b1318b01f8f62ef7c4c0f6347cb8517617))
|
|
40
|
+
* **web:** embed docs modal and redesign dashboard ([d2cf2fb](https://github.com/programinglive/dev-workflow-mcp-server/commit/d2cf2fb4ae90e61b20c72ed26f25dc0e5119247a))
|
|
41
|
+
|
|
5
42
|
### [1.1.11](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.1.10...v1.1.11) (2025-10-30)
|
|
6
43
|
|
|
7
44
|
|
package/README.md
CHANGED
|
@@ -34,6 +34,26 @@ cd dev-workflow-mcp-server
|
|
|
34
34
|
npm install
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
> **Windows prerequisites:** Installing dependencies from source compiles native modules such as `better-sqlite3`. Make sure Python 3 (added to PATH) and the Visual Studio Build Tools “Desktop development with C++” workload are installed before running `npm install`. Without them, npm will fail with a “need python” or build error.
|
|
38
|
+
|
|
39
|
+
### Option 3: Install on Plesk Hosting
|
|
40
|
+
|
|
41
|
+
Plesk supports Node.js applications through its Node.js extension. To deploy the MCP server on a Plesk subscription:
|
|
42
|
+
|
|
43
|
+
1. **Enable Node.js support** – Ensure the Plesk administrator has installed the Node.js extension and enabled SSH access for your subscription.
|
|
44
|
+
2. **Upload the project** – Either clone the repository or upload an archive into the directory you will run it from (e.g., `httpdocs/dev-workflow-mcp-server`). From SSH you can run:
|
|
45
|
+
```bash
|
|
46
|
+
cd httpdocs
|
|
47
|
+
git clone https://github.com/programinglive/dev-workflow-mcp-server.git
|
|
48
|
+
cd dev-workflow-mcp-server
|
|
49
|
+
```
|
|
50
|
+
3. **Install dependencies** – In Plesk’s **Node.js** panel use “NPM install” (or run `npm install --production` over SSH). Linux hosts already ship the Python/build toolchain required for `better-sqlite3`; if your plan uses a Windows host, install Python 3 and the Visual Studio Build Tools beforehand or ask your provider to enable them.
|
|
51
|
+
4. **Define environment variables** – In the Node.js panel add any environment variables you need (for example `DEV_WORKFLOW_USER_ID` or `DEV_WORKFLOW_STATE_FILE`). This keeps state files outside the web root if desired.
|
|
52
|
+
5. **Configure the application** – Set **Application startup file** to `index.js` and **Application mode** to `production`. Plesk will run the server with `node index.js`.
|
|
53
|
+
6. **Start/Restart the app** – Click “Restart App” so Plesk launches the MCP server with the new configuration. When you update the code, rerun “NPM install” and restart.
|
|
54
|
+
|
|
55
|
+
> **Tip:** The MCP server communicates over stdio. If you only need it as a CLI tool, you can also run `npx @programinglive/dev-workflow-mcp-server` directly in an SSH session without keeping it running under the Node.js panel.
|
|
56
|
+
|
|
37
57
|
#### Two Usage Modes
|
|
38
58
|
|
|
39
59
|
- **Local (source)**: Point your MCP client to `index.js`. This runs directly from source and requires no build step. Recommended for MCP usage.
|
|
@@ -142,6 +162,25 @@ This project includes a Vite-based build system for creating optimized distribut
|
|
|
142
162
|
- `npm run build` - Bundle the source into `dist/index.mjs` for distribution
|
|
143
163
|
- `npm run dev` - Run in development mode with file watching
|
|
144
164
|
- `npm run local` - Alias for running from source (same as `npm start`)
|
|
165
|
+
- `npm run web` - Launch the lightweight workflow dashboard for browsing task history (see [Web Dashboard docs](./docs/web-dashboard.md))
|
|
166
|
+
|
|
167
|
+
#### `npm run web`
|
|
168
|
+
|
|
169
|
+
This command starts the dashboard defined in `web/server.js`, giving you a quick view of workflow history and summary statistics.
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm run web
|
|
173
|
+
# 🌐 Dev Workflow Dashboard running at http://localhost:3111
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- **Default port:** 3111 (or the next free port if occupied). Override with `DEV_WORKFLOW_WEB_PORT`.
|
|
177
|
+
- **Query parameter:** `?user=<id>` lets you inspect another user’s history (defaults to `default`).
|
|
178
|
+
- **API endpoints:**
|
|
179
|
+
- `GET /api/summary?user=<id>` → overall stats for the user.
|
|
180
|
+
- `GET /api/history?user=<id>&page=1&pageSize=20&startDate=YYYY-MM-DD&endDate=YYYY-MM-DD` → paginated task history.
|
|
181
|
+
- `GET /api/history-summary?user=<id>&frequency=daily|monthly|yearly` → aggregated counts over time.
|
|
182
|
+
|
|
183
|
+
Open `http://localhost:3111` in a browser to view the dashboard UI (`web/index.html`).
|
|
145
184
|
|
|
146
185
|
### Build Output
|
|
147
186
|
|
|
@@ -249,6 +288,19 @@ If any requirements are missing, the guard exits with guidance to return to the
|
|
|
249
288
|
1. Use `perform_release {"command":"patch"}` (or `minor`/`major`) via the MCP client.
|
|
250
289
|
2. The guard runs automatically, verifies the workflow state, and records the release before letting you finish with `complete_task`.
|
|
251
290
|
|
|
291
|
+
### Automated npm Publishing
|
|
292
|
+
|
|
293
|
+
This repository ships with `.github/workflows/npm-publish.yml`, which publishes the package to npm whenever a git tag matching `v*` is pushed (for example, `v1.1.14`). To enable the workflow:
|
|
294
|
+
|
|
295
|
+
1. Create an npm automation token with publish rights (`npm token create --read-only false`).
|
|
296
|
+
2. In the repository settings, add a secret named **`NPM_TOKEN`** containing that token.
|
|
297
|
+
3. Ensure your release process pushes tags after running `npm run release:<type>` so the workflow triggers.
|
|
298
|
+
4. Confirm `npm run build` succeeds locally; the workflow runs the build before publishing so broken bundles block the release.
|
|
299
|
+
5. GitHub provenance is enabled via `npm publish --provenance`. Leave GitHub Actions' default OIDC permissions enabled so the job can request an ID token.
|
|
300
|
+
6. Keep the `repository.url` field in `package.json` pointing at this GitHub repo. Provenance validation fails if it does not match the repository that built the package.
|
|
301
|
+
|
|
302
|
+
The workflow verifies that the tag version matches `package.json` before publishing and fails fast if they diverge.
|
|
303
|
+
|
|
252
304
|
## 🛠️ Available Tools
|
|
253
305
|
|
|
254
306
|
### Tool Argument Requirements
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Product Requirements Document (PRD)
|
|
2
|
+
|
|
3
|
+
## 1. Overview
|
|
4
|
+
- **Product**: Development Workflow MCP Server
|
|
5
|
+
- **Prepared by**: Cascade (AI assistant)
|
|
6
|
+
- **Date**: 2025-10-31
|
|
7
|
+
- **Status**: Draft v0.1
|
|
8
|
+
|
|
9
|
+
## 2. Problem Statement
|
|
10
|
+
Developers need structured guidance to maintain disciplined workflows across projects. Existing tooling lacks enforcement of sequential best practices (testing, documentation, release hygiene) and offers limited visibility into historical task execution.
|
|
11
|
+
|
|
12
|
+
## 3. Goals & Objectives
|
|
13
|
+
1. Enforce a consistent, auditable development workflow across teams.
|
|
14
|
+
2. Provide actionable guidance for each workflow phase within IDE-integrated environments.
|
|
15
|
+
3. Maintain per-project, per-user workflow state while supporting shared infrastructure (e.g., SQLite summaries, dashboard).
|
|
16
|
+
|
|
17
|
+
## 4. Non-Goals
|
|
18
|
+
- Replacing full-featured project management tools.
|
|
19
|
+
- Implementing source control hosting or CI/CD pipelines.
|
|
20
|
+
- Providing opinionated task estimation or prioritization features.
|
|
21
|
+
|
|
22
|
+
## 5. Target Users & Personas
|
|
23
|
+
- **Individual developers** seeking workflow discipline.
|
|
24
|
+
- **Team leads** requiring enforced process compliance.
|
|
25
|
+
- **Tool integrators** embedding MCP capabilities into IDEs or agents.
|
|
26
|
+
|
|
27
|
+
## 6. User Stories
|
|
28
|
+
1. As a developer, I want to be guided through the workflow steps so I can avoid skipping critical tasks.
|
|
29
|
+
2. As a team lead, I want an auditable history of workflow completion so I can ensure compliance.
|
|
30
|
+
3. As an integrator, I want a simple API (tools/prompts) so I can plug the MCP server into existing agent workflows.
|
|
31
|
+
|
|
32
|
+
## 7. Functional Requirements
|
|
33
|
+
- Provide MCP tools covering the full workflow lifecycle (start task, fix, test, document, commit, release, complete).
|
|
34
|
+
- Persist workflow state per project and per user, with compatibility handling for legacy locations.
|
|
35
|
+
- Offer aggregated project summaries (JSON artifacts, SQLite sync, optional web dashboard).
|
|
36
|
+
- Guard release commands to ensure prerequisites are satisfied.
|
|
37
|
+
- Support `run_full_workflow` for scripted execution of all steps with validation.
|
|
38
|
+
|
|
39
|
+
## 8. Technical Considerations
|
|
40
|
+
- Node.js (ESM) codebase leveraging `@modelcontextprotocol/sdk`.
|
|
41
|
+
- File-based state stored under `.state/users/<user>/workflow-state.json` with compatibility symlinks.
|
|
42
|
+
- Optional SQLite integration for history and summary caching.
|
|
43
|
+
- Vite build pipeline for distribution bundle; source usage recommended for MCP.
|
|
44
|
+
- Windows compatibility requirements (Python, Build Tools for native modules).
|
|
45
|
+
|
|
46
|
+
## 9. Success Metrics
|
|
47
|
+
- ≥90% of workflow operations executed via MCP tools (vs. manual overrides).
|
|
48
|
+
- Reduction in release guard violations to <5% of attempts.
|
|
49
|
+
- Positive qualitative feedback on guidance clarity from pilot teams.
|
|
50
|
+
|
|
51
|
+
## 10. Milestones
|
|
52
|
+
1. **MVP**: Core workflow tools, state persistence, basic history (complete).
|
|
53
|
+
2. **Enhancements**: Improved project root detection, compatibility mirrors, `run_full_workflow` automation (in progress).
|
|
54
|
+
3. **Future**: Expanded dashboard analytics, customizable workflows, richer integrations.
|
|
55
|
+
|
|
56
|
+
## 11. Risks & Mitigations
|
|
57
|
+
- **Risk**: Workflow feels overly prescriptive. **Mitigation**: Provide escape hatches (`force_complete_task`, `drop_task`) with audit trails.
|
|
58
|
+
- **Risk**: State corruption due to concurrent access. **Mitigation**: File locking and defensive JSON parsing (existing code handles gracefully, but monitor).
|
|
59
|
+
- **Risk**: Integration friction with IDE clients. **Mitigation**: Maintain detailed README instructions and example configurations.
|
|
60
|
+
|
|
61
|
+
## 12. Open Questions
|
|
62
|
+
- Should additional workflow phases (e.g., design review) be supported?
|
|
63
|
+
- How should multi-branch workflows be represented in history summaries?
|
|
64
|
+
- What telemetry or analytics are acceptable within privacy constraints?
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Dev Workflow Web Dashboard
|
|
2
|
+
|
|
3
|
+
Learn how to start and use the Dev Workflow dashboard included with the MCP server. The dashboard exposes project history, summaries, and aggregates through a small Node.js service.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Before launching the dashboard, confirm the following:
|
|
8
|
+
|
|
9
|
+
- Node.js 18+ installed (same requirement as the MCP server)
|
|
10
|
+
- Project dependencies installed with `npm install`
|
|
11
|
+
- Workflow history stored under `.state/` (created automatically when using the MCP server)
|
|
12
|
+
|
|
13
|
+
> **Plesk users:** follow the [Plesk installation guide](../README.md#option-3-install-on-plesk-hosting) first and ensure SSH access is enabled.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Task history browser** – Paginated list of completed workflow entries.
|
|
18
|
+
- **Summary stats** – Counts of tasks by type and recent activity timeline.
|
|
19
|
+
- **Aggregated trends** – Daily, monthly, or yearly breakdowns of workflow activity.
|
|
20
|
+
- **Multi-user support** – Switch between user identifiers recorded in the `.state` directory.
|
|
21
|
+
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
Launch the dashboard from your project directory:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm run web
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
When the server is ready you will see:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
🌐 Dev Workflow Dashboard running at http://localhost:3111
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Open the URL in your browser. If port 3111 is in use, the script automatically selects a nearby free port.
|
|
37
|
+
|
|
38
|
+
### Choosing a Port
|
|
39
|
+
|
|
40
|
+
Set `DEV_WORKFLOW_WEB_PORT` to force a specific port:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
DEV_WORKFLOW_WEB_PORT=5000 npm run web
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Selecting a User
|
|
47
|
+
|
|
48
|
+
The dashboard defaults to the `default` user. Append `?user=<USER_ID>` to view another user’s data. User IDs map to the subdirectories under `.state/users/`.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
http://localhost:3111/?user=my-teammate
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Navigation
|
|
55
|
+
|
|
56
|
+
The HTML frontend lives in `web/index.html`. The default UI includes:
|
|
57
|
+
|
|
58
|
+
- **Summary cards** – Total tasks completed and last active date.
|
|
59
|
+
- **Recent tasks table** – Paginated entries with task descriptions and timestamps.
|
|
60
|
+
- **Charts** (if script extensions are enabled) – Visual trends per frequency selection.
|
|
61
|
+
|
|
62
|
+
## API Reference
|
|
63
|
+
|
|
64
|
+
Interact with the dashboard programmatically using the JSON endpoints served by `web/server.js`.
|
|
65
|
+
|
|
66
|
+
### `GET /api/summary`
|
|
67
|
+
|
|
68
|
+
Returns aggregate statistics for a user.
|
|
69
|
+
|
|
70
|
+
**Query parameters**
|
|
71
|
+
|
|
72
|
+
- `user` *(optional, string)* – User identifier (default `default`).
|
|
73
|
+
|
|
74
|
+
**Response**
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"summary": {
|
|
79
|
+
"totalTasks": 12,
|
|
80
|
+
"taskTypes": { "feature": 8, "bugfix": 3, "refactor": 1 },
|
|
81
|
+
"lastActive": "2025-10-01T09:25:41.123Z",
|
|
82
|
+
"recentTasks": [
|
|
83
|
+
{ "description": "feat: add billing webhooks", "type": "feature", "timestamp": "2025-10-01T09:25:41.123Z" }
|
|
84
|
+
],
|
|
85
|
+
"updatedAt": "2025-10-01T09:30:00.000Z"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `GET /api/history`
|
|
91
|
+
|
|
92
|
+
Returns paginated task history.
|
|
93
|
+
|
|
94
|
+
**Query parameters**
|
|
95
|
+
|
|
96
|
+
- `user` *(optional, string)* – User identifier (default `default`).
|
|
97
|
+
- `page` *(optional, number)* – Page number (default `1`).
|
|
98
|
+
- `pageSize` *(optional, number)* – Items per page (default `20`, max `100`).
|
|
99
|
+
- `startDate` *(optional, string)* – Filter by ISO date (inclusive).
|
|
100
|
+
- `endDate` *(optional, string)* – Filter by ISO date (inclusive).
|
|
101
|
+
|
|
102
|
+
**Response**
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"entries": [
|
|
107
|
+
{
|
|
108
|
+
"id": 42,
|
|
109
|
+
"user_id": "default",
|
|
110
|
+
"task_description": "feat: add reporting endpoint",
|
|
111
|
+
"task_type": "feature",
|
|
112
|
+
"commit_message": "feat: add reporting endpoint",
|
|
113
|
+
"timestamp": "2025-09-30T09:18:00.000Z"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"total": 42,
|
|
117
|
+
"page": 1,
|
|
118
|
+
"pageSize": 20
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `GET /api/history-summary`
|
|
123
|
+
|
|
124
|
+
Returns an array of task counts grouped by period.
|
|
125
|
+
|
|
126
|
+
**Query parameters**
|
|
127
|
+
|
|
128
|
+
- `user` *(optional, string)* – User identifier (default `default`).
|
|
129
|
+
- `frequency` *(optional, string)* – `daily`, `monthly`, or `yearly` (default `daily`).
|
|
130
|
+
- `startDate`, `endDate` *(optional, string)* – Limit range by ISO dates.
|
|
131
|
+
|
|
132
|
+
**Response**
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"summary": [
|
|
137
|
+
{ "period": "2025-09-30", "count": 3 },
|
|
138
|
+
{ "period": "2025-09-29", "count": 1 }
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
| Variable | Description | Default |
|
|
146
|
+
| --- | --- | --- |
|
|
147
|
+
| `DEV_WORKFLOW_WEB_PORT` | Port for the dashboard | `3111` or next available |
|
|
148
|
+
| `DEV_WORKFLOW_USER_ID` | User identifier when launching via MCP | auto-generated |
|
|
149
|
+
| `DEV_WORKFLOW_STATE_FILE` | Overrides path to workflow state JSON | auto-detected |
|
|
150
|
+
|
|
151
|
+
## Troubleshooting
|
|
152
|
+
|
|
153
|
+
| Symptom | Fix |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| Server fails with `better-sqlite3` build error | Install Python 3 and build tools (Windows) or ensure SQLite build dependencies exist (Linux/macOS). |
|
|
156
|
+
| Dashboard shows empty data | Confirm the MCP server has generated `.state/users/<user>/workflow-state.json` and that the chosen `user` matches. |
|
|
157
|
+
| Port already in use | Set `DEV_WORKFLOW_WEB_PORT` to a free port before running `npm run web`. |
|
|
158
|
+
|
|
159
|
+
## Related Topics
|
|
160
|
+
|
|
161
|
+
- [Main README](../README.md)
|
|
162
|
+
- [Workflow State Internals](./state.md) *(coming soon)*
|
|
163
|
+
- [CLI Tools](../README.md#🛠️-available-tools)
|
package/index.js
CHANGED
|
@@ -199,9 +199,74 @@ export async function main() {
|
|
|
199
199
|
console.error("Dev Workflow MCP Server running on stdio");
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
// --- Lightweight CLI: dev-workflow-mcp call <toolName> [--args '<json>'] ---
|
|
203
|
+
async function cliMain(argv) {
|
|
204
|
+
const [, , subcommand, toolName, ...rest] = argv;
|
|
205
|
+
if (subcommand !== "call") return false;
|
|
206
|
+
|
|
207
|
+
if (!toolName || typeof toolName !== "string" || toolName.trim() === "") {
|
|
208
|
+
console.error("Usage: dev-workflow-mcp call <toolName> [--args '<json>']");
|
|
209
|
+
process.exit(2);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Parse --args '<json>' if provided
|
|
213
|
+
let rawArgs = undefined;
|
|
214
|
+
for (let i = 0; i < rest.length; i++) {
|
|
215
|
+
if (rest[i] === "--args") {
|
|
216
|
+
rawArgs = rest[i + 1];
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
// Initialize workflow state similar to the server path
|
|
223
|
+
workflowState = new WorkflowState();
|
|
224
|
+
await workflowState.load();
|
|
225
|
+
await workflowState.ensurePrimaryFile();
|
|
226
|
+
|
|
227
|
+
const request = {
|
|
228
|
+
params: {
|
|
229
|
+
name: toolName,
|
|
230
|
+
arguments: rawArgs ?? {},
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const response = await handleToolCall({
|
|
235
|
+
request,
|
|
236
|
+
normalizeRequestArgs,
|
|
237
|
+
workflowState,
|
|
238
|
+
exec,
|
|
239
|
+
git: {
|
|
240
|
+
hasWorkingChanges,
|
|
241
|
+
hasTestChanges,
|
|
242
|
+
hasStagedChanges,
|
|
243
|
+
getStagedChanges,
|
|
244
|
+
getCurrentBranch,
|
|
245
|
+
getLastCommitMessage,
|
|
246
|
+
workingTreeSummary,
|
|
247
|
+
},
|
|
248
|
+
utils: { shellEscape, determineReleaseTypeFromCommit, createCommitMessageParts },
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const text = response?.content?.[0]?.text || "";
|
|
252
|
+
console.log(text);
|
|
253
|
+
const isError = /^\s*[⚠️❌]/u.test(text);
|
|
254
|
+
process.exit(isError ? 1 : 0);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error("CLI error:", error.message || String(error));
|
|
205
257
|
process.exit(1);
|
|
206
|
-
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const isDirectRun = process.argv[1] === __filename;
|
|
262
|
+
if (isDirectRun) {
|
|
263
|
+
// If invoked with a CLI subcommand, run it; otherwise start MCP server
|
|
264
|
+
if (process.argv[2] === "call") {
|
|
265
|
+
cliMain(process.argv);
|
|
266
|
+
} else {
|
|
267
|
+
main().catch((error) => {
|
|
268
|
+
console.error("Server error:", error);
|
|
269
|
+
process.exit(1);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
207
272
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@programinglive/dev-workflow-mcp-server",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.16",
|
|
4
4
|
"description": "MCP server to enforce development workflow discipline",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
],
|
|
31
31
|
"author": "",
|
|
32
32
|
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/programinglive/dev-workflow-mcp-server.git"
|
|
36
|
+
},
|
|
33
37
|
"dependencies": {
|
|
34
38
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
35
39
|
"better-sqlite3": "^11.8.0",
|
package/release-wrapper.js
CHANGED
|
@@ -22,7 +22,32 @@ async function main() {
|
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// Prefer state under INIT_CWD to support per-project runs and tests.
|
|
26
|
+
// If DEV_WORKFLOW_USER_ID is set and a user-scoped file exists, use it; otherwise fall back to legacy path.
|
|
27
|
+
const baseStateDir = path.join(projectRoot, '.state');
|
|
28
|
+
const legacyStatePath = path.join(baseStateDir, 'workflow-state.json');
|
|
29
|
+
let resolvedUserId = (process.env.DEV_WORKFLOW_USER_ID || '').trim();
|
|
30
|
+
if (!resolvedUserId) {
|
|
31
|
+
try {
|
|
32
|
+
const fileUserId = (await fs.readFile(path.join(baseStateDir, 'user-id'), 'utf8')).trim();
|
|
33
|
+
if (fileUserId) {
|
|
34
|
+
resolvedUserId = fileUserId;
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
// no user-id file; legacy layout likely
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const userStatePath = resolvedUserId ? path.join(baseStateDir, 'users', resolvedUserId, 'workflow-state.json') : null;
|
|
41
|
+
|
|
42
|
+
let statePath = legacyStatePath;
|
|
43
|
+
if (userStatePath) {
|
|
44
|
+
try {
|
|
45
|
+
await fs.access(userStatePath);
|
|
46
|
+
statePath = userStatePath;
|
|
47
|
+
} catch {
|
|
48
|
+
// fall back to legacy path
|
|
49
|
+
}
|
|
50
|
+
}
|
|
26
51
|
let state;
|
|
27
52
|
|
|
28
53
|
try {
|
package/tests/exec.test.js
CHANGED
|
@@ -6,17 +6,20 @@ import { mkdtemp, rm } from 'fs/promises';
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
|
|
8
8
|
test('exec runs commands successfully', async () => {
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const echoCommand = process.platform === 'win32' ? 'echo hello world' : 'echo "hello world"';
|
|
10
|
+
const { stdout } = await exec(echoCommand);
|
|
11
|
+
const normalized = stdout.trim().replace(/^"(.*)"$/, '$1');
|
|
12
|
+
assert.strictEqual(normalized, 'hello world');
|
|
11
13
|
});
|
|
12
14
|
|
|
13
15
|
test('exec respects explicit cwd option', async () => {
|
|
14
16
|
const tempDir = await mkdtemp(path.join(tmpdir(), 'mcp-test-'));
|
|
15
17
|
|
|
16
18
|
try {
|
|
17
|
-
const
|
|
19
|
+
const dirCommand = process.platform === 'win32' ? 'cd' : 'pwd';
|
|
20
|
+
const { stdout } = await exec(dirCommand, { cwd: tempDir });
|
|
18
21
|
// Just verify it runs in the specified directory (path may vary by platform)
|
|
19
|
-
assert(stdout.trim().includes('mcp-test-'));
|
|
22
|
+
assert(stdout.trim().toLowerCase().includes('mcp-test-'));
|
|
20
23
|
} finally {
|
|
21
24
|
await rm(tempDir, { recursive: true, force: true });
|
|
22
25
|
}
|
|
@@ -3,10 +3,14 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
import { mkdtemp, rm, writeFile, readFile, mkdir } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
6
7
|
import { spawn } from "node:child_process";
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
const releaseWrapperUrl = new URL("../release-wrapper.js", import.meta.url);
|
|
12
|
+
const RELEASE_WRAPPER = fileURLToPath(releaseWrapperUrl);
|
|
13
|
+
const PROJECT_ROOT = path.dirname(RELEASE_WRAPPER);
|
|
10
14
|
|
|
11
15
|
async function runWrapper({ state, args = ["patch"], env = {} }) {
|
|
12
16
|
const dir = await mkdtemp(path.join(tmpdir(), "release-wrapper-test-"));
|
|
@@ -22,14 +26,18 @@ async function runWrapper({ state, args = ["patch"], env = {} }) {
|
|
|
22
26
|
...env,
|
|
23
27
|
};
|
|
24
28
|
|
|
29
|
+
const nodeBinary = process.platform === "win32" ? "node" : process.execPath || "node";
|
|
30
|
+
const spawnShell = process.platform === "win32";
|
|
31
|
+
|
|
25
32
|
return await new Promise((resolve) => {
|
|
26
33
|
const child = spawn(
|
|
27
|
-
|
|
34
|
+
nodeBinary,
|
|
28
35
|
[RELEASE_WRAPPER, ...args],
|
|
29
36
|
{
|
|
30
37
|
cwd: PROJECT_ROOT,
|
|
31
38
|
env: childEnv,
|
|
32
39
|
stdio: ["ignore", "pipe", "pipe"],
|
|
40
|
+
shell: spawnShell,
|
|
33
41
|
}
|
|
34
42
|
);
|
|
35
43
|
|
|
@@ -36,6 +36,58 @@ async function withWorkflowState(callback) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
test("resolveStateFile prefers current working directory when multiple project markers exist", async () => {
|
|
40
|
+
const tempRoot = await mkdtemp(path.join(tmpdir(), "workflow-prefers-cwd-"));
|
|
41
|
+
const moduleDir = path.join(tempRoot, "module");
|
|
42
|
+
const projectA = path.join(tempRoot, "project-a");
|
|
43
|
+
const projectB = path.join(tempRoot, "project-b");
|
|
44
|
+
const projectBNested = path.join(projectB, "sub", "app");
|
|
45
|
+
|
|
46
|
+
await mkdir(moduleDir, { recursive: true });
|
|
47
|
+
await mkdir(projectA, { recursive: true });
|
|
48
|
+
await mkdir(projectBNested, { recursive: true });
|
|
49
|
+
|
|
50
|
+
const sourceModulePath = fileURLToPath(new URL("../workflow-state.js", import.meta.url));
|
|
51
|
+
await copyFile(sourceModulePath, path.join(moduleDir, "workflow-state.js"));
|
|
52
|
+
|
|
53
|
+
await writeFile(path.join(moduleDir, "package.json"), JSON.stringify({ name: "module" }));
|
|
54
|
+
await writeFile(path.join(projectA, "package.json"), JSON.stringify({ name: "project-a" }));
|
|
55
|
+
await writeFile(path.join(projectB, "package.json"), JSON.stringify({ name: "project-b" }));
|
|
56
|
+
|
|
57
|
+
const moduleUrl = pathToFileURL(path.join(moduleDir, "workflow-state.js"));
|
|
58
|
+
const { WorkflowState: TempWorkflowState } = await import(`${moduleUrl.href}?preferCwd=${Date.now()}`);
|
|
59
|
+
|
|
60
|
+
const originalCwd = process.cwd();
|
|
61
|
+
const initWasSet = Object.prototype.hasOwnProperty.call(process.env, "INIT_CWD");
|
|
62
|
+
const originalInitCwd = process.env.INIT_CWD;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
process.chdir(projectBNested);
|
|
66
|
+
process.env.INIT_CWD = projectA;
|
|
67
|
+
|
|
68
|
+
const state = new TempWorkflowState();
|
|
69
|
+
const resolvedStateFile = state.stateFile;
|
|
70
|
+
|
|
71
|
+
assert.ok(
|
|
72
|
+
resolvedStateFile.startsWith(path.join(projectB, ".state", "users")),
|
|
73
|
+
"State file should resolve under the current working directory's project"
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
assert.ok(
|
|
77
|
+
!resolvedStateFile.startsWith(path.join(projectA, ".state")),
|
|
78
|
+
"State file should not use INIT_CWD project when cwd has its own markers"
|
|
79
|
+
);
|
|
80
|
+
} finally {
|
|
81
|
+
process.chdir(originalCwd);
|
|
82
|
+
if (initWasSet) {
|
|
83
|
+
process.env.INIT_CWD = originalInitCwd;
|
|
84
|
+
} else {
|
|
85
|
+
delete process.env.INIT_CWD;
|
|
86
|
+
}
|
|
87
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
39
91
|
test("WorkflowState mirrors state file to compatibility locations", async () => {
|
|
40
92
|
const tempRoot = await mkdtemp(path.join(tmpdir(), "workflow-compat-"));
|
|
41
93
|
const moduleDir = path.join(tempRoot, "module");
|
|
@@ -94,8 +146,8 @@ test("WorkflowState avoids filesystem root when no project markers found", async
|
|
|
94
146
|
const resolvedTempRoot = await realpath(tempRoot);
|
|
95
147
|
|
|
96
148
|
assert.ok(
|
|
97
|
-
state.stateFile.startsWith(
|
|
98
|
-
"state file should
|
|
149
|
+
state.stateFile.startsWith(resolvedTempRoot),
|
|
150
|
+
"state file should remain inside the temporary root when no project markers exist"
|
|
99
151
|
);
|
|
100
152
|
assert.ok(
|
|
101
153
|
!state.stateFile.startsWith(path.join(path.sep, ".state")),
|