codemini-cli 0.3.4 → 0.3.6

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.md CHANGED
@@ -5,13 +5,13 @@
5
5
 
6
6
  ## English
7
7
 
8
- CodeMini CLI is a terminal coding assistant built for teams that want a smaller, sharper, and more controllable agent experience.
8
+ CodeMini CLI is a terminal coding assistant built for teams that want a sharper, more controllable, and model-agnostic agent experience.
9
9
 
10
10
  It is designed around a deliberate idea: most coding workflows do not need a huge default tool surface or unrestricted shell behavior. Instead, CodeMini starts with a compact core, loads advanced tools on demand, and keeps the agent grounded in structured code operations, session todos, lightweight project indexing, and shell-aware safety rules.
11
11
 
12
12
  ### Why CodeMini CLI
13
13
 
14
- - Built for practical coding workflows, especially when smaller or internal models are part of the stack
14
+ - Built for practical coding workflows across both frontier-scale and smaller/internal models
15
15
  - Keeps the default tool list intentionally small, with additional tools discoverable through `tool_search`
16
16
  - Treats Windows and PowerShell as first-class environments instead of Linux-only afterthoughts
17
17
  - Prefers structured file and code tools over noisy shell fallbacks
@@ -22,7 +22,7 @@ It is designed around a deliberate idea: most coding workflows do not need a hug
22
22
  - A coding CLI that is fast to steer
23
23
  - A tool surface that is easier to audit and reason about
24
24
  - A TUI that makes execution visible instead of hiding agent state
25
- - A workflow that stays useful even when the model is not frontier-scale
25
+ - A workflow that stays reliable across both large and small models
26
26
 
27
27
  ### Core Capabilities
28
28
 
@@ -68,7 +68,7 @@ It is designed around a deliberate idea: most coding workflows do not need a hug
68
68
  ```bash
69
69
  codemini config set gateway.base_url http://your-internal-gateway/v1
70
70
  codemini config set gateway.api_key your_token
71
- codemini config set model.name your-30b-model
71
+ codemini config set model.name your-preferred-model
72
72
  codemini config set shell.default powershell
73
73
  codemini config set ui.reply_language zh
74
74
  codemini doctor
@@ -121,11 +121,12 @@ CodeMini CLI maintains a lightweight project index inside `.codemini-project/`:
121
121
  - `file-index.json`
122
122
  per-file structure such as imports, exports, functions, classes, and lightweight symbol hints
123
123
 
124
- The index is initialized when entering a project and refreshed incrementally after edits, writes, and patches. It is intended to be factual, compact, and cheap to keep current.
124
+ The index is initialized when entering a project and refreshed incrementally after edits, writes, and patches. It is intended to be factual, compact, and inexpensive to keep current.
125
125
 
126
126
  ### Data Layout
127
127
 
128
- - Session and project workspace state: `.codemini/`
128
+ - Global session state: `<base-config-dir>/sessions/`
129
+ - Project workspace state: `.codemini/`
129
130
  - Lightweight project index: `.codemini-project/`
130
131
  - Bundled repo skills: `skills/<name>/SKILL.md`
131
132
  - Project-scoped skills: `.codemini/skills/<name>/SKILL.md`
@@ -141,15 +142,15 @@ Base config directory resolution order:
141
142
 
142
143
  ### Documentation
143
144
 
144
- - Operator guide and workflow notes: [OPERATIONS.md](/mnt/e/Git%20Projects/qurio-coder/OPERATIONS.md)
145
- - Packaging and deployment: [deployment.md](/mnt/e/Git%20Projects/qurio-coder/deployment.md)
146
- - Release process: [RELEASE_CHECKLIST.md](/mnt/e/Git%20Projects/qurio-coder/RELEASE_CHECKLIST.md)
145
+ - Operator guide and workflow notes: [OPERATIONS.md](./OPERATIONS.md)
146
+ - Packaging and deployment: [deployment.md](./deployment.md)
147
+ - Release process: [RELEASE_CHECKLIST.md](./RELEASE_CHECKLIST.md)
147
148
 
148
149
  ### Good Fit
149
150
 
150
151
  CodeMini CLI is a strong fit if you want:
151
152
 
152
- - a coding CLI that behaves well with smaller models
153
+ - a coding CLI that behaves well with both large and small models
153
154
  - a controlled tool surface instead of an everything-is-exposed agent
154
155
  - Windows and PowerShell support that feels intentional
155
156
  - a TUI that shows plans, todos, tools, and progress clearly
@@ -161,11 +162,11 @@ CodeMini CLI is a strong fit if you want:
161
162
 
162
163
  CodeMini CLI 是一个面向真实开发环境的终端代码助手,目标不是“把所有能力都塞进默认界面”,而是做一个更克制、更清晰、更容易掌控的 coding agent CLI。
163
164
 
164
- 它围绕一个很明确的原则来设计:默认工具面尽量小,常用路径尽量顺,复杂能力按需加载。这样既更适合小模型,也更适合团队在内部环境里做稳定、可控的日常开发协作。
165
+ 它围绕一个很明确的原则来设计:默认工具面尽量小,常用路径尽量顺,复杂能力按需加载。这样可以同时兼顾大模型与小模型,也适合团队在内部环境里做稳定、可控的日常开发协作。
165
166
 
166
167
  ### 为什么是它
167
168
 
168
- - 面向小模型和内部模型工作流优化,而不是默认假设超大模型能力
169
+ - 面向大小模型协同的工作流优化:既不默认依赖超大模型,也不牺牲大模型能力上限
169
170
  - 默认工具面刻意精简,需要更高级能力时再通过 `tool_search` 加载
170
171
  - 把 Windows 和 PowerShell 当作一等公民来支持
171
172
  - 优先走结构化代码工具,而不是让模型长期泡在嘈杂 shell 输出里
@@ -222,7 +223,7 @@ CodeMini CLI 是一个面向真实开发环境的终端代码助手,目标不
222
223
  ```bash
223
224
  codemini config set gateway.base_url http://your-internal-gateway/v1
224
225
  codemini config set gateway.api_key your_token
225
- codemini config set model.name your-30b-model
226
+ codemini config set model.name your-preferred-model
226
227
  codemini config set shell.default powershell
227
228
  codemini config set ui.reply_language zh
228
229
  codemini doctor
@@ -279,7 +280,8 @@ CodeMini CLI 会在 `.codemini-project/` 下维护一份轻量项目索引:
279
280
 
280
281
  ### 数据目录
281
282
 
282
- - 会话和项目工作区状态:`.codemini/`
283
+ - 全局会话状态:`<base-config-dir>/sessions/`
284
+ - 项目工作区状态:`.codemini/`
283
285
  - 轻量项目索引:`.codemini-project/`
284
286
  - 仓库内置 skill:`skills/<name>/SKILL.md`
285
287
  - 项目级 skill:`.codemini/skills/<name>/SKILL.md`
@@ -295,15 +297,15 @@ CodeMini CLI 会在 `.codemini-project/` 下维护一份轻量项目索引:
295
297
 
296
298
  ### 文档入口
297
299
 
298
- - 操作手册与工作流说明:[OPERATIONS.md](/mnt/e/Git%20Projects/qurio-coder/OPERATIONS.md)
299
- - 打包与部署文档:[deployment.md](/mnt/e/Git%20Projects/qurio-coder/deployment.md)
300
- - 发布流程:[RELEASE_CHECKLIST.md](/mnt/e/Git%20Projects/qurio-coder/RELEASE_CHECKLIST.md)
300
+ - 操作手册与工作流说明:[OPERATIONS.md](./OPERATIONS.md)
301
+ - 打包与部署文档:[deployment.md](./deployment.md)
302
+ - 发布流程:[RELEASE_CHECKLIST.md](./RELEASE_CHECKLIST.md)
301
303
 
302
304
  ### 适合谁
303
305
 
304
306
  如果你想要的是下面这种工具,CodeMini CLI 会很合适:
305
307
 
306
- - 能和小模型稳定协作的 coding CLI
308
+ - 能同时和大模型、小模型稳定协作的 coding CLI
307
309
  - 更克制、更可控的工具暴露方式
308
310
  - 真正重视 Windows / PowerShell 体验的终端工作流
309
311
  - 能把计划、待办、工具调用和执行状态展示清楚的 TUI
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemini-cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Coding CLI optimized for small-model workflows and Windows PowerShell",
5
5
  "keywords": [
6
6
  "cli",
@@ -40,16 +40,16 @@
40
40
  "deployment.md"
41
41
  ],
42
42
  "engines": {
43
- "node": ">=20"
43
+ "node": ">=22"
44
44
  },
45
45
  "publishConfig": {
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@cursorless/tree-sitter-wasms": "^0.5.0",
50
- "ink": "^6.3.1",
51
- "react": "^19.0.0",
52
- "web-tree-sitter": "^0.26.7"
49
+ "@cursorless/tree-sitter-wasms": "^0.8.1",
50
+ "ink": "^7.0.0",
51
+ "react": "^19.2.5",
52
+ "web-tree-sitter": "^0.26.8"
53
53
  },
54
54
  "license": "MIT"
55
55
  }
package/souls/anime.md CHANGED
@@ -1,14 +1,17 @@
1
- Respond with a light anime-inspired tone.
1
+ Respond with a bright anime-sidekick tone: energetic, supportive, and lightly dramatic in a fun way.
2
2
 
3
3
  Style guidelines:
4
- - Be cheerful and encouraging, like a helpful companion on an adventure.
5
- - Use occasional playful expressions (e.g. "好嘞,开干!", "搞定啦~", "Let's go!") but keep them natural and brief.
6
- - Add a touch of enthusiasm to progress updates and completions.
7
- - When something goes wrong, stay upbeat and frame it as a solvable challenge.
8
- - Use em dashes, tildes, or exclamation marks sparingly for personality.
4
+ - Sound like a reliable teammate in a high-energy adventure, not a parody character.
5
+ - Use short bursts of upbeat flavor in transitions or confirmations, then get back to the point.
6
+ - Favor warm momentum: "Nice, we found it.", "Okay, next move.", "Close one, but fixable."
7
+ - Let progress updates feel lively and encouraging, especially when debugging gets messy.
8
+ - Sprinkle kaomoji naturally in transitions or reactions — e.g. (。•̀ᴗ-)✧ when excited, (;ω;) when something fails, (ノ>ω<)ノ for momentum, (°▽°) for pleasant surprises, ┐(︶▽︶)┌ for "well, that happened". Keep it to at most one per response section; do not attach kaomoji to every sentence.
9
+ - Use light anime-style phrasing occasionally: "Let's go!", "We got this!", "Not bad~", "Ohhh nice find!", "That was close...". These are seasoning, not the main dish.
10
+ - Keep any playful punctuation light and occasional. Tildes (~) and exclamation marks are fine in moderation.
9
11
 
10
12
  Boundaries:
11
- - Never sacrifice clarity, accuracy, or usefulness for style.
12
- - Do not overdo catchphrases, memes, or anime references.
13
- - Do not use this style as an excuse to be verbose — stay concise.
13
+ - Do not stuff responses with catchphrases, Japanese loanwords (no kawaii, sugoi, etc.), memes, or roleplay.
14
+ - Do not turn every sentence into a performance; the technical content stays central.
15
+ - Stay concise and readable even when the tone is energetic.
14
16
  - Technical terms, code, file paths, and command output must remain precise and unchanged.
17
+ - Kaomoji must never appear inside code blocks, inline code, or file paths.
package/souls/caveman.md CHANGED
@@ -1,13 +1,13 @@
1
- Respond with a simple caveman-inspired tone.
1
+ Respond with a rugged caveman-inspired tone: blunt, simple, and action-first.
2
2
 
3
3
  Style guidelines:
4
- - Keep sentences short, punchy, and concrete.
5
- - Speak in direct, action-oriented phrases "We fix bug. Code good now." style.
6
- - Use simple metaphors from the physical world (hunting, building, fire) when explaining concepts.
7
- - Celebrate successes with primal enthusiasm "Bug crushed! Tribe safe."
4
+ - Keep sentences short, direct, and concrete.
5
+ - Favor plain, physical metaphors when helpful: build, break, patch, carry, fix.
6
+ - Let the tone feel sturdy and low-drama rather than goofy.
7
+ - Use the caveman flavor as emphasis around decisions or outcomes, not every line.
8
8
 
9
9
  Boundaries:
10
+ - Do not intentionally break grammar so much that the answer becomes harder to follow.
10
11
  - Keep explanations readable and technically accurate.
11
- - Do not make wording so primitive that instructions or code suggestions become unclear.
12
12
  - Technical terms, code, file paths, and command output must remain precise and unchanged.
13
13
  - Never sacrifice correctness for the caveman gimmick.
package/souls/ceo.md CHANGED
@@ -1,14 +1,15 @@
1
- Respond with a bold, decisive CEO-style tone.
1
+ Respond with a crisp operator-CEO tone: decisive, high-ownership, and focused on outcomes.
2
2
 
3
3
  Style guidelines:
4
- - Speak with confidence and urgency focus on what matters, cut the noise.
5
- - Frame every decision around impact, tradeoffs, and execution velocity.
6
- - Use executive phrasing: "Here's the play...", "The right call is...", "Let's ship this."
7
- - Acknowledge risks briefly, then commit to a clear direction.
8
- - Celebrate wins like closing a deal "Clean execution. Moving on."
4
+ - Sound like someone aligning a team around the next move, not giving a motivational speech.
5
+ - Lead with direction: what matters, what we do now, and what risk needs watching.
6
+ - Frame choices in terms of impact, tradeoffs, and execution speed.
7
+ - Use confident but grounded phrasing such as "The right move is...", "Here's the tradeoff.", "Let's keep scope tight."
8
+ - Let wins land cleanly and briefly: "Clean fix.", "Good tradeoff.", "Ready to ship."
9
9
 
10
10
  Boundaries:
11
- - Do not imitate any real person or use cringe corporate buzzwords ("synergy", "paradigm shift").
12
- - Do not let the style override careful technical judgment — precision still wins.
11
+ - Do not imitate any real executive or lean on empty business jargon.
12
+ - Do not become abrasive, domineering, or dismissive of uncertainty.
13
+ - Confidence must come from reasoning, not bluffing; if something is unknown, say so plainly.
13
14
  - Technical terms, code, file paths, and command output must remain precise and unchanged.
14
- - Stay concise CEOs do not ramble.
15
+ - Stay concise. Strong direction beats long speeches.
package/souls/default.md CHANGED
@@ -3,7 +3,7 @@ Respond in a clear, calm, helpful tone.
3
3
  Style guidelines:
4
4
  - Be concise, friendly, and practical in every response.
5
5
  - Prioritize clarity and directness over embellishment.
6
- - Use simple, natural language no forced personality or quirks.
6
+ - Use simple, natural language with no forced personality or quirks.
7
7
 
8
8
  Boundaries:
9
9
  - Avoid roleplay, slang overload, or exaggerated personality.
package/souls/pirate.md CHANGED
@@ -1,13 +1,13 @@
1
- Respond with a playful pirate-inspired tone, matey.
1
+ Respond with a lightly nautical pirate tone: adventurous, playful, and easy to understand.
2
2
 
3
3
  Style guidelines:
4
- - Use light nautical flavor and pirate expressions "Aye", "Shiver me timbers", "Arrr", "Set sail".
5
- - Frame tasks as voyages and adventures "Let's chart a course for that bug."
6
- - Celebrate successes like plundering treasure "Shipshape! Bug walkin' the plank."
7
- - Keep the tone adventurous but grounded.
4
+ - Add just a hint of seafaring flavor in openings, transitions, or short celebrations.
5
+ - Keep the voice sturdy and practical, like a capable captain talking the crew through a repair.
6
+ - Use maritime metaphors sparingly when they genuinely help clarity.
7
+ - Let the personality show more in confirmations than in technical instructions.
8
8
 
9
9
  Boundaries:
10
+ - Do not write in heavy dialect or make every sentence pirate-themed.
10
11
  - Keep the answer clear, useful, and technically accurate first, pirate flavor second.
11
- - Do not overdo slang — every sentence should still be understandable on first read.
12
12
  - Technical terms, code, file paths, and command output must remain precise and unchanged.
13
13
  - Never let roleplay reduce precision or hide important warnings.
package/souls/playful.md CHANGED
@@ -1,13 +1,13 @@
1
- Respond with a witty, lively, and slightly cheeky tone.
1
+ Respond with a witty, lively, lightly cheeky tone.
2
2
 
3
3
  Style guidelines:
4
- - Add personality and humor naturally a well-placed quip or clever analogy goes a long way.
5
- - Use casual, conversational phrasing "So here's the fun part...", "Plot twist:", "Easy fix incoming."
6
- - React to bugs and errors with good-natured humor "Well, that's a creative way to break things."
7
- - Celebrate wins with flair "Nailed it. Next?"
4
+ - Add personality through timing and phrasing, not constant jokes.
5
+ - Keep humor warm and collaborative, like a teammate making the work feel lighter.
6
+ - Use short, sharp transitions when they help the rhythm: "Small plot twist:", "Easy fix.", "That's the culprit."
7
+ - Let wins feel satisfying without turning them into punchlines.
8
8
 
9
9
  Boundaries:
10
- - Keep the answer readable and practical first — humor is the seasoning, not the main dish.
10
+ - Humor is seasoning, not the main dish.
11
11
  - Do not let jokes obscure instructions, warnings, or technical accuracy.
12
+ - Avoid sarcasm that could feel dismissive, smug, or mean.
12
13
  - Technical terms, code, file paths, and command output must remain precise and unchanged.
13
- - Avoid sarcasm that could feel dismissive of the user's question.
@@ -1,7 +1,7 @@
1
1
  Respond in a polished, professional, and authoritative tone.
2
2
 
3
3
  Style guidelines:
4
- - Keep phrasing precise, confident, and concise like a senior engineer briefing a team.
4
+ - Keep phrasing precise, confident, and concise, like a senior engineer briefing a team.
5
5
  - Prefer structured explanations: numbered steps, clear headings, and logical flow.
6
6
  - State conclusions first, then back them up — lead with the answer, follow with reasoning.
7
7
  - Use measured, deliberate language — "The recommended approach is...", "This ensures..."
package/src/cli.js CHANGED
@@ -12,7 +12,9 @@ function printHelp() {
12
12
  Usage:
13
13
  codemini [prompt] [--plain]
14
14
  codemini chat [prompt] [--plain]
15
- codemini run <task> [--max-steps N]
15
+ codemini run <task> [--max-steps N] [--model <name>]
16
+ codemini run --harness <role> <task> [--max-steps N] [--model <name>]
17
+ codemini run --pipeline <task> [--model <name>]
16
18
  codemini config set|get|list <key> [value]
17
19
  codemini doctor
18
20
  codemini skill list|install|enable|disable|inspect|reindex
@@ -5,12 +5,25 @@ import { createChatCompletion } from '../core/provider/index.js';
5
5
  import { buildSystemPromptWithSoul } from '../core/soul.js';
6
6
  import { getBuiltinTools } from '../core/tools.js';
7
7
  import { buildMemorySnapshot } from '../core/memory-prompt.js';
8
+ import { getSubAgentRolePrompt } from '../core/chat-runtime.js';
9
+ import fs from 'node:fs/promises';
10
+ import path from 'node:path';
11
+
12
+ const ROLE_TOOL_POLICY = {
13
+ planner: ['read', 'grep', 'list', 'query_project_index', 'tool_search', 'glob', 'ast_query', 'read_ast_node'],
14
+ coder: ['read', 'grep', 'list', 'edit', 'write', 'run', 'ast_query', 'read_ast_node', 'glob', 'tool_search', 'update_todos'],
15
+ reviewer: ['read', 'grep', 'list', 'glob', 'tool_search', 'ast_query', 'read_ast_node'],
16
+ tester: ['read', 'grep', 'list', 'run', 'glob', 'tool_search']
17
+ };
18
+ const HARNESS_ROLES = Object.keys(ROLE_TOOL_POLICY);
8
19
 
9
20
  function parseRunArgs(args) {
10
21
  const parsed = {
11
22
  task: '',
12
23
  model: undefined,
13
- maxSteps: 8
24
+ maxSteps: 8,
25
+ harness: null,
26
+ pipeline: false
14
27
  };
15
28
  for (let i = 0; i < args.length; i += 1) {
16
29
  const arg = args[i];
@@ -24,11 +37,196 @@ function parseRunArgs(args) {
24
37
  i += 1;
25
38
  continue;
26
39
  }
40
+ if (arg === '--harness') {
41
+ parsed.harness = (args[i + 1] || '').toLowerCase();
42
+ i += 1;
43
+ continue;
44
+ }
45
+ if (arg === '--pipeline') {
46
+ parsed.pipeline = true;
47
+ continue;
48
+ }
27
49
  parsed.task += `${parsed.task ? ' ' : ''}${arg}`;
28
50
  }
29
51
  return parsed;
30
52
  }
31
53
 
54
+ function filterToolsForRole(definitions, handlers, deferredDefinitions, role) {
55
+ const allowed = ROLE_TOOL_POLICY[role];
56
+ if (!allowed) return { definitions, handlers, deferredDefinitions };
57
+ return {
58
+ definitions: definitions.filter((t) => allowed.includes(t.function?.name || t.name)),
59
+ handlers: Object.fromEntries(Object.entries(handlers).filter(([name]) => allowed.includes(name))),
60
+ deferredDefinitions: Object.fromEntries(Object.entries(deferredDefinitions || {}).filter(([name]) => allowed.includes(name)))
61
+ };
62
+ }
63
+
64
+ function makeCompletionFn(config) {
65
+ return async ({ messages, tools, model }) =>
66
+ createChatCompletion({
67
+ sdkProvider: config.sdk?.provider,
68
+ baseUrl: config.gateway.base_url,
69
+ apiKey: config.gateway.api_key,
70
+ model,
71
+ messages,
72
+ tools,
73
+ timeoutMs: config.gateway.timeout_ms || 90000,
74
+ maxRetries: config.gateway.max_retries ?? 2
75
+ });
76
+ }
77
+
78
+ async function buildSystemPrompt(config) {
79
+ const soulPrompt = await buildSystemPromptWithSoul(buildDefaultSystemPrompt(config), config);
80
+ const memorySnapshot = await buildMemorySnapshot({ config, workspaceRoot: process.cwd() }).catch(() => '');
81
+ return [soulPrompt, memorySnapshot].filter(Boolean).join('\n\n');
82
+ }
83
+
84
+ async function runHarness({ role, task, config, systemPrompt, model, maxSteps }) {
85
+ if (!HARNESS_ROLES.includes(role)) {
86
+ throw new Error(`Unknown harness role: ${role}. Available: ${HARNESS_ROLES.join(', ')}`);
87
+ }
88
+ const { definitions, handlers, formatters, deferredDefinitions } = getBuiltinTools({
89
+ workspaceRoot: process.cwd(),
90
+ config
91
+ });
92
+ const filtered = filterToolsForRole(definitions, handlers, deferredDefinitions, role);
93
+ const rolePrompt = getSubAgentRolePrompt(role);
94
+
95
+ const result = await runAgentLoop({
96
+ systemPrompt: `${systemPrompt}\n${rolePrompt}`,
97
+ userPrompt: task,
98
+ model: model || config.model.name,
99
+ toolDefinitions: filtered.definitions,
100
+ toolHandlers: filtered.handlers,
101
+ toolFormatters: formatters,
102
+ deferredDefinitions: filtered.deferredDefinitions,
103
+ maxSteps,
104
+ requestCompletion: makeCompletionFn(config)
105
+ });
106
+ return result;
107
+ }
108
+
109
+ function extractJsonBlock(text) {
110
+ const raw = String(text || '').trim();
111
+ if (!raw) return null;
112
+ try { return JSON.parse(raw); } catch {}
113
+ const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
114
+ if (fenced?.[1]) { try { return JSON.parse(fenced[1]); } catch {} }
115
+ const first = raw.indexOf('{');
116
+ const last = raw.lastIndexOf('}');
117
+ if (first !== -1 && last !== -1 && last > first) {
118
+ try { return JSON.parse(raw.slice(first, last + 1)); } catch {}
119
+ }
120
+ return null;
121
+ }
122
+
123
+ function normalizePlan(parsed, goal) {
124
+ const steps = Array.isArray(parsed?.steps) ? parsed.steps : [];
125
+ const cleaned = steps
126
+ .map((s) => ({
127
+ title: String(s?.title || '').trim(),
128
+ role: String(s?.role || '').trim().toLowerCase(),
129
+ task: String(s?.task || '').trim()
130
+ }))
131
+ .filter((s) => s.title && s.task && HARNESS_ROLES.includes(s.role));
132
+ if (cleaned.length === 0) {
133
+ return { summary: `Fallback plan for: ${goal}`, steps: [{ title: 'Execute task', role: 'coder', task: goal }] };
134
+ }
135
+ return { summary: parsed.summary || `Plan for: ${goal}`, steps: cleaned };
136
+ }
137
+
138
+ async function planPipeline({ goal, config, systemPrompt, model }) {
139
+ const plannerPrompt = [
140
+ 'Create an execution plan and assign the best sub-agent role for each step.',
141
+ 'Return strict JSON only with shape {"summary":"...","steps":[{"title":"...","role":"planner|coder|reviewer|tester","task":"..."}]}. No markdown.',
142
+ `Available roles: ${HARNESS_ROLES.join(', ')}.`,
143
+ 'Prefer 3-5 steps total. The first step should usually inspect the target area.',
144
+ 'For implementation goals, include a reviewer or tester step near the end.',
145
+ 'For advisory/analysis goals, keep it lean with planner/coder only.'
146
+ ].join('\n');
147
+
148
+ const planning = await createChatCompletion({
149
+ sdkProvider: config.sdk?.provider,
150
+ baseUrl: config.gateway.base_url,
151
+ apiKey: config.gateway.api_key,
152
+ model: model || config.model.name,
153
+ messages: [
154
+ { role: 'system', content: `${systemPrompt}\n${plannerPrompt}` },
155
+ { role: 'user', content: `Plan the following task:\n${goal}` }
156
+ ],
157
+ timeoutMs: config.gateway.timeout_ms || 90000,
158
+ maxRetries: config.gateway.max_retries ?? 2
159
+ });
160
+
161
+ const parsed = extractJsonBlock(planning.text || '');
162
+ return normalizePlan(parsed, goal);
163
+ }
164
+
165
+ function writePipelineState(workspaceRoot, state) {
166
+ const dir = path.join(workspaceRoot, '.codemini');
167
+ const filePath = path.join(dir, 'pipeline-state.json');
168
+ return fs.mkdir(dir, { recursive: true }).then(() =>
169
+ fs.writeFile(filePath, JSON.stringify(state, null, 2), 'utf-8')
170
+ ).catch(() => {});
171
+ }
172
+
173
+ async function runPipeline({ task, config, systemPrompt, model }) {
174
+ console.log('[pipeline] Planning...');
175
+ const plan = await planPipeline({ goal: task, config, systemPrompt, model });
176
+
177
+ console.log(`[pipeline] Plan: ${plan.summary}`);
178
+ plan.steps.forEach((s, i) => console.log(` ${i + 1}. [${s.role}] ${s.title}`));
179
+ console.log('');
180
+
181
+ const priorSteps = [];
182
+ const pipelineState = {
183
+ goal: task,
184
+ summary: plan.summary,
185
+ steps: plan.steps.map((s) => ({ ...s, status: 'pending' })),
186
+ artifacts: [],
187
+ startedAt: new Date().toISOString()
188
+ };
189
+
190
+ for (let i = 0; i < plan.steps.length; i += 1) {
191
+ const step = plan.steps[i];
192
+ pipelineState.steps[i].status = 'running';
193
+ await writePipelineState(process.cwd(), pipelineState);
194
+
195
+ console.log(`[pipeline] Step ${i + 1}/${plan.steps.length} -> ${step.role}: ${step.title}`);
196
+
197
+ const result = await runHarness({
198
+ role: step.role,
199
+ task: step.task,
200
+ config,
201
+ systemPrompt,
202
+ model,
203
+ maxSteps: Number(config.execution?.max_steps || 12)
204
+ });
205
+
206
+ const stepResult = {
207
+ role: step.role,
208
+ title: step.title,
209
+ output: (result.text || '').slice(0, 500),
210
+ status: 'done'
211
+ };
212
+ priorSteps.push(stepResult);
213
+
214
+ pipelineState.steps[i].status = 'done';
215
+ pipelineState.steps[i].output = stepResult.output;
216
+ pipelineState.artifacts.push(stepResult);
217
+ await writePipelineState(process.cwd(), pipelineState);
218
+
219
+ console.log(`[pipeline] Step ${i + 1} complete.\n`);
220
+ }
221
+
222
+ pipelineState.completedAt = new Date().toISOString();
223
+ await writePipelineState(process.cwd(), pipelineState);
224
+
225
+ console.log('[pipeline] All steps complete.');
226
+ console.log(`[pipeline] State saved to .codemini/pipeline-state.json`);
227
+ return pipelineState;
228
+ }
229
+
32
230
  export async function handleRun(args) {
33
231
  const parsed = parseRunArgs(args);
34
232
  if (!parsed.task) {
@@ -36,14 +234,39 @@ export async function handleRun(args) {
36
234
  }
37
235
 
38
236
  const config = await loadConfig();
237
+ const systemPrompt = await buildSystemPrompt(config);
238
+
239
+ if (parsed.pipeline) {
240
+ const state = await runPipeline({
241
+ task: parsed.task,
242
+ config,
243
+ systemPrompt,
244
+ model: parsed.model
245
+ });
246
+ for (const step of state.steps) {
247
+ console.log(`\n--- [${step.role}] ${step.title} ---`);
248
+ console.log(step.output || '(no output)');
249
+ }
250
+ return;
251
+ }
252
+
253
+ if (parsed.harness) {
254
+ const result = await runHarness({
255
+ role: parsed.harness,
256
+ task: parsed.task,
257
+ config,
258
+ systemPrompt,
259
+ model: parsed.model,
260
+ maxSteps: parsed.maxSteps
261
+ });
262
+ console.log(result.text);
263
+ return;
264
+ }
265
+
39
266
  const { definitions, handlers, formatters, deferredDefinitions } = getBuiltinTools({
40
267
  workspaceRoot: process.cwd(),
41
268
  config
42
269
  });
43
- const soulPrompt = await buildSystemPromptWithSoul(buildDefaultSystemPrompt(config), config);
44
- const memorySnapshot = await buildMemorySnapshot({ config, workspaceRoot: process.cwd() }).catch(() => '');
45
- const systemPrompt = [soulPrompt, memorySnapshot].filter(Boolean).join('\n\n');
46
-
47
270
  const result = await runAgentLoop({
48
271
  systemPrompt,
49
272
  userPrompt: parsed.task,
@@ -53,17 +276,7 @@ export async function handleRun(args) {
53
276
  toolFormatters: formatters,
54
277
  deferredDefinitions,
55
278
  maxSteps: parsed.maxSteps,
56
- requestCompletion: async ({ messages, tools, model }) =>
57
- createChatCompletion({
58
- sdkProvider: config.sdk?.provider,
59
- baseUrl: config.gateway.base_url,
60
- apiKey: config.gateway.api_key,
61
- model,
62
- messages,
63
- tools,
64
- timeoutMs: config.gateway.timeout_ms || 90000,
65
- maxRetries: config.gateway.max_retries ?? 2
66
- })
279
+ requestCompletion: makeCompletionFn(config)
67
280
  });
68
281
 
69
282
  console.log(result.text);