Files
github-spec-kit/src/specify_cli/integrations/vibe/__init__.py
Puneet Dixit 5a678c552e Share skills hook note post-processing (#2679)
* fix(integrations): share skills hook note post-processing

* fix(integrations): tighten skill post-processing

Apply skill content post-processing before the initial write, use an exact hook-note sentinel for idempotence, and route Copilot skill post-processing through the shared helper before adding mode frontmatter.

* Make hook note injection per instruction

* Deduplicate Codex hook note processing

---------

Co-authored-by: Puneet Dixit <236133619+puneetdixit200@users.noreply.github.com>
Co-authored-by: Puneet Dixit <puneetdixit200@users.noreply.github.com>
2026-05-28 09:08:48 -05:00

111 lines
3.4 KiB
Python

"""
Mistral Vibe CLI integration — skills-based agent.
Vibe uses ``.vibe/skills/speckit-<name>/SKILL.md`` layout (enforced since v2.0.0).
"""
from __future__ import annotations
from pathlib import Path
from typing import Any
from ..base import IntegrationOption, SkillsIntegration
from ..manifest import IntegrationManifest
class VibeIntegration(SkillsIntegration):
key = "vibe"
config = {
"name": "Mistral Vibe",
"folder": ".vibe/",
"commands_subdir": "skills",
"install_url": "https://github.com/mistralai/mistral-vibe",
"requires_cli": True,
}
registrar_config = {
"dir": ".vibe/skills",
"format": "markdown",
"args": "$ARGUMENTS",
"extension": "/SKILL.md",
}
context_file = "AGENTS.md"
@classmethod
def options(cls) -> list[IntegrationOption]:
return [
IntegrationOption(
"--skills",
is_flag=True,
default=True,
help="Install as agent skills",
),
]
@staticmethod
def _inject_frontmatter_flag(content: str, key: str, value: str = "true") -> str:
"""
Insert ``key: value`` before the closing ``---`` if not already present.
Value: true by default
"""
lines = content.splitlines(keepends=True)
# Pre-scan: bail out if already present in frontmatter
dash_count = 0
for line in lines:
stripped = line.rstrip("\n\r")
if stripped == "---":
dash_count += 1
if dash_count == 2:
break
continue
if dash_count == 1 and stripped.startswith(f"{key}:"):
return content
# Inject before the closing --- of frontmatter
out: list[str] = []
dash_count = 0
injected = False
for line in lines:
stripped = line.rstrip("\n\r")
if stripped == "---":
dash_count += 1
if dash_count == 2 and not injected:
if line.endswith("\r\n"):
eol = "\r\n"
elif line.endswith("\n"):
eol = "\n"
else:
eol = ""
out.append(f"{key}: {value}{eol}")
injected = True
out.append(line)
return "".join(out)
def post_process_skill_content(self, content: str) -> str:
"""
Inject shared hook guidance and Vibe-specific frontmatter flags:
- user-invocable: allows the skill to be invoked by the user (not just other agents)
"""
updated = super().post_process_skill_content(content)
updated = self._inject_frontmatter_flag(updated, "user-invocable")
return updated
def setup(
self,
project_root: Path,
manifest: IntegrationManifest,
parsed_options: dict[str, Any] | None = None,
**opts: Any,
) -> list[Path]:
"""Install Vibe skills then inject Vibe-specific flags"""
import click
click.secho(
"Warning: The .vibe/skills layout requires Mistral Vibe v2.0.0 or newer. "
"Please ensure your installation is up to date.",
fg="yellow",
err=True,
)
return super().setup(project_root, manifest, parsed_options=parsed_options, **opts)