From 01793b2de60af979bec9679152a56154bb2eca2d Mon Sep 17 00:00:00 2001
From: ares <285551516+New1Direction@users.noreply.github.com>
Date: Tue, 9 Jun 2026 12:42:55 -0700
Subject: [PATCH 1/3] fix(opsec): guard+ignore Ghidra RE output; ruff --fix
backlog; doc-consistency fixes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Opsec (HIGH): .gitignore + scripts/githooks/pre-commit now block pyghidra/Ghidra
output (pyghidra_mcp_projects/, *ghidra*, *.gpr, *.rep) — previously neither did,
so 'git add .' could publish RE artifacts to the public repo. Added a regression
test (opsec guard suite 6 -> 7 passing).
gitignore: also ignore .wrangler/ and uv.lock; de-duplicate .DS_Store.
ruff: auto-fixed 74 of 94 backlog issues across 23 modules (no __init__ touched,
so no broken re-exports); full suite green (1,584 passing, 2 skipped). 19 manual
issues remain (unused vars, import-order, ambiguous names).
docs: ROADMAP.md HISTORICAL banner (it predated the v0.x pivot / '27 tests');
README test count 1,285 -> 1,586 + badge; split a glued 'Known limitations' bullet;
AGENTS.md stale tool names (list_files/read_file/set_plan -> Glob/Grep/Read).
assets: moved unreferenced korgex-chrome-light.png -> docs/images/.
---
.gitignore | 12 +++++++++++-
AGENTS.md | 6 +++---
README.md | 9 +++++----
ROADMAP.md | 7 +++++++
docs/images/korgex-chrome-light.png | Bin 0 -> 6652 bytes
scripts/githooks/pre-commit | 4 ++--
src/agent.py | 6 ++----
src/cli.py | 22 +++++++++++-----------
src/dashboard.py | 4 ----
src/dependency_graph.py | 2 +-
src/diff_engine.py | 6 +++---
src/feature_flags.py | 4 +---
src/github_api.py | 2 --
src/hooks.py | 2 --
src/korg_ledger.py | 2 --
src/mcp_client.py | 24 +++++++++++-------------
src/memory.py | 1 -
src/mode_schemas.py | 1 -
src/model_router.py | 8 +++-----
src/sandbox.py | 5 ++---
src/self_healing.py | 1 -
src/strict_pairing.py | 1 -
src/swarm.py | 1 -
src/system_prompt.py | 1 -
src/tool_abstraction.py | 1 -
src/tool_base.py | 4 ----
src/tools_impl.py | 2 +-
src/tui_app.py | 1 -
src/vision.py | 3 ---
tests/test_opsec_guard.py | 7 +++++++
30 files changed, 70 insertions(+), 79 deletions(-)
create mode 100644 docs/images/korgex-chrome-light.png
diff --git a/.gitignore b/.gitignore
index dfb7fb6..409a8dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,11 @@ RE_FINDINGS*.md
*-assessment-*.html
*reverse-engineering*
*-RE-*.md
+# Ghidra / binary RE projects (pyghidra-mcp output) — must never enter the public tree
+pyghidra_mcp_projects/
+*ghidra*
+*.gpr
+*.rep
# Python
__pycache__/
@@ -37,7 +42,6 @@ secrets*
*.swp
*.swo
*~
-.DS_Store
# Seluj
.seluj/
@@ -55,3 +59,9 @@ ecosystem_audit_*.html
# playwright MCP test artifacts
.playwright-mcp/
+
+# Cloudflare wrangler local state
+.wrangler/
+
+# uv lock (dev convenience; project builds via setuptools/pip from pyproject)
+uv.lock
diff --git a/AGENTS.md b/AGENTS.md
index 20dcb56..13027fa 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -5,9 +5,9 @@ Your purpose is to assist users by completing coding tasks, such as solving bugs
implementing features, and writing tests.
## Core Directives
-1. PLAN FIRST: Explore the codebase (list_files, read_file). Read this file and README.md.
- Ask clarifying questions. Articulate the plan using set_plan.
-2. VERIFY WORK: After every modification, use read_file or list_files to confirm success.
+1. PLAN FIRST: Explore the codebase (Glob, Grep, Read). Read this file and README.md.
+ Ask clarifying questions. Articulate the plan (track steps with TaskCreate/TaskUpdate).
+2. VERIFY WORK: After every modification, use Read or Grep to confirm success.
Do NOT mark a plan step complete until you've verified.
3. EDIT SOURCE, NOT ARTIFACTS: If a file is a build artifact (dist/, build/, node_modules/,
__pycache__/, .next/), trace back to its source.
diff --git a/README.md b/README.md
index 6f63e15..a750df6 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
-
+
@@ -541,7 +541,7 @@ korgex/
│ └── ...
├── docs/ # CLI reference, comparison, getting-started, tools-reference, …
├── spec/korg-ledger-v1/ # the ledger spec (SPEC.md, EVENTS.md)
-├── tests/ # ~1,571 tests
+├── tests/ # ~1,586 tests
├── .github/workflows/ # Linux CI (3.10–3.13) + PyPI publisher (OIDC)
├── pyproject.toml
└── README.md
@@ -560,7 +560,7 @@ ruff check src/ # lint
pytest -q # the full suite
```
-The suite is **~1,285 tests** with no live LLM calls (everything is unit-tested) and runs on Linux CI across **Python 3.10, 3.11, 3.12, 3.13** on every push and PR. Major areas: the agent loop (routing, provider schemas, mode/model resolution, loop guards, the stall classifier, compaction), tools (fuzzy Edit, edit-freshness, background Bash, web), the verifiable ledger (hash-chain + causal DAG, redaction, the Ed25519 signed bus), CodeAct (kernel isolation, fuel, the tool bridge), MCP (namespaced multi-server router, OAuth refresh, full round-trip), prompt caching, skills (trust tiers, self-learning, the curator), and the REPL.
+The suite is **~1,586 tests** with no live LLM calls (everything is unit-tested) and runs on Linux CI across **Python 3.10, 3.11, 3.12, 3.13** on every push and PR. Major areas: the agent loop (routing, provider schemas, mode/model resolution, loop guards, the stall classifier, compaction), tools (fuzzy Edit, edit-freshness, background Bash, web), the verifiable ledger (hash-chain + causal DAG, redaction, the Ed25519 signed bus), CodeAct (kernel isolation, fuel, the tool bridge), MCP (namespaced multi-server router, OAuth refresh, full round-trip), prompt caching, skills (trust tiers, self-learning, the curator), and the REPL.
---
@@ -594,7 +594,8 @@ To build locally for inspection: `python -m build` then `python -m twine check d
These exist today; PRs welcome.
-- **OpenAI streaming has fewer rendered events than Anthropic.** Anthropic emits thinking blocks and message-delta usage; OpenAI emits only text and tool-call chunks. Both render correctly, but the TUI is richer for Anthropic.- **Dashboard authentication is not implemented.** Don't expose port 8090 publicly without an auth-terminating reverse proxy in front.
+- **OpenAI streaming has fewer rendered events than Anthropic.** Anthropic emits thinking blocks and message-delta usage; OpenAI emits only text and tool-call chunks. Both render correctly, but the TUI is richer for Anthropic.
+- **Dashboard authentication is not implemented.** Don't expose port 8090 publicly without an auth-terminating reverse proxy in front.
- **The VS Code sidecar is a legacy companion** to the dashboard; korgex's primary interface is the terminal REPL.
---
diff --git a/ROADMAP.md b/ROADMAP.md
index 7595631..889f5aa 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,5 +1,12 @@
# Korgex — Next Frontier Roadmap
+> ⚠️ **HISTORICAL — kept for reference; do not treat as current.**
+> This roadmap predates korgex's v0.x pivot. It describes a pre-pivot plan (korg-bridge / PyO3
+> → KorgChat, "27 tests"). korgex has since shipped through **v0.35.0** (~1,571 tests) as a
+> provider-agnostic, MCP-native coding agent. For the current state see
+> [`CHANGELOG.md`](CHANGELOG.md) and [`README.md`](README.md). (The AlphaEvolve appendix below
+> is still useful.)
+
## Release Sequence
| Version | Status | Scope |
diff --git a/docs/images/korgex-chrome-light.png b/docs/images/korgex-chrome-light.png
new file mode 100644
index 0000000000000000000000000000000000000000..e98e1bc0ec21cc1f6d208eee2881f53d3ff290e4
GIT binary patch
literal 6652
zcmeAS@N?(olHy`uVBq!ia0y~yVA%k~OF5W;BIXg>fgAw_PZ!6KiaBqtGx9PRa2zoB
zbZ396z@}@i|KITe71`zk2@fGB28IR=MHU8zj#1`lIE*HO(abPf5{#CJqt(G^wK&=)
z7;PJkHVj8w#iOm_(N^(jt9Yokip5z^rZO;aE(MJRjF#r3rTJ)SK3bZOmgYmXG#@R&
zM@#V05`37J;47FJ82FVdQ&MBb@0OfQ1
AL;wH)
literal 0
HcmV?d00001
diff --git a/scripts/githooks/pre-commit b/scripts/githooks/pre-commit
index 55c801f..d132ef6 100755
--- a/scripts/githooks/pre-commit
+++ b/scripts/githooks/pre-commit
@@ -18,11 +18,11 @@ set -u
# so bare "hermes"/"nousresearch" are intentionally absent; only the RE-specific
# module `hermes_tools` is blocked. \bMITM\b (word-bounded) avoids the "comMITMent"
# false positive; the trailing grep -v strips other benign substrings.
-PATTERN='mitmproxy|manual mitm|\bmitm\b|reverse[- ]?engineer|captured from (claude|the api|api\.)|claude max|mahoraga|\bjules\b|hermes_tools|kikkaskills|kabukiskills|intercepted from'
+PATTERN='mitmproxy|manual mitm|\bmitm\b|reverse[- ]?engineer|captured from (claude|the api|api\.)|claude max|mahoraga|\bjules\b|hermes_tools|kikkaskills|kabukiskills|intercepted from|ghidra|pyghidra'
# 1) Sensitive file NAMES being added.
bad_files=$(git diff --cached --name-only --diff-filter=A \
- | grep -iE 'RE[_-]?FINDINGS|reverse.?eng|[-_]assessment[-_].*\.html$|_mitm' || true)
+ | grep -iE 'RE[_-]?FINDINGS|reverse.?eng|[-_]assessment[-_].*\.html$|_mitm|ghidra|\.gpr$' || true)
# 2) Sensitive ADDED lines (only new content, not context). The guard's own files
# legitimately contain these patterns (the definition + its docs), so exclude them.
diff --git a/src/agent.py b/src/agent.py
index 783ddc2..2596c00 100644
--- a/src/agent.py
+++ b/src/agent.py
@@ -14,8 +14,6 @@
import os
import sys
import time
-from pathlib import Path
-from typing import Optional
from src.tool_abstraction import USER_TOOLS, route_tool_call
from src import tool_abstraction as _TA
@@ -92,8 +90,8 @@ def codeact_unconfined_warning(platform: str) -> str:
from src.agent_resolve import ( # resolution helpers, extracted to keep agent.py focused
- _looks_anthropic, _OAUTH_BASE_URLS, _oauth_provider_for, _oauth_token_and_base,
- _READONLY_SUBAGENT_TOOLS, _MODEL_ALIASES, subagent_tools, _resolve_params, _resolve_model,
+ _looks_anthropic, _oauth_provider_for, _oauth_token_and_base,
+ _MODEL_ALIASES, subagent_tools, _resolve_params, _resolve_model,
)
diff --git a/src/cli.py b/src/cli.py
index 90fa8cc..bfe8168 100644
--- a/src/cli.py
+++ b/src/cli.py
@@ -298,15 +298,15 @@ def cmd_default():
subprocess.Popen([code, str(ext_path)])
print()
- print(f" ┌─────────────────────────────────────────────┐")
- print(f" │ Korgex is live │")
- print(f" │ │")
+ print(" ┌─────────────────────────────────────────────┐")
+ print(" │ Korgex is live │")
+ print(" │ │")
print(f" │ Dashboard → http://localhost:{DASHBOARD_PORT:<4} │")
- print(f" │ VS Code → Press F5 in the new window │")
- print(f" │ Commands → Cmd+Shift+P → 'Korgex:' │")
- print(f" │ │")
- print(f" │ korgex stop to shut down │")
- print(f" └─────────────────────────────────────────────┘")
+ print(" │ VS Code → Press F5 in the new window │")
+ print(" │ Commands → Cmd+Shift+P → 'Korgex:' │")
+ print(" │ │")
+ print(" │ korgex stop to shut down │")
+ print(" └─────────────────────────────────────────────┘")
def cmd_init():
@@ -335,7 +335,7 @@ def cmd_dashboard():
"""Start just the web dashboard."""
_start_background_server()
print(f" Dashboard: http://localhost:{DASHBOARD_PORT}")
- print(f" Press Ctrl+C to stop.")
+ print(" Press Ctrl+C to stop.")
def cmd_status():
@@ -346,7 +346,7 @@ def cmd_status():
print(f" Dashboard: http://localhost:{DASHBOARD_PORT}")
else:
print(" Korgex is not running.")
- print(f" Run `korgex` to start.")
+ print(" Run `korgex` to start.")
def cmd_stop():
@@ -411,7 +411,7 @@ def cmd_verify():
if not Path(path).exists():
print(f" No ledger journal at {path}")
- print(f" (set KORG_JOURNAL_PATH or pass: korgex verify )")
+ print(" (set KORG_JOURNAL_PATH or pass: korgex verify )")
return 1
n = len(load_journal_raw(path)) # real event count (array OR jsonl), not line count
diff --git a/src/dashboard.py b/src/dashboard.py
index 97f105f..c2954e1 100644
--- a/src/dashboard.py
+++ b/src/dashboard.py
@@ -9,11 +9,7 @@
- Subagent swarm dashboard
"""
-import json
-import os
import threading
-import uuid
-from pathlib import Path
from typing import Optional
try:
diff --git a/src/dependency_graph.py b/src/dependency_graph.py
index 25187f7..0436908 100644
--- a/src/dependency_graph.py
+++ b/src/dependency_graph.py
@@ -20,7 +20,7 @@
import os
import ast
from pathlib import Path
-from typing import Dict, List, Set, Optional, Any
+from typing import Dict, List, Set, Optional
class DependencyAnalyzer:
diff --git a/src/diff_engine.py b/src/diff_engine.py
index 45c8e75..13aa95d 100644
--- a/src/diff_engine.py
+++ b/src/diff_engine.py
@@ -71,7 +71,7 @@ def apply_git_three_way(filepath: str, new_content: str) -> dict:
"conflicts": False,
}
- except Exception as e:
+ except Exception:
# Fall back to SEARCH/REPLACE
return DiffEngine.apply_search_replace(filepath, f"<<<<<<< SEARCH\n>>>>>>> REPLACE\n{new_content}")
@@ -128,9 +128,9 @@ def apply_search_replace(filepath: str, merge_diff: str) -> dict:
changes += 1
break
else:
- errors.append(f"Could not locate SEARCH block (fuzzy match failed)")
+ errors.append("Could not locate SEARCH block (fuzzy match failed)")
else:
- errors.append(f"SEARCH block not found in file (exact or fuzzy)")
+ errors.append("SEARCH block not found in file (exact or fuzzy)")
if changes == 0:
return {"error": "No changes applied.", "details": errors}
diff --git a/src/feature_flags.py b/src/feature_flags.py
index ca1f455..368dea4 100644
--- a/src/feature_flags.py
+++ b/src/feature_flags.py
@@ -7,9 +7,7 @@
Flags also control runtime behavior: tool availability, model settings, etc.
"""
-import os
-from dataclasses import dataclass, field
-from typing import Optional
+from dataclasses import dataclass
@dataclass
diff --git a/src/github_api.py b/src/github_api.py
index 6faaec7..f1bc1c2 100644
--- a/src/github_api.py
+++ b/src/github_api.py
@@ -5,9 +5,7 @@
"""
import os
-import json
import subprocess
-from typing import Optional
GITHUB_TOKEN_ENV = "KORGEX_GITHUB_TOKEN"
GITHUB_API = "https://api.github.com"
diff --git a/src/hooks.py b/src/hooks.py
index 790b38a..4698bca 100644
--- a/src/hooks.py
+++ b/src/hooks.py
@@ -34,11 +34,9 @@
import hashlib
import json
import logging
-import os
import re
import subprocess
from pathlib import Path
-from typing import Any
logger = logging.getLogger(__name__)
diff --git a/src/korg_ledger.py b/src/korg_ledger.py
index 6c6ec5b..9b7a63f 100644
--- a/src/korg_ledger.py
+++ b/src/korg_ledger.py
@@ -65,13 +65,11 @@
from __future__ import annotations
import hashlib
-import hmac
import json
import logging
import os
import queue
import threading
-import time
from pathlib import Path
from typing import Any
diff --git a/src/mcp_client.py b/src/mcp_client.py
index 33133c5..2fffe6f 100644
--- a/src/mcp_client.py
+++ b/src/mcp_client.py
@@ -25,13 +25,11 @@
import json
import os
import subprocess
-import sys
import threading
-import time
import uuid
from collections import deque
from dataclasses import dataclass, field
-from typing import Any, Optional
+from typing import Optional
# Cap MCP server stderr capture so a chatty server can't grow memory unbounded.
# 1000 lines is plenty for post-mortem diagnostics; oldest are dropped first.
@@ -209,7 +207,7 @@ def connect(self) -> dict:
text=True,
bufsize=1, # Line-buffered
)
- except FileNotFoundError as e:
+ except FileNotFoundError:
return {"error": f"Command not found: {self.config.command}", "status": "failed"}
except Exception as e:
return {"error": f"Failed to spawn: {e}", "status": "failed"}
@@ -657,20 +655,20 @@ def get_manager() -> MCPServerManager:
print("=== MCP Client Module Test ===\n")
# Verify all components load
- print(f" MCPClient class: ✓")
- print(f" MCPServerManager class: ✓")
- print(f" MCPTool dataclass: ✓")
- print(f" MCPServerConfig dataclass: ✓")
- print(f" make_request: ✓")
- print(f" parse_response: ✓")
- print(f" load_mcp_config: ✓")
+ print(" MCPClient class: ✓")
+ print(" MCPServerManager class: ✓")
+ print(" MCPTool dataclass: ✓")
+ print(" MCPServerConfig dataclass: ✓")
+ print(" make_request: ✓")
+ print(" parse_response: ✓")
+ print(" load_mcp_config: ✓")
# Test JSON-RPC message format
req = make_request("initialize", {"protocolVersion": "2025-03-26"})
parsed = json.loads(req)
assert parsed["jsonrpc"] == "2.0"
assert parsed["method"] == "initialize"
- print(f"\n ✓ JSON-RPC 2.0 message format verified")
+ print("\n ✓ JSON-RPC 2.0 message format verified")
# Test config loading
test_config = """{
@@ -692,5 +690,5 @@ def get_manager() -> MCPServerManager:
assert configs["github"].command == "npx"
assert configs["github"].args == ["-y", "@modelcontextprotocol/server-github"]
os.unlink(tmp_path)
- print(f" ✓ MCP config parsing verified")
+ print(" ✓ MCP config parsing verified")
print(f"\n Ready: {len(configs)} servers from config")
\ No newline at end of file
diff --git a/src/memory.py b/src/memory.py
index c84b3e9..147664f 100644
--- a/src/memory.py
+++ b/src/memory.py
@@ -31,7 +31,6 @@
import re
import yaml
from datetime import datetime
-from pathlib import Path
from typing import Optional
MEMORY_DIR = None # Set during init
diff --git a/src/mode_schemas.py b/src/mode_schemas.py
index 742ad21..ae8bb04 100644
--- a/src/mode_schemas.py
+++ b/src/mode_schemas.py
@@ -17,7 +17,6 @@
[Agent Loop] → determines mode → selects tool subset → sends to LLM
"""
-from typing import Optional
# ── Tool Subset Definitions ─────────────────────────────────────────────
diff --git a/src/model_router.py b/src/model_router.py
index 2e1e5d1..0fd3f97 100644
--- a/src/model_router.py
+++ b/src/model_router.py
@@ -23,12 +23,10 @@
import json
import os
import time
-from dataclasses import dataclass, field
-from enum import Enum
-from typing import Any, Optional, Protocol
+from dataclasses import dataclass
+from typing import Optional, Protocol
# For cost tracking and display
-from src.feature_flags import is_enabled
def _to_epoch(value, ms: bool = False) -> float:
@@ -1519,5 +1517,5 @@ def on_mode_change(mode: str):
p3 = router.get_current_params()
assert p1["model"] == p3["model"], "Context lost during plan→execute→plan swap"
- print(f"\n ✓ Context preserved across plan→execute→plan swap")
+ print("\n ✓ Context preserved across plan→execute→plan swap")
print(f" ✓ Model swap count: {router._swap_count}")
\ No newline at end of file
diff --git a/src/sandbox.py b/src/sandbox.py
index 8f7e25e..97a8da0 100644
--- a/src/sandbox.py
+++ b/src/sandbox.py
@@ -13,12 +13,10 @@
"""
import os
-import json
import subprocess
import tempfile
import shutil
from pathlib import Path
-from typing import Optional
class SandboxBase:
@@ -143,7 +141,8 @@ def _setup_modal(self):
@self.app.function(image=image, timeout=600)
def run_in_cloud(cmd: str, repo_url: str = None):
- import subprocess, os, tempfile
+ import subprocess
+ import os
results = {"stdout": "", "stderr": "", "exit_code": 0}
diff --git a/src/self_healing.py b/src/self_healing.py
index 89ee0ac..d68a1e9 100644
--- a/src/self_healing.py
+++ b/src/self_healing.py
@@ -18,7 +18,6 @@
"""
import json
-import os
import re
from typing import Callable, Optional
diff --git a/src/strict_pairing.py b/src/strict_pairing.py
index 6119aa8..abbe8c4 100644
--- a/src/strict_pairing.py
+++ b/src/strict_pairing.py
@@ -26,7 +26,6 @@
import hashlib
import json
-import os
import secrets
import time
from typing import Any, Optional
diff --git a/src/swarm.py b/src/swarm.py
index 16a4045..0debc37 100644
--- a/src/swarm.py
+++ b/src/swarm.py
@@ -15,7 +15,6 @@
import json
import os
-import threading
import time
import uuid
from concurrent.futures import ThreadPoolExecutor, as_completed
diff --git a/src/system_prompt.py b/src/system_prompt.py
index bc03513..c5fa4a4 100644
--- a/src/system_prompt.py
+++ b/src/system_prompt.py
@@ -14,7 +14,6 @@
import os
import platform
import subprocess
-from pathlib import Path
def build_system_prompt(memory_text: str = "", workdir: str = None,
diff --git a/src/tool_abstraction.py b/src/tool_abstraction.py
index 693491d..8c6f95f 100644
--- a/src/tool_abstraction.py
+++ b/src/tool_abstraction.py
@@ -16,7 +16,6 @@
import json
import os
-from typing import Any, Callable, Dict, Optional
# ── User-Facing Tool Definitions ────────────────────────────────────────
# These are the ~12 tools the LLM sees. Each has deep descriptions.
diff --git a/src/tool_base.py b/src/tool_base.py
index 65db9b4..ce943ef 100644
--- a/src/tool_base.py
+++ b/src/tool_base.py
@@ -7,13 +7,9 @@
Param types: STRING, BOOLEAN, ARRAY, OBJECT (a compact schema, not full JSON Schema).
"""
-import json
import os
-import shlex
import subprocess
-import tempfile
from functools import lru_cache
-from pathlib import Path
from typing import Any, Callable, Optional
TOOL_REGISTRY = {}
diff --git a/src/tools_impl.py b/src/tools_impl.py
index 7904328..dde4962 100644
--- a/src/tools_impl.py
+++ b/src/tools_impl.py
@@ -834,7 +834,7 @@ def tool_analyze_image(filepath: str, question: str = None, context: dict = None
def tool_grok_imagine(prompt: str, aspect_ratio: str = "1:1", resolution: str = "1k",
n: int = 1, context: dict = None):
"""Generate images via xAI Grok Imagine API using grok-build OAuth."""
- import base64, json as _json
+ import base64
import httpx
from src.model_router import GrokClient
diff --git a/src/tui_app.py b/src/tui_app.py
index 46dd7e3..20da8ef 100644
--- a/src/tui_app.py
+++ b/src/tui_app.py
@@ -63,7 +63,6 @@ def run_app(repl) -> None:
import shutil
from prompt_toolkit.application import Application
- from prompt_toolkit.application.current import get_app
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout import HSplit, Layout, Window
from prompt_toolkit.layout.controls import FormattedTextControl
diff --git a/src/vision.py b/src/vision.py
index 19653c9..0924a8b 100644
--- a/src/vision.py
+++ b/src/vision.py
@@ -4,12 +4,9 @@
Integrates with browser automation for visual testing and screenshot capture.
"""
-import os
-import json
import base64
import tempfile
from pathlib import Path
-from typing import Optional
# Vision backends
try:
diff --git a/tests/test_opsec_guard.py b/tests/test_opsec_guard.py
index 97e765f..418140c 100644
--- a/tests/test_opsec_guard.py
+++ b/tests/test_opsec_guard.py
@@ -66,3 +66,10 @@ def test_guard_blocks_reverse_engineering_and_mitm(tmp_path):
def test_guard_blocks_re_findings_filename(tmp_path):
assert _run_guard(tmp_path, "RE_FINDINGS.md", "anything\n") == 1
+
+
+def test_guard_blocks_ghidra_project_files(tmp_path):
+ # Ghidra / pyghidra-mcp RE output must not enter the public repo.
+ # Blocked by filename (.gpr) and by content ("ghidra").
+ assert _run_guard(tmp_path, "my_project.gpr", "binary\n") == 1
+ assert _run_guard(tmp_path, "notes.py", "# opened in ghidra to inspect the binary\n") == 1
From bde56caa15bfe4c9e87ea915b0ae3e7134018ddc Mon Sep 17 00:00:00 2001
From: ares <285551516+New1Direction@users.noreply.github.com>
Date: Tue, 9 Jun 2026 12:45:24 -0700
Subject: [PATCH 2/3] docs: add plain-language 'korgex, explained' page
A self-contained, no-dependency HTML overview written for non-technical
readers: what korgex is, the verifiable-record idea (via a plain logbook
analogy), what it can do, a 60-second quickstart, and safety. Public-safe
(korgex only). Open in any browser.
---
docs/korgex-explained.html | 237 +++++++++++++++++++++++++++++++++++++
1 file changed, 237 insertions(+)
create mode 100644 docs/korgex-explained.html
diff --git a/docs/korgex-explained.html b/docs/korgex-explained.html
new file mode 100644
index 0000000..c021188
--- /dev/null
+++ b/docs/korgex-explained.html
@@ -0,0 +1,237 @@
+
+
+
+
+
+korgex, explained — the AI coding helper that keeps the receipts
+
+
+
+
+
+
+
+ Plain-English guide
+ korgex
+ The AI coding helper that keeps the receipts.
+ Tell it what you want in everyday words — “fix the failing test,” “add a sign-up page” — and it reads your code, makes the change, runs your tests, and shows its work. Then it hands you a record you can actually check. Free, open-source, and it works with whatever AI you like.
+
+
+ free & open-source
+ works in your terminal
+ any AI: Claude · ChatGPT · Gemini · local
+ no lock-in
+
+
+
+
+
+
+ What is it, really?
+ Think of a very capable junior developer who lives in your terminal.
+ You describe a job in plain language. korgex looks through your project, figures out a plan, edits the right files, runs the tests to check it worked, and tells you exactly what it did. You stay in control — it can ask before making big changes, and you can undo anything.
+ The twist that nothing else has: everything it does is written down in a way that can’t be quietly changed later. That’s the “receipts.”
+
+
+
+ The big idea: AI you can check, not just trust
+ Most AI tools ask you to take their word for it. korgex gives you proof.
+ As it works, korgex keeps a logbook. Every step — every file it read, every command it ran — is sealed to the step before it, like links in a chain. If anyone later changes, removes, or reorders even one entry, the chain visibly breaks and korgex points to exactly where.
+
+
+
+
✓Nothing touched — the record checks out, start to finish.
+
✗Something changed — the chain breaks and shows the exact spot.
+
+ Why care? Audits, compliance, debugging, or just peace of mind — you get honest, checkable proof of what the AI actually did, instead of hoping it behaved.
+
+
+
+ What it can do
+ The everyday stuff, plus a few things no other coding assistant offers.
+
+
⌨️
Writes & fixes code
Reads your project, makes the change, and edits the exact lines — not a wall of copy-paste.
+
✅
Runs your tests
It checks its own work by running your test suite, so you get changes that actually pass.
+
🔌
Any AI you like
Claude, ChatGPT, Gemini, Grok, or a private model on your own computer. Switch anytime — no lock-in.
+
🧾
Keeps the receipts
A tamper-evident record of every step you can verify later with one command.
+
🧩
Plugs into your tools
Connects to GitHub, your files, databases and more through open “MCP” connectors.
+
↩️
Undo anything
A rewind log lets you roll files back to before any step. Nothing is one-way.
+
+
+
+
+ Try it in 60 seconds
+ If you can open a terminal, you can run korgex.
+
+
+
1
+
+
Install it
+
One line. (Needs Python 3.10 or newer.)
+
pip install -U korgex
+
+
+
+
2
+
+
Connect an AI
+
Run korgex setup and paste a key from whichever provider you use — or set one and go.
+
export ANTHROPIC_API_KEY="…" # or OPENAI_API_KEY, or a local model
+
+
+
+
3
+
+
Ask for something — then check the receipt
+
Describe the job in plain words. When it’s done, prove the run wasn’t altered.
+
+
+
+
+
+
your terminal
+
$ korgex "add a /healthz endpoint that returns 200 with uptime"
+➤ Read(routes.py) ➤ Edit(routes.py) ➤ Bash(pytest -q)
+✓ Added GET /healthz returning {"status":"ok","uptime_seconds":…} — tests pass
+
+$ korgex verify
+✓ record intact — 7 steps, chain verified end-to-end
+
+
+
+
+ Is it safe to let it loose?
+ It’s built cautious. The powerful stuff is off until you turn it on.
+
+
Asks before risky editsBig or sensitive changes show you a diff and wait for your “yes.”
+
Blocks dangerous commandsA built-in guard refuses obviously destructive shell commands — and records that it did.
+
Runs code in a sandboxWhen it executes code for you, it can be boxed off from your network and files.
+
Watches what leavesAn optional guard flags secrets or large data trying to leave your machine.
+
+
+
+
+ Why people choose it
+ Two reasons, in plain terms. You’re never locked to one AI company — korgex speaks to all the major ones and to private models, so you can pick the best (or cheapest) for the job and switch whenever. And you can prove what happened — the checkable record means you don’t have to take an AI’s word for anything. As far as we know, no other coding assistant gives you that.
+
+
+
+
+
+
+
+
+
From 6906d63a94892d2f950764a73257d96cf04d9900 Mon Sep 17 00:00:00 2001
From: ares <285551516+New1Direction@users.noreply.github.com>
Date: Tue, 9 Jun 2026 16:04:47 -0700
Subject: [PATCH 3/3] style(ruff): clear the remaining 19 lint issues + gate
ruff in CI
Cleared the non-auto-fixable ruff backlog in src/:
- removed 7 dead local variables (diff_engine, interactive, sandbox,
self_healing, system_prompt, tools_impl, webhook_server)
- split 2 semicolon statements (agent.py streaming spinners)
- renamed 3 ambiguous 'l' loop vars -> ln/lbl (korgantic, memory, webhook_server)
- dropped 1 unused import (dashboard: fastapi.HTTPException)
- marked 6 deliberate import-after-defs with '# noqa: E402 ()'
(tool-registration / circular-import-avoidance patterns)
'ruff check src/' is now clean; added a 'lint' job to tests.yml so it stays
that way. Full suite green (1,584 passing, 2 skipped).
---
.github/workflows/tests.yml | 14 ++++++++++++++
src/agent.py | 8 +++++---
src/cli.py | 2 +-
src/dashboard.py | 4 ++--
src/diff_engine.py | 8 ++------
src/interactive.py | 3 +--
src/korgantic.py | 4 ++--
src/memory.py | 2 +-
src/sandbox.py | 2 --
src/self_healing.py | 1 -
src/swarm.py | 2 +-
src/system_prompt.py | 1 -
src/tool_abstraction.py | 2 +-
src/tools_impl.py | 7 -------
src/webhook_server.py | 3 +--
15 files changed, 31 insertions(+), 32 deletions(-)
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 24e716b..36e9bab 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -28,3 +28,17 @@ jobs:
pip install -e ".[dev]"
- name: Run the suite
run: python -m pytest -q
+
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v5
+ - uses: actions/setup-python@v6
+ with:
+ python-version: "3.12"
+ - name: Install ruff
+ run: |
+ python -m pip install --upgrade pip
+ pip install "ruff>=0.1.0,<1.0.0"
+ - name: Lint src/ (ruff)
+ run: ruff check src/
diff --git a/src/agent.py b/src/agent.py
index 2596c00..5a7b5cf 100644
--- a/src/agent.py
+++ b/src/agent.py
@@ -89,7 +89,7 @@ def codeact_unconfined_warning(platform: str) -> str:
f"Bash; raw stdlib bypasses the command + egress guards ({hint}).")
-from src.agent_resolve import ( # resolution helpers, extracted to keep agent.py focused
+from src.agent_resolve import ( # noqa: E402 — extracted helpers, imported after defs to avoid a circular import
_looks_anthropic, _oauth_provider_for, _oauth_token_and_base,
_MODEL_ALIASES, subagent_tools, _resolve_params, _resolve_model,
)
@@ -709,7 +709,8 @@ def _call_anthropic_streaming(self, client, messages: list, tools: list,
) as stream:
for event in stream:
if on_first is not None:
- on_first(); on_first = None # first event → clear the thinking spinner
+ on_first()
+ on_first = None # first event → clear the thinking spinner
ev_type = getattr(event, "type", None)
if not ev_type:
continue
@@ -754,7 +755,8 @@ def _call_openai_streaming(self, client, messages: list, tools: list, on_first=N
for chunk in stream:
if on_first is not None:
- on_first(); on_first = None # first chunk → clear the thinking spinner
+ on_first()
+ on_first = None # first chunk → clear the thinking spinner
if not chunk.choices:
usage = getattr(chunk, "usage", None) or usage # usage-only final chunk
continue
diff --git a/src/cli.py b/src/cli.py
index bfe8168..fe8a2ae 100644
--- a/src/cli.py
+++ b/src/cli.py
@@ -1435,7 +1435,7 @@ def cmd_repl(resume=False):
# ── Entry Point ──────────────────────────────────────────────────────────
-import argparse
+import argparse # noqa: E402 (imported beside the entry point)
# Map subcommand name → handler. Existing bodies untouched.
def cmd_providers():
diff --git a/src/dashboard.py b/src/dashboard.py
index c2954e1..59224d1 100644
--- a/src/dashboard.py
+++ b/src/dashboard.py
@@ -13,7 +13,7 @@
from typing import Optional
try:
- from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse, JSONResponse
import uvicorn
FASTAPI_AVAILABLE = True
@@ -364,7 +364,7 @@ def start_dashboard(host: str = "0.0.0.0", port: int = 8090):
# Tool registration
-from src.tool_base import register_tool, ToolParam
+from src.tool_base import register_tool, ToolParam # noqa: E402 (registered after the module's HTML/handlers)
@register_tool("start_dashboard", "Starts the Korgex web steering dashboard.", [
diff --git a/src/diff_engine.py b/src/diff_engine.py
index 13aa95d..b34b623 100644
--- a/src/diff_engine.py
+++ b/src/diff_engine.py
@@ -110,11 +110,7 @@ def apply_search_replace(filepath: str, merge_diff: str) -> dict:
content_normalized = re.sub(r'\s+', ' ', modified)
if search_normalized in content_normalized:
- # Find the actual location and replace
- idx = content_normalized.index(search_normalized)
- # Map back to original content
- actual_start = len(modified[:idx]) # approximate
- # Try to find by line proximity
+ # Find the actual location by line proximity
search_lines = search_part.split('\n')
for i, line in enumerate(modified.split('\n')):
if search_lines[0].strip() in line:
@@ -189,7 +185,7 @@ def _apply_js_ts(filepath: str, new_content: str) -> dict:
# Tool registration
-from src.tool_base import register_tool, ToolParam
+from src.tool_base import register_tool, ToolParam # noqa: E402 (registered after the module's functions)
def apply_patch(filepath: str, patch_path: str) -> dict:
diff --git a/src/interactive.py b/src/interactive.py
index d7a5c00..b2cec20 100644
--- a/src/interactive.py
+++ b/src/interactive.py
@@ -331,8 +331,7 @@ def should_confirm_edit(file_path: str, old_content: str,
]
old_lines = len(old_content.splitlines())
- new_lines = len(new_content.splitlines())
-
+
# Check if it's a critical file
basename = os.path.basename(file_path)
if any(crit in basename for crit in CRITICAL_FILES):
diff --git a/src/korgantic.py b/src/korgantic.py
index 675c458..805b4d1 100644
--- a/src/korgantic.py
+++ b/src/korgantic.py
@@ -257,11 +257,11 @@ def _record_best_of_n(ledger, prompt, attempts, winner, parent_seq) -> int:
def multi_modal_sweep(lenses, runner, base_prompt: str) -> list:
"""Run one understand-agent per lens CONCURRENTLY — each blind to the others.
- Lenses are independent, so this is a genuine fan-out. `l=lens` binds the loop
+ Lenses are independent, so this is a genuine fan-out. `ln=lens` binds the loop
var per-thunk (avoids late-binding closure capture).
"""
thunks = [
- (lambda l=lens: runner("understand", f"[{l} lens] Analyze for this task: {base_prompt}"))
+ (lambda ln=lens: runner("understand", f"[{ln} lens] Analyze for this task: {base_prompt}"))
for lens in lenses
]
return parallel(thunks)
diff --git a/src/memory.py b/src/memory.py
index 147664f..657a21d 100644
--- a/src/memory.py
+++ b/src/memory.py
@@ -283,7 +283,7 @@ def _remove_from_index(name: str):
with open(index_path) as f:
lines = f.readlines()
- lines = [l for l in lines if f"]({name}.md)" not in l]
+ lines = [ln for ln in lines if f"]({name}.md)" not in ln]
with open(index_path, "w") as f:
f.writelines(lines)
diff --git a/src/sandbox.py b/src/sandbox.py
index 97a8da0..eba336e 100644
--- a/src/sandbox.py
+++ b/src/sandbox.py
@@ -144,8 +144,6 @@ def run_in_cloud(cmd: str, repo_url: str = None):
import subprocess
import os
- results = {"stdout": "", "stderr": "", "exit_code": 0}
-
if repo_url:
repo_name = repo_url.split("/")[-1].replace(".git", "")
os.chdir(tempfile.mkdtemp())
diff --git a/src/self_healing.py b/src/self_healing.py
index d68a1e9..f7c9eb5 100644
--- a/src/self_healing.py
+++ b/src/self_healing.py
@@ -211,7 +211,6 @@ def _apply_patch_in_sandbox(self, filepath: str, patch: str) -> dict:
temp_patch = ".korgex_heal.patch"
# Write patch file to sandbox
- escaped = patch.replace("'", "'\\''")
self.sandbox.run(
f"cat << 'KORGEOF' > {temp_patch}\n{patch}\nKORGEOF"
)
diff --git a/src/swarm.py b/src/swarm.py
index 0debc37..26427a2 100644
--- a/src/swarm.py
+++ b/src/swarm.py
@@ -257,7 +257,7 @@ def analyze_pr(self, repo_root: str, pr_description: str) -> dict:
# Tool registration
-from src.tool_base import register_tool, ToolParam
+from src.tool_base import register_tool, ToolParam # noqa: E402 (registered after the module's functions)
@register_tool("swarm_analyze_pr", "Runs multiple specialist agents (test, security, refactor) in parallel on a PR.", [
diff --git a/src/system_prompt.py b/src/system_prompt.py
index c5fa4a4..0178281 100644
--- a/src/system_prompt.py
+++ b/src/system_prompt.py
@@ -126,7 +126,6 @@ def _build_session_block(memory_text: str, workdir: str = None,
""".strip())
# Environment context
- env_info = _get_environment_info(workdir)
parts.append(f"""
# Environment
You have been invoked in the following environment:
diff --git a/src/tool_abstraction.py b/src/tool_abstraction.py
index 8c6f95f..bf068cf 100644
--- a/src/tool_abstraction.py
+++ b/src/tool_abstraction.py
@@ -447,7 +447,7 @@ def tool_search(query: str, limit: int = 5) -> dict:
# Additionally, tools registered via register_mcp_tool() route through the
# MCP server manager instead of an in-process handler.
-import inspect
+import inspect # noqa: E402 (kept beside the MCP-tool registry it supports)
# Track which tool names came from MCP servers (vs. native handlers)
_MCP_TOOLS: set[str] = set()
diff --git a/src/tools_impl.py b/src/tools_impl.py
index dde4962..3f51fc5 100644
--- a/src/tools_impl.py
+++ b/src/tools_impl.py
@@ -842,13 +842,6 @@ def tool_grok_imagine(prompt: str, aspect_ratio: str = "1:1", resolution: str =
client = GrokClient()
token = client._ensure_token()
- # Map aspect ratio to width,height arrays
- ar_map = {
- "1:1": [1, 1], "3:2": [3, 2], "2:3": [2, 3],
- "16:9": [16, 9], "9:16": [9, 16],
- }
- ar = ar_map.get(aspect_ratio, [1, 1])
-
body = {
"model": "grok-imagine-image",
"prompt": prompt,
diff --git a/src/webhook_server.py b/src/webhook_server.py
index c0ff70b..b5bb0e8 100644
--- a/src/webhook_server.py
+++ b/src/webhook_server.py
@@ -105,13 +105,12 @@ def _process_webhook(event: str, data: dict):
elif event == "pull_request" and data.get("action") in ("opened", "labeled"):
pr = data.get("pull_request", {})
- labels = [l.get("name", "").lower() for l in pr.get("labels", [])]
+ labels = [lbl.get("name", "").lower() for lbl in pr.get("labels", [])]
if "korgex" in labels:
title = pr.get("title", "")
body = pr.get("body", "")
number = pr.get("number", "")
- head_sha = pr.get("head", {}).get("sha", "")
task = f"Review PR #{number}: {title}\n{body}"
_run_korgex(task, repo, clone_url)