@turri-dev/cli 0.1.4-next.67734c0 → 0.1.4-next.8254fbc

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 (45) hide show
  1. package/.pi/themes/turri-dark.json +67 -66
  2. package/.pi/themes/turri-light.json +71 -73
  3. package/README.md +1 -1
  4. package/assets/jellyfish/dark-00.png +0 -0
  5. package/assets/jellyfish/dark-01.png +0 -0
  6. package/assets/jellyfish/dark-02.png +0 -0
  7. package/assets/jellyfish/dark-03.png +0 -0
  8. package/assets/jellyfish/dark-04.png +0 -0
  9. package/assets/jellyfish/dark-05.png +0 -0
  10. package/assets/jellyfish/dark-06.png +0 -0
  11. package/assets/jellyfish/dark-07.png +0 -0
  12. package/assets/jellyfish/dark-08.png +0 -0
  13. package/assets/jellyfish/dark-09.png +0 -0
  14. package/assets/jellyfish/dark-10.png +0 -0
  15. package/assets/jellyfish/dark-11.png +0 -0
  16. package/assets/jellyfish/dark-12.png +0 -0
  17. package/assets/jellyfish/dark-13.png +0 -0
  18. package/assets/jellyfish/dark-14.png +0 -0
  19. package/assets/jellyfish/light-00.png +0 -0
  20. package/assets/jellyfish/light-01.png +0 -0
  21. package/assets/jellyfish/light-02.png +0 -0
  22. package/assets/jellyfish/light-03.png +0 -0
  23. package/assets/jellyfish/light-04.png +0 -0
  24. package/assets/jellyfish/light-05.png +0 -0
  25. package/assets/jellyfish/light-06.png +0 -0
  26. package/assets/jellyfish/light-07.png +0 -0
  27. package/assets/jellyfish/light-08.png +0 -0
  28. package/assets/jellyfish/light-09.png +0 -0
  29. package/assets/jellyfish/light-10.png +0 -0
  30. package/assets/jellyfish/light-11.png +0 -0
  31. package/assets/jellyfish/light-12.png +0 -0
  32. package/assets/jellyfish/light-13.png +0 -0
  33. package/assets/jellyfish/light-14.png +0 -0
  34. package/dist/brand.js +7 -7
  35. package/dist/cultivate-cli.js +16 -23
  36. package/dist/extension.js +4 -4
  37. package/dist/loop-cli.js +8 -8
  38. package/dist/module-report.js +5 -4
  39. package/dist/regenerate/plain-palette.js +51 -36
  40. package/dist/regenerate/spec-map-view.js +9 -5
  41. package/dist/release-cli.js +3 -8
  42. package/dist/validate-cli.js +3 -8
  43. package/docs/images/how-turri-works.svg +29 -31
  44. package/docs/images/turri-banner.png +0 -0
  45. package/package.json +1 -1
@@ -2,79 +2,80 @@
2
2
  "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
3
  "name": "turri-dark",
4
4
  "vars": {
5
- "yellow": "#e0ff6e",
6
- "yellowMuted": "#b9cb69",
7
- "green": "#79fc96",
8
- "greenLight": "#c5dcd9",
9
- "greenDark": "#223936",
10
- "black": "#0f0f0f",
11
- "white": "#ffffff",
12
- "gray": "#808080",
13
- "grayLight": "#666666",
14
- "statusError": "#dd5353",
15
- "statusWarning": "#dc6803"
5
+ "abyss": "#0a0f1a",
6
+ "blueWhale": "#1e3442",
7
+ "lines": "#22344a",
8
+ "path": "#3e5a6b",
9
+ "grauzone": "#85a3b2",
10
+ "white": "#f4f7f9",
11
+ "siestaTan": "#e9d8c8",
12
+ "pink": "#ff5c8d",
13
+ "pinkHover": "#ff7fa6",
14
+ "neon": "#5ce1e6",
15
+ "neonHover": "#8eebee",
16
+ "orange": "#ff7a3d"
16
17
  },
17
18
  "colors": {
18
- "accent": "yellow",
19
- "border": "green",
20
- "borderAccent": "green",
21
- "borderMuted": "greenDark",
22
- "success": "green",
23
- "error": "statusError",
24
- "warning": "statusWarning",
25
- "muted": "gray",
26
- "dim": "grayLight",
27
- "text": "greenLight",
28
- "thinkingText": "gray",
29
- "selectedBg": "greenDark",
30
- "scrollbarTrack": "greenDark",
31
- "scrollbarThumb": "green",
32
- "searchMatchBg": "yellow",
33
- "searchMatchText": "black",
19
+ "accent": "neon",
20
+ "border": "path",
21
+ "borderAccent": "neon",
22
+ "borderMuted": "lines",
23
+ "success": "pink",
24
+ "error": "orange",
25
+ "warning": "orange",
26
+ "muted": "grauzone",
27
+ "dim": "path",
28
+ "text": "grauzone",
29
+ "thinkingText": "grauzone",
30
+ "selectedBg": "blueWhale",
31
+ "scrollbarTrack": "lines",
32
+ "scrollbarThumb": "path",
33
+ "searchMatchBg": "neon",
34
+ "searchMatchText": "abyss",
34
35
  "userMessageBg": "",
35
- "userMessageText": "yellow",
36
+ "userMessageText": "white",
36
37
  "customMessageBg": "",
37
- "customMessageText": "greenLight",
38
- "customMessageLabel": "yellow",
38
+ "customMessageText": "grauzone",
39
+ "customMessageLabel": "neon",
39
40
  "toolPendingBg": "",
40
41
  "toolSuccessBg": "",
41
42
  "toolErrorBg": "",
42
- "toolTitle": "yellow",
43
- "toolOutput": "gray",
44
- "mdHeading": "yellow",
45
- "mdLink": "green",
46
- "mdLinkUrl": "grayLight",
47
- "mdCode": "yellowMuted",
48
- "mdCodeBlock": "greenLight",
49
- "mdCodeBlockBorder": "greenDark",
50
- "mdQuote": "gray",
51
- "mdQuoteBorder": "greenDark",
52
- "mdHr": "grayLight",
53
- "mdListBullet": "green",
54
- "toolDiffAdded": "green",
55
- "toolDiffRemoved": "statusError",
56
- "toolDiffContext": "gray",
57
- "syntaxComment": "grayLight",
58
- "syntaxKeyword": "yellow",
59
- "syntaxFunction": "green",
60
- "syntaxVariable": "greenLight",
61
- "syntaxString": "yellowMuted",
62
- "syntaxNumber": "statusWarning",
63
- "syntaxType": "white",
64
- "syntaxOperator": "gray",
65
- "syntaxPunctuation": "grayLight",
66
- "thinkingOff": "grayLight",
67
- "thinkingMinimal": "gray",
68
- "thinkingLow": "greenLight",
69
- "thinkingMedium": "green",
70
- "thinkingHigh": "yellowMuted",
71
- "thinkingXhigh": "yellow",
72
- "thinkingMax": "statusWarning",
73
- "bashMode": "yellow"
43
+ "toolTitle": "white",
44
+ "toolOutput": "grauzone",
45
+ "mdHeading": "white",
46
+ "mdLink": "neon",
47
+ "mdLinkUrl": "path",
48
+ "mdCode": "siestaTan",
49
+ "mdCodeBlock": "white",
50
+ "mdCodeBlockBorder": "lines",
51
+ "mdQuote": "grauzone",
52
+ "mdQuoteBorder": "path",
53
+ "mdHr": "lines",
54
+ "mdListBullet": "neon",
55
+ "toolDiffAdded": "neon",
56
+ "toolDiffRemoved": "pink",
57
+ "toolDiffContext": "grauzone",
58
+ "syntaxComment": "path",
59
+ "syntaxKeyword": "pinkHover",
60
+ "syntaxFunction": "neon",
61
+ "syntaxVariable": "white",
62
+ "syntaxString": "siestaTan",
63
+ "syntaxNumber": "pinkHover",
64
+ "syntaxType": "neonHover",
65
+ "syntaxOperator": "grauzone",
66
+ "syntaxPunctuation": "path",
67
+ "thinkingOff": "path",
68
+ "thinkingMinimal": "grauzone",
69
+ "thinkingLow": "siestaTan",
70
+ "thinkingMedium": "neonHover",
71
+ "thinkingHigh": "neon",
72
+ "thinkingXhigh": "pinkHover",
73
+ "thinkingMax": "pink",
74
+ "bashMode": "orange"
74
75
  },
75
76
  "export": {
76
- "pageBg": "#0f0f0f",
77
- "cardBg": "#121212",
78
- "infoBg": "#223936"
77
+ "pageBg": "#0a0f1a",
78
+ "cardBg": "#0d1420",
79
+ "infoBg": "#142030"
79
80
  }
80
- }
81
+ }
@@ -2,81 +2,79 @@
2
2
  "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
3
3
  "name": "turri-light",
4
4
  "vars": {
5
- "blue": "#0a1fd4",
6
- "greenLight": "#c5dcd9",
7
- "greenDark": "#223936",
8
- "diffGreen": "#1a7f37",
9
- "black": "#0f0f0f",
10
- "gray": "#808080",
11
- "grayLight": "#666666",
12
- "red": "#d00505",
13
- "statusErrorLight": "#fee4e2",
14
- "statusWarningLight": "#fef0c7",
15
- "statusWarning": "#dc6803",
16
- "paleGreen": "#eef7f5",
17
- "paleGray": "#f4f4f2"
5
+ "abyss": "#0a0f1a",
6
+ "blueWhale": "#1e3442",
7
+ "lines": "#22344a",
8
+ "path": "#3e5a6b",
9
+ "grauzone": "#85a3b2",
10
+ "white": "#f4f7f9",
11
+ "siestaTan": "#e9d8c8",
12
+ "eggplant": "#732553",
13
+ "pink": "#ff5c8d",
14
+ "neon": "#5ce1e6",
15
+ "orange": "#ff7a3d"
18
16
  },
19
17
  "colors": {
20
- "accent": "blue",
21
- "border": "gray",
22
- "borderAccent": "blue",
23
- "borderMuted": "greenLight",
24
- "success": "greenDark",
25
- "error": "red",
26
- "warning": "statusWarning",
27
- "muted": "grayLight",
28
- "dim": "gray",
29
- "text": "black",
30
- "thinkingText": "grayLight",
31
- "selectedBg": "greenLight",
32
- "scrollbarTrack": "greenLight",
33
- "scrollbarThumb": "grayLight",
34
- "searchMatchBg": "statusWarningLight",
35
- "searchMatchText": "black",
36
- "userMessageBg": "paleGray",
37
- "userMessageText": "black",
38
- "customMessageBg": "paleGreen",
39
- "customMessageText": "black",
40
- "customMessageLabel": "blue",
41
- "toolPendingBg": "paleGray",
42
- "toolSuccessBg": "paleGreen",
43
- "toolErrorBg": "statusErrorLight",
44
- "toolTitle": "blue",
45
- "toolOutput": "grayLight",
46
- "mdHeading": "blue",
47
- "mdLink": "blue",
48
- "mdLinkUrl": "grayLight",
49
- "mdCode": "greenDark",
50
- "mdCodeBlock": "black",
51
- "mdCodeBlockBorder": "greenLight",
52
- "mdQuote": "grayLight",
53
- "mdQuoteBorder": "greenLight",
54
- "mdHr": "gray",
55
- "mdListBullet": "blue",
56
- "toolDiffAdded": "diffGreen",
57
- "toolDiffRemoved": "red",
58
- "toolDiffContext": "grayLight",
59
- "syntaxComment": "grayLight",
60
- "syntaxKeyword": "blue",
61
- "syntaxFunction": "greenDark",
62
- "syntaxVariable": "black",
63
- "syntaxString": "greenDark",
64
- "syntaxNumber": "statusWarning",
65
- "syntaxType": "blue",
66
- "syntaxOperator": "gray",
67
- "syntaxPunctuation": "grayLight",
68
- "thinkingOff": "gray",
69
- "thinkingMinimal": "grayLight",
70
- "thinkingLow": "greenDark",
71
- "thinkingMedium": "blue",
72
- "thinkingHigh": "blue",
73
- "thinkingXhigh": "statusWarning",
74
- "thinkingMax": "red",
75
- "bashMode": "blue"
18
+ "accent": "eggplant",
19
+ "border": "path",
20
+ "borderAccent": "eggplant",
21
+ "borderMuted": "grauzone",
22
+ "success": "eggplant",
23
+ "error": "orange",
24
+ "warning": "orange",
25
+ "muted": "path",
26
+ "dim": "grauzone",
27
+ "text": "abyss",
28
+ "thinkingText": "path",
29
+ "selectedBg": "siestaTan",
30
+ "scrollbarTrack": "siestaTan",
31
+ "scrollbarThumb": "path",
32
+ "searchMatchBg": "neon",
33
+ "searchMatchText": "abyss",
34
+ "userMessageBg": "white",
35
+ "userMessageText": "abyss",
36
+ "customMessageBg": "",
37
+ "customMessageText": "abyss",
38
+ "customMessageLabel": "eggplant",
39
+ "toolPendingBg": "",
40
+ "toolSuccessBg": "",
41
+ "toolErrorBg": "",
42
+ "toolTitle": "abyss",
43
+ "toolOutput": "path",
44
+ "mdHeading": "eggplant",
45
+ "mdLink": "blueWhale",
46
+ "mdLinkUrl": "path",
47
+ "mdCode": "eggplant",
48
+ "mdCodeBlock": "abyss",
49
+ "mdCodeBlockBorder": "grauzone",
50
+ "mdQuote": "path",
51
+ "mdQuoteBorder": "grauzone",
52
+ "mdHr": "grauzone",
53
+ "mdListBullet": "eggplant",
54
+ "toolDiffAdded": "blueWhale",
55
+ "toolDiffRemoved": "eggplant",
56
+ "toolDiffContext": "path",
57
+ "syntaxComment": "path",
58
+ "syntaxKeyword": "eggplant",
59
+ "syntaxFunction": "blueWhale",
60
+ "syntaxVariable": "abyss",
61
+ "syntaxString": "lines",
62
+ "syntaxNumber": "eggplant",
63
+ "syntaxType": "blueWhale",
64
+ "syntaxOperator": "path",
65
+ "syntaxPunctuation": "path",
66
+ "thinkingOff": "grauzone",
67
+ "thinkingMinimal": "path",
68
+ "thinkingLow": "lines",
69
+ "thinkingMedium": "blueWhale",
70
+ "thinkingHigh": "eggplant",
71
+ "thinkingXhigh": "pink",
72
+ "thinkingMax": "orange",
73
+ "bashMode": "orange"
76
74
  },
77
75
  "export": {
78
- "pageBg": "#ffffff",
79
- "cardBg": "#f4f4f2",
80
- "infoBg": "#eef7f5"
76
+ "pageBg": "#f4f7f9",
77
+ "cardBg": "#f4f7f9",
78
+ "infoBg": "#e9d8c8"
81
79
  }
82
- }
80
+ }
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
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/brand.js CHANGED
@@ -2,8 +2,8 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  /**
5
- * turri's colours for what its command line prints - the ones its themes use (.pi/themes): on a dark terminal its lime
6
- * accent and green, on a light one its blue. Nothing is coloured when the output is not a terminal, or NO_COLOR is set;
5
+ * turri's colours for what its command line prints - the design system's, as its themes use them (.pi/themes): on a
6
+ * dark terminal neon and pink, on a light one blue whale and eggplant. Nothing is coloured when the output is not a terminal, or NO_COLOR is set;
7
7
  * a terminal without 24-bit colour gets the nearest of its sixteen.
8
8
  */
9
9
  /** Whether the terminal is light: COLORFGBG says so (a light background, 7 or 15), or pi's theme is a light one. */
@@ -35,15 +35,15 @@ function paint(stream = process.stdout) {
35
35
  };
36
36
  return {
37
37
  /** turri's accent: what to look at - a heading, a choice's number, the command to run. */
38
- accent: light ? ink("#0a1fd4", 34) : ink("#e0ff6e", 93),
38
+ accent: light ? ink("#1e3442", 34) : ink("#5ce1e6", 96),
39
39
  /** Done, and what was written. */
40
- success: light ? ink("#223936", 32) : ink("#79fc96", 92),
40
+ success: light ? ink("#732553", 35) : ink("#ff5c8d", 95),
41
41
  /** What is wrong: what stops turri. */
42
- error: light ? ink("#d00505", 31) : ink("#dd5353", 91),
42
+ error: ink("#ff7a3d", 33),
43
43
  /** What works, with limits. */
44
- warning: ink("#dc6803", 33),
44
+ warning: ink("#ff7a3d", 33),
45
45
  /** What goes with it, and can be skimmed. */
46
- muted: ink("#808080", 90),
46
+ muted: light ? ink("#3e5a6b", 90) : ink("#85a3b2", 37),
47
47
  bold: (text) => (on ? `\x1b[1m${text}\x1b[22m` : text),
48
48
  };
49
49
  }
@@ -7,40 +7,33 @@ import { skillRunDir } from "./regenerate/paths.js";
7
7
  import { loadSkill } from "./skill.js";
8
8
  import { discoverSkills } from "./skills.js";
9
9
  import { PACKAGE_ROOT } from "./environment.js";
10
- const c = {
11
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
12
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
13
- green: (s) => `\x1b[32m${s}\x1b[0m`,
14
- red: (s) => `\x1b[31m${s}\x1b[0m`,
15
- yellow: (s) => `\x1b[33m${s}\x1b[0m`,
16
- cyan: (s) => `\x1b[36m${s}\x1b[0m`,
17
- };
10
+ import { c } from "./module-report.js";
18
11
  const reporter = {
19
- step: (name, attempt) => console.log(`\n${c.bold(c.cyan(`> step ${name}`))} ${c.dim(`attempt ${attempt}`)}`),
12
+ step: (name, attempt) => console.log(`\n${c.bold(c.accent(`> step ${name}`))} ${c.dim(`attempt ${attempt}`)}`),
20
13
  artifact: (path, body) => {
21
14
  console.log(c.dim(` ${path}:`));
22
15
  for (const line of body.split("\n"))
23
16
  console.log(c.dim(` | ${line}`));
24
17
  },
25
18
  gate: (name, judges) => console.log(judges === 0
26
- ? `${c.bold(c.yellow(`> gate ${name}`))} ${c.dim("no artifact - failing without spending judges")}`
27
- : `${c.bold(c.yellow(`> gate ${name}`))} ${c.dim(`${judges} independent judges`)}`),
19
+ ? `${c.bold(c.warning(`> gate ${name}`))} ${c.dim("no artifact - failing without spending judges")}`
20
+ : `${c.bold(c.warning(`> gate ${name}`))} ${c.dim(`${judges} independent judges`)}`),
28
21
  livePass: (step, scope, findings) => console.log(findings === 0
29
22
  ? c.dim(` review ${step}: ${scope} - clean`)
30
- : ` ${c.yellow(`review ${step}`)}: ${scope} ${c.dim(`- ${findings} to fix`)}`),
23
+ : ` ${c.warning(`review ${step}`)}: ${scope} ${c.dim(`- ${findings} to fix`)}`),
31
24
  liveReview: (name, passes, findings) => console.log(findings === 0
32
- ? `${c.bold(c.yellow(`> gate ${name}`))} ${c.dim(`reviewer read along in ${passes} passes, nothing outstanding`)}`
33
- : `${c.bold(c.yellow(`> gate ${name}`))} ${c.dim(`${findings} unresolved from the reviewer - failing without spending judges`)}`),
25
+ ? `${c.bold(c.warning(`> gate ${name}`))} ${c.dim(`reviewer read along in ${passes} passes, nothing outstanding`)}`
26
+ : `${c.bold(c.warning(`> gate ${name}`))} ${c.dim(`${findings} unresolved from the reviewer - failing without spending judges`)}`),
34
27
  judge: (index, model, verdict, violations) => {
35
- const mark = verdict === "pass" ? c.green("pass") : c.red(verdict);
28
+ const mark = verdict === "pass" ? c.success("pass") : c.error(verdict);
36
29
  console.log(` judge ${index + 1} ${c.dim(`(${model})`)}: ${mark}${violations ? c.dim(` (${violations})`) : ""}`);
37
30
  },
