Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
135 lines (115 loc) · 5.91 KB
/
Copy pathDockerfile
File metadata and controls
135 lines (115 loc) · 5.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
FROM archlinux:base-devel
LABEL maintainer="nemanjan00 nemanjan00@gmail.com"
ARG UID=1000
ARG GID=1000
USER 0
# Install all system packages
RUN pacman -Syu --noconfirm \
git base-devel curl wget \
zsh \
clang cmake re2c protobuf \
htop ripgrep the_silver_searcher eza fzf jq jc \
zip unzip 7zip unrar zstd \
ctags strace tree xxd file \
diffutils patch \
man-db man-pages \
nss nspr \
python-pynvim neovim tree-sitter-cli \
tmux \
docker \
miller socat \
xorg-server-xvfb xorg-xauth \
scrot xdotool xorg-xwininfo \
wqy-microhei
# Generate Chinese locales. Some GUI apps (e.g. Wine apps that assume a
# non-Unicode Chinese environment) need zh_CN.GB18030 to launch — Wine
# derives codepage 936 from LANG — while zh_CN.UTF-8 covers modern apps.
# The base Arch image NoExtracts glibc's i18n source (usr/share/i18n/*),
# keeping only en/UTF-8/C — so locale-gen has nothing to compile zh_CN
# from. Re-include the zh_CN locale source + GB18030 charmap, reinstall
# glibc to extract them, then generate.
RUN printf '\nNoExtract = !usr/share/i18n/locales/zh_CN !usr/share/i18n/charmaps/GB18030.gz\n' \
>> /etc/pacman.conf && \
pacman -S --noconfirm glibc && \
sed -i -e 's/^#\(zh_CN.GB18030\)/\1/' -e 's/^#\(zh_CN.UTF-8\)/\1/' /etc/locale.gen && \
locale-gen && \
locale -a | grep -i zh_CN
# Create user with home at /work
RUN groupadd -g $GID user && \
useradd -u $UID -g $GID -s /usr/bin/zsh -d /work user && \
mkdir /work && \
chown $UID:$GID /work
WORKDIR /work
# Install asdf version manager
USER $UID
ENV ASDF_DATA_DIR=/work/.asdf
RUN curl -fsSL https://github.com/asdf-vm/asdf/releases/download/v0.18.1/asdf-v0.18.1-linux-amd64.tar.gz | tar xz -C /tmp && \
mkdir -p ~/.local/bin && \
mv /tmp/asdf ~/.local/bin/asdf
ENV PATH="/work/.local/bin:/work/.asdf/shims:${PATH}"
# Install Node.js and Python via asdf
RUN asdf plugin add nodejs && asdf install nodejs latest && asdf set --home nodejs latest
RUN asdf plugin add python && asdf install python 3.14.4 && asdf set --home python 3.14.4
# Python libraries available to scripts via asdf Python
RUN pip install --no-cache-dir curl_cffi && \
asdf reshim python && \
ln -sf "$(asdf which curl-cffi)" /work/.local/bin/curl-cffi
# Disable cache
#ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" /tmp/skipcache
# Setup zsh and plugins (antidote bootstraps itself on first shell init)
RUN git clone https://github.com/nemanjan00/zsh.git ~/.zsh
RUN echo "source ~/.zsh/index.zsh" > ~/.zshrc
RUN timeout 120 zsh -ic "TERM=xterm-256color exit"
# Download my dotfiles
USER $UID
RUN git clone https://github.com/nemanjan00/vim.git ~/.config/nvim
# Install plug manager for vim
RUN curl -fLo ~/.config/nvim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
# Install plugins inside of vim
RUN nvim +PlugInstall +qall
# Pre-bake treesitter parsers so fresh containers don't re-download them.
# Keep the list in sync with the install{} call in the vim repo's init.vim;
# parsers missing here still auto-install at runtime, just not pre-baked.
RUN nvim --headless "+lua require('nvim-treesitter').install({'bash','c','css','dockerfile','gitignore','go','html','javascript','json','kotlin','lua','markdown','markdown_inline','nginx','php','python','scss','sql','tsx','typescript','vim','vimdoc','vue','yaml'}):wait(600000); vim.cmd('qall!')"
# Install .tmux
RUN git clone https://github.com/gpakosz/.tmux.git ~/.tmux
RUN ln -s -f .tmux/.tmux.conf ~/.tmux.conf
RUN cp ~/.tmux/.tmux.conf.local ~/
# Append host-project-path display to the gpakosz status line. Sourced
# after the upstream defaults so the override wins. $HOST_PROJECT_DIR is
# set by bin/claude-docker; outside that wrapper the shell escape returns
# empty and the status line is unchanged.
COPY --chown=$UID:$GID templates/tmux.conf.local.append /tmp/tmux.append
RUN cat /tmp/tmux.append >> ~/.tmux.conf.local && rm /tmp/tmux.append
# Install Claude Code, opencode, Kimi Code CLI, Pi, muxmcp (stdio MCP
# multiplexer), and shell-session-mcp (PTY-backed interactive sessions;
# node-pty needs base-devel + python — both present)
RUN npm install -g @anthropic-ai/claude-code opencode-ai @moonshot-ai/kimi-code @earendil-works/pi-coding-agent muxmcp shell-session-mcp && \
ln -sf "$(asdf which node)" /work/.local/bin/node && \
ln -sf "$(asdf which claude)" /work/.local/bin/claude && \
ln -sf "$(asdf which opencode)" /work/.local/bin/opencode && \
ln -sf "$(asdf which kimi)" /work/.local/bin/kimi && \
ln -sf "$(asdf which pi)" /work/.local/bin/pi && \
ln -sf "$(asdf which muxmcp)" /work/.local/bin/muxmcp && \
ln -sf "$(asdf which shell-session-mcp)" /work/.local/bin/shell-session-mcp
# ~/.claude is bind-mounted from the host at runtime, so skills baked there
# would be shadowed. /work/skills is left untouched by that mount; the
# wrappers pass `--add-dir /work/skills`, and Claude Code auto-loads any
# skill under <added-dir>/.claude/skills/. Profiles drop skills here; the
# empty dir keeps --add-dir valid even for profiles that ship none.
# opencode dirs, ~/.kimi-code, and ~/.pi are pre-created (owned by uid 1000)
# so the wrappers can bind host config/state over them without docker creating
# root-owned mount points.
RUN mkdir -p ~/.claude ~/.config/claude/mcp.d ~/skills/.claude/skills \
~/.config/opencode ~/.local/share/opencode ~/.kimi-code ~/.pi
COPY --chown=$UID:$GID templates/AGENTS.md /work/AGENTS.md
COPY --chown=$UID:$GID templates/CLAUDE.md /work/CLAUDE.md
COPY --chown=$UID:$GID templates/mcp.d/ /work/.config/claude/mcp.d/
# Baked Ollama provider config for opencode, selected only when a wrapper is run
# with --ollama (via OPENCODE_CONFIG). Lives at /work, outside every mount, so a
# bind-mounted ~/.config/opencode never shadows it.
COPY --chown=$UID:$GID templates/opencode/ollama.json /work/opencode-ollama.json
# Prepare work area
USER $UID
WORKDIR /work
CMD ["tmux"]