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.
Files changed (65) hide show
  1. {crow_cli-0.1.30 → crow_cli-0.1.32}/PKG-INFO +18 -22
  2. {crow_cli-0.1.30 → crow_cli-0.1.32}/README.md +13 -16
  3. {crow_cli-0.1.30 → crow_cli-0.1.32}/pyproject.toml +5 -15
  4. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/compact.py +2 -2
  5. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/configure.py +55 -13
  6. crow_cli-0.1.32/src/crow_cli/agent/db.py +393 -0
  7. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/default/defaults.py +53 -13
  8. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/logger.py +3 -0
  9. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/main.py +93 -32
  10. crow_cli-0.1.32/src/crow_cli/agent/memory.py +320 -0
  11. crow_cli-0.1.32/src/crow_cli/agent/model_routing.py +137 -0
  12. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/react.py +97 -9
  13. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/session.py +73 -72
  14. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/cli/init_cmd.py +12 -27
  15. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/cli/main.py +191 -84
  16. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/client/main.py +101 -12
  17. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/analyze_payload_cache_invalidation.py +1 -1
  18. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/analyze_payload_deep.py +1 -1
  19. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/conftest.py +39 -36
  20. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/integration/test_auth_validation.py +2 -2
  21. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_compact.py +8 -8
  22. crow_cli-0.1.32/tests/unit/test_configure.py +44 -0
  23. crow_cli-0.1.32/tests/unit/test_db.py +100 -0
  24. crow_cli-0.1.32/tests/unit/test_model_override.py +39 -0
  25. crow_cli-0.1.32/tests/unit/test_model_routing.py +339 -0
  26. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_session.py +52 -4
  27. crow_cli-0.1.32/tests/unit/test_system_prompt_skills.py +26 -0
  28. crow_cli-0.1.32/tests/unit/test_usage_update.py +40 -0
  29. crow_cli-0.1.32/uv.lock +1678 -0
  30. crow_cli-0.1.30/src/crow_cli/agent/memory.py +0 -164
  31. crow_cli-0.1.30/uv.lock +0 -2930
  32. {crow_cli-0.1.30 → crow_cli-0.1.32}/.gitignore +0 -0
  33. {crow_cli-0.1.30 → crow_cli-0.1.32}/.python-version +0 -0
  34. {crow_cli-0.1.30 → crow_cli-0.1.32}/agent.json +0 -0
  35. {crow_cli-0.1.30 → crow_cli-0.1.32}/crow-cli.spec +0 -0
  36. {crow_cli-0.1.30 → crow_cli-0.1.32}/examples/mc_escher_loop.py +0 -0
  37. {crow_cli-0.1.30 → crow_cli-0.1.32}/examples/quick_test.py +0 -0
  38. {crow_cli-0.1.30 → crow_cli-0.1.32}/run_tests.sh +0 -0
  39. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/__init__.py +0 -0
  40. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/__init__.py +0 -0
  41. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/context.py +0 -0
  42. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/default/__init__.py +0 -0
  43. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/default/searxng_settings.py +0 -0
  44. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/hooks.py +0 -0
  45. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/llm.py +0 -0
  46. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/mcp_client.py +0 -0
  47. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/prompt.py +0 -0
  48. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/slash.py +0 -0
  49. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent/tools.py +0 -0
  50. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/agent_runner.py +0 -0
  51. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/cli/__init__.py +0 -0
  52. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/cli/install.py +0 -0
  53. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/client/__init__.py +0 -0
  54. {crow_cli-0.1.30 → crow_cli-0.1.32}/src/crow_cli/client/terminal.py +0 -0
  55. {crow_cli-0.1.30 → crow_cli-0.1.32}/test_send_request_error_handling.py +0 -0
  56. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/__init__.py +0 -0
  57. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/e2e/test_session_update_transmission.py +0 -0
  58. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/integration/test_agent_init.py +0 -0
  59. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_client_terminal.py +0 -0
  60. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_compact_last_messages_list_content.json +0 -0
  61. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_compact_last_messages_list_content.py +0 -0
  62. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_json_repair.py +0 -0
  63. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_request_logging.py +0 -0
  64. {crow_cli-0.1.30 → crow_cli-0.1.32}/tests/unit/test_stream_processing.py +0 -0
  65. {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.4
1
+ Metadata-Version: 2.5
2
2
  Name: crow-cli
3
- Version: 0.1.30
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>=0.9.0
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 dedicated memory service ([crow-memory](#crow-memory--persistence--memory-api)) built on [LanceDB](https://lancedb.github.io/lancedb/) with ColBERT and ColPali embeddings, so agents recall past conversations semantically and can delegate work to one another. Sessions get memorable coolname ids (like `taupe-squirrel-of-splendid-potency`) you can resume or read from any other agent.
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 crow-memory and SearXNG services
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 crow-memory + SearXNG
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
- ### crow-memory — persistence + memory API
117
+ ### Persistence — sqlite memory
119
118
 
120
- A standalone service — a LanceDB store with ColBERT (text) and ColPali (image) multivector embeddings — that the agent talks to over HTTP. It backs both session persistence and a semantic memory API, exposed to agents as three tools:
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. Today crow-memory runs as a Docker container the agent connects to; longer-term it moves toward an always-on daemon, in line with the ACP v2 direction.
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 `~/.crow/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.
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-memory`. Integration and end-to-end tiers are opt-in:
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 dedicated memory service ([crow-memory](#crow-memory--persistence--memory-api)) built on [LanceDB](https://lancedb.github.io/lancedb/) with ColBERT and ColPali embeddings, so agents recall past conversations semantically and can delegate work to one another. Sessions get memorable coolname ids (like `taupe-squirrel-of-splendid-potency`) you can resume or read from any other agent.
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 crow-memory and SearXNG services
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 crow-memory + SearXNG
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
- ### crow-memory — persistence + memory API
94
+ ### Persistence — sqlite memory
95
95
 
96
- A standalone service — a LanceDB store with ColBERT (text) and ColPali (image) multivector embeddings — that the agent talks to over HTTP. It backs both session persistence and a semantic memory API, exposed to agents as three tools:
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. Today crow-memory runs as a Docker container the agent connects to; longer-term it moves toward an always-on daemon, in line with the ACP v2 direction.
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 `~/.crow/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.
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-memory`. Integration and end-to-end tiers are opt-in:
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.30"
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.9.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
- CROW_DIR = Path.home() / ".crow"
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 CROW_DIR
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
- return ENV_PATTERN.sub(lambda m: os.getenv(m.group(1), ""), value)
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(Path.home() / ".crow" / "memory.lance"),
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
- parsed = resolve_env_vars(raw)
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/memory.lance")
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,