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

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 (43) hide show
  1. package/.pi/themes/turri-dark.json +66 -67
  2. package/.pi/themes/turri-light.json +73 -71
  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/light-00.png +0 -0
  19. package/assets/jellyfish/light-01.png +0 -0
  20. package/assets/jellyfish/light-02.png +0 -0
  21. package/assets/jellyfish/light-03.png +0 -0
  22. package/assets/jellyfish/light-04.png +0 -0
  23. package/assets/jellyfish/light-05.png +0 -0
  24. package/assets/jellyfish/light-06.png +0 -0
  25. package/assets/jellyfish/light-07.png +0 -0
  26. package/assets/jellyfish/light-08.png +0 -0
  27. package/assets/jellyfish/light-09.png +0 -0
  28. package/assets/jellyfish/light-10.png +0 -0
  29. package/assets/jellyfish/light-11.png +0 -0
  30. package/assets/jellyfish/light-12.png +0 -0
  31. package/assets/jellyfish/light-13.png +0 -0
  32. package/assets/jellyfish/light-14.png +0 -0
  33. package/dist/brand.js +7 -7
  34. package/dist/cultivate-cli.js +23 -16
  35. package/dist/extension.js +4 -4
  36. package/dist/loop-cli.js +8 -8
  37. package/dist/module-report.js +4 -5
  38. package/dist/regenerate/plain-palette.js +36 -51
  39. package/dist/regenerate/spec-map-view.js +5 -9
  40. package/dist/release-cli.js +8 -3
  41. package/dist/validate-cli.js +8 -3
  42. package/docs/images/how-turri-works.svg +31 -29
  43. package/package.json +1 -1
@@ -2,80 +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-dark",
4
4
  "vars": {
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"
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"
17
16
  },
18
17
  "colors": {
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",
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",
35
34
  "userMessageBg": "",
36
- "userMessageText": "white",
35
+ "userMessageText": "yellow",
37
36
  "customMessageBg": "",
38
- "customMessageText": "grauzone",
39
- "customMessageLabel": "neon",
37
+ "customMessageText": "greenLight",
38
+ "customMessageLabel": "yellow",
40
39
  "toolPendingBg": "",
41
40
  "toolSuccessBg": "",
42
41
  "toolErrorBg": "",
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"
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"
75
74
  },
76
75
  "export": {
77
- "pageBg": "#0a0f1a",
78
- "cardBg": "#0d1420",
79
- "infoBg": "#142030"
76
+ "pageBg": "#0f0f0f",
77
+ "cardBg": "#121212",
78
+ "infoBg": "#223936"
80
79
  }
81
- }
80
+ }
@@ -2,79 +2,81 @@
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
- "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"
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"
16
18
  },
17
19
  "colors": {
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"
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"
74
76
  },
75
77
  "export": {
76
- "pageBg": "#f4f7f9",
77
- "cardBg": "#f4f7f9",
78
- "infoBg": "#e9d8c8"
78
+ "pageBg": "#ffffff",
79
+ "cardBg": "#f4f4f2",
80
+ "infoBg": "#eef7f5"
79
81
  }
80
- }
82
+ }
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 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;
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;
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("#1e3442", 34) : ink("#5ce1e6", 96),
38
+ accent: light ? ink("#0a1fd4", 34) : ink("#e0ff6e", 93),
39
39
  /** Done, and what was written. */
40
- success: light ? ink("#732553", 35) : ink("#ff5c8d", 95),
40
+ success: light ? ink("#223936", 32) : ink("#79fc96", 92),
41
41
  /** What is wrong: what stops turri. */
42
- error: ink("#ff7a3d", 33),
42
+ error: light ? ink("#d00505", 31) : ink("#dd5353", 91),
43
43
  /** What works, with limits. */
44
- warning: ink("#ff7a3d", 33),
44
+ warning: ink("#dc6803", 33),
45
45
  /** What goes with it, and can be skimmed. */
46
- muted: light ? ink("#3e5a6b", 90) : ink("#85a3b2", 37),
46
+ muted: ink("#808080", 90),
47
47
  bold: (text) => (on ? `\x1b[1m${text}\x1b[22m` : text),
