@use-the-fork/spindle 0.0.0-stage → 0.0.2

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 (245) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -2
  3. package/build/Assets/Blueprints/Catalog.js +110 -0
  4. package/build/Assets/Blueprints/agents/general.agent.md +67 -0
  5. package/build/Assets/Blueprints/agents/sdd-code-reviewer.agent.md +62 -0
  6. package/build/Assets/Blueprints/agents/sdd-code-slop-reviewer.agent.md +59 -0
  7. package/build/Assets/Blueprints/agents/sdd-implementer.agent.md +77 -0
  8. package/build/Assets/Blueprints/agents/sdd-orchestrator.agent.md +42 -0
  9. package/build/Assets/Blueprints/agents/sdd-re-review.agent.md +57 -0
  10. package/build/Assets/Blueprints/agents/sdd-reviewer.agent.md +89 -0
  11. package/build/Assets/Blueprints/agents/sdd-task-reviewer.agent.md +54 -0
  12. package/build/Assets/Blueprints/agents/shared-codebase-analyzer.agent.md +151 -0
  13. package/build/Assets/Blueprints/agents/shared-codebase-locator.agent.md +130 -0
  14. package/build/Assets/Blueprints/agents/shared-codebase-pattern-finder.agent.md +247 -0
  15. package/build/Assets/Blueprints/agents/shared-explore.agent.md +53 -0
  16. package/build/Assets/Blueprints/agents/shared-r.agent.md +17 -0
  17. package/build/Assets/Blueprints/agents/shared-rw.agent.md +17 -0
  18. package/build/Assets/Blueprints/agents/shared-rwx.agent.md +17 -0
  19. package/build/Assets/Blueprints/agents/shared-web-search-researcher.agent.md +122 -0
  20. package/build/Assets/Blueprints/instructions/code-commenting.instructions.md +39 -0
  21. package/build/Assets/Blueprints/instructions/source-architecture.instructions.md +15 -0
  22. package/build/Assets/Blueprints/skills/asd-ste100/CREDITS.MD +1 -0
  23. package/build/Assets/Blueprints/skills/asd-ste100/SKILL.md +83 -0
  24. package/build/Assets/Blueprints/skills/asd-ste100/examples/before-after.md +107 -0
  25. package/build/Assets/Blueprints/skills/asd-ste100/references/writing-rules.md +65 -0
  26. package/build/Assets/Blueprints/skills/author-agent/SKILL.md +26 -0
  27. package/build/Assets/Blueprints/skills/author-design/SKILL.md +38 -0
  28. package/build/Assets/Blueprints/skills/author-instruction/SKILL.md +28 -0
  29. package/build/Assets/Blueprints/skills/author-skill/SKILL.md +26 -0
  30. package/build/Assets/Blueprints/skills/grill/SKILL.md +33 -0
  31. package/build/Assets/Blueprints/skills/grill-design/SKILL.md +41 -0
  32. package/build/Assets/Blueprints/skills/grill-with-docs/SKILL.md +14 -0
  33. package/build/Assets/Blueprints/skills/implement/SKILL.md +79 -0
  34. package/build/Assets/Blueprints/skills/implement-spec/SKILL.md +34 -0
  35. package/build/Assets/Blueprints/skills/improve-codebase-architecture/HTML-REPORT.md +129 -0
  36. package/build/Assets/Blueprints/skills/improve-codebase-architecture/SKILL.md +74 -0
  37. package/build/Assets/Blueprints/skills/int-code-review/SKILL.md +76 -0
  38. package/build/Assets/Blueprints/skills/int-code-slop-review/SKILL.md +238 -0
  39. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/_sections.md +36 -0
  40. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/_template.md +42 -0
  41. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-closing-brace-labels.md +112 -0
  42. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-empty-docblocks.md +132 -0
  43. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-narration.md +118 -0
  44. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-placeholder.md +130 -0
  45. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-generic-catch.md +153 -0
  46. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-impossible-null.md +157 -0
  47. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-missing-real.md +157 -0
  48. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-generic-placeholders.md +120 -0
  49. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-over-descriptive.md +108 -0
  50. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-suffix-abuse.md +167 -0
  51. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-type-in-name.md +100 -0
  52. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-dependency-creep.md +114 -0
  53. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-premature-interface.md +136 -0
  54. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-single-method-class.md +161 -0
  55. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-useless-wrapper.md +140 -0
  56. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-as-any-escape.md +127 -0
  57. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-debug-artifacts.md +144 -0
  58. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-hyper-consistent.md +84 -0
  59. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-no-hack-scars.md +113 -0
  60. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-trivial-boilerplate.md +191 -0
  61. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-doesnt-throw.md +132 -0
  62. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-mirror-implementation.md +125 -0
  63. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-mock-everything.md +148 -0
  64. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-snapshot-abuse.md +131 -0
  65. package/build/Assets/Blueprints/skills/int-codebase-design/SKILL.md +116 -0
  66. package/build/Assets/Blueprints/skills/int-codebase-design/references/DEEPENING.md +37 -0
  67. package/build/Assets/Blueprints/skills/int-codebase-design/references/DESIGN-IT-TWICE.md +44 -0
  68. package/build/Assets/Blueprints/skills/int-domain-modeling/SKILL.md +78 -0
  69. package/build/Assets/Blueprints/skills/int-domain-modeling/references/ADR-FORMAT.md +47 -0
  70. package/build/Assets/Blueprints/skills/int-domain-modeling/references/GLOSSARY-FORMAT.md +60 -0
  71. package/build/Assets/Blueprints/skills/int-prototype/LOGIC.md +67 -0
  72. package/build/Assets/Blueprints/skills/int-prototype/SKILL.md +30 -0
  73. package/build/Assets/Blueprints/skills/int-prototype/UI.md +112 -0
  74. package/build/Assets/Blueprints/skills/int-retrospective/SKILL.md +88 -0
  75. package/build/Assets/Blueprints/skills/int-retrospective/assets/retrospective-prompt.md +38 -0
  76. package/build/Assets/Blueprints/skills/int-retrospective/references/assessment.md +56 -0
  77. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/SKILL.md +64 -0
  78. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/implementer-prompt.md +40 -0
  79. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/reviewer-prompt.md +43 -0
  80. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/aggregate-review.md +7 -0
  81. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/corrective-rounds.md +72 -0
  82. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/example-workflow.md +25 -0
  83. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/full-spec.md +72 -0
  84. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/implementer-corrections.md +9 -0
  85. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/review-inputs.md +26 -0
  86. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/verification-evidence.md +7 -0
  87. package/build/Assets/Blueprints/skills/int-test-driven-development/SKILL.md +51 -0
  88. package/build/Assets/Blueprints/skills/int-test-driven-development/references/mocking.md +31 -0
  89. package/build/Assets/Blueprints/skills/int-test-driven-development/references/tests.md +68 -0
  90. package/build/Assets/Blueprints/skills/int-testing-agent-guidance/SKILL.md +53 -0
  91. package/build/Assets/Blueprints/skills/int-verification-before-completion/SKILL.md +25 -0
  92. package/build/Assets/Blueprints/skills/int-writing-agents/SKILL.md +66 -0
  93. package/build/Assets/Blueprints/skills/int-writing-for-agents/SKILL.md +86 -0
  94. package/build/Assets/Blueprints/skills/int-writing-instruction/SKILL.md +57 -0
  95. package/build/Assets/Blueprints/skills/int-writing-skills/SKILL.md +129 -0
  96. package/build/Assets/Blueprints/skills/int-writing-skills/assets/interactive.md +83 -0
  97. package/build/Assets/Blueprints/skills/int-writing-skills/assets/reference.md +32 -0
  98. package/build/Assets/Blueprints/skills/int-writing-skills/references/arguments.md +50 -0
  99. package/build/Assets/Blueprints/skills/int-writing-skills/references/interactive-skills.md +53 -0
  100. package/build/Assets/Blueprints/skills/int-writing-skills/references/persuasion-principles.md +220 -0
  101. package/build/Assets/Blueprints/skills/int-writing-skills/references/reference-skills.md +20 -0
  102. package/build/Assets/Blueprints/skills/int-writing-skills/references/testing-skills.md +48 -0
  103. package/build/Assets/Blueprints/skills/retrospective/SKILL.md +16 -0
  104. package/build/Assets/Blueprints/skills/setup-project/SKILL.md +66 -0
  105. package/build/Assets/Blueprints/skills/to-spec/SKILL.md +53 -0
  106. package/build/Assets/Blueprints/skills/to-tickets/SKILL.md +49 -0
  107. package/build/Assets/Blueprints/skills/update-tooling/SKILL.md +86 -0
  108. package/build/Assets/Templates/Catalog.js +12 -0
  109. package/build/Assets/Templates/Catalog.ts +17 -0
  110. package/build/Assets/Templates/project/copilot-instructions.md +17 -0
  111. package/build/Assets/Templates/project/domain.md +51 -0
  112. package/build/Assets/Templates/project/ticket-tracker.md +33 -0
  113. package/build/Assets/Templates/sdd/design.md +25 -0
  114. package/build/Assets/Templates/sdd/spec.md +29 -0
  115. package/build/Assets/Templates/sdd/ticket.md +15 -0
  116. package/build/BlueprintManagement/BlueprintManagementServiceProvider.js +15 -0
  117. package/build/BlueprintManagement/Catalog/BlueprintCatalog.js +238 -0
  118. package/build/BlueprintManagement/Commands/BlueprintCommandOptions.js +9 -0
  119. package/build/BlueprintManagement/Commands/ConflictPolicy.js +6 -0
  120. package/build/BlueprintManagement/Commands/InitCommand.js +117 -0
  121. package/build/BlueprintManagement/Commands/Prompts.js +66 -0
  122. package/build/BlueprintManagement/Commands/UpdateCommand.js +86 -0
  123. package/build/BlueprintManagement/Handlers/Agents/AgentHandler.js +21 -0
  124. package/build/BlueprintManagement/Handlers/BlueprintHandlers.js +4 -0
  125. package/build/BlueprintManagement/Handlers/Instructions/InstructionHandler.js +21 -0
  126. package/build/BlueprintManagement/Handlers/Skills/SkillHandler.js +10 -0
  127. package/build/BlueprintManagement/Reconciliation/BlueprintCatalogPlanner.js +87 -0
  128. package/build/BlueprintManagement/Reconciliation/BlueprintRemovalCoordinator.js +78 -0
  129. package/build/BlueprintManagement/Services/BlueprintReconciliationService.js +391 -0
  130. package/build/BlueprintManagement/Services/InstallationService.js +54 -0
  131. package/build/BlueprintManagement/Support/BlueprintFiles.js +113 -0
  132. package/build/BlueprintManagement/Types/Blueprint.js +1 -0
  133. package/build/BlueprintManagement/Types/Installation.js +1 -0
  134. package/build/BlueprintManagement/Types/Reconciliation.js +1 -0
  135. package/build/BlueprintManagement/Versioning/BlueprintVersion.js +70 -0
  136. package/build/BlueprintManagement/Versioning/BlueprintVersioning.js +55 -0
  137. package/build/Commands/Command.js +3 -0
  138. package/build/Commands/CommandOptions.js +12 -0
  139. package/build/Config/Config.js +3 -0
  140. package/build/Foundation/Application.js +112 -0
  141. package/build/Foundation/ApplicationBuilder.js +23 -0
  142. package/build/Foundation/Bootstrap/Bootstrapper.js +2 -0
  143. package/build/Foundation/Bootstrap/DiscoverGitRoot.js +8 -0
  144. package/build/Foundation/Bootstrap/LoadConsoleArgs.js +47 -0
  145. package/build/Foundation/Bootstrap/LoadProjectConfiguration.js +144 -0
  146. package/build/Foundation/Bootstrap/RegisterProviders.js +12 -0
  147. package/build/Foundation/Bootstrap/RequireProjectConfiguration.js +14 -0
  148. package/build/Foundation/ConsoleInvocation.js +1 -0
  149. package/build/Foundation/Container.js +185 -0
  150. package/build/Foundation/FoundationServiceProvider.js +5 -0
  151. package/build/Foundation/Kernel.js +30 -0
  152. package/build/Foundation/Service.js +23 -0
  153. package/build/Foundation/ServiceProvider.js +12 -0
  154. package/build/Git/GitServiceProvider.js +8 -0
  155. package/build/Git/Services/GitService.js +26 -0
  156. package/build/Project/ProjectServiceProvider.js +8 -0
  157. package/build/Project/Services/ProjectService.js +124 -0
  158. package/build/SubagentDrivenDevelopment/Commands/ChecksRecordCommand.js +35 -0
  159. package/build/SubagentDrivenDevelopment/Commands/ChecksRequireCommand.js +33 -0
  160. package/build/SubagentDrivenDevelopment/Commands/ClaimCommand.js +23 -0
  161. package/build/SubagentDrivenDevelopment/Commands/CorrectCommand.js +37 -0
  162. package/build/SubagentDrivenDevelopment/Commands/CreateCommand.js +26 -0
  163. package/build/SubagentDrivenDevelopment/Commands/FindingCreateCommand.js +39 -0
  164. package/build/SubagentDrivenDevelopment/Commands/FindingUpdateCommand.js +36 -0
  165. package/build/SubagentDrivenDevelopment/Commands/FinishCommand.js +23 -0
  166. package/build/SubagentDrivenDevelopment/Commands/ImplementCommand.js +23 -0
  167. package/build/SubagentDrivenDevelopment/Commands/NextCommand.js +21 -0
  168. package/build/SubagentDrivenDevelopment/Commands/ReadyCommand.js +21 -0
  169. package/build/SubagentDrivenDevelopment/Commands/RequirementsDecideCommand.js +33 -0
  170. package/build/SubagentDrivenDevelopment/Commands/ResumeCommand.js +31 -0
  171. package/build/SubagentDrivenDevelopment/Commands/ReviewCommand.js +55 -0
  172. package/build/SubagentDrivenDevelopment/Commands/ReviewRequireCommand.js +12 -0
  173. package/build/SubagentDrivenDevelopment/Commands/StatusCommand.js +21 -0
  174. package/build/SubagentDrivenDevelopment/Commands/SubmitCommand.js +59 -0
  175. package/build/SubagentDrivenDevelopment/Commands/TicketCommentCommand.js +24 -0
  176. package/build/SubagentDrivenDevelopment/Commands/TicketCreateCommand.js +25 -0
  177. package/build/SubagentDrivenDevelopment/Commands/TicketUpdateCommand.js +31 -0
  178. package/build/SubagentDrivenDevelopment/Commands/groups.js +36 -0
  179. package/build/SubagentDrivenDevelopment/Evidence/GitEvidence.js +449 -0
  180. package/build/SubagentDrivenDevelopment/Evidence/ReportEvidence.js +80 -0
  181. package/build/SubagentDrivenDevelopment/Models/Assignments.js +1 -0
  182. package/build/SubagentDrivenDevelopment/Models/Evidence.js +1 -0
  183. package/build/SubagentDrivenDevelopment/Models/Findings.js +1 -0
  184. package/build/SubagentDrivenDevelopment/Models/Options.js +1 -0
  185. package/build/SubagentDrivenDevelopment/Models/Workflow.js +16 -0
  186. package/build/SubagentDrivenDevelopment/Persistence/AssignmentSchema.js +155 -0
  187. package/build/SubagentDrivenDevelopment/Persistence/CheckSchema.js +71 -0
  188. package/build/SubagentDrivenDevelopment/Persistence/FeatureWorkspace.js +76 -0
  189. package/build/SubagentDrivenDevelopment/Persistence/FileSystem.js +28 -0
  190. package/build/SubagentDrivenDevelopment/Persistence/FindingSchema.js +81 -0
  191. package/build/SubagentDrivenDevelopment/Persistence/LedgerRepository.js +322 -0
  192. package/build/SubagentDrivenDevelopment/Persistence/PathSafety.js +62 -0
  193. package/build/SubagentDrivenDevelopment/Persistence/ProgressSchema.js +95 -0
  194. package/build/SubagentDrivenDevelopment/Persistence/ReadinessReader.js +40 -0
  195. package/build/SubagentDrivenDevelopment/Persistence/ReviewSchema.js +68 -0
  196. package/build/SubagentDrivenDevelopment/Persistence/TicketRepository.js +147 -0
  197. package/build/SubagentDrivenDevelopment/Persistence/WorkspaceTransaction.js +282 -0
  198. package/build/SubagentDrivenDevelopment/Policies/AcceptancePolicy.js +100 -0
  199. package/build/SubagentDrivenDevelopment/Policies/AssignmentPolicy.js +36 -0
  200. package/build/SubagentDrivenDevelopment/Policies/CheckPolicy.js +93 -0
  201. package/build/SubagentDrivenDevelopment/Policies/FindingPolicy.js +53 -0
  202. package/build/SubagentDrivenDevelopment/Policies/InputValidation.js +36 -0
  203. package/build/SubagentDrivenDevelopment/Policies/ReadinessPolicy.js +26 -0
  204. package/build/SubagentDrivenDevelopment/Policies/RequiredReviewers.js +12 -0
  205. package/build/SubagentDrivenDevelopment/Policies/ReviewPolicy.js +87 -0
  206. package/build/SubagentDrivenDevelopment/Policies/WorkflowPolicy.js +85 -0
  207. package/build/SubagentDrivenDevelopment/Services/AssignmentService.js +269 -0
  208. package/build/SubagentDrivenDevelopment/Services/ChecksService.js +319 -0
  209. package/build/SubagentDrivenDevelopment/Services/CorrectionService.js +273 -0
  210. package/build/SubagentDrivenDevelopment/Services/DeliveryCorrectionService.js +253 -0
  211. package/build/SubagentDrivenDevelopment/Services/DeliveryFindingService.js +302 -0
  212. package/build/SubagentDrivenDevelopment/Services/DeliveryService.js +160 -0
  213. package/build/SubagentDrivenDevelopment/Services/DeliverySubmissionService.js +273 -0
  214. package/build/SubagentDrivenDevelopment/Services/FeatureCreationService.js +138 -0
  215. package/build/SubagentDrivenDevelopment/Services/FeatureInspectionService.js +399 -0
  216. package/build/SubagentDrivenDevelopment/Services/FindingService.js +392 -0
  217. package/build/SubagentDrivenDevelopment/Services/IntegrationService.js +258 -0
  218. package/build/SubagentDrivenDevelopment/Services/ReadinessService.js +21 -0
  219. package/build/SubagentDrivenDevelopment/Services/RequirementsService.js +216 -0
  220. package/build/SubagentDrivenDevelopment/Services/ReviewService.js +471 -0
  221. package/build/SubagentDrivenDevelopment/Services/SubmissionService.js +299 -0
  222. package/build/SubagentDrivenDevelopment/Services/TicketService.js +186 -0
  223. package/build/SubagentDrivenDevelopment/SubagentDrivenDevelopmentServiceProvider.js +113 -0
  224. package/build/Support/Records.js +5 -0
  225. package/build/Support/Repository.js +139 -0
  226. package/build/Support/Stacks.js +10 -0
  227. package/build/TemplateRendering/Computed/ComputedContextService.js +13 -0
  228. package/build/TemplateRendering/Computed/ComputedVariableProvider.js +6 -0
  229. package/build/TemplateRendering/Computed/ComputedVariableProviders.js +6 -0
  230. package/build/TemplateRendering/Computed/Providers/InternalSkillDescriptionProvider.js +6 -0
  231. package/build/TemplateRendering/Computed/Providers/SpindlePackageManagerProvider.js +11 -0
  232. package/build/TemplateRendering/Computed/ResolveRenderContext.js +18 -0
  233. package/build/TemplateRendering/Rendering/RenderText.js +43 -0
  234. package/build/TemplateRendering/TemplateRenderingServiceProvider.js +8 -0
  235. package/build/Tooling/Commands/ToolingAddCommand.js +29 -0
  236. package/build/Tooling/Commands/ToolingListCommand.js +19 -0
  237. package/build/Tooling/Commands/ToolingRemoveCommand.js +19 -0
  238. package/build/Tooling/Commands/ToolingUpdateCommand.js +30 -0
  239. package/build/Tooling/Commands/groups.js +4 -0
  240. package/build/Tooling/Models/ToolingEntry.js +1 -0
  241. package/build/Tooling/Services/ToolingService.js +278 -0
  242. package/build/Tooling/ToolingServiceProvider.js +15 -0
  243. package/build/cli.js +36 -0
  244. package/build/index.js +31 -0
  245. package/package.json +54 -3
