build(ci): uv toolchain, release-please semver; feat(skills): vendor tools self-contained (no symlinks, relative refs)
- bin/db/psql-yq + bin/web/search + tools/{yamlout,websearch} vendored as real files
- bin/db/ssh-tunnel added (OnlyOffice VM pg on 5433)
- skills reference local bin/ paths; no agent-skills/abs links in git
- pyproject.toml + uv.lock; CI installs via uv sync --frozen
- release-please auto-tags semver from conventional commits when green
- LICENSE MIT, badges/mermaid README
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
"""Minimal YAML emitter.
|
||||
|
||||
Agents read YAML more cheaply than JSON and the output stays diffable. This is
|
||||
deliberately tiny: it emits the shapes these tools produce, nothing more.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
|
||||
def to_yaml(node, indent: int = 0) -> str:
|
||||
pad = " " * indent
|
||||
if isinstance(node, dict):
|
||||
if not node:
|
||||
return f"{pad}{{}}\n"
|
||||
out = ""
|
||||
for key, value in node.items():
|
||||
if isinstance(value, (dict, list)) and value:
|
||||
out += f"{pad}{key}:\n{to_yaml(value, indent + 1)}"
|
||||
elif isinstance(value, (dict, list)):
|
||||
out += f"{pad}{key}: {'{}' if isinstance(value, dict) else '[]'}\n"
|
||||
else:
|
||||
out += f"{pad}{key}: {scalar(value)}\n"
|
||||
return out
|
||||
if isinstance(node, list):
|
||||
out = ""
|
||||
for item in node:
|
||||
if isinstance(item, dict):
|
||||
out += f"{pad}-\n{to_yaml(item, indent + 1)}"
|
||||
else:
|
||||
out += f"{pad}- {scalar(item)}\n"
|
||||
return out
|
||||
return f"{pad}{scalar(node)}\n"
|
||||
|
||||
|
||||
def scalar(value) -> str:
|
||||
if value is None:
|
||||
return "null"
|
||||
if isinstance(value, bool):
|
||||
return "true" if value else "false"
|
||||
if isinstance(value, (int, float)):
|
||||
return str(value)
|
||||
text = str(value)
|
||||
if "\n" in text:
|
||||
return json.dumps(text, ensure_ascii=False)
|
||||
if text == "" or any(ch in text for ch in ":#'\"[]{}&*!|>%@`") or text != text.strip():
|
||||
return json.dumps(text, ensure_ascii=False)
|
||||
return text
|
||||
Reference in New Issue
Block a user