claude-smart 0.2.31 → 0.2.32

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.
Files changed (59) hide show
  1. package/README.md +2 -2
  2. package/bin/claude-smart.js +172 -18
  3. package/package.json +1 -1
  4. package/plugin/.claude-plugin/plugin.json +1 -1
  5. package/plugin/.codex-plugin/plugin.json +1 -1
  6. package/plugin/dashboard/app/api/config/route.ts +11 -2
  7. package/plugin/dashboard/app/api/health/route.ts +45 -6
  8. package/plugin/dashboard/app/api/reflexio/[...path]/route.ts +15 -7
  9. package/plugin/dashboard/app/api/rules/applied/route.ts +27 -0
  10. package/plugin/dashboard/app/configure/env/page.tsx +36 -31
  11. package/plugin/dashboard/app/configure/layout.tsx +1 -1
  12. package/plugin/dashboard/app/configure/server/page.tsx +8 -14
  13. package/plugin/dashboard/app/dashboard/page.tsx +311 -115
  14. package/plugin/dashboard/app/globals.css +80 -66
  15. package/plugin/dashboard/app/layout.tsx +13 -10
  16. package/plugin/dashboard/app/preferences/[id]/page.tsx +21 -32
  17. package/plugin/dashboard/app/preferences/page.tsx +154 -54
  18. package/plugin/dashboard/app/preferences/project/[id]/page.tsx +1 -0
  19. package/plugin/dashboard/app/rules/[id]/page.tsx +51 -0
  20. package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +4 -4
  21. package/plugin/dashboard/app/sessions/page.tsx +14 -10
  22. package/plugin/dashboard/app/skills/page.tsx +175 -56
  23. package/plugin/dashboard/app/skills/project/[id]/page.tsx +22 -38
  24. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +20 -37
  25. package/plugin/dashboard/components/common/delete-learning-danger-zone.tsx +166 -0
  26. package/plugin/dashboard/components/common/empty-state.tsx +4 -2
  27. package/plugin/dashboard/components/common/learnings-badge.tsx +1 -1
  28. package/plugin/dashboard/components/common/page-header.tsx +5 -3
  29. package/plugin/dashboard/components/common/page-tabs.tsx +4 -4
  30. package/plugin/dashboard/components/common/stat-card.tsx +9 -5
  31. package/plugin/dashboard/components/layout/sidebar.tsx +24 -9
  32. package/plugin/dashboard/components/layout/top-bar.tsx +37 -25
  33. package/plugin/dashboard/components/ui/input.tsx +1 -0
  34. package/plugin/dashboard/hooks/use-settings.tsx +30 -61
  35. package/plugin/dashboard/hooks/use-stall-state.ts +5 -9
  36. package/plugin/dashboard/lib/config-file.ts +2 -0
  37. package/plugin/dashboard/lib/reflexio-client.ts +23 -48
  38. package/plugin/dashboard/lib/session-reader.ts +222 -6
  39. package/plugin/dashboard/lib/types.ts +20 -1
  40. package/plugin/dashboard/package-lock.json +70 -95
  41. package/plugin/dashboard/package.json +5 -2
  42. package/plugin/pyproject.toml +1 -1
  43. package/plugin/scripts/_lib.sh +61 -0
  44. package/plugin/scripts/backend-service.sh +13 -2
  45. package/plugin/scripts/cli.sh +1 -0
  46. package/plugin/scripts/codex-hook.js +100 -3
  47. package/plugin/scripts/dashboard-service.sh +95 -19
  48. package/plugin/scripts/hook_entry.sh +13 -7
  49. package/plugin/scripts/smart-install.sh +18 -13
  50. package/plugin/src/claude_smart/cli.py +86 -20
  51. package/plugin/src/claude_smart/context_format.py +244 -6
  52. package/plugin/src/claude_smart/context_inject.py +8 -1
  53. package/plugin/src/claude_smart/cs_cite.py +186 -34
  54. package/plugin/src/claude_smart/env_config.py +102 -0
  55. package/plugin/src/claude_smart/events/stop.py +32 -6
  56. package/plugin/src/claude_smart/internal_call.py +30 -0
  57. package/plugin/src/claude_smart/publish.py +5 -0
  58. package/plugin/src/claude_smart/reflexio_adapter.py +102 -26
  59. package/plugin/uv.lock +1 -1
@@ -8,14 +8,14 @@ import { Input } from "@/components/ui/input";
8
8
  import { Label } from "@/components/ui/label";
9
9
  import { Switch } from "@/components/ui/switch";
10
10
  import { Separator } from "@/components/ui/separator";
11
- import { useSettings } from "@/hooks/use-settings";
11
+ import { SETTINGS_CHANGED_EVENT } from "@/hooks/use-settings";
12
12
  import type { ClaudeCodeHookConfig, ClaudeSmartConfig } from "@/lib/types";
13
13
 
14
14
  export default function ConfigureEnvPage() {
15
- const { reflexioUrl, setReflexioUrl } = useSettings();
16
15
  const [config, setConfig] = useState<ClaudeSmartConfig | null>(null);
17
16
  const [hookConfig, setHookConfig] = useState<ClaudeCodeHookConfig | null>(null);
18
17
  const [hookDirty, setHookDirty] = useState(false);
18
+ const [apiKeyDirty, setApiKeyDirty] = useState(false);
19
19
  const [error, setError] = useState<string | null>(null);
20
20
  const [saved, setSaved] = useState(false);
21
21
  const [saving, setSaving] = useState(false);
@@ -31,6 +31,7 @@ export default function ConfigureEnvPage() {
31
31
  setConfig(envConfig);
32
32
  setHookConfig(claudeConfig);
33
33
  setHookDirty(false);
34
+ setApiKeyDirty(false);
34
35
  })
35
36
  .catch((e) => setError(e instanceof Error ? e.message : String(e)));
36
37
  }, []);
