@xynogen/pix-optimizer 1.1.4 → 1.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-optimizer",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Performance optimization suite for Pi Coding Agent - caveman mode + RTK tool rewriting + jq/TOON JSON compression + ponytail lazy-dev mode",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -47,6 +47,9 @@
47
47
  "publishConfig": {
48
48
  "access": "public"
49
49
  },
50
+ "dependencies": {
51
+ "@xynogen/pix-pretty": "*"
52
+ },
50
53
  "peerDependencies": {
51
54
  "@earendil-works/pi-coding-agent": "*",
52
55
  "@earendil-works/pi-tui": "*"
@@ -2,11 +2,9 @@ import { describe, expect, it } from "bun:test";
2
2
  import {
3
3
  buildHelp,
4
4
  buildPrompt,
5
- DEFAULT_CONFIG,
6
5
  LEVEL_NUMBERS,
7
6
  LEVELS,
8
7
  type Level,
9
- parseConfig,
10
8
  resolveLevel,
11
9
  STATUS_LABELS,
12
10
  STOP_ALIASES,
@@ -203,38 +201,3 @@ describe("buildPrompt", () => {
203
201
  expect(lite).not.toContain("Abbreviate");
204
202
  });
205
203
  });
206
-
207
- // ── parseConfig ───────────────────────────────────────────────────────────────
208
-
209
- describe("parseConfig", () => {
210
- it("parses valid config", () => {
211
- const cfg = parseConfig({ defaultLevel: "ultra", showStatus: false });
212
- expect(cfg.defaultLevel).toBe("ultra");
213
- expect(cfg.showStatus).toBe(false);
214
- });
215
-
216
- it("falls back to defaults for invalid defaultLevel", () => {
217
- const cfg = parseConfig({ defaultLevel: "invalid", showStatus: true });
218
- expect(cfg.defaultLevel).toBe(DEFAULT_CONFIG.defaultLevel);
219
- });
220
-
221
- it("falls back to defaults for missing fields", () => {
222
- const cfg = parseConfig({});
223
- expect(cfg.defaultLevel).toBe(DEFAULT_CONFIG.defaultLevel);
224
- expect(cfg.showStatus).toBe(DEFAULT_CONFIG.showStatus);
225
- });
226
-
227
- it("falls back to defaults for null/undefined", () => {
228
- const cfg = parseConfig(null);
229
- expect(cfg.defaultLevel).toBe(DEFAULT_CONFIG.defaultLevel);
230
- expect(cfg.showStatus).toBe(DEFAULT_CONFIG.showStatus);
231
- });
232
-
233
- it("accepts all valid levels as defaultLevel", () => {
234
- for (const l of LEVELS) {
235
- expect(
236
- parseConfig({ defaultLevel: l, showStatus: true }).defaultLevel,
237
- ).toBe(l);
238
- }
239
- });
240
- });
package/src/caveman.ts CHANGED
@@ -5,21 +5,11 @@
5
5
  * called by index.ts alongside rtk(pi).
6
6
  */
7
7
 
8
- import { mkdir, readFile, writeFile } from "node:fs/promises";
9
- import { homedir } from "node:os";
10
- import { join } from "node:path";
11
8
  import type {
12
9
  ExtensionAPI,
13
10
  ExtensionCommandContext,
14
11
  ExtensionContext,
15
12
  } from "@earendil-works/pi-coding-agent";
16
- import { getSettingsListTheme } from "@earendil-works/pi-coding-agent";
17
- import {
18
- Container,
19
- type SettingItem,
20
- SettingsList,
21
- Text,
22
- } from "@earendil-works/pi-tui";
23
13
  import type { OptimizerHandle, OptimizerStatus } from "./status.ts";
24
14
 
25
15
  // ── Levels ────────────────────────────────────────────────────────────────────
@@ -37,24 +27,6 @@ export const LEVEL_NUMBERS: Record<string, Level> = {
37
27
  "3": "ultra",
38
28
  };
39
29
 
40
- const CAVEMAN_COMMAND_OPTIONS = [
41
- { value: "1", label: "1 (lite)", description: "Professional, no fluff" },
42
- { value: "2", label: "2 (full)", description: "Classic caveman" },
43
- { value: "3", label: "3 (ultra)", description: "Maximum compression" },
44
- { value: "lite", label: "lite", description: "Professional, no fluff" },
45
- { value: "full", label: "full", description: "Classic caveman" },
46
- { value: "ultra", label: "ultra", description: "Maximum compression" },
47
- {
48
- value: "micro",
49
- label: "micro",
50
- description: "Experimental prompt-minimized mode",
51
- },
52
- { value: "off", label: "off", description: "Disable caveman mode" },
53
- { value: "stop", label: "stop", description: "Disable caveman mode" },
54
- { value: "quit", label: "quit", description: "Disable caveman mode" },
55
- { value: "config", label: "config", description: "Open settings dialog" },
56
- ] as const;
57
-
58
30
  // ── Status labels ─────────────────────────────────────────────────────────────
59
31
 
60
32
  export const STATUS_LABELS: Record<Exclude<Level, "off">, string> = {
@@ -158,55 +130,6 @@ export function toggleLevel(current: Level): Level {
158
130
  return current === "off" ? "full" : "off";
159
131
  }
160
132
 
161
- // ── Config ────────────────────────────────────────────────────────────────────
162
-
163
- export interface CavemanConfig {
164
- /** Level to apply on new sessions. "off" means don't auto-enable. */
165
- defaultLevel: Level;
166
- /** Whether to show the status bar indicator. */
167
- showStatus: boolean;
168
- }
169
-
170
- export const DEFAULT_CONFIG: CavemanConfig = {
171
- defaultLevel: "full",
172
- showStatus: true,
173
- };
174
-
175
- const CONFIG_PATH = join(homedir(), ".pi", "agent", "caveman.json");
176
-
177
- export function parseConfig(raw: unknown): CavemanConfig {
178
- const parsed = raw as Record<string, unknown>;
179
- return {
180
- defaultLevel: LEVELS.includes(parsed?.defaultLevel as Level)
181
- ? (parsed.defaultLevel as Level)
182
- : DEFAULT_CONFIG.defaultLevel,
183
- showStatus:
184
- typeof parsed?.showStatus === "boolean"
185
- ? parsed.showStatus
186
- : DEFAULT_CONFIG.showStatus,
187
- };
188
- }
189
-
190
- let _saveQueue: Promise<void> = Promise.resolve();
191
-
192
- async function loadConfig(): Promise<CavemanConfig> {
193
- try {
194
- const raw = await readFile(CONFIG_PATH, "utf8");
195
- return parseConfig(JSON.parse(raw));
196
- } catch {
197
- return { ...DEFAULT_CONFIG };
198
- }
199
- }
200
-
201
- async function saveConfig(config: CavemanConfig): Promise<void> {
202
- const snapshot = `${JSON.stringify(config, null, 2)}\n`;
203
- _saveQueue = _saveQueue.then(async () => {
204
- await mkdir(join(homedir(), ".pi", "agent"), { recursive: true });
205
- await writeFile(CONFIG_PATH, snapshot, "utf8");
206
- });
207
- return _saveQueue;
208
- }
209
-
210
133
  // ── Pi extension ────────────────────────────────────────────────────────────
211
134
 
212
135
  export function caveman(
@@ -214,26 +137,11 @@ export function caveman(
214
137
  status: OptimizerStatus,
215
138
  ): OptimizerHandle {
216
139
  let level: Level = "off";
217
- let config: CavemanConfig = { ...DEFAULT_CONFIG };
218
- let configLoadPromise: Promise<void> | null = null;
219
-
220
- const ensureConfigLoaded = async () => {
221
- if (!configLoadPromise) {
222
- configLoadPromise = (async () => {
223
- config = await loadConfig();
224
- if (level === "off" && config.defaultLevel !== "off") {
225
- level = config.defaultLevel;
226
- }
227
- })();
228
- }
229
- await configLoadPromise;
230
- };
231
140
 
232
141
  // -- Status: report into the shared optimizer indicator. --
233
142
 
234
143
  function syncStatus(ctx: Pick<ExtensionContext, "ui">) {
235
- const on = level !== "off" && config.showStatus;
236
- status.set("caveman", on, ctx);
144
+ status.set("caveman", level !== "off", ctx);
237
145
  }
238
146
 
239
147
  // Inject caveman prompt via before_agent_start
@@ -244,25 +152,14 @@ export function caveman(
244
152
  return { systemPrompt: `${prompt}\n\n${existing}` };
245
153
  });
246
154
 
247
- // -- Restore state on session load --
155
+ // -- Restore live level from the session log on load --
248
156
 
249
157
  pi.on("session_start", async (_event, ctx) => {
250
- await ensureConfigLoaded();
251
-
252
- let sessionLevel: Level | null = null;
253
158
  for (const entry of ctx.sessionManager.getEntries()) {
254
159
  if (entry.type === "custom" && entry.customType === "caveman-level") {
255
- sessionLevel = (entry.data as { level: Level })?.level ?? null;
160
+ level = (entry.data as { level: Level })?.level ?? level;
256
161
  }
257
162
  }
258
-
259
- if (sessionLevel !== null) {
260
- level = sessionLevel;
261
- } else if (config.defaultLevel !== "off") {
262
- level = config.defaultLevel;
263
- pi.appendEntry("caveman-level", { level });
264
- }
265
-
266
163
  syncStatus(ctx);
267
164
  });
268
165
 
@@ -274,33 +171,14 @@ export function caveman(
274
171
  });
275
172
  pi.on("session_shutdown", async () => {});
276
173
 
277
- // -- Subcommand handler (dispatched by the merged /opt router) --
174
+ // -- Overlay value handler (called by the /optimizer overlay) --
278
175
 
279
176
  async function run(
280
- args: string,
177
+ value: string,
281
178
  ctx: ExtensionCommandContext,
282
179
  ): Promise<void> {
283
- const arg = args.trim().toLowerCase();
284
-
285
- // No argument → show help
286
- if (!arg) {
287
- ctx.ui.notify(buildHelp(level), "info");
288
- return;
289
- }
290
-
291
- if (arg === "config") {
292
- await openConfig(ctx);
293
- return;
294
- }
295
-
296
- const resolved = resolveLevel(arg);
297
- if (resolved === null) {
298
- ctx.ui.notify(
299
- `Unknown: "${arg}". Use 1/2/3, ${LEVELS.join(", ")}, stop, quit, or config`,
300
- "error",
301
- );
302
- return;
303
- }
180
+ const resolved = resolveLevel(value);
181
+ if (resolved === null) return;
304
182
  level = resolved;
305
183
 
306
184
  pi.appendEntry("caveman-level", { level });
@@ -314,131 +192,11 @@ export function caveman(
314
192
  );
315
193
  }
316
194
 
317
- function complete(prefix: string) {
318
- const normalized = prefix.trim().toLowerCase();
319
- const items = CAVEMAN_COMMAND_OPTIONS.filter((item) =>
320
- item.value.startsWith(normalized),
321
- );
322
- return items.length > 0 ? items.map((i) => ({ ...i })) : null;
323
- }
324
-
325
- // -- /caveman config: interactive SettingsList --
326
-
327
- async function openConfig(ctx: ExtensionContext) {
328
- await ensureConfigLoaded();
329
-
330
- await ctx.ui.custom((_tui, theme, _kb, done) => {
331
- const items: SettingItem[] = [
332
- {
333
- id: "defaultLevel",
334
- label: "Default level for new sessions",
335
- currentValue: config.defaultLevel,
336
- values: [...LEVELS],
337
- },
338
- {
339
- id: "showStatus",
340
- label: "Show status bar",
341
- currentValue: config.showStatus ? "on" : "off",
342
- values: ["on", "off"],
343
- },
344
- ];
345
-
346
- const container = new Container();
347
- container.addChild(
348
- new Text(theme.fg("accent", theme.bold(" Caveman Config")), 0, 0),
349
- );
350
- container.addChild(
351
- new Text(theme.fg("dim", " Saved to ~/.pi/agent/caveman.json"), 0, 0),
352
- );
353
- container.addChild(
354
- new Text(
355
- theme.fg("dim", " Default level applies to future sessions."),
356
- 0,
357
- 0,
358
- ),
359
- );
360
- container.addChild(new Text("", 0, 0));
361
-
362
- const applySettingChange = (id: string, newValue: string) => {
363
- if (id === "defaultLevel" && LEVELS.includes(newValue as Level)) {
364
- config.defaultLevel = newValue as Level;
365
- } else if (id === "showStatus") {
366
- config.showStatus = newValue === "on";
367
- }
368
- saveConfig(config);
369
- syncStatus(ctx);
370
- };
371
-
372
- const settingsList = new SettingsList(
373
- items,
374
- Math.min(items.length + 2, 10),
375
- getSettingsListTheme(),
376
- applySettingChange,
377
- () => done(undefined),
378
- );
379
-
380
- container.addChild(settingsList);
381
- container.addChild(
382
- new Text(
383
- theme.fg("dim", " ←→/hl/tab change • ↑↓/jk move • esc close"),
384
- 0,
385
- 0,
386
- ),
387
- );
388
-
389
- const cycleSelectedValue = (direction: -1 | 1) => {
390
- const selectedIndex = (
391
- settingsList as unknown as { selectedIndex: number }
392
- ).selectedIndex;
393
- const item = items[selectedIndex];
394
- if (!item?.values?.length) return;
395
- const currentIndex = item.values.indexOf(item.currentValue);
396
- const nextIndex =
397
- (currentIndex + direction + item.values.length) % item.values.length;
398
- const newValue = item.values[nextIndex]!;
399
- item.currentValue = newValue;
400
- settingsList.updateValue(item.id, newValue);
401
- applySettingChange(item.id, newValue);
402
- };
403
-
404
- return {
405
- render: (w: number) => container.render(w),
406
- invalidate: () => container.invalidate(),
407
- handleInput: (data: string) => {
408
- if (data === "j") data = "\u001b[B";
409
- else if (data === "k") data = "\u001b[A";
410
- else if (data === "h") {
411
- cycleSelectedValue(-1);
412
- _tui.requestRender();
413
- return;
414
- } else if (data === "l" || data === "\u001b[C" || data === "\t") {
415
- cycleSelectedValue(1);
416
- _tui.requestRender();
417
- return;
418
- } else if (data === "\u001b[D") {
419
- cycleSelectedValue(-1);
420
- _tui.requestRender();
421
- return;
422
- } else if (data === "\u001b" || data === "q") {
423
- done(undefined);
424
- return;
425
- }
426
-
427
- // SettingsList.handleInput now returns void; completion/cancel is
428
- // signalled through the onChange/onCancel callbacks passed to the
429
- // constructor (onCancel -> done(undefined)). Just forward input and
430
- // re-render.
431
- settingsList.handleInput(data);
432
- _tui.requestRender();
433
- },
434
- };
435
- });
436
- }
437
-
438
195
  return {
439
196
  name: "caveman",
440
- help: "caveman <1|2|3|lite|full|ultra|micro|off|config> — terse output",
197
+ help: "caveman — terse output",
198
+ values: LEVELS,
199
+ current: () => level,
441
200
  run,
442
- complete,
443
201
  };
444
202
  }
package/src/index.ts CHANGED
@@ -8,8 +8,8 @@
8
8
  * - ponytail: lazy-senior-dev system prompt (minimal code, YAGNI)
9
9
  *
10
10
  * They share ONE status-bar cell (󰜐 󰓥 󰗀 󰆐, only enabled tools shown) and ONE
11
- * command (/opt <tool> [args]). index.ts wires lifecycle hooks via each
12
- * module, then registers the merged command from their handles.
11
+ * command (/optimizer — an interactive overlay). index.ts wires lifecycle hooks
12
+ * via each module, then registers the overlay command from their handles.
13
13
  */
14
14
 
15
15
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
@@ -29,7 +29,7 @@ export default function optimizer(pi: ExtensionAPI) {
29
29
  const status = new OptimizerStatus();
30
30
 
31
31
  // Each module registers its own lifecycle hooks and returns a handle the
32
- // merged /opt command dispatches to.
32
+ // /optimizer overlay renders + drives.
33
33
  const handles: Record<OptimizerTool, OptimizerHandle> = {
34
34
  caveman: caveman(pi, status),
35
35
  rtk: rtk(pi, status),
@@ -37,7 +37,7 @@ export default function optimizer(pi: ExtensionAPI) {
37
37
  ponytail: ponytail(pi, status),
38
38
  };
39
39
 
40
- registerOptCommand(pi, handles);
40
+ registerOptCommand(pi, handles, status);
41
41
 
42
42
  // Strip model-guidance warnings injected by pi-lens into tool_result content.
43
43
  // These strings (BLIND WRITE, THRASHING) are directives for the LLM, not
package/src/json.ts CHANGED
@@ -270,41 +270,23 @@ export function json(
270
270
  return { systemPrompt: `${JSON_SYSTEM_PROMPT}\n\n${existing}` };
271
271
  });
272
272
 
273
- // -- Subcommand handler (dispatched by the merged /opt router) --
274
-
275
- function complete(prefix: string) {
276
- const items = [
277
- { value: "on", label: "on", description: "Force JSON/TOON guidance on" },
278
- {
279
- value: "off",
280
- label: "off",
281
- description: "Force JSON/TOON guidance off",
282
- },
283
- ];
284
- const n = prefix.trim().toLowerCase();
285
- const filtered = items.filter((i) => i.value.startsWith(n));
286
- return filtered.length > 0 ? filtered : null;
287
- }
273
+ // -- Overlay value handler (called by the /optimizer overlay) --
288
274
 
289
275
  async function run(
290
- args: string,
276
+ value: string,
291
277
  ctx: ExtensionCommandContext,
292
278
  ): Promise<void> {
293
- {
294
- const arg = args.trim().toLowerCase();
295
- if (arg === "on") enabled = true;
296
- else if (arg === "off") enabled = false;
297
- else enabled = !enabled; // bare toggles
279
+ enabled = value === "on";
298
280
 
299
- syncStatus(ctx);
300
- ctx.ui.notify(`JSON/TOON guidance ${enabled ? "on" : "off"}.`, "info");
301
- }
281
+ syncStatus(ctx);
282
+ ctx.ui.notify(`JSON/TOON guidance ${enabled ? "on" : "off"}.`, "info");
302
283
  }
303
284
 
304
285
  return {
305
286
  name: "toon",
306
- help: "toon [on|off] — jq+TOON guidance for dense JSON",
287
+ help: "toon — jq+TOON guidance for dense JSON",
288
+ values: ["off", "on"],
289
+ current: () => (enabled ? "on" : "off"),
307
290
  run,
308
- complete,
309
291
  };
310
292
  }
