(no commit message)

This commit is contained in:
2026-01-21 16:50:23 -08:00
parent 519fb491d4
commit 24f352e8c5
4 changed files with 47 additions and 22 deletions

View File

@@ -4,7 +4,7 @@ Minimal Claude Code alternative. Single Python file, zero dependencies, ~250 lin
Built using Claude Code, then used to build itself. Built using Claude Code, then used to build itself.
![screenshot](https://d1pz4mbco29rws.cloudfront.net/public/nanocode.png) ![screenshot](screenshot.png)
## Features ## Features

View File

@@ -3,5 +3,5 @@
"max_iters": 15, "max_iters": 15,
"lm": "openai/gpt-5.2-codex", "lm": "openai/gpt-5.2-codex",
"api_base": "https://openrouter.ai/api/v1", "api_base": "https://openrouter.ai/api/v1",
"max_tokens": 8192 "max_tokens": 16000
} }

View File

@@ -25,6 +25,7 @@ MAGENTA = "\033[35m"
# --- Display utilities --- # --- Display utilities ---
def separator(): def separator():
"""Return a horizontal separator line that fits the terminal width.""" """Return a horizontal separator line that fits the terminal width."""
return f"{DIM}{'' * min(os.get_terminal_size().columns, 80)}{RESET}" return f"{DIM}{'' * min(os.get_terminal_size().columns, 80)}{RESET}"
@@ -37,6 +38,7 @@ def render_markdown(text):
# --- File operations --- # --- File operations ---
def read_file(path: str, offset: int = 0, limit: int = None) -> str: def read_file(path: str, offset: int = 0, limit: int = None) -> str:
"""Read file contents with line numbers. """Read file contents with line numbers.
@@ -138,6 +140,7 @@ def grep_files(pattern: str, path: str = ".") -> str:
# --- Shell operations --- # --- Shell operations ---
def run_bash(cmd: str) -> str: def run_bash(cmd: str) -> str:
"""Run a shell command and return output. """Run a shell command and return output.
@@ -148,9 +151,7 @@ def run_bash(cmd: str) -> str:
Command output (stdout and stderr combined) Command output (stdout and stderr combined)
""" """
proc = subprocess.Popen( proc = subprocess.Popen(
cmd, shell=True, cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True
) )
output_lines = [] output_lines = []
try: try:
@@ -196,14 +197,20 @@ def select_model():
while True: while True:
try: try:
choice = input(f"\n{BOLD}{BLUE}{RESET} Enter choice (1-8 or c): ").strip().lower() choice = (
input(f"\n{BOLD}{BLUE}{RESET} Enter choice (1-8 or c): ")
.strip()
.lower()
)
if choice in AVAILABLE_MODELS: if choice in AVAILABLE_MODELS:
name, model_id = AVAILABLE_MODELS[choice] name, model_id = AVAILABLE_MODELS[choice]
print(f"{GREEN}⏺ Selected: {name}{RESET}") print(f"{GREEN}⏺ Selected: {name}{RESET}")
return model_id return model_id
elif choice == "c": elif choice == "c":
custom_model = input(f"{BOLD}{BLUE}{RESET} Enter model ID (e.g., openai/gpt-4): ").strip() custom_model = input(
f"{BOLD}{BLUE}{RESET} Enter model ID (e.g., openai/gpt-4): "
).strip()
if custom_model: if custom_model:
print(f"{GREEN}⏺ Selected custom model: {custom_model}{RESET}") print(f"{GREEN}⏺ Selected custom model: {custom_model}{RESET}")
return custom_model return custom_model
@@ -218,12 +225,18 @@ def select_model():
# --- DSPy Signature --- # --- DSPy Signature ---
class CodingAssistant(dspy.Signature): class CodingAssistant(dspy.Signature):
"""You are a concise coding assistant. Help the user with their coding task by using the available tools to read, write, edit files, search the codebase, and run commands.""" """You are a concise coding assistant. Help the user with their coding task by using the available tools to read, write, edit files, search the codebase, and run commands."""
task: str = dspy.InputField(desc="The user's coding task or question") task: str = dspy.InputField(desc="The user's coding task or question")
answer: str = dspy.OutputField(desc="Your response to the user after completing the task") answer: str = dspy.OutputField(
affected_files: list[str] = dspy.OutputField(desc="List of files that were written or modified during the task") desc="Your response to the user after completing the task"
)
affected_files: list[str] = dspy.OutputField(
desc="List of files that were written or modified during the task"
)
# ReAct agent with tools # ReAct agent with tools
@@ -235,7 +248,7 @@ class ToolLoggingCallback(BaseCallback):
def on_tool_start(self, call_id, instance, inputs): def on_tool_start(self, call_id, instance, inputs):
"""Log when a tool starts executing.""" """Log when a tool starts executing."""
tool_name = instance.name if hasattr(instance, 'name') else str(instance) tool_name = instance.name if hasattr(instance, "name") else str(instance)
# Format args nicely # Format args nicely
args_str = ", ".join(f"{k}={repr(v)[:50]}" for k, v in inputs.items()) args_str = ", ".join(f"{k}={repr(v)[:50]}" for k, v in inputs.items())
print(f" {MAGENTA}{tool_name}({args_str}){RESET}", flush=True) print(f" {MAGENTA}{tool_name}({args_str}){RESET}", flush=True)
@@ -248,10 +261,12 @@ class ToolLoggingCallback(BaseCallback):
def on_module_end(self, call_id, outputs, exception): def on_module_end(self, call_id, outputs, exception):
"""Log when the finish tool is called (ReAct completion).""" """Log when the finish tool is called (ReAct completion)."""
# Check if this is a ReAct prediction with tool_calls # Check if this is a ReAct prediction with tool_calls
if outputs and 'tool_calls' in outputs: if outputs and "tool_calls" in outputs:
for call in outputs['tool_calls']: for call in outputs["tool_calls"]:
args_str = ", ".join(f"{k}={repr(v)[:50]}" for k, v in call.args.items()) args_str = ", ".join(
if call.name == 'finish': f"{k}={repr(v)[:50]}" for k, v in call.args.items()
)
if call.name == "finish":
print(f" {GREEN}⏺ finish{RESET}", flush=True) print(f" {GREEN}⏺ finish{RESET}", flush=True)
else: else:
print(f" {MAGENTA}{call.name}({args_str}){RESET}", flush=True) print(f" {MAGENTA}{call.name}({args_str}){RESET}", flush=True)
@@ -261,7 +276,8 @@ class AgentConfig(PrecompiledConfig):
max_iters: int = 15 max_iters: int = 15
lm: str = "openrouter/anthropic/claude-3.5-sonnet" # Default fallback lm: str = "openrouter/anthropic/claude-3.5-sonnet" # Default fallback
api_base: str = "https://openrouter.ai/api/v1" api_base: str = "https://openrouter.ai/api/v1"
max_tokens: int = 8192 max_tokens: int = 16000
class AgentProgram(PrecompiledProgram): class AgentProgram(PrecompiledProgram):
config: AgentConfig config: AgentConfig
@@ -273,8 +289,14 @@ class AgentProgram(PrecompiledProgram):
# Configure logging callback globally # Configure logging callback globally
dspy.settings.configure(callbacks=[ToolLoggingCallback()]) dspy.settings.configure(callbacks=[ToolLoggingCallback()])
agent = dspy.ReAct(CodingAssistant, tools=tools, max_iters=self.config.max_iters) agent = dspy.ReAct(
lm = dspy.LM(self.config.lm, api_base=self.config.api_base, max_tokens=self.config.max_tokens) CodingAssistant, tools=tools, max_iters=self.config.max_iters
)
lm = dspy.LM(
self.config.lm,
api_base=self.config.api_base,
max_tokens=self.config.max_tokens,
)
agent.set_lm(lm) agent.set_lm(lm)
self.agent = agent self.agent = agent
@@ -282,6 +304,7 @@ class AgentProgram(PrecompiledProgram):
assert task, "Task cannot be empty" assert task, "Task cannot be empty"
return self.agent(task=task) return self.agent(task=task)
# --- Main --- # --- Main ---
@@ -299,7 +322,9 @@ def main():
config.lm = model config.lm = model
agent = AgentProgram(config) agent = AgentProgram(config)
print(f"{BOLD}nanocode-dspy{RESET} | {DIM}{agent.config.lm} | {os.getcwd()}{RESET}\n") print(
f"{BOLD}nanocode-dspy{RESET} | {DIM}{agent.config.lm} | {os.getcwd()}{RESET}\n"
)
# Conversation history for context # Conversation history for context
history = [] history = []
@@ -345,6 +370,7 @@ def main():
break break
except Exception as err: except Exception as err:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
print(f"{RED}⏺ Error: {err}{RESET}") print(f"{RED}⏺ Error: {err}{RESET}")
@@ -353,4 +379,3 @@ if __name__ == "__main__":
agent = AgentProgram(AgentConfig(lm="openai/gpt-5.2-codex")) agent = AgentProgram(AgentConfig(lm="openai/gpt-5.2-codex"))
agent.push_to_hub("farouk1/nanocode") agent.push_to_hub("farouk1/nanocode")
# main() # main()

View File

@@ -37,7 +37,7 @@
"launch_kwargs": {}, "launch_kwargs": {},
"train_kwargs": {}, "train_kwargs": {},
"temperature": null, "temperature": null,
"max_tokens": 8192, "max_tokens": 16000,
"api_base": "https://openrouter.ai/api/v1" "api_base": "https://openrouter.ai/api/v1"
} }
}, },
@@ -79,7 +79,7 @@
"launch_kwargs": {}, "launch_kwargs": {},
"train_kwargs": {}, "train_kwargs": {},
"temperature": null, "temperature": null,
"max_tokens": 8192, "max_tokens": 16000,
"api_base": "https://openrouter.ai/api/v1" "api_base": "https://openrouter.ai/api/v1"
} }
}, },