@@ -0,0 +1,23 @@
1
+ const bootedServices = new WeakSet();
2
+ export class Service {
3
+ _app;
4
+ constructor(app) {
5
+ this._app = app;
6
+ }
7
+ get app() {
8
+ return this._app;
9
+ }
10
+ get isBooted() {
11
+ return bootedServices.has(this);
12
+ }
13
+ assertApplicationBooted() {
14
+ if (!this._app.hasBeenBooted) {
15
+ throw new Error("Application services can only run after the application has booted.");
16
+ }
17
+ }
18
+ async boot() { }
19
+ }
20
+ export async function bootService(service) {
21
+ await service.boot();
22
+ bootedServices.add(service);
23
+ }
@@ -0,0 +1,12 @@
1
+ export class ServiceProvider {
2
+ _app;
3
+ constructor(app) {
4
+ this._app = app;
5
+ }
6
+ get app() {
7
+ return this._app;
8
+ }
9
+ static commands() {
10
+ return [];
11
+ }
12
+ }
@@ -0,0 +1,8 @@
1
+ import { GitService } from "./Services/GitService.js";
2
+ import { ServiceProvider } from "../Foundation/ServiceProvider.js";
3
+ export class GitServiceProvider extends ServiceProvider {
4
+ register() {
5
+ this.app.singleton(GitService);
6
+ }
7
+ async boot() { }
8
+ }
@@ -0,0 +1,26 @@
1
+ import { execFileSync, spawnSync } from "node:child_process";
2
+ import { Service } from "../../Foundation/Service.js";
3
+ export class GitService extends Service {
4
+ discoverRoot(startingDirectory) {
5
+ let root;
6
+ try {
7
+ root = execFileSync("git", ["rev-parse", "--show-toplevel"], {
8
+ cwd: startingDirectory,
9
+ encoding: "utf8",
10
+ stdio: ["ignore", "pipe", "ignore"],
11
+ }).trim();
12
+ }
13
+ catch {
14
+ throw this.discoveryError();
15
+ }
16
+ if (!root)
17
+ throw this.discoveryError();
18
+ return root;
19
+ }
20
+ run(args, options) {
21
+ return spawnSync("git", args, options);
22
+ }
23
+ discoveryError() {
24
+ return new Error("Unable to discover the Git working-tree root. Ensure Git is available and run Spindle inside a Git working tree.");
25
+ }
26
+ }
@@ -0,0 +1,8 @@
1
+ import { ServiceProvider } from "../Foundation/ServiceProvider.js";
2
+ import { ProjectService } from "./Services/ProjectService.js";
3
+ export class ProjectServiceProvider extends ServiceProvider {
4
+ register() {
5
+ this.app.singleton(ProjectService, (app) => new ProjectService(app));
6
+ }
7
+ async boot() { }
8
+ }
@@ -0,0 +1,124 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { chmodSync, constants, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
3
+ import { join, relative } from "node:path";
4
+ import { templateCatalog } from "../../Assets/Templates/Catalog.js";
5
+ import { LoadProjectConfiguration } from "../../Foundation/Bootstrap/LoadProjectConfiguration.js";
6
+ import { Service } from "../../Foundation/Service.js";
7
+ import { ToolingService } from "../../Tooling/Services/ToolingService.js";
8
+ export class ProjectService extends Service {
9
+ static configPath = ".github/spindle.json";
10
+ static copilotInstructionsPath = ".github/copilot-instructions.md";
11
+ async initialize() {
12
+ this.assertApplicationBooted();
13
+ const workspace = realpathSync(this.app.workspacePath());
14
+ const absolute = (path) => join(workspace, path);
15
+ const configFile = absolute(ProjectService.configPath);
16
+ this.assertRegularFileOrMissing(configFile, ProjectService.configPath);
17
+ const state = LoadProjectConfiguration.current(this.app);
18
+ if (state.status === "invalid")
19
+ throw new Error(state.reason, { cause: state.error });
20
+ if (state.status === "absent" || state.data.stacks.length === 0) {
21
+ throw new Error("Install at least one Blueprint stack with `spindle init` before initializing project guidance.");
22
+ }
23
+ const configuration = state.data;
24
+ const created = [];
25
+ const preserved = [];
26
+ const updated = [];
27
+ for (const directory of ["docs", "docs/adr", ".scratch", "docs/agents", ".github"]) {
28
+ const wasCreated = this.ensureDirectory(workspace, directory);
29
+ (wasCreated ? created : preserved).push(directory);
30
+ }
31
+ for (const { source, destination } of templateCatalog.project) {
32
+ const target = absolute(destination);
33
+ this.assertRegularFileOrMissing(target, destination);
34
+ if (destination === ProjectService.copilotInstructionsPath) {
35
+ const status = this.initializeCopilotInstructions(this.absoluteAsset(source), target);
36
+ if (status === "created")
37
+ created.push(destination);
38
+ else if (status === "updated")
39
+ updated.push(destination);
40
+ else
41
+ preserved.push(destination);
42
+ continue;
43
+ }
44
+ if (existsSync(target)) {
45
+ preserved.push(destination);
46
+ }
47
+ else {
48
+ copyFileSync(this.absoluteAsset(source), target, constants.COPYFILE_EXCL);
49
+ created.push(destination);
50
+ }
51
+ }
52
+ const tooling = await (await this.app.make(ToolingService)).initialize(configuration);
53
+ created.push(...tooling.created);
54
+ updated.push(...tooling.updated);
55
+ return { workspace, created, preserved, updated };
56
+ }
57
+ absoluteAsset(path) {
58
+ return join(this.app.appPath("Assets/Templates"), path);
59
+ }
60
+ initializeCopilotInstructions(source, destination) {
61
+ const existingStat = lstatSync(destination, { throwIfNoEntry: false });
62
+ if (!existingStat) {
63
+ copyFileSync(source, destination, constants.COPYFILE_EXCL);
64
+ return "created";
65
+ }
66
+ if (!existingStat.isFile()) {
67
+ throw new Error(`${ProjectService.copilotInstructionsPath} must be a regular file, not a directory or symlink.`);
68
+ }
69
+ const existing = readFileSync(destination, "utf8");
70
+ const template = readFileSync(source, "utf8");
71
+ if (existing.includes(template))
72
+ return "preserved";
73
+ const temporary = this.writeTemporary(destination, `${template}\n${existing}`);
74
+ try {
75
+ chmodSync(temporary, existingStat.mode & 0o777);
76
+ renameSync(temporary, destination);
77
+ }
78
+ finally {
79
+ this.removeTemporary(temporary);
80
+ }
81
+ return "updated";
82
+ }
83
+ writeTemporary(path, content) {
84
+ const temporary = `${path}.${randomUUID()}.tmp`;
85
+ writeFileSync(temporary, content, { flag: "wx" });
86
+ return temporary;
87
+ }
88
+ removeTemporary(path) {
89
+ try {
90
+ unlinkSync(path);
91
+ }
92
+ catch (error) {
93
+ if (!this.isMissing(error))
94
+ throw error;
95
+ }
96
+ }
97
+ ensureDirectory(workspace, path) {
98
+ let current = workspace;
99
+ let created = false;
100
+ for (const segment of path.split("/")) {
101
+ current = join(current, segment);
102
+ const entry = lstatSync(current, { throwIfNoEntry: false });
103
+ if (entry) {
104
+ if (!entry.isDirectory()) {
105
+ throw new Error(`${relative(workspace, current)} must be a real directory.`);
106
+ }
107
+ }
108
+ else {
109
+ mkdirSync(current);
110
+ created = true;
111
+ }
112
+ }
113
+ return created;
114
+ }
115
+ assertRegularFileOrMissing(path, relativePath) {
116
+ const entry = lstatSync(path, { throwIfNoEntry: false });
117
+ if (entry && !entry.isFile()) {
118
+ throw new Error(`${relativePath} must be a regular file, not a directory or symlink.`);
119
+ }
120
+ }
121
+ isMissing(error) {
122
+ return (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT");
123
+ }
124
+ }
@@ -0,0 +1,35 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { ChecksService } from "../Services/ChecksService.js";
5
+ export class ChecksRecordCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root, "checks")
8
+ .command("record")
9
+ .description("Record an externally executed check against a current snapshot.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .option("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .option("--scope <scope>", "Workflow scope: delivery")
13
+ .requiredOption("--snapshot <snapshot>", "CLI-returned content snapshot identity", CommandOptions.once("--snapshot"))
14
+ .requiredOption("--command <command>", "Exact required command", CommandOptions.once("--command"))
15
+ .requiredOption("--result <result>", "Check result: passed, failed, unavailable", CommandOptions.once("--result"))
16
+ .option("--details <details>", "Failure or unavailability details")
17
+ .option("--paths <paths...>", "Paths whose content this check explicitly covers")
18
+ .requiredOption("--author <author>", "Acting controller", CommandOptions.once("--author"))
19
+ .allowExcessArguments(false)
20
+ .action(async (options) => {
21
+ if (options.scope !== undefined && options.scope !== "delivery") {
22
+ throw new Error("--scope must be 'delivery'.");
23
+ }
24
+ if (Boolean(options.ticket) === (options.scope === "delivery")) {
25
+ throw new Error("checks record needs exactly one of --ticket or --scope delivery.");
26
+ }
27
+ const command = await (await createApp()).make(ChecksRecordCommand);
28
+ console.log(JSON.stringify(await command.run(options), null, 2));
29
+ });
30
+ }
31
+ async run(options) {
32
+ this.assertApplicationBooted();
33
+ return (await this.app.make(ChecksService)).checksRecord(options);
34
+ }
35
+ }
@@ -0,0 +1,33 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { ChecksService } from "../Services/ChecksService.js";
5
+ export class ChecksRequireCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root, "checks")
8
+ .command("require")
9
+ .description("Add or remove a required external check.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .option("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .option("--scope <scope>", "Workflow scope: delivery")
13
+ .requiredOption("--command <command>", "Exact externally executed command", CommandOptions.once("--command"))
14
+ .option("--remove", "Remove the requirement; requires --details rationale")
15
+ .option("--details <details>", "Removal rationale")
16
+ .requiredOption("--author <author>", "Acting controller", CommandOptions.once("--author"))
17
+ .allowExcessArguments(false)
18
+ .action(async (options) => {
19
+ if (options.scope !== undefined && options.scope !== "delivery") {
20
+ throw new Error("--scope must be 'delivery'.");
21
+ }
22
+ if (Boolean(options.ticket) === (options.scope === "delivery")) {
23
+ throw new Error("checks require needs exactly one of --ticket or --scope delivery.");
24
+ }
25
+ const command = await (await createApp()).make(ChecksRequireCommand);
26
+ console.log(JSON.stringify(await command.run(options), null, 2));
27
+ });
28
+ }
29
+ async run(options) {
30
+ this.assertApplicationBooted();
31
+ return (await this.app.make(ChecksService)).checksRequire(options);
32
+ }
33
+ }
@@ -0,0 +1,23 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { AssignmentService } from "../Services/AssignmentService.js";
5
+ export class ClaimCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root)
8
+ .command("claim")
9
+ .description("Claim a ready ticket from a clean Git checkout.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .requiredOption("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .requiredOption("--author <author>", "Controller author", CommandOptions.once("--author"))
13
+ .allowExcessArguments(false)
14
+ .action(async (options) => {
15
+ const command = await (await createApp()).make(ClaimCommand);
16
+ console.log(JSON.stringify(await command.run(options), null, 2));
17
+ });
18
+ }
19
+ async run(options) {
20
+ this.assertApplicationBooted();
21
+ return (await this.app.make(AssignmentService)).claim(options);
22
+ }
23
+ }
@@ -0,0 +1,37 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { CorrectionService } from "../Services/CorrectionService.js";
5
+ export class CorrectCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root)
8
+ .command("correct")
9
+ .description("Persist a ticket-scoped or delivery-scoped correction attempt and return its fix assignment.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .option("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .option("--scope <scope>", "Workflow scope: delivery")
13
+ .option("--findings <ids...>", "Selected actionable canonical finding IDs")
14
+ .option("--check-failure <command>", "Exact non-mechanical failed check command")
15
+ .requiredOption("--details <details>", "Correction objective or adjudication rationale", CommandOptions.once("--details"))
16
+ .option("--decision <decision>", "Continuation decision: continue or pause")
17
+ .option("--assessment <assessment>", "Recorded progress assessment for the prior attempt")
18
+ .option("--assessment-result <result>", "Progress result: productive")
19
+ .option("--reason <reason>", "Pause reason: no-progress, repeated-failure, recurring-blocker, requirements-disagreement, worker-blocked")
20
+ .requiredOption("--author <author>", "Controller author", CommandOptions.once("--author"))
21
+ .allowExcessArguments(false)
22
+ .action(async (options) => {
23
+ if (options.scope !== undefined && options.scope !== "delivery") {
24
+ throw new Error("--scope must be 'delivery'.");
25
+ }
26
+ if (Boolean(options.ticket) === (options.scope === "delivery")) {
27
+ throw new Error("correct needs exactly one of --ticket or --scope delivery.");
28
+ }
29
+ const command = await (await createApp()).make(CorrectCommand);
30
+ console.log(JSON.stringify(await command.run(options), null, 2));
31
+ });
32
+ }
33
+ async run(options) {
34
+ this.assertApplicationBooted();
35
+ return (await this.app.make(CorrectionService)).correct(options);
36
+ }
37
+ }
@@ -0,0 +1,26 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { validateCreateOptions } from "../Policies/InputValidation.js";
5
+ import { FeatureCreationService } from "../Services/FeatureCreationService.js";
6
+ export class CreateCommand extends Command {
7
+ static register(root, createApp) {
8
+ group(root)
9
+ .command("create")
10
+ .description("Create a numbered SDD feature workspace with spec template and initial ledger.")
11
+ .requiredOption("--title <title>", "Feature title", CommandOptions.once("--title"))
12
+ .requiredOption("--author <author>", "Creation author", CommandOptions.once("--author"))
13
+ .option("--require-retrospective", "Require a retrospective when finishing this feature")
14
+ .option("--with-design", "Copy the design template into the feature workspace")
15
+ .allowExcessArguments(false)
16
+ .action(async (options) => {
17
+ validateCreateOptions(options);
18
+ const command = await (await createApp()).make(CreateCommand);
19
+ console.log(JSON.stringify(await command.run(options), null, 2));
20
+ });
21
+ }
22
+ async run(options) {
23
+ this.assertApplicationBooted();
24
+ return (await this.app.make(FeatureCreationService)).create(options);
25
+ }
26
+ }
@@ -0,0 +1,39 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { FindingService } from "../Services/FindingService.js";
5
+ export class FindingCreateCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root, "finding")
8
+ .command("create")
9
+ .description("Register a review finding with its report and snapshot provenance.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .option("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .option("--scope <scope>", "Workflow scope: delivery")
13
+ .option("--related-ticket <id>", "Ticket to reopen for a blocking feature finding")
14
+ .requiredOption("--source <path>", "Assigned review report containing the finding", CommandOptions.once("--source"))
15
+ .requiredOption("--snapshot <snapshot>", "Reviewed snapshot returned by the CLI", CommandOptions.once("--snapshot"))
16
+ .requiredOption("--severity <severity>", "Finding severity: high, medium, low", CommandOptions.once("--severity"))
17
+ .option("--blocking", "Mark the finding as blocking")
18
+ .option("--non-blocking", "Mark the finding as non-blocking")
19
+ .option("--link <id>", "Explicitly link this observation to a canonical finding")
20
+ .requiredOption("--summary <summary>", "Concise finding summary", CommandOptions.once("--summary"))
21
+ .requiredOption("--details <details>", "Full finding details", CommandOptions.once("--details"))
22
+ .requiredOption("--author <author>", "Finding registration author", CommandOptions.once("--author"))
23
+ .allowExcessArguments(false)
24
+ .action(async (options) => {
25
+ if (options.scope !== undefined && options.scope !== "delivery") {
26
+ throw new Error("--scope must be 'delivery'.");
27
+ }
28
+ if (Boolean(options.ticket) === (options.scope === "delivery")) {
29
+ throw new Error("finding create needs exactly one of --ticket or --scope delivery.");
30
+ }
31
+ const command = await (await createApp()).make(FindingCreateCommand);
32
+ console.log(JSON.stringify(await command.run(options), null, 2));
33
+ });
34
+ }
35
+ async run(options) {
36
+ this.assertApplicationBooted();
37
+ return (await this.app.make(FindingService)).findingCreate(options);
38
+ }
39
+ }
@@ -0,0 +1,36 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { FindingService } from "../Services/FindingService.js";
5
+ export class FindingUpdateCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root, "finding")
8
+ .command("update")
9
+ .description("Record an explicit, attributed finding disposition.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .option("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .option("--scope <scope>", "Workflow scope: delivery")
13
+ .requiredOption("--finding <id>", "Canonical finding ID", CommandOptions.once("--finding"))
14
+ .requiredOption("--status <status>", "Finding status: open, confirmed, fixed, dismissed, deferred")
15
+ .option("--details <details>", "Ruling explanation or rationale")
16
+ .option("--follow-up <reference>", "Required follow-up reference for a deferral")
17
+ .option("--requirements-decision <id>", "Approved requirements decision covering a scope change")
18
+ .option("--evidence <snapshot>", "Current CLI snapshot with passing verification checks")
19
+ .requiredOption("--author <author>", "Ruling author", CommandOptions.once("--author"))
20
+ .allowExcessArguments(false)
21
+ .action(async (options) => {
22
+ if (options.scope !== undefined && options.scope !== "delivery") {
23
+ throw new Error("--scope must be 'delivery'.");
24
+ }
25
+ if (Boolean(options.ticket) === (options.scope === "delivery")) {
26
+ throw new Error("finding update needs exactly one of --ticket or --scope delivery.");
27
+ }
28
+ const command = await (await createApp()).make(FindingUpdateCommand);
29
+ console.log(JSON.stringify(await command.run(options), null, 2));
30
+ });
31
+ }
32
+ async run(options) {
33
+ this.assertApplicationBooted();
34
+ return (await this.app.make(FindingService)).findingUpdate(options);
35
+ }
36
+ }
@@ -0,0 +1,23 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { DeliveryService } from "../Services/DeliveryService.js";
5
+ export class FinishCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root)
8
+ .command("finish")
9
+ .description("Complete feature delivery after all tickets are closed and required checks and reviews pass.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .option("--report <path>", "Optional or policy-required Markdown report inside feature reports/", CommandOptions.once("--report"))
12
+ .requiredOption("--author <author>", "Controller author", CommandOptions.once("--author"))
13
+ .allowExcessArguments(false)
14
+ .action(async (options) => {
15
+ const command = await (await createApp()).make(FinishCommand);
16
+ console.log(JSON.stringify(await command.run(options), null, 2));
17
+ });
18
+ }
19
+ async run(options) {
20
+ this.assertApplicationBooted();
21
+ return (await this.app.make(DeliveryService)).finish(options);
22
+ }
23
+ }
@@ -0,0 +1,23 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { AssignmentService } from "../Services/AssignmentService.js";
5
+ export class ImplementCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root)
8
+ .command("implement")
9
+ .description("Persist and return an implementation assignment.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .requiredOption("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .requiredOption("--author <author>", "Controller author", CommandOptions.once("--author"))
13
+ .allowExcessArguments(false)
14
+ .action(async (options) => {
15
+ const command = await (await createApp()).make(ImplementCommand);
16
+ console.log(JSON.stringify(await command.run(options), null, 2));
17
+ });
18
+ }
19
+ async run(options) {
20
+ this.assertApplicationBooted();
21
+ return (await this.app.make(AssignmentService)).implement(options);
22
+ }
23
+ }
@@ -0,0 +1,21 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { DeliveryService } from "../Services/DeliveryService.js";
5
+ export class NextCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root)
8
+ .command("next")
9
+ .description("Show permitted ticket and whole-spec delivery actions without changing state.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .allowExcessArguments(false)
12
+ .action(async (options) => {
13
+ const command = await (await createApp()).make(NextCommand);
14
+ console.log(JSON.stringify(await command.run(options), null, 2));
15
+ });
16
+ }
17
+ async run(options) {
18
+ this.assertApplicationBooted();
19
+ return (await this.app.make(DeliveryService)).next(options);
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { ReadinessService } from "../Services/ReadinessService.js";
5
+ export class ReadyCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root)
8
+ .command("ready")
9
+ .description("List ready tickets and tickets waiting on dependencies.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .allowExcessArguments(false)
12
+ .action(async (options) => {
13
+ const command = await (await createApp()).make(ReadyCommand);
14
+ console.log(JSON.stringify(await command.run(options), null, 2));
15
+ });
16
+ }
17
+ async run(options) {
18
+ this.assertApplicationBooted();
19
+ return (await this.app.make(ReadinessService)).ready(options);
20
+ }
21
+ }
@@ -0,0 +1,33 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { RequirementsService } from "../Services/RequirementsService.js";
5
+ export class RequirementsDecideCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root, "requirements")
8
+ .command("decide")
9
+ .description("Approve or reject a requirements change and invalidate prior review.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .option("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .option("--scope <scope>", "Workflow scope: delivery")
13
+ .requiredOption("--finding <id>", "Finding affected by the requirements decision", CommandOptions.once("--finding"))
14
+ .requiredOption("--decision <decision>", "Decision: approved or rejected")
15
+ .requiredOption("--details <details>", "Decision rationale", CommandOptions.once("--details"))
16
+ .requiredOption("--author <author>", "Decision author", CommandOptions.once("--author"))
17
+ .allowExcessArguments(false)
18
+ .action(async (options) => {
19
+ if (options.scope !== undefined && options.scope !== "delivery") {
20
+ throw new Error("--scope must be 'delivery'.");
21
+ }
22
+ if (Boolean(options.ticket) === (options.scope === "delivery")) {
23
+ throw new Error("requirements decide needs exactly one of --ticket or --scope delivery.");
24
+ }
25
+ const command = await (await createApp()).make(RequirementsDecideCommand);
26
+ console.log(JSON.stringify(await command.run(options), null, 2));
27
+ });
28
+ }
29
+ async run(options) {
30
+ this.assertApplicationBooted();
31
+ return (await this.app.make(RequirementsService)).requirementsDecide(options);
32
+ }
33
+ }
@@ -0,0 +1,31 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { group } from "./groups.js";
4
+ import { AssignmentService } from "../Services/AssignmentService.js";
5
+ export class ResumeCommand extends Command {
6
+ static register(root, createApp) {
7
+ group(root)
8
+ .command("resume")
9
+ .description("Restore the recorded ticket or delivery gate and assignment.")
10
+ .requiredOption("--spec <path>", "Feature spec path", CommandOptions.once("--spec"))
11
+ .option("--ticket <id>", "Ticket ID", CommandOptions.once("--ticket"))
12
+ .option("--scope <scope>", "Workflow scope: delivery")
13
+ .requiredOption("--author <author>", "Controller author", CommandOptions.once("--author"))
14
+ .option("--confirm-handoff", "Confirm the previous controller has stopped editing")
15
+ .allowExcessArguments(false)
16
+ .action(async (options) => {
17
+ if (options.scope !== undefined && options.scope !== "delivery") {
18
+ throw new Error("--scope must be 'delivery'.");
19
+ }
20
+ if (Boolean(options.ticket) === (options.scope === "delivery")) {
21
+ throw new Error("resume needs exactly one of --ticket or --scope delivery.");
22
+ }
23
+ const command = await (await createApp()).make(ResumeCommand);
24
+ console.log(JSON.stringify(await command.run(options), null, 2));
25
+ });
26
+ }
27
+ async run(options) {
28
+ this.assertApplicationBooted();
29
+ return (await this.app.make(AssignmentService)).resume(options);
30
+ }
31
+ }