@turri-dev/cli 0.0.0-stage → 0.1.2-next.48dacfc

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 (315) hide show
  1. package/.pi/themes/turri-dark.json +80 -0
  2. package/.pi/themes/turri-light.json +82 -0
  3. package/LICENSE +21 -0
  4. package/README.md +272 -2
  5. package/assets/jellyfish/dark-00.png +0 -0
  6. package/assets/jellyfish/dark-01.png +0 -0
  7. package/assets/jellyfish/dark-02.png +0 -0
  8. package/assets/jellyfish/dark-03.png +0 -0
  9. package/assets/jellyfish/dark-04.png +0 -0
  10. package/assets/jellyfish/dark-05.png +0 -0
  11. package/assets/jellyfish/dark-06.png +0 -0
  12. package/assets/jellyfish/dark-07.png +0 -0
  13. package/assets/jellyfish/dark-08.png +0 -0
  14. package/assets/jellyfish/dark-09.png +0 -0
  15. package/assets/jellyfish/dark-10.png +0 -0
  16. package/assets/jellyfish/dark-11.png +0 -0
  17. package/assets/jellyfish/dark-12.png +0 -0
  18. package/assets/jellyfish/dark-13.png +0 -0
  19. package/assets/jellyfish/dark-14.png +0 -0
  20. package/assets/jellyfish/fallback.txt +252 -0
  21. package/assets/jellyfish/light-00.png +0 -0
  22. package/assets/jellyfish/light-01.png +0 -0
  23. package/assets/jellyfish/light-02.png +0 -0
  24. package/assets/jellyfish/light-03.png +0 -0
  25. package/assets/jellyfish/light-04.png +0 -0
  26. package/assets/jellyfish/light-05.png +0 -0
  27. package/assets/jellyfish/light-06.png +0 -0
  28. package/assets/jellyfish/light-07.png +0 -0
  29. package/assets/jellyfish/light-08.png +0 -0
  30. package/assets/jellyfish/light-09.png +0 -0
  31. package/assets/jellyfish/light-10.png +0 -0
  32. package/assets/jellyfish/light-11.png +0 -0
  33. package/assets/jellyfish/light-12.png +0 -0
  34. package/assets/jellyfish/light-13.png +0 -0
  35. package/assets/jellyfish/light-14.png +0 -0
  36. package/bin/turri.mjs +29 -0
  37. package/dist/ask.js +149 -0
  38. package/dist/brand.js +50 -0
  39. package/dist/btw.js +174 -0
  40. package/dist/chat-models.js +37 -0
  41. package/dist/chat-skills.js +18 -0
  42. package/dist/cli.js +178 -0
  43. package/dist/config-command.js +197 -0
  44. package/dist/config-edit.js +367 -0
  45. package/dist/config.js +489 -0
  46. package/dist/conform-cli.js +29 -0
  47. package/dist/cultivate-cli.js +110 -0
  48. package/dist/drift-cli.js +25 -0
  49. package/dist/environment.js +70 -0
  50. package/dist/extension.js +2372 -0
  51. package/dist/genome.js +301 -0
  52. package/dist/graph.js +246 -0
  53. package/dist/identity.js +35 -0
  54. package/dist/init-config.js +511 -0
  55. package/dist/judge.js +62 -0
  56. package/dist/lineage-cli.js +13 -0
  57. package/dist/loop-cli.js +132 -0
  58. package/dist/loop-tools.js +225 -0
  59. package/dist/mature-tools.js +193 -0
  60. package/dist/model-check.js +117 -0
  61. package/dist/module-report.js +9 -0
  62. package/dist/pi-launch.js +31 -0
  63. package/dist/pi.js +813 -0
  64. package/dist/plain/check.js +76 -0
  65. package/dist/plain/concepts.js +213 -0
  66. package/dist/plain/errors.js +43 -0
  67. package/dist/plain/files.js +57 -0
  68. package/dist/plain/highlight.js +195 -0
  69. package/dist/plain/index.js +15 -0
  70. package/dist/plain/liquid.js +129 -0
  71. package/dist/plain/markdown.js +208 -0
  72. package/dist/plain/parser.js +752 -0
  73. package/dist/plain/plain.tmLanguage.json +124 -0
  74. package/dist/plain/spec.js +291 -0
  75. package/dist/plain/stdlib.js +44 -0
  76. package/dist/question-dialog.js +125 -0
  77. package/dist/questioning.js +109 -0
  78. package/dist/quiet.js +125 -0
  79. package/dist/regenerate/activity.js +187 -0
  80. package/dist/regenerate/agent-env.js +190 -0
  81. package/dist/regenerate/agent-shell.js +114 -0
  82. package/dist/regenerate/arbiter.js +133 -0
  83. package/dist/regenerate/audit.js +203 -0
  84. package/dist/regenerate/cases.js +83 -0
  85. package/dist/regenerate/change-view.js +102 -0
  86. package/dist/regenerate/choose-conformance.js +117 -0
  87. package/dist/regenerate/chunk-plan.js +625 -0
  88. package/dist/regenerate/codebase.js +75 -0
  89. package/dist/regenerate/command-check.js +312 -0
  90. package/dist/regenerate/compaction.js +20 -0
  91. package/dist/regenerate/confine.js +310 -0
  92. package/dist/regenerate/conform.js +133 -0
  93. package/dist/regenerate/conformance-agents.js +955 -0
  94. package/dist/regenerate/conformance.js +936 -0
  95. package/dist/regenerate/conversation-writes.js +100 -0
  96. package/dist/regenerate/credentials.js +88 -0
  97. package/dist/regenerate/debug-log.js +228 -0
  98. package/dist/regenerate/debugger.js +90 -0
  99. package/dist/regenerate/delivery.js +19 -0
  100. package/dist/regenerate/diagnose-job.js +40 -0
  101. package/dist/regenerate/diagnose.js +81 -0
  102. package/dist/regenerate/diff-view.js +240 -0
  103. package/dist/regenerate/drift.js +194 -0
  104. package/dist/regenerate/engine.js +118 -0
  105. package/dist/regenerate/environment-error.js +220 -0
  106. package/dist/regenerate/job-log.js +102 -0
  107. package/dist/regenerate/job-panel.js +336 -0
  108. package/dist/regenerate/jobs-tree.js +300 -0
  109. package/dist/regenerate/jobs.js +427 -0
  110. package/dist/regenerate/lanes.js +32 -0
  111. package/dist/regenerate/lineage.js +384 -0
  112. package/dist/regenerate/loop.js +1435 -0
  113. package/dist/regenerate/masking.js +95 -0
  114. package/dist/regenerate/notes.js +397 -0
  115. package/dist/regenerate/os-sandbox.js +243 -0
  116. package/dist/regenerate/paths.js +165 -0
  117. package/dist/regenerate/plain-palette.js +83 -0
  118. package/dist/regenerate/prompt.js +704 -0
  119. package/dist/regenerate/records.js +294 -0
  120. package/dist/regenerate/regenerate.js +598 -0
  121. package/dist/regenerate/regression.js +181 -0
  122. package/dist/regenerate/release.js +262 -0
  123. package/dist/regenerate/requires.js +241 -0
  124. package/dist/regenerate/resources.js +164 -0
  125. package/dist/regenerate/self-check.js +111 -0
  126. package/dist/regenerate/services.js +155 -0
  127. package/dist/regenerate/shell.js +320 -0
  128. package/dist/regenerate/side-panel.js +278 -0
  129. package/dist/regenerate/spec-map-view.js +574 -0
  130. package/dist/regenerate/spec-map.js +561 -0
  131. package/dist/regenerate/spec-steps.js +527 -0
  132. package/dist/regenerate/state.js +10 -0
  133. package/dist/regenerate/steer.js +91 -0
  134. package/dist/regenerate/steering.js +119 -0
  135. package/dist/regenerate/stop-report.js +161 -0
  136. package/dist/regenerate/submit-work.js +71 -0
  137. package/dist/regenerate/test-kinds.js +124 -0
  138. package/dist/regenerate/test-scripts.js +695 -0
  139. package/dist/regenerate/testing.js +406 -0
  140. package/dist/regenerate/transcript-view.js +1 -0
  141. package/dist/regenerate/unit-gate.js +20 -0
  142. package/dist/regenerate/unit-review.js +347 -0
  143. package/dist/regenerate/web.js +256 -0
  144. package/dist/regenerate/workspace.js +99 -0
  145. package/dist/regenerate-cli.js +68 -0
  146. package/dist/release-agent.js +114 -0
  147. package/dist/release-cli.js +131 -0
  148. package/dist/release-tools.js +148 -0
  149. package/dist/review/live.js +165 -0
  150. package/dist/rules.js +47 -0
  151. package/dist/self-invocation.js +110 -0
  152. package/dist/setup.js +245 -0
  153. package/dist/skill-progress.js +78 -0
  154. package/dist/skill.js +149 -0
  155. package/dist/skills.js +74 -0
  156. package/dist/spec-diff-cli.js +37 -0
  157. package/dist/spec-tools.js +155 -0
  158. package/dist/stack.js +158 -0
  159. package/dist/stacks.js +97 -0
  160. package/dist/stdlib.js +100 -0
  161. package/dist/terminal-images.js +46 -0
  162. package/dist/tools.js +242 -0
  163. package/dist/validate-cli.js +52 -0
  164. package/docs/README.md +58 -0
  165. package/docs/authoring-plain.md +245 -0
  166. package/docs/config.md +353 -0
  167. package/docs/deploy.md +109 -0
  168. package/docs/development.md +88 -0
  169. package/docs/headless.md +148 -0
  170. package/docs/images/how-turri-works.svg +50 -0
  171. package/docs/images/turri-banner.png +0 -0
  172. package/docs/installation.md +119 -0
  173. package/docs/parser.md +108 -0
  174. package/docs/regenerating.md +1116 -0
  175. package/docs/sandbox.md +233 -0
  176. package/docs/skills-and-gates.md +254 -0
  177. package/docs/theming.md +76 -0
  178. package/docs/using.md +454 -0
  179. package/genome/rules/bullet-continuation.md +69 -0
  180. package/genome/rules/concise-specs-examples.md +431 -0
  181. package/genome/rules/concise-specs-integration-examples.md +270 -0
  182. package/genome/rules/concise-specs.md +80 -0
  183. package/genome/rules/definitions.md +107 -0
  184. package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +163 -0
  185. package/genome/rules/examples/integration-embedded-testing/run_unittests_java.sh +51 -0
  186. package/genome/rules/exported-concepts.md +39 -0
  187. package/genome/rules/func-specs.md +129 -0
  188. package/genome/rules/impl-reqs.md +68 -0
  189. package/genome/rules/import-modules.md +48 -0
  190. package/genome/rules/integration-embedded-testing.md +276 -0
  191. package/genome/rules/integration-embedded.md +171 -0
  192. package/genome/rules/integration-standalone.md +135 -0
  193. package/genome/rules/integrations.md +137 -0
  194. package/genome/rules/linked-resources.md +69 -0
  195. package/genome/rules/module-structure.md +105 -0
  196. package/genome/rules/project-model.md +88 -0
  197. package/genome/rules/required-concepts.md +44 -0
  198. package/genome/rules/requires-modules.md +80 -0
  199. package/genome/rules/stdlib.md +73 -0
  200. package/genome/rules/test-reqs.md +57 -0
  201. package/genome/skills/add-acceptance-test/SKILL.md +100 -0
  202. package/genome/skills/add-concept/SKILL.md +71 -0
  203. package/genome/skills/add-functional-spec/SKILL.md +84 -0
  204. package/genome/skills/add-functional-specs/SKILL.md +118 -0
  205. package/genome/skills/add-implementation-requirement/SKILL.md +86 -0
  206. package/genome/skills/add-resource/SKILL.md +105 -0
  207. package/genome/skills/add-template/SKILL.md +64 -0
  208. package/genome/skills/add-test-requirement/SKILL.md +73 -0
  209. package/genome/skills/analyze-2-func-specs/SKILL.md +101 -0
  210. package/genome/skills/analyze-func-specs/SKILL.md +123 -0
  211. package/genome/skills/analyze-if-func-spec-too-complex/SKILL.md +211 -0
  212. package/genome/skills/break-down-func-spec/SKILL.md +205 -0
  213. package/genome/skills/brood/SKILL.md +229 -0
  214. package/genome/skills/brood/references/intent.md +35 -0
  215. package/genome/skills/brood/references/product.md +101 -0
  216. package/genome/skills/brood/references/stack.md +61 -0
  217. package/genome/skills/brood/references/testing.md +56 -0
  218. package/genome/skills/bud/SKILL.md +148 -0
  219. package/genome/skills/bud/references/checklist.md +44 -0
  220. package/genome/skills/bud/references/intent.md +30 -0
  221. package/genome/skills/check-plain-env/SKILL.md +285 -0
  222. package/genome/skills/consolidate-concepts/SKILL.md +192 -0
  223. package/genome/skills/create-import-module/SKILL.md +104 -0
  224. package/genome/skills/create-requires-module/SKILL.md +109 -0
  225. package/genome/skills/debug-specs/SKILL.md +188 -0
  226. package/genome/skills/implement-conformance-testing-script/SKILL.md +238 -0
  227. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_cypress.ps1 +337 -0
  228. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_golang.ps1 +111 -0
  229. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +136 -0
  230. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.ps1 +103 -0
  231. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +150 -0
  232. package/genome/skills/implement-unit-testing-script/SKILL.md +160 -0
  233. package/genome/skills/implement-unit-testing-script/assets/run_unittests_flutter.ps1 +87 -0
  234. package/genome/skills/implement-unit-testing-script/assets/run_unittests_golang.ps1 +73 -0
  235. package/genome/skills/implement-unit-testing-script/assets/run_unittests_java.sh +72 -0
  236. package/genome/skills/implement-unit-testing-script/assets/run_unittests_python.ps1 +81 -0
  237. package/genome/skills/implement-unit-testing-script/assets/run_unittests_python.sh +98 -0
  238. package/genome/skills/implement-unit-testing-script/assets/run_unittests_react.ps1 +87 -0
  239. package/genome/skills/init-plain-project/SKILL.md +117 -0
  240. package/genome/skills/init-plain-project/references/checklist.md +30 -0
  241. package/genome/skills/mature/SKILL.md +126 -0
  242. package/genome/skills/refactor-module/SKILL.md +196 -0
  243. package/genome/skills/resolve-section-ownership/SKILL.md +64 -0
  244. package/genome/skills/resolve-spec-conflict/SKILL.md +96 -0
  245. package/genome/skills/spawn/SKILL.md +163 -0
  246. package/package.json +68 -4
  247. package/stdlib/backend/common.plain +25 -0
  248. package/stdlib/backend/conformance/common.plain +12 -0
  249. package/stdlib/backend/conformance/go-gin.plain +9 -0
  250. package/stdlib/backend/conformance/go-nethttp.plain +9 -0
  251. package/stdlib/backend/conformance/java-springboot.plain +9 -0
  252. package/stdlib/backend/conformance/python-django.plain +9 -0
  253. package/stdlib/backend/conformance/python-fastapi.plain +9 -0
  254. package/stdlib/backend/conformance/python-flask.plain +9 -0
  255. package/stdlib/backend/conformance/rust-axum.plain +9 -0
  256. package/stdlib/backend/conformance/typescript-express.plain +9 -0
  257. package/stdlib/backend/conformance/typescript-fastify.plain +9 -0
  258. package/stdlib/backend/conformance/typescript-nestjs.plain +9 -0
  259. package/stdlib/backend/go-gin.plain +18 -0
  260. package/stdlib/backend/go-nethttp.plain +18 -0
  261. package/stdlib/backend/java-springboot.plain +19 -0
  262. package/stdlib/backend/python-django.plain +19 -0
  263. package/stdlib/backend/python-fastapi.plain +23 -0
  264. package/stdlib/backend/python-flask.plain +23 -0
  265. package/stdlib/backend/rust-axum.plain +17 -0
  266. package/stdlib/backend/typescript-express.plain +19 -0
  267. package/stdlib/backend/typescript-fastify.plain +19 -0
  268. package/stdlib/backend/typescript-nestjs.plain +19 -0
  269. package/stdlib/cli/common.plain +17 -0
  270. package/stdlib/cli/conformance/common.plain +11 -0
  271. package/stdlib/cli/conformance/go-cobra.plain +9 -0
  272. package/stdlib/cli/conformance/go-flag.plain +9 -0
  273. package/stdlib/cli/conformance/java-picocli.plain +9 -0
  274. package/stdlib/cli/conformance/python-argparse.plain +9 -0
  275. package/stdlib/cli/conformance/python-click.plain +9 -0
  276. package/stdlib/cli/conformance/python-typer.plain +9 -0
  277. package/stdlib/cli/conformance/rust-clap.plain +9 -0
  278. package/stdlib/cli/conformance/typescript-commander.plain +9 -0
  279. package/stdlib/cli/go-cobra.plain +14 -0
  280. package/stdlib/cli/go-flag.plain +14 -0
  281. package/stdlib/cli/java-picocli.plain +15 -0
  282. package/stdlib/cli/python-argparse.plain +18 -0
  283. package/stdlib/cli/python-click.plain +16 -0
  284. package/stdlib/cli/python-typer.plain +16 -0
  285. package/stdlib/cli/rust-clap.plain +14 -0
  286. package/stdlib/cli/typescript-commander.plain +17 -0
  287. package/stdlib/frontend/angular.plain +17 -0
  288. package/stdlib/frontend/common.plain +17 -0
  289. package/stdlib/frontend/conformance/angular.plain +5 -0
  290. package/stdlib/frontend/conformance/common.plain +11 -0
  291. package/stdlib/frontend/conformance/react-vite.plain +5 -0
  292. package/stdlib/frontend/conformance/sveltekit-spa.plain +5 -0
  293. package/stdlib/frontend/conformance/vue-vite.plain +5 -0
  294. package/stdlib/frontend/react-vite.plain +16 -0
  295. package/stdlib/frontend/sveltekit-spa.plain +16 -0
  296. package/stdlib/frontend/vue-vite.plain +16 -0
  297. package/stdlib/lang/go.plain +13 -0
  298. package/stdlib/lang/java.plain +12 -0
  299. package/stdlib/lang/python.plain +18 -0
  300. package/stdlib/lang/rust.plain +12 -0
  301. package/stdlib/lang/typescript.plain +13 -0
  302. package/stdlib/library/common.plain +15 -0
  303. package/stdlib/library/conformance/common.plain +9 -0
  304. package/stdlib/library/conformance/go.plain +9 -0
  305. package/stdlib/library/conformance/java-gradle.plain +9 -0
  306. package/stdlib/library/conformance/java-maven.plain +9 -0
  307. package/stdlib/library/conformance/python.plain +9 -0
  308. package/stdlib/library/conformance/rust.plain +9 -0
  309. package/stdlib/library/conformance/typescript.plain +9 -0
  310. package/stdlib/library/go.plain +12 -0
  311. package/stdlib/library/java-gradle.plain +13 -0
  312. package/stdlib/library/java-maven.plain +13 -0
  313. package/stdlib/library/python.plain +13 -0
  314. package/stdlib/library/rust.plain +12 -0
  315. package/stdlib/library/typescript.plain +15 -0
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: add-implementation-requirement
3
+ description: >-
4
+ Add an implementation requirement to the ***implementation reqs*** section of
5
+ a ***plain spec file. Use when the user wants to add non-functional
6
+ requirements like technology choices, architectural constraints, coding
7
+ standards, data formats, error handling strategies, or any HOW-to-build
8
+ guidance to a .plain file.
9
+ allowed-tools: [read, write, edit, bash, ls, grep, find]
10
+ ---
11
+
12
+ # Add Implementation Requirement
13
+
14
+ ## Workflow
15
+
16
+ 1. **Identify the target `.plain` file.** If ambiguous, ask the user.
17
+ 2. **Read the file** to understand existing implementation reqs, definitions, and functional specs.
18
+ 3. **Determine if this belongs in implementation reqs** — it must describe HOW to build, not WHAT to build (that goes in `***functional specs***`).
19
+ 4. **Draft the requirement** following the rules below.
20
+ 5. **Insert it** into the `***implementation reqs***` section.
21
+ 6. **Read the file again** to confirm correct placement and syntax.
22
+
23
+ ## What Belongs Here
24
+
25
+ Implementation reqs are free-form instructions that steer code generation. Common contents:
26
+
27
+ - **Technology choices**: language, framework, runtime version
28
+ - **Architectural constraints**: patterns, layering, dependency rules
29
+ - **Coding standards**: naming conventions, style guidelines
30
+ - **Data formats**: serialization, encoding, transformation rules
31
+ - **Error handling**: strategies, retry logic, exception hierarchies
32
+ - **Algorithm descriptions**: specific approaches when behavior alone is insufficient
33
+ - **Performance guidance**: memory constraints, streaming requirements, batching strategies
34
+ - **Language-specific constructs**: generics, annotations, framework-specific types and idioms
35
+
36
+ ## What Does NOT Belong Here
37
+
38
+ - **Behavior and features** — those go in `***functional specs***`
39
+ - **Concept definitions** — those go in `***definitions***`
40
+ - **Conformance-test instructions** — those go in `***test reqs***`. Note: **unit-test instructions belong HERE**, not in `***test reqs***` — see *Unit-test guidance belongs here* below
41
+
42
+ ## Unit-test guidance belongs here
43
+
44
+ **Everything about `:UnitTests:` goes in `***implementation reqs***`** — paths, approach, packages, framework (JUnit / pytest / Jest / Go's `testing` / …), conventions, fixtures, mocking policy, file layout, naming, lint / static-analysis gates. Unit tests are part of the generated codebase, so requirements that shape them are implementation reqs by definition.
45
+
46
+ - The unit-test generator reads **only** `***implementation reqs***`; anything about `:UnitTests:` placed in `***test reqs***` is silently ignored
47
+ - Phrase each `:UnitTests:` requirement in terms of the predefined `:UnitTests:` concept so the partition stays visible at a glance
48
+ - `***test reqs***` is exclusively for `:ConformanceTests:` — see [`add-test-requirement`](../add-test-requirement/SKILL.md)
49
+
50
+ ## Key Principle: HOW vs WHAT
51
+
52
+ `***implementation reqs***` describe HOW the software should be built.
53
+ `***functional specs***` describe WHAT the software should do.
54
+
55
+ If the requirement describes observable behavior (endpoints, business rules, user-facing features), it belongs in functional specs. If it describes internal structure, technology, or coding guidance, it belongs here.
56
+
57
+ ## Format
58
+
59
+ Implementation reqs are bullet points in the `***implementation reqs***` section:
60
+
61
+ ```plain
62
+ ***implementation reqs***
63
+
64
+ - :Implementation: should be in Python 3.12.
65
+
66
+ - :Implementation: should use pip for dependency management.
67
+
68
+ - :ReportExporter: writes a :CsvFile: with streaming writes, never holding the full dataset in memory.
69
+
70
+ ```
71
+
72
+ Reference defined `:Concepts:` where they add clarity. Implementation reqs in non-leaf sections apply to all subsections.
73
+
74
+
75
+ ## Encapsulation Warning
76
+
77
+ `requires` modules only receive functional specs from their dependencies — not implementation reqs. If downstream modules need certain behavior to be visible, that behavior must be expressed in functional specs, not here.
78
+
79
+ ## Validation Checklist
80
+
81
+ - [ ] Describes HOW to build, not WHAT to build
82
+ - [ ] All referenced `:Concepts:` are defined in `***definitions***`
83
+ - [ ] Does not duplicate guidance already present in the file or its imports
84
+ - [ ] Trimmed per `concise-specs.md`: plain words, no rationale, no standard behavior of a technology already named
85
+ - [ ] Placed inside a `***implementation reqs***` section
86
+ - [ ] No behavioral requirements that should be in `***functional specs***`
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: add-resource
3
+ description: >-
4
+ Add a linked resource (external file reference) to a ***plain spec. Use when
5
+ the user wants to reference a JSON schema, API spec, data file, or other
6
+ external file from within a functional spec, definition, or implementation
7
+ requirement.
8
+ allowed-tools: [read, write, edit, bash, ls, grep, find]
9
+ ---
10
+
11
+ # Add Resource
12
+
13
+ Linked resources are external files referenced from within a `.plain` spec using markdown link syntax. The file contents are passed to the renderer alongside the spec, providing additional context for code generation.
14
+
15
+ ## Workflow
16
+
17
+ 1. **Identify or create the resource file.** Keep it inside the project — the `resources/` directory is the conventional home; it does not have to sit next to the `.plain` file.
18
+ 2. **Add the markdown link** at the appropriate place in the spec.
19
+ 3. **Verify the file path** is relative to the `.plain` file location.
20
+ 4. **Read the file back** to confirm correct link syntax and path.
21
+
22
+ ## Format
23
+
24
+ Use standard markdown link syntax inside any spec section:
25
+
26
+ ```plain
27
+ ***definitions***
28
+ - :AuthData: is the authentication data structure.
29
+ - Its format is defined in the [auth schema](resources/auth_schema.json).
30
+
31
+ ***implementation reqs***
32
+ - When transforming :BackupData:, use the JOLT transform.
33
+ - The transform is defined in [backup_transform.jolt](resources/backup_transform.jolt).
34
+
35
+ ***functional specs***
36
+
37
+ - An API conforming to the [API specification](resources/api_spec.yaml) is exposed.
38
+ ```
39
+
40
+ ## Path Rules
41
+
42
+ - Paths are resolved **relative to the directory where `turri` is run** (the current working directory).
43
+ - Relative paths may traverse into parent directories — `[resource](../../resources/resource.md)` is valid — as long as they resolve to a real text file on disk.
44
+ - **No external URLs** — only local file references.
45
+ - **No folder paths** — the target must be a file, not a directory.
46
+ - Only text-based files are supported.
47
+
48
+ ### A linked resource MUST be a single, text-based file that exists on disk
49
+
50
+ A linked resource is **always** a single file that exists on disk at the path you reference, **and** that file's content must be text-based. The renderer reads the linked file's bytes and feeds them into the model alongside the spec; a directory, a remote URL, or a binary blob breaks that contract and is one of the most disruptive mistakes you can make when authoring a `.plain` file — the spec looks valid but the renderer either silently ignores the link, fails to read it, or wastes the model's context window on bytes it cannot interpret.
51
+
52
+ Three things a linked resource **must not** be:
53
+
54
+ 1. **A folder / directory.** `[integrations](src/integrations/)`, `[schemas](resources/schemas/)`, `[host project](../host_project/)` are all invalid — the renderer cannot ingest a directory. If a whole directory's worth of content is relevant, pick the single most representative **file** inside it (a `README.md`, an exemplar source file, a manifest at the directory root) and link **that**.
55
+ 2. **A URL / external location.** `[Stripe docs](https://stripe.com/docs/api)`, `[OpenAPI spec](https://example.com/openapi.json)`, any `http://` / `https://` / `ftp://` / `git://` / `s3://` / `gs://` target. Linked resources are local-file only. If a URL's content is essential to the spec, fetch it once, save the response to a text file under `resources/` (e.g. `resources/stripe-docs-snapshot.md`, `resources/example-openapi.yaml`), and link **that file**.
56
+ 3. **A binary file.** PNG, JPG, JPEG, GIF, BMP, TIFF, WebP, ICO, PDF, DOCX, XLSX, PPTX, ZIP, TAR, GZ, MP3, MP4, WAV, compiled binaries (`.exe`, `.so`, `.dylib`, `.class`, `.wasm`), and anything else that isn't human-readable text in its raw form. Binary content cannot be meaningfully consumed by the renderer; linking a screenshot, a PDF spec, or a packaged artifact accomplishes nothing except bloating the context. If the information in a binary asset is essential, transcribe it into a text-based form first — a UI screenshot becomes a Markdown description or a structured YAML wireframe under `resources/`; a PDF spec becomes a Markdown extract or the underlying JSON Schema / OpenAPI; an architecture diagram becomes a Mermaid block inside a Markdown file.
57
+
58
+ If the markdown-link target ends with `/`, contains `://`, points at a path that resolves to a directory, or points at a file with a binary extension (see the list above), **stop** — it cannot be a linked resource. Convert it to a text file under `resources/` first, then link the converted file.
59
+
60
+ ### Do not mention URLs or folder paths in `.plain` content at all
61
+
62
+ The constraint above is **not** just about markdown link syntax. URLs (any `http://`, `https://`, `ftp://`, `git://`, `s3://`, … string) and folder paths (`src/integrations/`, `../host_project/`, anything ending with `/`, anything that resolves to a directory) **must not appear anywhere in `.plain` content** — not as link targets, not in concept body prose, not in functional-spec text, not in implementation-reqs. Mentioning a URL or a folder in prose is a critical and common mistake because:
63
+
64
+ - The renderer cannot follow URLs or open folders. A URL or folder reference in prose is a *ghost* dependency: it looks meaningful to a human reader, but it contributes nothing to code generation. Worse, downstream readers (and future you) assume the renderer used the referenced content, so the spec drifts from reality.
65
+ - The fix is always the same: if external content matters, fetch it (or pick one canonical file out of the directory), save it as a text file under `resources/`, and refer to it through a normal linked resource. The concept or spec then names the content through the linked file, not through a URL or folder path string.
66
+
67
+ The **only** exceptions are URLs and paths that are *values the produced software itself uses at runtime* (e.g. the base URL the integration calls, a database connection path, a CLI argument value). Those are configuration values, not external references, and they belong in the spec because the generated code needs them. A useful litmus test: "Would the renderer benefit from reading the bytes at this URL / folder?" If yes, save it to a text file and link the file. If no (it's a runtime value), it can stay as plain text in the spec.
68
+
69
+ ### What a linked resource CAN be
70
+
71
+ - A single file inside the project, reachable by a path relative to where `turri` is run — parent-directory traversal (`../`) is allowed (`resources/` is the conventional home).
72
+ - A **text-based** file the renderer can read end-to-end: JSON, YAML, XML, HTML, Markdown, plain text, CSV, TSV, source code in any language (`.py`, `.js`, `.ts`, `.go`, `.java`, `.rb`, `.rs`, `.kt`, `.swift`, `.c`, `.cpp`, `.cs`, …), shell scripts, SQL, JSON Schema, OpenAPI, AsyncAPI, Protobuf `.proto`, GraphQL SDL, `.jolt`, `.env.example`, `.toml`, `.ini`, `.proto`, `Dockerfile`, etc.
73
+
74
+ ## Common Resource Types
75
+
76
+ | Type | Typical location | Use case |
77
+ |------|-----------------|----------|
78
+ | JSON Schema | `resources/*.json` | Defining data structure contracts |
79
+ | OpenAPI / Swagger spec | `resources/*.yaml` | API endpoint definitions |
80
+ | Data transforms | `resources/*.jolt` | Data transformation rules |
81
+ | Test fixtures | `resources/*.json`, `resources/*.csv` | Sample data for tests |
82
+ | Configuration examples | `resources/*.yaml` | Reference configurations |
83
+
84
+ ## When to Use Resources
85
+
86
+ - The information is too detailed or structured to express inline in the spec (e.g., a full JSON schema).
87
+ - The same data is referenced by multiple specs or sections.
88
+ - The resource is an industry-standard format (OpenAPI, JSON Schema) that the renderer can interpret directly.
89
+
90
+ ## When NOT to Use Resources
91
+
92
+ - The information is short enough to include inline in the spec text.
93
+ - The file is generated code (those belong in `plain_modules/`, not `resources/`).
94
+
95
+ ## Validation Checklist
96
+
97
+ - [ ] Resource file exists at the specified path
98
+ - [ ] **Target is a file on disk, not a directory** (the path does not end in `/` and does not resolve to a folder)
99
+ - [ ] **Target is a local path, not a URL** (no `://` anywhere in the target: no `http://`, `https://`, `ftp://`, `git://`, `s3://`, `gs://`, etc.)
100
+ - [ ] **Target is a text-based file** (no binary extensions: `.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.tiff`, `.webp`, `.ico`, `.pdf`, `.docx`, `.xlsx`, `.pptx`, `.zip`, `.tar`, `.gz`, `.mp3`, `.mp4`, `.wav`, `.exe`, `.so`, `.dylib`, `.class`, `.wasm`, …)
101
+ - [ ] **No URLs or folder paths anywhere in the surrounding `.plain` content** (not as link targets, not in body prose), with the sole exception of URLs / paths that are runtime values the generated software itself uses
102
+ - [ ] Path is relative to the `.plain` file, not absolute
103
+ - [ ] Path resolves to a real text file on disk from where `turri` is run (`../` traversal is allowed)
104
+ - [ ] Markdown link syntax is correct: `[display text](relative/path)`
105
+ - [ ] Resource content is relevant and adds value beyond what the spec text says
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: add-template
3
+ description: >-
4
+ Create or include a Liquid template in a ***plain spec file using
5
+ {% include %} syntax. Use when the same spec content needs to be reused
6
+ across multiple .plain files with different parameters.
7
+ allowed-tools: [read, write, edit, bash, ls, grep, find]
8
+ ---
9
+
10
+ # Add Template
11
+
12
+ Templates use Liquid syntax (`{% include %}`) to reuse the same spec content across multiple `.plain` files. Each inclusion can pass different parameters, producing tailored output from a single source. This is distinct from `import` and `requires` — templates are about textual reuse with parameterization, not module dependencies.
13
+
14
+ ## When to Use Templates
15
+
16
+ - The **exact same spec structure** is needed in more than one `.plain` file.
17
+ - The reused content differs only in specific values (names, paths, config) that can be parameterized.
18
+ - You want a single source of truth for a pattern that appears in multiple modules.
19
+
20
+ ## Format
21
+
22
+ ### Including a Template
23
+
24
+ Use `{% include %}` with the template path and parameters:
25
+
26
+ ```plain
27
+ {% include "console_app_template.plain", main_file_name: "app.py", app_name: "MyApp" %}
28
+ ```
29
+
30
+ Parameters are passed as key-value pairs after the template path. Inside the template, parameters are accessed using Liquid variable syntax (`{{ main_file_name }}`).
31
+
32
+ ### Writing a Template
33
+
34
+ Templates live in the `template/` directory. Only **variables** (`{{ variable_name }}`) are supported — conditionals, loops, and other Liquid features are not available:
35
+
36
+ ```plain
37
+ ***definitions***
38
+ - :{{ app_name }}MainFile: is the entry point file for :{{ app_name }}:.
39
+
40
+ ***implementation reqs***
41
+ - :{{ app_name }}MainFile: should be called "{{ main_file_name }}".
42
+ ```
43
+
44
+ ## Workflow: Including an Existing Template
45
+
46
+ 1. **Check the `template/` directory** to see what templates are available.
47
+ 2. **Read the template** to understand what parameters it expects and what content it produces.
48
+ 3. **Add the `{% include %}` statement** to the target `.plain` file with the correct parameters.
49
+ 4. **Verify** that the expanded content does not introduce concept name collisions or duplicate sections.
50
+
51
+ ## Workflow: Creating a New Template
52
+
53
+ 1. **Identify the repeated pattern** — find spec content that is duplicated (or will be duplicated) across multiple `.plain` files.
54
+ 2. **Extract the common content** into a new `.plain` file in the `template/` directory.
55
+ 3. **Parameterize the differences** — replace the varying parts with Liquid variables.
56
+ 4. **Update the original files** to use `{% include %}` instead of the duplicated content.
57
+
58
+ ## Validation Checklist
59
+
60
+ - [ ] Template file is in the `template/` directory
61
+ - [ ] All varying parts are parameterized with Liquid variables
62
+ - [ ] All required parameters are passed at each `{% include %}` call site
63
+ - [ ] Expanded content does not introduce concept name collisions
64
+ - [ ] Template is used by more than one `.plain` file (otherwise, inline the content)
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: add-test-requirement
3
+ description: >-
4
+ Add a test requirement to the ***test reqs*** section of a ***plain spec
5
+ file. Use when the user wants to specify conformance testing instructions
6
+ like test frameworks, execution methods, or testing constraints in a .plain
7
+ file.
8
+ allowed-tools: [read, write, edit, bash, ls, grep, find]
9
+ ---
10
+
11
+ # Add Test Requirement
12
+
13
+ ## Workflow
14
+
15
+ 1. **Identify the target `.plain` file.** If ambiguous, ask the user.
16
+ 2. **Read the file** to understand existing test reqs and the overall spec structure.
17
+ 3. **Confirm this is a conformance test concern** — unit test guidance goes in `***implementation reqs***`, not here.
18
+ 4. **Draft the requirement** following the rules below.
19
+ 5. **Insert it** into the `***test reqs***` section.
20
+ 6. **Read the file again** to confirm correct placement and syntax.
21
+
22
+ ## What Belongs Here
23
+
24
+ Test reqs specify how **conformance tests** should be written and run. Common contents:
25
+
26
+ - **Test framework**: which framework to use (e.g., pytest, Unittest, xUnit)
27
+ - **Execution method**: the command to run the tests
28
+ - **Testing constraints**: what must or must not be done in tests (e.g., no skipping tests, mock requirements)
29
+ - **Test data**: how test fixtures or mock data should be structured
30
+ - **Environment setup**: any prerequisites for running conformance tests
31
+
32
+ ## What Does NOT Belong Here
33
+
34
+ - **Unit test guidance** — that goes in `***implementation reqs***`
35
+ - **Acceptance tests** — those are nested under individual functional specs in `***functional specs***`
36
+ - **Behavioral requirements** — those go in `***functional specs***`
37
+ - **Technology choices for the implementation** — those go in `***implementation reqs***`
38
+
39
+ ## Conformance Tests vs Other Tests
40
+
41
+ | Test Type | Where to Specify | Purpose |
42
+ |-----------|-----------------|---------|
43
+ | Unit tests | `***implementation reqs***` | Test individual functionalities in isolation |
44
+ | Conformance tests | `***test reqs***` | Verify implementation conforms to the full spec |
45
+ | Acceptance tests | `***acceptance tests***` under a functional spec | Verify a specific functional spec |
46
+
47
+ ## Format
48
+
49
+ Test reqs are bullet points in the `***test reqs***` section:
50
+
51
+ ```plain
52
+ ***test reqs***
53
+
54
+ - :ConformanceTests: should be implemented using pytest framework.
55
+
56
+ - :ConformanceTests: will be run using "pytest" command.
57
+
58
+ - :ConformanceTests: must be implemented and executed - do not skip tests.
59
+
60
+ - :ConformanceTests: should mock all external HTTP calls.
61
+ ```
62
+
63
+ Reference predefined concepts like `:ConformanceTests:` and any defined `:Concepts:` where they add clarity.
64
+
65
+
66
+ ## Validation Checklist
67
+
68
+ - [ ] Describes conformance testing concerns, not unit tests or behavior
69
+ - [ ] All referenced `:Concepts:` are defined (or are predefined like `:ConformanceTests:`)
70
+ - [ ] Does not duplicate guidance already present in the file or its imports
71
+ - [ ] Trimmed per `concise-specs.md`: plain words, no rationale, no default framework behavior
72
+ - [ ] Placed inside a `***test reqs***` section
73
+ - [ ] No behavioral requirements that should be in `***functional specs***`
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: analyze-2-func-specs
3
+ description: >-
4
+ Analyze two functional specs from a ***plain spec file to determine if they
5
+ conflict. Use when the user wants to check whether two specific functional
6
+ requirements are compatible, or when debugging a suspected conflict between
7
+ two specs.
8
+ allowed-tools: [read, ls, grep, find]
9
+ ---
10
+
11
+ # Analyze Two Functional Specs
12
+
13
+ ## Input
14
+
15
+ Two functional specs from a `.plain` file (or across `requires` modules). The user provides them directly or points to them by location in a file.
16
+
17
+ ## Workflow
18
+
19
+ 1. **Read both functional specs** and the `***definitions***` section so all referenced `:Concepts:` are understood.
20
+ 2. **Determine chronological order** — which spec comes first? The earlier spec was rendered first and the later spec was rendered with the earlier one in context.
21
+ 3. **Run the conflict analysis** using the checklist below.
22
+ 4. **Output the verdict and nothing else** — either `COMPATIBLE` or `CONFLICTING`. No reasoning, no category labels, no resolution suggestions.
23
+
24
+ ## Conflict Analysis Checklist
25
+
26
+ Work through each question. If any answer is "yes", the specs likely conflict.
27
+
28
+ ### 1. Direct Contradiction
29
+
30
+ Do the two specs make mutually exclusive assertions about the same behavior?
31
+
32
+ ```
33
+ Spec A: :Resource: items are returned sorted by name in ascending order.
34
+ Spec B: :Resource: items are returned sorted by creation date in descending order.
35
+
36
+ Verdict: CONFLICTING — both define the sort order for the same response,
37
+ but specify different fields and directions. A single implementation cannot
38
+ satisfy both unless scoped to different contexts.
39
+ ```
40
+
41
+ ### 2. State or Data Conflict
42
+
43
+ Does one spec set a state or value that the other spec assumes is different?
44
+
45
+ ```
46
+ Spec A: :TaskList: is initially empty.
47
+ Spec B: :TaskList: contains a default "Welcome" :Task: on first load.
48
+
49
+ Verdict: CONFLICTING — both define the initial state of :TaskList: differently.
50
+ ```
51
+
52
+ ### 3. Behavioral Override
53
+
54
+ Does the later spec silently replace behavior established by the earlier spec without acknowledging it?
55
+
56
+ ```
57
+ Spec A: :User: credentials are validated using an API key.
58
+ Spec B: :User: credentials are validated using OAuth 2.0.
59
+
60
+ Verdict: CONFLICTING — both define the authentication mechanism but pick
61
+ different approaches. The later spec overrides the earlier one.
62
+ ```
63
+
64
+ ### 4. Scope Ambiguity
65
+
66
+ Are the two specs ambiguous enough that a renderer could interpret them as conflicting, even if the user intends them to be complementary?
67
+
68
+ ```
69
+ Spec A: All :Resource: items are returned.
70
+ Spec B: Only active :Resource: items are returned.
71
+
72
+ Verdict: CONFLICTING (ambiguous) — "all" vs "only active" appear contradictory.
73
+ Could be resolved by scoping each to different conditions (e.g., filtered vs unfiltered).
74
+ ```
75
+
76
+ ### 5. Shared Concept, Different Constraints
77
+
78
+ Do both specs impose constraints on the same `:Concept:` that cannot coexist?
79
+
80
+ ```
81
+ Spec A: :BatchSize: is 100 items.
82
+ Spec B: :BatchSize: is 50 items for :Resource: types with attachments.
83
+
84
+ Verdict: COMPATIBLE — Spec B adds a conditional refinement, not a contradiction.
85
+ ```
86
+
87
+ ## Output Format
88
+
89
+ The skill emits exactly one of these two strings, with no surrounding text, explanation, category label, or resolution suggestion:
90
+
91
+ ```
92
+ COMPATIBLE
93
+ ```
94
+
95
+ or
96
+
97
+ ```
98
+ CONFLICTING
99
+ ```
100
+
101
+ The internal analysis (checklist, chronological reasoning) informs the verdict but must not appear in the output. The caller decides what to do with the result — typically: act on `COMPATIBLE` by proceeding, or invoke `resolve-spec-conflict` on `CONFLICTING` to produce the actual fix.
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: analyze-func-specs
3
+ description: >-
4
+ Analyze a batch of functional specs from a ***plain spec file to determine
5
+ which pairs conflict. Replaces the pair-by-pair `analyze-2-func-specs` flow
6
+ when a caller wants to check many specs at once (e.g. a new spec against
7
+ every existing spec, or a freshly inserted batch against itself).
8
+ allowed-tools: [read, ls, grep, find]
9
+ ---
10
+
11
+ # Analyze Functional Specs
12
+
13
+ ## Input
14
+
15
+ A **set of two or more functional specs** to check against each other. Callers typically supply one of:
16
+
17
+ - A whole `***functional specs***` section to self-check.
18
+ - A new spec (or batch of new specs) **plus** the existing specs from the same file and any `requires` modules.
19
+ - An explicit list of spec snippets the user wants compared.
20
+
21
+ Each spec must be identifiable. If the caller didn't label them, assign **stable identifiers** in order of appearance: `S1`, `S2`, `S3`, …. Use those identifiers in the output. If the caller provided their own IDs or quoted snippets, use those instead and keep them verbatim.
22
+
23
+ The skill also needs the `***definitions***` section (and any imported definitions) so referenced `:Concepts:` can be resolved.
24
+
25
+ ## Workflow
26
+
27
+ 1. **Read all input specs and the relevant `***definitions***`** so every `:Concept:` is understood.
28
+ 2. **Establish chronological order** across the batch. Earlier specs are considered to have been rendered first; later specs are evaluated with the earlier ones in context. If the caller already provided an order, preserve it.
29
+ 3. **Generate the pair list.** Enumerate every unordered pair `(Sᵢ, Sⱼ)` with `i < j`. Skip pairs where the two specs share no `:Concept:` and no overlapping subject — they are trivially compatible and listing them adds noise.
30
+ 4. **Run the conflict analysis below on each remaining pair.** Use the same checklist that the legacy `analyze-2-func-specs` skill used, but apply it pair by pair across the batch in a single pass — do not require the caller to invoke the analyzer once per pair.
31
+ 5. **Record every CONFLICTING pair.** Compatible pairs are not listed individually; only the conflicts are emitted.
32
+ 6. **Output the verdict** in the format below. No reasoning, no category labels, no resolution suggestions.
33
+
34
+ ## Conflict Analysis Checklist (applied per pair)
35
+
36
+ Work through each question for the pair under inspection. If any answer is "yes", that pair is CONFLICTING.
37
+
38
+ ### 1. Direct Contradiction
39
+
40
+ Do the two specs make mutually exclusive assertions about the same behavior?
41
+
42
+ ```
43
+ Spec A: :Resource: items are returned sorted by name in ascending order.
44
+ Spec B: :Resource: items are returned sorted by creation date in descending order.
45
+
46
+ Verdict: CONFLICTING — both define the sort order for the same response,
47
+ but specify different fields and directions. A single implementation cannot
48
+ satisfy both unless scoped to different contexts.
49
+ ```
50
+
51
+ ### 2. State or Data Conflict
52
+
53
+ Does one spec set a state or value that the other spec assumes is different?
54
+
55
+ ```
56
+ Spec A: :TaskList: is initially empty.
57
+ Spec B: :TaskList: contains a default "Welcome" :Task: on first load.
58
+
59
+ Verdict: CONFLICTING — both define the initial state of :TaskList: differently.
60
+ ```
61
+
62
+ ### 3. Behavioral Override
63
+
64
+ Does the later spec silently replace behavior established by the earlier spec without acknowledging it?
65
+
66
+ ```
67
+ Spec A: :User: credentials are validated using an API key.
68
+ Spec B: :User: credentials are validated using OAuth 2.0.
69
+
70
+ Verdict: CONFLICTING — both define the authentication mechanism but pick
71
+ different approaches. The later spec overrides the earlier one.
72
+ ```
73
+
74
+ ### 4. Scope Ambiguity
75
+
76
+ Are the two specs ambiguous enough that a renderer could interpret them as conflicting, even if the user intends them to be complementary?
77
+
78
+ ```
79
+ Spec A: All :Resource: items are returned.
80
+ Spec B: Only active :Resource: items are returned.
81
+
82
+ Verdict: CONFLICTING (ambiguous) — "all" vs "only active" appear contradictory.
83
+ Could be resolved by scoping each to different conditions (e.g., filtered vs unfiltered).
84
+ ```
85
+
86
+ ### 5. Shared Concept, Different Constraints
87
+
88
+ Do both specs impose constraints on the same `:Concept:` that cannot coexist?
89
+
90
+ ```
91
+ Spec A: :BatchSize: is 100 items.
92
+ Spec B: :BatchSize: is 50 items for :Resource: types with attachments.
93
+
94
+ Verdict: COMPATIBLE — Spec B adds a conditional refinement, not a contradiction.
95
+ ```
96
+
97
+ ## Output Format
98
+
99
+ Emit exactly one of the two shapes below, with no surrounding text, explanation, category label, or resolution suggestion.
100
+
101
+ If **no pair** is conflicting:
102
+
103
+ ```
104
+ COMPATIBLE
105
+ ```
106
+
107
+ If **one or more pairs** are conflicting, emit `CONFLICTING` followed by one line per conflicting pair, in `Sᵢ x Sⱼ` form (using the identifiers from the input, with `i < j`), sorted lexicographically:
108
+
109
+ ```
110
+ CONFLICTING
111
+ S1 x S4
112
+ S2 x S3
113
+ S3 x S5
114
+ ```
115
+
116
+ Rules for the output:
117
+
118
+ - Do **not** list compatible pairs.
119
+ - Do **not** include reasoning, checklist categories, or fixes — the caller decides what to do with each conflict (typically: invoke `resolve-spec-conflict` on each pair).
120
+ - If the input contained fewer than two specs, emit `COMPATIBLE` (nothing to compare).
121
+ - Identifiers must match exactly what the caller provided (or the `S1`, `S2`, … fallback assigned in step 1).
122
+
123
+ The internal pairwise analysis informs the verdict but must not appear in the output.