Files
.pouch/skills/ship/scripts/ship_lib.py
T
laily af6e139d30 feat: rename bugfix-ship/requirement-flow to short-ship/long-ship with shared ship records
short-ship and long-ship share one ship-id record under .pouch/ship/<id>/.
specifier writes PRDs into spec/; delivery-loop appends evidence pointers.
Old names stay as deprecation stubs so existing installs keep working.
2026-10-13 23:11:58 +08:00

75 lines
1.8 KiB
Python

#!/usr/bin/env python3
"""Shared ship-id and YAML helpers. Zero third-party deps."""
from __future__ import annotations
import re
import sys
from pathlib import Path
from typing import Any
_REPO_ROOT = Path(__file__).resolve().parents[3]
if str(_REPO_ROOT) not in sys.path:
sys.path.insert(0, str(_REPO_ROOT))
from pouch.yaml_io import safe_dump, safe_load # noqa: E402
SHIP_ID_RE = re.compile(r"^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")
DATE_ID_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$|^\d{8}$")
KIND = frozenset({"short", "long"})
SHIP_STATUS = frozenset(
{
"draft",
"spec_review",
"approved",
"in_progress",
"blocked",
"done",
"parked",
"superseded",
}
)
TASK_STATUS = frozenset(
{"open", "in_progress", "blocked", "verified", "merged", "done"}
)
DELIVERY_STATE = frozenset(
{
"change_ready",
"verified",
"pr_opened",
"merged",
"regression_passed",
"artifact_built",
"published",
"deployed",
}
)
SOURCE_KIND = frozenset({"user", "hunt", "ack", "pr", "feishu"})
COMMIT_ROLE = frozenset({"baseline", "merged", "head"})
def skill_dir() -> Path:
return Path(__file__).resolve().parents[1]
def load_yaml(path: Path) -> Any:
return safe_load(path.read_text(encoding="utf-8"))
def dump_yaml(path: Path, data: Any) -> None:
path.write_text(safe_dump(data), encoding="utf-8")
def check_ship_id(ship_id: str) -> str | None:
if not ship_id:
return "shipId 为空"
if len(ship_id) > 64:
return f"shipId 超过 64 字符: {ship_id}"
if DATE_ID_RE.fullmatch(ship_id):
return f"shipId 不能是日期: {ship_id}"
if not SHIP_ID_RE.fullmatch(ship_id):
return (
f"shipId 无效: {ship_id!r}(小写字母开头,仅小写字母、数字、连字符)"
)
return None