@@ -63,11 +64,14 @@ export default function ConfigureEnvPage() {
63
64
  setSaving(true);
64
65
  setError(null);
65
66
  try {
67
+ const envUpdate: Partial<ClaudeSmartConfig> = { ...config };
68
+ if (!apiKeyDirty) delete envUpdate.REFLEXIO_API_KEY;
69
+ delete envUpdate.REFLEXIO_API_KEY_SET;
66
70
  const [envRes, hookRes] = await Promise.all([
67
71
  fetch("/api/config", {
68
72
  method: "PUT",
69
73
  headers: { "content-type": "application/json" },
70
- body: JSON.stringify(config),
74
+ body: JSON.stringify(envUpdate),
71
75
  }),
72
76
  fetch("/api/claude-settings", {
73
77
  method: "PUT",
@@ -89,7 +93,9 @@ export default function ConfigureEnvPage() {
89
93
  const updatedHook: ClaudeCodeHookConfig = await hookRes.json();
90
94
  setConfig(updated);
91
95
  setHookConfig(updatedHook);
96
+ window.dispatchEvent(new Event(SETTINGS_CHANGED_EVENT));
92
97
  setHookDirty(false);
98
+ setApiKeyDirty(false);
93
99
  setSaved(true);
94
100
  } catch (e) {
95
101
  setError(e instanceof Error ? e.message : String(e));
@@ -111,7 +117,7 @@ export default function ConfigureEnvPage() {
111
117
  }
112
118
  />
113
119
 
114
- <div className="p-6 max-w-2xl mx-auto space-y-6">
120
+ <div className="p-6 max-w-3xl mx-auto space-y-5">
115
121
  {error && (
116
122
  <div className="rounded-lg border border-destructive/30 bg-destructive/5 text-destructive px-4 py-3 text-sm">
117
123
  {error}
@@ -124,27 +130,7 @@ export default function ConfigureEnvPage() {
124
130
  </div>
125
131
  )}
126
132
 
127
- <section className="space-y-4">
128
- <div>
129
- <h2 className="text-sm font-semibold">Dashboard</h2>
130
- <p className="text-xs text-muted-foreground">
131
- Stored in browser localStorage — only affects this UI.
132
- </p>
133
- </div>
134
- <div className="space-y-2">
135
- <Label>Reflexio endpoint (dashboard)</Label>
136
- <Input
137
- value={reflexioUrl}
138
- onChange={(e) => setReflexioUrl(e.target.value)}
139
- className="font-mono text-xs"
140
- placeholder="http://localhost:8071"
141
- />
142
- </div>
143
- </section>
144
-
145
- <Separator />
146
-
147
- <section className="space-y-4">
133
+ <section className="space-y-4 rounded-lg border border-border bg-card/92 p-5 shadow-sm">
148
134
  <div>
149
135
  <h2 className="text-sm font-semibold">claude-smart environment</h2>
150
136
  <p className="text-xs text-muted-foreground">
@@ -162,11 +148,30 @@ export default function ConfigureEnvPage() {
162
148
  <Input
163
149
  value={config.REFLEXIO_URL}
164
150
  onChange={(e) => update("REFLEXIO_URL", e.target.value)}
165
- className="font-mono text-xs"
151
+ className="font-mono text-xs bg-background/80"
166
152
  placeholder="http://localhost:8071/"
167
153
  />
168
154
  </div>
169
155
 
156
+ <div className="space-y-2">
157
+ <Label>REFLEXIO_API_KEY</Label>
158
+ <Input
159
+ type="password"
160
+ value={String(config.REFLEXIO_API_KEY ?? "")}
161
+ onChange={(e) => {
162
+ setApiKeyDirty(true);
163
+ update("REFLEXIO_API_KEY", e.target.value);
164
+ }}
165
+ className="font-mono text-xs bg-background/80"
166
+ placeholder={
167
+ config.REFLEXIO_API_KEY_SET
168
+ ? "(configured - leave blank to keep existing key)"
169
+ : "(empty - local mode)"
170
+ }
171
+ autoComplete="off"
172
+ />
173
+ </div>
174
+
170
175
  <div className="flex items-start justify-between gap-4">
171
176
  <div className="min-w-0">
172
177
  <Label htmlFor="use-local-cli">CLAUDE_SMART_USE_LOCAL_CLI</Label>
@@ -206,7 +211,7 @@ export default function ConfigureEnvPage() {
206
211
  <Input
207
212
  value={String(config.CLAUDE_SMART_CLI_PATH ?? "")}
208
213
  onChange={(e) => update("CLAUDE_SMART_CLI_PATH", e.target.value)}
209
- className="font-mono text-xs"
214
+ className="font-mono text-xs bg-background/80"
210
215
  placeholder="(empty — auto-detect via $PATH)"
211
216
  />
212
217
  </div>
@@ -218,7 +223,7 @@ export default function ConfigureEnvPage() {
218
223
  onChange={(e) =>
219
224
  update("CLAUDE_SMART_CLI_TIMEOUT", e.target.value)
220
225
  }
221
- className="font-mono text-xs"
226
+ className="font-mono text-xs bg-background/80"
222
227
  placeholder="120"
223
228
  />
224
229
  </div>
@@ -230,7 +235,7 @@ export default function ConfigureEnvPage() {
230
235
  onChange={(e) =>
231
236
  update("CLAUDE_SMART_STATE_DIR", e.target.value)
232
237
  }
233
- className="font-mono text-xs"
238
+ className="font-mono text-xs bg-background/80"
234
239
  placeholder="(empty — default ~/.claude-smart/sessions)"
235
240
  />
236
241
  </div>
@@ -240,7 +245,7 @@ export default function ConfigureEnvPage() {
240
245
 
241
246
  <Separator />
242
247
 
243
- <section className="space-y-4">
248
+ <section className="space-y-4 rounded-lg border border-border bg-card/92 p-5 shadow-sm">
244
249
  <div>
245
250
  <h2 className="text-sm font-semibold">Claude Code hook environment</h2>
246
251
  <p className="text-xs text-muted-foreground">
@@ -270,7 +275,7 @@ export default function ConfigureEnvPage() {
270
275
  onCheckedChange={updateOptimizer}
271
276
  />
272
277
  </div>
273
- <div className="rounded-md border border-border bg-muted/20 p-3 text-xs text-muted-foreground space-y-2">
278
+ <div className="rounded-md border border-border bg-background/60 p-3 text-xs text-muted-foreground space-y-2">
274
279
  <div className="flex items-center justify-between gap-4">
275
280
  <span>Effective value</span>
276
281
  <span className="font-medium text-foreground">
@@ -30,7 +30,7 @@ export default function ConfigureLayout({
30
30
 
31
31
  return (
32
32
  <div className="flex-1 flex flex-col overflow-hidden">
33
- <div className="border-b border-border bg-muted/20 px-6 py-4">
33
+ <div className="border-b border-border bg-background/72 px-6 py-4 backdrop-blur">
34
34
  <PageTabs
35
35
  items={tabs}
36
36
  activeId={
@@ -6,7 +6,6 @@ import { PageHeader } from "@/components/common/page-header";
6
6
  import { Button } from "@/components/ui/button";
7
7
  import { Input } from "@/components/ui/input";
8
8
  import { Label } from "@/components/ui/label";
9
- import { useSettings } from "@/hooks/use-settings";
10
9
  import type { ReflexioConfig } from "@/lib/types";
11
10
 
12
11
  type ExtractorField =
@@ -14,7 +13,6 @@ type ExtractorField =
14
13
  | "user_playbook_extractor_configs";
15
14
 
16
15
  export default function ConfigureServerPage() {
17
- const { reflexioUrl } = useSettings();
18
16
  const [srvConfig, setSrvConfig] = useState<ReflexioConfig | null>(null);
19
17
  const [srvError, setSrvError] = useState<string | null>(null);
20
18
  const [srvSaved, setSrvSaved] = useState(false);
@@ -22,11 +20,8 @@ export default function ConfigureServerPage() {
22
20
  const [srvLoading, setSrvLoading] = useState(true);
23
21
 
24
22
  const fetchSrvConfig = useCallback(async (): Promise<ReflexioConfig> => {
25
- const headers: HeadersInit = {};
26
- if (reflexioUrl) headers["x-reflexio-url"] = reflexioUrl;
27
23
  const res = await fetch("/api/reflexio/api/get_config", {
28
24
  cache: "no-store",
29
- headers,
30
25
  });
31
26
  if (!res.ok) {
32
27
  const body = await res.json().catch(() => null);
@@ -36,7 +31,7 @@ export default function ConfigureServerPage() {
36
31
  throw new Error(detail);
37
32
  }
38
33
  return (await res.json()) as ReflexioConfig;
39
- }, [reflexioUrl]);
34
+ }, []);
40
35
 
41
36
  useEffect(() => {
42
37
  let alive = true;
@@ -84,7 +79,6 @@ export default function ConfigureServerPage() {
84
79
  setSrvError(null);
85
80
  try {
86
81
  const headers: HeadersInit = { "content-type": "application/json" };
87
- if (reflexioUrl) headers["x-reflexio-url"] = reflexioUrl;
88
82
  const res = await fetch("/api/reflexio/api/set_config", {
89
83
  method: "POST",
90
84
  headers,
@@ -120,7 +114,7 @@ export default function ConfigureServerPage() {
120
114
  }
121
115
  />
122
116
 
123
- <div className="p-6 max-w-2xl mx-auto space-y-6">
117
+ <div className="p-6 max-w-3xl mx-auto space-y-5">
124
118
  {srvError && (
125
119
  <div className="rounded-lg border border-destructive/30 bg-destructive/5 text-destructive px-4 py-3 text-sm">
126
120
  {srvError}
@@ -136,7 +130,7 @@ export default function ConfigureServerPage() {
136
130
  {srvLoading ? (
137
131
  <div className="text-sm text-muted-foreground">Loading…</div>
138
132
  ) : srvConfig ? (
139
- <section className="space-y-5">
133
+ <section className="space-y-5 rounded-lg border border-border bg-card/92 p-5 shadow-sm">
140
134
  <div className="space-y-2">
141
135
  <Label>Additional agent description</Label>
142
136
  <p className="text-xs text-muted-foreground">
@@ -148,7 +142,7 @@ export default function ConfigureServerPage() {
148
142
  onChange={(e) =>
149
143
  updateSrv("agent_context_prompt", e.target.value || null)
150
144
  }
151
- className="w-full font-mono text-xs min-h-[80px] rounded-md border border-input bg-transparent px-3 py-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
145
+ className="w-full font-mono text-xs min-h-[80px] rounded-md border border-input bg-background/80 px-3 py-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
152
146
  placeholder="(empty)"
153
147
  />
154
148
  </div>
@@ -170,7 +164,7 @@ export default function ConfigureServerPage() {
170
164
  Number.isFinite(n) && n > 0 ? n : undefined,
171
165
  );
172
166
  }}
173
- className="font-mono text-xs"
167
+ className="font-mono text-xs bg-background/80"
174
168
  placeholder="10"
175
169
  />
176
170
  </div>
@@ -192,7 +186,7 @@ export default function ConfigureServerPage() {
192
186
  Number.isFinite(n) && n > 0 ? n : undefined,
193
187
  );
194
188
  }}
195
- className="font-mono text-xs"
189
+ className="font-mono text-xs bg-background/80"
196
190
  placeholder="5"
197
191
  />
198
192
  </div>
@@ -215,7 +209,7 @@ export default function ConfigureServerPage() {
215
209
  e.target.value,
216
210
  )
217
211
  }
218
- className="w-full font-mono text-xs min-h-[120px] rounded-md border border-input bg-transparent px-3 py-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
212
+ className="w-full font-mono text-xs min-h-[120px] rounded-md border border-input bg-background/80 px-3 py-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
219
213
  />
220
214
  ) : (
221
215
  <div className="text-xs text-muted-foreground italic">
@@ -242,7 +236,7 @@ export default function ConfigureServerPage() {
242
236
  e.target.value,
243
237
  )
244
238
  }
245
- className="w-full font-mono text-xs min-h-[120px] rounded-md border border-input bg-transparent px-3 py-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
239
+ className="w-full font-mono text-xs min-h-[120px] rounded-md border border-input bg-background/80 px-3 py-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
246
240
  />
247
241
  ) : (
248
242
  <div className="text-xs text-muted-foreground italic">