From fc6b757fb7727a720b53a792aea193b068273f5e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Jun 2026 18:46:32 +0000 Subject: [PATCH 1/6] fix: correct gh skill install command format for skill specs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/mattpocock-skills-reviewer.lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mattpocock-skills-reviewer.lock.yml b/.github/workflows/mattpocock-skills-reviewer.lock.yml index 2280323fb07..2677ae9206f 100644 --- a/.github/workflows/mattpocock-skills-reviewer.lock.yml +++ b/.github/workflows/mattpocock-skills-reviewer.lock.yml @@ -1,5 +1,5 @@ # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"26e8a754482b7347113adc6d77b27f62e3dee49e84d972a2e35662fd319720b5","body_hash":"62ce0bd5c9851424f73f018934c48ae0ca6493b0bc7415fdc5564e808707829a","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-4.6","engine_versions":{"copilot":"1.0.65"}} -# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.13","digest":"sha256:691a06b64961b5b35aac117eaace202fa721e91da19d1d2e22dcdd6663cd571b","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.13@sha256:691a06b64961b5b35aac117eaace202fa721e91da19d1d2e22dcdd6663cd571b"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13","digest":"sha256:c57febf4aeeefbb4fd96f5b12c07f4279ca55edca6a700032debf9dd0787286e","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.13@sha256:c57febf4aeeefbb4fd96f5b12c07f4279ca55edca6a700032debf9dd0787286e"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13","digest":"sha256:79dfd3a5d139bd1956ba6d7d1782b831a07175cf5afa29c45cb20bb0140f23c5","pinned_image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.13@sha256:79dfd3a5d139bd1956ba6d7d1782b831a07175cf5afa29c45cb20bb0140f23c5"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.13","digest":"sha256:700b1b5a73098373b04fb684f291e95d9be0124ab559717b04f27acaf8b41bed","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.13@sha256:700b1b5a73098373b04fb684f291e95d9be0124ab559717b04f27acaf8b41bed"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.32","digest":"sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.32@sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} +# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.15"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.15"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.15"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.15"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.32","digest":"sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.32@sha256:63e46b56dfd70895a701b6fc6dd0189e11e2d875f327f1781e81b31848735477"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.5.0","digest":"sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4","pinned_image":"ghcr.io/github/github-mcp-server:v1.5.0@sha256:e25564dccc9110a70a77b9df560cbde11aa392fcb5f08b9abe5c4ebc6d146ea4"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ From 95dd1d8a9fbf5d6d7bbdf6b1ec6d4fdfdc7f8d2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Jun 2026 18:53:03 +0000 Subject: [PATCH 2/6] fix: correct gh skill install command to split repo and skill path with --pin for SHA Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../mattpocock-skills-reviewer.lock.yml | 105 +++++++++++++----- pkg/workflow/activation_skills_step_test.go | 4 +- .../compiler_activation_job_builder.go | 20 +++- 3 files changed, 93 insertions(+), 36 deletions(-) diff --git a/.github/workflows/mattpocock-skills-reviewer.lock.yml b/.github/workflows/mattpocock-skills-reviewer.lock.yml index 2677ae9206f..68c378ea362 100644 --- a/.github/workflows/mattpocock-skills-reviewer.lock.yml +++ b/.github/workflows/mattpocock-skills-reviewer.lock.yml @@ -318,60 +318,109 @@ jobs: echo "Existing skills at destination may be replaced (--force) to ensure pinned refs are up to date" skill_spec="${GH_AW_SKILL_SPEC_0}" echo "Installing skill reference: ${skill_spec}" + skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_1}" echo "Installing skill reference: ${skill_spec}" + skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_2}" echo "Installing skill reference: ${skill_spec}" + skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_3}" echo "Installing skill reference: ${skill_spec}" + skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_4}" echo "Installing skill reference: ${skill_spec}" + skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_5}" echo "Installing skill reference: ${skill_spec}" + skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_6}" echo "Installing skill reference: ${skill_spec}" + skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force SKILL_COUNT=$(find "${SKILLS_DST}" -name "SKILL.md" | wc -l | tr -d '[:space:]') echo "Installed ${SKILL_COUNT} skill file(s)" core_summary_path="${GITHUB_STEP_SUMMARY:-}" diff --git a/pkg/workflow/activation_skills_step_test.go b/pkg/workflow/activation_skills_step_test.go index 1d417b57bec..e9ba425addd 100644 --- a/pkg/workflow/activation_skills_step_test.go +++ b/pkg/workflow/activation_skills_step_test.go @@ -40,8 +40,8 @@ func TestBuildActivationJob_AddsFrontmatterSkillsInstallSteps(t *testing.T) { assert.Contains(t, steps, "GH_AW_SKILL_SPEC_0: \"githubnext/skills@1f181b37d3fe5862ab590648f25a292e345b5de6\"", "expected first skill env var") assert.Contains(t, steps, "GH_AW_SKILL_SPEC_1: \"githubnext/skills/review/security@1f181b37d3fe5862ab590648f25a292e345b5de6\"", "expected second skill env var") assert.Contains(t, steps, "skill_spec=\"${GH_AW_SKILL_SPEC_0}\"", "expected runtime install loop to read first skill from env") - assert.Contains(t, steps, "install_args+=(--all)", "expected runtime repository-scope detection") - assert.Contains(t, steps, "gh skill install \"${skill_spec}\" \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force", "expected runtime install command to use quoted env values") + assert.Contains(t, steps, "gh skill install \"${skill_base}\" --all --pin \"${skill_ref}\" --dir \"${SKILLS_DST}\" --force", "expected repo-level install with --all and --pin") + assert.Contains(t, steps, "gh skill install \"${skill_repo}\" \"${skill_subpath}\" --pin \"${skill_ref}\" --dir \"${SKILLS_DST}\" --force", "expected path-level install with separate repo and skill args") assert.Contains(t, steps, "### Frontmatter skills installed", "expected step summary output") } diff --git a/pkg/workflow/compiler_activation_job_builder.go b/pkg/workflow/compiler_activation_job_builder.go index cc14f6e5dd8..152ecbbd6ad 100644 --- a/pkg/workflow/compiler_activation_job_builder.go +++ b/pkg/workflow/compiler_activation_job_builder.go @@ -564,14 +564,22 @@ func (c *Compiler) addActivationSkillInstallSteps(ctx *activationJobBuildContext for i := range ctx.data.Skills { ctx.steps = append(ctx.steps, fmt.Sprintf(" skill_spec=\"${GH_AW_SKILL_SPEC_%d}\"\n", i)) ctx.steps = append(ctx.steps, " echo \"Installing skill reference: ${skill_spec}\"\n") - // Keep this runtime owner/repo vs owner/repo/path detection aligned with - // isRepositorySkillSpec so expression-based refs behave the same after resolution. + // gh skill install expects [] as separate arguments. + // Split the spec into owner/repo and optional skill-path+pin, keeping this logic + // aligned with isRepositorySkillSpec so expression-based refs behave the same after resolution. + ctx.steps = append(ctx.steps, " skill_ref=\"${skill_spec##*@}\"\n") ctx.steps = append(ctx.steps, " skill_base=\"${skill_spec%@*}\"\n") - ctx.steps = append(ctx.steps, " install_args=()\n") - ctx.steps = append(ctx.steps, " if [[ \"${skill_base}\" == */* && \"${skill_base}\" != */*/* ]]; then\n") - ctx.steps = append(ctx.steps, " install_args+=(--all)\n") + ctx.steps = append(ctx.steps, " if [[ \"${skill_base}\" == */*/* ]]; then\n") + ctx.steps = append(ctx.steps, " skill_owner=\"${skill_base%%/*}\"\n") + ctx.steps = append(ctx.steps, " skill_remaining=\"${skill_base#*/}\"\n") + ctx.steps = append(ctx.steps, " skill_repo=\"${skill_owner}/${skill_remaining%%/*}\"\n") + ctx.steps = append(ctx.steps, " skill_subpath=\"${skill_remaining#*/}\"\n") + ctx.steps = append(ctx.steps, " gh skill install \"${skill_repo}\" \"${skill_subpath}\" --pin \"${skill_ref}\" --dir \"${SKILLS_DST}\" --force\n") + ctx.steps = append(ctx.steps, " elif [[ \"${skill_base}\" == */* ]]; then\n") + ctx.steps = append(ctx.steps, " gh skill install \"${skill_base}\" --all --pin \"${skill_ref}\" --dir \"${SKILLS_DST}\" --force\n") + ctx.steps = append(ctx.steps, " else\n") + ctx.steps = append(ctx.steps, " gh skill install \"${skill_spec}\" --dir \"${SKILLS_DST}\" --force\n") ctx.steps = append(ctx.steps, " fi\n") - ctx.steps = append(ctx.steps, " gh skill install \"${skill_spec}\" \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force\n") } ctx.steps = append(ctx.steps, " SKILL_COUNT=$(find \"${SKILLS_DST}\" -name \"SKILL.md\" | wc -l | tr -d '[:space:]')\n") ctx.steps = append(ctx.steps, " echo \"Installed ${SKILL_COUNT} skill file(s)\"\n") From 09da97d889186e7452f75c0a9dc7ec3a596cdaf8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Jun 2026 19:14:57 +0000 Subject: [PATCH 3/6] fix: trim generated skill install output Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../mattpocock-skills-reviewer.lock.yml | 105 +++++------------- pkg/workflow/activation_skills_step_test.go | 6 +- .../compiler_activation_job_builder.go | 17 ++- 3 files changed, 43 insertions(+), 85 deletions(-) diff --git a/.github/workflows/mattpocock-skills-reviewer.lock.yml b/.github/workflows/mattpocock-skills-reviewer.lock.yml index 68c378ea362..2677ae9206f 100644 --- a/.github/workflows/mattpocock-skills-reviewer.lock.yml +++ b/.github/workflows/mattpocock-skills-reviewer.lock.yml @@ -318,109 +318,60 @@ jobs: echo "Existing skills at destination may be replaced (--force) to ensure pinned refs are up to date" skill_spec="${GH_AW_SKILL_SPEC_0}" echo "Installing skill reference: ${skill_spec}" - skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force + install_args=() + if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then + install_args+=(--all) fi + gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_1}" echo "Installing skill reference: ${skill_spec}" - skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force + install_args=() + if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then + install_args+=(--all) fi + gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_2}" echo "Installing skill reference: ${skill_spec}" - skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force + install_args=() + if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then + install_args+=(--all) fi + gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_3}" echo "Installing skill reference: ${skill_spec}" - skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force + install_args=() + if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then + install_args+=(--all) fi + gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_4}" echo "Installing skill reference: ${skill_spec}" - skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force + install_args=() + if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then + install_args+=(--all) fi + gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_5}" echo "Installing skill reference: ${skill_spec}" - skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force + install_args=() + if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then + install_args+=(--all) fi + gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_6}" echo "Installing skill reference: ${skill_spec}" - skill_ref="${skill_spec##*@}" skill_base="${skill_spec%@*}" - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all --pin "${skill_ref}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force + install_args=() + if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then + install_args+=(--all) fi + gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force SKILL_COUNT=$(find "${SKILLS_DST}" -name "SKILL.md" | wc -l | tr -d '[:space:]') echo "Installed ${SKILL_COUNT} skill file(s)" core_summary_path="${GITHUB_STEP_SUMMARY:-}" diff --git a/pkg/workflow/activation_skills_step_test.go b/pkg/workflow/activation_skills_step_test.go index e9ba425addd..c40c4b91c6b 100644 --- a/pkg/workflow/activation_skills_step_test.go +++ b/pkg/workflow/activation_skills_step_test.go @@ -40,8 +40,10 @@ func TestBuildActivationJob_AddsFrontmatterSkillsInstallSteps(t *testing.T) { assert.Contains(t, steps, "GH_AW_SKILL_SPEC_0: \"githubnext/skills@1f181b37d3fe5862ab590648f25a292e345b5de6\"", "expected first skill env var") assert.Contains(t, steps, "GH_AW_SKILL_SPEC_1: \"githubnext/skills/review/security@1f181b37d3fe5862ab590648f25a292e345b5de6\"", "expected second skill env var") assert.Contains(t, steps, "skill_spec=\"${GH_AW_SKILL_SPEC_0}\"", "expected runtime install loop to read first skill from env") - assert.Contains(t, steps, "gh skill install \"${skill_base}\" --all --pin \"${skill_ref}\" --dir \"${SKILLS_DST}\" --force", "expected repo-level install with --all and --pin") - assert.Contains(t, steps, "gh skill install \"${skill_repo}\" \"${skill_subpath}\" --pin \"${skill_ref}\" --dir \"${SKILLS_DST}\" --force", "expected path-level install with separate repo and skill args") + assert.Contains(t, steps, "if [[ \"${skill_spec}\" == *@* ]]; then", "expected runtime pin detection for resolved skill refs") + assert.Contains(t, steps, "install_args+=(--pin \"${skill_ref}\")", "expected runtime pin args to be added only when a ref is present") + assert.Contains(t, steps, "gh skill install \"${skill_base}\" --all \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force", "expected repo-level install with optional pin args") + assert.Contains(t, steps, "gh skill install \"${skill_repo}\" \"${skill_subpath}\" \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force", "expected path-level install with separate repo and optional pin args") assert.Contains(t, steps, "### Frontmatter skills installed", "expected step summary output") } diff --git a/pkg/workflow/compiler_activation_job_builder.go b/pkg/workflow/compiler_activation_job_builder.go index 152ecbbd6ad..619f94767e7 100644 --- a/pkg/workflow/compiler_activation_job_builder.go +++ b/pkg/workflow/compiler_activation_job_builder.go @@ -565,18 +565,23 @@ func (c *Compiler) addActivationSkillInstallSteps(ctx *activationJobBuildContext ctx.steps = append(ctx.steps, fmt.Sprintf(" skill_spec=\"${GH_AW_SKILL_SPEC_%d}\"\n", i)) ctx.steps = append(ctx.steps, " echo \"Installing skill reference: ${skill_spec}\"\n") // gh skill install expects [] as separate arguments. - // Split the spec into owner/repo and optional skill-path+pin, keeping this logic - // aligned with isRepositorySkillSpec so expression-based refs behave the same after resolution. - ctx.steps = append(ctx.steps, " skill_ref=\"${skill_spec##*@}\"\n") - ctx.steps = append(ctx.steps, " skill_base=\"${skill_spec%@*}\"\n") + // Split the spec into owner/repo and optional skill-path, and only add --pin + // when the resolved value includes an @ref suffix. + ctx.steps = append(ctx.steps, " skill_base=\"${skill_spec}\"\n") + ctx.steps = append(ctx.steps, " install_args=()\n") + ctx.steps = append(ctx.steps, " if [[ \"${skill_spec}\" == *@* ]]; then\n") + ctx.steps = append(ctx.steps, " skill_ref=\"${skill_spec##*@}\"\n") + ctx.steps = append(ctx.steps, " skill_base=\"${skill_spec%@*}\"\n") + ctx.steps = append(ctx.steps, " install_args+=(--pin \"${skill_ref}\")\n") + ctx.steps = append(ctx.steps, " fi\n") ctx.steps = append(ctx.steps, " if [[ \"${skill_base}\" == */*/* ]]; then\n") ctx.steps = append(ctx.steps, " skill_owner=\"${skill_base%%/*}\"\n") ctx.steps = append(ctx.steps, " skill_remaining=\"${skill_base#*/}\"\n") ctx.steps = append(ctx.steps, " skill_repo=\"${skill_owner}/${skill_remaining%%/*}\"\n") ctx.steps = append(ctx.steps, " skill_subpath=\"${skill_remaining#*/}\"\n") - ctx.steps = append(ctx.steps, " gh skill install \"${skill_repo}\" \"${skill_subpath}\" --pin \"${skill_ref}\" --dir \"${SKILLS_DST}\" --force\n") + ctx.steps = append(ctx.steps, " gh skill install \"${skill_repo}\" \"${skill_subpath}\" \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force\n") ctx.steps = append(ctx.steps, " elif [[ \"${skill_base}\" == */* ]]; then\n") - ctx.steps = append(ctx.steps, " gh skill install \"${skill_base}\" --all --pin \"${skill_ref}\" --dir \"${SKILLS_DST}\" --force\n") + ctx.steps = append(ctx.steps, " gh skill install \"${skill_base}\" --all \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force\n") ctx.steps = append(ctx.steps, " else\n") ctx.steps = append(ctx.steps, " gh skill install \"${skill_spec}\" --dir \"${SKILLS_DST}\" --force\n") ctx.steps = append(ctx.steps, " fi\n") From 45466d83d261e124dda03036ee3b2a4e76cd3600 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Jun 2026 19:45:40 +0000 Subject: [PATCH 4/6] Plan update Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../mattpocock-skills-reviewer.lock.yml | 140 ++++++++++++++---- 1 file changed, 112 insertions(+), 28 deletions(-) diff --git a/.github/workflows/mattpocock-skills-reviewer.lock.yml b/.github/workflows/mattpocock-skills-reviewer.lock.yml index 2677ae9206f..77a0dbae960 100644 --- a/.github/workflows/mattpocock-skills-reviewer.lock.yml +++ b/.github/workflows/mattpocock-skills-reviewer.lock.yml @@ -318,60 +318,144 @@ jobs: echo "Existing skills at destination may be replaced (--force) to ensure pinned refs are up to date" skill_spec="${GH_AW_SKILL_SPEC_0}" echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec%@*}" + skill_base="${skill_spec}" install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_spec}" == *@* ]]; then + skill_ref="${skill_spec##*@}" + skill_base="${skill_spec%@*}" + install_args+=(--pin "${skill_ref}") + fi + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_1}" echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec%@*}" + skill_base="${skill_spec}" install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_spec}" == *@* ]]; then + skill_ref="${skill_spec##*@}" + skill_base="${skill_spec%@*}" + install_args+=(--pin "${skill_ref}") + fi + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_2}" echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec%@*}" + skill_base="${skill_spec}" install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_spec}" == *@* ]]; then + skill_ref="${skill_spec##*@}" + skill_base="${skill_spec%@*}" + install_args+=(--pin "${skill_ref}") + fi + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_3}" echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec%@*}" + skill_base="${skill_spec}" install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_spec}" == *@* ]]; then + skill_ref="${skill_spec##*@}" + skill_base="${skill_spec%@*}" + install_args+=(--pin "${skill_ref}") + fi + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_4}" echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec%@*}" + skill_base="${skill_spec}" install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_spec}" == *@* ]]; then + skill_ref="${skill_spec##*@}" + skill_base="${skill_spec%@*}" + install_args+=(--pin "${skill_ref}") + fi + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_5}" echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec%@*}" + skill_base="${skill_spec}" install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_spec}" == *@* ]]; then + skill_ref="${skill_spec##*@}" + skill_base="${skill_spec%@*}" + install_args+=(--pin "${skill_ref}") + fi + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force skill_spec="${GH_AW_SKILL_SPEC_6}" echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec%@*}" + skill_base="${skill_spec}" install_args=() - if [[ "${skill_base}" == */* && "${skill_base}" != */*/* ]]; then - install_args+=(--all) + if [[ "${skill_spec}" == *@* ]]; then + skill_ref="${skill_spec##*@}" + skill_base="${skill_spec%@*}" + install_args+=(--pin "${skill_ref}") + fi + if [[ "${skill_base}" == */*/* ]]; then + skill_owner="${skill_base%%/*}" + skill_remaining="${skill_base#*/}" + skill_repo="${skill_owner}/${skill_remaining%%/*}" + skill_subpath="${skill_remaining#*/}" + gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force + elif [[ "${skill_base}" == */* ]]; then + gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force + else + gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force fi - gh skill install "${skill_spec}" "${install_args[@]}" --dir "${SKILLS_DST}" --force SKILL_COUNT=$(find "${SKILLS_DST}" -name "SKILL.md" | wc -l | tr -d '[:space:]') echo "Installed ${SKILL_COUNT} skill file(s)" core_summary_path="${GITHUB_STEP_SUMMARY:-}" From 107935aae569edbd4e8cce92f40d15c66f1d570c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Jun 2026 19:54:03 +0000 Subject: [PATCH 5/6] refactor: move frontmatter skill install to JS runtime Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../setup/js/install_frontmatter_skills.cjs | 122 ++++++++++++++++++ .../js/install_frontmatter_skills.test.cjs | 87 +++++++++++++ pkg/workflow/activation_skills_step_test.go | 17 +-- .../compiler_activation_job_builder.go | 59 +-------- 4 files changed, 220 insertions(+), 65 deletions(-) create mode 100644 actions/setup/js/install_frontmatter_skills.cjs create mode 100644 actions/setup/js/install_frontmatter_skills.test.cjs diff --git a/actions/setup/js/install_frontmatter_skills.cjs b/actions/setup/js/install_frontmatter_skills.cjs new file mode 100644 index 00000000000..a66b6d07d47 --- /dev/null +++ b/actions/setup/js/install_frontmatter_skills.cjs @@ -0,0 +1,122 @@ +// @ts-check +/// + +const fs = require("fs"); +const path = require("path"); + +/** + * @param {string} rawSkills + * @returns {string[]} + */ +function parseSkillSpecs(rawSkills) { + return (rawSkills || "") + .split(/\r?\n/) + .map(skill => skill.trim()) + .filter(Boolean); +} + +/** + * @typedef {{args: string[]; displaySpec: string}} SkillInstallCommand + */ + +/** + * @param {string} skillSpec + * @param {string} skillsDst + * @returns {SkillInstallCommand} + */ +function buildSkillInstallCommand(skillSpec, skillsDst) { + const atIndex = skillSpec.lastIndexOf("@"); + const hasPin = atIndex >= 0; + const skillBase = hasPin ? skillSpec.slice(0, atIndex) : skillSpec; + const skillRef = hasPin ? skillSpec.slice(atIndex + 1) : ""; + const parts = skillBase.split("/"); + const pinArgs = skillRef ? ["--pin", skillRef] : []; + + if (parts.length >= 3) { + return { + displaySpec: skillSpec, + args: ["skill", "install", `${parts[0]}/${parts[1]}`, parts.slice(2).join("/"), ...pinArgs, "--dir", skillsDst, "--force"], + }; + } + + if (parts.length === 2) { + return { + displaySpec: skillSpec, + args: ["skill", "install", skillBase, "--all", ...pinArgs, "--dir", skillsDst, "--force"], + }; + } + + return { + displaySpec: skillSpec, + args: ["skill", "install", skillSpec, "--dir", skillsDst, "--force"], + }; +} + +/** + * @param {string} skillsDst + * @returns {number} + */ +function countInstalledSkillFiles(skillsDst) { + if (!fs.existsSync(skillsDst)) { + return 0; + } + + let count = 0; + const stack = [skillsDst]; + while (stack.length > 0) { + const currentDir = stack.pop(); + if (!currentDir) { + continue; + } + for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) { + const entryPath = path.join(currentDir, entry.name); + if (entry.isDirectory()) { + stack.push(entryPath); + continue; + } + if (entry.isFile() && entry.name === "SKILL.md") { + count++; + } + } + } + + return count; +} + +/** + * @param {string} skillDir + * @param {string[]} skills + * @param {number} installedSkillCount + * @returns {Promise} + */ +async function writeSkillSummary(skillDir, skills, installedSkillCount) { + core.summary + .addRaw("### Frontmatter skills installed\n\n") + .addRaw(`- Engine skill directory: \`${skillDir}\`\n`) + .addRaw(`- Requested references: \`${JSON.stringify(skills)}\`\n`) + .addRaw(`- Installed SKILL.md files: ${installedSkillCount}\n`); + await core.summary.write(); +} + +async function main() { + const skillDir = process.env.GH_AW_SKILL_DIR || ""; + const skills = parseSkillSpecs(process.env.GH_AW_FRONTMATTER_SKILLS || ""); + const skillsDst = path.join("/tmp/gh-aw", skillDir); + + fs.mkdirSync(skillsDst, { recursive: true }); + + core.info(`Installing frontmatter skills to ${skillsDst}`); + core.info("Existing skills at destination may be replaced (--force) to ensure pinned refs are up to date"); + + for (const skillSpec of skills) { + core.info(`Installing skill reference: ${skillSpec}`); + const command = buildSkillInstallCommand(skillSpec, skillsDst); + await exec.exec("gh", command.args); + } + + const installedSkillCount = countInstalledSkillFiles(skillsDst); + core.info(`Installed ${installedSkillCount} skill file(s)`); + await writeSkillSummary(skillDir, skills, installedSkillCount); +} + +module.exports = { main, parseSkillSpecs, buildSkillInstallCommand, countInstalledSkillFiles }; diff --git a/actions/setup/js/install_frontmatter_skills.test.cjs b/actions/setup/js/install_frontmatter_skills.test.cjs new file mode 100644 index 00000000000..484ed1e7526 --- /dev/null +++ b/actions/setup/js/install_frontmatter_skills.test.cjs @@ -0,0 +1,87 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import fs from "fs"; +import os from "os"; +import path from "path"; +import { createRequire } from "module"; + +const require = createRequire(import.meta.url); +const script = require("./install_frontmatter_skills.cjs"); + +describe("install_frontmatter_skills", () => { + let originalEnv; + let originalCore; + let originalExec; + let tempRoot; + + beforeEach(() => { + originalEnv = { + GH_AW_SKILL_DIR: process.env.GH_AW_SKILL_DIR, + GH_AW_FRONTMATTER_SKILLS: process.env.GH_AW_FRONTMATTER_SKILLS, + }; + originalCore = global.core; + originalExec = global.exec; + tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "gh-aw-frontmatter-skills-")); + + global.core = { + info: vi.fn(), + summary: { + addRaw: vi.fn().mockReturnThis(), + write: vi.fn().mockResolvedValue(undefined), + }, + }; + global.exec = { + exec: vi.fn().mockResolvedValue(0), + }; + }); + + afterEach(() => { + if (originalEnv.GH_AW_SKILL_DIR === undefined) { + delete process.env.GH_AW_SKILL_DIR; + } else { + process.env.GH_AW_SKILL_DIR = originalEnv.GH_AW_SKILL_DIR; + } + if (originalEnv.GH_AW_FRONTMATTER_SKILLS === undefined) { + delete process.env.GH_AW_FRONTMATTER_SKILLS; + } else { + process.env.GH_AW_FRONTMATTER_SKILLS = originalEnv.GH_AW_FRONTMATTER_SKILLS; + } + global.core = originalCore; + global.exec = originalExec; + fs.rmSync(tempRoot, { recursive: true, force: true }); + fs.rmSync("/tmp/gh-aw/.claude", { recursive: true, force: true }); + }); + + it("splits repo-level and path-level skill specs into gh skill install arguments", () => { + expect(script.buildSkillInstallCommand("githubnext/skills@abc123", "/tmp/gh-aw/.claude/skills").args).toEqual(["skill", "install", "githubnext/skills", "--all", "--pin", "abc123", "--dir", "/tmp/gh-aw/.claude/skills", "--force"]); + expect(script.buildSkillInstallCommand("githubnext/skills/review/security@abc123", "/tmp/gh-aw/.claude/skills").args).toEqual([ + "skill", + "install", + "githubnext/skills", + "review/security", + "--pin", + "abc123", + "--dir", + "/tmp/gh-aw/.claude/skills", + "--force", + ]); + }); + + it("omits --pin when the resolved skill spec is unpinned", () => { + expect(script.buildSkillInstallCommand("githubnext/skills/review/security", "/tmp/gh-aw/.claude/skills").args).toEqual(["skill", "install", "githubnext/skills", "review/security", "--dir", "/tmp/gh-aw/.claude/skills", "--force"]); + }); + + it("reads skill specs from the env var and installs them at runtime", async () => { + process.env.GH_AW_SKILL_DIR = ".claude/skills"; + process.env.GH_AW_FRONTMATTER_SKILLS = ["githubnext/skills@abc123", "githubnext/skills/review/security@def456", "${{ inputs.skill_ref }}"].join("\n"); + fs.mkdirSync("/tmp/gh-aw/.claude/skills/example", { recursive: true }); + fs.writeFileSync("/tmp/gh-aw/.claude/skills/example/SKILL.md", "# test\n", "utf8"); + + await script.main(); + + expect(global.exec.exec).toHaveBeenNthCalledWith(1, "gh", ["skill", "install", "githubnext/skills", "--all", "--pin", "abc123", "--dir", "/tmp/gh-aw/.claude/skills", "--force"]); + expect(global.exec.exec).toHaveBeenNthCalledWith(2, "gh", ["skill", "install", "githubnext/skills", "review/security", "--pin", "def456", "--dir", "/tmp/gh-aw/.claude/skills", "--force"]); + expect(global.exec.exec).toHaveBeenNthCalledWith(3, "gh", ["skill", "install", "${{ inputs.skill_ref }}", "--dir", "/tmp/gh-aw/.claude/skills", "--force"]); + expect(global.core.summary.addRaw).toHaveBeenCalledWith(expect.stringContaining("### Frontmatter skills installed")); + expect(global.core.summary.addRaw).toHaveBeenCalledWith(expect.stringContaining('["githubnext/skills@abc123","githubnext/skills/review/security@def456","${{ inputs.skill_ref }}"]')); + }); +}); diff --git a/pkg/workflow/activation_skills_step_test.go b/pkg/workflow/activation_skills_step_test.go index c40c4b91c6b..aef3b79a0d0 100644 --- a/pkg/workflow/activation_skills_step_test.go +++ b/pkg/workflow/activation_skills_step_test.go @@ -36,15 +36,9 @@ func TestBuildActivationJob_AddsFrontmatterSkillsInstallSteps(t *testing.T) { assert.Contains(t, steps, "Upgrade gh CLI for frontmatter skills", "expected gh upgrade step in activation job") assert.Contains(t, steps, "Install frontmatter skills", "expected frontmatter skills install step in activation job") assert.Contains(t, steps, "GH_AW_SKILL_DIR: \".claude/skills\"", "expected engine skill directory env var") - assert.Contains(t, steps, "GH_AW_SKILLS_SUMMARY: '[\"githubnext/skills@1f181b37d3fe5862ab590648f25a292e345b5de6\",\"githubnext/skills/review/security@1f181b37d3fe5862ab590648f25a292e345b5de6\"]'", "expected summary env var for requested skills") - assert.Contains(t, steps, "GH_AW_SKILL_SPEC_0: \"githubnext/skills@1f181b37d3fe5862ab590648f25a292e345b5de6\"", "expected first skill env var") - assert.Contains(t, steps, "GH_AW_SKILL_SPEC_1: \"githubnext/skills/review/security@1f181b37d3fe5862ab590648f25a292e345b5de6\"", "expected second skill env var") - assert.Contains(t, steps, "skill_spec=\"${GH_AW_SKILL_SPEC_0}\"", "expected runtime install loop to read first skill from env") - assert.Contains(t, steps, "if [[ \"${skill_spec}\" == *@* ]]; then", "expected runtime pin detection for resolved skill refs") - assert.Contains(t, steps, "install_args+=(--pin \"${skill_ref}\")", "expected runtime pin args to be added only when a ref is present") - assert.Contains(t, steps, "gh skill install \"${skill_base}\" --all \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force", "expected repo-level install with optional pin args") - assert.Contains(t, steps, "gh skill install \"${skill_repo}\" \"${skill_subpath}\" \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force", "expected path-level install with separate repo and optional pin args") - assert.Contains(t, steps, "### Frontmatter skills installed", "expected step summary output") + assert.Contains(t, steps, "GH_AW_FRONTMATTER_SKILLS: \"githubnext/skills@1f181b37d3fe5862ab590648f25a292e345b5de6\\ngithubnext/skills/review/security@1f181b37d3fe5862ab590648f25a292e345b5de6\"", "expected skills env var") + assert.Contains(t, steps, "const { main } = require('${{ runner.temp }}/gh-aw/actions/install_frontmatter_skills.cjs');", "expected github-script runtime loader for skill install") + assert.NotContains(t, steps, "GH_AW_SKILL_SPEC_0", "expected per-skill env vars to be removed") } func TestBuildActivationJob_AddsExpressionSkillInstallSteps(t *testing.T) { @@ -67,9 +61,8 @@ func TestBuildActivationJob_AddsExpressionSkillInstallSteps(t *testing.T) { require.NotNil(t, job) steps := strings.Join(job.Steps, "") - assert.Contains(t, steps, "GH_AW_SKILL_SPEC_0: \"${{ inputs.skill_ref }}\"", "expected whole-expression skill env var") - assert.Contains(t, steps, "GH_AW_SKILL_SPEC_1: \"githubnext/skills@${{ github.sha }}\"", "expected expression-ref skill env var") - assert.NotContains(t, steps, "echo \"Installing skill reference: ${{ inputs.skill_ref }}\"", "expression should not be interpolated directly into the run script") + assert.Contains(t, steps, "GH_AW_FRONTMATTER_SKILLS: \"${{ inputs.skill_ref }}\\ngithubnext/skills@${{ github.sha }}\"", "expected skills env var to preserve expressions for runtime resolution") + assert.NotContains(t, steps, "GH_AW_SKILL_SPEC_0", "expected per-skill env vars to be removed") } func TestBuildActivationJob_NoSkillsStepsWhenSkillsAbsent(t *testing.T) { diff --git a/pkg/workflow/compiler_activation_job_builder.go b/pkg/workflow/compiler_activation_job_builder.go index 619f94767e7..af98b4d1164 100644 --- a/pkg/workflow/compiler_activation_job_builder.go +++ b/pkg/workflow/compiler_activation_job_builder.go @@ -529,11 +529,6 @@ func (c *Compiler) addActivationSkillInstallSteps(ctx *activationJobBuildContext engineID = ctx.data.EngineConfig.ID } skillDir := GetEngineSkillDir(engineID) - skillSpecsJSON, err := json.Marshal(ctx.data.Skills) - if err != nil { - return fmt.Errorf("marshal activation skill specs: %w", err) - } - escapedSkillSpecsJSON := strings.ReplaceAll(string(skillSpecsJSON), "'", "''") ctx.steps = append(ctx.steps, " - name: Upgrade gh CLI for frontmatter skills\n") ctx.steps = append(ctx.steps, " run: |\n") @@ -550,54 +545,12 @@ func (c *Compiler) addActivationSkillInstallSteps(ctx *activationJobBuildContext ctx.steps = append(ctx.steps, " - name: Install frontmatter skills\n") ctx.steps = append(ctx.steps, " env:\n") ctx.steps = append(ctx.steps, fmt.Sprintf(" GH_TOKEN: %s\n", c.resolveActivationToken(ctx.data))) - ctx.steps = append(ctx.steps, fmt.Sprintf(" GH_AW_SKILL_DIR: %q\n", skillDir)) - ctx.steps = append(ctx.steps, fmt.Sprintf(" GH_AW_SKILLS_SUMMARY: '%s'\n", escapedSkillSpecsJSON)) - for i, skillSpec := range ctx.data.Skills { - ctx.steps = append(ctx.steps, formatYAMLEnv(" ", fmt.Sprintf("GH_AW_SKILL_SPEC_%d", i), skillSpec)) - } - ctx.steps = append(ctx.steps, " run: |\n") - ctx.steps = append(ctx.steps, " set -euo pipefail\n") - ctx.steps = append(ctx.steps, " SKILLS_DST=\"/tmp/gh-aw/${GH_AW_SKILL_DIR}\"\n") - ctx.steps = append(ctx.steps, " mkdir -p \"${SKILLS_DST}\"\n") - ctx.steps = append(ctx.steps, " echo \"Installing frontmatter skills to ${SKILLS_DST}\"\n") - ctx.steps = append(ctx.steps, " echo \"Existing skills at destination may be replaced (--force) to ensure pinned refs are up to date\"\n") - for i := range ctx.data.Skills { - ctx.steps = append(ctx.steps, fmt.Sprintf(" skill_spec=\"${GH_AW_SKILL_SPEC_%d}\"\n", i)) - ctx.steps = append(ctx.steps, " echo \"Installing skill reference: ${skill_spec}\"\n") - // gh skill install expects [] as separate arguments. - // Split the spec into owner/repo and optional skill-path, and only add --pin - // when the resolved value includes an @ref suffix. - ctx.steps = append(ctx.steps, " skill_base=\"${skill_spec}\"\n") - ctx.steps = append(ctx.steps, " install_args=()\n") - ctx.steps = append(ctx.steps, " if [[ \"${skill_spec}\" == *@* ]]; then\n") - ctx.steps = append(ctx.steps, " skill_ref=\"${skill_spec##*@}\"\n") - ctx.steps = append(ctx.steps, " skill_base=\"${skill_spec%@*}\"\n") - ctx.steps = append(ctx.steps, " install_args+=(--pin \"${skill_ref}\")\n") - ctx.steps = append(ctx.steps, " fi\n") - ctx.steps = append(ctx.steps, " if [[ \"${skill_base}\" == */*/* ]]; then\n") - ctx.steps = append(ctx.steps, " skill_owner=\"${skill_base%%/*}\"\n") - ctx.steps = append(ctx.steps, " skill_remaining=\"${skill_base#*/}\"\n") - ctx.steps = append(ctx.steps, " skill_repo=\"${skill_owner}/${skill_remaining%%/*}\"\n") - ctx.steps = append(ctx.steps, " skill_subpath=\"${skill_remaining#*/}\"\n") - ctx.steps = append(ctx.steps, " gh skill install \"${skill_repo}\" \"${skill_subpath}\" \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force\n") - ctx.steps = append(ctx.steps, " elif [[ \"${skill_base}\" == */* ]]; then\n") - ctx.steps = append(ctx.steps, " gh skill install \"${skill_base}\" --all \"${install_args[@]}\" --dir \"${SKILLS_DST}\" --force\n") - ctx.steps = append(ctx.steps, " else\n") - ctx.steps = append(ctx.steps, " gh skill install \"${skill_spec}\" --dir \"${SKILLS_DST}\" --force\n") - ctx.steps = append(ctx.steps, " fi\n") - } - ctx.steps = append(ctx.steps, " SKILL_COUNT=$(find \"${SKILLS_DST}\" -name \"SKILL.md\" | wc -l | tr -d '[:space:]')\n") - ctx.steps = append(ctx.steps, " echo \"Installed ${SKILL_COUNT} skill file(s)\"\n") - ctx.steps = append(ctx.steps, " core_summary_path=\"${GITHUB_STEP_SUMMARY:-}\"\n") - ctx.steps = append(ctx.steps, " if [ -n \"${core_summary_path}\" ]; then\n") - ctx.steps = append(ctx.steps, " {\n") - ctx.steps = append(ctx.steps, " echo \"### Frontmatter skills installed\"\n") - ctx.steps = append(ctx.steps, " echo \"\"\n") - ctx.steps = append(ctx.steps, " echo \"- Engine skill directory: \\`${GH_AW_SKILL_DIR}\\`\"\n") - ctx.steps = append(ctx.steps, " echo \"- Requested references: \\`${GH_AW_SKILLS_SUMMARY}\\`\"\n") - ctx.steps = append(ctx.steps, " echo \"- Installed SKILL.md files: ${SKILL_COUNT}\"\n") - ctx.steps = append(ctx.steps, " } >> \"${core_summary_path}\"\n") - ctx.steps = append(ctx.steps, " fi\n") + ctx.steps = append(ctx.steps, formatYAMLEnv(" ", "GH_AW_SKILL_DIR", skillDir)) + ctx.steps = append(ctx.steps, formatYAMLEnv(" ", "GH_AW_FRONTMATTER_SKILLS", strings.Join(ctx.data.Skills, "\n"))) + ctx.steps = append(ctx.steps, fmt.Sprintf(" uses: %s\n", getCachedActionPin("actions/github-script", ctx.data))) + ctx.steps = append(ctx.steps, " with:\n") + ctx.steps = append(ctx.steps, " script: |\n") + ctx.steps = append(ctx.steps, generateGitHubScriptWithRequire("install_frontmatter_skills.cjs")) return nil } From 86966c10ea1c5c062e98816be739bbd2436f5fde Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 30 Jun 2026 19:59:48 +0000 Subject: [PATCH 6/6] chore: refresh generated frontmatter skill lockfile Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../mattpocock-skills-reviewer.lock.yml | 174 +----------------- 1 file changed, 8 insertions(+), 166 deletions(-) diff --git a/.github/workflows/mattpocock-skills-reviewer.lock.yml b/.github/workflows/mattpocock-skills-reviewer.lock.yml index 77a0dbae960..d83d02bd3f0 100644 --- a/.github/workflows/mattpocock-skills-reviewer.lock.yml +++ b/.github/workflows/mattpocock-skills-reviewer.lock.yml @@ -302,172 +302,14 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_AW_SKILL_DIR: ".github/skills" - GH_AW_SKILLS_SUMMARY: '["mattpocock/skills/diagnosing-bugs@801dca688564c529fa84f247f64472520d9ebe28","mattpocock/skills/tdd@801dca688564c529fa84f247f64472520d9ebe28","mattpocock/skills/improve-codebase-architecture@801dca688564c529fa84f247f64472520d9ebe28","mattpocock/skills/grill-with-docs@801dca688564c529fa84f247f64472520d9ebe28","mattpocock/skills/to-prd@801dca688564c529fa84f247f64472520d9ebe28","mattpocock/skills/codebase-design@801dca688564c529fa84f247f64472520d9ebe28","mattpocock/skills/domain-modeling@801dca688564c529fa84f247f64472520d9ebe28"]' - GH_AW_SKILL_SPEC_0: "mattpocock/skills/diagnosing-bugs@801dca688564c529fa84f247f64472520d9ebe28" - GH_AW_SKILL_SPEC_1: "mattpocock/skills/tdd@801dca688564c529fa84f247f64472520d9ebe28" - GH_AW_SKILL_SPEC_2: "mattpocock/skills/improve-codebase-architecture@801dca688564c529fa84f247f64472520d9ebe28" - GH_AW_SKILL_SPEC_3: "mattpocock/skills/grill-with-docs@801dca688564c529fa84f247f64472520d9ebe28" - GH_AW_SKILL_SPEC_4: "mattpocock/skills/to-prd@801dca688564c529fa84f247f64472520d9ebe28" - GH_AW_SKILL_SPEC_5: "mattpocock/skills/codebase-design@801dca688564c529fa84f247f64472520d9ebe28" - GH_AW_SKILL_SPEC_6: "mattpocock/skills/domain-modeling@801dca688564c529fa84f247f64472520d9ebe28" - run: | - set -euo pipefail - SKILLS_DST="/tmp/gh-aw/${GH_AW_SKILL_DIR}" - mkdir -p "${SKILLS_DST}" - echo "Installing frontmatter skills to ${SKILLS_DST}" - echo "Existing skills at destination may be replaced (--force) to ensure pinned refs are up to date" - skill_spec="${GH_AW_SKILL_SPEC_0}" - echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec}" - install_args=() - if [[ "${skill_spec}" == *@* ]]; then - skill_ref="${skill_spec##*@}" - skill_base="${skill_spec%@*}" - install_args+=(--pin "${skill_ref}") - fi - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force - fi - skill_spec="${GH_AW_SKILL_SPEC_1}" - echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec}" - install_args=() - if [[ "${skill_spec}" == *@* ]]; then - skill_ref="${skill_spec##*@}" - skill_base="${skill_spec%@*}" - install_args+=(--pin "${skill_ref}") - fi - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force - fi - skill_spec="${GH_AW_SKILL_SPEC_2}" - echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec}" - install_args=() - if [[ "${skill_spec}" == *@* ]]; then - skill_ref="${skill_spec##*@}" - skill_base="${skill_spec%@*}" - install_args+=(--pin "${skill_ref}") - fi - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force - fi - skill_spec="${GH_AW_SKILL_SPEC_3}" - echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec}" - install_args=() - if [[ "${skill_spec}" == *@* ]]; then - skill_ref="${skill_spec##*@}" - skill_base="${skill_spec%@*}" - install_args+=(--pin "${skill_ref}") - fi - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force - fi - skill_spec="${GH_AW_SKILL_SPEC_4}" - echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec}" - install_args=() - if [[ "${skill_spec}" == *@* ]]; then - skill_ref="${skill_spec##*@}" - skill_base="${skill_spec%@*}" - install_args+=(--pin "${skill_ref}") - fi - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force - fi - skill_spec="${GH_AW_SKILL_SPEC_5}" - echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec}" - install_args=() - if [[ "${skill_spec}" == *@* ]]; then - skill_ref="${skill_spec##*@}" - skill_base="${skill_spec%@*}" - install_args+=(--pin "${skill_ref}") - fi - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force - fi - skill_spec="${GH_AW_SKILL_SPEC_6}" - echo "Installing skill reference: ${skill_spec}" - skill_base="${skill_spec}" - install_args=() - if [[ "${skill_spec}" == *@* ]]; then - skill_ref="${skill_spec##*@}" - skill_base="${skill_spec%@*}" - install_args+=(--pin "${skill_ref}") - fi - if [[ "${skill_base}" == */*/* ]]; then - skill_owner="${skill_base%%/*}" - skill_remaining="${skill_base#*/}" - skill_repo="${skill_owner}/${skill_remaining%%/*}" - skill_subpath="${skill_remaining#*/}" - gh skill install "${skill_repo}" "${skill_subpath}" "${install_args[@]}" --dir "${SKILLS_DST}" --force - elif [[ "${skill_base}" == */* ]]; then - gh skill install "${skill_base}" --all "${install_args[@]}" --dir "${SKILLS_DST}" --force - else - gh skill install "${skill_spec}" --dir "${SKILLS_DST}" --force - fi - SKILL_COUNT=$(find "${SKILLS_DST}" -name "SKILL.md" | wc -l | tr -d '[:space:]') - echo "Installed ${SKILL_COUNT} skill file(s)" - core_summary_path="${GITHUB_STEP_SUMMARY:-}" - if [ -n "${core_summary_path}" ]; then - { - echo "### Frontmatter skills installed" - echo "" - echo "- Engine skill directory: \`${GH_AW_SKILL_DIR}\`" - echo "- Requested references: \`${GH_AW_SKILLS_SUMMARY}\`" - echo "- Installed SKILL.md files: ${SKILL_COUNT}" - } >> "${core_summary_path}" - fi + GH_AW_FRONTMATTER_SKILLS: "mattpocock/skills/diagnosing-bugs@801dca688564c529fa84f247f64472520d9ebe28\nmattpocock/skills/tdd@801dca688564c529fa84f247f64472520d9ebe28\nmattpocock/skills/improve-codebase-architecture@801dca688564c529fa84f247f64472520d9ebe28\nmattpocock/skills/grill-with-docs@801dca688564c529fa84f247f64472520d9ebe28\nmattpocock/skills/to-prd@801dca688564c529fa84f247f64472520d9ebe28\nmattpocock/skills/codebase-design@801dca688564c529fa84f247f64472520d9ebe28\nmattpocock/skills/domain-modeling@801dca688564c529fa84f247f64472520d9ebe28" + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/install_frontmatter_skills.cjs'); + await main(); - name: Log runtime features if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }} run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh"