Agent skill

Welcome

by Q00 in Q00/ouroboros

First-touch experience for new Ouroboros users. An agent skill from Q00/ouroboros.

MITAuto-check passedAgent Workflows

Install Welcome

skills CLI
$ npx skills add Q00/ouroboros --skill welcome -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install Q00/ouroboros welcome --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/Q00/ouroboros.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/welcome .claude/skills/welcome && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
welcome
GitHub stars
6.2k
Token cost
~9.8k tokens
SKILL.md length
1,089 words
Files
1
Skills in repo
23
Repo updated
First seen
Licence
MIT

At a glance

First-touch experience for new Ouroboros users. An agent skill from Q00/ouroboros.

  • Works in 6 steps: Welcome Banner → Persona Detection → Advanced Runtime Check → …
  • Agent Workflows work in your project
  • SKILL.md covers Usage, Instructions, Prefs File Structure and RFC #1392 State Breadcrumb…
  • Calls python3, python and gh

What it does

Welcome is an agent skill from Q00/ouroboros. First-touch experience for new Ouroboros users

Its SKILL.md is about 9.8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Agent Workflows. It works with Python. The repository describes itself as: Agent OS: the agent gets smarter on its own. We just hold the line: Interview-gated, staged evaluation, budgeted evolution loop. MCP server, 14 runtimes: Claude Code, Codex CLI… The licence is MIT.

When your agent uses it

  • Agent Workflows work in your project

Example prompts

  • “/welcome”

Requirements

  • Python 3

Workflow steps

6 steps, taken from the step headings in SKILL.md.

  1. Welcome Banner
  2. Persona Detection
  3. Advanced Runtime Check
  4. Quick Reference
  5. First Action
  6. GitHub Star (Last Step)

What it can do on your machine

Read from SKILL.md and the folder at commit f587795. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • python3
    • python
    • gh
    • uvx
    • uv
    • just

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use gh, uvx and uv, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Welcome loads about 9.8k tokens when it runs. Until then it costs about 14 tokens; SKILL.md has 1,089 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~14
When it runs · the whole SKILL.md, loaded when a task matches
~9.8k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from Q00/ouroboros at commit f587795, republished under its MIT licence (© Q00). 1,089 words, ~9,758 tokens.

Download SKILL.mdSave it as .claude/skills/welcome/SKILL.md (or your agent's skills folder).
name
welcome
description
First-touch experience for new Ouroboros users

/ouroboros:welcome

Interactive onboarding for new Ouroboros users.

Usage

/ouroboros:welcome              # First-time or update onboarding
/ouroboros:welcome --skip       # Skip welcome, mark as shown
/ouroboros:welcome --force      # Force re-run welcome even if shown

Instructions

When this skill is invoked, follow this flow:

Python Runtime (Required)

Before running any shell snippet below, define this resolver in the same shell. It accepts only Python 3.12 or newer, prefers python3 and then python, and uses uv as the final fallback. Call ouroboros_python directly and quote every argument passed to it; the function preserves arguments and heredoc/stdin input. Only the probe and child interpreter discard inherited CPython path-selection overrides; the caller shell keeps its environment unchanged.

<!-- ouroboros-python-resolver:start -->
bash
ouroboros_python() {
  if command -v python3 >/dev/null 2>&1 &&
    (unset PYTHONHOME PYTHONPATH PYTHONPLATLIBDIR PYTHONEXECUTABLE __PYVENV_LAUNCHER__; command python3 -c 'import sys; raise SystemExit(sys.version_info < (3, 12))') >/dev/null 2>&1
  then
    (unset PYTHONHOME PYTHONPATH PYTHONPLATLIBDIR PYTHONEXECUTABLE __PYVENV_LAUNCHER__; command python3 "$@")
    return
  fi
  if command -v python >/dev/null 2>&1 &&
    (unset PYTHONHOME PYTHONPATH PYTHONPLATLIBDIR PYTHONEXECUTABLE __PYVENV_LAUNCHER__; command python -c 'import sys; raise SystemExit(sys.version_info < (3, 12))') >/dev/null 2>&1
  then
    (unset PYTHONHOME PYTHONPATH PYTHONPLATLIBDIR PYTHONEXECUTABLE __PYVENV_LAUNCHER__; command python "$@")
    return
  fi
  if command -v uv >/dev/null 2>&1; then
    (unset PYTHONHOME PYTHONPATH PYTHONPLATLIBDIR PYTHONEXECUTABLE __PYVENV_LAUNCHER__; command uv run --no-project --quiet --python '>=3.12' python "$@")
    return
  fi
  printf '%s\n' 'Ouroboros skills require Python >= 3.12 or uv on PATH.' >&2
  return 127
}
<!-- ouroboros-python-resolver:end -->

Pre-Check: Already Completed?

First, check ~/.ouroboros/prefs.json for welcomeCompleted. For upgrades from older releases, also treat legacy welcomeShown: true as completed so the welcome prompt does not reappear forever:

bash
PREFFILE="$HOME/.ouroboros/prefs.json"

if [ -f "$PREFFILE" ]; then
  WELCOME_COMPLETED=$(ouroboros_python - <<'PY'
import json, os
path = os.path.expanduser('~/.ouroboros/prefs.json')
try:
    prefs = json.load(open(path, encoding='utf-8'))
except Exception:
    prefs = {}
if not isinstance(prefs, dict):
    prefs = {}
print(prefs.get('welcomeCompleted') or ('legacy-welcomeShown' if prefs.get('welcomeShown') else ''))
PY
)
  WELCOME_VERSION=$(ouroboros_python - <<'PY'
import json, os
path = os.path.expanduser('~/.ouroboros/prefs.json')
try:
    prefs = json.load(open(path, encoding='utf-8'))
except Exception:
    prefs = {}
if not isinstance(prefs, dict):
    prefs = {}
print(prefs.get('welcomeVersion') or '')
PY
)

  if [ -n "$WELCOME_COMPLETED" ] && [ "$WELCOME_COMPLETED" != "null" ]; then
    ALREADY_COMPLETED="true"
  fi
fi

Before honoring that completion marker, determine whether setup is ready for the active runtime. A previously completed welcome must never hide the setup gate from a user who chose 나중에 or whose setup was later removed.

First accept a completed Claude Code setup:

bash
if ouroboros_python - "$HOME/.ouroboros/config.yaml" <<'PY'
from __future__ import annotations

import sys
from pathlib import Path

try:
    import yaml
except ModuleNotFoundError:
    yaml = None

config_path = Path(sys.argv[1])

def yaml_mapping(source: str) -> dict[str, dict[str, str]]:
    """Read the top-level mapping scalars this readiness gate owns."""
    if yaml is not None:
        loaded = yaml.safe_load(source) or {}
        return loaded if isinstance(loaded, dict) else {}

    parsed: dict[str, dict[str, str]] = {}
    section: str | None = None

    def scalar_value(raw: str) -> str:
        return raw.strip().split(" #", 1)[0].strip().rstrip(",}").strip().strip("'\"")

    def flow_mapping(raw: str) -> dict[str, str]:
        value = raw.strip().split(" #", 1)[0].strip()
        if not (value.startswith("{") and value.endswith("}")):
            return {}
        fields: dict[str, str] = {}
        for part in value[1:-1].split(","):
            key, separator, field_value = part.partition(":")
            if separator:
                fields[key.strip().strip("'\"")] = scalar_value(field_value)
        return fields

    for raw_line in source.splitlines():
        if not raw_line.strip() or raw_line.lstrip().startswith("#"):
            continue
        indent = len(raw_line) - len(raw_line.lstrip())
        key, separator, raw_value = raw_line.strip().partition(":")
        if not separator:
            continue
        if indent == 0:
            section = key.strip("'\"")
            parsed[section] = flow_mapping(raw_value)
        elif section is not None:
            parsed.setdefault(section, {})[key.strip("'\"")] = scalar_value(raw_value)
    return parsed

try:
    config = yaml_mapping(config_path.read_text(encoding="utf-8"))
except (OSError, ValueError):
    raise SystemExit(1)

orchestrator = config.get("orchestrator") if isinstance(config, dict) else None
llm = config.get("llm") if isinstance(config, dict) else None
# Existing YAML form: runtime_backend: claude. Parsing avoids assuming its order.
# The marketplace plugin owns its MCP capability. Host-owned
# ~/.claude/mcp.json is intentionally not part of SDK setup readiness.
ready = (
    isinstance(orchestrator, dict)
    and orchestrator.get("runtime_backend") in {"claude", "claude_mcp"}
    and isinstance(llm, dict)
    and llm.get("backend") == "claude"
)
raise SystemExit(0 if ready else 1)
PY
then
  SETUP_READY="true"
fi

If SETUP_READY is not true, determine whether the Codex setup is ready:

bash
CODEX_HOME_DIR="${CODEX_HOME:-$HOME/.codex}"
case "$CODEX_HOME_DIR" in
  "~") CODEX_HOME_DIR="$HOME" ;;
  "~/"*) CODEX_HOME_DIR="$HOME/${CODEX_HOME_DIR#"~/"}" ;;
esac
if ouroboros_python - "$HOME/.ouroboros/config.yaml" "$CODEX_HOME_DIR/config.toml" <<'PY'
from __future__ import annotations

import re
import os
import shutil
import sys
from pathlib import Path

try:
    import tomllib
except ModuleNotFoundError:  # Python 3.10 and earlier hosts
    tomllib = None

try:
    import yaml
except ModuleNotFoundError:
    yaml = None

config_path, codex_config_path = map(Path, sys.argv[1:])

def yaml_mapping(source: str) -> dict[str, dict[str, str]]:
    """Read only the top-level mapping scalars owned by this readiness gate."""
    if yaml is not None:
        loaded = yaml.safe_load(source) or {}
        return loaded if isinstance(loaded, dict) else {}

    parsed: dict[str, dict[str, str]] = {}
    section: str | None = None

    def scalar_value(raw: str) -> str:
        return raw.strip().split(" #", 1)[0].strip().rstrip(",}").strip().strip("'\"")

    def flow_mapping(raw: str) -> dict[str, str]:
        value = raw.strip().split(" #", 1)[0].strip()
        if not (value.startswith("{") and value.endswith("}")):
            return {}
        fields: dict[str, str] = {}
        for part in value[1:-1].split(","):
            key, separator, field_value = part.partition(":")
            if separator:
                fields[key.strip().strip("'\"")] = scalar_value(field_value)
        return fields

    for raw_line in source.splitlines():
        if not raw_line.strip() or raw_line.lstrip().startswith("#"):
            continue
        indent = len(raw_line) - len(raw_line.lstrip())
        key, separator, raw_value = raw_line.strip().partition(":")
        if not separator:
            continue
        if indent == 0:
            section = key.strip("'\"")
            parsed[section] = flow_mapping(raw_value)
        elif section is not None:
            parsed[section][key.strip("'\"")] = scalar_value(raw_value)
    return parsed


def toml_mcp_servers(source: str) -> dict[str, dict[str, object]]:
    """Read MCP server table membership when the host lacks ``tomllib``."""
    servers: dict[str, dict[str, object]] = {}
    table: list[str] = []

    def scalar_value(raw: str) -> str:
        value = raw.strip().split(" #", 1)[0].strip().rstrip(",}").strip()
        return value.strip("'\"").strip()

    def inline_value(raw: str, key: str) -> str | None:
        match = re.search(rf"\b{re.escape(key)}\s*=\s*(\"[^\"]*\"|'[^']*'|[^,}}]+)", raw)
        if match is None:
            return None
        return scalar_value(match.group(1))

    for raw_line in source.splitlines():
        line = raw_line.strip()
        if not line or line.startswith("#"):
            continue
        if line.startswith("[") and line.endswith("]"):
            table = [part.strip().strip("'\"") for part in line[1:-1].split(".")]
            if len(table) >= 2 and table[0] == "mcp_servers":
                servers.setdefault(table[1], {})
            continue
        if table == ["mcp_servers"] and "=" in line:
            key, raw_value = line.split("=", 1)
            server = servers.setdefault(key.strip().strip("'\""), {})
            for field in ("command", "url"):
                value = inline_value(raw_value, field)
                if value is not None:
                    server[field] = value
            continue
        if len(table) >= 2 and table[0] == "mcp_servers" and "=" in line:
            key, raw_value = line.split("=", 1)
            key = key.strip().strip("'\"")
            if key in {"command", "url"}:
                servers.setdefault(table[1], {})[key] = scalar_value(raw_value)
    return servers


