@robhowley/spinner-verbs 1.0.1 → 2.1.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
@@ -30,7 +30,7 @@ Or directly from GitHub:
30
30
  pi install git:github.com/robhowley/spinner-verbs
31
31
  ```
32
32
 
33
- That's it. The extension registers automatically and picks up your configuration on the next session start.
33
+ That's it. The extension registers automatically and on the next session start will randomly pick a theme for you.
34
34
 
35
35
  ### Switching Themes with `/verbs`
36
36
 
@@ -39,16 +39,25 @@ Change your verb list at any time during a session:
39
39
  ```
40
40
  /verbs game-of-thrones
41
41
  /verbs doc-emrick
42
+ /verbs random
42
43
  /verbs (default)
43
44
  ```
44
45
 
45
- Run `/verbs` with no argument to get an interactive picker. Use `(default)` to restore Claude's built-in spinner.
46
+ Run `/verbs` with no argument to get an interactive picker. Use `random` to pick a new random theme, or `(default)` to restore Claude's built-in spinner.
46
47
 
47
48
  ### Auto-configure via `settings.json`
48
49
 
49
50
  Set your preferred theme once and forget about it. Pi checks both project-local (`.pi/settings.json`) and global (`~/.pi/agent/settings.json`) settings on session start.
50
51
 
51
- **Use a built-in theme by name:**
52
+ **Randomly pick a theme each session (default):**
53
+
54
+ ```json
55
+ {
56
+ "spinnerVerbs": "random"
57
+ }
58
+ ```
59
+
60
+ **Use a specific built-in theme by name:**
52
61
 
53
62
  ```json
