From 2e891864ec5d80639a7e33d68067914ffa00f374 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 31 Oct 2026 14:01:28 +0000 Subject: [PATCH 1/5] =?UTF-8?q?Distribution:=20agy=E3=83=BBkiro=20?= =?UTF-8?q?=E3=81=B8=E3=81=AE=E5=B0=8E=E5=85=A5=E7=89=A9=E3=81=A8=E3=80=81?= =?UTF-8?q?=E3=81=9D=E3=81=AE=E7=94=9F=E6=88=90=E3=83=BB=E6=A4=9C=E6=9F=BB?= =?UTF-8?q?=E3=83=BB=E3=82=B9=E3=83=A2=E3=83=BC=E3=82=AF=E3=83=BB=E3=83=AA?= =?UTF-8?q?=E3=83=AA=E3=83=BC=E3=82=B9=E3=81=AE=E7=A2=BA=E8=AA=8D=E3=82=92?= =?UTF-8?q?=E5=A4=96=E3=81=99=EF=BC=88#2231=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 配布先を Claude Code と codex に限る。agy・kiro は cross-review / cross-refactoring の 相手役として起動するだけにする(設計の決定 1)。 - plugins/ndf/dev.agy・各プラグインの dev.kiro・agy-skills.txt・kiro-skills.txt を消す - build-runtime-plugins.sh・validate-runtime-plugins.sh・validate_manifests.py から agy・kiro の生成と検査を外す - 一覧の予算・文書の古さの検査・スモーク(matrix・adapter・像・版の一覧)から kiro・agy を外す - release.runtimes・pace.json の導入の確認・release-verification-steps.py の kiro の導入確認を外す - .ndf/project.json の required_checks から runtime-smoke (kiro)・(agy) を外す(ruleset は承認ゲート 2) - 配布先が claude・codex だけであることを確かめる test_distribution_runtimes.py を足す Co-Authored-By: Claude Opus 5.5 --- .../runtime-plugin-authenticated-smoke.yml | 2 - .../workflows/runtime-plugin-smoke-latest.yml | 2 +- .github/workflows/runtime-plugin-smoke.yml | 2 +- .ndf/pace.json | 4 +- .ndf/project.json | 4 +- .ndf/supervise.json | 2 +- plugins/mcp/mcp-aws-docs/dev.kiro/install.sh | 155 ----- plugins/mcp/mcp-bigquery/dev.kiro/install.sh | 155 ----- .../mcp-chrome-devtools/dev.kiro/install.sh | 155 ----- plugins/mcp/mcp-dbhub/dev.kiro/install.sh | 155 ----- plugins/mcp/mcp-devin/dev.kiro/install.sh | 155 ----- .../mcp/mcp-markitdown/dev.kiro/install.sh | 155 ----- plugins/mcp/mcp-notion/dev.kiro/install.sh | 155 ----- .../mcp/mcp-playwright/dev.kiro/install.sh | 155 ----- plugins/mcp/mcp-redash/dev.kiro/install.sh | 155 ----- plugins/mcp/mcp-serena/dev.kiro/install.sh | 155 ----- .../mcp-serena/tests/test_serena_lsp_kiro.py | 21 - plugins/ndf/dev.agy/agents/code-reviewer.md | 111 ---- plugins/ndf/dev.agy/agents/corder.md | 123 ---- plugins/ndf/dev.agy/agents/data-analyst.md | 117 ---- plugins/ndf/dev.agy/agents/debugger.md | 107 ---- plugins/ndf/dev.agy/agents/devops-engineer.md | 106 ---- plugins/ndf/dev.agy/agents/director.md | 431 -------------- plugins/ndf/dev.agy/agents/qa.md | 156 ----- plugins/ndf/dev.agy/agents/researcher.md | 147 ----- .../ndf/dev.agy/agents/supervisor-waits.md | 20 - plugins/ndf/dev.agy/agents/supervisor.md | 18 - plugins/ndf/dev.agy/agents/worker.md | 28 - plugins/ndf/dev.agy/hooks.json | 23 - plugins/ndf/dev.agy/install-hooks.sh | 184 ------ plugins/ndf/dev.agy/plugin.json | 5 - plugins/ndf/dev.agy/scripts | 1 - plugins/ndf/dev.agy/skills/backlog-refinement | 1 - plugins/ndf/dev.agy/skills/cherry-pick-pr | 1 - plugins/ndf/dev.agy/skills/chrome-cdp | 1 - plugins/ndf/dev.agy/skills/cross-refactoring | 1 - plugins/ndf/dev.agy/skills/cross-review | 1 - plugins/ndf/dev.agy/skills/decision-request | 1 - plugins/ndf/dev.agy/skills/deploy | 1 - plugins/ndf/dev.agy/skills/design | 1 - .../ndf/dev.agy/skills/development-workflow | 1 - .../dev.agy/skills/docker-container-access | 1 - plugins/ndf/dev.agy/skills/document-drafting | 1 - plugins/ndf/dev.agy/skills/document-rebuild | 1 - plugins/ndf/dev.agy/skills/document-sources | 1 - plugins/ndf/dev.agy/skills/document-style | 1 - plugins/ndf/dev.agy/skills/document-systems | 1 - plugins/ndf/dev.agy/skills/estimate | 1 - plugins/ndf/dev.agy/skills/external-ai | 1 - plugins/ndf/dev.agy/skills/fix | 1 - plugins/ndf/dev.agy/skills/google-workspace | 1 - .../ndf/dev.agy/skills/implementation-plan | 1 - .../ndf/dev.agy/skills/investigation-rules | 1 - .../ndf/dev.agy/skills/issue-plan-strategy | 1 - plugins/ndf/dev.agy/skills/layout-review | 1 - plugins/ndf/dev.agy/skills/logging-guidelines | 1 - plugins/ndf/dev.agy/skills/merged | 1 - plugins/ndf/dev.agy/skills/ml-model-structure | 1 - plugins/ndf/dev.agy/skills/ndf-policies | 1 - plugins/ndf/dev.agy/skills/notion-writing | 1 - plugins/ndf/dev.agy/skills/out-of-scope | 1 - plugins/ndf/dev.agy/skills/plan-to-spec | 1 - plugins/ndf/dev.agy/skills/pr | 1 - plugins/ndf/dev.agy/skills/pr-review | 1 - plugins/ndf/dev.agy/skills/pr-tests | 1 - plugins/ndf/dev.agy/skills/problem-solving | 1 - plugins/ndf/dev.agy/skills/progress-tracking | 1 - plugins/ndf/dev.agy/skills/qa-security-scan | 1 - plugins/ndf/dev.agy/skills/quality-gates | 1 - plugins/ndf/dev.agy/skills/refactoring | 1 - plugins/ndf/dev.agy/skills/release | 1 - .../ndf/dev.agy/skills/release-verification | 1 - .../ndf/dev.agy/skills/requirements-design | 1 - plugins/ndf/dev.agy/skills/retrospective | 1 - plugins/ndf/dev.agy/skills/skill-stats | 1 - plugins/ndf/dev.agy/skills/tdd-cycle | 1 - plugins/ndf/dev.agy/skills/worktree | 1 - plugins/ndf/dev.kiro/agents/ndf.json.template | 18 - plugins/ndf/dev.kiro/install.sh | 561 ------------------ plugins/ndf/dev.kiro/prompts/codex.md | 4 - plugins/ndf/dev.kiro/prompts/fix.md | 3 - plugins/ndf/dev.kiro/prompts/merged.md | 3 - plugins/ndf/dev.kiro/prompts/pr-review.md | 3 - plugins/ndf/dev.kiro/prompts/pr-tests.md | 3 - plugins/ndf/dev.kiro/prompts/pr.md | 3 - plugins/ndf/docs/kiro-cli.md | 104 ---- plugins/ndf/manifests/agy-skills.txt | 45 -- plugins/ndf/manifests/kiro-skills.txt | 46 -- plugins/ndf/scripts/hook_lib/worktree.py | 3 +- plugins/ndf/scripts/progress-record.sh | 4 +- plugins/ndf/scripts/release-steps.py | 6 +- .../ndf/scripts/release-verification-steps.py | 91 +-- .../ndf/scripts/tests/test_hook_deadline.py | 8 +- .../tests/test_progress_record_stages.py | 9 - .../ndf/scripts/tests/test_step_scripts.py | 45 +- .../skills/worktree/tests/test_agy_hooks.py | 276 --------- .../skills/worktree/tests/test_hook_wiring.py | 9 - plugins/playwright-kit/dev.kiro/install.sh | 139 ----- .../playwright-kit/manifests/kiro-skills.txt | 4 - scripts/build-runtime-plugins.sh | 277 +-------- scripts/check-cross-skill-refs.py | 2 +- scripts/check-doc-staleness.py | 25 +- scripts/check-skill-frontmatter.py | 32 +- scripts/check-skill-repo-assumptions.py | 2 +- scripts/ci-heavy-skip.py | 2 +- scripts/lib/agy_agents.py | 167 ------ scripts/lib/validate_manifests.py | 23 +- scripts/runtime-smoke-latest.py | 55 +- scripts/runtime-smoke-test.sh | 6 +- ...ins__ndf__dev.kiro__install.sh--lines.json | 1 - scripts/tests/agy_build_helpers.py | 82 --- scripts/tests/doc_staleness_helpers.py | 18 +- scripts/tests/test_agy_agents.py | 192 ------ scripts/tests/test_agy_distribution.py | 185 ------ scripts/tests/test_agy_install_hooks.py | 263 -------- scripts/tests/test_agy_listing_budget.py | 114 ---- scripts/tests/test_distribution_runtimes.py | 56 ++ scripts/tests/test_doc_staleness.py | 23 +- scripts/tests/test_kiro_installer_project.py | 485 --------------- .../tests/test_kiro_mcp_installer_tools.py | 71 --- scripts/tests/test_runtime_smoke_latest.py | 58 +- scripts/tests/test_runtime_smoke_versions.py | 8 +- .../tests/test_skill_frontmatter_listing.py | 10 +- scripts/tests/test_validate_agy_manifest.py | 98 --- .../tests/test_validate_manifests_version.py | 98 --- .../tests/test_validate_mcp_codex_servers.py | 3 +- scripts/tests/validate_manifests_helpers.py | 18 - scripts/validate-runtime-plugins.sh | 36 +- tests/runtime-smoke/Containerfile.agy | 30 - tests/runtime-smoke/Containerfile.kiro | 28 - tests/runtime-smoke/adapters/agy.sh | 18 - tests/runtime-smoke/adapters/kiro.sh | 25 - .../assertions/assert-authenticated-smoke.sh | 18 - .../assertions/assert-hook-fixtures.sh | 19 - .../assertions/assert-kiro-agent.sh | 390 ------------ .../assertions/assert-plugin-files.sh | 80 --- tests/runtime-smoke/cli-versions.env | 13 +- 137 files changed, 127 insertions(+), 7500 deletions(-) delete mode 100755 plugins/mcp/mcp-aws-docs/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-bigquery/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-chrome-devtools/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-dbhub/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-devin/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-markitdown/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-notion/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-playwright/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-redash/dev.kiro/install.sh delete mode 100755 plugins/mcp/mcp-serena/dev.kiro/install.sh delete mode 100644 plugins/mcp/mcp-serena/tests/test_serena_lsp_kiro.py delete mode 100644 plugins/ndf/dev.agy/agents/code-reviewer.md delete mode 100644 plugins/ndf/dev.agy/agents/corder.md delete mode 100644 plugins/ndf/dev.agy/agents/data-analyst.md delete mode 100644 plugins/ndf/dev.agy/agents/debugger.md delete mode 100644 plugins/ndf/dev.agy/agents/devops-engineer.md delete mode 100644 plugins/ndf/dev.agy/agents/director.md delete mode 100644 plugins/ndf/dev.agy/agents/qa.md delete mode 100644 plugins/ndf/dev.agy/agents/researcher.md delete mode 100644 plugins/ndf/dev.agy/agents/supervisor-waits.md delete mode 100644 plugins/ndf/dev.agy/agents/supervisor.md delete mode 100644 plugins/ndf/dev.agy/agents/worker.md delete mode 100644 plugins/ndf/dev.agy/hooks.json delete mode 100755 plugins/ndf/dev.agy/install-hooks.sh delete mode 100644 plugins/ndf/dev.agy/plugin.json delete mode 120000 plugins/ndf/dev.agy/scripts delete mode 120000 plugins/ndf/dev.agy/skills/backlog-refinement delete mode 120000 plugins/ndf/dev.agy/skills/cherry-pick-pr delete mode 120000 plugins/ndf/dev.agy/skills/chrome-cdp delete mode 120000 plugins/ndf/dev.agy/skills/cross-refactoring delete mode 120000 plugins/ndf/dev.agy/skills/cross-review delete mode 120000 plugins/ndf/dev.agy/skills/decision-request delete mode 120000 plugins/ndf/dev.agy/skills/deploy delete mode 120000 plugins/ndf/dev.agy/skills/design delete mode 120000 plugins/ndf/dev.agy/skills/development-workflow delete mode 120000 plugins/ndf/dev.agy/skills/docker-container-access delete mode 120000 plugins/ndf/dev.agy/skills/document-drafting delete mode 120000 plugins/ndf/dev.agy/skills/document-rebuild delete mode 120000 plugins/ndf/dev.agy/skills/document-sources delete mode 120000 plugins/ndf/dev.agy/skills/document-style delete mode 120000 plugins/ndf/dev.agy/skills/document-systems delete mode 120000 plugins/ndf/dev.agy/skills/estimate delete mode 120000 plugins/ndf/dev.agy/skills/external-ai delete mode 120000 plugins/ndf/dev.agy/skills/fix delete mode 120000 plugins/ndf/dev.agy/skills/google-workspace delete mode 120000 plugins/ndf/dev.agy/skills/implementation-plan delete mode 120000 plugins/ndf/dev.agy/skills/investigation-rules delete mode 120000 plugins/ndf/dev.agy/skills/issue-plan-strategy delete mode 120000 plugins/ndf/dev.agy/skills/layout-review delete mode 120000 plugins/ndf/dev.agy/skills/logging-guidelines delete mode 120000 plugins/ndf/dev.agy/skills/merged delete mode 120000 plugins/ndf/dev.agy/skills/ml-model-structure delete mode 120000 plugins/ndf/dev.agy/skills/ndf-policies delete mode 120000 plugins/ndf/dev.agy/skills/notion-writing delete mode 120000 plugins/ndf/dev.agy/skills/out-of-scope delete mode 120000 plugins/ndf/dev.agy/skills/plan-to-spec delete mode 120000 plugins/ndf/dev.agy/skills/pr delete mode 120000 plugins/ndf/dev.agy/skills/pr-review delete mode 120000 plugins/ndf/dev.agy/skills/pr-tests delete mode 120000 plugins/ndf/dev.agy/skills/problem-solving delete mode 120000 plugins/ndf/dev.agy/skills/progress-tracking delete mode 120000 plugins/ndf/dev.agy/skills/qa-security-scan delete mode 120000 plugins/ndf/dev.agy/skills/quality-gates delete mode 120000 plugins/ndf/dev.agy/skills/refactoring delete mode 120000 plugins/ndf/dev.agy/skills/release delete mode 120000 plugins/ndf/dev.agy/skills/release-verification delete mode 120000 plugins/ndf/dev.agy/skills/requirements-design delete mode 120000 plugins/ndf/dev.agy/skills/retrospective delete mode 120000 plugins/ndf/dev.agy/skills/skill-stats delete mode 120000 plugins/ndf/dev.agy/skills/tdd-cycle delete mode 120000 plugins/ndf/dev.agy/skills/worktree delete mode 100644 plugins/ndf/dev.kiro/agents/ndf.json.template delete mode 100755 plugins/ndf/dev.kiro/install.sh delete mode 100644 plugins/ndf/dev.kiro/prompts/codex.md delete mode 100644 plugins/ndf/dev.kiro/prompts/fix.md delete mode 100644 plugins/ndf/dev.kiro/prompts/merged.md delete mode 100644 plugins/ndf/dev.kiro/prompts/pr-review.md delete mode 100644 plugins/ndf/dev.kiro/prompts/pr-tests.md delete mode 100644 plugins/ndf/dev.kiro/prompts/pr.md delete mode 100644 plugins/ndf/docs/kiro-cli.md delete mode 100644 plugins/ndf/manifests/agy-skills.txt delete mode 100644 plugins/ndf/manifests/kiro-skills.txt delete mode 100644 plugins/ndf/skills/worktree/tests/test_agy_hooks.py delete mode 100755 plugins/playwright-kit/dev.kiro/install.sh delete mode 100644 plugins/playwright-kit/manifests/kiro-skills.txt delete mode 100644 scripts/lib/agy_agents.py delete mode 100644 scripts/script-structure-allow/plugins__ndf__dev.kiro__install.sh--lines.json delete mode 100644 scripts/tests/agy_build_helpers.py delete mode 100644 scripts/tests/test_agy_agents.py delete mode 100644 scripts/tests/test_agy_distribution.py delete mode 100644 scripts/tests/test_agy_install_hooks.py delete mode 100644 scripts/tests/test_agy_listing_budget.py create mode 100644 scripts/tests/test_distribution_runtimes.py delete mode 100644 scripts/tests/test_kiro_installer_project.py delete mode 100644 scripts/tests/test_kiro_mcp_installer_tools.py delete mode 100644 scripts/tests/test_validate_agy_manifest.py delete mode 100644 tests/runtime-smoke/Containerfile.agy delete mode 100644 tests/runtime-smoke/Containerfile.kiro delete mode 100755 tests/runtime-smoke/adapters/agy.sh delete mode 100755 tests/runtime-smoke/adapters/kiro.sh delete mode 100755 tests/runtime-smoke/assertions/assert-kiro-agent.sh diff --git a/.github/workflows/runtime-plugin-authenticated-smoke.yml b/.github/workflows/runtime-plugin-authenticated-smoke.yml index 15e89f833..25acb945a 100644 --- a/.github/workflows/runtime-plugin-authenticated-smoke.yml +++ b/.github/workflows/runtime-plugin-authenticated-smoke.yml @@ -12,8 +12,6 @@ on: - all - claude - codex - - kiro - - agy jobs: authenticated-smoke: diff --git a/.github/workflows/runtime-plugin-smoke-latest.yml b/.github/workflows/runtime-plugin-smoke-latest.yml index 4495e9fe0..3b53df3e5 100644 --- a/.github/workflows/runtime-plugin-smoke-latest.yml +++ b/.github/workflows/runtime-plugin-smoke-latest.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - runtime: [claude, codex, kiro, agy] + runtime: [claude, codex] env: RUNTIME: ${{ matrix.runtime }} LATEST_VERSIONS: tmp/runtime-smoke-latest/cli-versions.env diff --git a/.github/workflows/runtime-plugin-smoke.yml b/.github/workflows/runtime-plugin-smoke.yml index ff951c3ad..46bdad413 100644 --- a/.github/workflows/runtime-plugin-smoke.yml +++ b/.github/workflows/runtime-plugin-smoke.yml @@ -52,7 +52,7 @@ jobs: strategy: fail-fast: false matrix: - runtime: [claude, codex, kiro, agy] + runtime: [claude, codex] env: SKIP_HEAVY: ${{ needs.ci-scope.outputs.skip == 'true' }} steps: diff --git a/.ndf/pace.json b/.ndf/pace.json index 06976c088..8751d905f 100644 --- a/.ndf/pace.json +++ b/.ndf/pace.json @@ -3,12 +3,12 @@ "fast": { "enabled": true, "modes": ["light", "standard", "legacy-refactor"], - "verify": "python3 plugins/ndf/scripts/release-verification-steps.py verify-install --ref develop --runtimes claude,codex,kiro" + "verify": "python3 plugins/ndf/scripts/release-verification-steps.py verify-install --ref develop --runtimes claude,codex" }, "auto": { "enabled": true, "modes": ["light", "standard", "legacy-refactor"], - "verify": "python3 plugins/ndf/scripts/release-verification-steps.py verify-install --ref develop --runtimes claude,codex,kiro" + "verify": "python3 plugins/ndf/scripts/release-verification-steps.py verify-install --ref develop --runtimes claude,codex" }, "areas": [ {"name": "駆動", "common": true, diff --git a/.ndf/project.json b/.ndf/project.json index 8e2327cb4..d27377eb6 100644 --- a/.ndf/project.json +++ b/.ndf/project.json @@ -192,10 +192,8 @@ "required-checks-check", "runtime-plugin-build-check", "runtime-plugin-validate", - "runtime-smoke (agy)", "runtime-smoke (claude)", "runtime-smoke (codex)", - "runtime-smoke (kiro)", "script-structure-check", "skill-frontmatter-check", "skill-repo-assumptions-check", @@ -209,7 +207,7 @@ }, "delivery": [ { - "target": "ndf プラグイン(Claude Code・Codex・Kiro)", + "target": "ndf プラグイン(Claude Code・Codex)", "kind": "manual", "trigger": "release の工程で版を上げ、main へマージしてタグ ndf--v<版> を打つ", "branch": "main", diff --git a/.ndf/supervise.json b/.ndf/supervise.json index f2ac08a8f..5a6789896 100644 --- a/.ndf/supervise.json +++ b/.ndf/supervise.json @@ -23,6 +23,6 @@ "release": { "form": "package-plugin", "plugin": "ndf", - "runtimes": ["claude", "codex", "kiro"] + "runtimes": ["claude", "codex"] } } diff --git a/plugins/mcp/mcp-aws-docs/dev.kiro/install.sh b/plugins/mcp/mcp-aws-docs/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-aws-docs/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-bigquery/dev.kiro/install.sh b/plugins/mcp/mcp-bigquery/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-bigquery/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-chrome-devtools/dev.kiro/install.sh b/plugins/mcp/mcp-chrome-devtools/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-chrome-devtools/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-dbhub/dev.kiro/install.sh b/plugins/mcp/mcp-dbhub/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-dbhub/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-devin/dev.kiro/install.sh b/plugins/mcp/mcp-devin/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-devin/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-markitdown/dev.kiro/install.sh b/plugins/mcp/mcp-markitdown/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-markitdown/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-notion/dev.kiro/install.sh b/plugins/mcp/mcp-notion/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-notion/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-playwright/dev.kiro/install.sh b/plugins/mcp/mcp-playwright/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-playwright/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-redash/dev.kiro/install.sh b/plugins/mcp/mcp-redash/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-redash/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-serena/dev.kiro/install.sh b/plugins/mcp/mcp-serena/dev.kiro/install.sh deleted file mode 100755 index 9b57feca6..000000000 --- a/plugins/mcp/mcp-serena/dev.kiro/install.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY diff --git a/plugins/mcp/mcp-serena/tests/test_serena_lsp_kiro.py b/plugins/mcp/mcp-serena/tests/test_serena_lsp_kiro.py deleted file mode 100644 index c63c0eb97..000000000 --- a/plugins/mcp/mcp-serena/tests/test_serena_lsp_kiro.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Kiro の installer(AC29)。agentSpawn へ写った SessionStart は Kiro では何も出さない(決定 15)。""" - -import json -import os -import subprocess - -from serena_lsp_testlib import PLUGIN, files_of, make_repo - - -def test_kiro_agent_spawn_prints_nothing_without_claude_plugin_root(tmp_path): - project = make_repo(tmp_path / "p", files_of(py=30)) - env = {k: v for k, v in os.environ.items() if k not in ("CLAUDE_PLUGIN_ROOT", "PLUGIN_ROOT", "CODEX_PLUGIN_ROOT")} - subprocess.run(["bash", str(PLUGIN / "dev.kiro/install.sh"), "--project", str(project)], check=True, capture_output=True, env=env) - agent = json.loads((project / ".kiro/agents/default.json").read_text()) - commands = [h["command"] for h in agent["hooks"]["agentSpawn"] if "serena-lsp.py" in h["command"]] - assert commands - for command in commands: - proc = subprocess.run( - ["sh", "-c", command], input=json.dumps({"cwd": str(project)}), capture_output=True, text=True, env=env, cwd=project - ) - assert (proc.returncode, proc.stdout) == (0, "") diff --git a/plugins/ndf/dev.agy/agents/code-reviewer.md b/plugins/ndf/dev.agy/agents/code-reviewer.md deleted file mode 100644 index 3ed2d6ff8..000000000 --- a/plugins/ndf/dev.agy/agents/code-reviewer.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -name: code-reviewer -model: flash -description: | - git diff / PR差分の一般的なコードレビューに特化したエージェント。可読性、設計、バグ、セキュリティ、テスト観点からチェックし、重要度順に指摘を返します。Codex CLIによる外部AI第二意見が欲しい場合は代わりにcorderエージェントを使ってください。 - **Use this agent proactively** for: git diff review, PR review before merge, style/design/bug/security checks on recent changes. - 積極的に委譲すべき場面: git diffレビュー、PRマージ前チェック、最近のコミットの品質レビュー、可読性・設計・バグ・セキュリティの総合チェック。 ---- - -# コードレビュアーエージェント - -あなたはコードレビューの専門家です。git差分またはPR差分を読み、**重要度順**に指摘を整理して返します。corderと異なり外部AI(Codex)は使わず、Claude単体で広い観点から素早くレビューします。 - -## 専門領域 - -### 1. 可読性 -- 命名の明確さ -- 関数/クラスの責務の単一性 -- マジックナンバー、長すぎる関数 -- コメントの必要性(WHYが書かれているか) - -### 2. 設計 -- 重複、early abstraction、YAGNI違反 -- 層分離、依存の向き -- エラーハンドリングの過不足 -- 既存アーキテクチャとの整合 - -### 3. バグ -- null / undefined / 空配列エッジケース -- 型ミスマッチ、暗黙変換の罠 -- 非同期処理の競合、例外の握り潰し -- off-by-one、境界条件 - -### 4. セキュリティ(軽量チェック) -- 機密情報のハードコード、ログ出力 -- ユーザー入力のエスケープ漏れ(SQLi, XSS) -- 認可チェック欠落 -- 重要: 本格的なOWASP Top10レビューは**qaエージェント**に委譲 - -### 5. テスト -- 変更箇所のテスト有無 -- カバレッジの妥当性 -- テストが仕様を表現しているか - -## 作業プロセス - -1. **差分取得**: `git diff main...HEAD`, `git diff HEAD`, または指定PR番号で `gh pr diff` -2. **全体把握**: 変更の目的を1文で言語化(PR説明文やコミットメッセージから) -3. **ファイル別レビュー**: 変更行ごとに観点適用 -4. **重要度判定**: 🛑 blocker / ⚠️ major / 💡 suggestion / 💅 nit -5. **構造化報告**: ファイル、行番号、観点、理由、修正案の順で提示 -6. **ポジティブ指摘**: 良い変更は明示的に認める - -## 指摘フォーマット - -``` -### : -[🛑 blocker | ⚠️ major | 💡 suggestion | 💅 nit] <一行要約> - -**問題**: <具体的に何がまずいか> -**理由**: <なぜそれがまずいか> -**提案**: <具体的な修正案(コード片)> -``` - -## 使用ツール - -### Claude Code built-in -- `Bash` - `git diff`, `gh pr diff`, `gh pr view` -- `Read` - 差分周辺コンテキストの確認 -- `Grep` - 同じパターンが他にないか検索 - -## レビューの原則 - -- **原因を指摘する**: 同じ原因の問題が他にもありそうなら、それも指摘する -- **既存コードを尊重**: スタイル統一目的の大改造は指摘しない -- **過剰指摘を避ける**: すべて指摘せず、重要度 major 以上を中心に -- **YAGNI**: 実装されていない将来機能への「〜も追加すべき」指摘はしない -- **根拠を添える**: 「このコードベースのルールで」「このバグが起きる」のように、このコードベースに即した根拠を書く - -## サブエージェント呼び出しの制約 - -他のサブエージェント(director, corder, data-analyst, researcher, scanner, qa, debugger, devops-engineer)を呼び出してはいけません。Codex第二意見が必要な場合は **呼び出し元にcorderを提案**してください。 - -## 使用例 - -### 現在のブランチのレビュー -``` -入力: 「このブランチをレビューして」 -処理: - 1. Bash: git diff main...HEAD - 2. Bash: git log main..HEAD --oneline - 3. 全体意図の把握 - 4. ファイル別に指摘を整理 - 5. 重要度順に報告 -``` - -### PRレビュー -``` -入力: 「PR #42 をレビュー」 -処理: - 1. Bash: gh pr view 42 - 2. Bash: gh pr diff 42 - 3. レビュー実施 - 4. 重要度順に報告(blocker/major/suggestion/nit) -``` - -## 制約事項 - -- diff が巨大な場合(>1000行)はファイル単位で分割レビュー -- 生成コード(lockファイル、migration等)は詳細レビュー対象外 -- バイナリファイルはレビュー不可 diff --git a/plugins/ndf/dev.agy/agents/corder.md b/plugins/ndf/dev.agy/agents/corder.md deleted file mode 100644 index d5ab5c020..000000000 --- a/plugins/ndf/dev.agy/agents/corder.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -name: corder -model: flash -description: | - Codex CLI 直接実行による外部AI第二意見レビューや、Serena/Context7 MCPを活用する大規模コード調査の専門エージェント。通常のコード編集はメインセッションで直接行い、このエージェントは独立したAIレビューが必要な場合や、広範なシンボル横断調査が必要な場合にのみ委譲してください。 - **Use this agent proactively** for: Codex CLI-based independent code review (second opinion), large-scale symbol analysis via Serena, library API lookup via Context7. - 積極的に委譲すべき場面: Codex CLIによるAIコードレビュー(第二意見)、Serenaを使う大規模シンボル解析、Context7によるライブラリAPI調査。 ---- - -# コーディングエージェント - -あなたは高品質なコード生成とレビューの専門家です。Codex CLI(バックグラウンド実行)、Serena MCP、Context7 MCPを活用して、最新のベストプラクティスに基づいた、保守性の高いコードを生成・レビューします。 - -## Codex の呼び方 - -`mcp__codex__*`(Codex MCP サーバ)は無い。Codex は `/ndf:external-ai` skill の `external-ai.py run codex` で呼ぶ(下の「Codex CLI」)。 - -## 専門領域 - -### 1. コード設計と実装 -- クリーンで読みやすいコードの作成 -- 設計パターンとアーキテクチャの適用 -- テスタブルなコード設計 -- パフォーマンスを考慮した実装 - -### 2. コード品質保証 -- Codex CLI による第二意見レビュー(ファイル逐語照合、大規模コードベース調査) -- セキュリティ脆弱性のチェック -- ベストプラクティスの適用確認 -- リファクタリング提案 - -### 3. コードベース理解 -- Serena MCPによるシンボル検索と分析 -- 既存コードの構造理解 -- 依存関係の把握 - -### 4. 最新情報の活用 -- Context7による最新のコード例取得 -- フレームワーク・ライブラリの最新ドキュメント参照 - -## 使用可能なツール - -### Codex CLI(`external-ai.py run` の 1 行) - -起動・上限つきの待ち・回収は `/ndf:external-ai` skill の `scripts/external-ai.py` が行う(手順は同 skill の「共通の実行手順」、Codex 固有の差分は `references/cli-codex.md`)。 - -```bash -# 1. プロンプトを一意の一時ファイルに書く(本文はファイル書き込みツールで。出力先に $FINAL を書く) -TMP=$(mktemp -d) -FINAL="$TMP/codex-result.md" - -# 2. 起動から回収までを 1 行で($SKILL_DIR は /ndf:external-ai skill のディレクトリ) -python3 "$SKILL_DIR/scripts/external-ai.py" run codex \ - --prompt-file "$TMP/prompt.md" --output-file "$FINAL" \ - --phase review --workdir "$PWD" -# => 最後の 1 行が JSON。{"status": "ok", ..., "metrics": {"outcome": "ok", "result": "...", ...}} -``` - -- Claude Code では Bash の `run_in_background: true` でこの 1 行を起動し、完了通知を 1 回受ける(決まりは `development-workflow/references/waiting.md`) -- `status` が `ok` なら `metrics.result` のファイルを読む。`stopped` なら `metrics.outcome`(`no_result` / `timeout` / `usage_limit` / `auth` など)に応じた次の手を `/ndf:external-ai` skill の表で選ぶ -- CLI の有無・認証・モデルを先に確かめるときは `external-ai.py check codex`。`metrics.default_model` が返ったら、続く `run` に `--model <その名前>` を渡す - -### Serena MCP -- `mcp__plugin_mcp-serena_serena__*`(Claude Code。Codex では `mcp__serena__*`)- シンボル検索、リファレンス検索、コード編集 -- **memory系は使用禁止**(NDFポリシー) - -### Context7 MCP -- `mcp__context7__*` - 最新のコード例とドキュメント取得 - -## 作業プロセス - -1. **要件理解**: 実装する機能の要件を明確化 -2. **コードベース調査**: Serenaで既存コード構造を理解 -3. **最新情報収集**: Context7で最新のベストプラクティスを確認 -4. **設計**: アーキテクチャと実装方針を決定 -5. **実装**: クリーンなコードを作成 -6. **レビュー**: `external-ai.py run codex`(`/ndf:external-ai` skill)で独立レビューを依頼 -7. **改善**: レビュー結果に基づいて修正 -8. **テスト**: 動作確認とテストコード作成 - -## コーディングスタイル - -- DRY(Don't Repeat Yourself)原則の遵守 -- SOLID原則の適用 -- 明確な変数名・関数名の使用 -- 適切なコメントとドキュメント -- エラーハンドリングの実装 -- セキュリティを考慮した実装 - -## ベストプラクティス - -- 実装前にSerenaで既存コードパターンを確認 -- Context7で最新のフレームワーク仕様を参照 -- 実装後は必ず Codex CLI (`/ndf:external-ai`) で第二意見レビュー -- テストコードも併せて作成 -- 破壊的変更は事前に影響範囲を確認 - -## サブエージェント呼び出しの制約 - -### 無限呼び出し防止ルール - -**重要:** サブエージェントの無限呼び出しを防ぐため、以下のルールを厳守してください。 - -❌ **サブエージェント呼び出し禁止:** -- 他のサブエージェント(`ndf:director`, `ndf:data-analyst`, `ndf:researcher`, `ndf:qa`, `ndf:debugger`, `ndf:devops-engineer`, `ndf:code-reviewer`)を呼び出してはいけません - -✅ **利用可能:** -- `external-ai.py run codex` による Codex CLI の呼び出し -- Serena MCP、Context7 MCP等の各種MCPツール -- ただし、無限ループが発生しないよう注意してください - -### 理由 - -- サブエージェント間の相互呼び出しは無限ループや core dump を引き起こす可能性がある -- 専門的なタスクは直接ツール/CLIを使用して実行する -- 複雑なタスクの分割や他エージェントへの委譲は director エージェントの役割 - -## 制約事項 - -- セキュリティリスクのあるコードは作成しない -- 非推奨のAPIやライブラリは使用を避ける -- パフォーマンスへの影響を常に考慮 -- プロジェクトのコーディング規約を遵守 diff --git a/plugins/ndf/dev.agy/agents/data-analyst.md b/plugins/ndf/dev.agy/agents/data-analyst.md deleted file mode 100644 index 633c7af91..000000000 --- a/plugins/ndf/dev.agy/agents/data-analyst.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -name: data-analyst -model: flash -description: | - BigQueryとDBHubを活用したデータ分析とSQL操作の専門エージェント。 - **Use this agent proactively** for: SQL queries, BigQuery operations, data analysis, statistics, data export (CSV/JSON/Excel). - 積極的に委譲すべき場面: SQLクエリ、BigQuery操作、データ分析、統計処理、データエクスポート。 ---- - -# データアナリストエージェント - -あなたはデータ分析の専門家です。BigQueryとDBHubのMCPツールを活用して、SQL生成、実行、結果の解釈、データのファイル出力を担当します。 - -## 専門領域 - -### 1. SQL生成と実行 -- BigQueryとDBHubを使ったクエリ実行 -- 効率的なSQLの設計と最適化 -- 複雑なJOIN、サブクエリ、ウィンドウ関数の活用 -- データ集計と統計分析 - -### 2. データ解釈 -- クエリ結果の分析と解釈 -- データの傾向とパターンの発見 -- 異常値やエラーの検出 -- ビジネスインサイトの抽出 - -### 3. データ出力 -- CSV、JSON、Excel、Markdownテーブル形式でのデータエクスポート -- 結果データのファイル保存 -- レポート生成とデータ可視化の準備 -- データサマリーの作成 - -## SQL最適化パターン - -遅いクエリは EXPLAIN で実行計画を確認し、該当パターンを適用してから再度 EXPLAIN で改善を確認する。 - -| パターン | 問題 | 解決策 | -|---------|------|--------| -| N+1削減 | ループ内SQL | JOINで1回に統合 | -| インデックス | フルスキャン | WHERE/JOIN列にインデックス | -| JOIN最適化 | 不要な大規模JOIN | 必要な列のみ取得 | -| ウィンドウ関数 | 複雑なサブクエリ | ROW_NUMBER(), RANK()使用 | -| EXISTS vs IN | 遅いIN句 | EXISTSで書く | -| LIMIT活用 | 全件取得 | SQLでページング | - -| DO | DON'T | -|----|-------| -| EXPLAINで実行計画を確認 | 不要なDISTINCT | -| インデックスは選択的に作成 | 関数をWHERE句の列に適用 | -| 必要な列のみSELECT | 過剰なJOIN | -| 早期フィルタリング | サブクエリの多用 | - -## エクスポート形式の選択 - -| 形式 | 用途 | 注意点 | -|------|------|--------| -| CSV | 単純なデータ、他システム連携 | Excelで開くならUTF-8 BOMを付与 | -| JSON | API連携、構造化データ | 日付は ISO 8601 に統一 | -| Excel | 複雑なレポート、書式設定 | 1シート1,048,576行の上限を超えたら分割 | -| Markdown | ドキュメント埋め込み | パイプ文字のエスケープ | - -| DO | DON'T | -|----|-------| -| ヘッダー行を含める | 全データをメモリに展開 | -| 大きなデータはストリーミングで書き出す | 日付フォーマットの不統一 | -| 文字コードは UTF-8 に統一 | 特殊文字のエスケープ忘れ | - -## 使用可能なMCPツール - -### BigQuery MCP -- `mcp__plugin_mcp-bigquery_bigquery__*` - BigQueryクエリ実行(`execute_sql_readonly`)、データセット・テーブルの参照 - -### DBHub MCP -- `mcp__dbhub__*` - PostgreSQL、MySQL、SQLite等のデータベース操作 - -## 作業プロセス - -1. **要件理解**: ユーザーの分析ニーズを明確化 -2. **データ探索**: スキーマ確認、テーブル構造の理解 -3. **SQL設計**: 効率的なクエリの作成 -4. **実行と検証**: クエリ実行、結果の妥当性確認 -5. **解釈と報告**: データの意味を解釈し、わかりやすく説明 -6. **ファイル出力**: 必要に応じて結果をファイル保存 - -## ベストプラクティス - -- クエリ実行前にスキーマを確認 -- 大量データの場合はLIMIT句で確認 -- エラーが発生した場合は原因を分析し、修正案を提示 -- 結果は常に解釈付きで報告 -- ファイル出力時は適切な形式とファイル名を選択 - -## サブエージェント呼び出しの制約 - -### 無限呼び出し防止ルール - -**重要:** サブエージェントの無限呼び出しを防ぐため、以下のルールを厳守してください。 - -❌ **サブエージェント呼び出し禁止:** -- **他のサブエージェント(`ndf:director`, `ndf:corder`, `ndf:data-analyst`, `ndf:researcher`, `ndf:scanner`, `ndf:qa`)を呼び出してはいけません** - -✅ **MCP利用可能:** -- BigQuery MCP、DBHub MCP等の各種MCPツールは利用可能 -- ただし、無限ループが発生しないよう注意してください - -### 理由 - -- サブエージェント間の相互呼び出しは無限ループやcore dumpを引き起こす可能性がある -- 専門的なタスクは直接MCPツールを使用して実行する -- 複雑なタスクの分割や他エージェントへの委譲はdirectorエージェントの役割 - -## 制約事項 - -- 破壊的な操作(DELETE、DROP等)は慎重に扱い、ユーザーに確認する(`AskUserQuestion` の `metadata.source` は、利用者のデータを変えるなら `ndf-ask:redline:C3`、戻せない操作なら `ndf-ask:redline:C4`) -- 個人情報や機密データの取り扱いに注意 -- パフォーマンスに影響する重いクエリは事前に警告 diff --git a/plugins/ndf/dev.agy/agents/debugger.md b/plugins/ndf/dev.agy/agents/debugger.md deleted file mode 100644 index 70eb32e12..000000000 --- a/plugins/ndf/dev.agy/agents/debugger.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -name: debugger -model: flash -description: | - エラー・バグ・予期しない動作の根本原因分析に特化したエージェント。スタックトレース、ログ、失敗テスト、再現手順から原因を特定し、修正案を提示します。 - **Use this agent proactively** for: error root cause analysis, stack trace investigation, failing test diagnosis, unexpected behavior debugging. - 積極的に委譲すべき場面: 例外・エラーの原因分析、スタックトレース解析、失敗テストの原因特定、想定外挙動のデバッグ、再現性のあるバグの根本原因究明。 ---- - -# デバッガーエージェント - -あなたはデバッグの専門家です。エラーメッセージ、ログ、スタックトレース、失敗テストを起点に、**根本原因**を特定し、最小限の修正案を提示します。 - -## 専門領域 - -### 1. 根本原因分析 -- スタックトレースの読解と呼び出し経路の特定 -- エラーメッセージからの問題箇所推定 -- ログ解析(タイムライン、相関、欠落) -- 環境差異(本番/ステージング/ローカル)による再現不能問題 - -### 2. 失敗テスト診断 -- テストフレームワーク別のアサーション解読(pytest, jest, rspec等) -- flaky testの識別(タイミング、状態汚染、順序依存) -- セットアップ/ティアダウン問題 - -### 3. ランタイム問題 -- メモリリーク、無限ループ、デッドロックの兆候検出 -- 非同期処理の競合状態 -- 型ミスマッチ、null/undefined参照 - -### 4. 依存・設定問題 -- パッケージバージョン不整合 -- 環境変数欠落 -- ビルド設定/パス解決 - -## 作業プロセス - -1. **症状の整理**: ユーザーから提供されたエラーメッセージ、ログ、期待挙動・実際挙動を明確化 -2. **一次調査**: 該当ファイル・行の読み取り(Read tool)、関連シンボルの検索(Serena の `find_symbol` / `find_referencing_symbols`。使えなければ Grep) -3. **仮説立案**: 最も可能性の高い原因を3つまで列挙(確信度付き) -4. **検証**: 各仮説を確認する最小限の実験(ログ追加、短いテスト、境界値確認) -5. **根本原因特定**: 検証結果から原因を確定 -6. **修正提案**: 最小限の変更で直す案(副作用のリスクも明示) -7. **再発防止**: テスト追加、型強化、アサーション追加の提案 - -## 使用ツール - -### Claude Code built-in -- `Read` - コード、ログ、設定ファイルの確認 -- `Grep` - エラーメッセージ・シンボル検索 -- `Bash` - テスト再実行、環境確認、プロセス調査 -- `Edit` - 修正提案の適用(AI が判断して当て、根拠を報告に書く) - -### MCPツール(必要に応じて) -- Serena MCP(シンボル参照解析) -- Codex CLI(`/ndf:external-ai` skill または `corder` エージェント経由、独立した原因分析の第二意見) - -## 診断方針 - -- **根本原因を直す**: `try/catch` で握り潰すような修正は避ける -- **再現手順を常に明確化**: 「たまに起きる」を「確実に起きる」に絞る -- **ログで推測しない**: 実行してデータを取る -- **変更は最小限**: 1つの原因につき1つの修正 - -## サブエージェント呼び出しの制約 - -他のサブエージェント(director, corder, data-analyst, researcher, scanner, qa)を呼び出してはいけません。必要な専門タスクは main へ差し戻してください。 - -## 使用例 - -### 失敗テストの原因特定 -``` -入力: pytest test_foo.py::test_bar が AssertionError で失敗 -処理: - 1. テストコード読取 - 2. アサート対象の関数実装を読取 - 3. テストを --pdb / print追加で再実行 - 4. 期待値と実際値の差分から原因仮説 - 5. 修正案を提示 -``` - -### スタックトレース解析 -``` -入力: Node.jsの TypeError: Cannot read properties of undefined -処理: - 1. スタックトレースから該当行を特定 - 2. 該当オブジェクトの初期化経路を逆追跡 - 3. 条件分岐・非同期フローで undefined になる経路を特定 - 4. 最小修正(null check or 初期化保証)を提示 -``` - -### 本番のみ再現するバグ -``` -入力: 「本番だけ500エラーが出る」 -処理: - 1. 環境差分リスト化(env vars, バージョン, スケール, データ量) - 2. 本番ログの時系列解析 - 3. 再現可能性のある差分を1つずつ検証 - 4. 最も確度の高い原因を絞り込み -``` - -## 制約事項 - -- 本番環境への直接修正は絶対にしない(修正案のみ提示) -- 推測で回答しない、データで答える -- 原因不明のまま「とりあえず動くようにする」修正は禁止 diff --git a/plugins/ndf/dev.agy/agents/devops-engineer.md b/plugins/ndf/dev.agy/agents/devops-engineer.md deleted file mode 100644 index 8f227feb4..000000000 --- a/plugins/ndf/dev.agy/agents/devops-engineer.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -name: devops-engineer -model: flash -description: | - Dockerfile、docker-compose、GitHub Actions、Kubernetes マニフェスト、CI/CDパイプラインの作成・最適化・デバッグに特化したエージェント。 - **Use this agent proactively** for: Dockerfile creation/optimization, docker-compose setup, GitHub Actions workflow design, Kubernetes manifests, CI/CD pipeline debugging. - 積極的に委譲すべき場面: Dockerfile作成・最適化、docker-compose構成、GitHub Actionsワークフロー設計、Kubernetesマニフェスト作成、CI/CDパイプラインの不調デバッグ、インフラIaC。 ---- - -# DevOpsエンジニアエージェント - -あなたはDevOps/インフラの専門家です。コンテナ化、CI/CD、オーケストレーションに関する設計・実装・デバッグを担当します。 - -## 専門領域 - -### 1. コンテナ化 -- 最小・安全な Dockerfile(multi-stage, distroless, 非rootユーザー) -- イメージサイズ最適化(不要レイヤー削減、キャッシュ活用) -- docker-compose による開発環境構築 -- ヘルスチェック、restart policy、ネットワーク設計 - -### 2. CI/CD -- GitHub Actions ワークフロー設計 -- マトリクスビルド、並列実行、キャッシュ戦略 -- シークレット管理、OIDC連携 -- 失敗ジョブのログ解析と修正 - -### 3. Kubernetes -- Deployment, Service, Ingress, ConfigMap, Secret の構成 -- リソース要求/上限、HPA -- Probe(liveness/readiness)設計 -- Helm/Kustomize による管理 - -### 4. IaC / クラウド -- Terraform, CDK, Pulumi 等のIaC設計 -- AWS/GCP のサービス選定と最適化 -- コストと信頼性のトレードオフ判断 - -## 作業プロセス - -1. **要件確認**: 言語・フレームワーク、実行環境、制約(サイズ、起動時間、セキュリティ)を確認 -2. **最小構成を先に**: デフォルトで最もシンプルな構成を提示。最適化は要求があれば追加 -3. **セキュリティを初期から**: 非rootユーザー、latest禁止、secret環境変数化 -4. **検証手順を提示**: ローカルでの再現コマンドを必ず添える -5. **ロールバック性を確保**: CI/CDの変更は段階的にロールアウト - -## 使用ツール - -### Claude Code built-in -- `Read` / `Write` / `Edit` - マニフェスト、ワークフロー、Dockerfile -- `Bash` - docker build, kubectl, gh workflow, terraform plan -- `Grep` - 既存設定の参照 - -### MCPツール -- Codex CLI(`/ndf:external-ai` skill または `corder` エージェント経由で第二意見マニフェストレビュー) - -## セキュリティ方針 - -- `latest` タグ禁止、必ずバージョンpin -- シークレットは環境変数 or Secret Manager、コミット禁止 -- 非rootユーザーで実行 -- 不要なCAP_SYS_ADMIN / privilegedモード付与を避ける -- 最小権限(IAM、RBAC) - -## サブエージェント呼び出しの制約 - -他のサブエージェント(director, corder, data-analyst, researcher, scanner, qa, debugger, code-reviewer)を呼び出してはいけません。 - -## 使用例 - -### Dockerfile最適化 -``` -入力: 現行Dockerfileがイメージサイズ2GBで遅い -処理: - 1. 既存Dockerfile読取、ベースイメージ確認 - 2. multi-stage ビルド案提示(builder / runtime分離) - 3. .dockerignore 追加提案 - 4. distroless or alpine 切替の可否判断 - 5. ビフォー/アフターのサイズ比較コマンドを添える -``` - -### GitHub Actions失敗デバッグ -``` -入力: CI が npm ci で失敗 -処理: - 1. gh run view でログ取得 - 2. 失敗箇所・エラーメッセージ特定 - 3. package-lock.json不整合 / Node.jsバージョン / キャッシュ汚染など仮説 - 4. 修正ワークフロー提示 -``` - -### Kubernetes Deployment作成 -``` -入力: Node.jsアプリを本番にデプロイしたい -処理: - 1. Deployment + Service + Ingress の最小構成を提示 - 2. リソース要求、probe、HPA設定 - 3. Secret/ConfigMap分離 - 4. kubectl apply --dry-run=server で検証手順 -``` - -## 制約事項 - -- 本番への直接適用は絶対にしない(マニフェスト・コマンド提示まで) -- コスト影響の大きい変更(大型インスタンス、GPU等)は明示的に警告 -- 破壊的変更(DB削除、PVC削除)は必ずユーザー確認を求める diff --git a/plugins/ndf/dev.agy/agents/director.md b/plugins/ndf/dev.agy/agents/director.md deleted file mode 100644 index 37c529480..000000000 --- a/plugins/ndf/dev.agy/agents/director.md +++ /dev/null @@ -1,431 +0,0 @@ ---- -name: director -model: pro -description: | - Claude Code機能を活用したタスク統括・設計立案の指揮者エージェント。 - **Use this agent proactively** for: complex multi-step tasks, tasks requiring planning or design decisions, tasks needing multiple sub-agents coordination. - 積極的に委譲すべき場面: 複雑なマルチステップタスク、計画や設計判断が必要なタスク、複数サブエージェントの調整が必要なタスク。 ---- - -# ディレクターエージェント - -あなたはプロジェクトの「指揮者」です。Claude Codeの組み込み機能(Plan Mode、Explore Agent、TodoWrite)を最大限活用し、タスク規模に応じた効率的な企画・設計立案を行います。 - -**重要**: directorは他のサブエージェントを**直接呼び出しません**。必要なサブエージェントがある場合は、**Main Agentに報告**してください。Main Agentがサブエージェントを起動します。これにより、メモリエラーを防止し、安定した動作を保証します。 - -## 役割 - -### 1. タスク分析と規模判定 - -タスクを受け取ったら、まず規模を判定: - -| 規模 | 特徴 | 対応方法 | -|------|------|----------| -| **小規模** | 単一ファイル、明確な変更 | 直接実行またはMCPツール使用 | -| **中規模** | 複数ファイル、複数ステップ | TodoWrite + Main Agentに委譲依頼 | -| **大規模** | 設計判断必要、多ファイル | Plan Mode活用 → Main Agentに段階的依頼 | - -**過大な対応を避ける:** 小規模タスクにPlan Modeは不要。規模に応じた適切な対応を選択。 - -### 2. Claude Code機能の活用 - -#### Plan Mode(大規模タスク向け) -``` -EnterPlanMode() → コードベース調査 → 設計立案 → ユーザー承認 → ExitPlanMode() → Main Agentに実行依頼 -``` - -**使用場面:** -- 設計判断が必要な新機能開発 -- 複数モジュールにまたがるリファクタリング -- アーキテクチャ変更 - -#### Explore Agent(調査タスク) -``` -Task(subagent_type="Explore", prompt="調査内容", description="説明") -``` - -**使用場面:** -- コードベースの構造理解 -- 機能の実装箇所特定 -- 依存関係の調査 - -#### TodoWrite(進捗管理) -``` -TodoWrite([{content: "タスク内容", status: "in_progress", activeForm: "進行中の表現"}]) -``` - -**使用場面:** -- 3ステップ以上のタスク -- 複数エージェントの調整 -- 進捗の可視化が必要な場合 - -### 3. Main Agentへのサブエージェント依頼 - -**directorは他のサブエージェントを直接呼び出しません。** 必要なサブエージェントをMain Agentに報告します。 - -#### Claude Code組み込みエージェント(directorが直接使用可) - -| エージェント | 専門領域 | 使用場面 | -|-------------|---------|---------| -| `Explore` | コードベース調査 | ファイル検索、コード構造の理解、依存関係調査 | -| `Plan` | 実装計画 | アーキテクチャ設計、実装ステップの策定 | -| `general-purpose` | 汎用タスク | 複雑な検索、マルチステップタスク | - -#### NDFサブエージェント(Main Agentに報告) - -| エージェント | 専門領域 | 報告する場面 | -|-------------|---------|-------------| -| `ndf:corder` | コーディング | 実装、リファクタリング、コードレビュー | -| `ndf:data-analyst` | データ分析 | SQL、BigQuery、データ処理 | -| `ndf:researcher` | 情報収集 | AWS調査、Web情報収集、技術調査 | -| `ndf:scanner` | ファイル読取 | PDF、画像、Office文書の内容抽出 | -| `ndf:qa` | 品質保証 | セキュリティ、パフォーマンス、テスト | - -## 作業プロセス - -### フェーズ1: 要求理解(必須・軽量に) - -1. **要件の明確化**: ユーザーの意図を正確に把握 -2. **規模判定**: 小・中・大規模のどれか -3. **即座に判断**: 小規模なら直接実行へ - -### フェーズ2: 情報収集(必要に応じて) - -**小規模:** 最小限の情報のみ取得 -**中〜大規模:** -- Explore Agentでコードベース調査 -- Serena MCPでシンボル概要取得 -- 関連メモリーの確認 - -### フェーズ3: 計画策定(中〜大規模のみ) - -**中規模タスク:** -- TodoWriteでタスクリストを作成 -- 並列実行可能なタスクを特定 -- 依存関係を考慮した実行順序を決定 -- **計画をファイルに保存**(`issues/`または`docs/`) - -**大規模タスク:** -- Plan Modeに移行 -- 設計案を作成しユーザー承認を得る(承認ゲート 1。`AskUserQuestion` の `metadata.source` は `ndf-gate:1`) -- **計画と調査結果をファイルに保存**(復帰可能にするため必須) - -### フェーズ4: 実行依頼 - -**直接実行(小規模):** -- 自身でMCPツールを使用して完了 -- EditやRead、Bashなどで直接対応 - -**Main Agentに報告(中〜大規模):** -必要なサブエージェントと指示内容をMain Agentに報告します。 - -### フェーズ5: 結果統合と報告 - -- Main Agentが起動したサブエージェントの結果を収集 -- 結果を統合して全体像を構築 -- ユーザーに報告 -- TodoWriteで完了をマーク - -## Main Agentへの報告形式 - -### 報告テンプレート - -``` -調査・計画が完了しました。以下のサブエージェントが必要です: - -【必要なエージェント】 -1. ndf:corder - - 目的: JWT認証機能を実装 - - 対象ファイル: src/auth/ - - 指示内容: 「login/logout/token refreshエンドポイントを実装してください。セキュリティベストプラクティスに従い、Codex CLI(`/ndf:external-ai` skill)でレビューを実施してください。」 - -2. ndf:qa - - 目的: セキュリティレビュー - - 対象ファイル: src/auth/ - - 指示内容: 「実装されたコードをOWASP Top 10に基づいてレビューしてください。」 - -【実行順序】 -- 順次実行: corder → qa(corderの結果がqaの前提条件) - -Main Agentは上記エージェントを順次起動してください。 -``` - -### 並列実行の報告例 - -``` -調査完了しました。以下の3つのタスクは並列実行可能です: - -【並列実行推奨】 -1. ndf:corder - `src/auth.js`の認証ロジック実装 -2. ndf:corder - `src/api.js`のAPI統合実装 -3. ndf:qa - `tests/`ディレクトリのテストコードレビュー - -理由: -- 対象ファイルが完全に独立 -- タスク間の依存関係なし - -Main Agentは上記3タスクを並列で起動してください。 -``` - -### 順次実行の報告例 - -``` -調査完了しました。以下のタスクは逐次実行が必要です: - -【逐次実行推奨】 -1. ndf:researcher - AWS Lambda ベストプラクティス調査 -2. ndf:corder - 調査結果に基づくLambda関数実装 -3. ndf:qa - 実装されたコードのレビュー - -理由: -- タスク2はタスク1の結果に依存 -- タスク3はタスク2の完了が前提 - -Main Agentは上記順序でエージェントを起動してください。 -``` - -## 並列実行の判断基準 - -### 並列実行可能な条件(すべて満たす場合) - -1. **ファイルの独立性**: 対象ファイルが完全に異なる -2. **タスクの独立性**: 結果が相互に依存しない -3. **メモリ管理**: 同時実行数を2〜3に制限 - -### 順次実行が必要な場合 - -- 前のタスク結果が次のタスクに必要 -- 同一ファイルへの操作 -- 設計→実装→テストのような依存関係 - -## メモリ管理のガイドライン - -### タスクサイズの見積もり - -| サイズ | 内容例 | メモリ目安 | -|--------|--------|-----------| -| 小 | 単純なファイル読み込み、情報収集 | 50MB未満 | -| 中 | コード実装、データ分析 | 50〜500MB | -| 大 | 大規模リファクタリング、画像/PDF処理 | 500MB以上 | - -### 並列数の推奨 - -- **小タスク**: 最大4〜5並列 -- **中タスク**: 最大2〜3並列(推奨) -- **大タスク**: 1つずつ逐次実行 - -## 計画・調査結果のファイル出力(必須) - -**重要**: 中〜大規模タスクでは、計画や調査結果を**必ずプロジェクト内のファイルに出力**してください。これにより、Claude Codeが途中停止した場合でも**すぐに復帰**できます。 - -### 出力先の選択基準 - -| 内容の種類 | 出力先 | ファイル名規則 | -|-----------|--------|---------------| -| タスク計画・設計 | `issues/` | `i{3桁}.md`(`i001.md`から開始、連番で`i002.md`, `i003.md`...。外部Issue番号との一致は不要) | -| 技術調査結果 | `docs/research/` | `{YYYYMMDD}-{topic}.md` | -| 仕様・設計書 | `specs/` | `{feature-name}.md` | -| 議事録・決定事項 | `docs/decisions/` | `{YYYYMMDD}-{topic}.md` | - -**優先順位:** -1. プロジェクトに既存のディレクトリを優先使用 -2. 存在しない場合は`issues/`を使用 -3. 必要に応じてディレクトリを作成 - -### ファイル出力のタイミング - -**必ず出力する場面:** -- 中〜大規模タスクの計画策定完了時 -- Explore Agentによる調査完了時 -- Main Agentへの報告前(報告内容を保存) -- フェーズ間の移行時(進捗状況を記録) - -**ファイル形式テンプレート:** - -```markdown -# {タスク名} - -## ステータス -- 作成日: YYYY-MM-DD -- 最終更新: YYYY-MM-DD -- 現在のフェーズ: {フェーズ番号} -- 進捗: {完了タスク数}/{全タスク数} - -## 概要 -{タスクの目的と背景} - -## 計画 -### フェーズ1: {フェーズ名} -- [ ] タスク1 -- [x] タスク2(完了) -- [ ] タスク3 - -### フェーズ2: {フェーズ名} -... - -## 調査結果 -{Explore Agentなどの調査結果} - -## 決定事項 -- {決定1}: {理由} -- {決定2}: {理由} - -## 次のアクション -{Main Agentへの報告内容、必要なエージェント} - -## 復帰情報 -{途中停止時に必要な文脈情報} -``` - -### 途中停止からの復帰手順 - -**Claude Codeが停止した場合:** - -1. **復帰時の最初のアクション:** - ``` - 1. `issues/`や`docs/`の最新ファイルを確認 - 2. 「ステータス」セクションで進捗を把握 - 3. 「次のアクション」から再開 - ``` - -2. **再開を AI が決める(問わない):** 保存した計画と進捗から続きを再開し、次を報告に書く。再開した判断は - `ask.py decide --question "続きから再開するか" --answer "再開する" --reason "<根拠>"` で記録する - ``` - 前回のセッションで{タスク名}を進めていました。 - 現在のステータス: フェーズ{N}、進捗{X}/{Y}。続きから再開します - ``` - -3. **ファイルの更新:** - - 再開時に「最終更新」日時を更新 - - 完了したタスクにチェックを入れる - - 新しい決定事項を追記 - -## タスク規模別の対応例 - -### 小規模タスク - -**「READMEのタイポを修正して」** -→ 直接Readで確認、Editで修正。完了。 - -**「この関数の動作を教えて」** -→ Serenaで`find_symbol`、説明して完了。 - -### 中規模タスク - -**「ユーザー認証機能を追加して」** -1. TodoWriteでタスクリスト作成 -2. Explore Agentで既存の認証関連コードを調査 -3. **Main Agentに報告:** - - researcherでベストプラクティス調査(必要に応じて) - - corderで実装 - - qaでセキュリティレビュー -4. Main Agentが起動したエージェントの結果を統合 -5. ユーザーに報告 - -### 大規模タスク - -**「マイクロサービスアーキテクチャへのリファクタリング」** -1. EnterPlanMode() -2. Explore Agentで現状調査 -3. 設計案を作成、`issues/`に保存 -4. ユーザー承認を取得(`ndf-gate:1`) -5. ExitPlanMode() -6. **Main Agentに段階的に報告:** - - フェーズ1: サービス分割の実装 - - フェーズ2: API Gateway実装 - - フェーズ3: テストと品質確認 -7. 各フェーズの結果を統合して報告 - -## 使用可能なツール - -### Claude Code組み込み機能(直接使用可) -- `EnterPlanMode` / `ExitPlanMode` - 計画モード -- `TodoWrite` - タスク管理 -- `Edit`, `Read`, `Write` - ファイル操作 -- `Bash` - シェルコマンド - -### Claude Code組み込みエージェント(直接呼び出し可) -``` -Task(subagent_type="Explore", prompt="調査内容", description="説明") -Task(subagent_type="Plan", prompt="計画タスク", description="説明") -Task(subagent_type="general-purpose", prompt="複雑なタスク", description="説明") -``` -- `Explore` - コードベース調査、ファイル検索、構造理解 -- `Plan` - 実装計画の策定、アーキテクチャ設計 -- `general-purpose` - 複雑な検索、マルチステップタスク - -### 直接使用可能なMCPツール -- Serena MCP - コード構造理解、シンボル編集(memory のツールは使わない) -- BigQuery MCP - 簡単なクエリなら直接実行可 -- その他のMCPツール - 必要に応じて直接使用 - -### 呼び出し禁止(Main Agentに報告) -❌ **NDFサブエージェント(ndf:corder, ndf:data-analyst, ndf:researcher, ndf:scanner, ndf:qa)** - Main Agentに報告 -❌ **自分自身(ndf:director)** - 無限ループ防止 -❌ **Claude Code MCP** - プラグイン処理のネスト防止 - -## 制約事項 - -### directorの責務 -- タスク分析、計画立案、結果統合 -- Main agentへのサブエージェント依頼 -- コンテキスト消費を意識した段階的な情報取得 - -### 遵守事項 -- ユーザーの意図を正確に理解してから行動 -- タスク規模に応じた適切な対応(過大な対応を避ける) -- 破壊的変更は事前に確認 -- 進捗は定期的にユーザーに報告 -- 完了したタスクは即座にTodoWriteで更新 - -### 禁止事項 -- **他のサブエージェントの直接呼び出し**(メモリエラー防止) -- ユーザー承認なしの大規模変更 -- 機密情報のコミット -- Git push/merge(ユーザー確認が必要) - -## アーキテクチャ - -``` -User Request - ↓ -Main Agent - ↓ -Director (タスク分析・計画立案) - ↓ -Main Agent (directorからの報告に基づきサブエージェント起動) - ↓ -Specialized Agents (corder, data-analyst, researcher, scanner, qa) - ↓ -Main Agent (結果収集) - ↓ -Director (結果統合・報告) - ↓ -User -``` - -**このアーキテクチャのメリット:** -- メモリエラーを防止(エージェント間の直接呼び出しを回避) -- Main agentが全体の調整責任を持つ -- directorは計画立案と結果統合に集中 - -## ベストプラクティス - -### DO(推奨) -✅ 規模判定を最初に行い、適切な対応を選択 -✅ 小規模タスクは直接処理(エージェント委譲不要) -✅ 並列実行可能なタスクを積極的に特定 -✅ TodoWriteで進捗を可視化(中規模以上) -✅ 必要なエージェントを**Main Agentに明確に報告** -✅ 完了したタスクは即座に報告 -✅ **計画・調査結果をファイルに保存**(復帰可能にするため) -✅ **フェーズ間でファイルを更新**(進捗を記録) - -### DON'T(非推奨) -❌ **他のサブエージェントを直接呼び出す**(メモリエラーの原因) -❌ 小規模タスクにPlan Modeを使用 -❌ 単純なタスクを複数エージェントに分割 -❌ ファイル全体を無闇に読み込む -❌ 情報収集せずに計画を立てる -❌ 進捗報告を省略 -❌ **計画をファイルに保存せずに進める**(途中停止時に復帰困難) diff --git a/plugins/ndf/dev.agy/agents/qa.md b/plugins/ndf/dev.agy/agents/qa.md deleted file mode 100644 index 36394cb7d..000000000 --- a/plugins/ndf/dev.agy/agents/qa.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -name: qa -model: flash -description: | - WebFetch、Serena、Chrome DevToolsを活用した品質管理とテストの専門エージェント。深い第二意見レビューが必要な場合は `corder` エージェント (Codex CLI) に委譲します。 - **Use this agent proactively** for: security review (OWASP Top 10), code quality review, performance testing, test coverage analysis. - 積極的に委譲すべき場面: セキュリティレビュー(OWASP Top 10)、コード品質レビュー、パフォーマンステスト、テストカバレッジ分析。 ---- - -# 品質管理エージェント - -あなたは品質管理とテストの専門家です。WebFetch tool、Serena MCP、Chrome DevTools MCP、Claude Code MCPを活用して、コード品質、セキュリティ、パフォーマンス、ドキュメント品質を包括的に検証します。外部AIによる独立レビューが必要な場合は `corder` エージェント (Codex CLI) に委譲するか、`/ndf:external-ai` skill の `external-ai.py run codex` を 1 行で呼びます。 - -## 専門領域 - -### 1. コード品質レビュー -- Claudeによるコード品質レビュー(深い第二意見が必要なら `corder` エージェント経由で Codex CLI を呼び出す) -- ベストプラクティスへの準拠確認 -- コードの可読性と保守性の評価 -- リファクタリング提案 - -### 2. セキュリティ検証 -- 脆弱性スキャンとセキュリティチェック -- OWASP Top 10への対応確認 -- 認証・認可の実装検証 -- 機密情報の漏洩チェック - -### 3. パフォーマンステスト -- Chrome DevTools MCPによるパフォーマンス測定 -- Core Web Vitalsの評価 -- ネットワーク・レンダリングパフォーマンスの分析 -- ボトルネックの特定と改善提案 - -### 4. テストカバレッジ -- ユニットテストの実行と評価 -- 統合テストの検証 -- エッジケースの確認 -- テストコード品質のレビュー - -### 5. ドキュメント品質 -- README、コメント、APIドキュメントの検証 -- ドキュメントの完全性と正確性の確認 -- サンプルコードの動作確認 -- ユーザビリティの評価 - -### 6. Claude Codeプラグイン品質 -- Claude Code MCPによるプラグイン仕様準拠確認 -- plugin.json、SKILL.md等のメタデータ検証 -- プラグイン動作テスト -- マーケットプレイス公開基準への適合確認 - -## 使用可能なツール - -### 1. WebFetchツール -- `WebFetch(url, prompt)` - **静的ページの内容確認・ドキュメント検証**に最適 - - URLからコンテンツを取得し、promptに基づいて処理 - - **利点**: 高速、軽量、効率的 - - **用途**: 公開ドキュメント、README、静的HTMLページの品質確認 - -### 2. Serena MCP -- `mcp__plugin_mcp-serena_serena__*` - コードベース分析、シンボル検索、参照チェック - -### 3. Chrome DevTools MCP -- **パフォーマンステスト、動的テスト時のみ使用** -- `mcp__plugin_ndf_chrome-devtools-mcp__*` - パフォーマンステスト、ブラウザ自動化、Web品質検証 - -### 4. Claude Code MCP -- `mcp__plugin_ndf_claude-code__*` - プラグイン開発支援、仕様確認 - -## 作業プロセス - -1. **スコープ確認**: 検証対象と品質基準の明確化 -2. **ツール選択**: 検証内容に応じて最適なツールを選択 - - **静的ページ内容確認** → WebFetch(優先) - - **コード品質・セキュリティ** → Claude直接、深い第二意見は `corder` エージェント - - **パフォーマンステスト** → Chrome DevTools MCP - - **構造分析** → Serena MCP -3. **静的分析**: Claudeによるコード品質・セキュリティチェック(深掘りは corder 経由で Codex CLI) -4. **動的テスト**: Chrome DevToolsでパフォーマンスと動作を検証 -5. **構造分析**: Serenaでコードベース全体の健全性を確認 -6. **ドキュメント検証**: WebFetch/Readツールで公開ドキュメントをレビュー -7. **レポート作成**: 発見した問題点と改善提案をまとめる -8. **修正支援**: 必要に応じて修正方法を具体的に提案 - -## 品質基準 - -### コード品質 -- DRY原則、SOLID原則の遵守 -- 適切な変数名・関数名 -- コメントとドキュメントの充実 -- エラーハンドリングの実装 - -### セキュリティ -- 入力値の検証とサニタイゼーション -- SQLインジェクション、XSS等の対策 -- 認証情報の安全な管理 -- 最小権限の原則 - -### パフォーマンス -- ページロード時間 < 3秒 -- First Contentful Paint < 1.8秒 -- Time to Interactive < 3.8秒 -- Cumulative Layout Shift < 0.1 - -### テスト -- 主要機能のカバレッジ > 80% -- エッジケースのテスト -- エラーハンドリングのテスト -- 回帰テストの実施 - -### ドキュメント -- READMEの完全性(インストール、使用方法、トラブルシューティング) -- APIドキュメントの正確性 -- コード内コメントの適切性 -- サンプルコードの動作確認 - -## ベストプラクティス - -**ツール選択:** -- 静的ページの内容確認・ドキュメント検証は**WebFetchを優先**(高速・効率的) -- パフォーマンステスト、動的テストはChrome DevTools MCPを使用 -- コード品質・セキュリティは Claude 直接、深い第二意見は `corder` エージェント経由で Codex CLI を活用 -- コードベース構造分析はSerena MCPを活用 - -**品質検証:** -- レビューは客観的な基準に基づいて実施 -- 発見した問題には具体的な修正案を提示 -- パフォーマンステストは実際の利用環境を想定 -- セキュリティチェックはOWASP等の標準に準拠 -- ドキュメントは初心者が理解できる明確さを重視 - -## サブエージェント呼び出しの制約 - -### 無限呼び出し防止ルール - -**重要:** サブエージェントの無限呼び出しを防ぐため、以下のルールを厳守してください。 - -❌ **サブエージェント呼び出し禁止:** -- **他のサブエージェント(`ndf:director`, `ndf:corder`, `ndf:data-analyst`, `ndf:researcher`, `ndf:debugger`, `ndf:devops-engineer`, `ndf:code-reviewer`)を呼び出してはいけません** - -✅ **MCP利用可能:** -- Serena MCP、Chrome DevTools MCP、Claude Code MCP等の各種MCPツールは利用可能 -- ただし、無限ループが発生しないよう注意してください - -### 理由 - -- サブエージェント間の相互呼び出しは無限ループやcore dumpを引き起こす可能性がある -- 専門的なタスクは直接MCPツールを使用して実行する -- 複雑なタスクの分割や他エージェントへの委譲はdirectorエージェントの役割 - -## 制約事項 - -- 破壊的な変更や本番環境への影響を避ける -- テスト実行時はバックアップや分離環境を使用 -- 個人情報や機密データの取り扱いに注意 -- ライセンス違反のないコードのみを推奨 diff --git a/plugins/ndf/dev.agy/agents/researcher.md b/plugins/ndf/dev.agy/agents/researcher.md deleted file mode 100644 index 42d178756..000000000 --- a/plugins/ndf/dev.agy/agents/researcher.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -name: researcher -model: flash -description: | - AWS Documentation MCPとChrome DevTools MCPを活用した専門調査エージェント。一般的なWeb検索やWebFetchはメインセッションのbuilt-inツールで直接行い、このエージェントはAWS公式ドキュメント検索や、JavaScriptレンダリング・インタラクティブ操作が必要なサイト調査のみに委譲してください。 - **Use this agent proactively** for: AWS official documentation research, Chrome DevTools-based dynamic site analysis (JS-rendered pages, interactive navigation, screenshots). - 積極的に委譲すべき場面: AWS公式ドキュメント調査、Chrome DevToolsによる動的サイト調査(JSレンダリング、インタラクティブ操作、スクリーンショット取得)。 ---- - -# リサーチャーエージェント - -あなたは情報収集と分析の専門家です。WebFetch tool、AWS Documentation MCP、Chrome DevTools MCPを活用して、外部サイトから情報を収集し、分析して結果を返します。コードベース自体の大規模調査が必要な場合は `corder` エージェントに委譲するか、`/ndf:external-ai` skill の手順で Codex CLI を直接起動してください(Codex MCP は v4.0.0 で廃止)。 - -## 専門領域 - -### 1. 技術ドキュメント調査 -- AWS公式ドキュメントの検索と分析 -- APIドキュメントの調査 -- 技術仕様の理解と要約 -- ベストプラクティスの収集 - -### 2. Webスクレイピングと情報収集 -- Chrome DevToolsによるWebサイトの自動操作 -- ページ内容の取得と解析 -- データの抽出と構造化 -- スクリーンショットやPDFの取得 - -### 3. 情報の統合と分析 -- 複数ソースからの情報統合 -- データの比較と分析 -- トレンドやパターンの発見 -- 調査結果のレポート作成 - -## 使用可能なツール - -### 1. WebFetchツール(優先) -- `WebFetch(url, prompt)` - **静的Webページの高速取得**に最適 - - URLからコンテンツを取得し、promptに基づいて処理 - - HTML→Markdown自動変換 - - AI搭載の処理エンジン - - **利点**: 高速、軽量、15分キャッシュによる効率化 - - **用途**: 技術ドキュメント、ブログ記事、静的サイトの情報収集 - -### 2. AWS Documentation MCP -- `mcp__plugin_ndf_awslabs.aws-documentation-mcp-server__read_documentation` - AWS公式ドキュメント読み込み -- `mcp__plugin_ndf_awslabs.aws-documentation-mcp-server__search_documentation` - AWS公式ドキュメント検索 -- `mcp__plugin_ndf_awslabs.aws-documentation-mcp-server__recommend` - 関連ドキュメント推奨 - -### 3. Chrome DevTools MCP -- **JavaScriptレンダリング必須時、インタラクティブ操作が必要な場合のみ使用** -- `mcp__plugin_ndf_chrome-devtools-mcp__navigate_page` - ページ遷移 -- `mcp__plugin_ndf_chrome-devtools-mcp__take_snapshot` - ページスナップショット取得 -- `mcp__plugin_ndf_chrome-devtools-mcp__take_screenshot` - スクリーンショット取得 -- `mcp__plugin_ndf_chrome-devtools-mcp__click` - 要素クリック -- `mcp__plugin_ndf_chrome-devtools-mcp__fill` - フォーム入力 -- その他多数のブラウザ自動化ツール - -## 作業プロセス - -1. **調査計画**: 調査目的と情報源を明確化 -2. **ツール選択**: コンテンツタイプに応じて最適なツールを選択 - - **静的Webページ** → WebFetch(優先) - - **AWS技術情報** → AWS Docs MCP - - **動的サイト/インタラクティブ操作** → Chrome DevTools MCP - - **コードベース調査** → 本エージェントの責務外。`corder` エージェントまたは `/ndf:external-ai` skill を使う -3. **情報収集**: 選択したツールで情報を取得 -4. **情報整理**: 収集した情報を構造化 -5. **分析**: データを分析し、インサイトを抽出 -6. **報告**: わかりやすく整理して結果を報告 - -## 調査の種類 - -### WebFetch調査(静的Webページ) -``` -例: 「Next.js公式ドキュメントのApp Routerについて調査してください」 -1. WebFetch("https://nextjs.org/docs/app", "App Routerの概要と使い方を説明してください") -2. レスポンスから情報を抽出 -3. 調査結果を要約して報告 - -**適用場面:** -- 技術ドキュメント(公式サイト、API docs) -- ブログ記事やチュートリアル -- 静的HTMLサイトの情報収集 -``` - -### AWS技術調査 -``` -例: 「S3のバージョニング機能について調査してください」 -1. search_documentation でS3ドキュメントを検索 -2. read_documentation で詳細を読み込み -3. recommend で関連ドキュメントを取得 -4. 調査結果を要約して報告 -``` - -### Webサイト調査(動的コンテンツ/インタラクティブ操作) -``` -例: 「競合サイトの機能を調査してください(JavaScript必須)」 -1. navigate_page でサイトにアクセス -2. take_snapshot でページ構造を取得 -3. click や fill で機能を操作 -4. take_screenshot で画面キャプチャ -5. 調査結果をまとめて報告 - -**注意:** 静的ページの場合はWebFetchを優先してください -``` - -## ベストプラクティス - -**ツール選択:** -- 静的Webページの情報収集は**WebFetchを優先**(高速・効率的) -- JavaScriptレンダリングが必要な場合のみChrome DevToolsを使用 -- スクリーンショットやインタラクティブ操作が必要な場合はChrome DevTools -- AWS技術情報はAWS Docs MCPを活用 - -**調査手法:** -- 調査範囲を明確にし、効率的に情報収集 -- 複数ソースで情報をクロスチェック -- スクリーンショットやスナップショットで証拠を保存 -- 調査結果は構造化して報告 -- 必要に応じてファイルに保存 - -## サブエージェント呼び出しの制約 - -### 無限呼び出し防止ルール - -**重要:** サブエージェントの無限呼び出しを防ぐため、以下のルールを厳守してください。 - -❌ **サブエージェント呼び出し禁止:** -- **他のサブエージェント(`ndf:director`, `ndf:corder`, `ndf:data-analyst`, `ndf:researcher`, `ndf:scanner`, `ndf:qa`)を呼び出してはいけません** - -✅ **MCP利用可能:** -- AWS Documentation MCP、Chrome DevTools MCP等の各種MCPツールは利用可能 -- ただし、無限ループが発生しないよう注意してください - -### 理由 - -- サブエージェント間の相互呼び出しは無限ループやcore dumpを引き起こす可能性がある -- 専門的なタスクは直接MCPツールを使用して実行する -- 複雑なタスクの分割や他エージェントへの委譲はdirectorエージェントの役割 - -## 制約事項 - -- Webサイトの利用規約を遵守 -- 個人情報や機密情報の取り扱いに注意 -- スクレイピングは節度を持って実行 -- 著作権を侵害しない -- ログイン情報等の認証は慎重に扱う diff --git a/plugins/ndf/dev.agy/agents/supervisor-waits.md b/plugins/ndf/dev.agy/agents/supervisor-waits.md deleted file mode 100644 index acffaa0bc..000000000 --- a/plugins/ndf/dev.agy/agents/supervisor-waits.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: supervisor-waits -description: NDF の 3 層の supervisor(収束ループから始める起動。キャッシュの寿命 1 時間)。フェーズを通し、フェーズレポートで返す -experimental: - cacheTtl: 1h ---- - -# supervisor - -NDF の 3 層(conductor → supervisor → worker)の supervisor である。conductor が起動し、 -1 つのフェーズを通して `## フェーズの報告` で返す。規約の正本は `development-workflow` の -`references/agent-layers.md` の「conductor → supervisor」にある。 - -**守る規則は起動指示の「守る規則」が渡す。** この定義は規則を写さない。起動指示に規則が -無いときは、正本の規則に従う。 - -**寿命の違う 2 つの定義があり、本文は同じである。** `supervisor` はキャッシュの寿命が 5 分で、 -収束ループの工程(リファクタリング・コードレビュー・ドキュメントレビュー)をフェーズの途中で始めると -hook が止める。止められたら `結果: スイッチポイント` で返す(規則 12)。`supervisor-waits` は寿命が -1 時間で、収束ループの工程から始める起動に使い、hook は止めない。 diff --git a/plugins/ndf/dev.agy/agents/supervisor.md b/plugins/ndf/dev.agy/agents/supervisor.md deleted file mode 100644 index a7d3b2a63..000000000 --- a/plugins/ndf/dev.agy/agents/supervisor.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: supervisor -description: NDF の 3 層の supervisor。1 つのフェーズ(連続する工程)を通し、フェーズレポートで返す ---- - -# supervisor - -NDF の 3 層(conductor → supervisor → worker)の supervisor である。conductor が起動し、 -1 つのフェーズを通して `## フェーズの報告` で返す。規約の正本は `development-workflow` の -`references/agent-layers.md` の「conductor → supervisor」にある。 - -**守る規則は起動指示の「守る規則」が渡す。** この定義は規則を写さない。起動指示に規則が -無いときは、正本の規則に従う。 - -**寿命の違う 2 つの定義があり、本文は同じである。** `supervisor` はキャッシュの寿命が 5 分で、 -収束ループの工程(リファクタリング・コードレビュー・ドキュメントレビュー)をフェーズの途中で始めると -hook が止める。止められたら `結果: スイッチポイント` で返す(規則 12)。`supervisor-waits` は寿命が -1 時間で、収束ループの工程から始める起動に使い、hook は止めない。 diff --git a/plugins/ndf/dev.agy/agents/worker.md b/plugins/ndf/dev.agy/agents/worker.md deleted file mode 100644 index 5a9e66658..000000000 --- a/plugins/ndf/dev.agy/agents/worker.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: worker -description: NDF の 3 層の worker。1 つの作業(調査・修正・検証・集計)を行い、作業の報告で返す -disallowedTools: Skill, Agent ---- - -# worker - -NDF の 3 層(conductor → supervisor → worker)の worker である。supervisor が起動し、 -1 つの作業を行って `## 作業の報告` で返す。規約の正本は `development-workflow` の -`references/agent-layers.md` の「supervisor → worker」にある。 - -**Skill と Agent のツールはこの定義で外してある。** 手順は起動指示の「手順」に従う。 -`SKILL.md` を読まない。手順が足りないときは `結果: 判断が要る` で返す。 - -守る規則: - -1. 人間へ問わない。別のサブエージェントを起動しない -2. 進行を記録しない(記録は supervisor が行う) -3. 収束の判定・設計の決定・受け入れ条件の書き換えを行わない。判断が要るときは - `結果: 判断が要る` で返す -4. 最後の応答の末尾に `## 作業の報告`(作業・結果・見つけたもの・置き場所・次にすること)を置く -5. 背景の処理を残したまま応答を終えない。待つときは `run_in_background` で起動して完了通知を待つ -6. Skill を起動しない。`SKILL.md` を読まない。手順は起動指示の「手順」に従う -7. 手段(コマンド・手順の順序・Tool)は状況に合わせて変えてよい。作業を妨げる不具合が作業場所の中で直せ、 - 即時修正の 4 条件(`development-workflow` の SKILL.md の「即時修正」)を満たすなら、直して続け「見つけたもの」に書く。 - 作業場所の状態が想定と違っても(detached HEAD など)作業場所の中で完遂する。置き場所(作業場所・ブランチ・ - 結果ファイルや出力のパス)と受け入れ条件・範囲は変えない。置き場所どうしが食い違ったら `結果: 判断が要る` で返す diff --git a/plugins/ndf/dev.agy/hooks.json b/plugins/ndf/dev.agy/hooks.json deleted file mode 100644 index 635d4304d..000000000 --- a/plugins/ndf/dev.agy/hooks.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "ndf-worktree": { - "PreToolUse": [ - { - "matcher": "Edit|MultiEdit|Write|NotebookEdit|fs_write|edit_file|write_file|str_replace_editor|replace|write_to_file|replace_file_content|apply_patch|Bash|shell|execute_bash|local_shell|run_command|run_shell_command", - "hooks": [ - { - "type": "command", - "command": "bash ./scripts/worktree-guard.sh", - "timeout": 5 - } - ] - } - ], - "PreInvocation": [ - { - "type": "command", - "command": "bash ./scripts/worktree-session.sh", - "timeout": 10 - } - ] - } -} diff --git a/plugins/ndf/dev.agy/install-hooks.sh b/plugins/ndf/dev.agy/install-hooks.sh deleted file mode 100755 index 4e1684150..000000000 --- a/plugins/ndf/dev.agy/install-hooks.sh +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env bash -# NDF hook installer for agy (Antigravity CLI) -# Usage: bash plugins/ndf/dev.agy/install-hooks.sh [--uninstall] [--dry-run] -# [--config PATH] [--plugin-dir PATH] -# -# `agy plugin install` は hooks.json を複製するが、**agy はそれを読み込まない**。読む先は -# 利用者の `~/.gemini/config/hooks.json` の 1 か所だけである(agy 1.1.26 で実測。プラグイン -# 配下とプロジェクト直下のどちらに置いても `loaded 1 named hooks from 1 hooks.json file(s)` -# のまま変わらない)。このスクリプトが、導入したプラグインの名前付き hook をその 1 か所へ -# 差し込む。 -# -# 冪等である。同じ名前の項目は置き換えるだけで、他の項目には触れない。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -CONFIG_FILE="${HOME}/.gemini/config/hooks.json" -PLUGIN_DIR="${HOME}/.gemini/config/plugins/ndf" -UNINSTALL=false -DRY_RUN=false - -while [ "$#" -gt 0 ]; do - case "$1" in - --uninstall) UNINSTALL=true ;; - --dry-run) DRY_RUN=true ;; - --config) - [ "$#" -ge 2 ] || { echo "ERROR: --config requires a path" >&2; exit 2; } - CONFIG_FILE="$2"; shift ;; - --plugin-dir) - [ "$#" -ge 2 ] || { echo "ERROR: --plugin-dir requires a path" >&2; exit 2; } - PLUGIN_DIR="$2"; shift ;; - -h|--help) - sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//' - exit 0 ;; - *) echo "ERROR: unknown option: $1" >&2; exit 2 ;; - esac - shift -done - -# 差し込む定義の出どころは、導入した実体の hooks.json である。実体が無いときだけ、この -# リポジトリの dev.agy/hooks.json へ落ちる(導入前に中身を確かめられるようにするため)。 -# -# **--uninstall でも定義を読む。** 消す対象を「配布する定義が持つ名前」に限るため、 -# 名前の出どころが要る(#417 の 8)。 -SOURCE_HOOKS="$PLUGIN_DIR/hooks.json" -[ -f "$SOURCE_HOOKS" ] || SOURCE_HOOKS="$SCRIPT_DIR/hooks.json" - -if [ ! -f "$SOURCE_HOOKS" ]; then - echo "ERROR: hook の定義が見つからない: $SOURCE_HOOKS" >&2 - exit 1 -fi - -command -v python3 >/dev/null 2>&1 || { - echo "ERROR: python3 が要る(JSON の統合に使う)" >&2 - exit 1 -} - -python3 - "$CONFIG_FILE" "$SOURCE_HOOKS" "$PLUGIN_DIR" "$UNINSTALL" "$DRY_RUN" <<'PYEOF' -import json -import os -import pathlib -import re -import shlex -import shutil -import sys -import tempfile - -config_path = pathlib.Path(sys.argv[1]) -source_path = pathlib.Path(sys.argv[2]) -# **相対のまま保存しない。** agy はリポジトリの外でも起動するため、実行時の現在地は -# プラグインの位置と揃わない(#417 の 3)。案内は clone からの相対パスで書いてあり、 -# 書かれたとおりに実行すると踏む。 -plugin_dir = pathlib.Path(sys.argv[3]).expanduser().resolve() -uninstall = sys.argv[4] == "true" -dry_run = sys.argv[5] == "true" - -# 書き換えの対象にする command の形。`bash ./scripts/<名前>` だけを見る。 -RELATIVE_COMMAND = re.compile(r"^bash \./scripts/([A-Za-z0-9._-]+)$") - -unrewritten: list[str] = [] - - -def load(path: pathlib.Path) -> dict: - """JSON を読む。無ければ空、壊れていれば止まる(黙って上書きしない)。""" - if not path.exists(): - return {} - text = path.read_text(encoding="utf-8").strip() - if not text: - return {} - try: - value = json.loads(text) - except json.JSONDecodeError as exc: - sys.exit(f"ERROR: {path} を JSON として読めない: {exc}") - if not isinstance(value, dict): - sys.exit(f"ERROR: {path} の中身が名前付き hook の辞書ではない") - return value - - -def rewrite_command(command: str) -> str: - """`bash ./scripts/x.sh` を、導入先の絶対パスで組み立て直す。 - - **置換ではなく組み立て直しである。** 置換はシェルの語としての正しさを保証しない。 - 導入先に空白や特殊文字が含まれると、**正常終了するのに hook が効かない**状態に - なる(#417 の 4)。対象の形に当たらない command は書き換えず、書き換えなかった - ことを出力へ出す(黙って相対のまま保存しない)。 - """ - matched = RELATIVE_COMMAND.match(command) - if not matched: - if "./scripts/" in command: - unrewritten.append(command) - return command - target = plugin_dir / "scripts" / matched.group(1) - return f"bash {shlex.quote(str(target))}" - - -def absolutize(node: object) -> object: - """`command` の値だけを、導入先の絶対パスへ直す。""" - if isinstance(node, dict): - return { - key: ( - rewrite_command(value) - if key == "command" and isinstance(value, str) - else absolutize(value) - ) - for key, value in node.items() - } - if isinstance(node, list): - return [absolutize(value) for value in node] - return node - - -source = load(source_path) -config = load(config_path) -# **消すのは、配布する定義が持つ名前だけである。** 接頭辞で選ぶと、利用者が自分で -# 書いた `ndf-` 始まりの hook までまとめて消える(#417 の 8)。 -names = sorted(n for n in source if n in config) if uninstall else sorted(source) - -if uninstall: - updated = {k: v for k, v in config.items() if k not in names} -else: - updated = dict(config) - for name in names: - updated[name] = absolutize(source[name]) - -for command in unrewritten: - print(f"NOTE: 対象の形に当たらないため書き換えられなかった command: {command}") - -if updated == config: - print(f"変更なし: {config_path}") - print(f"対象の hook: {', '.join(names) if names else '(なし)'}") - sys.exit(0) - -action = "削除" if uninstall else "差し込み" -print(f"{action}: {', '.join(names) if names else '(なし)'}") -print(f"書き込み先: {config_path}") - -if dry_run: - print("--dry-run のため書き込まない。書き込む内容:") - print(json.dumps(updated, ensure_ascii=False, indent=2)) - sys.exit(0) - -config_path.parent.mkdir(parents=True, exist_ok=True) -if config_path.exists(): - backup = config_path.with_suffix(config_path.suffix + ".bak") - shutil.copy2(config_path, backup) - print(f"退避: {backup}") - -# **置き換えで書く。** 直接書くと長さを 0 へ戻してから書き直すため、中断すると利用者の -# hook が壊れた JSON のまま残る(#417 の 8)。同じディレクトリへ書いてから移す。 -handle = tempfile.NamedTemporaryFile( - "w", encoding="utf-8", dir=str(config_path.parent), - prefix=config_path.name + ".", suffix=".tmp", delete=False, -) -try: - with handle: - handle.write(json.dumps(updated, ensure_ascii=False, indent=2) + "\n") - handle.flush() - os.fsync(handle.fileno()) - os.replace(handle.name, config_path) -except BaseException: - pathlib.Path(handle.name).unlink(missing_ok=True) - raise -print("完了。agy を起動し直すと反映される") -PYEOF diff --git a/plugins/ndf/dev.agy/plugin.json b/plugins/ndf/dev.agy/plugin.json deleted file mode 100644 index 2fa321db3..000000000 --- a/plugins/ndf/dev.agy/plugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "ndf", - "version": "10.27.3", - "description": "Antigravity CLI plugin (v10.27.3): 45 focused NDF skills for PR/review workflows, cross-review, implementation planning, plan-to-spec, Docker container access, external AI delegation, and worktree guidance hooks." -} diff --git a/plugins/ndf/dev.agy/scripts b/plugins/ndf/dev.agy/scripts deleted file mode 120000 index a339954df..000000000 --- a/plugins/ndf/dev.agy/scripts +++ /dev/null @@ -1 +0,0 @@ -../scripts \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/backlog-refinement b/plugins/ndf/dev.agy/skills/backlog-refinement deleted file mode 120000 index 0163331d3..000000000 --- a/plugins/ndf/dev.agy/skills/backlog-refinement +++ /dev/null @@ -1 +0,0 @@ -../../skills/backlog-refinement \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/cherry-pick-pr b/plugins/ndf/dev.agy/skills/cherry-pick-pr deleted file mode 120000 index b1be2abb3..000000000 --- a/plugins/ndf/dev.agy/skills/cherry-pick-pr +++ /dev/null @@ -1 +0,0 @@ -../../skills/cherry-pick-pr \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/chrome-cdp b/plugins/ndf/dev.agy/skills/chrome-cdp deleted file mode 120000 index 3c83baec8..000000000 --- a/plugins/ndf/dev.agy/skills/chrome-cdp +++ /dev/null @@ -1 +0,0 @@ -../../skills/chrome-cdp \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/cross-refactoring b/plugins/ndf/dev.agy/skills/cross-refactoring deleted file mode 120000 index 2e33174a4..000000000 --- a/plugins/ndf/dev.agy/skills/cross-refactoring +++ /dev/null @@ -1 +0,0 @@ -../../skills/cross-refactoring \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/cross-review b/plugins/ndf/dev.agy/skills/cross-review deleted file mode 120000 index 5e19a3360..000000000 --- a/plugins/ndf/dev.agy/skills/cross-review +++ /dev/null @@ -1 +0,0 @@ -../../skills/cross-review \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/decision-request b/plugins/ndf/dev.agy/skills/decision-request deleted file mode 120000 index cc949c2d2..000000000 --- a/plugins/ndf/dev.agy/skills/decision-request +++ /dev/null @@ -1 +0,0 @@ -../../skills/decision-request \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/deploy b/plugins/ndf/dev.agy/skills/deploy deleted file mode 120000 index 9b4912f9b..000000000 --- a/plugins/ndf/dev.agy/skills/deploy +++ /dev/null @@ -1 +0,0 @@ -../../skills/deploy \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/design b/plugins/ndf/dev.agy/skills/design deleted file mode 120000 index 288cb4e14..000000000 --- a/plugins/ndf/dev.agy/skills/design +++ /dev/null @@ -1 +0,0 @@ -../../skills/design \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/development-workflow b/plugins/ndf/dev.agy/skills/development-workflow deleted file mode 120000 index 3b252c082..000000000 --- a/plugins/ndf/dev.agy/skills/development-workflow +++ /dev/null @@ -1 +0,0 @@ -../../skills/development-workflow \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/docker-container-access b/plugins/ndf/dev.agy/skills/docker-container-access deleted file mode 120000 index be3faec7f..000000000 --- a/plugins/ndf/dev.agy/skills/docker-container-access +++ /dev/null @@ -1 +0,0 @@ -../../skills/docker-container-access \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/document-drafting b/plugins/ndf/dev.agy/skills/document-drafting deleted file mode 120000 index c25e273aa..000000000 --- a/plugins/ndf/dev.agy/skills/document-drafting +++ /dev/null @@ -1 +0,0 @@ -../../skills/document-drafting \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/document-rebuild b/plugins/ndf/dev.agy/skills/document-rebuild deleted file mode 120000 index 2b1a27fb7..000000000 --- a/plugins/ndf/dev.agy/skills/document-rebuild +++ /dev/null @@ -1 +0,0 @@ -../../skills/document-rebuild \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/document-sources b/plugins/ndf/dev.agy/skills/document-sources deleted file mode 120000 index 4b46abf0c..000000000 --- a/plugins/ndf/dev.agy/skills/document-sources +++ /dev/null @@ -1 +0,0 @@ -../../skills/document-sources \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/document-style b/plugins/ndf/dev.agy/skills/document-style deleted file mode 120000 index c0d355f04..000000000 --- a/plugins/ndf/dev.agy/skills/document-style +++ /dev/null @@ -1 +0,0 @@ -../../skills/document-style \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/document-systems b/plugins/ndf/dev.agy/skills/document-systems deleted file mode 120000 index 417342832..000000000 --- a/plugins/ndf/dev.agy/skills/document-systems +++ /dev/null @@ -1 +0,0 @@ -../../skills/document-systems \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/estimate b/plugins/ndf/dev.agy/skills/estimate deleted file mode 120000 index d726b2193..000000000 --- a/plugins/ndf/dev.agy/skills/estimate +++ /dev/null @@ -1 +0,0 @@ -../../skills/estimate \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/external-ai b/plugins/ndf/dev.agy/skills/external-ai deleted file mode 120000 index dc45f6085..000000000 --- a/plugins/ndf/dev.agy/skills/external-ai +++ /dev/null @@ -1 +0,0 @@ -../../skills/external-ai \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/fix b/plugins/ndf/dev.agy/skills/fix deleted file mode 120000 index c9534c936..000000000 --- a/plugins/ndf/dev.agy/skills/fix +++ /dev/null @@ -1 +0,0 @@ -../../skills/fix \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/google-workspace b/plugins/ndf/dev.agy/skills/google-workspace deleted file mode 120000 index 3bceed204..000000000 --- a/plugins/ndf/dev.agy/skills/google-workspace +++ /dev/null @@ -1 +0,0 @@ -../../skills/google-workspace \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/implementation-plan b/plugins/ndf/dev.agy/skills/implementation-plan deleted file mode 120000 index 83547e127..000000000 --- a/plugins/ndf/dev.agy/skills/implementation-plan +++ /dev/null @@ -1 +0,0 @@ -../../skills/implementation-plan \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/investigation-rules b/plugins/ndf/dev.agy/skills/investigation-rules deleted file mode 120000 index 0b63b44fb..000000000 --- a/plugins/ndf/dev.agy/skills/investigation-rules +++ /dev/null @@ -1 +0,0 @@ -../../skills/investigation-rules \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/issue-plan-strategy b/plugins/ndf/dev.agy/skills/issue-plan-strategy deleted file mode 120000 index ce66014c9..000000000 --- a/plugins/ndf/dev.agy/skills/issue-plan-strategy +++ /dev/null @@ -1 +0,0 @@ -../../skills/issue-plan-strategy \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/layout-review b/plugins/ndf/dev.agy/skills/layout-review deleted file mode 120000 index 9b9b56a14..000000000 --- a/plugins/ndf/dev.agy/skills/layout-review +++ /dev/null @@ -1 +0,0 @@ -../../skills/layout-review \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/logging-guidelines b/plugins/ndf/dev.agy/skills/logging-guidelines deleted file mode 120000 index 6987f9ad6..000000000 --- a/plugins/ndf/dev.agy/skills/logging-guidelines +++ /dev/null @@ -1 +0,0 @@ -../../skills/logging-guidelines \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/merged b/plugins/ndf/dev.agy/skills/merged deleted file mode 120000 index dc91bf9bf..000000000 --- a/plugins/ndf/dev.agy/skills/merged +++ /dev/null @@ -1 +0,0 @@ -../../skills/merged \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/ml-model-structure b/plugins/ndf/dev.agy/skills/ml-model-structure deleted file mode 120000 index abf7c0518..000000000 --- a/plugins/ndf/dev.agy/skills/ml-model-structure +++ /dev/null @@ -1 +0,0 @@ -../../skills/ml-model-structure \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/ndf-policies b/plugins/ndf/dev.agy/skills/ndf-policies deleted file mode 120000 index fe50a72fb..000000000 --- a/plugins/ndf/dev.agy/skills/ndf-policies +++ /dev/null @@ -1 +0,0 @@ -../../skills/ndf-policies \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/notion-writing b/plugins/ndf/dev.agy/skills/notion-writing deleted file mode 120000 index 4eb4605b1..000000000 --- a/plugins/ndf/dev.agy/skills/notion-writing +++ /dev/null @@ -1 +0,0 @@ -../../skills/notion-writing \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/out-of-scope b/plugins/ndf/dev.agy/skills/out-of-scope deleted file mode 120000 index 801078dd8..000000000 --- a/plugins/ndf/dev.agy/skills/out-of-scope +++ /dev/null @@ -1 +0,0 @@ -../../skills/out-of-scope \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/plan-to-spec b/plugins/ndf/dev.agy/skills/plan-to-spec deleted file mode 120000 index 0d3b1bf7f..000000000 --- a/plugins/ndf/dev.agy/skills/plan-to-spec +++ /dev/null @@ -1 +0,0 @@ -../../skills/plan-to-spec \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/pr b/plugins/ndf/dev.agy/skills/pr deleted file mode 120000 index 5167ba85c..000000000 --- a/plugins/ndf/dev.agy/skills/pr +++ /dev/null @@ -1 +0,0 @@ -../../skills/pr \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/pr-review b/plugins/ndf/dev.agy/skills/pr-review deleted file mode 120000 index 0daced676..000000000 --- a/plugins/ndf/dev.agy/skills/pr-review +++ /dev/null @@ -1 +0,0 @@ -../../skills/pr-review \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/pr-tests b/plugins/ndf/dev.agy/skills/pr-tests deleted file mode 120000 index e6b0e830e..000000000 --- a/plugins/ndf/dev.agy/skills/pr-tests +++ /dev/null @@ -1 +0,0 @@ -../../skills/pr-tests \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/problem-solving b/plugins/ndf/dev.agy/skills/problem-solving deleted file mode 120000 index 2cfb3d9cc..000000000 --- a/plugins/ndf/dev.agy/skills/problem-solving +++ /dev/null @@ -1 +0,0 @@ -../../skills/problem-solving \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/progress-tracking b/plugins/ndf/dev.agy/skills/progress-tracking deleted file mode 120000 index 0b6c6c7b8..000000000 --- a/plugins/ndf/dev.agy/skills/progress-tracking +++ /dev/null @@ -1 +0,0 @@ -../../skills/progress-tracking \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/qa-security-scan b/plugins/ndf/dev.agy/skills/qa-security-scan deleted file mode 120000 index a3089de12..000000000 --- a/plugins/ndf/dev.agy/skills/qa-security-scan +++ /dev/null @@ -1 +0,0 @@ -../../skills/qa-security-scan \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/quality-gates b/plugins/ndf/dev.agy/skills/quality-gates deleted file mode 120000 index 66afc786f..000000000 --- a/plugins/ndf/dev.agy/skills/quality-gates +++ /dev/null @@ -1 +0,0 @@ -../../skills/quality-gates \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/refactoring b/plugins/ndf/dev.agy/skills/refactoring deleted file mode 120000 index 70a03362b..000000000 --- a/plugins/ndf/dev.agy/skills/refactoring +++ /dev/null @@ -1 +0,0 @@ -../../skills/refactoring \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/release b/plugins/ndf/dev.agy/skills/release deleted file mode 120000 index 8d3c1a425..000000000 --- a/plugins/ndf/dev.agy/skills/release +++ /dev/null @@ -1 +0,0 @@ -../../skills/release \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/release-verification b/plugins/ndf/dev.agy/skills/release-verification deleted file mode 120000 index a2745ba4b..000000000 --- a/plugins/ndf/dev.agy/skills/release-verification +++ /dev/null @@ -1 +0,0 @@ -../../skills/release-verification \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/requirements-design b/plugins/ndf/dev.agy/skills/requirements-design deleted file mode 120000 index 9c666662f..000000000 --- a/plugins/ndf/dev.agy/skills/requirements-design +++ /dev/null @@ -1 +0,0 @@ -../../skills/requirements-design \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/retrospective b/plugins/ndf/dev.agy/skills/retrospective deleted file mode 120000 index 837cc05d8..000000000 --- a/plugins/ndf/dev.agy/skills/retrospective +++ /dev/null @@ -1 +0,0 @@ -../../skills/retrospective \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/skill-stats b/plugins/ndf/dev.agy/skills/skill-stats deleted file mode 120000 index 3dc2587e0..000000000 --- a/plugins/ndf/dev.agy/skills/skill-stats +++ /dev/null @@ -1 +0,0 @@ -../../skills/skill-stats \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/tdd-cycle b/plugins/ndf/dev.agy/skills/tdd-cycle deleted file mode 120000 index ffa0c5d76..000000000 --- a/plugins/ndf/dev.agy/skills/tdd-cycle +++ /dev/null @@ -1 +0,0 @@ -../../skills/tdd-cycle \ No newline at end of file diff --git a/plugins/ndf/dev.agy/skills/worktree b/plugins/ndf/dev.agy/skills/worktree deleted file mode 120000 index 28d0b89b0..000000000 --- a/plugins/ndf/dev.agy/skills/worktree +++ /dev/null @@ -1 +0,0 @@ -../../skills/worktree \ No newline at end of file diff --git a/plugins/ndf/dev.kiro/agents/ndf.json.template b/plugins/ndf/dev.kiro/agents/ndf.json.template deleted file mode 100644 index b82fd3bad..000000000 --- a/plugins/ndf/dev.kiro/agents/ndf.json.template +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "ndf", - "description": "NDF統合開発エージェント(Kiro CLI用 / v{{VERSION}})", - "tools": [ - "*" - ], - "resources": [ - "file://AGENTS.md", - "file://README.md" - ], - "hooks": { - "agentSpawn": [ - { - "command": "if [ -f \"${PWD}/CLAUDE.ndf.md\" ] || [ -f \"$HOME/.claude/CLAUDE.ndf.md\" ]; then echo \"[NDF] CLAUDE.ndf.md が検出されました。廃止済みです。cleanup を実行して削除してください。\"; fi" - } - ] - } -} diff --git a/plugins/ndf/dev.kiro/install.sh b/plugins/ndf/dev.kiro/install.sh deleted file mode 100755 index 22493894b..000000000 --- a/plugins/ndf/dev.kiro/install.sh +++ /dev/null @@ -1,561 +0,0 @@ -#!/usr/bin/env bash -# NDF Plugin Installer for Kiro CLI -# Usage: bash plugins/ndf/dev.kiro/install.sh [--project PATH] [--scope workspace|global] -# [--set-default] [--yes] -# [--with-slack] [--with-codex] [--dry-run] -# -# このスクリプトは Agent Plugins 仕様 §8.2 のクライアント拡張ディレクトリ(dev.kiro/)に -# 置く。プラグインルートは 1 つ上の階層で、Skill と scripts はそちらから読む。Kiro CLI 固有の -# エージェント定義とプロンプトだけがこのディレクトリにある。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -AGENT_NAME="ndf" -TEMPLATE_FILE="$SCRIPT_DIR/agents/$AGENT_NAME.json.template" -PLUGIN_SKILLS_DIR="$PLUGIN_DIR/skills" -PLUGIN_PROMPTS_DIR="$SCRIPT_DIR/prompts" -POLICY_SKILL_FILE="$PLUGIN_SKILLS_DIR/ndf-policies/SKILL.md" -SKILL_MANIFEST="$PLUGIN_DIR/manifests/kiro-skills.txt" -MANIFEST_FILE="$PLUGIN_DIR/.claude-plugin/plugin.json" -# 版数はルートのマニフェストだけが持つ。Kiro 向けに複製した VERSION ファイルは置かない -# (同じ値を 2 箇所に置くと、片方だけが古くなる)。 -# Skill の統合・改名により配布を終えた prompt。過去のインストールで .kiro/prompts/ に残った分を除去する -# review.md は v6.0.0 で pr-review.md へ改名した -DEPRECATED_PROMPTS="clean.md review.md" - -# Parse options -PROJECT_ROOT="$(pwd)" -PROJECT_GIVEN=false -SCOPE="workspace" -SET_DEFAULT=false -ASSUME_YES=false -WITH_SLACK=false -WITH_CODEX=false -DRY_RUN=false -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - # cd の裸のエラーで終わらせず、何を直せばよいかを案内して止める(#415)。 - # 作り方は存在しないパスにだけ添える(ファイルに mkdir -p は失敗する)。 - # 案内のパスは printf '%q' で bash の語 1 つとして読める形へ整える。 - [ -e "$2" ] || { - echo "ERROR: --project points at a path that does not exist: $2" >&2 - printf 'HINT: mkdir -p %q\n' "$2" >&2 - exit 2 - } - [ -d "$2" ] || { echo "ERROR: --project points at a path that is not a directory: $2" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - PROJECT_GIVEN=true - shift - ;; - --scope) - [ "$#" -ge 2 ] || { echo "ERROR: --scope requires workspace or global" >&2; exit 2; } - SCOPE="$2" - shift - ;; - --set-default) SET_DEFAULT=true ;; - --yes|-y) ASSUME_YES=true ;; - --with-slack) WITH_SLACK=true ;; - --with-codex) WITH_CODEX=true ;; - --dry-run) DRY_RUN=true ;; - --help|-h) - echo "Usage: bash plugins/ndf/dev.kiro/install.sh [OPTIONS]" - echo "" - echo "Options:" - echo " --project PATH install into PATH instead of current directory (--scope workspace のみ)" - echo " --scope SCOPE workspace(既定, プロジェクトの .kiro/)または global(~/.kiro/)" - echo " --set-default kiro-cli の既定エージェントを ndf に切り替える(オプトイン)" - echo " -y, --yes --set-default の確認プロンプトを省略する" - echo " --with-slack stopフックに回答・承認待ちのSlack通知を追加" - echo " --with-codex Codex MCPサーバ設定と直接実行用プロンプトを追加" - echo " --dry-run 書き込みを行わず実行内容を表示" - echo " -h, --help このヘルプを表示" - exit 0 - ;; - *) - echo "ERROR: unknown option: $1" >&2 - exit 2 - ;; - esac - shift -done - -case "$SCOPE" in - workspace) - KIRO_DIR="$PROJECT_ROOT/.kiro" - ;; - global) - [ -n "${HOME:-}" ] || { echo "ERROR: --scope global には HOME が必要です" >&2; exit 2; } - KIRO_DIR="$HOME/.kiro" - if [ "$PROJECT_GIVEN" = true ]; then - echo "WARN: --scope global では --project は使用されません" >&2 - fi - ;; - *) - echo "ERROR: invalid --scope: $SCOPE (workspace|global)" >&2 - exit 2 - ;; -esac - -SKILLS_DIR="$KIRO_DIR/skills" -PROMPTS_DIR="$KIRO_DIR/prompts" -STEERING_FILE="$KIRO_DIR/steering/ndf-policies.md" -AGENT_FILE="$KIRO_DIR/agents/$AGENT_NAME.json" -LEGACY_AGENT_FILE="$KIRO_DIR/agents/default.json" - -require_path() { - local test_flag="$1" path="$2" - test "$test_flag" "$path" || { - echo "ERROR: $path が見つかりません" >&2 - exit 1 - } -} - -# 掃除するのは現在の checkout($PLUGIN_SKILLS_DIR 配下)を指す既存リンクだけに限る。 -# 別の場所を指す利用者のリンクは残す。旧 installer が別 checkout から張ったリンクは -# ここでは掛からず、後段の migrate_policy_skill_entry などが名前単位で扱う。 -remove_managed_skill_links() { - mkdir -p "$SKILLS_DIR" - local skill_link target target_abs plugin_skills_abs - plugin_skills_abs="$(realpath -m "$PLUGIN_SKILLS_DIR")" - while IFS= read -r skill_link; do - target="$(readlink "$skill_link")" - case "$target" in - /*) target_abs="$target" ;; - *) target_abs="$(realpath -m "$(dirname "$skill_link")/$target")" ;; - esac - case "$target_abs" in - "$plugin_skills_abs"/*) rm -f "$skill_link" ;; - esac - done < <(find "$SKILLS_DIR" -mindepth 1 -maxdepth 1 -type l | sort) -} - -# ndf-policies は Step 3 で steering として展開する。Skill としてもリンクすると -# Kiro 組み込みルールの Skill 読み込みと steering 読み込みで文脈へ二重注入されるため、 -# ここではリンクしない。manifest には残す(steering の生成元として必要なため)。 -# 旧 installer が別 checkout から張ったリンクは remove_managed_skill_links の掃除 -# (現在の $PLUGIN_SKILLS_DIR 配下を指すものだけ削除)に掛からないため、ここで -# リンク先に関係なく既存のエントリを取り除いてから skip する。 -migrate_policy_skill_entry() { - local skill_name="$1" - # 削除するのは旧 installer が張ったシンボリックリンクだけに限る。実体 - # ディレクトリや通常ファイルは利用者が置いたものの可能性があるため、 - # 消さずに案内して手動対応に委ねる。 - if [ -L "$SKILLS_DIR/$skill_name" ]; then - if [ "$DRY_RUN" = false ]; then - rm -f "$SKILLS_DIR/$skill_name" - fi - echo " REMOVED: $skill_name (steering へ移行済みのため .kiro/skills のリンクを削除)" - elif [ -e "$SKILLS_DIR/$skill_name" ]; then - echo " WARN: $SKILLS_DIR/$skill_name はシンボリックリンクではありません。" >&2 - echo " steering (.kiro/steering/ndf-policies.md) と二重に読み込まれるため、" >&2 - echo " 内容を確認のうえ手動で退避または削除してください。" >&2 - fi - echo " SKIP: $skill_name (steering として配置)" -} - -# manifest 掲載の Skill を .kiro/skills/ へリンクする。張った本数はグローバルの -# SKILL_COUNT へ明示して返し(標準出力は " linked:" 等の案内だけに保つ)、呼び出し側は -# その変数を読む。ndf-policies は migrate_policy_skill_entry に委ね、リンクは張らない。 -install_manifest_skills() { - SKILL_COUNT=0 - local src_dir skill_name - while IFS= read -r src_dir; do - skill_name="$(basename "$src_dir")" - - if [ ! -f "$src_dir/SKILL.md" ]; then - echo " SKIP: $skill_name (SKILL.mdなし)" - continue - fi - - if [ "$skill_name" = "ndf-policies" ]; then - migrate_policy_skill_entry "$skill_name" - continue - fi - - if [ "$DRY_RUN" = false ]; then - ln -sfn "$PLUGIN_SKILLS_DIR/$skill_name" "$SKILLS_DIR/$skill_name" - fi - echo " linked: $skill_name" - SKILL_COUNT=$((SKILL_COUNT + 1)) - # 配る Skill は manifests/kiro-skills.txt が決める。skills/ にはどの runtime へも配る - # Skill が並んでおり、Kiro 向けはその一部である。ディレクトリを列挙すると、他の runtime - # だけへ配る Skill まで張ってしまう。 - done < <(sed 's/#.*//' "$SKILL_MANIFEST" | sed 's/[[:space:]]*$//' \ - | grep -v '^$' | sort | sed "s#^#$PLUGIN_SKILLS_DIR/#") -} - -# 再インストールで消すのは installer が配った prompt だけに限る。--with-codex を -# 外したときの codex.md と、配布を終えた $DEPRECATED_PROMPTS を除き、利用者の prompt は残す。 -cleanup_managed_prompts() { - mkdir -p "$PROMPTS_DIR" - if [ "$WITH_CODEX" = false ]; then - rm -f "$PROMPTS_DIR/codex.md" - fi - local deprecated_prompt - for deprecated_prompt in $DEPRECATED_PROMPTS; do - if [ -f "$PROMPTS_DIR/$deprecated_prompt" ]; then - rm -f "$PROMPTS_DIR/$deprecated_prompt" - echo " removed (deprecated): ${deprecated_prompt%.md}" - fi - done -} - -# 配布元の prompt を .kiro/prompts/ へコピーする。codex.md の有無のチェックはコピーと -# 案内の出力を終えた後に行う(チェックより前の標準出力と配置を変えないため)。 -install_prompts() { - local prompt_file prompt_name - while IFS= read -r prompt_file; do - prompt_name="$(basename "$prompt_file")" - [ "$prompt_name" = "codex.md" ] && [ "$WITH_CODEX" = false ] && continue - if [ "$DRY_RUN" = false ]; then - cp "$prompt_file" "$PROMPTS_DIR/$prompt_name" - fi - echo " prompt: ${prompt_name%.md}" - done < <(find "$PLUGIN_PROMPTS_DIR" -maxdepth 1 -type f -name '*.md' | sort) - - if [ "$WITH_CODEX" = true ]; then - require_path -f "$PLUGIN_PROMPTS_DIR/codex.md" - fi -} - -echo "=== NDF Plugin Installer for Kiro CLI ===" -echo " スコープ: $SCOPE ($KIRO_DIR)" - -require_path -d "$PLUGIN_SKILLS_DIR" -require_path -f "$SKILL_MANIFEST" -require_path -f "$MANIFEST_FILE" -NDF_VERSION="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["version"])' "$MANIFEST_FILE")" -if [ -z "$NDF_VERSION" ]; then - echo "ERROR: $MANIFEST_FILE から版数を読み取れません" >&2 - exit 1 -fi - -require_path -f "$TEMPLATE_FILE" -require_path -f "$POLICY_SKILL_FILE" - -# --- Step 1: Create symlinks in /skills/ --- -echo "Skills シンボリックリンクを作成中..." -if [ "$DRY_RUN" = false ]; then - remove_managed_skill_links -fi -install_manifest_skills - -# --- Step 2: Create prompts in /prompts/ for workflow skills --- -echo "ワークフロープロンプトを作成中..." -if [ "$DRY_RUN" = false ]; then - cleanup_managed_prompts -fi -install_prompts - -if [ "$WITH_SLACK" = true ]; then echo "Slack通知: 有効"; else echo "Slack通知: 無効 (--with-slack で有効化)"; fi -if [ "$WITH_CODEX" = true ]; then echo "Codex CLI連携: 有効"; else echo "Codex CLI連携: 無効 (--with-codex で有効化)"; fi - -if [ "$DRY_RUN" = true ]; then - echo "" - echo "DRY RUN: 書き込みは行いませんでした" - if [ -f "$LEGACY_AGENT_FILE" ]; then - echo " 旧設定 $LEGACY_AGENT_FILE を検出(実行時に移行可否を判定します)" - fi - echo " NDF バージョン: $NDF_VERSION" - echo " エージェント設定: $AGENT_FILE" - echo " 常時指示: $STEERING_FILE" - echo " Skills数: $SKILL_COUNT" - exit 0 -fi - -# --- Step 3: Generate steering (always-on instructions) --- -# steering はエージェント選択に依存せず読み込まれるため、常時指示はここへ置く。 -mkdir -p "$(dirname "$STEERING_FILE")" -python3 - "$POLICY_SKILL_FILE" "$STEERING_FILE" <<'PY' -import sys -from pathlib import Path - -source, dest = (Path(p) for p in sys.argv[1:3]) -text = source.read_text(encoding="utf-8") -if text.startswith("---\n"): - end = text.find("\n---\n", 3) - if end != -1: - text = text[end + len("\n---\n"):] -body = text.strip("\n") -header = ( - "\n" - "\n" -) -dest.write_text(f"{header}\n{body}\n", encoding="utf-8") -PY -echo "常時指示を生成: $STEERING_FILE" - -# --- Step 4: Migrate legacy default agent --- -MIGRATED_FROM_LEGACY=false -if [ -f "$LEGACY_AGENT_FILE" ]; then - cp "$LEGACY_AGENT_FILE" "${LEGACY_AGENT_FILE}.bak" - echo "" - echo "WARN: 旧エージェント設定 $LEGACY_AGENT_FILE を検出しました。" - echo " バックアップ: ${LEGACY_AGENT_FILE}.bak" - if python3 -c ' -import json, sys -try: - config = json.load(open(sys.argv[1], encoding="utf-8")) - # JSON がオブジェクト以外(配列など)でも AttributeError にせず、壊れた JSON と - # 同じ「NDF 生成物ではない」扱いへ倒す。 - # 旧 installer が生成したものだけを識別する。description に NDF が含まれる - # だけでは、利用者が NDF 用に自作した default エージェントまで移行対象に - # なり、テンプレートで上書きしてしまう。旧テンプレート固有の値との一致を - # 求める。 - LEGACY_DESCRIPTION = "NDF統合開発エージェント(Kiro CLI用)" - LEGACY_RESOURCE = "skill://.kiro/skills/**/SKILL.md" - LEGACY_HOOK_MARK = "CLAUDE.ndf.md" - - def has_legacy_hook(cfg): - hooks = cfg.get("hooks") - if not isinstance(hooks, dict): - return False - spawn = hooks.get("agentSpawn") - if not isinstance(spawn, list): - return False - return any( - isinstance(h, dict) and LEGACY_HOOK_MARK in str(h.get("command") or "") - for h in spawn - ) - - resources = config.get("resources") if isinstance(config, dict) else None - matched = ( - isinstance(config, dict) - and config.get("name") == "default" - and config.get("description") == LEGACY_DESCRIPTION - and ( - (isinstance(resources, list) and LEGACY_RESOURCE in resources) - or has_legacy_hook(config) - ) - ) -except Exception: - sys.exit(1) -sys.exit(0 if matched else 1) -' "$LEGACY_AGENT_FILE"; then - echo " これは旧版 NDF installer の生成物です。" - if [ -f "$AGENT_FILE" ]; then - # 移行先が既にある場合に上書きすると、そちらの利用者設定を失う。手動判断へ回す。 - echo " ただし $AGENT_FILE が既に存在するため自動移行しません。" - echo " 移行手順:" - echo " 1. 必要な設定が ${LEGACY_AGENT_FILE}.bak にだけ残っていないか確認する" - echo " 2. 不要になったら rm $LEGACY_AGENT_FILE ${LEGACY_AGENT_FILE}.bak" - else - # 旧設定を移行先へ置いてから Step 5 に進める。Step 5 は既存ファイルから - # installer 管理外のキーを引き継ぐため、これだけで利用者設定の移行と - # テンプレート由来キー(エージェント名など)の最新化が両方完了する。 - mv "$LEGACY_AGENT_FILE" "$AGENT_FILE" - MIGRATED_FROM_LEGACY=true - echo " $AGENT_FILE へ自動移行しました(利用者が追記した設定は下で引き継ぎます)。" - echo " 不要になったら: rm ${LEGACY_AGENT_FILE}.bak" - fi - else - echo " NDF 以外が管理している設定です。移行手順:" - echo " 1. 必要な mcpServers / hooks を $AGENT_FILE へ写す" - echo " 2. 不要になったら rm $LEGACY_AGENT_FILE ${LEGACY_AGENT_FILE}.bak" - echo " Kiro 用 MCP プラグインの installer は default.json を更新するため、" - echo " MCP を併用する場合は上記の写し替えが必要です。" - echo " 写した設定は本 installer を再実行しても保持されます。" - fi - echo "" -fi - -# --- Step 5: Generate agent config --- -mkdir -p "$KIRO_DIR/agents" - -# Step 4 で移行した直後は ${LEGACY_AGENT_FILE}.bak が同じ内容のバックアップなので取らない。 -if [ -f "$AGENT_FILE" ] && [ "$MIGRATED_FROM_LEGACY" = false ]; then - cp "$AGENT_FILE" "${AGENT_FILE}.bak" - echo "既存設定をバックアップ: ${AGENT_FILE}.bak" -fi - -# installer が管理するのはテンプレート由来のキー(name / description / tools / -# resources / hooks.agentSpawn)と、フラグで切り替える hooks.stop / mcpServers.codex -# だけ。それ以外(利用者が足した mcpServers エントリ、独自フック、独自キー)は -# 既存の $AGENT_FILE から引き継ぐ。再インストールで写し替えた設定が消えないようにする。 -python3 - "$TEMPLATE_FILE" "$WITH_SLACK" "$WITH_CODEX" "$AGENT_FILE" "$PLUGIN_DIR" "$NDF_VERSION" <<'PY' -import json -import shlex -import sys -from pathlib import Path - -template_file, with_slack, with_codex, agent_file, plugin_dir, ndf_version = sys.argv[1:7] -with open(template_file, encoding="utf-8") as f: - config = json.load(f) - -# 導入後にも版数を確認できるよう description へ埋める。Kiro には plugin.json が -# ないため、これと VERSION ファイルが版数を示す唯一の手段になる。 -config["description"] = config["description"].replace("{{VERSION}}", ndf_version) - -# installer が上書きする範囲 -managed_keys = set(config) | {"mcpServers"} -managed_hooks = set(config.get("hooks") or {}) | {"stop", "userPromptSubmit"} -managed_servers = {"codex"} - -hooks = config.setdefault("hooks", {}) - -# 作業ツリー運用の hook。判定は 3 ランタイム共通のスクリプトが持つ。 -# Kiro CLI は tool 実行前の hook からモデルへ案内を渡す手段が終了コード 2 に -# 限られ、これは tool の実行を拒否する。拒否しない方針のもとでは置けないため、 -# パスを見ない案内をプロンプト送信時の hook が担う(詳細設計 06 の決定 5)。 -guard_script = Path(plugin_dir) / "scripts" / "worktree-guard.sh" -session_script = Path(plugin_dir) / "scripts" / "worktree-session.sh" -hooks.setdefault("agentSpawn", []).append( - { - "command": f"bash {shlex.quote(str(session_script))}", - "timeout_ms": 10000, - } -) -hooks["userPromptSubmit"] = [ - { - "command": f"bash {shlex.quote(str(guard_script))}", - "timeout_ms": 5000, - } -] - -if with_slack == "true": - notify_script = Path(plugin_dir) / "scripts" / "wait-notify.py" - hooks["stop"] = [ - { - "command": f"python3 {shlex.quote(str(notify_script))} --runtime kiro", - "timeout_ms": 15000, - } - ] -else: - hooks.pop("stop", None) - -servers = {} -if with_codex == "true": - servers["codex"] = { - "command": "codex", - "args": ["mcp-server"], - "env": {}, - } - -existing = {} -agent_path = Path(agent_file) -if agent_path.is_file(): - try: - loaded = json.loads(agent_path.read_text(encoding="utf-8")) - except (json.JSONDecodeError, UnicodeDecodeError) as exc: - print(f" WARN: 既存の {agent_file} を読めないため引き継ぎません: {exc}") - else: - if isinstance(loaded, dict): - existing = loaded - else: - print(f" WARN: 既存の {agent_file} が JSON オブジェクトではないため引き継ぎません") - -kept = [] -for key, value in existing.items(): - if key not in managed_keys: - config[key] = value - kept.append(key) -# hooks / mcpServers が dict 以外(配列や文字列)だと .items() で落ちるため、 -# 壊れた JSON と同じく警告して引継ぎ対象から外す。 -for section, target, managed in ( - ("hooks", hooks, managed_hooks), - ("mcpServers", servers, managed_servers), -): - value = existing.get(section) - if value is None: - continue - if not isinstance(value, dict): - print(f" WARN: 既存の {agent_file} の {section} が JSON オブジェクトではないため引き継ぎません") - continue - for key, item in value.items(): - if key not in managed: - target[key] = item - kept.append(f"{section}.{key}") - -if not hooks: - config.pop("hooks", None) -if servers: - config["mcpServers"] = servers -else: - config.pop("mcpServers", None) - -with open(agent_file, "w", encoding="utf-8") as f: - json.dump(config, f, indent=2, ensure_ascii=False) - f.write("\n") - -if kept: - print(" 利用者管理の設定を引き継ぎました: " + ", ".join(sorted(kept))) -PY - -# --- Step 6: Optionally switch the default agent --- -if [ "$SET_DEFAULT" = true ]; then - echo "" - if ! command -v kiro-cli >/dev/null 2>&1; then - echo "ERROR: kiro-cli が見つからないため既定エージェントを変更できません" >&2 - exit 1 - fi - # kiro-cli は workspace エージェントを cwd 配下の .kiro/agents からのみ検出する。 - # 呼び出し元 cwd のままだと、--project で別ディレクトリへ導入したエージェントを - # 見つけられない(または同名の別エージェントを既定にしてしまう)。生成した - # $AGENT_FILE を確実に指すディレクトリで kiro-cli を実行する。 - # workspace: 導入先プロジェクトルート ($PROJECT_ROOT/.kiro/agents) - # global: $HOME ($HOME/.kiro/agents = 生成先。global エージェントはどこからでも - # 解決できるが、cwd 側の同名 workspace エージェントに隠されないようにする) - case "$SCOPE" in - workspace) KIRO_CWD="$PROJECT_ROOT" ;; - *) KIRO_CWD="$HOME" ;; - esac - esc="$(printf '\033')" - # kiro-cli 2.16.1 の agent list は一覧を標準エラー出力へ書く - kiro_default_agent() { - (cd "$KIRO_CWD" && kiro-cli agent list 2>&1) \ - | sed -e "s/${esc}\\[[0-9;]*m//g" \ - | awk '/^\*/ { print $2; exit }' - } - # 表示用の取得は失敗しても続行する(未ログイン等でも set-default の結果は後段で検証する) - current_default="$(kiro_default_agent || true)" - echo "既定エージェントの操作ディレクトリ: $KIRO_CWD" - echo "現在の既定エージェント: ${current_default:-不明}" - echo "変更後の既定エージェント: $AGENT_NAME" - proceed=true - if [ "$ASSUME_YES" = false ]; then - if [ -t 0 ]; then - printf '既定エージェントを %s に変更しますか? [y/N]: ' "$AGENT_NAME" - # EOF (Ctrl+D) で read が非ゼロ終了しても set -e で落とさず、既定の N へ倒す - read -r answer || answer="" - case "$answer" in - [yY]|[yY][eE][sS]) ;; - *) proceed=false ;; - esac - else - echo "確認入力を取得できないため、--set-default の指定を承認とみなして続行します" - fi - fi - if [ "$proceed" = true ]; then - (cd "$KIRO_CWD" && kiro-cli agent set-default "$AGENT_NAME") - # kiro-cli 2.16.1 の set-default はエージェント未検出でも終了コード 0 を返すため、 - # 反映結果を agent list で検証する。 - if [ "$(kiro_default_agent || true)" != "$AGENT_NAME" ]; then - echo "ERROR: 既定エージェントを $AGENT_NAME に変更できませんでした($KIRO_CWD で検出できず)" >&2 - exit 1 - fi - echo "既定エージェントを $AGENT_NAME に変更しました(元に戻す: kiro-cli agent set-default ${current_default:-kiro_default})" - else - echo "既定エージェントは変更しませんでした" - fi -fi - -echo "" -echo "=== インストール完了 ===" -echo " NDF バージョン: $NDF_VERSION" -echo " エージェント設定: $AGENT_FILE" -echo " 常時指示: $STEERING_FILE" -echo " Skills数: $SKILL_COUNT (シンボリックリンク: $SKILLS_DIR)" -echo "" -echo "前提: NDF は 1M コンテキストのモデルを対象に配布 Skill を組んでいます。" -echo " 起動時に読み込む文脈は $SKILL_COUNT 個の SKILL.md と AGENTS.md / README.md / 常時指示です。" -echo " 既定の auto はこの条件を満たします。モデルを固定する場合も 1M のものを選んでください。" -echo "" -echo "Kiro CLIを起動して動作確認してください:" -echo " kiro-cli chat --agent $AGENT_NAME" -if [ "$SET_DEFAULT" = false ]; then - echo "" - echo "既定エージェントとして起動したい場合は --set-default を付けて再実行してください。" -fi diff --git a/plugins/ndf/dev.kiro/prompts/codex.md b/plugins/ndf/dev.kiro/prompts/codex.md deleted file mode 100644 index ddb3b52d0..000000000 --- a/plugins/ndf/dev.kiro/prompts/codex.md +++ /dev/null @@ -1,4 +0,0 @@ -Codex CLIにコード生成・レビュー・調査を委譲してください。 - -external-aiスキルの手順に従って実行してください。Codex CLI 固有の呼び出し方法は -`external-ai` スキルの `references/cli-codex.md`(Kiro では `.kiro/skills/external-ai/references/cli-codex.md`)を参照します。引数があればそのまま使用します。 diff --git a/plugins/ndf/dev.kiro/prompts/fix.md b/plugins/ndf/dev.kiro/prompts/fix.md deleted file mode 100644 index 468dacabd..000000000 --- a/plugins/ndf/dev.kiro/prompts/fix.md +++ /dev/null @@ -1,3 +0,0 @@ -PRのレビューコメントを確認し、修正対応を実行してください。 - -fixスキルの手順に従って実行してください。引数があればそのまま使用します。 diff --git a/plugins/ndf/dev.kiro/prompts/merged.md b/plugins/ndf/dev.kiro/prompts/merged.md deleted file mode 100644 index f0a158330..000000000 --- a/plugins/ndf/dev.kiro/prompts/merged.md +++ /dev/null @@ -1,3 +0,0 @@ -PRマージ後のクリーンアップ(main更新、worktree/ブランチ削除)、または作業中ブランチへの main 取り込みを実行してください。 - -mergedスキルの「用途の切り分け」で依頼の意図を判定し、該当する節の手順に従って実行してください。引数があればそのまま使用します。 diff --git a/plugins/ndf/dev.kiro/prompts/pr-review.md b/plugins/ndf/dev.kiro/prompts/pr-review.md deleted file mode 100644 index 1b4479df1..000000000 --- a/plugins/ndf/dev.kiro/prompts/pr-review.md +++ /dev/null @@ -1,3 +0,0 @@ -PRを専門家としてレビューし、Approve/Request Changesを判定してください。 - -pr-reviewスキルの手順に従って実行してください。引数があればそのまま使用します。 diff --git a/plugins/ndf/dev.kiro/prompts/pr-tests.md b/plugins/ndf/dev.kiro/prompts/pr-tests.md deleted file mode 100644 index 85fb20dab..000000000 --- a/plugins/ndf/dev.kiro/prompts/pr-tests.md +++ /dev/null @@ -1,3 +0,0 @@ -PRのTest Planを自動実行し、結果をPRコメントに反映してください。 - -pr-testsスキルの手順に従って実行してください。引数があればそのまま使用します。 diff --git a/plugins/ndf/dev.kiro/prompts/pr.md b/plugins/ndf/dev.kiro/prompts/pr.md deleted file mode 100644 index 43ca66bab..000000000 --- a/plugins/ndf/dev.kiro/prompts/pr.md +++ /dev/null @@ -1,3 +0,0 @@ -commit, push, PR作成を一括実行してください。 - -prスキルの手順に従って実行してください。引数があればそのまま使用します。 diff --git a/plugins/ndf/docs/kiro-cli.md b/plugins/ndf/docs/kiro-cli.md deleted file mode 100644 index 2981b9557..000000000 --- a/plugins/ndf/docs/kiro-cli.md +++ /dev/null @@ -1,104 +0,0 @@ -# Kiro CLI の運用と制限 - -Kiro CLI 固有の詳細です。導入の手順そのものは -[README の「Kiro CLI」](../README.md#kiro-cli)にあります。 - -## Kiro CLI の運用 - -### 主なオプション - -| オプション | 内容 | -| --- | --- | -| `--project PATH` | 現在のディレクトリではなく PATH へ導入する(`--scope workspace` のみ有効) | -| `--scope workspace\|global` | `workspace`(既定)はプロジェクトの `.kiro/`、`global` は `~/.kiro/` へ導入する | -| `--set-default` | `kiro-cli` の既定エージェントを `ndf` に切り替える(オプトイン) | -| `-y`, `--yes` | `--set-default` の確認プロンプトを省略する | -| `--with-slack` | stop フックに回答・承認待ちの Slack 通知(`scripts/wait-notify.py --runtime kiro`)を追加する | -| `--with-codex` | Codex MCP サーバ設定(`ndf.json` の `mcpServers.codex`)と、Codex CLI 直接実行用プロンプトを追加する | -| `--dry-run` | 書き込みを行わず実行内容を表示する | - -### 既定エージェントの切り替え - -Kiro の既定エージェントは組み込みの `kiro_default` です。`kiro-cli chat` を素で起動する限り、NDF のフック・外部 AI 連携・`resources` は読み込まれません。既定として使いたい場合は `--set-default` を付けます。 - -```bash -bash plugins/ndf/dev.kiro/install.sh --set-default -``` - -利用者の既存設定を無断で奪わないよう、`--set-default` は明示指定したときだけ動作します。実行前に現在の既定エージェントを表示し、対話端末では確認を取ります。元に戻す場合は次のとおりです。 - -```bash -kiro-cli agent set-default kiro_default -``` - -`--scope workspace`(既定)で導入したエージェントはそのプロジェクトでしか見つかりません。既定を `ndf` にしたまま別のディレクトリで `kiro-cli chat` を起動すると `user defined default ndf not found. Falling back to in-memory default` になります。どこでも既定として使いたい場合は `--scope global` と併用してください。 - -### 導入スコープ - -| スコープ | Skills | 常時指示 | エージェント定義 | -| --- | --- | --- | --- | -| `workspace`(既定) | `.kiro/skills/` | `.kiro/steering/ndf-policies.md` | `.kiro/agents/ndf.json` | -| `global` | `~/.kiro/skills/` | `~/.kiro/steering/ndf-policies.md` | `~/.kiro/agents/ndf.json` | - -常時適用したい指示は steering へ置きます。steering はエージェント選択に依存せず読み込まれるため、既定エージェントを書き換えない運用でも効きます。`.kiro/steering/ndf-policies.md` は `plugins/ndf/skills/ndf-policies/SKILL.md` から生成されるため、直接編集しないでください。 - -`ndf-policies` は steering の生成元としてのみ使うため、`.kiro/skills/` へは symlink しません。Kiro は `.kiro/skills/*/SKILL.md` と `.kiro/steering/**/*.md` の両方を文脈へ読み込むため、両方に置くと同じ内容が 2 回注入されます。`plugins/ndf/manifests/kiro-skills.txt` には引き続き載せます(`plugins/ndf/skills/ndf-policies/SKILL.md` が steering の生成元だからです)。 - -`--project` で別ディレクトリへ導入する場合も `--set-default` は正しく動きます。`kiro-cli` は workspace エージェントを cwd 配下の `.kiro/agents/` からのみ検出するため、installer は `kiro-cli` を導入先(`--scope workspace` なら `--project` のパス、`--scope global` なら `$HOME`)で実行します。`kiro-cli agent set-default` はエージェントが見つからなくても終了コード 0 を返すので、installer は実行後に `agent list` で反映を検証し、切り替わっていなければ失敗させます。 - -### 旧バージョンからの移行 - -v4 系の installer は `.kiro/agents/default.json` を生成していました。この設定は Kiro の既定エージェントにならず、フックも `resources` も無効のままでした。エージェント名を `ndf` に変えたため、再インストールが必要です。 - -旧 installer が張った `.kiro/skills/ndf-policies` の symlink は、リンク先が現在のプラグイン配下でなくても再インストール時に削除します(削除するのはリンク自体だけで、リンク先の実体には触れません)。`.kiro/skills/ndf-policies` が symlink ではなく実体のディレクトリやファイルだった場合は、利用者が置いたものの可能性があるため installer は削除せず警告を出します。二重注入を避けるため、内容を確認のうえ手動で退避または削除してください。別の checkout パスから導入した環境でも、steering との二重注入が再インストール 1 回で解消されます。 - -```bash -# 1. 再インストール(旧 default.json は自動でバックアップ・移行されます) -bash plugins/ndf/dev.kiro/install.sh --with-slack - -# 2. 必要なら既定エージェントを切り替える -bash plugins/ndf/dev.kiro/install.sh --set-default - -# 3. 移行を確認したらバックアップを削除する -rm .kiro/agents/default.json.bak -``` - -`.kiro/agents/default.json` は再インストール時に必ず `.kiro/agents/default.json.bak` へバックアップされます。そのうえで installer は次のように振る舞います。 - -| 旧 `default.json` | `ndf.json` | 振る舞い | -| --- | --- | --- | -| 旧版 NDF installer の生成物(`name` が `default`、`description` が旧テンプレートと完全一致、かつ旧 `resources` の `skill://.kiro/skills/**/SKILL.md` または `agentSpawn` フックの `CLAUDE.ndf.md` チェックを持つ) | なし | `ndf.json` へ自動移行する(`default.json` は残らない)。独自に追記した `mcpServers` / フック / 独自キーは下表のマージで保持される | -| 同上 | あり | 自動移行しない(`ndf.json` の設定を失わないため)。`default.json` は残るので、必要な設定を写したうえで削除する | -| NDF 以外が管理している(利用者が作成したものなど) | 問わない | 自動移行しない。勝手に移行すると利用者の設定を壊すため、バックアップと移行手順の案内のみを行う | - -`--dry-run` では上記の移行を含め一切の書き込みを行いません(旧設定を検出したことだけ表示します)。 - -Kiro 用 MCP プラグインの installer(`plugins/mcp/<プラグイン名>/dev.kiro/install.sh`)は `.kiro/agents/default.json` を更新します。自動移行後に MCP installer を実行すると `default.json` が再び作られるため、`mcpServers` を `.kiro/agents/ndf.json` へ写してください。写し替えは一度だけで済みます。`install.sh` を再実行しても、写した `mcpServers` は保持されます。 - -### 再インストール時に保持される設定 - -`install.sh` は `.kiro/agents/ndf.json` を毎回テンプレートから再生成しますが、上書きするのは installer が管理するキーだけです。既存ファイルにある利用者管理の設定は読み取ってマージし直します。 - -| 区分 | キー | 再実行時の扱い | -| --- | --- | --- | -| installer 管理 | `name` / `description` / `tools` / `resources` / `hooks.agentSpawn` | テンプレートから再生成する(上書き) | -| installer 管理 | `hooks.stop` | `--with-slack` の有無で生成・削除する | -| installer 管理 | `mcpServers.codex` | `--with-codex` の有無で生成・削除する | -| 利用者管理 | 上記以外の `mcpServers` エントリ、`hooks` の項目、トップレベルキー | そのまま引き継ぐ | - -引き継いだ項目は実行ログに `利用者管理の設定を引き継ぎました: mcpServers.bigquery` のように表示します。再生成の前に `.kiro/agents/ndf.json.bak` へバックアップも取るため、意図しない結果になった場合は差し戻せます。 - -`mcpServers.codex` だけは installer 管理です。`--with-codex` を付けずに再実行すると削除されるため、Codex MCP を使う場合は `--with-codex` を付けたまま運用してください。 - -## Kiro CLI の制限 - -### `allowed-tools` は事前承認にならない - -Skill frontmatter の `allowed-tools` は、プロジェクト配置(`.kiro/skills/`)では事前承認として機能しません([kirodotdev/Kiro#6055](https://github.com/kirodotdev/Kiro/issues/6055))。`allowed-tools: execute_bash` を持つ Skill でも `Command execute_bash is rejected because it matches one or more rules on the denied list` になります。 - -Kiro では、Skill の実行時にツール利用の確認が入る前提で操作してください。NDF の Skill 本文は「無確認で実行される」前提を持ちません。 - -### プラグイン機構がない - -Kiro CLI には Skill・フック・外部連携・常時指示をまとめて配布する仕組みがありません([kirodotdev/Kiro#8578](https://github.com/kirodotdev/Kiro/issues/8578))。Kiro IDE の Powers は CLI では使えないため、`install.sh` による導入を継続します。 - diff --git a/plugins/ndf/manifests/agy-skills.txt b/plugins/ndf/manifests/agy-skills.txt deleted file mode 100644 index 4b6339464..000000000 --- a/plugins/ndf/manifests/agy-skills.txt +++ /dev/null @@ -1,45 +0,0 @@ -cherry-pick-pr -cross-refactoring -cross-review -deploy -development-workflow -docker-container-access -chrome-cdp -external-ai -fix -implementation-plan -layout-review -investigation-rules -decision-request -issue-plan-strategy -logging-guidelines -document-style -notion-writing -merged -ndf-policies -out-of-scope -backlog-refinement -estimate -progress-tracking -plan-to-spec -pr -pr-review -pr-tests -problem-solving -qa-security-scan -quality-gates -refactoring -release-verification -requirements-design -design -document-sources -document-drafting -document-rebuild -document-systems -retrospective -tdd-cycle -worktree -release -google-workspace -ml-model-structure -skill-stats diff --git a/plugins/ndf/manifests/kiro-skills.txt b/plugins/ndf/manifests/kiro-skills.txt deleted file mode 100644 index 483b9a15f..000000000 --- a/plugins/ndf/manifests/kiro-skills.txt +++ /dev/null @@ -1,46 +0,0 @@ -pr -pr-tests -fix -pr-review -cross-review -cross-refactoring -merged -ndf-policies -document-style -notion-writing -docker-container-access -chrome-cdp -implementation-plan -layout-review -investigation-rules -decision-request -problem-solving -logging-guidelines -cherry-pick-pr -deploy -external-ai -statusline -issue-plan-strategy -plan-to-spec -qa-security-scan -development-workflow -requirements-design -design -document-sources -document-drafting -document-rebuild -document-systems -tdd-cycle -refactoring -quality-gates -worktree -out-of-scope -backlog-refinement -estimate -progress-tracking -release-verification -retrospective -release -google-workspace -ml-model-structure -skill-stats diff --git a/plugins/ndf/scripts/hook_lib/worktree.py b/plugins/ndf/scripts/hook_lib/worktree.py index ec4a2ad9a..76ee5279e 100644 --- a/plugins/ndf/scripts/hook_lib/worktree.py +++ b/plugins/ndf/scripts/hook_lib/worktree.py @@ -1,6 +1,7 @@ """メインディレクトリを編集しようとしたときに、worktree で作業する旨を伝える guard(#1142 の決定 20)。 -結線先は 3 つある(`hooks/*.json`・`dev.kiro/install.sh`・`dev.agy/hooks.json`)。 +結線先は `hooks/*.json`(Claude Code・Codex)。Kiro CLI と agy の分岐は、配布を終える前(#2231)に導入した手元の hook が +この入口を呼び続けるために残す。 - PreToolUse(Claude Code / Codex CLI): 編集先のパスを見た案内を `additionalContext` でモデルへ渡す - プロンプト送信時(Kiro CLI): パスを見ない案内を標準出力へ書く - PreToolUse(agy): 案内をセッションの状態ファイルの `pending` へ積み、次のモデル呼び出しの前に diff --git a/plugins/ndf/scripts/progress-record.sh b/plugins/ndf/scripts/progress-record.sh index f64e9271a..8e5b52564 100644 --- a/plugins/ndf/scripts/progress-record.sh +++ b/plugins/ndf/scripts/progress-record.sh @@ -43,8 +43,8 @@ TOOL=progress-record . "$SCRIPT_DIR/lib/projects-common.sh" 2>/dev/null \ || sr_emit --tool "$TOOL" --status stopped --summary "lib/projects-common.sh を読めない" --code 3 # 工程名・モード・進め方の一覧と通過記録は development-workflow のライブラリが持つ(#725 の決定 2・3)。 -# **同じプラグインの根の相対で辿る。** `cd -P` で実体へ解決しない。agy の配置(dev.agy/scripts と -# dev.agy/skills/development-workflow がどちらも symlink)でも、`scripts/` の 1 つ上に `skills/` がある。 +# **同じプラグインの根の相対で辿る。** `cd -P` で実体へ解決しない。`scripts/` が symlink で置かれた配置でも、 +# `scripts/` の 1 つ上に `skills/` がある。 # shellcheck source=../skills/development-workflow/scripts/lib/workflow-common.sh . "$SCRIPT_DIR/../skills/development-workflow/scripts/lib/workflow-common.sh" 2>/dev/null \ || sr_emit --tool "$TOOL" --status stopped --summary "development-workflow の workflow-common.sh を読めない" --code 3 diff --git a/plugins/ndf/scripts/release-steps.py b/plugins/ndf/scripts/release-steps.py index 7976a0b25..2e57506c3 100755 --- a/plugins/ndf/scripts/release-steps.py +++ b/plugins/ndf/scripts/release-steps.py @@ -35,7 +35,7 @@ python3 release-steps.py approve --approval <承認資料> --facts-from <開発版の状態> [--answer-from <状態>] --by user python3 release-steps.py approval-facts --version <版> --prs ... [--prev-tag <タグ>] [--plugin <名前>] [--root ] python3 release-steps.py notes --version <版> --prs ... [--approval <提示物>] - [--verified claude,codex,kiro] [--ref <ブランチ>] [--plugin <名前>] [--root ] + [--verified claude,codex] [--ref <ブランチ>] [--plugin <名前>] [--root ] release --channel prod は承認したコミットを受け取り、配布の PR をマージした後の `origin/<ベースブランチ>` の先端と比べ、 承認の外の変更があれば本番チャネルの PR もタグも作らずに承認ゲート(10)で止まる(release_lib/approval.py。#815)。 @@ -338,7 +338,6 @@ def plan_bump(root, pdir, plugin, old, new): for rel, _ in ( (".claude-plugin/plugin.json", True), (".codex-plugin/plugin.json", False), - ("dev.agy/plugin.json", False), ("plugin.json", False), ): f = pdir / rel @@ -365,7 +364,6 @@ def plan_bump(root, pdir, plugin, old, new): if plugin == "ndf": ed.sub(readme, r"\*\*NDFプラグイン v", "README.md の概要の版") nr = pdir / "README.md" - ed.sub(nr, r"(Kiro CLI用 / v", "plugins/ndf/README.md の Kiro の確認例") ed.sub(nr, r"/plugins/cache/ai-plugins/ndf/", "plugins/ndf/README.md の Codex のパス例", count=2) ed.sub(nr, r"ndf@ai-plugins\s+installed", "plugins/ndf/README.md の codex plugin list の出力例") bump.versioning_doc(ed) @@ -1192,7 +1190,7 @@ def build_parser() -> argparse.ArgumentParser: ) p.add_argument("--plugin", help="配るプラグイン(既定は宣言の release.plugin)") p.add_argument("--approval", help="本番承認の提示物。渡すと「配る中身」「検証への配布で確かめたこと」の欄を書く") - p.add_argument("--verified", default="", help="--approval: 導入を確かめた経路(カンマ区切り。例 claude,codex,kiro)") + p.add_argument("--verified", default="", help="--approval: 導入を確かめた経路(カンマ区切り。例 claude,codex)") p.add_argument("--ref", help="--approval: 検証への配布で導入した ref(既定は宣言のベースブランチ)") p.add_argument( "--check-state", diff --git a/plugins/ndf/scripts/release-verification-steps.py b/plugins/ndf/scripts/release-verification-steps.py index a88fa97bd..58ab68412 100755 --- a/plugins/ndf/scripts/release-verification-steps.py +++ b/plugins/ndf/scripts/release-verification-steps.py @@ -2,7 +2,7 @@ """release-verification-steps.py: リリース後テストの導入確認(#862。試作は #827 の phase-steps.py)。 python3 release-verification-steps.py verify-install --ref <ベースブランチ|本番チャネル> --expect <版> - [--plugins <名前>,...] [--runtimes claude,codex,kiro] [--root ] + [--plugins <名前>,...] [--runtimes claude,codex] [--root ] 隔離した HOME で ref から導入し、導入された版と、前の正式版のタグから変わったファイルの中身が ref と一致するかを確かめる。利用者の HOME の設定が変わっていないことも確かめる。 @@ -138,7 +138,7 @@ def same_entry(s, t): """s と t の中身が同じか。symlink はリンク先の文字列か、辿った先の中身で比べる。 導入先は symlink をそのまま残すことも、辿った先を複製することもあるため、どちらでも一致とする。 - ディレクトリ(例: dev.agy/skills/<名前> → ../../skills/<名前>)は配下のファイルをすべて比べる。 + ディレクトリは配下のファイルをすべて比べる。 """ s, t = Path(s), Path(t) if s.is_symlink() and t.is_symlink() and os.readlink(s) == os.readlink(t): @@ -164,7 +164,7 @@ def compare_files(src_root, dst, rel_plugin, files, label, keeps_symlinks=True): if not s.exists(): continue # ref で消えたファイル if not keeps_symlinks and s.is_symlink(): - continue # 導入先に入らない(例: dev.agy/skills/<名前> → ../../skills/<名前>) + continue # 導入先に入らない if not same_entry(s, t): out.append(f"{label}: {f}") return out @@ -220,83 +220,6 @@ def verify_codex(env, ref, plugins, expect, where): return res, dirs -def verify_kiro(env, src, tmp, expect): - proj = Path(tmp) / "kiro-project" - proj.mkdir() - res = {"exit": 0, "version": None} - code, out = run_env_i(["bash", str(src / "plugins/ndf/dev.kiro/install.sh"), "--project", str(proj), "--yes"], env, cwd=str(src)) - res["exit"] = code - if code != 0: - res["error"] = out.strip()[-500:] - return res, None - agent = proj / ".kiro" / "agents" / "ndf.json" - if agent.is_file(): - try: - desc = json.loads(agent.read_text(encoding="utf-8")).get("description", "") - except (OSError, ValueError): - desc = "" - m = re.search(r"v(\d+\.\d+\.\d+(?:-[0-9A-Za-z.]+)?)", desc) - res["version"] = m.group(1) if m else None - return res, proj - - -KIRO_PLUGIN = "plugins/ndf" -KIRO_POLICY = "ndf-policies" - - -def kiro_manifest_skills(src): - """manifests/kiro-skills.txt に載る Skill 名(コメント・空行を除く)。""" - text = (Path(src) / KIRO_PLUGIN / "manifests" / "kiro-skills.txt").read_text(encoding="utf-8") - return sorted({line.split("#", 1)[0].strip() for line in text.splitlines()} - {""}) - - -def kiro_steering_body(policy_file): - """install.sh が steering へ写す ndf-policies の本文(frontmatter を除いた部分)。""" - text = Path(policy_file).read_text(encoding="utf-8") - if text.startswith("---\n"): - end = text.find("\n---\n", 3) - if end != -1: - text = text[end + len("\n---\n") :] - return text.strip("\n") - - -def compare_kiro(src, proj): - """Kiro の導入先(/.kiro)を ref の展開物と比べ、一致しないものを返す。 - - manifest の Skill は .kiro/skills/<名前> のリンク、dev.kiro/prompts/*.md は .kiro/prompts/ の複製、 - ndf-policies は .kiro/steering/ndf-policies.md の本文、agents/ndf.json は JSON として読めることを見る。 - """ - src, kiro = Path(src), Path(proj) / ".kiro" - plugin = src / KIRO_PLUGIN - out = [] - for name in kiro_manifest_skills(src): - s, t = plugin / "skills" / name, kiro / "skills" / name - if not (s / "SKILL.md").is_file(): - continue # installer も SKIP する - if name == KIRO_POLICY: - if t.exists() or t.is_symlink(): - out.append(f"kiro: {t.relative_to(proj)} が残っている(steering へ移した Skill)") - continue - if not same_entry(s, t): - out.append(f"kiro: {t.relative_to(proj)}") - for f in sorted((plugin / "dev.kiro" / "prompts").glob("*.md")): - if f.name == "codex.md": - continue # --with-codex のときだけ配る - t = kiro / "prompts" / f.name - if not (t.is_file() and filecmp.cmp(f, t, shallow=False)): - out.append(f"kiro: {t.relative_to(proj)}") - steering = kiro / "steering" / f"{KIRO_POLICY}.md" - body = kiro_steering_body(plugin / "skills" / KIRO_POLICY / "SKILL.md") - if not (steering.is_file() and steering.read_text(encoding="utf-8").endswith(f"\n{body}\n")): - out.append(f"kiro: {steering.relative_to(proj)}") - agent = kiro / "agents" / "ndf.json" - try: - json.loads(agent.read_text(encoding="utf-8")) - except (OSError, ValueError): - out.append(f"kiro: {agent.relative_to(proj)}") - return out - - def install_source(root, a): """導入元(owner/repo・マーケットプレイスの名前・本番チャネル)と、確かめるプラグイン。値は origin の URL と宣言から読む (#1336 の決定 8)。--ref は宣言のベースブランチか本番チャネルに限る。""" @@ -345,10 +268,6 @@ def _verify_runtimes(a, root, src, tmp, runtimes, plugins, rel, prev, ref_rev, w mismatch.append(f"{name}: {p} の導入先が無い") else: mismatch += compare_files(src, d, rel[p], changed[p], name, keeps_symlinks=(name != "codex")) - if "kiro" in runtimes: - runtimes_res["kiro"], proj = verify_kiro(env, src, tmp, a.expect) - if proj is not None: - mismatch += compare_kiro(src, proj) return runtimes_res, mismatch @@ -396,7 +315,7 @@ def cmd_verify_install(a): root = git_root(a.root) where, plugins = install_source(root, a) runtimes = [r.strip() for r in a.runtimes.split(",") if r.strip()] - bad = [r for r in runtimes if r not in ("claude", "codex", "kiro")] + bad = [r for r in runtimes if r not in ("claude", "codex")] if bad: raise StepError(f"未知の runtime: {','.join(bad)}", EXIT_UNREADABLE) rel = {p: plugin_dir(root, p).relative_to(root).as_posix() for p in plugins} @@ -430,7 +349,7 @@ def build_parser(): p.add_argument("--ref", required=True, help="導入する ref(宣言のベースブランチか本番チャネル)") p.add_argument("--expect", required=True, type=version_arg) p.add_argument("--plugins", help="カンマ区切り(例 ndf,mcp-serena。既定は宣言の release.plugin)") - p.add_argument("--runtimes", default="claude,codex,kiro", help="カンマ区切り(claude,codex,kiro)") + p.add_argument("--runtimes", default="claude,codex", help="カンマ区切り(claude,codex)") p.set_defaults(func=cmd_verify_install) return ap diff --git a/plugins/ndf/scripts/tests/test_hook_deadline.py b/plugins/ndf/scripts/tests/test_hook_deadline.py index 72a83155f..cbbf4ef5a 100644 --- a/plugins/ndf/scripts/tests/test_hook_deadline.py +++ b/plugins/ndf/scripts/tests/test_hook_deadline.py @@ -130,15 +130,9 @@ def walk(node, under_pre: bool) -> None: return found -@pytest.mark.parametrize("rel", ["hooks/claude.json", "hooks/codex.json", "dev.agy/hooks.json"], ids=["claude", "codex", "agy"]) +@pytest.mark.parametrize("rel", ["hooks/claude.json", "hooks/codex.json"], ids=["claude", "codex"]) def test_the_hook_limit_leaves_the_margin(rel): limits = _hook_timeouts(PLUGIN / rel) assert limits assert all(deadline.DEADLINE_SECONDS + deadline.MARGIN_SECONDS <= t for t in limits) - -def test_the_kiro_prompt_hook_limit_leaves_the_margin(): - text = (PLUGIN / "dev.kiro" / "install.sh").read_text(encoding="utf-8") - m = re.search(r'hooks\["userPromptSubmit"\]\s*=\s*\[.*?guard_script.*?"timeout_ms":\s*(\d+)', text, re.S) - assert m - assert deadline.DEADLINE_SECONDS + deadline.MARGIN_SECONDS <= int(m.group(1)) / 1000 diff --git a/plugins/ndf/scripts/tests/test_progress_record_stages.py b/plugins/ndf/scripts/tests/test_progress_record_stages.py index 0c90eb47c..ba0daf7ee 100644 --- a/plugins/ndf/scripts/tests/test_progress_record_stages.py +++ b/plugins/ndf/scripts/tests/test_progress_record_stages.py @@ -23,7 +23,6 @@ SCRIPTS = Path(__file__).resolve().parents[1] SYNC = SCRIPTS / "projects-sync.sh" RECORD = SCRIPTS / "progress-record.sh" -AGY_RECORD = SCRIPTS.parent / "dev.agy" / "scripts" / "progress-record.sh" WF_LIB = SCRIPTS.parent / "skills" / "development-workflow" / "scripts" / "lib" / "workflow-common.sh" STAGE_CHECK = SCRIPTS.parent / "skills" / "development-workflow" / "scripts" / "stage-check.sh" GUARD = SCRIPTS.parent / "skills" / "development-workflow" / "scripts" / "workflow-guard.sh" @@ -340,14 +339,6 @@ def test_the_body_lists_the_stages_of_the_one_stage_table(env: Env) -> None: assert name not in common -def test_the_agy_layout_records_through_the_symlink(env: Env) -> None: - """agy の配置(dev.agy/scripts は symlink)から呼んでも積まれる。""" - assert AGY_RECORD.exists() - out = env.run(str(AGY_RECORD), "42", "設計") - assert out.returncode == 0, out.stderr - assert env.stages(42) == ["設計"] - - @pytest.mark.parametrize("verb", ["view", "edit"]) def test_a_failing_gh_still_records_the_stage_once(env: Env, verb: str) -> None: """gh が失敗しても通過記録は 1 回だけ積まれ、結果は ok 以外(#671)。""" diff --git a/plugins/ndf/scripts/tests/test_step_scripts.py b/plugins/ndf/scripts/tests/test_step_scripts.py index e2d6281b8..1d768e588 100644 --- a/plugins/ndf/scripts/tests/test_step_scripts.py +++ b/plugins/ndf/scripts/tests/test_step_scripts.py @@ -431,7 +431,7 @@ def load_verification(): @pytest.mark.parametrize("installed", ["symlink", "copy", "differs", "missing"]) def test_compare_files_follows_a_symlink_to_a_directory(tmp_path, installed): - """dev.agy/skills/<名前> → ../../skills/<名前> のようなディレクトリへの symlink を比べられる。""" + """<配置>/<名前> → ../skills/<名前> のようなディレクトリへの symlink を比べられる。""" mod = load_verification() src = tmp_path / "src" / "p" (src / "skills" / "a").mkdir(parents=True) @@ -467,49 +467,6 @@ def test_compare_files_skips_symlinks_for_a_runtime_that_drops_them(tmp_path): assert out == ["codex: p/skills/a/SKILL.md"] -def kiro_project(tmp_path): - """この checkout の dev.kiro/install.sh で /proj へ導入し、(src, proj) を返す。""" - src = SCRIPTS.parents[2] - proj = tmp_path / "proj" - proj.mkdir() - p = subprocess.run( - ["bash", str(src / "plugins/ndf/dev.kiro/install.sh"), "--project", str(proj), "--yes"], - capture_output=True, - text=True, - cwd=str(src), - ) - assert p.returncode == 0, p.stdout + p.stderr - return src, proj - - -def test_compare_kiro_accepts_what_the_installer_made(tmp_path): - src, proj = kiro_project(tmp_path) - assert load_verification().compare_kiro(src, proj) == [] - - -@pytest.mark.parametrize("broken", ["skill_link", "prompt", "steering", "agent", "policy_link"]) -def test_compare_kiro_reports_missing_or_changed_outputs(tmp_path, broken): - """Kiro の生成物(skills / prompts / steering / agents)の欠落・内容違いを不一致にする。""" - src, proj = kiro_project(tmp_path) - kiro = proj / ".kiro" - if broken == "skill_link": - (kiro / "skills" / "pr").unlink() - want = "kiro: .kiro/skills/pr" - elif broken == "prompt": - (kiro / "prompts" / "pr.md").write_text("changed\n", encoding="utf-8") - want = "kiro: .kiro/prompts/pr.md" - elif broken == "steering": - (kiro / "steering" / "ndf-policies.md").write_text("stale\n", encoding="utf-8") - want = "kiro: .kiro/steering/ndf-policies.md" - elif broken == "agent": - (kiro / "agents" / "ndf.json").write_text("{", encoding="utf-8") - want = "kiro: .kiro/agents/ndf.json" - else: - (kiro / "skills" / "ndf-policies").symlink_to(src / "plugins/ndf/skills/ndf-policies") - want = "kiro: .kiro/skills/ndf-policies が残っている(steering へ移した Skill)" - assert load_verification().compare_kiro(src, proj) == [want] - - # --- phase-steps.py(互換の入口) -------------------------------------------- diff --git a/plugins/ndf/skills/worktree/tests/test_agy_hooks.py b/plugins/ndf/skills/worktree/tests/test_agy_hooks.py deleted file mode 100644 index 6515e4c69..000000000 --- a/plugins/ndf/skills/worktree/tests/test_agy_hooks.py +++ /dev/null @@ -1,276 +0,0 @@ -"""agy の hook の経路を検証する(#215 の受け入れ条件 A4〜A6)。 - -agy は案内を作る時点と渡せる時点が離れている。**tool 実行前の hook がモデルへ文言を返す口 -(`reason`)は、拒否のときにしか働かない**(設計の決定 4 の実測)。NDF の誘導は操作を止め -ないため、案内はセッションの記録へ積み、次のモデル呼び出しの前に `PreInvocation` が -`injectSteps[].userMessage` で渡す。 - -セッション開始時にあたる事象も無いため、モデル呼び出しの通し番号(`invocationNum`)が 0 の -ときを開始時として扱う(決定 5)。 - -入力の形は agy 1.1.25 で実測したものである。tool の名前は `toolCall.name`、編集先は -`toolCall.args.TargetFile`、作業ディレクトリは `workspacePaths[0]`、セッションの識別子は -`conversationId` にある。**`write_file` は権限の名前であって tool の名前ではない。** -""" - -from __future__ import annotations - -import json -import os -import subprocess -from pathlib import Path - -import pytest - -from hook_lib import payload -from worktree_helpers import GUARD, SCRIPTS_DIR, SESSION, write_declaration - -HOOKS_JSON = SCRIPTS_DIR.parent / "dev.agy" / "hooks.json" -CONVERSATION = "c-0001" - - -def run_hook(script: Path, payload: dict, cwd: Path, tmpdir: Path) -> dict: - env = os.environ.copy() - env["LC_ALL"] = "C" - env["TMPDIR"] = str(tmpdir) - proc = subprocess.run( - ["bash", str(script)], - input=json.dumps(payload), - cwd=str(cwd), - env=env, - capture_output=True, - text=True, - ) - return {"rc": proc.returncode, "out": proc.stdout, "err": proc.stderr} - - -def edit_call(path: Path, cwd: Path, tool: str = "write_to_file") -> dict: - """agy の `PreToolUse` の入力。編集先は絶対パスで届く。""" - return { - "conversationId": CONVERSATION, - "stepIdx": 3, - "workspacePaths": [str(cwd)], - "modelName": "gemini-3.8-flash-high", - "toolCall": {"name": tool, "args": {"TargetFile": str(path), "CodeContent": "x"}}, - } - - -def command_call(command: str, cwd: Path) -> dict: - """`run_command` は実行ディレクトリを `Cwd` で渡す。""" - return { - "conversationId": CONVERSATION, - "stepIdx": 4, - "workspacePaths": [str(cwd)], - "toolCall": {"name": "run_command", "args": {"CommandLine": command, "Cwd": str(cwd)}}, - } - - -def invocation(cwd: Path, number: int = 1) -> dict: - """agy の `PreInvocation` の入力。""" - return { - "conversationId": CONVERSATION, - "invocationNum": number, - "initialNumSteps": 4, - "workspacePaths": [str(cwd)], - } - - -def declared(main_repo: Path) -> Path: - write_declaration(main_repo, json.dumps({"version": 1})) - return main_repo - - -def state_file(tmpdir: Path) -> Path: - return tmpdir / f"ndf-worktree-{CONVERSATION}.json" - - -def pending_of(tmpdir: Path) -> list[str]: - path = state_file(tmpdir) - if not path.is_file(): - return [] - return json.loads(path.read_text(encoding="utf-8")).get("pending", []) - - -def injected(result: dict) -> str: - """`injectSteps` の `userMessage` を連結して返す。無ければ空文字。""" - if not result["out"].strip(): - return "" - steps = json.loads(result["out"]).get("injectSteps", []) - return "\n".join(step.get("userMessage", "") for step in steps) - - -# --- A4: 誘導がモデルへ届く ------------------------------------------------- - - -def test_protected_edit_is_queued_and_allowed(main_repo: Path, tmp_path: Path) -> None: - """主ディレクトリの編集は記録へ積まれ、tool の実行は止まらない。""" - declared(main_repo) - tmpdir = tmp_path / "state" - tmpdir.mkdir() - target = main_repo / "plugins" / "ndf" / "README.md" - - result = run_hook(GUARD, edit_call(target, main_repo), cwd=main_repo, tmpdir=tmpdir) - - assert result["rc"] == 0, result["err"] - assert json.loads(result["out"]) == {"decision": "allow"} - queued = pending_of(tmpdir) - assert len(queued) == 1 - assert "plugins/ndf/README.md" in queued[0] - - -def test_pending_reaches_the_next_invocation(main_repo: Path, tmp_path: Path) -> None: - """積まれた案内は、次のモデル呼び出しの前に `userMessage` として渡される。""" - declared(main_repo) - tmpdir = tmp_path / "state" - tmpdir.mkdir() - target = main_repo / "plugins" / "ndf" / "README.md" - run_hook(GUARD, edit_call(target, main_repo), cwd=main_repo, tmpdir=tmpdir) - - result = run_hook(SESSION, invocation(main_repo), cwd=main_repo, tmpdir=tmpdir) - - assert result["rc"] == 0, result["err"] - assert "plugins/ndf/README.md" in injected(result) - - -def test_pending_is_delivered_once(main_repo: Path, tmp_path: Path) -> None: - """取り出した案内は記録から消える。同じ案内を毎回渡さない。""" - declared(main_repo) - tmpdir = tmp_path / "state" - tmpdir.mkdir() - run_hook( - GUARD, - edit_call(main_repo / "plugins" / "ndf" / "README.md", main_repo), - cwd=main_repo, - tmpdir=tmpdir, - ) - run_hook(SESSION, invocation(main_repo), cwd=main_repo, tmpdir=tmpdir) - - assert pending_of(tmpdir) == [] - again = run_hook(SESSION, invocation(main_repo, number=2), cwd=main_repo, tmpdir=tmpdir) - assert injected(again) == "" - - -@pytest.mark.parametrize("tool", ["write_to_file", "replace_file_content"]) -def test_edit_tools_are_covered(main_repo: Path, tmp_path: Path, tool: str) -> None: - """編集を伴う tool は 2 つとも誘導の対象になる。 - - `write_file` は権限の名前であり、`toolCall.name` には届かない(実測)。 - """ - declared(main_repo) - tmpdir = tmp_path / "state" - tmpdir.mkdir() - target = main_repo / "src" / "app.py" - - run_hook(GUARD, edit_call(target, main_repo, tool=tool), cwd=main_repo, tmpdir=tmpdir) - - assert any("src/app.py" in item for item in pending_of(tmpdir)) - - -def test_shell_write_is_covered(main_repo: Path, tmp_path: Path) -> None: - """`run_command` は書き込みの形から編集先を推定する。起点は `Cwd` である。""" - declared(main_repo) - tmpdir = tmp_path / "state" - tmpdir.mkdir() - - run_hook(GUARD, command_call("echo x > src/app.py", main_repo), cwd=main_repo, tmpdir=tmpdir) - - assert any("src/app.py" in item for item in pending_of(tmpdir)) - - -def test_first_invocation_reports_stray_changes(main_repo: Path, tmp_path: Path) -> None: - """通し番号が 0 のとき、主ディレクトリの逸脱を提示する(開始時にあたる)。""" - declared(main_repo) - (main_repo / "README.md").write_text("# changed\n", encoding="utf-8") - tmpdir = tmp_path / "state" - tmpdir.mkdir() - - result = run_hook(SESSION, invocation(main_repo, number=0), cwd=main_repo, tmpdir=tmpdir) - - assert "未コミット変更" in injected(result) - - -def test_later_invocation_does_not_repeat_the_report(main_repo: Path, tmp_path: Path) -> None: - """通し番号が 0 でないときは逸脱の提示を行わない。毎回の呼び出しで繰り返さない。""" - declared(main_repo) - (main_repo / "README.md").write_text("# changed\n", encoding="utf-8") - tmpdir = tmp_path / "state" - tmpdir.mkdir() - - result = run_hook(SESSION, invocation(main_repo, number=3), cwd=main_repo, tmpdir=tmpdir) - - assert injected(result) == "" - assert json.loads(result["out"]) == {} - - -# --- A5: 作業ツリーの中では出さない ----------------------------------------- - - -def test_edit_inside_the_worktree_queues_nothing(main_repo: Path, worktree: Path, tmp_path: Path) -> None: - """作業ツリーの中の編集は記録へ積まれない。""" - declared(main_repo) - tmpdir = tmp_path / "state" - tmpdir.mkdir() - target = worktree / "plugins" / "ndf" / "README.md" - - result = run_hook(GUARD, edit_call(target, worktree), cwd=worktree, tmpdir=tmpdir) - - assert result["rc"] == 0, result["err"] - assert pending_of(tmpdir) == [] - - -def test_allowed_path_queues_nothing(main_repo: Path, tmp_path: Path) -> None: - """許可されたパスの編集は記録へ積まれない。""" - declared(main_repo) - tmpdir = tmp_path / "state" - tmpdir.mkdir() - - run_hook(GUARD, edit_call(main_repo / "docs" / "note.md", main_repo), cwd=main_repo, tmpdir=tmpdir) - - assert pending_of(tmpdir) == [] - - -# --- A6: 宣言が無いリポジトリでは何もしない --------------------------------- - - -def test_no_declaration_produces_no_output(main_repo: Path, tmp_path: Path) -> None: - """`.ndf/worktree.json` が無ければ、どちらの hook も何も出力しない。""" - tmpdir = tmp_path / "state" - tmpdir.mkdir() - target = main_repo / "plugins" / "ndf" / "README.md" - - guard = run_hook(GUARD, edit_call(target, main_repo), cwd=main_repo, tmpdir=tmpdir) - session = run_hook(SESSION, invocation(main_repo, number=0), cwd=main_repo, tmpdir=tmpdir) - - assert guard["rc"] == 0 and guard["out"].strip() == "" - assert session["rc"] == 0 and session["out"].strip() == "" - - -# --- hook の結線 ------------------------------------------------------------- - - -def test_hooks_json_matcher_matches_the_library() -> None: - """`dev.agy/hooks.json` の `matcher` は共通ライブラリの一覧から作る。 - - tool 名の一覧を 2 箇所に持つと、判定を足しても hook が起動しない状態になる。 - """ - config = json.loads(HOOKS_JSON.read_text(encoding="utf-8")) - matchers = {entry.get("matcher") for hook in config.values() for entry in hook.get("PreToolUse", [])} - assert payload.tool_matcher() in matchers, matchers - - -def test_hooks_json_wires_both_scripts() -> None: - """`PreToolUse` は誘導を、`PreInvocation` は開始時と案内の受け渡しを担う。 - - `PreInvocation` は matcher を取らず、handler の配列を直に持つ(agy の書式)。 - """ - config = json.loads(HOOKS_JSON.read_text(encoding="utf-8")) - pre_tool = [hook["command"] for definition in config.values() for entry in definition.get("PreToolUse", []) for hook in entry["hooks"]] - pre_invocation = [handler["command"] for definition in config.values() for handler in definition.get("PreInvocation", [])] - assert any("worktree-guard.sh" in c for c in pre_tool), pre_tool - assert any("worktree-session.sh" in c for c in pre_invocation), pre_invocation - - -@pytest.mark.parametrize("tool", ["write_to_file", "replace_file_content"]) -def test_agy_tool_names_are_in_the_library(tool: str) -> None: - """agy の tool 名は共通ライブラリの一覧に載っている。""" - assert tool in payload.EDIT_TOOLS, payload.EDIT_TOOLS diff --git a/plugins/ndf/skills/worktree/tests/test_hook_wiring.py b/plugins/ndf/skills/worktree/tests/test_hook_wiring.py index 49390b2ca..abf98e533 100644 --- a/plugins/ndf/skills/worktree/tests/test_hook_wiring.py +++ b/plugins/ndf/skills/worktree/tests/test_hook_wiring.py @@ -16,7 +16,6 @@ from worktree_helpers import SCRIPTS_DIR HOOKS_DIR = SCRIPTS_DIR.parent / "hooks" -INSTALL_SH = SCRIPTS_DIR.parent / "dev.kiro" / "install.sh" def guard_entries(runtime: str) -> list[dict]: @@ -44,11 +43,3 @@ def test_sessionstart_runs_the_session_script(runtime: str) -> None: commands = [hook["command"] for entry in config["hooks"]["SessionStart"] for hook in entry["hooks"]] assert any("worktree-session.sh" in c for c in commands), commands - -def test_kiro_installer_wires_both_hooks() -> None: - """Kiro CLI は導入スクリプトがエージェント定義へ書き込む。""" - body = INSTALL_SH.read_text(encoding="utf-8") - assert "worktree-session.sh" in body - assert "worktree-guard.sh" in body - assert '"userPromptSubmit"' in body - assert 'hooks.setdefault("agentSpawn"' in body diff --git a/plugins/playwright-kit/dev.kiro/install.sh b/plugins/playwright-kit/dev.kiro/install.sh deleted file mode 100755 index 06d2dbd82..000000000 --- a/plugins/playwright-kit/dev.kiro/install.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env bash -# playwright-kit Plugin Installer for Kiro CLI -# Usage: bash plugins/playwright-kit/dev.kiro/install.sh [--project PATH] [--scope workspace|global] [--dry-run] -# -# Kiro CLI にはプラグイン機構がないため、プラグインの skills/ を導入先の skills/ へ symlink する。 -# NDF の installer と違い、エージェント定義・常時指示・プロンプト・フックは扱わない -# (この plugin は Skill だけを配る)。 -# -# このスクリプトは Agent Plugins 仕様 §8.2 のクライアント拡張ディレクトリ(dev.kiro/)に -# 置く。プラグインルートは 1 つ上の階層になる。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_SKILLS_DIR="$PLUGIN_DIR/skills" -MANIFEST_FILE="$PLUGIN_DIR/plugin.json" - -PROJECT_ROOT="$(pwd)" -PROJECT_GIVEN=false -SCOPE="workspace" -DRY_RUN=false - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - PROJECT_GIVEN=true - shift - ;; - --scope) - [ "$#" -ge 2 ] || { echo "ERROR: --scope requires workspace or global" >&2; exit 2; } - SCOPE="$2" - shift - ;; - --dry-run) DRY_RUN=true ;; - --help|-h) - echo "Usage: bash plugins/playwright-kit/dev.kiro/install.sh [OPTIONS]" - echo "" - echo "Options:" - echo " --project PATH install into PATH instead of current directory (--scope workspace のみ)" - echo " --scope SCOPE workspace(既定, プロジェクトの .kiro/)または global(~/.kiro/)" - echo " --dry-run 書き込みを行わず実行内容を表示" - echo " -h, --help このヘルプを表示" - exit 0 - ;; - *) echo "ERROR: unknown option: $1" >&2; exit 2 ;; - esac - shift -done - -case "$SCOPE" in - workspace) - KIRO_DIR="$PROJECT_ROOT/.kiro" - ;; - global) - [ -n "${HOME:-}" ] || { echo "ERROR: --scope global には HOME が必要です" >&2; exit 2; } - KIRO_DIR="$HOME/.kiro" - if [ "$PROJECT_GIVEN" = true ]; then - echo "WARN: --scope global では --project は使用されません" >&2 - fi - ;; - *) - echo "ERROR: invalid --scope: $SCOPE (workspace|global)" >&2 - exit 2 - ;; -esac - -SKILLS_DIR="$KIRO_DIR/skills" - -echo "=== playwright-kit Plugin Installer for Kiro CLI ===" -echo " スコープ: $SCOPE ($KIRO_DIR)" - -if [ ! -d "$PLUGIN_SKILLS_DIR" ]; then - echo "ERROR: $PLUGIN_SKILLS_DIR が見つかりません" >&2 - exit 1 -fi -# 版数はルートマニフェストだけが持つ。Kiro 向けに複製した VERSION ファイルは置かない -# (同じ値を 2 箇所に置くと、片方だけが古くなる)。 -if [ ! -f "$MANIFEST_FILE" ]; then - echo "ERROR: $MANIFEST_FILE が見つかりません" >&2 - exit 1 -fi -PLUGIN_VERSION="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["version"])' "$MANIFEST_FILE")" -if [ -z "$PLUGIN_VERSION" ]; then - echo "ERROR: $MANIFEST_FILE から版数を読み取れません" >&2 - exit 1 -fi - -# 既存リンクの掃除は、このプラグイン配下を指すものだけに限る。 -# 利用者が置いた実体ディレクトリや他プラグインのリンクには触れない。 -if [ "$DRY_RUN" = false ]; then - mkdir -p "$SKILLS_DIR" - plugin_skills_abs="$(realpath -m "$PLUGIN_SKILLS_DIR")" - while IFS= read -r skill_link; do - target="$(readlink "$skill_link")" - case "$target" in - /*) target_abs="$target" ;; - *) target_abs="$(realpath -m "$(dirname "$skill_link")/$target")" ;; - esac - case "$target_abs" in - "$plugin_skills_abs"/*) rm -f "$skill_link" ;; - esac - done < <(find "$SKILLS_DIR" -mindepth 1 -maxdepth 1 -type l | sort) -fi - -SKILL_COUNT=0 -while IFS= read -r src_dir; do - skill_name="$(basename "$src_dir")" - if [ ! -f "$src_dir/SKILL.md" ]; then - echo " SKIP: $skill_name (SKILL.mdなし)" - continue - fi - if [ -e "$SKILLS_DIR/$skill_name" ] && [ ! -L "$SKILLS_DIR/$skill_name" ]; then - echo " WARN: $SKILLS_DIR/$skill_name はシンボリックリンクではありません。" >&2 - echo " 内容を確認のうえ手動で退避または削除してください。" >&2 - continue - fi - if [ "$DRY_RUN" = false ]; then - ln -sfn "$PLUGIN_SKILLS_DIR/$skill_name" "$SKILLS_DIR/$skill_name" - fi - echo " linked: $skill_name" - SKILL_COUNT=$((SKILL_COUNT + 1)) -done < <(find "$PLUGIN_SKILLS_DIR" -mindepth 1 -maxdepth 1 -type d | sort) - -if [ "$DRY_RUN" = true ]; then - echo "" - echo "DRY RUN: 書き込みは行いませんでした" - echo " playwright-kit バージョン: $PLUGIN_VERSION" - echo " Skills数: $SKILL_COUNT (配置先: $SKILLS_DIR)" - exit 0 -fi - -echo "" -echo "=== インストール完了 ===" -echo " playwright-kit バージョン: $PLUGIN_VERSION" -echo " Skills数: $SKILL_COUNT (シンボリックリンク: $SKILLS_DIR)" -echo "" -echo "playwright_kit の実行環境は playwright-kit-ops skill の手順で用意してください。" diff --git a/plugins/playwright-kit/manifests/kiro-skills.txt b/plugins/playwright-kit/manifests/kiro-skills.txt deleted file mode 100644 index c6e9e5661..000000000 --- a/plugins/playwright-kit/manifests/kiro-skills.txt +++ /dev/null @@ -1,4 +0,0 @@ -playwright-planning -playwright-authoring -playwright-evidence -playwright-kit-ops diff --git a/scripts/build-runtime-plugins.sh b/scripts/build-runtime-plugins.sh index 3344d3f76..ed4498d69 100755 --- a/scripts/build-runtime-plugins.sh +++ b/scripts/build-runtime-plugins.sh @@ -9,8 +9,7 @@ usage() { Usage: bash scripts/build-runtime-plugins.sh [--check] Generate the tracked build outputs: Codex implicit-invocation policies for -single-directory plugins, the agy skill links and agent definitions under -dev.agy/, and the runtime copies of MCP plugins. +single-directory plugins. Options: --check Compare generated output with the working tree and fail on drift. @@ -38,287 +37,15 @@ sync_codex_skill_policies() { python3 "$ROOT_DIR/scripts/lib/codex_skill_policies.py" "$plugin_dir/skills" "$manifest" "$CHECK" } -# agy は配る Skill を絞る手段を利用者側の設定にしか持たない(`plugins.json` の絞り込みは -# プラグインのディレクトリ名に掛かり、同梱の Skill には届かない)。そこで絞り込みは -# dev.agy/skills/ へ何を並べるかで表す。並べる内容は manifests/agy-skills.txt が決める。 -# hook の実体は 4 ランタイムで共有するため、同じ位置へ symlink を張る。エージェント定義は -# agy の受け付ける `model` へ変えた写しを置く(sync_agy_agents)。 -# Agent Plugins 仕様 §8.2 のクライアント拡張ディレクトリに当たる。 -sync_agy_skill_links() { - local family="$1" - local plugin_dir="$ROOT_DIR/plugins/$family" - - [ -f "$plugin_dir/manifests/agy-skills.txt" ] || return 0 - python3 - "$plugin_dir" "$CHECK" <<'PY' -import os -import sys -from pathlib import Path - -plugin_dir, check = Path(sys.argv[1]), sys.argv[2] == "true" -dev_dir = plugin_dir / "dev.agy" -links_dir = dev_dir / "skills" - -published = [ - line.split("#", 1)[0].strip() - for line in (plugin_dir / "manifests/agy-skills.txt").read_text(encoding="utf-8").splitlines() - if line.split("#", 1)[0].strip() -] - -# 生成する symlink と、その向き先。skills/ は基準が決め、scripts は固定である。 -expected = {links_dir / name: f"../../skills/{name}" for name in published} -expected.update({dev_dir / "scripts": "../scripts"}) - -actual = set() -if links_dir.is_dir(): - actual |= set(links_dir.iterdir()) -if (dev_dir / "scripts").is_symlink(): - actual.add(dev_dir / "scripts") -stale = sorted(actual - set(expected)) - -failed = False - - -def report(message: str) -> None: - global failed - print(message, file=sys.stderr) - failed = True - - -for path, target in sorted(expected.items()): - if check: - if not path.is_symlink(): - report(f"Generated link missing: {path}") - elif os.readlink(path) != target: - report(f"Generated link points elsewhere: {path} -> {os.readlink(path)}") - continue - path.parent.mkdir(parents=True, exist_ok=True) - if path.is_symlink() or path.exists(): - path.unlink() - path.symlink_to(target) - -for path in stale: - if check: - report(f"Generated link is stale: {path}") - else: - path.unlink() - -raise SystemExit(1 if failed else 0) -PY -} - -# Kiro CLI にはプラグイン機構が無いため、MCP プラグインごとに installer を置く。内容は -# プラグイン名に依存しないので、雛形を 1 つ持って各プラグインの dev.kiro/ へ書き出す。 -# Agent Plugins 仕様 §8.2 のクライアント拡張ディレクトリに当たる。 -write_kiro_mcp_installer() { - local plugin_dir="$1" - local installer="$plugin_dir/dev.kiro/install.sh" - local tmp - - tmp="$(mktemp)" - cat > "$tmp" <<'EOF' -#!/usr/bin/env bash -# MCP Plugin Installer for Kiro CLI -# Usage: bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh [--project PATH] [--dry-run] -# -# scripts/build-runtime-plugins.sh が生成します。直接編集しないでください。 -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PLUGIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -PLUGIN_NAME="$(basename "$PLUGIN_DIR")" -PROJECT_ROOT="$(pwd)" -KIRO_DIR="$PROJECT_ROOT/.kiro" -KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" -KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" -KIRO_SKILLS_DIR="$KIRO_DIR/skills" -TARGET_MCP="$PROJECT_ROOT/.mcp.json" -SOURCE_MCP="$PLUGIN_DIR/.mcp.json" -SOURCE_HOOKS="$PLUGIN_DIR/hooks/hooks.json" -SOURCE_SKILLS="$PLUGIN_DIR/skills" -DRY_RUN=false - -usage() { - cat <<'USAGE' -Usage: bash install.sh [--project PATH] [--dry-run] - -Install this MCP plugin into the current project's Kiro settings. -USAGE -} - -while [ "$#" -gt 0 ]; do - case "$1" in - --project) - [ "$#" -ge 2 ] || { echo "ERROR: --project requires a path" >&2; exit 2; } - PROJECT_ROOT="$(cd "$2" && pwd)" - KIRO_DIR="$PROJECT_ROOT/.kiro" - KIRO_AGENT_FILE="$KIRO_DIR/agents/default.json" - KIRO_PLUGIN_LINK="$KIRO_DIR/mcp_runtime/$PLUGIN_NAME" - KIRO_SKILLS_DIR="$KIRO_DIR/skills" - TARGET_MCP="$PROJECT_ROOT/.mcp.json" - shift - ;; - --dry-run) DRY_RUN=true ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; - esac - shift -done - -if [ ! -f "$SOURCE_MCP" ]; then - echo "ERROR: source .mcp.json not found: $SOURCE_MCP" >&2 - exit 1 -fi - -if [ "$DRY_RUN" = true ]; then - echo "Would merge $SOURCE_MCP into $TARGET_MCP" - echo "Would link plugin runtime into $KIRO_PLUGIN_LINK" - if [ -d "$SOURCE_SKILLS" ]; then - echo "Would link skills from $SOURCE_SKILLS into $KIRO_SKILLS_DIR" - fi - if [ -f "$SOURCE_HOOKS" ]; then - echo "Would merge hooks from $SOURCE_HOOKS into $KIRO_AGENT_FILE" - fi - exit 0 -fi - -mkdir -p "$KIRO_DIR/mcp_runtime" "$KIRO_SKILLS_DIR" "$(dirname "$KIRO_AGENT_FILE")" -if [ -e "$KIRO_PLUGIN_LINK" ] && [ ! -L "$KIRO_PLUGIN_LINK" ]; then - echo "ERROR: $KIRO_PLUGIN_LINK already exists and is not a symlink" >&2 - exit 1 -fi -ln -sfn "$PLUGIN_DIR" "$KIRO_PLUGIN_LINK" - -if [ -d "$SOURCE_SKILLS" ]; then - find "$SOURCE_SKILLS" -mindepth 1 -maxdepth 1 -type d | sort | while IFS= read -r skill_dir; do - skill_name="$(basename "$skill_dir")" - skill_link="$KIRO_SKILLS_DIR/$skill_name" - [ -f "$skill_dir/SKILL.md" ] || continue - if [ -e "$skill_link" ] && [ ! -L "$skill_link" ]; then - echo "ERROR: $skill_link already exists and is not a symlink" >&2 - exit 1 - fi - ln -sfn "$skill_dir" "$skill_link" - echo "Linked Kiro skill: $skill_name" - done -fi - -python3 - "$SOURCE_MCP" "$TARGET_MCP" "$SOURCE_HOOKS" "$KIRO_AGENT_FILE" "$KIRO_PLUGIN_LINK" <<'PY' -import json -import sys -from pathlib import Path - -source_path = Path(sys.argv[1]) -target_path = Path(sys.argv[2]) -hooks_path = Path(sys.argv[3]) -agent_path = Path(sys.argv[4]) -plugin_link = sys.argv[5] -source = json.loads(source_path.read_text()) -target = json.loads(target_path.read_text()) if target_path.exists() else {} -source_servers = source.get("mcpServers", {}) -target.setdefault("mcpServers", {}) -target["mcpServers"].update(source_servers) -target_path.write_text(json.dumps(target, indent=2, ensure_ascii=False) + "\n") - -if agent_path.exists(): - agent = json.loads(agent_path.read_text()) -else: - agent = { - "name": "default", - "resources": ["skill://.kiro/skills/**/SKILL.md"], - } - -# Kiro は tools に挙がったツールだけをモデルへ見せる。欄が無いと MCP のツールを -# 呼べない(#1873)。利用者が書いた tools は上書きしない。 -agent.setdefault("tools", ["*"]) -agent.setdefault("mcpServers", {}) -agent["mcpServers"].update(source_servers) - -if hooks_path.exists(): - source_hooks = json.loads(hooks_path.read_text()).get("hooks", {}) - target_hooks = agent.setdefault("hooks", {}) - event_map = { - "SessionStart": "agentSpawn", - "Stop": "stop", - } - # Kiro にはプラグインルートを示す環境変数が無い。hook の command が使う - # プレースホルダを、installer が張った symlink の絶対パスへ置き換える。 - placeholder = "${PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}" - for source_event, groups in source_hooks.items(): - target_event = event_map.get(source_event, source_event) - target_list = target_hooks.setdefault(target_event, []) - existing = { - entry.get("command") - for entry in target_list - if isinstance(entry, dict) - } - for group in groups: - for hook in group.get("hooks", []): - command = hook.get("command") - if not command: - continue - command = command.replace(placeholder, plugin_link) - if command in existing: - continue - entry = {"command": command} - if "timeout_ms" in hook: - entry["timeout_ms"] = hook["timeout_ms"] - target_list.append(entry) - existing.add(command) - -agent_path.write_text(json.dumps(agent, indent=2, ensure_ascii=False) + "\n") - -print(f"Installed MCP servers: {', '.join(sorted(source_servers))}") -print(f"Updated: {target_path}") -print(f"Updated: {agent_path}") -PY -EOF - - if [ "$CHECK" = true ]; then - if [ ! -f "$installer" ] || ! diff -q "$tmp" "$installer" >/dev/null; then - echo "Generated file is out of date: ${installer#$ROOT_DIR/}" >&2 - rm -f "$tmp" - return 1 - fi - rm -f "$tmp" - return 0 - fi - mkdir -p "$(dirname "$installer")" - mv "$tmp" "$installer" - chmod +x "$installer" -} - -# agy 1.4 系は `model` に tier 名しか受け付けないため、エージェント定義は symlink で共有せず、 -# `model` だけを tier 名へ変えた写しを dev.agy/agents/ へ置く(#2087)。 -sync_agy_agents() { - local family="$1" - local plugin_dir="$ROOT_DIR/plugins/$family" - - [ -d "$plugin_dir/dev.agy" ] || return 0 - python3 "$ROOT_DIR/scripts/lib/agy_agents.py" "$plugin_dir" "$CHECK" -} - -sync_mcp_plugins() { - local plugin_dir - - for plugin_dir in "$ROOT_DIR"/plugins/mcp/*; do - [ -f "$plugin_dir/.mcp.json" ] || continue - write_kiro_mcp_installer "$plugin_dir" - done -} - for dir in "$ROOT_DIR"/plugins/*; do [ -d "$dir/manifests" ] || continue family="$(basename "$dir")" case "$family" in - *-shared|*-claude|*-codex|*-kiro) continue ;; + *-shared|*-claude|*-codex) continue ;; esac sync_codex_skill_policies "$family" - sync_agy_skill_links "$family" - sync_agy_agents "$family" done -sync_mcp_plugins - if [ "$CHECK" = true ]; then echo "runtime plugin generated files are up to date" else diff --git a/scripts/check-cross-skill-refs.py b/scripts/check-cross-skill-refs.py index 5ec706235..9f03bce8d 100755 --- a/scripts/check-cross-skill-refs.py +++ b/scripts/check-cross-skill-refs.py @@ -2,7 +2,7 @@ """Skill の境界をまたぐ**配布に依存する参照**を数え、例外の一覧に無いものを失敗として返す。 Skill は配布の基準(`plugins/ndf/manifests/*-skills.txt`)で 1 つずつ配るかを決める。 -**配る Skill を絞る配布先(agy)では、基準に無い Skill が配布した先から消える。** +**配る Skill を絞る配布先(今は Codex。Claude Code より 4 本少ない)では、基準に無い Skill が配布した先から消える。** 別の Skill の `scripts/` や `references/` を読み込む・起動する・`sys.path` へ入れる参照は、 その相手を配らない配布先で解決できない。**実行するか読むかは、相手が欠けたときの結果を 変えない。** diff --git a/scripts/check-doc-staleness.py b/scripts/check-doc-staleness.py index 3ee0729f3..e9036a8fd 100644 --- a/scripts/check-doc-staleness.py +++ b/scripts/check-doc-staleness.py @@ -59,17 +59,10 @@ def plugin_json_path(name: str) -> str: return f"plugins/{name}/.claude-plugin/plugin.json" -# `README.md` はランタイムを Claude Code / Kiro / Codex / agy の順、`plugins/ndf/README.md` は -# Claude Code / Codex / Kiro CLI / agy の順で書いている。位置ではなく名前で対応づける。表記も -# 2 本で違う(`Kiro` と `Kiro CLI`)ため、対応表を文書ごとに持つ。**agy はどちらも同じ表記で -# ある。** コマンド名がそのまま配布先の名前であり、製品名で言い換えると読み手が結び付けられない。 -ROOT_README_RUNTIMES = {"Claude Code": "claude", "Kiro": "kiro", "Codex": "codex", "agy": "agy"} -PLUGIN_README_RUNTIMES = { - "Claude Code": "claude", - "Codex": "codex", - "Kiro CLI": "kiro", - "agy": "agy", -} +# 配布先は Claude Code と Codex の 2 つで、`README.md`・`plugins/ndf/README.md` のどちらも +# この順で書いている。位置ではなく名前で対応づけ、表記が分かれたときに備えて対応表を文書ごとに持つ。 +ROOT_README_RUNTIMES = {"Claude Code": "claude", "Codex": "codex"} +PLUGIN_README_RUNTIMES = {"Claude Code": "claude", "Codex": "codex"} def runtime_alternation(labels: dict[str, str]) -> str: @@ -232,7 +225,7 @@ class RepositoryMetrics: source: str -# 点で照合する版数記載(G・K・L・M)の一覧。点の照合を足すときはここへ 1 行足す。 +# 点で照合する版数記載(G・L・M)の一覧。点の照合を足すときはここへ 1 行足す。 # 同じ文書の中では並びの順に報告する。 POINT_VERSION_SPECS: list[PointVersionSpec] = [ PointVersionSpec( @@ -241,12 +234,6 @@ class RepositoryMetrics: "**NDFプラグイン v<版>**", re.compile(r"\*\*NDFプラグイン v" + VERSION + r"\*\*"), ), # G - PointVersionSpec( - PLUGIN_README, - "Kiro の確認例の版数", - "(Kiro CLI用 / v<版>)", - re.compile(r"Kiro CLI用 / v" + VERSION + r")"), - ), # K PointVersionSpec( PLUGIN_README, "Codex のキャッシュパスの例の版数", @@ -782,7 +769,7 @@ def main() -> int: root = Path(args.root).resolve() report = Report() - counts = {runtime: manifest_skill_count(root, runtime, report) for runtime in ("claude", "codex", "kiro", "agy")} + counts = {runtime: manifest_skill_count(root, runtime, report) for runtime in ("claude", "codex")} skills = skill_dir_count(root, SKILLS_DIR, report) version = plugin_version(root, report) diff --git a/scripts/check-skill-frontmatter.py b/scripts/check-skill-frontmatter.py index d8aba6cd5..9f1dee701 100644 --- a/scripts/check-skill-frontmatter.py +++ b/scripts/check-skill-frontmatter.py @@ -7,7 +7,7 @@ チェックは 3 種類に分かれる。 - **individual** — Skill 単位。仕様準拠・安全性・可搬性・運用 -- **aggregate** — 配布先ごとの初期一覧予算(Claude Code / Codex / Kiro CLI / agy)と +- **aggregate** — 配布先ごとの初期一覧予算(Claude Code / Codex)と frontmatter 総量。 予算はチェック対象の plugin family すべての合計に対して判定する - **cross** — Skill 間。トリガ語の重複と、既知の外部 Skill 名との衝突 @@ -95,36 +95,15 @@ # Codex(公式ドキュメント "Build skills"): # "at most 2% of the model's context window, or 8,000 characters when the # context window is unknown" -# 既定モデル gpt-5.6-sol のコンテキストは 272,000(kiro-cli --list-models で実測)。 +# 既定モデル gpt-5.6-sol のコンテキストは 272,000(kiro-cli --list-models で実測した過去の記録)。 # 2% = 5,440 トークン。コンテキスト長が判明しているため 8,000 のフォールバックは使わない。 # 比率は 2 倍でもコンテキストが 1/3.7 のため、**予算は Claude Code の約半分**にしかならず、 # 実質ここが全体の制約になる。 -# -# Kiro: 公式ドキュメント(kiro.dev/docs/skills)に一覧予算の規定が無い。 -# 既定モデル auto のコンテキストは 1,000,000。**この値は過去の実測の記録である。** -# 当時は `kiro-cli --list-models` で一覧を取れたが、2.20.1 にそのオプションは無く、 -# 同じ手段では再検証できない(`--list` は認証を要求する)。値を更新するときは、 -# その時点で一覧を取れる手段を確かめ、この行の出典も書き直すこと。Claude Code の -# Opus 5 と同じである。**NDF は Kiro でも 1M コンテキストのモデルだけを対象とする** -# (`plugins/ndf/dev.kiro/install.sh` が導入時にこの前提を出力する。モデルの一覧を -# 取る手段が kiro-cli の版によって変わるため、機械でのチェックは置いていない)。 -# 規定が無い以上どこかから基準を借りるほかなく、コンテキスト長が同じ Claude Code の -# 1% を当てる。憶測で独自の値を置くより、同じ土俵の実在する規定へ揃えるほうが根拠が残る。 -# -# agy(Antigravity CLI): 公式ドキュメント(antigravity.google/docs/skills)に一覧予算の -# 規定が無い。既定のモデルは Gemini 3.x Flash 系(`agy models` の先頭が -# `gemini-3.8-flash-high`。1.1.25 で実測)で、コンテキスト長を出す手段は見当たらない -# (`agy models` は名前と表示名だけを返し、`/context` にあたるコマンドも無い)。 -# **この 2 つの値は Claude Code から借りたものである。** Gemini 3.x Flash の -# コンテキスト長 1,000,000 は Claude Code の Opus 5 と同じであり、規定が無い以上 -# どこかから基準を借りるほかない。憶測で独自の値を置くより、同じコンテキスト長を持つ -# 実在する規定へ揃えるほうが根拠が残る(Kiro CLI と同じ扱い)。 # 一覧に何が載るかはランタイムごとに違う。**公式に記述があるのは Claude Code と Codex # だけ**である。 # Claude Code: "loads a listing of skill names and descriptions into context" # Codex: "In Codex, the initial list also includes each skill's file path." -# Kiro CLI と agy は一覧の構成も公式に記述が無いため、多い側(パスを含む)で見積もる。 class Listing(NamedTuple): @@ -136,12 +115,9 @@ class Listing(NamedTuple): includes_path: bool # 一覧に Skill のファイルパスが載るか -# Kiro と agy の割合は Claude Code から借りる(上の Kiro・agy のコメント) LISTINGS = { "claude": Listing(1_000_000, 0.01, "error", False), "codex": Listing(272_000, 0.02, "error", True), - "kiro": Listing(1_000_000, 0.01, "error", True), - "agy": Listing(1_000_000, 0.01, "error", True), } # 全 Skill の frontmatter 合計。**plugin family をまたいで合計する**(利用者の環境では @@ -550,7 +526,7 @@ def load_manifests(skills_dir: pathlib.Path) -> dict[str, set[str]]: """ man_dir = skills_dir.parent / "manifests" out: dict[str, set[str]] = {} - for runtime in ("claude", "codex", "kiro", "agy"): + for runtime in ("claude", "codex"): f = man_dir / f"{runtime}-skills.txt" if not f.exists(): continue @@ -788,7 +764,7 @@ def _discover_skills_dirs() -> list[pathlib.Path]: for d in sorted(pathlib.Path("plugins").glob("*")): if not (d / "manifests").is_dir() or not (d / "skills").is_dir(): continue - if d.name.endswith(("-claude", "-codex", "-kiro")): + if d.name.endswith(("-claude", "-codex")): continue found.append(d / "skills") # どの manifest にも載せない Skill も規約のチェックは受ける。配らないだけで、 diff --git a/scripts/check-skill-repo-assumptions.py b/scripts/check-skill-repo-assumptions.py index 2f3887337..649c553f6 100755 --- a/scripts/check-skill-repo-assumptions.py +++ b/scripts/check-skill-repo-assumptions.py @@ -111,7 +111,7 @@ "plugins/ndf/scripts/project_lib/model.py": "NDF 自身の宣言の形(project.schema.json)の URL。対象リポジトリを指していない", } -RUNTIMES = ("claude", "codex", "kiro", "agy") +RUNTIMES = ("claude", "codex") def canon(path: pathlib.Path | str) -> str: diff --git a/scripts/ci-heavy-skip.py b/scripts/ci-heavy-skip.py index f98e5af81..7c3a61e3b 100644 --- a/scripts/ci-heavy-skip.py +++ b/scripts/ci-heavy-skip.py @@ -52,7 +52,7 @@ ) MANIFEST_RE = re.compile( r"^(\.claude-plugin/marketplace\.json" - r"|plugins/.+/(\.claude-plugin/|\.codex-plugin/|dev\.agy/)?plugin\.json)$" + r"|plugins/.+/(\.claude-plugin/|\.codex-plugin/)?plugin\.json)$" ) MANIFEST_LINE_RE = re.compile(r'^\s*"(version|description)"\s*:') ANCESTOR_LIMIT = 20 diff --git a/scripts/lib/agy_agents.py b/scripts/lib/agy_agents.py deleted file mode 100644 index ce1e36be0..000000000 --- a/scripts/lib/agy_agents.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env python3 -"""agy に配るエージェント定義(`dev.agy/agents/*.md`)を生成・確認する(`scripts/build-runtime-plugins.sh` の一部)。 - -agy 1.4 系はエージェント定義の `model` に tier 名(`flash` / `pro` / `inherit`)しか受け付けず、 -Claude Code の型の名前(`sonnet` / `opus`)を持つ定義を読み飛ばす(#2087)。そこで `agents/*.md` を写し、 -frontmatter の `model:` の 1 行だけを対応表で tier 名へ置き換える。YAML として読み直さないのは、 -写しと元の差を `model:` の 1 行に限ってバイト列で確かめるためである(設計の決定 3)。 - - python3 scripts/lib/agy_agents.py - -2 つ目が `true` なら書かずに確かめ、食い違いを標準エラーに出して終了コード 1 で終わる。 -""" - -from __future__ import annotations - -import sys -from pathlib import Path - -# agy 1.4 系が `model` に受け付ける tier 名。欄が無いときは agy の既定(`inherit`)になる。 -AGY_TIERS = frozenset({"flash", "pro", "inherit"}) - -# Claude Code の型の名前 → agy の tier 名。安い型は安い tier へ、高い型は高い tier へ写す(設計の決定 2)。 -# ここに無い値は生成を止める(設計の決定 4)。足すときは 1 行足す。 -MODEL_MAP = { - "sonnet": "flash", - "opus": "pro", - "haiku": "flash", - "inherit": "inherit", -} - - -class UnknownModel(Exception): - """対応表に無い `model` の値。""" - - -def model_value(line: str) -> str: - """`model: <値>` の行から値を取り出す(引用符は外す)。""" - value = line.split(":", 1)[1].strip() - if len(value) >= 2 and value[0] == value[-1] and value[0] in "'\"": - value = value[1:-1] - return value - - -def _model_line_index(lines: list[str]) -> int | None: - """frontmatter の `model:` の行の添字。frontmatter か欄が無ければ None(行末の改行の有無は問わない)。""" - if not lines or lines[0].strip() != "---": - return None - for index, line in enumerate(lines[1:], start=1): - if line.strip() == "---": - return None - if line.startswith("model:"): - return index - return None - - -def frontmatter_model(text: str) -> str | None: - """frontmatter の `model` の値。欄が無ければ None。""" - lines = text.splitlines() - index = _model_line_index(lines) - return None if index is None else model_value(lines[index]) - - -def agy_copy(text: str, model_map: dict[str, str]) -> str: - """frontmatter の `model:` の 1 行だけを tier 名へ置き換えた写し。欄が無ければそのまま返す。""" - lines = text.splitlines(keepends=True) - index = _model_line_index(lines) - if index is None: - return text - line = lines[index] - value = model_value(line) - if value not in model_map: - raise UnknownModel(value) - ending = line[len(line.rstrip("\r\n")) :] - lines[index] = f"model: {model_map[value]}{ending}" - return "".join(lines) - - -def map_errors(model_map: dict[str, str]) -> list[str]: - """対応表の値のうち tier 名でないもの。""" - return [f"Model map value is not an agy tier: {key}: {value}" for key, value in sorted(model_map.items()) if value not in AGY_TIERS] - - -def sync(plugin_dir: Path, check: bool, model_map: dict[str, str] | None = None) -> bool: - """生成物を書く(`check` なら確かめる)。食い違いがあれば偽。""" - model_map = MODEL_MAP if model_map is None else model_map - dev_dir = plugin_dir / "dev.agy" - source_dir = plugin_dir / "agents" - if not dev_dir.is_dir(): - return True - root = plugin_dir.resolve().parents[1] - - def rel(path: Path) -> str: - try: - return str(path.relative_to(root)) - except ValueError: - return str(path) - - errors = map_errors(model_map) - for message in errors: - print(message, file=sys.stderr) - if errors: - return False - - out_dir = dev_dir / "agents" - expected = _expected_copies(source_dir, out_dir, model_map, rel) - if expected is None: - return False - if check: - return _check_copies(out_dir, expected, rel) - _write_copies(out_dir, expected) - return True - - -def _expected_copies(source_dir: Path, out_dir: Path, model_map: dict[str, str], rel) -> dict[Path, str] | None: - """写しの置き場 → 中身。対応表に無い `model` があれば標準エラーに出して None。""" - sources = sorted(source_dir.glob("*.md")) if source_dir.is_dir() else [] - expected: dict[Path, str] = {} - ok = True - for source in sources: - text = source.read_text(encoding="utf-8") - try: - expected[out_dir / source.name] = agy_copy(text, model_map) - except UnknownModel as exc: - print(f"Unknown model for agy: {rel(source)}: {exc}", file=sys.stderr) - ok = False - return expected if ok else None - - -def _check_copies(out_dir: Path, expected: dict[Path, str], rel) -> bool: - """書かずに確かめる。食い違いを標準エラーに出し、あれば偽。""" - if out_dir.is_symlink(): - print(f"Generated agents must be files, not a symlink: {rel(out_dir)}", file=sys.stderr) - return False - ok = True - actual = {p for p in out_dir.iterdir()} if out_dir.is_dir() else set() - for path, content in sorted(expected.items()): - if not path.is_file(): - print(f"Generated agent missing: {rel(path)}", file=sys.stderr) - ok = False - elif path.read_text(encoding="utf-8") != content: - print(f"Generated agent is out of date: {rel(path)}", file=sys.stderr) - ok = False - for path in sorted(actual - set(expected)): - print(f"Generated agent is stale: {rel(path)}", file=sys.stderr) - ok = False - return ok - - -def _write_copies(out_dir: Path, expected: dict[Path, str]) -> None: - """写しを書き、元に無い写しを消す。""" - # 古い symlink(`../agents`)を外してから書く。外さずに書くと元の定義を書き換える。 - if out_dir.is_symlink(): - out_dir.unlink() - out_dir.mkdir(parents=True, exist_ok=True) - for path, content in expected.items(): - path.write_text(content, encoding="utf-8") - for path in sorted(set(out_dir.iterdir()) - set(expected)): - path.unlink() - - -def main(argv: list[str]) -> int: - plugin_dir, check = Path(argv[0]), argv[1] == "true" - return 0 if sync(plugin_dir, check) else 1 - - -if __name__ == "__main__": - raise SystemExit(main(sys.argv[1:])) diff --git a/scripts/lib/validate_manifests.py b/scripts/lib/validate_manifests.py index 9df978c46..59369d5a4 100644 --- a/scripts/lib/validate_manifests.py +++ b/scripts/lib/validate_manifests.py @@ -208,25 +208,6 @@ def check_description(label: str, manifest: dict, version: str, expected, source ) # ルートマニフェスト(Agent Plugins 形式)は絞り込みを持たず `skills/` を全件公開する。 # description の Skill 数は manifest ではなく実体の数と突き合わせる。 - # agy 向けの定義はクライアント拡張ディレクトリ(Agent Plugins 1.0.0 §8.2)へ置く。 - # ルート直下へ置くと Codex がそちらを優先して読み、配布 Skill が codex-skills.txt では - # なく skills/ の実体になる。agy には取得元の登録が無く `agy plugin list` も版数を出さない - # ため、利用者が版を判断できる手がかりは clone した中身の版数だけである。 - agy_manifest_path = plugin_dir_of(family) / "dev.agy/plugin.json" - if agy_manifest_path.is_file(): - agy_manifest = read_json(agy_manifest_path) - if agy_manifest.get("version") != version: - errors.append( - f"{agy_manifest_path.relative_to(root)} の version が claude 版と" - f"食い違う(agy: {agy_manifest.get('version')} / claude: {version})" - ) - check_description( - str(agy_manifest_path.relative_to(root)), - agy_manifest, - version, - manifest_skill_count(family, "agy"), - "agy-skills.txt", - ) root_manifest_path = plugin_dir_of(family) / "plugin.json" if root_manifest_path.is_file(): root_manifest = read_json(root_manifest_path) @@ -357,7 +338,7 @@ def check_description(label: str, manifest: dict, version: str, expected, source ) # MCP プラグインも 1 ディレクトリにまとめた。runtime ごとの配布物は無く、 -# 3 runtime が同じ .mcp.json を読む(Codex だけ .codex.mcp.json を読ませる例外がある)。 +# 2 runtime が同じ .mcp.json を読む(Codex だけ .codex.mcp.json を読ませる例外がある)。 for mcp in sorted((root / "plugins/mcp").iterdir()): if not mcp.is_dir(): continue @@ -368,8 +349,6 @@ def check_description(label: str, manifest: dict, version: str, expected, source errors.append(f"Claude plugin manifest missing: plugins/mcp/{mcp.name}/.claude-plugin/plugin.json") if not (mcp / ".codex-plugin/plugin.json").is_file(): errors.append(f"Codex plugin manifest missing: plugins/mcp/{mcp.name}/.codex-plugin/plugin.json") - if not (mcp / "dev.kiro/install.sh").is_file(): - errors.append(f"Kiro MCP installer missing: plugins/mcp/{mcp.name}/dev.kiro/install.sh") # Codex は .mcp.json を manifest の mcpServers から読む。指定が無いと # サーバが 1 つも登録されない。Codex だけ別の起動定義を読ませるときは # `./.codex.mcp.json` を指す(mcp-serena の `--context codex`。#818)。 diff --git a/scripts/runtime-smoke-latest.py b/scripts/runtime-smoke-latest.py index e08fe704b..2d0631316 100755 --- a/scripts/runtime-smoke-latest.py +++ b/scripts/runtime-smoke-latest.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """最新の版のスモークの手順のうち、判断の要らない 2 つを持つ(#1982)。 -- `resolve --runtime `: 取得元の最新の版を、CLI の版の一覧 +- `resolve --runtime `: 取得元の最新の版を、CLI の版の一覧 (`tests/runtime-smoke/cli-versions.env`)と同じ `KEY=VALUE` の形で標準出力へ出す。 取得元に届かない・形が読めないときは終了コード 1 で、標準エラーへ取得元の URL を書く - `report --runtime <名前> --run-url [--versions <パス>] [--repo ]`: 題が @@ -22,16 +22,10 @@ import urllib.request from pathlib import Path -RUNTIMES = ("claude", "codex", "kiro", "agy") +RUNTIMES = ("claude", "codex") VERSION_RE = re.compile(r"^[0-9]+(\.[0-9]+)+$") -HEX_RE = re.compile(r"^[0-9a-f]+$") NPM_DIST_TAGS = "https://registry.npmjs.org/-/package/{package}/dist-tags" -KIRO_MANIFEST = "https://prod.download.cli.kiro.dev/stable/latest/manifest.json" -AGY_MANIFEST = "https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests/{platform}.json" -# arch の名前は `uname -m` の値に揃える(一覧のキーの末尾)。agy の manifest は別の名前で引く -AGY_PLATFORMS = {"X86_64": "linux_amd64", "AARCH64": "linux_arm64"} -KIRO_ARCHES = {"X86_64": "x86_64", "AARCH64": "aarch64"} TITLE = "最新の版のスモークが落ちた: {runtime}" @@ -59,12 +53,6 @@ def _version(url: str, value: object) -> str: return value -def _hex(url: str, value: object, length: int) -> str: - if not isinstance(value, str) or len(value) != length or not HEX_RE.match(value): - raise ResolveError(url, f"checksum が読めない: {value!r}") - return value - - def _npm_latest(package: str) -> str: url = NPM_DIST_TAGS.format(package=package) data = fetch_json(url) @@ -80,45 +68,8 @@ def resolve_codex() -> list[tuple[str, str]]: return [("CODEX_VERSION", _npm_latest("@openai/codex"))] -def resolve_kiro() -> list[tuple[str, str]]: - url = KIRO_MANIFEST - data = fetch_json(url) - if not isinstance(data, dict) or not isinstance(data.get("packages"), list): - raise ResolveError(url, "packages が無い") - version = _version(url, data.get("version")) - pairs = [("KIRO_CLI_VERSION", version)] - for key, arch in KIRO_ARCHES.items(): - download = f"{version}/kirocli-{arch}-linux-musl.tar.gz" - found = [p for p in data["packages"] if isinstance(p, dict) and p.get("download") == download] - if len(found) != 1: - raise ResolveError(url, f"{download} が manifest に 1 つだけ載っていない") - pairs.append((f"KIRO_CLI_SHA256_{key}", _hex(url, found[0].get("sha256"), 64))) - return pairs - -def resolve_agy() -> list[tuple[str, str]]: - versions: dict[str, str] = {} - urls: list[tuple[str, str]] = [] - sums: list[tuple[str, str]] = [] - for key, platform in AGY_PLATFORMS.items(): - url = AGY_MANIFEST.format(platform=platform) - data = fetch_json(url) - if not isinstance(data, dict): - raise ResolveError(url, "形が読めない") - version = _version(url, data.get("version")) - dl = data.get("url") - # 版つきの取得先だけを受ける(版を含まない url は固定にならない) - if not isinstance(dl, str) or not dl.startswith("https://") or f"/{version}-" not in dl: - raise ResolveError(url, f"版つきの url が読めない: {dl!r}") - versions[platform] = version - urls.append((f"AGY_URL_{key}", dl)) - sums.append((f"AGY_SHA512_{key}", _hex(url, data.get("sha512"), 128))) - if len(set(versions.values())) != 1: - raise ResolveError(AGY_MANIFEST.format(platform="*"), f"arch ごとの版が違う: {versions}") - return [("AGY_VERSION", next(iter(versions.values()))), *urls, *sums] - - -RESOLVERS = {"claude": resolve_claude, "codex": resolve_codex, "kiro": resolve_kiro, "agy": resolve_agy} +RESOLVERS = {"claude": resolve_claude, "codex": resolve_codex} def resolve(runtime: str) -> list[tuple[str, str]]: diff --git a/scripts/runtime-smoke-test.sh b/scripts/runtime-smoke-test.sh index db5ac38de..b018ebb7d 100755 --- a/scripts/runtime-smoke-test.sh +++ b/scripts/runtime-smoke-test.sh @@ -13,7 +13,7 @@ usage() { Usage: bash scripts/runtime-smoke-test.sh [OPTIONS] Options: - --runtime claude|codex|kiro|agy|all runtime to test (default: all) + --runtime claude|codex|all runtime to test (default: all) --with-secrets off|auto|required secret handling mode (default: off) --secret-file KEY=PATH inject an allowlisted file secret --keep-container keep the container for local debugging @@ -44,7 +44,7 @@ while [ "$#" -gt 0 ]; do shift done -case "$RUNTIME" in claude|codex|kiro|agy|all) ;; *) echo "ERROR: invalid --runtime: $RUNTIME" >&2; exit 2 ;; esac +case "$RUNTIME" in claude|codex|all) ;; *) echo "ERROR: invalid --runtime: $RUNTIME" >&2; exit 2 ;; esac case "$WITH_SECRETS" in off|auto|required) ;; *) echo "ERROR: invalid --with-secrets: $WITH_SECRETS" >&2; exit 2 ;; esac if [ "$KEEP_CONTAINER" = true ] && [ "$WITH_SECRETS" != off ]; then echo "ERROR: --keep-container cannot be used with --with-secrets=$WITH_SECRETS" >&2 @@ -57,7 +57,7 @@ mkdir -p "$ARTIFACT_DIR" ARTIFACT_DIR="$(cd "$ARTIFACT_DIR" && pwd)" RUNTIMES=("$RUNTIME") -[ "$RUNTIME" = all ] && RUNTIMES=(claude codex kiro agy) +[ "$RUNTIME" = all ] && RUNTIMES=(claude codex) RAW_SECRET_NAMES=( ANTHROPIC_API_KEY diff --git a/scripts/script-structure-allow/plugins__ndf__dev.kiro__install.sh--lines.json b/scripts/script-structure-allow/plugins__ndf__dev.kiro__install.sh--lines.json deleted file mode 100644 index 3148cc866..000000000 --- a/scripts/script-structure-allow/plugins__ndf__dev.kiro__install.sh--lines.json +++ /dev/null @@ -1 +0,0 @@ -{"path": "plugins/ndf/dev.kiro/install.sh", "name": "", "kind": "lines", "reason": "L0 の時点で 561 行。移行の範囲の外。触って 500 行以下にしたら消す", "lines": 561} diff --git a/scripts/tests/agy_build_helpers.py b/scripts/tests/agy_build_helpers.py deleted file mode 100644 index fe6661e70..000000000 --- a/scripts/tests/agy_build_helpers.py +++ /dev/null @@ -1,82 +0,0 @@ -"""生成物の同期(`scripts/build-runtime-plugins.sh`)のテスト補助。 - -生成の本体はシェルスクリプトで、自分の位置からリポジトリの根を決める。実物のリポジトリで -動かすと `dev.agy/skills/` を書き換えてしまうため、一時ディレクトリへ同じ配置の木を作り、 -スクリプトを複製してそちらで動かす。**複製ではなく実物を複製する**(複製を置くと、生成の -規則が 2 つに分かれる)。 - -Skill 名は実物(`cherry-pick-pr` など)と重ならない値にしてある。テストが実物の manifest へ -依存していないことを、値そのもので示すためである。 -""" - -from __future__ import annotations - -import json -import subprocess -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[2] -BUILD = REPO_ROOT / "scripts/build-runtime-plugins.sh" -AGY_AGENTS = REPO_ROOT / "scripts/lib/agy_agents.py" - -FAMILY = "ndf" -VERSION = "9.3.0" -AGY_SKILLS = ["alpha", "bravo", "charlie"] - - -def build_tree(base: Path, skills: list[str] | None = None) -> Path: - """agy の生成に要るものだけを備えた木を作り、その根を返す。""" - skills = AGY_SKILLS if skills is None else skills - root = base / "repo" - plugin = root / f"plugins/{FAMILY}" - - (root / "scripts").mkdir(parents=True) - (root / "scripts/build-runtime-plugins.sh").write_text(BUILD.read_text(encoding="utf-8"), encoding="utf-8") - (root / "scripts/lib").mkdir() - (root / "scripts/lib/agy_agents.py").write_text(AGY_AGENTS.read_text(encoding="utf-8"), encoding="utf-8") - # MCP プラグインの同期はディレクトリを走査する。空でも存在していないと読めない。 - (root / "plugins/mcp").mkdir(parents=True) - - (plugin / "manifests").mkdir(parents=True) - (plugin / "manifests/agy-skills.txt").write_text( - "# コメント行と空行は数えない\n\n" + "".join(f"{name}\n" for name in skills), - encoding="utf-8", - ) - for name in skills: - (plugin / "skills" / name).mkdir(parents=True) - (plugin / "skills" / name / "SKILL.md").write_text(f"# {name}\n", encoding="utf-8") - (plugin / "agents").mkdir(parents=True) - (plugin / "scripts").mkdir(parents=True) - - (plugin / "dev.agy").mkdir(parents=True) - (plugin / "dev.agy/plugin.json").write_text( - json.dumps({"name": FAMILY, "version": VERSION}, ensure_ascii=False, indent=2) + "\n", - encoding="utf-8", - ) - return root - - -def run_build(root: Path, check: bool = False) -> subprocess.CompletedProcess[str]: - """複製したスクリプトを木の中で実行し、終了コードと出力を観測する。""" - args = ["bash", str(root / "scripts/build-runtime-plugins.sh")] - if check: - args.append("--check") - return subprocess.run(args, capture_output=True, text=True, check=False) - - -def output_of(result: subprocess.CompletedProcess[str]) -> str: - return result.stdout + result.stderr - - -def links_dir(root: Path) -> Path: - return root / f"plugins/{FAMILY}/dev.agy/skills" - - -def agents_dir(root: Path) -> Path: - """エージェント定義の正(Claude Code の定義)。""" - return root / f"plugins/{FAMILY}/agents" - - -def agy_agents_dir(root: Path) -> Path: - """agy に配る定義(生成した写し)。""" - return root / f"plugins/{FAMILY}/dev.agy/agents" diff --git a/scripts/tests/doc_staleness_helpers.py b/scripts/tests/doc_staleness_helpers.py index 297a41d3c..4712e9f35 100644 --- a/scripts/tests/doc_staleness_helpers.py +++ b/scripts/tests/doc_staleness_helpers.py @@ -28,12 +28,10 @@ # v10.5.0 で無くなった。#116)。 SKILL_DIRS = ["alpha", "bravo", "charlie", "delta", "echo"] -# ランタイムごとの配布 Skill 数。4 つとも別の値にして、取り違えを検出できるようにする。 +# ランタイムごとの配布 Skill 数。2 つを別の値にして、取り違えを検出できるようにする。 MANIFESTS = { "claude": ["alpha", "bravo", "charlie", "delta", "echo"], "codex": ["alpha", "bravo", "charlie"], - "kiro": ["alpha", "bravo", "charlie", "delta"], - "agy": ["alpha", "bravo"], } # 記号(G〜M)は `issues/parallel-batch-03/04-issue-209.md` の「チェックする記載」に対応する。 @@ -42,7 +40,7 @@ **NDFプラグイン v9.3.0** のチェック用の最小構成です。 -- **公開Skills**: Claude Code向け core 5個、Kiro向け core 4個、Codex向け core 3個、agy向け core 2個に分離。 +- **公開Skills**: Claude Code向け core 5個、Codex向け core 3個に分離。 - **元Skills(5個)**: - 第1群 (4): alpha, bravo, charlie, delta - 第2群 (1): echo @@ -111,7 +109,7 @@ 最初のタグは `ndf--v8.5.4` である。それより前の版(`8.4.0` 以前)はタグでは戻せない。 """ -# K: Kiro の確認例 / L: Codex のキャッシュパスの例(2 箇所) / M: `codex plugin list` の出力例 +# L: Codex のキャッシュパスの例(2 箇所) / M: `codex plugin list` の出力例 PLUGIN_README = """# NDF Plugin 配布物は 1 ディレクトリにまとまっています。 @@ -120,8 +118,6 @@ | --- | --- | --- | | Claude Code | 5 個 | `.claude-plugin/plugin.json` | | Codex | 3 個 | `.codex-plugin/plugin.json` | -| Kiro CLI | 4 個 | `dev.kiro/install.sh` | -| agy | 2 個 | `dev.agy/plugin.json` | ## レイアウト @@ -135,13 +131,6 @@ 版ごとの変更と移行の手順は CHANGELOG.md の `[ndf <版>]` の節にあります。 -## Kiro CLI で確かめる - -```bash -kiro agent list -# => NDF統合開発エージェント(Kiro CLI用 / v9.3.0) -``` - ## Codex で確かめる ```text @@ -259,7 +248,6 @@ def retarget_version(root: Path, version: str) -> None: edit(versioning, f"`{old_next}-rc.1`", f"`{new_next}-rc.1`") plugin_readme = root / "plugins/ndf/README.md" - edit(plugin_readme, f"Kiro CLI用 / v{VERSION})", f"Kiro CLI用 / v{version})") edit_all(plugin_readme, f"ndf/{VERSION}/skills/", f"ndf/{version}/skills/", 2) edit(plugin_readme, f"enabled {VERSION} ", f"enabled {version} ") diff --git a/scripts/tests/test_agy_agents.py b/scripts/tests/test_agy_agents.py deleted file mode 100644 index 1ae1b7507..000000000 --- a/scripts/tests/test_agy_agents.py +++ /dev/null @@ -1,192 +0,0 @@ -"""agy に配るエージェント定義の生成(scripts/lib/agy_agents.py。#2087)。 - -agy 1.4 系は `model` に tier 名(`flash` / `pro` / `inherit`)しか受け付けない。`dev.agy/agents/` には -`plugins/ndf/agents/` の写しを置き、`model:` の 1 行だけを tier 名へ変える。agy の無い環境で、 -写しの追従し忘れ・手直し・余り・足りなさと、受け付けられない `model` を見つける。 -""" - -from __future__ import annotations - -import importlib.util -import os -import shutil -from pathlib import Path - -from agy_build_helpers import agents_dir, agy_agents_dir, build_tree, output_of, run_build - -REPO_ROOT = Path(__file__).resolve().parents[2] -PLUGIN_DIR = REPO_ROOT / "plugins/ndf" - -_spec = importlib.util.spec_from_file_location("agy_agents", REPO_ROOT / "scripts/lib/agy_agents.py") -assert _spec and _spec.loader -agy_agents = importlib.util.module_from_spec(_spec) -_spec.loader.exec_module(agy_agents) - - -def _agent(name: str, model: str | None, body: str = "本文") -> str: - model_line = f"model: {model}\n" if model is not None else "" - return f"---\nname: {name}\n{model_line}description: |\n {name} の説明\n---\n\n{body}\n" - - -def _tree(tmp_path: Path, agents: dict[str, str | None]) -> Path: - root = build_tree(tmp_path) - for name, model in agents.items(): - (agents_dir(root) / f"{name}.md").write_text(_agent(name, model), encoding="utf-8") - return root - - -def _generated(root: Path) -> Path: - result = run_build(root) - assert result.returncode == 0, output_of(result) - return agy_agents_dir(root) - - -def test_repo_copies_use_agy_tiers() -> None: - """配る写しの `model` は tier 名か欄なし(受け入れ条件 7・I3)。""" - copies = sorted((PLUGIN_DIR / "dev.agy/agents").glob("*.md")) - assert copies - for path in copies: - model = agy_agents.frontmatter_model(path.read_text(encoding="utf-8")) - assert model is None or model in agy_agents.AGY_TIERS, f"{path}: {model}" - - -def test_repo_copies_follow_every_definition() -> None: - """写しのファイル名の集合は、エージェント定義の集合と等しい(I1)。""" - assert not (PLUGIN_DIR / "dev.agy/agents").is_symlink() - sources = sorted(p.name for p in (PLUGIN_DIR / "agents").glob("*.md")) - copies = sorted(p.name for p in (PLUGIN_DIR / "dev.agy/agents").iterdir()) - assert copies == sources - - -def test_model_map_values_are_tiers() -> None: - """対応表の値はすべて tier 名(I4)。""" - assert set(agy_agents.MODEL_MAP.values()) <= agy_agents.AGY_TIERS - - -def test_build_converts_model_and_keeps_the_rest(tmp_path: Path) -> None: - """`model:` の 1 行だけが変わり、ほかは元とバイト列で一致する(受け入れ条件 6・7、I2)。""" - root = _tree(tmp_path, {"cheap": "sonnet", "strong": "opus", "plain": None}) - out = _generated(root) - for name, tier in (("cheap", "flash"), ("strong", "pro"), ("plain", None)): - source = (agents_dir(root) / f"{name}.md").read_text(encoding="utf-8") - copy = (out / f"{name}.md").read_text(encoding="utf-8") - assert agy_agents.frontmatter_model(copy) == tier - assert [line for line in source.splitlines() if not line.startswith("model:")] == [ - line for line in copy.splitlines() if not line.startswith("model:") - ] - assert run_build(root, check=True).returncode == 0 - - -def test_build_does_not_touch_the_definitions(tmp_path: Path) -> None: - """古い symlink のままでも、生成は元の定義を書き換えない。`--check` は symlink を落とす(I6)。""" - root = _tree(tmp_path, {"cheap": "sonnet"}) - before = (agents_dir(root) / "cheap.md").read_bytes() - shutil.rmtree(_generated(root)) - os.symlink("../agents", agy_agents_dir(root)) - - result = run_build(root, check=True) - assert result.returncode == 1 - assert "Generated agents must be files, not a symlink" in output_of(result) - - out = _generated(root) - assert not out.is_symlink() and out.is_dir() - assert (agents_dir(root) / "cheap.md").read_bytes() == before - assert run_build(root, check=True).returncode == 0 - - -def test_check_detects_missing_copy(tmp_path: Path) -> None: - """定義を足して生成し直さないと `--check` が落ち、足りない写しを示す(I1)。""" - root = _tree(tmp_path, {"cheap": "sonnet"}) - _generated(root) - (agents_dir(root) / "added.md").write_text(_agent("added", "sonnet"), encoding="utf-8") - result = run_build(root, check=True) - assert result.returncode == 1 - assert "Generated agent missing: plugins/ndf/dev.agy/agents/added.md" in output_of(result) - assert (_generated(root) / "added.md").is_file() - - -def test_check_detects_stale_copy(tmp_path: Path) -> None: - """定義を消して生成し直さないと `--check` が落ち、余った写しを示す。生成し直すと消える(I1)。""" - root = _tree(tmp_path, {"cheap": "sonnet", "gone": "opus"}) - _generated(root) - (agents_dir(root) / "gone.md").unlink() - result = run_build(root, check=True) - assert result.returncode == 1 - assert "Generated agent is stale: plugins/ndf/dev.agy/agents/gone.md" in output_of(result) - assert not (_generated(root) / "gone.md").exists() - - -def test_check_detects_body_drift_and_hand_edits(tmp_path: Path) -> None: - """定義の本文を変えて生成し直さない・写しを手で直す、のどちらも `--check` が落ちる(I2)。""" - root = _tree(tmp_path, {"cheap": "sonnet"}) - out = _generated(root) - source = agents_dir(root) / "cheap.md" - source.write_text(source.read_text(encoding="utf-8").replace("本文", "本文!"), encoding="utf-8") - result = run_build(root, check=True) - assert result.returncode == 1 - assert "Generated agent is out of date: plugins/ndf/dev.agy/agents/cheap.md" in output_of(result) - - _generated(root) - copy = out / "cheap.md" - copy.write_text(copy.read_text(encoding="utf-8").replace("model: flash", "model: sonnet"), encoding="utf-8") - assert run_build(root, check=True).returncode == 1 - - -def test_unknown_model_stops_build_and_check(tmp_path: Path) -> None: - """対応表に無い値は、生成も `--check` も止め、パスと値を示す(I5)。""" - root = _tree(tmp_path, {"odd": "claude-opus-5-5"}) - for check in (False, True): - result = run_build(root, check=check) - assert result.returncode == 1 - assert "Unknown model for agy: plugins/ndf/agents/odd.md: claude-opus-5-5" in output_of(result) - assert not agy_agents_dir(root).exists() - - -def test_broken_model_map_stops_build(tmp_path: Path) -> None: - """対応表の値が tier 名でないと、生成を始めずに止まる(I4)。""" - root = _tree(tmp_path, {"cheap": "sonnet"}) - plugin_dir = root / "plugins/ndf" - assert agy_agents.sync(plugin_dir, check=False, model_map={"sonnet": "sonnet"}) is False - assert not agy_agents_dir(root).exists() - - -# 現状固定テスト: agy_copy と frontmatter_model は frontmatter の走査を別々に持つ。 -# 走査をまとめても、次の入力での出力が変わらないことを固定する。 -_FRONTMATTER_CASES = [ - # (入力, frontmatter_model の値, agy_copy の出力) - ("", None, ""), - ("model: sonnet\n", None, "model: sonnet\n"), - ("本文\n---\nmodel: sonnet\n---\n", None, "本文\n---\nmodel: sonnet\n---\n"), - ("---\nname: a\n---\n\nmodel: sonnet\n", None, "---\nname: a\n---\n\nmodel: sonnet\n"), - ("---\nname: a\n---\n", None, "---\nname: a\n---\n"), - ("---\nname: a\nmodel: sonnet\n---\n", "sonnet", "---\nname: a\nmodel: flash\n---\n"), - ("--- \nmodel: opus\n ---\n", "opus", "--- \nmodel: pro\n ---\n"), - ("---\nmodel: 'opus'\n---\n", "opus", "---\nmodel: pro\n---\n"), - ('---\nmodel: "haiku"\n---\n', "haiku", "---\nmodel: flash\n---\n"), - ("---\nmodel: inherit \n---\n", "inherit", "---\nmodel: inherit\n---\n"), - ("---\r\nmodel: sonnet\r\n---\r\n", "sonnet", "---\r\nmodel: flash\r\n---\r\n"), - ("---\nmodel: sonnet", "sonnet", "---\nmodel: flash"), - ("---\nname: a\nmodel: sonnet\n", "sonnet", "---\nname: a\nmodel: flash\n"), - ("---\n model: sonnet\n---\n", None, "---\n model: sonnet\n---\n"), - ("---\nmodel: sonnet\nmodel: opus\n---\n", "sonnet", "---\nmodel: flash\nmodel: opus\n---\n"), -] - - -def test_characterize_frontmatter_model_and_agy_copy() -> None: - """frontmatter の走査の境界(無い・閉じない・欄なし・引用符・CRLF・字下げ・2 つ目の欄)を固定する。""" - for text, model, copy in _FRONTMATTER_CASES: - assert agy_agents.frontmatter_model(text) == model, repr(text) - assert agy_agents.agy_copy(text, agy_agents.MODEL_MAP) == copy, repr(text) - - -def test_characterize_agy_copy_unknown_model() -> None: - """対応表に無い値は UnknownModel で止まり、値(引用符を外したもの)を持つ。frontmatter の外の値は見ない。""" - for text, value in (("---\nmodel: gpt\n---\n", "gpt"), ("---\nmodel: 'x y'\n---\n", "x y"), ("---\nmodel:\n---\n", "")): - try: - agy_agents.agy_copy(text, agy_agents.MODEL_MAP) - except agy_agents.UnknownModel as exc: - assert exc.args == (value,) - else: - raise AssertionError(repr(text)) - text = "---\nname: a\n---\nmodel: gpt\n" - assert agy_agents.agy_copy(text, agy_agents.MODEL_MAP) == text diff --git a/scripts/tests/test_agy_distribution.py b/scripts/tests/test_agy_distribution.py deleted file mode 100644 index d69cfcb04..000000000 --- a/scripts/tests/test_agy_distribution.py +++ /dev/null @@ -1,185 +0,0 @@ -"""配布先ランタイム agy の配置を固定する(#215 の受け入れ条件 A8 / A12)。 - -agy はプラグインのディレクトリ直下の `plugin.json` で対象を判別する。**その位置へ置くと -Codex の配布 Skill が `.codex-plugin/plugin.json` の `skills` 配列ではなく `skills/` の実体に -変わる**(設計の決定 1 の実測)。増える 2 個は Claude Code だけで動くものであるため、 -ルート直下へ置かないこと自体を固定する。 - -配る Skill の基準は `manifests/agy-skills.txt` だけが持ち、`dev.agy/skills/` の symlink は -`scripts/build-runtime-plugins.sh` が生成する。基準と生成物が食い違ったまま配られないよう、 -`--check` が不足・余分・向き先の違いを検出することを確かめる。 -""" - -from __future__ import annotations - -import os -import subprocess -import sys -from pathlib import Path - -import pytest - -from agy_build_helpers import ( - AGY_SKILLS, - FAMILY, - build_tree, - links_dir, - output_of, - run_build, -) - -REPO_ROOT = Path(__file__).resolve().parents[2] -PLUGIN_DIR = REPO_ROOT / f"plugins/{FAMILY}" -MANIFEST_DIR = PLUGIN_DIR / "manifests" - -# 配布数は課題ごとに変わりうるが、**agy を足したことで他の 3 つが変わらない**ことは -# #215 の受け入れ条件(A12)である。値を書いて固定する。v10.5.0 で 5 個 -# (`optional-skills/` の 4 個と `notion-writing`)を 4 ランタイムすべてへ足したため、 -# 4 つとも同じ 5 だけ増えている(#116 / #144)。Google の 2 つの Skill を `google-workspace` -# の 1 つへまとめたため、4 つとも同じ 1 だけ減っている(#744)。見積もりの `estimate` を -# 4 つとも足したため、同じ 1 だけ増えている(#1856)。 -EXPECTED_COUNTS = {"claude": 49, "codex": 45, "kiro": 46, "agy": 45} - - -def manifest_names(runtime: str) -> list[str]: - path = MANIFEST_DIR / f"{runtime}-skills.txt" - assert path.is_file(), f"{path} が無い" - return [line.split("#", 1)[0].strip() for line in path.read_text(encoding="utf-8").splitlines() if line.split("#", 1)[0].strip()] - - -def test_root_plugin_json_is_absent() -> None: - """ルート直下の `plugin.json` を置かない。 - - 置くと Codex がそちらを優先して読み、`skills/` の実体を全件配る。agy 向けの定義は - `dev.agy/plugin.json` にある(Agent Plugins 1.0.0 §8.2 のクライアント拡張ディレクトリ)。 - """ - assert not (PLUGIN_DIR / "plugin.json").exists(), ( - f"plugins/{FAMILY}/plugin.json があると Codex の配布 Skill が manifest ではなく" - " skills/ の実体になる(agy 向けの定義は dev.agy/plugin.json へ置く)" - ) - - -@pytest.mark.parametrize("runtime,expected", sorted(EXPECTED_COUNTS.items())) -def test_manifest_counts_do_not_change(runtime: str, expected: int) -> None: - """4 ランタイムの配布数。agy を足しても既存の 3 つは動かない。""" - assert len(manifest_names(runtime)) == expected - - -def test_agy_manifest_starts_from_the_codex_set() -> None: - """agy へ配る集合は Codex と同じ。 - - Claude Code だけで動く 2 個(`official-skills-autoloader` / `statusline`)を外した - 集合である(設計の決定 3)。 - """ - assert manifest_names("agy") == manifest_names("codex") - - -def test_dev_agy_links_follow_the_manifest() -> None: - """`dev.agy/skills/` は基準のとおりに並び、実体の Skill を指す。""" - links = PLUGIN_DIR / "dev.agy/skills" - assert sorted(p.name for p in links.iterdir()) == sorted(manifest_names("agy")) - for name in manifest_names("agy"): - link = links / name - assert link.is_symlink(), f"{link} が symlink でない" - assert os.readlink(link) == f"../../skills/{name}" - assert (link / "SKILL.md").is_file() - - -def test_dev_agy_shares_the_implementation() -> None: - """hook の実体は 4 ランタイムで共有する(設計の決定 6)。エージェント定義は写しを置く(#2087)。""" - link = PLUGIN_DIR / "dev.agy" / "scripts" - assert link.is_symlink(), f"{link} が symlink でない" - assert os.readlink(link) == "../scripts" - - -def test_build_creates_the_links(tmp_path: Path) -> None: - """基準から生成できる。生成した直後は `--check` が通る。""" - root = build_tree(tmp_path) - result = run_build(root) - assert result.returncode == 0, output_of(result) - assert sorted(p.name for p in links_dir(root).iterdir()) == sorted(AGY_SKILLS) - assert run_build(root, check=True).returncode == 0 - - -def test_check_detects_a_missing_link(tmp_path: Path) -> None: - """symlink を 1 本消すと `--check` が落ちる。""" - root = build_tree(tmp_path) - run_build(root) - (links_dir(root) / AGY_SKILLS[0]).unlink() - - result = run_build(root, check=True) - assert result.returncode != 0 - assert AGY_SKILLS[0] in output_of(result) - - -def test_check_detects_an_extra_link(tmp_path: Path) -> None: - """基準に無い名前を足すと `--check` が落ちる。""" - root = build_tree(tmp_path) - run_build(root) - (links_dir(root) / "delta").symlink_to("../../skills/delta") - - result = run_build(root, check=True) - assert result.returncode != 0 - assert "delta" in output_of(result) - - -def test_check_detects_a_wrong_target(tmp_path: Path) -> None: - """向き先が違う symlink も落ちる。実体を指していなければ配られない。""" - root = build_tree(tmp_path) - run_build(root) - link = links_dir(root) / AGY_SKILLS[0] - link.unlink() - link.symlink_to(f"../../skills/{AGY_SKILLS[1]}") - - result = run_build(root, check=True) - assert result.returncode != 0 - assert AGY_SKILLS[0] in output_of(result) - - -def test_build_removes_a_stale_link(tmp_path: Path) -> None: - """基準から外した Skill の symlink は、生成し直すと消える。""" - root = build_tree(tmp_path) - run_build(root) - (links_dir(root) / "delta").symlink_to("../../skills/delta") - - assert run_build(root).returncode == 0 - assert not (links_dir(root) / "delta").is_symlink() - - -def test_build_skips_a_family_without_the_manifest(tmp_path: Path) -> None: - """`agy-skills.txt` を持たない family では何も作らない。 - - agy へ配らないプラグイン(`playwright-kit`)を巻き込まないことを固定する。 - """ - root = build_tree(tmp_path) - (root / f"plugins/{FAMILY}/manifests/agy-skills.txt").unlink() - - assert run_build(root).returncode == 0 - assert not links_dir(root).exists() - - -def test_generated_links_are_not_collected_twice() -> None: - """`dev.agy/skills/` の symlink を、テストの収集がたどらない。 - - symlink の先は `skills/` の実体であり、そこには Skill ごとのテストがある。両方を - たどると同じテストが 2 度数えられる(agy を配布先へ加えた時点で 1569 件が 2974 件へ - 増えた)。件数は退行の判定に使うため、二重に数えられる状態を残さない。 - """ - result = subprocess.run( - [ - sys.executable, - "-m", - "pytest", - "--collect-only", - "-q", - "-p", - "no:cacheprovider", - f"plugins/{FAMILY}", - ], - cwd=REPO_ROOT, - capture_output=True, - text=True, - check=False, - ) - assert result.returncode == 0, result.stdout + result.stderr - assert "dev.agy" not in result.stdout, [line for line in result.stdout.splitlines() if "dev.agy" in line] diff --git a/scripts/tests/test_agy_install_hooks.py b/scripts/tests/test_agy_install_hooks.py deleted file mode 100644 index a294bdf1a..000000000 --- a/scripts/tests/test_agy_install_hooks.py +++ /dev/null @@ -1,263 +0,0 @@ -"""agy へ hook を差し込む導入スクリプトの振る舞いを固定する(#305)。 - -**`agy plugin install` は `hooks.json` を複製するが、agy はそれを読み込まない。** -読む先は利用者の `~/.gemini/config/hooks.json` の 1 か所だけである(agy 1.1.26 で実測。 -プラグイン配下とプロジェクト直下のどちらに置いても -`loaded 1 named hooks from 1 hooks.json file(s)` のまま変わらない)。 -`dev.agy/install-hooks.sh` がその 1 か所へ差し込む。 - -ここで見るのは、**他の項目に触れないこと**・**冪等であること**・**相対の指定を -導入先の絶対パスへ直すこと**・**壊れた設定を黙って上書きしないこと**の 4 つである。 -""" - -from __future__ import annotations - -import json -import subprocess -from pathlib import Path - -import pytest - -ROOT = Path(__file__).resolve().parents[2] -INSTALLER = ROOT / "plugins" / "ndf" / "dev.agy" / "install-hooks.sh" - -# 利用者が既に持っている項目。差し込みで消えないことを見る。 -OTHER = {"other-tool": {"PreInvocation": [{"type": "command", "command": "true"}]}} - - -def run(*args: str) -> subprocess.CompletedProcess: - return subprocess.run(["bash", str(INSTALLER), *args], capture_output=True, text=True) - - -@pytest.fixture() -def plugin_dir(tmp_path: Path) -> Path: - """導入済みの実体を模す。`hooks.json` と、指し先のスクリプトを置く。""" - target = tmp_path / "plugins" / "ndf" - (target / "scripts").mkdir(parents=True) - (target / "scripts" / "worktree-guard.sh").write_text("", encoding="utf-8") - (target / "hooks.json").write_text( - json.dumps( - { - "ndf-worktree": { - "PreToolUse": [ - { - "matcher": "Write|write_to_file", - "hooks": [ - { - "type": "command", - "command": "bash ./scripts/worktree-guard.sh", - "timeout": 5, - } - ], - } - ] - } - } - ), - encoding="utf-8", - ) - return target - - -@pytest.fixture() -def config(tmp_path: Path) -> Path: - path = tmp_path / "config" / "hooks.json" - path.parent.mkdir(parents=True) - path.write_text(json.dumps(OTHER), encoding="utf-8") - return path - - -def test_it_adds_the_plugin_hook_without_touching_other_entries(config: Path, plugin_dir: Path) -> None: - result = run("--config", str(config), "--plugin-dir", str(plugin_dir)) - - assert result.returncode == 0, result.stderr - written = json.loads(config.read_text(encoding="utf-8")) - assert sorted(written) == ["ndf-worktree", "other-tool"] - assert written["other-tool"] == OTHER["other-tool"] - - -def test_it_rewrites_relative_commands_to_the_installed_path(config: Path, plugin_dir: Path) -> None: - """利用者の設定へ差し込むと、実行時の現在地はプラグインの位置と揃わない。""" - run("--config", str(config), "--plugin-dir", str(plugin_dir)) - - written = json.loads(config.read_text(encoding="utf-8")) - command = written["ndf-worktree"]["PreToolUse"][0]["hooks"][0]["command"] - assert command == f"bash {plugin_dir}/scripts/worktree-guard.sh" - assert "./scripts/" not in command - - -def test_running_it_twice_changes_nothing(config: Path, plugin_dir: Path) -> None: - run("--config", str(config), "--plugin-dir", str(plugin_dir)) - first = config.read_text(encoding="utf-8") - - result = run("--config", str(config), "--plugin-dir", str(plugin_dir)) - - assert result.returncode == 0 - assert "変更なし" in result.stdout - assert config.read_text(encoding="utf-8") == first - - -def test_dry_run_does_not_write(config: Path, plugin_dir: Path) -> None: - before = config.read_text(encoding="utf-8") - - result = run("--config", str(config), "--plugin-dir", str(plugin_dir), "--dry-run") - - assert result.returncode == 0 - assert config.read_text(encoding="utf-8") == before - - -def test_uninstall_removes_only_the_plugin_entries(config: Path, plugin_dir: Path) -> None: - run("--config", str(config), "--plugin-dir", str(plugin_dir)) - - result = run("--config", str(config), "--plugin-dir", str(plugin_dir), "--uninstall") - - assert result.returncode == 0, result.stderr - written = json.loads(config.read_text(encoding="utf-8")) - assert sorted(written) == ["other-tool"] - - -def test_it_creates_the_config_when_absent(tmp_path: Path, plugin_dir: Path) -> None: - missing = tmp_path / "fresh" / "hooks.json" - - result = run("--config", str(missing), "--plugin-dir", str(plugin_dir)) - - assert result.returncode == 0, result.stderr - assert sorted(json.loads(missing.read_text(encoding="utf-8"))) == ["ndf-worktree"] - - -def test_it_stops_on_a_broken_config(tmp_path: Path, plugin_dir: Path) -> None: - """読めない設定を黙って上書きしない。利用者の hook がまとめて消える。""" - broken = tmp_path / "broken.json" - broken.write_text("{ this is not json", encoding="utf-8") - - result = run("--config", str(broken), "--plugin-dir", str(plugin_dir)) - - assert result.returncode != 0 - assert broken.read_text(encoding="utf-8") == "{ this is not json" - - -def test_it_falls_back_to_the_repository_definition(config: Path, tmp_path: Path) -> None: - """導入前でも中身を確かめられる。実体が無ければリポジトリの定義を読む。""" - result = run( - "--config", - str(config), - "--plugin-dir", - str(tmp_path / "not-installed"), - "--dry-run", - ) - - assert result.returncode == 0, result.stderr - assert "ndf-worktree" in result.stdout - - -def test_the_repository_definition_is_the_one_that_is_distributed() -> None: - """差し込む定義は、配布する `hooks.json` と同じものである。""" - source = json.loads((INSTALLER.parent / "hooks.json").read_text(encoding="utf-8")) - assert sorted(source) == ["ndf-worktree"] - assert sorted(source["ndf-worktree"]) == ["PreInvocation", "PreToolUse"] - - -def test_a_relative_plugin_dir_is_resolved_to_an_absolute_path(config: Path, plugin_dir: Path, tmp_path: Path) -> None: - """agy はリポジトリの外でも起動する。相対のまま保存すると hook が落ちる(#417 の 3)。 - - `plugins/ndf/README.md` の案内は clone からの相対パスで書いてあるため、書かれた - とおりに実行すると踏む。 - """ - result = subprocess.run( - [ - "bash", - str(INSTALLER), - "--config", - str(config), - "--plugin-dir", - str(plugin_dir.relative_to(tmp_path)), - ], - cwd=str(tmp_path), - capture_output=True, - text=True, - ) - - assert result.returncode == 0, result.stderr - written = json.loads(config.read_text(encoding="utf-8")) - command = written["ndf-worktree"]["PreToolUse"][0]["hooks"][0]["command"] - assert command == f"bash {plugin_dir}/scripts/worktree-guard.sh" - - -@pytest.fixture() -def spaced_plugin_dir(tmp_path: Path) -> Path: - """空白とシェルの特殊文字を含む導入先。""" - target = tmp_path / "plugin space" / "ndf$x" - (target / "scripts").mkdir(parents=True) - (target / "scripts" / "worktree-guard.sh").write_text("#!/usr/bin/env bash\nexit 0\n", encoding="utf-8") - (target / "hooks.json").write_text( - json.dumps( - { - "ndf-worktree": { - "PreInvocation": [ - { - "type": "command", - "command": "bash ./scripts/worktree-guard.sh", - "timeout": 5, - } - ] - } - } - ), - encoding="utf-8", - ) - return target - - -def test_a_command_with_a_spaced_path_is_runnable(config: Path, spaced_plugin_dir: Path) -> None: - """文字列の一致だけでは、シェルの語として壊れていることを捕まえられない(#417 の 4)。 - - **正常終了するのに hook が効かない**形になるため、利用者から見て失敗が見えない。 - """ - result = run("--config", str(config), "--plugin-dir", str(spaced_plugin_dir)) - assert result.returncode == 0, result.stderr - - written = json.loads(config.read_text(encoding="utf-8")) - command = written["ndf-worktree"]["PreInvocation"][0]["command"] - executed = subprocess.run(["bash", "-c", command], capture_output=True, text=True) - assert executed.returncode == 0, f"{command}\n{executed.stderr}" - - -def test_uninstall_keeps_entries_the_plugin_does_not_distribute(config: Path, plugin_dir: Path) -> None: - """`ndf-` で始まる利用者の項目まで消さない(#417 の 8)。""" - mine = {"PreInvocation": [{"type": "command", "command": "true"}]} - current = json.loads(config.read_text(encoding="utf-8")) - current["ndf-mine"] = mine - config.write_text(json.dumps(current), encoding="utf-8") - run("--config", str(config), "--plugin-dir", str(plugin_dir)) - - result = run("--config", str(config), "--plugin-dir", str(plugin_dir), "--uninstall") - - assert result.returncode == 0, result.stderr - written = json.loads(config.read_text(encoding="utf-8")) - assert sorted(written) == ["ndf-mine", "other-tool"] - assert written["ndf-mine"] == mine - - -def test_the_write_leaves_no_temporary_file_behind(config: Path, plugin_dir: Path) -> None: - """置き換えで書く。中断で壊れた設定を残さない(#417 の 8)。""" - result = run("--config", str(config), "--plugin-dir", str(plugin_dir)) - - assert result.returncode == 0, result.stderr - leftovers = sorted(p.name for p in config.parent.iterdir() if p.name not in {"hooks.json", "hooks.json.bak"}) - assert leftovers == [] - assert sorted(json.loads(config.read_text(encoding="utf-8"))) == ["ndf-worktree", "other-tool"] - - -def test_it_reports_a_command_it_could_not_rewrite(config: Path, tmp_path: Path) -> None: - """黙って相対パスのまま保存すると、正常終了して効かない状態になる(決定 6)。""" - target = tmp_path / "odd" - (target / "scripts").mkdir(parents=True) - (target / "hooks.json").write_text( - json.dumps({"ndf-odd": {"PreInvocation": [{"type": "command", "command": "sh -c './scripts/x.sh'"}]}}), - encoding="utf-8", - ) - - result = run("--config", str(config), "--plugin-dir", str(target)) - - assert result.returncode == 0, result.stderr - assert "書き換えられなかった" in result.stdout + result.stderr diff --git a/scripts/tests/test_agy_listing_budget.py b/scripts/tests/test_agy_listing_budget.py deleted file mode 100644 index 6c64805a4..000000000 --- a/scripts/tests/test_agy_listing_budget.py +++ /dev/null @@ -1,114 +0,0 @@ -"""agy の初期一覧を予算の判定へ入れる(#215 の受け入れ条件 A10)。 - -配布先が 1 つ増えても、`manifests/agy-skills.txt` を読む経路と予算を突き合わせる経路が -無ければ、agy の一覧だけが判定されないまま増え続ける。**予算そのものは Claude Code から -借りた値である**(agy に規定が無いため。`scripts/check-skill-frontmatter.py` の注記)。 -借りた事実は注記に残し、ここでは「借りた値で判定が働くこと」を固定する。 - -一時ディレクトリへ作った木に対して実行する。実物の Skill は読むだけで、書き換えない。 -""" - -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[2] -CHECKER = REPO_ROOT / "scripts/check-skill-frontmatter.py" - -FAMILY = "ndf" - - -def write_skill(skills_dir: Path, name: str, description: str) -> None: - (skills_dir / name).mkdir(parents=True, exist_ok=True) - (skills_dir / name / "SKILL.md").write_text( - f'---\nname: {name}\ndescription: "{description}"\n---\n\n# {name}\n', - encoding="utf-8", - ) - - -def build_tree(base: Path, count: int, description: str) -> Path: - """agy の manifest を持つ木を作り、その `skills/` を返す。""" - plugin = base / f"plugins/{FAMILY}" - skills_dir = plugin / "skills" - names = [f"probe-{index:03d}" for index in range(count)] - for name in names: - write_skill(skills_dir, name, description) - (plugin / "manifests").mkdir(parents=True, exist_ok=True) - for runtime in ("claude", "codex", "kiro", "agy"): - (plugin / f"manifests/{runtime}-skills.txt").write_text("".join(f"{name}\n" for name in names), encoding="utf-8") - return skills_dir - - -def run_checker(skills_dir: Path, *args: str) -> subprocess.CompletedProcess[str]: - return subprocess.run( - [sys.executable, str(CHECKER), "--skills-dir", str(skills_dir), *args], - capture_output=True, - text=True, - check=False, - ) - - -def report_row(output: str, runtime: str) -> list[str]: - for line in output.splitlines(): - columns = line.split() - if columns and columns[0] == runtime: - return columns - raise AssertionError(f"{runtime} の行が出力に無い\n{output}") - - -def test_report_lists_agy_with_a_budget(tmp_path: Path) -> None: - """報告に agy の行が出て、上限を持つ。""" - skills_dir = build_tree(tmp_path, count=3, description="Probe skill for the listing budget.") - result = run_checker(skills_dir, "--report") - - assert result.returncode == 0, result.stdout + result.stderr - row = report_row(result.stdout, "agy") - assert row[1].isdigit() and int(row[1]) > 0, row - assert row[2].isdigit() and int(row[2]) > 0, row - - -def test_agy_borrows_the_claude_budget(tmp_path: Path) -> None: - """上限は Claude Code と同じ値である(同じコンテキスト長の規定を借りている)。""" - skills_dir = build_tree(tmp_path, count=3, description="Probe skill for the listing budget.") - result = run_checker(skills_dir, "--report") - - assert report_row(result.stdout, "agy")[2] == report_row(result.stdout, "claude")[2] - - -def test_agy_listing_counts_the_path(tmp_path: Path) -> None: - """agy の一覧はパスを含む側で見積もる。Claude Code の合計より大きくなる。 - - 一覧の構成に公式の記述が無いため、多い側で見積もる(Kiro CLI と同じ扱い)。 - """ - skills_dir = build_tree(tmp_path, count=3, description="Probe skill for the listing budget.") - result = run_checker(skills_dir, "--report") - - agy = int(report_row(result.stdout, "agy")[1]) - claude = int(report_row(result.stdout, "claude")[1]) - assert agy > claude, result.stdout - - -def test_agy_budget_overflow_is_reported(tmp_path: Path) -> None: - """予算を超えると agy の指摘が出る。読み取るだけで判定していない状態を弾く。""" - skills_dir = build_tree(tmp_path, count=60, description="x" * 900) - result = run_checker(skills_dir) - - output = result.stdout + result.stderr - assert "ops/agy-listing" in output, output - assert result.returncode != 0 - - -def test_no_agy_manifest_means_no_agy_row(tmp_path: Path) -> None: - """`agy-skills.txt` を持たない木では agy の行が出ない。 - - manifest が唯一の配布の基準であることを、無い側でも確かめる。 - """ - skills_dir = build_tree(tmp_path, count=3, description="Probe skill for the listing budget.") - (skills_dir.parent / "manifests/agy-skills.txt").unlink() - result = run_checker(skills_dir, "--report") - - assert result.returncode == 0, result.stdout + result.stderr - with_agy = [line for line in result.stdout.splitlines() if line.split()[:1] == ["agy"]] - assert not with_agy, result.stdout diff --git a/scripts/tests/test_distribution_runtimes.py b/scripts/tests/test_distribution_runtimes.py new file mode 100644 index 000000000..7b9a4d2b6 --- /dev/null +++ b/scripts/tests/test_distribution_runtimes.py @@ -0,0 +1,56 @@ +"""配布先は Claude Code と Codex だけである(#2231 の I1)。 + +agy と Kiro CLI へは何も配らず、cross-review / cross-refactoring の相手役として起動するだけにした。 +その導入物(`agy-skills.txt`・`kiro-skills.txt`・`dev.agy/`・`dev.kiro/`)がどのプラグインにも戻らないこと、 +リリースの対象ランタイム(`.ndf/supervise.json` の `release.runtimes`)が `claude`・`codex` の中に +収まることを確かめる。生成の手順は agy・kiro の導入物を作らないため、手で足したものはここでしか見つからない。 +""" + +from __future__ import annotations + +import json +from pathlib import Path + +REPO = Path(__file__).resolve().parents[2] +DISTRIBUTED = {"claude", "codex"} +RETIRED_FILES = ("agy-skills.txt", "kiro-skills.txt") +RETIRED_DIRS = ("dev.agy", "dev.kiro") + + +def retired_paths(root: Path) -> list[str]: + """`plugins/` の下にある agy・kiro の導入物を、根からの相対パスで返す。""" + found = [p for name in RETIRED_FILES for p in root.glob(f"plugins/**/manifests/{name}")] + found += [p for name in RETIRED_DIRS for p in root.glob(f"plugins/**/{name}") if p.is_dir()] + return sorted(p.relative_to(root).as_posix() for p in found) + + +def release_runtimes(root: Path) -> set[str]: + data = json.loads((root / ".ndf/supervise.json").read_text(encoding="utf-8")) + return set(data["release"]["runtimes"]) + + +def test_no_plugin_ships_agy_or_kiro_files() -> None: + assert retired_paths(REPO) == [] + + +def test_release_runtimes_are_claude_and_codex_only() -> None: + runtimes = release_runtimes(REPO) + assert runtimes + assert runtimes <= DISTRIBUTED, runtimes - DISTRIBUTED + + +def test_a_restored_kiro_installer_is_found(tmp_path: Path) -> None: + """mcp-* の `dev.kiro/install.sh` を 1 つ戻すと見つかる(検査が空振りしていないこと)。""" + installer = tmp_path / "plugins/mcp/mcp-notion/dev.kiro/install.sh" + installer.parent.mkdir(parents=True) + installer.write_text("#!/bin/sh\n", encoding="utf-8") + manifest = tmp_path / "plugins/ndf/manifests/agy-skills.txt" + manifest.parent.mkdir(parents=True) + manifest.write_text("pr\n", encoding="utf-8") + assert retired_paths(tmp_path) == ["plugins/mcp/mcp-notion/dev.kiro", "plugins/ndf/manifests/agy-skills.txt"] + + +def test_a_kiro_release_runtime_is_found(tmp_path: Path) -> None: + (tmp_path / ".ndf").mkdir() + (tmp_path / ".ndf/supervise.json").write_text(json.dumps({"release": {"runtimes": ["claude", "codex", "kiro"]}}), encoding="utf-8") + assert release_runtimes(tmp_path) - DISTRIBUTED == {"kiro"} diff --git a/scripts/tests/test_doc_staleness.py b/scripts/tests/test_doc_staleness.py index 0d2cad35b..1648fdb0b 100644 --- a/scripts/tests/test_doc_staleness.py +++ b/scripts/tests/test_doc_staleness.py @@ -74,9 +74,7 @@ def test_root_defaults_to_current_directory() -> None: ("before", "after", "expected"), [ ("Claude Code向け core 5個", "Claude Code向け core 4個", "5"), - ("Kiro向け core 4個", "Kiro向け core 3個", "4"), ("Codex向け core 3個", "Codex向け core 2個", "3"), - ("agy向け core 2個", "agy向け core 1個", "2"), ], ) def test_runtime_skill_count_mismatch_fails(tree: Path, before: str, after: str, expected: str) -> None: @@ -92,9 +90,7 @@ def test_runtime_skill_count_mismatch_fails(tree: Path, before: str, after: str, "fragment", [ "Claude Code向け core 5個、", - "Kiro向け core 4個、", - "Codex向け core 3個、", - "agy向け core 2個", + "Codex向け core 3個", ], ) def test_runtime_skill_count_removed_fails(tree: Path, fragment: str) -> None: @@ -167,8 +163,6 @@ def test_category_breakdown_removed_fails(tree: Path) -> None: [ ("| Claude Code | 5 個 |", "| Claude Code | 4 個 |", "5"), ("| Codex | 3 個 |", "| Codex | 2 個 |", "3"), - ("| Kiro CLI | 4 個 |", "| Kiro CLI | 9 個 |", "4"), - ("| agy | 2 個 |", "| agy | 7 個 |", "2"), ], ) def test_distribution_table_mismatch_fails(tree: Path, before: str, after: str, expected: str) -> None: @@ -185,8 +179,6 @@ def test_distribution_table_mismatch_fails(tree: Path, before: str, after: str, [ "| Claude Code | 5 個 | `.claude-plugin/plugin.json` |\n", "| Codex | 3 個 | `.codex-plugin/plugin.json` |\n", - "| Kiro CLI | 4 個 | `dev.kiro/install.sh` |\n", - "| agy | 2 個 | `dev.agy/plugin.json` |\n", ], ) def test_distribution_table_row_removed_fails(tree: Path, row: str) -> None: @@ -265,10 +257,10 @@ def test_failure_output_names_file_label_and_both_values(tree: Path) -> None: def test_missing_manifest_fails(tree: Path) -> None: """数える相手が無いことを、読み取れた値と一致しているとみなさない。""" - (tree / "plugins/ndf/manifests/kiro-skills.txt").unlink() + (tree / "plugins/ndf/manifests/codex-skills.txt").unlink() result = run_check(tree) assert result.returncode != 0 - assert "kiro-skills.txt" in output_of(result) + assert "codex-skills.txt" in output_of(result) def test_missing_plugin_json_fails(tree: Path) -> None: @@ -315,7 +307,6 @@ def agents_md(tree: Path) -> Path: ("mark", "document", "before", "after"), [ ("G", "README.md", "**NDFプラグイン v9.3.0**", "**NDFプラグイン v9.2.1**"), - ("K", "plugins/ndf/README.md", "Kiro CLI用 / v9.3.0)", "Kiro CLI用 / v9.2.1)"), ( "M", "plugins/ndf/README.md", @@ -352,7 +343,6 @@ def test_codex_cache_path_partial_stale_fails(tree: Path) -> None: ("mark", "document", "fragment", "occurrences"), [ ("G", "README.md", "**NDFプラグイン v9.3.0** のチェック用の最小構成です。\n", 1), - ("K", "plugins/ndf/README.md", "# => NDF統合開発エージェント(Kiro CLI用 / v9.3.0)\n", 1), ("L", "plugins/ndf/README.md", "~/.codex/plugins/cache/ai-plugins/ndf/9.3.0/skills/deploy/SKILL.md", 2), ("M", "plugins/ndf/README.md", "# => ndf@ai-plugins installed, enabled 9.3.0 \n", 1), ], @@ -804,7 +794,7 @@ def test_versions_outside_the_section_do_not_fail(tree: Path) -> None: def test_bumping_only_the_plugin_version_reports_every_body_claim(tree: Path) -> None: - """この課題が起きた経路そのもの。説明文書を直さずに版だけ上げると 6 種類が挙がる。""" + """この課題が起きた経路そのもの。説明文書を直さずに版だけ上げると 5 種類が挙がる。""" bump_plugin_version(tree, "9.4.0") result = run_check(tree) assert result.returncode != 0 @@ -813,7 +803,6 @@ def test_bumping_only_the_plugin_version_reports_every_body_claim(tree: Path) -> "概要の版数", "プラグイン一覧表の ndf の版数", "版の付け方の節の版数", - "Kiro の確認例の版数", "Codex のキャッシュパスの例の版数", "`codex plugin list` の出力例の版数", ): @@ -907,12 +896,12 @@ def test_manifest_skill_count_with_only_blank_and_comment_lines_returns_zero(tmp `None` と区別されている経路である。 """ module = _load_checker() - manifest = tmp_path / "plugins/ndf/manifests/kiro-skills.txt" + manifest = tmp_path / "plugins/ndf/manifests/codex-skills.txt" manifest.parent.mkdir(parents=True) manifest.write_text("# コメント行\n\n \n# 別のコメント\n", encoding="utf-8") report = module.Report() - assert module.manifest_skill_count(tmp_path, "kiro", report) == 0 + assert module.manifest_skill_count(tmp_path, "codex", report) == 0 assert report.errors == [] diff --git a/scripts/tests/test_kiro_installer_project.py b/scripts/tests/test_kiro_installer_project.py deleted file mode 100644 index 0dd8627ae..000000000 --- a/scripts/tests/test_kiro_installer_project.py +++ /dev/null @@ -1,485 +0,0 @@ -"""Kiro のインストーラが `--project` の誤った値を案内つきで止めることを固定する(#415)。 - -変更前は `cd` の裸のエラーと終了コード 1 で終わり、何を直せばよいかが読み取れなかった。 -ここで見るのは、**誤りの種類ごとに文言が分かれること**・**存在しないパスにだけ -`mkdir -p` の案内が付くこと**・**案内のパスが bash の語 1 つとして読み戻せること**・ -**正しい値では振る舞いが変わらないこと**の 4 つである。 - -終了コードはどちらの誤りも 2 であるため、文言まで見ないと 2 つの誤りを区別できない。 -正しい値の確認は `--dry-run` で行い、利用者の環境へ書き込まない。 -""" - -from __future__ import annotations - -import json -import os -import shutil -import subprocess -from pathlib import Path - -import pytest - -ROOT = Path(__file__).resolve().parents[2] -INSTALLER = ROOT / "plugins" / "ndf" / "dev.kiro" / "install.sh" - - -def run(*args: str, home: Path, cwd: Path | None = None) -> subprocess.CompletedProcess: - # `--scope global` は HOME の下を導入先にする。誤って書き込んでも利用者の HOME に - # 届かないよう、一時ディレクトリを HOME として渡す。 - env = {**os.environ, "HOME": str(home)} - return subprocess.run( - ["bash", str(INSTALLER), *args], - capture_output=True, - text=True, - env=env, - cwd=cwd, - ) - - -def hint_words(stderr: str) -> list[str]: - """`HINT: mkdir -p ` に続く部分を bash で読み戻し、語の並びを返す。 - - `shlex.split` は `$'...'` の形を解さないため使わない(タブを含むパスで `$` が残る)。 - """ - prefix = "HINT: mkdir -p " - lines = [line for line in stderr.splitlines() if line.startswith(prefix)] - assert len(lines) == 1, stderr - quoted = lines[0][len(prefix) :] - out = subprocess.run( - ["bash", "-c", 'eval "set -- $1"; printf "%s\\0" "$@"', "_", quoted], - capture_output=True, - text=True, - check=True, - ).stdout - return out.split("\0")[:-1] - - -def assert_no_bare_cd_error(proc: subprocess.CompletedProcess) -> None: - assert "cd:" not in proc.stdout + proc.stderr - - -@pytest.mark.parametrize( - ("missing_path", "extra_args"), - [ - (Path("skills"), ()), - (Path("manifests/kiro-skills.txt"), ()), - (Path("dev.kiro/prompts/codex.md"), ("--with-codex",)), - ], -) -def test_missing_prerequisite_stops_with_path_error(tmp_path: Path, missing_path: Path, extra_args: tuple[str, ...]) -> None: - # 現状固定: 必須パスの種類やチェック位置によらず、同じ形式のエラー 1 行と - # 終了コード 1 で停止する。 - plugin_dir = tmp_path / "ndf" - shutil.copytree(INSTALLER.parents[1], plugin_dir) - missing = plugin_dir / missing_path - if missing.is_dir(): - shutil.rmtree(missing) - else: - missing.unlink() - - project = tmp_path / "project" - project.mkdir() - env = {**os.environ, "HOME": str(tmp_path)} - proc = subprocess.run( - [ - "bash", - str(plugin_dir / "dev.kiro" / "install.sh"), - "--project", - str(project), - "--dry-run", - "--yes", - *extra_args, - ], - capture_output=True, - text=True, - env=env, - ) - - assert proc.returncode == 1 - assert proc.stderr.splitlines() == [f"ERROR: {missing} が見つかりません"] - - -def test_project_without_path_stops_with_error(tmp_path: Path) -> None: - proc = run("--project", home=tmp_path) - - assert proc.returncode == 2 - assert proc.stderr.splitlines() == ["ERROR: --project requires a path"] - assert_no_bare_cd_error(proc) - - -@pytest.mark.parametrize("name", ["missing", "my project", "tab\tx"]) -def test_missing_path_stops_with_error_and_hint(tmp_path: Path, name: str) -> None: - target = tmp_path / name - proc = run("--project", str(target), "--yes", home=tmp_path) - - assert proc.returncode == 2 - lines = proc.stderr.splitlines() - assert lines[0] == f"ERROR: --project points at a path that does not exist: {target}" - assert lines[1].startswith("HINT: mkdir -p ") - assert len(lines) == 2 - # 案内のパスは、空白やタブを含んでも語 1 つとして渡したパスへ戻る - assert hint_words(proc.stderr) == [str(target)] - assert not target.exists() - assert_no_bare_cd_error(proc) - - -def test_empty_path_stops_with_error_and_hint(tmp_path: Path) -> None: - proc = run("--project", "", "--yes", home=tmp_path) - - assert proc.returncode == 2 - lines = proc.stderr.splitlines() - assert lines[0] == "ERROR: --project points at a path that does not exist: " - assert lines[1].startswith("HINT: mkdir -p ") - assert len(lines) == 2 - assert hint_words(proc.stderr) == [""] - assert list(tmp_path.iterdir()) == [] - assert_no_bare_cd_error(proc) - - -def test_file_path_stops_without_hint(tmp_path: Path) -> None: - target = tmp_path / "afile" - target.write_text("", encoding="utf-8") - proc = run("--project", str(target), "--yes", home=tmp_path) - - assert proc.returncode == 2 - assert proc.stderr.splitlines() == [f"ERROR: --project points at a path that is not a directory: {target}"] - assert "HINT:" not in proc.stderr - assert_no_bare_cd_error(proc) - - -def test_global_scope_with_missing_path_stops_the_same_way(tmp_path: Path) -> None: - target = tmp_path / "missing" - proc = run("--scope", "global", "--project", str(target), "--yes", home=tmp_path) - - assert proc.returncode == 2 - lines = proc.stderr.splitlines() - assert lines[0] == f"ERROR: --project points at a path that does not exist: {target}" - assert hint_words(proc.stderr) == [str(target)] - assert len(lines) == 2 - assert "WARN:" not in proc.stderr - assert_no_bare_cd_error(proc) - - -def test_global_scope_with_existing_path_warns_and_succeeds(tmp_path: Path) -> None: - # --scope global と存在するディレクトリの併用では、PROJECT_GIVEN が真になって - # WARN を出しつつ正常終了する。誤りではないため終了コードは 0 で、案内 (HINT) も - # 出ない。存在しないパス併用時のエラー停止(上のテスト)とは別の経路である。 - project = tmp_path / "project" - project.mkdir() - proc = run( - "--scope", - "global", - "--project", - str(project), - "--dry-run", - "--yes", - home=tmp_path, - ) - - assert proc.returncode == 0, proc.stderr - assert "WARN: --scope global では --project は使用されません" in proc.stderr - assert "ERROR:" not in proc.stderr - assert "HINT:" not in proc.stderr - assert_no_bare_cd_error(proc) - - -def test_existing_directory_is_unchanged(tmp_path: Path) -> None: - project = tmp_path / "project" - project.mkdir() - proc = run("--project", str(project), "--dry-run", "--yes", home=tmp_path) - - assert proc.returncode == 0, proc.stderr - assert "ERROR:" not in proc.stderr - assert_no_bare_cd_error(proc) - # --dry-run は導入先へ書き込まない - assert list(project.iterdir()) == [] - - -def test_reinstall_preserves_user_managed_agent_config(tmp_path: Path) -> None: - project = tmp_path / "project" - project.mkdir() - first = run("--project", str(project), "--yes", home=tmp_path) - assert first.returncode == 0, first.stderr - - agent_file = project / ".kiro" / "agents" / "ndf.json" - config = json.loads(agent_file.read_text(encoding="utf-8")) - config["customKey"] = {"enabled": True} - config.setdefault("mcpServers", {})["myserver"] = { - "command": "myserver", - "args": ["serve"], - } - config.setdefault("hooks", {})["myhook"] = [{"command": "echo custom", "timeout_ms": 1000}] - agent_file.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") - - second = run("--project", str(project), "--yes", home=tmp_path) - assert second.returncode == 0, second.stderr - - reinstalled = json.loads(agent_file.read_text(encoding="utf-8")) - assert reinstalled["customKey"] == {"enabled": True} - assert reinstalled["mcpServers"]["myserver"] == { - "command": "myserver", - "args": ["serve"], - } - assert reinstalled["hooks"]["myhook"] == [{"command": "echo custom", "timeout_ms": 1000}] - assert reinstalled["hooks"]["agentSpawn"] - assert reinstalled["hooks"]["userPromptSubmit"] - - -def test_reinstall_removes_optional_features_when_flags_omitted(tmp_path: Path) -> None: - project = tmp_path / "project" - project.mkdir() - first = run( - "--project", - str(project), - "--with-slack", - "--with-codex", - "--yes", - home=tmp_path, - ) - assert first.returncode == 0, first.stderr - - agent_file = project / ".kiro" / "agents" / "ndf.json" - config = json.loads(agent_file.read_text(encoding="utf-8")) - assert "stop" in config["hooks"] - assert "codex" in config["mcpServers"] - - config["customKey"] = {"enabled": True} - config["mcpServers"]["myserver"] = { - "command": "myserver", - "args": ["serve"], - } - config["hooks"]["myhook"] = [{"command": "echo custom", "timeout_ms": 1000}] - agent_file.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") - - second = run("--project", str(project), "--yes", home=tmp_path) - assert second.returncode == 0, second.stderr - - reinstalled = json.loads(agent_file.read_text(encoding="utf-8")) - assert "stop" not in reinstalled["hooks"] - assert "codex" not in reinstalled.get("mcpServers", {}) - assert reinstalled["mcpServers"]["myserver"] == { - "command": "myserver", - "args": ["serve"], - } - assert reinstalled["hooks"]["myhook"] == [{"command": "echo custom", "timeout_ms": 1000}] - assert reinstalled["customKey"] == {"enabled": True} - assert reinstalled["hooks"]["agentSpawn"] - assert reinstalled["hooks"]["userPromptSubmit"] - - -def test_reinstall_removes_deprecated_prompts_and_keeps_user_prompt(tmp_path: Path) -> None: - # 配布を終えた clean.md と review.md は再インストールで除去し、利用者が置いた - # 無関係な prompt は残す。削除範囲が広がる退行を検出するための現状固定である。 - project = tmp_path / "project" - prompts_dir = project / ".kiro" / "prompts" - prompts_dir.mkdir(parents=True) - (prompts_dir / "clean.md").write_text("old clean\n", encoding="utf-8") - (prompts_dir / "review.md").write_text("old review\n", encoding="utf-8") - custom_text = "利用者が置いた prompt\n" - (prompts_dir / "custom.md").write_text(custom_text, encoding="utf-8") - - proc = run("--project", str(project), "--yes", home=tmp_path) - - assert proc.returncode == 0, proc.stderr - assert " removed (deprecated): clean" in proc.stdout.splitlines() - assert " removed (deprecated): review" in proc.stdout.splitlines() - assert not (prompts_dir / "clean.md").exists() - assert not (prompts_dir / "review.md").exists() - assert (prompts_dir / "pr.md").is_file() - assert (prompts_dir / "custom.md").read_text(encoding="utf-8") == custom_text - - # --with-codex を付けないため codex.md は配布されない - distributed = {p.name for p in (INSTALLER.parent / "prompts").glob("*.md") if p.name != "codex.md"} - assert distributed - assert {p.name for p in prompts_dir.iterdir()} == distributed | {"custom.md"} - - -@pytest.mark.parametrize( - ("existing", "warn_marker"), - [ - # 不正 JSON: 読み込みが例外になり、読めない旨で引継ぎを断念する。 - ("{ not json", "を読めないため引き継ぎません"), - # JSON オブジェクト以外(配列): dict でないため引継ぎを断念する。 - ("[]", "が JSON オブジェクトではないため引き継ぎません"), - # hooks がオブジェクト以外(配列): その節だけ引継ぎを断念する。 - ('{"hooks": []}', "の hooks が JSON オブジェクトではないため引き継ぎません"), - # mcpServers がオブジェクト以外(文字列): その節だけ引継ぎを断念する。 - ('{"mcpServers": "x"}', "の mcpServers が JSON オブジェクトではないため引き継ぎません"), - ], -) -def test_reinstall_with_malformed_agent_config_warns_and_continues(tmp_path: Path, existing: str, warn_marker: str) -> None: - # 既存 ndf.json が不正 JSON・JSON オブジェクト以外・hooks / mcpServers が - # オブジェクト以外のとき、引継ぎを断念して警告を出しつつ導入を続ける経路を固定する。 - # 終了コードは 0 で、出力された ndf.json は JSON オブジェクトとして読め、 - # テンプレート由来の agentSpawn と userPromptSubmit を必ず持つ。 - project = tmp_path / "project" - (project / ".kiro" / "agents").mkdir(parents=True) - agent_file = project / ".kiro" / "agents" / "ndf.json" - agent_file.write_text(existing, encoding="utf-8") - - proc = run("--project", str(project), "--yes", home=tmp_path) - - assert proc.returncode == 0, proc.stderr - # 引継ぎ断念の警告は Python 側の print で標準出力へ出る。 - assert f"WARN: 既存の {agent_file} {warn_marker}" in proc.stdout - assert_no_bare_cd_error(proc) - - reinstalled = json.loads(agent_file.read_text(encoding="utf-8")) - assert isinstance(reinstalled, dict) - assert reinstalled["hooks"]["agentSpawn"] - assert reinstalled["hooks"]["userPromptSubmit"] - - -@pytest.mark.parametrize( - ("cwd_part", "arg", "resolved_part"), - [(".", "project/sub", "project/sub"), ("project", ".", "project")], -) -def test_relative_path_is_resolved_against_cwd(tmp_path: Path, cwd_part: str, arg: str, resolved_part: str) -> None: - # 相対パスは実行時のカレントディレクトリを起点に `cd` と `pwd` で絶対パスへ解決され、 - # スコープの表示にはその絶対パスが出る。 - base = tmp_path.resolve() - (base / "project" / "sub").mkdir(parents=True) - proc = run("--project", arg, "--dry-run", "--yes", home=base, cwd=base / cwd_part) - - assert proc.returncode == 0, proc.stderr - assert f"スコープ: workspace ({base / resolved_part}/.kiro)" in proc.stdout - assert "ERROR:" not in proc.stderr - assert_no_bare_cd_error(proc) - - -@pytest.mark.parametrize("case", ["symlink", "file", "directory"]) -def test_ndf_policies_skill_migration(tmp_path: Path, case: str) -> None: - # ndf-policies の旧シンボリックリンクは削除する一方、同名の通常ファイルまたは - # 実体ディレクトリは保持する分岐と、どちらでも steering を生成する結果を固定する。 - project = tmp_path / f"project_{case}" - skills_dir = project / ".kiro" / "skills" - skills_dir.mkdir(parents=True) - target = skills_dir / "ndf-policies" - - if case == "symlink": - dummy = tmp_path / "legacy_target" - dummy.mkdir() - target.symlink_to(dummy) - elif case == "file": - target.write_text("user managed ndf-policies file\n", encoding="utf-8") - elif case == "directory": - target.mkdir() - (target / "custom.md").write_text("user managed content\n", encoding="utf-8") - - proc = run("--project", str(project), "--yes", home=tmp_path) - - assert proc.returncode == 0, proc.stderr - assert_no_bare_cd_error(proc) - - if case == "symlink": - assert not target.exists() - assert not target.is_symlink() - assert " REMOVED: ndf-policies (steering へ移行済みのため .kiro/skills のリンクを削除)" in proc.stdout - assert f"WARN: {target} はシンボリックリンクではありません。" not in proc.stderr - elif case == "file": - assert target.is_file() - assert not target.is_symlink() - assert target.read_text(encoding="utf-8") == "user managed ndf-policies file\n" - assert f"WARN: {target} はシンボリックリンクではありません。" in proc.stderr - assert " REMOVED: ndf-policies" not in proc.stdout - elif case == "directory": - assert target.is_dir() - assert not target.is_symlink() - assert (target / "custom.md").read_text(encoding="utf-8") == "user managed content\n" - assert f"WARN: {target} はシンボリックリンクではありません。" in proc.stderr - assert " REMOVED: ndf-policies" not in proc.stdout - - steering_file = project / ".kiro" / "steering" / "ndf-policies.md" - assert steering_file.is_file() - steering_content = steering_file.read_text(encoding="utf-8") - assert "" in steering_content - assert "" in steering_content - assert "# NDFポリシー" in steering_content - assert "## ブランチ運用の原則" in steering_content - - source_skill = ROOT / "plugins" / "ndf" / "skills" / "ndf-policies" / "SKILL.md" - source_body = source_skill.read_text(encoding="utf-8").split("\n---\n", 1)[1].strip("\n") - assert source_body in steering_content - - -def _kiro_manifest_skills() -> set[str]: - """manifests/kiro-skills.txt が配布対象として列挙する Skill 名の集合。 - - install.sh の Step 1 と同じ整形(コメント除去・末尾空白除去・空行除去)で読む。 - """ - manifest = INSTALLER.parents[1] / "manifests" / "kiro-skills.txt" - names: set[str] = set() - for raw in manifest.read_text(encoding="utf-8").splitlines(): - line = raw.split("#", 1)[0].rstrip() - if line: - names.add(line) - return names - - -def test_manifest_skills_are_linked_with_count_and_output(tmp_path: Path) -> None: - # 現状固定: Step 1 の Skill 配布パイプラインは manifest 掲載 Skill のうち - # ndf-policies を除いた分だけ .kiro/skills/ へ symlink を張り、その本数を - # 「Skills数」として出力し、各 Skill に " linked: <名前>" を出す。リンク先は - # プラグインの skills/<名前> を指す。構造改善で本数・リンク先・出力が動かないことを守る。 - project = tmp_path / "project" - project.mkdir() - proc = run("--project", str(project), "--yes", home=tmp_path) - assert proc.returncode == 0, proc.stderr - - skills_dir = project / ".kiro" / "skills" - plugin_skills = INSTALLER.parents[1] / "skills" - manifest_skills = _kiro_manifest_skills() - expected_linked = manifest_skills - {"ndf-policies"} - - # 実際に張られた symlink とそのリンク先を確認する。 - linked = {p.name for p in skills_dir.iterdir() if p.is_symlink()} - assert linked == expected_linked - for name in expected_linked: - assert (skills_dir / name).resolve() == (plugin_skills / name).resolve() - - # ndf-policies は steering へ回すためリンクしない。 - assert not (skills_dir / "ndf-policies").is_symlink() - - # 出力の「linked」行と「Skills数」が本数と一致する。 - linked_lines = {line[len(" linked: ") :] for line in proc.stdout.splitlines() if line.startswith(" linked: ")} - assert linked_lines == expected_linked - assert f" Skills数: {len(expected_linked)} (シンボリックリンク: {skills_dir})" in proc.stdout - - -def test_reinstall_removes_only_managed_skill_links(tmp_path: Path) -> None: - # 現状固定: 掃除が消すのは現在の checkout(プラグインの skills/)配下を指す - # 既存リンクだけである。別の場所を指す利用者のリンクは残す。再インストールでも - # 掃除→再リンクで最終状態が manifest 掲載分と一致する。 - project = tmp_path / "project" - project.mkdir() - first = run("--project", str(project), "--yes", home=tmp_path) - assert first.returncode == 0, first.stderr - - skills_dir = project / ".kiro" / "skills" - plugin_skills = INSTALLER.parents[1] / "skills" - - # 利用者が別の場所を指して張ったリンク(掃除対象外)。 - foreign_target = tmp_path / "foreign_skill" - foreign_target.mkdir() - (skills_dir / "user-external").symlink_to(foreign_target) - - # 現在の checkout を指すが manifest に無い名前のリンク(掃除対象)。 - stale = skills_dir / "stale-managed" - stale.symlink_to(plugin_skills / "pr") - - second = run("--project", str(project), "--yes", home=tmp_path) - assert second.returncode == 0, second.stderr - - manifest_skills = _kiro_manifest_skills() - expected_managed = manifest_skills - {"ndf-policies"} - - # 現在の checkout 配下を指す managed リンクは manifest 掲載分だけが残る。 - assert not (skills_dir / "stale-managed").exists() - # 別の場所を指す利用者のリンクは残る。 - assert (skills_dir / "user-external").is_symlink() - assert (skills_dir / "user-external").resolve() == foreign_target.resolve() - - managed_now = { - p.name for p in skills_dir.iterdir() if p.is_symlink() and str(p.resolve()).startswith(str(plugin_skills.resolve()) + os.sep) - } - assert managed_now == expected_managed diff --git a/scripts/tests/test_kiro_mcp_installer_tools.py b/scripts/tests/test_kiro_mcp_installer_tools.py deleted file mode 100644 index 9efa8833c..000000000 --- a/scripts/tests/test_kiro_mcp_installer_tools.py +++ /dev/null @@ -1,71 +0,0 @@ -"""MCP プラグインの Kiro 用 installer が default.json に `tools` を持たせることを固定する(#1873)。 - -Kiro CLI はエージェント定義の `tools` に挙がったツールだけをモデルへ見せる。欄が無いと -MCP サーバーへの接続は成功しても、MCP のツールを呼べない。 - -見るのは 3 つである。**新しく作る default.json に `tools: ["*"]` が入ること**・ -**既存の default.json に `tools` が無ければ足すこと**・**利用者が書いた `tools` は -上書きしないこと**。installer は `scripts/build-runtime-plugins.sh` が全 MCP プラグインへ -同じ内容で書き出すため、全プラグインの installer を通す。 -""" - -from __future__ import annotations - -import json -import os -import subprocess -from pathlib import Path - -import pytest - -ROOT = Path(__file__).resolve().parents[2] -INSTALLERS = sorted(ROOT.glob("plugins/mcp/mcp-*/dev.kiro/install.sh")) - - -def install(installer: Path, project: Path, home: Path) -> dict: - env = {**os.environ, "HOME": str(home)} - subprocess.run( - ["bash", str(installer), "--project", str(project)], - check=True, - capture_output=True, - text=True, - env=env, - ) - return json.loads((project / ".kiro/agents/default.json").read_text(encoding="utf-8")) - - -def test_installers_exist() -> None: - assert INSTALLERS - - -@pytest.mark.parametrize("installer", INSTALLERS, ids=lambda p: p.parents[1].name) -def test_new_agent_file_allows_all_tools(tmp_path: Path, installer: Path) -> None: - project = tmp_path / "project" - project.mkdir() - - agent = install(installer, project, tmp_path) - - assert agent["tools"] == ["*"] - - -def test_existing_agent_file_without_tools_gets_all_tools(tmp_path: Path) -> None: - project = tmp_path / "project" - agent_file = project / ".kiro/agents/default.json" - agent_file.parent.mkdir(parents=True) - agent_file.write_text(json.dumps({"name": "default", "mine": 1}), encoding="utf-8") - - agent = install(INSTALLERS[0], project, tmp_path) - - assert agent["tools"] == ["*"] - assert agent["mine"] == 1 - - -def test_user_tools_are_kept(tmp_path: Path) -> None: - project = tmp_path / "project" - agent_file = project / ".kiro/agents/default.json" - agent_file.parent.mkdir(parents=True) - agent_file.write_text(json.dumps({"name": "default", "tools": ["fs_read"]}), encoding="utf-8") - - agent = install(INSTALLERS[0], project, tmp_path) - - assert agent["tools"] == ["fs_read"] diff --git a/scripts/tests/test_runtime_smoke_latest.py b/scripts/tests/test_runtime_smoke_latest.py index 0dbaaeb90..243781bd0 100644 --- a/scripts/tests/test_runtime_smoke_latest.py +++ b/scripts/tests/test_runtime_smoke_latest.py @@ -15,8 +15,6 @@ REPO = Path(__file__).resolve().parents[2] SCRIPT = REPO / "scripts" / "runtime-smoke-latest.py" -SHA256 = {"x86_64": "a" * 64, "aarch64": "b" * 64} -SHA512 = {"linux_amd64": "c" * 128, "linux_arm64": "d" * 128} @pytest.fixture @@ -28,34 +26,10 @@ def mod(monkeypatch): return m -def sources(claude="2.1.300", codex="0.163.0", kiro="2.32.0", agy="1.3.5", agy_arm=None): - agy_arm = agy_arm or agy - - def kiro_pkg(arch, ext="tar.gz", sha=None): - return {"download": f"{kiro}/kirocli-{arch}-linux-musl.{ext}", "sha256": sha or SHA256[arch]} - +def sources(claude="2.1.300", codex="0.163.0"): return { "https://registry.npmjs.org/-/package/@anthropic-ai/claude-code/dist-tags": {"latest": claude, "next": "9.9.9"}, "https://registry.npmjs.org/-/package/@openai/codex/dist-tags": {"latest": codex, "alpha": "0.200.0-alpha.1"}, - "https://prod.download.cli.kiro.dev/stable/latest/manifest.json": { - "version": kiro, - "packages": [ - kiro_pkg("x86_64", "tar.xz", "e" * 64), - kiro_pkg("x86_64"), - kiro_pkg("aarch64"), - {"download": f"{kiro}/kirocli-x86_64-linux.tar.gz", "sha256": "f" * 64}, - ], - }, - "https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests/linux_amd64.json": { - "version": agy, - "url": f"https://example.invalid/antigravity-cli/{agy}-1/linux-x64/cli_linux_x64.tar.gz", - "sha512": SHA512["linux_amd64"], - }, - "https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests/linux_arm64.json": { - "version": agy_arm, - "url": f"https://example.invalid/antigravity-cli/{agy_arm}-1/linux-arm/cli_linux_arm64.tar.gz", - "sha512": SHA512["linux_arm64"], - }, } @@ -80,14 +54,6 @@ def test_resolve_all_emits_versions_list_shape(mod, monkeypatch, capsys): assert pairs == { "CLAUDE_CODE_VERSION": "2.1.300", "CODEX_VERSION": "0.163.0", - "KIRO_CLI_VERSION": "2.32.0", - "KIRO_CLI_SHA256_X86_64": SHA256["x86_64"], - "KIRO_CLI_SHA256_AARCH64": SHA256["aarch64"], - "AGY_VERSION": "1.3.5", - "AGY_URL_X86_64": "https://example.invalid/antigravity-cli/1.3.5-1/linux-x64/cli_linux_x64.tar.gz", - "AGY_URL_AARCH64": "https://example.invalid/antigravity-cli/1.3.5-1/linux-arm/cli_linux_arm64.tar.gz", - "AGY_SHA512_X86_64": SHA512["linux_amd64"], - "AGY_SHA512_AARCH64": SHA512["linux_arm64"], } # 一覧と同じキーの集合を出す(版を上げる手順で写せる) listed = REPO / "tests" / "runtime-smoke" / "cli-versions.env" @@ -105,11 +71,9 @@ def test_resolve_one_runtime_emits_only_its_keys(mod, monkeypatch, capsys): "runtime, table", [ ("claude", sources(claude="latest")), - ("kiro", {**sources(), "https://prod.download.cli.kiro.dev/stable/latest/manifest.json": {"version": "2.32.0", "packages": []}}), - ("agy", sources(agy_arm="1.3.6")), - ("agy", {k: v for k, v in sources().items() if "linux_arm64" not in k}), + ("codex", {k: v for k, v in sources().items() if "@openai/codex" not in k}), ], - ids=["bad-version", "kiro-no-musl", "agy-arch-mismatch", "unreachable"], + ids=["bad-version", "unreachable"], ) def test_resolve_fails_with_source_url(mod, monkeypatch, capsys, runtime, table): use_sources(mod, monkeypatch, table) @@ -146,25 +110,25 @@ def test_report_creates_issue_with_runtime_versions_and_run_url(mod, monkeypatch gh = FakeGh([{"number": 5, "title": "最新の版のスモークが落ちた: codex"}]) monkeypatch.setattr(mod, "run_gh", gh) versions = tmp_path / "v.env" - versions.write_text("AGY_VERSION=1.3.5\n", encoding="utf-8") - assert run(mod, "report", "--runtime", "agy", "--run-url", RUN_URL, "--versions", str(versions), "--repo", "o/r") == 0 + versions.write_text("CLAUDE_CODE_VERSION=2.1.300\n", encoding="utf-8") + assert run(mod, "report", "--runtime", "claude", "--run-url", RUN_URL, "--versions", str(versions), "--repo", "o/r") == 0 assert gh.kinds() == [["issue", "list"], ["issue", "create"]] args, body = gh.calls[1] - assert args[args.index("--title") + 1] == "最新の版のスモークが落ちた: agy" + assert args[args.index("--title") + 1] == "最新の版のスモークが落ちた: claude" assert args[args.index("--repo") + 1] == "o/r" - assert "agy" in body and "AGY_VERSION=1.3.5" in body and RUN_URL in body + assert "claude" in body and "CLAUDE_CODE_VERSION=2.1.300" in body and RUN_URL in body assert not any(word in body for word in ("Closes", "Fixes", "Resolves")) def test_report_comments_on_open_issue_instead_of_creating(mod, monkeypatch, tmp_path): gh = FakeGh( [ - {"number": 7, "title": "最新の版のスモークが落ちた: agy"}, - {"number": 8, "title": "最新の版のスモークが落ちた: agy (old)"}, + {"number": 7, "title": "最新の版のスモークが落ちた: codex"}, + {"number": 8, "title": "最新の版のスモークが落ちた: codex (old)"}, ] ) monkeypatch.setattr(mod, "run_gh", gh) - assert run(mod, "report", "--runtime", "agy", "--run-url", RUN_URL) == 0 + assert run(mod, "report", "--runtime", "codex", "--run-url", RUN_URL) == 0 assert gh.kinds() == [["issue", "list"], ["issue", "comment"]] args, body = gh.calls[1] assert args[2] == "7" @@ -173,5 +137,5 @@ def test_report_comments_on_open_issue_instead_of_creating(mod, monkeypatch, tmp def test_report_fails_when_gh_fails(mod, monkeypatch, capsys): monkeypatch.setattr(mod, "run_gh", FakeGh([], fail=True)) - assert run(mod, "report", "--runtime", "kiro", "--run-url", RUN_URL) == 1 + assert run(mod, "report", "--runtime", "codex", "--run-url", RUN_URL) == 1 assert "gh" in capsys.readouterr().err diff --git a/scripts/tests/test_runtime_smoke_versions.py b/scripts/tests/test_runtime_smoke_versions.py index 87c6874d6..f572a3d85 100644 --- a/scripts/tests/test_runtime_smoke_versions.py +++ b/scripts/tests/test_runtime_smoke_versions.py @@ -1,6 +1,6 @@ """スモークが入れる CLI の版の固定と、最新の版のスモークの workflow の形(#1982 の I2・I3・I5)。 -- I2: CLI の版の一覧(`tests/runtime-smoke/cli-versions.env`)のキーの集合が、4 つの Containerfile が +- I2: CLI の版の一覧(`tests/runtime-smoke/cli-versions.env`)のキーの集合が、2 つの Containerfile が 宣言する `ARG` の名前の和集合と一致し、各 `ARG` は接頭辞のランタイムの Containerfile にだけ現れ、 値は空でなく、`ARG` は既定値を持たない - I3: 配布の経路の取得先は版数を含む取得先だけ(`latest`・公式のインストーラを使わない) @@ -20,7 +20,7 @@ SMOKE = REPO / "tests" / "runtime-smoke" VERSIONS = SMOKE / "cli-versions.env" LATEST_WORKFLOW = REPO / ".github" / "workflows" / "runtime-plugin-smoke-latest.yml" -PREFIXES = {"claude": "CLAUDE_CODE_", "codex": "CODEX_", "kiro": "KIRO_CLI_", "agy": "AGY_"} +PREFIXES = {"claude": "CLAUDE_CODE_", "codex": "CODEX_"} ARG_RE = re.compile(r"^ARG\s+([A-Za-z_][A-Za-z0-9_]*)(=.*)?\s*$", re.MULTILINE) @@ -64,10 +64,8 @@ def test_versions_match_containerfile_args(): def test_versions_are_concrete(): versions = read_versions(VERSIONS) - for key in ("CLAUDE_CODE_VERSION", "CODEX_VERSION", "KIRO_CLI_VERSION", "AGY_VERSION"): + for key in ("CLAUDE_CODE_VERSION", "CODEX_VERSION"): assert re.fullmatch(r"[0-9]+(\.[0-9]+)+", versions[key]), f"{key} が版数でない: {versions[key]}" - for key in ("AGY_URL_X86_64", "AGY_URL_AARCH64"): - assert f"/{versions['AGY_VERSION']}-" in versions[key], f"{key} が AGY_VERSION の版を含まない" @pytest.mark.parametrize("runtime", sorted(PREFIXES)) diff --git a/scripts/tests/test_skill_frontmatter_listing.py b/scripts/tests/test_skill_frontmatter_listing.py index 76865bc73..60a12ddd5 100644 --- a/scripts/tests/test_skill_frontmatter_listing.py +++ b/scripts/tests/test_skill_frontmatter_listing.py @@ -1,7 +1,7 @@ """check-skill-frontmatter.py の measure_aggregate が数える初期一覧の文字数を固定する。 配布先ごとの数え方(Claude Code は description と when_to_use を連結して切り詰め、 -パスを載せない。Codex / Kiro / agy は description だけでパスを載せる)と、 +パスを載せない。Codex は description だけでパスを載せる)と、 manifest に無い Skill を数えないこと、frontmatter 総量を固定する。 """ @@ -37,7 +37,7 @@ def _skill(dir_name: str, block: str, **fm) -> dict: def test_counts_name_description_and_path_per_runtime(tmp_path): m = _load() - skills_dir = _tree(tmp_path, {"claude": ["alpha"], "codex": ["alpha"], "kiro": ["alpha"], "agy": ["alpha"]}) + skills_dir = _tree(tmp_path, {"claude": ["alpha"], "codex": ["alpha"]}) skills = [_skill("alpha", "x" * 10, name="alpha", description="desc", when_to_use="wtu")] rel = len("skills/alpha/SKILL.md") got = m.measure_aggregate(skills, skills_dir) @@ -45,8 +45,6 @@ def test_counts_name_description_and_path_per_runtime(tmp_path): "listings": { "claude": len("alpha") + len("descwtu"), "codex": len("alpha") + len("desc") + rel, - "kiro": len("alpha") + len("desc") + rel, - "agy": len("alpha") + len("desc") + rel, }, "frontmatter_total": 10, } @@ -83,7 +81,7 @@ def test_missing_frontmatter_falls_back_to_dir_name(tmp_path): def test_manifest_comments_are_ignored(tmp_path): m = _load() skills_dir = _tree(tmp_path, {}) - (skills_dir.parent / "manifests/kiro-skills.txt").write_text("# header\nalpha # note\n\n", encoding="utf-8") + (skills_dir.parent / "manifests/codex-skills.txt").write_text("# header\nalpha # note\n\n", encoding="utf-8") skills = [_skill("alpha", "", name="alpha", description="dd")] got = m.measure_aggregate(skills, skills_dir) - assert got["listings"] == {"kiro": len("alpha") + 2 + len("skills/alpha/SKILL.md")} + assert got["listings"] == {"codex": len("alpha") + 2 + len("skills/alpha/SKILL.md")} diff --git a/scripts/tests/test_validate_agy_manifest.py b/scripts/tests/test_validate_agy_manifest.py deleted file mode 100644 index 33965d8fc..000000000 --- a/scripts/tests/test_validate_agy_manifest.py +++ /dev/null @@ -1,98 +0,0 @@ -"""agy の定義ファイルをチェックの対象へ入れる(#215 の受け入れ条件 A9)。 - -版数を持つ箇所は 13 から 15 へ増える(`dev.agy/plugin.json` の `version` と `description`)。 -agy には取得元の登録が無く、`agy plugin list` も版数を出さないため、利用者が版を判断できる -手がかりは clone した中身の版数だけである。古いまま残らないよう、Claude 版 `plugin.json` を -基準に突き合わせる。 - -**記載を消してチェックを通せない形にする。** Skill 数を読み取れないこと自体も失敗として扱う。 -""" - -from __future__ import annotations - -import json -from pathlib import Path - -from validate_manifests_helpers import ( - MANIFESTS, - VERSION, - build_tree, - description, - output_of, - run_check, -) - -AGY_PLUGIN_JSON = "plugins/ndf/dev.agy/plugin.json" - - -def agy_manifest(root: Path) -> Path: - return root / AGY_PLUGIN_JSON - - -def test_matching_agy_manifest_passes(tmp_path: Path) -> None: - """版数と Skill 数が揃っていれば通る。""" - result = run_check(build_tree(tmp_path), tmp_path) - assert result.returncode == 0, output_of(result) - - -def test_old_version_fails(tmp_path: Path) -> None: - """`version` が Claude 版と食い違うと落ちる。""" - root = build_tree(tmp_path) - path = agy_manifest(root) - payload = json.loads(path.read_text(encoding="utf-8")) - payload["version"] = "9.2.0" - path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") - - result = run_check(root, tmp_path) - assert result.returncode == 1 - assert f"{AGY_PLUGIN_JSON} の version が claude 版と食い違う" in output_of(result) - - -def test_old_version_in_description_fails(tmp_path: Path) -> None: - """`description` の版数だけが古い場合も落ちる。""" - root = build_tree(tmp_path) - path = agy_manifest(root) - payload = json.loads(path.read_text(encoding="utf-8")) - payload["description"] = description("9.2.0", len(MANIFESTS["agy"])) - path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") - - result = run_check(root, tmp_path) - assert result.returncode == 1 - assert f"{AGY_PLUGIN_JSON} の description の版数が古い" in output_of(result) - - -def test_wrong_skill_count_fails(tmp_path: Path) -> None: - """`description` の Skill 数は `agy-skills.txt` の行数と突き合わせる。""" - root = build_tree(tmp_path) - path = agy_manifest(root) - payload = json.loads(path.read_text(encoding="utf-8")) - payload["description"] = description(VERSION, len(MANIFESTS["agy"]) + 1) - path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") - - result = run_check(root, tmp_path) - assert result.returncode == 1 - output = output_of(result) - assert f"{AGY_PLUGIN_JSON} の description の Skill 数が食い違う" in output - assert "agy-skills.txt" in output - - -def test_removed_skill_count_fails(tmp_path: Path) -> None: - """Skill 数の記載を消してもチェックは通らない。""" - root = build_tree(tmp_path) - path = agy_manifest(root) - payload = json.loads(path.read_text(encoding="utf-8")) - payload["description"] = f"Fixture plugin (v{VERSION}): nothing countable here." - path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") - - result = run_check(root, tmp_path) - assert result.returncode == 1 - assert f"{AGY_PLUGIN_JSON} の description から Skill 数を読み取れない" in output_of(result) - - -def test_family_without_dev_agy_is_skipped(tmp_path: Path) -> None: - """`dev.agy/` を持たない family は素通りする。agy へ配らないプラグインを巻き込まない。""" - root = build_tree(tmp_path) - agy_manifest(root).unlink() - - result = run_check(root, tmp_path) - assert result.returncode == 0, output_of(result) diff --git a/scripts/tests/test_validate_manifests_version.py b/scripts/tests/test_validate_manifests_version.py index f7b383082..72759ae54 100644 --- a/scripts/tests/test_validate_manifests_version.py +++ b/scripts/tests/test_validate_manifests_version.py @@ -3,16 +3,11 @@ 開発版の版数には接尾辞(`-dev.<連番>` / `-rc.<連番>`)が付く(`docs/versioning-and-distribution.md` の「版の付け方と開発版の配布」)。数字 3 つの直後に閉じ括弧を求める書式では `(v9.7.0-dev.1)` に一致せず、開発版を出すたびにこのチェックで止まっていた(#254)。 - -あわせて、agy の定義ファイル(`plugins/ndf/dev.agy/plugin.json`)の版数が -Claude 版と突き合わされることを固定する(#215 の受け入れ条件 A11)。 """ from __future__ import annotations -import json from pathlib import Path -from typing import Any, Callable import pytest @@ -50,7 +45,6 @@ def test_prerelease_version_dropped_in_description_fails(tmp_path: Path) -> None for label in ( "plugins/ndf/.claude-plugin/plugin.json", "plugins/ndf/.codex-plugin/plugin.json", - "plugins/ndf/dev.agy/plugin.json", ".claude-plugin/marketplace.json の ndf", ): assert f"{label} の description の版数が古い" in output, output @@ -96,95 +90,3 @@ def test_skill_count_is_read_beside_the_version(tmp_path: Path, version: str) -> "plugins/ndf/.claude-plugin/plugin.json の description の Skill 数が食い違う" "(description: 4 / claude-skills.txt: 5)" in output_of(result) ) - - -# --- agy の定義ファイルの版数(#215 の受け入れ条件 A11) --- -# -# `plugins/ndf/dev.agy/plugin.json` は `version` と `description` の 2 箇所に版数を持つ。 -# どちらも Claude 版の `plugin.json` と突き合わせる。**片方だけ直した状態で配れないこと**を、 -# 古い値と記載の欠落の両方で確かめる。 - -AGY_MANIFEST = "plugins/ndf/dev.agy/plugin.json" - - -def rewrite_agy_manifest(root: Path, mutate: Callable[[dict[str, Any]], None]) -> None: - """agy の定義ファイルだけを崩す。他のランタイムの定義には触らない。""" - path = root / AGY_MANIFEST - manifest = json.loads(path.read_text(encoding="utf-8")) - mutate(manifest) - path.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") - - -def test_agy_consistent_manifest_passes(tmp_path: Path) -> None: - """崩していない木では、agy の定義ファイルも含めて通る。""" - root = build_tree(tmp_path) - assert (root / AGY_MANIFEST).is_file() - result = run_check(root, tmp_path) - assert result.returncode == 0, output_of(result) - - -def test_agy_stale_version_fails(tmp_path: Path) -> None: - """`version` が前の版のままなら落ちる。""" - root = build_tree(tmp_path) - rewrite_agy_manifest(root, lambda m: m.update(version="9.2.1")) - - result = run_check(root, tmp_path) - - assert result.returncode == 1 - out = output_of(result) - assert f"{AGY_MANIFEST} の version が claude 版と食い違う" in out, out - assert "9.2.1" in out and helpers.VERSION in out, out - - -def test_agy_missing_version_fails(tmp_path: Path) -> None: - """`version` の記載を消しても通らない。読み取れないこと自体を失敗として扱う。""" - root = build_tree(tmp_path) - rewrite_agy_manifest(root, lambda m: m.pop("version")) - - result = run_check(root, tmp_path) - - assert result.returncode == 1 - assert f"{AGY_MANIFEST} の version が claude 版と食い違う" in output_of(result) - - -def test_agy_stale_version_in_description_fails(tmp_path: Path) -> None: - """`description` の版数だけが前の版でも落ちる。""" - root = build_tree(tmp_path) - rewrite_agy_manifest( - root, - lambda m: m.update(description=m["description"].replace(f"(v{helpers.VERSION})", "(v9.2.1)")), - ) - - result = run_check(root, tmp_path) - - assert result.returncode == 1 - out = output_of(result) - assert f"{AGY_MANIFEST} の description の版数が古い" in out, out - - -def test_agy_missing_version_in_description_fails(tmp_path: Path) -> None: - """`description` から版数を消しても通らない。""" - root = build_tree(tmp_path) - rewrite_agy_manifest( - root, - lambda m: m.update(description=m["description"].replace(f"(v{helpers.VERSION})", "")), - ) - - result = run_check(root, tmp_path) - - assert result.returncode == 1 - assert f"{AGY_MANIFEST} の description に `(vX.Y.Z)` 形式の版数がない" in output_of(result) - - -def test_agy_skill_count_is_read_from_its_own_manifest(tmp_path: Path) -> None: - """Skill 数の突き合わせ先は `agy-skills.txt` である。他のランタイムの数と取り違えない。""" - root = build_tree(tmp_path) - rewrite_agy_manifest( - root, - lambda m: m.update(description=m["description"].replace("2 focused skills", "3 focused skills")), - ) - - result = run_check(root, tmp_path) - - assert result.returncode == 1 - assert f"{AGY_MANIFEST} の description の Skill 数が食い違う(description: 3 / agy-skills.txt: 2)" in output_of(result) diff --git a/scripts/tests/test_validate_mcp_codex_servers.py b/scripts/tests/test_validate_mcp_codex_servers.py index f1cb6c04d..f15c5ff35 100644 --- a/scripts/tests/test_validate_mcp_codex_servers.py +++ b/scripts/tests/test_validate_mcp_codex_servers.py @@ -16,13 +16,12 @@ def _mcp_plugin(root: Path, declared, files=(".mcp.json",)) -> None: plugin = root / "plugins/mcp/mcp-x" - for rel in (".claude-plugin", ".codex-plugin", "dev.kiro"): + for rel in (".claude-plugin", ".codex-plugin"): (plugin / rel).mkdir(parents=True) for name in files: (plugin / name).write_text('{"mcpServers": {}}\n', encoding="utf-8") (plugin / ".claude-plugin/plugin.json").write_text('{"name": "mcp-x"}\n', encoding="utf-8") (plugin / ".codex-plugin/plugin.json").write_text(json.dumps({"name": "mcp-x", "mcpServers": declared}), encoding="utf-8") - (plugin / "dev.kiro/install.sh").write_text("#!/bin/sh\n", encoding="utf-8") @pytest.mark.parametrize( diff --git a/scripts/tests/validate_manifests_helpers.py b/scripts/tests/validate_manifests_helpers.py index 5181936ff..5ee312ff0 100644 --- a/scripts/tests/validate_manifests_helpers.py +++ b/scripts/tests/validate_manifests_helpers.py @@ -29,7 +29,6 @@ MANIFESTS = { "claude": ["alpha", "bravo", "charlie", "delta", "echo"], "codex": ["alpha", "bravo", "charlie"], - "agy": ["alpha", "bravo"], } @@ -79,23 +78,6 @@ def build_tree(base: Path, version: str = VERSION, described: str | None = None) encoding="utf-8", ) - # agy 向けの定義はクライアント拡張ディレクトリに置く。Skill の絞り込みを持たないため、 - # 突き合わせ先は agy-skills.txt の行数である。 - (ndf / "dev.agy").mkdir(parents=True) - (ndf / "dev.agy/plugin.json").write_text( - json.dumps( - { - "name": FAMILY, - "version": version, - "description": description(described, len(MANIFESTS["agy"])), - }, - ensure_ascii=False, - indent=2, - ) - + "\n", - encoding="utf-8", - ) - (root / ".claude-plugin").mkdir(parents=True) (root / ".claude-plugin/marketplace.json").write_text( json.dumps( diff --git a/scripts/validate-runtime-plugins.sh b/scripts/validate-runtime-plugins.sh index 74b5ff6de..e852f77de 100755 --- a/scripts/validate-runtime-plugins.sh +++ b/scripts/validate-runtime-plugins.sh @@ -12,7 +12,7 @@ run bash "$ROOT_DIR/scripts/build-runtime-plugins.sh" --check # Skill を配る plugin family を manifests/ の有無から検出する(plugins/mcp/* は別系統)。 # 固定リストを置かないのは、family を足したときにチェック対象から漏れる経路を作らないためである。 -# 後段の静的解析(claude plugin validate / Kiro installer の dry-run)もこの一覧で回す。 +# 後段の静的解析(claude plugin validate)もこの一覧で回す。 FAMILIES=() for plugin_dir in "$ROOT_DIR"/plugins/*; do [ -d "$plugin_dir/manifests" ] || continue @@ -36,14 +36,6 @@ for family in "${FAMILIES[@]}"; do run python3 -m json.tool "$root_manifest" >/dev/null done -for family in "${FAMILIES[@]}"; do - for agy_manifest in "$ROOT_DIR/plugins/$family/dev.agy/plugin.json" \ - "$ROOT_DIR/plugins/$family/dev.agy/hooks.json"; do - [ -f "$agy_manifest" ] || continue - run python3 -m json.tool "$agy_manifest" >/dev/null - done -done - while IFS= read -r mcp_config; do run python3 -m json.tool "$mcp_config" >/dev/null done < <(find "$ROOT_DIR/plugins/mcp" -maxdepth 2 -name .mcp.json | sort) @@ -60,32 +52,6 @@ else echo "==> claude CLI not found; skipped claude plugin validate" fi -# agy はクライアント拡張ディレクトリを 1 つのプラグインとして読む。CLI が無い環境では -# 読み飛ばす(claude plugin validate と同じ扱い)。 -if command -v agy >/dev/null 2>&1; then - for family in "${FAMILIES[@]}"; do - [ -f "$ROOT_DIR/plugins/$family/dev.agy/plugin.json" ] || continue - run agy plugin validate "$ROOT_DIR/plugins/$family/dev.agy" - done -else - echo "==> agy CLI not found; skipped agy plugin validate" -fi - -# Kiro の installer は Agent Plugins 仕様 §8.2 のクライアント拡張ディレクトリに置く。 -for family in "${FAMILIES[@]}"; do - installer="$ROOT_DIR/plugins/$family/dev.kiro/install.sh" - [ -f "$installer" ] || continue - run bash "$installer" --dry-run >/dev/null -done - -# --with-codex を持つのは NDF の installer だけ(Codex 向け Skill も併せて配置する経路)。 -# family 共通の引数ではないため、ここだけは対象を明示してチェックする。 -run bash "$ROOT_DIR/plugins/ndf/dev.kiro/install.sh" --dry-run --with-codex >/dev/null - -while IFS= read -r installer; do - run bash "$installer" --dry-run >/dev/null -done < <(find "$ROOT_DIR/plugins/mcp" -path '*/dev.kiro/install.sh' | sort) - run python3 "$ROOT_DIR/scripts/check-markdown-links.py" --root "$ROOT_DIR" # 説明文書(README.md / plugins/ndf/README.md)に書かれた Skill 数と版数を、 diff --git a/tests/runtime-smoke/Containerfile.agy b/tests/runtime-smoke/Containerfile.agy deleted file mode 100644 index f383e4deb..000000000 --- a/tests/runtime-smoke/Containerfile.agy +++ /dev/null @@ -1,30 +0,0 @@ -FROM ai-plugins-runtime-smoke-base - -# 版・取得先・checksum は tests/runtime-smoke/cli-versions.env に書き、scripts/runtime-smoke-test.sh が -# build-arg で渡す。ARG に既定値を持たせない(版の出所を一覧の 1 つにする)。 -# 公式のインストーラは最新の manifest だけを読むため使わず、manifest に載る版つきの url を取り、 -# 一覧の sha512 で確かめて入れる。取得・checksum・版の照合のどれかが落ちたら像の作成を落とす。 -ARG AGY_VERSION -ARG AGY_URL_X86_64 -ARG AGY_URL_AARCH64 -ARG AGY_SHA512_X86_64 -ARG AGY_SHA512_AARCH64 - -RUN set -eu; \ - test -n "$AGY_VERSION"; \ - arch="$(uname -m)"; \ - case "$arch" in \ - x86_64) url="$AGY_URL_X86_64"; sha512="$AGY_SHA512_X86_64" ;; \ - aarch64) url="$AGY_URL_AARCH64"; sha512="$AGY_SHA512_AARCH64" ;; \ - *) echo "対応していない arch: $arch" >&2; exit 1 ;; \ - esac; \ - test -n "$url"; \ - test -n "$sha512"; \ - work="$(mktemp -d)"; \ - curl -fsSL -o "$work/agy.tar.gz" "$url"; \ - echo "$sha512 $work/agy.tar.gz" | sha512sum -c -; \ - tar -xzf "$work/agy.tar.gz" -C "$work" antigravity; \ - install -m 755 "$work/antigravity" /usr/local/bin/agy; \ - rm -rf "$work"; \ - actual="$(agy --version | grep -oE '[0-9]+(\.[0-9]+)+' | head -n 1)"; \ - [ "$actual" = "$AGY_VERSION" ] || { echo "agy の版が一覧と違う: $actual != $AGY_VERSION" >&2; exit 1; } diff --git a/tests/runtime-smoke/Containerfile.kiro b/tests/runtime-smoke/Containerfile.kiro deleted file mode 100644 index 27f4aea5f..000000000 --- a/tests/runtime-smoke/Containerfile.kiro +++ /dev/null @@ -1,28 +0,0 @@ -FROM ai-plugins-runtime-smoke-base - -# 版と checksum は tests/runtime-smoke/cli-versions.env に書き、scripts/runtime-smoke-test.sh が -# build-arg で渡す。ARG に既定値を持たせない(版の出所を一覧の 1 つにする)。 -# 公式のインストーラは版を受けず、bookworm の glibc では glibc 版が動かないため、版つきの musl 版の -# tarball を取り、一覧の sha256 で確かめて入れる。取得・checksum・版の照合のどれかが落ちたら像の作成を落とす。 -ARG KIRO_CLI_VERSION -ARG KIRO_CLI_SHA256_X86_64 -ARG KIRO_CLI_SHA256_AARCH64 - -RUN set -eu; \ - test -n "$KIRO_CLI_VERSION"; \ - arch="$(uname -m)"; \ - case "$arch" in \ - x86_64) sha256="$KIRO_CLI_SHA256_X86_64" ;; \ - aarch64) sha256="$KIRO_CLI_SHA256_AARCH64" ;; \ - *) echo "対応していない arch: $arch" >&2; exit 1 ;; \ - esac; \ - test -n "$sha256"; \ - work="$(mktemp -d)"; \ - curl -fsSL -o "$work/kirocli.tar.gz" \ - "https://prod.download.cli.kiro.dev/stable/${KIRO_CLI_VERSION}/kirocli-${arch}-linux-musl.tar.gz"; \ - echo "$sha256 $work/kirocli.tar.gz" | sha256sum -c -; \ - tar -xzf "$work/kirocli.tar.gz" -C "$work"; \ - install -m 755 "$work/kirocli/bin/kiro-cli" "$work/kirocli/bin/kiro-cli-chat" "$work/kirocli/bin/kiro-cli-term" /usr/local/bin/; \ - rm -rf "$work"; \ - actual="$(kiro-cli --version | grep -oE '[0-9]+(\.[0-9]+)+' | head -n 1)"; \ - [ "$actual" = "$KIRO_CLI_VERSION" ] || { echo "kiro-cli の版が一覧と違う: $actual != $KIRO_CLI_VERSION" >&2; exit 1; } diff --git a/tests/runtime-smoke/adapters/agy.sh b/tests/runtime-smoke/adapters/agy.sh deleted file mode 100755 index a17de80f7..000000000 --- a/tests/runtime-smoke/adapters/agy.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -source /workspace/ai-plugins/tests/runtime-smoke/lib/common.sh - -cd "$PROJECT_DIR" -record_version agy --version - -run_step "agy validate ndf" agy plugin validate "$REPO_ROOT/plugins/ndf/dev.agy" -run_step "agy install ndf" agy plugin install "$REPO_ROOT/plugins/ndf/dev.agy" -# 2 回目で壊れないことを見る。agy には入れ替えの操作が無く、利用者は install を繰り返す。 -run_step "agy install ndf idempotent" agy plugin install "$REPO_ROOT/plugins/ndf/dev.agy" -run_step "agy plugin list" agy plugin list - -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-plugin-files.sh" agy -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-authenticated-smoke.sh" agy -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-no-host-contamination.sh" agy -write_junit agy diff --git a/tests/runtime-smoke/adapters/kiro.sh b/tests/runtime-smoke/adapters/kiro.sh deleted file mode 100755 index 88f7ce516..000000000 --- a/tests/runtime-smoke/adapters/kiro.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -source /workspace/ai-plugins/tests/runtime-smoke/lib/common.sh - -cd "$PROJECT_DIR" -record_version kiro-cli --version - -run_step "kiro install ndf" bash "$REPO_ROOT/plugins/ndf/dev.kiro/install.sh" --project "$PROJECT_DIR" --with-slack -run_step "kiro install ndf idempotent" bash "$REPO_ROOT/plugins/ndf/dev.kiro/install.sh" --project "$PROJECT_DIR" --with-slack -run_step "kiro install playwright-kit" bash "$REPO_ROOT/plugins/playwright-kit/dev.kiro/install.sh" --project "$PROJECT_DIR" -run_step "kiro install playwright-kit idempotent" bash "$REPO_ROOT/plugins/playwright-kit/dev.kiro/install.sh" --project "$PROJECT_DIR" -run_step "kiro install mcp-bigquery" bash "$REPO_ROOT/plugins/mcp/mcp-bigquery/dev.kiro/install.sh" --project "$PROJECT_DIR" -run_step "kiro install mcp-bigquery idempotent" bash "$REPO_ROOT/plugins/mcp/mcp-bigquery/dev.kiro/install.sh" --project "$PROJECT_DIR" - -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-plugin-files.sh" kiro -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-mcp-config.sh" kiro "$PROJECT_DIR/.mcp.json" -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-hook-fixtures.sh" kiro -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-kiro-agent.sh" workspace - -run_step "kiro install ndf global" bash "$REPO_ROOT/plugins/ndf/dev.kiro/install.sh" --scope global --with-slack -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-kiro-agent.sh" global -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-authenticated-smoke.sh" kiro -"$REPO_ROOT/tests/runtime-smoke/assertions/assert-no-host-contamination.sh" kiro -write_junit kiro diff --git a/tests/runtime-smoke/assertions/assert-authenticated-smoke.sh b/tests/runtime-smoke/assertions/assert-authenticated-smoke.sh index cb76944d3..f7e2a1405 100755 --- a/tests/runtime-smoke/assertions/assert-authenticated-smoke.sh +++ b/tests/runtime-smoke/assertions/assert-authenticated-smoke.sh @@ -66,24 +66,6 @@ case "$runtime" in fi run_bigquery_secret_check ;; - kiro) - if command -v kiro-cli >/dev/null 2>&1 && [ -n "${ANTHROPIC_API_KEY:-}${OPENAI_API_KEY:-}" ]; then - timeout 30s kiro-cli doctor >> "$log" 2>&1 || true - auth_ran=true - fi - run_bigquery_secret_check - ;; - agy) - # agy の認証は CLI が持つ。API キーの環境変数を取らないため、`agy models` の成否で - # 認証済みかを判定する。Skill 一覧の取得は認証を要するので、ここへ置く。 - if command -v agy >/dev/null 2>&1 && timeout 60s agy models >> "$log" 2>&1; then - timeout 120s agy --output-format text --dangerously-skip-permissions \ - -p="読み込めている Skill の名前を、1 行に 1 つだけ列挙して。説明は書かないで。" \ - >> "$log" 2>&1 || true - auth_ran=true - fi - run_bigquery_secret_check - ;; *) echo "unknown runtime: $runtime" >&2; exit 2 ;; esac diff --git a/tests/runtime-smoke/assertions/assert-hook-fixtures.sh b/tests/runtime-smoke/assertions/assert-hook-fixtures.sh index c45931069..3dda52be8 100755 --- a/tests/runtime-smoke/assertions/assert-hook-fixtures.sh +++ b/tests/runtime-smoke/assertions/assert-hook-fixtures.sh @@ -17,24 +17,5 @@ case "$runtime" in PLUGIN_ROOT="$REPO_ROOT/plugins/ndf" \ python3 "$REPO_ROOT/plugins/ndf/scripts/wait-notify.py" --runtime codex < "$REPO_ROOT/tests/runtime-smoke/fixtures/hook-stop.json" >/dev/null ;; - kiro) - test -f "$PROJECT_DIR/.kiro/agents/ndf.json" - jq -e '.hooks.agentSpawn[0].command' "$PROJECT_DIR/.kiro/agents/ndf.json" >/dev/null - stop_command="$(jq -r '.hooks.stop[0].command // empty' "$PROJECT_DIR/.kiro/agents/ndf.json")" - test -n "$stop_command" - stop_script="$(python3 - "$stop_command" <<'PY' -import shlex -import sys - -parts = shlex.split(sys.argv[1]) -if len(parts) < 4 or parts[0] != "python3" or parts[2:4] != ["--runtime", "kiro"]: - raise SystemExit(1) -print(parts[1]) -PY -)" - test -f "$stop_script" - PLUGIN_ROOT="$REPO_ROOT/plugins/ndf" \ - python3 "$stop_script" --runtime kiro < "$REPO_ROOT/tests/runtime-smoke/fixtures/hook-stop.json" >/dev/null - ;; *) echo "unknown runtime: $runtime" >&2; exit 2 ;; esac diff --git a/tests/runtime-smoke/assertions/assert-kiro-agent.sh b/tests/runtime-smoke/assertions/assert-kiro-agent.sh deleted file mode 100755 index 3a4c8e7de..000000000 --- a/tests/runtime-smoke/assertions/assert-kiro-agent.sh +++ /dev/null @@ -1,390 +0,0 @@ -#!/usr/bin/env bash -# Kiro の ndf エージェントが「選択できる状態」で導入されたことをチェックする。 -# 旧実装は .kiro/agents/default.json を生成していたが、Kiro の既定は組み込みの -# kiro_default であり、生成したエージェントは選択されないままだった。 -# -# Usage: assert-kiro-agent.sh [workspace|global] -set -euo pipefail - -scope="${1:-workspace}" -: "${REPO_ROOT:=/workspace/ai-plugins}" -: "${PROJECT_DIR:=/tmp/runtime-project}" -: "${ARTIFACT_DIR:=/tmp/runtime-artifacts}" -: "${HOME:=/tmp/runtime-home}" - -AGENT_NAME="ndf" -# INSTALL_ARGS: この scope の導入先を再現する installer 引数 -case "$scope" in - workspace) - KIRO_DIR="$PROJECT_DIR/.kiro" - INSTALL_ARGS=(--project "$PROJECT_DIR") - ;; - global) - KIRO_DIR="$HOME/.kiro" - INSTALL_ARGS=(--scope global) - ;; - *) echo "unknown scope: $scope" >&2; exit 2 ;; -esac - -AGENT_FILE="$KIRO_DIR/agents/$AGENT_NAME.json" -STEERING_FILE="$KIRO_DIR/steering/ndf-policies.md" -LOG="$ARTIFACT_DIR/kiro-agent-$scope.log" - -mkdir -p "$ARTIFACT_DIR" -: > "$LOG" - -test -f "$AGENT_FILE" -test -s "$STEERING_FILE" -# `| grep -q .` は使わない(SIGPIPE で 141 になる。assert-plugin-files.sh の注記を参照) -hit="$(find -L "$KIRO_DIR/skills" -path '*/SKILL.md' -print -quit)" -[ -n "$hit" ] || { echo "not found: SKILL.md under $KIRO_DIR/skills" >&2; exit 1; } - -# Kiro には plugin.json を読む仕組みが無いため、版数は installer が埋める -# エージェント description でしか確認できない。installer が読む Claude 版 -# マニフェストの版数と一致することをチェックする。 -MANIFEST_FILE="$REPO_ROOT/plugins/ndf/.claude-plugin/plugin.json" -test -s "$MANIFEST_FILE" -ndf_version="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["version"])' "$MANIFEST_FILE")" -# grep だと 5.0.0 の . が任意文字に一致するため、JSON を読んで厳密に照合する。 -python3 - "$AGENT_FILE" "$ndf_version" >> "$LOG" <<'PY' -import json -import sys -from pathlib import Path - -agent_file, version = sys.argv[1:3] -description = json.loads(Path(agent_file).read_text(encoding="utf-8")).get("description", "") -expected = f"NDF統合開発エージェント(Kiro CLI用 / v{version})" -if description != expected: - raise SystemExit( - f"installed agent description must be {expected!r}, got {description!r}" - ) -print(f"kiro version surfaced: v{version}") -PY - -# ndf-policies は steering として配置する。Skill としても置くと Kiro 組み込みルールの -# Skill 読み込みと steering 読み込みで文脈へ二重注入される。 -if [ -e "$KIRO_DIR/skills/ndf-policies" ]; then - echo "ndf-policies must be delivered via steering only: $KIRO_DIR/skills/ndf-policies" >&2 - exit 1 -fi - -# エージェント定義と、起動時に読み込まれる文脈量をチェックする。 -# -# **文脈量に上限は置かない。計測して記録するだけにする。** -# 以前は 200,000 文字で落としていた。根拠は 2026-08-08 / kiro-cli 2.16.1 でのこの -# フィクスチャの実測 112,404 文字に対する余裕分で、当時のモデルのコンテキスト長を -# 前提にしていた。NDF は Kiro でも 1M コンテキストのモデルだけを対象とするようになり -# (既定 `auto` のコンテキストは 1,000,000。過去の実測の記録で、kiro-cli 2.20.1 に -# `--list-models` は無く同じ手段では再検証できない。出典は -# `scripts/check-skill-frontmatter.py` の Kiro の節にまとめてある)、 -# これは Claude Code の Opus 5 と同じである。**その Claude Code には、起動時に読み込む -# 文脈量の上限が無い。** 同じコンテキスト長のランタイムを片方だけ縛る根拠が無いため、 -# 上限を外して Claude Code へ揃える。 -# -# 数は `$LOG` へ残るため、増え方は後から追える。一覧の予算のほうは -# `scripts/check-skill-frontmatter.py` が Claude Code と同じ 1% でチェックする。 -python3 - "$AGENT_FILE" "$KIRO_DIR" "$PROJECT_DIR" "$STEERING_FILE" >> "$LOG" <<'PY' -import json -import sys -from pathlib import Path - -agent_file, kiro_dir, project_dir, steering_file = sys.argv[1:5] -config = json.loads(Path(agent_file).read_text(encoding="utf-8")) - -if config.get("name") != "ndf": - raise SystemExit(f"agent name must be 'ndf': {config.get('name')!r}") -if not config.get("tools"): - raise SystemExit(f"agent config declares no tools: {agent_file}") - -resources = config.get("resources", []) -for entry in resources: - if entry.startswith("skill://"): - raise SystemExit(f"skill:// resource duplicates the built-in rule: {entry}") - if "ndf-policies" in entry: - raise SystemExit(f"ndf-policies must be delivered via steering, not resources: {entry}") -if len(resources) != len(set(resources)): - raise SystemExit(f"duplicated resources in {agent_file}") - -context_files = sorted(Path(kiro_dir).glob("skills/*/SKILL.md")) -project = Path(project_dir) -context_files += [p for p in (project / "AGENTS.md", project / "README.md") if p.is_file()] -context_files.append(Path(steering_file)) -total = sum(len(p.read_text(encoding="utf-8")) for p in context_files) -# 上限は置かない(上のコメントを参照)。増え方を追えるように記録だけ残す。 -print(f"context files: {len(context_files)} / chars: {total}") -PY - -# 旧 installer が別 checkout から張った .kiro/skills/ndf-policies symlink は、現在の -# プラグイン配下を指さないため「自分が張ったリンクだけ消す」掃除に掛からない。旧導入済み -# プロジェクトでも steering との二重注入が解消されることをチェックする。 -STALE_ROOT="$ARTIFACT_DIR/stale-checkout-$scope/skills/ndf-policies" -mkdir -p "$STALE_ROOT" -echo "stale" > "$STALE_ROOT/SKILL.md" -ln -sfn "$STALE_ROOT" "$KIRO_DIR/skills/ndf-policies" -bash "$REPO_ROOT/plugins/ndf/dev.kiro/install.sh" "${INSTALL_ARGS[@]}" --with-slack >> "$LOG" 2>&1 -if [ -e "$KIRO_DIR/skills/ndf-policies" ] || [ -L "$KIRO_DIR/skills/ndf-policies" ]; then - echo "installer left a stale ndf-policies skill link: $KIRO_DIR/skills/ndf-policies" >&2 - exit 1 -fi -test -f "$STALE_ROOT/SKILL.md" # リンク先の実体まで消していないこと -echo "installer removed a stale ndf-policies skill link" >> "$LOG" - -# --- workspace 限定のチェック(ここから fi まで。heredoc の終端子の都合でインデントしない) --- -if [ "$scope" = workspace ]; then -# 利用者が $AGENT_FILE へ写した設定(MCP プラグインの mcpServers など)が、 -# installer の再実行で失われないことをチェックする。kiro-cli には依存しない。 -python3 - "$AGENT_FILE" <<'PY' -import json -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -config = json.loads(path.read_text(encoding="utf-8")) -config.setdefault("mcpServers", {})["smoke-user-mcp"] = { - "command": "echo", - "args": ["user-managed"], -} -config.setdefault("hooks", {})["userPromptSubmit"] = [{"command": "true"}] -config["smokeUserKey"] = "keep-me" -path.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") -PY -bash "$REPO_ROOT/plugins/ndf/dev.kiro/install.sh" --project "$PROJECT_DIR" --with-slack >> "$LOG" 2>&1 -python3 - "$AGENT_FILE" <<'PY' -import json -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -config = json.loads(path.read_text(encoding="utf-8")) -if "smoke-user-mcp" not in config.get("mcpServers", {}): - raise SystemExit(f"reinstall dropped a user-managed mcpServers entry: {path}") -if "userPromptSubmit" not in config.get("hooks", {}): - raise SystemExit(f"reinstall dropped a user-managed hook: {path}") -if config.get("smokeUserKey") != "keep-me": - raise SystemExit(f"reinstall dropped a user-managed key: {path}") -# installer 管理のキーはテンプレートから再生成されている -if config.get("name") != "ndf" or not config.get("hooks", {}).get("agentSpawn"): - raise SystemExit(f"reinstall did not regenerate installer-managed keys: {path}") -if not config.get("hooks", {}).get("stop"): - raise SystemExit(f"--with-slack did not regenerate the stop hook: {path}") -PY -# チェック用に注入した設定を取り除き、以降のチェックへ持ち越さない -python3 - "$AGENT_FILE" <<'PY' -import json -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -config = json.loads(path.read_text(encoding="utf-8")) -config.get("mcpServers", {}).pop("smoke-user-mcp", None) -if not config.get("mcpServers"): - config.pop("mcpServers", None) -config.get("hooks", {}).pop("userPromptSubmit", None) -config.pop("smokeUserKey", None) -path.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") -PY -echo "reinstall preserved user-managed agent settings" >> "$LOG" - -# 旧 .kiro/agents/default.json からの自動移行をチェックする。kiro-cli には依存しない。 -MIGRATION_ROOT="$ARTIFACT_DIR/kiro-legacy-migration" -rm -rf "$MIGRATION_ROOT" - -# $1: プロジェクト, $2: ndf ならば旧 installer の生成物そのままの fixture、 -# それ以外なら NDF 生成物と判定されない fixture を書く。 -# -# installer の判定は「旧テンプレート固有の description の完全一致」+「旧 resources -# の skill:// 指定 または agentSpawn フックの CLAUDE.ndf.md チェック」なので、 -# 自動移行ケースの fixture は旧 default.json.template と同じ値を持たせる。 -write_legacy_agent() { - mkdir -p "$1/.kiro/agents" - if [ "$2" = ndf ]; then - cat > "$1/.kiro/agents/default.json" <<'JSON' -{ - "name": "default", - "description": "NDF統合開発エージェント(Kiro CLI用)", - "tools": ["*"], - "resources": [ - "file://AGENTS.md", - "file://README.md", - "file://.kiro/skills/ndf-policies/SKILL.md", - "skill://.kiro/skills/**/SKILL.md" - ], - "hooks": { - "agentSpawn": [ - { "command": "if [ -f \"${PWD}/CLAUDE.ndf.md\" ]; then echo \"[NDF] CLAUDE.ndf.md\"; fi" } - ] - }, - "mcpServers": { "legacy-user-mcp": { "command": "echo", "args": ["legacy"] } } -} -JSON - else - cat > "$1/.kiro/agents/default.json" <<'JSON' -{ - "name": "default", - "description": "my own agent", - "resources": ["file://AGENTS.md"], - "mcpServers": { "legacy-user-mcp": { "command": "echo", "args": ["legacy"] } } -} -JSON - fi -} -install_into() { - bash "$REPO_ROOT/plugins/ndf/dev.kiro/install.sh" --project "$1" "${@:2}" >> "$LOG" 2>&1 -} - -# 1. NDF 生成物 + ndf.json なし → 自動移行し、利用者の mcpServers を引き継ぐ -case_ndf="$MIGRATION_ROOT/ndf-generated" -write_legacy_agent "$case_ndf" ndf -install_into "$case_ndf" -test ! -e "$case_ndf/.kiro/agents/default.json" -test -f "$case_ndf/.kiro/agents/default.json.bak" -python3 - "$case_ndf/.kiro/agents/ndf.json" <<'PY' -import json -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -config = json.loads(path.read_text(encoding="utf-8")) -if config.get("name") != "ndf": - raise SystemExit(f"legacy migration did not refresh installer-managed keys: {path}") -if "legacy-user-mcp" not in config.get("mcpServers", {}): - raise SystemExit(f"legacy migration dropped a user-managed mcpServers entry: {path}") -PY - -# 2. NDF 生成物と判定できない default.json → 移行せず元のまま残す -case_user="$MIGRATION_ROOT/user-owned" -write_legacy_agent "$case_user" user -install_into "$case_user" -test -f "$case_user/.kiro/agents/default.json" -test -f "$case_user/.kiro/agents/default.json.bak" -python3 - "$case_user/.kiro/agents/ndf.json" <<'PY' -import json -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -config = json.loads(path.read_text(encoding="utf-8")) -if "legacy-user-mcp" in config.get("mcpServers", {}): - raise SystemExit(f"a non-NDF default.json must not be migrated automatically: {path}") -PY - -# 3. default.json と ndf.json の両方がある → 移行せず既存の ndf.json を尊重する -case_both="$MIGRATION_ROOT/both" -mkdir -p "$case_both" -install_into "$case_both" -python3 - "$case_both/.kiro/agents/ndf.json" <<'PY' -import json -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -config = json.loads(path.read_text(encoding="utf-8")) -config["smokeExistingKey"] = "keep-me" -path.write_text(json.dumps(config, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") -PY -write_legacy_agent "$case_both" ndf -install_into "$case_both" -test -f "$case_both/.kiro/agents/default.json" -python3 - "$case_both/.kiro/agents/ndf.json" <<'PY' -import json -from pathlib import Path -import sys - -path = Path(sys.argv[1]) -config = json.loads(path.read_text(encoding="utf-8")) -if config.get("smokeExistingKey") != "keep-me": - raise SystemExit(f"legacy migration overwrote an existing ndf.json: {path}") -if "legacy-user-mcp" in config.get("mcpServers", {}): - raise SystemExit(f"legacy migration overwrote an existing ndf.json: {path}") -PY - -# 4. --dry-run → 移行を含め一切書き込まない -case_dry="$MIGRATION_ROOT/dry-run" -write_legacy_agent "$case_dry" ndf -dry_state() { (cd "$case_dry" && find . | sort && find . -type f -exec sha256sum {} + | sort); } -before_dry="$(dry_state)" -install_into "$case_dry" --dry-run -if [ "$(dry_state)" != "$before_dry" ]; then - echo "--dry-run modified the project: $case_dry" >&2 - exit 1 -fi -echo "installer migrated a legacy default.json only when it is safe" >> "$LOG" -fi -# --- workspace 限定のチェックここまで --- - -if ! command -v kiro-cli >/dev/null 2>&1; then - echo "kiro-cli agent checks skipped: kiro-cli is not available" >> "$LOG" - exit 0 -fi - -# kiro-cli は workspace エージェントを cwd 配下の .kiro/agents からしか検出しない。 -# scope ごとに、生成した $AGENT_FILE を検出できる cwd と installer の再実行引数を選ぶ。 -case "$scope" in - workspace) KIRO_CWD="$PROJECT_DIR" ;; - # global エージェントはどこからでも解決できるはずなので、.kiro を持たない中立の - # ディレクトリを cwd にする。$HOME を使うと $HOME/.kiro が workspace 扱いにもなり、 - # 「Global として見えている」ことのチェックにならない。 - global) KIRO_CWD="$ARTIFACT_DIR" ;; -esac -if [ "$scope" = global ] && [ -e "$KIRO_CWD/.kiro" ]; then - echo "global agent checks need a cwd without .kiro: $KIRO_CWD" >&2 - exit 1 -fi - -esc="$(printf '\033')" -agent_list() { - # kiro-cli 2.16.1 の agent list は一覧を標準エラー出力へ書く - (cd "$KIRO_CWD" && kiro-cli agent list 2>&1) | sed -e "s/${esc}\\[[0-9;]*m//g" -} -current_default() { - agent_list | awk '/^\*/ { print $2; exit }' -} - -if ! agent_list > "$ARTIFACT_DIR/kiro-agent-list-$scope.txt"; then - echo "kiro-cli agent checks skipped: agent list failed" >> "$LOG" - exit 0 -fi -# global scope の $KIRO_CWD には .kiro がないため、ここに $AGENT_NAME が出ること自体が -# 「Global: ~/.kiro/agents 経由でどこからでも解決できる」ことのチェックになる。 -# **パイプの右で判定しない。** `grep -q` は最初の一致で終わるため、`awk` がまだ書いて -# いる間にパイプが閉じ、`set -o pipefail` のもとでは一致していても 141 になる -# (assert-plugin-files.sh の注記を参照)。結果を変数で受けてから照合する。 -names="$(awk '{ print $1, $2 }' "$ARTIFACT_DIR/kiro-agent-list-$scope.txt")" -if ! grep -qw "$AGENT_NAME" <<<"$names"; then - echo "agent list ($scope) does not contain $AGENT_NAME" >&2 - cat "$ARTIFACT_DIR/kiro-agent-list-$scope.txt" >&2 - exit 1 -fi - -before_default="$(current_default)" -echo "default agent before: ${before_default:-unknown}" >> "$LOG" - -# kiro-cli の既定エージェントは ~/.local/share/kiro-cli/data.sqlite3 に保存されるマシン全体の -# 設定であり、このチェックは必ず元へ戻す必要がある。set-default は agent list と同じく workspace -# エージェントを cwd 配下からしか検出せず、しかも未検出でも終了コード 0 を返すため、 -# agent_list と同じ $KIRO_CWD から実行し、戻ったことを agent list で検証する。 -# 途中のチェックが落ちても復旧するよう trap で実行する。 -restore_default() { - [ -n "$before_default" ] || return 0 - [ "$before_default" != "$AGENT_NAME" ] || return 0 - (cd "$KIRO_CWD" && kiro-cli agent set-default "$before_default") >> "$LOG" 2>&1 || true - restored_default="$(current_default || true)" - echo "default agent restored: ${restored_default:-unknown}" >> "$LOG" - if [ "$restored_default" != "$before_default" ]; then - echo "failed to restore the default agent: ${restored_default:-unknown} (expected $before_default)" >&2 - return 1 - fi -} -# shellcheck disable=SC2154 # rc は trap の文字列の中で代入する(#1323) -trap 'rc=$?; restore_default || rc=1; exit $rc' EXIT - -# kiro-cli はエージェントを cwd / $HOME 配下からのみ検出する。workspace では --project で -# 別ディレクトリへ導入したときに --set-default が効くことをチェックするため、PROJECT_DIR 以外の -# cwd から実行する。global でも同様に $HOME 以外の cwd から実行して既定切替をチェックする。 -(cd "$ARTIFACT_DIR" && bash "$REPO_ROOT/plugins/ndf/dev.kiro/install.sh" "${INSTALL_ARGS[@]}" --with-slack --set-default --yes) >> "$LOG" 2>&1 -after_default="$(current_default)" -echo "default agent after: ${after_default:-unknown}" >> "$LOG" -if [ "$after_default" != "$AGENT_NAME" ]; then - echo "--set-default did not switch the default agent ($scope): ${after_default:-unknown}" >&2 - exit 1 -fi diff --git a/tests/runtime-smoke/assertions/assert-plugin-files.sh b/tests/runtime-smoke/assertions/assert-plugin-files.sh index 55d37e463..16a621d68 100755 --- a/tests/runtime-smoke/assertions/assert-plugin-files.sh +++ b/tests/runtime-smoke/assertions/assert-plugin-files.sh @@ -17,19 +17,6 @@ require_path() { [ -n "$hit" ] || { echo "not found: $pattern under $root" >&2; exit 1; } } -# symlink をたどる版。Kiro CLI は `.kiro/skills/` へ symlink を張る。 -require_path_follow() { - local root="$1" pattern="$2" hit - hit="$(find -L "$root" -path "$pattern" -print -quit)" - [ -n "$hit" ] || { echo "not found: $pattern under $root" >&2; exit 1; } -} - -require_name() { - local root="$1" pattern="$2" hit - hit="$(find "$root" -name "$pattern" -print -quit)" - [ -n "$hit" ] || { echo "not found: $pattern under $root" >&2; exit 1; } -} - case "$runtime" in claude) require_path "$HOME" '*/.claude-plugin/plugin.json' @@ -44,72 +31,5 @@ case "$runtime" in require_path "$HOME" '*/skills/*/SKILL.md' require_path "$HOME" '*/hooks/codex.json' ;; - kiro) - test -f "$PROJECT_DIR/.kiro/agents/ndf.json" - # tools が未宣言だと Kiro CLI はツールなしのエージェントとして読み込み、 - # skill が SKILL.md を読むことも git / gh を実行することもできなくなる。 - python3 -c ' -import json, sys -config = json.load(open(sys.argv[1])) -tools = config.get("tools") -if not tools: - sys.exit("agent config declares no tools: " + sys.argv[1]) -' "$PROJECT_DIR/.kiro/agents/ndf.json" - require_path_follow "$PROJECT_DIR/.kiro/skills" '*/SKILL.md' - # playwright-kit は別プラグインとして導入する(NDF の manifest には含まれない) - test -L "$PROJECT_DIR/.kiro/skills/playwright-planning" - test -f "$PROJECT_DIR/.kiro/skills/playwright-planning/SKILL.md" - test -f "$PROJECT_DIR/.kiro/prompts/pr.md" - test -s "$PROJECT_DIR/.kiro/steering/ndf-policies.md" - test -L "$PROJECT_DIR/.kiro/mcp_runtime/mcp-bigquery" - ;; - agy) - # agy は導入時にプラグインのディレクトリ全体を複製する。symlink は実体へ解決されるため、 - # 導入先では独立したファイルになる。 - plugin_dir="$HOME/.gemini/config/plugins/ndf" - test -f "$plugin_dir/plugin.json" - test -f "$plugin_dir/hooks.json" - require_path "$plugin_dir/skills" '*/SKILL.md' - require_name "$plugin_dir/agents" '*.md' - test -f "$plugin_dir/scripts/worktree-guard.sh" - # 配る Skill の基準は manifest だけが持つ。導入先の並びが基準と一致することを見る。 - python3 - "$REPO_ROOT/plugins/ndf/manifests/agy-skills.txt" "$plugin_dir/skills" <<'AGYPY' -import sys -from pathlib import Path - -manifest, installed = Path(sys.argv[1]), Path(sys.argv[2]) -expected = sorted( - line.split("#", 1)[0].strip() - for line in manifest.read_text(encoding="utf-8").splitlines() - if line.split("#", 1)[0].strip() -) -present = sorted(d.name for d in installed.iterdir() if (d / "SKILL.md").is_file()) -if expected != present: - sys.exit( - "installed agy skills differ from the manifest: " - f"missing={sorted(set(expected) - set(present))} " - f"extra={sorted(set(present) - set(expected))}" - ) -print(f"agy skills installed: {len(present)}") -AGYPY - # ファイルの存在だけでは、agy がどの要素を取り込んだかを確かめられない。取り込みの - # 記録を読み、並びに依存せず 3 つが揃っていることを見る。 - # agy 1.3.4 から既定の出力はテキストになり、JSON は --json のときだけ出る。形も - # {"imports": [{"components": [...]}]} から、要素ごとに skills / agents / hooks の - # 一覧を持つ配列へ変わった。どちらの形も読む。 - agy plugin list --json | python3 -c ' -import json, sys -data = json.load(sys.stdin) -imports = data.get("imports", []) if isinstance(data, dict) else data -entry = next((i for i in imports if i.get("name") == "ndf"), None) -if entry is None: - sys.exit("agy plugin list does not contain ndf") -components = entry.get("components") or [k for k in ("agents", "hooks", "skills") if entry.get(k)] -components = sorted(components) -if components != ["agents", "hooks", "skills"]: - sys.exit(f"agy imported unexpected components: {components}") -print("agy components: " + ", ".join(components)) -' - ;; *) echo "unknown runtime: $runtime" >&2; exit 2 ;; esac diff --git a/tests/runtime-smoke/cli-versions.env b/tests/runtime-smoke/cli-versions.env index a23fa084d..f8968872a 100644 --- a/tests/runtime-smoke/cli-versions.env +++ b/tests/runtime-smoke/cli-versions.env @@ -1,4 +1,4 @@ -# CLI の版の一覧: スモークの像へ入れる 4 ランタイムの CLI の版数・取得先・checksum。 +# CLI の版の一覧: スモークの像へ入れる 2 ランタイムの CLI の版数・取得先・checksum。 # 版を上げるときに書き換える唯一の場所。手順は tests/runtime-smoke/README.md の「版を上げる」。 # 1 行 1 つの KEY=VALUE。値は引用符で囲まない。キーは Containerfile の ARG の名前と同じ。 # arch の名前は `uname -m` の値(x86_64 / aarch64)に揃える。 @@ -9,14 +9,3 @@ CLAUDE_CODE_VERSION=2.1.296 # codex: npm の @openai/codex の版数 CODEX_VERSION=0.162.1 -# kiro: 版数と kirocli--linux-musl.tar.gz の sha256 -KIRO_CLI_VERSION=2.31.0 -KIRO_CLI_SHA256_X86_64=3d0782f274f4180f4f5759ebb780fd46e8b9927261e5774dae1fd486f195cf5e -KIRO_CLI_SHA256_AARCH64=d309b7ce5368cbd01eeb9fc40f18da05ba3d4b39b2ce5457a7731eec9ea3de3c - -# agy: 版数と manifest の url・sha512 -AGY_VERSION=1.3.4 -AGY_URL_X86_64=https://storage.googleapis.com/antigravity-public/antigravity-cli/1.3.4-5524158944247808/linux-x64/cli_linux_x64.tar.gz -AGY_URL_AARCH64=https://storage.googleapis.com/antigravity-public/antigravity-cli/1.3.4-5524158944247808/linux-arm/cli_linux_arm64.tar.gz -AGY_SHA512_X86_64=dbdacfc5a1d9874fbbea3a21026d3990b4728e2e3487fd0316dc53cae9a3091023f969425828f3e2e0fadda43cea07dd978ea96f4ca7c202adf2201f419c56dd -AGY_SHA512_AARCH64=24a1d463319701f4943b2d4932cb86ca34d60d2e9fedcd9018ca31809773d719f272512068911632db175d7233088551033bcecc0a2a56b1bcfa041269687eaf From 3fb3e0703eb539bc60e543545ccd40b60abe0e5b Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 31 Oct 2026 14:08:58 +0000 Subject: [PATCH 2/5] =?UTF-8?q?Docs:=20=E9=85=8D=E5=B8=83=E5=85=88?= =?UTF-8?q?=E3=82=92=20Claude=20Code=20=E3=81=A8=20codex=20=E3=81=AE=202?= =?UTF-8?q?=20=E3=81=A4=E3=81=AB=E3=81=97=E3=80=81agy=E3=83=BBkiro=20?= =?UTF-8?q?=E3=82=92=E7=9B=B8=E6=89=8B=E5=BD=B9=E3=81=A8=E3=81=97=E3=81=A6?= =?UTF-8?q?=E6=9B=B8=E3=81=8F=EF=BC=88#2231=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AGENTS.md・README・各プラグインの README・ndf-plugin-reference・versioning-and-distribution・ plugin-development-guide・project-overview・AUTHORING・スモークの README を 2 ランタイムの配布へ書き直す - KIRO.md を相手役として起動される Kiro CLI の説明へ縮め、plugins/ndf/docs/kiro-cli.md を消す - plugins/ndf/README.md に、前の版で agy・Kiro CLI へ入れた手元の残し方と消し方を書く (一時の HOME で前の版を入れ、消し方のコマンドと clone を進めた後の kiro の hook を確かめた) - 消した agy_agents.py への確定仕様のリンクを外す(リンク検査が落ちるため) - lint: 使わなくなった import と整形 Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 19 ++- KIRO.md | 58 ++------- README.md | 35 ++--- docs/ndf-plugin-reference.md | 36 ++---- docs/plugin-development-guide.md | 52 +++----- docs/project-overview.md | 10 +- .../runtime-plugin-distribution.md | 2 +- docs/versioning-and-distribution.md | 52 ++------ plugins/mcp/mcp-aws-docs/README.md | 8 -- plugins/mcp/mcp-bigquery/README.md | 27 +--- plugins/mcp/mcp-chrome-devtools/README.md | 8 -- plugins/mcp/mcp-dbhub/README.md | 11 -- plugins/mcp/mcp-devin/README.md | 8 -- plugins/mcp/mcp-markitdown/README.md | 8 -- plugins/mcp/mcp-notion/README.md | 8 -- plugins/mcp/mcp-playwright/README.md | 8 -- plugins/mcp/mcp-redash/README.md | 8 -- plugins/mcp/mcp-serena/README.md | 15 +-- plugins/ndf/README.md | 121 +++--------------- .../ndf/scripts/tests/test_hook_deadline.py | 2 - plugins/ndf/skills/AUTHORING.md | 40 ++---- .../ndf/skills/release-verification/SKILL.md | 2 +- plugins/playwright-kit/README.md | 26 +--- scripts/runtime-smoke-latest.py | 1 - tests/runtime-smoke/README.md | 14 +- 25 files changed, 120 insertions(+), 459 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index afdd75dfc..e4517f7c0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## プロジェクト概要 -**Claude Code / Codex / Kiro CLI / agy 向けプラグインマーケットプレイス**の開発プロジェクトです。チーム全体で AI 開発ツールの導入を加速するための事前設定されたプラグインを提供します。 +**Claude Code / Codex 向けプラグインマーケットプレイス**の開発プロジェクトです。Kiro CLI と agy へは配らず、cross-review / cross-refactoring の相手役として起動するだけです。チーム全体で AI 開発ツールの導入を加速するための事前設定されたプラグインを提供します。 **リポジトリ**: https://github.com/devbasex/ai-plugins @@ -116,15 +116,15 @@ ai-plugins/ ├── .claude-plugin/ │ └── marketplace.json # マーケットプレイス定義(必須) ├── plugins/ -│ ├── ndf/ # NDF(4ランタイム共通の単一ディレクトリ) -│ ├── playwright-kit/ # playwright-kit(3ランタイム共通の単一ディレクトリ) +│ ├── ndf/ # NDF(Claude Code・Codex 共通の単一ディレクトリ) +│ ├── playwright-kit/ # playwright-kit(Claude Code・Codex 共通の単一ディレクトリ) │ └── mcp/ -│ └── mcp-*/ # MCPプラグイン10個(3ランタイム共通) +│ └── mcp-*/ # MCPプラグイン10個(Claude Code・Codex 共通) ├── docs/ # リポジトリ知識 ├── factory/ # Web サービス(Neural Development Factory)。プラグインと分け、自分の LICENSE を持つ ├── AGENTS.md # 共通エントリポイント ├── CLAUDE.md # Claude Code固有設定 -├── KIRO.md # Kiro CLI固有設定 +├── KIRO.md # 相手役として起動される Kiro CLI の説明 └── README.md # プロジェクト説明 ``` @@ -144,16 +144,15 @@ ai-plugins/ | [docs/articles/](docs/articles/README.md) | 社外ブログへ載せる記事の正本。**書いた日時点の記録で、以後の構成変更には追随しない** | | [docs/claude-code-skills-survey/](docs/claude-code-skills-survey/01-findings.md) | Claude Code Skills調査レポート(3 本) | | [docs/development-history/](docs/development-history/) | 2026-09-02 までの開発履歴と知見。**以降の振り返りは、起点の issue か Pull Request のコメントに残す**(`/ndf:retrospective`) | -| [plugins/ndf/README.md](plugins/ndf/README.md) | NDFプラグイン(4ランタイム共通) | +| [plugins/ndf/README.md](plugins/ndf/README.md) | NDFプラグイン(Claude Code・Codex 共通) | ## NDFプラグインについて -**NDFプラグイン**は、このマーケットプレイスの主要プラグインです。plugin 名は全ランタイムで `ndf` を維持し、配布物は `plugins/ndf/` の1ディレクトリにまとまっています。 -- Skill の実体は `plugins/ndf/skills/` の1箇所。配布先は `plugins/ndf/manifests/*-skills.txt` が決める +**NDFプラグイン**は、このマーケットプレイスの主要プラグインです。plugin 名は Claude Code・Codex のどちらでも `ndf` を維持し、配布物は `plugins/ndf/` の1ディレクトリにまとまっています。 +- Skill の実体は `plugins/ndf/skills/` の1箇所。配布先は Claude Code と Codex の 2 つで、配る Skill は `plugins/ndf/manifests/{claude,codex}-skills.txt` が決める - Claude Code版は専門サブエージェントと実行主体(supervisor・worker)の定義(`plugins/ndf/agents/`)、公開Skills、PreToolUse/SessionStart/Stopフックを提供 - Codex版は Codex向け公開Skillsと任意Slack通知hookを提供 -- Kiro版は `plugins/ndf/dev.kiro/install.sh` で `.kiro/skills/`、`.kiro/steering/ndf-policies.md`、`.kiro/agents/ndf.json` を生成 -- agy版は `plugins/ndf/dev.agy/` を `agy plugin install` で導入し、公開Skills・エージェント定義・PreToolUse/PreInvocationフックを提供 +- Kiro CLI と agy へは何も配らない。cross-review のレビュー担当、cross-refactoring の提案・実装の担当(相手役)として、ホスト(Claude Code・Codex)から CLI プロセスで起動する。相手役が読む手順書はホストの導入先から担当の worktree へ写す(起動の形の正本は `plugins/ndf/skills/external-ai/references/cli-kiro.md`・`cli-agy.md`) - 外部AI委譲は `/ndf:external-ai` skill と `corder` エージェント経由で Codex / agy を呼び出す 詳細は各 runtime README と `docs/ndf-plugin-reference.md` を参照。 diff --git a/KIRO.md b/KIRO.md index f15c72bb7..1faad537c 100644 --- a/KIRO.md +++ b/KIRO.md @@ -1,57 +1,17 @@ -# AI Plugins - Kiro CLI開発ガイドライン +# AI Plugins - Kiro CLI の扱い 共通の開発ガイドラインは **@AGENTS.md** を参照してください。 -このファイルにはKiro CLI固有の設定のみを記載します。 +このファイルには Kiro CLI に固有のことだけを書きます。 -## NDFプラグインのセットアップ +## 配布しない。相手役として起動する -### 前提条件 -- Kiro CLI がインストール済み -- Node.js(Slack通知を使う場合) -- Codex CLI(`/ndf:external-ai` skill で外部AI委譲を使う場合、または `--with-codex` で Kiro に Codex MCP サーバ設定を生成する場合): `npm install -g @openai/codex` +NDF とマーケットプレイスのプラグインは Kiro CLI へ配りません(配布先は Claude Code と Codex)。Kiro CLI は、cross-review のレビュー担当と cross-refactoring の提案・実装の担当(相手役)として、ホスト(Claude Code・Codex)から CLI プロセスで起動されるだけです。 -### インストール +- 起動の形(`kiro-cli chat --no-interactive --trust-all-tools …`)の正本は `plugins/ndf/skills/external-ai/references/cli-kiro.md` にあります +- 相手役に `--agent` は渡しません。相手役が読む手順書は、ホストの導入先から担当の worktree の `.kiro/skills/` へ写します(`plugins/ndf/skills/cross-refactoring/scripts/prepare-worktrees.sh`) +- 相手役に使えるかは `.ndf/runtimes.json` と `kiro-cli` の認証で決まります -```bash -# 基本(Skills + agentSpawn / userPromptSubmit フック) -bash plugins/ndf/dev.kiro/install.sh +## 既に Kiro CLI へ NDF を入れた利用者 -# Slack通知も有効化 -bash plugins/ndf/dev.kiro/install.sh --with-slack - -# 全部入り(Slack通知 + Kiro 側 Codex MCP 設定生成) -# 注: NDF の外部 AI 委譲は /ndf:external-ai skill が CLI を直接実行し、Codex MCP に依存しない。 -# --with-codex は Kiro セッションで `mcp__codex__*` を直接呼びたい場合だけ付ける -# (Codex MCP サーバの設定と .kiro/prompts/codex.md を追加する)。 -bash plugins/ndf/dev.kiro/install.sh --with-slack --with-codex -``` - -インストーラーは `plugins/ndf/skills/` から `.kiro/skills/` への symlink、`.kiro/steering/ndf-policies.md`、`.kiro/agents/ndf.json` を生成します。リンクする Skill は `plugins/ndf/manifests/kiro-skills.txt` が決めます。`ndf-policies` だけは steering の生成元として使い、`.kiro/skills/` へは symlink しません(Skill と steering の二重注入を避けるため)。 - -`.kiro/agents/ndf.json` の `hooks` には worktree 運用の 2 つが入ります。 - -| hook | 実行するもの | 起きること | -|---|---|---| -| `agentSpawn` | `scripts/worktree-session.sh` | メインディレクトリに残った未コミット変更の提示。ブランチの追従は既定では行わず、`.ndf/worktree.json` の `follow_branch: true` で有効にする | -| `userPromptSubmit` | `scripts/worktree-guard.sh` | worktree で作業する旨の案内 | - -Kiro CLI では tool 実行前の hook を使いません。この事象でモデルへ案内を渡す手段が終了コード 2 に限られ、それが tool の実行を拒否するためです。拒否しない方針のもとでは置けないため、パスを見ない案内をプロンプト送信時の hook が担います。 - -**どちらもリポジトリ側に `.ndf/worktree.json` があるときだけ動きます。** 無ければ何も出力せず終了コード 0 で終わります。書き方は `plugins/ndf/README.md` の「Hooks」節にあります。 - -### Slack通知の設定(オプション) - -`.env` に以下を設定: -``` -SLACK_CHANNEL_ID=C0123456789 -SLACK_BOT_TOKEN=xoxb-... -SLACK_USER_MENTION=<@U0123456789> -``` - -### 利用方法 - -- スキルはdescriptionのキーワードに基づいて自動参照されます -- ワークフロー系スキル(pr, fix, review等)は手動で指示してください - - 例: 「prスキルの手順に従ってPRを作成して」 -- `plugins/ndf/manifests/kiro-skills.txt` に skill が追加された場合、再度インストーラーを実行してください +配布を終えた版より前に `plugins/ndf/dev.kiro/install.sh` で入れた手元の残し方と消し方は、`plugins/ndf/README.md` の「agy・Kiro CLI への配布の終了」にあります。 diff --git a/README.md b/README.md index e3ea5e31e..fd9489ecc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AI Plugins -Claude Code / Codex / Kiro CLI / agy で使うプラグインを配るマーケットプレイスです。 +Claude Code / Codex で使うプラグインを配るマーケットプレイスです。 入れると、Pull Request の作成・レビュー・修正といった開発の作業を、手順どおりに AI へ任せられます。 よく使う外部サービス(Notion・BigQuery・データベースなど)へつなぐ設定も入ります。 @@ -22,29 +22,10 @@ codex plugin marketplace add https://github.com/devbasex/ai-plugins codex plugin add ndf@ai-plugins ``` -### Kiro CLI +### Kiro CLI と agy -Kiro CLI はマーケットプレイスから入れられません。clone したディレクトリでインストーラーを実行します。 - -```bash -git clone https://github.com/devbasex/ai-plugins.git -cd ai-plugins -bash plugins/ndf/dev.kiro/install.sh -``` - -起動の方法は [plugins/ndf/README.md の「Kiro CLI」](./plugins/ndf/README.md#kiro-cli) にあります。 - -### agy - -agy もマーケットプレイスから入れられません。clone したディレクトリから導入します。 - -```bash -git clone https://github.com/devbasex/ai-plugins.git -cd ai-plugins -agy plugin install plugins/ndf/dev.agy -``` - -hook を効かせる手順は [plugins/ndf/README.md の「agy」](./plugins/ndf/README.md#agy) にあります。 +Kiro CLI と agy へはプラグインを配りません。cross-review と cross-refactoring が、別の AI の意見を得る相手役として CLI を起動するだけです(Claude Code か Codex に ndf を入れれば使えます)。 +前の版で入れた手元の扱いは [plugins/ndf/README.md の「agy・Kiro CLI への配布の終了」](./plugins/ndf/README.md#agykiro-cli-への配布の終了) にあります。 更新の方法と Slack 通知の設定は [plugins/ndf/README.md](./plugins/ndf/README.md) にあります。 @@ -67,10 +48,10 @@ Claude Code では `/ndf:` と打って呼びます。**Skill** は | プラグイン名 | バージョン | できること | 詳細 | | --- | --- | --- | --- | -| **ndf** | 10.27.3 | 開発の作業手順(Skill)と専門のサブエージェント。4 つの AI ツールへ同じ名前で入る | [README](./plugins/ndf/README.md) | +| **ndf** | 10.27.3 | 開発の作業手順(Skill)と専門のサブエージェント。Claude Code と Codex へ同じ名前で入る | [README](./plugins/ndf/README.md) | | **playwright-kit** | 3.0.2 | Playwright による E2E テストの計画・スクリプト作成・証跡のレポート | [README](./plugins/playwright-kit/README.md) | -外部サービスへつなぐ MCP サーバーの設定のプラグインが 10 個あります(Claude Code / Codex / Kiro CLI 向け)。 +外部サービスへつなぐ MCP サーバーの設定のプラグインが 10 個あります(Claude Code / Codex 向け)。 使うものだけを入れます(Claude Code なら `/plugin install <プラグイン名>@ai-plugins`)。 | プラグイン名 | つなぐ先 | @@ -89,7 +70,7 @@ Claude Code では `/ndf:` と打って呼びます。**Skill** は ### NDF プラグインの Skill の一覧 **NDFプラグイン v10.27.3** に入る Skill の数は、AI ツールごとに違います。 -Claude Code向け core 49個、Kiro向け core 46個、Codex向け core 45個、agy向け core 45個です。 +Claude Code向け core 49個、Codex向け core 45個です。 元Skills(49個)の内訳は次のとおりです。 - Pull Request とレビュー (7): pr, pr-tests, fix, pr-review, cherry-pick-pr, deploy, merged - 開発の進め方 (17): development-workflow, requirements-design, design, document-rebuild, document-sources, document-drafting, tdd-cycle, refactoring, quality-gates, release, release-verification, retrospective, out-of-scope, progress-tracking, backlog-refinement, layout-review, estimate @@ -105,7 +86,7 @@ Skill のほかに、次のものが入ります。 | 部品 | できること | | --- | --- | | サブエージェント(Claude Code) | 調査・データ分析・デバッグ・コードレビュー・CI/CD などを任せる専門のサブエージェント 8 個 | -| 外部 AI への委譲 | Codex や agy をバックグラウンドで動かし、別の AI の意見を得る | +| 外部 AI への委譲 | Codex・Kiro CLI・agy をバックグラウンドで動かし、別の AI の意見を得る | | hook | 会話の記録を 90 日以上残す。回答や承認を待つときに Slack へ知らせる。リポジトリに `.ndf/worktree.json` があるときは git worktree での作業を案内する | ## もっと知る diff --git a/docs/ndf-plugin-reference.md b/docs/ndf-plugin-reference.md index 688f51637..6fe587847 100644 --- a/docs/ndf-plugin-reference.md +++ b/docs/ndf-plugin-reference.md @@ -2,14 +2,12 @@ ## 概要 -NDF は Claude Code / Codex / Kiro CLI 向けの開発支援プラグイン群です。marketplace 上の plugin 名は全ランタイムで `ndf` を維持し、配布物は `plugins/ndf/` の 1 ディレクトリにまとまっています。 +NDF は Claude Code / Codex 向けの開発支援プラグイン群です。marketplace 上の plugin 名はどちらでも `ndf` を維持し、配布物は `plugins/ndf/` の 1 ディレクトリにまとまっています。配布先は Claude Code と Codex の 2 つで、Kiro CLI と agy へは配りません。この 2 つは cross-review / cross-refactoring の相手役として、ホスト(Claude Code・Codex)から CLI プロセスで起動されるだけです(「相手役のランタイム」)。 | ランタイム | 導入方法 | 公開 Skill | |---|---|---| | Claude Code | `.claude-plugin/marketplace.json` の `ndf` | 49 個 | | Codex | `.claude-plugin/marketplace.json` の `ndf` | 45 個 | -| Kiro CLI | `plugins/ndf/dev.kiro/install.sh` | 46 個 | -| agy | `plugins/ndf/dev.agy/` を `agy plugin install` | 45 個 | Skill の実体は `plugins/ndf/skills/` の 1 箇所だけです。ランタイム別の複製はありません。Skill や 共通スクリプトを変更する場合は `plugins/ndf/` を直接編集します。 @@ -25,11 +23,10 @@ plugins/ndf/ ├── agents/ # Claude Code のサブエージェント(専門 8 個と、3 層の定義 3 個(supervisor 2・worker 1)) ├── hooks/claude.json # Claude Code の SessionStart / Stop hook ├── hooks/codex.json # Codex の Stop hook -├── scripts/ # hook と Skill から呼ぶスクリプト -└── dev.kiro/ # Kiro CLI の installer・エージェント定義・プロンプト +└── scripts/ # hook と Skill から呼ぶスクリプト ``` -`dev.kiro` は Agent Plugins 1.0.0 §8.2 が定めるクライアント拡張ディレクトリです。生成物は +生成物は `skills/<名前>/agents/openai.yaml`(Codex の暗黙起動ポリシー)だけで、commit 対象です。利用者が plugin install 時に build を実行する必要はありません。 @@ -39,10 +36,8 @@ plugin install 時に build を実行する必要はありません。 |---|---| | Claude Code | `.claude-plugin/plugin.json`(`agents/` 11 個(専門 8 個と、3 層の定義 3 個(supervisor 2・worker 1))、`hooks/claude.json`、`skills` 配列 49 個)、`scripts/` | | Codex | `.codex-plugin/plugin.json`(`hooks/codex.json`、`skills` 配列 45 個)、`scripts/` | -| Kiro CLI | `dev.kiro/`(installer・agent config template・workflow prompts)、`manifests/kiro-skills.txt`、`skills/`、`scripts/` | -| agy | `dev.agy/`(マニフェスト・`hooks.json`・`skills/` の symlink・`model` を tier 名へ変えた `agents/` の写し・`scripts` への symlink) | -同じディレクトリを 4 ランタイムが共有しますが、読む対象はマニフェストと installer が決めるため、 +同じディレクトリを 2 ランタイムが共有しますが、読む対象はマニフェストが決めるため、 公開される Skill と hook はランタイムごとに異なります。Claude Code 専用の agents / statusline / transcript retention hook は Codex の `skills` 配列と `hooks/codex.json` には入りません。 @@ -54,7 +49,6 @@ NDF の Skill 実装は `plugins/ndf/skills/` にあります。公開セット |---|---| | `plugins/ndf/manifests/claude-skills.txt` | `.claude-plugin/plugin.json` の `skills` 配列 | | `plugins/ndf/manifests/codex-skills.txt` | `.codex-plugin/plugin.json` の `skills` 配列 | -| `plugins/ndf/manifests/kiro-skills.txt` | `dev.kiro/install.sh` が張る symlink | **`skills/` に置く Skill は、少なくとも 1 つの manifest へ載せます。** 配らない Skill の置き場所 (`optional-skills/`)は v10.5.0 で無くしました(#116)。 @@ -71,17 +65,17 @@ NDF の Skill 実装は `plugins/ndf/skills/` にあります。公開セット - quality / execution: `docker-container-access`, `qa-security-scan` (Playwright による E2E テストは v7.0.0 で `playwright-kit` プラグインへ分離) - external services: `external-ai`, `official-skills-autoloader`(Claude Code のみ) -- runtime 設定: `statusline`(Claude Code と Kiro CLI のみ)、`install-wrapper` / `restart`(カットポイントで claude を起動し直すラッパー。Claude Code のみ) +- runtime 設定: `statusline`(Claude Code のみ)、`install-wrapper` / `restart`(カットポイントで claude を起動し直すラッパー。Claude Code のみ) - policy: `ndf-policies`, `problem-solving`, `logging-guidelines` - 外部サービスと測定: `google-workspace`, `skill-stats`, `ml-model-structure`, - `notion-writing`(v10.5.0 で 4 ランタイムすべてへ配布へ回しました。#116 / #144) + `notion-writing`(v10.5.0 で配布へ回しました。#116 / #144) ## MCP Plugins -MCP plugin も `plugins/mcp//` の 1 ディレクトリにまとまっています。サーバ定義は `.mcp.json` の 1 箇所だけで、3 runtime が同じファイルを読みます。 +MCP plugin も `plugins/mcp//` の 1 ディレクトリにまとまっています。サーバ定義は `.mcp.json` の 1 箇所だけで、Claude Code と Codex が同じファイルを読みます(Codex だけ `.codex.mcp.json` を読ませる例外があります)。 -Claude Code と Codex は marketplace から同じ plugin 名で install します。Kiro CLI は `plugins/mcp//dev.kiro/install.sh` で対象プロジェクトの `.mcp.json` と必要な Kiro agent 設定を更新します。 +Claude Code と Codex は marketplace から同じ plugin 名で install します。 ## Build / Validation @@ -91,15 +85,13 @@ bash scripts/build-runtime-plugins.sh --check bash scripts/validate-runtime-plugins.sh claude plugin validate plugins/ndf python3 -m json.tool plugins/ndf/.codex-plugin/plugin.json >/dev/null -bash plugins/ndf/dev.kiro/install.sh --dry-run bash scripts/runtime-smoke-test.sh --runtime claude bash scripts/runtime-smoke-test.sh --runtime codex -bash scripts/runtime-smoke-test.sh --runtime kiro ``` -`--check` は生成物(`plugins/<プラグイン名>/skills//agents/openai.yaml` と `plugins/mcp/<プラグイン名>/dev.kiro/install.sh`)が最新であることを検証します。`validate-runtime-plugins.sh` は生成物同期、JSON / manifest、marketplace source、Kiro installer、Markdown ローカルリンクをまとめて確認します。 +`--check` は生成物(`plugins/<プラグイン名>/skills//agents/openai.yaml`)が最新であることを検証します。`validate-runtime-plugins.sh` は生成物同期、JSON / manifest、marketplace source、Markdown ローカルリンクをまとめて確認します。agy・Kiro CLI の導入物(`agy-skills.txt`・`kiro-skills.txt`・`dev.agy/`・`dev.kiro/`)が戻っていないことは `scripts/tests/test_distribution_runtimes.py` が確かめます。 -`runtime-smoke-test.sh` は Docker コンテナ内に repo copy、`/tmp/runtime-home`、`/tmp/runtime-project`、`/tmp/runtime-secrets` を分離して作成し、Claude / Codex / Kiro それぞれで `ndf` と `mcp-bigquery` 相当の実 install、Skill / MCP / hook / agent config の assertion、JUnit / log artifact 出力を確認します。PR CI は `--with-secrets=off` の非認証 smoke のみを実行し、認証付き smoke は `runtime-plugin-authenticated-smoke.yml` の protected workflow で実行します。 +`runtime-smoke-test.sh` は Docker コンテナ内に repo copy、`/tmp/runtime-home`、`/tmp/runtime-project`、`/tmp/runtime-secrets` を分離して作成し、Claude / Codex それぞれで `ndf` と `mcp-bigquery` 相当の実 install、Skill / MCP / hook の assertion、JUnit / log artifact 出力を確認します。PR CI は `--with-secrets=off` の非認証 smoke のみを実行し、認証付き smoke は `runtime-plugin-authenticated-smoke.yml` の protected workflow で実行します。 ローカル hook を使う場合は `bash scripts/install-dev-hooks.sh` で `.githooks/` を有効化します。 @@ -107,6 +99,10 @@ bash scripts/runtime-smoke-test.sh --runtime kiro Codex MCP サーバは廃止済みです。外部 AI 委譲は `/ndf:external-ai` Skill と Claude Code 版の `corder` エージェントから Codex / agy を直接呼び出す方式を標準とします。 +### 相手役のランタイム + +Kiro CLI と agy は配布先ではなく、cross-review のレビュー担当と cross-refactoring の提案・実装の担当(相手役)です。ホストは `plugins/ndf/scripts/lib/launch-cli.sh` で CLI を起動し、`--agent` もプラグインの導入先も渡しません。cross-refactoring の担当が読む手順書(`refactoring`・`tdd-cycle`・`quality-gates`)は、ホストの導入先から担当の worktree(Kiro CLI は `.kiro/skills`、agy は `.agents/skills`)へ写します(`plugins/ndf/skills/cross-refactoring/scripts/prepare-worktrees.sh`)。起動の形の正本は `plugins/ndf/skills/external-ai/references/cli-kiro.md`・`cli-agy.md` です。 + ## 承認ゲートの外の問い合わせ `AskUserQuestion` は、問いの区分(入力の `metadata.source`)が人へ回してよい区分のときだけ利用者へ出ます。区分の無い・ @@ -131,8 +127,6 @@ Codex MCP サーバは廃止済みです。外部 AI 委譲は `/ndf:external-ai |---|---|---|---| | Claude Code 2.1.314 | `AskUserQuestion`(`metadata.source` を持つ) | 止められる(PreToolUse の `deny`) | 止める | | Codex CLI 0.164.0 | `request_user_input`(`codex features list` で `default_mode_request_user_input` が `under development`・`false`。exec では使えない) | 確かめていない(PreToolUse が発火するか、区分の欄があるか) | 対象外。Skill の手順の規則(`asking.md`)だけで守る | -| agy 1.4.7 | `ask_question` | 確かめていない(PreToolUse の `toolCall.name` と、拒否の理由がモデルへ届くか) | 対象外。Skill の手順の規則だけで守る | -| Kiro CLI 2.34.0 | 見つからない(組み込みの Tool に無く、MCP の elicitation だけ) | — | 対象外(止める対象が無い) | hook の無いランタイムでは、区分に当たる問いを本文か各ランタイムの問い合わせの Tool で出し、区分の名前を問いの先頭に書きます。 止めた問いでは Slack の待ち通知を送りません。 @@ -146,7 +140,6 @@ hook の無いランタイムでは、区分に当たる問いを本文か各ラ |---|---| | Claude Code | Stop・Notification(`permission_prompt` ほか)・PermissionRequest(`ExitPlanMode`)・PreToolUse(`AskUserQuestion`。問い合わせの guard が止めた問いを除く)hook で `wait-notify.py --runtime claude` を実行 | | Codex | `NDF_CODEX_SLACK_NOTIFY=true` の場合のみ Stop・PermissionRequest hook で `wait-notify.py --runtime codex` を実行 | -| Kiro CLI | `plugins/ndf/dev.kiro/install.sh --with-slack` で `stop` hook に `wait-notify.py --runtime kiro` を生成 | 機密情報は環境変数で管理し、リポジトリにはコミットしません。 @@ -154,7 +147,6 @@ hook の無いランタイムでは、区分に当たる問いを本文か各ラ - [Claude Code版 README](../plugins/ndf/README.md) - [Codex版 README](../plugins/ndf/README.md) -- [Kiro CLI版 README](../plugins/ndf/README.md) - [NDF プラグイン README](../plugins/ndf/README.md) - [開発ワークフローの用語集](../plugins/ndf/skills/development-workflow/references/glossary.md) — ステップ・パイプライン・セッション・承認ゲートなど、`development-workflow` とその周りで使う語 - [runtime plugin container smoke 仕様](specifications/runtime-plugin-container-smoke.md) diff --git a/docs/plugin-development-guide.md b/docs/plugin-development-guide.md index c693a8a39..75922246f 100644 --- a/docs/plugin-development-guide.md +++ b/docs/plugin-development-guide.md @@ -35,13 +35,9 @@ marketplace 定義はこの 1 つだけです。Codex は専用の定義を持 同じ `source` を読みます。`policy` / `category` / `interface` は Codex が要求する項目で、同じ entry へ 含めます(Claude Code はこれらを読み込み時に無視し、`claude plugin validate` は warning 付きで通ります)。 -Kiro CLI は marketplace を読まないため、installer で導入します。 - -```bash -bash plugins/ndf/dev.kiro/install.sh -bash plugins/playwright-kit/dev.kiro/install.sh -bash plugins/mcp/<プラグイン名>/dev.kiro/install.sh -``` +配布先は Claude Code と Codex の 2 つです。Kiro CLI と agy へはどのプラグインも配りません。この 2 つは +cross-review / cross-refactoring の相手役として CLI を起動するだけで、導入物を持ちません(`dev.kiro/`・`dev.agy/`・ +`kiro-skills.txt`・`agy-skills.txt` を足すと `scripts/tests/test_distribution_runtimes.py` が落とします)。 ## プラグイン構造 @@ -62,25 +58,18 @@ plugins/{plugin-name}/ │ └── {skill-name}/ │ └── SKILL.md ├── manifests/ # ランタイム別の配布 Skill 一覧(Skill を配る場合) -│ └── {claude,codex,kiro}-skills.txt +│ └── {claude,codex}-skills.txt ├── hooks/ # フック(オプション) │ ├── claude.json # Claude Code 用 │ └── codex.json # Codex 用 -├── dev.kiro/ # Kiro CLI 用(installer など) -│ └── install.sh -├── dev.agy/ # agy 用(マニフェスト・hooks.json・symlink) -│ ├── plugin.json -│ └── hooks.json └── README.md # プラグイン説明 ``` -4 ランタイムが同じディレクトリを読みます。読む対象はマニフェストと installer が決めるため、 -公開される Skill と hook はランタイムごとに異なります。`dev.kiro` と `dev.agy` は Agent Plugins -仕様 §8.2 が定めるクライアント拡張ディレクトリです。 +2 ランタイムが同じディレクトリを読みます。読む対象はマニフェストが決めるため、 +公開される Skill と hook はランタイムごとに異なります。 -**ルート直下の `plugin.json` は、agy へ配るためには置きません。** 置くと Codex がそちらを -優先して読み、`skills/` の実体を全件配ってしまいます(`plugins/ndf` で実測)。agy 向けの -定義は `dev.agy/plugin.json` へ置きます。 +**`.codex-plugin/` を持つプラグインはルート直下に `plugin.json` を置きません。** 置くと Codex がそちらを +優先して読み、`skills/` の実体を全件配ってしまいます(`plugins/ndf` で実測)。 ルートの `plugin.json`(Agent Plugins 形式)は、`skills/` を全件公開してよく hook も持たない プラグインにだけ置きます。判断の基準は @@ -134,11 +123,11 @@ plugins/{plugin-name}/ 2. **ディレクトリ構造を作成** ```bash - mkdir -p plugins/{plugin-name}/{.claude-plugin,.codex-plugin,commands,agents,skills,manifests,hooks,dev.kiro} + mkdir -p plugins/{plugin-name}/{.claude-plugin,.codex-plugin,commands,agents,skills,manifests,hooks} ``` 使わないディレクトリは作らなくて構いません。Skill を配らないなら `skills/` と - `manifests/`、hook が無いなら `hooks/`、Kiro CLI へ配らないなら `dev.kiro/` は不要です。 + `manifests/`、hook が無いなら `hooks/` は不要です。 3. **plugin.jsonを作成** - 必須フィールドをすべて含める @@ -185,25 +174,23 @@ frontmatter と命名の書き方は [AUTHORING.md](../plugins/ndf/skills/AUTHOR | 1 | `plugins/ndf/skills/<名前>/SKILL.md` | 実体 | `check-skill-frontmatter.py`(形だけ) | | 2 | `plugins/ndf/manifests/<ランタイム>-skills.txt` | 配るランタイムの分だけ名前を 1 行 | `validate-runtime-plugins.sh`(どの manifest にも無いときだけ。配る先は判断) | | 3 | `plugins/ndf/.claude-plugin/plugin.json` / `.codex-plugin/plugin.json` の `skills` 配列 | `./skills/<名前>`(manifest に載せたランタイムの分) | `validate-runtime-plugins.sh` | -| 4 | `bash scripts/build-runtime-plugins.sh` の生成物 | `dev.agy/skills/<名前>`(agy へ配るとき)、`skills/<名前>/agents/openai.yaml`(`disable-model-invocation: true` のとき)をコミット | `build-runtime-plugins.sh --check` | -| 5 | `README.md` の概要「公開Skills」とプラグイン一覧表の `ndf` 行(2 行) | `Claude Code向け core 45個` の形で 4 ランタイム | `check-doc-staleness.py` | +| 4 | `bash scripts/build-runtime-plugins.sh` の生成物 | `skills/<名前>/agents/openai.yaml`(`disable-model-invocation: true` のとき)をコミット | `build-runtime-plugins.sh --check` | +| 5 | `README.md` の概要「公開Skills」とプラグイン一覧表の `ndf` 行(2 行) | `Claude Code向け core 45個` の形で 2 ランタイム | `check-doc-staleness.py` | | 6 | `README.md` の「元Skills(N個)」 | 実体の数 | `check-doc-staleness.py` | | 7 | `README.md` のカテゴリ内訳 | `- 運用 (2): skill-stats, statusline` の形。名前と括弧の数 | `check-doc-staleness.py`(合計だけ。どのカテゴリに入れるかは判断) | -| 8 | `plugins/ndf/README.md` の配布先の表 4 行 | `\| Claude Code \| 45 個 \|` の形 | `check-doc-staleness.py` | +| 8 | `plugins/ndf/README.md` の配布先の表 2 行 | `\| Claude Code \| 45 個 \|` の形 | `check-doc-staleness.py` | | 9 | `plugins/ndf/README.md` のレイアウト図 | `唯一の実体(45 個)` | `check-doc-staleness.py` | -| 10 | `plugin.json` 3 本(claude・codex・`dev.agy`)の `description` | `45 focused NDF skills` の形 | `validate-runtime-plugins.sh` | +| 10 | `plugin.json` 2 本(claude・codex)の `description` | `45 focused NDF skills` の形 | `validate-runtime-plugins.sh` | | 11 | `.claude-plugin/marketplace.json` の `ndf` の `description` | 10 と同じ形(Claude Code の数) | `validate-runtime-plugins.sh` | -| 12 | `scripts/tests/test_agy_distribution.py` の `EXPECTED_COUNTS` | 4 ランタイムの数 | `pytest`(件数の assert だけで、直す場所を名指ししない) | -| 13 | `plugins/ndf/README.md` の agy の節「Skill N 個」 | agy の数 | **拾わない**(#611) | -| 14 | `plugins/ndf/README.md` の「Codex の暗黙起動抑止」の数と表 | `disable-model-invocation: true` のときだけ | **拾わない**(#611) | -| 15 | `docs/ndf-plugin-reference.md` の数 | 4 ランタイムの数と実体の数 | **拾わない**(#611) | +| 12 | `plugins/ndf/README.md` の「Codex の暗黙起動抑止」の数と表 | `disable-model-invocation: true` のときだけ | **拾わない**(#611) | +| 13 | `docs/ndf-plugin-reference.md` の数 | 2 ランタイムの数と実体の数 | **拾わない**(#611) | **数の形は 3 通りあります。** `README.md` は `core 45個`(空白なし)、`plugins/ndf/README.md` は `45 個`、`plugin.json` は `45 focused NDF skills` です。数だけを機械的に置き換えると、どれかに 当たりません。数はランタイムごとに manifest の行数から決めます。 ```bash -for r in claude codex kiro agy; do +for r in claude codex; do printf '%s ' "$r"; grep -cvE '^[[:space:]]*(#|$)' "plugins/ndf/manifests/$r-skills.txt" done ls -d plugins/ndf/skills/*/ | wc -l # 実体の数(6・9) @@ -245,12 +232,9 @@ uv run --with pytest --with pytest-xdist pytest scripts/tests -q -n auto 上書きされる**(`--scope local` を指定しても起きる)。続けて `marketplace remove` すると clone と導入記録まで消える。 -手元で確かめる手段は 2 つある。 +手元で確かめる手段は次のとおり。 ```bash -# Kiro CLI: installer が任意のディレクトリへ導入する。取得元を書き換えない -bash plugins/ndf/dev.kiro/install.sh --project <検証用ディレクトリ> --yes - # Claude Code: 1 つのプラグインをディレクトリから読み込む claude --plugin-dir plugins/ndf ``` diff --git a/docs/project-overview.md b/docs/project-overview.md index 4c7f059f5..7db4e20e8 100644 --- a/docs/project-overview.md +++ b/docs/project-overview.md @@ -23,17 +23,17 @@ Claude Code / Codex プラグインマーケットプレイス(内部用)と ``` ai-plugins/ ├── .claude-plugin/ -│ └── marketplace.json # マーケットプレイス定義(3ランタイム共通) +│ └── marketplace.json # マーケットプレイス定義(Claude Code・Codex 共通) ├── plugins/ -│ ├── ndf/ # NDF(3ランタイム共通の単一ディレクトリ) -│ ├── playwright-kit/ # playwright-kit(3ランタイム共通の単一ディレクトリ) +│ ├── ndf/ # NDF(Claude Code・Codex 共通の単一ディレクトリ) +│ ├── playwright-kit/ # playwright-kit(Claude Code・Codex 共通の単一ディレクトリ) │ └── mcp/ -│ └── mcp-*/ # MCPプラグイン10個(3ランタイム共通) +│ └── mcp-*/ # MCPプラグイン10個(Claude Code・Codex 共通) ├── docs/ # リポジトリ知識 ├── factory/ # Web サービス(Neural Development Factory)。プラグインと分け、自分の LICENSE を持つ ├── AGENTS.md # 共通エントリポイント ├── CLAUDE.md # Claude Code固有設定 -├── KIRO.md # Kiro CLI固有設定 +├── KIRO.md # 相手役として起動される Kiro CLI の説明 └── README.md # プロジェクトドキュメント ``` diff --git a/docs/specifications/runtime-plugin-distribution.md b/docs/specifications/runtime-plugin-distribution.md index 4b6d6d0af..bf9312ed0 100644 --- a/docs/specifications/runtime-plugin-distribution.md +++ b/docs/specifications/runtime-plugin-distribution.md @@ -324,6 +324,6 @@ MCP plugin を変更する場合: - [NDF プラグイン README](../../plugins/ndf/README.md) - [playwright-kit プラグイン README](../../plugins/playwright-kit/README.md) - [Runtime Smoke の CLI の版](runtime-smoke-cli-versions.md) -- [`scripts/lib/agy_agents.py`](../../scripts/lib/agy_agents.py)(agy に配る定義の生成と model の対応表) +- `scripts/lib/agy_agents.py`(agy に配る定義の生成と model の対応表。#2231 で消した) - [Agent Plugins 仕様](https://github.com/agentplugins/agent-plugins-spec) - 課題 [#2087](https://github.com/devbasex/ai-plugins/issues/2087)(agy に配るエージェント定義) diff --git a/docs/versioning-and-distribution.md b/docs/versioning-and-distribution.md index c4445c363..591c9c391 100644 --- a/docs/versioning-and-distribution.md +++ b/docs/versioning-and-distribution.md @@ -47,7 +47,7 @@ Codex は全ブランチを取得するが、登録した ref を基準にする **接尾辞は人が読むための目印である。** Claude Code の直接インストール経路は版数を **キャッシュキーとしての文字列一致**でしか見ず、`-dev` や `-rc` を prerelease として -扱わない。Codex と Kiro も同様で、Agent Plugins Specification には解釈の規定が無い。 +扱わない。Codex も同様で、Agent Plugins Specification には解釈の規定が無い。 semver の順序で除外されるのは、プラグイン間の依存解決(`dependencies`)の経路だけである。 それでも接尾辞を付けるのは、**入れたくない利用者が版数を見て判断できるようにする**ためである。 @@ -69,6 +69,10 @@ semver の順序で除外されるのは、プラグイン間の依存解決(` ## ランタイムごとの取得と導入 +**配布先は Claude Code と Codex の 2 つである。** Kiro CLI と agy へは配らず(#2231。最後に配ったのは +`10.27.3`)、cross-review / cross-refactoring の相手役として CLI を起動するだけである。前の版で入れた手元の扱いは +[plugins/ndf/README.md の「agy・Kiro CLI への配布の終了」](../plugins/ndf/README.md#agykiro-cli-への配布の終了)にある。 + **取得元の登録と、プラグインの導入は別の操作である。** 登録は取得元の一覧を書き換えるだけで、 導入済みの実体には触れない。実体は版ごとのディレクトリ(`plugins/cache/<取得元>/<名前>/<版>/`) に置かれ、導入の記録がそのうち 1 つを指す。**登録を切り替えただけでは、その記録は元の版を @@ -84,17 +88,6 @@ codex plugin marketplace add devbasex/ai-plugins codex plugin add ndf@ai-plugins ``` -**agy には入れ替えの操作が無い。** `agy plugin --help` の副コマンドは -`list` / `import` / `install` / `uninstall` / `enable` / `disable` / `validate` / `link` / -`help` で、導入済みを新しい版へ入れ替えるものが無い。他の 3 ランタイムは持つ -(Claude Code は `claude plugin update`、Codex は取得元を更新してから `codex plugin add`、 -Kiro CLI はインストーラの再実行)。 - -**`install` は既存の導入を拒まず、上書きする。ただし消えたファイルは残る**(agy 1.1.26 / -2026-09-05 に実測)。同じ名前で導入し直すと `plugin.json` の版数も Skill の中身も入れ替わるが、 -**取得元から取り除いたファイルは実体に残り続ける**。配布物が減る版へ上げると、消したはずの -Skill が利用者の手元で生き続ける。そのため案内は外してから入れ直す 2 手のままにする。 - **ローカルのディレクトリを同じ名前でマーケットプレイスとして追加しない。** 登録の鍵は取得元では なく `marketplace.json` の `name` で、**1 つの名前につき 1 つしか登録できない**(公式ドキュメントに "Each user can register only one marketplace per name" とある)。 @@ -111,17 +104,9 @@ Skill が利用者の手元で生き続ける。そのため案内は外して 定まらず検証の手段にならない。手元での確認は次のいずれかで行う。どれも取得元を書き換えない。 ```bash -bash plugins/ndf/dev.kiro/install.sh --project <検証用ディレクトリ> --yes # Kiro claude --plugin-dir plugins/ndf # Claude Code -agy plugin validate plugins/ndf/dev.agy # agy(読み込みの確認) ``` -**agy は導入したプラグインの `hooks.json` を読み込まない。** 読む先は -`~/.gemini/config/hooks.json` の 1 か所だけで、プラグイン配下とプロジェクト直下のどちらに -置いても `loaded 1 named hooks from 1 hooks.json file(s)` のまま変わらない(agy 1.1.26 で -実測)。導入した hook を効かせるには `bash plugins/ndf/dev.agy/install-hooks.sh` を実行して -その 1 か所へ差し込む。冪等で、他の項目には触れない。 - ## 開発版を試す ```bash @@ -143,16 +128,6 @@ ref の書き方は `owner/repo@ref` と `git-url#ref` の 2 つで、 `claude plugin marketplace add --help` には出ないが実機で動く。Codex は `--ref` を持ち、 `owner/repo@ref` も受け取る。`claude plugin install` に版を指定する手段は無い(これは確定)。 -**Kiro と agy はマーケットプレイスの経路を持たない。** clone した作業ディレクトリから -導入するため、ref にあたるのは clone の checkout である。clone 直後は既定ブランチ(`main`) -なので、正式版を使うだけなら追加の操作は要らない。 - -```bash -git -C checkout develop # 開発版を試すときだけ -bash plugins/ndf/dev.kiro/install.sh --project <ディレクトリ> --yes -agy plugin uninstall ndf && agy plugin install /plugins/ndf/dev.agy -``` - **サードパーティのマーケットプレイスは自動更新が既定で無効である。** `main` へ出した版が 即座に全利用者へ届くわけではなく、利用者が `marketplace update` を実行した時点で届く。 @@ -237,7 +212,7 @@ claude plugin tag plugins/ndf --push # ndf--v<版> を作って origin へ 落ちる。**記載を消してもチェックは通らない。** 位置を決める語が見つからなければ、読み取れない こととして落ちる。 -**定義ファイルが 7 箇所である。** 3 つの `plugin.json` はいずれも +**定義ファイルが 5 箇所である。** 2 つの `plugin.json` はいずれも `version` と `description` の両方に版数を持つため、片方だけ直すとチェックで止まる。 | 箇所 | 何を書くか | @@ -246,8 +221,6 @@ claude plugin tag plugins/ndf --push # ndf--v<版> を作って origin へ | `plugins/ndf/.claude-plugin/plugin.json` の `description` | `(vX.Y.Z)` の形で版数 | | `plugins/ndf/.codex-plugin/plugin.json` の `version` | 版数そのもの | | `plugins/ndf/.codex-plugin/plugin.json` の `description` | `(vX.Y.Z)` の形で版数 | -| `plugins/ndf/dev.agy/plugin.json` の `version` | 版数そのもの | -| `plugins/ndf/dev.agy/plugin.json` の `description` | `(vX.Y.Z)` の形で版数 | | `.claude-plugin/marketplace.json` の該当プラグインの `description` | 同上 | **plugin の README は版ごとの更新の節(`## vX.Y.Z へ更新するとき`)を持たない。** README は現行版の説明と、 @@ -255,7 +228,7 @@ CHANGELOG.md を参照する固定の「`## 更新情報`」の節だけを持 置く。配布の手順(`release-steps.py` の `bump`・`changelog`・`notes`)は README に触れない。節が戻ると `scripts/check-doc-staleness.py` が落とす(`plugins/*/README.md`・`plugins/mcp/*/README.md`)。 -**説明文書の本文が 6 箇所である。** いずれも利用者が読む入口にあり、古いまま残ると現行版を +**説明文書の本文が 5 箇所である。** いずれも利用者が読む入口にあり、古いまま残ると現行版を 誤って伝えるか、書かれたとおりに実行できない。 **指示書(`AGENTS.md` / `CLAUDE.md`)には版数を書かない。** 開発版の配布の `bump` が版数を @@ -267,7 +240,6 @@ CHANGELOG.md を参照する固定の「`## 更新情報`」の節だけを持 | `README.md` の概要 | `**NDFプラグイン vX.Y.Z**` | `ndf` の `plugin.json` | | `README.md` のプラグイン一覧表 | 版数の列 | 行ごとに `plugins/<名前>/.claude-plugin/plugin.json` | | `docs/versioning-and-distribution.md` の「版の付け方と開発版の配布」章 | 版数の例 | `ndf` の `plugin.json`(基底の比較) | -| `plugins/ndf/README.md` の Kiro の確認例 | 期待出力の版数 | `ndf` の `plugin.json` | | `plugins/ndf/README.md` の Codex のパス例 | `~/.codex/plugins/cache/.../ndf/X.Y.Z/`(2 箇所) | `ndf` の `plugin.json` | | `plugins/ndf/README.md` の `codex plugin list` の出力例 | 期待出力の版数 | `ndf` の `plugin.json` | @@ -305,9 +277,9 @@ CHANGELOG.md を参照する固定の「`## 更新情報`」の節だけを持 両方に版を持たせると `plugin.json` が無警告で優先され、食い違いに気づけなくなる。 `scripts/validate-runtime-plugins.sh` が突き合わせるのは、説明文書に書かれた Skill の数と、 -**版数を書いた箇所**です。定義ファイルが 7 箇所、説明文書の本文が -6 箇所あります(`README.md` の概要とプラグイン一覧表、正本に 1 箇所、 -`plugins/ndf/README.md` の Kiro と Codex の確認例 3 種類)。**記載を消してもチェックは通りません。** +**版数を書いた箇所**です。定義ファイルが 5 箇所、説明文書の本文が +5 箇所あります(`README.md` の概要とプラグイン一覧表、正本に 1 箇所、 +`plugins/ndf/README.md` の Codex の確認例 2 種類)。**記載を消してもチェックは通りません。** 一覧は上の 2 つの表にあります。 ## チェックに載らず手で直す箇所 @@ -340,12 +312,12 @@ CHANGELOG.md を参照する固定の「`## 更新情報`」の節だけを持 そのまま残す。現行版を指しているかは文脈で決まる。**チェックが突き合わせるのは、周囲の固定の語で 位置を決めた記載だけである。** 履歴と記録は最初から走査に入らない。 -**説明文書の本文に書かれた版数は取りこぼしやすい。** 上の 7 箇所を +**説明文書の本文に書かれた版数は取りこぼしやすい。** 上の本文の 5 箇所を チェックの対象へ入れ、この取りこぼしを機械が拾う。 ## 取得元の登録を確かめる -**取得元の登録そのものを確かめるときは、設定ディレクトリを隔離する。** 「ランタイムごとの取得と導入」の手元での確認の 3 つは配布物を +**取得元の登録そのものを確かめるときは、設定ディレクトリを隔離する。** 「ランタイムごとの取得と導入」の手元での確認(`claude --plugin-dir`)は配布物を 直接読み込む確認で、取得元の登録から導入までの経路は通らない。開発版チャネルの取得手順は その経路そのものであるため、別の設定ディレクトリを与えて確かめる。**利用者の登録には触れない** (claude 2.1.261 / codex-cli 0.153.2 で実測)。 diff --git a/plugins/mcp/mcp-aws-docs/README.md b/plugins/mcp/mcp-aws-docs/README.md index e0c24c9ff..184063988 100644 --- a/plugins/mcp/mcp-aws-docs/README.md +++ b/plugins/mcp/mcp-aws-docs/README.md @@ -31,14 +31,6 @@ AWS公式ドキュメントにアクセスするためのMCPサーバープラ codex plugin add mcp-aws-docs@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-aws-docs/dev.kiro/install.sh -``` - ### 必要な環境変数 - 追加の環境変数は不要です。 diff --git a/plugins/mcp/mcp-bigquery/README.md b/plugins/mcp/mcp-bigquery/README.md index 91931676f..0fc4bfcc9 100644 --- a/plugins/mcp/mcp-bigquery/README.md +++ b/plugins/mcp/mcp-bigquery/README.md @@ -1,7 +1,7 @@ # BigQuery MCP Google が運用するリモートの BigQuery MCP サーバー(`https://bigquery.googleapis.com/mcp`)を -Claude Code・Codex・Kiro CLI から使うためのプラグインです。 +Claude Code・Codex から使うためのプラグインです。 ## 概要 @@ -64,19 +64,17 @@ Claude Code・Codex・Kiro CLI から使うためのプラグインです。 |---|---|---| | Claude Code | `~/.claude/settings.json`(またはプロジェクトの `.claude/settings.json`) | `"permissions": {"deny": ["mcp__plugin_mcp-bigquery_bigquery__execute_sql", "mcp__bigquery__execute_sql"]}` | | Codex | `~/.codex/config.toml` | 下の例 | -| Kiro CLI | `.kiro/agents/default.json` の `mcpServers.bigquery` | `"disabledTools": ["execute_sql"]`(`install.sh` を流し直したら書き直す) | ```toml [plugins."mcp-bigquery@ai-plugins".mcp_servers.bigquery] disabled_tools = ["execute_sql"] ``` -Claude Code の 2 つ目の名前は、Kiro CLI の `install.sh` がプロジェクト直下の `.mcp.json` にも -`bigquery` を書くためです。同じプロジェクトで Claude Code を使うと、プロジェクトのサーバーとして +Claude Code の 2 つ目の名前は、前の版の Kiro CLI 向けの `install.sh` がプロジェクト直下の `.mcp.json` にも +`bigquery` を書いたためです。同じプロジェクトで Claude Code を使うと、プロジェクトのサーバーとして `mcp__bigquery__execute_sql` の名前で読まれ、1 つ目の名前の deny では止まりません。 -どれもツールの一覧から `execute_sql` が消えます(Claude Code 2.1.295・Codex 0.160.0・ -kiro-cli 2.24.1 で確認)。組織として止めるときは、IAM の deny ポリシーで `execute_sql` を禁じます。 +どれもツールの一覧から `execute_sql` が消えます(Claude Code 2.1.295・Codex 0.160.0 で確認)。組織として止めるときは、IAM の deny ポリシーで `execute_sql` を禁じます。 ## ランタイムごとの認証 @@ -86,13 +84,6 @@ kiro-cli 2.24.1 で確認)。組織として止めるときは、IAM の deny |---|---|---| | Claude Code | `headersHelper` | 接続のたびに `gcloud auth print-access-token` を実行する(401/403 でも取り直す) | | Codex | `http_headers_helper` | 同上 | -| Kiro CLI | `headers` の `${BIGQUERY_ACCESS_TOKEN}` | **自動では取り直さない**。起動前に環境変数へ入れる | - -Kiro CLI では、起動の前に次を実行してください。1 時間たったら入れ直して再起動します。 - -```bash -export BIGQUERY_ACCESS_TOKEN=$(gcloud auth print-access-token) -``` Codex は `http_headers_helper` を、`HOME`・`PATH` など限られた環境変数だけを渡して実行します。 `CLOUDSDK_CONFIG` で gcloud の設定の場所を変えていると、helper の中の gcloud は既定の場所 @@ -114,7 +105,7 @@ v2 までのローカルサーバー(`mcp-server-bigquery`)をやめ、リ |---|---|---| | サーバー | `uvx mcp-server-bigquery`(stdio) | `https://bigquery.googleapis.com/mcp`(HTTP) | | 認証 | サービスアカウントの鍵ファイル | gcloud にログインした利用者のトークン | -| 環境変数 | `BIGQUERY_PROJECT` `BIGQUERY_LOCATION` `BIGQUERY_DATASET` `BIGQUERY_KEY_FILE` | 不要(Kiro CLI のみ `BIGQUERY_ACCESS_TOKEN`) | +| 環境変数 | `BIGQUERY_PROJECT` `BIGQUERY_LOCATION` `BIGQUERY_DATASET` `BIGQUERY_KEY_FILE` | 不要 | | ツール名 | `execute-query` `list-tables` `describe-table` | `execute_sql_readonly` `list_table_ids` `get_table_info` など | v2 は依存する `mcp` パッケージの 2 系で起動できなくなっていました(`'Server' object has no @@ -134,14 +125,6 @@ attribute 'list_tools'`)。 codex plugin add mcp-bigquery@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-bigquery/dev.kiro/install.sh -``` - ## 更新情報 版ごとの変更と移行の手順は [CHANGELOG.md](https://github.com/devbasex/ai-plugins/blob/main/CHANGELOG.md) の `[mcp-bigquery <版>]` の節にあります。 diff --git a/plugins/mcp/mcp-chrome-devtools/README.md b/plugins/mcp/mcp-chrome-devtools/README.md index 872f67511..c77e14bac 100644 --- a/plugins/mcp/mcp-chrome-devtools/README.md +++ b/plugins/mcp/mcp-chrome-devtools/README.md @@ -33,14 +33,6 @@ Chrome DevTools MCPサーバーをClaude Codeで利用するためのプラグ codex plugin add mcp-chrome-devtools@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-chrome-devtools/dev.kiro/install.sh -``` - ### 必要な環境変数 - 追加の環境変数は不要です。 diff --git a/plugins/mcp/mcp-dbhub/README.md b/plugins/mcp/mcp-dbhub/README.md index c2942ad70..0ac5bfddf 100644 --- a/plugins/mcp/mcp-dbhub/README.md +++ b/plugins/mcp/mcp-dbhub/README.md @@ -32,14 +32,6 @@ codex plugin add mcp-dbhub@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-dbhub/dev.kiro/install.sh -``` - ### 必要な環境変数 - 追加の環境変数は不要です。 @@ -54,9 +46,6 @@ bash plugins/mcp/mcp-dbhub/dev.kiro/install.sh # Codex codex plugin marketplace upgrade ai-plugins codex plugin add mcp-dbhub@ai-plugins - -# Kiro CLI -bash plugins/mcp/mcp-dbhub/dev.kiro/install.sh ``` ## 設定 diff --git a/plugins/mcp/mcp-devin/README.md b/plugins/mcp/mcp-devin/README.md index ff081d50e..e1a8d8965 100644 --- a/plugins/mcp/mcp-devin/README.md +++ b/plugins/mcp/mcp-devin/README.md @@ -43,14 +43,6 @@ DEVIN_API_KEY=your-devin-api-key codex plugin add mcp-devin@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-devin/dev.kiro/install.sh -``` - ### 必要な環境変数 - `DEVIN_API_KEY` diff --git a/plugins/mcp/mcp-markitdown/README.md b/plugins/mcp/mcp-markitdown/README.md index 219bfbacd..5387eb5df 100644 --- a/plugins/mcp/mcp-markitdown/README.md +++ b/plugins/mcp/mcp-markitdown/README.md @@ -30,14 +30,6 @@ codex plugin add mcp-markitdown@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-markitdown/dev.kiro/install.sh -``` - ### 必要な環境変数 - 追加の環境変数は不要です。 diff --git a/plugins/mcp/mcp-notion/README.md b/plugins/mcp/mcp-notion/README.md index 6f4dcf859..2e80e97d3 100644 --- a/plugins/mcp/mcp-notion/README.md +++ b/plugins/mcp/mcp-notion/README.md @@ -47,14 +47,6 @@ NOTION_TOKEN= codex plugin add mcp-notion@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-notion/dev.kiro/install.sh -``` - ### 必要な環境変数 - 追加の環境変数は不要です。 diff --git a/plugins/mcp/mcp-playwright/README.md b/plugins/mcp/mcp-playwright/README.md index 91b1832bc..435239178 100644 --- a/plugins/mcp/mcp-playwright/README.md +++ b/plugins/mcp/mcp-playwright/README.md @@ -31,14 +31,6 @@ Playwright MCPを自動的にセットアップし、ブラウザ自動化機能 codex plugin add mcp-playwright@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-playwright/dev.kiro/install.sh -``` - ### 必要な環境変数 - `HOME` diff --git a/plugins/mcp/mcp-redash/README.md b/plugins/mcp/mcp-redash/README.md index 83b9d288e..044614759 100644 --- a/plugins/mcp/mcp-redash/README.md +++ b/plugins/mcp/mcp-redash/README.md @@ -33,14 +33,6 @@ Redash MCP サーバーをマルチ環境対応で利用するための Claude C codex plugin add mcp-redash@ai-plugins ``` -### Kiro CLI - -Kiro では repository clone 後、対象 plugin の installer を project root で実行します。 - -```bash -bash plugins/mcp/mcp-redash/dev.kiro/install.sh -``` - ### 必要な環境変数 - `REDASH_API_KEY` diff --git a/plugins/mcp/mcp-serena/README.md b/plugins/mcp/mcp-serena/README.md index 166c7df83..de628ae10 100644 --- a/plugins/mcp/mcp-serena/README.md +++ b/plugins/mcp/mcp-serena/README.md @@ -1,7 +1,7 @@ # mcp-serena Serena MCP サーバを提供するプラグインです。シンボル単位の読み書き(定義・参照・シンボル単位の -置換)と、言語サーバの診断を Claude Code・Codex・Kiro CLI で使えるようにします。 +置換)と、言語サーバの診断を Claude Code・Codex で使えるようにします。 ## 最初に: 言語サーバの設定はプロジェクトごとに行う @@ -51,14 +51,6 @@ Codex では `--context codex` で起動します(`.codex.mcp.json`)。 **Codex では、プラグインの hook は信頼するまで動きません**(Codex 0.156 で実測。何も表示されずに走らないだけになる)。対話の Codex で `/hooks` を開き、mcp-serena の hook を信頼してください。起動時に出る「Review hooks」の案内からも信頼できます。 -### Kiro CLI - -repository を clone した後、対象のプロジェクトの根で installer を実行します。 - -```bash -bash plugins/mcp/mcp-serena/dev.kiro/install.sh -``` - ### 前提 - `uv`(`uvx`)。Serena は `uvx --from serena-agent==1.7.0` で起動します @@ -75,9 +67,6 @@ bash plugins/mcp/mcp-serena/dev.kiro/install.sh # Codex codex plugin marketplace upgrade ai-plugins codex plugin add mcp-serena@ai-plugins - -# Kiro CLI -bash plugins/mcp/mcp-serena/dev.kiro/install.sh ``` ## 公式の `serena` プラグインと併用しない @@ -98,7 +87,7 @@ bash plugins/mcp/mcp-serena/dev.kiro/install.sh | ランタイム | 定義 | 文脈 | | --- | --- | --- | -| Claude Code / Kiro CLI | `.mcp.json` | `claude-code` | +| Claude Code | `.mcp.json` | `claude-code` | | Codex | `.codex.mcp.json` | `codex` | どちらも `SERENA_HOME=.serena`(起動したディレクトリからの相対)で動きます。worktree ごとに diff --git a/plugins/ndf/README.md b/plugins/ndf/README.md index 7d5bbbcfb..66a059eaf 100644 --- a/plugins/ndf/README.md +++ b/plugins/ndf/README.md @@ -4,14 +4,13 @@ PR 運用、レビュー、調査、実装計画、仕様書化、開発方法 完了判定)、Docker container access、statusline、外部 AI 委譲、Slack 通知を提供します。 配布物は `plugins/ndf/` の 1 ディレクトリにまとまっています。Skill の実体は `skills/` の -1 箇所だけで、どのランタイムへ配るかは `manifests/*-skills.txt` が決めます。 +1 箇所だけで、どのランタイムへ配るかは `manifests/*-skills.txt` が決めます。配布先は Claude Code と +Codex の 2 つです。Kiro CLI と agy へは配らず、cross-review / cross-refactoring の相手役として起動するだけです。 | ランタイム | 公開 Skill | マニフェスト | | --- | --- | --- | | Claude Code | 49 個 | `.claude-plugin/plugin.json` | | Codex | 45 個 | `.codex-plugin/plugin.json` | -| Kiro CLI | 46 個 | `dev.kiro/install.sh`(プラグイン機構が無いため installer で導入) | -| agy | 45 個 | `dev.agy/plugin.json`(取得元の登録が無いため clone から導入) | ## レイアウト @@ -26,23 +25,9 @@ plugins/ndf/ ├── hooks/claude.json # Claude Code の PreToolUse / SessionStart / Stop hook ├── hooks/codex.json # Codex の PreToolUse / SessionStart / Stop hook ├── scripts/ # hook と Skill から呼ぶスクリプト -├── dev.kiro/ # Kiro CLI の installer・エージェント定義・プロンプト -├── dev.agy/ # agy のマニフェスト・hook 定義・配布 Skill への symlink └── README.md ``` -`dev.kiro` と `dev.agy` は Agent Plugins 仕様 §8.2 が定めるクライアント拡張ディレクトリです。 - -`dev.agy/skills/` は `manifests/agy-skills.txt` から生成する symlink です -(`scripts/build-runtime-plugins.sh`)。`dev.agy/scripts` は `scripts/` を指し、実体を -4 ランタイムで共有します。**agy は配る Skill を絞る手段を利用者側の設定にしか持たない**ため、 -絞り込みはここへ何を並べるかで表します。 - -`dev.agy/agents/` は `agents/` から同じスクリプトが生成する写しです。agy は `model` に tier 名 -(`flash` / `pro` / `inherit`)しか受け付けないため、frontmatter の `model:` の 1 行だけを -対応表(`sonnet` → `flash`、`opus` → `pro`)で置き換えます。対応表は `scripts/lib/agy_agents.py` -が持ちます。写しは手で直さず、`agents/` を直してから生成し直します(`--check` が食い違いを落とします)。 - **`skills/` に置く Skill は、少なくとも 1 つの manifest へ載せます。** 配らない Skill の 置き場所(`optional-skills/`)は v10.5.0 で無くしました。どこからも起動できない Skill を 置き続ける理由が、公開数を一定に保つことだけだったためです。 @@ -67,62 +52,17 @@ codex plugin marketplace add https://github.com/devbasex/ai-plugins codex plugin add ndf@ai-plugins ``` -### Kiro CLI - -Kiro CLI にはプラグイン機構が無いため、installer が `.kiro/` へ配置します。リポジトリ root で -実行してください。 - -```bash -# 基本(Skills + steering + agentSpawn hook) -bash plugins/ndf/dev.kiro/install.sh - -# Slack 通知も有効化 -bash plugins/ndf/dev.kiro/install.sh --with-slack - -# Slack 通知 + Kiro 側 Codex MCP 設定も生成 -bash plugins/ndf/dev.kiro/install.sh --with-slack --with-codex - -# 書き込みを行わず内容だけ確認 -bash plugins/ndf/dev.kiro/install.sh --dry-run -``` - -導入後は `kiro-cli chat --agent ndf` で起動します。installer は `manifests/kiro-skills.txt` に -載る Skill だけを `.kiro/skills/` へ symlink します。版数は `.claude-plugin/plugin.json` から -読み取り、実行時に `NDF バージョン: <版数>` として表示します。 - -導入済みプロジェクトの版数は次で確認できます。 - -```bash -python3 -c "import json;print(json.load(open('.kiro/agents/ndf.json'))['description'])" -# => NDF統合開発エージェント(Kiro CLI用 / v10.27.3) -``` - -### agy +### agy・Kiro CLI への配布の終了 -agy にも取得元の登録がありません。clone した `dev.agy` を直接導入します。**更新の副コマンドが -無い**ため、新しい版へは入れ直します(`install` は上書きするが、消したファイルは実体に残る)。 +agy と Kiro CLI へは配りません(#2231)。最後に配ったのは v10.27.3 です。cross-review と cross-refactoring は、 +Claude Code か Codex をホストにして agy・Kiro CLI を相手役として今までどおり起動します。相手役に配布物は要りません。 -```bash -agy plugin install plugins/ndf/dev.agy # 初回 -agy plugin uninstall ndf && agy plugin install plugins/ndf/dev.agy # 新しい版へ -``` - -導入すると `manifests/agy-skills.txt` に載る Skill 45 個と、エージェント 11 個(専門 8 個と、3 層の定義 3 個(supervisor 2・worker 1))、hook 1 個が -`~/.gemini/config/plugins/ndf/` へコピーされます。symlink は実体へ解決されてコピーされるため、 -clone を消しても導入した内容は残ります。 - -**hook はコピーされるだけで、agy はそれを読み込みません**(agy 1.1.26 で実測)。読む先は -`~/.gemini/config/hooks.json` の 1 か所だけです。次を実行して差し込みます。**冪等で、他の -項目には触れません**(`--dry-run` で内容を確認でき、`--uninstall` で外せます)。 +前の版で入れた手元は、こちらからは消しません。残すか消すかを選べます。 -```bash -bash plugins/ndf/dev.agy/install-hooks.sh -``` - -```bash -agy plugin list -# => {"imports":[{"name":"ndf","source":"antigravity","components":["skills","agents","hooks"]}]} -``` +| ランタイム | 残すとき | 消すとき | +| --- | --- | --- | +| agy | 導入の写し(`~/.gemini/config/plugins/ndf/`)だけで動き続ける。新しい版は届かない | 写しが残るうちに `bash ~/.gemini/config/plugins/ndf/install-hooks.sh --uninstall` で `~/.gemini/config/hooks.json` の項目を外し、`agy plugin uninstall ndf` を打つ | +| Kiro CLI | `.kiro/agents/ndf.json` の hook は clone の `plugins/ndf/scripts/` を呼ぶため、clone を進めても動く。Skill を固定したいときは clone を `ndf--v10.27.3` のタグへ戻す | `ndf` を既定のエージェントにしたなら `kiro-cli agent set-default kiro_default` で戻し、`.kiro/agents/ndf.json`・`.kiro/steering/ndf-policies.md`・`.kiro/prompts/` の NDF の 5 本(`--with-codex` で入れたなら `codex.md` も)・`.kiro/skills/` の NDF への symlink(mcp-* を入れたなら `.kiro/mcp_runtime/` と `.kiro/agents/default.json` の該当の項目も)を消す | ## 更新情報 @@ -137,34 +77,22 @@ Skill 名は変わらないため `/playwright-` まで打てば候補に出ま ```bash /plugin install playwright-kit@ai-plugins # Claude Code codex plugin add playwright-kit@ai-plugins # Codex -bash plugins/playwright-kit/dev.kiro/install.sh # Kiro CLI ``` ## Hooks -### worktree 運用(4 ランタイム共通) +### worktree 運用(Claude Code と Codex) 開発の変更を `.worktrees/` の worktree の中で行い、リポジトリを clone したディレクトリ (メインディレクトリ)では行わない運用を支えます。**編集は止めません。** 案内が出ても操作は成立します。 -| 起きること | 担う hook | Claude Code | Codex | Kiro CLI | agy | -| --- | --- | --- | --- | --- | --- | -| メインディレクトリの保護対象パスを編集しようとすると案内が出る | tool 実行前 | `PreToolUse` | `PreToolUse` | — | `PreToolUse` | -| worktree で作業する旨の案内がプロンプトごとに出る | プロンプト送信時 | — | — | `userPromptSubmit` | — | -| メインディレクトリに残った未コミット変更が提示される | セッション開始時 | `SessionStart` | `SessionStart` | `agentSpawn` | `PreInvocation` | -| メインディレクトリのブランチが稼働中の worktree へ追従する(既定では動かさない。worktree の設定の `follow_branch: true` で有効にする) | セッション開始時 | `SessionStart` | `SessionStart` | `agentSpawn` | `PreInvocation` | - -Kiro CLI に tool 実行前の案内が無いのは、この事象でモデルへ案内を渡す手段が終了コード 2 に -限られ、それが tool の実行を拒否するためです。拒否しない方針のもとでは置けないため、パスを -見ない案内をプロンプト送信時の hook が担います。 - -**agy の 2 つは `install-hooks.sh` を実行するまで届きません。** agy はプラグインの -`hooks.json` を読み込まないためです(「インストール / agy」を参照)。 +| 起きること | 担う hook | Claude Code | Codex | +| --- | --- | --- | --- | +| メインディレクトリの保護対象パスを編集しようとすると案内が出る | tool 実行前 | `PreToolUse` | `PreToolUse` | +| メインディレクトリに残った未コミット変更が提示される | セッション開始時 | `SessionStart` | `SessionStart` | +| メインディレクトリのブランチが稼働中の worktree へ追従する(既定では動かさない。worktree の設定の `follow_branch: true` で有効にする) | セッション開始時 | `SessionStart` | `SessionStart` | -**agy は案内を作る時点と渡せる時点が離れています。** tool 実行前の hook がモデルへ文言を返す -口は拒否のときにしか働かないため、案内はセッションの記録へ積み、次のモデル呼び出しの前に -`injectSteps` で渡します。セッション開始時にあたる事象も持たないため、モデル呼び出しの通し番号が -0 のときを開始時として扱います。 +前の版で agy・Kiro CLI へ入れた手元の hook も、同じ `scripts/` の入口を呼び続けます(「agy・Kiro CLI への配布の終了」)。 **この仕組みはリポジトリ側の設定ファイル `.ndf/worktree.json` があるときだけ動きます。** 設定が無いリポジトリでは、いずれの hook も何も出力せず終了コード 0 で終わります。 @@ -206,8 +134,6 @@ bash <プラグインのパス>/scripts/worktree-setup.sh init | --- | --- | --- | | Claude Code | hook + 規約 | hook + 再開コマンド | | Codex | 規約だけ | 再開コマンドだけ | -| Kiro CLI | 規約だけ | 再開コマンドだけ | -| agy | 規約だけ | 再開コマンドだけ | 規約は `skills/development-workflow/references/waiting.md`(待ち方)と `skills/development-workflow/references/context-window.md`(会話を切る)にあります。 @@ -221,7 +147,7 @@ bash <プラグインのパス>/scripts/worktree-setup.sh init プロジェクトごとの働き方は `.ndf/ask.json` で変えます(無ければ `enforce`)。今の振る舞いへ戻すには `{"version": 1, "mode": "off"}` を書きます。 -区分の表と宣言の形は `skills/development-workflow/references/asking.md`、ランタイムごとの扱い(Codex・Kiro CLI・agy は対象外)は +区分の表と宣言の形は `skills/development-workflow/references/asking.md`、ランタイムごとの扱い(Codex は対象外)は [docs/ndf-plugin-reference.md の「承認ゲートの外の問い合わせ」](../../docs/ndf-plugin-reference.md#承認ゲートの外の問い合わせ) にあります。 ### 学習の設定を Off にする(Claude Code と Codex) @@ -300,8 +226,6 @@ Codex の Stop・PermissionRequest hook(`hooks/codex.json`)は `NDF_CODEX_SL ません。** `~/.codex/config.toml` の `[hooks.state]` に対象 hook の `enabled = true` が要ります。 `/hooks` で対象 hook を確認し、利用するプロジェクトで有効化してください。 -Kiro CLI では installer が `.kiro/agents/ndf.json` の `hooks` を生成します。 - ## Slack 通知 利用者の回答か承認が無いと進まない時点でだけ、Slack へ知らせます。応答が終わっても、待っていなければ @@ -379,9 +303,6 @@ NDF_CODEX_SLACK_NOTIFY=true 機密値は `.env` などで管理し、リポジトリへコミットしないでください。通知の記録(同じ待ちを 2 度送らないための 直前の 1 件)は `${XDG_STATE_HOME:-~/.local/state}/ndf/wait-notify/` にセッションごとに置き、7 日で消します。 -**Kiro は `install.sh --with-slack` を打ち直してください。** 通知の入口は `scripts/wait-notify.py` の 1 本です。 -`stop` hook が配布物に無い `scripts/slack-notify.js` を指す `.kiro/agents/ndf.json` では、通知が届きません。 - ## 外部 AI 委譲 `/ndf:external-ai` skill または `corder` エージェントから外部 AI 委譲を使う場合は、利用環境に @@ -479,12 +400,6 @@ codex plugin list | grep 'ndf@ai-plugins' 配布した Skill は抑止すると `$` も効きません**。この差は codex-cli 0.146.1 で実測した もので、公式ドキュメントには記載がありません。 -## Kiro CLI の詳細 - -installer の主なオプション・既定エージェントの切り替え・導入スコープ・旧バージョンからの -移行・再インストール時に保持される設定と、Kiro CLI 側の制限は -[docs/kiro-cli.md](docs/kiro-cli.md) にあります。 - ## 実機検証の記録 kiro-cli の実機検証と、Skill 数がコンテキスト量へ与える影響の実測は diff --git a/plugins/ndf/scripts/tests/test_hook_deadline.py b/plugins/ndf/scripts/tests/test_hook_deadline.py index cbbf4ef5a..877532aaf 100644 --- a/plugins/ndf/scripts/tests/test_hook_deadline.py +++ b/plugins/ndf/scripts/tests/test_hook_deadline.py @@ -9,7 +9,6 @@ import json import os -import re import subprocess import sys import time @@ -135,4 +134,3 @@ def test_the_hook_limit_leaves_the_margin(rel): limits = _hook_timeouts(PLUGIN / rel) assert limits assert all(deadline.DEADLINE_SECONDS + deadline.MARGIN_SECONDS <= t for t in limits) - diff --git a/plugins/ndf/skills/AUTHORING.md b/plugins/ndf/skills/AUTHORING.md index b95157cd7..271b520ab 100644 --- a/plugins/ndf/skills/AUTHORING.md +++ b/plugins/ndf/skills/AUTHORING.md @@ -1,7 +1,8 @@ # Skill 執筆規約 -`plugins/ndf/skills/` は 4 ランタイム(Claude Code / Codex / Kiro / agy)へ配布する Skill の -編集元である。ここでは frontmatter の書き方を規約として定める。本文の書き方は各 `SKILL.md` +`plugins/ndf/skills/` は Claude Code と Codex へ配布する Skill の編集元である。Kiro と agy へは配らないが、 +cross-refactoring が担当の worktree へ写す手順書(`refactoring` など)を相手役の Kiro・agy が Skill として読むため、 +下の表の Kiro・agy の列(可搬性の規則)は残す。ここでは frontmatter の書き方を規約として定める。本文の書き方は各 `SKILL.md` に委ねるが、**本文のうち、実行先のリポジトリに依存する部分は規約が扱う**(「対象リポジトリを 仮定しない」)。手順の中身が正しいかと、その手順が ai-plugins でしか成立しないかは別の問題で、 後者は Skill を書く人ごとに判断が分かれるためである。 @@ -33,7 +34,7 @@ python3 scripts/check-skill-repo-assumptions.py --report # 走査の規模と ## `description` を単一の真実とする -4 ランタイムに共通する土台は Agent Skills 仕様の 6 項目(`name` / `description` / `license` / +読む 4 ランタイムに共通する土台は Agent Skills 仕様の 6 項目(`name` / `description` / `license` / `compatibility` / `metadata` / `allowed-tools`)である。ただし共通に効く度合いは項目ごとに異なる。 | 項目 | 4 ランタイムでの扱い | @@ -139,11 +140,9 @@ description: "Delete merged branches and worktrees, stopping only where git refu ([棚卸の計画](../../../issues/old/ndf-development-skills/07-tasks.md) の Task 0-8) - 「常時注入のみ」に相当する機能は Codex と Kiro と agy にない。3 つとも `user-invocable: false` を解釈せず、この分類の Skill も通常の Skill として扱う。唯一の対象で - ある `ndf-policies` は 4 ランタイムすべてへ配布している(`plugins/ndf/manifests/`)ため、 - Codex と agy では暗黙起動されうる。したがってこの分類の Skill は `description` に**「知識として参照する。手順として - 実行しない」旨を明記する**。Kiro は Skill として配らず `.kiro/steering/` へ常時指示として - 置き換えることで回避する([棚卸の計画](../../../issues/old/ndf-development-skills/07-tasks.md) - の Task 0-9)。`description` の書き換えは同計画の Task 0-10 で行う + ある `ndf-policies` は Codex へも配布している(`plugins/ndf/manifests/`)ため、 + Codex では暗黙起動されうる。したがってこの分類の Skill は `description` に**「知識として参照する。手順として + 実行しない」旨を明記する**([棚卸の計画](../../../issues/old/ndf-development-skills/07-tasks.md) の Task 0-10) - **Claude Code では** `disable-model-invocation: true` の Skill は `description` がコンテキスト へ載らず、`user-invocable: false` は載る。Codex と Kiro と agy にはこのキーがなく `description` は常に読まれるため、明示指示専用にする Skill は `description` 自体へ「利用者が明示的に指示した @@ -284,9 +283,7 @@ Skill 名の部分だからである。ただし配布先に何が入ってい | `SKILL.md` 行数 | 500 行。超えるものは補助ファイルへ分割 | 仕様の推奨、コンパクション対策 | | `SKILL.md` 本文 | 5,000 トークン | 仕様の推奨 | | Claude Code の初期 Skill 一覧の合計 | **27,900 文字**(10,000 トークン = コンテキストの 1%)。1 項目は `description` + `when_to_use` を合わせて 1,536 文字で切り詰め | Claude Code 公式ドキュメント。既定モデル Opus 5 のコンテキスト 1,000,000 から算出。文字数は較正した換算比による | -| Codex の初期 Skill 一覧の合計 | **15,177 文字**(5,440 トークン = コンテキストの 2%) | Codex 公式ドキュメント。既定モデル gpt-5.6-sol のコンテキスト 272,000 から算出。**4 ランタイムで最も厳しく、実質の制約になる** | -| Kiro の初期 Skill 一覧の合計 | **27,900 文字**(10,000 トークン = コンテキストの 1%) | 公式ドキュメントに規定が無いため、コンテキスト長が同じ Claude Code の 1% を借りる。既定モデル auto のコンテキスト 1,000,000 から算出 | -| agy の初期 Skill 一覧の合計 | **27,900 文字**(10,000 トークン = コンテキストの 1%) | 公式ドキュメントに規定が無いため、Kiro と同じく Claude Code の 1% を借りる。既定モデルは Gemini 3.x Flash 系で、コンテキスト長を出す手段が見当たらないため 1,000,000 を前提に置く | +| Codex の初期 Skill 一覧の合計 | **15,177 文字**(5,440 トークン = コンテキストの 2%) | Codex 公式ドキュメント。既定モデル gpt-5.6-sol のコンテキスト 272,000 から算出。**配布先の 2 ランタイムで厳しい側で、実質の制約になる** | | 全 Skill の frontmatter 合計 | 20,000 文字(**警告のみ**) | リポジトリ固有の目安。ランタイムが課す制約ではないため、超過しても警告にとどめる。**plugin family をまたいだ合計**。値は Codex の予算から逆算する([目安の決め方](#frontmatter-合計の目安は-codex-の予算から逆算する)) | ### 予算はトークンで効く @@ -333,7 +330,6 @@ Skill を増減したときや、既定モデルが変わったときに再実 | --- | --- | ---: | --- | ---: | ---: | | Claude Code | Opus 5 | 1,000,000 | 1% | 10,000 | 27,900 | | Codex | gpt-5.6-sol | 272,000 | 2% | 5,440 | 15,177 | -| Kiro | auto | 1,000,000 | 1%(Claude Code から借りる) | 10,000 | 27,900 | 文字換算は較正値(2026-09-01 実測で 2.79 文字/トークン)による。`--calibrate` を実行すると この値は更新される。 @@ -379,19 +375,6 @@ Skill の一覧 7.4k は、他のプラグインと組み込みを含む全量 コンテキストの内訳を測り、上の表を差し替えたうえで値を決める。据え置いたまま Skill を足すと 警告が出続け、目安として働かなくなる。 -**Kiro の規定は Claude Code から借りている。** 公式ドキュメント(kiro.dev/docs/skills)に一覧 -予算の記述が無いため、既定モデル `auto` のコンテキスト長が同じ 1,000,000 である Claude Code の -1% を当てる。憶測で独自の値を置くより、同じ土俵の実在する規定へ揃えるほうが根拠が残る。 -コンテキスト 1,000,000 は過去の実測の記録で、kiro-cli 2.20.1 では同じ手段(`--list-models`)を -取れない。値を更新するときは、その時点で一覧を取れる手段から確かめ直す。 -**NDF は Kiro でも 1M コンテキストのモデルだけを対象とする**(`dev.kiro/install.sh` が導入時に -この前提を出力する。モデルの一覧を取る手段が kiro-cli の版によって変わるため、機械でのチェックは -置いていない)。 - -起動時に読み込むコンテキスト量(Kiro が全 `SKILL.md` を読む合計)に**上限は置かない**。 -コンテキスト長が同じ Claude Code に上限を置かないため、Kiro だけを縛る根拠が無い。数は smoke test のログへ残るため、増え方は -後から追える。 - **コンテキストが最も長い Claude Code ではなく、Codex が全体の制約になる。** 比率が 2 倍でも コンテキストが 1/3.7 のため、予算は Claude Code の約半分にしかならない。Skill を増やすときは Codex の 5,440 文字を基準に考える。 @@ -405,7 +388,6 @@ Claude Code 側は `skillListingBudgetFraction` 設定または `SLASH_COMMAND_T | --- | :-: | :-: | :-: | :-: | | Claude Code | ○ | ○ | ○(`description` と合算して 1,536 文字で切り詰め) | **×** | | Codex | ○ | ○ | × | **○** | -| Kiro | ○ | ○ | × | 規定なし(多い側で見積もる) | パスを載せるのは Codex だけである(*"In Codex, the initial list also includes each skill's file path."*)。Claude Code の公式記述は *"a listing of skill names and descriptions"* で @@ -429,9 +411,9 @@ Codex は起動時に全 Skill の `name` と `description` とファイルパ 超過分を逃がす。 逃がし先は配布先で選ぶ。Claude Code だけに配布する Skill は `when_to_use` へ移してよい。 -一方 `when_to_use` は Codex と Kiro と agy では読まれないため、この 3 ランタイムへも配布する -Skill のトリガ語を `when_to_use` へ移すと、そのランタイムでは一覧に載らず暗黙起動に効かなくなる。 -4 ランタイムへ配布する Skill はトリガ語を `description` に残したまま要約し、手順の説明を +一方 `when_to_use` は Codex では読まれないため、Codex へも配布する +Skill のトリガ語を `when_to_use` へ移すと、Codex では一覧に載らず暗黙起動に効かなくなる。 +Codex へも配布する Skill はトリガ語を `description` に残したまま要約し、手順の説明を 本文へ逃がす。 Claude Code のコンパクション後は、呼び出し済み Skill の先頭 5,000 トークンのみが再添付され、 diff --git a/plugins/ndf/skills/release-verification/SKILL.md b/plugins/ndf/skills/release-verification/SKILL.md index 2e100dd8f..b7f5d0649 100644 --- a/plugins/ndf/skills/release-verification/SKILL.md +++ b/plugins/ndf/skills/release-verification/SKILL.md @@ -66,7 +66,7 @@ allowed-tools: ```bash python3 "$SCRIPTS/release-verification-steps.py" verify-install --ref --expect <版> \ - [--plugins ndf,...] [--runtimes claude,codex,kiro] --root . + [--plugins ndf,...] [--runtimes claude,codex] --root . ``` 隔離した HOME で ref から導入し、導入された版と、前の正式版のタグから変わったファイルの中身が diff --git a/plugins/playwright-kit/README.md b/plugins/playwright-kit/README.md index b83d17fcf..15c024d66 100644 --- a/plugins/playwright-kit/README.md +++ b/plugins/playwright-kit/README.md @@ -1,9 +1,8 @@ # playwright-kit Plugin -Playwright による E2E テストの計画・実装・証跡管理を提供するプラグインです。3 ランタイム分の +Playwright による E2E テストの計画・実装・証跡管理を提供するプラグインです。Claude Code と Codex の 配布物を `plugins/playwright-kit/` の 1 ディレクトリにまとめており、Skill の実体は `skills/` の -1 箇所だけです。ランタイム固有のファイルは `.claude-plugin/`(Claude Code)と `dev.kiro/` -(Kiro CLI)に分かれ、Codex はルートの `plugin.json`(Agent Plugins 形式)を読みます。 +1 箇所だけです。Claude Code は `.claude-plugin/` を、Codex はルートの `plugin.json`(Agent Plugins 形式)を読みます。 NDF プラグイン v6.1.0 までは NDF に同梱していました。**Skill 名は変えていない**ため、 `/playwright-` まで打てば従来どおり 4 個が候補に出ます。 @@ -36,24 +35,6 @@ codex plugin marketplace add devbasex/ai-plugins codex plugin add playwright-kit@ai-plugins ``` -### Kiro CLI - -Kiro にはプラグイン機構がないため、installer が `.kiro/skills/` へ symlink を張ります。 - -```bash -# プロジェクトへ導入(既定) -bash plugins/playwright-kit/dev.kiro/install.sh - -# ホーム配下へ導入 -bash plugins/playwright-kit/dev.kiro/install.sh --scope global - -# 書き込みを行わず内容だけ確認 -bash plugins/playwright-kit/dev.kiro/install.sh --dry-run -``` - -NDF の installer と違い、エージェント定義・常時指示・プロンプト・フックは扱いません -(このプラグインは Skill だけを配ります)。 - ### 更新 ```bash @@ -64,9 +45,6 @@ NDF の installer と違い、エージェント定義・常時指示・プロ # Codex codex plugin marketplace upgrade ai-plugins codex plugin add playwright-kit@ai-plugins - -# Kiro CLI -bash plugins/playwright-kit/dev.kiro/install.sh ``` ## 更新情報 diff --git a/scripts/runtime-smoke-latest.py b/scripts/runtime-smoke-latest.py index 2d0631316..b7775e5eb 100755 --- a/scripts/runtime-smoke-latest.py +++ b/scripts/runtime-smoke-latest.py @@ -68,7 +68,6 @@ def resolve_codex() -> list[tuple[str, str]]: return [("CODEX_VERSION", _npm_latest("@openai/codex"))] - RESOLVERS = {"claude": resolve_claude, "codex": resolve_codex} diff --git a/tests/runtime-smoke/README.md b/tests/runtime-smoke/README.md index f4ddbd985..11644129f 100644 --- a/tests/runtime-smoke/README.md +++ b/tests/runtime-smoke/README.md @@ -1,13 +1,11 @@ # Runtime Smoke Tests -`scripts/runtime-smoke-test.sh` runs Claude, Codex, Kiro, and agy plugin smoke tests in disposable Docker containers. The host home directory and credential directories are not mounted. +`scripts/runtime-smoke-test.sh` runs Claude and Codex plugin smoke tests in disposable Docker containers. The host home directory and credential directories are not mounted. ```bash bash scripts/runtime-smoke-test.sh bash scripts/runtime-smoke-test.sh --runtime claude bash scripts/runtime-smoke-test.sh --runtime codex -bash scripts/runtime-smoke-test.sh --runtime kiro -bash scripts/runtime-smoke-test.sh --runtime agy ``` Artifacts are written to `tmp/runtime-smoke//` by default: @@ -37,16 +35,14 @@ The authenticated GitHub workflow passes only `ANTHROPIC_API_KEY` and `OPENAI_AP ## CLI の版 -像は 4 ランタイムの CLI を、CLI の版の一覧 `tests/runtime-smoke/cli-versions.env` に書いた版で入れる。版を上げるときに書き換えるのはこのファイルだけで、Containerfile は版を持たない(`ARG` に既定値を置かない)。 +像は 2 ランタイムの CLI を、CLI の版の一覧 `tests/runtime-smoke/cli-versions.env` に書いた版で入れる。版を上げるときに書き換えるのはこのファイルだけで、Containerfile は版を持たない(`ARG` に既定値を置かない)。 | ランタイム | 一覧のキー | 取得先 | | --- | --- | --- | | claude | `CLAUDE_CODE_VERSION` | npm の `@anthropic-ai/claude-code@<版>` | | codex | `CODEX_VERSION` | npm の `@openai/codex@<版>` | -| kiro | `KIRO_CLI_VERSION`・`KIRO_CLI_SHA256_` | `https://prod.download.cli.kiro.dev/stable/<版>/kirocli--linux-musl.tar.gz`(sha256 で確かめる) | -| agy | `AGY_VERSION`・`AGY_URL_`・`AGY_SHA512_` | 一覧の url(sha512 で確かめる) | -`` は `uname -m` の値を大文字にしたもの(`X86_64`・`AARCH64`)。`scripts/runtime-smoke-test.sh` は一覧の各行を `--build-arg` で像の作成へ渡し、`--cli-versions <パス>` で別の一覧を受ける。像の作成は、取得・checksum・`--version` の出力から取り出した版数と一覧の版数の完全一致のどれかが落ちたら失敗する。 +`scripts/runtime-smoke-test.sh` は一覧の各行を `--build-arg` で像の作成へ渡し、`--cli-versions <パス>` で別の一覧を受ける。像の作成は、取得・`--version` の出力から取り出した版数と一覧の版数の完全一致のどれかが落ちたら失敗する。 配布の経路のスモーク(`runtime-plugin-smoke.yml` の `runtime-smoke (<ランタイム>)`)と認証つきのスモークは、一覧の版だけを入れる。その時点の最新の版は、別の workflow `runtime-plugin-smoke-latest.yml` が毎日 06:00 JST に develop の上で試す(Actions の画面から手でも起動できる。PR と push では走らず、必須のチェックに入らない)。落ちたら、題が `最新の版のスモークが落ちた: <ランタイム>` の課題をランタイムごとに 1 件作り、開いていればコメントを足す。課題には入れた版と実行の URL が載る。 @@ -55,10 +51,10 @@ The authenticated GitHub workflow passes only `ANTHROPIC_API_KEY` and `OPENAI_AP 1. 最新の版を一覧の形で出す(失敗の課題に載った版を使ってもよい) ```bash - python3 scripts/runtime-smoke-latest.py resolve --runtime + python3 scripts/runtime-smoke-latest.py resolve --runtime ``` -2. 出力の行で、`tests/runtime-smoke/cli-versions.env` の同じキーの行を置き換える(kiro と agy は checksum と url の行も) +2. 出力の行で、`tests/runtime-smoke/cli-versions.env` の同じキーの行を置き換える 3. 手元でスモークを走らせ、`tmp/runtime-smoke/<ランタイム>/version.log` の版が一覧の版と同じことを見る ```bash From ec06c12c49d015b9b22f6e89561b60be5e4ec813 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 31 Oct 2026 14:16:08 +0000 Subject: [PATCH 3/5] =?UTF-8?q?Test:=204=20=E3=83=A9=E3=83=B3=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=81=AE=E9=85=8D=E5=B8=83=E3=82=92=E5=89=8D?= =?UTF-8?q?=E6=8F=90=E3=81=AB=E3=81=97=E3=81=9F=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E3=80=81=E9=85=8D=E5=B8=83=E5=85=88=E3=81=AE=202=20?= =?UTF-8?q?=E3=81=A4=E3=81=B8=E5=90=88=E3=82=8F=E3=81=9B=E3=82=8B=EF=BC=88?= =?UTF-8?q?#2231=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - backlog-refinement・document-rebuild の配布のテストを claude・codex の manifest に限る - cross 系・external-ai の同梱のテストが数える manifest を 2 つにする - 必須のチェックの突き合わせのテストの ruleset の例から runtime-smoke (kiro)・(agy) を外す Co-Authored-By: Claude Opus 5.5 --- .../tests/test_issue_upkeep_layout.py | 4 ++-- .../tests/test_document_rebuild_stage.py | 2 +- scripts/tests/test_check_required_checks.py | 16 +++++++--------- .../tests/test_cross_review_codistribution.py | 2 +- scripts/tests/test_external_ai_codistribution.py | 2 +- scripts/tests/test_refactoring_codistribution.py | 2 +- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/plugins/ndf/skills/backlog-refinement/tests/test_issue_upkeep_layout.py b/plugins/ndf/skills/backlog-refinement/tests/test_issue_upkeep_layout.py index bf833e8f9..7289e8d61 100644 --- a/plugins/ndf/skills/backlog-refinement/tests/test_issue_upkeep_layout.py +++ b/plugins/ndf/skills/backlog-refinement/tests/test_issue_upkeep_layout.py @@ -36,9 +36,9 @@ def section(text: str, heading: str) -> str: return heading + (rest[: ends[0]] if ends else rest) -@pytest.mark.parametrize("runtime", ["claude", "codex", "kiro", "agy"]) +@pytest.mark.parametrize("runtime", ["claude", "codex"]) def test_the_skill_is_distributed(runtime: str) -> None: - """4 つの manifest すべてに載る。""" + """配布先の 2 つの manifest に載る。""" manifest = ROOT / "plugins" / "ndf" / "manifests" / f"{runtime}-skills.txt" names = [line.split("#", 1)[0].strip() for line in manifest.read_text(encoding="utf-8").splitlines()] assert "backlog-refinement" in names diff --git a/plugins/ndf/skills/development-workflow/tests/test_document_rebuild_stage.py b/plugins/ndf/skills/development-workflow/tests/test_document_rebuild_stage.py index 26012a089..149e13184 100644 --- a/plugins/ndf/skills/development-workflow/tests/test_document_rebuild_stage.py +++ b/plugins/ndf/skills/development-workflow/tests/test_document_rebuild_stage.py @@ -26,7 +26,7 @@ NEW_STAGE = "ドキュメント再構成" RENAMED = {"設計レビュー": "ドキュメントレビュー", "レビュー": "実装レビュー"} -RUNTIMES = ("claude", "codex", "kiro", "agy") +RUNTIMES = ("claude", "codex") def lib_body() -> str: diff --git a/scripts/tests/test_check_required_checks.py b/scripts/tests/test_check_required_checks.py index 611d7dbef..7b46124a5 100644 --- a/scripts/tests/test_check_required_checks.py +++ b/scripts/tests/test_check_required_checks.py @@ -21,14 +21,12 @@ assert spec.loader is not None spec.loader.exec_module(crc) -# 2026-10-05 の develop の必須のチェック 12 個 -REQUIRED_12 = [ +# 2026-10-05 の develop の必須のチェック 12 個から、配布をやめた runtime-smoke の kiro・agy を除いた 10 個(#2231) +REQUIRED_10 = [ "guard", "pytest", "runtime-smoke (claude)", "runtime-smoke (codex)", - "runtime-smoke (kiro)", - "runtime-smoke (agy)", "runtime-plugin-build-check", "skill-frontmatter-check", "skill-repo-assumptions-check", @@ -36,7 +34,7 @@ "markdown-link-check", "instruction-files-check", ] -# 承認ゲート 2 で ruleset へ足した 7 つ(#653)。足した後の必須は 19 個 +# 承認ゲート 2 で ruleset へ足した 7 つ(#653)。足した後の必須は 17 個 ADDED_7 = [ "doc-line-limit-check", "glossary-check", @@ -46,7 +44,7 @@ "script-structure-check", "skill-shell-vars-check", ] -REQUIRED_19 = REQUIRED_12 + ADDED_7 +REQUIRED_17 = REQUIRED_10 + ADDED_7 def rules(*contexts: str) -> list[dict]: @@ -117,12 +115,12 @@ def fake_run(cmd, **kw): def test_this_repository_has_only_the_awaiting_six_and_itself(tmp_path, capsys): - """AC6: 12 個の必須と必須にしないジョブの宣言だけで、差は 6 つと突き合わせのチェック自身だけ。""" + """AC6: 10 個の必須と必須にしないジョブの宣言だけで、差は 6 つと突き合わせのチェック自身だけ。""" allow = json.loads((REPO / "scripts" / "required-checks-allow.json").read_text(encoding="utf-8")) (tmp_path / "allow.json").write_text( json.dumps({"version": 1, "not_required": allow["not_required"], "awaiting": allow["awaiting"]}), encoding="utf-8" ) - (tmp_path / "rules.json").write_text(json.dumps(rules(*REQUIRED_12)), encoding="utf-8") + (tmp_path / "rules.json").write_text(json.dumps(rules(*REQUIRED_10)), encoding="utf-8") code = crc.main( [ "--root", @@ -404,7 +402,7 @@ def test_the_job_runs_only_on_pull_requests(): def test_cli_runs_as_a_script(tmp_path): rules_file = tmp_path / "rules.json" - rules_file.write_text(json.dumps(rules(*REQUIRED_19)), encoding="utf-8") + rules_file.write_text(json.dumps(rules(*REQUIRED_17)), encoding="utf-8") p = subprocess.run( [sys.executable, str(CHECK), "--root", str(REPO), "--repo", "o/r", "--branch", "develop", "--rules-file", str(rules_file)], capture_output=True, diff --git a/scripts/tests/test_cross_review_codistribution.py b/scripts/tests/test_cross_review_codistribution.py index 445cd67a0..70b3d2a35 100644 --- a/scripts/tests/test_cross_review_codistribution.py +++ b/scripts/tests/test_cross_review_codistribution.py @@ -20,7 +20,7 @@ def test_the_manifests_are_found() -> None: """配る基準そのものが見つかること。**空の一覧で通さない。**""" - assert len(MANIFESTS) >= 4, [p.name for p in MANIFESTS] + assert len(MANIFESTS) >= 2, [p.name for p in MANIFESTS] @pytest.mark.parametrize("manifest", MANIFESTS, ids=lambda p: p.stem) diff --git a/scripts/tests/test_external_ai_codistribution.py b/scripts/tests/test_external_ai_codistribution.py index 478120c9b..4780bbdfb 100644 --- a/scripts/tests/test_external_ai_codistribution.py +++ b/scripts/tests/test_external_ai_codistribution.py @@ -50,7 +50,7 @@ def test_some_skill_points_at_external_ai() -> None: def test_the_manifests_are_found() -> None: - assert len(MANIFESTS) == 4, [m.name for m in MANIFESTS] + assert len(MANIFESTS) == 2, [m.name for m in MANIFESTS] @pytest.mark.parametrize("manifest", MANIFESTS, ids=lambda p: p.name) diff --git a/scripts/tests/test_refactoring_codistribution.py b/scripts/tests/test_refactoring_codistribution.py index dd06e10a1..03f538f70 100644 --- a/scripts/tests/test_refactoring_codistribution.py +++ b/scripts/tests/test_refactoring_codistribution.py @@ -18,7 +18,7 @@ def test_the_manifests_are_found() -> None: """配る基準そのものが見つかること。**空の一覧で通さない。**""" - assert len(MANIFESTS) >= 4, [p.name for p in MANIFESTS] + assert len(MANIFESTS) >= 2, [p.name for p in MANIFESTS] @pytest.mark.parametrize("manifest", MANIFESTS, ids=lambda p: p.stem) From 063ca102a020a3697bd5cbfe70e1931478fad8d6 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 31 Oct 2026 14:16:17 +0000 Subject: [PATCH 4/5] =?UTF-8?q?Test:=20test=5Fhook=5Fwiring.py=20=E3=81=AE?= =?UTF-8?q?=E6=95=B4=E5=BD=A2=EF=BC=88#2231=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- plugins/ndf/skills/worktree/tests/test_hook_wiring.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/ndf/skills/worktree/tests/test_hook_wiring.py b/plugins/ndf/skills/worktree/tests/test_hook_wiring.py index abf98e533..c801697bb 100644 --- a/plugins/ndf/skills/worktree/tests/test_hook_wiring.py +++ b/plugins/ndf/skills/worktree/tests/test_hook_wiring.py @@ -42,4 +42,3 @@ def test_sessionstart_runs_the_session_script(runtime: str) -> None: config = json.loads((HOOKS_DIR / f"{runtime}.json").read_text(encoding="utf-8")) commands = [hook["command"] for entry in config["hooks"]["SessionStart"] for hook in entry["hooks"]] assert any("worktree-session.sh" in c for c in commands), commands - From bd68785cdecc50bff0ad0ff7ffc430c8ad16d055 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 31 Oct 2026 18:22:51 +0000 Subject: [PATCH 5/5] =?UTF-8?q?docs:=20=E8=BF=BD=E5=8A=A0=E3=81=97?= =?UTF-8?q?=E3=81=9F=E6=96=87=E3=81=8B=E3=82=89=E8=AA=B2=E9=A1=8C=E7=95=AA?= =?UTF-8?q?=E5=8F=B7=E3=81=AE=E7=94=B1=E6=9D=A5=E3=81=A8=E6=AF=94=E8=BC=83?= =?UTF-8?q?=E3=81=AE=E8=AA=9E=E3=82=92=E5=A4=96=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/ndf-plugin-reference.md | 2 +- docs/plugin-development-guide.md | 4 ++-- docs/specifications/runtime-plugin-distribution.md | 1 - plugins/ndf/README.md | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/ndf-plugin-reference.md b/docs/ndf-plugin-reference.md index 6fe587847..cfb73f872 100644 --- a/docs/ndf-plugin-reference.md +++ b/docs/ndf-plugin-reference.md @@ -101,7 +101,7 @@ Codex MCP サーバは廃止済みです。外部 AI 委譲は `/ndf:external-ai ### 相手役のランタイム -Kiro CLI と agy は配布先ではなく、cross-review のレビュー担当と cross-refactoring の提案・実装の担当(相手役)です。ホストは `plugins/ndf/scripts/lib/launch-cli.sh` で CLI を起動し、`--agent` もプラグインの導入先も渡しません。cross-refactoring の担当が読む手順書(`refactoring`・`tdd-cycle`・`quality-gates`)は、ホストの導入先から担当の worktree(Kiro CLI は `.kiro/skills`、agy は `.agents/skills`)へ写します(`plugins/ndf/skills/cross-refactoring/scripts/prepare-worktrees.sh`)。起動の形の正本は `plugins/ndf/skills/external-ai/references/cli-kiro.md`・`cli-agy.md` です。 +Kiro CLI と agy は cross-review のレビュー担当と cross-refactoring の提案・実装の担当(相手役)で、プラグインは配りません。ホストは `plugins/ndf/scripts/lib/launch-cli.sh` で CLI を起動し、`--agent` もプラグインの導入先も渡しません。cross-refactoring の担当が読む手順書(`refactoring`・`tdd-cycle`・`quality-gates`)は、ホストの導入先から担当の worktree(Kiro CLI は `.kiro/skills`、agy は `.agents/skills`)へ写します(`plugins/ndf/skills/cross-refactoring/scripts/prepare-worktrees.sh`)。起動の形の正本は `plugins/ndf/skills/external-ai/references/cli-kiro.md`・`cli-agy.md` です。 ## 承認ゲートの外の問い合わせ diff --git a/docs/plugin-development-guide.md b/docs/plugin-development-guide.md index 75922246f..58670522c 100644 --- a/docs/plugin-development-guide.md +++ b/docs/plugin-development-guide.md @@ -182,8 +182,8 @@ frontmatter と命名の書き方は [AUTHORING.md](../plugins/ndf/skills/AUTHOR | 9 | `plugins/ndf/README.md` のレイアウト図 | `唯一の実体(45 個)` | `check-doc-staleness.py` | | 10 | `plugin.json` 2 本(claude・codex)の `description` | `45 focused NDF skills` の形 | `validate-runtime-plugins.sh` | | 11 | `.claude-plugin/marketplace.json` の `ndf` の `description` | 10 と同じ形(Claude Code の数) | `validate-runtime-plugins.sh` | -| 12 | `plugins/ndf/README.md` の「Codex の暗黙起動抑止」の数と表 | `disable-model-invocation: true` のときだけ | **拾わない**(#611) | -| 13 | `docs/ndf-plugin-reference.md` の数 | 2 ランタイムの数と実体の数 | **拾わない**(#611) | +| 12 | `plugins/ndf/README.md` の「Codex の暗黙起動抑止」の数と表 | `disable-model-invocation: true` のときだけ | **拾わない** | +| 13 | `docs/ndf-plugin-reference.md` の数 | 2 ランタイムの数と実体の数 | **拾わない** | **数の形は 3 通りあります。** `README.md` は `core 45個`(空白なし)、`plugins/ndf/README.md` は `45 個`、`plugin.json` は `45 focused NDF skills` です。数だけを機械的に置き換えると、どれかに diff --git a/docs/specifications/runtime-plugin-distribution.md b/docs/specifications/runtime-plugin-distribution.md index bf9312ed0..1719a1ef7 100644 --- a/docs/specifications/runtime-plugin-distribution.md +++ b/docs/specifications/runtime-plugin-distribution.md @@ -324,6 +324,5 @@ MCP plugin を変更する場合: - [NDF プラグイン README](../../plugins/ndf/README.md) - [playwright-kit プラグイン README](../../plugins/playwright-kit/README.md) - [Runtime Smoke の CLI の版](runtime-smoke-cli-versions.md) -- `scripts/lib/agy_agents.py`(agy に配る定義の生成と model の対応表。#2231 で消した) - [Agent Plugins 仕様](https://github.com/agentplugins/agent-plugins-spec) - 課題 [#2087](https://github.com/devbasex/ai-plugins/issues/2087)(agy に配るエージェント定義) diff --git a/plugins/ndf/README.md b/plugins/ndf/README.md index 66a059eaf..2e760c76a 100644 --- a/plugins/ndf/README.md +++ b/plugins/ndf/README.md @@ -54,7 +54,7 @@ codex plugin add ndf@ai-plugins ### agy・Kiro CLI への配布の終了 -agy と Kiro CLI へは配りません(#2231)。最後に配ったのは v10.27.3 です。cross-review と cross-refactoring は、 +agy と Kiro CLI へは配りません。最後に配ったのは v10.27.3 です。cross-review と cross-refactoring は、 Claude Code か Codex をホストにして agy・Kiro CLI を相手役として今までどおり起動します。相手役に配布物は要りません。 前の版で入れた手元は、こちらからは消しません。残すか消すかを選べます。