printing-press-import

printing-press-import

热门

将已发布的CLI从公共库导入内部库,使其与全新生成的副本完全一致——模块路径恢复原样,手稿一并放置,准备好用于/printing-press-polish或/printing-press-emboss。当公共库中有你本地没有的CLI,或者需要从损坏/丢失的内部副本中恢复时使用。触发短语:“导入CLI”、“将其导入我的库”、“从公共库获取”、“我本地还没有”。

4028Star
441Fork
更新于 2026/7/16
SKILL.md
readonly只读
name
printing-press-import
description

将已发布的CLI从公共库导入内部库,使其与全新生成的副本完全一致——模块路径恢复原样,手稿一并放置,准备好用于/printing-press-polish或/printing-press-emboss。当公共库中有你本地没有的CLI,或者需要从损坏/丢失的内部副本中恢复时使用。触发短语:“导入CLI”、“将其导入我的库”、“从公共库获取”、“我本地还没有”。

/printing-press-import

将已发布的CLI从公共库(mvanhorn/printing-press-library)导入到位于$PRESS_LIBRARY/的内部库,使其与生成器产生的形式一致。手稿也会一并导入。

/printing-press-import notion
/printing-press-import cal.com
/printing-press-import allrecipes --from-clone ~/Code/printing-press-library

内部库是工作副本;公共库是持久化的制品。导入后,CLI即可用于polish、emboss或重新发布——发布步骤会重新应用模块路径重写。

何时运行

  • 公共库中有你本地没有的CLI
  • 内部副本损坏、丢失或不同步
  • 在对已发布的CLI运行polish之前,想要一个干净的基线

如果用户要求polish某个CLI,并提到“在公共库中”或“从仓库中”,建议先运行此技能。

设置

PRESS_HOME="${PRINTING_PRESS_HOME:-$HOME/printing-press}"
PRESS_LIBRARY="$PRESS_HOME/library"
PRESS_MANUSCRIPTS="$PRESS_HOME/manuscripts"
SCRIPTS_DIR="$(dirname "${BASH_SOURCE[0]:-$0}")/references"

if ! command -v go >/dev/null 2>&1; then
  echo ""
  echo "[setup-error] 未找到Go工具链。"
  echo ""
  echo "此Printing Press流程需要运行基于Go的构建或验证命令。"
  echo "请从 https://go.dev/dl/ 安装Go 1.26.5或更新版本,然后验证:"
  echo "  go version"
  echo "然后重新运行此技能。"
  echo ""
  return 1 2>/dev/null || exit 1
fi

_pp_check_disk_space() {
  _pp_disk_warn_kb="${PRINTING_PRESS_DISK_WARN_KB:-3145728}"
  _pp_disk_fail_kb="${PRINTING_PRESS_DISK_FAIL_KB:-524288}"
  case "$_pp_disk_warn_kb$_pp_disk_fail_kb" in
    ""|*[!0-9]*) return 0 ;;
  esac

  _pp_disk_path="$PRESS_HOME"
  while [ ! -e "$_pp_disk_path" ] && [ "$_pp_disk_path" != "/" ]; do
    _pp_disk_path="$(dirname "$_pp_disk_path")"
  done

  _pp_disk_avail_kb="$(df -Pk "$_pp_disk_path" 2>/dev/null | awk 'NR == 2 { print $4; exit }')"
  case "$_pp_disk_avail_kb" in
    ""|*[!0-9]*) return 0 ;;
  esac

  if [ "$_pp_disk_avail_kb" -lt "$_pp_disk_fail_kb" ]; then
    echo ""
    echo "[setup-error] Printing Press工作区卷的磁盘空间严重不足。"
    echo "PRESS_DISK_PATH=$_pp_disk_path"
    echo "PRESS_DISK_AVAIL_KB=$_pp_disk_avail_kb"
    echo "PRESS_DISK_FAIL_KB=$_pp_disk_fail_kb"
    echo "请释放磁盘空间或将PRINTING_PRESS_HOME设置为空间更大的卷,然后重新运行此技能。"
    echo ""
    return 1
  fi

  if [ "$_pp_disk_avail_kb" -lt "$_pp_disk_warn_kb" ]; then
    echo ""
    echo "[low-disk] Printing Press工作区卷的可用空间较低。"
    echo "PRESS_DISK_PATH=$_pp_disk_path"
    echo "PRESS_DISK_AVAIL_KB=$_pp_disk_avail_kb"
    echo "PRESS_DISK_WARN_KB=$_pp_disk_warn_kb"
    echo "此流程可能需要数GiB空间用于生成的文件、Go构建缓存、模块下载或仓库克隆。"
    echo ""
  fi
}
_pp_check_disk_space || { return 1 2>/dev/null || exit 1; }

四个参考脚本与SKILL.md位于同一目录下的references/中:

  • import-fetch.sh <library-path> <staging> [--clone <path>]
  • import-backup.sh <api-slug>(在标准输出上打印zip路径)
  • import-rewrite.sh <staging> <api-slug>
  • import-place.sh <staging> <api-slug>

如果设置输出了[low-disk],请向用户显示该提示并继续,除非设置也输出了[setup-error][low-disk]表示此次运行可能需要数GiB空间用于仓库克隆、暂存文件、备份、Go构建缓存或模块下载。

阶段1 — 解析CLI

参数可以是任何自然形式:API slug(notion)、品牌名称(cal.com)、旧CLI名称(notion-pp-cli)或近似名称(Allrecipes)。通过公共库的registry.json解析——该文件包含每个条目的namecategoryapidescriptionpath,一次获取即可。

REGISTRY=$(mktemp)
gh api -H "Accept: application/vnd.github.v3.raw" \
  repos/mvanhorn/printing-press-library/contents/registry.json \
  > "$REGISTRY"

按以下顺序匹配:

  1. 精确匹配namejq --arg q "$ARG" '.entries[] | select(.name == $q)' "$REGISTRY"
  2. 标准化精确匹配 — 去除-pp-cli后缀,转小写,点号转连字符,然后精确匹配
  3. 子串匹配namedescription — 不区分大小写的包含
# 精确匹配:
jq --arg q "$ARG" '.entries[] | select(.name == $q)' "$REGISTRY"

# 标准化精确匹配($ARG2为小写、点号转连字符、去除后缀后):
jq --arg q "$ARG2" '.entries[] | select(.name == $q)' "$REGISTRY"

# 模糊匹配(name或description的子串):
jq --arg q "$ARG2" '.entries[]
  | select((.name | ascii_downcase | contains($q | ascii_downcase))
        or (.description | ascii_downcase | contains($q | ascii_downcase)))
' "$REGISTRY"

如果只有一个匹配项:使用它。如果有多个:通过AskUserQuestion向用户展示最多4个候选,显示每个候选的namedescription。如果没有匹配项:告知用户公共库中没有该CLI。

匹配的条目提供了所需的一切:

  • LIB_PATH来自.path(例如library/productivity/cal-com
  • API_SLUG来自.name
  • CATEGORY来自.category

在推理候选时不要读取整个文件。上述字段已足够;如果确实需要更多信息,每个CLI的清单文件位于<LIB_PATH>/manifest.json,其中的描述可以通过相同方式获取(gh api -H "Accept: ... raw" .../manifest.json | jq -r '.description')。

阶段2 — 决定是否覆盖

检查内部库是否已有此CLI:

LIB_TARGET="$PRESS_LIBRARY/$API_SLUG"
MAN_TARGET="$PRESS_MANUSCRIPTS/$API_SLUG"

如果两者都不存在: 直接导入,进入阶段3。

如果任一存在: 读取双方的来源信息以决定是否覆盖。不要读取整个.printing-press.json文件——只提取关键字段:

# 内部来源信息(如果存在):
jq '{run_id, generated_at, printing_press_version, spec_checksum}' \
  "$LIB_TARGET/.printing-press.json" 2>/dev/null

# 公共来源信息(一次性原始获取):
gh api -H "Accept: application/vnd.github.v3.raw" \
  repos/mvanhorn/printing-press-library/contents/$LIB_PATH/.printing-press.json \
  | jq '{run_id, generated_at, printing_press_version, spec_checksum}'

根据差异进行推理:

  • 相同的run_id — 公共库与内部库是同一代。可能无需操作;覆盖前询问用户。如果用户仍要导入(例如恢复损坏的内部副本),则继续。
  • 公共库的generated_at更新 — 公共库有内部库没有的更改。导入是安全的选择;请用户确认。
  • 内部库的generated_at更新 — 内部库有公共库没有的工作(进行中的polish、手动修复)。导入会覆盖这些更改。停止并向用户说明——他们可能希望先发布内部更改。
  • 任一侧缺少.printing-press.json — 较旧或手动导入。询问用户。

当用户确认覆盖时,阶段3中的备份步骤会捕获当前内部状态。

阶段3 — 导入

STAGING=$(mktemp -d)

# 获取(远程,除非传入了--from-clone)
if [[ -n "${CLONE_PATH:-}" ]]; then
  bash "$SCRIPTS_DIR/import-fetch.sh" "$LIB_PATH" "$STAGING" --clone "$CLONE_PATH"
else
  bash "$SCRIPTS_DIR/import-fetch.sh" "$LIB_PATH" "$STAGING"
fi

# 如果有任何内容被覆盖则备份。在标准输出上打印zip路径。
if [[ -d "$LIB_TARGET" || -d "$MAN_TARGET" ]]; then
  BACKUP_ZIP=$(bash "$SCRIPTS_DIR/import-backup.sh" "$API_SLUG")
  echo "已备份至:$BACKUP_ZIP"
fi

# 反转发布步骤的模块路径重写。
bash "$SCRIPTS_DIR/import-rewrite.sh" "$STAGING" "$API_SLUG"

# 原子地将暂存内容移动到目标位置。
bash "$SCRIPTS_DIR/import-place.sh" "$STAGING" "$API_SLUG"

阶段4 — 验证内部一致性

移动后,确认导入的CLI可以构建且结构完整。将任何失败视为真正的问题——不要掩盖。

cd "$LIB_TARGET"

# 模块路径应为本地形式
grep -q "^module ${API_SLUG}-pp-cli\$" go.mod \
  || { echo "失败:go.mod仍为公共模块路径"; exit 1; }

# 源代码中未泄漏公共模块路径
if grep -rq "github.com/mvanhorn/printing-press-library/library" \
   --include='*.go' --include='*.yaml' --include='*.yml' .; then
  echo "失败:源代码仍引用公共模块路径"
  exit 1
fi

# 构建
go build ./... \
  || { echo "失败:go build"; exit 1; }

# 自检
make doctor 2>/dev/null \
  || ./bin/${API_SLUG}-pp-cli doctor 2>/dev/null \
  || true   # 尽力而为;并非所有CLI的doctor实现方式相同

报告导入结果:

  • 源路径(来自注册表:<category>/<api-slug>
  • 运行ID(来自.printing-press.json
  • 放置的手稿运行ID(数量+名称)
  • 备份zip路径(如果有)
  • 构建状态

Polish侧提示

如果用户的导入请求是由polish需求触发的(例如,他们说“polish公共库中的notion”),建议:

已导入$API_SLUG。要执行polish:/printing-press-polish $API_SLUG

polish技能操作的是内部库,因此从已发布的CLI开始时,导入后polish是正确的流程。