@turri-dev/cli 0.1.6-next.336dd6e → 0.1.7-next.8f13f3a

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 (233) hide show
  1. package/.pi/themes/turri-dark.json +1 -1
  2. package/README.md +2 -4
  3. package/assets/jellyfish/dark-00.png +0 -0
  4. package/assets/jellyfish/dark-01.png +0 -0
  5. package/assets/jellyfish/dark-02.png +0 -0
  6. package/assets/jellyfish/dark-03.png +0 -0
  7. package/assets/jellyfish/dark-04.png +0 -0
  8. package/assets/jellyfish/dark-05.png +0 -0
  9. package/assets/jellyfish/dark-06.png +0 -0
  10. package/assets/jellyfish/dark-07.png +0 -0
  11. package/assets/jellyfish/dark-08.png +0 -0
  12. package/assets/jellyfish/dark-09.png +0 -0
  13. package/assets/jellyfish/dark-10.png +0 -0
  14. package/assets/jellyfish/dark-11.png +0 -0
  15. package/assets/jellyfish/dark-12.png +0 -0
  16. package/assets/jellyfish/dark-13.png +0 -0
  17. package/assets/jellyfish/dark-14.png +0 -0
  18. package/assets/jellyfish/dark-15.png +0 -0
  19. package/assets/jellyfish/dark-16.png +0 -0
  20. package/assets/jellyfish/dark-17.png +0 -0
  21. package/assets/jellyfish/dark-18.png +0 -0
  22. package/assets/jellyfish/dark-19.png +0 -0
  23. package/assets/jellyfish/dark-20.png +0 -0
  24. package/assets/jellyfish/dark-21.png +0 -0
  25. package/assets/jellyfish/dark-22.png +0 -0
  26. package/assets/jellyfish/dark-23.png +0 -0
  27. package/assets/jellyfish/dark-24.png +0 -0
  28. package/assets/jellyfish/dark-25.png +0 -0
  29. package/assets/jellyfish/dark-26.png +0 -0
  30. package/assets/jellyfish/dark-27.png +0 -0
  31. package/assets/jellyfish/dark-28.png +0 -0
  32. package/assets/jellyfish/dark-29.png +0 -0
  33. package/assets/jellyfish/dark-30.png +0 -0
  34. package/assets/jellyfish/dark-31.png +0 -0
  35. package/assets/jellyfish/dark-32.png +0 -0
  36. package/assets/jellyfish/dark-33.png +0 -0
  37. package/assets/jellyfish/dark-34.png +0 -0
  38. package/assets/jellyfish/dark-35.png +0 -0
  39. package/assets/jellyfish/dark-36.png +0 -0
  40. package/assets/jellyfish/dark-37.png +0 -0
  41. package/assets/jellyfish/dark-38.png +0 -0
  42. package/assets/jellyfish/dark-39.png +0 -0
  43. package/assets/jellyfish/dark-40.png +0 -0
  44. package/assets/jellyfish/light-00.png +0 -0
  45. package/assets/jellyfish/light-01.png +0 -0
  46. package/assets/jellyfish/light-02.png +0 -0
  47. package/assets/jellyfish/light-03.png +0 -0
  48. package/assets/jellyfish/light-04.png +0 -0
  49. package/assets/jellyfish/light-05.png +0 -0
  50. package/assets/jellyfish/light-06.png +0 -0
  51. package/assets/jellyfish/light-07.png +0 -0
  52. package/assets/jellyfish/light-08.png +0 -0
  53. package/assets/jellyfish/light-09.png +0 -0
  54. package/assets/jellyfish/light-10.png +0 -0
  55. package/assets/jellyfish/light-11.png +0 -0
  56. package/assets/jellyfish/light-12.png +0 -0
  57. package/assets/jellyfish/light-13.png +0 -0
  58. package/assets/jellyfish/light-14.png +0 -0
  59. package/assets/jellyfish/light-15.png +0 -0
  60. package/assets/jellyfish/light-16.png +0 -0
  61. package/assets/jellyfish/light-17.png +0 -0
  62. package/assets/jellyfish/light-18.png +0 -0
  63. package/assets/jellyfish/light-19.png +0 -0
  64. package/assets/jellyfish/light-20.png +0 -0
  65. package/assets/jellyfish/light-21.png +0 -0
  66. package/assets/jellyfish/light-22.png +0 -0
  67. package/assets/jellyfish/light-23.png +0 -0
  68. package/assets/jellyfish/light-24.png +0 -0
  69. package/assets/jellyfish/light-25.png +0 -0
  70. package/assets/jellyfish/light-26.png +0 -0
  71. package/assets/jellyfish/light-27.png +0 -0
  72. package/assets/jellyfish/light-28.png +0 -0
  73. package/assets/jellyfish/light-29.png +0 -0
  74. package/assets/jellyfish/light-30.png +0 -0
  75. package/assets/jellyfish/light-31.png +0 -0
  76. package/assets/jellyfish/light-32.png +0 -0
  77. package/assets/jellyfish/light-33.png +0 -0
  78. package/assets/jellyfish/light-34.png +0 -0
  79. package/assets/jellyfish/light-35.png +0 -0
  80. package/assets/jellyfish/light-36.png +0 -0
  81. package/assets/jellyfish/light-37.png +0 -0
  82. package/assets/jellyfish/light-38.png +0 -0
  83. package/assets/jellyfish/light-39.png +0 -0
  84. package/assets/jellyfish/light-40.png +0 -0
  85. package/dist/ask.d.ts +83 -0
  86. package/dist/brand.d.ts +13 -0
  87. package/dist/btw.d.ts +67 -0
  88. package/dist/chat-models.d.ts +22 -0
  89. package/dist/chat-skills.d.ts +17 -0
  90. package/dist/cli-options.d.ts +25 -0
  91. package/dist/cli.d.ts +9 -0
  92. package/dist/cli.js +7 -1
  93. package/dist/config-command.d.ts +26 -0
  94. package/dist/config-edit.d.ts +69 -0
  95. package/dist/config.d.ts +321 -0
  96. package/dist/conform-cli.d.ts +1 -0
  97. package/dist/cultivate-cli.d.ts +1 -0
  98. package/dist/drift-cli.d.ts +1 -0
  99. package/dist/environment.d.ts +39 -0
  100. package/dist/environment.js +26 -0
  101. package/dist/extension.d.ts +147 -0
  102. package/dist/extension.js +49 -18
  103. package/dist/genome.d.ts +62 -0
  104. package/dist/graph.d.ts +52 -0
  105. package/dist/host.d.ts +21 -0
  106. package/dist/host.js +25 -0
  107. package/dist/identity.d.ts +2 -0
  108. package/dist/init-config.d.ts +106 -0
  109. package/dist/jellyfish.d.ts +35 -0
  110. package/dist/jellyfish.js +230 -97
  111. package/dist/judge.d.ts +25 -0
  112. package/dist/lineage-cli.d.ts +1 -0
  113. package/dist/loop-cli.d.ts +34 -0
  114. package/dist/loop-tools.d.ts +80 -0
  115. package/dist/mature-tools.d.ts +53 -0
  116. package/dist/model-check.d.ts +42 -0
  117. package/dist/module-report.d.ts +9 -0
  118. package/dist/pi-launch.d.ts +1 -0
  119. package/dist/pi.d.ts +246 -0
  120. package/dist/picture-row.d.ts +10 -0
  121. package/dist/plain/check.d.ts +23 -0
  122. package/dist/plain/concepts.d.ts +11 -0
  123. package/dist/plain/errors.d.ts +22 -0
  124. package/dist/plain/files.d.ts +12 -0
  125. package/dist/plain/highlight.d.ts +42 -0
  126. package/dist/plain/index.d.ts +15 -0
  127. package/dist/plain/liquid.d.ts +22 -0
  128. package/dist/plain/markdown.d.ts +74 -0
  129. package/dist/plain/parser.d.ts +87 -0
  130. package/dist/plain/spec.d.ts +90 -0
  131. package/dist/plain/stdlib.d.ts +14 -0
  132. package/dist/question-dialog.d.ts +51 -0
  133. package/dist/questioning.d.ts +55 -0
  134. package/dist/quiet.d.ts +34 -0
  135. package/dist/regenerate/activity.d.ts +207 -0
  136. package/dist/regenerate/agent-env.d.ts +44 -0
  137. package/dist/regenerate/agent-shell.d.ts +49 -0
  138. package/dist/regenerate/arbiter.d.ts +68 -0
  139. package/dist/regenerate/audit.d.ts +69 -0
  140. package/dist/regenerate/cases.d.ts +9 -0
  141. package/dist/regenerate/change-view.d.ts +18 -0
  142. package/dist/regenerate/choose-conformance.d.ts +32 -0
  143. package/dist/regenerate/chunk-plan.d.ts +164 -0
  144. package/dist/regenerate/codebase.d.ts +21 -0
  145. package/dist/regenerate/command-check.d.ts +40 -0
  146. package/dist/regenerate/compaction.d.ts +13 -0
  147. package/dist/regenerate/confine.d.ts +62 -0
  148. package/dist/regenerate/conform.d.ts +63 -0
  149. package/dist/regenerate/conformance-agents.d.ts +405 -0
  150. package/dist/regenerate/conformance.d.ts +189 -0
  151. package/dist/regenerate/conversation-writes.d.ts +10 -0
  152. package/dist/regenerate/credentials.d.ts +18 -0
  153. package/dist/regenerate/debug-log.d.ts +54 -0
  154. package/dist/regenerate/debugger.d.ts +31 -0
  155. package/dist/regenerate/delivery.d.ts +6 -0
  156. package/dist/regenerate/diagnose-job.d.ts +2 -0
  157. package/dist/regenerate/diagnose.d.ts +46 -0
  158. package/dist/regenerate/diff-view.d.ts +61 -0
  159. package/dist/regenerate/drift.d.ts +84 -0
  160. package/dist/regenerate/engine.d.ts +52 -0
  161. package/dist/regenerate/environment-error.d.ts +136 -0
  162. package/dist/regenerate/job-log.d.ts +28 -0
  163. package/dist/regenerate/job-panel.d.ts +122 -0
  164. package/dist/regenerate/jobs-tree.d.ts +71 -0
  165. package/dist/regenerate/jobs.d.ts +186 -0
  166. package/dist/regenerate/lanes.d.ts +16 -0
  167. package/dist/regenerate/lineage.d.ts +95 -0
  168. package/dist/regenerate/list-arguments.d.ts +21 -0
  169. package/dist/regenerate/loop.d.ts +392 -0
  170. package/dist/regenerate/masking.d.ts +19 -0
  171. package/dist/regenerate/notes.d.ts +159 -0
  172. package/dist/regenerate/os-sandbox.d.ts +89 -0
  173. package/dist/regenerate/panel-dock.d.ts +23 -0
  174. package/dist/regenerate/paths.d.ts +92 -0
  175. package/dist/regenerate/plain-palette.d.ts +34 -0
  176. package/dist/regenerate/prompt.d.ts +192 -0
  177. package/dist/regenerate/records.d.ts +88 -0
  178. package/dist/regenerate/regenerate.d.ts +108 -0
  179. package/dist/regenerate/regression.d.ts +58 -0
  180. package/dist/regenerate/release.d.ts +123 -0
  181. package/dist/regenerate/requires.d.ts +96 -0
  182. package/dist/regenerate/resources.d.ts +53 -0
  183. package/dist/regenerate/self-check.d.ts +29 -0
  184. package/dist/regenerate/services.d.ts +28 -0
  185. package/dist/regenerate/shell.d.ts +91 -0
  186. package/dist/regenerate/side-panel.d.ts +57 -0
  187. package/dist/regenerate/spec-map-view.d.ts +139 -0
  188. package/dist/regenerate/spec-map.d.ts +138 -0
  189. package/dist/regenerate/spec-steps.d.ts +137 -0
  190. package/dist/regenerate/state.d.ts +20 -0
  191. package/dist/regenerate/steer.d.ts +34 -0
  192. package/dist/regenerate/steering.d.ts +56 -0
  193. package/dist/regenerate/stop-report.d.ts +43 -0
  194. package/dist/regenerate/submit-work.d.ts +42 -0
  195. package/dist/regenerate/test-kinds.d.ts +42 -0
  196. package/dist/regenerate/test-scripts.d.ts +221 -0
  197. package/dist/regenerate/testing.d.ts +141 -0
  198. package/dist/regenerate/transcript-view.d.ts +21 -0
  199. package/dist/regenerate/unit-gate.d.ts +27 -0
  200. package/dist/regenerate/unit-review.d.ts +152 -0
  201. package/dist/regenerate/web.d.ts +61 -0
  202. package/dist/regenerate/workspace.d.ts +12 -0
  203. package/dist/regenerate-cli.d.ts +1 -0
  204. package/dist/release-agent.d.ts +26 -0
  205. package/dist/release-cli.d.ts +1 -0
  206. package/dist/release-tools.d.ts +38 -0
  207. package/dist/resume-line.d.ts +2 -0
  208. package/dist/review/live.d.ts +72 -0
  209. package/dist/rules.d.ts +20 -0
  210. package/dist/run-report.d.ts +88 -0
  211. package/dist/screen-padding.d.ts +28 -0
  212. package/dist/self-invocation.d.ts +25 -0
  213. package/dist/setup.d.ts +43 -0
  214. package/dist/skill-progress.d.ts +46 -0
  215. package/dist/skill.d.ts +98 -0
  216. package/dist/skills.d.ts +30 -0
  217. package/dist/spec-diff-cli.d.ts +1 -0
  218. package/dist/spec-tools.d.ts +36 -0
  219. package/dist/stack.d.ts +58 -0
  220. package/dist/stacks.d.ts +12 -0
  221. package/dist/stdlib.d.ts +27 -0
  222. package/dist/terminal-images.d.ts +2 -0
  223. package/dist/tools.d.ts +101 -0
  224. package/dist/validate-cli.d.ts +1 -0
  225. package/docs/README.md +2 -0
  226. package/docs/hosting.md +52 -0
  227. package/docs/images/turri-banner.gif +0 -0
  228. package/docs/installation.md +1 -1
  229. package/docs/theming.md +8 -6
  230. package/docs/using.md +1 -1
  231. package/package.json +8 -1
  232. package/docs/images/turri-banner.png +0 -0
  233. package/docs/images/turri-screenshot.png +0 -0
