Skip to content
Open
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
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all lint test format check
.PHONY: all lint test format check install-kiro

all: lint test

Expand All @@ -13,3 +13,6 @@ test:

check: lint test
@echo "All checks passed"

install-kiro:
uv run python3 clients/kiro/install.py
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,32 @@ sub-agent. Verify with `/plugin list`, `/mcp` (expect `sdpm` connected), and `/a
skill drives briefing → outline → art direction, then delegates Phase 2 (compose) to parallel
composer sub-agents, and finishes with review.

> **Which entry point?** **Kiro CLI** → use the Layer 1 skill. **Claude Desktop / other MCP
> clients** → use the Layer 2 local MCP server. **Claude Code** → use this plugin (it wraps the
> same Layer 2 MCP server with a CC-native skill + parallel compose sub-agent).
> **Which entry point?** **Kiro CLI** → `make install-kiro` (next section). **Claude Desktop /
> other MCP clients** → use the Layer 2 local MCP server. **Claude Code** → use this plugin (it
> wraps the same Layer 2 MCP server with a CC-native skill + parallel compose sub-agent).

### 🛠 Kiro CLI setup (skill + parallel compose sub-agents)

Kiro CLI users get the same flow — the `sdpm-vibe` skill drives Phase 1, then dispatches
parallel `sdpm-composer` sub-agents for Phase 2 — via one make target. The same
prerequisites apply: [`uv`](https://docs.astral.sh/uv/) on your `PATH`, plus **LibreOffice**
and **poppler** for slide previews.

```bash
git clone https://github.com/aws-samples/sample-spec-driven-presentation-maker.git
cd sample-spec-driven-presentation-maker
make install-kiro
kiro-cli chat # then just ask: "make slides about ..."
```

`make install-kiro` symlinks the skills into `~/.kiro/skills/`, generates the composer
agent config at `~/.kiro/agents/sdpm-composer.json`, and registers the `sdpm` local MCP
server in the global `~/.kiro/settings/mcp.json` (pass `--agent NAME` to
`clients/kiro/install.py` to target a specific agent config instead). It is safe to re-run.

**Updating:** `git pull` in the checkout is enough — skills are symlinks and the composer
prompt is a `file://` reference into the checkout, so no reinstall is needed. Re-run
`make install-kiro` only if you move the checkout to a different path.

### 🚀 One-Click Deploy — Just an AWS Account to Get Started

