claude-smart 0.1.14 → 0.1.16

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.14-green.svg" alt="Version">
16
+ <img src="https://img.shields.io/badge/version-0.1.16-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">
@@ -37,7 +37,7 @@
37
37
  </p>
38
38
 
39
39
  <p align="center">
40
- It learns both corrections and successful execution patterns—so Claude Code avoids repeating mistakes and reuses what works. Claude Code steadily adapts to <i>how you like</i> to work—across projects, codebases, and sessions.
40
+ It learns both corrections and successful execution patterns—so Claude Code avoids repeating mistakes and reuses what works. Playbook rules travel with you across every project; per-project profiles capture how you like to work on each repo.
41
41
  </p>
42
42
 
43
43
  <p align="center">
@@ -69,7 +69,7 @@ Four ways this changes what Claude Code can do for you:
69
69
 
70
70
  Instead of re-exploring, Claude starts from the proven path—reducing planning steps, latency, and token usage.
71
71
 
72
- - 🌐 **Project-wide, not session-siloed:** Session memory disappears with the conversation. The project playbook persists and improves across every session in that repo.
72
+ - 🌐 **Global playbook, project-scoped profiles:** Session memory disappears with the conversation. The playbook persists *globally* rules learned in one repo are available in every repo you work in while user profiles stay scoped to the current project so per-repo preferences don't leak across projects.
73
73
 
74
74
  - 🪶 **Better context without prompt bloat:** Distilled, deduplicated playbooks stay in dozens of tokens—not thousands—even as the project grows.
75
75
 
@@ -105,6 +105,7 @@ claude plugin uninstall claude-smart@reflexioai
105
105
  Local data under `~/.reflexio/` and `~/.claude-smart/` is left in place — remove manually if desired.
106
106
 
