Skip to content

Commit b06022d

Browse files
authored
Merge pull request #355 from Cisco-Talos/dev
Release v1.12.0: update agent skill installation
2 parents a18957d + 3c2c86a commit b06022d

8 files changed

Lines changed: 331 additions & 161 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@ Detailed development history for the EvidenceForge project. Transferred from TOD
66

77
## Unreleased
88

9+
## v1.12.0 (2026-07-10)
10+
11+
This minor release aligns EvidenceForge skill installation with current Claude
12+
Code and ChatGPT project-local and user-wide discovery paths.
13+
14+
**Agent skill installation**
15+
16+
- Changed `eforge install-skills` to install both Claude Code and ChatGPT skills
17+
by default, with explicit `all`, `claude`, and `chatgpt` agent selections and
18+
`codex` retained as a compatibility alias (`1c585619`).
19+
- Added project-local and user-wide ChatGPT destinations under `.agents/skills`,
20+
best-effort multi-agent installation, and warnings for preserved legacy
21+
`.codex/skills` installations (`1c585619`).
22+
- Updated installer documentation and regression coverage for agent selection,
23+
scope handling, compatibility, partial failures, and legacy detection
24+
(`1c585619`).
25+
926
## v1.11.1 (2026-07-08)
1027

1128
This patch release preserves the expanded blind-review experiment results and

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ git clone https://github.com/Cisco-Talos/EvidenceForge.git
3535
cd EvidenceForge
3636
uv sync
3737

38-
# Install agent skills (Claude Code by default)
38+
# Install project-local skills for all supported agents
3939
uv run eforge install-skills
4040

41-
# Or install Codex skills
42-
uv run eforge install-skills --agent codex
41+
# Or select one agent or install user-wide
42+
uv run eforge install-skills --agent chatgpt
43+
uv run eforge install-skills --global
4344

4445
# Create a scenario interactively
4546
# /eforge scenario
@@ -66,7 +67,12 @@ EvidenceForge includes agent skills for interactive, guided workflows. These are
6667
| `/eforge evaluate` | Runs the data quality evaluation, interprets scores, reviews records for realism, and suggests improvements. |
6768
| `/eforge config` | Add, modify, or remove personas, domains, applications, and other configuration data. Handles cross-file dependencies automatically. See [Customizing Configuration](docs/reference/CUSTOMIZING_CONFIG.md). |
6869

69-
Install Claude Code skills with `uv run eforge install-skills` (project scope) or `uv run eforge install-skills --global`. Install Codex skills with `uv run eforge install-skills --agent codex`.
70+
`uv run eforge install-skills` installs project-local skills for both Claude Code
71+
and ChatGPT. Claude commands go under `.claude/commands/`; ChatGPT skills go
72+
under `.agents/skills/`. Add `--global` to install both user-wide under
73+
`~/.claude/commands/` and `~/.agents/skills/`, or select one with `--agent
74+
claude` or `--agent chatgpt`. The legacy name `--agent codex` remains an alias
75+
for `--agent chatgpt` and uses the same destinations.
7076

7177
## CLI Reference
7278

@@ -79,7 +85,7 @@ For scripted or non-interactive use:
7985
| `eforge eval <output_dir> -s <scenario.yaml>` | Evaluate data quality (4 pillars, 20 sub-scores) |
8086
| `eforge info [field]` | Show installation info, config paths, and data inventories. Pass a dot-path field for a specific value (e.g., `eforge info personas`). Use `--fields` to list available fields, `--json` for machine output. |
8187
| `eforge validate-config` | Validate config files for cross-reference integrity. Use `--json` for machine output. |
82-
| `eforge install-skills [--agent claude\|codex] [--global]` | Install agent skills (`--global` is Claude-only) |
88+
| `eforge install-skills [--agent all\|claude\|chatgpt\|codex] [--global]` | Install project-local or user-wide agent skills; defaults to all agents (`codex` aliases `chatgpt`) |
8389
| `eforge version` | Show version |
8490

8591
Useful command flags: `generate` accepts `--verbose` / `--debug` for logging,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
[project]
2424
name = "evidence-forge"
25-
version = "1.11.1"
25+
version = "1.12.0"
2626
description = "Generate realistic synthetic security logs for cybersecurity threat hunting training and research"
2727
readme = "README.md"
2828
license = "MIT"

