1bcoder 0.1.9__tar.gz → 0.1.10__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 (101) hide show
  1. {1bcoder-0.1.9 → 1bcoder-0.1.10}/1bcoder.egg-info/PKG-INFO +31 -4
  2. {1bcoder-0.1.9 → 1bcoder-0.1.10}/PKG-INFO +31 -4
  3. {1bcoder-0.1.9 → 1bcoder-0.1.10}/README.md +30 -3
  4. {1bcoder-0.1.9 → 1bcoder-0.1.10}/chat.py +46 -7
  5. {1bcoder-0.1.9 → 1bcoder-0.1.10}/map_index.py +7 -4
  6. {1bcoder-0.1.9 → 1bcoder-0.1.10}/map_query.py +68 -0
  7. {1bcoder-0.1.9 → 1bcoder-0.1.10}/pyproject.toml +1 -1
  8. {1bcoder-0.1.9 → 1bcoder-0.1.10}/1bcoder.egg-info/SOURCES.txt +0 -0
  9. {1bcoder-0.1.9 → 1bcoder-0.1.10}/1bcoder.egg-info/dependency_links.txt +0 -0
  10. {1bcoder-0.1.9 → 1bcoder-0.1.10}/1bcoder.egg-info/entry_points.txt +0 -0
  11. {1bcoder-0.1.9 → 1bcoder-0.1.10}/1bcoder.egg-info/requires.txt +0 -0
  12. {1bcoder-0.1.9 → 1bcoder-0.1.10}/1bcoder.egg-info/top_level.txt +0 -0
  13. {1bcoder-0.1.9 → 1bcoder-0.1.10}/LICENSE +0 -0
  14. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/__init__.py +0 -0
  15. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/advance.txt +0 -0
  16. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/ask.txt +0 -0
  17. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/compact.txt +0 -0
  18. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/concepts.txt +0 -0
  19. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/fill.txt +0 -0
  20. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/planning.txt +0 -0
  21. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/scan.txt +0 -0
  22. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/sqlite.txt +0 -0
  23. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/agents/websearch.txt +0 -0
  24. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/aliases.txt +0 -0
  25. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/doc/FLOWS.md +0 -0
  26. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/doc/MCP.md +0 -0
  27. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/doc/OLLAMA_SERVER_PARAM.md +0 -0
  28. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/doc/PARAM.md +0 -0
  29. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/doc/PROC.md +0 -0
  30. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/doc/TRANSLATE.md +0 -0
  31. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/flows/__pycache__/commit_message.cpython-311.pyc +0 -0
  32. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/flows/commit_message.py +0 -0
  33. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/flows/grounding.py +0 -0
  34. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/flows/py_error_trace.py +0 -0
  35. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/flows/simargl_files.py +0 -0
  36. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/flows/webask.py +0 -0
  37. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/map.txt +0 -0
  38. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/action-required.py +0 -0
  39. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/add-save.py +0 -0
  40. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/assist.py +0 -0
  41. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/collect-files.py +0 -0
  42. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/ctx_cut.py +0 -0
  43. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/extract-code.py +0 -0
  44. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/extract-files.py +0 -0
  45. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/extract-list.py +0 -0
  46. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/grounding-check.py +0 -0
  47. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/md.py +0 -0
  48. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/mdx.py +0 -0
  49. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/pattern-gate.py +0 -0
  50. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/regexp-extract.py +0 -0
  51. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/rude_words.py +0 -0
  52. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/scan-save.py +0 -0
  53. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/secret_check.py +0 -0
  54. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/sql_readonly_guard.py +0 -0
  55. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/proc/tempctx-cut.py +0 -0
  56. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/profiles.txt +0 -0
  57. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/prompts/analysis.txt +0 -0
  58. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/prompts/sumarise.txt +0 -0
  59. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/prompts.txt +0 -0
  60. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/AddFunction.txt +0 -0
  61. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/AskProject.txt +0 -0
  62. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/CheckRequirements.txt +0 -0
  63. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/DockerMySQL.txt +0 -0
  64. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/DockerNginx.txt +0 -0
  65. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/DockerPython.txt +0 -0
  66. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/DockerStack.txt +0 -0
  67. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/DuckDuckGoInstant.txt +0 -0
  68. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/EnvTemplate.txt +0 -0
  69. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/Explain.txt +0 -0
  70. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/ExploreProjectStructure.txt +0 -0
  71. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/GitIgnorePython.txt +0 -0
  72. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/MySQLDump.txt +0 -0
  73. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/NewScript.txt +0 -0
  74. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/PipFreeze.txt +0 -0
  75. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/PyPI.txt +0 -0
  76. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/Refactor.txt +0 -0
  77. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/RunAndFix.txt +0 -0
  78. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/SQLiteSchema.txt +0 -0
  79. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/Translate.txt +0 -0
  80. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/WikiPage.txt +0 -0
  81. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/WikiSearch.txt +0 -0
  82. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/auto-bkup.txt +0 -0
  83. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/edit-control.txt +0 -0
  84. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/parallel_call.txt +0 -0
  85. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/personal/content/create-regular-content.txt +0 -0
  86. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/personal/content/plan.txt +0 -0
  87. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/personal/test/collect-data-from-test-environment.txt +0 -0
  88. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/plan.txt +0 -0
  89. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/remote/create-content-on-remote-server.txt +0 -0
  90. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/set_ctx.txt +0 -0
  91. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/simargl-cli_index_files.txt +0 -0
  92. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/simargl-cli_index_units.txt +0 -0
  93. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/simargl-cli_search.txt +0 -0
  94. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/team-map-worker.txt +0 -0
  95. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/team-search-worker.txt +0 -0
  96. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/team-summarize.txt +0 -0
  97. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/team-tree-worker.txt +0 -0
  98. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/scripts/test.txt +0 -0
  99. {1bcoder-0.1.9 → 1bcoder-0.1.10}/_bcoder_data/teams/code-analysis.yaml +0 -0
  100. {1bcoder-0.1.9 → 1bcoder-0.1.10}/setup.cfg +0 -0
  101. {1bcoder-0.1.9 → 1bcoder-0.1.10}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: 1bcoder
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: AI coding assistant agent for 1B–7B local models (Ollama, LMStudio, llama.cpp). Terminal REPL with file editing, project map, agents, scripts, and parallel multi-model queries.
5
5
  Project-URL: Homepage, https://github.com/szholobetsky/1bcoder
