loom-code 0.1.1__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.
- loom_code-0.1.1/LICENSE +21 -0
- loom_code-0.1.1/PKG-INFO +224 -0
- loom_code-0.1.1/README.md +170 -0
- loom_code-0.1.1/loom_code/__init__.py +22 -0
- loom_code-0.1.1/loom_code/_post_commit.py +119 -0
- loom_code-0.1.1/loom_code/agent.py +544 -0
- loom_code-0.1.1/loom_code/approval.py +616 -0
- loom_code-0.1.1/loom_code/browse/__init__.py +291 -0
- loom_code-0.1.1/loom_code/browse/act.py +467 -0
- loom_code-0.1.1/loom_code/browse/observe.py +249 -0
- loom_code-0.1.1/loom_code/browse/session.py +96 -0
- loom_code-0.1.1/loom_code/browse/verify.py +194 -0
- loom_code-0.1.1/loom_code/checkpoint.py +283 -0
- loom_code-0.1.1/loom_code/cli.py +495 -0
- loom_code-0.1.1/loom_code/code_index.py +703 -0
- loom_code-0.1.1/loom_code/compact.py +143 -0
- loom_code-0.1.1/loom_code/consent.py +47 -0
- loom_code-0.1.1/loom_code/credentials.py +527 -0
- loom_code-0.1.1/loom_code/edit_tool.py +635 -0
- loom_code-0.1.1/loom_code/extensions.py +522 -0
- loom_code-0.1.1/loom_code/file_history.py +322 -0
- loom_code-0.1.1/loom_code/file_tools.py +93 -0
- loom_code-0.1.1/loom_code/git_hook.py +200 -0
- loom_code-0.1.1/loom_code/grep_tool.py +430 -0
- loom_code-0.1.1/loom_code/hooks.py +297 -0
- loom_code-0.1.1/loom_code/loominit/__init__.py +23 -0
- loom_code-0.1.1/loom_code/loominit/_ast_walk.py +429 -0
- loom_code-0.1.1/loom_code/loominit/_files.py +284 -0
- loom_code-0.1.1/loom_code/loominit/_graph.py +141 -0
- loom_code-0.1.1/loom_code/loominit/_resolve.py +392 -0
- loom_code-0.1.1/loom_code/loominit/_tests_map.py +108 -0
- loom_code-0.1.1/loom_code/loominit/extractor.py +332 -0
- loom_code-0.1.1/loom_code/loominit/repomap.py +225 -0
- loom_code-0.1.1/loom_code/loominit/schema.py +242 -0
- loom_code-0.1.1/loom_code/lsp_tools.py +396 -0
- loom_code-0.1.1/loom_code/mcp_host.py +79 -0
- loom_code-0.1.1/loom_code/operator.py +449 -0
- loom_code-0.1.1/loom_code/paste.py +97 -0
- loom_code-0.1.1/loom_code/paths.py +52 -0
- loom_code-0.1.1/loom_code/permissions.py +177 -0
- loom_code-0.1.1/loom_code/project.py +104 -0
- loom_code-0.1.1/loom_code/prompts.py +451 -0
- loom_code-0.1.1/loom_code/render.py +783 -0
- loom_code-0.1.1/loom_code/repl.py +4080 -0
- loom_code-0.1.1/loom_code/rules.py +267 -0
- loom_code-0.1.1/loom_code/sandboxed_bash.py +176 -0
- loom_code-0.1.1/loom_code/scribe.py +88 -0
- loom_code-0.1.1/loom_code/skills/__init__.py +16 -0
- loom_code-0.1.1/loom_code/skills/graphify/SKILL.md +97 -0
- loom_code-0.1.1/loom_code/skills/graphify/tools.py +570 -0
- loom_code-0.1.1/loom_code/trust.py +216 -0
- loom_code-0.1.1/loom_code/turn.py +169 -0
- loom_code-0.1.1/loom_code/web_fetch.py +370 -0
- loom_code-0.1.1/loom_code/workers.py +758 -0
- loom_code-0.1.1/loom_code/worktree.py +134 -0
- loom_code-0.1.1/loom_code.egg-info/PKG-INFO +224 -0
- loom_code-0.1.1/loom_code.egg-info/SOURCES.txt +99 -0
- loom_code-0.1.1/loom_code.egg-info/dependency_links.txt +1 -0
- loom_code-0.1.1/loom_code.egg-info/entry_points.txt +2 -0
- loom_code-0.1.1/loom_code.egg-info/requires.txt +12 -0
- loom_code-0.1.1/loom_code.egg-info/top_level.txt +1 -0
- loom_code-0.1.1/pyproject.toml +150 -0
- loom_code-0.1.1/setup.cfg +4 -0
- loom_code-0.1.1/tests/test_agent.py +215 -0
- loom_code-0.1.1/tests/test_antipoison_gate.py +159 -0
- loom_code-0.1.1/tests/test_approval.py +72 -0
- loom_code-0.1.1/tests/test_approval_danger.py +75 -0
- loom_code-0.1.1/tests/test_approval_integration.py +177 -0
- loom_code-0.1.1/tests/test_checkpoint.py +157 -0
- loom_code-0.1.1/tests/test_code_index.py +190 -0
- loom_code-0.1.1/tests/test_compact.py +68 -0
- loom_code-0.1.1/tests/test_credentials.py +232 -0
- loom_code-0.1.1/tests/test_edit_tool.py +529 -0
- loom_code-0.1.1/tests/test_extensions.py +400 -0
- loom_code-0.1.1/tests/test_file_boundary.py +141 -0
- loom_code-0.1.1/tests/test_file_history.py +184 -0
- loom_code-0.1.1/tests/test_git_hook.py +198 -0
- loom_code-0.1.1/tests/test_graphify_file_discovery.py +152 -0
- loom_code-0.1.1/tests/test_graphify_query_tiers.py +187 -0
- loom_code-0.1.1/tests/test_graphify_wiring.py +78 -0
- loom_code-0.1.1/tests/test_grep_tool.py +308 -0
- loom_code-0.1.1/tests/test_learned_notes.py +123 -0
- loom_code-0.1.1/tests/test_loom_hooks.py +368 -0
- loom_code-0.1.1/tests/test_lsp_tools.py +100 -0
- loom_code-0.1.1/tests/test_mcp.py +218 -0
- loom_code-0.1.1/tests/test_paste.py +98 -0
- loom_code-0.1.1/tests/test_permissions.py +220 -0
- loom_code-0.1.1/tests/test_pricing.py +57 -0
- loom_code-0.1.1/tests/test_project.py +67 -0
- loom_code-0.1.1/tests/test_prompts.py +120 -0
- loom_code-0.1.1/tests/test_render.py +285 -0
- loom_code-0.1.1/tests/test_repl_guards.py +99 -0
- loom_code-0.1.1/tests/test_resume_migration.py +264 -0
- loom_code-0.1.1/tests/test_resume_preview.py +249 -0
- loom_code-0.1.1/tests/test_routing.py +194 -0
- loom_code-0.1.1/tests/test_rules.py +90 -0
- loom_code-0.1.1/tests/test_sandboxed_bash.py +153 -0
- loom_code-0.1.1/tests/test_stream_liveness.py +159 -0
- loom_code-0.1.1/tests/test_turn_economy.py +116 -0
- loom_code-0.1.1/tests/test_web_fetch.py +298 -0
- loom_code-0.1.1/tests/test_workers.py +73 -0
loom_code-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anupam Nautiyal
|
|
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, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
loom_code-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: loom-code
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: loom-code — a loomflow-native terminal coding agent
|
|
5
|
+
Author: Anupam Nautiyal
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Anupam Nautiyal
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/Anurich/loomflow-cli
|
|
29
|
+
Project-URL: Repository, https://github.com/Anurich/loomflow-cli
|
|
30
|
+
Project-URL: Issues, https://github.com/Anurich/loomflow-cli/issues
|
|
31
|
+
Keywords: ai,agent,cli,coding-assistant,llm,loomflow
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Environment :: Console
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
38
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
39
|
+
Requires-Python: >=3.11
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
License-File: LICENSE
|
|
42
|
+
Requires-Dist: loomflow[litellm,mcp,web]<0.11,>=0.10.23
|
|
43
|
+
Requires-Dist: rich>=13
|
|
44
|
+
Requires-Dist: prompt-toolkit>=3.0
|
|
45
|
+
Requires-Dist: anthropic
|
|
46
|
+
Requires-Dist: openai
|
|
47
|
+
Requires-Dist: graphifyy<0.9,>=0.8.11
|
|
48
|
+
Requires-Dist: jedi>=0.19
|
|
49
|
+
Provides-Extra: dev
|
|
50
|
+
Requires-Dist: pytest; extra == "dev"
|
|
51
|
+
Requires-Dist: ruff; extra == "dev"
|
|
52
|
+
Requires-Dist: bump-my-version>=0.30; extra == "dev"
|
|
53
|
+
Dynamic: license-file
|
|
54
|
+
|
|
55
|
+
# loom-code
|
|
56
|
+
|
|
57
|
+
**A terminal coding agent built on [loomflow](https://github.com/Anurich/LoomFlow).**
|
|
58
|
+
Plans before it codes, asks before it breaks things, works with any model —
|
|
59
|
+
including free ones.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
› add a retry decorator to the http client
|
|
63
|
+
|
|
64
|
+
● loom
|
|
65
|
+
Added `retry(max_attempts=3, backoff=2.0)` to http/client.py and wired it
|
|
66
|
+
onto get() and post(). Tests pass (14/14).
|
|
67
|
+
───────────────────────────────────────────── 12,431 in · 217 out · $0.0043
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
loom-code is a thin terminal shell — the brain is loomflow. The CLI detects
|
|
71
|
+
your project, builds a loomflow `Agent`, streams the run to your terminal,
|
|
72
|
+
and gates destructive tool calls behind an approval prompt. Everything
|
|
73
|
+
load-bearing — the agent loop, tools, planning, memory — is loomflow.
|
|
74
|
+
|
|
75
|
+
## Highlights
|
|
76
|
+
|
|
77
|
+
- **Plans before it codes.** Every task gets a living plan
|
|
78
|
+
(TodoWrite-style), visible as it progresses, hard to drift from.
|
|
79
|
+
- **Any model, including free ones.** OpenAI, Anthropic, NVIDIA's free
|
|
80
|
+
NIM tier, local Ollama, or anything LiteLLM routes (Groq, Together,
|
|
81
|
+
Azure, Bedrock, Vertex…). `/set_model` walks you through provider →
|
|
82
|
+
API key → model with arrow-key menus.
|
|
83
|
+
- **Claude-Code-style permissions.** Reads are lenient, writes are
|
|
84
|
+
strict. Every write/edit/shell command routes through an approval
|
|
85
|
+
gate with a unified-diff preview. Allow/ask/deny rules, approval
|
|
86
|
+
modes (`default` / `accept-edits` / `plan` / `yolo`), and an
|
|
87
|
+
optional OS-level bash sandbox (`--sandbox`).
|
|
88
|
+
- **Specialist sub-agents on demand.** The main loop can call
|
|
89
|
+
`explore` (read-only investigation) and `review` (independent
|
|
90
|
+
verification) as tools — one coherent thread, specialists when they
|
|
91
|
+
earn their keep.
|
|
92
|
+
- **Session isolation.** `/isolate` runs the session in its own git
|
|
93
|
+
worktree; `/review` shows the diff, `/merge` or `/discard` ends it.
|
|
94
|
+
Auto-checkpoints before every edit; `/undo` restores.
|
|
95
|
+
- **Gets sharper at your repo.** A per-project notebook plus episode
|
|
96
|
+
memory (`.loom/`) — notes the agent used get credited when a turn
|
|
97
|
+
goes well, so future runs surface what worked. `/good` and `/bad`
|
|
98
|
+
train it.
|
|
99
|
+
- **Cost you can see.** Every response closes with that turn's tokens
|
|
100
|
+
and dollar cost (`free` on free tiers). `/cost` has session totals.
|
|
101
|
+
- **MCP out of the box.** Connect Linear, Sentry, Postgres,
|
|
102
|
+
Playwright, or any MCP server; `/mcp` lists what's live.
|
|
103
|
+
- **Goal mode.** `/goal make all tests pass` keeps working until the
|
|
104
|
+
condition is verifiably met.
|
|
105
|
+
|
|
106
|
+
## Install
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pipx install git+https://github.com/Anurich/loomflow-cli
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
(`pip install` works too; `pipx` keeps CLI tools in their own venvs.
|
|
113
|
+
No pipx? `brew install pipx` or `python -m pip install --user pipx`.)
|
|
114
|
+
|
|
115
|
+
Requires Python 3.11+. To update: `pipx upgrade loom-code`.
|
|
116
|
+
|
|
117
|
+
## Quickstart
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
cd ~/your-project
|
|
121
|
+
loom-code
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
First run: type `/set_model`, pick a provider with the arrow keys,
|
|
125
|
+
paste your API key once (it's saved for future sessions), pick a
|
|
126
|
+
model. **No paid key?** Pick NVIDIA — free at
|
|
127
|
+
[build.nvidia.com](https://build.nvidia.com).
|
|
128
|
+
|
|
129
|
+
Then just type what you want:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
› fix the failing test in tests/test_auth.py
|
|
133
|
+
› add a /users endpoint with pagination
|
|
134
|
+
› why is startup slow? profile it
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
One-shot mode (does the task, prints a summary, exits):
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
loom-code "add a retry decorator to the http client"
|
|
141
|
+
loom-code --yes "scaffold a FastAPI backend" # skip approval prompts
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Works on existing code and empty directories alike — scaffolding new
|
|
145
|
+
projects is a first-class path.
|
|
146
|
+
|
|
147
|
+
## Models
|
|
148
|
+
|
|
149
|
+
| model string | provider | env key |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `claude-opus-4-8`, `claude-sonnet-4-6`, … | Anthropic | `ANTHROPIC_API_KEY` |
|
|
152
|
+
| `gpt-4.1`, `gpt-4.1-mini`, `o4-mini`, … | OpenAI | `OPENAI_API_KEY` |
|
|
153
|
+
| `nvidia/…` (Nemotron, Llama, DeepSeek) | NVIDIA NIM — **free tier** | `NVIDIA_NIM_API_KEY` |
|
|
154
|
+
| `ollama/llama3`, `ollama/qwen2.5-coder`, … | local [Ollama](https://ollama.com) — free, offline | — |
|
|
155
|
+
| `litellm/<provider>/<model>` | anything LiteLLM routes | provider's own |
|
|
156
|
+
|
|
157
|
+
Switch anytime with `/model <name>` or the guided `/set_model`.
|
|
158
|
+
Reasoning models support `/effort low|medium|high`.
|
|
159
|
+
|
|
160
|
+
> Tip: tool-heavy agent work needs a model with solid function
|
|
161
|
+
> calling. On the free NVIDIA tier, `deepseek-v4-pro` and
|
|
162
|
+
> `nemotron-super-49b` hold up well; tiny models fumble tool calls.
|
|
163
|
+
|
|
164
|
+
## Safety & permissions
|
|
165
|
+
|
|
166
|
+
The permission layer is the boundary, not the working directory:
|
|
167
|
+
|
|
168
|
+
- **Reads** anywhere are allowed; **writes outside the project** are
|
|
169
|
+
only possible for files *you* referenced, and always show a diff
|
|
170
|
+
prompt — in every mode, even `--yes`.
|
|
171
|
+
- **Approval modes** (`/mode`): `default` asks for writes and shell;
|
|
172
|
+
`accept-edits` auto-approves in-project edits; `plan` is read-only;
|
|
173
|
+
`yolo` approves everything except your deny rules.
|
|
174
|
+
- **Rules** live in `.loom/settings.toml` — glob-based
|
|
175
|
+
`allow` / `ask` / `deny` per tool (e.g. `deny = ["edit(*.env)"]`).
|
|
176
|
+
Deny always wins, even in yolo.
|
|
177
|
+
- **Sandbox**: `--sandbox` runs bash under OS-level isolation
|
|
178
|
+
(writes limited to the repo, network off unless
|
|
179
|
+
`--sandbox-allow-network`).
|
|
180
|
+
- Irreversible commands (`git push --force`, `rm -rf`, …) always
|
|
181
|
+
get an explicit prompt.
|
|
182
|
+
|
|
183
|
+
## Commands
|
|
184
|
+
|
|
185
|
+
Type `/` in the REPL — the menu autocompletes. Highlights:
|
|
186
|
+
|
|
187
|
+
| | |
|
|
188
|
+
|---|---|
|
|
189
|
+
| `/plan` | show or start the living plan |
|
|
190
|
+
| `/goal <condition>` | work until the condition is met |
|
|
191
|
+
| `/undo` · `/checkpoints` | restore / list auto-checkpoints |
|
|
192
|
+
| `/isolate` · `/review` · `/merge` · `/discard` | worktree-isolated sessions |
|
|
193
|
+
| `/model` · `/set_model` · `/effort` · `/mode` | model + approval setup |
|
|
194
|
+
| `/set_web` | web search (Serper / DuckDuckGo) |
|
|
195
|
+
| `/mcp` | list connected MCP servers |
|
|
196
|
+
| `/cost` · `/compact` · `/export` | session accounting + history |
|
|
197
|
+
| `/resume` | pick up the last session for this project |
|
|
198
|
+
| `/good` · `/bad` | credit / debit the agent's notes |
|
|
199
|
+
|
|
200
|
+
## Project context
|
|
201
|
+
|
|
202
|
+
loom-code reads `LOOM.md` / `CLAUDE.md` / `AGENTS.md` /
|
|
203
|
+
`.loom/context.md` at the project root and treats it as binding house
|
|
204
|
+
rules. `/init-loom` creates a starter file.
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
git clone https://github.com/Anurich/loomflow-cli
|
|
210
|
+
cd loomflow-cli
|
|
211
|
+
python -m venv .venv && source .venv/bin/activate
|
|
212
|
+
pip install -e ".[dev]"
|
|
213
|
+
pytest -q # 465 tests
|
|
214
|
+
ruff check .
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Architecture in one line: **loom-code is deliberately thin** — if a
|
|
218
|
+
capability belongs in the agent loop, it goes in
|
|
219
|
+
[loomflow](https://github.com/Anurich/LoomFlow), not here. See
|
|
220
|
+
`DESIGN.md` for the boundary.
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# loom-code
|
|
2
|
+
|
|
3
|
+
**A terminal coding agent built on [loomflow](https://github.com/Anurich/LoomFlow).**
|
|
4
|
+
Plans before it codes, asks before it breaks things, works with any model —
|
|
5
|
+
including free ones.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
› add a retry decorator to the http client
|
|
9
|
+
|
|
10
|
+
● loom
|
|
11
|
+
Added `retry(max_attempts=3, backoff=2.0)` to http/client.py and wired it
|
|
12
|
+
onto get() and post(). Tests pass (14/14).
|
|
13
|
+
───────────────────────────────────────────── 12,431 in · 217 out · $0.0043
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
loom-code is a thin terminal shell — the brain is loomflow. The CLI detects
|
|
17
|
+
your project, builds a loomflow `Agent`, streams the run to your terminal,
|
|
18
|
+
and gates destructive tool calls behind an approval prompt. Everything
|
|
19
|
+
load-bearing — the agent loop, tools, planning, memory — is loomflow.
|
|
20
|
+
|
|
21
|
+
## Highlights
|
|
22
|
+
|
|
23
|
+
- **Plans before it codes.** Every task gets a living plan
|
|
24
|
+
(TodoWrite-style), visible as it progresses, hard to drift from.
|
|
25
|
+
- **Any model, including free ones.** OpenAI, Anthropic, NVIDIA's free
|
|
26
|
+
NIM tier, local Ollama, or anything LiteLLM routes (Groq, Together,
|
|
27
|
+
Azure, Bedrock, Vertex…). `/set_model` walks you through provider →
|
|
28
|
+
API key → model with arrow-key menus.
|
|
29
|
+
- **Claude-Code-style permissions.** Reads are lenient, writes are
|
|
30
|
+
strict. Every write/edit/shell command routes through an approval
|
|
31
|
+
gate with a unified-diff preview. Allow/ask/deny rules, approval
|
|
32
|
+
modes (`default` / `accept-edits` / `plan` / `yolo`), and an
|
|
33
|
+
optional OS-level bash sandbox (`--sandbox`).
|
|
34
|
+
- **Specialist sub-agents on demand.** The main loop can call
|
|
35
|
+
`explore` (read-only investigation) and `review` (independent
|
|
36
|
+
verification) as tools — one coherent thread, specialists when they
|
|
37
|
+
earn their keep.
|
|
38
|
+
- **Session isolation.** `/isolate` runs the session in its own git
|
|
39
|
+
worktree; `/review` shows the diff, `/merge` or `/discard` ends it.
|
|
40
|
+
Auto-checkpoints before every edit; `/undo` restores.
|
|
41
|
+
- **Gets sharper at your repo.** A per-project notebook plus episode
|
|
42
|
+
memory (`.loom/`) — notes the agent used get credited when a turn
|
|
43
|
+
goes well, so future runs surface what worked. `/good` and `/bad`
|
|
44
|
+
train it.
|
|
45
|
+
- **Cost you can see.** Every response closes with that turn's tokens
|
|
46
|
+
and dollar cost (`free` on free tiers). `/cost` has session totals.
|
|
47
|
+
- **MCP out of the box.** Connect Linear, Sentry, Postgres,
|
|
48
|
+
Playwright, or any MCP server; `/mcp` lists what's live.
|
|
49
|
+
- **Goal mode.** `/goal make all tests pass` keeps working until the
|
|
50
|
+
condition is verifiably met.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pipx install git+https://github.com/Anurich/loomflow-cli
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
(`pip install` works too; `pipx` keeps CLI tools in their own venvs.
|
|
59
|
+
No pipx? `brew install pipx` or `python -m pip install --user pipx`.)
|
|
60
|
+
|
|
61
|
+
Requires Python 3.11+. To update: `pipx upgrade loom-code`.
|
|
62
|
+
|
|
63
|
+
## Quickstart
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
cd ~/your-project
|
|
67
|
+
loom-code
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
First run: type `/set_model`, pick a provider with the arrow keys,
|
|
71
|
+
paste your API key once (it's saved for future sessions), pick a
|
|
72
|
+
model. **No paid key?** Pick NVIDIA — free at
|
|
73
|
+
[build.nvidia.com](https://build.nvidia.com).
|
|
74
|
+
|
|
75
|
+
Then just type what you want:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
› fix the failing test in tests/test_auth.py
|
|
79
|
+
› add a /users endpoint with pagination
|
|
80
|
+
› why is startup slow? profile it
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
One-shot mode (does the task, prints a summary, exits):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
loom-code "add a retry decorator to the http client"
|
|
87
|
+
loom-code --yes "scaffold a FastAPI backend" # skip approval prompts
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Works on existing code and empty directories alike — scaffolding new
|
|
91
|
+
projects is a first-class path.
|
|
92
|
+
|
|
93
|
+
## Models
|
|
94
|
+
|
|
95
|
+
| model string | provider | env key |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `claude-opus-4-8`, `claude-sonnet-4-6`, … | Anthropic | `ANTHROPIC_API_KEY` |
|
|
98
|
+
| `gpt-4.1`, `gpt-4.1-mini`, `o4-mini`, … | OpenAI | `OPENAI_API_KEY` |
|
|
99
|
+
| `nvidia/…` (Nemotron, Llama, DeepSeek) | NVIDIA NIM — **free tier** | `NVIDIA_NIM_API_KEY` |
|
|
100
|
+
| `ollama/llama3`, `ollama/qwen2.5-coder`, … | local [Ollama](https://ollama.com) — free, offline | — |
|
|
101
|
+
| `litellm/<provider>/<model>` | anything LiteLLM routes | provider's own |
|
|
102
|
+
|
|
103
|
+
Switch anytime with `/model <name>` or the guided `/set_model`.
|
|
104
|
+
Reasoning models support `/effort low|medium|high`.
|
|
105
|
+
|
|
106
|
+
> Tip: tool-heavy agent work needs a model with solid function
|
|
107
|
+
> calling. On the free NVIDIA tier, `deepseek-v4-pro` and
|
|
108
|
+
> `nemotron-super-49b` hold up well; tiny models fumble tool calls.
|
|
109
|
+
|
|
110
|
+
## Safety & permissions
|
|
111
|
+
|
|
112
|
+
The permission layer is the boundary, not the working directory:
|
|
113
|
+
|
|
114
|
+
- **Reads** anywhere are allowed; **writes outside the project** are
|
|
115
|
+
only possible for files *you* referenced, and always show a diff
|
|
116
|
+
prompt — in every mode, even `--yes`.
|
|
117
|
+
- **Approval modes** (`/mode`): `default` asks for writes and shell;
|
|
118
|
+
`accept-edits` auto-approves in-project edits; `plan` is read-only;
|
|
119
|
+
`yolo` approves everything except your deny rules.
|
|
120
|
+
- **Rules** live in `.loom/settings.toml` — glob-based
|
|
121
|
+
`allow` / `ask` / `deny` per tool (e.g. `deny = ["edit(*.env)"]`).
|
|
122
|
+
Deny always wins, even in yolo.
|
|
123
|
+
- **Sandbox**: `--sandbox` runs bash under OS-level isolation
|
|
124
|
+
(writes limited to the repo, network off unless
|
|
125
|
+
`--sandbox-allow-network`).
|
|
126
|
+
- Irreversible commands (`git push --force`, `rm -rf`, …) always
|
|
127
|
+
get an explicit prompt.
|
|
128
|
+
|
|
129
|
+
## Commands
|
|
130
|
+
|
|
131
|
+
Type `/` in the REPL — the menu autocompletes. Highlights:
|
|
132
|
+
|
|
133
|
+
| | |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `/plan` | show or start the living plan |
|
|
136
|
+
| `/goal <condition>` | work until the condition is met |
|
|
137
|
+
| `/undo` · `/checkpoints` | restore / list auto-checkpoints |
|
|
138
|
+
| `/isolate` · `/review` · `/merge` · `/discard` | worktree-isolated sessions |
|
|
139
|
+
| `/model` · `/set_model` · `/effort` · `/mode` | model + approval setup |
|
|
140
|
+
| `/set_web` | web search (Serper / DuckDuckGo) |
|
|
141
|
+
| `/mcp` | list connected MCP servers |
|
|
142
|
+
| `/cost` · `/compact` · `/export` | session accounting + history |
|
|
143
|
+
| `/resume` | pick up the last session for this project |
|
|
144
|
+
| `/good` · `/bad` | credit / debit the agent's notes |
|
|
145
|
+
|
|
146
|
+
## Project context
|
|
147
|
+
|
|
148
|
+
loom-code reads `LOOM.md` / `CLAUDE.md` / `AGENTS.md` /
|
|
149
|
+
`.loom/context.md` at the project root and treats it as binding house
|
|
150
|
+
rules. `/init-loom` creates a starter file.
|
|
151
|
+
|
|
152
|
+
## Development
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
git clone https://github.com/Anurich/loomflow-cli
|
|
156
|
+
cd loomflow-cli
|
|
157
|
+
python -m venv .venv && source .venv/bin/activate
|
|
158
|
+
pip install -e ".[dev]"
|
|
159
|
+
pytest -q # 465 tests
|
|
160
|
+
ruff check .
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Architecture in one line: **loom-code is deliberately thin** — if a
|
|
164
|
+
capability belongs in the agent loop, it goes in
|
|
165
|
+
[loomflow](https://github.com/Anurich/LoomFlow), not here. See
|
|
166
|
+
`DESIGN.md` for the boundary.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""loom-code — a loomflow-native terminal coding agent.
|
|
2
|
+
|
|
3
|
+
The entire agent brain is loomflow:
|
|
4
|
+
|
|
5
|
+
* ``Agent`` + ``ReAct`` — the agent loop
|
|
6
|
+
* ``living_plan=True`` — the task tracker (Claude Code's TodoWrite)
|
|
7
|
+
* ``LocalDiskWorkspace`` — per-project memory + the self-improvement
|
|
8
|
+
loop (citation tracking + relevance-aware recall)
|
|
9
|
+
* ``read`` / ``write`` / ``edit`` / ``bash`` / ``grep`` / ``find``
|
|
10
|
+
/ ``ls`` builtin tools — the file-and-shell kernel
|
|
11
|
+
* ``StandardPermissions`` + ``approval_handler`` — the safety gate
|
|
12
|
+
* ``Agent.stream()`` — streaming output
|
|
13
|
+
|
|
14
|
+
This package is ONLY the terminal shell: REPL, ``rich`` rendering,
|
|
15
|
+
slash commands, project detection, the diff-approval prompt. If
|
|
16
|
+
agent-loop / memory / tool-dispatch logic ever shows up here, that
|
|
17
|
+
is a bug — it means loomflow is missing something and the fix
|
|
18
|
+
belongs in the framework, not here. loom-code is the dogfood test
|
|
19
|
+
that keeps loomflow honest.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
__version__ = "0.1.1"
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Post-commit hook runner — debounced indexer refresh.
|
|
2
|
+
|
|
3
|
+
Invoked by ``.git/hooks/post-commit`` (installed by
|
|
4
|
+
``loom_code.git_hook.install``). Counts commits since the last
|
|
5
|
+
refresh per indexer; when the threshold is hit (5 by default),
|
|
6
|
+
runs the indexer's incremental update.
|
|
7
|
+
|
|
8
|
+
Designed to FAIL SILENT. A git hook crashing has the same UX
|
|
9
|
+
cost as a broken commit — we'd rather skip a refresh than make
|
|
10
|
+
``git commit`` look broken. All exception handling is broad and
|
|
11
|
+
mute; the worst case is "graph stayed stale one more commit."
|
|
12
|
+
|
|
13
|
+
Why debounce: graphify rebuilds + loominit structural rebuilds
|
|
14
|
+
are fast (5-15s on typical projects) but not free. Running them
|
|
15
|
+
on every single commit during a heavy dev session (10+ commits/
|
|
16
|
+
hour) is wasteful. Every-5-commits keeps the indexes "close
|
|
17
|
+
enough" without burning cycles.
|
|
18
|
+
|
|
19
|
+
Invoked as::
|
|
20
|
+
|
|
21
|
+
python -m loom_code._post_commit <project_root>
|
|
22
|
+
|
|
23
|
+
Backgrounded by the shell hook so it doesn't delay the commit.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import sys
|
|
29
|
+
from collections.abc import Callable
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
# Refresh threshold — commits since last refresh before triggering
|
|
33
|
+
# the indexer's incremental rebuild. Empirically tuned: every
|
|
34
|
+
# commit is wasteful, every 20 is too stale to be useful, 5 sits
|
|
35
|
+
# in the goldilocks zone for typical dev pace.
|
|
36
|
+
_THRESHOLD = 5
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main() -> int:
|
|
40
|
+
if len(sys.argv) < 2:
|
|
41
|
+
return 0
|
|
42
|
+
project_root = Path(sys.argv[1])
|
|
43
|
+
loom_dir = project_root / ".loom"
|
|
44
|
+
if not loom_dir.is_dir():
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
# Graphify: incremental rebuild via the package's own
|
|
48
|
+
# ``--update`` flag (re-extracts only changed files, merges
|
|
49
|
+
# into the existing graph). Only runs if graphify has been
|
|
50
|
+
# set up at least once for this project.
|
|
51
|
+
graphify_dir = loom_dir / "graphify"
|
|
52
|
+
if (graphify_dir / "graph.json").is_file():
|
|
53
|
+
_maybe_refresh(
|
|
54
|
+
counter_file=graphify_dir / "_commits_since_refresh.txt",
|
|
55
|
+
refresh_fn=lambda: _refresh_graphify(project_root, graphify_dir),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _maybe_refresh(
|
|
62
|
+
*, counter_file: Path, refresh_fn: Callable[[], None]
|
|
63
|
+
) -> None:
|
|
64
|
+
"""Increment the counter; if it crosses the threshold, run
|
|
65
|
+
the refresh and reset. Errors are swallowed — better to skip
|
|
66
|
+
a refresh than break the commit."""
|
|
67
|
+
try:
|
|
68
|
+
count = (
|
|
69
|
+
int(counter_file.read_text())
|
|
70
|
+
if counter_file.is_file()
|
|
71
|
+
else 0
|
|
72
|
+
)
|
|
73
|
+
except (ValueError, OSError):
|
|
74
|
+
count = 0
|
|
75
|
+
count += 1
|
|
76
|
+
if count >= _THRESHOLD:
|
|
77
|
+
try:
|
|
78
|
+
refresh_fn()
|
|
79
|
+
counter_file.write_text("0")
|
|
80
|
+
except Exception: # noqa: BLE001 — never break a commit
|
|
81
|
+
# Leave counter at threshold; next commit will retry.
|
|
82
|
+
pass
|
|
83
|
+
else:
|
|
84
|
+
try:
|
|
85
|
+
counter_file.write_text(str(count))
|
|
86
|
+
except OSError:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _refresh_graphify(project_root: Path, graphify_dir: Path) -> None:
|
|
91
|
+
"""Re-run the graphify extract → build → cluster → persist
|
|
92
|
+
pipeline in-process via the shared ``graphify_build_impl``
|
|
93
|
+
helper that the ``@tool`` wrapper + ``/loominit`` already use.
|
|
94
|
+
|
|
95
|
+
Single source of truth means three things stay in sync: the
|
|
96
|
+
submodule-import shim that dodges graphify's ``__getattr__``
|
|
97
|
+
namespace shadowing, the git-ls-files fast path that skips
|
|
98
|
+
walking ``.venv`` / ``node_modules``, and the exact tree-sitter
|
|
99
|
+
/ Leiden / JSON pipeline. Bypassing it here is what caused this
|
|
100
|
+
function to silently fail on every commit before: it called
|
|
101
|
+
``graphify.extract(files)`` (a submodule, not a function),
|
|
102
|
+
passed ``[extraction]`` (build_from_json wants a dict), and
|
|
103
|
+
dropped the ``communities`` arg to ``to_json``.
|
|
104
|
+
|
|
105
|
+
Capped via subprocess from the shell hook (5 min, see the hook
|
|
106
|
+
wrapper) so a hung extraction can't block git indefinitely."""
|
|
107
|
+
# graphify_dir kept in the signature for the caller's existing
|
|
108
|
+
# path math; the impl writes to the same `.loom/graphify/graph.json`
|
|
109
|
+
# via its own ``_graph_path`` helper, so we don't need to use it.
|
|
110
|
+
_ = graphify_dir
|
|
111
|
+
import anyio
|
|
112
|
+
|
|
113
|
+
from .skills.graphify.tools import graphify_build_impl
|
|
114
|
+
|
|
115
|
+
anyio.run(graphify_build_impl, project_root)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
sys.exit(main())
|