@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,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Inheritance Resolver
|
|
3
|
+
*
|
|
4
|
+
* Resolves the complete pattern inheritance chain for tickets:
|
|
5
|
+
* Specification → Epic → Ticket
|
|
6
|
+
*
|
|
7
|
+
* Later values override earlier ones. Arrays are concatenated.
|
|
8
|
+
*
|
|
9
|
+
* @see MCI-030-05-pattern-inheritance-resolver
|
|
10
|
+
*/
|
|
11
|
+
import type { SpecificationCodeStandards, ReturnTypes, NamingConventions, EpicSharedPatterns, EpicCommonFiles } from '../types/index.js';
|
|
12
|
+
/**
|
|
13
|
+
* Fully resolved patterns for a ticket
|
|
14
|
+
*
|
|
15
|
+
* Combines patterns from specification, epic, and ticket levels
|
|
16
|
+
* with proper override semantics.
|
|
17
|
+
*/
|
|
18
|
+
export interface ResolvedPatterns {
|
|
19
|
+
/** All imports (spec.commonImports + epic.additionalImports) */
|
|
20
|
+
imports: string[];
|
|
21
|
+
/** Merged patterns (spec → epic → ticket, later wins) */
|
|
22
|
+
patterns: Record<string, string>;
|
|
23
|
+
/** Return types from specification */
|
|
24
|
+
returnTypes: ReturnTypes;
|
|
25
|
+
/** Common files from epic */
|
|
26
|
+
commonFiles: EpicCommonFiles;
|
|
27
|
+
/** Naming conventions from specification */
|
|
28
|
+
naming: NamingConventions;
|
|
29
|
+
/** Source information for debugging */
|
|
30
|
+
sources: {
|
|
31
|
+
specificationId: string;
|
|
32
|
+
epicId: string;
|
|
33
|
+
ticketId: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Override information for a single pattern key
|
|
38
|
+
*/
|
|
39
|
+
export interface PatternOverride {
|
|
40
|
+
/** Pattern key */
|
|
41
|
+
key: string;
|
|
42
|
+
/** Value from specification (if defined) */
|
|
43
|
+
specValue?: string;
|
|
44
|
+
/** Value from epic (if defined) */
|
|
45
|
+
epicValue?: string;
|
|
46
|
+
/** Value from ticket (if defined) */
|
|
47
|
+
ticketValue?: string;
|
|
48
|
+
/** Which level the final value came from */
|
|
49
|
+
resolvedFrom: 'specification' | 'epic' | 'ticket';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Detailed override report for debugging
|
|
53
|
+
*/
|
|
54
|
+
export interface PatternOverrideReport {
|
|
55
|
+
/** Final merged patterns */
|
|
56
|
+
final: Record<string, string>;
|
|
57
|
+
/** Details about each override */
|
|
58
|
+
overrides: PatternOverride[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Input data for pattern resolution
|
|
62
|
+
*/
|
|
63
|
+
export interface PatternResolutionInput {
|
|
64
|
+
/** Ticket ID */
|
|
65
|
+
ticketId: string;
|
|
66
|
+
/** Specification data */
|
|
67
|
+
specification: {
|
|
68
|
+
id: string;
|
|
69
|
+
codeStandards?: SpecificationCodeStandards | null;
|
|
70
|
+
commonImports?: string[] | null;
|
|
71
|
+
returnTypes?: ReturnTypes | null;
|
|
72
|
+
};
|
|
73
|
+
/** Epic data */
|
|
74
|
+
epic: {
|
|
75
|
+
id: string;
|
|
76
|
+
sharedPatterns?: EpicSharedPatterns | null;
|
|
77
|
+
additionalImports?: string[] | null;
|
|
78
|
+
commonFiles?: EpicCommonFiles | null;
|
|
79
|
+
};
|
|
80
|
+
/** Ticket data (optional, for override information) */
|
|
81
|
+
ticket?: {
|
|
82
|
+
id: string;
|
|
83
|
+
technicalDetails?: {
|
|
84
|
+
patterns?: Record<string, string>;
|
|
85
|
+
} | null;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Flatten SpecificationCodeStandards to a flat Record
|
|
90
|
+
*
|
|
91
|
+
* Extracts all pattern-related fields from code standards into a
|
|
92
|
+
* flat key-value structure for easy merging.
|
|
93
|
+
*
|
|
94
|
+
* @param standards - Specification code standards object
|
|
95
|
+
* @returns Flat record of pattern key-value pairs
|
|
96
|
+
*/
|
|
97
|
+
export declare function flattenCodeStandards(standards: SpecificationCodeStandards | null | undefined): Record<string, string>;
|
|
98
|
+
/**
|
|
99
|
+
* Flatten EpicSharedPatterns to a flat Record
|
|
100
|
+
*
|
|
101
|
+
* Extracts all pattern fields from epic shared patterns into a
|
|
102
|
+
* flat key-value structure for merging.
|
|
103
|
+
*
|
|
104
|
+
* @param patterns - Epic shared patterns object
|
|
105
|
+
* @returns Flat record of pattern key-value pairs
|
|
106
|
+
*/
|
|
107
|
+
export declare function flattenSharedPatterns(patterns: EpicSharedPatterns | null | undefined): Record<string, string>;
|
|
108
|
+
/**
|
|
109
|
+
* Resolve inherited patterns for a ticket
|
|
110
|
+
*
|
|
111
|
+
* Merges patterns from the specification → epic → ticket chain.
|
|
112
|
+
* Later values override earlier ones. Arrays are concatenated.
|
|
113
|
+
*
|
|
114
|
+
* @param input - Pattern resolution input with spec, epic, and ticket data
|
|
115
|
+
* @returns Fully resolved patterns
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* const resolved = resolvePatterns({
|
|
120
|
+
* ticketId: 'ticket-123',
|
|
121
|
+
* specification: {
|
|
122
|
+
* id: 'spec-1',
|
|
123
|
+
* codeStandards: { errorHandling: 'Result<T>' },
|
|
124
|
+
* commonImports: ['import { z } from "zod"']
|
|
125
|
+
* },
|
|
126
|
+
* epic: {
|
|
127
|
+
* id: 'epic-1',
|
|
128
|
+
* sharedPatterns: { errorHandling: 'AppError' },
|
|
129
|
+
* additionalImports: ['import { api } from "@/lib/api"']
|
|
130
|
+
* }
|
|
131
|
+
* });
|
|
132
|
+
*
|
|
133
|
+
* // resolved.patterns.errorHandling === 'AppError' (epic overrides spec)
|
|
134
|
+
* // resolved.imports contains both spec and epic imports
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export declare function resolvePatterns(input: PatternResolutionInput): ResolvedPatterns;
|
|
138
|
+
/**
|
|
139
|
+
* Get detailed override information for debugging
|
|
140
|
+
*
|
|
141
|
+
* Shows where each pattern value came from in the inheritance chain.
|
|
142
|
+
* Useful for debugging and understanding pattern resolution.
|
|
143
|
+
*
|
|
144
|
+
* @param input - Pattern resolution input
|
|
145
|
+
* @returns Override report with final values and source information
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const report = getPatternOverrides(input);
|
|
150
|
+
*
|
|
151
|
+
* for (const override of report.overrides) {
|
|
152
|
+
* if (override.resolvedFrom === 'epic') {
|
|
153
|
+
* console.log(`${override.key}: Epic overrides spec`);
|
|
154
|
+
* }
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export declare function getPatternOverrides(input: PatternResolutionInput): PatternOverrideReport;
|
|
159
|
+
/**
|
|
160
|
+
* Resolve patterns with caching for performance
|
|
161
|
+
*
|
|
162
|
+
* Uses an in-memory cache to avoid repeated resolution for the same ticket.
|
|
163
|
+
* Cache entries expire after TTL (default 1 minute).
|
|
164
|
+
*
|
|
165
|
+
* @param input - Pattern resolution input
|
|
166
|
+
* @param ttl - Cache TTL in milliseconds (default: 60000)
|
|
167
|
+
* @returns Resolved patterns (may be cached)
|
|
168
|
+
*/
|
|
169
|
+
export declare function resolvePatternsWithCache(input: PatternResolutionInput, ttl?: number): ResolvedPatterns;
|
|
170
|
+
/**
|
|
171
|
+
* Clear cache for a specific ticket
|
|
172
|
+
*
|
|
173
|
+
* Call this after updating a ticket's patterns.
|
|
174
|
+
*
|
|
175
|
+
* @param ticketId - Ticket ID to invalidate
|
|
176
|
+
*/
|
|
177
|
+
export declare function invalidatePatternCache(ticketId: string): void;
|
|
178
|
+
/**
|
|
179
|
+
* Clear all pattern caches
|
|
180
|
+
*
|
|
181
|
+
* Call this after updating specification or epic patterns,
|
|
182
|
+
* as these changes affect multiple tickets.
|
|
183
|
+
*/
|
|
184
|
+
export declare function invalidateAllPatternCaches(): void;
|
|
185
|
+
/**
|
|
186
|
+
* Get current cache size
|
|
187
|
+
*
|
|
188
|
+
* Useful for monitoring and debugging.
|
|
189
|
+
*
|
|
190
|
+
* @returns Number of cached entries
|
|
191
|
+
*/
|
|
192
|
+
export declare function getPatternCacheSize(): number;
|
|
193
|
+
//# sourceMappingURL=inheritance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inheritance.d.ts","sourceRoot":"","sources":["../../src/patterns/inheritance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
function flattenCodeStandards(standards) {
|
|
2
|
+
if (!standards) return {};
|
|
3
|
+
const result = {};
|
|
4
|
+
if (standards.language) result.language = standards.language;
|
|
5
|
+
if (standards.asyncPattern) result.asyncPattern = standards.asyncPattern;
|
|
6
|
+
if (standards.errorHandling) result.errorHandling = standards.errorHandling;
|
|
7
|
+
if (standards.documentation) result.documentation = standards.documentation;
|
|
8
|
+
if (standards.testing) result.testing = standards.testing;
|
|
9
|
+
if (standards.stateManagement) result.stateManagement = standards.stateManagement;
|
|
10
|
+
if (standards.apiPattern) result.apiPattern = standards.apiPattern;
|
|
11
|
+
if (standards.logging) result.logging = standards.logging;
|
|
12
|
+
if (standards.validation) result.validation = standards.validation;
|
|
13
|
+
if (standards.custom) {
|
|
14
|
+
Object.assign(result, standards.custom);
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
function flattenSharedPatterns(patterns) {
|
|
19
|
+
if (!patterns) return {};
|
|
20
|
+
const result = {};
|
|
21
|
+
if (patterns.errorHandling) result.errorHandling = patterns.errorHandling;
|
|
22
|
+
if (patterns.returnType) result.returnType = patterns.returnType;
|
|
23
|
+
if (patterns.actionPrefix) result.actionPrefix = patterns.actionPrefix;
|
|
24
|
+
if (patterns.stateSlice) result.stateSlice = patterns.stateSlice;
|
|
25
|
+
if (patterns.apiBasePath) result.apiBasePath = patterns.apiBasePath;
|
|
26
|
+
if (patterns.componentFolder) result.componentFolder = patterns.componentFolder;
|
|
27
|
+
if (patterns.hookPattern) result.hookPattern = patterns.hookPattern;
|
|
28
|
+
if (patterns.custom) {
|
|
29
|
+
Object.assign(result, patterns.custom);
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function resolvePatterns(input) {
|
|
34
|
+
const { ticketId, specification, epic, ticket } = input;
|
|
35
|
+
const specImports = specification.commonImports ?? [];
|
|
36
|
+
const epicImports = epic.additionalImports ?? [];
|
|
37
|
+
const imports = [.../* @__PURE__ */ new Set([...specImports, ...epicImports])];
|
|
38
|
+
const specPatterns = flattenCodeStandards(specification.codeStandards);
|
|
39
|
+
const epicPatterns = flattenSharedPatterns(epic.sharedPatterns);
|
|
40
|
+
const ticketPatterns = ticket?.technicalDetails?.patterns ?? {};
|
|
41
|
+
const patterns = {
|
|
42
|
+
...specPatterns,
|
|
43
|
+
...epicPatterns,
|
|
44
|
+
...ticketPatterns
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
imports,
|
|
48
|
+
patterns,
|
|
49
|
+
returnTypes: specification.returnTypes ?? {},
|
|
50
|
+
commonFiles: epic.commonFiles ?? {},
|
|
51
|
+
naming: specification.codeStandards?.naming ?? {},
|
|
52
|
+
sources: {
|
|
53
|
+
specificationId: specification.id,
|
|
54
|
+
epicId: epic.id,
|
|
55
|
+
ticketId: ticket?.id ?? ticketId
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function getPatternOverrides(input) {
|
|
60
|
+
const { specification, epic, ticket } = input;
|
|
61
|
+
const specPatterns = flattenCodeStandards(specification.codeStandards);
|
|
62
|
+
const epicPatterns = flattenSharedPatterns(epic.sharedPatterns);
|
|
63
|
+
const ticketPatterns = ticket?.technicalDetails?.patterns ?? {};
|
|
64
|
+
const allKeys = /* @__PURE__ */ new Set([
|
|
65
|
+
...Object.keys(specPatterns),
|
|
66
|
+
...Object.keys(epicPatterns),
|
|
67
|
+
...Object.keys(ticketPatterns)
|
|
68
|
+
]);
|
|
69
|
+
const overrides = Array.from(allKeys).map((key) => {
|
|
70
|
+
const specValue = specPatterns[key];
|
|
71
|
+
const epicValue = epicPatterns[key];
|
|
72
|
+
const ticketValue = ticketPatterns[key];
|
|
73
|
+
let resolvedFrom;
|
|
74
|
+
if (ticketValue !== void 0) {
|
|
75
|
+
resolvedFrom = "ticket";
|
|
76
|
+
} else if (epicValue !== void 0) {
|
|
77
|
+
resolvedFrom = "epic";
|
|
78
|
+
} else {
|
|
79
|
+
resolvedFrom = "specification";
|
|
80
|
+
}
|
|
81
|
+
return { key, specValue, epicValue, ticketValue, resolvedFrom };
|
|
82
|
+
});
|
|
83
|
+
overrides.sort((a, b) => a.key.localeCompare(b.key));
|
|
84
|
+
const final = {
|
|
85
|
+
...specPatterns,
|
|
86
|
+
...epicPatterns,
|
|
87
|
+
...ticketPatterns
|
|
88
|
+
};
|
|
89
|
+
return { final, overrides };
|
|
90
|
+
}
|
|
91
|
+
const patternCache = /* @__PURE__ */ new Map();
|
|
92
|
+
const DEFAULT_CACHE_TTL = 6e4;
|
|
93
|
+
function resolvePatternsWithCache(input, ttl = DEFAULT_CACHE_TTL) {
|
|
94
|
+
const cacheKey = input.ticketId;
|
|
95
|
+
const cached = patternCache.get(cacheKey);
|
|
96
|
+
if (cached && cached.expiry > Date.now()) {
|
|
97
|
+
return cached.patterns;
|
|
98
|
+
}
|
|
99
|
+
const patterns = resolvePatterns(input);
|
|
100
|
+
patternCache.set(cacheKey, {
|
|
101
|
+
patterns,
|
|
102
|
+
expiry: Date.now() + ttl
|
|
103
|
+
});
|
|
104
|
+
return patterns;
|
|
105
|
+
}
|
|
106
|
+
function invalidatePatternCache(ticketId) {
|
|
107
|
+
patternCache.delete(ticketId);
|
|
108
|
+
}
|
|
109
|
+
function invalidateAllPatternCaches() {
|
|
110
|
+
patternCache.clear();
|
|
111
|
+
}
|
|
112
|
+
function getPatternCacheSize() {
|
|
113
|
+
return patternCache.size;
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
flattenCodeStandards,
|
|
117
|
+
flattenSharedPatterns,
|
|
118
|
+
getPatternCacheSize,
|
|
119
|
+
getPatternOverrides,
|
|
120
|
+
invalidateAllPatternCaches,
|
|
121
|
+
invalidatePatternCache,
|
|
122
|
+
resolvePatterns,
|
|
123
|
+
resolvePatternsWithCache
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=inheritance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/patterns/inheritance.ts"],"sourcesContent":["/**\n * Pattern Inheritance Resolver\n *\n * Resolves the complete pattern inheritance chain for tickets:\n * Specification → Epic → Ticket\n *\n * Later values override earlier ones. Arrays are concatenated.\n *\n * @see MCI-030-05-pattern-inheritance-resolver\n */\n\nimport type {\n SpecificationCodeStandards,\n ReturnTypes,\n NamingConventions,\n EpicSharedPatterns,\n EpicCommonFiles,\n} from '../types/index.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Fully resolved patterns for a ticket\n *\n * Combines patterns from specification, epic, and ticket levels\n * with proper override semantics.\n */\nexport interface ResolvedPatterns {\n /** All imports (spec.commonImports + epic.additionalImports) */\n imports: string[];\n\n /** Merged patterns (spec → epic → ticket, later wins) */\n patterns: Record<string, string>;\n\n /** Return types from specification */\n returnTypes: ReturnTypes;\n\n /** Common files from epic */\n commonFiles: EpicCommonFiles;\n\n /** Naming conventions from specification */\n naming: NamingConventions;\n\n /** Source information for debugging */\n sources: {\n specificationId: string;\n epicId: string;\n ticketId: string;\n };\n}\n\n/**\n * Override information for a single pattern key\n */\nexport interface PatternOverride {\n /** Pattern key */\n key: string;\n /** Value from specification (if defined) */\n specValue?: string;\n /** Value from epic (if defined) */\n epicValue?: string;\n /** Value from ticket (if defined) */\n ticketValue?: string;\n /** Which level the final value came from */\n resolvedFrom: 'specification' | 'epic' | 'ticket';\n}\n\n/**\n * Detailed override report for debugging\n */\nexport interface PatternOverrideReport {\n /** Final merged patterns */\n final: Record<string, string>;\n /** Details about each override */\n overrides: PatternOverride[];\n}\n\n/**\n * Input data for pattern resolution\n */\nexport interface PatternResolutionInput {\n /** Ticket ID */\n ticketId: string;\n /** Specification data */\n specification: {\n id: string;\n codeStandards?: SpecificationCodeStandards | null;\n commonImports?: string[] | null;\n returnTypes?: ReturnTypes | null;\n };\n /** Epic data */\n epic: {\n id: string;\n sharedPatterns?: EpicSharedPatterns | null;\n additionalImports?: string[] | null;\n commonFiles?: EpicCommonFiles | null;\n };\n /** Ticket data (optional, for override information) */\n ticket?: {\n id: string;\n technicalDetails?: {\n patterns?: Record<string, string>;\n } | null;\n };\n}\n\n// =============================================================================\n// Pattern Flattening Helpers\n// =============================================================================\n\n/**\n * Flatten SpecificationCodeStandards to a flat Record\n *\n * Extracts all pattern-related fields from code standards into a\n * flat key-value structure for easy merging.\n *\n * @param standards - Specification code standards object\n * @returns Flat record of pattern key-value pairs\n */\nexport function flattenCodeStandards(\n standards: SpecificationCodeStandards | null | undefined\n): Record<string, string> {\n if (!standards) return {};\n\n const result: Record<string, string> = {};\n\n // Extract all standard fields\n if (standards.language) result.language = standards.language;\n if (standards.asyncPattern) result.asyncPattern = standards.asyncPattern;\n if (standards.errorHandling) result.errorHandling = standards.errorHandling;\n if (standards.documentation) result.documentation = standards.documentation;\n if (standards.testing) result.testing = standards.testing;\n if (standards.stateManagement) result.stateManagement = standards.stateManagement;\n if (standards.apiPattern) result.apiPattern = standards.apiPattern;\n if (standards.logging) result.logging = standards.logging;\n if (standards.validation) result.validation = standards.validation;\n\n // Include custom standards\n if (standards.custom) {\n Object.assign(result, standards.custom);\n }\n\n return result;\n}\n\n/**\n * Flatten EpicSharedPatterns to a flat Record\n *\n * Extracts all pattern fields from epic shared patterns into a\n * flat key-value structure for merging.\n *\n * @param patterns - Epic shared patterns object\n * @returns Flat record of pattern key-value pairs\n */\nexport function flattenSharedPatterns(\n patterns: EpicSharedPatterns | null | undefined\n): Record<string, string> {\n if (!patterns) return {};\n\n const result: Record<string, string> = {};\n\n // Extract all standard fields\n if (patterns.errorHandling) result.errorHandling = patterns.errorHandling;\n if (patterns.returnType) result.returnType = patterns.returnType;\n if (patterns.actionPrefix) result.actionPrefix = patterns.actionPrefix;\n if (patterns.stateSlice) result.stateSlice = patterns.stateSlice;\n if (patterns.apiBasePath) result.apiBasePath = patterns.apiBasePath;\n if (patterns.componentFolder) result.componentFolder = patterns.componentFolder;\n if (patterns.hookPattern) result.hookPattern = patterns.hookPattern;\n\n // Include custom patterns\n if (patterns.custom) {\n Object.assign(result, patterns.custom);\n }\n\n return result;\n}\n\n// =============================================================================\n// Main Resolution Function\n// =============================================================================\n\n/**\n * Resolve inherited patterns for a ticket\n *\n * Merges patterns from the specification → epic → ticket chain.\n * Later values override earlier ones. Arrays are concatenated.\n *\n * @param input - Pattern resolution input with spec, epic, and ticket data\n * @returns Fully resolved patterns\n *\n * @example\n * ```typescript\n * const resolved = resolvePatterns({\n * ticketId: 'ticket-123',\n * specification: {\n * id: 'spec-1',\n * codeStandards: { errorHandling: 'Result<T>' },\n * commonImports: ['import { z } from \"zod\"']\n * },\n * epic: {\n * id: 'epic-1',\n * sharedPatterns: { errorHandling: 'AppError' },\n * additionalImports: ['import { api } from \"@/lib/api\"']\n * }\n * });\n *\n * // resolved.patterns.errorHandling === 'AppError' (epic overrides spec)\n * // resolved.imports contains both spec and epic imports\n * ```\n */\nexport function resolvePatterns(input: PatternResolutionInput): ResolvedPatterns {\n const { ticketId, specification, epic, ticket } = input;\n\n // Concatenate imports (spec + epic, deduplicated)\n const specImports = specification.commonImports ?? [];\n const epicImports = epic.additionalImports ?? [];\n const imports = [...new Set([...specImports, ...epicImports])];\n\n // Flatten patterns from each level\n const specPatterns = flattenCodeStandards(specification.codeStandards);\n const epicPatterns = flattenSharedPatterns(epic.sharedPatterns);\n const ticketPatterns = ticket?.technicalDetails?.patterns ?? {};\n\n // Merge patterns (later overrides earlier)\n const patterns = {\n ...specPatterns,\n ...epicPatterns,\n ...ticketPatterns,\n };\n\n return {\n imports,\n patterns,\n returnTypes: specification.returnTypes ?? {},\n commonFiles: epic.commonFiles ?? {},\n naming: specification.codeStandards?.naming ?? {},\n sources: {\n specificationId: specification.id,\n epicId: epic.id,\n ticketId: ticket?.id ?? ticketId,\n },\n };\n}\n\n// =============================================================================\n// Override Tracking\n// =============================================================================\n\n/**\n * Get detailed override information for debugging\n *\n * Shows where each pattern value came from in the inheritance chain.\n * Useful for debugging and understanding pattern resolution.\n *\n * @param input - Pattern resolution input\n * @returns Override report with final values and source information\n *\n * @example\n * ```typescript\n * const report = getPatternOverrides(input);\n *\n * for (const override of report.overrides) {\n * if (override.resolvedFrom === 'epic') {\n * console.log(`${override.key}: Epic overrides spec`);\n * }\n * }\n * ```\n */\nexport function getPatternOverrides(input: PatternResolutionInput): PatternOverrideReport {\n const { specification, epic, ticket } = input;\n\n const specPatterns = flattenCodeStandards(specification.codeStandards);\n const epicPatterns = flattenSharedPatterns(epic.sharedPatterns);\n const ticketPatterns = ticket?.technicalDetails?.patterns ?? {};\n\n // Get all unique keys\n const allKeys = new Set([\n ...Object.keys(specPatterns),\n ...Object.keys(epicPatterns),\n ...Object.keys(ticketPatterns),\n ]);\n\n // Build override information for each key\n const overrides: PatternOverride[] = Array.from(allKeys).map((key) => {\n const specValue = specPatterns[key];\n const epicValue = epicPatterns[key];\n const ticketValue = ticketPatterns[key];\n\n // Determine which level the value came from\n let resolvedFrom: 'specification' | 'epic' | 'ticket';\n if (ticketValue !== undefined) {\n resolvedFrom = 'ticket';\n } else if (epicValue !== undefined) {\n resolvedFrom = 'epic';\n } else {\n resolvedFrom = 'specification';\n }\n\n return { key, specValue, epicValue, ticketValue, resolvedFrom };\n });\n\n // Sort by key for consistent output\n overrides.sort((a, b) => a.key.localeCompare(b.key));\n\n // Build final merged patterns\n const final = {\n ...specPatterns,\n ...epicPatterns,\n ...ticketPatterns,\n };\n\n return { final, overrides };\n}\n\n// =============================================================================\n// Caching\n// =============================================================================\n\n/** Cache entry with expiry */\ninterface CacheEntry {\n patterns: ResolvedPatterns;\n expiry: number;\n}\n\n/** Pattern resolution cache */\nconst patternCache = new Map<string, CacheEntry>();\n\n/** Default cache TTL (1 minute) */\nconst DEFAULT_CACHE_TTL = 60000;\n\n/**\n * Resolve patterns with caching for performance\n *\n * Uses an in-memory cache to avoid repeated resolution for the same ticket.\n * Cache entries expire after TTL (default 1 minute).\n *\n * @param input - Pattern resolution input\n * @param ttl - Cache TTL in milliseconds (default: 60000)\n * @returns Resolved patterns (may be cached)\n */\nexport function resolvePatternsWithCache(\n input: PatternResolutionInput,\n ttl: number = DEFAULT_CACHE_TTL\n): ResolvedPatterns {\n const cacheKey = input.ticketId;\n const cached = patternCache.get(cacheKey);\n\n if (cached && cached.expiry > Date.now()) {\n return cached.patterns;\n }\n\n const patterns = resolvePatterns(input);\n\n patternCache.set(cacheKey, {\n patterns,\n expiry: Date.now() + ttl,\n });\n\n return patterns;\n}\n\n/**\n * Clear cache for a specific ticket\n *\n * Call this after updating a ticket's patterns.\n *\n * @param ticketId - Ticket ID to invalidate\n */\nexport function invalidatePatternCache(ticketId: string): void {\n patternCache.delete(ticketId);\n}\n\n/**\n * Clear all pattern caches\n *\n * Call this after updating specification or epic patterns,\n * as these changes affect multiple tickets.\n */\nexport function invalidateAllPatternCaches(): void {\n patternCache.clear();\n}\n\n/**\n * Get current cache size\n *\n * Useful for monitoring and debugging.\n *\n * @returns Number of cached entries\n */\nexport function getPatternCacheSize(): number {\n return patternCache.size;\n}\n"],"mappings":"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","names":[]}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpecForge MCP Server
|
|
3
|
+
*
|
|
4
|
+
* MCP (Model Context Protocol) server implementation using stdio transport.
|
|
5
|
+
* Provides tools for AI agents to interact with SpecForge projects.
|
|
6
|
+
*
|
|
7
|
+
* M9: the boot-time `set_working_context` mechanism was removed — that backend
|
|
8
|
+
* op no longer exists. Project/spec/session identity is injected per-call from
|
|
9
|
+
* `.specforge/config.json` by the tool handlers (M9.6b), and the agent reads
|
|
10
|
+
* the M8 envelope (interpreted in `handleToolCall`), TOON-encoded on the wire.
|
|
11
|
+
*/
|
|
12
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
13
|
+
import { ApiClient } from './client/api-client.js';
|
|
14
|
+
import { McpConfig } from './config/index.js';
|
|
15
|
+
import type { OutputFormat } from './cli/config/types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Dispatch a tool call and frame the result for the MCP wire.
|
|
18
|
+
*
|
|
19
|
+
* The wire encoding follows the resolved `mcpOutputFormat` (M9.6c) — TOON by
|
|
20
|
+
* default, JSON when the project/global/env config selects it — on both the
|
|
21
|
+
* success and error paths. Exposed for direct testing without spinning up the
|
|
22
|
+
* full MCP transport.
|
|
23
|
+
*/
|
|
24
|
+
export declare function dispatchToolCall(apiClient: ApiClient, toolName: string, args: Record<string, unknown>, debug: boolean, format?: OutputFormat): Promise<{
|
|
25
|
+
content: Array<{
|
|
26
|
+
type: 'text';
|
|
27
|
+
text: string;
|
|
28
|
+
}>;
|
|
29
|
+
isError?: boolean;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Create and configure the MCP server
|
|
33
|
+
*
|
|
34
|
+
* @param config - Configuration for the server
|
|
35
|
+
* @returns Configured MCP Server instance
|
|
36
|
+
*/
|
|
37
|
+
export declare function createServer(config: McpConfig): Promise<Server>;
|
|
38
|
+
/**
|
|
39
|
+
* Start the MCP server with stdio transport
|
|
40
|
+
*
|
|
41
|
+
* @param server - The MCP server instance
|
|
42
|
+
*/
|
|
43
|
+
export declare function startServer(server: Server): Promise<void>;
|
|
44
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAMnE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAY1D;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,OAAO,EACd,MAAM,GAAE,YAAqB,GAC5B,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAgBhF;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CA8CrE;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B/D"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
ListToolsRequestSchema
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import { ApiClient } from "./client/api-client.js";
|
|
8
|
+
import { getTools, handleToolCall } from "./tools/index.js";
|
|
9
|
+
import { formatToon } from "./lib/format.js";
|
|
10
|
+
function encodeMcp(value, format) {
|
|
11
|
+
return format === "json" ? JSON.stringify(value, null, 2) : formatToon(value);
|
|
12
|
+
}
|
|
13
|
+
const VERSION = "1.0.0";
|
|
14
|
+
async function dispatchToolCall(apiClient, toolName, args, debug, format = "toon") {
|
|
15
|
+
try {
|
|
16
|
+
const result = await handleToolCall(apiClient, toolName, args, debug);
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: "text", text: encodeMcp(result, format) }]
|
|
19
|
+
};
|
|
20
|
+
} catch (error) {
|
|
21
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
22
|
+
if (debug) {
|
|
23
|
+
console.error(`[DEBUG] Tool ${toolName} failed:`, message);
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
content: [{ type: "text", text: encodeMcp({ error: message }, format) }],
|
|
27
|
+
isError: true
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function createServer(config) {
|
|
32
|
+
const apiClient = new ApiClient(config);
|
|
33
|
+
const server = new Server(
|
|
34
|
+
{
|
|
35
|
+
name: "specforge",
|
|
36
|
+
version: VERSION
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
capabilities: {
|
|
40
|
+
tools: {}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
45
|
+
if (config.debug) {
|
|
46
|
+
console.error("[DEBUG] ListTools request received");
|
|
47
|
+
}
|
|
48
|
+
const tools = getTools();
|
|
49
|
+
if (config.debug) {
|
|
50
|
+
console.error(`[DEBUG] Returning ${tools.length} tools`);
|
|
51
|
+
}
|
|
52
|
+
return { tools };
|
|
53
|
+
});
|
|
54
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
55
|
+
const { name, arguments: args } = request.params;
|
|
56
|
+
const argsRecord = args || {};
|
|
57
|
+
if (config.debug) {
|
|
58
|
+
console.error("[DEBUG] CallTool request:", {
|
|
59
|
+
name,
|
|
60
|
+
args: JSON.stringify(args)
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return dispatchToolCall(apiClient, name, argsRecord, config.debug, config.mcpOutputFormat ?? "toon");
|
|
64
|
+
});
|
|
65
|
+
return server;
|
|
66
|
+
}
|
|
67
|
+
async function startServer(server) {
|
|
68
|
+
const transport = new StdioServerTransport();
|
|
69
|
+
const shutdown = async () => {
|
|
70
|
+
console.error("[INFO] Shutting down MCP server...");
|
|
71
|
+
await server.close();
|
|
72
|
+
process.exit(0);
|
|
73
|
+
};
|
|
74
|
+
process.on("SIGINT", shutdown);
|
|
75
|
+
process.on("SIGTERM", shutdown);
|
|
76
|
+
process.on("uncaughtException", (error) => {
|
|
77
|
+
console.error("[ERROR] Uncaught exception:", error);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|
|
80
|
+
process.on("unhandledRejection", (reason) => {
|
|
81
|
+
console.error("[ERROR] Unhandled rejection:", reason);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
84
|
+
await server.connect(transport);
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
createServer,
|
|
88
|
+
dispatchToolCall,
|
|
89
|
+
startServer
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["/**\n * SpecForge MCP Server\n *\n * MCP (Model Context Protocol) server implementation using stdio transport.\n * Provides tools for AI agents to interact with SpecForge projects.\n *\n * M9: the boot-time `set_working_context` mechanism was removed — that backend\n * op no longer exists. Project/spec/session identity is injected per-call from\n * `.specforge/config.json` by the tool handlers (M9.6b), and the agent reads\n * the M8 envelope (interpreted in `handleToolCall`), TOON-encoded on the wire.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { ApiClient } from './client/api-client.js';\nimport { McpConfig } from './config/index.js';\nimport { getTools, handleToolCall } from './tools/index.js';\nimport { formatToon } from './lib/format.js';\nimport type { OutputFormat } from './cli/config/types.js';\n\n/** Encode an MCP result for the wire per the resolved `mcpOutputFormat`. */\nfunction encodeMcp(value: unknown, format: OutputFormat): string {\n return format === 'json' ? JSON.stringify(value, null, 2) : formatToon(value);\n}\n\n/**\n * Package version - should match package.json\n */\nconst VERSION = '1.0.0';\n\n/**\n * Dispatch a tool call and frame the result for the MCP wire.\n *\n * The wire encoding follows the resolved `mcpOutputFormat` (M9.6c) — TOON by\n * default, JSON when the project/global/env config selects it — on both the\n * success and error paths. Exposed for direct testing without spinning up the\n * full MCP transport.\n */\nexport async function dispatchToolCall(\n apiClient: ApiClient,\n toolName: string,\n args: Record<string, unknown>,\n debug: boolean,\n format: OutputFormat = 'toon'\n): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean }> {\n try {\n const result = await handleToolCall(apiClient, toolName, args, debug);\n return {\n content: [{ type: 'text', text: encodeMcp(result, format) }],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (debug) {\n console.error(`[DEBUG] Tool ${toolName} failed:`, message);\n }\n return {\n content: [{ type: 'text', text: encodeMcp({ error: message }, format) }],\n isError: true,\n };\n }\n}\n\n/**\n * Create and configure the MCP server\n *\n * @param config - Configuration for the server\n * @returns Configured MCP Server instance\n */\nexport async function createServer(config: McpConfig): Promise<Server> {\n const apiClient = new ApiClient(config);\n\n const server = new Server(\n {\n name: 'specforge',\n version: VERSION,\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n // Handle ListTools requests - return available tools\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n if (config.debug) {\n console.error('[DEBUG] ListTools request received');\n }\n\n const tools = getTools();\n\n if (config.debug) {\n console.error(`[DEBUG] Returning ${tools.length} tools`);\n }\n\n return { tools };\n });\n\n // Handle CallTool requests - execute the requested tool\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n const argsRecord = (args || {}) as Record<string, unknown>;\n\n if (config.debug) {\n console.error('[DEBUG] CallTool request:', {\n name,\n args: JSON.stringify(args),\n });\n }\n\n return dispatchToolCall(apiClient, name, argsRecord, config.debug, config.mcpOutputFormat ?? 'toon');\n });\n\n return server;\n}\n\n/**\n * Start the MCP server with stdio transport\n *\n * @param server - The MCP server instance\n */\nexport async function startServer(server: Server): Promise<void> {\n const transport = new StdioServerTransport();\n\n // Handle graceful shutdown\n const shutdown = async () => {\n console.error('[INFO] Shutting down MCP server...');\n await server.close();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n // Handle uncaught errors\n process.on('uncaughtException', (error) => {\n console.error('[ERROR] Uncaught exception:', error);\n process.exit(1);\n });\n\n process.on('unhandledRejection', (reason) => {\n console.error('[ERROR] Unhandled rejection:', reason);\n process.exit(1);\n });\n\n // Connect to transport\n await server.connect(transport);\n}\n"],"mappings":"AAYA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAE1B,SAAS,UAAU,sBAAsB;AACzC,SAAS,kBAAkB;AAI3B,SAAS,UAAU,OAAgB,QAA8B;AAC/D,SAAO,WAAW,SAAS,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,WAAW,KAAK;AAC9E;AAKA,MAAM,UAAU;AAUhB,eAAsB,iBACpB,WACA,UACA,MACA,OACA,SAAuB,QACyD;AAChF,MAAI;AACF,UAAM,SAAS,MAAM,eAAe,WAAW,UAAU,MAAM,KAAK;AACpE,WAAO;AAAA,MACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,QAAQ,MAAM,EAAE,CAAC;AAAA,IAC7D;AAAA,EACF,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,QAAI,OAAO;AACT,cAAQ,MAAM,gBAAgB,QAAQ,YAAY,OAAO;AAAA,IAC3D;AACA,WAAO;AAAA,MACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,EAAE,OAAO,QAAQ,GAAG,MAAM,EAAE,CAAC;AAAA,MACvE,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAQA,eAAsB,aAAa,QAAoC;AACrE,QAAM,YAAY,IAAI,UAAU,MAAM;AAEtC,QAAM,SAAS,IAAI;AAAA,IACjB;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,cAAc;AAAA,QACZ,OAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAGA,SAAO,kBAAkB,wBAAwB,YAAY;AAC3D,QAAI,OAAO,OAAO;AAChB,cAAQ,MAAM,oCAAoC;AAAA,IACpD;AAEA,UAAM,QAAQ,SAAS;AAEvB,QAAI,OAAO,OAAO;AAChB,cAAQ,MAAM,qBAAqB,MAAM,MAAM,QAAQ;AAAA,IACzD;AAEA,WAAO,EAAE,MAAM;AAAA,EACjB,CAAC;AAGD,SAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,UAAM,EAAE,MAAM,WAAW,KAAK,IAAI,QAAQ;AAC1C,UAAM,aAAc,QAAQ,CAAC;AAE7B,QAAI,OAAO,OAAO;AAChB,cAAQ,MAAM,6BAA6B;AAAA,QACzC;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B,CAAC;AAAA,IACH;AAEA,WAAO,iBAAiB,WAAW,MAAM,YAAY,OAAO,OAAO,OAAO,mBAAmB,MAAM;AAAA,EACrG,CAAC;AAED,SAAO;AACT;AAOA,eAAsB,YAAY,QAA+B;AAC/D,QAAM,YAAY,IAAI,qBAAqB;AAG3C,QAAM,WAAW,YAAY;AAC3B,YAAQ,MAAM,oCAAoC;AAClD,UAAM,OAAO,MAAM;AACnB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAG9B,UAAQ,GAAG,qBAAqB,CAAC,UAAU;AACzC,YAAQ,MAAM,+BAA+B,KAAK;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAED,UAAQ,GAAG,sBAAsB,CAAC,WAAW;AAC3C,YAAQ,MAAM,gCAAgC,MAAM;AACpD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAGD,QAAM,OAAO,QAAQ,SAAS;AAChC;","names":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Helper
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for injecting working context from .specforge/config.json
|
|
5
|
+
* into tool arguments. This ensures tools scope to the correct project/specification
|
|
6
|
+
* when not explicitly provided.
|
|
7
|
+
*/
|
|
8
|
+
import type { ResolvedAgentTeamsConfig } from '../../cli/config/loader.js';
|
|
9
|
+
import type { ActiveSpecification } from '../../cli/config/agent-teams.types.js';
|
|
10
|
+
type ContextField = 'projectId' | 'specificationId';
|
|
11
|
+
/**
|
|
12
|
+
* Inject missing context fields from .specforge/config.json.
|
|
13
|
+
* Explicit args always take precedence over config values.
|
|
14
|
+
*
|
|
15
|
+
* @param args - Tool arguments that may be missing context fields
|
|
16
|
+
* @param fields - Context fields to inject if missing
|
|
17
|
+
* @returns Args with context fields injected from config
|
|
18
|
+
*/
|
|
19
|
+
export declare function injectContext<T extends Record<string, unknown>>(args: T, fields: ContextField[]): T;
|
|
20
|
+
/**
|
|
21
|
+
* Inject context and validate required fields are present.
|
|
22
|
+
* Throws error if required fields cannot be resolved from config.
|
|
23
|
+
*
|
|
24
|
+
* @param args - Tool arguments that may be missing context fields
|
|
25
|
+
* @param requiredFields - Fields that MUST be present after injection
|
|
26
|
+
* @param optionalFields - Fields to inject if available but not require
|
|
27
|
+
* @returns Args with context fields injected
|
|
28
|
+
* @throws Error if required fields cannot be resolved
|
|
29
|
+
*/
|
|
30
|
+
export declare function injectContextRequired<T extends Record<string, unknown>>(args: T, requiredFields: ContextField[], optionalFields?: ContextField[]): T;
|
|
31
|
+
/**
|
|
32
|
+
* Get the resolved Agent Teams config from .specforge/config.json.
|
|
33
|
+
*
|
|
34
|
+
* Claude Code Agent Teams only. Not used by MCP tool handlers.
|
|
35
|
+
*
|
|
36
|
+
* Always returns a fully-resolved config with defaults applied (never null).
|
|
37
|
+
* Used by CLI commands like `plan` and `status --teams` to read the user's
|
|
38
|
+
* preferred strategy, team limits, and other settings.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getAgentTeamsContext(): ResolvedAgentTeamsConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Get the active specification from .specforge/config.json.
|
|
43
|
+
*
|
|
44
|
+
* Claude Code Agent Teams only. Not used by MCP tool handlers.
|
|
45
|
+
*
|
|
46
|
+
* @returns ActiveSpecification if set, null if no spec is activated
|
|
47
|
+
*/
|
|
48
|
+
export declare function getActiveSpecificationContext(): ActiveSpecification | null;
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=context-helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-helper.d.ts","sourceRoot":"","sources":["../../../src/tools/core/context-helper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAEjF,KAAK,YAAY,GAAG,WAAW,GAAG,iBAAiB,CAAC;AAwBpD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7D,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,YAAY,EAAE,GACrB,CAAC,CAaH;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,IAAI,EAAE,CAAC,EACP,cAAc,EAAE,YAAY,EAAE,EAC9B,cAAc,GAAE,YAAY,EAAO,GAClC,CAAC,CAWH;AAQD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI,wBAAwB,CAG/D;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,IAAI,mBAAmB,GAAG,IAAI,CAG1E"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tryLoadProjectConfig,
|
|
3
|
+
getAgentTeamsConfig as resolveAgentTeamsConfig,
|
|
4
|
+
getActiveSpecification as resolveActiveSpecification
|
|
5
|
+
} from "../../cli/config/loader.js";
|
|
6
|
+
function getLocalContext() {
|
|
7
|
+
const config = tryLoadProjectConfig();
|
|
8
|
+
return {
|
|
9
|
+
projectId: config?.projectId,
|
|
10
|
+
specificationId: config?.specificationId
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function injectContext(args, fields) {
|
|
14
|
+
const missingFields = fields.filter((f) => !args[f]);
|
|
15
|
+
if (missingFields.length === 0) return args;
|
|
16
|
+
const context = getLocalContext();
|
|
17
|
+
const result = { ...args };
|
|
18
|
+
for (const field of missingFields) {
|
|
19
|
+
if (context[field]) {
|
|
20
|
+
result[field] = context[field];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
function injectContextRequired(args, requiredFields, optionalFields = []) {
|
|
26
|
+
const result = injectContext(args, [...requiredFields, ...optionalFields]);
|
|
27
|
+
const missing = requiredFields.filter((f) => !result[f]);
|
|
28
|
+
if (missing.length > 0) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Missing required context: ${missing.join(", ")}. Provide explicitly or run 'specforge switch' to set working context.`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function getAgentTeamsContext() {
|
|
36
|
+
const config = tryLoadProjectConfig();
|
|
37
|
+
return resolveAgentTeamsConfig(config);
|
|
38
|
+
}
|
|
39
|
+
function getActiveSpecificationContext() {
|
|
40
|
+
const config = tryLoadProjectConfig();
|
|
41
|
+
return resolveActiveSpecification(config);
|
|
42
|
+
}
|
|
43
|
+
export {
|
|
44
|
+
getActiveSpecificationContext,
|
|
45
|
+
getAgentTeamsContext,
|
|
46
|
+
injectContext,
|
|
47
|
+
injectContextRequired
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=context-helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/tools/core/context-helper.ts"],"sourcesContent":["/**\n * Context Helper\n *\n * Provides utilities for injecting working context from .specforge/config.json\n * into tool arguments. This ensures tools scope to the correct project/specification\n * when not explicitly provided.\n */\n\nimport {\n tryLoadProjectConfig,\n getAgentTeamsConfig as resolveAgentTeamsConfig,\n getActiveSpecification as resolveActiveSpecification,\n} from '../../cli/config/loader.js';\nimport type { ResolvedAgentTeamsConfig } from '../../cli/config/loader.js';\nimport type { ActiveSpecification } from '../../cli/config/agent-teams.types.js';\n\ntype ContextField = 'projectId' | 'specificationId';\n\ninterface LocalContext {\n projectId?: string;\n specificationId?: string;\n}\n\n// ============================================================\n// UNIVERSAL CONTEXT — Used by MCP tool handlers\n// Works with any MCP-connected agent CLI\n// ============================================================\n\n/**\n * Get context from .specforge/config.json file.\n * This is the source of truth for the current project/specification.\n */\nfunction getLocalContext(): LocalContext {\n const config = tryLoadProjectConfig();\n return {\n projectId: config?.projectId,\n specificationId: config?.specificationId,\n };\n}\n\n/**\n * Inject missing context fields from .specforge/config.json.\n * Explicit args always take precedence over config values.\n *\n * @param args - Tool arguments that may be missing context fields\n * @param fields - Context fields to inject if missing\n * @returns Args with context fields injected from config\n */\nexport function injectContext<T extends Record<string, unknown>>(\n args: T,\n fields: ContextField[]\n): T {\n const missingFields = fields.filter(f => !args[f]);\n if (missingFields.length === 0) return args;\n\n const context = getLocalContext();\n const result: Record<string, unknown> = { ...args };\n\n for (const field of missingFields) {\n if (context[field]) {\n result[field] = context[field];\n }\n }\n return result as T;\n}\n\n/**\n * Inject context and validate required fields are present.\n * Throws error if required fields cannot be resolved from config.\n *\n * @param args - Tool arguments that may be missing context fields\n * @param requiredFields - Fields that MUST be present after injection\n * @param optionalFields - Fields to inject if available but not require\n * @returns Args with context fields injected\n * @throws Error if required fields cannot be resolved\n */\nexport function injectContextRequired<T extends Record<string, unknown>>(\n args: T,\n requiredFields: ContextField[],\n optionalFields: ContextField[] = []\n): T {\n const result = injectContext(args, [...requiredFields, ...optionalFields]);\n\n const missing = requiredFields.filter(f => !result[f]);\n if (missing.length > 0) {\n throw new Error(\n `Missing required context: ${missing.join(', ')}. ` +\n `Provide explicitly or run 'specforge switch' to set working context.`\n );\n }\n return result;\n}\n\n// ============================================================\n// AGENT TEAMS CONTEXT — Claude Code specific\n// Used by CLI orchestration layer only (spec-activate, plan, status --teams)\n// NOT used by any MCP tool handler\n// ============================================================\n\n/**\n * Get the resolved Agent Teams config from .specforge/config.json.\n *\n * Claude Code Agent Teams only. Not used by MCP tool handlers.\n *\n * Always returns a fully-resolved config with defaults applied (never null).\n * Used by CLI commands like `plan` and `status --teams` to read the user's\n * preferred strategy, team limits, and other settings.\n */\nexport function getAgentTeamsContext(): ResolvedAgentTeamsConfig {\n const config = tryLoadProjectConfig();\n return resolveAgentTeamsConfig(config);\n}\n\n/**\n * Get the active specification from .specforge/config.json.\n *\n * Claude Code Agent Teams only. Not used by MCP tool handlers.\n *\n * @returns ActiveSpecification if set, null if no spec is activated\n */\nexport function getActiveSpecificationContext(): ActiveSpecification | null {\n const config = tryLoadProjectConfig();\n return resolveActiveSpecification(config);\n}\n"],"mappings":"AAQA;AAAA,EACE;AAAA,EACA,uBAAuB;AAAA,EACvB,0BAA0B;AAAA,OACrB;AAoBP,SAAS,kBAAgC;AACvC,QAAM,SAAS,qBAAqB;AACpC,SAAO;AAAA,IACL,WAAW,QAAQ;AAAA,IACnB,iBAAiB,QAAQ;AAAA,EAC3B;AACF;AAUO,SAAS,cACd,MACA,QACG;AACH,QAAM,gBAAgB,OAAO,OAAO,OAAK,CAAC,KAAK,CAAC,CAAC;AACjD,MAAI,cAAc,WAAW,EAAG,QAAO;AAEvC,QAAM,UAAU,gBAAgB;AAChC,QAAM,SAAkC,EAAE,GAAG,KAAK;AAElD,aAAW,SAAS,eAAe;AACjC,QAAI,QAAQ,KAAK,GAAG;AAClB,aAAO,KAAK,IAAI,QAAQ,KAAK;AAAA,IAC/B;AAAA,EACF;AACA,SAAO;AACT;AAYO,SAAS,sBACd,MACA,gBACA,iBAAiC,CAAC,GAC/B;AACH,QAAM,SAAS,cAAc,MAAM,CAAC,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAEzE,QAAM,UAAU,eAAe,OAAO,OAAK,CAAC,OAAO,CAAC,CAAC;AACrD,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,6BAA6B,QAAQ,KAAK,IAAI,CAAC;AAAA,IAEjD;AAAA,EACF;AACA,SAAO;AACT;AAiBO,SAAS,uBAAiD;AAC/D,QAAM,SAAS,qBAAqB;AACpC,SAAO,wBAAwB,MAAM;AACvC;AASO,SAAS,gCAA4D;AAC1E,QAAM,SAAS,qBAAqB;AACpC,SAAO,2BAA2B,MAAM;AAC1C;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/core/index.ts"],"names":[],"mappings":"AAEA,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/tools/core/index.ts"],"sourcesContent":["// mcp/src/tools/core/index.ts\n\nexport * from './context-helper.js';\n"],"mappings":"AAEA,cAAc;","names":[]}
|