mcpField
W
welcome

welcome

github.com/q00Agent 工作流6.3k73 次安装今天更新
代码仓库

关于 welcome

welcome 是面向 Ouroboros 新用户的交互式入门技能,负责检查运行环境、引导配置并介绍项目命令。它支持跳过或重新运行入门流程,并记录完成状态和偏好。

主要功能

  1. 01检查 Claude Code 和 Codex 配置
  2. 02交互式运行环境设置引导
  3. 03模型选择与迁移提示
  4. 04介绍 Ouroboros 命令
  5. 05记录完成状态和跳过偏好

适用场景

  1. 01引导首次使用 Ouroboros 的用户
  2. 02检查运行环境是否就绪
  3. 03选择 Codex 模型行为
  4. 04重新运行或跳过入门流程

MCP 相关

该技能会检查 Claude Code 或 Codex 的 MCP 配置是否就绪,并在需要时引导完成设置。它说明,使用 MCP 功能的命令需要受支持的宿主配置。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/q00/ouroboros --skill welcome

安装后在 Claude Code 中输入/welcome确认已加载。

详细介绍

/ouroboros:welcome

面向 Ouroboros 新用户的交互式引导。

用法

/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

说明

使用当前运行时的 ask_user 能力提出每个问题,并使用用户的语言。以下韩语标签为规范用语。

Python 运行时(必需)

在执行以下任何代码片段前,请在同一个 shell 中定义此解析器。它只接受 Python 3.12 或更高版本(依次尝试 python3、python,然后是 uv),并保留参数和标准输入。传递给 ouroboros_python 的每个参数都必须加引号。

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
}
预检查:是否已完成?

在一次 shell 调用中运行解析器、本节的代码块和下方的迁移检查,然后使用 printf 'completed=%s setup=%s codex=%s migration=%s\n' "$WELCOME_COMPLETED" "${SETUP_READY:-}" "${CODEX_READY:-}" "${LEGACY_CODEX_MODEL_MIGRATION_REQUIRED:-}" 打印结果。

读取完成标记(旧版 welcomeShown: true 也视为已完成):

