aichs 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aichs-0.1.0.data/data/share/aichs/docs/compact.md +63 -0
- aichs-0.1.0.data/data/share/aichs/docs/configuration.md +17 -0
- aichs-0.1.0.data/data/share/aichs/docs/custom-models.md +294 -0
- aichs-0.1.0.data/data/share/aichs/docs/extensions.md +458 -0
- aichs-0.1.0.data/data/share/aichs/docs/skills.md +45 -0
- aichs-0.1.0.dist-info/METADATA +122 -0
- aichs-0.1.0.dist-info/RECORD +89 -0
- aichs-0.1.0.dist-info/WHEEL +5 -0
- aichs-0.1.0.dist-info/entry_points.txt +2 -0
- aichs-0.1.0.dist-info/licenses/LICENSE +21 -0
- aichs-0.1.0.dist-info/top_level.txt +7 -0
- app.py +1 -0
- assets/__init__.py +1 -0
- assets/avatars/agent.svg +15 -0
- assets/avatars/crew_archivist.svg +8 -0
- assets/avatars/crew_scout.svg +7 -0
- assets/avatars/human.svg +8 -0
- assets/checkmark.svg +3 -0
- assets/icon.svg +44 -0
- assets/logo.png +0 -0
- assets/logo.svg +62 -0
- assets/png/icon-128.png +0 -0
- assets/png/icon-16.png +0 -0
- assets/png/icon-256.png +0 -0
- assets/png/icon-32.png +0 -0
- assets/png/icon-512.png +0 -0
- assets/png/icon-64.png +0 -0
- config.py +28 -0
- main.py +63 -0
- services/__init__.py +0 -0
- services/auto_title.py +137 -0
- services/chat.py +752 -0
- services/chat_drag.py +143 -0
- services/compaction.py +356 -0
- services/content.py +343 -0
- services/context_budget.py +108 -0
- services/continuation.py +116 -0
- services/crew.py +249 -0
- services/crew_context.py +44 -0
- services/diff_html.py +100 -0
- services/export.py +126 -0
- services/file_ref_clipboard.py +71 -0
- services/git_diff.py +127 -0
- services/git_status.py +71 -0
- services/highlight.py +42 -0
- services/model_registry.py +361 -0
- services/palette.py +155 -0
- services/processes.py +420 -0
- services/shell_tool.py +11 -0
- services/skills.py +73 -0
- services/slash_commands.py +113 -0
- services/subprocess_utils.py +32 -0
- services/terminal_refs.py +89 -0
- services/tool_policy.py +201 -0
- services/tool_registry.py +766 -0
- services/tools.py +969 -0
- services/usage.py +112 -0
- services/user_terminal.py +118 -0
- services/workspace.py +120 -0
- storage/__init__.py +0 -0
- storage/repository.py +157 -0
- storage/settings.py +67 -0
- ui/__init__.py +0 -0
- ui/avatars.py +123 -0
- ui/main_window.py +178 -0
- ui/theme.py +686 -0
- ui/widgets/__init__.py +0 -0
- ui/widgets/bubble.py +583 -0
- ui/widgets/chat_panel.py +2823 -0
- ui/widgets/code_card.py +138 -0
- ui/widgets/command_palette.py +161 -0
- ui/widgets/context_breakdown.py +123 -0
- ui/widgets/context_ring.py +69 -0
- ui/widgets/conversation_panel.py +470 -0
- ui/widgets/docs_dialog.py +170 -0
- ui/widgets/extension_contributions.py +151 -0
- ui/widgets/extension_panel_dialog.py +292 -0
- ui/widgets/extensions_dialog.py +809 -0
- ui/widgets/file_mention_picker.py +112 -0
- ui/widgets/file_viewer.py +270 -0
- ui/widgets/git_changes_list.py +74 -0
- ui/widgets/git_panel.py +134 -0
- ui/widgets/left_panel.py +404 -0
- ui/widgets/message_input.py +681 -0
- ui/widgets/settings_dialog.py +1438 -0
- ui/widgets/skill_picker.py +119 -0
- ui/widgets/terminal_card.py +211 -0
- ui/widgets/tool_approval_dialog.py +194 -0
- ui/win_caption.py +82 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Compaction and Decision Memory
|
|
2
|
+
|
|
3
|
+
Compaction is only context management. When a conversation grows past the
|
|
4
|
+
context budget, compaction cuts off an older prefix of the chat, asks the model
|
|
5
|
+
for a concise continuation summary, and keeps that summary plus recent verbatim
|
|
6
|
+
messages.
|
|
7
|
+
|
|
8
|
+
The compacted summary is optimized for the next model call: current goal,
|
|
9
|
+
important constraints, relevant files, decisions, tool results, blockers, and
|
|
10
|
+
the next step. It is not a durable archive.
|
|
11
|
+
|
|
12
|
+
## Raw History
|
|
13
|
+
|
|
14
|
+
Compaction replaces the saved conversation messages with the compacted history.
|
|
15
|
+
The old raw prefix is not kept in the active conversation JSON.
|
|
16
|
+
|
|
17
|
+
Do not automatically save raw compaction archives by default. That would make
|
|
18
|
+
compaction a hidden retention mechanism instead of a straightforward context
|
|
19
|
+
cleanup operation.
|
|
20
|
+
|
|
21
|
+
## Decision Memory
|
|
22
|
+
|
|
23
|
+
Durable project decisions should live outside core compaction as an opt-in
|
|
24
|
+
extension.
|
|
25
|
+
|
|
26
|
+
The decision-memory extension exposes narrow tools:
|
|
27
|
+
|
|
28
|
+
- `remember_decision(topic, decision)` saves one short durable decision
|
|
29
|
+
- `recall_decisions(topic)` returns decisions for one topic
|
|
30
|
+
- `list_decision_topics()` lists known decision keys
|
|
31
|
+
|
|
32
|
+
Storage is intentionally tiny:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"authentication": [
|
|
37
|
+
"Use JWT for API authentication."
|
|
38
|
+
],
|
|
39
|
+
"compaction": [
|
|
40
|
+
"Decision memory should be an opt-in extension, not part of core compaction."
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The extension may add a small context snippet to the main prompt:
|
|
46
|
+
|
|
47
|
+
- recall decisions before revisiting a durable architecture, product, strategy,
|
|
48
|
+
or implementation topic
|
|
49
|
+
- remember only strong user-confirmed decisions
|
|
50
|
+
- avoid transient plans, tool output, summaries, guesses, secrets, and facts
|
|
51
|
+
easily rediscovered from the repo
|
|
52
|
+
- optionally show known topic keys, but do not inject all decision contents into
|
|
53
|
+
every prompt
|
|
54
|
+
|
|
55
|
+
## Why Extension-Only
|
|
56
|
+
|
|
57
|
+
Keeping decision memory as an extension makes the behavior explicit,
|
|
58
|
+
workspace-disableable, and easy to inspect. It also avoids coupling memory
|
|
59
|
+
policy to compaction, cron jobs, or raw transcript retention.
|
|
60
|
+
|
|
61
|
+
Core infrastructure should only provide the extension API needed for this:
|
|
62
|
+
extension tools and context providers receive `ctx.extension_id` and
|
|
63
|
+
`ctx.storage`, so they can share project-scoped state without hand-rolled paths.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Settings are stored in `~/.aichs/settings.json` (written by **Settings** in the app).
|
|
4
|
+
|
|
5
|
+
| Key | Description |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `anthropic_api_key` | Fallback if `ANTHROPIC_API_KEY` is unset |
|
|
8
|
+
| `openai_api_key` | Fallback if `OPENAI_API_KEY` is unset |
|
|
9
|
+
| `provider_api_keys` | Per-provider keys for built-in and custom providers |
|
|
10
|
+
| `system_prompt` | Overrides the default system prompt |
|
|
11
|
+
| `default_models` | Default model per provider |
|
|
12
|
+
| `theme` | `"dark"`, `"modern"`, or `"light"` |
|
|
13
|
+
| `font_size` | Chat font size (pt) |
|
|
14
|
+
| `compaction.reserve_tokens` | Optional. Tokens held back for the next reply before auto-compaction (omit to scale from each model's context window) |
|
|
15
|
+
| `compaction.keep_recent_tokens` | Optional. Recent message tokens to keep verbatim when compacting (omit to scale automatically) |
|
|
16
|
+
|
|
17
|
+
API keys can also be set via environment variables or **Settings → Models** before launch.
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Custom models
|
|
2
|
+
|
|
3
|
+
aichs ships with Claude and OpenAI models built in. You can add any provider that exposes an **OpenAI-compatible API** by creating `~/.aichs/models.json`.
|
|
4
|
+
|
|
5
|
+
Custom providers appear automatically in the provider dropdown — no code changes needed.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## File format
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"providers": {
|
|
14
|
+
"<provider-id>": {
|
|
15
|
+
"api": "openai-compatible",
|
|
16
|
+
"baseUrl": "https://...",
|
|
17
|
+
"apiKey": "ENV_VAR_NAME | !shell-command | literal-key",
|
|
18
|
+
"models": [
|
|
19
|
+
{ "id": "model-id", "name": "Display Name" }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Field | Required | Description |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `api` | yes (new providers) | Always `"openai-compatible"` for now |
|
|
29
|
+
| `baseUrl` | yes (new providers) | API base URL |
|
|
30
|
+
| `apiKey` | yes | Key resolution — see below |
|
|
31
|
+
| `models` | yes (new providers) | List of models to expose |
|
|
32
|
+
| `contextWindow` | no | Context size in **tokens** for compaction and the usage ring (defaults: Claude 180k, OpenAI-compatible 100k) |
|
|
33
|
+
|
|
34
|
+
### API key formats
|
|
35
|
+
|
|
36
|
+
| Value | Resolved as |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `"GEMINI_API_KEY"` | Read env var `$GEMINI_API_KEY` |
|
|
39
|
+
| `"!op read op://vault/gemini/key"` | Run shell command, use stdout |
|
|
40
|
+
| `"AIza..."` | Used literally (not recommended — use an env var) |
|
|
41
|
+
|
|
42
|
+
### Overriding a built-in provider
|
|
43
|
+
|
|
44
|
+
Omit `api` and `models` to only override `baseUrl` or `apiKey` on `claude` / `openai`:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"providers": {
|
|
49
|
+
"openai": {
|
|
50
|
+
"baseUrl": "https://my-corporate-proxy.example.com/v1"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Examples
|
|
59
|
+
|
|
60
|
+
### Google Gemini
|
|
61
|
+
|
|
62
|
+
Google exposes an OpenAI-compatible endpoint for all Gemini models. See [current model IDs](https://ai.google.dev/gemini-api/docs/models) — Gemini 2.0 is deprecated; prefer 3.x.
|
|
63
|
+
|
|
64
|
+
Get an API key at [aistudio.google.com](https://aistudio.google.com).
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"providers": {
|
|
69
|
+
"google": {
|
|
70
|
+
"api": "openai-compatible",
|
|
71
|
+
"baseUrl": "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
72
|
+
"apiKey": "GEMINI_API_KEY",
|
|
73
|
+
"models": [
|
|
74
|
+
{ "id": "gemini-3.5-flash", "name": "Gemini 3.5 Flash" },
|
|
75
|
+
{ "id": "gemini-3.1-pro-preview", "name": "Gemini 3.1 Pro" },
|
|
76
|
+
{ "id": "gemini-3-flash-preview", "name": "Gemini 3 Flash" },
|
|
77
|
+
{ "id": "gemini-3.1-flash-lite", "name": "Gemini 3.1 Flash Lite" }
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
export GEMINI_API_KEY=AIza...
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Ollama (local models)
|
|
91
|
+
|
|
92
|
+
[Ollama](https://ollama.com) runs models on your machine with no API key or internet connection required.
|
|
93
|
+
|
|
94
|
+
**1. Install Ollama**
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# macOS
|
|
98
|
+
brew install ollama
|
|
99
|
+
|
|
100
|
+
# Linux
|
|
101
|
+
curl -fsSL https://ollama.com/install.sh | sh
|
|
102
|
+
|
|
103
|
+
# Windows — download the installer from https://ollama.com/download
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**2. Pull models**
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
ollama pull llama3.1:8b # general purpose, fast
|
|
110
|
+
ollama pull qwen2.5-coder:7b # coding-focused, recommended
|
|
111
|
+
ollama pull qwen2.5-coder:32b # better quality, needs ~20 GB RAM
|
|
112
|
+
ollama pull deepseek-coder-v2 # strong at code, ~8 GB RAM
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Browse all available models at [ollama.com/library](https://ollama.com/library).
|
|
116
|
+
|
|
117
|
+
**3. Start the server** (runs automatically on macOS after install; on Linux run manually)
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
ollama serve
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Ollama listens on `http://localhost:11434` by default.
|
|
124
|
+
|
|
125
|
+
**4. Add to `~/.aichs/models.json`**
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"providers": {
|
|
130
|
+
"ollama": {
|
|
131
|
+
"api": "openai-compatible",
|
|
132
|
+
"baseUrl": "http://localhost:11434/v1",
|
|
133
|
+
"apiKey": "ollama",
|
|
134
|
+
"contextWindow": 32768,
|
|
135
|
+
"models": [
|
|
136
|
+
{ "id": "llama3.1:8b", "name": "Llama 3.1 8B" },
|
|
137
|
+
{ "id": "llama3.1:70b", "name": "Llama 3.1 70B" },
|
|
138
|
+
{ "id": "qwen2.5-coder:7b", "name": "Qwen 2.5 Coder 7B" },
|
|
139
|
+
{ "id": "qwen2.5-coder:32b", "name": "Qwen 2.5 Coder 32B" },
|
|
140
|
+
{ "id": "mistral:7b", "name": "Mistral 7B" },
|
|
141
|
+
{ "id": "deepseek-coder-v2", "name": "DeepSeek Coder V2" }
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
No API key needed — `"ollama"` is a placeholder the server ignores.
|
|
149
|
+
|
|
150
|
+
Set `contextWindow` to the size you configured in Ollama (or in **Settings → Models → Edit provider**). Without it, aichs assumes 100k like cloud OpenAI models.
|
|
151
|
+
|
|
152
|
+
> **Tip:** Only list models you have already pulled. Selecting an unpulled model will return an error from the Ollama server.
|
|
153
|
+
|
|
154
|
+
**Verify it works**
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
curl http://localhost:11434/v1/models
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Should return a JSON list of your pulled models.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### DeepSeek
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"providers": {
|
|
169
|
+
"deepseek": {
|
|
170
|
+
"api": "openai-compatible",
|
|
171
|
+
"baseUrl": "https://api.deepseek.com/v1",
|
|
172
|
+
"apiKey": "DEEPSEEK_API_KEY",
|
|
173
|
+
"models": [
|
|
174
|
+
{ "id": "deepseek-chat", "name": "DeepSeek V3" },
|
|
175
|
+
{ "id": "deepseek-reasoner", "name": "DeepSeek R1" }
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
export DEEPSEEK_API_KEY=sk-...
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
### OpenRouter
|
|
189
|
+
|
|
190
|
+
[OpenRouter](https://openrouter.ai) proxies 100+ models under a single API key.
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"providers": {
|
|
195
|
+
"openrouter": {
|
|
196
|
+
"api": "openai-compatible",
|
|
197
|
+
"baseUrl": "https://openrouter.ai/api/v1",
|
|
198
|
+
"apiKey": "OPENROUTER_API_KEY",
|
|
199
|
+
"models": [
|
|
200
|
+
{ "id": "meta-llama/llama-3.3-70b-instruct", "name": "Llama 3.3 70B" },
|
|
201
|
+
{ "id": "mistralai/mistral-large", "name": "Mistral Large" },
|
|
202
|
+
{ "id": "qwen/qwen-2.5-coder-32b-instruct", "name": "Qwen 2.5 Coder 32B" },
|
|
203
|
+
{ "id": "google/gemini-3.1-pro-preview", "name": "Gemini 3.1 Pro (OR)" }
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
export OPENROUTER_API_KEY=sk-or-...
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### Proxy / corporate gateway
|
|
217
|
+
|
|
218
|
+
Route a built-in provider through your own endpoint without changing models:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"providers": {
|
|
223
|
+
"anthropic": {
|
|
224
|
+
"baseUrl": "https://gateway.corp.example.com/anthropic"
|
|
225
|
+
},
|
|
226
|
+
"openai": {
|
|
227
|
+
"baseUrl": "https://gateway.corp.example.com/openai"
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The existing model lists and API keys are preserved; only the base URL changes.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### API key from a password manager
|
|
238
|
+
|
|
239
|
+
Use `!command` to fetch keys at runtime instead of storing them in env vars:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"providers": {
|
|
244
|
+
"google": {
|
|
245
|
+
"api": "openai-compatible",
|
|
246
|
+
"baseUrl": "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
247
|
+
"apiKey": "!op read op://Personal/Gemini/credential",
|
|
248
|
+
"models": [
|
|
249
|
+
{ "id": "gemini-3.5-flash", "name": "Gemini 3.5 Flash" }
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Works with any CLI tool: `op` (1Password), `bw` (Bitwarden), `pass`, `security` (macOS Keychain), etc.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## Full example
|
|
261
|
+
|
|
262
|
+
A `~/.aichs/models.json` combining multiple providers:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"providers": {
|
|
267
|
+
"google": {
|
|
268
|
+
"api": "openai-compatible",
|
|
269
|
+
"baseUrl": "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
270
|
+
"apiKey": "GEMINI_API_KEY",
|
|
271
|
+
"models": [
|
|
272
|
+
{ "id": "gemini-3.5-flash", "name": "Gemini 3.5 Flash" },
|
|
273
|
+
{ "id": "gemini-3.1-pro-preview", "name": "Gemini 3.1 Pro" }
|
|
274
|
+
]
|
|
275
|
+
},
|
|
276
|
+
"ollama": {
|
|
277
|
+
"api": "openai-compatible",
|
|
278
|
+
"baseUrl": "http://localhost:11434/v1",
|
|
279
|
+
"apiKey": "ollama",
|
|
280
|
+
"models": [
|
|
281
|
+
{ "id": "qwen2.5-coder:32b", "name": "Qwen 2.5 Coder 32B" }
|
|
282
|
+
]
|
|
283
|
+
},
|
|
284
|
+
"deepseek": {
|
|
285
|
+
"api": "openai-compatible",
|
|
286
|
+
"baseUrl": "https://api.deepseek.com/v1",
|
|
287
|
+
"apiKey": "DEEPSEEK_API_KEY",
|
|
288
|
+
"models": [
|
|
289
|
+
{ "id": "deepseek-chat", "name": "DeepSeek V3" }
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|