Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ And when an agent spawns sub-agents of its own — Claude Code, Codex, Grok and
![Delegating a task to sub-agents from a single Codeg conversation](./docs/images/collaboration-light.gif#gh-light-mode-only)
![Delegating a task to sub-agents from a single Codeg conversation](./docs/images/collaboration-dark.gif#gh-dark-mode-only)

## 🔁 Agent Pipelines

Compose agents into structured, multi-step execution chains with automated review and test loops. Choose **Duet** to have a coder write code and a reviewer verify it, or **Team** to run planning, coding, reviewing, and testing in sequence. When a reviewer or test step requests changes, the pipeline routes findings back to the coder for a new iteration, up to a round limit you configure.

Each pipeline run executes in its own isolated Git worktree so your working tree stays clean. Inspect the interactive **Chat + code** diff panel to leave line-by-line notes for the next iteration or apply changes directly via squash or merge commit when you're satisfied. Optional local SQLite or external MCP memory lets agents recall architecture decisions, bug fixes, and project conventions across runs. → [Agent Pipelines](docs/agent-pipelines.md)

## ✅ To-dos

Not every job needs you watching it. Write one down — a title, a description, the agent to run it with — and Codeg hands it **its own copy of the code**: a git worktree beside your project, on its own branch. Several run at once without touching each other, or the tree you're working in. Schedule one for tonight, or let a folder work through its queue on its own, up to a concurrency limit you set.
Expand Down Expand Up @@ -154,6 +160,7 @@ Step away from your desk, not your work. The native iOS and Android clients conn

- **[Conversation Aggregation](https://docs.codeg.app/guide/aggregation)** — import sessions from every supported agent into one unified, searchable workspace, and pick any of them up where you left off
- **[Multi-Agent Collaboration](https://docs.codeg.app/guide/multi-agent)** — `@`-mention any agent to delegate: sub-agents of different types run as their own sessions, in parallel, inside a single task
- **[Agent Pipelines](docs/agent-pipelines.md)** — orchestrate agents in multi-step chains (Duet, Team, Custom) with automated review loops, worktree isolation, line notes, and persistent memory
- **[To-dos](https://docs.codeg.app/guide/tasks)** — write down what needs doing and agents work through the queue, each task in its own worktree, landing on your branch only after you've reviewed it
- **[Custom Agents](https://docs.codeg.app/guide/custom-agents)** — register any other ACP-compatible agent from the public registry or its distribution JSON; Codeg installs it, records its history, and treats it like a built-in
- **[The Workspace](https://docs.codeg.app/guide/workspace)** — the full engineering loop next to the agent: file tree, editor and diff, git changes, commit, an embedded terminal, and [several folders linked into one workspace](https://docs.codeg.app/guide/workspace#work-across-several-folders)
Expand Down
160 changes: 160 additions & 0 deletions docs/agent-pipelines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Agent pipelines

One agent in a chat handles a targeted edit well. A larger task usually wants a
shape: plan it, write it, review it, and send the review back to whoever wrote
the code. Until now that meant driving every hand-off by hand.

A pipeline runs that chain for you. Nothing here is on by default: Codeg still
opens in single-agent mode with memory off, and the chat you already know
behaves exactly as before.

## Picking a chain

The composer has four buttons above the message box.

| Mode | Chain | Fix rounds |
| --- | --- | --- |
| Single agent | no pipeline, the classic chat | n/a |
| Duet | coder, then reviewer | up to 3 |
| Team | planner, coder, reviewer, tests | up to 3 |
| Custom | whatever you build | 1 to 10 |

Pick anything but Single and a row of chips appears below, one per step, each
naming its role, its agent and its model. That row is the chain that will run.

The choice is remembered per folder.

## Editing the chain

Click a chip. Two lists open: the agent that runs the step, and the model it
runs on. The models come from asking that agent what it accepts, so the list is
whatever your installed adapter really offers rather than a table that goes
stale. There is no save button. The chip updates and the change is stored.

Editing Duet or Team stores your version of the built-in chain; a **Reset**
appears beside the chips and puts the shipped one back. A custom chain saves as
itself.

In Custom mode a **+** sits at the end of the row. It asks for the role first,
because the role decides where the step lands: a planner goes to the front,
since it exists to brief the steps after it, and everything else is appended.
Deleting a step lives inside the same popover, and the last step cannot go.

While a pipeline mode is active the composer hides its own model and reasoning
pickers. The model belongs to each step now, and two controls for one thing
point at a session the run does not use. Everything else stays, including the
edit-permission mode, which matters more once several agents are writing.

For the rest of a step (its prompt, timeout, read-only flag and loop target)
open the pipeline card on the Infinite Conversations canvas.

## What the roles do

- **planner** reads the task and the code and writes a plan. It must not edit.
- **coder** makes the change. This is the only step expected to write files.
- **reviewer** checks the result and reports a verdict. It must not edit.
- **tests** runs the suite and reports a verdict. It may write, because test
runs leave artifacts.
- **custom** is whatever you need.

A step marked read-only is policed: Codeg hashes the worktree before and after
it, and a step that changed anything has its verdict turned into `inconclusive`
with the offending paths named. The step's prompt says so in plain words too,
so the agent is told the rule rather than only punished for breaking it.

Two things commonly trip that guard and are worth knowing about: a session-start
hook that writes a file into every new directory, and a build cache the project
does not ignore. Both show up in the note, so you can add them to `.gitignore`
and move on.

## Verdicts

A reviewer or test step ends by calling the `pipeline_verdict` tool once:

- `pass`: the work is good, the chain moves on.
- `changes_requested`: back to the coder with the notes, which are required.
- `inconclusive`: the step could not tell, and the run stops.

If the agent's tool calling is unreliable, a `VERDICT: PASS` line in its own
output is parsed as a fallback. Prefer an agent that calls the tool properly;
the fallback is a safety net, not a plan.

After the last fix round the run stops as `stopped_max_iterations` rather than
looping forever.

## Isolation and landing the work

Each run works in its own git worktree on a temporary branch, so your working
copy is untouched while agents write. When you are satisfied, **Apply** brings
the result over as either a squash commit or a merge commit, then removes the
worktree and the branch.

Apply refuses to run when the project has staged changes of its own, rather
than sweeping them into a commit you did not write.

A folder can only host one run at a time.

If Codeg is closed mid-run, the run is marked `interrupted` on the next start
rather than being left looking alive.

## Watching a run

The run card in the conversation shows each step, the round count and the
verdicts as they land.

The **chat + code** panel beside it shows what changed: a file tree with A/M/D/R
badges, the diff itself, and a place to leave notes on individual lines. From
there you can send those notes back for another round, stop and finish by hand,
or apply.

## Running one on a schedule

Automations take a **Run pipeline** action. Pick the chain, pick the folder, set
a cron expression or leave it manual. The run is headless and behaves exactly as
it would from the composer, worktree included.

## Memory

Off by default. Turn it on in Settings, Memory.

Three backends: off, a local SQLite graph with full-text search and two-hop edge
traversal, or your own MCP server.

What gets remembered is your choice. Four built-in kinds ship (decisions, fixed
bugs, task summaries, facts and preferences) and you can add your own with an
instruction in your own words. Each kind is set to automatic, on request, or
off, and the whole store is scoped either to the project or shared globally.

`memory_write`, `memory_search` and `memory_link` are exposed to agents only
while memory is on. Everything written passes a redactor that strips API keys,
tokens, passwords and private keys first. Injected memory is wrapped in a
`<memory untrusted="true">` block so an agent treats it as data.

Scope is worth a thought if you keep several clients' projects in one window:
project scope keeps each store separate, global shares one across all of them.

## Step reference

A step in the graph carries:

| Field | Meaning |
| --- | --- |
| `id` | unique, lowercase, up to 32 chars |
| `role` | `planner`, `coder`, `reviewer`, `tests`, `custom` |
| `label` | what the UI shows |
| `agent_type` | `claude_code`, `codex`, `gemini`, any installed ACP agent |
| `mode_id` | optional agent sub-mode |
| `config_values` | passed to the agent; `model` lives here |
| `prompt_template` | the instructions, with the variables below |
| `timeout_secs` | default 1800, range 1 to 86400 |
| `read_memory` | query memory before this step |
| `read_only` | the step must not touch files |

Prompt variables: `$task` (what you typed), `$plan` (the last planner's
summary), `$summary` (the previous step's), `$review` (notes from the last
`changes_requested`), `$memory` (retrieved context).

A graph is rejected before it runs if it is empty, has more than eight steps,
repeats a step id, names an unknown agent, has an empty prompt, has a timeout
out of range, or has a loop that points forward, starts anywhere but a reviewer
or tests step, or asks for fewer than 1 or more than 10 rounds.
7 changes: 7 additions & 0 deletions docs/readme/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ Claude Code · Codex · Gemini · OpenClaw · OpenCode · Cline · Hermes · Cod
![在单个 Codeg 会话中将任务委派给子智能体](../images/collaboration-light.gif#gh-light-mode-only)
![在单个 Codeg 会话中将任务委派给子智能体](../images/collaboration-dark.gif#gh-dark-mode-only)

## 🔁 智能体流水线

将多个智能体编排为结构化的多步骤执行链,内置自动化的代码审阅与测试反馈闭环。选择 **双人结对** 让编码者写代码、审阅者核验,或选择 **团队协作** 依次执行规划、编码、审阅与测试。当审阅或测试步骤提出修改意见时,流水线会将具体问题打回给编码者开启新一轮修复,直到达到设定的轮次上限。

每次流水线运行都在独立的 Git 工作树中进行,绝不污染你当前的工作目录。通过 **聊天 + 代码** 差异面板逐行添加批注并直接交给编码者,满意后一键 Squash 或合并提交应用变更。可选的本地 SQLite 或外部 MCP 记忆系统还能让智能体在多次运行间持久化记录架构决策、历史缺陷与项目偏好。→ [智能体流水线](../agent-pipelines.md)

## ✅ 待办任务

不是每件事都得你盯着做完。写下来就行——标题、说明、用哪个智能体跑——Codeg 会给它**一份独立的代码副本**:项目旁边的一个 git 工作树,跑在自己的分支上。几个任务同时开工也互不干扰,更不会碰你手头那份代码。可以约在今晚开始,也可以让某个文件夹自己按并发上限一件件处理下去。
Expand Down Expand Up @@ -154,6 +160,7 @@ Claude Code · Codex · Gemini · OpenClaw · OpenCode · Cline · Hermes · Cod

- **[会话聚合](https://docs.codeg.app/zh/guide/aggregation)** — 把所有受支持智能体的会话导入统一、可搜索的工作区,并从上次中断处继续
- **[多智能体协作](https://docs.codeg.app/zh/guide/multi-agent)** — `@` 提及任意智能体即可委派:不同类型的子智能体各自作为独立会话,在同一个任务内并行运行
- **[智能体流水线](../agent-pipelines.md)** — 将智能体编排为多步骤流水线(双人结对、团队协作、自定义),支持自动化审阅闭环、工作树隔离、代码行批注与持久化记忆
- **[待办任务](https://docs.codeg.app/zh/guide/tasks)** — 把要做的事写下来,智能体一件件做完;每个任务在自己的工作树里跑,只有你验收之后才会合进你的分支
- **[自定义智能体](https://docs.codeg.app/zh/guide/custom-agents)** — 从公开注册表或 distribution JSON 注册任何其它兼容 ACP 的智能体;Codeg 负责安装、记录历史,并像内置智能体一样对待它
- **[工作区](https://docs.codeg.app/zh/guide/workspace)** — 智能体旁边就是完整的工程闭环:文件树、编辑器与 diff、Git 变更、提交、内置终端,以及[挂进同一个工作区的多个文件夹](https://docs.codeg.app/zh/guide/workspace#work-across-several-folders)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"tauri:prepare-sidecars": "node src-tauri/scripts/prepare-sidecars.mjs",
"tauri:before-dev": "pnpm tauri:prepare-sidecars && pnpm dev",
"tauri:before-build": "pnpm build && pnpm tauri:prepare-sidecars",
"i18n:check": "node scripts/check-i18n-parity.mjs",
"postinstall": "node -e \"const fs=require('fs');fs.cpSync('node_modules/monaco-editor/min/vs','public/vs',{recursive:true,force:true});const p='public/vs/loader.js';fs.writeFileSync(p,fs.readFileSync(p,'utf8').replace(/\\n\\/\\/# sourceMappingURL=.*/,''))\""
},
"dependencies": {
Expand Down
93 changes: 93 additions & 0 deletions scripts/check-i18n-parity.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import fs from "node:fs"
import path from "node:path"
import { fileURLToPath } from "node:url"

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const MESSAGES_DIR = path.resolve(__dirname, "../src/i18n/messages")
const EN_FILE = path.join(MESSAGES_DIR, "en.json")

function collectKeys(node, prefix = "") {
if (typeof node !== "object" || node === null) {
return [prefix]
}
const out = []
for (const [key, value] of Object.entries(node)) {
const next = prefix ? `${prefix}.${key}` : key
out.push(...collectKeys(value, next))
}
return out
}

function run() {
if (!fs.existsSync(EN_FILE)) {
console.error(`en.json not found at ${EN_FILE}`)
process.exit(1)
}

let enContent
try {
enContent = JSON.parse(fs.readFileSync(EN_FILE, "utf8"))
} catch (err) {
console.error(`Failed to parse en.json: ${err.message}`)
process.exit(1)
}

const enKeys = new Set(collectKeys(enContent))
console.log(`Reference (en.json): ${enKeys.size} recursive keys`)

const files = fs
.readdirSync(MESSAGES_DIR)
.filter((f) => f.endsWith(".json") && f !== "en.json")
.sort()

let hasDiscrepancy = false

for (const file of files) {
const fullPath = path.join(MESSAGES_DIR, file)
let content
try {
content = JSON.parse(fs.readFileSync(fullPath, "utf8"))
} catch (err) {
console.error(`[FAIL] ${file}: Failed to parse JSON - ${err.message}`)
hasDiscrepancy = true
continue
}

const localeKeys = new Set(collectKeys(content))
const missing = [...enKeys].filter((k) => !localeKeys.has(k))
const extra = [...localeKeys].filter((k) => !enKeys.has(k))

if (missing.length === 0 && extra.length === 0) {
console.log(`[PASS] ${file}: ${localeKeys.size} keys match en.json`)
} else {
hasDiscrepancy = true
console.error(
`[FAIL] ${file}: Discrepancies found (${localeKeys.size} keys vs ${enKeys.size} reference)`
)
if (missing.length > 0) {
console.error(` Missing in ${file} (${missing.length}):`)
for (const k of missing) {
console.error(` - ${k}`)
}
}
if (extra.length > 0) {
console.error(` Extra in ${file} (${extra.length}):`)
for (const k of extra) {
console.error(` + ${k}`)
}
}
}
}

if (hasDiscrepancy) {
console.error("\ni18n parity check failed.")
process.exit(1)
}

console.log("\nAll locales are in perfect parity with en.json.")
process.exit(0)
}

run()
1 change: 1 addition & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ keyring = { version = "3", features = ["apple-native", "windows-native", "sync-s
axum = { version = "0.8", features = ["ws", "multipart"] }
async_zip = { version = "0.0.18", features = ["tokio", "deflate"] }
tower-http = { version = "0.6", features = ["fs", "cors", "compression-gzip", "compression-br"] }
tower = { version = "0.5" }
# Direct deps (already in the graph via axum) so the compression predicate can
# name the exact types tower-http's `Predicate` trait is defined over.
http = "1"
Expand Down
Loading