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.
Files changed (304) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1028 -0
  3. package/README.ru.md +1046 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.js +867 -0
  6. package/dist/features/approve/adapters/inbound/CliApproveHandler.d.ts +17 -0
  7. package/dist/features/approve/adapters/inbound/CliApproveHandler.js +108 -0
  8. package/dist/features/approve/adapters/outbound/NodeApproveFileSystem.d.ts +8 -0
  9. package/dist/features/approve/adapters/outbound/NodeApproveFileSystem.js +147 -0
  10. package/dist/features/approve/adapters/outbound/NodePlanFileWriter.d.ts +6 -0
  11. package/dist/features/approve/adapters/outbound/NodePlanFileWriter.js +92 -0
  12. package/dist/features/approve/adapters/outbound/SystemApproveClock.d.ts +4 -0
  13. package/dist/features/approve/adapters/outbound/SystemApproveClock.js +5 -0
  14. package/dist/features/approve/application/ApplyApproval.d.ts +19 -0
  15. package/dist/features/approve/application/ApplyApproval.js +30 -0
  16. package/dist/features/approve/application/WriteAttestation.d.ts +19 -0
  17. package/dist/features/approve/application/WriteAttestation.js +23 -0
  18. package/dist/features/approve/domain/ApproveRequest.d.ts +24 -0
  19. package/dist/features/approve/domain/ApproveRequest.js +24 -0
  20. package/dist/features/approve/domain/Rewrite.d.ts +1 -0
  21. package/dist/features/approve/domain/Rewrite.js +6 -0
  22. package/dist/features/approve/ports/inbound/ApproveCommand.d.ts +10 -0
  23. package/dist/features/approve/ports/inbound/ApproveCommand.js +1 -0
  24. package/dist/features/approve/ports/outbound/ApproveClock.d.ts +3 -0
  25. package/dist/features/approve/ports/outbound/ApproveClock.js +1 -0
  26. package/dist/features/approve/ports/outbound/ApproveConfigPort.d.ts +4 -0
  27. package/dist/features/approve/ports/outbound/ApproveConfigPort.js +1 -0
  28. package/dist/features/approve/ports/outbound/ApproveFileSystem.d.ts +8 -0
  29. package/dist/features/approve/ports/outbound/ApproveFileSystem.js +1 -0
  30. package/dist/features/approve/ports/outbound/PlanFileWriter.d.ts +13 -0
  31. package/dist/features/approve/ports/outbound/PlanFileWriter.js +1 -0
  32. package/dist/features/check/adapters/inbound/CliCheckHandler.d.ts +8 -0
  33. package/dist/features/check/adapters/inbound/CliCheckHandler.js +62 -0
  34. package/dist/features/check/adapters/outbound/ChildProcessCheckGit.d.ts +8 -0
  35. package/dist/features/check/adapters/outbound/ChildProcessCheckGit.js +112 -0
  36. package/dist/features/check/adapters/outbound/NodeCheckFileReader.d.ts +7 -0
  37. package/dist/features/check/adapters/outbound/NodeCheckFileReader.js +44 -0
  38. package/dist/features/check/application/CheckBaseline.d.ts +28 -0
  39. package/dist/features/check/application/CheckBaseline.js +54 -0
  40. package/dist/features/check/domain/BaselineComparison.d.ts +1 -0
  41. package/dist/features/check/domain/BaselineComparison.js +2 -0
  42. package/dist/features/check/ports/inbound/CheckCommand.d.ts +4 -0
  43. package/dist/features/check/ports/inbound/CheckCommand.js +1 -0
  44. package/dist/features/check/ports/outbound/CheckConfigPort.d.ts +4 -0
  45. package/dist/features/check/ports/outbound/CheckConfigPort.js +1 -0
  46. package/dist/features/check/ports/outbound/CheckGitPort.d.ts +7 -0
  47. package/dist/features/check/ports/outbound/CheckGitPort.js +1 -0
  48. package/dist/features/check/ports/outbound/CheckSpecPort.d.ts +9 -0
  49. package/dist/features/check/ports/outbound/CheckSpecPort.js +1 -0
  50. package/dist/features/doctor/adapters/inbound/CliDoctorHandler.d.ts +8 -0
  51. package/dist/features/doctor/adapters/inbound/CliDoctorHandler.js +77 -0
  52. package/dist/features/doctor/adapters/outbound/NodeRegistryReader.d.ts +5 -0
  53. package/dist/features/doctor/adapters/outbound/NodeRegistryReader.js +40 -0
  54. package/dist/features/doctor/application/RunDoctor.d.ts +30 -0
  55. package/dist/features/doctor/application/RunDoctor.js +78 -0
  56. package/dist/features/doctor/domain/RegistryRow.d.ts +23 -0
  57. package/dist/features/doctor/domain/RegistryRow.js +114 -0
  58. package/dist/features/doctor/domain/SemverRange.d.ts +7 -0
  59. package/dist/features/doctor/domain/SemverRange.js +82 -0
  60. package/dist/features/doctor/ports/inbound/DoctorCommand.d.ts +8 -0
  61. package/dist/features/doctor/ports/inbound/DoctorCommand.js +1 -0
  62. package/dist/features/doctor/ports/outbound/RegistryReader.d.ts +12 -0
  63. package/dist/features/doctor/ports/outbound/RegistryReader.js +1 -0
  64. package/dist/features/finalize/adapters/inbound/CliFinalizeHandler.d.ts +8 -0
  65. package/dist/features/finalize/adapters/inbound/CliFinalizeHandler.js +80 -0
  66. package/dist/features/finalize/adapters/outbound/NodeFinalizeFileSystem.d.ts +11 -0
  67. package/dist/features/finalize/adapters/outbound/NodeFinalizeFileSystem.js +167 -0
  68. package/dist/features/finalize/adapters/outbound/NodePlanRepo.d.ts +7 -0
  69. package/dist/features/finalize/adapters/outbound/NodePlanRepo.js +82 -0
  70. package/dist/features/finalize/adapters/outbound/SystemFinalizeClock.d.ts +4 -0
  71. package/dist/features/finalize/adapters/outbound/SystemFinalizeClock.js +5 -0
  72. package/dist/features/finalize/application/RunFinalize.d.ts +34 -0
  73. package/dist/features/finalize/application/RunFinalize.js +98 -0
  74. package/dist/features/finalize/domain/ValidateFinalizeGraph.d.ts +9 -0
  75. package/dist/features/finalize/domain/ValidateFinalizeGraph.js +86 -0
  76. package/dist/features/finalize/ports/inbound/FinalizeCommand.d.ts +7 -0
  77. package/dist/features/finalize/ports/inbound/FinalizeCommand.js +1 -0
  78. package/dist/features/finalize/ports/outbound/FinalizeClock.d.ts +3 -0
  79. package/dist/features/finalize/ports/outbound/FinalizeClock.js +1 -0
  80. package/dist/features/finalize/ports/outbound/FinalizeConfigPort.d.ts +4 -0
  81. package/dist/features/finalize/ports/outbound/FinalizeConfigPort.js +1 -0
  82. package/dist/features/finalize/ports/outbound/FinalizeFileSystem.d.ts +14 -0
  83. package/dist/features/finalize/ports/outbound/FinalizeFileSystem.js +1 -0
  84. package/dist/features/finalize/ports/outbound/PlanRepo.d.ts +21 -0
  85. package/dist/features/finalize/ports/outbound/PlanRepo.js +1 -0
  86. package/dist/features/install/adapters/inbound/CliInstallHandler.d.ts +8 -0
  87. package/dist/features/install/adapters/inbound/CliInstallHandler.js +54 -0
  88. package/dist/features/install/adapters/outbound/NodeInstallSource.d.ts +7 -0
  89. package/dist/features/install/adapters/outbound/NodeInstallSource.js +24 -0
  90. package/dist/features/install/adapters/outbound/NodeInstallTargetFs.d.ts +7 -0
  91. package/dist/features/install/adapters/outbound/NodeInstallTargetFs.js +30 -0
  92. package/dist/features/install/application/InstallRules.d.ts +10 -0
  93. package/dist/features/install/application/InstallRules.js +73 -0
  94. package/dist/features/install/domain/InstallPlan.d.ts +27 -0
  95. package/dist/features/install/domain/InstallPlan.js +168 -0
  96. package/dist/features/install/domain/InstallResult.d.ts +23 -0
  97. package/dist/features/install/domain/InstallResult.js +1 -0
  98. package/dist/features/install/domain/InstallTarget.d.ts +6 -0
  99. package/dist/features/install/domain/InstallTarget.js +7 -0
  100. package/dist/features/install/domain/ManagedBlock.d.ts +3 -0
  101. package/dist/features/install/domain/ManagedBlock.js +20 -0
  102. package/dist/features/install/domain/RuleManifest.d.ts +17 -0
  103. package/dist/features/install/domain/RuleManifest.js +69 -0
  104. package/dist/features/install/domain/SettingsMerge.d.ts +5 -0
  105. package/dist/features/install/domain/SettingsMerge.js +43 -0
  106. package/dist/features/install/ports/inbound/InstallCommand.d.ts +10 -0
  107. package/dist/features/install/ports/inbound/InstallCommand.js +1 -0
  108. package/dist/features/install/ports/outbound/InstallSource.d.ts +4 -0
  109. package/dist/features/install/ports/outbound/InstallSource.js +1 -0
  110. package/dist/features/install/ports/outbound/InstallTargetFs.d.ts +6 -0
  111. package/dist/features/install/ports/outbound/InstallTargetFs.js +1 -0
  112. package/dist/features/lint/adapters/inbound/CliLintHandler.d.ts +8 -0
  113. package/dist/features/lint/adapters/inbound/CliLintHandler.js +61 -0
  114. package/dist/features/lint/adapters/outbound/NodeLintFileReader.d.ts +7 -0
  115. package/dist/features/lint/adapters/outbound/NodeLintFileReader.js +165 -0
  116. package/dist/features/lint/application/RunLint.d.ts +10 -0
  117. package/dist/features/lint/application/RunLint.js +100 -0
  118. package/dist/features/lint/domain/Diagnostic.d.ts +1 -0
  119. package/dist/features/lint/domain/Diagnostic.js +2 -0
  120. package/dist/features/lint/domain/Record.d.ts +1 -0
  121. package/dist/features/lint/domain/Record.js +5 -0
  122. package/dist/features/lint/domain/Rules.d.ts +1 -0
  123. package/dist/features/lint/domain/Rules.js +2 -0
  124. package/dist/features/lint/domain/SpecParser.d.ts +1 -0
  125. package/dist/features/lint/domain/SpecParser.js +2 -0
  126. package/dist/features/lint/ports/inbound/LintCommand.d.ts +4 -0
  127. package/dist/features/lint/ports/inbound/LintCommand.js +1 -0
  128. package/dist/features/lint/ports/outbound/LintConfigPort.d.ts +4 -0
  129. package/dist/features/lint/ports/outbound/LintConfigPort.js +1 -0
  130. package/dist/features/lint/ports/outbound/LintFileReader.d.ts +10 -0
  131. package/dist/features/lint/ports/outbound/LintFileReader.js +1 -0
  132. package/dist/features/plan/adapters/inbound/CliPlanShowHandler.d.ts +8 -0
  133. package/dist/features/plan/adapters/inbound/CliPlanShowHandler.js +73 -0
  134. package/dist/features/plan/adapters/outbound/NodePlanReader.d.ts +7 -0
  135. package/dist/features/plan/adapters/outbound/NodePlanReader.js +68 -0
  136. package/dist/features/plan/application/ShowPlan.d.ts +7 -0
  137. package/dist/features/plan/application/ShowPlan.js +4 -0
  138. package/dist/features/plan/ports/inbound/PlanShowCommand.d.ts +7 -0
  139. package/dist/features/plan/ports/inbound/PlanShowCommand.js +1 -0
  140. package/dist/features/plan/ports/outbound/PlanConfigPort.d.ts +4 -0
  141. package/dist/features/plan/ports/outbound/PlanConfigPort.js +1 -0
  142. package/dist/features/plan/ports/outbound/PlanReader.d.ts +19 -0
  143. package/dist/features/plan/ports/outbound/PlanReader.js +1 -0
  144. package/dist/features/ready/adapters/inbound/CliReadyHandler.d.ts +8 -0
  145. package/dist/features/ready/adapters/inbound/CliReadyHandler.js +79 -0
  146. package/dist/features/ready/adapters/outbound/ChildProcessReadyGit.d.ts +9 -0
  147. package/dist/features/ready/adapters/outbound/ChildProcessReadyGit.js +113 -0
  148. package/dist/features/ready/adapters/outbound/NodeReadyFileSystem.d.ts +8 -0
  149. package/dist/features/ready/adapters/outbound/NodeReadyFileSystem.js +159 -0
  150. package/dist/features/ready/application/RunReady.d.ts +16 -0
  151. package/dist/features/ready/application/RunReady.js +572 -0
  152. package/dist/features/ready/domain/AggregatedRules.d.ts +16 -0
  153. package/dist/features/ready/domain/AggregatedRules.js +42 -0
  154. package/dist/features/ready/domain/MarkerParser.d.ts +17 -0
  155. package/dist/features/ready/domain/MarkerParser.js +108 -0
  156. package/dist/features/ready/domain/PartitionResolver.d.ts +1 -0
  157. package/dist/features/ready/domain/PartitionResolver.js +5 -0
  158. package/dist/features/ready/domain/ReadyInput.d.ts +6 -0
  159. package/dist/features/ready/domain/ReadyInput.js +1 -0
  160. package/dist/features/ready/domain/ReadyViolation.d.ts +38 -0
  161. package/dist/features/ready/domain/ReadyViolation.js +19 -0
  162. package/dist/features/ready/domain/Rules.d.ts +22 -0
  163. package/dist/features/ready/domain/Rules.js +243 -0
  164. package/dist/features/ready/domain/SpecDiff.d.ts +33 -0
  165. package/dist/features/ready/domain/SpecDiff.js +321 -0
  166. package/dist/features/ready/ports/inbound/ReadyCommand.d.ts +4 -0
  167. package/dist/features/ready/ports/inbound/ReadyCommand.js +1 -0
  168. package/dist/features/ready/ports/outbound/ReadyConfigPort.d.ts +4 -0
  169. package/dist/features/ready/ports/outbound/ReadyConfigPort.js +1 -0
  170. package/dist/features/ready/ports/outbound/ReadyFileReader.d.ts +12 -0
  171. package/dist/features/ready/ports/outbound/ReadyFileReader.js +1 -0
  172. package/dist/features/ready/ports/outbound/ReadyGitPort.d.ts +10 -0
  173. package/dist/features/ready/ports/outbound/ReadyGitPort.js +5 -0
  174. package/dist/features/record/adapters/inbound/CliRecordHandler.d.ts +10 -0
  175. package/dist/features/record/adapters/inbound/CliRecordHandler.js +111 -0
  176. package/dist/features/record/adapters/outbound/NodeRecordFileSystem.d.ts +9 -0
  177. package/dist/features/record/adapters/outbound/NodeRecordFileSystem.js +152 -0
  178. package/dist/features/record/application/AddRecord.d.ts +11 -0
  179. package/dist/features/record/application/AddRecord.js +84 -0
  180. package/dist/features/record/application/GetRecord.d.ts +8 -0
  181. package/dist/features/record/application/GetRecord.js +22 -0
  182. package/dist/features/record/application/ListRecords.d.ts +9 -0
  183. package/dist/features/record/application/ListRecords.js +24 -0
  184. package/dist/features/record/application/SetRecord.d.ts +11 -0
  185. package/dist/features/record/application/SetRecord.js +68 -0
  186. package/dist/features/record/domain/RecordBody.d.ts +12 -0
  187. package/dist/features/record/domain/RecordBody.js +66 -0
  188. package/dist/features/record/domain/RecordPartition.d.ts +1 -0
  189. package/dist/features/record/domain/RecordPartition.js +7 -0
  190. package/dist/features/record/domain/RecordSlice.d.ts +7 -0
  191. package/dist/features/record/domain/RecordSlice.js +1 -0
  192. package/dist/features/record/domain/RecordSummary.d.ts +11 -0
  193. package/dist/features/record/domain/RecordSummary.js +13 -0
  194. package/dist/features/record/domain/RecordWrite.d.ts +14 -0
  195. package/dist/features/record/domain/RecordWrite.js +8 -0
  196. package/dist/features/record/ports/inbound/RecordCommand.d.ts +19 -0
  197. package/dist/features/record/ports/inbound/RecordCommand.js +1 -0
  198. package/dist/features/record/ports/outbound/RecordConfigPort.d.ts +4 -0
  199. package/dist/features/record/ports/outbound/RecordConfigPort.js +1 -0
  200. package/dist/features/record/ports/outbound/RecordFileReader.d.ts +10 -0
  201. package/dist/features/record/ports/outbound/RecordFileReader.js +1 -0
  202. package/dist/features/record/ports/outbound/RecordFileWriter.d.ts +6 -0
  203. package/dist/features/record/ports/outbound/RecordFileWriter.js +1 -0
  204. package/dist/features/refresh/adapters/inbound/CliRefreshHandler.d.ts +8 -0
  205. package/dist/features/refresh/adapters/inbound/CliRefreshHandler.js +24 -0
  206. package/dist/features/refresh/adapters/outbound/ChildProcessRefreshGit.d.ts +8 -0
  207. package/dist/features/refresh/adapters/outbound/ChildProcessRefreshGit.js +118 -0
  208. package/dist/features/refresh/adapters/outbound/NodeRefreshFileReader.d.ts +7 -0
  209. package/dist/features/refresh/adapters/outbound/NodeRefreshFileReader.js +44 -0
  210. package/dist/features/refresh/adapters/outbound/SystemRefreshClock.d.ts +4 -0
  211. package/dist/features/refresh/adapters/outbound/SystemRefreshClock.js +5 -0
  212. package/dist/features/refresh/application/BuildRefreshStubs.d.ts +25 -0
  213. package/dist/features/refresh/application/BuildRefreshStubs.js +43 -0
  214. package/dist/features/refresh/domain/DiffStubs.d.ts +24 -0
  215. package/dist/features/refresh/domain/DiffStubs.js +81 -0
  216. package/dist/features/refresh/domain/Footprint.d.ts +14 -0
  217. package/dist/features/refresh/domain/Footprint.js +45 -0
  218. package/dist/features/refresh/ports/inbound/RefreshCommand.d.ts +4 -0
  219. package/dist/features/refresh/ports/inbound/RefreshCommand.js +1 -0
  220. package/dist/features/refresh/ports/outbound/RefreshClockPort.d.ts +3 -0
  221. package/dist/features/refresh/ports/outbound/RefreshClockPort.js +1 -0
  222. package/dist/features/refresh/ports/outbound/RefreshConfigPort.d.ts +4 -0
  223. package/dist/features/refresh/ports/outbound/RefreshConfigPort.js +1 -0
  224. package/dist/features/refresh/ports/outbound/RefreshGitPort.d.ts +7 -0
  225. package/dist/features/refresh/ports/outbound/RefreshGitPort.js +1 -0
  226. package/dist/features/refresh/ports/outbound/RefreshSpecPort.d.ts +9 -0
  227. package/dist/features/refresh/ports/outbound/RefreshSpecPort.js +1 -0
  228. package/dist/features/report/adapters/inbound/CliReportHandler.d.ts +8 -0
  229. package/dist/features/report/adapters/inbound/CliReportHandler.js +35 -0
  230. package/dist/features/report/adapters/outbound/NodeReportFileSystem.d.ts +7 -0
  231. package/dist/features/report/adapters/outbound/NodeReportFileSystem.js +128 -0
  232. package/dist/features/report/application/RunReport.d.ts +19 -0
  233. package/dist/features/report/application/RunReport.js +161 -0
  234. package/dist/features/report/ports/inbound/ReportCommand.d.ts +8 -0
  235. package/dist/features/report/ports/inbound/ReportCommand.js +1 -0
  236. package/dist/features/report/ports/outbound/ReportConfigPort.d.ts +4 -0
  237. package/dist/features/report/ports/outbound/ReportConfigPort.js +1 -0
  238. package/dist/features/report/ports/outbound/ReportFileReader.d.ts +7 -0
  239. package/dist/features/report/ports/outbound/ReportFileReader.js +1 -0
  240. package/dist/features/token/adapters/inbound/CliTokenHandler.d.ts +8 -0
  241. package/dist/features/token/adapters/inbound/CliTokenHandler.js +53 -0
  242. package/dist/features/token/adapters/outbound/ChildProcessTokenGit.d.ts +8 -0
  243. package/dist/features/token/adapters/outbound/ChildProcessTokenGit.js +112 -0
  244. package/dist/features/token/adapters/outbound/NodeTokenConfigReader.d.ts +5 -0
  245. package/dist/features/token/adapters/outbound/NodeTokenConfigReader.js +41 -0
  246. package/dist/features/token/application/ComputeToken.d.ts +18 -0
  247. package/dist/features/token/application/ComputeToken.js +27 -0
  248. package/dist/features/token/ports/inbound/TokenCommand.d.ts +4 -0
  249. package/dist/features/token/ports/inbound/TokenCommand.js +1 -0
  250. package/dist/features/token/ports/outbound/TokenConfigPort.d.ts +4 -0
  251. package/dist/features/token/ports/outbound/TokenConfigPort.js +1 -0
  252. package/dist/features/token/ports/outbound/TokenGitPort.d.ts +7 -0
  253. package/dist/features/token/ports/outbound/TokenGitPort.js +1 -0
  254. package/dist/shared/domain/AgentBlocklist.d.ts +5 -0
  255. package/dist/shared/domain/AgentBlocklist.js +28 -0
  256. package/dist/shared/domain/BoundaryReachability.d.ts +5 -0
  257. package/dist/shared/domain/BoundaryReachability.js +71 -0
  258. package/dist/shared/domain/CheckOutcome.d.ts +10 -0
  259. package/dist/shared/domain/CheckOutcome.js +7 -0
  260. package/dist/shared/domain/CliOutput.d.ts +10 -0
  261. package/dist/shared/domain/CliOutput.js +29 -0
  262. package/dist/shared/domain/Config.d.ts +29 -0
  263. package/dist/shared/domain/Config.js +201 -0
  264. package/dist/shared/domain/DiagnosticRegistry.d.ts +8 -0
  265. package/dist/shared/domain/DiagnosticRegistry.js +71 -0
  266. package/dist/shared/domain/Errors.d.ts +12 -0
  267. package/dist/shared/domain/Errors.js +23 -0
  268. package/dist/shared/domain/GlobMatch.d.ts +2 -0
  269. package/dist/shared/domain/GlobMatch.js +58 -0
  270. package/dist/shared/domain/LintReport.d.ts +16 -0
  271. package/dist/shared/domain/LintReport.js +11 -0
  272. package/dist/shared/domain/LintRules.d.ts +67 -0
  273. package/dist/shared/domain/LintRules.js +956 -0
  274. package/dist/shared/domain/PartitionGrammar.d.ts +4 -0
  275. package/dist/shared/domain/PartitionGrammar.js +16 -0
  276. package/dist/shared/domain/PlanFile.d.ts +28 -0
  277. package/dist/shared/domain/PlanFile.js +112 -0
  278. package/dist/shared/domain/Scope.d.ts +1 -0
  279. package/dist/shared/domain/Scope.js +3 -0
  280. package/dist/shared/domain/SpecApprovalRewrite.d.ts +23 -0
  281. package/dist/shared/domain/SpecApprovalRewrite.js +254 -0
  282. package/dist/shared/domain/SpecBlocks.d.ts +12 -0
  283. package/dist/shared/domain/SpecBlocks.js +96 -0
  284. package/dist/shared/domain/SpecRecord.d.ts +17 -0
  285. package/dist/shared/domain/SpecRecord.js +208 -0
  286. package/dist/shared/domain/TemplateFieldMetadata.d.ts +2 -0
  287. package/dist/shared/domain/TemplateFieldMetadata.js +177 -0
  288. package/dist/shared/domain/Token.d.ts +2 -0
  289. package/dist/shared/domain/Token.js +5 -0
  290. package/dist/shared/domain/WeaselWords.d.ts +3 -0
  291. package/dist/shared/domain/WeaselWords.js +32 -0
  292. package/package.json +71 -0
  293. package/rules/enforcement_registry.md +126 -0
  294. package/rules/hooks/sdd-lint-reminder.sh +33 -0
  295. package/rules/hooks/sdd-spec-read-guard.sh +73 -0
  296. package/rules/manifest.json +15 -0
  297. package/rules/review-sdd.md +9 -0
  298. package/rules/sdd-cli-usage.md +91 -0
  299. package/rules/skills/spec-driven-development/SKILL.md +554 -0
  300. package/rules/skills/spec-driven-development/data/weasel-words.json +22 -0
  301. package/rules/spec-driven-development.md +69 -0
  302. package/rules/tdd-sdd.md +127 -0
  303. package/rules/workflow-sdd.md +56 -0
  304. package/schema/sdd.config.schema.json +104 -0
@@ -0,0 +1,100 @@
1
+ import { appendDiagnostic, emptyReport, } from "../domain/Diagnostic.js";
2
+ import { lintRecordsFromMarkdown } from "../domain/SpecParser.js";
3
+ import { applicabilityRequiredRule, approvalRecordRules, assumptionDowngradeApprovalRule, baselineVersionRequiredRule, boundaryConcurrencyModelRule, boundaryPolicyRefRule, dataScopeRequiredRule, debtBudgetFormRule, deprecatedFieldsRequiredRule, fieldTypeRules, generatedArtifactSurfaceRefRule, lifecycleStatusRules, migrationCrossPartitionRule, migrationEnforcementStageRule, openQBlockingRule, partitionDefaultPolicySetRule, REQUIRED_PARTITION_SECTIONS, sectionViolations, testObligationRules, weaselFindings, } from "../domain/Rules.js";
4
+ import { reachableBoundaryIds } from "../../../shared/domain/BoundaryReachability.js";
5
+ import { fileInGlobs } from "../../../shared/domain/GlobMatch.js";
6
+ export async function runLint(cwd, ports) {
7
+ const config = await ports.config.config(cwd);
8
+ const entries = await ports.files.resolveSpecFiles(cwd, config.lint.specFiles);
9
+ let report = emptyReport();
10
+ for (const entry of entries) {
11
+ report = lintFileInto(report, entry, config.lint.approverBlocklist, config.lint.partitionGlob);
12
+ }
13
+ return report;
14
+ }
15
+ function lintFileInto(report, entry, approverBlocklist, partitionGlob) {
16
+ let next = report;
17
+ /*
18
+ * §2 — section presence. When `lint.partition_glob` is configured, the §2
19
+ * structure check applies to files whose path matches a glob (OQ-011 / BEH-052);
20
+ * otherwise it falls back to heading-based detection ("## 1. Context").
21
+ */
22
+ const isPartitionFile = partitionGlob.length > 0
23
+ ? fileInGlobs(entry.path, partitionGlob)
24
+ : looksLikePartitionFile(entry.content);
25
+ if (isPartitionFile) {
26
+ for (const v of sectionViolations(entry.content)) {
27
+ next = appendDiagnostic(next, {
28
+ severity: "error",
29
+ rule: v.rule,
30
+ file: entry.path,
31
+ message: v.message,
32
+ });
33
+ }
34
+ }
35
+ /*
36
+ * ID-level rules + weasel scan share the parsed records (P0.5: weasel
37
+ * pass 2 needs them for field-aware modal-verb detection).
38
+ */
39
+ const records = lintRecordsFromMarkdown(entry.path, entry.content);
40
+ const boundaryIds = reachableBoundaryIds(records);
41
+ /*
42
+ * §5.1 — weasel words in normative sections (Pass 1) + modal verbs in
43
+ * normative fields (Pass 2, P0.5).
44
+ */
45
+ for (const w of weaselFindings(entry.content, records)) {
46
+ const where = w.field !== undefined
47
+ ? `normative field ${w.field}`
48
+ : `normative section "${w.section}"`;
49
+ next = appendDiagnostic(next, {
50
+ severity: "error",
51
+ rule: "sdd:weasel-word",
52
+ file: entry.path,
53
+ line: w.line,
54
+ message: `Banned phrase "${w.word}" in ${where} (SDD §5.1).`,
55
+ });
56
+ }
57
+ for (const rec of records) {
58
+ for (const d of [
59
+ ...lifecycleStatusRules(rec),
60
+ ...approvalRecordRules(rec),
61
+ ...testObligationRules(rec),
62
+ ...fieldTypeRules(rec),
63
+ /* P1 (ENF-003/009/010/011/012) */
64
+ ...baselineVersionRequiredRule(rec),
65
+ ...deprecatedFieldsRequiredRule(rec),
66
+ ...assumptionDowngradeApprovalRule(rec, approverBlocklist),
67
+ ...partitionDefaultPolicySetRule(rec),
68
+ ...generatedArtifactSurfaceRefRule(rec),
69
+ /* P2.1 — boundary requiredness (ENF-013/014/015/016) */
70
+ ...boundaryPolicyRefRule(rec, boundaryIds),
71
+ ...boundaryConcurrencyModelRule(rec, boundaryIds),
72
+ ...applicabilityRequiredRule(rec, boundaryIds),
73
+ ...dataScopeRequiredRule(rec, boundaryIds),
74
+ /* P2.2 — migration consistency (ENF-017/018) */
75
+ ...migrationEnforcementStageRule(rec, records),
76
+ ...migrationCrossPartitionRule(rec),
77
+ /* P3.1 — debt budget form (ENF-020) */
78
+ ...debtBudgetFormRule(rec),
79
+ /* unresolved blocking Open-Q (ENF-059) */
80
+ ...openQBlockingRule(rec),
81
+ ]) {
82
+ next = appendDiagnostic(next, d);
83
+ }
84
+ }
85
+ return next;
86
+ }
87
+ function looksLikePartitionFile(markdown) {
88
+ const firstNumberedHeading = REQUIRED_PARTITION_SECTIONS[0];
89
+ const re = new RegExp(`^##\\s+${firstNumberedHeading.replace(/\./g, "\\.").replace(/ /g, "\\s+")}`, "m");
90
+ return re.test(markdown);
91
+ }
92
+ export function diagnosticsByFile(report) {
93
+ const map = new Map();
94
+ for (const d of report.diagnostics) {
95
+ const list = map.get(d.file) ?? [];
96
+ list.push(d);
97
+ map.set(d.file, list);
98
+ }
99
+ return map;
100
+ }
@@ -0,0 +1 @@
1
+ export { appendDiagnostic, emptyReport, type Diagnostic, type DiagnosticSeverity, type LintReport, } from "../../../shared/domain/LintReport.js";
@@ -0,0 +1,2 @@
1
+ /* Re-export shim — content lives in src/shared/domain/LintReport.ts. */
2
+ export { appendDiagnostic, emptyReport, } from "../../../shared/domain/LintReport.js";
@@ -0,0 +1 @@
1
+ export { NORMATIVE_TEMPLATES, VALID_LIFECYCLE_STATUS, type LifecycleStatus, type LintRecord, type LintTemplate, } from "../../../shared/domain/SpecRecord.js";
@@ -0,0 +1,5 @@
1
+ /*
2
+ * INV-004 / CST-003: re-export shim over src/shared/domain/SpecRecord.ts so
3
+ * lint and ready share one shape without a cross-feature import. See spec.
4
+ */
5
+ export { NORMATIVE_TEMPLATES, VALID_LIFECYCLE_STATUS, } from "../../../shared/domain/SpecRecord.js";
@@ -0,0 +1 @@
1
+ export { applicabilityRequiredRule, approvalRecordRules, assumptionDowngradeApprovalRule, baselineVersionRequiredRule, boundaryConcurrencyModelRule, boundaryPolicyRefRule, dataScopeRequiredRule, deprecatedFieldsRequiredRule, debtBudgetFormRule, fieldTypeRules, generatedArtifactSurfaceRefRule, lifecycleStatusRules, migrationCrossPartitionRule, migrationEnforcementStageRule, NORMATIVE_SECTIONS, openQBlockingRule, partitionDefaultPolicySetRule, REQUIRED_PARTITION_SECTIONS, sectionViolations, testObligationRules, WEASEL_ABSOLUTE, WEASEL_MODAL_IN_NORMATIVE, WEASEL_WORDS, weaselFindings, type SectionViolation, type WeaselFinding, } from "../../../shared/domain/LintRules.js";
@@ -0,0 +1,2 @@
1
+ /* Re-export shim — content lives in src/shared/domain/LintRules.ts. */
2
+ export { applicabilityRequiredRule, approvalRecordRules, assumptionDowngradeApprovalRule, baselineVersionRequiredRule, boundaryConcurrencyModelRule, boundaryPolicyRefRule, dataScopeRequiredRule, deprecatedFieldsRequiredRule, debtBudgetFormRule, fieldTypeRules, generatedArtifactSurfaceRefRule, lifecycleStatusRules, migrationCrossPartitionRule, migrationEnforcementStageRule, NORMATIVE_SECTIONS, openQBlockingRule, partitionDefaultPolicySetRule, REQUIRED_PARTITION_SECTIONS, sectionViolations, testObligationRules, WEASEL_ABSOLUTE, WEASEL_MODAL_IN_NORMATIVE, WEASEL_WORDS, weaselFindings, } from "../../../shared/domain/LintRules.js";
@@ -0,0 +1 @@
1
+ export { lintRecordsFromMarkdown } from "../../../shared/domain/SpecRecord.js";
@@ -0,0 +1,2 @@
1
+ /* Re-export shim — content lives in src/shared/domain/SpecRecord.ts. */
2
+ export { lintRecordsFromMarkdown } from "../../../shared/domain/SpecRecord.js";
@@ -0,0 +1,4 @@
1
+ import type { CommandResult, OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ export interface LintCommand {
3
+ execute(cwd: string, format: Exclude<OutputFormat, "yaml">): Promise<CommandResult>;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { SddConfig } from "../../../../shared/domain/Config.js";
2
+ export interface LintConfigPort {
3
+ config(repoRoot: string): Promise<SddConfig>;
4
+ }
@@ -0,0 +1,10 @@
1
+ export interface SpecFileEntry {
2
+ /** Path relative to repoRoot. */
3
+ path: string;
4
+ /** UTF-8 markdown content. */
5
+ content: string;
6
+ }
7
+ export interface LintFileReader {
8
+ /** Resolve glob patterns to a deduplicated list of {path, content} entries. */
9
+ resolveSpecFiles(repoRoot: string, patterns: readonly string[]): Promise<SpecFileEntry[]>;
10
+ }
@@ -0,0 +1,8 @@
1
+ import { type CommandResult, type OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ import { type ShowPlanPorts } from "../../application/ShowPlan.js";
3
+ import type { PlanShowCommand, PlanShowRequest } from "../../ports/inbound/PlanShowCommand.js";
4
+ export declare class CliPlanShowHandler implements PlanShowCommand {
5
+ private readonly ports;
6
+ constructor(ports: ShowPlanPorts);
7
+ execute(cwd: string, req: PlanShowRequest, format: Exclude<OutputFormat, "yaml">): Promise<CommandResult>;
8
+ }
@@ -0,0 +1,73 @@
1
+ import { failed, ok, } from "../../../../shared/domain/CliOutput.js";
2
+ import { CliFailure } from "../../../../shared/domain/Errors.js";
3
+ import { showPlan } from "../../application/ShowPlan.js";
4
+ export class CliPlanShowHandler {
5
+ ports;
6
+ constructor(ports) {
7
+ this.ports = ports;
8
+ }
9
+ async execute(cwd, req, format) {
10
+ try {
11
+ const lookup = await showPlan(cwd, req.planId, this.ports);
12
+ return renderLookup(lookup, format);
13
+ }
14
+ catch (error) {
15
+ if (error instanceof CliFailure) {
16
+ return failed(error, format);
17
+ }
18
+ throw error;
19
+ }
20
+ }
21
+ }
22
+ function renderLookup(lookup, format) {
23
+ if (lookup.kind === "found") {
24
+ if (format === "json") {
25
+ return ok(JSON.stringify({
26
+ format_version: 1,
27
+ ok: true,
28
+ plan: {
29
+ plan_id: lookup.plan.planId,
30
+ created_at: lookup.plan.createdAt,
31
+ pending_attestations: lookup.plan.pendingAttestations.map((a) => ({
32
+ id: a.id,
33
+ owner_role: a.ownerRole,
34
+ approver_identity: a.approverIdentity,
35
+ timestamp: a.timestamp,
36
+ change_request: a.changeRequest,
37
+ scope: a.scope,
38
+ target_status: a.targetStatus,
39
+ ...(a.reviewedTestOracle !== undefined
40
+ ? { reviewed_test_oracle: a.reviewedTestOracle }
41
+ : {}),
42
+ })),
43
+ },
44
+ }));
45
+ }
46
+ const lines = [];
47
+ lines.push(`plan_id: ${lookup.plan.planId}`);
48
+ lines.push(`created_at: ${lookup.plan.createdAt}`);
49
+ lines.push(`source: ${lookup.sourcePath}`);
50
+ lines.push(`pending_attestations: ${lookup.plan.pendingAttestations.length}`);
51
+ for (const a of lookup.plan.pendingAttestations) {
52
+ lines.push(` - ${a.id} → ${a.targetStatus} (approver=${a.approverIdentity}, role=${a.ownerRole})`);
53
+ }
54
+ return ok(lines.join("\n"));
55
+ }
56
+ if (lookup.kind === "no-active-plan") {
57
+ return refusal({ format_version: 1, ok: false, kind: "no-active-plan" }, "no active plan", format);
58
+ }
59
+ return refusal({
60
+ format_version: 1,
61
+ ok: false,
62
+ kind: "invalid-plan-file",
63
+ plan_id: lookup.planId,
64
+ source: lookup.sourcePath,
65
+ reason: lookup.reason,
66
+ }, `invalid plan file at ${lookup.sourcePath}: ${lookup.reason}`, format);
67
+ }
68
+ function refusal(envelope, human, format) {
69
+ if (format === "json") {
70
+ return { exitCode: 2, stdout: `${JSON.stringify(envelope)}\n`, stderr: "" };
71
+ }
72
+ return { exitCode: 2, stdout: "", stderr: `plan show: ${human}\n` };
73
+ }
@@ -0,0 +1,7 @@
1
+ import { type SddConfig } from "../../../../shared/domain/Config.js";
2
+ import type { PlanConfigPort } from "../../ports/outbound/PlanConfigPort.js";
3
+ import type { PlanLookup, PlanReader } from "../../ports/outbound/PlanReader.js";
4
+ export declare class NodePlanReader implements PlanReader, PlanConfigPort {
5
+ config(repoRoot: string): Promise<SddConfig>;
6
+ read(repoRoot: string, plansDir: string, planId: string | undefined): Promise<PlanLookup>;
7
+ }
@@ -0,0 +1,68 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { resolve, join } from "node:path";
3
+ import { parse as yamlParse } from "yaml";
4
+ import { configFromJson, } from "../../../../shared/domain/Config.js";
5
+ import { errorMessage } from "../../../../shared/domain/Errors.js";
6
+ import { parsePlanFile } from "../../../../shared/domain/PlanFile.js";
7
+ export class NodePlanReader {
8
+ async config(repoRoot) {
9
+ const configPath = join(repoRoot, ".sdd", "config.json");
10
+ const text = await fs.readFile(configPath, "utf8");
11
+ return configFromJson(JSON.parse(text), configPath);
12
+ }
13
+ async read(repoRoot, plansDir, planId) {
14
+ const dir = resolve(repoRoot, plansDir);
15
+ const id = planId ?? (await readActiveMarker(dir));
16
+ if (id === null) {
17
+ return { kind: "no-active-plan" };
18
+ }
19
+ const sourcePath = join(plansDir, `${id}.yaml`);
20
+ const absolute = resolve(repoRoot, sourcePath);
21
+ let raw;
22
+ try {
23
+ raw = await fs.readFile(absolute, "utf8");
24
+ }
25
+ catch {
26
+ return {
27
+ kind: "invalid-plan-file",
28
+ planId: id,
29
+ sourcePath,
30
+ reason: "plan file not found",
31
+ };
32
+ }
33
+ let parsed;
34
+ try {
35
+ parsed = yamlParse(raw);
36
+ }
37
+ catch (e) {
38
+ return {
39
+ kind: "invalid-plan-file",
40
+ planId: id,
41
+ sourcePath,
42
+ reason: `YAML parse error: ${errorMessage(e)}`,
43
+ };
44
+ }
45
+ try {
46
+ const plan = parsePlanFile(parsed);
47
+ return { kind: "found", planId: plan.planId, plan, sourcePath };
48
+ }
49
+ catch (e) {
50
+ return {
51
+ kind: "invalid-plan-file",
52
+ planId: id,
53
+ sourcePath,
54
+ reason: errorMessage(e),
55
+ };
56
+ }
57
+ }
58
+ }
59
+ async function readActiveMarker(dir) {
60
+ try {
61
+ const raw = await fs.readFile(join(dir, ".active"), "utf8");
62
+ const id = raw.trim();
63
+ return id.length > 0 ? id : null;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
@@ -0,0 +1,7 @@
1
+ import type { PlanConfigPort } from "../ports/outbound/PlanConfigPort.js";
2
+ import type { PlanLookup, PlanReader } from "../ports/outbound/PlanReader.js";
3
+ export interface ShowPlanPorts {
4
+ config: PlanConfigPort;
5
+ reader: PlanReader;
6
+ }
7
+ export declare function showPlan(cwd: string, planId: string | undefined, ports: ShowPlanPorts): Promise<PlanLookup>;
@@ -0,0 +1,4 @@
1
+ export async function showPlan(cwd, planId, ports) {
2
+ const config = await ports.config.config(cwd);
3
+ return ports.reader.read(cwd, config.plansDir, planId);
4
+ }
@@ -0,0 +1,7 @@
1
+ import type { CommandResult, OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ export interface PlanShowRequest {
3
+ planId?: string;
4
+ }
5
+ export interface PlanShowCommand {
6
+ execute(cwd: string, req: PlanShowRequest, format: Exclude<OutputFormat, "yaml">): Promise<CommandResult>;
7
+ }
@@ -0,0 +1,4 @@
1
+ import type { SddConfig } from "../../../../shared/domain/Config.js";
2
+ export interface PlanConfigPort {
3
+ config(repoRoot: string): Promise<SddConfig>;
4
+ }
@@ -0,0 +1,19 @@
1
+ import type { PlanFileShape } from "../../../../shared/domain/PlanFile.js";
2
+ export type PlanLookup = {
3
+ kind: "found";
4
+ planId: string;
5
+ plan: PlanFileShape;
6
+ sourcePath: string;
7
+ } | {
8
+ kind: "no-active-plan";
9
+ } | {
10
+ kind: "invalid-plan-file";
11
+ planId: string;
12
+ sourcePath: string;
13
+ reason: string;
14
+ };
15
+ export interface PlanReader {
16
+ /** Reads and parses the requested plan, or the active plan if `planId` is
17
+ * undefined. Returns a discriminated outcome — never throws. */
18
+ read(repoRoot: string, plansDir: string, planId: string | undefined): Promise<PlanLookup>;
19
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import type { CommandResult, OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ import { type RunReadyPorts } from "../../application/RunReady.js";
3
+ import type { ReadyCommand } from "../../ports/inbound/ReadyCommand.js";
4
+ export declare class CliReadyHandler implements ReadyCommand {
5
+ private readonly ports;
6
+ constructor(ports: RunReadyPorts);
7
+ execute(cwd: string, format: Exclude<OutputFormat, "yaml">, partition?: string, against?: string): Promise<CommandResult>;
8
+ }
@@ -0,0 +1,79 @@
1
+ import { runReady, } from "../../application/RunReady.js";
2
+ export class CliReadyHandler {
3
+ ports;
4
+ constructor(ports) {
5
+ this.ports = ports;
6
+ }
7
+ async execute(cwd, format, partition, against) {
8
+ let envelope;
9
+ try {
10
+ envelope = await runReady(cwd, { partitionFilter: partition, against }, this.ports);
11
+ }
12
+ catch (error) {
13
+ /*
14
+ * Anything not converted into a ReadyError by RunReady itself is an
15
+ * internal evaluate-failure (exit 2).
16
+ */
17
+ const message = error instanceof Error ? error.message : String(error);
18
+ envelope = {
19
+ ok: false,
20
+ error: { kind: "internal", message },
21
+ violations: [],
22
+ advisories: [],
23
+ };
24
+ }
25
+ const exitCode = envelope.error !== null ? 2 : envelope.violations.length > 0 ? 1 : 0;
26
+ if (format === "json") {
27
+ return { exitCode, stdout: `${JSON.stringify(envelope)}\n`, stderr: "" };
28
+ }
29
+ return humanResult(envelope, exitCode);
30
+ }
31
+ }
32
+ function humanResult(envelope, exitCode) {
33
+ if (envelope.error !== null) {
34
+ return {
35
+ exitCode,
36
+ stdout: "",
37
+ stderr: `${envelope.error.kind}: ${envelope.error.message}${envelope.error.file !== undefined ? ` (${envelope.error.file})` : ""}\n`,
38
+ };
39
+ }
40
+ const lines = [];
41
+ for (const v of envelope.violations) {
42
+ lines.push(formatViolationLine(v));
43
+ }
44
+ for (const a of envelope.advisories) {
45
+ lines.push(`[advisory:${a.kind}] ${a.file}:${a.line} ${truncate(a.remediation, 200)}`);
46
+ }
47
+ if (envelope.violations.length === 0) {
48
+ lines.push("sdd ready: 0 violation(s).");
49
+ if (envelope.advisories.length > 0) {
50
+ lines.push(`sdd ready: ${envelope.advisories.length} advisory(ies).`);
51
+ }
52
+ return { exitCode: 0, stdout: `${lines.join("\n")}\n`, stderr: "" };
53
+ }
54
+ lines.push("");
55
+ lines.push(`sdd ready: ${envelope.violations.length} violation(s).`);
56
+ return { exitCode, stdout: `${lines.join("\n")}\n`, stderr: "" };
57
+ }
58
+ function formatViolationLine(v) {
59
+ const where = v.file !== undefined
60
+ ? `${v.file}${v.line !== undefined ? `:${v.line}` : ""}`
61
+ : "(no-file)";
62
+ const ctx = v.id ?? v.partition ?? "";
63
+ const remediation = truncate(v.remediation ?? defaultRemediation(v), 200);
64
+ return `[${v.kind}] ${where} ${ctx}: ${remediation}`;
65
+ }
66
+ function defaultRemediation(v) {
67
+ if (v.kind === "removed_compat_action_mismatch" &&
68
+ v.expected !== undefined &&
69
+ v.actual !== undefined) {
70
+ return `expected compatibility_action=${v.expected}, found ${v.actual}`;
71
+ }
72
+ return "(see CTR-014)";
73
+ }
74
+ function truncate(value, max) {
75
+ if (value.length <= max) {
76
+ return value;
77
+ }
78
+ return `${value.slice(0, max - 1)}…`;
79
+ }
@@ -0,0 +1,9 @@
1
+ import type { ReadyGitPort } from "../../ports/outbound/ReadyGitPort.js";
2
+ export declare class ChildProcessReadyGit implements ReadyGitPort {
3
+ isGitRepo(cwd: string): Promise<boolean>;
4
+ repoRoot(cwd: string): Promise<string>;
5
+ treeBytes(repoRoot: string, scope: readonly string[]): Promise<Uint8Array>;
6
+ treePaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
7
+ dirtyPaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
8
+ readAtRef(repoRoot: string, ref: string, relativePath: string): Promise<string | null>;
9
+ }
@@ -0,0 +1,113 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ export class ChildProcessReadyGit {
5
+ async isGitRepo(cwd) {
6
+ try {
7
+ const result = await runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
8
+ return (result.code === 0 && result.stdout.toString("utf8").trim() === "true");
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ repoRoot(cwd) {
15
+ let current = resolve(cwd);
16
+ while (true) {
17
+ if (existsSync(join(current, ".git"))) {
18
+ return Promise.resolve(current);
19
+ }
20
+ const parent = dirname(current);
21
+ if (parent === current) {
22
+ return Promise.reject(new Error("not-a-git-repo"));
23
+ }
24
+ current = parent;
25
+ }
26
+ }
27
+ async treeBytes(repoRoot, scope) {
28
+ const result = await runGit(repoRoot, ["ls-tree", "HEAD", "--", ...scope]);
29
+ if (result.code !== 0) {
30
+ throw new Error(result.stderr.toString("utf8").trim() || "git ls-tree failed");
31
+ }
32
+ return result.stdout;
33
+ }
34
+ async treePaths(repoRoot, scope) {
35
+ const result = await runGit(repoRoot, [
36
+ "ls-tree",
37
+ "-r",
38
+ "--name-only",
39
+ "HEAD",
40
+ "--",
41
+ ...scope,
42
+ ]);
43
+ if (result.code !== 0) {
44
+ throw new Error(result.stderr.toString("utf8").trim() || "git ls-tree failed");
45
+ }
46
+ return nonEmptyLines(result.stdout.toString("utf8"));
47
+ }
48
+ async dirtyPaths(repoRoot, scope) {
49
+ const diffResult = await runGit(repoRoot, [
50
+ "diff",
51
+ "--quiet",
52
+ "HEAD",
53
+ "--",
54
+ ...scope,
55
+ ]);
56
+ if (diffResult.code !== 0 && diffResult.code !== 1) {
57
+ throw new Error(diffResult.stderr.toString("utf8").trim() || "git diff --quiet failed");
58
+ }
59
+ const result = await runGit(repoRoot, [
60
+ "status",
61
+ "--porcelain",
62
+ "--",
63
+ ...scope,
64
+ ]);
65
+ if (result.code !== 0) {
66
+ throw new Error(result.stderr.toString("utf8").trim() || "git status failed");
67
+ }
68
+ return nonEmptyLines(result.stdout.toString("utf8"))
69
+ .map((line) => porcelainPath(line))
70
+ .sort();
71
+ }
72
+ async readAtRef(repoRoot, ref, relativePath) {
73
+ const result = await runGit(repoRoot, ["show", `${ref}:${relativePath}`]);
74
+ if (result.code !== 0) {
75
+ return null;
76
+ }
77
+ return result.stdout.toString("utf8");
78
+ }
79
+ }
80
+ function runGit(cwd, args) {
81
+ return new Promise((resolvePromise, reject) => {
82
+ const child = spawn("git", [...args], {
83
+ cwd,
84
+ stdio: ["ignore", "pipe", "pipe"],
85
+ });
86
+ const stdout = [];
87
+ const stderr = [];
88
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
89
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
90
+ child.on("error", (error) => {
91
+ if (error.code === "ENOENT") {
92
+ reject(new Error("git binary is not on PATH"));
93
+ return;
94
+ }
95
+ reject(error);
96
+ });
97
+ child.on("close", (code) => {
98
+ resolvePromise({
99
+ code: code ?? 1,
100
+ stdout: Buffer.concat(stdout),
101
+ stderr: Buffer.concat(stderr),
102
+ });
103
+ });
104
+ });
105
+ }
106
+ function nonEmptyLines(value) {
107
+ return value.split(/\r?\n/).filter((line) => line.length > 0);
108
+ }
109
+ function porcelainPath(line) {
110
+ const raw = line.slice(3);
111
+ const parts = raw.split(" -> ");
112
+ return parts[parts.length - 1];
113
+ }
@@ -0,0 +1,8 @@
1
+ import { type SddConfig } from "../../../../shared/domain/Config.js";
2
+ import type { ReadyConfigPort } from "../../ports/outbound/ReadyConfigPort.js";
3
+ import type { ReadyFileReader, SpecFileEntry, TestFileEntry } from "../../ports/outbound/ReadyFileReader.js";
4
+ export declare class NodeReadyFileSystem implements ReadyConfigPort, ReadyFileReader {
5
+ config(repoRoot: string): Promise<SddConfig>;
6
+ resolveSpecFiles(repoRoot: string, patterns: readonly string[]): Promise<SpecFileEntry[]>;
7
+ resolveTestFiles(repoRoot: string, patterns: readonly string[]): Promise<TestFileEntry[]>;
8
+ }