@programinglive/dev-workflow-mcp-server 1.7.0 → 1.7.9
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/.gitattributes +10 -0
- package/.github/workflows/github-release.yml +17 -0
- package/CHANGELOG.md +146 -1
- package/README.md +74 -2
- package/db/adapters/base.js +46 -0
- package/db/adapters/mysql.js +292 -0
- package/db/adapters/postgres.js +291 -0
- package/db/adapters/sqlite.js +343 -0
- package/db/index.js +52 -183
- package/docs/product/PRD.md +15 -5
- package/docs/release-notes/RELEASE_NOTES.md +129 -0
- package/package.json +6 -3
- package/scripts/release.cjs +11 -4
- package/tests/handlers.test.js +6 -1
- package/tools/handlers.js +22 -11
- package/vite.config.js +8 -0
- package/web/app/docs/page.tsx +81 -81
- package/workflow-state.js +32 -5
- package/.github/workflows/npm-publish.yml +0 -50
- package/web/tests/build.test.js +0 -69
package/db/index.js
CHANGED
|
@@ -1,204 +1,73 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
1
|
+
import { SqliteAdapter } from "./adapters/sqlite.js";
|
|
2
|
+
import { MysqlAdapter } from "./adapters/mysql.js";
|
|
3
|
+
import { PostgresAdapter } from "./adapters/postgres.js";
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
const DB_FILENAME = "dev-workflow.db";
|
|
9
|
-
const FREQUENCY_FORMATS = {
|
|
10
|
-
daily: "%Y-%m-%d",
|
|
11
|
-
monthly: "%Y-%m",
|
|
12
|
-
yearly: "%Y",
|
|
13
|
-
};
|
|
5
|
+
let adapterInstance = null;
|
|
14
6
|
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
while (projectRoot !== path.dirname(projectRoot)) {
|
|
19
|
-
if (existsSync(path.join(projectRoot, "package.json"))) break;
|
|
20
|
-
projectRoot = path.dirname(projectRoot);
|
|
7
|
+
export async function getAdapter() {
|
|
8
|
+
if (adapterInstance) {
|
|
9
|
+
return adapterInstance;
|
|
21
10
|
}
|
|
22
|
-
return path.join(projectRoot, ".state", DB_FILENAME);
|
|
23
|
-
}
|
|
24
11
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
12
|
+
const type = (process.env.DEV_WORKFLOW_DB_TYPE || "sqlite").toLowerCase();
|
|
13
|
+
const config = {
|
|
14
|
+
connectionUrl: process.env.DEV_WORKFLOW_DB_URL,
|
|
15
|
+
dbPath: process.env.DEV_WORKFLOW_DB_PATH // optional override for sqlite
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
switch (type) {
|
|
19
|
+
case "mysql":
|
|
20
|
+
adapterInstance = new MysqlAdapter(config);
|
|
21
|
+
break;
|
|
22
|
+
case "postgres":
|
|
23
|
+
case "postgresql":
|
|
24
|
+
adapterInstance = new PostgresAdapter(config);
|
|
25
|
+
break;
|
|
26
|
+
case "sqlite":
|
|
27
|
+
default:
|
|
28
|
+
adapterInstance = new SqliteAdapter(config);
|
|
29
|
+
break;
|
|
32
30
|
}
|
|
33
|
-
|
|
31
|
+
|
|
32
|
+
await adapterInstance.connect();
|
|
33
|
+
return adapterInstance;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
const createHistory = `
|
|
38
|
-
CREATE TABLE IF NOT EXISTS workflow_history (
|
|
39
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
40
|
-
user_id TEXT NOT NULL,
|
|
41
|
-
task_description TEXT NOT NULL,
|
|
42
|
-
task_type TEXT NOT NULL,
|
|
43
|
-
commit_message TEXT,
|
|
44
|
-
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
45
|
-
forced INTEGER DEFAULT 0,
|
|
46
|
-
force_reason TEXT,
|
|
47
|
-
dropped INTEGER DEFAULT 0,
|
|
48
|
-
drop_reason TEXT,
|
|
49
|
-
UNIQUE(user_id, timestamp, task_description)
|
|
50
|
-
)
|
|
51
|
-
`;
|
|
52
|
-
const createSummary = `
|
|
53
|
-
CREATE TABLE IF NOT EXISTS project_summary (
|
|
54
|
-
user_id TEXT PRIMARY KEY,
|
|
55
|
-
total_tasks INTEGER DEFAULT 0,
|
|
56
|
-
task_types TEXT DEFAULT '{}',
|
|
57
|
-
last_active DATETIME,
|
|
58
|
-
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
59
|
-
recent_tasks TEXT DEFAULT '[]'
|
|
60
|
-
)
|
|
61
|
-
`;
|
|
62
|
-
const idxHistory = `CREATE INDEX IF NOT EXISTS idx_workflow_history_user ON workflow_history(user_id)`;
|
|
63
|
-
const idxSummary = `CREATE INDEX IF NOT EXISTS idx_project_summary_user ON project_summary(user_id)`;
|
|
36
|
+
// Proxies for backward compatibility (though call sites need to handle Promises now)
|
|
64
37
|
|
|
65
|
-
|
|
66
|
-
db
|
|
67
|
-
db.
|
|
68
|
-
db.exec(idxSummary);
|
|
38
|
+
export async function insertHistoryEntry(userId, projectPath, entry) {
|
|
39
|
+
const db = await getAdapter();
|
|
40
|
+
await db.insertHistoryEntry(userId, projectPath, entry);
|
|
69
41
|
}
|
|
70
42
|
|
|
71
|
-
export function
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
(user_id, task_description, task_type, commit_message, timestamp, forced, force_reason, dropped, drop_reason)
|
|
75
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
76
|
-
`);
|
|
77
|
-
stmt.run(
|
|
78
|
-
userId,
|
|
79
|
-
entry.taskDescription,
|
|
80
|
-
entry.taskType,
|
|
81
|
-
entry.commitMessage,
|
|
82
|
-
entry.timestamp || new Date().toISOString(),
|
|
83
|
-
entry.forced ? 1 : 0,
|
|
84
|
-
entry.forceReason || null,
|
|
85
|
-
entry.dropped ? 1 : 0,
|
|
86
|
-
entry.dropReason || null
|
|
87
|
-
);
|
|
88
|
-
updateSummaryForUser(userId);
|
|
43
|
+
export async function updateSummaryForUser(userId, projectPath) {
|
|
44
|
+
const db = await getAdapter();
|
|
45
|
+
await db.updateSummaryForUser(userId, projectPath);
|
|
89
46
|
}
|
|
90
47
|
|
|
91
|
-
export function
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
FROM workflow_history
|
|
95
|
-
WHERE user_id = ?
|
|
96
|
-
ORDER BY timestamp DESC
|
|
97
|
-
LIMIT 20
|
|
98
|
-
`);
|
|
99
|
-
const rows = getHistory.all(userId);
|
|
100
|
-
|
|
101
|
-
const taskTypes = {};
|
|
102
|
-
for (const row of rows) {
|
|
103
|
-
taskTypes[row.task_type] = (taskTypes[row.task_type] || 0) + 1;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const recentTasks = rows.slice(0, 5).map(r => ({
|
|
107
|
-
description: r.task_description,
|
|
108
|
-
type: r.task_type,
|
|
109
|
-
timestamp: r.timestamp,
|
|
110
|
-
}));
|
|
111
|
-
|
|
112
|
-
const lastActive = rows[0]?.timestamp || null;
|
|
113
|
-
|
|
114
|
-
const upsert = getDb().prepare(`
|
|
115
|
-
INSERT OR REPLACE INTO project_summary
|
|
116
|
-
(user_id, total_tasks, task_types, last_active, recent_tasks, updated_at)
|
|
117
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
118
|
-
`);
|
|
119
|
-
upsert.run(
|
|
120
|
-
userId,
|
|
121
|
-
rows.length,
|
|
122
|
-
JSON.stringify(taskTypes),
|
|
123
|
-
lastActive,
|
|
124
|
-
JSON.stringify(recentTasks),
|
|
125
|
-
new Date().toISOString()
|
|
126
|
-
);
|
|
48
|
+
export async function getSummaryForUser(userId, projectPath) {
|
|
49
|
+
const db = await getAdapter();
|
|
50
|
+
return await db.getSummaryForUser(userId, projectPath);
|
|
127
51
|
}
|
|
128
52
|
|
|
129
|
-
export function
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
`);
|
|
133
|
-
const row = stmt.get(userId);
|
|
134
|
-
if (!row) return null;
|
|
135
|
-
return {
|
|
136
|
-
totalTasks: row.total_tasks,
|
|
137
|
-
taskTypes: JSON.parse(row.task_types),
|
|
138
|
-
lastActive: row.last_active,
|
|
139
|
-
recentTasks: JSON.parse(row.recent_tasks),
|
|
140
|
-
updatedAt: row.updated_at,
|
|
141
|
-
};
|
|
53
|
+
export async function getHistoryForUser(userId, projectPath, options) {
|
|
54
|
+
const db = await getAdapter();
|
|
55
|
+
return await db.getHistoryForUser(userId, projectPath, options);
|
|
142
56
|
}
|
|
143
57
|
|
|
144
|
-
function
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
if (startDate) {
|
|
148
|
-
clauses.push("DATE(timestamp) >= DATE(?)");
|
|
149
|
-
params.push(startDate);
|
|
150
|
-
}
|
|
151
|
-
if (endDate) {
|
|
152
|
-
clauses.push("DATE(timestamp) <= DATE(?)");
|
|
153
|
-
params.push(endDate);
|
|
154
|
-
}
|
|
155
|
-
return { clauses, params };
|
|
58
|
+
export async function getHistorySummary(userId, projectPath, options) {
|
|
59
|
+
const db = await getAdapter();
|
|
60
|
+
return await db.getHistorySummary(userId, projectPath, options);
|
|
156
61
|
}
|
|
157
62
|
|
|
158
|
-
export function
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
const sanitizedPageSize = Math.min(100, Math.max(1, Number.isFinite(Number(pageSize)) ? Math.floor(Number(pageSize)) : 20));
|
|
162
|
-
const offset = (sanitizedPage - 1) * sanitizedPageSize;
|
|
163
|
-
|
|
164
|
-
const { clauses, params } = buildHistoryFilters({ startDate, endDate });
|
|
165
|
-
const where = ["user_id = ?", ...clauses].join(" AND ");
|
|
166
|
-
const dbInstance = getDb();
|
|
167
|
-
|
|
168
|
-
const countStmt = dbInstance.prepare(`SELECT COUNT(*) as total FROM workflow_history WHERE ${where}`);
|
|
169
|
-
const total = countStmt.get(userId, ...params).total;
|
|
170
|
-
|
|
171
|
-
const rowsStmt = dbInstance.prepare(`
|
|
172
|
-
SELECT *
|
|
173
|
-
FROM workflow_history
|
|
174
|
-
WHERE ${where}
|
|
175
|
-
ORDER BY timestamp DESC
|
|
176
|
-
LIMIT ?
|
|
177
|
-
OFFSET ?
|
|
178
|
-
`);
|
|
179
|
-
const entries = rowsStmt.all(userId, ...params, sanitizedPageSize, offset);
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
entries,
|
|
183
|
-
total,
|
|
184
|
-
page: sanitizedPage,
|
|
185
|
-
pageSize: sanitizedPageSize,
|
|
186
|
-
};
|
|
63
|
+
export async function saveState(userId, projectPath, state) {
|
|
64
|
+
const db = await getAdapter();
|
|
65
|
+
await db.saveState(userId, projectPath, state);
|
|
187
66
|
}
|
|
188
67
|
|
|
189
|
-
export function
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
const { clauses, params } = buildHistoryFilters({ startDate, endDate });
|
|
193
|
-
const where = ["user_id = ?", ...clauses].join(" AND ");
|
|
194
|
-
const stmt = getDb().prepare(`
|
|
195
|
-
SELECT strftime('${format}', timestamp) AS period, COUNT(*) AS count
|
|
196
|
-
FROM workflow_history
|
|
197
|
-
WHERE ${where}
|
|
198
|
-
GROUP BY period
|
|
199
|
-
ORDER BY period DESC
|
|
200
|
-
`);
|
|
201
|
-
return stmt
|
|
202
|
-
.all(userId, ...params)
|
|
203
|
-
.map((row) => ({ period: row.period, count: row.count }));
|
|
68
|
+
export async function getState(userId, projectPath) {
|
|
69
|
+
const db = await getAdapter();
|
|
70
|
+
return await db.getState(userId, projectPath);
|
|
204
71
|
}
|
|
72
|
+
|
|
73
|
+
|
package/docs/product/PRD.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
## 1. Overview
|
|
4
4
|
- **Product**: Development Workflow MCP Server
|
|
5
5
|
- **Prepared by**: Cascade (AI assistant)
|
|
6
|
-
- **Date**: 2025-
|
|
7
|
-
- **Status**:
|
|
6
|
+
- **Date**: 2025-12-16
|
|
7
|
+
- **Status**: Live v1.6+
|
|
8
8
|
|
|
9
9
|
## 2. Problem Statement
|
|
10
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.
|
|
@@ -13,6 +13,7 @@ Developers need structured guidance to maintain disciplined workflows across pro
|
|
|
13
13
|
1. Enforce a consistent, auditable development workflow across teams.
|
|
14
14
|
2. Provide actionable guidance for each workflow phase within IDE-integrated environments.
|
|
15
15
|
3. Maintain per-project, per-user workflow state while supporting shared infrastructure (e.g., SQLite summaries, dashboard).
|
|
16
|
+
4. **Support flexible data persistence**: Enable usage of various database backends (MySQL, PostgreSQL, SQLite) to adapt to different hosting and enterprise environments.
|
|
16
17
|
|
|
17
18
|
## 4. Non-Goals
|
|
18
19
|
- Replacing full-featured project management tools.
|
|
@@ -28,19 +29,24 @@ Developers need structured guidance to maintain disciplined workflows across pro
|
|
|
28
29
|
1. As a developer, I want to be guided through the workflow steps so I can avoid skipping critical tasks.
|
|
29
30
|
2. As a team lead, I want an auditable history of workflow completion so I can ensure compliance.
|
|
30
31
|
3. As an integrator, I want a simple API (tools/prompts) so I can plug the MCP server into existing agent workflows.
|
|
32
|
+
4. As a devops engineer, I want to configure the server to use my existing database infrastructure (MySQL/PostgreSQL) instead of local files.
|
|
31
33
|
|
|
32
34
|
## 7. Functional Requirements
|
|
33
35
|
- Provide MCP tools covering the full workflow lifecycle (start task, fix, test, document, commit, release, complete).
|
|
34
36
|
- Persist workflow state per project and per user, with compatibility handling for legacy locations.
|
|
35
|
-
- Offer aggregated project summaries (JSON artifacts,
|
|
37
|
+
- Offer aggregated project summaries (JSON artifacts, Database sync, optional web dashboard).
|
|
36
38
|
- Guard release commands to ensure prerequisites are satisfied.
|
|
37
39
|
- Support `run_full_workflow` for scripted execution of all steps with validation.
|
|
38
40
|
- Web dashboard displays version dynamically from `package.json` via `/api/version` endpoint to stay in sync after releases.
|
|
41
|
+
- **Multi-Database Support**: Abstract the storage layer to support:
|
|
42
|
+
- SQLite (default/local)
|
|
43
|
+
- MySQL
|
|
44
|
+
- PostgreSQL
|
|
39
45
|
|
|
40
46
|
## 8. Technical Considerations
|
|
41
47
|
- Node.js (ESM) codebase leveraging `@modelcontextprotocol/sdk`.
|
|
42
48
|
- File-based state stored under `.state/users/<user>/workflow-state.json` with compatibility symlinks.
|
|
43
|
-
-
|
|
49
|
+
- **Database Abstraction**: Implement an Adapter/Repository pattern or use an ORM (e.g., Knex, Prisma, Drizzle) to support multiple SQL dialects.
|
|
44
50
|
- Vite build pipeline for distribution bundle; source usage recommended for MCP.
|
|
45
51
|
- Windows compatibility requirements (Python, Build Tools for native modules).
|
|
46
52
|
- **Deployment options**: Local development, Netlify (static), Plesk (full-featured).
|
|
@@ -49,17 +55,21 @@ Developers need structured guidance to maintain disciplined workflows across pro
|
|
|
49
55
|
- ≥90% of workflow operations executed via MCP tools (vs. manual overrides).
|
|
50
56
|
- Reduction in release guard violations to <5% of attempts.
|
|
51
57
|
- Positive qualitative feedback on guidance clarity from pilot teams.
|
|
58
|
+
- Seamless switching between SQLite, MySQL, and PostgreSQL via configuration.
|
|
52
59
|
|
|
53
60
|
## 10. Milestones
|
|
54
61
|
1. **MVP**: Core workflow tools, state persistence, basic history (complete).
|
|
55
62
|
2. **Enhancements**: Improved project root detection, compatibility mirrors, `run_full_workflow` automation (complete).
|
|
56
63
|
3. **v1.5.0**: Netlify deployment support for static web dashboard (complete).
|
|
57
|
-
4. **
|
|
64
|
+
4. **v1.6.0+**: Dark/Light mode, UI Improvements (complete).
|
|
65
|
+
5. **Multi-Database Support**: Refactor storage layer to support MySQL and PostgreSQL (Upcoming).
|
|
66
|
+
6. **Future**: Expanded dashboard analytics, customizable workflows, richer integrations.
|
|
58
67
|
|
|
59
68
|
## 11. Risks & Mitigations
|
|
60
69
|
- **Risk**: Workflow feels overly prescriptive. **Mitigation**: Provide escape hatches (`force_complete_task`, `drop_task`) with audit trails.
|
|
61
70
|
- **Risk**: State corruption due to concurrent access. **Mitigation**: File locking and defensive JSON parsing (existing code handles gracefully, but monitor).
|
|
62
71
|
- **Risk**: Integration friction with IDE clients. **Mitigation**: Maintain detailed README instructions and example configurations.
|
|
72
|
+
- **Risk**: Complicated setup for users not needing external DBs. **Mitigation**: Keep SQLite/File-system as the default zero-config option.
|
|
63
73
|
|
|
64
74
|
## 12. Open Questions
|
|
65
75
|
- Should additional workflow phases (e.g., design review) be supported?
|
|
@@ -2,6 +2,135 @@
|
|
|
2
2
|
|
|
3
3
|
Authoritative history of user-visible changes for the Development Workflow MCP Server. Each entry highlights the improvements that shipped with the tagged version.
|
|
4
4
|
|
|
5
|
+
| Version | Date | Notes |
|
|
6
|
+
|---------|------|-------|
|
|
7
|
+
| 1.7.8 | 2025-12-16 | **ci:** remove all token config for pure OIDC publishing (d4759cc) |
|
|
8
|
+
| 1.7.7 | 2025-12-16 | **ci:** remove duplicates and apply robust trusted publishing config (eedb661) |
|
|
9
|
+
| 1.7.6 | 2025-12-16 | **ci:** restore registry config with token fallback for Trusted Publishing (542615c) |
|
|
10
|
+
| 1.7.5 | 2025-12-16 | **ci:** switch to tokenless trusted publishing (fdfc4a9) |
|
|
11
|
+
| 1.7.4 | 2025-12-16 | **ci:** add id-token permission for npm provenance (8930338) |
|
|
12
|
+
| 1.7.3 | 2025-12-16 | **ci:** add web dependencies installation step (7d5dc62) |
|
|
13
|
+
| 1.7.2 | 2025-12-16 | **ci:** consolidate release and publish workflows (98e5b31) |
|
|
14
|
+
| 1.7.1 | 2025-12-16 | build errors and warnings (dc24831) |
|
|
15
|
+
| 1.7.0 | 2025-12-16 | See CHANGELOG for details. |
|
|
16
|
+
| 1.6.3 | 2025-12-06 | update release notes for 1.6.2 (f0c2d49) |
|
|
17
|
+
| 1.6.2 | 2025-12-06 | update release notest for 1.6.1 (72a9284) |
|
|
18
|
+
| 1.6.1 | 2025-12-06 | add table header to release notes for release script compatibility (44e47fc) |
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## 1.7.8 – 🐛 Bug Fixes
|
|
33
|
+
|
|
34
|
+
Released on **2025-12-16**.
|
|
35
|
+
|
|
36
|
+
- **ci:** remove all token config for pure OIDC publishing (d4759cc)
|
|
37
|
+
|
|
38
|
+
## 1.7.7 – 🐛 Bug Fixes
|
|
39
|
+
|
|
40
|
+
Released on **2025-12-16**.
|
|
41
|
+
|
|
42
|
+
- **ci:** remove duplicates and apply robust trusted publishing config (eedb661)
|
|
43
|
+
|
|
44
|
+
## 1.7.6 – 🐛 Bug Fixes
|
|
45
|
+
|
|
46
|
+
Released on **2025-12-16**.
|
|
47
|
+
|
|
48
|
+
- **ci:** restore registry config with token fallback for Trusted Publishing (542615c)
|
|
49
|
+
|
|
50
|
+
## 1.7.5 – 🐛 Bug Fixes
|
|
51
|
+
|
|
52
|
+
Released on **2025-12-16**.
|
|
53
|
+
|
|
54
|
+
- **ci:** switch to tokenless trusted publishing (fdfc4a9)
|
|
55
|
+
|
|
56
|
+
## 1.7.4 – 🐛 Bug Fixes
|
|
57
|
+
|
|
58
|
+
Released on **2025-12-16**.
|
|
59
|
+
|
|
60
|
+
- **ci:** add id-token permission for npm provenance (8930338)
|
|
61
|
+
|
|
62
|
+
## 1.7.3 – 🐛 Bug Fixes
|
|
63
|
+
|
|
64
|
+
Released on **2025-12-16**.
|
|
65
|
+
|
|
66
|
+
- **ci:** add web dependencies installation step (7d5dc62)
|
|
67
|
+
|
|
68
|
+
## 1.7.2 – 🐛 Bug Fixes
|
|
69
|
+
|
|
70
|
+
Released on **2025-12-16**.
|
|
71
|
+
|
|
72
|
+
- **ci:** consolidate release and publish workflows (98e5b31)
|
|
73
|
+
|
|
74
|
+
## 1.7.1 – 🐛 Bug Fixes
|
|
75
|
+
|
|
76
|
+
Released on **2025-12-16**.
|
|
77
|
+
|
|
78
|
+
- build errors and warnings (dc24831)
|
|
79
|
+
- suppress remaining build warnings (f7d9b2d)
|
|
80
|
+
- handle null CustPL in birthday-customers.blade.php (ERP-19) (71b8f7e)
|
|
81
|
+
- integrate database adapters into workflow state (370cbb4)
|
|
82
|
+
- update PRD and add configuration guide (1dfccf5)
|
|
83
|
+
- update release notes for 1.6.3 (afb9fb9)
|
|
84
|
+
- add mysql2 and pg dependencies (94423d7)
|
|
85
|
+
- **release:** null 🚀 (93a17e2)
|
|
86
|
+
- **release:** null 🚀 (033097e)
|
|
87
|
+
- **release:** null 🚀 (91c800a)
|
|
88
|
+
- **release:** null 🚀 (f0958c4)
|
|
89
|
+
- **release:** null 🚀 (ac3cc6e)
|
|
90
|
+
- **release:** null 🚀 (f3f9a00)
|
|
91
|
+
- **release:** null 🚀 (bcf7d95)
|
|
92
|
+
- **release:** null 🚀 (74df6d6)
|
|
93
|
+
- **release:** null 🚀 (0d9dc4d)
|
|
94
|
+
- **release:** null 🚀 (9667012)
|
|
95
|
+
- **release:** null 🚀 (db25d0c)
|
|
96
|
+
- **release:** null 🚀 (50d5353)
|
|
97
|
+
- **release:** null 🚀 (ee0a540)
|
|
98
|
+
- **release:** null 🚀 (92558af)
|
|
99
|
+
- **release:** null 🚀 (cd851ac)
|
|
100
|
+
- **release:** null 🚀 (49ec562)
|
|
101
|
+
- **release:** null 🚀 (bcc7a3c)
|
|
102
|
+
- implement database adapters for sqlite, mysql, postgres (8e7416a)
|
|
103
|
+
- persist active workflow state to database (d29546d)
|
|
104
|
+
|
|
105
|
+
## 1.7.0
|
|
106
|
+
|
|
107
|
+
Released on **2025-12-16**.
|
|
108
|
+
|
|
109
|
+
- See CHANGELOG for details.
|
|
110
|
+
|
|
111
|
+
## 1.6.3 – 📝 Documentation
|
|
112
|
+
|
|
113
|
+
Released on **2025-12-06**.
|
|
114
|
+
|
|
115
|
+
- update release notes for 1.6.2 (f0c2d49)
|
|
116
|
+
- add light mode support to docs page (490cf22)
|
|
117
|
+
|
|
118
|
+
## 1.6.2 – 📝 Documentation
|
|
119
|
+
|
|
120
|
+
Released on **2025-12-06**.
|
|
121
|
+
|
|
122
|
+
- update release notest for 1.6.1 (72a9284)
|
|
123
|
+
- update create_documentation test to verify PRD check and remove incompatible jest test (a7f2b28)
|
|
124
|
+
- add PRD file existence check to create_documentation workflow step (62cdee3)
|
|
125
|
+
- add PRD file existence check to create_documentation workflow step (6c51012)
|
|
126
|
+
|
|
127
|
+
## 1.6.1 – 🐛 Bug Fixes
|
|
128
|
+
|
|
129
|
+
Released on **2025-12-06**.
|
|
130
|
+
|
|
131
|
+
- add table header to release notes for release script compatibility (44e47fc)
|
|
132
|
+
- add responsive vertical monitor support and dark/light mode toggle (5565765)
|
|
133
|
+
|
|
5
134
|
## Unreleased
|
|
6
135
|
|
|
7
136
|
## 1.5.11 — 2025-12-05
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@programinglive/dev-workflow-mcp-server",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.9",
|
|
4
4
|
"description": "MCP server to enforce development workflow discipline",
|
|
5
5
|
"homepage": "https://devworkflow.programinglive.com/",
|
|
6
6
|
"mcpName": "io.github.programinglive/dev-workflow-mcp-server",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"release:major": "node scripts/release.cjs major",
|
|
26
26
|
"release:minor": "node scripts/release.cjs minor",
|
|
27
27
|
"release:patch": "node scripts/release.cjs patch",
|
|
28
|
-
"web": "npm start --prefix web"
|
|
28
|
+
"web": "npm start --prefix web",
|
|
29
|
+
"release:first": "node scripts/release.cjs --first-release"
|
|
29
30
|
},
|
|
30
31
|
"keywords": [
|
|
31
32
|
"mcp",
|
|
@@ -45,6 +46,8 @@
|
|
|
45
46
|
"esbuild": "^0.25.0",
|
|
46
47
|
"get-port": "^7.1.0",
|
|
47
48
|
"marked": "^12.0.2",
|
|
49
|
+
"mysql2": "^3.15.3",
|
|
50
|
+
"pg": "^8.16.3",
|
|
48
51
|
"vite": "^7.1.12"
|
|
49
52
|
},
|
|
50
53
|
"publishConfig": {
|
|
@@ -53,7 +56,7 @@
|
|
|
53
56
|
"devDependencies": {
|
|
54
57
|
"@commitlint/cli": "^20.2.0",
|
|
55
58
|
"@commitlint/config-conventional": "^20.2.0",
|
|
56
|
-
"@programinglive/commiter": "^1.2.
|
|
59
|
+
"@programinglive/commiter": "^1.2.16",
|
|
57
60
|
"autoprefixer": "^10.4.22",
|
|
58
61
|
"husky": "^9.1.7",
|
|
59
62
|
"postcss": "^8.5.6",
|
package/scripts/release.cjs
CHANGED
|
@@ -26,18 +26,18 @@ const SEMVER_REGEX = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
|
26
26
|
const LOCKFILE_PREFERENCES = [
|
|
27
27
|
{ manager: 'pnpm', file: 'pnpm-lock.yaml' },
|
|
28
28
|
{ manager: 'yarn', file: 'yarn.lock' },
|
|
29
|
-
{ manager: 'bun', file: 'bun.
|
|
29
|
+
{ manager: 'bun', file: 'bun.lock' },
|
|
30
30
|
{ manager: 'npm', file: 'package-lock.json' }
|
|
31
31
|
];
|
|
32
32
|
|
|
33
33
|
function getCliArguments(argv = process.argv) {
|
|
34
34
|
const rawArgs = argv.slice(2);
|
|
35
35
|
if (rawArgs.length === 0) {
|
|
36
|
-
return { releaseType:
|
|
36
|
+
return { releaseType: 'patch', extraArgs: [] };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
if (rawArgs[0].startsWith('-')) {
|
|
40
|
-
return { releaseType:
|
|
40
|
+
return { releaseType: 'patch', extraArgs: rawArgs };
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const [firstArg, ...rest] = rawArgs;
|
|
@@ -60,7 +60,14 @@ function getNpmRunArgument(env = process.env) {
|
|
|
60
60
|
|
|
61
61
|
function buildStandardVersionArgs({ releaseType, extraArgs }) {
|
|
62
62
|
const args = [];
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
// Handle --first-release flag
|
|
65
|
+
const isFirstRelease = Array.isArray(extraArgs) && extraArgs.includes('--first-release');
|
|
66
|
+
if (isFirstRelease) {
|
|
67
|
+
args.push('--release-as', '0.0.1');
|
|
68
|
+
// Remove --first-release from extraArgs to avoid passing it to standard-version
|
|
69
|
+
extraArgs = extraArgs.filter(arg => arg !== '--first-release');
|
|
70
|
+
} else if (releaseType) {
|
|
64
71
|
const normalized = releaseType.trim();
|
|
65
72
|
const isValid = VALID_RELEASE_TYPES.has(normalized) || SEMVER_REGEX.test(normalized);
|
|
66
73
|
if (!isValid) {
|
package/tests/handlers.test.js
CHANGED
|
@@ -79,7 +79,7 @@ test("run_tests guidance mentions core documentation types", async () => {
|
|
|
79
79
|
});
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
test("create_documentation
|
|
82
|
+
test("create_documentation verifies PRD file exists", async () => {
|
|
83
83
|
await withWorkflowState(async (workflowState) => {
|
|
84
84
|
workflowState.state.testsPassed = true;
|
|
85
85
|
await workflowState.save();
|
|
@@ -105,6 +105,7 @@ test("create_documentation accepts PRD and records created/updated message", asy
|
|
|
105
105
|
utils,
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
// Should succeed since PRD.md exists in the project
|
|
108
109
|
assert.equal(workflowState.state.documentationCreated, true);
|
|
109
110
|
assert.equal(workflowState.state.documentationType, "PRD");
|
|
110
111
|
assert.equal(
|
|
@@ -115,6 +116,10 @@ test("create_documentation accepts PRD and records created/updated message", asy
|
|
|
115
116
|
response.content[0].text.includes("Documentation created/updated!"),
|
|
116
117
|
"create_documentation response should mention created/updated",
|
|
117
118
|
);
|
|
119
|
+
assert.ok(
|
|
120
|
+
response.content[0].text.includes("PRD verified"),
|
|
121
|
+
"create_documentation response should verify PRD file",
|
|
122
|
+
);
|
|
118
123
|
});
|
|
119
124
|
});
|
|
120
125
|
|
package/tools/handlers.js
CHANGED
|
@@ -23,7 +23,7 @@ async function resetToCommitIfWorkingChanges(workflowState, git) {
|
|
|
23
23
|
|
|
24
24
|
const hasWorkingChanges = await git.hasWorkingChanges();
|
|
25
25
|
const hasStagedChanges = await git.hasStagedChanges();
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
if (!hasWorkingChanges && !hasStagedChanges) {
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
@@ -105,9 +105,8 @@ function getContinueGuidance(status) {
|
|
|
105
105
|
: "Run your automated tests and record the outcome with 'run_tests'. Fix any failures before moving forward.";
|
|
106
106
|
case "documentation":
|
|
107
107
|
return status.testsSkipped
|
|
108
|
-
? `Document the change (including manual QA for skipped tests) with 'create_documentation'. Reason recorded: ${
|
|
109
|
-
|
|
110
|
-
}.`
|
|
108
|
+
? `Document the change (including manual QA for skipped tests) with 'create_documentation'. Reason recorded: ${status.testsSkippedReason || "(none provided)"
|
|
109
|
+
}.`
|
|
111
110
|
: "Capture your updates with 'create_documentation' (provide the documentation type and a short summary).";
|
|
112
111
|
case "ready":
|
|
113
112
|
return "Run 'check_ready_to_commit' to verify all prerequisite steps before committing.";
|
|
@@ -355,6 +354,16 @@ async function handleCreateDocumentation(args, workflowState) {
|
|
|
355
354
|
return textResponse("⚠️ Please ensure tests are passing first! Run 'run_tests' with passed=true.");
|
|
356
355
|
}
|
|
357
356
|
|
|
357
|
+
// Check if PRD exists in docs folder
|
|
358
|
+
const prdPath = path.join(__dirname, "..", "docs", "product", "PRD.md");
|
|
359
|
+
const prdExists = existsSync(prdPath);
|
|
360
|
+
|
|
361
|
+
if (!prdExists) {
|
|
362
|
+
return textResponse(
|
|
363
|
+
`⚠️ PRD file not found!\n\nExpected location: docs/product/PRD.md\n\nPlease create or update the PRD before marking documentation as complete.`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
358
367
|
workflowState.state.documentationCreated = true;
|
|
359
368
|
workflowState.state.documentationType = args.documentationType;
|
|
360
369
|
workflowState.state.documentationSummary = args.summary;
|
|
@@ -368,7 +377,7 @@ async function handleCreateDocumentation(args, workflowState) {
|
|
|
368
377
|
await workflowState.save();
|
|
369
378
|
|
|
370
379
|
return textResponse(
|
|
371
|
-
`✅ Documentation created/updated!\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/update documentation\n5. ⏳ Run 'check_ready_to_commit' to verify\n6. ⏳ Run 'commit_and_push'\n7. ⏳ Run 'perform_release'\n8. ⏳ Mark as complete with 'complete_task'`
|
|
380
|
+
`✅ Documentation created/updated!\n\nType: ${args.documentationType}\nSummary: ${args.summary}\n✅ PRD verified: docs/product/PRD.md exists\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/update documentation\n5. ⏳ Run 'check_ready_to_commit' to verify\n6. ⏳ Run 'commit_and_push'\n7. ⏳ Run 'perform_release'\n8. ⏳ Mark as complete with 'complete_task'`
|
|
372
381
|
);
|
|
373
382
|
}
|
|
374
383
|
|
|
@@ -443,8 +452,7 @@ async function handleCommitAndPush(args, context) {
|
|
|
443
452
|
await workflowState.save();
|
|
444
453
|
|
|
445
454
|
return textResponse(
|
|
446
|
-
`ℹ️ No changes detected. Assuming commit and push already completed.\nLast commit: ${
|
|
447
|
-
effectiveCommitMessage || "(unavailable)"
|
|
455
|
+
`ℹ️ No changes detected. Assuming commit and push already completed.\nLast commit: ${effectiveCommitMessage || "(unavailable)"
|
|
448
456
|
}\n\nNext: Run 'perform_release' to handle the release and push tags.`
|
|
449
457
|
);
|
|
450
458
|
}
|
|
@@ -510,8 +518,7 @@ async function handleCommitAndPush(args, context) {
|
|
|
510
518
|
await workflowState.save();
|
|
511
519
|
|
|
512
520
|
return textResponse(
|
|
513
|
-
`✅ Commit and push completed!\n\nCommit message: ${generatedSummary}\nPushed to: ${
|
|
514
|
-
branchForPush || "(default upstream)"
|
|
521
|
+
`✅ Commit and push completed!\n\nCommit message: ${generatedSummary}\nPushed to: ${branchForPush || "(default upstream)"
|
|
515
522
|
}\n\nNext: Run 'perform_release' to handle the release and push tags.`
|
|
516
523
|
);
|
|
517
524
|
}
|
|
@@ -772,7 +779,11 @@ async function handleProjectSummaryData(workflowState) {
|
|
|
772
779
|
async function handleProjectSummaryDb(workflowState) {
|
|
773
780
|
const { getSummaryForUser } = await import("../db/index.js");
|
|
774
781
|
const userId = process.env.DEV_WORKFLOW_USER_ID || "default";
|
|
775
|
-
|
|
782
|
+
|
|
783
|
+
// Calculate project root from state file path: <root>/.state/users/<user>/workflow-state.json
|
|
784
|
+
const projectPath = path.dirname(path.dirname(path.dirname(path.dirname(workflowState.stateFile))));
|
|
785
|
+
|
|
786
|
+
const summary = await getSummaryForUser(userId, projectPath);
|
|
776
787
|
if (!summary) {
|
|
777
788
|
return textResponse("📜 No project summary in database yet.");
|
|
778
789
|
}
|
|
@@ -791,7 +802,7 @@ async function handleRerunWorkflow(workflowState) {
|
|
|
791
802
|
|
|
792
803
|
// Reset all progress flags but preserve history
|
|
793
804
|
workflowState.reset();
|
|
794
|
-
|
|
805
|
+
|
|
795
806
|
// Restore task context after reset
|
|
796
807
|
workflowState.state.currentPhase = "coding";
|
|
797
808
|
workflowState.state.taskDescription = currentDescription;
|