关于 welcome
welcome 是面向 Ouroboros 新用户的交互式入门技能,负责检查运行环境、引导配置并介绍项目命令。它支持跳过或重新运行入门流程,并记录完成状态和偏好。
主要功能
- 01检查 Claude Code 和 Codex 配置
- 02交互式运行环境设置引导
- 03模型选择与迁移提示
- 04介绍 Ouroboros 命令
- 05记录完成状态和跳过偏好
适用场景
- 01引导首次使用 Ouroboros 的用户
- 02检查运行环境是否就绪
- 03选择 Codex 模型行为
- 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 个选项。