claude-smart 0.1.15 → 0.1.17

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
@@ -13,7 +13,7 @@
13
13
  <img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License">
14
14
  </a>
15
15
  <a href="plugin/pyproject.toml">
16
- <img src="https://img.shields.io/badge/version-0.1.15-green.svg" alt="Version">
16
+ <img src="https://img.shields.io/badge/version-0.1.17-green.svg" alt="Version">
17
17
  </a>
18
18
  <a href="plugin/pyproject.toml">
19
19
  <img src="https://img.shields.io/badge/python-%3E%3D3.12-brightgreen.svg" alt="Python">
@@ -9,7 +9,7 @@
9
9
  */
10
10
  "use strict";
11
11
 
12
- const { execFileSync, execSync } = require("child_process");
12
+ const { execFileSync, execSync, spawn } = require("child_process");
13
13
  const { appendFileSync, existsSync, mkdirSync, readFileSync } = require("fs");
14
14
  const { homedir } = require("os");
15
15
  const { dirname, join } = require("path");
@@ -18,6 +18,67 @@ const DEFAULT_MARKETPLACE_SOURCE = "ReflexioAI/claude-smart";
18
18
  const PLUGIN_SPEC = "claude-smart@reflexioai";
19
19
  const REFLEXIO_ENV_PATH = join(homedir(), ".reflexio", ".env");
20
20
 