package/dist/jellyfish.js CHANGED
@@ -1,114 +1,247 @@
1
1
  import { sgr } from "./regenerate/plain-palette.js";
2
- /** The pixels' colours on a dark background and a light one - the tentacles darker on light, where neon does not show. */
2
+ /** How much of a pixel shows. */
3
+ const ALPHA = { p: 1, m: 1, i: 1, e: 1, h: 1, o: 1, N: 1, n: 0.5, c: 0.6 };
4
+ /** The pixels' colours on a dark background and a light one - the tentacles blue whale on light, where neon does not show. */
3
5
  export const JELLYFISH_COLOURS = {
4
- dark: { p: "#ff5c8d", h: "#ff7fa6", w: "#f4f7f9", o: "#ff7a3d", e: "#732553", N: "#8eebee", n: "#5ce1e6", l: "#85a3b2", g: "#3e5a6b" },
5
- light: { p: "#ff5c8d", h: "#ff7fa6", w: "#f4f7f9", o: "#ff7a3d", e: "#732553", N: "#3e5a6b", n: "#1e3442", l: "#85a3b2", g: "#85a3b2" },
6
+ dark: { p: "#ff5c8d", m: "#a9479a", i: "#5b57d9", e: "#732553", h: "#ff7fa6", o: "#ff7a3d", N: "#5ce1e6", n: "#5ce1e6", c: "#5ce1e6" },
7
+ light: { p: "#ff5c8d", m: "#a9479a", i: "#5b57d9", e: "#732553", h: "#ff7fa6", o: "#ff7a3d", N: "#1e3442", n: "#1e3442", c: "#1e3442" },
6
8
  };
