Skip to content
Merged
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
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,65 @@ For full setup and remote usage details, see [docs/telegram.md](docs/telegram.md
| `CoClaw.telegram.silentUnauthorized` | `false` | When `true`, silently drop messages from non-linked users instead of replying with `Unauthorized` |
| `CoClaw.agents.mode` | `slash` | Multi-agent mode: `off`, `slash` (only on `/agents`), or `always` (route every prompt) |
| `CoClaw.agents.maxParallelCoders` | `4` | Maximum number of coder agents that may run in parallel (1–8) |
| `CoClaw.agents.minParallelCoders` | `1` | Minimum number of coder agents to spawn per task. The splitter pads small tasks with generic lanes (implementation, tests, docs, …) up to this floor. Capped to `maxParallelCoders` |
| `CoClaw.agents.summaryMaxChars` | `8000` | Per-task character cap for the multi-agent run summary and the copy persisted to shared memory. `0` = unlimited |
| `CoClaw.agents.alwaysShowFullOutput` | `false` | When `true`, every `/agents` run skips the per-task cap (equivalent to typing `--full` on every prompt). One-shot alternative: prefix or suffix your prompt with `--full` (aliases: `--all`, `--no-truncate`) |
| `CoClaw.tools.maxPerRequest` | `120` | Hard cap on tools sent per LM request. Lower this if a model rejects calls with “Cannot have more than N tools per request” (most providers cap at 128) |
| `CoClaw.tools.exclude` | `[]` | Substring patterns; matching tool names are dropped before the request reaches the model (e.g. `["mssql", "jupyter"]`) |
| `CoClaw.tools.priority` | `[]` | Substring patterns; matching tools are bumped above CoClaw’s own tools so they survive the per-request cap |

## Development

```bash
# Install dependencies
npm install

# Build (production)
npm run build

# Watch mode (development)
npm run watch

# Type-check
npm run typecheck

# Lint
npm run lint

# Run tests
npm test

# Package as .vsix
npm run package
```

To debug, open the project in VS Code and press **F5** — this launches an Extension Development Host with CoClaw loaded.

## Architecture

```
src/
├── extension.ts # Extension entry point
├── agents/ # Multi-agent orchestration (Planner, Coder, Reviewer, Tester, Memory)
├── commands/ # VS Code command implementations
├── cron/ # Cron job scheduler for /open mode
├── heartbeat/ # Heartbeat checks for /open mode
├── lm/ # Language model integration (model manager, prompt builder, tool filter)
├── memory/ # Two-layer memory system (daily + long-term)
├── participant/ # Copilot Chat participant handler
├── profile/ # Identity (SOUL) and user profile (USER) management
├── telegram/ # Telegram bot bridge
├── tools/ # LM tool definitions and handlers
├── ui/ # Tree views, webviews, status bar
└── util/ # Shared utilities
```

## Contributing

See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for branch protection rules and PR requirements.

In short: open a PR against `main`. CI must pass (`typecheck`, `test`, `build`, `package`) and one CODEOWNER must approve before merging.

## Documentation

- [Getting Started](docs/getting-started.md)
Expand Down
2 changes: 1 addition & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Runs your task through the multi-agent orchestrator. A Planner produces a JSON D
@CoClaw /agents Build a login page with API and tests
```

Live progress is shown in the **Agents** sidebar (the CoClaw activity bar icon). Tune fan-out width with `CoClaw.agents.maxParallelCoders` and switch modes with `CoClaw.agents.mode` (`off`, `slash`, `always`).
Live progress is shown in the **Agents** sidebar (the CoClaw activity bar icon). Tune fan-out width with `CoClaw.agents.maxParallelCoders` (ceiling) and `CoClaw.agents.minParallelCoders` (floor — pads small tasks with generic lanes), and switch modes with `CoClaw.agents.mode` (`off`, `slash`, `always`).

## Command Palette Commands

Expand Down
47 changes: 47 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,53 @@ All CoClaw settings are under the `CoClaw.*` namespace. Open them quickly with *
- **Range:** 1–8
- **Description:** Maximum number of coder agents that may run in parallel during a `/agents` run.

### `CoClaw.agents.minParallelCoders`

- **Type:** integer
- **Default:** `1` (no forced minimum — current/legacy behavior)
- **Range:** 1–8
- **Description:** Minimum number of coder agents to spawn for any single coder task. When the heuristic splitter would naturally produce fewer (e.g. an atomic typo fix, or a task that doesn't trigger any work-lane keywords), it is **padded** with generic lanes from this fallback list — in this order:
`implementation` → `tests` → `docs` → `error-handling` → `logging-telemetry` → `config-build` → `auth-security` → `business-logic`.
- **Always capped to `maxParallelCoders`** at runtime, so a misconfigured `min=8 / max=4` pair silently behaves as `min=4`.
- **Trade-off:** raising the floor guarantees more parallel coverage on small tasks, but for genuinely atomic work (a one-line fix) it spends model tokens on padded lanes that may not have anything meaningful to do. Default `1` keeps the original "split only when warranted" behavior.

### `CoClaw.agents.summaryMaxChars`

- **Type:** integer
- **Default:** `8000`
- **Range:** 0–200000 (0 = unlimited)
- **Description:** Per-task character cap applied in two places:
1. The chat-panel summary block that lists `✓ <agent> [<role>]` results at the end of a `/agents` run.
2. The copy of each agent's text written to the shared-memory store under `<role>:<task-id>` for downstream agents (Reviewer/Tester) to read.

Bump it to `0` for unlimited output if you want the full report inline; raise it to a finite number (e.g. `20000`) if you need more room without going boundless. Lowering it shortens the chat but also reduces the context that dependent agents receive — keep both axes in mind.

### `CoClaw.agents.alwaysShowFullOutput`

- **Type:** boolean
- **Default:** `false`
- **Description:** When `true`, every `/agents` run skips the per-task character cap entirely — equivalent to passing `--full` on every prompt. This overrides `CoClaw.agents.summaryMaxChars`. Useful when you regularly need long, untrimmed summaries; otherwise leave it off and pass `--full` ad-hoc.

#### Precedence

The runtime cap is resolved in this order (first match wins):

1. `--full` (or `--all` / `--no-truncate` / `--notrunc`) anywhere in the `/agents` prompt as a leading or trailing token → unlimited for this run only.
2. `CoClaw.agents.alwaysShowFullOutput: true` → unlimited for every run.
3. `CoClaw.agents.summaryMaxChars: 0` → unlimited for every run (legacy "magic 0" form).
4. `CoClaw.agents.summaryMaxChars: <N>` → cap at N characters per task.
5. Default → 8000 characters.

#### Per-run override: `--full` flag

Prefer not to touch settings every time you want a long answer? Prefix or suffix your `/agents` prompt with `--full` (aliases: `--all`, `--no-truncate`, `--notrunc`) and the cap is bypassed for that run only:

```
@CoClaw /agents --full investigate the workflow files and summarize every job
```

The flag is stripped before the planner sees the prompt, so it never leaks into the agent's reasoning. A mid-prompt occurrence (e.g. *"explain what `--full` does"*) is preserved verbatim so you can still talk *about* the flag without triggering it.

## Tool-Selection Settings

These settings control which tools CoClaw forwards to the language model on every request. They apply uniformly across the chat participant, the Telegram bridge, and the multi-agent orchestrator.
Expand Down
21 changes: 20 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "CoClaw",
"displayName": "CoClaw",
"description": "AI coding assistant with persistent memory, powered by GitHub Copilot",
"version": "0.2.2",
"version": "0.2.3",
"publisher": "gdhanush270",
"license": "Apache-2.0",
"icon": "icon.png",
Expand Down Expand Up @@ -521,6 +521,25 @@
"maximum": 8,
"description": "Maximum number of parallel Coder agents the orchestrator may fan out for a single coder task."
},
"CoClaw.agents.minParallelCoders": {
"type": "integer",
"default": 1,
"minimum": 1,
"maximum": 8,
"description": "Minimum number of parallel Coder agents to spawn for a single coder task. When the splitter would produce fewer (e.g. for a small task), it is padded with generic lanes (implementation, tests, docs, ...) up to this floor. Default 1 = current behavior (no forced minimum). Capped to 'maxParallelCoders' if you accidentally set it higher. Use with care: a min above 1 forces parallel work even on atomic tasks like typo fixes."
},
"CoClaw.agents.summaryMaxChars": {
"type": "integer",
"default": 8000,
"minimum": 0,
"maximum": 200000,
"description": "Per-task character cap for the multi-agent run summary (and the copy persisted to shared memory). 0 = unlimited (use with care: a runaway agent can dump tens of thousands of chars into the chat). Ignored when 'CoClaw.agents.alwaysShowFullOutput' is true or when '--full' is in the prompt."
},
"CoClaw.agents.alwaysShowFullOutput": {
"type": "boolean",
"default": false,
"description": "When true, every /agents run prints each agent's full output without truncation — equivalent to passing '--full' on every prompt. Overrides 'CoClaw.agents.summaryMaxChars'. Use this if you regularly need long, untrimmed summaries; leave off (default) and pass '--full' ad-hoc when you want occasional long output."
},
"CoClaw.tools.maxPerRequest": {
"type": "integer",
"default": 120,
Expand Down
38 changes: 37 additions & 1 deletion src/agents/CoderSplitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,31 @@ export interface SplitResult {
didSplit: boolean;
}

/**
* Generic lanes used to *pad* a coder task up to `minParallel` when the
* heuristic would otherwise hand back fewer units. Ordered by usefulness —
* implementation/tests/docs are almost always something a coder can do.
*/
const PAD_LANES: readonly string[] = [
'implementation',
'tests',
'docs',
'error-handling',
'logging-telemetry',
'config-build',
'auth-security',
'business-logic',
];

/**
* Decide how many parallel coder agents to fan out for a single coder task.
* Heuristic order:
* 1. If the planner provided `units`, use them (one agent per unit, capped).
* 2. Otherwise extract file-path-like tokens from the prompt.
* 3. Otherwise extract bullet/numbered list items from the prompt.
* 4. Otherwise leave as a single task.
* 4. Otherwise leave as a single task — UNLESS minParallel > 1, in which
* case pad with generic lanes (implementation/tests/docs/...) to reach
* the floor.
*
* If two units target the same file path, they are chained sequentially
* (dependsOn) instead of parallelized to avoid file-write races.
Expand All @@ -22,17 +40,35 @@ export function splitCoderTask(
task: SubTask,
userPrompt: string,
maxParallel: number,
minParallel = 1,
): SplitResult {
if (task.agent !== 'coder') {
return { replacements: [task], didSplit: false };
}

const cap = Math.max(1, Math.min(maxParallel, 8));
// Minimum can never exceed the maximum, and never goes below 1. We
// silently clamp instead of erroring so a misconfigured `min > max`
// setting doesn't block /agents from running.
const floor = Math.max(1, Math.min(minParallel, cap));

let units = (task.units && task.units.length > 0) ? task.units.slice() : extractUnits(task.prompt, userPrompt);

// Deduplicate while preserving order
units = Array.from(new Set(units.map(u => u.trim()).filter(u => u.length > 0)));

// Pad up to the floor with generic lanes that aren't already represented.
if (units.length < floor) {
const have = new Set(units.map(u => u.toLowerCase()));
for (const lane of PAD_LANES) {
if (units.length >= floor) { break; }
if (!have.has(lane)) {
units.push(lane);
have.add(lane);
}
}
}

if (units.length <= 1) {
return { replacements: [task], didSplit: false };
}
Expand Down
Loading
Loading