def executable_candidate(candidate: str) -> bool:
    """Return whether a CLI candidate points to something runnable."""
    value = candidate.strip()
    if not value:
        return False
    if "/" not in value:
        return shutil.which(value) is not None
    path = Path(value).expanduser()
    return path.is_file() and os.access(path, os.X_OK)


def codex_cli_ready(candidate: object) -> bool:
    """Return whether Codex runtime would have an executable launch candidate."""
    if not isinstance(candidate, str):
        return executable_candidate("codex")
    value = candidate.strip()
    if not value:
        return executable_candidate("codex")
    return executable_candidate(value)


def mcp_endpoint_ready(entry: object) -> bool:
    """Return whether the configured MCP endpoint can actually launch."""
    if not isinstance(entry, dict):
        return False
    command = entry.get("command")
    if isinstance(command, str) and command.strip():
        return executable_candidate(command)
    url = entry.get("url")
    return isinstance(url, str) and bool(url.strip())

try:
    config = yaml_mapping(config_path.read_text(encoding="utf-8"))
    codex_source = codex_config_path.read_text(encoding="utf-8")
    codex_config = tomllib.loads(codex_source) if tomllib is not None else {
        "mcp_servers": toml_mcp_servers(codex_source)
    }
except (OSError, ValueError):
    raise SystemExit(1)

orchestrator = config.get("orchestrator") if isinstance(config, dict) else None
llm = config.get("llm") if isinstance(config, dict) else None
# Equivalent to [mcp_servers\.ouroboros], including quoted TOML key forms.
mcp_servers = codex_config.get("mcp_servers") if isinstance(codex_config, dict) else None
ouroboros_mcp = mcp_servers.get("ouroboros") if isinstance(mcp_servers, dict) else None
codex_cli_path = os.environ.get("OUROBOROS_CODEX_CLI_PATH")
if not codex_cli_path and isinstance(orchestrator, dict):
    codex_cli_path = orchestrator.get("codex_cli_path")
ready = (
    isinstance(orchestrator, dict)
    and orchestrator.get("runtime_backend") == "codex"
    and isinstance(llm, dict)
    and llm.get("backend") == "codex"
    and codex_cli_ready(codex_cli_path)
    and mcp_endpoint_ready(ouroboros_mcp)
)
raise SystemExit(0 if ready else 1)
PY
then
  CODEX_READY="true"
fi
Legacy Codex Model Migration

Some older Ouroboros configurations saved gpt-5 into all four stage-model fields. That was a historical default, but it is now an explicit pin and would stop Codex App/CLI model changes from taking effect. Do not silently rewrite a possible user pin. Instead, when Codex is ready, detect that exact legacy shape once before honoring the welcome-completed marker:

bash
if ouroboros_python - "$HOME/.ouroboros/config.yaml" "$HOME/.ouroboros/prefs.json" <<'PY'
from __future__ import annotations

import json
import sys
from pathlib import Path

config_path, prefs_path = map(Path, sys.argv[1:])

def yaml_mapping(source: str) -> dict[str, dict[str, str]]:
    parsed: dict[str, dict[str, str]] = {}
    section: str | None = None
    for raw_line in source.splitlines():
        if not raw_line.strip() or raw_line.lstrip().startswith("#"):
            continue
        indent = len(raw_line) - len(raw_line.lstrip())
        key, separator, raw_value = raw_line.strip().partition(":")
        if not separator:
            continue
        value = raw_value.strip().split(" #", 1)[0].strip().strip("'\"")
        if indent == 0:
            section = key.strip("'\"")
            parsed.setdefault(section, {})
        elif section is not None:
            parsed[section][key.strip("'\"")] = value
    return parsed

try:
    config = yaml_mapping(config_path.read_text(encoding="utf-8"))
except OSError:
    raise SystemExit(1)
try:
    prefs = json.loads(prefs_path.read_text(encoding="utf-8"))
except (OSError, ValueError):
    prefs = {}
if not isinstance(prefs, dict):
    prefs = {}

stage_values = (
    config.get("clarification", {}).get("default_model"),
    config.get("execution", {}).get("default_model"),
    config.get("evaluation", {}).get("semantic_model"),
    config.get("resilience", {}).get("reflect_model"),
)
legacy_gpt5 = all(value == "gpt-5" for value in stage_values)
partial_automatic_migration = (
    any(value == "gpt-5" for value in stage_values)
    and any(value == "default" for value in stage_values)
    and all(value in {"gpt-5", "default"} for value in stage_values)
)
handled = prefs.get("codexModelMigration") in {"automatic-v1", "kept-gpt-5-v1"}
raise SystemExit(0 if (legacy_gpt5 or partial_automatic_migration) and not handled else 1)
PY
then
  LEGACY_CODEX_MODEL_MIGRATION_REQUIRED="true"
fi

If CODEX_READY is true and LEGACY_CODEX_MODEL_MIGRATION_REQUIRED is true:

Use AskUserQuestion:

json
{
  "questions": [{
    "question": "현재 설정은 모든 단계에서 gpt-5를 고정해 두고 있어요. Codex에서 선택한 모델을 자동으로 사용하도록 바꿀까요?",
    "header": "모델 설정",
    "options": [
      {
        "label": "Codex 선택으로 전환하기 (권장)",
        "description": "App이나 CLI에서 바꾼 모델을 모든 단계가 자동으로 따라가요"
      },
      {
        "label": "gpt-5 고정 유지하기",
        "description": "지금처럼 모든 단계를 gpt-5로 계속 실행해요"
      }
    ],
    "multiSelect": false
  }]
}
  • Codex 선택으로 전환하기: atomically rewrite the four legacy model pins on the current host:
bash
ouroboros_python - "$HOME/.ouroboros/config.yaml" <<'PY'
from __future__ import annotations

import os
import sys
import tempfile
from pathlib import Path

path = Path(sys.argv[1])
original = path.read_text(encoding="utf-8")
replacements = {
    ("clarification", "default_model"): "default",
    ("execution", "default_model"): "default",
    ("evaluation", "semantic_model"): "default",
    ("resilience", "reflect_model"): "default",
}
seen: set[tuple[str, str]] = set()
section: str | None = None
output: list[str] = []
for line in original.splitlines(keepends=True):
    stripped = line.strip()
    indent = len(line) - len(line.lstrip())
    key = stripped.split(":", 1)[0].strip("'\"") if ":" in stripped else ""
    if indent == 0 and ":" in stripped:
        section = key
    target = (section or "", key)
    if indent > 0 and target in replacements:
        newline = "\n" if line.endswith("\n") else ""
        prefix = line[:indent]
        comment = ""
        value_part = line.strip().split(":", 1)[1]
        if " #" in value_part:
            comment = " #" + value_part.split(" #", 1)[1].rstrip("\n")
        output.append(f"{prefix}{key}: {replacements[target]}{comment}{newline}")
        seen.add(target)
    else:
        output.append(line)
missing = set(replacements) - seen
if missing:
    raise SystemExit(f"Cannot migrate Codex model pins; missing keys: {sorted(missing)}")
updated = "".join(output)
fd, tmp_name = tempfile.mkstemp(prefix=".config.yaml.", dir=str(path.parent))
try:
    with os.fdopen(fd, "w", encoding="utf-8") as tmp:
        tmp.write(updated)
        tmp.flush()
        os.fsync(tmp.fileno())
    os.replace(tmp_name, path)
finally:
    try:
        os.unlink(tmp_name)
    except FileNotFoundError:
        pass
PY

default deliberately sends no model pin to Codex; it does not name a model called "default". Confirm that the rewrite succeeded before recording the decision. If this step is interrupted before the marker is written, the next readiness check recognizes the partial gpt-5/default state and offers the migration again.

  • gpt-5 고정 유지하기: do not change config.yaml.

For either completed choice, merge exactly one marker into ~/.ouroboros/prefs.json without deleting existing keys:

bash
ouroboros_python - "automatic-v1" <<'PY'
import json, os, sys
path = os.path.expanduser('~/.ouroboros/prefs.json')
try:
    prefs = json.load(open(path, encoding='utf-8'))
except Exception:
    prefs = {}
if not isinstance(prefs, dict):
    prefs = {}
prefs['codexModelMigration'] = sys.argv[1]
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'w', encoding='utf-8') as f:
    json.dump(prefs, f, indent=2)
    f.write('\n')
PY

Pass kept-gpt-5-v1 instead of automatic-v1 for the keep choice. If welcome was already completed, show a short confirmation and exit after recording this decision; do not make the user answer the generic welcome question too.

If ALREADY_COMPLETED is true, SETUP_READY or CODEX_READY is true, AND no --force flag:

Use AskUserQuestion:

json
{
  "questions": [{
    "question": "Ouroboros welcome was already completed on $WELCOME_COMPLETED. What would you like to do?",
    "header": "Welcome",
    "options": [
      { "label": "Skip", "description": "Continue to work (recommended)" },
      { "label": "Re-run welcome", "description": "Go through the interactive onboarding again" }
    ],
    "multiSelect": false
  }]
}
  • Skip: Mark as complete and exit
  • Re-run welcome: Continue to Step 1 below

If the welcome was completed but neither SETUP_READY nor CODEX_READY is true, bypass this completion prompt and continue to the Setup Gate below.

If --skip flag present:

  • Merge welcomeShown: true, welcomeCompleted: <current timestamp>, and welcomeVersion into ~/.ouroboros/prefs.json without deleting existing keys:
    bash

ouroboros_python - <<'PY' import json, os from datetime import UTC, datetime path = os.path.expanduser('~/.ouroboros/prefs.json') os.makedirs(os.path.dirname(path), exist_ok=True) try: with open(path, encoding='utf-8') as f: prefs = json.load(f) if not isinstance(prefs, dict): prefs = {} except Exception: prefs = {} prefs.update({ 'welcomeShown': True, 'welcomeCompleted': datetime.now(UTC).isoformat(), 'welcomeVersion': '0.50.5', }) with open(path, 'w', encoding='utf-8') as f: json.dump(prefs, f, indent=2) f.write('\n') PY

- Show brief message:

Ouroboros welcome skipped. Run /ouroboros:welcome --force to re-run onboarding.

- Exit

---

### Setup Gate: First Use

Before showing the welcome banner, check whether setup is prepared for the
active runtime on this machine. A global `config.yaml` alone is not enough: it
must name a runtime this gate recognizes.

First accept a completed **Claude Code** setup. The marketplace plugin owns
its MCP capability; host-owned `~/.claude/mcp.json` is intentionally not part
of SDK setup readiness:

```bash
if ouroboros_python - "$HOME/.ouroboros/config.yaml" <<'PY'
from __future__ import annotations

import sys
from pathlib import Path

try:
  import yaml
except ModuleNotFoundError:
  yaml = None

config_path = Path(sys.argv[1])

def yaml_mapping(source: str) -> dict[str, dict[str, str]]:
  """Read only the top-level mapping scalars owned by this readiness gate."""
  if yaml is not None:
      loaded = yaml.safe_load(source) or {}
      return loaded if isinstance(loaded, dict) else {}

  parsed: dict[str, dict[str, str]] = {}
  section: str | None = None

  def scalar_value(raw: str) -> str:
      return raw.strip().split(" #", 1)[0].strip().rstrip(",}").strip().strip("'\"")

  def flow_mapping(raw: str) -> dict[str, str]:
      value = raw.strip().split(" #", 1)[0].strip()
      if not (value.startswith("{") and value.endswith("}")):
          return {}
      fields: dict[str, str] = {}
      for part in value[1:-1].split(","):
          key, separator, field_value = part.partition(":")
          if separator:
              fields[key.strip().strip("'\"")] = scalar_value(field_value)
      return fields

  for raw_line in source.splitlines():
      if not raw_line.strip() or raw_line.lstrip().startswith("#"):
          continue
      indent = len(raw_line) - len(raw_line.lstrip())
      key, separator, raw_value = raw_line.strip().partition(":")
      if not separator:
          continue
      if indent == 0:
          section = key.strip("'\"")
          parsed[section] = flow_mapping(raw_value)
      elif section is not None:
          parsed.setdefault(section, {})[key.strip("'\"")] = scalar_value(raw_value)
  return parsed

try:
  config = yaml_mapping(config_path.read_text(encoding="utf-8"))
except (OSError, ValueError):
  raise SystemExit(1)

orchestrator = config.get("orchestrator") if isinstance(config, dict) else None
llm = config.get("llm") if isinstance(config, dict) else None
# Existing YAML form: runtime_backend: claude. Parsing avoids assuming its order.
# The marketplace plugin owns its MCP capability. Host-owned
# ~/.claude/mcp.json is intentionally not part of SDK setup readiness.
ready = (
  isinstance(orchestrator, dict)
  and orchestrator.get("runtime_backend") in {"claude", "claude_mcp"}
  and isinstance(llm, dict)
  and llm.get("backend") == "claude"
)
raise SystemExit(0 if ready else 1)
PY
then
echo "SETUP_READY"
else
echo "SETUP_REQUIRED"
fi

If the Claude gate printed SETUP_READY, setup is complete: skip the Codex gate below and continue directly to the welcome banner.

Otherwise check whether Codex is prepared:

bash
CODEX_HOME_DIR="${CODEX_HOME:-$HOME/.codex}"
case "$CODEX_HOME_DIR" in
  "~") CODEX_HOME_DIR="$HOME" ;;
  "~/"*) CODEX_HOME_DIR="$HOME/${CODEX_HOME_DIR#"~/"}" ;;
esac
if ouroboros_python - "$HOME/.ouroboros/config.yaml" "$CODEX_HOME_DIR/config.toml" <<'PY'
from __future__ import annotations

import re
import os
import shutil
import sys
from pathlib import Path

try:
    import tomllib
except ModuleNotFoundError:  # Python 3.10 and earlier hosts
    tomllib = None

try:
    import yaml
except ModuleNotFoundError:
    yaml = None

config_path, codex_config_path = map(Path, sys.argv[1:])

def yaml_mapping(source: str) -> dict[str, dict[str, str]]:
    """Read only the top-level mapping scalars owned by this readiness gate."""
    if yaml is not None:
        loaded = yaml.safe_load(source) or {}
        return loaded if isinstance(loaded, dict) else {}

    parsed: dict[str, dict[str, str]] = {}
    section: str | None = None

    def scalar_value(raw: str) -> str:
        return raw.strip().split(" #", 1)[0].strip().rstrip(",}").strip().strip("'\"")

    def flow_mapping(raw: str) -> dict[str, str]:
        value = raw.strip().split(" #", 1)[0].strip()
        if not (value.startswith("{") and value.endswith("}")):
            return {}
        fields: dict[str, str] = {}
        for part in value[1:-1].split(","):
            key, separator, field_value = part.partition(":")
            if separator:
                fields[key.strip().strip("'\"")] = scalar_value(field_value)
        return fields

    for raw_line in source.splitlines():
        if not raw_line.strip() or raw_line.lstrip().startswith("#"):
            continue
        indent = len(raw_line) - len(raw_line.lstrip())
        key, separator, raw_value = raw_line.strip().partition(":")
        if not separator:
            continue
        if indent == 0:
            section = key.strip("'\"")
            parsed[section] = flow_mapping(raw_value)
        elif section is not None:
            parsed.setdefault(section, {})[key.strip("'\"")] = scalar_value(raw_value)
    return parsed


def toml_mcp_servers(source: str) -> dict[str, dict[str, object]]:
    """Read MCP table membership when the host lacks the TOML standard library."""
    servers: dict[str, dict[str, object]] = {}
    table: list[str] = []

    def scalar_value(raw: str) -> str:
        value = raw.strip().split(" #", 1)[0].strip().rstrip(",}").strip()
        return value.strip("'\"").strip()

    def inline_value(raw: str, key: str) -> str | None:
        match = re.search(rf"\b{re.escape(key)}\s*=\s*(\"[^\"]*\"|'[^']*'|[^,}}]+)", raw)
        if match is None:
            return None
        return scalar_value(match.group(1))

    for raw_line in source.splitlines():
        line = raw_line.strip()
        if not line or line.startswith("#"):
            continue
        if line.startswith("[") and line.endswith("]"):
            table = [part.strip().strip("'\"") for part in line[1:-1].split(".")]
            if len(table) >= 2 and table[0] == "mcp_servers":
                servers.setdefault(table[1], {})
            continue
        if table == ["mcp_servers"] and "=" in line:
            key, raw_value = line.split("=", 1)
            server = servers.setdefault(key.strip().strip("'\""), {})
            for field in ("command", "url"):
                value = inline_value(raw_value, field)
                if value is not None:
                    server[field] = value
            continue
        if len(table) >= 2 and table[0] == "mcp_servers" and "=" in line:
            key, raw_value = line.split("=", 1)
            key = key.strip().strip("'\"")
            if key in {"command", "url"}:
                servers.setdefault(table[1], {})[key] = scalar_value(raw_value)
    return servers


def executable_candidate(candidate: str) -> bool:
    """Return whether a CLI candidate points to something runnable."""
    value = candidate.strip()
    if not value:
        return False
    if "/" not in value:
        return shutil.which(value) is not None
    path = Path(value).expanduser()
    return path.is_file() and os.access(path, os.X_OK)


def codex_cli_ready(candidate: object) -> bool:
    """Return whether Codex runtime would have an executable launch candidate."""
    if not isinstance(candidate, str):
        return executable_candidate("codex")
    value = candidate.strip()
    if not value:
        return executable_candidate("codex")
    return executable_candidate(value)


def mcp_endpoint_ready(entry: object) -> bool:
    """Return whether the configured MCP endpoint can actually launch."""
    if not isinstance(entry, dict):
        return False
    command = entry.get("command")
    if isinstance(command, str) and command.strip():
        return executable_candidate(command)
    url = entry.get("url")
    return isinstance(url, str) and bool(url.strip())

try:
    config = yaml_mapping(config_path.read_text(encoding="utf-8"))
    codex_source = codex_config_path.read_text(encoding="utf-8")
    codex_config = tomllib.loads(codex_source) if tomllib is not None else {
        "mcp_servers": toml_mcp_servers(codex_source)
    }
except (OSError, ValueError):
    raise SystemExit(1)

orchestrator = config.get("orchestrator") if isinstance(config, dict) else None
llm = config.get("llm") if isinstance(config, dict) else None
# This is equivalent to checking [mcp_servers\.ouroboros], but TOML parsing
# also accepts a quoted "ouroboros" key and does not depend on table ordering.
mcp_servers = codex_config.get("mcp_servers") if isinstance(codex_config, dict) else None
ouroboros_mcp = mcp_servers.get("ouroboros") if isinstance(mcp_servers, dict) else None
codex_cli_path = os.environ.get("OUROBOROS_CODEX_CLI_PATH")
if not codex_cli_path and isinstance(orchestrator, dict):
    codex_cli_path = orchestrator.get("codex_cli_path")
ready = (
    isinstance(orchestrator, dict)
    and orchestrator.get("runtime_backend") == "codex"
    and isinstance(llm, dict)
    and llm.get("backend") == "codex"
    and codex_cli_ready(codex_cli_path)
    and mcp_endpoint_ready(ouroboros_mcp)
)
raise SystemExit(0 if ready else 1)
PY
then
  echo "CODEX_READY"
else
  echo "CODEX_SETUP_REQUIRED"
fi

If neither gate reports ready (the Claude gate printed SETUP_REQUIRED and the Codex gate printed CODEX_SETUP_REQUIRED), ask one concise question in the user's language. This includes a user whose existing Ouroboros configuration names a runtime neither gate recognizes. For a Korean conversation, use:

json
{
  "questions": [{
    "question": "Ouroboros를 처음 사용하시네요. 시작하기 전에 실행 환경을 설정할까요?",
    "header": "Ouroboros 시작하기",
    "options": [
      {
        "label": "설정하고 시작하기 (권장)",
        "description": "한 번만 설정하면 바로 사용할 수 있어요"
      },
      {
        "label": "나중에",
        "description": "지금은 기본 안내만 보고 나중에 설정할게요"
      }
    ],
    "multiSelect": false
  }]
}
  • 설정하고 시작하기: Run the setup command for the active host. In Codex App or Codex CLI, use ouroboros setup --runtime codex when the executable is installed. For a Marketplace-plugin-only install, use uvx --isolated --python '>=3.12' --from 'ouroboros-ai[mcp]' ouroboros setup --runtime codex instead. In Claude Code, follow ../setup/SKILL.md. Do not ask the user to copy a command when the current host can run it.
  • 나중에: Continue with the welcome flow, but do not claim that MCP-only execution features are ready.

After successful Codex setup, immediately ask:

json
{
  "questions": [{
    "question": "설정이 완료됐어요. 기본적으로 Codex에서 선택한 모델을 사용합니다. 모델은 언제든 나중에 바꿀 수 있어요.",
    "header": "준비 완료",
    "options": [
      {
        "label": "바로 시작하기 (권장)",
        "description": "기본 모델로 바로 작업을 시작해요"
      },
      {
        "label": "직접 모델 설정하기",
        "description": "단계별로 모델을 바꾸거나 목록에 없는 모델 ID를 입력해 고정해요"
      }
    ],
    "multiSelect": false
  }]
}
  • 바로 시작하기: Continue to Step 1.
  • 직접 모델 설정하기: Read and follow ../config/SKILL.md. On the user's local Codex App or Codex CLI this opens the settings UI in their browser at a temporary localhost address; it is not an external website. The UI offers Use Codex default model for the current Codex selection and Enter another model ID… for a deliberate stage pin. After the settings session ends, continue to Step 1.

For Claude Code, ../setup/SKILL.md presents the equivalent model choice during its own completion flow. Do not show this Codex-specific question a second time; continue to Step 1 after the Claude setup skill returns.

Do not show this gate again once Codex is ready. The normal settings UI remains available later through ooo config, so a model choice made now is never permanent.


Show full SKILL.md (341 more words)Show less
Step 1: Welcome Banner

Display:

Welcome to Ouroboros!

The serpent that eats itself -- better every loop.

Most AI coding fails at the input, not the output.
Ouroboros fixes this by exposing hidden assumptions
BEFORE any code is written.

Interview -> Seed -> Execute -> Evaluate
    ^                            |
    +---- Evolutionary Loop -----+

Step 2: Persona Detection

AskUserQuestion:

json
{
  "questions": [{
    "question": "What brings you to Ouroboros?",
    "header": "Welcome",
    "options": [
      {
        "label": "New project idea",
        "description": "I have a vague idea and want to crystallize it into a clear spec"
      },
      {
        "label": "Tired of rewriting prompts",
        "description": "AI keeps building the wrong thing because my requirements are unclear"
      },
      {
        "label": "Just exploring",
        "description": "Heard about Ouroboros and want to see what it does"
      }
    ],
    "multiSelect": false
  }]
}

Give brief personalized response (1-2 sentences) based on choice.


Step 3: Advanced Runtime Check

Ordinary Claude setup uses the default [claude] Agent SDK profile on MCP 1.x. It intentionally leaves host-owned ~/.claude/mcp.json untouched; do not inspect or mutate that file as an onboarding health check. The dependency-free worker is the explicit [claude-cli] profile used by an isolated MCP 2 process.

If the active runtime does not expose Ouroboros MCP tools, AskUserQuestion:

json
{
  "questions": [{
    "question": "Advanced MCP workflows require a host-managed MCP 2 launcher. What would you like to do?",
    "header": "Runtime",
    "options": [
      { "label": "Continue native (Recommended)", "description": "Use Claude-native interview, seed, evaluate, and unstuck workflows" },
      { "label": "Show MCP setup", "description": "See supported Codex, OpenCode, Kiro, Copilot, or Hermes setup commands" }
    ],
    "multiSelect": false
  }]
}
  • Continue native: Continue to Step 4
  • Show MCP setup: Explain that the Claude marketplace plugin or another supported host setup owns the isolated [mcp] launcher. Never combine [claude-sdk] with [mcp]. Then continue to Step 4.

Step 4: Quick Reference
Available Commands:
+---------------------------------------------------+
| Command         | What It Does                     |
|-----------------|----------------------------------|
| ooo interview   | Socratic Q&A -- expose hidden    |
|                 | assumptions in your requirements |
| ooo seed        | Crystallize answers into spec    |
| ooo run         | Execute with visual TUI          |
| ooo evaluate    | 3-stage verification             |
| ooo unstuck     | Lateral thinking when stuck      |
| ooo config      | Settings GUI: agents & models    |
| ooo help        | Full command reference           |
+---------------------------------------------------+

Step 4: First Action

AskUserQuestion:

json
{
  "questions": [{
    "question": "What would you like to do first?",
    "header": "Get started",
    "options": [
      { "label": "Start a project", "description": "Run a Socratic interview on your idea right now" },
      { "label": "Try the tutorial", "description": "Interactive hands-on learning with a sample project" },
      { "label": "Read the docs", "description": "Full command reference and architecture overview" }
    ],
    "multiSelect": false
  }]
}

Based on choice:

  • Start a project: Ask "What do you want to build?" → execute ../interview/SKILL.md
  • Try the tutorial: Execute ../tutorial/SKILL.md
  • Read the docs: Execute ../help/SKILL.md

Step 5: GitHub Star (Last Step)

Check gh availability first:

bash
gh auth status &>/dev/null && echo "GH_OK" || echo "GH_MISSING"

If GH_OK AND star_asked not true:

AskUserQuestion:

json
{
  "questions": [{
    "question": "If you're enjoying Ouroboros, would you like to star it on GitHub?",
    "header": "Community",
    "options": [
      { "label": "Star on GitHub", "description": "Takes 1 second -- helps the project grow" },
      { "label": "Maybe later", "description": "Skip for now" }
    ],
    "multiSelect": false
  }]
}
  • Star on GitHub: gh api -X PUT /user/starred/Q00/ouroboros
  • Both choices: merge the welcome completion fields into ~/.ouroboros/prefs.json without deleting existing keys. Set star_asked: true after either star prompt choice so the star prompt is not repeated:
    bash

ouroboros_python - <<'PY' import json, os from datetime import UTC, datetime path = os.path.expanduser('~/.ouroboros/prefs.json') os.makedirs(os.path.dirname(path), exist_ok=True) try: with open(path, encoding='utf-8') as f: prefs = json.load(f) if not isinstance(prefs, dict): prefs = {} except Exception: prefs = {} prefs.update({ 'star_asked': True, 'welcomeShown': True, 'welcomeCompleted': datetime.now(UTC).isoformat(), 'welcomeVersion': '0.50.5', }) with open(path, 'w', encoding='utf-8') as f: json.dump(prefs, f, indent=2) f.write('\n') PY


**If `GH_MISSING` or `star_asked` is true:**
Merge the welcome completion fields into `~/.ouroboros/prefs.json` without deleting existing keys:
```bash
ouroboros_python - <<'PY'
import json, os
from datetime import UTC, datetime
path = os.path.expanduser('~/.ouroboros/prefs.json')
os.makedirs(os.path.dirname(path), exist_ok=True)
try:
  with open(path, encoding='utf-8') as f:
      prefs = json.load(f)
  if not isinstance(prefs, dict):
      prefs = {}
