regcode 0.6.0__tar.gz → 0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. regcode-0.6.2/.gitlab-ci.yml +67 -0
  2. {regcode-0.6.0 → regcode-0.6.2}/GOALS.md +33 -3
  3. {regcode-0.6.0 → regcode-0.6.2}/PKG-INFO +2 -2
  4. {regcode-0.6.0 → regcode-0.6.2}/pyproject.toml +2 -2
  5. {regcode-0.6.0 → regcode-0.6.2}/regcode/__init__.py +1 -1
  6. {regcode-0.6.0 → regcode-0.6.2}/regcode/main.py +52 -2
  7. {regcode-0.6.0 → regcode-0.6.2}/regcode/tracing.py +17 -7
  8. {regcode-0.6.0 → regcode-0.6.2}/regcode/tui.py +273 -284
  9. regcode-0.6.2/tests/conftest.py +37 -0
  10. {regcode-0.6.0 → regcode-0.6.2}/tests/test_context_manager.py +1 -1
  11. {regcode-0.6.0 → regcode-0.6.2}/tests/test_osv_cve.py +98 -55
  12. {regcode-0.6.0 → regcode-0.6.2}/uv.lock +5 -5
  13. regcode-0.6.0/tests/conftest.py +0 -7
  14. {regcode-0.6.0 → regcode-0.6.2}/.gitignore +0 -0
  15. {regcode-0.6.0 → regcode-0.6.2}/.python-version +0 -0
  16. {regcode-0.6.0 → regcode-0.6.2}/.qwen/settings.json +0 -0
  17. {regcode-0.6.0 → regcode-0.6.2}/AGENTS.md +0 -0
  18. {regcode-0.6.0 → regcode-0.6.2}/README.md +0 -0
  19. {regcode-0.6.0 → regcode-0.6.2}/config.yaml.template +0 -0
  20. {regcode-0.6.0 → regcode-0.6.2}/main.py +0 -0
  21. {regcode-0.6.0 → regcode-0.6.2}/regcode/cli.py +0 -0
  22. {regcode-0.6.0 → regcode-0.6.2}/regcode/config.py +0 -0
  23. {regcode-0.6.0 → regcode-0.6.2}/regcode/conversation_manager.py +0 -0
  24. {regcode-0.6.0 → regcode-0.6.2}/regcode/dependencies.py +0 -0
  25. {regcode-0.6.0 → regcode-0.6.2}/regcode/monty_sandbox.py +0 -0
  26. {regcode-0.6.0 → regcode-0.6.2}/regcode/permissions.py +0 -0
  27. {regcode-0.6.0 → regcode-0.6.2}/regcode/sandbox.py +0 -0
  28. {regcode-0.6.0 → regcode-0.6.2}/regcode/tools/__init__.py +0 -0
  29. {regcode-0.6.0 → regcode-0.6.2}/regcode/tools/base.py +0 -0
  30. {regcode-0.6.0 → regcode-0.6.2}/regcode/tools/builtins.py +0 -0
  31. {regcode-0.6.0 → regcode-0.6.2}/regcode/tools/registry.py +0 -0
  32. {regcode-0.6.0 → regcode-0.6.2}/regcode/tools/review_notes.py +0 -0
  33. {regcode-0.6.0 → regcode-0.6.2}/regcode/tools/web_crawl.py +0 -0
  34. {regcode-0.6.0 → regcode-0.6.2}/regcode/tools/web_search.py +0 -0
  35. {regcode-0.6.0 → regcode-0.6.2}/tests/__init__.py +0 -0
  36. {regcode-0.6.0 → regcode-0.6.2}/tests/test_api.py +0 -0
  37. {regcode-0.6.0 → regcode-0.6.2}/tests/test_cli.py +0 -0
  38. {regcode-0.6.0 → regcode-0.6.2}/tests/test_config.py +0 -0
  39. {regcode-0.6.0 → regcode-0.6.2}/tests/test_dependencies.py +0 -0
  40. {regcode-0.6.0 → regcode-0.6.2}/tests/test_main.py +0 -0
  41. {regcode-0.6.0 → regcode-0.6.2}/tests/test_monty_sandbox.py +0 -0
  42. {regcode-0.6.0 → regcode-0.6.2}/tests/test_provider_extra.py +0 -0
  43. {regcode-0.6.0 → regcode-0.6.2}/tests/test_review_notes.py +0 -0
  44. {regcode-0.6.0 → regcode-0.6.2}/tests/test_web_search.py +0 -0
@@ -0,0 +1,67 @@
1
+ # .gitlab-ci.yml
2
+
3
+ stages:
4
+ - test
5
+ - build
6
+ - publish
7
+
8
+ variables:
9
+ UV_PIP_PREVENT_WRITE_BACK: "1"
10
+
11
+ lint:
12
+ stage: test
13
+ image: python:3.12-slim
14
+ before_script:
15
+ - pip install uv
16
+ script:
17
+ - uv venv .venv
18
+ - source .venv/bin/activate
19
+ - uv pip install ".[dev]"
20
+ - uv run ruff check regcode/ tests/
21
+
22
+ pytest:
23
+ stage: test
24
+ image: python:3.12-slim
25
+ before_script:
26
+ - pip install uv
27
+ script:
28
+ - uv venv .venv
29
+ - source .venv/bin/activate
30
+ - uv pip install ".[dev]"
31
+ - uv run pytest tests/ -v --tb=short
32
+
33
+ build:
34
+ stage: build
35
+ image: python:3.12-slim
36
+ before_script:
37
+ - pip install uv
38
+ script:
39
+ - uv build
40
+ artifacts:
41
+ paths:
42
+ - dist/
43
+ expire_in: 1 week
44
+ rules:
45
+ - if: $CI_COMMIT_BRANCH == "main"
46
+ changes:
47
+ paths:
48
+ - "regcode/**/*"
49
+ - "pyproject.toml"
50
+ when: on_success
51
+ - when: never
52
+
53
+ publish:
54
+ stage: publish
55
+ image: python:3.12-slim
56
+ before_script:
57
+ - pip install uv
58
+ script:
59
+ - uv publish --token $UV_PUBLISH_TOKEN
60
+ rules:
61
+ - if: $CI_COMMIT_BRANCH == "main"
62
+ changes:
63
+ paths:
64
+ - "regcode/**/*"
65
+ - "pyproject.toml"
66
+ when: on_success
67
+ - when: never
@@ -497,7 +497,7 @@ All phases complete. 50/50 tests passing. Lint clean.
497
497
 
498
498
  1. ✅ **Remove model config params from AgentConfig** — `model`, `max_tokens`, `temperature` moved from `Config.agent` to `Config.provider`. `AgentConfig` now only holds `context_window`, `compaction_threshold`, and `enable_compaction`. All tests updated to use new format.
499
499
 
500
- 2. ✅ **TUI complete overhaul** — `tui.py` rewritten from scratch. New layout: (1) top status bar showing `[RegCode <version>] [model] [$cost] [tokens]`, (2) large chat history area with clearly separated user/agent/tool/system message blocks, (3) bottom bar with progress indicator and input field. Streaming works correctly: text chunks stream live, then on completion full Markdown is rendered. Tool calls are interleaved after streaming text (stream buffer flushed before status rendering). Context compaction shows status indicators. All 137 tests pass, lint clean.
500
+ 2. ✅ **TUI complete overhaul** — `tui.py` rewritten from scratch. New layout: (1) top status bar showing `[RegCode <version>] [model] [$cost] [tokens]`, (2) large chat history area with RichLog widget and clearly separated user/agent/tool/system message blocks, (3) bottom bar with progress indicator and input field. Streaming works correctly: text chunks stream live, then on completion full Markdown is rendered. Tool calls are interleaved after streaming text (stream buffer flushed before status rendering). Context compaction shows status indicators. RichLog renders Rich markup for colors; text selection is available via terminal scrollback. All 164 tests pass, lint clean.
501
501
 
