feat(build.py): --no-purge flag and opencode-archive install target
Conflict fix: --install opencode wipes ~/.config/opencode/skills/ on every build, which clobbers the work of external managers like opc-skills. New flag --no-purge skips that wipe so opc-skills' enabled set survives a rebuild. New install target opencode-archive: instead of writing to ~/.config/opencode/, it regenerates personas/skills-archive/ + personas/agents-opencode-archive/ (the source-of-truth that opc-skills + opc-agents consume). This lets the build pipeline stay decoupled from runtime install dirs. install_opencode() now accepts agents_dest / skills_dest overrides for the archive target. Live target behavior unchanged.
This commit is contained in:
67
build.py
67
build.py
@@ -1513,6 +1513,9 @@ def install_opencode(
|
|||||||
output_dir: Path,
|
output_dir: Path,
|
||||||
shared_dir: Path | None = None,
|
shared_dir: Path | None = None,
|
||||||
topics: set[str] | None = None,
|
topics: set[str] | None = None,
|
||||||
|
purge_skills: bool = True,
|
||||||
|
agents_dest: Path | None = None,
|
||||||
|
skills_dest: Path | None = None,
|
||||||
):
|
):
|
||||||
"""Install personas to OpenCode as agents + skills.
|
"""Install personas to OpenCode as agents + skills.
|
||||||
|
|
||||||
@@ -1529,11 +1532,26 @@ def install_opencode(
|
|||||||
Args:
|
Args:
|
||||||
topics: set of topics to install (see OPENCODE_TOPICS). Defaults to
|
topics: set of topics to install (see OPENCODE_TOPICS). Defaults to
|
||||||
OPENCODE_DEFAULT_TOPICS which drops marketing/biz skills.
|
OPENCODE_DEFAULT_TOPICS which drops marketing/biz skills.
|
||||||
|
purge_skills: when True (default), wipe ~/.config/opencode/skills/
|
||||||
|
before installing. Set False (via --no-purge) when this dir is also
|
||||||
|
managed by an external tool (e.g. opc-skills) — purge would otherwise
|
||||||
|
delete its work on every build.
|
||||||
"""
|
"""
|
||||||
if topics is None:
|
if topics is None:
|
||||||
topics = OPENCODE_DEFAULT_TOPICS
|
topics = OPENCODE_DEFAULT_TOPICS
|
||||||
agents_dir = Path.home() / ".config" / "opencode" / "agents"
|
# Archive mode: agents_dest / skills_dest override the live opencode dirs.
|
||||||
skills_dir = Path.home() / ".config" / "opencode" / "skills"
|
# Used by --install opencode-archive to regenerate the personas archive
|
||||||
|
# that opc-skills and opc-agents consume as source-of-truth.
|
||||||
|
if agents_dest is not None:
|
||||||
|
agents_dir = agents_dest
|
||||||
|
agents_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
else:
|
||||||
|
agents_dir = Path.home() / ".config" / "opencode" / "agents"
|
||||||
|
if skills_dest is not None:
|
||||||
|
skills_dir = skills_dest
|
||||||
|
skills_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
else:
|
||||||
|
skills_dir = Path.home() / ".config" / "opencode" / "skills"
|
||||||
agents_dir.mkdir(parents=True, exist_ok=True)
|
agents_dir.mkdir(parents=True, exist_ok=True)
|
||||||
skills_dir.mkdir(parents=True, exist_ok=True)
|
skills_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
@@ -1719,12 +1737,16 @@ def install_opencode(
|
|||||||
skipped_topic = 0
|
skipped_topic = 0
|
||||||
|
|
||||||
# Purge existing skills dir so stale filtered-out skills are removed.
|
# Purge existing skills dir so stale filtered-out skills are removed.
|
||||||
if skills_dir.exists():
|
# Skipped when --no-purge is passed (so external tools like opc-skills
|
||||||
|
# that manage this directory don't get clobbered on every build).
|
||||||
|
if purge_skills and skills_dir.exists():
|
||||||
import shutil as _shutil
|
import shutil as _shutil
|
||||||
|
|
||||||
for existing in skills_dir.iterdir():
|
for existing in skills_dir.iterdir():
|
||||||
if existing.is_dir():
|
if existing.is_dir():
|
||||||
_shutil.rmtree(existing)
|
_shutil.rmtree(existing)
|
||||||
|
elif not purge_skills:
|
||||||
|
print(f" OpenCode: --no-purge — preserving existing {skills_dir}/")
|
||||||
|
|
||||||
if shared_dir:
|
if shared_dir:
|
||||||
for skills_subdir in [
|
for skills_subdir in [
|
||||||
@@ -2107,12 +2129,16 @@ def main():
|
|||||||
"gemini",
|
"gemini",
|
||||||
"openclaw",
|
"openclaw",
|
||||||
"opencode",
|
"opencode",
|
||||||
|
"opencode-archive",
|
||||||
"paperclip",
|
"paperclip",
|
||||||
"all",
|
"all",
|
||||||
],
|
],
|
||||||
help="Install generated personas to a target platform. "
|
help="Install generated personas to a target platform. "
|
||||||
"'claude' installs persona agents+commands; 'claude-skills' installs "
|
"'claude' installs persona agents+commands; 'claude-skills' installs "
|
||||||
"shared skills to ~/.claude/skills/ with category filters.",
|
"shared skills to ~/.claude/skills/ with category filters; "
|
||||||
|
"'opencode-archive' writes opencode-format agents+skills to "
|
||||||
|
"personas/agents-opencode-archive/ + personas/skills-archive/ "
|
||||||
|
"(consumed by opc-skills/opc-agents) without touching ~/.config/opencode/.",
|
||||||
)
|
)
|
||||||
# --- claude-skills filters --------------------------------------------
|
# --- claude-skills filters --------------------------------------------
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -2171,6 +2197,13 @@ def main():
|
|||||||
"business-pm, uncategorized. "
|
"business-pm, uncategorized. "
|
||||||
"Default drops marketing/biz. Use 'all' for no filter.",
|
"Default drops marketing/biz. Use 'all' for no filter.",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-purge",
|
||||||
|
action="store_true",
|
||||||
|
help="For --install opencode: do NOT wipe ~/.config/opencode/skills/ "
|
||||||
|
"before installing. Use when the directory is also managed by an "
|
||||||
|
"external tool (e.g. opc-skills) so its enabled skills are preserved.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--search",
|
"--search",
|
||||||
type=str,
|
type=str,
|
||||||
@@ -2325,7 +2358,7 @@ def main():
|
|||||||
install_gemini(output_dir)
|
install_gemini(output_dir)
|
||||||
elif target == "openclaw":
|
elif target == "openclaw":
|
||||||
install_openclaw(output_dir)
|
install_openclaw(output_dir)
|
||||||
elif target == "opencode":
|
elif target in ("opencode", "opencode-archive"):
|
||||||
if args.opencode_topics:
|
if args.opencode_topics:
|
||||||
if args.opencode_topics.strip().lower() == "all":
|
if args.opencode_topics.strip().lower() == "all":
|
||||||
oc_topics = OPENCODE_TOPICS
|
oc_topics = OPENCODE_TOPICS
|
||||||
@@ -2337,7 +2370,29 @@ def main():
|
|||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
oc_topics = None # use default
|
oc_topics = None # use default
|
||||||
install_opencode(output_dir, shared_dir, topics=oc_topics)
|
if target == "opencode-archive":
|
||||||
|
# Source-of-truth layout consumed by opc-skills + opc-agents:
|
||||||
|
# personas/skills-archive/<name>/SKILL.md ← opc-skills reads
|
||||||
|
# personas/agents-opencode-archive/<n>.md ← opc-agents reads
|
||||||
|
install_opencode(
|
||||||
|
output_dir,
|
||||||
|
shared_dir,
|
||||||
|
topics=oc_topics,
|
||||||
|
purge_skills=False,
|
||||||
|
agents_dest=root / "agents-opencode-archive",
|
||||||
|
skills_dest=root / "skills-archive",
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
" opcode-archive: regenerated source-of-truth used by "
|
||||||
|
"opc-skills + opc-agents. Did NOT touch ~/.config/opencode/."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
install_opencode(
|
||||||
|
output_dir,
|
||||||
|
shared_dir,
|
||||||
|
topics=oc_topics,
|
||||||
|
purge_skills=not args.no_purge,
|
||||||
|
)
|
||||||
elif target == "paperclip":
|
elif target == "paperclip":
|
||||||
install_paperclip(output_dir, personas_dir, shared_dir)
|
install_paperclip(output_dir, personas_dir, shared_dir)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user