except Exception:
  prefs = {}
prefs.update({
  'welcomeShown': True,
  'welcomeCompleted': datetime.now(UTC).isoformat(),
  'welcomeVersion': '0.50.5',
})
with open(path, 'w', encoding='utf-8') as f:
  json.dump(prefs, f, indent=2)
  f.write('\n')
PY

Completion Message
Ouroboros Setup Complete!

MAGIC KEYWORDS (optional shortcuts):
Just include these naturally in your request:

| Keyword | Effect | Example |
|---------|--------|---------|
| interview | Socratic Q&A | "interview me about my app idea" |
| seed | Crystallize spec | "seed the requirements" |
| evaluate | 3-stage check | "evaluate this implementation" |
| stuck | Lateral thinking | "I'm stuck on the auth flow" |

REAL-TIME MONITORING (TUI):
When running ooo run or ooo evolve, open a separate terminal:
  uvx --python '>=3.12' --from 'ouroboros-ai[tui]' ouroboros tui monitor
Press 1-4 to switch screens (Dashboard, Execution, Logs, Debug).

READY TO BUILD:
- ooo interview "your project idea"
- ooo tutorial  # Interactive learning
- ooo help      # Full reference

Prefs File Structure

~/.ouroboros/prefs.json:

json
{
  "welcomeShown": true,
  "welcomeCompleted": "2025-02-23T15:30:00+09:00",
  "welcomeVersion": "0.50.5",
  "star_asked": true
}

