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
8 changes: 5 additions & 3 deletions server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,11 @@ async def setup_status():
# Check for Claude CLI
claude_cli = shutil.which("claude") is not None

# Check for credentials file
credentials_path = Path.home() / ".claude" / ".credentials.json"
credentials = credentials_path.exists()
# Check for Claude CLI configuration directory
# Note: Claude CLI no longer stores credentials in ~/.claude/.credentials.json
# The existence of ~/.claude indicates the CLI has been configured
claude_dir = Path.home() / ".claude"
credentials = claude_dir.exists() and claude_dir.is_dir()
Comment on lines +123 to +127
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Claude CLI ~/.claude directory authentication configuration

💡 Result:

Summary — what ~/.claude is and how authentication/config works

  • ~/.claude contains Claude Code user settings (settings.json) and per-project .claude/settings*.json files. User settings apply globally; project settings live in .claude/ in the project. [1][2]

  • Authentication is normally done with an Anthropic API key via the ANTHROPIC_API_KEY environment variable. Claude Code also supports enterprise providers (Amazon Bedrock, Google Vertex) by setting provider-specific env vars and credentials (e.g., CLAUDE_CODE_USE_BEDROCK=1 + AWS creds). Some third‑party wrappers/proxies (e.g., ZenMux) use different env vars (ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN). [3][2][5]

Quick examples

  • Preferred: export ANTHROPIC_API_KEY="sk-..." (works systemwide / in your shell/profile). [3]
  • Alternative (user settings file): put an apiKey field in ~/.claude/settings.json (less recommended for secrets). [1][4]
  • For Bedrock/Vertex: set CLAUDE_CODE_USE_BEDROCK=1 and configure AWS credentials, or CLAUDE_CODE_USE_VERTEX=1 and configure GCP credentials. [3]

Notes / gotchas

  • Environment-variable auth is the standard / recommended method. Some third‑party tools or versions may change how ~/.claude/env or settings are read — check your Claude Code version/docs if env settings in settings.json appear ignored. [5][1]

Sources
[1] Claude Code settings (settings.json, user & project settings).
[2] Claude Code CLI reference (commands/flags & config file locations).
[3] Claude Code SDK — Authentication (ANTHROPIC_API_KEY, Bedrock/Vertex).
[4] Community config reference (example ~/.claude config).
[5] Third‑party integration note (env loading / ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN).


The directory-based authentication check is unreliable and should verify the ANTHROPIC_API_KEY environment variable instead.

Web search confirms that Claude CLI authentication is primarily handled via the ANTHROPIC_API_KEY environment variable (or provider-specific env vars for enterprise), not by checking directory existence. While ~/.claude stores settings.json, the directory's presence does not reliably indicate authentication is configured—a user could have the directory without credentials set, or vice versa.

Instead, the check should verify the relevant environment variable(s):

  • For standard auth: check if ANTHROPIC_API_KEY is set
  • For enterprise: check CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or similar provider flags alongside their respective credentials
🤖 Prompt for AI Agents
In @server/main.py around lines 123 - 127, The current directory-based check
using claude_dir and credentials is unreliable; change the logic so credentials
is true when the appropriate environment variables are present instead of
checking ~/.claude: check ANTHROPIC_API_KEY for standard auth and also consider
provider flags like CLAUDE_CODE_USE_BEDROCK or CLAUDE_CODE_USE_VERTEX combined
with their respective provider credential env vars; update the assignment of the
credentials variable (replace the claude_dir.exists() check) to test os.environ
for these keys so authentication detection is based on environment variables
rather than directory presence.


# Check for Node.js and npm
node = shutil.which("node") is not None
Expand Down
62 changes: 62 additions & 0 deletions server/services/process_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,47 @@
r'aws[_-]?secret[=:][^\s]+',
]

# Patterns that indicate Claude CLI authentication errors
AUTH_ERROR_PATTERNS = [
r"not\s+logged\s+in",
r"not\s+authenticated",
r"authentication\s+(failed|required|error)",
r"login\s+required",
r"please\s+(run\s+)?['\"]?claude\s+login",
r"unauthorized",
r"invalid\s+(token|credential|api.?key)",
r"expired\s+(token|session|credential)",
r"could\s+not\s+authenticate",
r"sign\s+in\s+(to|required)",
]
Comment on lines +39 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Claude CLI authentication error messages and login requirements

💡 Result:

Summary — common Claude (Claude Code / claude CLI) authentication errors and what they mean

  • "Missing API key · Run /login" — CLI/IDE couldn’t find a stored session or ANTHROPIC_API_KEY; run the interactive /login inside claude or set ANTHROPIC_API_KEY in your environment (export ANTHROPIC_API_KEY=sk-...). [1][2]

  • "Invalid API key · Please run /login" — the provided API key is rejected or not accepted by the current auth flow (CLI sometimes expects a session from /login rather than a raw API key); re-check key value, ensure it’s an Anthropic API key, or run /login to authenticate. [2][3]

  • Errors about Cloudflare/verification or web-based auth failures — when the browser-based auth flow is blocked (e.g., Cloudflare checks or SSO/browser restrictions) the CLI cannot complete interactive /login; resolve by allowing the browser flow or using an API key if the CLI build supports it. (Users have reported this preventing login.) [3]

  • "Please run setup-token" (or instructions referencing setup-token) — some CLI workflows expect a subscription/session-based token; use claude setup-token if your account/subscription supports it, or use ANTHROPIC_API_KEY for direct API access if the CLI version supports it. [1][2]

Quick checklist to fix auth problems

  • If automation/non-interactive: export ANTHROPIC_API_KEY with a valid Anthropic API key and retry. [1]
  • If interactive CLI/IDE: run /login from within claude to open the browser auth flow and complete sign-in. [1][3]
  • If browser flow is blocked: allow the browser auth, disable blocking proxies/Cloudflare checks, or use an API-key-compatible CLI version. [3]
  • If errors persist: check ~/.config/claude/config.json for conflicting config and update CLI to the latest release. [1][3]

Sources

  1. Anthropic Claude Code quickstart / CLI auth docs. [Anthropic documentation]. [1]
  2. Claude CLI docs / integration notes (environment variable ANTHROPIC_API_KEY; setup-token mention). [2]
  3. GitHub issue reports describing "Missing API key · Run /login" and Cloudflare/browser auth failures. [3]

Would you like exact command examples for exporting keys, running /login, or checking your config file?


🏁 Script executed:

# Find the is_auth_error function and understand pattern usage
rg -A 10 "is_auth_error|AUTH_ERROR_PATTERNS" server/services/process_manager.py

Repository: leonvanzyl/autocoder

Length of output: 1944


🏁 Script executed:

# Search for any test files that might show expected error messages
fd -e test.py -e spec.py -o -e "*test*.py" | xargs rg -l "AUTH_ERROR|auth.*error" 2>/dev/null | head -5

Repository: leonvanzyl/autocoder

Length of output: 241


🏁 Script executed:

# Look for any claude CLI or authentication-related documentation in the repo
fd -type f \( -name "*.md" -o -name "*.txt" -o -name "*.rst" \) | xargs rg -l "claude.*login|authentication.*error|not.*logged" 2>/dev/null | head -10

Repository: leonvanzyl/autocoder

Length of output: 361


Add missing patterns for API key and setup-token errors.

The patterns cover most common authentication errors well, but the web documentation reveals gaps:

  1. "Missing API key · Run /login" — The current invalid\s+(token|credential|api.?key) pattern covers "Invalid API key" but misses the "missing" variant. Add pattern: r"missing\s+(api.?key|session)"

  2. "Please run setup-token" — Not covered by any current pattern. Add pattern: r"setup.?token"

These additions would ensure more comprehensive coverage of Claude CLI authentication error messages as documented.

🤖 Prompt for AI Agents
In @server/services/process_manager.py around lines 39 - 51, Update the
AUTH_ERROR_PATTERNS list to include two additional regexes to catch missing API
key/session and setup-token messages: add a pattern matching "missing api key"
or "missing session" (e.g., r"missing\s+(api.?key|session)") and a pattern
matching setup-token mentions (e.g., r"setup.?token"); ensure these new patterns
are appended to the AUTH_ERROR_PATTERNS array so functions that scan error
strings (using AUTH_ERROR_PATTERNS) will detect "Missing API key · Run /login"
and "Please run setup-token" cases.



def is_auth_error(text: str) -> bool:
"""Check if text contains Claude CLI authentication error messages."""
if not text:
return False
text_lower = text.lower()
for pattern in AUTH_ERROR_PATTERNS:
if re.search(pattern, text_lower):
return True
return False


AUTH_ERROR_HELP = """
================================================================================
AUTHENTICATION ERROR DETECTED
================================================================================

Claude CLI requires authentication to work.

To fix this, run:
claude login

This will open a browser window to sign in.
After logging in, try starting the agent again.
================================================================================
"""


def sanitize_output(line: str) -> str:
"""Remove sensitive information from output lines."""
Expand Down Expand Up @@ -185,6 +226,9 @@ async def _stream_output(self) -> None:
if not self.process or not self.process.stdout:
return

auth_error_detected = False
output_buffer = [] # Buffer recent lines for auth error detection

try:
loop = asyncio.get_running_loop()
while True:
Expand All @@ -198,6 +242,18 @@ async def _stream_output(self) -> None:
decoded = line.decode("utf-8", errors="replace").rstrip()
sanitized = sanitize_output(decoded)

# Buffer recent output for auth error detection
output_buffer.append(decoded)
if len(output_buffer) > 20:
output_buffer.pop(0)

# Check for auth errors
if not auth_error_detected and is_auth_error(decoded):
auth_error_detected = True
# Broadcast auth error help message
for help_line in AUTH_ERROR_HELP.strip().split('\n'):
await self._broadcast_output(help_line)

await self._broadcast_output(sanitized)

except asyncio.CancelledError:
Expand All @@ -209,6 +265,12 @@ async def _stream_output(self) -> None:
if self.process and self.process.poll() is not None:
exit_code = self.process.returncode
if exit_code != 0 and self.status == "running":
# Check buffered output for auth errors if we haven't detected one yet
if not auth_error_detected:
combined_output = '\n'.join(output_buffer)
if is_auth_error(combined_output):
for help_line in AUTH_ERROR_HELP.strip().split('\n'):
await self._broadcast_output(help_line)
self.status = "crashed"
elif self.status == "running":
self.status = "stopped"
Expand Down
94 changes: 90 additions & 4 deletions start.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""

import os
import re
import subprocess
import sys
from pathlib import Path
Expand All @@ -24,6 +25,53 @@
register_project,
)

# Patterns that indicate Claude CLI authentication errors
AUTH_ERROR_PATTERNS = [
r"not\s+logged\s+in",
r"not\s+authenticated",
r"authentication\s+(failed|required|error)",
r"login\s+required",
r"please\s+(run\s+)?['\"]?claude\s+login",
r"unauthorized",
r"invalid\s+(token|credential|api.?key)",
r"expired\s+(token|session|credential)",
r"could\s+not\s+authenticate",
r"sign\s+in\s+(to|required)",
]


def is_auth_error(output: str) -> bool:
"""
Check if output contains Claude CLI authentication error messages.

Args:
output: Combined stdout/stderr from subprocess

Returns:
True if authentication error detected, False otherwise
"""
if not output:
return False

output_lower = output.lower()
for pattern in AUTH_ERROR_PATTERNS:
if re.search(pattern, output_lower):
return True
return False


def print_auth_error_help() -> None:
"""Print helpful message when authentication error is detected."""
print("\n" + "=" * 50)
print(" Authentication Error Detected")
print("=" * 50)
print("\nClaude CLI requires authentication to work.")
print("\nTo fix this, run:")
print(" claude login")
print("\nThis will open a browser window to sign in.")
print("After logging in, try running this command again.")
print("=" * 50 + "\n")


def check_spec_exists(project_dir: Path) -> bool:
"""
Expand Down Expand Up @@ -203,6 +251,7 @@ def run_spec_creation(project_dir: Path) -> bool:
Run Claude Code with /create-spec command to create project specification.

The project path is passed as an argument so create-spec knows where to write files.
Captures stderr to detect authentication errors and provide helpful guidance.
"""
print("\n" + "=" * 50)
print(" Project Specification Setup")
Expand All @@ -217,12 +266,25 @@ def run_spec_creation(project_dir: Path) -> bool:
try:
# Launch Claude Code with /create-spec command
# Project path included in command string so it populates $ARGUMENTS
subprocess.run(
# Capture stderr to detect auth errors while letting stdout flow to terminal
result = subprocess.run(
["claude", f"/create-spec {project_dir}"],
check=False, # Don't raise on non-zero exit
cwd=str(Path(__file__).parent) # Run from project root
cwd=str(Path(__file__).parent), # Run from project root
stderr=subprocess.PIPE,
text=True
)

# Check for authentication errors in stderr
stderr_output = result.stderr or ""
if result.returncode != 0 and is_auth_error(stderr_output):
print_auth_error_help()
return False

# If there was stderr output but not an auth error, show it
if stderr_output.strip() and result.returncode != 0:
print(f"\nClaude CLI error: {stderr_output.strip()}")

# Check if spec was created in project prompts directory
if check_spec_exists(project_dir):
print("\n" + "-" * 50)
Expand All @@ -232,6 +294,9 @@ def run_spec_creation(project_dir: Path) -> bool:
print("\n" + "-" * 50)
print("Spec creation incomplete.")
print(f"Please ensure app_spec.txt exists in: {get_project_prompts_dir(project_dir)}")
# If failed with non-zero exit and no spec, might be auth issue
if result.returncode != 0:
print("\nIf you're having authentication issues, try running: claude login")
return False

except FileNotFoundError:
Expand Down Expand Up @@ -348,6 +413,8 @@ def create_new_project_flow() -> tuple[str, Path] | None:
def run_agent(project_name: str, project_dir: Path) -> None:
"""Run the autonomous agent with the given project.

Captures stderr to detect authentication errors and provide helpful guidance.

Args:
project_name: Name of the project
project_dir: Absolute path to the project directory
Expand All @@ -367,9 +434,28 @@ def run_agent(project_name: str, project_dir: Path) -> None:
# Build the command - pass absolute path
cmd = [sys.executable, "autonomous_agent_demo.py", "--project-dir", str(project_dir.resolve())]

# Run the agent
# Run the agent with stderr capture to detect auth errors
# stdout goes directly to terminal for real-time output
try:
subprocess.run(cmd, check=False)
result = subprocess.run(
cmd,
check=False,
stderr=subprocess.PIPE,
text=True
)

# Check for authentication errors
stderr_output = result.stderr or ""
if result.returncode != 0:
if is_auth_error(stderr_output):
print_auth_error_help()
elif stderr_output.strip():
# Show any other errors
print(f"\nAgent error:\n{stderr_output.strip()}")
# Still hint about auth if exit was unexpected
if "error" in stderr_output.lower() or "exception" in stderr_output.lower():
print("\nIf this is an authentication issue, try running: claude login")

except KeyboardInterrupt:
print("\n\nAgent interrupted. Run again to resume.")

Expand Down
40 changes: 9 additions & 31 deletions start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,40 +20,18 @@ fi

echo "[OK] Claude CLI found"

# Check if user has credentials
CLAUDE_CREDS="$HOME/.claude/.credentials.json"
if [ -f "$CLAUDE_CREDS" ]; then
echo "[OK] Claude credentials found"
# Note: Claude CLI no longer stores credentials in ~/.claude/.credentials.json
# We can't reliably check auth status without making an API call, so we just
# verify the CLI is installed and remind the user to login if needed
if [ -d "$HOME/.claude" ]; then
echo "[OK] Claude CLI directory found"
echo " (If you're not logged in, run: claude login)"
Comment on lines +23 to +28
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Implementation doesn't match PR description: missing CLI operational check.

The PR description explicitly states the fix should use:

if claude --version &> /dev/null && [ -d "$HOME/.claude" ]; then

However, line 26 only checks for the directory:

if [ -d "$HOME/.claude" ]; then

The claude --version check is essential because it verifies the CLI is operational, not just that a config directory exists. Without it, the script could proceed with a broken or misconfigured CLI installation.

🔧 Add the operational check as described in the PR
-if [ -d "$HOME/.claude" ]; then
+if claude --version &> /dev/null && [ -d "$HOME/.claude" ]; then
     echo "[OK] Claude CLI directory found"
     echo "     (If you're not logged in, run: claude login)"
🤖 Prompt for AI Agents
In @start.sh around lines 23 - 28, Replace the existing directory-only check in
the if statement that currently reads like the block checking "$HOME/.claude"
with a combined operational check that runs the claude CLI version command and
verifies the directory exists (i.e., use a command-execution check for "claude
--version" and the directory test together). Update the if condition used around
the echo lines so it only enters the block when the CLI executable returns
success and the config directory exists, and keep the existing echo messages
unchanged.

else
echo "[!] Not authenticated with Claude"
echo "[!] Claude CLI not configured"
echo ""
echo "You need to run 'claude login' to authenticate."
echo "This will open a browser window to sign in."
echo "Please run 'claude login' to authenticate before continuing."
echo ""
read -p "Would you like to run 'claude login' now? (y/n): " LOGIN_CHOICE

if [[ "$LOGIN_CHOICE" =~ ^[Yy]$ ]]; then
echo ""
echo "Running 'claude login'..."
echo "Complete the login in your browser, then return here."
echo ""
claude login

# Check if login succeeded
if [ -f "$CLAUDE_CREDS" ]; then
echo ""
echo "[OK] Login successful!"
else
echo ""
echo "[ERROR] Login failed or was cancelled."
echo "Please try again."
exit 1
fi
else
echo ""
echo "Please run 'claude login' manually, then try again."
exit 1
fi
read -p "Press Enter to continue anyway, or Ctrl+C to exit..."
fi
Comment on lines 29 to 35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Script still blocks execution, contradicting PR's non-blocking claim.

The PR description states the change "emits a non-blocking warning with instructions when not configured (instead of forcing login)" and "removes the forced interactive flow." However, line 34 still blocks execution:

read -p "Press Enter to continue anyway, or Ctrl+C to exit..."

This forces user interaction before the script can proceed, which contradicts the non-blocking design goal.

♻️ Make the warning truly non-blocking
 else
     echo "[!] Claude CLI not configured"
     echo ""
     echo "Please run 'claude login' to authenticate before continuing."
-    echo ""
-    read -p "Press Enter to continue anyway, or Ctrl+C to exit..."
 fi
🤖 Prompt for AI Agents
In @start.sh around lines 29 - 35, The else branch currently still blocks with a
read prompt; remove the blocking read or guard it so the script truly emits a
non-blocking warning. Specifically, in the else block that prints "[!] Claude
CLI not configured" (the section containing the read -p "Press Enter to continue
anyway, or Ctrl+C to exit..."), either delete that read line entirely to make
the warning non-blocking, or wrap it in an interactive-only guard (e.g., if [ -t
0 ]; then read -p ...; fi) so automated/non-interactive runs are not blocked.


echo ""
Expand Down
21 changes: 21 additions & 0 deletions start_ui.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ echo " AutoCoder UI"
echo "===================================="
echo ""

# Check if Claude CLI is installed
if ! command -v claude &> /dev/null; then
echo "[!] Claude CLI not found"
echo ""
echo " The agent requires Claude CLI to work."
echo " Install it from: https://claude.ai/download"
echo ""
echo " After installing, run: claude login"
echo ""
else
echo "[OK] Claude CLI found"
# Note: Claude CLI no longer stores credentials in ~/.claude/.credentials.json
# We can't reliably check auth status without making an API call
if [ -d "$HOME/.claude" ]; then
echo " (If you're not logged in, run: claude login)"
else
echo "[!] Claude CLI not configured - run 'claude login' first"
fi
fi
echo ""

# Check if Python is available
if ! command -v python3 &> /dev/null; then
if ! command -v python &> /dev/null; then
Expand Down