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
3 changes: 2 additions & 1 deletion docs/integrations/pi.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Invoke them in the agent's chat using dotted notation, for example
## Workflow Dispatch and Tooling

Workflow steps use `pi -p <prompt>`, with `--model` when configured and
`--output-format json` when structured output is requested.
`--mode json` (JSONL events), not `--output-format`, when structured output is
requested.
There are no integration-specific setup options.

This integration scaffolds prompt files; it does not install or configure MCP
Expand Down
34 changes: 34 additions & 0 deletions src/specify_cli/integrations/pi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
"""Pi Coding Agent integration."""

from __future__ import annotations

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

from ..base import MarkdownIntegration


Expand All @@ -19,3 +25,31 @@ class PiIntegration(MarkdownIntegration):
"extension": ".md",
}
multi_install_safe = True

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 ``pi`` execution.

``MarkdownIntegration``'s default requests ``--output-format json``,
which Pi does not define: it rejects the unknown option and exits 1
before the prompt runs. Pi selects JSONL events with ``--mode json``,
the same flag the OMP integration already passes.
"""
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(["--mode", "json"])
return args
56 changes: 56 additions & 0 deletions tests/integrations/test_integration_pi.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,59 @@ def test_multi_install_safe(self):
# every other integration, so it must be co-install safe (mirrors
# qwen/shai/qodercli and the kiro-cli #3471 precedent).
assert get_integration(self.KEY).multi_install_safe is True

def test_build_exec_args_uses_prompt_flag(self):
integration = get_integration(self.KEY)

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

assert args == ["pi", "-p", "/speckit.specify build a login page"]

def test_build_exec_args_requests_json_mode(self):
"""Pi has no `--output-format`; JSONL events are selected with `--mode json`."""
integration = get_integration(self.KEY)

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

assert args == ["pi", "-p", "/speckit.plan add OAuth", "--mode", "json"]
assert "--output-format" not in args

def test_build_exec_args_forwards_model(self):
integration = get_integration(self.KEY)

args = integration.build_exec_args(
"/speckit.specify build a login page",
model="openai/gpt-4o",
output_json=False,
)

assert args == [
"pi",
"-p",
"/speckit.specify build a login page",
"--model",
"openai/gpt-4o",
]

def test_build_exec_args_applies_extra_args(self, monkeypatch):
monkeypatch.setenv("SPECKIT_INTEGRATION_PI_EXTRA_ARGS", "--thinking high")
integration = get_integration(self.KEY)

args = integration.build_exec_args(
"check the build", model="openai/gpt-4o", output_json=True
)

assert args == [
"pi",
"-p",
"check the build",
"--thinking",
"high",
"--model",
"openai/gpt-4o",
"--mode",
"json",
]
Loading