Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/specify_cli/integrations/kimi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from __future__ import annotations

import shutil
from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -61,6 +62,34 @@ def post_process_skill_content(self, content: str) -> str:
"""Ensure in-skill cross-command references use Kimi's `/skill:` syntax."""
return super().post_process_skill_content(content)

def build_exec_args(
self,
prompt: str,
*,
model: str | None = None,
output_json: bool = True,
integration_args: Sequence[str] | None = None,
integration_options: Mapping[str, Any] | None = None,
project_root: Path | None = None,
) -> list[str] | None:
"""Build CLI arguments for non-interactive ``kimi`` execution.

``SkillsIntegration``'s default requests ``--output-format json``,
which Kimi Code CLI does not accept: the option only allows ``text``
and ``stream-json``, so the CLI exits 1 before the prompt runs.
Structured output maps to ``stream-json`` (JSONL events) instead.
"""
self.validate_runtime_config(integration_args, integration_options)
if not self.config or not self.config.get("requires_cli"):
return None
args = [self._resolve_executable(), "-p", prompt]
self._apply_extra_args_env_var(args)
if model:
args.extend(["--model", model])
if output_json:
args.extend(["--output-format", "stream-json"])
return args

@classmethod
def options(cls) -> list[IntegrationOption]:
return [
Expand Down
64 changes: 64 additions & 0 deletions tests/integrations/test_integration_kimi.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,70 @@ class TestKimiIntegration(SkillsIntegrationTests):
COMMANDS_SUBDIR = "skills"
REGISTRAR_DIR = ".kimi-code/skills"

def test_build_exec_args_uses_prompt_flag(self):
integration = get_integration("kimi")

args = integration.build_exec_args(
"/skill:speckit-specify build a login page",
output_json=False,
)

assert args == ["kimi", "-p", "/skill:speckit-specify build a login page"]

def test_build_exec_args_requests_stream_json(self):
"""Kimi only accepts `text` and `stream-json`; `json` exits 1 before the prompt runs."""
integration = get_integration("kimi")

args = integration.build_exec_args(
"/skill:speckit-plan add OAuth", output_json=True
)

assert args == [
"kimi",
"-p",
"/skill:speckit-plan add OAuth",
"--output-format",
"stream-json",
]
assert "json" not in args

def test_build_exec_args_forwards_model(self):
integration = get_integration("kimi")

args = integration.build_exec_args(
"/skill:speckit-specify build a login page",
model="kimi-code/k3",
output_json=False,
)

assert args == [
"kimi",
"-p",
"/skill:speckit-specify build a login page",
"--model",
"kimi-code/k3",
]

def test_build_exec_args_applies_extra_args(self, monkeypatch):
monkeypatch.setenv("SPECKIT_INTEGRATION_KIMI_EXTRA_ARGS", "--add-dir ../shared")
integration = get_integration("kimi")

args = integration.build_exec_args(
"check the build", model="kimi-code/k3", output_json=True
)

assert args == [
"kimi",
"-p",
"check the build",
"--add-dir",
"../shared",
"--model",
"kimi-code/k3",
"--output-format",
"stream-json",
]


class TestKimiOptions:
"""Kimi declares --skills and --migrate-legacy options."""
Expand Down
Loading