ccteams 0.1.0
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/LICENSE +21 -0
- package/README.md +227 -0
- package/bin/ccteams.js +174 -0
- package/lib/manifest.js +59 -0
- package/lib/teams.js +64 -0
- package/lib/use.js +230 -0
- package/package.json +25 -0
- package/teams/debug/agents/bug-fixer.md +52 -0
- package/teams/debug/agents/bug-reproducer.md +53 -0
- package/teams/debug/orchestration.md +19 -0
- package/teams/debug/team.json +6 -0
- package/teams/frontend/agents/ui-builder.md +47 -0
- package/teams/frontend/agents/ux-reviewer.md +60 -0
- package/teams/frontend/orchestration.md +19 -0
- package/teams/frontend/team.json +6 -0
- package/teams/generalist/agents/architect.md +48 -0
- package/teams/generalist/agents/builder.md +43 -0
- package/teams/generalist/agents/qa-reviewer.md +59 -0
- package/teams/generalist/agents/scope-planner.md +42 -0
- package/teams/generalist/agents/shipper.md +54 -0
- package/teams/generalist/orchestration.md +32 -0
- package/teams/generalist/team.json +6 -0
- package/teams/go-api/agents/go-builder.md +61 -0
- package/teams/go-api/agents/go-reviewer.md +54 -0
- package/teams/go-api/orchestration.md +19 -0
- package/teams/go-api/team.json +6 -0
- package/teams/next-ts/agents/next-builder.md +49 -0
- package/teams/next-ts/agents/next-reviewer.md +39 -0
- package/teams/next-ts/orchestration.md +20 -0
- package/teams/next-ts/team.json +6 -0
- package/teams/python-fastapi/agents/fastapi-builder.md +74 -0
- package/teams/python-fastapi/agents/fastapi-reviewer.md +60 -0
- package/teams/python-fastapi/orchestration.md +19 -0
- package/teams/python-fastapi/team.json +6 -0
- package/teams/rails/agents/rails-builder.md +55 -0
- package/teams/rails/agents/rails-reviewer.md +55 -0
- package/teams/rails/orchestration.md +19 -0
- package/teams/rails/team.json +6 -0
- package/teams/research/agents/tech-researcher.md +67 -0
- package/teams/research/orchestration.md +18 -0
- package/teams/research/team.json +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 toffyui
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# ccteams — Agent-Team Package Manager for Claude Code
|
|
2
|
+
|
|
3
|
+
Apply a pre-built team of Claude Code subagents to your project with one command, and switch teams whenever the work changes. An **agent team** is a bundle of subagents (with specific roles, expertise, and behaviors) plus orchestration rules that control how they collaborate — managed as a single unit in your project's `.claude/` directory.
|
|
4
|
+
|
|
5
|
+
## Two ways to use it
|
|
6
|
+
|
|
7
|
+
Use ccteams from the terminal, from inside Claude Code, or both — whichever fits your flow.
|
|
8
|
+
|
|
9
|
+
https://github.com/user-attachments/assets/d6c89e7f-3945-484f-95a5-c69eb73cf035
|
|
10
|
+
|
|
11
|
+
https://github.com/user-attachments/assets/ff71a9cf-a981-440b-8c35-c783dd559ad1
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ccteams list # see the teams
|
|
15
|
+
ccteams use <team> # apply one (e.g. ccteams use go-api) to the current project
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| | How you drive it | |
|
|
19
|
+
| ------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| **CLI** (`ccteams`) | From your terminal | `ccteams list`, `ccteams use <team>`, `ccteams current` |
|
|
21
|
+
| **Plugin** (`/ccteams:*`) | From inside Claude Code | `/ccteams:list-teams`, `/ccteams:use-team`, and `/ccteams:choose-team` — describe what you need and it picks the team for you |
|
|
22
|
+
|
|
23
|
+
The CLI is the engine, so it's always installed; the plugin adds the in-Claude-Code slash
|
|
24
|
+
commands (its skills call the CLI under the hood). Install one or both.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
### 1. Install the CLI
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g ccteams
|
|
32
|
+
ccteams list
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Verify it prints the available teams. You can already use ccteams now — apply a team with
|
|
36
|
+
`ccteams use <team>` and restart Claude Code.
|
|
37
|
+
|
|
38
|
+
### 2. Add the Claude Code plugin
|
|
39
|
+
|
|
40
|
+
For slash commands inside Claude Code, add the marketplace and install the plugin:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
/plugin marketplace add toffyui/ccteams
|
|
44
|
+
/plugin install ccteams@ccteams
|
|
45
|
+
/reload-plugins
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or restart Claude Code. The slash commands `/ccteams:list-teams`, `/ccteams:use-team`, and `/ccteams:choose-team` will then be available. (The plugin's skills call the `ccteams` CLI under the hood, so the CLI must be installed too.)
|
|
49
|
+
|
|
50
|
+
## Updating
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# CLI (new commands, new bundled teams)
|
|
54
|
+
npm install -g ccteams@latest
|
|
55
|
+
|
|
56
|
+
# Plugin (new or changed slash commands)
|
|
57
|
+
/plugin marketplace update ccteams # re-pull the latest from the repo
|
|
58
|
+
/reload-plugins # or restart Claude Code
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
A full uninstall/reinstall is **not** needed. New slash commands reach users when the
|
|
62
|
+
plugin's `version` is bumped (the plugin is versioned via `plugin.json`); a marketplace
|
|
63
|
+
update followed by `/reload-plugins` picks them up.
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
### Command Line (CLI)
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
ccteams list # All teams (compact, one line each)
|
|
71
|
+
ccteams list --details # Full descriptions and tags
|
|
72
|
+
ccteams list --json # Machine-readable JSON
|
|
73
|
+
ccteams use <team> # Apply a team to the current project
|
|
74
|
+
ccteams use <team> --agent-teams # Apply it AND enable agent-teams mode (optional)
|
|
75
|
+
ccteams current # Show the currently active team
|
|
76
|
+
ccteams --version # Print the version
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
After `ccteams use`, **restart Claude Code** so the team loads (see below).
|
|
80
|
+
|
|
81
|
+
### Claude Code (slash commands — via the plugin)
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
/ccteams:list-teams # List available teams
|
|
85
|
+
/ccteams:use-team <team-name> # Apply a team
|
|
86
|
+
/ccteams:choose-team <natural-language> # Find and apply a team by description ("for backend work", "frontend-focused", etc.)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Available teams
|
|
90
|
+
|
|
91
|
+
ccteams ships with these teams out of the box. Each is a builder + reviewer pair (except `research`, which is a single read-only researcher).
|
|
92
|
+
|
|
93
|
+
| Team | What it's for |
|
|
94
|
+
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `generalist` | Stack-agnostic, end-to-end feature team: scope → design → build → QA → ship. Use when no stack-specific team fits or for general cross-stack work. |
|
|
96
|
+
| `next-ts` | Next.js (App Router) + TypeScript + Tailwind — RSC, Server Actions, type-safe data fetching, accessible UI. |
|
|
97
|
+
| `frontend` | Framework-agnostic UI/UX and accessibility — UI work that isn't Next.js-specific, or focused on a11y/responsive/UX quality. |
|
|
98
|
+
| `go-api` | Go HTTP API backend — idiomatic services with `net/http` and `database/sql`. |
|
|
99
|
+
| `python-fastapi` | Python FastAPI + Pydantic v2 — async HTTP APIs with full type coverage and validation. |
|
|
100
|
+
| `rails` | Ruby on Rails — ActiveRecord, convention-over-configuration, the full Rails stack. |
|
|
101
|
+
| `debug` | Stack-agnostic bug hunting — reproduce → root-cause → minimal fix → regression test. |
|
|
102
|
+
| `research` | Stack-agnostic technical research — compare options and produce a written recommendation. Writes no code. |
|
|
103
|
+
|
|
104
|
+
Run `ccteams list` for the full descriptions and tags, or `/ccteams:choose-team <what you need>` to let Claude pick one for you.
|
|
105
|
+
|
|
106
|
+
## One team per session (and monorepos)
|
|
107
|
+
|
|
108
|
+
ccteams applies **one team per project at a time**. `ccteams use <team>` is an exclusive switch: applying a new team cleanly replaces the previous one.
|
|
109
|
+
|
|
110
|
+
This is partly a Claude Code constraint: subagents in `.claude/agents/` are **global to the project** and cannot be scoped to a subdirectory. You can't, for example, have the `next-ts` team active only in `apps/web/` and `go-api` only in `apps/api/` at the same time with isolation.
|
|
111
|
+
|
|
112
|
+
**Monorepo workaround:** pick the team that matches the area you're actively working on. Claude Code loads `CLAUDE.md` files along the path to the files you're editing, so launching `claude` from the subdirectory you're working in gives you that subtree's `CLAUDE.md` context — but the applied team's agents themselves remain available repo-wide.
|
|
113
|
+
|
|
114
|
+
## IMPORTANT: Session restart required
|
|
115
|
+
|
|
116
|
+
After running `ccteams use`, `/ccteams:use-team`, or `/ccteams:choose-team`, **you must restart Claude Code** for the new agent team to load. The agents are instantiated at session start, not mid-session.
|
|
117
|
+
|
|
118
|
+
**To restart:** type `/exit` (or close Claude Code) and start a new session.
|
|
119
|
+
|
|
120
|
+
## How teams are applied to your project
|
|
121
|
+
|
|
122
|
+
When you apply a team with `ccteams use <team>` or `/ccteams:use-team <team>`:
|
|
123
|
+
|
|
124
|
+
1. The team's agent definitions are copied into `.claude/agents/`.
|
|
125
|
+
2. A `.claude/active-team.md` file is created, documenting the active team and its purpose.
|
|
126
|
+
3. Your project's `.claude/CLAUDE.md` is updated with an import statement (`@.claude/active-team.md`) to include the team's orchestration rules.
|
|
127
|
+
4. A `.claude/.ccteams-manifest.json` is written to track which team is active and allow clean switching.
|
|
128
|
+
5. If you pass `--agent-teams` (or the team opts in via `"requiresAgentTeams": true`), `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` is set in `.claude/settings.json`. This is optional — without it, the team runs in the normal orchestrated mode.
|
|
129
|
+
|
|
130
|
+
ccteams includes a **collision guard**: it will refuse to apply a team if any of its agents share a name with agents you've written by hand in `.claude/agents/`. This prevents accidental overwrites.
|
|
131
|
+
|
|
132
|
+
## Committing `.claude/` — your choice
|
|
133
|
+
|
|
134
|
+
You have two options:
|
|
135
|
+
|
|
136
|
+
**Option A (shared teams):** Commit `.claude/agents/`, `.claude/active-team.md`, and `.claude/.ccteams-manifest.json` to git. Teammates pulling the repo will automatically have the same team active.
|
|
137
|
+
|
|
138
|
+
**Option B (local teams):** Add `.claude/agents/`, `.claude/active-team.md`, and `.claude/.ccteams-manifest.json` to `.gitignore`. Each developer can run `ccteams use` locally to activate their preferred team.
|
|
139
|
+
|
|
140
|
+
**Recommendation:** If your project benefits from consistent team composition (e.g., a shared code style or mandatory QA agents), commit the team. Otherwise, keep it local.
|
|
141
|
+
|
|
142
|
+
## Contributing a team
|
|
143
|
+
|
|
144
|
+
ccteams applies the teams bundled in this repo's `teams/` directory. To add a new team,
|
|
145
|
+
contribute it here (open a PR) — there's no separate user-local team registry. A team lives
|
|
146
|
+
in `teams/<name>/`:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
teams/<name>/
|
|
150
|
+
├── team.json # Metadata: name, description, tags, optional flags
|
|
151
|
+
├── orchestration.md # The CLAUDE.md rules to import (defines roles, goals, behavior)
|
|
152
|
+
└── agents/
|
|
153
|
+
├── agent1.md # YAML frontmatter + agent system prompt
|
|
154
|
+
├── agent2.md
|
|
155
|
+
└── ...
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `team.json` schema
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"name": "my-team",
|
|
163
|
+
"description": "A short pitch of what this team does",
|
|
164
|
+
"tags": ["backend", "api", "performance"],
|
|
165
|
+
"requiresAgentTeams": false
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Set `"requiresAgentTeams": true` if your team uses agent-to-agent messaging or collaborative member features.
|
|
170
|
+
|
|
171
|
+
### Agent files (`.md`)
|
|
172
|
+
|
|
173
|
+
Each agent file is a standard Claude Code subagent: YAML frontmatter (`name`, `description`, and optional `tools`) followed by its system prompt:
|
|
174
|
+
|
|
175
|
+
```markdown
|
|
176
|
+
---
|
|
177
|
+
name: my-agent
|
|
178
|
+
description: Backend API specialist. Use for building and reviewing REST/GraphQL endpoints, data layers, and integrations.
|
|
179
|
+
tools: Read, Write, Edit, Bash, Glob, Grep
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
You are a Python backend expert. Your job is to...
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The `description` is what Claude uses to decide when to delegate to this agent, so make it specific. Omit `tools` to inherit all available tools.
|
|
186
|
+
|
|
187
|
+
For examples to copy from, see `teams/next-ts/` (a stack-specific team) and `teams/debug/` (a stack-agnostic team). `next-ts/` is the cleanest reference for the builder + reviewer shape.
|
|
188
|
+
|
|
189
|
+
### Orchestrated vs. collaborative teams
|
|
190
|
+
|
|
191
|
+
All teams that ship today are **orchestrated**: one lead delegates to specialized subagents that report back independently. This is the simple, predictable default.
|
|
192
|
+
|
|
193
|
+
ccteams also supports **collaborative** teams — where subagents message each other directly — via Claude Code's experimental agent-teams feature (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`). ccteams writes that env key into `.claude/settings.json` for you in two cases:
|
|
194
|
+
|
|
195
|
+
- The team declares `"requiresAgentTeams": true` in its `team.json` — agent-teams mode is enabled automatically whenever you apply it.
|
|
196
|
+
- You pass the `--agent-teams` flag to `ccteams use`, which opts any team into agent-teams mode for that project:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
ccteams use <team> --agent-teams
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The flag is position-agnostic, so `ccteams use --agent-teams <team>` works too.
|
|
203
|
+
|
|
204
|
+
When ccteams added the env key (either way), it removes it again the next time you switch to a team that doesn't need it. No collaborative team ships by default, but the format supports authoring one.
|
|
205
|
+
|
|
206
|
+
## Development / local testing
|
|
207
|
+
|
|
208
|
+
### Test the plugin locally (session-only)
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
claude --plugin-dir ./plugins/ccteams
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
This loads the plugin for the current session only — no permanent install. Useful for development.
|
|
215
|
+
|
|
216
|
+
### Test the CLI locally
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npm install -g .
|
|
220
|
+
ccteams list
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Installs the CLI from the repo's current source.
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT © toffyui. See [LICENSE](./LICENSE) for the full text.
|
package/bin/ccteams.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ccteams — Claude Code agent-team package manager
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* list [--json] List available teams
|
|
7
|
+
* use <team> Apply a team to the current project
|
|
8
|
+
* current Show the currently-applied team
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { listTeams } from '../lib/teams.js';
|
|
15
|
+
import { readManifest } from '../lib/manifest.js';
|
|
16
|
+
import { useTeam } from '../lib/use.js';
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
const command = args[0];
|
|
20
|
+
|
|
21
|
+
// Read the version from package.json (single source of truth), resolved relative
|
|
22
|
+
// to this file so it works regardless of where ccteams is installed.
|
|
23
|
+
function getVersion() {
|
|
24
|
+
try {
|
|
25
|
+
const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
26
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version ?? 'unknown';
|
|
27
|
+
} catch {
|
|
28
|
+
return 'unknown';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── version ───────────────────────────────────────────────────────────────────
|
|
33
|
+
if (command === '--version' || command === '-V' || command === 'version') {
|
|
34
|
+
console.log(`ccteams ${getVersion()}`);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ── list ────────────────────────────────────────────────────────────────────
|
|
39
|
+
if (command === 'list') {
|
|
40
|
+
const teams = listTeams();
|
|
41
|
+
const jsonFlag = args.includes('--json');
|
|
42
|
+
|
|
43
|
+
if (jsonFlag) {
|
|
44
|
+
// Print ONLY valid JSON — no decorative text, nothing else.
|
|
45
|
+
const output = teams.map(({ name, summary, description, tags, requiresAgentTeams }) => ({
|
|
46
|
+
name,
|
|
47
|
+
summary,
|
|
48
|
+
description,
|
|
49
|
+
tags,
|
|
50
|
+
requiresAgentTeams,
|
|
51
|
+
}));
|
|
52
|
+
process.stdout.write(JSON.stringify(output, null, 2) + '\n');
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (teams.length === 0) {
|
|
57
|
+
console.log('No teams found.');
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Color follows the NO_COLOR / FORCE_COLOR conventions, then falls back to TTY
|
|
62
|
+
// detection (so piped/captured output stays plain).
|
|
63
|
+
const color = !process.env.NO_COLOR && (process.env.FORCE_COLOR ? true : !!process.stdout.isTTY);
|
|
64
|
+
const bold = (s) => (color ? `\x1b[1;36m${s}\x1b[0m` : s); // bold cyan (team names)
|
|
65
|
+
const dim = (s) => (color ? `\x1b[2m${s}\x1b[0m` : s); // dim (secondary text)
|
|
66
|
+
|
|
67
|
+
const details = args.includes('--details') || args.includes('-d');
|
|
68
|
+
|
|
69
|
+
if (details) {
|
|
70
|
+
// Full view: name + complete description + tags, one block per team.
|
|
71
|
+
console.log('Available teams:\n');
|
|
72
|
+
for (const t of teams) {
|
|
73
|
+
console.log(` ${bold(t.name)}`);
|
|
74
|
+
console.log(` ${t.description}`);
|
|
75
|
+
if (t.tags.length > 0) {
|
|
76
|
+
console.log(` ${dim('tags:')} ${dim(t.tags.join(', '))}`);
|
|
77
|
+
}
|
|
78
|
+
console.log();
|
|
79
|
+
}
|
|
80
|
+
console.log(dim(' Apply: ccteams use <team> Optional: add --agent-teams to run members in parallel.'));
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Compact view (default): name (bold cyan) + a short one-line summary. One row
|
|
85
|
+
// per team, no wrapping, so the list stays scannable. `--details` shows the full
|
|
86
|
+
// descriptions and tags.
|
|
87
|
+
const nameWidth = Math.max(...teams.map((t) => t.name.length));
|
|
88
|
+
|
|
89
|
+
console.log('Available teams:\n');
|
|
90
|
+
for (const t of teams) {
|
|
91
|
+
console.log(` ${bold(t.name.padEnd(nameWidth))} ${t.summary}`);
|
|
92
|
+
}
|
|
93
|
+
console.log(dim('\n Apply: ccteams use <team> Full details: ccteams list --details'));
|
|
94
|
+
console.log(dim(' Optional: add --agent-teams to run a team’s members in parallel.'));
|
|
95
|
+
process.exit(0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── current ──────────────────────────────────────────────────────────────────
|
|
99
|
+
if (command === 'current') {
|
|
100
|
+
const manifest = readManifest(process.cwd());
|
|
101
|
+
if (!manifest?.appliedTeam) {
|
|
102
|
+
console.log('No team currently applied. Run: ccteams use <team>');
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
console.log(`Current team: ${manifest.appliedTeam}`);
|
|
106
|
+
console.log(`Applied at : ${manifest.appliedAt}`);
|
|
107
|
+
console.log(`Files placed: ${manifest.placedFiles?.length ?? 0}`);
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── use ──────────────────────────────────────────────────────────────────────
|
|
112
|
+
if (command === 'use') {
|
|
113
|
+
// Parse position-agnostic: strip --agent-teams from the args list, whatever
|
|
114
|
+
// position it appears in, then take the first remaining non-flag word as the
|
|
115
|
+
// team name.
|
|
116
|
+
const agentTeamsFlag = args.includes('--agent-teams');
|
|
117
|
+
const useArgs = args.slice(1).filter((a) => a !== '--agent-teams');
|
|
118
|
+
const teamName = useArgs[0];
|
|
119
|
+
|
|
120
|
+
if (!teamName) {
|
|
121
|
+
console.error('Usage: ccteams use [--agent-teams] <team-name>');
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const result = useTeam(teamName, process.cwd(), { agentTeams: agentTeamsFlag });
|
|
126
|
+
if (!result.success) {
|
|
127
|
+
console.error(`Error: ${result.message}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
console.log(result.message);
|
|
131
|
+
process.exit(0);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── no args / unknown command ────────────────────────────────────────────────
|
|
135
|
+
const usageText = `
|
|
136
|
+
ccteams — Claude Code agent-team package manager
|
|
137
|
+
|
|
138
|
+
Usage:
|
|
139
|
+
ccteams list List all available teams (compact)
|
|
140
|
+
ccteams list --details List teams with full descriptions and tags
|
|
141
|
+
ccteams list --json Machine-readable JSON list (for scripts/slash commands)
|
|
142
|
+
ccteams use <team> Apply a team to the current project
|
|
143
|
+
ccteams use <team> --agent-teams Apply a team AND enable agent-teams mode
|
|
144
|
+
ccteams current Show the currently-applied team
|
|
145
|
+
ccteams --version Print the ccteams version
|
|
146
|
+
|
|
147
|
+
Flags:
|
|
148
|
+
--agent-teams Enable CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 in .claude/settings.json
|
|
149
|
+
for the applied team. Position-agnostic: works before or after <team>.
|
|
150
|
+
Teams that declare "requiresAgentTeams" set this automatically.
|
|
151
|
+
|
|
152
|
+
Agent teams mode:
|
|
153
|
+
By default a team is orchestrated: one lead delegates to members one at a time,
|
|
154
|
+
and they report back. With --agent-teams, members become parallel teammates that
|
|
155
|
+
run at once and message each other directly — better for big, splittable work.
|
|
156
|
+
It uses Claude Code's experimental agent-teams feature and takes effect after you
|
|
157
|
+
restart the session.
|
|
158
|
+
|
|
159
|
+
Examples:
|
|
160
|
+
ccteams list
|
|
161
|
+
ccteams use frontend
|
|
162
|
+
ccteams use go-api --agent-teams
|
|
163
|
+
ccteams use --agent-teams rails
|
|
164
|
+
ccteams current
|
|
165
|
+
`.trimStart();
|
|
166
|
+
|
|
167
|
+
if (command === undefined || command === '--help' || command === '-h') {
|
|
168
|
+
console.log(usageText);
|
|
169
|
+
process.exit(0);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.error(`Unknown command: "${command}"\n`);
|
|
173
|
+
console.error(usageText);
|
|
174
|
+
process.exit(1);
|
package/lib/manifest.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* manifest.js — read/write .claude/.ccteams-manifest.json in the user's project.
|
|
3
|
+
*
|
|
4
|
+
* The manifest tracks what ccteams placed so we can clean up on team switches
|
|
5
|
+
* without touching hand-written agent files.
|
|
6
|
+
*
|
|
7
|
+
* Schema:
|
|
8
|
+
* {
|
|
9
|
+
* "version": "1", // schema version, bump if format changes
|
|
10
|
+
* "appliedTeam": "<team-name>", // name of the currently-applied team
|
|
11
|
+
* "appliedAt": "<ISO-8601>", // timestamp for diagnostics
|
|
12
|
+
* "placedFiles": ["<abs-path>", ...],// every file ccteams wrote, for clean removal
|
|
13
|
+
* "agentTeamsFlagSet": <boolean> // true if ccteams wrote the experimental env key
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
|
|
20
|
+
const MANIFEST_VERSION = '1';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the manifest path inside the given project root.
|
|
24
|
+
*/
|
|
25
|
+
export function manifestPath(projectRoot) {
|
|
26
|
+
return path.join(projectRoot, '.claude', '.ccteams-manifest.json');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Read the manifest from disk. Returns null if it does not exist or is invalid.
|
|
31
|
+
*/
|
|
32
|
+
export function readManifest(projectRoot) {
|
|
33
|
+
const mPath = manifestPath(projectRoot);
|
|
34
|
+
if (!fs.existsSync(mPath)) return null;
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(fs.readFileSync(mPath, 'utf8'));
|
|
37
|
+
} catch {
|
|
38
|
+
// Corrupted manifest — treat as absent so we start fresh.
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Write a new manifest to disk.
|
|
45
|
+
* agentTeamsFlagSet tracks whether ccteams injected the experimental env key so
|
|
46
|
+
* we can remove it on switch without clobbering a user's pre-existing value.
|
|
47
|
+
*/
|
|
48
|
+
export function writeManifest(projectRoot, { appliedTeam, placedFiles, agentTeamsFlagSet = false }) {
|
|
49
|
+
const mPath = manifestPath(projectRoot);
|
|
50
|
+
const data = {
|
|
51
|
+
version: MANIFEST_VERSION,
|
|
52
|
+
appliedTeam,
|
|
53
|
+
appliedAt: new Date().toISOString(),
|
|
54
|
+
placedFiles,
|
|
55
|
+
agentTeamsFlagSet,
|
|
56
|
+
};
|
|
57
|
+
fs.mkdirSync(path.dirname(mPath), { recursive: true });
|
|
58
|
+
fs.writeFileSync(mPath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
59
|
+
}
|
package/lib/teams.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* teams.js — discover and read team packages from the ccteams source repo.
|
|
3
|
+
*
|
|
4
|
+
* Teams live at <ccteams-repo-root>/teams/<team-name>/ and are resolved relative
|
|
5
|
+
* to this file's location (import.meta.url), so the path works regardless of
|
|
6
|
+
* where the user runs ccteams from (process.cwd is the user's project, not ours).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
// Resolve the ccteams source root from this file's location.
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
const TEAMS_DIR = path.resolve(__dirname, '..', 'teams');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Return an array of all available team descriptors.
|
|
20
|
+
* Each element: { name, description, tags, teamDir }
|
|
21
|
+
*/
|
|
22
|
+
export function listTeams() {
|
|
23
|
+
if (!fs.existsSync(TEAMS_DIR)) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const entries = fs.readdirSync(TEAMS_DIR, { withFileTypes: true });
|
|
28
|
+
const teams = [];
|
|
29
|
+
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (!entry.isDirectory()) continue;
|
|
32
|
+
const teamDir = path.join(TEAMS_DIR, entry.name);
|
|
33
|
+
const jsonPath = path.join(teamDir, 'team.json');
|
|
34
|
+
if (!fs.existsSync(jsonPath)) continue;
|
|
35
|
+
|
|
36
|
+
let meta;
|
|
37
|
+
try {
|
|
38
|
+
meta = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
|
39
|
+
} catch {
|
|
40
|
+
// Skip malformed team packages silently — user-visible error would be noise.
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
teams.push({
|
|
45
|
+
name: meta.name ?? entry.name,
|
|
46
|
+
// Short one-line label for `list`; falls back to the description.
|
|
47
|
+
summary: meta.summary ?? meta.description ?? '',
|
|
48
|
+
description: meta.description ?? '',
|
|
49
|
+
tags: meta.tags ?? [],
|
|
50
|
+
// Optional: signals that CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS is required.
|
|
51
|
+
requiresAgentTeams: meta.requiresAgentTeams === true,
|
|
52
|
+
teamDir,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return teams;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Return a single team descriptor by name, or null if not found.
|
|
61
|
+
*/
|
|
62
|
+
export function findTeam(name) {
|
|
63
|
+
return listTeams().find((t) => t.name === name) ?? null;
|
|
64
|
+
}
|