refactor(halcon): single-skill plugin layout → invoke as /halcon (not /halcon:halcon)

Move SKILL.md + references/ scripts/ evals/ from skills/halcon/ up to the
plugin root. Per Claude Code plugins-reference, a plugin with SKILL.md at its
root and no skills/ subdir is auto-loaded as a single-skill plugin (v2.1.142+),
so the invocation name = frontmatter name = halcon → clean /halcon.
Bump plugin.json 2.0.0 → 2.0.1 so existing installs receive the update.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 23:14:39 +08:00
parent 36eadffa87
commit 3fe919e37c
44 changed files with 1 additions and 1 deletions
+40
View File
@@ -0,0 +1,40 @@
"""Shared helpers for the HALCON runner scripts."""
import re
_INT_RE = re.compile(r"^[+-]?\d+$")
_FLOAT_RE = re.compile(r"^[+-]?(\d+\.\d*|\.\d+|\d+)([eE][+-]?\d+)?$")
def coerce(value: str):
"""Turn a CLI string into int/float/str, or a list for comma-separated input.
Absolute paths (C:/..., /mnt/...) and anything non-numeric stay strings.
Use a trailing comma or commas to force a tuple, e.g. '1,2,3' -> [1,2,3].
"""
if "," in value:
return [coerce(v) for v in value.split(",") if v != ""]
if _INT_RE.match(value):
return int(value)
if _FLOAT_RE.match(value) and not value.strip().startswith(("C:", "c:")):
return float(value)
return value
def jsonable(v):
"""Make a HALCON output control value JSON-serializable."""
if isinstance(v, (list, tuple)):
return [jsonable(x) for x in v]
if isinstance(v, (int, float, str, bool)) or v is None:
return v
return str(v)
def parse_set(pairs):
"""['Name=Value', ...] -> [(name, coerced_value), ...]."""
out = []
for p in pairs or []:
if "=" not in p:
raise SystemExit(f"--set expects Name=Value, got: {p!r}")
name, _, raw = p.partition("=")
out.append((name.strip(), coerce(raw)))
return out