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.
Files changed (89) hide show
  1. aichs-0.1.0.data/data/share/aichs/docs/compact.md +63 -0
  2. aichs-0.1.0.data/data/share/aichs/docs/configuration.md +17 -0
  3. aichs-0.1.0.data/data/share/aichs/docs/custom-models.md +294 -0
  4. aichs-0.1.0.data/data/share/aichs/docs/extensions.md +458 -0
  5. aichs-0.1.0.data/data/share/aichs/docs/skills.md +45 -0
  6. aichs-0.1.0.dist-info/METADATA +122 -0
  7. aichs-0.1.0.dist-info/RECORD +89 -0
  8. aichs-0.1.0.dist-info/WHEEL +5 -0
  9. aichs-0.1.0.dist-info/entry_points.txt +2 -0
  10. aichs-0.1.0.dist-info/licenses/LICENSE +21 -0
  11. aichs-0.1.0.dist-info/top_level.txt +7 -0
  12. app.py +1 -0
  13. assets/__init__.py +1 -0
  14. assets/avatars/agent.svg +15 -0
  15. assets/avatars/crew_archivist.svg +8 -0
  16. assets/avatars/crew_scout.svg +7 -0
  17. assets/avatars/human.svg +8 -0
  18. assets/checkmark.svg +3 -0
  19. assets/icon.svg +44 -0
  20. assets/logo.png +0 -0
  21. assets/logo.svg +62 -0
  22. assets/png/icon-128.png +0 -0
  23. assets/png/icon-16.png +0 -0
  24. assets/png/icon-256.png +0 -0
  25. assets/png/icon-32.png +0 -0
  26. assets/png/icon-512.png +0 -0
  27. assets/png/icon-64.png +0 -0
  28. config.py +28 -0
  29. main.py +63 -0
  30. services/__init__.py +0 -0
  31. services/auto_title.py +137 -0
  32. services/chat.py +752 -0
  33. services/chat_drag.py +143 -0
  34. services/compaction.py +356 -0
  35. services/content.py +343 -0
  36. services/context_budget.py +108 -0
  37. services/continuation.py +116 -0
  38. services/crew.py +249 -0
  39. services/crew_context.py +44 -0
  40. services/diff_html.py +100 -0
  41. services/export.py +126 -0
  42. services/file_ref_clipboard.py +71 -0
  43. services/git_diff.py +127 -0
  44. services/git_status.py +71 -0
  45. services/highlight.py +42 -0
  46. services/model_registry.py +361 -0
  47. services/palette.py +155 -0
  48. services/processes.py +420 -0
  49. services/shell_tool.py +11 -0
  50. services/skills.py +73 -0
  51. services/slash_commands.py +113 -0
  52. services/subprocess_utils.py +32 -0
  53. services/terminal_refs.py +89 -0
  54. services/tool_policy.py +201 -0
  55. services/tool_registry.py +766 -0
  56. services/tools.py +969 -0
  57. services/usage.py +112 -0
  58. services/user_terminal.py +118 -0
  59. services/workspace.py +120 -0
  60. storage/__init__.py +0 -0
  61. storage/repository.py +157 -0
  62. storage/settings.py +67 -0
  63. ui/__init__.py +0 -0
  64. ui/avatars.py +123 -0
  65. ui/main_window.py +178 -0
  66. ui/theme.py +686 -0
  67. ui/widgets/__init__.py +0 -0
  68. ui/widgets/bubble.py +583 -0
  69. ui/widgets/chat_panel.py +2823 -0
  70. ui/widgets/code_card.py +138 -0
  71. ui/widgets/command_palette.py +161 -0
  72. ui/widgets/context_breakdown.py +123 -0
  73. ui/widgets/context_ring.py +69 -0
  74. ui/widgets/conversation_panel.py +470 -0
  75. ui/widgets/docs_dialog.py +170 -0
  76. ui/widgets/extension_contributions.py +151 -0
  77. ui/widgets/extension_panel_dialog.py +292 -0
  78. ui/widgets/extensions_dialog.py +809 -0
  79. ui/widgets/file_mention_picker.py +112 -0
  80. ui/widgets/file_viewer.py +270 -0
  81. ui/widgets/git_changes_list.py +74 -0
  82. ui/widgets/git_panel.py +134 -0
  83. ui/widgets/left_panel.py +404 -0
  84. ui/widgets/message_input.py +681 -0
  85. ui/widgets/settings_dialog.py +1438 -0
  86. ui/widgets/skill_picker.py +119 -0
  87. ui/widgets/terminal_card.py +211 -0
  88. ui/widgets/tool_approval_dialog.py +194 -0
  89. 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
+ ```