192 lines
7.6 KiB
Bash
192 lines
7.6 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
# crazycoder-setup.sh
|
||
|
|
# Runs via postStartCommand — configures aichat, opencode and Claude Code MCP.
|
||
|
|
# Executed by Zed/VS Code/Cursor after the container starts, as remoteUser (devuser).
|
||
|
|
|
||
|
|
export USERNAME=${USERNAME:-devuser}
|
||
|
|
export WORKSPACE=${WORKSPACE:-/workspace}
|
||
|
|
export LOCAL_LLM_API=${LOCAL_LLM_API:-http://host.docker.internal:1248/v1}
|
||
|
|
export LLM_MODEL=${LLM_MODEL:-'ollama:qwen/qwen3-coder-next@4bit'}
|
||
|
|
export AICHAT_API_BASE=$(sed -E 's@(https?://[^/]+).*$@\1@' <<< $LOCAL_LLM_API)
|
||
|
|
export MAXTOKEN=${MAXTOKEN:-4096}
|
||
|
|
export NUM_CTX=${NUM_CTX:-32768}
|
||
|
|
export REPEAT_PENALTY=${REPEAT_PENALTY:-1.3}
|
||
|
|
export TEMPERATURE=${TEMPERATURE:-0.5}
|
||
|
|
export TOP_P=${TOP_P:-0.95}
|
||
|
|
export TOP_K=${TOP_K:-40}
|
||
|
|
export STOP=${STOP:-"[\"</s>\", \"\\n\\n\", \"def \", \"class \"]"}
|
||
|
|
|
||
|
|
# --- Clear Zed external_agents so Zed can re-download without "Directory not empty" ---
|
||
|
|
rm -rf "$HOME/.local/share/zed/external_agents"
|
||
|
|
|
||
|
|
# --- Initialise .ai_index si nécessaire ---
|
||
|
|
if [ ! -d "$WORKSPACE/.ai_index" ]; then
|
||
|
|
mkdir -p "$WORKSPACE/.ai_index/ast"
|
||
|
|
mkdir -p "$WORKSPACE/.ai_index/embeddings"
|
||
|
|
touch "$WORKSPACE/.ai_index/graph.db"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# --- Configure PATH dans .bashrc (une seule fois) ---
|
||
|
|
if ! grep -q "LOCAL_LLM_API" "$HOME/.bashrc"; then
|
||
|
|
echo "export LOCAL_LLM_API=$LOCAL_LLM_API" >> "$HOME/.bashrc"
|
||
|
|
echo "export AICHAT_API_BASE=$AICHAT_API_BASE" >> "$HOME/.bashrc"
|
||
|
|
echo "export PATH=\$HOME/.cargo/bin:\$HOME/go/bin:\$HOME/.local/bin:\$PATH" >> "$HOME/.bashrc"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# --- Configure aichat ---
|
||
|
|
mkdir -p "$HOME/.config/aichat"
|
||
|
|
cat > "$HOME/.config/aichat/config.yaml" <<EOF
|
||
|
|
model: lmstudio:${LLM_MODEL#*:}
|
||
|
|
clients:
|
||
|
|
- type: openai-compatible
|
||
|
|
name: lmstudio
|
||
|
|
api_base: ${LOCAL_LLM_API}
|
||
|
|
api_key: placeholder
|
||
|
|
models:
|
||
|
|
- name: ${LLM_MODEL#*:}
|
||
|
|
max_input_tokens: ${NUM_CTX}
|
||
|
|
temperature: ${TEMPERATURE}
|
||
|
|
top_p: ${TOP_P}
|
||
|
|
EOF
|
||
|
|
|
||
|
|
# --- Configure opencode ---
|
||
|
|
RUST_ANALYZER=$(rustup which --toolchain nightly rust-analyzer 2>/dev/null || true)
|
||
|
|
|
||
|
|
mkdir -p "$HOME/.config/opencode"
|
||
|
|
jq -n \
|
||
|
|
--arg workspace "$WORKSPACE" \
|
||
|
|
--arg gopath "$GOPATH" \
|
||
|
|
--arg rust_analyzer "$RUST_ANALYZER" \
|
||
|
|
--arg llm_api "$LOCAL_LLM_API" \
|
||
|
|
--arg llm_model "${LLM_MODEL#*:}" \
|
||
|
|
--arg full_model "$LLM_MODEL" \
|
||
|
|
--arg github_token "${GITHUB_TOKEN:-}" \
|
||
|
|
--arg gitea_url "${GITEA_URL:-}" \
|
||
|
|
--arg gitea_token "${GITEA_TOKEN:-}" \
|
||
|
|
--argjson temperature "$TEMPERATURE" \
|
||
|
|
--argjson top_p "$TOP_P" \
|
||
|
|
--argjson top_k "$TOP_K" \
|
||
|
|
--argjson max_tokens "$MAXTOKEN" \
|
||
|
|
--argjson num_ctx "$NUM_CTX" \
|
||
|
|
--argjson repeat_penalty "$REPEAT_PENALTY" \
|
||
|
|
--argjson stop "$STOP" \
|
||
|
|
'{
|
||
|
|
"$schema": "https://opencode.ai/config.json",
|
||
|
|
"mcp": ({
|
||
|
|
"go-lsp-tools": {
|
||
|
|
"type": "local",
|
||
|
|
"command": ["mcp-language-server", "--workspace", $workspace, "--lsp", "gopls"],
|
||
|
|
"enabled": true,
|
||
|
|
"environment": { "GOPATH": $gopath }
|
||
|
|
},
|
||
|
|
"python-lsp-tools": {
|
||
|
|
"type": "local",
|
||
|
|
"command": ["mcp-language-server", "--workspace", $workspace, "--lsp", "pyright-langserver", "--", "--stdio"],
|
||
|
|
"enabled": true
|
||
|
|
},
|
||
|
|
"rust-lsp-tools": {
|
||
|
|
"type": "local",
|
||
|
|
"command": ["mcp-language-server", "--workspace", $workspace, "--lsp", $rust_analyzer],
|
||
|
|
"enabled": true
|
||
|
|
},
|
||
|
|
"typescript-lsp-tools": {
|
||
|
|
"type": "local",
|
||
|
|
"command": ["mcp-language-server", "--workspace", $workspace, "--lsp", "typescript-language-server", "--", "--stdio"],
|
||
|
|
"enabled": true
|
||
|
|
},
|
||
|
|
"shell": {
|
||
|
|
"type": "local",
|
||
|
|
"command": ["mcp-server-commands"],
|
||
|
|
"enabled": true
|
||
|
|
},
|
||
|
|
"github": (if $github_token != "" then {
|
||
|
|
"type": "local",
|
||
|
|
"command": ["github-mcp-server", "stdio"],
|
||
|
|
"enabled": true,
|
||
|
|
"environment": { "GITHUB_PERSONAL_ACCESS_TOKEN": $github_token }
|
||
|
|
} else null end),
|
||
|
|
"gitea": (if ($gitea_token != "" and $gitea_url != "") then {
|
||
|
|
"type": "local",
|
||
|
|
"command": ["gitea-mcp", "-t", "stdio", "--host", $gitea_url, "--token", $gitea_token],
|
||
|
|
"enabled": true
|
||
|
|
} else null end)
|
||
|
|
} | with_entries(select(.value != null))),
|
||
|
|
"provider": {
|
||
|
|
"lmstudio": {
|
||
|
|
"npm": "@ai-sdk/openai-compatible",
|
||
|
|
"name": "LM Studio (local)",
|
||
|
|
"options": { "baseURL": $llm_api, "apiKey": "" },
|
||
|
|
"models": {
|
||
|
|
"qwen/qwen3-coder-next@4bit": {
|
||
|
|
"name": "Qwen 3 Coder Next 80B — (LLMStudio ; 4-bit)",
|
||
|
|
"limit": { "context": 200000, "output": 65536 },
|
||
|
|
"settings": { "temperature": $temperature, "top_p": $top_p, "top_k": $top_k, "max_tokens": $max_tokens, "num_ctx": $num_ctx, "repeat_penalty": $repeat_penalty, "stop": $stop }
|
||
|
|
},
|
||
|
|
"qwen/qwen3-coder-next@6bit": {
|
||
|
|
"name": "Qwen 3 Coder Next 80B — (LLMStudio ; 6-bit)",
|
||
|
|
"limit": { "context": 200000, "output": 65536 },
|
||
|
|
"settings": { "temperature": $temperature, "top_p": $top_p, "top_k": $top_k, "max_tokens": $max_tokens, "num_ctx": $num_ctx, "repeat_penalty": $repeat_penalty, "stop": $stop }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"model": $full_model
|
||
|
|
}' > "$HOME/.config/opencode/opencode.json"
|
||
|
|
|
||
|
|
# --- Configure MCP pour Claude Code ---
|
||
|
|
MCP_SERVERS=$(jq -n \
|
||
|
|
--arg workspace "$WORKSPACE" \
|
||
|
|
--arg gopath "$GOPATH" \
|
||
|
|
--arg rust_analyzer "$RUST_ANALYZER" \
|
||
|
|
--arg github_token "${GITHUB_TOKEN:-}" \
|
||
|
|
--arg gitea_url "${GITEA_URL:-}" \
|
||
|
|
--arg gitea_token "${GITEA_TOKEN:-}" \
|
||
|
|
'{
|
||
|
|
"go-lsp-tools": {
|
||
|
|
"type": "stdio",
|
||
|
|
"command": "mcp-language-server",
|
||
|
|
"args": ["--workspace", $workspace, "--lsp", "gopls"],
|
||
|
|
"env": { "GOPATH": $gopath }
|
||
|
|
},
|
||
|
|
"python-lsp-tools": {
|
||
|
|
"type": "stdio",
|
||
|
|
"command": "mcp-language-server",
|
||
|
|
"args": ["--workspace", $workspace, "--lsp", "pyright-langserver", "--", "--stdio"]
|
||
|
|
},
|
||
|
|
"rust-lsp-tools": {
|
||
|
|
"type": "stdio",
|
||
|
|
"command": "mcp-language-server",
|
||
|
|
"args": ["--workspace", $workspace, "--lsp", $rust_analyzer]
|
||
|
|
},
|
||
|
|
"typescript-lsp-tools": {
|
||
|
|
"type": "stdio",
|
||
|
|
"command": "mcp-language-server",
|
||
|
|
"args": ["--workspace", $workspace, "--lsp", "typescript-language-server", "--", "--stdio"]
|
||
|
|
},
|
||
|
|
"github": (if $github_token != "" then {
|
||
|
|
"type": "stdio",
|
||
|
|
"command": "github-mcp-server",
|
||
|
|
"args": ["stdio"],
|
||
|
|
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": $github_token }
|
||
|
|
} else null end),
|
||
|
|
"shell": {
|
||
|
|
"type": "stdio",
|
||
|
|
"command": "mcp-server-commands"
|
||
|
|
},
|
||
|
|
"gitea": (if ($gitea_token != "" and $gitea_url != "") then {
|
||
|
|
"type": "stdio",
|
||
|
|
"command": "gitea-mcp",
|
||
|
|
"args": ["-t", "stdio", "--host", $gitea_url, "--token", $gitea_token]
|
||
|
|
} else null end)
|
||
|
|
} | with_entries(select(.value != null))')
|
||
|
|
|
||
|
|
CLAUDE_JSON="$HOME/.claude/.claude.json"
|
||
|
|
mkdir -p "$HOME/.claude"
|
||
|
|
|
||
|
|
if [ -f "$CLAUDE_JSON" ]; then
|
||
|
|
jq --argjson mcp "$MCP_SERVERS" '. + {mcpServers: $mcp}' "$CLAUDE_JSON" > "${CLAUDE_JSON}.tmp" \
|
||
|
|
&& mv "${CLAUDE_JSON}.tmp" "$CLAUDE_JSON"
|
||
|
|
else
|
||
|
|
jq -n --argjson mcp "$MCP_SERVERS" '{mcpServers: $mcp}' > "$CLAUDE_JSON"
|
||
|
|
fi
|