feat: package vscode skill target
This commit is contained in:
@@ -87,12 +87,14 @@ TARGET_TRANSFORMS: dict[str, dict[str, Any]] = {
|
||||
"unsupported_features": [],
|
||||
},
|
||||
"vscode": {
|
||||
"adapter_mode": "agent-skills-compatible-project-or-user-scope",
|
||||
"generated_files": ["SKILL.md", "agents/interface.yaml"],
|
||||
"adapter_mode": "vscode-agent-skills-adapter",
|
||||
"generated_files": ["targets/vscode/adapter.json", "targets/vscode/README.md"],
|
||||
"metadata_mapping": {
|
||||
"name": "folder-name-and-SKILL.md::frontmatter.name",
|
||||
"description": "SKILL.md::frontmatter.description",
|
||||
"interface": "agents/interface.yaml",
|
||||
"permissions": "targets/vscode/adapter.json::target_permission_contract",
|
||||
"install_scope": "targets/vscode/README.md",
|
||||
},
|
||||
"preserved_semantics": ["trigger", "workflow", "resources", "eval-plan", "risk", "governance", "runtime", "trust", "permissions"],
|
||||
"unsupported_features": ["VS Code installation scope is documented but not installed by this compiler"],
|
||||
@@ -265,6 +267,12 @@ TARGET_PERMISSION_MODELS = {
|
||||
"representation": "targets/generic/adapter.json::target_permission_contract",
|
||||
"operator_note": "Generic target exposes permission metadata for downstream clients to enforce or review.",
|
||||
},
|
||||
"vscode": {
|
||||
"model": "vscode-workspace-trust-plus-metadata",
|
||||
"native_enforcement": False,
|
||||
"representation": "targets/vscode/adapter.json::target_permission_contract and targets/vscode/README.md install notes",
|
||||
"operator_note": "VS Code target relies on project or user skill installation plus VS Code workspace trust; Yao preserves permission metadata for reviewer and installer checks.",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -545,7 +553,7 @@ def compile_target_contract(skill_dir: Path, target: str) -> dict[str, Any]:
|
||||
"preserved_semantics": [],
|
||||
"unsupported_features": [],
|
||||
}
|
||||
if target not in declared and not (target == "agent-skills" and "agent-skills-compatible" in declared):
|
||||
if target not in declared and not (target in {"agent-skills", "vscode"} and "agent-skills-compatible" in declared):
|
||||
warnings.append(f"Target is not declared in Skill IR or interface metadata: {target}")
|
||||
if not sources["ir"]:
|
||||
warnings.append("Skill IR is missing; compiler used frontmatter fallback")
|
||||
|
||||
@@ -147,13 +147,17 @@ def build_semantic_contract(
|
||||
"targets": target_values,
|
||||
"source_files_count": count_list(ir, "source_files") if ir else 0,
|
||||
}
|
||||
alias_declared = (
|
||||
platform in {"agent-skills", "vscode"} and "agent-skills-compatible" in target_values
|
||||
)
|
||||
semantic_parity = {
|
||||
"source": "skill-ir" if ir else "frontmatter-fallback",
|
||||
"ir_source": ir_source,
|
||||
"name_matches_ir": bool(ir) and frontmatter_name == name,
|
||||
"description_matches_ir": bool(ir) and frontmatter_description == description,
|
||||
"platform_declared_in_ir": platform in target_values
|
||||
or (platform == "generic" and "agent-skills-compatible" in target_values),
|
||||
or (platform == "generic" and "agent-skills-compatible" in target_values)
|
||||
or alias_declared,
|
||||
"platform_declared_in_interface": platform in adapter_targets,
|
||||
"display_name_present": bool(interface.get("display_name")),
|
||||
"default_prompt_present": bool(interface.get("default_prompt")),
|
||||
@@ -356,6 +360,44 @@ PLATFORM_CONTRACTS = {
|
||||
"shell": "compatibility.execution.shell",
|
||||
},
|
||||
},
|
||||
"vscode": {
|
||||
"required_fields": [
|
||||
"name",
|
||||
"description",
|
||||
"version",
|
||||
"display_name",
|
||||
"short_description",
|
||||
"default_prompt",
|
||||
"job_to_be_done",
|
||||
"ir_source",
|
||||
"ir_schema_version",
|
||||
"semantic_contract",
|
||||
"semantic_parity",
|
||||
"compiler",
|
||||
"compiled_contract",
|
||||
"permission_contract",
|
||||
"target_permission_contract",
|
||||
"target_native_contract",
|
||||
"target_transform",
|
||||
"canonical_metadata",
|
||||
"canonical_format",
|
||||
"activation_mode",
|
||||
"execution_context",
|
||||
"shell",
|
||||
"trust_level",
|
||||
"remote_inline_execution",
|
||||
"degradation_strategy",
|
||||
"portability_profile",
|
||||
],
|
||||
"required_files": ["targets/vscode/adapter.json", "targets/vscode/README.md"],
|
||||
"field_mapping": {
|
||||
"name": "SKILL.md::frontmatter.name and folder name",
|
||||
"description": "SKILL.md::frontmatter.description",
|
||||
"display_name": "agents/interface.yaml::interface.display_name",
|
||||
"execution_context": "compatibility.execution.context",
|
||||
"permissions": "adapter.target_permission_contract",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
EXCLUDED_ARCHIVE_PARTS = {".git", "__pycache__", ".venv", "venv", "node_modules", "dist"}
|
||||
@@ -463,6 +505,24 @@ def write_adapter(skill_dir: Path, out_dir: Path, platform: str) -> Path:
|
||||
encoding="utf-8",
|
||||
)
|
||||
payload["install_hint"] = f"Use the packaged skill directly; this target relies on SKILL.md and optional neutral metadata."
|
||||
elif platform == "vscode":
|
||||
notes = target_dir / "README.md"
|
||||
native = payload["target_native_contract"]
|
||||
notes.write_text(
|
||||
f"# VS Code / Copilot Agent Skills Package\n\n"
|
||||
f"Install `{skill_dir.name}` as a VS Code user or project scoped Agent Skill. Keep the folder name aligned with `SKILL.md` frontmatter name.\n\n"
|
||||
f"Native surface: {native['native_surface']}.\n\n"
|
||||
f"Activation: {native['activation']['policy']}\n\n"
|
||||
f"Resources: {native['resources']['strategy']}\n\n"
|
||||
f"Scripts: {native['scripts']['strategy']}\n\n"
|
||||
f"Permission model: {payload['target_permission_contract']['permission_model']}. "
|
||||
"Review `target_permission_contract`, workspace trust, and `reports/security_trust_report.md` before running scripts.\n\n"
|
||||
"This adapter does not perform automatic VS Code installation; it preserves the reviewed source package plus install notes.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
payload["install_hint"] = (
|
||||
"Install the package as a VS Code user or project scoped Agent Skill; use targets/vscode/README.md for scope and trust notes."
|
||||
)
|
||||
else:
|
||||
payload["install_hint"] = f"Use {skill_dir.name} as an Agent Skills compatible package."
|
||||
path = target_dir / "adapter.json"
|
||||
@@ -511,9 +571,9 @@ def validate_exports(out_dir: Path, expectations: dict) -> dict:
|
||||
required_targets = expectations.get("required_targets", [])
|
||||
required_fields = expectations.get("required_fields", [])
|
||||
required_by_target = {
|
||||
"openai": expectations.get("openai_required_files", []),
|
||||
"claude": expectations.get("claude_required_files", []),
|
||||
"generic": expectations.get("generic_required_files", []),
|
||||
key[: -len("_required_files")]: value
|
||||
for key, value in expectations.items()
|
||||
if key.endswith("_required_files")
|
||||
}
|
||||
|
||||
for target in required_targets:
|
||||
@@ -534,7 +594,7 @@ def validate_exports(out_dir: Path, expectations: dict) -> dict:
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Generate lightweight cross-platform packaging artifacts.")
|
||||
parser.add_argument("skill_dir", help="Path to the skill directory")
|
||||
parser.add_argument("--platform", action="append", default=[], help="Target platform: openai, claude, generic")
|
||||
parser.add_argument("--platform", action="append", default=[], help="Target platform: openai, claude, generic, vscode")
|
||||
parser.add_argument("--output-dir", default="dist", help="Output directory")
|
||||
parser.add_argument("--expectations", help="JSON file describing packaging expectations")
|
||||
parser.add_argument("--zip", action="store_true", help="Create a zip package")
|
||||
|
||||
@@ -129,6 +129,7 @@ compatibility:
|
||||
- "openai"
|
||||
- "claude"
|
||||
- "generic"
|
||||
- "vscode"
|
||||
activation:
|
||||
mode: "manual"
|
||||
paths: []
|
||||
@@ -143,6 +144,7 @@ compatibility:
|
||||
openai: "metadata-adapter"
|
||||
claude: "neutral-source-plus-adapter"
|
||||
generic: "neutral-source"
|
||||
vscode: "agent-skills-source-with-vscode-notes"
|
||||
"""
|
||||
|
||||
|
||||
@@ -213,6 +215,7 @@ def build_manifest(name: str, mode: str, archetype: str) -> dict:
|
||||
"claude",
|
||||
"generic",
|
||||
"agent-skills-compatible",
|
||||
"vscode",
|
||||
],
|
||||
"factory_components": [
|
||||
"references",
|
||||
|
||||
@@ -10,7 +10,7 @@ except ImportError: # pragma: no cover
|
||||
yaml = None
|
||||
|
||||
|
||||
DEFAULT_TARGETS = ["openai", "claude", "generic"]
|
||||
DEFAULT_TARGETS = ["openai", "claude", "generic", "vscode"]
|
||||
|
||||
|
||||
def display_path(path: Path, root: Path) -> str:
|
||||
|
||||
@@ -108,12 +108,13 @@ def verify_package(
|
||||
field in adapter,
|
||||
f"{target} adapter includes field: {field}",
|
||||
)
|
||||
for target, key in (
|
||||
("openai", "openai_required_files"),
|
||||
("claude", "claude_required_files"),
|
||||
("generic", "generic_required_files"),
|
||||
):
|
||||
for rel in expectations.get(key, []):
|
||||
required_files_by_target = {
|
||||
key[: -len("_required_files")]: value
|
||||
for key, value in expectations.items()
|
||||
if key.endswith("_required_files")
|
||||
}
|
||||
for target, required_files in required_files_by_target.items():
|
||||
for rel in required_files:
|
||||
add_check(checks, failures, f"{target}-file-{rel}", (package_dir / rel).exists(), f"Package contains {rel}")
|
||||
|
||||
archive_path = package_dir / f"{skill_dir.name}.zip"
|
||||
|
||||
Reference in New Issue
Block a user