loom-code 0.1.1__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.
- loom_code/__init__.py +22 -0
- loom_code/_post_commit.py +119 -0
- loom_code/agent.py +544 -0
- loom_code/approval.py +616 -0
- loom_code/browse/__init__.py +291 -0
- loom_code/browse/act.py +467 -0
- loom_code/browse/observe.py +249 -0
- loom_code/browse/session.py +96 -0
- loom_code/browse/verify.py +194 -0
- loom_code/checkpoint.py +283 -0
- loom_code/cli.py +495 -0
- loom_code/code_index.py +703 -0
- loom_code/compact.py +143 -0
- loom_code/consent.py +47 -0
- loom_code/credentials.py +527 -0
- loom_code/edit_tool.py +635 -0
- loom_code/extensions.py +522 -0
- loom_code/file_history.py +322 -0
- loom_code/file_tools.py +93 -0
- loom_code/git_hook.py +200 -0
- loom_code/grep_tool.py +430 -0
- loom_code/hooks.py +297 -0
- loom_code/loominit/__init__.py +23 -0
- loom_code/loominit/_ast_walk.py +429 -0
- loom_code/loominit/_files.py +284 -0
- loom_code/loominit/_graph.py +141 -0
- loom_code/loominit/_resolve.py +392 -0
- loom_code/loominit/_tests_map.py +108 -0
- loom_code/loominit/extractor.py +332 -0
- loom_code/loominit/repomap.py +225 -0
- loom_code/loominit/schema.py +242 -0
- loom_code/lsp_tools.py +396 -0
- loom_code/mcp_host.py +79 -0
- loom_code/operator.py +449 -0
- loom_code/paste.py +97 -0
- loom_code/paths.py +52 -0
- loom_code/permissions.py +177 -0
- loom_code/project.py +104 -0
- loom_code/prompts.py +451 -0
- loom_code/render.py +783 -0
- loom_code/repl.py +4080 -0
- loom_code/rules.py +267 -0
- loom_code/sandboxed_bash.py +176 -0
- loom_code/scribe.py +88 -0
- loom_code/skills/__init__.py +16 -0
- loom_code/skills/graphify/SKILL.md +97 -0
- loom_code/skills/graphify/tools.py +570 -0
- loom_code/trust.py +216 -0
- loom_code/turn.py +169 -0
- loom_code/web_fetch.py +370 -0
- loom_code/workers.py +758 -0
- loom_code/worktree.py +134 -0
- loom_code-0.1.1.dist-info/METADATA +224 -0
- loom_code-0.1.1.dist-info/RECORD +58 -0
- loom_code-0.1.1.dist-info/WHEEL +5 -0
- loom_code-0.1.1.dist-info/entry_points.txt +2 -0
- loom_code-0.1.1.dist-info/licenses/LICENSE +21 -0
- loom_code-0.1.1.dist-info/top_level.txt +1 -0
loom_code/worktree.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Git worktree lifecycle for session isolation.
|
|
2
|
+
|
|
3
|
+
An *isolated* session edits in its own git worktree on branch
|
|
4
|
+
``loom/<session_id>`` — a separate working copy of the same repo — so
|
|
5
|
+
two concurrent sessions on one repo (two ``loom-code`` terminals, two
|
|
6
|
+
desktop chat tabs) can't collide on disk. When done, the branch is
|
|
7
|
+
either merged into the base branch or discarded, and the worktree is
|
|
8
|
+
removed.
|
|
9
|
+
|
|
10
|
+
This is the SHARED lifecycle: the loom-code CLI drives it via
|
|
11
|
+
``/isolate`` `/review` `/merge` `/discard`, and the desktop sidecar can
|
|
12
|
+
use the same functions instead of its own copy.
|
|
13
|
+
|
|
14
|
+
Git-only. Every function degrades gracefully — git failures come back
|
|
15
|
+
as ``(None, error)`` / ``(False, error)`` rather than raising, so a
|
|
16
|
+
bad git state never crashes the REPL.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import subprocess
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class WorktreeInfo:
|
|
28
|
+
"""A live session worktree: where it is, its branch, and the base
|
|
29
|
+
branch it was forked from (and will merge back into)."""
|
|
30
|
+
|
|
31
|
+
path: Path
|
|
32
|
+
branch: str
|
|
33
|
+
base: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _git(
|
|
37
|
+
cwd: Path | str, args: list[str], *, timeout: int = 60
|
|
38
|
+
) -> tuple[int, str, str]:
|
|
39
|
+
"""Run a git subcommand in ``cwd``. Returns (returncode, out, err);
|
|
40
|
+
never raises on a non-zero exit — callers inspect the code."""
|
|
41
|
+
try:
|
|
42
|
+
proc = subprocess.run(
|
|
43
|
+
["git", *args],
|
|
44
|
+
cwd=str(cwd),
|
|
45
|
+
capture_output=True,
|
|
46
|
+
text=True,
|
|
47
|
+
timeout=timeout,
|
|
48
|
+
)
|
|
49
|
+
except (OSError, subprocess.SubprocessError) as exc:
|
|
50
|
+
return 1, "", str(exc)
|
|
51
|
+
return proc.returncode, proc.stdout, proc.stderr
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def is_git_repo(root: Path | str) -> bool:
|
|
55
|
+
return (Path(root) / ".git").exists()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def worktree_path(root: Path | str, session_id: str) -> Path:
|
|
59
|
+
return Path(root) / ".loom" / "worktrees" / session_id
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def branch_name(session_id: str) -> str:
|
|
63
|
+
# session ids (chat-<...> / ULIDs) are ref-safe; "loom/" namespaces
|
|
64
|
+
# them so they're easy to spot + bulk-clean.
|
|
65
|
+
return f"loom/{session_id}"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def current_branch(root: Path | str) -> str:
|
|
69
|
+
rc, out, _ = _git(root, ["symbolic-ref", "--short", "HEAD"])
|
|
70
|
+
return out.strip() if rc == 0 and out.strip() else "HEAD"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def create(
|
|
74
|
+
root: Path | str, session_id: str
|
|
75
|
+
) -> tuple[WorktreeInfo | None, str]:
|
|
76
|
+
"""Create a worktree for ``session_id``. Returns ``(info, "")`` or
|
|
77
|
+
``(None, error)``. Idempotent-ish: if the branch already exists
|
|
78
|
+
(re-isolate after a discard) it's reused."""
|
|
79
|
+
root = Path(root)
|
|
80
|
+
if not is_git_repo(root):
|
|
81
|
+
return None, "not a git repository"
|
|
82
|
+
wt = worktree_path(root, session_id)
|
|
83
|
+
branch = branch_name(session_id)
|
|
84
|
+
base = current_branch(root)
|
|
85
|
+
try:
|
|
86
|
+
wt.parent.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
except OSError as exc:
|
|
88
|
+
return None, f"mkdir failed: {exc}"
|
|
89
|
+
rc, _out, err = _git(root, ["worktree", "add", str(wt), "-b", branch])
|
|
90
|
+
if rc != 0:
|
|
91
|
+
rc2, _o2, err2 = _git(root, ["worktree", "add", str(wt), branch])
|
|
92
|
+
if rc2 != 0:
|
|
93
|
+
return None, (err or err2).strip()
|
|
94
|
+
return WorktreeInfo(path=wt, branch=branch, base=base), ""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def diff(info: WorktreeInfo) -> tuple[str, str]:
|
|
98
|
+
"""Unified diff of the worktree (committed-on-branch + uncommitted)
|
|
99
|
+
vs the base branch. Returns ``(diff_text, error)``."""
|
|
100
|
+
rc, out, err = _git(info.path, ["diff", info.base])
|
|
101
|
+
if rc != 0:
|
|
102
|
+
return "", err.strip()
|
|
103
|
+
return out, ""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def merge(root: Path | str, info: WorktreeInfo) -> tuple[bool, str]:
|
|
107
|
+
"""Commit the worktree's uncommitted edits (only when dirty), then
|
|
108
|
+
merge its branch into the base branch from the MAIN tree. Returns
|
|
109
|
+
``(ok, error)``. Refuses if the main tree isn't on the base branch
|
|
110
|
+
(to avoid merging into the wrong branch); aborts on conflict."""
|
|
111
|
+
root = Path(root)
|
|
112
|
+
rc, out, _ = _git(info.path, ["status", "--porcelain"])
|
|
113
|
+
if rc == 0 and out.strip():
|
|
114
|
+
_git(info.path, ["add", "-A"])
|
|
115
|
+
_git(info.path, ["commit", "-m", f"loom session {info.branch}"])
|
|
116
|
+
cur = current_branch(root)
|
|
117
|
+
if cur != info.base:
|
|
118
|
+
return False, (
|
|
119
|
+
f"the main working tree is on '{cur}', not the session's "
|
|
120
|
+
f"base '{info.base}' — switch back before merging"
|
|
121
|
+
)
|
|
122
|
+
rc, _o, err = _git(root, ["merge", "--no-edit", info.branch])
|
|
123
|
+
if rc != 0:
|
|
124
|
+
_git(root, ["merge", "--abort"])
|
|
125
|
+
return False, f"merge conflict: {err.strip()}"
|
|
126
|
+
return True, ""
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def remove(root: Path | str, info: WorktreeInfo) -> None:
|
|
130
|
+
"""Remove the worktree + delete its branch. Best-effort — used on
|
|
131
|
+
both discard and post-merge cleanup."""
|
|
132
|
+
root = Path(root)
|
|
133
|
+
_git(root, ["worktree", "remove", "--force", str(info.path)])
|
|
134
|
+
_git(root, ["branch", "-D", info.branch])
|
|
@@ -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,58 @@
|
|
|
1
|
+
loom_code/__init__.py,sha256=am0nt0omKYSWlzGtoVZC2JScZoNy1ncT2SCCIraw5XY,949
|
|
2
|
+
loom_code/_post_commit.py,sha256=wM1E9T-52dXbTF_eHzb6Ve_F02yPTOU1OXGUf1C7hu4,4239
|
|
3
|
+
loom_code/agent.py,sha256=DslJCjfO1P5LrMVBFjCjTrebB1_AMM3GxDuqqrDMjVU,24809
|
|
4
|
+
loom_code/approval.py,sha256=2lNZC4inyF_qggySVxlnJVlZwQgM9GGHFHyWR0E547k,22791
|
|
5
|
+
loom_code/checkpoint.py,sha256=9lCSo88PVfK9_KquX6EqO5nQbIuDdM5Ip2asTNHFDng,10282
|
|
6
|
+
loom_code/cli.py,sha256=GPy5nrXnShfgMtEg0JG7dBn6UCiVhVzPAZ1CKQomic0,17570
|
|
7
|
+
loom_code/code_index.py,sha256=nlUfIXKZ6xMRoMPGl_Scu93i52OZSapBI3_BGqYYMD4,27553
|
|
8
|
+
loom_code/compact.py,sha256=45-iCdMAJ5wcX6lWab8_NZZ_z3VfJO3mAZwNHFsAyUY,5456
|
|
9
|
+
loom_code/consent.py,sha256=Qnyo6TymAmOUxv8JAdv3Px74CJvcF_dXv0xUb3vuGOQ,1721
|
|
10
|
+
loom_code/credentials.py,sha256=0cUjElx6hLBYieeucqzlZAe_ey-nMXpJw1K17nljOn8,21404
|
|
11
|
+
loom_code/edit_tool.py,sha256=34cB9TRpTSfMnD7vFX5kMCrJ9i4SPbUoMOBBEOtmDio,25703
|
|
12
|
+
loom_code/extensions.py,sha256=ClCbZ-anomkTKXUMDrpBdZRp4t07eBXv4zvR8XEPO7A,19031
|
|
13
|
+
loom_code/file_history.py,sha256=6MRi6khIVLt-oF6F4M4OgtaIhPpWs6MeC4GOz0AfAoI,11574
|
|
14
|
+
loom_code/file_tools.py,sha256=08c1jrc9nDxL_jgvWInO-uqhXOscY6exZdUwzBFVGK0,4016
|
|
15
|
+
loom_code/git_hook.py,sha256=gx556a385-dEsctz-HqBj4m-3273PIUT-48x8puH8mM,7415
|
|
16
|
+
loom_code/grep_tool.py,sha256=wtQYRkE6OpY_EeUdd8geLuj4MWt8C2iKFv96ZqLjat0,16593
|
|
17
|
+
loom_code/hooks.py,sha256=x-LX92jOn--uBmDPNcsW1K1h5nqxAGJrfDQZH6FXPow,10992
|
|
18
|
+
loom_code/lsp_tools.py,sha256=R73TSwRs9ZgiKgfJ5xP4sUiBvm5tYlyJjDZhJ8Qq8ss,14739
|
|
19
|
+
loom_code/mcp_host.py,sha256=a-JrPfJKE7s8YuZJFNIAGoH73UwUdFT8zzGtNe0YepU,3165
|
|
20
|
+
loom_code/operator.py,sha256=Qbhnz8CSdZBGi-7DGggP1xbOy4n_8Fd0qqy20vLs3Kc,20773
|
|
21
|
+
loom_code/paste.py,sha256=o7Zq70aCU6HQB6Gziez8jNP8KBVBTbnqAIfeVOnXFww,3437
|
|
22
|
+
loom_code/paths.py,sha256=HHFbDkpXgIQWND1XGWSao_9RVpF9HyZ2fCEpOWDcY2E,2044
|
|
23
|
+
loom_code/permissions.py,sha256=RkxqiHu1eqwaVyaLGHinW4amz69S9i8zC9lW0OTH1h8,6314
|
|
24
|
+
loom_code/project.py,sha256=iEUQOYUA23d06ZlaAcNouei635t5K-B_E-Ri0DPZEB0,3639
|
|
25
|
+
loom_code/prompts.py,sha256=i0NoblhIk58UhQTkHuOFi3yqenDs9C60KpFPLP-P7iQ,21892
|
|
26
|
+
loom_code/render.py,sha256=6fuGpC74WBAw-31qrqwa5XEfMVw_ZZqYEI0eqJ9jYm0,33756
|
|
27
|
+
loom_code/repl.py,sha256=Tja7idGyeAOOsdAmLkTA0wVbKSwBQieGhPyEUe4Emkw,174054
|
|
28
|
+
loom_code/rules.py,sha256=pdy1t8L7ic9nE5bSJPs_FoHxfKdhK0A6-c8JIws7ZMM,9778
|
|
29
|
+
loom_code/sandboxed_bash.py,sha256=kfDfKwgTj7MUjUPutxRUykytjgY8qOjih2OZlRnL88g,6438
|
|
30
|
+
loom_code/scribe.py,sha256=Lz4pT4Xu9RquoQafgaOmkIaodFI252Dttr_6ycKgmMI,3145
|
|
31
|
+
loom_code/trust.py,sha256=Eimvs0OwE9rr8Ve3c3if4wiQaRB6kfgIi0XA_23Hmsc,7655
|
|
32
|
+
loom_code/turn.py,sha256=vl3dZkFVAoiBYpAfsXB5RUSjFcuqHTpxgzwK0MWV1nE,5773
|
|
33
|
+
loom_code/web_fetch.py,sha256=IfMua8sTmCB61T0gpWxe3oC1yst0Ul4tEXmzy1rIaZU,15386
|
|
34
|
+
loom_code/workers.py,sha256=e_mZx-bg5M6g4vEnGxgReJiDivGLYqa_cSZ7Opa4s9w,31333
|
|
35
|
+
loom_code/worktree.py,sha256=CKbiYAvVd6OS3eX89NIeB2KJ1O_rNfUlIuLNu1uZXO8,4782
|
|
36
|
+
loom_code/browse/__init__.py,sha256=nYi8jQBzLGDCZb0W0s6IYX4EV0K2pgJTz304j6tdk2s,12741
|
|
37
|
+
loom_code/browse/act.py,sha256=5h3DeBKJkDbUjniXPBSbqq1vEg23QRlwGwi8HpBtKSM,17015
|
|
38
|
+
loom_code/browse/observe.py,sha256=PJi6OmrIX93MO1Ri5R9qp5kFjs6RhKJmpF5beE0il6E,9337
|
|
39
|
+
loom_code/browse/session.py,sha256=RNPA39oNrhYN_ZTK1dIl2hGL3BNSDkvRI6dmD3jhb-g,3778
|
|
40
|
+
loom_code/browse/verify.py,sha256=zuJhy17BR6iLPu2EZ0bqklxIV72NCrJQY9EHsOj1zG4,7660
|
|
41
|
+
loom_code/loominit/__init__.py,sha256=R4UDbwhmwAOg9QD24zbxeqG4vbdIIrfsFST9FxSzgrM,1166
|
|
42
|
+
loom_code/loominit/_ast_walk.py,sha256=4iSF0SUxSp3gDGVqqeB8TyDrOes3SaLpUu6554qcHTA,15784
|
|
43
|
+
loom_code/loominit/_files.py,sha256=CQWiMf25RRWrePpHlAkwbz95T1T7xJ9bz7hojA0mcTY,9212
|
|
44
|
+
loom_code/loominit/_graph.py,sha256=vtZBdIEyFeXjf9XLrnS0l2p0lMqw09os4_5MMFnmHfA,5184
|
|
45
|
+
loom_code/loominit/_resolve.py,sha256=f-zPHDvSAEn5uw30I9UEIE-OMYo1UYPKWSPlJHqQiFc,14035
|
|
46
|
+
loom_code/loominit/_tests_map.py,sha256=Na6bfsj5gpG-_y1_jT0smRIvs7i3LeSrs_c6e-UkJiI,3604
|
|
47
|
+
loom_code/loominit/extractor.py,sha256=WmMEXAVadPw6vYIB0PiBZwofr_x647T8GFfMP1CzYZY,11803
|
|
48
|
+
loom_code/loominit/repomap.py,sha256=gyyDgfMmo8mNNwWsj4aZPLJxJfJSkz-P65ijluM6C1s,8307
|
|
49
|
+
loom_code/loominit/schema.py,sha256=WVHREn_xXjGtafmMBPkQUeeSFplnkj0ZNTTzw37gl70,9037
|
|
50
|
+
loom_code/skills/__init__.py,sha256=_CeYfPjc82C6AXOIJ8gvhdB-2YbYqx9OwPoZ0_k1U9A,641
|
|
51
|
+
loom_code/skills/graphify/SKILL.md,sha256=sCsZ_J3i1mSCM6IV_WsYVu84LbYOXXbQmF5hy5alSPg,4424
|
|
52
|
+
loom_code/skills/graphify/tools.py,sha256=67LVXOzYE2cM3T061xxuQaQLDXjR810GX_wMoUG1hUM,23316
|
|
53
|
+
loom_code-0.1.1.dist-info/licenses/LICENSE,sha256=Qw2tq9Jw4fNrucoVwMew3TJv281S77WxU4pWvlf9YUk,1072
|
|
54
|
+
loom_code-0.1.1.dist-info/METADATA,sha256=xoztBTcjQiIjfs5qT1fxmVgZnfG_1yKjkVGGAapLq4o,9062
|
|
55
|
+
loom_code-0.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
56
|
+
loom_code-0.1.1.dist-info/entry_points.txt,sha256=yRqKU6PZCzbkM3bdbNK3uLEzjkIhTTP3YO5I30H3cPY,49
|
|
57
|
+
loom_code-0.1.1.dist-info/top_level.txt,sha256=scpQuHtufFFRPlPQjrCqkCPWpH-vdjlwZ_H6pH_AbvE,10
|
|
58
|
+
loom_code-0.1.1.dist-info/RECORD,,
|
|
@@ -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.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
loom_code
|