Răsfoiți Sursa

fix: wire Kimi plugin into release metadata

Drew Ritter 3 luni în urmă
părinte
comite
f61300eac8

+ 1 - 0
.version-bump.json

@@ -4,6 +4,7 @@
     { "path": ".claude-plugin/plugin.json", "field": "version" },
     { "path": ".cursor-plugin/plugin.json", "field": "version" },
     { "path": ".codex-plugin/plugin.json", "field": "version" },
+    { "path": ".kimi-plugin/plugin.json", "field": "version" },
     { "path": ".claude-plugin/marketplace.json", "field": "plugins.0.version" },
     { "path": "gemini-extension.json", "field": "version" }
   ],

+ 27 - 1
README.md

@@ -11,7 +11,7 @@ If this sounds like someone you know, definitely send them our way.
 
 ## Quickstart
 
-Give your agent Superpowers: [Claude Code](#claude-code), [Antigravity](#antigravity), [Codex App](#codex-app), [Codex CLI](#codex-cli), [Cursor](#cursor), [Factory Droid](#factory-droid), [Gemini CLI](#gemini-cli), [GitHub Copilot CLI](#github-copilot-cli), [OpenCode](#opencode), [Pi](#pi).
+Give your agent Superpowers: [Claude Code](#claude-code), [Antigravity](#antigravity), [Codex App](#codex-app), [Codex CLI](#codex-cli), [Cursor](#cursor), [Factory Droid](#factory-droid), [Gemini CLI](#gemini-cli), [GitHub Copilot CLI](#github-copilot-cli), [Kimi Code](#kimi-code), [OpenCode](#opencode), [Pi](#pi).
 
 ## How it works
 
@@ -156,6 +156,32 @@ Superpowers is available via the [official Codex plugin marketplace](https://git
   copilot plugin install superpowers@superpowers-marketplace
   ```
 
+### Kimi Code
+
+Superpowers is available in Kimi Code's plugin marketplace.
+
+- Open Kimi Code's plugin manager:
+
+  ```text
+  /plugins
+  ```
+
+- Search for Superpowers and install it.
+
+- To install from this repository instead:
+
+  ```text
+  /plugins install https://github.com/obra/superpowers
+  ```
+
+- For unreleased validation against `dev`:
+
+  ```text
+  /plugins install https://github.com/obra/superpowers/tree/dev
+  ```
+
+- Detailed docs: [docs/README.kimi.md](docs/README.kimi.md)
+
 ### OpenCode
 
 OpenCode uses its own plugin install; install Superpowers separately even if you

+ 85 - 0
docs/README.kimi.md

@@ -0,0 +1,85 @@
+# Superpowers for Kimi Code
+
+Complete guide for using Superpowers with [Kimi Code](https://github.com/MoonshotAI/kimi-code).
+
+## Installation
+
+Superpowers is available in Kimi Code's plugin marketplace.
+
+Open the plugin manager:
+
+```text
+/plugins
+```
+
+Search for Superpowers and install it.
+
+You can also install from this repository:
+
+```text
+/plugins install https://github.com/obra/superpowers
+```
+
+For unreleased validation against `dev`, pin the branch explicitly:
+
+```text
+/plugins install https://github.com/obra/superpowers/tree/dev
+```
+
+Kimi Code applies plugin changes to new sessions. After installing, updating, enabling, disabling, or reloading a plugin, start a fresh session with `/new`.
+
+## How It Works
+
+The Kimi plugin manifest lives at `.kimi-plugin/plugin.json`.
+
+The manifest does three things:
+
+1. Points Kimi Code at the existing `skills/` directory.
+2. Loads `using-superpowers` at session start through `sessionStart.skill`.
+3. Provides Kimi-specific tool mapping through `skillInstructions`.
+
+Kimi Code reads Superpowers skills from this repository. There are no copied skills, symlinks, hooks, or extra runtime dependencies.
+
+## Tool Mapping
+
+Skills describe actions instead of hard-coding one runtime's tool names. On Kimi Code these resolve to:
+
+- "Ask the user" / "ask clarifying questions" -> `AskUserQuestion`
+- "Create a todo" / "mark complete in todo list" -> `TodoList`
+- "Dispatch a subagent" -> `Agent`
+- "Invoke a skill" -> Kimi Code's native `Skill` tool
+- "Read a file" / "write a file" / "edit a file" -> `Read`, `Write`, `Edit`
+- "Run a shell command" -> `Bash`
+- "Search the web" -> `WebSearch`
+
+## Updating
+
+Use Kimi Code's plugin manager:
+
+```text
+/plugins
+```
+
+Select Superpowers and update it from there. Start a fresh session with `/new` after updating.
+
+## Troubleshooting
+
+### Plugin not loading
+
+1. Run `/plugins info superpowers` and check diagnostics.
+2. Make sure the plugin is enabled.
+3. Start a fresh session with `/new` after install or update.
+
+### Direct GitHub install used an old release
+
+Kimi Code installs the latest GitHub release for a bare repository URL when one exists. To test unreleased changes before the next Superpowers release, install the branch explicitly:
+
+```text
+/plugins install https://github.com/obra/superpowers/tree/dev
+```
+
+### Skills not triggering
+
+1. Confirm `/plugins info superpowers` shows the plugin enabled.
+2. Start a fresh session with `/new`.
+3. Try the acceptance prompt: `Let's make a react todo list`. A working install should load `brainstorming` before writing code.

+ 2 - 1
docs/porting-to-a-new-harness.md

@@ -675,7 +675,7 @@ it. Distribution differs per harness ecosystem — find yours:
 |---|---|---|
 | Native plugin marketplace | Claude Code | Register in `.claude-plugin/marketplace.json`; users `/plugin install`. The external `superpowers-marketplace` repo is the source of truth users install from — see the release steps in `CLAUDE.md`. |
 | External marketplace fork, synced by script | Codex | `scripts/sync-to-codex-plugin.sh` rsyncs the tracked plugin files into a separate fork repo and opens a PR. Read its include/exclude list so you ship the right tree (it deliberately drops repo-internal dirs and other harnesses' dotdirs). |
-| Git-URL extension install | Gemini, OpenCode | Users install from a git URL (`gemini extensions install …`; an `opencode.json` `plugin` array entry). Document the exact command. |
+| Git-URL extension install | Gemini, Kimi Code, OpenCode | Users install from a git URL (`gemini extensions install …`; Kimi Code `/plugins install …`; an `opencode.json` `plugin` array entry). Document the exact command. |
 | Package-manifest fields | pi | Declared through fields in the repo-root `package.json`; users install via the harness's package command. |
 | Local installer (plugin install) | Antigravity (`agy`) | A small `install.sh` that runs the harness's own `agy plugin install` against a staging dir holding the manifest, the skills, and a generated `contextFileName` context file (the bootstrap). Everything arrives through the install mechanism — *not* by editing the user's config (see below). |
 
@@ -788,6 +788,7 @@ Use this as the live index; when in doubt, read the files, not this table.
 | Cursor | `.cursor-plugin/plugin.json` + `hooks/hooks-cursor.json` | shell hook → `hooks/session-start` (`additional_context`) | `references/claude-code-tools.md` | `tests/hooks/` | hand-authored |
 | Copilot CLI | (shares Claude Code hook path; `COPILOT_CLI` env) | shell hook → `hooks/session-start` (`additionalContext`) | `references/copilot-tools.md` | `tests/hooks/` | — |
 | Gemini CLI | `gemini-extension.json` + `GEMINI.md` | instructions file `@`-includes bootstrap + mapping | `references/gemini-tools.md` | — | `gemini extensions install` |
+| Kimi Code | `.kimi-plugin/plugin.json` | manifest `sessionStart.skill` loads `using-superpowers` | inline `skillInstructions` in manifest | `tests/kimi/` | marketplace or `/plugins install` GitHub URL |
 | OpenCode | `.opencode/plugins/superpowers.js` (declared via root `package.json` `main`) | in-process: `config` hook registers skills dir; `experimental.chat.messages.transform` injects user message | inline in `superpowers.js` | `tests/opencode/` | `opencode.json` plugin git URL |
 | pi | `.pi/extensions/superpowers.ts` | in-process: `resources_discover` registers skills; `context` event injects user message; lifecycle-flag + compaction-aware | `piToolMapping()` inline **and** `references/pi-tools.md` | `tests/pi/` | repo-root `package.json` fields |
 

+ 1 - 0
docs/testing.md

@@ -12,6 +12,7 @@ Live in `tests/`. Currently:
 - `tests/brainstorm-server/` — node test suite for the brainstorm server JS code.
 - `tests/opencode/` — bash tests for OpenCode plugin loading, bootstrap caching, and tool registration.
 - `tests/codex-plugin-sync/` — bash sync verification.
+- `tests/kimi/` — bash/Python checks for Kimi plugin manifest wiring.
 - `tests/claude-code/test-helpers.sh`, `analyze-token-usage.py` — utilities used by remaining bash tests.
 - `tests/claude-code/test-subagent-driven-development.sh` — agent-can-describe-SDD test (no drill counterpart; tests description-recall, not behavior).
 - `tests/claude-code/test-subagent-driven-development-integration.sh` — extended SDD integration with token analysis (drill covers the YAGNI subset; bash adds commit-count, Claude Code task-tracking, and token telemetry assertions).

+ 1 - 0
scripts/sync-to-codex-plugin.sh

@@ -52,6 +52,7 @@ EXCLUDES=(
   "/.gitattributes"
   "/.github/"
   "/.gitignore"
+  "/.kimi-plugin/"
   "/.opencode/"
   "/.pi/"
   "/.version-bump.json"

+ 10 - 0
tests/codex-plugin-sync/test-sync-to-codex-plugin.sh

@@ -175,6 +175,7 @@ write_upstream_fixture() {
 
     mkdir -p \
         "$repo/.codex-plugin" \
+        "$repo/.kimi-plugin" \
         "$repo/.private-journal" \
         "$repo/assets" \
         "$repo/evals/drill" \
@@ -210,6 +211,13 @@ EOF
   "name": "superpowers",
   "version": "$MANIFEST_VERSION"
 }
+EOF
+
+    cat > "$repo/.kimi-plugin/plugin.json" <<EOF
+{
+  "name": "superpowers",
+  "version": "$MANIFEST_VERSION"
+}
 EOF
 
     cat > "$repo/assets/superpowers-small.svg" <<'EOF'
@@ -267,6 +275,7 @@ EOF
 
     git -C "$repo" add \
         .codex-plugin/plugin.json \
+        .kimi-plugin/plugin.json \
         .gitignore \
         assets/app-icon.png \
         assets/superpowers-small.svg \
@@ -618,6 +627,7 @@ main() {
     assert_contains "$preview_output" "Version:  $MANIFEST_VERSION" "Preview uses manifest version"
     assert_not_contains "$preview_output" "Version:  $PACKAGE_VERSION" "Preview does not use package.json version"
     assert_contains "$preview_section" ".codex-plugin/plugin.json" "Preview includes manifest path"
+    assert_not_contains "$preview_section" ".kimi-plugin/plugin.json" "Preview excludes Kimi manifest from Codex sync"
     assert_contains "$preview_section" "assets/superpowers-small.svg" "Preview includes SVG asset"
     assert_contains "$preview_section" "assets/app-icon.png" "Preview includes PNG asset"
     assert_contains "$preview_section" "hooks/hooks-codex.json" "Preview includes Codex hook manifest"

+ 6 - 0
tests/kimi/run-tests.sh

@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+bash "$SCRIPT_DIR/test-plugin-manifest.sh"

+ 67 - 0
tests/kimi/test-plugin-manifest.sh

@@ -0,0 +1,67 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+MANIFEST="$REPO_ROOT/.kimi-plugin/plugin.json"
+
+python3 - "$MANIFEST" <<'PY'
+import json
+import sys
+from pathlib import Path
+
+manifest_path = Path(sys.argv[1])
+manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
+
+def assert_equal(actual, expected, label):
+    if actual != expected:
+        raise AssertionError(f"{label}: expected {expected!r}, got {actual!r}")
+
+def assert_present(text, needle, label):
+    if needle not in text:
+        raise AssertionError(f"{label}: missing {needle!r}")
+
+assert_equal(manifest.get("name"), "superpowers", "plugin name")
+assert_equal(manifest.get("skills"), "./skills/", "skills path")
+assert_equal(
+    manifest.get("sessionStart", {}).get("skill"),
+    "using-superpowers",
+    "sessionStart.skill",
+)
+
+instructions = manifest.get("skillInstructions")
+if not isinstance(instructions, str) or not instructions.strip():
+    raise AssertionError("skillInstructions must be a non-empty string")
+
+for token in [
+    "AskUserQuestion",
+    "TodoList",
+    "Agent",
+    "Skill",
+    "Read",
+    "Write",
+    "Edit",
+    "Bash",
+    "WebSearch",
+]:
+    assert_present(instructions, token, "skillInstructions")
+
+unsupported_fields = [
+    "tools",
+    "commands",
+    "hooks",
+    "apps",
+    "inject",
+    "configFile",
+    "config_file",
+    "bootstrap",
+]
+present_unsupported = sorted(field for field in unsupported_fields if field in manifest)
+if present_unsupported:
+    raise AssertionError(
+        "unsupported Kimi runtime fields present: "
+        + ", ".join(present_unsupported)
+    )
+
+print("Kimi plugin manifest looks good")
+PY