@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
package/docs/deploy.md ADDED
@@ -0,0 +1,109 @@
1
+ # Trying what is built: deploy
2
+
3
+ Ask to try it - "let me try it", "run the app" - at any time, even while a render is running. The
4
+ chat calls `deploy`: it puts the module's code, as it was last built, in `dist/<module>` in the
5
+ project, and starts it on your machine. An app with a backend and a frontend is started whole, and
6
+ you are told where to open your browser. For a command-line tool or a library you get a few commands
7
+ to paste, run from `dist/<module>`, and what they print.
8
+
9
+ Every pulse that lands code - a render, a spec diff, a conformance run, a functionality regenerated - ends with
10
+ "Want to try it? Just ask me - I'll set it up from what's built so far." When a render's conformance
11
+ tests follow as a pulse of their own, the conformance pulse says it, once they pass too.
12
+
13
+ ## When the project says how it is released
14
+
15
+ Every release starts the same way: the module's last commit - what last landed whole - goes in
16
+ `dist/<module>`, as below. Not everything is tried from there, though. An integration that a host
17
+ application loads goes on into the host's own tree, with the host's own scripts, and then the host
18
+ is started. So the model asks itself whether there is guidance for how this project is released -
19
+ in what you said, the project's AGENTS.md, a skill, anything it was given - and passes it to
20
+ `release` whole:
21
+
22
+ - **There is guidance.** The code goes on from `dist/<module>` as the guidance says, on your machine,
23
+ from the host codebase - the git repository the project is in, or else where **turri** runs: the
24
+ scripts it names, in their order, and any change it says the host needs. The code is taken from
25
+ `dist`, never from `plain_modules`, where a pulse may be writing; where the host's tooling reads
26
+ `plain_modules` itself, the model points it at `dist`, or does by hand from `dist` what it does. When
27
+ to release - after every pulse, say - is the guidance's to say too. The rules below hold as they do for `dist`: the
28
+ model runs the project's scripts and installs what they need itself, and what keeps running - a
29
+ start script, a server - is started with `activate`. Nothing built is changed.
30
+ - **There is none.** The default: it is started from `dist/<module>`, as below, as best it can be.
31
+
32
+ Guidance is whatever says it, in any words - a line in AGENTS.md such as *integrations are released
33
+ with `scripts/deploy_integrations.sh`, then the app is started with `scripts/start.sh`*, or the
34
+ same said in the chat.
35
+
36
+ The chat reacts to every pulse that finishes as it finishes, without waiting for you to speak: guidance
37
+ that says to release after a build - *release each integration once it is regenerated* - is followed then.
38
+ It never has to work out whether what a pulse landed is already out: `release` does. When the module's
39
+ last commit is what its last release in this conversation shipped, it does nothing - nothing exported,
40
+ nothing stopped, what runs left running - and says so; `again: true` releases it anyway, for when what
41
+ that release said to do was not done.
42
+
43
+ ## What goes in dist
44
+
45
+ - **The module on top.** Code accumulates up a `requires` chain: a module starts from a copy of the
46
+ code of the one it requires. So deploy always takes the module on top of the spec's chain - the
47
+ project's `module` from `config.turri.yaml` unless another spec is named. Before the module on top has
48
+ anything built, it takes the nearest one below it that has, and says so.
49
+ - **Its last commit.** Each `plain_modules/<module>` is a git repository (`.plaingit`), and every unit
50
+ of work starts from a commit and ends with one. What a pulse is writing right now is half-done, so it
51
+ is left out: deploy exports the commit - `git archive` of `code/` - which is the working tree with
52
+ every unstaged and untracked change put back, and no `node_modules`, `.venv`, `target` or other
53
+ build leftovers, whatever the stack. The working tree the pulse is using is never read or touched.
54
+ The harness's own files - `.turri.json`, the agents' notes - are left out too.
55
+ - **Replaced whole.** Each deploy stops what the last one of the module started, empties
56
+ `dist/<module>` and exports again. Change nothing there you want to keep. `dist/` is not added to
57
+ `.gitignore`: whether it is committed is yours to decide.
58
+
59
+ ## Starting it
60
+
61
+ It runs on your machine, outside the [sandbox](sandbox.md) - with your toolchains, where your
62
+ browser can reach it. The chat reads how the code runs (its README, manifests and entry points),
63
+ then:
64
+
65
+ 1. **Checks the toolchains** it needs (`node`, `python3`, `go`, ...). One is missing: it installs it
66
+ with the machine's package manager (`brew` on macOS, `apt` or `dnf` on Linux).
67
+ 2. **Installs what it needs itself.** Dependencies into a virtual environment or `node_modules`, a
68
+ build that downloads packages, an image pull, a guided release's project scripts: it runs them in
69
+ its own shell, as any agent with a shell would, and fixes what fails. Nothing is put to you - there
70
+ is no dialog to answer.
71
+ 3. **Starts what serves** with `activate`: in the background, in its own process group, its
72
+ output in `.turri/deploy/<module>-<name>.log`. It waits until something answers at the
73
+ server's URL - up to a minute - so the URL you are given has answered once; one that exits first
74
+ comes back with the end of its output. A port something else holds is said, with what holds it,
75
+ and nothing is started there: the app is not moved to a port you were not told about.
76
+
77
+ What it started is listed by the `pulses` tool and by **`/halt`**, and stopped by **`/halt activated <name>`**, by
78
+ `halt`, by the next deploy of the module, and when the chat exits.
79
+
80
+ ## After every pulse
81
+
82
+ With `release-after-pulse: true` in `config.turri.yaml`, turri releases what each pulse lands itself, as the
83
+ pulse's last stage (`releasing what this pulse landed`): the pulse ends once it is released, so the
84
+ module's next pulse starts on what is out, and the chat - waiting on your answer to a question, say - has
85
+ no part in it. One release a pulse that lands, in order.
86
+
87
+ The release is done by an agent on the coding model, thinking low - it follows steps, and a small model does that
88
+ well - the same one `turri release` runs, and it finds
89
+ the guidance as the chat would: what was passed to `release` earlier in the conversation, the project's
90
+ AGENTS.md and its skills. When nothing there says to release after a build, it releases nothing and says
91
+ so. Its last words go in the pulse's report - "Released after the pulse: ..." - and the chat, seeing them,
92
+ does not release it again. A release that fails is said there too; what the pulse landed stays.
93
+
94
+ Off by default: without the key, releasing after a pulse is the chat's, when its guidance says so.
95
+
96
+ ## On the command line
97
+
98
+ ```bash
99
+ turri release [file.plain] [--in-module <module>] [--guidance <text>] [--model <provider/id>]
100
+ ```
101
+
102
+ The same, without the chat: a one-off agent on `coding-model`, thinking low (or `--model`; without either,
103
+ pi's default) is given the project's AGENTS.md and what `--guidance <text>` says, looks for guidance in
104
+ them, and releases as above - as the guidance says, or to `dist/<module>` - then works out how to
105
+ start it and starts it here, installing what it needs without asking. It stays in the foreground with the servers'
106
+ output; `Ctrl-C` stops them. If it does not get there, deploy from the chat instead.
107
+
108
+ `turri regenerate`, `spec-diff` and `conform` end a build that landed with
109
+ `To try it: turri release <spec>`.
@@ -0,0 +1,88 @@
1
+ # Development
2
+
3
+ ```bash
4
+ npm run typecheck # types
5
+ npm test # the whole suite, about a minute
6
+ ```
7
+
8
+ The suite runs the projects' testing scripts on this machine, as turri does. The fixture scripts stage each run in a
9
+ folder of their own, as test files running side by side would otherwise share one.
10
+
11
+ ## Mutation testing
12
+
13
+ `npm run mutation` runs [Stryker](https://stryker-mutator.io) over the core of regeneration - the parts in
14
+ `stryker.config.mjs` - one part at a time, each part's report in `reports/mutation/<part>/`. A part with a report is
15
+ skipped, so a run that is stopped goes on from the part it was in; `npm run mutation -- <part>` runs one again, and
16
+ `npm run mutation -- --summary` sums up the reports there are. It takes hours: each part runs the suite once to find
17
+ which tests reach which mutant (`vitest.stryker.config.ts`, the suite without the files that run real commands behind the sandbox), then each
18
+ mutant against those tests. `scripts/mutation.mjs` sets two things in Stryker's vitest runner the suite needs - forked
19
+ workers, and a test-name filter that matches vitest 5's names - again after every `npm install`.
20
+
21
+ Working on turri itself, `npm link` points the global `turri` at this clone, so edits
22
+ take effect immediately. `npm run chat` starts pi with the extension loaded from `src/`.
23
+
24
+ The package on npm is built: Node runs no `.ts` file inside a `node_modules`, so `npm run build` (`scripts/build.mjs`,
25
+ over `tsconfig.build.json`) compiles `src/` into `dist/`, file for file - each file as deep as its source, so it finds
26
+ `genome/`, `stdlib/` and `.pi/` the same way - with its imports' `.ts` rewritten to `.js`, and copies the other files
27
+ in `src/` beside them. `npm pack` and `npm publish` run it first (`prepack`), and the package holds `dist/`, not
28
+ `src/`. `bin/turri.mjs` runs `src/cli.ts` where there is one and `dist/cli.js` otherwise; `cli.ts` starts pi with
29
+ the launcher and extension beside itself, whichever they are. To try the package as users get it:
30
+ `npm pack`, then `npm install -g ./turri-dev-cli-<version>.tgz`.
31
+
32
+ ## Publishing
33
+
34
+ `.github/workflows/publish.yml` publishes `@turri-dev/cli`, after the typecheck and the suite pass:
35
+
36
+ - every push or merge into `main` publishes a prerelease under the `next` dist-tag - the patch after the `latest` on
37
+ npm, with the commit's sha: `0.1.2-next.<sha>`. `latest` is not touched; `npm install -g @turri-dev/cli@next` gets it.
38
+ - a GitHub Release tagged `vX.Y.Z`, published by hand, publishes `X.Y.Z` as `latest` (or under `next`, if the release
39
+ is marked a pre-release). The tag is the version: it is set in the runner and never committed, so `package.json`'s
40
+ version on `main` is stale by design.
41
+
42
+ The workflow signs in to npm with npm's trusted publishing (OIDC), not a token: the package's settings on npmjs.com
43
+ trust `turri-oss/turri`, the workflow `publish.yml` and the environment `npm`. Renaming the workflow file breaks it.
44
+
45
+ ## Layout
46
+
47
+ | path | what it is |
48
+ |---|---|
49
+ | `bin/turri.mjs` | the launcher: checks the Node version, then loads `src/cli.ts` |
50
+ | `src/cli.ts` | the `turri` command and its subcommands |
51
+ | `src/setup.ts`, `src/environment.ts`, `src/stack.ts` | `turri doctor`, and what it checks - this machine, and in a project what its modules need of it |
52
+ | `src/extension.ts` | the pi extension: every tool and command the conversation has |
53
+ | `src/skill.ts` | parses `SKILL.md` into steps and gates |
54
+ | `src/graph.ts` | compiles a skill into a LangGraph `StateGraph`: step -> gate -> route |
55
+ | `src/judge.ts` | runs independent judges, applies the quorum, builds the critique |
56
+ | `src/genome.ts` | `runSkill()` - the one entry point the CLI and the extension share |
57
+ | `src/pi.ts` | pi session factories for executors, sub-agents and judges |
58
+ | `src/spec-tools.ts` | `write_spec_diff` |
59
+ | `src/loop-tools.ts` | `regenerate`, `drift` and `halt` - one definition each, for the conversation and the skills alike |
60
+ | `src/mature-tools.ts` | `mature`'s own two: `watch_jobs` and `spec_findings` |
61
+ | `src/plain/` | the \*\*\*plain parser - see [parser](parser.md) - and `highlight.ts`, which highlights \*\*\*plain with plyn's grammar, `plain.tmLanguage.json` (`npm run grammar:sync` refreshes it from [plyn](https://github.com/plainlang/plyn)) |
62
+ | `src/regenerate/conformance.ts` | conformance tests: writing them, and fixing the test or the code until they pass |
63
+ | `src/regenerate/regenerate.ts`, `src/regenerate/conform.ts` | a code job - a spec diff rendered in the code - and a conformance job |
64
+ | `src/regenerate/loop.ts`, `src/regenerate/records.ts`, `src/regenerate/drift.ts`, `src/regenerate/jobs.ts` | the work loop, the records and the work derived from them, and background jobs |
65
+ | `src/regenerate/chunk-plan.ts`, `src/regenerate/cases.ts` | cutting the work into jobs, and what a job's agents are told by its cases |
66
+ | `src/regenerate/regression.ts` | the whole conformance suite run, and the repair agent that may not touch tests |
67
+ | `src/regenerate/lineage.ts` | module history in `.plaingit` |
68
+ | `src/regenerate/release.ts`, `src/release-tools.ts`, `src/release-cli.ts` | deploy: the last commit to `dist/<module>`, what installs software, what it starts on the host - see [deploy](deploy.md) |
69
+ | `src/regenerate/confine.ts`, `src/regenerate/agent-shell.ts`, `src/regenerate/command-check.ts`, `src/regenerate/os-sandbox.ts`, `src/regenerate/audit.ts`, `src/regenerate/agent-env.ts`, `src/regenerate/shell.ts` | the file-tool fence and the walls around every agent command: the check, the OS sandbox, the audit, the module's environment, the host runner - see [sandbox](sandbox.md) |
70
+ | `src/regenerate/engine.ts` | choosing and resolving render models |
71
+ | `src/regenerate/job-panel.ts`, `src/regenerate/transcript-view.ts`, `src/regenerate/diff-view.ts` | the job panel, the side panel's colours, and a spec diff as they show it - highlighted and banded |
72
+ | `src/regenerate/spec-map.ts`, `src/regenerate/spec-map-view.ts` | regen: each item's mark, from the records, the `.plain` files and the work loop's plan (`mapPlan`), and the map drawn |
73
+ | `src/regenerate/debug-log.ts` | each render agent's whole conversation, always streamed to `.turri/debug/` |
74
+ | `genome/` | what turri knows and can do: `genome/skills/`, the skill library, and `genome/rules/`, the \*\*\*plain authoring rules |
75
+
76
+ ## Known edges
77
+
78
+ - Judges default to the executor's model. Real separation wants a different provider - set
79
+ `model:` on the gate. An unconfigured model falls back to the executor's with a note rather than
80
+ failing the run.
81
+ - The executor keeps one session across steps and retries, so context builds up with attempts. Long
82
+ skills want compaction or a session per step.
83
+ - A gate's escalation lives in memory (LangGraph's `MemorySaver`): it must be answered in the same
84
+ run.
85
+ - The gate machinery has no automated coverage; see
86
+ [trying the gate semantics](skills-and-gates.md#trying-the-gate-semantics).
87
+ - Jobs live in memory: there is no resuming a job after pi exits. The work itself is on disk, so a
88
+ killed job leaves a partial build rather than nothing.
@@ -0,0 +1,148 @@
1
+ # Running unattended
2
+
3
+ Given a spec, build it without a conversation - overnight, in CI, on a spare machine.
4
+
5
+ ## Before you leave it
6
+
7
+ ```bash
8
+ turri doctor
9
+ ```
10
+
11
+ Everything marked `✗` stops the run, so fix those first. Two more things matter for a long run:
12
+
13
+ - **The toolchains.** The agents use this machine's: run `turri doctor` in the project, and it says which toolchain
14
+ a module's implementation reqs name that this machine lacks.
15
+ - **The models.** In a project, `turri doctor` sends each model `config.turri.yaml` names one short request, as turri's
16
+ agents will - so a key the provider refuses, a model pi does not know or an account without access to it shows now,
17
+ not an hour into the run.
18
+ - **Credentials that do not expire mid-run.** An API key in the environment
19
+ (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, ...) or pi's saved login - whatever
20
+ `doctor` shows as the model provider.
21
+
22
+ Check the spec parses, without spending anything:
23
+
24
+ ```bash
25
+ cd ~/my-project # the directory that holds the spec
26
+ turri validate app.plain
27
+ ```
28
+
29
+ ## Run it
30
+
31
+ ```bash
32
+ turri regenerate app.plain --model anthropic/claude-sonnet-5
33
+ ```
34
+
35
+ That builds every functionality in order - code and unit tests, then conformance tests when the
36
+ module has `***test reqs***`, then every conformance test so far - and advances only when they
37
+ pass. A module that `requires` others builds them first, bottom up. When it is done it prints
38
+ `To try it: turri release <spec>` - see [deploy](deploy.md).
39
+
40
+ Keep it running when the terminal closes and the laptop would sleep, and keep the log:
41
+
42
+ ```bash
43
+ # macOS: caffeinate keeps the machine awake for as long as the command runs
44
+ nohup caffeinate -is turri regenerate app.plain --model anthropic/claude-sonnet-5 > render.log 2>&1 &
45
+
46
+ # Linux
47
+ nohup turri regenerate app.plain --model anthropic/claude-sonnet-5 > render.log 2>&1 &
48
+ ```
49
+
50
+ Or run it in `tmux` / `screen` to look in on it. `tail -f render.log` follows it either way.
51
+
52
+ The project's `config.turri.yaml`, in the folder you run it from, can give the defaults, so a run can
53
+ be just `turri regenerate`:
54
+
55
+ ```yaml
56
+ module: app.plain # what to build when no file is given
57
+ coding-model: # the model every agent runs on, and how hard it thinks
58
+ model: anthropic/claude-sonnet-5
59
+ thinking: medium
60
+ judge-model: anthropic/claude-opus-5-5 # the judge and the test review (default: coding-model)
61
+ conformance-tests: true # write and run conformance tests where there are test reqs (default: false)
62
+ ```
63
+
64
+ The same file names the [testing scripts](regenerating.md#the-testing-scripts); every key is in
65
+ [the project config](config.md). Options on the command
66
+ line win over it; `turri regenerate`, `spec-diff` and `conform` take its
67
+ `coding-model` too, and `--coding-model` means the same as `--model` on every one of them.
68
+ A project whose parts are on different stacks keeps each stack's settings in
69
+ `config.turri.<stack>.yaml` beside it ([stacks](config.md#stacks)). Choose the one to build with
70
+ `--stack <name>` - on any command: `regenerate`, `conform`, `spec-diff`, `glimpse`, `release`, `cultivate` -
71
+ or `TURRI_STACK`: `turri regenerate backend.plain --stack backend`. With none chosen,
72
+ `config.turri.yaml` alone is used; a stack the project has no file for is refused.
73
+
74
+ Useful options - `turri regenerate --help` lists all of them:
75
+
76
+ | | |
77
+ | --- | --- |
78
+ | `--model <provider/id>` | the model every agent runs on (`--coding-model` too), with `:level` for its thinking (`provider/id:high`); default, `config.turri.yaml`'s `coding-model`, then its `harness-model`, then pi's configured model |
79
+ | `--judge-model <provider/id>` | `conformance`: the model of the judge and the test review; default, `config.turri.yaml`'s `judge-model`, then the coding model |
80
+ | `--stack <name>` | build with the stack's `config.turri.<name>.yaml` over `config.turri.yaml`; default, `TURRI_STACK`, else none |
81
+ | `--to <frid>` | queue functionalities only up to this one |
82
+ | `--only` | build just this module, not the ones it requires |
83
+ | `--build-dir <dir>` | where the code goes; default `plain_modules/<module>/code` |
84
+
85
+ `turri regenerate` queues the jobs the chat's `regenerate` does - a spec diff that brings what
86
+ is built in line, when it no longer matches the spec, then one render functionality job per
87
+ functional spec not built, each with its conformance job - and runs them all to the end, the queue
88
+ master reworking them as in the chat (see [a whole module](regenerating.md#a-whole-module)).
89
+ Conformance tests follow `conformance-tests:` in `config.turri.yaml`.
90
+
91
+ ## In the morning
92
+
93
+ The command exits `0` only when every module is complete. The log says, per functionality, what
94
+ was built and whether its tests passed; the last lines summarise each module.
95
+
96
+ | | |
97
+ | --- | --- |
98
+ | `plain_modules/<module>/code/` | the code and unit tests |
99
+ | `plain_modules/<module>/tests/` | the conformance tests |
100
+ | `plain_modules/<module>/code/.turri.json` | the marshalled plain tree the code is built from |
101
+
102
+ And what happened, step by step, in the module's history:
103
+
104
+ ```bash
105
+ git --git-dir=plain_modules/<module>/.plaingit --work-tree=plain_modules/<module> log --stat
106
+ ```
107
+
108
+ ## If it stopped
109
+
110
+ Run the same command again. Functionalities already built from the current spec are left alone,
111
+ and modules that are complete are skipped, so it picks up where it stopped - after a crash, a
112
+ provider outage, or a machine that went to sleep anyway. Whatever the stopped run left
113
+ half-written is undone first: each step starts from the module's last commit. A functionality whose spec has changed
114
+ since counts as not built, so editing the spec and re-running rebuilds what changed. `--force`
115
+ rebuilds everything.
116
+
117
+ ## Rebuilding one functionality
118
+
119
+ ```bash
120
+ turri regenerate app.plain 3
121
+ ```
122
+
123
+ It rebuilds just functionality 3 over the code that already exists - after its spec changed, or as
124
+ it is - without rebuilding the ones before it. To change what it does, change its spec first: the
125
+ spec is what the code is built from, and a change made there lasts. Then it updates the
126
+ functionality's conformance tests and runs every conformance test in the codebase, since a change can break something built
127
+ later as easily as something built earlier. It works in the codebase the functionality now lives
128
+ in: when later modules are built on top of this one, the furthest-built of them (`--in <module>`
129
+ picks one when there are several). It exits `0` when everything passes, and refuses when there is
130
+ no code yet - build it with `module` first.
131
+
132
+ Several changes do not need one command each: after editing the spec, re-running `turri regenerate`
133
+ brings the code up to it - one spec diff for what changed in what is built, then the functional
134
+ specs not built yet.
135
+
136
+ ## Asking in words instead
137
+
138
+ pi's print mode runs one request and exits, with the render tools waiting for their work rather
139
+ than starting background jobs:
140
+
141
+ ```bash
142
+ nohup turri -p "Render app.plain as a whole module, with conformance tests" > render.log 2>&1 &
143
+ ```
144
+
145
+ It reports what it did at the end, and nothing streams in between, so for long runs
146
+ `turri regenerate` is the better choice. The interview skill (`brood`) needs
147
+ someone to answer; without one they decide and record their assumptions, and a gate that escalates
148
+ to a human stops the run.
@@ -0,0 +1,50 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="16 -5 1100 490" width="1100" height="490" role="img" aria-labelledby="d-title d-desc">
2
+ <title id="d-title">How turri works</title>
3
+ <desc id="d-desc">You describe changes to turri; turri asks and reports back. turri writes the ***plain specs, which you read and review with turri. Then turri loops without end: it reads the specs, regenerates the code from them, and tests the code against them.</desc>
4
+ <style>
5
+ @font-face { font-family: "Archivo"; font-weight: 900; src: url(data:font/woff2;base64,d09GMgABAAAAAAN0ABAAAAAABuQAAAMZAAIAQgAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbehwqBmA/U1RBVFoARBEICoIAgV8BNgIkAxQLDAAEIAWESgcgDAcbnQUgrgN4MoaXUIIRo4bSsCiga71j9UcNccO+yjyC+P1os19OzJJmreeIVRLJ+4WIehOXTKRJJRSJ8Pyncf+de2ce5wNCgxqZAC0ct6/blrChgUdrbfZNUdmKmpz5LBKSlaKpfEIsfsMjiZDIAa9FmMiRBO5xu8oTYkgzRXzs+9+lkO3UTW0sDfC/Eg80wzWI0sVrASYYaPgX/QUUUZwVWGASpe6a4qnU8vN++BgB6gBkCJEIgRo+ovDC4Pg8xPbtoA4xEkgQEHs5W4cACBAkpkf0mAcJRkVgEgAAgTGBCIQQJjCRXOsngM9xiaHI0vMFJWTvskM4oi4XP9X6lXQAfsTmjZMjkCBh+cB/UGwghwgga+BnGCIQoidAGwEm8do+ioSAtZ0iEiB2445KnDAtK24IZGGvBwgEUSMWkGMczoAAZBA8JHWfFAGq6DHYzcYSIRCVLqQT6bgAkGIzEQeUgBqc3whsB6D43F9FhbTpcmwPhZZSSaVRteGjj59qv27LeOjVk2PYcSj5hTCSqg9DMZq5r2qx/exN7XkyKJ65eHCjDr99TrmSxrrQ/ddvbRP77/bfbnTXBClNV3Wb/V8OzhdlfN+zmyYuvQ78yq+N29b1NhUoOpktvThzPnRiPpgdaR7/8x/4q21F2Fy8K2X997rzFrMa3zXdVG/2h9xezABAgEzWr9dWPOVvgPgG+P6P4MdMl+X/1lRRbBQToAIDAqr30kH43z8jcK3MVqyLdCnxtj+kNzsyh0CpnxcQsFXvUo4KA+AWpkJC3CvMzFdFyPA9lG6KhlN4yVpBkZIhoC76oBCMMUIa2QnfHuyS7wR203cG/ql5BtMxm0QgQlSUiOkoDzVEHCqTbY9IcsBAVbAZZJw96CIyCAw2JwwIhkImR6tw4fpJ462YXZW6GTRVyml0kl2Mo0KNYyZOVdLpkkNE1TjaAtmLq9pHZVHt2KOYxREwuYutVpgIESIlKzdh0oi2kNVNXUEsmhfLSQw6qnQVhlhynlg3gWQfui5Ikww2zQ7VCFSTkj2zjI2LdG6ANOUYfxXpj+RRAAA=) format("woff2"); }
6
+ @font-face { font-family: "Archivo"; font-weight: 800; src: url(data:font/woff2;base64,d09GMgABAAAAAARoABAAAAAACJwAAAQNAAIAQgAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbgRAcKgZgP1NUQVRaAEwRCAqEcIQUATYCJAMcCxAABCAFhGoHIAwHGxgHKB6FccNdgjLFEIv53uLyOp7///3+N9e+57sm0yTgybwE8f4rdCIhkRhdNGRC4VMilffzedb7TYcZyRkQmwJOF4CK41InIf9hXSYupz+WPr61fmGAXUCBJToX/m9+bNFHYwImlGY9dw/0QE2Pto0FGJB3TbabIke9urYiGAWUiH4RDNuQg3atvrUXPF6zdQrw0ODXaHrF1yjAmY3SFD8GlZ0L4lAlTLPL0UnJ/fIYmVhii1q6qEK3JOFBTgBoVdFFcaB+0WYB77Aof+fwXxCQP/HwfnsTXAAuqlP8D7R7aBUQqHHaPmAFJmJQh1kYDtBOeDoRQuDkmUiHQF7o2DIUgqbftAgIlUA8IEKGsCigUyqhAAGF/VwQSXc+N1xZ2/KEkh7VAzxCmfp/naUWmJuTGJHdgwkwAMYxhN8AGQCgAaKUEtmc9w139ZoOjmDPstnQRLsdn221WsH17b5TrrNOHABvDt7EZ5/ej/AJFoulj7zk6slgHOF0137XPZb+2ecPIzRxk+LQSZsywKgp5fju1U3FMD5x3a5jGsN7hCMCUv/s2/bKh24R+AZxs8+EMJw2YeXOrcHkMM/CjjsWtnQtADFmhTLwvqUbNwbjy9fvuxc4gA/usTywFOADFFFpJskqM0X0FzGC6/Pju4o3xkiPRid8ITPi0smIxJgrY2JWFcZ31+UzQ+DvSA2byKWYZJ6JSVRFzr371fH3Fss0vNFpV08yjN+40JocFHFQyw2Nv1ucGptSfAcqRobHllTrLa6KGk0M9BuKy4zMWHMKFZ4SPLLsid/6rJjmmhxW8IG2RyHXZqYFxn1fC5dHBscQ1WaSrKbGEINDFJACzdaG5ExZeny6PCM5KUPe/wZZJnzE+0aR7FFb6vz357/zCT+1Lixvi+d0l2an4GfVfrDwStzwZlFdmIlTdd9jf9tr5vOXtY5l75dtLqt+s/noT/ekh+bWKAAIypVR2eOTTM/Sf84Y9hbw52/YQcBpUYX3/7hedptMjwL6FQifI/1JhvhvHAOxWw3ULLhbPIgvagCl4Gh7PRBVjiYY8IyDzTa73ZgP0ElzAT4CCh3HdtBl3CndueX9YJOk+ywnx9fvMK0NCIxmHKQRDGcW7QLTWZIJhOi2TAhEDzkL5ZpzcJWSCkVDTEhEJ0qOrIsYpZcO8o0qGlyKIWagnMFOs5IEH5dOitTTuZU0tLkke/N0VLRKZMokJF6+lR5HBi4luUwd5BTEOPS00vWiqPBl+h2dHIfRg09OA6tFGd5C1jHpGmyOfzc+IT3Zp2jkyJAlSzZChzbtmhDWW7fB0sH+aK6TxJQUolCvLQIDyepHKC4xHl076Vx7fblybDJNsUetGFc3Je9KChjQk0mzV39D/6dOnwsA) format("woff2"); }
7
+ @font-face { font-family: "JetBrains Mono"; font-weight: 500; src: url(data:font/woff2;base64,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) format("woff2"); }
8
+ @font-face { font-family: "JetBrains Mono"; font-weight: 700; src: url(data:font/woff2;base64,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) format("woff2"); }
9
+ text { font-variant-ligatures: none; font-feature-settings: "calt" 0, "liga" 0; }
10
+ .lbl { font-family: "JetBrains Mono", ui-monospace, Menlo, monospace; font-size: 15px; font-weight: 500; fill: #0f0f0f; }
11
+ .node { font-family: "Archivo", "Helvetica Neue", Arial, sans-serif; font-weight: 800; font-size: 22px; }
12
+ .sub { font-family: "JetBrains Mono", ui-monospace, Menlo, monospace; font-weight: 700; font-size: 12px; letter-spacing: 0.06em; }
13
+ .flow { fill: none; stroke: #0f0f0f; stroke-width: 2.25; stroke-linecap: round; }
14
+ .review { stroke-dasharray: 9 7; }
15
+ @media (prefers-reduced-motion: no-preference) {
16
+ .spin { transform-box: fill-box; transform-origin: 50% 58%; animation: turn 6s linear infinite; }
17
+ @keyframes turn { to { transform: rotate(360deg); } }
18
+ }
19
+ </style>
20
+ <rect x="16" y="-5" width="1100" height="490" rx="18" fill="#e0ff6e"/>
21
+ <defs>
22
+ <marker id="head" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
23
+ <path d="M0 0 L10 5 L0 10 z" fill="#0f0f0f"/>
24
+ </marker>
25
+ </defs>
26
+ <path class="flow review" d="M120 142 V 70 Q 120 40 150 40 H 955 Q 985 40 985 70 V 158"/>
27
+ <text class="lbl" x="552" y="28" text-anchor="middle">review loop</text>
28
+ <path class="flow" d="M174.4 180 L 474.8 180" marker-end="url(#head)"/>
29
+ <text class="lbl" x="324" y="166" text-anchor="middle">describe changes</text>
30
+ <path class="flow" d="M474.8 220 L 174.4 220" marker-end="url(#head)"/>
31
+ <text class="lbl" x="324" y="246" text-anchor="middle">asks · reports</text>
32
+ <path class="flow" d="M641.2 180 L 900 180" marker-end="url(#head)"/>
33
+ <text class="lbl" x="771" y="166" text-anchor="middle">writes</text>
34
+ <path class="flow" d="M900 220 L 641.2 220" marker-end="url(#head)"/>
35
+ <text class="lbl" x="771" y="246" text-anchor="middle">reads</text>
36
+ <path class="flow" d="M538 244 L 538 380" marker-end="url(#head)"/>
37
+ <text class="lbl" x="524" y="318" text-anchor="end">regenerates</text>
38
+ <path class="flow" d="M578 380 L 578 244" marker-end="url(#head)"/>
39
+ <text class="lbl" x="592" y="318" text-anchor="start">tests against ***plain</text>
40
+ <text class="spin" x="880" y="372" text-anchor="middle" font-family="Archivo, sans-serif" font-weight="900" font-size="44" fill="#0f0f0f">↻</text>
41
+ <circle cx="120" cy="200" r="58" fill="none" stroke="#0f0f0f" stroke-width="2.5"/>
42
+ <text class="node" x="120" y="208" text-anchor="middle" fill="#0f0f0f">you</text>
43
+ <rect x="470" y="156" width="176" height="88" rx="44" fill="#0f0f0f"/>
44
+ <text x="558" y="212" text-anchor="middle" font-family="Archivo, sans-serif" font-weight="900" font-size="34" letter-spacing="-1" fill="#e0ff6e">turri</text>
45
+ <rect x="900" y="158" width="170" height="84" rx="6" fill="#223936"/>
46
+ <text x="985" y="208" text-anchor="middle" font-family="JetBrains Mono, ui-monospace, monospace" font-weight="700" font-size="24" fill="#c5dcd9">***plain</text>
47
+ <rect x="473" y="380" width="170" height="84" rx="6" fill="#e0ff6e" stroke="#0f0f0f" stroke-width="2.5" stroke-dasharray="7 6"/>
48
+ <text class="node" x="558" y="420" text-anchor="middle" fill="#0f0f0f">code</text>
49
+ <text class="sub" x="558" y="444" text-anchor="middle" fill="#0f0f0f">DISPOSABLE</text>
50
+ </svg>
Binary file
@@ -0,0 +1,119 @@
1
+ # Installation
2
+
3
+ **turri** runs wherever pi runs - macOS, Linux, WSL - with git, bash and Node 22.19 or newer (a Linux
4
+ distribution's own `nodejs` package is often older: take it from nodejs.org, nvm or NodeSource). Native Windows is
5
+ best effort: it needs bash on the PATH (Git for Windows' Git Bash), and has no OS sandbox under the agents' commands.
6
+
7
+ The agents' commands run on this machine, with its toolchains - see [the sandbox](sandbox.md). Each is checked before
8
+ it runs and audited after, on every platform; under them an OS sandbox fences it where the OS has one: Seatbelt on
9
+ macOS, always there, and bubblewrap on Linux and WSL 2 - `apt install bubblewrap`, or your distribution's package.
10
+
11
+ ```bash
12
+ npm install -g @turri-dev/cli # puts `turri` on your PATH
13
+ turri doctor # checks the machine: what turri needs, and what is missing
14
+ ```
15
+
16
+ Then, in the project that holds your specs:
17
+
18
+ ```bash
19
+ cd ~/my-project
20
+ turri init-config # a config.turri.yaml with the model you talk to and the one that writes the code, every other setting at its default
21
+ turri # pi, with turri's tools, skills and themes loaded
22
+ ```
23
+
24
+ The package is `@turri-dev/cli` - npm keeps the name `turri` back, as too like another
25
+ package's - and the command it puts on your PATH is `turri`. `npm install -g @turri-dev/cli@next` gets the latest
26
+ prerelease instead, built from the last commit on `main`.
27
+
28
+ ## From a clone
29
+
30
+ To work on turri itself, put the clone on your PATH instead:
31
+
32
+ ```bash
33
+ git clone git@github.com:turri-oss/turri.git && cd turri
34
+ npm install
35
+ npm link # puts `turri` on your PATH, pointing at this clone
36
+ ```
37
+
38
+ A clone runs its TypeScript sources directly - Node strips the types - so edits take effect immediately. The package
39
+ on npm runs JavaScript compiled from them instead (`npm run build`, into `dist/`, run by `npm pack` and `npm publish`
40
+ before they pack), as Node runs no `.ts` file inside a `node_modules`. `bin/turri.mjs` runs `src/` where there is one,
41
+ and `dist/` otherwise.
42
+
43
+ ## In Docker
44
+
45
+ `docker/Dockerfile` builds an image with Node, git, bash, bubblewrap and **turri** linked; the agents' commands and the
46
+ testing scripts run inside it, so add the toolchains your projects need first. It runs **turri** as the user who owns
47
+ the project folder mounted at `/work`, never as root - bubblewrap as root in a container lacks the capability to make
48
+ its namespaces - and with seccomp and AppArmor unconfined the OS sandbox starts. `systempaths=unconfined` lets it mount
49
+ a `/proc` of its own; without it, it binds the container's. The build, the run, Windows and the keys:
50
+ [turri in Docker](../docker/README.md).
51
+
52
+ ## What `turri doctor` checks
53
+
54
+ It checks, in order, and says exactly what to run for anything missing:
55
+
56
+ | what | why | if it is missing |
57
+ | --- | --- | --- |
58
+ | Node 22.19+ | pi needs 22.19, as does a clone running turri's `.ts` sources directly | install a newer Node |
59
+ | bash | every command an agent runs, and every testing script, runs with it | on Windows, Git for Windows; elsewhere, the system's package manager |
60
+ | the OS sandbox | the wall under the command check and the audit | macOS has Seatbelt; on Linux, `apt install bubblewrap`. Where it does not start it says why and how to give it one - user namespaces off (Docker: a user that is not root, `--security-opt seccomp=unconfined`), AppArmor in the way (Docker: `--security-opt apparmor=unconfined`; Ubuntu 24.04: `sysctl -w kernel.apparmor_restrict_unprivileged_userns=0`), Docker's masked `/proc` (`--security-opt systempaths=unconfined`, or nothing: turri binds the container's `/proc` instead). Without one, the check and the audit hold alone |
61
+ | a model provider | the agents need a model | run `pi` and `/login`, or set an API key (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, ...) |
62
+ | `turri` on PATH | so it runs from any project | `npm install -g @turri-dev/cli`, or `npm link` in a clone |
63
+ | turri and `pi` apart | `turri` starts pi with turri; a plain `pi` stays pi | an older `turri setup` wrote a file that loads turri into every `pi` - `doctor` offers to remove it |
64
+
65
+ In a project - a folder with `config.turri.yaml` (or the old `config.yaml`) or `plain_modules` - it checks what the project needs of this machine too:
66
+
67
+ | what | |
68
+ | --- | --- |
69
+ | each module's toolchains | what its implementation reqs name - Python, Node, Java, Go, Rust, C/C++, Zig, ... - against this machine's PATH: the agents use this machine's toolchains and install none, so a missing one is yours to install |
70
+ | the testing scripts | what in them this machine does not have: a program named by its whole path that is not here (a macOS-only `/usr/libexec/java_home` on Linux), a toolchain they run that is not on the PATH |
71
+ | the models | each model `config.turri.yaml` names - `harness-model`, `coding-model`, `judge-model`, every role under `models:` - sent one short request through pi, with the credentials turri's agents will use, for any provider pi supports. One that answers is ready; for one that does not, it says what the provider said and what to do: the credentials pi reads for that provider (`AWS_PROFILE` and `AWS_REGION` for Amazon Bedrock, `OPENAI_API_KEY`, `/login`, ...), model access in the Bedrock console, the closest model ids pi knows for one it does not. `--no-models` skips it |
72
+
73
+ Removing that file is the one thing `doctor` changes, and it asks first; `turri doctor --yes` says yes. Run without a
74
+ terminal - piped, or in CI - it asks nothing and changes nothing. Run it again whenever something breaks;
75
+ `turri doctor --no-models` leaves out the models' requests.
76
+
77
+ The chat is told every turn which wall is up and what the project's modules need of this machine that it lacks. What they need is worked out from every spec of the project - seconds, on a project of long requires chains - so it is worked out once the chat is up and again after a turn in which a spec changed, never while your message waits: a turn is told what was known last.
78
+
79
+ ## turri and `pi`
80
+
81
+ `turri` runs pi from this project's `node_modules`, with turri loaded, so no global pi install is needed. A plain
82
+ `pi` is left as it is: starting pi does not start turri, and turri puts nothing into pi's own folders.
83
+
84
+ An older `turri setup` could load turri into every `pi`, with a one-line file in `~/.pi/agent/extensions/` pointing at
85
+ this clone. `turri doctor` says when it is there, and offers to remove it. Until it is gone, `turri`
86
+ leaves out its own `-e`: pi treats a tool registered twice as an error.
87
+
88
+ `turri` starts in the `turri-dark` theme. A plain `pi` keeps its own theme; to give it
89
+ `turri-dark` too, in `~/.pi/agent/settings.json`:
90
+
91
+ ```json
92
+ { "theme": "turri-dark" }
93
+ ```
94
+
95
+ ### Project trust
96
+
97
+ pi will not auto-discover a project-local extension until you trust the project, and `-p` has no
98
+ way to ask - so `npm run chat` and `npm run ask` pass `-e` explicitly. Once you have run `pi`
99
+ interactively in this directory and accepted the trust prompt, plain `pi` picks the extension up
100
+ on its own. A `defaultProjectTrust` key in `.pi/settings.json` does *not* work - a project cannot
101
+ vouch for itself; that setting only counts in `~/.pi/agent/settings.json`.
102
+
103
+ ## Environment
104
+
105
+ Models and credentials come from your pi configuration (`~/.pi/agent`) or provider environment
106
+ variables - the same resolution `pi` itself uses. Put the rest in your shell profile:
107
+
108
+ ```bash
109
+ export PLAIN_TEMPLATE_DIRS=~/path/to/standard_template_library # shared templates
110
+ export GEMINI_API_KEY=... # and leave GOOGLE_API_KEY unset
111
+ ```
112
+
113
+ For Gemini, `GEMINI_API_KEY` is what makes pi *offer* the google provider, but when
114
+ `GOOGLE_API_KEY` is also set pi uses that one for the calls and says so.
115
+
116
+ | variable | |
117
+ | --- | --- |
118
+ | `PLAIN_TEMPLATE_DIRS` | colon-separated template directories, e.g. the standard library |
119
+ | `TURRI_COMMAND_TIMEOUT` | seconds a command may run before it is stopped - an agent's own, its tests (default `120`) |