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
40 changes: 39 additions & 1 deletion container-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,49 @@ if [ ! -f "$PROMPT_FILE" ]; then
fi
PROMPT=$(cat "$PROMPT_FILE")

# ── Step 3b: Pre-clone repo from reference if available ──────────
REPO_NAME="${CLAUDEBOX_REPO_NAME:-}"
BASE_BRANCH="${CLAUDEBOX_BASE_BRANCH:-next}"
REPO_DIR="/workspace/${REPO_NAME}"

if [ -n "$REPO_NAME" ] && [ -d "/reference-repo/.git" ] && [ ! -d "${REPO_DIR}/.git" ]; then
echo ""
echo "━━━ Pre-cloning $REPO_NAME (sparse) from reference ━━━"
git config --global --add safe.directory /reference-repo/.git
git config --global --add safe.directory "$REPO_DIR"
# Sparse clone: only checkout .claude/ dirs (skills, settings, CLAUDE.md)
# Claude will populate the full tree via clone_repo when it needs to work
git clone --shared --no-checkout /reference-repo/.git "$REPO_DIR" 2>&1 || true
if [ -d "${REPO_DIR}/.git" ]; then
cd "$REPO_DIR"
# Autodetect .claude-related paths from the reference repo
CLAUDE_PATHS=$(git ls-tree -r -d --name-only HEAD 2>/dev/null | grep -E '(^\.claude$|/\.claude$)' || true)
if [ -n "$CLAUDE_PATHS" ]; then
git sparse-checkout set --cone $CLAUDE_PATHS 2>/dev/null || true
else
git sparse-checkout set --cone .claude 2>/dev/null || true
fi
# Try to checkout the base branch
git checkout --detach "origin/${BASE_BRANCH}" 2>/dev/null \
|| git checkout --detach origin/main 2>/dev/null \
|| git checkout --detach HEAD 2>/dev/null \
|| true
echo "Pre-cloned (sparse) at $(git rev-parse --short HEAD 2>/dev/null || echo '???')"
fi
elif [ -n "$REPO_NAME" ] && [ -d "${REPO_DIR}/.git" ]; then
echo "Repo already exists at $REPO_DIR"
cd "$REPO_DIR"
fi

# Set working directory: prefer repo dir if it exists, else /workspace
WORK_DIR="$REPO_DIR"
[ ! -d "$WORK_DIR" ] && WORK_DIR="/workspace"

echo ""
echo "━━━ Launching Claude ━━━"
echo ""

cd /workspace
cd "$WORK_DIR"

# ── Step 4: Run Claude (or CLAUDE_BINARY override) ────────────────
CLAUDE_BIN="${CLAUDE_BINARY:-claude}"
Expand Down
9 changes: 6 additions & 3 deletions packages/libclaudebox/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ export const SESSION_PAGE_USER = process.env.CLAUDEBOX_SESSION_USER || "admin";
export const SESSION_PAGE_PASS = process.env.CLAUDEBOX_SESSION_PASS || "";

// ── Log URL builder ─────────────────────────────────────────────
// Override with CLAUDEBOX_LOG_BASE_URL to change from default.
export const LOG_BASE_URL = process.env.CLAUDEBOX_LOG_BASE_URL || `http://${CLAUDEBOX_HOST}`;
// Points to the session page on CLAUDEBOX_HOST (e.g. claudebox.work/s/<worktreeId>).
// The logId format is <worktreeId>-<seq>, so we extract the worktreeId prefix.
export const LOG_BASE_URL = `https://${CLAUDEBOX_HOST}`;
export function buildLogUrl(logId: string): string {
return `${LOG_BASE_URL}/${logId}`;
// Extract worktreeId from logId (e.g. "d9441073aae158ae-3" → "d9441073aae158ae")
const worktreeId = logId.replace(/-\d+$/, "");
return `${LOG_BASE_URL}/s/${worktreeId}`;
}

export const DEFAULT_BASE_BRANCH = process.env.CLAUDEBOX_DEFAULT_BRANCH || "main";
1 change: 1 addition & 0 deletions packages/libclaudebox/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ export class DockerService {
`CLAUDEBOX_LINK=${opts.link || ""}`,
`CLAUDEBOX_HOST=${CLAUDEBOX_HOST}`,
`CLAUDEBOX_BASE_BRANCH=${baseBranch}`,
`CLAUDEBOX_REPO_NAME=${basename(REPO_DIR)}`,
`CLAUDEBOX_QUIET=${opts.quiet ? "1" : "0"}`,
`CLAUDEBOX_CI_ALLOW=${opts.ciAllow ? "1" : "0"}`,
`CLAUDEBOX_PROFILE=${profileDir}`,
Expand Down
12 changes: 8 additions & 4 deletions packages/libclaudebox/html/shared.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type { RunMeta } from "../types.ts";
export type { SessionMeta } from "../types.ts";

export function esc(s: string): string {
return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
Expand Down Expand Up @@ -65,8 +65,8 @@ export interface ActivityEntry {

export interface WorkspacePageData {
hash: string; // current session log_id
session: RunMeta; // current session
sessions: RunMeta[]; // all sessions for this worktree (newest first)
session: SessionMeta; // current session
sessions: SessionMeta[]; // all sessions for this worktree (newest first)
worktreeAlive: boolean;
activity: ActivityEntry[]; // newest first
/** Last reply text per session log_id (for collapsed run card summaries) */
Expand Down Expand Up @@ -186,7 +186,11 @@ export function renderActivityEntry(a: ActivityEntry, agentLogUrl?: string): str
const toolArgs = spIdx > 0 ? linkify(raw.slice(spIdx)) : "";
return `<div class="chat-status" data-msg="${msgHash}"><span class="tool-icon">\u25B8</span><code><span class="tool-name">${toolName}</span><span class="tool-args">${toolArgs}</span></code><span class="ts">${timeStr}</span></div>`;
} else if (a.type === "tool_result") {
return `<div class="chat-status" data-msg="${msgHash}"><span class="tool-icon">\u25C2</span><code><span class="tool-args">${linked}</span></code><span class="ts">${timeStr}</span></div>`;
const raw = a.text || "";
const isErr = raw.includes("<tool_use_error>") || raw.startsWith("Error:") || raw.startsWith("ERROR:");
const cleaned = linkify(raw.replace(/<\/?tool_use_error>/g, "").trim());
const errStyle = isErr ? ";color:#E94560;border-left-color:#E94560" : "";
return `<div class="chat-result" data-msg="${msgHash}" style="border-left:2px solid #222;margin-left:18px;padding:4px 10px;font-size:12px;color:#666;white-space:pre-wrap;word-break:break-all;max-height:80px;overflow:hidden;font-family:'SF Mono',monospace${errStyle}"><code>${cleaned}</code><span class="ts">${timeStr}</span></div>`;
} else if (a.type === "status") {
return `<div class="chat-status" data-msg="${msgHash}"><span class="tool-icon">\u25CB</span><span>${linked}</span><span class="ts">${timeStr}</span></div>`;
}
Expand Down
Loading