@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/lib/format.ts"],"sourcesContent":["/**\n * Format Utility Module\n *\n * Provides utilities for formatting response data as JSON or TOON format.\n * TOON (Token Optimized Object Notation) is a human-readable format that\n * uses fewer tokens than JSON while maintaining semantic clarity.\n *\n * @see MCI-014-03-format-utility-module\n */\n\nimport { encode as toonEncode, type EncodeOptions } from '@toon-format/toon';\n\n/**\n * Supported output formats for response data\n */\nexport type OutputFormat = 'json' | 'toon';\n\n/**\n * Default TOON formatting options optimized for SpecForge data\n *\n * These settings balance readability with token efficiency:\n * - indent: 2 spaces for clean nesting\n * - keyFolding: 'safe' collapses single-key chains (e.g., data.id instead of nested)\n */\nconst TOON_OPTIONS: EncodeOptions = {\n indent: 2,\n keyFolding: 'safe',\n};\n\n/**\n * Pre-process data for optimal TOON output.\n *\n * This function handles:\n * - Removing undefined values from objects\n * - Recursively processing nested structures\n * - Preserving null values (TOON represents these as ~)\n * - Handling arrays with mixed or complex content\n *\n * @param data - Data to preprocess\n * @returns Preprocessed data ready for TOON encoding\n *\n * @example\n * ```typescript\n * preprocessForToon({ a: 1, b: undefined, c: null })\n * // Returns: { a: 1, c: null } (undefined removed, null preserved)\n *\n * preprocessForToon({ nested: { deep: { value: \"test\" } } })\n * // Returns the same structure, recursively processed\n * ```\n */\nfunction preprocessForToon<T>(data: T): T {\n // Handle null/undefined at top level\n if (data === null || data === undefined) {\n return data;\n }\n\n // Handle arrays - recursively process each element\n if (Array.isArray(data)) {\n return data.map(preprocessForToon) as T;\n }\n\n // Handle objects - filter undefined and recursively process\n if (typeof data === 'object') {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n // Skip undefined values entirely\n if (value === undefined) {\n continue;\n }\n\n // Recursively process nested values\n result[key] = preprocessForToon(value);\n }\n\n return result as T;\n }\n\n // Return primitives unchanged\n return data;\n}\n\n/**\n * Format response data as JSON or TOON.\n *\n * JSON format (default) returns the original data object unchanged,\n * allowing the MCP protocol to handle JSON serialization.\n *\n * TOON format returns a human-readable string optimized for token efficiency.\n *\n * @param data - The data to format\n * @param format - Output format ('json' or 'toon'), defaults to 'json'\n * @returns Formatted data - original object for JSON, string for TOON\n *\n * @example\n * ```typescript\n * // JSON (default) - returns unchanged\n * formatResponse({ id: \"123\" }) // returns { id: \"123\" }\n *\n * // TOON - returns formatted string\n * formatResponse({ id: \"123\" }, 'toon')\n * // returns \"id: 123\"\n *\n * // Complex objects\n * formatResponse({\n * ticket: { id: \"T-001\", title: \"Task\", status: \"done\" }\n * }, 'toon')\n * // returns:\n * // ticket.id: T-001\n * // ticket.title: Task\n * // ticket.status: done\n * ```\n */\nexport function formatResponse<T>(data: T, format: OutputFormat = 'json'): string | T {\n if (format === 'json') {\n return data;\n }\n\n try {\n // Preprocess data to handle undefined values and complex structures\n const processed = preprocessForToon(data);\n return toonEncode(processed, TOON_OPTIONS);\n } catch (error) {\n // Fallback to JSON on TOON encoding errors\n console.error('TOON formatting failed, falling back to JSON:', error);\n return data;\n }\n}\n\n/**\n * Encode any value as a TOON string — always returns a string (M9 §2.6d.2).\n *\n * Unlike {@link formatResponse} (which returns the original object for the\n * `'json'` branch), `formatToon` is the canonical wire encoder for the local\n * MCP server: agent-bound responses over stdio are ALWAYS TOON. On encoder\n * failure it logs once and falls back to pretty JSON — failing to encode a\n * response is worse than slightly larger output, so it never throws.\n *\n * @param value - The value to encode (success envelope, error envelope, or stub)\n * @returns A TOON string (or JSON string on encoder exception)\n */\nexport function formatToon<T>(value: T): string {\n try {\n const processed = preprocessForToon(value);\n return toonEncode(processed, TOON_OPTIONS);\n } catch (err) {\n console.error(\n `[toon] encode failed for value type ${typeof value}; falling back to JSON`,\n err\n );\n return JSON.stringify(value, null, 2);\n }\n}\n\n/**\n * Format an array of items with optional TOON tabular formatting\n *\n * For TOON format, arrays with homogeneous objects can be rendered\n * in a more compact tabular format. This is useful for lists of\n * tickets, epics, etc.\n *\n * @param items - Array of items to format\n * @param format - Output format\n * @returns Formatted items\n *\n * @example\n * ```typescript\n * formatArrayResponse([\n * { id: '1', status: 'done' },\n * { id: '2', status: 'pending' }\n * ], 'toon')\n * // Returns:\n * // []:\n * // - id: 1\n * // status: done\n * // - id: 2\n * // status: pending\n * ```\n */\nexport function formatArrayResponse<T>(items: T[], format: OutputFormat = 'json'): string | T[] {\n return formatResponse(items, format);\n}\n\n/**\n * Format an error response with consistent structure\n *\n * @param error - Error message or Error object\n * @param code - Optional error code\n * @param format - Output format\n * @returns Formatted error response\n *\n * @example\n * ```typescript\n * formatErrorResponse('Not found', 'NOT_FOUND', 'toon')\n * // Returns:\n * // error: Not found\n * // code: NOT_FOUND\n * ```\n */\nexport function formatErrorResponse(\n error: string | Error,\n code?: string,\n format: OutputFormat = 'json'\n): string | { error: string; code?: string } {\n const errorMessage = error instanceof Error ? error.message : error;\n const response: { error: string; code?: string } = { error: errorMessage };\n if (code) {\n response.code = code;\n }\n return formatResponse(response, format);\n}\n\n/**\n * Format a success response with optional data\n *\n * @param message - Success message\n * @param data - Optional additional data\n * @param format - Output format\n * @returns Formatted success response\n *\n * @example\n * ```typescript\n * formatSuccessResponse('Created', { id: '123' }, 'toon')\n * // Returns:\n * // success: true\n * // message: Created\n * // id: 123\n * ```\n */\nexport function formatSuccessResponse<T extends Record<string, unknown>>(\n message: string,\n data?: T,\n format: OutputFormat = 'json'\n): string | { success: true; message: string } & T {\n const response = { success: true as const, message, ...data } as { success: true; message: string } & T;\n return formatResponse(response, format);\n}\n"],"mappings":"AAUA,SAAS,UAAU,kBAAsC;AAczD,MAAM,eAA8B;AAAA,EAClC,QAAQ;AAAA,EACR,YAAY;AACd;AAuBA,SAAS,kBAAqB,MAAY;AAExC,MAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,WAAO;AAAA,EACT;AAGA,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,IAAI,iBAAiB;AAAA,EACnC;AAGA,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,SAAkC,CAAC;AAEzC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAA+B,GAAG;AAE1E,UAAI,UAAU,QAAW;AACvB;AAAA,MACF;AAGA,aAAO,GAAG,IAAI,kBAAkB,KAAK;AAAA,IACvC;AAEA,WAAO;AAAA,EACT;AAGA,SAAO;AACT;AAiCO,SAAS,eAAkB,MAAS,SAAuB,QAAoB;AACpF,MAAI,WAAW,QAAQ;AACrB,WAAO;AAAA,EACT;AAEA,MAAI;AAEF,UAAM,YAAY,kBAAkB,IAAI;AACxC,WAAO,WAAW,WAAW,YAAY;AAAA,EAC3C,SAAS,OAAO;AAEd,YAAQ,MAAM,iDAAiD,KAAK;AACpE,WAAO;AAAA,EACT;AACF;AAcO,SAAS,WAAc,OAAkB;AAC9C,MAAI;AACF,UAAM,YAAY,kBAAkB,KAAK;AACzC,WAAO,WAAW,WAAW,YAAY;AAAA,EAC3C,SAAS,KAAK;AACZ,YAAQ;AAAA,MACN,uCAAuC,OAAO,KAAK;AAAA,MACnD;AAAA,IACF;AACA,WAAO,KAAK,UAAU,OAAO,MAAM,CAAC;AAAA,EACtC;AACF;AA2BO,SAAS,oBAAuB,OAAY,SAAuB,QAAsB;AAC9F,SAAO,eAAe,OAAO,MAAM;AACrC;AAkBO,SAAS,oBACd,OACA,MACA,SAAuB,QACoB;AAC3C,QAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,QAAM,WAA6C,EAAE,OAAO,aAAa;AACzE,MAAI,MAAM;AACR,aAAS,OAAO;AAAA,EAClB;AACA,SAAO,eAAe,UAAU,MAAM;AACxC;AAmBO,SAAS,sBACd,SACA,MACA,SAAuB,QAC0B;AACjD,QAAM,WAAW,EAAE,SAAS,MAAe,SAAS,GAAG,KAAK;AAC5D,SAAO,eAAe,UAAU,MAAM;AACxC;","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library Utilities
|
|
3
|
+
*
|
|
4
|
+
* Exports shared utility modules for the MCP server.
|
|
5
|
+
*/
|
|
6
|
+
export { type OutputFormat, formatResponse, formatArrayResponse, formatErrorResponse, formatSuccessResponse, } from './format.js';
|
|
7
|
+
export { type WorkflowStep, type WorkflowDefinition, type ReviewChecklistItem, type ReviewWorkflowDefinition, type FinalizeWorkflowDefinition, type WorkflowType, CREATION_WORKFLOW, IMPLEMENTATION_WORKFLOW, REVIEW_WORKFLOW, FINALIZE_WORKFLOW, WORKFLOW_TYPES, getWorkflowDefinition, } from './workflow-definitions.js';
|
|
8
|
+
export { type SpecSummary, type EpicSummary, type TicketSummary, type DependencyEdge, type Strategy, type StrategyRecommendation, CROSS_DEP_THRESHOLD, SINGLE_SESSION_MAX_TICKETS, analyzeStrategy, } from './strategy-analyzer.js';
|
|
9
|
+
export { type MonorepoTool, type MonorepoInfo, detectMonorepo, } from './monorepo-detector.js';
|
|
10
|
+
export { type TicketContext, type EpicContext, type SpecContext, type WorkspaceContext, type ConventionsContext, type ValidationCommands as PromptValidationCommands, generateWorkerPrompt, generateValidatorPrompt, } from './prompt-generator.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,YAAY,EACjB,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,YAAY,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,sBAAsB,EAC3B,mBAAmB,EACnB,0BAA0B,EAC1B,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,cAAc,GACf,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,IAAI,wBAAwB,EACnD,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatResponse,
|
|
3
|
+
formatArrayResponse,
|
|
4
|
+
formatErrorResponse,
|
|
5
|
+
formatSuccessResponse
|
|
6
|
+
} from "./format.js";
|
|
7
|
+
import {
|
|
8
|
+
CREATION_WORKFLOW,
|
|
9
|
+
IMPLEMENTATION_WORKFLOW,
|
|
10
|
+
REVIEW_WORKFLOW,
|
|
11
|
+
FINALIZE_WORKFLOW,
|
|
12
|
+
WORKFLOW_TYPES,
|
|
13
|
+
getWorkflowDefinition
|
|
14
|
+
} from "./workflow-definitions.js";
|
|
15
|
+
import {
|
|
16
|
+
CROSS_DEP_THRESHOLD,
|
|
17
|
+
SINGLE_SESSION_MAX_TICKETS,
|
|
18
|
+
analyzeStrategy
|
|
19
|
+
} from "./strategy-analyzer.js";
|
|
20
|
+
import {
|
|
21
|
+
detectMonorepo
|
|
22
|
+
} from "./monorepo-detector.js";
|
|
23
|
+
import {
|
|
24
|
+
generateWorkerPrompt,
|
|
25
|
+
generateValidatorPrompt
|
|
26
|
+
} from "./prompt-generator.js";
|
|
27
|
+
export {
|
|
28
|
+
CREATION_WORKFLOW,
|
|
29
|
+
CROSS_DEP_THRESHOLD,
|
|
30
|
+
FINALIZE_WORKFLOW,
|
|
31
|
+
IMPLEMENTATION_WORKFLOW,
|
|
32
|
+
REVIEW_WORKFLOW,
|
|
33
|
+
SINGLE_SESSION_MAX_TICKETS,
|
|
34
|
+
WORKFLOW_TYPES,
|
|
35
|
+
analyzeStrategy,
|
|
36
|
+
detectMonorepo,
|
|
37
|
+
formatArrayResponse,
|
|
38
|
+
formatErrorResponse,
|
|
39
|
+
formatResponse,
|
|
40
|
+
formatSuccessResponse,
|
|
41
|
+
generateValidatorPrompt,
|
|
42
|
+
generateWorkerPrompt,
|
|
43
|
+
getWorkflowDefinition
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/index.ts"],"sourcesContent":["/**\n * Library Utilities\n *\n * Exports shared utility modules for the MCP server.\n */\n\nexport {\n type OutputFormat,\n formatResponse,\n formatArrayResponse,\n formatErrorResponse,\n formatSuccessResponse,\n} from './format.js';\n\nexport {\n type WorkflowStep,\n type WorkflowDefinition,\n type ReviewChecklistItem,\n type ReviewWorkflowDefinition,\n type FinalizeWorkflowDefinition,\n type WorkflowType,\n CREATION_WORKFLOW,\n IMPLEMENTATION_WORKFLOW,\n REVIEW_WORKFLOW,\n FINALIZE_WORKFLOW,\n WORKFLOW_TYPES,\n getWorkflowDefinition,\n} from './workflow-definitions.js';\n\nexport {\n type SpecSummary,\n type EpicSummary,\n type TicketSummary,\n type DependencyEdge,\n type Strategy,\n type StrategyRecommendation,\n CROSS_DEP_THRESHOLD,\n SINGLE_SESSION_MAX_TICKETS,\n analyzeStrategy,\n} from './strategy-analyzer.js';\n\nexport {\n type MonorepoTool,\n type MonorepoInfo,\n detectMonorepo,\n} from './monorepo-detector.js';\n\nexport {\n type TicketContext,\n type EpicContext,\n type SpecContext,\n type WorkspaceContext,\n type ConventionsContext,\n type ValidationCommands as PromptValidationCommands,\n generateWorkerPrompt,\n generateValidatorPrompt,\n} from './prompt-generator.js';\n"],"mappings":"AAMA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EAOE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EAOE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP;AAAA,EAGE;AAAA,OACK;AAEP;AAAA,EAOE;AAAA,EACA;AAAA,OACK;","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monorepo Detection Utility
|
|
3
|
+
*
|
|
4
|
+
* Detects monorepo setups by checking for tool-specific config files.
|
|
5
|
+
* Returns the detected tool, workspace globs, resolved workspace paths,
|
|
6
|
+
* and package manager. Returns null for single-project repos.
|
|
7
|
+
*
|
|
8
|
+
* Detection priority: turborepo → nx → lerna → pnpm → npm → yarn
|
|
9
|
+
*
|
|
10
|
+
* This runs locally (MCP stdio transport only). For SSE/cloud transport
|
|
11
|
+
* the caller should skip detection and return null.
|
|
12
|
+
*/
|
|
13
|
+
export type MonorepoTool = 'turborepo' | 'nx' | 'lerna' | 'pnpm' | 'npm' | 'yarn';
|
|
14
|
+
export interface MonorepoInfo {
|
|
15
|
+
/** Detected monorepo tool */
|
|
16
|
+
type: MonorepoTool;
|
|
17
|
+
/** Raw workspace glob patterns (e.g., ["packages/*", "apps/*"]) */
|
|
18
|
+
workspaces: string[];
|
|
19
|
+
/** Resolved directory paths that match the workspace globs */
|
|
20
|
+
resolvedPaths: string[];
|
|
21
|
+
/** Detected package manager */
|
|
22
|
+
packageManager: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Detect monorepo configuration from a project root directory.
|
|
26
|
+
*
|
|
27
|
+
* @param projectRoot Absolute or relative path to the project root.
|
|
28
|
+
* @returns MonorepoInfo if a monorepo setup is detected, null otherwise.
|
|
29
|
+
*/
|
|
30
|
+
export declare function detectMonorepo(projectRoot: string): MonorepoInfo | null;
|
|
31
|
+
//# sourceMappingURL=monorepo-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monorepo-detector.d.ts","sourceRoot":"","sources":["../../src/lib/monorepo-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAElF,MAAM,WAAW,YAAY;IAC3B,6BAA6B;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,mEAAmE;IACnE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;CACxB;AAMD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA2BvE"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { minimatch } from "minimatch";
|
|
4
|
+
function detectMonorepo(projectRoot) {
|
|
5
|
+
if (!fs.existsSync(projectRoot)) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const rootStats = fs.statSync(projectRoot);
|
|
9
|
+
if (!rootStats.isDirectory()) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const detectors = [
|
|
13
|
+
() => detectTurborepo(projectRoot),
|
|
14
|
+
() => detectNx(projectRoot),
|
|
15
|
+
() => detectLerna(projectRoot),
|
|
16
|
+
() => detectPnpmWorkspaces(projectRoot),
|
|
17
|
+
() => detectNpmYarnWorkspaces(projectRoot)
|
|
18
|
+
];
|
|
19
|
+
for (const detect of detectors) {
|
|
20
|
+
const result = detect();
|
|
21
|
+
if (result !== null) {
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function detectTurborepo(root) {
|
|
28
|
+
const turboPath = path.join(root, "turbo.json");
|
|
29
|
+
if (!fs.existsSync(turboPath)) return null;
|
|
30
|
+
const workspaces = readPackageJsonWorkspaces(root);
|
|
31
|
+
if (workspaces.length === 0) return null;
|
|
32
|
+
return {
|
|
33
|
+
type: "turborepo",
|
|
34
|
+
workspaces,
|
|
35
|
+
resolvedPaths: resolveWorkspaceGlobs(root, workspaces),
|
|
36
|
+
packageManager: detectPackageManager(root)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function detectNx(root) {
|
|
40
|
+
const nxPath = path.join(root, "nx.json");
|
|
41
|
+
if (!fs.existsSync(nxPath)) return null;
|
|
42
|
+
let workspaces = readPackageJsonWorkspaces(root);
|
|
43
|
+
if (workspaces.length === 0) {
|
|
44
|
+
const defaults = ["packages/*", "apps/*", "libs/*"];
|
|
45
|
+
const existing = defaults.filter((g) => {
|
|
46
|
+
const prefix = g.replace("/*", "");
|
|
47
|
+
return fs.existsSync(path.join(root, prefix));
|
|
48
|
+
});
|
|
49
|
+
if (existing.length === 0) return null;
|
|
50
|
+
workspaces = existing;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
type: "nx",
|
|
54
|
+
workspaces,
|
|
55
|
+
resolvedPaths: resolveWorkspaceGlobs(root, workspaces),
|
|
56
|
+
packageManager: detectPackageManager(root)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function detectLerna(root) {
|
|
60
|
+
const lernaPath = path.join(root, "lerna.json");
|
|
61
|
+
if (!fs.existsSync(lernaPath)) return null;
|
|
62
|
+
try {
|
|
63
|
+
const lernaConfig = JSON.parse(fs.readFileSync(lernaPath, "utf-8"));
|
|
64
|
+
let workspaces = lernaConfig.packages ?? [];
|
|
65
|
+
if (workspaces.length === 0) {
|
|
66
|
+
workspaces = readPackageJsonWorkspaces(root);
|
|
67
|
+
}
|
|
68
|
+
if (workspaces.length === 0) {
|
|
69
|
+
workspaces = ["packages/*"];
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
type: "lerna",
|
|
73
|
+
workspaces,
|
|
74
|
+
resolvedPaths: resolveWorkspaceGlobs(root, workspaces),
|
|
75
|
+
packageManager: detectPackageManager(root)
|
|
76
|
+
};
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function detectPnpmWorkspaces(root) {
|
|
82
|
+
const yamlPath = path.join(root, "pnpm-workspace.yaml");
|
|
83
|
+
if (!fs.existsSync(yamlPath)) return null;
|
|
84
|
+
try {
|
|
85
|
+
const content = fs.readFileSync(yamlPath, "utf-8");
|
|
86
|
+
const workspaces = parsePnpmWorkspaceYaml(content);
|
|
87
|
+
if (workspaces.length === 0) return null;
|
|
88
|
+
return {
|
|
89
|
+
type: "pnpm",
|
|
90
|
+
workspaces,
|
|
91
|
+
resolvedPaths: resolveWorkspaceGlobs(root, workspaces),
|
|
92
|
+
packageManager: "pnpm"
|
|
93
|
+
};
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function detectNpmYarnWorkspaces(root) {
|
|
99
|
+
const workspaces = readPackageJsonWorkspaces(root);
|
|
100
|
+
if (workspaces.length === 0) return null;
|
|
101
|
+
const isYarn = fs.existsSync(path.join(root, "yarn.lock"));
|
|
102
|
+
return {
|
|
103
|
+
type: isYarn ? "yarn" : "npm",
|
|
104
|
+
workspaces,
|
|
105
|
+
resolvedPaths: resolveWorkspaceGlobs(root, workspaces),
|
|
106
|
+
packageManager: isYarn ? "yarn" : "npm"
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function readPackageJsonWorkspaces(root) {
|
|
110
|
+
try {
|
|
111
|
+
const pkgPath = path.join(root, "package.json");
|
|
112
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
113
|
+
const workspaces = pkg.workspaces;
|
|
114
|
+
if (Array.isArray(workspaces)) {
|
|
115
|
+
return workspaces;
|
|
116
|
+
}
|
|
117
|
+
if (workspaces && Array.isArray(workspaces.packages)) {
|
|
118
|
+
return workspaces.packages;
|
|
119
|
+
}
|
|
120
|
+
return [];
|
|
121
|
+
} catch {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function parsePnpmWorkspaceYaml(content) {
|
|
126
|
+
const lines = content.split("\n");
|
|
127
|
+
const workspaces = [];
|
|
128
|
+
let inPackages = false;
|
|
129
|
+
for (const line of lines) {
|
|
130
|
+
const trimmed = line.trim();
|
|
131
|
+
if (trimmed === "packages:") {
|
|
132
|
+
inPackages = true;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (inPackages && trimmed.length > 0 && !trimmed.startsWith("-") && !trimmed.startsWith("#")) {
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
if (inPackages && trimmed.startsWith("-")) {
|
|
139
|
+
const value = trimmed.slice(1).trim().replace(/^['"]/, "").replace(/['"]$/, "");
|
|
140
|
+
if (value) {
|
|
141
|
+
workspaces.push(value);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return workspaces;
|
|
146
|
+
}
|
|
147
|
+
function resolveWorkspaceGlobs(root, globs) {
|
|
148
|
+
const resolved = [];
|
|
149
|
+
for (const glob of globs) {
|
|
150
|
+
if (glob.startsWith("!")) continue;
|
|
151
|
+
const parts = glob.split("/");
|
|
152
|
+
const prefix = parts.slice(0, -1).join("/");
|
|
153
|
+
const prefixDir = prefix ? path.join(root, prefix) : root;
|
|
154
|
+
if (!fs.existsSync(prefixDir) || !fs.statSync(prefixDir).isDirectory()) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const entries = fs.readdirSync(prefixDir, { withFileTypes: true });
|
|
159
|
+
for (const entry of entries) {
|
|
160
|
+
if (!entry.isDirectory()) continue;
|
|
161
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
162
|
+
if (minimatch(relativePath, glob)) {
|
|
163
|
+
resolved.push(relativePath);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return resolved.sort();
|
|
170
|
+
}
|
|
171
|
+
function detectPackageManager(root) {
|
|
172
|
+
try {
|
|
173
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
|
|
174
|
+
if (typeof pkg.packageManager === "string") {
|
|
175
|
+
const name = pkg.packageManager.split("@")[0];
|
|
176
|
+
if (name) return name;
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
if (fs.existsSync(path.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
181
|
+
if (fs.existsSync(path.join(root, "yarn.lock"))) return "yarn";
|
|
182
|
+
if (fs.existsSync(path.join(root, "bun.lockb"))) return "bun";
|
|
183
|
+
return "npm";
|
|
184
|
+
}
|
|
185
|
+
export {
|
|
186
|
+
detectMonorepo
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=monorepo-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/monorepo-detector.ts"],"sourcesContent":["/**\n * Monorepo Detection Utility\n *\n * Detects monorepo setups by checking for tool-specific config files.\n * Returns the detected tool, workspace globs, resolved workspace paths,\n * and package manager. Returns null for single-project repos.\n *\n * Detection priority: turborepo → nx → lerna → pnpm → npm → yarn\n *\n * This runs locally (MCP stdio transport only). For SSE/cloud transport\n * the caller should skip detection and return null.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { minimatch } from 'minimatch';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type MonorepoTool = 'turborepo' | 'nx' | 'lerna' | 'pnpm' | 'npm' | 'yarn';\n\nexport interface MonorepoInfo {\n /** Detected monorepo tool */\n type: MonorepoTool;\n /** Raw workspace glob patterns (e.g., [\"packages/*\", \"apps/*\"]) */\n workspaces: string[];\n /** Resolved directory paths that match the workspace globs */\n resolvedPaths: string[];\n /** Detected package manager */\n packageManager: string;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Detect monorepo configuration from a project root directory.\n *\n * @param projectRoot Absolute or relative path to the project root.\n * @returns MonorepoInfo if a monorepo setup is detected, null otherwise.\n */\nexport function detectMonorepo(projectRoot: string): MonorepoInfo | null {\n if (!fs.existsSync(projectRoot)) {\n return null;\n }\n\n const rootStats = fs.statSync(projectRoot);\n if (!rootStats.isDirectory()) {\n return null;\n }\n\n // Try each detector in priority order.\n const detectors: Array<() => MonorepoInfo | null> = [\n () => detectTurborepo(projectRoot),\n () => detectNx(projectRoot),\n () => detectLerna(projectRoot),\n () => detectPnpmWorkspaces(projectRoot),\n () => detectNpmYarnWorkspaces(projectRoot),\n ];\n\n for (const detect of detectors) {\n const result = detect();\n if (result !== null) {\n return result;\n }\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Detectors\n// ---------------------------------------------------------------------------\n\nfunction detectTurborepo(root: string): MonorepoInfo | null {\n const turboPath = path.join(root, 'turbo.json');\n if (!fs.existsSync(turboPath)) return null;\n\n // Turborepo reads workspaces from package.json, not turbo.json.\n const workspaces = readPackageJsonWorkspaces(root);\n if (workspaces.length === 0) return null;\n\n return {\n type: 'turborepo',\n workspaces,\n resolvedPaths: resolveWorkspaceGlobs(root, workspaces),\n packageManager: detectPackageManager(root),\n };\n}\n\nfunction detectNx(root: string): MonorepoInfo | null {\n const nxPath = path.join(root, 'nx.json');\n if (!fs.existsSync(nxPath)) return null;\n\n // Nx can use package.json workspaces or its own project structure.\n let workspaces = readPackageJsonWorkspaces(root);\n\n // If no workspaces in package.json, check for common Nx patterns.\n if (workspaces.length === 0) {\n const defaults = ['packages/*', 'apps/*', 'libs/*'];\n const existing = defaults.filter(g => {\n const prefix = g.replace('/*', '');\n return fs.existsSync(path.join(root, prefix));\n });\n if (existing.length === 0) return null;\n workspaces = existing;\n }\n\n return {\n type: 'nx',\n workspaces,\n resolvedPaths: resolveWorkspaceGlobs(root, workspaces),\n packageManager: detectPackageManager(root),\n };\n}\n\nfunction detectLerna(root: string): MonorepoInfo | null {\n const lernaPath = path.join(root, 'lerna.json');\n if (!fs.existsSync(lernaPath)) return null;\n\n try {\n const lernaConfig = JSON.parse(fs.readFileSync(lernaPath, 'utf-8'));\n let workspaces: string[] = lernaConfig.packages ?? [];\n\n // Fallback to package.json workspaces if lerna.json has no packages.\n if (workspaces.length === 0) {\n workspaces = readPackageJsonWorkspaces(root);\n }\n\n if (workspaces.length === 0) {\n // Lerna default\n workspaces = ['packages/*'];\n }\n\n return {\n type: 'lerna',\n workspaces,\n resolvedPaths: resolveWorkspaceGlobs(root, workspaces),\n packageManager: detectPackageManager(root),\n };\n } catch {\n return null;\n }\n}\n\nfunction detectPnpmWorkspaces(root: string): MonorepoInfo | null {\n const yamlPath = path.join(root, 'pnpm-workspace.yaml');\n if (!fs.existsSync(yamlPath)) return null;\n\n try {\n const content = fs.readFileSync(yamlPath, 'utf-8');\n const workspaces = parsePnpmWorkspaceYaml(content);\n if (workspaces.length === 0) return null;\n\n return {\n type: 'pnpm',\n workspaces,\n resolvedPaths: resolveWorkspaceGlobs(root, workspaces),\n packageManager: 'pnpm',\n };\n } catch {\n return null;\n }\n}\n\nfunction detectNpmYarnWorkspaces(root: string): MonorepoInfo | null {\n const workspaces = readPackageJsonWorkspaces(root);\n if (workspaces.length === 0) return null;\n\n const isYarn = fs.existsSync(path.join(root, 'yarn.lock'));\n return {\n type: isYarn ? 'yarn' : 'npm',\n workspaces,\n resolvedPaths: resolveWorkspaceGlobs(root, workspaces),\n packageManager: isYarn ? 'yarn' : 'npm',\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read the \"workspaces\" field from root package.json.\n * Handles both array format and object format ({ packages: [...] }).\n */\nfunction readPackageJsonWorkspaces(root: string): string[] {\n try {\n const pkgPath = path.join(root, 'package.json');\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n const workspaces = pkg.workspaces;\n\n if (Array.isArray(workspaces)) {\n return workspaces;\n }\n if (workspaces && Array.isArray(workspaces.packages)) {\n return workspaces.packages;\n }\n return [];\n } catch {\n return [];\n }\n}\n\n/**\n * Parse pnpm-workspace.yaml without a YAML library.\n * Extracts entries under the `packages:` key.\n *\n * Expected format:\n * packages:\n * - 'packages/*'\n * - 'apps/*'\n */\nfunction parsePnpmWorkspaceYaml(content: string): string[] {\n const lines = content.split('\\n');\n const workspaces: string[] = [];\n let inPackages = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n if (trimmed === 'packages:') {\n inPackages = true;\n continue;\n }\n\n // Exit the packages block on a new top-level key.\n if (inPackages && trimmed.length > 0 && !trimmed.startsWith('-') && !trimmed.startsWith('#')) {\n break;\n }\n\n if (inPackages && trimmed.startsWith('-')) {\n // Strip \"- \", then strip surrounding quotes.\n const value = trimmed\n .slice(1)\n .trim()\n .replace(/^['\"]/, '')\n .replace(/['\"]$/, '');\n if (value) {\n workspaces.push(value);\n }\n }\n }\n\n return workspaces;\n}\n\n/**\n * Resolve workspace glob patterns to actual directory paths.\n * Uses minimatch for pattern matching against directory entries.\n */\nfunction resolveWorkspaceGlobs(root: string, globs: string[]): string[] {\n const resolved: string[] = [];\n\n for (const glob of globs) {\n // Skip negated patterns (e.g., \"!packages/internal\").\n if (glob.startsWith('!')) continue;\n\n // Simple case: \"packages/*\" → list dirs in \"packages/\".\n const parts = glob.split('/');\n const prefix = parts.slice(0, -1).join('/');\n const prefixDir = prefix ? path.join(root, prefix) : root;\n\n if (!fs.existsSync(prefixDir) || !fs.statSync(prefixDir).isDirectory()) {\n continue;\n }\n\n try {\n const entries = fs.readdirSync(prefixDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;\n if (minimatch(relativePath, glob)) {\n resolved.push(relativePath);\n }\n }\n } catch {\n // Skip unreadable directories.\n }\n }\n\n return resolved.sort();\n}\n\n/**\n * Detect the package manager from lockfiles or the package.json\n * \"packageManager\" field.\n */\nfunction detectPackageManager(root: string): string {\n // Check package.json \"packageManager\" field first (corepack).\n try {\n const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8'));\n if (typeof pkg.packageManager === 'string') {\n // Format: \"pnpm@9.0.0\" → extract name.\n const name = pkg.packageManager.split('@')[0];\n if (name) return name;\n }\n } catch {\n // Ignore.\n }\n\n // Lockfile detection.\n if (fs.existsSync(path.join(root, 'pnpm-lock.yaml'))) return 'pnpm';\n if (fs.existsSync(path.join(root, 'yarn.lock'))) return 'yarn';\n if (fs.existsSync(path.join(root, 'bun.lockb'))) return 'bun';\n return 'npm';\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace-Scoped Worker Prompt Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates context-rich prompts for Agent Teams workers and validators.
|
|
5
|
+
* Follows the Worker Initial Prompt and Validation Watcher Prompt templates
|
|
6
|
+
* from the design doc.
|
|
7
|
+
*
|
|
8
|
+
* Prompts are kept concise (~2000-4000 tokens) for worker context efficiency.
|
|
9
|
+
* Sections with no data are omitted entirely.
|
|
10
|
+
*/
|
|
11
|
+
export interface TicketContext {
|
|
12
|
+
id: string;
|
|
13
|
+
ticketNumber: number;
|
|
14
|
+
title: string;
|
|
15
|
+
description: string;
|
|
16
|
+
implementation?: {
|
|
17
|
+
steps?: string[];
|
|
18
|
+
filesToCreate?: string[];
|
|
19
|
+
filesToModify?: string[];
|
|
20
|
+
} | null;
|
|
21
|
+
acceptanceCriteria?: Array<{
|
|
22
|
+
description: string;
|
|
23
|
+
}> | null;
|
|
24
|
+
technicalDetails?: Record<string, unknown> | null;
|
|
25
|
+
}
|
|
26
|
+
export interface EpicContext {
|
|
27
|
+
title: string;
|
|
28
|
+
objective?: string | null;
|
|
29
|
+
}
|
|
30
|
+
export interface SpecContext {
|
|
31
|
+
title: string;
|
|
32
|
+
techStack?: string[] | null;
|
|
33
|
+
codeStandards?: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface WorkspaceContext {
|
|
36
|
+
/** Relative path from project root (e.g., "apps/web") */
|
|
37
|
+
path: string;
|
|
38
|
+
/** Detected framework (e.g., "Next.js", "Express") */
|
|
39
|
+
framework?: string | null;
|
|
40
|
+
/** Package name from package.json */
|
|
41
|
+
name?: string | null;
|
|
42
|
+
/** Key dependency names */
|
|
43
|
+
dependencies?: string[] | null;
|
|
44
|
+
}
|
|
45
|
+
export interface ConventionsContext {
|
|
46
|
+
/** Free-form conventions text (e.g., from CLAUDE.md) */
|
|
47
|
+
content?: string | null;
|
|
48
|
+
}
|
|
49
|
+
export interface ValidationCommands {
|
|
50
|
+
test?: string | null;
|
|
51
|
+
lint?: string | null;
|
|
52
|
+
typeCheck?: string | null;
|
|
53
|
+
build?: string | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Generate a context-rich prompt for an Agent Teams worker.
|
|
57
|
+
*
|
|
58
|
+
* Omits entire sections when the corresponding data is missing.
|
|
59
|
+
*/
|
|
60
|
+
export declare function generateWorkerPrompt(ticket: TicketContext, epic: EpicContext, spec: SpecContext, workspace?: WorkspaceContext | null, conventions?: ConventionsContext | null): string;
|
|
61
|
+
/**
|
|
62
|
+
* Generate a prompt for an Agent Teams validation watcher.
|
|
63
|
+
*/
|
|
64
|
+
export declare function generateValidatorPrompt(epic: EpicContext, validationCommands?: ValidationCommands | null, workspace?: WorkspaceContext | null): string;
|
|
65
|
+
//# sourceMappingURL=prompt-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-generator.d.ts","sourceRoot":"","sources":["../../src/lib/prompt-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE;QACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;KAC1B,GAAG,IAAI,CAAC;IACT,kBAAkB,CAAC,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAMD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,WAAW,EACjB,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,EACnC,WAAW,CAAC,EAAE,kBAAkB,GAAG,IAAI,GACtC,MAAM,CAiHR;AAMD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,WAAW,EACjB,kBAAkB,CAAC,EAAE,kBAAkB,GAAG,IAAI,EAC9C,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAClC,MAAM,CAiER"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
function generateWorkerPrompt(ticket, epic, spec, workspace, conventions) {
|
|
2
|
+
const sections = [];
|
|
3
|
+
sections.push(
|
|
4
|
+
`# SpecForge Worker: Implementing E-T${ticket.ticketNumber}`,
|
|
5
|
+
"",
|
|
6
|
+
`**Epic:** ${epic.title}`,
|
|
7
|
+
`**Spec:** ${spec.title}`,
|
|
8
|
+
""
|
|
9
|
+
);
|
|
10
|
+
sections.push(`## Ticket: ${ticket.title}`, "");
|
|
11
|
+
sections.push("### Description", ticket.description, "");
|
|
12
|
+
const impl = ticket.implementation;
|
|
13
|
+
if (impl) {
|
|
14
|
+
const hasSteps = impl.steps && impl.steps.length > 0;
|
|
15
|
+
const hasCreate = impl.filesToCreate && impl.filesToCreate.length > 0;
|
|
16
|
+
const hasModify = impl.filesToModify && impl.filesToModify.length > 0;
|
|
17
|
+
if (hasSteps || hasCreate || hasModify) {
|
|
18
|
+
sections.push("### Implementation Guide", "");
|
|
19
|
+
if (hasSteps) {
|
|
20
|
+
sections.push(
|
|
21
|
+
...impl.steps.map((step, i) => `${i + 1}. ${step}`),
|
|
22
|
+
""
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (hasCreate) {
|
|
26
|
+
sections.push("**Files to create:**");
|
|
27
|
+
sections.push(...impl.filesToCreate.map((f) => `- \`${f}\``), "");
|
|
28
|
+
}
|
|
29
|
+
if (hasModify) {
|
|
30
|
+
sections.push("**Files to modify:**");
|
|
31
|
+
sections.push(...impl.filesToModify.map((f) => `- \`${f}\``), "");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (ticket.acceptanceCriteria && ticket.acceptanceCriteria.length > 0) {
|
|
36
|
+
sections.push("### Acceptance Criteria");
|
|
37
|
+
sections.push(
|
|
38
|
+
...ticket.acceptanceCriteria.map((ac) => `- [ ] ${ac.description}`),
|
|
39
|
+
""
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (ticket.technicalDetails && Object.keys(ticket.technicalDetails).length > 0) {
|
|
43
|
+
sections.push("### Technical Details");
|
|
44
|
+
sections.push(formatTechnicalDetails(ticket.technicalDetails), "");
|
|
45
|
+
}
|
|
46
|
+
if (spec.techStack && spec.techStack.length > 0) {
|
|
47
|
+
sections.push("### Tech Stack");
|
|
48
|
+
sections.push(spec.techStack.join(", "), "");
|
|
49
|
+
}
|
|
50
|
+
if (workspace) {
|
|
51
|
+
sections.push("### Workspace");
|
|
52
|
+
sections.push(`- **Path:** ${workspace.path}`);
|
|
53
|
+
if (workspace.name) {
|
|
54
|
+
sections.push(`- **Package:** ${workspace.name}`);
|
|
55
|
+
}
|
|
56
|
+
if (workspace.framework) {
|
|
57
|
+
sections.push(`- **Framework:** ${workspace.framework}`);
|
|
58
|
+
}
|
|
59
|
+
if (workspace.dependencies && workspace.dependencies.length > 0) {
|
|
60
|
+
sections.push(`- **Key dependencies:** ${workspace.dependencies.join(", ")}`);
|
|
61
|
+
}
|
|
62
|
+
sections.push("");
|
|
63
|
+
}
|
|
64
|
+
if (conventions?.content) {
|
|
65
|
+
sections.push("### Conventions", conventions.content, "");
|
|
66
|
+
}
|
|
67
|
+
if (spec.codeStandards) {
|
|
68
|
+
sections.push("### Code Standards", spec.codeStandards, "");
|
|
69
|
+
}
|
|
70
|
+
sections.push("### Rules");
|
|
71
|
+
const rules = [
|
|
72
|
+
"Only modify files listed in the implementation guide unless necessary",
|
|
73
|
+
"Follow the project's coding conventions",
|
|
74
|
+
"Write tests for new functionality",
|
|
75
|
+
"BEFORE starting implementation: call `start_work_session` with the ticketId to mark it active",
|
|
76
|
+
"AFTER completing implementation and tests: call `complete_work_session` with ticketId, summary of work done, filesCreated, filesModified, filesDeleted, and validated: true",
|
|
77
|
+
"If blocked: call `pause_work_session` to pause work and notify the team lead",
|
|
78
|
+
"Report any blockers or discoveries to team-lead immediately"
|
|
79
|
+
];
|
|
80
|
+
if (workspace) {
|
|
81
|
+
rules.unshift(
|
|
82
|
+
`Only modify files within \`${workspace.path}/\` unless the ticket explicitly requires cross-workspace changes`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
sections.push(...rules.map((r) => `- ${r}`), "");
|
|
86
|
+
return sections.join("\n").trimEnd();
|
|
87
|
+
}
|
|
88
|
+
function generateValidatorPrompt(epic, validationCommands, workspace) {
|
|
89
|
+
const sections = [];
|
|
90
|
+
sections.push(`# SpecForge Validator: ${epic.title}`, "");
|
|
91
|
+
sections.push("## Your Role");
|
|
92
|
+
sections.push(
|
|
93
|
+
"You are the validation watcher for this epic.",
|
|
94
|
+
"After each teammate reports a ticket as complete:",
|
|
95
|
+
""
|
|
96
|
+
);
|
|
97
|
+
const steps = [];
|
|
98
|
+
steps.push("1. Pull their changes");
|
|
99
|
+
if (validationCommands?.test) {
|
|
100
|
+
steps.push(`2. Run tests: \`${validationCommands.test}\``);
|
|
101
|
+
}
|
|
102
|
+
if (validationCommands?.lint) {
|
|
103
|
+
steps.push(`${steps.length + 1}. Run lint: \`${validationCommands.lint}\``);
|
|
104
|
+
}
|
|
105
|
+
if (validationCommands?.typeCheck) {
|
|
106
|
+
steps.push(`${steps.length + 1}. Run type check: \`${validationCommands.typeCheck}\``);
|
|
107
|
+
}
|
|
108
|
+
if (validationCommands?.build) {
|
|
109
|
+
steps.push(`${steps.length + 1}. Run build: \`${validationCommands.build}\``);
|
|
110
|
+
}
|
|
111
|
+
steps.push(`${steps.length + 1}. Report results to team-lead`);
|
|
112
|
+
sections.push(...steps, "");
|
|
113
|
+
if (workspace) {
|
|
114
|
+
sections.push("## Workspace");
|
|
115
|
+
sections.push(`- **Path:** ${workspace.path}`);
|
|
116
|
+
if (workspace.framework) {
|
|
117
|
+
sections.push(`- **Framework:** ${workspace.framework}`);
|
|
118
|
+
}
|
|
119
|
+
sections.push("");
|
|
120
|
+
}
|
|
121
|
+
sections.push("## Rules");
|
|
122
|
+
sections.push(
|
|
123
|
+
"- Do NOT modify code yourself",
|
|
124
|
+
"- If tests fail, message the worker with specific errors and file:line references",
|
|
125
|
+
"- If all checks pass, confirm to team-lead",
|
|
126
|
+
"- Run validations in the correct workspace directory",
|
|
127
|
+
""
|
|
128
|
+
);
|
|
129
|
+
sections.push("## Reporting Format");
|
|
130
|
+
sections.push(
|
|
131
|
+
"**On success:**",
|
|
132
|
+
"> Ticket E-T{n} validated. All checks passed.",
|
|
133
|
+
"",
|
|
134
|
+
"**On failure:**",
|
|
135
|
+
"> Ticket E-T{n} failed validation:",
|
|
136
|
+
"> - {check}: {error message} ({file}:{line})",
|
|
137
|
+
""
|
|
138
|
+
);
|
|
139
|
+
return sections.join("\n").trimEnd();
|
|
140
|
+
}
|
|
141
|
+
function formatTechnicalDetails(details) {
|
|
142
|
+
const lines = [];
|
|
143
|
+
for (const [key, value] of Object.entries(details)) {
|
|
144
|
+
if (value === null || value === void 0) continue;
|
|
145
|
+
const label = key.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase());
|
|
146
|
+
if (Array.isArray(value)) {
|
|
147
|
+
if (value.length > 0) {
|
|
148
|
+
lines.push(`- **${label}:** ${value.join(", ")}`);
|
|
149
|
+
}
|
|
150
|
+
} else if (typeof value === "object") {
|
|
151
|
+
lines.push(`- **${label}:** ${JSON.stringify(value)}`);
|
|
152
|
+
} else {
|
|
153
|
+
lines.push(`- **${label}:** ${String(value)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return lines.join("\n");
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
generateValidatorPrompt,
|
|
160
|
+
generateWorkerPrompt
|
|
161
|
+
};
|
|
162
|
+
//# sourceMappingURL=prompt-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/prompt-generator.ts"],"sourcesContent":["/**\n * Workspace-Scoped Worker Prompt Generator\n *\n * Generates context-rich prompts for Agent Teams workers and validators.\n * Follows the Worker Initial Prompt and Validation Watcher Prompt templates\n * from the design doc.\n *\n * Prompts are kept concise (~2000-4000 tokens) for worker context efficiency.\n * Sections with no data are omitted entirely.\n */\n\n// ---------------------------------------------------------------------------\n// Input types\n// ---------------------------------------------------------------------------\n\nexport interface TicketContext {\n id: string;\n ticketNumber: number;\n title: string;\n description: string;\n implementation?: {\n steps?: string[];\n filesToCreate?: string[];\n filesToModify?: string[];\n } | null;\n acceptanceCriteria?: Array<{ description: string }> | null;\n technicalDetails?: Record<string, unknown> | null;\n}\n\nexport interface EpicContext {\n title: string;\n objective?: string | null;\n}\n\nexport interface SpecContext {\n title: string;\n techStack?: string[] | null;\n codeStandards?: string | null;\n}\n\nexport interface WorkspaceContext {\n /** Relative path from project root (e.g., \"apps/web\") */\n path: string;\n /** Detected framework (e.g., \"Next.js\", \"Express\") */\n framework?: string | null;\n /** Package name from package.json */\n name?: string | null;\n /** Key dependency names */\n dependencies?: string[] | null;\n}\n\nexport interface ConventionsContext {\n /** Free-form conventions text (e.g., from CLAUDE.md) */\n content?: string | null;\n}\n\nexport interface ValidationCommands {\n test?: string | null;\n lint?: string | null;\n typeCheck?: string | null;\n build?: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Worker prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a context-rich prompt for an Agent Teams worker.\n *\n * Omits entire sections when the corresponding data is missing.\n */\nexport function generateWorkerPrompt(\n ticket: TicketContext,\n epic: EpicContext,\n spec: SpecContext,\n workspace?: WorkspaceContext | null,\n conventions?: ConventionsContext | null,\n): string {\n const sections: string[] = [];\n\n // Header\n sections.push(\n `# SpecForge Worker: Implementing E-T${ticket.ticketNumber}`,\n '',\n `**Epic:** ${epic.title}`,\n `**Spec:** ${spec.title}`,\n '',\n );\n\n // Ticket details\n sections.push(`## Ticket: ${ticket.title}`, '');\n sections.push('### Description', ticket.description, '');\n\n // Implementation guide\n const impl = ticket.implementation;\n if (impl) {\n const hasSteps = impl.steps && impl.steps.length > 0;\n const hasCreate = impl.filesToCreate && impl.filesToCreate.length > 0;\n const hasModify = impl.filesToModify && impl.filesToModify.length > 0;\n\n if (hasSteps || hasCreate || hasModify) {\n sections.push('### Implementation Guide', '');\n\n if (hasSteps) {\n sections.push(\n ...impl.steps!.map((step, i) => `${i + 1}. ${step}`),\n '',\n );\n }\n\n if (hasCreate) {\n sections.push('**Files to create:**');\n sections.push(...impl.filesToCreate!.map(f => `- \\`${f}\\``), '');\n }\n\n if (hasModify) {\n sections.push('**Files to modify:**');\n sections.push(...impl.filesToModify!.map(f => `- \\`${f}\\``), '');\n }\n }\n }\n\n // Acceptance criteria\n if (ticket.acceptanceCriteria && ticket.acceptanceCriteria.length > 0) {\n sections.push('### Acceptance Criteria');\n sections.push(\n ...ticket.acceptanceCriteria.map(ac => `- [ ] ${ac.description}`),\n '',\n );\n }\n\n // Technical details\n if (ticket.technicalDetails && Object.keys(ticket.technicalDetails).length > 0) {\n sections.push('### Technical Details');\n sections.push(formatTechnicalDetails(ticket.technicalDetails), '');\n }\n\n // Tech stack from spec\n if (spec.techStack && spec.techStack.length > 0) {\n sections.push('### Tech Stack');\n sections.push(spec.techStack.join(', '), '');\n }\n\n // Workspace info (monorepo)\n if (workspace) {\n sections.push('### Workspace');\n sections.push(`- **Path:** ${workspace.path}`);\n if (workspace.name) {\n sections.push(`- **Package:** ${workspace.name}`);\n }\n if (workspace.framework) {\n sections.push(`- **Framework:** ${workspace.framework}`);\n }\n if (workspace.dependencies && workspace.dependencies.length > 0) {\n sections.push(`- **Key dependencies:** ${workspace.dependencies.join(', ')}`);\n }\n sections.push('');\n }\n\n // Conventions\n if (conventions?.content) {\n sections.push('### Conventions', conventions.content, '');\n }\n\n // Code standards from spec\n if (spec.codeStandards) {\n sections.push('### Code Standards', spec.codeStandards, '');\n }\n\n // Rules\n sections.push('### Rules');\n const rules = [\n 'Only modify files listed in the implementation guide unless necessary',\n \"Follow the project's coding conventions\",\n 'Write tests for new functionality',\n 'BEFORE starting implementation: call `start_work_session` with the ticketId to mark it active',\n 'AFTER completing implementation and tests: call `complete_work_session` with ticketId, summary of work done, filesCreated, filesModified, filesDeleted, and validated: true',\n 'If blocked: call `pause_work_session` to pause work and notify the team lead',\n 'Report any blockers or discoveries to team-lead immediately',\n ];\n\n if (workspace) {\n rules.unshift(\n `Only modify files within \\`${workspace.path}/\\` unless the ticket explicitly requires cross-workspace changes`,\n );\n }\n\n sections.push(...rules.map(r => `- ${r}`), '');\n\n return sections.join('\\n').trimEnd();\n}\n\n// ---------------------------------------------------------------------------\n// Validator prompt\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a prompt for an Agent Teams validation watcher.\n */\nexport function generateValidatorPrompt(\n epic: EpicContext,\n validationCommands?: ValidationCommands | null,\n workspace?: WorkspaceContext | null,\n): string {\n const sections: string[] = [];\n\n sections.push(`# SpecForge Validator: ${epic.title}`, '');\n\n sections.push('## Your Role');\n sections.push(\n 'You are the validation watcher for this epic.',\n 'After each teammate reports a ticket as complete:',\n '',\n );\n\n // Validation commands\n const steps: string[] = [];\n steps.push('1. Pull their changes');\n\n if (validationCommands?.test) {\n steps.push(`2. Run tests: \\`${validationCommands.test}\\``);\n }\n if (validationCommands?.lint) {\n steps.push(`${steps.length + 1}. Run lint: \\`${validationCommands.lint}\\``);\n }\n if (validationCommands?.typeCheck) {\n steps.push(`${steps.length + 1}. Run type check: \\`${validationCommands.typeCheck}\\``);\n }\n if (validationCommands?.build) {\n steps.push(`${steps.length + 1}. Run build: \\`${validationCommands.build}\\``);\n }\n\n steps.push(`${steps.length + 1}. Report results to team-lead`);\n sections.push(...steps, '');\n\n // Workspace scope\n if (workspace) {\n sections.push('## Workspace');\n sections.push(`- **Path:** ${workspace.path}`);\n if (workspace.framework) {\n sections.push(`- **Framework:** ${workspace.framework}`);\n }\n sections.push('');\n }\n\n // Rules\n sections.push('## Rules');\n sections.push(\n '- Do NOT modify code yourself',\n '- If tests fail, message the worker with specific errors and file:line references',\n '- If all checks pass, confirm to team-lead',\n '- Run validations in the correct workspace directory',\n '',\n );\n\n // Reporting format\n sections.push('## Reporting Format');\n sections.push(\n '**On success:**',\n '> Ticket E-T{n} validated. All checks passed.',\n '',\n '**On failure:**',\n '> Ticket E-T{n} failed validation:',\n '> - {check}: {error message} ({file}:{line})',\n '',\n );\n\n return sections.join('\\n').trimEnd();\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Format technical details as a readable key-value list.\n * Handles nested objects and arrays.\n */\nfunction formatTechnicalDetails(details: Record<string, unknown>): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(details)) {\n if (value === null || value === undefined) continue;\n\n const label = key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());\n\n if (Array.isArray(value)) {\n if (value.length > 0) {\n lines.push(`- **${label}:** ${value.join(', ')}`);\n }\n } else if (typeof value === 'object') {\n lines.push(`- **${label}:** ${JSON.stringify(value)}`);\n } else {\n lines.push(`- **${label}:** ${String(value)}`);\n }\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":[]}
|