@kpnpm/homie 0.3.0 → 0.3.2

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.
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # homie
1
+ <p align="center">
2
+ <img src="docs/logo.svg" alt="homie" width="96" height="96">
3
+ </p>
2
4
 
3
- *Same brain. Same code. Different voice.*
5
+ <h1 align="center">homie</h1>
6
+
7
+ <p align="center"><em>Same brain. Same code. Different voice.</em></p>
4
8
 
5
9
  Your coding agent didn't need another corporate assistant. **homie** switches
6
10
  its chat voice to a technically competent friend — in three levels:
@@ -11,10 +15,12 @@ its chat voice to a technically competent friend — in three levels:
11
15
  | 😏 **dawg** | Brutal opinions with playful energy. Reacts like a hype friend: "damn that's crazy", "insaneee", "no wayyy", "what the hell", "jeez". Roasts the work, not you. Mild swearing (damn, hell, crap), rarely. |
12
16
  | 💀 **mafa** | No mercy zone. Says what a blunt friend says on a bad day: "shut up and listen", calls bad work "bullshit" or "dogshit" — including yours. Swears zero to four times per response, never forced. Roasts the person too. |
13
17
 
18
+ > [!NOTE]
14
19
  > **Default is dawg.** Out of the box homie is blunt with mild swearing. Want
15
20
  > the clean voice? `/homie default yo`, or set `HOMIE_DEFAULT_LEVEL=yo`.
16
21
 
17
- > ⚠️ **mafa has no mercy.** It will call your architecture dogshit and may
22
+ > [!CAUTION]
23
+ > **mafa has no mercy.** It will call your architecture dogshit and may
18
24
  > tell you to shut up and listen — that's the product, not a bug. What it never
19
25
  > does: slurs, attacks on who you are, or mocking someone genuinely stuck. It
20
26
  > still drops the bit when things get real. Opting into mafa is opting into a
@@ -60,7 +66,10 @@ Same four technical points every time. Only the voice changes.
60
66
 
61
67
  ## Install
62
68
 
63
- **Claude Code:**
69
+ Choose your agent:
70
+
71
+ <details open>
72
+ <summary><strong>Claude Code</strong></summary>
64
73
 
65
74
  ```
66
75
  /plugin marketplace add prashanthgit19/homie
@@ -72,7 +81,10 @@ Same four technical points every time. Only the voice changes.
72
81
 
73
82
  (two separate prompts)
74
83
 
75
- **OpenCode:**
84
+ </details>
85
+
86
+ <details open>
87
+ <summary><strong>OpenCode</strong></summary>
76
88
 
77
89
  ```bash
78
90
  opencode plugin add @kpnpm/homie
@@ -84,7 +96,10 @@ or in a project's `opencode.json`:
84
96
  { "plugins": ["@kpnpm/homie"] }
85
97
  ```
86
98
 
87
- **Codex:**
99
+ </details>
100
+
101
+ <details open>
102
+ <summary><strong>Codex</strong></summary>
88
103
 
89
104
  ```bash
90
105
  codex plugin marketplace add prashanthgit19/homie
@@ -94,7 +109,10 @@ codex plugin add homie@homie
94
109
  Then open `/hooks` in Codex, trust the two lifecycle hooks, and start a new
95
110
  thread.
96
111
 
97
- **Pi (pi.dev):**
112
+ </details>
113
+
114
+ <details open>
115
+ <summary><strong>Pi (pi.dev)</strong></summary>
98
116
 
