hexcli 2.14.0__tar.gz → 2.16.0__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. {hexcli-2.14.0 → hexcli-2.16.0}/CHANGELOG.md +57 -0
  2. {hexcli-2.14.0 → hexcli-2.16.0}/PKG-INFO +1 -1
  3. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/__init__.py +1 -1
  4. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/agent.py +6 -1
  5. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/tools.py +11 -0
  6. {hexcli-2.14.0 → hexcli-2.16.0}/.gitignore +0 -0
  7. {hexcli-2.14.0 → hexcli-2.16.0}/Hex CLI.cmd +0 -0
  8. {hexcli-2.14.0 → hexcli-2.16.0}/LICENSE +0 -0
  9. {hexcli-2.14.0 → hexcli-2.16.0}/README.md +0 -0
  10. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/assets/hexcli.ico +0 -0
  11. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/assets/hexcli.png +0 -0
  12. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/cancel.py +0 -0
  13. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/chatlog.py +0 -0
  14. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/commands.py +0 -0
  15. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/compaction.py +0 -0
  16. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/config.py +0 -0
  17. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/diffview.py +0 -0
  18. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/distribution.py +0 -0
  19. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/doctor.py +0 -0
  20. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/editing.py +0 -0
  21. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/http_client.py +0 -0
  22. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/launcher.py +0 -0
  23. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/lineedit.py +0 -0
  24. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/llm.py +0 -0
  25. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/lockfile.py +0 -0
  26. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/markdown_stream.py +0 -0
  27. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/memory.py +0 -0
  28. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/network.py +0 -0
  29. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/parsing.py +0 -0
  30. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/paths.py +0 -0
  31. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/prompts.py +0 -0
  32. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/psparse.py +0 -0
  33. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/repl.py +0 -0
  34. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/safety.py +0 -0
  35. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/sessions.py +0 -0
  36. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/setup_wizard.py +0 -0
  37. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/statusbar.py +0 -0
  38. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/stream_render.py +0 -0
  39. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/telemetry.py +0 -0
  40. {hexcli-2.14.0 → hexcli-2.16.0}/hexcli/ui.py +0 -0
  41. {hexcli-2.14.0 → hexcli-2.16.0}/install.ps1 +0 -0
  42. {hexcli-2.14.0 → hexcli-2.16.0}/launcher.py +0 -0
  43. {hexcli-2.14.0 → hexcli-2.16.0}/pyproject.toml +0 -0
  44. {hexcli-2.14.0 → hexcli-2.16.0}/shellai.example.json +0 -0
@@ -4,6 +4,63 @@ Full evidence for every claim below — including the experiments that failed
4
4
  lives in `docs/V2_PLAN.md` §14. Numbers are pass^k over repeated live runs on
5
5
  the Hexagon NPU, not single-run anecdotes.
6
6
 
