arkaos 4.3.2 → 4.3.3
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/VERSION +1 -1
- package/bin/arka +225 -0
- package/bin/arka-claude +64 -0
- package/bin/arka-claude.cmd +11 -0
- package/bin/arka-claude.ps1 +126 -0
- package/bin/arka-doctor +391 -0
- package/bin/arka-providers +261 -0
- package/bin/arka-py +46 -0
- package/bin/arka-py.cmd +9 -0
- package/bin/arka-py.ps1 +36 -0
- package/bin/arka-registry-gen +185 -0
- package/bin/arka-skill +300 -0
- package/bin/scheduler-daemon.py +48 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/cost_governor.cpython-312.pyc +0 -0
- package/installer/update.js +18 -0
- package/package.json +2 -2
- package/pyproject.toml +1 -1
package/bin/arka-doctor
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# ARKA OS — Doctor (Health Check System)
|
|
4
|
+
# Usage: arka doctor [--fix] [--json]
|
|
5
|
+
# 16 modular checks: fail = critical, warn = advisory
|
|
6
|
+
# ============================================================================
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# ─── Args ──────────────────────────────────────────────────────────────────
|
|
10
|
+
FIX_MODE=false
|
|
11
|
+
JSON_MODE=false
|
|
12
|
+
for arg in "$@"; do
|
|
13
|
+
case "$arg" in
|
|
14
|
+
--fix) FIX_MODE=true ;;
|
|
15
|
+
--json) JSON_MODE=true ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
# ─── Colors ────────────────────────────────────────────────────────────────
|
|
20
|
+
if [ "$JSON_MODE" = true ]; then
|
|
21
|
+
# No colors in JSON mode
|
|
22
|
+
G="" Y="" R="" C="" N="" B=""
|
|
23
|
+
else
|
|
24
|
+
G='\033[0;32m' # green
|
|
25
|
+
Y='\033[1;33m' # yellow
|
|
26
|
+
R='\033[0;31m' # red
|
|
27
|
+
C='\033[0;36m' # cyan
|
|
28
|
+
N='\033[0m' # reset
|
|
29
|
+
B='\033[1m' # bold
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
ARKA_OS="${ARKA_OS:-$HOME/.claude/skills/arka}"
|
|
33
|
+
RESULTS=()
|
|
34
|
+
PASS_COUNT=0
|
|
35
|
+
WARN_COUNT=0
|
|
36
|
+
FAIL_COUNT=0
|
|
37
|
+
|
|
38
|
+
# ─── Check Runner ──────────────────────────────────────────────────────────
|
|
39
|
+
# run_check <name> <severity:fail|warn> <description> <pass:true|false> [fix_message]
|
|
40
|
+
run_check() {
|
|
41
|
+
local name="$1" severity="$2" desc="$3" passed="$4" fix_msg="${5:-}"
|
|
42
|
+
local status icon
|
|
43
|
+
|
|
44
|
+
if [ "$passed" = true ]; then
|
|
45
|
+
status="pass"
|
|
46
|
+
icon="${G}✓${N}"
|
|
47
|
+
PASS_COUNT=$((PASS_COUNT + 1))
|
|
48
|
+
elif [ "$severity" = "fail" ]; then
|
|
49
|
+
status="fail"
|
|
50
|
+
icon="${R}✗${N}"
|
|
51
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
52
|
+
else
|
|
53
|
+
status="warn"
|
|
54
|
+
icon="${Y}⚠${N}"
|
|
55
|
+
WARN_COUNT=$((WARN_COUNT + 1))
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
if [ "$JSON_MODE" = true ]; then
|
|
59
|
+
RESULTS+=("{\"name\":\"$name\",\"status\":\"$status\",\"severity\":\"$severity\",\"description\":\"$desc\",\"fix\":\"$fix_msg\"}")
|
|
60
|
+
else
|
|
61
|
+
printf " %b %-20s %s" "$icon" "$name" "$desc"
|
|
62
|
+
if [ "$status" != "pass" ] && [ -n "$fix_msg" ]; then
|
|
63
|
+
printf " ${Y}(%s)${N}" "$fix_msg"
|
|
64
|
+
fi
|
|
65
|
+
echo ""
|
|
66
|
+
fi
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ─── Header ────────────────────────────────────────────────────────────────
|
|
70
|
+
if [ "$JSON_MODE" != true ]; then
|
|
71
|
+
echo ""
|
|
72
|
+
echo -e "${C}╔══════════════════════════════════════════════════╗${N}"
|
|
73
|
+
echo -e "${C}║${N} ${B}ARKA OS Doctor${N} — Health Check System ${C}║${N}"
|
|
74
|
+
echo -e "${C}╚══════════════════════════════════════════════════╝${N}"
|
|
75
|
+
echo ""
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# ─── Check 1: Claude CLI ──────────────────────────────────────────────────
|
|
79
|
+
CLAUDE_OK=false
|
|
80
|
+
if command -v claude &>/dev/null; then
|
|
81
|
+
CLAUDE_OK=true
|
|
82
|
+
fi
|
|
83
|
+
run_check "claude-cli" "fail" "Claude Code CLI installed" "$CLAUDE_OK" "Install: npm install -g @anthropic-ai/claude-code"
|
|
84
|
+
|
|
85
|
+
# ─── Check 2: ARKA OS Install ─────────────────────────────────────────────
|
|
86
|
+
ARKA_OK=false
|
|
87
|
+
ARKA_DESC="ARKA OS installed"
|
|
88
|
+
if [ -d "$ARKA_OS" ] && [ -f "$ARKA_OS/SKILL.md" ]; then
|
|
89
|
+
ARKA_OK=true
|
|
90
|
+
ARKA_VER=$(cat "$ARKA_OS/VERSION" 2>/dev/null || echo "?")
|
|
91
|
+
ARKA_DESC="ARKA OS v${ARKA_VER} installed"
|
|
92
|
+
fi
|
|
93
|
+
run_check "arka-install" "fail" "$ARKA_DESC" "$ARKA_OK" "Run: bash install.sh"
|
|
94
|
+
|
|
95
|
+
# ─── Check 3: jq ──────────────────────────────────────────────────────────
|
|
96
|
+
JQ_OK=false
|
|
97
|
+
if command -v jq &>/dev/null; then
|
|
98
|
+
JQ_OK=true
|
|
99
|
+
fi
|
|
100
|
+
run_check "jq" "fail" "jq available" "$JQ_OK" "Install: brew install jq"
|
|
101
|
+
|
|
102
|
+
# ─── Check 4: User Profile ────────────────────────────────────────────────
|
|
103
|
+
PROFILE_OK=false
|
|
104
|
+
PROFILE_DESC="User profile exists"
|
|
105
|
+
PROFILE_FILE="$HOME/.arka-os/profile.json"
|
|
106
|
+
if [ -f "$PROFILE_FILE" ]; then
|
|
107
|
+
PNAME=$(jq -r '.user_name // ""' "$PROFILE_FILE" 2>/dev/null)
|
|
108
|
+
if [ -n "$PNAME" ] && [ "$PNAME" != "null" ]; then
|
|
109
|
+
PROFILE_OK=true
|
|
110
|
+
PROFILE_DESC="User profile exists ($PNAME)"
|
|
111
|
+
else
|
|
112
|
+
PROFILE_DESC="User profile exists but missing name"
|
|
113
|
+
fi
|
|
114
|
+
fi
|
|
115
|
+
if [ "$FIX_MODE" = true ] && [ "$PROFILE_OK" = false ] && [ ! -f "$PROFILE_FILE" ]; then
|
|
116
|
+
mkdir -p "$HOME/.arka-os"
|
|
117
|
+
echo '{"user_name":"","company_name":"","role":"","onboarding_version":"1"}' > "$PROFILE_FILE"
|
|
118
|
+
PROFILE_DESC="User profile created (run /arka setup to complete)"
|
|
119
|
+
fi
|
|
120
|
+
run_check "profile" "warn" "$PROFILE_DESC" "$PROFILE_OK" "Run: /arka setup"
|
|
121
|
+
|
|
122
|
+
# ─── Check 5: Status Line ─────────────────────────────────────────────────
|
|
123
|
+
SL_OK=false
|
|
124
|
+
SL_DESC="Status line configured"
|
|
125
|
+
SETTINGS_FILE="$HOME/.claude/settings.json"
|
|
126
|
+
if [ -f "$SETTINGS_FILE" ] && jq -e '.statusLine.command' "$SETTINGS_FILE" &>/dev/null; then
|
|
127
|
+
SL_CMD=$(jq -r '.statusLine.command' "$SETTINGS_FILE")
|
|
128
|
+
if [ -f "$SL_CMD" ]; then
|
|
129
|
+
SL_OK=true
|
|
130
|
+
else
|
|
131
|
+
SL_DESC="Status line command not found: $SL_CMD"
|
|
132
|
+
fi
|
|
133
|
+
fi
|
|
134
|
+
if [ "$FIX_MODE" = true ] && [ "$SL_OK" = false ] && [ -f "$ARKA_OS/statusline.sh" ]; then
|
|
135
|
+
if [ -f "$SETTINGS_FILE" ] && command -v jq &>/dev/null; then
|
|
136
|
+
jq --arg cmd "$ARKA_OS/statusline.sh" '.statusLine = {"type":"command","command":$cmd,"padding":2}' \
|
|
137
|
+
"$SETTINGS_FILE" > "$SETTINGS_FILE.tmp" && mv "$SETTINGS_FILE.tmp" "$SETTINGS_FILE"
|
|
138
|
+
SL_OK=true
|
|
139
|
+
SL_DESC="Status line configured (fixed)"
|
|
140
|
+
fi
|
|
141
|
+
fi
|
|
142
|
+
run_check "statusline" "warn" "$SL_DESC" "$SL_OK" "Run: bash install.sh"
|
|
143
|
+
|
|
144
|
+
# ─── Check 6: Hooks ───────────────────────────────────────────────────────
|
|
145
|
+
HOOKS_OK=false
|
|
146
|
+
HOOKS_DESC="Hooks configured"
|
|
147
|
+
if [ -f "$SETTINGS_FILE" ] && jq -e '.hooks.UserPromptSubmit' "$SETTINGS_FILE" &>/dev/null; then
|
|
148
|
+
HOOKS_OK=true
|
|
149
|
+
HOOK_COUNT=$(jq '[.hooks | to_entries[].value[].hooks[]] | length' "$SETTINGS_FILE" 2>/dev/null || echo "0")
|
|
150
|
+
HOOKS_DESC="Hooks configured ($HOOK_COUNT hook(s))"
|
|
151
|
+
fi
|
|
152
|
+
if [ "$FIX_MODE" = true ] && [ "$HOOKS_OK" = false ]; then
|
|
153
|
+
HOOKS_DIR="$HOME/.claude/skills/arka/hooks"
|
|
154
|
+
if [ -d "$HOOKS_DIR" ] && [ -f "$SETTINGS_FILE" ] && command -v jq &>/dev/null; then
|
|
155
|
+
HOOKS_JSON=$(jq -n \
|
|
156
|
+
--arg ups "$HOOKS_DIR/user-prompt-submit.sh" \
|
|
157
|
+
--arg pc "$HOOKS_DIR/pre-compact.sh" \
|
|
158
|
+
--arg ptu "$HOOKS_DIR/post-tool-use.sh" \
|
|
159
|
+
'{
|
|
160
|
+
"UserPromptSubmit": [{"hooks":[{"type":"command","command":$ups,"timeout":10}]}],
|
|
161
|
+
"PreCompact": [{"hooks":[{"type":"command","command":$pc,"timeout":30}]}],
|
|
162
|
+
"PostToolUse": [{"hooks":[{"type":"command","command":$ptu,"timeout":5}]}]
|
|
163
|
+
}')
|
|
164
|
+
jq --argjson hooks "$HOOKS_JSON" '.hooks = $hooks' "$SETTINGS_FILE" > "$SETTINGS_FILE.tmp" && \
|
|
165
|
+
mv "$SETTINGS_FILE.tmp" "$SETTINGS_FILE"
|
|
166
|
+
HOOKS_OK=true
|
|
167
|
+
HOOKS_DESC="Hooks configured (fixed)"
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
170
|
+
run_check "hooks" "warn" "$HOOKS_DESC" "$HOOKS_OK" "Run: bash install.sh"
|
|
171
|
+
|
|
172
|
+
# ─── Check 7: Obsidian Vault ──────────────────────────────────────────────
|
|
173
|
+
OBS_OK=false
|
|
174
|
+
OBS_DESC="Obsidian vault configured"
|
|
175
|
+
OBS_CONFIG="$ARKA_OS/knowledge/obsidian-config.json"
|
|
176
|
+
if [ -f "$OBS_CONFIG" ]; then
|
|
177
|
+
VAULT_PATH=$(jq -r '.vault_path // ""' "$OBS_CONFIG" 2>/dev/null)
|
|
178
|
+
if [ -n "$VAULT_PATH" ] && [ -d "$VAULT_PATH" ] && [ -d "$VAULT_PATH/.obsidian" ]; then
|
|
179
|
+
OBS_OK=true
|
|
180
|
+
OBS_DESC="Obsidian vault: $(basename "$VAULT_PATH")"
|
|
181
|
+
elif [ -n "$VAULT_PATH" ]; then
|
|
182
|
+
OBS_DESC="Obsidian vault path invalid: $VAULT_PATH"
|
|
183
|
+
fi
|
|
184
|
+
fi
|
|
185
|
+
run_check "obsidian" "warn" "$OBS_DESC" "$OBS_OK" "Run: bash install.sh (will auto-detect)"
|
|
186
|
+
|
|
187
|
+
# ─── Check 8: Departments ─────────────────────────────────────────────────
|
|
188
|
+
DEPT_OK=false
|
|
189
|
+
DEPT_COUNT=0
|
|
190
|
+
for dept_dir in "$HOME/.claude/skills"/arka-*/; do
|
|
191
|
+
[ -d "$dept_dir" ] && [ -f "$dept_dir/SKILL.md" ] && DEPT_COUNT=$((DEPT_COUNT + 1))
|
|
192
|
+
done
|
|
193
|
+
# Subtract non-department skills (arka-scaffold, arka-mcp, etc. are sub-skills but still count as skills)
|
|
194
|
+
DEPT_DESC="$DEPT_COUNT department skills installed"
|
|
195
|
+
if [ "$DEPT_COUNT" -ge 7 ]; then
|
|
196
|
+
DEPT_OK=true
|
|
197
|
+
fi
|
|
198
|
+
run_check "departments" "warn" "$DEPT_DESC" "$DEPT_OK" "Run: bash install.sh"
|
|
199
|
+
|
|
200
|
+
# ─── Check 9: Personas ────────────────────────────────────────────────────
|
|
201
|
+
PERSONA_OK=false
|
|
202
|
+
PERSONA_COUNT=0
|
|
203
|
+
for agent in "$HOME/.claude/agents"/arka-*.md; do
|
|
204
|
+
[ -f "$agent" ] && PERSONA_COUNT=$((PERSONA_COUNT + 1))
|
|
205
|
+
done
|
|
206
|
+
PERSONA_DESC="$PERSONA_COUNT agent files installed"
|
|
207
|
+
if [ "$PERSONA_COUNT" -ge 10 ]; then
|
|
208
|
+
PERSONA_OK=true
|
|
209
|
+
fi
|
|
210
|
+
run_check "personas" "warn" "$PERSONA_DESC" "$PERSONA_OK" "Run: bash install.sh"
|
|
211
|
+
|
|
212
|
+
# ─── Check 10: MCP Registry ───────────────────────────────────────────────
|
|
213
|
+
MCP_OK=false
|
|
214
|
+
MCP_DESC="MCP registry present"
|
|
215
|
+
MCP_REG="$ARKA_OS/mcps/registry.json"
|
|
216
|
+
if [ -f "$MCP_REG" ]; then
|
|
217
|
+
MCP_COUNT=$(jq '.mcpServers | length' "$MCP_REG" 2>/dev/null || echo "0")
|
|
218
|
+
MCP_OK=true
|
|
219
|
+
MCP_DESC="MCP registry ($MCP_COUNT MCPs)"
|
|
220
|
+
fi
|
|
221
|
+
run_check "mcp-registry" "fail" "$MCP_DESC" "$MCP_OK" "Run: bash install.sh"
|
|
222
|
+
|
|
223
|
+
# ─── Check 11: Prerequisites ──────────────────────────────────────────────
|
|
224
|
+
PREREQ_OK=true
|
|
225
|
+
MISSING_PREREQS=""
|
|
226
|
+
for bin in yt-dlp ffmpeg python3; do
|
|
227
|
+
if ! command -v "$bin" &>/dev/null; then
|
|
228
|
+
MISSING_PREREQS="$MISSING_PREREQS $bin"
|
|
229
|
+
PREREQ_OK=false
|
|
230
|
+
fi
|
|
231
|
+
done
|
|
232
|
+
MISSING_PREREQS="${MISSING_PREREQS# }" # trim leading space
|
|
233
|
+
if [ "$PREREQ_OK" = true ]; then
|
|
234
|
+
PREREQ_DESC="yt-dlp, ffmpeg, python3 available"
|
|
235
|
+
else
|
|
236
|
+
PREREQ_DESC="Missing: $MISSING_PREREQS"
|
|
237
|
+
fi
|
|
238
|
+
run_check "prerequisites" "warn" "$PREREQ_DESC" "$PREREQ_OK" "brew install $MISSING_PREREQS"
|
|
239
|
+
|
|
240
|
+
# ─── Check 12: Capabilities ───────────────────────────────────────────────
|
|
241
|
+
CAPS_OK=false
|
|
242
|
+
CAPS_DESC="capabilities.json present"
|
|
243
|
+
CAPS_FILE="$HOME/.arka-os/capabilities.json"
|
|
244
|
+
if [ -f "$CAPS_FILE" ]; then
|
|
245
|
+
# Check age (< 7 days)
|
|
246
|
+
CAPS_AGE=$(( ($(date +%s) - $(stat -f%m "$CAPS_FILE" 2>/dev/null || stat -c%Y "$CAPS_FILE" 2>/dev/null || echo 0)) ))
|
|
247
|
+
CAPS_DAYS=$((CAPS_AGE / 86400))
|
|
248
|
+
if [ "$CAPS_DAYS" -lt 7 ]; then
|
|
249
|
+
CAPS_OK=true
|
|
250
|
+
CAPS_DESC="capabilities.json (${CAPS_DAYS}d old)"
|
|
251
|
+
else
|
|
252
|
+
CAPS_DESC="capabilities.json stale (${CAPS_DAYS}d old, max 7d)"
|
|
253
|
+
fi
|
|
254
|
+
fi
|
|
255
|
+
if [ "$FIX_MODE" = true ] && [ "$CAPS_OK" = false ]; then
|
|
256
|
+
CAPS_SCRIPT="$ARKA_OS/../arka-knowledge/scripts/kb-check-capabilities.sh"
|
|
257
|
+
# Also check repo path
|
|
258
|
+
REPO_PATH=$(cat "$ARKA_OS/.repo-path" 2>/dev/null || echo "")
|
|
259
|
+
if [ -n "$REPO_PATH" ] && [ -f "$REPO_PATH/departments/kb/scripts/kb-check-capabilities.sh" ]; then
|
|
260
|
+
CAPS_SCRIPT="$REPO_PATH/departments/kb/scripts/kb-check-capabilities.sh"
|
|
261
|
+
fi
|
|
262
|
+
if [ -f "$CAPS_SCRIPT" ]; then
|
|
263
|
+
bash "$CAPS_SCRIPT" >/dev/null 2>&1 && CAPS_OK=true && CAPS_DESC="capabilities.json (refreshed)"
|
|
264
|
+
fi
|
|
265
|
+
fi
|
|
266
|
+
run_check "capabilities" "warn" "$CAPS_DESC" "$CAPS_OK" "Run: arka kb capabilities"
|
|
267
|
+
|
|
268
|
+
# ─── Check 13: Agent Memory ─────────────────────────────────────────────
|
|
269
|
+
AMEM_OK=false
|
|
270
|
+
AMEM_COUNT=0
|
|
271
|
+
AMEM_DIR="$HOME/.claude/agent-memory"
|
|
272
|
+
if [ -d "$AMEM_DIR" ]; then
|
|
273
|
+
for mem_dir in "$AMEM_DIR"/arka-*/MEMORY.md; do
|
|
274
|
+
[ -f "$mem_dir" ] && AMEM_COUNT=$((AMEM_COUNT + 1))
|
|
275
|
+
done
|
|
276
|
+
fi
|
|
277
|
+
AMEM_DESC="$AMEM_COUNT/15 agent memory files"
|
|
278
|
+
if [ "$AMEM_COUNT" -ge 15 ]; then
|
|
279
|
+
AMEM_OK=true
|
|
280
|
+
fi
|
|
281
|
+
run_check "agent-memory" "warn" "$AMEM_DESC" "$AMEM_OK" "Run: bash install.sh"
|
|
282
|
+
|
|
283
|
+
# ─── Check 14: Install Manifest ─────────────────────────────────────────
|
|
284
|
+
MANIFEST_OK=false
|
|
285
|
+
MANIFEST_DESC="Install manifest present"
|
|
286
|
+
MANIFEST_FILE="$HOME/.arka-os/install-manifest.json"
|
|
287
|
+
if [ -f "$MANIFEST_FILE" ]; then
|
|
288
|
+
if jq empty "$MANIFEST_FILE" 2>/dev/null; then
|
|
289
|
+
MANIFEST_VER=$(jq -r '.version // "?"' "$MANIFEST_FILE")
|
|
290
|
+
FILE_COUNT=$(jq '.files | length' "$MANIFEST_FILE" 2>/dev/null || echo "0")
|
|
291
|
+
MANIFEST_OK=true
|
|
292
|
+
MANIFEST_DESC="Install manifest v${MANIFEST_VER} ($FILE_COUNT files)"
|
|
293
|
+
else
|
|
294
|
+
MANIFEST_DESC="Install manifest corrupted"
|
|
295
|
+
fi
|
|
296
|
+
fi
|
|
297
|
+
run_check "install-manifest" "warn" "$MANIFEST_DESC" "$MANIFEST_OK" "Run: bash install.sh"
|
|
298
|
+
|
|
299
|
+
# ─── Check 15: Gotchas ──────────────────────────────────────────────────
|
|
300
|
+
GOTCHAS_OK=false
|
|
301
|
+
GOTCHAS_DESC="Gotchas file present"
|
|
302
|
+
GOTCHAS_FILE="$HOME/.arka-os/gotchas.json"
|
|
303
|
+
if [ -f "$GOTCHAS_FILE" ]; then
|
|
304
|
+
if jq empty "$GOTCHAS_FILE" 2>/dev/null; then
|
|
305
|
+
GOTCHA_COUNT=$(jq 'length' "$GOTCHAS_FILE" 2>/dev/null || echo "0")
|
|
306
|
+
GOTCHAS_OK=true
|
|
307
|
+
GOTCHAS_DESC="Gotchas tracking ($GOTCHA_COUNT patterns)"
|
|
308
|
+
else
|
|
309
|
+
GOTCHAS_DESC="Gotchas file corrupted"
|
|
310
|
+
fi
|
|
311
|
+
else
|
|
312
|
+
# Create empty gotchas file if missing (auto-fix always)
|
|
313
|
+
mkdir -p "$HOME/.arka-os"
|
|
314
|
+
echo '[]' > "$GOTCHAS_FILE"
|
|
315
|
+
GOTCHAS_OK=true
|
|
316
|
+
GOTCHAS_DESC="Gotchas file created (empty)"
|
|
317
|
+
fi
|
|
318
|
+
run_check "gotchas" "warn" "$GOTCHAS_DESC" "$GOTCHAS_OK" "Run: bash install.sh"
|
|
319
|
+
|
|
320
|
+
# ─── Check 16: Plugins (Superpowers + Claude-Mem) ─────────────────────────
|
|
321
|
+
PLUGINS_OK=false
|
|
322
|
+
PLUGINS_DESC="Plugins not checked"
|
|
323
|
+
if command -v claude &>/dev/null; then
|
|
324
|
+
PLUGIN_LIST=$(claude plugin list 2>/dev/null || echo "")
|
|
325
|
+
HAS_SP=false
|
|
326
|
+
HAS_CM=false
|
|
327
|
+
echo "$PLUGIN_LIST" | grep -q "superpowers" && HAS_SP=true
|
|
328
|
+
echo "$PLUGIN_LIST" | grep -q "claude-mem" && HAS_CM=true
|
|
329
|
+
|
|
330
|
+
if [ "$HAS_SP" = true ] && [ "$HAS_CM" = true ]; then
|
|
331
|
+
PLUGINS_OK=true
|
|
332
|
+
PLUGINS_DESC="Plugins installed (Superpowers + Claude-Mem)"
|
|
333
|
+
elif [ "$HAS_SP" = true ]; then
|
|
334
|
+
PLUGINS_DESC="Missing Claude-Mem plugin"
|
|
335
|
+
elif [ "$HAS_CM" = true ]; then
|
|
336
|
+
PLUGINS_DESC="Missing Superpowers plugin"
|
|
337
|
+
else
|
|
338
|
+
PLUGINS_DESC="Missing both plugins (Superpowers + Claude-Mem)"
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
# Auto-fix: install missing plugins
|
|
342
|
+
if [ "$FIX_MODE" = true ] && [ "$PLUGINS_OK" = false ]; then
|
|
343
|
+
if [ "$HAS_SP" = false ]; then
|
|
344
|
+
MARKETPLACES=$(claude plugin marketplace list 2>/dev/null || echo "")
|
|
345
|
+
echo "$MARKETPLACES" | grep -q "superpowers-marketplace" || \
|
|
346
|
+
claude plugin marketplace add obra/superpowers-marketplace >/dev/null 2>&1
|
|
347
|
+
claude plugin install superpowers@superpowers-marketplace >/dev/null 2>&1 && HAS_SP=true
|
|
348
|
+
fi
|
|
349
|
+
if [ "$HAS_CM" = false ]; then
|
|
350
|
+
MARKETPLACES=$(claude plugin marketplace list 2>/dev/null || echo "")
|
|
351
|
+
echo "$MARKETPLACES" | grep -q "thedotmack" || \
|
|
352
|
+
claude plugin marketplace add thedotmack/claude-mem >/dev/null 2>&1
|
|
353
|
+
claude plugin install claude-mem@thedotmack >/dev/null 2>&1 && HAS_CM=true
|
|
354
|
+
fi
|
|
355
|
+
if [ "$HAS_SP" = true ] && [ "$HAS_CM" = true ]; then
|
|
356
|
+
PLUGINS_OK=true
|
|
357
|
+
PLUGINS_DESC="Plugins installed (auto-fixed)"
|
|
358
|
+
fi
|
|
359
|
+
fi
|
|
360
|
+
else
|
|
361
|
+
PLUGINS_DESC="Claude CLI not found — cannot check plugins"
|
|
362
|
+
fi
|
|
363
|
+
run_check "plugins" "warn" "$PLUGINS_DESC" "$PLUGINS_OK" "Run: bash install.sh"
|
|
364
|
+
|
|
365
|
+
# ─── Output ────────────────────────────────────────────────────────────────
|
|
366
|
+
TOTAL=$((PASS_COUNT + WARN_COUNT + FAIL_COUNT))
|
|
367
|
+
|
|
368
|
+
if [ "$JSON_MODE" = true ]; then
|
|
369
|
+
# Build JSON array
|
|
370
|
+
echo -n "["
|
|
371
|
+
for i in "${!RESULTS[@]}"; do
|
|
372
|
+
[ "$i" -gt 0 ] && echo -n ","
|
|
373
|
+
echo -n "${RESULTS[$i]}"
|
|
374
|
+
done
|
|
375
|
+
echo "]"
|
|
376
|
+
else
|
|
377
|
+
echo ""
|
|
378
|
+
echo -e " ${B}Results:${N} ${G}${PASS_COUNT} passed${N}, ${Y}${WARN_COUNT} warnings${N}, ${R}${FAIL_COUNT} failed${N} (${TOTAL} checks)"
|
|
379
|
+
|
|
380
|
+
if [ "$FAIL_COUNT" -gt 0 ]; then
|
|
381
|
+
echo ""
|
|
382
|
+
echo -e " ${R}Some critical checks failed.${N} Run ${C}arka doctor --fix${N} to attempt auto-repair."
|
|
383
|
+
elif [ "$WARN_COUNT" -gt 0 ]; then
|
|
384
|
+
echo ""
|
|
385
|
+
echo -e " ${Y}Some warnings detected.${N} Run ${C}arka doctor --fix${N} to attempt auto-repair."
|
|
386
|
+
else
|
|
387
|
+
echo ""
|
|
388
|
+
echo -e " ${G}All checks passed!${N} ARKA OS is healthy."
|
|
389
|
+
fi
|
|
390
|
+
echo ""
|
|
391
|
+
fi
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# ARKA OS — Provider Registry Manager
|
|
4
|
+
# Manage external AI providers and models (image, video, text)
|
|
5
|
+
# Usage: arka providers [command] [args...]
|
|
6
|
+
# ============================================================================
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
11
|
+
REGISTRY="$REPO_DIR/config/providers-registry.json"
|
|
12
|
+
ENV_FILE="$HOME/.arka-os/.env"
|
|
13
|
+
|
|
14
|
+
CYAN='\033[0;36m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
YELLOW='\033[1;33m'
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
BLUE='\033[0;34m'
|
|
19
|
+
BOLD='\033[1m'
|
|
20
|
+
NC='\033[0m'
|
|
21
|
+
|
|
22
|
+
# Require jq
|
|
23
|
+
command -v jq &>/dev/null || { echo "Error: jq is required. Install: brew install jq"; exit 1; }
|
|
24
|
+
|
|
25
|
+
# Load env if present
|
|
26
|
+
[ -f "$ENV_FILE" ] && source "$ENV_FILE"
|
|
27
|
+
|
|
28
|
+
# ─── Helper: Check if API key is configured ─────────────────────────────────
|
|
29
|
+
is_key_configured() {
|
|
30
|
+
local var_name="$1"
|
|
31
|
+
local value="${!var_name:-}"
|
|
32
|
+
[ -n "$value" ] && [ ${#value} -ge 8 ]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# ─── List providers ──────────────────────────────────────────────────────────
|
|
36
|
+
cmd_list() {
|
|
37
|
+
echo ""
|
|
38
|
+
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
|
|
39
|
+
echo -e "${CYAN}║${NC} ${BOLD}ARKA OS — AI Providers${NC} ${CYAN}║${NC}"
|
|
40
|
+
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
|
|
41
|
+
echo ""
|
|
42
|
+
|
|
43
|
+
local providers
|
|
44
|
+
providers=$(jq -r '.providers | keys[]' "$REGISTRY")
|
|
45
|
+
|
|
46
|
+
for provider_id in $providers; do
|
|
47
|
+
local name auth_env
|
|
48
|
+
name=$(jq -r --arg id "$provider_id" '.providers[$id].name' "$REGISTRY")
|
|
49
|
+
auth_env=$(jq -r --arg id "$provider_id" '.providers[$id].auth_env' "$REGISTRY")
|
|
50
|
+
|
|
51
|
+
if is_key_configured "$auth_env"; then
|
|
52
|
+
echo -e " ${GREEN}●${NC} ${BOLD}$name${NC} ($provider_id) — ${GREEN}configured${NC} [$auth_env]"
|
|
53
|
+
else
|
|
54
|
+
echo -e " ${RED}○${NC} ${BOLD}$name${NC} ($provider_id) — ${YELLOW}not configured${NC} [$auth_env]"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# List models
|
|
58
|
+
local models
|
|
59
|
+
models=$(jq -r --arg id "$provider_id" '.providers[$id].models | to_entries[] | " \(.value.type | ascii_upcase) \(.key) — \(.value.description)"' "$REGISTRY")
|
|
60
|
+
echo "$models"
|
|
61
|
+
echo ""
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
local total_providers total_models
|
|
65
|
+
total_providers=$(jq '.providers | length' "$REGISTRY")
|
|
66
|
+
total_models=$(jq '[.providers[].models | length] | add' "$REGISTRY")
|
|
67
|
+
echo -e " ${CYAN}$total_providers${NC} providers, ${CYAN}$total_models${NC} models"
|
|
68
|
+
echo ""
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# ─── Add provider (interactive) ─────────────────────────────────────────────
|
|
72
|
+
cmd_add() {
|
|
73
|
+
local provider_id="${1:-}"
|
|
74
|
+
if [ -z "$provider_id" ]; then
|
|
75
|
+
echo -e "${BLUE}Add a new AI provider${NC}"
|
|
76
|
+
read -rp " Provider ID (lowercase, no spaces): " provider_id
|
|
77
|
+
fi
|
|
78
|
+
[ -z "$provider_id" ] && { echo "Error: Provider ID is required."; exit 1; }
|
|
79
|
+
|
|
80
|
+
# Check if already exists
|
|
81
|
+
if jq -e --arg id "$provider_id" '.providers[$id]' "$REGISTRY" &>/dev/null; then
|
|
82
|
+
echo -e "${YELLOW}Provider '$provider_id' already exists. Use 'add-model' to add models.${NC}"
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
read -rp " Display name: " name
|
|
87
|
+
read -rp " Base URL (e.g., https://api.example.com/v1): " base_url
|
|
88
|
+
read -rp " Auth env variable name (e.g., EXAMPLE_API_KEY): " auth_env
|
|
89
|
+
read -rp " Auth header format (e.g., Authorization: Bearer): " auth_header
|
|
90
|
+
|
|
91
|
+
[ -z "$name" ] || [ -z "$base_url" ] || [ -z "$auth_env" ] && { echo "Error: All fields are required."; exit 1; }
|
|
92
|
+
[ -z "$auth_header" ] && auth_header="Authorization: Bearer"
|
|
93
|
+
|
|
94
|
+
# Add to registry
|
|
95
|
+
local tmp
|
|
96
|
+
tmp=$(mktemp)
|
|
97
|
+
jq --arg id "$provider_id" \
|
|
98
|
+
--arg name "$name" \
|
|
99
|
+
--arg base_url "$base_url" \
|
|
100
|
+
--arg auth_env "$auth_env" \
|
|
101
|
+
--arg auth_header "$auth_header" \
|
|
102
|
+
'.providers[$id] = {name: $name, base_url: $base_url, auth_env: $auth_env, auth_header: $auth_header, models: {}}' \
|
|
103
|
+
"$REGISTRY" > "$tmp" && mv "$tmp" "$REGISTRY"
|
|
104
|
+
|
|
105
|
+
echo -e "${GREEN}✓${NC} Provider '$provider_id' added. Use 'arka providers add-model $provider_id <model-id>' to add models."
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# ─── Remove provider ─────────────────────────────────────────────────────────
|
|
109
|
+
cmd_remove() {
|
|
110
|
+
local provider_id="${1:-}"
|
|
111
|
+
[ -z "$provider_id" ] && { echo "Usage: arka providers remove <provider-id>"; exit 1; }
|
|
112
|
+
|
|
113
|
+
if ! jq -e --arg id "$provider_id" '.providers[$id]' "$REGISTRY" &>/dev/null; then
|
|
114
|
+
echo -e "${RED}Provider '$provider_id' not found.${NC}"
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
local tmp
|
|
119
|
+
tmp=$(mktemp)
|
|
120
|
+
jq --arg id "$provider_id" '
|
|
121
|
+
.providers |= del(.[$id]) |
|
|
122
|
+
.routing |= with_entries(.value |= [.[] | select(startswith($id + "/") | not)])
|
|
123
|
+
' "$REGISTRY" > "$tmp" && mv "$tmp" "$REGISTRY"
|
|
124
|
+
|
|
125
|
+
echo -e "${GREEN}✓${NC} Provider '$provider_id' removed."
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# ─── Add model to existing provider ──────────────────────────────────────────
|
|
129
|
+
cmd_add_model() {
|
|
130
|
+
local provider_id="${1:-}"
|
|
131
|
+
local model_id="${2:-}"
|
|
132
|
+
[ -z "$provider_id" ] || [ -z "$model_id" ] && { echo "Usage: arka providers add-model <provider-id> <model-id> [--type image|video|text] [--description \"...\"]"; exit 1; }
|
|
133
|
+
|
|
134
|
+
if ! jq -e --arg id "$provider_id" '.providers[$id]' "$REGISTRY" &>/dev/null; then
|
|
135
|
+
echo -e "${RED}Provider '$provider_id' not found.${NC}"
|
|
136
|
+
exit 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
shift 2
|
|
140
|
+
local model_type="text"
|
|
141
|
+
local description=""
|
|
142
|
+
|
|
143
|
+
while [ $# -gt 0 ]; do
|
|
144
|
+
case "$1" in
|
|
145
|
+
--type) model_type="$2"; shift 2 ;;
|
|
146
|
+
--description) description="$2"; shift 2 ;;
|
|
147
|
+
*) shift ;;
|
|
148
|
+
esac
|
|
149
|
+
done
|
|
150
|
+
|
|
151
|
+
[ -z "$description" ] && description="$model_id"
|
|
152
|
+
|
|
153
|
+
local tmp
|
|
154
|
+
tmp=$(mktemp)
|
|
155
|
+
jq --arg pid "$provider_id" \
|
|
156
|
+
--arg mid "$model_id" \
|
|
157
|
+
--arg type "$model_type" \
|
|
158
|
+
--arg desc "$description" \
|
|
159
|
+
'.providers[$pid].models[$mid] = {type: $type, description: $desc}' \
|
|
160
|
+
"$REGISTRY" > "$tmp" && mv "$tmp" "$REGISTRY"
|
|
161
|
+
|
|
162
|
+
echo -e "${GREEN}✓${NC} Model '$model_id' ($model_type) added to '$provider_id'."
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# ─── Add API key ─────────────────────────────────────────────────────────────
|
|
166
|
+
cmd_add_key() {
|
|
167
|
+
local var_name="${1:-}"
|
|
168
|
+
[ -z "$var_name" ] && { echo "Usage: arka providers add-key <ENV_VAR_NAME>"; exit 1; }
|
|
169
|
+
|
|
170
|
+
mkdir -p "$(dirname "$ENV_FILE")"
|
|
171
|
+
|
|
172
|
+
if is_key_configured "$var_name"; then
|
|
173
|
+
echo -e "${GREEN}✓${NC} $var_name is already configured."
|
|
174
|
+
read -rp " Overwrite? (y/N): " confirm
|
|
175
|
+
[ "$confirm" != "y" ] && [ "$confirm" != "Y" ] && exit 0
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
read -rp " Value for $var_name: " value
|
|
179
|
+
[ -z "$value" ] && { echo "Skipped."; exit 0; }
|
|
180
|
+
|
|
181
|
+
if grep -q "^export $var_name=" "$ENV_FILE" 2>/dev/null; then
|
|
182
|
+
local tmp
|
|
183
|
+
tmp=$(mktemp)
|
|
184
|
+
sed "s|^export $var_name=.*|export $var_name=\"$value\"|" "$ENV_FILE" > "$tmp" && mv "$tmp" "$ENV_FILE"
|
|
185
|
+
else
|
|
186
|
+
echo "export $var_name=\"$value\"" >> "$ENV_FILE"
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
echo -e "${GREEN}✓${NC} $var_name saved to $ENV_FILE"
|
|
190
|
+
echo -e " Run ${CYAN}source ~/.zshrc${NC} to load."
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
# ─── Show routing chains ─────────────────────────────────────────────────────
|
|
194
|
+
cmd_routing() {
|
|
195
|
+
echo ""
|
|
196
|
+
echo -e "${CYAN}═══ AI Provider Routing Chains ═══${NC}"
|
|
197
|
+
echo ""
|
|
198
|
+
|
|
199
|
+
local chains
|
|
200
|
+
chains=$(jq -r '.routing | to_entries[] | .key' "$REGISTRY")
|
|
201
|
+
|
|
202
|
+
for chain in $chains; do
|
|
203
|
+
echo -e " ${BOLD}$chain:${NC}"
|
|
204
|
+
jq -r --arg chain "$chain" '.routing[$chain][]' "$REGISTRY" | while IFS= read -r model_ref; do
|
|
205
|
+
local provider_id="${model_ref%%/*}"
|
|
206
|
+
local model_id="${model_ref#*/}"
|
|
207
|
+
local auth_env
|
|
208
|
+
auth_env=$(jq -r --arg id "$provider_id" '.providers[$id].auth_env // "?"' "$REGISTRY")
|
|
209
|
+
if is_key_configured "$auth_env"; then
|
|
210
|
+
echo -e " ${GREEN}●${NC} $model_ref"
|
|
211
|
+
else
|
|
212
|
+
echo -e " ${RED}○${NC} $model_ref (${YELLOW}no key${NC})"
|
|
213
|
+
fi
|
|
214
|
+
done
|
|
215
|
+
echo ""
|
|
216
|
+
done
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# ─── JSON output ──────────────────────────────────────────────────────────────
|
|
220
|
+
cmd_json() {
|
|
221
|
+
# Enrich with configured status
|
|
222
|
+
jq --arg env_file "$ENV_FILE" '
|
|
223
|
+
. as $reg |
|
|
224
|
+
.providers | to_entries | map(
|
|
225
|
+
.value.configured = (
|
|
226
|
+
.value.auth_env as $env |
|
|
227
|
+
(env[$env] // "") | length >= 8
|
|
228
|
+
)
|
|
229
|
+
) | from_entries |
|
|
230
|
+
$reg | .providers = .
|
|
231
|
+
' "$REGISTRY" 2>/dev/null || cat "$REGISTRY"
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# ─── Main ─────────────────────────────────────────────────────────────────────
|
|
235
|
+
case "${1:-}" in
|
|
236
|
+
add)
|
|
237
|
+
shift
|
|
238
|
+
cmd_add "$@"
|
|
239
|
+
;;
|
|
240
|
+
remove)
|
|
241
|
+
shift
|
|
242
|
+
cmd_remove "$@"
|
|
243
|
+
;;
|
|
244
|
+
add-model)
|
|
245
|
+
shift
|
|
246
|
+
cmd_add_model "$@"
|
|
247
|
+
;;
|
|
248
|
+
add-key)
|
|
249
|
+
shift
|
|
250
|
+
cmd_add_key "$@"
|
|
251
|
+
;;
|
|
252
|
+
routing)
|
|
253
|
+
cmd_routing
|
|
254
|
+
;;
|
|
255
|
+
--json)
|
|
256
|
+
cmd_json
|
|
257
|
+
;;
|
|
258
|
+
*)
|
|
259
|
+
cmd_list
|
|
260
|
+
;;
|
|
261
|
+
esac
|