Your final response MUST end with exactly one breadcrumb footer line:

◆ <current state> → next: <recommended action>

Derive <current state> from live session state via ouroboros_session_status when that MCP projection is available; otherwise derive it from this skill's actual outcome. Never use a linear Step N of M footer because Ouroboros is an evolutionary loop. When the next action is genuinely a choice, list 2-3 honest options in the next: clause. The breadcrumb line must be the last line of the response.

© Q00, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in skills/welcome of Q00/ouroboros.

Open the folder on GitHubat commit f587795

Compare with similar skills

Welcome next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Welcome compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Welcome this skillQ00/ouroboros6.2k—~9.8kAutomated safety check: PassMIT
MCP Server Builderanthropics/skills180k64 repos~2.3kAutomated safety check: PassApache-2.0
MCP Server BuildershareAI-lab/learn-claude-code78k5 repos~1.2kAutomated safety check: PassMIT
Fastmcp Client CLIPrefectHQ/fastmcp28k1 repos~823Automated safety check: PassApache-2.0
Mem0 CLI Memory Commandsmem0ai/mem067k—~2kAutomated safety check: NotesApache-2.0
MemPalace Setup and OperationMemPalace/mempalace59k—~2.2kAutomated safety check: PassMIT

Similar skills

  • MCP Server Builder

    anthropics/skills

    Official

    Guides the design and implementation of Model Context Protocol servers in TypeScript or Python, from tool naming and error messages to evaluation.

    180k GitHub starsUsed in 64 repos~2.3k tokens
    Agent WorkflowsAuto-check passed
  • MCP Server Builder

    shareAI-lab/learn-claude-code

    Walks through building MCP servers in Python or TypeScript that expose tools, resources and prompts to Claude, with templates, registration and testing.

    78k GitHub starsUsed in 5 repos~1.2k tokens
    Agent WorkflowsAuto-check passed
  • Fastmcp Client CLI

    PrefectHQ/fastmcp

    Query and invoke tools on MCP servers using fastmcp list and fastmcp call.

    28k GitHub starsUsed in 1 repo~823 tokens
    Agent WorkflowsAuto-check passed
  • Adds, searches, lists, updates and deletes memories on the Mem0 platform from the terminal with the mem0 command, including a JSON mode built for agents.

    67k GitHub stars~2k tokensUpdated yesterday
    Agent WorkflowsAuto-check: notes
  • Installs and configures MemPalace as a private local palace, a shared-brain hub or a client of an existing hub, including MCP registration and version-correct initialization.

    59k GitHub stars~2.2k tokensUpdated today
    Agent WorkflowsAuto-check passed
  • Install and Run Cognee

    topoteretes/cognee

    Installs the cognee AI memory library in a Python environment, sets the LLM key and gets a first remember and recall script running with the Python SDK.

    32k GitHub starsUsed in 1 repo~1k tokens
    Agent WorkflowsAuto-check: notes

