@turri-dev/cli 0.1.4-next.4342332 → 0.1.4-next.5a8c6e5

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 (39) hide show
  1. package/README.md +1 -1
  2. package/assets/jellyfish/dark-00.png +0 -0
  3. package/assets/jellyfish/dark-01.png +0 -0
  4. package/assets/jellyfish/dark-02.png +0 -0
  5. package/assets/jellyfish/dark-03.png +0 -0
  6. package/assets/jellyfish/dark-04.png +0 -0
  7. package/assets/jellyfish/dark-05.png +0 -0
  8. package/assets/jellyfish/dark-06.png +0 -0
  9. package/assets/jellyfish/dark-07.png +0 -0
  10. package/assets/jellyfish/dark-08.png +0 -0
  11. package/assets/jellyfish/dark-09.png +0 -0
  12. package/assets/jellyfish/dark-10.png +0 -0
  13. package/assets/jellyfish/dark-11.png +0 -0
  14. package/assets/jellyfish/light-00.png +0 -0
  15. package/assets/jellyfish/light-01.png +0 -0
  16. package/assets/jellyfish/light-02.png +0 -0
  17. package/assets/jellyfish/light-03.png +0 -0
  18. package/assets/jellyfish/light-04.png +0 -0
  19. package/assets/jellyfish/light-05.png +0 -0
  20. package/assets/jellyfish/light-06.png +0 -0
  21. package/assets/jellyfish/light-07.png +0 -0
  22. package/assets/jellyfish/light-08.png +0 -0
  23. package/assets/jellyfish/light-09.png +0 -0
  24. package/assets/jellyfish/light-10.png +0 -0
  25. package/assets/jellyfish/light-11.png +0 -0
  26. package/dist/extension.js +18 -38
  27. package/dist/jellyfish.js +130 -0
  28. package/dist/regenerate/plain-palette.js +1 -1
  29. package/docs/images/turri-banner.png +0 -0
  30. package/docs/theming.md +3 -3
  31. package/docs/using.md +1 -1
  32. package/package.json +1 -1
  33. package/assets/jellyfish/dark-12.png +0 -0
  34. package/assets/jellyfish/dark-13.png +0 -0
  35. package/assets/jellyfish/dark-14.png +0 -0
  36. package/assets/jellyfish/fallback.txt +0 -252
  37. package/assets/jellyfish/light-12.png +0 -0
  38. package/assets/jellyfish/light-13.png +0 -0
  39. package/assets/jellyfish/light-14.png +0 -0
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- <p align="center"><img src="docs/images/turri-banner.png" alt="turri - coding agent with the power of regenerative software: regenerate, drift, lineage"></p>
1
+ <p align="center"><img src="docs/images/turri-banner.png" alt="turri - the first coding agent with the power of regenerative software, beside a pixel jellyfish: npm install -g @turri-dev/cli"></p>
2
2
 
3
3
  <h1 align="center">turri<br>the first coding agent with the power of<br>regenerative software<br>&nbsp;</h1>
4
4
 
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/extension.js CHANGED
@@ -5,7 +5,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
5
5
  import { basename, dirname, join, relative, resolve } from "node:path";
6
6
  import { conversationCommand, conversationSettled, conversationWriting } from "./regenerate/conversation-writes.js";
7
7
  import { fileURLToPath } from "node:url";