48
48
  };
49
49
  }
@@ -7,33 +7,40 @@ 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
- import { c } from "./module-report.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
+ };
11
18
  const reporter = {
12
- step: (name, attempt) => console.log(`\n${c.bold(c.accent(`> step ${name}`))} ${c.dim(`attempt ${attempt}`)}`),
19
+ step: (name, attempt) => console.log(`\n${c.bold(c.cyan(`> step ${name}`))} ${c.dim(`attempt ${attempt}`)}`),
13
20
  artifact: (path, body) => {
14
21
  console.log(c.dim(` ${path}:`));
15
22
  for (const line of body.split("\n"))
16
23
  console.log(c.dim(` | ${line}`));
17
24
  },
18
25
  gate: (name, judges) => console.log(judges === 0
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`)}`),
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`)}`),
21
28
  livePass: (step, scope, findings) => console.log(findings === 0
22
29
  ? c.dim(` review ${step}: ${scope} - clean`)
23
- : ` ${c.warning(`review ${step}`)}: ${scope} ${c.dim(`- ${findings} to fix`)}`),
30
+ : ` ${c.yellow(`review ${step}`)}: ${scope} ${c.dim(`- ${findings} to fix`)}`),
24
31
  liveReview: (name, passes, findings) => console.log(findings === 0
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`)}`),
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`)}`),
27
34
  judge: (index, model, verdict, violations) => {
28
- const mark = verdict === "pass" ? c.success("pass") : c.error(verdict);
35
+ const mark = verdict === "pass" ? c.green("pass") : c.red(verdict);
29
36
  console.log(` judge ${index + 1} ${c.dim(`(${model})`)}: ${mark}${violations ? c.dim(` (${violations})`) : ""}`);
30
37
  },
31
38
  outcome: (name, decision, critique) => {
32
39
  const colour = {
33
- advance: c.success,
34
- retry: c.warning,
35
- escalate: c.warning,
36
- abort: c.error,
40
+ advance: c.green,
41
+ retry: c.yellow,
42
+ escalate: c.yellow,
43
+ abort: c.red,
37
44
  };
38
45
  console.log(` ${c.bold(colour[decision](decision))} ${c.dim(`<- gate ${name}`)}`);
39
46
  if (critique)
@@ -71,7 +78,7 @@ async function main() {
71
78
  select: async (title, options, context) => {
72
79
  const rl = createInterface({ input: process.stdin, output: process.stdout });
73
80
  const list = options.map((option, i) => ` ${c.bold(String(i + 1))}. ${option}`).join("\n");
74
- const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.accent("? "))}${title}\n${list}\n> `)).trim();
81
+ const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.cyan("? "))}${title}\n${list}\n> `)).trim();
75
82
  rl.close();
76
83
  const index = Number.parseInt(reply, 10);
77
84
  if (Number.isInteger(index) && index >= 1 && index <= options.length)
@@ -80,13 +87,13 @@ async function main() {
80
87
  },