38
31
  outcome: (name, decision, critique) => {
39
32
  const colour = {
40
- advance: c.green,
41
- retry: c.yellow,
42
- escalate: c.yellow,
43
- abort: c.red,
33
+ advance: c.success,
34
+ retry: c.warning,
35
+ escalate: c.warning,
36
+ abort: c.error,
44
37
  };
45
38
  console.log(` ${c.bold(colour[decision](decision))} ${c.dim(`<- gate ${name}`)}`);
46
39
  if (critique)
@@ -78,7 +71,7 @@ async function main() {
78
71
  select: async (title, options, context) => {
79
72
  const rl = createInterface({ input: process.stdin, output: process.stdout });
80
73
  const list = options.map((option, i) => ` ${c.bold(String(i + 1))}. ${option}`).join("\n");
81
- const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.cyan("? "))}${title}\n${list}\n> `)).trim();
74
+ const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.accent("? "))}${title}\n${list}\n> `)).trim();
82
75
  rl.close();
83
76
  const index = Number.parseInt(reply, 10);
84
77
  if (Number.isInteger(index) && index >= 1 && index <= options.length)
@@ -87,13 +80,13 @@ async function main() {
87
80
  },
88
81
  input: async (title, context) => {
89
82
  const rl = createInterface({ input: process.stdin, output: process.stdout });
90
- const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.cyan("? "))}${title}\n> `)).trim();
83
+ const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.accent("? "))}${title}\n> `)).trim();
91
84
  rl.close();
92
85
  return reply === "" ? undefined : reply;
93
86
  },
94
87
  }),
95
88
  escalate: async ({ question, critique }) => {
96
- console.log(`\n${c.bold(c.yellow("! human review required"))}`);
89
+ console.log(`\n${c.bold(c.warning("! human review required"))}`);
97
90
  console.log(c.dim(critique));
98
91
  const rl = createInterface({ input: process.stdin, output: process.stdout });
99
92
  const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();
@@ -101,7 +94,7 @@ async function main() {
101
94
  return answer === "y";
102
95
  },
103
96
  });
104
- console.log(`\n${result.ok ? c.green(c.bold("run complete")) : c.red(c.bold("run aborted at a gate"))} ${c.dim(`artifacts in ${relative(cwd, skillRunDir(cwd, skill.name))}`)}`);
97
+ console.log(`\n${result.ok ? c.success(c.bold("run complete")) : c.error(c.bold("run aborted at a gate"))} ${c.dim(`artifacts in ${relative(cwd, skillRunDir(cwd, skill.name))}`)}`);
105
98
  process.exit(result.ok ? 0 : 1);
106
99
  }
107
100
  main().catch((err) => {
package/dist/extension.js CHANGED
@@ -29,7 +29,7 @@ import { describeChoices, describeResolution, modelFor, resolveModel } from "./r
29
29
  import { checkPlainFile, checkPlainTargets, formatCheckResults } from "./plain/check.js";
30
30
  import { whyFailed } from "./regenerate/diagnose-job.js";
31
31
  import { createWorkLoop, describeModules, isUpToDate, previewJobs } from "./regenerate/loop.js";
32
- import { brandBlue, plainPaint } from "./regenerate/plain-palette.js";
32
+ import { brandNeon, brandPink, plainPaint } from "./regenerate/plain-palette.js";
33
33
  import { createJobPanel, didNotPass, keyPressed, PANEL_KEY, spinner } from "./regenerate/job-panel.js";
34
34
  import { clickToFocus, dockedWidth, narrowChat, shade, shadeChat } from "./regenerate/panel-dock.js";
35
35
  import { overPictureRow, PICTURE_AT } from "./picture-row.js";
@@ -1646,8 +1646,8 @@ function registerJobTools(pi, jobs, cwd) {
1646
1646
  removed: (text) => theme.fg("toolDiffRemoved", text),
1647
1647
  // A spec diff's ***plain highlighted as plyn highlights it, and its added and removed lines filled.
1648
1648
  diff: plainPaint(theme),
1649
- // What conformance tests show does what its spec says: brand blue.
1650
- conforms: brandBlue(theme),
1649
+ // What conformance tests show does what its spec says: neon.
1650
+ conforms: brandNeon(theme),
1651
1651
  },
1652
1652
  back,
1653
1653
  close,
@@ -2308,7 +2308,7 @@ function registerBranding(pi, ruleCount, skills) {
2308
2308
  if (data === undefined)
2309
2309
  return undefined;
2310
2310
  const lines = [
2311
- `${theme.fg("accent", "turri")} ${theme.fg("muted", `v${data.version}`)}` +
2311
+ `${brandPink(theme)("turri")} ${theme.fg("muted", `v${data.version}`)}` +
2312
2312
  (data.commit !== undefined ? theme.fg("dim", ` from a clone, ${data.commit}${data.changed ? " with changes" : ""}`) : ""),
2313
2313
  theme.fg("text", "regenerative software - the code is regenerated from the spec"),
2314
2314
  theme.fg("muted", `${data.rules} rules · ${data.skills} skill${data.skills === 1 ? "" : "s"}` +
package/dist/loop-cli.js CHANGED
@@ -80,17 +80,17 @@ async function runLoopReporting(options) {
80
80
  return;
81
81
  seen.set(job.id, line);
82
82
  if (job.status === "running")
83
- console.log(`${c.cyan(`▶ ${job.id}`)} ${pulseName(job)}${job.stage !== undefined ? c.dim(` - ${job.stage}`) : ""}`);
83
+ console.log(`${c.accent(`▶ ${job.id}`)} ${pulseName(job)}${job.stage !== undefined ? c.dim(` - ${job.stage}`) : ""}`);
84
84
  },
85
85
  onFinish: (job) => {
86
- const mark = job.status === "succeeded" ? c.green("✓") : job.status === "stopped" ? c.dim("■") : c.red("✗");
86
+ const mark = job.status === "succeeded" ? c.success("✓") : job.status === "stopped" ? c.dim("■") : c.error("✗");
87
87
  console.log(`${mark} ${job.id} ${pulseName(job)} - ${job.status}`);
88
88
  if (job.status !== "succeeded")
89
89
  console.log(c.dim((job.announce ?? job.report ?? job.error ?? "").trim()));
90
90
  // Its report is not printed when it succeeds, and nobody is asked here: the spec gaps its agents met are said.
91
91
  else
92
92
  for (const gap of job.details?.specGaps ?? [])
93
- console.log(c.yellow(` spec gap: ${gap.question}`), c.dim(`Meanwhile: ${gap.assumption}`));
93
+ console.log(c.warning(` spec gap: ${gap.question}`), c.dim(`Meanwhile: ${gap.assumption}`));
94
94
  report?.write();
95
95
  },
96
96
  });
@@ -98,7 +98,7 @@ async function runLoopReporting(options) {
98
98
  onModelEvent((event) => {
99
99
  const line = describeTurnIncident(event);
100
100
  if (line !== undefined)
101
- console.log(c.yellow(` ${event.job !== undefined ? `${event.job}: ` : ""}${line}`));
101
+ console.log(c.warning(` ${event.job !== undefined ? `${event.job}: ` : ""}${line}`));
102
102
  });
103
103
  const planner = modelSpec(projectModels(project).harness);
104
104
  const loop = createWorkLoop({
@@ -131,7 +131,7 @@ async function runLoopReporting(options) {
131
131
  // No terminal and no --start-over: no one to ask, so the run stops here rather than build nothing and say it is done.
132
132
  if (options.startOver !== true && process.stdin.isTTY !== true) {
133
133
  const message = `${why}\nThere is no terminal to ask whether to start over: run it again with --start-over to start over without asking.`;
134
- console.error(c.red(message));
134
+ console.error(c.error(message));
135
135
  report?.end("incomplete", message);
136
136
  process.exit(1);
137
137
  }
@@ -162,10 +162,10 @@ async function runLoopReporting(options) {
162
162
  // waiting for a start over, nothing the planner left out.
163
163
  const unfinished = moduleStates(loop.drift(plainFile)).filter((module) => module.state !== "complete");
164
164
  const verdict = failed.length > 0
165
- ? c.red(c.bold(`${failed.length} job${failed.length === 1 ? "" : "s"} failed`))
165
+ ? c.error(c.bold(`${failed.length} job${failed.length === 1 ? "" : "s"} failed`))
166
166
  : unfinished.length > 0
167
- ? c.red(c.bold(`not complete: ${unfinished.map((module) => `${module.module}${module.why !== undefined ? ` - ${module.why}` : ""}`).join("; ")}`))
168
- : c.green(c.bold("all jobs done"));
167
+ ? c.error(c.bold(`not complete: ${unfinished.map((module) => `${module.module}${module.why !== undefined ? ` - ${module.why}` : ""}`).join("; ")}`))
168
+ : c.success(c.bold("all jobs done"));
169
169
  console.log(`\n${verdict}`);
170
170
  for (const job of [...ended].reverse())
171
171
  console.log(` ${job.id} ${pulseName(job)}: ${job.status}`);
@@ -1,9 +1,10 @@
1
+ import { brand } from "./brand.js";
1
2
  /** Colours for what the command line prints. */
2
3
  export const c = {
3
4
  dim: (s) => `\x1b[2m${s}\x1b[0m`,
4
5
  bold: (s) => `\x1b[1m${s}\x1b[0m`,
5
- green: (s) => `\x1b[32m${s}\x1b[0m`,
6
- red: (s) => `\x1b[31m${s}\x1b[0m`,
7
- yellow: (s) => `\x1b[33m${s}\x1b[0m`,
8
- cyan: (s) => `\x1b[36m${s}\x1b[0m`,
6
+ success: brand.success,
7
+ error: brand.error,
8
+ warning: brand.warning,
9
+ accent: brand.accent,
9
10
  };
@@ -1,38 +1,46 @@
1
- /** On a dark surface - turri-dark's #0f0f0f, pi's dark ones. Every colour at least 7:1 on it, and 4.5:1 on the fills. */
1
+ /**
2
+ * On a dark surface - turri-dark's abyss, #0a0f1a, pi's dark ones - in the design system's colours alone: a section's
3
+ * header in pink, a concept in neon, text in quotes or backticks in siesta tan. Every colour at least 7:1 on abyss and 4.8:1 on the blue whale fill
4
+ * of an added line; on the eggplant of a removed one, the design system's only fill of that warmth, its pinks and
5
+ * grauzone drop to 3.7:1.
6
+ */
2
7
  const DARK = {
3
8
  roles: {
4
- section: { colour: "#fcd34d", bold: true },
5
- concept: { colour: "#7dd3fc" },
6
- link: { colour: "#c4b5fd", underline: true },
7
- literal: { colour: "#fdba74" },
8
- keyword: { colour: "#f9a8d4", bold: true },
9
- module: { colour: "#5eead4" },
10
- comment: { colour: "#b4bfcf", italic: true },
11
- bullet: { colour: "#b4bfcf" },
9
+ section: { colour: "#ff7fa6", bold: true },
10
+ concept: { colour: "#5ce1e6" },
11
+ link: { colour: "#f4f7f9", underline: true },
12
+ literal: { colour: "#e9d8c8" },
13
+ keyword: { colour: "#f4f7f9", bold: true },
14
+ module: { colour: "#f4f7f9" },
15
+ comment: { colour: "#85a3b2", italic: true },
16
+ bullet: { colour: "#85a3b2" },
12
17
  },
13
- added: "#86efac",
14
- removed: "#fca5a5",
15
- addedFill: "#1f4d2e",
16
- removedFill: "#5c2323",
17
- muted: "#808080",
18
+ added: "#5ce1e6",
19
+ removed: "#ff7fa6",
20
+ addedFill: "#1e3442",
21
+ removedFill: "#732553",
22
+ muted: "#85a3b2",
18
23
  };
19
- /** On a light surface - turri-light's white, pi's light ones. Every colour at least 7:1 on it, and 5:1 on the fills. */
24
+ /**
25
+ * On a light surface - turri-light's white, pi's light ones - in the design system's deep colours, the only ones it has
26
+ * that read on white. Every colour at least 7:1 on white, and 5:1 on the neon hover and siesta tan fills.
27
+ */
20
28
  const LIGHT = {
21
29
  roles: {
22
- section: { colour: "#0a1fd4", bold: true },
23
- concept: { colour: "#155e75" },
24
- link: { colour: "#6d28d9", underline: true },
25
- literal: { colour: "#92400e" },
26
- keyword: { colour: "#9d174d", bold: true },
27
- module: { colour: "#065f46" },
28
- comment: { colour: "#475569", italic: true },
29
- bullet: { colour: "#475569" },
30
+ section: { colour: "#3e5a6b", bold: true },
31
+ concept: { colour: "#732553" },
32
+ link: { colour: "#1e3442", underline: true },
33
+ literal: { colour: "#1e3442" },
34
+ keyword: { colour: "#0a0f1a", bold: true },
35
+ module: { colour: "#1e3442" },
36
+ comment: { colour: "#3e5a6b", italic: true },
37
+ bullet: { colour: "#3e5a6b" },
30
38
  },
31
- added: "#166534",
32
- removed: "#991b1b",
33
- addedFill: "#d3f5dc",
34
- removedFill: "#fcd9d9",
35
- muted: "#6b7280",
39
+ added: "#1e3442",
40
+ removed: "#732553",
41
+ addedFill: "#8eebee",
42
+ removedFill: "#e9d8c8",
43
+ muted: "#3e5a6b",
36
44
  };
37
45
  const rgb = (hex) => {
38
46
  const value = Number.parseInt(hex.slice(1), 16);
@@ -57,17 +65,24 @@ const fillWith = (hex, text, truecolor) => {
57
65
  return `${on}${text.replace(/\x1b\[(?:0|49)?m/g, (reset) => `${reset}${on}`)}\x1b[49m`;
58
66
  };
59
67
  /**
60
- * Brand blue, for what is shown to do what its spec says - regen's ◆. Brand blue itself on a light surface, 9.72:1
61
- * on white; on a dark one, where it is illegible (1.97:1 on brand black - see docs/theming.md), its tint 55% of the way
62
- * to white, 7.34:1 on brand black.
68
+ * Neon, for what is shown to do what its spec says - regen's ◆: the design system's colour for what can be followed,
69
+ * 12.2:1 on abyss. On a light surface, where neon is illegible (1.6:1 on white), blue whale, 12.9:1 on white.
63
70
  */
64
- export const BRAND_BLUE = { light: "#0a1fd4", dark: "#919aec" };
65
- /** Text in brand blue on a theme - its tint on a dark one - in as many colours as the terminal has. */
66
- export function brandBlue(theme) {
67
- const colour = theme.appearance === "light" ? BRAND_BLUE.light : BRAND_BLUE.dark;
71
+ export const BRAND_NEON = { light: "#1e3442", dark: "#5ce1e6" };
72
+ /**
73
+ * Pink, the one pink thing in a view - the banner's wordmark: 6.54:1 on abyss. On a light surface, where pink is
74
+ * too pale (2.9:1 on white), eggplant, 9.9:1 on white.
75
+ */
76
+ export const BRAND_PINK = { light: "#732553", dark: "#ff5c8d" };
77
+ const brandInk = (colours) => (theme) => {
78
+ const colour = theme.appearance === "light" ? colours.light : colours.dark;
68
79
  const truecolor = (theme.getColorMode?.() ?? "truecolor") === "truecolor";
69
80
  return (text) => paintWith({ colour, bold: true }, text, truecolor);
70
- }
81
+ };
82
+ /** Text in neon on a theme - blue whale on a light one - in as many colours as the terminal has. */
83
+ export const brandNeon = brandInk(BRAND_NEON);
84
+ /** Text in pink on a theme - eggplant on a light one - in as many colours as the terminal has. */
85
+ export const brandPink = brandInk(BRAND_PINK);
71
86
  /** How a spec diff is painted on a theme: its palette by whether the theme is dark or light, in as many colours as the terminal has. */
72
87
  export function plainPaint(theme) {
73
88
  const palette = theme.appearance === "light" ? LIGHT : DARK;
@@ -52,7 +52,7 @@ export function tabBar(pages, shown, width, style, now) {
52
52
  const text = `${mark}${tabName(page)}`;
53
53
  if (page === shown)
54
54
  return style.heading(`[${text}]`);
55
- return regenerating ? style.accent(text) : page.stuck ? style.error(text) : style.muted(text);
55
+ return regenerating ? style.accent(text) : page.stuck ? needs(style)(text) : style.muted(text);
56
56
  };
57
57
  const tabs = pages.map(label);
58
58
  const at = Math.max(0, shown === undefined ? 0 : pages.indexOf(shown));
@@ -69,6 +69,8 @@ export function tabBar(pages, shown, width, style, now) {
69
69
  }
70
70
  return truncateToWidth(lineOf(from, to), width, "…");
71
71
  }
72
+ /** What needs the person - a stuck page, its `!` - in the warning colour: it waits on them, nothing is broken. */
73
+ const needs = (style) => style.warning ?? style.error;
72
74
  /** Strike text through. */
73
75
  const strike = (text) => `\x1b[9m${text}\x1b[29m`;
74
76
  /** How far a group, and what is in it, is indented under its module. */
@@ -121,7 +123,7 @@ function rungGlyph(rung, style, now) {
121
123
  if (rung === "conform")
122
124
  return green(LADDER.conform);
123
125
  if (rung === "needsYou")
124
- return style.error(LADDER.needsYou);
126
+ return needs(style)(LADDER.needsYou);
125
127
  if (rung === "takenOut")
126
128
  return style.error(LADDER.takenOut);
127
129
  if (rung === "toRegenerate")
@@ -486,7 +488,7 @@ export function specMapLines(pages, state, width, style, _jobs = [], now = Date.
486
488
  // The first item that needs the person says what for, and how to go about it - opened or closed.
487
489
  if (page.stuck && !toldWhy && rungOf(item) === "needsYou") {
488
490
  toldWhy = true;
489
- for (const line of wrapped(needsLine(page), width, style.error))
491
+ for (const line of wrapped(needsLine(page), width, needs(style)))
490
492
  add(line, mark);
491
493
  }
492
494
  if (state.open.has(row.key)) {
@@ -515,7 +517,9 @@ function paintOf(mark, style) {
515
517
  return style.success ?? style.added;
516
518
  if (mark === "regenerating" || mark === "planning")
517
519
  return style.accent;
518
- if (mark === "stuck" || mark === "removed")
520
+ if (mark === "stuck")
521
+ return needs(style);
522
+ if (mark === "removed")
519
523
  return style.error;
520
524
  return style.muted;
521
525
  }
@@ -548,7 +552,7 @@ export function specMapHeader(page, width, style, waiting = "") {
548
552
  const strip = progressStrip(page, style);
549
553
  const count = total > 0 ? `${built} of ${total} ${total === 1 ? "functionality" : "functionalities"} built · ` : "";
550
554
  // Short, never cut off: what for, and how, is under the first `!`.
551
- const status = page.stuck ? style.error(`${LADDER.needsYou} needs you`) : page.status;
555
+ const status = page.stuck ? needs(style)(`${LADDER.needsYou} needs you`) : page.status;
552
556
  // The module's file heads its column, under this line.
553
557
  return truncateToWidth(`${style.heading("regen")}${strip !== "" ? ` ${strip}` : ""} ${count}${status}${waiting}`, width);
554
558
  }
@@ -4,6 +4,7 @@ import { configForSpec, modelSpec, projectModels, readProjectConfig } from "./co
4
4
  import { createDeployState } from "./release-tools.js";
5
5
  import { runReleaseAgent } from "./release-agent.js";
6
6
  import { listDeployed, runningDeployed, stopDeployed } from "./regenerate/release.js";
7
+ import { c } from "./module-report.js";
7
8
  const USAGE = `usage: turri release [file.plain] [options]
8
9
 
9
10
  Releases the module on top of the spec's requires chain, which holds the code of the ones below, and
@@ -20,12 +21,6 @@ harness-model (the agent's model).
20
21
  --in-module <module> the codebase to take, when modules built on this one branch
21
22
  --guidance <text> how this project releases it: the scripts to run, in order
22
23
  --model <provider/id> the agent's model (default: harness-model)`;
23
- const c = {
24
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
25
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
26
- green: (s) => `\x1b[32m${s}\x1b[0m`,
27
- cyan: (s) => `\x1b[36m${s}\x1b[0m`,
28
- };
29
24
  const PROBLEMS = "If you are having problems, use chat mode to deploy: run turri and ask to try it.";
30
25
  function parseArgs(argv) {
31
26
  const positional = [];
@@ -76,7 +71,7 @@ function followLogs() {
76
71
  closeSync(fd);
77
72
  offsets.set(item.log, size);
78
73
  for (const line of buffer.toString("utf8").split("\n").filter((l) => l !== ""))
79
- console.log(`${c.cyan(`[${item.name}]`)} ${line}`);
74
+ console.log(`${c.accent(`[${item.name}]`)} ${line}`);
80
75
  }
81
76
  if (running.length === 0) {
82
77
  clearInterval(timer);
@@ -119,7 +114,7 @@ async function main() {
119
114
  console.log(`\n\n${c.dim(PROBLEMS)}`);
120
115
  if (runningDeployed().length === 0)
121
116
  process.exit(0);
122
- console.log(c.green(c.bold("\nRunning - Ctrl-C stops it.")) + c.dim(" Its output follows.\n"));
117
+ console.log(c.success(c.bold("\nRunning - Ctrl-C stops it.")) + c.dim(" Its output follows.\n"));
123
118
  await followLogs();
124
119
  process.exit(0);
125
120
  }
@@ -1,4 +1,5 @@
1
1
  import { checkPlainTargets, formatCheckResults } from "./plain/check.js";
2
+ import { c } from "./module-report.js";
2
3
  const USAGE = `usage: turri validate <file.plain | directory> [options]
3
4
 
4
5
  Validates a ***plain specification statically: parses it, resolves its imports and
@@ -8,12 +9,6 @@ required modules, and reports every error without rendering anything.
8
9
  --json machine-readable output
9
10
 
10
11
  PLAIN_TEMPLATE_DIRS colon-separated template directories`;
11
- const c = {
12
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
13
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
14
- green: (s) => `\x1b[32m${s}\x1b[0m`,
15
- red: (s) => `\x1b[31m${s}\x1b[0m`,
16
- };
17
12
  const argv = process.argv.slice(2);
18
13
  const templateDirs = [];
19
14
  const positional = [];
@@ -43,10 +38,10 @@ if (json) {
43
38
  else {
44
39
  const output = formatCheckResults(results)
45
40
  .split("\n")
46
- .map((line) => line.startsWith("PASS") ? c.green(line) : line.startsWith("FAIL") ? c.red(line) : c.dim(line))
41
+ .map((line) => line.startsWith("PASS") ? c.success(line) : line.startsWith("FAIL") ? c.error(line) : c.dim(line))
47
42
  .join("\n");
48
43
  console.log(output);
49
44
  const failed = results.filter((r) => !r.ok).length;
50
- console.log(`\n${failed === 0 ? c.green(c.bold("all specs valid")) : c.red(c.bold(`${failed} of ${results.length} failed`))}`);
45
+ console.log(`\n${failed === 0 ? c.success(c.bold("all specs valid")) : c.error(c.bold(`${failed} of ${results.length} failed`))}`);
51
46
  }
52
47
  process.exit(results.every((r) => r.ok) ? 0 : 1);
@@ -2,49 +2,47 @@
2
2
  <title id="d-title">How turri works</title>
3
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
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,d09GMgABAAAAAAXcABAAAAAACxAAAAWAAAI2BAAAAAAAAAAAAAAAAAAAAAAAAAAAGhwbIBwqBmA/U1RBVEwAgRQRCAqKFId8ATYCJANMCygABCAFhCYHIAwHG7wIAC4KbGPY6Kmv4iVmaXOe4rF+1lGC5/n3/H7tnVQn6QXQ3pGUEZTOuHfE9wX9Cs480B1+3NubqG94Yt2d6zkBCMlRTjAE7PJKMIIo4Rx+LHt7WpPXyn9YgPwvLPufY6b7i7K9gXQAjoZY0Vg4iwaa4ICiLxOjwQ6P7uje8QHC+VsvYzHcoknnne+prqDv3hcBXABAUQQHIU4WggCGCqqQwUN5MlCKDZABAHDoCZrQ8apXzY8FQCsT2NvWaXo/IdC0nAZg1Hy1zAEi/QEKMQZFYePIgQBwNTWss/79/4HLL615b6AJDYAaQghCFAUKUKAB3brUFcsDlm8y+/KyEWxK5KeRsN4POg2g3wMWgkpOfVCLlU+Nq+ckG/9uKsjBwqWGlgyEDhseIqSYceMjQCgDlDwAmhzh35z5yedGg1atlEslYpEwYZARJINsiPl8PQGUgJwC+Rlwv0uLdYCQFE1ohu89BZBoLD8Wc43rhvOy4rFWlRUmu6ikurfrzHVmG89MnlnhXftm2aVHFU7IaX7xn9eNPPXGwugp5ZbUZfSbhph4A2VYLSQbx8VOdcSxjMQx6hBRbseZn37zTW7gfyvc5okrfbhsZ171HjdqVVNHZuoc//UF566eWCWNe4qJrlnwpy5TTveUNF5yHEltp6fEUCejRkwr/5HX/JbIlWvx1/ZUYom3pVpnbL9zes3hdE+F6rszT0HQTepSYOpbmClom18T4T0T/jnVox5+6KHiop2HTyY8pfzoMw8UFh86e62Igx9dXVK4/9Zzyaly2ZHHHzcF5288UDpARqFc7dYycym7uWTlzoOSGpzmDr+tv+v5NXe/4ydvvms+FKFoz8RjV2ePre2rHvsQY4IPV+y0KecmmJyRucL+E0dnhWgiWu+CsENON/Jjp05fuy232yBr021XIYsAN3hn5Cok1s6oo2MdPhRHG9Eh/nXJhf3rOguogjtX97rZuqr1Sa/VQLNm9/+za65/HgFnsHsJIgYmYkOXwBAmCoGyK98hBtPuIJCJCPv/ww9FQAkwOBQKhxGgDMAPfhmxCom10ag4uxfJTrRuJzp/7tT5U+ctYCo2avUcRle9uw0JgkLcad14LD2pWd6kC4sJSK1jjuHJ+oB290QIOqcfH17zeC2KJm1ISsAavFDwfWzKfDVP7FVat9s14abGpIJM6ZjtWqM9ZlPE06ZGDF7N7NPDKfAJ6mbiSkOSB3gbbg+6aX31+VMbWNLjJ6ZE8Eun17cZ7e7vPhaGn8i4+ztpAi5dbPJq3fM6CHxmmglTAd5tAeruvP5HtCdKrWHT3v6tDWIoiqZvTL1eae1zFhXOKmE7sKmzMyroDnQauGFnEnfePPPYP4WFq6e2DmwZdpNbksQX/w2aXPKAGh5itd/4uWng/w/7vaZSW5ynNhY1/X38mdfToad4fSQ5sXhicusbrVLDd/bVXhyevQ3QBC4QJi8PkAOMBmhbKw8BtKv9UoePI3Oze/8WtawvAa68euNVx9mDl3/+y2Sqdab1NUIYBUDg19Op0xj4/3NHkMdQytdpaCwMAB+4NpUfnDYWBmhA2XgWOjB86P5NAAgQIJ/TRLmdcL3zWSjgkMZC7BhwP6yZQgX3M5UobzMtuvIh0yYhRcwQZdKbEaZBPCEBV3Z0hXO5JBvDHLpdDkbOMIggIdJEcOPERYxeOtLehmqY7AW7aaSGYlCY3GbcBIhyjyFIgGBoPBEt82DHSo65DCBODBdBImxLU+7dYoSIgqEd7ViKm/U5cSxMs5AgfmYqNfbraw8/1pmJYrl92IDQ1tRBQr0YiQyLzWqfimzlnvVMUKjv0uJGu3rrtV4HlPrPvZxNDDO+h7QVCUH8mL+tAAAyAQGEofJukXn7OAAA) 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; }
5
+ @font-face { font-family: "Geist Mono"; font-weight: 400; src: url(data:font/woff2;base64,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) format("woff2"); }
6
+ @font-face { font-family: "Geist Mono"; font-weight: 500; src: url(data:font/woff2;base64,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) format("woff2"); }
7
+ text { font-family: "Geist Mono", ui-monospace, Menlo, monospace; font-variant-ligatures: none; font-feature-settings: "calt" 0, "liga" 0; }
8
+ .lbl { font-family: "Geist Mono", ui-monospace, Menlo, monospace; font-size: 15px; font-weight: 400; fill: #85a3b2; }
9
+ .node { font-family: "Geist Mono", ui-monospace, Menlo, monospace; font-weight: 500; font-size: 22px; letter-spacing: -0.01em; }
10
+ .sub { font-family: "Geist Mono", ui-monospace, Menlo, monospace; font-weight: 400; font-size: 12px; letter-spacing: 0.04em; fill: #85a3b2; }
11
+ .flow { fill: none; stroke: #85a3b2; stroke-width: 2; }
12
+ .review { stroke: #3e5a6b; stroke-width: 4; stroke-dasharray: 8 8; }
15
13
  @media (prefers-reduced-motion: no-preference) {
16
- .spin { transform-box: fill-box; transform-origin: 50% 58%; animation: turn 6s linear infinite; }
14
+ .spin { transform-box: fill-box; transform-origin: 50% 58%; animation: turn 6s steps(8) infinite; }
17
15
  @keyframes turn { to { transform: rotate(360deg); } }
18
16
  }
19
17
  </style>
20
- <rect x="16" y="-5" width="1100" height="490" rx="18" fill="#e0ff6e"/>
18
+ <rect x="16" y="-5" width="1100" height="490" fill="#0a0f1a"/>
21
19
  <defs>
22
20
  <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"/>
21
+ <path d="M0 0 L10 5 L0 10 z" fill="#85a3b2"/>
24
22
  </marker>
25
23
  </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)"/>
24
+ <path class="flow review" d="M120 140 V 40 H 985 V 155"/>
25
+ <text class="lbl" x="552" y="24" text-anchor="middle">review loop</text>
26
+ <path class="flow" d="M180 180 L 466 180" marker-end="url(#head)"/>
29
27
  <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)"/>
28
+ <path class="flow" d="M466 220 L 180 220" marker-end="url(#head)"/>
31
29
  <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)"/>
30
+ <path class="flow" d="M650 180 L 896 180" marker-end="url(#head)"/>
33
31
  <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)"/>
32
+ <path class="flow" d="M896 220 L 650 220" marker-end="url(#head)"/>
35
33
  <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)"/>
34
+ <path class="flow" d="M538 247 L 538 376" marker-end="url(#head)"/>
37
35
  <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)"/>
36
+ <path class="flow" d="M578 376 L 578 247" marker-end="url(#head)"/>
39
37
  <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>
38
+ <text class="spin" x="880" y="372" text-anchor="middle" font-weight="500" font-size="44" fill="#5ce1e6">↻</text>
39
+ <rect x="66" y="146" width="108" height="108" fill="#142030"/><rect x="66" y="143" width="108" height="3" fill="#85a3b2"/><rect x="66" y="254" width="108" height="3" fill="#85a3b2"/><rect x="63" y="146" width="3" height="108" fill="#85a3b2"/><rect x="174" y="146" width="3" height="108" fill="#85a3b2"/>
40
+ <text class="node" x="120" y="208" text-anchor="middle" fill="#f4f7f9">you</text>
41
+ <rect x="470" y="156" width="176" height="88" fill="#142030"/><rect x="470" y="153" width="176" height="3" fill="#85a3b2"/><rect x="470" y="244" width="176" height="3" fill="#85a3b2"/><rect x="467" y="156" width="3" height="88" fill="#85a3b2"/><rect x="646" y="156" width="3" height="88" fill="#85a3b2"/>
42
+ <text x="558" y="211" text-anchor="middle" font-weight="500" font-size="34" letter-spacing="-0.02em" fill="#ff5c8d">turri</text>
43
+ <rect x="900" y="158" width="170" height="84" fill="#142030"/><rect x="900" y="155" width="170" height="3" fill="#85a3b2"/><rect x="900" y="242" width="170" height="3" fill="#85a3b2"/><rect x="897" y="158" width="3" height="84" fill="#85a3b2"/><rect x="1070" y="158" width="3" height="84" fill="#85a3b2"/>
44
+ <text x="985" y="208" text-anchor="middle" font-weight="500" font-size="24" fill="#e9d8c8">***plain</text>
45
+ <rect x="473" y="380" width="170" height="84" fill="none" stroke="#3e5a6b" stroke-width="4" stroke-dasharray="8 8"/>
46
+ <text class="node" x="558" y="420" text-anchor="middle" fill="#f4f7f9">code</text>
47
+ <text class="sub" x="558" y="444" text-anchor="middle">disposable</text>
50
48
  </svg>
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turri-dev/cli",
3
- "version": "0.1.4-next.67734c0",
3
+ "version": "0.1.4-next.8254fbc",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {