oh-pi 0.1.21 → 0.1.23

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.
package/README.fr.md ADDED
@@ -0,0 +1,196 @@
1
+ <div align="center">
2
+
3
+ <img src="./logo.svg" width="180" alt="oh-pi logo"/>
4
+
5
+ # 🐜 oh-pi
6
+
7
+ **Une seule commande pour booster [pi-coding-agent](https://github.com/badlogic/pi-mono).**
8
+
9
+ Comme oh-my-zsh pour pi — mais avec une colonie de fourmis autonome.
10
+
11
+ [![npm](https://img.shields.io/npm/v/oh-pi)](https://www.npmjs.com/package/oh-pi)
12
+ [![license](https://img.shields.io/npm/l/oh-pi)](./LICENSE)
13
+ [![node](https://img.shields.io/node/v/oh-pi)](https://nodejs.org)
14
+
15
+ [English](./README.md) | [中文](./README.zh.md) | [Français](./README.fr.md)
16
+
17
+ ```bash
18
+ npx oh-pi
19
+ ```
20
+
21
+ </div>
22
+
23
+ ---
24
+
25
+ ## Pourquoi
26
+
27
+ pi-coding-agent est puissant dès l'installation. Mais configurer manuellement les fournisseurs, thèmes, extensions, compétences et modèles de prompts est fastidieux. oh-pi vous offre une TUI moderne qui fait tout en moins d'une minute — et intègre un **essaim de fourmis** qui transforme pi en système multi-agents.
28
+
29
+ ## Démarrage rapide
30
+
31
+ ```bash
32
+ npx oh-pi # tout configurer
33
+ pi # commencer à coder
34
+ ```
35
+
36
+ C'est tout. oh-pi détecte votre environnement, vous guide dans la configuration et génère `~/.pi/agent/` pour vous.
37
+
38
+ Vous avez déjà une config ? oh-pi la détecte et propose une **sauvegarde avant écrasement**.
39
+
40
+ ## Ce que vous obtenez
41
+
42
+ ```
43
+ ~/.pi/agent/
44
+ ├── auth.json Clés API (permissions 0600)
45
+ ├── settings.json Modèle, thème, niveau de réflexion
46
+ ├── keybindings.json Raccourcis Vim/Emacs (optionnel)
47
+ ├── AGENTS.md Directives IA par rôle
48
+ ├── extensions/ 4 extensions
49
+ │ ├── safe-guard Confirmation des commandes dangereuses + protection des chemins
50
+ │ ├── git-guard Checkpoints auto stash + alerte dépôt modifié
51
+ │ ├── auto-session Nommage de session depuis le premier message
52
+ │ └── ant-colony/ 🐜 Essaim multi-agents autonome
53
+ ├── prompts/ 10 modèles (/review /fix /commit /test ...)
54
+ ├── skills/ 4 compétences (debug, git, setup, colony)
55
+ └── themes/ 6 thèmes personnalisés
56
+ ```
57
+
58
+ ## Modes de configuration
59
+
60
+ | Mode | Étapes | Pour |
61
+ |------|--------|------|
62
+ | 🚀 **Rapide** | 3 | Choisir fournisseur → entrer la clé → terminé |
63
+ | 📦 **Préréglage** | 2 | Choisir un profil de rôle → entrer la clé |
64
+ | 🎛️ **Personnalisé** | 6 | Tout choisir soi-même |
65
+
66
+ ### Préréglages
67
+
68
+ | | Thème | Réflexion | Inclut |
69
+ |---|-------|-----------|--------|
70
+ | 🟢 Débutant | oh-pi Dark | moyen | Sécurité + bases git |
71
+ | 🔵 Développeur Pro | Catppuccin | élevé | Chaîne d'outils complète |
72
+ | 🟣 Chercheur en sécurité | Cyberpunk | élevé | Audit + pentesting |
73
+ | 🟠 Data & IA | Tokyo Night | moyen | MLOps + pipelines |
74
+ | 🔴 Minimal | Default | désactivé | Noyau uniquement |
75
+ | ⚫ Pleine puissance | oh-pi Dark | élevé | Tout + colonie de fourmis |
76
+
77
+ ### Fournisseurs
78
+
79
+ Anthropic · OpenAI · Google Gemini · Groq · OpenRouter · xAI · Mistral · [FOXNIO](https://www.foxnio.com) (fournisseur Claude d'intérêt public recommandé)
80
+
81
+ Détection automatique des clés API depuis les variables d'environnement.
82
+
83
+ ## 🐜 Colonie de fourmis
84
+
85
+ La fonctionnalité phare. Un essaim multi-agents inspiré de l'écologie réelle des fourmis.
86
+
87
+ ```
88
+ Vous : "Refactorer l'auth des sessions vers JWT"
89
+
90
+ oh-pi :
91
+ 🔍 Fourmis éclaireuses explorent le code (haiku — rapide, économique)
92
+ 📋 Pool de tâches généré à partir des découvertes
93
+ ⚒️ Fourmis ouvrières exécutent en parallèle (sonnet — capable)
94
+ 🛡️ Fourmis soldats révisent tous les changements (sonnet — minutieux)
95
+ ✅ Terminé — rapport de synthèse avec métriques
96
+ ```
97
+
98
+ ### Pourquoi des fourmis ?
99
+
100
+ Les vraies colonies de fourmis résolvent des problèmes complexes sans contrôle central. Chaque fourmi suit des règles simples, communique par **pistes de phéromones**, et la colonie s'auto-organise. oh-pi reproduit directement ce modèle :
101
+
102
+ | Vraies fourmis | oh-pi |
103
+ |----------------|-------|
104
+ | L'éclaireuse trouve la nourriture | L'éclaireuse scanne le code, identifie les cibles |
105
+ | Piste de phéromones | `.ant-colony/pheromone.jsonl` — découvertes partagées |
106
+ | L'ouvrière transporte la nourriture | L'ouvrière exécute la tâche sur les fichiers assignés |
107
+ | Le soldat garde le nid | Le soldat révise les changements, demande des corrections |
108
+ | Plus de nourriture → plus de fourmis | Plus de tâches → concurrence plus élevée (auto-adaptée) |
109
+ | Les phéromones s'évaporent | Demi-vie de 10 min — les infos obsolètes s'estompent |
110
+
111
+ ### Déclenchement automatique
112
+
113
+ Le LLM décide quand déployer la colonie. Vous n'avez pas à y penser :
114
+
115
+ - **≥3 fichiers** à modifier → colonie
116
+ - **Flux de travail parallèles** possibles → colonie
117
+ - **Un seul fichier** à modifier → exécution directe (pas de surcharge colonie)
118
+
119
+ Ou déclencher manuellement :
120
+
121
+ ```
122
+ /colony migrer tout le projet de CJS vers ESM
123
+ ```
124
+
125
+ ### Concurrence adaptative
126
+
127
+ La colonie trouve automatiquement le parallélisme optimal pour votre machine :
128
+
129
+ ```
130
+ Démarrage à froid → 1-2 fourmis (conservateur)
131
+ Exploration → +1 par vague, surveillance du débit
132
+ Débit en baisse → verrouiller l'optimal, stabiliser
133
+ CPU > 85% → réduire immédiatement
134
+ 429 rate limit → diviser la concurrence par 2 + backoff exponentiel (15s→30s→60s)
135
+ Tâches terminées → réduire au minimum
136
+ ```
137
+
138
+ ### Sécurité des fichiers
139
+
140
+ Une fourmi par fichier. Toujours. Les tâches en conflit sont automatiquement bloquées et reprennent à la libération des verrous.
141
+
142
+ ## Thèmes
143
+
144
+ | | |
145
+ |---|---|
146
+ | 🌙 **oh-pi Dark** | Cyan + violet, contraste élevé |
147
+ | 🌙 **Cyberpunk** | Magenta néon + cyan électrique |
148
+ | 🌙 **Nord** | Palette bleu arctique |
149
+ | 🌙 **Catppuccin Mocha** | Pastel sur fond sombre |
150
+ | 🌙 **Tokyo Night** | Crépuscule bleu + violet |
151
+ | 🌙 **Gruvbox Dark** | Tons rétro chaleureux |
152
+
153
+ ## Modèles de prompts
154
+
155
+ ```
156
+ /review Revue de code : bugs, sécurité, performance
157
+ /fix Corriger les erreurs avec un minimum de changements
158
+ /explain Expliquer le code, du simple au détaillé
159
+ /refactor Refactorer en préservant le comportement
160
+ /test Générer des tests
161
+ /commit Message Conventional Commit
162
+ /pr Description de Pull Request
163
+ /security Audit de sécurité OWASP
164
+ /optimize Optimisation des performances
165
+ /document Générer la documentation
166
+ ```
167
+
168
+ ## Modèles AGENTS.md
169
+
170
+ | Modèle | Focus |
171
+ |--------|-------|
172
+ | Développeur généraliste | Directives de codage universelles |
173
+ | Développeur Full-Stack | Frontend + backend + BDD |
174
+ | Chercheur en sécurité | Pentesting & audit |
175
+ | Ingénieur Data & IA | MLOps & pipelines |
176
+ | 🐜 Opérateur de colonie | Orchestration multi-agents |
177
+
178
+ ## Aussi un package Pi
179
+
180
+ Passez le configurateur, installez directement les ressources :
181
+
182
+ ```bash
183
+ pi install npm:oh-pi
184
+ ```
185
+
186
+ Ajoute tous les thèmes, prompts, compétences et extensions à votre configuration pi existante.
187
+
188
+ ## Prérequis
189
+
190
+ - Node.js ≥ 20
191
+ - Au moins une clé API LLM
192
+ - pi-coding-agent (installé automatiquement si absent)
193
+
194
+ ## Licence
195
+
196
+ MIT
package/README.md CHANGED
@@ -12,6 +12,8 @@ Like oh-my-zsh for pi — but with an autonomous ant colony.
12
12
  [![license](https://img.shields.io/npm/l/oh-pi)](./LICENSE)
13
13
  [![node](https://img.shields.io/node/v/oh-pi)](https://nodejs.org)
14
14
 
15
+ [English](./README.md) | [中文](./README.zh.md) | [Français](./README.fr.md)
16
+
15
17
  ```bash
16
18
  npx oh-pi
17
19
  ```
@@ -74,7 +76,7 @@ Already have a config? oh-pi detects it and offers **backup before overwriting**
74
76
 
75
77
  ### Providers
76
78
 
77
- Anthropic · OpenAI · Google Gemini · Groq · OpenRouter · xAI · Mistral
79
+ Anthropic · OpenAI · Google Gemini · Groq · OpenRouter · xAI · Mistral · [FOXNIO](https://www.foxnio.com) (recommended public-benefit Claude provider)
78
80
 
79
81
  Auto-detects API keys from environment variables.
80
82
 
package/README.zh.md ADDED
@@ -0,0 +1,196 @@
1
+ <div align="center">
2
+
3
+ <img src="./logo.svg" width="180" alt="oh-pi logo"/>
4
+
5
+ # 🐜 oh-pi
6
+
7
+ **一条命令,增强 [pi-coding-agent](https://github.com/badlogic/pi-mono)。**
8
+
9
+ 就像 oh-my-zsh 之于 pi —— 但带有自主蚁群系统。
10
+
11
+ [![npm](https://img.shields.io/npm/v/oh-pi)](https://www.npmjs.com/package/oh-pi)
12
+ [![license](https://img.shields.io/npm/l/oh-pi)](./LICENSE)
13
+ [![node](https://img.shields.io/node/v/oh-pi)](https://nodejs.org)
14
+
15
+ [English](./README.md) | [中文](./README.zh.md) | [Français](./README.fr.md)
16
+
17
+ ```bash
18
+ npx oh-pi
19
+ ```
20
+
21
+ </div>
22
+
23
+ ---
24
+
25
+ ## 为什么选择 oh-pi
26
+
27
+ pi-coding-agent 开箱即用已经很强大,但手动配置供应商、主题、扩展、技能和提示词模板非常繁琐。oh-pi 提供现代化 TUI,一分钟内搞定一切 —— 还附带**蚁群集群**,将 pi 变成多智能体系统。
28
+
29
+ ## 快速开始
30
+
31
+ ```bash
32
+ npx oh-pi # 配置一切
33
+ pi # 开始编码
34
+ ```
35
+
36
+ 就这么简单。oh-pi 检测你的环境,引导你完成设置,并为你生成 `~/.pi/agent/` 配置。
37
+
38
+ 已有配置?oh-pi 会检测到并提供**覆盖前备份**。
39
+
40
+ ## 你将获得
41
+
42
+ ```
43
+ ~/.pi/agent/
44
+ ├── auth.json API 密钥(0600 权限)
45
+ ├── settings.json 模型、主题、思考级别
46
+ ├── keybindings.json Vim/Emacs 快捷键(可选)
47
+ ├── AGENTS.md 角色专属 AI 指南
48
+ ├── extensions/ 4 个扩展
49
+ │ ├── safe-guard 危险命令确认 + 路径保护
50
+ │ ├── git-guard 自动 stash 检查点 + 脏仓库警告
51
+ │ ├── auto-session 根据首条消息自动命名会话
52
+ │ └── ant-colony/ 🐜 自主多智能体蚁群
53
+ ├── prompts/ 10 个模板(/review /fix /commit /test ...)
54
+ ├── skills/ 4 个技能(debug、git、setup、colony)
55
+ └── themes/ 6 个自定义主题
56
+ ```
57
+
58
+ ## 设置模式
59
+
60
+ | 模式 | 步骤 | 适用场景 |
61
+ |------|------|----------|
62
+ | 🚀 **快速** | 3 | 选供应商 → 输入密钥 → 完成 |
63
+ | 📦 **预设** | 2 | 选择角色配置 → 输入密钥 |
64
+ | 🎛️ **自定义** | 6 | 自己选择所有选项 |
65
+
66
+ ### 预设方案
67
+
68
+ | | 主题 | 思考级别 | 包含 |
69
+ |---|------|----------|------|
70
+ | 🟢 入门 | oh-pi Dark | 中等 | 安全 + git 基础 |
71
+ | 🔵 专业开发者 | Catppuccin | 高 | 完整工具链 |
72
+ | 🟣 安全研究员 | Cyberpunk | 高 | 审计 + 渗透测试 |
73
+ | 🟠 数据 & AI | Tokyo Night | 中等 | MLOps + 流水线 |
74
+ | 🔴 极简 | Default | 关闭 | 仅核心功能 |
75
+ | ⚫ 全功率 | oh-pi Dark | 高 | 所有功能 + 蚁群 |
76
+
77
+ ### 供应商
78
+
79
+ Anthropic · OpenAI · Google Gemini · Groq · OpenRouter · xAI · Mistral · [FOXNIO](https://www.foxnio.com)(推荐公益 Claude 供应商)
80
+
81
+ 自动从环境变量检测 API 密钥。
82
+
83
+ ## 🐜 蚁群系统
84
+
85
+ 核心特性。模拟真实蚂蚁生态的多智能体集群。
86
+
87
+ ```
88
+ 你:"把认证从 session 重构为 JWT"
89
+
90
+ oh-pi:
91
+ 🔍 侦察蚁探索代码库(haiku — 快速、低成本)
92
+ 📋 根据发现生成任务池
93
+ ⚒️ 工蚁并行执行任务(sonnet — 能力强)
94
+ 🛡️ 兵蚁审查所有变更(sonnet — 细致)
95
+ ✅ 完成 — 汇总报告及指标
96
+ ```
97
+
98
+ ### 为什么是蚂蚁?
99
+
100
+ 真实蚁群无需中央控制即可解决复杂问题。每只蚂蚁遵循简单规则,通过**信息素轨迹**通信,蚁群自组织运作。oh-pi 直接映射了这一模型:
101
+
102
+ | 真实蚂蚁 | oh-pi |
103
+ |----------|-------|
104
+ | 侦察蚁发现食物 | 侦察蚁扫描代码库,识别目标 |
105
+ | 信息素轨迹 | `.ant-colony/pheromone.jsonl` — 共享发现 |
106
+ | 工蚁搬运食物 | 工蚁在分配的文件上执行任务 |
107
+ | 兵蚁守卫巢穴 | 兵蚁审查变更,请求修复 |
108
+ | 食物越多 → 蚂蚁越多 | 任务越多 → 并发越高(自动适配) |
109
+ | 信息素蒸发 | 10 分钟半衰期 — 过时信息自动淡化 |
110
+
111
+ ### 自动触发
112
+
113
+ LLM 自行决定何时部署蚁群,你无需操心:
114
+
115
+ - **≥3 个文件**需要修改 → 蚁群
116
+ - 可**并行工作流** → 蚁群
117
+ - **单文件**修改 → 直接执行(无蚁群开销)
118
+
119
+ 或手动触发:
120
+
121
+ ```
122
+ /colony 将整个项目从 CJS 迁移到 ESM
123
+ ```
124
+
125
+ ### 自适应并发
126
+
127
+ 蚁群自动为你的机器找到最优并行度:
128
+
129
+ ```
130
+ 冷启动 → 1-2 只蚂蚁(保守)
131
+ 探索阶段 → 每波 +1,监控吞吐量
132
+ 吞吐量下降 → 锁定最优值,稳定运行
133
+ CPU > 85% → 立即减少
134
+ 429 限流 → 并发减半 + 指数退避(15s→30s→60s)
135
+ 任务完成 → 缩减至最小值
136
+ ```
137
+
138
+ ### 文件安全
139
+
140
+ 一个文件一只蚂蚁,始终如此。冲突任务自动阻塞,锁释放后恢复执行。
141
+
142
+ ## 主题
143
+
144
+ | | |
145
+ |---|---|
146
+ | 🌙 **oh-pi Dark** | 青色 + 紫色,高对比度 |
147
+ | 🌙 **Cyberpunk** | 霓虹洋红 + 电光青 |
148
+ | 🌙 **Nord** | 北极蓝色调 |
149
+ | 🌙 **Catppuccin Mocha** | 暗底柔和色 |
150
+ | 🌙 **Tokyo Night** | 蓝紫暮光 |
151
+ | 🌙 **Gruvbox Dark** | 暖色复古风 |
152
+
153
+ ## 提示词模板
154
+
155
+ ```
156
+ /review 代码审查:bug、安全、性能
157
+ /fix 最小改动修复错误
158
+ /explain 代码解释,由浅入深
159
+ /refactor 保持行为的重构
160
+ /test 生成测试
161
+ /commit Conventional Commit 提交信息
162
+ /pr Pull Request 描述
163
+ /security OWASP 安全审计
164
+ /optimize 性能优化
165
+ /document 生成文档
166
+ ```
167
+
168
+ ## AGENTS.md 模板
169
+
170
+ | 模板 | 侧重 |
171
+ |------|------|
172
+ | 通用开发者 | 通用编码指南 |
173
+ | 全栈开发者 | 前端 + 后端 + 数据库 |
174
+ | 安全研究员 | 渗透测试 & 审计 |
175
+ | 数据 & AI 工程师 | MLOps & 流水线 |
176
+ | 🐜 蚁群操作员 | 多智能体编排 |
177
+
178
+ ## 也是 Pi 包
179
+
180
+ 跳过配置器,直接安装资源:
181
+
182
+ ```bash
183
+ pi install npm:oh-pi
184
+ ```
185
+
186
+ 将所有主题、提示词、技能和扩展添加到你现有的 pi 配置中。
187
+
188
+ ## 环境要求
189
+
190
+ - Node.js ≥ 20
191
+ - 至少一个 LLM API 密钥
192
+ - pi-coding-agent(缺失时自动安装)
193
+
194
+ ## 许可证
195
+
196
+ MIT
package/dist/index.js CHANGED
@@ -63,7 +63,7 @@ async function customFlow(env) {
63
63
  p.cancel(t("cancelled"));
64
64
  process.exit(0);
65
65
  }
66
- let compactThreshold = 0.75;
66
+ let compactThreshold = 0.80;
67
67
  if (wantAdvanced) {
68
68
  const threshold = await p.text({
69
69
  message: t("advanced.compactThreshold"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-pi",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "One-click setup for pi-coding-agent. Like oh-my-zsh for pi.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,7 +37,7 @@ function formatTokens(n: number): string {
37
37
  function statusIcon(status: string): string {
38
38
  const icons: Record<string, string> = {
39
39
  scouting: "🔍", working: "⚒️", reviewing: "🛡️",
40
- done: "✅", failed: "❌",
40
+ done: "✅", failed: "❌", budget_exceeded: "💰",
41
41
  };
42
42
  return icons[status] || "🐜";
43
43
  }
@@ -79,11 +79,20 @@ For simple single-file tasks, work directly without the colony.`,
79
79
  parameters: Type.Object({
80
80
  goal: Type.String({ description: "What the colony should accomplish" }),
81
81
  maxAnts: Type.Optional(Type.Number({ description: "Max concurrent ants (default: auto-adapt)", minimum: 1, maximum: 8 })),
82
+ maxCost: Type.Optional(Type.Number({ description: "Max cost budget in USD (default: unlimited)", minimum: 0.01 })),
82
83
  }),
83
84
 
84
85
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
85
86
  const details: ColonyDetails = { state: null, phase: "initializing", log: [] };
86
87
 
88
+ // Resolve models: use current session model for worker/soldier, find a cheap model for scout
89
+ const currentModel = ctx.model?.id;
90
+ const modelOverrides: Record<string, string> = {};
91
+ if (currentModel) {
92
+ modelOverrides.worker = currentModel;
93
+ modelOverrides.soldier = currentModel;
94
+ }
95
+
87
96
  const emit = () => {
88
97
  const summary = details.state
89
98
  ? `${statusIcon(details.state.status)} Colony: ${details.phase}`
@@ -126,6 +135,8 @@ For simple single-file tasks, work directly without the colony.`,
126
135
  cwd: ctx.cwd,
127
136
  goal: params.goal,
128
137
  maxAnts: params.maxAnts,
138
+ maxCost: params.maxCost,
139
+ modelOverrides,
129
140
  signal: signal ?? undefined,
130
141
  callbacks,
131
142
  });
@@ -140,6 +151,7 @@ For simple single-file tasks, work directly without the colony.`,
140
151
  `**Goal:** ${state.goal}`,
141
152
  `**Status:** ${statusIcon(state.status)} ${state.status}`,
142
153
  `**Duration:** ${elapsed}`,
154
+ ...(state.maxCost != null ? [`**Budget:** ${formatCost(m.totalCost)} / ${formatCost(state.maxCost)}`] : []),
143
155
  ``,
144
156
  `### Metrics`,
145
157
  `- Tasks: ${m.tasksDone}/${m.tasksTotal} done, ${m.tasksFailed} failed`,
@@ -165,7 +177,7 @@ For simple single-file tasks, work directly without the colony.`,
165
177
  return {
166
178
  content: [{ type: "text", text: report }],
167
179
  details: { ...details },
168
- isError: state.status === "failed",
180
+ isError: state.status === "failed" || state.status === "budget_exceeded",
169
181
  };
170
182
  } catch (e) {
171
183
  return {
@@ -184,6 +196,7 @@ For simple single-file tasks, work directly without the colony.`,
184
196
  const goal = args.goal?.length > 60 ? args.goal.slice(0, 57) + "..." : args.goal;
185
197
  text += "\n " + theme.fg("dim", goal || "...");
186
198
  if (args.maxAnts) text += theme.fg("muted", ` (max: ${args.maxAnts})`);
199
+ if (args.maxCost) text += theme.fg("warning", ` (budget: $${args.maxCost})`);
187
200
  return new Text(text, 0, 0);
188
201
  },
189
202
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  import type {
16
16
  ColonyState, Task, Ant, AntCaste, ColonyMetrics,
17
- ConcurrencyConfig, TaskPriority,
17
+ ConcurrencyConfig, TaskPriority, ModelOverrides,
18
18
  } from "./types.js";
19
19
  import { DEFAULT_ANT_CONFIGS } from "./types.js";
20
20
  import { Nest } from "./nest.js";
@@ -33,6 +33,8 @@ export interface QueenOptions {
33
33
  cwd: string;
34
34
  goal: string;
35
35
  maxAnts?: number;
36
+ maxCost?: number;
37
+ modelOverrides?: ModelOverrides;
36
38
  signal?: AbortSignal;
37
39
  callbacks: QueenCallbacks;
38
40
  }
@@ -138,8 +140,11 @@ async function runAntWave(
138
140
  caste: AntCaste,
139
141
  signal: AbortSignal | undefined,
140
142
  callbacks: QueenCallbacks,
141
- ): Promise<void> {
142
- const config = DEFAULT_ANT_CONFIGS[caste];
143
+ modelOverrides?: ModelOverrides,
144
+ maxCost?: number,
145
+ ): Promise<"ok" | "budget_exceeded"> {
146
+ const config = { ...DEFAULT_ANT_CONFIGS[caste] };
147
+ if (modelOverrides?.[caste]) config.model = modelOverrides[caste];
143
148
 
144
149
  let backoffMs = 0; // 429 退避时间
145
150
 
@@ -191,6 +196,15 @@ async function runAntWave(
191
196
 
192
197
  // 调度循环:持续派蚂蚁直到没有待处理任务
193
198
  while (!signal?.aborted) {
199
+ // Budget check
200
+ if (maxCost != null) {
201
+ const currentCost = nest.getState().ants.reduce((s, a) => s + a.usage.cost, 0);
202
+ if (currentCost >= maxCost) {
203
+ callbacks.onPhase("working", `Budget exceeded ($${currentCost.toFixed(3)} >= $${maxCost.toFixed(2)}). Stopping.`);
204
+ return "budget_exceeded";
205
+ }
206
+ }
207
+
194
208
  const state = nest.getState();
195
209
  const pending = state.tasks.filter(t => t.status === "pending" && t.caste === caste);
196
210
  if (pending.length === 0) break;
@@ -254,6 +268,7 @@ async function runAntWave(
254
268
  backoffMs = 0;
255
269
  }
256
270
  }
271
+ return "ok";
257
272
  }
258
273
 
259
274
  /**
@@ -276,6 +291,8 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
276
291
  antsSpawned: 0, totalCost: 0, totalTokens: 0,
277
292
  startTime: Date.now(), throughputHistory: [],
278
293
  },
294
+ maxCost: opts.maxCost ?? null,
295
+ modelOverrides: opts.modelOverrides ?? {},
279
296
  createdAt: Date.now(),
280
297
  finishedAt: null,
281
298
  };
@@ -286,11 +303,22 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
286
303
 
287
304
  nest.init(initialState);
288
305
  const { signal, callbacks } = opts;
306
+ const mo = opts.modelOverrides;
307
+ const mc = opts.maxCost;
308
+
309
+ const budgetStop = (phase: string) => {
310
+ nest.updateState({ status: "budget_exceeded", finishedAt: Date.now() });
311
+ callbacks.onPhase("budget_exceeded" as any, phase);
312
+ const s = nest.getState();
313
+ callbacks.onComplete(s);
314
+ return s;
315
+ };
289
316
 
290
317
  try {
291
318
  // ═══ Phase 1: 侦察 ═══
292
319
  callbacks.onPhase("scouting", "Dispatching scout ants to explore codebase...");
293
- await runAntWave(nest, opts.cwd, "scout", signal, callbacks);
320
+ if (await runAntWave(nest, opts.cwd, "scout", signal, callbacks, mo, mc) === "budget_exceeded")
321
+ return budgetStop("Budget exceeded during scouting");
294
322
 
295
323
  // 检查侦察结果,如果没有产生工蚁任务,用侦察结果让女王自己拆
296
324
  const postScout = nest.getAllTasks();
@@ -306,7 +334,8 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
306
334
  // ═══ Phase 2: 工作 ═══
307
335
  nest.updateState({ status: "working" });
308
336
  callbacks.onPhase("working", `${workerTasks.length} tasks discovered. Dispatching worker ants...`);
309
- await runAntWave(nest, opts.cwd, "worker", signal, callbacks);
337
+ if (await runAntWave(nest, opts.cwd, "worker", signal, callbacks, mo, mc) === "budget_exceeded")
338
+ return budgetStop("Budget exceeded during working");
310
339
 
311
340
  // 处理工蚁产生的子任务(可能有多轮)
312
341
  let rounds = 0;
@@ -317,7 +346,8 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
317
346
  if (remaining.length === 0) break;
318
347
  rounds++;
319
348
  callbacks.onPhase("working", `Round ${rounds + 1}: ${remaining.length} sub-tasks from workers...`);
320
- await runAntWave(nest, opts.cwd, "worker", signal, callbacks);
349
+ if (await runAntWave(nest, opts.cwd, "worker", signal, callbacks, mo, mc) === "budget_exceeded")
350
+ return budgetStop("Budget exceeded during sub-tasks");
321
351
  }
322
352
 
323
353
  // ═══ Phase 3: 审查 ═══
@@ -327,7 +357,8 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
327
357
  callbacks.onPhase("reviewing", "Dispatching soldier ants to review changes...");
328
358
  const reviewTask = makeReviewTask(completedWorkerTasks);
329
359
  nest.writeTask(reviewTask);
330
- await runAntWave(nest, opts.cwd, "soldier", signal, callbacks);
360
+ if (await runAntWave(nest, opts.cwd, "soldier", signal, callbacks, mo, mc) === "budget_exceeded")
361
+ return budgetStop("Budget exceeded during review");
331
362
 
332
363
  // 兵蚁产生的修复任务
333
364
  const fixTasks = nest.getAllTasks().filter(t =>
@@ -336,7 +367,8 @@ export async function runColony(opts: QueenOptions): Promise<ColonyState> {
336
367
  if (fixTasks.length > 0) {
337
368
  nest.updateState({ status: "working" });
338
369
  callbacks.onPhase("working", `${fixTasks.length} fix tasks from review. Dispatching workers...`);
339
- await runAntWave(nest, opts.cwd, "worker", signal, callbacks);
370
+ if (await runAntWave(nest, opts.cwd, "worker", signal, callbacks, mo, mc) === "budget_exceeded")
371
+ return budgetStop("Budget exceeded during fixes");
340
372
  }
341
373
  }
342
374
 
@@ -19,6 +19,9 @@ export const DEFAULT_ANT_CONFIGS: Record<AntCaste, Omit<AntConfig, "systemPrompt
19
19
  soldier: { caste: "soldier", model: "claude-sonnet-4-5", tools: ["read", "bash", "grep", "find", "ls"], maxTurns: 8 },
20
20
  };
21
21
 
22
+ /** Per-caste model overrides from user config */
23
+ export type ModelOverrides = Partial<Record<AntCaste, string>>;
24
+
22
25
  // ═══ 任务 (Food Source) ═══
23
26
  export type TaskStatus = "pending" | "claimed" | "active" | "done" | "failed" | "blocked";
24
27
  export type TaskPriority = 1 | 2 | 3 | 4 | 5; // 1=highest
@@ -79,12 +82,14 @@ export interface Ant {
79
82
  export interface ColonyState {
80
83
  id: string;
81
84
  goal: string;
82
- status: "scouting" | "working" | "reviewing" | "done" | "failed";
85
+ status: "scouting" | "working" | "reviewing" | "done" | "failed" | "budget_exceeded";
83
86
  tasks: Task[];
84
87
  ants: Ant[];
85
88
  pheromones: Pheromone[];
86
89
  concurrency: ConcurrencyConfig;
87
90
  metrics: ColonyMetrics;
91
+ maxCost: number | null; // cost budget in USD, null = unlimited
92
+ modelOverrides: ModelOverrides;
88
93
  createdAt: number;
89
94
  finishedAt: number | null;
90
95
  }
@@ -75,13 +75,15 @@ export default function (pi: ExtensionAPI) {
75
75
 
76
76
  const thinking = pi.getThinkingLevel();
77
77
  const modelId = ctx.model?.id || "no-model";
78
- const right = theme.fg("dim", `${modelId} ${thinking}`);
78
+ const modelStr = theme.fg("accent", `◆ ${modelId}`);
79
79
 
80
80
  const sep = theme.fg("dim", " | ");
81
- const leftParts = [tokenStats, elapsed, cwdStr];
81
+ const leftParts = [modelStr, tokenStats, elapsed, cwdStr];
82
82
  if (branchStr) leftParts.push(branchStr);
83
83
  const left = leftParts.join(sep);
84
84
 
85
+ const right = theme.fg("dim", thinking);
86
+
85
87
  const leftW = visibleWidth(left);
86
88
  const rightW = visibleWidth(right);
87
89
  const gap = width - leftW - rightW;