@programinglive/dev-workflow-mcp-server 1.7.0 โ†’ 1.7.10

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 ADDED
@@ -0,0 +1,10 @@
1
+ # Enforce LF for all text files (suppresses warnings on Windows)
2
+ * text=auto eol=lf
3
+
4
+ # Binary files
5
+ *.png binary
6
+ *.jpg binary
7
+ *.jpeg binary
8
+ *.gif binary
9
+ *.ico binary
10
+ *.webp binary
@@ -12,6 +12,7 @@ on:
12
12
 
13
13
  permissions:
14
14
  contents: write
15
+ id-token: write
15
16
 
16
17
  jobs:
17
18
  release:
@@ -28,6 +29,7 @@ jobs:
28
29
  uses: actions/setup-node@v4
29
30
  with:
30
31
  node-version: "20"
32
+ registry-url: "https://registry.npmjs.org"
31
33
 
32
34
  - name: Determine version
33
35
  id: version
@@ -47,6 +49,21 @@ jobs:
47
49
  echo "tag=$TAG" >> "$GITHUB_OUTPUT"
48
50
  echo "version=$VERSION" >> "$GITHUB_OUTPUT"
49
51
 
52
+ - name: Update npm
53
+ run: npm install -g npm@latest
54
+
55
+ - name: Install dependencies
56
+ run: npm ci
57
+
58
+ - name: Install web dependencies
59
+ run: npm ci --prefix web
60
+
61
+ - name: Build package
62
+ run: npm run build
63
+
64
+ - name: Publish to npm
65
+ run: npm publish --provenance
66
+
50
67
  - name: Generate release notes
51
68
  run: |
52
69
  echo "Extracting release notes for version ${{ steps.version.outputs.version }}"
package/CHANGELOG.md CHANGED
@@ -2,7 +2,164 @@
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.7.0](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.6.0...v1.7.0) (2025-12-06)
5
+ ### [1.7.10](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.9...v1.7.10) (2025-12-16)
6
+
7
+
8
+ ### ๐Ÿงน Chores
9
+
10
+ * update release notes and finalize ci config ([92ac78a](https://github.com/programinglive/dev-workflow-mcp-server/commit/92ac78aeefa1ced8e8a076185bb6689463a46e80))
11
+
12
+
13
+ ### ๐Ÿ“ Documentation
14
+
15
+ * update PRD and release notes for v1.7.9 ([024b3c2](https://github.com/programinglive/dev-workflow-mcp-server/commit/024b3c25cd1703622a5f86d6eb09055ea93678d2))
16
+
17
+ ### [1.7.9](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.8...v1.7.9) (2025-12-16)
18
+
19
+
20
+ ### ๐Ÿ› Bug Fixes
21
+
22
+ * **ci:** apply official npm trusted publishing config ([9256583](https://github.com/programinglive/dev-workflow-mcp-server/commit/925658336c093c4e2fcfca30dfe469262d67c02d))
23
+
24
+ ### [1.7.8](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.7...v1.7.8) (2025-12-16)
25
+
26
+
27
+ ### ๐Ÿ› Bug Fixes
28
+
29
+ * **ci:** remove all token config for pure OIDC publishing ([d4759cc](https://github.com/programinglive/dev-workflow-mcp-server/commit/d4759ccf492396c22fa5aa0fdfbd624c571f7f42))
30
+
31
+ ### [1.7.7](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.6...v1.7.7) (2025-12-16)
32
+
33
+
34
+ ### ๐Ÿ› Bug Fixes
35
+
36
+ * **ci:** remove duplicates and apply robust trusted publishing config ([eedb661](https://github.com/programinglive/dev-workflow-mcp-server/commit/eedb661fce4461de68b636e8e7b60b4f0cf465b3))
37
+
38
+ ### [1.7.6](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.5...v1.7.6) (2025-12-16)
39
+
40
+
41
+ ### ๐Ÿ› Bug Fixes
42
+
43
+ * **ci:** restore registry config with token fallback for Trusted Publishing ([542615c](https://github.com/programinglive/dev-workflow-mcp-server/commit/542615cb95fe1aaac5f1a5be18694a7577b8eb86))
44
+
45
+ ### [1.7.5](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.4...v1.7.5) (2025-12-16)
46
+
47
+
48
+ ### ๐Ÿ› Bug Fixes
49
+
50
+ * **ci:** switch to tokenless trusted publishing ([fdfc4a9](https://github.com/programinglive/dev-workflow-mcp-server/commit/fdfc4a9a86485ee60f0f650e16aa9195671d9ab3))
51
+
52
+ ### [1.7.4](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.3...v1.7.4) (2025-12-16)
53
+
54
+
55
+ ### ๐Ÿ› Bug Fixes
56
+
57
+ * **ci:** add id-token permission for npm provenance ([8930338](https://github.com/programinglive/dev-workflow-mcp-server/commit/8930338aaf90616f1c741c4634ac8e96d3c43fb7))
58
+
59
+ ### [1.7.3](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.2...v1.7.3) (2025-12-16)
60
+
61
+
62
+ ### ๐Ÿ› Bug Fixes
63
+
64
+ * **ci:** add web dependencies installation step ([7d5dc62](https://github.com/programinglive/dev-workflow-mcp-server/commit/7d5dc62a213d3105c2329a1fb810cc6260fe4815))
65
+
66
+ ### [1.7.2](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.1...v1.7.2) (2025-12-16)
67
+
68
+
69
+ ### ๐Ÿ› Bug Fixes
70
+
71
+ * **ci:** consolidate release and publish workflows ([98e5b31](https://github.com/programinglive/dev-workflow-mcp-server/commit/98e5b31fb64e39b1c846ce594215fb38ab13bf19))
72
+
73
+ ### [1.7.1](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.0...v1.7.1) (2025-12-16)
74
+
75
+
76
+ ### ๐Ÿ› Bug Fixes
77
+
78
+ * build errors and warnings ([dc24831](https://github.com/programinglive/dev-workflow-mcp-server/commit/dc2483188a58d932590503ec2058c51e3de78d0b))
79
+ * suppress remaining build warnings ([f7d9b2d](https://github.com/programinglive/dev-workflow-mcp-server/commit/f7d9b2d9fb695e6699d65456747c21a301b62fee))
80
+
81
+ ## [1.7.0](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.6.3...v1.7.0) (2025-12-16)
82
+
83
+
84
+ ### ๐Ÿ› Bug Fixes
85
+
86
+ * handle null CustPL in birthday-customers.blade.php (ERP-19) ([71b8f7e](https://github.com/programinglive/dev-workflow-mcp-server/commit/71b8f7ebbf83266008e28446630be97ae24e1d64))
87
+
88
+
89
+ ### โ™ป๏ธ Refactors
90
+
91
+ * integrate database adapters into workflow state ([370cbb4](https://github.com/programinglive/dev-workflow-mcp-server/commit/370cbb416b49c758239177296978f7deba5e49e2))
92
+
93
+
94
+ ### ๐Ÿ“ Documentation
95
+
96
+ * update PRD and add configuration guide ([1dfccf5](https://github.com/programinglive/dev-workflow-mcp-server/commit/1dfccf5e37eee2994acaf9067320cf13a64fd515))
97
+ * update release notes for 1.6.3 ([afb9fb9](https://github.com/programinglive/dev-workflow-mcp-server/commit/afb9fb9b4c14be0a9f8ecfada3bb72ca75b7f116))
98
+
99
+
100
+ ### ๐Ÿงน Chores
101
+
102
+ * add mysql2 and pg dependencies ([94423d7](https://github.com/programinglive/dev-workflow-mcp-server/commit/94423d72aab3cb1c2ee47d9a4995e05cceda1f11))
103
+ * **release:** null ๐Ÿš€ ([93a17e2](https://github.com/programinglive/dev-workflow-mcp-server/commit/93a17e26f1995fda34d2ae40f61f39b697dc03bf))
104
+ * **release:** null ๐Ÿš€ ([033097e](https://github.com/programinglive/dev-workflow-mcp-server/commit/033097eecd04b2dd3f9299d497ebe71d32f470b9))
105
+ * **release:** null ๐Ÿš€ ([91c800a](https://github.com/programinglive/dev-workflow-mcp-server/commit/91c800a584f4b967fe60b8058684d5224543f26b))
106
+ * **release:** null ๐Ÿš€ ([f0958c4](https://github.com/programinglive/dev-workflow-mcp-server/commit/f0958c40f187c0bd4ff77cb2f628fb2338dafa6b))
107
+ * **release:** null ๐Ÿš€ ([ac3cc6e](https://github.com/programinglive/dev-workflow-mcp-server/commit/ac3cc6e8a5dea2d119c072fbb8029947e234643c))
108
+ * **release:** null ๐Ÿš€ ([f3f9a00](https://github.com/programinglive/dev-workflow-mcp-server/commit/f3f9a00059c435d2e18940f8a4b41e166a779ff3))
109
+ * **release:** null ๐Ÿš€ ([bcf7d95](https://github.com/programinglive/dev-workflow-mcp-server/commit/bcf7d9560af34b0a257bfe6566a0cd1572ebb5d1))
110
+ * **release:** null ๐Ÿš€ ([74df6d6](https://github.com/programinglive/dev-workflow-mcp-server/commit/74df6d6d2972ab2c4702da7e773ab651a41daaa3))
111
+ * **release:** null ๐Ÿš€ ([0d9dc4d](https://github.com/programinglive/dev-workflow-mcp-server/commit/0d9dc4d3ba751bbe69aecf6221eb79ea2f54ccd9))
112
+ * **release:** null ๐Ÿš€ ([9667012](https://github.com/programinglive/dev-workflow-mcp-server/commit/966701266afec03b6f621670d2217b98c1a85eb1))
113
+ * **release:** null ๐Ÿš€ ([db25d0c](https://github.com/programinglive/dev-workflow-mcp-server/commit/db25d0c9117e00d20584a5a175a727f16219bfe9))
114
+ * **release:** null ๐Ÿš€ ([50d5353](https://github.com/programinglive/dev-workflow-mcp-server/commit/50d535373b080de2b765b0ac9c32ce6f7d4a55f7))
115
+ * **release:** null ๐Ÿš€ ([ee0a540](https://github.com/programinglive/dev-workflow-mcp-server/commit/ee0a54097cc481c2aeb74ca99a41b34914ab540f))
116
+ * **release:** null ๐Ÿš€ ([92558af](https://github.com/programinglive/dev-workflow-mcp-server/commit/92558af18f41169760dfbe069f32c9e4969b656d))
117
+ * **release:** null ๐Ÿš€ ([cd851ac](https://github.com/programinglive/dev-workflow-mcp-server/commit/cd851ac6d3ddd91286b63a83d05a1a5a5a17aee6))
118
+ * **release:** null ๐Ÿš€ ([49ec562](https://github.com/programinglive/dev-workflow-mcp-server/commit/49ec5629b8308dc9ed19672b07966c3711748817))
119
+ * **release:** null ๐Ÿš€ ([bcc7a3c](https://github.com/programinglive/dev-workflow-mcp-server/commit/bcc7a3c1a554dbea2dce16585ef926bed925e062))
120
+
121
+
122
+ ### โœจ Features
123
+
124
+ * implement database adapters for sqlite, mysql, postgres ([8e7416a](https://github.com/programinglive/dev-workflow-mcp-server/commit/8e7416a467286eaa26051e8e0b4af5f350295087))
125
+ * persist active workflow state to database ([d29546d](https://github.com/programinglive/dev-workflow-mcp-server/commit/d29546dee52940170a9a855146ca870fa078cf66))
126
+
127
+ ### [1.6.3](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.6.2...v1.6.3) (2025-12-06)
128
+
129
+
130
+ ### ๐Ÿ“ Documentation
131
+
132
+ * update release notes for 1.6.2 ([f0c2d49](https://github.com/programinglive/dev-workflow-mcp-server/commit/f0c2d4965f3e08afdaccae5eb49f7ea7d21a6ed3))
133
+
134
+
135
+ ### ๐Ÿ› Bug Fixes
136
+
137
+ * add light mode support to docs page ([490cf22](https://github.com/programinglive/dev-workflow-mcp-server/commit/490cf2210253a7498cbb3d3a33d1c329672a2dfa))
138
+
139
+ ### [1.6.2](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.6.1...v1.6.2) (2025-12-06)
140
+
141
+
142
+ ### ๐Ÿ“ Documentation
143
+
144
+ * update release notest for 1.6.1 ([72a9284](https://github.com/programinglive/dev-workflow-mcp-server/commit/72a92847444436e2c4bd17407e664117fe08a31f))
145
+
146
+
147
+ ### โœ… Tests
148
+
149
+ * update create_documentation test to verify PRD check and remove incompatible jest test ([a7f2b28](https://github.com/programinglive/dev-workflow-mcp-server/commit/a7f2b28e65116cfb556767e7e6b25c0b4cdba613))
150
+
151
+
152
+ ### โœจ Features
153
+
154
+ * add PRD file existence check to create_documentation workflow step ([62cdee3](https://github.com/programinglive/dev-workflow-mcp-server/commit/62cdee3f7402ed70e3e36003020e84958cdd4a3d))
155
+ * add PRD file existence check to create_documentation workflow step ([6c51012](https://github.com/programinglive/dev-workflow-mcp-server/commit/6c510121980789c3d0e96376fe0375fbaf78a656))
156
+
157
+ ### [1.6.1](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.6.0...v1.6.1) (2025-12-06)
158
+
159
+
160
+ ### ๐Ÿ› Bug Fixes
161
+
162
+ * add table header to release notes for release script compatibility ([44e47fc](https://github.com/programinglive/dev-workflow-mcp-server/commit/44e47fc49ee9334cad82df095de1277038d0a0e9))
6
163
 
7
164
  ## [1.6.0](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.5.11...v1.6.0) (2025-12-06)
8
165
 
package/README.md CHANGED
@@ -181,9 +181,76 @@ Antigravity users should configure the MCP server in their `mcp_config.json`.
181
181
 
182
182
  See [Antigravity Getting Started](./docs/antigravity/GETTING-STARTED.md) for detailed instructions and troubleshooting.
183
183
 
184
- ## ๐Ÿ—๏ธ Build System
184
+ ## โš™๏ธ Configuration
185
+
186
+ ### Database Backend
187
+
188
+ By default, the server uses a local **SQLite** database stored in `.state/dev-workflow.db`. You can switch to **MySQL** or **PostgreSQL** for centralized storage using environment variables.
189
+
190
+ | Variable | Description | Default |
191
+ |----------|-------------|---------|
192
+ | `DEV_WORKFLOW_DB_TYPE` | Database driver (`sqlite`, `mysql`, `postgres`) | `sqlite` |
193
+ | `DEV_WORKFLOW_DB_URL` | Connection string for MySQL/Postgres | `null` |
194
+ | `DEV_WORKFLOW_DB_PATH` | Override path for the SQLite database file | `<project>/.state/dev-workflow.db` |
195
+
196
+ #### Examples
197
+
198
+ **MySQL:**
199
+ ```bash
200
+ export DEV_WORKFLOW_DB_TYPE=mysql
201
+ export DEV_WORKFLOW_DB_URL="mysql://user:password@localhost:3306/dev_workflow"
202
+ ```
203
+
204
+ **PostgreSQL:**
205
+ ```bash
206
+ export DEV_WORKFLOW_DB_TYPE=postgres
207
+ export DEV_WORKFLOW_DB_URL="postgresql://user:password@localhost:5432/dev_workflow"
208
+ ```
209
+
210
+ ### User & State Management
211
+
212
+ | Variable | Description |
213
+ |----------|-------------|
214
+ | `DEV_WORKFLOW_USER_ID` | Override the auto-generated user ID (e.g., set to your name/email) |
215
+ | `DEV_WORKFLOW_STATE_FILE` | Override the location of the `workflow-state.json` file |
216
+
217
+ ### Using Multiple Clients (Antigravity & Windsurf)
218
+
219
+ If you use multiple AI coding tools simultaneously (e.g., Antigravity and Windsurf) on the same project, they will share the same workflow state by default.
220
+
221
+ To maintain **separate, distinct sessions** for each tool, configure a unique `DEV_WORKFLOW_USER_ID` for each.
222
+
223
+ **Antigravity Config (`mcp_config.json`):**
224
+ ```json
225
+ {
226
+ "mcpServers": {
227
+ "dev-workflow": {
228
+ "command": "node",
229
+ "args": ["path/to/server/index.js"],
230
+ "env": {
231
+ "DEV_WORKFLOW_USER_ID": "antigravity_user"
232
+ }
233
+ }
234
+ }
235
+ }
236
+ ```
237
+
238
+ **Windsurf Config:**
239
+ Add the environment variable in your Windsurf MCP settings:
240
+ ```json
241
+ {
242
+ "mcpServers": {
243
+ "dev-workflow": {
244
+ "command": "node",
245
+ "args": ["path/to/server/index.js"],
246
+ "env": {
247
+ "DEV_WORKFLOW_USER_ID": "windsurf_user"
248
+ }
249
+ }
250
+ }
251
+ }
252
+ ```
185
253
 
186
- This project includes a Vite-based build system for creating optimized distributions.
187
254
 
188
255
  ### Scripts
189
256
 
@@ -353,6 +420,7 @@ The tool will:
353
420
  2. `create_tests`
354
421
  3. `run_tests` with `testsPassed`, `testCommand`, and optional `testDetails`
355
422
  4. `create_documentation` with `documentationType` and `documentationSummary`
423
+ - **Requires**: `docs/product/PRD.md` must exist before documentation can be marked complete
356
424
  5. `check_ready_to_commit`
357
425
  6. `commit_and_push` with `commitMessage` and optional `branch`
358
426
  7. `perform_release` with `releaseCommand`, plus optional `releaseNotes`, `releaseType`, and `preset`
@@ -360,6 +428,10 @@ The tool will:
360
428
 
361
429
  All arguments except the optional flags are required and must be non-empty strings.
362
430
 
431
+ ### Documentation Requirements
432
+
433
+ The `create_documentation` step enforces that a PRD (Product Requirements Document) exists at `docs/product/PRD.md` before documentation can be marked as complete. This ensures all projects maintain a current PRD that describes the product's goals, features, and requirements.
434
+
363
435
  ## ๐Ÿšซ Releasing Without the Workflow Steps
364
436
 
365
437
  The package ships with a release guard (`release-wrapper.js`) that backs the `npm run release:*` scripts. The guard refuses to run unless:
@@ -0,0 +1,46 @@
1
+ export class DbAdapter {
2
+ constructor(config = {}) {
3
+ this.config = config;
4
+ }
5
+
6
+ async connect() {
7
+ throw new Error("connect() must be implemented by subclass");
8
+ }
9
+
10
+ async migrate() {
11
+ throw new Error("migrate() must be implemented by subclass");
12
+ }
13
+
14
+ /**
15
+ * @param {string} userId - User identifier
16
+ * @param {string} projectPath - Path/ID of the project
17
+ * @param {Object} entry - Workflow history entry
18
+ */
19
+ async insertHistoryEntry(userId, projectPath, entry) {
20
+ throw new Error("insertHistoryEntry() must be implemented by subclass");
21
+ }
22
+
23
+ async updateSummaryForUser(userId, projectPath) {
24
+ throw new Error("updateSummaryForUser() must be implemented by subclass");
25
+ }
26
+
27
+ async getSummaryForUser(userId, projectPath) {
28
+ throw new Error("getSummaryForUser() must be implemented by subclass");
29
+ }
30
+
31
+ async getHistoryForUser(userId, projectPath, options = {}) {
32
+ throw new Error("getHistoryForUser() must be implemented by subclass");
33
+ }
34
+
35
+ async getHistorySummary(userId, projectPath, options = {}) {
36
+ throw new Error("getHistorySummary() must be implemented by subclass");
37
+ }
38
+
39
+ async saveState(userId, projectPath, state) {
40
+ throw new Error("saveState() must be implemented by subclass");
41
+ }
42
+
43
+ async getState(userId, projectPath) {
44
+ throw new Error("getState() must be implemented by subclass");
45
+ }
46
+ }
@@ -0,0 +1,292 @@
1
+ import mysql from "mysql2/promise";
2
+ import { DbAdapter } from "./base.js";
3
+
4
+ const FREQUENCY_FORMATS = {
5
+ daily: "%Y-%m-%d",
6
+ monthly: "%Y-%m",
7
+ yearly: "%Y",
8
+ };
9
+
10
+ export class MysqlAdapter extends DbAdapter {
11
+ constructor(config = {}) {
12
+ super(config);
13
+ this.pool = null;
14
+ }
15
+
16
+ async connect() {
17
+ if (!this.pool) {
18
+ this.pool = mysql.createPool({
19
+ uri: this.config.connectionUrl,
20
+ waitForConnections: true,
21
+ connectionLimit: 10,
22
+ queueLimit: 0,
23
+ // Support named parameters or handle manually? mysql2 supports '?' by default.
24
+ });
25
+ await this.migrate();
26
+ }
27
+ }
28
+
29
+ async migrate() {
30
+ const conn = await this.pool.getConnection();
31
+ try {
32
+ await conn.query(`
33
+ CREATE TABLE IF NOT EXISTS workflow_history (
34
+ id INT AUTO_INCREMENT PRIMARY KEY,
35
+ user_id VARCHAR(255) NOT NULL,
36
+ project_path VARCHAR(255) DEFAULT '',
37
+ task_description TEXT NOT NULL,
38
+ task_type VARCHAR(50) NOT NULL,
39
+ commit_message TEXT,
40
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
41
+ forced TINYINT(1) DEFAULT 0,
42
+ force_reason TEXT,
43
+ dropped TINYINT(1) DEFAULT 0,
44
+ drop_reason TEXT,
45
+ UNIQUE KEY uniq_workflow_entry (user_id, project_path, timestamp, task_description(100))
46
+ )
47
+ `);
48
+
49
+ await conn.query(`
50
+ CREATE TABLE IF NOT EXISTS project_summary (
51
+ user_id VARCHAR(255),
52
+ project_path VARCHAR(255) DEFAULT '',
53
+ total_tasks INT DEFAULT 0,
54
+ task_types JSON,
55
+ last_active DATETIME,
56
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
57
+ recent_tasks JSON,
58
+ PRIMARY KEY (user_id, project_path)
59
+ )
60
+ `);
61
+
62
+ await conn.query(`
63
+ CREATE TABLE IF NOT EXISTS workflow_state (
64
+ user_id VARCHAR(255),
65
+ project_path VARCHAR(255) DEFAULT '',
66
+ state_json JSON,
67
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
68
+ PRIMARY KEY (user_id, project_path)
69
+ )
70
+ `);
71
+
72
+ // Indexes are effectively covered by PRIMARY/UNIQUE keys, but we can add secondary if needed.
73
+ } finally {
74
+ conn.release();
75
+ }
76
+ }
77
+
78
+ async insertHistoryEntry(userId, projectPath, entry) {
79
+ await this.connect();
80
+ const normalizedProjectPath = projectPath || '';
81
+
82
+ // MySQL INSERT ON DUPLICATE KEY UPDATE
83
+ const query = `
84
+ INSERT INTO workflow_history
85
+ (user_id, project_path, task_description, task_type, commit_message, timestamp, forced, force_reason, dropped, drop_reason)
86
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
87
+ ON DUPLICATE KEY UPDATE
88
+ commit_message=VALUES(commit_message),
89
+ forced=VALUES(forced),
90
+ force_reason=VALUES(force_reason),
91
+ dropped=VALUES(dropped),
92
+ drop_reason=VALUES(drop_reason)
93
+ `;
94
+
95
+ await this.pool.execute(query, [
96
+ userId,
97
+ normalizedProjectPath,
98
+ entry.taskDescription,
99
+ entry.taskType,
100
+ entry.commitMessage,
101
+ entry.timestamp || new Date().toISOString(),
102
+ entry.forced ? 1 : 0,
103
+ entry.forceReason || null,
104
+ entry.dropped ? 1 : 0,
105
+ entry.dropReason || null
106
+ ]);
107
+
108
+ await this.updateSummaryForUser(userId, normalizedProjectPath);
109
+ }
110
+
111
+ async updateSummaryForUser(userId, projectPath) {
112
+ await this.connect();
113
+ const normalizedProjectPath = projectPath || '';
114
+
115
+ const [rows] = await this.pool.execute(`
116
+ SELECT task_type, timestamp, task_description
117
+ FROM workflow_history
118
+ WHERE user_id = ? AND project_path = ?
119
+ ORDER BY timestamp DESC
120
+ LIMIT 20
121
+ `, [userId, normalizedProjectPath]);
122
+
123
+ const taskTypes = {};
124
+ for (const row of rows) {
125
+ taskTypes[row.task_type] = (taskTypes[row.task_type] || 0) + 1;
126
+ }
127
+
128
+ const recentTasks = rows.slice(0, 5).map(r => ({
129
+ description: r.task_description,
130
+ type: r.task_type,
131
+ timestamp: r.timestamp,
132
+ }));
133
+
134
+ const lastActive = rows[0]?.timestamp || null;
135
+
136
+ const upsert = `
137
+ INSERT INTO project_summary
138
+ (user_id, project_path, total_tasks, task_types, last_active, recent_tasks, updated_at)
139
+ VALUES (?, ?, ?, ?, ?, ?, ?)
140
+ ON DUPLICATE KEY UPDATE
141
+ total_tasks=VALUES(total_tasks),
142
+ task_types=VALUES(task_types),
143
+ last_active=VALUES(last_active),
144
+ recent_tasks=VALUES(recent_tasks),
145
+ updated_at=VALUES(updated_at)
146
+ `;
147
+
148
+ await this.pool.execute(upsert, [
149
+ userId,
150
+ normalizedProjectPath,
151
+ rows.length,
152
+ JSON.stringify(taskTypes),
153
+ lastActive,
154
+ JSON.stringify(recentTasks),
155
+ new Date().toISOString()
156
+ ]);
157
+ }
158
+
159
+ async getSummaryForUser(userId, projectPath) {
160
+ await this.connect();
161
+ const normalizedProjectPath = projectPath || '';
162
+
163
+ const [rows] = await this.pool.execute(`
164
+ SELECT * FROM project_summary WHERE user_id = ? AND project_path = ?
165
+ `, [userId, normalizedProjectPath]);
166
+
167
+ const row = rows[0];
168
+ if (!row) return null;
169
+
170
+ return {
171
+ totalTasks: row.total_tasks,
172
+ taskTypes: row.task_types, // MySQL driver typically parses JSON columns automatically
173
+ lastActive: row.last_active,
174
+ recentTasks: row.recent_tasks,
175
+ updatedAt: row.updated_at,
176
+ };
177
+ }
178
+
179
+ async getHistoryForUser(userId, projectPath, options = {}) {
180
+ await this.connect();
181
+ const normalizedProjectPath = projectPath || '';
182
+
183
+ const { page = 1, pageSize = 20, startDate, endDate } = options;
184
+ const sanitizedPage = Math.max(1, Number.isFinite(Number(page)) ? Math.floor(Number(page)) : 1);
185
+ const sanitizedPageSize = Math.min(100, Math.max(1, Number.isFinite(Number(pageSize)) ? Math.floor(Number(pageSize)) : 20));
186
+ const offset = (sanitizedPage - 1) * sanitizedPageSize;
187
+
188
+ const clauses = [];
189
+ const params = [userId, normalizedProjectPath];
190
+
191
+ if (startDate) {
192
+ clauses.push("DATE(timestamp) >= DATE(?)");
193
+ params.push(startDate);
194
+ }
195
+ if (endDate) {
196
+ clauses.push("DATE(timestamp) <= DATE(?)");
197
+ params.push(endDate);
198
+ }
199
+
200
+ const where = ["user_id = ?", "project_path = ?", ...clauses].join(" AND ");
201
+
202
+ const [countRows] = await this.pool.execute(
203
+ `SELECT COUNT(*) as total FROM workflow_history WHERE ${where}`,
204
+ params
205
+ );
206
+ const total = countRows[0].total;
207
+
208
+ const [entries] = await this.pool.execute(`
209
+ SELECT *
210
+ FROM workflow_history
211
+ WHERE ${where}
212
+ ORDER BY timestamp DESC
213
+ LIMIT ?
214
+ OFFSET ?
215
+ `, [...params, sanitizedPageSize.toString(), offset.toString()]); // prepared statement params often stringified in strict mode, but execute handles numbers usually.
216
+
217
+ return {
218
+ entries,
219
+ total,
220
+ page: sanitizedPage,
221
+ pageSize: sanitizedPageSize,
222
+ };
223
+ }
224
+
225
+ async getHistorySummary(userId, projectPath, options = {}) {
226
+ await this.connect();
227
+ const normalizedProjectPath = projectPath || '';
228
+
229
+ const { startDate, endDate, frequency = "daily" } = options;
230
+ const format = FREQUENCY_FORMATS[frequency] || FREQUENCY_FORMATS.daily;
231
+
232
+ const clauses = [];
233
+ const params = [userId, normalizedProjectPath];
234
+ if (startDate) {
235
+ clauses.push("DATE(timestamp) >= DATE(?)");
236
+ params.push(startDate);
237
+ }
238
+ if (endDate) {
239
+ clauses.push("DATE(timestamp) <= DATE(?)");
240
+ params.push(endDate);
241
+ }
242
+
243
+ const where = ["user_id = ?", "project_path = ?", ...clauses].join(" AND ");
244
+
245
+ const [rows] = await this.pool.execute(`
246
+ SELECT DATE_FORMAT(timestamp, '${format}') AS period, COUNT(*) AS count
247
+ FROM workflow_history
248
+ WHERE ${where}
249
+ GROUP BY period
250
+ ORDER BY period DESC
251
+ `, params);
252
+
253
+ return rows;
254
+ }
255
+
256
+ async saveState(userId, projectPath, state) {
257
+ await this.connect();
258
+ const normalizedProjectPath = projectPath || '';
259
+
260
+ const query = `
261
+ INSERT INTO workflow_state
262
+ (user_id, project_path, state_json, updated_at)
263
+ VALUES (?, ?, ?, ?)
264
+ ON DUPLICATE KEY UPDATE
265
+ state_json=VALUES(state_json),
266
+ updated_at=VALUES(updated_at)
267
+ `;
268
+
269
+ await this.pool.execute(query, [
270
+ userId,
271
+ normalizedProjectPath,
272
+ JSON.stringify(state),
273
+ new Date().toISOString()
274
+ ]);
275
+ }
276
+
277
+ async getState(userId, projectPath) {
278
+ await this.connect();
279
+ const normalizedProjectPath = projectPath || '';
280
+
281
+ const [rows] = await this.pool.execute(`
282
+ SELECT state_json FROM workflow_state WHERE user_id = ? AND project_path = ?
283
+ `, [userId, normalizedProjectPath]);
284
+
285
+ const row = rows[0];
286
+ if (!row) return null;
287
+
288
+ // mysql2 usually parses JSON columns, but if not:
289
+ return typeof row.state_json === 'string' ? JSON.parse(row.state_json) : row.state_json;
290
+ }
291
+ }
292
+