agent-sdd 1.0.3
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/LICENSE +21 -0
- package/README.md +1028 -0
- package/README.ru.md +1046 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +867 -0
- package/dist/features/approve/adapters/inbound/CliApproveHandler.d.ts +17 -0
- package/dist/features/approve/adapters/inbound/CliApproveHandler.js +108 -0
- package/dist/features/approve/adapters/outbound/NodeApproveFileSystem.d.ts +8 -0
- package/dist/features/approve/adapters/outbound/NodeApproveFileSystem.js +147 -0
- package/dist/features/approve/adapters/outbound/NodePlanFileWriter.d.ts +6 -0
- package/dist/features/approve/adapters/outbound/NodePlanFileWriter.js +92 -0
- package/dist/features/approve/adapters/outbound/SystemApproveClock.d.ts +4 -0
- package/dist/features/approve/adapters/outbound/SystemApproveClock.js +5 -0
- package/dist/features/approve/application/ApplyApproval.d.ts +19 -0
- package/dist/features/approve/application/ApplyApproval.js +30 -0
- package/dist/features/approve/application/WriteAttestation.d.ts +19 -0
- package/dist/features/approve/application/WriteAttestation.js +23 -0
- package/dist/features/approve/domain/ApproveRequest.d.ts +24 -0
- package/dist/features/approve/domain/ApproveRequest.js +24 -0
- package/dist/features/approve/domain/Rewrite.d.ts +1 -0
- package/dist/features/approve/domain/Rewrite.js +6 -0
- package/dist/features/approve/ports/inbound/ApproveCommand.d.ts +10 -0
- package/dist/features/approve/ports/inbound/ApproveCommand.js +1 -0
- package/dist/features/approve/ports/outbound/ApproveClock.d.ts +3 -0
- package/dist/features/approve/ports/outbound/ApproveClock.js +1 -0
- package/dist/features/approve/ports/outbound/ApproveConfigPort.d.ts +4 -0
- package/dist/features/approve/ports/outbound/ApproveConfigPort.js +1 -0
- package/dist/features/approve/ports/outbound/ApproveFileSystem.d.ts +8 -0
- package/dist/features/approve/ports/outbound/ApproveFileSystem.js +1 -0
- package/dist/features/approve/ports/outbound/PlanFileWriter.d.ts +13 -0
- package/dist/features/approve/ports/outbound/PlanFileWriter.js +1 -0
- package/dist/features/check/adapters/inbound/CliCheckHandler.d.ts +8 -0
- package/dist/features/check/adapters/inbound/CliCheckHandler.js +62 -0
- package/dist/features/check/adapters/outbound/ChildProcessCheckGit.d.ts +8 -0
- package/dist/features/check/adapters/outbound/ChildProcessCheckGit.js +112 -0
- package/dist/features/check/adapters/outbound/NodeCheckFileReader.d.ts +7 -0
- package/dist/features/check/adapters/outbound/NodeCheckFileReader.js +44 -0
- package/dist/features/check/application/CheckBaseline.d.ts +28 -0
- package/dist/features/check/application/CheckBaseline.js +54 -0
- package/dist/features/check/domain/BaselineComparison.d.ts +1 -0
- package/dist/features/check/domain/BaselineComparison.js +2 -0
- package/dist/features/check/ports/inbound/CheckCommand.d.ts +4 -0
- package/dist/features/check/ports/inbound/CheckCommand.js +1 -0
- package/dist/features/check/ports/outbound/CheckConfigPort.d.ts +4 -0
- package/dist/features/check/ports/outbound/CheckConfigPort.js +1 -0
- package/dist/features/check/ports/outbound/CheckGitPort.d.ts +7 -0
- package/dist/features/check/ports/outbound/CheckGitPort.js +1 -0
- package/dist/features/check/ports/outbound/CheckSpecPort.d.ts +9 -0
- package/dist/features/check/ports/outbound/CheckSpecPort.js +1 -0
- package/dist/features/doctor/adapters/inbound/CliDoctorHandler.d.ts +8 -0
- package/dist/features/doctor/adapters/inbound/CliDoctorHandler.js +77 -0
- package/dist/features/doctor/adapters/outbound/NodeRegistryReader.d.ts +5 -0
- package/dist/features/doctor/adapters/outbound/NodeRegistryReader.js +40 -0
- package/dist/features/doctor/application/RunDoctor.d.ts +30 -0
- package/dist/features/doctor/application/RunDoctor.js +78 -0
- package/dist/features/doctor/domain/RegistryRow.d.ts +23 -0
- package/dist/features/doctor/domain/RegistryRow.js +114 -0
- package/dist/features/doctor/domain/SemverRange.d.ts +7 -0
- package/dist/features/doctor/domain/SemverRange.js +82 -0
- package/dist/features/doctor/ports/inbound/DoctorCommand.d.ts +8 -0
- package/dist/features/doctor/ports/inbound/DoctorCommand.js +1 -0
- package/dist/features/doctor/ports/outbound/RegistryReader.d.ts +12 -0
- package/dist/features/doctor/ports/outbound/RegistryReader.js +1 -0
- package/dist/features/finalize/adapters/inbound/CliFinalizeHandler.d.ts +8 -0
- package/dist/features/finalize/adapters/inbound/CliFinalizeHandler.js +80 -0
- package/dist/features/finalize/adapters/outbound/NodeFinalizeFileSystem.d.ts +11 -0
- package/dist/features/finalize/adapters/outbound/NodeFinalizeFileSystem.js +167 -0
- package/dist/features/finalize/adapters/outbound/NodePlanRepo.d.ts +7 -0
- package/dist/features/finalize/adapters/outbound/NodePlanRepo.js +82 -0
- package/dist/features/finalize/adapters/outbound/SystemFinalizeClock.d.ts +4 -0
- package/dist/features/finalize/adapters/outbound/SystemFinalizeClock.js +5 -0
- package/dist/features/finalize/application/RunFinalize.d.ts +34 -0
- package/dist/features/finalize/application/RunFinalize.js +98 -0
- package/dist/features/finalize/domain/ValidateFinalizeGraph.d.ts +9 -0
- package/dist/features/finalize/domain/ValidateFinalizeGraph.js +86 -0
- package/dist/features/finalize/ports/inbound/FinalizeCommand.d.ts +7 -0
- package/dist/features/finalize/ports/inbound/FinalizeCommand.js +1 -0
- package/dist/features/finalize/ports/outbound/FinalizeClock.d.ts +3 -0
- package/dist/features/finalize/ports/outbound/FinalizeClock.js +1 -0
- package/dist/features/finalize/ports/outbound/FinalizeConfigPort.d.ts +4 -0
- package/dist/features/finalize/ports/outbound/FinalizeConfigPort.js +1 -0
- package/dist/features/finalize/ports/outbound/FinalizeFileSystem.d.ts +14 -0
- package/dist/features/finalize/ports/outbound/FinalizeFileSystem.js +1 -0
- package/dist/features/finalize/ports/outbound/PlanRepo.d.ts +21 -0
- package/dist/features/finalize/ports/outbound/PlanRepo.js +1 -0
- package/dist/features/install/adapters/inbound/CliInstallHandler.d.ts +8 -0
- package/dist/features/install/adapters/inbound/CliInstallHandler.js +54 -0
- package/dist/features/install/adapters/outbound/NodeInstallSource.d.ts +7 -0
- package/dist/features/install/adapters/outbound/NodeInstallSource.js +24 -0
- package/dist/features/install/adapters/outbound/NodeInstallTargetFs.d.ts +7 -0
- package/dist/features/install/adapters/outbound/NodeInstallTargetFs.js +30 -0
- package/dist/features/install/application/InstallRules.d.ts +10 -0
- package/dist/features/install/application/InstallRules.js +73 -0
- package/dist/features/install/domain/InstallPlan.d.ts +27 -0
- package/dist/features/install/domain/InstallPlan.js +168 -0
- package/dist/features/install/domain/InstallResult.d.ts +23 -0
- package/dist/features/install/domain/InstallResult.js +1 -0
- package/dist/features/install/domain/InstallTarget.d.ts +6 -0
- package/dist/features/install/domain/InstallTarget.js +7 -0
- package/dist/features/install/domain/ManagedBlock.d.ts +3 -0
- package/dist/features/install/domain/ManagedBlock.js +20 -0
- package/dist/features/install/domain/RuleManifest.d.ts +17 -0
- package/dist/features/install/domain/RuleManifest.js +69 -0
- package/dist/features/install/domain/SettingsMerge.d.ts +5 -0
- package/dist/features/install/domain/SettingsMerge.js +43 -0
- package/dist/features/install/ports/inbound/InstallCommand.d.ts +10 -0
- package/dist/features/install/ports/inbound/InstallCommand.js +1 -0
- package/dist/features/install/ports/outbound/InstallSource.d.ts +4 -0
- package/dist/features/install/ports/outbound/InstallSource.js +1 -0
- package/dist/features/install/ports/outbound/InstallTargetFs.d.ts +6 -0
- package/dist/features/install/ports/outbound/InstallTargetFs.js +1 -0
- package/dist/features/lint/adapters/inbound/CliLintHandler.d.ts +8 -0
- package/dist/features/lint/adapters/inbound/CliLintHandler.js +61 -0
- package/dist/features/lint/adapters/outbound/NodeLintFileReader.d.ts +7 -0
- package/dist/features/lint/adapters/outbound/NodeLintFileReader.js +165 -0
- package/dist/features/lint/application/RunLint.d.ts +10 -0
- package/dist/features/lint/application/RunLint.js +100 -0
- package/dist/features/lint/domain/Diagnostic.d.ts +1 -0
- package/dist/features/lint/domain/Diagnostic.js +2 -0
- package/dist/features/lint/domain/Record.d.ts +1 -0
- package/dist/features/lint/domain/Record.js +5 -0
- package/dist/features/lint/domain/Rules.d.ts +1 -0
- package/dist/features/lint/domain/Rules.js +2 -0
- package/dist/features/lint/domain/SpecParser.d.ts +1 -0
- package/dist/features/lint/domain/SpecParser.js +2 -0
- package/dist/features/lint/ports/inbound/LintCommand.d.ts +4 -0
- package/dist/features/lint/ports/inbound/LintCommand.js +1 -0
- package/dist/features/lint/ports/outbound/LintConfigPort.d.ts +4 -0
- package/dist/features/lint/ports/outbound/LintConfigPort.js +1 -0
- package/dist/features/lint/ports/outbound/LintFileReader.d.ts +10 -0
- package/dist/features/lint/ports/outbound/LintFileReader.js +1 -0
- package/dist/features/plan/adapters/inbound/CliPlanShowHandler.d.ts +8 -0
- package/dist/features/plan/adapters/inbound/CliPlanShowHandler.js +73 -0
- package/dist/features/plan/adapters/outbound/NodePlanReader.d.ts +7 -0
- package/dist/features/plan/adapters/outbound/NodePlanReader.js +68 -0
- package/dist/features/plan/application/ShowPlan.d.ts +7 -0
- package/dist/features/plan/application/ShowPlan.js +4 -0
- package/dist/features/plan/ports/inbound/PlanShowCommand.d.ts +7 -0
- package/dist/features/plan/ports/inbound/PlanShowCommand.js +1 -0
- package/dist/features/plan/ports/outbound/PlanConfigPort.d.ts +4 -0
- package/dist/features/plan/ports/outbound/PlanConfigPort.js +1 -0
- package/dist/features/plan/ports/outbound/PlanReader.d.ts +19 -0
- package/dist/features/plan/ports/outbound/PlanReader.js +1 -0
- package/dist/features/ready/adapters/inbound/CliReadyHandler.d.ts +8 -0
- package/dist/features/ready/adapters/inbound/CliReadyHandler.js +79 -0
- package/dist/features/ready/adapters/outbound/ChildProcessReadyGit.d.ts +9 -0
- package/dist/features/ready/adapters/outbound/ChildProcessReadyGit.js +113 -0
- package/dist/features/ready/adapters/outbound/NodeReadyFileSystem.d.ts +8 -0
- package/dist/features/ready/adapters/outbound/NodeReadyFileSystem.js +159 -0
- package/dist/features/ready/application/RunReady.d.ts +16 -0
- package/dist/features/ready/application/RunReady.js +572 -0
- package/dist/features/ready/domain/AggregatedRules.d.ts +16 -0
- package/dist/features/ready/domain/AggregatedRules.js +42 -0
- package/dist/features/ready/domain/MarkerParser.d.ts +17 -0
- package/dist/features/ready/domain/MarkerParser.js +108 -0
- package/dist/features/ready/domain/PartitionResolver.d.ts +1 -0
- package/dist/features/ready/domain/PartitionResolver.js +5 -0
- package/dist/features/ready/domain/ReadyInput.d.ts +6 -0
- package/dist/features/ready/domain/ReadyInput.js +1 -0
- package/dist/features/ready/domain/ReadyViolation.d.ts +38 -0
- package/dist/features/ready/domain/ReadyViolation.js +19 -0
- package/dist/features/ready/domain/Rules.d.ts +22 -0
- package/dist/features/ready/domain/Rules.js +243 -0
- package/dist/features/ready/domain/SpecDiff.d.ts +33 -0
- package/dist/features/ready/domain/SpecDiff.js +321 -0
- package/dist/features/ready/ports/inbound/ReadyCommand.d.ts +4 -0
- package/dist/features/ready/ports/inbound/ReadyCommand.js +1 -0
- package/dist/features/ready/ports/outbound/ReadyConfigPort.d.ts +4 -0
- package/dist/features/ready/ports/outbound/ReadyConfigPort.js +1 -0
- package/dist/features/ready/ports/outbound/ReadyFileReader.d.ts +12 -0
- package/dist/features/ready/ports/outbound/ReadyFileReader.js +1 -0
- package/dist/features/ready/ports/outbound/ReadyGitPort.d.ts +10 -0
- package/dist/features/ready/ports/outbound/ReadyGitPort.js +5 -0
- package/dist/features/record/adapters/inbound/CliRecordHandler.d.ts +10 -0
- package/dist/features/record/adapters/inbound/CliRecordHandler.js +111 -0
- package/dist/features/record/adapters/outbound/NodeRecordFileSystem.d.ts +9 -0
- package/dist/features/record/adapters/outbound/NodeRecordFileSystem.js +152 -0
- package/dist/features/record/application/AddRecord.d.ts +11 -0
- package/dist/features/record/application/AddRecord.js +84 -0
- package/dist/features/record/application/GetRecord.d.ts +8 -0
- package/dist/features/record/application/GetRecord.js +22 -0
- package/dist/features/record/application/ListRecords.d.ts +9 -0
- package/dist/features/record/application/ListRecords.js +24 -0
- package/dist/features/record/application/SetRecord.d.ts +11 -0
- package/dist/features/record/application/SetRecord.js +68 -0
- package/dist/features/record/domain/RecordBody.d.ts +12 -0
- package/dist/features/record/domain/RecordBody.js +66 -0
- package/dist/features/record/domain/RecordPartition.d.ts +1 -0
- package/dist/features/record/domain/RecordPartition.js +7 -0
- package/dist/features/record/domain/RecordSlice.d.ts +7 -0
- package/dist/features/record/domain/RecordSlice.js +1 -0
- package/dist/features/record/domain/RecordSummary.d.ts +11 -0
- package/dist/features/record/domain/RecordSummary.js +13 -0
- package/dist/features/record/domain/RecordWrite.d.ts +14 -0
- package/dist/features/record/domain/RecordWrite.js +8 -0
- package/dist/features/record/ports/inbound/RecordCommand.d.ts +19 -0
- package/dist/features/record/ports/inbound/RecordCommand.js +1 -0
- package/dist/features/record/ports/outbound/RecordConfigPort.d.ts +4 -0
- package/dist/features/record/ports/outbound/RecordConfigPort.js +1 -0
- package/dist/features/record/ports/outbound/RecordFileReader.d.ts +10 -0
- package/dist/features/record/ports/outbound/RecordFileReader.js +1 -0
- package/dist/features/record/ports/outbound/RecordFileWriter.d.ts +6 -0
- package/dist/features/record/ports/outbound/RecordFileWriter.js +1 -0
- package/dist/features/refresh/adapters/inbound/CliRefreshHandler.d.ts +8 -0
- package/dist/features/refresh/adapters/inbound/CliRefreshHandler.js +24 -0
- package/dist/features/refresh/adapters/outbound/ChildProcessRefreshGit.d.ts +8 -0
- package/dist/features/refresh/adapters/outbound/ChildProcessRefreshGit.js +118 -0
- package/dist/features/refresh/adapters/outbound/NodeRefreshFileReader.d.ts +7 -0
- package/dist/features/refresh/adapters/outbound/NodeRefreshFileReader.js +44 -0
- package/dist/features/refresh/adapters/outbound/SystemRefreshClock.d.ts +4 -0
- package/dist/features/refresh/adapters/outbound/SystemRefreshClock.js +5 -0
- package/dist/features/refresh/application/BuildRefreshStubs.d.ts +25 -0
- package/dist/features/refresh/application/BuildRefreshStubs.js +43 -0
- package/dist/features/refresh/domain/DiffStubs.d.ts +24 -0
- package/dist/features/refresh/domain/DiffStubs.js +81 -0
- package/dist/features/refresh/domain/Footprint.d.ts +14 -0
- package/dist/features/refresh/domain/Footprint.js +45 -0
- package/dist/features/refresh/ports/inbound/RefreshCommand.d.ts +4 -0
- package/dist/features/refresh/ports/inbound/RefreshCommand.js +1 -0
- package/dist/features/refresh/ports/outbound/RefreshClockPort.d.ts +3 -0
- package/dist/features/refresh/ports/outbound/RefreshClockPort.js +1 -0
- package/dist/features/refresh/ports/outbound/RefreshConfigPort.d.ts +4 -0
- package/dist/features/refresh/ports/outbound/RefreshConfigPort.js +1 -0
- package/dist/features/refresh/ports/outbound/RefreshGitPort.d.ts +7 -0
- package/dist/features/refresh/ports/outbound/RefreshGitPort.js +1 -0
- package/dist/features/refresh/ports/outbound/RefreshSpecPort.d.ts +9 -0
- package/dist/features/refresh/ports/outbound/RefreshSpecPort.js +1 -0
- package/dist/features/report/adapters/inbound/CliReportHandler.d.ts +8 -0
- package/dist/features/report/adapters/inbound/CliReportHandler.js +35 -0
- package/dist/features/report/adapters/outbound/NodeReportFileSystem.d.ts +7 -0
- package/dist/features/report/adapters/outbound/NodeReportFileSystem.js +128 -0
- package/dist/features/report/application/RunReport.d.ts +19 -0
- package/dist/features/report/application/RunReport.js +161 -0
- package/dist/features/report/ports/inbound/ReportCommand.d.ts +8 -0
- package/dist/features/report/ports/inbound/ReportCommand.js +1 -0
- package/dist/features/report/ports/outbound/ReportConfigPort.d.ts +4 -0
- package/dist/features/report/ports/outbound/ReportConfigPort.js +1 -0
- package/dist/features/report/ports/outbound/ReportFileReader.d.ts +7 -0
- package/dist/features/report/ports/outbound/ReportFileReader.js +1 -0
- package/dist/features/token/adapters/inbound/CliTokenHandler.d.ts +8 -0
- package/dist/features/token/adapters/inbound/CliTokenHandler.js +53 -0
- package/dist/features/token/adapters/outbound/ChildProcessTokenGit.d.ts +8 -0
- package/dist/features/token/adapters/outbound/ChildProcessTokenGit.js +112 -0
- package/dist/features/token/adapters/outbound/NodeTokenConfigReader.d.ts +5 -0
- package/dist/features/token/adapters/outbound/NodeTokenConfigReader.js +41 -0
- package/dist/features/token/application/ComputeToken.d.ts +18 -0
- package/dist/features/token/application/ComputeToken.js +27 -0
- package/dist/features/token/ports/inbound/TokenCommand.d.ts +4 -0
- package/dist/features/token/ports/inbound/TokenCommand.js +1 -0
- package/dist/features/token/ports/outbound/TokenConfigPort.d.ts +4 -0
- package/dist/features/token/ports/outbound/TokenConfigPort.js +1 -0
- package/dist/features/token/ports/outbound/TokenGitPort.d.ts +7 -0
- package/dist/features/token/ports/outbound/TokenGitPort.js +1 -0
- package/dist/shared/domain/AgentBlocklist.d.ts +5 -0
- package/dist/shared/domain/AgentBlocklist.js +28 -0
- package/dist/shared/domain/BoundaryReachability.d.ts +5 -0
- package/dist/shared/domain/BoundaryReachability.js +71 -0
- package/dist/shared/domain/CheckOutcome.d.ts +10 -0
- package/dist/shared/domain/CheckOutcome.js +7 -0
- package/dist/shared/domain/CliOutput.d.ts +10 -0
- package/dist/shared/domain/CliOutput.js +29 -0
- package/dist/shared/domain/Config.d.ts +29 -0
- package/dist/shared/domain/Config.js +201 -0
- package/dist/shared/domain/DiagnosticRegistry.d.ts +8 -0
- package/dist/shared/domain/DiagnosticRegistry.js +71 -0
- package/dist/shared/domain/Errors.d.ts +12 -0
- package/dist/shared/domain/Errors.js +23 -0
- package/dist/shared/domain/GlobMatch.d.ts +2 -0
- package/dist/shared/domain/GlobMatch.js +58 -0
- package/dist/shared/domain/LintReport.d.ts +16 -0
- package/dist/shared/domain/LintReport.js +11 -0
- package/dist/shared/domain/LintRules.d.ts +67 -0
- package/dist/shared/domain/LintRules.js +956 -0
- package/dist/shared/domain/PartitionGrammar.d.ts +4 -0
- package/dist/shared/domain/PartitionGrammar.js +16 -0
- package/dist/shared/domain/PlanFile.d.ts +28 -0
- package/dist/shared/domain/PlanFile.js +112 -0
- package/dist/shared/domain/Scope.d.ts +1 -0
- package/dist/shared/domain/Scope.js +3 -0
- package/dist/shared/domain/SpecApprovalRewrite.d.ts +23 -0
- package/dist/shared/domain/SpecApprovalRewrite.js +254 -0
- package/dist/shared/domain/SpecBlocks.d.ts +12 -0
- package/dist/shared/domain/SpecBlocks.js +96 -0
- package/dist/shared/domain/SpecRecord.d.ts +17 -0
- package/dist/shared/domain/SpecRecord.js +208 -0
- package/dist/shared/domain/TemplateFieldMetadata.d.ts +2 -0
- package/dist/shared/domain/TemplateFieldMetadata.js +177 -0
- package/dist/shared/domain/Token.d.ts +2 -0
- package/dist/shared/domain/Token.js +5 -0
- package/dist/shared/domain/WeaselWords.d.ts +3 -0
- package/dist/shared/domain/WeaselWords.js +32 -0
- package/package.json +71 -0
- package/rules/enforcement_registry.md +126 -0
- package/rules/hooks/sdd-lint-reminder.sh +33 -0
- package/rules/hooks/sdd-spec-read-guard.sh +73 -0
- package/rules/manifest.json +15 -0
- package/rules/review-sdd.md +9 -0
- package/rules/sdd-cli-usage.md +91 -0
- package/rules/skills/spec-driven-development/SKILL.md +554 -0
- package/rules/skills/spec-driven-development/data/weasel-words.json +22 -0
- package/rules/spec-driven-development.md +69 -0
- package/rules/tdd-sdd.md +127 -0
- package/rules/workflow-sdd.md +56 -0
- package/schema/sdd.config.schema.json +104 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { configFromJson, } from "../../../../shared/domain/Config.js";
|
|
4
|
+
import { configFailure } from "../../../../shared/domain/Errors.js";
|
|
5
|
+
import { specBlocks } from "../../../../shared/domain/SpecBlocks.js";
|
|
6
|
+
export class NodeRefreshFileReader {
|
|
7
|
+
async config(repoRoot) {
|
|
8
|
+
const configPath = join(repoRoot, ".sdd", "config.json");
|
|
9
|
+
const text = await readConfig(configPath);
|
|
10
|
+
const config = configFromJson(parseConfigJson(text, configPath), configPath);
|
|
11
|
+
await this.spec(repoRoot, config);
|
|
12
|
+
return config;
|
|
13
|
+
}
|
|
14
|
+
async spec(repoRoot, config) {
|
|
15
|
+
const path = join(repoRoot, config.specFile);
|
|
16
|
+
let text;
|
|
17
|
+
try {
|
|
18
|
+
text = await readFile(path, "utf8");
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw configFailure("config-invalid", `spec_file is not readable: ${config.specFile}`, errorMessage(error), path);
|
|
22
|
+
}
|
|
23
|
+
return { path, blocks: specBlocks(text) };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function readConfig(path) {
|
|
27
|
+
try {
|
|
28
|
+
return await readFile(path, "utf8");
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw configFailure("config-missing", ".sdd/config.json is missing or unreadable", errorMessage(error), path);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function parseConfigJson(text, path) {
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(text);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
throw configFailure("config-invalid", ".sdd/config.json is not valid JSON", errorMessage(error), path);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function errorMessage(error) {
|
|
43
|
+
return error instanceof Error ? error.message : String(error);
|
|
44
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Stub } from "../domain/DiffStubs.js";
|
|
2
|
+
import type { RefreshClockPort } from "../ports/outbound/RefreshClockPort.js";
|
|
3
|
+
import type { RefreshConfigPort } from "../ports/outbound/RefreshConfigPort.js";
|
|
4
|
+
import type { RefreshGitPort } from "../ports/outbound/RefreshGitPort.js";
|
|
5
|
+
import type { RefreshSpecPort } from "../ports/outbound/RefreshSpecPort.js";
|
|
6
|
+
export type RefreshOutcome = {
|
|
7
|
+
kind: "json";
|
|
8
|
+
body: {
|
|
9
|
+
format_version: 1;
|
|
10
|
+
stubs: Stub[];
|
|
11
|
+
};
|
|
12
|
+
} | {
|
|
13
|
+
kind: "human";
|
|
14
|
+
text: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "yaml";
|
|
17
|
+
text: string;
|
|
18
|
+
};
|
|
19
|
+
export interface BuildRefreshStubsPorts {
|
|
20
|
+
clock: RefreshClockPort;
|
|
21
|
+
config: RefreshConfigPort;
|
|
22
|
+
git: RefreshGitPort;
|
|
23
|
+
spec: RefreshSpecPort;
|
|
24
|
+
}
|
|
25
|
+
export declare function buildRefreshStubs(cwd: string, format: "human" | "json" | "yaml", ports: BuildRefreshStubsPorts): Promise<RefreshOutcome>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { configFailure } from "../../../shared/domain/Errors.js";
|
|
2
|
+
import { stringValue, typedBlock, } from "../../../shared/domain/SpecBlocks.js";
|
|
3
|
+
import { humanStubText, stubs, yamlStubStream, } from "../domain/DiffStubs.js";
|
|
4
|
+
import { footprint, impsMissingBinding } from "../domain/Footprint.js";
|
|
5
|
+
export async function buildRefreshStubs(cwd, format, ports) {
|
|
6
|
+
const repoRoot = await ports.git.repoRoot(cwd);
|
|
7
|
+
await ports.git.headSha(repoRoot);
|
|
8
|
+
const config = await ports.config.config(repoRoot);
|
|
9
|
+
await assertGlobMatches(ports.git, repoRoot, config.discoveryScope);
|
|
10
|
+
const loadedSpec = await ports.spec.spec(repoRoot, config);
|
|
11
|
+
const missingBinding = impsMissingBinding(loadedSpec.blocks, config.footprint.bindingIdPrefix, config.footprint.bindingField);
|
|
12
|
+
if (missingBinding.length > 0) {
|
|
13
|
+
throw configFailure("config-invalid", `IMP block(s) missing ${config.footprint.bindingField}: ${missingBinding.join(", ")} (OQ-004 / BEH-051)`);
|
|
14
|
+
}
|
|
15
|
+
const recorded = baseline(loadedSpec.path, loadedSpec.blocks, config.baselineId);
|
|
16
|
+
const committedPaths = await ports.git.changedPaths(repoRoot, recorded.baselineCommitSha, config.discoveryScope);
|
|
17
|
+
const dirtyPaths = await ports.git.dirtyPaths(repoRoot, config.discoveryScope);
|
|
18
|
+
const changedPaths = [...new Set([...committedPaths, ...dirtyPaths])].sort();
|
|
19
|
+
const pathFootprint = footprint(loadedSpec.blocks, config.footprint.bindingIdPrefix, config.footprint.bindingField);
|
|
20
|
+
const emittedStubs = stubs(changedPaths, pathFootprint, ports.clock.iso());
|
|
21
|
+
if (format === "json") {
|
|
22
|
+
return { kind: "json", body: { format_version: 1, stubs: emittedStubs } };
|
|
23
|
+
}
|
|
24
|
+
if (format === "human") {
|
|
25
|
+
return { kind: "human", text: humanStubText(emittedStubs) };
|
|
26
|
+
}
|
|
27
|
+
return { kind: "yaml", text: yamlStubStream(emittedStubs) };
|
|
28
|
+
}
|
|
29
|
+
function baseline(specPath, blocks, baselineId) {
|
|
30
|
+
const block = typedBlock(blocks, baselineId, "BrownfieldBaseline", specPath);
|
|
31
|
+
return {
|
|
32
|
+
freshnessToken: stringValue(block, "freshness_token", specPath),
|
|
33
|
+
baselineCommitSha: stringValue(block, "baseline_commit_sha", specPath),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function assertGlobMatches(git, repoRoot, scope) {
|
|
37
|
+
for (const entry of scope.filter((value) => /[*?[]/.test(value))) {
|
|
38
|
+
const paths = await git.treePaths(repoRoot, [entry]);
|
|
39
|
+
if (paths.length === 0) {
|
|
40
|
+
throw configFailure("config-invalid", `discovery_scope glob matched zero files: ${entry}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Footprint } from "./Footprint.js";
|
|
2
|
+
export type Stub = DeltaStub | OpenQStub;
|
|
3
|
+
export interface DeltaStub {
|
|
4
|
+
kind: "Delta";
|
|
5
|
+
path: string;
|
|
6
|
+
target_imp_ids: string[];
|
|
7
|
+
target_ids: string[];
|
|
8
|
+
emitted_at: string;
|
|
9
|
+
compatibility_action: "TODO";
|
|
10
|
+
kind_of_change: "TODO";
|
|
11
|
+
tests_old_behavior: "TODO";
|
|
12
|
+
tests_new_behavior: "TODO";
|
|
13
|
+
}
|
|
14
|
+
export interface OpenQStub {
|
|
15
|
+
kind: "Open-Q";
|
|
16
|
+
path: string;
|
|
17
|
+
question: string;
|
|
18
|
+
options: string[];
|
|
19
|
+
blocking: "TODO";
|
|
20
|
+
emitted_at: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function stubs(paths: readonly string[], footprint: Footprint, emittedAt: string): Stub[];
|
|
23
|
+
export declare function yamlStubStream(stubs: readonly Stub[]): string;
|
|
24
|
+
export declare function humanStubText(stubs: readonly Stub[]): string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { footprintEntriesForPath } from "./Footprint.js";
|
|
2
|
+
export function stubs(paths, footprint, emittedAt) {
|
|
3
|
+
return [...new Set(paths)]
|
|
4
|
+
.sort((left, right) => left.localeCompare(right, "en"))
|
|
5
|
+
.map((path) => {
|
|
6
|
+
const entries = footprintEntriesForPath(footprint, path);
|
|
7
|
+
if (entries.length === 0) {
|
|
8
|
+
return openQStub(path, emittedAt);
|
|
9
|
+
}
|
|
10
|
+
const targetImpIds = entries.map((entry) => entry.impId).sort();
|
|
11
|
+
const targetIds = [
|
|
12
|
+
...new Set(entries.flatMap((entry) => entry.targetIds)),
|
|
13
|
+
].sort();
|
|
14
|
+
return {
|
|
15
|
+
kind: "Delta",
|
|
16
|
+
path,
|
|
17
|
+
target_imp_ids: targetImpIds,
|
|
18
|
+
target_ids: targetIds,
|
|
19
|
+
emitted_at: emittedAt,
|
|
20
|
+
compatibility_action: "TODO",
|
|
21
|
+
kind_of_change: "TODO",
|
|
22
|
+
tests_old_behavior: "TODO",
|
|
23
|
+
tests_new_behavior: "TODO",
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function yamlStubStream(stubs) {
|
|
28
|
+
if (stubs.length === 0) {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
return stubs.map((stub) => `---\n${yamlStub(stub)}`).join("\n");
|
|
32
|
+
}
|
|
33
|
+
export function humanStubText(stubs) {
|
|
34
|
+
return stubs
|
|
35
|
+
.map((stub) => {
|
|
36
|
+
if (stub.kind === "Delta") {
|
|
37
|
+
return `Delta ${stub.path} -> ${stub.target_imp_ids.join(",")}`;
|
|
38
|
+
}
|
|
39
|
+
return `Open-Q ${stub.path} -> -`;
|
|
40
|
+
})
|
|
41
|
+
.join("\n");
|
|
42
|
+
}
|
|
43
|
+
function openQStub(path, emittedAt) {
|
|
44
|
+
return {
|
|
45
|
+
kind: "Open-Q",
|
|
46
|
+
path,
|
|
47
|
+
question: `Should ${path} be bound to a normative ID?`,
|
|
48
|
+
options: ["bind_to_existing_or_new_id", "leave_unmodeled"],
|
|
49
|
+
blocking: "TODO",
|
|
50
|
+
emitted_at: emittedAt,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function yamlStub(stub) {
|
|
54
|
+
if (stub.kind === "Delta") {
|
|
55
|
+
return [
|
|
56
|
+
"kind: Delta",
|
|
57
|
+
`path: ${quoted(stub.path)}`,
|
|
58
|
+
"target_imp_ids:",
|
|
59
|
+
...stub.target_imp_ids.map((id) => ` - ${quoted(id)}`),
|
|
60
|
+
"target_ids:",
|
|
61
|
+
...stub.target_ids.map((id) => ` - ${quoted(id)}`),
|
|
62
|
+
`emitted_at: ${quoted(stub.emitted_at)}`,
|
|
63
|
+
"compatibility_action: TODO",
|
|
64
|
+
"kind_of_change: TODO",
|
|
65
|
+
"tests_old_behavior: TODO",
|
|
66
|
+
"tests_new_behavior: TODO",
|
|
67
|
+
].join("\n");
|
|
68
|
+
}
|
|
69
|
+
return [
|
|
70
|
+
"kind: Open-Q",
|
|
71
|
+
`path: ${quoted(stub.path)}`,
|
|
72
|
+
`question: ${quoted(stub.question)}`,
|
|
73
|
+
"options:",
|
|
74
|
+
...stub.options.map((option) => ` - ${quoted(option)}`),
|
|
75
|
+
"blocking: TODO",
|
|
76
|
+
`emitted_at: ${quoted(stub.emitted_at)}`,
|
|
77
|
+
].join("\n");
|
|
78
|
+
}
|
|
79
|
+
function quoted(value) {
|
|
80
|
+
return JSON.stringify(value);
|
|
81
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SpecBlock } from "../../../shared/domain/SpecBlocks.js";
|
|
2
|
+
export interface FootprintEntry {
|
|
3
|
+
impId: string;
|
|
4
|
+
targetIds: string[];
|
|
5
|
+
paths: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface Footprint {
|
|
8
|
+
entries: FootprintEntry[];
|
|
9
|
+
}
|
|
10
|
+
export declare function footprint(blocks: readonly SpecBlock[], bindingIdPrefix: string, bindingField: string): Footprint;
|
|
11
|
+
/** ENF/OQ-004 — IDs of binding-prefixed (IMP-*) blocks that carry no
|
|
12
|
+
* `bindingField`. An empty result means every such block declares a binding. */
|
|
13
|
+
export declare function impsMissingBinding(blocks: readonly SpecBlock[], bindingIdPrefix: string, bindingField: string): string[];
|
|
14
|
+
export declare function footprintEntriesForPath(footprint: Footprint, path: string): FootprintEntry[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { blocksByNeutralPrefix } from "../../../shared/domain/SpecBlocks.js";
|
|
2
|
+
export function footprint(blocks, bindingIdPrefix, bindingField) {
|
|
3
|
+
const entries = blocksByNeutralPrefix(blocks, bindingIdPrefix).map((block) => {
|
|
4
|
+
return {
|
|
5
|
+
impId: block.id,
|
|
6
|
+
targetIds: stringArray(block.parsed.target_ids),
|
|
7
|
+
paths: stringLeaves(block.parsed[bindingField]),
|
|
8
|
+
};
|
|
9
|
+
});
|
|
10
|
+
return { entries };
|
|
11
|
+
}
|
|
12
|
+
/** ENF/OQ-004 — IDs of binding-prefixed (IMP-*) blocks that carry no
|
|
13
|
+
* `bindingField`. An empty result means every such block declares a binding. */
|
|
14
|
+
export function impsMissingBinding(blocks, bindingIdPrefix, bindingField) {
|
|
15
|
+
return blocksByNeutralPrefix(blocks, bindingIdPrefix)
|
|
16
|
+
.filter((block) => block.parsed[bindingField] === undefined)
|
|
17
|
+
.map((block) => block.id);
|
|
18
|
+
}
|
|
19
|
+
export function footprintEntriesForPath(footprint, path) {
|
|
20
|
+
return footprint.entries.filter((entry) => entry.paths.some((boundPath) => covers(boundPath, path)));
|
|
21
|
+
}
|
|
22
|
+
function covers(boundPath, path) {
|
|
23
|
+
return path === boundPath || path.startsWith(`${boundPath}/`);
|
|
24
|
+
}
|
|
25
|
+
function stringLeaves(value) {
|
|
26
|
+
if (typeof value === "string") {
|
|
27
|
+
return [value];
|
|
28
|
+
}
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
return value.flatMap((entry) => stringLeaves(entry));
|
|
31
|
+
}
|
|
32
|
+
if (isRecord(value)) {
|
|
33
|
+
return Object.values(value).flatMap((entry) => stringLeaves(entry));
|
|
34
|
+
}
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
function stringArray(value) {
|
|
38
|
+
if (!Array.isArray(value)) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return value.filter((entry) => typeof entry === "string");
|
|
42
|
+
}
|
|
43
|
+
function isRecord(value) {
|
|
44
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface RefreshGitPort {
|
|
2
|
+
repoRoot(cwd: string): Promise<string>;
|
|
3
|
+
headSha(repoRoot: string): Promise<string>;
|
|
4
|
+
treePaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
|
|
5
|
+
dirtyPaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
|
|
6
|
+
changedPaths(repoRoot: string, baselineCommitSha: string, scope: readonly string[]): Promise<string[]>;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SddConfig } from "../../../../shared/domain/Config.js";
|
|
2
|
+
import type { SpecBlock } from "../../../../shared/domain/SpecBlocks.js";
|
|
3
|
+
export interface RefreshSpec {
|
|
4
|
+
path: string;
|
|
5
|
+
blocks: SpecBlock[];
|
|
6
|
+
}
|
|
7
|
+
export interface RefreshSpecPort {
|
|
8
|
+
spec(repoRoot: string, config: SddConfig): Promise<RefreshSpec>;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CommandResult, type OutputFormat } from "../../../../shared/domain/CliOutput.js";
|
|
2
|
+
import { type RunReportPorts } from "../../application/RunReport.js";
|
|
3
|
+
import type { ReportCommand, ReportRequest } from "../../ports/inbound/ReportCommand.js";
|
|
4
|
+
export declare class CliReportHandler implements ReportCommand {
|
|
5
|
+
private readonly ports;
|
|
6
|
+
constructor(ports: RunReportPorts);
|
|
7
|
+
execute(cwd: string, req: ReportRequest, format: Exclude<OutputFormat, "yaml">): Promise<CommandResult>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ok, } from "../../../../shared/domain/CliOutput.js";
|
|
2
|
+
import { CliFailure } from "../../../../shared/domain/Errors.js";
|
|
3
|
+
import { runReport } from "../../application/RunReport.js";
|
|
4
|
+
export class CliReportHandler {
|
|
5
|
+
ports;
|
|
6
|
+
constructor(ports) {
|
|
7
|
+
this.ports = ports;
|
|
8
|
+
}
|
|
9
|
+
async execute(cwd, req, format) {
|
|
10
|
+
if (!req.prSummary) {
|
|
11
|
+
return {
|
|
12
|
+
exitCode: 2,
|
|
13
|
+
stdout: "",
|
|
14
|
+
stderr: "report: --pr-summary is required (no other modes implemented in v0.4)\n",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const out = await runReport(cwd, { prSummary: true, against: req.against }, this.ports);
|
|
19
|
+
if (format === "json") {
|
|
20
|
+
return ok(JSON.stringify({
|
|
21
|
+
format_version: 1,
|
|
22
|
+
ok: true,
|
|
23
|
+
markdown: out.markdown,
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
return ok(out.markdown);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error instanceof CliFailure) {
|
|
30
|
+
return { exitCode: 2, stdout: "", stderr: `${error.message}\n` };
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type SddConfig } from "../../../../shared/domain/Config.js";
|
|
2
|
+
import type { ReportConfigPort } from "../../ports/outbound/ReportConfigPort.js";
|
|
3
|
+
import type { ReportFileReader, SpecFileEntry } from "../../ports/outbound/ReportFileReader.js";
|
|
4
|
+
export declare class NodeReportFileSystem implements ReportConfigPort, ReportFileReader {
|
|
5
|
+
config(repoRoot: string): Promise<SddConfig>;
|
|
6
|
+
resolveSpecFiles(repoRoot: string, patterns: readonly string[]): Promise<SpecFileEntry[]>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join, relative, resolve } from "node:path";
|
|
3
|
+
import { configFromJson, } from "../../../../shared/domain/Config.js";
|
|
4
|
+
import { configFailure, errorMessage, } from "../../../../shared/domain/Errors.js";
|
|
5
|
+
export class NodeReportFileSystem {
|
|
6
|
+
async config(repoRoot) {
|
|
7
|
+
const configPath = join(repoRoot, ".sdd", "config.json");
|
|
8
|
+
let text;
|
|
9
|
+
try {
|
|
10
|
+
text = await readFile(configPath, "utf8");
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
throw configFailure("config-invalid", `cannot read .sdd/config.json: ${errorMessage(e)}`, undefined, configPath);
|
|
14
|
+
}
|
|
15
|
+
return configFromJson(JSON.parse(text), configPath);
|
|
16
|
+
}
|
|
17
|
+
async resolveSpecFiles(repoRoot, patterns) {
|
|
18
|
+
const matched = new Set();
|
|
19
|
+
for (const pattern of patterns) {
|
|
20
|
+
for (const abs of await expandGlob(repoRoot, pattern)) {
|
|
21
|
+
matched.add(abs);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const list = [...matched].sort();
|
|
25
|
+
const out = [];
|
|
26
|
+
for (const abs of list) {
|
|
27
|
+
const text = await readFile(abs, "utf8");
|
|
28
|
+
const rel = relative(repoRoot, abs).split("\\").join("/");
|
|
29
|
+
out.push({ path: rel, content: text });
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function expandGlob(repoRoot, pattern) {
|
|
35
|
+
const normalised = pattern.split("\\").join("/");
|
|
36
|
+
if (!hasGlob(normalised)) {
|
|
37
|
+
const abs = resolve(repoRoot, normalised);
|
|
38
|
+
return (await isFile(abs)) ? [abs] : [];
|
|
39
|
+
}
|
|
40
|
+
const segments = normalised.split("/");
|
|
41
|
+
const literal = [];
|
|
42
|
+
let firstGlobIndex = -1;
|
|
43
|
+
for (let i = 0; i < segments.length; i++) {
|
|
44
|
+
if (hasGlob(segments[i])) {
|
|
45
|
+
firstGlobIndex = i;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
literal.push(segments[i]);
|
|
49
|
+
}
|
|
50
|
+
const baseDir = resolve(repoRoot, ...literal);
|
|
51
|
+
return walk(baseDir, segments.slice(firstGlobIndex));
|
|
52
|
+
}
|
|
53
|
+
function hasGlob(s) {
|
|
54
|
+
return s.includes("*") || s.includes("?");
|
|
55
|
+
}
|
|
56
|
+
async function walk(baseDir, segments) {
|
|
57
|
+
if (segments.length === 0) {
|
|
58
|
+
return (await isFile(baseDir)) ? [baseDir] : [];
|
|
59
|
+
}
|
|
60
|
+
const [head, ...rest] = segments;
|
|
61
|
+
if (head === "**") {
|
|
62
|
+
const out = [];
|
|
63
|
+
out.push(...(await walk(baseDir, rest)));
|
|
64
|
+
let entries;
|
|
65
|
+
try {
|
|
66
|
+
entries = await readdir(baseDir, { withFileTypes: true });
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
for (const e of entries) {
|
|
72
|
+
if (!e.isDirectory()) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
out.push(...(await walk(join(baseDir, e.name), segments)));
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
const re = segmentToRegExp(head);
|
|
80
|
+
let entries;
|
|
81
|
+
try {
|
|
82
|
+
entries = await readdir(baseDir, { withFileTypes: true });
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const e of entries) {
|
|
89
|
+
if (!re.test(e.name)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const next = join(baseDir, e.name);
|
|
93
|
+
if (rest.length === 0) {
|
|
94
|
+
if (await isFile(next)) {
|
|
95
|
+
out.push(next);
|
|
96
|
+
}
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (e.isDirectory()) {
|
|
100
|
+
out.push(...(await walk(next, rest)));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
function segmentToRegExp(seg) {
|
|
106
|
+
let body = "^";
|
|
107
|
+
for (const ch of seg) {
|
|
108
|
+
if (ch === "*") {
|
|
109
|
+
body += "[^/]*";
|
|
110
|
+
}
|
|
111
|
+
else if (ch === "?") {
|
|
112
|
+
body += "[^/]";
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
body += ch.replace(/[-./\\^$+?()|[\]{}]/g, "\\$&");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
body += "$";
|
|
119
|
+
return new RegExp(body);
|
|
120
|
+
}
|
|
121
|
+
async function isFile(abs) {
|
|
122
|
+
try {
|
|
123
|
+
return (await stat(abs)).isFile();
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReportConfigPort } from "../ports/outbound/ReportConfigPort.js";
|
|
2
|
+
import type { ReportFileReader } from "../ports/outbound/ReportFileReader.js";
|
|
3
|
+
export interface RunReportPorts {
|
|
4
|
+
config: ReportConfigPort;
|
|
5
|
+
files: ReportFileReader;
|
|
6
|
+
/** Optional git-at-ref reader for the debt-budget delta section. When
|
|
7
|
+
* omitted, debt-budget delta is rendered as "(--against not provided
|
|
8
|
+
* or git unavailable)". */
|
|
9
|
+
readAtRef?: (repoRoot: string, ref: string, relativePath: string) => Promise<string | null>;
|
|
10
|
+
repoRoot?: (cwd: string) => Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
export interface RunReportInput {
|
|
13
|
+
prSummary: boolean;
|
|
14
|
+
against?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface RunReportOutput {
|
|
17
|
+
markdown: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function runReport(cwd: string, input: RunReportInput, ports: RunReportPorts): Promise<RunReportOutput>;
|