@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,105 @@
|
|
|
1
|
+
import { RemoteTransport } from "../transport/remote.js";
|
|
2
|
+
const DEFAULT_TIMEOUT = 3e4;
|
|
3
|
+
class ApiClient {
|
|
4
|
+
config;
|
|
5
|
+
transport;
|
|
6
|
+
sessionId;
|
|
7
|
+
/**
|
|
8
|
+
* @param config - resolved MCP config (URL, key, debug)
|
|
9
|
+
* @param options - optional client options; `origin` sets the
|
|
10
|
+
* `X-SpecForge-Origin` header (M9.7), defaulting to `mcp` (agent traffic).
|
|
11
|
+
* The human CLI surface ({@link createCliClient} / the feedback command)
|
|
12
|
+
* passes `cli`.
|
|
13
|
+
*/
|
|
14
|
+
constructor(config, options = {}) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.transport = new RemoteTransport({
|
|
17
|
+
apiUrl: config.apiUrl,
|
|
18
|
+
apiKey: config.apiKey,
|
|
19
|
+
debug: config.debug,
|
|
20
|
+
origin: options.origin
|
|
21
|
+
});
|
|
22
|
+
this.sessionId = crypto.randomUUID();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get the underlying transport for direct use by tool handlers
|
|
26
|
+
*/
|
|
27
|
+
getTransport() {
|
|
28
|
+
return this.transport;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get the session ID for this MCP connection
|
|
32
|
+
* Used for working context management
|
|
33
|
+
*/
|
|
34
|
+
getSessionId() {
|
|
35
|
+
return this.sessionId;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Make an API call to SpecForge
|
|
39
|
+
*
|
|
40
|
+
* @param operation - Name of the operation to execute
|
|
41
|
+
* @param args - Arguments for the operation
|
|
42
|
+
* @param options - Optional request configuration
|
|
43
|
+
* @returns Promise resolving to the operation result
|
|
44
|
+
* @throws {Error} If the request fails or returns an error
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* // List projects
|
|
49
|
+
* const result = await client.call('list_projects');
|
|
50
|
+
*
|
|
51
|
+
* // Get a specific ticket
|
|
52
|
+
* const ticket = await client.call('get_ticket', { ticketId: 'ticket123' });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
async call(operation, args = {}, _options = {}) {
|
|
56
|
+
const body = await this.transport.execute(operation, args);
|
|
57
|
+
if (body && typeof body === "object") {
|
|
58
|
+
const rec = body;
|
|
59
|
+
if (rec.error !== void 0 && rec.error !== null) {
|
|
60
|
+
throw new Error(`Operation failed: ${String(rec.error)}`);
|
|
61
|
+
}
|
|
62
|
+
if ("data" in rec) {
|
|
63
|
+
return rec.data;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return body;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Validate the configured API key
|
|
70
|
+
*
|
|
71
|
+
* Makes a lightweight call to verify the API key is valid and
|
|
72
|
+
* returns the associated user information.
|
|
73
|
+
*
|
|
74
|
+
* @returns Promise resolving to validation result
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const result = await client.validateApiKey();
|
|
79
|
+
* if (result.valid) {
|
|
80
|
+
* console.log('Authenticated as:', result.userId);
|
|
81
|
+
* } else {
|
|
82
|
+
* console.error('Invalid API key:', result.message);
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
async validateApiKey() {
|
|
87
|
+
try {
|
|
88
|
+
const result = await this.call("validate_api_key");
|
|
89
|
+
return {
|
|
90
|
+
valid: true,
|
|
91
|
+
userId: result.userId,
|
|
92
|
+
permissions: result.permissions
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return {
|
|
96
|
+
valid: false,
|
|
97
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
ApiClient
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/api-client.ts"],"sourcesContent":["/**\n * SpecForge API Client\n *\n * HTTP client for communicating with the SpecForge API Gateway.\n * Handles authentication, request/response formatting, and error handling.\n */\n\nimport { McpConfig } from '../config/index.js';\nimport type { ITransport, TransportOrigin } from '../transport/interface.js';\nimport { RemoteTransport } from '../transport/remote.js';\n\n/**\n * API response structure returned by SpecForge API\n */\nexport interface ApiResponse<T = unknown> {\n /** Response data on success */\n data?: T;\n /** Error message on failure */\n error?: string;\n}\n\n/**\n * Result of API key validation\n */\nexport interface ApiKeyValidationResult {\n /** Whether the API key is valid */\n valid: boolean;\n /** User ID associated with the key (if valid) */\n userId?: string;\n /** Permissions granted to the key (if valid) */\n permissions?: {\n read?: boolean;\n write?: boolean;\n admin?: boolean;\n };\n /** Error message (if invalid) */\n message?: string;\n}\n\n/**\n * Options for API requests\n */\nexport interface ApiRequestOptions {\n /** Request timeout in milliseconds */\n timeout?: number;\n /** Additional headers to include */\n headers?: Record<string, string>;\n /** Signal for aborting the request */\n signal?: AbortSignal;\n}\n\n/**\n * Default request timeout (30 seconds)\n */\nconst DEFAULT_TIMEOUT = 30000;\n\n/**\n * API Client for SpecForge\n *\n * @example\n * ```typescript\n * import { getConfig } from './config/index.js';\n * import { ApiClient } from './client/api-client.js';\n *\n * const config = getConfig();\n * const client = new ApiClient(config);\n *\n * // Make an API call\n * const projects = await client.call('list_projects', { userId: 'user123' });\n * ```\n */\nexport class ApiClient {\n private config: McpConfig;\n private transport: ITransport;\n private sessionId: string;\n\n /**\n * @param config - resolved MCP config (URL, key, debug)\n * @param options - optional client options; `origin` sets the\n * `X-SpecForge-Origin` header (M9.7), defaulting to `mcp` (agent traffic).\n * The human CLI surface ({@link createCliClient} / the feedback command)\n * passes `cli`.\n */\n constructor(config: McpConfig, options: { origin?: TransportOrigin } = {}) {\n this.config = config;\n this.transport = new RemoteTransport({\n apiUrl: config.apiUrl,\n apiKey: config.apiKey,\n debug: config.debug,\n origin: options.origin,\n });\n // Generate a unique session ID for this MCP connection\n this.sessionId = crypto.randomUUID();\n }\n\n /**\n * Get the underlying transport for direct use by tool handlers\n */\n getTransport(): ITransport {\n return this.transport;\n }\n\n /**\n * Get the session ID for this MCP connection\n * Used for working context management\n */\n getSessionId(): string {\n return this.sessionId;\n }\n\n /**\n * Make an API call to SpecForge\n *\n * @param operation - Name of the operation to execute\n * @param args - Arguments for the operation\n * @param options - Optional request configuration\n * @returns Promise resolving to the operation result\n * @throws {Error} If the request fails or returns an error\n *\n * @example\n * ```typescript\n * // List projects\n * const result = await client.call('list_projects');\n *\n * // Get a specific ticket\n * const ticket = await client.call('get_ticket', { ticketId: 'ticket123' });\n * ```\n */\n async call<T = unknown>(\n operation: string,\n args: Record<string, unknown> = {},\n _options: ApiRequestOptions = {}\n ): Promise<T> {\n // `/cli` semantics (M9, approach A): the transport returns the raw body;\n // unwrap the `{ data }` envelope and surface `{ error }` as a throw. A raw\n // body without `data`/`error` (e.g. a /local M8 envelope read via the agent\n // path) passes through untouched.\n const body = await this.transport.execute<unknown>(operation, args);\n if (body && typeof body === 'object') {\n const rec = body as { data?: unknown; error?: unknown };\n if (rec.error !== undefined && rec.error !== null) {\n throw new Error(`Operation failed: ${String(rec.error)}`);\n }\n if ('data' in rec) {\n return rec.data as T;\n }\n }\n return body as T;\n }\n\n /**\n * Validate the configured API key\n *\n * Makes a lightweight call to verify the API key is valid and\n * returns the associated user information.\n *\n * @returns Promise resolving to validation result\n *\n * @example\n * ```typescript\n * const result = await client.validateApiKey();\n * if (result.valid) {\n * console.log('Authenticated as:', result.userId);\n * } else {\n * console.error('Invalid API key:', result.message);\n * }\n * ```\n */\n async validateApiKey(): Promise<ApiKeyValidationResult> {\n try {\n const result = await this.call<{\n userId: string;\n permissions: {\n read?: boolean;\n write?: boolean;\n admin?: boolean;\n };\n }>('validate_api_key');\n\n return {\n valid: true,\n userId: result.userId,\n permissions: result.permissions,\n };\n } catch (error) {\n return {\n valid: false,\n message: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n}\n"],"mappings":"AASA,SAAS,uBAAuB;AA6ChC,MAAM,kBAAkB;AAiBjB,MAAM,UAAU;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASR,YAAY,QAAmB,UAAwC,CAAC,GAAG;AACzE,SAAK,SAAS;AACd,SAAK,YAAY,IAAI,gBAAgB;AAAA,MACnC,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAED,SAAK,YAAY,OAAO,WAAW;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,eAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,KACJ,WACA,OAAgC,CAAC,GACjC,WAA8B,CAAC,GACnB;AAKZ,UAAM,OAAO,MAAM,KAAK,UAAU,QAAiB,WAAW,IAAI;AAClE,QAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,YAAM,MAAM;AACZ,UAAI,IAAI,UAAU,UAAa,IAAI,UAAU,MAAM;AACjD,cAAM,IAAI,MAAM,qBAAqB,OAAO,IAAI,KAAK,CAAC,EAAE;AAAA,MAC1D;AACA,UAAI,UAAU,KAAK;AACjB,eAAO,IAAI;AAAA,MACb;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,MAAM,iBAAkD;AACtD,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,KAOvB,kBAAkB;AAErB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,OAAO;AAAA,QACf,aAAa,OAAO;AAAA,MACtB;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,OAAO;AAAA,QACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAEF;","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-direct client (M9 `/cli`)
|
|
3
|
+
*
|
|
4
|
+
* The SpecForge CLI's HUMAN commands (`switch` / `doctor` / `init` / `debug` /
|
|
5
|
+
* `members` / `invitations`) talk to the mcp-handler `/cli` route — plain,
|
|
6
|
+
* guidance-free ops that return `{ data }` (which the shared transport unwraps
|
|
7
|
+
* to the raw result). This is distinct from the AGENT path: the stdio MCP
|
|
8
|
+
* server proxies the 21 agent tools to `/local` (envelopes).
|
|
9
|
+
*
|
|
10
|
+
* `/cli` lives on the same host as `/local`, on the `/cli` path — so the client
|
|
11
|
+
* is just an {@link ApiClient} pointed at the derived URL.
|
|
12
|
+
*/
|
|
13
|
+
import { ApiClient } from './api-client.js';
|
|
14
|
+
/** Derive the `/cli` REST URL from the `/local` MCP URL (same host, `/cli` path). */
|
|
15
|
+
export declare function resolveCliUrl(apiUrl: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Build a client for the CLI's human commands → mcp-handler `/cli` route.
|
|
18
|
+
*
|
|
19
|
+
* @param config - resolved config carrying the authenticated key + `/local` URL
|
|
20
|
+
*/
|
|
21
|
+
export declare function createCliClient(config: {
|
|
22
|
+
apiKey: string;
|
|
23
|
+
apiUrl: string;
|
|
24
|
+
debug?: boolean;
|
|
25
|
+
}): ApiClient;
|
|
26
|
+
//# sourceMappingURL=cli-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-client.d.ts","sourceRoot":"","sources":["../../src/client/cli-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,SAAS,CAUZ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ApiClient } from "./api-client.js";
|
|
2
|
+
function resolveCliUrl(apiUrl) {
|
|
3
|
+
return apiUrl.replace(/\/local\/?$/, "/cli");
|
|
4
|
+
}
|
|
5
|
+
function createCliClient(config) {
|
|
6
|
+
return new ApiClient(
|
|
7
|
+
{
|
|
8
|
+
apiKey: config.apiKey,
|
|
9
|
+
apiUrl: resolveCliUrl(config.apiUrl),
|
|
10
|
+
debug: config.debug ?? false
|
|
11
|
+
},
|
|
12
|
+
// M9.7: human CLI surface → `X-SpecForge-Origin: cli`.
|
|
13
|
+
{ origin: "cli" }
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
createCliClient,
|
|
18
|
+
resolveCliUrl
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=cli-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/client/cli-client.ts"],"sourcesContent":["/**\n * CLI-direct client (M9 `/cli`)\n *\n * The SpecForge CLI's HUMAN commands (`switch` / `doctor` / `init` / `debug` /\n * `members` / `invitations`) talk to the mcp-handler `/cli` route — plain,\n * guidance-free ops that return `{ data }` (which the shared transport unwraps\n * to the raw result). This is distinct from the AGENT path: the stdio MCP\n * server proxies the 21 agent tools to `/local` (envelopes).\n *\n * `/cli` lives on the same host as `/local`, on the `/cli` path — so the client\n * is just an {@link ApiClient} pointed at the derived URL.\n */\n\nimport { ApiClient } from './api-client.js';\n\n/** Derive the `/cli` REST URL from the `/local` MCP URL (same host, `/cli` path). */\nexport function resolveCliUrl(apiUrl: string): string {\n return apiUrl.replace(/\\/local\\/?$/, '/cli');\n}\n\n/**\n * Build a client for the CLI's human commands → mcp-handler `/cli` route.\n *\n * @param config - resolved config carrying the authenticated key + `/local` URL\n */\nexport function createCliClient(config: {\n apiKey: string;\n apiUrl: string;\n debug?: boolean;\n}): ApiClient {\n return new ApiClient(\n {\n apiKey: config.apiKey,\n apiUrl: resolveCliUrl(config.apiUrl),\n debug: config.debug ?? false,\n },\n // M9.7: human CLI surface → `X-SpecForge-Origin: cli`.\n { origin: 'cli' },\n );\n}\n"],"mappings":"AAaA,SAAS,iBAAiB;AAGnB,SAAS,cAAc,QAAwB;AACpD,SAAO,OAAO,QAAQ,eAAe,MAAM;AAC7C;AAOO,SAAS,gBAAgB,QAIlB;AACZ,SAAO,IAAI;AAAA,IACT;AAAA,MACE,QAAQ,OAAO;AAAA,MACf,QAAQ,cAAc,OAAO,MAAM;AAAA,MACnC,OAAO,OAAO,SAAS;AAAA,IACzB;AAAA;AAAA,IAEA,EAAE,QAAQ,MAAM;AAAA,EAClB;AACF;","names":[]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Configuration Module
|
|
3
|
+
*
|
|
4
|
+
* Loads and validates configuration from environment variables for
|
|
5
|
+
* the SpecForge MCP server.
|
|
6
|
+
*/
|
|
7
|
+
import type { OutputFormat } from '../cli/config/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* MCP Server configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface McpConfig {
|
|
12
|
+
/** API key for authentication (format: sf_live_*) */
|
|
13
|
+
apiKey: string;
|
|
14
|
+
/** API Gateway URL for the SpecForge backend */
|
|
15
|
+
apiUrl: string;
|
|
16
|
+
/** Enable debug logging */
|
|
17
|
+
debug: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Wire encoding for the server's responses to the agent (TOON default, JSON
|
|
20
|
+
* when configured). Resolved at the entry point from the CLI config
|
|
21
|
+
* (env > project > global > default); `createServer` falls back to `'toon'`.
|
|
22
|
+
*/
|
|
23
|
+
mcpOutputFormat?: OutputFormat;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Environment variable names
|
|
27
|
+
*/
|
|
28
|
+
export declare const ENV_VARS: {
|
|
29
|
+
readonly API_KEY: "SPECFORGE_API_KEY";
|
|
30
|
+
readonly API_URL: "SPECFORGE_API_URL";
|
|
31
|
+
readonly DEBUG: "SPECFORGE_DEBUG";
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Load configuration from environment variables
|
|
35
|
+
*
|
|
36
|
+
* @throws {Error} If required environment variables are missing
|
|
37
|
+
* @returns {McpConfig} Loaded configuration
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const config = loadConfig();
|
|
42
|
+
* console.log(config.apiUrl);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function loadConfig(): McpConfig;
|
|
46
|
+
/**
|
|
47
|
+
* Validate configuration values
|
|
48
|
+
*
|
|
49
|
+
* @param config - Configuration to validate
|
|
50
|
+
* @throws {Error} If configuration is invalid
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const config = loadConfig();
|
|
55
|
+
* validateConfig(config); // Throws if invalid
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function validateConfig(config: McpConfig): void;
|
|
59
|
+
/**
|
|
60
|
+
* Load and validate configuration in one step
|
|
61
|
+
*
|
|
62
|
+
* @throws {Error} If configuration is missing or invalid
|
|
63
|
+
* @returns {McpConfig} Validated configuration
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* const config = getConfig();
|
|
68
|
+
* // Configuration is guaranteed to be valid
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare function getConfig(): McpConfig;
|
|
72
|
+
/**
|
|
73
|
+
* Check if configuration is available without throwing
|
|
74
|
+
*
|
|
75
|
+
* @returns {boolean} True if all required environment variables are set
|
|
76
|
+
*/
|
|
77
|
+
export declare function isConfigured(): boolean;
|
|
78
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf;;;;OAIG;IACH,eAAe,CAAC,EAAE,YAAY,CAAC;CAChC;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;CAIX,CAAC;AAOX;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,IAAI,SAAS,CAqBtC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAuCtD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,IAAI,SAAS,CAIrC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { CHANNEL } from "../channel.js";
|
|
2
|
+
const ENV_VARS = {
|
|
3
|
+
API_KEY: "SPECFORGE_API_KEY",
|
|
4
|
+
API_URL: "SPECFORGE_API_URL",
|
|
5
|
+
// Optional - for self-hosted deployments
|
|
6
|
+
DEBUG: "SPECFORGE_DEBUG"
|
|
7
|
+
};
|
|
8
|
+
const API_KEY_PREFIX = "sf_live_";
|
|
9
|
+
function loadConfig() {
|
|
10
|
+
const apiKey = process.env[ENV_VARS.API_KEY];
|
|
11
|
+
const apiUrl = process.env[ENV_VARS.API_URL] || CHANNEL.endpoint.primary;
|
|
12
|
+
const debug = process.env[ENV_VARS.DEBUG] === "true";
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`${ENV_VARS.API_KEY} environment variable is required.
|
|
16
|
+
Get your API key from the SpecForge webapp:
|
|
17
|
+
1. Go to Settings > API Keys
|
|
18
|
+
2. Create a new API key
|
|
19
|
+
3. Copy the key (starts with sf_live_)
|
|
20
|
+
4. Set: export ${ENV_VARS.API_KEY}="your_key"`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
apiKey,
|
|
25
|
+
apiUrl,
|
|
26
|
+
debug
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function validateConfig(config) {
|
|
30
|
+
if (!config.apiKey.startsWith(API_KEY_PREFIX)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Invalid API key format.
|
|
33
|
+
Expected: Key starting with "${API_KEY_PREFIX}"
|
|
34
|
+
Got: "${config.apiKey.substring(0, 10)}..."
|
|
35
|
+
|
|
36
|
+
Make sure you copied the full API key from the SpecForge webapp.`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (config.apiKey.length < API_KEY_PREFIX.length + 20) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Invalid API key length.
|
|
42
|
+
API keys should be at least 28 characters long.
|
|
43
|
+
Make sure you copied the full key.`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const url = new URL(config.apiUrl);
|
|
48
|
+
if (url.protocol !== "https:" && !config.debug) {
|
|
49
|
+
console.warn(
|
|
50
|
+
`Warning: API URL is not using HTTPS: ${config.apiUrl}
|
|
51
|
+
This is allowed in debug mode but not recommended for production.`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Invalid API URL format.
|
|
57
|
+
Expected: Valid URL (e.g., https://api.example.com/prod)
|
|
58
|
+
Got: "${config.apiUrl}"
|
|
59
|
+
|
|
60
|
+
Make sure you copied the full URL including the protocol.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function getConfig() {
|
|
65
|
+
const config = loadConfig();
|
|
66
|
+
validateConfig(config);
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
function isConfigured() {
|
|
70
|
+
return !!process.env[ENV_VARS.API_KEY];
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
ENV_VARS,
|
|
74
|
+
getConfig,
|
|
75
|
+
isConfigured,
|
|
76
|
+
loadConfig,
|
|
77
|
+
validateConfig
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/config/index.ts"],"sourcesContent":["/**\n * MCP Configuration Module\n *\n * Loads and validates configuration from environment variables for\n * the SpecForge MCP server.\n */\n\nimport { CHANNEL } from '../channel.js';\nimport type { OutputFormat } from '../cli/config/types.js';\n\n/**\n * MCP Server configuration\n */\nexport interface McpConfig {\n /** API key for authentication (format: sf_live_*) */\n apiKey: string;\n /** API Gateway URL for the SpecForge backend */\n apiUrl: string;\n /** Enable debug logging */\n debug: boolean;\n /**\n * Wire encoding for the server's responses to the agent (TOON default, JSON\n * when configured). Resolved at the entry point from the CLI config\n * (env > project > global > default); `createServer` falls back to `'toon'`.\n */\n mcpOutputFormat?: OutputFormat;\n}\n\n/**\n * Environment variable names\n */\nexport const ENV_VARS = {\n API_KEY: 'SPECFORGE_API_KEY',\n API_URL: 'SPECFORGE_API_URL', // Optional - for self-hosted deployments\n DEBUG: 'SPECFORGE_DEBUG',\n} as const;\n\n/**\n * API key prefix for validation\n */\nconst API_KEY_PREFIX = 'sf_live_';\n\n/**\n * Load configuration from environment variables\n *\n * @throws {Error} If required environment variables are missing\n * @returns {McpConfig} Loaded configuration\n *\n * @example\n * ```typescript\n * const config = loadConfig();\n * console.log(config.apiUrl);\n * ```\n */\nexport function loadConfig(): McpConfig {\n const apiKey = process.env[ENV_VARS.API_KEY];\n const apiUrl = process.env[ENV_VARS.API_URL] || CHANNEL.endpoint.primary;\n const debug = process.env[ENV_VARS.DEBUG] === 'true';\n\n if (!apiKey) {\n throw new Error(\n `${ENV_VARS.API_KEY} environment variable is required.\\n` +\n 'Get your API key from the SpecForge webapp:\\n' +\n ' 1. Go to Settings > API Keys\\n' +\n ' 2. Create a new API key\\n' +\n ' 3. Copy the key (starts with sf_live_)\\n' +\n ` 4. Set: export ${ENV_VARS.API_KEY}=\"your_key\"`\n );\n }\n\n return {\n apiKey,\n apiUrl,\n debug,\n };\n}\n\n/**\n * Validate configuration values\n *\n * @param config - Configuration to validate\n * @throws {Error} If configuration is invalid\n *\n * @example\n * ```typescript\n * const config = loadConfig();\n * validateConfig(config); // Throws if invalid\n * ```\n */\nexport function validateConfig(config: McpConfig): void {\n // Validate API key format\n if (!config.apiKey.startsWith(API_KEY_PREFIX)) {\n throw new Error(\n `Invalid API key format.\\n` +\n ` Expected: Key starting with \"${API_KEY_PREFIX}\"\\n` +\n ` Got: \"${config.apiKey.substring(0, 10)}...\"\\n` +\n '\\nMake sure you copied the full API key from the SpecForge webapp.'\n );\n }\n\n // Validate API key minimum length (prefix + at least 20 chars)\n if (config.apiKey.length < API_KEY_PREFIX.length + 20) {\n throw new Error(\n `Invalid API key length.\\n` +\n ' API keys should be at least 28 characters long.\\n' +\n ' Make sure you copied the full key.'\n );\n }\n\n // Validate API URL format\n try {\n const url = new URL(config.apiUrl);\n\n // Must be HTTPS in production\n if (url.protocol !== 'https:' && !config.debug) {\n console.warn(\n `Warning: API URL is not using HTTPS: ${config.apiUrl}\\n` +\n ' This is allowed in debug mode but not recommended for production.'\n );\n }\n } catch {\n throw new Error(\n `Invalid API URL format.\\n` +\n ` Expected: Valid URL (e.g., https://api.example.com/prod)\\n` +\n ` Got: \"${config.apiUrl}\"\\n` +\n '\\nMake sure you copied the full URL including the protocol.'\n );\n }\n}\n\n/**\n * Load and validate configuration in one step\n *\n * @throws {Error} If configuration is missing or invalid\n * @returns {McpConfig} Validated configuration\n *\n * @example\n * ```typescript\n * const config = getConfig();\n * // Configuration is guaranteed to be valid\n * ```\n */\nexport function getConfig(): McpConfig {\n const config = loadConfig();\n validateConfig(config);\n return config;\n}\n\n/**\n * Check if configuration is available without throwing\n *\n * @returns {boolean} True if all required environment variables are set\n */\nexport function isConfigured(): boolean {\n return !!process.env[ENV_VARS.API_KEY];\n}\n"],"mappings":"AAOA,SAAS,eAAe;AAwBjB,MAAM,WAAW;AAAA,EACtB,SAAS;AAAA,EACT,SAAS;AAAA;AAAA,EACT,OAAO;AACT;AAKA,MAAM,iBAAiB;AAchB,SAAS,aAAwB;AACtC,QAAM,SAAS,QAAQ,IAAI,SAAS,OAAO;AAC3C,QAAM,SAAS,QAAQ,IAAI,SAAS,OAAO,KAAK,QAAQ,SAAS;AACjE,QAAM,QAAQ,QAAQ,IAAI,SAAS,KAAK,MAAM;AAE9C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,GAAG,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKG,SAAS,OAAO;AAAA,IACxC;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAcO,SAAS,eAAe,QAAyB;AAEtD,MAAI,CAAC,OAAO,OAAO,WAAW,cAAc,GAAG;AAC7C,UAAM,IAAI;AAAA,MACR;AAAA,iCACoC,cAAc;AAAA,UACrC,OAAO,OAAO,UAAU,GAAG,EAAE,CAAC;AAAA;AAAA;AAAA,IAE7C;AAAA,EACF;AAGA,MAAI,OAAO,OAAO,SAAS,eAAe,SAAS,IAAI;AACrD,UAAM,IAAI;AAAA,MACR;AAAA;AAAA;AAAA,IAGF;AAAA,EACF;AAGA,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,OAAO,MAAM;AAGjC,QAAI,IAAI,aAAa,YAAY,CAAC,OAAO,OAAO;AAC9C,cAAQ;AAAA,QACN,wCAAwC,OAAO,MAAM;AAAA;AAAA,MAEvD;AAAA,IACF;AAAA,EACF,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA;AAAA,UAEa,OAAO,MAAM;AAAA;AAAA;AAAA,IAE5B;AAAA,EACF;AACF;AAcO,SAAS,YAAuB;AACrC,QAAM,SAAS,WAAW;AAC1B,iBAAe,MAAM;AACrB,SAAO;AACT;AAOO,SAAS,eAAwB;AACtC,SAAO,CAAC,CAAC,QAAQ,IAAI,SAAS,OAAO;AACvC;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpecForge MCP Server
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for the SpecForge MCP (Model Context Protocol) server.
|
|
5
|
+
* It provides tools for AI agents to interact with SpecForge projects, specifications,
|
|
6
|
+
* epics, and tickets.
|
|
7
|
+
*
|
|
8
|
+
* @module @specforge/cli
|
|
9
|
+
*/
|
|
10
|
+
export { McpConfig, loadConfig, validateConfig, getConfig, isConfigured, ENV_VARS } from './config/index.js';
|
|
11
|
+
export { ApiClient, ApiResponse, ApiKeyValidationResult, ApiRequestOptions } from './client/api-client.js';
|
|
12
|
+
export { createServer, startServer } from './server.js';
|
|
13
|
+
export { getTools, handleToolCall, handleToolCallSafe, Tool } from './tools/index.js';
|
|
14
|
+
export { ValidationError, ApiError, validateToolArgs, formatMCPError, transformError, MCPErrorResponse, } from './validation/index.js';
|
|
15
|
+
export * from './types/index.js';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG7G,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG3G,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGxD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGtF,OAAO,EACL,eAAe,EACf,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAG/B,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { McpConfig, loadConfig, validateConfig, getConfig, isConfigured, ENV_VARS } from "./config/index.js";
|
|
2
|
+
import { ApiClient, ApiResponse, ApiKeyValidationResult, ApiRequestOptions } from "./client/api-client.js";
|
|
3
|
+
import { createServer, startServer } from "./server.js";
|
|
4
|
+
import { getTools, handleToolCall, handleToolCallSafe, Tool } from "./tools/index.js";
|
|
5
|
+
import {
|
|
6
|
+
ValidationError,
|
|
7
|
+
ApiError,
|
|
8
|
+
validateToolArgs,
|
|
9
|
+
formatMCPError,
|
|
10
|
+
transformError,
|
|
11
|
+
MCPErrorResponse
|
|
12
|
+
} from "./validation/index.js";
|
|
13
|
+
export * from "./types/index.js";
|
|
14
|
+
async function main() {
|
|
15
|
+
console.error("[INFO] SpecForge MCP server initializing...");
|
|
16
|
+
console.error("[INFO] Node version:", process.version);
|
|
17
|
+
console.error("[INFO] argv[1]:", process.argv[1]);
|
|
18
|
+
const { loadConfig: loadConfig2, validateConfig: validateConfig2 } = await import("./config/index.js");
|
|
19
|
+
const { createServer: createServer2, startServer: startServer2 } = await import("./server.js");
|
|
20
|
+
const { resolveConfig } = await import("./cli/config/index.js");
|
|
21
|
+
try {
|
|
22
|
+
console.error("[INFO] Loading configuration...");
|
|
23
|
+
const config = loadConfig2();
|
|
24
|
+
validateConfig2(config);
|
|
25
|
+
config.mcpOutputFormat = resolveConfig().mcpOutputFormat;
|
|
26
|
+
console.error("[INFO] Configuration loaded:", {
|
|
27
|
+
apiUrl: config.apiUrl,
|
|
28
|
+
apiKey: config.apiKey.substring(0, 15) + "...",
|
|
29
|
+
debug: config.debug
|
|
30
|
+
});
|
|
31
|
+
console.error("[INFO] Creating MCP server...");
|
|
32
|
+
const server = await createServer2(config);
|
|
33
|
+
console.error("[INFO] Starting MCP server (connecting to stdio transport)...");
|
|
34
|
+
await startServer2(server);
|
|
35
|
+
console.error("[INFO] MCP server started and connected successfully");
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error("[ERROR] Failed to start MCP server:", error);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const entry = process.argv[1] ?? "";
|
|
42
|
+
const isDirectRun = entry.endsWith("index.js") || entry.endsWith("index.ts") || entry.endsWith("specforge") || entry.endsWith("specforge-canary") || entry.includes("@specforge/cli") || entry.includes("@specforge/canary-cli");
|
|
43
|
+
if (isDirectRun && !process.env.VITEST) {
|
|
44
|
+
main().catch((error) => {
|
|
45
|
+
console.error("[ERROR] Unhandled error:", error);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
ApiClient,
|
|
51
|
+
ApiError,
|
|
52
|
+
ApiKeyValidationResult,
|
|
53
|
+
ApiRequestOptions,
|
|
54
|
+
ApiResponse,
|
|
55
|
+
ENV_VARS,
|
|
56
|
+
MCPErrorResponse,
|
|
57
|
+
McpConfig,
|
|
58
|
+
Tool,
|
|
59
|
+
ValidationError,
|
|
60
|
+
createServer,
|
|
61
|
+
formatMCPError,
|
|
62
|
+
getConfig,
|
|
63
|
+
getTools,
|
|
64
|
+
handleToolCall,
|
|
65
|
+
handleToolCallSafe,
|
|
66
|
+
isConfigured,
|
|
67
|
+
loadConfig,
|
|
68
|
+
startServer,
|
|
69
|
+
transformError,
|
|
70
|
+
validateConfig,
|
|
71
|
+
validateToolArgs
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * SpecForge MCP Server\n *\n * This is the main entry point for the SpecForge MCP (Model Context Protocol) server.\n * It provides tools for AI agents to interact with SpecForge projects, specifications,\n * epics, and tickets.\n *\n * @module @specforge/cli\n */\n\n// Export configuration\nexport { McpConfig, loadConfig, validateConfig, getConfig, isConfigured, ENV_VARS } from './config/index.js';\n\n// Export API client\nexport { ApiClient, ApiResponse, ApiKeyValidationResult, ApiRequestOptions } from './client/api-client.js';\n\n// Export server\nexport { createServer, startServer } from './server.js';\n\n// Export tools\nexport { getTools, handleToolCall, handleToolCallSafe, Tool } from './tools/index.js';\n\n// Export validation\nexport {\n ValidationError,\n ApiError,\n validateToolArgs,\n formatMCPError,\n transformError,\n MCPErrorResponse,\n} from './validation/index.js';\n\n// Export types\nexport * from './types/index.js';\n\n// Main entry point\nasync function main(): Promise<void> {\n // Always log startup info to stderr (MCP clients read stdout for protocol)\n console.error('[INFO] SpecForge MCP server initializing...');\n console.error('[INFO] Node version:', process.version);\n console.error('[INFO] argv[1]:', process.argv[1]);\n\n // Import dynamically to avoid circular dependency issues\n const { loadConfig, validateConfig } = await import('./config/index.js');\n const { createServer, startServer } = await import('./server.js');\n const { resolveConfig } = await import('./cli/config/index.js');\n\n try {\n console.error('[INFO] Loading configuration...');\n const config = loadConfig();\n validateConfig(config);\n // Honor the resolved MCP output format (env > project > global > default).\n config.mcpOutputFormat = resolveConfig().mcpOutputFormat;\n\n console.error('[INFO] Configuration loaded:', {\n apiUrl: config.apiUrl,\n apiKey: config.apiKey.substring(0, 15) + '...',\n debug: config.debug,\n });\n\n console.error('[INFO] Creating MCP server...');\n const server = await createServer(config);\n\n console.error('[INFO] Starting MCP server (connecting to stdio transport)...');\n await startServer(server);\n\n console.error('[INFO] MCP server started and connected successfully');\n } catch (error) {\n console.error('[ERROR] Failed to start MCP server:', error);\n process.exit(1);\n }\n}\n\n// Run if executed directly (not when imported as a library).\n// In ESM, check via process.argv: matches node on dist/src entry, the\n// specforge / specforge-canary bins, and npx invocations via the published\n// packages (@specforge/cli stable, @specforge/canary-cli canary).\nconst entry = process.argv[1] ?? '';\nconst isDirectRun = entry.endsWith('index.js') ||\n entry.endsWith('index.ts') ||\n entry.endsWith('specforge') ||\n entry.endsWith('specforge-canary') ||\n entry.includes('@specforge/cli') ||\n entry.includes('@specforge/canary-cli');\n\nif (isDirectRun && !process.env.VITEST) {\n main().catch((error) => {\n console.error('[ERROR] Unhandled error:', error);\n process.exit(1);\n });\n}\n"],"mappings":"AAWA,SAAS,WAAW,YAAY,gBAAgB,WAAW,cAAc,gBAAgB;AAGzF,SAAS,WAAW,aAAa,wBAAwB,yBAAyB;AAGlF,SAAS,cAAc,mBAAmB;AAG1C,SAAS,UAAU,gBAAgB,oBAAoB,YAAY;AAGnE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,cAAc;AAGd,eAAe,OAAsB;AAEnC,UAAQ,MAAM,6CAA6C;AAC3D,UAAQ,MAAM,wBAAwB,QAAQ,OAAO;AACrD,UAAQ,MAAM,mBAAmB,QAAQ,KAAK,CAAC,CAAC;AAGhD,QAAM,EAAE,YAAAA,aAAY,gBAAAC,gBAAe,IAAI,MAAM,OAAO,mBAAmB;AACvE,QAAM,EAAE,cAAAC,eAAc,aAAAC,aAAY,IAAI,MAAM,OAAO,aAAa;AAChE,QAAM,EAAE,cAAc,IAAI,MAAM,OAAO,uBAAuB;AAE9D,MAAI;AACF,YAAQ,MAAM,iCAAiC;AAC/C,UAAM,SAASH,YAAW;AAC1B,IAAAC,gBAAe,MAAM;AAErB,WAAO,kBAAkB,cAAc,EAAE;AAEzC,YAAQ,MAAM,gCAAgC;AAAA,MAC5C,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO,OAAO,UAAU,GAAG,EAAE,IAAI;AAAA,MACzC,OAAO,OAAO;AAAA,IAChB,CAAC;AAED,YAAQ,MAAM,+BAA+B;AAC7C,UAAM,SAAS,MAAMC,cAAa,MAAM;AAExC,YAAQ,MAAM,+DAA+D;AAC7E,UAAMC,aAAY,MAAM;AAExB,YAAQ,MAAM,sDAAsD;AAAA,EACtE,SAAS,OAAO;AACd,YAAQ,MAAM,uCAAuC,KAAK;AAC1D,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAMA,MAAM,QAAQ,QAAQ,KAAK,CAAC,KAAK;AACjC,MAAM,cAAc,MAAM,SAAS,UAAU,KACzB,MAAM,SAAS,UAAU,KACzB,MAAM,SAAS,WAAW,KAC1B,MAAM,SAAS,kBAAkB,KACjC,MAAM,SAAS,gBAAgB,KAC/B,MAAM,SAAS,uBAAuB;AAE1D,IAAI,eAAe,CAAC,QAAQ,IAAI,QAAQ;AACtC,OAAK,EAAE,MAAM,CAAC,UAAU;AACtB,YAAQ,MAAM,4BAA4B,KAAK;AAC/C,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;","names":["loadConfig","validateConfig","createServer","startServer"]}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format Utility Module
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for formatting response data as JSON or TOON format.
|
|
5
|
+
* TOON (Token Optimized Object Notation) is a human-readable format that
|
|
6
|
+
* uses fewer tokens than JSON while maintaining semantic clarity.
|
|
7
|
+
*
|
|
8
|
+
* @see MCI-014-03-format-utility-module
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Supported output formats for response data
|
|
12
|
+
*/
|
|
13
|
+
export type OutputFormat = 'json' | 'toon';
|
|
14
|
+
/**
|
|
15
|
+
* Format response data as JSON or TOON.
|
|
16
|
+
*
|
|
17
|
+
* JSON format (default) returns the original data object unchanged,
|
|
18
|
+
* allowing the MCP protocol to handle JSON serialization.
|
|
19
|
+
*
|
|
20
|
+
* TOON format returns a human-readable string optimized for token efficiency.
|
|
21
|
+
*
|
|
22
|
+
* @param data - The data to format
|
|
23
|
+
* @param format - Output format ('json' or 'toon'), defaults to 'json'
|
|
24
|
+
* @returns Formatted data - original object for JSON, string for TOON
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // JSON (default) - returns unchanged
|
|
29
|
+
* formatResponse({ id: "123" }) // returns { id: "123" }
|
|
30
|
+
*
|
|
31
|
+
* // TOON - returns formatted string
|
|
32
|
+
* formatResponse({ id: "123" }, 'toon')
|
|
33
|
+
* // returns "id: 123"
|
|
34
|
+
*
|
|
35
|
+
* // Complex objects
|
|
36
|
+
* formatResponse({
|
|
37
|
+
* ticket: { id: "T-001", title: "Task", status: "done" }
|
|
38
|
+
* }, 'toon')
|
|
39
|
+
* // returns:
|
|
40
|
+
* // ticket.id: T-001
|
|
41
|
+
* // ticket.title: Task
|
|
42
|
+
* // ticket.status: done
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function formatResponse<T>(data: T, format?: OutputFormat): string | T;
|
|
46
|
+
/**
|
|
47
|
+
* Encode any value as a TOON string — always returns a string (M9 §2.6d.2).
|
|
48
|
+
*
|
|
49
|
+
* Unlike {@link formatResponse} (which returns the original object for the
|
|
50
|
+
* `'json'` branch), `formatToon` is the canonical wire encoder for the local
|
|
51
|
+
* MCP server: agent-bound responses over stdio are ALWAYS TOON. On encoder
|
|
52
|
+
* failure it logs once and falls back to pretty JSON — failing to encode a
|
|
53
|
+
* response is worse than slightly larger output, so it never throws.
|
|
54
|
+
*
|
|
55
|
+
* @param value - The value to encode (success envelope, error envelope, or stub)
|
|
56
|
+
* @returns A TOON string (or JSON string on encoder exception)
|
|
57
|
+
*/
|
|
58
|
+
export declare function formatToon<T>(value: T): string;
|
|
59
|
+
/**
|
|
60
|
+
* Format an array of items with optional TOON tabular formatting
|
|
61
|
+
*
|
|
62
|
+
* For TOON format, arrays with homogeneous objects can be rendered
|
|
63
|
+
* in a more compact tabular format. This is useful for lists of
|
|
64
|
+
* tickets, epics, etc.
|
|
65
|
+
*
|
|
66
|
+
* @param items - Array of items to format
|
|
67
|
+
* @param format - Output format
|
|
68
|
+
* @returns Formatted items
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* formatArrayResponse([
|
|
73
|
+
* { id: '1', status: 'done' },
|
|
74
|
+
* { id: '2', status: 'pending' }
|
|
75
|
+
* ], 'toon')
|
|
76
|
+
* // Returns:
|
|
77
|
+
* // []:
|
|
78
|
+
* // - id: 1
|
|
79
|
+
* // status: done
|
|
80
|
+
* // - id: 2
|
|
81
|
+
* // status: pending
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function formatArrayResponse<T>(items: T[], format?: OutputFormat): string | T[];
|
|
85
|
+
/**
|
|
86
|
+
* Format an error response with consistent structure
|
|
87
|
+
*
|
|
88
|
+
* @param error - Error message or Error object
|
|
89
|
+
* @param code - Optional error code
|
|
90
|
+
* @param format - Output format
|
|
91
|
+
* @returns Formatted error response
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* formatErrorResponse('Not found', 'NOT_FOUND', 'toon')
|
|
96
|
+
* // Returns:
|
|
97
|
+
* // error: Not found
|
|
98
|
+
* // code: NOT_FOUND
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function formatErrorResponse(error: string | Error, code?: string, format?: OutputFormat): string | {
|
|
102
|
+
error: string;
|
|
103
|
+
code?: string;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Format a success response with optional data
|
|
107
|
+
*
|
|
108
|
+
* @param message - Success message
|
|
109
|
+
* @param data - Optional additional data
|
|
110
|
+
* @param format - Output format
|
|
111
|
+
* @returns Formatted success response
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* formatSuccessResponse('Created', { id: '123' }, 'toon')
|
|
116
|
+
* // Returns:
|
|
117
|
+
* // success: true
|
|
118
|
+
* // message: Created
|
|
119
|
+
* // id: 123
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare function formatSuccessResponse<T extends Record<string, unknown>>(message: string, data?: T, format?: OutputFormat): string | {
|
|
123
|
+
success: true;
|
|
124
|
+
message: string;
|
|
125
|
+
} & T;
|
|
126
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAmE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,GAAE,YAAqB,GAAG,MAAM,GAAG,CAAC,CAcpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAW9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,GAAE,YAAqB,GAAG,MAAM,GAAG,CAAC,EAAE,CAE9F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GAAG,KAAK,EACrB,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,GAAE,YAAqB,GAC5B,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAO3C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,CAAC,EACR,MAAM,GAAE,YAAqB,GAC5B,MAAM,GAAG;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,CAAC,CAGjD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { encode as toonEncode } from "@toon-format/toon";
|
|
2
|
+
const TOON_OPTIONS = {
|
|
3
|
+
indent: 2,
|
|
4
|
+
keyFolding: "safe"
|
|
5
|
+
};
|
|
6
|
+
function preprocessForToon(data) {
|
|
7
|
+
if (data === null || data === void 0) {
|
|
8
|
+
return data;
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(data)) {
|
|
11
|
+
return data.map(preprocessForToon);
|
|
12
|
+
}
|
|
13
|
+
if (typeof data === "object") {
|
|
14
|
+
const result = {};
|
|
15
|
+
for (const [key, value] of Object.entries(data)) {
|
|
16
|
+
if (value === void 0) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
result[key] = preprocessForToon(value);
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
return data;
|
|
24
|
+
}
|
|
25
|
+
function formatResponse(data, format = "json") {
|
|
26
|
+
if (format === "json") {
|
|
27
|
+
return data;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const processed = preprocessForToon(data);
|
|
31
|
+
return toonEncode(processed, TOON_OPTIONS);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error("TOON formatting failed, falling back to JSON:", error);
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function formatToon(value) {
|
|
38
|
+
try {
|
|
39
|
+
const processed = preprocessForToon(value);
|
|
40
|
+
return toonEncode(processed, TOON_OPTIONS);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.error(
|
|
43
|
+
`[toon] encode failed for value type ${typeof value}; falling back to JSON`,
|
|
44
|
+
err
|
|
45
|
+
);
|
|
46
|
+
return JSON.stringify(value, null, 2);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function formatArrayResponse(items, format = "json") {
|
|
50
|
+
return formatResponse(items, format);
|
|
51
|
+
}
|
|
52
|
+
function formatErrorResponse(error, code, format = "json") {
|
|
53
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
54
|
+
const response = { error: errorMessage };
|
|
55
|
+
if (code) {
|
|
56
|
+
response.code = code;
|
|
57
|
+
}
|
|
58
|
+
return formatResponse(response, format);
|
|
59
|
+
}
|
|
60
|
+
function formatSuccessResponse(message, data, format = "json") {
|
|
61
|
+
const response = { success: true, message, ...data };
|
|
62
|
+
return formatResponse(response, format);
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
formatArrayResponse,
|
|
66
|
+
formatErrorResponse,
|
|
67
|
+
formatResponse,
|
|
68
|
+
formatSuccessResponse,
|
|
69
|
+
formatToon
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=format.js.map
|