More from Q00/ouroboros

All 23 skills in this repo
  • Runs a guided product-manager interview that classifies each question automatically and produces a Product Requirements Document.

    6.2k GitHub starsUsed in 1 repo~5.7k tokens
    Auto-check passed
  • Triages and works through GitHub issues and pull requests in the Q00/ouroboros repo as a maintainer, within a stated review boundary and clear limits on what it may change.

    6.2k GitHub stars~1.7k tokensUpdated yesterday
    Auto-check passed
  • Scans a directory for existing git repositories and worktrees, then registers and manages which ones serve as default context during interviews.

    6.2k GitHub stars~2.2k tokensUpdated yesterday
    Auto-check passed
  • Scores an agent's finished work with a three-stage pipeline: free mechanical checks, an advisory semantic review, and an optional multi-model consensus vote.

    6.2k GitHub stars~2.2k tokensUpdated yesterday
    Auto-check passed
  • Starts, monitors or rewinds an evolutionary development loop that refines an ontology and acceptance criteria generation by generation until it converges, using the Ouroboros MCP tools.

    6.2k GitHub stars~3.2k tokensUpdated yesterday
    Auto-check passed
  • Opens or drives the Ouroboros settings GUI, picking a browser, TUI or chat-based approach depending on whether the user can reach a browser window.

    6.2k GitHub stars~1.2k tokensUpdated yesterday
    Auto-check passed

Works with

Categories

Questions about Welcome

What does Welcome do?

First-touch experience for new Ouroboros users. An agent skill from Q00/ouroboros. Welcome is an agent skill from Q00/ouroboros.

When should I use Welcome?

Welcome fits situations like: agent Workflows work in your project.

How do I install Welcome in Claude Code?

Run `npx skills add Q00/ouroboros --skill welcome -a claude-code`. Or copy the skill folder (skills/welcome in Q00/ouroboros) into .claude/skills/welcome in your project. Claude Code loads it when a task matches its description.

How do I install Welcome in Codex?

Run `npx skills add Q00/ouroboros --skill welcome -a codex`. Or copy the skill folder (skills/welcome in Q00/ouroboros) into .agents/skills/welcome in your project. Codex loads it when a task matches its description.

Can I use Welcome in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Q00/ouroboros --skill welcome -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/welcome, .gemini/skills/welcome, .github/skills/welcome and .opencode/skills/welcome in your project.

What does Welcome need to run?

Going by SKILL.md and its folder, Welcome needs the command-line tools its instructions call (python3, python, gh, uvx, uv and just). Our summary lists: Python 3.

Does Welcome access the network?

SKILL.md contains no URLs. Its commands use gh, uvx and uv, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Welcome safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Welcome use?

Welcome is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Welcome use?

About 9.8k tokens (SKILL.md is roughly 39k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Welcome?

Skills that share tags, products or a category with Welcome: MCP Server Builder (anthropics/skills, 180k stars), MCP Server Builder (shareAI-lab/learn-claude-code, 78k stars), Fastmcp Client CLI (PrefectHQ/fastmcp, 28k stars) and Mem0 CLI Memory Commands (mem0ai/mem0, 67k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Welcome?

Q00 (a GitHub user) maintains it in Q00/ouroboros, which has 6,194 GitHub stars. The repository holds 23 skills in this directory. The repository was last updated on October 7, 2026.

Source: Q00/ouroboros on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.