cli-nix 0.3.0__py3-none-any.whl

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 (100) hide show
  1. cli_nix-0.3.0.dist-info/METADATA +340 -0
  2. cli_nix-0.3.0.dist-info/RECORD +100 -0
  3. cli_nix-0.3.0.dist-info/WHEEL +5 -0
  4. cli_nix-0.3.0.dist-info/entry_points.txt +2 -0
  5. cli_nix-0.3.0.dist-info/licenses/LICENSE +18 -0
  6. cli_nix-0.3.0.dist-info/top_level.txt +1 -0
  7. nix/__init__.py +1 -0
  8. nix/__main__.py +4 -0
  9. nix/app.py +188 -0
  10. nix/avatar.py +518 -0
  11. nix/body.py +55 -0
  12. nix/brain.py +198 -0
  13. nix/cli.py +27 -0
  14. nix/commands.py +1733 -0
  15. nix/config.py +83 -0
  16. nix/daemon.py +210 -0
  17. nix/git.py +231 -0
  18. nix/i18n.py +734 -0
  19. nix/journal.py +59 -0
  20. nix/logger.py +24 -0
  21. nix/modules/__init__.py +46 -0
  22. nix/modules/bundled/__init__.py +0 -0
  23. nix/modules/bundled/c/__init__.py +0 -0
  24. nix/modules/bundled/c/blocks.json +21 -0
  25. nix/modules/bundled/c/gen/function.tmpl +4 -0
  26. nix/modules/bundled/c/module.json +6 -0
  27. nix/modules/bundled/c/ops.json +19 -0
  28. nix/modules/bundled/c/probe.json +9 -0
  29. nix/modules/bundled/cpp/__init__.py +0 -0
  30. nix/modules/bundled/cpp/blocks.json +30 -0
  31. nix/modules/bundled/cpp/gen/class.tmpl +4 -0
  32. nix/modules/bundled/cpp/gen/function.tmpl +4 -0
  33. nix/modules/bundled/cpp/module.json +6 -0
  34. nix/modules/bundled/cpp/ops.json +25 -0
  35. nix/modules/bundled/cpp/probe.json +14 -0
  36. nix/modules/bundled/csharp/__init__.py +0 -0
  37. nix/modules/bundled/csharp/blocks.json +30 -0
  38. nix/modules/bundled/csharp/gen/class.tmpl +4 -0
  39. nix/modules/bundled/csharp/gen/function.tmpl +4 -0
  40. nix/modules/bundled/csharp/module.json +6 -0
  41. nix/modules/bundled/csharp/ops.json +25 -0
  42. nix/modules/bundled/csharp/probe.json +14 -0
  43. nix/modules/bundled/go/__init__.py +0 -0
  44. nix/modules/bundled/go/blocks.json +17 -0
  45. nix/modules/bundled/go/gen/function.tmpl +4 -0
  46. nix/modules/bundled/go/module.json +6 -0
  47. nix/modules/bundled/go/ops.json +13 -0
  48. nix/modules/bundled/go/probe.json +14 -0
  49. nix/modules/bundled/java/__init__.py +0 -0
  50. nix/modules/bundled/java/blocks.json +30 -0
  51. nix/modules/bundled/java/gen/class.tmpl +4 -0
  52. nix/modules/bundled/java/gen/function.tmpl +4 -0
  53. nix/modules/bundled/java/module.json +6 -0
  54. nix/modules/bundled/java/ops.json +25 -0
  55. nix/modules/bundled/java/probe.json +14 -0
  56. nix/modules/bundled/javascript/__init__.py +0 -0
  57. nix/modules/bundled/javascript/blocks.json +30 -0
  58. nix/modules/bundled/javascript/gen/class.tmpl +4 -0
  59. nix/modules/bundled/javascript/gen/function.tmpl +4 -0
  60. nix/modules/bundled/javascript/module.json +6 -0
  61. nix/modules/bundled/javascript/ops.json +25 -0
  62. nix/modules/bundled/javascript/probe.json +19 -0
  63. nix/modules/bundled/php/__init__.py +0 -0
  64. nix/modules/bundled/php/blocks.json +30 -0
  65. nix/modules/bundled/php/gen/class.tmpl +4 -0
  66. nix/modules/bundled/php/gen/function.tmpl +4 -0
  67. nix/modules/bundled/php/module.json +6 -0
  68. nix/modules/bundled/php/ops.json +25 -0
  69. nix/modules/bundled/php/probe.json +14 -0
  70. nix/modules/bundled/python/__init__.py +0 -0
  71. nix/modules/bundled/python/blocks.json +30 -0
  72. nix/modules/bundled/python/gen/class.tmpl +3 -0
  73. nix/modules/bundled/python/gen/function.tmpl +3 -0
  74. nix/modules/bundled/python/gen/scaffold_crud.tmpl +35 -0
  75. nix/modules/bundled/python/gen/scaffold_model.tmpl +9 -0
  76. nix/modules/bundled/python/gen/scaffold_test.tmpl +41 -0
  77. nix/modules/bundled/python/gen/testgen.tmpl +13 -0
  78. nix/modules/bundled/python/module.json +1 -0
  79. nix/modules/bundled/python/ops.json +25 -0
  80. nix/modules/bundled/python/probe.json +14 -0
  81. nix/modules/bundled/rust/__init__.py +0 -0
  82. nix/modules/bundled/rust/blocks.json +21 -0
  83. nix/modules/bundled/rust/gen/function.tmpl +4 -0
  84. nix/modules/bundled/rust/module.json +6 -0
  85. nix/modules/bundled/rust/ops.json +19 -0
  86. nix/modules/bundled/rust/probe.json +14 -0
  87. nix/modules/bundled/typescript/__init__.py +0 -0
  88. nix/modules/bundled/typescript/blocks.json +30 -0
  89. nix/modules/bundled/typescript/gen/class.tmpl +4 -0
  90. nix/modules/bundled/typescript/gen/function.tmpl +4 -0
  91. nix/modules/bundled/typescript/module.json +6 -0
  92. nix/modules/bundled/typescript/ops.json +25 -0
  93. nix/modules/bundled/typescript/probe.json +19 -0
  94. nix/modules/engine.py +174 -0
  95. nix/modules/loader.py +172 -0
  96. nix/pet.py +103 -0
  97. nix/scaffold.py +251 -0
  98. nix/scanner.py +73 -0
  99. nix/state.py +63 -0
  100. nix/ui.py +1187 -0
@@ -0,0 +1,340 @@
1
+ Metadata-Version: 2.4
2
+ Name: cli-nix
3
+ Version: 0.3.0
4
+ Summary: NIX — local-first project-aware testing agent
5
+ License-Expression: MIT-0
6
+ Keywords: cli,tui,testing,agent,textual,code-analysis
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: Microsoft :: Windows
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Testing
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: rich>=13
22
+ Requires-Dist: textual>=0.60
23
+ Dynamic: license-file
24
+
25
+ <div align="center">
26
+
27
+ <pre>
28
+ _ _______ __
29
+ / | / / _/ |/ /
30
+ / |/ // / | /
31
+ / /| // / / |
32
+ /_/ |_/___//_/|_|
33
+ </pre>
34
+
35
+ # NIX — Local Project Testing Agent
36
+
37
+ A local-first terminal agent that lives inside your project directory.
38
+ It scans, analyzes, mutates, tests, and learns — all through a rich,
39
+ modern TUI in the terminal.
40
+
41
+ ![Python](https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat&logo=python&logoColor=white)
42
+ ![License](https://img.shields.io/badge/license-MIT--0-brightgreen)
43
+ ![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey)
44
+ ![TUI](https://img.shields.io/badge/built_on-Textual-8BE9FD)
45
+
46
+ </div>
47
+
48
+ ---
49
+
50
+ ## Screenshots
51
+
52
+ | Main view with your pet | Project scan |
53
+ |:---:|:---:|
54
+ | ![Main](docs/screenshots/main.svg) | ![Scan](docs/screenshots/scan.svg) |
55
+
56
+ | Pet panel | Status | Help |
57
+ |:---:|:---:|:---:|
58
+ | ![Pet](docs/screenshots/pet.svg) | ![Status](docs/screenshots/status.svg) | ![Help](docs/screenshots/help.svg) |
59
+
60
+ > Screenshots are real headless renders of the actual TUI. Regenerate them
61
+ > any time with [`scripts/make_screenshots.py`](scripts/make_screenshots.py).
62
+
63
+ ## Features
64
+
65
+ - Full-screen terminal UI built on [Textual](https://github.com/Textualize/textual)
66
+ — like opencode or Claude Code
67
+ - **Pet companion** — a tamagotchi that lives in your repo, evolves, and
68
+ gets a new skin every `pill` (8 skins, 30-minute cooldown)
69
+ - **Language modules** — plain-data packs (no code) describing how blocks,
70
+ operations and generation work per language
71
+ - **Project brain** — `.nix/brain/` index of functions/classes/patterns that
72
+ the pet rebuilds on demand (always fresher than the developer's memory)
73
+ - **Read-only by default** — dry-runs for everything (scan, status, git,
74
+ gen, wrap, rename): nothing changes unless `--apply`
75
+ - **Git companion** — status, diff, log, branch, auto-messaged commits,
76
+ remote info and optional `gh`/`glab` PR support
77
+ - **IDE daemon** — headless JSON-line protocol over stdin/stdout or a socket
78
+ - Chain commands on one line with `;` or `&&`
79
+
80
+ ## Install
81
+
82
+ ### From a git clone (any platform)
83
+
84
+ **Windows 10/11 + Python 3.11+**
85
+
86
+ ```bat
87
+ git clone https://github.com/Mr-Bulochka/NIX.git
88
+ cd NIX
89
+ python -m pip install -r requirements.txt
90
+ python -m nix
91
+ ```
92
+
93
+ **Linux / macOS + Python 3.11+**
94
+
95
+ ```bash
96
+ git clone https://github.com/Mr-Bulochka/NIX.git
97
+ cd NIX
98
+ python3 -m pip install -r requirements.txt
99
+ python3 -m nix
100
+ ```
101
+
102
+ ### From PyPI
103
+
104
+ ```bash
105
+ pip install cli-nix
106
+ nix
107
+ ```
108
+
109
+ ### From the latest GitHub Release
110
+
111
+ Check the **Releases** tab for the current `.whl`, then:
112
+
113
+ ```bash
114
+ pip install https://github.com/Mr-Bulochka/NIX/releases/download/v0.3.0/cli_nix-0.3.0-py3-none-any.whl
115
+ nix
116
+ ```
117
+
118
+ ### Platform support
119
+
120
+ NIX is pure Python (3.11+) with two cross-platform dependencies
121
+ (`rich`, `textual`) — the same code runs on Windows, Linux and macOS.
122
+ The `nix` console command is created by the package entry point and works
123
+ everywhere; `nix.bat` in the repo root is just a Windows convenience
124
+ launcher equivalent to `python -m nix`.
125
+
126
+ ## Quick Start
127
+
128
+ ```bash
129
+ cd C:\MyProject # or /home/you/myproject on Linux/macOS
130
+ nix # or: python -m nix
131
+ ```
132
+
133
+ On first launch, NIX creates a `.nix/` directory and asks for your pet's name.
134
+
135
+ ## The Interface
136
+
137
+ - Header bar with project name, safety mode and live clock
138
+ - Pet panel with animated idle spinner and stats
139
+ - Buttons row — **clickable with the mouse**: Pet, Scan, Status, Settings, Quit
140
+ - Scrollable color-coded event log (syntax highlighting, tables, panels)
141
+ - Command input with focus, arrow-key navigation
142
+ - Hotkeys: `Ctrl+Q` quit, `Ctrl+L` clear, `Ctrl+S` scan, `Ctrl+P` pet
143
+
144
+ ## Commands
145
+
146
+ Every command is a **constructor**: positional words + `--flag value`, `-flag`
147
+ switches and `k=v` pairs, all combinable on one infinite-length line. Chain
148
+ several commands in a single line with `;` or `&&`.
149
+
150
+ | Command | Description |
151
+ |---------|-------------|
152
+ | `help [cmd]` | Show all commands, or details of one |
153
+ | `which <cmd>` | Show info about a command |
154
+ | `status` | Current project and NIX state |
155
+ | `stats [--top N]` | Full project analytics |
156
+ | `scan [--tree] [--depth N]` | Read-only project inventory |
157
+ | `tree [--depth N]` | Show project tree |
158
+ | `ls [path] [--all] [--size]` | List a directory |
159
+ | `lang [--top N]` | Detect languages in the project |
160
+ | `tests [--max N] [--count]` | Find test files and functions |
161
+ | `deps [--top N]` | Collect imported modules |
162
+ | `find <name> [--ext py,js]` | Search files by name |
163
+ | `todo [--max N]` | Scan code for TODO/FIXME |
164
+ | `attempts [N\|+N\|-N]` | Show/set attempt budget |
165
+ | `mode [local\|safe\|git]` | Show/set safety mode |
166
+ | `save` | Persist config and pet |
167
+ | `pet` | Show pet status |
168
+ | `pill [--status]` | Give the pet a skin pill (30 min cooldown) |
169
+ | `settings` | Open settings |
170
+ | `tag [name] [--remove]` | List/add/remove pet tags |
171
+ | `note <text>` | Save an idea to project memory |
172
+ | `memory [N]` | Show recent project notes |
173
+ | `journal <text>` | Write a journal entry |
174
+ | `logs [N]` | Show session log |
175
+ | `history [N]` | Show journal history |
176
+ | `checkpoint [name]` | List or create checkpoints |
177
+ | `echo <text>` | Print text to the log |
178
+ | `time` | Show current UTC time |
179
+ | `pwd` | Show current working directory |
180
+ | `version` | Show NIX version |
181
+ | `clear` | Clear the event log |
182
+ | `quit` | Exit NIX |
183
+
184
+ ### Code & language modules
185
+
186
+ NIX understands code through **code modules** (language modules) —
187
+ plain-data packs (no code) that describe how blocks, operations and
188
+ generation work in a given language. Bundled modules live in
189
+ `nix/modules/bundled/`; user modules are picked up from
190
+ `~/.nix/modules/<id>/` and shadow bundled ones.
191
+
192
+ | Command | Description |
193
+ |---------|-------------|
194
+ | `module [name]` | List language modules or show one in detail |
195
+ | `defs [--max N] [glob]` | List project functions/classes (from the project index) |
196
+ | `blocks <file> <line>` | Show the block enclosing a line |
197
+ | `wrap <file> <line> in <op> [--slot v] [--apply]` | Wrap a block in a language op (dry-run unless `--apply`) |
198
+ | `gen <type> <name> [--into f] [--at N] [--apply] [--params .. --ret .. --doc .. --body ..]` | Generate code from a template (preview unless `--into`; `--apply` writes into it or creates a new file) |
199
+ | `make <entity> [--cols name:str:pk,...] [--into f] [--test-into f] [--apply]` | Scaffold a model + CRUD + tests from column specs (dry-run unless `--apply`; language inferred from `--into` extension or `--lang`, an unsupported hint is an error) |
200
+ | `testgen <file\|all> [--into f] [--apply]` | Generate test stubs from the project index (dry-run unless `--apply`) |
201
+ | `recipe [list\|<name>] [--apply] [args...]` | Run named command chains; built-ins: `feature`, `scaffold`, `test`; user recipes live in `.nix/recipes.json` (`{0}`, `{1}`, ... are positional args; re-run with `--apply` to write) |
202
+ | `rename <old> <new> [--file f] [--apply]` | Project-wide symbolic rename (dry-run unless `--apply`) |
203
+ | `ident` | Show the project's identity patterns (naming, docstrings, ...) |
204
+
205
+ ### Version control companion
206
+
207
+ | Command | Description |
208
+ |---------|-------------|
209
+ | `git status` | Short working-tree status |
210
+ | `git log [N]` | Last N commits oneline |
211
+ | `git diff` | Change statistics |
212
+ | `git branch` | Current branch |
213
+ | `git commit [msg] [--apply]` | Stage all and commit; message auto-generated from diff (dry-run unless `--apply`) |
214
+ | `remote info` | Show origin, platform (GitHub/GitLab/other), branch, ahead/behind |
215
+ | `remote fetch` / `pull` / `push` | Remote sync, dry-run unless `--apply` |
216
+ | `remote pr` | List open PRs/MRs via `gh`/`glab`; `remote pr "title" --apply` opens one |
217
+
218
+ ### IDE daemon & protocol (Stage I)
219
+
220
+ NIX runs headless without the TUI, so any IDE/editor can drive the same
221
+ deterministic toolkit:
222
+
223
+ ```
224
+ nix daemon # line protocol over stdin/stdout (JSON Lines)
225
+ nix daemon --socket 127.0.0.1:7411
226
+ nix daemon --once "status" # one command, one JSON response
227
+ ```
228
+
229
+ Each request is one line; each response is one JSON object:
230
+
231
+ ```
232
+ > HELP
233
+ {"ok": true, "session_end": false, "commands": [{"name": "defs", ...}]}
234
+ > defs
235
+ {"ok": true, "session_end": false, "events": [{"op": "block", "title": "Symbols", ...}]}
236
+ > quit
237
+ {"ok": true, "session_end": true}
238
+ ```
239
+
240
+ Commands are the exact `COMMANDS` the TUI uses; events are ordered
241
+ (`message`, `block`, `code`, `tree`, `pet`, `status`, `scan`, `logs`,
242
+ `history`, `help`) and re-renderable by the client verbatim.
243
+
244
+ Module layout:
245
+
246
+ ```
247
+ nix/modules/bundled/python/
248
+ ├── module.json id, name, version, extensions
249
+ ├── blocks.json block start regex + body mode (indent/braces/do-end)
250
+ ├── ops.json semantic ops (wrap-in-try, wrap-in-if, ...)
251
+ ├── gen/*.tmpl generation templates (function, class, ...)
252
+ └── probe.json self-test cases
253
+ ```
254
+
255
+ The pet's memory of a project lives in `.nix/brain/` (`index.json`,
256
+ `patterns.json`) and is (re)built by `defs`/`ident`/`gen` on demand, so
257
+ the pet always knows the project fresher than the developer does.
258
+
259
+ Pets evolve and get a **new random skin** when you give them a pill (once every
260
+ 30 minutes); the stage/pattern is always preserved. Each of the 8 skins has
261
+ its own signature palette, so every pill is a visible transformation.
262
+
263
+ ## Learning how a project works
264
+
265
+ NIX is interesting when it can *change* things. Try, in order:
266
+
267
+ ```text
268
+ status # what is here
269
+ scan # inventory (read-only)
270
+ lang # what languages live here
271
+ ident # how this project is written
272
+ defs # what functions/classes exist
273
+ wrap src/app.py 12 in try # dry-run proposed edit
274
+ gen function login --into src/app.py # preview a generated function
275
+ recipe feature "my idea text"
276
+ ```
277
+
278
+ Everything stays a dry-run until you add `--apply`.
279
+
280
+ ## `.nix/` Structure
281
+
282
+ ```
283
+ .nix/
284
+ ├── config.json
285
+ ├── recipes.json
286
+ ├── state/
287
+ ├── journal/
288
+ ├── logs/
289
+ ├── memory/
290
+ ├── experiments/
291
+ ├── checkpoints/
292
+ │ ├── permanent/
293
+ │ └── temporary/
294
+ ├── pet/
295
+ │ └── identity.json
296
+ ├── brain/
297
+ │ ├── index.json
298
+ │ ├── patterns.json
299
+ │ └── backups/
300
+ └── origin/
301
+ ```
302
+
303
+ ## Development
304
+
305
+ ```bash
306
+ pip install -e .
307
+ python -m unittest discover -s tests -v
308
+ python -m nix
309
+ ```
310
+
311
+ ## Build a release
312
+
313
+ ```bash
314
+ pip install build
315
+ python -m build
316
+ ```
317
+
318
+ Produces `dist/cli_nix-<version>*.whl` and `.tar.gz` (the wheel embeds
319
+ all bundled language modules). Attach both to a GitHub Release to let anyone
320
+ `pip install` the program directly.
321
+
322
+ ## License
323
+
324
+ [MIT-0](LICENSE) — the code is free to use, copy, modify, merge, publish,
325
+ distribute, sublicense, or sell, with **no obligations** to the author.
326
+
327
+ That said, it would make my day if you mention the author or link to this
328
+ repository — purely as a nice-to-have, never a requirement.
329
+
330
+ ## Roadmap
331
+
332
+ - **Stage A** — Foundation + modern TUI (done)
333
+ - **Stage B** — Code modules + project brain: structural engine, `defs`/`blocks`/`wrap`/`gen`/`rename` + `git` companion (current)
334
+ - **Stage C** — Mutation Engine + Sandbox + World Laws
335
+ - **Stage D** — Checkpoint Manager + Experience System
336
+ - **Stage E** — Destructive Testing
337
+ - **Stage F** — GitHub/GitLab Bridge
338
+ - **Stage G** — Tamagotchi Evolution
339
+ - **Stage H** — Full TUI polish
340
+ - **Stage I** — IDE daemon + protocol (done: `nix daemon`, see above; plugins per-editor remain)
@@ -0,0 +1,100 @@
1
+ cli_nix-0.3.0.dist-info/licenses/LICENSE,sha256=qMKQ9NbWlmNo9ULutOdh8XO0kG3-nIiqctDZIlFxZUE,908
2
+ nix/__init__.py,sha256=5mDtfU2t04ATmg92S5B-scUcdjCy1ZBq620y3GmpdtQ,21
3
+ nix/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
4
+ nix/app.py,sha256=W_f3AsJP2Y_UAqrceZXfhofdKtq8YtqwlCcwfS4tzfM,5932
5
+ nix/avatar.py,sha256=A7EcWg28EjObkHF_CDvKdozlfh-okyJ3t5U4yck2YfA,14262
6
+ nix/body.py,sha256=rwMZzd70MsXH2-nvLKS7sTTYACRJyzXwk9t_KqfydoU,1713
7
+ nix/brain.py,sha256=swsGWnaGkEHEPHnl5TUKqoIcRMTyOrsOH1A-WjeuVqU,7104
8
+ nix/cli.py,sha256=ExzT29Xeu5xUFjMcLbw29c_BUz-7luRfjvaZPKX75Ac,816
9
+ nix/commands.py,sha256=D72EiiuloDy6n5vCx8yYkQInpOXIDQFgcz5L_WamDl4,65098
10
+ nix/config.py,sha256=JjbZSXuuy3YYcSsIRnYAhfNQrqYHpvln3dEih-u1kG8,2912
11
+ nix/daemon.py,sha256=Alghy8ruzf_LztTRWeOPvExAqH9E9gDZdXG6oJD_NDc,6996
12
+ nix/git.py,sha256=1KPYLx3ikJfHhOeAzI_Di52VRlVOOg4mlfn061ks7YE,8125
13
+ nix/i18n.py,sha256=jcEPXlMdZ9iY9n3MEE9NBY6fzi8lawfqlRVrOM49uHE,35311
14
+ nix/journal.py,sha256=ALMjwC4x7RSyzZopC_Bo4xDEfhbRLDPQd9faO9az0w8,1808
15
+ nix/logger.py,sha256=PptPK_NqnIqIEvyFXD0VbOaAet79nMfTUx1Ko-f95nc,793
16
+ nix/pet.py,sha256=vv99PrVfZuZtoKNFJwqGSBUU6rnSSN52dkG7bSQq3s4,3105
17
+ nix/scaffold.py,sha256=A2k5b8EhmiLI3XR6sw_wRh9NawP-gC0YJEBzm2lhsDY,8121
18
+ nix/scanner.py,sha256=y4FkhNlEd7AojuNaJGnjZ61TXm4uFDFNnVcRE-QLEnM,2095
19
+ nix/state.py,sha256=4GGgipbSyyxVIlwz46xsVae3hpecqVpE5LIrHPyIqwU,1618
20
+ nix/ui.py,sha256=FveeoSvwR2RL2bH1XosFVMMiuu2FyJfJhGKttqxApc8,35197
21
+ nix/modules/__init__.py,sha256=BVKhlXT_HWr6NuHm9BoDFdZK9t2E82oUO-pG58WVzPw,1060
22
+ nix/modules/engine.py,sha256=35J0ZuRjxao5vHGKVd-XUl6bM5b0TjRkC_pceCWT0TM,5836
23
+ nix/modules/loader.py,sha256=zIJXu1eRjDXL5H0puzqDK6w-QaHv7JK4Ud_Frl3xkxk,5889
24
+ nix/modules/bundled/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ nix/modules/bundled/c/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ nix/modules/bundled/c/blocks.json,sha256=Hnn2SBpcDVyDbbaxsDcvprmNecJ4j5-AvPTb5qPhZQM,487
27
+ nix/modules/bundled/c/module.json,sha256=IeLgRbHp8bP-jq7oi2xWHHuL2G5UGKZsdDP9eb7SAEw,80
28
+ nix/modules/bundled/c/ops.json,sha256=DQcd-T6C_9JP-QGTh8pSxB-XuzuxfjxNm52mXVHya3Q,406
29
+ nix/modules/bundled/c/probe.json,sha256=MECsJ0-WJGYstj8IbxYRY7StjuNyPyy_Eyb2w2Qn6y4,197
30
+ nix/modules/bundled/c/gen/function.tmpl,sha256=3rvzuyDVVrfYcVk18LIPt7t3Mmtkaso7ojb_wJtMc3k,60
31
+ nix/modules/bundled/cpp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ nix/modules/bundled/cpp/blocks.json,sha256=zTqHdZXaJOGfq73IgRxKnTn47b8HCwS45SPObt_Ynao,736
33
+ nix/modules/bundled/cpp/module.json,sha256=vFnL_z4K-3I362KKKGvSMoYWdQfFDdfcgh4upTa-RAE,103
34
+ nix/modules/bundled/cpp/ops.json,sha256=cjt_perpJqQ8TZZPT6VmKTjI6fNYozFDNgREr6oe9b4,656
35
+ nix/modules/bundled/cpp/probe.json,sha256=259gTVvDGrEoeICsZunzz0CwT6uOQE5uEo0y4wNndIM,340
36
+ nix/modules/bundled/cpp/gen/class.tmpl,sha256=H-_ViWmLGd_we2otkYyAsPv5HwXd68WemIOsDfFgrhQ,58
37
+ nix/modules/bundled/cpp/gen/function.tmpl,sha256=3rvzuyDVVrfYcVk18LIPt7t3Mmtkaso7ojb_wJtMc3k,60
38
+ nix/modules/bundled/csharp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ nix/modules/bundled/csharp/blocks.json,sha256=Vlv-BsGZSma6UO_AUE7Qybzo1nP6RpVY94N1MeGrri0,841
40
+ nix/modules/bundled/csharp/module.json,sha256=9tCHIY0cv-uF25-T6qIZYDQdoOhxpWhu0T0KbqBQSrc,81
41
+ nix/modules/bundled/csharp/ops.json,sha256=yuo35Ud9guLKDfWLYXFum5qJ2HM_wI_nkqQUGdM8e-g,644
42
+ nix/modules/bundled/csharp/probe.json,sha256=qjG2jSBJDHJARW3DjmcuQS_MZPVpe03mbUlwv9_U6qc,367
43
+ nix/modules/bundled/csharp/gen/class.tmpl,sha256=gkPzMRPF5dYiYlcKE47uHuIURsu9L4Rucx-Y-AHlTfY,64
44
+ nix/modules/bundled/csharp/gen/function.tmpl,sha256=AldGk7cYsuUsMuKsQ_siYS8IlqOmFhonv8GAWDFzKSE,67
45
+ nix/modules/bundled/go/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ nix/modules/bundled/go/blocks.json,sha256=c0Ln5gZ82c6m6O4xIbt906ziiM5zXYlyhGA30mWvTQ0,345
47
+ nix/modules/bundled/go/module.json,sha256=QkshBQEv4_c_6FABmhjCbVaKMVleQt1SnMdz4d8EOy8,77
48
+ nix/modules/bundled/go/ops.json,sha256=W9RUL4pqdp5KvIs1CAfGdOjpt2iD5lBy_cQcJjX8vYc,261
49
+ nix/modules/bundled/go/probe.json,sha256=LAPUJqp_qZ8CWn-vu1kHzu5RjCeIANFcBm9sMufUDZ4,345
50
+ nix/modules/bundled/go/gen/function.tmpl,sha256=L1WRfQJIbhVvcK-PUCUHbIM4sTxhYpQL5UeuGYn3DE4,60
51
+ nix/modules/bundled/java/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ nix/modules/bundled/java/blocks.json,sha256=ruh-fIa4pPhPXmr4P-Eb7S_0Y4lR0vlqZPQ-LGLQVu4,812
53
+ nix/modules/bundled/java/module.json,sha256=sn6-iZdfpXVfMcfwK88OhB_iJoNjBIdwmf9iDduTPc0,83
54
+ nix/modules/bundled/java/ops.json,sha256=yuo35Ud9guLKDfWLYXFum5qJ2HM_wI_nkqQUGdM8e-g,644
55
+ nix/modules/bundled/java/probe.json,sha256=tWsXwd1aRtmdqPeKrjOvOtP5ubmDuBg6HR2oDuAP-bk,351
56
+ nix/modules/bundled/java/gen/class.tmpl,sha256=gkPzMRPF5dYiYlcKE47uHuIURsu9L4Rucx-Y-AHlTfY,64
57
+ nix/modules/bundled/java/gen/function.tmpl,sha256=AldGk7cYsuUsMuKsQ_siYS8IlqOmFhonv8GAWDFzKSE,67
58
+ nix/modules/bundled/javascript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
+ nix/modules/bundled/javascript/blocks.json,sha256=WJ86fKfLYbjivbALH8UprENHwzXrW3IWavEFAwGMcps,734
60
+ nix/modules/bundled/javascript/module.json,sha256=QZogOqn0R4Tc0cXpLFrExe4zZtDgqt62NUuHrUHgBU4,117
61
+ nix/modules/bundled/javascript/ops.json,sha256=TPybAjJLgkqm6MoiMwmT9vmSHYMCY1_oTsxTKffQtVY,640
62
+ nix/modules/bundled/javascript/probe.json,sha256=HG84_7lOO0i_aBqwmLUyUgcQQhpi3I92kaekQZEsCDY,527
63
+ nix/modules/bundled/javascript/gen/class.tmpl,sha256=FhGii2MB-xS3_wQz0rNjkq09DX_5Ah6UnPwPNA0j2N4,57
64
+ nix/modules/bundled/javascript/gen/function.tmpl,sha256=S-aQ6YtXXAK3L55xNG9AYpq8bXOgfub5X20esDGsJE4,64
65
+ nix/modules/bundled/php/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ nix/modules/bundled/php/blocks.json,sha256=HlJ1TR-lZaAGOzBQtgJKCNrx4F-edrjlREebkSP4Xgc,729
67
+ nix/modules/bundled/php/module.json,sha256=TtAc13ZE2rP406To1Xp8AHKAiKlgx_ttsW1pkqsBpUA,80
68
+ nix/modules/bundled/php/ops.json,sha256=t-TFChRtUmFE0GUFZUFoMndMIbVA8hz0uXTZ3ih1Gmo,645
69
+ nix/modules/bundled/php/probe.json,sha256=8R8wW9NocI4loPeMPWVELZjy8wkrw6cFMSS5aUndIqA,330
70
+ nix/modules/bundled/php/gen/class.tmpl,sha256=FhGii2MB-xS3_wQz0rNjkq09DX_5Ah6UnPwPNA0j2N4,57
71
+ nix/modules/bundled/php/gen/function.tmpl,sha256=S-aQ6YtXXAK3L55xNG9AYpq8bXOgfub5X20esDGsJE4,64
72
+ nix/modules/bundled/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ nix/modules/bundled/python/blocks.json,sha256=HDobC1RUFHvQ1sZn8pB7Wcclmu99rEKNI0pPIeuNX5c,616
74
+ nix/modules/bundled/python/module.json,sha256=_Fh7ULUFs7TYEI0WdSHQBnb4pZh1jr3e7OqhyS521Ak,75
75
+ nix/modules/bundled/python/ops.json,sha256=yWOcE11NTA71duNZV8FSiyKeGAxOEc-ek8yDOAhBsQ4,648
76
+ nix/modules/bundled/python/probe.json,sha256=Yw-MA2bQpIu6AkpHCYA8YursXOIEw0d6ENfpck6a4lI,366
77
+ nix/modules/bundled/python/gen/class.tmpl,sha256=gTAJ3a-VKYYwfiAY8qcYE2AAEmgprg3kU-qrc1TWmcI,50
78
+ nix/modules/bundled/python/gen/function.tmpl,sha256=9ocSB3H3Zg9GtYnsJSDurfz43rbqacYTBtva3N3ANVo,63
79
+ nix/modules/bundled/python/gen/scaffold_crud.tmpl,sha256=zU8iX4JBfTKIhHp5dShnCLap7KxM_1I6O6kgF921Ny0,1085
80
+ nix/modules/bundled/python/gen/scaffold_model.tmpl,sha256=OI3McrlynHQ8I1bjJnJy38i37eH7TENwSI4P-DnExVk,132
81
+ nix/modules/bundled/python/gen/scaffold_test.tmpl,sha256=XMTXihmLjfOs3tw84RkjCq5i0mkaMU1VoRSYKA7fd1s,1160
82
+ nix/modules/bundled/python/gen/testgen.tmpl,sha256=fSlFUuvQRRgSO-2EWXTA0_Cr81F0Yww2gj0JoPApFRE,217
83
+ nix/modules/bundled/rust/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
+ nix/modules/bundled/rust/blocks.json,sha256=pMExggdGUrPbx_K38hcLP4qs982kjRcLgTI7P7iT6xw,438
85
+ nix/modules/bundled/rust/module.json,sha256=LblOEyPj_vH_PiW0v0v8W9u9JfVnl-7AhbaWziXOy8s,81
86
+ nix/modules/bundled/rust/ops.json,sha256=5LGarHV2d3U6vY_jurT_qDytkf03pNaMKpYQ0hx6_mY,408
87
+ nix/modules/bundled/rust/probe.json,sha256=HtHQk-BO66ZHVh5dmkwFZzT3bo4zS8aasbXeqoubmxM,344
88
+ nix/modules/bundled/rust/gen/function.tmpl,sha256=Ivx7AsYDzRQKo-2ttJPMLFpBaz3HejghITGuBU0-Kyk,58
89
+ nix/modules/bundled/typescript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
+ nix/modules/bundled/typescript/blocks.json,sha256=WJ86fKfLYbjivbALH8UprENHwzXrW3IWavEFAwGMcps,734
91
+ nix/modules/bundled/typescript/module.json,sha256=2u0cHrGsvHHNCeQ8ITsDoaVENWX6u1IHqBqceckd78E,101
92
+ nix/modules/bundled/typescript/ops.json,sha256=TPybAjJLgkqm6MoiMwmT9vmSHYMCY1_oTsxTKffQtVY,640
93
+ nix/modules/bundled/typescript/probe.json,sha256=HG84_7lOO0i_aBqwmLUyUgcQQhpi3I92kaekQZEsCDY,527
94
+ nix/modules/bundled/typescript/gen/class.tmpl,sha256=FhGii2MB-xS3_wQz0rNjkq09DX_5Ah6UnPwPNA0j2N4,57
95
+ nix/modules/bundled/typescript/gen/function.tmpl,sha256=S-aQ6YtXXAK3L55xNG9AYpq8bXOgfub5X20esDGsJE4,64
96
+ cli_nix-0.3.0.dist-info/METADATA,sha256=i4cprkhvCuRqkXyNA7kU7oSiJtz-EW3wqQhsvGOgRNk,12633
97
+ cli_nix-0.3.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
98
+ cli_nix-0.3.0.dist-info/entry_points.txt,sha256=nNqbhNQ7EUSEU9uhQ0yq1-c1oL73ij_Ar-6-3ORlNfI,37
99
+ cli_nix-0.3.0.dist-info/top_level.txt,sha256=IdIBPjCB8eRV_dW6YjCoYgw8_JqcMZgdhX_jiR95RJ4,4
100
+ cli_nix-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ nix = nix.cli:main
@@ -0,0 +1,18 @@
1
+ MIT No Attribution
2
+
3
+ Copyright (c) 2026 MrBulochka
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ nix
nix/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
nix/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
nix/app.py ADDED
@@ -0,0 +1,188 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from . import __version__
6
+ from .brain import Brain
7
+ from .config import ConfigStore
8
+ from .modules.loader import all_modules, set_enabled_modules
9
+ from .journal import Journal
10
+ from .logger import SessionLogger
11
+ from .pet import Pet
12
+ from .state import State, utc_now_ts
13
+
14
+ PILL_COOLDOWN = 30 * 60
15
+
16
+
17
+ def _tokenize_nix(text: str) -> list[tuple[str, str]]:
18
+ """Split a command line into args and chain separators.
19
+
20
+ Whitespace separates arguments; a matching pair of single or double
21
+ quotes groups a value (the quotes themselves are dropped); outside
22
+ quotes, ``;`` or ``&&`` splits the line into separate commands.
23
+ Returns ``("arg", value)`` and ``("sep", ";")`` / ``("sep", "&&")``
24
+ items in order.
25
+ """
26
+ items: list[tuple[str, str]] = []
27
+ buf: list[str] = []
28
+ i, n = 0, len(text)
29
+ while i < n:
30
+ c = text[i]
31
+ if c in ("'", '"'):
32
+ if buf:
33
+ items.append(("arg", "".join(buf)))
34
+ buf = []
35
+ quote = c
36
+ i += 1
37
+ value: list[str] = []
38
+ while i < n and text[i] != quote:
39
+ value.append(text[i])
40
+ i += 1
41
+ i += 1
42
+ items.append(("arg", "".join(value)))
43
+ continue
44
+ if c == ";":
45
+ if buf:
46
+ items.append(("arg", "".join(buf)))
47
+ buf = []
48
+ items.append(("sep", ";"))
49
+ i += 1
50
+ continue
51
+ if c == "&" and i + 1 < n and text[i + 1] == "&":
52
+ if buf:
53
+ items.append(("arg", "".join(buf)))
54
+ buf = []
55
+ items.append(("sep", "&&"))
56
+ i += 2
57
+ continue
58
+ if c.isspace():
59
+ if buf:
60
+ items.append(("arg", "".join(buf)))
61
+ buf = []
62
+ i += 1
63
+ continue
64
+ buf.append(c)
65
+ i += 1
66
+ if buf:
67
+ items.append(("arg", "".join(buf)))
68
+ return items
69
+
70
+
71
+ class NixApp:
72
+ version = __version__
73
+
74
+ def __init__(self) -> None:
75
+ self.root = Path.cwd().resolve()
76
+ self.state = State(self.root)
77
+ self.state.initialize()
78
+
79
+ self.config_store = ConfigStore(self.state.nix)
80
+ self.config = self.config_store.load()
81
+ set_enabled_modules(self.config.enabled_modules)
82
+
83
+ self.session_logger = SessionLogger(self.state.nix)
84
+ self.journal = Journal(self.state.nix)
85
+
86
+ self.pet_store = Pet(self.state.nix)
87
+ self.pet = self.pet_store.load()
88
+
89
+ self.brain = Brain(self.state.nix, self.root)
90
+
91
+ def run(self) -> None:
92
+ from .ui import NixUI
93
+ self.ui = NixUI(self)
94
+ self.ui.run()
95
+
96
+ def apply_module_settings(self) -> None:
97
+ """Re-apply the enabled-modules set from config and refresh the
98
+ brain's module map (settings may have changed at runtime)."""
99
+ set_enabled_modules(self.config.enabled_modules)
100
+ self.brain.modules = {m.id: m for m in all_modules()}
101
+
102
+ def t(self, key: str, **kwargs) -> str:
103
+ return self.config.t(key, **kwargs)
104
+
105
+ def pill_remaining(self) -> float | None:
106
+ pet = self.pet
107
+ if pet is None:
108
+ return None
109
+ last = pet.get("last_pill_at") or 0
110
+ return max(0.0, PILL_COOLDOWN - (utc_now_ts() - last))
111
+
112
+ def give_pill(self) -> tuple[bool, str]:
113
+ pet = self.pet
114
+ if pet is None:
115
+ return False, self.t("pet.no_pet")
116
+ remaining = self.pill_remaining()
117
+ if remaining and remaining > 0:
118
+ minutes = int(remaining // 60) + (1 if remaining % 60 else 0)
119
+ return False, self.t("fb.pill_cooldown", min=minutes)
120
+ from .avatar import choose_skin
121
+ new_skin = choose_skin(not_kind=pet.get("skin"))
122
+ pet["skin"] = new_skin
123
+ pet["last_pill_at"] = utc_now_ts()
124
+ self.pet_store.save(pet)
125
+ self.journal.write("PET", f"pill given: skin={new_skin}")
126
+ try:
127
+ if getattr(self, "ui", None):
128
+ self.ui._refresh_pet()
129
+ except Exception:
130
+ pass
131
+ return True, self.t("fb.pill_given", skin=self.t(f"skin.{new_skin}"))
132
+
133
+ def handle_command(self, raw: str) -> bool:
134
+ raw = raw.strip()
135
+ if not raw:
136
+ return True
137
+
138
+ self.session_logger.write("COMMAND", raw)
139
+
140
+ text = raw[1:] if raw.startswith("/") else raw
141
+ chunks: list[list[str]] = []
142
+ cur: list[str] = []
143
+ for kind, value in _tokenize_nix(text):
144
+ if kind == "arg":
145
+ cur.append(value)
146
+ elif cur:
147
+ chunks.append(cur)
148
+ cur = []
149
+ if cur:
150
+ chunks.append(cur)
151
+ continue_session = True
152
+ for chunk in chunks:
153
+ if not chunk:
154
+ continue
155
+ if not self._handle_command_args(chunk):
156
+ continue_session = False
157
+ break
158
+ return continue_session
159
+
160
+ def _handle_command_args(self, args: list[str]) -> bool:
161
+ if not args:
162
+ return True
163
+
164
+ name = args[0].lower()
165
+ rest = args[1:]
166
+
167
+ if name in ("quit", "exit"):
168
+ self.session_logger.write("SYSTEM", "session ended by user")
169
+ self.journal.write("SYSTEM", "session ended by user")
170
+ return False
171
+
172
+ from .commands import get_command
173
+
174
+ cmd = get_command(name)
175
+ if cmd is None:
176
+ self.ui.show_message("ERROR", self.t("fb.unknown", name=name))
177
+ return True
178
+
179
+ try:
180
+ result = cmd.handler(self, rest)
181
+ except Exception as exc:
182
+ self.ui.show_message("ERROR", self.t("fb.failed", name=name, exc=exc))
183
+ self.session_logger.write("ERROR", f"/{name} failed: {exc}")
184
+ return True
185
+
186
+ if result.message:
187
+ self.ui.show_message("SYSTEM", result.message)
188
+ return result.continue_session