99
117
  ```bash
100
118
  pi install npm:@kpnpm/homie
@@ -102,9 +120,23 @@ pi install npm:@kpnpm/homie
102
120
 
103
121
  Also works for Oh My Pi (`omp`), which runs Pi extensions unchanged.
104
122
 
105
- **Any other agent:** copy [`AGENTS.md`](AGENTS.md) into your project, or ask
106
- your agent to install [`skills/homie/SKILL.md`](skills/homie/SKILL.md) as a
107
- skill. More in [INSTALL.md](INSTALL.md).
123
+ </details>
124
+
125
+ <details open>
126
+ <summary><strong>Any other agent</strong></summary>
127
+
128
+ One command copies the skill into most agents' skills folders (Claude Code,
129
+ Codex, OpenCode, Cursor, Windsurf, Cline, Gemini, and more):
130
+
131
+ ```bash
132
+ npx skills add prashanthgit19/homie
133
+ ```
134
+
135
+ Or copy [`AGENTS.md`](AGENTS.md) into your project, or ask your agent to install
136
+ [`skills/homie/SKILL.md`](skills/homie/SKILL.md) as a skill. More in
137
+ [INSTALL.md](INSTALL.md).
138
+
139
+ </details>
108
140
 
109
141
  ## Commands
110
142
 
package/docs/logo.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" shape-rendering="crispEdges" role="img" aria-label="homie logo"><rect x="23" y="11" width="10" height="1" fill="#000000"/><rect x="19" y="12" width="4" height="1" fill="#000000"/><rect x="23" y="12" width="10" height="1" fill="#FFC803"/><rect x="33" y="12" width="4" height="1" fill="#000000"/><rect x="19" y="13" width="4" height="1" fill="#000000"/><rect x="23" y="13" width="10" height="1" fill="#FFC803"/><rect x="33" y="13" width="1" height="1" fill="#FFE104"/><rect x="34" y="13" width="4" height="1" fill="#000000"/><rect x="18" y="14" width="1" height="1" fill="#000000"/><rect x="19" y="14" width="7" height="1" fill="#FFC803"/><rect x="26" y="14" width="11" height="1" fill="#FFE104"/><rect x="37" y="14" width="2" height="1" fill="#000000"/><rect x="15" y="15" width="3" height="1" fill="#000000"/><rect x="18" y="15" width="5" height="1" fill="#FFC803"/><rect x="23" y="15" width="16" height="1" fill="#FFE104"/><rect x="39" y="15" width="2" height="1" fill="#000000"/><rect x="14" y="16" width="1" height="1" fill="#000000"/><rect x="15" y="16" width="6" height="1" fill="#FFC803"/><rect x="21" y="16" width="20" height="1" fill="#FFE104"/><rect x="41" y="16" width="1" height="1" fill="#000000"/><rect x="13" y="17" width="1" height="1" fill="#000000"/><rect x="14" y="17" width="6" height="1" fill="#FFC803"/><rect x="20" y="17" width="22" height="1" fill="#FFE104"/><rect x="42" y="17" width="1" height="1" fill="#000000"/><rect x="13" y="18" width="1" height="1" fill="#000000"/><rect x="14" y="18" width="5" height="1" fill="#FFC803"/><rect x="19" y="18" width="24" height="1" fill="#FFE104"/><rect x="43" y="18" width="1" height="1" fill="#000000"/><rect x="12" y="19" width="1" height="1" fill="#000000"/><rect x="13" y="19" width="5" height="1" fill="#FFC803"/><rect x="18" y="19" width="25" height="1" fill="#FFE104"/><rect x="43" y="19" width="1" height="1" fill="#000000"/><rect x="12" y="20" width="1" height="1" fill="#000000"/><rect x="13" y="20" width="4" height="1" fill="#FFC803"/><rect x="17" y="20" width="27" height="1" fill="#FFE104"/><rect x="44" y="20" width="1" height="1" fill="#000000"/><rect x="11" y="21" width="1" height="1" fill="#000000"/><rect x="12" y="21" width="5" height="1" fill="#FFC803"/><rect x="17" y="21" width="28" height="1" fill="#FFE104"/><rect x="45" y="21" width="1" height="1" fill="#000000"/><rect x="10" y="22" width="1" height="1" fill="#000000"/><rect x="11" y="22" width="5" height="1" fill="#FFC803"/><rect x="16" y="22" width="29" height="1" fill="#FFE104"/><rect x="45" y="22" width="1" height="1" fill="#000000"/><rect x="10" y="23" width="2" height="1" fill="#000000"/><rect x="12" y="23" width="3" height="1" fill="#FFC803"/><rect x="15" y="23" width="30" height="1" fill="#FFE104"/><rect x="45" y="23" width="1" height="1" fill="#000000"/><rect x="9" y="24" width="39" height="1" fill="#000000"/><rect x="9" y="25" width="39" height="1" fill="#000000"/><rect x="9" y="26" width="17" height="1" fill="#000000"/><rect x="26" y="26" width="1" height="1" fill="#FFFFFF"/><rect x="27" y="26" width="17" height="1" fill="#000000"/><rect x="44" y="26" width="1" height="1" fill="#FFFFFF"/><rect x="45" y="26" width="3" height="1" fill="#000000"/><rect x="9" y="27" width="1" height="1" fill="#000000"/><rect x="10" y="27" width="2" height="1" fill="#FFC803"/><rect x="12" y="27" width="11" height="1" fill="#000000"/><rect x="23" y="27" width="1" height="1" fill="#FFFFFF"/><rect x="24" y="27" width="2" height="1" fill="#000000"/><rect x="26" y="27" width="1" height="1" fill="#FFFFFF"/><rect x="27" y="27" width="14" height="1" fill="#000000"/><rect x="41" y="27" width="2" height="1" fill="#FFFFFF"/><rect x="43" y="27" width="1" height="1" fill="#000000"/><rect x="44" y="27" width="1" height="1" fill="#FFFFFF"/><rect x="45" y="27" width="3" height="1" fill="#000000"/><rect x="9" y="28" width="1" height="1" fill="#000000"/><rect x="10" y="28" width="4" height="1" fill="#FFC803"/><rect x="14" y="28" width="8" height="1" fill="#000000"/><rect x="22" y="28" width="1" height="1" fill="#FFFFFF"/><rect x="23" y="28" width="1" height="1" fill="#000000"/><rect x="24" y="28" width="2" height="1" fill="#FFFFFF"/><rect x="26" y="28" width="4" height="1" fill="#000000"/><rect x="30" y="28" width="1" height="1" fill="#FFE104"/><rect x="31" y="28" width="9" height="1" fill="#000000"/><rect x="40" y="28" width="1" height="1" fill="#FFFFFF"/><rect x="41" y="28" width="1" height="1" fill="#000000"/><rect x="42" y="28" width="2" height="1" fill="#FFFFFF"/><rect x="44" y="28" width="4" height="1" fill="#000000"/><rect x="9" y="29" width="1" height="1" fill="#000000"/><rect x="10" y="29" width="4" height="1" fill="#FFC803"/><rect x="14" y="29" width="7" height="1" fill="#000000"/><rect x="21" y="29" width="1" height="1" fill="#FFFFFF"/><rect x="22" y="29" width="1" height="1" fill="#000000"/><rect x="23" y="29" width="1" height="1" fill="#FFFFFF"/><rect x="24" y="29" width="6" height="1" fill="#000000"/><rect x="30" y="29" width="1" height="1" fill="#FFE104"/><rect x="31" y="29" width="8" height="1" fill="#000000"/><rect x="39" y="29" width="1" height="1" fill="#FFFFFF"/><rect x="40" y="29" width="1" height="1" fill="#000000"/><rect x="41" y="29" width="2" height="1" fill="#FFFFFF"/><rect x="43" y="29" width="5" height="1" fill="#000000"/><rect x="9" y="30" width="1" height="1" fill="#000000"/><rect x="10" y="30" width="4" height="1" fill="#FFC803"/><rect x="14" y="30" width="1" height="1" fill="#FFE104"/><rect x="15" y="30" width="14" height="1" fill="#000000"/><rect x="29" y="30" width="3" height="1" fill="#FFE104"/><rect x="32" y="30" width="13" height="1" fill="#000000"/><rect x="45" y="30" width="1" height="1" fill="#FFE104"/><rect x="46" y="30" width="2" height="1" fill="#000000"/><rect x="9" y="31" width="1" height="1" fill="#000000"/><rect x="10" y="31" width="4" height="1" fill="#FFC803"/><rect x="14" y="31" width="2" height="1" fill="#FFE104"/><rect x="16" y="31" width="12" height="1" fill="#000000"/><rect x="28" y="31" width="5" height="1" fill="#FFE104"/><rect x="33" y="31" width="12" height="1" fill="#000000"/><rect x="45" y="31" width="1" height="1" fill="#FFE104"/><rect x="46" y="31" width="2" height="1" fill="#000000"/><rect x="9" y="32" width="1" height="1" fill="#000000"/><rect x="10" y="32" width="4" height="1" fill="#FFC803"/><rect x="14" y="32" width="3" height="1" fill="#FFE104"/><rect x="17" y="32" width="10" height="1" fill="#000000"/><rect x="27" y="32" width="7" height="1" fill="#FFE104"/><rect x="34" y="32" width="10" height="1" fill="#000000"/><rect x="44" y="32" width="2" height="1" fill="#FFE104"/><rect x="46" y="32" width="2" height="1" fill="#000000"/><rect x="9" y="33" width="1" height="1" fill="#000000"/><rect x="10" y="33" width="4" height="1" fill="#FFC803"/><rect x="14" y="33" width="32" height="1" fill="#FFE104"/><rect x="46" y="33" width="2" height="1" fill="#000000"/><rect x="9" y="34" width="1" height="1" fill="#000000"/><rect x="10" y="34" width="5" height="1" fill="#FFC803"/><rect x="15" y="34" width="31" height="1" fill="#FFE104"/><rect x="46" y="34" width="2" height="1" fill="#000000"/><rect x="52" y="34" width="1" height="1" fill="#A1A1A1"/><rect x="10" y="35" width="1" height="1" fill="#000000"/><rect x="11" y="35" width="4" height="1" fill="#FFC803"/><rect x="15" y="35" width="30" height="1" fill="#FFE104"/><rect x="45" y="35" width="2" height="1" fill="#000000"/><rect x="52" y="35" width="1" height="1" fill="#A1A1A1"/><rect x="10" y="36" width="1" height="1" fill="#000000"/><rect x="11" y="36" width="5" height="1" fill="#FFC803"/><rect x="16" y="36" width="29" height="1" fill="#FFE104"/><rect x="45" y="36" width="1" height="1" fill="#000000"/><rect x="52" y="36" width="1" height="1" fill="#A1A1A1"/><rect x="10" y="37" width="1" height="1" fill="#000000"/><rect x="11" y="37" width="6" height="1" fill="#FFC803"/><rect x="17" y="37" width="28" height="1" fill="#FFE104"/><rect x="45" y="37" width="1" height="1" fill="#000000"/><rect x="52" y="37" width="2" height="1" fill="#A1A1A1"/><rect x="10" y="38" width="1" height="1" fill="#000000"/><rect x="11" y="38" width="6" height="1" fill="#FFC803"/><rect x="17" y="38" width="28" height="1" fill="#FFE104"/><rect x="45" y="38" width="1" height="1" fill="#000000"/><rect x="53" y="38" width="1" height="1" fill="#A1A1A1"/><rect x="11" y="39" width="2" height="1" fill="#000000"/><rect x="13" y="39" width="5" height="1" fill="#FFC803"/><rect x="18" y="39" width="17" height="1" fill="#FFE104"/><rect x="35" y="39" width="2" height="1" fill="#000000"/><rect x="37" y="39" width="7" height="1" fill="#FFE104"/><rect x="44" y="39" width="1" height="1" fill="#000000"/><rect x="53" y="39" width="1" height="1" fill="#A1A1A1"/><rect x="12" y="40" width="1" height="1" fill="#000000"/><rect x="13" y="40" width="5" height="1" fill="#FFC803"/><rect x="18" y="40" width="17" height="1" fill="#FFE104"/><rect x="35" y="40" width="3" height="1" fill="#000000"/><rect x="38" y="40" width="6" height="1" fill="#FFE104"/><rect x="44" y="40" width="1" height="1" fill="#000000"/><rect x="53" y="40" width="1" height="1" fill="#A1A1A1"/><rect x="13" y="41" width="1" height="1" fill="#000000"/><rect x="14" y="41" width="5" height="1" fill="#FFC803"/><rect x="19" y="41" width="15" height="1" fill="#FFE104"/><rect x="34" y="41" width="1" height="1" fill="#000000"/><rect x="35" y="41" width="2" height="1" fill="#FFFFFF"/><rect x="37" y="41" width="2" height="1" fill="#000000"/><rect x="39" y="41" width="4" height="1" fill="#FFE104"/><rect x="43" y="41" width="1" height="1" fill="#000000"/><rect x="49" y="41" width="1" height="1" fill="#A1A1A1"/><rect x="53" y="41" width="1" height="1" fill="#A1A1A1"/><rect x="13" y="42" width="1" height="1" fill="#000000"/><rect x="14" y="42" width="7" height="1" fill="#FFC803"/><rect x="21" y="42" width="12" height="1" fill="#FFE104"/><rect x="33" y="42" width="2" height="1" fill="#000000"/><rect x="35" y="42" width="4" height="1" fill="#FFFFFF"/><rect x="39" y="42" width="2" height="1" fill="#000000"/><rect x="41" y="42" width="2" height="1" fill="#FFE104"/><rect x="43" y="42" width="1" height="1" fill="#000000"/><rect x="49" y="42" width="1" height="1" fill="#A1A1A1"/><rect x="14" y="43" width="1" height="1" fill="#000000"/><rect x="15" y="43" width="8" height="1" fill="#FFC803"/><rect x="23" y="43" width="12" height="1" fill="#FFE104"/><rect x="35" y="43" width="1" height="1" fill="#000000"/><rect x="36" y="43" width="1" height="1" fill="#A1A1A1"/><rect x="37" y="43" width="4" height="1" fill="#FFFFFF"/><rect x="41" y="43" width="2" height="1" fill="#000000"/><rect x="49" y="43" width="2" height="1" fill="#A1A1A1"/><rect x="53" y="43" width="1" height="1" fill="#A1A1A1"/><rect x="15" y="44" width="3" height="1" fill="#000000"/><rect x="18" y="44" width="7" height="1" fill="#FFC803"/><rect x="25" y="44" width="10" height="1" fill="#FFE104"/><rect x="35" y="44" width="1" height="1" fill="#FFC803"/><rect x="36" y="44" width="2" height="1" fill="#A1A1A1"/><rect x="38" y="44" width="5" height="1" fill="#FFFFFF"/><rect x="43" y="44" width="2" height="1" fill="#000000"/><rect x="50" y="44" width="1" height="1" fill="#A1A1A1"/><rect x="53" y="44" width="1" height="1" fill="#A1A1A1"/><rect x="16" y="45" width="2" height="1" fill="#000000"/><rect x="18" y="45" width="8" height="1" fill="#FFC803"/><rect x="26" y="45" width="9" height="1" fill="#FFE104"/><rect x="35" y="45" width="1" height="1" fill="#FFC803"/><rect x="36" y="45" width="1" height="1" fill="#000000"/><rect x="37" y="45" width="2" height="1" fill="#A1A1A1"/><rect x="39" y="45" width="4" height="1" fill="#FFFFFF"/><rect x="43" y="45" width="2" height="1" fill="#000000"/><rect x="50" y="45" width="1" height="1" fill="#A1A1A1"/><rect x="53" y="45" width="1" height="1" fill="#A1A1A1"/><rect x="18" y="46" width="1" height="1" fill="#000000"/><rect x="19" y="46" width="18" height="1" fill="#FFC803"/><rect x="37" y="46" width="2" height="1" fill="#000000"/><rect x="39" y="46" width="1" height="1" fill="#A1A1A1"/><rect x="40" y="46" width="5" height="1" fill="#FFFFFF"/><rect x="45" y="46" width="3" height="1" fill="#000000"/><rect x="52" y="46" width="2" height="1" fill="#A1A1A1"/><rect x="19" y="47" width="4" height="1" fill="#000000"/><rect x="23" y="47" width="11" height="1" fill="#FFC803"/><rect x="34" y="47" width="3" height="1" fill="#000000"/><rect x="39" y="47" width="1" height="1" fill="#000000"/><rect x="40" y="47" width="2" height="1" fill="#A1A1A1"/><rect x="42" y="47" width="4" height="1" fill="#FFFFFF"/><rect x="46" y="47" width="2" height="1" fill="#A1A1A1"/><rect x="48" y="47" width="1" height="1" fill="#000000"/><rect x="52" y="47" width="1" height="1" fill="#A1A1A1"/><rect x="23" y="48" width="11" height="1" fill="#000000"/><rect x="40" y="48" width="2" height="1" fill="#000000"/><rect x="42" y="48" width="2" height="1" fill="#A1A1A1"/><rect x="44" y="48" width="1" height="1" fill="#FFFFFF"/><rect x="45" y="48" width="2" height="1" fill="#A1A1A1"/><rect x="47" y="48" width="2" height="1" fill="#CD2027"/><rect x="49" y="48" width="1" height="1" fill="#000000"/><rect x="52" y="48" width="1" height="1" fill="#A1A1A1"/><rect x="24" y="49" width="9" height="1" fill="#000000"/><rect x="40" y="49" width="3" height="1" fill="#000000"/><rect x="43" y="49" width="1" height="1" fill="#A1A1A1"/><rect x="44" y="49" width="1" height="1" fill="#FFFFFF"/><rect x="45" y="49" width="1" height="1" fill="#A1A1A1"/><rect x="46" y="49" width="3" height="1" fill="#CD2027"/><rect x="49" y="49" width="1" height="1" fill="#000000"/><rect x="43" y="50" width="1" height="1" fill="#000000"/><rect x="44" y="50" width="1" height="1" fill="#A1A1A1"/><rect x="45" y="50" width="4" height="1" fill="#CD2027"/><rect x="49" y="50" width="1" height="1" fill="#000000"/><rect x="44" y="51" width="5" height="1" fill="#000000"/></svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kpnpm/homie",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Personality layer for coding agents. Same code, different voice: yo / dawg / mafa / off.",
5
5
  "keywords": [
6
6
  "opencode-plugin",
@@ -40,6 +40,8 @@
40
40
  "pi-extension/",
41
41
  "!pi-extension/test/",
42
42
  "scripts/tone-check.js",
43
+ "scripts/tone-run.sh",
44
+ "docs/logo.svg",
43
45
  "LICENSE"
44
46
  ],
45
47
  "pi": {
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env bash
2
+ # homie — tone-delta realism driver.
3
+ #
4
+ # Runs the same prompts through a live agent at each level (off/yo/dawg/mafa),
5
+ # collects the answers, and hands them to scripts/tone-check.js for scoring.
6
+ #
7
+ # Usage:
8
+ # scripts/tone-run.sh [--model provider/model] [--host opencode] [--out samples.json] [prompt ...]
9
+ #
10
+ # Requires a logged-in host CLI. Defaults: opencode, the host's default model.
11
+ # With no prompts, a built-in set of opinion-eliciting coding questions is used.
12
+
13
+ set -euo pipefail
14
+
15
+ MODEL=""
16
+ HOST="opencode"
17
+ OUT="samples.json"
18
+ PROMPTS=()
19
+
20
+ while [ $# -gt 0 ]; do
21
+ case "$1" in
22
+ --model) MODEL="$2"; shift 2 ;;
23
+ --host) HOST="$2"; shift 2 ;;
24
+ --out) OUT="$2"; shift 2 ;;
25
+ *) PROMPTS+=("$1"); shift ;;
26
+ esac
27
+ done
28
+
29
+ if [ "${#PROMPTS[@]}" -eq 0 ]; then
30
+ PROMPTS=(
31
+ "Should I increase top_k from 5 to 50 for better retrieval? Answer in 3-4 sentences. Do not use any tools."
32
+ "I keep user sessions in one Postgres table with 40 columns. Is that fine? Answer in 3-4 sentences. Do not use any tools."
33
+ "My CI takes 25 minutes, so I'm adding more parallel runners. Good plan? Answer in 3-4 sentences. Do not use any tools."
34
+ )
35
+ fi
36
+
37
+ # Where the host reads the active level. OpenCode keeps it beside its config.
38
+ FLAG="${XDG_CONFIG_HOME:-$HOME/.config}/opencode/.homie-active"
39
+
40
+ if [ "$HOST" != "opencode" ]; then
41
+ echo "tone-run: only the opencode host is wired up so far" >&2
42
+ exit 2
43
+ fi
44
+
45
+ mkdir -p "$(dirname "$FLAG")"
46
+ TMP="$(mktemp -d)"
47
+ trap 'rm -rf "$TMP"' EXIT
48
+
49
+ # Strip ANSI, drop opencode's "> build · model" banner and blank lines.
50
+ clean() {
51
+ sed -e 's/\x1b\[[0-9;]*m//g' -e '/^> /d' | sed -e '/^[[:space:]]*$/d'
52
+ }
53
+
54
+ for level in off yo dawg mafa; do
55
+ printf '%s' "$level" > "$FLAG"
56
+ : > "$TMP/$level.txt"
57
+ echo "tone-run: level=$level" >&2
58
+ for p in "${PROMPTS[@]}"; do
59
+ if [ -n "$MODEL" ]; then
60
+ opencode run -m "$MODEL" "$p" 2>/dev/null | clean >> "$TMP/$level.txt"
61
+ else
62
+ opencode run "$p" 2>/dev/null | clean >> "$TMP/$level.txt"
63
+ fi
64
+ printf '\n\n' >> "$TMP/$level.txt"
65
+ done
66
+ done
67
+
68
+ # Restore the flag to off so the run does not leave the user in a voice.
69
+ printf 'off' > "$FLAG"
70
+
71
+ # Assemble samples.json without requiring node to be invoked twice.
72
+ node - "$TMP" "$OUT" <<'NODE'
73
+ const fs = require('fs');
74
+ const path = require('path');
75
+ const [tmp, out] = process.argv.slice(2);
76
+ const data = {};
77
+ for (const level of ['off', 'yo', 'dawg', 'mafa']) {
78
+ const text = fs.readFileSync(path.join(tmp, level + '.txt'), 'utf8');
79
+ data[level] = text.split(/\n{2,}/).map((s) => s.trim()).filter(Boolean);
80
+ }
81
+ fs.writeFileSync(out, JSON.stringify(data, null, 2));
82
+ console.log('tone-run: wrote ' + out);
83
+ NODE
84
+
85
+ node "$(dirname "$0")/tone-check.js" "$OUT"