crow-cli 0.1.30__tar.gz → 0.1.32__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.
- {crow_cli-0.1.30 → crow_cli-0.1.32}/PKG-INFO +18 -22
- {crow_cli-0.1.30 → crow_cli-0.1.32}/README.md +13 -16
- {crow_cli-0.1.30 → crow_cli-0.1.32}/pyproject.toml +5 -15
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/compact.py +2 -2
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/configure.py +55 -13
- crow_cli-0.1.32/src/crow_cli/agent/db.py +393 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/default/defaults.py +53 -13
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/logger.py +3 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/main.py +93 -32
- crow_cli-0.1.32/src/crow_cli/agent/memory.py +320 -0
- crow_cli-0.1.32/src/crow_cli/agent/model_routing.py +137 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/react.py +97 -9
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/session.py +73 -72
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/cli/init_cmd.py +12 -27
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/cli/main.py +191 -84
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/client/main.py +101 -12
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/analyze_payload_cache_invalidation.py +1 -1
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/analyze_payload_deep.py +1 -1
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/conftest.py +39 -36
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/integration/test_auth_validation.py +2 -2
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_compact.py +8 -8
- crow_cli-0.1.32/tests/unit/test_configure.py +44 -0
- crow_cli-0.1.32/tests/unit/test_db.py +100 -0
- crow_cli-0.1.32/tests/unit/test_model_override.py +39 -0
- crow_cli-0.1.32/tests/unit/test_model_routing.py +339 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_session.py +52 -4
- crow_cli-0.1.32/tests/unit/test_system_prompt_skills.py +26 -0
- crow_cli-0.1.32/tests/unit/test_usage_update.py +40 -0
- crow_cli-0.1.32/uv.lock +1678 -0
- crow_cli-0.1.30/src/crow_cli/agent/memory.py +0 -164
- crow_cli-0.1.30/uv.lock +0 -2930
- {crow_cli-0.1.30 → crow_cli-0.1.32}/.gitignore +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/.python-version +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/agent.json +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/crow-cli.spec +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/examples/mc_escher_loop.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/examples/quick_test.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/run_tests.sh +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/__init__.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/__init__.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/context.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/default/__init__.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/default/searxng_settings.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/hooks.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/llm.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/mcp_client.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/prompt.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/slash.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/tools.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent_runner.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/cli/__init__.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/cli/install.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/client/__init__.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/client/terminal.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/test_send_request_error_handling.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/__init__.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/e2e/test_session_update_transmission.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/integration/test_agent_init.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_client_terminal.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_compact_last_messages_list_content.json +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_compact_last_messages_list_content.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_json_repair.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_request_logging.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_stream_processing.py +0 -0
- {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_uv_project_enforcement.py +0 -0
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: crow-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.32
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Project-URL: Homepage, https://github.com/crow-cli/crow-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/crow-cli/crow-cli
|
|
7
7
|
Project-URL: Issues, https://github.com/crow-cli/crow-cli/issues
|
|
8
8
|
Project-URL: Documentation, https://github.com/crow-cli/crow-cli#readme
|
|
9
9
|
Requires-Python: >=3.14
|
|
10
|
-
Requires-Dist: agent-client-protocol
|
|
10
|
+
Requires-Dist: agent-client-protocol<0.13.0,>=0.12.0
|
|
11
11
|
Requires-Dist: coolname>=2.2.0
|
|
12
|
-
Requires-Dist: crow-memory>=0.1.30
|
|
13
12
|
Requires-Dist: directory-tree>=1.0.0
|
|
13
|
+
Requires-Dist: docker>=7.2.0
|
|
14
14
|
Requires-Dist: fastmcp>=2.14.5
|
|
15
15
|
Requires-Dist: httpx>=0.28.1
|
|
16
|
-
Requires-Dist: ipython>=9.10.0
|
|
17
16
|
Requires-Dist: jinja2>=3.1.6
|
|
18
|
-
Requires-Dist: json-schema-to-pydantic>=0.4.9
|
|
19
17
|
Requires-Dist: openai>=2.21.0
|
|
20
18
|
Requires-Dist: pyyaml>=6.0.3
|
|
21
19
|
Requires-Dist: rich>=14.3.3
|
|
20
|
+
Requires-Dist: sqlalchemy>=2.0.51
|
|
22
21
|
Requires-Dist: typer>=0.24.1
|
|
23
22
|
Description-Content-Type: text/markdown
|
|
24
23
|
|
|
@@ -34,12 +33,12 @@ Description-Content-Type: text/markdown
|
|
|
34
33
|
|
|
35
34
|
`crow-cli` is an [Agent Client Protocol (ACP)](https://agentclientprotocol.com/) coding agent that runs in your terminal and inside ACP-compatible editors. It reads and edits code, runs shell commands, searches the web, and remembers your work across sessions.
|
|
36
35
|
|
|
37
|
-
Most agent toolkits treat persistence as an afterthought. crow-cli treats it as the point: every session lives in a
|
|
36
|
+
Most agent toolkits treat persistence as an afterthought. crow-cli treats it as the point: every session lives in a local sqlite database (`~/.agents/crow/crow.db`) with FTS5 full-text search, so agents recall past conversations and can delegate work to one another. Images are stored as files next to the database and hydrated only when sent to the LLM. Sessions get memorable coolname ids (like `taupe-squirrel-of-splendid-potency`) you can resume or read from any other agent.
|
|
38
37
|
|
|
39
38
|
## Requirements
|
|
40
39
|
|
|
41
40
|
- Python 3.14+, managed with [uv](https://docs.astral.sh/uv/)
|
|
42
|
-
- Docker, for the
|
|
41
|
+
- Docker, for the SearXNG service
|
|
43
42
|
- An API key for an OpenAI-compatible LLM provider (OpenRouter, OpenAI, your own endpoint, …)
|
|
44
43
|
|
|
45
44
|
| Platform | Notes |
|
|
@@ -61,11 +60,11 @@ uv tool install crow-cli --python 3.14 # or run without installing: uvx cro
|
|
|
61
60
|
Initialize your configuration and start the backing services:
|
|
62
61
|
|
|
63
62
|
```bash
|
|
64
|
-
crow-cli init # scaffolds ~/.crow (config.yaml, .env, docker-compose)
|
|
65
|
-
cd ~/.crow && docker compose up -d # starts
|
|
63
|
+
crow-cli init # scaffolds ~/.agents/crow (config.yaml, .env, docker-compose)
|
|
64
|
+
cd ~/.agents/crow && docker compose up -d # starts SearXNG
|
|
66
65
|
```
|
|
67
66
|
|
|
68
|
-
`crow-cli init` walks you through provider and model selection and writes your secrets to `~/.crow/.env`, referenced from the config as `${VAR}`.
|
|
67
|
+
`crow-cli init` walks you through provider and model selection and writes your secrets to `~/.agents/crow/.env`, referenced from the config as `${VAR}`.
|
|
69
68
|
|
|
70
69
|
## Quick start
|
|
71
70
|
|
|
@@ -113,17 +112,17 @@ crow-cli is a monorepo. The pieces:
|
|
|
113
112
|
|
|
114
113
|
### crow-cli — the agent
|
|
115
114
|
|
|
116
|
-
The ACP-native agent: a streaming ReAct loop with tool calling, cancellation, conversation compaction, and multimodal input. Provider and model configuration lives in `~/.crow/config.yaml`.
|
|
115
|
+
The ACP-native agent: a streaming ReAct loop with tool calling, cancellation, conversation compaction, and multimodal input. Provider and model configuration lives in `~/.agents/crow/config.yaml`.
|
|
117
116
|
|
|
118
|
-
###
|
|
117
|
+
### Persistence — sqlite memory
|
|
119
118
|
|
|
120
|
-
|
|
119
|
+
Sessions persist to a single sqlite database (`~/.agents/crow/crow.db`, schema v3, WAL mode) with an FTS5 index for BM25 keyword search. Images in messages are written to `~/.agents/crow/images/` and referenced by path; they are hydrated to base64 data URLs only when the conversation is sent to the LLM. The same database backs the memory API, exposed to agents as three tools:
|
|
121
120
|
|
|
122
121
|
- `list_sessions()` — sessions ordered by recent activity (who's working on what)
|
|
123
122
|
- `query_memory(query)` — find which session discussed something, across all sessions
|
|
124
123
|
- `query_session(session_id)` — read or search within one session (spans all of that session's agents)
|
|
125
124
|
|
|
126
|
-
This is what makes multi-agent delegation work: launch a worker, then read its thoughts from any other agent.
|
|
125
|
+
This is what makes multi-agent delegation work: launch a worker, then read its thoughts from any other agent. No service to run — the sqlite file is the integration point.
|
|
127
126
|
|
|
128
127
|
### crow-mcp — the tool server
|
|
129
128
|
|
|
@@ -137,7 +136,7 @@ The built-in [MCP](https://modelcontextprotocol.io/) server providing the agent'
|
|
|
137
136
|
| `capture_webcam` / `read_image_file` | Vision input |
|
|
138
137
|
| `list_sessions` / `query_memory` / `query_session` | Memory (see above) |
|
|
139
138
|
|
|
140
|
-
**Extensible by design:** register any MCP server in `~/.crow/config.yaml` and its tools appear alongside these automatically.
|
|
139
|
+
**Extensible by design:** register any MCP server in `~/.agents/crow/config.yaml` and its tools appear alongside these automatically.
|
|
141
140
|
|
|
142
141
|
> ⚠️ **Tool names are not namespaced.** crow-mcp registers its tools as `read`, `edit`, `terminal`, … — not `crow-mcp_read`. When you add your own MCP servers, watch for name collisions.
|
|
143
142
|
|
|
@@ -147,11 +146,11 @@ crow-cli ships a maintained SearXNG configuration (stored as JSON so the agent c
|
|
|
147
146
|
|
|
148
147
|
### Skills
|
|
149
148
|
|
|
150
|
-
Agents load reusable skills from `~/.
|
|
149
|
+
Agents load reusable skills from `~/.agents/skills/` — each a directory with a `SKILL.md` describing when and how to use it. Skill distribution is still being worked out; today skills are local directories.
|
|
151
150
|
|
|
152
151
|
## Configuration
|
|
153
152
|
|
|
154
|
-
`~/.crow/config.yaml` holds providers, models, and MCP servers; secrets live in `~/.crow/.env` and are interpolated with `${VAR}`.
|
|
153
|
+
`~/.agents/crow/config.yaml` holds providers, models, and MCP servers; secrets live in `~/.agents/crow/.env` and are interpolated with `${VAR}`.
|
|
155
154
|
|
|
156
155
|
```yaml
|
|
157
156
|
providers:
|
|
@@ -178,7 +177,7 @@ Run the unit tests — fast and hermetic, no services required (tests that touch
|
|
|
178
177
|
uv run --project crow-cli pytest crow-cli/tests/unit
|
|
179
178
|
```
|
|
180
179
|
|
|
181
|
-
The persistence layer itself is tested in `crow-
|
|
180
|
+
The persistence layer itself is tested in `crow-cli/tests/unit/test_db.py`. Integration and end-to-end tiers are opt-in:
|
|
182
181
|
|
|
183
182
|
```bash
|
|
184
183
|
uv run --project crow-cli pytest crow-cli/tests --run-integration # spawn the agent
|
|
@@ -190,9 +189,6 @@ uv run --project crow-cli pytest crow-cli/tests --run-e2e # live LLM c
|
|
|
190
189
|
```
|
|
191
190
|
crow-cli/ the agent — ACP server, ReAct loop, CLI
|
|
192
191
|
crow-mcp/ built-in MCP tool server
|
|
193
|
-
crow-memory/ persistence + memory service (LanceDB, ColBERT/ColPali)
|
|
194
|
-
crow-task-mcp/ task-list MCP server for delegation (being reworked)
|
|
195
|
-
crow-orchestrator-mcp/ orchestration MCP server for delegation (being reworked)
|
|
196
192
|
```
|
|
197
193
|
|
|
198
194
|
## License
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
`crow-cli` is an [Agent Client Protocol (ACP)](https://agentclientprotocol.com/) coding agent that runs in your terminal and inside ACP-compatible editors. It reads and edits code, runs shell commands, searches the web, and remembers your work across sessions.
|
|
12
12
|
|
|
13
|
-
Most agent toolkits treat persistence as an afterthought. crow-cli treats it as the point: every session lives in a
|
|
13
|
+
Most agent toolkits treat persistence as an afterthought. crow-cli treats it as the point: every session lives in a local sqlite database (`~/.agents/crow/crow.db`) with FTS5 full-text search, so agents recall past conversations and can delegate work to one another. Images are stored as files next to the database and hydrated only when sent to the LLM. Sessions get memorable coolname ids (like `taupe-squirrel-of-splendid-potency`) you can resume or read from any other agent.
|
|
14
14
|
|
|
15
15
|
## Requirements
|
|
16
16
|
|
|
17
17
|
- Python 3.14+, managed with [uv](https://docs.astral.sh/uv/)
|
|
18
|
-
- Docker, for the
|
|
18
|
+
- Docker, for the SearXNG service
|
|
19
19
|
- An API key for an OpenAI-compatible LLM provider (OpenRouter, OpenAI, your own endpoint, …)
|
|
20
20
|
|
|
21
21
|
| Platform | Notes |
|
|
@@ -37,11 +37,11 @@ uv tool install crow-cli --python 3.14 # or run without installing: uvx cro
|
|
|
37
37
|
Initialize your configuration and start the backing services:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
crow-cli init # scaffolds ~/.crow (config.yaml, .env, docker-compose)
|
|
41
|
-
cd ~/.crow && docker compose up -d # starts
|
|
40
|
+
crow-cli init # scaffolds ~/.agents/crow (config.yaml, .env, docker-compose)
|
|
41
|
+
cd ~/.agents/crow && docker compose up -d # starts SearXNG
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
`crow-cli init` walks you through provider and model selection and writes your secrets to `~/.crow/.env`, referenced from the config as `${VAR}`.
|
|
44
|
+
`crow-cli init` walks you through provider and model selection and writes your secrets to `~/.agents/crow/.env`, referenced from the config as `${VAR}`.
|
|
45
45
|
|
|
46
46
|
## Quick start
|
|
47
47
|
|
|
@@ -89,17 +89,17 @@ crow-cli is a monorepo. The pieces:
|
|
|
89
89
|
|
|
90
90
|
### crow-cli — the agent
|
|
91
91
|
|
|
92
|
-
The ACP-native agent: a streaming ReAct loop with tool calling, cancellation, conversation compaction, and multimodal input. Provider and model configuration lives in `~/.crow/config.yaml`.
|
|
92
|
+
The ACP-native agent: a streaming ReAct loop with tool calling, cancellation, conversation compaction, and multimodal input. Provider and model configuration lives in `~/.agents/crow/config.yaml`.
|
|
93
93
|
|
|
94
|
-
###
|
|
94
|
+
### Persistence — sqlite memory
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
Sessions persist to a single sqlite database (`~/.agents/crow/crow.db`, schema v3, WAL mode) with an FTS5 index for BM25 keyword search. Images in messages are written to `~/.agents/crow/images/` and referenced by path; they are hydrated to base64 data URLs only when the conversation is sent to the LLM. The same database backs the memory API, exposed to agents as three tools:
|
|
97
97
|
|
|
98
98
|
- `list_sessions()` — sessions ordered by recent activity (who's working on what)
|
|
99
99
|
- `query_memory(query)` — find which session discussed something, across all sessions
|
|
100
100
|
- `query_session(session_id)` — read or search within one session (spans all of that session's agents)
|
|
101
101
|
|
|
102
|
-
This is what makes multi-agent delegation work: launch a worker, then read its thoughts from any other agent.
|
|
102
|
+
This is what makes multi-agent delegation work: launch a worker, then read its thoughts from any other agent. No service to run — the sqlite file is the integration point.
|
|
103
103
|
|
|
104
104
|
### crow-mcp — the tool server
|
|
105
105
|
|
|
@@ -113,7 +113,7 @@ The built-in [MCP](https://modelcontextprotocol.io/) server providing the agent'
|
|
|
113
113
|
| `capture_webcam` / `read_image_file` | Vision input |
|
|
114
114
|
| `list_sessions` / `query_memory` / `query_session` | Memory (see above) |
|
|
115
115
|
|
|
116
|
-
**Extensible by design:** register any MCP server in `~/.crow/config.yaml` and its tools appear alongside these automatically.
|
|
116
|
+
**Extensible by design:** register any MCP server in `~/.agents/crow/config.yaml` and its tools appear alongside these automatically.
|
|
117
117
|
|
|
118
118
|
> ⚠️ **Tool names are not namespaced.** crow-mcp registers its tools as `read`, `edit`, `terminal`, … — not `crow-mcp_read`. When you add your own MCP servers, watch for name collisions.
|
|
119
119
|
|
|
@@ -123,11 +123,11 @@ crow-cli ships a maintained SearXNG configuration (stored as JSON so the agent c
|
|
|
123
123
|
|
|
124
124
|
### Skills
|
|
125
125
|
|
|
126
|
-
Agents load reusable skills from `~/.
|
|
126
|
+
Agents load reusable skills from `~/.agents/skills/` — each a directory with a `SKILL.md` describing when and how to use it. Skill distribution is still being worked out; today skills are local directories.
|
|
127
127
|
|
|
128
128
|
## Configuration
|
|
129
129
|
|
|
130
|
-
`~/.crow/config.yaml` holds providers, models, and MCP servers; secrets live in `~/.crow/.env` and are interpolated with `${VAR}`.
|
|
130
|
+
`~/.agents/crow/config.yaml` holds providers, models, and MCP servers; secrets live in `~/.agents/crow/.env` and are interpolated with `${VAR}`.
|
|
131
131
|
|
|
132
132
|
```yaml
|
|
133
133
|
providers:
|
|
@@ -154,7 +154,7 @@ Run the unit tests — fast and hermetic, no services required (tests that touch
|
|
|
154
154
|
uv run --project crow-cli pytest crow-cli/tests/unit
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
The persistence layer itself is tested in `crow-
|
|
157
|
+
The persistence layer itself is tested in `crow-cli/tests/unit/test_db.py`. Integration and end-to-end tiers are opt-in:
|
|
158
158
|
|
|
159
159
|
```bash
|
|
160
160
|
uv run --project crow-cli pytest crow-cli/tests --run-integration # spawn the agent
|
|
@@ -166,9 +166,6 @@ uv run --project crow-cli pytest crow-cli/tests --run-e2e # live LLM c
|
|
|
166
166
|
```
|
|
167
167
|
crow-cli/ the agent — ACP server, ReAct loop, CLI
|
|
168
168
|
crow-mcp/ built-in MCP tool server
|
|
169
|
-
crow-memory/ persistence + memory service (LanceDB, ColBERT/ColPali)
|
|
170
|
-
crow-task-mcp/ task-list MCP server for delegation (being reworked)
|
|
171
|
-
crow-orchestrator-mcp/ orchestration MCP server for delegation (being reworked)
|
|
172
169
|
```
|
|
173
170
|
|
|
174
171
|
## License
|
|
@@ -1,35 +1,24 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "crow-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.32"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.14"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"agent-client-protocol>=0.
|
|
8
|
+
"agent-client-protocol>=0.12.0,<0.13.0",
|
|
9
9
|
"coolname>=2.2.0",
|
|
10
|
-
"crow-memory>=0.1.30",
|
|
11
10
|
"directory-tree>=1.0.0",
|
|
11
|
+
"docker>=7.2.0",
|
|
12
12
|
"fastmcp>=2.14.5",
|
|
13
13
|
"httpx>=0.28.1",
|
|
14
|
-
"ipython>=9.10.0",
|
|
15
14
|
"jinja2>=3.1.6",
|
|
16
|
-
"json-schema-to-pydantic>=0.4.9",
|
|
17
15
|
"openai>=2.21.0",
|
|
18
16
|
"pyyaml>=6.0.3",
|
|
19
17
|
"rich>=14.3.3",
|
|
18
|
+
"sqlalchemy>=2.0.51",
|
|
20
19
|
"typer>=0.24.1",
|
|
21
20
|
]
|
|
22
21
|
|
|
23
|
-
[tool.uv.sources]
|
|
24
|
-
crow-memory = { path = "../crow-memory", editable = true }
|
|
25
|
-
torch = [{ index = "pytorch-cpu" }]
|
|
26
|
-
torchvision = [{ index = "pytorch-cpu" }]
|
|
27
|
-
|
|
28
|
-
[[tool.uv.index]]
|
|
29
|
-
name = "pytorch-cpu"
|
|
30
|
-
url = "https://download.pytorch.org/whl/cpu"
|
|
31
|
-
explicit = true
|
|
32
|
-
|
|
33
22
|
|
|
34
23
|
[project.urls]
|
|
35
24
|
Homepage = "https://github.com/crow-cli/crow-cli"
|
|
@@ -49,6 +38,7 @@ packages = ["src/crow_cli"]
|
|
|
49
38
|
|
|
50
39
|
[dependency-groups]
|
|
51
40
|
dev = [
|
|
41
|
+
"ipython>=9.10.0",
|
|
52
42
|
"pytest>=9.0.2",
|
|
53
43
|
"pytest-asyncio>=0.23.0",
|
|
54
44
|
"pyinstaller>=6.0.0",
|
|
@@ -146,7 +146,7 @@ async def compact(
|
|
|
146
146
|
# 5. Create new agent record: same session_id, next agent_idx
|
|
147
147
|
new_agent_idx = original_agent_idx + 1
|
|
148
148
|
new_agent_id = f"{original_session_id}-{new_agent_idx}"
|
|
149
|
-
new_session = make_agent_session(
|
|
149
|
+
new_session = await make_agent_session(
|
|
150
150
|
config,
|
|
151
151
|
session.tools,
|
|
152
152
|
session.model_identifier if session.model_identifier else "",
|
|
@@ -157,7 +157,7 @@ async def compact(
|
|
|
157
157
|
|
|
158
158
|
last_msgs = last_messages(session)
|
|
159
159
|
new_agent_prompt = f"{summary}\n\nLast messages:\n\n{last_msgs}"
|
|
160
|
-
new_session.add_message(
|
|
160
|
+
await new_session.add_message(
|
|
161
161
|
{"role": "user", "content": new_agent_prompt},
|
|
162
162
|
)
|
|
163
163
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Configuration management for crow-cli.
|
|
3
3
|
|
|
4
4
|
Default config files are Python string constants imported from defaults.
|
|
5
|
-
On first access to ~/.crow, defaults are written to disk if nothing exists.
|
|
5
|
+
On first access to ~/.agents/crow, defaults are written to disk if nothing exists.
|
|
6
6
|
The user's config.yaml is read from disk for actual runtime config.
|
|
7
7
|
"""
|
|
8
8
|
|
|
@@ -25,13 +25,19 @@ from crow_cli.agent.logger import setup_logger
|
|
|
25
25
|
|
|
26
26
|
ENV_PATTERN = re.compile(r"\$\{([^}]+)\}")
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
AGENTS_DIR = Path.home() / ".agents"
|
|
29
|
+
# Config lives under ~/.agents/crow; the shared workspaces (skills, notes,
|
|
30
|
+
# global AGENTS.md) are SIBLINGS of the config dir, directly under ~/.agents.
|
|
31
|
+
DEFAULT_CONFIG_DIR = AGENTS_DIR / "crow"
|
|
32
|
+
SKILLS_DIR = AGENTS_DIR / "skills"
|
|
33
|
+
NOTES_DIR = AGENTS_DIR / "notes"
|
|
34
|
+
GLOBAL_AGENTS_MD = AGENTS_DIR / "AGENTS.md"
|
|
29
35
|
|
|
30
36
|
|
|
31
37
|
def get_default_config_dir(config_dir: Path | str | None = None) -> Path:
|
|
32
|
-
"""Return the config directory. If config_dir is given, use it. Otherwise ~/.crow."""
|
|
38
|
+
"""Return the config directory. If config_dir is given, use it. Otherwise ~/.agents/crow."""
|
|
33
39
|
if config_dir is None:
|
|
34
|
-
return
|
|
40
|
+
return DEFAULT_CONFIG_DIR
|
|
35
41
|
return Path(config_dir).resolve()
|
|
36
42
|
|
|
37
43
|
|
|
@@ -56,14 +62,29 @@ def _write_defaults_if_missing(config_dir: Path) -> None:
|
|
|
56
62
|
target.write_text(content)
|
|
57
63
|
|
|
58
64
|
|
|
59
|
-
def resolve_env_vars(value: Any) -> Any:
|
|
60
|
-
"""Recursively replace ${VAR} with environment variable values.
|
|
65
|
+
def resolve_env_vars(value: Any, missing: set[str] | None = None) -> Any:
|
|
66
|
+
"""Recursively replace ${VAR} with environment variable values.
|
|
67
|
+
|
|
68
|
+
Unset variables expand to "" (parity with the Rust CLI), but their names
|
|
69
|
+
are collected in `missing` so the caller can warn instead of failing later
|
|
70
|
+
with an opaque provider error.
|
|
71
|
+
"""
|
|
61
72
|
if isinstance(value, str):
|
|
62
|
-
|
|
73
|
+
|
|
74
|
+
def _sub(m: re.Match) -> str:
|
|
75
|
+
name = m.group(1)
|
|
76
|
+
val = os.getenv(name)
|
|
77
|
+
if val is None:
|
|
78
|
+
if missing is not None:
|
|
79
|
+
missing.add(name)
|
|
80
|
+
return ""
|
|
81
|
+
return val
|
|
82
|
+
|
|
83
|
+
return ENV_PATTERN.sub(_sub, value)
|
|
63
84
|
elif isinstance(value, dict):
|
|
64
|
-
return {k: resolve_env_vars(v) for k, v in value.items()}
|
|
85
|
+
return {k: resolve_env_vars(v, missing) for k, v in value.items()}
|
|
65
86
|
elif isinstance(value, list):
|
|
66
|
-
return [resolve_env_vars(v) for v in value]
|
|
87
|
+
return [resolve_env_vars(v, missing) for v in value]
|
|
67
88
|
return value
|
|
68
89
|
|
|
69
90
|
|
|
@@ -79,6 +100,12 @@ class LLModel:
|
|
|
79
100
|
name: str
|
|
80
101
|
provider_name: str
|
|
81
102
|
model_id: str
|
|
103
|
+
# None = unknown → assume capable of everything (permissive default).
|
|
104
|
+
# A set restricts: e.g. set() is text-only, {"vision"} handles images.
|
|
105
|
+
capabilities: set[str] | None = None
|
|
106
|
+
# Ordered fallback chain (model NAMES from this config) used when this
|
|
107
|
+
# model cannot handle the modalities present in the conversation.
|
|
108
|
+
fallbacks: list[str] = field(default_factory=list)
|
|
82
109
|
|
|
83
110
|
|
|
84
111
|
@dataclass
|
|
@@ -92,6 +119,7 @@ class Config:
|
|
|
92
119
|
config_dir: Path
|
|
93
120
|
llm: LLMConfig = field(default_factory=LLMConfig)
|
|
94
121
|
memory_path: str = ""
|
|
122
|
+
skills_dir: str = str(SKILLS_DIR)
|
|
95
123
|
mcp_servers: dict[str, Any] = field(default_factory=dict)
|
|
96
124
|
max_retries_per_step: int = 3
|
|
97
125
|
MAX_COMPACT_TOKENS: int = 190000
|
|
@@ -128,21 +156,29 @@ class Config:
|
|
|
128
156
|
if env_file.exists():
|
|
129
157
|
load_dotenv(env_file)
|
|
130
158
|
|
|
159
|
+
|
|
160
|
+
|
|
131
161
|
# If no config.yaml, return a bare Config
|
|
132
162
|
config_file = target_dir / "config.yaml"
|
|
133
163
|
if not config_file.exists():
|
|
134
164
|
_logger.info("No config.yaml found, returning bare Config")
|
|
135
165
|
return cls(
|
|
136
166
|
config_dir=target_dir,
|
|
137
|
-
memory_path=str(
|
|
167
|
+
memory_path=str(DEFAULT_CONFIG_DIR / "crow.db"),
|
|
138
168
|
)
|
|
139
169
|
|
|
140
170
|
with open(config_file) as f:
|
|
141
171
|
raw = yaml.safe_load(f) or {}
|
|
142
172
|
|
|
143
173
|
_logger.info("RAW config.yaml mcpServers: %s", raw.get("mcpServers", {}))
|
|
144
|
-
|
|
174
|
+
missing_vars: set[str] = set()
|
|
175
|
+
parsed = resolve_env_vars(raw, missing_vars)
|
|
145
176
|
_logger.info("PARSED config.yaml mcpServers after resolve_env_vars: %s", parsed.get("mcpServers", {}))
|
|
177
|
+
if missing_vars:
|
|
178
|
+
_logger.warning(
|
|
179
|
+
"config.yaml references unset environment variables (expanded to empty): %s",
|
|
180
|
+
", ".join(f"${{{v}}}" for v in sorted(missing_vars)),
|
|
181
|
+
)
|
|
146
182
|
|
|
147
183
|
# Parse providers
|
|
148
184
|
llm = LLMConfig()
|
|
@@ -155,14 +191,19 @@ class Config:
|
|
|
155
191
|
|
|
156
192
|
# Parse models
|
|
157
193
|
for name, data in parsed.get("models", {}).items():
|
|
194
|
+
raw_caps = data.get("capabilities")
|
|
195
|
+
capabilities = set(raw_caps) if raw_caps is not None else None
|
|
158
196
|
llm.models[name] = LLModel(
|
|
159
197
|
name=name,
|
|
160
198
|
provider_name=data.get("provider", ""),
|
|
161
199
|
model_id=data.get("model", ""),
|
|
200
|
+
capabilities=capabilities,
|
|
201
|
+
fallbacks=list(data.get("fallbacks") or []),
|
|
162
202
|
)
|
|
163
203
|
|
|
164
204
|
# Parse overrides
|
|
165
|
-
memory_path = os.path.expanduser(parsed.get("memory_path") or "~/.crow/
|
|
205
|
+
memory_path = os.path.expanduser(parsed.get("memory_path") or "~/.agents/crow/crow.db")
|
|
206
|
+
skills_dir = os.path.expanduser(parsed.get("skills_dir") or str(SKILLS_DIR))
|
|
166
207
|
overrides = {}
|
|
167
208
|
for key, typ in (
|
|
168
209
|
("max_retries_per_step", int),
|
|
@@ -179,12 +220,13 @@ class Config:
|
|
|
179
220
|
_logger.info("FINAL mcp_servers stored in Config: %s", mcp_servers)
|
|
180
221
|
system_prompt_path = None
|
|
181
222
|
if "system_prompt_path" in parsed:
|
|
182
|
-
system_prompt_path = Path(parsed["system_prompt_path"])
|
|
223
|
+
system_prompt_path = Path(os.path.expanduser(parsed["system_prompt_path"]))
|
|
183
224
|
|
|
184
225
|
return cls(
|
|
185
226
|
config_dir=target_dir,
|
|
186
227
|
llm=llm,
|
|
187
228
|
memory_path=memory_path,
|
|
229
|
+
skills_dir=skills_dir,
|
|
188
230
|
mcp_servers=mcp_servers,
|
|
189
231
|
system_prompt_path=system_prompt_path,
|
|
190
232
|
**overrides,
|