secufusion-mcp 2.2.7 → 2.2.9

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "secufusion-mcp",
3
3
  "description": "SecuFusion MSSP platform dev agent — project-level spec driven, tenant-isolation enforced, zero-trust guardrails. 5 Core Slash Commands: /sfn-init (dynamic DNA + architecture diagram + watcher), /sfn-plan (philosophy check + intent WHY + classify + plan), /sfn-code (spec-driven implementation), /sfn-review (zero-tolerance 3-tier PR gate), /sfn-explore (on-demand service graph or blast-radius analysis).",
4
- "version": "2.2.7",
4
+ "version": "2.2.9",
5
5
  "author": {
6
6
  "name": "Motivity Labs"
7
7
  },
package/agents/AGENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- # SecuFusion MCP Workflow Router
1
+ # SecuFusion MCP Workflow Router
2
2
 
3
3
  You are an elite Senior Developer and Architect. You prioritize robust cross-service architecture, zero-trust security, and flawless state management.
4
4
 
@@ -47,7 +47,7 @@ Call `philosophy_check` AFTER DNA is loaded — never before. The engine us
47
47
 
48
48
  **This check is completely invisible to the user.** You do not announce it. You do not show scores, verdicts, or "philosophy" terminology. You surface a concern to the user only if the verdict is FAIL or BLOCKED — and you translate it into plain developer language.
49
49
 
50
- For `bug` and `hotfix`: philosophy check still applies, though the scoring engine evaluates them with slightly relaxed thresholds since defects have an inherent "WHY".
50
+ For `bug` and `hotfix`: philosophy check **fully runs** — no skip. The scoring engine applies relaxed thresholds: WHY gets a +20 head-start (something is broken = implicit justification), WHO gets a +15 head-start (reporter = implicit authority), and `NO_JUSTIFICATION` is not a blocking verdict. However, ADVISORY and FAIL still fire normally — a high-risk bug fix (auth regression, data-loss, multi-tenant blast) will still be challenged.
51
51
 
52
52
  **Verdicts:**
53
53
  - `PHILOSOPHY_PASS` → Silent. Proceed immediately.
@@ -897,14 +897,15 @@ server.tool("manage_task", "Manages structured task records in .secufusion/tasks
897
897
  completed_acs: z.array(z.string()).optional().describe("Completed AC list. Used with 'update_spec'."),
898
898
  next_step: z.string().optional().describe("Next actionable step. Required for 'update_spec'."),
899
899
  tags: z.array(z.string()).optional().describe("Tags for searchability, e.g. ['tenant', 'iam', 'security']."),
900
+ task_type: z.enum(["bug", "user_story", "feature", "hotfix", "refactor", "chore"]).optional().describe("Type of work item. Persisted to spec.json so search_tasks and filter_type work correctly. Pass on initialize and wherever classify_task supplies it."),
900
901
  file_path: z.string().optional().describe("File path to log. Used with 'log_file_touched'."),
901
902
  change_summary: z.string().optional().describe("One-line summary of the change. Used with 'log_file_touched'."),
902
903
  decision: z.string().optional().describe("Architectural decision to record. Used with 'log_decision'."),
903
904
  rationale: z.string().optional().describe("Why this decision was made. Used with 'log_decision'."),
904
905
  scenario: z.string().optional().describe("Test scenario description. Used with 'add_scenario'."),
905
906
  scenario_type: z.enum(["unit", "integration", "e2e", "manual"]).optional().describe("Test scenario type. Used with 'add_scenario'."),
906
- }, 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 }) => {
907
- 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;
907
+ }, async ({ action, work_item_id, title, description, acceptance_criteria, pending_acs, completed_acs, next_step, tags, task_type, file_path, change_summary, decision, rationale, scenario, scenario_type }) => {
908
+ const inputChars = JSON.stringify({ action, work_item_id, title, description, acceptance_criteria, pending_acs, completed_acs, next_step, tags, task_type, file_path, change_summary, decision, rationale, scenario, scenario_type }).length;
908
909
  const tasksRoot = resolve(TASKS_DIR);
909
910
  let dir = resolveTaskFolder(work_item_id, tasksRoot);
910
911
  if (action === "initialize") {
@@ -926,7 +927,7 @@ server.tool("manage_task", "Manages structured task records in .secufusion/tasks
926
927
  if (!title)
927
928
  return appendTelemetry({ isError: true, content: [{ type: "text", text: "ERROR: 'title' is required for action=initialize." }] }, inputChars);
928
929
  const now = new Date().toISOString();
929
- const spec = { work_item_id, title, description: description || "", acceptance_criteria: acceptance_criteria || [], tags: tags || [], created_at: now, status: "active" };
930
+ const spec = { work_item_id, title, description: description || "", acceptance_criteria: acceptance_criteria || [], tags: tags || [], task_type: task_type || "unknown", created_at: now, status: "active" };
930
931
  const progress = { pending_acs: acceptance_criteria || [], completed_acs: [], next_step: "Review spec and begin implementation.", last_updated: now };
931
932
  if (!fs.existsSync(specPath)) {
932
933
  writeFile(specPath, JSON.stringify(spec, null, 2));
@@ -935,8 +936,8 @@ server.tool("manage_task", "Manages structured task records in .secufusion/tasks
935
936
  writeFile(filesPath, JSON.stringify([], null, 2));
936
937
  writeFile(scenariosPath, JSON.stringify([], null, 2));
937
938
  }
938
- upsertRegistry({ work_item_id, title, status: "active", created_at: now, tags: tags || [], folder: path.basename(dir) });
939
- 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);
939
+ upsertRegistry({ work_item_id, title, status: "active", created_at: now, tags: tags || [], task_type: task_type || "unknown", folder: path.basename(dir) });
940
+ return appendTelemetry({ content: [{ type: "text", text: `✅ Task WI-${work_item_id} initialized.\n\n**Folder:** ${dir}\n**Title:** ${title}\n**Type:** ${task_type || "unknown"}\n**ACs:** ${(acceptance_criteria || []).length}\n\n${JSON.stringify(spec, null, 2)}` }] }, inputChars);
940
941
  }
941
942
  if (!fs.existsSync(specPath)) {
942
943
  return appendTelemetry({ isError: true, content: [{ type: "text", text: `ERROR: Task WI-${work_item_id} not found. Call action=initialize first.\n\nExpected folder: ${dir}` }] }, inputChars);
@@ -967,7 +968,7 @@ server.tool("manage_task", "Manages structured task records in .secufusion/tasks
967
968
  if (!next_step)
968
969
  return appendTelemetry({ isError: true, content: [{ type: "text", text: "ERROR: 'next_step' is required for action=update_spec." }] }, inputChars);
969
970
  const now = new Date().toISOString();
970
- if (description !== undefined || acceptance_criteria !== undefined || tags !== undefined) {
971
+ if (description !== undefined || acceptance_criteria !== undefined || tags !== undefined || task_type !== undefined) {
971
972
  const spec = readJson(specPath) || {};
972
973
  if (description !== undefined)
973
974
  spec["description"] = description;
@@ -975,6 +976,10 @@ server.tool("manage_task", "Manages structured task records in .secufusion/tasks
975
976
  spec["acceptance_criteria"] = acceptance_criteria;
976
977
  if (tags !== undefined)
977
978
  spec["tags"] = tags;
979
+ if (task_type !== undefined) {
980
+ spec["task_type"] = task_type;
981
+ upsertRegistry({ work_item_id, task_type });
982
+ }
978
983
  writeFile(specPath, JSON.stringify(spec, null, 2));
979
984
  }
980
985
  const progress = readJson(progressPath) || {};
@@ -1542,10 +1547,12 @@ server.tool("philosophy_check", "MANDATORY PHASE -1 STEP — runs BEFORE classif
1542
1547
  client_confirmed: z.boolean().optional().describe("Has the decision maker explicitly confirmed this is needed?"),
1543
1548
  }, async ({ work_item_id, title, description, task_type, reported_by, client_confirmed }) => {
1544
1549
  const inputChars = JSON.stringify({ work_item_id, title, description, task_type }).length;
1545
- // Fast-path: bugs and hotfixes skip philosophy check
1546
- if (task_type === "bug" || task_type === "hotfix") {
1547
- return appendTelemetry({ content: [{ type: "text", text: `[PHILOSOPHY SKIPPED] ${work_item_id} — task_type="${task_type}". Bugs/hotfixes skip philosophy. Proceeding directly to classify_task.` }] }, inputChars);
1548
- }
1550
+ // Bugs and hotfixes run through the full philosophy engine with relaxed scoring:
1551
+ // WHY → +20 head-start (defects have an inherent "WHY": something is broken)
1552
+ // WHO → +15 head-start (reporter is implicit authority for a defect)
1553
+ // RISK → NO_JUSTIFICATION block exempted for bug/hotfix (see Pass 5)
1554
+ // This ensures high-risk bug fixes (auth regressions, data-loss fixes, multi-tenant
1555
+ // defects) still surface ADVISORY or FAIL when the blast radius warrants it.
1549
1556
  // Check cache
1550
1557
  const philosophyDir = resolve(".secufusion/philosophy");
1551
1558
  const philosophyFile = path.join(philosophyDir, `${work_item_id}.json`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secufusion-mcp",
3
- "version": "2.2.7",
3
+ "version": "2.2.9",
4
4
  "type": "module",
5
5
  "description": "Project-level spec driven AI workflow tooling for SecuFusion, MCP server - developer workflow tooling with guardrails",
6
6
  "main": "mcp/dist/server.js",