secufusion-mcp 1.0.6 → 1.0.7
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 +64 -205
- package/index.js +107 -161
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
| Tool | Phase | What it does |
|
|
16
16
|
|---|---|---|
|
|
17
|
-
| `
|
|
17
|
+
| `manage_branch_state` | Planning & Execution | Manages a `.secufusion-state.json` tracker tied to the active Git branch |
|
|
18
18
|
| `log_rejected_pattern` | Course Correction | Records bad patterns to `.rejected-patterns.json` so they are never repeated |
|
|
19
19
|
| `run_pre_pr_checks` | PR Handoff | Discovers modified microservices across the workspace and gates PRs via AST-level linters + structural checks |
|
|
20
20
|
|
|
@@ -102,69 +102,40 @@ Open Cline settings → MCP Servers → Add:
|
|
|
102
102
|
|
|
103
103
|
## Tools Reference
|
|
104
104
|
|
|
105
|
-
### 1.
|
|
105
|
+
### 1. manage_branch_state
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
Manages a structured JSON state file (`.secufusion-state.json`) keyed to the current Git branch. This replaces unstructured Markdown parsing and ensures the AI can resume perfectly.
|
|
108
108
|
|
|
109
109
|
**Parameters:**
|
|
110
110
|
|
|
111
111
|
| Parameter | Type | Required | Description |
|
|
112
112
|
|---|---|---|---|
|
|
113
|
-
| `action` |
|
|
114
|
-
| `task_description` | string |
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
Ask your AI assistant:
|
|
123
|
-
"Start working on WI-1042: Add tenant-scoped audit log export to CSV.
|
|
124
|
-
Reference file: src/services/AuditService.java"
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
The AI will call:
|
|
113
|
+
| `action` | string | Yes | `initialize`, `read`, or `update` |
|
|
114
|
+
| `task_description` | string | No | (For `initialize`) Summary of the work |
|
|
115
|
+
| `pending_acs` | array | No | Array of strings for pending tasks (For `initialize`/`update`) |
|
|
116
|
+
| `completed_acs` | array | No | Array of completed AC strings (For `update`) |
|
|
117
|
+
| `next_step` | string | No | **CRITICAL for `update`**: A clear instruction on what to do next to allow instant resumption. |
|
|
118
|
+
| `reference_file_path` | string | No | Path to a reference file with standard coding patterns |
|
|
119
|
+
|
|
120
|
+
**Example — Starting a new task:**
|
|
128
121
|
```json
|
|
129
122
|
{
|
|
130
|
-
"action": "
|
|
131
|
-
"task_description": "
|
|
132
|
-
"
|
|
133
|
-
"reference_file_path": "src/services/AuditService.java"
|
|
123
|
+
"action": "initialize",
|
|
124
|
+
"task_description": "Implement DeviceActivitySummaryDTO with browserUsage map.",
|
|
125
|
+
"pending_acs": ["Add browserUsage map", "Add unit tests", "Update OpenAPI"]
|
|
134
126
|
}
|
|
135
127
|
```
|
|
136
128
|
|
|
137
|
-
|
|
138
|
-
- Task description
|
|
139
|
-
- Pre-filled guardrails checklist
|
|
140
|
-
- Acceptance criteria placeholders
|
|
141
|
-
- Reference code snippet
|
|
142
|
-
- Session log
|
|
143
|
-
|
|
144
|
-
**Example — Tick off completed work:**
|
|
145
|
-
|
|
146
|
-
```
|
|
147
|
-
Ask: "Mark the tenantId scoping and unit tests as done in the spec."
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
The AI will call:
|
|
129
|
+
**Example — Updating progress:**
|
|
151
130
|
```json
|
|
152
131
|
{
|
|
153
132
|
"action": "update",
|
|
154
|
-
"
|
|
133
|
+
"pending_acs": ["Update OpenAPI"],
|
|
134
|
+
"completed_acs": ["Add browserUsage map", "Add unit tests"],
|
|
135
|
+
"next_step": "Generate the OpenAPI YAML for DeviceActivitySummaryDTO and test generation."
|
|
155
136
|
}
|
|
156
137
|
```
|
|
157
138
|
|
|
158
|
-
**Example — Read current spec:**
|
|
159
|
-
|
|
160
|
-
```
|
|
161
|
-
Ask: "Where did we leave off? Read the current spec."
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
```json
|
|
165
|
-
{ "action": "read" }
|
|
166
|
-
```
|
|
167
|
-
|
|
168
139
|
---
|
|
169
140
|
|
|
170
141
|
### 2. `log_rejected_pattern`
|
|
@@ -219,13 +190,13 @@ This writes to `.rejected-patterns.json`:
|
|
|
219
190
|
|---|---|---|---|
|
|
220
191
|
| `work_item_id` | string | Yes | Azure DevOps work item ID |
|
|
221
192
|
| `root_dir` | string | No | Directory to scan (defaults to cwd) |
|
|
222
|
-
| `skip_checks` | array | No | `
|
|
193
|
+
| `skip_checks` | array | No | `branch_state`, `console_logs`, `tenant_isolation`, `hardcoded_urls`, `flyway_migrations` |
|
|
223
194
|
|
|
224
195
|
**Guardrail checks:**
|
|
225
196
|
|
|
226
197
|
| Check | Fails when |
|
|
227
198
|
|---|---|
|
|
228
|
-
| **
|
|
199
|
+
| **STATE** | `.secufusion-state.json` has pending ACs for the active branch |
|
|
229
200
|
| **LINTING** | AST linters (ESLint, Checkstyle) fail in any discovered microservice. Fix natively, do not suppress! (Falls back to regex if no linters exist) |
|
|
230
201
|
| **SECURITY** | Hardcoded IPs or `uat.*` / `prod.*` / `staging.*` URLs in source or config files |
|
|
231
202
|
| **TENANT ISOLATION** | `*Repository.java` files contain query methods or `@Query` annotations that do NOT include `tenant` filtering |
|
|
@@ -248,14 +219,14 @@ The AI will call:
|
|
|
248
219
|
```
|
|
249
220
|
# ✅ Pre-PR Checks PASSED — Work Item #1042
|
|
250
221
|
|
|
251
|
-
✅ [
|
|
222
|
+
✅ [STATE] All branch tasks completed.
|
|
252
223
|
✅ [LOGGING] No console.log / System.out.println found.
|
|
253
224
|
✅ [SECURITY] No hardcoded UAT/Prod IPs or environment URLs found.
|
|
254
225
|
✅ [FLYWAY] Flyway migration coverage looks good.
|
|
255
226
|
|
|
256
227
|
## PR Summary
|
|
257
228
|
- Work Item: #1042
|
|
258
|
-
-
|
|
229
|
+
- State: All pending ACs resolved ✅
|
|
259
230
|
- Ready to raise PR 🚀
|
|
260
231
|
```
|
|
261
232
|
|
|
@@ -263,7 +234,7 @@ The AI will call:
|
|
|
263
234
|
```
|
|
264
235
|
# ❌ Pre-PR Checks FAILED — Work Item #1042
|
|
265
236
|
|
|
266
|
-
❌ [
|
|
237
|
+
❌ [STATE] 3 pending AC(s) remain in .secufusion-state.json:
|
|
267
238
|
• Flyway SQL migration created for every modified JPA @Entity
|
|
268
239
|
• Unit tests written / updated for new business logic
|
|
269
240
|
• API contract updated if endpoints changed
|
|
@@ -284,7 +255,7 @@ These rules are enforced automatically — the AI will never violate them:
|
|
|
284
255
|
✅ No console.log() or System.out.println() in any source file
|
|
285
256
|
✅ No hardcoded UAT/Prod IPs or environment URLs
|
|
286
257
|
✅ Every JPA @Entity change accompanied by a Flyway .sql migration
|
|
287
|
-
✅
|
|
258
|
+
✅ State in .secufusion-state.json must be fully resolved before PR is raised
|
|
288
259
|
```
|
|
289
260
|
|
|
290
261
|
---
|
|
@@ -293,7 +264,7 @@ These rules are enforced automatically — the AI will never violate them:
|
|
|
293
264
|
|
|
294
265
|
| File | Description |
|
|
295
266
|
|---|---|
|
|
296
|
-
| `.
|
|
267
|
+
| `.secufusion-state.json` | Living task blueprint — tracks pending/completed ACs per branch |
|
|
297
268
|
| `.rejected-patterns.json` | Cumulative log of all rejected patterns across sessions |
|
|
298
269
|
|
|
299
270
|
> **Tip:** Commit both files to your repo so the entire team benefits from the shared knowledge.
|
|
@@ -306,11 +277,11 @@ These rules are enforced automatically — the AI will never violate them:
|
|
|
306
277
|
┌─────────────────────────────────────────────────────┐
|
|
307
278
|
│ SecuFusion MCP Workflow │
|
|
308
279
|
├──────────────┬──────────────────────────────────────┤
|
|
309
|
-
│ Phase 1 │
|
|
310
|
-
│ Planning │ →
|
|
280
|
+
│ Phase 1 │ manage_branch_state (action=init) │
|
|
281
|
+
│ Planning │ → Initializes .secufusion-state.json │
|
|
311
282
|
├──────────────┼──────────────────────────────────────┤
|
|
312
|
-
│ Phase 2 │
|
|
313
|
-
│ Execution │ →
|
|
283
|
+
│ Phase 2 │ manage_branch_state (action=update) │
|
|
284
|
+
│ Execution │ → Resolve ACs & define next_step │
|
|
314
285
|
├──────────────┼──────────────────────────────────────┤
|
|
315
286
|
│ Phase 3 │ log_rejected_pattern │
|
|
316
287
|
│ Correction │ → Record mistakes to avoid repeat │
|
|
@@ -373,46 +344,14 @@ Admin users must be forced through TOTP verification before
|
|
|
373
344
|
accessing any dashboard route. Exempt service accounts.
|
|
374
345
|
```
|
|
375
346
|
|
|
376
|
-
**The AI automatically calls `
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
Add MFA enforcement for admin users on login...
|
|
385
|
-
|
|
386
|
-
## Guardrails Checklist
|
|
387
|
-
- [ ] All DB queries / event payloads are scoped with `tenantId`
|
|
388
|
-
- [ ] No `console.log` / `System.out.println` left in source files
|
|
389
|
-
- [ ] No hardcoded UAT/Prod IPs or environment URLs
|
|
390
|
-
- [ ] Flyway SQL migration created for every modified JPA `@Entity`
|
|
391
|
-
- [ ] Unit tests written / updated for new business logic
|
|
392
|
-
- [ ] API contract updated if endpoints changed
|
|
393
|
-
|
|
394
|
-
## Acceptance Criteria
|
|
395
|
-
- [ ] AC-1: (fill in from ticket)
|
|
396
|
-
- [ ] AC-2: (fill in from ticket)
|
|
397
|
-
|
|
398
|
-
## Session Log
|
|
399
|
-
| 2026-08-26T14:35:00Z | Spec created |
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
You can paste as little or as much as you want:
|
|
403
|
-
|
|
404
|
-
```
|
|
405
|
-
# Minimal — one liner
|
|
406
|
-
WI-2847: MFA for admin login with TOTP.
|
|
407
|
-
|
|
408
|
-
# Full ticket paste — straight from Azure
|
|
409
|
-
WI-2847
|
|
410
|
-
Title: Add MFA enforcement for admin users
|
|
411
|
-
Description: Admin users must complete TOTP verification...
|
|
412
|
-
Acceptance Criteria:
|
|
413
|
-
- Given admin logs in, When MFA not done, Then redirect to /mfa
|
|
414
|
-
- Service accounts in GROUP_SERVICE_ACCOUNTS are exempt
|
|
415
|
-
Priority: High
|
|
347
|
+
**The AI automatically calls `manage_branch_state` and creates/initializes `.secufusion-state.json`:**
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"task": "WI-2847: Add MFA enforcement",
|
|
351
|
+
"pending_acs": ["AC-1: Auth flow", "AC-2: Redirects", "AC-3: Exemptions"],
|
|
352
|
+
"completed_acs": [],
|
|
353
|
+
"next_step": "Implement MFA logic in AuthController"
|
|
354
|
+
}
|
|
416
355
|
```
|
|
417
356
|
|
|
418
357
|
---
|
|
@@ -435,23 +374,20 @@ As you finish pieces of the feature, tell the AI:
|
|
|
435
374
|
|
|
436
375
|
```
|
|
437
376
|
I've finished the tenantId scoping on all queries and written the unit tests.
|
|
438
|
-
Update the
|
|
377
|
+
Update the state.
|
|
439
378
|
```
|
|
440
379
|
|
|
441
|
-
The AI calls `
|
|
442
|
-
|
|
443
|
-
```markdown
|
|
444
|
-
# Before
|
|
445
|
-
- [ ] All DB queries / event payloads are scoped with `tenantId`
|
|
446
|
-
- [ ] Unit tests written / updated for new business logic
|
|
380
|
+
The AI calls `manage_branch_state` with `action=update` and the state updates:
|
|
447
381
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
-
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"task": "WI-2847: Add MFA enforcement",
|
|
385
|
+
"pending_acs": ["AC-3: Exemptions"],
|
|
386
|
+
"completed_acs": ["Add tenantId scoping", "Add unit tests"],
|
|
387
|
+
"next_step": "Implement exemption logic for service accounts"
|
|
388
|
+
}
|
|
451
389
|
```
|
|
452
390
|
|
|
453
|
-
A new row is also appended to the Session Log automatically, so you always have an audit trail of progress across sessions.
|
|
454
|
-
|
|
455
391
|
---
|
|
456
392
|
|
|
457
393
|
### Step 5 — Course correction (if the AI does something wrong)
|
|
@@ -474,23 +410,6 @@ The AI **immediately** calls `log_rejected_pattern`:
|
|
|
474
410
|
}
|
|
475
411
|
```
|
|
476
412
|
|
|
477
|
-
This gets appended to `.rejected-patterns.json`:
|
|
478
|
-
|
|
479
|
-
```json
|
|
480
|
-
[
|
|
481
|
-
{
|
|
482
|
-
"id": 1,
|
|
483
|
-
"timestamp": "2026-08-26T15:10:00.000Z",
|
|
484
|
-
"category": "security",
|
|
485
|
-
"pattern": "Hardcoded staging URL https://staging.secufusion.io in source files",
|
|
486
|
-
"reason": "Must use @Value from application.properties...",
|
|
487
|
-
"file_context": "src/services/NotificationService.java"
|
|
488
|
-
}
|
|
489
|
-
]
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
From this point on, the AI checks this file before every suggestion — the mistake will never be repeated, even in future sessions.
|
|
493
|
-
|
|
494
413
|
---
|
|
495
414
|
|
|
496
415
|
### Step 6 — Pre-PR checks (PR Handoff phase)
|
|
@@ -508,101 +427,41 @@ The AI scans your entire repo and produces a report.
|
|
|
508
427
|
# ✅ Pre-PR Checks PASSED — Work Item #2847
|
|
509
428
|
|
|
510
429
|
Scanned directory: C:\projects\secufusion-backend
|
|
511
|
-
Timestamp: 2026-08-26T16:00:00.000Z
|
|
512
430
|
|
|
513
|
-
✅ [
|
|
431
|
+
✅ [STATE] All tasks resolved.
|
|
514
432
|
✅ [LOGGING] No console.log / System.out.println found.
|
|
515
433
|
✅ [SECURITY] No hardcoded UAT/Prod IPs or environment URLs found.
|
|
516
434
|
✅ [FLYWAY] Flyway migration coverage looks good.
|
|
517
435
|
|
|
518
436
|
## PR Summary
|
|
519
437
|
- Work Item: #2847
|
|
520
|
-
-
|
|
438
|
+
- State: All pending ACs resolved ✅
|
|
521
439
|
- Guardrails: tenant isolation secured, no debug logs, no hardcoded URLs, Flyway covered ✅
|
|
522
440
|
- Ready to raise PR 🚀
|
|
523
|
-
|
|
524
|
-
🚫 Rejected Patterns Reminder (1 on record)
|
|
525
|
-
- [security] Hardcoded staging URL https://staging.secufusion.io in source files
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
**❌ Checks fail — you must fix before PR:**
|
|
529
|
-
```
|
|
530
|
-
# ❌ Pre-PR Checks FAILED — Work Item #2847
|
|
531
|
-
|
|
532
|
-
## Errors (must fix before PR)
|
|
533
|
-
|
|
534
|
-
❌ [SPEC] 2 unchecked item(s) in the spec:
|
|
535
|
-
• Flyway SQL migration created for every modified JPA @Entity
|
|
536
|
-
• API contract (OpenAPI / TS types) updated if endpoints changed
|
|
537
|
-
|
|
538
|
-
❌ [LINTING] 2 linter error(s) across workspace — fix natively in their respective microservices:
|
|
539
|
-
• [ESLint] apps/admin-dashboard/src/components/MfaSetup.tsx:47 — Unexpected console statement (no-console)
|
|
540
|
-
• [Checkstyle/Maven] Build failed: [ERROR] AuditService.java:[112] Line contains System.out.println
|
|
541
|
-
|
|
542
|
-
## Passed
|
|
543
|
-
✅ [SECURITY] No hardcoded UAT/Prod IPs or environment URLs found.
|
|
544
|
-
✅ [FLYWAY] Flyway migration coverage looks good.
|
|
545
441
|
```
|
|
546
442
|
|
|
547
|
-
Fix the issues, run checks again, and only raise the PR once it's fully green.
|
|
548
|
-
|
|
549
|
-
---
|
|
550
|
-
|
|
551
|
-
### Quick cheat sheet
|
|
552
|
-
|
|
553
|
-
| What you want to do | What to say to the AI |
|
|
554
|
-
|---|---|
|
|
555
|
-
| Start a new task | `WI-XXXX: [paste description from Azure]` |
|
|
556
|
-
| Check where you left off | `Read the current spec` |
|
|
557
|
-
| Mark work as done | `Mark the tenantId scoping as complete in the spec` |
|
|
558
|
-
| Record a mistake | `Never do [X] again because [Y]` |
|
|
559
|
-
| Gate the PR | `Run pre-PR checks for WI-XXXX` |
|
|
560
|
-
| Resume after a break | `What's left on the current task?` |
|
|
561
|
-
| Use a reference file | `WI-XXXX: [desc]. Reference: src/services/MyService.java` |
|
|
562
|
-
|
|
563
443
|
---
|
|
564
444
|
|
|
565
|
-
## How It
|
|
566
|
-
|
|
567
|
-
There are two layers that need to be in place for the MCP server to work automatically.
|
|
568
|
-
|
|
569
|
-
### Layer 1 — `mcp_config.json` (makes tools available)
|
|
570
|
-
|
|
571
|
-
This tells the IDE to start the MCP server process when it launches. The tools become registered and ready, but nothing calls them yet.
|
|
572
|
-
|
|
573
|
-
```
|
|
574
|
-
IDE starts → reads mcp_config.json → spawns node index.js → tools registered
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
### Layer 2 — `AGENTS.md` (the invoker — tells AI when to call each tool)
|
|
578
|
-
|
|
579
|
-
Without this, the tools sit idle. The AI doesn't know when to invoke them.
|
|
580
|
-
Create `.agents/AGENTS.md` in your project root:
|
|
445
|
+
## How It Works (The 2 Layers)
|
|
581
446
|
|
|
582
|
-
|
|
583
|
-
# SecuFusion MCP Workflow Rules
|
|
447
|
+
To prevent AI amnesia or generic bad habits across your projects, the SecuFusion MCP relies on structured branch-aware JSON and native AST tooling.
|
|
584
448
|
|
|
585
|
-
|
|
449
|
+
## Phase 0 — Resume (TRIGGER: new session, switching branches, or user says "resume" or "continue")
|
|
450
|
+
- Call `manage_branch_state` with `action: "read"`. The tool will automatically detect the current Git branch and return the structured JSON state for that specific branch. Do not re-read requirements; immediately begin executing the item explicitly listed in the `next_step` field.
|
|
586
451
|
|
|
587
452
|
## Phase 1 — Planning (TRIGGER: user assigns a task or work item)
|
|
588
|
-
-
|
|
589
|
-
- Determine which microservices are likely affected. Pass a `reference_file_path` from each to observe exact coding patterns.
|
|
590
|
-
- Pass the user's requirements into `task_description` to generate the workspace-level `.current-task-spec.md` blueprint.
|
|
591
|
-
- Wait for the spec to be generated before writing any code.
|
|
453
|
+
- Call `manage_branch_state` with `action: "initialize"`. Pass the `task_description` and `reference_file_path`. The tool will generate a branch-bound JSON state file tracking the Acceptance Criteria (ACs) as a structured array of pending tasks, keyed to your active Git branch.
|
|
592
454
|
|
|
593
|
-
## Phase 2 — Execution (TRIGGER: as you complete
|
|
594
|
-
-
|
|
595
|
-
-
|
|
596
|
-
- If you are resuming a session, always call `manage_feature_spec` with `action=read` first to know exactly where you left off.
|
|
455
|
+
## Phase 2 — Execution (TRIGGER: as you complete ACs, or before ending a session/response)
|
|
456
|
+
- Call `manage_branch_state` with `action: "update"`. Move completed items from `pending_acs` to `completed_acs`.
|
|
457
|
+
- CRITICAL: You must explicitly write a clear, actionable instruction into the `next_step` parameter (e.g., "Implement browserUsage map in DeviceActivitySummaryDTO"). This ensures your future self can resume instantly without parsing the full spec.
|
|
597
458
|
|
|
598
459
|
## Phase 3 — Course Correction (TRIGGER: user corrects you or rejects an approach)
|
|
599
|
-
- Immediately call `log_rejected_pattern`.
|
|
600
|
-
- Pass the bad `pattern` you used and the `reason`/correction the user provided.
|
|
601
|
-
- Always check `.rejected-patterns.json` implicitly before suggesting architectural choices to ensure you never repeat past mistakes.
|
|
460
|
+
- Immediately call `log_rejected_pattern`. Pass the bad `pattern` and the `reason`. Always check `.rejected-patterns.json` implicitly before suggesting architectural choices.
|
|
602
461
|
|
|
603
|
-
## Phase 4 — PR Handoff (TRIGGER: user says "prepare PR"
|
|
604
|
-
- Call `run_pre_pr_checks` with the Azure DevOps `
|
|
605
|
-
- If
|
|
462
|
+
## Phase 4 — PR Handoff (TRIGGER: user says "prepare PR" or "run checks")
|
|
463
|
+
- Call `run_pre_pr_checks` with the Azure DevOps `workItemId`. The tool checks the structured branch state to ensure `pending_acs` is empty, then discovers modified microservices to run native linters, Flyway checks, and the Tenant-Isolation scanner.
|
|
464
|
+
- If an error is thrown, YOU MUST navigate to that microservice, FIX THE ERROR natively, and rerun until the workspace passes.
|
|
606
465
|
|
|
607
466
|
## Guardrails (enforce always, no exceptions)
|
|
608
467
|
- TENANT SAFETY IS AUTOMATED: The PR Gatekeeper actively scans all `*Repository.java` files. If you write a database query (derived method or `@Query`) that does not explicitly filter by `tenantId` or contain the word `tenant`, the PR will be blocked. Write secure, tenant-isolated queries on your first attempt.
|
|
@@ -620,9 +479,9 @@ mcp_config.json → starts the server (tools available)
|
|
|
620
479
|
↓
|
|
621
480
|
You say: "WI-1042: Add audit log export"
|
|
622
481
|
↓
|
|
623
|
-
AI reads AGENTS.md → Phase 1 triggered → calls
|
|
482
|
+
AI reads AGENTS.md → Phase 1 triggered → calls manage_branch_state
|
|
624
483
|
↓
|
|
625
|
-
.
|
|
484
|
+
.secufusion-state.json initialized for your Git branch
|
|
626
485
|
```
|
|
627
486
|
|
|
628
487
|
### Reusing across projects
|
|
@@ -648,14 +507,14 @@ Once both layers are in place, you interact completely naturally:
|
|
|
648
507
|
| Situation | What you say |
|
|
649
508
|
|---|---|
|
|
650
509
|
| 🆕 New task | `WI-XXXX: [paste description from Azure]` |
|
|
651
|
-
| ✅ Done a chunk | `Done with the tenantId scoping, update the
|
|
510
|
+
| ✅ Done a chunk | `Done with the tenantId scoping, update the state` |
|
|
652
511
|
| ❌ AI did something wrong | `Don't do X, do Y instead` |
|
|
653
512
|
| 🚀 Ready for PR | `Run checks for WI-XXXX` or `Prepare PR` |
|
|
654
513
|
| 🔄 Resuming after a break | `What's left?` or `Resume the current task` |
|
|
655
514
|
|
|
656
515
|
**Before `AGENTS.md`:** You had to remember which tool to invoke and when.
|
|
657
516
|
|
|
658
|
-
**After `AGENTS.md`:** You just describe work. The AI follows the
|
|
517
|
+
**After `AGENTS.md`:** You just describe work. The AI follows the 5-phase workflow automatically — no commands, no syntax, no manual tool calls.
|
|
659
518
|
|
|
660
519
|
---
|
|
661
520
|
|
package/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import { execSync } from "child_process";
|
|
|
22
22
|
// ─────────────────────────────────────────────
|
|
23
23
|
// Constants / helpers
|
|
24
24
|
// ─────────────────────────────────────────────
|
|
25
|
-
const
|
|
25
|
+
const STATE_FILE = ".secufusion-state.json";
|
|
26
26
|
const REJECTED_FILE = ".rejected-patterns.json";
|
|
27
27
|
/** Resolve a path relative to cwd (where the MCP server is invoked). */
|
|
28
28
|
function resolve(file) {
|
|
@@ -40,56 +40,13 @@ function writeFile(filePath, content) {
|
|
|
40
40
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
41
41
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
: "";
|
|
51
|
-
const refSection = referenceSnippet
|
|
52
|
-
? `\n## Reference Code Pattern\n\n> Extracted from the provided reference file to guide implementation style.\n\n\`\`\`\n${referenceSnippet.slice(0, 800)}\n\`\`\`\n`
|
|
53
|
-
: "";
|
|
54
|
-
return `# SecuFusion Feature Spec
|
|
55
|
-
<!-- AUTO-GENERATED — do not remove the checkboxes; they are used by run_pre_pr_checks -->
|
|
56
|
-
|
|
57
|
-
**Generated:** ${now}
|
|
58
|
-
${wiLine}
|
|
59
|
-
## Task Description
|
|
60
|
-
|
|
61
|
-
${taskDescription}
|
|
62
|
-
${refSection}
|
|
63
|
-
## Guardrails Checklist
|
|
64
|
-
|
|
65
|
-
- [ ] All DB queries / event payloads are scoped with \`tenantId\`
|
|
66
|
-
- [ ] No \`console.log\` / \`System.out.println\` left in source files
|
|
67
|
-
- [ ] No hardcoded UAT/Prod IPs or environment URLs
|
|
68
|
-
- [ ] Flyway SQL migration created for every modified JPA \`@Entity\`
|
|
69
|
-
- [ ] Unit tests written / updated for new business logic
|
|
70
|
-
- [ ] API contract (OpenAPI / TS types) updated if endpoints changed
|
|
71
|
-
|
|
72
|
-
## Acceptance Criteria
|
|
73
|
-
|
|
74
|
-
- [ ] AC-1: (fill in from ticket)
|
|
75
|
-
- [ ] AC-2: (fill in from ticket)
|
|
76
|
-
- [ ] AC-3: (fill in from ticket)
|
|
77
|
-
|
|
78
|
-
## Implementation Notes
|
|
79
|
-
|
|
80
|
-
> Add architecture decisions, edge-cases, and external dependency notes here.
|
|
81
|
-
|
|
82
|
-
## Session Log
|
|
83
|
-
|
|
84
|
-
| Timestamp | Update |
|
|
85
|
-
|-----------|--------|
|
|
86
|
-
| ${now} | Spec created |
|
|
87
|
-
`;
|
|
88
|
-
}
|
|
89
|
-
function appendSessionLog(existing, update) {
|
|
90
|
-
const ts = new Date().toISOString();
|
|
91
|
-
const logRow = `| ${ts} | ${update} |`;
|
|
92
|
-
return existing.replace(/(\| [^\n]+ \|)\s*$/, `$1\n${logRow}`);
|
|
43
|
+
function getCurrentBranch(cwd) {
|
|
44
|
+
try {
|
|
45
|
+
return execSync("git rev-parse --abbrev-ref HEAD", { cwd, stdio: "pipe" }).toString().trim();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return "unknown-branch";
|
|
49
|
+
}
|
|
93
50
|
}
|
|
94
51
|
// ─────────────────────────────────────────────
|
|
95
52
|
// run_pre_pr_checks helpers
|
|
@@ -100,34 +57,29 @@ const CONSOLE_LOG_REGEX = [
|
|
|
100
57
|
];
|
|
101
58
|
const HARDCODED_URL_PATTERN = /(?:https?:\/\/)?(?:\d{1,3}\.){3}\d{1,3}(?::\d+)?|(?:uat|prod|staging|preprod)\.[a-z0-9.-]+\.[a-z]{2,}/gi;
|
|
102
59
|
function walkDir(dir, exts) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (entry.isDirectory()) {
|
|
109
|
-
if (["node_modules", ".git", "dist", "build", "target", ".idea"].includes(entry.name))
|
|
60
|
+
let results = [];
|
|
61
|
+
try {
|
|
62
|
+
const list = fs.readdirSync(dir);
|
|
63
|
+
for (const file of list) {
|
|
64
|
+
if (file === "node_modules" || file === ".git" || file === "dist" || file === "target")
|
|
110
65
|
continue;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
66
|
+
const filePath = path.join(dir, file);
|
|
67
|
+
const stat = fs.statSync(filePath);
|
|
68
|
+
if (stat && stat.isDirectory()) {
|
|
69
|
+
results = results.concat(walkDir(filePath, exts));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
if (exts.some(ext => file.endsWith(ext))) {
|
|
73
|
+
results.push(filePath);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
115
76
|
}
|
|
116
77
|
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
/* ignore */
|
|
80
|
+
}
|
|
117
81
|
return results;
|
|
118
82
|
}
|
|
119
|
-
function checkSpecFile() {
|
|
120
|
-
const content = readFileSafe(resolve(SPEC_FILE));
|
|
121
|
-
if (!content)
|
|
122
|
-
return { unchecked: [], missing: true };
|
|
123
|
-
const unchecked = [];
|
|
124
|
-
for (const line of content.split("\n")) {
|
|
125
|
-
const match = line.match(/^- \[ \] (.+)/);
|
|
126
|
-
if (match)
|
|
127
|
-
unchecked.push(match[1].trim());
|
|
128
|
-
}
|
|
129
|
-
return { unchecked, missing: false };
|
|
130
|
-
}
|
|
131
83
|
// ── AST-level linting via ESLint (preferred over regex for JS/TS) ─────────────
|
|
132
84
|
function runEslint(rootDir) {
|
|
133
85
|
// Check if ESLint is configured in the project
|
|
@@ -304,128 +256,113 @@ const server = new McpServer({
|
|
|
304
256
|
name: "secufusion-mcp",
|
|
305
257
|
version: "1.0.0",
|
|
306
258
|
});
|
|
307
|
-
// ─── Tool 1:
|
|
308
|
-
server.tool("
|
|
309
|
-
|
|
310
|
-
|
|
259
|
+
// ─── Tool 1: manage_branch_state ─────────────────────────────────────────────
|
|
260
|
+
server.tool("manage_branch_state", "Manage the structured JSON state for the current Git branch. Use to initialize new tasks, track pending/completed ACs, and leave explicit next steps for resuming.", {
|
|
261
|
+
action: z
|
|
262
|
+
.enum(["initialize", "read", "update"])
|
|
263
|
+
.describe("Action to perform on the branch state."),
|
|
311
264
|
task_description: z
|
|
312
265
|
.string()
|
|
313
266
|
.optional()
|
|
314
|
-
.describe("
|
|
315
|
-
|
|
267
|
+
.describe("Used with 'initialize' to describe the task."),
|
|
268
|
+
reference_file_path: z
|
|
316
269
|
.string()
|
|
317
270
|
.optional()
|
|
318
|
-
.describe("
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
.string()
|
|
271
|
+
.describe("Used with 'initialize' to note a reference file."),
|
|
272
|
+
pending_acs: z
|
|
273
|
+
.array(z.string())
|
|
322
274
|
.optional()
|
|
323
|
-
.describe("
|
|
324
|
-
|
|
275
|
+
.describe("Array of ACs. Used with 'initialize' or 'update' to set pending tasks."),
|
|
276
|
+
completed_acs: z
|
|
277
|
+
.array(z.string())
|
|
278
|
+
.optional()
|
|
279
|
+
.describe("Array of completed ACs. Used with 'update'."),
|
|
280
|
+
next_step: z
|
|
325
281
|
.string()
|
|
326
282
|
.optional()
|
|
327
|
-
.describe("
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if (!content) {
|
|
340
|
-
return {
|
|
341
|
-
content: [
|
|
342
|
-
{
|
|
343
|
-
type: "text",
|
|
344
|
-
text: `No spec file found at ${SPEC_FILE}. Run with action='create' to generate one.`,
|
|
345
|
-
},
|
|
346
|
-
],
|
|
347
|
-
};
|
|
283
|
+
.describe("Explicit actionable instruction for resuming. Required for 'update'."),
|
|
284
|
+
}, async ({ action, task_description, reference_file_path, pending_acs, completed_acs, next_step }) => {
|
|
285
|
+
const cwd = process.cwd();
|
|
286
|
+
const branch = getCurrentBranch(cwd);
|
|
287
|
+
const statePath = path.resolve(STATE_FILE);
|
|
288
|
+
let state = {};
|
|
289
|
+
if (fs.existsSync(statePath)) {
|
|
290
|
+
try {
|
|
291
|
+
state = JSON.parse(readFileSafe(statePath) || "{}");
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
state = {};
|
|
348
295
|
}
|
|
349
|
-
return { content: [{ type: "text", text: content }] };
|
|
350
296
|
}
|
|
351
|
-
if (action === "
|
|
352
|
-
|
|
297
|
+
if (action === "read") {
|
|
298
|
+
const branchState = state[branch];
|
|
299
|
+
if (!branchState) {
|
|
353
300
|
return {
|
|
354
301
|
content: [
|
|
355
302
|
{
|
|
356
303
|
type: "text",
|
|
357
|
-
text:
|
|
304
|
+
text: `No state found for branch '${branch}'. Please initialize first.`,
|
|
358
305
|
},
|
|
359
306
|
],
|
|
360
|
-
isError: true,
|
|
361
307
|
};
|
|
362
308
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
309
|
+
return {
|
|
310
|
+
content: [{ type: "text", text: JSON.stringify(branchState, null, 2) }],
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (action === "initialize") {
|
|
314
|
+
state[branch] = {
|
|
315
|
+
task_description: task_description || "",
|
|
316
|
+
reference_file_path: reference_file_path || "",
|
|
317
|
+
pending_acs: pending_acs || [],
|
|
318
|
+
completed_acs: [],
|
|
319
|
+
next_step: "Review requirements and begin implementation.",
|
|
320
|
+
last_updated: new Date().toISOString()
|
|
321
|
+
};
|
|
322
|
+
writeFile(statePath, JSON.stringify(state, null, 2));
|
|
371
323
|
return {
|
|
372
324
|
content: [
|
|
373
325
|
{
|
|
374
326
|
type: "text",
|
|
375
|
-
text: `✅
|
|
376
|
-
`Review and fill in the Acceptance Criteria before starting implementation.\n\n` +
|
|
377
|
-
`---\n\n${spec}`,
|
|
327
|
+
text: `✅ Branch state initialized for '${branch}'.\n\nState:\n${JSON.stringify(state[branch], null, 2)}`,
|
|
378
328
|
},
|
|
379
329
|
],
|
|
380
330
|
};
|
|
381
331
|
}
|
|
382
332
|
if (action === "update") {
|
|
383
|
-
if (!
|
|
333
|
+
if (!state[branch]) {
|
|
384
334
|
return {
|
|
335
|
+
isError: true,
|
|
385
336
|
content: [
|
|
386
|
-
{
|
|
387
|
-
type: "text",
|
|
388
|
-
text: "ERROR: `update_content` is required when action='update'.",
|
|
389
|
-
},
|
|
337
|
+
{ type: "text", text: `No state found for branch '${branch}'. Initialize first.` },
|
|
390
338
|
],
|
|
391
|
-
isError: true,
|
|
392
339
|
};
|
|
393
340
|
}
|
|
394
|
-
|
|
395
|
-
if (!existing) {
|
|
341
|
+
if (!next_step) {
|
|
396
342
|
return {
|
|
343
|
+
isError: true,
|
|
397
344
|
content: [
|
|
398
|
-
{
|
|
399
|
-
type: "text",
|
|
400
|
-
text: `ERROR: No spec file found at ${SPEC_FILE}. Create one first with action='create'.`,
|
|
401
|
-
},
|
|
345
|
+
{ type: "text", text: "ERROR: 'next_step' is required when updating." },
|
|
402
346
|
],
|
|
403
|
-
isError: true,
|
|
404
347
|
};
|
|
405
348
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
updated = appendSessionLog(updated, update_content.replace(/\n/g, " ").slice(0, 120));
|
|
415
|
-
writeFile(specPath, updated);
|
|
349
|
+
if (pending_acs)
|
|
350
|
+
state[branch].pending_acs = pending_acs;
|
|
351
|
+
if (completed_acs)
|
|
352
|
+
state[branch].completed_acs = completed_acs;
|
|
353
|
+
state[branch].next_step = next_step;
|
|
354
|
+
state[branch].last_updated = new Date().toISOString();
|
|
355
|
+
writeFile(statePath, JSON.stringify(state, null, 2));
|
|
416
356
|
return {
|
|
417
357
|
content: [
|
|
418
358
|
{
|
|
419
359
|
type: "text",
|
|
420
|
-
text: `✅
|
|
360
|
+
text: `✅ Branch state updated for '${branch}'. Next step recorded:\n> ${next_step}`,
|
|
421
361
|
},
|
|
422
362
|
],
|
|
423
363
|
};
|
|
424
364
|
}
|
|
425
|
-
return {
|
|
426
|
-
content: [{ type: "text", text: "Unknown action." }],
|
|
427
|
-
isError: true,
|
|
428
|
-
};
|
|
365
|
+
return { content: [{ type: "text", text: "Invalid action." }] };
|
|
429
366
|
});
|
|
430
367
|
// ─── Tool 2: log_rejected_pattern ────────────────────────────────────────────
|
|
431
368
|
server.tool("log_rejected_pattern", "Record a coding pattern that was rejected by the team so it is never repeated. " +
|
|
@@ -515,23 +452,32 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks acr
|
|
|
515
452
|
const errors = [];
|
|
516
453
|
const warnings = [];
|
|
517
454
|
const passed = [];
|
|
518
|
-
// ── 1.
|
|
455
|
+
// ── 1. Branch state check ─────────────────────────────────────────────
|
|
519
456
|
if (!skip_checks.includes("spec_boxes")) {
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
457
|
+
const branch = getCurrentBranch(scanRoot);
|
|
458
|
+
const statePath = path.resolve(scanRoot, STATE_FILE);
|
|
459
|
+
let branchState = null;
|
|
460
|
+
if (fs.existsSync(statePath)) {
|
|
461
|
+
try {
|
|
462
|
+
const stateData = JSON.parse(readFileSafe(statePath) || "{}");
|
|
463
|
+
branchState = stateData[branch];
|
|
464
|
+
}
|
|
465
|
+
catch { }
|
|
466
|
+
}
|
|
467
|
+
if (!branchState) {
|
|
468
|
+
errors.push(`❌ [STATE] No branch state found for '${branch}' in \`${STATE_FILE}\`. ` +
|
|
469
|
+
`Call \`manage_branch_state\` with action='initialize' before raising a PR.`);
|
|
524
470
|
}
|
|
525
|
-
else if (
|
|
526
|
-
errors.push(`❌ [
|
|
527
|
-
|
|
471
|
+
else if (branchState.pending_acs && branchState.pending_acs.length > 0) {
|
|
472
|
+
errors.push(`❌ [STATE] ${branchState.pending_acs.length} pending AC(s) remain for branch '${branch}':\n` +
|
|
473
|
+
branchState.pending_acs.map((u) => ` • ${u}`).join("\n"));
|
|
528
474
|
}
|
|
529
475
|
else {
|
|
530
|
-
passed.push(
|
|
476
|
+
passed.push(`✅ [STATE] Branch state for '${branch}' is clear (no pending ACs).`);
|
|
531
477
|
}
|
|
532
478
|
}
|
|
533
479
|
else {
|
|
534
|
-
warnings.push("⚠️ [
|
|
480
|
+
warnings.push("⚠️ [STATE] Branch state check SKIPPED (manually bypassed).");
|
|
535
481
|
}
|
|
536
482
|
// ── 2. AST-level linting (Workspace Discovery → ESLint/Checkstyle → regex fallback)
|
|
537
483
|
if (!skip_checks.includes("console_logs")) {
|