mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-06-24 02:04:11 +00:00
Prevent partial jangbu wrapper installs on collisions
Promoted upstream jangbu skills now preflight every Claude and agents top-level destination before the installer mutates home skill directories. This keeps unrelated user-authored skills from causing a mixed partial discovery state, while preserving the existing managed-marker overwrite path and the explicit override escape hatch. The installer also prints the namespace re-sync warning next to the upstream runtime install command so users know to restore wrapper-managed top-level skills after running upstream's Claude-only installer. Constraint: Upstream skill contents must be checked out before collision preflight can validate promoted SKILL.md files. Rejected: Roll back cache checkout on collision | cache writes are outside the advertised home skill discovery namespace and are needed to inspect pinned upstream content. Confidence: high Scope-risk: narrow Directive: Keep promoted-skill collision checks before install_wrapper_payload and sync_dir calls for home skill roots. Tested: bash -n korean-jangbu-for/scripts/install.sh Tested: node --test scripts/skill-docs.test.js --test-name-pattern='korean-jangbu-for' Tested: temp HOME real pinned upstream install and .agents jangbu-tax collision preflight smoke Tested: npm run ci
This commit is contained in:
parent
11b1150110
commit
c934b4076b
2 changed files with 59 additions and 5 deletions
|
|
@ -91,9 +91,8 @@ is_managed_promoted_skill() {
|
|||
[[ -f "${skill_file}" ]] && grep -q "${MANAGED_MARKER}" "${skill_file}"
|
||||
}
|
||||
|
||||
sync_promoted_skill() {
|
||||
local source_dir="$1"
|
||||
local target_dir="$2"
|
||||
assert_promoted_skill_writable() {
|
||||
local target_dir="$1"
|
||||
|
||||
if [[ -e "${target_dir}" || -L "${target_dir}" ]]; then
|
||||
if [[ "${KOREAN_JANGBU_FOR_OVERWRITE_SKILLS:-}" != "1" ]] && ! is_managed_promoted_skill "${target_dir}"; then
|
||||
|
|
@ -102,10 +101,46 @@ sync_promoted_skill() {
|
|||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
sync_promoted_skill() {
|
||||
local source_dir="$1"
|
||||
local target_dir="$2"
|
||||
|
||||
assert_promoted_skill_writable "${target_dir}"
|
||||
sync_dir "${source_dir}" "${target_dir}"
|
||||
}
|
||||
|
||||
preflight_promoted_skill() {
|
||||
local source_dir="$1"
|
||||
local target_dir="$2"
|
||||
|
||||
if [[ ! -f "${source_dir}/SKILL.md" ]]; then
|
||||
echo "[korean-jangbu-for] missing upstream skill: ${source_dir}/SKILL.md" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
assert_promoted_skill_writable "${target_dir}"
|
||||
}
|
||||
|
||||
preflight_promoted_skills() {
|
||||
local home_skill_dir
|
||||
local home_skills_root
|
||||
local upstream_skill
|
||||
local upstream_skill_dir
|
||||
local home_upstream_skill_dir
|
||||
|
||||
for home_skill_dir in "${HOME_DIRS[@]}"; do
|
||||
home_skills_root="$(dirname "${home_skill_dir}")"
|
||||
|
||||
for upstream_skill in "${UPSTREAM_SUBSKILLS[@]}"; do
|
||||
upstream_skill_dir="${CLONE_DIR}/skills/${upstream_skill}"
|
||||
home_upstream_skill_dir="${home_skills_root}/${upstream_skill}"
|
||||
preflight_promoted_skill "${upstream_skill_dir}" "${home_upstream_skill_dir}"
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
append_response_policy() {
|
||||
local skill_file="$1"
|
||||
|
||||
|
|
@ -152,6 +187,8 @@ HOME_DIRS=(
|
|||
"${HOME}/.agents/skills/${SKILL_NAME}"
|
||||
)
|
||||
|
||||
preflight_promoted_skills
|
||||
|
||||
for HOME_SKILL_DIR in "${HOME_DIRS[@]}"; do
|
||||
HOME_UPSTREAM="${HOME_SKILL_DIR}/upstream"
|
||||
HOME_SKILLS_ROOT="$(dirname "${HOME_SKILL_DIR}")"
|
||||
|
|
@ -193,6 +230,7 @@ echo " pinned upstream SHA: ${UPSTREAM_SHA}"
|
|||
echo " upstream repo: ${UPSTREAM_REPO}"
|
||||
echo " runtime install: bash ~/.claude/skills/korean-jangbu-for/upstream/scripts/install.sh"
|
||||
echo " verify command: bash ~/.claude/skills/korean-jangbu-for/upstream/scripts/verify.sh"
|
||||
echo " namespace note: Re-run this wrapper installer after upstream runtime install to restore wrapper-managed top-level skills."
|
||||
echo " subskills: /korean-jangbu-for /jangbu-connect /jangbu-import /jangbu-tag /jangbu-tax /jangbu-dash /jangbu-jongso"
|
||||
echo " 원저작자: @kimlawtech (SpeciAI) — 응답마다 원본 링크와 함께 언급해야 한다."
|
||||
echo " 생성물은 참고용 초안이며 공식 회계감사·세무신고를 대체하지 않는다."
|
||||
|
|
|
|||
|
|
@ -2995,6 +2995,7 @@ test("korean-jangbu-for ships an install.sh wrapper and a pinned upstream SHA",
|
|||
assert.match(install, /git clone --filter=blob:none/);
|
||||
assert.match(install, /upstream\.pin/);
|
||||
assert.match(install, /verify\.sh/);
|
||||
assert.match(install, /Re-run this wrapper installer after upstream runtime install/);
|
||||
|
||||
const stat = fs.statSync(installPath);
|
||||
assert.ok((stat.mode & 0o111) !== 0, "install.sh must be executable");
|
||||
|
|
@ -3115,7 +3116,7 @@ test("korean-jangbu-for installer registers upstream subskills for Claude and ag
|
|||
}
|
||||
});
|
||||
|
||||
test("korean-jangbu-for installer refuses to overwrite unrelated promoted subskills", () => {
|
||||
test("korean-jangbu-for installer preflights promoted subskill collisions before home writes", () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "korean-jangbu-for-collision-"));
|
||||
const homeDir = path.join(tmpDir, "home");
|
||||
const upstreamDir = path.join(tmpDir, "upstream");
|
||||
|
|
@ -3147,7 +3148,7 @@ test("korean-jangbu-for installer refuses to overwrite unrelated promoted subski
|
|||
childProcess.execFileSync("git", ["commit", "-m", "seed upstream skills"], { cwd: upstreamDir, stdio: "ignore" });
|
||||
const upstreamSha = childProcess.execFileSync("git", ["rev-parse", "HEAD"], { cwd: upstreamDir, encoding: "utf8" }).trim();
|
||||
|
||||
const unrelatedSkillDir = path.join(homeDir, ".claude", "skills", "jangbu-tax");
|
||||
const unrelatedSkillDir = path.join(homeDir, ".agents", "skills", "jangbu-tax");
|
||||
fs.mkdirSync(unrelatedSkillDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(unrelatedSkillDir, "SKILL.md"),
|
||||
|
|
@ -3174,6 +3175,21 @@ test("korean-jangbu-for installer refuses to overwrite unrelated promoted subski
|
|||
/user-authored jangbu-tax/,
|
||||
"unrelated existing subskill should be preserved after installer refusal",
|
||||
);
|
||||
|
||||
for (const root of [".claude", ".agents"]) {
|
||||
const skillRoot = path.join(homeDir, root, "skills");
|
||||
assert.ok(
|
||||
!fs.existsSync(path.join(skillRoot, "korean-jangbu-for")),
|
||||
`${root} should not create the wrapper directory after a promoted-subskill preflight failure`,
|
||||
);
|
||||
|
||||
for (const skillName of upstreamSubskills.filter((name) => name !== "jangbu-tax")) {
|
||||
assert.ok(
|
||||
!fs.existsSync(path.join(skillRoot, skillName)),
|
||||
`${root} should not create promoted subskill ${skillName} after a promoted-subskill preflight failure`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("korean-jangbu-for feature doc documents source-first use and mandatory attribution", () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue