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,84 @@
1
+ import { findMatches } from "../../../shared/domain/SpecApprovalRewrite.js";
2
+ import { lintRecordsFromMarkdown } from "../../../shared/domain/SpecRecord.js";
3
+ import { inspectBody, normalizeBody } from "../domain/RecordBody.js";
4
+ import { isProtectedStatus, } from "../domain/RecordWrite.js";
5
+ export async function addRecord(cwd, afterId, rawBody, ports) {
6
+ const body = normalizeBody(rawBody);
7
+ const facts = inspectBody(body);
8
+ if (facts.id === null) {
9
+ return invalid("body has no id or does not parse as YAML");
10
+ }
11
+ if (isProtectedStatus(facts.status)) {
12
+ return governed(`refusing to add a record with lifecycle.status ${facts.status}; add as draft/proposed`);
13
+ }
14
+ const config = await ports.config.config(cwd);
15
+ const entries = await ports.files.resolveSpecFiles(cwd, config.lint.specFiles);
16
+ for (const entry of entries) {
17
+ if (lintRecordsFromMarkdown(entry.path, entry.content).some((r) => r.id === facts.id)) {
18
+ return duplicate(`id already exists: ${facts.id}`);
19
+ }
20
+ }
21
+ const anchors = locate(entries, afterId);
22
+ if (anchors.length === 0) {
23
+ return notFound(`anchor not found: ${afterId}`);
24
+ }
25
+ if (anchors.length > 1) {
26
+ return ambiguous(`anchor id is ambiguous: ${afterId} matches ${anchors.length} records`);
27
+ }
28
+ const anchor = anchors[0];
29
+ const lines = anchor.entry.content.split(/\r?\n/);
30
+ let fenceClose = -1;
31
+ for (let i = anchor.endLine - 1; i < lines.length; i++) {
32
+ if (/^```\s*$/.test(lines[i])) {
33
+ fenceClose = i;
34
+ break;
35
+ }
36
+ }
37
+ if (fenceClose === -1) {
38
+ return invalid(`could not locate the fence enclosing anchor ${afterId}`);
39
+ }
40
+ const bodyLines = body.split("\n");
41
+ const block = ["", "```yaml", "---", ...bodyLines, "---", "```"];
42
+ const newLines = [
43
+ ...lines.slice(0, fenceClose + 1),
44
+ ...block,
45
+ ...lines.slice(fenceClose + 1),
46
+ ];
47
+ await ports.writer.writeSpecFile(cwd, anchor.entry.path, newLines.join("\n"));
48
+ const startLine = fenceClose +
49
+ 1 +
50
+ 3 +
51
+ 1; /* ``` (1-based) + blank + ```yaml + --- → first body line */
52
+ return {
53
+ ok: true,
54
+ action: "add",
55
+ id: facts.id,
56
+ file: anchor.entry.path,
57
+ startLine,
58
+ endLine: startLine + bodyLines.length,
59
+ };
60
+ }
61
+ function locate(entries, id) {
62
+ const hits = [];
63
+ for (const entry of entries) {
64
+ for (const m of findMatches(entry.content.split(/\r?\n/), id)) {
65
+ hits.push({ entry, startLine: m.startLine, endLine: m.endLine });
66
+ }
67
+ }
68
+ return hits;
69
+ }
70
+ function invalid(message) {
71
+ return { ok: false, exitCode: 2, reason: "invalid-body", message };
72
+ }
73
+ function notFound(message) {
74
+ return { ok: false, exitCode: 1, reason: "anchor-not-found", message };
75
+ }
76
+ function ambiguous(message) {
77
+ return { ok: false, exitCode: 1, reason: "record-ambiguous", message };
78
+ }
79
+ function duplicate(message) {
80
+ return { ok: false, exitCode: 1, reason: "duplicate-id", message };
81
+ }
82
+ function governed(message) {
83
+ return { ok: false, exitCode: 1, reason: "record-protected", message };
84
+ }
@@ -0,0 +1,8 @@
1
+ import type { RecordSlice } from "../domain/RecordSlice.js";
2
+ import type { RecordConfigPort } from "../ports/outbound/RecordConfigPort.js";
3
+ import type { RecordFileReader } from "../ports/outbound/RecordFileReader.js";
4
+ export type { RecordSlice };
5
+ export declare function getRecord(cwd: string, id: string, ports: {
6
+ config: RecordConfigPort;
7
+ files: RecordFileReader;
8
+ }): Promise<RecordSlice[]>;
@@ -0,0 +1,22 @@
1
+ import { findMatches } from "../../../shared/domain/SpecApprovalRewrite.js";
2
+ export async function getRecord(cwd, id, ports) {
3
+ const config = await ports.config.config(cwd);
4
+ const entries = await ports.files.resolveSpecFiles(cwd, config.lint.specFiles);
5
+ const out = [];
6
+ for (const entry of entries) {
7
+ const lines = entry.content.split(/\r?\n/);
8
+ for (const match of findMatches(lines, id)) {
9
+ const raw = lines
10
+ .slice(match.startLine - 1, match.endLine - 1)
11
+ .join("\n");
12
+ out.push({
13
+ id: match.id,
14
+ file: entry.path,
15
+ startLine: match.startLine,
16
+ endLine: match.endLine,
17
+ raw,
18
+ });
19
+ }
20
+ }
21
+ return out;
22
+ }
@@ -0,0 +1,9 @@
1
+ import { type RecordSummary } from "../domain/RecordSummary.js";
2
+ import type { RecordConfigPort } from "../ports/outbound/RecordConfigPort.js";
3
+ import type { RecordFileReader } from "../ports/outbound/RecordFileReader.js";
4
+ export type { RecordSummary };
5
+ export interface RecordPorts {
6
+ config: RecordConfigPort;
7
+ files: RecordFileReader;
8
+ }
9
+ export declare function listRecords(cwd: string, ports: RecordPorts, partition?: string): Promise<RecordSummary[]>;
@@ -0,0 +1,24 @@
1
+ import { lintRecordsFromMarkdown } from "../../../shared/domain/SpecRecord.js";
2
+ import { partitionOf } from "../domain/RecordPartition.js";
3
+ import { titleOf } from "../domain/RecordSummary.js";
4
+ export async function listRecords(cwd, ports, partition) {
5
+ const config = await ports.config.config(cwd);
6
+ const entries = await ports.files.resolveSpecFiles(cwd, config.lint.specFiles);
7
+ const out = [];
8
+ for (const entry of entries) {
9
+ for (const rec of lintRecordsFromMarkdown(entry.path, entry.content)) {
10
+ if (partition !== undefined && partitionOf(rec.id) !== partition) {
11
+ continue;
12
+ }
13
+ out.push({
14
+ id: rec.id,
15
+ type: rec.template,
16
+ status: rec.lifecycleStatus,
17
+ title: titleOf(rec.parsed),
18
+ file: rec.file,
19
+ line: rec.line,
20
+ });
21
+ }
22
+ }
23
+ return out;
24
+ }
@@ -0,0 +1,11 @@
1
+ import { type RecordWriteResult } from "../domain/RecordWrite.js";
2
+ import type { RecordConfigPort } from "../ports/outbound/RecordConfigPort.js";
3
+ import type { RecordFileReader } from "../ports/outbound/RecordFileReader.js";
4
+ import type { RecordFileWriter } from "../ports/outbound/RecordFileWriter.js";
5
+ export interface RecordWritePorts {
6
+ config: RecordConfigPort;
7
+ files: RecordFileReader;
8
+ writer: RecordFileWriter;
9
+ }
10
+ export type { RecordWriteResult };
11
+ export declare function setRecord(cwd: string, id: string, rawBody: string, ports: RecordWritePorts): Promise<RecordWriteResult>;
@@ -0,0 +1,68 @@
1
+ import { findMatches } from "../../../shared/domain/SpecApprovalRewrite.js";
2
+ import { lintRecordsFromMarkdown } from "../../../shared/domain/SpecRecord.js";
3
+ import { inspectBody, normalizeBody } from "../domain/RecordBody.js";
4
+ import { isProtectedStatus, } from "../domain/RecordWrite.js";
5
+ export async function setRecord(cwd, id, rawBody, ports) {
6
+ const body = normalizeBody(rawBody);
7
+ const facts = inspectBody(body);
8
+ if (facts.id === null) {
9
+ return invalid("body has no id or does not parse as YAML");
10
+ }
11
+ if (facts.id !== id) {
12
+ return invalid(`body id (${facts.id}) does not match target id (${id})`);
13
+ }
14
+ if (isProtectedStatus(facts.status)) {
15
+ return governed(`refusing to set lifecycle.status ${facts.status}; promote via sdd approve + sdd finalize`);
16
+ }
17
+ const config = await ports.config.config(cwd);
18
+ const entries = await ports.files.resolveSpecFiles(cwd, config.lint.specFiles);
19
+ const hits = locate(entries, id);
20
+ if (hits.length === 0) {
21
+ return notFound(`record not found: ${id}`);
22
+ }
23
+ if (hits.length > 1) {
24
+ return ambiguous(`record id is ambiguous: ${id} matches ${hits.length} records`);
25
+ }
26
+ const hit = hits[0];
27
+ const current = lintRecordsFromMarkdown(hit.entry.path, hit.entry.content).find((r) => r.id === id);
28
+ if (current !== undefined && isProtectedStatus(current.lifecycleStatus)) {
29
+ return governed(`refusing to edit ${current.lifecycleStatus} record ${id}; use sdd approve + sdd finalize`);
30
+ }
31
+ const lines = hit.entry.content.split(/\r?\n/);
32
+ const bodyLines = body.split("\n");
33
+ const newLines = [
34
+ ...lines.slice(0, hit.startLine - 1),
35
+ ...bodyLines,
36
+ ...lines.slice(hit.endLine - 1),
37
+ ];
38
+ await ports.writer.writeSpecFile(cwd, hit.entry.path, newLines.join("\n"));
39
+ return {
40
+ ok: true,
41
+ action: "set",
42
+ id,
43
+ file: hit.entry.path,
44
+ startLine: hit.startLine,
45
+ endLine: hit.startLine + bodyLines.length,
46
+ };
47
+ }
48
+ function locate(entries, id) {
49
+ const hits = [];
50
+ for (const entry of entries) {
51
+ for (const m of findMatches(entry.content.split(/\r?\n/), id)) {
52
+ hits.push({ entry, startLine: m.startLine, endLine: m.endLine });
53
+ }
54
+ }
55
+ return hits;
56
+ }
57
+ function invalid(message) {
58
+ return { ok: false, exitCode: 2, reason: "invalid-body", message };
59
+ }
60
+ function notFound(message) {
61
+ return { ok: false, exitCode: 1, reason: "record-not-found", message };
62
+ }
63
+ function ambiguous(message) {
64
+ return { ok: false, exitCode: 1, reason: "record-ambiguous", message };
65
+ }
66
+ function governed(message) {
67
+ return { ok: false, exitCode: 1, reason: "record-protected", message };
68
+ }
@@ -0,0 +1,12 @@
1
+ /** Normalise a supplied record body to the bare YAML body (the form
2
+ * `sdd record get` emits): strip an enclosing ```yaml fence and any
3
+ * surrounding `---` document markers, then trim blank edges. */
4
+ export declare function normalizeBody(raw: string): string;
5
+ export interface BodyFacts {
6
+ id: string | null;
7
+ status: string | null;
8
+ }
9
+ /** Best-effort parse of a record body to its id and lifecycle status.
10
+ * Unparseable input yields `{ id: null, status: null }` so callers treat
11
+ * it as an invalid body. */
12
+ export declare function inspectBody(body: string): BodyFacts;
@@ -0,0 +1,66 @@
1
+ import { parseDocument } from "yaml";
2
+ /** Normalise a supplied record body to the bare YAML body (the form
3
+ * `sdd record get` emits): strip an enclosing ```yaml fence and any
4
+ * surrounding `---` document markers, then trim blank edges. */
5
+ export function normalizeBody(raw) {
6
+ let lines = raw.replace(/\r\n/g, "\n").split("\n");
7
+ lines = trimBlankEdges(lines);
8
+ if (lines.length > 0 && /^```yaml\s*$/.test(lines[0])) {
9
+ const close = lines.findIndex((l, i) => i > 0 && /^```\s*$/.test(l));
10
+ if (close !== -1) {
11
+ lines = trimBlankEdges(lines.slice(1, close));
12
+ }
13
+ }
14
+ if (lines.length > 0 && lines[0] === "---") {
15
+ lines = lines.slice(1);
16
+ }
17
+ if (lines.length > 0 && lines[lines.length - 1] === "---") {
18
+ lines = lines.slice(0, -1);
19
+ }
20
+ return trimBlankEdges(lines).join("\n");
21
+ }
22
+ /** Best-effort parse of a record body to its id and lifecycle status.
23
+ * Unparseable input yields `{ id: null, status: null }` so callers treat
24
+ * it as an invalid body. */
25
+ export function inspectBody(body) {
26
+ const doc = parseDocument(body, { prettyErrors: false });
27
+ if (doc.errors.length > 0) {
28
+ return { id: null, status: null };
29
+ }
30
+ const value = doc.toJS();
31
+ if (!isRecord(value)) {
32
+ return { id: null, status: null };
33
+ }
34
+ const obj = value;
35
+ const id = typeof obj.id === "string" ? obj.id : null;
36
+ const status = lifecycleStatus(obj);
37
+ return { id, status };
38
+ }
39
+ function lifecycleStatus(obj) {
40
+ const flat = obj["lifecycle.status"];
41
+ if (typeof flat === "string") {
42
+ return flat;
43
+ }
44
+ const lifecycle = obj.lifecycle;
45
+ if (isRecord(lifecycle)) {
46
+ const status = lifecycle.status;
47
+ if (typeof status === "string") {
48
+ return status;
49
+ }
50
+ }
51
+ return null;
52
+ }
53
+ function isRecord(value) {
54
+ return typeof value === "object" && value !== null && !Array.isArray(value);
55
+ }
56
+ function trimBlankEdges(lines) {
57
+ let start = 0;
58
+ let end = lines.length;
59
+ while (start < end && lines[start].trim().length === 0) {
60
+ start++;
61
+ }
62
+ while (end > start && lines[end - 1].trim().length === 0) {
63
+ end--;
64
+ }
65
+ return lines.slice(start, end);
66
+ }
@@ -0,0 +1 @@
1
+ export declare function partitionOf(id: string): string;
@@ -0,0 +1,7 @@
1
+ /* CST-007: partition component of a normative ID. The ID-tail has no `:`,
2
+ * so the rightmost colon splits `<partition>:<tail>`; a bare id is its own
3
+ * partition. See spec record. */
4
+ export function partitionOf(id) {
5
+ const idx = id.lastIndexOf(":");
6
+ return idx === -1 ? id : id.slice(0, idx);
7
+ }
@@ -0,0 +1,7 @@
1
+ export interface RecordSlice {
2
+ id: string;
3
+ file: string;
4
+ startLine: number;
5
+ endLine: number;
6
+ raw: string;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ export interface RecordSummary {
2
+ id: string;
3
+ type: string | null;
4
+ status: string | null;
5
+ title: string | null;
6
+ file: string;
7
+ line: number;
8
+ }
9
+ /** Derive a one-line display title for a record. Fixed fallback chain:
10
+ * the `title` field, else the `name` field (Surface records), else null. */
11
+ export declare function titleOf(parsed: Record<string, unknown>): string | null;
@@ -0,0 +1,13 @@
1
+ /** Derive a one-line display title for a record. Fixed fallback chain:
2
+ * the `title` field, else the `name` field (Surface records), else null. */
3
+ export function titleOf(parsed) {
4
+ const title = parsed.title;
5
+ if (typeof title === "string") {
6
+ return title;
7
+ }
8
+ const name = parsed.name;
9
+ if (typeof name === "string") {
10
+ return name;
11
+ }
12
+ return null;
13
+ }
@@ -0,0 +1,14 @@
1
+ export type RecordWriteResult = {
2
+ ok: true;
3
+ action: "set" | "add";
4
+ id: string;
5
+ file: string;
6
+ startLine: number;
7
+ endLine: number;
8
+ } | {
9
+ ok: false;
10
+ exitCode: 1 | 2;
11
+ reason: string;
12
+ message: string;
13
+ };
14
+ export declare function isProtectedStatus(status: string | null): boolean;
@@ -0,0 +1,8 @@
1
+ const PROTECTED_STATUSES = new Set([
2
+ "approved",
3
+ "deprecated",
4
+ "removed",
5
+ ]);
6
+ export function isProtectedStatus(status) {
7
+ return status !== null && PROTECTED_STATUSES.has(status);
8
+ }
@@ -0,0 +1,19 @@
1
+ import type { CommandResult, OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ export type RecordAction = {
3
+ kind: "list";
4
+ partition?: string;
5
+ } | {
6
+ kind: "get";
7
+ id: string;
8
+ } | {
9
+ kind: "set";
10
+ id: string;
11
+ body: string;
12
+ } | {
13
+ kind: "add";
14
+ afterId: string;
15
+ body: string;
16
+ };
17
+ export interface RecordCommand {
18
+ execute(cwd: string, action: RecordAction, format: Exclude<OutputFormat, "yaml">): Promise<CommandResult>;
19
+ }
@@ -0,0 +1,4 @@
1
+ import type { SddConfig } from "../../../../shared/domain/Config.js";
2
+ export interface RecordConfigPort {
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 RecordFileReader {
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,6 @@
1
+ export interface RecordFileWriter {
2
+ /** Atomically write `content` to the spec file at `relativePath` (relative
3
+ * to repoRoot). Must be a temp-write + rename so a crash never leaves a
4
+ * partial file. */
5
+ writeSpecFile(repoRoot: string, relativePath: string, content: string): Promise<void>;
6
+ }
@@ -0,0 +1,8 @@
1
+ import { type CommandResult, type OutputFormat } from "../../../../shared/domain/CliOutput.js";
2
+ import { type BuildRefreshStubsPorts } from "../../application/BuildRefreshStubs.js";
3
+ import type { RefreshCommand } from "../../ports/inbound/RefreshCommand.js";
4
+ export declare class CliRefreshHandler implements RefreshCommand {
5
+ private readonly ports;
6
+ constructor(ports: BuildRefreshStubsPorts);
7
+ execute(cwd: string, format: OutputFormat): Promise<CommandResult>;
8
+ }
@@ -0,0 +1,24 @@
1
+ import { failed, ok, } from "../../../../shared/domain/CliOutput.js";
2
+ import { CliFailure } from "../../../../shared/domain/Errors.js";
3
+ import { buildRefreshStubs, } from "../../application/BuildRefreshStubs.js";
4
+ export class CliRefreshHandler {
5
+ ports;
6
+ constructor(ports) {
7
+ this.ports = ports;
8
+ }
9
+ async execute(cwd, format) {
10
+ try {
11
+ const outcome = await buildRefreshStubs(cwd, format, this.ports);
12
+ if (outcome.kind === "json") {
13
+ return ok(JSON.stringify(outcome.body));
14
+ }
15
+ return ok(outcome.text);
16
+ }
17
+ catch (error) {
18
+ if (error instanceof CliFailure) {
19
+ return failed(error, format);
20
+ }
21
+ throw error;
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,8 @@
1
+ import type { RefreshGitPort } from "../../ports/outbound/RefreshGitPort.js";
2
+ export declare class ChildProcessRefreshGit implements RefreshGitPort {
3
+ repoRoot(cwd: string): Promise<string>;
4
+ headSha(repoRoot: string): Promise<string>;
5
+ treePaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
6
+ dirtyPaths(repoRoot: string, scope: readonly string[]): Promise<string[]>;
7
+ changedPaths(repoRoot: string, baselineCommitSha: string, scope: readonly string[]): Promise<string[]>;
8
+ }
@@ -0,0 +1,118 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { configFailure, environmentFailure, } from "../../../../shared/domain/Errors.js";
5
+ export class ChildProcessRefreshGit {
6
+ async repoRoot(cwd) {
7
+ const result = await runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
8
+ if (result.code !== 0 || result.stdout.toString("utf8").trim() !== "true") {
9
+ throw environmentFailure("not-a-git-repo", "cwd is not inside a git working tree", cwd);
10
+ }
11
+ return findRepoRoot(cwd);
12
+ }
13
+ async headSha(repoRoot) {
14
+ const result = await runGit(repoRoot, ["rev-parse", "HEAD"]);
15
+ if (result.code === 0) {
16
+ return result.stdout.toString("utf8").trim();
17
+ }
18
+ throw environmentFailure("head-unborn", "HEAD does not resolve to a commit", result.stderr.toString("utf8").trim());
19
+ }
20
+ async treePaths(repoRoot, scope) {
21
+ const result = await runGit(repoRoot, [
22
+ "ls-tree",
23
+ "-r",
24
+ "--name-only",
25
+ "HEAD",
26
+ "--",
27
+ ...scope,
28
+ ]);
29
+ if (result.code !== 0) {
30
+ throw environmentFailure("head-unborn", "git ls-tree failed", result.stderr.toString("utf8").trim());
31
+ }
32
+ return nonEmptyLines(result.stdout.toString("utf8"));
33
+ }
34
+ async dirtyPaths(repoRoot, scope) {
35
+ const diffResult = await runGit(repoRoot, [
36
+ "diff",
37
+ "--quiet",
38
+ "HEAD",
39
+ "--",
40
+ ...scope,
41
+ ]);
42
+ if (diffResult.code !== 0 && diffResult.code !== 1) {
43
+ throw environmentFailure("head-unborn", "git diff --quiet failed", diffResult.stderr.toString("utf8").trim());
44
+ }
45
+ const result = await runGit(repoRoot, [
46
+ "status",
47
+ "--porcelain",
48
+ "--",
49
+ ...scope,
50
+ ]);
51
+ if (result.code !== 0) {
52
+ throw environmentFailure("not-a-git-repo", "git status failed", result.stderr.toString("utf8").trim());
53
+ }
54
+ return nonEmptyLines(result.stdout.toString("utf8"))
55
+ .map((line) => porcelainPath(line))
56
+ .sort();
57
+ }
58
+ async changedPaths(repoRoot, baselineCommitSha, scope) {
59
+ const result = await runGit(repoRoot, [
60
+ "diff",
61
+ "--name-only",
62
+ `${baselineCommitSha}..HEAD`,
63
+ "--",
64
+ ...scope,
65
+ ]);
66
+ if (result.code !== 0) {
67
+ throw configFailure("config-invalid", `baseline_commit_sha does not resolve: ${baselineCommitSha}`, result.stderr.toString("utf8").trim());
68
+ }
69
+ return nonEmptyLines(result.stdout.toString("utf8")).sort();
70
+ }
71
+ }
72
+ function runGit(cwd, args) {
73
+ return new Promise((resolve, reject) => {
74
+ const child = spawn("git", [...args], {
75
+ cwd,
76
+ stdio: ["ignore", "pipe", "pipe"],
77
+ });
78
+ const stdout = [];
79
+ const stderr = [];
80
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
81
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
82
+ child.on("error", (error) => {
83
+ if (error.code === "ENOENT") {
84
+ reject(environmentFailure("git-not-on-path", "git binary is not on PATH"));
85
+ return;
86
+ }
87
+ reject(error);
88
+ });
89
+ child.on("close", (code) => {
90
+ resolve({
91
+ code: code ?? 1,
92
+ stdout: Buffer.concat(stdout),
93
+ stderr: Buffer.concat(stderr),
94
+ });
95
+ });
96
+ });
97
+ }
98
+ function nonEmptyLines(value) {
99
+ return value.split(/\r?\n/).filter((line) => line.length > 0);
100
+ }
101
+ function porcelainPath(line) {
102
+ const raw = line.slice(3);
103
+ const parts = raw.split(" -> ");
104
+ return parts[parts.length - 1];
105
+ }
106
+ function findRepoRoot(cwd) {
107
+ let current = resolve(cwd);
108
+ while (true) {
109
+ if (existsSync(join(current, ".git"))) {
110
+ return current;
111
+ }
112
+ const parent = dirname(current);
113
+ if (parent === current) {
114
+ throw environmentFailure("not-a-git-repo", "cwd is not inside a git working tree", cwd);
115
+ }
116
+ current = parent;
117
+ }
118
+ }
@@ -0,0 +1,7 @@
1
+ import { type SddConfig } from "../../../../shared/domain/Config.js";
2
+ import type { RefreshConfigPort } from "../../ports/outbound/RefreshConfigPort.js";
3
+ import type { RefreshSpec, RefreshSpecPort } from "../../ports/outbound/RefreshSpecPort.js";
4
+ export declare class NodeRefreshFileReader implements RefreshConfigPort, RefreshSpecPort {
5
+ config(repoRoot: string): Promise<SddConfig>;
6
+ spec(repoRoot: string, config: SddConfig): Promise<RefreshSpec>;
7
+ }