secufusion-mcp 1.2.7 → 2.0.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/.claude/settings.json +9 -0
- package/.claude-plugin/plugin.json +5 -6
- package/.mcp.json +19 -0
- package/{AGENTS.md → agents/AGENTS.md} +3 -3
- package/agents/claude.md +50 -0
- package/agents/coder.md +80 -0
- package/agents/planner.md +354 -0
- package/agents/reviewer.md +384 -0
- package/commands/analyze.md +15 -0
- package/commands/blast-radius.md +14 -0
- package/commands/map-architecture.md +10 -0
- package/commands/map-services.md +3 -0
- package/commands/watch-dna.md +9 -0
- package/hooks/hooks.json +17 -0
- package/hooks/knowledge-drift.sh +48 -0
- package/mcp/dist/parsers/api.js +30 -0
- package/mcp/dist/parsers/backend-node.js +26 -0
- package/mcp/dist/parsers/config.js +74 -0
- package/mcp/dist/parsers/database.js +40 -0
- package/mcp/dist/parsers/domain.js +21 -0
- package/mcp/dist/parsers/events.js +36 -0
- package/mcp/dist/parsers/frontend.js +34 -0
- package/mcp/dist/parsers/infra.js +42 -0
- package/mcp/dist/parsers/patterns.js +49 -0
- package/mcp/dist/parsers/security.js +16 -0
- package/{index.js → mcp/dist/server.js} +408 -4
- package/package.json +19 -15
- package/scripts/sfn-pr-check.js +108 -0
- package/scripts/sfn-pr-check.ts +121 -0
- package/scripts/utils.js +29 -0
- package/scripts/utils.ts +31 -0
- package/.secufusion-project-spec.json +0 -1068
- /package/commands/{code.md → sfn-code.md} +0 -0
- /package/commands/{init.md → sfn-init.md} +0 -0
- /package/commands/{plan.md → sfn-plan.md} +0 -0
- /package/commands/{review.md → sfn-review.md} +0 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
## Phase 4 — PR Handoff
|
|
2
|
+
(TRIGGER: developer says "prepare PR", "run checks", or "ready to merge")
|
|
3
|
+
|
|
4
|
+
### MANDATORY sequence — zero exceptions
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
STEP 1: call run_pre_pr_checks_with_reviewer_agent(work_item_id: <id>)
|
|
8
|
+
— Tier 1: 5 mechanical checks
|
|
9
|
+
P1 TENANT_ISOLATION — every Repository query scoped to tenantId
|
|
10
|
+
P2 N_PLUS_ONE — no repo calls inside loops
|
|
11
|
+
P3 MISSING_INDEX — new query columns have migration index
|
|
12
|
+
P4 KAFKA_SYNC — no sync work inside @KafkaListener
|
|
13
|
+
P5 EARLY_RETURN — no early returns (single-exit rule)
|
|
14
|
+
(Note: If a mechanical check is a false positive, pass the `suppressions` parameter to the tool with `{ check, method, reason }` instead of ignoring it.)
|
|
15
|
+
— Tier 2: AI File Reviewer (only after P1–P5 pass)
|
|
16
|
+
Checks: hardcoded URLs, missing @Transactional(readOnly),
|
|
17
|
+
missing @PreAuthorize, debug statements, exception swallowing,
|
|
18
|
+
TS `any` overuse, missing Flyway for @Entity, cross-service timeouts
|
|
19
|
+
— Tier 3: Context Reviewer (only after Tier 2 passes)
|
|
20
|
+
Reads: spec.json, progress.json, decisions.json, scenarios.json,
|
|
21
|
+
files-touched.json, project-spec golden_rules, rejected patterns
|
|
22
|
+
Cross-references: AC coverage, decision drift, scope creep,
|
|
23
|
+
rejected patterns in file content, test coverage gaps, golden rules
|
|
24
|
+
— Verdict: APPROVED | CHANGES_REQUESTED | DISCUSS
|
|
25
|
+
→ If CHANGES_REQUESTED: fix all ❌ findings, then re-run this step
|
|
26
|
+
→ If APPROVED or DISCUSS: proceed to Step 2
|
|
27
|
+
|
|
28
|
+
STEP 2: call manage_task(action: "complete", work_item_id: <id>)
|
|
29
|
+
— Only permitted after APPROVED or DISCUSS verdict
|
|
30
|
+
— Marks status complete, auto-generates pr-summary.md
|
|
31
|
+
|
|
32
|
+
STEP 3: Generate and save PR & ADO Documents manually
|
|
33
|
+
— Read files-touched.json, decisions.json, and scenarios.json from the task folder
|
|
34
|
+
— Generate ado-comments.md and pr-comment.md inside .secufusion/tasks/{id}-{slug}/
|
|
35
|
+
— Follow the strict templates and guardrails defined in the [Document Generation Protocol] section
|
|
36
|
+
— Present the documents to the developer for review
|
|
37
|
+
— call manage_task(action: "log_decision", decision: "ADO comments and PR comment generated")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Hard enforcement
|
|
41
|
+
|
|
42
|
+
❌ Do NOT call `manage_task(complete)` before `run_pre_pr_checks_with_reviewer_agent` returns `APPROVED` or `DISCUSS`
|
|
43
|
+
❌ Do NOT raise a PR while `pending_acs` is non-empty
|
|
44
|
+
❌ Do NOT raise a PR if the verdict is `CHANGES_REQUESTED`
|
|
45
|
+
❌ Do NOT skip the tool and declare the code "obviously clean" — the three tiers catch different classes of issues
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Phase 5 — Retrospective
|
|
49
|
+
(TRIGGER: after manage_task action=complete is called
|
|
50
|
+
AND after PR is raised or merged)
|
|
51
|
+
|
|
52
|
+
### The rule
|
|
53
|
+
|
|
54
|
+
A partial retrospective is auto-generated by
|
|
55
|
+
manage_task complete. Your job is to fill it in.
|
|
56
|
+
|
|
57
|
+
When the complete action shows the RETROSPECTIVE STARTED
|
|
58
|
+
message — respond to the questions.
|
|
59
|
+
Do not skip unless genuinely time-pressured.
|
|
60
|
+
Each answer makes every future plan more accurate.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### Answering retrospective questions
|
|
65
|
+
|
|
66
|
+
The complete action will show Q1-Q7.
|
|
67
|
+
You can answer them all in one message:
|
|
68
|
+
Or answer partially — any answers given are recorded,
|
|
69
|
+
unanswered ones stay null.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### After receiving retro answers
|
|
74
|
+
|
|
75
|
+
Parse each "retro {key} {value}" line.
|
|
76
|
+
Call record_retrospective with all parsed values
|
|
77
|
+
plus work_item_id from current task.
|
|
78
|
+
|
|
79
|
+
Confirm:
|
|
80
|
+
"✅ Retrospective complete for {work_item_id}.
|
|
81
|
+
Insights added to retrospective-insights.json.
|
|
82
|
+
{if classifier_feedback provided:}
|
|
83
|
+
🧠 Classifier feedback queued — will improve future
|
|
84
|
+
classify_task accuracy for similar tasks."
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### What the data is used for
|
|
89
|
+
|
|
90
|
+
retrospective-insights.json accumulates across tasks.
|
|
91
|
+
|
|
92
|
+
When classify_task runs on a new task:
|
|
93
|
+
1. It reads retrospective-insights.json
|
|
94
|
+
2. Checks classifier_learning_queue for applied=false items
|
|
95
|
+
3. If frequency >= 2 for a signal:
|
|
96
|
+
→ Applies it as a temporary boost for this classification
|
|
97
|
+
→ Logs: "[LEARNED] applying signal '{term}' from
|
|
98
|
+
{n} past retrospectives"
|
|
99
|
+
4. After applying → marks applied=true in queue
|
|
100
|
+
|
|
101
|
+
This means: the more tasks completed, the smarter
|
|
102
|
+
the classifier gets — automatically, from your own
|
|
103
|
+
real task history on SecuFusion.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### When to call record_retrospective manually
|
|
108
|
+
|
|
109
|
+
- If you forgot to answer after complete action
|
|
110
|
+
- If you want to update a partial retrospective later
|
|
111
|
+
- If PR review surfaced new information
|
|
112
|
+
(breaking change found by reviewer,
|
|
113
|
+
performance issue flagged in review comment)
|
|
114
|
+
|
|
115
|
+
Just say: "Update retrospective for WI-{id}"
|
|
116
|
+
And provide whatever new information you have.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### Retrospective triggers from PR review
|
|
121
|
+
|
|
122
|
+
If during PR review a reviewer comments:
|
|
123
|
+
- "This query will be slow on large tables"
|
|
124
|
+
→ performance_issues_found: true
|
|
125
|
+
→ record_retrospective immediately with this update
|
|
126
|
+
|
|
127
|
+
- "This breaks the existing API contract"
|
|
128
|
+
→ breaking_changes_actual: increment by 1
|
|
129
|
+
→ record_retrospective immediately
|
|
130
|
+
|
|
131
|
+
- "Missing tenantId scope on line X"
|
|
132
|
+
→ pre_pr_attempts += 1 (conceptually — checks needed again)
|
|
133
|
+
→ record_retrospective with updated attempt count
|
|
134
|
+
|
|
135
|
+
These updates close the feedback loop completely —
|
|
136
|
+
not just what the MCP caught, but what human reviewers
|
|
137
|
+
catch too.
|
|
138
|
+
|
|
139
|
+
## Guardrails — enforce always, zero exceptions, zero tolerance
|
|
140
|
+
|
|
141
|
+
### Security Guardrails
|
|
142
|
+
|
|
143
|
+
- **TENANT ISOLATION IS NON-NEGOTIABLE:** Every `*Repository.java` query method (derived or `@Query`) MUST explicitly filter by `tenantId`. The PR gatekeeper blocks violations. Write it right the first time.
|
|
144
|
+
- **No hardcoded secrets, IPs, or environment URLs.** Use environment variables or config properties. Any hardcoded UAT/Prod IP or URL is a PR-blocking violation.
|
|
145
|
+
- **Auth scope changes MUST be flagged** in the plan and require explicit developer confirmation before implementation.
|
|
146
|
+
- **Zero-trust default:** Never assume a request is authorized. Always validate token claims before acting on them.
|
|
147
|
+
- **Linter errors are blocking.** AST-level tools (ESLint, Checkstyle/Maven) are the source of truth for hygiene. Fix them natively — suppressing warnings is forbidden.
|
|
148
|
+
|
|
149
|
+
### Performance Guardrails
|
|
150
|
+
(enforce whenever writing queries, Kafka consumers, or cross-service calls)
|
|
151
|
+
|
|
152
|
+
- **Read-only transactions:** All `GET` service methods that only read data MUST use `@Transactional(readOnly = true)`. No exceptions.
|
|
153
|
+
- **No repository call in a loop:** NEVER call `.findById`, `.save`, `.findAll`, or any repository method inside a `for` / `forEach` loop. Batch with `findAllById` or `saveAll`. Violation = PR blocked.
|
|
154
|
+
- **Index MUST be in migration:** If a new query filters or sorts by a column, the Flyway migration MUST include the corresponding `CREATE INDEX`. An unindexed query that passes in dev WILL fail in production.
|
|
155
|
+
- **Kafka consumer MUST NOT block thread:** Kafka listener methods MUST NOT perform synchronous DB writes, REST calls, or file I/O inline. Offload to `@Async` or a dedicated thread pool. Always.
|
|
156
|
+
- **Cross-service call MUST have timeout and fallback:** Every `RestTemplate` / `WebClient` call MUST have explicit connection timeout, read timeout, and fallback response. No fire-and-forget calls to external services.
|
|
157
|
+
|
|
158
|
+
### Rollback Guardrails
|
|
159
|
+
(enforce whenever writing Flyway migrations, touching API contracts, or changing Kafka schemas)
|
|
160
|
+
|
|
161
|
+
- **Flyway migration risk — three tiers, classify before writing any migration:**
|
|
162
|
+
- `SAFE` — additive only (new table, new nullable column, new index): safe to roll back by reverting code
|
|
163
|
+
- `RISKY` — NOT NULL column without a DEFAULT, or bulk data migration: rollback requires a compensating migration. MUST flag in plan.
|
|
164
|
+
- `DANGEROUS` — DROP TABLE, DROP COLUMN, or RENAME COLUMN: **HARD STOP.** Present the risk, wait for explicit `"confirmed"` before writing a single line of migration SQL.
|
|
165
|
+
- **API hard cutover requires confirmation:** Before removing a `/v1/` endpoint or deleting a response field, STOP. Prefer deprecation + `/v2/` first. Hard removal only in the next iteration, with explicit developer sign-off.
|
|
166
|
+
- **Kafka schema change = coordinated deployment:** Any change to an existing Kafka message schema MUST include a deployment coordination note in the plan. Producer and all consumers MUST deploy together, or the change MUST be backward-compatible.
|
|
167
|
+
- **Log rollback decision on every initialize:** Every `manage_task(action: "initialize")` MUST be immediately followed by `manage_task(action: "log_decision")` with the rollback tier and rationale. Not optional.
|
|
168
|
+
|
|
169
|
+
### Breaking Change Detection
|
|
170
|
+
(enforce whenever modifying existing endpoints, entities, or Kafka topics — creation is exempt)
|
|
171
|
+
|
|
172
|
+
**Endpoint modification rules:**
|
|
173
|
+
- Before changing ANY existing endpoint: call `manage_project_spec(action: "read")` → identify all services and the Chrome extension that call this endpoint
|
|
174
|
+
- NEVER change response shape silently — any shape change is a breaking change
|
|
175
|
+
- Always propose `/v2/` versioned endpoint first — never modify `/v1/` in place
|
|
176
|
+
- Hard cutover only with explicit developer confirmation — not implied, not "probably fine"
|
|
177
|
+
- Adding a new required field to response: MUST propose as optional first — consumers may break on strict deserialization
|
|
178
|
+
- Removing a field from response: always a breaking change — MUST get developer confirmation — deprecate first, remove in next iteration
|
|
179
|
+
|
|
180
|
+
**Entity/table modification rules:**
|
|
181
|
+
- Before adding a `NOT NULL` column: migration MUST include a `DEFAULT` value or explicit backfill. Without this, migration WILL fail on a non-empty table. Flag as `RISKY`.
|
|
182
|
+
- Before renaming a column: check ALL `@Query` annotations across ALL repos for references. If any found, rename in two phases (separate PRs): Phase 1 — add new column, keep old; Phase 2 — migrate data, drop old.
|
|
183
|
+
- Before dropping a column: always `DANGEROUS` — HARD STOP. Confirm with developer. Check all repos for references first. Never drop without explicit sign-off.
|
|
184
|
+
|
|
185
|
+
**Kafka topic modification rules:**
|
|
186
|
+
- Before changing a message schema: MUST list all consumer services from project-spec.json → confirm coordinated deployment plan → flag in rollback plan: "Rollback requires coordinated revert"
|
|
187
|
+
|
|
188
|
+
**The silent break rule:**
|
|
189
|
+
A change that breaks something the developer did not know about is a planning failure.
|
|
190
|
+
Over-flagging a potential break is acceptable. Under-flagging and causing a production incident is not.
|
|
191
|
+
When in doubt: flag it, present it, ask. Every time.
|
|
192
|
+
|
|
193
|
+
### Retrospective (enforce after every task complete)
|
|
194
|
+
|
|
195
|
+
- manage_task action=complete auto-starts retrospective
|
|
196
|
+
- Respond to retro questions after every completion
|
|
197
|
+
- "retro skip" is allowed but discouraged —
|
|
198
|
+
every skipped retrospective = missed learning
|
|
199
|
+
- classifier_learning_queue signals with frequency >= 2
|
|
200
|
+
are automatically applied to classify_task
|
|
201
|
+
- Never manually edit retrospective-insights.json —
|
|
202
|
+
always use record_retrospective tool
|
|
203
|
+
- Retrospective updates from PR review are MANDATORY
|
|
204
|
+
if reviewer catches something the MCP missed
|
|
205
|
+
|
|
206
|
+
## Commenting Rules — enforce in every file you touch
|
|
207
|
+
|
|
208
|
+
- Comments explain **WHY** — never WHAT. The code already says what.
|
|
209
|
+
- NEVER write obvious comments:
|
|
210
|
+
- `// Get the user` ← **FORBIDDEN**
|
|
211
|
+
- `// Loop through list` ← **FORBIDDEN**
|
|
212
|
+
- `// Return result` ← **FORBIDDEN**
|
|
213
|
+
- `// Initialize the service` ← **FORBIDDEN**
|
|
214
|
+
- Write a comment ONLY when the reason behind the code is non-obvious:
|
|
215
|
+
- Why a workaround exists (and reference the ticket)
|
|
216
|
+
- Why a specific algorithm was chosen over a simpler one
|
|
217
|
+
- Why a value is hardcoded in the rare case it absolutely must be
|
|
218
|
+
- If you cannot explain the WHY in one sentence, the comment does not belong there.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Cross-Task Intelligence
|
|
223
|
+
(TRIGGER: starting any new task — MANDATORY before initialize)
|
|
224
|
+
|
|
225
|
+
### MANDATORY sequence
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
STEP 1: call search_tasks(keywords: <keywords from new task description>)
|
|
229
|
+
— ALWAYS. "I'm sure there's no prior work" is not a reason to skip.
|
|
230
|
+
|
|
231
|
+
STEP 2: if ANY relevant past task found:
|
|
232
|
+
call get_task_history(work_item_id: <matching id>)
|
|
233
|
+
— understand how it was done before
|
|
234
|
+
|
|
235
|
+
STEP 3: if implementing anything similar to a past feature:
|
|
236
|
+
call get_pattern_from_task(work_item_id: <matching id>)
|
|
237
|
+
— extract reusable architectural patterns, file paths, and test scenarios
|
|
238
|
+
|
|
239
|
+
STEP 4: proceed to manage_task(action: "initialize") only after steps 1-3 complete
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Retrospective-Informed Planning
|
|
243
|
+
|
|
244
|
+
Before Phase 0.7 plan presentation:
|
|
245
|
+
1. Read retrospective-insights.json
|
|
246
|
+
2. If avg_step_accuracy_pct < 80%:
|
|
247
|
+
→ Add note in plan:
|
|
248
|
+
"⚠️ Historical note: past plans averaged
|
|
249
|
+
{pct}% step accuracy — this plan may need
|
|
250
|
+
adjustment during execution"
|
|
251
|
+
3. If most_common_failed_check is not empty:
|
|
252
|
+
→ Add to plan's pre-PR section:
|
|
253
|
+
"⚠️ Historically failing check: {check}
|
|
254
|
+
— pay extra attention"
|
|
255
|
+
4. If classifier_learning_queue has
|
|
256
|
+
unapplied signals with frequency >= 2:
|
|
257
|
+
→ Apply as temporary boost in classify_task
|
|
258
|
+
→ Log applied signals in classification output
|
|
259
|
+
|
|
260
|
+
### Hard enforcement
|
|
261
|
+
|
|
262
|
+
❌ Do NOT call `manage_task(action: "initialize")` before `search_tasks` completes
|
|
263
|
+
❌ Do NOT skip `get_task_history` if a match exists — "I remember it" is not a substitute
|
|
264
|
+
❌ Do NOT re-solve a solved problem — task history exists precisely to prevent this
|
|
265
|
+
❌ Re-using a rejected pattern found in task history is a violation even if you disagree with the rejection
|
|
266
|
+
|
|
267
|
+
## Document Generation Protocol
|
|
268
|
+
(For Phase 4 — PR Handoff)
|
|
269
|
+
|
|
270
|
+
### ADO Comments Template (`ado-comments.md`)
|
|
271
|
+
**Audience:** Technical and Non-Technical stakeholders on the Azure DevOps board.
|
|
272
|
+
**Format rules:**
|
|
273
|
+
- Layman summary: NO class names, NO method names, NO technical terms. Pure English.
|
|
274
|
+
- Execution Flow: BE specific. Exact class names, exact method names, exact file paths. Source from `files-touched.json`.
|
|
275
|
+
- Sequential flow ONLY. Do not use before/after framing (e.g., avoid "not X directly," "exactly as before," "hardened after review," etc.). Just state what happens and in what order.
|
|
276
|
+
- Tables must have content in every cell. If unknown → "not recorded".
|
|
277
|
+
- Max 2 sentences in any single prose paragraph.
|
|
278
|
+
- Max 600 words.
|
|
279
|
+
|
|
280
|
+
```markdown
|
|
281
|
+
## Layman Summary
|
|
282
|
+
{layman_summary — 2-3 sentences max. Sequential flow only.}
|
|
283
|
+
|
|
284
|
+
## Execution Flow
|
|
285
|
+
{If auth/permission gating exists:}
|
|
286
|
+
> 1. Request gated by `{permission}` authority.
|
|
287
|
+
|
|
288
|
+
{Describe the straight sequence of what happens, in order, step-by-step. E.g.:}
|
|
289
|
+
2. **Trigger:** {How the flow starts}
|
|
290
|
+
3. **Validation:** {What checks occur}
|
|
291
|
+
4. **Processing:** {What data is transformed or queried}
|
|
292
|
+
5. **Result:** {What is returned or persisted}
|
|
293
|
+
|
|
294
|
+
### Security & Error Handling
|
|
295
|
+
{for each security_case in scenarios.json:}
|
|
296
|
+
- {security concern} → {enforcement}
|
|
297
|
+
{for each sad_path in scenarios.json:}
|
|
298
|
+
- {sad_path description} → {how handled}
|
|
299
|
+
|
|
300
|
+
### Infrastructure Impact
|
|
301
|
+
| Area | Status |
|
|
302
|
+
|---|---|
|
|
303
|
+
| DB migration | {✅ Not required / ⚠️ Created: V{n}__...sql} |
|
|
304
|
+
| Kafka topics | {✅ No new topics / ⚠️ New topic: {name}} |
|
|
305
|
+
| API contract | {✅ No breaking changes / ⚠️ See decisions} |
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
*Generated by SecuFusion MCP · {service} · {work_item_id}*
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### PR Comment Template (`pr-comment.md`)
|
|
312
|
+
**Audience:** Busy Developer Reviewers reading the PR description.
|
|
313
|
+
**Format rules:**
|
|
314
|
+
- Group changes by the sequence of execution, not by file name.
|
|
315
|
+
- Sequential flow ONLY. No before/after comparisons. Just state what the code does now.
|
|
316
|
+
- ✅ for things NOT changing is equally important.
|
|
317
|
+
- Guardrails section is mandatory.
|
|
318
|
+
- Max 400 words. Condense changes table if longer.
|
|
319
|
+
|
|
320
|
+
```markdown
|
|
321
|
+
# {task_type_emoji} {work_item_id}: {title}
|
|
322
|
+
|
|
323
|
+
> **Type:** {task_type_display} · **Service:** `{service}` · **Work Item:** [{work_item_id}]({devops_url}/{work_item_id})
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
## Summary
|
|
327
|
+
{2-3 sentences MAX. What the code does. Sequential flow only.}
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
## Implementation Sequence
|
|
331
|
+
1. {Step 1 of the new flow}
|
|
332
|
+
2. {Step 2 of the new flow}
|
|
333
|
+
3. {Step 3 of the new flow}
|
|
334
|
+
4. {Step 4 of the new flow}
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
## Affected Service
|
|
338
|
+
- `{service-name}`
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
## Impact
|
|
342
|
+
- ✅ No functional or behavioral changes {OR describe actual behavioral change sequentially}
|
|
343
|
+
- ✅ No DB migration required {OR: ⚠️ Flyway migration V{n} included}
|
|
344
|
+
- ✅ No Kafka topic changes {OR: ⚠️ New topic: {name}}
|
|
345
|
+
- ✅ No API contract changes {OR: ⚠️ New endpoints: see Implementation Sequence above}
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
## Error Handling
|
|
349
|
+
- {what fails} → {how handled}
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
## Guardrails
|
|
353
|
+
- ✅ No `console.log` / `System.out.println` in source
|
|
354
|
+
- ✅ No hardcoded UAT/Prod URLs or IPs
|
|
355
|
+
- ✅ `tenantId` scoping maintained on all queries
|
|
356
|
+
- ✅ No `@Entity` changes — Flyway not required
|
|
357
|
+
- ✅ All pre-PR checks passed
|
|
358
|
+
|
|
359
|
+
{if any rejected patterns were relevant:}
|
|
360
|
+
**Patterns avoided:**
|
|
361
|
+
- Rejected pattern #{id}: {short description}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Task Type Emoji Map
|
|
365
|
+
- bug → 🐛
|
|
366
|
+
- user_story → 📖
|
|
367
|
+
- feature → ✨
|
|
368
|
+
- hotfix → 🚨
|
|
369
|
+
- refactor → 🔄
|
|
370
|
+
- chore → 🧹
|
|
371
|
+
|
|
372
|
+
### Presentation Format
|
|
373
|
+
When presenting to the developer, output this exactly:
|
|
374
|
+
```text
|
|
375
|
+
────────────────────────────────────────────────
|
|
376
|
+
✅ Two documents generated for {work_item_id}:
|
|
377
|
+
|
|
378
|
+
📄 ADO Comments → paste into WI comment thread
|
|
379
|
+
📄 PR Comment → paste into PR description
|
|
380
|
+
|
|
381
|
+
Review below. Say LGTM to confirm, or tell me what to change.
|
|
382
|
+
────────────────────────────────────────────────
|
|
383
|
+
```
|
|
384
|
+
Then show the full `ado-comments.md` followed by a `────────────────────────────────` divider, followed by the full `pr-comment.md`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Analyzes the current project to build its DNA Knowledge Graph
|
|
3
|
+
argument-hint: (no args)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the SecuFusion DNA Discovery Agent. Your goal is to analyze this ecosystem and build the Poly-Repo DNA Knowledge Graph.
|
|
7
|
+
|
|
8
|
+
The underlying MCP tools will automatically detect if you are inside a poly-repo structure. If they find sibling repositories, they will scan all of them simultaneously to build a cross-repo DNA file.
|
|
9
|
+
|
|
10
|
+
1. Call the `secufusion-dna_scan_repository_stack` tool to identify the framework and infrastructure across the ecosystem.
|
|
11
|
+
2. Call the `secufusion-dna_extract_domain_models` tool to find and parse entities and domain models across all repos.
|
|
12
|
+
3. Call the `secufusion-dna_extract_api_endpoints` tool to map out the API layer across all repos.
|
|
13
|
+
4. Call the `secufusion-dna_extract_event_topics` tool to map out event consumers and producers across all repos.
|
|
14
|
+
|
|
15
|
+
Once you have gathered this information, summarize your findings for the user. Do not delete any existing data in the knowledge graph.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Determines the impact of changing a specific component or file
|
|
3
|
+
argument-hint: <component-name-or-file-path>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the SecuFusion DNA Blast Radius Agent. Your goal is to determine the impact of changing the provided component or file across the entire Poly-Repo ecosystem.
|
|
7
|
+
|
|
8
|
+
1. Call the `secufusion-dna_query_knowledge_graph` tool with the query "Find all components, services, and APIs that depend on or consume {argument}".
|
|
9
|
+
2. Analyze the returned dependency chain, paying special attention to cross-repo dependencies (e.g., changing a DTO in `sfn-events-api` breaking a listener in `sfn-notification-api`).
|
|
10
|
+
3. Present a Cross-Repo Blast Radius Report to the user, categorizing the impact into:
|
|
11
|
+
- Impacted Microservices
|
|
12
|
+
- Impacted APIs
|
|
13
|
+
- Impacted Events/Consumers
|
|
14
|
+
- Impacted Databases
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Generates a Mermaid architecture diagram from the project DNA
|
|
3
|
+
argument-hint: (no args)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the SecuFusion DNA Architecture Agent. Your goal is to visualize the cross-service dependencies and architecture of this project.
|
|
7
|
+
|
|
8
|
+
1. Call the `secufusion-dna_query_knowledge_graph` tool with the query "Get all services, APIs, and event topics with their dependencies".
|
|
9
|
+
2. Based on the returned relationships, generate a comprehensive Mermaid diagram (`graph TD` or `graph LR`).
|
|
10
|
+
3. Render the diagram in your response, grouping components by bounded context or service if possible.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Starts the background watcher to continuously update the Poly-Repo DNA.
|
|
3
|
+
argument-hint: (no args)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are the SecuFusion DNA Watcher Agent. Your goal is to start the background file watcher.
|
|
7
|
+
|
|
8
|
+
1. Call the `start_dna_watcher` tool.
|
|
9
|
+
2. Inform the user that the DNA Knowledge Graph is now actively monitoring the codebase in the background and will dynamically update whenever a developer saves a Java file.
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "Hooks active on plugin install. Universal,",
|
|
3
|
+
"//1": "fast, project-agnostic only. Project-specific",
|
|
4
|
+
"//2": "hooks (lint, test, format) stay opt-in.",
|
|
5
|
+
"hooks": {
|
|
6
|
+
"SessionStart": [
|
|
7
|
+
{
|
|
8
|
+
"hooks": [
|
|
9
|
+
{
|
|
10
|
+
"type": "command",
|
|
11
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/knowledge-drift.sh"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Silent unless drift detected.
|
|
3
|
+
# Checks if SecuFusion knowledge layer is stale.
|
|
4
|
+
|
|
5
|
+
ROOT="${1:-.}"
|
|
6
|
+
SPEC_FILE="$ROOT/.secufusion-project-spec.json"
|
|
7
|
+
DOCS_DIR="$ROOT/docs"
|
|
8
|
+
CLAUDE_MD="$ROOT/CLAUDE.md"
|
|
9
|
+
|
|
10
|
+
# No knowledge layer yet → silent
|
|
11
|
+
if [ ! -f "$SPEC_FILE" ] && [ ! -d "$DOCS_DIR" ]; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Check git — if no git, skip silently
|
|
16
|
+
if ! git -C "$ROOT" rev-parse --git-dir \
|
|
17
|
+
> /dev/null 2>&1; then
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Find when knowledge was last updated
|
|
22
|
+
LAST_SPEC=$(git -C "$ROOT" log -1 \
|
|
23
|
+
--format="%H" -- \
|
|
24
|
+
".secufusion-project-spec.json" \
|
|
25
|
+
"docs/" "CLAUDE.md" 2>/dev/null)
|
|
26
|
+
|
|
27
|
+
if [ -z "$LAST_SPEC" ]; then
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Count source files changed since last doc update
|
|
32
|
+
CHANGED=$(git -C "$ROOT" diff \
|
|
33
|
+
--name-only "$LAST_SPEC"..HEAD \
|
|
34
|
+
-- "src/" "*/src/" \
|
|
35
|
+
"pom.xml" "*/pom.xml" \
|
|
36
|
+
"application*.yml" \
|
|
37
|
+
"*/application*.yml" \
|
|
38
|
+
2>/dev/null | wc -l | tr -d ' ')
|
|
39
|
+
|
|
40
|
+
if [ "$CHANGED" -gt 10 ]; then
|
|
41
|
+
echo "⚠️ SecuFusion knowledge layer may be stale."
|
|
42
|
+
echo " $CHANGED source file(s) changed since"
|
|
43
|
+
echo " last /sfn:refresh."
|
|
44
|
+
echo " Run /sfn:doctor to check,"
|
|
45
|
+
echo " /sfn:refresh to update."
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
exit 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function parseApiEndpoints(filePath, fileContent, repository) {
|
|
2
|
+
const apis = [];
|
|
3
|
+
if (!fileContent.includes("@RestController"))
|
|
4
|
+
return apis;
|
|
5
|
+
const classMatch = fileContent.match(/public\s+class\s+(\w+)/);
|
|
6
|
+
if (!classMatch)
|
|
7
|
+
return apis;
|
|
8
|
+
const controllerName = classMatch[1];
|
|
9
|
+
const endpoints = [];
|
|
10
|
+
const violations = [];
|
|
11
|
+
const mappingRegex = /@(Get|Post|Put|Delete)Mapping\s*\(\s*["']([^"']+)["']\s*\)/g;
|
|
12
|
+
let match;
|
|
13
|
+
while ((match = mappingRegex.exec(fileContent)) !== null) {
|
|
14
|
+
endpoints.push(`${match[1].toUpperCase()} ${match[2]}`);
|
|
15
|
+
}
|
|
16
|
+
if (!fileContent.includes("ResponseDto")) {
|
|
17
|
+
violations.push("Violation: Controller methods should return ResponseEntity<ResponseDto<T>>.");
|
|
18
|
+
}
|
|
19
|
+
if (endpoints.length > 0 && !fileContent.includes("String tenantId")) {
|
|
20
|
+
violations.push("P1 Violation: API endpoints must explicitly accept a String tenantId parameter.");
|
|
21
|
+
}
|
|
22
|
+
apis.push({
|
|
23
|
+
controllerName,
|
|
24
|
+
endpoints,
|
|
25
|
+
filePath,
|
|
26
|
+
repository,
|
|
27
|
+
violations
|
|
28
|
+
});
|
|
29
|
+
return apis;
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function parseNodeApis(filePath, fileContent, repository) {
|
|
2
|
+
const apis = [];
|
|
3
|
+
if (!filePath.endsWith('.ts') && !filePath.endsWith('.js'))
|
|
4
|
+
return apis;
|
|
5
|
+
const endpoints = [];
|
|
6
|
+
const expressRegex = /app\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/g;
|
|
7
|
+
let match;
|
|
8
|
+
while ((match = expressRegex.exec(fileContent)) !== null) {
|
|
9
|
+
endpoints.push(`${match[1].toUpperCase()} ${match[2]}`);
|
|
10
|
+
}
|
|
11
|
+
const nestjsRegex = /@(Get|Post|Put|Delete|Patch)\s*\(\s*['"]?([^'"]*)['"]?\s*\)/g;
|
|
12
|
+
while ((match = nestjsRegex.exec(fileContent)) !== null) {
|
|
13
|
+
endpoints.push(`${match[1].toUpperCase()} ${match[2]}`);
|
|
14
|
+
}
|
|
15
|
+
if (endpoints.length > 0) {
|
|
16
|
+
const controllerMatch = fileContent.match(/export\s+class\s+(\w+Controller)/);
|
|
17
|
+
const controllerName = controllerMatch ? controllerMatch[1] : filePath.split(/[\\/]/).pop()?.replace(/\.(js|ts)$/, '') || 'NodeRoute';
|
|
18
|
+
apis.push({
|
|
19
|
+
controllerName,
|
|
20
|
+
endpoints,
|
|
21
|
+
filePath,
|
|
22
|
+
repository
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return apis;
|
|
26
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export function parseConfig(filePath, fileContent, repository) {
|
|
2
|
+
const isEnv = filePath.includes('.env');
|
|
3
|
+
const isProperties = filePath.endsWith('.properties');
|
|
4
|
+
const isYaml = filePath.endsWith('.yml') || filePath.endsWith('.yaml');
|
|
5
|
+
if (!isEnv && !isProperties && !isYaml)
|
|
6
|
+
return [];
|
|
7
|
+
const config = {
|
|
8
|
+
filePath,
|
|
9
|
+
repository,
|
|
10
|
+
entries: [],
|
|
11
|
+
hasMaskedValues: false
|
|
12
|
+
};
|
|
13
|
+
const lines = fileContent.split('\n');
|
|
14
|
+
if (isEnv || isProperties) {
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
18
|
+
continue;
|
|
19
|
+
const splitIdx = trimmed.indexOf('=');
|
|
20
|
+
if (splitIdx > 0) {
|
|
21
|
+
const key = trimmed.substring(0, splitIdx).trim();
|
|
22
|
+
const value = trimmed.substring(splitIdx + 1).trim();
|
|
23
|
+
const requiresRealValues = filePath.includes('.example') ||
|
|
24
|
+
value === '' ||
|
|
25
|
+
value.includes('***') ||
|
|
26
|
+
value.toLowerCase().includes('changeme') ||
|
|
27
|
+
value.startsWith('${') ||
|
|
28
|
+
value.startsWith('<');
|
|
29
|
+
config.entries.push({ key, value, requiresRealValues });
|
|
30
|
+
if (requiresRealValues)
|
|
31
|
+
config.hasMaskedValues = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (isYaml) {
|
|
36
|
+
// Simple parsing for Spring Boot application.yml and similar config keys
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
const trimmed = line.trim();
|
|
39
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
40
|
+
continue;
|
|
41
|
+
const match = trimmed.match(/^([\w-]+):\s*(.*)$/);
|
|
42
|
+
if (match) {
|
|
43
|
+
const key = match[1];
|
|
44
|
+
const value = match[2];
|
|
45
|
+
if (value && value !== '{' && value !== '[') {
|
|
46
|
+
const requiresRealValues = value.includes('***') || value.toLowerCase().includes('changeme') || value.startsWith('${');
|
|
47
|
+
config.entries.push({ key, value, requiresRealValues });
|
|
48
|
+
if (requiresRealValues)
|
|
49
|
+
config.hasMaskedValues = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (config.entries.length > 0) {
|
|
55
|
+
return [config];
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
export function parseExternalEnv(fileContent) {
|
|
60
|
+
const values = {};
|
|
61
|
+
const lines = fileContent.split('\n');
|
|
62
|
+
for (const line of lines) {
|
|
63
|
+
const trimmed = line.trim();
|
|
64
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
65
|
+
continue;
|
|
66
|
+
const splitIdx = trimmed.indexOf('=');
|
|
67
|
+
if (splitIdx > 0) {
|
|
68
|
+
const key = trimmed.substring(0, splitIdx).trim();
|
|
69
|
+
const value = trimmed.substring(splitIdx + 1).trim();
|
|
70
|
+
values[key] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return values;
|
|
74
|
+
}
|