54
63
  {
@@ -142,3 +151,5 @@ Add the spinner verbs configuration:
142
151
  | `game-show` | Come on down..., Survey says..., Is that your final answer... |
143
152
  | `doc-emrick` | Shunting..., Sliding..., Fiddling... |
144
153
  | `momentum` | Making moves..., Spinning up..., Getting traction... |
154
+
155
+ Use `"spinnerVerbs": "random"` to have a theme randomly selected from the list above at each session start.
@@ -13,26 +13,40 @@ export default function (pi: ExtensionAPI) {
13
13
  .map((f) => basename(f, ".json"));
14
14
 
15
15
  const DEFAULT = "(default)";
16
- const availableWithDefault = [...available, DEFAULT];
16
+ const RANDOM = "random";
17
+ const availableWithDefault = [...available, RANDOM, DEFAULT];
18
+ const validChoices = new Set(availableWithDefault);
19
+
20
+ function parseVerbsData(data: unknown): string[] | undefined {
21
+ const verbs = (data as any)?.spinnerVerbs?.verbs ?? data;
22
+ return Array.isArray(verbs) && verbs.length > 0 ? verbs : undefined;
23
+ }
17
24
 
18
25
  function loadVerbs(name: string): string[] {
19
26
  const data = JSON.parse(readFileSync(join(verbsDir, `${name}.json`), "utf-8"));
20
- return data?.spinnerVerbs?.verbs ?? data;
27
+ const verbs = parseVerbsData(data);
28
+ if (!verbs) throw new Error(`Failed to parse verbs from ${name}.json`);
29
+ return verbs;
30
+ }
31
+
32
+ function randomVerbs(): string[] {
33
+ const name = available[Math.floor(Math.random() * available.length)];
34
+ return loadVerbs(name);
21
35
  }
22
36
 
23
37
  pi.registerFlag("verbs", {
24
38
  description: `Spinner verb list (${available.join(", ")})`,
25
39
  type: "string",
26
- default: "(default)",
40
+ default: DEFAULT,
27
41
  });
28
42
 
29
43
  let interval: ReturnType<typeof setInterval> | undefined;
30
44
 
31
45
  function activate(verbs: string[], ctx: ExtensionContext) {
32
46
  clearInterval(interval);
33
- const pick = () => verbs[Math.floor(Math.random() * verbs.length)];
34
- ctx.ui.setWorkingMessage(`${pick()}...`);
35
- interval = setInterval(() => ctx.ui.setWorkingMessage(`${pick()}...`), 3000);
47
+ const tick = () => ctx.ui.setWorkingMessage(`${verbs[Math.floor(Math.random() * verbs.length)]}...`);
48
+ tick();
49
+ interval = setInterval(tick, 3000);
36
50
  }
37
51
 
38
52
  function readSettings(settingsPath: string): Record<string, unknown> | undefined {
@@ -49,8 +63,9 @@ export default function (pi: ExtensionAPI) {
49
63
  if (!settings) return undefined;
50
64
 
51
65
  const named = settings.spinnerVerbs;
52
- if (typeof named === "string" && available.includes(named)) {
53
- return loadVerbs(named);
66
+ if (typeof named === "string") {
67
+ if (named === RANDOM) return randomVerbs();
68
+ if (available.includes(named)) return loadVerbs(named);
54
69
  }
55
70
 
56
71
  const filePath = settings.spinnerVerbsFile;
@@ -62,9 +77,8 @@ export default function (pi: ExtensionAPI) {
62
77
  : join(dirname(settingsPath), filePath);
63
78
  if (existsSync(resolved)) {
64
79
  try {
65
- const data = JSON.parse(readFileSync(resolved, "utf-8"));
66
- const verbs = data?.spinnerVerbs?.verbs ?? data;
67
- if (Array.isArray(verbs) && verbs.length > 0) return verbs;
80
+ const verbs = parseVerbsData(JSON.parse(readFileSync(resolved, "utf-8")));
81
+ if (verbs) return verbs;
68
82
  } catch {}
69
83
  }
70
84
  }
@@ -79,8 +93,9 @@ export default function (pi: ExtensionAPI) {
79
93
 
80
94
  let verbs: string[] | undefined;
81
95
 
82
- if (flag && flag !== "(default)" && available.includes(flag)) {
83
- verbs = loadVerbs(flag);
96
+ if (flag && flag !== DEFAULT) {
97
+ if (flag === RANDOM) verbs = randomVerbs();
98
+ else if (available.includes(flag)) verbs = loadVerbs(flag);
84
99
  }
85
100
 
86
101
  verbs ??= resolveVerbs(projectSettings) ?? resolveVerbs(globalSettings);
@@ -98,7 +113,7 @@ export default function (pi: ExtensionAPI) {
98
113
  },
99
114
  handler: async (args, ctx) => {
100
115
  const arg = args?.trim();
101
- if (arg && arg !== DEFAULT && !available.includes(arg)) {
116
+ if (arg && !validChoices.has(arg)) {
102
117
  ctx.ui.notify(`Unknown verb list: ${arg}. Available: ${availableWithDefault.join(", ")}`, "error");
103
118
  return;
104
119
  }
@@ -108,6 +123,9 @@ export default function (pi: ExtensionAPI) {
108
123
  clearInterval(interval);
109
124
  ctx.ui.setWorkingMessage();
110
125
  ctx.ui.notify("Restored default spinner", "info");
126
+ } else if (choice === RANDOM) {
127
+ activate(randomVerbs(), ctx);
128
+ ctx.ui.notify("Spinner: random", "info");
111
129
  } else {
112
130
  activate(loadVerbs(choice), ctx);
113
131
  ctx.ui.notify(`Spinner: ${choice}`, "info");
package/package.json CHANGED
@@ -1,10 +1,29 @@
1
1
  {
2
2
  "name": "@robhowley/spinner-verbs",
3
- "version": "1.0.1",
3
+ "version": "2.1.2",
4
4
  "description": "Replace boring LLM spinner verbs with themes that have a little more personality.",
5
+ "author": "Rob Howley",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/robhowley/spinner-verbs"
10
+ },
11
+ "homepage": "https://github.com/robhowley/spinner-verbs",
12
+ "bugs": {
13
+ "url": "https://github.com/robhowley/spinner-verbs/issues"
14
+ },
5
15
  "keywords": [
6
16
  "pi-package"
7
17
  ],
18
+ "files": [
19
+ "extensions",
20
+ "spinner-verbs",
21
+ "LICENSE",
22
+ "README.md"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
8
27
  "pi": {
9
28
  "extensions": [
10
29
  "./extensions"