81
88
  input: async (title, context) => {
82
89
  const rl = createInterface({ input: process.stdin, output: process.stdout });
83
- const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.accent("? "))}${title}\n> `)).trim();
90
+ const reply = (await rl.question(`\n${context !== undefined ? `${context}\n\n` : ""}${c.bold(c.cyan("? "))}${title}\n> `)).trim();
84
91
  rl.close();
85
92
  return reply === "" ? undefined : reply;
86
93
  },
87
94
  }),
88
95
  escalate: async ({ question, critique }) => {
89
- console.log(`\n${c.bold(c.warning("! human review required"))}`);
96
+ console.log(`\n${c.bold(c.yellow("! human review required"))}`);
90
97
  console.log(c.dim(critique));
91
98
  const rl = createInterface({ input: process.stdin, output: process.stdout });
92
99
  const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();
@@ -94,7 +101,7 @@ async function main() {
94
101
  return answer === "y";
95
102
  },
96
103
  });
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))}`)}`);
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))}`)}`);
98
105
  process.exit(result.ok ? 0 : 1);
99
106
  }
100
107
  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 { brandNeon, brandPink, plainPaint } from "./regenerate/plain-palette.js";
32
+ import { brandBlue, 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: neon.
1650
- conforms: brandNeon(theme),
1649
+ // What conformance tests show does what its spec says: brand blue.
1650
+ conforms: brandBlue(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
- `${brandPink(theme)("turri")} ${theme.fg("muted", `v${data.version}`)}` +
2311
+ `${theme.fg("accent", "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.accent(`▶ ${job.id}`)} ${pulseName(job)}${job.stage !== undefined ? c.dim(` - ${job.stage}`) : ""}`);
83
+ console.log(`${c.cyan(`▶ ${job.id}`)} ${pulseName(job)}${job.stage !== undefined ? c.dim(` - ${job.stage}`) : ""}`);
84
84
  },
85
85
  onFinish: (job) => {
86
- const mark = job.status === "succeeded" ? c.success("✓") : job.status === "stopped" ? c.dim("■") : c.error("✗");
86
+ const mark = job.status === "succeeded" ? c.green("✓") : job.status === "stopped" ? c.dim("■") : c.red("✗");
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.warning(` spec gap: ${gap.question}`), c.dim(`Meanwhile: ${gap.assumption}`));
93
+ console.log(c.yellow(` 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.warning(` ${event.job !== undefined ? `${event.job}: ` : ""}${line}`));
101
+ console.log(c.yellow(` ${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.error(message));
134
+ console.error(c.red(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.error(c.bold(`${failed.length} job${failed.length === 1 ? "" : "s"} failed`))
165
+ ? c.red(c.bold(`${failed.length} job${failed.length === 1 ? "" : "s"} failed`))
166
166
  : unfinished.length > 0
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"));
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"));
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,10 +1,9 @@
1
- import { brand } from "./brand.js";
2
1
  /** Colours for what the command line prints. */
3
2
  export const c = {
4
3
  dim: (s) => `\x1b[2m${s}\x1b[0m`,
5
4
  bold: (s) => `\x1b[1m${s}\x1b[0m`,
6
- success: brand.success,
7
- error: brand.error,
8
- warning: brand.warning,
9
- accent: brand.accent,
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`,
10
9
  };
@@ -1,46 +1,38 @@
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
- */
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. */
7
2
  const DARK = {
8
3
  roles: {
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" },
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" },
17
12
  },
18
- added: "#5ce1e6",
19
- removed: "#ff7fa6",
20
- addedFill: "#1e3442",
21
- removedFill: "#732553",
22
- muted: "#85a3b2",
13
+ added: "#86efac",
14
+ removed: "#fca5a5",
15
+ addedFill: "#1f4d2e",
16
+ removedFill: "#5c2323",
17
+ muted: "#808080",
23
18
  };
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
- */
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. */
28
20
  const LIGHT = {
29
21
  roles: {
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" },
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" },
38
30
  },
39
- added: "#1e3442",
40
- removed: "#732553",
41
- addedFill: "#8eebee",
42
- removedFill: "#e9d8c8",
43
- muted: "#3e5a6b",
31
+ added: "#166534",
32
+ removed: "#991b1b",
33
+ addedFill: "#d3f5dc",
34
+ removedFill: "#fcd9d9",
35
+ muted: "#6b7280",
44
36
  };
45
37
  const rgb = (hex) => {
46
38
  const value = Number.parseInt(hex.slice(1), 16);
@@ -65,24 +57,17 @@ const fillWith = (hex, text, truecolor) => {
65
57
  return `${on}${text.replace(/\x1b\[(?:0|49)?m/g, (reset) => `${reset}${on}`)}\x1b[49m`;
66
58
  };
67
59
  /**
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.
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.
70
63
  */
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;
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;
79
68
  const truecolor = (theme.getColorMode?.() ?? "truecolor") === "truecolor";
80
69
  return (text) => paintWith({ colour, bold: true }, text, truecolor);
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);
70
+ }
86
71
  /** 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. */
87
72
  export function plainPaint(theme) {
88
73
  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 ? needs(style)(text) : style.muted(text);
55
+ return regenerating ? style.accent(text) : page.stuck ? style.error(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,8 +69,6 @@ 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;
74
72
  /** Strike text through. */
75
73
  const strike = (text) => `\x1b[9m${text}\x1b[29m`;
76
74
  /** How far a group, and what is in it, is indented under its module. */
@@ -123,7 +121,7 @@ function rungGlyph(rung, style, now) {
123
121
  if (rung === "conform")
124
122
  return green(LADDER.conform);
125
123
  if (rung === "needsYou")
126
- return needs(style)(LADDER.needsYou);
124
+ return style.error(LADDER.needsYou);
127
125
  if (rung === "takenOut")
128
126
  return style.error(LADDER.takenOut);
129
127
  if (rung === "toRegenerate")
@@ -488,7 +486,7 @@ export function specMapLines(pages, state, width, style, _jobs = [], now = Date.
488
486
  // The first item that needs the person says what for, and how to go about it - opened or closed.
489
487
  if (page.stuck && !toldWhy && rungOf(item) === "needsYou") {
490
488
  toldWhy = true;
491
- for (const line of wrapped(needsLine(page), width, needs(style)))
489
+ for (const line of wrapped(needsLine(page), width, style.error))
492
490
  add(line, mark);
493
491
  }
494
492
  if (state.open.has(row.key)) {
@@ -517,9 +515,7 @@ function paintOf(mark, style) {
517
515
  return style.success ?? style.added;
518
516
  if (mark === "regenerating" || mark === "planning")
519
517
  return style.accent;
520
- if (mark === "stuck")
521
- return needs(style);
522
- if (mark === "removed")
518
+ if (mark === "stuck" || mark === "removed")
523
519
  return style.error;
524
520
  return style.muted;
525
521
  }
@@ -552,7 +548,7 @@ export function specMapHeader(page, width, style, waiting = "") {
552
548
  const strip = progressStrip(page, style);
553
549
  const count = total > 0 ? `${built} of ${total} ${total === 1 ? "functionality" : "functionalities"} built · ` : "";
554
550
  // Short, never cut off: what for, and how, is under the first `!`.
555
- const status = page.stuck ? needs(style)(`${LADDER.needsYou} needs you`) : page.status;
551
+ const status = page.stuck ? style.error(`${LADDER.needsYou} needs you`) : page.status;
556
552
  // The module's file heads its column, under this line.
557
553
  return truncateToWidth(`${style.heading("regen")}${strip !== "" ? ` ${strip}` : ""} ${count}${status}${waiting}`, width);
558
554
  }
@@ -4,7 +4,6 @@ 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";
8
7
  const USAGE = `usage: turri release [file.plain] [options]
9
8
 
10
9
  Releases the module on top of the spec's requires chain, which holds the code of the ones below, and
@@ -21,6 +20,12 @@ harness-model (the agent's model).
21
20
  --in-module <module> the codebase to take, when modules built on this one branch
22
21
  --guidance <text> how this project releases it: the scripts to run, in order
23
22
  --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
+ };
24
29
  const PROBLEMS = "If you are having problems, use chat mode to deploy: run turri and ask to try it.";
25
30
  function parseArgs(argv) {
26
31
  const positional = [];
@@ -71,7 +76,7 @@ function followLogs() {
71
76
  closeSync(fd);
72
77
  offsets.set(item.log, size);
73
78
  for (const line of buffer.toString("utf8").split("\n").filter((l) => l !== ""))
74
- console.log(`${c.accent(`[${item.name}]`)} ${line}`);
79
+ console.log(`${c.cyan(`[${item.name}]`)} ${line}`);
75
80
  }
76
81
  if (running.length === 0) {
77
82
  clearInterval(timer);
@@ -114,7 +119,7 @@ async function main() {
114
119
  console.log(`\n\n${c.dim(PROBLEMS)}`);
115
120
  if (runningDeployed().length === 0)
116
121
  process.exit(0);
117
- console.log(c.success(c.bold("\nRunning - Ctrl-C stops it.")) + c.dim(" Its output follows.\n"));
122
+ console.log(c.green(c.bold("\nRunning - Ctrl-C stops it.")) + c.dim(" Its output follows.\n"));
118
123
  await followLogs();
119
124
  process.exit(0);
120
125
  }
@@ -1,5 +1,4 @@
1
1
  import { checkPlainTargets, formatCheckResults } from "./plain/check.js";
2
- import { c } from "./module-report.js";
3
2
  const USAGE = `usage: turri validate <file.plain | directory> [options]
4
3
 
5
4
  Validates a ***plain specification statically: parses it, resolves its imports and
@@ -9,6 +8,12 @@ required modules, and reports every error without rendering anything.
9
8
  --json machine-readable output
10
9
 
11
10
  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
+ };
12
17
  const argv = process.argv.slice(2);
13
18
  const templateDirs = [];
14
19
  const positional = [];
@@ -38,10 +43,10 @@ if (json) {
38
43
  else {
39
44
  const output = formatCheckResults(results)
40
45
  .split("\n")
41
- .map((line) => line.startsWith("PASS") ? c.success(line) : line.startsWith("FAIL") ? c.error(line) : c.dim(line))
46
+ .map((line) => line.startsWith("PASS") ? c.green(line) : line.startsWith("FAIL") ? c.red(line) : c.dim(line))
42
47
  .join("\n");
43
48
  console.log(output);
44
49
  const failed = results.filter((r) => !r.ok).length;
45
- console.log(`\n${failed === 0 ? c.success(c.bold("all specs valid")) : c.error(c.bold(`${failed} of ${results.length} failed`))}`);
50
+ console.log(`\n${failed === 0 ? c.green(c.bold("all specs valid")) : c.red(c.bold(`${failed} of ${results.length} failed`))}`);
46
51
  }
47
52
  process.exit(results.every((r) => r.ok) ? 0 : 1);
@@ -2,47 +2,49 @@
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: "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; }
5
+ @font-face { font-family: "Archivo"; font-weight: 900; src: url(data:font/woff2;base64,d09GMgABAAAAAAN0ABAAAAAABuQAAAMZAAIAQgAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbehwqBmA/U1RBVFoARBEICoIAgV8BNgIkAxQLDAAEIAWESgcgDAcbnQUgrgN4MoaXUIIRo4bSsCiga71j9UcNccO+yjyC+P1os19OzJJmreeIVRLJ+4WIehOXTKRJJRSJ8Pyncf+de2ce5wNCgxqZAC0ct6/blrChgUdrbfZNUdmKmpz5LBKSlaKpfEIsfsMjiZDIAa9FmMiRBO5xu8oTYkgzRXzs+9+lkO3UTW0sDfC/Eg80wzWI0sVrASYYaPgX/QUUUZwVWGASpe6a4qnU8vN++BgB6gBkCJEIgRo+ovDC4Pg8xPbtoA4xEkgQEHs5W4cACBAkpkf0mAcJRkVgEgAAgTGBCIQQJjCRXOsngM9xiaHI0vMFJWTvskM4oi4XP9X6lXQAfsTmjZMjkCBh+cB/UGwghwgga+BnGCIQoidAGwEm8do+ioSAtZ0iEiB2445KnDAtK24IZGGvBwgEUSMWkGMczoAAZBA8JHWfFAGq6DHYzcYSIRCVLqQT6bgAkGIzEQeUgBqc3whsB6D43F9FhbTpcmwPhZZSSaVRteGjj59qv27LeOjVk2PYcSj5hTCSqg9DMZq5r2qx/exN7XkyKJ65eHCjDr99TrmSxrrQ/ddvbRP77/bfbnTXBClNV3Wb/V8OzhdlfN+zmyYuvQ78yq+N29b1NhUoOpktvThzPnRiPpgdaR7/8x/4q21F2Fy8K2X997rzFrMa3zXdVG/2h9xezABAgEzWr9dWPOVvgPgG+P6P4MdMl+X/1lRRbBQToAIDAqr30kH43z8jcK3MVqyLdCnxtj+kNzsyh0CpnxcQsFXvUo4KA+AWpkJC3CvMzFdFyPA9lG6KhlN4yVpBkZIhoC76oBCMMUIa2QnfHuyS7wR203cG/ql5BtMxm0QgQlSUiOkoDzVEHCqTbY9IcsBAVbAZZJw96CIyCAw2JwwIhkImR6tw4fpJ462YXZW6GTRVyml0kl2Mo0KNYyZOVdLpkkNE1TjaAtmLq9pHZVHt2KOYxREwuYutVpgIESIlKzdh0oi2kNVNXUEsmhfLSQw6qnQVhlhynlg3gWQfui5Ikww2zQ7VCFSTkj2zjI2LdG6ANOUYfxXpj+RRAAA=) format("woff2"); }
6
+ @font-face { font-family: "Archivo"; font-weight: 800; src: url(data:font/woff2;base64,d09GMgABAAAAAARoABAAAAAACJwAAAQNAAIAQgAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbgRAcKgZgP1NUQVRaAEwRCAqEcIQUATYCJAMcCxAABCAFhGoHIAwHGxgHKB6FccNdgjLFEIv53uLyOp7///3+N9e+57sm0yTgybwE8f4rdCIhkRhdNGRC4VMilffzedb7TYcZyRkQmwJOF4CK41InIf9hXSYupz+WPr61fmGAXUCBJToX/m9+bNFHYwImlGY9dw/0QE2Pto0FGJB3TbabIke9urYiGAWUiH4RDNuQg3atvrUXPF6zdQrw0ODXaHrF1yjAmY3SFD8GlZ0L4lAlTLPL0UnJ/fIYmVhii1q6qEK3JOFBTgBoVdFFcaB+0WYB77Aof+fwXxCQP/HwfnsTXAAuqlP8D7R7aBUQqHHaPmAFJmJQh1kYDtBOeDoRQuDkmUiHQF7o2DIUgqbftAgIlUA8IEKGsCigUyqhAAGF/VwQSXc+N1xZ2/KEkh7VAzxCmfp/naUWmJuTGJHdgwkwAMYxhN8AGQCgAaKUEtmc9w139ZoOjmDPstnQRLsdn221WsH17b5TrrNOHABvDt7EZ5/ej/AJFoulj7zk6slgHOF0137XPZb+2ecPIzRxk+LQSZsywKgp5fju1U3FMD5x3a5jGsN7hCMCUv/s2/bKh24R+AZxs8+EMJw2YeXOrcHkMM/CjjsWtnQtADFmhTLwvqUbNwbjy9fvuxc4gA/usTywFOADFFFpJskqM0X0FzGC6/Pju4o3xkiPRid8ITPi0smIxJgrY2JWFcZ31+UzQ+DvSA2byKWYZJ6JSVRFzr371fH3Fss0vNFpV08yjN+40JocFHFQyw2Nv1ucGptSfAcqRobHllTrLa6KGk0M9BuKy4zMWHMKFZ4SPLLsid/6rJjmmhxW8IG2RyHXZqYFxn1fC5dHBscQ1WaSrKbGEINDFJACzdaG5ExZeny6PCM5KUPe/wZZJnzE+0aR7FFb6vz357/zCT+1Lixvi+d0l2an4GfVfrDwStzwZlFdmIlTdd9jf9tr5vOXtY5l75dtLqt+s/noT/ekh+bWKAAIypVR2eOTTM/Sf84Y9hbw52/YQcBpUYX3/7hedptMjwL6FQifI/1JhvhvHAOxWw3ULLhbPIgvagCl4Gh7PRBVjiYY8IyDzTa73ZgP0ElzAT4CCh3HdtBl3CndueX9YJOk+ywnx9fvMK0NCIxmHKQRDGcW7QLTWZIJhOi2TAhEDzkL5ZpzcJWSCkVDTEhEJ0qOrIsYpZcO8o0qGlyKIWagnMFOs5IEH5dOitTTuZU0tLkke/N0VLRKZMokJF6+lR5HBi4luUwd5BTEOPS00vWiqPBl+h2dHIfRg09OA6tFGd5C1jHpGmyOfzc+IT3Zp2jkyJAlSzZChzbtmhDWW7fB0sH+aK6TxJQUolCvLQIDyepHKC4xHl076Vx7fblybDJNsUetGFc3Je9KChjQk0mzV39D/6dOnwsA) format("woff2"); }
7
+ @font-face { font-family: "JetBrains Mono"; font-weight: 500; src: url(data:font/woff2;base64,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) format("woff2"); }
8
+ @font-face { font-family: "JetBrains Mono"; font-weight: 700; src: url(data:font/woff2;base64,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) format("woff2"); }
9
+ text { font-variant-ligatures: none; font-feature-settings: "calt" 0, "liga" 0; }
10
+ .lbl { font-family: "JetBrains Mono", ui-monospace, Menlo, monospace; font-size: 15px; font-weight: 500; fill: #0f0f0f; }
11
+ .node { font-family: "Archivo", "Helvetica Neue", Arial, sans-serif; font-weight: 800; font-size: 22px; }
12
+ .sub { font-family: "JetBrains Mono", ui-monospace, Menlo, monospace; font-weight: 700; font-size: 12px; letter-spacing: 0.06em; }
13
+ .flow { fill: none; stroke: #0f0f0f; stroke-width: 2.25; stroke-linecap: round; }
14
+ .review { stroke-dasharray: 9 7; }
13
15
  @media (prefers-reduced-motion: no-preference) {
14
- .spin { transform-box: fill-box; transform-origin: 50% 58%; animation: turn 6s steps(8) infinite; }
16
+ .spin { transform-box: fill-box; transform-origin: 50% 58%; animation: turn 6s linear infinite; }
15
17
  @keyframes turn { to { transform: rotate(360deg); } }
16
18
  }
17
19
  </style>
18
- <rect x="16" y="-5" width="1100" height="490" fill="#0a0f1a"/>
20
+ <rect x="16" y="-5" width="1100" height="490" rx="18" fill="#e0ff6e"/>
19
21
  <defs>
20
22
  <marker id="head" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
21
- <path d="M0 0 L10 5 L0 10 z" fill="#85a3b2"/>
23
+ <path d="M0 0 L10 5 L0 10 z" fill="#0f0f0f"/>
22
24
  </marker>
23
25
  </defs>
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)"/>
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)"/>
27
29
  <text class="lbl" x="324" y="166" text-anchor="middle">describe changes</text>
28
- <path class="flow" d="M466 220 L 180 220" marker-end="url(#head)"/>
30
+ <path class="flow" d="M474.8 220 L 174.4 220" marker-end="url(#head)"/>
29
31
  <text class="lbl" x="324" y="246" text-anchor="middle">asks · reports</text>
30
- <path class="flow" d="M650 180 L 896 180" marker-end="url(#head)"/>
32
+ <path class="flow" d="M641.2 180 L 900 180" marker-end="url(#head)"/>
31
33
  <text class="lbl" x="771" y="166" text-anchor="middle">writes</text>
32
- <path class="flow" d="M896 220 L 650 220" marker-end="url(#head)"/>
34
+ <path class="flow" d="M900 220 L 641.2 220" marker-end="url(#head)"/>
33
35
  <text class="lbl" x="771" y="246" text-anchor="middle">reads</text>
34
- <path class="flow" d="M538 247 L 538 376" marker-end="url(#head)"/>
36
+ <path class="flow" d="M538 244 L 538 380" marker-end="url(#head)"/>
35
37
  <text class="lbl" x="524" y="318" text-anchor="end">regenerates</text>
36
- <path class="flow" d="M578 376 L 578 247" marker-end="url(#head)"/>
38
+ <path class="flow" d="M578 380 L 578 244" marker-end="url(#head)"/>
37
39
  <text class="lbl" x="592" y="318" text-anchor="start">tests against ***plain</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>
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>
48
50
  </svg>
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.67734c0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {