cluxion-agentplugin-supercoder 0.2.14__tar.gz → 0.2.15__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 (75) hide show
  1. cluxion_agentplugin_supercoder-0.2.15/.grok-briefs/ci-hermes-probe-skip.md +36 -0
  2. cluxion_agentplugin_supercoder-0.2.15/.grok-briefs/fix-empty-oldtext-infinite-loop.md +28 -0
  3. cluxion_agentplugin_supercoder-0.2.15/.grok-briefs/fix-hash-patch-race-0619.md +30 -0
  4. cluxion_agentplugin_supercoder-0.2.15/.grok-briefs/fix-live-audit-0619.md +27 -0
  5. cluxion_agentplugin_supercoder-0.2.15/.grok-briefs/p0-read-path-security.md +51 -0
  6. cluxion_agentplugin_supercoder-0.2.15/.grok-briefs/p2-doctor-security-probes.md +40 -0
  7. cluxion_agentplugin_supercoder-0.2.15/.grok-briefs/perf-0619.md +14 -0
  8. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/PKG-INFO +29 -1
  9. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/README.md +28 -0
  10. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/adapters/hermes/README.md +10 -1
  11. cluxion_agentplugin_supercoder-0.2.15/dist-merged/cluxion_agentplugin_supercoder-0.2.13-cp311-abi3-macosx_11_0_arm64.whl +0 -0
  12. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/pyproject.toml +1 -1
  13. cluxion_agentplugin_supercoder-0.2.15/rust/supercoder_index/uv.lock +8 -0
  14. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/plugin.py +11 -0
  15. cluxion_agentplugin_supercoder-0.2.15/src/cluxion_agentplugin_supercoder/slash_commands.py +73 -0
  16. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_plugin.py +11 -0
  17. cluxion_agentplugin_supercoder-0.2.15/tests/test_slash_commands.py +37 -0
  18. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/uv.lock +1 -1
  19. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/.github/workflows/ci.yml +0 -0
  20. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/.github/workflows/publish.yml +0 -0
  21. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/.gitignore +0 -0
  22. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/ARCHITECTURE.md +0 -0
  23. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/Docs/README.md +0 -0
  24. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/Docs/agent-surfaces.md +0 -0
  25. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/Docs/architecture.md +0 -0
  26. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/Docs/capabilities.md +0 -0
  27. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/Docs/design.md +0 -0
  28. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/Docs/installation.md +0 -0
  29. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/Docs/rust-architecture.md +0 -0
  30. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/Docs/tools.md +0 -0
  31. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/LICENSE +0 -0
  32. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/__init__.py +0 -0
  33. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/adapters/claude/.claude-plugin/plugin.json +0 -0
  34. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/adapters/claude/skills/supercoder/SKILL.md +0 -0
  35. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/adapters/codex/config-snippet.toml +0 -0
  36. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/plugin.yaml +0 -0
  37. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/rust/supercoder_index/Cargo.lock +0 -0
  38. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/rust/supercoder_index/Cargo.toml +0 -0
  39. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/rust/supercoder_index/pyproject.toml +0 -0
  40. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/rust/supercoder_index/src/lib.rs +0 -0
  41. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/rust/supercoder_index/src/main.rs +0 -0
  42. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/rust/supercoder_index/src/outline.rs +0 -0
  43. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/rust/supercoder_index/src/syntax.rs +0 -0
  44. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/scripts/repack_native_wheel.py +0 -0
  45. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/__init__.py +0 -0
  46. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/cli.py +0 -0
  47. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/cursor.py +0 -0
  48. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/hash_patch.py +0 -0
  49. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/line_budget.py +0 -0
  50. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/lint_gate.py +0 -0
  51. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/queue.py +0 -0
  52. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/repo_map.py +0 -0
  53. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/retry_loop.py +0 -0
  54. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/safety.py +0 -0
  55. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/syntax_gate.py +0 -0
  56. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/core/test_gate.py +0 -0
  57. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/doctor/__init__.py +0 -0
  58. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/doctor/catalog.json +0 -0
  59. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/doctor/framework.py +0 -0
  60. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/doctor/probes.py +0 -0
  61. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/runner.py +0 -0
  62. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/rust_bridge.py +0 -0
  63. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/src/cluxion_agentplugin_supercoder/schemas.py +0 -0
  64. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_cursor.py +0 -0
  65. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_doctor.py +0 -0
  66. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_hash_patch.py +0 -0
  67. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_line_budget.py +0 -0
  68. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_lint_gate.py +0 -0
  69. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_queue.py +0 -0
  70. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_repo_map.py +0 -0
  71. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_retry_loop.py +0 -0
  72. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_rust_bridge.py +0 -0
  73. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_safety.py +0 -0
  74. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_syntax_gate.py +0 -0
  75. {cluxion_agentplugin_supercoder-0.2.14 → cluxion_agentplugin_supercoder-0.2.15}/tests/test_test_gate.py +0 -0
@@ -0,0 +1,36 @@
1
+ # Task: doctor hermes-dependent critical probes must SKIP when hermes absent + env-independent test (CI fix #2)
2
+
3
+ ## Context
4
+ CI (no `hermes` binary on PATH) STILL fails: `tests/test_doctor.py` test
5
+ `test_critical_skip_marks_degraded_summary` expects summary=="degraded" but gets "fail".
6
+ Root cause: critical probes `hermes_on_path` (probes.py:27) and `hermes_oneshot_flag` (probes.py:46)
7
+ FAIL when hermes isn't on PATH (the CI build runner has no hermes). That makes summary "fail" instead
8
+ of "degraded". The sibling ultracode plugin already fixed this by skipping hermes probes when hermes
9
+ is absent — MIRROR that approach here.
10
+
11
+ ## Fix
12
+ 1. `doctor/probes.py`: `hermes_on_path` and `hermes_oneshot_flag` (and any probe needing the hermes
13
+ binary): when `shutil.which(ctx.hermes_bin)` is None, return `("skip", "hermes binary not on PATH
14
+ — cannot verify")` instead of fail. FAIL only when hermes IS present but the contract is violated.
15
+ 2. Make `tests/test_doctor.py`'s honest-summary test ENVIRONMENT-INDEPENDENT — it must pass whether or
16
+ not hermes/native exist on the machine. Best: unit-test the summary computation directly (feed a
17
+ controlled statuses dict — one critical "skip" + rest "pass" → assert "degraded"; one critical
18
+ "fail" → assert "fail"). If it runs the real doctor, monkeypatch `shutil.which` so hermes appears
19
+ absent and assert degraded (not fail).
20
+
21
+ ## Invariants (MUST hold)
22
+ - hermes absent → hermes probes SKIP → summary "degraded". hermes present + valid → PASS.
23
+ - Critical SKIP → "degraded"; critical FAIL → "fail" (unchanged). Security/cursor probes UNCHANGED.
24
+
25
+ ## Tests (CRITICAL — CI is a NO-hermes, NO-native environment)
26
+ - `uv run pytest` must be green REGARDLESS of whether hermes/native are installed. The honest-summary
27
+ test must NOT depend on the machine having hermes. (If unsure, make it a pure unit test of the
28
+ summary function.)
29
+ - `uv run ruff check .` clean.
30
+
31
+ ## Out of scope
32
+ - No version bump. No change to the P0 security fix or DB/other probes beyond skip-on-absence.
33
+
34
+ ## Done
35
+ hermes-dependent probes skip when hermes absent; honest-summary test is environment-independent;
36
+ pytest green with no hermes/native. Concise diff.
@@ -0,0 +1,28 @@
1
+ # Task: supercoder hash_patch — fix infinite loop / OOM on empty old_text (_exact_spans empty-needle)
2
+
3
+ ## Context (LIVE-VERIFIED crash — user-facing)
4
+ 0.2.12 installed. `apply_patch(path, old_text="", new_text=...)` HANGS in an infinite loop and OOMs (process killed, exit 124 on timeout / 137 on OOM). Root cause in `core/hash_patch.py::_exact_spans`:
5
+ ```python
6
+ while True:
7
+ start = text.find(needle, offset)
8
+ if start < 0:
9
+ return spans
10
+ spans.append((start, start + len(needle)))
11
+ offset = start + len(needle)
12
+ ```
13
+ With `needle == ""`, `text.find("", offset)` ALWAYS returns `offset` (never -1) and `offset += len("") == 0`, so `offset` never advances → infinite `spans.append` → unbounded memory → OOM. Reproduced live: `_exact_spans('hello', '')` times out (exit 124); `apply_patch(x, old_text='', new_text='INSERT')` times out. Any caller passing an empty `old_text` hangs the whole process. Do NOT bump the version. Do NOT touch `.grok-briefs/`.
14
+
15
+ ## Fix
16
+ 1. In `_exact_spans`: guard the empty needle at the top — `if not needle: return []` (an empty search string has no meaningful patch target).
17
+ 2. In `apply_patch`: reject empty `old_text` early, BEFORE acquiring the lock / scanning, with a clear failure:
18
+ `if not old_text: return _failed(str(path), "empty_old_text", expected_file_hash, "old_text must be non-empty")`.
19
+ 3. Audit `_candidate_spans` / `_best_fuzzy_span` for the same empty-`reference` hazard: if `reference == ""` produces a degenerate or huge candidate set (or a misleading match), guard it too (e.g. early no_match / `if not reference: return []`). Verify with a timeout test that empty reference does not hang.
20
+
21
+ ## Invariant + tests (MANDATORY)
22
+ - `apply_patch(..., old_text='')` returns a FAST failure (`success=False`, strategy/message indicating empty old_text), with NO hang — add a test asserting it returns in well under a second (e.g. wrap with a thread + timeout, or just assert it returns and check the strategy).
23
+ - `_exact_spans(text, '')` returns `[]` immediately.
24
+ - Non-empty old_text behavior is UNCHANGED (existing exact / fuzzy / ambiguity / concurrency tests stay green).
25
+ - `uv run pytest` GREEN; `uv run ruff check .` pass.
26
+
27
+ ## Done
28
+ - Empty old_text no longer hangs/OOMs; it returns a fast, clear failure. Tests added proving no-hang. No version bump. No `.grok-briefs/` edits. Concise diff.
@@ -0,0 +1,30 @@
1
+ # Task: supercoder hash_patch — fix concurrent lost-update race (flock on a replaced inode)
2
+
3
+ ## Context
4
+ A live stress run found `tests/test_hash_patch.py::test_concurrent_patches_no_lost_update` is FLAKY (~1 in 12-15 runs): 8 concurrent ThreadPoolExecutor workers each apply a distinct, non-overlapping patch to the same file. Occasionally one marker stays `# UNIQUE_PATCH_i_START` (a lost update) even though every worker returns `success=True`. This is a real concurrency bug: two agents/threads patching the same file can silently lose one patch (user-facing code loss). Do NOT bump the version (deploy handled separately). Do NOT touch `.grok-briefs/`.
5
+
6
+ ## Root cause (verified by reading `core/hash_patch.py`)
7
+ `_exclusive_lock(path)` does `os.open(path)` then `fcntl.flock(fd, LOCK_EX)`. But `_commit` → `_atomic_write` finalizes via `os.replace(tmp, path)`, which swaps `path` to a NEW inode. The flock is held on the OLD inode, so the lock does not actually serialize writers across the replace. Sequence that loses a patch:
8
+ 1. Thread A: `os.open(path)` → inode1; acquires flock(inode1).
9
+ 2. Thread B: `os.open(path)` → inode1 (before A's replace); blocks on flock(inode1).
10
+ 3. A: reads inode1, patches its marker, `_atomic_write` → `os.replace` → `path` now = inode2; A releases flock(inode1).
11
+ 4. B: acquires flock(inode1), but reads **inode1** (STALE — A's change is in inode2, not inode1), patches only its own marker, `os.replace` → `path` = inode3. **A's patch is lost.**
12
+ Because the lock lives on a file that is replaced out from under it, two writers can serialize on DIFFERENT inodes and lose updates. Additional window: when `path` does not exist, `_exclusive_lock` yields WITHOUT any lock (`if fcntl is None or not path.exists(): yield; return`), so concurrent creation is unserialized too.
13
+
14
+ ## Fix
15
+ Lock on a STABLE sidecar lock file whose inode never changes, instead of on the target file that gets replaced:
16
+ - Choose a fixed lock path next to the target, e.g. `lock_path = path.parent / f".{path.name}.cluxion-lock"`.
17
+ - `_exclusive_lock` opens the lock file with `os.open(lock_path, os.O_CREAT | os.O_RDWR, 0o600)`, `fcntl.flock(fd, fcntl.LOCK_EX)`, yields, then `fcntl.flock(fd, fcntl.LOCK_UN)` and `os.close(fd)` in `finally`.
18
+ - Do NOT delete the lock file (deleting it reintroduces an inode/unlink race); leaving an empty `.cluxion-lock` is harmless and gets reused.
19
+ - The lock file inode is never `os.replace`d, so all threads AND processes serialize correctly regardless of the target's atomic replace.
20
+ - Acquire the lock even when `path` does not yet exist (the lock file is always creatable), so creation is serialized too. Keep the actual "file not found" handling in `apply_patch` as-is (it returns missing_file), but acquire/serialize first if you choose to create files later — at minimum, the existing-file patch path must serialize.
21
+ - Graceful degrade when `fcntl is None` (non-POSIX, e.g. Windows): fall back to a module-level `threading.Lock()` so at least same-process thread safety holds (the test is single-process multithread, so this fallback alone fixes the test on platforms without fcntl).
22
+ - Read-modify-write (`path.read_text` → match → `_atomic_write`) must stay entirely INSIDE the lock (it already is — keep it).
23
+
24
+ ## Invariant + tests
25
+ - Make `test_concurrent_patches_no_lost_update` pass DETERMINISTICALLY. Add a stress test that runs the 8-worker scenario in a loop (e.g. 50 iterations) and asserts ZERO lost updates across all iterations (all 8 markers end `_DONE`, none `_START`, every worker success).
26
+ - No regression: existing exact / fuzzy / stale-hash / `test_atomic_write_interruption_leaves_original_intact` tests still pass.
27
+ - `uv run pytest` GREEN, `uv run ruff check .` pass.
28
+
29
+ ## Done criteria
30
+ - Race fixed at the lock layer (sidecar lock file + threading.Lock fallback). Deterministic stress test added. No version bump. No `.grok-briefs/` edits. Concise diff summary.
@@ -0,0 +1,27 @@
1
+ # Task: supercoder — fix 3 live-audit defects (1 P2 false-degraded doctor + 2 P3)
2
+
3
+ ## Context
4
+ Installed build is 0.2.9. A live adversarial audit (running the installed site-packages build) confirmed 3 REAL defects. Fix all in repo `src/`. Do NOT regress the 15 live-verified working functions. Do NOT bump the version in pyproject (deploy handled separately). Do NOT touch `.grok-briefs/`.
5
+
6
+ ## Defect 1 (P2 — FALSE HEALTH ALARM): `cluxion-supercoder doctor` reports 'degraded' / exit 1 on a fully-healthy install
7
+ Running the plugin's own self-doctor on a correct install yields a red 'degraded' verdict and a non-zero exit status even though every real probe passes and the plugin is fully functional. Cause: a catalog check (`hermes_contract_tool_registration`) is marked 'critical' but has NO registered probe, so it is skipped, and a critical-skip is treated as failure → 'degraded'. This directly contradicts the user's standing requirement that the built-in doctor be a trustworthy self-monitor (a false-negative health report wastes turns and erodes trust).
8
+ Fix (pick the correct one):
9
+ - PREFERRED: register a real probe for `hermes_contract_tool_registration` — it IS verifiable in-process (the `register()` wiring is already exercised by the `handler_exception_coverage` probe), so the probe can confirm the contract tools are registered and return pass; OR
10
+ - If it genuinely cannot be probed in this environment, treat a critical check that is SKIPPED (not failed) as NOT degrading overall health — i.e. overall status is 'degraded' only on a real FAIL, while an unprobeable critical is reported as 'skip' and overall stays ok.
11
+ Apply the same principle consistently: a skip is not a fail.
12
+ Invariant + test: on a healthy install, `cluxion-supercoder doctor` → overall ok=True, exit 0; a genuine failure still → degraded/exit 1. Add an environment-independent test (monkeypatch) mirroring the pattern already used elsewhere.
13
+
14
+ ## Defect 2 (P3 — HONESTY): repo_map silently drops files beyond max_files cap
15
+ REPO_MAP_SCHEMA / docstring promises "files beyond the character budget are counted in files_omitted, never silently dropped." But when `scan_repo` hits the `max_files` cap, the extra files are dropped with `files_omitted=0` and `truncated=false`, so the host model receives a PARTIAL orientation map believing it is COMPLETE.
16
+ Fix: surface the file-count cap. When `scan_repo` hits `max_files`, either expose `files_scanned` vs a separate `files_capped` count, or set `truncated=True` and/or add the capped remainder to `files_omitted`, so the model knows the map is incomplete. Keep the existing character-budget omission accounting intact.
17
+ Invariant + test: a repo with more files than `max_files` → result has `truncated=True` or `files_omitted>0` reflecting the capped files (never `omitted=0`+`truncated=false` while files were actually dropped).
18
+
19
+ ## Defect 3 (P3): is_coding_task naive substring match → false positives
20
+ `is_coding_task` uses raw substring matching, so non-coding prompts are misrouted into the coding queue: 'latest' matches 'test', 'prefix' matches 'fix', etc. Bias is fail-toward-coding so output isn't wrong, but it wastes a repo scan and emits a misleading 'coding task' classification + repo_map for clearly non-coding prompts.
21
+ Fix: use word-boundary matching (regex `\b<kw>\b` or tokenize-and-compare) instead of raw substring, so 'latest' does not match 'test' and 'prefix' does not match 'fix'. Keep the genuine-non-coding bypass branch.
22
+ Invariant + test: 'what is the latest news' → NOT classified coding; 'fix the bug in main.py' → classified coding. Add tests for the false-positive words ('latest', 'prefix', 'contest') and true positives ('fix', 'test', 'refactor', 'debug').
23
+
24
+ ## Done criteria
25
+ - `uv run pytest` GREEN. `uv run ruff check .` pass.
26
+ - New tests for Defect 1 (healthy → ok/exit0, env-independent), Defect 2 (capped files surfaced), Defect 3 (word-boundary classification).
27
+ - No version bump in pyproject. No edits under `.grok-briefs/`. Provide a concise per-defect diff summary.
@@ -0,0 +1,51 @@
1
+ # Task: Fix 2 P0 security holes in supercoder's read path (live-verified)
2
+
3
+ ## Context (both confirmed by live execution)
4
+ 1. **Secret-file read leak** — `read_window_tool` (src/cluxion_agentplugin_supercoder/runner.py:73-92)
5
+ and `cursor.read_window` (src/cluxion_agentplugin_supercoder/core/cursor.py:30-35) BYPASS the
6
+ security gate. `patch_tool` calls `pre_tool_gate` (runner.py:98) and refuses secret files, but the
7
+ read path never does. Live: `read_window_tool({'cwd':'/tmp','path':'.env'})` returned ok=True and
8
+ leaked `AWS_SECRET=...`; also leaks `credentials`, `sub/.env`. `supercoder_read_window` is a
9
+ REGISTERED host tool (plugin.py:25-31), so any agent can exfiltrate `.env`/`id_rsa`/`credentials`/
10
+ secrets. `tests/test_safety.py::test_secret_paths_blocked` proves the intent for the patch path.
11
+
12
+ 2. **Workspace sibling-directory escape** — `cursor.read_window` (core/cursor.py:33) uses
13
+ `str(path).startswith(str(root.resolve()))`. This is string-prefix, not containment. Live:
14
+ `read_window(Path('/tmp/work'), '../work2/secret.py')` leaked because `/tmp/work2` string-prefixes
15
+ `/tmp/work`. Plain `../` is caught, but a sibling dir sharing the name prefix escapes.
16
+ `safety._path_gate` (safety.py:~54) already does this correctly with `is_relative_to`, and
17
+ `tests/test_safety.py::test_sibling_directory_prefix_is_not_containment` regression-guards the
18
+ patch path.
19
+
20
+ ## Fix (mirror the patch path's existing, correct gate — do NOT invent a new policy)
21
+ 1. Route BOTH `cursor.read_window` and `read_window_tool` through the SAME secret-blocking +
22
+ containment gate the patch path uses (`pre_tool_gate` / `_path_gate` / `_SECRET_PARTS` in
23
+ safety.py and runner.py:98). Reading `.env`/`credentials`/`secrets`/`id_rsa` (the same
24
+ `_SECRET_PARTS` set patch uses) must return `ok=False` with the SAME message/shape as
25
+ `patch_tool` ("secret file access blocked").
26
+ 2. core/cursor.py:33 — replace the `startswith` prefix check with
27
+ `if not path.is_relative_to(root.resolve()): <reject>`. This closes BOTH plain `../` traversal
28
+ and the sibling-prefix escape.
29
+ 3. Legitimate in-workspace, non-secret reads MUST keep working unchanged (same return shape).
30
+
31
+ ## Reference
32
+ - `safety.py`: `_path_gate` (~line 54, uses `is_relative_to`), `pre_tool_gate`, `_SECRET_PARTS`.
33
+ - `runner.py:98`: `patch_tool` calls `pre_tool_gate` — mirror exactly for the read path.
34
+
35
+ ## Tests (MUST pass; add the missing coverage — tests/test_cursor.py currently has ZERO escape/secret tests)
36
+ - read_window / read_window_tool on `.env` and `credentials` → blocked (ok=False, secret message).
37
+ - read_window with a sibling-prefix escape (`/tmp/work` + `../work2/x`) → blocked.
38
+ - read_window with plain `../../etc/passwd` → blocked.
39
+ - read_window on a normal in-workspace file → STILL returns content (ok=True).
40
+ - All existing `tests/test_safety.py` and other tests stay green. `uv run pytest` green.
41
+
42
+ ## Out of scope (DO NOT)
43
+ - No version bump, build, wheel, pip install, or publish.
44
+ - No change to patch-path semantics (already correct) or to the native backend.
45
+ - Optional ONLY if zero-risk: doctor probe `_json_result`→`_wrap` misname (probes.py:111,114). The
46
+ two security fixes are the priority — never let probe cleanup endanger them.
47
+
48
+ ## Done
49
+ read_window (both entry points) is gated identically to patch (secret-block + is_relative_to
50
+ containment); new tests prove both P0s are closed and normal reads still work; all tests green.
51
+ Report a concise diff summary and confirm the live exploits no longer reproduce.
@@ -0,0 +1,40 @@
1
+ # Task: Complete supercoder doctor coverage — register the missing critical security probes
2
+
3
+ ## Context
4
+ The doctor catalog declares 31 checks but probes.py registers only 12. The probes that would
5
+ catch security holes are UNREGISTERED and silently skip — which is exactly how the two P0
6
+ read-path holes (now FIXED: read_window secret leak + sibling-dir escape) reached a shipped
7
+ build. Make doctor actually verify them so a regression can never silently ship again.
8
+
9
+ ## Implement (in doctor/probes.py — follow the EXISTING probe registration pattern)
10
+ Register these catalog checks as real probes (the catalog's `detection_method` describes each test):
11
+ 1. `path_security_secrets_blocked` (critical) — assert BOTH `read_window_tool` AND `patch_tool` on a
12
+ `.env` (and another secret name like `credentials`) return ok=False with the secret-blocked
13
+ message. This is the regression guard for P0-1.
14
+ 2. `hermes_context_workspace_root` (critical) — assert `read_window` on `../../../etc/passwd` AND on
15
+ a sibling-prefix escape (workspace `/work` + path `../work2/x`) are blocked. Regression guard for
16
+ P0-2. (Both fixes already live in core/cursor.py + runner.py via pre_tool_gate + is_relative_to.)
17
+ 3. `patch_cursor_validity`, `stale_cursor_protection_enforced` (critical) — implement per their
18
+ catalog detection_method if feasible standalone. If a probe genuinely cannot run standalone,
19
+ make the doctor SUMMARY downgrade to "degraded" whenever a CRITICAL catalog check is unregistered
20
+ — never report green while a critical check silently skips.
21
+ 4. Fix `handler_exception_coverage` (probes.py:111,114): it imports a nonexistent `_json_result`;
22
+ the real symbol is `_wrap` (plugin.py:117). Point the probe at `_wrap` (wrap a raising callback,
23
+ assert it degrades to error-JSON), or delete the dead probe.
24
+
25
+ ## Invariants (MUST hold)
26
+ - Probes are READ-ONLY and use temp dirs; never mutate the real workspace or touch real secrets.
27
+ - Existing passing checks stay green; NO behavior change to runtime tools or the read-path fix.
28
+
29
+ ## Tests (must pass; prove the probes have teeth)
30
+ - `uv run pytest` green. The new security probes PASS now (P0s fixed).
31
+ - Add a focused test proving each security probe DETECTS an ungated read (e.g. monkeypatch the gate
32
+ to a no-op and assert the probe reports fail) — so the probe can't silently pass when broken.
33
+
34
+ ## Out of scope
35
+ - No version bump / build / publish. No change to the read-path security fix (already correct).
36
+
37
+ ## Done
38
+ doctor registers + passes path_security_secrets_blocked and hermes_context_workspace_root (with
39
+ teeth-proving tests), handler_exception_coverage points at _wrap, summary never reports green while
40
+ a critical check silently skips, all tests green. Concise diff summary.
@@ -0,0 +1,14 @@
1
+ # Task: supercoder perf — _best_fuzzy_span quick_ratio pruning (59ms/call on large files, byte-identical)
2
+
3
+ ## Context
4
+ 0.2.11 installed. Direct profiling found `_best_fuzzy_span` is a REAL hot-path: fuzzy `apply_patch` on a 500-line file measured 59.3ms/call. The fuzzy path runs whenever exact match fails (common in code editing). Do NOT bump the version. Do NOT touch `.grok-briefs/`.
5
+
6
+ ## Optimization (LOW risk, MUST be byte-identical)
7
+ File: `src/cluxion_agentplugin_supercoder/core/hash_patch.py` `_best_fuzzy_span` (152) + `_candidate_spans` (132).
8
+ Current: for EVERY candidate span (all start_line × width-in-[target ± MAX_LINE_DRIFT] combinations) it computes `SequenceMatcher(None, block, reference, autojunk=False).ratio()` — O(spans × n²). On a 500-line file ≈ 59ms.
9
+ Fix: use `SequenceMatcher`'s cheap UPPER-BOUND pruning. Construct ONE `sm = SequenceMatcher(autojunk=False)` and `sm.set_seq2(reference)` once (reference is fixed across candidates — this also avoids re-building the b2j index every candidate, a win by itself). Then per block: `sm.set_seq1(block)`, check the cheap upper bounds `sm.real_quick_ratio()` then `sm.quick_ratio()` (both are guaranteed `>= ratio()`), and only call the full `sm.ratio()` when the upper bound could still matter — i.e. when `quick_ratio >= best_score - AMBIGUITY_MARGIN` (use the existing ambiguity margin, 0.015) AND `>= 0` relative to whatever the code needs. Because `quick_ratio() >= ratio()`, any candidate whose `quick_ratio < best_score - margin` can NEITHER become the new best NOR fall within the ambiguity band, so skipping its `ratio()` is EXACTLY equivalent.
10
+ CRITICAL — byte-identical result REQUIRED: the returned `(start, end, block, score, ambiguous)` MUST be identical to the current implementation for ALL inputs. The `ambiguous` flag logic (score within 0.015 of best, non-overlapping windows, `>= DEFAULT_FUZZY_THRESHOLD`) must evaluate to the same value. Be CONSERVATIVE: only prune a candidate's `ratio()` when its `quick_ratio` is strictly below the margin that could affect best OR ambiguity. When in doubt, compute `ratio()` (correctness over speed). Keep the iteration order identical so ties break the same way.
11
+ Invariant: `_best_fuzzy_span` returns IDENTICAL output vs current for all inputs. Add a test comparing OLD vs NEW (keep a copy of the old function) across: exact-best-in-middle, two near-ties triggering `ambiguous=True`, a clear single best, and no-match (None). Assert identical `(start,end,score,ambiguous)`. Bench the large-file speedup.
12
+
13
+ ## Done
14
+ - quick_ratio/real_quick_ratio pruning + set_seq2-once, IDENTICAL result, large-file fuzzy much faster. `uv run pytest` GREEN (esp. existing fuzzy + ambiguity + concurrency tests), `uv run ruff check .` pass. No version bump. No `.grok-briefs/` edits. Concise diff.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cluxion-agentplugin-supercoder
3
- Version: 0.2.14
3
+ Version: 0.2.15
4
4
  Summary: Universal agent coding harness plugin: cursor logic, safe patch, line budget, Rust index, test gates.
5
5
  Project-URL: Homepage, https://github.com/cluxion/cluxion-Agentplugin-supercoder
6
6
  Project-URL: Repository, https://github.com/cluxion/cluxion-Agentplugin-supercoder
@@ -73,6 +73,20 @@ cluxion-supercoder doctor --json # 구조화 출력
73
73
 
74
74
  Hermes 안에서는 `supercoder_doctor` 도구로도 노출됩니다.
75
75
 
76
+ ## Hermes 슬래시 커맨드 (0.2.15+)
77
+
78
+ ```
79
+ /supercoder fix failing auth tests and run gates
80
+ /supercoder-doctor
81
+ ```
82
+
83
+ | 슬래시 | 용도 |
84
+ |---|---|
85
+ | `/supercoder <task>` | 슈퍼코더 코딩 모드 — plan + repo map을 에이전트에 전달 |
86
+ | `/supercoder-doctor` | doctor (CLI와 동일) |
87
+
88
+ `/` 입력 시 `/super`로 필터 가능 · `deliver=agent`로 작업 지시가 에이전트 턴에 전달됩니다.
89
+
76
90
  ## 라이선스
77
91
 
78
92
  Apache-2.0
@@ -128,6 +142,20 @@ cluxion-supercoder doctor --json # structured output
128
142
 
129
143
  Also exposed inside Hermes as the `supercoder_doctor` tool.
130
144
 
145
+ ## Hermes slash commands (0.2.15+)
146
+
147
+ ```
148
+ /supercoder fix failing auth tests and run gates
149
+ /supercoder-doctor
150
+ ```
151
+
152
+ | Slash | Purpose |
153
+ |---|---|
154
+ | `/supercoder <task>` | Supercoder coding mode — plan + repo map sent to the agent |
155
+ | `/supercoder-doctor` | Run doctor (same as CLI) |
156
+
157
+ Filter with `/super` · uses `deliver=agent` so the harness directive becomes the next agent turn.
158
+
131
159
  ## License
132
160
 
133
161
  Apache-2.0
@@ -50,6 +50,20 @@ cluxion-supercoder doctor --json # 구조화 출력
50
50
 
51
51
  Hermes 안에서는 `supercoder_doctor` 도구로도 노출됩니다.
52
52
 
53
+ ## Hermes 슬래시 커맨드 (0.2.15+)
54
+
55
+ ```
56
+ /supercoder fix failing auth tests and run gates
57
+ /supercoder-doctor
58
+ ```
59
+
60
+ | 슬래시 | 용도 |
61
+ |---|---|
62
+ | `/supercoder <task>` | 슈퍼코더 코딩 모드 — plan + repo map을 에이전트에 전달 |
63
+ | `/supercoder-doctor` | doctor (CLI와 동일) |
64
+
65
+ `/` 입력 시 `/super`로 필터 가능 · `deliver=agent`로 작업 지시가 에이전트 턴에 전달됩니다.
66
+
53
67
  ## 라이선스
54
68
 
55
69
  Apache-2.0
@@ -105,6 +119,20 @@ cluxion-supercoder doctor --json # structured output
105
119
 
106
120
  Also exposed inside Hermes as the `supercoder_doctor` tool.
107
121
 
122
+ ## Hermes slash commands (0.2.15+)
123
+
124
+ ```
125
+ /supercoder fix failing auth tests and run gates
126
+ /supercoder-doctor
127
+ ```
128
+
129
+ | Slash | Purpose |
130
+ |---|---|
131
+ | `/supercoder <task>` | Supercoder coding mode — plan + repo map sent to the agent |
132
+ | `/supercoder-doctor` | Run doctor (same as CLI) |
133
+
134
+ Filter with `/super` · uses `deliver=agent` so the harness directive becomes the next agent turn.
135
+
108
136
  ## License
109
137
 
110
138
  Apache-2.0
@@ -8,4 +8,13 @@ cluxion-supercoder check
8
8
 
9
9
  Tools: `supercoder_plan`, `supercoder_read_window`, `supercoder_patch`, `supercoder_cursor_map`, `supercoder_test_gate`, `supercoder_brief`
10
10
 
11
- 연결된 AI가 코딩 task에서 위 도구를 순서대로 호출합니다.
11
+ 연결된 AI가 코딩 task에서 위 도구를 순서대로 호출합니다.
12
+
13
+ ## 슬래시 (0.2.15+)
14
+
15
+ ```
16
+ /supercoder <task> # 코딩 모드 — plan + 패치/게이트 워크플로우 지시
17
+ /supercoder-doctor
18
+ ```
19
+
20
+ `/` 입력 시 `/super`로 필터 · `/supercoder`는 에이전트 턴으로 전달(`deliver=agent`).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cluxion-agentplugin-supercoder"
7
- version = "0.2.14"
7
+ version = "0.2.15"
8
8
  description = "Universal agent coding harness plugin: cursor logic, safe patch, line budget, Rust index, test gates."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,8 @@
1
+ version = 1
2
+ revision = 3
3
+ requires-python = ">=3.11"
4
+
5
+ [[package]]
6
+ name = "supercoder-index-native"
7
+ version = "0.2.0"
8
+ source = { editable = "." }
@@ -16,6 +16,7 @@ from cluxion_agentplugin_supercoder.schemas import (
16
16
  SYNTAX_GATE_SCHEMA,
17
17
  TEST_GATE_SCHEMA,
18
18
  )
19
+ from cluxion_agentplugin_supercoder.slash_commands import handle_supercoder
19
20
 
20
21
 
21
22
  def register(ctx: object) -> None:
@@ -116,10 +117,20 @@ def register(ctx: object) -> None:
116
117
  register_command = getattr(ctx, "register_command", None)
117
118
  if callable(register_command):
118
119
 
120
+ def _slash_supercoder(raw_args: str) -> str:
121
+ return handle_supercoder(raw_args, ctx)
122
+
119
123
  def _slash_supercoder_doctor(raw_args: str) -> str:
120
124
  del raw_args
121
125
  return _handle_supercoder_doctor({})
122
126
 
127
+ register_command(
128
+ "supercoder",
129
+ _slash_supercoder,
130
+ description="Supercoder coding mode — plan, verified patches, test gates",
131
+ args_hint="<task>",
132
+ deliver="agent",
133
+ )
123
134
  register_command(
124
135
  "supercoder-doctor",
125
136
  _slash_supercoder_doctor,
@@ -0,0 +1,73 @@
1
+ """Hermes slash commands for supercoder (/supercoder, /supercoder-doctor)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from cluxion_agentplugin_supercoder import runner
9
+
10
+ SUPERCODER_HELP = """\
11
+ /supercoder <task> — Enter supercoder coding mode
12
+
13
+ Runs supercoder_plan and sends a coding harness directive to the agent.
14
+
15
+ Workflow the agent must follow:
16
+ supercoder_plan → supercoder_read_window → supercoder_patch
17
+ → supercoder_syntax_gate → supercoder_lint_gate → supercoder_test_gate → supercoder_brief
18
+
19
+ Examples:
20
+ /supercoder fix failing auth tests in src/auth/
21
+ /supercoder add pagination to the users API with tests
22
+
23
+ Notes:
24
+ - Patches are hash-verified; empty old_text is rejected immediately
25
+ - Repo map is attached to the plan when available
26
+ - Diagnostics: /supercoder-doctor
27
+ """
28
+
29
+
30
+ def build_supercoder_directive(task: str, plan_payload: dict[str, object]) -> str:
31
+ plan_block = json.dumps(plan_payload, ensure_ascii=False, indent=2)
32
+ return (
33
+ "[SUPERCODER MODE]\n"
34
+ "Use the supercoder toolset for all code changes in this task.\n\n"
35
+ f"Task: {task}\n\n"
36
+ "Required sequence:\n"
37
+ "1. Use the supercoder_plan result below (do not skip repo_map)\n"
38
+ "2. supercoder_read_window before each edit\n"
39
+ "3. supercoder_patch only with verified old_text hashes\n"
40
+ "4. supercoder_syntax_gate → supercoder_lint_gate → supercoder_test_gate after edits\n"
41
+ "5. supercoder_brief with files_changed, tests_run, verification_status\n\n"
42
+ f"supercoder_plan:\n{plan_block}"
43
+ )
44
+
45
+
46
+ def handle_supercoder(raw_args: str, ctx: object | None = None) -> str:
47
+ del ctx # Hermes deliver=agent routes the return value to the agent turn
48
+ task = raw_args.strip()
49
+ if not task or task.lower() in {"help", "-h", "--help"}:
50
+ return SUPERCODER_HELP
51
+ try:
52
+ result = runner.plan({"prompt": task, "cwd": str(Path.cwd())})
53
+ payload = json.loads(result.to_json())
54
+ if not payload.get("ok"):
55
+ return f"supercoder error: {payload.get('error', 'plan failed')}"
56
+ body = payload.get("result", payload)
57
+ if not isinstance(body, dict):
58
+ body = {"plan": body}
59
+ if body.get("mode") == "bypass":
60
+ return (
61
+ f"supercoder: not a coding task ({body.get('reason', 'bypass')}).\n"
62
+ "Rephrase as a concrete code change request."
63
+ )
64
+ return build_supercoder_directive(task, body)
65
+ except Exception as exc:
66
+ return f"supercoder error: {exc}"
67
+
68
+
69
+ __all__ = [
70
+ "SUPERCODER_HELP",
71
+ "build_supercoder_directive",
72
+ "handle_supercoder",
73
+ ]
@@ -11,12 +11,23 @@ def test_register_exposes_all_tools() -> None:
11
11
  class FakeCtx:
12
12
  def __init__(self):
13
13
  self.tools = {}
14
+ self.commands = {}
14
15
 
15
16
  def register_tool(self, name, toolset=None, schema=None, handler=None, emoji=None):
16
17
  self.tools[name] = {"toolset": toolset, "schema": schema}
17
18
 
19
+ def register_command(self, name, handler, description="", args_hint="", deliver="output"):
20
+ self.commands[name] = {
21
+ "handler": handler,
22
+ "description": description,
23
+ "args_hint": args_hint,
24
+ "deliver": deliver,
25
+ }
26
+
18
27
  ctx = FakeCtx()
19
28
  plugin.register(ctx)
29
+ assert "supercoder" in ctx.commands
30
+ assert "supercoder-doctor" in ctx.commands
20
31
  assert sorted(ctx.tools) == [
21
32
  "supercoder_brief",
22
33
  "supercoder_cursor_map",
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ from cluxion_agentplugin_supercoder.slash_commands import (
6
+ SUPERCODER_HELP,
7
+ build_supercoder_directive,
8
+ handle_supercoder,
9
+ )
10
+
11
+
12
+ def test_supercoder_help_without_args() -> None:
13
+ assert "supercoder_plan" in handle_supercoder("")
14
+ assert handle_supercoder("help") == SUPERCODER_HELP
15
+
16
+
17
+ def test_build_directive_includes_task() -> None:
18
+ body = {"mode": "coding_queue", "units": [{"id": "u1", "goal": "fix tests"}]}
19
+ text = build_supercoder_directive("fix auth tests", body)
20
+ assert "[SUPERCODER MODE]" in text
21
+ assert "fix auth tests" in text
22
+ assert "supercoder_plan" in text
23
+
24
+
25
+ def test_handle_supercoder_bypass(tmp_path, monkeypatch) -> None:
26
+ monkeypatch.chdir(tmp_path)
27
+ out = handle_supercoder("what is the weather today?")
28
+ assert "not a coding task" in out
29
+
30
+
31
+ def test_handle_supercoder_returns_directive(tmp_path, monkeypatch) -> None:
32
+ monkeypatch.chdir(tmp_path)
33
+ (tmp_path / "a.py").write_text("x=1\n", encoding="utf-8")
34
+ out = handle_supercoder("refactor a.py and fix the failing unit tests")
35
+ assert "[SUPERCODER MODE]" in out
36
+ plan = json.loads(out.split("supercoder_plan:\n", 1)[1])
37
+ assert plan.get("mode") == "coding_queue"
@@ -160,7 +160,7 @@ wheels = [
160
160
 
161
161
  [[package]]
162
162
  name = "cluxion-agentplugin-supercoder"
163
- version = "0.2.14"
163
+ version = "0.2.15"
164
164
  source = { editable = "." }
165
165
  dependencies = [
166
166
  { name = "psutil" },