502
502
  3. ✅ **WebSearchTool** — New `regcode/tools/web_search.py` tool connecting to SearXNG instances. Defaults to `https://search.regmi.dev`, supports custom SearXNG URL, language code, and result count (1-20). Uses only stdlib `urllib` — no extra dependencies. Registered in `create_default_tools()`. 15 tests pass.
503
503
 
@@ -505,5 +505,35 @@ All phases complete. 50/50 tests passing. Lint clean.
505
505
 
506
506
  - ✅ WebSearchTool: connects to SearXNG, defaults to https://search.regmi.dev, no extra deps needed
507
507
 
508
- ## To Dos:
509
- - If current Dir has `Agents.MD`, or `AGENTS.md` or `agents.md`, read it and provide it clearly marked as content of Agents.md to the model as extra context about the project.
508
+ ## Completed (2026-06-30 — TUI fixes)
509
+
510
+ 1. ✅ AGENTS.md injection — Agent now reads AGENTS.md/agents.md from CWD or allowed directory and injects it into system prompt (both `_build_system_prompt` and `_build_system_prompt_no_tools`). Appended after base system_prompt, before tool descriptions.
511
+
512
+ 2. ✅ TUI text selectability — Replaced RichLog with TextArea for the chat area. TextArea is the only Textual widget with real text selection and copy support (`action_copy`, `action_select_all`, `selected_text`). All write paths use `_append_text()` which converts Rich markup to ANSI escape codes via Rich's `Console.print()`, so text displays with colors AND remains selectable/copyable.
513
+
514
+ 3. ✅ Context window config — Hardcoded `_context_window: int = 128000` replaced with 0 default, with fallback chain in `_update_progress()`: `_context_window` → `self._agent.config.agent.context_window` → 128000.
515
+
516
+ 4. ✅ Hardcoded context window — Fixed `_update_progress()` to check `self._agent.config.agent.context_window` before falling back to 128000.
517
+
518
+ ## Completed (2026-06-30 — TUI overhaul)
519
+
520
+ - ✅ `tui.py` rewritten: 3-zone layout (status bar, chat area, input bar). Chat area uses TextArea (selectable/copyable) instead of RichLog. All Rich markup tags are stripped before insertion so text is readable. Agent runs in background thread, callbacks use `call_from_thread`.
521
+ - ✅ All 164 tests pass, lint clean.
522
+
523
+ ## Completed (2026-06-30 — TUI cleanup)
524
+
525
+ 1. ✅ **Added `textual>=2.0.0` dependency** — was imported but missing from `pyproject.toml`.
526
+ 2. ✅ **Replaced top-bar `TextArea` with `Label`** — the status bar only displays plain text; Label is lighter-weight and semantically correct. Same for progress bar widget.
527
+ 3. ✅ **Simplified streaming state machine** — reduced from 7 state variables (`_streaming`, `_streaming_started`, `_stream_pending`, `_stream_has_label`, `_stream_line_open`, `_stream_trim_leading`, `_current_response`) and 5 flushing methods down to 2 (`_streaming`, `_current_response`) and 1 flush method (`_flush_stream_for_status`). The old code buffered partial lines with complex state tracking; the new approach simply accumulates chunks in `_current_response` and flushes them as a single block when needed.
528
+ 4. ✅ **Optimized `_rich_to_ansi`** — replaced per-call `Console()` instantiation with a cached `_rich_console` instance created once in `__init__`, eliminating redundant object allocation on every text write.
529
+ 5. ✅ **All 164 tests pass**, lint clean.
530
+
531
+ ## Completed (2026-06-30 — TUI rewrite)
532
+
533
+ Complete rewrite of `tui.py` from scratch using Textual's `RichLog` widget instead of `TextArea`:
534
+
535
+ 1. ✅ **`RichLog` instead of `TextArea`** — RichLog natively renders Rich markup (`[bold]`, `[dim]`, etc.) so no fragile Rich→ANSI conversion is needed. Users see properly colored, styled text. RichLog also supports scrollback selection/copy via terminal features.
536
+ 2. ✅ **Clean Catppuccin Mocha dark theme** — uses the Catppuccin color palette (`#1e1e2e` background, `#cdd6f4` text, `#313244` surfaces, `#89b4fa` accents) for a modern, polished look.
537
+ 3. ✅ **Proper three-zone layout** — status bar (version | model | cost | tokens), scrollable chat history, bottom input bar with progress indicator. No raw markup tags visible.
538
+ 4. ✅ **Same backward-compatible API** — `ChatUI` facade, `ChatApp` class, `_ColorFormatter`, all callback methods (`handle_status`, `handle_result`, `handle_cost`, `print_text_chunk`, etc.) preserved exactly as before for CLI integration.
539
+ 5. ✅ **All 164 tests pass**, lint clean.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: regcode
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: Minimalistic coding agent with Python API and CLI
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: click>=8.0
@@ -12,7 +12,7 @@ Requires-Dist: pydantic-settings>=2.14.2
12
12
  Requires-Dist: pytest>=8.0
13
13
  Requires-Dist: pyyaml>=6.0
14
14
  Requires-Dist: ruff>=0.8
15
- Requires-Dist: textual>=8.2.7
15
+ Requires-Dist: textual>=2.0.0
16
16
  Provides-Extra: dev
17
17
  Description-Content-Type: text/markdown
18
18
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "regcode"
3
- version = "0.6.0"
3
+ version = "0.6.2"
4
4
  description = "Minimalistic coding agent with Python API and CLI"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -13,8 +13,8 @@ dependencies = [
13
13
  "pytest>=8.0",
14
14
  "ruff>=0.8",
15
15
  "pydantic-monty>=0.0.18",
16
- "textual>=8.2.7",
17
16
  "mlflow>=3.14.0",
17
+ "textual>=2.0.0",
18
18
  ]
19
19
 
20
20
  [project.scripts]
@@ -2,4 +2,4 @@ from regcode.config import Config
2
2
  from regcode.main import Agent, AgentStatus, ToolCall
3
3
 
4
4
  __all__ = ["Agent", "Config", "AgentStatus", "ToolCall"]
5
- __version__ = "0.6.0"
5
+ __version__ = "0.6.2"
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import warnings
7
+ from datetime import datetime, timezone
7
8
  from enum import Enum
8
9
  from pathlib import Path
9
10
  from typing import Any, Callable
@@ -444,11 +445,48 @@ class Agent:
444
445
  self._history = [m for m in messages if m.get("role") != "system"]
445
446
  return final_text or response_message.get("content", "") or ""
446
447
 
448
+ @staticmethod
449
+ def _read_agents_md(directory: str | None = None) -> str:
450
+ """Read AGENTS.md from the given directory if it exists.
451
+
452
+ Returns the file contents with a header, or an empty string if
453
+ the file is not found.
454
+ """
455
+ search_dirs: list[Path] = []
456
+ if directory:
457
+ search_dirs.append(Path(directory))
458
+ search_dirs.append(Path.cwd())
459
+ for candidate in search_dirs:
460
+ for name in ("AGENTS.md", "agents.md", "Agents.md"):
461
+ p = candidate / name
462
+ if p.exists():
463
+ try:
464
+ return p.read_text(encoding="utf-8")
465
+ except OSError:
466
+ continue
467
+ return ""
468
+
447
469
  def _build_system_prompt(self) -> str:
448
470
  """Build the full system prompt including tool definitions."""
471
+ now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
472
+ date_section = f"\n\nCurrent time: {now}\n"
473
+ agents_md = self._read_agents_md(self._allowed_dir)
474
+ agents_md_section = ""
475
+ if agents_md:
476
+ agents_md_section = (
477
+ f"\n\n{'=' * 60}\n"
478
+ "AGENT INSTRUCTIONS (injected from AGENTS.md):\n"
479
+ f"{'=' * 60}\n\n{agents_md}"
480
+ )
449
481
  tool_descriptions = self._format_tool_descriptions()
450
482
  notes_section = self._format_review_notes()
451
- return f"{self.system_prompt}{tool_descriptions}{notes_section}"
483
+ return (
484
+ f"{self.system_prompt}"
485
+ f"{date_section}"
486
+ f"{agents_md_section}"
487
+ f"{tool_descriptions}"
488
+ f"{notes_section}"
489
+ )
452
490
 
453
491
  def _build_system_prompt_no_tools(self) -> str:
454
492
  """Build the system prompt WITHOUT tool definitions.
@@ -460,7 +498,19 @@ class Agent:
460
498
  stripped, because the accumulated review findings remain relevant
461
499
  context for the LLM's final response.
462
500
  """
463
- return f"{self.system_prompt}{self._format_review_notes()}"
501
+ now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
502
+ date_section = f"\n\nCurrent time: {now}\n"
503
+ agents_md = self._read_agents_md(self._allowed_dir)
504
+ agents_md_section = ""
505
+ if agents_md:
506
+ agents_md_section = (
507
+ f"\n\n{'=' * 60}\n"
508
+ "AGENT INSTRUCTIONS (injected from AGENTS.md):\n"
509
+ f"{'=' * 60}\n\n{agents_md}"
510
+ )
511
+ parts = [self.system_prompt, date_section, agents_md_section]
512
+ parts.append(self._format_review_notes())
513
+ return "".join(parts)
464
514
 
465
515
  def _stream(
466
516
  self,
@@ -39,9 +39,17 @@ def _initialize_tracing(config: Config | None = None) -> None:
39
39
  if not _tracing_config.tracing_enabled():
40
40
  return
41
41
 
42
- if _tracing_config.mlflow_requires_auth and _tracing_config.mlflow_username and _tracing_config.mlflow_password:
43
- MLFLOW_TRACKING_USERNAME.set(_tracing_config.mlflow_username)
44
- MLFLOW_TRACKING_PASSWORD.set(_tracing_config.mlflow_password)
42
+ if (
43
+ _tracing_config.mlflow_requires_auth
44
+ and _tracing_config.mlflow_username
45
+ and _tracing_config.mlflow_password
46
+ ):
47
+ MLFLOW_TRACKING_USERNAME.set(
48
+ _tracing_config.mlflow_username
49
+ )
50
+ MLFLOW_TRACKING_PASSWORD.set(
51
+ _tracing_config.mlflow_password
52
+ )
45
53
 
46
54
  _logger.info(f"Setting up MLflow tracing with URL: {_tracing_config.mlflow_url}")
47
55
 
@@ -76,14 +84,16 @@ def agent_trace(user_message: str, tool_calls_count: int = 0) -> Iterator[None]:
76
84
  with agent_trace("Fix the bug", 3):
77
85
  result = agent.chat("Fix the bug")
78
86
  """
79
- if not _tracing_initialized or not _tracing_config or not _tracing_config.tracing_enabled():
87
+ if (
88
+ not _tracing_initialized
89
+ or not _tracing_config
90
+ or not _tracing_config.tracing_enabled()
91
+ ):
80
92
  yield
81
93
  return
82
94
 
83
- trace_name = f"agent.turn.{tool_calls_count}tools"
84
-
85
95
  try:
86
- with mlflow.start_trace() as trace:
96
+ with mlflow.start_trace():
87
97
  # Log input attributes for better trace UI
88
98
  mlflow.set_tag("user_message", user_message[:200])
89
99
  mlflow.set_tag("tool_calls", tool_calls_count)