107
107
  Developing the plugin itself? See [DEVELOPER.md](./DEVELOPER.md#developing-locally).
108
+ > **Not supported:** Claude Code Cowork and claude.ai/code web — they run in a remote sandbox, so the local backend/dashboard and `~/.reflexio/` aren't reachable.
108
109
 
109
110
  ---
110
111
 
@@ -123,7 +124,7 @@ Developing the plugin itself? See [DEVELOPER.md](./DEVELOPER.md#developing-local
123
124
 
124
125
  ## Dashboard
125
126
 
126
- A web UI for browsing session histories, inspecting user profiles, and editing project playbooks. The dashboard auto-starts alongside the backend, so you can open **http://localhost:3001** directly. Or run `/claude-smart:dashboard` in Claude Code to launch dashboard in browser.
127
+ A web UI for browsing session histories, inspecting user profiles, and editing playbook rules. The dashboard auto-starts alongside the backend, so you can open **http://localhost:3001** directly. Or run `/claude-smart:dashboard` in Claude Code to launch dashboard in browser.
127
128
 
128
129
  <p align="center">
129
130
  <img src="assets/profile_dashboard.png" alt="Profile dashboard" width="49%">
@@ -136,8 +137,8 @@ A web UI for browsing session histories, inspecting user profiles, and editing p
136
137
 
137
138
  claude-smart builds two artifacts as you work and injects them into Claude at the start of every new session:
138
139
 
139
- - **User profile** — session-scoped preferences (stack, role, small quirks). *e.g.* "uses pnpm, not npm"; "prefers terse answers"; "backend engineer — explain frontend with backend analogues."
140
- - **Project playbook** — durable, generalized rules accumulated across every session in the repo. Each says when it applies and why. *e.g.* "always pass `--run` to `npm test` — watch mode hangs CI"; "use real Postgres for integration tests — mocks once hid a broken migration."
140
+ - **User profile** (project-scoped) preferences about how you work in this specific repo (stack, role, small quirks). *e.g.* "uses pnpm, not npm"; "prefers terse answers"; "backend engineer — explain frontend with backend analogues."
141
+ - **Playbook** (global, cross-project) — durable, generalized rules accumulated across every session, shared across every project you use claude-smart in. Each rule says when it applies and why, so a rule learned in one repo only fires in the contexts where it's relevant. *e.g.* "always pass `--run` to `npm test` — watch mode hangs CI"; "use real Postgres for integration tests — mocks once hid a broken migration."
141
142
 
142
143
  Playbooks clean themselves up: correct the same thing twice and they merge; change your mind and the old one is archived.
143
144
 
@@ -146,8 +147,10 @@ Under the hood: hooks watch your turns, tool calls, and Claude's replies, auto-f
146
147
  **Citations (`cs-cite`).** At the end of a reply, Claude may run:
147
148
 
148
149
  ```
149
- ⏺ Bash(cs-cite p1-2d57)
150
- 1 claude-smart learning applied
150
+ ⏺ Bash(cs-cite r1-252,p1-5aed)
151
+ (No output)
152
+
153
+ ⏺ ✨ 2 claude-smart learnings applied
151
154
  ```
152
155
 
153
156
  That signals a profile entry (`p…`) or playbook rule (`r…`) materially shaped the reply. Open the interaction's detail page in the [dashboard](#dashboard) to see the exact cited item.
@@ -161,7 +164,7 @@ See [ARCHITECTURE.md](./ARCHITECTURE.md) for hooks, data flow, and reflexio deta
161
164
  | Command | What it does |
162
165
  | --- | --- |
163
166
  | `/dashboard` | Open the dashboard in your browser, auto-starting the reflexio backend and dashboard services if they aren't already running. |
164
- | `/show` | Print the current project playbook plus the current session's user profiles (same markdown that `SessionStart` injects). Use it to audit what playbooks and preferences Claude is being told to follow. |
167
+ | `/show` | Print the current (global) playbook plus the current project's user profiles (same markdown that `SessionStart` injects). Use it to audit what playbooks and preferences Claude is being told to follow. |
165
168
  | `/learn` | Force reflexio to run extraction *now* on the current session's unpublished interactions. Without this, extraction runs at the end of the session or on reflexio's batch interval. |
166
169
  | `/tag [note]` | Tag the most recent turn as a correction, for cases the automatic heuristic missed. The note becomes the correction description the extractor sees. |
167
170
  | `/restart` | Restart the reflexio backend and dashboard to pick up new changes (e.g. after upgrading the plugin or editing local reflexio code). |
@@ -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,45 @@ 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
+ let spinnerActive = false;
29
+ let timer = null;
30
+ if (useSpinner) {
31
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
32
+ let i = 0;
33
+ spinnerActive = true;
34
+ const render = () => {
35
+ process.stdout.write(`\r${frames[i = (i + 1) % frames.length]} ${spinnerLabel}`);
36
+ };
37
+ render();
38
+ timer = setInterval(render, 80);
39
+
40
+ const clear = () => {
41
+ if (!spinnerActive) return;
42
+ spinnerActive = false;
43
+ clearInterval(timer);
44
+ process.stdout.write("\r\x1b[2K");
45
+ };
46
+ const passthrough = (stream) => (chunk) => {
47
+ clear();
48
+ stream.write(chunk);
49
+ };
50
+ child.stdout.on("data", passthrough(process.stdout));
51
+ child.stderr.on("data", passthrough(process.stderr));
52
+ child.on("exit", clear);
53
+ }
54
+
55
+ child.on("exit", (code) => resolve(typeof code === "number" ? code : 1));
56
+ child.on("error", () => resolve(1));
57
+ });
58
+ }
59
+
21
60
  function hasClaudeCli() {
22
61
  const probe = process.platform === "win32" ? "where claude" : "command -v claude";
23
62
  try {
@@ -79,7 +118,7 @@ function parseSource(args) {
79
118
  return value;
80
119
  }
81
120
 
82
- function runUpdate() {
121
+ async function runUpdate() {
83
122
  if (!hasClaudeCli()) {
84
123
  process.stderr.write(
85
124
  "error: 'claude' CLI not found on PATH. " +
@@ -88,10 +127,10 @@ function runUpdate() {
88
127
  process.exit(1);
89
128
  }
90
129
 
91
- try {
92
- execFileSync("claude", ["plugin", "update", PLUGIN_SPEC], { stdio: "inherit" });
93
- } catch (err) {
94
- const code = typeof err.status === "number" ? err.status : 1;
130
+ const code = await runClaude(["plugin", "update", PLUGIN_SPEC], {
131
+ spinnerLabel: "Checking for claude-smart updates…",
132
+ });
133
+ if (code !== 0) {
95
134
  process.stderr.write(`error: \`claude plugin update ${PLUGIN_SPEC}\` failed (exit ${code})\n`);
96
135
  process.exit(code);
97
136
  }
@@ -99,7 +138,7 @@ function runUpdate() {
99
138
  process.stdout.write("\nclaude-smart updated. Restart Claude Code to apply.\n");
100
139
  }
101
140
 
102
- function runUninstall() {
141
+ async function runUninstall() {
103
142
  if (!hasClaudeCli()) {
104
143
  process.stderr.write(
105
144
  "error: 'claude' CLI not found on PATH. " +
@@ -108,10 +147,10 @@ function runUninstall() {
108
147
  process.exit(1);
109
148
  }
110
149
 
111
- try {
112
- execFileSync("claude", ["plugin", "uninstall", PLUGIN_SPEC], { stdio: "inherit" });
113
- } catch (err) {
114
- const code = typeof err.status === "number" ? err.status : 1;
150
+ const code = await runClaude(["plugin", "uninstall", PLUGIN_SPEC], {
151
+ spinnerLabel: "Uninstalling claude-smart…",
152
+ });
153
+ if (code !== 0) {
115
154
  process.stderr.write(
116
155
  `error: \`claude plugin uninstall ${PLUGIN_SPEC}\` failed (exit ${code})\n`,
117
156
  );
@@ -128,7 +167,7 @@ function runUninstall() {
128
167
  );
129
168
  }
130
169
 
131
- function runInstall(args) {
170
+ async function runInstall(args) {
132
171
  if (!hasClaudeCli()) {
133
172
  process.stderr.write(
134
173
  "error: 'claude' CLI not found on PATH. " +
@@ -139,17 +178,15 @@ function runInstall(args) {
139
178
 
140
179
  const source = parseSource(args);
141
180
  const steps = [
142
- ["plugin", "marketplace", "add", source],
143
- ["plugin", "install", PLUGIN_SPEC],
181
+ { args: ["plugin", "marketplace", "add", source], label: "Adding marketplace…" },
182
+ { args: ["plugin", "install", PLUGIN_SPEC], label: "Installing claude-smart…" },
144
183
  ];
145
184
 
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;
185
+ for (const step of steps) {
186
+ const code = await runClaude(step.args, { spinnerLabel: step.label });
187
+ if (code !== 0) {
151
188
  process.stderr.write(
152
- `error: \`claude ${stepArgs.join(" ")}\` failed (exit ${code})\n`,
189
+ `error: \`claude ${step.args.join(" ")}\` failed (exit ${code})\n`,
153
190
  );
154
191
  process.exit(code);
155
192
  }
@@ -173,7 +210,7 @@ function runInstall(args) {
173
210
  );
174
211
  }
175
212
 
176
- function main() {
213
+ async function main() {
177
214
  const args = process.argv.slice(2);
178
215
  const cmd = args[0] || "install";
179
216
 
@@ -183,17 +220,17 @@ function main() {
183
220
  }
184
221
 
185
222
  if (cmd === "install") {
186
- runInstall(args.slice(1));
223
+ await runInstall(args.slice(1));
187
224
  return;
188
225
  }
189
226
 
190
227
  if (cmd === "update") {
191
- runUpdate();
228
+ await runUpdate();
192
229
  return;
193
230
  }
194
231
 
195
232
  if (cmd === "uninstall") {
196
- runUninstall();
233
+ await runUninstall();
197
234
  return;
198
235
  }
199
236
 
@@ -203,4 +240,7 @@ function main() {
203
240
  process.exit(1);
204
241
  }
205
242
 
206
- main();
243
+ main().catch((err) => {
244
+ process.stderr.write(`claude-smart: ${err && err.message ? err.message : err}\n`);
245
+ process.exit(1);
246
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Self-improving Claude Code plugin — learns from corrections via reflexio",
5
5
  "keywords": [
6
6
  "claude",
@@ -31,6 +31,6 @@
31
31
  "LICENSE"
32
32
  ],
33
33
  "engines": {
34
- "node": ">=18.0.0"
34
+ "node": ">=20.9.0"
35
35
  }
36
36
  }