make-pdf
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese<!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->
<!-- Regenerate: bun run gen:skill-docs -->
<!-- 由SKILL.md.tmpl自动生成——请勿直接编辑 -->
<!-- 重新生成:bun run gen:skill-docs -->
Preamble (run first)
前置步骤(先运行)
bash
_UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true)
[ -n "$_UPD" ] && echo "$_UPD" || true
mkdir -p ~/.gstack/sessions
touch ~/.gstack/sessions/"$PPID"
_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ')
find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true
_PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true")
_PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no")
_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
echo "BRANCH: $_BRANCH"
_SKILL_PREFIX=$(~/.claude/skills/gstack/bin/gstack-config get skill_prefix 2>/dev/null || echo "false")
echo "PROACTIVE: $_PROACTIVE"
echo "PROACTIVE_PROMPTED: $_PROACTIVE_PROMPTED"
echo "SKILL_PREFIX: $_SKILL_PREFIX"
source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true
REPO_MODE=${REPO_MODE:-unknown}
echo "REPO_MODE: $REPO_MODE"
_LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no")
echo "LAKE_INTRO: $_LAKE_SEEN"
_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true)
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)"
echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED"
_EXPLAIN_LEVEL=$(~/.claude/skills/gstack/bin/gstack-config get explain_level 2>/dev/null || echo "default")
if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi
echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL"
_QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false")
echo "QUESTION_TUNING: $_QUESTION_TUNING"
mkdir -p ~/.gstack/analytics
if [ "$_TEL" != "off" ]; then
echo '{"skill":"make-pdf","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
fi
for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do
if [ -f "$_PF" ]; then
if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then
~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true
fi
rm -f "$_PF" 2>/dev/null || true
fi
break
done
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
_LEARN_FILE="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}/learnings.jsonl"
if [ -f "$_LEARN_FILE" ]; then
_LEARN_COUNT=$(wc -l < "$_LEARN_FILE" 2>/dev/null | tr -d ' ')
echo "LEARNINGS: $_LEARN_COUNT entries loaded"
if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 3 2>/dev/null || true
fi
else
echo "LEARNINGS: 0"
fi
~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"make-pdf","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null &
_HAS_ROUTING="no"
if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
_HAS_ROUTING="yes"
fi
_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
echo "HAS_ROUTING: $_HAS_ROUTING"
echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
_VENDORED="no"
if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then
if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then
_VENDORED="yes"
fi
fi
echo "VENDORED_GSTACK: $_VENDORED"
echo "MODEL_OVERLAY: claude"
_CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit")
_CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false")
echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE"
echo "CHECKPOINT_PUSH: $_CHECKPOINT_PUSH"
[ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || truebash
_UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true)
[ -n "$_UPD" ] && echo "$_UPD" || true
mkdir -p ~/.gstack/sessions
touch ~/.gstack/sessions/"$PPID"
_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ')
find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true
_PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true")
_PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no")
_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
echo "BRANCH: $_BRANCH"
_SKILL_PREFIX=$(~/.claude/skills/gstack/bin/gstack-config get skill_prefix 2>/dev/null || echo "false")
echo "PROACTIVE: $_PROACTIVE"
echo "PROACTIVE_PROMPTED: $_PROACTIVE_PROMPTED"
echo "SKILL_PREFIX: $_SKILL_PREFIX"
source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true
REPO_MODE=${REPO_MODE:-unknown}
echo "REPO_MODE: $REPO_MODE"
_LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no")
echo "LAKE_INTRO: $_LAKE_SEEN"
_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true)
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)"
echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED"
_EXPLAIN_LEVEL=$(~/.claude/skills/gstack/bin/gstack-config get explain_level 2>/dev/null || echo "default")
if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi
echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL"
_QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false")
echo "QUESTION_TUNING: $_QUESTION_TUNING"
mkdir -p ~/.gstack/analytics
if [ "$_TEL" != "off" ]; then
echo '{"skill":"make-pdf","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
fi
for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do
if [ -f "$_PF" ]; then
if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then
~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true
fi
rm -f "$_PF" 2>/dev/null || true
fi
break
done
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
_LEARN_FILE="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}/learnings.jsonl"
if [ -f "$_LEARN_FILE" ]; then
_LEARN_COUNT=$(wc -l < "$_LEARN_FILE" 2>/dev/null | tr -d ' ')
echo "LEARNINGS: $_LEARN_COUNT entries loaded"
if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 3 2>/dev/null || true
fi
else
echo "LEARNINGS: 0"
fi
~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"make-pdf","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null &
_HAS_ROUTING="no"
if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
_HAS_ROUTING="yes"
fi
_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
echo "HAS_ROUTING: $_HAS_ROUTING"
echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
_VENDORED="no"
if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then
if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then
_VENDORED="yes"
fi
fi
echo "VENDORED_GSTACK: $_VENDORED"
echo "MODEL_OVERLAY: claude"
_CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit")
_CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false")
echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE"
echo "CHECKPOINT_PUSH: $_CHECKPOINT_PUSH"
[ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || truePlan Mode Safe Operations
计划模式安全操作
In plan mode, allowed because they inform the plan: , , /, writes to , writes to the plan file, and for generated artifacts.
$B$Dcodex execcodex review~/.gstack/open在计划模式下,以下操作被允许,因为它们为计划提供信息:、、/、写入目录、写入计划文件,以及对生成的工件执行操作。
$B$Dcodex execcodex review~/.gstack/openSkill Invocation During Plan Mode
计划模式下的技能调用
If the user invokes a skill in plan mode, the skill takes precedence over generic plan mode behavior. Treat the skill file as executable instructions, not reference. Follow it step by step starting from Step 0; the first AskUserQuestion is the workflow entering plan mode, not a violation of it. AskUserQuestion (any variant — or native; see "AskUserQuestion Format → Tool resolution") satisfies plan mode's end-of-turn requirement. If no variant is callable, the skill is BLOCKED — stop and report per the AskUserQuestion Format rule. At a STOP point, stop immediately. Do not continue the workflow or call ExitPlanMode there. Commands marked "PLAN MODE EXCEPTION — ALWAYS RUN" execute. Call ExitPlanMode only after the skill workflow completes, or if the user tells you to cancel the skill or leave plan mode.
mcp__*__AskUserQuestionBLOCKED — AskUserQuestion unavailableIf is , do not auto-invoke or proactively suggest skills. If a skill seems useful, ask: "I think /skillname might help here — want me to run it?"
PROACTIVE"false"If is , suggest/invoke names. Disk paths stay .
SKILL_PREFIX"true"/gstack-*~/.claude/skills/gstack/[skill-name]/SKILL.mdIf output shows : read and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined).
UPGRADE_AVAILABLE <old> <new>~/.claude/skills/gstack/gstack-upgrade/SKILL.mdIf output shows : print "Running gstack v{to} (just updated!)". If is true, skip feature discovery.
JUST_UPGRADED <from> <to>SPAWNED_SESSIONFeature discovery, max one prompt per session:
- Missing : AskUserQuestion for Continuous checkpoint auto-commits. If accepted, run
~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint. Always touch marker.~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous - Missing : inform "Model overlays are active. MODEL_OVERLAY shows the patch." Always touch marker.
~/.claude/skills/gstack/.feature-prompted-model-overlay
After upgrade prompts, continue workflow.
If is : ask once about writing style:
WRITING_STYLE_PENDINGyesv1 prompts are simpler: first-use jargon glosses, outcome-framed questions, shorter prose. Keep default or restore terse?
Options:
- A) Keep the new default (recommended — good writing helps everyone)
- B) Restore V0 prose — set
explain_level: terse
If A: leave unset (defaults to ).
If B: run .
explain_leveldefault~/.claude/skills/gstack/bin/gstack-config set explain_level terseAlways run (regardless of choice):
bash
rm -f ~/.gstack/.writing-style-prompt-pending
touch ~/.gstack/.writing-style-promptedSkip if is .
WRITING_STYLE_PENDINGnoIf is : say "gstack follows the Boil the Lake principle — do the complete thing when AI makes marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean" Offer to open:
LAKE_INTROnobash
open https://garryslist.org/posts/boil-the-ocean
touch ~/.gstack/.completeness-intro-seenOnly run if yes. Always run .
opentouchIf is AND is : ask telemetry once via AskUserQuestion:
TEL_PROMPTEDnoLAKE_INTROyesHelp gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names.
Options:
- A) Help gstack get better! (recommended)
- B) No thanks
If A: run
~/.claude/skills/gstack/bin/gstack-config set telemetry communityIf B: ask follow-up:
Anonymous mode sends only aggregate usage, no unique ID.
Options:
- A) Sure, anonymous is fine
- B) No thanks, fully off
If B→A: run
If B→B: run
~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous~/.claude/skills/gstack/bin/gstack-config set telemetry offAlways run:
bash
touch ~/.gstack/.telemetry-promptedSkip if is .
TEL_PROMPTEDyesIf is AND is : ask once:
PROACTIVE_PROMPTEDnoTEL_PROMPTEDyesLet gstack proactively suggest skills, like /qa for "does this work?" or /investigate for bugs?
Options:
- A) Keep it on (recommended)
- B) Turn it off — I'll type /commands myself
If A: run
If B: run
~/.claude/skills/gstack/bin/gstack-config set proactive true~/.claude/skills/gstack/bin/gstack-config set proactive falseAlways run:
bash
touch ~/.gstack/.proactive-promptedSkip if is .
PROACTIVE_PROMPTEDyesIf is AND is AND is :
Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
HAS_ROUTINGnoROUTING_DECLINEDfalsePROACTIVE_PROMPTEDyesUse AskUserQuestion:
gstack works best when your project's CLAUDE.md includes skill routing rules.
Options:
- A) Add routing rules to CLAUDE.md (recommended)
- B) No thanks, I'll invoke skills manually
If A: Append this section to the end of CLAUDE.md:
markdown
undefined如果用户在计划模式下调用技能,该技能优先于通用计划模式行为。将技能文件视为可执行指令,而非参考文档。从步骤0开始逐步执行;第一个AskUserQuestion表示工作流进入计划模式,并非违反规则。AskUserQuestion(任何变体——或原生;请参阅“AskUserQuestion格式 → 工具解析”)满足计划模式的回合结束要求。如果没有可调用的变体,则技能被BLOCKED——根据AskUserQuestion格式规则,停止并报告。在STOP点,立即停止。不要继续工作流或调用ExitPlanMode。标记为“PLAN MODE EXCEPTION — ALWAYS RUN”的命令必须执行。仅在技能工作流完成后,或用户要求取消技能或退出计划模式时,才调用ExitPlanMode。
mcp__*__AskUserQuestionBLOCKED — AskUserQuestion unavailable如果为,请勿自动调用或主动建议技能。如果某个技能看起来有用,请询问:“我认为/skillname可能会有帮助——需要我运行它吗?”
PROACTIVE"false"如果为,建议/调用名称。磁盘路径保持为。
SKILL_PREFIX"true"/gstack-*~/.claude/skills/gstack/[skill-name]/SKILL.md如果输出显示:阅读并遵循“内联升级流程”(如果已配置则自动升级,否则通过AskUserQuestion提供4个选项,如果用户拒绝则写入暂停状态)。
UPGRADE_AVAILABLE <old> <new>~/.claude/skills/gstack/gstack-upgrade/SKILL.md如果输出显示:打印“Running gstack v{to} (just updated!)”。如果为true,跳过功能发现环节。
JUST_UPGRADED <from> <to>SPAWNED_SESSION功能发现,每个会话最多提示一次:
- 缺少:通过AskUserQuestion询问是否启用Continuous checkpoint自动提交。如果用户同意,运行
~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint。无论如何都要创建标记文件。~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous - 缺少:告知用户“Model overlays已激活。MODEL_OVERLAY显示补丁内容。”无论如何都要创建标记文件。
~/.claude/skills/gstack/.feature-prompted-model-overlay
完成升级提示后,继续工作流。
如果为:询问一次写作风格偏好:
WRITING_STYLE_PENDINGyesv1提示更简洁:首次使用时提供术语解释、以结果为导向的问题、更简短的文本。保留默认风格还是恢复简洁风格?
选项:
- A) 保留新默认风格(推荐——良好的写作体验对所有人都有帮助)
- B) 恢复V0文本风格——设置
explain_level: terse
如果选择A:不设置(默认值为)。
如果选择B:运行。
explain_leveldefault~/.claude/skills/gstack/bin/gstack-config set explain_level terse无论选择哪个选项,都要运行:
bash
rm -f ~/.gstack/.writing-style-prompt-pending
touch ~/.gstack/.writing-style-prompted如果为,则跳过此步骤。
WRITING_STYLE_PENDINGno如果为:说明“gstack遵循Boil the Lake原则——当AI的边际成本接近零时,完成完整的任务。了解更多:https://garryslist.org/posts/boil-the-ocean”,并提供打开链接的选项:
LAKE_INTROnobash
open https://garryslist.org/posts/boil-the-ocean
touch ~/.gstack/.completeness-intro-seen仅在用户同意时运行命令。无论如何都要运行命令。
opentouch如果为且为:通过AskUserQuestion询问一次遥测设置:
TEL_PROMPTEDnoLAKE_INTROyes帮助gstack变得更好。仅分享使用数据:技能名称、时长、崩溃情况、稳定设备ID。不包含代码、文件路径或仓库名称。
选项:
- A) 帮助gstack变得更好!(推荐)
- B) 不用了,谢谢
如果选择A:运行
~/.claude/skills/gstack/bin/gstack-config set telemetry community如果选择B:跟进询问:
匿名模式仅发送汇总使用数据,不包含唯一ID。
选项:
- A) 好的,匿名模式可以接受
- B) 不用了,完全关闭
如果B→A:运行
如果B→B:运行
~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous~/.claude/skills/gstack/bin/gstack-config set telemetry off无论选择哪个选项,都要运行:
bash
touch ~/.gstack/.telemetry-prompted如果为,则跳过此步骤。
TEL_PROMPTEDyes如果为且为:询问一次:
PROACTIVE_PROMPTEDnoTEL_PROMPTEDyes让gstack主动建议技能,比如针对“这个能运行吗?”调用/qa,或者针对bug调用/investigate?
选项:
- A) 保持开启(推荐)
- B) 关闭——我会手动输入/命令
如果选择A:运行
如果选择B:运行
~/.claude/skills/gstack/bin/gstack-config set proactive true~/.claude/skills/gstack/bin/gstack-config set proactive false无论选择哪个选项,都要运行:
bash
touch ~/.gstack/.proactive-prompted如果为,则跳过此步骤。
PROACTIVE_PROMPTEDyes如果为且为且为:
检查项目根目录是否存在CLAUDE.md文件。如果不存在,创建该文件。
HAS_ROUTINGnoROUTING_DECLINEDfalsePROACTIVE_PROMPTEDyes通过AskUserQuestion询问:
当项目的CLAUDE.md包含技能路由规则时,gstack的效果最佳。
选项:
- A) 向CLAUDE.md添加路由规则(推荐)
- B) 不用了,谢谢,我会手动调用技能
如果选择A:将以下章节追加到CLAUDE.md末尾:
markdown
undefinedSkill routing
Skill routing
When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill.
Key routing rules:
- Product ideas/brainstorming → invoke /office-hours
- Strategy/scope → invoke /plan-ceo-review
- Architecture → invoke /plan-eng-review
- Design system/plan review → invoke /design-consultation or /plan-design-review
- Full review pipeline → invoke /autoplan
- Bugs/errors → invoke /investigate
- QA/testing site behavior → invoke /qa or /qa-only
- Code review/diff check → invoke /review
- Visual polish → invoke /design-review
- Ship/deploy/PR → invoke /ship or /land-and-deploy
- Save progress → invoke /context-save
- Resume context → invoke /context-restore
Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true` and say they can re-enable with `gstack-config set routing_declined false`.
This only happens once per project. Skip if `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`.
If `VENDORED_GSTACK` is `yes`, warn once via AskUserQuestion unless `~/.gstack/.vendoring-warned-$SLUG` exists:
> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated.
> Migrate to team mode?
Options:
- A) Yes, migrate to team mode now
- B) No, I'll handle it myself
If A:
1. Run `git rm -r .claude/skills/gstack/`
2. Run `echo '.claude/skills/gstack/' >> .gitignore`
3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`)
4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"`
5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`"
If B: say "OK, you're on your own to keep the vendored copy up to date."
Always run (regardless of choice):
```bash
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
touch ~/.gstack/.vendoring-warned-${SLUG:-unknown}If marker exists, skip.
If is , you are running inside a session spawned by an
AI orchestrator (e.g., OpenClaw). In spawned sessions:
SPAWNED_SESSION"true"- Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option.
- Do NOT run upgrade checks, telemetry prompts, routing injection, or lake intro.
- Focus on completing the task and reporting results via prose output.
- End with a completion report: what shipped, decisions made, anything uncertain.
When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill.
Key routing rules:
- Product ideas/brainstorming → invoke /office-hours
- Strategy/scope → invoke /plan-ceo-review
- Architecture → invoke /plan-eng-review
- Design system/plan review → invoke /design-consultation or /plan-design-review
- Full review pipeline → invoke /autoplan
- Bugs/errors → invoke /investigate
- QA/testing site behavior → invoke /qa or /qa-only
- Code review/diff check → invoke /review
- Visual polish → invoke /design-review
- Ship/deploy/PR → invoke /ship or /land-and-deploy
- Save progress → invoke /context-save
- Resume context → invoke /context-restore
然后提交更改:`git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
如果选择B:运行`~/.claude/skills/gstack/bin/gstack-config set routing_declined true`并告知用户可以通过`gstack-config set routing_declined false`重新启用。
每个项目仅执行此操作一次。如果`HAS_ROUTING`为`yes`或`ROUTING_DECLINED`为`true`,则跳过此步骤。
如果`VENDORED_GSTACK`为`yes`,且`~/.gstack/.vendoring-warned-$SLUG`不存在,则通过AskUserQuestion警告一次:
> 此项目已将gstack内嵌在`.claude/skills/gstack/`目录中。内嵌方式已被弃用。
> 是否迁移到团队模式?
选项:
- A) 是的,立即迁移到团队模式
- B) 不用了,我会自行处理
如果选择A:
1. 运行`git rm -r .claude/skills/gstack/`
2. 运行`echo '.claude/skills/gstack/' >> .gitignore`
3. 运行`~/.claude/skills/gstack/bin/gstack-team-init required`(或`optional`)
4. 运行`git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"`
5. 告知用户:“完成。每位开发者现在需要运行:`cd ~/.claude/skills/gstack && ./setup --team`”
如果选择B:告知用户“好的,你需要自行保持内嵌副本的更新。”
无论选择哪个选项,都要运行:
```bash
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
touch ~/.gstack/.vendoring-warned-${SLUG:-unknown}如果标记文件已存在,则跳过此步骤。
如果为,说明你正在AI编排器(如OpenClaw)生成的会话中运行。在此类会话中:
SPAWNED_SESSION"true"- 请勿使用AskUserQuestion进行交互式提示。自动选择推荐选项。
- 请勿运行升级检查、遥测提示、路由注入或lake介绍环节。
- 专注于完成任务并通过文本输出报告结果。
- 最后提交完成报告:已完成的内容、做出的决策、任何不确定的事项。
Artifacts Sync (skill start)
工件同步(技能启动时)
bash
_GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"bash
_GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"Prefer the v1.27.0.0 artifacts file; fall back to brain file for users
Prefer the v1.27.0.0 artifacts file; fall back to brain file for users
upgrading mid-stream before the migration script runs.
upgrading mid-stream before the migration script runs.
if [ -f "$HOME/.gstack-artifacts-remote.txt" ]; then
_BRAIN_REMOTE_FILE="$HOME/.gstack-artifacts-remote.txt"
else
_BRAIN_REMOTE_FILE="$HOME/.gstack-brain-remote.txt"
fi
_BRAIN_SYNC_BIN="/.claude/skills/gstack/bin/gstack-brain-sync"
_BRAIN_CONFIG_BIN="/.claude/skills/gstack/bin/gstack-config"
if [ -f "$HOME/.gstack-artifacts-remote.txt" ]; then
_BRAIN_REMOTE_FILE="$HOME/.gstack-artifacts-remote.txt"
else
_BRAIN_REMOTE_FILE="$HOME/.gstack-brain-remote.txt"
fi
_BRAIN_SYNC_BIN="/.claude/skills/gstack/bin/gstack-brain-sync"
_BRAIN_CONFIG_BIN="/.claude/skills/gstack/bin/gstack-config"
/sync-gbrain context-load: teach the agent to use gbrain when it's available.
/sync-gbrain context-load: teach the agent to use gbrain when it's available.
Per-worktree pin: post-spike redesign uses kubectl-style .gbrain-source
in the
.gbrain-sourcePer-worktree pin: post-spike redesign uses kubectl-style .gbrain-source
in the
.gbrain-sourcegit toplevel to scope queries. Look for the pin in the worktree (not a global
git toplevel to scope queries. Look for the pin in the worktree (not a global
state file) so that opening worktree B without a pin doesn't claim "indexed"
state file) so that opening worktree B without a pin doesn't claim "indexed"
just because worktree A was synced. Empty string when gbrain is not
just because worktree A was synced. Empty string when gbrain is not
configured (zero context cost for non-gbrain users).
configured (zero context cost for non-gbrain users).
_GBRAIN_CONFIG="$HOME/.gbrain/config.json"
if [ -f "$_GBRAIN_CONFIG" ] && command -v gbrain >/dev/null 2>&1; then
_GBRAIN_VERSION_OK=$(gbrain --version 2>/dev/null | grep -c '^gbrain ' || echo 0)
if [ "$_GBRAIN_VERSION_OK" -gt 0 ] 2>/dev/null; then
_GBRAIN_PIN_PATH=""
_REPO_TOP=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
if [ -n "$_REPO_TOP" ] && [ -f "$_REPO_TOP/.gbrain-source" ]; then
_GBRAIN_PIN_PATH="$_REPO_TOP/.gbrain-source"
fi
if [ -n "$_GBRAIN_PIN_PATH" ]; then
echo "GBrain configured. Prefer `gbrain search`/`gbrain query` over Grep for"
echo "semantic questions; use `gbrain code-def`/`code-refs`/`code-callers` for"
echo "symbol-aware code lookup. See "## GBrain Search Guidance" in CLAUDE.md."
echo "Run /sync-gbrain to refresh."
else
echo "GBrain configured but this worktree isn't pinned yet. Run `/sync-gbrain --full`"
echo "before relying on `gbrain search` for code questions in this worktree."
echo "Falls back to Grep until pinned."
fi
fi
fi
_BRAIN_SYNC_MODE=$("$_BRAIN_CONFIG_BIN" get artifacts_sync_mode 2>/dev/null || echo off)
_GBRAIN_CONFIG="$HOME/.gbrain/config.json"
if [ -f "$_GBRAIN_CONFIG" ] && command -v gbrain >/dev/null 2>&1; then
_GBRAIN_VERSION_OK=$(gbrain --version 2>/dev/null | grep -c '^gbrain ' || echo 0)
if [ "$_GBRAIN_VERSION_OK" -gt 0 ] 2>/dev/null; then
_GBRAIN_PIN_PATH=""
_REPO_TOP=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
if [ -n "$_REPO_TOP" ] && [ -f "$_REPO_TOP/.gbrain-source" ]; then
_GBRAIN_PIN_PATH="$_REPO_TOP/.gbrain-source"
fi
if [ -n "$_GBRAIN_PIN_PATH" ]; then
echo "GBrain configured. Prefer `gbrain search`/`gbrain query` over Grep for"
echo "semantic questions; use `gbrain code-def`/`code-refs`/`code-callers` for"
echo "symbol-aware code lookup. See "## GBrain Search Guidance" in CLAUDE.md."
echo "Run /sync-gbrain to refresh."
else
echo "GBrain configured but this worktree isn't pinned yet. Run `/sync-gbrain --full`"
echo "before relying on `gbrain search` for code questions in this worktree."
echo "Falls back to Grep until pinned."
fi
fi
fi
_BRAIN_SYNC_MODE=$("$_BRAIN_CONFIG_BIN" get artifacts_sync_mode 2>/dev/null || echo off)
Detect remote-MCP mode (Path 4 of /setup-gbrain). Local artifacts sync is
Detect remote-MCP mode (Path 4 of /setup-gbrain). Local artifacts sync is
a no-op in remote mode; the brain server pulls from GitHub/GitLab on its
a no-op in remote mode; the brain server pulls from GitHub/GitLab on its
own cadence. Read claude.json directly to keep this preamble fast (no
own cadence. Read claude.json directly to keep this preamble fast (no
subprocess to claude CLI on every skill start).
subprocess to claude CLI on every skill start).
_GBRAIN_MCP_MODE="none"
if command -v jq >/dev/null 2>&1 && [ -f "$HOME/.claude.json" ]; then
_GBRAIN_MCP_TYPE=$(jq -r '.mcpServers.gbrain.type // .mcpServers.gbrain.transport // empty' "$HOME/.claude.json" 2>/dev/null)
case "$_GBRAIN_MCP_TYPE" in
url|http|sse) _GBRAIN_MCP_MODE="remote-http" ;;
stdio) _GBRAIN_MCP_MODE="local-stdio" ;;
esac
fi
if [ -f "$_BRAIN_REMOTE_FILE" ] && [ ! -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" = "off" ]; then
_BRAIN_NEW_URL=$(head -1 "$_BRAIN_REMOTE_FILE" 2>/dev/null | tr -d '[:space:]')
if [ -n "$_BRAIN_NEW_URL" ]; then
echo "ARTIFACTS_SYNC: artifacts repo detected: $_BRAIN_NEW_URL"
echo "ARTIFACTS_SYNC: run 'gstack-brain-restore' to pull your cross-machine artifacts (or 'gstack-config set artifacts_sync_mode off' to dismiss forever)"
fi
fi
if [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then
_BRAIN_LAST_PULL_FILE="$_GSTACK_HOME/.brain-last-pull"
_BRAIN_NOW=$(date +%s)
_BRAIN_DO_PULL=1
if [ -f "$_BRAIN_LAST_PULL_FILE" ]; then
_BRAIN_LAST=$(cat "$_BRAIN_LAST_PULL_FILE" 2>/dev/null || echo 0)
_BRAIN_AGE=$(( _BRAIN_NOW - _BRAIN_LAST ))
[ "$_BRAIN_AGE" -lt 86400 ] && _BRAIN_DO_PULL=0
fi
if [ "$_BRAIN_DO_PULL" = "1" ]; then
( cd "$_GSTACK_HOME" && git fetch origin >/dev/null 2>&1 && git merge --ff-only "origin/$(git rev-parse --abbrev-ref HEAD)" >/dev/null 2>&1 ) || true
echo "$_BRAIN_NOW" > "$_BRAIN_LAST_PULL_FILE"
fi
"$_BRAIN_SYNC_BIN" --once 2>/dev/null || true
fi
if [ "$_GBRAIN_MCP_MODE" = "remote-http" ]; then
Remote-MCP mode: local artifacts sync is a no-op (brain admin's server
pulls from GitHub/GitLab). Show the user this is by design, not broken.
_GBRAIN_HOST=$(jq -r '.mcpServers.gbrain.url // empty' "$HOME/.claude.json" 2>/dev/null | sed -E 's|^https?://([^/:]+).*|\1|')
echo "ARTIFACTS_SYNC: remote-mode (managed by brain server ${_GBRAIN_HOST:-remote})"
elif [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then
_BRAIN_QUEUE_DEPTH=0
[ -f "$_GSTACK_HOME/.brain-queue.jsonl" ] && _BRAIN_QUEUE_DEPTH=$(wc -l < "$_GSTACK_HOME/.brain-queue.jsonl" | tr -d ' ')
_BRAIN_LAST_PUSH="never"
[ -f "$_GSTACK_HOME/.brain-last-push" ] && _BRAIN_LAST_PUSH=$(cat "$_GSTACK_HOME/.brain-last-push" 2>/dev/null || echo never)
echo "ARTIFACTS_SYNC: mode=$_BRAIN_SYNC_MODE | last_push=$_BRAIN_LAST_PUSH | queue=$_BRAIN_QUEUE_DEPTH"
else
echo "ARTIFACTS_SYNC: off"
fi
Privacy stop-gate: if output shows `ARTIFACTS_SYNC: off`, `artifacts_sync_mode_prompted` is `false`, and gbrain is on PATH or `gbrain doctor --fast --json` works, ask once:
> gstack can publish your artifacts (CEO plans, designs, reports) to a private GitHub repo that GBrain indexes across machines. How much should sync?
Options:
- A) Everything allowlisted (recommended)
- B) Only artifacts
- C) Decline, keep everything local
After answer:
```bash_GBRAIN_MCP_MODE="none"
if command -v jq >/dev/null 2>&1 && [ -f "$HOME/.claude.json" ]; then
_GBRAIN_MCP_TYPE=$(jq -r '.mcpServers.gbrain.type // .mcpServers.gbrain.transport // empty' "$HOME/.claude.json" 2>/dev/null)
case "$_GBRAIN_MCP_TYPE" in
url|http|sse) _GBRAIN_MCP_MODE="remote-http" ;;
stdio) _GBRAIN_MCP_MODE="local-stdio" ;;
esac
fi
if [ -f "$_BRAIN_REMOTE_FILE" ] && [ ! -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" = "off" ]; then
_BRAIN_NEW_URL=$(head -1 "$_BRAIN_REMOTE_FILE" 2>/dev/null | tr -d '[:space:]')
if [ -n "$_BRAIN_NEW_URL" ]; then
echo "ARTIFACTS_SYNC: artifacts repo detected: $_BRAIN_NEW_URL"
echo "ARTIFACTS_SYNC: run 'gstack-brain-restore' to pull your cross-machine artifacts (or 'gstack-config set artifacts_sync_mode off' to dismiss forever)"
fi
fi
if [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then
_BRAIN_LAST_PULL_FILE="$_GSTACK_HOME/.brain-last-pull"
_BRAIN_NOW=$(date +%s)
_BRAIN_DO_PULL=1
if [ -f "$_BRAIN_LAST_PULL_FILE" ]; then
_BRAIN_LAST=$(cat "$_BRAIN_LAST_PULL_FILE" 2>/dev/null || echo 0)
_BRAIN_AGE=$(( _BRAIN_NOW - _BRAIN_LAST ))
[ "$_BRAIN_AGE" -lt 86400 ] && _BRAIN_DO_PULL=0
fi
if [ "$_BRAIN_DO_PULL" = "1" ]; then
( cd "$_GSTACK_HOME" && git fetch origin >/dev/null 2>&1 && git merge --ff-only "origin/$(git rev-parse --abbrev-ref HEAD)" >/dev/null 2>&1 ) || true
echo "$_BRAIN_NOW" > "$_BRAIN_LAST_PULL_FILE"
fi
"$_BRAIN_SYNC_BIN" --once 2>/dev/null || true
fi
if [ "$_GBRAIN_MCP_MODE" = "remote-http" ]; then
Remote-MCP mode: local artifacts sync is a no-op (brain admin's server
pulls from GitHub/GitLab). Show the user this is by design, not broken.
_GBRAIN_HOST=$(jq -r '.mcpServers.gbrain.url // empty' "$HOME/.claude.json" 2>/dev/null | sed -E 's|^https?://([^/:]+).*|\1|')
echo "ARTIFACTS_SYNC: remote-mode (managed by brain server ${_GBRAIN_HOST:-remote})"
elif [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then
_BRAIN_QUEUE_DEPTH=0
[ -f "$_GSTACK_HOME/.brain-queue.jsonl" ] && _BRAIN_QUEUE_DEPTH=$(wc -l < "$_GSTACK_HOME/.brain-queue.jsonl" | tr -d ' ')
_BRAIN_LAST_PUSH="never"
[ -f "$_GSTACK_HOME/.brain-last-push" ] && _BRAIN_LAST_PUSH=$(cat "$_GSTACK_HOME/.brain-last-push" 2>/dev/null || echo never)
echo "ARTIFACTS_SYNC: mode=$_BRAIN_SYNC_MODE | last_push=$_BRAIN_LAST_PUSH | queue=$_BRAIN_QUEUE_DEPTH"
else
echo "ARTIFACTS_SYNC: off"
fi
隐私检查点:如果输出显示`ARTIFACTS_SYNC: off`,`artifacts_sync_mode_prompted`为`false`,且gbrain已在PATH中或`gbrain doctor --fast --json`可正常运行,则询问一次:
> gstack可以将你的工件(CEO计划、设计稿、报告)发布到私有GitHub仓库,供GBrain跨机器索引。同步范围设置为?
选项:
- A) 所有允许的内容(推荐)
- B) 仅工件
- C) 拒绝,全部保留在本地
用户回答后运行:
```bashChosen mode: full | artifacts-only | off
Chosen mode: full | artifacts-only | off
"$_BRAIN_CONFIG_BIN" set artifacts_sync_mode <choice>
"$_BRAIN_CONFIG_BIN" set artifacts_sync_mode_prompted true
If A/B and `~/.gstack/.git` is missing, ask whether to run `gstack-artifacts-init`. Do not block the skill.
At skill END before telemetry:
```bash
"~/.claude/skills/gstack/bin/gstack-brain-sync" --discover-new 2>/dev/null || true
"~/.claude/skills/gstack/bin/gstack-brain-sync" --once 2>/dev/null || true"$_BRAIN_CONFIG_BIN" set artifacts_sync_mode <choice>
"$_BRAIN_CONFIG_BIN" set artifacts_sync_mode_prompted true
如果选择A/B且`~/.gstack/.git`不存在,询问是否运行`gstack-artifacts-init`。不要阻塞技能运行。
在技能结束、遥测之前运行:
```bash
"~/.claude/skills/gstack/bin/gstack-brain-sync" --discover-new 2>/dev/null || true
"~/.claude/skills/gstack/bin/gstack-brain-sync" --once 2>/dev/null || trueModel-Specific Behavioral Patch (claude)
特定模型行为补丁(claude)
The following nudges are tuned for the claude model family. They are
subordinate to skill workflow, STOP points, AskUserQuestion gates, plan-mode
safety, and /ship review gates. If a nudge below conflicts with skill instructions,
the skill wins. Treat these as preferences, not rules.
Todo-list discipline. When working through a multi-step plan, mark each task
complete individually as you finish it. Do not batch-complete at the end. If a task
turns out to be unnecessary, mark it skipped with a one-line reason.
Think before heavy actions. For complex operations (refactors, migrations,
non-trivial new features), briefly state your approach before executing. This lets
the user course-correct cheaply instead of mid-flight.
Dedicated tools over Bash. Prefer Read, Edit, Write, Glob, Grep over shell
equivalents (cat, sed, find, grep). The dedicated tools are cheaper and clearer.
以下提示针对claude模型家族进行了优化。它们服从于技能工作流、STOP点、AskUserQuestion检查点、计划模式安全规则和/ship审核检查点。如果以下提示与技能说明冲突,以技能说明为准。将这些视为偏好,而非强制规则。
待办事项纪律。处理多步骤计划时,完成每个任务后单独标记为已完成。不要在最后批量标记完成。如果某个任务被证明是不必要的,标记为已跳过并附上一行原因说明。
执行复杂操作前先思考。对于复杂操作(重构、迁移、非平凡的新功能),在执行前简要说明你的方法。这样用户可以在早期进行调整,而不必在操作中途纠正。
优先使用专用工具而非Bash。优先使用Read、Edit、Write、Glob、Grep而非shell等效命令(cat、sed、find、grep)。专用工具成本更低、更清晰。
Voice
语言风格
Direct, concrete, builder-to-builder. Name the file, function, command, and user-visible impact. No filler.
No em dashes. No AI vocabulary: delve, crucial, robust, comprehensive, nuanced, multifaceted. Never corporate or academic. Short paragraphs. End with what to do.
The user has context you do not. Cross-model agreement is a recommendation, not a decision. The user decides.
直接、具体,开发者对开发者的语气。明确文件名、函数、命令和对用户可见的影响。不要冗余内容。
不要使用破折号。不要使用AI词汇:delve、crucial、robust、comprehensive、nuanced、multifaceted。避免企业或学术风格。使用短段落。结尾说明下一步操作。
用户拥有你不知道的上下文。跨模型共识是建议,而非决策。最终由用户决定。
Completion Status Protocol
完成状态协议
When completing a skill workflow, report status using one of:
- DONE — completed with evidence.
- DONE_WITH_CONCERNS — completed, but list concerns.
- BLOCKED — cannot proceed; state blocker and what was tried.
- NEEDS_CONTEXT — missing info; state exactly what is needed.
Escalate after 3 failed attempts, uncertain security-sensitive changes, or scope you cannot verify. Format: , , , .
STATUSREASONATTEMPTEDRECOMMENDATION完成技能工作流时,使用以下状态之一报告:
- DONE — 已完成并提供证据。
- DONE_WITH_CONCERNS — 已完成,但列出关注点。
- BLOCKED — 无法继续;说明阻塞原因和已尝试的操作。
- NEEDS_CONTEXT — 缺少信息;明确说明需要的内容。
如果3次尝试失败、涉及不确定的安全敏感更改,或无法验证范围,则升级问题。格式:、、、。
STATUSREASONATTEMPTEDRECOMMENDATIONOperational Self-Improvement
操作自我改进
Before completing, if you discovered a durable project quirk or command fix that would save 5+ minutes next time, log it:
bash
~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"SKILL_NAME","type":"operational","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"observed"}'Do not log obvious facts or one-time transient errors.
完成前,如果发现持久的项目特性或命令修复可以在下次节省5分钟以上时间,请记录:
bash
~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"SKILL_NAME","type":"operational","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"observed"}'不要记录明显的事实或一次性临时错误。
Telemetry (run last)
遥测(最后运行)
After workflow completion, log telemetry. Use skill from frontmatter. OUTCOME is success/error/abort/unknown.
name:PLAN MODE EXCEPTION — ALWAYS RUN: This command writes telemetry to
, matching preamble analytics writes.
~/.gstack/analytics/Run this bash:
bash
_TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true工作流完成后,记录遥测数据。使用前端部分的技能。OUTCOME为success/error/abort/unknown。
name:PLAN MODE EXCEPTION — ALWAYS RUN: 此命令将遥测数据写入,与前置步骤中的分析写入一致。
~/.gstack/analytics/运行以下bash命令:
bash
_TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || trueSession timeline: record skill completion (local-only, never sent anywhere)
Session timeline: record skill completion (local-only, never sent anywhere)
~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"SKILL_NAME","event":"completed","branch":"'$(git branch --show-current 2>/dev/null || echo unknown)'","outcome":"OUTCOME","duration_s":"'"$_TEL_DUR"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null || true
~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"SKILL_NAME","event":"completed","branch":"'$(git branch --show-current 2>/dev/null || echo unknown)'","outcome":"OUTCOME","duration_s":"'"$_TEL_DUR"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null || true
Local analytics (gated on telemetry setting)
Local analytics (gated on telemetry setting)
if [ "$_TEL" != "off" ]; then
echo '{"skill":"SKILL_NAME","duration_s":"'"$_TEL_DUR"'","outcome":"OUTCOME","browse":"USED_BROWSE","session":"'"$_SESSION_ID"'","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
fi
if [ "$_TEL" != "off" ]; then
echo '{"skill":"SKILL_NAME","duration_s":"'"$_TEL_DUR"'","outcome":"OUTCOME","browse":"USED_BROWSE","session":"'"$_SESSION_ID"'","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
fi
Remote telemetry (opt-in, requires binary)
Remote telemetry (opt-in, requires binary)
if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then
~/.claude/skills/gstack/bin/gstack-telemetry-log
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME"
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & fi
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME"
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & fi
Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running.if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then
~/.claude/skills/gstack/bin/gstack-telemetry-log
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME"
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & fi
--skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME"
--used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & fi
运行前替换`SKILL_NAME`、`OUTCOME`和`USED_BROWSE`。Plan Status Footer
计划状态页脚
In plan mode before ExitPlanMode: if the plan file lacks , run and append the standard runs/status/findings table. With or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run ". If a richer report exists, skip.
## GSTACK REVIEW REPORT~/.claude/skills/gstack/bin/gstack-review-readNO_REVIEWS/autoplanPLAN MODE EXCEPTION — always allowed (it's the plan file).
在计划模式下、ExitPlanMode之前:如果计划文件缺少,运行并追加标准的运行/状态/发现表格。如果或为空,追加5行占位符,结论为"NO REVIEWS YET — run "。如果已有更详细的报告,则跳过此步骤。
## GSTACK REVIEW REPORT~/.claude/skills/gstack/bin/gstack-review-readNO_REVIEWS/autoplanPLAN MODE EXCEPTION — 始终允许(这是计划文件的操作)。
make-pdf: publication-quality PDFs from markdown
make-pdf:从Markdown生成出版级PDF
Turn files into PDFs that look like Faber & Faber essays: 1in margins,
left-aligned body, Helvetica throughout, curly quotes and em dashes, optional
cover page and clickable TOC, diagonal DRAFT watermark when you need it.
Copy-paste from the PDF produces clean words, never "S a i l i n g".
.mdOn Linux, install for correct rendering — Helvetica and Arial
aren't present by default, and Liberation Sans is the standard metric-compatible
fallback. CI and Docker builds install it automatically via Dockerfile.ci.
fonts-liberation将文件转换为类似Faber & Faber出版社风格的PDF:1英寸边距、左对齐正文、全程使用Helvetica字体、弯引号和破折号、可选封面页和可点击目录、需要时添加斜向DRAFT水印。从PDF中复制粘贴会得到干净的文本,不会出现“S a i l i n g”这种碎片化情况。
.md在Linux系统上,安装以确保正确渲染——默认情况下没有Helvetica和Arial字体,Liberation Sans是标准的 metric-compatible 替代字体。CI和Docker构建会通过Dockerfile.ci自动安装该字体。
fonts-liberationMAKE-PDF SETUP (run this check BEFORE any make-pdf command)
MAKE-PDF 设置(运行任何make-pdf命令前先执行此检查)
bash
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
P=""
[ -n "$MAKE_PDF_BIN" ] && [ -x "$MAKE_PDF_BIN" ] && P="$MAKE_PDF_BIN"
[ -z "$P" ] && [ -n "$_ROOT" ] && [ -x "$_ROOT/.claude/skills/gstack/make-pdf/dist/pdf" ] && P="$_ROOT/.claude/skills/gstack/make-pdf/dist/pdf"
[ -z "$P" ] && P="$HOME/.claude/skills/gstack/make-pdf/dist/pdf"
if [ -x "$P" ]; then
echo "MAKE_PDF_READY: $P"
alias _p_="$P" # shellcheck alias helper (not exported)
export P # available as $P in subsequent blocks within the same skill invocation
else
echo "MAKE_PDF_NOT_AVAILABLE (run './setup' in the gstack repo to build it)"
fiIf is printed: tell the user the binary is not
built. Have them run from the gstack repo, then retry.
MAKE_PDF_NOT_AVAILABLE./setupIf is printed: is the binary path for the rest of
the skill. Use (not an explicit path) so the skill body stays portable.
MAKE_PDF_READY$P$PCore commands:
- — render markdown to PDF (80% use case)
$P generate <input.md> [output.pdf] - — full publication layout
$P generate --cover --toc essay.md out.pdf - — diagonal DRAFT watermark
$P generate --watermark DRAFT memo.md draft.pdf - — render HTML and open in browser (fast iteration)
$P preview <input.md> - — verify browse + Chromium + pdftotext and run a smoke test
$P setup - — full flag reference
$P --help
Output contract:
- : ONLY the output path on success. One line.
stdout - : progress (
stderr) unlessRendering HTML... Generating PDF....--quiet - Exit 0 success / 1 bad args / 2 render error / 3 Paged.js timeout / 4 browse unavailable.
bash
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
P=""
[ -n "$MAKE_PDF_BIN" ] && [ -x "$MAKE_PDF_BIN" ] && P="$MAKE_PDF_BIN"
[ -z "$P" ] && [ -n "$_ROOT" ] && [ -x "$_ROOT/.claude/skills/gstack/make-pdf/dist/pdf" ] && P="$_ROOT/.claude/skills/gstack/make-pdf/dist/pdf"
[ -z "$P" ] && P="$HOME/.claude/skills/gstack/make-pdf/dist/pdf"
if [ -x "$P" ]; then
echo "MAKE_PDF_READY: $P"
alias _p_="$P" # shellcheck alias helper (not exported)
export P # available as $P in subsequent blocks within the same skill invocation
else
echo "MAKE_PDF_NOT_AVAILABLE (run './setup' in the gstack repo to build it)"
fi如果输出:告知用户二进制文件未构建。让他们在gstack仓库中运行,然后重试。
MAKE_PDF_NOT_AVAILABLE./setup如果输出:为技能后续使用的二进制文件路径。使用(而非显式路径)以保持技能主体的可移植性。
MAKE_PDF_READY$P$P核心命令:
- — 将Markdown渲染为PDF(80%的使用场景)
$P generate <input.md> [output.pdf] - — 完整出版布局
$P generate --cover --toc essay.md out.pdf - — 添加斜向DRAFT水印
$P generate --watermark DRAFT memo.md draft.pdf - — 渲染HTML并在浏览器中打开(快速迭代)
$P preview <input.md> - — 验证browse + Chromium + pdftotext并运行冒烟测试
$P setup - — 完整的参数参考
$P --help
输出约定:
- :仅在成功时输出路径,一行内容。
stdout - :进度信息(
stderr),除非使用Rendering HTML... Generating PDF...参数。--quiet - 退出码:0表示成功 / 1表示参数错误 / 2表示渲染错误 / 3表示Paged.js超时 / 4表示browse不可用。
Core patterns
核心使用模式
80% case — memo/letter
80%场景——备忘录/信函
One command, no flags. Gets a clean PDF with running header + page numbers
- CONFIDENTIAL footer by default.
bash
$P generate letter.md # writes /tmp/letter.pdf
$P generate letter.md letter.pdf # explicit output path一个命令,无参数。默认生成包含页眉+页码+CONFIDENTIAL页脚的干净PDF。
bash
$P generate letter.md # 写入/tmp/letter.pdf
$P generate letter.md letter.pdf # 显式指定输出路径Publication mode — cover + TOC + chapter breaks
出版模式——封面+目录+章节分页
bash
$P generate --cover --toc --author "Garry Tan" --title "On Horizons" \
essay.md essay.pdfEach top-level H1 in the markdown starts a new page. Disable with
for memos that happen to have multiple H1s.
--no-chapter-breaksbash
$P generate --cover --toc --author "Garry Tan" --title "On Horizons" \
essay.md essay.pdfMarkdown中的每个顶级H1标题会启动新页面。对于包含多个H1的备忘录,可使用禁用此功能。
--no-chapter-breaksDraft-stage watermark
草稿阶段水印
bash
$P generate --watermark DRAFT memo.md draft.pdfDiagonal 10% opacity DRAFT across every page. When the draft is final, drop
the flag and regenerate.
bash
$P generate --watermark DRAFT memo.md draft.pdf在每一页添加10%透明度的斜向DRAFT水印。草稿完成后,移除该参数并重新生成。
Fast iteration via preview
通过预览快速迭代
bash
$P preview essay.mdRenders HTML with the same print CSS and opens it in your browser. Refresh
as you edit the markdown. Skip the PDF round trip until you're ready.
bash
$P preview essay.md使用相同的打印CSS渲染HTML并在浏览器中打开。编辑Markdown后刷新页面即可。在准备好之前跳过PDF生成步骤。
Brand-free (no CONFIDENTIAL footer)
无品牌标识(无CONFIDENTIAL页脚)
bash
$P generate --no-confidential memo.md memo.pdfbash
$P generate --no-confidential memo.md memo.pdfCommon flags
常用参数
Page layout:
--margins <dim> 1in (default) | 72pt | 2.54cm | 25mm
--page-size letter|a4|legal
Structure:
--cover Cover page (title, author, date, hairline rule)
--toc Clickable TOC with page numbers
--no-chapter-breaks Don't start a new page at every H1
Branding:
--watermark <text> Diagonal watermark ("DRAFT", "CONFIDENTIAL")
--header-template <html> Custom running header
--footer-template <html> Custom footer (mutex with --page-numbers)
--no-confidential Suppress the CONFIDENTIAL right-footer
Output:
--page-numbers "N of M" footer (default on)
--tagged Accessible PDF (default on)
--outline PDF bookmarks from headings (default on)
--quiet Suppress progress on stderr
--verbose Per-stage timings
Network:
--allow-network Fetch external images. Off by default
(blocks tracking pixels).
Metadata:
--title "..." Document title (defaults to first H1)
--author "..." Author for cover + PDF metadata
--date "..." Date for cover (defaults to today)页面布局:
--margins <dim> 1英寸(默认)| 72pt | 2.54cm | 25mm
--page-size letter|a4|legal
结构:
--cover 封面页(标题、作者、日期、细线条)
--toc 带页码的可点击目录
--no-chapter-breaks 不在每个H1标题处启动新页面
品牌标识:
--watermark <text> 斜向水印("DRAFT"、"CONFIDENTIAL")
--header-template <html> 自定义页眉
--footer-template <html> 自定义页脚(与--page-numbers互斥)
--no-confidential 禁用右下角的CONFIDENTIAL页脚
输出:
--page-numbers "N of M"页脚(默认开启)
--tagged 无障碍PDF(默认开启)
--outline 从标题生成PDF书签(默认开启)
--quiet 禁止在stderr输出进度信息
--verbose 输出每个阶段的计时信息
网络:
--allow-network 获取外部图片。默认关闭
(阻止跟踪像素)。
元数据:
--title "..." 文档标题(默认使用第一个H1)
--author "..." 封面和PDF元数据中的作者
--date "..." 封面中的日期(默认为今天)When Claude should run it
Claude何时运行此工具
Watch for markdown-to-PDF intent. Any of these patterns → run :
$P generate- "Can you make this markdown a PDF"
- "Export it as a PDF"
- "Turn this letter into a PDF"
- "I need a PDF of the essay"
- "Print this as a PDF for me"
If the user has a file open and says "make it look nice", propose
and ask before running.
.md$P generate --cover --toc留意Markdown转PDF的意图。出现以下任何模式时,运行:
$P generate- "Can you make this markdown a PDF"
- "Export it as a PDF"
- "Turn this letter into a PDF"
- "I need a PDF of the essay"
- "Print this as a PDF for me"
如果用户打开了一个文件并说"make it look nice",建议使用并在运行前询问用户。
.md$P generate --cover --tocDebugging
调试
- Output looks empty / blank → check browse daemon is running: .
$B status - Fragmented text on copy-paste → highlight.js output (Phase 4). Retry with
once that flag exists. For now, remove fenced code blocks and regenerate.
--no-syntax - Paged.js timeout → probably no headings in the markdown. Drop .
--toc - External image missing → add (understand you're giving the markdown file permission to fetch from its image URLs).
--allow-network - Generated PDF too tall/wide → or
--page-size a4.--margins 0.75in
- 输出为空/空白 → 检查browse守护进程是否运行:。
$B status - 复制粘贴时文本碎片化 → highlight.js输出(第4阶段)。一旦参数可用,重试时使用该参数。目前,移除代码块并重新生成。
--no-syntax - Paged.js超时 → 可能是Markdown中没有标题。移除参数。
--toc - 外部图片缺失 → 添加参数(注意这会授予Markdown文件从其图片URL获取内容的权限)。
--allow-network - 生成的PDF过高/过宽 → 使用或
--page-size a4。--margins 0.75in
Output contract
输出约定
stdout: /tmp/letter.pdf ← just the path, one line
stderr: Rendering HTML... ← progress spinner (unless --quiet)
Generating PDF...
Done in 1.5s. 43 words · 22KB · /tmp/letter.pdf
exit code: 0 success / 1 bad args / 2 render error / 3 Paged.js timeout
/ 4 browse unavailableCapture the path: — then use .
PDF=$($P generate letter.md)$PDFstdout: /tmp/letter.pdf ← 仅输出路径,一行
stderr: Rendering HTML... ← 进度提示(除非使用--quiet)
Generating PDF...
Done in 1.5s. 43 words · 22KB · /tmp/letter.pdf
exit code: 0 success / 1 bad args / 2 render error / 3 Paged.js timeout
/ 4 browse unavailable捕获路径: — 然后使用。
PDF=$($P generate letter.md)$PDF