@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,867 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* `specgate change <sub>` — the change lifecycle.
|
|
5
|
+
*
|
|
6
|
+
* new · list · show · status · validate · archive
|
|
7
|
+
*
|
|
8
|
+
* Every subcommand accepts `--json` and, in that mode, prints exactly one JSON
|
|
9
|
+
* document on stdout while prose goes to stderr. Failures print the command's
|
|
10
|
+
* null-shape plus a `status` array of diagnostics and exit 1. That contract is
|
|
11
|
+
* what lets an agent drive this without screen-scraping.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.ARTIFACTS = exports.artifactState = exports.CliCommand = exports.TEMPLATES = void 0;
|
|
48
|
+
const fs = __importStar(require("node:fs"));
|
|
49
|
+
const path = __importStar(require("node:path"));
|
|
50
|
+
const command_1 = require("../../../lib/command");
|
|
51
|
+
const project_root_1 = require("../../../lib/project-root");
|
|
52
|
+
const os = __importStar(require("node:os"));
|
|
53
|
+
const node_child_process_1 = require("node:child_process");
|
|
54
|
+
const diagnostics_1 = require("../../../lib/diagnostics");
|
|
55
|
+
const instructions_1 = require("../../../change/instructions");
|
|
56
|
+
const HarnessConfigFile_1 = require("../../../../packages/core/src/infrastructure/HarnessConfigFile");
|
|
57
|
+
const AuthoringScope_1 = require("../../../../packages/core/src/domain/AuthoringScope");
|
|
58
|
+
const agent_1 = require("../../../lib/agent");
|
|
59
|
+
const DiskLanguageRepository_1 = require("../../../../packages/core/src/infrastructure/DiskLanguageRepository");
|
|
60
|
+
const artifacts_1 = require("../../../change/artifacts");
|
|
61
|
+
Object.defineProperty(exports, "ARTIFACTS", { enumerable: true, get: function () { return artifacts_1.ARTIFACTS; } });
|
|
62
|
+
Object.defineProperty(exports, "artifactState", { enumerable: true, get: function () { return artifacts_1.artifactState; } });
|
|
63
|
+
const SpecParser_1 = require("../../../../packages/core/src/domain/SpecParser");
|
|
64
|
+
const ArchiveChangeUseCase_1 = require("../../../../packages/core/src/application/ArchiveChangeUseCase");
|
|
65
|
+
const DiskProjectRepository_1 = require("../../../../packages/core/src/infrastructure/DiskProjectRepository");
|
|
66
|
+
const ChangeWorkspace_1 = require("../../../../packages/core/src/infrastructure/ChangeWorkspace");
|
|
67
|
+
const ChangeTemplates_1 = require("../../../../packages/core/src/domain/ChangeTemplates");
|
|
68
|
+
const ValidateChangeUseCase_1 = require("../../../../packages/core/src/application/ValidateChangeUseCase");
|
|
69
|
+
const capability_specs_1 = require("../../../lib/capability-specs");
|
|
70
|
+
const ValueAnnotations_1 = require("../../../../packages/core/src/domain/ValueAnnotations");
|
|
71
|
+
const DeclaredArtifacts_1 = require("../../../../packages/core/src/domain/DeclaredArtifacts");
|
|
72
|
+
const PlanCommand_1 = require("../spec/PlanCommand");
|
|
73
|
+
const instructions_2 = require("../../../change/instructions");
|
|
74
|
+
/**
|
|
75
|
+
* The scaffold set as the CLI hands it out: `(changeId, projectDir)`, with the
|
|
76
|
+
* project's phrase table resolved here. `CHANGE_TEMPLATES` itself is pure and
|
|
77
|
+
* takes the table directly.
|
|
78
|
+
*/
|
|
79
|
+
exports.TEMPLATES = {
|
|
80
|
+
...ChangeTemplates_1.CHANGE_TEMPLATES,
|
|
81
|
+
specs: (_changeId, projectDir) => (0, ChangeTemplates_1.templateDelta)("<capability>", "REQ-NNN", (0, DiskLanguageRepository_1.phrases)(projectDir)),
|
|
82
|
+
};
|
|
83
|
+
const COLOR_ENABLED = process.stdout.isTTY && process.env.NO_COLOR === undefined && process.env.TERM !== "dumb";
|
|
84
|
+
const c = {
|
|
85
|
+
reset: COLOR_ENABLED ? "\x1b[0m" : "",
|
|
86
|
+
bold: COLOR_ENABLED ? "\x1b[1m" : "",
|
|
87
|
+
dim: COLOR_ENABLED ? "\x1b[2m" : "",
|
|
88
|
+
green: COLOR_ENABLED ? "\x1b[32m" : "",
|
|
89
|
+
yellow: COLOR_ENABLED ? "\x1b[33m" : "",
|
|
90
|
+
cyan: COLOR_ENABLED ? "\x1b[36m" : "",
|
|
91
|
+
red: COLOR_ENABLED ? "\x1b[31m" : "",
|
|
92
|
+
};
|
|
93
|
+
// The artefact graph. Dependencies are enablers, not gates: `requires` says
|
|
94
|
+
// what makes an artefact *possible*, never what the author must do next.
|
|
95
|
+
// F3 makes this configurable per schema; for now the built-in is the default.
|
|
96
|
+
/** The skeletons `change new` writes, shared with `change instructions`. */
|
|
97
|
+
function usage() {
|
|
98
|
+
process.stdout.write(`\n ${c.bold}${c.cyan}🔄 change${c.reset} ${c.dim}— propose, review and archive a change${c.reset}\n\n` +
|
|
99
|
+
` ${c.bold}USAGE${c.reset}\n` +
|
|
100
|
+
` ${c.cyan}specgate change${c.reset} <new|list|show|status|instructions|author|validate|archive> [options]\n\n` +
|
|
101
|
+
` ${c.bold}SUBCOMMANDS${c.reset}\n` +
|
|
102
|
+
` ${c.green}new <id>${c.reset} ${c.dim}Scaffold a change folder (proposal, tasks, delta skeleton).${c.reset}\n` +
|
|
103
|
+
` ${c.green}list${c.reset} ${c.dim}Active changes with task progress.${c.reset}\n` +
|
|
104
|
+
` ${c.green}show <id>${c.reset} ${c.dim}Proposal, deltas and what they touch.${c.reset}\n` +
|
|
105
|
+
` ${c.green}status [id]${c.reset} ${c.dim}Which artefact to write next.${c.reset}\n` +
|
|
106
|
+
` ${c.green}author <id>${c.reset} ${c.dim}Have an agent write one artefact, confined to the change.${c.reset}\n` +
|
|
107
|
+
` ${c.green}validate [id]${c.reset} ${c.dim}Check the deltas against the capability specs.${c.reset}\n` +
|
|
108
|
+
` ${c.green}archive <id>${c.reset} ${c.dim}Merge deltas into the specs, sync traceability, archive.${c.reset}\n\n` +
|
|
109
|
+
` ${c.bold}OPTIONS${c.reset}\n` +
|
|
110
|
+
` ${c.green}--project-dir <path>${c.reset} ${c.dim}Project root (default: auto-detected).${c.reset}\n` +
|
|
111
|
+
` ${c.green}--json${c.reset} ${c.dim}One JSON document on stdout; prose on stderr.${c.reset}\n` +
|
|
112
|
+
` ${c.green}--capability <name>${c.reset} ${c.dim}(new) Seed a delta for this capability.${c.reset}\n` +
|
|
113
|
+
` ${c.green}--from-value-drift <REQ:id>${c.reset} ${c.dim}(new) Propose a spec update for a diverging` +
|
|
114
|
+
` declared value (§8.6).${c.reset}\n` +
|
|
115
|
+
` ${c.green}--full${c.reset} ${c.dim}(new) Full rigor — also scaffold design.md.${c.reset}\n` +
|
|
116
|
+
` ${c.green}--artifact <name>${c.reset} ${c.dim}(author) Which artefact to write. Default: proposal.${c.reset}\n` +
|
|
117
|
+
` ${c.green}--agent <cmd>${c.reset} ${c.dim}(author) Agent command; must contain {prompt_file}.${c.reset}\n` +
|
|
118
|
+
` ${c.green}--agent-profile <n>${c.reset} ${c.dim}(author) A profile from .harness/profiles.yaml.${c.reset}\n` +
|
|
119
|
+
` ${c.green}--reserve <n>${c.reset} ${c.dim}(new) Reserve n REQ ids for this change (default 3).${c.reset}\n` +
|
|
120
|
+
` ${c.green}--strict${c.reset} ${c.dim}(validate) Treat advisory warnings as failures.${c.reset}\n` +
|
|
121
|
+
` ${c.green}--dry-run${c.reset} ${c.dim}(archive) Print the plan, write nothing.${c.reset}\n` +
|
|
122
|
+
` ${c.green}--force${c.reset} ${c.dim}(archive) Archive with unchecked tasks; overwrite features.${c.reset}\n\n`);
|
|
123
|
+
}
|
|
124
|
+
function parseArgs(argv) {
|
|
125
|
+
const opts = {
|
|
126
|
+
projectDir: ".",
|
|
127
|
+
json: false,
|
|
128
|
+
strict: false,
|
|
129
|
+
dryRun: false,
|
|
130
|
+
force: false,
|
|
131
|
+
full: false,
|
|
132
|
+
capability: null,
|
|
133
|
+
fromValueDrift: null,
|
|
134
|
+
reserve: 3,
|
|
135
|
+
positional: [],
|
|
136
|
+
};
|
|
137
|
+
for (let i = 0; i < argv.length; i++) {
|
|
138
|
+
const a = argv[i];
|
|
139
|
+
if (a === "--project-dir" && argv[i + 1])
|
|
140
|
+
opts.projectDir = argv[++i];
|
|
141
|
+
else if (a === "--capability" && argv[i + 1])
|
|
142
|
+
opts.capability = argv[++i];
|
|
143
|
+
else if (a === "--from-value-drift" && argv[i + 1])
|
|
144
|
+
opts.fromValueDrift = argv[++i];
|
|
145
|
+
else if (a === "--schema" && argv[i + 1])
|
|
146
|
+
opts.schema = argv[++i];
|
|
147
|
+
else if (a === "--reserve" && argv[i + 1])
|
|
148
|
+
opts.reserve = parseInt(argv[++i], 10) || 3;
|
|
149
|
+
else if (a === "--artifact" && argv[i + 1])
|
|
150
|
+
opts.artifact = argv[++i];
|
|
151
|
+
else if (a === "--agent" && argv[i + 1])
|
|
152
|
+
opts.agent = argv[++i];
|
|
153
|
+
else if (a === "--agent-profile" && argv[i + 1])
|
|
154
|
+
opts.agentProfile = argv[++i];
|
|
155
|
+
else if (a === "--json")
|
|
156
|
+
opts.json = true;
|
|
157
|
+
else if (a === "--strict")
|
|
158
|
+
opts.strict = true;
|
|
159
|
+
else if (a === "--dry-run")
|
|
160
|
+
opts.dryRun = true;
|
|
161
|
+
else if (a === "--force")
|
|
162
|
+
opts.force = true;
|
|
163
|
+
else if (a === "--yes" || a === "-y")
|
|
164
|
+
opts.yes = true;
|
|
165
|
+
else if (a === "--full")
|
|
166
|
+
opts.full = true;
|
|
167
|
+
else if (a === "--lite")
|
|
168
|
+
opts.full = false;
|
|
169
|
+
else if (a === "--help" || a === "-h") {
|
|
170
|
+
usage();
|
|
171
|
+
process.exit(0);
|
|
172
|
+
}
|
|
173
|
+
else if (a.startsWith("-")) {
|
|
174
|
+
process.stderr.write(`Unknown flag: ${a}\n`);
|
|
175
|
+
process.exit(2);
|
|
176
|
+
}
|
|
177
|
+
else
|
|
178
|
+
opts.positional.push(a);
|
|
179
|
+
}
|
|
180
|
+
return opts;
|
|
181
|
+
}
|
|
182
|
+
// The contract itself lives in scripts/lib/agent.ts; these are thin adapters
|
|
183
|
+
// that keep the existing `opts`-first call sites unchanged.
|
|
184
|
+
const fail = (opts, nullShape, diags) => (0, agent_1.agentIo)(opts.json).fail(nullShape, diags);
|
|
185
|
+
const emit = (opts, payload, renderHuman) => (0, agent_1.agentIo)(opts.json).emit(payload, renderHuman);
|
|
186
|
+
/**
|
|
187
|
+
* Route 2 of the three-way resolution for a declared-value divergence
|
|
188
|
+
* (§8.6): `REQ-ID:value_id` becomes a `MODIFIED Requirements` delta
|
|
189
|
+
* proposing the spec take the code's value. Routes 1 ("fix the code") and 3
|
|
190
|
+
* ("retire the requirement") need no tooling of their own — `specgate report`
|
|
191
|
+
* already points at the code file:line for route 1, and route 3 is
|
|
192
|
+
* `change new` with a hand-written `REMOVED Requirements` section, same as
|
|
193
|
+
* retiring any other requirement.
|
|
194
|
+
*/
|
|
195
|
+
function resolveValueDriftDelta(projectDir, spec) {
|
|
196
|
+
const sep = spec.indexOf(":");
|
|
197
|
+
const reqId = sep === -1 ? "" : spec.slice(0, sep);
|
|
198
|
+
const valueId = sep === -1 ? "" : spec.slice(sep + 1);
|
|
199
|
+
if (!reqId || !valueId) {
|
|
200
|
+
return {
|
|
201
|
+
diagnostics: [
|
|
202
|
+
(0, diagnostics_1.error)("invalid_value_drift_spec", `"${spec}" is not REQ-ID:value_id.`, {
|
|
203
|
+
fix: "e.g. --from-value-drift REQ-100:session_timeout",
|
|
204
|
+
}),
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const found = (0, capability_specs_1.readCapabilityRequirements)(projectDir).find((c) => c.req.id === reqId);
|
|
209
|
+
if (!found) {
|
|
210
|
+
return {
|
|
211
|
+
diagnostics: [
|
|
212
|
+
(0, diagnostics_1.error)("value_drift_requirement_not_found", `No capability spec declares ${reqId}.`, {
|
|
213
|
+
target: reqId,
|
|
214
|
+
fix: "Check the id, or that docs/specs/capabilities/ exists.",
|
|
215
|
+
}),
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
const { req, capability } = found;
|
|
220
|
+
const specValue = (0, ValueAnnotations_1.declaredSpecValues)(req.trace).find((v) => v.id === valueId);
|
|
221
|
+
if (!specValue) {
|
|
222
|
+
return {
|
|
223
|
+
diagnostics: [
|
|
224
|
+
(0, diagnostics_1.error)("value_drift_id_not_declared", `declares no value_${valueId} in its csda:trace.`, {
|
|
225
|
+
target: reqId,
|
|
226
|
+
fix: `Add value_${valueId}=<literal> to its csda:trace, or check the id.`,
|
|
227
|
+
}),
|
|
228
|
+
],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
let rows = [];
|
|
232
|
+
const tracePath = path.join(projectDir, "docs/specs/traceability.md");
|
|
233
|
+
if (fs.existsSync(tracePath)) {
|
|
234
|
+
try {
|
|
235
|
+
rows = (0, PlanCommand_1.parseTraceability)(fs.readFileSync(tracePath, "utf8"));
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
rows = [];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const row = rows.find((r) => r.requirement === reqId);
|
|
242
|
+
const codeFiles = new Set();
|
|
243
|
+
for (const cell of [row && row.technicalArtifact, row && row.testArtifact]) {
|
|
244
|
+
for (const rel of (0, DeclaredArtifacts_1.declaredPaths)(cell))
|
|
245
|
+
codeFiles.add(rel.split("#")[0]);
|
|
246
|
+
}
|
|
247
|
+
let codeValue = null;
|
|
248
|
+
for (const rel of codeFiles) {
|
|
249
|
+
const abs = path.join(projectDir, rel);
|
|
250
|
+
if (!fs.existsSync(abs))
|
|
251
|
+
continue;
|
|
252
|
+
const hit = (0, ValueAnnotations_1.declaredCodeValues)(fs.readFileSync(abs, "utf8")).find((e) => e.id === valueId);
|
|
253
|
+
if (hit) {
|
|
254
|
+
codeValue = hit.value;
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (codeValue === null) {
|
|
259
|
+
return {
|
|
260
|
+
diagnostics: [
|
|
261
|
+
(0, diagnostics_1.error)("value_drift_no_code_value", `No \`csda:value ${valueId}=\` marker found in ${reqId}'s declared Technical/Test artifact.`, {
|
|
262
|
+
target: reqId,
|
|
263
|
+
fix: "Either the marker is missing, or the declared file doesn't exist yet — see `specgate report` / --strict-links.",
|
|
264
|
+
}),
|
|
265
|
+
],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
if (codeValue === specValue.value) {
|
|
269
|
+
return {
|
|
270
|
+
diagnostics: [
|
|
271
|
+
(0, diagnostics_1.error)("value_drift_already_matches", `value_${valueId} already matches the code (${codeValue}) — nothing to propose.`, { target: reqId }),
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
capability,
|
|
277
|
+
content: (0, ChangeTemplates_1.templateValueDriftDelta)(capability, req, valueId, specValue.value, codeValue),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
// ── new ───────────────────────────────────────────────────────────────────────
|
|
281
|
+
function cmdNew(opts) {
|
|
282
|
+
const projectDir = (0, project_root_1.resolveProjectDir)(opts.projectDir);
|
|
283
|
+
const p = (0, ChangeWorkspace_1.paths)(projectDir);
|
|
284
|
+
const changeId = opts.positional[0];
|
|
285
|
+
const nullShape = { change: null };
|
|
286
|
+
if (!changeId) {
|
|
287
|
+
return fail(opts, nullShape, [
|
|
288
|
+
(0, diagnostics_1.error)("change_id_required", "`change new` expects a change id.", {
|
|
289
|
+
fix: "e.g. `specgate change new add-dynamic-pricing`",
|
|
290
|
+
}),
|
|
291
|
+
]);
|
|
292
|
+
}
|
|
293
|
+
if (!ChangeWorkspace_1.CHANGE_ID_RE.test(changeId)) {
|
|
294
|
+
return fail(opts, nullShape, [
|
|
295
|
+
(0, diagnostics_1.error)("invalid_change_id", `"${changeId}" is not a valid change id.`, {
|
|
296
|
+
target: changeId,
|
|
297
|
+
fix: "Use lowercase kebab-case, e.g. add-dynamic-pricing.",
|
|
298
|
+
}),
|
|
299
|
+
]);
|
|
300
|
+
}
|
|
301
|
+
const dir = p.change(changeId);
|
|
302
|
+
if (fs.existsSync(dir)) {
|
|
303
|
+
return fail(opts, nullShape, [
|
|
304
|
+
(0, diagnostics_1.error)("change_exists", `Change "${changeId}" already exists.`, {
|
|
305
|
+
target: changeId,
|
|
306
|
+
fix: `Edit ${path.relative(projectDir, dir)}, or pick another id.`,
|
|
307
|
+
}),
|
|
308
|
+
]);
|
|
309
|
+
}
|
|
310
|
+
if (opts.capability && opts.fromValueDrift) {
|
|
311
|
+
return fail(opts, nullShape, [
|
|
312
|
+
(0, diagnostics_1.error)("conflicting_seed_flags", "--capability and --from-value-drift cannot both be given.", {
|
|
313
|
+
fix: "Use one or the other — they seed the delta two different ways.",
|
|
314
|
+
}),
|
|
315
|
+
]);
|
|
316
|
+
}
|
|
317
|
+
let valueDrift = null;
|
|
318
|
+
if (opts.fromValueDrift) {
|
|
319
|
+
const resolved = resolveValueDriftDelta(projectDir, opts.fromValueDrift);
|
|
320
|
+
if ("diagnostics" in resolved) {
|
|
321
|
+
return fail(opts, nullShape, resolved.diagnostics);
|
|
322
|
+
}
|
|
323
|
+
valueDrift = resolved;
|
|
324
|
+
}
|
|
325
|
+
const reqRange = (0, ChangeWorkspace_1.reserveReqRange)(projectDir, opts.reserve);
|
|
326
|
+
const config = {
|
|
327
|
+
...ChangeWorkspace_1.DEFAULT_CONFIG,
|
|
328
|
+
schema: opts.schema || ChangeWorkspace_1.DEFAULT_CONFIG.schema,
|
|
329
|
+
created: new Date().toISOString().slice(0, 10),
|
|
330
|
+
rigor: opts.full ? "full" : "lite",
|
|
331
|
+
req_range: reqRange,
|
|
332
|
+
};
|
|
333
|
+
const created = [];
|
|
334
|
+
const write = (file, contents) => {
|
|
335
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
336
|
+
fs.writeFileSync(file, contents, "utf8");
|
|
337
|
+
created.push(path.relative(projectDir, file).split(path.sep).join("/"));
|
|
338
|
+
};
|
|
339
|
+
write(path.join(dir, ChangeWorkspace_1.CHANGE_CONFIG_FILE), (0, ChangeWorkspace_1.renderConfig)(config));
|
|
340
|
+
write(path.join(dir, "proposal.md"), (0, ChangeTemplates_1.templateProposal)(changeId));
|
|
341
|
+
write(path.join(dir, "tasks.md"), (0, ChangeTemplates_1.templateTasks)());
|
|
342
|
+
if (opts.full)
|
|
343
|
+
write(path.join(dir, "design.md"), (0, ChangeTemplates_1.templateDesign)(changeId));
|
|
344
|
+
if (opts.capability) {
|
|
345
|
+
write(path.join(p.changeSpecs(changeId), opts.capability, "spec.md"), (0, ChangeTemplates_1.templateDelta)(opts.capability, reqRange[0], (0, DiskLanguageRepository_1.phrases)(projectDir)));
|
|
346
|
+
}
|
|
347
|
+
if (valueDrift) {
|
|
348
|
+
write(path.join(p.changeSpecs(changeId), valueDrift.capability, "spec.md"), valueDrift.content);
|
|
349
|
+
}
|
|
350
|
+
emit(opts, {
|
|
351
|
+
change: {
|
|
352
|
+
id: changeId,
|
|
353
|
+
path: path.relative(projectDir, dir).split(path.sep).join("/"),
|
|
354
|
+
schema: config.schema,
|
|
355
|
+
rigor: config.rigor,
|
|
356
|
+
reqRange,
|
|
357
|
+
created,
|
|
358
|
+
},
|
|
359
|
+
}, () => {
|
|
360
|
+
process.stdout.write(`\n ${c.green}✔${c.reset} Change ${c.bold}${changeId}${c.reset} created ` +
|
|
361
|
+
`${c.dim}(${config.rigor} · REQ ids ${reqRange[0]}…${reqRange[1]} reserved)${c.reset}\n\n` +
|
|
362
|
+
created.map((f) => ` ${c.dim}+${c.reset} ${f}\n`).join("") +
|
|
363
|
+
`\n ${c.dim}Next:${c.reset} write the proposal, then the delta, then ` +
|
|
364
|
+
`${c.cyan}specgate change validate ${changeId}${c.reset}\n\n`);
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
// ── list ──────────────────────────────────────────────────────────────────────
|
|
368
|
+
function cmdList(opts) {
|
|
369
|
+
const projectDir = (0, project_root_1.resolveProjectDir)(opts.projectDir);
|
|
370
|
+
const ids = (0, ChangeWorkspace_1.listChangeIds)(projectDir);
|
|
371
|
+
const changes = ids.map((id) => {
|
|
372
|
+
const progress = (0, ChangeWorkspace_1.taskProgress)(projectDir, id);
|
|
373
|
+
const config = (0, ChangeWorkspace_1.readConfig)(projectDir, id);
|
|
374
|
+
return {
|
|
375
|
+
id,
|
|
376
|
+
schema: config.schema,
|
|
377
|
+
rigor: config.rigor,
|
|
378
|
+
completedTasks: progress.complete,
|
|
379
|
+
totalTasks: progress.total,
|
|
380
|
+
deltaCount: (0, ChangeWorkspace_1.listDeltas)(projectDir, id).length,
|
|
381
|
+
status: (0, ChangeWorkspace_1.changeStatusLabel)(progress),
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
emit(opts, { changes, archived: (0, ChangeWorkspace_1.listArchivedIds)(projectDir).length }, () => {
|
|
385
|
+
if (changes.length === 0) {
|
|
386
|
+
process.stdout.write(`\n ${c.dim}No active changes.${c.reset}\n\n`);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
process.stdout.write(`\n ${c.bold}Active changes${c.reset}\n\n`);
|
|
390
|
+
for (const ch of changes) {
|
|
391
|
+
const mark = ch.status === "complete"
|
|
392
|
+
? `${c.green}●${c.reset}`
|
|
393
|
+
: ch.status === "in-progress"
|
|
394
|
+
? `${c.yellow}◐${c.reset}`
|
|
395
|
+
: `${c.dim}○${c.reset}`;
|
|
396
|
+
process.stdout.write(` ${mark} ${c.bold}${ch.id}${c.reset} ` +
|
|
397
|
+
`${c.dim}${ch.completedTasks}/${ch.totalTasks} tasks · ${ch.deltaCount} delta(s) · ${ch.rigor}${c.reset}\n`);
|
|
398
|
+
}
|
|
399
|
+
process.stdout.write("\n");
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
// ── show ──────────────────────────────────────────────────────────────────────
|
|
403
|
+
function cmdShow(opts) {
|
|
404
|
+
const projectDir = (0, project_root_1.resolveProjectDir)(opts.projectDir);
|
|
405
|
+
const p = (0, ChangeWorkspace_1.paths)(projectDir);
|
|
406
|
+
const changeId = opts.positional[0];
|
|
407
|
+
const nullShape = { change: null };
|
|
408
|
+
if (!changeId || !fs.existsSync(p.change(changeId))) {
|
|
409
|
+
return fail(opts, nullShape, [
|
|
410
|
+
(0, diagnostics_1.error)("change_not_found", `Change "${changeId || "(none)"}" does not exist.`, {
|
|
411
|
+
target: changeId,
|
|
412
|
+
fix: "Run `specgate change list`.",
|
|
413
|
+
}),
|
|
414
|
+
]);
|
|
415
|
+
}
|
|
416
|
+
const deltas = (0, ChangeWorkspace_1.listDeltas)(projectDir, changeId).map((entry) => {
|
|
417
|
+
const parsed = (0, SpecParser_1.parseDelta)(fs.readFileSync(entry.file, "utf8"));
|
|
418
|
+
return {
|
|
419
|
+
capability: entry.capability,
|
|
420
|
+
file: entry.relative,
|
|
421
|
+
added: parsed.added.map((r) => r.heading),
|
|
422
|
+
modified: parsed.modified.map((r) => r.heading),
|
|
423
|
+
removed: parsed.removed.map((r) => r.heading),
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
const proposalFile = path.join(p.change(changeId), "proposal.md");
|
|
427
|
+
const proposal = fs.existsSync(proposalFile) ? fs.readFileSync(proposalFile, "utf8") : "";
|
|
428
|
+
const titleMatch = /^#\s+(.+)$/m.exec(proposal);
|
|
429
|
+
const progress = (0, ChangeWorkspace_1.taskProgress)(projectDir, changeId);
|
|
430
|
+
emit(opts, {
|
|
431
|
+
change: {
|
|
432
|
+
id: changeId,
|
|
433
|
+
title: titleMatch ? titleMatch[1] : changeId,
|
|
434
|
+
config: (0, ChangeWorkspace_1.readConfig)(projectDir, changeId),
|
|
435
|
+
deltaCount: deltas.length,
|
|
436
|
+
deltas,
|
|
437
|
+
tasks: { total: progress.total, complete: progress.complete },
|
|
438
|
+
},
|
|
439
|
+
}, () => {
|
|
440
|
+
process.stdout.write(`\n ${c.bold}${titleMatch ? titleMatch[1] : changeId}${c.reset}\n\n`);
|
|
441
|
+
if (deltas.length === 0) {
|
|
442
|
+
process.stdout.write(` ${c.dim}No delta specs.${c.reset}\n\n`);
|
|
443
|
+
}
|
|
444
|
+
for (const d of deltas) {
|
|
445
|
+
process.stdout.write(` ${c.cyan}${d.capability}${c.reset} ${c.dim}(${d.file})${c.reset}\n`);
|
|
446
|
+
for (const r of d.added)
|
|
447
|
+
process.stdout.write(` ${c.green}+${c.reset} ${r}\n`);
|
|
448
|
+
for (const r of d.modified)
|
|
449
|
+
process.stdout.write(` ${c.yellow}~${c.reset} ${r}\n`);
|
|
450
|
+
for (const r of d.removed)
|
|
451
|
+
process.stdout.write(` ${c.red}-${c.reset} ${r}\n`);
|
|
452
|
+
}
|
|
453
|
+
process.stdout.write(`\n ${c.dim}${progress.complete}/${progress.total} tasks complete${c.reset}\n\n`);
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
// ── status ────────────────────────────────────────────────────────────────────
|
|
457
|
+
function cmdStatus(opts) {
|
|
458
|
+
const projectDir = (0, project_root_1.resolveProjectDir)(opts.projectDir);
|
|
459
|
+
const ids = (0, ChangeWorkspace_1.listChangeIds)(projectDir);
|
|
460
|
+
const changeId = opts.positional[0] || (ids.length === 1 ? ids[0] : null);
|
|
461
|
+
if (!changeId) {
|
|
462
|
+
const payload = {
|
|
463
|
+
changes: ids,
|
|
464
|
+
message: ids.length === 0 ? "No active changes." : "Several active changes — name the one you mean.",
|
|
465
|
+
};
|
|
466
|
+
return emit(opts, payload, () => {
|
|
467
|
+
process.stdout.write(`\n ${c.dim}${payload.message}${c.reset}\n\n`);
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
const p = (0, ChangeWorkspace_1.paths)(projectDir);
|
|
471
|
+
if (!fs.existsSync(p.change(changeId))) {
|
|
472
|
+
return fail(opts, { changeName: null, artifacts: [] }, [
|
|
473
|
+
(0, diagnostics_1.error)("change_not_found", `Change "${changeId}" does not exist.`, {
|
|
474
|
+
target: changeId,
|
|
475
|
+
fix: "Run `specgate change list`.",
|
|
476
|
+
}),
|
|
477
|
+
]);
|
|
478
|
+
}
|
|
479
|
+
const config = (0, ChangeWorkspace_1.readConfig)(projectDir, changeId);
|
|
480
|
+
const artifacts = (0, artifacts_1.artifactState)(projectDir, changeId, config);
|
|
481
|
+
const progress = (0, ChangeWorkspace_1.taskProgress)(projectDir, changeId);
|
|
482
|
+
const isPlanningComplete = artifacts.every((a) => a.status === "done" || a.status === "skipped");
|
|
483
|
+
const nextSteps = artifacts
|
|
484
|
+
.filter((a) => a.status === "ready")
|
|
485
|
+
.map((a) => `Write ${a.outputPath}`);
|
|
486
|
+
if (isPlanningComplete && progress.remaining > 0) {
|
|
487
|
+
nextSteps.push(`Work through tasks.md (${progress.remaining} remaining)`);
|
|
488
|
+
}
|
|
489
|
+
if (isPlanningComplete && progress.remaining === 0) {
|
|
490
|
+
nextSteps.push(`Archive with \`specgate change archive ${changeId}\``);
|
|
491
|
+
}
|
|
492
|
+
emit(opts, {
|
|
493
|
+
changeName: changeId,
|
|
494
|
+
schemaName: config.schema,
|
|
495
|
+
artifacts,
|
|
496
|
+
tasks: { total: progress.total, complete: progress.complete, remaining: progress.remaining },
|
|
497
|
+
isPlanningComplete,
|
|
498
|
+
nextSteps,
|
|
499
|
+
}, () => {
|
|
500
|
+
process.stdout.write(`\n ${c.bold}${changeId}${c.reset} ${c.dim}(${config.schema})${c.reset}\n\n`);
|
|
501
|
+
const mark = {
|
|
502
|
+
done: `${c.green}✔${c.reset}`,
|
|
503
|
+
ready: `${c.yellow}▶${c.reset}`,
|
|
504
|
+
blocked: `${c.dim}·${c.reset}`,
|
|
505
|
+
skipped: `${c.dim}—${c.reset}`,
|
|
506
|
+
};
|
|
507
|
+
for (const a of artifacts) {
|
|
508
|
+
const deps = a.missingDeps ? ` ${c.dim}(needs ${a.missingDeps.join(", ")})${c.reset}` : "";
|
|
509
|
+
process.stdout.write(` ${mark[a.status]} ${a.id.padEnd(10)} ${c.dim}${a.outputPath}${c.reset}${deps}\n`);
|
|
510
|
+
}
|
|
511
|
+
process.stdout.write(`\n ${c.bold}Next${c.reset}\n`);
|
|
512
|
+
for (const step of nextSteps)
|
|
513
|
+
process.stdout.write(` ${c.cyan}→${c.reset} ${step}\n`);
|
|
514
|
+
process.stdout.write("\n");
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
// ── validate ──────────────────────────────────────────────────────────────────
|
|
518
|
+
/**
|
|
519
|
+
* `specgate change author` — hand one change artefact to an agent, then hold it to
|
|
520
|
+
* the same gate a human would face.
|
|
521
|
+
*
|
|
522
|
+
* This is the `spec-author` role of the multi-agent harness (E2-02), and it is
|
|
523
|
+
* deliberately *not* part of `harness run`. That loop is built around a
|
|
524
|
+
* requirement: a worktree per REQ, a branch named for it, and a gate of
|
|
525
|
+
* `validate --strict-tdd` plus the project's tests. A change has no requirement
|
|
526
|
+
* yet — writing one is the whole job — so it needs its own loop, its own scope
|
|
527
|
+
* and its own gate: `specgate change validate`.
|
|
528
|
+
*
|
|
529
|
+
* The prompt is not invented here either. `change instructions` already knows
|
|
530
|
+
* what each artefact is for, what rules it must satisfy, what it may unlock and
|
|
531
|
+
* which REQ ids are reserved; this renders that same structure for an agent
|
|
532
|
+
* rather than for a person.
|
|
533
|
+
*
|
|
534
|
+
* **The tree must be clean before it runs.** That is not ceremony: enforcing the
|
|
535
|
+
* scope means reverting whatever the agent wrote outside the change directory,
|
|
536
|
+
* and on a dirty tree "whatever the agent wrote" cannot be told apart from
|
|
537
|
+
* whatever you were in the middle of. Clean first, and reverting can only ever
|
|
538
|
+
* discard the agent's own work.
|
|
539
|
+
*/
|
|
540
|
+
function cmdAuthor(opts) {
|
|
541
|
+
const projectDir = (0, project_root_1.resolveProjectDir)(opts.projectDir);
|
|
542
|
+
const changeId = opts.positional[0];
|
|
543
|
+
const artifact = opts.artifact || "proposal";
|
|
544
|
+
const NULL_SHAPE = { change: null, artifact: null, wrote: [], reverted: [] };
|
|
545
|
+
if (!changeId) {
|
|
546
|
+
return fail(opts, NULL_SHAPE, [
|
|
547
|
+
(0, diagnostics_1.error)("change_required", "change author needs a change id.", {
|
|
548
|
+
fix: "specgate change author <id> [--artifact proposal|specs|design|tasks]",
|
|
549
|
+
}),
|
|
550
|
+
]);
|
|
551
|
+
}
|
|
552
|
+
const p = (0, ChangeWorkspace_1.paths)(projectDir);
|
|
553
|
+
if (!fs.existsSync(p.change(changeId))) {
|
|
554
|
+
return fail(opts, NULL_SHAPE, [
|
|
555
|
+
(0, diagnostics_1.error)("change_not_found", `Change "${changeId}" does not exist.`, {
|
|
556
|
+
target: changeId,
|
|
557
|
+
fix: `Create it first: specgate change new ${changeId}`,
|
|
558
|
+
}),
|
|
559
|
+
]);
|
|
560
|
+
}
|
|
561
|
+
const agentCommand = resolveAuthorAgent(projectDir, opts);
|
|
562
|
+
if (!agentCommand) {
|
|
563
|
+
return fail(opts, NULL_SHAPE, [
|
|
564
|
+
(0, diagnostics_1.error)("author_agent_unset", "No agent is configured for change author.", {
|
|
565
|
+
fix: 'Pass --agent "<cmd with {prompt_file}>", or --agent-profile <name> from .harness/profiles.yaml.',
|
|
566
|
+
}),
|
|
567
|
+
]);
|
|
568
|
+
}
|
|
569
|
+
const status = (0, node_child_process_1.spawnSync)("git", ["-C", projectDir, "status", "--porcelain"], {
|
|
570
|
+
encoding: "utf8",
|
|
571
|
+
});
|
|
572
|
+
if (status.status !== 0) {
|
|
573
|
+
return fail(opts, NULL_SHAPE, [
|
|
574
|
+
(0, diagnostics_1.error)("author_needs_git", "change author needs a git repository.", {
|
|
575
|
+
fix: "Run it inside the project's repository — the scope is enforced with git.",
|
|
576
|
+
}),
|
|
577
|
+
]);
|
|
578
|
+
}
|
|
579
|
+
if (String(status.stdout).trim() !== "") {
|
|
580
|
+
return fail(opts, NULL_SHAPE, [
|
|
581
|
+
(0, diagnostics_1.error)("author_tree_dirty", "The working tree has uncommitted changes.", {
|
|
582
|
+
fix: "Commit or stash first. The scope is enforced by reverting what the agent wrote outside the change, and on a dirty tree that would take your work with it.",
|
|
583
|
+
}),
|
|
584
|
+
]);
|
|
585
|
+
}
|
|
586
|
+
const instructions = (0, instructions_1.buildInstructions)(projectDir, artifact, changeId, exports.TEMPLATES);
|
|
587
|
+
const prompt = authorPrompt(changeId, artifact, instructions);
|
|
588
|
+
if (opts.dryRun) {
|
|
589
|
+
return emit(opts, { change: changeId, artifact, wrote: [], reverted: [], prompt }, () => process.stdout.write(`${prompt}\n`));
|
|
590
|
+
}
|
|
591
|
+
const promptFile = path.join(os.tmpdir(), `csda-author-${changeId}-${artifact}-${Date.now()}.md`);
|
|
592
|
+
fs.writeFileSync(promptFile, prompt, "utf8");
|
|
593
|
+
try {
|
|
594
|
+
const command = agentCommand.split("{prompt_file}").join(promptFile);
|
|
595
|
+
const agent = (0, node_child_process_1.spawnSync)(command, {
|
|
596
|
+
shell: true,
|
|
597
|
+
cwd: projectDir,
|
|
598
|
+
encoding: "utf8",
|
|
599
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
600
|
+
});
|
|
601
|
+
if (agent.status !== 0) {
|
|
602
|
+
return fail(opts, NULL_SHAPE, [
|
|
603
|
+
(0, diagnostics_1.error)("author_agent_failed", `The agent exited ${agent.status}.`, {
|
|
604
|
+
fix: `${agent.stdout || ""}${agent.stderr || ""}`.trim().slice(-500) ||
|
|
605
|
+
"Re-run with --dry-run to see the prompt it was given.",
|
|
606
|
+
}),
|
|
607
|
+
]);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
finally {
|
|
611
|
+
fs.rmSync(promptFile, { force: true });
|
|
612
|
+
}
|
|
613
|
+
const touched = (0, node_child_process_1.spawnSync)("git", ["-C", projectDir, "status", "--porcelain"], {
|
|
614
|
+
encoding: "utf8",
|
|
615
|
+
});
|
|
616
|
+
// Keep git's status code, not just the path. `??` means untracked — the agent
|
|
617
|
+
// created it, so undoing means deleting. Anything else means the file existed
|
|
618
|
+
// and was modified, so undoing means restoring it. Losing that distinction is
|
|
619
|
+
// how a revert deletes a file the project already had.
|
|
620
|
+
const entries = String(touched.stdout)
|
|
621
|
+
.split("\n")
|
|
622
|
+
.filter(Boolean)
|
|
623
|
+
.map((line) => ({ code: line.slice(0, 2), file: line.slice(3).trim() }))
|
|
624
|
+
.filter((e) => e.file);
|
|
625
|
+
const { allowed, strayed } = (0, AuthoringScope_1.judgeScope)(entries.map((e) => e.file), changeId);
|
|
626
|
+
const untracked = new Set(entries.filter((e) => e.code === "??").map((e) => e.file));
|
|
627
|
+
// The boundary is enforced, not requested. An agent asked to *describe* a
|
|
628
|
+
// change, and able to edit the capability spec it describes, can make the
|
|
629
|
+
// change unnecessary instead of proposing it — quietly, and in a diff nobody
|
|
630
|
+
// reads because it looks like the work.
|
|
631
|
+
for (const stray of strayed) {
|
|
632
|
+
if (untracked.has(stray)) {
|
|
633
|
+
const full = path.join(projectDir, stray);
|
|
634
|
+
if (fs.existsSync(full))
|
|
635
|
+
fs.rmSync(full, { recursive: true, force: true });
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
(0, node_child_process_1.spawnSync)("git", ["-C", projectDir, "checkout", "--", stray], { encoding: "utf8" });
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
const validation = new ValidateChangeUseCase_1.ValidateChangeUseCase(new DiskProjectRepository_1.DiskProjectRepository(projectDir)).execute(changeId, { strict: opts.strict });
|
|
642
|
+
const diagnostics = [...validation.diagnostics];
|
|
643
|
+
if (strayed.length > 0) {
|
|
644
|
+
diagnostics.push((0, diagnostics_1.warning)("author_out_of_scope", `The agent wrote outside ${(0, AuthoringScope_1.changeScope)(changeId)}.`, {
|
|
645
|
+
target: strayed[0],
|
|
646
|
+
fix: `Reverted: ${strayed.join(", ")}. Only the change's own directory is the author's to write; \`change archive\` is what moves a delta into a capability spec.`,
|
|
647
|
+
}));
|
|
648
|
+
}
|
|
649
|
+
if ((0, diagnostics_1.hasErrors)(diagnostics)) {
|
|
650
|
+
return fail(opts, { change: changeId, artifact, wrote: allowed, reverted: strayed }, diagnostics);
|
|
651
|
+
}
|
|
652
|
+
return emit(opts, { change: changeId, artifact, wrote: allowed, reverted: strayed, status: diagnostics }, () => {
|
|
653
|
+
process.stdout.write(`\n ${c.bold}${changeId}${c.reset} — ${artifact} written by the agent\n\n`);
|
|
654
|
+
for (const f of allowed)
|
|
655
|
+
process.stdout.write(` ${c.green}+${c.reset} ${f}\n`);
|
|
656
|
+
for (const f of strayed)
|
|
657
|
+
process.stdout.write(` ${c.yellow}~${c.reset} ${f} ${c.dim}(out of scope, reverted)${c.reset}\n`);
|
|
658
|
+
if (diagnostics.length > 0)
|
|
659
|
+
(0, diagnostics_1.printDiagnostics)(diagnostics);
|
|
660
|
+
process.stdout.write(`\n Next: read it, then ${c.cyan}specgate change validate ${changeId}${c.reset}\n\n`);
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
/** The agent command for authoring: explicit flag, then profile, then harness config. */
|
|
664
|
+
function resolveAuthorAgent(projectDir, opts) {
|
|
665
|
+
if (opts.agent)
|
|
666
|
+
return opts.agent;
|
|
667
|
+
const config = (0, HarnessConfigFile_1.readHarnessConfig)(projectDir) || {};
|
|
668
|
+
if (opts.agentProfile) {
|
|
669
|
+
return config.profileAgents?.[opts.agentProfile] || null;
|
|
670
|
+
}
|
|
671
|
+
return config.agent || null;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* The instructions `change instructions` already produces, rendered for an
|
|
675
|
+
* agent rather than for a person.
|
|
676
|
+
*/
|
|
677
|
+
function authorPrompt(changeId, artifact, instructions) {
|
|
678
|
+
const parts = [
|
|
679
|
+
`# Write the ${artifact} for change \`${changeId}\``,
|
|
680
|
+
"",
|
|
681
|
+
`You are a specification author. Write **only** inside \`${(0, AuthoringScope_1.changeScope)(changeId)}\`.`,
|
|
682
|
+
"Anything you write elsewhere will be reverted before it is read — the capability",
|
|
683
|
+
"specs and the traceability matrix are moved by `specgate change archive`, after a",
|
|
684
|
+
"human has reviewed this proposal. Describe the change; do not make it.",
|
|
685
|
+
"",
|
|
686
|
+
];
|
|
687
|
+
if (instructions.outputPath)
|
|
688
|
+
parts.push(`## Write to\n\n\`${instructions.outputPath}\`\n`);
|
|
689
|
+
if (instructions.rules?.length) {
|
|
690
|
+
parts.push("## Rules\n", ...instructions.rules.map((r) => `- ${r}`), "");
|
|
691
|
+
}
|
|
692
|
+
const ctx = instructions.context || {};
|
|
693
|
+
if (ctx.reservedReqRange) {
|
|
694
|
+
parts.push(`## Reserved requirement ids\n\n\`${ctx.reservedReqRange[0]}\`…\`${ctx.reservedReqRange[1]}\` — use these and no others.\n`);
|
|
695
|
+
}
|
|
696
|
+
if (ctx.proposal) {
|
|
697
|
+
parts.push("## The proposal this must stay faithful to\n", "```markdown", ctx.proposal.trim(), "```", "");
|
|
698
|
+
}
|
|
699
|
+
if (instructions.template) {
|
|
700
|
+
parts.push("## Template\n", "```markdown", String(instructions.template).trim(), "```", "");
|
|
701
|
+
}
|
|
702
|
+
parts.push(`When finished, the change must pass \`specgate change validate ${changeId}\`.`);
|
|
703
|
+
return parts.join("\n");
|
|
704
|
+
}
|
|
705
|
+
function cmdValidate(opts) {
|
|
706
|
+
const projectDir = (0, project_root_1.resolveProjectDir)(opts.projectDir);
|
|
707
|
+
const requested = opts.positional[0];
|
|
708
|
+
const ids = requested ? [requested] : (0, ChangeWorkspace_1.listChangeIds)(projectDir);
|
|
709
|
+
const p = (0, ChangeWorkspace_1.paths)(projectDir);
|
|
710
|
+
if (requested && !fs.existsSync(p.change(requested))) {
|
|
711
|
+
return fail(opts, { items: [] }, [
|
|
712
|
+
(0, diagnostics_1.error)("change_not_found", `Change "${requested}" does not exist.`, {
|
|
713
|
+
target: requested,
|
|
714
|
+
fix: "Run `specgate change list`.",
|
|
715
|
+
}),
|
|
716
|
+
]);
|
|
717
|
+
}
|
|
718
|
+
const items = ids.map((id) => {
|
|
719
|
+
const result = new ValidateChangeUseCase_1.ValidateChangeUseCase(new DiskProjectRepository_1.DiskProjectRepository(projectDir)).execute(id, opts);
|
|
720
|
+
const failed = opts.strict ? result.diagnostics.length > 0 : (0, diagnostics_1.hasErrors)(result.diagnostics);
|
|
721
|
+
return {
|
|
722
|
+
id,
|
|
723
|
+
type: "change",
|
|
724
|
+
valid: !failed,
|
|
725
|
+
deltaCount: result.deltaCount,
|
|
726
|
+
issues: result.diagnostics,
|
|
727
|
+
};
|
|
728
|
+
});
|
|
729
|
+
const failedCount = items.filter((i) => !i.valid).length;
|
|
730
|
+
const payload = {
|
|
731
|
+
items,
|
|
732
|
+
summary: { total: items.length, passed: items.length - failedCount, failed: failedCount },
|
|
733
|
+
version: "1.0",
|
|
734
|
+
};
|
|
735
|
+
if (opts.json) {
|
|
736
|
+
process.stdout.write(`${JSON.stringify({ ...payload, status: [] }, null, 2)}\n`);
|
|
737
|
+
}
|
|
738
|
+
else if (items.length === 0) {
|
|
739
|
+
process.stdout.write(`\n ${c.dim}No active changes to validate.${c.reset}\n\n`);
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
process.stdout.write("\n");
|
|
743
|
+
for (const item of items) {
|
|
744
|
+
const mark = item.valid ? `${c.green}✔${c.reset}` : `${c.red}✖${c.reset}`;
|
|
745
|
+
process.stdout.write(` ${mark} ${c.bold}${item.id}${c.reset} ${c.dim}(${item.deltaCount} delta(s))${c.reset}\n`);
|
|
746
|
+
if (item.issues.length > 0) {
|
|
747
|
+
(0, diagnostics_1.printDiagnostics)(item.issues, process.stdout);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
process.stdout.write("\n");
|
|
751
|
+
}
|
|
752
|
+
process.exit(failedCount > 0 ? 1 : 0);
|
|
753
|
+
}
|
|
754
|
+
// ── archive ───────────────────────────────────────────────────────────────────
|
|
755
|
+
function cmdArchive(opts) {
|
|
756
|
+
const projectDir = (0, project_root_1.resolveProjectDir)(opts.projectDir);
|
|
757
|
+
const changeId = opts.positional[0];
|
|
758
|
+
const nullShape = { archive: null };
|
|
759
|
+
if (!changeId) {
|
|
760
|
+
return fail(opts, nullShape, [
|
|
761
|
+
(0, diagnostics_1.error)("archive_change_name_required", "`change archive` expects a change id.", {
|
|
762
|
+
fix: "e.g. `specgate change archive add-dynamic-pricing`",
|
|
763
|
+
}),
|
|
764
|
+
]);
|
|
765
|
+
}
|
|
766
|
+
const repo = new DiskProjectRepository_1.DiskProjectRepository(projectDir);
|
|
767
|
+
const useCase = new ArchiveChangeUseCase_1.ArchiveChangeUseCase(repo);
|
|
768
|
+
const plan = useCase.execute(changeId, { force: opts.force });
|
|
769
|
+
if (!plan.ok) {
|
|
770
|
+
return fail(opts, nullShape, plan.diagnostics);
|
|
771
|
+
}
|
|
772
|
+
const archivedAs = plan.move ? path.basename(plan.move.to) : `${changeId}-archived`;
|
|
773
|
+
const rel = (f) => path.relative(projectDir, f).split(path.sep).join("/");
|
|
774
|
+
const summary = {
|
|
775
|
+
change: changeId,
|
|
776
|
+
archivedAs: archivedAs,
|
|
777
|
+
specsUpdated: plan.totals.specsWritten > 0 || plan.totals.specsRetired > 0,
|
|
778
|
+
totals: plan.totals,
|
|
779
|
+
writes: plan.writes.map((w) => ({ file: rel(w.file), kind: w.kind })),
|
|
780
|
+
deletes: plan.deletes.map((d) => ({ file: rel(d.file), reason: d.reason })),
|
|
781
|
+
warnings: plan.warnings,
|
|
782
|
+
};
|
|
783
|
+
if (opts.dryRun) {
|
|
784
|
+
return emit(opts, { archive: { ...summary, dryRun: true } }, () => {
|
|
785
|
+
process.stdout.write(`\n ${c.bold}Archive plan${c.reset} ${c.dim}(dry run — nothing written)${c.reset}\n\n`);
|
|
786
|
+
for (const w of summary.writes) {
|
|
787
|
+
process.stdout.write(` ${c.green}~${c.reset} ${w.file} ${c.dim}(${w.kind})${c.reset}\n`);
|
|
788
|
+
}
|
|
789
|
+
for (const d of summary.deletes) {
|
|
790
|
+
process.stdout.write(` ${c.red}-${c.reset} ${d.file} ${c.dim}(${d.reason})${c.reset}\n`);
|
|
791
|
+
}
|
|
792
|
+
if (plan.move) {
|
|
793
|
+
process.stdout.write(` ${c.cyan}→${c.reset} ${rel(plan.move.from)} ${c.dim}moves to${c.reset} ${rel(plan.move.to)}\n`);
|
|
794
|
+
}
|
|
795
|
+
if (plan.warnings.length > 0) {
|
|
796
|
+
process.stdout.write("\n");
|
|
797
|
+
(0, diagnostics_1.printDiagnostics)(plan.warnings, process.stdout);
|
|
798
|
+
}
|
|
799
|
+
process.stdout.write("\n");
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
repo.executePlan(plan);
|
|
804
|
+
}
|
|
805
|
+
catch (err) {
|
|
806
|
+
return fail(opts, nullShape, [
|
|
807
|
+
(0, diagnostics_1.error)("archive_spec_update_failed", `Archive failed and was rolled back: ${err.message}`, {
|
|
808
|
+
target: changeId,
|
|
809
|
+
fix: "Fix the underlying filesystem error and re-run; the project is unchanged.",
|
|
810
|
+
}),
|
|
811
|
+
]);
|
|
812
|
+
}
|
|
813
|
+
emit(opts, { archive: summary }, () => {
|
|
814
|
+
const t = plan.totals;
|
|
815
|
+
process.stdout.write(`\n ${c.green}✔${c.reset} Archived as ${c.bold}${archivedAs}${c.reset}\n\n` +
|
|
816
|
+
` ${c.dim}specs:${c.reset} ${t.added} added · ${t.modified} modified · ${t.removed} removed\n` +
|
|
817
|
+
` ${c.dim}traceability:${c.reset} ${t.traceability.added} row(s) added · ${t.traceability.updated} updated · ${t.traceability.removed} removed\n` +
|
|
818
|
+
` ${c.dim}features:${c.reset} ${plan.writes.filter((w) => w.kind === "feature").length} materialised\n\n` +
|
|
819
|
+
` ${c.dim}Now run${c.reset} ${c.cyan}specgate validate . --strict-tdd${c.reset} ${c.dim}— it will fail until the tests exist.${c.reset}\n\n`);
|
|
820
|
+
if (plan.warnings.length > 0)
|
|
821
|
+
(0, diagnostics_1.printDiagnostics)(plan.warnings, process.stdout);
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
// ── entry ─────────────────────────────────────────────────────────────────────
|
|
825
|
+
class CliCommand extends command_1.BaseCommand {
|
|
826
|
+
execute() {
|
|
827
|
+
const argv = this.args;
|
|
828
|
+
const sub = argv[0];
|
|
829
|
+
if (!sub || sub === "--help" || sub === "-h") {
|
|
830
|
+
usage();
|
|
831
|
+
process.exit(sub ? 0 : 2);
|
|
832
|
+
}
|
|
833
|
+
// `instructions` parses its own arguments: its first positional is an
|
|
834
|
+
// artefact name, not a change id, so the shared parser does not fit.
|
|
835
|
+
if (sub === "instructions") {
|
|
836
|
+
(0, instructions_2.main)(argv.slice(1), exports.TEMPLATES);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
const opts = parseArgs(argv.slice(1));
|
|
840
|
+
const table = {
|
|
841
|
+
new: cmdNew,
|
|
842
|
+
list: cmdList,
|
|
843
|
+
show: cmdShow,
|
|
844
|
+
status: cmdStatus,
|
|
845
|
+
validate: cmdValidate,
|
|
846
|
+
archive: cmdArchive,
|
|
847
|
+
author: cmdAuthor,
|
|
848
|
+
};
|
|
849
|
+
const handler = table[sub];
|
|
850
|
+
if (!handler) {
|
|
851
|
+
process.stderr.write(`Unknown change sub-command: ${sub}. Expected: ${Object.keys(table).join(", ")}\n`);
|
|
852
|
+
process.exit(2);
|
|
853
|
+
}
|
|
854
|
+
try {
|
|
855
|
+
handler(opts);
|
|
856
|
+
}
|
|
857
|
+
catch (err) {
|
|
858
|
+
fail(opts, {}, [
|
|
859
|
+
(0, diagnostics_1.error)("change_error", err.message, {
|
|
860
|
+
fix: "Re-run with --json for the machine-readable form of this failure.",
|
|
861
|
+
}),
|
|
862
|
+
]);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
exports.CliCommand = CliCommand;
|
|
867
|
+
//# sourceMappingURL=ChangeCommand.js.map
|