6
6
  Project-URL: Repository, https://github.com/szholobetsky/1bcoder
@@ -59,7 +59,7 @@ Key design decisions:
59
59
  - **Short agent system prompts, at most 5 tools per agent, one function per agent** — `ask`, `edit`, `fill`, `scan`, `compact`. Not universal agents with bloated skill sets.
60
60
  - **Tolerant of long and malformed output** — post-processing is automatic; the programmer does not teach the model JSON syntax.
61
61
  - **`/parallel`** — send the same context to several models simultaneously and combine results; a 0.5b and a 1b model working together often outperform either alone; designed to coordinate small models running on multiple machines or phones.
62
- - **`/map`** — project structure index with structural diff; lets the model navigate a codebase without loading it into context.
62
+ - **`/map`** — project structure index with structural diff and keyword grounding; lets the model navigate a codebase without loading it into context. Keyword extract supports three modes: exact, like (`%token%`), and fuzzy (subword); `-o` resolves each keyword to the files it appears in.
63
63
  - **`/ctx`** — surgical context management: savepoints, selective compaction, named context library, multi-turn rollback. Small models cannot afford wasted tokens.
64
64
  - **`/scan`** — reads any large file chunk by chunk and builds a themed summary without overflowing context.
65
65
  - **`/proc`** — parameterized command scripts for repeatable preparation workflows.
@@ -538,8 +538,8 @@ The map command scans your project with language-agnostic regex, extracts defini
538
538
  /map trace <start> <end> [-y] — shortest dependency path between two points
539
539
  /map idiff [path] [depth] — re-index then show diff vs previous snapshot
540
540
  /map diff — show diff without re-indexing (safe to repeat)
541
- /map keyword index — build keyword vocabulary from map.txt
542
- /map keyword extract <text> [-f] [-a] [-n] [-c] — extract real identifiers from keyword.txt matching text/file
541
+ /map keyword index — build keyword vocabulary from map.txt
542
+ /map keyword extract <text> [-f] [-l] [-a] [-s] [-n] [-c] [-o] — extract real identifiers from keyword.txt matching text/file
543
543
  ```
544
544
 
545
545
  **Partial / incremental indexing** — for large codebases where a full re-scan is slow:
@@ -583,6 +583,33 @@ This lets you re-index a changed module in seconds instead of hours.
583
583
  /map find models -d 2 — filenames + defines/vars only
584
584
  ```
