clone

a hermes agent plugin for openspec: tools for spec workflows and a dashboard tab for browsing changes and specs

main
hermes-openspec / __init__.py
__init__.py 4.0K
1"""OpenSpec plugin — registers CLI-backed spec workflow tools and bundled skills."""23from __future__ import annotations45import logging6from pathlib import Path78try:9 from . import schemas10 from . import tools11except ImportError: # pragma: no cover - pytest may import the repo root as a top-level module12 import schemas # type: ignore13 import tools # type: ignore1415logger = logging.getLogger(__name__)16_PLUGIN_DIR = Path(__file__).parent1718_TOOLS = (19 ("openspec_list", schemas.OPENSPEC_LIST, tools.openspec_list, "📋"),20 ("openspec_show", schemas.OPENSPEC_SHOW, tools.openspec_show, "📖"),21 ("openspec_validate", schemas.OPENSPEC_VALIDATE, tools.openspec_validate, "✅"),22 ("openspec_status", schemas.OPENSPEC_STATUS, tools.openspec_status, "📊"),23 ("openspec_instructions", schemas.OPENSPEC_INSTRUCTIONS, tools.openspec_instructions, "🧭"),24)2526_WRITE_TOOLS = (27 ("openspec_idea_create", schemas.OPENSPEC_IDEA_CREATE, tools.openspec_idea_create, "💡"),28 ("openspec_idea_enrich", schemas.OPENSPEC_IDEA_ENRICH, tools.openspec_idea_enrich, "🔎"),29 ("openspec_idea_promote", schemas.OPENSPEC_IDEA_PROMOTE, tools.openspec_idea_promote, "🚀"),30 ("openspec_task_list", schemas.OPENSPEC_TASK_LIST, tools.openspec_task_list, "☑️"),31 ("openspec_change_sequence_set", schemas.OPENSPEC_CHANGE_SEQUENCE_SET, tools.openspec_change_sequence_set, "🔢"),32 ("openspec_task_set_status", schemas.OPENSPEC_TASK_SET_STATUS, tools.openspec_task_set_status, "✅"),33 ("openspec_change_create", schemas.OPENSPEC_CHANGE_CREATE, tools.openspec_change_create, "📝"),34 ("openspec_change_promote", schemas.OPENSPEC_CHANGE_PROMOTE, tools.openspec_change_promote, "➡️"),35 ("openspec_change_archive", schemas.OPENSPEC_CHANGE_ARCHIVE, tools.openspec_change_archive, "📦"),36 ("openspec_change_unarchive", schemas.OPENSPEC_CHANGE_UNARCHIVE, tools.openspec_change_unarchive, "♻️"),37 ("openspec_spec_diff", schemas.OPENSPEC_SPEC_DIFF, tools.openspec_spec_diff, "🔍"),38 ("openspec_spec_create", schemas.OPENSPEC_SPEC_CREATE, tools.openspec_spec_create, "✏️"),39 ("openspec_spec_show", schemas.OPENSPEC_SPEC_SHOW, tools.openspec_spec_show, "📄"),40 ("openspec_spec_list", schemas.OPENSPEC_SPEC_LIST, tools.openspec_spec_list, "📋"),41)424344def _check_openspec_available() -> bool:45 return tools._openspec_bin() is not None464748def register(ctx) -> None:49 # openspec_context only reads the registry DB + files on disk, so it is50 # available even when the openspec CLI binary is not installed. It is the51 # entry point for resolving copyable 'os_' identifiers.52 ctx.register_tool(53 name="openspec_context",54 toolset="openspec",55 schema=schemas.OPENSPEC_CONTEXT,56 handler=tools.openspec_context,57 emoji="🔖",58 )59 for name, schema, handler, emoji in _TOOLS:60 ctx.register_tool(61 name=name,62 toolset="openspec",63 schema=schema,64 handler=handler,65 check_fn=_check_openspec_available,66 emoji=emoji,67 )68 for name, schema, handler, emoji in _WRITE_TOOLS:69 ctx.register_tool(70 name=name,71 toolset="openspec",72 schema=schema,73 handler=handler,74 emoji=emoji,75 )76 # CLI passthrough tool — gated on openspec binary availability77 ctx.register_tool(78 name="openspec_cli",79 toolset="openspec",80 schema=schemas.OPENSPEC_CLI,81 handler=tools.openspec_cli,82 check_fn=_check_openspec_available,83 emoji="🖥️",84 )85 # Register bundled upstream workflow skills (openspec-propose, openspec-apply-change, etc.)86 skills_dir = _PLUGIN_DIR / "skills"87 for child in sorted(skills_dir.iterdir()) if skills_dir.is_dir() else []:88 skill_md = child / "SKILL.md"89 if child.is_dir() and skill_md.exists():90 try:91 ctx.register_skill(child.name, skill_md)92 except Exception:93 logger.warning("Failed to register skill %s", child.name, exc_info=True)