@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,146 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, writeFile, unlink } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { CHANNEL } from "../channel.js";
|
|
6
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 2e3;
|
|
7
|
+
const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
8
|
+
function cacheDir(opts) {
|
|
9
|
+
const base = opts.cacheBaseDir ?? os.homedir();
|
|
10
|
+
const dir = opts.configDir ?? CHANNEL.configDir;
|
|
11
|
+
return path.join(base, dir);
|
|
12
|
+
}
|
|
13
|
+
function cachePath(input, opts) {
|
|
14
|
+
return path.join(cacheDir(opts), `${input.cacheKey}.json`);
|
|
15
|
+
}
|
|
16
|
+
async function readCache(input, opts) {
|
|
17
|
+
const file = cachePath(input, opts);
|
|
18
|
+
let raw;
|
|
19
|
+
try {
|
|
20
|
+
raw = await readFile(file, "utf-8");
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(raw);
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
if (typeof parsed?.url !== "string" || typeof parsed?.timestamp !== "number") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const ttl = opts.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
34
|
+
if (Date.now() - parsed.timestamp > ttl) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (parsed.url !== input.primary && parsed.url !== input.fallback) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return parsed.url;
|
|
41
|
+
}
|
|
42
|
+
async function writeCache(input, url, opts) {
|
|
43
|
+
const dir = cacheDir(opts);
|
|
44
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
45
|
+
const entry = { url, timestamp: Date.now() };
|
|
46
|
+
await writeFile(cachePath(input, opts), JSON.stringify(entry), { mode: 384 });
|
|
47
|
+
}
|
|
48
|
+
function isDnsClassError(err) {
|
|
49
|
+
if (!err) return false;
|
|
50
|
+
if (err instanceof Error) {
|
|
51
|
+
if (err.name === "AbortError") return true;
|
|
52
|
+
const msg = err.message;
|
|
53
|
+
if (msg.includes("ENOTFOUND")) return true;
|
|
54
|
+
if (msg.includes("ENETUNREACH")) return true;
|
|
55
|
+
if (msg.includes("EAI_AGAIN")) return true;
|
|
56
|
+
if (msg.includes("ECONNREFUSED")) return true;
|
|
57
|
+
if (msg.includes("CERT_") || msg.includes("TLS") || msg.includes("handshake")) return true;
|
|
58
|
+
const cause = err.cause;
|
|
59
|
+
if (cause) return isDnsClassError(cause);
|
|
60
|
+
}
|
|
61
|
+
const code = err.code;
|
|
62
|
+
if (typeof code === "string") {
|
|
63
|
+
if (code === "ENOTFOUND" || code === "ENETUNREACH" || code === "EAI_AGAIN" || code === "ECONNREFUSED") {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
if (code.startsWith("ERR_TLS_") || code.startsWith("CERT_")) return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
async function probeOnce(url, opts) {
|
|
71
|
+
const timeoutMs = opts.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
74
|
+
try {
|
|
75
|
+
const probe = opts.probe ?? ((u, signal) => fetch(u, { method: "GET", signal }));
|
|
76
|
+
const res = await probe(url, controller.signal);
|
|
77
|
+
return { ok: res.ok, status: res.status };
|
|
78
|
+
} catch (err) {
|
|
79
|
+
return { ok: false, error: err };
|
|
80
|
+
} finally {
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function resolveEndpoint(input, opts = {}) {
|
|
85
|
+
const cached = await readCache(input, opts);
|
|
86
|
+
if (cached) return cached;
|
|
87
|
+
const primaryRes = await probeOnce(input.healthPrimary, opts);
|
|
88
|
+
if (primaryRes.ok) {
|
|
89
|
+
await writeCache(input, input.primary, opts);
|
|
90
|
+
return input.primary;
|
|
91
|
+
}
|
|
92
|
+
if (!isDnsClassError(primaryRes.error)) {
|
|
93
|
+
if (primaryRes.status) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Endpoint probe of ${input.healthPrimary} returned ${primaryRes.status}; backend reachable but unhealthy. Not falling over.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Endpoint probe of ${input.healthPrimary} failed but the backend was reachable (${describeProbe(primaryRes)}). Not falling over.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const fallbackRes = await probeOnce(input.healthFallback, opts);
|
|
103
|
+
if (fallbackRes.ok) {
|
|
104
|
+
await writeCache(input, input.fallback, opts);
|
|
105
|
+
return input.fallback;
|
|
106
|
+
}
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Both endpoint probes failed.
|
|
109
|
+
primary ${input.healthPrimary}: ${describeProbe(primaryRes)}
|
|
110
|
+
fallback ${input.healthFallback}: ${describeProbe(fallbackRes)}
|
|
111
|
+
Check your network connection.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
async function invalidate(cacheKey, opts = {}) {
|
|
115
|
+
const file = path.join(cacheDir(opts), `${cacheKey}.json`);
|
|
116
|
+
try {
|
|
117
|
+
await unlink(file);
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function describeProbe(res) {
|
|
122
|
+
if (res.error) {
|
|
123
|
+
const err = res.error;
|
|
124
|
+
if (err instanceof Error) {
|
|
125
|
+
const cause = err.cause;
|
|
126
|
+
const causeMsg = cause instanceof Error ? ` (cause: ${cause.message})` : "";
|
|
127
|
+
return `${err.name}: ${err.message}${causeMsg}`;
|
|
128
|
+
}
|
|
129
|
+
return String(err);
|
|
130
|
+
}
|
|
131
|
+
return `HTTP ${res.status ?? "?"}`;
|
|
132
|
+
}
|
|
133
|
+
function shouldInvalidateOnStatus(status) {
|
|
134
|
+
return status === 401 || status === 403;
|
|
135
|
+
}
|
|
136
|
+
import { CHANNEL as CHANNEL2 } from "../channel.js";
|
|
137
|
+
void fs;
|
|
138
|
+
export {
|
|
139
|
+
CHANNEL2 as CHANNEL,
|
|
140
|
+
DEFAULT_CACHE_TTL_MS,
|
|
141
|
+
DEFAULT_PROBE_TIMEOUT_MS,
|
|
142
|
+
invalidate,
|
|
143
|
+
resolveEndpoint,
|
|
144
|
+
shouldInvalidateOnStatus
|
|
145
|
+
};
|
|
146
|
+
//# sourceMappingURL=resolve-endpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/transport/resolve-endpoint.ts"],"sourcesContent":["/**\n * Endpoint resolver — picks `primary` or `fallback` per the canary-channel\n * client probe contract (PLAN-canary-channel.md §D).\n *\n * Cache file: `${homedir}/${CHANNEL.configDir}/${cacheKey}.json`. TTL: 5 min.\n *\n * Probe: GET `healthPrimary` with a 2 s timeout. On success → cache `primary`\n * and return it. On a DNS-class failure (`ENOTFOUND`, `ENETUNREACH`, TLS\n * handshake, `AbortError` from the timeout) → probe `healthFallback`. On\n * success → cache `fallback` and return it. Both fail → throw a clear\n * network error.\n *\n * `5xx`, `429`, `401`, `403` from the real call do NOT trigger fallover —\n * those mean the backend is reachable but rejecting. Callers handle them\n * directly and may invalidate the cache via `invalidate(cacheKey)` on\n * auth-class failures so the next call re-probes.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { mkdir, readFile, writeFile, unlink } from 'node:fs/promises';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { CHANNEL } from '../channel.js';\n\nexport interface ResolveEndpointInput {\n /** Primary URL — production target on stable, dev target on canary. */\n primary: string;\n /** Fallback URL — used on DNS-class failure of primary. */\n fallback: string;\n /** `GET`-able probe target on the primary surface. */\n healthPrimary: string;\n /** `GET`-able probe target on the fallback surface. */\n healthFallback: string;\n /** Filename stem for the cache file under `~/${CHANNEL.configDir}/`. */\n cacheKey: string;\n}\n\nexport interface ResolveEndpointOptions {\n /** Override the per-call probe timeout (ms). Default 2000. */\n probeTimeoutMs?: number;\n /** Override the cache TTL (ms). Default 5 minutes. */\n cacheTtlMs?: number;\n /** Override the cache directory (testing). Default `os.homedir()`. */\n cacheBaseDir?: string;\n /** Override the channel config dir (testing). Default `CHANNEL.configDir`. */\n configDir?: string;\n /** Inject a custom probe function (testing). Default uses `fetch`. */\n probe?: (url: string, signal: AbortSignal) => Promise<Response>;\n}\n\nexport const DEFAULT_PROBE_TIMEOUT_MS = 2_000;\nexport const DEFAULT_CACHE_TTL_MS = 5 * 60 * 1_000;\n\ninterface CacheEntry {\n url: string;\n /** Epoch ms when the cache entry was written. */\n timestamp: number;\n}\n\nfunction cacheDir(opts: ResolveEndpointOptions): string {\n const base = opts.cacheBaseDir ?? os.homedir();\n const dir = opts.configDir ?? CHANNEL.configDir;\n return path.join(base, dir);\n}\n\nfunction cachePath(input: ResolveEndpointInput, opts: ResolveEndpointOptions): string {\n return path.join(cacheDir(opts), `${input.cacheKey}.json`);\n}\n\nasync function readCache(input: ResolveEndpointInput, opts: ResolveEndpointOptions): Promise<string | null> {\n const file = cachePath(input, opts);\n let raw: string;\n try {\n raw = await readFile(file, 'utf-8');\n } catch {\n return null;\n }\n let parsed: CacheEntry;\n try {\n parsed = JSON.parse(raw) as CacheEntry;\n } catch {\n return null;\n }\n if (typeof parsed?.url !== 'string' || typeof parsed?.timestamp !== 'number') {\n return null;\n }\n const ttl = opts.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;\n if (Date.now() - parsed.timestamp > ttl) {\n return null;\n }\n // Only honor cache entries that match one of the current spec's URLs;\n // otherwise the channel changed underneath us.\n if (parsed.url !== input.primary && parsed.url !== input.fallback) {\n return null;\n }\n return parsed.url;\n}\n\nasync function writeCache(\n input: ResolveEndpointInput,\n url: string,\n opts: ResolveEndpointOptions,\n): Promise<void> {\n const dir = cacheDir(opts);\n await mkdir(dir, { recursive: true, mode: 0o700 });\n const entry: CacheEntry = { url, timestamp: Date.now() };\n await writeFile(cachePath(input, opts), JSON.stringify(entry), { mode: 0o600 });\n}\n\n/**\n * DNS-class failure: errors that mean the request never reached a backend.\n * Treat as \"primary unreachable, try fallback\". Errors that DO reach a\n * backend (TLS-validated 5xx/4xx, ECONNRESET mid-stream) are NOT DNS-class.\n */\nfunction isDnsClassError(err: unknown): boolean {\n if (!err) return false;\n if (err instanceof Error) {\n if (err.name === 'AbortError') return true;\n const msg = err.message;\n if (msg.includes('ENOTFOUND')) return true;\n if (msg.includes('ENETUNREACH')) return true;\n if (msg.includes('EAI_AGAIN')) return true;\n if (msg.includes('ECONNREFUSED')) return true;\n // TLS handshake failures are DNS-class for our purposes (fallback is\n // a different cert / different ALB which may be healthy).\n if (msg.includes('CERT_') || msg.includes('TLS') || msg.includes('handshake')) return true;\n // node fetch wraps DNS errors as \"fetch failed\" with a `cause` property.\n const cause = (err as Error & { cause?: unknown }).cause;\n if (cause) return isDnsClassError(cause);\n }\n // System errors carry `code` directly.\n const code = (err as { code?: unknown }).code;\n if (typeof code === 'string') {\n if (code === 'ENOTFOUND' || code === 'ENETUNREACH' || code === 'EAI_AGAIN' || code === 'ECONNREFUSED') {\n return true;\n }\n if (code.startsWith('ERR_TLS_') || code.startsWith('CERT_')) return true;\n }\n return false;\n}\n\nasync function probeOnce(\n url: string,\n opts: ResolveEndpointOptions,\n): Promise<{ ok: boolean; status?: number; error?: unknown }> {\n const timeoutMs = opts.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const probe = opts.probe ?? ((u, signal) => fetch(u, { method: 'GET', signal }));\n const res = await probe(url, controller.signal);\n return { ok: res.ok, status: res.status };\n } catch (err) {\n return { ok: false, error: err };\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Resolve a usable endpoint URL by checking the cache, then probing.\n * Throws when both probes fail.\n */\nexport async function resolveEndpoint(\n input: ResolveEndpointInput,\n opts: ResolveEndpointOptions = {},\n): Promise<string> {\n const cached = await readCache(input, opts);\n if (cached) return cached;\n\n // Probe primary first.\n const primaryRes = await probeOnce(input.healthPrimary, opts);\n if (primaryRes.ok) {\n await writeCache(input, input.primary, opts);\n return input.primary;\n }\n\n // Anything that ISN'T DNS-class means primary is reachable — either an\n // HTTP response (4xx/5xx) or a post-connect error (ECONNRESET, etc.).\n // Bubble up; do not fall over.\n if (!isDnsClassError(primaryRes.error)) {\n if (primaryRes.status) {\n throw new Error(\n `Endpoint probe of ${input.healthPrimary} returned ${primaryRes.status}; ` +\n `backend reachable but unhealthy. Not falling over.`,\n );\n }\n throw new Error(\n `Endpoint probe of ${input.healthPrimary} failed but the backend was ` +\n `reachable (${describeProbe(primaryRes)}). Not falling over.`,\n );\n }\n\n // DNS-class failure on primary → probe fallback.\n const fallbackRes = await probeOnce(input.healthFallback, opts);\n if (fallbackRes.ok) {\n await writeCache(input, input.fallback, opts);\n return input.fallback;\n }\n\n throw new Error(\n `Both endpoint probes failed.\\n` +\n ` primary ${input.healthPrimary}: ${describeProbe(primaryRes)}\\n` +\n ` fallback ${input.healthFallback}: ${describeProbe(fallbackRes)}\\n` +\n `Check your network connection.`,\n );\n}\n\n/** Wipe the cache for a given key — call on auth-class failures. */\nexport async function invalidate(cacheKey: string, opts: ResolveEndpointOptions = {}): Promise<void> {\n const file = path.join(cacheDir(opts), `${cacheKey}.json`);\n try {\n await unlink(file);\n } catch {\n // No cache file → already invalidated.\n }\n}\n\nfunction describeProbe(res: { ok: boolean; status?: number; error?: unknown }): string {\n if (res.error) {\n const err = res.error;\n if (err instanceof Error) {\n const cause = (err as Error & { cause?: unknown }).cause;\n const causeMsg = cause instanceof Error ? ` (cause: ${cause.message})` : '';\n return `${err.name}: ${err.message}${causeMsg}`;\n }\n return String(err);\n }\n return `HTTP ${res.status ?? '?'}`;\n}\n\n/**\n * Helper: should the caller invalidate the cache after this status?\n * 401/403 from a real call are auth-class; tomorrow's call should re-probe\n * in case the operator pointed the channel elsewhere.\n */\nexport function shouldInvalidateOnStatus(status: number): boolean {\n return status === 401 || status === 403;\n}\n\n// Re-export for convenience so callers get all symbols from one module.\nexport { CHANNEL } from '../channel.js';\n\n// Suppress lint about unused promises import (kept for future use in tests).\nvoid fs;\n"],"mappings":"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","names":["CHANNEL"]}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpecForge MCP Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Most wire-protocol types live in `@specforge/types` (the canonical source
|
|
5
|
+
* of truth). This file re-exports them under stable CLI names and defines a
|
|
6
|
+
* small set of CLI-internal shapes: work sessions, implementation context,
|
|
7
|
+
* discovery/attempt summaries, git-link records, and structured acceptance
|
|
8
|
+
* criteria with normalization helpers.
|
|
9
|
+
*/
|
|
10
|
+
export type { SpecificationStatus, EpicStatus, TicketStatus, DependencyType, } from '@specforge/types/schema';
|
|
11
|
+
import type { TicketStatus } from '@specforge/types/schema';
|
|
12
|
+
export type { Project, Specification, NamingConventions, SpecificationCodeStandards, ReturnTypes, Epic, EpicSharedPatterns, EpicCommonFiles, Ticket, TicketTechnicalDetails, TicketDependency, } from '@specforge/types/runtime';
|
|
13
|
+
import type { Specification, Epic, Ticket } from '@specforge/types/runtime';
|
|
14
|
+
export type { TicketImplementation } from '@specforge/types/schema';
|
|
15
|
+
export type { ListResponse, DependencyTicketSummary, SpecificationStatusReport, EpicStatusReport, ImplementationSummary, TimeReport, BlockersReport, TicketUpdate, BulkUpdateRequest, BulkUpdateResult, BulkCreateTicketDefinition, BulkCreateDependencyDefinition, BulkCreateTicketsRequest, BulkCreatedTicketInfo, BulkCreateTicketsResult, BulkDependencyDefinition, BulkAddDependenciesRequest, BulkAddDependenciesResult, LookupProjectRequest, LookupProjectResult, LookupSpecificationRequest, LookupSpecificationResult, LookupEpicRequest, LookupEpicResult, LookupTicketRequest, LookupTicketResult, } from '@specforge/types/api';
|
|
16
|
+
import type { DependencyTicketSummary } from '@specforge/types/api';
|
|
17
|
+
/**
|
|
18
|
+
* Structured acceptance criterion with validation state.
|
|
19
|
+
*/
|
|
20
|
+
export interface AcceptanceCriterion {
|
|
21
|
+
id: string;
|
|
22
|
+
description: string;
|
|
23
|
+
validated: boolean;
|
|
24
|
+
validatedAt?: string;
|
|
25
|
+
validatedBy?: string;
|
|
26
|
+
notes?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Normalizes acceptance criteria from legacy string[] format to structured
|
|
30
|
+
* `AcceptanceCriterion[]`. Provides backwards compatibility for old data
|
|
31
|
+
* stored as string arrays.
|
|
32
|
+
*/
|
|
33
|
+
export declare function normalizeAcceptanceCriteria(criteria: (string | AcceptanceCriterion)[]): AcceptanceCriterion[];
|
|
34
|
+
export interface RelatedTicketSummary {
|
|
35
|
+
id: string;
|
|
36
|
+
title: string;
|
|
37
|
+
status: TicketStatus;
|
|
38
|
+
sameEpic: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface TestResultSummary {
|
|
41
|
+
id: string;
|
|
42
|
+
testType?: string;
|
|
43
|
+
allPassed: boolean;
|
|
44
|
+
passed: number;
|
|
45
|
+
failed: number;
|
|
46
|
+
total: number;
|
|
47
|
+
runAt: string;
|
|
48
|
+
}
|
|
49
|
+
export interface PreviousAttempt {
|
|
50
|
+
attemptNumber: number;
|
|
51
|
+
startedAt: string;
|
|
52
|
+
endedAt?: string;
|
|
53
|
+
result: 'completed' | 'failed' | 'in_progress';
|
|
54
|
+
testResults: TestResultSummary[];
|
|
55
|
+
summary?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface DiscoverySummary {
|
|
58
|
+
id: string;
|
|
59
|
+
type: string;
|
|
60
|
+
severity: string;
|
|
61
|
+
status: string;
|
|
62
|
+
title: string;
|
|
63
|
+
description: string;
|
|
64
|
+
createdAt: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ActiveSessionSummary {
|
|
67
|
+
id: string;
|
|
68
|
+
status: string;
|
|
69
|
+
startedAt: string;
|
|
70
|
+
config: unknown;
|
|
71
|
+
ticketsCompleted: string[];
|
|
72
|
+
ticketsFailed: string[];
|
|
73
|
+
ticketsSkipped: string[];
|
|
74
|
+
totalActiveMinutes: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Implementation context returned by get_implementation_context.
|
|
78
|
+
*/
|
|
79
|
+
export interface ImplementationContext {
|
|
80
|
+
/** The ticket being implemented */
|
|
81
|
+
ticket: Ticket;
|
|
82
|
+
/** The epic containing the ticket */
|
|
83
|
+
epic: Epic;
|
|
84
|
+
/** The specification containing the epic */
|
|
85
|
+
specification: Specification;
|
|
86
|
+
/** Dependency information */
|
|
87
|
+
dependencies: {
|
|
88
|
+
/** Tickets that must be completed before this one */
|
|
89
|
+
blockedBy: DependencyTicketSummary[];
|
|
90
|
+
/** Tickets waiting on this one */
|
|
91
|
+
blocks: DependencyTicketSummary[];
|
|
92
|
+
};
|
|
93
|
+
/** Related tickets */
|
|
94
|
+
relatedTickets: RelatedTicketSummary[];
|
|
95
|
+
/** Patterns and conventions from the project */
|
|
96
|
+
patterns?: Record<string, unknown>;
|
|
97
|
+
/** Active implementation session (Phase 5) */
|
|
98
|
+
activeSession: ActiveSessionSummary | null;
|
|
99
|
+
/** Previous implementation attempts (Phase 5) */
|
|
100
|
+
previousAttempts: PreviousAttempt[];
|
|
101
|
+
/** Related discoveries (Phase 5) */
|
|
102
|
+
relatedDiscoveries: DiscoverySummary[];
|
|
103
|
+
}
|
|
104
|
+
export interface WorkSession {
|
|
105
|
+
id: string;
|
|
106
|
+
ticketId: string;
|
|
107
|
+
specificationId: string;
|
|
108
|
+
projectId: string;
|
|
109
|
+
epicId: string;
|
|
110
|
+
implementationSessionId?: string;
|
|
111
|
+
status: 'active' | 'paused' | 'completed' | 'failed';
|
|
112
|
+
startedAt: string;
|
|
113
|
+
pausedAt?: string;
|
|
114
|
+
completedAt?: string;
|
|
115
|
+
actualHours?: number;
|
|
116
|
+
filesCreated?: string[];
|
|
117
|
+
filesModified?: string[];
|
|
118
|
+
filesDeleted?: string[];
|
|
119
|
+
testsPassed?: boolean;
|
|
120
|
+
retryCount: number;
|
|
121
|
+
retryLog?: unknown;
|
|
122
|
+
actions?: unknown;
|
|
123
|
+
completionAttempts?: unknown;
|
|
124
|
+
tokensUsed?: {
|
|
125
|
+
inputTokens: number;
|
|
126
|
+
outputTokens: number;
|
|
127
|
+
};
|
|
128
|
+
costUsd?: number;
|
|
129
|
+
durationMs?: number;
|
|
130
|
+
commitSha?: string;
|
|
131
|
+
modelUsed?: string;
|
|
132
|
+
createdAt: string;
|
|
133
|
+
updatedAt: string;
|
|
134
|
+
}
|
|
135
|
+
export interface TicketAcceptanceCriterion {
|
|
136
|
+
id: string;
|
|
137
|
+
ticketId: string;
|
|
138
|
+
description: string;
|
|
139
|
+
order: number;
|
|
140
|
+
createdAt?: string;
|
|
141
|
+
updatedAt?: string;
|
|
142
|
+
}
|
|
143
|
+
export interface WorkSessionACValidation {
|
|
144
|
+
id: string;
|
|
145
|
+
workSessionId: string;
|
|
146
|
+
criterionId: string;
|
|
147
|
+
validated: boolean;
|
|
148
|
+
validatedAt?: string;
|
|
149
|
+
notes?: string;
|
|
150
|
+
createdAt?: string;
|
|
151
|
+
updatedAt?: string;
|
|
152
|
+
}
|
|
153
|
+
export interface TicketImplementationStep {
|
|
154
|
+
id: string;
|
|
155
|
+
ticketId: string;
|
|
156
|
+
text: string;
|
|
157
|
+
order: number;
|
|
158
|
+
createdAt?: string;
|
|
159
|
+
updatedAt?: string;
|
|
160
|
+
}
|
|
161
|
+
export interface WorkSessionStepCompletion {
|
|
162
|
+
id: string;
|
|
163
|
+
workSessionId: string;
|
|
164
|
+
stepId: string;
|
|
165
|
+
completed: boolean;
|
|
166
|
+
completedAt?: string;
|
|
167
|
+
notes?: string;
|
|
168
|
+
createdAt?: string;
|
|
169
|
+
updatedAt?: string;
|
|
170
|
+
}
|
|
171
|
+
export type TestType = 'unit' | 'integration' | 'e2e' | 'manual' | 'typescript' | 'lint' | 'build' | 'typecheck' | 'format' | 'security' | 'custom';
|
|
172
|
+
export interface WorkSessionTestResult {
|
|
173
|
+
id: string;
|
|
174
|
+
workSessionId: string;
|
|
175
|
+
testType: TestType;
|
|
176
|
+
passed: boolean;
|
|
177
|
+
totalTests: number;
|
|
178
|
+
passedTests: number;
|
|
179
|
+
failedTests: number;
|
|
180
|
+
skippedTests: number;
|
|
181
|
+
coverage?: number;
|
|
182
|
+
errorDetails?: string;
|
|
183
|
+
command?: string;
|
|
184
|
+
exitCode?: number;
|
|
185
|
+
durationMs?: number;
|
|
186
|
+
errorCount?: number;
|
|
187
|
+
warningCount?: number;
|
|
188
|
+
summary?: string;
|
|
189
|
+
suites?: Array<{
|
|
190
|
+
name: string;
|
|
191
|
+
passed: number;
|
|
192
|
+
failed: number;
|
|
193
|
+
skipped?: number;
|
|
194
|
+
duration?: number;
|
|
195
|
+
}>;
|
|
196
|
+
runAt: string;
|
|
197
|
+
}
|
|
198
|
+
/** @deprecated Use WorkSessionTestResult instead */
|
|
199
|
+
export type TicketTestResult = WorkSessionTestResult;
|
|
200
|
+
export type LinkType = 'commit' | 'pull_request' | 'branch';
|
|
201
|
+
export interface TicketLink {
|
|
202
|
+
id: string;
|
|
203
|
+
ticketId: string;
|
|
204
|
+
linkType: LinkType;
|
|
205
|
+
url: string;
|
|
206
|
+
sha?: string;
|
|
207
|
+
prNumber?: number;
|
|
208
|
+
title?: string;
|
|
209
|
+
status?: string;
|
|
210
|
+
createdAt: string;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function normalizeAcceptanceCriteria(criteria) {
|
|
2
|
+
return criteria.map((c, index) => {
|
|
3
|
+
if (typeof c === "string") {
|
|
4
|
+
return {
|
|
5
|
+
id: `ac-${index}-${Date.now()}`,
|
|
6
|
+
description: c,
|
|
7
|
+
validated: false
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
return c;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
normalizeAcceptanceCriteria
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types/index.ts"],"sourcesContent":["/**\n * SpecForge MCP Type Definitions\n *\n * Most wire-protocol types live in `@specforge/types` (the canonical source\n * of truth). This file re-exports them under stable CLI names and defines a\n * small set of CLI-internal shapes: work sessions, implementation context,\n * discovery/attempt summaries, git-link records, and structured acceptance\n * criteria with normalization helpers.\n */\n\n// -----------------------------------------------------------------------------\n// Schema enums and primitive types — re-exported from @specforge/types/schema\n// -----------------------------------------------------------------------------\n\nexport type {\n SpecificationStatus,\n EpicStatus,\n TicketStatus,\n DependencyType,\n} from '@specforge/types/schema';\n\nimport type {\n SpecificationStatus,\n EpicStatus,\n TicketStatus,\n} from '@specforge/types/schema';\n\n// -----------------------------------------------------------------------------\n// Runtime entity shapes — re-exported from @specforge/types/runtime\n// -----------------------------------------------------------------------------\n\nexport type {\n Project,\n Specification,\n NamingConventions,\n SpecificationCodeStandards,\n ReturnTypes,\n Epic,\n EpicSharedPatterns,\n EpicCommonFiles,\n Ticket,\n TicketTechnicalDetails,\n TicketDependency,\n} from '@specforge/types/runtime';\n\nimport type {\n Specification,\n Epic,\n Ticket,\n} from '@specforge/types/runtime';\n\nexport type { TicketImplementation } from '@specforge/types/schema';\n\n// -----------------------------------------------------------------------------\n// API wire types — re-exported from @specforge/types/api\n// -----------------------------------------------------------------------------\n\nexport type {\n ListResponse,\n DependencyTicketSummary,\n SpecificationStatusReport,\n EpicStatusReport,\n ImplementationSummary,\n TimeReport,\n BlockersReport,\n TicketUpdate,\n BulkUpdateRequest,\n BulkUpdateResult,\n BulkCreateTicketDefinition,\n BulkCreateDependencyDefinition,\n BulkCreateTicketsRequest,\n BulkCreatedTicketInfo,\n BulkCreateTicketsResult,\n BulkDependencyDefinition,\n BulkAddDependenciesRequest,\n BulkAddDependenciesResult,\n LookupProjectRequest,\n LookupProjectResult,\n LookupSpecificationRequest,\n LookupSpecificationResult,\n LookupEpicRequest,\n LookupEpicResult,\n LookupTicketRequest,\n LookupTicketResult,\n} from '@specforge/types/api';\n\nimport type { DependencyTicketSummary } from '@specforge/types/api';\n\n// =============================================================================\n// CLI-internal types below — no canonical equivalent in @specforge/types\n// =============================================================================\n\n// -----------------------------------------------------------------------------\n// Structured acceptance criteria (TS-001)\n// -----------------------------------------------------------------------------\n\n/**\n * Structured acceptance criterion with validation state.\n */\nexport interface AcceptanceCriterion {\n id: string;\n description: string;\n validated: boolean;\n validatedAt?: string;\n validatedBy?: string;\n notes?: string;\n}\n\n/**\n * Normalizes acceptance criteria from legacy string[] format to structured\n * `AcceptanceCriterion[]`. Provides backwards compatibility for old data\n * stored as string arrays.\n */\nexport function normalizeAcceptanceCriteria(\n criteria: (string | AcceptanceCriterion)[]\n): AcceptanceCriterion[] {\n return criteria.map((c, index) => {\n if (typeof c === 'string') {\n return {\n id: `ac-${index}-${Date.now()}`,\n description: c,\n validated: false,\n };\n }\n return c;\n });\n}\n\n// -----------------------------------------------------------------------------\n// Implementation context (returned by get_implementation_context)\n// -----------------------------------------------------------------------------\n\nexport interface RelatedTicketSummary {\n id: string;\n title: string;\n status: TicketStatus;\n sameEpic: boolean;\n}\n\nexport interface TestResultSummary {\n id: string;\n testType?: string;\n allPassed: boolean;\n passed: number;\n failed: number;\n total: number;\n runAt: string;\n}\n\nexport interface PreviousAttempt {\n attemptNumber: number;\n startedAt: string;\n endedAt?: string;\n result: 'completed' | 'failed' | 'in_progress';\n testResults: TestResultSummary[];\n summary?: string;\n}\n\nexport interface DiscoverySummary {\n id: string;\n type: string;\n severity: string;\n status: string;\n title: string;\n description: string;\n createdAt: string;\n}\n\nexport interface ActiveSessionSummary {\n id: string;\n status: string;\n startedAt: string;\n config: unknown;\n ticketsCompleted: string[];\n ticketsFailed: string[];\n ticketsSkipped: string[];\n totalActiveMinutes: number;\n}\n\n/**\n * Implementation context returned by get_implementation_context.\n */\nexport interface ImplementationContext {\n /** The ticket being implemented */\n ticket: Ticket;\n /** The epic containing the ticket */\n epic: Epic;\n /** The specification containing the epic */\n specification: Specification;\n /** Dependency information */\n dependencies: {\n /** Tickets that must be completed before this one */\n blockedBy: DependencyTicketSummary[];\n /** Tickets waiting on this one */\n blocks: DependencyTicketSummary[];\n };\n /** Related tickets */\n relatedTickets: RelatedTicketSummary[];\n /** Patterns and conventions from the project */\n patterns?: Record<string, unknown>;\n /** Active implementation session (Phase 5) */\n activeSession: ActiveSessionSummary | null;\n /** Previous implementation attempts (Phase 5) */\n previousAttempts: PreviousAttempt[];\n /** Related discoveries (Phase 5) */\n relatedDiscoveries: DiscoverySummary[];\n}\n\n// -----------------------------------------------------------------------------\n// Work session state (session-scoped tables)\n// -----------------------------------------------------------------------------\n\nexport interface WorkSession {\n id: string;\n ticketId: string;\n specificationId: string;\n projectId: string;\n epicId: string;\n implementationSessionId?: string;\n status: 'active' | 'paused' | 'completed' | 'failed';\n startedAt: string;\n pausedAt?: string;\n completedAt?: string;\n actualHours?: number;\n filesCreated?: string[];\n filesModified?: string[];\n filesDeleted?: string[];\n testsPassed?: boolean;\n retryCount: number;\n retryLog?: unknown;\n actions?: unknown;\n completionAttempts?: unknown;\n tokensUsed?: { inputTokens: number; outputTokens: number };\n costUsd?: number;\n durationMs?: number;\n commitSha?: string;\n modelUsed?: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface TicketAcceptanceCriterion {\n id: string;\n ticketId: string;\n description: string;\n order: number;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface WorkSessionACValidation {\n id: string;\n workSessionId: string;\n criterionId: string;\n validated: boolean;\n validatedAt?: string;\n notes?: string;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface TicketImplementationStep {\n id: string;\n ticketId: string;\n text: string;\n order: number;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface WorkSessionStepCompletion {\n id: string;\n workSessionId: string;\n stepId: string;\n completed: boolean;\n completedAt?: string;\n notes?: string;\n createdAt?: string;\n updatedAt?: string;\n}\n\n// -----------------------------------------------------------------------------\n// Test result types\n// -----------------------------------------------------------------------------\n\nexport type TestType =\n | 'unit'\n | 'integration'\n | 'e2e'\n | 'manual'\n | 'typescript'\n | 'lint'\n | 'build'\n | 'typecheck'\n | 'format'\n | 'security'\n | 'custom';\n\nexport interface WorkSessionTestResult {\n id: string;\n workSessionId: string;\n testType: TestType;\n passed: boolean;\n totalTests: number;\n passedTests: number;\n failedTests: number;\n skippedTests: number;\n coverage?: number;\n errorDetails?: string;\n command?: string;\n exitCode?: number;\n durationMs?: number;\n errorCount?: number;\n warningCount?: number;\n summary?: string;\n suites?: Array<{\n name: string;\n passed: number;\n failed: number;\n skipped?: number;\n duration?: number;\n }>;\n runAt: string;\n}\n\n/** @deprecated Use WorkSessionTestResult instead */\nexport type TicketTestResult = WorkSessionTestResult;\n\n// -----------------------------------------------------------------------------\n// Git integration\n// -----------------------------------------------------------------------------\n\nexport type LinkType = 'commit' | 'pull_request' | 'branch';\n\nexport interface TicketLink {\n id: string;\n ticketId: string;\n linkType: LinkType;\n url: string;\n sha?: string;\n prNumber?: number;\n title?: string;\n status?: string;\n createdAt: string;\n}\n\n// -----------------------------------------------------------------------------\n// Silence unused-import warnings for enum aliases referenced only by JSDoc\n// -----------------------------------------------------------------------------\n// (Each of these is re-exported above; the local `import type` pulls them\n// in so that embedded JSDoc cross-references resolve in editors.)\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\ntype _ReferencedEnums = SpecificationStatus | EpicStatus;\n"],"mappings":"AAiHO,SAAS,4BACd,UACuB;AACvB,SAAO,SAAS,IAAI,CAAC,GAAG,UAAU;AAChC,QAAI,OAAO,MAAM,UAAU;AACzB,aAAO;AAAA,QACL,IAAI,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;AAAA,QAC7B,aAAa;AAAA,QACb,WAAW;AAAA,MACb;AAAA,IACF;AACA,WAAO;AAAA,EACT,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities for MCP Tools
|
|
3
|
+
*
|
|
4
|
+
* Provides comprehensive input validation and error handling for all MCP tools.
|
|
5
|
+
* Validation is run before each tool call to ensure proper argument types and formats.
|
|
6
|
+
*/
|
|
7
|
+
export { type TicketComplexity, type WarningSeverity, type StrictnessLevel, type ValidationWarning, type TicketValidationResult, type ComplexityRules, type TicketValidationInput, validationRules, validateTicket, filterWarningsByStrictness, getComplexityRules, isValidComplexity, getComplexityDescription, suggestComplexity, } from './ticket-validation.js';
|
|
8
|
+
/**
|
|
9
|
+
* Custom error class for validation failures
|
|
10
|
+
* Includes field name and error code for structured error handling
|
|
11
|
+
*/
|
|
12
|
+
export declare class ValidationError extends Error {
|
|
13
|
+
field: string;
|
|
14
|
+
code: string;
|
|
15
|
+
constructor(message: string, field: string, code: string);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* API Error class for handling HTTP errors from the backend
|
|
19
|
+
*/
|
|
20
|
+
export declare class ApiError extends Error {
|
|
21
|
+
statusCode: number;
|
|
22
|
+
errorCode?: string | undefined;
|
|
23
|
+
constructor(message: string, statusCode: number, errorCode?: string | undefined);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validate that a value is present (not null, undefined, or empty string)
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateRequired(value: unknown, fieldName: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Validate that a value is a valid UUID format
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateUUID(value: string, fieldName: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Validate that a value is one of the allowed enum values
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateEnum(value: string, fieldName: string, allowedValues: string[]): void;
|
|
37
|
+
/**
|
|
38
|
+
* Validate that a number is within a range
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateRange(value: number, fieldName: string, min: number, max: number): void;
|
|
41
|
+
/**
|
|
42
|
+
* Validate that a value is an array, with optional item validation
|
|
43
|
+
*/
|
|
44
|
+
export declare function validateArray(value: unknown, fieldName: string, itemValidator?: (item: unknown, index: number) => void): void;
|
|
45
|
+
/**
|
|
46
|
+
* Validate a 40-character hex SHA
|
|
47
|
+
*/
|
|
48
|
+
export declare function validateSha(value: string, fieldName?: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* Validate an ISO 8601 date string
|
|
51
|
+
*/
|
|
52
|
+
export declare function validateDate(value: string, fieldName: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Validate a string length
|
|
55
|
+
*/
|
|
56
|
+
export declare function validateStringLength(value: string, fieldName: string, minLength: number, maxLength: number): void;
|
|
57
|
+
type ValidatorFn = (args: Record<string, unknown>) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Validators for each tool (22 tools)
|
|
60
|
+
* These are applied before the tool handler is called
|
|
61
|
+
*/
|
|
62
|
+
export declare const toolValidators: Record<string, ValidatorFn>;
|
|
63
|
+
/**
|
|
64
|
+
* MCP error response format
|
|
65
|
+
*/
|
|
66
|
+
export interface MCPErrorResponse {
|
|
67
|
+
content: Array<{
|
|
68
|
+
type: 'text';
|
|
69
|
+
text: string;
|
|
70
|
+
}>;
|
|
71
|
+
isError: true;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Transform an error into MCP error response format
|
|
75
|
+
*/
|
|
76
|
+
export declare function formatMCPError(error: Error): MCPErrorResponse;
|
|
77
|
+
/**
|
|
78
|
+
* Transform HTTP/network errors into ApiError
|
|
79
|
+
*/
|
|
80
|
+
export declare function transformError(error: unknown): Error;
|
|
81
|
+
/**
|
|
82
|
+
* Run validation for a specific tool
|
|
83
|
+
* @throws ValidationError if validation fails
|
|
84
|
+
*/
|
|
85
|
+
export declare function validateToolArgs(toolName: string, args: Record<string, unknown>): void;
|
|
86
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validation/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,eAAe,EACf,cAAc,EACd,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAEhC;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IAG/B,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,MAAM;gBAFnB,OAAO,EAAE,MAAM,EACR,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM;CAKtB;AAED;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAGxB,UAAU,EAAE,MAAM;IAClB,SAAS,CAAC,EAAE,MAAM;gBAFzB,OAAO,EAAE,MAAM,EACR,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,YAAA;CAK5B;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAQxE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAUnE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EAAE,GACtB,IAAI,CASN;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,IAAI,CAeN;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACrD,IAAI,CAsBN;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,GAAE,MAAc,GAAG,IAAI,CAS1E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAUnE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,IAAI,CAeN;AAMD,KAAK,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CA2LtD,CAAC;AAMF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;IACH,OAAO,EAAE,IAAI,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,gBAAgB,CAkB7D;AA2BD;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAiCpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,IAAI,CAKN"}
|