@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 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/ui/output.ts"],"sourcesContent":["/**\n * Output Formatting Utilities\n *\n * Provides consistent output formatting for tables, lists, and structured data.\n */\n\nimport { colors, style, icons } from './colors.js';\n\n/**\n * Print a labeled value\n *\n * @example\n * printField('Project', 'my-project');\n * // Output: Project: my-project\n */\nexport function printField(label: string, value: string | null | undefined): void {\n const displayValue = value ?? colors.muted('(not set)');\n console.log(`${style.label(label)}: ${displayValue}`);\n}\n\n/**\n * Print a section header\n *\n * @example\n * printHeader('Configuration');\n * // Output:\n * // Configuration\n * // ─────────────\n */\nexport function printHeader(title: string): void {\n console.log();\n console.log(style.label(title));\n console.log(colors.muted('─'.repeat(title.length)));\n}\n\n/**\n * Print a blank line\n */\nexport function printBlank(): void {\n console.log();\n}\n\n/**\n * Print a list of items\n *\n * @example\n * printList(['Item 1', 'Item 2', 'Item 3']);\n * // Output:\n * // • Item 1\n * // • Item 2\n * // • Item 3\n */\nexport function printList(items: string[], bullet: string = icons.bullet): void {\n for (const item of items) {\n console.log(` ${bullet} ${item}`);\n }\n}\n\n/**\n * Print a success message\n *\n * @example\n * printSuccess('Configuration saved');\n * // Output: ✓ Configuration saved\n */\nexport function printSuccess(message: string): void {\n console.log(`${icons.success} ${message}`);\n}\n\n/**\n * Print an error message\n *\n * @example\n * printError('Failed to connect');\n * // Output: ✗ Failed to connect\n */\nexport function printError(message: string): void {\n console.error(`${icons.error} ${style.err(message)}`);\n}\n\n/**\n * Print a warning message\n *\n * @example\n * printWarning('API key will expire soon');\n * // Output: ⚠ API key will expire soon\n */\nexport function printWarning(message: string): void {\n console.error(`${icons.warning} ${style.warn(message)}`);\n}\n\n/**\n * Print an info message\n *\n * @example\n * printInfo('Fetching data...');\n * // Output: ℹ Fetching data...\n */\nexport function printInfo(message: string): void {\n console.log(`${icons.info} ${message}`);\n}\n\n/**\n * Print a key-value table\n *\n * @example\n * printTable({\n * 'Project': 'my-project',\n * 'Specification': 'v2.0',\n * 'Status': 'active',\n * });\n */\nexport function printTable(data: Record<string, string | null | undefined>): void {\n const maxKeyLength = Math.max(...Object.keys(data).map(k => k.length));\n\n for (const [key, value] of Object.entries(data)) {\n const paddedKey = key.padEnd(maxKeyLength);\n const displayValue = value ?? colors.muted('(not set)');\n console.log(` ${style.label(paddedKey)} ${displayValue}`);\n }\n}\n\n/**\n * Print JSON output (for --format json)\n *\n * @example\n * printJson({ project: 'my-project', status: 'active' });\n */\nexport function printJson(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\n/**\n * Print a command hint\n *\n * @example\n * printHint('specforge init', 'Initialize project');\n * // Output: → Run `specforge init` to initialize project\n */\nexport function printHint(command: string, description: string): void {\n console.log(`${icons.arrow} Run ${style.command(command)} to ${description}`);\n}\n\n/**\n * Print a URL with description\n *\n * @example\n * printLink('https://specforge.com', 'Visit documentation');\n * // Output: → Visit documentation: https://specforge.com\n */\nexport function printLink(url: string, description: string): void {\n console.log(`${icons.arrow} ${description}: ${style.url(url)}`);\n}\n\n/**\n * Format bytes to human readable size\n */\nexport function formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n\n return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;\n}\n\n/**\n * Format duration in milliseconds to human readable\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n if (ms < 3600000) return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;\n\n const hours = Math.floor(ms / 3600000);\n const minutes = Math.floor((ms % 3600000) / 60000);\n return `${hours}h ${minutes}m`;\n}\n\n/**\n * Truncate string with ellipsis\n */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength - 1) + '…';\n}\n"],"mappings":"AAMA,SAAS,QAAQ,OAAO,aAAa;AAS9B,SAAS,WAAW,OAAe,OAAwC;AAChF,QAAM,eAAe,SAAS,OAAO,MAAM,WAAW;AACtD,UAAQ,IAAI,GAAG,MAAM,MAAM,KAAK,CAAC,KAAK,YAAY,EAAE;AACtD;AAWO,SAAS,YAAY,OAAqB;AAC/C,UAAQ,IAAI;AACZ,UAAQ,IAAI,MAAM,MAAM,KAAK,CAAC;AAC9B,UAAQ,IAAI,OAAO,MAAM,SAAI,OAAO,MAAM,MAAM,CAAC,CAAC;AACpD;AAKO,SAAS,aAAmB;AACjC,UAAQ,IAAI;AACd;AAYO,SAAS,UAAU,OAAiB,SAAiB,MAAM,QAAc;AAC9E,aAAW,QAAQ,OAAO;AACxB,YAAQ,IAAI,KAAK,MAAM,IAAI,IAAI,EAAE;AAAA,EACnC;AACF;AASO,SAAS,aAAa,SAAuB;AAClD,UAAQ,IAAI,GAAG,MAAM,OAAO,IAAI,OAAO,EAAE;AAC3C;AASO,SAAS,WAAW,SAAuB;AAChD,UAAQ,MAAM,GAAG,MAAM,KAAK,IAAI,MAAM,IAAI,OAAO,CAAC,EAAE;AACtD;AASO,SAAS,aAAa,SAAuB;AAClD,UAAQ,MAAM,GAAG,MAAM,OAAO,IAAI,MAAM,KAAK,OAAO,CAAC,EAAE;AACzD;AASO,SAAS,UAAU,SAAuB;AAC/C,UAAQ,IAAI,GAAG,MAAM,IAAI,IAAI,OAAO,EAAE;AACxC;AAYO,SAAS,WAAW,MAAuD;AAChF,QAAM,eAAe,KAAK,IAAI,GAAG,OAAO,KAAK,IAAI,EAAE,IAAI,OAAK,EAAE,MAAM,CAAC;AAErE,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,UAAM,YAAY,IAAI,OAAO,YAAY;AACzC,UAAM,eAAe,SAAS,OAAO,MAAM,WAAW;AACtD,YAAQ,IAAI,KAAK,MAAM,MAAM,SAAS,CAAC,KAAK,YAAY,EAAE;AAAA,EAC5D;AACF;AAQO,SAAS,UAAU,MAAqB;AAC7C,UAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAC3C;AASO,SAAS,UAAU,SAAiB,aAA2B;AACpE,UAAQ,IAAI,GAAG,MAAM,KAAK,QAAQ,MAAM,QAAQ,OAAO,CAAC,OAAO,WAAW,EAAE;AAC9E;AASO,SAAS,UAAU,KAAa,aAA2B;AAChE,UAAQ,IAAI,GAAG,MAAM,KAAK,IAAI,WAAW,KAAK,MAAM,IAAI,GAAG,CAAC,EAAE;AAChE;AAKO,SAAS,YAAY,OAAuB;AACjD,MAAI,UAAU,EAAG,QAAO;AAExB,QAAM,QAAQ,CAAC,KAAK,MAAM,MAAM,MAAM,IAAI;AAC1C,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;AAErD,SAAO,IAAI,QAAQ,KAAK,IAAI,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAC9D;AAKO,SAAS,eAAe,IAAoB;AACjD,MAAI,KAAK,IAAM,QAAO,GAAG,EAAE;AAC3B,MAAI,KAAK,IAAO,QAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AAChD,MAAI,KAAK,KAAS,QAAO,GAAG,KAAK,MAAM,KAAK,GAAK,CAAC,KAAK,KAAK,MAAO,KAAK,MAAS,GAAI,CAAC;AAEtF,QAAM,QAAQ,KAAK,MAAM,KAAK,IAAO;AACrC,QAAM,UAAU,KAAK,MAAO,KAAK,OAAW,GAAK;AACjD,SAAO,GAAG,KAAK,KAAK,OAAO;AAC7B;AAKO,SAAS,SAAS,KAAa,WAA2B;AAC/D,MAAI,IAAI,UAAU,UAAW,QAAO;AACpC,SAAO,IAAI,MAAM,GAAG,YAAY,CAAC,IAAI;AACvC;","names":[]}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loading Spinner Utilities
|
|
3
|
+
*
|
|
4
|
+
* Wraps ora spinner with consistent styling and error handling.
|
|
5
|
+
*/
|
|
6
|
+
import { Ora } from 'ora';
|
|
7
|
+
/**
|
|
8
|
+
* A single stage in a forgeProgress pipeline.
|
|
9
|
+
*/
|
|
10
|
+
export interface ForgeStage {
|
|
11
|
+
label: string;
|
|
12
|
+
fn: () => Promise<unknown>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Run multi-stage operations with a branded gradient progress bar.
|
|
16
|
+
*
|
|
17
|
+
* TTY: draws the bar, stage counter, and labeled stage row, redrawing
|
|
18
|
+
* after each stage completes. Non-TTY: falls back to `[i/N] label...`
|
|
19
|
+
* log lines. Suppressed entirely when `SPECFORGE_JSON=1` is set.
|
|
20
|
+
*/
|
|
21
|
+
export declare function forgeProgress(context: string, stages: ForgeStage[]): Promise<unknown[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Spinner instance type
|
|
24
|
+
*/
|
|
25
|
+
export type Spinner = Ora;
|
|
26
|
+
/**
|
|
27
|
+
* Create a new spinner with consistent styling
|
|
28
|
+
*
|
|
29
|
+
* @param text - Initial spinner text
|
|
30
|
+
* @returns Spinner instance
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* const spinner = createSpinner('Loading...');
|
|
35
|
+
* spinner.start();
|
|
36
|
+
*
|
|
37
|
+
* try {
|
|
38
|
+
* await doAsyncWork();
|
|
39
|
+
* spinner.succeed('Done!');
|
|
40
|
+
* } catch (error) {
|
|
41
|
+
* spinner.fail('Failed');
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function createSpinner(text: string): Spinner;
|
|
46
|
+
/**
|
|
47
|
+
* Run an async function with a spinner
|
|
48
|
+
*
|
|
49
|
+
* Automatically handles success/failure states.
|
|
50
|
+
*
|
|
51
|
+
* @param text - Spinner text while running
|
|
52
|
+
* @param fn - Async function to run
|
|
53
|
+
* @param successText - Text to show on success (optional)
|
|
54
|
+
* @returns Result of the async function
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const data = await withSpinner(
|
|
59
|
+
* 'Fetching tickets...',
|
|
60
|
+
* async () => await api.getTickets(),
|
|
61
|
+
* 'Tickets loaded'
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function withSpinner<T>(text: string, fn: () => Promise<T>, successText?: string): Promise<T>;
|
|
66
|
+
/**
|
|
67
|
+
* Run multiple async operations with progress
|
|
68
|
+
*
|
|
69
|
+
* @param operations - Array of operations with labels
|
|
70
|
+
* @returns Array of results
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const results = await withProgress([
|
|
75
|
+
* { label: 'Loading config', fn: loadConfig },
|
|
76
|
+
* { label: 'Fetching data', fn: fetchData },
|
|
77
|
+
* { label: 'Processing', fn: process },
|
|
78
|
+
* ]);
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function withProgress<T>(operations: Array<{
|
|
82
|
+
label: string;
|
|
83
|
+
fn: () => Promise<T>;
|
|
84
|
+
}>): Promise<T[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Create a manual spinner for custom control
|
|
87
|
+
*
|
|
88
|
+
* @param text - Initial text
|
|
89
|
+
* @returns Object with spinner control methods
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* const { start, update, succeed, fail } = manualSpinner('Loading');
|
|
94
|
+
*
|
|
95
|
+
* start();
|
|
96
|
+
* update('Loading step 1...');
|
|
97
|
+
* update('Loading step 2...');
|
|
98
|
+
* succeed('All loaded');
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function manualSpinner(text: string): {
|
|
102
|
+
start: () => Ora;
|
|
103
|
+
stop: () => Ora;
|
|
104
|
+
update: (newText: string) => void;
|
|
105
|
+
succeed: (finalText?: string) => void;
|
|
106
|
+
fail: (finalText?: string) => void;
|
|
107
|
+
warn: (finalText?: string) => void;
|
|
108
|
+
info: (finalText?: string) => void;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Check if spinners should be disabled (non-TTY)
|
|
112
|
+
*/
|
|
113
|
+
export declare function shouldDisableSpinner(): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Simple progress indicator for non-TTY environments
|
|
116
|
+
*
|
|
117
|
+
* @param text - Progress text
|
|
118
|
+
*/
|
|
119
|
+
export declare function progressLog(text: string): void;
|
|
120
|
+
//# sourceMappingURL=spinner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../../src/cli/ui/spinner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAY,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AA0C/B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,EAAE,GACnB,OAAO,CAAC,OAAO,EAAE,CAAC,CAmDpB;AAED;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,GAAG,CAAC;AAE1B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMnD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,CAAC,CAAC,CAYZ;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,UAAU,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC,GACzD,OAAO,CAAC,CAAC,EAAE,CAAC,CAad;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM;;;sBAMpB,MAAM;0BAGF,MAAM;uBAGT,MAAM;uBAGN,MAAM;uBAGN,MAAM;EAI5B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO9C"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import readline from "node:readline";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import { icons } from "./colors.js";
|
|
4
|
+
import { BRAND_PALETTE, ANSI_RESET, ANSI_BOLD } from "./banner.js";
|
|
5
|
+
const C = BRAND_PALETTE;
|
|
6
|
+
const GRADIENT_STOPS = [C.g1, C.g2, C.g3, C.g4, C.g5, C.g6, C.g7, C.g8, C.pink];
|
|
7
|
+
const BAR_WIDTH = 23;
|
|
8
|
+
const FORGE_RENDER_LINES = 5;
|
|
9
|
+
function renderGradientBar(filledCount, totalWidth) {
|
|
10
|
+
let out = "";
|
|
11
|
+
for (let i = 0; i < totalWidth; i++) {
|
|
12
|
+
if (i < filledCount) {
|
|
13
|
+
const stopIdx = Math.min(
|
|
14
|
+
GRADIENT_STOPS.length - 1,
|
|
15
|
+
Math.floor(i / Math.max(1, totalWidth - 1) * (GRADIENT_STOPS.length - 1))
|
|
16
|
+
);
|
|
17
|
+
out += `${GRADIENT_STOPS[stopIdx]}\u25B0${ANSI_RESET}`;
|
|
18
|
+
} else {
|
|
19
|
+
out += `${C.darkGray}\u25B1${ANSI_RESET}`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
function renderStageLabels(stages, activeIdx, allDone) {
|
|
25
|
+
const parts = [];
|
|
26
|
+
for (let i = 0; i < stages.length; i++) {
|
|
27
|
+
if (allDone || i < activeIdx) {
|
|
28
|
+
parts.push(`${C.g4}\u2713${ANSI_RESET} ${stages[i].label}`);
|
|
29
|
+
} else if (i === activeIdx) {
|
|
30
|
+
parts.push(`${C.pink}\u2692${ANSI_RESET} ${stages[i].label}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return parts.join(" ");
|
|
34
|
+
}
|
|
35
|
+
async function forgeProgress(context, stages) {
|
|
36
|
+
const results = [];
|
|
37
|
+
const silent = process.env.SPECFORGE_JSON === "1";
|
|
38
|
+
const tty = process.stdout.isTTY && !silent;
|
|
39
|
+
if (silent) {
|
|
40
|
+
for (const stage of stages) {
|
|
41
|
+
results.push(await stage.fn());
|
|
42
|
+
}
|
|
43
|
+
return results;
|
|
44
|
+
}
|
|
45
|
+
if (!tty) {
|
|
46
|
+
for (let i = 0; i < stages.length; i++) {
|
|
47
|
+
console.log(`[${i + 1}/${stages.length}] ${stages[i].label}...`);
|
|
48
|
+
results.push(await stages[i].fn());
|
|
49
|
+
}
|
|
50
|
+
return results;
|
|
51
|
+
}
|
|
52
|
+
const render = (activeIdx, allDone) => {
|
|
53
|
+
const current = allDone ? stages.length : activeIdx;
|
|
54
|
+
const filled = Math.round(current / stages.length * BAR_WIDTH);
|
|
55
|
+
const header = ` ${C.darkGray}${context}${ANSI_RESET} ${C.darkGray}\xB7${ANSI_RESET} ${ANSI_BOLD}${C.g5}${current}/${stages.length}${ANSI_RESET} ${C.darkGray}stages${ANSI_RESET}`;
|
|
56
|
+
const bar = ` ${renderGradientBar(filled, BAR_WIDTH)}`;
|
|
57
|
+
const labels = ` ${renderStageLabels(stages, activeIdx, allDone)}`;
|
|
58
|
+
return `${header}
|
|
59
|
+
|
|
60
|
+
${bar}
|
|
61
|
+
|
|
62
|
+
${labels}`;
|
|
63
|
+
};
|
|
64
|
+
process.stdout.write(render(0, false) + "\n");
|
|
65
|
+
for (let i = 0; i < stages.length; i++) {
|
|
66
|
+
try {
|
|
67
|
+
const result = await stages[i].fn();
|
|
68
|
+
results.push(result);
|
|
69
|
+
readline.moveCursor(process.stdout, 0, -FORGE_RENDER_LINES);
|
|
70
|
+
readline.clearScreenDown(process.stdout);
|
|
71
|
+
const done = i === stages.length - 1;
|
|
72
|
+
process.stdout.write(render(done ? i : i + 1, done) + "\n");
|
|
73
|
+
} catch (err) {
|
|
74
|
+
readline.moveCursor(process.stdout, 0, -FORGE_RENDER_LINES);
|
|
75
|
+
readline.clearScreenDown(process.stdout);
|
|
76
|
+
const failHeader = ` ${C.darkGray}${context}${ANSI_RESET} ${C.pink}failed${ANSI_RESET} ${C.darkGray}at${ANSI_RESET} ${ANSI_BOLD}${C.pink}${stages[i].label}${ANSI_RESET}`;
|
|
77
|
+
process.stdout.write(`${failHeader}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
`);
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return results;
|
|
87
|
+
}
|
|
88
|
+
function createSpinner(text) {
|
|
89
|
+
return ora({
|
|
90
|
+
text,
|
|
91
|
+
color: "cyan",
|
|
92
|
+
spinner: "dots"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async function withSpinner(text, fn, successText) {
|
|
96
|
+
const spinner = createSpinner(text);
|
|
97
|
+
spinner.start();
|
|
98
|
+
try {
|
|
99
|
+
const result = await fn();
|
|
100
|
+
spinner.succeed(successText || text.replace(/\.{3}$/, ""));
|
|
101
|
+
return result;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
spinner.fail(text.replace(/\.{3}$/, " failed"));
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function withProgress(operations) {
|
|
108
|
+
const results = [];
|
|
109
|
+
for (let i = 0; i < operations.length; i++) {
|
|
110
|
+
const { label, fn } = operations[i];
|
|
111
|
+
const progress = `[${i + 1}/${operations.length}]`;
|
|
112
|
+
const text = `${progress} ${label}...`;
|
|
113
|
+
const result = await withSpinner(text, fn);
|
|
114
|
+
results.push(result);
|
|
115
|
+
}
|
|
116
|
+
return results;
|
|
117
|
+
}
|
|
118
|
+
function manualSpinner(text) {
|
|
119
|
+
const spinner = createSpinner(text);
|
|
120
|
+
return {
|
|
121
|
+
start: () => spinner.start(),
|
|
122
|
+
stop: () => spinner.stop(),
|
|
123
|
+
update: (newText) => {
|
|
124
|
+
spinner.text = newText;
|
|
125
|
+
},
|
|
126
|
+
succeed: (finalText) => {
|
|
127
|
+
spinner.succeed(finalText);
|
|
128
|
+
},
|
|
129
|
+
fail: (finalText) => {
|
|
130
|
+
spinner.fail(finalText);
|
|
131
|
+
},
|
|
132
|
+
warn: (finalText) => {
|
|
133
|
+
spinner.warn(finalText);
|
|
134
|
+
},
|
|
135
|
+
info: (finalText) => {
|
|
136
|
+
spinner.info(finalText);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function shouldDisableSpinner() {
|
|
141
|
+
return !process.stdout.isTTY;
|
|
142
|
+
}
|
|
143
|
+
function progressLog(text) {
|
|
144
|
+
if (process.stdout.isTTY) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
console.log(`${icons.running} ${text}`);
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
createSpinner,
|
|
151
|
+
forgeProgress,
|
|
152
|
+
manualSpinner,
|
|
153
|
+
progressLog,
|
|
154
|
+
shouldDisableSpinner,
|
|
155
|
+
withProgress,
|
|
156
|
+
withSpinner
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/ui/spinner.ts"],"sourcesContent":["/**\n * Loading Spinner Utilities\n *\n * Wraps ora spinner with consistent styling and error handling.\n */\n\nimport readline from 'node:readline';\nimport ora, { Ora } from 'ora';\nimport { colors, icons } from './colors.js';\nimport { BRAND_PALETTE, ANSI_RESET, ANSI_BOLD } from './banner.js';\n\nconst C = BRAND_PALETTE;\n\nconst GRADIENT_STOPS = [C.g1, C.g2, C.g3, C.g4, C.g5, C.g6, C.g7, C.g8, C.pink] as const;\nconst BAR_WIDTH = 23;\nconst FORGE_RENDER_LINES = 5;\n\nfunction renderGradientBar(filledCount: number, totalWidth: number): string {\n let out = '';\n for (let i = 0; i < totalWidth; i++) {\n if (i < filledCount) {\n const stopIdx = Math.min(\n GRADIENT_STOPS.length - 1,\n Math.floor((i / Math.max(1, totalWidth - 1)) * (GRADIENT_STOPS.length - 1))\n );\n out += `${GRADIENT_STOPS[stopIdx]}▰${ANSI_RESET}`;\n } else {\n out += `${C.darkGray}▱${ANSI_RESET}`;\n }\n }\n return out;\n}\n\nfunction renderStageLabels(\n stages: ReadonlyArray<ForgeStage>,\n activeIdx: number,\n allDone: boolean\n): string {\n const parts: string[] = [];\n for (let i = 0; i < stages.length; i++) {\n if (allDone || i < activeIdx) {\n parts.push(`${C.g4}✓${ANSI_RESET} ${stages[i].label}`);\n } else if (i === activeIdx) {\n parts.push(`${C.pink}⚒${ANSI_RESET} ${stages[i].label}`);\n }\n }\n return parts.join(' ');\n}\n\n/**\n * A single stage in a forgeProgress pipeline.\n */\nexport interface ForgeStage {\n label: string;\n fn: () => Promise<unknown>;\n}\n\n/**\n * Run multi-stage operations with a branded gradient progress bar.\n *\n * TTY: draws the bar, stage counter, and labeled stage row, redrawing\n * after each stage completes. Non-TTY: falls back to `[i/N] label...`\n * log lines. Suppressed entirely when `SPECFORGE_JSON=1` is set.\n */\nexport async function forgeProgress(\n context: string,\n stages: ForgeStage[]\n): Promise<unknown[]> {\n const results: unknown[] = [];\n const silent = process.env.SPECFORGE_JSON === '1';\n const tty = process.stdout.isTTY && !silent;\n\n if (silent) {\n for (const stage of stages) {\n results.push(await stage.fn());\n }\n return results;\n }\n\n if (!tty) {\n for (let i = 0; i < stages.length; i++) {\n console.log(`[${i + 1}/${stages.length}] ${stages[i].label}...`);\n results.push(await stages[i].fn());\n }\n return results;\n }\n\n const render = (activeIdx: number, allDone: boolean): string => {\n const current = allDone ? stages.length : activeIdx;\n const filled = Math.round((current / stages.length) * BAR_WIDTH);\n const header = ` ${C.darkGray}${context}${ANSI_RESET} ${C.darkGray}·${ANSI_RESET} ${ANSI_BOLD}${C.g5}${current}/${stages.length}${ANSI_RESET} ${C.darkGray}stages${ANSI_RESET}`;\n const bar = ` ${renderGradientBar(filled, BAR_WIDTH)}`;\n const labels = ` ${renderStageLabels(stages, activeIdx, allDone)}`;\n return `${header}\\n\\n${bar}\\n\\n${labels}`;\n };\n\n // Initial frame with stage 0 active\n process.stdout.write(render(0, false) + '\\n');\n\n for (let i = 0; i < stages.length; i++) {\n try {\n const result = await stages[i].fn();\n results.push(result);\n\n readline.moveCursor(process.stdout, 0, -FORGE_RENDER_LINES);\n readline.clearScreenDown(process.stdout);\n const done = i === stages.length - 1;\n process.stdout.write(render(done ? i : i + 1, done) + '\\n');\n } catch (err) {\n readline.moveCursor(process.stdout, 0, -FORGE_RENDER_LINES);\n readline.clearScreenDown(process.stdout);\n const failHeader = ` ${C.darkGray}${context}${ANSI_RESET} ${C.pink}failed${ANSI_RESET} ${C.darkGray}at${ANSI_RESET} ${ANSI_BOLD}${C.pink}${stages[i].label}${ANSI_RESET}`;\n process.stdout.write(`${failHeader}\\n\\n\\n\\n\\n`);\n throw err;\n }\n }\n\n return results;\n}\n\n/**\n * Spinner instance type\n */\nexport type Spinner = Ora;\n\n/**\n * Create a new spinner with consistent styling\n *\n * @param text - Initial spinner text\n * @returns Spinner instance\n *\n * @example\n * ```typescript\n * const spinner = createSpinner('Loading...');\n * spinner.start();\n *\n * try {\n * await doAsyncWork();\n * spinner.succeed('Done!');\n * } catch (error) {\n * spinner.fail('Failed');\n * }\n * ```\n */\nexport function createSpinner(text: string): Spinner {\n return ora({\n text,\n color: 'cyan',\n spinner: 'dots',\n });\n}\n\n/**\n * Run an async function with a spinner\n *\n * Automatically handles success/failure states.\n *\n * @param text - Spinner text while running\n * @param fn - Async function to run\n * @param successText - Text to show on success (optional)\n * @returns Result of the async function\n *\n * @example\n * ```typescript\n * const data = await withSpinner(\n * 'Fetching tickets...',\n * async () => await api.getTickets(),\n * 'Tickets loaded'\n * );\n * ```\n */\nexport async function withSpinner<T>(\n text: string,\n fn: () => Promise<T>,\n successText?: string\n): Promise<T> {\n const spinner = createSpinner(text);\n spinner.start();\n\n try {\n const result = await fn();\n spinner.succeed(successText || text.replace(/\\.{3}$/, ''));\n return result;\n } catch (error) {\n spinner.fail(text.replace(/\\.{3}$/, ' failed'));\n throw error;\n }\n}\n\n/**\n * Run multiple async operations with progress\n *\n * @param operations - Array of operations with labels\n * @returns Array of results\n *\n * @example\n * ```typescript\n * const results = await withProgress([\n * { label: 'Loading config', fn: loadConfig },\n * { label: 'Fetching data', fn: fetchData },\n * { label: 'Processing', fn: process },\n * ]);\n * ```\n */\nexport async function withProgress<T>(\n operations: Array<{ label: string; fn: () => Promise<T> }>\n): Promise<T[]> {\n const results: T[] = [];\n\n for (let i = 0; i < operations.length; i++) {\n const { label, fn } = operations[i];\n const progress = `[${i + 1}/${operations.length}]`;\n const text = `${progress} ${label}...`;\n\n const result = await withSpinner(text, fn);\n results.push(result);\n }\n\n return results;\n}\n\n/**\n * Create a manual spinner for custom control\n *\n * @param text - Initial text\n * @returns Object with spinner control methods\n *\n * @example\n * ```typescript\n * const { start, update, succeed, fail } = manualSpinner('Loading');\n *\n * start();\n * update('Loading step 1...');\n * update('Loading step 2...');\n * succeed('All loaded');\n * ```\n */\nexport function manualSpinner(text: string) {\n const spinner = createSpinner(text);\n\n return {\n start: () => spinner.start(),\n stop: () => spinner.stop(),\n update: (newText: string) => {\n spinner.text = newText;\n },\n succeed: (finalText?: string) => {\n spinner.succeed(finalText);\n },\n fail: (finalText?: string) => {\n spinner.fail(finalText);\n },\n warn: (finalText?: string) => {\n spinner.warn(finalText);\n },\n info: (finalText?: string) => {\n spinner.info(finalText);\n },\n };\n}\n\n/**\n * Check if spinners should be disabled (non-TTY)\n */\nexport function shouldDisableSpinner(): boolean {\n return !process.stdout.isTTY;\n}\n\n/**\n * Simple progress indicator for non-TTY environments\n *\n * @param text - Progress text\n */\nexport function progressLog(text: string): void {\n if (process.stdout.isTTY) {\n // In TTY, use spinner instead\n return;\n }\n\n console.log(`${icons.running} ${text}`);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time Utilities
|
|
3
|
+
*
|
|
4
|
+
* Formatting utilities for time display in CLI output.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Format ISO timestamp to relative time string
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* - "just now" (< 1 minute)
|
|
11
|
+
* - "5m ago" (minutes)
|
|
12
|
+
* - "2h ago" (hours)
|
|
13
|
+
* - "3 days ago" (days)
|
|
14
|
+
* - "2 weeks ago" (weeks)
|
|
15
|
+
* - "1 month ago" (months)
|
|
16
|
+
* - "1 year ago" (years)
|
|
17
|
+
*
|
|
18
|
+
* @param isoString ISO 8601 timestamp string
|
|
19
|
+
* @returns Human-readable relative time
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatRelativeTime(isoString: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Format duration between two timestamps
|
|
24
|
+
*
|
|
25
|
+
* @param startIso Start timestamp (ISO 8601)
|
|
26
|
+
* @param endIso End timestamp (ISO 8601), defaults to now
|
|
27
|
+
* @returns Human-readable duration string
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatDuration(startIso: string, endIso?: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Format a timestamp for display
|
|
32
|
+
*
|
|
33
|
+
* @param isoString ISO 8601 timestamp string
|
|
34
|
+
* @param format Output format: 'relative', 'short', 'long'
|
|
35
|
+
* @returns Formatted time string
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatTimestamp(isoString: string, format?: 'relative' | 'short' | 'long'): string;
|
|
38
|
+
/**
|
|
39
|
+
* Check if a timestamp is within a given time window
|
|
40
|
+
*
|
|
41
|
+
* @param isoString ISO 8601 timestamp string
|
|
42
|
+
* @param windowMs Time window in milliseconds
|
|
43
|
+
* @returns True if within window
|
|
44
|
+
*/
|
|
45
|
+
export declare function isWithinTimeWindow(isoString: string, windowMs: number): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Get "time ago" for session display
|
|
48
|
+
* Similar to formatRelativeTime but more compact
|
|
49
|
+
*
|
|
50
|
+
* @param isoString ISO 8601 timestamp string
|
|
51
|
+
* @returns Compact relative time (e.g., "2h 15m ago")
|
|
52
|
+
*/
|
|
53
|
+
export declare function formatTimeAgo(isoString: string): string;
|
|
54
|
+
//# sourceMappingURL=time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../../src/cli/ui/time.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAgD5D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BxE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,UAAU,GAAG,OAAO,GAAG,MAAmB,GACjD,MAAM,CA6BR;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK/E;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA0BvD"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const MS_PER_MINUTE = 60 * 1e3;
|
|
2
|
+
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
3
|
+
const MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
4
|
+
const MS_PER_WEEK = 7 * MS_PER_DAY;
|
|
5
|
+
const MS_PER_MONTH = 30 * MS_PER_DAY;
|
|
6
|
+
const MS_PER_YEAR = 365 * MS_PER_DAY;
|
|
7
|
+
function formatRelativeTime(isoString) {
|
|
8
|
+
const date = new Date(isoString);
|
|
9
|
+
const now = /* @__PURE__ */ new Date();
|
|
10
|
+
const diffMs = now.getTime() - date.getTime();
|
|
11
|
+
if (diffMs < 0) {
|
|
12
|
+
return "in the future";
|
|
13
|
+
}
|
|
14
|
+
if (diffMs < MS_PER_MINUTE) {
|
|
15
|
+
return "just now";
|
|
16
|
+
}
|
|
17
|
+
if (diffMs < MS_PER_HOUR) {
|
|
18
|
+
const minutes = Math.floor(diffMs / MS_PER_MINUTE);
|
|
19
|
+
return `${minutes}m ago`;
|
|
20
|
+
}
|
|
21
|
+
if (diffMs < MS_PER_DAY) {
|
|
22
|
+
const hours = Math.floor(diffMs / MS_PER_HOUR);
|
|
23
|
+
return `${hours}h ago`;
|
|
24
|
+
}
|
|
25
|
+
if (diffMs < MS_PER_WEEK) {
|
|
26
|
+
const days = Math.floor(diffMs / MS_PER_DAY);
|
|
27
|
+
return days === 1 ? "1 day ago" : `${days} days ago`;
|
|
28
|
+
}
|
|
29
|
+
if (diffMs < MS_PER_MONTH) {
|
|
30
|
+
const weeks = Math.floor(diffMs / MS_PER_WEEK);
|
|
31
|
+
return weeks === 1 ? "1 week ago" : `${weeks} weeks ago`;
|
|
32
|
+
}
|
|
33
|
+
if (diffMs < MS_PER_YEAR) {
|
|
34
|
+
const months = Math.floor(diffMs / MS_PER_MONTH);
|
|
35
|
+
return months === 1 ? "1 month ago" : `${months} months ago`;
|
|
36
|
+
}
|
|
37
|
+
const years = Math.floor(diffMs / MS_PER_YEAR);
|
|
38
|
+
return years === 1 ? "1 year ago" : `${years} years ago`;
|
|
39
|
+
}
|
|
40
|
+
function formatDuration(startIso, endIso) {
|
|
41
|
+
const start = new Date(startIso);
|
|
42
|
+
const end = endIso ? new Date(endIso) : /* @__PURE__ */ new Date();
|
|
43
|
+
const diffMs = end.getTime() - start.getTime();
|
|
44
|
+
if (diffMs < 0) {
|
|
45
|
+
return "0m";
|
|
46
|
+
}
|
|
47
|
+
const days = Math.floor(diffMs / MS_PER_DAY);
|
|
48
|
+
const hours = Math.floor(diffMs % MS_PER_DAY / MS_PER_HOUR);
|
|
49
|
+
const minutes = Math.floor(diffMs % MS_PER_HOUR / MS_PER_MINUTE);
|
|
50
|
+
if (days > 0) {
|
|
51
|
+
return `${days}d ${hours}h`;
|
|
52
|
+
}
|
|
53
|
+
if (hours > 0) {
|
|
54
|
+
return `${hours}h ${minutes}m`;
|
|
55
|
+
}
|
|
56
|
+
if (minutes > 0) {
|
|
57
|
+
return `${minutes}m`;
|
|
58
|
+
}
|
|
59
|
+
return "just started";
|
|
60
|
+
}
|
|
61
|
+
function formatTimestamp(isoString, format = "relative") {
|
|
62
|
+
const date = new Date(isoString);
|
|
63
|
+
switch (format) {
|
|
64
|
+
case "relative":
|
|
65
|
+
return formatRelativeTime(isoString);
|
|
66
|
+
case "short":
|
|
67
|
+
return date.toLocaleString("en-US", {
|
|
68
|
+
month: "short",
|
|
69
|
+
day: "numeric",
|
|
70
|
+
hour: "numeric",
|
|
71
|
+
minute: "2-digit"
|
|
72
|
+
});
|
|
73
|
+
case "long":
|
|
74
|
+
return date.toLocaleString("en-US", {
|
|
75
|
+
year: "numeric",
|
|
76
|
+
month: "long",
|
|
77
|
+
day: "numeric",
|
|
78
|
+
hour: "numeric",
|
|
79
|
+
minute: "2-digit"
|
|
80
|
+
});
|
|
81
|
+
default:
|
|
82
|
+
return formatRelativeTime(isoString);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function isWithinTimeWindow(isoString, windowMs) {
|
|
86
|
+
const date = new Date(isoString);
|
|
87
|
+
const now = /* @__PURE__ */ new Date();
|
|
88
|
+
const diffMs = now.getTime() - date.getTime();
|
|
89
|
+
return diffMs >= 0 && diffMs <= windowMs;
|
|
90
|
+
}
|
|
91
|
+
function formatTimeAgo(isoString) {
|
|
92
|
+
const date = new Date(isoString);
|
|
93
|
+
const now = /* @__PURE__ */ new Date();
|
|
94
|
+
const diffMs = now.getTime() - date.getTime();
|
|
95
|
+
if (diffMs < 0) {
|
|
96
|
+
return "in the future";
|
|
97
|
+
}
|
|
98
|
+
if (diffMs < MS_PER_MINUTE) {
|
|
99
|
+
return "just now";
|
|
100
|
+
}
|
|
101
|
+
const days = Math.floor(diffMs / MS_PER_DAY);
|
|
102
|
+
const hours = Math.floor(diffMs % MS_PER_DAY / MS_PER_HOUR);
|
|
103
|
+
const minutes = Math.floor(diffMs % MS_PER_HOUR / MS_PER_MINUTE);
|
|
104
|
+
if (days > 0) {
|
|
105
|
+
return `${days}d ${hours}h ago`;
|
|
106
|
+
}
|
|
107
|
+
if (hours > 0) {
|
|
108
|
+
return `${hours}h ${minutes}m ago`;
|
|
109
|
+
}
|
|
110
|
+
return `${minutes}m ago`;
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
formatDuration,
|
|
114
|
+
formatRelativeTime,
|
|
115
|
+
formatTimeAgo,
|
|
116
|
+
formatTimestamp,
|
|
117
|
+
isWithinTimeWindow
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/ui/time.ts"],"sourcesContent":["/**\n * Time Utilities\n *\n * Formatting utilities for time display in CLI output.\n */\n\n/**\n * Time unit thresholds in milliseconds\n */\nconst MS_PER_MINUTE = 60 * 1000;\nconst MS_PER_HOUR = 60 * MS_PER_MINUTE;\nconst MS_PER_DAY = 24 * MS_PER_HOUR;\nconst MS_PER_WEEK = 7 * MS_PER_DAY;\nconst MS_PER_MONTH = 30 * MS_PER_DAY;\nconst MS_PER_YEAR = 365 * MS_PER_DAY;\n\n/**\n * Format ISO timestamp to relative time string\n *\n * Examples:\n * - \"just now\" (< 1 minute)\n * - \"5m ago\" (minutes)\n * - \"2h ago\" (hours)\n * - \"3 days ago\" (days)\n * - \"2 weeks ago\" (weeks)\n * - \"1 month ago\" (months)\n * - \"1 year ago\" (years)\n *\n * @param isoString ISO 8601 timestamp string\n * @returns Human-readable relative time\n */\nexport function formatRelativeTime(isoString: string): string {\n const date = new Date(isoString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n\n // Handle future dates\n if (diffMs < 0) {\n return 'in the future';\n }\n\n // Just now (< 1 minute)\n if (diffMs < MS_PER_MINUTE) {\n return 'just now';\n }\n\n // Minutes\n if (diffMs < MS_PER_HOUR) {\n const minutes = Math.floor(diffMs / MS_PER_MINUTE);\n return `${minutes}m ago`;\n }\n\n // Hours\n if (diffMs < MS_PER_DAY) {\n const hours = Math.floor(diffMs / MS_PER_HOUR);\n return `${hours}h ago`;\n }\n\n // Days\n if (diffMs < MS_PER_WEEK) {\n const days = Math.floor(diffMs / MS_PER_DAY);\n return days === 1 ? '1 day ago' : `${days} days ago`;\n }\n\n // Weeks\n if (diffMs < MS_PER_MONTH) {\n const weeks = Math.floor(diffMs / MS_PER_WEEK);\n return weeks === 1 ? '1 week ago' : `${weeks} weeks ago`;\n }\n\n // Months\n if (diffMs < MS_PER_YEAR) {\n const months = Math.floor(diffMs / MS_PER_MONTH);\n return months === 1 ? '1 month ago' : `${months} months ago`;\n }\n\n // Years\n const years = Math.floor(diffMs / MS_PER_YEAR);\n return years === 1 ? '1 year ago' : `${years} years ago`;\n}\n\n/**\n * Format duration between two timestamps\n *\n * @param startIso Start timestamp (ISO 8601)\n * @param endIso End timestamp (ISO 8601), defaults to now\n * @returns Human-readable duration string\n */\nexport function formatDuration(startIso: string, endIso?: string): string {\n const start = new Date(startIso);\n const end = endIso ? new Date(endIso) : new Date();\n const diffMs = end.getTime() - start.getTime();\n\n if (diffMs < 0) {\n return '0m';\n }\n\n const days = Math.floor(diffMs / MS_PER_DAY);\n const hours = Math.floor((diffMs % MS_PER_DAY) / MS_PER_HOUR);\n const minutes = Math.floor((diffMs % MS_PER_HOUR) / MS_PER_MINUTE);\n\n if (days > 0) {\n return `${days}d ${hours}h`;\n }\n\n if (hours > 0) {\n return `${hours}h ${minutes}m`;\n }\n\n if (minutes > 0) {\n return `${minutes}m`;\n }\n\n return 'just started';\n}\n\n/**\n * Format a timestamp for display\n *\n * @param isoString ISO 8601 timestamp string\n * @param format Output format: 'relative', 'short', 'long'\n * @returns Formatted time string\n */\nexport function formatTimestamp(\n isoString: string,\n format: 'relative' | 'short' | 'long' = 'relative'\n): string {\n const date = new Date(isoString);\n\n switch (format) {\n case 'relative':\n return formatRelativeTime(isoString);\n\n case 'short':\n // e.g., \"Jan 15, 2:30 PM\"\n return date.toLocaleString('en-US', {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n });\n\n case 'long':\n // e.g., \"January 15, 2024 at 2:30 PM\"\n return date.toLocaleString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n });\n\n default:\n return formatRelativeTime(isoString);\n }\n}\n\n/**\n * Check if a timestamp is within a given time window\n *\n * @param isoString ISO 8601 timestamp string\n * @param windowMs Time window in milliseconds\n * @returns True if within window\n */\nexport function isWithinTimeWindow(isoString: string, windowMs: number): boolean {\n const date = new Date(isoString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n return diffMs >= 0 && diffMs <= windowMs;\n}\n\n/**\n * Get \"time ago\" for session display\n * Similar to formatRelativeTime but more compact\n *\n * @param isoString ISO 8601 timestamp string\n * @returns Compact relative time (e.g., \"2h 15m ago\")\n */\nexport function formatTimeAgo(isoString: string): string {\n const date = new Date(isoString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n\n if (diffMs < 0) {\n return 'in the future';\n }\n\n if (diffMs < MS_PER_MINUTE) {\n return 'just now';\n }\n\n const days = Math.floor(diffMs / MS_PER_DAY);\n const hours = Math.floor((diffMs % MS_PER_DAY) / MS_PER_HOUR);\n const minutes = Math.floor((diffMs % MS_PER_HOUR) / MS_PER_MINUTE);\n\n if (days > 0) {\n return `${days}d ${hours}h ago`;\n }\n\n if (hours > 0) {\n return `${hours}h ${minutes}m ago`;\n }\n\n return `${minutes}m ago`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpecForge API Client
|
|
3
|
+
*
|
|
4
|
+
* HTTP client for communicating with the SpecForge API Gateway.
|
|
5
|
+
* Handles authentication, request/response formatting, and error handling.
|
|
6
|
+
*/
|
|
7
|
+
import { McpConfig } from '../config/index.js';
|
|
8
|
+
import type { ITransport, TransportOrigin } from '../transport/interface.js';
|
|
9
|
+
/**
|
|
10
|
+
* API response structure returned by SpecForge API
|
|
11
|
+
*/
|
|
12
|
+
export interface ApiResponse<T = unknown> {
|
|
13
|
+
/** Response data on success */
|
|
14
|
+
data?: T;
|
|
15
|
+
/** Error message on failure */
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Result of API key validation
|
|
20
|
+
*/
|
|
21
|
+
export interface ApiKeyValidationResult {
|
|
22
|
+
/** Whether the API key is valid */
|
|
23
|
+
valid: boolean;
|
|
24
|
+
/** User ID associated with the key (if valid) */
|
|
25
|
+
userId?: string;
|
|
26
|
+
/** Permissions granted to the key (if valid) */
|
|
27
|
+
permissions?: {
|
|
28
|
+
read?: boolean;
|
|
29
|
+
write?: boolean;
|
|
30
|
+
admin?: boolean;
|
|
31
|
+
};
|
|
32
|
+
/** Error message (if invalid) */
|
|
33
|
+
message?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Options for API requests
|
|
37
|
+
*/
|
|
38
|
+
export interface ApiRequestOptions {
|
|
39
|
+
/** Request timeout in milliseconds */
|
|
40
|
+
timeout?: number;
|
|
41
|
+
/** Additional headers to include */
|
|
42
|
+
headers?: Record<string, string>;
|
|
43
|
+
/** Signal for aborting the request */
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* API Client for SpecForge
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* import { getConfig } from './config/index.js';
|
|
52
|
+
* import { ApiClient } from './client/api-client.js';
|
|
53
|
+
*
|
|
54
|
+
* const config = getConfig();
|
|
55
|
+
* const client = new ApiClient(config);
|
|
56
|
+
*
|
|
57
|
+
* // Make an API call
|
|
58
|
+
* const projects = await client.call('list_projects', { userId: 'user123' });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare class ApiClient {
|
|
62
|
+
private config;
|
|
63
|
+
private transport;
|
|
64
|
+
private sessionId;
|
|
65
|
+
/**
|
|
66
|
+
* @param config - resolved MCP config (URL, key, debug)
|
|
67
|
+
* @param options - optional client options; `origin` sets the
|
|
68
|
+
* `X-SpecForge-Origin` header (M9.7), defaulting to `mcp` (agent traffic).
|
|
69
|
+
* The human CLI surface ({@link createCliClient} / the feedback command)
|
|
70
|
+
* passes `cli`.
|
|
71
|
+
*/
|
|
72
|
+
constructor(config: McpConfig, options?: {
|
|
73
|
+
origin?: TransportOrigin;
|
|
74
|
+
});
|
|
75
|
+
/**
|
|
76
|
+
* Get the underlying transport for direct use by tool handlers
|
|
77
|
+
*/
|
|
78
|
+
getTransport(): ITransport;
|
|
79
|
+
/**
|
|
80
|
+
* Get the session ID for this MCP connection
|
|
81
|
+
* Used for working context management
|
|
82
|
+
*/
|
|
83
|
+
getSessionId(): string;
|
|
84
|
+
/**
|
|
85
|
+
* Make an API call to SpecForge
|
|
86
|
+
*
|
|
87
|
+
* @param operation - Name of the operation to execute
|
|
88
|
+
* @param args - Arguments for the operation
|
|
89
|
+
* @param options - Optional request configuration
|
|
90
|
+
* @returns Promise resolving to the operation result
|
|
91
|
+
* @throws {Error} If the request fails or returns an error
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* // List projects
|
|
96
|
+
* const result = await client.call('list_projects');
|
|
97
|
+
*
|
|
98
|
+
* // Get a specific ticket
|
|
99
|
+
* const ticket = await client.call('get_ticket', { ticketId: 'ticket123' });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
call<T = unknown>(operation: string, args?: Record<string, unknown>, _options?: ApiRequestOptions): Promise<T>;
|
|
103
|
+
/**
|
|
104
|
+
* Validate the configured API key
|
|
105
|
+
*
|
|
106
|
+
* Makes a lightweight call to verify the API key is valid and
|
|
107
|
+
* returns the associated user information.
|
|
108
|
+
*
|
|
109
|
+
* @returns Promise resolving to validation result
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const result = await client.validateApiKey();
|
|
114
|
+
* if (result.valid) {
|
|
115
|
+
* console.log('Authenticated as:', result.userId);
|
|
116
|
+
* } else {
|
|
117
|
+
* console.error('Invalid API key:', result.message);
|
|
118
|
+
* }
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
validateApiKey(): Promise<ApiKeyValidationResult>;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/client/api-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG7E;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,+BAA+B;IAC/B,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,mCAAmC;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,WAAW,CAAC,EAAE;QACZ,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,sCAAsC;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAOD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;;;OAMG;gBACS,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,eAAe,CAAA;KAAO;IAYzE;;OAEG;IACH,YAAY,IAAI,UAAU;IAI1B;;;OAGG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;;;;;;;;;;;;;OAiBG;IACG,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAClC,QAAQ,GAAE,iBAAsB,GAC/B,OAAO,CAAC,CAAC,CAAC;IAkBb;;;;;;;;;;;;;;;;;OAiBG;IACG,cAAc,IAAI,OAAO,CAAC,sBAAsB,CAAC;CAwBxD"}
|