How to create project type plugins, register custom gates, and use the template system.
set-core is extensible through project type plugins. A plugin provides framework-specific behavior -- test detection, forbidden patterns, build commands, verification rules -- without modifying the core engine.
A project type is a Python class inheriting from CoreProfile:
from set_orch.profile_loader import CoreProfile
from set_orch.profile_types import VerificationRule, OrchestrationDirective
class FintechProjectType(CoreProfile):
"""Project type for fintech applications."""
def detect_test_command(self, project_path: str) -> str:
if (Path(project_path) / "jest.config.ts").exists():
return "pnpm test"
return ""
def detect_e2e_command(self, project_path: str) -> str:
if (Path(project_path) / "playwright.config.ts").exists():
return "pnpm test:e2e"
return ""
def get_forbidden_patterns(self) -> list[dict]:
patterns = super().get_forbidden_patterns()
patterns.extend([
{"pattern": r"customer_id.*=.*params\.", "message": "IDOR: use session user ID"},
{"pattern": r"eval\(", "message": "No eval() in financial code"},
])
return patterns
def get_verification_rules(self) -> list[VerificationRule]:
rules = super().get_verification_rules()
rules.append(VerificationRule(
name="pci-no-card-numbers",
description="No credit card numbers in source",
pattern=r"\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b",
severity="critical",
))
return rules
def post_merge_install(self, project_path: str) -> list[str]:
return ["pnpm install", "pnpm db:generate"]In your plugin's pyproject.toml:
[project]
name = "set-project-fintech"
version = "0.1.0"
[project.entry-points."set_core.project_types"]
fintech = "my_plugin.project_type:FintechProjectType"Install with pip install -e . and set project_type: fintech in .claude/project-type.yaml.
Place the module under modules/<name>/ with its own pyproject.toml:
modules/fintech/
├── pyproject.toml
├── set_project_fintech/
│ ├── __init__.py
│ └── project_type.py
└── templates/
└── fintech/
└── rules/
└── pci-compliance.md
Install with pip install -e modules/fintech.
- Entry points -- external plugins installed via pip (highest)
- Direct import -- explicit class path in config
- Built-in modules --
modules/directory - NullProfile -- fallback (no-op, no gates)
Gates are driven by the profile. The engine calls profile methods during the merge pipeline:
execute_merge_queue()
for each change:
1. profile.post_merge_install() -> dep install gate
2. config.build_command -> build gate
3. config.test_command -> test gate
4. profile.detect_e2e_command() -> e2e gate
5. code review (if review_model) -> review gate
6. git merge --ff-only -> merge
7. profile.post_merge_install() -> post-merge cleanup
To add a custom gate, extend the appropriate profile method:
def get_verification_rules(self) -> list[VerificationRule]:
rules = super().get_verification_rules()
rules.append(VerificationRule(
name="security-scan",
description="Run security scanner before merge",
command="pnpm audit --production",
severity="critical",
))
return rulesPlugins can ship template files deployed to consumer projects via set-project init.
| Source | Deployed To | Priority |
|---|---|---|
templates/core/rules/ |
.claude/rules/ |
1 (lowest) |
modules/<type>/templates/<template>/ |
Various paths | 2 |
<project>/.claude/project-templates/ |
Various paths | 3 (highest) |
rules/my-rule.md->.claude/rules/my-rule.mdsrc/lib/prisma.ts->src/lib/prisma.ts(project root)framework-rules/web/custom.md->.claude/rules/web/set-custom.md
Projects can override any template by placing files in .claude/project-templates/:
my-project/.claude/project-templates/
├── rules/
│ └── pci-compliance.md # project-specific rule
└── src/lib/prisma.ts # custom Prisma client
On set-project init, these override module defaults:
[project-template] Overwritten: src/lib/prisma.ts
| Use Case | Location |
|---|---|
| All projects of a type | Module template (modules/<type>/templates/) |
| Your project only | .claude/project-templates/ |
| All projects regardless of type | Core template (templates/core/rules/) |
- Inherit from
CoreProfile, notProjectTypedirectly -- ensures universal rules are included - Keep your own
pyproject.toml-- allows standalonepip install - Module-specific deps in module's pyproject.toml -- not in set-core root
- OpenSpec lives only in set-core root -- modules do not have their own
openspec/ - A single change can touch both
lib/set_orch/andmodules/-- this is expected
| Name | Status | Description |
|---|---|---|
modules/web |
Stable | Next.js, Playwright, Prisma |
modules/example |
Reference | Dungeon Builder demo |
set-project-example |
Published | GitHub reference plugin |
To list a community plugin, submit a PR to this doc.
See also: Architecture · Configuration · CLI Reference