@programinglive/dev-workflow-mcp-server 1.9.1 → 1.11.0
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/README.md +311 -173
- package/factory-client.js +636 -0
- package/git-facade.js +35 -0
- package/index.js +4 -30
- package/package.json +14 -18
- package/scripts/setup-test-dbs.sh +61 -0
- package/scripts/test-db-adapters.sh +71 -0
- package/scripts/verify-bundle.mjs +92 -0
- package/session-keeper.js +72 -0
- package/tools/definitions.js +124 -0
- package/tools/handler-modules/factory-handlers.js +288 -0
- package/tools/handler-modules/index.js +11 -0
- package/tools/handler-modules/status-handlers.js +22 -2
- package/tools/handler-modules/workflow-handlers.js +32 -3
- package/vite.config.js +6 -0
- package/workflow-state.js +166 -2
- package/scripts/test-auth-flow.js +0 -75
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Dev Workflow MCP Server
|
|
2
2
|
|
|
3
|
-
MCP server
|
|
3
|
+
An MCP server that enforces development workflow discipline: **tests → documentation → commit → release**, with no way to skip a step.
|
|
4
4
|
|
|
5
5
|
```mermaid
|
|
6
6
|
graph LR
|
|
@@ -18,9 +18,9 @@ graph LR
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
-
## Quick Start
|
|
21
|
+
## Quick Start
|
|
22
22
|
|
|
23
|
-
### 1. Clone
|
|
23
|
+
### 1. Clone and install
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
git clone https://github.com/programinglive/dev-workflow-mcp-server.git
|
|
@@ -28,62 +28,138 @@ cd dev-workflow-mcp-server
|
|
|
28
28
|
npm install
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
`better-sqlite3` ships prebuilt binaries for Node 22 and newer (ABI 127, 137,
|
|
32
|
+
141, 147). On Node 20 there is no prebuild for the current version, so npm falls
|
|
33
|
+
back to `node-gyp` and you need [Python 3](https://python.org) and
|
|
34
|
+
[Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
|
|
35
|
+
on Windows. See [Troubleshooting](#troubleshooting).
|
|
32
36
|
|
|
33
|
-
### 2.
|
|
37
|
+
### 2. Verify the server runs
|
|
34
38
|
|
|
35
39
|
```bash
|
|
36
40
|
node index.js call get_workflow_status
|
|
37
41
|
```
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
Expected output:
|
|
40
44
|
|
|
41
45
|
```
|
|
42
46
|
📊 Current Workflow Status:
|
|
47
|
+
|
|
43
48
|
Phase: idle
|
|
44
49
|
Task: (none)
|
|
45
|
-
|
|
50
|
+
Feature flow: ❌
|
|
51
|
+
...
|
|
46
52
|
```
|
|
47
53
|
|
|
48
|
-
### 3.
|
|
54
|
+
### 3. Connect an MCP client
|
|
49
55
|
|
|
50
|
-
|
|
56
|
+
See [MCP client configuration](#mcp-client-configuration).
|
|
51
57
|
|
|
52
58
|
---
|
|
53
59
|
|
|
54
|
-
##
|
|
60
|
+
## Available Tools
|
|
61
|
+
|
|
62
|
+
22 tools.
|
|
55
63
|
|
|
56
|
-
| Tool |
|
|
64
|
+
| Tool | Purpose |
|
|
57
65
|
|---|---|
|
|
58
|
-
| `start_task` |
|
|
59
|
-
| `create_feature_flow` |
|
|
60
|
-
| `mark_bug_fixed` |
|
|
61
|
-
| `create_tests` |
|
|
62
|
-
| `skip_tests` |
|
|
63
|
-
| `run_tests` |
|
|
64
|
-
| `create_documentation` |
|
|
65
|
-
| `check_ready_to_commit` |
|
|
66
|
-
| `commit_and_push` | `git add` → `commit` → `push`
|
|
67
|
-
| `perform_release` |
|
|
68
|
-
| `skip_release` |
|
|
69
|
-
| `complete_task` |
|
|
70
|
-
| `get_workflow_status` |
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
| `start_task` | Begin a task (always the first step) |
|
|
67
|
+
| `create_feature_flow` | Record the feature flow as Mermaid |
|
|
68
|
+
| `mark_bug_fixed` | Mark the implementation done |
|
|
69
|
+
| `create_tests` | Mark tests as written |
|
|
70
|
+
| `skip_tests` | Skip tests with a justification |
|
|
71
|
+
| `run_tests` | Record test results (blocks commit when failing) |
|
|
72
|
+
| `create_documentation` | Mark docs as written |
|
|
73
|
+
| `check_ready_to_commit` | Verify every required step is complete |
|
|
74
|
+
| `commit_and_push` | `git add` → `commit` → `push` |
|
|
75
|
+
| `perform_release` | Record a release |
|
|
76
|
+
| `skip_release` | Skip the release step with a justification |
|
|
77
|
+
| `complete_task` | Finish and reset the workflow |
|
|
78
|
+
| `get_workflow_status` | Show the current phase |
|
|
79
|
+
| `continue_workflow` | Get guidance on the next step |
|
|
80
|
+
| `view_history` | List completed tasks |
|
|
81
|
+
| `run_full_workflow` | Run every step in one call |
|
|
82
|
+
| `rerun_workflow` | Restart the current task from the beginning |
|
|
83
|
+
| `drop_task` | Abandon the task without completing it |
|
|
84
|
+
| `force_complete_task` | Complete despite missing steps, with a reason |
|
|
85
|
+
| `project_summary` | Aggregate activity for the project |
|
|
86
|
+
| `project_summary_data` | Read the persisted `project-summary.json` |
|
|
87
|
+
| `project_summary_db` | Read the per-user summary from SQLite |
|
|
88
|
+
| `list_factory_tasks` | List your Factory task queue (requires config) |
|
|
89
|
+
| `claim_factory_task` | Claim a Factory task and start a local workflow |
|
|
90
|
+
| `sync_task_status` | Report a status change back to Factory |
|
|
91
|
+
|
|
92
|
+
### Factory task integration
|
|
93
|
+
|
|
94
|
+
Three tools connect this server to the **Factory** board at
|
|
95
|
+
`factory.programinglive.com`. They read and write the real task table
|
|
96
|
+
(`factory_tasks`), not the retired PocketBase `activity_tasks` collection
|
|
97
|
+
(which still exists but holds 0 records and never reached the board).
|
|
98
|
+
|
|
99
|
+
They stay **inert** until configured. With no configuration the server behaves
|
|
100
|
+
exactly as before, and the tools reply with setup instructions.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
FACTORY_URL=https://factory.programinglive.com
|
|
104
|
+
FACTORY_EMAIL=<activity_users email>
|
|
105
|
+
FACTORY_PASSWORD=<password>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Or supply a ready token instead of a password:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
FACTORY_TOKEN=<PocketBase activity_users token>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Optional:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
FACTORY_PB_URL=https://pocketbase.programinglive.com # where the login happens
|
|
118
|
+
FACTORY_MACHINE_ID=<name shown on the device row> # defaults to the hostname
|
|
119
|
+
FACTORY_DEVICE_ID=<fixed device UUID> # else derived from the name
|
|
120
|
+
FACTORY_AGENT=dev-workflow-mcp # max 40 chars
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Use an **`activity_users` account, not a PocketBase superuser**. Factory accepts
|
|
124
|
+
that token as `Authorization: Bearer <token>` for non-browser clients, and the
|
|
125
|
+
account only sees its own tasks.
|
|
126
|
+
|
|
127
|
+
| Tool | Writes? | What it does |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| `list_factory_tasks` | no | Reads `GET /api/factory`, maps tasks plus their latest work session, and orders them status → priority. Defaults to `running, queued, backlog, review, blocked` |
|
|
130
|
+
| `claim_factory_task` | yes | Opens a work session (`POST /api/work {action:start}`), which is what turns the task `running` on the board, then starts a local workflow |
|
|
131
|
+
| `sync_task_status` | yes | Uses the only endpoint that can produce the status; see the table below |
|
|
132
|
+
|
|
133
|
+
#### Status mapping is not free-form
|
|
134
|
+
|
|
135
|
+
Factory has no generic "set status" endpoint, so the tool routes each status to
|
|
136
|
+
the endpoint that actually supports it:
|
|
137
|
+
|
|
138
|
+
| Requested status | Endpoint used | Result |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `review`, `blocked`, `paused` | `POST /api/work {action:finish}` | Session closes, task changes accordingly |
|
|
141
|
+
| `backlog`, `queued`, `cancelled` | `POST /api/factory {action:move_task}` | Board column changes |
|
|
142
|
+
| `running` | — rejected | Point the caller at `claim_factory_task` |
|
|
143
|
+
| `done` | — rejected | A human approves the work in Factory |
|
|
144
|
+
|
|
145
|
+
`review` requires an active work session. Asking for it without one fails with
|
|
146
|
+
"no active work session" instead of silently doing nothing — the task must be
|
|
147
|
+
claimed first. Full behaviour, limits (summary 2000 chars, step 240 chars) and
|
|
148
|
+
troubleshooting live in [docs/factory-integration.md](docs/factory-integration.md).
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
### Example session
|
|
76
152
|
|
|
77
153
|
```
|
|
78
154
|
"Start a new task: add user authentication, type: feature"
|
|
79
|
-
→ start_task
|
|
155
|
+
→ start_task, phase moves to coding
|
|
80
156
|
|
|
81
|
-
[
|
|
157
|
+
[you write code]
|
|
82
158
|
|
|
83
159
|
"Mark as fixed: login with JWT token"
|
|
84
160
|
→ mark_bug_fixed, phase moves to testing
|
|
85
161
|
|
|
86
|
-
[
|
|
162
|
+
[you write tests]
|
|
87
163
|
|
|
88
164
|
"Create tests done"
|
|
89
165
|
→ create_tests
|
|
@@ -95,10 +171,10 @@ Lihat bagian [Konfigurasi MCP Client](#-konfigurasi-mcp-client) di bawah.
|
|
|
95
171
|
→ create_documentation, phase moves to ready_to_commit
|
|
96
172
|
|
|
97
173
|
"Check if ready to commit"
|
|
98
|
-
→ check_ready_to_commit →
|
|
174
|
+
→ check_ready_to_commit → READY
|
|
99
175
|
|
|
100
176
|
"Commit and push: message='feat(auth): add JWT authentication'"
|
|
101
|
-
→ commit_and_push
|
|
177
|
+
→ commit_and_push, phase moves to release
|
|
102
178
|
|
|
103
179
|
"Perform release: command='npm run release:minor'"
|
|
104
180
|
→ perform_release, phase moves to ready_to_complete
|
|
@@ -109,27 +185,37 @@ Lihat bagian [Konfigurasi MCP Client](#-konfigurasi-mcp-client) di bawah.
|
|
|
109
185
|
|
|
110
186
|
---
|
|
111
187
|
|
|
112
|
-
##
|
|
188
|
+
## MCP client configuration
|
|
113
189
|
|
|
114
190
|
### OpenCode
|
|
115
191
|
|
|
116
|
-
|
|
192
|
+
OpenCode V2 nests servers under `mcp.servers` and does **not** accept an
|
|
193
|
+
`enabled` key there. Config path is reported by `opencode debug paths`
|
|
194
|
+
(`~/.config/opencode` on every platform, including Windows).
|
|
117
195
|
|
|
118
196
|
```json
|
|
119
197
|
{
|
|
120
198
|
"mcp": {
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
|
|
199
|
+
"servers": {
|
|
200
|
+
"dev-workflow": {
|
|
201
|
+
"type": "local",
|
|
202
|
+
"command": ["node", "C:\\path\\to\\dev-workflow-mcp-server\\index.js"]
|
|
203
|
+
}
|
|
125
204
|
}
|
|
126
205
|
}
|
|
127
206
|
}
|
|
128
207
|
```
|
|
129
208
|
|
|
209
|
+
The simplest way to write this correctly is to let OpenCode do it:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
opencode mcp add dev-workflow --global -- node "C:\path\to\dev-workflow-mcp-server\index.js"
|
|
213
|
+
```
|
|
214
|
+
|
|
130
215
|
### Claude Desktop
|
|
131
216
|
|
|
132
|
-
|
|
217
|
+
`%APPDATA%\Claude\claude_desktop_config.json` (Windows) or
|
|
218
|
+
`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
|
|
133
219
|
|
|
134
220
|
```json
|
|
135
221
|
{
|
|
@@ -144,7 +230,8 @@ File: `%APPDATA%\Claude\claude_desktop_config.json` (Windows) atau `~/Library/Ap
|
|
|
144
230
|
|
|
145
231
|
### Windsurf
|
|
146
232
|
|
|
147
|
-
|
|
233
|
+
`%APPDATA%\Windsurf\config.json` (Windows) or
|
|
234
|
+
`~/Library/Application Support/Windsurf/config.json` (macOS):
|
|
148
235
|
|
|
149
236
|
```json
|
|
150
237
|
{
|
|
@@ -160,7 +247,7 @@ File: `%APPDATA%\Windsurf\config.json` (Windows) atau `~/Library/Application Sup
|
|
|
160
247
|
|
|
161
248
|
### Antigravity
|
|
162
249
|
|
|
163
|
-
|
|
250
|
+
`%APPDATA%\Antigravity\mcp_config.json`:
|
|
164
251
|
|
|
165
252
|
```json
|
|
166
253
|
{
|
|
@@ -173,35 +260,46 @@ File: `%APPDATA%\Antigravity\mcp_config.json`
|
|
|
173
260
|
}
|
|
174
261
|
```
|
|
175
262
|
|
|
176
|
-
> **Performance
|
|
263
|
+
> **Performance:** run `node index.js` directly rather than through `npx`.
|
|
264
|
+
> Startup is noticeably faster.
|
|
177
265
|
|
|
178
266
|
---
|
|
179
267
|
|
|
180
|
-
##
|
|
268
|
+
## Configuration
|
|
181
269
|
|
|
182
270
|
### Database
|
|
183
271
|
|
|
184
|
-
|
|
272
|
+
Defaults to **SQLite** (file-based, zero config). PostgreSQL and MySQL are also
|
|
273
|
+
supported:
|
|
185
274
|
|
|
186
275
|
```bash
|
|
187
|
-
# .env
|
|
276
|
+
# .env or environment variable
|
|
188
277
|
DEV_WORKFLOW_DB_TYPE=postgres
|
|
189
278
|
DEV_WORKFLOW_DB_URL=postgresql://user:pass@localhost:5432/devworkflow
|
|
190
279
|
```
|
|
191
280
|
|
|
192
|
-
|
|
|
281
|
+
| Variable | Default | Description |
|
|
193
282
|
|---|---|---|
|
|
194
|
-
| `DEV_WORKFLOW_DB_TYPE` | `sqlite` | `sqlite`, `mysql`,
|
|
195
|
-
| `DEV_WORKFLOW_DB_URL` | — | Connection string (
|
|
196
|
-
| `DEV_WORKFLOW_DB_PATH` | `.state/dev-workflow.db` |
|
|
197
|
-
| `DEV_WORKFLOW_USER_ID` | auto-generated | ID
|
|
198
|
-
| `DEV_WORKFLOW_STATE_FILE` | auto-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
283
|
+
| `DEV_WORKFLOW_DB_TYPE` | `sqlite` | `sqlite`, `mysql`, or `postgres` |
|
|
284
|
+
| `DEV_WORKFLOW_DB_URL` | — | Connection string (MySQL/Postgres) |
|
|
285
|
+
| `DEV_WORKFLOW_DB_PATH` | `.state/dev-workflow.db` | SQLite path (optional) |
|
|
286
|
+
| `DEV_WORKFLOW_USER_ID` | auto-generated | User ID for multi-user isolation |
|
|
287
|
+
| `DEV_WORKFLOW_STATE_FILE` | auto-resolved | Override for `workflow-state.json` |
|
|
288
|
+
| `FACTORY_URL` | - | Factory base URL, e.g. `https://factory.programinglive.com` |
|
|
289
|
+
| `FACTORY_TOKEN` | - | PocketBase `activity_users` token sent as a Bearer header |
|
|
290
|
+
| `FACTORY_EMAIL` | - | `activity_users` email, exchanged for a token on demand |
|
|
291
|
+
| `FACTORY_PASSWORD` | - | Password for that account |
|
|
292
|
+
| `FACTORY_PB_URL` | `https://pocketbase.programinglive.com` | Where the token is obtained |
|
|
293
|
+
| `FACTORY_MACHINE_ID` | hostname | Device name shown on the Factory board |
|
|
294
|
+
| `FACTORY_DEVICE_ID` | derived from the name | Stable device UUID |
|
|
295
|
+
|
|
296
|
+
### Project-specific state
|
|
297
|
+
|
|
298
|
+
Each project keeps its own state in `.state/users/<user-id>/workflow-state.json`.
|
|
299
|
+
The `.state/` directory is gitignored.
|
|
300
|
+
|
|
301
|
+
For multi-client isolation (for example separate state for Windsurf and
|
|
302
|
+
Antigravity), set a different user ID per client:
|
|
205
303
|
|
|
206
304
|
```json
|
|
207
305
|
{
|
|
@@ -217,17 +315,18 @@ Untuk isolasi multi-client (misal Windsurf + Antigravity pakai state terpisah):
|
|
|
217
315
|
|
|
218
316
|
---
|
|
219
317
|
|
|
220
|
-
##
|
|
318
|
+
## One-command stack setup (OpenCode V2)
|
|
221
319
|
|
|
222
|
-
`scripts/setup-stack.js` installs the whole
|
|
223
|
-
|
|
320
|
+
`scripts/setup-stack.js` installs the whole agent stack: this MCP server,
|
|
321
|
+
[obra/superpowers](https://github.com/obra/superpowers) skills, and
|
|
224
322
|
[RTK](https://github.com/rtk-ai/rtk) token compression.
|
|
225
323
|
|
|
226
324
|
```bash
|
|
227
325
|
node scripts/setup-stack.js # any platform
|
|
228
326
|
```
|
|
229
327
|
|
|
230
|
-
Platform wrappers
|
|
328
|
+
Platform wrappers install RTK, run `npm install`, then delegate to the same
|
|
329
|
+
script:
|
|
231
330
|
|
|
232
331
|
```powershell
|
|
233
332
|
pwsh scripts/bootstrap-windows.ps1
|
|
@@ -240,35 +339,68 @@ bash scripts/bootstrap-unix.sh
|
|
|
240
339
|
### What it does
|
|
241
340
|
|
|
242
341
|
1. **Finds the real config directory.** Runs `opencode debug paths` and uses the
|
|
243
|
-
reported `config` path
|
|
244
|
-
|
|
342
|
+
reported `config` path, falling back to `~/.config/opencode`. OpenCode V2
|
|
343
|
+
uses that path on Windows too — `%APPDATA%\opencode` is not read.
|
|
245
344
|
2. **Backs up `opencode.json`** to `secrets/opencode-backup-<timestamp>.json`
|
|
246
345
|
before touching it.
|
|
247
|
-
3. **Registers Superpowers** under the V2 `plugins` key
|
|
248
|
-
deprecated and ignored by V2
|
|
249
|
-
4. **Registers the MCP server**
|
|
250
|
-
|
|
346
|
+
3. **Registers Superpowers** under the V2 `plugins` key. The V1 `plugin` key is
|
|
347
|
+
deprecated and ignored by V2.
|
|
348
|
+
4. **Registers the MCP server** via `opencode mcp add`, which writes a
|
|
349
|
+
schema-valid `mcp.servers` entry.
|
|
251
350
|
5. **Installs dependencies.** Without `npm install` the server dies at startup
|
|
252
351
|
with `ERR_MODULE_NOT_FOUND: Cannot find package 'dotenv'`.
|
|
253
352
|
6. **Smoke tests the server**, then verifies with `opencode mcp list` and
|
|
254
|
-
`opencode plugin list`. Exits non-zero when
|
|
255
|
-
install can no longer report success.
|
|
353
|
+
`opencode plugin list`. Exits non-zero when a check fails.
|
|
256
354
|
7. **Creates `AGENTS.md`** in the current project if missing.
|
|
257
355
|
|
|
258
|
-
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Tests
|
|
259
359
|
|
|
260
360
|
```bash
|
|
261
361
|
npm test # full suite
|
|
262
362
|
node --test tests/setup-stack.test.js
|
|
263
363
|
```
|
|
264
364
|
|
|
365
|
+
```
|
|
366
|
+
ℹ tests 112
|
|
367
|
+
ℹ pass 110
|
|
368
|
+
ℹ fail 0
|
|
369
|
+
ℹ skipped 2
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
The two skipped tests are the MySQL and PostgreSQL adapters. They run only when a
|
|
373
|
+
database is reachable:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
TEST_MYSQL_URL=mysql://user:pass@localhost:3306/db npm test
|
|
377
|
+
TEST_POSTGRES_URL=postgres://user:pass@localhost:5432/db npm test
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
To exercise them against real servers without exposing them publicly:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
# on the database host, once: creates isolated devworkflow_test databases
|
|
384
|
+
./scripts/setup-test-dbs.sh
|
|
385
|
+
|
|
386
|
+
# locally: opens an SSH tunnel, runs only the adapter tests, closes the tunnel
|
|
387
|
+
TEST_POSTGRES_URL=... TEST_MYSQL_URL=... ./scripts/test-db-adapters.sh
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Both adapters pass against live MySQL and PostgreSQL this way (19/19 tests, none
|
|
391
|
+
skipped). The tests **write real rows**, so `setup-test-dbs.sh` creates separate
|
|
392
|
+
`devworkflow_test` databases and grants the test role access only to those. Never
|
|
393
|
+
point these variables at a production database.
|
|
394
|
+
|
|
395
|
+
Everything else, including the SQLite adapter, always runs.
|
|
396
|
+
|
|
265
397
|
`tests/test-helpers.js` exports `stripNpmNotices()`, which removes npm's own
|
|
266
|
-
`npm warn`/`npm notice`
|
|
267
|
-
captured stderr before the build test asserts on it.
|
|
268
|
-
|
|
269
|
-
|
|
398
|
+
`npm warn`/`npm notice` output and Rollup's third-party annotation warnings from
|
|
399
|
+
captured stderr before the build test asserts on it.
|
|
400
|
+
|
|
401
|
+
---
|
|
270
402
|
|
|
271
|
-
|
|
403
|
+
## CI
|
|
272
404
|
|
|
273
405
|
`.github/workflows/ci.yml` runs on the **self-hosted runner on MMCH**, not on
|
|
274
406
|
GitHub-hosted minutes:
|
|
@@ -277,151 +409,157 @@ GitHub-hosted minutes:
|
|
|
277
409
|
runs-on: [self-hosted, windows, mmch]
|
|
278
410
|
```
|
|
279
411
|
|
|
280
|
-
Two jobs:
|
|
281
|
-
|
|
282
412
|
| Job | Steps |
|
|
283
413
|
|---|---|
|
|
284
|
-
| `test` | `npm ci` → `npm test` → `build:server` → `npm audit --omit=dev --audit-level=high` → setup-stack suite |
|
|
414
|
+
| `test` | `npm ci` → `npm test` → `build:server` → **verify the bundle starts** → `npm audit --omit=dev --audit-level=high` → setup-stack suite |
|
|
285
415
|
| `web` | `npm ci` in `web/` → static export build → verify `out/index.html` and CSS size → `npm audit --audit-level=high` |
|
|
286
416
|
|
|
287
|
-
The
|
|
288
|
-
|
|
289
|
-
|
|
417
|
+
The bundle verification step exists because a successful build does not mean
|
|
418
|
+
the artefact runs. `dist/index.mjs` is the published package entry, and a
|
|
419
|
+
bundler upgrade once produced a green build whose output crashed on startup.
|
|
420
|
+
`scripts/verify-bundle.mjs` starts the bundle and requires the stdio startup
|
|
421
|
+
banner, so that class of failure fails the pull request instead of the release.
|
|
290
422
|
|
|
291
|
-
|
|
423
|
+
The `web` job exists because the dashboard is deployed to Netlify from `web/`.
|
|
424
|
+
The CSS size check catches a Tailwind regression that a green build would
|
|
425
|
+
otherwise hide.
|
|
292
426
|
|
|
293
|
-
|
|
294
|
-
[docs/homelab/ci-runner.md](./docs/homelab/ci-runner.md).
|
|
427
|
+
### Tailwind stays on v3
|
|
295
428
|
|
|
296
|
-
|
|
429
|
+
`web/` uses Tailwind 3.4 and is **not** being migrated to v4. Tailwind 4 is a
|
|
430
|
+
rewrite of the CSS pipeline: the `@tailwind` directives become
|
|
431
|
+
`@import "tailwindcss"`, the config moves into CSS, and the PostCSS plugin is
|
|
432
|
+
replaced by `@tailwindcss/postcss`. The dashboard is finished and stable, so
|
|
433
|
+
rewriting its styling pipeline risks the entire visual design for no functional
|
|
434
|
+
gain.
|
|
297
435
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
```
|
|
436
|
+
`dependabot.yml` ignores `tailwindcss`, `@tailwindcss/postcss` and
|
|
437
|
+
`@tailwindcss/cli` entirely — not just majors — so no v4 pull request
|
|
438
|
+
reappears.
|
|
302
439
|
|
|
303
|
-
|
|
304
|
-
`package.json`; bump that pin when a new advisory lands rather than relying on
|
|
305
|
-
the transitive range.
|
|
306
|
-
|
|
307
|
-
The root `tailwindcss` devDependency was removed: it was added when the web
|
|
308
|
-
dashboard still shipped a static HTML page, but that build step no longer
|
|
309
|
-
exists. The dashboard under `web/` has its own Tailwind setup
|
|
310
|
-
(`web/tailwind.config.ts`). The root `tailwind.config.js` pointed `content` at
|
|
311
|
-
`web/public/**/*.{html,js}`, a directory that holds only SVG icons, so it
|
|
312
|
-
generated nothing and was referenced by no script.
|
|
440
|
+
Runner provisioning and troubleshooting: [docs/homelab/ci-runner.md](./docs/homelab/ci-runner.md).
|
|
313
441
|
|
|
314
442
|
---
|
|
315
443
|
|
|
316
|
-
## Release
|
|
444
|
+
## Release workflow
|
|
317
445
|
|
|
318
|
-
`.github/workflows/github-release.yml` runs on a `v*` tag
|
|
446
|
+
`.github/workflows/github-release.yml` runs on a `v*` tag, split in two:
|
|
319
447
|
|
|
320
|
-
| Job | Runner |
|
|
448
|
+
| Job | Runner | Work |
|
|
321
449
|
|---|---|---|
|
|
322
|
-
| `build` | self-hosted MMCH | `npm ci`
|
|
323
|
-
| `publish` | `ubuntu-latest` | download tarball
|
|
324
|
-
|
|
325
|
-
**Why publish is not on MMCH:** npm provenance requires a supported cloud CI/CD
|
|
326
|
-
provider with a **cloud-hosted** runner
|
|
327
|
-
([docs](https://docs.npmjs.com/generating-provenance-statements)). A self-hosted
|
|
328
|
-
runner cannot produce an attestation, so `--provenance` would have to be dropped
|
|
329
|
-
and consumers would lose the ability to verify which commit the package was
|
|
330
|
-
built from.
|
|
450
|
+
| `build` | self-hosted MMCH | `npm ci` → `npm test` → `build:server` → verify the bundle starts → `npm pack` → upload tarball |
|
|
451
|
+
| `publish` | `ubuntu-latest` | download tarball → `npm publish` → create/update the GitHub release |
|
|
331
452
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
453
|
+
Publishing uses **Trusted Publishing (OIDC)**: no long-lived npm token is stored
|
|
454
|
+
anywhere, and `id-token: write` plus `registry-url` on `setup-node` is the whole
|
|
455
|
+
configuration. The trusted publisher is registered with `npm trust`:
|
|
335
456
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
457
|
+
```bash
|
|
458
|
+
npm trust github @programinglive/dev-workflow-mcp-server \
|
|
459
|
+
--file .github/workflows/github-release.yml \
|
|
460
|
+
--repo programinglive/dev-workflow-mcp-server \
|
|
461
|
+
--allow-publish -y
|
|
462
|
+
```
|
|
339
463
|
|
|
340
|
-
|
|
464
|
+
The `file` must match the workflow filename exactly, or OIDC is rejected.
|
|
341
465
|
|
|
342
|
-
|
|
343
|
-
and 65 kB. It excludes `.env*` (only `.env.example` ships), `.state/`, tests,
|
|
344
|
-
CI config, `web/`, and docs. Without it npm falls back to `.gitignore`, which
|
|
345
|
-
still allows tracked files such as `.env.production` into the tarball.
|
|
466
|
+
### No provenance
|
|
346
467
|
|
|
347
|
-
`
|
|
348
|
-
|
|
349
|
-
dependency does not fail on husky. The `husky || true` idiom is not portable to
|
|
350
|
-
Windows, where npm runs scripts through `cmd.exe`.
|
|
468
|
+
`--provenance` is intentionally **not** used. npm rejects provenance bundles
|
|
469
|
+
whose source repository is private:
|
|
351
470
|
|
|
352
|
-
|
|
471
|
+
```
|
|
472
|
+
422 Unprocessable Entity - Unsupported GitHub Actions source repository
|
|
473
|
+
visibility: "private". Only public source repositories are supported when
|
|
474
|
+
publishing with provenance.
|
|
475
|
+
```
|
|
353
476
|
|
|
354
|
-
|
|
477
|
+
This repository is private by policy, so provenance is unavailable. Consumers
|
|
478
|
+
cannot cryptographically verify which commit produced a published tarball.
|
|
355
479
|
|
|
356
|
-
|
|
480
|
+
### What ships in the package
|
|
357
481
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
| [docs/homelab/infra-tailnet-ssh.md](./docs/homelab/infra-tailnet-ssh.md) | Tailnet + SSH setup lengkap |
|
|
362
|
-
| [docs/homelab/cli-tools.md](./docs/homelab/cli-tools.md) | CLI tools per machine |
|
|
363
|
-
| [docs/homelab/ci-runner.md](./docs/homelab/ci-runner.md) | Self-hosted CI runner di MMCH |
|
|
364
|
-
| [docs/homelab/security-rules.md](./docs/homelab/security-rules.md) | Aturan keamanan |
|
|
365
|
-
| [.agent/workflows/homelab-setup.md](./.agent/workflows/homelab-setup.md) | Workflow MCP untuk onboarding mesin baru |
|
|
482
|
+
`.npmignore` limits the tarball to the runtime server: about 60 files and 66 kB.
|
|
483
|
+
It excludes `.env*` (only `.env.example` ships), `.state/`, tests, CI config,
|
|
484
|
+
`web/`, and docs.
|
|
366
485
|
|
|
367
|
-
|
|
486
|
+
`scripts/install-husky.js` runs from `prepare`. It installs git hooks only when a
|
|
487
|
+
`.git` directory is present, so installing the package as a dependency does not
|
|
488
|
+
fail on husky.
|
|
368
489
|
|
|
369
490
|
---
|
|
370
491
|
|
|
371
|
-
##
|
|
492
|
+
## What this server prevents
|
|
372
493
|
|
|
373
|
-
-
|
|
374
|
-
-
|
|
375
|
-
-
|
|
376
|
-
-
|
|
377
|
-
-
|
|
494
|
+
- Committing without tests
|
|
495
|
+
- Committing with failing tests
|
|
496
|
+
- Committing without documentation
|
|
497
|
+
- Forgetting a workflow step
|
|
498
|
+
- Releasing without committing and pushing
|
|
378
499
|
|
|
379
500
|
---
|
|
380
501
|
|
|
381
|
-
##
|
|
502
|
+
## Tips
|
|
382
503
|
|
|
383
|
-
1.
|
|
384
|
-
2.
|
|
385
|
-
3.
|
|
386
|
-
4. `run_full_workflow`
|
|
387
|
-
5.
|
|
504
|
+
1. Always start with `start_task` — it sets the intent for the session
|
|
505
|
+
2. Do not skip tests unless it is genuinely impossible — use `skip_tests` with a clear reason
|
|
506
|
+
3. Call `get_workflow_status` whenever you lose track of the phase
|
|
507
|
+
4. `run_full_workflow` is a shortcut for simple tasks where all the data is already known
|
|
508
|
+
5. For docs-only work, use `skip_release` rather than forcing a release
|
|
388
509
|
|
|
389
510
|
---
|
|
390
511
|
|
|
391
|
-
##
|
|
512
|
+
## Troubleshooting
|
|
392
513
|
|
|
393
|
-
|
|
394
|
-
npm
|
|
395
|
-
```
|
|
514
|
+
**`ERR_MODULE_NOT_FOUND: Cannot find package 'dotenv'`** — dependencies are not
|
|
515
|
+
installed. Run `npm install` in the repo.
|
|
396
516
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
ℹ fail 0
|
|
401
|
-
ℹ skipped 2
|
|
402
|
-
```
|
|
517
|
+
**`gyp ERR! find VS` on Windows** — the native module is compiling from source
|
|
518
|
+
because there is no prebuild for your Node ABI. Either switch to a Node version
|
|
519
|
+
that has one, or install Visual Studio Build Tools.
|
|
403
520
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
SQLite adapter, runs.
|
|
521
|
+
**`EPERM ... better_sqlite3.node`** — a running process holds the file. Stop it,
|
|
522
|
+
then reinstall.
|
|
407
523
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
524
|
+
**OpenCode does not load the server** — check the config shape. V2 requires
|
|
525
|
+
`mcp.servers` and rejects `enabled`:
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
opencode mcp list # should show: dev-workflow connected
|
|
529
|
+
```
|
|
411
530
|
|
|
412
531
|
---
|
|
413
532
|
|
|
414
|
-
##
|
|
533
|
+
## Build
|
|
415
534
|
|
|
416
535
|
```bash
|
|
417
|
-
npm run build # → dist/index.mjs (optimized bundle)
|
|
418
|
-
npm run web # → dashboard
|
|
536
|
+
npm run build # → dist/index.mjs (optimized bundle, used for npm distribution)
|
|
537
|
+
npm run web # → dashboard on http://localhost:3000
|
|
419
538
|
```
|
|
420
539
|
|
|
421
|
-
|
|
540
|
+
For MCP use, point the client at `index.js` directly — startup is faster than the
|
|
541
|
+
bundled `dist/index.mjs`.
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## Homelab Development Guide
|
|
546
|
+
|
|
547
|
+
This repository also documents the internal **programinglive** homelab: Tailscale
|
|
548
|
+
plus SSH across three machines (MMCH, HELLOWORLD, Mac).
|
|
549
|
+
|
|
550
|
+
| Document | Contents |
|
|
551
|
+
|---|---|
|
|
552
|
+
| [docs/homelab/README.md](./docs/homelab/README.md) | Overview and quick reference |
|
|
553
|
+
| [docs/homelab/infra-tailnet-ssh.md](./docs/homelab/infra-tailnet-ssh.md) | Tailnet and SSH setup |
|
|
554
|
+
| [docs/homelab/cli-tools.md](./docs/homelab/cli-tools.md) | CLI tools per machine |
|
|
555
|
+
| [docs/homelab/ci-runner.md](./docs/homelab/ci-runner.md) | Self-hosted CI runner on MMCH |
|
|
556
|
+
| [docs/homelab/security-rules.md](./docs/homelab/security-rules.md) | Security rules |
|
|
557
|
+
| [.agent/workflows/homelab-setup.md](./.agent/workflows/homelab-setup.md) | MCP workflow for onboarding a new machine |
|
|
558
|
+
|
|
559
|
+
> ⚠️ This repository is **private**. Do not share it publicly.
|
|
422
560
|
|
|
423
561
|
---
|
|
424
562
|
|
|
425
|
-
##
|
|
563
|
+
## License
|
|
426
564
|
|
|
427
565
|
MIT
|