@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,139 @@
1
+ function isPlainObject(value) {
2
+ if (value === null || typeof value !== "object")
3
+ return false;
4
+ const prototype = Object.getPrototypeOf(value);
5
+ return prototype === Object.prototype || prototype === null;
6
+ }
7
+ export class Repository {
8
+ items = {};
9
+ get(key, defaultValue) {
10
+ const result = this.lookup(this.segments(key), key);
11
+ if (result.found)
12
+ return result.value;
13
+ return typeof defaultValue === "function" ? defaultValue() : defaultValue;
14
+ }
15
+ set(key, value) {
16
+ const segments = this.segments(key);
17
+ let parent = this.items;
18
+ for (let index = 0; index < segments.length - 1; index++) {
19
+ const segment = segments[index];
20
+ if (!Object.hasOwn(parent, segment)) {
21
+ const branch = {};
22
+ let nested = branch;
23
+ for (const remaining of segments.slice(index + 1, -1)) {
24
+ const child = {};
25
+ this.write(nested, remaining, child);
26
+ nested = child;
27
+ }
28
+ this.write(nested, segments[segments.length - 1], value);
29
+ this.write(parent, segment, branch);
30
+ return;
31
+ }
32
+ parent = this.container(parent[segment], key, segments.slice(0, index + 1));
33
+ }
34
+ this.write(parent, segments[segments.length - 1], value);
35
+ }
36
+ has(key) {
37
+ return this.lookup(this.segments(key), key).found;
38
+ }
39
+ delete(key) {
40
+ const segments = this.segments(key);
41
+ let parent = this.items;
42
+ for (let index = 0; index < segments.length - 1; index++) {
43
+ const segment = segments[index];
44
+ if (!Object.hasOwn(parent, segment))
45
+ return false;
46
+ parent = this.container(parent[segment], key, segments.slice(0, index + 1));
47
+ }
48
+ const last = segments[segments.length - 1];
49
+ if (!Object.hasOwn(parent, last))
50
+ return false;
51
+ return delete parent[last];
52
+ }
53
+ string(key, defaultValue) {
54
+ return this.typed(key, (value) => typeof value === "string", "a string", defaultValue, arguments.length > 1);
55
+ }
56
+ integer(key, defaultValue) {
57
+ return this.typed(key, (value) => typeof value === "number" && Number.isSafeInteger(value), "a safe integer", defaultValue, arguments.length > 1);
58
+ }
59
+ float(key, defaultValue) {
60
+ return this.typed(key, (value) => typeof value === "number" && Number.isFinite(value), "a finite number", defaultValue, arguments.length > 1);
61
+ }
62
+ boolean(key, defaultValue) {
63
+ return this.typed(key, (value) => typeof value === "boolean", "a boolean", defaultValue, arguments.length > 1);
64
+ }
65
+ array(key, defaultValue) {
66
+ return this.typed(key, (value) => Array.isArray(value), "an array", defaultValue, arguments.length > 1);
67
+ }
68
+ push(key, value) {
69
+ const items = this.array(key, () => {
70
+ const items = [];
71
+ this.set(key, items);
72
+ return items;
73
+ });
74
+ items.push(value);
75
+ }
76
+ prepend(key, value) {
77
+ const items = this.array(key, () => {
78
+ const items = [];
79
+ this.set(key, items);
80
+ return items;
81
+ });
82
+ items.unshift(value);
83
+ }
84
+ typed(key, validate, expected, defaultValue, hasDefault) {
85
+ const result = this.lookup(this.segments(key), key);
86
+ if (!result.found && !hasDefault) {
87
+ throw new Error(`Repository key "${key}" is missing; expected ${expected}.`);
88
+ }
89
+ const value = result.found
90
+ ? result.value
91
+ : typeof defaultValue === "function"
92
+ ? defaultValue()
93
+ : defaultValue;
94
+ if (!validate(value)) {
95
+ throw new TypeError(`Repository key "${key}" must contain ${expected}.`);
96
+ }
97
+ return value;
98
+ }
99
+ segments(key) {
100
+ if (typeof key !== "string")
101
+ throw new TypeError("Repository keys must be strings.");
102
+ const segments = key.split(".");
103
+ if (segments.some((segment) => segment.length === 0)) {
104
+ throw new Error(`Invalid repository key "${key}": empty path segments are not allowed.`);
105
+ }
106
+ const forbidden = segments.find((segment) => segment === "__proto__" || segment === "prototype" || segment === "constructor");
107
+ if (forbidden !== undefined) {
108
+ throw new Error(`Invalid repository key "${key}": forbidden path segment "${forbidden}".`);
109
+ }
110
+ return segments;
111
+ }
112
+ lookup(segments, key) {
113
+ let parent = this.items;
114
+ for (let index = 0; index < segments.length; index++) {
115
+ const segment = segments[index];
116
+ if (!Object.hasOwn(parent, segment))
117
+ return { found: false };
118
+ const value = parent[segment];
119
+ if (index === segments.length - 1)
120
+ return { found: true, value };
121
+ parent = this.container(value, key, segments.slice(0, index + 1));
122
+ }
123
+ return { found: false };
124
+ }
125
+ container(value, key, traversed) {
126
+ if (!isPlainObject(value)) {
127
+ throw new TypeError(`Cannot traverse repository key "${key}": "${traversed.join(".")}" is not a plain object.`);
128
+ }
129
+ return value;
130
+ }
131
+ write(parent, key, value) {
132
+ Object.defineProperty(parent, key, {
133
+ value,
134
+ enumerable: true,
135
+ configurable: true,
136
+ writable: true,
137
+ });
138
+ }
139
+ }
@@ -0,0 +1,10 @@
1
+ export class Stacks {
2
+ static supported = ["php-laravel", "python", "typescript"];
3
+ static validate(stacks) {
4
+ for (const stack of stacks) {
5
+ if (!Stacks.supported.includes(stack)) {
6
+ throw new Error(`Invalid stack '${stack}'. Supported stacks: ${Stacks.supported.join(", ")}.`);
7
+ }
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ import { Service } from "../../Foundation/Service.js";
2
+ import { computedVariableProviders } from "./ComputedVariableProviders.js";
3
+ import { resolveRenderContext } from "./ResolveRenderContext.js";
4
+ export class ComputedContextService extends Service {
5
+ resolvedContext;
6
+ resolve(resolveBaseContext, registry = computedVariableProviders) {
7
+ this.resolvedContext ??= (async () => {
8
+ const inputs = await resolveBaseContext();
9
+ return inputs === undefined ? undefined : resolveRenderContext(this.app, inputs, registry);
10
+ })();
11
+ return this.resolvedContext;
12
+ }
13
+ }
@@ -0,0 +1,6 @@
1
+ export class ComputedVariableProvider {
2
+ app;
3
+ constructor(app) {
4
+ this.app = app;
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ import { InternalSkillDescriptionProvider } from "./Providers/InternalSkillDescriptionProvider.js";
2
+ import { SpindlePackageManagerProvider } from "./Providers/SpindlePackageManagerProvider.js";
3
+ export const computedVariableProviders = {
4
+ internal_skill_description: InternalSkillDescriptionProvider,
5
+ spindle_package_manager: SpindlePackageManagerProvider,
6
+ };
@@ -0,0 +1,6 @@
1
+ import { ComputedVariableProvider } from "../ComputedVariableProvider.js";
2
+ export class InternalSkillDescriptionProvider extends ComputedVariableProvider {
3
+ render() {
4
+ return "This is an internal skill do not load it unless explicitly required by the user or another skill.";
5
+ }
6
+ }
@@ -0,0 +1,11 @@
1
+ import { LoadProjectConfiguration } from "../../../Foundation/Bootstrap/LoadProjectConfiguration.js";
2
+ import { ComputedVariableProvider } from "../ComputedVariableProvider.js";
3
+ export class SpindlePackageManagerProvider extends ComputedVariableProvider {
4
+ render() {
5
+ const { config } = LoadProjectConfiguration.requireCurrent(this.app);
6
+ if (config.spindle_package_manager === undefined) {
7
+ throw new Error('Missing "spindle_package_manager" in project Config; run `spindle init` before rendering Blueprints.');
8
+ }
9
+ return config.spindle_package_manager;
10
+ }
11
+ }
@@ -0,0 +1,18 @@
1
+ import { computedVariableProviders } from "./ComputedVariableProviders.js";
2
+ export function resolveRenderContext(app, inputs, registry = computedVariableProviders) {
3
+ const stacks = [...(inputs.stacks ?? [])];
4
+ const variables = { ...inputs.variables };
5
+ const computed = {};
6
+ for (const [name, Provider] of Object.entries(registry)) {
7
+ try {
8
+ const value = new Provider(app).render();
9
+ if (value !== undefined)
10
+ computed[name] = value;
11
+ }
12
+ catch (error) {
13
+ const detail = error instanceof Error ? error.message : String(error);
14
+ throw new Error(`Computed Variable '${name}' failed: ${detail}`, { cause: error });
15
+ }
16
+ }
17
+ return { stacks, variables, computed };
18
+ }
@@ -0,0 +1,43 @@
1
+ import nunjucks from "nunjucks";
2
+ const environment = new nunjucks.Environment(null, {
3
+ autoescape: false,
4
+ throwOnUndefined: true,
5
+ });
6
+ function missingComputedOutput(source, error) {
7
+ if (!(error instanceof Error))
8
+ return undefined;
9
+ const location = /\[Line (\d+), Column (\d+)\]/.exec(error.message);
10
+ if (!location)
11
+ return undefined;
12
+ const line = Number(location[1]);
13
+ const column = Number(location[2]);
14
+ let lineStart = 0;
15
+ for (let currentLine = 1; currentLine < line; currentLine += 1) {
16
+ const newline = source.indexOf("\n", lineStart);
17
+ if (newline < 0)
18
+ return undefined;
19
+ lineStart = newline + 1;
20
+ }
21
+ const offset = lineStart + column - 1;
22
+ const opening = source.lastIndexOf("{{", offset);
23
+ const closing = source.indexOf("}}", offset);
24
+ if (opening < 0 || closing < 0)
25
+ return undefined;
26
+ const expression = source.slice(opening + 2, closing);
27
+ return /^\s*computed(?:\.([A-Za-z_][A-Za-z0-9_]*)|\[\s*(?:"([^"]*)"|'([^']*)')\s*\])/
28
+ .exec(expression)
29
+ ?.slice(1)
30
+ .find((name) => name !== undefined);
31
+ }
32
+ export function renderText(source, context) {
33
+ try {
34
+ return environment.renderString(source, context);
35
+ }
36
+ catch (error) {
37
+ const name = missingComputedOutput(source, error);
38
+ if (name !== undefined) {
39
+ throw new Error(`Unknown computed output 'computed.${name}'.`, { cause: error });
40
+ }
41
+ throw error;
42
+ }
43
+ }
@@ -0,0 +1,8 @@
1
+ import { ServiceProvider } from "../Foundation/ServiceProvider.js";
2
+ import { ComputedContextService } from "./Computed/ComputedContextService.js";
3
+ export class TemplateRenderingServiceProvider extends ServiceProvider {
4
+ register() {
5
+ this.app.bind(ComputedContextService);
6
+ }
7
+ async boot() { }
8
+ }
@@ -0,0 +1,29 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { toolGroup } from "./groups.js";
4
+ import { ToolingService } from "../Services/ToolingService.js";
5
+ export class ToolingAddCommand extends Command {
6
+ static register(program, createApp) {
7
+ toolGroup(program)
8
+ .command("add")
9
+ .description("Register a project tooling command and regenerate docs/agents/tooling.md.")
10
+ .requiredOption("--key <key>", "Stable lowercase kebab-case key", CommandOptions.once("--key"))
11
+ .requiredOption("--section <section>", "Tooling documentation section", CommandOptions.once("--section"))
12
+ .requiredOption("--command <command>", "Verified command text", CommandOptions.once("--command"))
13
+ .option("--description <description>", "Optional command description", CommandOptions.once("--description"))
14
+ .allowExcessArguments(false)
15
+ .action(async (options) => {
16
+ const command = await (await createApp()).make(ToolingAddCommand);
17
+ console.log(JSON.stringify(await command.run({
18
+ key: options.key,
19
+ section: options.section,
20
+ command: options.command,
21
+ ...(options.description === undefined ? {} : { description: options.description }),
22
+ }), null, 2));
23
+ });
24
+ }
25
+ async run(input) {
26
+ this.assertApplicationBooted();
27
+ return (await this.app.make(ToolingService)).addTooling(input);
28
+ }
29
+ }
@@ -0,0 +1,19 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { toolGroup } from "./groups.js";
3
+ import { ToolingService } from "../Services/ToolingService.js";
4
+ export class ToolingListCommand extends Command {
5
+ static register(program, createApp) {
6
+ toolGroup(program)
7
+ .command("list")
8
+ .description("List registered tooling commands and their keys.")
9
+ .allowExcessArguments(false)
10
+ .action(async () => {
11
+ const command = await (await createApp()).make(ToolingListCommand);
12
+ console.log(JSON.stringify(await command.run(), null, 2));
13
+ });
14
+ }
15
+ async run() {
16
+ this.assertApplicationBooted();
17
+ return (await this.app.make(ToolingService)).listTooling();
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { toolGroup } from "./groups.js";
3
+ import { ToolingService } from "../Services/ToolingService.js";
4
+ export class ToolingRemoveCommand extends Command {
5
+ static register(program, createApp) {
6
+ toolGroup(program)
7
+ .command("remove <key>")
8
+ .description("Remove a registered tooling command and regenerate docs/agents/tooling.md.")
9
+ .allowExcessArguments(false)
10
+ .action(async (key) => {
11
+ const command = await (await createApp()).make(ToolingRemoveCommand);
12
+ console.log(JSON.stringify(await command.run(key), null, 2));
13
+ });
14
+ }
15
+ async run(key) {
16
+ this.assertApplicationBooted();
17
+ return (await this.app.make(ToolingService)).removeTooling(key);
18
+ }
19
+ }
@@ -0,0 +1,30 @@
1
+ import { Command } from "../../Commands/Command.js";
2
+ import { CommandOptions } from "../../Commands/CommandOptions.js";
3
+ import { toolGroup } from "./groups.js";
4
+ import { ToolingService } from "../Services/ToolingService.js";
5
+ export class ToolingUpdateCommand extends Command {
6
+ static register(program, createApp) {
7
+ toolGroup(program)
8
+ .command("update <key>")
9
+ .description("Update a registered tooling command and regenerate docs/agents/tooling.md.")
10
+ .option("--section <section>", "Replacement documentation section", CommandOptions.once("--section"))
11
+ .option("--command <command>", "Replacement command text", CommandOptions.once("--command"))
12
+ .option("--description <description>", "Replacement description", CommandOptions.once("--description"))
13
+ .option("--clear-description", "Remove the current description")
14
+ .allowExcessArguments(false)
15
+ .action(async (key, options) => {
16
+ const command = await (await createApp()).make(ToolingUpdateCommand);
17
+ console.log(JSON.stringify(await command.run({
18
+ key,
19
+ ...(options.section === undefined ? {} : { section: options.section }),
20
+ ...(options.command === undefined ? {} : { command: options.command }),
21
+ ...(options.description === undefined ? {} : { description: options.description }),
22
+ ...(options.clearDescription ? { clearDescription: true } : {}),
23
+ }), null, 2));
24
+ });
25
+ }
26
+ async run(input) {
27
+ this.assertApplicationBooted();
28
+ return (await this.app.make(ToolingService)).updateTooling(input);
29
+ }
30
+ }
@@ -0,0 +1,4 @@
1
+ export function toolGroup(root) {
2
+ return (root.commands.find((command) => command.name() === "tool") ??
3
+ root.command("tool").description("Manage project tooling command documentation."));
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,278 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { existsSync, lstatSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { LoadProjectConfiguration, } from "../../Foundation/Bootstrap/LoadProjectConfiguration.js";
5
+ import { Service } from "../../Foundation/Service.js";
6
+ const toolingPath = "docs/agents/tooling.md";
7
+ const configPath = ".github/spindle.json";
8
+ const toolingKeyPattern = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
9
+ const defaultTooling = [
10
+ {
11
+ key: "spindle-init",
12
+ section: "Spindle",
13
+ command: "spindle init",
14
+ description: "Install Spindle Blueprints and initialize project guidance.",
15
+ },
16
+ {
17
+ key: "spindle-update",
18
+ section: "Spindle",
19
+ command: "spindle update",
20
+ description: "Update installed Spindle Blueprints.",
21
+ },
22
+ {
23
+ key: "spindle-tool",
24
+ section: "Spindle",
25
+ command: "spindle tool <list|add|update|remove>",
26
+ description: "Manage documented project tooling commands.",
27
+ },
28
+ ];
29
+ export class ToolingService extends Service {
30
+ async initialize(previous) {
31
+ this.assertApplicationBooted();
32
+ const workspace = this.app.workspacePath();
33
+ const configFile = join(workspace, configPath);
34
+ this.assertRegularFileOrMissing(configFile, configPath);
35
+ const tooling = previous.tooling === undefined
36
+ ? structuredClone(defaultTooling)
37
+ : this.migrateDefaultTooling(previous.tooling);
38
+ const config = {
39
+ ...previous,
40
+ stacks: previous.stacks,
41
+ tooling,
42
+ };
43
+ const documentFile = join(workspace, toolingPath);
44
+ this.assertRegularFileOrMissing(documentFile, toolingPath);
45
+ const documentExisted = existsSync(documentFile);
46
+ this.writeConfigurationAndDocumentation(configFile, `${JSON.stringify(config, null, 2)}\n`, documentFile, this.renderTooling(tooling), readFileSync(configFile));
47
+ await LoadProjectConfiguration.refresh(this.app);
48
+ return {
49
+ created: documentExisted ? [] : [toolingPath],
50
+ updated: documentExisted ? [configPath, toolingPath] : [configPath],
51
+ };
52
+ }
53
+ async listTooling() {
54
+ this.assertApplicationBooted();
55
+ return [...this.readInitializedConfiguration().tooling];
56
+ }
57
+ async addTooling(input) {
58
+ this.assertApplicationBooted();
59
+ const entry = this.validateEntry(input);
60
+ const config = this.readInitializedConfiguration();
61
+ const tooling = config.tooling;
62
+ if (tooling.some(({ key }) => key === entry.key)) {
63
+ throw new Error(`Tooling key '${entry.key}' already exists; use 'spindle tool update'.`);
64
+ }
65
+ const updated = [...tooling, entry];
66
+ await this.saveTooling(config, updated);
67
+ return entry;
68
+ }
69
+ async updateTooling(input) {
70
+ this.assertApplicationBooted();
71
+ this.validateKey(input.key);
72
+ if (input.section === undefined &&
73
+ input.command === undefined &&
74
+ input.description === undefined &&
75
+ !input.clearDescription) {
76
+ throw new Error("Provide at least one tooling field to update.");
77
+ }
78
+ if (input.clearDescription && input.description !== undefined) {
79
+ throw new Error("Use either --description or --clear-description, not both.");
80
+ }
81
+ const config = this.readInitializedConfiguration();
82
+ const tooling = config.tooling;
83
+ const index = tooling.findIndex(({ key }) => key === input.key);
84
+ if (index < 0)
85
+ throw new Error(`No tooling command has key '${input.key}'.`);
86
+ const current = tooling[index];
87
+ const updatedEntry = {
88
+ ...current,
89
+ ...(input.section === undefined
90
+ ? {}
91
+ : { section: this.requiredText(input.section, "section") }),
92
+ ...(input.command === undefined
93
+ ? {}
94
+ : { command: this.requiredText(input.command, "command") }),
95
+ };
96
+ if (input.clearDescription)
97
+ delete updatedEntry.description;
98
+ else if (input.description !== undefined) {
99
+ updatedEntry.description = this.requiredText(input.description, "description");
100
+ }
101
+ const updated = [...tooling];
102
+ updated[index] = updatedEntry;
103
+ await this.saveTooling(config, updated);
104
+ return updatedEntry;
105
+ }
106
+ async removeTooling(key) {
107
+ this.assertApplicationBooted();
108
+ this.validateKey(key);
109
+ const config = this.readInitializedConfiguration();
110
+ const tooling = config.tooling;
111
+ if (!tooling.some((entry) => entry.key === key)) {
112
+ throw new Error(`No tooling command has key '${key}'.`);
113
+ }
114
+ await this.saveTooling(config, tooling.filter((entry) => entry.key !== key));
115
+ return { key, removed: true };
116
+ }
117
+ readInitializedConfiguration() {
118
+ const state = LoadProjectConfiguration.current(this.app);
119
+ if (state.status === "absent") {
120
+ throw new Error("Project tooling is not initialized. Run `spindle init` first.");
121
+ }
122
+ if (state.status === "invalid")
123
+ throw new Error(state.reason, { cause: state.error });
124
+ if (state.data.tooling === undefined) {
125
+ throw new Error("Project tooling is not initialized. Run `spindle init` first.");
126
+ }
127
+ this.assertToolingDocumentAvailable();
128
+ return state.data;
129
+ }
130
+ async saveTooling(config, tooling) {
131
+ const updated = { ...config, tooling };
132
+ const configFile = this.app.workspacePath(configPath);
133
+ const documentFile = this.app.workspacePath(toolingPath);
134
+ this.writeConfigurationAndDocumentation(configFile, `${JSON.stringify(updated, null, 2)}\n`, documentFile, this.renderTooling(tooling), readFileSync(configFile));
135
+ await LoadProjectConfiguration.refresh(this.app);
136
+ }
137
+ assertToolingDocumentAvailable() {
138
+ const workspace = this.app.workspacePath();
139
+ const parent = join(workspace, "docs", "agents");
140
+ try {
141
+ if (!lstatSync(parent).isDirectory()) {
142
+ throw new Error("docs/agents must be a real directory.");
143
+ }
144
+ }
145
+ catch (error) {
146
+ if (isMissing(error)) {
147
+ throw new Error("Project tooling is not initialized. Run `spindle init` first.");
148
+ }
149
+ throw error;
150
+ }
151
+ this.assertRegularFileOrMissing(join(parent, "tooling.md"), toolingPath);
152
+ }
153
+ validateEntry(input) {
154
+ this.validateKey(input.key);
155
+ const entry = {
156
+ key: input.key,
157
+ section: this.requiredText(input.section, "section"),
158
+ command: this.requiredText(input.command, "command"),
159
+ ...(input.description === undefined
160
+ ? {}
161
+ : { description: this.requiredText(input.description, "description") }),
162
+ };
163
+ return entry;
164
+ }
165
+ migrateDefaultTooling(entries) {
166
+ const migrated = [...entries];
167
+ const legacyIndex = entries.findIndex(({ key, command }) => key === "spindle-project-init" && command === "spindle project init");
168
+ if (legacyIndex >= 0) {
169
+ if (migrated.some(({ key }) => key === "spindle-tool")) {
170
+ migrated.splice(legacyIndex, 1);
171
+ }
172
+ else {
173
+ migrated[legacyIndex] = structuredClone(defaultTooling[2]);
174
+ }
175
+ }
176
+ const initIndex = migrated.findIndex(({ key, command, description }) => key === "spindle-init" &&
177
+ command === "spindle init" &&
178
+ description === "Install Spindle Blueprints for selected stacks.");
179
+ if (initIndex >= 0) {
180
+ migrated[initIndex] = structuredClone(defaultTooling[0]);
181
+ }
182
+ return migrated;
183
+ }
184
+ validateKey(key) {
185
+ if (!toolingKeyPattern.test(key)) {
186
+ throw new Error("Tooling key must use lowercase letters, digits, and interior hyphens.");
187
+ }
188
+ }
189
+ requiredText(value, label) {
190
+ const trimmed = value.trim();
191
+ if (!trimmed)
192
+ throw new Error(`Tooling ${label} must not be empty.`);
193
+ if (/[\r\n]/.test(trimmed))
194
+ throw new Error(`Tooling ${label} must be a single line.`);
195
+ return trimmed;
196
+ }
197
+ renderTooling(entries) {
198
+ const sections = new Map();
199
+ for (const entry of entries) {
200
+ const section = sections.get(entry.section) ?? [];
201
+ section.push(entry);
202
+ sections.set(entry.section, section);
203
+ }
204
+ const lines = ["# Project Tooling"];
205
+ for (const [section, commands] of sections) {
206
+ lines.push("", `## ${section}`);
207
+ for (const entry of commands) {
208
+ const description = entry.description ? `: ${entry.description}` : "";
209
+ lines.push(`- ${inlineCode(entry.command)}${description}`);
210
+ }
211
+ }
212
+ return `${lines.join("\n")}\n`;
213
+ }
214
+ writeConfigurationAndDocumentation(configFile, configContent, documentFile, documentContent, previousConfig) {
215
+ this.assertRegularFileOrMissing(configFile, configPath);
216
+ this.assertRegularFileOrMissing(documentFile, toolingPath);
217
+ let configTemp;
218
+ let documentTemp;
219
+ try {
220
+ configTemp = this.writeTemporary(configFile, configContent);
221
+ documentTemp = this.writeTemporary(documentFile, documentContent);
222
+ renameSync(configTemp, configFile);
223
+ try {
224
+ renameSync(documentTemp, documentFile);
225
+ }
226
+ catch (error) {
227
+ try {
228
+ this.restoreFile(configFile, previousConfig);
229
+ }
230
+ catch (rollbackError) {
231
+ throw new AggregateError([error, rollbackError], "Could not update the tooling guide and restore the previous project configuration.");
232
+ }
233
+ throw error;
234
+ }
235
+ }
236
+ finally {
237
+ if (configTemp)
238
+ this.removeTemporary(configTemp);
239
+ if (documentTemp)
240
+ this.removeTemporary(documentTemp);
241
+ }
242
+ }
243
+ writeTemporary(path, content) {
244
+ const temporary = `${path}.${randomUUID()}.tmp`;
245
+ writeFileSync(temporary, content, { flag: "wx" });
246
+ return temporary;
247
+ }
248
+ restoreFile(path, content) {
249
+ if (content === undefined) {
250
+ unlinkSync(path);
251
+ return;
252
+ }
253
+ writeFileSync(path, content);
254
+ }
255
+ removeTemporary(path) {
256
+ try {
257
+ unlinkSync(path);
258
+ }
259
+ catch (error) {
260
+ if (!isMissing(error))
261
+ throw error;
262
+ }
263
+ }
264
+ assertRegularFileOrMissing(path, relativePath) {
265
+ const entry = lstatSync(path, { throwIfNoEntry: false });
266
+ if (entry && !entry.isFile()) {
267
+ throw new Error(`${relativePath} must be a regular file, not a directory or symlink.`);
268
+ }
269
+ }
270
+ }
271
+ function inlineCode(value) {
272
+ const runs = value.match(/`+/g) ?? [];
273
+ const delimiter = "`".repeat(Math.max(0, ...runs.map((run) => run.length)) + 1);
274
+ return `${delimiter}${value}${delimiter}`;
275
+ }
276
+ function isMissing(error) {
277
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
278
+ }