585
585
 
586
+ **`/map keyword`** — two-step identifier grounding:
587
+
588
+ ```
589
+ /map keyword index — scan map.txt → .1bcoder/keyword.txt (word, count, line refs)
590
+ /map keyword extract "fix rule search" -l -o — find identifiers, show which files they're in
591
+ ```
592
+
593
+ Extract flags:
594
+
595
+ | Flag | Mode | Effect |
596
+ |---|---|---|
597
+ | *(none)* | exact | query word must exactly match a keyword |
598
+ | `-f` | fuzzy | splits camelCase/snake_case into subwords; matches if all query subwords (≥5 chars) appear in the identifier |
599
+ | `-l` | like | substring match: any keyword containing the token (`%token%`) |
600
+ | `-n` | — | show frequency count: `RuleIndex(25)` |
601
+ | `-s` | — | sort by frequency descending |
602
+ | `-a` | — | sort alphabetically |
603
+ | `-c` | — | comma-separated output (default: one per line) |
604
+ | `-o` | — | show origin files: `parameter_rule -> lib/cop/rule.rb, config/default.yml` |
605
+
606
+ ```
607
+ /map keyword extract "add validation rule" -l -o
608
+ # parameter_rule -> lib/rubocop/cop/style/rule.rb, config/default.yml
609
+ # cop_rule -> lib/rubocop/cop/base.rb
610
+ # load_rules -> lib/rubocop/config_loader.rb
611
+ ```
612
+
586
613
  **`/map trace`** — three modes:
587
614
 
588
615
  **1. Backwards BFS** (who depends on this?):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: 1bcoder
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: AI coding assistant agent for 1B–7B local models (Ollama, LMStudio, llama.cpp). Terminal REPL with file editing, project map, agents, scripts, and parallel multi-model queries.
5
5
  Project-URL: Homepage, https://github.com/szholobetsky/1bcoder
6
6
  Project-URL: Repository, https://github.com/szholobetsky/1bcoder
@@ -59,7 +59,7 @@ Key design decisions:
59
59
  - **Short agent system prompts, at most 5 tools per agent, one function per agent** — `ask`, `edit`, `fill`, `scan`, `compact`. Not universal agents with bloated skill sets.
60
60
  - **Tolerant of long and malformed output** — post-processing is automatic; the programmer does not teach the model JSON syntax.
61
61
  - **`/parallel`** — send the same context to several models simultaneously and combine results; a 0.5b and a 1b model working together often outperform either alone; designed to coordinate small models running on multiple machines or phones.
62
- - **`/map`** — project structure index with structural diff; lets the model navigate a codebase without loading it into context.
62
+ - **`/map`** — project structure index with structural diff and keyword grounding; lets the model navigate a codebase without loading it into context. Keyword extract supports three modes: exact, like (`%token%`), and fuzzy (subword); `-o` resolves each keyword to the files it appears in.
63
63
  - **`/ctx`** — surgical context management: savepoints, selective compaction, named context library, multi-turn rollback. Small models cannot afford wasted tokens.
64
64
  - **`/scan`** — reads any large file chunk by chunk and builds a themed summary without overflowing context.
65
65
  - **`/proc`** — parameterized command scripts for repeatable preparation workflows.
@@ -538,8 +538,8 @@ The map command scans your project with language-agnostic regex, extracts defini
538
538
  /map trace <start> <end> [-y] — shortest dependency path between two points
539
539
  /map idiff [path] [depth] — re-index then show diff vs previous snapshot
540
540
  /map diff — show diff without re-indexing (safe to repeat)
541
- /map keyword index — build keyword vocabulary from map.txt
542
- /map keyword extract <text> [-f] [-a] [-n] [-c] — extract real identifiers from keyword.txt matching text/file
541
+ /map keyword index — build keyword vocabulary from map.txt
542
+ /map keyword extract <text> [-f] [-l] [-a] [-s] [-n] [-c] [-o] — extract real identifiers from keyword.txt matching text/file
543
543
  ```
544
544
 
545
545
  **Partial / incremental indexing** — for large codebases where a full re-scan is slow:
@@ -583,6 +583,33 @@ This lets you re-index a changed module in seconds instead of hours.
583
583
  /map find models -d 2 — filenames + defines/vars only
584
584
  ```
