claude-smart 0.2.22 → 0.2.24
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/.agents/plugins/marketplace.json +20 -0
- package/README.md +69 -27
- package/bin/claude-smart.js +296 -11
- package/package.json +11 -1
- package/plugin/.claude-plugin/plugin.json +17 -0
- package/plugin/.codex-plugin/plugin.json +35 -0
- package/plugin/LICENSE +202 -0
- package/plugin/README.md +37 -0
- package/plugin/bin/cs-cite +77 -0
- package/plugin/commands/clear-all.md +8 -0
- package/plugin/commands/dashboard.md +8 -0
- package/plugin/commands/learn.md +12 -0
- package/plugin/commands/restart.md +8 -0
- package/plugin/commands/show.md +8 -0
- package/plugin/dashboard/AGENTS.md +6 -0
- package/plugin/dashboard/app/api/claude-settings/route.ts +19 -0
- package/plugin/dashboard/app/api/config/route.ts +16 -0
- package/plugin/dashboard/app/api/health/route.ts +10 -0
- package/plugin/dashboard/app/api/reflexio/[...path]/route.ts +63 -0
- package/plugin/dashboard/app/api/sessions/[id]/route.ts +28 -0
- package/plugin/dashboard/app/api/sessions/route.ts +14 -0
- package/plugin/dashboard/app/configure/env/page.tsx +318 -0
- package/plugin/dashboard/app/configure/layout.tsx +47 -0
- package/plugin/dashboard/app/configure/page.tsx +5 -0
- package/plugin/dashboard/app/configure/server/page.tsx +258 -0
- package/plugin/dashboard/app/dashboard/page.tsx +227 -0
- package/plugin/dashboard/app/globals.css +129 -0
- package/plugin/dashboard/app/icon.png +0 -0
- package/plugin/dashboard/app/layout.tsx +40 -0
- package/plugin/dashboard/app/page.tsx +5 -0
- package/plugin/dashboard/app/preferences/[id]/page.tsx +531 -0
- package/plugin/dashboard/app/preferences/page.tsx +126 -0
- package/plugin/dashboard/app/providers.tsx +12 -0
- package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +321 -0
- package/plugin/dashboard/app/sessions/page.tsx +186 -0
- package/plugin/dashboard/app/skills/page.tsx +362 -0
- package/plugin/dashboard/app/skills/project/[id]/page.tsx +597 -0
- package/plugin/dashboard/app/skills/shared/[id]/page.tsx +830 -0
- package/plugin/dashboard/components/common/delete-all-button.tsx +45 -0
- package/plugin/dashboard/components/common/empty-state.tsx +34 -0
- package/plugin/dashboard/components/common/learnings-badge.tsx +34 -0
- package/plugin/dashboard/components/common/page-header.tsx +34 -0
- package/plugin/dashboard/components/common/page-tabs.tsx +115 -0
- package/plugin/dashboard/components/common/stat-card.tsx +38 -0
- package/plugin/dashboard/components/layout/nav-items.ts +22 -0
- package/plugin/dashboard/components/layout/sidebar.tsx +45 -0
- package/plugin/dashboard/components/layout/top-bar.tsx +64 -0
- package/plugin/dashboard/components/stall-banner.tsx +53 -0
- package/plugin/dashboard/components/ui/badge.tsx +52 -0
- package/plugin/dashboard/components/ui/button.tsx +60 -0
- package/plugin/dashboard/components/ui/collapsible.tsx +21 -0
- package/plugin/dashboard/components/ui/input.tsx +20 -0
- package/plugin/dashboard/components/ui/label.tsx +20 -0
- package/plugin/dashboard/components/ui/scroll-area.tsx +55 -0
- package/plugin/dashboard/components/ui/select.tsx +201 -0
- package/plugin/dashboard/components/ui/separator.tsx +25 -0
- package/plugin/dashboard/components/ui/sheet.tsx +135 -0
- package/plugin/dashboard/components/ui/switch.tsx +32 -0
- package/plugin/dashboard/components.json +25 -0
- package/plugin/dashboard/eslint.config.mjs +16 -0
- package/plugin/dashboard/hooks/use-settings.tsx +88 -0
- package/plugin/dashboard/hooks/use-stall-state.ts +59 -0
- package/plugin/dashboard/lib/claude-settings-file.ts +114 -0
- package/plugin/dashboard/lib/config-file.ts +131 -0
- package/plugin/dashboard/lib/format.ts +58 -0
- package/plugin/dashboard/lib/reflexio-client.ts +238 -0
- package/plugin/dashboard/lib/reflexio-url.ts +17 -0
- package/plugin/dashboard/lib/session-reader.ts +245 -0
- package/plugin/dashboard/lib/status.ts +24 -0
- package/plugin/dashboard/lib/types.ts +145 -0
- package/plugin/dashboard/lib/utils.ts +6 -0
- package/plugin/dashboard/next.config.ts +7 -0
- package/plugin/dashboard/package-lock.json +10275 -0
- package/plugin/dashboard/package.json +37 -0
- package/plugin/dashboard/postcss.config.mjs +7 -0
- package/plugin/dashboard/public/claude-smart-icon.png +0 -0
- package/plugin/dashboard/tsconfig.json +34 -0
- package/plugin/hooks/codex-hooks.json +67 -0
- package/plugin/hooks/hooks.json +111 -0
- package/plugin/pyproject.toml +49 -0
- package/plugin/scripts/_codex_env.sh +27 -0
- package/plugin/scripts/_lib.sh +325 -0
- package/plugin/scripts/backend-service.sh +208 -0
- package/plugin/scripts/cli.sh +40 -0
- package/plugin/scripts/dashboard-build.sh +139 -0
- package/plugin/scripts/dashboard-open.sh +107 -0
- package/plugin/scripts/dashboard-service.sh +195 -0
- package/plugin/scripts/ensure-plugin-root.sh +84 -0
- package/plugin/scripts/hook_entry.sh +70 -0
- package/plugin/scripts/smart-install.sh +411 -0
- package/plugin/src/claude_smart/__init__.py +3 -0
- package/plugin/src/claude_smart/cli.py +1273 -0
- package/plugin/src/claude_smart/context_format.py +277 -0
- package/plugin/src/claude_smart/context_inject.py +92 -0
- package/plugin/src/claude_smart/cs_cite.py +236 -0
- package/plugin/src/claude_smart/events/__init__.py +1 -0
- package/plugin/src/claude_smart/events/post_tool.py +148 -0
- package/plugin/src/claude_smart/events/pre_tool.py +52 -0
- package/plugin/src/claude_smart/events/session_end.py +20 -0
- package/plugin/src/claude_smart/events/session_start.py +119 -0
- package/plugin/src/claude_smart/events/stop.py +393 -0
- package/plugin/src/claude_smart/events/user_prompt.py +73 -0
- package/plugin/src/claude_smart/hook.py +114 -0
- package/plugin/src/claude_smart/ids.py +56 -0
- package/plugin/src/claude_smart/internal_call.py +89 -0
- package/plugin/src/claude_smart/optimizer_assistant.py +203 -0
- package/plugin/src/claude_smart/publish.py +71 -0
- package/plugin/src/claude_smart/query_compose.py +51 -0
- package/plugin/src/claude_smart/reflexio_adapter.py +403 -0
- package/plugin/src/claude_smart/runtime.py +52 -0
- package/plugin/src/claude_smart/stall_banner.py +61 -0
- package/plugin/src/claude_smart/state.py +276 -0
- package/plugin/uv.lock +3720 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-smart-dashboard",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev -p 3001 -H 127.0.0.1",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start -p 3001 -H 127.0.0.1",
|
|
9
|
+
"lint": "eslint"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@base-ui/react": "^1.3.0",
|
|
13
|
+
"class-variance-authority": "^0.7.1",
|
|
14
|
+
"clsx": "^2.1.1",
|
|
15
|
+
"lucide-react": "^0.577.0",
|
|
16
|
+
"next": "16.2.0",
|
|
17
|
+
"next-themes": "^0.4.6",
|
|
18
|
+
"react": "19.2.4",
|
|
19
|
+
"react-dom": "19.2.4",
|
|
20
|
+
"tailwind-merge": "^3.5.0",
|
|
21
|
+
"tw-animate-css": "^1.4.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@tailwindcss/postcss": "^4",
|
|
25
|
+
"@types/node": "^20",
|
|
26
|
+
"@types/react": "^19",
|
|
27
|
+
"@types/react-dom": "^19",
|
|
28
|
+
"eslint": "^9",
|
|
29
|
+
"eslint-config-next": "16.2.0",
|
|
30
|
+
"shadcn": "^4.0.8",
|
|
31
|
+
"tailwindcss": "^4",
|
|
32
|
+
"typescript": "^5"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20.9.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [
|
|
17
|
+
{
|
|
18
|
+
"name": "next"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"paths": {
|
|
22
|
+
"@/*": ["./*"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"include": [
|
|
26
|
+
"next-env.d.ts",
|
|
27
|
+
"**/*.ts",
|
|
28
|
+
"**/*.tsx",
|
|
29
|
+
".next/types/**/*.ts",
|
|
30
|
+
".next/dev/types/**/*.ts",
|
|
31
|
+
"**/*.mts"
|
|
32
|
+
],
|
|
33
|
+
"exclude": ["node_modules"]
|
|
34
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "claude-smart hooks for Codex — learn from sessions via reflexio",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "startup|resume",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -z \"$_R\" ] && _R=$(ls -dt \"$HOME/plugins/claude-smart\" \"$HOME/.codex/plugins/cache/reflexioai/claude-smart\"/*/ 2>/dev/null | head -n 1); [ -n \"$_R\" ] && . \"${_R%/}/scripts/_codex_env.sh\" && bash \"$_R/scripts/ensure-plugin-root.sh\" \"$_R\" || true",
|
|
11
|
+
"timeout": 10
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"type": "command",
|
|
15
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -z \"$_R\" ] && _R=$(ls -dt \"$HOME/plugins/claude-smart\" \"$HOME/.codex/plugins/cache/reflexioai/claude-smart\"/*/ 2>/dev/null | head -n 1); [ -n \"$_R\" ] && . \"${_R%/}/scripts/_codex_env.sh\" && bash \"$_R/scripts/backend-service.sh\" start",
|
|
16
|
+
"timeout": 30
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -z \"$_R\" ] && _R=$(ls -dt \"$HOME/plugins/claude-smart\" \"$HOME/.codex/plugins/cache/reflexioai/claude-smart\"/*/ 2>/dev/null | head -n 1); [ -n \"$_R\" ] && . \"${_R%/}/scripts/_codex_env.sh\" && bash \"$_R/scripts/dashboard-service.sh\" start",
|
|
21
|
+
"timeout": 10
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "command",
|
|
25
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -z \"$_R\" ] && _R=$(ls -dt \"$HOME/plugins/claude-smart\" \"$HOME/.codex/plugins/cache/reflexioai/claude-smart\"/*/ 2>/dev/null | head -n 1); [ -n \"$_R\" ] && . \"${_R%/}/scripts/_codex_env.sh\" && bash \"$_R/scripts/hook_entry.sh\" codex session-start",
|
|
26
|
+
"timeout": 30,
|
|
27
|
+
"statusMessage": "Loading claude-smart context"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"UserPromptSubmit": [
|
|
33
|
+
{
|
|
34
|
+
"hooks": [
|
|
35
|
+
{
|
|
36
|
+
"type": "command",
|
|
37
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -z \"$_R\" ] && _R=$(ls -dt \"$HOME/plugins/claude-smart\" \"$HOME/.codex/plugins/cache/reflexioai/claude-smart\"/*/ 2>/dev/null | head -n 1); [ -n \"$_R\" ] && . \"${_R%/}/scripts/_codex_env.sh\" && bash \"$_R/scripts/hook_entry.sh\" codex user-prompt",
|
|
38
|
+
"timeout": 15
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"PostToolUse": [
|
|
44
|
+
{
|
|
45
|
+
"matcher": ".*",
|
|
46
|
+
"hooks": [
|
|
47
|
+
{
|
|
48
|
+
"type": "command",
|
|
49
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -z \"$_R\" ] && _R=$(ls -dt \"$HOME/plugins/claude-smart\" \"$HOME/.codex/plugins/cache/reflexioai/claude-smart\"/*/ 2>/dev/null | head -n 1); [ -n \"$_R\" ] && . \"${_R%/}/scripts/_codex_env.sh\" && bash \"$_R/scripts/hook_entry.sh\" codex post-tool",
|
|
50
|
+
"timeout": 15
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"Stop": [
|
|
56
|
+
{
|
|
57
|
+
"hooks": [
|
|
58
|
+
{
|
|
59
|
+
"type": "command",
|
|
60
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}\"; [ -z \"$_R\" ] && _R=$(ls -dt \"$HOME/plugins/claude-smart\" \"$HOME/.codex/plugins/cache/reflexioai/claude-smart\"/*/ 2>/dev/null | head -n 1); [ -n \"$_R\" ] && . \"${_R%/}/scripts/_codex_env.sh\" && bash \"$_R/scripts/hook_entry.sh\" codex stop",
|
|
61
|
+
"timeout": 30
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "claude-smart hooks — learn from Claude Code sessions via reflexio",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"Setup": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "*",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/smart-install.sh\"",
|
|
11
|
+
"timeout": 300
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"SessionStart": [
|
|
17
|
+
{
|
|
18
|
+
"matcher": "startup|clear|compact|resume",
|
|
19
|
+
"hooks": [
|
|
20
|
+
{
|
|
21
|
+
"type": "command",
|
|
22
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/ensure-plugin-root.sh\" \"$_R\" || true",
|
|
23
|
+
"timeout": 10
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "command",
|
|
27
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/hook_entry.sh\" claude-code session-start",
|
|
28
|
+
"timeout": 30
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"type": "command",
|
|
32
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/backend-service.sh\" start",
|
|
33
|
+
"timeout": 30
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"type": "command",
|
|
37
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/dashboard-service.sh\" start",
|
|
38
|
+
"timeout": 10
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"UserPromptSubmit": [
|
|
44
|
+
{
|
|
45
|
+
"hooks": [
|
|
46
|
+
{
|
|
47
|
+
"type": "command",
|
|
48
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/hook_entry.sh\" claude-code user-prompt",
|
|
49
|
+
"timeout": 15
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"PreToolUse": [
|
|
55
|
+
{
|
|
56
|
+
"matcher": "Edit|Write|NotebookEdit|Bash",
|
|
57
|
+
"hooks": [
|
|
58
|
+
{
|
|
59
|
+
"type": "command",
|
|
60
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/hook_entry.sh\" claude-code pre-tool",
|
|
61
|
+
"timeout": 10
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"PostToolUse": [
|
|
67
|
+
{
|
|
68
|
+
"matcher": "*",
|
|
69
|
+
"hooks": [
|
|
70
|
+
{
|
|
71
|
+
"type": "command",
|
|
72
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/hook_entry.sh\" claude-code post-tool",
|
|
73
|
+
"timeout": 15
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"Stop": [
|
|
79
|
+
{
|
|
80
|
+
"hooks": [
|
|
81
|
+
{
|
|
82
|
+
"type": "command",
|
|
83
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/hook_entry.sh\" claude-code stop",
|
|
84
|
+
"timeout": 30
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"SessionEnd": [
|
|
90
|
+
{
|
|
91
|
+
"hooks": [
|
|
92
|
+
{
|
|
93
|
+
"type": "command",
|
|
94
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/hook_entry.sh\" claude-code session-end",
|
|
95
|
+
"timeout": 60
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"type": "command",
|
|
99
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/dashboard-service.sh\" session-end",
|
|
100
|
+
"timeout": 10
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"type": "command",
|
|
104
|
+
"command": "_R=\"${CLAUDE_PLUGIN_ROOT}\"; [ -z \"$_R\" ] && _R=\"$HOME/.claude/plugins/marketplaces/reflexioai/plugin\"; bash \"$_R/scripts/backend-service.sh\" session-end",
|
|
105
|
+
"timeout": 10
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "claude-smart"
|
|
3
|
+
version = "0.2.24"
|
|
4
|
+
description = "Self-improving Claude Code plugin — learns from corrections via reflexio"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"reflexio-ai>=0.2.22",
|
|
9
|
+
# Used by reflexio's local embedding provider (ONNXMiniLM_L6_V2).
|
|
10
|
+
# Pulls in onnxruntime + tokenizers; the ~80 MB ONNX model itself is
|
|
11
|
+
# downloaded on first use, not at install time.
|
|
12
|
+
"chromadb>=0.5",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
claude-smart-hook = "claude_smart.hook:main"
|
|
17
|
+
claude-smart = "claude_smart.cli:main"
|
|
18
|
+
claude-smart-optimizer-assistant = "claude_smart.optimizer_assistant:main"
|
|
19
|
+
|
|
20
|
+
# NOTE: LOCAL DEV ONLY — do not commit this block to main.
|
|
21
|
+
# Published installs must resolve reflexio-ai from PyPI. This override
|
|
22
|
+
# points at the vendored submodule so editable changes flow through.
|
|
23
|
+
# `git update-index --skip-worktree plugin/pyproject.toml plugin/uv.lock`
|
|
24
|
+
# hides the local divergence from `git status`.
|
|
25
|
+
# [tool.uv.sources]
|
|
26
|
+
# reflexio-ai = { path = "/Users/yilu/repos/claude-smart/reflexio", editable = true }
|
|
27
|
+
|
|
28
|
+
[build-system]
|
|
29
|
+
requires = ["hatchling"]
|
|
30
|
+
build-backend = "hatchling.build"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build.targets.wheel]
|
|
33
|
+
packages = ["src/claude_smart"]
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.sdist]
|
|
36
|
+
include = [
|
|
37
|
+
"src/claude_smart",
|
|
38
|
+
"pyproject.toml",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["../tests"]
|
|
45
|
+
|
|
46
|
+
[dependency-groups]
|
|
47
|
+
dev = [
|
|
48
|
+
"pytest>=9.0.3",
|
|
49
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Shared bootstrap for Codex hook commands. This file is sourced by hook
|
|
2
|
+
# entries after a tiny root-discovery step, so keep it POSIX-shell compatible.
|
|
3
|
+
|
|
4
|
+
_HP=$(printenv PATH 2>/dev/null || true)
|
|
5
|
+
if [ -z "$_HP" ] && [ -n "${SHELL:-}" ]; then
|
|
6
|
+
_HP=$("$SHELL" -lc 'printf %s "$PATH"' 2>/dev/null || true)
|
|
7
|
+
fi
|
|
8
|
+
_HP=$(printf '%s' "$_HP" | tr ' ' ':')
|
|
9
|
+
export PATH="${_HP:+$_HP:}$PATH"
|
|
10
|
+
|
|
11
|
+
if [ -z "${_R:-}" ]; then
|
|
12
|
+
_R="${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}"
|
|
13
|
+
fi
|
|
14
|
+
if [ -z "$_R" ]; then
|
|
15
|
+
_R=$(ls -dt "$HOME/plugins/claude-smart" \
|
|
16
|
+
"$HOME/.codex/plugins/cache/reflexioai/claude-smart"/*/ \
|
|
17
|
+
2>/dev/null | head -n 1)
|
|
18
|
+
fi
|
|
19
|
+
if [ -z "$_R" ]; then
|
|
20
|
+
echo "claude-smart: plugin root not found" >&2
|
|
21
|
+
return 1 2>/dev/null || exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
_R="${_R%/}"
|
|
25
|
+
export _R
|
|
26
|
+
export PLUGIN_ROOT="$_R"
|
|
27
|
+
export CLAUDE_PLUGIN_ROOT="$_R"
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Shared helpers for claude-smart plugin scripts. Source, do not execute.
|
|
2
|
+
|
|
3
|
+
# Claude Code hooks run with a minimal non-interactive PATH that often omits
|
|
4
|
+
# nvm/asdf/brew shims where `npm`, `uv`, etc. live. Pull the user's login-shell
|
|
5
|
+
# PATH the same way claude-mem does so hook-spawned scripts find them without
|
|
6
|
+
# the user having to mutate their global PATH. Best-effort — failures silent.
|
|
7
|
+
claude_smart_source_login_path() {
|
|
8
|
+
local _SHELL_PATH
|
|
9
|
+
if [ -n "${SHELL:-}" ] && [ -x "$SHELL" ]; then
|
|
10
|
+
if _SHELL_PATH="$("$SHELL" -lc 'printf %s "$PATH"' 2>/dev/null)"; then
|
|
11
|
+
[ -n "$_SHELL_PATH" ] && export PATH="$_SHELL_PATH:$PATH"
|
|
12
|
+
fi
|
|
13
|
+
fi
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# Prepend the astral.sh installer's default bin directories to PATH so a
|
|
17
|
+
# freshly-installed `uv` is reachable before the user re-sources their
|
|
18
|
+
# shell rc. Prepend (not append) so the just-installed binary wins over
|
|
19
|
+
# any stale copy earlier in PATH. Literals only — no subshell, so safe
|
|
20
|
+
# under `set -u`.
|
|
21
|
+
claude_smart_prepend_astral_bins() {
|
|
22
|
+
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Prepend claude-smart's private Node.js install, if present. The Setup
|
|
26
|
+
# hook installs Node here when the user does not already have a suitable
|
|
27
|
+
# node/npm on PATH, so later hook-spawned dashboard scripts can run without
|
|
28
|
+
# requiring nvm/brew/global npm to be visible from Claude Code's shell.
|
|
29
|
+
claude_smart_prepend_node_bins() {
|
|
30
|
+
local _CS_NODE_ROOT
|
|
31
|
+
_CS_NODE_ROOT="$HOME/.claude-smart/node/current"
|
|
32
|
+
export PATH="$_CS_NODE_ROOT/bin:$_CS_NODE_ROOT:$PATH"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
claude_smart_dashboard_unavailable_marker() {
|
|
36
|
+
printf '%s\n' "$HOME/.claude-smart/dashboard-unavailable"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
claude_smart_node_recovery_hint() {
|
|
40
|
+
cat <<'EOF'
|
|
41
|
+
Recovery:
|
|
42
|
+
1. Restart Claude Code to let claude-smart retry its private Node.js install.
|
|
43
|
+
2. If the retry is blocked by your network or OS policy, install Node.js 20.9+ manually:
|
|
44
|
+
EOF
|
|
45
|
+
if claude_smart_is_windows; then
|
|
46
|
+
cat <<'EOF'
|
|
47
|
+
- winget install OpenJS.NodeJS.LTS
|
|
48
|
+
- or download the LTS installer from https://nodejs.org/
|
|
49
|
+
EOF
|
|
50
|
+
elif [ "$(uname -s 2>/dev/null)" = "Darwin" ]; then
|
|
51
|
+
cat <<'EOF'
|
|
52
|
+
- brew install node
|
|
53
|
+
- or download the LTS installer from https://nodejs.org/
|
|
54
|
+
EOF
|
|
55
|
+
else
|
|
56
|
+
cat <<'EOF'
|
|
57
|
+
- use your distro package manager for nodejs/npm
|
|
58
|
+
- or download the LTS archive from https://nodejs.org/
|
|
59
|
+
EOF
|
|
60
|
+
fi
|
|
61
|
+
cat <<'EOF'
|
|
62
|
+
3. Run /claude-smart:restart, then /claude-smart:dashboard.
|
|
63
|
+
EOF
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
claude_smart_write_dashboard_unavailable() {
|
|
67
|
+
local reason marker
|
|
68
|
+
reason="$1"
|
|
69
|
+
marker="$(claude_smart_dashboard_unavailable_marker)"
|
|
70
|
+
mkdir -p "$(dirname "$marker")"
|
|
71
|
+
{
|
|
72
|
+
printf 'claude-smart dashboard is unavailable: %s\n\n' "$reason"
|
|
73
|
+
printf 'The learning backend and hooks can still work; only the dashboard UI is affected.\n\n'
|
|
74
|
+
printf 'Private Node.js location: %s\n\n' "$HOME/.claude-smart/node/current"
|
|
75
|
+
claude_smart_node_recovery_hint
|
|
76
|
+
} > "$marker"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
claude_smart_clear_dashboard_unavailable() {
|
|
80
|
+
rm -f "$(claude_smart_dashboard_unavailable_marker)"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Return 0 (true) if running under a Windows-flavoured bash (Git Bash,
|
|
84
|
+
# MSYS, Cygwin). Used to gate POSIX-only primitives (setsid, process
|
|
85
|
+
# groups) and route around Windows-specific potholes (the python3 App
|
|
86
|
+
# Execution Alias stub at WindowsApps\python3.exe).
|
|
87
|
+
claude_smart_is_windows() {
|
|
88
|
+
case "$(uname -s 2>/dev/null)" in
|
|
89
|
+
MINGW*|MSYS*|CYGWIN*) return 0 ;;
|
|
90
|
+
*) return 1 ;;
|
|
91
|
+
esac
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Print the absolute path of a working python interpreter, or nothing
|
|
95
|
+
# (and return non-zero) if none is usable. On Windows, `python3` is
|
|
96
|
+
# usually the Microsoft Store "App Execution Alias" stub at
|
|
97
|
+
# %LocalAppData%\Microsoft\WindowsApps\python3.exe — `command -v python3`
|
|
98
|
+
# returns truthy but invoking it just prints a "Python was not found"
|
|
99
|
+
# message and exits non-zero. We probe with `-V` to filter the stub out
|
|
100
|
+
# and prefer `python` (the real interpreter when one is installed).
|
|
101
|
+
claude_smart_resolve_python() {
|
|
102
|
+
if claude_smart_is_windows; then
|
|
103
|
+
for cand in python python3; do
|
|
104
|
+
if command -v "$cand" >/dev/null 2>&1 && "$cand" -V >/dev/null 2>&1; then
|
|
105
|
+
command -v "$cand"
|
|
106
|
+
return 0
|
|
107
|
+
fi
|
|
108
|
+
done
|
|
109
|
+
return 1
|
|
110
|
+
fi
|
|
111
|
+
for cand in python3 python; do
|
|
112
|
+
if command -v "$cand" >/dev/null 2>&1 && "$cand" -V >/dev/null 2>&1; then
|
|
113
|
+
command -v "$cand"
|
|
114
|
+
return 0
|
|
115
|
+
fi
|
|
116
|
+
done
|
|
117
|
+
return 1
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
claude_smart_download() {
|
|
121
|
+
local url dest src _CS_PY
|
|
122
|
+
url="$1"
|
|
123
|
+
dest="$2"
|
|
124
|
+
mkdir -p "$(dirname "$dest")"
|
|
125
|
+
case "$url" in
|
|
126
|
+
file://*)
|
|
127
|
+
src="${url#file://}"
|
|
128
|
+
cp "$src" "$dest"
|
|
129
|
+
return $?
|
|
130
|
+
;;
|
|
131
|
+
esac
|
|
132
|
+
if command -v curl >/dev/null 2>&1; then
|
|
133
|
+
curl -fsSL "$url" -o "$dest"
|
|
134
|
+
return $?
|
|
135
|
+
fi
|
|
136
|
+
if command -v wget >/dev/null 2>&1; then
|
|
137
|
+
wget -q -O "$dest" "$url"
|
|
138
|
+
return $?
|
|
139
|
+
fi
|
|
140
|
+
if command -v powershell >/dev/null 2>&1; then
|
|
141
|
+
URL="$url" DEST="$dest" powershell -NoProfile -ExecutionPolicy Bypass -Command \
|
|
142
|
+
'$ProgressPreference="SilentlyContinue"; Invoke-WebRequest -Uri $env:URL -OutFile $env:DEST'
|
|
143
|
+
return $?
|
|
144
|
+
fi
|
|
145
|
+
if command -v pwsh >/dev/null 2>&1; then
|
|
146
|
+
URL="$url" DEST="$dest" pwsh -NoProfile -Command \
|
|
147
|
+
'$ProgressPreference="SilentlyContinue"; Invoke-WebRequest -Uri $env:URL -OutFile $env:DEST'
|
|
148
|
+
return $?
|
|
149
|
+
fi
|
|
150
|
+
_CS_PY=$(claude_smart_resolve_python || true)
|
|
151
|
+
if [ -n "$_CS_PY" ]; then
|
|
152
|
+
"$_CS_PY" - "$url" "$dest" <<'PY'
|
|
153
|
+
import sys
|
|
154
|
+
import urllib.request
|
|
155
|
+
|
|
156
|
+
url, dest = sys.argv[1], sys.argv[2]
|
|
157
|
+
with urllib.request.urlopen(url, timeout=60) as response:
|
|
158
|
+
data = response.read()
|
|
159
|
+
with open(dest, "wb") as fh:
|
|
160
|
+
fh.write(data)
|
|
161
|
+
PY
|
|
162
|
+
return $?
|
|
163
|
+
fi
|
|
164
|
+
return 1
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
claude_smart_sha256_file() {
|
|
168
|
+
local path _CS_PY
|
|
169
|
+
path="$1"
|
|
170
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
171
|
+
sha256sum "$path" | awk '{print $1}'
|
|
172
|
+
return ${PIPESTATUS[0]}
|
|
173
|
+
fi
|
|
174
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
175
|
+
shasum -a 256 "$path" | awk '{print $1}'
|
|
176
|
+
return ${PIPESTATUS[0]}
|
|
177
|
+
fi
|
|
178
|
+
if command -v openssl >/dev/null 2>&1; then
|
|
179
|
+
openssl dgst -sha256 "$path" | awk '{print $NF}'
|
|
180
|
+
return ${PIPESTATUS[0]}
|
|
181
|
+
fi
|
|
182
|
+
_CS_PY=$(claude_smart_resolve_python || true)
|
|
183
|
+
if [ -n "$_CS_PY" ]; then
|
|
184
|
+
"$_CS_PY" - "$path" <<'PY'
|
|
185
|
+
import hashlib
|
|
186
|
+
import sys
|
|
187
|
+
|
|
188
|
+
h = hashlib.sha256()
|
|
189
|
+
with open(sys.argv[1], "rb") as fh:
|
|
190
|
+
for chunk in iter(lambda: fh.read(1024 * 1024), b""):
|
|
191
|
+
h.update(chunk)
|
|
192
|
+
print(h.hexdigest())
|
|
193
|
+
PY
|
|
194
|
+
return $?
|
|
195
|
+
fi
|
|
196
|
+
return 1
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# Return 0 if `node` exists and satisfies the minimum major/minor pair.
|
|
200
|
+
# Patch versions are intentionally ignored because our requirement is a
|
|
201
|
+
# floor, not an exact runtime pin.
|
|
202
|
+
claude_smart_node_satisfies() {
|
|
203
|
+
local min_major min_minor version major minor
|
|
204
|
+
min_major="$1"
|
|
205
|
+
min_minor="$2"
|
|
206
|
+
command -v node >/dev/null 2>&1 || return 1
|
|
207
|
+
version=$(node -v 2>/dev/null | sed 's/^v//') || return 1
|
|
208
|
+
major=$(printf '%s' "$version" | awk -F. '{print $1}')
|
|
209
|
+
minor=$(printf '%s' "$version" | awk -F. '{print $2}')
|
|
210
|
+
[ -n "$major" ] && [ -n "$minor" ] || return 1
|
|
211
|
+
case "$major:$minor" in
|
|
212
|
+
*[!0-9:]*|:*|*:) return 1 ;;
|
|
213
|
+
esac
|
|
214
|
+
[ "$major" -gt "$min_major" ] && return 0
|
|
215
|
+
[ "$major" -eq "$min_major" ] && [ "$minor" -ge "$min_minor" ]
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
claude_smart_resolve_npm() {
|
|
219
|
+
local cand
|
|
220
|
+
for cand in npm npm.cmd; do
|
|
221
|
+
if command -v "$cand" >/dev/null 2>&1; then
|
|
222
|
+
command -v "$cand"
|
|
223
|
+
return 0
|
|
224
|
+
fi
|
|
225
|
+
done
|
|
226
|
+
return 1
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
claude_smart_npm_available() {
|
|
230
|
+
local npm_bin
|
|
231
|
+
npm_bin=$(claude_smart_resolve_npm || true)
|
|
232
|
+
[ -n "$npm_bin" ] || return 1
|
|
233
|
+
"$npm_bin" --version >/dev/null 2>&1
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# Spawn a command fully detached from the current shell so a hook timeout
|
|
237
|
+
# (Claude Code's install/SessionStart budget) cannot kill it mid-flight.
|
|
238
|
+
# POSIX: setsid → python3 os.setsid → nohup (in that order of strength).
|
|
239
|
+
# Windows: nohup alone — Git Bash has no setsid, no process groups, and
|
|
240
|
+
# `os.setsid()` is POSIX-only; nohup ignores SIGHUP which is enough to
|
|
241
|
+
# survive the parent console closing. The python3 fallback is gated on a
|
|
242
|
+
# real-interpreter probe (-V) so the Windows App Execution Alias stub
|
|
243
|
+
# doesn't get invoked. Caller is responsible for redirecting stdout/stderr;
|
|
244
|
+
# we do not impose a log destination here. Stdin is closed so the child
|
|
245
|
+
# cannot inherit a tty. Use `$!` after this call to capture the pid.
|
|
246
|
+
claude_smart_spawn_detached() {
|
|
247
|
+
if claude_smart_is_windows; then
|
|
248
|
+
nohup "$@" < /dev/null &
|
|
249
|
+
return 0
|
|
250
|
+
fi
|
|
251
|
+
if command -v setsid >/dev/null 2>&1; then
|
|
252
|
+
setsid nohup "$@" < /dev/null &
|
|
253
|
+
elif _CS_PY=$(claude_smart_resolve_python) && [ -n "$_CS_PY" ]; then
|
|
254
|
+
"$_CS_PY" -c 'import os,sys; os.setsid(); os.execvp(sys.argv[1], sys.argv[1:])' \
|
|
255
|
+
"$@" < /dev/null &
|
|
256
|
+
else
|
|
257
|
+
nohup "$@" < /dev/null &
|
|
258
|
+
fi
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Terminate a process and (on POSIX) its whole process group, escalating
|
|
262
|
+
# from TERM to KILL after a short grace period. On Windows there are no
|
|
263
|
+
# POSIX process groups, so we use `taskkill /T /F /PID` which walks the
|
|
264
|
+
# child-process tree via the Windows job-object/parent-pid relationships
|
|
265
|
+
# — the closest equivalent to a group kill.
|
|
266
|
+
#
|
|
267
|
+
# Windows-specific subtlety: in Git Bash / MSYS, `$!` for a backgrounded
|
|
268
|
+
# job returns the MSYS pid (an internal counter), NOT the native Windows
|
|
269
|
+
# pid that taskkill needs. `ps -W` (or `-o winpid=`) exposes the WINPID
|
|
270
|
+
# column for the translation. If the lookup fails we fall back to
|
|
271
|
+
# treating the input as a native pid, so callers can pass either an MSYS
|
|
272
|
+
# pid (recorded via $!) or a Windows pid (from tasklist) interchangeably.
|
|
273
|
+
# The `//T //F //PID` syntax escapes Git Bash's MSYS path-mangling of
|
|
274
|
+
# arguments that begin with `/`.
|
|
275
|
+
claude_smart_kill_tree() {
|
|
276
|
+
pid="$1"
|
|
277
|
+
[ -z "$pid" ] && return 0
|
|
278
|
+
if claude_smart_is_windows; then
|
|
279
|
+
# Git Bash's `ps` is the procps fork, not BSD/Linux ps; it has no
|
|
280
|
+
# -o option but its default header is `PID PPID PGID WINPID TTY ...`,
|
|
281
|
+
# so column 4 of the data row is the Windows pid. awk extracts it
|
|
282
|
+
# without depending on -o support.
|
|
283
|
+
target=""
|
|
284
|
+
if command -v ps >/dev/null 2>&1; then
|
|
285
|
+
target=$(ps -p "$pid" 2>/dev/null | awk 'NR==2 {print $4}' | tr -d ' \r\n' || true)
|
|
286
|
+
fi
|
|
287
|
+
[ -z "$target" ] && target="$pid"
|
|
288
|
+
if command -v taskkill >/dev/null 2>&1; then
|
|
289
|
+
taskkill //T //F //PID "$target" >/dev/null 2>&1 || true
|
|
290
|
+
else
|
|
291
|
+
kill -TERM "$pid" 2>/dev/null || true
|
|
292
|
+
sleep 0.5
|
|
293
|
+
kill -KILL "$pid" 2>/dev/null || true
|
|
294
|
+
fi
|
|
295
|
+
return 0
|
|
296
|
+
fi
|
|
297
|
+
current_pgid=""
|
|
298
|
+
if command -v ps >/dev/null 2>&1; then
|
|
299
|
+
current_pgid=$(ps -o pgid= -p "$$" 2>/dev/null | tr -d ' ')
|
|
300
|
+
fi
|
|
301
|
+
if [ -n "$current_pgid" ] && [ "$pid" = "$current_pgid" ]; then
|
|
302
|
+
return 0
|
|
303
|
+
fi
|
|
304
|
+
if ! kill -TERM -- "-$pid" 2>/dev/null; then
|
|
305
|
+
kill -TERM "$pid" 2>/dev/null || true
|
|
306
|
+
sleep 0.5
|
|
307
|
+
kill -KILL "$pid" 2>/dev/null || true
|
|
308
|
+
return 0
|
|
309
|
+
fi
|
|
310
|
+
for _ in 1 2 3 4 5; do
|
|
311
|
+
kill -0 -- "-$pid" 2>/dev/null || return 0
|
|
312
|
+
sleep 0.2
|
|
313
|
+
done
|
|
314
|
+
kill -KILL -- "-$pid" 2>/dev/null || true
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
# Return 0 (true) if $1 names a pid file whose pid is currently alive.
|
|
318
|
+
# Silent on missing/empty/stale files.
|
|
319
|
+
claude_smart_pid_alive_file() {
|
|
320
|
+
pid_file="$1"
|
|
321
|
+
[ -f "$pid_file" ] || return 1
|
|
322
|
+
pid=$(cat "$pid_file" 2>/dev/null || echo "")
|
|
323
|
+
[ -n "$pid" ] || return 1
|
|
324
|
+
kill -0 "$pid" 2>/dev/null
|
|
325
|
+
}
|