8
- import { allocateImageId, Box, getCapabilities, getPngDimensions, isKeyRelease, isKeyRepeat, matchesKey, renderImage, Text, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
8
+ import { allocateImageId, Box, getCapabilities, getPngDimensions, isKeyRelease, isKeyRepeat, matchesKey, renderImage, Text, wrapTextWithAnsi } from "@earendil-works/pi-tui";
9
9
  import { defineTool } from "@earendil-works/pi-coding-agent";
10
10
  import { Type } from "typebox";
11
11
  import { CODING_MODEL_ENV } from "./chat-models.js";
@@ -30,6 +30,7 @@ import { checkPlainFile, checkPlainTargets, formatCheckResults } from "./plain/c
30
30
  import { whyFailed } from "./regenerate/diagnose-job.js";
31
31
  import { createWorkLoop, describeModules, isUpToDate, previewJobs } from "./regenerate/loop.js";
32
32
  import { brandNeon, brandPink, plainPaint } from "./regenerate/plain-palette.js";
33
+ import { halfBlocks, JELLYFISH_FRAME_MS, JELLYFISH_TEXT_FRAMES } from "./jellyfish.js";
33
34
  import { createJobPanel, didNotPass, keyPressed, PANEL_KEY, spinner } from "./regenerate/job-panel.js";
34
35
  import { clickToFocus, dockedWidth, narrowChat, shade, shadeChat } from "./regenerate/panel-dock.js";
35
36
  import { overPictureRow, PICTURE_AT } from "./picture-row.js";
@@ -47,7 +48,7 @@ import { inheritProviders, inheritThinkingLevel } from "./pi.js";
47
48
  import { beginCapture, captureConsole, describeCaptured, dropNoise } from "./quiet.js";
48
49
  import { loadRules, renderRulesBlock } from "./rules.js";
49
50
  import { registerTurriConfig } from "./config-command.js";
50
- import { describeStdlib, stdlibStackCount } from "./stdlib.js";
51
+ import { describeStdlib } from "./stdlib.js";
51
52
  import { peekKeyListener, runAsk } from "./ask.js";
52
53
  import { QuestionDialog } from "./question-dialog.js";
53
54
  import { registerStacks } from "./stacks.js";
@@ -370,7 +371,7 @@ export default function (pi) {
370
371
  conversationModels = ctx.modelRegistry;
371
372
  });
372
373
  inheritProviders(() => conversationModels);
373
- registerBranding(pi, rules.length, discoveredSkills);
374
+ registerBranding(pi, discoveredSkills);
374
375
  registerInterviewLog(pi);
375
376
  registerJobTools(pi, jobs, cwd);
376
377
  registerCheckPlain(pi, cwd);
@@ -2295,7 +2296,7 @@ const BANNER_ENTRY = "turri_banner";
2295
2296
  export function chatRulesBlock(rules = loadRules(join(PACKAGE_ROOT, "genome", "rules"))) {
2296
2297
  return renderRulesBlock(rules);
2297
2298
  }
