teamcast 1.0.2 → 1.0.3
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
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# TeamCast
|
|
2
2
|
|
|
3
|
+
[](https://github.com/Katoshy/teamcast/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/teamcast)
|
|
5
|
+
[](https://github.com/Katoshy/teamcast/blob/main/LICENSE)
|
|
6
|
+
|
|
3
7
|
CLI to design, generate, and validate multi-target agent teams for Claude Code and Codex from a single manifest.
|
|
4
8
|
|
|
5
9
|
Define your agent team in one `teamcast.yaml` file. TeamCast validates the manifest, generates `.claude/` and/or `.codex/` config files, and keeps generated output in sync with the source config.
|
|
6
10
|
|
|
7
11
|
## Install
|
|
8
12
|
|
|
13
|
+
Requires Node.js 24 or newer.
|
|
14
|
+
|
|
9
15
|
```bash
|
|
10
16
|
npm install -g teamcast
|
|
11
17
|
```
|
|
@@ -16,6 +22,14 @@ Or run without installing:
|
|
|
16
22
|
npx teamcast <command>
|
|
17
23
|
```
|
|
18
24
|
|
|
25
|
+
## Community
|
|
26
|
+
|
|
27
|
+
- Contribution guide: [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
28
|
+
- Code of conduct: [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md)
|
|
29
|
+
- Security policy: [SECURITY.md](./SECURITY.md)
|
|
30
|
+
- Bug reports and feature requests: use the GitHub issue templates
|
|
31
|
+
- Pull requests: use the repository PR template and run `npm test` plus `npm run build` before opening one
|
|
32
|
+
|
|
19
33
|
## Quick Start
|
|
20
34
|
|
|
21
35
|
```bash
|
|
@@ -43,6 +57,7 @@ After `generate`, your project will have files for the selected target(s):
|
|
|
43
57
|
- Codex target:
|
|
44
58
|
- `.codex/config.toml` - workspace-level Codex config
|
|
45
59
|
- `.codex/agents/<name>.toml` - one TOML config per agent
|
|
60
|
+
- `.agents/skills/<skill>/SKILL.md` - one stub file per unique skill
|
|
46
61
|
- `AGENTS.md` - team-level instructions for Codex agents
|
|
47
62
|
|
|
48
63
|
Use `teamcast init --target claude|codex|both` to choose which outputs are generated up front.
|
|
@@ -273,7 +288,7 @@ Use `--yes` to skip confirmation.
|
|
|
273
288
|
|
|
274
289
|
### `create skill <name>`
|
|
275
290
|
|
|
276
|
-
`teamcast create skill <name>` registers a new skill name on one agent, writes `teamcast.yaml`, and generates `.claude/skills/<name>/SKILL.md`.
|
|
291
|
+
`teamcast create skill <name>` registers a new skill name on one agent, writes `teamcast.yaml`, and generates a target-specific stub: `.claude/skills/<name>/SKILL.md` for Claude or `.agents/skills/<name>/SKILL.md` for Codex.
|
|
277
292
|
|
|
278
293
|
Rules and prompts:
|
|
279
294
|
|
|
@@ -310,7 +325,7 @@ teamcast generate --dry-run
|
|
|
310
325
|
Behavior:
|
|
311
326
|
|
|
312
327
|
- `generate` overwrites generated agent/docs/settings files
|
|
313
|
-
- skill stub files under `.claude/skills/` are created only if missing
|
|
328
|
+
- skill stub files under `.claude/skills/` and `.agents/skills/` are created only if missing
|
|
314
329
|
- `--dry-run` shows what would be generated without writing anything
|
|
315
330
|
|
|
316
331
|
### `diff`
|
|
@@ -429,6 +444,7 @@ It targets:
|
|
|
429
444
|
|
|
430
445
|
- `.claude/agents`
|
|
431
446
|
- `.claude/skills`
|
|
447
|
+
- generated Codex skill files under `.agents/skills/`
|
|
432
448
|
- `.claude/settings.json`
|
|
433
449
|
- `.claude/settings.local.json`
|
|
434
450
|
- `CLAUDE.md`
|
|
@@ -467,9 +483,9 @@ Everything is defined in `teamcast.yaml` at the root of your project.
|
|
|
467
483
|
|
|
468
484
|
For Claude targets, TeamCast renders `.claude/agents/<name>.md` and `CLAUDE.md` from `claude.agents.<name>`.
|
|
469
485
|
|
|
470
|
-
For Codex targets, TeamCast renders `.codex/agents/<name>.toml` plus `.codex/config.toml` from `codex.agents.<name
|
|
486
|
+
For Codex targets, TeamCast renders `.codex/agents/<name>.toml` plus `.codex/config.toml` from `codex.agents.<name>`, and writes Codex skill docs to `.agents/skills/<skill>/`.
|
|
471
487
|
|
|
472
|
-
`.codex/config.toml` is the workspace-level agent index. Concrete agent runtime config lives in `.codex/agents/<name>.toml`.
|
|
488
|
+
`.codex/config.toml` is the workspace-level agent index. Concrete agent runtime config lives in `.codex/agents/<name>.toml`. Codex skill discovery uses `.agents/skills/`, not `.codex/`.
|
|
473
489
|
|
|
474
490
|
Native Claude Code fields are rendered into frontmatter:
|
|
475
491
|
|
|
@@ -642,6 +658,7 @@ claude:
|
|
|
642
658
|
| `claude.tools` | string[] | Native Claude Code tool allow-list |
|
|
643
659
|
| `claude.disallowed_tools` | string[] | Native Claude Code tool deny-list |
|
|
644
660
|
| `claude.skills` | string[] | Skill names. Each unique skill generates `.claude/skills/<skill>/SKILL.md` |
|
|
661
|
+
| `codex.skills` | string[] | Skill names. Each unique skill generates `.agents/skills/<skill>/SKILL.md` |
|
|
645
662
|
| `claude.max_turns` | number | Maximum agentic turns |
|
|
646
663
|
| `claude.mcp_servers` | object[] | MCP server definitions |
|
|
647
664
|
| `claude.permission_mode` | `default \| acceptEdits \| bypassPermissions \| plan \| dontAsk` | Claude Code permission mode |
|
|
@@ -7,8 +7,11 @@ export function buildGeneratedOutputs(team, targetName, options) {
|
|
|
7
7
|
const renderer = target.renderer;
|
|
8
8
|
const files = renderer.render(spec);
|
|
9
9
|
if (!options.dryRun) {
|
|
10
|
-
const editable =
|
|
11
|
-
const generated =
|
|
10
|
+
const editable = [];
|
|
11
|
+
const generated = [];
|
|
12
|
+
for (const file of files) {
|
|
13
|
+
(isUserEditableGeneratedFile(file.path) ? editable : generated).push(file);
|
|
14
|
+
}
|
|
12
15
|
writeFiles(generated, options.cwd);
|
|
13
16
|
writeFiles(editable, options.cwd, { skipExisting: true });
|
|
14
17
|
}
|
|
@@ -12,7 +12,7 @@ export function registerManageCommands(program) {
|
|
|
12
12
|
const createCmd = program.command('create').description('Create a new resource');
|
|
13
13
|
createCmd
|
|
14
14
|
.command('skill <name>')
|
|
15
|
-
.description('Create a new skill (
|
|
15
|
+
.description('Create a new skill (Claude: .claude/skills/, Codex: .agents/skills/)')
|
|
16
16
|
.option('--target <name>', 'Target block to modify')
|
|
17
17
|
.action(async (name, options) => {
|
|
18
18
|
const { runCreateSkillCommand } = await import('../manage.js');
|
package/dist/cli/reset.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { existsSync, rmSync, readdirSync, statSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
4
|
import { readManifest, ManifestError } from '../manifest/reader.js';
|
|
5
5
|
import { generate } from '../generator/index.js';
|
|
6
6
|
import { printSuccess, printHeader, printCommandSuccess, printDim, } from '../utils/chalk-helpers.js';
|
|
@@ -47,6 +47,22 @@ function deleteEmptyDirIfPossible(cwd, rel) {
|
|
|
47
47
|
printSuccess(`Deleted ${rel}/`);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
function pruneEmptyAncestorDirs(cwd, rel) {
|
|
51
|
+
let current = dirname(rel);
|
|
52
|
+
while (current !== '.' && current !== '') {
|
|
53
|
+
const abs = join(cwd, current);
|
|
54
|
+
if (!existsSync(abs)) {
|
|
55
|
+
current = dirname(current);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (readdirSync(abs).length > 0) {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
rmSync(abs, { recursive: true });
|
|
62
|
+
printSuccess(`Deleted ${current}/`);
|
|
63
|
+
current = dirname(current);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
50
66
|
function deleteFiles(cwd, paths) {
|
|
51
67
|
for (const rel of paths) {
|
|
52
68
|
const abs = join(cwd, rel);
|
|
@@ -60,6 +76,7 @@ function deleteFiles(cwd, paths) {
|
|
|
60
76
|
rmSync(abs);
|
|
61
77
|
}
|
|
62
78
|
printSuccess(`Deleted ${rel}`);
|
|
79
|
+
pruneEmptyAncestorDirs(cwd, rel);
|
|
63
80
|
}
|
|
64
81
|
deleteEmptyDirIfPossible(cwd, '.claude/agents');
|
|
65
82
|
deleteEmptyDirIfPossible(cwd, '.claude/skills');
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
// Codex skill renderer — generates {id}/SKILL.md + agents/openai.yaml per skill.
|
|
1
|
+
// Codex skill renderer — generates .agents/skills/{id}/SKILL.md + agents/openai.yaml per skill.
|
|
2
2
|
import { defaultRegistry } from '../../registry/index.js';
|
|
3
|
+
function getSkillBasePath(skillId) {
|
|
4
|
+
return `.agents/skills/${skillId}`;
|
|
5
|
+
}
|
|
3
6
|
// --- Frontmatter (Codex: name + description only) ---
|
|
4
7
|
function buildFrontmatter(name, description) {
|
|
5
8
|
return ['---', `name: ${name}`, `description: ${description}`, '---'].join('\n');
|
|
@@ -59,15 +62,16 @@ function renderOpenaiYaml(skill) {
|
|
|
59
62
|
if (skill.metadata.version)
|
|
60
63
|
lines.push(`version: "${skill.metadata.version}"`);
|
|
61
64
|
}
|
|
62
|
-
if (hasTools) {
|
|
65
|
+
if (hasTools || hasMcp) {
|
|
63
66
|
lines.push('dependencies:');
|
|
67
|
+
}
|
|
68
|
+
if (hasTools) {
|
|
64
69
|
lines.push(' tools:');
|
|
65
70
|
for (const tool of skill.allowed_tools) {
|
|
66
71
|
lines.push(` - ${tool}`);
|
|
67
72
|
}
|
|
68
73
|
}
|
|
69
74
|
if (hasMcp) {
|
|
70
|
-
lines.push('dependencies:');
|
|
71
75
|
lines.push(' mcp_servers:');
|
|
72
76
|
for (const server of skill.required_mcp_servers) {
|
|
73
77
|
lines.push(` - ${server}`);
|
|
@@ -78,7 +82,7 @@ function renderOpenaiYaml(skill) {
|
|
|
78
82
|
// --- Companion files ---
|
|
79
83
|
function renderCompanionFiles(skillId, skill) {
|
|
80
84
|
const files = [];
|
|
81
|
-
const base = skillId;
|
|
85
|
+
const base = getSkillBasePath(skillId);
|
|
82
86
|
if (skill.reference_files) {
|
|
83
87
|
for (const [name, content] of Object.entries(skill.reference_files)) {
|
|
84
88
|
files.push({ path: `${base}/references/${name}`, content });
|
|
@@ -108,14 +112,14 @@ export function renderCodexSkillMd(team) {
|
|
|
108
112
|
const definition = defaultRegistry.getSkill(skillName);
|
|
109
113
|
if (definition) {
|
|
110
114
|
files.push({
|
|
111
|
-
path: `${skillName}/SKILL.md`,
|
|
115
|
+
path: `${getSkillBasePath(skillName)}/SKILL.md`,
|
|
112
116
|
content: renderSkillContent(definition),
|
|
113
117
|
});
|
|
114
118
|
files.push(...renderCompanionFiles(skillName, definition));
|
|
115
119
|
}
|
|
116
120
|
else {
|
|
117
121
|
files.push({
|
|
118
|
-
path: `${skillName}/SKILL.md`,
|
|
122
|
+
path: `${getSkillBasePath(skillName)}/SKILL.md`,
|
|
119
123
|
content: generateSkillStub(skillName),
|
|
120
124
|
});
|
|
121
125
|
}
|