585
585
 
586
+ **`/map keyword`** — two-step identifier grounding:
587
+
588
+ ```
589
+ /map keyword index — scan map.txt → .1bcoder/keyword.txt (word, count, line refs)
590
+ /map keyword extract "fix rule search" -l -o — find identifiers, show which files they're in
591
+ ```
592
+
593
+ Extract flags:
594
+
595
+ | Flag | Mode | Effect |
596
+ |---|---|---|
597
+ | *(none)* | exact | query word must exactly match a keyword |
598
+ | `-f` | fuzzy | splits camelCase/snake_case into subwords; matches if all query subwords (≥5 chars) appear in the identifier |
599
+ | `-l` | like | substring match: any keyword containing the token (`%token%`) |
600
+ | `-n` | — | show frequency count: `RuleIndex(25)` |
601
+ | `-s` | — | sort by frequency descending |
602
+ | `-a` | — | sort alphabetically |
603
+ | `-c` | — | comma-separated output (default: one per line) |
604
+ | `-o` | — | show origin files: `parameter_rule -> lib/cop/rule.rb, config/default.yml` |
605
+
606
+ ```
607
+ /map keyword extract "add validation rule" -l -o
608
+ # parameter_rule -> lib/rubocop/cop/style/rule.rb, config/default.yml
609
+ # cop_rule -> lib/rubocop/cop/base.rb
610
+ # load_rules -> lib/rubocop/config_loader.rb
611
+ ```
612
+
586
613
  **`/map trace`** — three modes:
587
614
 
588
615
  **1. Backwards BFS** (who depends on this?):
@@ -44,7 +44,7 @@ Key design decisions:
44
44
  - **Short agent system prompts, at most 5 tools per agent, one function per agent** — `ask`, `edit`, `fill`, `scan`, `compact`. Not universal agents with bloated skill sets.
45
45
  - **Tolerant of long and malformed output** — post-processing is automatic; the programmer does not teach the model JSON syntax.
46
46
  - **`/parallel`** — send the same context to several models simultaneously and combine results; a 0.5b and a 1b model working together often outperform either alone; designed to coordinate small models running on multiple machines or phones.
47
- - **`/map`** — project structure index with structural diff; lets the model navigate a codebase without loading it into context.
47
+ - **`/map`** — project structure index with structural diff and keyword grounding; lets the model navigate a codebase without loading it into context. Keyword extract supports three modes: exact, like (`%token%`), and fuzzy (subword); `-o` resolves each keyword to the files it appears in.
48
48
  - **`/ctx`** — surgical context management: savepoints, selective compaction, named context library, multi-turn rollback. Small models cannot afford wasted tokens.
49
49
  - **`/scan`** — reads any large file chunk by chunk and builds a themed summary without overflowing context.
50
50
  - **`/proc`** — parameterized command scripts for repeatable preparation workflows.
@@ -523,8 +523,8 @@ The map command scans your project with language-agnostic regex, extracts defini
523
523
  /map trace <start> <end> [-y] — shortest dependency path between two points
524
524
  /map idiff [path] [depth] — re-index then show diff vs previous snapshot
525
525
  /map diff — show diff without re-indexing (safe to repeat)
526
- /map keyword index — build keyword vocabulary from map.txt
527
- /map keyword extract <text> [-f] [-a] [-n] [-c] — extract real identifiers from keyword.txt matching text/file
526
+ /map keyword index — build keyword vocabulary from map.txt
527
+ /map keyword extract <text> [-f] [-l] [-a] [-s] [-n] [-c] [-o] — extract real identifiers from keyword.txt matching text/file
528
528
  ```
529
529
 
530
530
  **Partial / incremental indexing** — for large codebases where a full re-scan is slow:
@@ -568,6 +568,33 @@ This lets you re-index a changed module in seconds instead of hours.
568
568
  /map find models -d 2 — filenames + defines/vars only
569
569
  ```
570
570
 
571
+ **`/map keyword`** — two-step identifier grounding:
572
+
573
+ ```
574
+ /map keyword index — scan map.txt → .1bcoder/keyword.txt (word, count, line refs)
575
+ /map keyword extract "fix rule search" -l -o — find identifiers, show which files they're in
576
+ ```
577
+
578
+ Extract flags:
579
+
580
+ | Flag | Mode | Effect |
581
+ |---|---|---|
582
+ | *(none)* | exact | query word must exactly match a keyword |
583
+ | `-f` | fuzzy | splits camelCase/snake_case into subwords; matches if all query subwords (≥5 chars) appear in the identifier |
584
+ | `-l` | like | substring match: any keyword containing the token (`%token%`) |
585
+ | `-n` | — | show frequency count: `RuleIndex(25)` |
586
+ | `-s` | — | sort by frequency descending |
587
+ | `-a` | — | sort alphabetically |
588
+ | `-c` | — | comma-separated output (default: one per line) |
589
+ | `-o` | — | show origin files: `parameter_rule -> lib/cop/rule.rb, config/default.yml` |
590
+
591
+ ```
592
+ /map keyword extract "add validation rule" -l -o
593
+ # parameter_rule -> lib/rubocop/cop/style/rule.rb, config/default.yml
594
+ # cop_rule -> lib/rubocop/cop/base.rb
595
+ # load_rules -> lib/rubocop/config_loader.rb
596
+ ```
597
+
571
598
  **`/map trace`** — three modes:
572
599
 
573
600
  **1. Backwards BFS** (who depends on this?):
@@ -916,7 +916,7 @@ Output capture operators (work with any command — LLM reply, tool, proc):
916
916
  CSV format: word, count, semicolon-separated list of line numbers in map.txt.
917
917
  Sorted alphabetically. Run once after /map index (or whenever map changes).
918
918
  e.g. /map keyword index
919
- /map keyword extract <text or file> [-a] [-f] [-n] [-c]
919
+ /map keyword extract <text or file> [-a] [-s] [-f] [-l] [-n] [-c] [-o]
920
920
  Extract real identifiers from keyword.txt matching words in the given text or file.
921
921
  Output is always real identifiers from keyword.txt — never synthetic splits.
922
922
  Default (exact): query word must exactly match a keyword.txt entry.
@@ -928,16 +928,20 @@ Output capture operators (work with any command — LLM reply, tool, proc):
928
928
  "RuleIndex" → matches RuleIndex only (needs both 'rule' AND 'index')
929
929
  "coverage" → matches CoverageMetric, LineCoverage, BranchCoverage
930
930
  "RuleIndex" → does NOT match Rule (missing 'index') or Index (missing 'rule')
931
+ -l like match (%token%): any keyword containing the query token as substring
932
+ "rule" → RuleIndex, rule_validator, parseRule, getRuleSet
931
933
  -a alphabetical order
932
934
  -s sort by codebase count descending (most frequent first)
933
935
  -n show codebase count next to each word: RuleIndex(25) RuleName(12)
934
936
  (-n implies -s)
935
937
  -c comma-separated output instead of one per line
938
+ -o show origin: which files each keyword appears in: keyword -> file1, file2
936
939
  e.g. /map keyword extract notes.txt
937
940
  /map keyword extract notes.txt -f
941
+ /map keyword extract notes.txt -l
938
942
  /map keyword extract notes.txt -f -n -c
939
943
  /map keyword extract "add isbn field to the Book class" -f -a
940
- /map keyword extract "fix rule search" -f -c
944
+ /map keyword extract "fix rule search" -l -o
941
945
 
942
946
  /hook before|after <cmd> <script>
943
947
  Run a script before or after a command (edit, patch, fix, insert).
@@ -1175,8 +1179,11 @@ def _fmt_ctx(n: int) -> str:
1175
1179
 
1176
1180
 
1177
1181
  def read_file(path, start=None, end=None, line_numbers=True):
1178
- with open(path, "r", encoding="utf-8") as f:
1179
- lines = f.readlines()
1182
+ try:
1183
+ with open(path, "r", encoding="utf-8") as f:
1184
+ lines = f.readlines()
1185
+ except UnicodeDecodeError:
1186
+ raise ValueError(f"[read] {path}: binary file, cannot read as text")
1180
1187
  total = len(lines)
