Skip to content

Commit d8e2c64

Browse files
DavidJBiancoclaude
andcommitted
Move skills/ to commands/, fix install target and username pattern
- Rename skills/eforge/ → commands/eforge/ to match Claude Code's .claude/commands/ convention for custom slash commands - Update install-skills to target .claude/commands/ instead of .claude/skills/ - Update all path references in installer, pyproject.toml force-include, tests, and documentation - Allow dots in usernames (^[a-zA-Z0-9._-]+$) for first.last format 538 tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0b91914 commit d8e2c64

12 files changed

Lines changed: 48 additions & 51 deletions

File tree

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ evidence-forge/
117117
├── config.example.yaml # Example configuration
118118
├── .env.example # Example environment variables
119119
120-
├── skills/
120+
├── commands/
121121
│ └── eforge/ # Claude Code Skills for scenario creation
122122
│ ├── scenario.md # /eforge scenario - guided scenario creation
123123
│ └── generate.md # /eforge generate - generation workflow
@@ -1351,7 +1351,7 @@ def test_state_manager_creates_unique_pids(user_count: int):
13511351

13521352
Claude Code Skills handle the interactive, creative aspects of scenario creation -- work that was originally planned as a built-in conversational CLI.
13531353

1354-
**Location:** `skills/eforge/` directory
1354+
**Location:** `commands/eforge/` directory
13551355

13561356
**Installation:**
13571357
```bash
@@ -1374,7 +1374,7 @@ eforge install-skills --global
13741374

13751375
### Adding a New Skill
13761376

1377-
1. Create `skills/eforge/{name}.md` with the skill prompt
1377+
1. Create `commands/eforge/{name}.md` with the skill prompt
13781378
2. Follow the hybrid interview pattern: structured questions first, then free-form elaboration
13791379
3. Reference the scenario schema from `docs/scenario-reference.md` to ensure output validity
13801380
4. Test interactively by running the skill in Claude Code

TODO.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@
356356

357357
### 3.1 Claude Code Skills + Install Command
358358

359-
- [x] Create `skills/eforge/scenario.md``/eforge scenario` skill
359+
- [x] Create `commands/eforge/scenario.md``/eforge scenario` skill
360360
- [x] Hybrid interview flow: structured questions first, then free-form gap-filling
361361
- [x] Environment, network, personas, attacks, time window, output formats
362362
- [x] References persona library and scenario schema
@@ -365,18 +365,18 @@
365365
- [x] 10-tactic MITRE ATT&CK kill chain template
366366
- [x] Base64/encoded content must be generated via Bash, never fabricated
367367
- [x] Use `/skill-creator` to develop skill prompt content (2 iterations, 30/30 assertions)
368-
- [x] Create `skills/eforge/generate.md``/eforge generate` skill
368+
- [x] Create `commands/eforge/generate.md``/eforge generate` skill
369369
- [x] Runs `eforge generate` on scenario file
370370
- [x] Runs `eforge validate` as pre-flight check
371371
- [x] Monitors output, diagnoses errors
372372
- [x] Suggests fixes for common issues, escalates structural problems to `/eforge scenario`
373373
- [x] Copies ENVIRONMENT.md to output directory alongside GROUND_TRUTH.md
374-
- [x] Create `skills/eforge/validate.md``/eforge validate` skill
374+
- [x] Create `commands/eforge/validate.md``/eforge validate` skill
375375
- [x] Runs `eforge validate` and interprets output
376376
- [x] Fixes simple issues directly, escalates structural problems to `/eforge scenario`
377377
- [x] Add `eforge install-skills` CLI command to `cli/commands.py`
378-
- [x] `--project` flag: copies to `.claude/skills/` (default)
379-
- [x] `--global` flag: copies to `~/.claude/skills/`
378+
- [x] `--project` flag: copies to `.claude/commands/` (default)
379+
- [x] `--global` flag: copies to `~/.claude/commands/`
380380
- [x] Skills bundled as package data via `importlib.resources` + hatch force-include
381381
- [x] Updates existing installations: overwrites changed files, removes stale files
382382
- [x] Bundles skills, personas, and scenario-reference.md
File renamed without changes.
File renamed without changes.
File renamed without changes.

docs/PRD.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ eforge init [--force]
114114
```bash
115115
eforge install-skills [--project | --global]
116116
```
117-
1. Copies skill files from the repo's `skills/eforge/` directory
118-
2. `--project` (default): Installs to `.claude/skills/` in the current project
119-
3. `--global`: Installs to `~/.claude/skills/`
117+
1. Copies skill files from the repo's `commands/eforge/` directory
118+
2. `--project` (default): Installs to `.claude/commands/` in the current project
119+
3. `--global`: Installs to `~/.claude/commands/`
120120
4. Reports which skills were installed and their slash-command triggers
121121

122122
#### Workflow 4: Validate Scenario
@@ -518,8 +518,8 @@ Non-interactive: Simply copies the example config with all options documented.
518518
eforge install-skills [--project | --global]
519519
520520
Options:
521-
--project Install skills to .claude/skills/ in the current project (default)
522-
--global Install skills to ~/.claude/skills/
521+
--project Install skills to .claude/commands/ in the current project (default)
522+
--global Install skills to ~/.claude/commands/
523523
524524
Copies EvidenceForge skill files to the appropriate Claude Code skills location.
525525
Skill files are bundled as package data and loaded via importlib.resources at runtime.
@@ -616,7 +616,7 @@ EvidenceForge uses Claude Code skills as the primary scenario authoring interfac
616616

617617
#### Skill Files
618618

619-
Skills live in `skills/eforge/` in the repository and are installed via `eforge install-skills`.
619+
Skills live in `commands/eforge/` in the repository and are installed via `eforge install-skills`.
620620

621621
**`/eforge scenario`** -- Guided scenario creation skill
622622

@@ -651,13 +651,13 @@ Responsibilities:
651651
```
652652
# Install to current project (default)
653653
eforge install-skills --project
654-
# Creates .claude/skills/eforge-scenario.md
655-
# Creates .claude/skills/eforge-generate.md
654+
# Creates .claude/commands/eforge-scenario.md
655+
# Creates .claude/commands/eforge-generate.md
656656
657657
# Install globally for all projects
658658
eforge install-skills --global
659-
# Creates ~/.claude/skills/eforge-scenario.md
660-
# Creates ~/.claude/skills/eforge-generate.md
659+
# Creates ~/.claude/commands/eforge-scenario.md
660+
# Creates ~/.claude/commands/eforge-generate.md
661661
```
662662

663663
Skills are plain Markdown files and can be version-controlled, customized, or extended by users.
@@ -724,7 +724,7 @@ Skills are plain Markdown files and can be version-controlled, customized, or ex
724724
- pytz for timezone handling
725725

726726
**Skills:**
727-
- Claude Code skills (Markdown files in `skills/eforge/`)
727+
- Claude Code skills (Markdown files in `commands/eforge/`)
728728
- Installed via `eforge install-skills` command
729729
- No runtime dependency on Claude Code for generation (skills are authoring-time only)
730730

@@ -751,7 +751,7 @@ evidenceforge/
751751
+-- config.example.yaml # Example configuration
752752
+-- .env.example # Example environment variables
753753
|
754-
+-- skills/ # Claude Code skills (source, installed via eforge install-skills)
754+
+-- commands/ # Claude Code skills (source, installed via eforge install-skills)
755755
| +-- eforge/
756756
| +-- scenario.md # /eforge scenario skill
757757
| +-- generate.md # /eforge generate skill
@@ -1084,7 +1084,7 @@ Required scenario files:
10841084
- Each with realistic activity patterns, work hours, and risk profiles
10851085

10861086
3. **`eforge install-skills` command**
1087-
- Installs skills, personas, and reference docs to `.claude/skills/` (project) or `~/.claude/skills/` (global)
1087+
- Installs skills, personas, and reference docs to `.claude/commands/` (project) or `~/.claude/commands/` (global)
10881088
- Bundled as package data via `importlib.resources` + hatch force-include
10891089
- Handles updates: overwrites changed files, removes stale files
10901090

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ build-backend = "hatchling.build"
4040
packages = ["src/evidenceforge"]
4141

4242
[tool.hatch.build.targets.wheel.force-include]
43-
"skills/eforge/scenario.md" = "evidenceforge/_data/skills/eforge/scenario.md"
44-
"skills/eforge/generate.md" = "evidenceforge/_data/skills/eforge/generate.md"
45-
"skills/eforge/validate.md" = "evidenceforge/_data/skills/eforge/validate.md"
43+
"commands/eforge/scenario.md" = "evidenceforge/_data/commands/eforge/scenario.md"
44+
"commands/eforge/generate.md" = "evidenceforge/_data/commands/eforge/generate.md"
45+
"commands/eforge/validate.md" = "evidenceforge/_data/commands/eforge/validate.md"
4646
"docs/scenario-reference.md" = "evidenceforge/_data/references/scenario-reference.md"
4747
"personas" = "evidenceforge/_data/personas"
4848
"config.example.yaml" = "evidenceforge/_data/config.example.yaml"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"""Package data for EvidenceForge (skills, personas, reference docs)."""
1+
"""Package data for EvidenceForge (commands, personas, reference docs)."""

src/evidenceforge/cli/commands.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -452,25 +452,25 @@ def validate(
452452
@app.command("install-skills")
453453
def install_skills_cmd(
454454
global_install: bool = typer.Option(
455-
False, "--global", help="Install to ~/.claude/skills/ (global)"
455+
False, "--global", help="Install to ~/.claude/commands/ (global)"
456456
),
457457
) -> None:
458-
"""Install EvidenceForge Claude Code skills.
458+
"""Install EvidenceForge Claude Code skills as custom slash commands.
459459
460460
Copies skill files, persona library, and reference docs to the Claude Code
461-
skills directory. By default installs to .claude/skills/ in the current
462-
directory (project scope). Use --global to install to ~/.claude/skills/.
461+
commands directory. By default installs to .claude/commands/ in the current
462+
directory (project scope). Use --global to install to ~/.claude/commands/.
463463
464464
Existing installations are updated: new files are copied, changed files
465465
are overwritten, and stale files from previous versions are removed.
466466
"""
467467
from evidenceforge.cli.install_skills import install_skills
468468

469469
if global_install:
470-
target_dir = Path.home() / ".claude" / "skills"
470+
target_dir = Path.home() / ".claude" / "commands"
471471
scope = "global"
472472
else:
473-
target_dir = Path.cwd() / ".claude" / "skills"
473+
target_dir = Path.cwd() / ".claude" / "commands"
474474
scope = "project"
475475

476476
console.print(f"[bold blue]Installing EvidenceForge skills ({scope})[/bold blue]")

src/evidenceforge/cli/install_skills.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Install EvidenceForge Claude Code skills to .claude/skills/ directory."""
1+
"""Install EvidenceForge Claude Code commands to .claude/commands/ directory."""
22

33
import importlib.resources
44
import shutil
@@ -34,7 +34,7 @@ def _get_data_root() -> Path:
3434
try:
3535
data_pkg = importlib.resources.files("evidenceforge._data")
3636
# Check that it actually has content (not just the __init__.py)
37-
skills_dir = data_pkg / "skills" / "eforge" / "scenario.md"
37+
skills_dir = data_pkg / "commands" / "eforge" / "scenario.md"
3838
# Traversable.is_file() works for both filesystem and zip paths
3939
if skills_dir.is_file():
4040
return Path(str(data_pkg))
@@ -45,7 +45,7 @@ def _get_data_root() -> Path:
4545
current = Path(__file__).resolve().parent
4646
for _ in range(5):
4747
current = current.parent
48-
if (current / "skills" / "eforge" / "scenario.md").exists():
48+
if (current / "commands" / "eforge" / "scenario.md").exists():
4949
return current
5050

5151
raise FileNotFoundError(
@@ -57,22 +57,19 @@ def _get_data_root() -> Path:
5757
def _collect_source_files(data_root: Path) -> dict[str, Path]:
5858
"""Build a mapping of relative target paths to source file paths.
5959
60-
Handles both installed layout (_data/skills/eforge/, _data/personas/, _data/references/)
61-
and development layout (skills/eforge/, personas/, docs/).
60+
Handles both installed layout (_data/commands/eforge/, _data/personas/, _data/references/)
61+
and development layout (commands/eforge/, personas/, docs/).
6262
6363
Returns:
6464
Dict mapping relative path within eforge/ -> absolute source path.
6565
"""
6666
manifest: dict[str, Path] = {}
6767

68-
# Detect layout: installed has _data/skills/eforge/, dev has skills/eforge/
69-
installed_skills = data_root / "skills" / "eforge"
70-
dev_skills = data_root / "skills" / "eforge"
71-
72-
if (data_root / "skills" / "eforge" / "scenario.md").exists():
73-
skills_dir = data_root / "skills" / "eforge"
68+
# Detect layout: installed has _data/commands/eforge/, dev has commands/eforge/
69+
if (data_root / "commands" / "eforge" / "scenario.md").exists():
70+
skills_dir = data_root / "commands" / "eforge"
7471
else:
75-
raise FileNotFoundError(f"Skills not found under {data_root}")
72+
raise FileNotFoundError(f"Command files not found under {data_root}")
7673

7774
# Skill markdown files
7875
for skill_file in SKILL_FILES:
@@ -132,7 +129,7 @@ def install_skills(target_dir: Path) -> tuple[list[str], list[str]]:
132129
Overwrites existing files and removes stale files from previous installs.
133130
134131
Args:
135-
target_dir: Parent directory (e.g., .claude/skills/)
132+
target_dir: Parent directory (e.g., .claude/commands/)
136133
137134
Returns:
138135
Tuple of (installed_files, removed_files) as relative path lists.

0 commit comments

Comments
 (0)