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
+ [![CI](https://github.com/Katoshy/teamcast/actions/workflows/ci.yml/badge.svg)](https://github.com/Katoshy/teamcast/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/teamcast)](https://www.npmjs.com/package/teamcast)
5
+ [![License](https://img.shields.io/github/license/Katoshy/teamcast)](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 = files.filter((file) => isUserEditableGeneratedFile(file.path));
11
- const generated = files.filter((file) => !isUserEditableGeneratedFile(file.path));
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 (generates stub file in .claude/skills/)')
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,3 +1,3 @@
1
1
  export function isUserEditableGeneratedFile(path) {
2
- return path.startsWith('.claude/skills/');
2
+ return path.startsWith('.claude/skills/') || path.startsWith('.agents/skills/');
3
3
  }
@@ -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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamcast",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "YAML-driven CLI to design, validate, and generate multi-target agent teams for Claude Code and Codex",
5
5
  "type": "module",
6
6
  "bin": {