arkaos 2.7.0 → 2.9.0
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/config/hooks/cwd-changed.sh +104 -0
- package/config/hooks/session-start.sh +60 -0
- package/config/hooks/user-prompt-submit.sh +105 -277
- package/config/settings-template.json +22 -0
- package/config/user-claude.md +15 -0
- package/installer/adapters/claude-code.js +26 -0
- package/installer/index.js +39 -16
- package/installer/update.js +32 -11
- package/knowledge/ecosystems.json +362 -10
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.9.0
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# ArkaOS — CwdChanged Hook
|
|
4
|
+
# Fires when the working directory changes. Detects ecosystem and injects
|
|
5
|
+
# project context so Claude knows which squad and stack to use.
|
|
6
|
+
# ============================================================================
|
|
7
|
+
|
|
8
|
+
input=$(cat)
|
|
9
|
+
NEW_CWD=$(echo "$input" | jq -r '.cwd // ""' 2>/dev/null)
|
|
10
|
+
|
|
11
|
+
if [ -z "$NEW_CWD" ] || [ ! -d "$NEW_CWD" ]; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# ─── Detect ecosystem from ecosystems.json ─────────────────────────────
|
|
16
|
+
ECOSYSTEMS_FILE="$HOME/.claude/skills/arka/knowledge/ecosystems.json"
|
|
17
|
+
ECOSYSTEM=""
|
|
18
|
+
ECOSYSTEM_NAME=""
|
|
19
|
+
|
|
20
|
+
if [ -f "$ECOSYSTEMS_FILE" ] && command -v python3 &>/dev/null; then
|
|
21
|
+
eval "$(python3 -c "
|
|
22
|
+
import json, os, sys
|
|
23
|
+
|
|
24
|
+
cwd = '$NEW_CWD'
|
|
25
|
+
eco_file = os.path.expanduser('$ECOSYSTEMS_FILE')
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
data = json.load(open(eco_file))
|
|
29
|
+
ecosystems = data.get('ecosystems', {})
|
|
30
|
+
|
|
31
|
+
for eco_id, eco in ecosystems.items():
|
|
32
|
+
projects = eco.get('projects', [])
|
|
33
|
+
for proj in projects:
|
|
34
|
+
# Check if cwd contains the project name
|
|
35
|
+
if proj in cwd:
|
|
36
|
+
print(f'ECOSYSTEM=\"{eco_id}\"')
|
|
37
|
+
print(f'ECOSYSTEM_NAME=\"{eco.get(\"name\", eco_id)}\"')
|
|
38
|
+
sys.exit(0)
|
|
39
|
+
|
|
40
|
+
# No match by project name, try by path patterns
|
|
41
|
+
if '/herd/' in cwd or '/Herd/' in cwd:
|
|
42
|
+
dir_name = os.path.basename(cwd.rstrip('/'))
|
|
43
|
+
for eco_id, eco in ecosystems.items():
|
|
44
|
+
for proj in eco.get('projects', []):
|
|
45
|
+
if proj == dir_name:
|
|
46
|
+
print(f'ECOSYSTEM=\"{eco_id}\"')
|
|
47
|
+
print(f'ECOSYSTEM_NAME=\"{eco.get(\"name\", eco_id)}\"')
|
|
48
|
+
sys.exit(0)
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
print('ECOSYSTEM=\"\"')
|
|
53
|
+
print('ECOSYSTEM_NAME=\"\"')
|
|
54
|
+
" 2>/dev/null)"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# ─── Detect stack ──────────────────────────────────────────────────────
|
|
58
|
+
STACK="unknown"
|
|
59
|
+
if [ -f "$NEW_CWD/composer.json" ]; then
|
|
60
|
+
STACK="laravel"
|
|
61
|
+
elif [ -f "$NEW_CWD/package.json" ]; then
|
|
62
|
+
if grep -q '"nuxt"' "$NEW_CWD/package.json" 2>/dev/null; then
|
|
63
|
+
STACK="nuxt"
|
|
64
|
+
elif grep -q '"next"' "$NEW_CWD/package.json" 2>/dev/null; then
|
|
65
|
+
STACK="nextjs"
|
|
66
|
+
elif grep -q '"react"' "$NEW_CWD/package.json" 2>/dev/null; then
|
|
67
|
+
STACK="react"
|
|
68
|
+
elif grep -q '"vue"' "$NEW_CWD/package.json" 2>/dev/null; then
|
|
69
|
+
STACK="vue"
|
|
70
|
+
else
|
|
71
|
+
STACK="node"
|
|
72
|
+
fi
|
|
73
|
+
elif [ -f "$NEW_CWD/pyproject.toml" ]; then
|
|
74
|
+
STACK="python"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ─── Check for project descriptor ─────────────────────────────────────
|
|
78
|
+
DIR_NAME=$(basename "$NEW_CWD")
|
|
79
|
+
DESCRIPTOR=""
|
|
80
|
+
DESCRIPTOR_FILE="$HOME/.claude/skills/arka/projects/${DIR_NAME}.md"
|
|
81
|
+
DESCRIPTOR_DIR="$HOME/.claude/skills/arka/projects/${DIR_NAME}/PROJECT.md"
|
|
82
|
+
|
|
83
|
+
if [ -f "$DESCRIPTOR_FILE" ]; then
|
|
84
|
+
DESCRIPTOR="$DESCRIPTOR_FILE"
|
|
85
|
+
elif [ -f "$DESCRIPTOR_DIR" ]; then
|
|
86
|
+
DESCRIPTOR="$DESCRIPTOR_DIR"
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# ─── Build context output ─────────────────────────────────────────────
|
|
90
|
+
CONTEXT=""
|
|
91
|
+
|
|
92
|
+
if [ -n "$ECOSYSTEM" ]; then
|
|
93
|
+
CONTEXT="[arka:project-context] Ecosystem: ${ECOSYSTEM_NAME} (${ECOSYSTEM}) | Stack: ${STACK} | Use /arka-${ECOSYSTEM} for dedicated squad routing."
|
|
94
|
+
elif [ "$STACK" != "unknown" ]; then
|
|
95
|
+
CONTEXT="[arka:project-context] Stack: ${STACK} | No ecosystem assigned. Use /arka onboard to register this project."
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
if [ -n "$DESCRIPTOR" ]; then
|
|
99
|
+
CONTEXT="${CONTEXT} Descriptor: ${DESCRIPTOR}"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if [ -n "$CONTEXT" ]; then
|
|
103
|
+
echo "{\"additionalContext\": \"${CONTEXT}\"}"
|
|
104
|
+
fi
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# ArkaOS — SessionStart Hook
|
|
4
|
+
# Uses systemMessage (same protocol as claude-mem) for guaranteed display.
|
|
5
|
+
# ============================================================================
|
|
6
|
+
|
|
7
|
+
# ─── Profile ───────────────────────────────────────────────────────────
|
|
8
|
+
NAME="founder"
|
|
9
|
+
COMPANY="WizardingCode"
|
|
10
|
+
VERSION="2.x"
|
|
11
|
+
|
|
12
|
+
if [ -f "$HOME/.arkaos/profile.json" ] && command -v python3 &>/dev/null; then
|
|
13
|
+
NAME=$(python3 -c "import json; p=json.load(open('$HOME/.arkaos/profile.json')); print(p.get('name', p.get('role', 'founder')))" 2>/dev/null || echo "founder")
|
|
14
|
+
COMPANY=$(python3 -c "import json; print(json.load(open('$HOME/.arkaos/profile.json')).get('company', 'WizardingCode'))" 2>/dev/null || echo "WizardingCode")
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
if [ -f "$HOME/.arkaos/.repo-path" ]; then
|
|
18
|
+
REPO=$(cat "$HOME/.arkaos/.repo-path")
|
|
19
|
+
[ -f "$REPO/VERSION" ] && VERSION=$(cat "$REPO/VERSION" | tr -d '[:space:]')
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# ─── Time greeting ─────────────────────────────────────────────────────
|
|
23
|
+
HOUR=$(date +%H)
|
|
24
|
+
if [ "$HOUR" -ge 5 ] && [ "$HOUR" -lt 12 ]; then GREETING="Bom dia"
|
|
25
|
+
elif [ "$HOUR" -ge 12 ] && [ "$HOUR" -lt 19 ]; then GREETING="Boa tarde"
|
|
26
|
+
else GREETING="Boa noite"; fi
|
|
27
|
+
|
|
28
|
+
# ─── Version drift ─────────────────────────────────────────────────────
|
|
29
|
+
SYNC_STATE="$HOME/.arkaos/sync-state.json"
|
|
30
|
+
DRIFT=""
|
|
31
|
+
|
|
32
|
+
if [ -f "$SYNC_STATE" ]; then
|
|
33
|
+
SYNCED=$(python3 -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
|
|
34
|
+
if [ "$SYNCED" != "$VERSION" ]; then
|
|
35
|
+
DRIFT="\\n[arka:update-available] Core v${VERSION} != synced v${SYNCED}. Run /arka update."
|
|
36
|
+
fi
|
|
37
|
+
else
|
|
38
|
+
DRIFT="\\n[arka:update-available] Never synced. Run /arka update."
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# ─── Build message ─────────────────────────────────────────────────────
|
|
42
|
+
MSG="╔══════════════════════════════════════════════╗\\n"
|
|
43
|
+
MSG+="║ ║\\n"
|
|
44
|
+
MSG+="║ A R K A O S ║\\n"
|
|
45
|
+
MSG+="║ ║\\n"
|
|
46
|
+
MSG+="║ The Operating System for AI Teams ║\\n"
|
|
47
|
+
MSG+="║ by WizardingCode ║\\n"
|
|
48
|
+
MSG+="║ ║\\n"
|
|
49
|
+
MSG+="╚══════════════════════════════════════════════╝\\n"
|
|
50
|
+
MSG+="\\n"
|
|
51
|
+
MSG+="${GREETING}, ${NAME} (${COMPANY})\\n"
|
|
52
|
+
MSG+="ArkaOS v${VERSION} | 65 agents | 17 departments | 244+ skills"
|
|
53
|
+
MSG+="${DRIFT}"
|
|
54
|
+
|
|
55
|
+
# ─── Output as systemMessage (same protocol as claude-mem) ─────────────
|
|
56
|
+
python3 -c "
|
|
57
|
+
import json
|
|
58
|
+
msg = '''$(echo -e "$MSG")'''
|
|
59
|
+
print(json.dumps({'systemMessage': msg}))
|
|
60
|
+
"
|
|
@@ -1,312 +1,140 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ============================================================================
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# Timeout: 10s | Output: JSON to stdout | Target: <
|
|
3
|
+
# ArkaOS v2 — UserPromptSubmit Hook (Synapse v2 Bridge)
|
|
4
|
+
# Calls Python Synapse engine for 8-layer context injection
|
|
5
|
+
# Timeout: 10s | Output: JSON to stdout | Target: <100ms
|
|
6
6
|
# ============================================================================
|
|
7
7
|
|
|
8
8
|
input=$(cat)
|
|
9
9
|
|
|
10
|
-
# ───
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
_hook_ms() {
|
|
14
|
-
local end_ns=$(date +%s%N 2>/dev/null || echo "0")
|
|
15
|
-
if [ "$_HOOK_START_NS" != "0" ] && [ "$end_ns" != "0" ] && [ ${#end_ns} -gt 10 ]; then
|
|
16
|
-
echo $(( (end_ns - _HOOK_START_NS) / 1000000 ))
|
|
17
|
-
else
|
|
18
|
-
echo $(( ($(date +%s) - _HOOK_START) * 1000 ))
|
|
19
|
-
fi
|
|
20
|
-
}
|
|
10
|
+
# ─── V1 Migration Detection ─────────────────────────────────────────────
|
|
11
|
+
V1_PATHS=("$HOME/.claude/skills/arka-os" "$HOME/.claude/skills/arkaos")
|
|
12
|
+
MIGRATION_MARKER="$HOME/.arkaos/migrated-from-v1"
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
for v1_path in "${V1_PATHS[@]}"; do
|
|
15
|
+
if [ -d "$v1_path" ] && [ ! -f "$MIGRATION_MARKER" ]; then
|
|
16
|
+
echo "{\"additionalContext\": \"[MIGRATION] ArkaOS v1 detected at $v1_path. Run: npx arkaos migrate — This will backup v1, preserve your data, and install v2. See: https://github.com/andreagroferreira/arka-os#install\"}"
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
done
|
|
20
|
+
|
|
21
|
+
# ─── Sync Version Detection ────────────────────────────────────────────
|
|
22
|
+
SYNC_STATE="$HOME/.arkaos/sync-state.json"
|
|
23
|
+
ARKAOS_VERSION_FILE="$HOME/.arkaos/.repo-path"
|
|
24
|
+
|
|
25
|
+
if [ -f "$ARKAOS_VERSION_FILE" ]; then
|
|
26
|
+
_REPO_PATH=$(cat "$ARKAOS_VERSION_FILE")
|
|
27
|
+
if [ -f "$_REPO_PATH/VERSION" ]; then
|
|
28
|
+
_CURRENT_VERSION=$(cat "$_REPO_PATH/VERSION")
|
|
29
|
+
elif [ -f "$_REPO_PATH/package.json" ]; then
|
|
30
|
+
_CURRENT_VERSION=$(python3 -c "import json; print(json.load(open('$_REPO_PATH/package.json'))['version'])" 2>/dev/null || echo "")
|
|
31
|
+
fi
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
if [ -n "${_CURRENT_VERSION:-}" ]; then
|
|
34
|
+
if [ -f "$SYNC_STATE" ]; then
|
|
35
|
+
_SYNCED_VERSION=$(python3 -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
|
|
36
|
+
else
|
|
37
|
+
_SYNCED_VERSION="none"
|
|
38
|
+
fi
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
cache_get() {
|
|
35
|
-
local file="$CACHE_DIR/$1"
|
|
36
|
-
local ttl="$2"
|
|
37
|
-
if [ -f "$file" ]; then
|
|
38
|
-
local age=$(( $(date +%s) - $(stat -f%m "$file" 2>/dev/null || stat -c%Y "$file" 2>/dev/null || echo 0) ))
|
|
39
|
-
if [ "$age" -lt "$ttl" ]; then
|
|
40
|
-
cat "$file"
|
|
41
|
-
return 0
|
|
40
|
+
if [ "$_CURRENT_VERSION" != "$_SYNCED_VERSION" ]; then
|
|
41
|
+
_SYNC_NOTICE="[arka:update-available] ArkaOS v${_CURRENT_VERSION} installed (synced: ${_SYNCED_VERSION}). Run /arka update to sync all projects. "
|
|
42
42
|
fi
|
|
43
43
|
fi
|
|
44
|
-
|
|
45
|
-
}
|
|
44
|
+
fi
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
46
|
+
# ─── Session Greeting (now handled by SessionStart hook via systemMessage) ──
|
|
47
|
+
_ARKA_GREETING=""
|
|
50
48
|
|
|
51
|
-
# ───
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
[
|
|
56
|
-
|
|
57
|
-
L0="[Constitution] NON-NEGOTIABLE: branch-isolation, obsidian-output, authority-boundaries, security-gate, context-first, solid-clean-code, spec-driven, human-writing, squad-routing, full-visibility, sequential-validation, mandatory-qa, arka-supremacy | QUALITY-GATE: marta-cqo, eduardo-copy, francisca-tech-ux | MUST: conventional-commits, test-coverage, pattern-matching, actionable-output, memory-persistence"
|
|
58
|
-
cache_set "constitution" "$L0"
|
|
49
|
+
# ─── Performance Timing ──────────────────────────────────────────────────
|
|
50
|
+
_HOOK_START_NS=$(date +%s%N 2>/dev/null || echo "0")
|
|
51
|
+
_hook_ms() {
|
|
52
|
+
local end_ns=$(date +%s%N 2>/dev/null || echo "0")
|
|
53
|
+
if [ "$_HOOK_START_NS" != "0" ] && [ "$end_ns" != "0" ] && [ ${#end_ns} -gt 10 ]; then
|
|
54
|
+
echo $(( (end_ns - _HOOK_START_NS) / 1000000 ))
|
|
59
55
|
else
|
|
60
|
-
|
|
56
|
+
echo "0"
|
|
61
57
|
fi
|
|
62
58
|
}
|
|
63
|
-
[ -n "$L0" ] && CONTEXT_PARTS+=("$L0")
|
|
64
|
-
_L0_MS=$(_hook_ms)
|
|
65
|
-
|
|
66
|
-
# ─── L1: Department Routing (no cache — fast string match) ───────────────
|
|
67
|
-
PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
|
|
68
|
-
DETECTED_DEPT=""
|
|
69
59
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
elif
|
|
75
|
-
|
|
76
|
-
elif
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\b(task|email|calendar|automate|meeting|workflow|process|schedule|slack|discord|notify)\b'; then
|
|
81
|
-
DETECTED_DEPT="operations"
|
|
82
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\b(learn|persona|knowledge|youtube|transcribe|article|research)\b'; then
|
|
83
|
-
DETECTED_DEPT="knowledge"
|
|
84
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\b(brand|logo|colors|palette|mockup|photoshoot|brand.?identity|brand.?guide|mood.?board|naming|positioning|visual.?design|motion.?design|brand.?video|social.?kit)\b'; then
|
|
85
|
-
DETECTED_DEPT="brand"
|
|
86
|
-
fi
|
|
87
|
-
[ -n "$DETECTED_DEPT" ] && CONTEXT_PARTS+=("[dept:$DETECTED_DEPT]")
|
|
88
|
-
_L1_MS=$(( $(_hook_ms) - ${_L0_MS:-0} ))
|
|
89
|
-
|
|
90
|
-
# ─── L2: Agent Memory (30s cache) ────────────────────────────────────────
|
|
91
|
-
# Detect agent name from prompt
|
|
92
|
-
DETECTED_AGENT=""
|
|
93
|
-
if echo "$PROMPT_LOWER" | grep -qE '\bmarco\b|/.*cto\b'; then
|
|
94
|
-
DETECTED_AGENT="cto"
|
|
95
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bpaulo\b|tech.?lead'; then
|
|
96
|
-
DETECTED_AGENT="tech-lead"
|
|
97
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bgabriel\b|architect'; then
|
|
98
|
-
DETECTED_AGENT="architect"
|
|
99
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bandre\b|senior.?dev|backend'; then
|
|
100
|
-
DETECTED_AGENT="senior-dev"
|
|
101
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bdiana\b|frontend.?dev'; then
|
|
102
|
-
DETECTED_AGENT="frontend-dev"
|
|
103
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bbruno\b|security'; then
|
|
104
|
-
DETECTED_AGENT="security"
|
|
105
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bcarlos\b|devops'; then
|
|
106
|
-
DETECTED_AGENT="devops"
|
|
107
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\brita\b|\bqa\b'; then
|
|
108
|
-
DETECTED_AGENT="qa"
|
|
109
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\blucas\b|analyst'; then
|
|
110
|
-
DETECTED_AGENT="analyst"
|
|
111
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bhelena\b|cfo'; then
|
|
112
|
-
DETECTED_AGENT="cfo"
|
|
113
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bsofia\b|coo'; then
|
|
114
|
-
DETECTED_AGENT="coo"
|
|
115
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bluna\b|content.?creator'; then
|
|
116
|
-
DETECTED_AGENT="content-creator"
|
|
117
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bricardo\b|ecommerce.?manager'; then
|
|
118
|
-
DETECTED_AGENT="ecommerce-manager"
|
|
119
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\btomas\b|strategist'; then
|
|
120
|
-
DETECTED_AGENT="strategist"
|
|
121
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bclara\b|knowledge.?curator'; then
|
|
122
|
-
DETECTED_AGENT="knowledge-curator"
|
|
123
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bvalentina\b|creative.?director'; then
|
|
124
|
-
DETECTED_AGENT="creative-director"
|
|
125
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bmateus\b|brand.?strategist'; then
|
|
126
|
-
DETECTED_AGENT="brand-strategist"
|
|
127
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\bisabel\b|visual.?designer'; then
|
|
128
|
-
DETECTED_AGENT="visual-designer"
|
|
129
|
-
elif echo "$PROMPT_LOWER" | grep -qE '\brafael\b|motion.?designer'; then
|
|
130
|
-
DETECTED_AGENT="motion-designer"
|
|
60
|
+
# ─── Paths ───────────────────────────────────────────────────────────────
|
|
61
|
+
# Resolve ARKAOS_ROOT: env var → .repo-path → npm package → fallback
|
|
62
|
+
if [ -n "${ARKAOS_ROOT:-}" ]; then
|
|
63
|
+
: # already set
|
|
64
|
+
elif [ -f "$HOME/.arkaos/.repo-path" ]; then
|
|
65
|
+
ARKAOS_ROOT=$(cat "$HOME/.arkaos/.repo-path")
|
|
66
|
+
elif [ -d "$HOME/.arkaos" ]; then
|
|
67
|
+
ARKAOS_ROOT="$HOME/.arkaos"
|
|
68
|
+
else
|
|
69
|
+
ARKAOS_ROOT="/Users/andreagroferreira/.npm/_npx/67d92defd11b9985/node_modules/arkaos"
|
|
131
70
|
fi
|
|
71
|
+
export ARKAOS_ROOT
|
|
132
72
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
AGENT_CTX=$(cache_get "$AGENT_CACHE_KEY" 30) || {
|
|
136
|
-
AGENT_MEM="$HOME/.claude/agent-memory/arka-${DETECTED_AGENT}/MEMORY.md"
|
|
137
|
-
AGENT_CTX="[agent:$DETECTED_AGENT"
|
|
73
|
+
CACHE_DIR="/tmp/arkaos-context-cache"
|
|
74
|
+
CACHE_TTL=300 # Constitution cache: 5 minutes
|
|
138
75
|
|
|
139
|
-
|
|
140
|
-
REPO_PATH=$(cat "$ARKA_OS/.repo-path" 2>/dev/null || echo "")
|
|
141
|
-
REGISTRY_FILE="$REPO_PATH/knowledge/agents-registry.json"
|
|
142
|
-
if [ -f "$REGISTRY_FILE" ] && command -v jq &>/dev/null; then
|
|
143
|
-
DISC_COMBO=$(jq -r --arg id "$DETECTED_AGENT" '.agents[] | select(.id == $id) | .disc.combination // ""' "$REGISTRY_FILE" 2>/dev/null)
|
|
144
|
-
[ -n "$DISC_COMBO" ] && AGENT_CTX+=" disc:$DISC_COMBO"
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
if [ -f "$AGENT_MEM" ]; then
|
|
148
|
-
# Extract last 3 gotchas from agent memory
|
|
149
|
-
GOTCHAS=$(sed -n '/^## Gotchas/,/^## /{ /^## Gotchas/d; /^## /d; /^$/d; p; }' "$AGENT_MEM" 2>/dev/null | head -3)
|
|
150
|
-
if [ -n "$GOTCHAS" ]; then
|
|
151
|
-
AGENT_CTX+=" gotchas: $(echo "$GOTCHAS" | tr '\n' '; ' | head -c 200)"
|
|
152
|
-
fi
|
|
153
|
-
fi
|
|
76
|
+
mkdir -p "$CACHE_DIR" 2>/dev/null
|
|
154
77
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
78
|
+
# ─── Extract user input from hook JSON ───────────────────────────────────
|
|
79
|
+
user_input=""
|
|
80
|
+
if command -v jq &>/dev/null; then
|
|
81
|
+
user_input=$(echo "$input" | jq -r '.userInput // .message // ""' 2>/dev/null)
|
|
82
|
+
fi
|
|
83
|
+
# Fallback: try to get the raw text
|
|
84
|
+
if [ -z "$user_input" ]; then
|
|
85
|
+
user_input=$(echo "$input" | head -c 2000)
|
|
159
86
|
fi
|
|
160
|
-
_L2_MS=$(( $(_hook_ms) - ${_L0_MS:-0} - ${_L1_MS:-0} ))
|
|
161
87
|
|
|
162
|
-
# ───
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
PROJECT_CTX=$(cache_get "$PROJECT_CACHE_KEY" 30) || {
|
|
166
|
-
PROJECT_CTX=""
|
|
167
|
-
# Check ARKA OS projects/ directory
|
|
168
|
-
REPO_PATH=$(cat "$ARKA_OS/.repo-path" 2>/dev/null || echo "")
|
|
169
|
-
if [ -n "$REPO_PATH" ] && [ -d "$REPO_PATH/projects" ]; then
|
|
170
|
-
for proj_md in "$REPO_PATH/projects"/*/PROJECT.md; do
|
|
171
|
-
[ -f "$proj_md" ] || continue
|
|
172
|
-
PROJ_DIR=$(dirname "$proj_md")
|
|
173
|
-
if [ -f "$PROJ_DIR/.project-path" ]; then
|
|
174
|
-
PROJ_PATH=$(cat "$PROJ_DIR/.project-path" 2>/dev/null)
|
|
175
|
-
if [ -n "$PROJ_PATH" ] && [[ "$CWD" == "$PROJ_PATH"* ]]; then
|
|
176
|
-
PROJ_NAME=$(basename "$PROJ_DIR")
|
|
177
|
-
# Try to extract stack from PROJECT.md
|
|
178
|
-
PROJ_STACK=$(grep -i 'stack\|framework' "$proj_md" 2>/dev/null | head -1 | sed 's/.*: *//' | head -c 50)
|
|
179
|
-
PROJECT_CTX="[project:$PROJ_NAME"
|
|
180
|
-
[ -n "$PROJ_STACK" ] && PROJECT_CTX+=" stack:$PROJ_STACK"
|
|
181
|
-
PROJECT_CTX+="]"
|
|
182
|
-
break
|
|
183
|
-
fi
|
|
184
|
-
fi
|
|
185
|
-
done
|
|
186
|
-
fi
|
|
88
|
+
# ─── Try Python Synapse bridge first ────────────────────────────────────
|
|
89
|
+
python_result=""
|
|
90
|
+
BRIDGE_SCRIPT="${ARKAOS_ROOT}/scripts/synapse-bridge.py"
|
|
187
91
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
[ -d "$proj_dir" ] || continue
|
|
192
|
-
if [ -f "$proj_dir/.project-path" ]; then
|
|
193
|
-
PROJ_PATH=$(cat "$proj_dir/.project-path" 2>/dev/null)
|
|
194
|
-
if [ -n "$PROJ_PATH" ] && [[ "$CWD" == "$PROJ_PATH"* ]]; then
|
|
195
|
-
PROJ_NAME=$(basename "$proj_dir" | sed 's/^arka-//')
|
|
196
|
-
PROJECT_CTX="[project:$PROJ_NAME]"
|
|
197
|
-
break
|
|
198
|
-
fi
|
|
199
|
-
fi
|
|
200
|
-
done
|
|
201
|
-
fi
|
|
92
|
+
if command -v python3 &>/dev/null && [ -f "$BRIDGE_SCRIPT" ]; then
|
|
93
|
+
bridge_output=$(echo "{\"user_input\":$(echo "$user_input" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null || echo '""')}" \
|
|
94
|
+
| ARKAOS_ROOT="$ARKAOS_ROOT" python3 "$BRIDGE_SCRIPT" --root "$ARKAOS_ROOT" 2>/dev/null)
|
|
202
95
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
96
|
+
if [ -n "$bridge_output" ]; then
|
|
97
|
+
python_result=$(echo "$bridge_output" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('context_string',''))" 2>/dev/null)
|
|
98
|
+
fi
|
|
206
99
|
fi
|
|
207
|
-
_L3_MS=$(( $(_hook_ms) - ${_L0_MS:-0} - ${_L1_MS:-0} - ${_L2_MS:-0} ))
|
|
208
100
|
|
|
209
|
-
# ───
|
|
210
|
-
if [ -
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
101
|
+
# ─── Fallback: Bash-only context (if Python unavailable) ────────────────
|
|
102
|
+
if [ -z "$python_result" ]; then
|
|
103
|
+
# L0: Constitution (cached)
|
|
104
|
+
L0=""
|
|
105
|
+
L0_CACHE="$CACHE_DIR/l0-constitution"
|
|
106
|
+
if [ -f "$L0_CACHE" ] && [ $(($(date +%s) - $(stat -f%m "$L0_CACHE" 2>/dev/null || stat -c%Y "$L0_CACHE" 2>/dev/null || echo 0))) -lt $CACHE_TTL ]; then
|
|
107
|
+
L0=$(cat "$L0_CACHE")
|
|
108
|
+
else
|
|
109
|
+
L0="[Constitution] NON-NEGOTIABLE: branch-isolation, obsidian-output, authority-boundaries, security-gate, context-first, solid-clean-code, spec-driven, human-writing, squad-routing, full-visibility, sequential-validation, mandatory-qa, arka-supremacy | QUALITY-GATE: marta-cqo, eduardo-copy, francisca-tech-ux | MUST: conventional-commits, test-coverage, pattern-matching, actionable-output, memory-persistence"
|
|
110
|
+
echo "$L0" > "$L0_CACHE" 2>/dev/null
|
|
214
111
|
fi
|
|
215
|
-
fi
|
|
216
|
-
_L4_MS=$(( $(_hook_ms) - ${_L0_MS:-0} - ${_L1_MS:-0} - ${_L2_MS:-0} - ${_L3_MS:-0} ))
|
|
217
112
|
|
|
218
|
-
#
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if [ -f "$GOTCHAS_FILE" ] && command -v jq &>/dev/null; then
|
|
225
|
-
# Get top 2 recurring errors for this department with count >= 3 (include suggestions)
|
|
226
|
-
TOP_GOTCHAS=$(jq -r --arg cat "$DETECTED_DEPT" \
|
|
227
|
-
'[.[] | select(.category == $cat and .count >= 3)] | sort_by(-.count) | .[0:2] | .[] |
|
|
228
|
-
if .suggestion then "\(.pattern) (x\(.count)) \u2192 \(.suggestion)" else "\(.pattern) (x\(.count))" end' \
|
|
229
|
-
"$GOTCHAS_FILE" 2>/dev/null)
|
|
230
|
-
if [ -n "$TOP_GOTCHAS" ]; then
|
|
231
|
-
GOTCHA_CTX="[gotchas:$DETECTED_DEPT $(echo "$TOP_GOTCHAS" | tr '\n' '; ' | head -c 300)]"
|
|
232
|
-
fi
|
|
233
|
-
fi
|
|
234
|
-
cache_set "$GOTCHA_CACHE_KEY" "$GOTCHA_CTX"
|
|
235
|
-
}
|
|
236
|
-
[ -n "$GOTCHA_CTX" ] && CONTEXT_PARTS+=("$GOTCHA_CTX")
|
|
237
|
-
fi
|
|
113
|
+
# L4: Git branch
|
|
114
|
+
L4=""
|
|
115
|
+
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
|
116
|
+
if [ -n "$branch" ] && [ "$branch" != "main" ] && [ "$branch" != "master" ] && [ "$branch" != "dev" ]; then
|
|
117
|
+
L4="[branch:$branch]"
|
|
118
|
+
fi
|
|
238
119
|
|
|
239
|
-
#
|
|
240
|
-
|
|
241
|
-
if [
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
# Extract words from prompt and match against registry keywords
|
|
249
|
-
CMD_HINT=$(jq -r --arg prompt "$PROMPT_LOWER" '
|
|
250
|
-
($prompt | split(" ") | map(select(length > 2))) as $words |
|
|
251
|
-
[.commands[] |
|
|
252
|
-
{id, command, department,
|
|
253
|
-
score: ([.keywords[] | . as $kw |
|
|
254
|
-
if ($words | any(. == $kw)) then 2
|
|
255
|
-
elif ($words | join(" ") | test($kw)) then 3
|
|
256
|
-
else 0 end
|
|
257
|
-
] | add // 0)}
|
|
258
|
-
] | sort_by(-.score) | [.[:2][] | select(.score > 0)] |
|
|
259
|
-
if length > 0 then
|
|
260
|
-
map("[hint:\(.command)]") | join(" ")
|
|
261
|
-
else "" end
|
|
262
|
-
' "$REGISTRY" 2>/dev/null)
|
|
263
|
-
fi
|
|
264
|
-
cache_set "$CMD_CACHE_KEY" "$CMD_HINT"
|
|
265
|
-
}
|
|
266
|
-
[ -n "$CMD_HINT" ] && CONTEXT_PARTS+=("$CMD_HINT")
|
|
267
|
-
fi
|
|
268
|
-
_L5_MS=$(( $(_hook_ms) - ${_L0_MS:-0} - ${_L1_MS:-0} - ${_L2_MS:-0} - ${_L3_MS:-0} - ${_L4_MS:-0} ))
|
|
120
|
+
# L7: Time
|
|
121
|
+
hour=$(date +%H)
|
|
122
|
+
if [ "$hour" -ge 5 ] && [ "$hour" -lt 12 ]; then
|
|
123
|
+
L7="[time:morning]"
|
|
124
|
+
elif [ "$hour" -ge 12 ] && [ "$hour" -lt 18 ]; then
|
|
125
|
+
L7="[time:afternoon]"
|
|
126
|
+
else
|
|
127
|
+
L7="[time:evening]"
|
|
128
|
+
fi
|
|
269
129
|
|
|
270
|
-
|
|
271
|
-
HOUR=$(date +%H)
|
|
272
|
-
if [ "$HOUR" -lt 12 ]; then
|
|
273
|
-
CONTEXT_PARTS+=("[time:morning]")
|
|
274
|
-
elif [ "$HOUR" -lt 18 ]; then
|
|
275
|
-
CONTEXT_PARTS+=("[time:afternoon]")
|
|
276
|
-
else
|
|
277
|
-
CONTEXT_PARTS+=("[time:evening]")
|
|
130
|
+
python_result="$L0 $L4 $L7"
|
|
278
131
|
fi
|
|
279
132
|
|
|
280
|
-
# ───
|
|
281
|
-
|
|
282
|
-
METRICS_FILE="$HOME/.arka-os/hook-metrics.json"
|
|
283
|
-
METRICS_LOCK="$HOME/.arka-os/hook-metrics.lock"
|
|
284
|
-
mkdir -p "$HOME/.arka-os"
|
|
285
|
-
(
|
|
286
|
-
if command -v flock &>/dev/null; then flock -w 2 200; else true; fi
|
|
287
|
-
[ ! -f "$METRICS_FILE" ] && echo '[]' > "$METRICS_FILE"
|
|
288
|
-
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
289
|
-
jq --argjson dur "$_DURATION_MS" --arg ts "$NOW" --arg hook "user-prompt-submit" \
|
|
290
|
-
--argjson l0 "${_L0_MS:-0}" --argjson l1 "${_L1_MS:-0}" --argjson l2 "${_L2_MS:-0}" \
|
|
291
|
-
--argjson l3 "${_L3_MS:-0}" --argjson l4 "${_L4_MS:-0}" --argjson l5 "${_L5_MS:-0}" \
|
|
292
|
-
'. += [{"hook": $hook, "duration_ms": $dur, "timestamp": $ts, "layers": {"L0": $l0, "L1": $l1, "L2": $l2, "L3": $l3, "L4": $l4, "L5": $l5}}] | .[-500:]' \
|
|
293
|
-
"$METRICS_FILE" > "$METRICS_FILE.tmp" 2>/dev/null && mv "$METRICS_FILE.tmp" "$METRICS_FILE"
|
|
294
|
-
) 200>"$METRICS_LOCK" 2>/dev/null
|
|
133
|
+
# ─── Output ──────────────────────────────────────────────────────────────
|
|
134
|
+
echo "{\"additionalContext\": \"${_ARKA_GREETING:-}${_SYNC_NOTICE:-}$python_result\"}"
|
|
295
135
|
|
|
296
|
-
# ───
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
CONTEXT+="$part "
|
|
301
|
-
done
|
|
302
|
-
CONTEXT="${CONTEXT% }"
|
|
303
|
-
|
|
304
|
-
jq -n --arg ctx "$CONTEXT" '{
|
|
305
|
-
"hookSpecificOutput": {
|
|
306
|
-
"hookEventName": "UserPromptSubmit",
|
|
307
|
-
"additionalContext": $ctx
|
|
308
|
-
}
|
|
309
|
-
}'
|
|
310
|
-
else
|
|
311
|
-
echo '{}'
|
|
136
|
+
# ─── Metrics ─────────────────────────────────────────────────────────────
|
|
137
|
+
elapsed=$(_hook_ms)
|
|
138
|
+
if [ "$elapsed" -gt 0 ] 2>/dev/null; then
|
|
139
|
+
echo "{\"hook\":\"user-prompt-submit-v2\",\"ms\":$elapsed}" >> "$CACHE_DIR/hook-metrics.jsonl" 2>/dev/null
|
|
312
140
|
fi
|
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
"padding": 2
|
|
6
6
|
},
|
|
7
7
|
"hooks": {
|
|
8
|
+
"SessionStart": [
|
|
9
|
+
{
|
|
10
|
+
"hooks": [
|
|
11
|
+
{
|
|
12
|
+
"type": "command",
|
|
13
|
+
"command": "{{HOOKS_DIR}}/session-start.sh",
|
|
14
|
+
"timeout": 5
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
],
|
|
8
19
|
"UserPromptSubmit": [
|
|
9
20
|
{
|
|
10
21
|
"hooks": [
|
|
@@ -37,6 +48,17 @@
|
|
|
37
48
|
}
|
|
38
49
|
]
|
|
39
50
|
}
|
|
51
|
+
],
|
|
52
|
+
"CwdChanged": [
|
|
53
|
+
{
|
|
54
|
+
"hooks": [
|
|
55
|
+
{
|
|
56
|
+
"type": "command",
|
|
57
|
+
"command": "{{HOOKS_DIR}}/cwd-changed.sh",
|
|
58
|
+
"timeout": 5
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
40
62
|
]
|
|
41
63
|
}
|
|
42
64
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# ArkaOS — User Instructions
|
|
2
|
+
|
|
3
|
+
## Version Drift Warning
|
|
4
|
+
|
|
5
|
+
When your context contains `[arka:update-available]`, show this warning before your response:
|
|
6
|
+
|
|
7
|
+
> ArkaOS update available. Run `/arka update` to sync all projects.
|
|
8
|
+
|
|
9
|
+
## Squad Routing
|
|
10
|
+
|
|
11
|
+
You are operating within ArkaOS. Every request routes through the appropriate department squad. Never respond as a generic assistant.
|
|
12
|
+
|
|
13
|
+
## Language
|
|
14
|
+
|
|
15
|
+
Match the user's language. When writing in Portuguese, use European Portuguese (pt-PT).
|
|
@@ -28,6 +28,19 @@ export default {
|
|
|
28
28
|
settings.hooks = {};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// SessionStart — Branded greeting + version drift detection
|
|
32
|
+
settings.hooks.SessionStart = [
|
|
33
|
+
{
|
|
34
|
+
hooks: [
|
|
35
|
+
{
|
|
36
|
+
type: "command",
|
|
37
|
+
command: join(hooksDir, "session-start.sh"),
|
|
38
|
+
timeout: 5,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
31
44
|
// UserPromptSubmit — Synapse v2 context injection
|
|
32
45
|
settings.hooks.UserPromptSubmit = [
|
|
33
46
|
{
|
|
@@ -67,6 +80,19 @@ export default {
|
|
|
67
80
|
},
|
|
68
81
|
];
|
|
69
82
|
|
|
83
|
+
// CwdChanged — Project/ecosystem auto-detection
|
|
84
|
+
settings.hooks.CwdChanged = [
|
|
85
|
+
{
|
|
86
|
+
hooks: [
|
|
87
|
+
{
|
|
88
|
+
type: "command",
|
|
89
|
+
command: join(hooksDir, "cwd-changed.sh"),
|
|
90
|
+
timeout: 5,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
|
|
70
96
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
71
97
|
console.log(" Claude Code hooks configured.");
|
|
72
98
|
},
|
package/installer/index.js
CHANGED
|
@@ -30,14 +30,14 @@ export async function install({ runtime, path, force }) {
|
|
|
30
30
|
const installDir = userConfig.installDir;
|
|
31
31
|
|
|
32
32
|
// ═══ Step 1: Create directories ═══
|
|
33
|
-
step(1,
|
|
33
|
+
step(1, 13, "Creating directories...");
|
|
34
34
|
ensureDir(installDir);
|
|
35
35
|
const dirs = ["config", "config/hooks", "agents", "media", "session-digests", "vault"];
|
|
36
36
|
for (const d of dirs) ensureDir(join(installDir, d));
|
|
37
37
|
ok(`${dirs.length + 1} directories ready`);
|
|
38
38
|
|
|
39
39
|
// ═══ Step 2: Detect v1 installation ═══
|
|
40
|
-
step(2,
|
|
40
|
+
step(2, 13, "Checking for v1 installation...");
|
|
41
41
|
const v1Paths = [
|
|
42
42
|
join(homedir(), ".claude", "skills", "arka-os"),
|
|
43
43
|
join(homedir(), ".claude", "skills", "arkaos"),
|
|
@@ -51,35 +51,56 @@ export async function install({ runtime, path, force }) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// ═══ Step 3: Check Python ═══
|
|
54
|
-
step(3,
|
|
54
|
+
step(3, 13, "Checking Python 3.11+...");
|
|
55
55
|
const pythonCmd = checkPython();
|
|
56
56
|
ok(`Found: ${pythonCmd}`);
|
|
57
57
|
|
|
58
58
|
// ═══ Step 4: Install Python core + dependencies based on user choices ═══
|
|
59
|
-
step(4,
|
|
59
|
+
step(4, 13, "Installing Python dependencies (this may take a minute)...");
|
|
60
60
|
installAllPythonDeps(pythonCmd, userConfig);
|
|
61
61
|
|
|
62
62
|
// ═══ Step 5: Copy configuration files ═══
|
|
63
|
-
step(5,
|
|
63
|
+
step(5, 13, "Copying configuration files...");
|
|
64
64
|
copyConfigFiles(installDir);
|
|
65
65
|
ok("Constitution, standards, and config copied");
|
|
66
66
|
|
|
67
67
|
// ═══ Step 6: Install hooks with real paths ═══
|
|
68
|
-
step(6,
|
|
68
|
+
step(6, 13, "Installing hooks...");
|
|
69
69
|
installHooks(installDir);
|
|
70
70
|
|
|
71
71
|
// ═══ Step 7: Configure runtime ═══
|
|
72
|
-
step(7,
|
|
72
|
+
step(7, 13, "Configuring runtime...");
|
|
73
73
|
const adapter = await loadAdapter(runtime);
|
|
74
74
|
adapter.configureHooks(config, installDir);
|
|
75
75
|
ok(`${config.name} configured`);
|
|
76
76
|
|
|
77
77
|
// ═══ Step 8: Install ArkaOS skill to Claude Code ═══
|
|
78
|
-
step(8,
|
|
78
|
+
step(8, 13, "Installing /arka skill...");
|
|
79
79
|
installSkill(config, installDir);
|
|
80
80
|
|
|
81
|
-
// ═══ Step 9:
|
|
82
|
-
step(9,
|
|
81
|
+
// ═══ Step 9: Install CLI wrapper and user instructions ═══
|
|
82
|
+
step(9, 13, "Installing CLI wrapper...");
|
|
83
|
+
const binDir = join(installDir, "bin");
|
|
84
|
+
ensureDir(binDir);
|
|
85
|
+
const wrapperSrc = join(ARKAOS_ROOT, "bin", "arka-claude");
|
|
86
|
+
if (existsSync(wrapperSrc)) {
|
|
87
|
+
copyFileSync(wrapperSrc, join(binDir, "arka-claude"));
|
|
88
|
+
try { chmodSync(join(binDir, "arka-claude"), 0o755); } catch {}
|
|
89
|
+
ok("arka-claude wrapper installed");
|
|
90
|
+
console.log(` Add to PATH: export PATH="$HOME/.arkaos/bin:$PATH"`);
|
|
91
|
+
console.log(` Optional alias: alias claude="arka-claude"`);
|
|
92
|
+
}
|
|
93
|
+
const claudeMdSrc = join(ARKAOS_ROOT, "config", "user-claude.md");
|
|
94
|
+
const userClaudeMd = join(homedir(), ".claude", "CLAUDE.md");
|
|
95
|
+
if (existsSync(claudeMdSrc) && !existsSync(userClaudeMd)) {
|
|
96
|
+
copyFileSync(claudeMdSrc, userClaudeMd);
|
|
97
|
+
ok("~/.claude/CLAUDE.md created (ArkaOS user instructions)");
|
|
98
|
+
} else if (existsSync(userClaudeMd)) {
|
|
99
|
+
ok("~/.claude/CLAUDE.md already exists (preserved)");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ═══ Step 10: Create references and profile ═══
|
|
103
|
+
step(10, 13, "Creating references...");
|
|
83
104
|
writeFileSync(join(installDir, ".repo-path"), ARKAOS_ROOT);
|
|
84
105
|
const skillsDir = join(config.skillsDir || join(homedir(), ".claude", "skills"), "arkaos");
|
|
85
106
|
ensureDir(skillsDir);
|
|
@@ -115,7 +136,7 @@ export async function install({ runtime, path, force }) {
|
|
|
115
136
|
}
|
|
116
137
|
|
|
117
138
|
// ═══ Step 10: Index knowledge base ═══
|
|
118
|
-
step(
|
|
139
|
+
step(11, 13, "Setting up knowledge base...");
|
|
119
140
|
if (userConfig.installKnowledge) {
|
|
120
141
|
const kbDb = join(installDir, "knowledge.db");
|
|
121
142
|
// Index ArkaOS skills first
|
|
@@ -143,7 +164,7 @@ export async function install({ runtime, path, force }) {
|
|
|
143
164
|
}
|
|
144
165
|
|
|
145
166
|
// ═══ Step 11: Verify installation ═══
|
|
146
|
-
step(
|
|
167
|
+
step(12, 13, "Verifying installation...");
|
|
147
168
|
let checks = 0;
|
|
148
169
|
if (existsSync(join(installDir, "config", "constitution.yaml"))) checks++;
|
|
149
170
|
if (existsSync(join(installDir, "config", "hooks", "user-prompt-submit.sh"))) checks++;
|
|
@@ -158,7 +179,7 @@ export async function install({ runtime, path, force }) {
|
|
|
158
179
|
ok(`${checks}/5 checks passed`);
|
|
159
180
|
|
|
160
181
|
// ═══ Step 12: Finalize ═══
|
|
161
|
-
step(
|
|
182
|
+
step(13, 13, "Finalizing...");
|
|
162
183
|
const manifest = {
|
|
163
184
|
version: VERSION,
|
|
164
185
|
runtime,
|
|
@@ -358,9 +379,11 @@ function installHooks(installDir) {
|
|
|
358
379
|
ensureDir(hooksDir);
|
|
359
380
|
|
|
360
381
|
const hookMap = {
|
|
361
|
-
"
|
|
362
|
-
"
|
|
363
|
-
"
|
|
382
|
+
"session-start.sh": "session-start.sh",
|
|
383
|
+
"user-prompt-submit.sh": "user-prompt-submit.sh",
|
|
384
|
+
"post-tool-use.sh": "post-tool-use.sh",
|
|
385
|
+
"pre-compact.sh": "pre-compact.sh",
|
|
386
|
+
"cwd-changed.sh": "cwd-changed.sh",
|
|
364
387
|
};
|
|
365
388
|
|
|
366
389
|
const srcHooksDir = join(ARKAOS_ROOT, "config", "hooks");
|
package/installer/update.js
CHANGED
|
@@ -83,9 +83,11 @@ export async function update() {
|
|
|
83
83
|
// ── 3. Update hooks ──
|
|
84
84
|
console.log(" [3/6] Updating hooks...");
|
|
85
85
|
const hookMap = {
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
86
|
+
"session-start.sh": "session-start.sh",
|
|
87
|
+
"user-prompt-submit.sh": "user-prompt-submit.sh",
|
|
88
|
+
"post-tool-use.sh": "post-tool-use.sh",
|
|
89
|
+
"pre-compact.sh": "pre-compact.sh",
|
|
90
|
+
"cwd-changed.sh": "cwd-changed.sh",
|
|
89
91
|
};
|
|
90
92
|
const srcHooksDir = join(ARKAOS_ROOT, "config", "hooks");
|
|
91
93
|
const destHooksDir = join(installDir, "config", "hooks");
|
|
@@ -110,8 +112,26 @@ export async function update() {
|
|
|
110
112
|
}
|
|
111
113
|
console.log(" ✓ Hooks updated");
|
|
112
114
|
|
|
113
|
-
// ── 4. Update
|
|
114
|
-
console.log(" [4/
|
|
115
|
+
// ── 4. Update CLI wrapper + user CLAUDE.md ──
|
|
116
|
+
console.log(" [4/7] Updating CLI wrapper and user instructions...");
|
|
117
|
+
const binDir = join(installDir, "bin");
|
|
118
|
+
mkdirSync(binDir, { recursive: true });
|
|
119
|
+
const wrapperSrc = join(ARKAOS_ROOT, "bin", "arka-claude");
|
|
120
|
+
if (existsSync(wrapperSrc)) {
|
|
121
|
+
copyFileSync(wrapperSrc, join(binDir, "arka-claude"));
|
|
122
|
+
try { chmodSync(join(binDir, "arka-claude"), 0o755); } catch {}
|
|
123
|
+
console.log(" ✓ arka-claude wrapper updated");
|
|
124
|
+
}
|
|
125
|
+
const userClaudeMd = join(homedir(), ".claude", "CLAUDE.md");
|
|
126
|
+
const claudeMdSrc = join(ARKAOS_ROOT, "config", "user-claude.md");
|
|
127
|
+
if (existsSync(claudeMdSrc)) {
|
|
128
|
+
mkdirSync(join(homedir(), ".claude"), { recursive: true });
|
|
129
|
+
copyFileSync(claudeMdSrc, userClaudeMd);
|
|
130
|
+
console.log(" ✓ ~/.claude/CLAUDE.md updated");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ── 5. Update /arka skill ──
|
|
134
|
+
console.log(" [5/7] Updating /arka skill...");
|
|
115
135
|
const skillSrc = join(ARKAOS_ROOT, "arka", "SKILL.md");
|
|
116
136
|
const skillDest = join(homedir(), ".claude", "skills", "arka");
|
|
117
137
|
mkdirSync(skillDest, { recursive: true });
|
|
@@ -122,13 +142,13 @@ export async function update() {
|
|
|
122
142
|
console.log(" ✓ /arka skill updated");
|
|
123
143
|
}
|
|
124
144
|
|
|
125
|
-
// ──
|
|
126
|
-
console.log(" [
|
|
145
|
+
// ── 6. Update .repo-path ──
|
|
146
|
+
console.log(" [6/7] Updating references...");
|
|
127
147
|
writeFileSync(join(installDir, ".repo-path"), ARKAOS_ROOT);
|
|
128
148
|
console.log(" ✓ Repo path updated");
|
|
129
149
|
|
|
130
|
-
// ──
|
|
131
|
-
console.log(" [
|
|
150
|
+
// ── 7. Update manifest ──
|
|
151
|
+
console.log(" [7/7] Finalizing...");
|
|
132
152
|
manifest.version = VERSION;
|
|
133
153
|
manifest.repoRoot = ARKAOS_ROOT;
|
|
134
154
|
manifest.updatedAt = new Date().toISOString();
|
|
@@ -147,7 +167,7 @@ export async function update() {
|
|
|
147
167
|
core_updated_at: new Date().toISOString()
|
|
148
168
|
};
|
|
149
169
|
writeFileSync(syncStatePath, JSON.stringify(syncState, null, 2));
|
|
150
|
-
console.log(" ✓ Sync state reset (
|
|
170
|
+
console.log(" ✓ Sync state reset (auto-detected on next Claude session)");
|
|
151
171
|
|
|
152
172
|
console.log(`
|
|
153
173
|
╔══════════════════════════════════════════╗
|
|
@@ -160,6 +180,7 @@ export async function update() {
|
|
|
160
180
|
Projects: ${profile.projectsDir || "not set"}
|
|
161
181
|
Vault: ${profile.vaultPath || "not set"}
|
|
162
182
|
|
|
163
|
-
|
|
183
|
+
Next time you open Claude Code, ArkaOS will automatically
|
|
184
|
+
detect the update and sync all your projects.
|
|
164
185
|
`);
|
|
165
186
|
}
|
|
@@ -1,20 +1,372 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_meta": {
|
|
3
3
|
"description": "ARKA OS — Project Ecosystem Registry",
|
|
4
|
-
"updated": "2026-04-
|
|
4
|
+
"updated": "2026-04-07"
|
|
5
5
|
},
|
|
6
6
|
"ecosystems": {
|
|
7
|
+
"edp": {
|
|
8
|
+
"name": "EDP",
|
|
9
|
+
"description": "Enterprise integration architecture platform for EDP (Energias de Portugal). Architect solutions, AI agents, and API governance for system analysts.",
|
|
10
|
+
"client": "EDP - Energias de Portugal",
|
|
11
|
+
"type": "client",
|
|
12
|
+
"status": "active",
|
|
13
|
+
"projects": [
|
|
14
|
+
"gba-web",
|
|
15
|
+
"gba-lurch-a2a",
|
|
16
|
+
"gba-zenodotus"
|
|
17
|
+
],
|
|
18
|
+
"squad": {
|
|
19
|
+
"roles": [
|
|
20
|
+
"project-manager",
|
|
21
|
+
"backend-developer",
|
|
22
|
+
"frontend-developer",
|
|
23
|
+
"ai-specialist",
|
|
24
|
+
"context-analyst",
|
|
25
|
+
"security-engineer",
|
|
26
|
+
"qa-tester"
|
|
27
|
+
],
|
|
28
|
+
"description": "Dedicated EDP squad for integration architecture platform"
|
|
29
|
+
},
|
|
30
|
+
"tech_stack": {
|
|
31
|
+
"frontend": ["Laravel 12", "Inertia.js 2", "Vue 3", "TypeScript", "Tailwind CSS 4"],
|
|
32
|
+
"backend": ["PHP 8.4", "Python 3.12+", "FastAPI"],
|
|
33
|
+
"databases": ["MySQL", "PostgreSQL", "SQLite (dev)", "Redis"],
|
|
34
|
+
"ai": ["Azure OpenAI (GPT-5-mini-gba)", "pydantic-ai", "FastA2A"],
|
|
35
|
+
"infra": ["Docker", "FrankenPHP", "Laravel Octane", "Uvicorn", "Kafka", "AWS RDS", "ELK Stack"],
|
|
36
|
+
"auth": ["Azure AD OAuth 2.0", "JWT", "Spatie Permissions"],
|
|
37
|
+
"protocols": ["REST", "SOAP", "A2A (JSON-RPC 2.0)", "MCP"]
|
|
38
|
+
},
|
|
39
|
+
"created_at": "2026-03-17"
|
|
40
|
+
},
|
|
41
|
+
"rockport": {
|
|
42
|
+
"name": "Rockport",
|
|
43
|
+
"description": "E-commerce marketplace platform for Rockport premium footwear. CRM, storefront, SAP ERP integration, and logistics microservice.",
|
|
44
|
+
"client": "Rockport",
|
|
45
|
+
"type": "client",
|
|
46
|
+
"status": "active",
|
|
47
|
+
"projects": [
|
|
48
|
+
"crm-rockport",
|
|
49
|
+
"rockport-ecommerce-v2",
|
|
50
|
+
"sap-erp-service",
|
|
51
|
+
"gologistic-ms"
|
|
52
|
+
],
|
|
53
|
+
"squad": {
|
|
54
|
+
"roles": [
|
|
55
|
+
"project-manager",
|
|
56
|
+
"backend-developer",
|
|
57
|
+
"frontend-developer",
|
|
58
|
+
"ecommerce-specialist",
|
|
59
|
+
"integrations-specialist",
|
|
60
|
+
"security-engineer",
|
|
61
|
+
"qa-tester"
|
|
62
|
+
],
|
|
63
|
+
"description": "Dedicated Rockport squad for e-commerce platform"
|
|
64
|
+
},
|
|
65
|
+
"tech_stack": {
|
|
66
|
+
"frontend": ["Nuxt 4", "Vue 3", "TypeScript", "Tailwind CSS 4", "Nuxt UI 4", "Pinia 3", "Vuetify 3"],
|
|
67
|
+
"backend": ["PHP 8.4", "Laravel 11/12", "Laravel Modules"],
|
|
68
|
+
"databases": ["PostgreSQL", "Redis", "Meilisearch", "SQLite (dev)"],
|
|
69
|
+
"payments": ["SIBS", "Klarna", "Adyen", "MBWay"],
|
|
70
|
+
"integrations": ["SAP Business One", "Kafka", "GoLogistic", "DPD", "Mirakl", "Actito", "SendGrid", "Infobip"],
|
|
71
|
+
"infra": ["Docker", "FrankenPHP", "Octane/Roadrunner", "Horizon", "DigitalOcean Spaces", "AWS S3"],
|
|
72
|
+
"auth": ["Sanctum", "Spatie Permissions", "Social Login (Google/Facebook/Apple)"],
|
|
73
|
+
"protocols": ["REST", "Kafka", "WebSocket (Reverb)"]
|
|
74
|
+
},
|
|
75
|
+
"created_at": "2026-03-17"
|
|
76
|
+
},
|
|
77
|
+
"clubefashion": {
|
|
78
|
+
"name": "ClubeFashion",
|
|
79
|
+
"description": "E-commerce marketplace platform for ClubeFashion. Complex ecosystem with CRM migration in progress, Mirakl marketplace, Primavera ERP, and AI-ready modernization.",
|
|
80
|
+
"client": "ClubeFashion",
|
|
81
|
+
"type": "client",
|
|
82
|
+
"status": "active",
|
|
83
|
+
"projects": [
|
|
84
|
+
"the-burrow-hub",
|
|
85
|
+
"marketplace-ms",
|
|
86
|
+
"goblinledger",
|
|
87
|
+
"crm-cf-v2",
|
|
88
|
+
"nuxt-boilerplate",
|
|
89
|
+
"crm-frontend-v2"
|
|
90
|
+
],
|
|
91
|
+
"squad": {
|
|
92
|
+
"roles": [
|
|
93
|
+
"project-manager",
|
|
94
|
+
"backend-developer",
|
|
95
|
+
"frontend-developer",
|
|
96
|
+
"ecommerce-specialist",
|
|
97
|
+
"marketplace-specialist",
|
|
98
|
+
"erp-specialist",
|
|
99
|
+
"migration-architect",
|
|
100
|
+
"ai-specialist",
|
|
101
|
+
"security-engineer",
|
|
102
|
+
"qa-tester",
|
|
103
|
+
"devops"
|
|
104
|
+
],
|
|
105
|
+
"description": "Dedicated ClubeFashion squad — largest team due to migration complexity"
|
|
106
|
+
},
|
|
107
|
+
"tech_stack": {
|
|
108
|
+
"frontend": ["Nuxt 4", "Vue 3", "Vuetify 3", "Nuxt UI 4", "TypeScript", "Tailwind CSS 4", "Pinia"],
|
|
109
|
+
"backend": ["PHP 8.4/8.5", "Laravel 11/12", "Laravel Modules (19)"],
|
|
110
|
+
"databases": ["PostgreSQL", "Redis", "Meilisearch", "SQLite (dev)"],
|
|
111
|
+
"payments": ["SIBS", "Klarna", "Adyen", "MBWay"],
|
|
112
|
+
"integrations": ["Mirakl (marketplace-ms)", "Primavera ERP (goblinledger)", "Kafka (30+ topics)", "Actito", "SendGrid", "Infobip", "Skeepers", "DPD", "Google Merchant"],
|
|
113
|
+
"infra": ["Docker", "FrankenPHP", "Octane/Roadrunner", "Horizon", "AWS S3", "DigitalOcean Spaces"],
|
|
114
|
+
"auth": ["Sanctum", "Spatie Permissions", "Social Login"],
|
|
115
|
+
"protocols": ["REST", "Kafka", "WebSocket (Reverb)"],
|
|
116
|
+
"project_management": ["ClickUp (Space: 90153217724)"]
|
|
117
|
+
},
|
|
118
|
+
"migration": {
|
|
119
|
+
"from": "the-burrow-hub (Laravel 11, 19 modules, 431 migrations, 163 models)",
|
|
120
|
+
"to": "crm-cf-v2 (Laravel 12, AI-ready, strict types) + crm-frontend-v2 (Vue 3 SPA)",
|
|
121
|
+
"status": "paused — foundation laid, 0% content migration",
|
|
122
|
+
"priority": "planning phase"
|
|
123
|
+
},
|
|
124
|
+
"created_at": "2026-03-17"
|
|
125
|
+
},
|
|
126
|
+
"fovory": {
|
|
127
|
+
"name": "Fovory",
|
|
128
|
+
"description": "Supplier-to-Shopify integration engine with custom Laravel codebase (fovory-supplier-sync) AND Shopify store operations/marketing. Dual nature: dev squad for code work, ops/marketing squad for store management.",
|
|
129
|
+
"client": "Fovory (internal business)",
|
|
130
|
+
"type": "internal",
|
|
131
|
+
"status": "active",
|
|
132
|
+
"projects": [
|
|
133
|
+
"fovory-supplier-sync"
|
|
134
|
+
],
|
|
135
|
+
"platform": "Shopify + Custom Laravel App",
|
|
136
|
+
"squad": {
|
|
137
|
+
"roles": [
|
|
138
|
+
"tech-lead",
|
|
139
|
+
"backend-developer",
|
|
140
|
+
"frontend-developer",
|
|
141
|
+
"ai-specialist",
|
|
142
|
+
"security-engineer",
|
|
143
|
+
"qa-tester",
|
|
144
|
+
"operations-manager",
|
|
145
|
+
"ecommerce-manager",
|
|
146
|
+
"product-manager",
|
|
147
|
+
"marketing-strategist",
|
|
148
|
+
"content-creator",
|
|
149
|
+
"ads-specialist",
|
|
150
|
+
"email-marketing",
|
|
151
|
+
"seo-specialist",
|
|
152
|
+
"analytics-specialist",
|
|
153
|
+
"pricing-strategist"
|
|
154
|
+
],
|
|
155
|
+
"description": "Dual squad: dev team (Laravel/Vue) + operations/marketing team (Shopify store)"
|
|
156
|
+
},
|
|
157
|
+
"tech_stack": {
|
|
158
|
+
"frontend": ["Vue 3.5", "Inertia.js 3", "TypeScript", "Tailwind CSS 4", "Nuxt UI 4", "TipTap 3", "TanStack Table", "Unovis"],
|
|
159
|
+
"backend": ["PHP 8.4", "Laravel 13", "Laravel Horizon", "Laravel Reverb", "Laravel Fortify", "Laravel Pennant"],
|
|
160
|
+
"databases": ["PostgreSQL", "Redis", "SQLite (dev)"],
|
|
161
|
+
"ai": ["Laravel AI SDK", "Claude/Anthropic", "OpenRouter", "OpenAI"],
|
|
162
|
+
"import": ["Maatwebsite Excel", "WebSocket progress"],
|
|
163
|
+
"testing": ["Pest 5", "Playwright", "Larastan", "Rector", "Pint"],
|
|
164
|
+
"build": ["Bun", "Vite"],
|
|
165
|
+
"shopify": ["Shopify GraphQL Admin API", "Shopify MCP"]
|
|
166
|
+
},
|
|
167
|
+
"codebase_stats": {
|
|
168
|
+
"models": 43,
|
|
169
|
+
"actions": 75,
|
|
170
|
+
"controllers": 42,
|
|
171
|
+
"vue_pages": 44,
|
|
172
|
+
"migrations": 72,
|
|
173
|
+
"tests": 943,
|
|
174
|
+
"assertions": 2997,
|
|
175
|
+
"updated": "2026-04-07"
|
|
176
|
+
},
|
|
177
|
+
"capabilities": {
|
|
178
|
+
"dev": ["Supplier integration (API + CSV)", "Product CRUD with variants/offers", "AI system (providers/agents/personas/triggers)", "Bulk import (Excel + WebSocket)", "E-commerce settings", "Multi-locale translations"],
|
|
179
|
+
"store": ["Shopify MCP", "Product management", "CSV bulk operations", "Pricing strategy (min 35% margin)", "Stock management (15min sync, 5% buffer)", "Translations"],
|
|
180
|
+
"marketing": ["Social media (Instagram, LinkedIn, TikTok, X)", "Email campaigns", "Google Ads", "Meta Ads", "Content calendars", "Reels/Shorts scripts"],
|
|
181
|
+
"analytics": ["Google Analytics 4", "Google Ads", "Shopify Analytics", "Meta Pixel"],
|
|
182
|
+
"knowledge": ["KB personas for content voice", "Market research", "Competitor analysis", "Trend analysis"],
|
|
183
|
+
"automation": ["Bulk product updates", "Price optimization", "Translation automation", "Ad creative generation"]
|
|
184
|
+
},
|
|
185
|
+
"created_at": "2026-03-17",
|
|
186
|
+
"updated_at": "2026-04-07"
|
|
187
|
+
},
|
|
7
188
|
"wizardingcode": {
|
|
189
|
+
"name": "WizardingCode Internal",
|
|
190
|
+
"description": "Internal growth engine. Revenue target: 1M EUR by Dec 2026. Micro-SaaS, AI tools, content creation, financial management, trend scouting.",
|
|
191
|
+
"client": "WizardingCode (internal)",
|
|
192
|
+
"type": "internal",
|
|
193
|
+
"status": "active",
|
|
194
|
+
"projects": [
|
|
195
|
+
"lora-tester",
|
|
196
|
+
"purz-comfyui-workflows"
|
|
197
|
+
],
|
|
198
|
+
"squad": {
|
|
199
|
+
"roles": [
|
|
200
|
+
"ceo-copilot",
|
|
201
|
+
"creative-director",
|
|
202
|
+
"marketing-lead",
|
|
203
|
+
"data-analyst",
|
|
204
|
+
"market-scout",
|
|
205
|
+
"content-creator",
|
|
206
|
+
"financial-controller",
|
|
207
|
+
"full-stack-builder",
|
|
208
|
+
"growth-hacker",
|
|
209
|
+
"community-manager"
|
|
210
|
+
],
|
|
211
|
+
"description": "Growth team — strategy, content, finance, and product development"
|
|
212
|
+
},
|
|
213
|
+
"revenue_target": {
|
|
214
|
+
"amount": 1000000,
|
|
215
|
+
"currency": "EUR",
|
|
216
|
+
"deadline": "2026-12-31",
|
|
217
|
+
"monthly_target": 83333
|
|
218
|
+
},
|
|
219
|
+
"capabilities": {
|
|
220
|
+
"strategy": ["Idea evaluation", "OKRs", "Roadmap", "Market analysis"],
|
|
221
|
+
"product": ["Micro-SaaS", "AI tools", "ARKA OS Pro", "Landing pages"],
|
|
222
|
+
"content": ["AI articles", "Entrepreneurship", "YouTube", "LinkedIn", "Newsletter"],
|
|
223
|
+
"finance": ["Gmail accounting", "Revenue tracking", "Investment plans", "Invoicing"],
|
|
224
|
+
"marketing": ["Social media", "Email funnels", "Google Ads", "Growth hacking"]
|
|
225
|
+
},
|
|
226
|
+
"integrations": ["Gmail (accounting)", "Google Calendar", "ClickUp", "InvoiceExpress"],
|
|
227
|
+
"created_at": "2026-03-17"
|
|
228
|
+
},
|
|
229
|
+
"rothbard100": {
|
|
230
|
+
"name": "Rothbard 100",
|
|
231
|
+
"description": "Libertarian event platform celebrating the 100th anniversary of Murray Rothbard. Landing page, future registration app, and event CRM. Organized by Cataláxia EDITORA. Event: Porto, 27 Jun 2026.",
|
|
232
|
+
"client": "Cataláxia EDITORA",
|
|
233
|
+
"type": "internal",
|
|
234
|
+
"status": "active",
|
|
235
|
+
"projects": [
|
|
236
|
+
"rothbard100-landing"
|
|
237
|
+
],
|
|
238
|
+
"squad": {
|
|
239
|
+
"roles": [
|
|
240
|
+
"project-manager",
|
|
241
|
+
"frontend-developer",
|
|
242
|
+
"content-creator",
|
|
243
|
+
"seo-marketing",
|
|
244
|
+
"security-engineer",
|
|
245
|
+
"qa-tester",
|
|
246
|
+
"devops"
|
|
247
|
+
],
|
|
248
|
+
"description": "Dedicated Rothbard 100 squad for event platform — landing page focus"
|
|
249
|
+
},
|
|
250
|
+
"tech_stack": {
|
|
251
|
+
"frontend": ["Nuxt 4.4.2", "Vue 3", "TypeScript", "Tailwind CSS 4", "Nuxt UI v4.5.1"],
|
|
252
|
+
"i18n": ["@nuxtjs/i18n (pt, en, fr, es, de)"],
|
|
253
|
+
"content": ["@nuxt/content v3"],
|
|
254
|
+
"images": ["@nuxt/image v2"],
|
|
255
|
+
"icons": ["Iconify (lucide + simple-icons)"],
|
|
256
|
+
"infra": ["Cloudflare Pages", "pnpm"],
|
|
257
|
+
"build": ["SSG (Nitro prerender)"]
|
|
258
|
+
},
|
|
259
|
+
"brand": {
|
|
260
|
+
"primary_color": "#FFC939",
|
|
261
|
+
"secondary_color": "#000000",
|
|
262
|
+
"theme": "dark-premium-revolutionary",
|
|
263
|
+
"style": "Silicon Valley event level",
|
|
264
|
+
"font": "Inter (400-900)",
|
|
265
|
+
"mode": "light-only"
|
|
266
|
+
},
|
|
267
|
+
"created_at": "2026-03-26",
|
|
268
|
+
"updated_at": "2026-04-07"
|
|
269
|
+
},
|
|
270
|
+
"cascais24horas": {
|
|
271
|
+
"name": "Cascais 24 Horas",
|
|
272
|
+
"description": "Local news and events portal for Cascais, Portugal. Security-critical rebuild after WordPress hack. CMS backend with admin panel and public-facing Nuxt frontend.",
|
|
273
|
+
"client": "WizardingCode (internal project)",
|
|
274
|
+
"type": "internal",
|
|
275
|
+
"status": "active",
|
|
276
|
+
"projects": [
|
|
277
|
+
"cascais24horas-cms",
|
|
278
|
+
"cascais24horas-web"
|
|
279
|
+
],
|
|
280
|
+
"squad": {
|
|
281
|
+
"roles": [
|
|
282
|
+
"project-manager",
|
|
283
|
+
"backend-developer",
|
|
284
|
+
"frontend-developer-cms",
|
|
285
|
+
"frontend-developer-web",
|
|
286
|
+
"content-creator",
|
|
287
|
+
"seo-marketing",
|
|
288
|
+
"security-engineer",
|
|
289
|
+
"qa-tester"
|
|
290
|
+
],
|
|
291
|
+
"description": "Dedicated Cascais24Horas squad for news portal rebuild"
|
|
292
|
+
},
|
|
293
|
+
"tech_stack": {
|
|
294
|
+
"frontend_cms": ["Vue 3", "Inertia.js 3", "TypeScript", "Tailwind CSS 4", "TipTap"],
|
|
295
|
+
"frontend_web": ["Nuxt 4", "Nuxt UI 4", "Vue 3", "TypeScript", "Tailwind CSS 4"],
|
|
296
|
+
"backend": ["PHP 8.4", "Laravel 12"],
|
|
297
|
+
"databases": ["SQLite (dev)", "PostgreSQL (prod)", "Redis"],
|
|
298
|
+
"search": ["Laravel Scout"],
|
|
299
|
+
"media": ["Spatie MediaLibrary"],
|
|
300
|
+
"auth": ["Sanctum", "Spatie Permission"],
|
|
301
|
+
"seo": ["Spatie Sitemap", "Nuxt SEO", "Google News"],
|
|
302
|
+
"content": ["@nuxt/content v3", "TipTap", "HTML Purifier"]
|
|
303
|
+
},
|
|
304
|
+
"security_context": "Rebuilt after WordPress hack — security is first-class concern",
|
|
305
|
+
"created_at": "2026-03-31"
|
|
306
|
+
},
|
|
307
|
+
"marlon": {
|
|
308
|
+
"name": "Marlon",
|
|
309
|
+
"description": "Services and consulting client of WizardingCode. Web application ecosystem with Nuxt/Vue frontend. Dedicated squad and orchestrator.",
|
|
310
|
+
"client": "Marlon",
|
|
311
|
+
"type": "client",
|
|
312
|
+
"status": "active",
|
|
313
|
+
"projects": [],
|
|
314
|
+
"squad": {
|
|
315
|
+
"roles": [
|
|
316
|
+
"project-manager",
|
|
317
|
+
"backend-developer",
|
|
318
|
+
"frontend-developer",
|
|
319
|
+
"security-engineer",
|
|
320
|
+
"qa-tester",
|
|
321
|
+
"devops"
|
|
322
|
+
],
|
|
323
|
+
"description": "Dedicated Marlon squad for services/consulting web platform"
|
|
324
|
+
},
|
|
325
|
+
"tech_stack": {
|
|
326
|
+
"frontend": ["Nuxt 4", "Vue 3", "TypeScript", "Tailwind CSS 4", "Nuxt UI 4"],
|
|
327
|
+
"backend": ["PHP 8.4", "Laravel 12"],
|
|
328
|
+
"databases": ["PostgreSQL", "SQLite (dev)", "Redis"],
|
|
329
|
+
"auth": ["Sanctum", "Spatie Permissions"],
|
|
330
|
+
"infra": ["Docker", "pnpm", "Vercel/Netlify"]
|
|
331
|
+
},
|
|
332
|
+
"created_at": "2026-04-06"
|
|
333
|
+
},
|
|
334
|
+
"zugatv": {
|
|
335
|
+
"name": "ZugaTV",
|
|
336
|
+
"description": "Portuguese content/media partner. Viral interactive content and audience growth funnels. First project: Raio-X Ideologico — political ideology quiz for follower growth.",
|
|
337
|
+
"client": "ZugaTV",
|
|
338
|
+
"type": "partner",
|
|
339
|
+
"status": "active",
|
|
8
340
|
"projects": [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
341
|
+
"raiox-ideologico"
|
|
342
|
+
],
|
|
343
|
+
"squad": {
|
|
344
|
+
"roles": [
|
|
345
|
+
"project-manager",
|
|
346
|
+
"frontend-developer",
|
|
347
|
+
"content-creator",
|
|
348
|
+
"seo-marketing",
|
|
349
|
+
"security-engineer",
|
|
350
|
+
"qa-tester"
|
|
351
|
+
],
|
|
352
|
+
"description": "Dedicated ZugaTV squad for viral content platforms"
|
|
353
|
+
},
|
|
354
|
+
"tech_stack": {
|
|
355
|
+
"frontend": ["Nuxt 4", "Vue 3", "TypeScript", "Tailwind CSS 4", "Nuxt UI 4", "Chart.js", "vue-chartjs"],
|
|
356
|
+
"content": ["@nuxt/content v3"],
|
|
357
|
+
"composables": ["@vueuse/nuxt"],
|
|
358
|
+
"seo": ["@nuxtjs/seo", "@nuxtjs/robots", "nuxt-og-image"],
|
|
359
|
+
"analytics": ["nuxt-plausible"],
|
|
360
|
+
"fonts": ["@nuxt/fonts (Inter)"],
|
|
361
|
+
"testing": ["Vitest", "@vue/test-utils", "happy-dom"],
|
|
362
|
+
"build": ["pnpm", "SSR/SPA hybrid", "Vercel or Cloudflare Pages"]
|
|
363
|
+
},
|
|
364
|
+
"constraints": {
|
|
365
|
+
"architecture": "100% client-side scoring — NO backend, NO database, NO auth",
|
|
366
|
+
"privacy": "RGPD by design — no personal data stored server-side",
|
|
367
|
+
"language": "Portuguese (pt-PT) only — no i18n"
|
|
368
|
+
},
|
|
369
|
+
"created_at": "2026-03-28"
|
|
18
370
|
}
|
|
19
371
|
}
|
|
20
372
|
}
|
package/package.json
CHANGED