Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@

.bin/
out.html
captain
specs/
.claude/
82 changes: 82 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
FROM flanksource/base-image:latest

ARG TZ
ENV TZ="$TZ"

ARG CLAUDE_CODE_VERSION=latest
ARG USERNAME=claude
ARG USER_UID=501
ARG USER_GID=20

# Create user/group matching host (default: moshe:501:20)
RUN if ! getent group ${USER_GID} > /dev/null 2>&1; then groupadd -g ${USER_GID} ${USERNAME}; fi && \
useradd -u ${USER_UID} -g ${USER_GID} -m -s /bin/zsh ${USERNAME} && \
mkdir -p /home/${USERNAME}/.claude && \
chown -R ${USERNAME}:${USER_GID} /home/${USERNAME}

# Install Node.js and basic development tools
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get update && apt-get install -y --no-install-recommends \
nodejs \
less \
git \
procps \
sudo \
fzf \
zsh \
man-db \
unzip \
gnupg2 \
gh \
iptables \
ipset \
iproute2 \
dnsutils \
aggregate \
jq \
nano \
vim \
build-essential \
gosu \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

# Ensure user has access to /usr/local/share
RUN mkdir -p /usr/local/share/npm-global && \
chown -R ${USERNAME} /usr/local/share


# Set `DEVCONTAINER` environment variable to help with orientation
ENV DEVCONTAINER=true

# Create workspace directory
RUN mkdir -p /workspace && chown ${USERNAME}:${USER_GID} /workspace

WORKDIR /workspace

ARG GIT_DELTA_VERSION=0.18.2
RUN ARCH=$(dpkg --print-architecture) && \
wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"

COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

USER ${USERNAME}

# Install global packages
ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
ENV PATH=$PATH:/usr/local/share/npm-global/bin

# Set the default shell to zsh rather than sh
ENV SHELL=/bin/zsh

# Set the default editor and visual
ENV EDITOR=nano
ENV VISUAL=nano

# Install Claude
RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}

USER root
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
58 changes: 53 additions & 5 deletions cmd/captain/main.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package main

import (
"fmt"
"os"

"github.com/flanksource/captain/pkg/cli"
"github.com/flanksource/clicky"
"github.com/spf13/cobra"
)

var (
version = "dev"
commit = "none"
date = "unknown"
)
var version = "dev"

func main() {
rootCmd := &cobra.Command{
Expand All @@ -28,13 +25,64 @@ func main() {
clicky.AddNamedCommand("history", rootCmd, cli.HistoryOptions{}, cli.RunHistory)
clicky.AddNamedCommand("info", rootCmd, cli.InfoOptions{}, cli.RunInfo)
clicky.AddNamedCommand("cost", rootCmd, cli.CostOptions{}, cli.RunCost)
sandboxCmd := &cobra.Command{
Use: "sandbox",
Short: "Sandbox configuration tools",
}
sandboxCmd.SetHelpFunc(func(c *cobra.Command, args []string) {
fmt.Fprint(os.Stderr, cli.SandboxHelp().ANSI())
})
rootCmd.AddCommand(sandboxCmd)
clicky.AddNamedCommand("generate", sandboxCmd, cli.SRTGenerateOptions{}, cli.RunSRTGenerate).Short = "Generate sandbox-runtime config"
clicky.AddNamedCommand("presets", sandboxCmd, cli.SandboxPresetsOptions{}, cli.RunSandboxPresets).Short = "List available sandbox-runtime presets"

aiCmd := &cobra.Command{Use: "ai", Short: "AI provider commands"}
rootCmd.AddCommand(aiCmd)
clicky.AddNamedCommand("prompt", aiCmd, cli.AIPromptOptions{}, cli.RunAIPrompt)
clicky.AddNamedCommand("models", aiCmd, cli.AIModelsOptions{}, cli.RunAIModels)
clicky.AddNamedCommand("test", aiCmd, cli.AITestOptions{}, cli.RunAITest)

dodCmd := &cobra.Command{Use: "dod", Short: "Definition of Done — gate Claude's stop on passing commands"}
rootCmd.AddCommand(dodCmd)
clicky.AddNamedCommand("set", dodCmd, cli.DodSetOptions{}, cli.RunDodSet)
clicky.AddNamedCommand("check", dodCmd, cli.DodCheckOptions{}, cli.RunDodCheck)
clicky.AddNamedCommand("clear", dodCmd, cli.DodClearOptions{}, cli.RunDodClear)
clicky.AddNamedCommand("status", dodCmd, cli.DodStatusOptions{}, cli.RunDodStatus)
clicky.AddNamedCommand("run", dodCmd, cli.DodRunOptions{}, cli.RunDodRun)

hookCmd := &cobra.Command{Use: "hook", Short: "Claude Code hook commands"}
rootCmd.AddCommand(hookCmd)
bashCheckCmd := &cobra.Command{Use: "bash-check", Short: "Scan bash command for violations (PreToolUse hook)", RunE: func(cmd *cobra.Command, args []string) error {
_, err := cli.RunBashCheck(cli.BashCheckOptions{})
return err
}}
hookCmd.AddCommand(bashCheckCmd)
clicky.AddNamedCommand("install", bashCheckCmd, cli.HookInstallOptions{}, cli.RunBashCheckInstall)
dodHookCmd := &cobra.Command{Use: "dod", Short: "Definition of Done hook"}
hookCmd.AddCommand(dodHookCmd)
clicky.AddNamedCommand("install", dodHookCmd, cli.HookInstallOptions{}, cli.RunDodInstall)

projectsCmd := &cobra.Command{Use: "projects", Short: "Manage Claude Code project sessions"}
rootCmd.AddCommand(projectsCmd)
clicky.AddNamedCommand("list", projectsCmd, cli.ProjectsListOptions{}, cli.RunProjectsList)
clicky.AddNamedCommand("clean", projectsCmd, cli.ProjectsCleanOptions{}, cli.RunProjectsClean)

containerCmd := &cobra.Command{
Use: "container",
Short: "Container sandbox builder for Claude Code",
RunE: func(cmd *cobra.Command, args []string) error {
return cli.RunContainerTUI()
},
}
containerCmd.SetHelpFunc(func(c *cobra.Command, args []string) {
fmt.Fprint(os.Stderr, cli.ContainerHelp().ANSI())
})
rootCmd.AddCommand(containerCmd)
clicky.AddNamedCommand("list", containerCmd, cli.ContainerListOptions{}, cli.RunContainerList).Short = "List discovered components"
clicky.AddNamedCommand("generate", containerCmd, cli.ContainerGenerateOptions{}, cli.RunContainerGenerate).Short = "Generate Dockerfile and sandbox config"
clicky.AddNamedCommand("build", containerCmd, cli.ContainerBuildOptions{}, cli.RunContainerBuild).Short = "Build container image"
clicky.AddNamedCommand("run", containerCmd, cli.ContainerRunOptions{}, cli.RunContainerRun).Short = "Run container sandbox"

if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
Expand Down
5 changes: 5 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh
set -e
EXEC_UID="${USER_UID:-501}"
EXEC_GID="${USER_GID:-20}"
exec gosu "${EXEC_UID}:${EXEC_GID}" "$@"
Loading
Loading