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.
Files changed (3) hide show
  1. package/README.md +137 -21
  2. package/index.js +51 -28
  3. 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 **nine powerful tools** to enforce SecuFusion's engineering standards throughout the development lifecycle:
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/WI-{id}/` folder with spec, progress, decisions, files-touched, scenarios, and PR summary |
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 ever initialized — used by `search_tasks` | ✅ Yes |
411
- | `.secufusion/tasks/WI-{id}/spec.json` | Task spec — title, description, ACs, tags, status | ✅ Yes |
412
- | `.secufusion/tasks/WI-{id}/progress.json` | AC tracking — pending, completed, next_step | ✅ Yes |
413
- | `.secufusion/tasks/WI-{id}/decisions.json` | Architectural decisions log (including rollback strategy) | ✅ Yes |
414
- | `.secufusion/tasks/WI-{id}/files-touched.json` | All files modified with change summaries | ✅ Yes |
415
- | `.secufusion/tasks/WI-{id}/scenarios.json` | Test scenarios (unit / integration / e2e / manual) | ✅ Yes |
416
- | `.secufusion/tasks/WI-{id}/pr-summary.md` | Auto-generated PR summary on `manage_task complete` | ✅ Yes |
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 │ CLASSIFY: BACKEND_ONLY / FRONTEND_ONLY / FULL_STACK │
437
- │ Ownership │ Step 3: Performance risk scan (GREEN/AMBER/RED verdict) │
438
- │ Check │ Step 4: Breaking change scan (endpoints/entities/Kafka/ext) │
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/WI-{id}/ with all 5 files │
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/WI-2847/`:**
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/WI-{id}/` — one 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.
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: classify BACKEND_ONLY/FRONTEND_ONLY/FULL_STACK
653
- + performance risk scan + breaking change scan
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 → .secufusion/tasks/WI-1042/ created
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 → must pass before raising PR
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 classifies the task (backend/frontend/full-stack), 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**.
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 taskDir(workItemId, title) {
846
- const baseDir = resolve(TASKS_DIR);
847
- const prefixes = [workItemId + "-", "WI-" + workItemId + "-"];
848
- if (fs.existsSync(baseDir)) {
849
- const folders = fs.readdirSync(baseDir);
850
- // Find an existing folder that starts with the workItemId
851
- const match = folders.find(f => f === workItemId ||
852
- f === `WI-${workItemId}` ||
853
- prefixes.some(p => f.startsWith(p)));
854
- if (match) {
855
- return resolve(`${TASKS_DIR}/${match}`);
856
- }
857
- }
858
- // If no folder exists, generate a new one
859
- if (title) {
860
- const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').substring(0, 50);
861
- return resolve(`${TASKS_DIR}/${workItemId}-${slug}`);
862
- }
863
- // Fallback if no title is provided and folder doesn't exist
864
- return resolve(`${TASKS_DIR}/${workItemId}`);
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 dir = taskDir(work_item_id, action === "initialize" ? title : undefined);
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 dir = taskDir(work_item_id);
1036
- if (!fs.existsSync(dir)) {
1037
- return appendTelemetry({ isError: true, content: [{ type: "text", text: `Task WI-${work_item_id} not found in ${resolve(TASKS_DIR)}.` }] }, inputChars);
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 = taskDir(entry["work_item_id"]);
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 dir = taskDir(work_item_id);
1086
- if (!fs.existsSync(dir))
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")) || [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secufusion-mcp",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "type": "module",
5
5
  "description": "SecuFusion MCP server - developer workflow tooling with guardrails",
6
6
  "main": "index.js",