WELCOME_COMPLETED=$(ouroboros_python - <<'PY'
import json, os
try:
    prefs = json.load(open(os.path.expanduser('~/.ouroboros/prefs.json'), 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
)

在确认完成标记之前,先判断当前运行时的设置是否就绪。 如果用户选择了 나중에,或之后移除了设置,就绝不能让先前已完成的引导掩盖设置门槛。

当 ~/.ouroboros/config.yaml 中的 orchestrator.runtime_backend 为 {claude, claude_mcp} 之一,且 llm.backend: claude 时,Claude Code 即为就绪状态。Marketplace 插件负责注册其 MCP,因此由宿主管理的 ~/.claude/mcp.json 绝不属于就绪状态的判断条件:

if ouroboros_python - "$HOME/.ouroboros/config.yaml" <<'PY'
import sys
from pathlib import Path

try:
    import yaml
except ModuleNotFoundError:
    yaml = None


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


def load_yaml(path: str) -> dict:
    source = Path(path).read_text(encoding="utf-8")
    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 = None
    for line in source.splitlines():
        if not line.strip() or line.lstrip().startswith("#"):
            continue
        key, separator, value = line.strip().partition(":")
        if not separator:
            continue
        key = key.strip("'\"")
        if not line[0].isspace():
            section = key
            parsed[section] = {}
            flow = value.strip().split(" #", 1)[0].strip()
            if flow.startswith("{") and flow.endswith("}"):
                for part in flow[1:-1].split(","):
                    field, separator, field_value = part.partition(":")
                    if separator:
                        parsed[section][field.strip().strip("'\"")] = scalar(field_value)
        elif section is not None:
            parsed[section][key] = scalar(value)
    return parsed


try:
    config = load_yaml(sys.argv[1])
except (OSError, ValueError):
    raise SystemExit(1)
orchestrator, llm = config.get("orchestrator"), config.get("llm")
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

如果 SETUP_READY 不为 true,则检查 Codex。只有在两个后端都为 codex、Codex CLI(OUROBOROS_CODEX_CLI_PATH,否则使用 orchestrator.codex_cli_path,再否则使用 PATH 中的 codex)可运行,且 $CODEX_HOME/config.toml 中的 mcp_servers\.ouroboros(任意 TOML 键写法) 有可运行的 command 或非空白的 url 时,Codex 才就绪:

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'
import os
import shutil
import sys
import tomllib
from pathlib import Path

try:
    import yaml
except ModuleNotFoundError:
    yaml = None


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


def load_yaml(path: str) -> dict:
    source = Path(path).read_text(encoding="utf-8")
    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 = None
    for line in source.splitlines():
        if not line.strip() or line.lstrip().startswith("#"):
            continue
        key, separator, value = line.strip().partition(":")
        if not separator:
            continue
        key = key.strip("'\"")
        if not line[0].isspace():
            section = key
            parsed[section] = {}
            flow = value.strip().split(" #", 1)[0].strip()
            if flow.startswith("{") and flow.endswith("}"):
                for part in flow[1:-1].split(","):
                    field, separator, field_value = part.partition(":")
                    if separator:
                        parsed[section][field.strip().strip("'\"")] = scalar(field_value)
        elif section is not None:
            parsed[section][key] = scalar(value)
    return parsed


def runnable(candidate: str) -> bool:
    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 mcp_ready(entry: object) -> bool:
    if not isinstance(entry, dict):
        return False
    command, url = entry.get("command"), entry.get("url")
    if isinstance(command, str) and command.strip():
        return runnable(command)
    return isinstance(url, str) and bool(url.strip())


try:
    config = load_yaml(sys.argv[1])
    codex_config = tomllib.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
except (OSError, ValueError):
    raise SystemExit(1)
orchestrator, llm = config.get("orchestrator"), config.get("llm")
servers = codex_config.get("mcp_servers")
cli_path = os.environ.get("OUROBOROS_CODEX_CLI_PATH")
if not cli_path and isinstance(orchestrator, dict):
    cli_path = orchestrator.get("codex_cli_path")
if not isinstance(cli_path, str) or not cli_path.strip():
    cli_path = "codex"
ready = (
    isinstance(orchestrator, dict)
    and orchestrator.get("runtime_backend") == "codex"
    and isinstance(llm, dict)
    and llm.get("backend") == "codex"
    and runnable(cli_path)
    and mcp_ready(servers.get("ouroboros") if isinstance(servers, dict) else None)
)
raise SystemExit(0 if ready else 1)
PY
then
  CODEX_READY="true"
fi
旧版 Codex 模型迁移

旧版配置会在所有四个阶段模型字段中保存 gpt-5。现在这会成为显式固定设置,导致 Codex App/CLI 的模型更改不再生效。绝不要在未告知用户的情况下改写可能由用户设置的固定模型。Codex 就绪时,只检测一次这种确切的旧版配置(或中断的迁移):

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

如果 CODEX_READY 和 LEGACY_CODEX_MODEL_MIGRATION_REQUIRED 都为 true,请询问 “현재 설정은 모든 단계에서 gpt-5를 고정해 두고 있어요. Codex에서 선택한 모델을 자동으로 사용하도록 바꿀까요?” (标题为 모델 설정),并提供以下选项:

  • Codex 선택으로 전환하기 (권장):所有阶段都会自动跟随你在 App 或 CLI 中选择的模型。 以原子方式重写这四个固定设置:
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 表示不向 Codex 发送模型固定设置;它不是名为 “default” 的模型。 确认重写成功后再记录选择。若重写中断,会留下部分 gpt-5/default 状态,下次检查时会再次提供此选项。

  • gpt-5 고정 유지하기:继续像现在一样在所有阶段运行 gpt-5。不要更改 config.yaml。

无论选择哪项,都要将恰好一个 codexModelMigration 标记合并到 ~/.ouroboros/prefs.json,并保留现有键:

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

选择保留固定设置时,传入 kept-gpt-5-v1,而不是 automatic-v1。如果引导已完成,请简短确认后退出;不要再询问通用引导问题。

预检查后的分流
  • 已完成,且 SETUP_READY 或 CODEX_READY 为 true,没有 --force:询问 “Ouroboros welcome was already completed on $WELCOME_COMPLETED. What would you like to do?”,并提供 Skip(继续工作,推荐;退出)和 Re-run welcome(继续执行步骤 1)。
  • 已完成,但两者都未就绪:跳过此提示,进入设置门槛。
  • --skip:写入完成偏好设置(步骤 4),打印 Ouroboros welcome skipped. Run /ouroboros:welcome --force to re-run onboarding., 然后退出。
初次使用:设置门槛

使用预检查输出的就绪状态;仅有全局 config.yaml 并不足够,除非其中指定了这些门槛可识别的运行时。若要在设置后重新检查(或预检查未运行),请使用以下独立门槛,它们会输出判定结果。Claude Code:

if ouroboros_python - "$HOME/.ouroboros/config.yaml" <<'PY'
import sys
from pathlib import Path

try:
    import yaml
except ModuleNotFoundError:
    yaml = None


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


def load_yaml(path: str) -> dict:
    source = Path(path).read_text(encoding="utf-8")
    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 = None
    for line in source.splitlines():
        if not line.strip() or line.lstrip().startswith("#"):
            continue
        key, separator, value = line.strip().partition(":")
        if not separator:
            continue
        key = key.strip("'\"")
        if not line[0].isspace():
            section = key
            parsed[section] = {}
            flow = value.strip().split(" #", 1)[0].strip()
            if flow.startswith("{") and flow.endswith("}"):
                for part in flow[1:-1].split(","):
                    field, separator, field_value = part.partition(":")
                    if separator:
                        parsed[section][field.strip().strip("'\"")] = scalar(field_value)
        elif section is not None:
            parsed[section][key] = scalar(value)
    return parsed


try:
    config = load_yaml(sys.argv[1])
except (OSError, ValueError):
    raise SystemExit(1)
orchestrator, llm = config.get("orchestrator"), config.get("llm")
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

如果输出了 SETUP_READY,请跳过 Codex 门槛并继续步骤 1。 否则检查 Codex:

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'
import os
import shutil
import sys
import tomllib
from pathlib import Path

try:
    import yaml
except ModuleNotFoundError:
    yaml = None


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


def load_yaml(path: str) -> dict:
    source = Path(path).read_text(encoding="utf-8")
    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 = None
    for line in source.splitlines():
        if not line.strip() or line.lstrip().startswith("#"):
            continue
        key, separator, value = line.strip().partition(":")
        if not separator:
            continue
        key = key.strip("'\"")
        if not line[0].isspace():
            section = key
            parsed[section] = {}
            flow = value.strip().split(" #", 1)[0].strip()
            if flow.startswith("{") and flow.endswith("}"):
                for part in flow[1:-1].split(","):
                    field, separator, field_value = part.partition(":")
                    if separator:
                        parsed[section][field.strip().strip("'\"")] = scalar(field_value)
        elif section is not None:
            parsed[section][key] = scalar(value)
    return parsed


def runnable(candidate: str) -> bool:
    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 mcp_ready(entry: object) -> bool:
    if not isinstance(entry, dict):
        return False
    command, url = entry.get("command"), entry.get("url")
    if isinstance(command, str) and command.strip():
        return runnable(command)
    return isinstance(url, str) and bool(url.strip())


try:
    config = load_yaml(sys.argv[1])
    codex_config = tomllib.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
except (OSError, ValueError):
    raise SystemExit(1)
orchestrator, llm = config.get("orchestrator"), config.get("llm")
servers = codex_config.get("mcp_servers")
cli_path = os.environ.get("OUROBOROS_CODEX_CLI_PATH")
if not cli_path and isinstance(orchestrator, dict):
    cli_path = orchestrator.get("codex_cli_path")
if not isinstance(cli_path, str) or not cli_path.strip():
    cli_path = "codex"
ready = (
    isinstance(orchestrator, dict)
    and orchestrator.get("runtime_backend") == "codex"
    and isinstance(llm, dict)
    and llm.get("backend") == "codex"
    and runnable(cli_path)
    and mcp_ready(servers.get("ouroboros") if isinstance(servers, dict) else None)
)
raise SystemExit(0 if ready else 1)
PY
then
  echo "CODEX_READY"
else
  echo "CODEX_SETUP_REQUIRED"
fi

如果两者都未就绪(SETUP_REQUIRED 和 CODEX_SETUP_REQUIRED,包括配置中指定了两个门槛都无法识别的运行时),请询问 「Ouroboros를 처음 사용하시네요. 시작하기 전에 실행 환경을 설정할까요?」 (标题 Ouroboros 시작하기):

  • 설정하고 시작하기 (권장):只需设置一次,即可立即使用。自行针对当前主机运行设置;绝不要要求用户复制主机可以运行的命令。在 Codex App 或 Codex CLI 中,如果已安装可执行文件,请运行 ouroboros setup --runtime codex。如果仅安装了 Marketplace 插件,则改为运行 uvx --isolated --python '>=3.12' --from 'ouroboros-ai[mcp]' ouroboros setup --runtime codex。 在 Claude Code 中,按照 ../setup/SKILL.md 操作。
  • 나중에:现在只看基本说明,以后再设置。继续欢迎流程,但不要声称 MCP 支持的功能已就绪。

Codex 设置成功后,请询问「설정이 완료됐어요. 기본적으로 Codex에서 선택한 모델을 사용합니다. 모델은 언제든 나중에 바꿀 수 있어요.」(标题 준비 완료):

  • 바로 시작하기 (권장):立即使用默认模型开始工作。继续步骤 1。
  • 직접 모델 설정하기:逐步更换模型,或输入列表中没有的模型 ID 并固定使用。阅读并按照 ../config/SKILL.md 操作。在用户本地的 Codex App 或 CLI 中,它会在浏览器中打开设置界面,地址为临时的 localhost 地址(不是外部网站)。界面提供 Use Codex default model 和 Enter another model ID…,用于有意固定某个阶段的模型。设置会话结束后继续步骤 1。

在 Claude Code 中,../setup/SKILL.md 会自行询问相应的模型问题;不要重复询问。运行时就绪后,不要再次显示此门槛;仍可通过 ooo config 使用设置界面。

步骤 1:横幅
Welcome to Ouroboros!

Most AI coding fails at the input, not the output.
Ouroboros exposes hidden assumptions BEFORE any code is written.

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

普通的 Claude 设置会在 MCP 1.x 上使用默认的 [claude] Agent SDK 配置文件。切勿将 ~/.claude/mcp.json 用作健康检查,也不要修改它。如果在此处无法调用 Ouroboros MCP 工具,请说明 MCP 支持的命令需要由受支持的主机进行设置,该主机会管理一个隔离的 [mcp] 启动器;[claude-sdk] 和 [mcp] 绝不会同时安装。

步骤 2:角色定位

询问「是什么让你来到 Ouroboros?」并提供 New project idea(将模糊想法凝练为 Seed)、Tired of rewriting prompts(AI 一直在构建错误的东西)和 Just exploring。用 1-2 句个性化回复。

步骤 3:首次操作

展示核心命令:ooo interview(通过苏格拉底式问答揭示隐藏假设)、ooo seed(将答案固定为 Seed)、ooo run(执行 Seed)、ooo evaluate(根据验收标准进行验证)、ooo unstuck(横向思考)、ooo config(设置)、ooo help(完整参考)。

询问「你想先做什么?」:

  • Start a project:询问「你想构建什么?」,然后继续执行 ooo interview。
  • Try the tutorial:继续执行 ooo tutorial。
  • Read the docs:继续执行 ooo help。

交接前先执行步骤 4,这样即使下一条命令接管对话,也会记录完成状态。

第 4 步:GitHub Star 和完成

运行 gh auth status &>/dev/null && echo GH_OK || echo GH_MISSING。如果结果为 GH_OK,且 ~/.ouroboros/prefs.json 中的 star_asked 不为 true,则询问“如果你喜欢 Ouroboros,愿意在 GitHub 上给它点个 Star 吗?”,并提供 Star on GitHub(运行 gh api -X PUT /user/starred/Q00/ouroboros)和 Maybe later 选项。

然后将完成相关字段合并到 ~/.ouroboros/prefs.json,保留已有键。如果询问了 Star 问题,则在命令前加上 STAR_ASKED=true,以免重复询问:

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(),
})
if os.environ.get('STAR_ASKED') == 'true':
    prefs['star_asked'] = True
with open(path, 'w', encoding='utf-8') as f:
    json.dump(prefs, f, indent=2)
    f.write('\n')
PY

交接前,说明像“采访我”“我卡住了”或“评估一下”这样的短语也会路由到匹配的命令;要实时查看 ooo run 或 ooo evolve,请在单独的终端中运行 uvx --python '>=3.12' --from 'ouroboros-ai[tui]' ouroboros tui monitor。

RFC #1392 状态提示脚注

在最终回复末尾另起一行,且仅使用一行,格式为 ◆ <current state> → next: <recommended action>。如果可用,通过 ouroboros_session_status 获取当前会话的实时状态;否则,根据此技能的实际结果确定状态。不要使用线性的 Step N of M 脚注;如果下一步确实有多个选择,请列出 2-3 个选项。

评论

支持 Markdown