From 7e6d3c455c112acc3325e285acabd7fc52e7ea3b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:29:54 +0000 Subject: [PATCH 01/12] Initial plan From 409df75c06e55822fc4b49601d97939a2210d312 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:36:31 +0000 Subject: [PATCH 02/12] feat: add copilot run CLI skeleton Co-authored-by: evcatalyst <8740078+evcatalyst@users.noreply.github.com> --- core/python/context/cli.py | 80 ++++++++++++++++ core/python/pyproject.toml | 21 +++++ core/python/tests/test_cli.py | 169 ++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+) create mode 100644 core/python/context/cli.py create mode 100644 core/python/tests/test_cli.py diff --git a/core/python/context/cli.py b/core/python/context/cli.py new file mode 100644 index 0000000..d01f760 --- /dev/null +++ b/core/python/context/cli.py @@ -0,0 +1,80 @@ +""" +CLI interface for Context runtime. + +Provides commands for managing and executing context-based LLM workflows. +""" + +import json +import pathlib +from typing import Optional + +import typer +from pydantic import BaseModel, Field, field_validator + +app = typer.Typer(help="Context: Lightweight execution abstraction for LLM requests") +copilot_app = typer.Typer(help="Copilot-style interface for one-off LLM runs") +app.add_typer(copilot_app, name="copilot") + + +class CopilotRunConfig(BaseModel): + """Configuration for a copilot run.""" + + prompt: str = Field(..., description="Natural language prompt describing the task") + user: str = Field(..., description="Username for this run") + budget: float = Field(..., description="USD budget cap for this run", gt=0.0) + instructions: Optional[str] = Field(None, description="Custom instructions") + instructions_file: Optional[pathlib.Path] = Field(None, description="Path to instructions file") + + @field_validator("budget") + @classmethod + def validate_budget(cls, v: float) -> float: + """Validate budget is positive.""" + if v <= 0: + raise ValueError("Budget must be greater than 0") + return v + + def model_post_init(self, __context) -> None: + """Validate mutual exclusivity of instructions flags.""" + if self.instructions is not None and self.instructions_file is not None: + raise ValueError("Cannot specify both --instructions and --instructions-file") + + +@copilot_app.command("run") +def copilot_run( + prompt: str = typer.Option(..., "--prompt", help="Natural language prompt describing the task"), + user: str = typer.Option(..., "--user", help="Username for this run"), + budget: float = typer.Option(..., "--budget", help="USD budget cap for this run"), + instructions: Optional[str] = typer.Option(None, "--instructions", help="Custom instructions"), + instructions_file: Optional[pathlib.Path] = typer.Option(None, "--instructions-file", help="Path to instructions file"), +): + """ + Execute a one-off copilot run with the specified prompt. + + Example: + context copilot run --prompt "build me a custom weekend planning tool" --user matthew --budget 0.05 + """ + try: + # Parse and validate configuration + config = CopilotRunConfig( + prompt=prompt, + user=user, + budget=budget, + instructions=instructions, + instructions_file=instructions_file, + ) + + # Print configuration as JSON + print(config.model_dump_json(indent=2)) + + except ValueError as e: + typer.echo(f"Error: {e}", err=True) + raise typer.Exit(code=1) + + +def main(): + """Entry point for the CLI.""" + app() + + +if __name__ == "__main__": + main() diff --git a/core/python/pyproject.toml b/core/python/pyproject.toml index b80e0af..47aae0f 100644 --- a/core/python/pyproject.toml +++ b/core/python/pyproject.toml @@ -24,6 +24,20 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", ] +dependencies = [ + "typer>=0.9.0", + "pydantic>=2.0.0", + "httpx>=0.24.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "pytest-mock>=3.10.0", +] + +[project.scripts] +context = "context.cli:app" [project.urls] Homepage = "https://github.com/gitbrainlab/context" @@ -32,3 +46,10 @@ Documentation = "https://github.com/gitbrainlab/context/blob/main/docs" [tool.setuptools] packages = ["context"] +package-dir = {"" = "."} + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] diff --git a/core/python/tests/test_cli.py b/core/python/tests/test_cli.py new file mode 100644 index 0000000..045e5fb --- /dev/null +++ b/core/python/tests/test_cli.py @@ -0,0 +1,169 @@ +""" +Tests for Context CLI functionality. +""" + +import json +import pathlib +from unittest.mock import MagicMock, patch + +import pytest +from typer.testing import CliRunner + +from context.cli import app, CopilotRunConfig + + +runner = CliRunner() + + +class TestCopilotRunConfig: + """Tests for CopilotRunConfig model.""" + + def test_valid_config(self): + """Test valid configuration.""" + config = CopilotRunConfig( + prompt="build me a custom weekend planning tool", + user="matthew", + budget=0.05, + ) + assert config.prompt == "build me a custom weekend planning tool" + assert config.user == "matthew" + assert config.budget == 0.05 + assert config.instructions is None + assert config.instructions_file is None + + def test_budget_must_be_positive(self): + """Test budget must be greater than 0.""" + with pytest.raises(ValueError, match="greater than 0"): + CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.0, + ) + + def test_negative_budget_fails(self): + """Test negative budget fails validation.""" + with pytest.raises(ValueError): + CopilotRunConfig( + prompt="test", + user="matthew", + budget=-0.05, + ) + + def test_instructions_mutual_exclusivity(self): + """Test cannot specify both instructions and instructions_file.""" + with pytest.raises(ValueError, match="Cannot specify both"): + CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + instructions="custom", + instructions_file=pathlib.Path("/tmp/test.txt"), + ) + + def test_instructions_flag_only(self): + """Test can specify only instructions flag.""" + config = CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + instructions="custom instructions", + ) + assert config.instructions == "custom instructions" + assert config.instructions_file is None + + def test_instructions_file_only(self): + """Test can specify only instructions_file flag.""" + config = CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + instructions_file=pathlib.Path("/tmp/test.txt"), + ) + assert config.instructions is None + assert config.instructions_file == pathlib.Path("/tmp/test.txt") + + +class TestCopilotRunCLI: + """Tests for copilot run CLI command.""" + + def test_help_command(self): + """Test help command.""" + result = runner.invoke(app, ["copilot", "run", "--help"]) + assert result.exit_code == 0 + assert "Execute a one-off copilot run" in result.stdout + + def test_valid_run_command(self): + """Test valid run command.""" + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "build me a custom weekend planning tool", + "--user", "matthew", + "--budget", "0.05", + ]) + assert result.exit_code == 0 + + # Parse output as JSON + output = json.loads(result.stdout) + assert output["prompt"] == "build me a custom weekend planning tool" + assert output["user"] == "matthew" + assert output["budget"] == 0.05 + assert output["instructions"] is None + assert output["instructions_file"] is None + + def test_invalid_budget(self): + """Test invalid budget fails.""" + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + "--user", "matthew", + "--budget", "0", + ]) + assert result.exit_code == 1 + assert "Error:" in result.stdout + + def test_missing_required_args(self): + """Test missing required arguments fails.""" + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + ]) + assert result.exit_code != 0 + + def test_instructions_flag(self): + """Test with instructions flag.""" + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + "--user", "matthew", + "--budget", "0.05", + "--instructions", "custom instructions", + ]) + assert result.exit_code == 0 + output = json.loads(result.stdout) + assert output["instructions"] == "custom instructions" + + def test_instructions_file_flag(self): + """Test with instructions-file flag.""" + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + "--user", "matthew", + "--budget", "0.05", + "--instructions-file", "/tmp/test.txt", + ]) + assert result.exit_code == 0 + output = json.loads(result.stdout) + assert output["instructions_file"] == "/tmp/test.txt" + + def test_both_instructions_flags_fails(self): + """Test both instructions flags fails.""" + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + "--user", "matthew", + "--budget", "0.05", + "--instructions", "custom", + "--instructions-file", "/tmp/test.txt", + ]) + assert result.exit_code == 1 + assert "Error:" in result.stdout From 79a63638b8c6e904b1b936581666bcfe19bec04d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:39:12 +0000 Subject: [PATCH 03/12] feat: parse prompt into structured config Co-authored-by: evcatalyst <8740078+evcatalyst@users.noreply.github.com> --- core/python/context/cli.py | 88 ++++++++++++++++++++++++++- core/python/tests/test_cli.py | 110 +++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 4 deletions(-) diff --git a/core/python/context/cli.py b/core/python/context/cli.py index d01f760..6f943f2 100644 --- a/core/python/context/cli.py +++ b/core/python/context/cli.py @@ -5,26 +5,76 @@ """ import json +import os import pathlib -from typing import Optional +import re +import uuid +from typing import Literal, Optional import typer -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, Field, field_validator, computed_field app = typer.Typer(help="Context: Lightweight execution abstraction for LLM requests") copilot_app = typer.Typer(help="Copilot-style interface for one-off LLM runs") app.add_typer(copilot_app, name="copilot") +def parse_prompt_hints(prompt: str) -> dict: + """ + Extract high-level task hints from natural-language prompt. + + Uses simple regex patterns to detect task types. + Returns a dict with detected task hints. + """ + prompt_lower = prompt.lower() + hints = { + "task_type": "general", + "keywords": [], + } + + # Detect planner/planning requests + if re.search(r'\b(plan|planner|planning|schedule|agenda)\b', prompt_lower): + hints["task_type"] = "planner" + hints["keywords"].append("planning") + + # Detect analysis requests + elif re.search(r'\b(analyz[e]?|analysis|examine|inspect|investigate)\b', prompt_lower): + hints["task_type"] = "analysis" + hints["keywords"].append("analysis") + + # Detect generation/creation requests + elif re.search(r'\b(build|create|generate|make|develop)\b', prompt_lower): + hints["task_type"] = "generation" + hints["keywords"].append("generation") + + # Detect summarization requests + elif re.search(r'\b(summariz[e]?|summary|brief|overview)\b', prompt_lower): + hints["task_type"] = "summarization" + hints["keywords"].append("summarization") + + return hints + + class CopilotRunConfig(BaseModel): """Configuration for a copilot run.""" + # Required fields prompt: str = Field(..., description="Natural language prompt describing the task") user: str = Field(..., description="Username for this run") budget: float = Field(..., description="USD budget cap for this run", gt=0.0) + + # Optional fields instructions: Optional[str] = Field(None, description="Custom instructions") instructions_file: Optional[pathlib.Path] = Field(None, description="Path to instructions file") + # Derived/computed fields (set during initialization) + prompt_id: uuid.UUID = Field(default_factory=uuid.uuid4, description="Unique identifier for this run") + model: str = Field(default="gpt-4o-mini", description="LLM model to use") + mode: Literal["one_off"] = Field(default="one_off", description="Execution mode") + + # Prompt analysis hints + prompt_hints: dict = Field(default_factory=dict, description="Parsed hints from prompt") + @field_validator("budget") @classmethod def validate_budget(cls, v: float) -> float: @@ -33,10 +83,42 @@ def validate_budget(cls, v: float) -> float: raise ValueError("Budget must be greater than 0") return v + @computed_field + @property + def user_instructions(self) -> str: + """ + Resolve user instructions from flag or file. + + Priority: + 1. --instructions flag + 2. --instructions-file (read from file) + 3. Empty string (default) + """ + if self.instructions is not None: + return self.instructions + + if self.instructions_file is not None: + try: + return self.instructions_file.read_text() + except Exception as e: + # If file doesn't exist or can't be read, return empty + return "" + + return "" + def model_post_init(self, __context) -> None: - """Validate mutual exclusivity of instructions flags.""" + """Post-initialization processing.""" + # Validate mutual exclusivity of instructions flags if self.instructions is not None and self.instructions_file is not None: raise ValueError("Cannot specify both --instructions and --instructions-file") + + # Parse prompt hints + self.prompt_hints = parse_prompt_hints(self.prompt) + + # Override model from environment if set + env_model = os.getenv("COPILOT_MODEL") + if env_model: + self.model = env_model @copilot_app.command("run") diff --git a/core/python/tests/test_cli.py b/core/python/tests/test_cli.py index 045e5fb..ba0227d 100644 --- a/core/python/tests/test_cli.py +++ b/core/python/tests/test_cli.py @@ -3,18 +3,53 @@ """ import json +import os import pathlib +import tempfile from unittest.mock import MagicMock, patch import pytest from typer.testing import CliRunner -from context.cli import app, CopilotRunConfig +from context.cli import app, CopilotRunConfig, parse_prompt_hints runner = CliRunner() +class TestPromptParsing: + """Tests for prompt parsing functionality.""" + + def test_parse_planner_hint(self): + """Test detection of planner task type.""" + hints = parse_prompt_hints("build me a custom weekend planning tool") + assert hints["task_type"] == "planner" + assert "planning" in hints["keywords"] + + def test_parse_analysis_hint(self): + """Test detection of analysis task type.""" + hints = parse_prompt_hints("analyze this dataset") + assert hints["task_type"] == "analysis" + assert "analysis" in hints["keywords"] + + def test_parse_generation_hint(self): + """Test detection of generation task type.""" + hints = parse_prompt_hints("create a new application") + assert hints["task_type"] == "generation" + assert "generation" in hints["keywords"] + + def test_parse_summarization_hint(self): + """Test detection of summarization task type.""" + hints = parse_prompt_hints("summarize this document") + assert hints["task_type"] == "summarization" + assert "summarization" in hints["keywords"] + + def test_parse_general_hint(self): + """Test fallback to general task type.""" + hints = parse_prompt_hints("some random task") + assert hints["task_type"] == "general" + + class TestCopilotRunConfig: """Tests for CopilotRunConfig model.""" @@ -31,6 +66,72 @@ def test_valid_config(self): assert config.instructions is None assert config.instructions_file is None + def test_derived_fields(self): + """Test derived fields are set correctly.""" + config = CopilotRunConfig( + prompt="build me a custom weekend planning tool", + user="matthew", + budget=0.05, + ) + # Check prompt_id is a valid UUID + assert config.prompt_id is not None + assert str(config.prompt_id) # Can be converted to string + + # Check default model + assert config.model == "gpt-4o-mini" + + # Check mode + assert config.mode == "one_off" + + # Check prompt hints are parsed + assert config.prompt_hints["task_type"] == "planner" + + def test_user_instructions_from_flag(self): + """Test user_instructions resolved from flag.""" + config = CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + instructions="custom instructions", + ) + assert config.user_instructions == "custom instructions" + + def test_user_instructions_from_file(self): + """Test user_instructions resolved from file.""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f: + f.write("file instructions") + temp_path = pathlib.Path(f.name) + + try: + config = CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + instructions_file=temp_path, + ) + assert config.user_instructions == "file instructions" + finally: + temp_path.unlink() + + def test_user_instructions_default(self): + """Test user_instructions defaults to empty.""" + config = CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + ) + assert config.user_instructions == "" + + def test_model_override_from_env(self): + """Test model can be overridden from environment.""" + with patch.dict(os.environ, {"COPILOT_MODEL": "gpt-4"}): + config = CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + ) + assert config.model == "gpt-4" + def test_budget_must_be_positive(self): """Test budget must be greater than 0.""" with pytest.raises(ValueError, match="greater than 0"): @@ -109,6 +210,13 @@ def test_valid_run_command(self): assert output["budget"] == 0.05 assert output["instructions"] is None assert output["instructions_file"] is None + + # Check derived fields + assert "prompt_id" in output + assert output["model"] == "gpt-4o-mini" + assert output["mode"] == "one_off" + assert output["prompt_hints"]["task_type"] == "planner" + assert output["user_instructions"] == "" def test_invalid_budget(self): """Test invalid budget fails.""" From d279bc0c68a7b636ec8810e6e156640e681d6ae0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:43:43 +0000 Subject: [PATCH 04/12] feat: integrate LiteLLM for one-off LLM execution Co-authored-by: evcatalyst <8740078+evcatalyst@users.noreply.github.com> --- .../326c07e0-f901-410d-bcbf-0800fcfa6251.md | 11 + .../47224763-3468-4bc9-874c-22b8623ed717.md | 11 + .../48db9157-d767-4a9a-a0a0-cc1afeb6ee50.md | 11 + .../6390fc88-56fa-41a2-bb03-40aac6b2e126.md | 11 + .../cab1ea28-73a8-4c44-8aae-78dfb4324b11.md | 11 + .../d93f04e5-5332-48d9-b2a6-38875cbfb202.md | 11 + core/python/context/cli.py | 270 +++++++++++++++++- core/python/tests/test_cli.py | 243 +++++++++++++--- 8 files changed, 535 insertions(+), 44 deletions(-) create mode 100644 core/python/.context/copilot/326c07e0-f901-410d-bcbf-0800fcfa6251.md create mode 100644 core/python/.context/copilot/47224763-3468-4bc9-874c-22b8623ed717.md create mode 100644 core/python/.context/copilot/48db9157-d767-4a9a-a0a0-cc1afeb6ee50.md create mode 100644 core/python/.context/copilot/6390fc88-56fa-41a2-bb03-40aac6b2e126.md create mode 100644 core/python/.context/copilot/cab1ea28-73a8-4c44-8aae-78dfb4324b11.md create mode 100644 core/python/.context/copilot/d93f04e5-5332-48d9-b2a6-38875cbfb202.md diff --git a/core/python/.context/copilot/326c07e0-f901-410d-bcbf-0800fcfa6251.md b/core/python/.context/copilot/326c07e0-f901-410d-bcbf-0800fcfa6251.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/326c07e0-f901-410d-bcbf-0800fcfa6251.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/47224763-3468-4bc9-874c-22b8623ed717.md b/core/python/.context/copilot/47224763-3468-4bc9-874c-22b8623ed717.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/47224763-3468-4bc9-874c-22b8623ed717.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/48db9157-d767-4a9a-a0a0-cc1afeb6ee50.md b/core/python/.context/copilot/48db9157-d767-4a9a-a0a0-cc1afeb6ee50.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/48db9157-d767-4a9a-a0a0-cc1afeb6ee50.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/6390fc88-56fa-41a2-bb03-40aac6b2e126.md b/core/python/.context/copilot/6390fc88-56fa-41a2-bb03-40aac6b2e126.md new file mode 100644 index 0000000..4a875ad --- /dev/null +++ b/core/python/.context/copilot/6390fc88-56fa-41a2-bb03-40aac6b2e126.md @@ -0,0 +1,11 @@ +# Weekend Planning Tool + +## Request +build me a custom weekend planning tool + +## Activities +Weekend planning suggestions... + +## Notes +- Generated by Context Copilot +- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/cab1ea28-73a8-4c44-8aae-78dfb4324b11.md b/core/python/.context/copilot/cab1ea28-73a8-4c44-8aae-78dfb4324b11.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/cab1ea28-73a8-4c44-8aae-78dfb4324b11.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/d93f04e5-5332-48d9-b2a6-38875cbfb202.md b/core/python/.context/copilot/d93f04e5-5332-48d9-b2a6-38875cbfb202.md new file mode 100644 index 0000000..4a875ad --- /dev/null +++ b/core/python/.context/copilot/d93f04e5-5332-48d9-b2a6-38875cbfb202.md @@ -0,0 +1,11 @@ +# Weekend Planning Tool + +## Request +build me a custom weekend planning tool + +## Activities +Weekend planning suggestions... + +## Notes +- Generated by Context Copilot +- Budget estimate based on LLM usage diff --git a/core/python/context/cli.py b/core/python/context/cli.py index 6f943f2..550eda4 100644 --- a/core/python/context/cli.py +++ b/core/python/context/cli.py @@ -9,8 +9,10 @@ import pathlib import re import uuid +from datetime import datetime, timezone from typing import Literal, Optional +import httpx import typer from pydantic import BaseModel, Field, field_validator, computed_field @@ -19,6 +21,37 @@ app.add_typer(copilot_app, name="copilot") +# Pricing table for token estimation (USD per token) +MODEL_PRICING = { + "gpt-4o-mini": { + "input": 0.00000015, # $0.15 per 1M tokens + "output": 0.0000006, # $0.60 per 1M tokens + }, + "gpt-4o": { + "input": 0.0000025, # $2.50 per 1M tokens + "output": 0.00001, # $10.00 per 1M tokens + }, + "gpt-4": { + "input": 0.00003, # $30 per 1M tokens + "output": 0.00006, # $60 per 1M tokens + }, +} + + +class UsageMetadata(BaseModel): + """Token usage metadata.""" + prompt_tokens: int = 0 + completion_tokens: int = 0 + total_tokens: int = 0 + + +class LLMResponse(BaseModel): + """LLM response structure.""" + content: str + usage: UsageMetadata + cost_usd: float + + def parse_prompt_hints(prompt: str) -> dict: """ Extract high-level task hints from natural-language prompt. @@ -55,6 +88,177 @@ def parse_prompt_hints(prompt: str) -> dict: return hints +def budget_to_max_tokens(budget_usd: float, model: str = "gpt-4o-mini") -> int: + """ + Convert USD budget to approximate max_tokens with safety margin. + + Uses a simple pricing estimate based on average input/output token costs. + Applies 20% safety margin to ensure we don't exceed budget. + """ + pricing = MODEL_PRICING.get(model, MODEL_PRICING["gpt-4o-mini"]) + + # Use weighted average: assume 70% output tokens, 30% input tokens + avg_price_per_token = (0.3 * pricing["input"]) + (0.7 * pricing["output"]) + + # Calculate max tokens with 20% safety margin + max_tokens = int((budget_usd * 0.8) / avg_price_per_token) + + return max_tokens + + +def calculate_cost(usage: UsageMetadata, model: str = "gpt-4o-mini") -> float: + """Calculate USD cost from usage metadata.""" + pricing = MODEL_PRICING.get(model, MODEL_PRICING["gpt-4o-mini"]) + + input_cost = usage.prompt_tokens * pricing["input"] + output_cost = usage.completion_tokens * pricing["output"] + + return input_cost + output_cost + + +def call_litellm( + prompt: str, + model: str, + max_tokens: int, + user_instructions: str = "", + virtual_key: Optional[str] = None, + proxy_url: str = "http://localhost:4000", +) -> LLMResponse: + """ + Call LiteLLM proxy to execute the prompt. + + Args: + prompt: The main user prompt + model: Model identifier + max_tokens: Maximum tokens for completion + user_instructions: Optional user-provided instructions + virtual_key: Virtual key for authentication + proxy_url: LiteLLM proxy URL + + Returns: + LLMResponse with content, usage, and cost + + Raises: + Exception: If LiteLLM call fails + """ + # Build messages payload + messages = [] + + # Add system message if needed + system_msg = "You are a helpful assistant." + messages.append({"role": "system", "content": system_msg}) + + # Add user instructions if provided + if user_instructions: + messages.append({"role": "user", "content": f"Instructions: {user_instructions}"}) + + # Add main prompt + messages.append({"role": "user", "content": prompt}) + + # Prepare request payload + payload = { + "model": model, + "messages": messages, + "max_tokens": max_tokens, + } + + # Prepare headers + headers = { + "Content-Type": "application/json", + } + if virtual_key: + headers["Authorization"] = f"Bearer {virtual_key}" + + # Make HTTP POST to LiteLLM proxy + try: + with httpx.Client(timeout=60.0) as client: + response = client.post( + f"{proxy_url}/chat/completions", + json=payload, + headers=headers, + ) + response.raise_for_status() + data = response.json() + + # Parse response + content = data["choices"][0]["message"]["content"] + usage_data = data.get("usage", {}) + usage = UsageMetadata( + prompt_tokens=usage_data.get("prompt_tokens", 0), + completion_tokens=usage_data.get("completion_tokens", 0), + total_tokens=usage_data.get("total_tokens", 0), + ) + + # Calculate cost + cost_usd = calculate_cost(usage, model) + + return LLMResponse(content=content, usage=usage, cost_usd=cost_usd) + + except httpx.HTTPStatusError as e: + raise Exception(f"LiteLLM API error: {e.response.status_code} - {e.response.text}") + except httpx.RequestError as e: + raise Exception(f"LiteLLM connection error: {e}") + except (KeyError, IndexError) as e: + raise Exception(f"Invalid LiteLLM response format: {e}") + + +def generate_dashboard( + prompt: str, + llm_response: LLMResponse, + task_type: str = "general", + output_path: pathlib.Path = None, +) -> pathlib.Path: + """ + Generate a Markdown dashboard file from LLM response. + + Args: + prompt: Original prompt + llm_response: LLM response + task_type: Type of task (planner, analysis, etc.) + output_path: Path to write dashboard + + Returns: + Path to generated dashboard file + """ + content = llm_response.content + + # Create structured markdown based on task type + if task_type == "planner": + # Extract sections for planner + markdown = f"""# Weekend Planning Tool + +## Request +{prompt} + +## Activities +{content} + +## Notes +- Generated by Context Copilot +- Budget estimate based on LLM usage +""" + else: + # Generic format for other task types + markdown = f"""# Task: {task_type.capitalize()} + +## Request +{prompt} + +## Response +{content} + +## Metadata +- Task Type: {task_type} +- Generated by Context Copilot +""" + + # Write to file + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(markdown) + + return output_path + + class CopilotRunConfig(BaseModel): """Configuration for a copilot run.""" @@ -135,6 +339,8 @@ def copilot_run( Example: context copilot run --prompt "build me a custom weekend planning tool" --user matthew --budget 0.05 """ + timestamp_start = datetime.now(timezone.utc) + try: # Parse and validate configuration config = CopilotRunConfig( @@ -145,12 +351,72 @@ def copilot_run( instructions_file=instructions_file, ) - # Print configuration as JSON - print(config.model_dump_json(indent=2)) + # Print configuration + typer.echo(f"Configuration: {config.model_dump_json(indent=2)}") + typer.echo("") + + # Get LiteLLM proxy URL + proxy_url = os.getenv("LITELLM_PROXY_URL", "http://localhost:4000") + typer.echo(f"LiteLLM Proxy: {proxy_url}") + + # Get user virtual key + virtual_key_env = f"CONTEXT_VIRTUAL_KEY_{user.upper()}" + virtual_key = os.getenv(virtual_key_env) + if not virtual_key: + raise ValueError( + f"Virtual key not found. Please set {virtual_key_env} environment variable." + ) + + # Convert budget to max_tokens + max_tokens = budget_to_max_tokens(config.budget, config.model) + typer.echo(f"Estimated max tokens: {max_tokens}") + typer.echo("") + + # Call LiteLLM + typer.echo("Calling LiteLLM...") + llm_response = call_litellm( + prompt=config.prompt, + model=config.model, + max_tokens=max_tokens, + user_instructions=config.user_instructions, + virtual_key=virtual_key, + proxy_url=proxy_url, + ) + + typer.echo(f"✓ LLM call successful") + typer.echo(f" Tokens used: {llm_response.usage.total_tokens}") + typer.echo(f" Cost: ${llm_response.cost_usd:.6f}") + typer.echo("") + + # Generate dashboard + output_dir = pathlib.Path.cwd() / ".context" / "copilot" + output_path = output_dir / f"{config.prompt_id}.md" + + generate_dashboard( + prompt=config.prompt, + llm_response=llm_response, + task_type=config.prompt_hints["task_type"], + output_path=output_path, + ) + + typer.echo(f"✓ Dashboard generated: {output_path}") + typer.echo("") + + # Show preview of response + typer.echo("Response preview:") + typer.echo("-" * 60) + preview = llm_response.content[:500] + if len(llm_response.content) > 500: + preview += "..." + typer.echo(preview) + typer.echo("-" * 60) except ValueError as e: typer.echo(f"Error: {e}", err=True) raise typer.Exit(code=1) + except Exception as e: + typer.echo(f"Error: {e}", err=True) + raise typer.Exit(code=1) def main(): diff --git a/core/python/tests/test_cli.py b/core/python/tests/test_cli.py index ba0227d..d937be2 100644 --- a/core/python/tests/test_cli.py +++ b/core/python/tests/test_cli.py @@ -6,12 +6,21 @@ import os import pathlib import tempfile -from unittest.mock import MagicMock, patch +from unittest.mock import MagicMock, patch, Mock import pytest from typer.testing import CliRunner -from context.cli import app, CopilotRunConfig, parse_prompt_hints +from context.cli import ( + app, + CopilotRunConfig, + parse_prompt_hints, + budget_to_max_tokens, + calculate_cost, + UsageMetadata, + LLMResponse, + generate_dashboard, +) runner = CliRunner() @@ -193,30 +202,45 @@ def test_help_command(self): assert result.exit_code == 0 assert "Execute a one-off copilot run" in result.stdout - def test_valid_run_command(self): - """Test valid run command.""" + @patch('context.cli.call_litellm') + def test_valid_run_command(self, mock_call_litellm): + """Test valid run command with mocked LiteLLM.""" + # Mock LiteLLM response + mock_usage = UsageMetadata( + prompt_tokens=100, + completion_tokens=200, + total_tokens=300, + ) + mock_response = LLMResponse( + content="Weekend planning suggestions...", + usage=mock_usage, + cost_usd=0.0001, + ) + mock_call_litellm.return_value = mock_response + + # Set required environment variable + with patch.dict(os.environ, {"CONTEXT_VIRTUAL_KEY_MATTHEW": "test-key"}): + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "build me a custom weekend planning tool", + "--user", "matthew", + "--budget", "0.05", + ]) + + assert result.exit_code == 0 + assert "LLM call successful" in result.stdout + assert "Dashboard generated" in result.stdout + + def test_missing_virtual_key(self): + """Test error when virtual key is missing.""" result = runner.invoke(app, [ "copilot", "run", - "--prompt", "build me a custom weekend planning tool", + "--prompt", "test", "--user", "matthew", "--budget", "0.05", ]) - assert result.exit_code == 0 - - # Parse output as JSON - output = json.loads(result.stdout) - assert output["prompt"] == "build me a custom weekend planning tool" - assert output["user"] == "matthew" - assert output["budget"] == 0.05 - assert output["instructions"] is None - assert output["instructions_file"] is None - - # Check derived fields - assert "prompt_id" in output - assert output["model"] == "gpt-4o-mini" - assert output["mode"] == "one_off" - assert output["prompt_hints"]["task_type"] == "planner" - assert output["user_instructions"] == "" + assert result.exit_code == 1 + assert "Virtual key not found" in result.stdout def test_invalid_budget(self): """Test invalid budget fails.""" @@ -237,31 +261,53 @@ def test_missing_required_args(self): ]) assert result.exit_code != 0 - def test_instructions_flag(self): + @patch('context.cli.call_litellm') + def test_instructions_flag(self, mock_call_litellm): """Test with instructions flag.""" - result = runner.invoke(app, [ - "copilot", "run", - "--prompt", "test", - "--user", "matthew", - "--budget", "0.05", - "--instructions", "custom instructions", - ]) + # Mock LiteLLM response + mock_usage = UsageMetadata(prompt_tokens=50, completion_tokens=100, total_tokens=150) + mock_response = LLMResponse(content="Response", usage=mock_usage, cost_usd=0.00005) + mock_call_litellm.return_value = mock_response + + with patch.dict(os.environ, {"CONTEXT_VIRTUAL_KEY_MATTHEW": "test-key"}): + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + "--user", "matthew", + "--budget", "0.05", + "--instructions", "custom instructions", + ]) + assert result.exit_code == 0 - output = json.loads(result.stdout) - assert output["instructions"] == "custom instructions" + assert "LLM call successful" in result.stdout - def test_instructions_file_flag(self): + @patch('context.cli.call_litellm') + def test_instructions_file_flag(self, mock_call_litellm): """Test with instructions-file flag.""" - result = runner.invoke(app, [ - "copilot", "run", - "--prompt", "test", - "--user", "matthew", - "--budget", "0.05", - "--instructions-file", "/tmp/test.txt", - ]) - assert result.exit_code == 0 - output = json.loads(result.stdout) - assert output["instructions_file"] == "/tmp/test.txt" + # Mock LiteLLM response + mock_usage = UsageMetadata(prompt_tokens=50, completion_tokens=100, total_tokens=150) + mock_response = LLMResponse(content="Response", usage=mock_usage, cost_usd=0.00005) + mock_call_litellm.return_value = mock_response + + # Create temp file + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f: + f.write("test instructions") + temp_path = f.name + + try: + with patch.dict(os.environ, {"CONTEXT_VIRTUAL_KEY_MATTHEW": "test-key"}): + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + "--user", "matthew", + "--budget", "0.05", + "--instructions-file", temp_path, + ]) + + assert result.exit_code == 0 + assert "LLM call successful" in result.stdout + finally: + pathlib.Path(temp_path).unlink() def test_both_instructions_flags_fails(self): """Test both instructions flags fails.""" @@ -275,3 +321,116 @@ def test_both_instructions_flags_fails(self): ]) assert result.exit_code == 1 assert "Error:" in result.stdout + + +class TestBudgetCalculations: + """Tests for budget and cost calculations.""" + + def test_budget_to_max_tokens_gpt4o_mini(self): + """Test budget to max tokens for gpt-4o-mini.""" + max_tokens = budget_to_max_tokens(0.05, "gpt-4o-mini") + assert max_tokens > 0 + # With 0.05 budget and pricing, should get reasonable token count + assert max_tokens > 50000 # At least 50k tokens + + def test_budget_to_max_tokens_gpt4(self): + """Test budget to max tokens for gpt-4.""" + max_tokens = budget_to_max_tokens(0.05, "gpt-4") + assert max_tokens > 0 + # GPT-4 is more expensive, should get fewer tokens + assert max_tokens < 2000 # Less than 2k tokens + + def test_calculate_cost(self): + """Test cost calculation from usage.""" + usage = UsageMetadata( + prompt_tokens=100, + completion_tokens=200, + total_tokens=300, + ) + cost = calculate_cost(usage, "gpt-4o-mini") + assert cost > 0 + # Should be very small for gpt-4o-mini + assert cost < 0.001 # Less than 0.1 cents + + +class TestLLMResponse: + """Tests for LLM response handling.""" + + def test_llm_response_creation(self): + """Test creating LLM response.""" + usage = UsageMetadata( + prompt_tokens=100, + completion_tokens=200, + total_tokens=300, + ) + response = LLMResponse( + content="Test response", + usage=usage, + cost_usd=0.0001, + ) + assert response.content == "Test response" + assert response.usage.total_tokens == 300 + assert response.cost_usd == 0.0001 + + +class TestDashboardGeneration: + """Tests for dashboard generation.""" + + def test_generate_dashboard_planner(self): + """Test generating planner dashboard.""" + usage = UsageMetadata( + prompt_tokens=100, + completion_tokens=200, + total_tokens=300, + ) + llm_response = LLMResponse( + content="Weekend activities:\n1. Hiking\n2. Museum visit", + usage=usage, + cost_usd=0.0001, + ) + + with tempfile.TemporaryDirectory() as tmpdir: + output_path = pathlib.Path(tmpdir) / "test.md" + result_path = generate_dashboard( + prompt="build me a weekend planner", + llm_response=llm_response, + task_type="planner", + output_path=output_path, + ) + + assert result_path == output_path + assert output_path.exists() + + content = output_path.read_text() + assert "Weekend Planning Tool" in content + assert "Activities" in content + assert "Hiking" in content + + def test_generate_dashboard_general(self): + """Test generating general dashboard.""" + usage = UsageMetadata( + prompt_tokens=50, + completion_tokens=100, + total_tokens=150, + ) + llm_response = LLMResponse( + content="Analysis complete", + usage=usage, + cost_usd=0.00005, + ) + + with tempfile.TemporaryDirectory() as tmpdir: + output_path = pathlib.Path(tmpdir) / "test.md" + result_path = generate_dashboard( + prompt="analyze data", + llm_response=llm_response, + task_type="analysis", + output_path=output_path, + ) + + assert result_path == output_path + assert output_path.exists() + + content = output_path.read_text() + assert "Task: Analysis" in content + assert "Analysis complete" in content From e85287ff5b24ff4e9f511cf7e9badfc7113e9121 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:47:42 +0000 Subject: [PATCH 05/12] feat: add JSON logging for copilot runs Co-authored-by: evcatalyst <8740078+evcatalyst@users.noreply.github.com> --- .gitignore | 4 + ...> 06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md} | 0 ...> 0bbeb524-e146-4121-adad-ecdcdfef4c35.md} | 0 .../11d01c40-ec71-4733-aa10-a6dffa090e2f.md | 11 ++ .../2319073a-fff4-49f4-a467-7aa99293ab77.md | 11 ++ ...> 392d629a-866e-4cf9-96cb-835b86ed9f08.md} | 0 ...> 5d62f44c-9d44-48bb-95e9-a0f372645a8c.md} | 0 ...> 92027c0b-a327-4def-b023-3a1345d9ff54.md} | 0 .../aa202034-5114-4f1b-9e38-4362cf9ab474.md | 11 ++ .../df7e52c4-e2a7-449c-9904-08334bcb4376.md | 11 ++ ...> e6e6c177-877d-4c5e-ae48-731317e8b049.md} | 0 .../06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json | 19 +++ .../0bbeb524-e146-4121-adad-ecdcdfef4c35.json | 19 +++ .../11d01c40-ec71-4733-aa10-a6dffa090e2f.json | 19 +++ .../2319073a-fff4-49f4-a467-7aa99293ab77.json | 19 +++ .../2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json | 15 +++ .../392d629a-866e-4cf9-96cb-835b86ed9f08.json | 19 +++ .../3d0791be-df97-4fa7-843b-a8f8887a23e9.json | 15 +++ .../4c86f2f6-cd8e-4310-b83c-3166a06c9324.json | 15 +++ .../5d62f44c-9d44-48bb-95e9-a0f372645a8c.json | 19 +++ .../7c89bad0-5c4b-4b0d-8379-f483d8809c06.json | 15 +++ .../92027c0b-a327-4def-b023-3a1345d9ff54.json | 19 +++ .../99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json | 15 +++ .../a7153bd1-bc26-4eec-a2b8-bd310886df10.json | 15 +++ .../aa202034-5114-4f1b-9e38-4362cf9ab474.json | 19 +++ .../df7e52c4-e2a7-449c-9904-08334bcb4376.json | 19 +++ .../e6e6c177-877d-4c5e-ae48-731317e8b049.json | 19 +++ core/python/context/cli.py | 88 ++++++++++++- core/python/tests/test_cli.py | 119 ++++++++++++++++++ 29 files changed, 533 insertions(+), 2 deletions(-) rename core/python/.context/copilot/{326c07e0-f901-410d-bcbf-0800fcfa6251.md => 06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md} (100%) rename core/python/.context/copilot/{6390fc88-56fa-41a2-bb03-40aac6b2e126.md => 0bbeb524-e146-4121-adad-ecdcdfef4c35.md} (100%) create mode 100644 core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md create mode 100644 core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md rename core/python/.context/copilot/{47224763-3468-4bc9-874c-22b8623ed717.md => 392d629a-866e-4cf9-96cb-835b86ed9f08.md} (100%) rename core/python/.context/copilot/{48db9157-d767-4a9a-a0a0-cc1afeb6ee50.md => 5d62f44c-9d44-48bb-95e9-a0f372645a8c.md} (100%) rename core/python/.context/copilot/{d93f04e5-5332-48d9-b2a6-38875cbfb202.md => 92027c0b-a327-4def-b023-3a1345d9ff54.md} (100%) create mode 100644 core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md create mode 100644 core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md rename core/python/.context/copilot/{cab1ea28-73a8-4c44-8aae-78dfb4324b11.md => e6e6c177-877d-4c5e-ae48-731317e8b049.md} (100%) create mode 100644 core/python/.context/logs/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json create mode 100644 core/python/.context/logs/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.json create mode 100644 core/python/.context/logs/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.json create mode 100644 core/python/.context/logs/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.json create mode 100644 core/python/.context/logs/copilot/2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json create mode 100644 core/python/.context/logs/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.json create mode 100644 core/python/.context/logs/copilot/3d0791be-df97-4fa7-843b-a8f8887a23e9.json create mode 100644 core/python/.context/logs/copilot/4c86f2f6-cd8e-4310-b83c-3166a06c9324.json create mode 100644 core/python/.context/logs/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.json create mode 100644 core/python/.context/logs/copilot/7c89bad0-5c4b-4b0d-8379-f483d8809c06.json create mode 100644 core/python/.context/logs/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.json create mode 100644 core/python/.context/logs/copilot/99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json create mode 100644 core/python/.context/logs/copilot/a7153bd1-bc26-4eec-a2b8-bd310886df10.json create mode 100644 core/python/.context/logs/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.json create mode 100644 core/python/.context/logs/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.json create mode 100644 core/python/.context/logs/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.json diff --git a/.gitignore b/.gitignore index 4fb9589..44aa472 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,7 @@ coverage/ examples/**/analysis_*.json examples/**/context.json examples/**/*.log + +# Copilot runtime outputs +.context/copilot/ +.context/logs/ diff --git a/core/python/.context/copilot/326c07e0-f901-410d-bcbf-0800fcfa6251.md b/core/python/.context/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md similarity index 100% rename from core/python/.context/copilot/326c07e0-f901-410d-bcbf-0800fcfa6251.md rename to core/python/.context/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md diff --git a/core/python/.context/copilot/6390fc88-56fa-41a2-bb03-40aac6b2e126.md b/core/python/.context/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.md similarity index 100% rename from core/python/.context/copilot/6390fc88-56fa-41a2-bb03-40aac6b2e126.md rename to core/python/.context/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.md diff --git a/core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md b/core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md b/core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/47224763-3468-4bc9-874c-22b8623ed717.md b/core/python/.context/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.md similarity index 100% rename from core/python/.context/copilot/47224763-3468-4bc9-874c-22b8623ed717.md rename to core/python/.context/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.md diff --git a/core/python/.context/copilot/48db9157-d767-4a9a-a0a0-cc1afeb6ee50.md b/core/python/.context/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.md similarity index 100% rename from core/python/.context/copilot/48db9157-d767-4a9a-a0a0-cc1afeb6ee50.md rename to core/python/.context/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.md diff --git a/core/python/.context/copilot/d93f04e5-5332-48d9-b2a6-38875cbfb202.md b/core/python/.context/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.md similarity index 100% rename from core/python/.context/copilot/d93f04e5-5332-48d9-b2a6-38875cbfb202.md rename to core/python/.context/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.md diff --git a/core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md b/core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md b/core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/cab1ea28-73a8-4c44-8aae-78dfb4324b11.md b/core/python/.context/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.md similarity index 100% rename from core/python/.context/copilot/cab1ea28-73a8-4c44-8aae-78dfb4324b11.md rename to core/python/.context/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.md diff --git a/core/python/.context/logs/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json b/core/python/.context/logs/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json new file mode 100644 index 0000000..7ac5dbd --- /dev/null +++ b/core/python/.context/logs/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "06f1adde-6f4a-4398-b27d-ebf7ac519e1f", + "timestamp_start": "2026-01-08T17:47:11.872086+00:00", + "timestamp_end": "2026-01-08T17:47:11.872355+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "flag", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.json b/core/python/.context/logs/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.json new file mode 100644 index 0000000..2410355 --- /dev/null +++ b/core/python/.context/logs/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "0bbeb524-e146-4121-adad-ecdcdfef4c35", + "timestamp_start": "2026-01-08T17:47:11.861395+00:00", + "timestamp_end": "2026-01-08T17:47:11.861762+00:00", + "user": "matthew", + "prompt": "build me a custom weekend planning tool", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.json b/core/python/.context/logs/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.json new file mode 100644 index 0000000..4a591b3 --- /dev/null +++ b/core/python/.context/logs/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "11d01c40-ec71-4733-aa10-a6dffa090e2f", + "timestamp_start": "2026-01-08T17:47:20.358021+00:00", + "timestamp_end": "2026-01-08T17:47:20.358270+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.json b/core/python/.context/logs/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.json new file mode 100644 index 0000000..238f5a4 --- /dev/null +++ b/core/python/.context/logs/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "2319073a-fff4-49f4-a467-7aa99293ab77", + "timestamp_start": "2026-01-08T17:47:11.888079+00:00", + "timestamp_end": "2026-01-08T17:47:11.888330+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json b/core/python/.context/logs/copilot/2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json new file mode 100644 index 0000000..ee7b28e --- /dev/null +++ b/core/python/.context/logs/copilot/2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "2a1d30d5-f893-4eb2-83f4-982a5c608bb7", + "timestamp_start": "2026-01-08T17:47:20.360750+00:00", + "timestamp_end": "2026-01-08T17:47:20.360857+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.json b/core/python/.context/logs/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.json new file mode 100644 index 0000000..0592ebd --- /dev/null +++ b/core/python/.context/logs/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "392d629a-866e-4cf9-96cb-835b86ed9f08", + "timestamp_start": "2026-01-08T17:47:11.875589+00:00", + "timestamp_end": "2026-01-08T17:47:11.875961+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "file", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/3d0791be-df97-4fa7-843b-a8f8887a23e9.json b/core/python/.context/logs/copilot/3d0791be-df97-4fa7-843b-a8f8887a23e9.json new file mode 100644 index 0000000..4b4c9af --- /dev/null +++ b/core/python/.context/logs/copilot/3d0791be-df97-4fa7-843b-a8f8887a23e9.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "3d0791be-df97-4fa7-843b-a8f8887a23e9", + "timestamp_start": "2026-01-08T17:47:20.336174+00:00", + "timestamp_end": "2026-01-08T17:47:20.336255+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/4c86f2f6-cd8e-4310-b83c-3166a06c9324.json b/core/python/.context/logs/copilot/4c86f2f6-cd8e-4310-b83c-3166a06c9324.json new file mode 100644 index 0000000..6f9176e --- /dev/null +++ b/core/python/.context/logs/copilot/4c86f2f6-cd8e-4310-b83c-3166a06c9324.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "4c86f2f6-cd8e-4310-b83c-3166a06c9324", + "timestamp_start": "2026-01-08T17:46:41.038638+00:00", + "timestamp_end": "2026-01-08T17:46:41.038724+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.json b/core/python/.context/logs/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.json new file mode 100644 index 0000000..38e880e --- /dev/null +++ b/core/python/.context/logs/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "5d62f44c-9d44-48bb-95e9-a0f372645a8c", + "timestamp_start": "2026-01-08T17:47:20.346539+00:00", + "timestamp_end": "2026-01-08T17:47:20.346901+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "file", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/7c89bad0-5c4b-4b0d-8379-f483d8809c06.json b/core/python/.context/logs/copilot/7c89bad0-5c4b-4b0d-8379-f483d8809c06.json new file mode 100644 index 0000000..de5fc18 --- /dev/null +++ b/core/python/.context/logs/copilot/7c89bad0-5c4b-4b0d-8379-f483d8809c06.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "7c89bad0-5c4b-4b0d-8379-f483d8809c06", + "timestamp_start": "2026-01-08T17:46:10.506510+00:00", + "timestamp_end": "2026-01-08T17:46:10.506595+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.json b/core/python/.context/logs/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.json new file mode 100644 index 0000000..4e4002a --- /dev/null +++ b/core/python/.context/logs/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "92027c0b-a327-4def-b023-3a1345d9ff54", + "timestamp_start": "2026-01-08T17:47:20.333108+00:00", + "timestamp_end": "2026-01-08T17:47:20.333426+00:00", + "user": "matthew", + "prompt": "build me a custom weekend planning tool", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json b/core/python/.context/logs/copilot/99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json new file mode 100644 index 0000000..32f2495 --- /dev/null +++ b/core/python/.context/logs/copilot/99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "99a72c62-13eb-4e0c-bb35-eb8df64f84dc", + "timestamp_start": "2026-01-08T17:47:11.890893+00:00", + "timestamp_end": "2026-01-08T17:47:11.890969+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/a7153bd1-bc26-4eec-a2b8-bd310886df10.json b/core/python/.context/logs/copilot/a7153bd1-bc26-4eec-a2b8-bd310886df10.json new file mode 100644 index 0000000..0067719 --- /dev/null +++ b/core/python/.context/logs/copilot/a7153bd1-bc26-4eec-a2b8-bd310886df10.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "a7153bd1-bc26-4eec-a2b8-bd310886df10", + "timestamp_start": "2026-01-08T17:47:11.864586+00:00", + "timestamp_end": "2026-01-08T17:47:11.864677+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.json b/core/python/.context/logs/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.json new file mode 100644 index 0000000..393c601 --- /dev/null +++ b/core/python/.context/logs/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "aa202034-5114-4f1b-9e38-4362cf9ab474", + "timestamp_start": "2026-01-08T17:46:10.502891+00:00", + "timestamp_end": "2026-01-08T17:46:10.503650+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.json b/core/python/.context/logs/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.json new file mode 100644 index 0000000..3a9ec33 --- /dev/null +++ b/core/python/.context/logs/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "df7e52c4-e2a7-449c-9904-08334bcb4376", + "timestamp_start": "2026-01-08T17:46:41.035295+00:00", + "timestamp_end": "2026-01-08T17:46:41.035952+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.json b/core/python/.context/logs/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.json new file mode 100644 index 0000000..4cd59a8 --- /dev/null +++ b/core/python/.context/logs/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "e6e6c177-877d-4c5e-ae48-731317e8b049", + "timestamp_start": "2026-01-08T17:47:20.343208+00:00", + "timestamp_end": "2026-01-08T17:47:20.343471+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "flag", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.md", + "error": null +} \ No newline at end of file diff --git a/core/python/context/cli.py b/core/python/context/cli.py index 550eda4..6a94ae0 100644 --- a/core/python/context/cli.py +++ b/core/python/context/cli.py @@ -52,6 +52,50 @@ class LLMResponse(BaseModel): cost_usd: float +class CopilotRunLog(BaseModel): + """Structured log for copilot run.""" + prompt_id: str + timestamp_start: datetime + timestamp_end: datetime + user: str + prompt: str + instructions_source: Literal["flag", "file", "default"] + model: str + budget_usd: float + estimated_max_tokens: int + usage: Optional[UsageMetadata] = None + cost_usd: Optional[float] = None + output_path: Optional[str] = None + error: Optional[str] = None + + +def write_log(log: CopilotRunLog, log_dir: pathlib.Path = None) -> pathlib.Path: + """ + Write copilot run log to JSON file. + + Args: + log: CopilotRunLog instance + log_dir: Directory to write logs (defaults to .context/logs/copilot) + + Returns: + Path to log file + """ + if log_dir is None: + log_dir = pathlib.Path.cwd() / ".context" / "logs" / "copilot" + + log_dir.mkdir(parents=True, exist_ok=True) + log_path = log_dir / f"{log.prompt_id}.json" + + # Custom serialization to handle datetime + log_dict = log.model_dump() + log_dict["timestamp_start"] = log.timestamp_start.isoformat() + log_dict["timestamp_end"] = log.timestamp_end.isoformat() + + log_path.write_text(json.dumps(log_dict, indent=2)) + + return log_path + + def parse_prompt_hints(prompt: str) -> dict: """ Extract high-level task hints from natural-language prompt. @@ -340,6 +384,11 @@ def copilot_run( context copilot run --prompt "build me a custom weekend planning tool" --user matthew --budget 0.05 """ timestamp_start = datetime.now(timezone.utc) + config = None + error_msg = None + llm_response = None + output_path = None + max_tokens = 0 try: # Parse and validate configuration @@ -351,6 +400,14 @@ def copilot_run( instructions_file=instructions_file, ) + # Determine instructions source + if instructions is not None: + instructions_source = "flag" + elif instructions_file is not None: + instructions_source = "file" + else: + instructions_source = "default" + # Print configuration typer.echo(f"Configuration: {config.model_dump_json(indent=2)}") typer.echo("") @@ -412,11 +469,38 @@ def copilot_run( typer.echo("-" * 60) except ValueError as e: + error_msg = str(e) typer.echo(f"Error: {e}", err=True) - raise typer.Exit(code=1) except Exception as e: + error_msg = str(e) typer.echo(f"Error: {e}", err=True) - raise typer.Exit(code=1) + finally: + # Always write log, even on failure + timestamp_end = datetime.now(timezone.utc) + + if config is not None: + log = CopilotRunLog( + prompt_id=str(config.prompt_id), + timestamp_start=timestamp_start, + timestamp_end=timestamp_end, + user=config.user, + prompt=config.prompt, + instructions_source=instructions_source, + model=config.model, + budget_usd=config.budget, + estimated_max_tokens=max_tokens, + usage=llm_response.usage if llm_response else None, + cost_usd=llm_response.cost_usd if llm_response else None, + output_path=str(output_path) if output_path else None, + error=error_msg, + ) + + log_path = write_log(log) + typer.echo("") + typer.echo(f"✓ Log written: {log_path}") + + if error_msg: + raise typer.Exit(code=1) def main(): diff --git a/core/python/tests/test_cli.py b/core/python/tests/test_cli.py index d937be2..8f9f8c2 100644 --- a/core/python/tests/test_cli.py +++ b/core/python/tests/test_cli.py @@ -6,6 +6,7 @@ import os import pathlib import tempfile +from datetime import datetime, timezone from unittest.mock import MagicMock, patch, Mock import pytest @@ -434,3 +435,121 @@ def test_generate_dashboard_general(self): content = output_path.read_text() assert "Task: Analysis" in content assert "Analysis complete" in content + + +class TestLogging: + """Tests for structured logging.""" + + def test_copilot_run_log_creation(self): + """Test creating CopilotRunLog.""" + from context.cli import CopilotRunLog + + log = CopilotRunLog( + prompt_id="test-123", + timestamp_start=datetime.now(timezone.utc), + timestamp_end=datetime.now(timezone.utc), + user="matthew", + prompt="test prompt", + instructions_source="flag", + model="gpt-4o-mini", + budget_usd=0.05, + estimated_max_tokens=1000, + usage=UsageMetadata(prompt_tokens=100, completion_tokens=200, total_tokens=300), + cost_usd=0.0001, + output_path="/tmp/test.md", + error=None, + ) + + assert log.prompt_id == "test-123" + assert log.user == "matthew" + assert log.usage.total_tokens == 300 + assert log.error is None + + def test_copilot_run_log_with_error(self): + """Test creating CopilotRunLog with error.""" + from context.cli import CopilotRunLog + + log = CopilotRunLog( + prompt_id="test-123", + timestamp_start=datetime.now(timezone.utc), + timestamp_end=datetime.now(timezone.utc), + user="matthew", + prompt="test prompt", + instructions_source="default", + model="gpt-4o-mini", + budget_usd=0.05, + estimated_max_tokens=1000, + usage=None, + cost_usd=None, + output_path=None, + error="LiteLLM connection error", + ) + + assert log.error == "LiteLLM connection error" + assert log.usage is None + assert log.cost_usd is None + + def test_write_log(self): + """Test writing log to file.""" + from context.cli import CopilotRunLog, write_log + + log = CopilotRunLog( + prompt_id="test-456", + timestamp_start=datetime.now(timezone.utc), + timestamp_end=datetime.now(timezone.utc), + user="matthew", + prompt="test prompt", + instructions_source="file", + model="gpt-4o-mini", + budget_usd=0.05, + estimated_max_tokens=1000, + usage=UsageMetadata(prompt_tokens=100, completion_tokens=200, total_tokens=300), + cost_usd=0.0001, + output_path="/tmp/test.md", + error=None, + ) + + with tempfile.TemporaryDirectory() as tmpdir: + log_dir = pathlib.Path(tmpdir) / "logs" + log_path = write_log(log, log_dir) + + assert log_path.exists() + assert log_path.name == "test-456.json" + + # Verify log content + log_data = json.loads(log_path.read_text()) + assert log_data["prompt_id"] == "test-456" + assert log_data["user"] == "matthew" + assert log_data["usage"]["total_tokens"] == 300 + + @patch('context.cli.call_litellm') + def test_cli_writes_log_on_success(self, mock_call_litellm): + """Test that CLI writes log on successful run.""" + # Mock LiteLLM response + mock_usage = UsageMetadata(prompt_tokens=100, completion_tokens=200, total_tokens=300) + mock_response = LLMResponse(content="Test response", usage=mock_usage, cost_usd=0.0001) + mock_call_litellm.return_value = mock_response + + with patch.dict(os.environ, {"CONTEXT_VIRTUAL_KEY_MATTHEW": "test-key"}): + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + "--user", "matthew", + "--budget", "0.05", + ]) + + assert result.exit_code == 0 + assert "Log written" in result.stdout + + def test_cli_writes_log_on_failure(self): + """Test that CLI writes log on failure.""" + # Missing virtual key should cause failure + result = runner.invoke(app, [ + "copilot", "run", + "--prompt", "test", + "--user", "matthew", + "--budget", "0.05", + ]) + + assert result.exit_code == 1 + assert "Log written" in result.stdout From ddd547189e781f273a15896ed83e92b97e4a8ab0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:48:43 +0000 Subject: [PATCH 06/12] docs: add copilot CLI documentation --- .../06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md | 11 - .../0bbeb524-e146-4121-adad-ecdcdfef4c35.md | 11 - .../11d01c40-ec71-4733-aa10-a6dffa090e2f.md | 11 - .../2319073a-fff4-49f4-a467-7aa99293ab77.md | 11 - .../392d629a-866e-4cf9-96cb-835b86ed9f08.md | 11 - .../5d62f44c-9d44-48bb-95e9-a0f372645a8c.md | 11 - .../92027c0b-a327-4def-b023-3a1345d9ff54.md | 11 - .../aa202034-5114-4f1b-9e38-4362cf9ab474.md | 11 - .../df7e52c4-e2a7-449c-9904-08334bcb4376.md | 11 - .../e6e6c177-877d-4c5e-ae48-731317e8b049.md | 11 - .../06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json | 19 -- .../0bbeb524-e146-4121-adad-ecdcdfef4c35.json | 19 -- .../11d01c40-ec71-4733-aa10-a6dffa090e2f.json | 19 -- .../2319073a-fff4-49f4-a467-7aa99293ab77.json | 19 -- .../2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json | 15 -- .../392d629a-866e-4cf9-96cb-835b86ed9f08.json | 19 -- .../3d0791be-df97-4fa7-843b-a8f8887a23e9.json | 15 -- .../4c86f2f6-cd8e-4310-b83c-3166a06c9324.json | 15 -- .../5d62f44c-9d44-48bb-95e9-a0f372645a8c.json | 19 -- .../7c89bad0-5c4b-4b0d-8379-f483d8809c06.json | 15 -- .../92027c0b-a327-4def-b023-3a1345d9ff54.json | 19 -- .../99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json | 15 -- .../a7153bd1-bc26-4eec-a2b8-bd310886df10.json | 15 -- .../aa202034-5114-4f1b-9e38-4362cf9ab474.json | 19 -- .../df7e52c4-e2a7-449c-9904-08334bcb4376.json | 19 -- .../e6e6c177-877d-4c5e-ae48-731317e8b049.json | 19 -- docs/copilot-cli.md | 205 ++++++++++++++++++ 27 files changed, 205 insertions(+), 390 deletions(-) delete mode 100644 core/python/.context/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md delete mode 100644 core/python/.context/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.md delete mode 100644 core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md delete mode 100644 core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md delete mode 100644 core/python/.context/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.md delete mode 100644 core/python/.context/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.md delete mode 100644 core/python/.context/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.md delete mode 100644 core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md delete mode 100644 core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md delete mode 100644 core/python/.context/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.md delete mode 100644 core/python/.context/logs/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json delete mode 100644 core/python/.context/logs/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.json delete mode 100644 core/python/.context/logs/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.json delete mode 100644 core/python/.context/logs/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.json delete mode 100644 core/python/.context/logs/copilot/2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json delete mode 100644 core/python/.context/logs/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.json delete mode 100644 core/python/.context/logs/copilot/3d0791be-df97-4fa7-843b-a8f8887a23e9.json delete mode 100644 core/python/.context/logs/copilot/4c86f2f6-cd8e-4310-b83c-3166a06c9324.json delete mode 100644 core/python/.context/logs/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.json delete mode 100644 core/python/.context/logs/copilot/7c89bad0-5c4b-4b0d-8379-f483d8809c06.json delete mode 100644 core/python/.context/logs/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.json delete mode 100644 core/python/.context/logs/copilot/99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json delete mode 100644 core/python/.context/logs/copilot/a7153bd1-bc26-4eec-a2b8-bd310886df10.json delete mode 100644 core/python/.context/logs/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.json delete mode 100644 core/python/.context/logs/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.json delete mode 100644 core/python/.context/logs/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.json create mode 100644 docs/copilot-cli.md diff --git a/core/python/.context/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md b/core/python/.context/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.md b/core/python/.context/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.md deleted file mode 100644 index 4a875ad..0000000 --- a/core/python/.context/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.md +++ /dev/null @@ -1,11 +0,0 @@ -# Weekend Planning Tool - -## Request -build me a custom weekend planning tool - -## Activities -Weekend planning suggestions... - -## Notes -- Generated by Context Copilot -- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md b/core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md b/core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.md b/core/python/.context/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.md b/core/python/.context/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.md b/core/python/.context/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.md deleted file mode 100644 index 4a875ad..0000000 --- a/core/python/.context/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.md +++ /dev/null @@ -1,11 +0,0 @@ -# Weekend Planning Tool - -## Request -build me a custom weekend planning tool - -## Activities -Weekend planning suggestions... - -## Notes -- Generated by Context Copilot -- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md b/core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md b/core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.md b/core/python/.context/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/logs/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json b/core/python/.context/logs/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json deleted file mode 100644 index 7ac5dbd..0000000 --- a/core/python/.context/logs/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "06f1adde-6f4a-4398-b27d-ebf7ac519e1f", - "timestamp_start": "2026-01-08T17:47:11.872086+00:00", - "timestamp_end": "2026-01-08T17:47:11.872355+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "flag", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/06f1adde-6f4a-4398-b27d-ebf7ac519e1f.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.json b/core/python/.context/logs/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.json deleted file mode 100644 index 2410355..0000000 --- a/core/python/.context/logs/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "0bbeb524-e146-4121-adad-ecdcdfef4c35", - "timestamp_start": "2026-01-08T17:47:11.861395+00:00", - "timestamp_end": "2026-01-08T17:47:11.861762+00:00", - "user": "matthew", - "prompt": "build me a custom weekend planning tool", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/0bbeb524-e146-4121-adad-ecdcdfef4c35.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.json b/core/python/.context/logs/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.json deleted file mode 100644 index 4a591b3..0000000 --- a/core/python/.context/logs/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "11d01c40-ec71-4733-aa10-a6dffa090e2f", - "timestamp_start": "2026-01-08T17:47:20.358021+00:00", - "timestamp_end": "2026-01-08T17:47:20.358270+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/11d01c40-ec71-4733-aa10-a6dffa090e2f.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.json b/core/python/.context/logs/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.json deleted file mode 100644 index 238f5a4..0000000 --- a/core/python/.context/logs/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "2319073a-fff4-49f4-a467-7aa99293ab77", - "timestamp_start": "2026-01-08T17:47:11.888079+00:00", - "timestamp_end": "2026-01-08T17:47:11.888330+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/2319073a-fff4-49f4-a467-7aa99293ab77.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json b/core/python/.context/logs/copilot/2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json deleted file mode 100644 index ee7b28e..0000000 --- a/core/python/.context/logs/copilot/2a1d30d5-f893-4eb2-83f4-982a5c608bb7.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "2a1d30d5-f893-4eb2-83f4-982a5c608bb7", - "timestamp_start": "2026-01-08T17:47:20.360750+00:00", - "timestamp_end": "2026-01-08T17:47:20.360857+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.json b/core/python/.context/logs/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.json deleted file mode 100644 index 0592ebd..0000000 --- a/core/python/.context/logs/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "392d629a-866e-4cf9-96cb-835b86ed9f08", - "timestamp_start": "2026-01-08T17:47:11.875589+00:00", - "timestamp_end": "2026-01-08T17:47:11.875961+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "file", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/392d629a-866e-4cf9-96cb-835b86ed9f08.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/3d0791be-df97-4fa7-843b-a8f8887a23e9.json b/core/python/.context/logs/copilot/3d0791be-df97-4fa7-843b-a8f8887a23e9.json deleted file mode 100644 index 4b4c9af..0000000 --- a/core/python/.context/logs/copilot/3d0791be-df97-4fa7-843b-a8f8887a23e9.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "3d0791be-df97-4fa7-843b-a8f8887a23e9", - "timestamp_start": "2026-01-08T17:47:20.336174+00:00", - "timestamp_end": "2026-01-08T17:47:20.336255+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/4c86f2f6-cd8e-4310-b83c-3166a06c9324.json b/core/python/.context/logs/copilot/4c86f2f6-cd8e-4310-b83c-3166a06c9324.json deleted file mode 100644 index 6f9176e..0000000 --- a/core/python/.context/logs/copilot/4c86f2f6-cd8e-4310-b83c-3166a06c9324.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "4c86f2f6-cd8e-4310-b83c-3166a06c9324", - "timestamp_start": "2026-01-08T17:46:41.038638+00:00", - "timestamp_end": "2026-01-08T17:46:41.038724+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.json b/core/python/.context/logs/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.json deleted file mode 100644 index 38e880e..0000000 --- a/core/python/.context/logs/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "5d62f44c-9d44-48bb-95e9-a0f372645a8c", - "timestamp_start": "2026-01-08T17:47:20.346539+00:00", - "timestamp_end": "2026-01-08T17:47:20.346901+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "file", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/5d62f44c-9d44-48bb-95e9-a0f372645a8c.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/7c89bad0-5c4b-4b0d-8379-f483d8809c06.json b/core/python/.context/logs/copilot/7c89bad0-5c4b-4b0d-8379-f483d8809c06.json deleted file mode 100644 index de5fc18..0000000 --- a/core/python/.context/logs/copilot/7c89bad0-5c4b-4b0d-8379-f483d8809c06.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "7c89bad0-5c4b-4b0d-8379-f483d8809c06", - "timestamp_start": "2026-01-08T17:46:10.506510+00:00", - "timestamp_end": "2026-01-08T17:46:10.506595+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.json b/core/python/.context/logs/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.json deleted file mode 100644 index 4e4002a..0000000 --- a/core/python/.context/logs/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "92027c0b-a327-4def-b023-3a1345d9ff54", - "timestamp_start": "2026-01-08T17:47:20.333108+00:00", - "timestamp_end": "2026-01-08T17:47:20.333426+00:00", - "user": "matthew", - "prompt": "build me a custom weekend planning tool", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/92027c0b-a327-4def-b023-3a1345d9ff54.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json b/core/python/.context/logs/copilot/99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json deleted file mode 100644 index 32f2495..0000000 --- a/core/python/.context/logs/copilot/99a72c62-13eb-4e0c-bb35-eb8df64f84dc.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "99a72c62-13eb-4e0c-bb35-eb8df64f84dc", - "timestamp_start": "2026-01-08T17:47:11.890893+00:00", - "timestamp_end": "2026-01-08T17:47:11.890969+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/a7153bd1-bc26-4eec-a2b8-bd310886df10.json b/core/python/.context/logs/copilot/a7153bd1-bc26-4eec-a2b8-bd310886df10.json deleted file mode 100644 index 0067719..0000000 --- a/core/python/.context/logs/copilot/a7153bd1-bc26-4eec-a2b8-bd310886df10.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "a7153bd1-bc26-4eec-a2b8-bd310886df10", - "timestamp_start": "2026-01-08T17:47:11.864586+00:00", - "timestamp_end": "2026-01-08T17:47:11.864677+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.json b/core/python/.context/logs/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.json deleted file mode 100644 index 393c601..0000000 --- a/core/python/.context/logs/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "aa202034-5114-4f1b-9e38-4362cf9ab474", - "timestamp_start": "2026-01-08T17:46:10.502891+00:00", - "timestamp_end": "2026-01-08T17:46:10.503650+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/aa202034-5114-4f1b-9e38-4362cf9ab474.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.json b/core/python/.context/logs/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.json deleted file mode 100644 index 3a9ec33..0000000 --- a/core/python/.context/logs/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "df7e52c4-e2a7-449c-9904-08334bcb4376", - "timestamp_start": "2026-01-08T17:46:41.035295+00:00", - "timestamp_end": "2026-01-08T17:46:41.035952+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/df7e52c4-e2a7-449c-9904-08334bcb4376.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.json b/core/python/.context/logs/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.json deleted file mode 100644 index 4cd59a8..0000000 --- a/core/python/.context/logs/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "e6e6c177-877d-4c5e-ae48-731317e8b049", - "timestamp_start": "2026-01-08T17:47:20.343208+00:00", - "timestamp_end": "2026-01-08T17:47:20.343471+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "flag", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/e6e6c177-877d-4c5e-ae48-731317e8b049.md", - "error": null -} \ No newline at end of file diff --git a/docs/copilot-cli.md b/docs/copilot-cli.md new file mode 100644 index 0000000..a096638 --- /dev/null +++ b/docs/copilot-cli.md @@ -0,0 +1,205 @@ +# Copilot CLI + +The Copilot CLI provides a command-line interface for running one-off LLM tasks with the Context runtime. + +## Installation + +```bash +cd core/python +pip install -e . +``` + +## Quick Start + +### Basic Usage + +```bash +context copilot run \ + --prompt "build me a custom weekend planning tool" \ + --user matthew \ + --budget 0.05 +``` + +### With Custom Instructions + +```bash +context copilot run \ + --prompt "analyze this quarterly sales data" \ + --user matthew \ + --budget 0.10 \ + --instructions "Focus on trends and anomalies" +``` + +### With Instructions File + +```bash +context copilot run \ + --prompt "create a project plan" \ + --user matthew \ + --budget 0.05 \ + --instructions-file ./instructions.txt +``` + +## Configuration + +### Environment Variables + +- `LITELLM_PROXY_URL` - LiteLLM proxy endpoint (default: `http://localhost:4000`) +- `CONTEXT_VIRTUAL_KEY_` - User-specific virtual key for LiteLLM authentication (required) +- `COPILOT_MODEL` - Override default model (default: `gpt-4o-mini`) + +### Example Setup + +```bash +export LITELLM_PROXY_URL="http://localhost:4000" +export CONTEXT_VIRTUAL_KEY_MATTHEW="sk-your-key-here" +export COPILOT_MODEL="gpt-4o-mini" # Optional +``` + +## Features + +### Automatic Task Detection + +The CLI automatically detects task types from your prompt: +- **Planner** - Planning, scheduling, agenda tasks +- **Analysis** - Data analysis, examination tasks +- **Generation** - Creation, building, development tasks +- **Summarization** - Summary, overview tasks +- **General** - All other tasks + +### Budget Management + +Specify a USD budget cap for each run. The CLI converts your budget to estimated max tokens with a safety margin: + +```bash +--budget 0.05 # $0.05 budget cap +``` + +### Output Files + +Each run generates two output files: + +1. **Dashboard** - Markdown file with formatted results + - Location: `.context/copilot/{prompt_id}.md` + - Content: Task-specific formatting (e.g., planner layout for planning tasks) + +2. **Log** - JSON file with run metadata + - Location: `.context/logs/copilot/{prompt_id}.json` + - Content: Full run details including usage, cost, and errors + +### Error Handling + +Logs are created even when runs fail, ensuring you have visibility into all executions: + +```json +{ + "prompt_id": "abc-123", + "error": "LiteLLM connection error", + "cost_usd": null, + ... +} +``` + +## Command Reference + +### `context copilot run` + +Execute a one-off copilot run. + +**Required Arguments:** +- `--prompt TEXT` - Natural language prompt describing the task +- `--user TEXT` - Username for this run +- `--budget FLOAT` - USD budget cap (must be > 0) + +**Optional Arguments:** +- `--instructions TEXT` - Custom instructions +- `--instructions-file PATH` - Path to instructions file + +**Note:** You cannot specify both `--instructions` and `--instructions-file`. + +## Examples + +### Weekend Planner + +```bash +context copilot run \ + --prompt "build me a custom weekend planning tool" \ + --user matthew \ + --budget 0.05 +``` + +Output: +- Dashboard: `.context/copilot/{prompt_id}.md` with Activities, Costs, Notes +- Log: `.context/logs/copilot/{prompt_id}.json` + +### Data Analysis + +```bash +context copilot run \ + --prompt "analyze Q3 sales trends and identify anomalies" \ + --user sarah \ + --budget 0.10 \ + --instructions "Focus on regional variations" +``` + +## Pricing + +The CLI uses approximate pricing for token estimation: + +| Model | Input (per 1M tokens) | Output (per 1M tokens) | +|-------|----------------------|------------------------| +| gpt-4o-mini | $0.15 | $0.60 | +| gpt-4o | $2.50 | $10.00 | +| gpt-4 | $30.00 | $60.00 | + +Actual costs are calculated from usage and included in the log file. + +## Troubleshooting + +### "Virtual key not found" + +Ensure you've set the environment variable for your user: + +```bash +export CONTEXT_VIRTUAL_KEY_="sk-your-key-here" +``` + +The username should be uppercase in the environment variable name. + +### "LiteLLM connection error" + +Check that your LiteLLM proxy is running: + +```bash +curl http://localhost:4000/health +``` + +Or set `LITELLM_PROXY_URL` if using a different endpoint. + +### Budget validation error + +Budget must be greater than 0: + +```bash +--budget 0.05 # Valid +--budget 0 # Invalid +``` + +## Development + +### Running Tests + +```bash +cd core/python +python -m pytest tests/test_cli.py -v +``` + +### Test Coverage + +The CLI has comprehensive test coverage including: +- Configuration validation +- Prompt parsing +- Budget calculations +- LiteLLM integration (mocked) +- Dashboard generation +- Logging functionality From 1c84850d6c3db144263e2aab0f76fd00d6eb92b0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:49:08 +0000 Subject: [PATCH 07/12] docs: add copilot CLI documentation Co-authored-by: evcatalyst <8740078+evcatalyst@users.noreply.github.com> --- .../8b3ef418-e96c-4f1c-87dc-656b99433845.md | 11 +++++++++++ .../c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md | 11 +++++++++++ .../f8ccfd0d-f26f-438e-9fe2-1f506d126270.md | 11 +++++++++++ .../fc858d83-c087-49c4-b770-9051e825b6e8.md | 11 +++++++++++ .../47fc936d-b931-4221-a9c1-3a0d095a1e23.json | 15 +++++++++++++++ .../8b3ef418-e96c-4f1c-87dc-656b99433845.json | 19 +++++++++++++++++++ .../b15b4afd-4370-4eb3-8381-60e1af4d6701.json | 15 +++++++++++++++ .../c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json | 19 +++++++++++++++++++ .../f8ccfd0d-f26f-438e-9fe2-1f506d126270.json | 19 +++++++++++++++++++ .../fc858d83-c087-49c4-b770-9051e825b6e8.json | 19 +++++++++++++++++++ 10 files changed, 150 insertions(+) create mode 100644 core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md create mode 100644 core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md create mode 100644 core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md create mode 100644 core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md create mode 100644 core/python/.context/logs/copilot/47fc936d-b931-4221-a9c1-3a0d095a1e23.json create mode 100644 core/python/.context/logs/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.json create mode 100644 core/python/.context/logs/copilot/b15b4afd-4370-4eb3-8381-60e1af4d6701.json create mode 100644 core/python/.context/logs/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json create mode 100644 core/python/.context/logs/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.json create mode 100644 core/python/.context/logs/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.json diff --git a/core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md b/core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md b/core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md b/core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md new file mode 100644 index 0000000..4a875ad --- /dev/null +++ b/core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md @@ -0,0 +1,11 @@ +# Weekend Planning Tool + +## Request +build me a custom weekend planning tool + +## Activities +Weekend planning suggestions... + +## Notes +- Generated by Context Copilot +- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md b/core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/logs/copilot/47fc936d-b931-4221-a9c1-3a0d095a1e23.json b/core/python/.context/logs/copilot/47fc936d-b931-4221-a9c1-3a0d095a1e23.json new file mode 100644 index 0000000..94466af --- /dev/null +++ b/core/python/.context/logs/copilot/47fc936d-b931-4221-a9c1-3a0d095a1e23.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "47fc936d-b931-4221-a9c1-3a0d095a1e23", + "timestamp_start": "2026-01-08T17:48:34.928147+00:00", + "timestamp_end": "2026-01-08T17:48:34.928224+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.json b/core/python/.context/logs/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.json new file mode 100644 index 0000000..715a8cb --- /dev/null +++ b/core/python/.context/logs/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "8b3ef418-e96c-4f1c-87dc-656b99433845", + "timestamp_start": "2026-01-08T17:48:34.925276+00:00", + "timestamp_end": "2026-01-08T17:48:34.925554+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/b15b4afd-4370-4eb3-8381-60e1af4d6701.json b/core/python/.context/logs/copilot/b15b4afd-4370-4eb3-8381-60e1af4d6701.json new file mode 100644 index 0000000..4d2e77b --- /dev/null +++ b/core/python/.context/logs/copilot/b15b4afd-4370-4eb3-8381-60e1af4d6701.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "b15b4afd-4370-4eb3-8381-60e1af4d6701", + "timestamp_start": "2026-01-08T17:48:34.902363+00:00", + "timestamp_end": "2026-01-08T17:48:34.902445+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json b/core/python/.context/logs/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json new file mode 100644 index 0000000..93c9b04 --- /dev/null +++ b/core/python/.context/logs/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "c787e675-3f92-4ceb-b419-f7bbbf9f63fa", + "timestamp_start": "2026-01-08T17:48:34.909602+00:00", + "timestamp_end": "2026-01-08T17:48:34.909896+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "flag", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.json b/core/python/.context/logs/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.json new file mode 100644 index 0000000..a3dea6e --- /dev/null +++ b/core/python/.context/logs/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "f8ccfd0d-f26f-438e-9fe2-1f506d126270", + "timestamp_start": "2026-01-08T17:48:34.898976+00:00", + "timestamp_end": "2026-01-08T17:48:34.899402+00:00", + "user": "matthew", + "prompt": "build me a custom weekend planning tool", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.json b/core/python/.context/logs/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.json new file mode 100644 index 0000000..ae4179f --- /dev/null +++ b/core/python/.context/logs/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "fc858d83-c087-49c4-b770-9051e825b6e8", + "timestamp_start": "2026-01-08T17:48:34.913061+00:00", + "timestamp_end": "2026-01-08T17:48:34.913412+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "file", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md", + "error": null +} \ No newline at end of file From 42d436e8d148a87b1bfa464c22b6671b8ae8cdf6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:49:20 +0000 Subject: [PATCH 08/12] chore: remove test output files --- .../8b3ef418-e96c-4f1c-87dc-656b99433845.md | 11 ----------- .../c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md | 11 ----------- .../f8ccfd0d-f26f-438e-9fe2-1f506d126270.md | 11 ----------- .../fc858d83-c087-49c4-b770-9051e825b6e8.md | 11 ----------- .../47fc936d-b931-4221-a9c1-3a0d095a1e23.json | 15 --------------- .../8b3ef418-e96c-4f1c-87dc-656b99433845.json | 19 ------------------- .../b15b4afd-4370-4eb3-8381-60e1af4d6701.json | 15 --------------- .../c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json | 19 ------------------- .../f8ccfd0d-f26f-438e-9fe2-1f506d126270.json | 19 ------------------- .../fc858d83-c087-49c4-b770-9051e825b6e8.json | 19 ------------------- 10 files changed, 150 deletions(-) delete mode 100644 core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md delete mode 100644 core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md delete mode 100644 core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md delete mode 100644 core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md delete mode 100644 core/python/.context/logs/copilot/47fc936d-b931-4221-a9c1-3a0d095a1e23.json delete mode 100644 core/python/.context/logs/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.json delete mode 100644 core/python/.context/logs/copilot/b15b4afd-4370-4eb3-8381-60e1af4d6701.json delete mode 100644 core/python/.context/logs/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json delete mode 100644 core/python/.context/logs/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.json delete mode 100644 core/python/.context/logs/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.json diff --git a/core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md b/core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md b/core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md b/core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md deleted file mode 100644 index 4a875ad..0000000 --- a/core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md +++ /dev/null @@ -1,11 +0,0 @@ -# Weekend Planning Tool - -## Request -build me a custom weekend planning tool - -## Activities -Weekend planning suggestions... - -## Notes -- Generated by Context Copilot -- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md b/core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/logs/copilot/47fc936d-b931-4221-a9c1-3a0d095a1e23.json b/core/python/.context/logs/copilot/47fc936d-b931-4221-a9c1-3a0d095a1e23.json deleted file mode 100644 index 94466af..0000000 --- a/core/python/.context/logs/copilot/47fc936d-b931-4221-a9c1-3a0d095a1e23.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "47fc936d-b931-4221-a9c1-3a0d095a1e23", - "timestamp_start": "2026-01-08T17:48:34.928147+00:00", - "timestamp_end": "2026-01-08T17:48:34.928224+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.json b/core/python/.context/logs/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.json deleted file mode 100644 index 715a8cb..0000000 --- a/core/python/.context/logs/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "8b3ef418-e96c-4f1c-87dc-656b99433845", - "timestamp_start": "2026-01-08T17:48:34.925276+00:00", - "timestamp_end": "2026-01-08T17:48:34.925554+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/8b3ef418-e96c-4f1c-87dc-656b99433845.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/b15b4afd-4370-4eb3-8381-60e1af4d6701.json b/core/python/.context/logs/copilot/b15b4afd-4370-4eb3-8381-60e1af4d6701.json deleted file mode 100644 index 4d2e77b..0000000 --- a/core/python/.context/logs/copilot/b15b4afd-4370-4eb3-8381-60e1af4d6701.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "b15b4afd-4370-4eb3-8381-60e1af4d6701", - "timestamp_start": "2026-01-08T17:48:34.902363+00:00", - "timestamp_end": "2026-01-08T17:48:34.902445+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json b/core/python/.context/logs/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json deleted file mode 100644 index 93c9b04..0000000 --- a/core/python/.context/logs/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "c787e675-3f92-4ceb-b419-f7bbbf9f63fa", - "timestamp_start": "2026-01-08T17:48:34.909602+00:00", - "timestamp_end": "2026-01-08T17:48:34.909896+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "flag", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/c787e675-3f92-4ceb-b419-f7bbbf9f63fa.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.json b/core/python/.context/logs/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.json deleted file mode 100644 index a3dea6e..0000000 --- a/core/python/.context/logs/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "f8ccfd0d-f26f-438e-9fe2-1f506d126270", - "timestamp_start": "2026-01-08T17:48:34.898976+00:00", - "timestamp_end": "2026-01-08T17:48:34.899402+00:00", - "user": "matthew", - "prompt": "build me a custom weekend planning tool", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/f8ccfd0d-f26f-438e-9fe2-1f506d126270.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.json b/core/python/.context/logs/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.json deleted file mode 100644 index ae4179f..0000000 --- a/core/python/.context/logs/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "fc858d83-c087-49c4-b770-9051e825b6e8", - "timestamp_start": "2026-01-08T17:48:34.913061+00:00", - "timestamp_end": "2026-01-08T17:48:34.913412+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "file", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/fc858d83-c087-49c4-b770-9051e825b6e8.md", - "error": null -} \ No newline at end of file From c59e13a27dd961c78d58ee839e236ab66d4bd109 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:52:17 +0000 Subject: [PATCH 09/12] refactor: address code review feedback Co-authored-by: evcatalyst <8740078+evcatalyst@users.noreply.github.com> --- .../274ce632-7910-4f7d-a022-5cfc8c6a84a4.md | 11 +++++++++++ .../9693e34b-af34-400f-b1a8-e8e0487800ef.md | 11 +++++++++++ .../ad741c01-2c64-400c-86fb-a4500e2db7f5.md | 11 +++++++++++ .../c9b30642-5910-46ae-a4a7-335483681b27.md | 11 +++++++++++ .../274ce632-7910-4f7d-a022-5cfc8c6a84a4.json | 19 +++++++++++++++++++ .../2ac1d86e-7327-482e-b128-9adebf402189.json | 15 +++++++++++++++ .../9693e34b-af34-400f-b1a8-e8e0487800ef.json | 19 +++++++++++++++++++ .../a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json | 15 +++++++++++++++ .../ad741c01-2c64-400c-86fb-a4500e2db7f5.json | 19 +++++++++++++++++++ .../c9b30642-5910-46ae-a4a7-335483681b27.json | 19 +++++++++++++++++++ core/python/context/cli.py | 12 ++++++------ 11 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md create mode 100644 core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md create mode 100644 core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md create mode 100644 core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md create mode 100644 core/python/.context/logs/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.json create mode 100644 core/python/.context/logs/copilot/2ac1d86e-7327-482e-b128-9adebf402189.json create mode 100644 core/python/.context/logs/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.json create mode 100644 core/python/.context/logs/copilot/a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json create mode 100644 core/python/.context/logs/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.json create mode 100644 core/python/.context/logs/copilot/c9b30642-5910-46ae-a4a7-335483681b27.json diff --git a/core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md b/core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md new file mode 100644 index 0000000..4a875ad --- /dev/null +++ b/core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md @@ -0,0 +1,11 @@ +# Weekend Planning Tool + +## Request +build me a custom weekend planning tool + +## Activities +Weekend planning suggestions... + +## Notes +- Generated by Context Copilot +- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md b/core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md b/core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md b/core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/logs/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.json b/core/python/.context/logs/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.json new file mode 100644 index 0000000..908bbe4 --- /dev/null +++ b/core/python/.context/logs/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "274ce632-7910-4f7d-a022-5cfc8c6a84a4", + "timestamp_start": "2026-01-08T17:51:50.291419+00:00", + "timestamp_end": "2026-01-08T17:51:50.291811+00:00", + "user": "matthew", + "prompt": "build me a custom weekend planning tool", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/2ac1d86e-7327-482e-b128-9adebf402189.json b/core/python/.context/logs/copilot/2ac1d86e-7327-482e-b128-9adebf402189.json new file mode 100644 index 0000000..f04b7f6 --- /dev/null +++ b/core/python/.context/logs/copilot/2ac1d86e-7327-482e-b128-9adebf402189.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "2ac1d86e-7327-482e-b128-9adebf402189", + "timestamp_start": "2026-01-08T17:51:50.321777+00:00", + "timestamp_end": "2026-01-08T17:51:50.321897+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.json b/core/python/.context/logs/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.json new file mode 100644 index 0000000..d959bbe --- /dev/null +++ b/core/python/.context/logs/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "9693e34b-af34-400f-b1a8-e8e0487800ef", + "timestamp_start": "2026-01-08T17:51:50.305737+00:00", + "timestamp_end": "2026-01-08T17:51:50.306053+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "file", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json b/core/python/.context/logs/copilot/a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json new file mode 100644 index 0000000..20bcbf9 --- /dev/null +++ b/core/python/.context/logs/copilot/a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3", + "timestamp_start": "2026-01-08T17:51:50.294681+00:00", + "timestamp_end": "2026-01-08T17:51:50.294758+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.json b/core/python/.context/logs/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.json new file mode 100644 index 0000000..22bc990 --- /dev/null +++ b/core/python/.context/logs/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "ad741c01-2c64-400c-86fb-a4500e2db7f5", + "timestamp_start": "2026-01-08T17:51:50.302188+00:00", + "timestamp_end": "2026-01-08T17:51:50.302450+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "flag", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/c9b30642-5910-46ae-a4a7-335483681b27.json b/core/python/.context/logs/copilot/c9b30642-5910-46ae-a4a7-335483681b27.json new file mode 100644 index 0000000..f62ebc4 --- /dev/null +++ b/core/python/.context/logs/copilot/c9b30642-5910-46ae-a4a7-335483681b27.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "c9b30642-5910-46ae-a4a7-335483681b27", + "timestamp_start": "2026-01-08T17:51:50.318761+00:00", + "timestamp_end": "2026-01-08T17:51:50.319054+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md", + "error": null +} \ No newline at end of file diff --git a/core/python/context/cli.py b/core/python/context/cli.py index 6a94ae0..47851b7 100644 --- a/core/python/context/cli.py +++ b/core/python/context/cli.py @@ -348,7 +348,7 @@ def user_instructions(self) -> str: if self.instructions_file is not None: try: return self.instructions_file.read_text() - except Exception as e: + except (FileNotFoundError, PermissionError, UnicodeDecodeError): # If file doesn't exist or can't be read, return empty return "" @@ -389,6 +389,7 @@ def copilot_run( llm_response = None output_path = None max_tokens = 0 + instructions_source = "default" # Initialize before try block try: # Parse and validate configuration @@ -408,8 +409,10 @@ def copilot_run( else: instructions_source = "default" - # Print configuration - typer.echo(f"Configuration: {config.model_dump_json(indent=2)}") + # Print minimal configuration info (avoid sensitive data) + typer.echo(f"Prompt ID: {config.prompt_id}") + typer.echo(f"Model: {config.model}") + typer.echo(f"Budget: ${config.budget}") typer.echo("") # Get LiteLLM proxy URL @@ -468,9 +471,6 @@ def copilot_run( typer.echo(preview) typer.echo("-" * 60) - except ValueError as e: - error_msg = str(e) - typer.echo(f"Error: {e}", err=True) except Exception as e: error_msg = str(e) typer.echo(f"Error: {e}", err=True) From 3b42bb3ff0eccf404e0a8d7d43240a71b2f36821 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 17:53:28 +0000 Subject: [PATCH 10/12] chore: remove test output files and finalize implementation Co-authored-by: evcatalyst <8740078+evcatalyst@users.noreply.github.com> --- .../274ce632-7910-4f7d-a022-5cfc8c6a84a4.md | 11 ----------- .../9693e34b-af34-400f-b1a8-e8e0487800ef.md | 11 ----------- .../ad741c01-2c64-400c-86fb-a4500e2db7f5.md | 11 ----------- .../c9b30642-5910-46ae-a4a7-335483681b27.md | 11 ----------- .../274ce632-7910-4f7d-a022-5cfc8c6a84a4.json | 19 ------------------- .../2ac1d86e-7327-482e-b128-9adebf402189.json | 15 --------------- .../9693e34b-af34-400f-b1a8-e8e0487800ef.json | 19 ------------------- .../a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json | 15 --------------- .../ad741c01-2c64-400c-86fb-a4500e2db7f5.json | 19 ------------------- .../c9b30642-5910-46ae-a4a7-335483681b27.json | 19 ------------------- 10 files changed, 150 deletions(-) delete mode 100644 core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md delete mode 100644 core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md delete mode 100644 core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md delete mode 100644 core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md delete mode 100644 core/python/.context/logs/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.json delete mode 100644 core/python/.context/logs/copilot/2ac1d86e-7327-482e-b128-9adebf402189.json delete mode 100644 core/python/.context/logs/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.json delete mode 100644 core/python/.context/logs/copilot/a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json delete mode 100644 core/python/.context/logs/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.json delete mode 100644 core/python/.context/logs/copilot/c9b30642-5910-46ae-a4a7-335483681b27.json diff --git a/core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md b/core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md deleted file mode 100644 index 4a875ad..0000000 --- a/core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md +++ /dev/null @@ -1,11 +0,0 @@ -# Weekend Planning Tool - -## Request -build me a custom weekend planning tool - -## Activities -Weekend planning suggestions... - -## Notes -- Generated by Context Copilot -- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md b/core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md b/core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md b/core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/logs/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.json b/core/python/.context/logs/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.json deleted file mode 100644 index 908bbe4..0000000 --- a/core/python/.context/logs/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "274ce632-7910-4f7d-a022-5cfc8c6a84a4", - "timestamp_start": "2026-01-08T17:51:50.291419+00:00", - "timestamp_end": "2026-01-08T17:51:50.291811+00:00", - "user": "matthew", - "prompt": "build me a custom weekend planning tool", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/274ce632-7910-4f7d-a022-5cfc8c6a84a4.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/2ac1d86e-7327-482e-b128-9adebf402189.json b/core/python/.context/logs/copilot/2ac1d86e-7327-482e-b128-9adebf402189.json deleted file mode 100644 index f04b7f6..0000000 --- a/core/python/.context/logs/copilot/2ac1d86e-7327-482e-b128-9adebf402189.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "2ac1d86e-7327-482e-b128-9adebf402189", - "timestamp_start": "2026-01-08T17:51:50.321777+00:00", - "timestamp_end": "2026-01-08T17:51:50.321897+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.json b/core/python/.context/logs/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.json deleted file mode 100644 index d959bbe..0000000 --- a/core/python/.context/logs/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "9693e34b-af34-400f-b1a8-e8e0487800ef", - "timestamp_start": "2026-01-08T17:51:50.305737+00:00", - "timestamp_end": "2026-01-08T17:51:50.306053+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "file", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/9693e34b-af34-400f-b1a8-e8e0487800ef.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json b/core/python/.context/logs/copilot/a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json deleted file mode 100644 index 20bcbf9..0000000 --- a/core/python/.context/logs/copilot/a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "a05714da-c29b-4e0a-9bf5-5e1feaf4a9e3", - "timestamp_start": "2026-01-08T17:51:50.294681+00:00", - "timestamp_end": "2026-01-08T17:51:50.294758+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.json b/core/python/.context/logs/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.json deleted file mode 100644 index 22bc990..0000000 --- a/core/python/.context/logs/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "ad741c01-2c64-400c-86fb-a4500e2db7f5", - "timestamp_start": "2026-01-08T17:51:50.302188+00:00", - "timestamp_end": "2026-01-08T17:51:50.302450+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "flag", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/ad741c01-2c64-400c-86fb-a4500e2db7f5.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/c9b30642-5910-46ae-a4a7-335483681b27.json b/core/python/.context/logs/copilot/c9b30642-5910-46ae-a4a7-335483681b27.json deleted file mode 100644 index f62ebc4..0000000 --- a/core/python/.context/logs/copilot/c9b30642-5910-46ae-a4a7-335483681b27.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "c9b30642-5910-46ae-a4a7-335483681b27", - "timestamp_start": "2026-01-08T17:51:50.318761+00:00", - "timestamp_end": "2026-01-08T17:51:50.319054+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/c9b30642-5910-46ae-a4a7-335483681b27.md", - "error": null -} \ No newline at end of file From 33aa799a23dfbe632da6f0b9e2c672f849ae8f00 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 19:26:04 +0000 Subject: [PATCH 11/12] fix: address code review feedback - improve error handling, British spellings, and edge cases Co-authored-by: evcatalyst <8740078+evcatalyst@users.noreply.github.com> --- .../0b4f4340-9d68-44e5-92ff-f321464e2093.md | 11 +++ .../115c94e4-dc59-4234-9c12-98511573b24e.md | 11 +++ .../1ae5c996-b364-437c-aa83-5d6934d24185.md | 11 +++ .../1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md | 11 +++ .../35c201cb-cc05-4a64-b87c-157517789bc3.md | 11 +++ .../41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md | 11 +++ .../4c92b654-d46a-4da5-9f10-47d77defaa46.md | 11 +++ .../64f60be0-106d-4892-930d-6a8d0f6abb53.md | 11 +++ .../704928d4-979b-455e-83cf-ebeb0f6c5280.md | 11 +++ .../791d55d5-3627-4b59-900c-66be008fa8d4.md | 11 +++ .../9e5acfe1-0825-49cf-a705-c166a8bf0001.md | 11 +++ .../a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md | 11 +++ .../0b4f4340-9d68-44e5-92ff-f321464e2093.json | 19 +++++ .../115c94e4-dc59-4234-9c12-98511573b24e.json | 19 +++++ .../1ae5c996-b364-437c-aa83-5d6934d24185.json | 19 +++++ .../1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json | 19 +++++ .../2e916d85-16ab-4795-9829-10a95a712149.json | 15 ++++ .../35c201cb-cc05-4a64-b87c-157517789bc3.json | 19 +++++ .../41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json | 19 +++++ .../4c92b654-d46a-4da5-9f10-47d77defaa46.json | 19 +++++ .../64f60be0-106d-4892-930d-6a8d0f6abb53.json | 19 +++++ .../704928d4-979b-455e-83cf-ebeb0f6c5280.json | 19 +++++ .../791d55d5-3627-4b59-900c-66be008fa8d4.json | 19 +++++ .../887552be-ec85-4fd6-895e-b2f9c4ca9aec.json | 15 ++++ .../90aeb93e-5476-42fc-b639-cee6acafc459.json | 15 ++++ .../9e5acfe1-0825-49cf-a705-c166a8bf0001.json | 19 +++++ .../a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json | 19 +++++ .../e70a6e22-8148-403a-af63-a99b82cb979a.json | 15 ++++ .../fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json | 15 ++++ .../fe531481-29db-4836-9c2b-e378c72518d7.json | 15 ++++ core/python/context/cli.py | 40 ++++++---- core/python/tests/test_cli.py | 75 ++++++++++++++++++- 32 files changed, 549 insertions(+), 16 deletions(-) create mode 100644 core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md create mode 100644 core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md create mode 100644 core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md create mode 100644 core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md create mode 100644 core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md create mode 100644 core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md create mode 100644 core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md create mode 100644 core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md create mode 100644 core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md create mode 100644 core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md create mode 100644 core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md create mode 100644 core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md create mode 100644 core/python/.context/logs/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.json create mode 100644 core/python/.context/logs/copilot/115c94e4-dc59-4234-9c12-98511573b24e.json create mode 100644 core/python/.context/logs/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.json create mode 100644 core/python/.context/logs/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json create mode 100644 core/python/.context/logs/copilot/2e916d85-16ab-4795-9829-10a95a712149.json create mode 100644 core/python/.context/logs/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.json create mode 100644 core/python/.context/logs/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json create mode 100644 core/python/.context/logs/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.json create mode 100644 core/python/.context/logs/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.json create mode 100644 core/python/.context/logs/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.json create mode 100644 core/python/.context/logs/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.json create mode 100644 core/python/.context/logs/copilot/887552be-ec85-4fd6-895e-b2f9c4ca9aec.json create mode 100644 core/python/.context/logs/copilot/90aeb93e-5476-42fc-b639-cee6acafc459.json create mode 100644 core/python/.context/logs/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.json create mode 100644 core/python/.context/logs/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json create mode 100644 core/python/.context/logs/copilot/e70a6e22-8148-403a-af63-a99b82cb979a.json create mode 100644 core/python/.context/logs/copilot/fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json create mode 100644 core/python/.context/logs/copilot/fe531481-29db-4836-9c2b-e378c72518d7.json diff --git a/core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md b/core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md b/core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md new file mode 100644 index 0000000..4bdd489 --- /dev/null +++ b/core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md @@ -0,0 +1,11 @@ +# Planning Tool + +## Request +build me a custom weekend planning tool + +## Plan +Weekend planning suggestions... + +## Notes +- Generated by Context Copilot +- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md b/core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md b/core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md b/core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md new file mode 100644 index 0000000..4a875ad --- /dev/null +++ b/core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md @@ -0,0 +1,11 @@ +# Weekend Planning Tool + +## Request +build me a custom weekend planning tool + +## Activities +Weekend planning suggestions... + +## Notes +- Generated by Context Copilot +- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md b/core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md new file mode 100644 index 0000000..4bdd489 --- /dev/null +++ b/core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md @@ -0,0 +1,11 @@ +# Planning Tool + +## Request +build me a custom weekend planning tool + +## Plan +Weekend planning suggestions... + +## Notes +- Generated by Context Copilot +- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md b/core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md b/core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md b/core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md b/core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md b/core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md new file mode 100644 index 0000000..1175ed2 --- /dev/null +++ b/core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md b/core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md new file mode 100644 index 0000000..979c503 --- /dev/null +++ b/core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md @@ -0,0 +1,11 @@ +# Task: General + +## Request +test + +## Response +Test response + +## Metadata +- Task Type: general +- Generated by Context Copilot diff --git a/core/python/.context/logs/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.json b/core/python/.context/logs/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.json new file mode 100644 index 0000000..cd245a0 --- /dev/null +++ b/core/python/.context/logs/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "0b4f4340-9d68-44e5-92ff-f321464e2093", + "timestamp_start": "2026-01-08T19:22:55.249121+00:00", + "timestamp_end": "2026-01-08T19:22:55.249402+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "flag", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/115c94e4-dc59-4234-9c12-98511573b24e.json b/core/python/.context/logs/copilot/115c94e4-dc59-4234-9c12-98511573b24e.json new file mode 100644 index 0000000..f5b6bb9 --- /dev/null +++ b/core/python/.context/logs/copilot/115c94e4-dc59-4234-9c12-98511573b24e.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "115c94e4-dc59-4234-9c12-98511573b24e", + "timestamp_start": "2026-01-08T19:25:46.081069+00:00", + "timestamp_end": "2026-01-08T19:25:46.081429+00:00", + "user": "matthew", + "prompt": "build me a custom weekend planning tool", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.json b/core/python/.context/logs/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.json new file mode 100644 index 0000000..9638573 --- /dev/null +++ b/core/python/.context/logs/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "1ae5c996-b364-437c-aa83-5d6934d24185", + "timestamp_start": "2026-01-08T19:25:46.094479+00:00", + "timestamp_end": "2026-01-08T19:25:46.094784+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "file", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json b/core/python/.context/logs/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json new file mode 100644 index 0000000..3735215 --- /dev/null +++ b/core/python/.context/logs/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2", + "timestamp_start": "2026-01-08T19:25:22.591973+00:00", + "timestamp_end": "2026-01-08T19:25:22.592278+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "flag", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/2e916d85-16ab-4795-9829-10a95a712149.json b/core/python/.context/logs/copilot/2e916d85-16ab-4795-9829-10a95a712149.json new file mode 100644 index 0000000..e9ca94d --- /dev/null +++ b/core/python/.context/logs/copilot/2e916d85-16ab-4795-9829-10a95a712149.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "2e916d85-16ab-4795-9829-10a95a712149", + "timestamp_start": "2026-01-08T19:25:46.107291+00:00", + "timestamp_end": "2026-01-08T19:25:46.107383+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.json b/core/python/.context/logs/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.json new file mode 100644 index 0000000..3647b27 --- /dev/null +++ b/core/python/.context/logs/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "35c201cb-cc05-4a64-b87c-157517789bc3", + "timestamp_start": "2026-01-08T19:22:55.239515+00:00", + "timestamp_end": "2026-01-08T19:22:55.239930+00:00", + "user": "matthew", + "prompt": "build me a custom weekend planning tool", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json b/core/python/.context/logs/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json new file mode 100644 index 0000000..2de7dd1 --- /dev/null +++ b/core/python/.context/logs/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54", + "timestamp_start": "2026-01-08T19:25:22.581984+00:00", + "timestamp_end": "2026-01-08T19:25:22.582342+00:00", + "user": "matthew", + "prompt": "build me a custom weekend planning tool", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.json b/core/python/.context/logs/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.json new file mode 100644 index 0000000..6f9a3db --- /dev/null +++ b/core/python/.context/logs/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "4c92b654-d46a-4da5-9f10-47d77defaa46", + "timestamp_start": "2026-01-08T19:25:46.104614+00:00", + "timestamp_end": "2026-01-08T19:25:46.104907+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.json b/core/python/.context/logs/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.json new file mode 100644 index 0000000..1272690 --- /dev/null +++ b/core/python/.context/logs/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "64f60be0-106d-4892-930d-6a8d0f6abb53", + "timestamp_start": "2026-01-08T19:25:22.595327+00:00", + "timestamp_end": "2026-01-08T19:25:22.595624+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "file", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.json b/core/python/.context/logs/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.json new file mode 100644 index 0000000..493418b --- /dev/null +++ b/core/python/.context/logs/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "704928d4-979b-455e-83cf-ebeb0f6c5280", + "timestamp_start": "2026-01-08T19:22:55.262135+00:00", + "timestamp_end": "2026-01-08T19:22:55.262397+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.json b/core/python/.context/logs/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.json new file mode 100644 index 0000000..4535dde --- /dev/null +++ b/core/python/.context/logs/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "791d55d5-3627-4b59-900c-66be008fa8d4", + "timestamp_start": "2026-01-08T19:25:46.091152+00:00", + "timestamp_end": "2026-01-08T19:25:46.091444+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "flag", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/887552be-ec85-4fd6-895e-b2f9c4ca9aec.json b/core/python/.context/logs/copilot/887552be-ec85-4fd6-895e-b2f9c4ca9aec.json new file mode 100644 index 0000000..e18c568 --- /dev/null +++ b/core/python/.context/logs/copilot/887552be-ec85-4fd6-895e-b2f9c4ca9aec.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "887552be-ec85-4fd6-895e-b2f9c4ca9aec", + "timestamp_start": "2026-01-08T19:25:46.084088+00:00", + "timestamp_end": "2026-01-08T19:25:46.084183+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/90aeb93e-5476-42fc-b639-cee6acafc459.json b/core/python/.context/logs/copilot/90aeb93e-5476-42fc-b639-cee6acafc459.json new file mode 100644 index 0000000..321cfbc --- /dev/null +++ b/core/python/.context/logs/copilot/90aeb93e-5476-42fc-b639-cee6acafc459.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "90aeb93e-5476-42fc-b639-cee6acafc459", + "timestamp_start": "2026-01-08T19:25:22.585048+00:00", + "timestamp_end": "2026-01-08T19:25:22.585145+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.json b/core/python/.context/logs/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.json new file mode 100644 index 0000000..0b3aaed --- /dev/null +++ b/core/python/.context/logs/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "9e5acfe1-0825-49cf-a705-c166a8bf0001", + "timestamp_start": "2026-01-08T19:22:55.252334+00:00", + "timestamp_end": "2026-01-08T19:22:55.252613+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "file", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 50, + "completion_tokens": 100, + "total_tokens": 150 + }, + "cost_usd": 5e-05, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json b/core/python/.context/logs/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json new file mode 100644 index 0000000..19443f2 --- /dev/null +++ b/core/python/.context/logs/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json @@ -0,0 +1,19 @@ +{ + "prompt_id": "a8b06959-7916-4d32-ac2b-77ed27c7e7c6", + "timestamp_start": "2026-01-08T19:25:22.626485+00:00", + "timestamp_end": "2026-01-08T19:25:22.626794+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 86021, + "usage": { + "prompt_tokens": 100, + "completion_tokens": 200, + "total_tokens": 300 + }, + "cost_usd": 0.0001, + "output_path": "/home/runner/work/context/context/core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md", + "error": null +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/e70a6e22-8148-403a-af63-a99b82cb979a.json b/core/python/.context/logs/copilot/e70a6e22-8148-403a-af63-a99b82cb979a.json new file mode 100644 index 0000000..86da2ea --- /dev/null +++ b/core/python/.context/logs/copilot/e70a6e22-8148-403a-af63-a99b82cb979a.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "e70a6e22-8148-403a-af63-a99b82cb979a", + "timestamp_start": "2026-01-08T19:22:55.242491+00:00", + "timestamp_end": "2026-01-08T19:22:55.242582+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json b/core/python/.context/logs/copilot/fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json new file mode 100644 index 0000000..566e174 --- /dev/null +++ b/core/python/.context/logs/copilot/fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "fb1d9be3-ad51-40b7-a873-b7bfa94f21c2", + "timestamp_start": "2026-01-08T19:25:22.629233+00:00", + "timestamp_end": "2026-01-08T19:25:22.629327+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/fe531481-29db-4836-9c2b-e378c72518d7.json b/core/python/.context/logs/copilot/fe531481-29db-4836-9c2b-e378c72518d7.json new file mode 100644 index 0000000..ca9223e --- /dev/null +++ b/core/python/.context/logs/copilot/fe531481-29db-4836-9c2b-e378c72518d7.json @@ -0,0 +1,15 @@ +{ + "prompt_id": "fe531481-29db-4836-9c2b-e378c72518d7", + "timestamp_start": "2026-01-08T19:22:55.264720+00:00", + "timestamp_end": "2026-01-08T19:22:55.264804+00:00", + "user": "matthew", + "prompt": "test", + "instructions_source": "default", + "model": "gpt-4o-mini", + "budget_usd": 0.05, + "estimated_max_tokens": 0, + "usage": null, + "cost_usd": null, + "output_path": null, + "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." +} \ No newline at end of file diff --git a/core/python/context/cli.py b/core/python/context/cli.py index 47851b7..6ef75a0 100644 --- a/core/python/context/cli.py +++ b/core/python/context/cli.py @@ -114,8 +114,8 @@ def parse_prompt_hints(prompt: str) -> dict: hints["task_type"] = "planner" hints["keywords"].append("planning") - # Detect analysis requests - elif re.search(r'\b(analyz[e]?|analysis|examine|inspect|investigate)\b', prompt_lower): + # Detect analysis requests (supports both American and British spellings) + elif re.search(r'\b(analy[sz]e|analysis|examine|inspect|investigate)\b', prompt_lower): hints["task_type"] = "analysis" hints["keywords"].append("analysis") @@ -124,8 +124,8 @@ def parse_prompt_hints(prompt: str) -> dict: hints["task_type"] = "generation" hints["keywords"].append("generation") - # Detect summarization requests - elif re.search(r'\b(summariz[e]?|summary|brief|overview)\b', prompt_lower): + # Detect summarization requests (supports both American and British spellings) + elif re.search(r'\b(summari[sz]e|summary|brief|overview)\b', prompt_lower): hints["task_type"] = "summarization" hints["keywords"].append("summarization") @@ -147,6 +147,10 @@ def budget_to_max_tokens(budget_usd: float, model: str = "gpt-4o-mini") -> int: # Calculate max tokens with 20% safety margin max_tokens = int((budget_usd * 0.8) / avg_price_per_token) + # Ensure at least 1 token + if max_tokens < 1: + max_tokens = 1 + return max_tokens @@ -214,8 +218,15 @@ def call_litellm( headers["Authorization"] = f"Bearer {virtual_key}" # Make HTTP POST to LiteLLM proxy + # Allow configurable timeout for long-running LLM requests + timeout_env = os.getenv("CONTEXT_HTTP_TIMEOUT", "180") + try: + timeout_seconds = float(timeout_env) + except ValueError: + timeout_seconds = 180.0 + try: - with httpx.Client(timeout=60.0) as client: + with httpx.Client(timeout=timeout_seconds) as client: response = client.post( f"{proxy_url}/chat/completions", json=payload, @@ -239,11 +250,11 @@ def call_litellm( return LLMResponse(content=content, usage=usage, cost_usd=cost_usd) except httpx.HTTPStatusError as e: - raise Exception(f"LiteLLM API error: {e.response.status_code} - {e.response.text}") + raise Exception(f"LiteLLM API error: {e.response.status_code} - {e.response.text}") from e except httpx.RequestError as e: - raise Exception(f"LiteLLM connection error: {e}") + raise Exception(f"LiteLLM connection error: {e}") from e except (KeyError, IndexError) as e: - raise Exception(f"Invalid LiteLLM response format: {e}") + raise Exception(f"Invalid LiteLLM response format: {e}") from e def generate_dashboard( @@ -269,12 +280,12 @@ def generate_dashboard( # Create structured markdown based on task type if task_type == "planner": # Extract sections for planner - markdown = f"""# Weekend Planning Tool + markdown = f"""# Planning Tool ## Request {prompt} -## Activities +## Plan {content} ## Notes @@ -348,9 +359,12 @@ def user_instructions(self) -> str: if self.instructions_file is not None: try: return self.instructions_file.read_text() - except (FileNotFoundError, PermissionError, UnicodeDecodeError): - # If file doesn't exist or can't be read, return empty - return "" + except FileNotFoundError: + raise ValueError(f"Instructions file not found: {self.instructions_file}") + except PermissionError: + raise ValueError(f"Permission denied reading instructions file: {self.instructions_file}") + except UnicodeDecodeError: + raise ValueError(f"Invalid encoding in instructions file: {self.instructions_file}") return "" diff --git a/core/python/tests/test_cli.py b/core/python/tests/test_cli.py index 8f9f8c2..62b34ec 100644 --- a/core/python/tests/test_cli.py +++ b/core/python/tests/test_cli.py @@ -7,7 +7,7 @@ import pathlib import tempfile from datetime import datetime, timezone -from unittest.mock import MagicMock, patch, Mock +from unittest.mock import patch import pytest from typer.testing import CliRunner @@ -403,8 +403,8 @@ def test_generate_dashboard_planner(self): assert output_path.exists() content = output_path.read_text() - assert "Weekend Planning Tool" in content - assert "Activities" in content + assert "Planning Tool" in content + assert "Plan" in content assert "Hiking" in content def test_generate_dashboard_general(self): @@ -553,3 +553,72 @@ def test_cli_writes_log_on_failure(self): assert result.exit_code == 1 assert "Log written" in result.stdout + + +class TestPromptParsingBritishSpellings: + """Tests for British spelling support in prompt parsing.""" + + def test_parse_analyse_british(self): + """Test detection of British spelling 'analyse'.""" + hints = parse_prompt_hints("analyse this dataset") + assert hints["task_type"] == "analysis" + assert "analysis" in hints["keywords"] + + def test_parse_summarise_british(self): + """Test detection of British spelling 'summarise'.""" + hints = parse_prompt_hints("summarise this document") + assert hints["task_type"] == "summarization" + assert "summarization" in hints["keywords"] + + +class TestBudgetEdgeCases: + """Tests for edge cases in budget calculations.""" + + def test_very_small_budget_returns_minimum_tokens(self): + """Test that very small budget returns at least 1 token.""" + max_tokens = budget_to_max_tokens(0.0001, "gpt-4o-mini") + assert max_tokens >= 1 + + def test_zero_budget_returns_minimum_tokens(self): + """Test that zero budget returns at least 1 token.""" + max_tokens = budget_to_max_tokens(0.0, "gpt-4o-mini") + assert max_tokens >= 1 + + def test_unknown_model_uses_fallback_pricing(self): + """Test that unknown model falls back to gpt-4o-mini pricing.""" + max_tokens_unknown = budget_to_max_tokens(0.05, "unknown-model") + max_tokens_default = budget_to_max_tokens(0.05, "gpt-4o-mini") + assert max_tokens_unknown == max_tokens_default + + +class TestInstructionsFileErrorHandling: + """Tests for instructions file error handling.""" + + def test_missing_instructions_file_raises_error(self): + """Test that missing instructions file raises ValueError.""" + with pytest.raises(ValueError, match="Instructions file not found"): + config = CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + instructions_file=pathlib.Path("/nonexistent/file.txt"), + ) + # Access the property to trigger file read + _ = config.user_instructions + + def test_valid_instructions_file_works(self): + """Test that valid instructions file is read correctly.""" + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f: + f.write("test instructions content") + temp_path = pathlib.Path(f.name) + + try: + config = CopilotRunConfig( + prompt="test", + user="matthew", + budget=0.05, + instructions_file=temp_path, + ) + assert config.user_instructions == "test instructions content" + finally: + temp_path.unlink() From 014a138d2b0fe8ea42f1cf8180799deeb0f12fc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 19:26:47 +0000 Subject: [PATCH 12/12] chore: remove test output files --- .../0b4f4340-9d68-44e5-92ff-f321464e2093.md | 11 ----------- .../115c94e4-dc59-4234-9c12-98511573b24e.md | 11 ----------- .../1ae5c996-b364-437c-aa83-5d6934d24185.md | 11 ----------- .../1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md | 11 ----------- .../35c201cb-cc05-4a64-b87c-157517789bc3.md | 11 ----------- .../41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md | 11 ----------- .../4c92b654-d46a-4da5-9f10-47d77defaa46.md | 11 ----------- .../64f60be0-106d-4892-930d-6a8d0f6abb53.md | 11 ----------- .../704928d4-979b-455e-83cf-ebeb0f6c5280.md | 11 ----------- .../791d55d5-3627-4b59-900c-66be008fa8d4.md | 11 ----------- .../9e5acfe1-0825-49cf-a705-c166a8bf0001.md | 11 ----------- .../a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md | 11 ----------- .../0b4f4340-9d68-44e5-92ff-f321464e2093.json | 19 ------------------- .../115c94e4-dc59-4234-9c12-98511573b24e.json | 19 ------------------- .../1ae5c996-b364-437c-aa83-5d6934d24185.json | 19 ------------------- .../1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json | 19 ------------------- .../2e916d85-16ab-4795-9829-10a95a712149.json | 15 --------------- .../35c201cb-cc05-4a64-b87c-157517789bc3.json | 19 ------------------- .../41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json | 19 ------------------- .../4c92b654-d46a-4da5-9f10-47d77defaa46.json | 19 ------------------- .../64f60be0-106d-4892-930d-6a8d0f6abb53.json | 19 ------------------- .../704928d4-979b-455e-83cf-ebeb0f6c5280.json | 19 ------------------- .../791d55d5-3627-4b59-900c-66be008fa8d4.json | 19 ------------------- .../887552be-ec85-4fd6-895e-b2f9c4ca9aec.json | 15 --------------- .../90aeb93e-5476-42fc-b639-cee6acafc459.json | 15 --------------- .../9e5acfe1-0825-49cf-a705-c166a8bf0001.json | 19 ------------------- .../a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json | 19 ------------------- .../e70a6e22-8148-403a-af63-a99b82cb979a.json | 15 --------------- .../fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json | 15 --------------- .../fe531481-29db-4836-9c2b-e378c72518d7.json | 15 --------------- 30 files changed, 450 deletions(-) delete mode 100644 core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md delete mode 100644 core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md delete mode 100644 core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md delete mode 100644 core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md delete mode 100644 core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md delete mode 100644 core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md delete mode 100644 core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md delete mode 100644 core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md delete mode 100644 core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md delete mode 100644 core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md delete mode 100644 core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md delete mode 100644 core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md delete mode 100644 core/python/.context/logs/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.json delete mode 100644 core/python/.context/logs/copilot/115c94e4-dc59-4234-9c12-98511573b24e.json delete mode 100644 core/python/.context/logs/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.json delete mode 100644 core/python/.context/logs/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json delete mode 100644 core/python/.context/logs/copilot/2e916d85-16ab-4795-9829-10a95a712149.json delete mode 100644 core/python/.context/logs/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.json delete mode 100644 core/python/.context/logs/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json delete mode 100644 core/python/.context/logs/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.json delete mode 100644 core/python/.context/logs/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.json delete mode 100644 core/python/.context/logs/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.json delete mode 100644 core/python/.context/logs/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.json delete mode 100644 core/python/.context/logs/copilot/887552be-ec85-4fd6-895e-b2f9c4ca9aec.json delete mode 100644 core/python/.context/logs/copilot/90aeb93e-5476-42fc-b639-cee6acafc459.json delete mode 100644 core/python/.context/logs/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.json delete mode 100644 core/python/.context/logs/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json delete mode 100644 core/python/.context/logs/copilot/e70a6e22-8148-403a-af63-a99b82cb979a.json delete mode 100644 core/python/.context/logs/copilot/fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json delete mode 100644 core/python/.context/logs/copilot/fe531481-29db-4836-9c2b-e378c72518d7.json diff --git a/core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md b/core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md b/core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md deleted file mode 100644 index 4bdd489..0000000 --- a/core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md +++ /dev/null @@ -1,11 +0,0 @@ -# Planning Tool - -## Request -build me a custom weekend planning tool - -## Plan -Weekend planning suggestions... - -## Notes -- Generated by Context Copilot -- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md b/core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md b/core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md b/core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md deleted file mode 100644 index 4a875ad..0000000 --- a/core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md +++ /dev/null @@ -1,11 +0,0 @@ -# Weekend Planning Tool - -## Request -build me a custom weekend planning tool - -## Activities -Weekend planning suggestions... - -## Notes -- Generated by Context Copilot -- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md b/core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md deleted file mode 100644 index 4bdd489..0000000 --- a/core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md +++ /dev/null @@ -1,11 +0,0 @@ -# Planning Tool - -## Request -build me a custom weekend planning tool - -## Plan -Weekend planning suggestions... - -## Notes -- Generated by Context Copilot -- Budget estimate based on LLM usage diff --git a/core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md b/core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md b/core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md b/core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md b/core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md b/core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md deleted file mode 100644 index 1175ed2..0000000 --- a/core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md b/core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md deleted file mode 100644 index 979c503..0000000 --- a/core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md +++ /dev/null @@ -1,11 +0,0 @@ -# Task: General - -## Request -test - -## Response -Test response - -## Metadata -- Task Type: general -- Generated by Context Copilot diff --git a/core/python/.context/logs/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.json b/core/python/.context/logs/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.json deleted file mode 100644 index cd245a0..0000000 --- a/core/python/.context/logs/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "0b4f4340-9d68-44e5-92ff-f321464e2093", - "timestamp_start": "2026-01-08T19:22:55.249121+00:00", - "timestamp_end": "2026-01-08T19:22:55.249402+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "flag", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/0b4f4340-9d68-44e5-92ff-f321464e2093.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/115c94e4-dc59-4234-9c12-98511573b24e.json b/core/python/.context/logs/copilot/115c94e4-dc59-4234-9c12-98511573b24e.json deleted file mode 100644 index f5b6bb9..0000000 --- a/core/python/.context/logs/copilot/115c94e4-dc59-4234-9c12-98511573b24e.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "115c94e4-dc59-4234-9c12-98511573b24e", - "timestamp_start": "2026-01-08T19:25:46.081069+00:00", - "timestamp_end": "2026-01-08T19:25:46.081429+00:00", - "user": "matthew", - "prompt": "build me a custom weekend planning tool", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/115c94e4-dc59-4234-9c12-98511573b24e.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.json b/core/python/.context/logs/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.json deleted file mode 100644 index 9638573..0000000 --- a/core/python/.context/logs/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "1ae5c996-b364-437c-aa83-5d6934d24185", - "timestamp_start": "2026-01-08T19:25:46.094479+00:00", - "timestamp_end": "2026-01-08T19:25:46.094784+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "file", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/1ae5c996-b364-437c-aa83-5d6934d24185.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json b/core/python/.context/logs/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json deleted file mode 100644 index 3735215..0000000 --- a/core/python/.context/logs/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2", - "timestamp_start": "2026-01-08T19:25:22.591973+00:00", - "timestamp_end": "2026-01-08T19:25:22.592278+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "flag", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/1cc4cd71-96c9-41d5-9eba-8600a9bfd7a2.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/2e916d85-16ab-4795-9829-10a95a712149.json b/core/python/.context/logs/copilot/2e916d85-16ab-4795-9829-10a95a712149.json deleted file mode 100644 index e9ca94d..0000000 --- a/core/python/.context/logs/copilot/2e916d85-16ab-4795-9829-10a95a712149.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "2e916d85-16ab-4795-9829-10a95a712149", - "timestamp_start": "2026-01-08T19:25:46.107291+00:00", - "timestamp_end": "2026-01-08T19:25:46.107383+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.json b/core/python/.context/logs/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.json deleted file mode 100644 index 3647b27..0000000 --- a/core/python/.context/logs/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "35c201cb-cc05-4a64-b87c-157517789bc3", - "timestamp_start": "2026-01-08T19:22:55.239515+00:00", - "timestamp_end": "2026-01-08T19:22:55.239930+00:00", - "user": "matthew", - "prompt": "build me a custom weekend planning tool", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/35c201cb-cc05-4a64-b87c-157517789bc3.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json b/core/python/.context/logs/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json deleted file mode 100644 index 2de7dd1..0000000 --- a/core/python/.context/logs/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54", - "timestamp_start": "2026-01-08T19:25:22.581984+00:00", - "timestamp_end": "2026-01-08T19:25:22.582342+00:00", - "user": "matthew", - "prompt": "build me a custom weekend planning tool", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/41ded8ce-b9a4-4ba5-8b61-c1f5bf654d54.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.json b/core/python/.context/logs/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.json deleted file mode 100644 index 6f9a3db..0000000 --- a/core/python/.context/logs/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "4c92b654-d46a-4da5-9f10-47d77defaa46", - "timestamp_start": "2026-01-08T19:25:46.104614+00:00", - "timestamp_end": "2026-01-08T19:25:46.104907+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/4c92b654-d46a-4da5-9f10-47d77defaa46.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.json b/core/python/.context/logs/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.json deleted file mode 100644 index 1272690..0000000 --- a/core/python/.context/logs/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "64f60be0-106d-4892-930d-6a8d0f6abb53", - "timestamp_start": "2026-01-08T19:25:22.595327+00:00", - "timestamp_end": "2026-01-08T19:25:22.595624+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "file", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/64f60be0-106d-4892-930d-6a8d0f6abb53.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.json b/core/python/.context/logs/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.json deleted file mode 100644 index 493418b..0000000 --- a/core/python/.context/logs/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "704928d4-979b-455e-83cf-ebeb0f6c5280", - "timestamp_start": "2026-01-08T19:22:55.262135+00:00", - "timestamp_end": "2026-01-08T19:22:55.262397+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/704928d4-979b-455e-83cf-ebeb0f6c5280.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.json b/core/python/.context/logs/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.json deleted file mode 100644 index 4535dde..0000000 --- a/core/python/.context/logs/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "791d55d5-3627-4b59-900c-66be008fa8d4", - "timestamp_start": "2026-01-08T19:25:46.091152+00:00", - "timestamp_end": "2026-01-08T19:25:46.091444+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "flag", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/791d55d5-3627-4b59-900c-66be008fa8d4.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/887552be-ec85-4fd6-895e-b2f9c4ca9aec.json b/core/python/.context/logs/copilot/887552be-ec85-4fd6-895e-b2f9c4ca9aec.json deleted file mode 100644 index e18c568..0000000 --- a/core/python/.context/logs/copilot/887552be-ec85-4fd6-895e-b2f9c4ca9aec.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "887552be-ec85-4fd6-895e-b2f9c4ca9aec", - "timestamp_start": "2026-01-08T19:25:46.084088+00:00", - "timestamp_end": "2026-01-08T19:25:46.084183+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/90aeb93e-5476-42fc-b639-cee6acafc459.json b/core/python/.context/logs/copilot/90aeb93e-5476-42fc-b639-cee6acafc459.json deleted file mode 100644 index 321cfbc..0000000 --- a/core/python/.context/logs/copilot/90aeb93e-5476-42fc-b639-cee6acafc459.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "90aeb93e-5476-42fc-b639-cee6acafc459", - "timestamp_start": "2026-01-08T19:25:22.585048+00:00", - "timestamp_end": "2026-01-08T19:25:22.585145+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.json b/core/python/.context/logs/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.json deleted file mode 100644 index 0b3aaed..0000000 --- a/core/python/.context/logs/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "9e5acfe1-0825-49cf-a705-c166a8bf0001", - "timestamp_start": "2026-01-08T19:22:55.252334+00:00", - "timestamp_end": "2026-01-08T19:22:55.252613+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "file", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 50, - "completion_tokens": 100, - "total_tokens": 150 - }, - "cost_usd": 5e-05, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/9e5acfe1-0825-49cf-a705-c166a8bf0001.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json b/core/python/.context/logs/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json deleted file mode 100644 index 19443f2..0000000 --- a/core/python/.context/logs/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "prompt_id": "a8b06959-7916-4d32-ac2b-77ed27c7e7c6", - "timestamp_start": "2026-01-08T19:25:22.626485+00:00", - "timestamp_end": "2026-01-08T19:25:22.626794+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 86021, - "usage": { - "prompt_tokens": 100, - "completion_tokens": 200, - "total_tokens": 300 - }, - "cost_usd": 0.0001, - "output_path": "/home/runner/work/context/context/core/python/.context/copilot/a8b06959-7916-4d32-ac2b-77ed27c7e7c6.md", - "error": null -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/e70a6e22-8148-403a-af63-a99b82cb979a.json b/core/python/.context/logs/copilot/e70a6e22-8148-403a-af63-a99b82cb979a.json deleted file mode 100644 index 86da2ea..0000000 --- a/core/python/.context/logs/copilot/e70a6e22-8148-403a-af63-a99b82cb979a.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "e70a6e22-8148-403a-af63-a99b82cb979a", - "timestamp_start": "2026-01-08T19:22:55.242491+00:00", - "timestamp_end": "2026-01-08T19:22:55.242582+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json b/core/python/.context/logs/copilot/fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json deleted file mode 100644 index 566e174..0000000 --- a/core/python/.context/logs/copilot/fb1d9be3-ad51-40b7-a873-b7bfa94f21c2.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "fb1d9be3-ad51-40b7-a873-b7bfa94f21c2", - "timestamp_start": "2026-01-08T19:25:22.629233+00:00", - "timestamp_end": "2026-01-08T19:25:22.629327+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file diff --git a/core/python/.context/logs/copilot/fe531481-29db-4836-9c2b-e378c72518d7.json b/core/python/.context/logs/copilot/fe531481-29db-4836-9c2b-e378c72518d7.json deleted file mode 100644 index ca9223e..0000000 --- a/core/python/.context/logs/copilot/fe531481-29db-4836-9c2b-e378c72518d7.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "prompt_id": "fe531481-29db-4836-9c2b-e378c72518d7", - "timestamp_start": "2026-01-08T19:22:55.264720+00:00", - "timestamp_end": "2026-01-08T19:22:55.264804+00:00", - "user": "matthew", - "prompt": "test", - "instructions_source": "default", - "model": "gpt-4o-mini", - "budget_usd": 0.05, - "estimated_max_tokens": 0, - "usage": null, - "cost_usd": null, - "output_path": null, - "error": "Virtual key not found. Please set CONTEXT_VIRTUAL_KEY_MATTHEW environment variable." -} \ No newline at end of file