Expand Down
27 changes: 25 additions & 2 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,33 @@ Claude Code ユーザーはプラグインで一括導入できます(手動
art direction を進め、Phase 2(compose)を複数の composer サブエージェントへ並列委譲し、
review まで実施します。

> **どの入口を使う?** **Kiro CLI** → Layer 1 の skill。**Claude Desktop / その他 MCP
> クライアント** → Layer 2 のローカル MCP サーバー。**Claude Code** → このプラグイン(同じ
> **どの入口を使う?** **Kiro CLI** → `make install-kiro`(次節)。**Claude Desktop / その他
> MCP クライアント** → Layer 2 のローカル MCP サーバー。**Claude Code** → このプラグイン(同じ
> Layer 2 MCP サーバーを CC ネイティブの skill + 並列 compose サブエージェントで包んだもの)。

### 🛠 Kiro CLI セットアップ(skill + 並列 compose サブエージェント)

Kiro CLI ユーザーも同じフロー — `sdpm-vibe` skill が Phase 1 を進め、Phase 2 を
`sdpm-composer` サブエージェントへ並列委譲 — を make ターゲット 1 つで導入できます。
事前インストールは同じく [`uv`](https://docs.astral.sh/uv/)(`PATH` に)、および
スライドプレビュー用の **LibreOffice** と **poppler** です。

```bash
git clone https://github.com/aws-samples/sample-spec-driven-presentation-maker.git
cd sample-spec-driven-presentation-maker
make install-kiro
kiro-cli chat # あとは「〇〇のスライドを作って」と頼むだけ
```

`make install-kiro` は skill を `~/.kiro/skills/` へ symlink し、composer エージェント設定
`~/.kiro/agents/sdpm-composer.json` を生成し、`sdpm` ローカル MCP サーバーをグローバル
`~/.kiro/settings/mcp.json` に登録します(特定のエージェント設定に追加したい場合は
`clients/kiro/install.py --agent NAME` を直接実行)。再実行しても安全です。

**更新:** チェックアウトで `git pull` するだけで反映されます — skill は symlink、composer
プロンプトはチェックアウトへの `file://` 参照のため再インストール不要です。チェックアウトを
別パスへ移動した場合のみ `make install-kiro` を再実行してください。

### 🚀 AWS アカウントだけですぐに開始! ワンクリックデプロイ

| リージョン | デプロイ |
Expand Down
180 changes: 180 additions & 0 deletions clients/kiro/install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""Install sdpm support for Kiro CLI.

Idempotent installer that wires this repository checkout into Kiro CLI:

1. Symlinks the skills under ``skills/`` (sdpm-vibe, sdpm-spec, ...) into
``~/.kiro/skills/`` so Kiro CLI discovers them.
2. Renders ``clients/kiro/sdpm-composer.json.tmpl`` (resolving ``{{CHECKOUT}}``
to this checkout's absolute path) into ``~/.kiro/agents/sdpm-composer.json``
so the sdpm-vibe skill can dispatch composer sub-agents via the subagent tool.
3. Registers the ``sdpm`` MCP server in the global ``~/.kiro/settings/mcp.json``
via ``kiro-cli mcp add`` (skipped if already registered). Use ``--agent NAME``
to register it into a specific agent config instead.

The repository stays the single source of truth: skills are symlinks and the
composer prompt is a ``file://`` reference, so ``git pull`` updates take effect
without re-running this script. Re-run only if you move the checkout.
"""

from __future__ import annotations

import argparse
import json
import shutil
import subprocess
import sys
from pathlib import Path

HERE = Path(__file__).resolve().parent
REPO_ROOT = HERE.parent.parent
SKILLS_SRC = REPO_ROOT / "skills"
TEMPLATE = HERE / "sdpm-composer.json.tmpl"

KIRO_HOME = Path.home() / ".kiro"
SKILLS_DEST = KIRO_HOME / "skills"
AGENTS_DEST = KIRO_HOME / "agents"
GLOBAL_MCP_JSON = KIRO_HOME / "settings" / "mcp.json"

MCP_SERVER_NAME = "sdpm"


def info(msg: str) -> None:
print(f" {msg}")


def warn(msg: str) -> None:
print(f" WARNING: {msg}", file=sys.stderr)


def symlink_skills() -> None:
"""Symlink each skill directory under skills/ into ~/.kiro/skills/."""
print("[1/3] Linking skills into ~/.kiro/skills/")
SKILLS_DEST.mkdir(parents=True, exist_ok=True)
for src in sorted(SKILLS_SRC.iterdir()):
if not src.is_dir() or not (src / "SKILL.md").is_file():
continue
link = SKILLS_DEST / src.name
if link.is_symlink():
if link.resolve() == src.resolve():
info(f"{link} -> {src} (already linked, skipped)")
continue
link.unlink()
link.symlink_to(src)
info(f"{link} -> {src} (re-pointed stale symlink)")
elif link.exists():
warn(
f"{link} exists and is not a symlink — left untouched. "
f"Remove it and re-run to link {src}."
)
else:
link.symlink_to(src)
info(f"{link} -> {src} (linked)")


def render_composer_agent() -> Path:
"""Render the composer agent config template into ~/.kiro/agents/."""
print("[2/3] Generating ~/.kiro/agents/sdpm-composer.json")
rendered = TEMPLATE.read_text(encoding="utf-8").replace(
"{{CHECKOUT}}", str(REPO_ROOT)
)
config = json.loads(rendered) # validate JSON before writing
AGENTS_DEST.mkdir(parents=True, exist_ok=True)
dest = AGENTS_DEST / f"{config['name']}.json"
if dest.exists() and dest.read_text(encoding="utf-8") == rendered:
info(f"{dest} (up to date, skipped)")
else:
dest.write_text(rendered, encoding="utf-8")
info(f"{dest} (written)")
return dest


def _mcp_server_registered(config_path: Path) -> bool:
"""Return True if the sdpm MCP server is present in the given config file."""
try:
data = json.loads(config_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return False
return MCP_SERVER_NAME in data.get("mcpServers", {})


def register_mcp_server(kiro_cli: str | None, agent: str | None) -> None:
"""Register the sdpm MCP server via `kiro-cli mcp add` (idempotent)."""
target = f"agent '{agent}'" if agent else "global ~/.kiro/settings/mcp.json"
print(f"[3/3] Registering MCP server '{MCP_SERVER_NAME}' in {target}")

config_path = AGENTS_DEST / f"{agent}.json" if agent else GLOBAL_MCP_JSON
if _mcp_server_registered(config_path):
info(f"'{MCP_SERVER_NAME}' already registered in {config_path} (skipped)")
return

if kiro_cli is None:
warn(
"kiro-cli not found on PATH — skipped MCP registration. "
"Install Kiro CLI and re-run `make install-kiro` (files above were "
"still generated)."
)
return

args = ["run", "--directory", str(REPO_ROOT / "mcp-local"), "python", "server.py"]
cmd = [
kiro_cli,
"mcp",
"add",
"--name",
MCP_SERVER_NAME,
"--command",
"uv",
"--args",
json.dumps(args),
"--timeout",
"120000",
]
if agent:
cmd += ["--agent", agent]
else:
cmd += ["--scope", "global"]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
warn(f"`{' '.join(cmd)}` failed:\n{result.stderr.strip() or result.stdout.strip()}")
return
info(f"'{MCP_SERVER_NAME}' registered in {config_path}")


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument(
"--agent",
metavar="NAME",
help=(
"register the sdpm MCP server into ~/.kiro/agents/NAME.json instead "
"of the global ~/.kiro/settings/mcp.json"
),
)
opts = parser.parse_args()

print(f"Installing sdpm for Kiro CLI (checkout: {REPO_ROOT})\n")
kiro_cli = shutil.which("kiro-cli")
if kiro_cli is None:
warn("kiro-cli not found on PATH — file generation will proceed anyway.")

symlink_skills()
render_composer_agent()
register_mcp_server(kiro_cli, opts.agent)

print(
"\nDone. Usage:\n"
" 1. Make sure `uv` and LibreOffice/poppler are installed (previews need them).\n"
" 2. Start a NEW session: kiro-cli chat\n"
' 3. Ask for slides, e.g. "このURLをスライドにして https://..." — the sdpm-vibe\n'
" skill runs Phase 1 and dispatches sdpm-composer sub-agents for Phase 2.\n"
"\n"
"Updates: `git pull` in this checkout is enough (skills are symlinks; the\n"
"composer prompt is a file:// reference). Re-run `make install-kiro` only if\n"
"you move the checkout."
)
return 0


if __name__ == "__main__":
sys.exit(main())
31 changes: 31 additions & 0 deletions clients/kiro/sdpm-composer.json.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "sdpm-composer",
"description": "sdpm slide composer (dispatched by the sdpm-vibe skill). Composes assigned slides from approved specs via the sdpm MCP server. No user interaction.",
"prompt": "file://{{CHECKOUT}}/agents/sdpm-composer.md",
"mcpServers": {
"sdpm": {
"command": "uv",
"args": [
"run",
"--directory",
"{{CHECKOUT}}/mcp-local",
"python",
"server.py"
],
"timeout": 120000
}
},
"tools": [
"read",
"glob",
"grep",
"@sdpm"
],
"allowedTools": [
"read",
"glob",
"grep",
"@sdpm"
],
"useLegacyMcpJson": false
}
22 changes: 17 additions & 5 deletions skills/sdpm-vibe/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ server** (its tools are exposed as `mcp__plugin_sdpm_sdpm__*` when installed as
hearing and without per-step approval. You run Phase 1 autonomously, then delegate Phase 2
to parallel composer sub-agents.

> Ported from the ACP `vibe-agent.md`. Only Claude Code reads this file. The shared workflows
> Ported from the ACP `vibe-agent.md`. Only SKILL.md-driven clients (Claude Code, Kiro CLI)
> read this file. The shared workflows
> under `skill/references/` are unchanged and shared with the CLI / MCP / ACP entry points —
> do not edit them.

Expand Down Expand Up @@ -129,8 +130,10 @@ one message.
After Step 5, **`specs/art-direction.html` and `deck.json` are FROZEN.**

### Step 6 — Compose (DELEGATE to parallel composer sub-agents)
Prerequisite: Steps 2–5 complete. Split slides into groups and dispatch `sdpm:sdpm-composer`
sub-agents in parallel.
Prerequisite: Steps 2–5 complete. Split slides into groups and dispatch composer
sub-agents in parallel. How you dispatch depends on your client (see **Dispatch** below):
- **Task tool available (Claude Code):** dispatch `sdpm:sdpm-composer` sub-agents.
- **subagent tool available (Kiro CLI):** dispatch stages with role `sdpm-composer`.

**Group assignment (small groups, keep design-coupled slides together):**
- **Step 1 — keep design-coupled slides in ONE group:** override-inherited slides (same slug
Expand All @@ -142,8 +145,17 @@ sub-agents in parallel.
- **Parallelism cap: up to 10 agents in parallel** (Claude Code handles 10 concurrent Tasks
safely). If there are more than 10 groups, dispatch in successive waves of ≤10.

**Dispatch:** invoke one `sdpm:sdpm-composer` per group, **all in a single message** (up to 10)
so they run in parallel. Per-group prompt template:
**Dispatch — client with the Task tool (Claude Code):** invoke one `sdpm:sdpm-composer` per
group, **all in a single message** (up to 10) so they run in parallel.

**Dispatch — client with the subagent tool (Kiro CLI):** invoke the `subagent` tool once with
one stage per group (no `depends_on`, so all stages start in parallel), each stage using
`role: "sdpm-composer"` and the same per-group prompt template below as its `prompt_template`.
If role `sdpm-composer` is **not** in the subagent tool's role list, STOP — do not improvise
with another role and do not retry: tell the user to run `make install-kiro` in the
repository checkout and restart the session (the role list is fixed at session start).

Per-group prompt template (common to both clients):

```
deck_id=<ABSOLUTE deck path>.
Expand Down
Loading