7
- /**
8
- * How shut the bell is, frame by frame - a quarter second each: shut quickly, held a moment, opened slowly, at rest
9
- * until the next stroke.
10
- */
11
- const SHUT = [0.1, 0.55, 0.9, 1, 0.85, 0.7, 0.56, 0.44, 0.33, 0.23, 0.14, 0.06];
12
- const LARGE = {
13
- width: 24,
14
- height: 26,
15
- open: [6, 12, 16, 18, 20, 22, 22, 24, 24],
16
- shut: [6, 10, 14, 16, 18, 18, 18, 20, 20],
17
- glint: [[-3, 2], [-4, 2], [-5, 3], [-6, 4], [-6, 5]],
18
- shine: [-2, 2],
19
- gut: [".oo..oo.", "oooooooo", "oooooooo", ".oo..oo."],
20
- gutRow: 4,
21
- strands: [
22
- { at: -8, length: 10, sway: 1.1, ink: "N" },
23
- { at: -4.5, length: 13, sway: 1.3, ink: "N" },
24
- { at: -1, length: 14, sway: 0.8, ink: "n" },
25
- { at: 2, length: 13, sway: 0.8, ink: "n" },
26
- { at: 5.5, length: 13, sway: 1.3, ink: "N" },
27
- { at: 9, length: 10, sway: 1.1, ink: "N" },
28
- ],
29
- };
30
- const SMALL = {
31
- width: 18,
32
- height: 20,
33
- open: [4, 10, 12, 14, 16, 18, 18],
34
- shut: [4, 8, 10, 12, 14, 14, 14],
35
- glint: [[-2, 1], [-3, 2], [-4, 3]],
36
- shine: [-1, 1],
37
- gut: ["oo..oo", "oooooo", "oo..oo"],
38
- gutRow: 3,
39
- strands: [
40
- { at: -6, length: 8, sway: 0.9, ink: "N" },
41
- { at: -3, length: 10, sway: 1, ink: "N" },
42
- { at: -0.5, length: 11, sway: 0.7, ink: "n" },
43
- { at: 1.5, length: 10, sway: 0.7, ink: "n" },
44
- { at: 4, length: 10, sway: 1, ink: "N" },
45
- { at: 7, length: 8, sway: 0.9, ink: "N" },
46
- ],
9
+ /** What a pixel short of whole is laid over, to draw it in half blocks: abyss, or white. */
10
+ const BACKGROUND = { dark: "#0a0f1a", light: "#f4f7f9" };
11
+ /** How much of a pixel shows, for a picture's alpha. */
12
+ export function jellyfishAlpha(pixel) {
13
+ return ALPHA[pixel] ?? 0;
14
+ }
15
+ /** The lifecycle's grid, and the sea floor the polyp stands on. */
16
+ const WIDTH = 40;
17
+ const HEIGHT = 60;
18
+ const FLOOR = 59;
19
+ const put = (grid, x, y, pixel, bounded = true) => {
20
+ x = Math.round(x);
21
+ y = Math.round(y);
22
+ if (bounded && (x < 0 || x > WIDTH - 1 || y < 0 || y > FLOOR))
23
+ return;
24
+ grid.set(`${x},${y}`, pixel);
47
25
  };
48
- /** One frame of a design: its height in rows of its width in pixels, "." where there is none. */
49
- function frame(design, t) {
50
- const grid = Array.from({ length: design.height }, () => Array(design.width).fill("."));
51
- const centre = design.width / 2;
52
- const set = (y, x, pixel, over = true) => {
53
- if (y < 0 || y >= design.height || x < 0 || x >= design.width)
54
- return;
55
- if (over || grid[y][x] === ".")
56
- grid[y][x] = pixel;
57
- };
58
- const shut = SHUT[t % SHUT.length];
59
- // Shut, it has risen a pixel.
60
- const top = shut > 0.6 ? 0 : 1;
61
- const widths = design.open.map((open, row) => 2 * Math.round((open - (open - design.shut[row]) * shut) / 2));
62
- widths.forEach((width, row) => {
63
- for (let x = centre - width / 2; x < centre + width / 2; x++)
64
- set(top + row, x, "p");
65
- });
66
- const rimWidth = widths.at(-1);
67
- const rim = top + widths.length;
68
- // The underside along the bottom row, the rim scalloped under it.
69
- for (let x = centre - rimWidth / 2 + 2; x < centre + rimWidth / 2 - 2; x++)
70
- set(rim - 1, x, "e");
71
- for (let x = centre - rimWidth / 2; x < centre + rimWidth / 2; x += 2)
72
- set(rim, x + 1, "p");
73
- // A glint up on the left, curving with the dome, and a pixel of shine.
74
- for (const [dx, dy] of design.glint)
75
- set(top + dy, centre + dx, "h");
76
- set(top + design.shine[1], centre + design.shine[0], "w");
77
- const gutLeft = centre - Math.floor(design.gut[0].length / 2);
78
- design.gut.forEach((line, dy) => [...line].forEach((pixel, dx) => pixel === "o" && set(top + design.gutRow + dy, gutLeft + dx, "o")));
79
- // The tentacles: rooted under the bell, drawn in as it shuts, swaying - the further down, the later.
80
- const phase = (2 * Math.PI * t) / SHUT.length;
81
- design.strands.forEach((strand, i) => {
82
- const root = centre - 0.5 + strand.at * (rimWidth / design.open.at(-1));
83
- for (let y = 0; y < strand.length; y++) {
84
- const x = Math.round(root + strand.sway * Math.sin(phase - y * 0.55 + i * 0.8) * Math.min(1, y / 3));
85
- const left = strand.length - y;
86
- // The tips come apart, every other pixel, fading - into code.
87
- if (left <= 3 && y % 2 === 1)
26
+ /** The bell's colour down its height: pink, through its midtone, to its shadow - dithered where one meets the next. */
27
+ const band = (t, x, y) => t < 0.3 ? "p" : t < 0.42 ? ((x + y) % 2 ? "p" : "m") : t < 0.62 ? "m" : t < 0.74 ? ((x + y) % 2 ? "m" : "i") : "i";
28
+ /** An ellipse filled with the bell's colours, or its top half - a dome. */
29
+ function shape(grid, cx, cy, rx, ry, dome, bounded = true) {
30
+ for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++) {
31
+ if (dome && y > cy)
32
+ continue;
33
+ for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {
34
+ const dx = (x + 0.5 - cx) / rx;
35
+ const dy = (y + 0.5 - cy) / ry;
36
+ if (dx * dx + dy * dy > 1)
88
37
  continue;
89
- set(rim + 1 + y, x, left > 5 ? strand.ink : left > 2 ? "l" : "g", false);
90
- // The oral arms are frilled.
91
- if (strand.ink === "n" && left > 5 && (y + t) % 3 === 0)
92
- set(rim + 1 + y, x + (strand.at < 0 ? -1 : 1), "n", false);
38
+ const t = dome ? (y - (cy - ry)) / ry : (y - (cy - ry)) / (2 * ry);
39
+ put(grid, x, y, band(Math.max(0, Math.min(1, t)), x, y), bounded);
93
40
  }
94
- });
95
- return grid.map((row) => row.join(""));
41
+ }
96
42
  }
97
- export const JELLYFISH_WIDTH = LARGE.width;
98
- export const JELLYFISH_HEIGHT = LARGE.height;
99
- /** The large jellyfish's frames, a stroke: what the pictures are rendered from. */
100
- export const JELLYFISH_FRAMES = SHUT.map((_, t) => frame(LARGE, t));
101
- /** The small jellyfish's frames, a stroke: what a terminal without images draws, in half blocks. */
102
- export const JELLYFISH_TEXT_FRAMES = SHUT.map((_, t) => frame(SMALL, t));
43
+ /** The bell's rim: every fourth pixel from the middle left out, the rest eggplant. */
44
+ function rim(grid, y) {
45
+ for (let x = 0; x < WIDTH + 1; x++) {
46
+ const key = `${x},${y}`;
47
+ if (!grid.has(key))
48
+ continue;
49
+ if (Math.abs(x - 20) % 4 === 2)
50
+ grid.delete(key);
51
+ else
52
+ grid.set(key, "e");
53
+ }
54
+ }
55
+ /** The glint on the bell, up on the left. */
56
+ const GLINT = [[11, 6], [12, 5], [13, 5], [10, 7], [11, 7], [14, 4]];
57
+ /** Where each of the thirteen tentacles hangs, how long it is, and its place in the sway. */
58
+ const TENTACLES = [8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32].map((x, i) => ({ x, length: 16 + ((i * 7) % 12), i }));
59
+ /** The jellyfish at a scale `k`, its rim on row `cy`, its tentacles grown `grown` of the way. */
60
+ function medusa(grid, k, cy, grown) {
61
+ shape(grid, 20.5, cy, 14.5 * k, 16 * k, true);
62
+ if (k > 0.45)
63
+ rim(grid, Math.floor(cy));
64
+ if (k > 0.8) {
65
+ for (const [gx, gy] of GLINT) {
66
+ const x = Math.round(20 + (gx - 20) * k);
67
+ const y = Math.round(cy + (gy - 18) * k);
68
+ if (grid.has(`${x},${y}`))
69
+ put(grid, x, y, "h");
70
+ }
71
+ }
72
+ const gutY = cy - 7 * k;
73
+ const halfWidth = Math.max(1.5, 2.5 * k);
74
+ const halfHeight = Math.max(0.6, 1.5 * k);
75
+ for (let y = Math.round(gutY - halfHeight); y < gutY + halfHeight; y++)
76
+ for (let x = Math.round(20 - halfWidth); x < 20 + halfWidth; x++)
77
+ put(grid, x, y, "o");
78
+ if (k > 0.7) {
79
+ for (let x = 19; x <= 21; x++) {
80
+ put(grid, x, gutY - 2, "o");
81
+ put(grid, x, gutY + 2, "o");
82
+ }
83
+ }
84
+ for (const { x, length, i } of TENTACLES) {
85
+ const shown = Math.round(length * Math.pow(grown, 1.2));
86
+ const root = 20 + (x - 20) * k;
87
+ for (let j = 0; j < shown; j++)
88
+ put(grid, root + Math.round(Math.sin(j * 0.5 + i) * 1.6 * (j / shown)), cy + 1 + j, j % 2 ? "n" : "N");
89
+ }
90
+ }
91
+ /** The polyp on the floor, grown `u` of the way: a stalk, a head with its gut, a crown of tentacles. */
92
+ function polyp(grid, u) {
93
+ shape(grid, 20, FLOOR, 3, 1.4, true);
94
+ const height = Math.round(2 + 12 * u);
95
+ const radius = 1.4 + 2.2 * u;
96
+ for (let y = FLOOR - height; y <= FLOOR; y++) {
97
+ for (let x = 19; x <= 20; x++)
98
+ put(grid, x, y, band(0.45 + (0.55 * (y - (FLOOR - height))) / Math.max(1, height), x, y));
99
+ }
100
+ const head = FLOOR - height - radius + 1;
101
+ shape(grid, 20, head, radius, radius, false);
102
+ put(grid, 19, head, "o");
103
+ put(grid, 20, head, "o");
104
+ if (radius > 2.5) {
105
+ put(grid, 19, head - 1, "o");
106
+ put(grid, 20, head - 1, "o");
107
+ }
108
+ const crown = [[-4, -3], [3, -3], [-1, -5], [1, -5], [-5, -4], [4, -4], [-1, -6], [1, -6], [-6, -5], [5, -5]];
109
+ const scale = radius / 3.6;
110
+ for (let i = 0; i < Math.floor(u * crown.length); i++) {
111
+ const [dx, dy] = crown[i];
112
+ put(grid, 20 + dx * scale + (dx < 0 ? 0 : 0.5), head + dy * scale, i % 2 ? "c" : "N");
113
+ }
114
+ }
115
+ const ease = (t) => (t < 0 ? 0 : t > 1 ? 1 : t * t * (3 - 2 * t));
116
+ /** The lifecycle: twenty frames of the polyp growing into the jellyfish, nineteen of it sinking and the polyp growing back. */
117
+ function lifecycle() {
118
+ const frames = [];
119
+ for (let f = 0; f < 20; f++) {
120
+ const grid = new Map();
121
+ const g = f / 19;
122
+ const p = ease((g - 0.2) / 0.65);
123
+ if (1 - p > 0.06)
124
+ polyp(grid, 1 - p);
125
+ else if (p < 1)
126
+ shape(grid, 20, FLOOR, 2.2, 1, true);
127
+ const e = ease((g - 0.08) / 0.92);
128
+ if (e > 0)
129
+ medusa(grid, 0.18 + 0.82 * e, 40 - 22 * e, Math.pow(e, 1.5));
130
+ frames.push(grid);
131
+ }
132
+ // It sinks to the floor first, and the polyp grows up from there.
133
+ for (let f = 0; f <= 18; f++) {
134
+ const grid = new Map();
135
+ if (f <= 8) {
136
+ const s = f / 8;
137
+ medusa(grid, 1 - 0.72 * s, 18 + 40 * s, 1 - s);
138
+ }
139
+ else if (f <= 11) {
140
+ const w = (f - 9) / 2;
141
+ shape(grid, 20, FLOOR, 4.2 - 1.2 * w, 3.2 - 0.8 * w, true);
142
+ put(grid, 19, FLOOR - 1, "o");
143
+ put(grid, 20, FLOOR - 1, "o");
144
+ }
145
+ else
146
+ polyp(grid, (f - 11) / 7);
147
+ frames.push(grid);
148
+ }
149
+ return frames;
150
+ }
151
+ /**
152
+ * The jellyfish swimming, grown: its bell still, its tentacles in one of two strokes - the second drawn a pixel higher,
153
+ * as it bobs.
154
+ */
155
+ function hero(stroke) {
156
+ const grid = new Map();
157
+ shape(grid, 20.5, 18, 14.5, 16, true, false);
158
+ rim(grid, 18);
159
+ for (const [x, y] of GLINT)
160
+ if (grid.has(`${x},${y}`))
161
+ put(grid, x, y, "h", false);
162
+ for (let y = 10; y <= 12; y++)
163
+ for (let x = 18; x <= 22; x++)
164
+ put(grid, x, y, "o", false);
165
+ for (let x = 19; x <= 21; x++) {
166
+ put(grid, x, 9, "o", false);
167
+ put(grid, x, 13, "o", false);
168
+ }
169
+ for (const { x, length, i } of TENTACLES) {
170
+ for (let k = 0; k < length; k++) {
171
+ const sway = Math.round(Math.sin(k * 0.5 + i + (stroke === 1 ? 2.2 : 0)) * 1.6 * (k / length));
172
+ put(grid, x + sway, 19 + k, k % 2 ? "n" : "N", false);
173
+ }
174
+ }
175
+ if (stroke === 0)
176
+ return grid;
177
+ const raised = new Map();
178
+ for (const [key, pixel] of grid) {
179
+ const [x, y] = key.split(",").map(Number);
180
+ raised.set(`${x},${y - 1}`, pixel);
181
+ }
182
+ return raised;
183
+ }
184
+ /** Every frame on one grid, cut to what any of them draws on: rows of pixels, "." where there is none. */
185
+ function crop(grids) {
186
+ const cells = grids.flatMap((grid) => [...grid.keys()].map((key) => key.split(",").map(Number)));
187
+ const left = Math.min(...cells.map(([x]) => x));
188
+ const right = Math.max(...cells.map(([x]) => x));
189
+ const top = Math.min(...cells.map(([, y]) => y));
190
+ const bottom = Math.max(...cells.map(([, y]) => y));
191
+ return grids.map((grid) => Array.from({ length: bottom - top + 1 }, (_, y) => Array.from({ length: right - left + 1 }, (_, x) => grid.get(`${x + left},${y + top}`) ?? ".").join("")));
192
+ }
193
+ /** The lifecycle's frames, then the jellyfish's two strokes, swimming. */
194
+ const LIFECYCLE = lifecycle();
195
+ const SWIM = LIFECYCLE.length;
196
+ /** The jellyfish's frames, each as rows of pixels: what the pictures are rendered from. */
197
+ export const JELLYFISH_FRAMES = crop([...LIFECYCLE, hero(0), hero(1)]);
198
+ export const JELLYFISH_WIDTH = JELLYFISH_FRAMES[0][0].length;
199
+ export const JELLYFISH_HEIGHT = JELLYFISH_FRAMES[0].length;
200
+ /**
201
+ * The order the frames play in, round and round: the polyp a moment, growing into the jellyfish, swimming four
202
+ * seconds - a stroke every five frames - then sinking, and the polyp growing back.
203
+ */
204
+ export const JELLYFISH_SEQUENCE = [
205
+ ...[0, 0, 0],
206
+ ...Array.from({ length: 20 }, (_, f) => f),
207
+ ...Array.from({ length: 27 }, (_, f) => SWIM + (Math.floor(f / 5) % 2)),
208
+ ...Array.from({ length: 19 }, (_, f) => 20 + f),
209
+ ];
103
210
  /** How long each frame shows. */
104
- export const JELLYFISH_FRAME_MS = 250;
211
+ export const JELLYFISH_FRAME_MS = 150;
212
+ /** How much smaller the frames are drawn in half blocks: a cell is a block of SHRINK pixels square. */
213
+ const SHRINK = 3;
214
+ /**
215
+ * A frame a third of the size: each block drawn as the pixel at its middle. What most of a block is would run the
216
+ * tentacles, two pixels apart, together into one fringe; its middle keeps strands apart.
217
+ */
218
+ function shrink(pixels) {
219
+ const middle = Math.floor(SHRINK / 2);
220
+ const rows = [];
221
+ for (let y = middle; y < pixels.length; y += SHRINK) {
222
+ let row = "";
223
+ for (let x = middle; x < pixels[0].length; x += SHRINK)
224
+ row += pixels[y][x];
225
+ rows.push(row);
226
+ }
227
+ return rows;
228
+ }
229
+ /** The frames as a terminal without images draws them, in half blocks: the same order, a third of the size. */
230
+ export const JELLYFISH_TEXT_FRAMES = JELLYFISH_FRAMES.map(shrink);
231
+ /** A colour laid over another, `alpha` of it showing. */
232
+ function over(hex, under, alpha) {
233
+ if (alpha >= 1)
234
+ return hex;
235
+ const channel = (colour, at) => Number.parseInt(colour.slice(at, at + 2), 16);
236
+ return `#${[1, 3, 5].map((at) => Math.round(channel(hex, at) * alpha + channel(under, at) * (1 - alpha)).toString(16).padStart(2, "0")).join("")}`;
237
+ }
105
238
  /**
106
239
  * A frame drawn in half blocks for a terminal that shows no images: two pixels a cell, the upper one the text's colour
107
240
  * and the lower one the background's, in as many colours as the terminal has: a column a pixel, a row every two.
108
241
  */
109
242
  export function halfBlocks(pixels, appearance, truecolor) {
110
243
  const colours = JELLYFISH_COLOURS[appearance];
111
- const colour = (pixel) => (pixel === undefined || pixel === "." ? undefined : colours[pixel]);
244
+ const colour = (pixel) => pixel === undefined || pixel === "." ? undefined : over(colours[pixel], BACKGROUND[appearance], ALPHA[pixel]);
112
245
  const rows = [];
113
246
  for (let y = 0; y < pixels.length; y += 2) {
114
247
  let row = "";
@@ -0,0 +1,25 @@
1
+ import type { AgentSession } from "@earendil-works/pi-coding-agent";
2
+ import { type Verdict } from "./tools.ts";
3
+ import type { ReviewSpec } from "./skill.ts";
4
+ export interface JudgeResult {
5
+ index: number;
6
+ model: string;
7
+ verdict: Verdict | undefined;
8
+ }
9
+ export interface GateOutcome {
10
+ passed: boolean;
11
+ results: JudgeResult[];
12
+ /** Feedback handed back to the executor on retry. Empty when the gate passed. */
13
+ critique: string;
14
+ }
15
+ export declare function runGate(opts: {
16
+ cwd: string;
17
+ review: ReviewSpec;
18
+ rubric: string;
19
+ artifactPath: string;
20
+ artifact: string;
21
+ executor: AgentSession;
22
+ /** Used when the gate does not name a model - normally the conversation's. */
23
+ defaultModel?: string;
24
+ onJudgeStart?: (index: number, model: string) => void;
25
+ }): Promise<GateOutcome>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import { type DriftReport, type ModuleStatus, type WorkLoop } from "./regenerate/loop.ts";
2
+ import { type ModuleState } from "./run-report.ts";
3
+ /**
4
+ * What every command that builds - `turri regenerate`, `conform`, `spec-diff` - does around its own
5
+ * request of the work loop, as the chat does it: the project's models, a job that fails run once more and, failing again,
6
+ * diagnosed, a line as each job starts and ends; then the loop run until nothing more can start, how each job ended,
7
+ * and exit 1 when any failed or a module is not complete. With `dryRun`, the work is planned and the prompt of the first job each codebase would
8
+ * start printed, and nothing runs.
9
+ */
10
+ export declare function runLoop(options: {
11
+ plainFile: string;
12
+ /** Where the code is exported to as the run ends, however it ends: the furthest-built codebase's last commit. */
13
+ buildDir?: string;
14
+ /** The run report's file (`--report`): see run-report.ts. */
15
+ report?: string;
16
+ /** The command, as the report gives it: its name and its arguments. */
17
+ command?: string[];
18
+ dryRun?: boolean;
19
+ /** Start over, unasked, a codebase that has to be on a module it now requires (`--start-over`). */
20
+ startOver?: boolean;
21
+ /** The request: bring up, regenerate, conformance. */
22
+ ask: (loop: WorkLoop) => Promise<ModuleStatus[]>;
23
+ }): Promise<never>;
24
+ /** How each codebase stands, from what is left to regenerate there. */
25
+ export declare function moduleStates(reports: DriftReport[]): ModuleState[];
26
+ /**
27
+ * The options every command that regenerates takes for a headless run: `--report` and `--build-dir`, and the config's
28
+ * own keys (configKeyOptions) - `--harness-model`, `--coding-model` (`--model`), `--judge-model`, `--review`, `--mutation-testing` and, where
29
+ * `conformance` is true, `--conformance-tests` - read as if config.turri.yaml had them. Exits on a value it cannot read.
30
+ */
31
+ export declare function headlessOptions(options: Record<string, string | undefined>, conformance: boolean): {
32
+ report?: string;
33
+ buildDir?: string;
34
+ };
@@ -0,0 +1,80 @@
1
+ import { type ExtensionContext, type ToolDefinition } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { type ModuleStatus, type WorkLoop } from "./regenerate/loop.ts";
4
+ import type { RenderWork } from "./regenerate/engine.ts";
5
+ import { type JobRegistry } from "./regenerate/jobs.ts";
6
+ /**
7
+ * The tools that act on the work loop - `regenerate`, `drift` and `halt` - defined once, for the conversation and for the
8
+ * skills alike: a skill that regenerates what a spec holds, or reads how far it is regenerated, uses the conversation's
9
+ * own tools, on the conversation's loop, never copies of them.
10
+ */
11
+ type AnyTool = ToolDefinition<any, any, any>;
12
+ /** How the queue is to be planned, for the work this call queues - see regenerate/steer.ts. */
13
+ export declare const GUIDANCE_PARAM: Type.TOptional<Type.TString>;
14
+ export declare const WORKING_DIR_PARAM: Type.TOptional<Type.TString>;
15
+ export declare const MODEL_PARAM: Type.TOptional<Type.TString>;
16
+ export declare const STACK_PARAM: Type.TOptional<Type.TString>;
17
+ /**
18
+ * Why a module asked for with no stack named cannot be built: a project of stacks where it has no stack of its own and
19
+ * none is chosen, and config.turri.yaml alone names no unit testing script - every pulse would stop for want of one.
20
+ * The stacks and the script each names, and what to do. Undefined when it can be built.
21
+ */
22
+ export declare function stacklessBuild(plainFile: string): string | undefined;
23
+ /**
24
+ * `work` with the stack a call names chosen for it - or, none named, as it is: each module is then built with its own
25
+ * (`stackForModule`). A module that could not be built that way is refused before anything is planned (`stacklessBuild`).
26
+ */
27
+ export declare function withCallStack<T>(plainFile: string, named: string | undefined, work: () => T): T;
28
+ export declare const WAIT_PARAM: Type.TOptional<Type.TBoolean>;
29
+ /**
30
+ * A render tool's arguments as if turri had been started in `working_dir`: the .plain file
31
+ * and the build folder made absolute from there, and that folder as `base`.
32
+ */
33
+ export declare function fromWorkingDir<T extends {
34
+ working_dir?: string;
35
+ plain_file?: string;
36
+ build_dir?: string;
37
+ }>(cwd: string, params: T): T & {
38
+ base: string;
39
+ };
40
+ /** What the loop is doing for a spec's modules, for the conversation - and, waiting, how each module ended. */
41
+ export declare function loopReply(loop: WorkLoop, statuses: ModuleStatus[], wait: boolean): Promise<{
42
+ content: {
43
+ type: "text";
44
+ text: string;
45
+ }[];
46
+ details: {
47
+ jobIds: string[];
48
+ };
49
+ }>;
50
+ /** Halt a pulse by its id, or the start of it: what it was, and what moves up behind it. `reason`: why, kept with it. */
51
+ export declare function stopJob(jobs: JobRegistry, named: string, reason?: string, loop?: WorkLoop): string;
52
+ export interface LoopToolsOptions {
53
+ /** Where turri was started: the folder plain_file and working_dir are relative to. */
54
+ cwd: string;
55
+ /** The pulses `halt` stops. Without them - the release agent's - it stops only what activate started. */
56
+ jobs?: JobRegistry;
57
+ /** The work loop the tools act on - the conversation's, for a call in it. */
58
+ loop?: (ctx: ExtensionContext) => WorkLoop;
59
+ /** The model a pulse of this call regenerates on: the one named, or the one chosen for the work. Default: the one named. */
60
+ model?: (ctx: ExtensionContext, plainFile: string, named?: string, work?: RenderWork) => string | undefined;
61
+ /** Whether a call waits for its work to finish, given what it asked. Default: only when it asked to. */
62
+ wait?: (ctx: ExtensionContext, asked?: boolean) => boolean;
63
+ }
64
+ /** Whether a /regenerate's words ask for the module from scratch: its code deleted, and all of it regenerated again. */
65
+ export declare function asksFromScratch(words: string): boolean;
66
+ /** Whether a /regenerate asking for its module from scratch asks for its whole requires chain with it. */
67
+ export declare function asksWholeChain(words: string): boolean;
68
+ /** regenerate: regenerate a module's code up to its spec as it is now. */
69
+ export declare function regenerateTool(options: LoopToolsOptions & {
70
+ loop: (ctx: ExtensionContext) => WorkLoop;
71
+ }): AnyTool;
72
+ /** drift: what is left to regenerate - planning nothing, starting nothing. */
73
+ export declare function driftTool(options: LoopToolsOptions & {
74
+ loop: (ctx: ExtensionContext) => WorkLoop;
75
+ }): AnyTool;
76
+ /** halt: stop a pulse, or what activate started. */
77
+ export declare function haltTool(options: Pick<LoopToolsOptions, "jobs"> & {
78
+ workLoop?: () => WorkLoop | undefined;
79
+ }): AnyTool;
80
+ export {};
@@ -0,0 +1,53 @@
1
+ import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
2
+ import { type Job, type JobRegistry } from "./regenerate/jobs.ts";
3
+ /**
4
+ * The tools a skill that matures a spec works with, besides the conversation's regenerate, drift and halt: wait for
5
+ * the builds, and read back where the spec left the agents to decide.
6
+ *
7
+ * A render that passes can still have been built on a guess: where the spec is silent, the agent
8
+ * decides, and says so in the working notes' assumptions. Those, the pitfalls, a failed job's
9
+ * diagnosis and a spec that contradicts itself are what the spec should have said - and a skill
10
+ * reading them can write it in, so the next build does not have to guess.
11
+ */
12
+ type AnyTool = ToolDefinition<any, any, any>;
13
+ /** What a maturing skill found: where it came from, and what it says. */
14
+ export interface SpecFinding {
15
+ id: string;
16
+ kind: "assumption" | "pitfall" | "diagnosis" | "conflict";
17
+ /** The module whose codebase it came from, or the job it came from. */
18
+ where: string;
19
+ text: string;
20
+ }
21
+ /** A time as the notes stamp their lines: "2026-09-28 17:40". */
22
+ export declare function noteStamp(date: Date): string;
23
+ /**
24
+ * What the agents and the harness recorded since `since`: the assumptions and pitfalls in every
25
+ * codebase's working notes, the diagnosis of every job that failed for good, and every spec that
26
+ * contradicts itself.
27
+ */
28
+ export declare function collectFindings(options: {
29
+ project: string;
30
+ jobs: Job[];
31
+ since: Date;
32
+ }): Omit<SpecFinding, "id">[];
33
+ /** Why a build is struggling: a key for what kind of struggle it is - handed out once each - and what it says. */
34
+ export interface Struggle {
35
+ key: string;
36
+ why: string;
37
+ }
38
+ /** How long a build may run before it counts as struggling on its length alone. */
39
+ export declare const LONG_BUILD_MS: number;
40
+ /**
41
+ * Whether a running build is struggling, and how: sent back four times or more in one loop, five
42
+ * setbacks or more, a spec conflict raised, a failure being run again, or running far longer than a
43
+ * build should. Each kind comes once per build - a round count again only every four rounds more.
44
+ */
45
+ export declare function struggling(job: Job, now?: number): Struggle | undefined;
46
+ export declare function createMatureTools(options: {
47
+ jobs: JobRegistry;
48
+ cwd: string;
49
+ startedAt?: Date;
50
+ }): AnyTool[];
51
+ /** The names of the maturing tools, as a skill's allowed-tools names them: its own - it regenerates, halts and reads drift with the conversation's (loop-tools.ts). */
52
+ export declare const MATURE_TOOLS: string[];
53
+ export {};
@@ -0,0 +1,42 @@
1
+ import { type ProjectConfig } from "./config.ts";
2
+ /**
3
+ * Whether the models a project's config names answer, with the credentials this machine has - for any provider pi
4
+ * supports. Each model the config uses (harness-model, coding-model, judge-model and every role under models:) is sent
5
+ * one short request through pi's own model runtime, the one turri's agents use, so the credentials are found as they
6
+ * will be when it builds: an API key in the environment, pi's /login, an AWS profile. A model pi does not know, a key
7
+ * it rejects, an account without access to the model - each is said, with what to do about it.
8
+ */
9
+ /** A model the config uses, and which of its settings name it. */
10
+ export interface UsedModel {
11
+ spec: string;
12
+ usedBy: string[];
13
+ }
14
+ /** Every model the config uses, each once, with the settings that name it - the two models first. */
15
+ export declare function modelsInUse(config: Pick<ProjectConfig, "harnessModel" | "codingModel" | "judgeModel" | "models">): UsedModel[];
16
+ export type ModelVerdict = {
17
+ ok: true;
18
+ spec: string;
19
+ usedBy: string[];
20
+ ms: number;
21
+ } | {
22
+ ok: false;
23
+ spec: string;
24
+ usedBy: string[];
25
+ problem: string;
26
+ fix: string;
27
+ };
28
+ /** What it takes to talk to a model: pi's runtime, or a stand-in for tests. */
29
+ export interface ModelAccess {
30
+ /** The model, as pi knows it - undefined when it does not. */
31
+ find(provider: string, id: string): unknown;
32
+ /** Other models of the provider pi knows, to suggest from. */
33
+ known(provider: string): string[];
34
+ /** One short request: the error the provider gave, or nothing when it answered. */
35
+ ask(model: unknown): Promise<string | undefined>;
36
+ }
37
+ /** pi's own model runtime, with pi's credentials - auth.json, models.json and the environment - as turri's agents have them. */
38
+ export declare function piModelAccess(): Promise<ModelAccess>;
39
+ /** What to do about a provider's error, from what it says. */
40
+ export declare function fixFor(provider: string, problem: string): string;
41
+ /** Each model the config uses, tried: answered, or what is wrong and what to do. One request a model. */
42
+ export declare function checkModels(models: UsedModel[], access: ModelAccess): Promise<ModelVerdict[]>;
@@ -0,0 +1,9 @@
1
+ /** Colours for what the command line prints. */
2
+ export declare const c: {
3
+ dim: (s: string) => string;
4
+ bold: (s: string) => string;
5
+ success: (text: string) => string;
6
+ error: (text: string) => string;
7
+ warning: (text: string) => string;
8
+ accent: (text: string) => string;
9
+ };
@@ -0,0 +1 @@
1
+ import "./terminal-images.ts";