2298
- function registerBranding(pi, ruleCount, skills) {
2299
+ function registerBranding(pi, skills) {
2299
2300
  pi.registerCommand("stdlib", {
2300
2301
  description: "List the ***plain standard library: the stacks a spec can import instead of writing its implementation reqs.",
2301
2302
  handler: async (_args, ctx) => {
@@ -2310,22 +2311,16 @@ function registerBranding(pi, ruleCount, skills) {
2310
2311
  const lines = [
2311
2312
  `${brandPink(theme)("turri")} ${theme.fg("muted", `v${data.version}`)}` +
2312
2313
  (data.commit !== undefined ? theme.fg("dim", ` from a clone, ${data.commit}${data.changed ? " with changes" : ""}`) : ""),
2313
- theme.fg("text", "regenerative software - the code is regenerated from the spec"),
2314
- theme.fg("muted", `${data.rules} rules · ${data.skills} skill${data.skills === 1 ? "" : "s"}` +
2315
- (data.projectSkills !== undefined && data.projectSkills > 0 ? ` (${data.projectSkills} the project's)` : "") +
2316
- (data.stdlib !== undefined && data.stdlib > 0 ? ` · ${data.stdlib} stdlib stacks (/stdlib)` : "") +
2317
- (data.pending > 0 ? theme.fg("warning", ` (${data.pending} failed to load)`) : "")),
2314
+ theme.fg("text", "the first coding agent with the power of regenerative software"),
2318
2315
  // Where the settings are changed, without leaving the chat.
2319
2316
  theme.fg("muted", "configure with /turri-config"),
2320
- theme.fg("dim", data.cwd),
2321
- // Where every command an agent runs will run - or that none can.
2322
- ...(data.sandbox
2323
- ? [theme.fg(data.sandbox.level === "ok" ? "dim" : data.sandbox.level === "limited" ? "warning" : "error", data.sandbox.text)]
2324
- : []),
2317
+ // Only what is wrong: skills that did not load, and agents' commands less fenced than they should be.
2318
+ ...(data.pending > 0 ? [theme.fg("warning", `${data.pending} skill${data.pending === 1 ? "" : "s"} failed to load`)] : []),
2319
+ ...(data.sandbox !== undefined && data.sandbox.level !== "ok" ? [theme.fg(data.sandbox.level === "limited" ? "warning" : "error", data.sandbox.text)] : []),
2325
2320
  ];
2326
2321
  // The jellyfish turri is named after, in ink for the theme's background: a picture where the
2327
2322
  // terminal shows images, drawn in text where it does not.
2328
- return new Banner(lines, (rule) => theme.fg("borderMuted", rule), jellyfishArt(theme.appearance, (art) => theme.fg("text", art)));
2323
+ return new Banner(lines, (rule) => theme.fg("borderMuted", rule), jellyfishArt(theme.appearance, (theme.getColorMode?.() ?? "truecolor") === "truecolor"));
2329
2324
  });
2330
2325
  pi.on("session_start", (_event, ctx) => {
2331
2326
  if (ctx.hasUI)
@@ -2336,12 +2331,7 @@ function registerBranding(pi, ruleCount, skills) {
2336
2331
  version: identity.version ?? "0.0.0",
2337
2332
  commit: identity.git_commit?.slice(0, 7),
2338
2333
  changed: identity.git_dirty === true,
2339
- rules: ruleCount,
2340
- skills: skills.skills.length,
2341
- projectSkills: skills.skills.filter((skill) => skill.origin !== "turri").length,
2342
2334
  pending: skills.pending.length,
2343
- stdlib: stdlibStackCount(),
2344
- cwd: ctx.cwd,
2345
2335
  sandbox: sandboxSummary(),
2346
2336
  });
2347
2337
  }
@@ -2350,10 +2340,8 @@ function registerBranding(pi, ruleCount, skills) {
2350
2340
  }
2351
2341
  });
2352
2342
  }
2353
- /** How many rows the banner's jellyfish takes: taller than the lines beside it. */
2354
- const JELLYFISH_ROWS = 13;
2355
- /** How long each frame of the jellyfish shows. */
2356
- const JELLYFISH_FRAME_MS = 250;
2343
+ /** How many rows the banner's jellyfish takes at most: as tall as the one drawn in text. */
2344
+ const JELLYFISH_ROWS = 10;
2357
2345
  /** Which frame of the jellyfish every banner shows, moved on by `animateJellyfish`. */
2358
2346
  let jellyfishFrame = 0;
2359
2347
  let jellyfishTimer;
@@ -2473,24 +2461,16 @@ class Banner {
2473
2461
  }
2474
2462
  const jellyfishFrameCache = new Map();
2475
2463
  /**
2476
- * The banner's jellyfish for this terminal: where it shows images, the picture's frames spread evenly
2477
- * over the video, light ink for a dark background and dark ink for a light one; where it does not, the
2478
- * jellyfish drawn in text, in `ink`. None when they cannot be read.
2464
+ * The banner's jellyfish for this terminal: where it shows images, the picture's frames
2465
+ * (scripts/jellyfish-frames.mjs writes them), in the colours for the theme's background; where it does not, the
2466
+ * small one in half blocks. None when the pictures cannot be read.
2479
2467
  */
2480
- function jellyfishArt(appearance, ink) {
2468
+ function jellyfishArt(appearance, truecolor) {
2481
2469
  const dir = join(PACKAGE_ROOT, "assets", "jellyfish");
2482
2470
  try {
2483
2471
  if (getCapabilities().images === null) {
2484
- // Its frames one after another, each ended by a line of "---" (scripts/jellyfish-frames.mjs writes them).
2485
- const drawn = readFileSync(join(dir, "fallback.txt"), "utf8")
2486
- .split(/^---$/m)
2487
- .map((frame) => frame.replace(/^\n+|\n+$/g, "").split("\n"))
2488
- .filter((frame) => frame.some((line) => line.trim() !== ""));
2489
- if (drawn.length === 0)
2490
- return undefined;
2491
- const columns = Math.max(...drawn.flat().map((line) => visibleWidth(line)));
2492
- const rows = Math.max(...drawn.map((frame) => frame.length));
2493
- return { kind: "text", frames: drawn.map((frame) => frame.map((line) => ink(line.padEnd(columns)))), columns, rows };
2472
+ const frames = JELLYFISH_TEXT_FRAMES.map((pixels) => halfBlocks(pixels, appearance, truecolor));
2473
+ return { kind: "text", frames, columns: JELLYFISH_TEXT_FRAMES[0][0].length, rows: frames[0].length };
2494
2474
  }
2495
2475
  let frames = jellyfishFrameCache.get(appearance);
2496
2476
  if (frames === undefined) {
@@ -0,0 +1,130 @@
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. */
3
+ 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
+ };
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
+ ],
47
+ };
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)
88
+ 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);
93
+ }
94
+ });
95
+ return grid.map((row) => row.join(""));
96
+ }
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));
103
+ /** How long each frame shows. */
104
+ export const JELLYFISH_FRAME_MS = 250;
105
+ /**
106
+ * 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
+ * and the lower one the background's, in as many colours as the terminal has: a column a pixel, a row every two.
108
+ */
109
+ export function halfBlocks(pixels, appearance, truecolor) {
110
+ const colours = JELLYFISH_COLOURS[appearance];
111
+ const colour = (pixel) => (pixel === undefined || pixel === "." ? undefined : colours[pixel]);
112
+ const rows = [];
113
+ for (let y = 0; y < pixels.length; y += 2) {
114
+ let row = "";
115
+ for (let x = 0; x < (pixels[0]?.length ?? 0); x++) {
116
+ const upper = colour(pixels[y]?.[x]);
117
+ const lower = colour(pixels[y + 1]?.[x]);
118
+ if (upper === undefined && lower === undefined)
119
+ row += " ";
120
+ else if (upper === undefined)
121
+ row += `\x1b[${sgr(lower, false, truecolor)}m▄\x1b[39m`;
122
+ else if (lower === undefined)
123
+ row += `\x1b[${sgr(upper, false, truecolor)}m▀\x1b[39m`;
124
+ else
125
+ row += `\x1b[${sgr(upper, false, truecolor)};${sgr(lower, true, truecolor)}m▀\x1b[39;49m`;
126
+ }
127
+ rows.push(row);
128
+ }
129
+ return rows;
130
+ }
@@ -47,7 +47,7 @@ const rgb = (hex) => {
47
47
  return [(value >> 16) & 255, (value >> 8) & 255, value & 255];
48
48
  };
