@specforge/cli 0.1.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/README.md +781 -0
- package/bin/specforge +21 -0
- package/dist/channel.d.ts +27 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +22 -0
- package/dist/channel.js.map +1 -0
- package/dist/cli/commands/configure.d.ts +16 -0
- package/dist/cli/commands/configure.d.ts.map +1 -0
- package/dist/cli/commands/configure.js +186 -0
- package/dist/cli/commands/configure.js.map +1 -0
- package/dist/cli/commands/configure.types.d.ts +110 -0
- package/dist/cli/commands/configure.types.d.ts.map +1 -0
- package/dist/cli/commands/configure.types.js +82 -0
- package/dist/cli/commands/configure.types.js.map +1 -0
- package/dist/cli/commands/debug/call.d.ts +14 -0
- package/dist/cli/commands/debug/call.d.ts.map +1 -0
- package/dist/cli/commands/debug/call.js +96 -0
- package/dist/cli/commands/debug/call.js.map +1 -0
- package/dist/cli/commands/debug/index.d.ts +12 -0
- package/dist/cli/commands/debug/index.d.ts.map +1 -0
- package/dist/cli/commands/debug/index.js +14 -0
- package/dist/cli/commands/debug/index.js.map +1 -0
- package/dist/cli/commands/debug/register.d.ts +11 -0
- package/dist/cli/commands/debug/register.d.ts.map +1 -0
- package/dist/cli/commands/debug/register.js +78 -0
- package/dist/cli/commands/debug/register.js.map +1 -0
- package/dist/cli/commands/debug/test.d.ts +13 -0
- package/dist/cli/commands/debug/test.d.ts.map +1 -0
- package/dist/cli/commands/debug/test.js +91 -0
- package/dist/cli/commands/debug/test.js.map +1 -0
- package/dist/cli/commands/debug/tools.d.ts +13 -0
- package/dist/cli/commands/debug/tools.d.ts.map +1 -0
- package/dist/cli/commands/debug/tools.js +86 -0
- package/dist/cli/commands/debug/tools.js.map +1 -0
- package/dist/cli/commands/debug/types.d.ts +119 -0
- package/dist/cli/commands/debug/types.d.ts.map +1 -0
- package/dist/cli/commands/debug/types.js +36 -0
- package/dist/cli/commands/debug/types.js.map +1 -0
- package/dist/cli/commands/debug/whoami.d.ts +13 -0
- package/dist/cli/commands/debug/whoami.d.ts.map +1 -0
- package/dist/cli/commands/debug/whoami.js +131 -0
- package/dist/cli/commands/debug/whoami.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +16 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +413 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/doctor.types.d.ts +180 -0
- package/dist/cli/commands/doctor.types.d.ts.map +1 -0
- package/dist/cli/commands/doctor.types.js +140 -0
- package/dist/cli/commands/doctor.types.js.map +1 -0
- package/dist/cli/commands/feedback.d.ts +27 -0
- package/dist/cli/commands/feedback.d.ts.map +1 -0
- package/dist/cli/commands/feedback.js +262 -0
- package/dist/cli/commands/feedback.js.map +1 -0
- package/dist/cli/commands/feedback.types.d.ts +116 -0
- package/dist/cli/commands/feedback.types.d.ts.map +1 -0
- package/dist/cli/commands/feedback.types.js +65 -0
- package/dist/cli/commands/feedback.types.js.map +1 -0
- package/dist/cli/commands/index.d.ts +15 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +21 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.d.ts +17 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +773 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/init.types.d.ts +193 -0
- package/dist/cli/commands/init.types.d.ts.map +1 -0
- package/dist/cli/commands/init.types.js +29 -0
- package/dist/cli/commands/init.types.js.map +1 -0
- package/dist/cli/commands/invitations.d.ts +31 -0
- package/dist/cli/commands/invitations.d.ts.map +1 -0
- package/dist/cli/commands/invitations.js +250 -0
- package/dist/cli/commands/invitations.js.map +1 -0
- package/dist/cli/commands/login.d.ts +40 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +139 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/members.d.ts +55 -0
- package/dist/cli/commands/members.d.ts.map +1 -0
- package/dist/cli/commands/members.js +296 -0
- package/dist/cli/commands/members.js.map +1 -0
- package/dist/cli/commands/scaffold/agent-types.d.ts +54 -0
- package/dist/cli/commands/scaffold/agent-types.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/agent-types.js +22 -0
- package/dist/cli/commands/scaffold/agent-types.js.map +1 -0
- package/dist/cli/commands/scaffold/display.d.ts +56 -0
- package/dist/cli/commands/scaffold/display.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/display.js +217 -0
- package/dist/cli/commands/scaffold/display.js.map +1 -0
- package/dist/cli/commands/scaffold/files.d.ts +99 -0
- package/dist/cli/commands/scaffold/files.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/files.js +122 -0
- package/dist/cli/commands/scaffold/files.js.map +1 -0
- package/dist/cli/commands/scaffold/index.d.ts +13 -0
- package/dist/cli/commands/scaffold/index.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/index.js +12 -0
- package/dist/cli/commands/scaffold/index.js.map +1 -0
- package/dist/cli/commands/scaffold/register.d.ts +11 -0
- package/dist/cli/commands/scaffold/register.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/register.js +11 -0
- package/dist/cli/commands/scaffold/register.js.map +1 -0
- package/dist/cli/commands/scaffold/scaffold.d.ts +11 -0
- package/dist/cli/commands/scaffold/scaffold.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/scaffold.js +728 -0
- package/dist/cli/commands/scaffold/scaffold.js.map +1 -0
- package/dist/cli/commands/scaffold/targets.d.ts +60 -0
- package/dist/cli/commands/scaffold/targets.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/targets.js +251 -0
- package/dist/cli/commands/scaffold/targets.js.map +1 -0
- package/dist/cli/commands/scaffold/types.d.ts +170 -0
- package/dist/cli/commands/scaffold/types.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/types.js +12 -0
- package/dist/cli/commands/scaffold/types.js.map +1 -0
- package/dist/cli/commands/scaffold/utils.d.ts +89 -0
- package/dist/cli/commands/scaffold/utils.d.ts.map +1 -0
- package/dist/cli/commands/scaffold/utils.js +401 -0
- package/dist/cli/commands/scaffold/utils.js.map +1 -0
- package/dist/cli/commands/status.d.ts +19 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +124 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/status.types.d.ts +37 -0
- package/dist/cli/commands/status.types.d.ts.map +1 -0
- package/dist/cli/commands/status.types.js +7 -0
- package/dist/cli/commands/status.types.js.map +1 -0
- package/dist/cli/commands/switch.d.ts +18 -0
- package/dist/cli/commands/switch.d.ts.map +1 -0
- package/dist/cli/commands/switch.js +134 -0
- package/dist/cli/commands/switch.js.map +1 -0
- package/dist/cli/commands/switch.types.d.ts +123 -0
- package/dist/cli/commands/switch.types.d.ts.map +1 -0
- package/dist/cli/commands/switch.types.js +46 -0
- package/dist/cli/commands/switch.types.js.map +1 -0
- package/dist/cli/config/agent-teams.types.d.ts +120 -0
- package/dist/cli/config/agent-teams.types.d.ts.map +1 -0
- package/dist/cli/config/agent-teams.types.js +23 -0
- package/dist/cli/config/agent-teams.types.js.map +1 -0
- package/dist/cli/config/index.d.ts +13 -0
- package/dist/cli/config/index.d.ts.map +1 -0
- package/dist/cli/config/index.js +7 -0
- package/dist/cli/config/index.js.map +1 -0
- package/dist/cli/config/loader.d.ts +114 -0
- package/dist/cli/config/loader.d.ts.map +1 -0
- package/dist/cli/config/loader.js +198 -0
- package/dist/cli/config/loader.js.map +1 -0
- package/dist/cli/config/paths.d.ts +107 -0
- package/dist/cli/config/paths.d.ts.map +1 -0
- package/dist/cli/config/paths.js +79 -0
- package/dist/cli/config/paths.js.map +1 -0
- package/dist/cli/config/planning-sessions-registry.d.ts +52 -0
- package/dist/cli/config/planning-sessions-registry.d.ts.map +1 -0
- package/dist/cli/config/planning-sessions-registry.js +46 -0
- package/dist/cli/config/planning-sessions-registry.js.map +1 -0
- package/dist/cli/config/types.d.ts +186 -0
- package/dist/cli/config/types.d.ts.map +1 -0
- package/dist/cli/config/types.js +27 -0
- package/dist/cli/config/types.js.map +1 -0
- package/dist/cli/config/validation.d.ts +62 -0
- package/dist/cli/config/validation.d.ts.map +1 -0
- package/dist/cli/config/validation.js +133 -0
- package/dist/cli/config/validation.js.map +1 -0
- package/dist/cli/config/writer.d.ts +153 -0
- package/dist/cli/config/writer.d.ts.map +1 -0
- package/dist/cli/config/writer.js +168 -0
- package/dist/cli/config/writer.js.map +1 -0
- package/dist/cli/index.d.ts +30 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +142 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/middleware/auth-guard.d.ts +107 -0
- package/dist/cli/middleware/auth-guard.d.ts.map +1 -0
- package/dist/cli/middleware/auth-guard.js +97 -0
- package/dist/cli/middleware/auth-guard.js.map +1 -0
- package/dist/cli/middleware/error-handler.d.ts +133 -0
- package/dist/cli/middleware/error-handler.d.ts.map +1 -0
- package/dist/cli/middleware/error-handler.js +139 -0
- package/dist/cli/middleware/error-handler.js.map +1 -0
- package/dist/cli/middleware/index.d.ts +6 -0
- package/dist/cli/middleware/index.d.ts.map +1 -0
- package/dist/cli/middleware/index.js +3 -0
- package/dist/cli/middleware/index.js.map +1 -0
- package/dist/cli/templates/agents/content/core/sfag-orchestrator.d.ts +9 -0
- package/dist/cli/templates/agents/content/core/sfag-orchestrator.d.ts.map +1 -0
- package/dist/cli/templates/agents/content/core/sfag-orchestrator.js +160 -0
- package/dist/cli/templates/agents/content/core/sfag-orchestrator.js.map +1 -0
- package/dist/cli/templates/agents/content/core/sfag-spec-creator.d.ts +9 -0
- package/dist/cli/templates/agents/content/core/sfag-spec-creator.d.ts.map +1 -0
- package/dist/cli/templates/agents/content/core/sfag-spec-creator.js +343 -0
- package/dist/cli/templates/agents/content/core/sfag-spec-creator.js.map +1 -0
- package/dist/cli/templates/agents/content/core/sfag-ticket-implementer.d.ts +9 -0
- package/dist/cli/templates/agents/content/core/sfag-ticket-implementer.d.ts.map +1 -0
- package/dist/cli/templates/agents/content/core/sfag-ticket-implementer.js +254 -0
- package/dist/cli/templates/agents/content/core/sfag-ticket-implementer.js.map +1 -0
- package/dist/cli/templates/agents/content/research/sfag-package-researcher.d.ts +8 -0
- package/dist/cli/templates/agents/content/research/sfag-package-researcher.d.ts.map +1 -0
- package/dist/cli/templates/agents/content/research/sfag-package-researcher.js +128 -0
- package/dist/cli/templates/agents/content/research/sfag-package-researcher.js.map +1 -0
- package/dist/cli/templates/agents/index.d.ts +23 -0
- package/dist/cli/templates/agents/index.d.ts.map +1 -0
- package/dist/cli/templates/agents/index.js +29 -0
- package/dist/cli/templates/agents/index.js.map +1 -0
- package/dist/cli/templates/commands.d.ts +17 -0
- package/dist/cli/templates/commands.d.ts.map +1 -0
- package/dist/cli/templates/commands.js +78 -0
- package/dist/cli/templates/commands.js.map +1 -0
- package/dist/cli/templates/content/sf-blockers.d.ts +7 -0
- package/dist/cli/templates/content/sf-blockers.d.ts.map +1 -0
- package/dist/cli/templates/content/sf-blockers.js +69 -0
- package/dist/cli/templates/content/sf-blockers.js.map +1 -0
- package/dist/cli/templates/content/sf-commit.d.ts +7 -0
- package/dist/cli/templates/content/sf-commit.d.ts.map +1 -0
- package/dist/cli/templates/content/sf-commit.js +79 -0
- package/dist/cli/templates/content/sf-commit.js.map +1 -0
- package/dist/cli/templates/content/sf-context.d.ts +7 -0
- package/dist/cli/templates/content/sf-context.d.ts.map +1 -0
- package/dist/cli/templates/content/sf-context.js +63 -0
- package/dist/cli/templates/content/sf-context.js.map +1 -0
- package/dist/cli/templates/content/sf-help.d.ts +7 -0
- package/dist/cli/templates/content/sf-help.d.ts.map +1 -0
- package/dist/cli/templates/content/sf-help.js +42 -0
- package/dist/cli/templates/content/sf-help.js.map +1 -0
- package/dist/cli/templates/content/sf-init.d.ts +7 -0
- package/dist/cli/templates/content/sf-init.d.ts.map +1 -0
- package/dist/cli/templates/content/sf-init.js +62 -0
- package/dist/cli/templates/content/sf-init.js.map +1 -0
- package/dist/cli/templates/content/sf-reset.d.ts +7 -0
- package/dist/cli/templates/content/sf-reset.d.ts.map +1 -0
- package/dist/cli/templates/content/sf-reset.js +67 -0
- package/dist/cli/templates/content/sf-reset.js.map +1 -0
- package/dist/cli/templates/content/sf-search.d.ts +7 -0
- package/dist/cli/templates/content/sf-search.d.ts.map +1 -0
- package/dist/cli/templates/content/sf-search.js +63 -0
- package/dist/cli/templates/content/sf-search.js.map +1 -0
- package/dist/cli/templates/content/sf-status.d.ts +7 -0
- package/dist/cli/templates/content/sf-status.d.ts.map +1 -0
- package/dist/cli/templates/content/sf-status.js +62 -0
- package/dist/cli/templates/content/sf-status.js.map +1 -0
- package/dist/cli/templates/index.d.ts +8 -0
- package/dist/cli/templates/index.d.ts.map +1 -0
- package/dist/cli/templates/index.js +17 -0
- package/dist/cli/templates/index.js.map +1 -0
- package/dist/cli/ui/banner.d.ts +38 -0
- package/dist/cli/ui/banner.d.ts.map +1 -0
- package/dist/cli/ui/banner.js +89 -0
- package/dist/cli/ui/banner.js.map +1 -0
- package/dist/cli/ui/colors.d.ts +76 -0
- package/dist/cli/ui/colors.d.ts.map +1 -0
- package/dist/cli/ui/colors.js +67 -0
- package/dist/cli/ui/colors.js.map +1 -0
- package/dist/cli/ui/index.d.ts +7 -0
- package/dist/cli/ui/index.d.ts.map +1 -0
- package/dist/cli/ui/index.js +4 -0
- package/dist/cli/ui/index.js.map +1 -0
- package/dist/cli/ui/output.d.ts +117 -0
- package/dist/cli/ui/output.d.ts.map +1 -0
- package/dist/cli/ui/output.js +83 -0
- package/dist/cli/ui/output.js.map +1 -0
- package/dist/cli/ui/spinner.d.ts +120 -0
- package/dist/cli/ui/spinner.d.ts.map +1 -0
- package/dist/cli/ui/spinner.js +158 -0
- package/dist/cli/ui/spinner.js.map +1 -0
- package/dist/cli/ui/time.d.ts +54 -0
- package/dist/cli/ui/time.d.ts.map +1 -0
- package/dist/cli/ui/time.js +119 -0
- package/dist/cli/ui/time.js.map +1 -0
- package/dist/client/api-client.d.ts +123 -0
- package/dist/client/api-client.d.ts.map +1 -0
- package/dist/client/api-client.js +105 -0
- package/dist/client/api-client.js.map +1 -0
- package/dist/client/cli-client.d.ts +26 -0
- package/dist/client/cli-client.d.ts.map +1 -0
- package/dist/client/cli-client.js +20 -0
- package/dist/client/cli-client.js.map +1 -0
- package/dist/config/index.d.ts +78 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +79 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/format.d.ts +126 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +71 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/index.d.ts +11 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +45 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/monorepo-detector.d.ts +31 -0
- package/dist/lib/monorepo-detector.d.ts.map +1 -0
- package/dist/lib/monorepo-detector.js +188 -0
- package/dist/lib/monorepo-detector.js.map +1 -0
- package/dist/lib/prompt-generator.d.ts +65 -0
- package/dist/lib/prompt-generator.d.ts.map +1 -0
- package/dist/lib/prompt-generator.js +162 -0
- package/dist/lib/prompt-generator.js.map +1 -0
- package/dist/lib/strategy-analyzer.d.ts +59 -0
- package/dist/lib/strategy-analyzer.d.ts.map +1 -0
- package/dist/lib/strategy-analyzer.js +91 -0
- package/dist/lib/strategy-analyzer.js.map +1 -0
- package/dist/lib/workflow-definitions.d.ts +41 -0
- package/dist/lib/workflow-definitions.d.ts.map +1 -0
- package/dist/lib/workflow-definitions.js +497 -0
- package/dist/lib/workflow-definitions.js.map +1 -0
- package/dist/patterns/index.d.ts +9 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +21 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/inheritance.d.ts +193 -0
- package/dist/patterns/inheritance.d.ts.map +1 -0
- package/dist/patterns/inheritance.js +125 -0
- package/dist/patterns/inheritance.js.map +1 -0
- package/dist/server.d.ts +44 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +91 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/core/context-helper.d.ts +50 -0
- package/dist/tools/core/context-helper.d.ts.map +1 -0
- package/dist/tools/core/context-helper.js +49 -0
- package/dist/tools/core/context-helper.js.map +1 -0
- package/dist/tools/core/index.d.ts +2 -0
- package/dist/tools/core/index.d.ts.map +1 -0
- package/dist/tools/core/index.js +2 -0
- package/dist/tools/core/index.js.map +1 -0
- package/dist/tools/index.d.ts +58 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +1499 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/transport/__tests__/resolve-endpoint.test.d.ts +6 -0
- package/dist/transport/__tests__/resolve-endpoint.test.d.ts.map +1 -0
- package/dist/transport/index.d.ts +6 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +5 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/interface.d.ts +25 -0
- package/dist/transport/interface.d.ts.map +1 -0
- package/dist/transport/interface.js +1 -0
- package/dist/transport/interface.js.map +1 -0
- package/dist/transport/remote.d.ts +38 -0
- package/dist/transport/remote.d.ts.map +1 -0
- package/dist/transport/remote.js +107 -0
- package/dist/transport/remote.js.map +1 -0
- package/dist/transport/resolve-endpoint.d.ts +58 -0
- package/dist/transport/resolve-endpoint.d.ts.map +1 -0
- package/dist/transport/resolve-endpoint.js +146 -0
- package/dist/transport/resolve-endpoint.js.map +1 -0
- package/dist/types/index.d.ts +212 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +16 -0
- package/dist/types/index.js.map +1 -0
- package/dist/validation/index.d.ts +86 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +392 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/ticket-validation.d.ts +162 -0
- package/dist/validation/ticket-validation.d.ts.map +1 -0
- package/dist/validation/ticket-validation.js +188 -0
- package/dist/validation/ticket-validation.js.map +1 -0
- package/package.json +69 -0
- package/src/cli/templates/agents/content/core/sfag-orchestrator.ts +165 -0
- package/src/cli/templates/agents/content/core/sfag-spec-creator.ts +348 -0
- package/src/cli/templates/agents/content/core/sfag-ticket-implementer.ts +259 -0
- package/src/cli/templates/agents/content/research/sfag-package-researcher.ts +132 -0
- package/src/cli/templates/agents/index.ts +47 -0
- package/src/cli/templates/commands.ts +90 -0
- package/src/cli/templates/content/sf-blockers.ts +71 -0
- package/src/cli/templates/content/sf-commit.ts +81 -0
- package/src/cli/templates/content/sf-context.ts +65 -0
- package/src/cli/templates/content/sf-help.ts +44 -0
- package/src/cli/templates/content/sf-init.ts +64 -0
- package/src/cli/templates/content/sf-reset.ts +69 -0
- package/src/cli/templates/content/sf-search.ts +65 -0
- package/src/cli/templates/content/sf-status.ts +64 -0
- package/src/cli/templates/index.ts +16 -0
- package/src/cli/templates/skills/specforge-conventions.md +114 -0
- package/src/cli/templates/skills/specforge-orchestrator.md +409 -0
- package/src/cli/templates/skills/specforge-validator.md +128 -0
- package/src/cli/templates/skills/specforge-worker.md +412 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import {
|
|
2
|
+
validationRules,
|
|
3
|
+
validateTicket,
|
|
4
|
+
filterWarningsByStrictness,
|
|
5
|
+
getComplexityRules,
|
|
6
|
+
isValidComplexity,
|
|
7
|
+
getComplexityDescription,
|
|
8
|
+
suggestComplexity
|
|
9
|
+
} from "./ticket-validation.js";
|
|
10
|
+
class ValidationError extends Error {
|
|
11
|
+
constructor(message, field, code) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.field = field;
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = "ValidationError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class ApiError extends Error {
|
|
19
|
+
constructor(message, statusCode, errorCode) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.statusCode = statusCode;
|
|
22
|
+
this.errorCode = errorCode;
|
|
23
|
+
this.name = "ApiError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function validateRequired(value, fieldName) {
|
|
27
|
+
if (value === void 0 || value === null || value === "") {
|
|
28
|
+
throw new ValidationError(
|
|
29
|
+
`${fieldName} is required`,
|
|
30
|
+
fieldName,
|
|
31
|
+
"REQUIRED"
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function validateUUID(value, fieldName) {
|
|
36
|
+
validateRequired(value, fieldName);
|
|
37
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
38
|
+
if (!uuidRegex.test(value)) {
|
|
39
|
+
throw new ValidationError(
|
|
40
|
+
`${fieldName} must be a valid UUID`,
|
|
41
|
+
fieldName,
|
|
42
|
+
"INVALID_UUID"
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function validateEnum(value, fieldName, allowedValues) {
|
|
47
|
+
validateRequired(value, fieldName);
|
|
48
|
+
if (!allowedValues.includes(value)) {
|
|
49
|
+
throw new ValidationError(
|
|
50
|
+
`${fieldName} must be one of: ${allowedValues.join(", ")}`,
|
|
51
|
+
fieldName,
|
|
52
|
+
"INVALID_ENUM"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function validateRange(value, fieldName, min, max) {
|
|
57
|
+
if (typeof value !== "number" || isNaN(value)) {
|
|
58
|
+
throw new ValidationError(
|
|
59
|
+
`${fieldName} must be a number`,
|
|
60
|
+
fieldName,
|
|
61
|
+
"INVALID_NUMBER"
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (value < min || value > max) {
|
|
65
|
+
throw new ValidationError(
|
|
66
|
+
`${fieldName} must be between ${min} and ${max}`,
|
|
67
|
+
fieldName,
|
|
68
|
+
"OUT_OF_RANGE"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function validateArray(value, fieldName, itemValidator) {
|
|
73
|
+
if (!Array.isArray(value)) {
|
|
74
|
+
throw new ValidationError(
|
|
75
|
+
`${fieldName} must be an array`,
|
|
76
|
+
fieldName,
|
|
77
|
+
"INVALID_ARRAY"
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (itemValidator) {
|
|
81
|
+
value.forEach((item, index) => {
|
|
82
|
+
try {
|
|
83
|
+
itemValidator(item, index);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
throw new ValidationError(
|
|
87
|
+
`${fieldName}[${index}]: ${message}`,
|
|
88
|
+
`${fieldName}[${index}]`,
|
|
89
|
+
"INVALID_ITEM"
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function validateSha(value, fieldName = "sha") {
|
|
96
|
+
validateRequired(value, fieldName);
|
|
97
|
+
if (!/^[a-f0-9]{40}$/i.test(value)) {
|
|
98
|
+
throw new ValidationError(
|
|
99
|
+
`${fieldName} must be a 40-character hex string`,
|
|
100
|
+
fieldName,
|
|
101
|
+
"INVALID_SHA"
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function validateDate(value, fieldName) {
|
|
106
|
+
validateRequired(value, fieldName);
|
|
107
|
+
const date = new Date(value);
|
|
108
|
+
if (isNaN(date.getTime())) {
|
|
109
|
+
throw new ValidationError(
|
|
110
|
+
`${fieldName} must be a valid ISO 8601 date`,
|
|
111
|
+
fieldName,
|
|
112
|
+
"INVALID_DATE"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function validateStringLength(value, fieldName, minLength, maxLength) {
|
|
117
|
+
if (typeof value !== "string") {
|
|
118
|
+
throw new ValidationError(
|
|
119
|
+
`${fieldName} must be a string`,
|
|
120
|
+
fieldName,
|
|
121
|
+
"INVALID_STRING"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (value.length < minLength || value.length > maxLength) {
|
|
125
|
+
throw new ValidationError(
|
|
126
|
+
`${fieldName} must be between ${minLength} and ${maxLength} characters`,
|
|
127
|
+
fieldName,
|
|
128
|
+
"INVALID_LENGTH"
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const toolValidators = {
|
|
133
|
+
// Queries
|
|
134
|
+
get: (args) => {
|
|
135
|
+
validateRequired(args.type, "type");
|
|
136
|
+
validateEnum(
|
|
137
|
+
args.type,
|
|
138
|
+
"type",
|
|
139
|
+
["project", "specification", "epic", "ticket", "implementation_session"]
|
|
140
|
+
);
|
|
141
|
+
},
|
|
142
|
+
list: (args) => {
|
|
143
|
+
validateRequired(args.type, "type");
|
|
144
|
+
validateEnum(
|
|
145
|
+
args.type,
|
|
146
|
+
"type",
|
|
147
|
+
["projects", "specifications", "epics", "tickets"]
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
search: (args) => {
|
|
151
|
+
if (!args.query && !args.files && !args.tags && !args.relatedTo) {
|
|
152
|
+
throw new ValidationError(
|
|
153
|
+
"At least one filter is required: query, files, tags, or relatedTo",
|
|
154
|
+
"query",
|
|
155
|
+
"REQUIRED_ONE_OF"
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (args.query) {
|
|
159
|
+
validateStringLength(args.query, "query", 1, 500);
|
|
160
|
+
}
|
|
161
|
+
if (args.limit !== void 0) {
|
|
162
|
+
validateRange(args.limit, "limit", 1, 100);
|
|
163
|
+
}
|
|
164
|
+
if (args.offset !== void 0) {
|
|
165
|
+
validateRange(args.offset, "offset", 0, 1e4);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
get_next_actionable_tickets: (args) => {
|
|
169
|
+
if (!args.specificationId && !args.projectId) {
|
|
170
|
+
throw new ValidationError(
|
|
171
|
+
"Either specificationId or projectId is required",
|
|
172
|
+
"specificationId/projectId",
|
|
173
|
+
"REQUIRED_ONE_OF"
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
if (args.limit !== void 0) {
|
|
177
|
+
validateRange(args.limit, "limit", 1, 100);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
get_blocked_tickets: (args) => {
|
|
181
|
+
validateRequired(args.specificationId, "specificationId");
|
|
182
|
+
},
|
|
183
|
+
get_report: (args) => {
|
|
184
|
+
validateRequired(args.type, "type");
|
|
185
|
+
validateRequired(args.scope, "scope");
|
|
186
|
+
validateRequired(args.scopeId, "scopeId");
|
|
187
|
+
},
|
|
188
|
+
// Lifecycle. Planning verbs inject identity from local config (M9): SPS takes
|
|
189
|
+
// an optional specificationId (falls back to config), APS takes only
|
|
190
|
+
// `operation` (sessionId injected), CPS takes nothing (sessionId injected).
|
|
191
|
+
// So no specificationId/sessionId requirements here — the handlers resolve
|
|
192
|
+
// and validate them.
|
|
193
|
+
start_planning_session: () => {
|
|
194
|
+
},
|
|
195
|
+
action_planning_session: (args) => {
|
|
196
|
+
validateRequired(args.operation, "operation");
|
|
197
|
+
},
|
|
198
|
+
complete_planning_session: () => {
|
|
199
|
+
},
|
|
200
|
+
start_work_session: (args) => {
|
|
201
|
+
validateRequired(args.ticketId, "ticketId");
|
|
202
|
+
},
|
|
203
|
+
action_work_session: (args) => {
|
|
204
|
+
validateRequired(args.ticketId, "ticketId");
|
|
205
|
+
if (args.filesCreated !== void 0) {
|
|
206
|
+
validateArray(args.filesCreated, "filesCreated");
|
|
207
|
+
}
|
|
208
|
+
if (args.filesModified !== void 0) {
|
|
209
|
+
validateArray(args.filesModified, "filesModified");
|
|
210
|
+
}
|
|
211
|
+
if (args.filesDeleted !== void 0) {
|
|
212
|
+
validateArray(args.filesDeleted, "filesDeleted");
|
|
213
|
+
}
|
|
214
|
+
if (args.blockReason !== void 0) {
|
|
215
|
+
validateStringLength(args.blockReason, "blockReason", 1, 5e3);
|
|
216
|
+
}
|
|
217
|
+
if (args.clearBlockReason !== void 0 && typeof args.clearBlockReason !== "boolean") {
|
|
218
|
+
throw new Error("clearBlockReason must be a boolean");
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
complete_work_session: (args) => {
|
|
222
|
+
validateRequired(args.ticketId, "ticketId");
|
|
223
|
+
validateRequired(args.summary, "summary");
|
|
224
|
+
if (args.summary) {
|
|
225
|
+
validateStringLength(args.summary, "summary", 1, 1e4);
|
|
226
|
+
}
|
|
227
|
+
if (args.filesModified !== void 0) {
|
|
228
|
+
validateArray(args.filesModified, "filesModified");
|
|
229
|
+
}
|
|
230
|
+
if (args.filesCreated !== void 0) {
|
|
231
|
+
validateArray(args.filesCreated, "filesCreated");
|
|
232
|
+
}
|
|
233
|
+
if (args.filesDeleted !== void 0) {
|
|
234
|
+
validateArray(args.filesDeleted, "filesDeleted");
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
start_review_session: (args) => {
|
|
238
|
+
validateRequired(args.specificationId, "specificationId");
|
|
239
|
+
},
|
|
240
|
+
action_review_session: (args) => {
|
|
241
|
+
validateRequired(args.specificationId, "specificationId");
|
|
242
|
+
},
|
|
243
|
+
complete_review_session: (args) => {
|
|
244
|
+
validateRequired(args.specificationId, "specificationId");
|
|
245
|
+
},
|
|
246
|
+
reopen_specification: (args) => {
|
|
247
|
+
validateRequired(args.specificationId, "specificationId");
|
|
248
|
+
},
|
|
249
|
+
// Mutation
|
|
250
|
+
create_specification: (args) => {
|
|
251
|
+
validateRequired(args.projectId, "projectId");
|
|
252
|
+
validateRequired(args.title, "title");
|
|
253
|
+
},
|
|
254
|
+
// Utilities
|
|
255
|
+
feedback: () => {
|
|
256
|
+
},
|
|
257
|
+
reset_work_session: (args) => {
|
|
258
|
+
validateRequired(args.specificationId, "specificationId");
|
|
259
|
+
},
|
|
260
|
+
link_pull_request: (args) => {
|
|
261
|
+
validateRequired(args.ticketId, "ticketId");
|
|
262
|
+
if (!args.prNumber && !args.prUrl) {
|
|
263
|
+
throw new ValidationError(
|
|
264
|
+
"Either prNumber or prUrl must be provided",
|
|
265
|
+
"prNumber",
|
|
266
|
+
"REQUIRED_ONE_OF"
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
if (args.prNumber !== void 0 && typeof args.prNumber !== "number") {
|
|
270
|
+
throw new ValidationError(
|
|
271
|
+
"prNumber must be a number",
|
|
272
|
+
"prNumber",
|
|
273
|
+
"INVALID_NUMBER"
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
if (args.prUrl !== void 0) {
|
|
277
|
+
const url = args.prUrl;
|
|
278
|
+
if (!url.includes("github.com") && !url.includes("gitlab.com") && !url.includes("bitbucket.org")) {
|
|
279
|
+
throw new ValidationError(
|
|
280
|
+
"prUrl must be a valid GitHub, GitLab, or Bitbucket URL",
|
|
281
|
+
"prUrl",
|
|
282
|
+
"INVALID_URL"
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
// Orchestration
|
|
288
|
+
get_critical_path: (args) => {
|
|
289
|
+
validateRequired(args.specificationId, "specificationId");
|
|
290
|
+
},
|
|
291
|
+
get_dependency_tree: (args) => {
|
|
292
|
+
validateRequired(args.specificationId, "specificationId");
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
function formatMCPError(error) {
|
|
296
|
+
let message = error.message;
|
|
297
|
+
if (error instanceof ValidationError) {
|
|
298
|
+
message = `Validation error for '${error.field}': ${error.message} (${error.code})`;
|
|
299
|
+
} else if (error instanceof ApiError) {
|
|
300
|
+
message = formatApiErrorMessage(error);
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
content: [
|
|
304
|
+
{
|
|
305
|
+
type: "text",
|
|
306
|
+
text: message
|
|
307
|
+
}
|
|
308
|
+
],
|
|
309
|
+
isError: true
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function formatApiErrorMessage(error) {
|
|
313
|
+
switch (error.statusCode) {
|
|
314
|
+
case 400:
|
|
315
|
+
return `Bad request: ${error.message}`;
|
|
316
|
+
case 401:
|
|
317
|
+
return "Authentication failed. Check your API key.";
|
|
318
|
+
case 403:
|
|
319
|
+
return "Permission denied. Your API key may lack required permissions.";
|
|
320
|
+
case 404:
|
|
321
|
+
return `Resource not found: ${error.message}`;
|
|
322
|
+
case 429:
|
|
323
|
+
return "Rate limit exceeded. Please wait before retrying.";
|
|
324
|
+
case 500:
|
|
325
|
+
case 502:
|
|
326
|
+
case 503:
|
|
327
|
+
case 504:
|
|
328
|
+
return "Server error. Please try again later.";
|
|
329
|
+
default:
|
|
330
|
+
return error.message;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function transformError(error) {
|
|
334
|
+
if (error instanceof ValidationError || error instanceof ApiError) {
|
|
335
|
+
return error;
|
|
336
|
+
}
|
|
337
|
+
if (error instanceof Error) {
|
|
338
|
+
if (error.message.includes("ECONNREFUSED")) {
|
|
339
|
+
return new ApiError(
|
|
340
|
+
"Unable to connect to SpecForge API. Check your network connection.",
|
|
341
|
+
0,
|
|
342
|
+
"NETWORK_ERROR"
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
if (error.message.includes("ETIMEDOUT")) {
|
|
346
|
+
return new ApiError(
|
|
347
|
+
"Request timed out. Please try again.",
|
|
348
|
+
0,
|
|
349
|
+
"TIMEOUT"
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
if (error.message.includes("ECONNRESET")) {
|
|
353
|
+
return new ApiError(
|
|
354
|
+
"Connection was reset. Please try again.",
|
|
355
|
+
0,
|
|
356
|
+
"CONNECTION_RESET"
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
return error;
|
|
360
|
+
}
|
|
361
|
+
return new Error(String(error));
|
|
362
|
+
}
|
|
363
|
+
function validateToolArgs(toolName, args) {
|
|
364
|
+
const validator = toolValidators[toolName];
|
|
365
|
+
if (validator) {
|
|
366
|
+
validator(args);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
export {
|
|
370
|
+
ApiError,
|
|
371
|
+
ValidationError,
|
|
372
|
+
filterWarningsByStrictness,
|
|
373
|
+
formatMCPError,
|
|
374
|
+
getComplexityDescription,
|
|
375
|
+
getComplexityRules,
|
|
376
|
+
isValidComplexity,
|
|
377
|
+
suggestComplexity,
|
|
378
|
+
toolValidators,
|
|
379
|
+
transformError,
|
|
380
|
+
validateArray,
|
|
381
|
+
validateDate,
|
|
382
|
+
validateEnum,
|
|
383
|
+
validateRange,
|
|
384
|
+
validateRequired,
|
|
385
|
+
validateSha,
|
|
386
|
+
validateStringLength,
|
|
387
|
+
validateTicket,
|
|
388
|
+
validateToolArgs,
|
|
389
|
+
validateUUID,
|
|
390
|
+
validationRules
|
|
391
|
+
};
|
|
392
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/validation/index.ts"],"sourcesContent":["/**\n * Validation Utilities for MCP Tools\n *\n * Provides comprehensive input validation and error handling for all MCP tools.\n * Validation is run before each tool call to ensure proper argument types and formats.\n */\n\n// Re-export ticket validation utilities\nexport {\n type TicketComplexity,\n type WarningSeverity,\n type StrictnessLevel,\n type ValidationWarning,\n type TicketValidationResult,\n type ComplexityRules,\n type TicketValidationInput,\n validationRules,\n validateTicket,\n filterWarningsByStrictness,\n getComplexityRules,\n isValidComplexity,\n getComplexityDescription,\n suggestComplexity,\n} from './ticket-validation.js';\n\n/**\n * Custom error class for validation failures\n * Includes field name and error code for structured error handling\n */\nexport class ValidationError extends Error {\n constructor(\n message: string,\n public field: string,\n public code: string\n ) {\n super(message);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * API Error class for handling HTTP errors from the backend\n */\nexport class ApiError extends Error {\n constructor(\n message: string,\n public statusCode: number,\n public errorCode?: string\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\n// ============================================================================\n// Basic Validators\n// ============================================================================\n\n/**\n * Validate that a value is present (not null, undefined, or empty string)\n */\nexport function validateRequired(value: unknown, fieldName: string): void {\n if (value === undefined || value === null || value === '') {\n throw new ValidationError(\n `${fieldName} is required`,\n fieldName,\n 'REQUIRED'\n );\n }\n}\n\n/**\n * Validate that a value is a valid UUID format\n */\nexport function validateUUID(value: string, fieldName: string): void {\n validateRequired(value, fieldName);\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(value)) {\n throw new ValidationError(\n `${fieldName} must be a valid UUID`,\n fieldName,\n 'INVALID_UUID'\n );\n }\n}\n\n/**\n * Validate that a value is one of the allowed enum values\n */\nexport function validateEnum(\n value: string,\n fieldName: string,\n allowedValues: string[]\n): void {\n validateRequired(value, fieldName);\n if (!allowedValues.includes(value)) {\n throw new ValidationError(\n `${fieldName} must be one of: ${allowedValues.join(', ')}`,\n fieldName,\n 'INVALID_ENUM'\n );\n }\n}\n\n/**\n * Validate that a number is within a range\n */\nexport function validateRange(\n value: number,\n fieldName: string,\n min: number,\n max: number\n): void {\n if (typeof value !== 'number' || isNaN(value)) {\n throw new ValidationError(\n `${fieldName} must be a number`,\n fieldName,\n 'INVALID_NUMBER'\n );\n }\n if (value < min || value > max) {\n throw new ValidationError(\n `${fieldName} must be between ${min} and ${max}`,\n fieldName,\n 'OUT_OF_RANGE'\n );\n }\n}\n\n/**\n * Validate that a value is an array, with optional item validation\n */\nexport function validateArray(\n value: unknown,\n fieldName: string,\n itemValidator?: (item: unknown, index: number) => void\n): void {\n if (!Array.isArray(value)) {\n throw new ValidationError(\n `${fieldName} must be an array`,\n fieldName,\n 'INVALID_ARRAY'\n );\n }\n if (itemValidator) {\n value.forEach((item, index) => {\n try {\n itemValidator(item, index);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n throw new ValidationError(\n `${fieldName}[${index}]: ${message}`,\n `${fieldName}[${index}]`,\n 'INVALID_ITEM'\n );\n }\n });\n }\n}\n\n/**\n * Validate a 40-character hex SHA\n */\nexport function validateSha(value: string, fieldName: string = 'sha'): void {\n validateRequired(value, fieldName);\n if (!/^[a-f0-9]{40}$/i.test(value)) {\n throw new ValidationError(\n `${fieldName} must be a 40-character hex string`,\n fieldName,\n 'INVALID_SHA'\n );\n }\n}\n\n/**\n * Validate an ISO 8601 date string\n */\nexport function validateDate(value: string, fieldName: string): void {\n validateRequired(value, fieldName);\n const date = new Date(value);\n if (isNaN(date.getTime())) {\n throw new ValidationError(\n `${fieldName} must be a valid ISO 8601 date`,\n fieldName,\n 'INVALID_DATE'\n );\n }\n}\n\n/**\n * Validate a string length\n */\nexport function validateStringLength(\n value: string,\n fieldName: string,\n minLength: number,\n maxLength: number\n): void {\n if (typeof value !== 'string') {\n throw new ValidationError(\n `${fieldName} must be a string`,\n fieldName,\n 'INVALID_STRING'\n );\n }\n if (value.length < minLength || value.length > maxLength) {\n throw new ValidationError(\n `${fieldName} must be between ${minLength} and ${maxLength} characters`,\n fieldName,\n 'INVALID_LENGTH'\n );\n }\n}\n\n// ============================================================================\n// Tool-Specific Validators\n// ============================================================================\n\ntype ValidatorFn = (args: Record<string, unknown>) => void;\n\n/**\n * Validators for each tool (22 tools)\n * These are applied before the tool handler is called\n */\nexport const toolValidators: Record<string, ValidatorFn> = {\n // Queries\n get: (args) => {\n validateRequired(args.type, 'type');\n validateEnum(\n args.type as string,\n 'type',\n ['project', 'specification', 'epic', 'ticket', 'implementation_session']\n );\n },\n\n list: (args) => {\n validateRequired(args.type, 'type');\n validateEnum(\n args.type as string,\n 'type',\n ['projects', 'specifications', 'epics', 'tickets']\n );\n },\n\n search: (args) => {\n if (!args.query && !args.files && !args.tags && !args.relatedTo) {\n throw new ValidationError(\n 'At least one filter is required: query, files, tags, or relatedTo',\n 'query',\n 'REQUIRED_ONE_OF'\n );\n }\n if (args.query) {\n validateStringLength(args.query as string, 'query', 1, 500);\n }\n if (args.limit !== undefined) {\n validateRange(args.limit as number, 'limit', 1, 100);\n }\n if (args.offset !== undefined) {\n validateRange(args.offset as number, 'offset', 0, 10000);\n }\n },\n\n get_next_actionable_tickets: (args) => {\n // Either specificationId or projectId is required\n if (!args.specificationId && !args.projectId) {\n throw new ValidationError(\n 'Either specificationId or projectId is required',\n 'specificationId/projectId',\n 'REQUIRED_ONE_OF'\n );\n }\n if (args.limit !== undefined) {\n validateRange(args.limit as number, 'limit', 1, 100);\n }\n },\n\n get_blocked_tickets: (args) => {\n validateRequired(args.specificationId, 'specificationId');\n },\n\n get_report: (args) => {\n validateRequired(args.type, 'type');\n validateRequired(args.scope, 'scope');\n validateRequired(args.scopeId, 'scopeId');\n },\n\n // Lifecycle. Planning verbs inject identity from local config (M9): SPS takes\n // an optional specificationId (falls back to config), APS takes only\n // `operation` (sessionId injected), CPS takes nothing (sessionId injected).\n // So no specificationId/sessionId requirements here — the handlers resolve\n // and validate them.\n start_planning_session: () => {\n /* specificationId optional — handler falls back to config */\n },\n\n action_planning_session: (args) => {\n validateRequired(args.operation, 'operation');\n },\n\n complete_planning_session: () => {\n /* no args — sessionId injected from config */\n },\n\n start_work_session: (args) => {\n validateRequired(args.ticketId, 'ticketId');\n },\n\n action_work_session: (args) => {\n validateRequired(args.ticketId, 'ticketId');\n if (args.filesCreated !== undefined) {\n validateArray(args.filesCreated, 'filesCreated');\n }\n if (args.filesModified !== undefined) {\n validateArray(args.filesModified, 'filesModified');\n }\n if (args.filesDeleted !== undefined) {\n validateArray(args.filesDeleted, 'filesDeleted');\n }\n if (args.blockReason !== undefined) {\n validateStringLength(args.blockReason as string, 'blockReason', 1, 5000);\n }\n if (args.clearBlockReason !== undefined && typeof args.clearBlockReason !== 'boolean') {\n throw new Error('clearBlockReason must be a boolean');\n }\n },\n\n complete_work_session: (args) => {\n validateRequired(args.ticketId, 'ticketId');\n validateRequired(args.summary, 'summary');\n if (args.summary) {\n validateStringLength(args.summary as string, 'summary', 1, 10000);\n }\n if (args.filesModified !== undefined) {\n validateArray(args.filesModified, 'filesModified');\n }\n if (args.filesCreated !== undefined) {\n validateArray(args.filesCreated, 'filesCreated');\n }\n if (args.filesDeleted !== undefined) {\n validateArray(args.filesDeleted, 'filesDeleted');\n }\n },\n\n start_review_session: (args) => {\n validateRequired(args.specificationId, 'specificationId');\n },\n\n action_review_session: (args) => {\n validateRequired(args.specificationId, 'specificationId');\n },\n\n complete_review_session: (args) => {\n validateRequired(args.specificationId, 'specificationId');\n },\n\n reopen_specification: (args) => {\n validateRequired(args.specificationId, 'specificationId');\n },\n\n // Mutation\n create_specification: (args) => {\n validateRequired(args.projectId, 'projectId');\n validateRequired(args.title, 'title');\n },\n\n // Utilities\n feedback: () => {\n // Validated by feedback handler\n },\n\n reset_work_session: (args) => {\n validateRequired(args.specificationId, 'specificationId');\n },\n\n link_pull_request: (args) => {\n validateRequired(args.ticketId, 'ticketId');\n if (!args.prNumber && !args.prUrl) {\n throw new ValidationError(\n 'Either prNumber or prUrl must be provided',\n 'prNumber',\n 'REQUIRED_ONE_OF'\n );\n }\n if (args.prNumber !== undefined && typeof args.prNumber !== 'number') {\n throw new ValidationError(\n 'prNumber must be a number',\n 'prNumber',\n 'INVALID_NUMBER'\n );\n }\n if (args.prUrl !== undefined) {\n const url = args.prUrl as string;\n if (!url.includes('github.com') && !url.includes('gitlab.com') && !url.includes('bitbucket.org')) {\n throw new ValidationError(\n 'prUrl must be a valid GitHub, GitLab, or Bitbucket URL',\n 'prUrl',\n 'INVALID_URL'\n );\n }\n }\n },\n\n // Orchestration\n get_critical_path: (args) => {\n validateRequired(args.specificationId, 'specificationId');\n },\n\n get_dependency_tree: (args) => {\n validateRequired(args.specificationId, 'specificationId');\n },\n};\n\n// ============================================================================\n// Error Transformation\n// ============================================================================\n\n/**\n * MCP error response format\n */\nexport interface MCPErrorResponse {\n content: Array<{\n type: 'text';\n text: string;\n }>;\n isError: true;\n}\n\n/**\n * Transform an error into MCP error response format\n */\nexport function formatMCPError(error: Error): MCPErrorResponse {\n let message = error.message;\n\n if (error instanceof ValidationError) {\n message = `Validation error for '${error.field}': ${error.message} (${error.code})`;\n } else if (error instanceof ApiError) {\n message = formatApiErrorMessage(error);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: message,\n },\n ],\n isError: true,\n };\n}\n\n/**\n * Format API error message based on status code\n */\nfunction formatApiErrorMessage(error: ApiError): string {\n switch (error.statusCode) {\n case 400:\n return `Bad request: ${error.message}`;\n case 401:\n return 'Authentication failed. Check your API key.';\n case 403:\n return 'Permission denied. Your API key may lack required permissions.';\n case 404:\n return `Resource not found: ${error.message}`;\n case 429:\n return 'Rate limit exceeded. Please wait before retrying.';\n case 500:\n case 502:\n case 503:\n case 504:\n return 'Server error. Please try again later.';\n default:\n return error.message;\n }\n}\n\n/**\n * Transform HTTP/network errors into ApiError\n */\nexport function transformError(error: unknown): Error {\n if (error instanceof ValidationError || error instanceof ApiError) {\n return error;\n }\n\n if (error instanceof Error) {\n // Network errors\n if (error.message.includes('ECONNREFUSED')) {\n return new ApiError(\n 'Unable to connect to SpecForge API. Check your network connection.',\n 0,\n 'NETWORK_ERROR'\n );\n }\n if (error.message.includes('ETIMEDOUT')) {\n return new ApiError(\n 'Request timed out. Please try again.',\n 0,\n 'TIMEOUT'\n );\n }\n if (error.message.includes('ECONNRESET')) {\n return new ApiError(\n 'Connection was reset. Please try again.',\n 0,\n 'CONNECTION_RESET'\n );\n }\n\n return error;\n }\n\n return new Error(String(error));\n}\n\n/**\n * Run validation for a specific tool\n * @throws ValidationError if validation fails\n */\nexport function validateToolArgs(\n toolName: string,\n args: Record<string, unknown>\n): void {\n const validator = toolValidators[toolName];\n if (validator) {\n validator(args);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ticket Complexity Validation
|
|
3
|
+
*
|
|
4
|
+
* Provides complexity-based validation rules that return warnings rather than
|
|
5
|
+
* errors. This encourages completeness without blocking ticket creation.
|
|
6
|
+
*
|
|
7
|
+
* @see MCI-023-04-complexity-validation-rules
|
|
8
|
+
*/
|
|
9
|
+
import type { TicketImplementation, TicketTechnicalDetails } from '../types/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Ticket complexity levels
|
|
12
|
+
*/
|
|
13
|
+
export type TicketComplexity = 'small' | 'medium' | 'large' | 'xlarge';
|
|
14
|
+
/**
|
|
15
|
+
* Validation warning severity levels
|
|
16
|
+
*/
|
|
17
|
+
export type WarningSeverity = 'info' | 'warning' | 'recommendation';
|
|
18
|
+
/**
|
|
19
|
+
* Validation strictness levels
|
|
20
|
+
*/
|
|
21
|
+
export type StrictnessLevel = 'lenient' | 'normal' | 'strict';
|
|
22
|
+
/**
|
|
23
|
+
* Single validation warning
|
|
24
|
+
*/
|
|
25
|
+
export interface ValidationWarning {
|
|
26
|
+
/** Warning code for programmatic handling */
|
|
27
|
+
code: string;
|
|
28
|
+
/** Human-readable message */
|
|
29
|
+
message: string;
|
|
30
|
+
/** Field that triggered the warning */
|
|
31
|
+
field?: string;
|
|
32
|
+
/** Severity level */
|
|
33
|
+
severity: WarningSeverity;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Complete validation result
|
|
37
|
+
*/
|
|
38
|
+
export interface TicketValidationResult {
|
|
39
|
+
/** Always true - warnings don't block creation */
|
|
40
|
+
valid: true;
|
|
41
|
+
/** List of warnings */
|
|
42
|
+
warnings: ValidationWarning[];
|
|
43
|
+
/** Completeness score (0-100) */
|
|
44
|
+
completenessScore: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validation rules for a complexity level
|
|
48
|
+
*/
|
|
49
|
+
export interface ComplexityRules {
|
|
50
|
+
/** Whether implementation steps are recommended */
|
|
51
|
+
requiresImplementation: boolean;
|
|
52
|
+
/** Whether technical details are recommended */
|
|
53
|
+
requiresTechnicalDetails: boolean;
|
|
54
|
+
/** Whether notes are recommended */
|
|
55
|
+
requiresNotes: boolean;
|
|
56
|
+
/** Minimum recommended implementation steps */
|
|
57
|
+
minSteps: number;
|
|
58
|
+
/** Minimum recommended acceptance criteria */
|
|
59
|
+
minAcceptanceCriteria: number;
|
|
60
|
+
/** Whether file information is recommended */
|
|
61
|
+
requiresFileInfo: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Validation rules by complexity
|
|
65
|
+
*
|
|
66
|
+
* These rules define what fields are recommended for each complexity level.
|
|
67
|
+
* Small tickets have minimal requirements, while xlarge tickets need
|
|
68
|
+
* comprehensive documentation.
|
|
69
|
+
*/
|
|
70
|
+
export declare const validationRules: Record<TicketComplexity, ComplexityRules>;
|
|
71
|
+
/**
|
|
72
|
+
* Ticket data for validation
|
|
73
|
+
*/
|
|
74
|
+
export interface TicketValidationInput {
|
|
75
|
+
complexity?: TicketComplexity;
|
|
76
|
+
implementation?: TicketImplementation | null;
|
|
77
|
+
technicalDetails?: TicketTechnicalDetails | null;
|
|
78
|
+
notes?: string | null;
|
|
79
|
+
acceptanceCriteria?: string[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Validate a ticket against complexity-based rules
|
|
83
|
+
*
|
|
84
|
+
* Returns warnings for missing or incomplete fields based on the ticket's
|
|
85
|
+
* complexity level. Does not block ticket creation - all validations
|
|
86
|
+
* produce warnings, not errors.
|
|
87
|
+
*
|
|
88
|
+
* @param ticket - The ticket data to validate
|
|
89
|
+
* @returns Validation result with warnings and completeness score
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* const result = validateTicket({
|
|
94
|
+
* complexity: 'large',
|
|
95
|
+
* implementation: { steps: [{ order: 1, title: 'Setup', action: 'create', detail: '...' }] },
|
|
96
|
+
* acceptanceCriteria: ['Works correctly']
|
|
97
|
+
* });
|
|
98
|
+
*
|
|
99
|
+
* if (result.warnings.length > 0) {
|
|
100
|
+
* console.log('Suggestions:', result.warnings.map(w => w.message));
|
|
101
|
+
* }
|
|
102
|
+
* console.log('Completeness:', result.completenessScore + '%');
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare function validateTicket(ticket: TicketValidationInput): TicketValidationResult;
|
|
106
|
+
/**
|
|
107
|
+
* Filter warnings by strictness level
|
|
108
|
+
*
|
|
109
|
+
* Allows callers to filter out lower-priority warnings based on their
|
|
110
|
+
* desired strictness level.
|
|
111
|
+
*
|
|
112
|
+
* - lenient: Only show 'warning' severity (most important issues only)
|
|
113
|
+
* - normal: Show 'warning' and 'recommendation' (default)
|
|
114
|
+
* - strict: Show all warnings including 'info' level
|
|
115
|
+
*
|
|
116
|
+
* @param warnings - Array of validation warnings
|
|
117
|
+
* @param strictness - Desired strictness level
|
|
118
|
+
* @returns Filtered array of warnings
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const result = validateTicket(ticket);
|
|
123
|
+
* const warnings = filterWarningsByStrictness(result.warnings, 'lenient');
|
|
124
|
+
* // Only includes warnings with severity 'warning'
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare function filterWarningsByStrictness(warnings: ValidationWarning[], strictness: StrictnessLevel): ValidationWarning[];
|
|
128
|
+
/**
|
|
129
|
+
* Get the rules for a specific complexity level
|
|
130
|
+
*
|
|
131
|
+
* Useful for displaying requirements to users or for custom validation.
|
|
132
|
+
*
|
|
133
|
+
* @param complexity - The complexity level
|
|
134
|
+
* @returns Rules for that complexity level
|
|
135
|
+
*/
|
|
136
|
+
export declare function getComplexityRules(complexity: TicketComplexity): ComplexityRules;
|
|
137
|
+
/**
|
|
138
|
+
* Check if a complexity value is valid
|
|
139
|
+
*
|
|
140
|
+
* @param value - The value to check
|
|
141
|
+
* @returns True if value is a valid TicketComplexity
|
|
142
|
+
*/
|
|
143
|
+
export declare function isValidComplexity(value: unknown): value is TicketComplexity;
|
|
144
|
+
/**
|
|
145
|
+
* Get human-readable description of complexity level
|
|
146
|
+
*
|
|
147
|
+
* @param complexity - The complexity level
|
|
148
|
+
* @returns Description of what this complexity level means
|
|
149
|
+
*/
|
|
150
|
+
export declare function getComplexityDescription(complexity: TicketComplexity): string;
|
|
151
|
+
/**
|
|
152
|
+
* Suggest appropriate complexity based on ticket content
|
|
153
|
+
*
|
|
154
|
+
* Analyzes the provided ticket data and suggests an appropriate
|
|
155
|
+
* complexity level. Useful for auto-suggesting complexity during
|
|
156
|
+
* ticket creation.
|
|
157
|
+
*
|
|
158
|
+
* @param ticket - The ticket data
|
|
159
|
+
* @returns Suggested complexity level
|
|
160
|
+
*/
|
|
161
|
+
export declare function suggestComplexity(ticket: TicketValidationInput): TicketComplexity;
|
|
162
|
+
//# sourceMappingURL=ticket-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ticket-validation.d.ts","sourceRoot":"","sources":["../../src/validation/ticket-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAM3B;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvE;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,kDAAkD;IAClD,KAAK,EAAE,IAAI,CAAC;IACZ,uBAAuB;IACvB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,iCAAiC;IACjC,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,sBAAsB,EAAE,OAAO,CAAC;IAChC,gDAAgD;IAChD,wBAAwB,EAAE,OAAO,CAAC;IAClC,oCAAoC;IACpC,aAAa,EAAE,OAAO,CAAC;IACvB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8CAA8C;IAC9C,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,gBAAgB,EAAE,eAAe,CAiCrE,CAAC;AAMF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC7C,gBAAgB,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,qBAAqB,GAAG,sBAAsB,CA8EpF;AAyDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,UAAU,EAAE,eAAe,GAC1B,iBAAiB,EAAE,CASrB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,gBAAgB,GAAG,eAAe,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE3E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAQ7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,CAuCjF"}
|