package/src/opt.test.ts CHANGED
@@ -1,103 +1,60 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { buildOptHelp, completeInvocation, parseInvocation } from "./opt.ts";
2
+ import { buildOptHelp, levelBar } from "./opt.ts";
3
3
  import type { OptimizerHandle, OptimizerTool } from "./status.ts";
4
4
 
5
- /** Build a handle set with spy-able run/complete. */
5
+ /** Build a handle set with fixed current values + value lists. */
6
6
  function fakeHandles(): Record<OptimizerTool, OptimizerHandle> {
7
- const mk = (name: OptimizerTool): OptimizerHandle => ({
7
+ const mk = (
8
+ name: OptimizerTool,
9
+ current: string,
10
+ values: string[],
11
+ ): OptimizerHandle => ({
8
12
  name,
9
- help: `${name} help`,
13
+ help: `${name} — ${name} help`,
14
+ values,
15
+ current: () => current,
10
16
  run: () => {},
11
- complete: (prefix: string) =>
12
- ["on", "off"]
13
- .filter((v) => v.startsWith(prefix.trim().toLowerCase()))
14
- .map((v) => ({ value: v, label: v, description: v })),
15
17
  });
16
18
  return {
17
- caveman: mk("caveman"),
18
- rtk: mk("rtk"),
19
- toon: mk("toon"),
20
- ponytail: mk("ponytail"),
19
+ caveman: mk("caveman", "full", ["off", "lite", "full", "ultra", "micro"]),
20
+ rtk: mk("rtk", "on", ["off", "on"]),
21
+ toon: mk("toon", "off", ["off", "on"]),
22
+ ponytail: mk("ponytail", "off", ["off", "lite", "full", "ultra"]),
21
23
  };
22
24
  }
23
25
 
24
- describe("parseInvocation", () => {
25
- it("empty args → empty name", () => {
26
- expect(parseInvocation("")).toEqual({ name: "", rest: "" });
27
- expect(parseInvocation(" ")).toEqual({ name: "", rest: "" });
28
- });
26
+ describe("levelBar", () => {
27
+ const PONYTAIL = ["off", "lite", "full", "ultra"];
28
+ const BINARY = ["off", "on"];
29
29
 
30
- it("single token → name only", () => {
31
- expect(parseInvocation("rtk")).toEqual({ name: "rtk", rest: "" });
32
- expect(parseInvocation(" RTK ")).toEqual({ name: "rtk", rest: "" });
30
+ it("fills segments by value position in a multi-step ladder", () => {
31
+ expect(levelBar("off", PONYTAIL)).toBe("▱▱▱");
32
+ expect(levelBar("lite", PONYTAIL)).toBe("▰▱▱");
33
+ expect(levelBar("full", PONYTAIL)).toBe("▰▰▱");
34
+ expect(levelBar("ultra", PONYTAIL)).toBe("▰▰▰");
33
35
  });
34
36
 
35
- it("splits name from rest", () => {
36
- expect(parseInvocation("caveman ultra")).toEqual({
37
- name: "caveman",
38
- rest: "ultra",
39
- });
40
- expect(parseInvocation("rtk on extra")).toEqual({
41
- name: "rtk",
42
- rest: "on extra",
43
- });
37
+ it("renders a single segment for binary tools", () => {
38
+ expect(levelBar("off", BINARY)).toBe("▱");
39
+ expect(levelBar("on", BINARY)).toBe("▰");
44
40
  });
45
- });
46
41
 
47
- describe("completeInvocation", () => {
48
- it("completes tool names before any space", () => {
49
- const handles = fakeHandles();
50
- const out = completeInvocation("r", handles);
51
- expect(out?.map((i) => i.value)).toEqual(["rtk"]);
42
+ it("treats an unknown value as off (no fill)", () => {
43
+ expect(levelBar("???", PONYTAIL)).toBe("▱▱▱");
52
44
  });
53
45
 
54
- it("returns all tool names for empty prefix", () => {
55
- const handles = fakeHandles();
56
- const out = completeInvocation("", handles);
57
- expect(out?.map((i) => i.value)).toEqual([
58
- "caveman",
59
- "rtk",
60
- "toon",
61
- "ponytail",
62
- ]);
63
- });
64
-
65
- it("delegates to the tool completer after the name", () => {
66
- const handles = fakeHandles();
67
- const out = completeInvocation("rtk o", handles);
68
- expect(out?.map((i) => i.value)).toEqual(["on", "off"]);
69
- });
70
-
71
- it("null for unknown tool", () => {
72
- const handles = fakeHandles();
73
- expect(completeInvocation("nope x", handles)).toBeNull();
46
+ it("returns empty string when there are no non-off steps", () => {
47
+ expect(levelBar("off", ["off"])).toBe("");
74
48
  });
75
49
  });
76
50
 
77
51
  describe("buildOptHelp", () => {
78
- it("lists every tool's help line", () => {
52
+ it("lists every tool with its current value", () => {
79
53
  const help = buildOptHelp(fakeHandles());
80
- expect(help).toContain("/opt caveman help");
81
- expect(help).toContain("/opt rtk help");
82
- expect(help).toContain("/opt toon help");
83
- expect(help).toContain("/opt ponytail help");
84
- expect(help).toContain("Usage: /opt <tool> [args]");
85
- });
86
- });
87
-
88
- describe("dispatch via run", () => {
89
- it("routes rest to the matching handle", () => {
90
- const handles = fakeHandles();
91
- let received: string | null = null;
92
- handles.rtk.run = (args: string) => {
93
- received = args;
94
- };
95
- const { name, rest } = parseInvocation("rtk off");
96
- expect(name).toBe("rtk");
97
- void handles[name as OptimizerTool].run(rest, {} as never);
98
- // `received` is mutated inside the run() callback above; TS narrows it to
99
- // `null` here since it can't see the side effect, so read it through an
100
- // untyped cast before asserting.
101
- expect(received as string | null).toBe("off");
54
+ expect(help).toContain("caveman: full");
55
+ expect(help).toContain("rtk: on");
56
+ expect(help).toContain("toon: off");
57
+ expect(help).toContain("ponytail: off");
58
+ expect(help).toContain("caveman help");
102
59
  });
103
60
  });