diff --git a/scripts/package-codex-plugin.sh b/scripts/package-codex-plugin.sh index 4cf01a0..58894ca 100755 --- a/scripts/package-codex-plugin.sh +++ b/scripts/package-codex-plugin.sh @@ -3,9 +3,10 @@ # Package the Wukong Code Codex plugin as a rootless archive for portal upload. # # The Codex portal artifact differs from the old openai/plugins sync flow: -# it is a standalone archive, but it still needs the OpenAI-owned -# skills/*/agents/openai.yaml metadata that used to be preserved from the -# destination plugin repo. Seed that metadata from a prior official package. +# it is a standalone archive, but it still needs skills/*/agents/openai.yaml +# for every packaged skill. Prefer source-tree yaml. When a skill is missing +# that file, seed it from a prior official package. A complete source tree +# does not require --metadata-source. set -euo pipefail @@ -30,10 +31,12 @@ Options: --format FORMAT Archive format: zip or tar.gz. Default: zip. If --output ends in .zip, .tar.gz, or .tgz, that extension is used when --format is omitted. - --metadata-source PATH Prior official package directory, .zip, or .tar.gz used to - seed skills/*/agents/openai.yaml. + --metadata-source PATH Optional prior official package directory, .zip, or .tar.gz + used to seed skills/*/agents/openai.yaml when the source + tree is missing them. Default: ../_tmp/sup-codex-packaging/wukong-code, - falling back to wukong-code.zip, then wukong-code.tar.gz + falling back to wukong-code.zip, then wukong-code.tar.gz. + Unused when every packaged skill already has yaml. --ref REF Git ref to package. Default: HEAD. --allow-dirty Permit a dirty working tree. The archive still uses --ref. --keep-stage Print and keep the temporary staging directory. @@ -171,8 +174,6 @@ if [[ -z "$METADATA_SOURCE" ]]; then METADATA_SOURCE="$REPO_ROOT/../_tmp/sup-codex-packaging/wukong-code.zip" elif [[ -f "$REPO_ROOT/../_tmp/sup-codex-packaging/wukong-code.tar.gz" ]]; then METADATA_SOURCE="$REPO_ROOT/../_tmp/sup-codex-packaging/wukong-code.tar.gz" - else - die "no metadata source found; pass --metadata-source " fi fi @@ -239,7 +240,10 @@ prepare_metadata_root() { die "metadata source does not contain a skills/ directory: $source" } -METADATA_ROOT="$(prepare_metadata_root "$METADATA_SOURCE")" +METADATA_ROOT="" +if [[ -n "$METADATA_SOURCE" ]]; then + METADATA_ROOT="$(prepare_metadata_root "$METADATA_SOURCE")" +fi MANIFEST_HELPER="$SCRIPT_DIR/codex-package-manifest.py" [[ -f "$MANIFEST_HELPER" ]] || die "missing Codex package manifest helper: $MANIFEST_HELPER" @@ -295,16 +299,19 @@ while IFS= read -r skill_dir; do if [[ -f "$source_metadata" ]]; then continue fi - if [[ ! -f "$fallback_metadata" ]]; then - echo "Missing OpenAI agent metadata for skill: $skill_name" >&2 - missing_metadata=1 + if [[ -n "$METADATA_ROOT" && -f "$fallback_metadata" ]]; then + mkdir -p "$skill_dir/agents" + cp "$fallback_metadata" "$skill_dir/agents/openai.yaml" continue fi - mkdir -p "$skill_dir/agents" - cp "$fallback_metadata" "$skill_dir/agents/openai.yaml" + echo "Missing OpenAI agent metadata for skill: $skill_name" >&2 + missing_metadata=1 done < <(find "$STAGE/skills" -mindepth 1 -maxdepth 1 -type d -print | sort) if [[ "$missing_metadata" -ne 0 ]]; then + if [[ -z "$METADATA_SOURCE" ]]; then + die "no metadata source found; pass --metadata-source " + fi die "metadata source is incomplete" fi diff --git a/skills/brainstorming/agents/openai.yaml b/skills/brainstorming/agents/openai.yaml new file mode 100644 index 0000000..4b32c2e --- /dev/null +++ b/skills/brainstorming/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Brainstorming" + short_description: "Explore intent, requirements, and design before implementation" + default_prompt: "Use $brainstorming to explore intent, requirements, and design before implementation." +policy: + allow_implicit_invocation: true diff --git a/skills/dispatching-parallel-agents/agents/openai.yaml b/skills/dispatching-parallel-agents/agents/openai.yaml new file mode 100644 index 0000000..8fb6cf4 --- /dev/null +++ b/skills/dispatching-parallel-agents/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Dispatching Parallel Agents" + short_description: "Split independent work across parallel agents" + default_prompt: "Use $dispatching-parallel-agents to split these independent tasks across parallel agents." +policy: + allow_implicit_invocation: true diff --git a/skills/executing-plans/agents/openai.yaml b/skills/executing-plans/agents/openai.yaml new file mode 100644 index 0000000..d5c8f13 --- /dev/null +++ b/skills/executing-plans/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Executing Plans" + short_description: "Execute written implementation plans with review checkpoints" + default_prompt: "Use $executing-plans to execute this written implementation plan with review checkpoints." +policy: + allow_implicit_invocation: true diff --git a/skills/finishing-a-development-branch/agents/openai.yaml b/skills/finishing-a-development-branch/agents/openai.yaml new file mode 100644 index 0000000..e298b69 --- /dev/null +++ b/skills/finishing-a-development-branch/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Finishing a Development Branch" + short_description: "Wrap up implementation work and choose the right integration path" + default_prompt: "Use $finishing-a-development-branch to choose how to merge, open a PR, or clean up this completed work." +policy: + allow_implicit_invocation: true diff --git a/skills/receiving-code-review/agents/openai.yaml b/skills/receiving-code-review/agents/openai.yaml new file mode 100644 index 0000000..16be8ac --- /dev/null +++ b/skills/receiving-code-review/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Receiving Code Review" + short_description: "Evaluate review feedback before implementing changes" + default_prompt: "Use $receiving-code-review to evaluate this review feedback before implementing changes." +policy: + allow_implicit_invocation: true diff --git a/skills/requesting-code-review/agents/openai.yaml b/skills/requesting-code-review/agents/openai.yaml new file mode 100644 index 0000000..6736031 --- /dev/null +++ b/skills/requesting-code-review/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Requesting Code Review" + short_description: "Request focused code review before merging" + default_prompt: "Use $requesting-code-review to request a focused review of this work before merging." +policy: + allow_implicit_invocation: true diff --git a/skills/subagent-driven-development/agents/openai.yaml b/skills/subagent-driven-development/agents/openai.yaml new file mode 100644 index 0000000..ec846ca --- /dev/null +++ b/skills/subagent-driven-development/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Subagent-Driven Development" + short_description: "Execute implementation plans with staged subagent reviews" + default_prompt: "Use $subagent-driven-development to execute this implementation plan with staged subagent reviews." +policy: + allow_implicit_invocation: true diff --git a/skills/systematic-debugging/agents/openai.yaml b/skills/systematic-debugging/agents/openai.yaml new file mode 100644 index 0000000..a541350 --- /dev/null +++ b/skills/systematic-debugging/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Systematic Debugging" + short_description: "Find root causes before attempting fixes" + default_prompt: "Use $systematic-debugging to find the root cause of this bug before proposing fixes." +policy: + allow_implicit_invocation: true diff --git a/skills/test-driven-development/agents/openai.yaml b/skills/test-driven-development/agents/openai.yaml new file mode 100644 index 0000000..62ab6d4 --- /dev/null +++ b/skills/test-driven-development/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Test-Driven Development" + short_description: "Implement features and bug fixes with test-first development" + default_prompt: "Use $test-driven-development to implement this change test-first." +policy: + allow_implicit_invocation: true diff --git a/skills/using-git-worktrees/agents/openai.yaml b/skills/using-git-worktrees/agents/openai.yaml new file mode 100644 index 0000000..da024f5 --- /dev/null +++ b/skills/using-git-worktrees/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Using Git Worktrees" + short_description: "Isolate feature work with native tools or a git worktree" + default_prompt: "Use $using-git-worktrees to isolate this feature work before implementation." +policy: + allow_implicit_invocation: true diff --git a/skills/using-wukong-code/agents/openai.yaml b/skills/using-wukong-code/agents/openai.yaml new file mode 100644 index 0000000..6f3027e --- /dev/null +++ b/skills/using-wukong-code/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Using Wukong Code" + short_description: "Establish how and when to use Wukong Code skills" + default_prompt: "Use $using-wukong-code to find and invoke the right skill before responding." +policy: + allow_implicit_invocation: true diff --git a/skills/verification-before-completion/agents/openai.yaml b/skills/verification-before-completion/agents/openai.yaml new file mode 100644 index 0000000..6f87023 --- /dev/null +++ b/skills/verification-before-completion/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Verification Before Completion" + short_description: "Verify claims with commands and output before declaring success" + default_prompt: "Use $verification-before-completion to run verification and confirm output before claiming this work is done." +policy: + allow_implicit_invocation: true diff --git a/skills/writing-plans/agents/openai.yaml b/skills/writing-plans/agents/openai.yaml new file mode 100644 index 0000000..d090156 --- /dev/null +++ b/skills/writing-plans/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Writing Plans" + short_description: "Turn specs into detailed multi-step implementation plans" + default_prompt: "Use $writing-plans to turn this spec into a detailed multi-step implementation plan." +policy: + allow_implicit_invocation: true diff --git a/skills/writing-skills/agents/openai.yaml b/skills/writing-skills/agents/openai.yaml new file mode 100644 index 0000000..ab6fe2a --- /dev/null +++ b/skills/writing-skills/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "Writing Skills" + short_description: "Create and verify skills before deployment" + default_prompt: "Use $writing-skills to create, edit, or verify this skill before deployment." +policy: + allow_implicit_invocation: true diff --git a/tests/codex/test-package-codex-plugin.sh b/tests/codex/test-package-codex-plugin.sh index 7a53262..16c5719 100755 --- a/tests/codex/test-package-codex-plugin.sh +++ b/tests/codex/test-package-codex-plugin.sh @@ -191,6 +191,19 @@ else printf '%s\n' "$output" | sed 's/^/ /' fi +self_contained_archive="$TEST_ROOT/wukong-code-no-metadata-source.zip" +if self_contained_output="$("$SCRIPT_UNDER_TEST" --allow-dirty --ref "$candidate_ref" --output "$self_contained_archive" 2>&1)"; then + pass "package script succeeds without metadata source" +else + fail "package script succeeds without metadata source" + printf '%s\n' "$self_contained_output" | sed 's/^/ /' +fi +if [[ -f "$archive" && -f "$self_contained_archive" ]] && cmp -s "$archive" "$self_contained_archive"; then + pass "complete source tree does not use fallback metadata" +else + fail "complete source tree does not use fallback metadata" +fi + if [[ -f "$archive" ]]; then pass "package script writes archive" else @@ -255,6 +268,8 @@ assert_contains "$archive_paths" "skills/brainstorming/agents/openai.yaml" "arch assert_contains "$archive_paths" "skills/language-guidance/agents/openai.yaml" "archive keeps source metadata" language_metadata="$(read_archive_file "$archive" skills/language-guidance/agents/openai.yaml)" assert_contains "$language_metadata" "display_name: \"Language Guidance\"" "uses source metadata" +brainstorming_metadata="$(read_archive_file "$archive" skills/brainstorming/agents/openai.yaml)" +assert_contains "$brainstorming_metadata" "display_name: \"Brainstorming\"" "source yaml wins over fixture" assert_contains "$archive_paths" "assets/app-icon.png" "archive includes app icon" assert_contains "$archive_paths" "assets/wukong-code-small.svg" "archive includes composer icon" assert_contains "$archive_paths" "assets/readme/hero.png" "archive includes published README hero" @@ -386,8 +401,35 @@ mkdir -p "$incomplete_metadata/skills/brainstorming/agents" cp "$metadata_source/skills/brainstorming/agents/openai.yaml" \ "$incomplete_metadata/skills/brainstorming/agents/openai.yaml" +complete_with_incomplete_fallback="$TEST_ROOT/complete-source-incomplete-fallback.zip" +if output="$("$SCRIPT_UNDER_TEST" --allow-dirty --ref "$candidate_ref" --metadata-source "$incomplete_metadata" --output "$complete_with_incomplete_fallback" 2>&1)"; then + pass "complete source tree ignores incomplete metadata source" +else + fail "complete source tree ignores incomplete metadata source" + printf '%s\n' "$output" | sed 's/^/ /' +fi +if [[ -f "$archive" && -f "$complete_with_incomplete_fallback" ]] && cmp -s "$archive" "$complete_with_incomplete_fallback"; then + pass "incomplete fallback is unused when source yaml is complete" +else + fail "incomplete fallback is unused when source yaml is complete" +fi + +partial_index="$TEST_ROOT/partial-index" +GIT_INDEX_FILE="$partial_index" git -C "$REPO_ROOT" read-tree "$candidate_tree" +GIT_INDEX_FILE="$partial_index" git -C "$REPO_ROOT" rm -q --cached --ignore-unmatch \ + skills/writing-plans/agents/openai.yaml +partial_tree="$(GIT_INDEX_FILE="$partial_index" git -C "$REPO_ROOT" write-tree)" +partial_ref="$( + GIT_AUTHOR_NAME="Wukong Code Tests" \ + GIT_AUTHOR_EMAIL="tests@wukong-code.local" \ + GIT_COMMITTER_NAME="Wukong Code Tests" \ + GIT_COMMITTER_EMAIL="tests@wukong-code.local" \ + git -C "$REPO_ROOT" commit-tree "$partial_tree" -p HEAD \ + -m "Temporary Codex packaging candidate missing one skill yaml" +)" + set +e -missing_output="$("$SCRIPT_UNDER_TEST" --allow-dirty --ref "$candidate_ref" --metadata-source "$incomplete_metadata" --output "$TEST_ROOT/missing.tar.gz" 2>&1)" +missing_output="$("$SCRIPT_UNDER_TEST" --allow-dirty --ref "$partial_ref" --metadata-source "$incomplete_metadata" --output "$TEST_ROOT/missing.tar.gz" 2>&1)" missing_status=$? set -e if [[ "$missing_status" -ne 0 ]]; then @@ -396,6 +438,8 @@ else fail "package script rejects incomplete metadata source" fi assert_contains "$missing_output" "ERROR: metadata source is incomplete" "incomplete metadata reports clear error" +assert_contains "$missing_output" "Missing OpenAI agent metadata for skill: writing-plans" \ + "incomplete metadata names the skill still missing yaml" dirty_repo="$TEST_ROOT/dirty-repo" git clone -q --no-local "$REPO_ROOT" "$dirty_repo"