1181
1188
  if start is not None:
1182
1189
  start = max(1, start)
@@ -3828,7 +3835,7 @@ advanced_tools =
3828
3835
  _ok(f"context: injected {label}")
3829
3836
  except FileNotFoundError:
3830
3837
  print(f"file not found: {path}")
3831
- except OSError as e:
3838
+ except (OSError, ValueError) as e:
3832
3839
  _err(e)
3833
3840
 
3834
3841
  def _cmd_edit(self, user_input: str):
@@ -7731,11 +7738,13 @@ Config stored in ~/.1bcoder/translate.json
7731
7738
  sort_alpha = "-a" in args
7732
7739
  sort_count = "-s" in args
7733
7740
  fuzzy = "-f" in args
7741
+ like = "-l" in args
7734
7742
  show_counts = "-n" in args
7735
7743
  csv_out = "-c" in args
7736
- src_tokens = [a for a in args if a not in ("-a", "-s", "-f", "-n", "-c")]
7744
+ show_origin = "-o" in args
7745
+ src_tokens = [a for a in args if a not in ("-a", "-s", "-f", "-l", "-n", "-c", "-o")]
7737
7746
  if not src_tokens:
7738
- print("usage: /map keyword extract <text or file> [-a] [-s] [-f] [-n] [-c]")
7747
+ print("usage: /map keyword extract <text or file> [-a] [-s] [-f] [-l] [-n] [-c] [-o]")
7739
7748
  return
7740
7749
  # load keyword vocab: word → count
7741
7750
  _csv.field_size_limit(10_000_000) # lines field can be large for common words
@@ -7777,6 +7786,12 @@ Config stored in ~/.1bcoder/translate.json
7777
7786
  # keyword matches if ALL query subwords are present in keyword's subwords
7778
7787
  if query_parts <= kp and kw not in seen:
7779
7788
  seen[kw] = i
7789
+ elif like:
7790
+ for i, m in enumerate(token_re.finditer(text)):
7791
+ token = m.group().lower()
7792
+ for j, kw in enumerate(kw_freq):
7793
+ if token in kw.lower() and kw not in seen:
7794
+ seen[kw] = i * 100000 + j
7780
7795
  else:
7781
7796
  # default: exact identifier match
7782
7797
  kw_set = set(kw_freq)
@@ -7793,6 +7808,30 @@ Config stored in ~/.1bcoder/translate.json
7793
7808
  result = sorted(seen, key=lambda w: (-kw_freq[w], w.lower()))
7794
7809
  else:
7795
7810
  result = sorted(seen, key=lambda w: (seen[w], w.lower()))
7811
+ if show_origin:
7812
+ map_path = os.path.join(BCODER_DIR, "map.txt")
7813
+ line_to_file = map_query._build_line_to_file(map_path)
7814
+ # load line numbers column from keyword.txt
7815
+ kw_lines: dict = {}
7816
+ with open(kw_path, encoding="utf-8", newline="") as f:
7817
+ reader2 = _csv.reader(f)
7818
+ next(reader2, None)
7819
+ for row in reader2:
7820
+ if len(row) >= 3 and row[0] in seen:
7821
+ kw_lines[row[0]] = [int(x) for x in row[2].split(";") if x]
7822
+ lines_out = []
7823
+ for w in result:
7824
+ label = f"{w}({kw_freq[w]})" if show_counts else w
7825
+ files: list = []
7826
+ seen_files: set = set()
7827
+ for ln in kw_lines.get(w, []):
7828
+ f = line_to_file.get(ln)
7829
+ if f and f not in seen_files:
7830
+ seen_files.add(f)
7831
+ files.append(f)
7832
+ lines_out.append(f"{label} -> {', '.join(files)}" if files else label)
7833
+ print("\n".join(lines_out))
7834
+ return
7796
7835
  if show_counts:
7797
7836
  items = [f"{w}({kw_freq[w]})" for w in result]
7798
7837
  else:
@@ -292,15 +292,18 @@ def build_map(root: str, depth: int = 2, map_path: str = None) -> str:
292
292
  global_index[name] = rel
293
293
 
294
294
  # ── link phase (changed files only) ──────────────────────────────────────────
