Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .kimi-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"sessionStart": {
"skill": "using-wukong-code"
},
"skillInstructions": "Kimi Code tool mapping for Wukong Code skills:\n\n- When a Wukong Code skill says to ask the user, ask clarifying questions, ask one question at a time, present multiple-choice options, use the terminal for a question, or wait for the user's choice, call Kimi Code's `AskUserQuestion` tool. Do not render those choices as plain assistant text unless `AskUserQuestion` is unavailable or the session is in auto permission mode.\n- For `AskUserQuestion`, provide 1 question with 2-4 concrete options when possible. Put the recommended option first and suffix its label with `(Recommended)`.\n- When a Wukong Code skill refers to `TodoWrite`, use Kimi Code's `TodoList` tool.\n- When a Wukong Code skill says `Task tool (general-purpose)` or asks you to dispatch an implementer/reviewer subagent, use Kimi Code's `Agent` tool with a Kimi subagent type. Do not pass `general-purpose` as `subagent_type`.\n- For implementation, code review, spec review, quality review, and filled Wukong Code subagent prompt templates, call `Agent` with `subagent_type: \"coder\"`, paste the fully filled prompt into `prompt`, and provide a short `description`.\n- For read-only codebase exploration that would take several searches, use `Agent` with `subagent_type: \"explore\"`.\n- For read-only planning or architecture design, use `Agent` with `subagent_type: \"plan\"`.\n- Keep dependent Wukong Code subagent steps sequential. Use multiple `Agent` calls, or `run_in_background: true` only when the work is independent and background agents are available.\n- When a Wukong Code skill refers to the `Skill` tool, use Kimi Code's native `Skill` tool.\n- Use Kimi Code's `Read`, `Write`, `Edit`, `Bash`, `Grep`, `Glob`, `FetchURL`, `WebSearch`, and MCP tools by their actual exposed names.\n- When a skill asks to search file contents, use `Grep`; when it asks to find files by path or pattern, use `Glob`; when it asks to fetch a URL, use `FetchURL`; when it asks to search the web, use `WebSearch`.",
"skillInstructions": "Kimi Code tool mapping for Wukong Code skills:\n\n- When a Wukong Code skill says to ask the user, ask clarifying questions, ask one question at a time, present multiple-choice options, use the terminal for a question, or wait for the user's choice, call Kimi Code's `AskUserQuestion` tool. Do not render those choices as plain assistant text unless `AskUserQuestion` is unavailable or the session is in auto permission mode.\n- For `AskUserQuestion`, provide 1 question with 2-4 concrete options when possible. Put the recommended option first and suffix its label with `(Recommended)`.\n- When a Wukong Code skill refers to `TodoWrite`, use Kimi Code's `TodoList` tool.\n- When a Wukong Code skill says `Task tool (general-purpose)` or asks you to dispatch an implementer/reviewer subagent, use Kimi Code's `Agent` tool with a Kimi subagent type. Do not pass `general-purpose` as `subagent_type`.\n- For implementation, code review, spec review, quality review, and filled Wukong Code subagent prompt templates, call `Agent` with `subagent_type: \"coder\"`, paste the fully filled prompt into `prompt`, and provide a short `description`.\n- For read-only codebase exploration that would take several searches, use `Agent` with `subagent_type: \"explore\"`.\n- For read-only planning or architecture design, use `Agent` with `subagent_type: \"plan\"`.\n- Keep dependent Wukong Code subagent steps sequential. Use multiple `Agent` calls, or `run_in_background: true` only when the work is independent and background agents are available.\n- When a Wukong Code skill refers to the `Skill` tool, use Kimi Code's native `Skill` tool.\n- Use Kimi Code's `Read`, `Write`, `Edit`, `Bash`, `Grep`, `Glob`, `FetchURL`, `WebSearch`, and MCP tools by their actual exposed names.\n- When a skill asks to search file contents, use `Grep`; when it asks to find files by path or pattern, use `Glob`; when it asks to fetch a URL, use `FetchURL`; when it asks to search the web, use `WebSearch`.\n\nProcess skills stay primary; Product Design is secondary. Conflicts keep the process gates. See `references/wukong-product-design-composition.md`.",
"interface": {
"displayName": "Wukong Code",
"shortDescription": "Planning, TDD, debugging, and delivery workflows for coding agents",
Expand Down
17 changes: 14 additions & 3 deletions .kimi-plugin/sync-skill-instructions.sh
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
#!/usr/bin/env bash
# Copy skills/using-wukong-code/references/kimi-tools.md into
# .kimi-plugin/plugin.json skillInstructions. Edit the markdown file, then run
# this script. Do not hand-edit the mapping string in plugin.json.
# .kimi-plugin/plugin.json skillInstructions. When
# skills/product-design/SKILL.md exists, append the composition pointer after
# the mapping. Edit the markdown files, then run this script. Do not
# hand-edit the mapping string in plugin.json.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
MANIFEST="$REPO_ROOT/.kimi-plugin/plugin.json"
TOOLS="$REPO_ROOT/skills/using-wukong-code/references/kimi-tools.md"
POINTER="$REPO_ROOT/skills/using-wukong-code/references/product-design-composition-pointer.md"
PD_SKILL="$REPO_ROOT/skills/product-design/SKILL.md"

python3 - "$MANIFEST" "$TOOLS" <<'PY'
python3 - "$MANIFEST" "$TOOLS" "$POINTER" "$PD_SKILL" <<'PY'
import json
import sys
from pathlib import Path

manifest_path = Path(sys.argv[1])
tools_path = Path(sys.argv[2])
pointer_path = Path(sys.argv[3])
pd_skill_path = Path(sys.argv[4])
text = tools_path.read_text(encoding="utf-8")
if text.endswith("\n"):
text = text[:-1]
if pd_skill_path.is_file() and pointer_path.is_file():
pointer = pointer_path.read_text(encoding="utf-8")
if pointer.endswith("\n"):
pointer = pointer[:-1]
text = f"{text}\n\n{pointer}"
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
manifest["skillInstructions"] = text
manifest_path.write_text(
Expand Down
18 changes: 17 additions & 1 deletion .opencode/plugins/wukong-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ const extractAndStripFrontmatter = (content) => {
return { frontmatter, content: body };
};

export const productDesignCompositionPointer = (skillsDir) => {
const skillPath = path.join(skillsDir, 'product-design', 'SKILL.md');
const pointerPath = path.join(
skillsDir,
'using-wukong-code',
'references',
'product-design-composition-pointer.md'
);
if (!fs.existsSync(skillPath) || !fs.existsSync(pointerPath)) {
return '';
}
return fs.readFileSync(pointerPath, 'utf8').replace(/\s+$/, '');
};

// Normalize a path: trim whitespace, expand ~, resolve to absolute
const normalizePath = (p, homeDir) => {
if (!p || typeof p !== 'string') return null;
Expand Down Expand Up @@ -86,6 +100,8 @@ export const WukongCodePlugin = async ({ client, directory }) => {
const fullContent = fs.readFileSync(skillPath, 'utf8');
const { content } = extractAndStripFrontmatter(fullContent);
const toolMapping = fs.readFileSync(toolsPath, 'utf8').replace(/\s+$/, '');
const pointer = productDesignCompositionPointer(wukongCodeSkillsDir);
const pointerBlock = pointer ? `\n\n${pointer}` : '';

_bootstrapCache = `<EXTREMELY_IMPORTANT>
You have wukong-code.
Expand All @@ -94,7 +110,7 @@ You have wukong-code.

${content}

${toolMapping}
${toolMapping}${pointerBlock}
</EXTREMELY_IMPORTANT>`;

return _bootstrapCache;
Expand Down
20 changes: 18 additions & 2 deletions .pi/extensions/wukong-code.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { readFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
Expand Down Expand Up @@ -64,6 +64,8 @@ function getBootstrapContent(): string | null {
const skillContent = readFileSync(bootstrapSkillPath, "utf8");
const body = stripFrontmatter(skillContent);
const toolMapping = readFileSync(piToolsPath, "utf8").replace(/\s+$/, "");
const pointer = productDesignCompositionPointer(skillsDir);
const pointerBlock = pointer ? `\n\n${pointer}` : "";
cachedBootstrap = `${EXTREMELY_IMPORTANT_MARKER}
${BOOTSTRAP_MARKER}

Expand All @@ -73,7 +75,7 @@ The using-wukong-code skill content is included below and is already loaded for

${body}

${toolMapping}
${toolMapping}${pointerBlock}
</EXTREMELY_IMPORTANT>`;
return cachedBootstrap;
} catch {
Expand All @@ -82,6 +84,20 @@ ${toolMapping}
}
}

export function productDesignCompositionPointer(targetSkillsDir: string): string {
const skillPath = resolve(targetSkillsDir, "product-design", "SKILL.md");
const pointerPath = resolve(
targetSkillsDir,
"using-wukong-code",
"references",
"product-design-composition-pointer.md",
);
if (!existsSync(skillPath) || !existsSync(pointerPath)) {
return "";
}
return readFileSync(pointerPath, "utf8").replace(/\s+$/, "");
}

export function stripFrontmatter(content: string): string {
const match = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
return (match ? match[1] : content).trim();
Expand Down
3 changes: 2 additions & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ Live in `tests/`. Currently:
- `tests/brainstorm-server/` — node test suite for the brainstorm server JS code.
- `tests/hooks/test-session-start.sh` — SessionStart JSON shapes (Claude / Cursor / Copilot / Codex). Language-router cases are not here.
- `tests/hooks/test-language-router.sh` — Codex `UserPromptSubmit` language-router cases.
- `tests/hooks/test-tool-mapping-canonical.sh` — injected mapping / `skillInstructions` must equal `references/<harness>-tools.md`. New `*-tools.md` files fail until classified. `tests/claude-code/` stays out of the core gate.
- `tests/hooks/test-tool-mapping-canonical.sh` — injected mapping / `skillInstructions` must equal `references/<harness>-tools.md` (Kimi may append the Product Design composition pointer when that skill exists). New `*-tools.md` files fail until classified. `tests/claude-code/` stays out of the core gate.
- `tests/hooks/test-product-design-bootstrap-pointer.sh` — SessionStart / OpenCode / Pi / Kimi append the composition pointer only when `skills/product-design/SKILL.md` exists.
- `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.
Expand Down
27 changes: 26 additions & 1 deletion hooks/session-start
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ read_harness_tools() {
cat "$path"
}

product_design_skill_path() {
printf '%s' "$1/skills/product-design/SKILL.md"
}

product_design_composition_pointer_path() {
printf '%s' "$1/skills/using-wukong-code/references/product-design-composition-pointer.md"
}

# Delivery-layer only. Inject when the Product Design router exists; do not
# rewrite using-wukong-code Scope routing.
read_product_design_composition_pointer() {
local plugin_root="$1"
local skill_path pointer_path
skill_path="$(product_design_skill_path "$plugin_root")"
pointer_path="$(product_design_composition_pointer_path "$plugin_root")"
[[ -f "$skill_path" && -f "$pointer_path" ]] || return 1
cat "$pointer_path"
}

detect_session_harness() {
if [[ -n "${CURSOR_PLUGIN_ROOT:-}" ]]; then
printf '%s' cursor
Expand Down Expand Up @@ -82,7 +101,13 @@ if mapping_raw="$(read_harness_tools "$PLUGIN_ROOT" "$detected_harness")"; then
mapping_escaped=$(escape_for_json "$mapping_raw")
mapping_escaped="\n\n${mapping_escaped}"
fi
session_context="<EXTREMELY_IMPORTANT>\nYou have wukong-code.\n\n**Below is the full content of your 'wukong-code:using-wukong-code' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**\n\n${using_wukong_code_escaped}${mapping_escaped}\n</EXTREMELY_IMPORTANT>"
pointer_escaped=""
if pointer_raw="$(read_product_design_composition_pointer "$PLUGIN_ROOT")"; then
pointer_raw="${pointer_raw%$'\n'}"
pointer_escaped=$(escape_for_json "$pointer_raw")
pointer_escaped="\n\n${pointer_escaped}"
fi
session_context="<EXTREMELY_IMPORTANT>\nYou have wukong-code.\n\n**Below is the full content of your 'wukong-code:using-wukong-code' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**\n\n${using_wukong_code_escaped}${mapping_escaped}${pointer_escaped}\n</EXTREMELY_IMPORTANT>"

# Output context injection as JSON.
# Cursor hooks expect additional_context (snake_case).
Expand Down
1 change: 1 addition & 0 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ run_core() {
run bash tests/hooks/test-session-start.sh
run bash tests/hooks/test-language-router.sh
run bash tests/hooks/test-tool-mapping-canonical.sh
run bash tests/hooks/test-product-design-bootstrap-pointer.sh
run bash tests/opencode/run-tests.sh
run bash tests/kimi/run-tests.sh
run bash tests/cursor/run-tests.sh
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Process skills stay primary; Product Design is secondary. Conflicts keep the process gates. See `references/wukong-product-design-composition.md`.
Loading
Loading