7
+ ## 2.16.0 — 2026-09-18
8
+
9
+ A minor release: a tool result the model reads changes (`find_files` returns
10
+ a listing where it returned an argument error).
11
+
12
+ > **Gate: PASS** on the pinned 24-case set after one recheck (`agentic-4`
13
+ > and `agentic-5`, neither of which calls `find_files`, missed once at 5
14
+ > runs and then held at 6). Own arm, fresh server, seed 20260918, 8 invalid
15
+ > of 245 runs. Against the v2.11.1 12-run baseline: run-level 402/514 vs
16
+ > 174/222, +0.2 %, Fisher p = 1.00; pass^k 26 → 30 of 46 shared cases,
17
+ > McNemar p = 0.29. **The alias did not fire in this arm**: all ten
18
+ > `find_files` calls carried `glob` (two also sent `pattern`, and `glob`
19
+ > won as designed), so the arm is evidence of no regression, and the case
20
+ > for the change rests on the 21 of 170 recorded calls that would have
21
+ > hit it. `findfile-1` 2/5, all three misses the decoy answer, which is
22
+ > the separate finding still open. Smoke 10/10 on a fresh server.
23
+
24
+ - `find_files` takes `pattern` as its glob when no `glob` is sent. The
25
+ model names the file under the key `search_files` uses in 21 of the 170
26
+ `find_files` calls on record, across eight cases and one of the owner's
27
+ sessions, and each one came back "find_files requires 'glob'". In the
28
+ 2.12.0 arm three of `findfile-1`'s twelve runs spent two to three steps
29
+ on it and one reached the step limit that way. The value is what the
30
+ model meant to look for either way, and a bare name is a valid glob for
31
+ that exact name; `glob` still wins when both are sent, and a call with
32
+ neither is still an error.
33
+
34
+ ## 2.15.0 — 2026-09-18
35
+
36
+ A minor release: one tool error the model reads is new (`edit_file` on
37
+ identical strings).
38
+
39
+ > **Gate: PASS** on the pinned 24-case set after one recheck (`agentic-5`
40
+ > 4/5, a counting answer with no edit in it, then 6/6). Own arm, fresh
41
+ > server, seed 20260917, 9 invalid of 245 runs. Against the v2.11.1 12-run
42
+ > baseline: run-level 402/514 vs 172/221, −0.4 %, Fisher p = 0.92; pass^k
43
+ > 26 → 28 of 46 shared cases, McNemar p = 0.63. The error fired in the arm
44
+ > on 3 `tests-claim-1` runs and nowhere else; that case went 4/5 against
45
+ > 12/24 (p = 0.34, ceiling). `self-correct-1` read 2/5 in the arm on a
46
+ > server 75 minutes into its traffic (the arm's invalid runs were climbing),
47
+ > and the error had not fired in any of its runs; a paired control on a
48
+ > fresh server, ten runs each on the parent tree and this one, came back
49
+ > **10/10 vs 9/10, p = 1.0**. In the one control run where the error did
50
+ > fire the model had sent a hallucinated `main()` as both strings, was told
51
+ > nothing changed, read the file, and then made the real fix. Smoke miss
52
+ > on `agentic-2` re-measured 9/10 against its recorded 75 %.
53
+
54
+ - `edit_file` refuses an edit whose `old_string` and `new_string` are the
55
+ same text, instead of answering "Edited". 37 of the 840 `edit_file` calls
56
+ on record sent identical strings and were told the edit landed; the model
57
+ then ran or read the file believing it had changed it (`tests-claim-1`:
58
+ "The median function was correctly fixed", after an edit of `mean` to
59
+ itself; `runit-1` before 2.14.0: the broken line edited to itself, twice,
60
+ to the step limit). The tool now says nothing changed and what to send
61
+ instead, and leaves the file byte-identical. A missing file still reports
62
+ the path first.
63
+
7
64
  ## 2.14.0 — 2026-09-17
8
65
 
9
66
  A minor release: one tool behaves differently for the model (a `write_file`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hexcli
3
- Version: 2.14.0
3
+ Version: 2.16.0
4
4
  Summary: Local Hexagon NPU terminal agent for Snapdragon X Elite Windows ARM64
5
5
  Project-URL: Homepage, https://github.com/NathanL15/Hex-CLI
6
6
  Project-URL: Repository, https://github.com/NathanL15/Hex-CLI
@@ -3,4 +3,4 @@
3
3
  # The one place the version is written. pyproject.toml reads it (hatch
4
4
  # dynamic version), agent.VERSION re-exports it, and CI refuses a release
5
5
  # tag that does not match it.
6
- __version__ = "2.14.0"
6
+ __version__ = "2.16.0"
@@ -826,7 +826,12 @@ def execute_tool_call(config: dict[str, Any], action: dict[str, Any], shell_exe:
826
826
  raise RuntimeError("search_files requires 'pattern'.")
827
827
  return search_files_tool(pattern, path, glob_pat, limit)
828
828
  if tool == "find_files":
829
- glob_pat = str(args.get("glob") or "").strip()
829
+ # 21 of the 170 find_files calls on record (2026-09-17) sent the
830
+ # name under "pattern", the key search_files uses, and got "requires
831
+ # 'glob'" -- three of findfile-1's twelve runs burned steps on it,
832
+ # one to the step limit. The value is what the model meant to look
833
+ # for either way; a bare name is a valid glob for that exact name.
834
+ glob_pat = str(args.get("glob") or args.get("pattern") or "").strip()
830
835
  path = str(args.get("path") or ".").strip() or "."
831
836
  if not glob_pat:
832
837
  raise RuntimeError("find_files requires 'glob'.")
@@ -423,6 +423,17 @@ def edit_file_tool(path_text: str, old_string: str, new_string: str) -> str:
423
423
  raise RuntimeError("edit_file requires a non-empty 'old_string'. Use write_file to overwrite the whole file.")
424
424
  if not path.exists():
425
425
  raise RuntimeError(f"File not found: {path}.{missing_path_hint(path)}")
426
+ if old_string == new_string:
427
+ # 37 of the 840 edit_file calls on record (2026-09-17) sent the same
428
+ # text as old and new and were told "Edited"; the model then ran or
429
+ # read the file believing it had changed it (tests-claim-1: "The
430
+ # median function was correctly fixed", after an edit of mean to
431
+ # itself). A no-op is not an edit, and saying so is the only way the
432
+ # next step can be the change that was meant.
433
+ raise RuntimeError(
434
+ f"old_string and new_string are identical, so nothing changed in {path}. "
435
+ "Put the changed text in new_string, or read_file to see what is there now."
436
+ )
426
437
  content = path.read_text(encoding="utf-8")
427
438
  tier = "exact"
428
439
  if content.count(old_string) == 1:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes