secufusion-mcp 1.0.16 → 1.0.18
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 +137 -21
- package/index.js +51 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
## What is this?
|
|
12
12
|
|
|
13
|
-
`secufusion-mcp` is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that plugs into AI coding assistants (Claude Desktop, Cursor, Cline, etc.) and gives them **
|
|
13
|
+
`secufusion-mcp` is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that plugs into AI coding assistants (Claude Desktop, Cursor, Cline, etc.) and gives them **ten powerful tools** to enforce SecuFusion's engineering standards throughout the development lifecycle:
|
|
14
14
|
|
|
15
15
|
| Tool | Phase | What it does |
|
|
16
16
|
|---|---|---|
|
|
17
17
|
| `manage_project_spec` | **Phase 00** — Session Start | Loads `.secufusion-project-spec.json` — full project DNA (ports, repos, domains, coding patterns, golden rules) |
|
|
18
|
-
| `manage_task` | **Phase 0.7 / 1 / 2 / 4** — Task Lifecycle | Creates `.secufusion/tasks/
|
|
18
|
+
| `manage_task` | **Phase 0.7 / 1 / 2 / 4** — Task Lifecycle | Creates dynamically named `.secufusion/tasks/{id}-{slug}/` folder with spec, progress, decisions, files-touched, scenarios, and PR summary |
|
|
19
19
|
| `get_task_history` | **Phase 0 / 1** — Cross-Task Intelligence | Retrieves the full history of a past task before starting similar work |
|
|
20
20
|
| `search_tasks` | **Phase 0 / 1** — Cross-Task Intelligence | Keyword search across all past task files — prevents re-solving solved problems |
|
|
21
21
|
| `get_pattern_from_task` | **Phase 1** — Cross-Task Intelligence | Extracts reusable decisions, file patterns, and test scenarios from a completed task |
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
| `log_rejected_pattern` | **Phase 3** — Course Correction | Records bad patterns to `.rejected-patterns.json` so they are never repeated |
|
|
24
24
|
| `run_pre_pr_checks` | **Phase 4** — PR Handoff | Discovers modified microservices and gates PRs via AST-level linters + structural checks |
|
|
25
25
|
| `get_secufusion_rules` | **Setup** | Returns the `AGENTS.md` rules for AI clients that don't natively support MCP Resources |
|
|
26
|
+
| `classify_task` | **Phase 0.5** — Task Classification | **NEW** — Deep multi-pass analysis engine. Classifies any task as `BACKEND_ONLY`, `FRONTEND_ONLY`, `FULL_STACK`, or `EXTENSION_ONLY` based on root cause (where the fix lives), not surface symptoms. Must be the first tool called on any task. |
|
|
26
27
|
|
|
27
28
|
---
|
|
28
29
|
|
|
@@ -400,6 +401,16 @@ These rules are enforced automatically — the AI will never violate them:
|
|
|
400
401
|
✅ Over-flag > under-flag: always present a breaking change report if in doubt
|
|
401
402
|
```
|
|
402
403
|
|
|
404
|
+
**classify_task — Deep Analysis Engine** (runs first on every task, before anything else):
|
|
405
|
+
```
|
|
406
|
+
✅ Weighted signal tiers: service names = 10pts, tech constructs = 5pts, generic = 1pt
|
|
407
|
+
✅ Root-cause extraction: classifies by WHERE THE FIX LIVES, not where the symptom appears
|
|
408
|
+
✅ Negation detection: "not a UI issue" removes frontend signal weight
|
|
409
|
+
✅ Bug disambiguation: data-correctness/exception/auth/CRUD/performance bugs → +backend
|
|
410
|
+
✅ Confidence gate: HIGH only when score ratio ≥ 1.8× AND Tier 1/2 signal matched
|
|
411
|
+
✅ Persists to .secufusion/classifications/{id}.json — no re-classification on resume
|
|
412
|
+
```
|
|
413
|
+
|
|
403
414
|
---
|
|
404
415
|
|
|
405
416
|
## File Outputs
|
|
@@ -407,13 +418,13 @@ These rules are enforced automatically — the AI will never violate them:
|
|
|
407
418
|
| File | Description | Commit? |
|
|
408
419
|
|---|---|---|
|
|
409
420
|
| `.secufusion-project-spec.json` | **Project DNA** — all services, ports, patterns, golden rules. Generated once, read every session | ✅ Yes |
|
|
410
|
-
| `.secufusion/registry.json` | Index of all work items
|
|
411
|
-
| `.secufusion/tasks/
|
|
412
|
-
| `.secufusion/tasks/
|
|
413
|
-
| `.secufusion/tasks/
|
|
414
|
-
| `.secufusion/tasks/
|
|
415
|
-
| `.secufusion/tasks/
|
|
416
|
-
| `.secufusion/tasks/
|
|
421
|
+
| `.secufusion/registry.json` | Index of all initialized work items and their exact dynamic folder names — used by `search_tasks` | ✅ Yes |
|
|
422
|
+
| `.secufusion/tasks/{id}-{slug}/spec.json` | Task spec — title, description, ACs, tags, status | ✅ Yes |
|
|
423
|
+
| `.secufusion/tasks/{id}-{slug}/progress.json` | AC tracking — pending, completed, next_step | ✅ Yes |
|
|
424
|
+
| `.secufusion/tasks/{id}-{slug}/decisions.json` | Architectural decisions log (including rollback strategy) | ✅ Yes |
|
|
425
|
+
| `.secufusion/tasks/{id}-{slug}/files-touched.json` | All files modified with change summaries | ✅ Yes |
|
|
426
|
+
| `.secufusion/tasks/{id}-{slug}/scenarios.json` | Test scenarios (unit / integration / e2e / manual) | ✅ Yes |
|
|
427
|
+
| `.secufusion/tasks/{id}-{slug}/pr-summary.md` | Auto-generated PR summary on `manage_task complete` | ✅ Yes |
|
|
417
428
|
| `.secufusion-state.json` | **Legacy** branch-scoped state — still works via `manage_branch_state` | ✅ Yes |
|
|
418
429
|
| `.rejected-patterns.json` | Cumulative log of all rejected patterns across sessions | ✅ Yes |
|
|
419
430
|
| `.secufusion-tokens.json` | Persistent tracking of session-wide LLM token usage and cost | ❌ No |
|
|
@@ -433,16 +444,20 @@ These rules are enforced automatically — the AI will never violate them:
|
|
|
433
444
|
│ Phase 0 │ manage_task (action=read_summary) OR search_tasks │
|
|
434
445
|
│ Resume │ → Token-efficient status view → resume next_step instantly │
|
|
435
446
|
├──────────────┼──────────────────────────────────────────────────────────────┤
|
|
436
|
-
│ Phase 0.5 │
|
|
437
|
-
│ Ownership │
|
|
438
|
-
│ Check │
|
|
447
|
+
│ Phase 0.5 │ classify_task → 5-pass deep analysis engine │
|
|
448
|
+
│ Ownership │ Pass 1: Weighted signal scoring (Tier 1-4) │
|
|
449
|
+
│ Check │ Pass 2: Negation detection per sentence │
|
|
450
|
+
│ │ Pass 3: Root-cause phrase extraction (fix location, not symptom) │
|
|
451
|
+
│ │ Pass 4: Bug disambiguation matrix │
|
|
452
|
+
│ │ Pass 5: Confidence gate (ratio ≥ 1.8× + Tier 1/2 required) │
|
|
453
|
+
│ │ → allowed_next_action: PROCEED / CONFIRM / STOP │
|
|
439
454
|
├──────────────┼──────────────────────────────────────────────────────────────┤
|
|
440
455
|
│ Phase 0.7 │ Present full plan (scope, files, ACs, perf, rollback) │
|
|
441
456
|
│ Plan Gate │ → STOP and wait for "proceed" / "adjust" / "cancel" │
|
|
442
457
|
│ │ → manage_task (action=initialize) only after proceed │
|
|
443
458
|
├──────────────┼──────────────────────────────────────────────────────────────┤
|
|
444
459
|
│ Phase 1 │ search_tasks → get_task_history → manage_task initialize │
|
|
445
|
-
│ Planning │ → Creates .secufusion/tasks/
|
|
460
|
+
│ Planning │ → Creates dynamic .secufusion/tasks/{id}-{slug}/ with all 5 files │
|
|
446
461
|
├──────────────┼──────────────────────────────────────────────────────────────┤
|
|
447
462
|
│ Phase 2 │ manage_task: update_spec / log_file_touched / │
|
|
448
463
|
│ Execution │ log_decision / add_scenario │
|
|
@@ -517,7 +532,7 @@ So that sensitive dashboard actions are protected against compromised credential
|
|
|
517
532
|
4. If MFA fails 3 times, the account should be temporarily locked for 15 minutes.
|
|
518
533
|
```
|
|
519
534
|
|
|
520
|
-
**The AI automatically classifies the task, presents a plan, and upon approval, calls `manage_task` to initialize `.secufusion/tasks/
|
|
535
|
+
**The AI automatically classifies the task, presents a plan, and upon approval, calls `manage_task` to initialize the dynamic folder `.secufusion/tasks/2847-add-mfa-enforcement-for-admin-users-on-login/`:**
|
|
521
536
|
```json
|
|
522
537
|
{
|
|
523
538
|
"action": "initialize",
|
|
@@ -630,7 +645,7 @@ The SecuFusion MCP operates across three complementary layers to prevent AI amne
|
|
|
630
645
|
|
|
631
646
|
**Layer 1 — Project Spec (permanent, project-scoped):** `.secufusion-project-spec.json` — loaded once per session via `manage_project_spec`. The AI never needs to be told what port a service runs on, what pattern to use for DTO mapping, or how tenantId flows through the system.
|
|
632
647
|
|
|
633
|
-
**Layer 2 — Task Memory (persistent, work-item-scoped):** `.secufusion/tasks/
|
|
648
|
+
**Layer 2 — Task Memory (persistent, work-item-scoped):** `.secufusion/tasks/{id}-{slug}/` — one dynamically named folder per work item, initialized via `manage_task`. Tracks spec, progress, decisions, files touched, scenarios, and generates a `pr-summary.md` on completion. Cross-task intelligence via `search_tasks`, `get_task_history`, and `get_pattern_from_task` prevents re-solving solved problems.
|
|
634
649
|
|
|
635
650
|
**Layer 3 — AST Guardrails (automated, quality-gating):** ESLint, Maven Checkstyle, tenant isolation scanner, Flyway checker, performance rules, rollback classification, and breaking change detection run at PR time. Cannot be bypassed without explicit `skip_checks`.
|
|
636
651
|
|
|
@@ -649,17 +664,21 @@ Phase 0: manage_task read_summary → resumes active work item
|
|
|
649
664
|
↓
|
|
650
665
|
You say: "WI-1042: Add audit log export"
|
|
651
666
|
↓
|
|
652
|
-
Phase 0.5:
|
|
653
|
-
+
|
|
667
|
+
Phase 0.5: classify_task → 5-pass deep analysis (FIRST tool call, no exceptions)
|
|
668
|
+
→ weighted scores + root-cause phrases + negation + bug heuristics
|
|
669
|
+
→ allowed_next_action: PROCEED (backend) / CONFIRM (mixed) / STOP (frontend)
|
|
670
|
+
+ performance risk scan (GREEN/AMBER/RED)
|
|
671
|
+
+ breaking change scan (endpoints/entities/Kafka/extension)
|
|
654
672
|
↓
|
|
655
673
|
Phase 0.7: plan presented → developer approves ("proceed")
|
|
656
674
|
↓
|
|
657
|
-
Phase 1: manage_task initialize
|
|
675
|
+
Phase 1: search_tasks (always) → get_task_history → manage_task initialize
|
|
676
|
+
→ .secufusion/tasks/1042-add-audit-log-export/ created
|
|
658
677
|
↓
|
|
659
|
-
Phase 2: code + log_file_touched + log_decision + add_scenario
|
|
678
|
+
Phase 2: code + log_file_touched + log_decision + add_scenario (ALL mandatory)
|
|
660
679
|
↓
|
|
661
680
|
Phase 4: manage_task complete → pr-summary.md generated
|
|
662
|
-
+ run_pre_pr_checks
|
|
681
|
+
+ run_pre_pr_checks (fix → recheck loop until ZERO errors) → then PR
|
|
663
682
|
```
|
|
664
683
|
|
|
665
684
|
### Reusing across projects (Global Bundling)
|
|
@@ -676,6 +695,16 @@ Depending on your AI client's capabilities, you can load the rules instantly by
|
|
|
676
695
|
|
|
677
696
|
*(If you prefer the legacy method, you can still copy `.agents/AGENTS.md` and `.secufusion-project-spec.json` into your project root).*
|
|
678
697
|
|
|
698
|
+
### Dynamic Task Folders (v1.0.17+)
|
|
699
|
+
|
|
700
|
+
As of version **1.0.17**, the MCP server automatically generates human-readable, safe folder names for all new tasks using the task's title.
|
|
701
|
+
|
|
702
|
+
When you pass a title like `"BUG-1140: Tenant deletion reports failure"` to `manage_task initialize`, the server strips bad characters, truncates the string safely, and generates a perfect folder name:
|
|
703
|
+
`.secufusion/tasks/BUG-1140-tenant-deletion-reports-failure/`
|
|
704
|
+
|
|
705
|
+
- **Backward Compatible:** The AI only ever needs to supply the `work_item_id` (e.g. `BUG-1140`) for subsequent updates. The server instantly finds the correct folder via `registry.json` (O(1) lookup) or falls back to a prefix scan for legacy `WI-{id}` folders.
|
|
706
|
+
- **OS Safe:** Automatically trims trailing dashes and clamps lengths to prevent Windows `MAX_PATH` errors.
|
|
707
|
+
|
|
679
708
|
---
|
|
680
709
|
|
|
681
710
|
## Talking to the AI — What You'll Ever Say
|
|
@@ -702,7 +731,94 @@ Once all three layers are in place, you interact completely naturally:
|
|
|
702
731
|
|
|
703
732
|
**Before Phase 0.5 + 0.7:** The AI started coding immediately with no ownership check or explicit plan.
|
|
704
733
|
|
|
705
|
-
**After Phase 0.5 + 0.7:** The AI
|
|
734
|
+
**After Phase 0.5 + 0.7:** The AI calls `classify_task` first (no exceptions). The deep analysis engine classifies by root cause — not surface symptoms — runs a performance risk and breaking change scan, presents a complete plan with rollback strategy, and **waits for your approval before writing a single line of code**.
|
|
735
|
+
|
|
736
|
+
**Before strict AGENTS.md:** Each phase was a soft bullet list with suggestions. The AI could skip steps.
|
|
737
|
+
|
|
738
|
+
**After strict AGENTS.md:** Every phase has a MANDATORY tool-call sequence in code-block format, an explicit ❌ prohibition list, and a hard gate. Skipping any step is a named violation.
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
## Tool 10: `classify_task` — Deep Analysis Engine
|
|
743
|
+
|
|
744
|
+
The **mandatory first step** for every task without exception. Classifies a task as `BACKEND_ONLY`, `FRONTEND_ONLY`, `FULL_STACK`, or `EXTENSION_ONLY` using a 5-pass deep analysis pipeline.
|
|
745
|
+
|
|
746
|
+
> **Core principle:** Classifies by **where the fix lives** — not where the symptom appears.
|
|
747
|
+
> `"Dashboard shows wrong device count"` → fix is in the API/DB query → **BACKEND_ONLY**
|
|
748
|
+
> `"Button layout is broken"` → fix is in the React component → **FRONTEND_ONLY**
|
|
749
|
+
|
|
750
|
+
**Parameters:**
|
|
751
|
+
|
|
752
|
+
| Parameter | Type | Required | Description |
|
|
753
|
+
|---|---|---|---|
|
|
754
|
+
| `work_item_id` | string | Yes | Azure DevOps work item ID, e.g. `BUG-1140` or `2847` |
|
|
755
|
+
| `title` | string | Yes | Full task title from Azure DevOps |
|
|
756
|
+
| `description` | string | Yes | Full task description / problem statement — paste everything |
|
|
757
|
+
| `task_type` | enum | Yes | `bug` \| `user_story` \| `feature` \| `hotfix` \| `refactor` \| `chore` |
|
|
758
|
+
|
|
759
|
+
**The 5 analysis passes:**
|
|
760
|
+
|
|
761
|
+
| Pass | What it does |
|
|
762
|
+
|---|---|
|
|
763
|
+
| **Pass 1 — Weighted signal tiers** | Tier 1: service names = 10pts each (e.g. `sfn-events`, `DeviceRepository`). Tier 2: tech constructs = 5pts (e.g. `NullPointerException`, `@Query`, `Flyway`). Tier 3: domain terms = 2-3pts. Tier 4: generic words = 1pt. |
|
|
764
|
+
| **Pass 2 — Negation detection** | Scans each sentence. `"not a UI issue"` → frontend penalty. `"backend is fine"` → backend penalty. Each negated sentence subtracts 8pts from the relevant domain. |
|
|
765
|
+
| **Pass 3 — Root-cause phrase extraction** | 25 backend patterns + 9 frontend patterns matched via regex. `"shows wrong count"` → +12 backend. `"data not saved"` → +12 backend. `"layout broken"` → +10 frontend. |
|
|
766
|
+
| **Pass 4 — Bug disambiguation matrix** | For `task_type: bug`: data-correctness → +15 backend, exception/crash → +15 backend, auth/permission → +12 backend, CRUD failure → +12 backend, performance → +10 backend. |
|
|
767
|
+
| **Pass 5 — Confidence gate** | `HIGH` only when dominant score ≥ 1.8× second-place **AND** at least one Tier 1/2 signal matched. Generic words alone cannot produce HIGH confidence. |
|
|
768
|
+
|
|
769
|
+
**Output — `allowed_next_action`:**
|
|
770
|
+
|
|
771
|
+
| Value | Meaning | What the AI does |
|
|
772
|
+
|---|---|---|
|
|
773
|
+
| `PROCEED` | `BACKEND_ONLY` HIGH confidence | Moves directly to Phase 0.7 plan presentation |
|
|
774
|
+
| `CONFIRM` | Mixed / LOW / extension | Presents analysis report, waits for developer YES |
|
|
775
|
+
| `STOP` | `FRONTEND_ONLY` | Hard stop — routes to frontend team, no code written |
|
|
776
|
+
|
|
777
|
+
**Mixed signal resolution:** Backend dominates only when `backendScore ≥ 2.5× frontendScore`. Below that threshold → `FULL_STACK` (requires confirmation).
|
|
778
|
+
|
|
779
|
+
**Persistence:** Result saved to `.secufusion/classifications/{work_item_id}.json`. Resuming a classified task skips re-classification and loads the prior result.
|
|
780
|
+
|
|
781
|
+
**Example output for a bug:**
|
|
782
|
+
```
|
|
783
|
+
✅ BACKEND_ONLY (HIGH confidence)
|
|
784
|
+
|
|
785
|
+
Weighted scores: Backend=47 | Frontend=3 | Extension=0
|
|
786
|
+
Score ratio: 15.7x dominant
|
|
787
|
+
Root-cause evidence: [BE+12] data correctness → backend query | [BE+12] persistence failure → backend
|
|
788
|
+
Bug heuristic: data-correctness bug → +15 backend (API/DB likely source)
|
|
789
|
+
Classification reason: Backend dominates (47 vs FE:3 EXT:0) — frontend signals are noise
|
|
790
|
+
|
|
791
|
+
Proceeding to plan presentation. No developer confirmation needed.
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
796
|
+
## What Changed — Strict Enforcement Update
|
|
797
|
+
|
|
798
|
+
### `classify_task` — Deep analysis engine (replaces keyword counting)
|
|
799
|
+
|
|
800
|
+
| Before | After |
|
|
801
|
+
|---|---|
|
|
802
|
+
| Flat keyword counting — every word scored equally | 4-tier weighted scoring — service names = 10× generic words |
|
|
803
|
+
| `"dashboard"` → scored as frontend | Root-cause phrases — `"shows wrong count on dashboard"` → backend +12 |
|
|
804
|
+
| No negation awareness | Sentence-level negation — `"not a UI issue"` removes frontend weight |
|
|
805
|
+
| Bug heuristic: default to backend only on LOW confidence | 5-category bug disambiguation matrix (+12–15pts per category) |
|
|
806
|
+
| HIGH confidence even on equal scores | HIGH only when ratio ≥ 1.8× AND Tier 1/2 signal matched |
|
|
807
|
+
| Mixed signals → always FULL_STACK | Backend dominates at 2.5× → classified BACKEND_ONLY, frontend treated as noise |
|
|
808
|
+
|
|
809
|
+
### `AGENTS.md` — All phases rewritten to strict enforcement
|
|
810
|
+
|
|
811
|
+
| Phase | Before | After |
|
|
812
|
+
|---|---|---|
|
|
813
|
+
| **Phase 00** | Bullet list, no gate | MANDATORY 3-step sequence + ❌ prohibition list |
|
|
814
|
+
| **Phase 0 (Resume)** | `"Call read_summary, begin executing"` | Explicit STEP 1/2/3 + ❌ list (no guessing, no re-reading) |
|
|
815
|
+
| **Phase 0.7 (Plan Gate)** | `"Build a plan"`, soft suggestions | Every plan section is **mandatory** — omitting any = violation. Explicit proceed/adjust/cancel contract. |
|
|
816
|
+
| **Phase 1 (Planning)** | `"Call search_tasks if relevant"` | `search_tasks` is **unconditional** — STEP 1 always, even if "sure" there's no prior work |
|
|
817
|
+
| **Phase 2 (Execution)** | Bullet suggestions | MANDATORY code block for all 4 tool calls + `next_step` contract with explicit VIOLATION labels |
|
|
818
|
+
| **Phase 3 (Correction)** | `"Immediately call log_rejected_pattern"` | Explicit STEP 1/2/3 + ❌ list — log immediately, not end of session |
|
|
819
|
+
| **Phase 4 (PR Handoff)** | `"Call complete → run checks → fix if error"` | Explicit STEP 1-4 **fix → recheck loop** until ZERO errors |
|
|
820
|
+
| **Guardrails** | Mixed soft/hard language | All `should` → `MUST`, all `avoid` → `FORBIDDEN`, linter errors explicitly blocking |
|
|
821
|
+
| **Cross-Task Intelligence** | Prose bullets | MANDATORY STEP 1-4 sequence + ❌ list |
|
|
706
822
|
|
|
707
823
|
---
|
|
708
824
|
|
package/index.js
CHANGED
|
@@ -842,26 +842,34 @@ server.tool("manage_project_spec", "Manages the .secufusion-project-spec.json fi
|
|
|
842
842
|
// ─── Task management helpers ──────────────────────────────────────────────────
|
|
843
843
|
const TASKS_DIR = ".secufusion/tasks";
|
|
844
844
|
const REGISTRY_FILE = ".secufusion/registry.json";
|
|
845
|
-
function
|
|
846
|
-
const
|
|
847
|
-
const
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
845
|
+
function resolveTaskFolder(workItemId, tasksRoot, registryData) {
|
|
846
|
+
const registry = registryData || readRegistry();
|
|
847
|
+
const entry = registry.find(t => t["work_item_id"] === workItemId);
|
|
848
|
+
// 1. Try registry first
|
|
849
|
+
if (entry && entry["folder"]) {
|
|
850
|
+
const folderPath = path.join(tasksRoot, entry["folder"]);
|
|
851
|
+
if (fs.existsSync(folderPath))
|
|
852
|
+
return folderPath;
|
|
853
|
+
}
|
|
854
|
+
// 2. Fallback: scan directory
|
|
855
|
+
if (fs.existsSync(tasksRoot)) {
|
|
856
|
+
const taskFolders = fs.readdirSync(tasksRoot);
|
|
857
|
+
const match = taskFolders.find(f => f.startsWith(workItemId + "-") || f === workItemId || f === `WI-${workItemId}`);
|
|
858
|
+
if (match)
|
|
859
|
+
return path.join(tasksRoot, match);
|
|
860
|
+
}
|
|
861
|
+
// 3. Not found
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
function generateNewTaskFolder(workItemId, title, tasksRoot) {
|
|
865
|
+
const slug = title.toLowerCase()
|
|
866
|
+
.replace(/[:\s]+/g, '-')
|
|
867
|
+
.replace(/[^a-z0-9-]+/g, '')
|
|
868
|
+
.replace(/-+/g, '-')
|
|
869
|
+
.replace(/^-|-$/g, '')
|
|
870
|
+
.slice(0, 50)
|
|
871
|
+
.replace(/-+$/, '');
|
|
872
|
+
return path.join(tasksRoot, `${workItemId}-${slug}`);
|
|
865
873
|
}
|
|
866
874
|
function readJson(filePath) {
|
|
867
875
|
const raw = readFileSafe(filePath);
|
|
@@ -910,7 +918,17 @@ server.tool("manage_task", "Manages structured task records in .secufusion/tasks
|
|
|
910
918
|
scenario_type: z.enum(["unit", "integration", "e2e", "manual"]).optional().describe("Test scenario type. Used with 'add_scenario'."),
|
|
911
919
|
}, async ({ action, work_item_id, title, description, acceptance_criteria, pending_acs, completed_acs, next_step, tags, file_path, change_summary, decision, rationale, scenario, scenario_type }) => {
|
|
912
920
|
const inputChars = JSON.stringify({ action, work_item_id, title, description, acceptance_criteria, pending_acs, completed_acs, next_step, tags, file_path, change_summary, decision, rationale, scenario, scenario_type }).length;
|
|
913
|
-
const
|
|
921
|
+
const tasksRoot = resolve(TASKS_DIR);
|
|
922
|
+
let dir = resolveTaskFolder(work_item_id, tasksRoot);
|
|
923
|
+
if (action === "initialize") {
|
|
924
|
+
if (!title)
|
|
925
|
+
return appendTelemetry({ isError: true, content: [{ type: "text", text: "ERROR: 'title' is required for action=initialize." }] }, inputChars);
|
|
926
|
+
if (!dir)
|
|
927
|
+
dir = generateNewTaskFolder(work_item_id, title, tasksRoot);
|
|
928
|
+
}
|
|
929
|
+
if (!dir && action !== "initialize") {
|
|
930
|
+
return appendTelemetry({ isError: true, content: [{ type: "text", text: `Task WI-${work_item_id} not found in ${tasksRoot}. Has it been initialized?` }] }, inputChars);
|
|
931
|
+
}
|
|
914
932
|
const specPath = path.join(dir, "spec.json");
|
|
915
933
|
const progressPath = path.join(dir, "progress.json");
|
|
916
934
|
const decisionsPath = path.join(dir, "decisions.json");
|
|
@@ -930,7 +948,7 @@ server.tool("manage_task", "Manages structured task records in .secufusion/tasks
|
|
|
930
948
|
writeFile(filesPath, JSON.stringify([], null, 2));
|
|
931
949
|
writeFile(scenariosPath, JSON.stringify([], null, 2));
|
|
932
950
|
}
|
|
933
|
-
upsertRegistry({ work_item_id, title, status: "active", created_at: now, tags: tags || [] });
|
|
951
|
+
upsertRegistry({ work_item_id, title, status: "active", created_at: now, tags: tags || [], folder: path.basename(dir) });
|
|
934
952
|
return appendTelemetry({ content: [{ type: "text", text: `✅ Task WI-${work_item_id} initialized.\n\n**Folder:** ${dir}\n**Title:** ${title}\n**ACs:** ${(acceptance_criteria || []).length}\n\n${JSON.stringify(spec, null, 2)}` }] }, inputChars);
|
|
935
953
|
}
|
|
936
954
|
if (!fs.existsSync(specPath)) {
|
|
@@ -1032,9 +1050,10 @@ server.tool("manage_task", "Manages structured task records in .secufusion/tasks
|
|
|
1032
1050
|
// ─── Tool 6: get_task_history ─────────────────────────────────────────────────
|
|
1033
1051
|
server.tool("get_task_history", "Retrieve the full history of a past task — spec, decisions, files touched, and scenarios. Use before starting similar work to understand how it was done before.", { work_item_id: z.string().describe("Work item ID of the past task to retrieve.") }, async ({ work_item_id }) => {
|
|
1034
1052
|
const inputChars = JSON.stringify({ work_item_id }).length;
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1053
|
+
const tasksRoot = resolve(TASKS_DIR);
|
|
1054
|
+
const dir = resolveTaskFolder(work_item_id, tasksRoot);
|
|
1055
|
+
if (!dir) {
|
|
1056
|
+
return appendTelemetry({ isError: true, content: [{ type: "text", text: `Task WI-${work_item_id} not found in ${tasksRoot}. Has it been initialized? Run manage_task action=initialize first.` }] }, inputChars);
|
|
1038
1057
|
}
|
|
1039
1058
|
const history = {
|
|
1040
1059
|
spec: readJson(path.join(dir, "spec.json")),
|
|
@@ -1056,10 +1075,13 @@ server.tool("search_tasks", "Search all past tasks by keyword. Scans registry an
|
|
|
1056
1075
|
return appendTelemetry({ content: [{ type: "text", text: "No tasks found in registry. Initialize tasks first." }] }, inputChars);
|
|
1057
1076
|
const terms = keywords.toLowerCase().split(/\s+/).filter(Boolean);
|
|
1058
1077
|
const matches = [];
|
|
1078
|
+
const tasksRoot = resolve(TASKS_DIR);
|
|
1059
1079
|
for (const entry of registry) {
|
|
1060
1080
|
if (status_filter !== "any" && entry["status"] !== status_filter)
|
|
1061
1081
|
continue;
|
|
1062
|
-
const dir =
|
|
1082
|
+
const dir = resolveTaskFolder(entry["work_item_id"], tasksRoot, registry);
|
|
1083
|
+
if (!dir)
|
|
1084
|
+
continue;
|
|
1063
1085
|
const blobs = [
|
|
1064
1086
|
JSON.stringify(entry),
|
|
1065
1087
|
readFileSafe(path.join(dir, "spec.json")) || "",
|
|
@@ -1082,8 +1104,9 @@ server.tool("search_tasks", "Search all past tasks by keyword. Scans registry an
|
|
|
1082
1104
|
// ─── Tool 8: get_pattern_from_task ────────────────────────────────────────────
|
|
1083
1105
|
server.tool("get_pattern_from_task", "Extract reusable implementation patterns from a completed task — decisions made, files modified, and test scenarios used. Call this before implementing something similar to a past feature.", { work_item_id: z.string().describe("Work item ID of the past task to extract patterns from.") }, async ({ work_item_id }) => {
|
|
1084
1106
|
const inputChars = JSON.stringify({ work_item_id }).length;
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1107
|
+
const tasksRoot = resolve(TASKS_DIR);
|
|
1108
|
+
const dir = resolveTaskFolder(work_item_id, tasksRoot);
|
|
1109
|
+
if (!dir)
|
|
1087
1110
|
return appendTelemetry({ isError: true, content: [{ type: "text", text: `Task WI-${work_item_id} not found. Cannot extract patterns.` }] }, inputChars);
|
|
1088
1111
|
const spec = readJson(path.join(dir, "spec.json")) || {};
|
|
1089
1112
|
const decisions = readJson(path.join(dir, "decisions.json")) || [];
|