clone

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

main
hermes-openspec / schemas.py
schemas.py 19.8K
1"""OpenSpec Hermes tool schemas."""23WORKDIR_PROP = {4 "type": "string",5 "description": (6 "Project directory containing the openspec/ folder. Defaults to the "7 "agent/session working directory. Use an absolute path when known."8 ),9}1011OPENSPEC_CONTEXT = {12 "name": "openspec_context",13 "description": (14 "Resolve a copyable OpenSpec identifier into working context. Paste an "15 "identifier like 'puzzletea' (a registered repo, by its vanity name) or "16 "'puzzletea/os_a1b2c3' (a specific change or spec within it) and this returns the "17 "repo path plus the change proposal/tasks/design/specs content, or the spec content. "18 "The bare repo name lists active changes and current specs with their tokens. Use the "19 "returned 'path' as 'workdir' for the other openspec_* tools. Call this "20 "first whenever the user gives you an OpenSpec identifier."21 ),22 "parameters": {23 "type": "object",24 "properties": {25 "identifier": {26 "type": "string",27 "description": "A repo vanity name ('puzzletea') or name/change-token ('puzzletea/os_a1b2c3').",28 },29 },30 "required": ["identifier"],31 },32}333435OPENSPEC_LIST = {36 "name": "openspec_list",37 "description": (38 "List OpenSpec changes or specs in a project. Use before choosing a "39 "change/spec to inspect, validate, or coordinate with kanban tasks."40 ),41 "parameters": {42 "type": "object",43 "properties": {44 "workdir": WORKDIR_PROP,45 "kind": {46 "type": "string",47 "enum": ["changes", "specs"],48 "description": "Which OpenSpec items to list. Defaults to changes.",49 },50 "sort": {51 "type": "string",52 "enum": ["recent", "name"],53 "description": "Sort order. Defaults to recent.",54 },55 },56 },57}5859OPENSPEC_SHOW = {60 "name": "openspec_show",61 "description": (62 "Show an OpenSpec change or spec as JSON. Use to read proposals, tasks, "63 "designs, specs, deltas, and requirement/scenario content."64 ),65 "parameters": {66 "type": "object",67 "properties": {68 "workdir": WORKDIR_PROP,69 "name": {"type": "string", "description": "Change or spec name to show."},70 "type": {71 "type": "string",72 "enum": ["change", "spec"],73 "description": "Disambiguate item type when needed.",74 },75 "deltas_only": {76 "type": "boolean",77 "description": "For changes, return only requirement deltas.",78 },79 "requirements_only": {80 "type": "boolean",81 "description": "For JSON output, omit scenarios and return requirement text only.",82 },83 "no_scenarios": {84 "type": "boolean",85 "description": "For JSON output, exclude scenario content.",86 },87 "requirement": {88 "type": "string",89 "description": "Optional 1-based requirement id to show, matching openspec --requirement.",90 },91 },92 "required": ["name"],93 },94}9596OPENSPEC_VALIDATE = {97 "name": "openspec_validate",98 "description": (99 "Validate OpenSpec artifacts. Use after editing specs/proposals/tasks or "100 "before completing kanban tasks tied to OpenSpec work."101 ),102 "parameters": {103 "type": "object",104 "properties": {105 "workdir": WORKDIR_PROP,106 "target": {107 "type": "string",108 "description": "Specific change/spec name to validate. Omit when using scope.",109 },110 "scope": {111 "type": "string",112 "enum": ["target", "all", "changes", "specs"],113 "description": "Validation scope. Defaults to target if target is supplied, otherwise all.",114 },115 "type": {116 "type": "string",117 "enum": ["change", "spec"],118 "description": "Disambiguate target type when needed.",119 },120 "strict": {121 "type": "boolean",122 "description": "Enable strict validation. Defaults to true.",123 },124 },125 },126}127128OPENSPEC_STATUS = {129 "name": "openspec_status",130 "description": (131 "Show artifact completion status for an OpenSpec change as JSON. Useful "132 "for turning proposal/task progress into kanban updates."133 ),134 "parameters": {135 "type": "object",136 "properties": {137 "workdir": WORKDIR_PROP,138 "change": {"type": "string", "description": "OpenSpec change id/name."},139 "schema": {"type": "string", "description": "Optional schema override."},140 },141 "required": ["change"],142 },143}144145OPENSPEC_INSTRUCTIONS = {146 "name": "openspec_instructions",147 "description": (148 "Return enriched OpenSpec instructions for creating an artifact or "149 "applying tasks. Use before implementing a spec-driven change."150 ),151 "parameters": {152 "type": "object",153 "properties": {154 "workdir": WORKDIR_PROP,155 "artifact": {156 "type": "string",157 "description": "Artifact/instruction name, e.g. proposal, design, tasks, specs, apply. 'spec' is accepted as an alias for 'specs'.",158 },159 "change": {"type": "string", "description": "Optional change id/name."},160 "schema": {"type": "string", "description": "Optional schema override."},161 },162 },163}164165PROJECT_PROPS = {166 "workdir": WORKDIR_PROP,167 "identifier": {168 "type": "string",169 "description": "Optional registered OpenSpec source identifier/name. Used when workdir is not supplied.",170 },171 "project": {172 "type": "string",173 "description": "Alias for identifier: a registered OpenSpec source name or token.",174 },175}176177OPENSPEC_IDEA_CREATE = {178 "name": "openspec_idea_create",179 "description": "Create a markdown idea under openspec/ideas from a title and prompt.",180 "parameters": {181 "type": "object",182 "properties": {183 **PROJECT_PROPS,184 "title": {"type": "string", "description": "Idea title."},185 "prompt": {"type": "string", "description": "Raw human/agent idea text to preserve."},186 "origin": {"type": "string", "description": "Idea origin, e.g. human, agent, or mixed."},187 "source": {"type": "string", "description": "Alias for origin."},188 "tags": {"type": "array", "items": {"type": "string"}},189 "notes": {"type": "string"},190 "slug": {"type": "string", "description": "Optional preferred file slug."},191 },192 "required": ["title", "prompt"],193 },194}195196OPENSPEC_IDEA_ENRICH = {197 "name": "openspec_idea_enrich",198 "description": "Write or update a structured enrichment report for an existing OpenSpec idea.",199 "parameters": {200 "type": "object",201 "properties": {202 **PROJECT_PROPS,203 "idea": {"type": "string", "description": "Idea slug/name under openspec/ideas without .md."},204 "slug": {"type": "string", "description": "Alias for idea."},205 "problem": {"type": "string"},206 "proposed_direction": {"type": "string"},207 "key_questions": {"type": "array", "items": {"type": "string"}},208 "feasibility": {"type": "string", "enum": ["Low", "Medium", "High"]},209 "tshirt_size": {"type": "string", "enum": ["XS", "S", "M", "L", "XL"]},210 "size_justification": {"type": "string"},211 "risks": {"type": "array", "items": {"type": "string"}},212 "suggested_next_step": {"type": "string"},213 },214 "required": ["idea"],215 },216}217218OPENSPEC_IDEA_PROMOTE = {219 "name": "openspec_idea_promote",220 "description": "Promote an existing OpenSpec idea into a new change scaffold with traceability.",221 "parameters": {222 "type": "object",223 "properties": {224 **PROJECT_PROPS,225 "idea": {"type": "string"},226 "slug": {"type": "string", "description": "Alias for idea."},227 "change": {"type": "string", "description": "Kebab-case OpenSpec change id to create."},228 "change_id": {"type": "string", "description": "Alias for change."},229 "summary": {"type": "string"},230 "force": {"type": "boolean"},231 },232 "required": ["idea", "change"],233 },234}235236OPENSPEC_TASK_LIST = {237 "name": "openspec_task_list",238 "description": "List checklist tasks for an OpenSpec change with ids, text, status, and counts.",239 "parameters": {"type": "object", "properties": {**PROJECT_PROPS, "change": {"type": "string"}, "change_id": {"type": "string"}}, "required": ["change"]},240}241242OPENSPEC_CHANGE_SEQUENCE_SET = {243 "name": "openspec_change_sequence_set",244 "description": (245 "Declare dashboard-local review order and dependency/proof relationships for active OpenSpec changes. "246 "Use after creating multiple related changes so the dashboard and later agents know the intended sequence. "247 "Stores metadata in the Hermes OpenSpec plugin DB only; it does not modify OpenSpec files."248 ),249 "parameters": {250 "type": "object",251 "properties": {252 **PROJECT_PROPS,253 "changes": {254 "type": "array",255 "items": {"type": "string"},256 "description": "Ordered change names or tokens, e.g. five prerequisite changes followed by the final proving change.",257 },258 "group_id": {"type": "string", "description": "Optional sequence group label. Defaults to 'default'."},259 "dependencies": {260 "type": "object",261 "additionalProperties": {"type": "array", "items": {"type": "string"}},262 "description": "Optional map of change name/token -> prerequisite/proving change names/tokens. Example: {final: [phase1, phase2]}.",263 },264 "depends_on": {265 "type": "object",266 "additionalProperties": {"type": "array", "items": {"type": "string"}},267 "description": "Alias for dependencies.",268 },269 },270 "required": ["changes"],271 },272}273274OPENSPEC_TASK_SET_STATUS = {275 "name": "openspec_task_set_status",276 "description": "Set selected checklist task ids in an OpenSpec change to todo or done.",277 "parameters": {278 "type": "object",279 "properties": {280 **PROJECT_PROPS,281 "change": {"type": "string"},282 "change_id": {"type": "string"},283 "tasks": {"type": "array", "items": {"type": "string"}},284 "task_ids": {"type": "array", "items": {"type": "string"}},285 "status": {"type": "string", "enum": ["todo", "done"]},286 },287 "required": ["change", "tasks", "status"],288 },289}290291OPENSPEC_CHANGE_CREATE = {292 "name": "openspec_change_create",293 "description": "Create a draft OpenSpec change scaffold directly from title/summary and optional tasks/spec placeholder.",294 "parameters": {295 "type": "object",296 "properties": {297 **PROJECT_PROPS,298 "change": {"type": "string"},299 "change_id": {"type": "string"},300 "title": {"type": "string"},301 "summary": {"type": "string"},302 "tasks": {"type": "array", "items": {"type": "string"}},303 "with_spec": {"type": "boolean"},304 "force": {"type": "boolean"},305 },306 "required": ["change", "title", "summary"],307 },308}309310OPENSPEC_CHANGE_PROMOTE = {311 "name": "openspec_change_promote",312 "description": "Promote a draft OpenSpec change to todo by ensuring tasks and a valid spec placeholder exist.",313 "parameters": {314 "type": "object",315 "properties": {**PROJECT_PROPS, "change": {"type": "string"}, "change_id": {"type": "string"}, "tasks": {"type": "array", "items": {"type": "string"}}, "replace_tasks": {"type": "boolean"}},316 "required": ["change"],317 },318}319320OPENSPEC_CHANGE_ARCHIVE = {321 "name": "openspec_change_archive",322 "description": "Archive a completed OpenSpec change, refusing incomplete tasks unless force is true.",323 "parameters": {"type": "object", "properties": {**PROJECT_PROPS, "change": {"type": "string"}, "change_id": {"type": "string"}, "force": {"type": "boolean"}}, "required": ["change"]},324}325326OPENSPEC_CHANGE_UNARCHIVE = {327 "name": "openspec_change_unarchive",328 "description": "Move an archived OpenSpec change back to the active changes directory.",329 "parameters": {"type": "object", "properties": {**PROJECT_PROPS, "change": {"type": "string"}, "change_id": {"type": "string"}, "force": {"type": "boolean"}}, "required": ["change"]},330}331332OPENSPEC_SPEC_DIFF = {333 "name": "openspec_spec_diff",334 "description": (335 "Compare a change's delta spec against its baseline, or a worktree spec "336 "against its HEAD version, and return a structured semantic delta at the "337 "requirement and scenario level plus a unified line diff fallback. "338 "Filesystem-backed — does not require the OpenSpec CLI binary. "339 "Pass 'change' to diff a change's delta spec against the baseline spec; "340 "omit 'change' to diff the current worktree spec against its HEAD version."341 ),342 "parameters": {343 "type": "object",344 "properties": {345 **PROJECT_PROPS,346 "spec": {347 "type": "string",348 "description": "Spec name (e.g. 'agent-tools') or relative path under openspec/specs/.",349 },350 "change": {351 "type": "string",352 "description": "Change id — when provided, diff the change's delta spec against the baseline. Omit for worktree-vs-HEAD comparison.",353 },354 },355 "required": ["spec"],356 },357}358359OPENSPEC_SPEC_CREATE = {360 "name": "openspec_spec_create",361 "description": (362 "Create a spec from structured input (title, purpose, requirements with "363 "scenarios) and write a properly formatted spec.md. Filesystem-backed — "364 "does not require the OpenSpec CLI binary. Pass 'change' to write a "365 "change-scoped delta spec under openspec/changes/<change>/specs/; omit to "366 "write a baseline spec under openspec/specs/. Use 'force' to overwrite an "367 "existing spec. The requirements structure matches what openspec_spec_show "368 "returns, enabling round-trip edit workflows."369 ),370 "parameters": {371 "type": "object",372 "properties": {373 **PROJECT_PROPS,374 "spec": {375 "type": "string",376 "description": "Spec name (kebab-case, e.g. 'agent-tools'). Used as the directory name.",377 },378 "change": {379 "type": "string",380 "description": "Change id — when provided, writes under openspec/changes/<change>/specs/<spec>/. Omit for baseline openspec/specs/<spec>/.",381 },382 "title": {383 "type": "string",384 "description": "Spec title (used in the '# Title' header).",385 },386 "purpose": {387 "type": "string",388 "description": "Spec purpose statement (used in the '## Purpose' section).",389 },390 "requirements": {391 "type": "array",392 "description": "Array of requirements. Each: {name, description, scenarios: [{name, steps: [{type, text}]}]}. 'type' is GIVEN/WHEN/THEN/AND/BUT.",393 "items": {394 "type": "object",395 "properties": {396 "name": {"type": "string"},397 "description": {"type": "string"},398 "scenarios": {399 "type": "array",400 "items": {401 "type": "object",402 "properties": {403 "name": {"type": "string"},404 "steps": {405 "type": "array",406 "items": {407 "type": "object",408 "properties": {409 "type": {"type": "string", "description": "GIVEN, WHEN, THEN, AND, or BUT"},410 "text": {"type": "string"},411 },412 },413 },414 },415 },416 },417 },418 },419 },420 "force": {421 "type": "boolean",422 "description": "Overwrite an existing spec at the target path. Defaults to false.",423 },424 },425 "required": ["spec", "title", "purpose", "requirements"],426 },427}428429OPENSPEC_SPEC_SHOW = {430 "name": "openspec_spec_show",431 "description": (432 "Read a spec as structured JSON (title, purpose, requirements with "433 "scenarios and steps). Filesystem-backed — does not require the OpenSpec "434 "CLI binary. Pass 'change' to read a change-scoped delta spec; omit to "435 "read a baseline spec."436 ),437 "parameters": {438 "type": "object",439 "properties": {440 **PROJECT_PROPS,441 "spec": {442 "type": "string",443 "description": "Spec name (e.g. 'agent-tools') or relative path.",444 },445 "change": {446 "type": "string",447 "description": "Change id — when provided, reads from openspec/changes/<change>/specs/. Omit for baseline openspec/specs/.",448 },449 },450 "required": ["spec"],451 },452}453454OPENSPEC_CLI = {455 "name": "openspec_cli",456 "description": (457 "Run the openspec CLI binary directly and return raw output. "458 "Use this for CLI commands that need the exact upstream JSON format "459 "(e.g. `openspec status --change <name> --json` returns applyRequires, "460 "artifacts, contextFiles, actionContext). The plugin's other tools "461 "(openspec_show, openspec_status, etc.) return different JSON shapes — "462 "use openspec_cli when you need the CLI's native output. "463 "Requires the openspec binary to be installed."464 ),465 "parameters": {466 "type": "object",467 "properties": {468 "command": {469 "type": "string",470 "description": (471 "Openspec CLI command and arguments without the binary name "472 "(e.g. 'status --change my-change', 'list --specs', "473 "'instructions apply --change my-change')."474 ),475 },476 "workdir": {477 "type": "string",478 "description": "Working directory (repo root). Defaults to current directory.",479 },480 "json_output": {481 "type": "boolean",482 "description": "If true (default), appends --json to the command and returns parsed JSON. If false, returns raw text output.",483 },484 },485 "required": ["command"],486 },487}488489OPENSPEC_SPEC_LIST = {490 "name": "openspec_spec_list",491 "description": (492 "List specs in a project. Filesystem-backed — does not require the "493 "OpenSpec CLI binary. Pass 'change' to list delta specs within a change; "494 "omit to list baseline specs."495 ),496 "parameters": {497 "type": "object",498 "properties": {499 **PROJECT_PROPS,500 "change": {501 "type": "string",502 "description": "Change id — when provided, lists specs under openspec/changes/<change>/specs/. Omit for baseline openspec/specs/.",503 },504 },505 },506}