src/evidenceforge/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@
2727
architecture combining LLM-driven scenario creation with deterministic log generation.
2828
"""
2929

30-
__version__ = "1.11.1"
30+
__version__ = "1.12.0"
3131
__all__ = [] # Will be expanded as modules are implemented

src/evidenceforge/cli/commands.py

Lines changed: 105 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,96 +1013,136 @@ def eval_progress(event_type: str, data: dict) -> None:
10131013
@app.command("install-skills")
10141014
def install_skills_cmd(
10151015
agent: str = typer.Option(
1016-
"claude",
1016+
"all",
10171017
"--agent",
1018-
help="Agent to install skills for: claude or codex",
1018+
help="Agent to install skills for: all, claude, chatgpt, or codex (alias)",
10191019
),
10201020
global_install: bool = typer.Option(
1021-
False, "--global", help="Install to ~/.claude/commands/ (global)"
1021+
False,
1022+
"--global",
1023+
help="Install to each selected agent user directory",
10221024
),
10231025
) -> None:
10241026
"""Install EvidenceForge skills for supported agent workflows.
10251027
1026-
By default, installs Claude Code slash commands to .claude/commands/ in the
1027-
current directory. Use --global with Claude installs to install to
1028-
~/.claude/commands/. Use --agent codex to install Codex skills to
1029-
~/.codex/skills/.
1028+
By default, installs skills for Claude Code and ChatGPT in the current
1029+
project. Use --global to install to each selected agent user directory.
1030+
The codex agent name remains available as an alias for chatgpt.
10301031
10311032
Existing installations are updated: new files are copied, changed files
10321033
are overwritten, and stale files from previous versions are removed.
10331034
"""
1034-
from evidenceforge.cli.install_skills import install_codex_skills, install_skills
1035-
1036-
normalized_agent = agent.lower()
1037-
if normalized_agent not in {"claude", "codex"}:
1038-
console.print(
1039-
f"[bold red]Error:[/bold red] Unknown agent '{agent}'. Use 'claude' or 'codex'.",
1040-
style="red",
1041-
)
1042-
raise typer.Exit(EXIT_INPUT_ERROR)
1035+
from evidenceforge.cli.install_skills import (
1036+
find_evidenceforge_chatgpt_skills,
1037+
install_chatgpt_skills,
1038+
install_skills,
1039+
)
10431040

1044-
if normalized_agent == "codex" and global_install:
1041+
requested_agent = agent.lower()
1042+
valid_agents = {"all", "claude", "chatgpt", "codex"}
1043+
if requested_agent not in valid_agents:
10451044
console.print(
1046-
"[bold red]Error:[/bold red] --global is only valid for Claude installs. "
1047-
"Codex skills install to ~/.codex/skills/.",
1045+
f"[bold red]Error:[/bold red] Unknown agent {agent!r}. "
1046+
"Use all, claude, chatgpt, or codex.",
10481047
style="red",
10491048
)
10501049
raise typer.Exit(EXIT_INPUT_ERROR)
10511050

1052-
if normalized_agent == "codex":
1053-
target_dir = Path.home() / ".codex" / "skills"
1054-
scope = "user"
1055-
install_func = install_codex_skills
1056-
elif global_install:
1057-
target_dir = Path.home() / ".claude" / "commands"
1058-
scope = "global"
1059-
install_func = install_skills
1051+
if requested_agent == "all":
1052+
selected_agents = ("claude", "chatgpt")
1053+
elif requested_agent == "codex":
1054+
selected_agents = ("chatgpt",)
10601055
else:
1061-
target_dir = Path.cwd() / ".claude" / "commands"
1062-
scope = "project"
1063-
install_func = install_skills
1056+
selected_agents = (requested_agent,)
10641057

1065-
console.print(
1066-
f"[bold blue]Installing EvidenceForge skills for {normalized_agent} ({scope})[/bold blue]"
1067-
)
1068-
console.print(f"Target: {target_dir}\n")
1058+
scope = "global" if global_install else "project"
1059+
failures: list[tuple[str, str]] = []
1060+
successful_agents: set[str] = set()
10691061

1070-
try:
1071-
installed, removed = install_func(target_dir)
1072-
except FileNotFoundError as e:
1073-
console.print(f"[bold red]Error:[/bold red] {e}", style="red")
1074-
raise typer.Exit(EXIT_INPUT_ERROR)
1075-
except PermissionError as e:
1076-
console.print(f"[bold red]Error:[/bold red] {e}", style="red")
1077-
raise typer.Exit(EXIT_INPUT_ERROR)
1062+
for index, normalized_agent in enumerate(selected_agents):
1063+
if index:
1064+
console.print()
10781065

1079-
if installed:
1080-
console.print(f"[green]✓[/green] Installed {len(installed)} files:")
1081-
for f in installed:
1082-
if normalized_agent == "claude":
1083-
console.print(f" eforge/{f}")
1084-
else:
1085-
console.print(f" {f}")
1066+
if normalized_agent == "claude":
1067+
target_dir = (
1068+
Path.home() / ".claude" / "commands"
1069+
if global_install
1070+
else Path.cwd() / ".claude" / "commands"
1071+
)
1072+
else:
1073+
target_dir = (
1074+
Path.home() / ".agents" / "skills"
1075+
if global_install
1076+
else Path.cwd() / ".agents" / "skills"
1077+
)
10861078

1087-
if removed:
1088-
console.print(f"\n[yellow]Removed {len(removed)} stale files:[/yellow]")
1089-
for f in removed:
1079+
console.print(
1080+
f"[bold blue]Installing EvidenceForge skills for "
1081+
f"{normalized_agent} ({scope})[/bold blue]"
1082+
)
1083+
console.print(f"Target: {target_dir}\n")
1084+
1085+
try:
10901086
if normalized_agent == "claude":
1091-
console.print(f" eforge/{f}", style="dim")
1087+
installed, removed = install_skills(target_dir)
10921088
else:
1093-
console.print(f" {f}", style="dim")
1089+
installed, removed = install_chatgpt_skills(target_dir)
1090+
except (FileNotFoundError, PermissionError) as error:
1091+
console.print(f"[bold red]Error:[/bold red] {error}", style="red")
1092+
failures.append((normalized_agent, str(error)))
1093+
continue
10941094

1095-
if normalized_agent == "claude":
1096-
console.print(f"\n[bold green]✓ Skills installed to {target_dir / 'eforge'}[/bold green]")
1097-
console.print(
1098-
"Use /eforge scenario, /eforge generate, /eforge validate, /eforge evaluate, or /eforge config."
1099-
)
1100-
else:
1101-
console.print(f"\n[bold green]✓ Skills installed to {target_dir}[/bold green]")
1102-
console.print(
1103-
"Use the eforge-scenario, eforge-generate, eforge-validate, "
1104-
"eforge-evaluate, or eforge-config skills."
1105-
)
1095+
successful_agents.add(normalized_agent)
1096+
if installed:
1097+
console.print(f"[green]✓[/green] Installed {len(installed)} files:")
1098+
for installed_file in installed:
1099+
if normalized_agent == "claude":
1100+
console.print(f" eforge/{installed_file}")
1101+
else:
1102+
console.print(f" {installed_file}")
1103+
1104+
if removed:
1105+
console.print(f"\n[yellow]Removed {len(removed)} stale files:[/yellow]")
1106+
for removed_file in removed:
1107+
if normalized_agent == "claude":
1108+
console.print(f" eforge/{removed_file}", style="dim")
1109+
else:
1110+
console.print(f" {removed_file}", style="dim")
1111+
1112+
if normalized_agent == "claude":
1113+
installed_dir = target_dir / "eforge"
1114+
console.print(f"\n[bold green]✓ Skills installed to {installed_dir}[/bold green]")
1115+
console.print(
1116+
"Use /eforge scenario, /eforge generate, /eforge validate, "
1117+
"/eforge evaluate, or /eforge config."
1118+
)
1119+
else:
1120+
console.print(f"\n[bold green]✓ Skills installed to {target_dir}[/bold green]")
1121+
console.print(
1122+
"Use the eforge-scenario, eforge-generate, eforge-validate, "
1123+
"eforge-evaluate, or eforge-config skills."
1124+
)
1125+
1126+
if global_install and "chatgpt" in successful_agents:
1127+
legacy_dir = Path.home() / ".codex" / "skills"
1128+
legacy_skills = find_evidenceforge_chatgpt_skills(legacy_dir)
1129+
if legacy_skills:
1130+
console.print(
1131+
"\n[bold yellow]Warning:[/bold yellow] Legacy EvidenceForge skills "
1132+
f"also exist under {legacy_dir} and may appear as duplicates:"
1133+
)
1134+
for legacy_skill in legacy_skills:
1135+
console.print(f" {legacy_skill}", style="dim")
1136+
console.print(
1137+
"These legacy files were not modified. Remove them manually after "
1138+
"confirming the new installation works."
1139+
)
1140+
1141+
if failures:
1142+
console.print("\n[bold red]Skill installation completed with errors:[/bold red]")
1143+
for failed_agent, message in failures:
1144+
console.print(f" {failed_agent}: {message}", style="red")
1145+
raise typer.Exit(EXIT_INPUT_ERROR)
11061146

11071147

11081148
@app.command()

0 commit comments

Comments
 (0)