forked from retoor/devplacepy
Community nodes (Phase 1 of plan.md): the protocol specification in services/nodes/spec.py as the single source of truth, GET /nodes/spec.json, a generated protocol docs page, the Community nodes API docs group, and the implementation plan plus the residential proxy research. The no-open-proxy rule is enforced in the spec (open_proxy cannot be set). Agent Skills: register the Skills API docs group, cascade skills on account deletion, add the nested CLAUDE.md, README and routes docs, and cover the parser, the fetcher (end to end against a mock origin), the data layer and the routes with 73 unit and 16 api tests. Tests made hermetic with the repo's existing patterns instead of ambient environment: embeddings tests set OPENROUTER_API_KEY via monkeypatch, the workspace tests install FakeBackend via runtime.set_backend, and the skills install tests use run_async. DockerCliBackend now returns 127 (command not found) when the CLI is missing instead of raising FileNotFoundError, so a host without docker answers with a clean error rather than a 500. The Providers tab empty state no longer names the Model routes tab. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
31 lines
934 B
Python
31 lines
934 B
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from devplacepy.database.skills import enabled_skill_summaries
|
|
from devplacepy.services.skills import limits
|
|
|
|
WHITESPACE_PATTERN = re.compile(r"\s+")
|
|
ELLIPSIS = "..."
|
|
|
|
|
|
def _one_line(text: str, maximum: int) -> str:
|
|
collapsed = WHITESPACE_PATTERN.sub(" ", text).strip().lstrip("#- ").strip()
|
|
if len(collapsed) <= maximum:
|
|
return collapsed
|
|
return collapsed[: maximum - len(ELLIPSIS)].rstrip() + ELLIPSIS
|
|
|
|
|
|
def skill_index_lines(user_uid: str) -> str:
|
|
lines: list[str] = []
|
|
total = 0
|
|
for name, description in enabled_skill_summaries(user_uid)[: limits.PROMPT_MAX_SKILLS]:
|
|
line = f"- {name}: {_one_line(description, limits.PROMPT_DESCRIPTION_MAX_CHARS)}"
|
|
if total + len(line) + 1 > limits.PROMPT_MAX_CHARS:
|
|
break
|
|
lines.append(line)
|
|
total += len(line) + 1
|
|
return "\n".join(lines)
|