【第2回】Kiro 仕様駆動開発 実践入門 — Spec WorkflowとHooksを使った仕様ファイルの作成・管理フロー

テックツール

前回(第1回)はKiroそのものの紹介と、仕様駆動開発ってそもそも何なのか、そして requirements / design / tasks の3点セットが生成されるところまでを触りました。で、実際に1週間くらい使ってみて何が起きたかというと、仕様ファイルを作ったあと誰も(=自分も)更新しなくなるという、わりとよくある結末でした。

コードだけ先に進んで design.md が置いてけぼり。これじゃ普通のAIコーディングと変わらないなと思って、そのへんをHooksで自動化できないか調べたのが今回の内容です。

この記事でわかること

  • Kiro IDE 1.0 での Hooksの新フォーマット(v1形式)と基本的な使い方
  • PostTaskExec / PreToolUse など、spec管理に役立つトリガーの種類
  • 仕様と実装を同期させるHooksの3つの実装例
  • 自動化の罠とタイムアウト、正規表現マッチャーの設定ポイント

まず .kiro/specs の中身をちゃんと見てみる

Hooksの話に入る前に、管理対象になる仕様ファイルの構造を整理しておきます。Kiroのspecは3フェーズのワークフローになっていて、生成物は .kiro/specs/<spec名>/ の下にまとまって置かれます。

.kiro/
├── specs/
│   └── receipt-ocr/
│       ├── requirements.md   # ユーザーストーリーと受入基準
│       ├── design.md         # 構成・図・実装上の考慮点
│       └── tasks.md          # 実行可能なタスク一覧
├── steering/
└── hooks/

requirements.md は「WHEN 〜 THE SYSTEM SHALL 〜」みたいな書き方で受入基準が並びます。最初見たとき「EARS記法っぽいな」と思ったんですが、たぶんそういう発想なんだと思います(このへん正直まだ勉強中)。

で、specには種類があって、新機能向けの Feature Spec、バグ修正向けの Bugfix Spec(こちらは requirements.md ではなく bugfix.md が出る)、それと Quick Spec があります。Feature Spec はさらに Requirements-First と Design-First の2つのワークフローに分かれていて、これは作成時に選ぶ形式です。あとから切り替えはできないようです。

Quick Spec が思ったより便利だった

Quick Spec は、最初にまとめて質問された後、requirements → design → tasks を承認ゲートなしで一気に生成してくれるモードです。出てくる成果物のフォーマットは通常のFeature Specと同じなので、あとから普通に編集できるし #spec でチャットから参照もできます。

個人的には、「仕様が自明な小機能」はQuick Spec、「自分でも要件が固まってない機能」は通常のFeature Specで一段ずつレビュー、という分け方に落ち着きました。逆にコンプライアンス絡みとか、よく知らない領域は承認ゲートありのほうがいいとドキュメントにも書かれています。まあそりゃそうですよね。

あとタスク実行まわりも地味に進化してて、Run all Tasks を押すと tasks.md の依存関係グラフを作って、独立したタスクを「波(wave)」にまとめて並列実行してくれるようです。Wave 1 が終わったら Wave 2、という感じ。CLI側からも /spec run <name> で同じことができて、.kiro/specs/ はIDEとCLIで共有されるので、CLIで作ってIDEで続き、みたいな動きも一応できます。

Hooksの新フォーマット — IDE 1.0 で別物になっている

ここが一番ハマりました。ネットの記事を読みながら設定していたら全然動かなくて、原因はHooksのフォーマットが変わっていたことでした。

IDE 1.0 / CLI 3.0 から、Hooksは .kiro/hooks/<id>.json という独立したJSONファイル形式(version: “v1″)になっています。ワークスペース単位なら .kiro/hooks/、ユーザー単位なら ~/.kiro/hooks/ に置けます。トリガー名もPascalCaseに統一されました。0.x時代の .hook ファイル(eventType / hookAction のやつ)は、移行しないと動きません。Agent Hooksパネルにアップグレード用のバッジが出るので、そこから変換する流れになります。

ついでに、0.x の Manual hook(手動実行するやつ)は廃止されて、Steering ファイルに置き換わっています。「手で起動するならそれもう steering でよくない?」という整理なんだと思います。納得はする。

基本形はこれだけです。

{
  "version": "v1",
  "hooks": [
    {
      "name": "Lint on save",
      "trigger": "PostFileSave",
      "matcher": "\\.(ts|tsx)$",
      "action": {
        "type": "command",
        "command": "npx eslint --fix"
      },
      "timeout": 30
    }
  ]
}

トリガーは SessionStart / Stop / UserPromptSubmit / PreToolUse / PostToolUse / PreTaskExec / PostTaskExec / PostFileCreate / PostFileSave / PostFileDelete など多数あります。ファイル保存系だけじゃなくて、エージェントがツールを使う前後specのタスク実行前後にも刺せるのが今回のポイントです。

アクションは2種類。command はプロジェクトルートでシェルコマンドを実行して、セッションのコンテキストがJSONでSTDINに流れてきます。agent は今の会話にプロンプトを注入して、エージェント自身に作業させるやつ。「テストを書き直して」みたいな曖昧な指示は後者、機械的な処理は前者、という使い分けになります。

実装例:仕様管理を自動化する3つのHook

1. 仕様を保存したら tasks.md との差分を見てもらう

冒頭の「design.md が置いてけぼり」問題への対処です。仕様を編集して保存したタイミングで、エージェントに整合性を確認させます。

{
  "version": "v1",
  "hooks": [
    {
      "name": "spec-sync-check",
      "trigger": "PostFileSave",
      "matcher": "\\.kiro/specs/.+/(requirements|design)\\.md$",
      "action": {
        "type": "agent",
        "prompt": "保存された仕様ファイルと同じspec配下のtasks.mdを読み、追加・変更された要件に対応するタスクが不足していないか確認して。不足があれば差分だけを提案する(勝手に書き換えない)。"
      }
    }
  ]
}

「勝手に書き換えない」を入れているのは、書き換えられると PostFileSave がまた発火して無限ループになりかけたからです。ここ注意ポイントで、ファイルを書くアクションを保存トリガーに紐づけるときはmatcherを相当絞ったほうがいいです。自分は最初 \.md$ でやって、3周くらいしたところで慌てて止めました。

2. 危険な書き込みをブロックする(Hooksによるガードレール)

PreToolUse / PreTaskExec / UserPromptSubmit は処理をブロックできるのが特徴で、commandアクションが exit code 2 を返すと実行が止まり、STDERRの内容がエージェントに返されます。つまりガードレールが作れる。

{
  "version": "v1",
  "hooks": [{
    "name": "protect-migrations",
    "trigger": "PreToolUse",
    "matcher": "fs_write",
    "action": {
      "type": "command",
      "command": "python3 .kiro/scripts/guard.py"
    }
  }]
}

ガード用のPythonスクリプト(.kiro/scripts/guard.py)は、STDINから受け取ったJSON形式のイベント情報をチェックします。

import json, sys

ev = json.load(sys.stdin)
path = ev.get("tool_input", {}).get("path", "")

if "/migrations/" in path or path.endswith(".env"):
    print("このパスは自動編集禁止。人間が書くこと。", file=sys.stderr)
    sys.exit(2)

exit 0 なら成功(STDOUTはユーザーには表示されない)、exit 2 なら(PreToolUse / UserPromptSubmit / PreTaskExec の場合)実行をブロック、それ以外の異常終了は警告扱いになります。マイグレーションと .env を守れるだけでも、自動実行に対する心理的なハードルはだいぶ下がりました。

3. タスク完了後にテストを流す

PostTaskExec は spec のタスクが1つ終わるたびに走ります。ここで pytest を回しておくと、全タスク終わってから「実は3つ目から壊れてた」を防げます。

{
  "version": "v1",
  "hooks": [{
    "name": "test-after-task",
    "trigger": "PostTaskExec",
    "action": {
      "type": "command",
      "command": "python -m pytest -q --timeout=20 || true"
    },
    "timeout": 120
  }]
}

末尾の || true は、テストが落ちたときに「hook失敗」の警告が毎回出るのが鬱陶しかったので付けています。ここは本来 exit code をちゃんと使ってエージェントに失敗を伝えたほうが正しい気がしていて、もっといいやり方がある気がしてます。

ハマったところメモ

  • timeout のデフォルトは60秒(IDE)/ 30秒(CLI)。ビルドやテストを回すHookはだいたい足りません。素直に伸ばす。
  • matcherは正規表現。globのつもりで *.py と書いて動かず、しばらく悩みました(お恥ずかしい)。
  • 自然言語でHookを作れるが、生成結果は必ず目視で確認する。Agent Hooksパネルで「TypeScriptを保存したらlint」と書けばJSONを作ってくれますが、matcherの正規表現とtimeoutは実際のプロジェクト規模と噛み合わないことがあります。
  • CLI側の旧設定(agent config に埋め込む camelCase の agentSpawn 等)とIDEのPascalCaseが検索結果に混在してるので、記事を読むときはバージョンを先に確認したほうが早いです。

余談ですが、~/.kiro/hooks/ のほうに置いたガードレール系Hookを dotfiles リポジトリに入れておいたら、別マシンに移ったときもそのまま効いて地味に感動しました。プロジェクト固有のものは .kiro/hooks/ でGit管理、共通の安全装置はユーザー側、という分け方が今のところしっくりきています。

まとめ

仕様ファイルは作るより腐らせないほうが難しいというのが正直な感想で、Hooksはそこを機械的に支えてくれる仕組みでした。特に PreTaskExec / PostTaskExec のような spec と直結したトリガーが入ったことで、「仕様 → タスク → 検証」のループをKiroの中で閉じられるようになったのは大きいと思います。

次のステップとしてはSteering ファイルを掘り進める予定です。プロジェクト固有のルールをどこまでsteeringに寄せて、どこからHookで強制するか、その線引きがまだ自分の中で決まっていません。agentアクションでエージェントに注意させるのと、commandアクションで exit 2 して物理的に止めるのと、どっちを標準にすべきなんでしょうね。使ってる人の運用、ちょっと聞いてみたいです。

📚 シリーズ「Kiro 仕様駆動開発 実践入門」(第2回 / 全4回)

← 前回の記事: 前回の記事はこちら

→ 次回の記事: 【第3回】Kiro 仕様駆動開発 実践入門 — SteeringとAgentモードでPythonプロジェクトのタスクを自動化する

参考になったらクリックしてもらえると嬉しいです!

Blogmura NetDev Ranking
タイトルとURLをコピーしました