49
49
  /** A colour's SGR parameters: 24-bit, or the nearest of the 256 where the terminal has no more. */
50
- function sgr(hex, background, truecolor) {
50
+ export function sgr(hex, background, truecolor) {
51
51
  const [r, g, b] = rgb(hex);
52
52
  if (truecolor)
53
53
  return `${background ? 48 : 38};2;${r};${g};${b}`;
Binary file
package/docs/theming.md CHANGED
@@ -25,14 +25,14 @@ Both themes, the command line's colours, the spec diff's, the banner's jellyfish
25
25
  | lines | `#22344a` | rules, dividers, frames: `borderMuted`, `mdHr` |
26
26
  | path | `#3e5a6b` | the editor's border, what is dim: `dim`, line numbers |
27
27
  | grauzone | `#85a3b2` | body text, and what can be skimmed: `text`, `muted` |
28
- | white | `#f4f7f9` | headings, tool titles, what you typed, the jellyfish |
28
+ | white | `#f4f7f9` | headings, tool titles, what you typed, the jellyfish's shine |
29
29
  | siesta tan | `#e9d8c8` | inline code, \*\*\*plain's text in quotes or backticks |
30
30
  | eggplant | `#732553` | a removed line's fill in a spec diff |
31
31
  | neon | `#5ce1e6` | links, focus, the selection, section labels, \*\*\*plain's concepts in a spec diff: `accent`, `mdLink`, regen's `◆` |
32
32
  | neon hover | `#8eebee` | types in code, an added line's fill in a light theme's spec diff |
33
- | pink | `#ff5c8d` | the wordmark, and what is done - its `✓`, the spec map's `◇` and `◆`: `success` |
33
+ | pink | `#ff5c8d` | the wordmark, the jellyfish's bell, and what is done - its `✓`, the spec map's `◇` and `◆`: `success` |
34
34
  | pink hover | `#ff7fa6` | \*\*\*plain's section headers, a removed line's `-`, keywords in code |
35
- | orange | `#ff7a3d` | the `$` of bash mode, and everything that asks for you - what failed (`✗`), what works with limits, what needs you (the spec map's `!`): `error`, `warning` |
35
+ | orange | `#ff7a3d` | the jellyfish's gut, the `$` of bash mode, and everything that asks for you - what failed (`✗`), what works with limits, what needs you (the spec map's `!`): `error`, `warning` |
36
36
 
37
37
  The design system keeps pink for the one thing to press and orange for the `$` prompt alone. A terminal has no buttons, but it needs status colours, so pink carries what is done and orange everything that asks for you - what failed, what works with limits, what waits on you - the mark (`✗`, `!`, `■`) telling them apart. The palette has no red.
38
38
 
package/docs/using.md CHANGED
@@ -7,7 +7,7 @@ cd ~/my-project
7
7
  turri
8
8
  ```
9
9
 
10
- That is pi, with turri's tools, skills and themes loaded - `turri` alone, or with pi's options (`turri -c`, `turri --harness-model <m>`). It opens with turri's own banner - the jellyfish it is named after, swimming until the conversation fills the screen: a picture in a terminal that shows images (Kitty, Ghostty, WezTerm, Warp, iTerm2), and drawn in text in any other, tmux included - swimming a stroke a loop, its tentacles following its bell, frames `scripts/jellyfish-frames.mjs` writes - turri's version (and, run from a clone, the commit it is on, and whether the clone has changes), where commands will run, the rules and skills loaded - rather than pi's startup header and resource list; `turri --verbose` shows those too. pi's "Update Available" notice is left out - turri's pi is a pinned dependency `pi update` never touches - and the terminal's tab reads `🪼 turri - <folder>`, not pi's `π`. Talk to it: ask it to start a project, add a feature, render a module, fix a spec. It decides which skill or tool applies - but not *how* a skill runs: `cultivate` hands the work to the gated runner, which owns the steps, the gates and the judges (see [skills and gates](skills-and-gates.md)).
10
+ That is pi, with turri's tools, skills and themes loaded - `turri` alone, or with pi's options (`turri -c`, `turri --harness-model <m>`). It opens with turri's own banner - the jellyfish it is named after, swimming until the conversation fills the screen: a pixel jellyfish - a pink bell, an orange gut, neon tentacles coming apart into code at their tips - swimming a stroke a loop, its bell shutting and rising, its tentacles following: a picture in a terminal that shows images (Kitty, Ghostty, WezTerm, Warp, iTerm2), and drawn smaller in half blocks in any other, tmux included (`src/jellyfish.ts` draws it, `scripts/jellyfish-frames.mjs` writes the pictures) - turri's version (and, run from a clone, the commit it is on, and whether the clone has changes) and where its settings are changed - and only when something is wrong, skills that failed to load or agents' commands less fenced than they should be - rather than pi's startup header and resource list; `turri --verbose` shows those too. pi's "Update Available" notice is left out - turri's pi is a pinned dependency `pi update` never touches - and the terminal's tab reads `🪼 turri - <folder>`, not pi's `π`. Talk to it: ask it to start a project, add a feature, render a module, fix a spec. It decides which skill or tool applies - but not *how* a skill runs: `cultivate` hands the work to the gated runner, which owns the steps, the gates and the judges (see [skills and gates](skills-and-gates.md)).
11
11
 
12
12
  The skills that build from scratch:
13
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turri-dev/cli",
3
- "version": "0.1.4-next.4342332",
3
+ "version": "0.1.4-next.5a8c6e5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
Binary file
Binary file
Binary file
@@ -1,252 +0,0 @@
1
-
2
- ▄▄▆▆██▆▆▄▄
3
- ▄▆█▓▒░░░░░░▒▓█▆▄
4
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
5
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
6
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
7
- ) ) ) ║║ ╱ ) )
8
- ( ╱ ╱ ╱╱ ( ( )
9
- ╱ ╱ ) )) ) ) )
10
- ╱ ( ( ) ⁏ ) ) (
11
- ) ⁏ ) ⁏ @ ) ⁏ )
12
- ⁏ $ ⁏ ; ⁏ * ⁏
13
- # % & .
14
- ---
15
-
16
- ▄▆▆██▆▆▄
17
- ▄▆█▓▒░░░░▒▓█▆▄
18
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
19
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
20
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
21
- ) ) ) ║║ ) ) )
22
- ( ( ( (╱ ( ( (
23
- ╱ ╱ ╱ ╱ ) ) )
24
- ╱ ( ) ) ⁏ ) ) )
25
- ) ⁏ ) ⁏ @ ) ⁏ ╱
26
- ⁏ $ ⁏ ; ⁏ * ⁏
27
- # % & .
28
- ---
29
- ▄▆██▆▄
30
- ▄▆█▓▒░░▒▓█▆▄
31
- ▄█▓▒░░▒▓▓▒░░▒▓█▄
32
- ▐█▒░░▒▓████▓▒░░▒█▌
33
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
34
- ) ) ) ║║ ) ) )
35
- ( ( ( (( ( ( )
36
- ) ) ) )) ) ) )
37
- ╱ ╱ ( (⁏ ) ) )
38
- ) ⁏ ) ⁏ @ ) ⁏ ╱
39
- ⁏ $ ⁏ ; ⁏ *⁏
40
- # % & .
41
-
42
- ---
43
- ▄▆██▆▄
44
- ▄▆█▓▒░░▒▓█▆▄
45
- ▄█▓▒░░▒▓▓▒░░▒▓█▄
46
- ▐█▒░░▒▓████▓▒░░▒█▌
47
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
48
- ) ) ) ║║ ) ) )
49
- ( ( ) (( ( ) (
50
- ) ) ))) ) ) )
51
- ╱ ╱ ( (⁏ ( ( )
52
- ) ⁏ ) ⁏@ ) ⁏ )
53
- ⁏ $ ⁏ ; ⁏ * ⁏
54
- # % & .
55
-
56
- ---
57
- ▄▆██▆▄
58
- ▄▆█▓▒░░▒▓█▆▄
59
- ▄█▓▒░░▒▓▓▒░░▒▓█▄
60
- ▐█▒░░▒▓████▓▒░░▒█▌
61
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
62
- ) ) ) ║║ ) ) )
63
- ( ) ) )) ) ) (
64
- ) ) ) )) ) ) )
65
- ( ( ( (⁏ ( ( )
66
- ) ⁏ ) ⁏@ ) ⁏ )
67
- ⁏ $ ⁏ ; ⁏ * ⁏
68
- # % & .
69
-
70
- ---
71
- ▄▆██▆▄
72
- ▄▆█▓▒░░▒▓█▆▄
73
- ▄█▓▒░░▒▓▓▒░░▒▓█▄
74
- ▐█▒░░▒▓████▓▒░░▒█▌
75
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
76
- ) ) ) ║║ ) ) )
77
- ( ( ) )) ( ) )
78
- ) ) ) ))) ) )
79
- ( ( ( (⁏ ( ( )
80
- ) ⁏ ) ⁏@ ) ⁏ )
81
- ⁏ $ ⁏ ; ⁏ * ⁏
82
- # % & .
83
-
84
- ---
85
- ▄▆██▆▄
86
- ▄▆█▓▒░░▒▓█▆▄
87
- ▄█▓▒░░▒▓▓▒░░▒▓█▄
88
- ▐█▒░░▒▓████▓▒░░▒█▌
89
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
90
- ) ) ) ║║ ) ) )
91
- ( ( ((( ( ) )
92
- ) ) ))) ) ) )
93
- ) ( ( (⁏ ( ( )
94
- ) ⁏ ) ⁏@ ) ⁏ )
95
- ⁏ $ ⁏ ; ⁏ * ⁏
96
- # % & .
97
-
98
- ---
99
-
100
- ▄▆▆██▆▆▄
101
- ▄▆█▓▒░░░░▒▓█▆▄
102
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
103
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
104
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
105
- ) ) ) ║║ ) ) )
106
- ( ( ( (( ( ( )
107
- ) ) ) )) ) ) )
108
- ( ( ) ) ⁏ ( ( (
109
- ) ⁏ ) ⁏@ ) ⁏ )
110
- ⁏ $ ⁏ ; ⁏ * ⁏
111
- # % & .
112
- ---
113
-
114
- ▄▆▆██▆▆▄
115
- ▄▆█▓▒░░░░▒▓█▆▄
116
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
117
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
118
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
119
- ) ) ) ║║ ) ) )
120
- ( ( ( (( ( ) )
121
- ╱ ) ) )) ) ) )
122
- ) ( ) )⁏ ( ( )
123
- ) ⁏ ) ⁏@ ) ⁏ )
124
- ⁏ $ ⁏ ; ⁏ * ⁏
125
- # % & .
126
- ---
127
-
128
- ▄▆▆██▆▆▄
129
- ▄▆█▓▒░░░░▒▓█▆▄
130
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
131
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
132
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
133
- ) ) ) ║║ ) ) )
134
- ( ( ( (( ( ) (
135
- ╱ ) ) )) ) ) )
136
- ( ( ( )⁏ ( ) )
137
- ) ⁏ ) ⁏@ ) ⁏ )
138
- ⁏ $ ⁏ ; ⁏ * ⁏
139
- # % & .
140
- ---
141
-
142
- ▄▆▆██▆▆▄
143
- ▄▆█▓▒░░░░▒▓█▆▄
144
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
145
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
146
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
147
- ) ) ) ║ ) ) )
148
- ( ( ( (( ( ) (
149
- ╱ ╱ ) )) ) ) )
150
- ( ) ( )⁏ ( ) )
151
- ) ⁏ ) ⁏@ ) ⁏ )
152
- ⁏ $ ⁏ ; ⁏ * ⁏
153
- # % & .
154
- ---
155
-
156
- ▄▄▆▆██▆▆▄▄
157
- ▄▆█▓▒░░░░░░▒▓█▆▄
158
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
159
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
160
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
161
- ) ) ) ║ ╱ ) )
162
- ( ╱ ( (( ( ( )
163
- ╱ ) ╱ )) ) ) )
164
- ( ( ) (⁏ ) ) (
165
- ) ⁏ ) ⁏ @ ) ⁏ )
166
- ⁏ $ ⁏ ; ⁏ * ⁏
167
- # % & .
168
- ---
169
-
170
- ▄▄▆▆██▆▆▄▄
171
- ▄▆█▓▒░░░░░░▒▓█▆▄
172
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
173
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
174
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
175
- ) ) ) ║║ ╱ ) )
176
- ( ╱ ( (( ( ( )
177
- ╱ ) ╱ )) ) ) )
178
- ( ( ) (⁏ ) ) (
179
- ) ⁏ ) ⁏ @ ) ⁏ )
180
- ⁏ $ ⁏ ; ⁏ * ⁏
181
- # % & .
182
- ---
183
-
184
- ▄▄▆▆██▆▆▄▄
185
- ▄▆█▓▒░░░░░░▒▓█▆▄
186
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
187
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
188
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
189
- ) ) ) ║║ ╱ ) )
190
- ( ╱ ( (( ( ( )
191
- ╱ ) ╱ )) ) ) )
192
- ( ( ) (⁏ ) ) (
193
- ) ⁏ ) ⁏ @ ) ⁏ )
194
- ⁏ $ ⁏ ; ⁏ * ⁏
195
- # % & .
196
- ---
197
-
198
- ▄▄▆▆██▆▆▄▄
199
- ▄▆█▓▒░░░░░░▒▓█▆▄
200
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
201
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
202
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
203
- ) ) ) ║║ ╱ ╱ )
204
- ( ╱ ╱ ╱╱ ( ) )
205
- ╱ ╱ ) )) ) ) )
206
- ( ( ( ) ⁏ ) ) (
207
- ) ⁏ ) ⁏ @ ) ⁏ )
208
- ⁏ $ ⁏ ; ⁏ * ⁏
209
- # % & .
210
- ---
211
-
212
- ▄▄▆▆██▆▆▄▄
213
- ▄▆█▓▒░░░░░░▒▓█▆▄
214
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
215
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
216
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
217
- ) ) ╱ ║║ ╱ ╱ )
218
- ( ╱ ╱ ╱╱ ( ) )
219
- ╱ ╱ ) )) ) ) )
220
- ( ( ( ) ⁏ ) ) (
221
- ) ⁏ ) ⁏ @ ) ⁏ )
222
- ⁏ $ ⁏ ; ⁏ * ⁏
223
- # % & .
224
- ---
225
-
226
- ▄▄▆▆██▆▆▄▄
227
- ▄▆█▓▒░░░░░░▒▓█▆▄
228
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
229
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
230
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
231
- ) ) ╱ ║║ ╱ ╱ )
232
- ( ╱ ╱ ╱╱ ( ) )
233
- ╱ ╱ ) )) ) ) )
234
- ( ( ( ) ⁏ ) ) (
235
- ) ⁏ ) ⁏ @ ) ⁏ )
236
- ⁏ $ ⁏ ; ⁏ * ⁏
237
- # % & .
238
- ---
239
-
240
- ▄▄▆▆██▆▆▄▄
241
- ▄▆█▓▒░░░░░░▒▓█▆▄
242
- ▄█▓▒░ ░▒▒▒▒░ ░▒▓█▄
243
- ▐█▒░ ░▒▓████▓▒░ ░▒█▌
244
- ▝█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█▘
245
- ) ) ) ║║ ╱ ╱ )
246
- ( ╱ ╱ ╱╱ ( ) )
247
- ╱ ╱ ) )) ) ) )
248
- ( ( ( ) ⁏ ) ) (
249
- ) ⁏ ) ⁏ @ ) ⁏ )
250
- ⁏ $ ⁏ ; ⁏ * ⁏
251
- # % & .
252
- ---
Binary file
Binary file
Binary file