21
+ function runClaude(args, { spinnerLabel } = {}) {
22
+ const useSpinner = Boolean(spinnerLabel) && process.stdout.isTTY && !process.env.CI;
23
+ return new Promise((resolve) => {
24
+ const child = spawn("claude", args, {
25
+ stdio: useSpinner ? ["inherit", "pipe", "pipe"] : "inherit",
26
+ });
27
+
28
+ if (useSpinner) {
29
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
30
+ let i = 0;
31
+ let spinTimer = null;
32
+ let rearmTimer = null;
33
+ let exited = false;
34
+
35
+ const draw = () => {
36
+ process.stdout.write(`\r⠿ ${spinnerLabel}`.replace("⠿", frames[i = (i + 1) % frames.length]));
37
+ };
38
+ const clearLine = () => process.stdout.write("\r\x1b[2K");
39
+ const startSpin = () => {
40
+ if (spinTimer || exited) return;
41
+ draw();
42
+ spinTimer = setInterval(draw, 80);
43
+ };
44
+ const stopSpin = () => {
45
+ if (!spinTimer) return;
46
+ clearInterval(spinTimer);
47
+ spinTimer = null;
48
+ clearLine();
49
+ };
50
+ const armRearm = () => {
51
+ if (rearmTimer) clearTimeout(rearmTimer);
52
+ rearmTimer = setTimeout(() => {
53
+ rearmTimer = null;
54
+ startSpin();
55
+ }, 200);
56
+ };
57
+
58
+ startSpin();
59
+
60
+ const passthrough = (stream) => (chunk) => {
61
+ stopSpin();
62
+ stream.write(chunk);
63
+ armRearm();
64
+ };
65
+ child.stdout.on("data", passthrough(process.stdout));
66
+ child.stderr.on("data", passthrough(process.stderr));
67
+ child.on("exit", () => {
68
+ exited = true;
69
+ if (rearmTimer) {
70
+ clearTimeout(rearmTimer);
71
+ rearmTimer = null;
72
+ }
73
+ stopSpin();
74
+ });
75
+ }
76
+
77
+ child.on("exit", (code) => resolve(typeof code === "number" ? code : 1));
78
+ child.on("error", () => resolve(1));
79
+ });
80
+ }
81
+
21
82
  function hasClaudeCli() {
22
83
  const probe = process.platform === "win32" ? "where claude" : "command -v claude";
23
84
  try {
@@ -79,7 +140,7 @@ function parseSource(args) {
79
140
  return value;
80
141
  }
81
142
 
82
- function runUpdate() {
143
+ async function runUpdate() {
83
144
  if (!hasClaudeCli()) {
84
145
  process.stderr.write(
85
146
  "error: 'claude' CLI not found on PATH. " +
@@ -88,10 +149,10 @@ function runUpdate() {
88
149
  process.exit(1);
89
150
  }
90
151
 
91
- try {
92
- execFileSync("claude", ["plugin", "update", PLUGIN_SPEC], { stdio: "inherit" });
93
- } catch (err) {
94
- const code = typeof err.status === "number" ? err.status : 1;
152
+ const code = await runClaude(["plugin", "update", PLUGIN_SPEC], {
153
+ spinnerLabel: "Checking for claude-smart updates…",
154
+ });
155
+ if (code !== 0) {
95
156
  process.stderr.write(`error: \`claude plugin update ${PLUGIN_SPEC}\` failed (exit ${code})\n`);
96
157
  process.exit(code);
97
158
  }
@@ -99,7 +160,7 @@ function runUpdate() {
99
160
  process.stdout.write("\nclaude-smart updated. Restart Claude Code to apply.\n");
100
161
  }
101
162
 
102
- function runUninstall() {
163
+ async function runUninstall() {
103
164
  if (!hasClaudeCli()) {
104
165
  process.stderr.write(
105
166
  "error: 'claude' CLI not found on PATH. " +
@@ -108,10 +169,10 @@ function runUninstall() {
108
169
  process.exit(1);
109
170
  }
110
171
 
111
- try {
112
- execFileSync("claude", ["plugin", "uninstall", PLUGIN_SPEC], { stdio: "inherit" });
113
- } catch (err) {
114
- const code = typeof err.status === "number" ? err.status : 1;
172
+ const code = await runClaude(["plugin", "uninstall", PLUGIN_SPEC], {
173
+ spinnerLabel: "Uninstalling claude-smart…",
174
+ });
175
+ if (code !== 0) {
115
176
  process.stderr.write(
116
177
  `error: \`claude plugin uninstall ${PLUGIN_SPEC}\` failed (exit ${code})\n`,
117
178
  );
@@ -128,7 +189,7 @@ function runUninstall() {
128
189
  );
129
190
  }
130
191
 
131
- function runInstall(args) {
192
+ async function runInstall(args) {
132
193
  if (!hasClaudeCli()) {
133
194
  process.stderr.write(
134
195
  "error: 'claude' CLI not found on PATH. " +
@@ -139,17 +200,15 @@ function runInstall(args) {
139
200
 
140
201
  const source = parseSource(args);
141
202
  const steps = [
142
- ["plugin", "marketplace", "add", source],
143
- ["plugin", "install", PLUGIN_SPEC],
203
+ { args: ["plugin", "marketplace", "add", source], label: "Adding marketplace…" },
204
+ { args: ["plugin", "install", PLUGIN_SPEC], label: "Installing claude-smart…" },
144
205
  ];
145
206
 
146
- for (const stepArgs of steps) {
147
- try {
148
- execFileSync("claude", stepArgs, { stdio: "inherit" });
149
- } catch (err) {
150
- const code = typeof err.status === "number" ? err.status : 1;
207
+ for (const step of steps) {
208
+ const code = await runClaude(step.args, { spinnerLabel: step.label });
209
+ if (code !== 0) {
151
210
  process.stderr.write(
152
- `error: \`claude ${stepArgs.join(" ")}\` failed (exit ${code})\n`,
211
+ `error: \`claude ${step.args.join(" ")}\` failed (exit ${code})\n`,
153
212
  );
154
213
  process.exit(code);
155
214
  }
@@ -173,7 +232,7 @@ function runInstall(args) {
173
232
  );
174
233
  }
175
234
 
176
- function main() {
235
+ async function main() {
177
236
  const args = process.argv.slice(2);
178
237
  const cmd = args[0] || "install";
179
238
 
@@ -183,17 +242,17 @@ function main() {
183
242
  }
184
243
 
185
244
  if (cmd === "install") {
186
- runInstall(args.slice(1));
245
+ await runInstall(args.slice(1));
187
246
  return;
188
247
  }
189
248
 
190
249
  if (cmd === "update") {
191
- runUpdate();
250
+ await runUpdate();
192
251
  return;
193
252
  }
194
253
 
195
254
  if (cmd === "uninstall") {
196
- runUninstall();
255
+ await runUninstall();
197
256
  return;
198
257
  }
199
258
 
@@ -203,4 +262,7 @@ function main() {
203
262
  process.exit(1);
204
263
  }
205
264
 
206
- main();
265
+ main().catch((err) => {
266
+ process.stderr.write(`claude-smart: ${err && err.message ? err.message : err}\n`);
267
+ process.exit(1);
268
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Self-improving Claude Code plugin — learns from corrections via reflexio",
5
5
  "keywords": [
6
6
  "claude",