将已发布的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解析——该文件包含每个条目的name、category、api、description和path,一次获取即可。
REGISTRY=$(mktemp)
gh api -H "Accept: application/vnd.github.v3.raw" \
repos/mvanhorn/printing-press-library/contents/registry.json \
> "$REGISTRY"
按以下顺序匹配:
- 精确匹配
name—jq --arg q "$ARG" '.entries[] | select(.name == $q)' "$REGISTRY" - 标准化精确匹配 — 去除
-pp-cli后缀,转小写,点号转连字符,然后精确匹配 - 子串匹配
name或description— 不区分大小写的包含
# 精确匹配:
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个候选,显示每个候选的name和description。如果没有匹配项:告知用户公共库中没有该CLI。
匹配的条目提供了所需的一切:
LIB_PATH来自.path(例如library/productivity/cal-com)API_SLUG来自.nameCATEGORY来自.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是正确的流程。






