feat(install): add OpenCode target + InternalAllTheThings knowledge base

- install_opencode: deploys 29 personas as agents + 1011 skills to
  ~/.config/opencode/{agents,skills}/. Uses OpenCode's markdown+YAML
  agent format (mode/color/permission) and SKILL.md format.
- Topic filter with sensible defaults (drops marketing/biz ~514 skills).
  CLI: --opencode-topics security-offensive,coding-backend,...
- Clone of swisskyrepo/InternalAllTheThings (168 MD, 1.7MB) added to
  _shared/ as a reference trove for AD attack paths, ADCS ESC1-15,
  Kerberos delegation, NTLM relay/coerce, lateral movement, persistence.
- NEO redteam + VORTEX cloud-ad personas reference the new KB with
  MITRE ATT&CK TTP mapping pointers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
salvacybersec
2026-04-16 03:11:44 +03:00
parent 309e389c65
commit 448d1cdcd9
179 changed files with 26140 additions and 1 deletions

340
build.py
View File

@@ -1226,6 +1226,320 @@ def install_antigravity(output_dir: Path):
return count
OPENCODE_TOPICS = {
"security-offensive",
"security-defensive",
"security-cloud",
"security-specialized",
"security-iam",
"security-network",
"security-general",
"ai-llm-dev",
"coding-backend",
"coding-frontend",
"coding-tools",
"cloud-infra",
"database",
"browser-scrape",
"ops-sysadmin",
"osint-intel",
"marketing-content",
"business-pm",
"uncategorized",
}
# Default set: dev + security + AI + ops. Drops marketing/biz fluff.
OPENCODE_DEFAULT_TOPICS = {
"security-offensive", "security-defensive", "security-cloud",
"security-specialized", "security-iam", "security-network",
"security-general",
"ai-llm-dev", "coding-backend", "coding-frontend", "coding-tools",
"cloud-infra", "database", "browser-scrape", "ops-sysadmin",
"osint-intel",
}
def _classify_skill_topic(name: str, fm: dict) -> str:
"""Map a skill to one of OPENCODE_TOPICS based on frontmatter + name."""
CYBER_MAP = {
"red-teaming": "security-offensive",
"penetration-testing": "security-offensive",
"web-application-security": "security-offensive",
"api-security": "security-offensive",
"mobile-security": "security-offensive",
"cryptography": "security-offensive",
"threat-hunting": "security-defensive",
"threat-intelligence": "security-defensive",
"threat-detection": "security-defensive",
"digital-forensics": "security-defensive",
"incident-response": "security-defensive",
"soc-operations": "security-defensive",
"security-operations": "security-defensive",
"malware-analysis": "security-defensive",
"ransomware-defense": "security-defensive",
"phishing-defense": "security-defensive",
"endpoint-security": "security-defensive",
"deception-technology": "security-defensive",
"network-security": "security-network",
"cloud-security": "security-cloud",
"container-security": "security-cloud",
"identity-access-management": "security-iam",
"zero-trust-architecture": "security-iam",
"ot-ics-security": "security-specialized",
"vulnerability-management": "security-specialized",
"devsecops": "security-specialized",
"compliance-governance": "security-specialized",
"application-security": "security-specialized",
"supply-chain-security": "security-specialized",
}
sd = fm.get("subdomain")
if sd in CYBER_MAP:
return CYBER_MAP[sd]
if fm.get("domain") == "cybersecurity":
return "security-general"
NAME_PATTERNS = [
("coding-frontend", r"^(react|nextjs|next-|angular|vue-|svelte|tailwind|shadcn|vercel|expo|remotion|frontend|ui-ux|accessibility|canvas-|stitch|framer)"),
("coding-backend", r"^(python|java-|csharp|dotnet|aspnet|kotlin|swift|rust-|golang|go-|ruby-|php-|nodejs|node-|bash-|cli-|bazel|async-|architecting-|aspire-)"),
("coding-tools", r"^(commit|changelog|debug-|refactor|test-driven|tdd|bdd|git-|github-|gitlab-|bats|copilot|codeql|code-review|linting|formatting|add-|adr-|agent-browser|mcp-)"),
("ai-llm-dev", r"^(ai-|agentic|claude-|mcp|openai|anthropic|llm|rag-|embedding|fine-tun|prompt|anythingllm|olla|huggingface|elevenlabs|crawl-for-ai|agent-tools|agent-ui|agent-governance|para-memory|knowledge-hub)"),
("cloud-infra", r"^(aws|azure|gcp|kubernetes|docker|terraform|cloudflare|vercel|netlify|supabase|firebase|k8s|iac|devops|cicd|ansible|helm|bigquery|airflow|az-)"),
("database", r"^(sql-|postgres|mysql|mongodb|redis)"),
("browser-scrape", r"^(browser|playwright|puppeteer|firecrawl|stealth|scrape|crawl|use-my-browser)"),
("osint-intel", r"^(osint|recon|intel-|foia|seithar|deep-scraper|stealth-browser|social-trust|news-crawler|proudguard|gov-cyber|tavily|session-logs|youtube-transcript)"),
("marketing-content", r"^(copywriting|content-|seo-|blog-|article-|marketing-|ad-(creative|campaign)|brand-|banner|churn|billing|gtm-|competitive|backlink|boost|twitter|ai-social|ai-marketing|ai-content|ai-podcast|ai-music|ai-avatar|ai-automation|ai-image|ai-video|impeccable)"),
("ops-sysadmin", r"^(healthcheck|sysadmin|dns-networking|network-|nmap-|pcap-|tmux|freshrss|obsidian-|librarian|pdf-|image-ocr|mistral-ocr|analyze|weather|node-connect|clawflow|skill-creator|devops-engineer)"),
("business-pm", r"^(ceo-|cfo-|product-manager|marketing-strategist|marketing-psychology|qa-testing|design-md|persona-customer|product-|gtm-|arize|dataverse|power-|microsoft-)"),
("security-offensive", r"^(exploiting|pentest-|performing-(web|api|initial|privilege|credential|graphql|soap|lateral|clickjacking|subdomain|open-source|wireless|physical|iot|external|directory|oauth|csrf|web-application|web-cache|http|thick|content-security|active-directory|kerberoasting|second-order|blind-ssrf|jwt-none|initial-access)|testing-(for|api|oauth2|jwt|websocket|websocket-api|cors)|sql-injection|pwnclaw-security)"),
("security-defensive", r"^(security-(review|audit|scanner|headers|skill-scanner)|senior-secops|threat-|ctf-|sys-guard|clawsec|agent-intelligence|war-intel|sentinel)"),
]
for topic, pattern in NAME_PATTERNS:
if re.match(pattern, name.lower()):
return topic
return "uncategorized"
def _parse_skill_frontmatter_simple(skill_md: Path) -> dict:
"""Minimal YAML frontmatter parser — just key: value pairs."""
try:
text = skill_md.read_text(encoding="utf-8", errors="ignore")
except Exception:
return {}
if not text.startswith("---"):
return {}
end = text.find("\n---", 4)
if end < 0:
return {}
fm = {}
for line in text[4:end].splitlines():
m = re.match(r"^([a-z_]+):\s*(.+?)\s*$", line)
if m:
fm[m.group(1)] = m.group(2).strip().strip('"\'')
return fm
def install_opencode(
output_dir: Path,
shared_dir: Path | None = None,
topics: set[str] | None = None,
):
"""Install personas to OpenCode as agents + skills.
OpenCode agent format (per https://opencode.ai/docs/agents/):
- Location: ~/.config/opencode/agents/<codename>.md
- YAML frontmatter: description, mode (primary|subagent), model,
temperature, color, permission (edit/bash/webfetch/task).
OpenCode skill format (per https://opencode.ai/docs/skills/):
- Location: ~/.config/opencode/skills/<name>/SKILL.md
- YAML frontmatter: name, description (required).
- OpenCode ALSO reads ~/.claude/skills/ natively.
Args:
topics: set of topics to install (see OPENCODE_TOPICS). Defaults to
OPENCODE_DEFAULT_TOPICS which drops marketing/biz skills.
"""
if topics is None:
topics = OPENCODE_DEFAULT_TOPICS
agents_dir = Path.home() / ".config" / "opencode" / "agents"
skills_dir = Path.home() / ".config" / "opencode" / "skills"
agents_dir.mkdir(parents=True, exist_ok=True)
skills_dir.mkdir(parents=True, exist_ok=True)
# Offensive/engineering personas get full permissions (primary mode).
# Analytical personas are subagents with readonly bias.
OFFENSIVE_DOMAINS = {
"cybersecurity",
"engineering",
"devops",
"software-development",
"ai-ml",
}
DOMAIN_COLOR = {
"cybersecurity": "error", # red-like
"intelligence": "info", # cyan-like
"military": "warning", # orange
"law": "warning",
"economics": "success",
"politics": "accent",
"history": "primary",
"linguistics": "secondary",
"media": "secondary",
"engineering": "success",
"academia": "primary",
"humanities": "accent",
"science": "info",
"strategy": "accent",
}
agent_count = 0
for persona_dir in sorted(output_dir.iterdir()):
if not persona_dir.is_dir() or persona_dir.name.startswith("_"):
continue
general_json = persona_dir / "general.json"
if not general_json.exists():
continue
data = json.loads(general_json.read_text(encoding="utf-8"))
codename = data.get("codename", persona_dir.name)
name = data.get("name", codename.title())
role = data.get("role", "Specialist")
domain = data.get("domain", "")
tone = data.get("tone", "")
address_to = data.get("address_to", "")
quote = data.get("quote", "")
skills = data.get("skills", [])
soul = data.get("sections", {}).get("soul", "")
methodology = data.get("sections", {}).get("methodology", "")
behavior = data.get("sections", {}).get("behavior_rules", "")
body = f"You are **{name}** ({address_to}) — {role}.\n\n"
body += f"Domain: {domain} | Tone: {tone}\n\n"
if quote:
body += f'> "{quote}"\n\n'
if soul:
body += "## Soul\n" + soul.strip() + "\n\n"
if methodology:
body += "## Methodology\n" + methodology.strip() + "\n\n"
if behavior:
body += "## Behavior\n" + behavior.strip() + "\n\n"
if skills:
body += "## Mapped Skills\n" + ", ".join(skills) + "\n"
is_offensive = domain in OFFENSIVE_DOMAINS
mode = "primary" if is_offensive else "subagent"
color = DOMAIN_COLOR.get(domain, "primary")
# Permission block differs for offensive vs analytical personas.
if is_offensive:
permission_block = (
"permission:\n"
" edit: allow\n"
" bash:\n"
' "*": allow\n'
" webfetch: allow\n"
)
else:
permission_block = (
"permission:\n"
" edit: ask\n"
" bash:\n"
' "*": ask\n'
" webfetch: allow\n"
)
desc = f"{name} ({address_to}) — {role}. Domain: {domain}.".replace(
"\n", " "
)
frontmatter = (
"---\n"
f"description: {desc}\n"
f"mode: {mode}\n"
"temperature: 0.3\n"
f"color: {color}\n"
f"{permission_block}"
"---\n\n"
)
agent_file = agents_dir / f"{codename}.md"
agent_file.write_text(frontmatter + body, encoding="utf-8")
agent_count += 1
# Install shared skills with topic filter. OpenCode reads SKILL.md with
# name+description frontmatter (same as Claude).
skill_count = 0
per_topic: dict[str, int] = {}
skipped_topic = 0
# Purge existing skills dir so stale filtered-out skills are removed.
if skills_dir.exists():
import shutil as _shutil
for existing in skills_dir.iterdir():
if existing.is_dir():
_shutil.rmtree(existing)
if shared_dir:
for skills_subdir in ["skills", "paperclip-skills", "community-skills"]:
src_root = shared_dir / skills_subdir
if not src_root.exists():
continue
for skill_dir in src_root.iterdir():
if not skill_dir.is_dir():
continue
src_skill = skill_dir / "SKILL.md"
if not src_skill.exists():
continue
# Honor opencode name regex: ^[a-z0-9]+(-[a-z0-9]+)*$.
sanitized = skill_dir.name.lower()
if not re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", sanitized):
continue
# Topic filter — drop skills not in requested topics.
fm = _parse_skill_frontmatter_simple(src_skill)
topic = _classify_skill_topic(skill_dir.name, fm)
if topic not in topics:
skipped_topic += 1
continue
per_topic[topic] = per_topic.get(topic, 0) + 1
dest_dir = skills_dir / sanitized
dest_dir.mkdir(parents=True, exist_ok=True)
dest_skill = dest_dir / "SKILL.md"
dest_skill.write_text(
src_skill.read_text(encoding="utf-8"), encoding="utf-8"
)
# Copy references/ if present.
refs = skill_dir / "references"
if refs.exists() and refs.is_dir():
dest_refs = dest_dir / "references"
dest_refs.mkdir(exist_ok=True)
for ref in refs.iterdir():
if ref.is_file():
(dest_refs / ref.name).write_text(
ref.read_text(encoding="utf-8"),
encoding="utf-8",
)
skill_count += 1
print(
f" OpenCode: {agent_count} agents installed to {agents_dir}"
)
print(
f" OpenCode skills: {skill_count} installed "
f"({skipped_topic} skipped by topic filter)"
)
if per_topic:
print(" Per topic: " + ", ".join(
f"{k}={v}" for k, v in sorted(per_topic.items(), key=lambda x: -x[1])
))
return agent_count
def install_gemini(output_dir: Path):
"""Install personas as Gemini Gems (JSON format for Google AI Studio)."""
gems_dir = output_dir / "_gems"
@@ -1534,6 +1848,7 @@ def main():
"antigravity",
"gemini",
"openclaw",
"opencode",
"paperclip",
"all",
],
@@ -1587,6 +1902,17 @@ def main():
"offensive=red-team+pentest+exploit verbs; defensive=DFIR+threat-hunting; "
"ctiops=threat-intel+APT; minimal=top categories only; all=no filters.",
)
parser.add_argument(
"--opencode-topics",
default=None,
help="Comma-separated topic filter for --install opencode. "
"Topics: security-offensive, security-defensive, security-cloud, "
"security-specialized, security-iam, security-network, security-general, "
"ai-llm-dev, coding-backend, coding-frontend, coding-tools, cloud-infra, "
"database, browser-scrape, ops-sysadmin, osint-intel, marketing-content, "
"business-pm, uncategorized. "
"Default drops marketing/biz. Use 'all' for no filter.",
)
parser.add_argument(
"--search",
type=str,
@@ -1717,6 +2043,7 @@ def main():
"antigravity",
"gemini",
"openclaw",
"opencode",
"paperclip",
]
else:
@@ -1740,6 +2067,19 @@ def main():
install_gemini(output_dir)
elif target == "openclaw":
install_openclaw(output_dir)
elif target == "opencode":
if args.opencode_topics:
if args.opencode_topics.strip().lower() == "all":
oc_topics = OPENCODE_TOPICS
else:
oc_topics = {
t.strip()
for t in args.opencode_topics.split(",")
if t.strip()
}
else:
oc_topics = None # use default
install_opencode(output_dir, shared_dir, topics=oc_topics)
elif target == "paperclip":
install_paperclip(output_dir, personas_dir, shared_dir)