feat: add Devin CLI support (graphify devin install/uninstall) (#1020)

* feat: add Devin CLI support

- Add 'devin' to _PLATFORM_CONFIG with skill-devin.md
- Override _platform_skill_destination for devin:
  user scope -> ~/.config/devin/skills/graphify/SKILL.md
  project scope -> .devin/skills/graphify/SKILL.md
- Add _DEVIN_RULES + _devin_rules_install/_uninstall:
  writes .windsurf/rules/graphify.md (always-on context)
  Devin reads .windsurf/rules/*.md the same way Windsurf IDE does
- Add graphify devin install [--project] / uninstall subcommand
- Create graphify/skill-devin.md with Devin YAML frontmatter
  (name, argument-hint, model, allowed-tools, triggers)
- Add 21 tests in tests/test_devin.py covering user/project scope
- Register _DEVIN_RULES in test_install_strings.py policy checks
- Update README.md and pyproject.toml to list Devin CLI

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* expand skill-devin.md to full feature parity with skill-pi.md

Rebuilds skill-devin.md from ~267 lines to 1176 lines, matching the
depth of other mature platform skills (pi, droid, codex).

Added:
- Parallel Part A + B dispatch guidance (AST alongside semantic subagents)
- Full subagent extraction prompt with rationale, semantic similarity,
  hyperedge, and confidence_score rules
- Extraction cache check (Step B0) and cache save/load flow
- --mode deep, --svg, --graphml, --neo4j/--neo4j-push, --mcp, --watch
- /graphify path, /graphify explain, /graphify add, /graphify query
- BFS vs DFS traversal with --dfs and --budget flags
- Token reduction benchmark step
- --update (incremental) and --cluster-only pipelines with graph diff
- Video/audio transcription (Step 2.5) with Whisper domain-hint strategy
- git commit hook section
- Always-on Devin rules section referencing `graphify devin install`

Updated test_devin.py: corrected python_c_syntax assertion to match
the interpreter-detection pattern used across all mature skills.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: vishal-gandhi <ivishalgandhi@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
vishal-gandhi
2026-05-26 09:17:18 +01:00
committed by GitHub
co-authored by Devin vishal-gandhi
parent 336652779b
commit 065a621fa6
6 changed files with 1751 additions and 4 deletions
+3
View File
@@ -137,6 +137,7 @@ for example `graphify claude install --project` or `graphify codex install --pro
| Kiro IDE/CLI | `graphify kiro install` |
| Pi coding agent | `graphify install --platform pi` |
| Cursor | `graphify cursor install` |
| Devin CLI | `graphify devin install` |
| Google Antigravity | `graphify antigravity install` |
> Codex users: also add `multi_agent = true` under `[features]` in `~/.codex/config.toml`.
@@ -187,6 +188,7 @@ Run this once in your project after building a graph:
| Kimi Code | `graphify install --platform kimi` |
| Kiro IDE/CLI | `graphify kiro install` |
| Pi coding agent | `graphify pi install` |
| Devin CLI | `graphify devin install` |
| Google Antigravity | `graphify antigravity install` |
This writes a small config file that tells your assistant to consult the knowledge graph for codebase questions — preferring scoped queries like `graphify query "<question>"` over reading the full report or grepping raw files. On platforms that support payload-bearing hooks (Claude Code, Gemini CLI), a hook fires automatically before search-style tool calls and nudges your assistant toward the graph path. On the others (Codex, OpenCode, Cursor, etc.), the persistent instruction files (`AGENTS.md`, `.cursor/rules/`, etc.) provide the same query-first guidance. `GRAPH_REPORT.md` is still available for broad architecture review.
@@ -466,6 +468,7 @@ graphify trae install / uninstall
graphify trae-cn install / uninstall
graphify hermes install / uninstall
graphify kiro install / uninstall
graphify devin install / uninstall
graphify antigravity install / uninstall
graphify extract ./docs # headless LLM extraction for CI (no IDE needed)
+78 -1
View File
@@ -77,6 +77,11 @@ def _platform_skill_destination(platform_name: str, *, project: bool = False, pr
return Path.home() / ".agents" / "skills" / "graphify" / "SKILL.md"
return Path.home() / ".gemini" / "skills" / "graphify" / "SKILL.md"
if platform_name == "devin":
if project:
return (project_dir or Path(".")) / ".devin" / "skills" / "graphify" / "SKILL.md"
return Path.home() / ".config" / "devin" / "skills" / "graphify" / "SKILL.md"
cfg = _PLATFORM_CONFIG[platform_name]
if project:
return (project_dir or Path(".")) / cfg["skill_dst"]
@@ -284,6 +289,13 @@ _PLATFORM_CONFIG: dict[str, dict] = {
"skill_dst": Path(".kimi") / "skills" / "graphify" / "SKILL.md",
"claude_md": False,
},
"devin": {
"skill_file": "skill-devin.md",
# User scope: ~/.config/devin/skills/graphify/SKILL.md
# Project scope: .devin/skills/graphify/SKILL.md (overridden in _platform_skill_destination)
"skill_dst": Path(".config") / "devin" / "skills" / "graphify" / "SKILL.md",
"claude_md": False,
},
}
@@ -854,6 +866,44 @@ def _cursor_uninstall(project_dir: Path) -> None:
print(f"graphify Cursor rule removed from {rule_path.resolve()}")
# Devin CLI — .windsurf/rules/graphify.md (always-on context)
# Devin reads .windsurf/rules/*.md files the same way Windsurf IDE does.
_DEVIN_RULES_PATH = Path(".windsurf") / "rules" / "graphify.md"
_DEVIN_RULES_MARKER = "## graphify"
_DEVIN_RULES = """\
## graphify
This project has a graphify knowledge graph at graphify-out/.
Rules:
- For codebase or architecture questions, when `graphify-out/graph.json` exists, first run `graphify query "<question>"` (or `graphify path "<A>" "<B>"` / `graphify explain "<concept>"`). These return a scoped subgraph, usually much smaller than `GRAPH_REPORT.md` or raw grep output.
- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context
- After modifying code files in this session, run `graphify update .` to keep the graph current (AST-only, no API cost)
"""
def _devin_rules_install(project_dir: Path) -> None:
"""Write .windsurf/rules/graphify.md for always-on Devin context."""
rules_path = (project_dir or Path(".")) / _DEVIN_RULES_PATH
rules_path.parent.mkdir(parents=True, exist_ok=True)
if rules_path.exists() and rules_path.read_text(encoding="utf-8") == _DEVIN_RULES:
print(f" {rules_path} -> already configured (no change)")
return
action = "updated" if rules_path.exists() else "written"
rules_path.write_text(_DEVIN_RULES, encoding="utf-8")
print(f" rules {action} -> {rules_path}")
def _devin_rules_uninstall(project_dir: Path) -> None:
"""Remove .windsurf/rules/graphify.md."""
rules_path = (project_dir or Path(".")) / _DEVIN_RULES_PATH
if not rules_path.exists():
return
rules_path.unlink()
print(f" rules removed -> {rules_path}")
# OpenCode tool.execute.before plugin — fires before every tool call.
# Injects a graph reminder into bash command output when graph.json exists.
_OPENCODE_PLUGIN_JS = """\
@@ -1080,6 +1130,10 @@ def _project_install(platform_name: str, project_dir: Path | None = None) -> Non
elif platform_name == "codex":
hint_paths.append(project_dir / ".codex")
_print_project_git_add_hint(hint_paths)
elif platform_name == "devin":
skill_dst = _copy_skill_file("devin", project=True, project_dir=project_dir)
_devin_rules_install(project_dir)
_print_project_git_add_hint([_project_scope_root(skill_dst, project_dir), project_dir / ".windsurf"])
elif platform_name in ("copilot", "pi", "antigravity", "kimi"):
skill_dst = _copy_skill_file(platform_name, project=True, project_dir=project_dir)
_print_project_git_add_hint([_project_scope_root(skill_dst, project_dir)])
@@ -1107,6 +1161,11 @@ def _project_uninstall(platform_name: str, project_dir: Path | None = None) -> N
_uninstall_codex_hook(project_dir)
elif platform_name == "antigravity":
_antigravity_uninstall(project_dir)
elif platform_name == "devin":
removed = _remove_skill_file("devin", project=True, project_dir=project_dir)
_devin_rules_uninstall(project_dir)
if not removed:
print("nothing to remove")
elif platform_name in ("copilot", "pi", "kimi"):
removed = _remove_skill_file(platform_name, project=True, project_dir=project_dir)
if not removed:
@@ -1366,7 +1425,7 @@ def main() -> None:
print("Usage: graphify <command>")
print()
print("Commands:")
print(" install [--platform P] copy skill to platform config dir (claude|windows|codex|opencode|aider|claw|droid|trae|trae-cn|gemini|cursor|antigravity|hermes|kiro|pi)")
print(" install [--platform P] copy skill to platform config dir (claude|windows|codex|opencode|aider|claw|droid|trae|trae-cn|gemini|cursor|antigravity|hermes|kiro|pi|devin)")
print(" uninstall remove graphify from all detected platforms in one shot")
print(" --purge also delete graphify-out/ directory")
print(" path \"A\" \"B\" shortest path between two nodes in graph.json")
@@ -1478,6 +1537,8 @@ def main() -> None:
print(" kiro uninstall remove skill + steering file")
print(" pi install write skill to ~/.pi/agent/skills/graphify/ (Pi coding agent)")
print(" pi uninstall remove skill from ~/.pi/agent/skills/graphify/")
print(" devin install write skill to ~/.config/devin/skills/graphify/ (Devin CLI)")
print(" devin uninstall remove skill from ~/.config/devin/skills/graphify/")
print()
return
@@ -1638,6 +1699,22 @@ def main() -> None:
else:
print("Usage: graphify kiro [install|uninstall]", file=sys.stderr)
sys.exit(1)
elif cmd == "devin":
subcmd = sys.argv[2] if len(sys.argv) > 2 else ""
if subcmd == "install":
if "--project" in sys.argv[3:]:
_project_install("devin", Path("."))
else:
install(platform="devin")
elif subcmd == "uninstall":
if "--project" in sys.argv[3:]:
_project_uninstall("devin", Path("."))
else:
removed = _remove_skill_file("devin")
print("skill removed" if removed else "nothing to remove")
else:
print("Usage: graphify devin [install|uninstall]", file=sys.stderr)
sys.exit(1)
elif cmd == "pi":
subcmd = sys.argv[2] if len(sys.argv) > 2 else ""
if subcmd == "install":
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -5,10 +5,10 @@ build-backend = "setuptools.build_meta"
[project]
name = "graphifyy"
version = "0.8.18"
description = "AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, Pi, Google Antigravity) - turn any folder of code, docs, papers, images, or videos into a queryable knowledge graph"
description = "AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, Aider, OpenClaw, Factory Droid, Trae, Hermes, Kiro, Pi, Devin CLI, Google Antigravity) - turn any folder of code, docs, papers, images, or videos into a queryable knowledge graph"
readme = "README.md"
license = { file = "LICENSE" }
keywords = ["claude", "claude-code", "codex", "opencode", "cursor", "gemini", "aider", "kiro", "pi", "knowledge-graph", "rag", "graphrag", "obsidian", "community-detection", "tree-sitter", "leiden", "llm"]
keywords = ["claude", "claude-code", "codex", "opencode", "cursor", "gemini", "aider", "kiro", "pi", "devin", "knowledge-graph", "rag", "graphrag", "obsidian", "community-detection", "tree-sitter", "leiden", "llm"]
requires-python = ">=3.10"
dependencies = [
"networkx",
@@ -96,7 +96,7 @@ packages = ["graphify"]
include-package-data = false
[tool.setuptools.package-data]
graphify = ["skill.md", "skill-codex.md", "skill-opencode.md", "skill-aider.md", "skill-copilot.md", "skill-claw.md", "skill-windows.md", "skill-droid.md", "skill-trae.md", "skill-kiro.md", "skill-vscode.md", "skill-pi.md"]
graphify = ["skill.md", "skill-codex.md", "skill-opencode.md", "skill-aider.md", "skill-copilot.md", "skill-claw.md", "skill-windows.md", "skill-droid.md", "skill-trae.md", "skill-kiro.md", "skill-vscode.md", "skill-pi.md", "skill-devin.md"]
[tool.pytest.ini_options]
testpaths = ["tests"]
+309
View File
@@ -0,0 +1,309 @@
"""Tests for graphify devin install / uninstall commands."""
from pathlib import Path
import sys
from unittest.mock import patch
import pytest
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _devin_install_user(tmp_path):
from graphify.__main__ import install
old_cwd = Path.cwd()
try:
import os
os.chdir(tmp_path)
with patch("graphify.__main__.Path.home", return_value=tmp_path):
install(platform="devin")
finally:
import os
os.chdir(old_cwd)
def _skill_path_user(tmp_path):
return tmp_path / ".config" / "devin" / "skills" / "graphify" / "SKILL.md"
def _skill_path_project(project_dir):
return project_dir / ".devin" / "skills" / "graphify" / "SKILL.md"
def _rules_path(project_dir):
return project_dir / ".windsurf" / "rules" / "graphify.md"
# ---------------------------------------------------------------------------
# User-scope install (graphify install --platform devin / graphify devin install)
# ---------------------------------------------------------------------------
def test_devin_install_user_creates_skill_file(tmp_path):
"""User-scope install copies skill to ~/.config/devin/skills/graphify/SKILL.md."""
_devin_install_user(tmp_path)
skill_path = _skill_path_user(tmp_path)
assert skill_path.exists()
def test_devin_skill_file_contains_frontmatter(tmp_path):
"""Installed skill file must include Devin-specific YAML frontmatter."""
_devin_install_user(tmp_path)
content = _skill_path_user(tmp_path).read_text()
assert "name: graphify" in content
assert "argument-hint:" in content
assert "triggers:" in content
def test_devin_skill_file_references_graphify_query(tmp_path):
"""/graphify skill must mention graphify query (query-first policy)."""
_devin_install_user(tmp_path)
content = _skill_path_user(tmp_path).read_text()
assert "graphify query" in content or "/graphify query" in content
def test_devin_install_user_does_not_write_rules(tmp_path):
"""User-scope install does NOT write .windsurf/rules/ — that's project-only."""
_devin_install_user(tmp_path)
assert not _rules_path(tmp_path).exists()
# ---------------------------------------------------------------------------
# Project-scope install (graphify devin install --project)
# ---------------------------------------------------------------------------
def test_devin_install_project_creates_skill_file(tmp_path, monkeypatch):
"""Project-scope install copies skill to .devin/skills/graphify/SKILL.md."""
from graphify.__main__ import main
home = tmp_path / "home"
project = tmp_path / "project"
project.mkdir()
monkeypatch.chdir(project)
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "install", "--project"])
with patch("graphify.__main__.Path.home", return_value=home):
main()
assert _skill_path_project(project).exists()
assert not _skill_path_user(home).exists()
def test_devin_install_project_creates_rules_file(tmp_path, monkeypatch):
"""Project-scope install writes .windsurf/rules/graphify.md."""
from graphify.__main__ import main
home = tmp_path / "home"
project = tmp_path / "project"
project.mkdir()
monkeypatch.chdir(project)
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "install", "--project"])
with patch("graphify.__main__.Path.home", return_value=home):
main()
rules = _rules_path(project)
assert rules.exists()
assert "graphify" in rules.read_text()
assert "GRAPH_REPORT.md" in rules.read_text()
def test_devin_rules_content_recommends_graphify_query(tmp_path):
"""The rules file installed by devin must use query-first policy."""
from graphify.__main__ import _devin_rules_install
_devin_rules_install(tmp_path)
content = _rules_path(tmp_path).read_text()
assert "graphify query" in content
def test_devin_rules_install_idempotent(tmp_path, capsys):
"""Installing rules twice does not change content and prints 'no change'."""
from graphify.__main__ import _devin_rules_install
_devin_rules_install(tmp_path)
content_first = _rules_path(tmp_path).read_text()
_devin_rules_install(tmp_path)
content_second = _rules_path(tmp_path).read_text()
assert content_first == content_second
assert "no change" in capsys.readouterr().out
def test_devin_install_project_hints_git_add(tmp_path, monkeypatch, capsys):
"""Project-scope install prints a git add hint covering .devin/ and .windsurf/."""
from graphify.__main__ import main
home = tmp_path / "home"
project = tmp_path / "project"
project.mkdir()
monkeypatch.chdir(project)
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "install", "--project"])
with patch("graphify.__main__.Path.home", return_value=home):
main()
out = capsys.readouterr().out
assert "git add" in out
# ---------------------------------------------------------------------------
# Uninstall — user scope
# ---------------------------------------------------------------------------
def test_devin_uninstall_user_removes_skill_file(tmp_path):
"""User-scope uninstall removes the skill file."""
_devin_install_user(tmp_path)
skill = _skill_path_user(tmp_path)
assert skill.exists()
from graphify.__main__ import _remove_skill_file
with patch("graphify.__main__.Path.home", return_value=tmp_path):
_remove_skill_file("devin")
assert not skill.exists()
def test_devin_uninstall_user_noop_when_not_installed(tmp_path, capsys):
"""User-scope uninstall prints an appropriate message when nothing is installed."""
from graphify.__main__ import main
import os
old_cwd = Path.cwd()
try:
os.chdir(tmp_path)
with patch("graphify.__main__.Path.home", return_value=tmp_path):
sys.argv = ["graphify", "devin", "uninstall"]
main()
finally:
os.chdir(old_cwd)
out = capsys.readouterr().out
assert "nothing to remove" in out
# ---------------------------------------------------------------------------
# Uninstall — project scope
# ---------------------------------------------------------------------------
def test_devin_uninstall_project_removes_skill_file(tmp_path, monkeypatch):
"""Project-scope uninstall removes .devin/skills/graphify/SKILL.md."""
from graphify.__main__ import main
home = tmp_path / "home"
project = tmp_path / "project"
project.mkdir()
monkeypatch.chdir(project)
with patch("graphify.__main__.Path.home", return_value=home):
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "install", "--project"])
main()
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "uninstall", "--project"])
main()
assert not _skill_path_project(project).exists()
def test_devin_uninstall_project_removes_rules_file(tmp_path, monkeypatch):
"""Project-scope uninstall removes .windsurf/rules/graphify.md."""
from graphify.__main__ import main
home = tmp_path / "home"
project = tmp_path / "project"
project.mkdir()
monkeypatch.chdir(project)
with patch("graphify.__main__.Path.home", return_value=home):
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "install", "--project"])
main()
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "uninstall", "--project"])
main()
assert not _rules_path(project).exists()
def test_devin_uninstall_project_does_not_touch_user_scope(tmp_path, monkeypatch):
"""Project-scope uninstall must not remove the user-scope skill file."""
from graphify.__main__ import main
home = tmp_path / "home"
project = tmp_path / "project"
project.mkdir()
# Pre-create a user-scope skill file
user_skill = _skill_path_user(home)
user_skill.parent.mkdir(parents=True, exist_ok=True)
user_skill.write_text("user skill")
monkeypatch.chdir(project)
with patch("graphify.__main__.Path.home", return_value=home):
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "install", "--project"])
main()
monkeypatch.setattr(sys, "argv", ["graphify", "devin", "uninstall", "--project"])
main()
assert user_skill.exists()
def test_devin_rules_uninstall_noop_when_not_installed(tmp_path):
"""_devin_rules_uninstall does nothing if the rules file was never written."""
from graphify.__main__ import _devin_rules_uninstall
_devin_rules_uninstall(tmp_path) # should not raise
# ---------------------------------------------------------------------------
# Skill file content
# ---------------------------------------------------------------------------
def test_devin_skill_file_exists_in_package():
"""skill-devin.md must be present in the installed package."""
import graphify
skill = Path(graphify.__file__).parent / "skill-devin.md"
assert skill.exists(), "skill-devin.md missing from package"
def test_devin_skill_file_uses_python_c_syntax():
"""Devin skill must use inline python -c syntax (cross-platform, no bash heredocs).
All mature graphify skills use the interpreter-detection pattern
``$(cat graphify-out/.graphify_python) -c "..."`` rather than bare
``python -c "..."`` so they work in pipx / venv environments.
"""
import graphify
skill = (Path(graphify.__file__).parent / "skill-devin.md").read_text()
assert '.graphify_python) -c "' in skill, (
"skill-devin.md must use the interpreter-detection pattern "
"'$(cat graphify-out/.graphify_python) -c \"...\"'"
)
assert "#!/bin/bash" not in skill
def test_devin_skill_file_frontmatter_has_triggers():
"""Devin skill frontmatter must list triggers for model-invocable activation."""
import graphify
skill = (Path(graphify.__file__).parent / "skill-devin.md").read_text()
assert "triggers:" in skill
assert "model" in skill
# ---------------------------------------------------------------------------
# Platform config sanity
# ---------------------------------------------------------------------------
def test_devin_in_platform_config():
"""devin must be registered in _PLATFORM_CONFIG."""
from graphify.__main__ import _PLATFORM_CONFIG
assert "devin" in _PLATFORM_CONFIG
assert _PLATFORM_CONFIG["devin"]["skill_file"] == "skill-devin.md"
assert _PLATFORM_CONFIG["devin"]["claude_md"] is False
def test_devin_platform_skill_destination_user_scope(tmp_path):
"""User-scope destination must be ~/.config/devin/skills/graphify/SKILL.md."""
from graphify.__main__ import _platform_skill_destination
with patch("graphify.__main__.Path.home", return_value=tmp_path):
dst = _platform_skill_destination("devin", project=False)
assert dst == tmp_path / ".config" / "devin" / "skills" / "graphify" / "SKILL.md"
def test_devin_in_main_help_text(capsys, monkeypatch):
"""`graphify --help` must list devin in the platform list and in the per-platform section."""
from graphify.__main__ import main
monkeypatch.setattr(sys, "argv", ["graphify", "--help"])
main()
captured = capsys.readouterr().out
# devin should appear in the top-level platform list
assert "|devin)" in captured or "|devin |" in captured or "|devin" in captured, (
"devin missing from `graphify --help` platform list"
)
# devin install / uninstall should appear in the per-platform section
assert "devin install" in captured, "`devin install` line missing from help text"
assert "devin uninstall" in captured, "`devin uninstall` line missing from help text"
assert "~/.config/devin" in captured, "devin user-scope path missing from help text"
# Convention: `--project` is supported by all platforms but documented by none.
# devin should not be the lone outlier that documents it.
devin_section = captured.split("devin install", 1)[1].split("\n\n", 1)[0]
assert "--project" not in devin_section, (
"devin help should NOT document --project — no other platform does"
)
def test_devin_platform_skill_destination_project_scope(tmp_path):
"""Project-scope destination must be <project>/.devin/skills/graphify/SKILL.md."""
from graphify.__main__ import _platform_skill_destination
dst = _platform_skill_destination("devin", project=True, project_dir=tmp_path)
assert dst == tmp_path / ".devin" / "skills" / "graphify" / "SKILL.md"
+3
View File
@@ -22,6 +22,7 @@ from graphify.__main__ import (
_KIRO_STEERING,
_CURSOR_RULE,
_OPENCODE_PLUGIN_JS,
_DEVIN_RULES,
)
@@ -39,6 +40,7 @@ _INSTALL_TEXTS: dict[str, str] = {
"_KIRO_STEERING": _KIRO_STEERING,
"_CURSOR_RULE": _CURSOR_RULE,
"_OPENCODE_PLUGIN_JS": _OPENCODE_PLUGIN_JS,
"_DEVIN_RULES": _DEVIN_RULES,
}
@@ -105,6 +107,7 @@ def test_report_is_still_referenced_as_fallback():
"_ANTIGRAVITY_RULES": _ANTIGRAVITY_RULES,
"_KIRO_STEERING": _KIRO_STEERING,
"_CURSOR_RULE": _CURSOR_RULE,
"_DEVIN_RULES": _DEVIN_RULES,
}
missing: list[str] = []
for name, text in md_section_texts.items():