@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,131 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import { resolveConfig, getConfigPaths } from "../../config/index.js";
|
|
4
|
+
import { createCliClient } from "../../../client/cli-client.js";
|
|
5
|
+
import { CHANNEL } from "../../../channel.js";
|
|
6
|
+
function maskApiKey(apiKey) {
|
|
7
|
+
if (apiKey.length < 12) {
|
|
8
|
+
return "****";
|
|
9
|
+
}
|
|
10
|
+
return `${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`;
|
|
11
|
+
}
|
|
12
|
+
function getConfigSource(config) {
|
|
13
|
+
if (process.env.SPECFORGE_API_KEY) {
|
|
14
|
+
return "environment";
|
|
15
|
+
}
|
|
16
|
+
if (config.projectId) {
|
|
17
|
+
return ".specforge.json (project)";
|
|
18
|
+
}
|
|
19
|
+
if (config.apiKey) {
|
|
20
|
+
return "~/.specforge/config.json (global)";
|
|
21
|
+
}
|
|
22
|
+
return "defaults";
|
|
23
|
+
}
|
|
24
|
+
function displayUserInfo(info) {
|
|
25
|
+
console.log(chalk.bold("\nUser"));
|
|
26
|
+
console.log("\u2500\u2500\u2500\u2500");
|
|
27
|
+
if (info.user.name) {
|
|
28
|
+
console.log(`Name: ${info.user.name}`);
|
|
29
|
+
}
|
|
30
|
+
console.log(`Email: ${info.user.email}`);
|
|
31
|
+
console.log(`API Key: ${info.user.apiKey}`);
|
|
32
|
+
}
|
|
33
|
+
function displayConfiguration(info) {
|
|
34
|
+
console.log(chalk.bold("\nConfiguration"));
|
|
35
|
+
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
36
|
+
console.log(`Source: ${info.configuration.source}`);
|
|
37
|
+
if (info.configuration.projectName) {
|
|
38
|
+
console.log(`Project: ${info.configuration.projectName} (${info.configuration.projectId})`);
|
|
39
|
+
} else if (info.configuration.projectId) {
|
|
40
|
+
console.log(`Project: ${info.configuration.projectId}`);
|
|
41
|
+
} else {
|
|
42
|
+
console.log(`Project: ${chalk.dim("(not set)")}`);
|
|
43
|
+
}
|
|
44
|
+
if (info.configuration.specificationTitle) {
|
|
45
|
+
console.log(`Spec: ${info.configuration.specificationTitle} (${info.configuration.specificationId})`);
|
|
46
|
+
} else if (info.configuration.specificationId) {
|
|
47
|
+
console.log(`Spec: ${info.configuration.specificationId}`);
|
|
48
|
+
} else {
|
|
49
|
+
console.log(`Spec: ${chalk.dim("(not set)")}`);
|
|
50
|
+
}
|
|
51
|
+
console.log(`MCP format: ${info.configuration.mcpOutputFormat}`);
|
|
52
|
+
}
|
|
53
|
+
function displayPaths(info) {
|
|
54
|
+
console.log(chalk.bold("\nPaths"));
|
|
55
|
+
console.log("\u2500\u2500\u2500\u2500\u2500");
|
|
56
|
+
console.log(`Project config: ${info.paths.projectConfig}`);
|
|
57
|
+
console.log(`MCP config: ${info.paths.mcpConfig}`);
|
|
58
|
+
console.log(`Global config: ${info.paths.globalConfig}`);
|
|
59
|
+
console.log();
|
|
60
|
+
}
|
|
61
|
+
async function whoamiCommand(options) {
|
|
62
|
+
const config = resolveConfig();
|
|
63
|
+
const paths = getConfigPaths();
|
|
64
|
+
const spinner = ora("Fetching user info...").start();
|
|
65
|
+
try {
|
|
66
|
+
const client = createCliClient({
|
|
67
|
+
apiKey: config.apiKey,
|
|
68
|
+
apiUrl: process.env.SPECFORGE_API_URL || CHANNEL.endpoint.primary,
|
|
69
|
+
debug: config.debug
|
|
70
|
+
});
|
|
71
|
+
const userResponse = await client.call("get_current_user", {});
|
|
72
|
+
const userEmail = userResponse.email || "Unknown";
|
|
73
|
+
const userName = userResponse.name;
|
|
74
|
+
let projectName;
|
|
75
|
+
if (config.projectId) {
|
|
76
|
+
try {
|
|
77
|
+
const proj = await client.call("get_project", { projectId: config.projectId });
|
|
78
|
+
projectName = proj.name;
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
let specTitle;
|
|
83
|
+
if (config.specificationId) {
|
|
84
|
+
try {
|
|
85
|
+
const spec = await client.call("get_specification", {
|
|
86
|
+
specificationId: config.specificationId
|
|
87
|
+
});
|
|
88
|
+
specTitle = spec.title;
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
spinner.stop();
|
|
93
|
+
const info = {
|
|
94
|
+
user: {
|
|
95
|
+
name: userName,
|
|
96
|
+
email: userEmail,
|
|
97
|
+
apiKey: maskApiKey(config.apiKey)
|
|
98
|
+
},
|
|
99
|
+
configuration: {
|
|
100
|
+
source: getConfigSource(config),
|
|
101
|
+
projectId: config.projectId || void 0,
|
|
102
|
+
projectName,
|
|
103
|
+
specificationId: config.specificationId || void 0,
|
|
104
|
+
specificationTitle: specTitle,
|
|
105
|
+
mcpOutputFormat: config.mcpOutputFormat || "json"
|
|
106
|
+
},
|
|
107
|
+
paths: {
|
|
108
|
+
projectConfig: paths.projectConfig,
|
|
109
|
+
mcpConfig: paths.mcpConfig,
|
|
110
|
+
globalConfig: paths.globalConfig
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
if (options.json) {
|
|
114
|
+
console.log(JSON.stringify(info, null, 2));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
displayUserInfo(info);
|
|
118
|
+
displayConfiguration(info);
|
|
119
|
+
displayPaths(info);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
spinner.fail("Failed to fetch user info");
|
|
122
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
123
|
+
console.error(chalk.red(`
|
|
124
|
+
Error: ${message}`));
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
whoamiCommand
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=whoami.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/cli/commands/debug/whoami.ts"],"sourcesContent":["/**\n * Whoami Command Implementation\n *\n * Displays current user information, configuration settings, and config file paths.\n */\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { resolveConfig, getConfigPaths } from '../../config/index.js';\nimport { createCliClient } from '../../../client/cli-client.js';\nimport { CHANNEL } from '../../../channel.js';\nimport type { WhoamiOptions, WhoamiInfo } from './types.js';\n\n/**\n * Mask API key for display\n */\nfunction maskApiKey(apiKey: string): string {\n if (apiKey.length < 12) {\n return '****';\n }\n return `${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`;\n}\n\n/**\n * Determine configuration source\n */\nfunction getConfigSource(config: { apiKey?: string | null; projectId?: string | null }): string {\n if (process.env.SPECFORGE_API_KEY) {\n return 'environment';\n }\n if (config.projectId) {\n return '.specforge.json (project)';\n }\n if (config.apiKey) {\n return '~/.specforge/config.json (global)';\n }\n return 'defaults';\n}\n\n/**\n * Display user information\n */\nfunction displayUserInfo(info: WhoamiInfo): void {\n console.log(chalk.bold('\\nUser'));\n console.log('────');\n if (info.user.name) {\n console.log(`Name: ${info.user.name}`);\n }\n console.log(`Email: ${info.user.email}`);\n console.log(`API Key: ${info.user.apiKey}`);\n}\n\n/**\n * Display configuration information\n */\nfunction displayConfiguration(info: WhoamiInfo): void {\n console.log(chalk.bold('\\nConfiguration'));\n console.log('─────────────');\n console.log(`Source: ${info.configuration.source}`);\n\n if (info.configuration.projectName) {\n console.log(`Project: ${info.configuration.projectName} (${info.configuration.projectId})`);\n } else if (info.configuration.projectId) {\n console.log(`Project: ${info.configuration.projectId}`);\n } else {\n console.log(`Project: ${chalk.dim('(not set)')}`);\n }\n\n if (info.configuration.specificationTitle) {\n console.log(`Spec: ${info.configuration.specificationTitle} (${info.configuration.specificationId})`);\n } else if (info.configuration.specificationId) {\n console.log(`Spec: ${info.configuration.specificationId}`);\n } else {\n console.log(`Spec: ${chalk.dim('(not set)')}`);\n }\n\n console.log(`MCP format: ${info.configuration.mcpOutputFormat}`);\n}\n\n/**\n * Display configuration file paths\n */\nfunction displayPaths(info: WhoamiInfo): void {\n console.log(chalk.bold('\\nPaths'));\n console.log('─────');\n console.log(`Project config: ${info.paths.projectConfig}`);\n console.log(`MCP config: ${info.paths.mcpConfig}`);\n console.log(`Global config: ${info.paths.globalConfig}`);\n console.log();\n}\n\n/**\n * Whoami command implementation\n *\n * Displays current user and configuration information.\n */\nexport async function whoamiCommand(options: WhoamiOptions): Promise<void> {\n const config = resolveConfig();\n const paths = getConfigPaths();\n\n const spinner = ora('Fetching user info...').start();\n\n try {\n const client = createCliClient({\n apiKey: config.apiKey!,\n apiUrl: process.env.SPECFORGE_API_URL || CHANNEL.endpoint.primary,\n debug: config.debug,\n });\n\n // Get user info\n const userResponse = await client.call('get_current_user', {}) as { email?: string; name?: string };\n const userEmail = userResponse.email || 'Unknown';\n const userName = userResponse.name;\n\n // Get project name if configured\n let projectName: string | undefined;\n if (config.projectId) {\n try {\n const proj = await client.call('get_project', { projectId: config.projectId });\n projectName = (proj as { name?: string }).name;\n } catch {\n // Ignore - project might not exist\n }\n }\n\n // Get specification title if configured\n let specTitle: string | undefined;\n if (config.specificationId) {\n try {\n const spec = await client.call('get_specification', {\n specificationId: config.specificationId\n });\n specTitle = (spec as { title?: string }).title;\n } catch {\n // Ignore - spec might not exist\n }\n }\n\n spinner.stop();\n\n // Build whoami info\n const info: WhoamiInfo = {\n user: {\n name: userName,\n email: userEmail,\n apiKey: maskApiKey(config.apiKey!),\n },\n configuration: {\n source: getConfigSource(config),\n projectId: config.projectId || undefined,\n projectName,\n specificationId: config.specificationId || undefined,\n specificationTitle: specTitle,\n mcpOutputFormat: config.mcpOutputFormat || 'json',\n },\n paths: {\n projectConfig: paths.projectConfig,\n mcpConfig: paths.mcpConfig,\n globalConfig: paths.globalConfig,\n },\n };\n\n // JSON output\n if (options.json) {\n console.log(JSON.stringify(info, null, 2));\n return;\n }\n\n // Display formatted output\n displayUserInfo(info);\n displayConfiguration(info);\n displayPaths(info);\n\n } catch (error) {\n spinner.fail('Failed to fetch user info');\n const message = error instanceof Error ? error.message : 'Unknown error';\n console.error(chalk.red(`\\nError: ${message}`));\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor Command
|
|
3
|
+
*
|
|
4
|
+
* Diagnoses configuration and connection issues for SpecForge MCP.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { DoctorOptions } from './doctor.types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Doctor command action handler
|
|
10
|
+
*/
|
|
11
|
+
export declare function doctorAction(options: DoctorOptions): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Register doctor command with Commander
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerDoctorCommand(program: Command): void;
|
|
16
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,OAAO,EACL,aAAa,EAkBd,MAAM,mBAAmB,CAAC;AAoZ3B;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DxE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsB5D"}
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
resolveConfig,
|
|
4
|
+
loadGlobalConfig,
|
|
5
|
+
tryLoadProjectConfig,
|
|
6
|
+
getConfigPaths
|
|
7
|
+
} from "../config/index.js";
|
|
8
|
+
import { withErrorHandler } from "../middleware/error-handler.js";
|
|
9
|
+
import { printBlank, printHeader } from "../ui/output.js";
|
|
10
|
+
import { colors } from "../ui/colors.js";
|
|
11
|
+
import { forgeProgress } from "../ui/spinner.js";
|
|
12
|
+
import { createCliClient } from "../../client/cli-client.js";
|
|
13
|
+
import { CHANNEL } from "../../channel.js";
|
|
14
|
+
import {
|
|
15
|
+
createPassResult,
|
|
16
|
+
createFailResult,
|
|
17
|
+
createWarnResult,
|
|
18
|
+
calculateSummary,
|
|
19
|
+
hasFailures,
|
|
20
|
+
getStatusIcon,
|
|
21
|
+
CHECK_CATEGORIES,
|
|
22
|
+
CONFIG_CHECK_NAMES,
|
|
23
|
+
AUTH_CHECK_NAMES,
|
|
24
|
+
CONNECTION_CHECK_NAMES,
|
|
25
|
+
PROJECT_CHECK_NAMES,
|
|
26
|
+
PERFORMANCE_THRESHOLDS,
|
|
27
|
+
API_KEY_VALIDATION,
|
|
28
|
+
FIX_SUGGESTIONS
|
|
29
|
+
} from "./doctor.types.js";
|
|
30
|
+
function maskApiKey(apiKey, visibleChars = 8) {
|
|
31
|
+
if (apiKey.length <= visibleChars) {
|
|
32
|
+
return "***";
|
|
33
|
+
}
|
|
34
|
+
return `${API_KEY_VALIDATION.PREFIX}...${apiKey.slice(-visibleChars)}`;
|
|
35
|
+
}
|
|
36
|
+
function runConfigurationChecks() {
|
|
37
|
+
const results = [];
|
|
38
|
+
const paths = getConfigPaths();
|
|
39
|
+
const globalConfig = loadGlobalConfig();
|
|
40
|
+
const projectConfig = tryLoadProjectConfig();
|
|
41
|
+
results.push(createPassResult(
|
|
42
|
+
CONFIG_CHECK_NAMES.CHANNEL,
|
|
43
|
+
`${CHANNEL.name} \xB7 ${CHANNEL.packageName} \xB7 ${CHANNEL.endpoint.primary}`,
|
|
44
|
+
{ name: CHANNEL.name, packageName: CHANNEL.packageName, endpoint: CHANNEL.endpoint.primary }
|
|
45
|
+
));
|
|
46
|
+
if (fs.existsSync(paths.mcpConfig)) {
|
|
47
|
+
results.push(createPassResult(
|
|
48
|
+
CONFIG_CHECK_NAMES.MCP_CONFIG_FOUND,
|
|
49
|
+
paths.mcpConfig
|
|
50
|
+
));
|
|
51
|
+
} else {
|
|
52
|
+
results.push(createFailResult(
|
|
53
|
+
CONFIG_CHECK_NAMES.MCP_CONFIG_FOUND,
|
|
54
|
+
"File not found",
|
|
55
|
+
FIX_SUGGESTIONS.MISSING_MCP_CONFIG
|
|
56
|
+
));
|
|
57
|
+
}
|
|
58
|
+
if (projectConfig) {
|
|
59
|
+
results.push(createPassResult(
|
|
60
|
+
CONFIG_CHECK_NAMES.PROJECT_CONFIG_FOUND,
|
|
61
|
+
paths.projectConfig
|
|
62
|
+
));
|
|
63
|
+
} else {
|
|
64
|
+
results.push(createWarnResult(
|
|
65
|
+
CONFIG_CHECK_NAMES.PROJECT_CONFIG_FOUND,
|
|
66
|
+
"File not found (optional)",
|
|
67
|
+
FIX_SUGGESTIONS.MISSING_PROJECT_CONFIG
|
|
68
|
+
));
|
|
69
|
+
}
|
|
70
|
+
results.push(createPassResult(
|
|
71
|
+
CONFIG_CHECK_NAMES.GLOBAL_CONFIG_DIR,
|
|
72
|
+
paths.globalConfig
|
|
73
|
+
));
|
|
74
|
+
if (projectConfig?.planningSessionId) {
|
|
75
|
+
results.push(createPassResult(
|
|
76
|
+
CONFIG_CHECK_NAMES.PLANNING_SESSION,
|
|
77
|
+
projectConfig.planningSessionId
|
|
78
|
+
));
|
|
79
|
+
} else {
|
|
80
|
+
results.push(createWarnResult(
|
|
81
|
+
CONFIG_CHECK_NAMES.PLANNING_SESSION,
|
|
82
|
+
"None (no planning session persisted for this spec)"
|
|
83
|
+
));
|
|
84
|
+
}
|
|
85
|
+
const apiKeySource = process.env.SPECFORGE_API_KEY ? "From environment variable" : globalConfig.apiKey ? "From ~/.specforge/config.json" : void 0;
|
|
86
|
+
if (apiKeySource) {
|
|
87
|
+
results.push(createPassResult(
|
|
88
|
+
CONFIG_CHECK_NAMES.API_KEY_CONFIGURED,
|
|
89
|
+
apiKeySource
|
|
90
|
+
));
|
|
91
|
+
} else {
|
|
92
|
+
results.push(createFailResult(
|
|
93
|
+
CONFIG_CHECK_NAMES.API_KEY_CONFIGURED,
|
|
94
|
+
"No API key found",
|
|
95
|
+
FIX_SUGGESTIONS.MISSING_API_KEY
|
|
96
|
+
));
|
|
97
|
+
}
|
|
98
|
+
return results;
|
|
99
|
+
}
|
|
100
|
+
async function runAuthenticationChecks(apiKey) {
|
|
101
|
+
const results = [];
|
|
102
|
+
if (!apiKey) {
|
|
103
|
+
results.push(createFailResult(
|
|
104
|
+
AUTH_CHECK_NAMES.API_KEY_FORMAT,
|
|
105
|
+
"No API key to validate"
|
|
106
|
+
));
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
const hasValidPrefix = apiKey.startsWith(API_KEY_VALIDATION.PREFIX);
|
|
110
|
+
const hasValidLength = apiKey.length >= API_KEY_VALIDATION.MIN_LENGTH;
|
|
111
|
+
if (hasValidPrefix && hasValidLength) {
|
|
112
|
+
results.push(createPassResult(
|
|
113
|
+
AUTH_CHECK_NAMES.API_KEY_FORMAT,
|
|
114
|
+
maskApiKey(apiKey)
|
|
115
|
+
));
|
|
116
|
+
} else {
|
|
117
|
+
const issues = [];
|
|
118
|
+
if (!hasValidPrefix) issues.push(`should start with ${API_KEY_VALIDATION.PREFIX}`);
|
|
119
|
+
if (!hasValidLength) issues.push(`minimum ${API_KEY_VALIDATION.MIN_LENGTH} characters`);
|
|
120
|
+
results.push(createFailResult(
|
|
121
|
+
AUTH_CHECK_NAMES.API_KEY_FORMAT,
|
|
122
|
+
`Invalid format (${issues.join(", ")})`,
|
|
123
|
+
FIX_SUGGESTIONS.INVALID_API_KEY_FORMAT
|
|
124
|
+
));
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
const config = resolveConfig();
|
|
128
|
+
const client = createCliClient({
|
|
129
|
+
apiKey,
|
|
130
|
+
apiUrl: config.apiUrl,
|
|
131
|
+
debug: config.debug
|
|
132
|
+
});
|
|
133
|
+
try {
|
|
134
|
+
await client.call("list_projects", {});
|
|
135
|
+
results.push(createPassResult(
|
|
136
|
+
AUTH_CHECK_NAMES.API_KEY_AUTHENTICATED,
|
|
137
|
+
"Successfully authenticated"
|
|
138
|
+
));
|
|
139
|
+
try {
|
|
140
|
+
const userResponse = await client.call(
|
|
141
|
+
"get_current_user",
|
|
142
|
+
{}
|
|
143
|
+
);
|
|
144
|
+
const userInfo = userResponse.name ? `${userResponse.name} (${userResponse.email})` : userResponse.email || "Unknown";
|
|
145
|
+
results.push(createPassResult(
|
|
146
|
+
AUTH_CHECK_NAMES.USER_INFO,
|
|
147
|
+
userInfo
|
|
148
|
+
));
|
|
149
|
+
} catch {
|
|
150
|
+
results.push(createPassResult(
|
|
151
|
+
AUTH_CHECK_NAMES.USER_INFO,
|
|
152
|
+
"Authenticated (email not available)"
|
|
153
|
+
));
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
157
|
+
if (message.includes("401") || message.includes("Unauthorized")) {
|
|
158
|
+
results.push(createFailResult(
|
|
159
|
+
AUTH_CHECK_NAMES.API_KEY_AUTHENTICATED,
|
|
160
|
+
"Invalid or expired API key",
|
|
161
|
+
FIX_SUGGESTIONS.EXPIRED_API_KEY
|
|
162
|
+
));
|
|
163
|
+
} else {
|
|
164
|
+
results.push(createFailResult(
|
|
165
|
+
AUTH_CHECK_NAMES.API_KEY_AUTHENTICATED,
|
|
166
|
+
`Authentication failed: ${message}`
|
|
167
|
+
));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return results;
|
|
171
|
+
}
|
|
172
|
+
async function runConnectionChecks(apiKey) {
|
|
173
|
+
const results = [];
|
|
174
|
+
const config = resolveConfig();
|
|
175
|
+
const apiUrl = config.apiUrl;
|
|
176
|
+
if (!apiKey) {
|
|
177
|
+
results.push(createFailResult(
|
|
178
|
+
CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,
|
|
179
|
+
"No API key to test connection",
|
|
180
|
+
FIX_SUGGESTIONS.MISSING_API_KEY
|
|
181
|
+
));
|
|
182
|
+
return results;
|
|
183
|
+
}
|
|
184
|
+
const client = createCliClient({
|
|
185
|
+
apiKey,
|
|
186
|
+
apiUrl: config.apiUrl,
|
|
187
|
+
debug: config.debug
|
|
188
|
+
});
|
|
189
|
+
try {
|
|
190
|
+
const start = Date.now();
|
|
191
|
+
await client.call("list_projects", {});
|
|
192
|
+
const elapsed = Date.now() - start;
|
|
193
|
+
results.push(createPassResult(
|
|
194
|
+
CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,
|
|
195
|
+
apiUrl
|
|
196
|
+
));
|
|
197
|
+
if (elapsed < PERFORMANCE_THRESHOLDS.RESPONSE_TIME_WARNING) {
|
|
198
|
+
results.push(createPassResult(
|
|
199
|
+
CONNECTION_CHECK_NAMES.RESPONSE_TIME,
|
|
200
|
+
`${elapsed}ms`,
|
|
201
|
+
{ elapsed }
|
|
202
|
+
));
|
|
203
|
+
} else if (elapsed < PERFORMANCE_THRESHOLDS.RESPONSE_TIME_ERROR) {
|
|
204
|
+
results.push(createWarnResult(
|
|
205
|
+
CONNECTION_CHECK_NAMES.RESPONSE_TIME,
|
|
206
|
+
`${elapsed}ms (slow)`,
|
|
207
|
+
void 0,
|
|
208
|
+
{ elapsed }
|
|
209
|
+
));
|
|
210
|
+
} else {
|
|
211
|
+
results.push(createFailResult(
|
|
212
|
+
CONNECTION_CHECK_NAMES.RESPONSE_TIME,
|
|
213
|
+
`${elapsed}ms (very slow)`,
|
|
214
|
+
FIX_SUGGESTIONS.CONNECTION_TIMEOUT,
|
|
215
|
+
{ elapsed }
|
|
216
|
+
));
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
220
|
+
if (message.includes("ENOTFOUND") || message.includes("ECONNREFUSED") || message.includes("getaddrinfo")) {
|
|
221
|
+
results.push(createFailResult(
|
|
222
|
+
CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,
|
|
223
|
+
"Could not connect to API",
|
|
224
|
+
FIX_SUGGESTIONS.CONNECTION_FAILED
|
|
225
|
+
));
|
|
226
|
+
} else if (message.includes("timeout") || message.includes("TimeoutError")) {
|
|
227
|
+
results.push(createFailResult(
|
|
228
|
+
CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,
|
|
229
|
+
"Connection timed out",
|
|
230
|
+
FIX_SUGGESTIONS.CONNECTION_TIMEOUT
|
|
231
|
+
));
|
|
232
|
+
} else if (message.includes("401") || message.includes("Unauthorized")) {
|
|
233
|
+
results.push(createPassResult(
|
|
234
|
+
CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,
|
|
235
|
+
`${apiUrl} (auth issue handled separately)`
|
|
236
|
+
));
|
|
237
|
+
} else {
|
|
238
|
+
results.push(createFailResult(
|
|
239
|
+
CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,
|
|
240
|
+
message
|
|
241
|
+
));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return results;
|
|
245
|
+
}
|
|
246
|
+
async function runProjectAccessChecks() {
|
|
247
|
+
const results = [];
|
|
248
|
+
const config = resolveConfig();
|
|
249
|
+
if (!config.apiKey) {
|
|
250
|
+
return results;
|
|
251
|
+
}
|
|
252
|
+
const client = createCliClient({
|
|
253
|
+
apiKey: config.apiKey,
|
|
254
|
+
apiUrl: config.apiUrl,
|
|
255
|
+
debug: config.debug
|
|
256
|
+
});
|
|
257
|
+
if (config.projectId) {
|
|
258
|
+
try {
|
|
259
|
+
await client.call("get_project", { projectId: config.projectId });
|
|
260
|
+
results.push(createPassResult(
|
|
261
|
+
PROJECT_CHECK_NAMES.PROJECT_ACCESSIBLE,
|
|
262
|
+
config.projectId
|
|
263
|
+
));
|
|
264
|
+
} catch (error) {
|
|
265
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
266
|
+
results.push(createFailResult(
|
|
267
|
+
PROJECT_CHECK_NAMES.PROJECT_ACCESSIBLE,
|
|
268
|
+
`Cannot access: ${message}`,
|
|
269
|
+
FIX_SUGGESTIONS.PROJECT_NOT_ACCESSIBLE
|
|
270
|
+
));
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
results.push(createWarnResult(
|
|
274
|
+
PROJECT_CHECK_NAMES.PROJECT_ACCESSIBLE,
|
|
275
|
+
"No default project configured",
|
|
276
|
+
FIX_SUGGESTIONS.NO_PROJECT_CONFIGURED
|
|
277
|
+
));
|
|
278
|
+
}
|
|
279
|
+
if (config.specificationId) {
|
|
280
|
+
try {
|
|
281
|
+
await client.call("get_specification", { specificationId: config.specificationId });
|
|
282
|
+
results.push(createPassResult(
|
|
283
|
+
PROJECT_CHECK_NAMES.SPECIFICATION_ACCESSIBLE,
|
|
284
|
+
config.specificationId
|
|
285
|
+
));
|
|
286
|
+
} catch (error) {
|
|
287
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
288
|
+
results.push(createFailResult(
|
|
289
|
+
PROJECT_CHECK_NAMES.SPECIFICATION_ACCESSIBLE,
|
|
290
|
+
`Cannot access: ${message}`,
|
|
291
|
+
FIX_SUGGESTIONS.SPEC_NOT_ACCESSIBLE
|
|
292
|
+
));
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
results.push(createWarnResult(
|
|
296
|
+
PROJECT_CHECK_NAMES.SPECIFICATION_ACCESSIBLE,
|
|
297
|
+
"No default specification configured"
|
|
298
|
+
));
|
|
299
|
+
}
|
|
300
|
+
return results;
|
|
301
|
+
}
|
|
302
|
+
function displayReport(report) {
|
|
303
|
+
printBlank();
|
|
304
|
+
printHeader("SpecForge MCP Health Check");
|
|
305
|
+
printBlank();
|
|
306
|
+
for (const category of CHECK_CATEGORIES) {
|
|
307
|
+
const checks = report.checks[category.id];
|
|
308
|
+
if (checks.length === 0) continue;
|
|
309
|
+
console.log(colors.bold(category.name));
|
|
310
|
+
for (const check of checks) {
|
|
311
|
+
const icon = check.status === "pass" ? colors.success(getStatusIcon("pass")) : check.status === "warn" ? colors.warning(getStatusIcon("warn")) : colors.error(getStatusIcon("fail"));
|
|
312
|
+
console.log(` ${icon} ${check.name}: ${check.message}`);
|
|
313
|
+
if (check.fix && check.status !== "pass") {
|
|
314
|
+
console.log(colors.muted(` \u2192 ${check.fix}`));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
printBlank();
|
|
318
|
+
}
|
|
319
|
+
const { passed, failed, warnings, total } = report.summary;
|
|
320
|
+
if (failed === 0 && warnings === 0) {
|
|
321
|
+
console.log(colors.success(`All ${total} checks passed!`));
|
|
322
|
+
} else if (failed === 0) {
|
|
323
|
+
console.log(
|
|
324
|
+
colors.success("All checks passed!") + colors.warning(` (${warnings} warning${warnings > 1 ? "s" : ""})`)
|
|
325
|
+
);
|
|
326
|
+
} else {
|
|
327
|
+
const failMsg = `${failed} check${failed > 1 ? "s" : ""} failed`;
|
|
328
|
+
const warnMsg = warnings > 0 ? `, ${warnings} warning${warnings > 1 ? "s" : ""}` : "";
|
|
329
|
+
console.log(colors.error(failMsg) + colors.warning(warnMsg));
|
|
330
|
+
}
|
|
331
|
+
printBlank();
|
|
332
|
+
}
|
|
333
|
+
async function doctorAction(options) {
|
|
334
|
+
const config = resolveConfig();
|
|
335
|
+
let configChecks = [];
|
|
336
|
+
let authChecks = [];
|
|
337
|
+
let connectionChecks = [];
|
|
338
|
+
let projectChecks = [];
|
|
339
|
+
if (options.json) {
|
|
340
|
+
configChecks = runConfigurationChecks();
|
|
341
|
+
authChecks = await runAuthenticationChecks(config.apiKey);
|
|
342
|
+
connectionChecks = await runConnectionChecks(config.apiKey);
|
|
343
|
+
projectChecks = await runProjectAccessChecks();
|
|
344
|
+
} else {
|
|
345
|
+
await forgeProgress("Running diagnostics", [
|
|
346
|
+
{
|
|
347
|
+
label: "config",
|
|
348
|
+
fn: async () => {
|
|
349
|
+
configChecks = runConfigurationChecks();
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
label: "auth",
|
|
354
|
+
fn: async () => {
|
|
355
|
+
authChecks = await runAuthenticationChecks(config.apiKey);
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
label: "connection",
|
|
360
|
+
fn: async () => {
|
|
361
|
+
connectionChecks = await runConnectionChecks(config.apiKey);
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
label: "project",
|
|
366
|
+
fn: async () => {
|
|
367
|
+
projectChecks = await runProjectAccessChecks();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
]);
|
|
371
|
+
}
|
|
372
|
+
const checks = {
|
|
373
|
+
configuration: configChecks,
|
|
374
|
+
authentication: authChecks,
|
|
375
|
+
connection: connectionChecks,
|
|
376
|
+
projectAccess: projectChecks
|
|
377
|
+
};
|
|
378
|
+
const report = {
|
|
379
|
+
checks,
|
|
380
|
+
summary: calculateSummary(checks),
|
|
381
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
382
|
+
};
|
|
383
|
+
if (options.json) {
|
|
384
|
+
console.log(JSON.stringify(report, null, 2));
|
|
385
|
+
} else {
|
|
386
|
+
displayReport(report);
|
|
387
|
+
}
|
|
388
|
+
if (hasFailures(report)) {
|
|
389
|
+
process.exit(1);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function registerDoctorCommand(program) {
|
|
393
|
+
program.command("doctor").description("Diagnose configuration and connection issues").option("--json", "Output as JSON for scripting").option("--fix", "Attempt to fix issues automatically (future)").addHelpText("after", `
|
|
394
|
+
Examples:
|
|
395
|
+
$ specforge doctor # Run health checks
|
|
396
|
+
$ specforge doctor --json # Output as JSON
|
|
397
|
+
|
|
398
|
+
Check Categories:
|
|
399
|
+
Configuration - Config files presence and validity
|
|
400
|
+
Authentication - API key format and authentication
|
|
401
|
+
Connection - API connectivity and response time
|
|
402
|
+
Project Access - Access to configured project/specification
|
|
403
|
+
|
|
404
|
+
Exit Codes:
|
|
405
|
+
0 - All checks passed (warnings OK)
|
|
406
|
+
1 - One or more checks failed
|
|
407
|
+
`).action(withErrorHandler(doctorAction));
|
|
408
|
+
}
|
|
409
|
+
export {
|
|
410
|
+
doctorAction,
|
|
411
|
+
registerDoctorCommand
|
|
412
|
+
};
|
|
413
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/doctor.ts"],"sourcesContent":["/**\n * Doctor Command\n *\n * Diagnoses configuration and connection issues for SpecForge MCP.\n */\n\nimport { Command } from 'commander';\nimport fs from 'node:fs';\nimport {\n resolveConfig,\n loadGlobalConfig,\n tryLoadProjectConfig,\n getConfigPaths,\n} from '../config/index.js';\nimport { withErrorHandler } from '../middleware/error-handler.js';\nimport { printBlank, printHeader } from '../ui/output.js';\nimport { colors } from '../ui/colors.js';\nimport { forgeProgress } from '../ui/spinner.js';\nimport { createCliClient } from '../../client/cli-client.js';\nimport { CHANNEL } from '../../channel.js';\nimport {\n DoctorOptions,\n DoctorReport,\n CheckResult,\n CheckCategory,\n createPassResult,\n createFailResult,\n createWarnResult,\n calculateSummary,\n hasFailures,\n getStatusIcon,\n CHECK_CATEGORIES,\n CONFIG_CHECK_NAMES,\n AUTH_CHECK_NAMES,\n CONNECTION_CHECK_NAMES,\n PROJECT_CHECK_NAMES,\n PERFORMANCE_THRESHOLDS,\n API_KEY_VALIDATION,\n FIX_SUGGESTIONS,\n} from './doctor.types.js';\n\n/**\n * Mask an API key for display (show only last N characters)\n */\nfunction maskApiKey(apiKey: string, visibleChars = 8): string {\n if (apiKey.length <= visibleChars) {\n return '***';\n }\n return `${API_KEY_VALIDATION.PREFIX}...${apiKey.slice(-visibleChars)}`;\n}\n\n/**\n * Run configuration checks\n */\nfunction runConfigurationChecks(): CheckResult[] {\n const results: CheckResult[] = [];\n const paths = getConfigPaths();\n const globalConfig = loadGlobalConfig();\n const projectConfig = tryLoadProjectConfig();\n\n // Channel + endpoint the binary is wired to (M9.6c).\n results.push(createPassResult(\n CONFIG_CHECK_NAMES.CHANNEL,\n `${CHANNEL.name} · ${CHANNEL.packageName} · ${CHANNEL.endpoint.primary}`,\n { name: CHANNEL.name, packageName: CHANNEL.packageName, endpoint: CHANNEL.endpoint.primary },\n ));\n\n // Check .mcp.json\n if (fs.existsSync(paths.mcpConfig)) {\n results.push(createPassResult(\n CONFIG_CHECK_NAMES.MCP_CONFIG_FOUND,\n paths.mcpConfig\n ));\n } else {\n results.push(createFailResult(\n CONFIG_CHECK_NAMES.MCP_CONFIG_FOUND,\n 'File not found',\n FIX_SUGGESTIONS.MISSING_MCP_CONFIG\n ));\n }\n\n // Check .specforge.json (optional)\n if (projectConfig) {\n results.push(createPassResult(\n CONFIG_CHECK_NAMES.PROJECT_CONFIG_FOUND,\n paths.projectConfig\n ));\n } else {\n results.push(createWarnResult(\n CONFIG_CHECK_NAMES.PROJECT_CONFIG_FOUND,\n 'File not found (optional)',\n FIX_SUGGESTIONS.MISSING_PROJECT_CONFIG\n ));\n }\n\n // Global config directory (M9.6c — channel-aware path).\n results.push(createPassResult(\n CONFIG_CHECK_NAMES.GLOBAL_CONFIG_DIR,\n paths.globalConfig\n ));\n\n // Active planning session for this spec (M9.6c) — read from local config; the\n // agent injects this id per-call. (The old `get_working_context` op is dead.)\n if (projectConfig?.planningSessionId) {\n results.push(createPassResult(\n CONFIG_CHECK_NAMES.PLANNING_SESSION,\n projectConfig.planningSessionId\n ));\n } else {\n results.push(createWarnResult(\n CONFIG_CHECK_NAMES.PLANNING_SESSION,\n 'None (no planning session persisted for this spec)'\n ));\n }\n\n // Check API key configured\n const apiKeySource = process.env.SPECFORGE_API_KEY\n ? 'From environment variable'\n : globalConfig.apiKey\n ? 'From ~/.specforge/config.json'\n : undefined;\n\n if (apiKeySource) {\n results.push(createPassResult(\n CONFIG_CHECK_NAMES.API_KEY_CONFIGURED,\n apiKeySource\n ));\n } else {\n results.push(createFailResult(\n CONFIG_CHECK_NAMES.API_KEY_CONFIGURED,\n 'No API key found',\n FIX_SUGGESTIONS.MISSING_API_KEY\n ));\n }\n\n return results;\n}\n\n/**\n * Run authentication checks\n */\nasync function runAuthenticationChecks(apiKey: string | null | undefined): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n\n if (!apiKey) {\n results.push(createFailResult(\n AUTH_CHECK_NAMES.API_KEY_FORMAT,\n 'No API key to validate'\n ));\n return results;\n }\n\n // Check format\n const hasValidPrefix = apiKey.startsWith(API_KEY_VALIDATION.PREFIX);\n const hasValidLength = apiKey.length >= API_KEY_VALIDATION.MIN_LENGTH;\n\n if (hasValidPrefix && hasValidLength) {\n results.push(createPassResult(\n AUTH_CHECK_NAMES.API_KEY_FORMAT,\n maskApiKey(apiKey)\n ));\n } else {\n const issues: string[] = [];\n if (!hasValidPrefix) issues.push(`should start with ${API_KEY_VALIDATION.PREFIX}`);\n if (!hasValidLength) issues.push(`minimum ${API_KEY_VALIDATION.MIN_LENGTH} characters`);\n\n results.push(createFailResult(\n AUTH_CHECK_NAMES.API_KEY_FORMAT,\n `Invalid format (${issues.join(', ')})`,\n FIX_SUGGESTIONS.INVALID_API_KEY_FORMAT\n ));\n return results; // Don't test against backend if format is invalid\n }\n\n // Validate against backend\n const config = resolveConfig();\n const client = createCliClient({\n apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n try {\n // First verify authentication works\n await client.call('list_projects', {});\n\n results.push(createPassResult(\n AUTH_CHECK_NAMES.API_KEY_AUTHENTICATED,\n 'Successfully authenticated'\n ));\n\n // Get user info\n try {\n const userResponse = await client.call<{ email?: string; name?: string }>(\n 'get_current_user',\n {}\n );\n const userInfo = userResponse.name\n ? `${userResponse.name} (${userResponse.email})`\n : userResponse.email || 'Unknown';\n results.push(createPassResult(\n AUTH_CHECK_NAMES.USER_INFO,\n userInfo\n ));\n } catch {\n results.push(createPassResult(\n AUTH_CHECK_NAMES.USER_INFO,\n 'Authenticated (email not available)'\n ));\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n\n if (message.includes('401') || message.includes('Unauthorized')) {\n results.push(createFailResult(\n AUTH_CHECK_NAMES.API_KEY_AUTHENTICATED,\n 'Invalid or expired API key',\n FIX_SUGGESTIONS.EXPIRED_API_KEY\n ));\n } else {\n results.push(createFailResult(\n AUTH_CHECK_NAMES.API_KEY_AUTHENTICATED,\n `Authentication failed: ${message}`\n ));\n }\n }\n\n return results;\n}\n\n/**\n * Run connection checks\n */\nasync function runConnectionChecks(apiKey: string | null | undefined): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n const config = resolveConfig();\n const apiUrl = config.apiUrl;\n\n // Skip connection check if no API key (auth checks will fail anyway)\n if (!apiKey) {\n results.push(createFailResult(\n CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,\n 'No API key to test connection',\n FIX_SUGGESTIONS.MISSING_API_KEY\n ));\n return results;\n }\n\n const client = createCliClient({\n apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n try {\n const start = Date.now();\n // Use an authenticated call to test connectivity\n await client.call('list_projects', {});\n const elapsed = Date.now() - start;\n\n results.push(createPassResult(\n CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,\n apiUrl\n ));\n\n // Response time check\n if (elapsed < PERFORMANCE_THRESHOLDS.RESPONSE_TIME_WARNING) {\n results.push(createPassResult(\n CONNECTION_CHECK_NAMES.RESPONSE_TIME,\n `${elapsed}ms`,\n { elapsed }\n ));\n } else if (elapsed < PERFORMANCE_THRESHOLDS.RESPONSE_TIME_ERROR) {\n results.push(createWarnResult(\n CONNECTION_CHECK_NAMES.RESPONSE_TIME,\n `${elapsed}ms (slow)`,\n undefined,\n { elapsed }\n ));\n } else {\n results.push(createFailResult(\n CONNECTION_CHECK_NAMES.RESPONSE_TIME,\n `${elapsed}ms (very slow)`,\n FIX_SUGGESTIONS.CONNECTION_TIMEOUT,\n { elapsed }\n ));\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n\n if (message.includes('ENOTFOUND') || message.includes('ECONNREFUSED') || message.includes('getaddrinfo')) {\n results.push(createFailResult(\n CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,\n 'Could not connect to API',\n FIX_SUGGESTIONS.CONNECTION_FAILED\n ));\n } else if (message.includes('timeout') || message.includes('TimeoutError')) {\n results.push(createFailResult(\n CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,\n 'Connection timed out',\n FIX_SUGGESTIONS.CONNECTION_TIMEOUT\n ));\n } else if (message.includes('401') || message.includes('Unauthorized')) {\n // Auth issue, but connection works\n results.push(createPassResult(\n CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,\n `${apiUrl} (auth issue handled separately)`\n ));\n } else {\n results.push(createFailResult(\n CONNECTION_CHECK_NAMES.API_ENDPOINT_REACHABLE,\n message\n ));\n }\n }\n\n return results;\n}\n\n/**\n * Run project access checks\n */\nasync function runProjectAccessChecks(): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n const config = resolveConfig();\n\n if (!config.apiKey) {\n // Skip if no API key (auth checks already failed)\n return results;\n }\n\n const client = createCliClient({\n apiKey: config.apiKey,\n apiUrl: config.apiUrl,\n debug: config.debug,\n });\n\n // Check project access\n if (config.projectId) {\n try {\n await client.call('get_project', { projectId: config.projectId });\n results.push(createPassResult(\n PROJECT_CHECK_NAMES.PROJECT_ACCESSIBLE,\n config.projectId\n ));\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n results.push(createFailResult(\n PROJECT_CHECK_NAMES.PROJECT_ACCESSIBLE,\n `Cannot access: ${message}`,\n FIX_SUGGESTIONS.PROJECT_NOT_ACCESSIBLE\n ));\n }\n } else {\n results.push(createWarnResult(\n PROJECT_CHECK_NAMES.PROJECT_ACCESSIBLE,\n 'No default project configured',\n FIX_SUGGESTIONS.NO_PROJECT_CONFIGURED\n ));\n }\n\n // Check specification access\n if (config.specificationId) {\n try {\n await client.call('get_specification', { specificationId: config.specificationId });\n results.push(createPassResult(\n PROJECT_CHECK_NAMES.SPECIFICATION_ACCESSIBLE,\n config.specificationId\n ));\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n results.push(createFailResult(\n PROJECT_CHECK_NAMES.SPECIFICATION_ACCESSIBLE,\n `Cannot access: ${message}`,\n FIX_SUGGESTIONS.SPEC_NOT_ACCESSIBLE\n ));\n }\n } else {\n results.push(createWarnResult(\n PROJECT_CHECK_NAMES.SPECIFICATION_ACCESSIBLE,\n 'No default specification configured'\n ));\n }\n\n // NOTE: the implementation-session probe (old `get_working_context`) was\n // removed in M8/M9 — that backend op no longer exists. The active planning\n // session is surfaced from local config in runConfigurationChecks (M9.6c).\n\n return results;\n}\n\n/**\n * Display the report in formatted console output\n */\nfunction displayReport(report: DoctorReport): void {\n printBlank();\n printHeader('SpecForge MCP Health Check');\n printBlank();\n\n for (const category of CHECK_CATEGORIES) {\n const checks = report.checks[category.id];\n if (checks.length === 0) continue;\n\n console.log(colors.bold(category.name));\n\n for (const check of checks) {\n const icon = check.status === 'pass'\n ? colors.success(getStatusIcon('pass'))\n : check.status === 'warn'\n ? colors.warning(getStatusIcon('warn'))\n : colors.error(getStatusIcon('fail'));\n\n console.log(` ${icon} ${check.name}: ${check.message}`);\n\n if (check.fix && check.status !== 'pass') {\n console.log(colors.muted(` → ${check.fix}`));\n }\n }\n\n printBlank();\n }\n\n // Summary\n const { passed, failed, warnings, total } = report.summary;\n\n if (failed === 0 && warnings === 0) {\n console.log(colors.success(`All ${total} checks passed!`));\n } else if (failed === 0) {\n console.log(\n colors.success('All checks passed!') +\n colors.warning(` (${warnings} warning${warnings > 1 ? 's' : ''})`)\n );\n } else {\n const failMsg = `${failed} check${failed > 1 ? 's' : ''} failed`;\n const warnMsg = warnings > 0\n ? `, ${warnings} warning${warnings > 1 ? 's' : ''}`\n : '';\n console.log(colors.error(failMsg) + colors.warning(warnMsg));\n }\n\n printBlank();\n}\n\n/**\n * Doctor command action handler\n */\nexport async function doctorAction(options: DoctorOptions): Promise<void> {\n const config = resolveConfig();\n\n let configChecks: CheckResult[] = [];\n let authChecks: CheckResult[] = [];\n let connectionChecks: CheckResult[] = [];\n let projectChecks: CheckResult[] = [];\n\n if (options.json) {\n configChecks = runConfigurationChecks();\n authChecks = await runAuthenticationChecks(config.apiKey);\n connectionChecks = await runConnectionChecks(config.apiKey);\n projectChecks = await runProjectAccessChecks();\n } else {\n await forgeProgress('Running diagnostics', [\n {\n label: 'config',\n fn: async () => { configChecks = runConfigurationChecks(); },\n },\n {\n label: 'auth',\n fn: async () => { authChecks = await runAuthenticationChecks(config.apiKey); },\n },\n {\n label: 'connection',\n fn: async () => { connectionChecks = await runConnectionChecks(config.apiKey); },\n },\n {\n label: 'project',\n fn: async () => { projectChecks = await runProjectAccessChecks(); },\n },\n ]);\n }\n\n // Compile report\n const checks: Record<CheckCategory, CheckResult[]> = {\n configuration: configChecks,\n authentication: authChecks,\n connection: connectionChecks,\n projectAccess: projectChecks,\n };\n\n const report: DoctorReport = {\n checks,\n summary: calculateSummary(checks),\n timestamp: new Date().toISOString(),\n };\n\n // Output\n if (options.json) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n displayReport(report);\n }\n\n // Exit with error if any checks failed\n if (hasFailures(report)) {\n process.exit(1);\n }\n}\n\n/**\n * Register doctor command with Commander\n */\nexport function registerDoctorCommand(program: Command): void {\n program\n .command('doctor')\n .description('Diagnose configuration and connection issues')\n .option('--json', 'Output as JSON for scripting')\n .option('--fix', 'Attempt to fix issues automatically (future)')\n .addHelpText('after', `\nExamples:\n $ specforge doctor # Run health checks\n $ specforge doctor --json # Output as JSON\n\nCheck Categories:\n Configuration - Config files presence and validity\n Authentication - API key format and authentication\n Connection - API connectivity and response time\n Project Access - Access to configured project/specification\n\nExit Codes:\n 0 - All checks passed (warnings OK)\n 1 - One or more checks failed\n`)\n .action(withErrorHandler(doctorAction));\n}\n"],"mappings":"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","names":[]}
|