295
+ global_set = set(global_index.keys())
295
296
  file_links = {} # rel → { target_rel → { name → kind } }
296
297
  for rel, text in tqdm(file_content.items(), desc="linking", unit="file", file=sys.stderr):
298
+ tokens_in_file = set(_WORD_RE.findall(text))
299
+ candidates = tokens_in_file & global_set
297
300
  by_target = {}
298
- for name, target_rel in global_index.items():
301
+ for name in candidates:
302
+ target_rel = global_index[name]
299
303
  if target_rel == rel:
300
304
  continue
301
- if re.search(r'\b' + re.escape(name) + r'\b', text):
302
- kind = classify_ref(name, text)
303
- by_target.setdefault(target_rel, {})[name] = kind
305
+ kind = classify_ref(name, text)
306
+ by_target.setdefault(target_rel, {})[name] = kind
304
307
  file_links[rel] = by_target
305
308
 
306
309
  # ── format output ────────────────────────────────────────────────────────────
@@ -538,6 +538,74 @@ def idiff_report(map_prev: str, map_curr: str) -> str:
538
538
  return '\n'.join(lines)
539
539
 
540
540
 
541
+ # ── keywords ────────────────────────────────────────────────────────────────────
542
+
543
+ def _split_identifier(name: str) -> list:
544
+ """Split camelCase / snake_case / PascalCase into lowercase subwords."""
545
+ parts = re.split(r'[_\-]+', name)
546
+ result = []
547
+ for part in parts:
548
+ if not part:
549
+ continue
550
+ s = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', part)
551
+ s = re.sub(r'([a-z\d])([A-Z])', r'\1_\2', s)
552
+ result.extend(w.lower() for w in s.split('_') if len(w) >= 2)
553
+ seen: dict = {}
554
+ for w in result:
555
+ seen.setdefault(w, None)
556
+ return list(seen)
557
+
558
+
559
+ def _build_line_to_file(map_path: str) -> dict:
560
+ """Return {line_number: file_path} for every line in map.txt.
561
+
562
+ File ownership: nearest non-indented, non-comment line above.
563
+ """
564
+ with open(map_path, encoding='utf-8', errors='replace') as f:
565
+ lines = f.readlines()
566
+ result = {}
567
+ current_file = None
568
+ for i, line in enumerate(lines, 1):
569
+ stripped = line.rstrip('\n')
570
+ if stripped and not stripped[0].isspace() and not stripped.startswith('#'):
571
+ current_file = stripped.strip()
572
+ if current_file:
573
+ result[i] = current_file
574
+ return result
575
+
576
+
577
+ def keyword_to_files(map_path: str, word: str) -> list:
578
+ """Return list of files where *word* appears, in order of first occurrence."""
579
+ import csv as _csv
580
+
581
+ kw_path = os.path.join(os.path.dirname(os.path.abspath(map_path)), 'keyword.txt')
582
+ if not os.path.exists(kw_path):
583
+ return []
584
+
585
+ line_nums = []
586
+ _csv.field_size_limit(10_000_000)
587
+ with open(kw_path, encoding='utf-8', newline='') as f:
588
+ reader = _csv.reader(f)
589
+ next(reader, None)
590
+ for row in reader:
591
+ if len(row) >= 3 and row[0] == word:
592
+ line_nums = [int(x) for x in row[2].split(';') if x]
593
+ break
594
+
595
+ if not line_nums:
596
+ return []
597
+
598
+ line_to_file = _build_line_to_file(map_path)
599
+ seen: set = set()
600
+ files = []
601
+ for ln in line_nums:
602
+ f = line_to_file.get(ln)
603
+ if f and f not in seen:
604
+ seen.add(f)
605
+ files.append(f)
606
+ return files
607
+
608
+
541
609
  # ── CLI entry point ─────────────────────────────────────────────────────────────
542
610
 
543
611
  def main():
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "1bcoder"
7
- version = "0.1.9"
7
+ version = "0.1.10"
8
8
  description = "AI coding assistant agent for 1B–7B local models (Ollama, LMStudio, llama.cpp). Terminal REPL with file editing, project map, agents, scripts, and parallel multi-model queries."
9
9
  requires-python = ">=3.10"
10
10
  readme = {file = "README.md", content-type = "text/markdown"}
File without changes
File without changes
File without changes
File without changes