@rsaglobaltech/specgate 0.8.1
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/CHANGELOG.md +1016 -0
- package/README.md +148 -0
- package/bin/specgate.js +2 -0
- package/bin/specgate.ts +383 -0
- package/dist/bin/specgate.js +342 -0
- package/dist/bin/specgate.js.map +1 -0
- package/dist/packages/core/src/application/AddRequirementUseCase.js +24 -0
- package/dist/packages/core/src/application/AddRequirementUseCase.js.map +1 -0
- package/dist/packages/core/src/application/ArchiveChangeUseCase.js +185 -0
- package/dist/packages/core/src/application/ArchiveChangeUseCase.js.map +1 -0
- package/dist/packages/core/src/application/CheckAgainstLockUseCase.js +134 -0
- package/dist/packages/core/src/application/CheckAgainstLockUseCase.js.map +1 -0
- package/dist/packages/core/src/application/GenerateAgentPromptUseCase.js +25 -0
- package/dist/packages/core/src/application/GenerateAgentPromptUseCase.js.map +1 -0
- package/dist/packages/core/src/application/GeneratePlanUseCase.js +135 -0
- package/dist/packages/core/src/application/GeneratePlanUseCase.js.map +1 -0
- package/dist/packages/core/src/application/LinkRequirementUseCase.js +25 -0
- package/dist/packages/core/src/application/LinkRequirementUseCase.js.map +1 -0
- package/dist/packages/core/src/application/RemovePackUseCase.js +28 -0
- package/dist/packages/core/src/application/RemovePackUseCase.js.map +1 -0
- package/dist/packages/core/src/application/UpdateRequirementStatusUseCase.js +37 -0
- package/dist/packages/core/src/application/UpdateRequirementStatusUseCase.js.map +1 -0
- package/dist/packages/core/src/application/ValidateChangeUseCase.js +86 -0
- package/dist/packages/core/src/application/ValidateChangeUseCase.js.map +1 -0
- package/dist/packages/core/src/application/ValidateProjectUseCase.js +101 -0
- package/dist/packages/core/src/application/ValidateProjectUseCase.js.map +1 -0
- package/dist/packages/core/src/application/ports/IDomainPackRepository.js +3 -0
- package/dist/packages/core/src/application/ports/IDomainPackRepository.js.map +1 -0
- package/dist/packages/core/src/application/ports/IHarnessConfigRepository.js +3 -0
- package/dist/packages/core/src/application/ports/IHarnessConfigRepository.js.map +1 -0
- package/dist/packages/core/src/application/ports/ILockfileRepository.js +3 -0
- package/dist/packages/core/src/application/ports/ILockfileRepository.js.map +1 -0
- package/dist/packages/core/src/application/ports/IManifestRepository.js +3 -0
- package/dist/packages/core/src/application/ports/IManifestRepository.js.map +1 -0
- package/dist/packages/core/src/application/ports/IProjectRepository.js +3 -0
- package/dist/packages/core/src/application/ports/IProjectRepository.js.map +1 -0
- package/dist/packages/core/src/application/ports/IRequirementGraphRepository.js +3 -0
- package/dist/packages/core/src/application/ports/IRequirementGraphRepository.js.map +1 -0
- package/dist/packages/core/src/application/ports/ITraceabilityRepository.js +3 -0
- package/dist/packages/core/src/application/ports/ITraceabilityRepository.js.map +1 -0
- package/dist/packages/core/src/domain/AgentPrompt.js +79 -0
- package/dist/packages/core/src/domain/AgentPrompt.js.map +1 -0
- package/dist/packages/core/src/domain/ArchivePlan.js +26 -0
- package/dist/packages/core/src/domain/ArchivePlan.js.map +1 -0
- package/dist/packages/core/src/domain/AuthoringScope.js +48 -0
- package/dist/packages/core/src/domain/AuthoringScope.js.map +1 -0
- package/dist/packages/core/src/domain/ChangeTemplates.js +133 -0
- package/dist/packages/core/src/domain/ChangeTemplates.js.map +1 -0
- package/dist/packages/core/src/domain/CucumberMessages.js +256 -0
- package/dist/packages/core/src/domain/CucumberMessages.js.map +1 -0
- package/dist/packages/core/src/domain/DeclaredArtifacts.js +117 -0
- package/dist/packages/core/src/domain/DeclaredArtifacts.js.map +1 -0
- package/dist/packages/core/src/domain/DeltaSpec.js +219 -0
- package/dist/packages/core/src/domain/DeltaSpec.js.map +1 -0
- package/dist/packages/core/src/domain/Diagnostic.js +75 -0
- package/dist/packages/core/src/domain/Diagnostic.js.map +1 -0
- package/dist/packages/core/src/domain/DomainPack.js +62 -0
- package/dist/packages/core/src/domain/DomainPack.js.map +1 -0
- package/dist/packages/core/src/domain/EmptyAttempt.js +60 -0
- package/dist/packages/core/src/domain/EmptyAttempt.js.map +1 -0
- package/dist/packages/core/src/domain/FileChangeSet.js +37 -0
- package/dist/packages/core/src/domain/FileChangeSet.js.map +1 -0
- package/dist/packages/core/src/domain/Gherkin.js +272 -0
- package/dist/packages/core/src/domain/Gherkin.js.map +1 -0
- package/dist/packages/core/src/domain/GherkinDialects.js +97 -0
- package/dist/packages/core/src/domain/GherkinDialects.js.map +1 -0
- package/dist/packages/core/src/domain/GherkinQuality.js +160 -0
- package/dist/packages/core/src/domain/GherkinQuality.js.map +1 -0
- package/dist/packages/core/src/domain/GherkinTags.js +119 -0
- package/dist/packages/core/src/domain/GherkinTags.js.map +1 -0
- package/dist/packages/core/src/domain/GitSafety.js +77 -0
- package/dist/packages/core/src/domain/GitSafety.js.map +1 -0
- package/dist/packages/core/src/domain/HarnessConfig.js +58 -0
- package/dist/packages/core/src/domain/HarnessConfig.js.map +1 -0
- package/dist/packages/core/src/domain/HarnessReport.js +162 -0
- package/dist/packages/core/src/domain/HarnessReport.js.map +1 -0
- package/dist/packages/core/src/domain/HarnessRun.js +154 -0
- package/dist/packages/core/src/domain/HarnessRun.js.map +1 -0
- package/dist/packages/core/src/domain/InboundChange.js +127 -0
- package/dist/packages/core/src/domain/InboundChange.js.map +1 -0
- package/dist/packages/core/src/domain/Language.js +112 -0
- package/dist/packages/core/src/domain/Language.js.map +1 -0
- package/dist/packages/core/src/domain/Lockfile.js +64 -0
- package/dist/packages/core/src/domain/Lockfile.js.map +1 -0
- package/dist/packages/core/src/domain/PackContribution.js +106 -0
- package/dist/packages/core/src/domain/PackContribution.js.map +1 -0
- package/dist/packages/core/src/domain/PackDelta.js +298 -0
- package/dist/packages/core/src/domain/PackDelta.js.map +1 -0
- package/dist/packages/core/src/domain/PackSpec.js +481 -0
- package/dist/packages/core/src/domain/PackSpec.js.map +1 -0
- package/dist/packages/core/src/domain/ProfileMatch.js +87 -0
- package/dist/packages/core/src/domain/ProfileMatch.js.map +1 -0
- package/dist/packages/core/src/domain/Reconciliation.js +72 -0
- package/dist/packages/core/src/domain/Reconciliation.js.map +1 -0
- package/dist/packages/core/src/domain/RequirementGraph.js +197 -0
- package/dist/packages/core/src/domain/RequirementGraph.js.map +1 -0
- package/dist/packages/core/src/domain/RequirementPlan.js +85 -0
- package/dist/packages/core/src/domain/RequirementPlan.js.map +1 -0
- package/dist/packages/core/src/domain/RequirementReadiness.js +115 -0
- package/dist/packages/core/src/domain/RequirementReadiness.js.map +1 -0
- package/dist/packages/core/src/domain/RequirementSyntax.js +91 -0
- package/dist/packages/core/src/domain/RequirementSyntax.js.map +1 -0
- package/dist/packages/core/src/domain/ResumeState.js +115 -0
- package/dist/packages/core/src/domain/ResumeState.js.map +1 -0
- package/dist/packages/core/src/domain/RunBudget.js +103 -0
- package/dist/packages/core/src/domain/RunBudget.js.map +1 -0
- package/dist/packages/core/src/domain/SpecDiff.js +48 -0
- package/dist/packages/core/src/domain/SpecDiff.js.map +1 -0
- package/dist/packages/core/src/domain/SpecParser.js +406 -0
- package/dist/packages/core/src/domain/SpecParser.js.map +1 -0
- package/dist/packages/core/src/domain/SpecopsManifest.js +90 -0
- package/dist/packages/core/src/domain/SpecopsManifest.js.map +1 -0
- package/dist/packages/core/src/domain/TraceabilityFormat.js +317 -0
- package/dist/packages/core/src/domain/TraceabilityFormat.js.map +1 -0
- package/dist/packages/core/src/domain/TraceabilityMatrix.js +200 -0
- package/dist/packages/core/src/domain/TraceabilityMatrix.js.map +1 -0
- package/dist/packages/core/src/domain/TraceabilityMerge.js +180 -0
- package/dist/packages/core/src/domain/TraceabilityMerge.js.map +1 -0
- package/dist/packages/core/src/domain/ValidationReport.js +53 -0
- package/dist/packages/core/src/domain/ValidationReport.js.map +1 -0
- package/dist/packages/core/src/domain/ValueAnnotations.js +138 -0
- package/dist/packages/core/src/domain/ValueAnnotations.js.map +1 -0
- package/dist/packages/core/src/domain/WriteScope.js +155 -0
- package/dist/packages/core/src/domain/WriteScope.js.map +1 -0
- package/dist/packages/core/src/domain/YamlLite.js +226 -0
- package/dist/packages/core/src/domain/YamlLite.js.map +1 -0
- package/dist/packages/core/src/index.js +120 -0
- package/dist/packages/core/src/index.js.map +1 -0
- package/dist/packages/core/src/infrastructure/ChangeWorkspace.js +276 -0
- package/dist/packages/core/src/infrastructure/ChangeWorkspace.js.map +1 -0
- package/dist/packages/core/src/infrastructure/ConsoleReporter.js +19 -0
- package/dist/packages/core/src/infrastructure/ConsoleReporter.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DirectorySnapshot.js +106 -0
- package/dist/packages/core/src/infrastructure/DirectorySnapshot.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskDomainPackRepository.js +57 -0
- package/dist/packages/core/src/infrastructure/DiskDomainPackRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskHarnessConfigRepository.js +58 -0
- package/dist/packages/core/src/infrastructure/DiskHarnessConfigRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskLanguageRepository.js +69 -0
- package/dist/packages/core/src/infrastructure/DiskLanguageRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskLockfileRepository.js +64 -0
- package/dist/packages/core/src/infrastructure/DiskLockfileRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskManifestRepository.js +88 -0
- package/dist/packages/core/src/infrastructure/DiskManifestRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskPackDeltaRepository.js +138 -0
- package/dist/packages/core/src/infrastructure/DiskPackDeltaRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskPackRepository.js +130 -0
- package/dist/packages/core/src/infrastructure/DiskPackRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskProjectRepository.js +127 -0
- package/dist/packages/core/src/infrastructure/DiskProjectRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskRequirementGraphRepository.js +124 -0
- package/dist/packages/core/src/infrastructure/DiskRequirementGraphRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/DiskTraceabilityRepository.js +53 -0
- package/dist/packages/core/src/infrastructure/DiskTraceabilityRepository.js.map +1 -0
- package/dist/packages/core/src/infrastructure/GitContributionStager.js +99 -0
- package/dist/packages/core/src/infrastructure/GitContributionStager.js.map +1 -0
- package/dist/packages/core/src/infrastructure/GitMergeDriver.js +103 -0
- package/dist/packages/core/src/infrastructure/GitMergeDriver.js.map +1 -0
- package/dist/packages/core/src/infrastructure/HarnessConfigFile.js +348 -0
- package/dist/packages/core/src/infrastructure/HarnessConfigFile.js.map +1 -0
- package/dist/packages/core/src/infrastructure/PackChangeDeposit.js +106 -0
- package/dist/packages/core/src/infrastructure/PackChangeDeposit.js.map +1 -0
- package/dist/packages/core/src/infrastructure/RemotePackResolver.js +181 -0
- package/dist/packages/core/src/infrastructure/RemotePackResolver.js.map +1 -0
- package/dist/packages/core/src/infrastructure/SpecopsConfigFile.js +94 -0
- package/dist/packages/core/src/infrastructure/SpecopsConfigFile.js.map +1 -0
- package/dist/packages/core/test/unit/authoring-scope.test.js +102 -0
- package/dist/packages/core/test/unit/authoring-scope.test.js.map +1 -0
- package/dist/packages/core/test/unit/change-templates.test.js +96 -0
- package/dist/packages/core/test/unit/change-templates.test.js.map +1 -0
- package/dist/packages/core/test/unit/cucumber-messages.test.js +232 -0
- package/dist/packages/core/test/unit/cucumber-messages.test.js.map +1 -0
- package/dist/packages/core/test/unit/declared-artifacts.test.js +124 -0
- package/dist/packages/core/test/unit/declared-artifacts.test.js.map +1 -0
- package/dist/packages/core/test/unit/empty-attempt.test.js +79 -0
- package/dist/packages/core/test/unit/empty-attempt.test.js.map +1 -0
- package/dist/packages/core/test/unit/gherkin-dialects.test.js +115 -0
- package/dist/packages/core/test/unit/gherkin-dialects.test.js.map +1 -0
- package/dist/packages/core/test/unit/gherkin-keyword-case.test.js +110 -0
- package/dist/packages/core/test/unit/gherkin-keyword-case.test.js.map +1 -0
- package/dist/packages/core/test/unit/gherkin-quality.test.js +151 -0
- package/dist/packages/core/test/unit/gherkin-quality.test.js.map +1 -0
- package/dist/packages/core/test/unit/gherkin-tags.test.js +127 -0
- package/dist/packages/core/test/unit/gherkin-tags.test.js.map +1 -0
- package/dist/packages/core/test/unit/git-safety.test.js +104 -0
- package/dist/packages/core/test/unit/git-safety.test.js.map +1 -0
- package/dist/packages/core/test/unit/harness-report.test.js +192 -0
- package/dist/packages/core/test/unit/harness-report.test.js.map +1 -0
- package/dist/packages/core/test/unit/harness-roles.test.js +86 -0
- package/dist/packages/core/test/unit/harness-roles.test.js.map +1 -0
- package/dist/packages/core/test/unit/inbound-change.test.js +145 -0
- package/dist/packages/core/test/unit/inbound-change.test.js.map +1 -0
- package/dist/packages/core/test/unit/matrix-dependencies.test.js +100 -0
- package/dist/packages/core/test/unit/matrix-dependencies.test.js.map +1 -0
- package/dist/packages/core/test/unit/profile-match.test.js +115 -0
- package/dist/packages/core/test/unit/profile-match.test.js.map +1 -0
- package/dist/packages/core/test/unit/reconciliation.test.js +112 -0
- package/dist/packages/core/test/unit/reconciliation.test.js.map +1 -0
- package/dist/packages/core/test/unit/requirement-readiness.test.js +161 -0
- package/dist/packages/core/test/unit/requirement-readiness.test.js.map +1 -0
- package/dist/packages/core/test/unit/resume-state.test.js +124 -0
- package/dist/packages/core/test/unit/resume-state.test.js.map +1 -0
- package/dist/packages/core/test/unit/run-budget.test.js +113 -0
- package/dist/packages/core/test/unit/run-budget.test.js.map +1 -0
- package/dist/packages/core/test/unit/traceability-merge.test.js +159 -0
- package/dist/packages/core/test/unit/traceability-merge.test.js.map +1 -0
- package/dist/packages/core/test/unit/validate-project.test.js +156 -0
- package/dist/packages/core/test/unit/validate-project.test.js.map +1 -0
- package/dist/packages/core/test/unit/value-annotations.test.js +149 -0
- package/dist/packages/core/test/unit/value-annotations.test.js.map +1 -0
- package/dist/packages/core/test/unit/write-scope.test.js +165 -0
- package/dist/packages/core/test/unit/write-scope.test.js.map +1 -0
- package/dist/packages/core/test/unit/yaml-lite.test.js +78 -0
- package/dist/packages/core/test/unit/yaml-lite.test.js.map +1 -0
- package/dist/scripts/adopt_project.js +11 -0
- package/dist/scripts/adopt_project.js.map +1 -0
- package/dist/scripts/agents/commands.js +100 -0
- package/dist/scripts/agents/commands.js.map +1 -0
- package/dist/scripts/agents/init.js +482 -0
- package/dist/scripts/agents/init.js.map +1 -0
- package/dist/scripts/alm/cli.js +154 -0
- package/dist/scripts/alm/cli.js.map +1 -0
- package/dist/scripts/alm/clients.js +30 -0
- package/dist/scripts/alm/clients.js.map +1 -0
- package/dist/scripts/alm/core.js +258 -0
- package/dist/scripts/alm/core.js.map +1 -0
- package/dist/scripts/alm/port.js +116 -0
- package/dist/scripts/alm/port.js.map +1 -0
- package/dist/scripts/alm/providers/azure.js +79 -0
- package/dist/scripts/alm/providers/azure.js.map +1 -0
- package/dist/scripts/alm/providers/github.js +124 -0
- package/dist/scripts/alm/providers/github.js.map +1 -0
- package/dist/scripts/alm/providers/http-client.js +47 -0
- package/dist/scripts/alm/providers/http-client.js.map +1 -0
- package/dist/scripts/alm/providers/index.js +107 -0
- package/dist/scripts/alm/providers/index.js.map +1 -0
- package/dist/scripts/alm/providers/jira.js +129 -0
- package/dist/scripts/alm/providers/jira.js.map +1 -0
- package/dist/scripts/bundle_pack.js +10 -0
- package/dist/scripts/bundle_pack.js.map +1 -0
- package/dist/scripts/change/archive.js +68 -0
- package/dist/scripts/change/archive.js.map +1 -0
- package/dist/scripts/change/artifacts.js +143 -0
- package/dist/scripts/change/artifacts.js.map +1 -0
- package/dist/scripts/change/cli.js +39 -0
- package/dist/scripts/change/cli.js.map +1 -0
- package/dist/scripts/change/delta.js +20 -0
- package/dist/scripts/change/delta.js.map +1 -0
- package/dist/scripts/change/instructions.js +339 -0
- package/dist/scripts/change/instructions.js.map +1 -0
- package/dist/scripts/ci_init.js +12 -0
- package/dist/scripts/ci_init.js.map +1 -0
- package/dist/scripts/cli/commands/alm/PullCommand.js +238 -0
- package/dist/scripts/cli/commands/alm/PullCommand.js.map +1 -0
- package/dist/scripts/cli/commands/change/ChangeCommand.js +867 -0
- package/dist/scripts/cli/commands/change/ChangeCommand.js.map +1 -0
- package/dist/scripts/cli/commands/config/CiInitCommand.js +144 -0
- package/dist/scripts/cli/commands/config/CiInitCommand.js.map +1 -0
- package/dist/scripts/cli/commands/config/CompletionCommand.js +160 -0
- package/dist/scripts/cli/commands/config/CompletionCommand.js.map +1 -0
- package/dist/scripts/cli/commands/config/ConfigInitCommand.js +108 -0
- package/dist/scripts/cli/commands/config/ConfigInitCommand.js.map +1 -0
- package/dist/scripts/cli/commands/config/ConfigSetCommand.js +166 -0
- package/dist/scripts/cli/commands/config/ConfigSetCommand.js.map +1 -0
- package/dist/scripts/cli/commands/config/GenAgentContractCommand.js +212 -0
- package/dist/scripts/cli/commands/config/GenAgentContractCommand.js.map +1 -0
- package/dist/scripts/cli/commands/harness/RunCommand.js +1922 -0
- package/dist/scripts/cli/commands/harness/RunCommand.js.map +1 -0
- package/dist/scripts/cli/commands/mcp/McpInstallCommand.js +114 -0
- package/dist/scripts/cli/commands/mcp/McpInstallCommand.js.map +1 -0
- package/dist/scripts/cli/commands/mcp/index.js +29 -0
- package/dist/scripts/cli/commands/mcp/index.js.map +1 -0
- package/dist/scripts/cli/commands/pack/BundlePackCommand.js +59 -0
- package/dist/scripts/cli/commands/pack/BundlePackCommand.js.map +1 -0
- package/dist/scripts/cli/commands/pack/ExpandDomainPackCommand.js +559 -0
- package/dist/scripts/cli/commands/pack/ExpandDomainPackCommand.js.map +1 -0
- package/dist/scripts/cli/commands/pack/InferPackCommand.js +304 -0
- package/dist/scripts/cli/commands/pack/InferPackCommand.js.map +1 -0
- package/dist/scripts/cli/commands/pack/InitPackCommand.js +368 -0
- package/dist/scripts/cli/commands/pack/InitPackCommand.js.map +1 -0
- package/dist/scripts/cli/commands/pack/LintPackCommand.js +631 -0
- package/dist/scripts/cli/commands/pack/LintPackCommand.js.map +1 -0
- package/dist/scripts/cli/commands/pack/pack-args.js +83 -0
- package/dist/scripts/cli/commands/pack/pack-args.js.map +1 -0
- package/dist/scripts/cli/commands/project/AdoptCommand.js +414 -0
- package/dist/scripts/cli/commands/project/AdoptCommand.js.map +1 -0
- package/dist/scripts/cli/commands/project/InitFromPackCommand.js +188 -0
- package/dist/scripts/cli/commands/project/InitFromPackCommand.js.map +1 -0
- package/dist/scripts/cli/commands/project/InitProjectCommand.js +791 -0
- package/dist/scripts/cli/commands/project/InitProjectCommand.js.map +1 -0
- package/dist/scripts/cli/commands/project/OnboardCommand.js +655 -0
- package/dist/scripts/cli/commands/project/OnboardCommand.js.map +1 -0
- package/dist/scripts/cli/commands/project/UpdateCommand.js +206 -0
- package/dist/scripts/cli/commands/project/UpdateCommand.js.map +1 -0
- package/dist/scripts/cli/commands/project/WizardCommand.js +166 -0
- package/dist/scripts/cli/commands/project/WizardCommand.js.map +1 -0
- package/dist/scripts/cli/commands/quality/DoctorCommand.js +553 -0
- package/dist/scripts/cli/commands/quality/DoctorCommand.js.map +1 -0
- package/dist/scripts/cli/commands/quality/FixCommand.js +201 -0
- package/dist/scripts/cli/commands/quality/FixCommand.js.map +1 -0
- package/dist/scripts/cli/commands/quality/LicenseCheckCommand.js +187 -0
- package/dist/scripts/cli/commands/quality/LicenseCheckCommand.js.map +1 -0
- package/dist/scripts/cli/commands/quality/ReportCommand.js +557 -0
- package/dist/scripts/cli/commands/quality/ReportCommand.js.map +1 -0
- package/dist/scripts/cli/commands/quality/ValidateSpecsCommand.js +764 -0
- package/dist/scripts/cli/commands/quality/ValidateSpecsCommand.js.map +1 -0
- package/dist/scripts/cli/commands/spec/DoneCommand.js +116 -0
- package/dist/scripts/cli/commands/spec/DoneCommand.js.map +1 -0
- package/dist/scripts/cli/commands/spec/PlanCommand.js +397 -0
- package/dist/scripts/cli/commands/spec/PlanCommand.js.map +1 -0
- package/dist/scripts/cli/commands/spec/ReqCommand.js +303 -0
- package/dist/scripts/cli/commands/spec/ReqCommand.js.map +1 -0
- package/dist/scripts/cli/commands/spec/StatusCommand.js +206 -0
- package/dist/scripts/cli/commands/spec/StatusCommand.js.map +1 -0
- package/dist/scripts/cli/commands/spec/StudioCommand.js +241 -0
- package/dist/scripts/cli/commands/spec/StudioCommand.js.map +1 -0
- package/dist/scripts/cli/commands/specops/ContributeCommand.js +313 -0
- package/dist/scripts/cli/commands/specops/ContributeCommand.js.map +1 -0
- package/dist/scripts/cli/commands/specops/DiffCommand.js +394 -0
- package/dist/scripts/cli/commands/specops/DiffCommand.js.map +1 -0
- package/dist/scripts/cli/commands/specops/SyncCommand.js +380 -0
- package/dist/scripts/cli/commands/specops/SyncCommand.js.map +1 -0
- package/dist/scripts/completion.js +24 -0
- package/dist/scripts/completion.js.map +1 -0
- package/dist/scripts/config_init.js +12 -0
- package/dist/scripts/config_init.js.map +1 -0
- package/dist/scripts/config_set.js +20 -0
- package/dist/scripts/config_set.js.map +1 -0
- package/dist/scripts/docs/blocks.js +137 -0
- package/dist/scripts/docs/blocks.js.map +1 -0
- package/dist/scripts/docs/build-site.js +425 -0
- package/dist/scripts/docs/build-site.js.map +1 -0
- package/dist/scripts/docs/nav.js +198 -0
- package/dist/scripts/docs/nav.js.map +1 -0
- package/dist/scripts/docs/record-terminal.js +477 -0
- package/dist/scripts/docs/record-terminal.js.map +1 -0
- package/dist/scripts/docs/terminal-storyboard.js +121 -0
- package/dist/scripts/docs/terminal-storyboard.js.map +1 -0
- package/dist/scripts/doctor.js +14 -0
- package/dist/scripts/doctor.js.map +1 -0
- package/dist/scripts/done.js +17 -0
- package/dist/scripts/done.js.map +1 -0
- package/dist/scripts/expand_domain_pack.js +18 -0
- package/dist/scripts/expand_domain_pack.js.map +1 -0
- package/dist/scripts/fix.js +12 -0
- package/dist/scripts/fix.js.map +1 -0
- package/dist/scripts/gen_agent_contract.js +14 -0
- package/dist/scripts/gen_agent_contract.js.map +1 -0
- package/dist/scripts/gen_mcp_tools.js +264 -0
- package/dist/scripts/gen_mcp_tools.js.map +1 -0
- package/dist/scripts/harness/init.js +358 -0
- package/dist/scripts/harness/init.js.map +1 -0
- package/dist/scripts/harness/prompt.js +18 -0
- package/dist/scripts/harness/prompt.js.map +1 -0
- package/dist/scripts/harness/report.js +323 -0
- package/dist/scripts/harness/report.js.map +1 -0
- package/dist/scripts/harness/run.js +33 -0
- package/dist/scripts/harness/run.js.map +1 -0
- package/dist/scripts/infer_pack.js +17 -0
- package/dist/scripts/infer_pack.js.map +1 -0
- package/dist/scripts/init_from_pack.js +16 -0
- package/dist/scripts/init_from_pack.js.map +1 -0
- package/dist/scripts/init_pack.js +16 -0
- package/dist/scripts/init_pack.js.map +1 -0
- package/dist/scripts/init_project.js +17 -0
- package/dist/scripts/init_project.js.map +1 -0
- package/dist/scripts/lib/agent.js +99 -0
- package/dist/scripts/lib/agent.js.map +1 -0
- package/dist/scripts/lib/capability-specs.js +80 -0
- package/dist/scripts/lib/capability-specs.js.map +1 -0
- package/dist/scripts/lib/command.js +17 -0
- package/dist/scripts/lib/command.js.map +1 -0
- package/dist/scripts/lib/diagnostics.js +62 -0
- package/dist/scripts/lib/diagnostics.js.map +1 -0
- package/dist/scripts/lib/monorepo-fanout.js +118 -0
- package/dist/scripts/lib/monorepo-fanout.js.map +1 -0
- package/dist/scripts/lib/placeholders.js +190 -0
- package/dist/scripts/lib/placeholders.js.map +1 -0
- package/dist/scripts/lib/project-root.js +124 -0
- package/dist/scripts/lib/project-root.js.map +1 -0
- package/dist/scripts/lib/requirement-graph.js +31 -0
- package/dist/scripts/lib/requirement-graph.js.map +1 -0
- package/dist/scripts/lib/surface.js +609 -0
- package/dist/scripts/lib/surface.js.map +1 -0
- package/dist/scripts/license_check.js +15 -0
- package/dist/scripts/license_check.js.map +1 -0
- package/dist/scripts/lint_pack.js +18 -0
- package/dist/scripts/lint_pack.js.map +1 -0
- package/dist/scripts/merge-traceability.js +106 -0
- package/dist/scripts/merge-traceability.js.map +1 -0
- package/dist/scripts/onboard.js +22 -0
- package/dist/scripts/onboard.js.map +1 -0
- package/dist/scripts/plan.js +17 -0
- package/dist/scripts/plan.js.map +1 -0
- package/dist/scripts/plugin/gate-hook.js +171 -0
- package/dist/scripts/plugin/gate-hook.js.map +1 -0
- package/dist/scripts/report.js +17 -0
- package/dist/scripts/report.js.map +1 -0
- package/dist/scripts/req.js +22 -0
- package/dist/scripts/req.js.map +1 -0
- package/dist/scripts/schema/cli.js +215 -0
- package/dist/scripts/schema/cli.js.map +1 -0
- package/dist/scripts/schema/registry.js +264 -0
- package/dist/scripts/schema/registry.js.map +1 -0
- package/dist/scripts/specops/add.js +180 -0
- package/dist/scripts/specops/add.js.map +1 -0
- package/dist/scripts/specops/against_lock.js +100 -0
- package/dist/scripts/specops/against_lock.js.map +1 -0
- package/dist/scripts/specops/as_change.js +49 -0
- package/dist/scripts/specops/as_change.js.map +1 -0
- package/dist/scripts/specops/contribute.js +24 -0
- package/dist/scripts/specops/contribute.js.map +1 -0
- package/dist/scripts/specops/diff.js +32 -0
- package/dist/scripts/specops/diff.js.map +1 -0
- package/dist/scripts/specops/lock.js +39 -0
- package/dist/scripts/specops/lock.js.map +1 -0
- package/dist/scripts/specops/manifest.js +90 -0
- package/dist/scripts/specops/manifest.js.map +1 -0
- package/dist/scripts/specops/remove.js +102 -0
- package/dist/scripts/specops/remove.js.map +1 -0
- package/dist/scripts/specops/sync.js +27 -0
- package/dist/scripts/specops/sync.js.map +1 -0
- package/dist/scripts/specops/verify.js +149 -0
- package/dist/scripts/specops/verify.js.map +1 -0
- package/dist/scripts/status.js +17 -0
- package/dist/scripts/status.js.map +1 -0
- package/dist/scripts/studio.js +14 -0
- package/dist/scripts/studio.js.map +1 -0
- package/dist/scripts/update.js +17 -0
- package/dist/scripts/update.js.map +1 -0
- package/dist/scripts/validate_specs.js +10 -0
- package/dist/scripts/validate_specs.js.map +1 -0
- package/examples/project.config.example +19 -0
- package/examples/project.yaml.example +17 -0
- package/package.json +105 -0
- package/templates/adopt/AI_RULES.md.tpl +27 -0
- package/templates/adopt/adr-readme.md.tpl +5 -0
- package/templates/adopt/baseline.feature.tpl +9 -0
- package/templates/adopt/spec.md.tpl +25 -0
- package/templates/adopt/traceability.md.tpl +11 -0
- package/templates/backend/AI_RULES.md.tpl +35 -0
- package/templates/backend/features/core/health.feature.tpl +7 -0
- package/templates/base/.devcontainer/devcontainer.json.tpl +18 -0
- package/templates/base/.dockerignore.tpl +11 -0
- package/templates/base/.env.dev.app.tpl +13 -0
- package/templates/base/.env.dev.infra.tpl +6 -0
- package/templates/base/.env.dev.tpl +16 -0
- package/templates/base/.env.example.tpl +17 -0
- package/templates/base/.env.feature.app.tpl +11 -0
- package/templates/base/.env.feature.infra.tpl +4 -0
- package/templates/base/.env.feature.tpl +16 -0
- package/templates/base/.env.prod.app.tpl +13 -0
- package/templates/base/.env.prod.infra.tpl +6 -0
- package/templates/base/.env.prod.tpl +16 -0
- package/templates/base/.github/workflows/specs.yml.tpl +26 -0
- package/templates/base/.gitignore.tpl +9 -0
- package/templates/base/.pre-commit-config.yaml.tpl +10 -0
- package/templates/base/AGENTS.md.tpl +32 -0
- package/templates/base/README.md.tpl +32 -0
- package/templates/base/docker-compose.yml.tpl +13 -0
- package/templates/base/docs/specs/adr/README.md.tpl +10 -0
- package/templates/base/docs/specs/aggregates.md.tpl +5 -0
- package/templates/base/docs/specs/commands.md.tpl +13 -0
- package/templates/base/docs/specs/domain-model.md.tpl +30 -0
- package/templates/base/docs/specs/events.md.tpl +5 -0
- package/templates/base/docs/specs/review-checklist.md.tpl +18 -0
- package/templates/base/docs/specs/runtime-environments.md.tpl +26 -0
- package/templates/base/docs/specs/status-model.md.tpl +16 -0
- package/templates/base/docs/specs/traceability.md.tpl +7 -0
- package/templates/base/docs/specs/use-cases.md.tpl +5 -0
- package/templates/base/spec.md.tpl +63 -0
- package/templates/ci/azure.yml.tpl +40 -0
- package/templates/ci/github.yml.tpl +40 -0
- package/templates/ci/gitlab.yml.tpl +21 -0
- package/templates/ci/jenkins.tpl +35 -0
- package/templates/contracts/AI_RULES.md.tpl +55 -0
- package/templates/contracts/asyncapi-stub.yaml.tpl +24 -0
- package/templates/contracts/contract-traceability.md.tpl +10 -0
- package/templates/contracts/openapi-stub.yaml.tpl +29 -0
- package/templates/contracts/pacts-gitkeep.tpl +0 -0
- package/templates/contracts/test-strategy.md.tpl +94 -0
- package/templates/frontend/AI_RULES.md.tpl +34 -0
- package/templates/frontend/features/core/health.feature.tpl +6 -0
- package/templates/harness/harness.config.yaml.tpl +74 -0
- package/templates/harness/prompt-prefix.md.tpl +60 -0
- package/templates/mobile/AI_RULES.md.tpl +56 -0
- package/templates/mobile/features/core/health.feature.tpl +7 -0
- package/templates/modules/auth/backend/registration_and_login.feature.tpl +10 -0
- package/templates/modules/auth/frontend/login.feature.tpl +5 -0
- package/templates/modules/billing/backend/billing_settlement.feature.tpl +5 -0
- package/templates/modules/billing/frontend/billing_summary.feature.tpl +5 -0
- package/templates/modules/dashboard/backend/dashboard_kpi.feature.tpl +5 -0
- package/templates/modules/dashboard/frontend/dashboard.feature.tpl +5 -0
- package/vendor/mermaid.min.js +3636 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.InitProjectCommand = exports.KNOWN_KEYS = exports.ARCHITECTURE_DOCS = exports.ARCHITECTURES = exports.PROJECT_TYPES = void 0;
|
|
37
|
+
exports.architectureSections = architectureSections;
|
|
38
|
+
exports.parseArgs = parseArgs;
|
|
39
|
+
exports.parseConfig = parseConfig;
|
|
40
|
+
exports.parseConfigKeyValue = parseConfigKeyValue;
|
|
41
|
+
exports.parseConfigYaml = parseConfigYaml;
|
|
42
|
+
exports.validateConfig = validateConfig;
|
|
43
|
+
const fs = __importStar(require("node:fs"));
|
|
44
|
+
const path = __importStar(require("node:path"));
|
|
45
|
+
const node_child_process_1 = require("node:child_process");
|
|
46
|
+
const YamlLite_1 = require("../../../../packages/core/src/domain/YamlLite");
|
|
47
|
+
const WizardCommand_1 = require("./WizardCommand");
|
|
48
|
+
const command_1 = require("../../../lib/command");
|
|
49
|
+
const project_root_1 = require("../../../lib/project-root");
|
|
50
|
+
const ROOT_DIR = (0, project_root_1.findCliRoot)(__dirname);
|
|
51
|
+
const TEMPLATES_DIR = path.join(ROOT_DIR, "templates");
|
|
52
|
+
exports.PROJECT_TYPES = ["backend", "frontend", "mobile"];
|
|
53
|
+
/**
|
|
54
|
+
* How much architectural vocabulary a project asks to be scaffolded with.
|
|
55
|
+
*
|
|
56
|
+
* ADR-0022: patterns are optional, principles are not. The gate is identical
|
|
57
|
+
* for all three — `validate` never asks which profile a project chose — so this
|
|
58
|
+
* decides what `init` writes and what the agent's rulebook demands, nothing
|
|
59
|
+
* more.
|
|
60
|
+
*/
|
|
61
|
+
exports.ARCHITECTURES = ["minimal", "layered", "tactical-ddd"];
|
|
62
|
+
/**
|
|
63
|
+
* Domain documents each profile keeps. Every profile is a subset of the one
|
|
64
|
+
* below it, which is why raising a profile is additive and lowering it is not.
|
|
65
|
+
*/
|
|
66
|
+
exports.ARCHITECTURE_DOCS = {
|
|
67
|
+
minimal: [],
|
|
68
|
+
layered: ["use-cases.md"],
|
|
69
|
+
"tactical-ddd": [
|
|
70
|
+
"domain-model.md",
|
|
71
|
+
"use-cases.md",
|
|
72
|
+
"commands.md",
|
|
73
|
+
"aggregates.md",
|
|
74
|
+
"events.md",
|
|
75
|
+
"status-model.md",
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
/** Every domain document `templates/base` ships, so the rest can be removed. */
|
|
79
|
+
const ALL_DOMAIN_DOCS = exports.ARCHITECTURE_DOCS["tactical-ddd"];
|
|
80
|
+
/**
|
|
81
|
+
* The agent's rulebook, per profile.
|
|
82
|
+
*
|
|
83
|
+
* This is where the obligation people actually feel lives. `validate` never
|
|
84
|
+
* asks for an aggregate; `AI_RULES.md` does, on every prompt, and the agent
|
|
85
|
+
* obeys prose no machine checks. So the profile has to reach here or it changes
|
|
86
|
+
* nothing that matters (ADR-0022).
|
|
87
|
+
*
|
|
88
|
+
* Two lines appear in every profile because they are principles rather than
|
|
89
|
+
* patterns: work is traceable, and business logic stays out of framework code.
|
|
90
|
+
* The second is not DDD — a business rule inside a controller is equally wrong
|
|
91
|
+
* without aggregates — and it is the part teams believe DDD is giving them.
|
|
92
|
+
*/
|
|
93
|
+
function architectureSections(cfg) {
|
|
94
|
+
const profile = cfg.ARCHITECTURE;
|
|
95
|
+
const isFrontend = cfg.PROJECT_TYPE === "frontend" || cfg.PROJECT_TYPE === "mobile";
|
|
96
|
+
const target = isFrontend
|
|
97
|
+
? "aggregate, read model, or UI state model"
|
|
98
|
+
: "aggregate or read model";
|
|
99
|
+
const invariantRules = [
|
|
100
|
+
"- Do not implement anything that has no requirement and no scenario.",
|
|
101
|
+
"- Keep business logic out of framework code: no business rules in controllers, components, or infrastructure adapters.",
|
|
102
|
+
"- If a new business rule appears during implementation, update the feature scenario first.",
|
|
103
|
+
];
|
|
104
|
+
const modeling = {
|
|
105
|
+
minimal: invariantRules,
|
|
106
|
+
layered: [
|
|
107
|
+
"- Do not implement a flow that is not listed in `docs/specs/use-cases.md` or `docs/specs/traceability.md`.",
|
|
108
|
+
...invariantRules,
|
|
109
|
+
],
|
|
110
|
+
"tactical-ddd": [
|
|
111
|
+
"- Do not implement a use case that is not listed in `docs/specs/use-cases.md` or `docs/specs/traceability.md`.",
|
|
112
|
+
"- Do not create a command without mapping it to a use case.",
|
|
113
|
+
"- Do not emit a domain event that is not listed in `docs/specs/events.md`.",
|
|
114
|
+
"- Keep domain logic independent from HTTP, database, queues, and framework code.",
|
|
115
|
+
...invariantRules,
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
const invariantGates = ["- [ ] Requirement has ID.", "- [ ] Scenario has ID."];
|
|
119
|
+
const gates = {
|
|
120
|
+
minimal: [...invariantGates, "- [ ] Traceability row is complete."],
|
|
121
|
+
layered: [
|
|
122
|
+
...invariantGates,
|
|
123
|
+
"- [ ] Scenario maps to a use case.",
|
|
124
|
+
"- [ ] Traceability row is complete.",
|
|
125
|
+
],
|
|
126
|
+
"tactical-ddd": [
|
|
127
|
+
...invariantGates,
|
|
128
|
+
"- [ ] Scenario maps to a use case.",
|
|
129
|
+
"- [ ] Use case maps to command or query.",
|
|
130
|
+
`- [ ] Command/query maps to ${target}.`,
|
|
131
|
+
"- [ ] Domain events are listed when state changes matter.",
|
|
132
|
+
"- [ ] Traceability row is complete.",
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
const docs = exports.ARCHITECTURE_DOCS[profile] || [];
|
|
136
|
+
const reads = docs.length
|
|
137
|
+
? `2. Read ${docs.map((d) => `\`docs/specs/${d}\``).join(", ")}.`
|
|
138
|
+
: "2. Read `docs/specs/traceability.md`.";
|
|
139
|
+
return {
|
|
140
|
+
ARCHITECTURE_MODELING_RULES: (modeling[profile] || modeling["tactical-ddd"]).join("\n"),
|
|
141
|
+
ARCHITECTURE_GATES: (gates[profile] || gates["tactical-ddd"]).join("\n"),
|
|
142
|
+
ARCHITECTURE_READS: reads,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function logInfo(msg) {
|
|
146
|
+
process.stdout.write(`ℹ️ [INFO] ${msg}\n`);
|
|
147
|
+
}
|
|
148
|
+
function logWarn(msg) {
|
|
149
|
+
process.stdout.write(`⚠️ [WARN] ${msg}\n`);
|
|
150
|
+
}
|
|
151
|
+
function logError(msg) {
|
|
152
|
+
process.stderr.write(`❌ [ERROR] ${msg}\n`);
|
|
153
|
+
}
|
|
154
|
+
function usage() {
|
|
155
|
+
process.stdout.write("Usage:\n" +
|
|
156
|
+
" specgate init [--config <path>] [--out <directory>] [--yes] [--force] [--dry-run]\n" +
|
|
157
|
+
" [--no-git] [--no-sample-req]\n\n" +
|
|
158
|
+
"Options:\n" +
|
|
159
|
+
" --config <path> Configuration file. Accepts a YAML mapping (.yaml/.yml)\n" +
|
|
160
|
+
' or the legacy KEY="value" format (.config). When omitted,\n' +
|
|
161
|
+
" an interactive wizard asks for each value.\n" +
|
|
162
|
+
" --out <dir> Parent directory for generated project (default: current directory)\n" +
|
|
163
|
+
" --yes, -y Skip the wizard and accept every default (non-interactive)\n" +
|
|
164
|
+
" --force Overwrite target directory if it already exists\n" +
|
|
165
|
+
" --dry-run Print actions without writing files\n" +
|
|
166
|
+
" --no-git Skip git initialization\n" +
|
|
167
|
+
" --no-sample-req Omit the starter REQ-000 health requirement. Use when a\n" +
|
|
168
|
+
" domain pack will supply the real ones (implied by\n" +
|
|
169
|
+
" --from-pack).\n");
|
|
170
|
+
}
|
|
171
|
+
function parseArgs(argv) {
|
|
172
|
+
const opts = {
|
|
173
|
+
config: null,
|
|
174
|
+
out: null,
|
|
175
|
+
yes: false,
|
|
176
|
+
force: false,
|
|
177
|
+
dryRun: false,
|
|
178
|
+
noGit: false,
|
|
179
|
+
noSampleReq: false,
|
|
180
|
+
};
|
|
181
|
+
for (let i = 0; i < argv.length; i++) {
|
|
182
|
+
const a = argv[i];
|
|
183
|
+
if (a === "--config" && argv[i + 1]) {
|
|
184
|
+
opts.config = argv[++i];
|
|
185
|
+
}
|
|
186
|
+
else if (a === "--out" && argv[i + 1]) {
|
|
187
|
+
opts.out = argv[++i];
|
|
188
|
+
}
|
|
189
|
+
else if (a === "--yes" || a === "-y") {
|
|
190
|
+
opts.yes = true;
|
|
191
|
+
}
|
|
192
|
+
else if (a === "--force") {
|
|
193
|
+
opts.force = true;
|
|
194
|
+
}
|
|
195
|
+
else if (a === "--dry-run") {
|
|
196
|
+
opts.dryRun = true;
|
|
197
|
+
}
|
|
198
|
+
else if (a === "--no-git") {
|
|
199
|
+
opts.noGit = true;
|
|
200
|
+
}
|
|
201
|
+
else if (a === "--no-sample-req") {
|
|
202
|
+
opts.noSampleReq = true;
|
|
203
|
+
}
|
|
204
|
+
else if (a === "--help" || a === "-h") {
|
|
205
|
+
usage();
|
|
206
|
+
process.exit(0);
|
|
207
|
+
}
|
|
208
|
+
else if (a === "--engine" || a.startsWith("--engine=")) {
|
|
209
|
+
/* consumed by dispatcher */
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
logError(`Unknown argument: ${a}`);
|
|
213
|
+
usage();
|
|
214
|
+
process.exit(2);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return opts;
|
|
218
|
+
}
|
|
219
|
+
function stripInlineComment(s) {
|
|
220
|
+
return s.replace(/#.*$/, "");
|
|
221
|
+
}
|
|
222
|
+
function stripQuotes(s) {
|
|
223
|
+
s = s.trim();
|
|
224
|
+
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
|
|
225
|
+
return s.slice(1, -1);
|
|
226
|
+
}
|
|
227
|
+
return s;
|
|
228
|
+
}
|
|
229
|
+
exports.KNOWN_KEYS = new Set([
|
|
230
|
+
"PROJECT_NAME",
|
|
231
|
+
"PROJECT_SLUG",
|
|
232
|
+
"PROJECT_TYPE",
|
|
233
|
+
"ARCHITECTURE",
|
|
234
|
+
"DOMAIN",
|
|
235
|
+
"LANG",
|
|
236
|
+
"STACK",
|
|
237
|
+
"API_STYLE",
|
|
238
|
+
"TESTING",
|
|
239
|
+
"ENVIRONMENTS",
|
|
240
|
+
"DEFAULT_ENV",
|
|
241
|
+
"DOCKER_SUPPORT",
|
|
242
|
+
"DEVCONTAINER_SUPPORT",
|
|
243
|
+
"DATASTORE",
|
|
244
|
+
"DATABASE_ENGINE",
|
|
245
|
+
"DATABASE_VERSION",
|
|
246
|
+
"DATABASE_IMAGE",
|
|
247
|
+
"DATABASE_HOST",
|
|
248
|
+
"DATABASE_PORT",
|
|
249
|
+
"DATABASE_PORT_DEV",
|
|
250
|
+
"DATABASE_PORT_FEATURE",
|
|
251
|
+
"DATABASE_PORT_PROD",
|
|
252
|
+
"DATABASE_CONTAINER_PORT",
|
|
253
|
+
"DATABASE_NAME",
|
|
254
|
+
"DATABASE_USER",
|
|
255
|
+
"DATABASE_PASSWORD",
|
|
256
|
+
"MODULES",
|
|
257
|
+
]);
|
|
258
|
+
function parseConfig(configPath) {
|
|
259
|
+
if (!fs.existsSync(configPath)) {
|
|
260
|
+
logError(`Config file not found: ${configPath}`);
|
|
261
|
+
process.exit(2);
|
|
262
|
+
}
|
|
263
|
+
const content = fs.readFileSync(configPath, "utf8");
|
|
264
|
+
const ext = path.extname(configPath).toLowerCase();
|
|
265
|
+
return ext === ".yaml" || ext === ".yml"
|
|
266
|
+
? parseConfigYaml(content)
|
|
267
|
+
: parseConfigKeyValue(content);
|
|
268
|
+
}
|
|
269
|
+
function parseConfigKeyValue(content) {
|
|
270
|
+
const raw = {};
|
|
271
|
+
for (const line of content.split("\n")) {
|
|
272
|
+
const trimmed = line.trim();
|
|
273
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
274
|
+
continue;
|
|
275
|
+
const eq = trimmed.indexOf("=");
|
|
276
|
+
if (eq === -1) {
|
|
277
|
+
logError(`Invalid config line: ${trimmed}`);
|
|
278
|
+
process.exit(2);
|
|
279
|
+
}
|
|
280
|
+
const key = trimmed.slice(0, eq).trim();
|
|
281
|
+
const value = stripQuotes(stripInlineComment(trimmed.slice(eq + 1)).trim());
|
|
282
|
+
if (exports.KNOWN_KEYS.has(key)) {
|
|
283
|
+
raw[key] = value;
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
logWarn(`Unknown config key (ignored): ${key}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return raw;
|
|
290
|
+
}
|
|
291
|
+
function parseConfigYaml(content) {
|
|
292
|
+
let parsed;
|
|
293
|
+
try {
|
|
294
|
+
parsed = (0, YamlLite_1.parseYamlLite)(content);
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
logError(`Invalid YAML config: ${err.message}`);
|
|
298
|
+
process.exit(2);
|
|
299
|
+
}
|
|
300
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
301
|
+
logError("YAML config must be a flat mapping of KEY: value pairs.");
|
|
302
|
+
process.exit(2);
|
|
303
|
+
}
|
|
304
|
+
const raw = {};
|
|
305
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
306
|
+
if (exports.KNOWN_KEYS.has(key)) {
|
|
307
|
+
raw[key] = value === null || value === undefined ? "" : String(value);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
logWarn(`Unknown config key (ignored): ${key}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return raw;
|
|
314
|
+
}
|
|
315
|
+
function hasDatastore(cfg) {
|
|
316
|
+
return cfg.DATASTORE !== "none";
|
|
317
|
+
}
|
|
318
|
+
function runtimeDockerSection(cfg) {
|
|
319
|
+
if (cfg.DOCKER_SUPPORT !== "true") {
|
|
320
|
+
return [
|
|
321
|
+
"## Docker",
|
|
322
|
+
"",
|
|
323
|
+
"Docker support is disabled for this project (`DOCKER_SUPPORT=false`), so no",
|
|
324
|
+
"`docker-compose.yml`, `.dockerignore` or `.devcontainer/` is generated. The",
|
|
325
|
+
"environment contract above still holds: provision the database yourself and",
|
|
326
|
+
"point `DATABASE_URL` at it.",
|
|
327
|
+
].join("\n");
|
|
328
|
+
}
|
|
329
|
+
const service = hasDatastore(cfg) ? cfg.DATABASE_HOST : "workspace";
|
|
330
|
+
const lines = [
|
|
331
|
+
"## Docker",
|
|
332
|
+
"",
|
|
333
|
+
"- Compose file: `docker-compose.yml`",
|
|
334
|
+
...(hasDatastore(cfg) ? [`- Database image: \`${cfg.DATABASE_IMAGE}\``] : []),
|
|
335
|
+
`- Service name: \`${service}\``,
|
|
336
|
+
"",
|
|
337
|
+
"Bring one environment up:",
|
|
338
|
+
"",
|
|
339
|
+
"```bash",
|
|
340
|
+
`APP_ENV=dev docker compose --env-file .env.dev up -d ${service}`,
|
|
341
|
+
`APP_ENV=feature docker compose --env-file .env.feature up -d ${service}`,
|
|
342
|
+
`APP_ENV=prod docker compose --env-file .env.prod up -d ${service}`,
|
|
343
|
+
"```",
|
|
344
|
+
];
|
|
345
|
+
if (cfg.DEVCONTAINER_SUPPORT === "true") {
|
|
346
|
+
lines.push("", "## Devcontainer", "", "- Configuration: `.devcontainer/devcontainer.json`", ...(hasDatastore(cfg)
|
|
347
|
+
? [
|
|
348
|
+
"- The devcontainer joins the same Compose network as the database service,",
|
|
349
|
+
` so it reaches it at \`${cfg.DATABASE_HOST}:${cfg.DATABASE_CONTAINER_PORT}\`.`,
|
|
350
|
+
]
|
|
351
|
+
: ["- The devcontainer runs the workspace service defined in `docker-compose.yml`."]));
|
|
352
|
+
}
|
|
353
|
+
return lines.join("\n");
|
|
354
|
+
}
|
|
355
|
+
function runtimeEnvTable(cfg) {
|
|
356
|
+
const rows = [
|
|
357
|
+
["dev", "Local development and day-to-day implementation", "`.env.dev`", cfg.DATABASE_NAME_DEV],
|
|
358
|
+
[
|
|
359
|
+
"feature",
|
|
360
|
+
"Short-lived branch or preview validation",
|
|
361
|
+
"`.env.feature`",
|
|
362
|
+
cfg.DATABASE_NAME_FEATURE,
|
|
363
|
+
],
|
|
364
|
+
["prod", "Production-like configuration contract", "`.env.prod`", cfg.DATABASE_NAME_PROD],
|
|
365
|
+
];
|
|
366
|
+
if (!hasDatastore(cfg)) {
|
|
367
|
+
return [
|
|
368
|
+
"| Environment | Purpose | Env file |",
|
|
369
|
+
"| --- | --- | --- |",
|
|
370
|
+
...rows.map((r) => `| ${r[0]} | ${r[1]} | ${r[2]} |`),
|
|
371
|
+
].join("\n");
|
|
372
|
+
}
|
|
373
|
+
return [
|
|
374
|
+
"| Environment | Purpose | Env file | Database |",
|
|
375
|
+
"| --- | --- | --- | --- |",
|
|
376
|
+
...rows.map((r) => `| ${r[0]} | ${r[1]} | ${r[2]} | \`${r[3]}\` |`),
|
|
377
|
+
].join("\n");
|
|
378
|
+
}
|
|
379
|
+
function runtimeDatabaseSection(cfg) {
|
|
380
|
+
if (!hasDatastore(cfg)) {
|
|
381
|
+
return [
|
|
382
|
+
"## Data",
|
|
383
|
+
"",
|
|
384
|
+
"This project owns no datastore (`DATASTORE=none`). It reads its data through",
|
|
385
|
+
"the APIs declared in `AI_RULES.md`; the environments above differ in",
|
|
386
|
+
"configuration, not in schema.",
|
|
387
|
+
].join("\n");
|
|
388
|
+
}
|
|
389
|
+
return [
|
|
390
|
+
"## Database",
|
|
391
|
+
"",
|
|
392
|
+
`- Engine: \`${cfg.DATABASE_ENGINE}\` (version \`${cfg.DATABASE_VERSION}\`)`,
|
|
393
|
+
`- Application user: \`${cfg.DATABASE_USER}\``,
|
|
394
|
+
"",
|
|
395
|
+
"| Environment | Host port | URL |",
|
|
396
|
+
"| --- | --- | --- |",
|
|
397
|
+
`| dev | \`${cfg.DATABASE_PORT_DEV}\` | \`${cfg.DATABASE_URL_DEV}\` |`,
|
|
398
|
+
`| feature | \`${cfg.DATABASE_PORT_FEATURE}\` | \`${cfg.DATABASE_URL_FEATURE}\` |`,
|
|
399
|
+
`| prod | \`${cfg.DATABASE_PORT_PROD}\` | \`${cfg.DATABASE_URL_PROD}\` |`,
|
|
400
|
+
"",
|
|
401
|
+
"Each environment binds a different host port so two of them can run side by",
|
|
402
|
+
`side. Inside the network every database listens on \`${cfg.DATABASE_CONTAINER_PORT}\`.`,
|
|
403
|
+
].join("\n");
|
|
404
|
+
}
|
|
405
|
+
function runtimeInvariants(cfg) {
|
|
406
|
+
const invariants = [];
|
|
407
|
+
if (hasDatastore(cfg)) {
|
|
408
|
+
invariants.push("**No shared databases.** Each environment owns its own database name; the three above never point at the same instance.");
|
|
409
|
+
}
|
|
410
|
+
invariants.push("**No credentials in the repository.** The generated `.env.*` files carry placeholders. Real secrets arrive from the deployment platform or a secret manager, never from a commit.", "**No hardcoded configuration.** Hosts, ports and credentials are read from the environment at runtime.", "**`.env.example` is the schema.** Every variable the application reads must appear there, with a safe placeholder value.", "**Adding an environment is a spec change.** Extend this document and the `.env.*` set together — a new environment that exists only in deployment configuration is drift.");
|
|
411
|
+
return invariants.map((line, i) => `${i + 1}. ${line}`).join("\n");
|
|
412
|
+
}
|
|
413
|
+
function composeDependsOn(cfg) {
|
|
414
|
+
return hasDatastore(cfg) ? " depends_on:\n db:\n condition: service_healthy" : "";
|
|
415
|
+
}
|
|
416
|
+
function composeDbService(cfg) {
|
|
417
|
+
if (!hasDatastore(cfg))
|
|
418
|
+
return "";
|
|
419
|
+
return `
|
|
420
|
+
db:
|
|
421
|
+
image: ${cfg.DATABASE_IMAGE}
|
|
422
|
+
restart: unless-stopped
|
|
423
|
+
env_file:
|
|
424
|
+
- .env.\${APP_ENV:-${cfg.DEFAULT_ENV}}.infra
|
|
425
|
+
ports:
|
|
426
|
+
- "\${DATABASE_PORT:-${cfg.DATABASE_PORT_DEV}}:${cfg.DATABASE_CONTAINER_PORT}"
|
|
427
|
+
volumes:
|
|
428
|
+
- db_data:/var/lib/postgresql/data
|
|
429
|
+
healthcheck:
|
|
430
|
+
test: ["CMD-SHELL", "pg_isready -U $$\${POSTGRES_USER} -d $$\${POSTGRES_DB}"]
|
|
431
|
+
interval: 10s
|
|
432
|
+
timeout: 5s
|
|
433
|
+
retries: 5
|
|
434
|
+
|
|
435
|
+
volumes:
|
|
436
|
+
db_data:
|
|
437
|
+
name: ${cfg.PROJECT_SLUG}_\${APP_ENV:-${cfg.DEFAULT_ENV}}_db_data`;
|
|
438
|
+
}
|
|
439
|
+
function validateConfig(cfg) {
|
|
440
|
+
const required = [
|
|
441
|
+
"PROJECT_NAME",
|
|
442
|
+
"PROJECT_SLUG",
|
|
443
|
+
"PROJECT_TYPE",
|
|
444
|
+
"DOMAIN",
|
|
445
|
+
"STACK",
|
|
446
|
+
"API_STYLE",
|
|
447
|
+
"TESTING",
|
|
448
|
+
];
|
|
449
|
+
for (const k of required) {
|
|
450
|
+
if (!cfg[k]) {
|
|
451
|
+
logError(`Missing ${k} in config`);
|
|
452
|
+
process.exit(2);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
cfg.LANG = cfg.LANG || "en";
|
|
456
|
+
cfg.MODULES = cfg.MODULES || "";
|
|
457
|
+
cfg.ENVIRONMENTS = cfg.ENVIRONMENTS || "dev,feature,prod";
|
|
458
|
+
cfg.DEFAULT_ENV = cfg.DEFAULT_ENV || "dev";
|
|
459
|
+
cfg.DOCKER_SUPPORT = cfg.DOCKER_SUPPORT || "true";
|
|
460
|
+
cfg.DEVCONTAINER_SUPPORT = cfg.DEVCONTAINER_SUPPORT || "true";
|
|
461
|
+
cfg.DATABASE_ENGINE = cfg.DATABASE_ENGINE || "postgres";
|
|
462
|
+
cfg.DATABASE_VERSION = cfg.DATABASE_VERSION || "16";
|
|
463
|
+
cfg.DATABASE_HOST = cfg.DATABASE_HOST || "db";
|
|
464
|
+
cfg.DATABASE_PORT = cfg.DATABASE_PORT || "5432";
|
|
465
|
+
cfg.DATABASE_PORT_DEV = cfg.DATABASE_PORT_DEV || cfg.DATABASE_PORT;
|
|
466
|
+
cfg.DATABASE_PORT_FEATURE = cfg.DATABASE_PORT_FEATURE || "5433";
|
|
467
|
+
cfg.DATABASE_PORT_PROD = cfg.DATABASE_PORT_PROD || "5434";
|
|
468
|
+
cfg.DATABASE_CONTAINER_PORT = cfg.DATABASE_CONTAINER_PORT || "5432";
|
|
469
|
+
cfg.DATABASE_NAME = cfg.DATABASE_NAME || cfg.PROJECT_SLUG.replace(/-/g, "_");
|
|
470
|
+
cfg.DATABASE_NAME_DEV = `${cfg.DATABASE_NAME}_dev`;
|
|
471
|
+
cfg.DATABASE_NAME_FEATURE = `${cfg.DATABASE_NAME}_feature`;
|
|
472
|
+
cfg.DATABASE_NAME_PROD = `${cfg.DATABASE_NAME}_prod`;
|
|
473
|
+
cfg.DATABASE_USER = cfg.DATABASE_USER || `${cfg.DATABASE_NAME}_app`;
|
|
474
|
+
cfg.DATABASE_PASSWORD = cfg.DATABASE_PASSWORD || "change-me";
|
|
475
|
+
cfg.DATASTORE = cfg.DATASTORE || (cfg.PROJECT_TYPE === "backend" ? "postgres" : "none");
|
|
476
|
+
const SUPPORTED_DATASTORES = ["postgres", "none"];
|
|
477
|
+
if (!SUPPORTED_DATASTORES.includes(cfg.DATASTORE)) {
|
|
478
|
+
logError(`DATASTORE '${cfg.DATASTORE}' is not supported. Supported: ${SUPPORTED_DATASTORES.join(", ")}`);
|
|
479
|
+
process.exit(2);
|
|
480
|
+
}
|
|
481
|
+
// Unchanged default on purpose: ADR-0022 records the reasoning for `layered`
|
|
482
|
+
// being the better recommendation, and leaves moving the default to a major,
|
|
483
|
+
// because it changes what an unchanged command produces.
|
|
484
|
+
cfg.ARCHITECTURE = cfg.ARCHITECTURE || "tactical-ddd";
|
|
485
|
+
if (!exports.ARCHITECTURES.includes(cfg.ARCHITECTURE)) {
|
|
486
|
+
logError(`ARCHITECTURE '${cfg.ARCHITECTURE}' is not supported. Supported: ${exports.ARCHITECTURES.join(", ")}`);
|
|
487
|
+
process.exit(2);
|
|
488
|
+
}
|
|
489
|
+
const SUPPORTED_ENGINES = ["postgres"];
|
|
490
|
+
if (!SUPPORTED_ENGINES.includes(cfg.DATABASE_ENGINE)) {
|
|
491
|
+
logError(`DATABASE_ENGINE '${cfg.DATABASE_ENGINE}' is not supported. Supported engines: ${SUPPORTED_ENGINES.join(", ")}`);
|
|
492
|
+
process.exit(2);
|
|
493
|
+
}
|
|
494
|
+
if (cfg.DOCKER_SUPPORT !== "true" && cfg.DEVCONTAINER_SUPPORT === "true") {
|
|
495
|
+
logError("DEVCONTAINER_SUPPORT requires DOCKER_SUPPORT=true");
|
|
496
|
+
process.exit(2);
|
|
497
|
+
}
|
|
498
|
+
cfg.DATABASE_IMAGE = cfg.DATABASE_IMAGE || `postgres:${cfg.DATABASE_VERSION}`;
|
|
499
|
+
const dbUrl = (db) => `postgresql://${cfg.DATABASE_USER}:${cfg.DATABASE_PASSWORD}@${cfg.DATABASE_HOST}:${cfg.DATABASE_CONTAINER_PORT}/${db}`;
|
|
500
|
+
cfg.DATABASE_URL_DEV = dbUrl(cfg.DATABASE_NAME_DEV);
|
|
501
|
+
cfg.DATABASE_URL_FEATURE = dbUrl(cfg.DATABASE_NAME_FEATURE);
|
|
502
|
+
cfg.DATABASE_URL_PROD = dbUrl(cfg.DATABASE_NAME_PROD);
|
|
503
|
+
Object.assign(cfg, architectureSections(cfg));
|
|
504
|
+
cfg.RUNTIME_DOCKER_SECTION = runtimeDockerSection(cfg);
|
|
505
|
+
cfg.RUNTIME_ENV_TABLE = runtimeEnvTable(cfg);
|
|
506
|
+
cfg.RUNTIME_DATABASE_SECTION = runtimeDatabaseSection(cfg);
|
|
507
|
+
cfg.RUNTIME_INVARIANTS = runtimeInvariants(cfg);
|
|
508
|
+
cfg.COMPOSE_DEPENDS_ON = composeDependsOn(cfg);
|
|
509
|
+
cfg.COMPOSE_DB_SERVICE = composeDbService(cfg);
|
|
510
|
+
if (!exports.PROJECT_TYPES.includes(cfg.PROJECT_TYPE)) {
|
|
511
|
+
logError(`PROJECT_TYPE must be one of ${exports.PROJECT_TYPES.join(", ")}`);
|
|
512
|
+
process.exit(2);
|
|
513
|
+
}
|
|
514
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(cfg.PROJECT_SLUG)) {
|
|
515
|
+
logError("PROJECT_SLUG must use lowercase letters, numbers, and dashes (example: my-project)");
|
|
516
|
+
process.exit(2);
|
|
517
|
+
}
|
|
518
|
+
return cfg;
|
|
519
|
+
}
|
|
520
|
+
function renderContent(content, vars) {
|
|
521
|
+
return content.replace(/{{([A-Z][A-Z0-9_]*)}}/g, (_, token) => {
|
|
522
|
+
if (!(token in vars)) {
|
|
523
|
+
logWarn(`Template variable '{{${token}}}' not defined — leaving as-is.`);
|
|
524
|
+
return `{{${token}}}`;
|
|
525
|
+
}
|
|
526
|
+
return String(vars[token]);
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
function renderFile(src, dst, vars, dryRun) {
|
|
530
|
+
if (dryRun) {
|
|
531
|
+
logInfo(`[dry-run] render ${src} -> ${dst}`);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const content = fs.readFileSync(src, "utf8");
|
|
535
|
+
const rendered = renderContent(content, vars);
|
|
536
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
537
|
+
fs.writeFileSync(dst, rendered, "utf8");
|
|
538
|
+
}
|
|
539
|
+
function walkDir(dir) {
|
|
540
|
+
const results = [];
|
|
541
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
542
|
+
const full = path.join(dir, entry.name);
|
|
543
|
+
if (entry.isDirectory())
|
|
544
|
+
results.push(...walkDir(full));
|
|
545
|
+
else
|
|
546
|
+
results.push(full);
|
|
547
|
+
}
|
|
548
|
+
return results;
|
|
549
|
+
}
|
|
550
|
+
function renderTree(srcRoot, dstRoot, vars, dryRun) {
|
|
551
|
+
if (!fs.existsSync(srcRoot))
|
|
552
|
+
return;
|
|
553
|
+
const allFiles = walkDir(srcRoot).filter((f) => f.endsWith(".tpl"));
|
|
554
|
+
allFiles.sort();
|
|
555
|
+
for (const tpl of allFiles) {
|
|
556
|
+
const rel = path.relative(srcRoot, tpl);
|
|
557
|
+
const dstRel = rel.replace(/\.tpl$/, "");
|
|
558
|
+
renderFile(tpl, path.join(dstRoot, dstRel), vars, dryRun);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function applyRuntimeSupportFlags(projectDir, cfg, dryRun) {
|
|
562
|
+
if (cfg.DATASTORE === "none" && !dryRun) {
|
|
563
|
+
for (const file of fs.readdirSync(projectDir)) {
|
|
564
|
+
if (!/^\.env\b/.test(file))
|
|
565
|
+
continue;
|
|
566
|
+
const full = path.join(projectDir, file);
|
|
567
|
+
const kept = fs
|
|
568
|
+
.readFileSync(full, "utf8")
|
|
569
|
+
.split("\n")
|
|
570
|
+
.filter((line) => !/^\s*(DATABASE_|POSTGRES_)/.test(line));
|
|
571
|
+
fs.writeFileSync(full, kept.join("\n").replace(/\n{3,}/g, "\n\n"), "utf8");
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (cfg.DOCKER_SUPPORT !== "true") {
|
|
575
|
+
if (dryRun) {
|
|
576
|
+
logInfo("[dry-run] skip Docker artifacts");
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
for (const f of ["docker-compose.yml", ".dockerignore"]) {
|
|
580
|
+
const p = path.join(projectDir, f);
|
|
581
|
+
if (fs.existsSync(p))
|
|
582
|
+
fs.rmSync(p);
|
|
583
|
+
}
|
|
584
|
+
for (const f of fs.readdirSync(projectDir)) {
|
|
585
|
+
if (/^\.env\.\w+\.(infra|app)$/.test(f)) {
|
|
586
|
+
fs.rmSync(path.join(projectDir, f));
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (cfg.DEVCONTAINER_SUPPORT !== "true") {
|
|
592
|
+
if (dryRun) {
|
|
593
|
+
logInfo("[dry-run] skip devcontainer artifacts");
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
const dc = path.join(projectDir, ".devcontainer");
|
|
597
|
+
if (fs.existsSync(dc))
|
|
598
|
+
fs.rmSync(dc, { recursive: true, force: true });
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Keep only the domain documents the declared profile asks for.
|
|
603
|
+
*
|
|
604
|
+
* Same shape as `applyRuntimeSupportFlags`: the templates render in full and
|
|
605
|
+
* what the configuration did not ask for is removed, so there is one rendering
|
|
606
|
+
* path rather than three.
|
|
607
|
+
*
|
|
608
|
+
* A static site given `aggregates.md` is not merely untidy — an agent reads it
|
|
609
|
+
* as a description of the system and invents aggregates to fill it. Removing
|
|
610
|
+
* the file is the point, not tidiness (ADR-0022).
|
|
611
|
+
*/
|
|
612
|
+
function applyArchitectureProfile(projectDir, cfg, dryRun) {
|
|
613
|
+
const keep = new Set(exports.ARCHITECTURE_DOCS[cfg.ARCHITECTURE] || ALL_DOMAIN_DOCS);
|
|
614
|
+
const drop = ALL_DOMAIN_DOCS.filter((doc) => !keep.has(doc));
|
|
615
|
+
if (drop.length === 0)
|
|
616
|
+
return;
|
|
617
|
+
if (dryRun) {
|
|
618
|
+
logInfo(`[dry-run] ${cfg.ARCHITECTURE}: skip ${drop.join(", ")}`);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
for (const doc of drop) {
|
|
622
|
+
const full = path.join(projectDir, "docs", "specs", doc);
|
|
623
|
+
if (fs.existsSync(full))
|
|
624
|
+
fs.rmSync(full);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function removeSampleRequirement(projectDir, dryRun) {
|
|
628
|
+
const traceFile = path.join(projectDir, "docs/specs/traceability.md");
|
|
629
|
+
if (fs.existsSync(traceFile)) {
|
|
630
|
+
const content = fs.readFileSync(traceFile, "utf8");
|
|
631
|
+
const kept = content
|
|
632
|
+
.split("\n")
|
|
633
|
+
.filter((line) => !/^\|\s*REQ-000\s*\|/.test(line))
|
|
634
|
+
.join("\n");
|
|
635
|
+
if (kept !== content && !dryRun)
|
|
636
|
+
fs.writeFileSync(traceFile, kept, "utf8");
|
|
637
|
+
}
|
|
638
|
+
const healthFeature = path.join(projectDir, "features", "core", "health.feature");
|
|
639
|
+
if (fs.existsSync(healthFeature) && !dryRun) {
|
|
640
|
+
fs.rmSync(healthFeature);
|
|
641
|
+
const coreDir = path.dirname(healthFeature);
|
|
642
|
+
if (fs.existsSync(coreDir) && fs.readdirSync(coreDir).length === 0)
|
|
643
|
+
fs.rmdirSync(coreDir);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function featureTitleFromPath(rel) {
|
|
647
|
+
return path.basename(rel, ".feature").replace(/_/g, " ");
|
|
648
|
+
}
|
|
649
|
+
function ensureTraceabilityCoverage(projectDir) {
|
|
650
|
+
const traceFile = path.join(projectDir, "docs/specs/traceability.md");
|
|
651
|
+
if (!fs.existsSync(traceFile))
|
|
652
|
+
return;
|
|
653
|
+
const content = fs.readFileSync(traceFile, "utf8");
|
|
654
|
+
const isRich = content.includes("| Requirement | Scenario ID | Feature file | Use Case | Command/Query | Aggregate | Event | Technical artifact | Test artifact | Status |");
|
|
655
|
+
const featuresDir = path.join(projectDir, "features");
|
|
656
|
+
if (!fs.existsSync(featuresDir))
|
|
657
|
+
return;
|
|
658
|
+
const featureFiles = walkDir(featuresDir)
|
|
659
|
+
.filter((f) => f.endsWith(".feature"))
|
|
660
|
+
.sort();
|
|
661
|
+
const lines = [];
|
|
662
|
+
let counter = 1;
|
|
663
|
+
for (const ff of featureFiles) {
|
|
664
|
+
const rel = path.relative(projectDir, ff).split(path.sep).join("/");
|
|
665
|
+
if (content.includes(rel))
|
|
666
|
+
continue;
|
|
667
|
+
const title = featureTitleFromPath(rel);
|
|
668
|
+
const scenarioId = `SCN-TBD-${String(counter).padStart(3, "0")}`;
|
|
669
|
+
const ucId = `UC-TBD-${String(counter).padStart(3, "0")} ${title}`;
|
|
670
|
+
if (isRich) {
|
|
671
|
+
lines.push(`| REQ-TBD | ${scenarioId} | \`${rel}\` | ${ucId} | TBD | TBD | TBD | TBD | TBD | Draft |`);
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
lines.push(`| \`${rel}\` | ${title} | TBD | Draft |`);
|
|
675
|
+
}
|
|
676
|
+
counter++;
|
|
677
|
+
}
|
|
678
|
+
if (lines.length > 0) {
|
|
679
|
+
fs.appendFileSync(traceFile, "\n" + lines.join("\n") + "\n", "utf8");
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
class InitProjectCommand extends command_1.BaseCommand {
|
|
683
|
+
async execute() {
|
|
684
|
+
let rawArgs = this.args;
|
|
685
|
+
if (rawArgs[0] === "init")
|
|
686
|
+
rawArgs = rawArgs.slice(1);
|
|
687
|
+
const opts = parseArgs(rawArgs);
|
|
688
|
+
let wizardAnswers = null;
|
|
689
|
+
let cfg;
|
|
690
|
+
if (opts.config) {
|
|
691
|
+
cfg = validateConfig(parseConfig(opts.config));
|
|
692
|
+
}
|
|
693
|
+
else if (opts.yes) {
|
|
694
|
+
wizardAnswers = (0, WizardCommand_1.defaultAnswers)();
|
|
695
|
+
cfg = validateConfig({ ...wizardAnswers });
|
|
696
|
+
}
|
|
697
|
+
else if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
698
|
+
logInfo("No --config given — starting the interactive wizard (Enter accepts the default).");
|
|
699
|
+
wizardAnswers = await (0, WizardCommand_1.runInitWizard)();
|
|
700
|
+
cfg = validateConfig({ ...wizardAnswers });
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
logError("--config is required when not running in a terminal.");
|
|
704
|
+
logInfo("Fix: run `specgate init` in a terminal for the interactive wizard,");
|
|
705
|
+
logInfo(" pass --yes to scaffold with defaults, or provide --config <path>.");
|
|
706
|
+
process.exit(2);
|
|
707
|
+
}
|
|
708
|
+
if (!opts.out) {
|
|
709
|
+
logInfo(`No --out given — using the current directory: ${process.cwd()}`);
|
|
710
|
+
}
|
|
711
|
+
const outDir = opts.out ? path.resolve(opts.out) : process.cwd();
|
|
712
|
+
const projectDir = path.join(outDir, cfg.PROJECT_SLUG);
|
|
713
|
+
if (fs.existsSync(projectDir) && !opts.force) {
|
|
714
|
+
logError(`Destination already exists: ${projectDir} (use --force to overwrite)`);
|
|
715
|
+
process.exit(4);
|
|
716
|
+
}
|
|
717
|
+
if (opts.dryRun) {
|
|
718
|
+
logInfo(`[dry-run] project would be generated at: ${projectDir}`);
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
if (fs.existsSync(projectDir))
|
|
722
|
+
fs.rmSync(projectDir, { recursive: true, force: true });
|
|
723
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
724
|
+
}
|
|
725
|
+
logInfo("🧩 Rendering base template");
|
|
726
|
+
renderTree(path.join(TEMPLATES_DIR, "base"), projectDir, cfg, opts.dryRun);
|
|
727
|
+
applyRuntimeSupportFlags(projectDir, cfg, opts.dryRun);
|
|
728
|
+
applyArchitectureProfile(projectDir, cfg, opts.dryRun);
|
|
729
|
+
logInfo(`🛠️ Applying project type template: ${cfg.PROJECT_TYPE}`);
|
|
730
|
+
renderFile(path.join(TEMPLATES_DIR, cfg.PROJECT_TYPE, "AI_RULES.md.tpl"), path.join(projectDir, "AI_RULES.md"), cfg, opts.dryRun);
|
|
731
|
+
if (opts.noSampleReq) {
|
|
732
|
+
logInfo("🧩 Skipping the sample requirement (--no-sample-req)");
|
|
733
|
+
if (!opts.dryRun)
|
|
734
|
+
fs.mkdirSync(path.join(projectDir, "features"), { recursive: true });
|
|
735
|
+
removeSampleRequirement(projectDir, opts.dryRun);
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
renderTree(path.join(TEMPLATES_DIR, cfg.PROJECT_TYPE, "features"), path.join(projectDir, "features"), cfg, opts.dryRun);
|
|
739
|
+
}
|
|
740
|
+
if (cfg.MODULES) {
|
|
741
|
+
for (let mod of cfg.MODULES.split(",")) {
|
|
742
|
+
mod = mod.trim();
|
|
743
|
+
if (!mod)
|
|
744
|
+
continue;
|
|
745
|
+
const modTpl = path.join(TEMPLATES_DIR, "modules", mod, cfg.PROJECT_TYPE);
|
|
746
|
+
if (fs.existsSync(modTpl)) {
|
|
747
|
+
logInfo(`➕ Adding module: ${mod}`);
|
|
748
|
+
renderTree(modTpl, path.join(projectDir, "features", mod), cfg, opts.dryRun);
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
logWarn(`Template missing for module '${mod}' and type '${cfg.PROJECT_TYPE}'`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
logInfo("🧩 No optional modules selected. Generating base + project-type features only.");
|
|
757
|
+
}
|
|
758
|
+
if (!opts.dryRun) {
|
|
759
|
+
ensureTraceabilityCoverage(projectDir);
|
|
760
|
+
if (wizardAnswers) {
|
|
761
|
+
fs.writeFileSync(path.join(projectDir, "project.yaml"), (0, WizardCommand_1.renderConfigYaml)(wizardAnswers), "utf8");
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (!opts.noGit) {
|
|
765
|
+
if (opts.dryRun) {
|
|
766
|
+
logInfo(`[dry-run] git init at ${projectDir}`);
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
const git = (0, node_child_process_1.spawnSync)("git", ["init"], { cwd: projectDir, stdio: "ignore" });
|
|
770
|
+
if (git.error || git.status !== 0) {
|
|
771
|
+
logWarn("git init failed — skipping git initialization.");
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
logInfo("📋 Summary");
|
|
776
|
+
logInfo(`- Project: ${cfg.PROJECT_NAME}`);
|
|
777
|
+
logInfo(`- Slug: ${cfg.PROJECT_SLUG}`);
|
|
778
|
+
logInfo(`- Type: ${cfg.PROJECT_TYPE}`);
|
|
779
|
+
logInfo(`- Domain: ${cfg.DOMAIN}`);
|
|
780
|
+
logInfo(`- Output: ${projectDir}`);
|
|
781
|
+
logInfo(`- Dry-run: ${opts.dryRun}`);
|
|
782
|
+
logInfo(`- Git: ${opts.noGit ? "skipped" : "initialized"}`);
|
|
783
|
+
if (wizardAnswers && !opts.dryRun) {
|
|
784
|
+
logInfo("- Config: saved to project.yaml (reproduce with `init --config project.yaml`)");
|
|
785
|
+
}
|
|
786
|
+
logInfo("✅ Generation completed");
|
|
787
|
+
process.exit(0);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
exports.InitProjectCommand = InitProjectCommand;
|
|
791
|
+
//# sourceMappingURL=InitProjectCommand.js.map
|