diff --git a/README.md b/README.md index 6e60fd7..ff94455 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ running package installs. ## Workflow Skills DevNexus TypeScript ships baseline workflow skills for TypeScript diagnosis, -bounded refactoring, project topology, test hygiene, API boundary review, and -dry-run codemod planning. The plugin config declares them as `projected_skill` -capabilities, and the package also exports DevNexus skill definitions for -projects that materialize package-owned skills: +bounded refactoring, project topology, quality feedback, test hygiene, API +boundary review, and dry-run codemod planning. The plugin config declares them +as `projected_skill` capabilities, and the package also exports DevNexus skill +definitions for projects that materialize package-owned skills: ```ts import { @@ -162,6 +162,9 @@ attention to new bugs, vulnerabilities, security hotspots, and critical or blocker findings. Rule playbooks cover `typescript:S3776`, `typescript:S5852`, and `typescript:S4036`; their references point to SonarSource guidance on cognitive complexity, regex backtracking, quality gates, and PATH trust review. +The plugin advertises these operations through its MCP capability list and +projects the `typescript-quality-feedback` skill so agents know when to use the +snapshot, delta, and rule playbook workflow. ## Boundaries diff --git a/skills/typescript-quality-feedback/SKILL.md b/skills/typescript-quality-feedback/SKILL.md new file mode 100644 index 0000000..5fb0c5e --- /dev/null +++ b/skills/typescript-quality-feedback/SKILL.md @@ -0,0 +1,87 @@ +--- +name: typescript-quality-feedback +description: TypeScript quality feedback workflow for using diagnostics, import graphs, Sonar JSON snapshots, touched-file deltas, and rule playbooks before and after source changes. Use when Codex must audit or improve TypeScript quality, triage Sonar findings, or avoid introducing new quality regressions. +--- + +# TypeScript Quality Feedback + +Use this skill when working on TypeScript or JavaScript source quality. + +1. Start with setup facts. Confirm the project has local dependency context, + TypeScript, a selected `tsconfig`, and usable package scripts. Report setup + blockers instead of installing packages or fetching tools automatically. +2. Take a read-only baseline before editing when quality tools are available: + TypeScript diagnostics, import graph cycles, and any local Sonar JSON exports. +3. Scope the work to touched files unless the work item explicitly asks for a + broad cleanup. Avoid hiding old debt behind unrelated edits. +4. After editing, compare before/after quality snapshots for touched files. + Call out new bugs, vulnerabilities, security hotspots, critical/blocker + findings, failed quality-gate conditions, diagnostics, and import cycles. +5. Treat new bugs, vulnerabilities, security hotspots, and critical/blocker + findings as stop-and-fix issues unless the user or project policy explicitly + accepts the risk. +6. Use focused verification first, then the nearest package check. Keep Sonar or + Docker-backed scans local unless the project has an explicit CI policy. +7. In the handoff, report the touched-file quality delta, serious remaining + findings, verification commands, and any deferred debt. + +## Rule Playbooks + +### `typescript:S3776` Cognitive Complexity + +Preferred fixes: + +- Extract helpers around cohesive decisions or output construction. +- Replace deep nesting with guards, early returns, or named predicate helpers. +- Move repeated condition matrices into data-driven policy tables when that + matches the domain. +- Keep behavior tests around the original path before splitting a large + function. + +Defer when the code is generated, adapter glue, or safer to split after a +broader boundary change. Do not create single-use helpers that make names worse +than the original branches. + +References: + +- https://www.sonarsource.com/resources/cognitive-complexity/ +- https://www.sonarsource.com/blog/5-clean-code-tips-for-reducing-cognitive-complexity/ + +### `typescript:S5852` Regex Backtracking Risk + +Preferred fixes: + +- Remove nested or overlapping quantifiers on attacker-controlled or unbounded + input. +- Constrain repetition with explicit character classes, anchors, and length + bounds. +- Replace the regex with a parser or linear scan when the accepted language is + not simple. +- Add worst-case tests for long non-matching inputs when the path stays regex + based. + +Defer only when the input is trusted, tightly bounded, and that bound is +enforced nearby, or when a proper parser needs separate design work. + +References: + +- https://www.sonarsource.com/blog/crafting-regexes-to-avoid-stack-overflows/ +- https://community.sonarsource.com/t/write-efficient-error-free-and-safe-regular-expressions-in-javascript-and-typescript/47720 + +### `typescript:S4036` PATH Trust Boundary + +Preferred fixes: + +- Resolve executable paths from trusted configuration or an allowlist instead of + ambient `PATH`. +- Sanitize `PATH` before process launch when ambient lookup is required. +- Keep process launch policy behind one helper so callers cannot bypass it. +- Test that untrusted path entries are rejected or ignored. + +Defer when the command is deliberately interactive developer tooling and the +trust model is documented, or when a proper fix depends on a broader credential, +runner, or host-policy boundary. + +Reference: + +- https://community.sonarsource.com/t/false-positive-for-rule-typescript-s4036/142908 diff --git a/skills/typescript-quality-feedback/agents/openai.yaml b/skills/typescript-quality-feedback/agents/openai.yaml new file mode 100644 index 0000000..fe9c277 --- /dev/null +++ b/skills/typescript-quality-feedback/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "TypeScript Quality Feedback" + short_description: "Use TypeScript and Sonar quality feedback." + default_prompt: "Use TypeScript Quality Feedback to audit touched-file quality and apply the relevant rule playbooks." diff --git a/skills/typescript-quality-feedback/dev-nexus.skill.json b/skills/typescript-quality-feedback/dev-nexus.skill.json new file mode 100644 index 0000000..fb94f42 --- /dev/null +++ b/skills/typescript-quality-feedback/dev-nexus.skill.json @@ -0,0 +1,14 @@ +{ + "id": "typescript-quality-feedback", + "name": "typescript-quality-feedback", + "description": "TypeScript quality feedback workflow for using diagnostics, import graphs, Sonar JSON snapshots, touched-file deltas, and rule playbooks before and after source changes. Use when Codex must audit or improve TypeScript quality, triage Sonar findings, or avoid introducing new quality regressions.", + "version": "0.1.0", + "license": "Apache-2.0", + "source": { + "type": "local", + "uri": "@evref-bl/dev-nexus-typescript/skills/typescript-quality-feedback" + }, + "supportedAgents": ["codex", "claude"], + "materialization": "copy", + "sourceControl": "support" +} diff --git a/src/devNexusTypeScriptPlugin.test.ts b/src/devNexusTypeScriptPlugin.test.ts index b17d9f8..39ede94 100644 --- a/src/devNexusTypeScriptPlugin.test.ts +++ b/src/devNexusTypeScriptPlugin.test.ts @@ -112,12 +112,14 @@ describe("DevNexus TypeScript plugin", () => { "skill-typescript-diagnose", "skill-typescript-refactor", "skill-typescript-project-topology", + "skill-typescript-quality-feedback", "skill-typescript-test-hygiene", "skill-typescript-api-boundaries", "skill-typescript-codemod-planning", "mcp-typescript-diagnostics-tracer", "mcp-typescript-import-graph-analysis", "mcp-typescript-bulk-rewrite-planning", + "mcp-typescript-quality-feedback", "context-typescript-toolchain-boundary", "briefing-typescript-worktree-setup", ]); @@ -200,6 +202,14 @@ describe("DevNexus TypeScript plugin", () => { skillId: "typescript-project-topology", targetAgents: ["codex", "claude"], }, + { + kind: "projected_skill", + id: "skill-typescript-quality-feedback", + description: + "Project the TypeScript quality feedback and Sonar rule playbook skill.", + skillId: "typescript-quality-feedback", + targetAgents: ["codex", "claude"], + }, { kind: "projected_skill", id: "skill-typescript-test-hygiene", @@ -236,6 +246,7 @@ describe("DevNexus TypeScript plugin", () => { "typescript-diagnose", "typescript-refactor", "typescript-project-topology", + "typescript-quality-feedback", "typescript-test-hygiene", "typescript-api-boundaries", "typescript-codemod-planning", @@ -297,6 +308,27 @@ describe("DevNexus TypeScript plugin", () => { }, ], }, + { + kind: "mcp_server", + id: "mcp-typescript-quality-feedback", + description: + "Advertise read-only TypeScript quality snapshot and delta operations.", + serverName: "dev-nexus-typescript", + command: devNexusTypeScriptMcpCommand, + args: [...devNexusTypeScriptMcpArgs], + tools: [ + { + name: "typescript.qualitySnapshot", + description: + "Read TypeScript diagnostics, import cycles, and Sonar JSON into one quality snapshot.", + }, + { + name: "typescript.qualityDelta", + description: + "Compare two TypeScript quality snapshots and highlight touched-file regressions.", + }, + ], + }, ]); const projected = projectPluginCapabilityProjections({ @@ -353,6 +385,26 @@ describe("DevNexus TypeScript plugin", () => { }, ], }); + expect( + projected[0]!.capabilities.find( + (capability) => capability.id === "mcp-typescript-quality-feedback", + ), + ).toMatchObject({ + kind: "mcp_server", + serverName: "dev-nexus-typescript", + tools: [ + { + name: "typescript.qualitySnapshot", + description: + "Read TypeScript diagnostics, import cycles, and Sonar JSON into one quality snapshot.", + }, + { + name: "typescript.qualityDelta", + description: + "Compare two TypeScript quality snapshots and highlight touched-file regressions.", + }, + ], + }); }); it("provides worker guidance without package-manager mutation", () => { @@ -366,6 +418,7 @@ describe("DevNexus TypeScript plugin", () => { expect(contextFragment.body).toContain("does not choose or supervise"); expect(contextFragment.body).toContain("report missing dependency context"); expect(contextFragment.body).toContain("instead of silently fetching packages"); + expect(contextFragment.body).toContain("quality snapshot"); expect(briefingFragment).toMatchObject({ id: "briefing-typescript-worktree-setup", @@ -374,6 +427,7 @@ describe("DevNexus TypeScript plugin", () => { expect(briefingFragment.body).toContain("Prefer existing package scripts"); expect(briefingFragment.body).toContain("Do not run npm, pnpm, yarn, or bun install"); expect(briefingFragment.body).toContain("instead of using npx package fetches"); + expect(briefingFragment.body).toContain("critical/blocker findings"); }); it("exposes plugin capabilities through DevNexus projection helpers", () => { @@ -386,7 +440,7 @@ describe("DevNexus TypeScript plugin", () => { pluginId: "dev-nexus-typescript", pluginName: "DevNexus TypeScript", version: devNexusTypeScriptPluginVersion, - capabilityCount: 12, + capabilityCount: 14, }, ]); expect(projected[0]!.capabilities.map((capability) => capability.kind)).toEqual([ @@ -397,6 +451,8 @@ describe("DevNexus TypeScript plugin", () => { "projected_skill", "projected_skill", "projected_skill", + "projected_skill", + "mcp_server", "mcp_server", "mcp_server", "mcp_server", @@ -416,12 +472,14 @@ describe("DevNexus TypeScript plugin", () => { "skill-typescript-diagnose", "skill-typescript-refactor", "skill-typescript-project-topology", + "skill-typescript-quality-feedback", "skill-typescript-test-hygiene", "skill-typescript-api-boundaries", "skill-typescript-codemod-planning", "mcp-typescript-diagnostics-tracer", "mcp-typescript-import-graph-analysis", "mcp-typescript-bulk-rewrite-planning", + "mcp-typescript-quality-feedback", "context-typescript-toolchain-boundary", "briefing-typescript-worktree-setup", "context-typescript-setup-inventory", diff --git a/src/devNexusTypeScriptPlugin.ts b/src/devNexusTypeScriptPlugin.ts index e9d445d..2a4e981 100644 --- a/src/devNexusTypeScriptPlugin.ts +++ b/src/devNexusTypeScriptPlugin.ts @@ -3,6 +3,7 @@ import type { TypeScriptProjectSetupInventory } from "./typeScriptProjectSetupIn import { devNexusTypeScriptBulkRewritePlanningCapability } from "./typeScriptBulkRewritePlanning.js"; import { devNexusTypeScriptImportGraphAnalysisCapability } from "./typeScriptImportGraphAnalysis.js"; import { devNexusTypeScriptMcpDiagnosticsTracerCapability } from "./typeScriptMcpDiagnosticsTracer.js"; +import { devNexusTypeScriptQualityFeedbackCapability } from "./typeScriptQualityFeedback.js"; import { typeScriptProjectSetupWorkerFragmentCapabilities } from "./typeScriptWorkerGuidance.js"; import { devNexusTypeScriptProjectedSkillCapabilities } from "./typeScriptWorkflowSkills.js"; @@ -44,6 +45,7 @@ export function devNexusTypeScriptDevNexusPluginConfig( devNexusTypeScriptMcpDiagnosticsTracerCapability(), devNexusTypeScriptImportGraphAnalysisCapability(), devNexusTypeScriptBulkRewritePlanningCapability(), + devNexusTypeScriptQualityFeedbackCapability(), { kind: "worker_context_fragment", id: "context-typescript-toolchain-boundary", @@ -52,6 +54,7 @@ export function devNexusTypeScriptDevNexusPluginConfig( "DevNexus TypeScript composes with DevNexus and does not choose or supervise implementation work.", "It contributes TypeScript and JavaScript setup policy through DevNexus plugin capabilities.", "Use projected dependencies when available, and report missing dependency context as a setup blocker instead of silently fetching packages.", + "For quality work, use read-only TypeScript diagnostics, import graph, quality snapshot, and quality delta tools before changing source.", ].join(" "), targetAgents: ["codex", "claude"], ...targetComponents, @@ -66,6 +69,7 @@ export function devNexusTypeScriptDevNexusPluginConfig( "Prefer existing package scripts such as test, check, typecheck, lint, or build.", "Do not run npm, pnpm, yarn, or bun install commands unless the project policy or user explicitly allows dependency installation.", "If package binaries are unavailable, report the setup blocker instead of using npx package fetches as a substitute.", + "When touching quality findings, compare before/after quality snapshots for touched files and call out new bugs, vulnerabilities, security hotspots, and critical/blocker findings.", ].join(" "), targetAgents: ["codex", "claude"], ...targetComponents, diff --git a/src/typeScriptQualityFeedback.ts b/src/typeScriptQualityFeedback.ts index 02943e8..43cccc0 100644 --- a/src/typeScriptQualityFeedback.ts +++ b/src/typeScriptQualityFeedback.ts @@ -1,10 +1,16 @@ import fs from "node:fs"; import path from "node:path"; +import type { NexusPluginMcpServerCapability } from "dev-nexus"; import { analyzeTypeScriptImportGraph } from "./typeScriptImportGraphAnalysis.js"; import { traceTypeScriptDiagnostics, type TypeScriptMcpTraceInput, } from "./typeScriptMcpDiagnosticsTracer.js"; +import { + devNexusTypeScriptMcpArgs, + devNexusTypeScriptMcpCommand, + devNexusTypeScriptMcpServerName, +} from "./typeScriptMcpServerConfig.js"; import type { TypeScriptImportGraphInput } from "./typeScriptImportGraphAnalysis.js"; import type { TypeScriptSetupFinding } from "./typeScriptProjectSetupInventory.js"; @@ -275,6 +281,22 @@ export function createTypeScriptQualityAnalyzer(): TypeScriptQualityAnalyzer { }; } +export function devNexusTypeScriptQualityFeedbackCapability(): NexusPluginMcpServerCapability { + return { + kind: "mcp_server", + id: "mcp-typescript-quality-feedback", + description: + "Advertise read-only TypeScript quality snapshot and delta operations.", + serverName: devNexusTypeScriptMcpServerName, + command: devNexusTypeScriptMcpCommand, + args: [...devNexusTypeScriptMcpArgs], + tools: typeScriptQualityFeedbackToolDescriptors.map((tool) => ({ + name: tool.name, + description: tool.description, + })), + } as NexusPluginMcpServerCapability; +} + export function readTypeScriptQualitySnapshot( input: TypeScriptQualitySnapshotInput, ): TypeScriptQualitySnapshot { diff --git a/src/typeScriptWorkflowSkills.test.ts b/src/typeScriptWorkflowSkills.test.ts index 8077b0e..9399284 100644 --- a/src/typeScriptWorkflowSkills.test.ts +++ b/src/typeScriptWorkflowSkills.test.ts @@ -10,6 +10,7 @@ describe("DevNexus TypeScript workflow skills", () => { "typescript-diagnose", "typescript-refactor", "typescript-project-topology", + "typescript-quality-feedback", "typescript-test-hygiene", "typescript-api-boundaries", "typescript-codemod-planning", @@ -21,6 +22,7 @@ describe("DevNexus TypeScript workflow skills", () => { "typescript-diagnose", "typescript-refactor", "typescript-project-topology", + "typescript-quality-feedback", "typescript-test-hygiene", "typescript-api-boundaries", "typescript-codemod-planning", @@ -59,5 +61,10 @@ describe("DevNexus TypeScript workflow skills", () => { (definition) => definition.manifest.id === "typescript-project-topology", )!.files["REFERENCES.md"], ).toContain("https://www.typescriptlang.org/tsconfig/rootDir.html"); + expect( + definitions.find( + (definition) => definition.manifest.id === "typescript-quality-feedback", + )!.files["SKILL.md"], + ).toContain("typescript:S3776"); }); }); diff --git a/src/typeScriptWorkflowSkills.ts b/src/typeScriptWorkflowSkills.ts index 6d4d528..e76a4c5 100644 --- a/src/typeScriptWorkflowSkills.ts +++ b/src/typeScriptWorkflowSkills.ts @@ -11,6 +11,7 @@ export const devNexusTypeScriptWorkflowSkillIds = [ "typescript-diagnose", "typescript-refactor", "typescript-project-topology", + "typescript-quality-feedback", "typescript-test-hygiene", "typescript-api-boundaries", "typescript-codemod-planning", @@ -29,6 +30,8 @@ const projectedSkillDescriptions: Record< "Project the bounded TypeScript refactoring workflow skill.", "typescript-project-topology": "Project the TypeScript project directory topology workflow skill.", + "typescript-quality-feedback": + "Project the TypeScript quality feedback and Sonar rule playbook skill.", "typescript-test-hygiene": "Project the TypeScript test hygiene workflow skill.", "typescript-api-boundaries": "Project the TypeScript API boundary review workflow skill.",