From 0708273e5a764e3ef3762474bafea9d0d7793d31 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Mon, 20 Apr 2026 05:33:30 +0800 Subject: [PATCH] feat(resolvers): add MAKE_PDF_SETUP + makePdfDir host paths Skill templates can now embed {{MAKE_PDF_SETUP}} to resolve $P to the make-pdf binary via the same discovery order as $B / $D: env override (MAKE_PDF_BIN), local skill root, global install, or PATH. Mirrors the pattern established by generateBrowseSetup() and generateDesignSetup() in scripts/resolvers/design.ts. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/resolvers/index.ts | 2 ++ scripts/resolvers/make-pdf.ts | 50 +++++++++++++++++++++++++++++++++++ scripts/resolvers/types.ts | 3 +++ 3 files changed, 55 insertions(+) create mode 100644 scripts/resolvers/make-pdf.ts diff --git a/scripts/resolvers/index.ts b/scripts/resolvers/index.ts index 55f463cd..8c84ec9d 100644 --- a/scripts/resolvers/index.ts +++ b/scripts/resolvers/index.ts @@ -20,6 +20,7 @@ import { generateReviewArmy } from './review-army'; import { generateDxFramework } from './dx'; import { generateGBrainContextLoad, generateGBrainSaveResults } from './gbrain'; import { generateQuestionPreferenceCheck, generateQuestionLog, generateInlineTuneFeedback } from './question-tuning'; +import { generateMakePdfSetup } from './make-pdf'; export const RESOLVERS: Record = { SLUG_EVAL: generateSlugEval, @@ -70,4 +71,5 @@ export const RESOLVERS: Record = { QUESTION_PREFERENCE_CHECK: generateQuestionPreferenceCheck, QUESTION_LOG: generateQuestionLog, INLINE_TUNE_FEEDBACK: generateInlineTuneFeedback, + MAKE_PDF_SETUP: generateMakePdfSetup, }; diff --git a/scripts/resolvers/make-pdf.ts b/scripts/resolvers/make-pdf.ts new file mode 100644 index 00000000..c73d0bf1 --- /dev/null +++ b/scripts/resolvers/make-pdf.ts @@ -0,0 +1,50 @@ +import type { TemplateContext } from './types'; + +/** + * {{MAKE_PDF_SETUP}} — emits the shell preamble that resolves $P to the + * make-pdf binary. Mirrors generateBrowseSetup / generateDesignSetup. + * + * $P = make-pdf/dist/pdf. + * + * Resolution order (matches src/browseClient.ts::resolveBrowseBin): + * 1. Local skill root: $_ROOT/{localSkillRoot}/make-pdf/dist/pdf + * 2. Global: ~/{globalRoot}/make-pdf/dist/pdf + * 3. Env override (MAKE_PDF_BIN) — for contributor dev builds + */ +export function generateMakePdfSetup(ctx: TemplateContext): string { + return `## MAKE-PDF SETUP (run this check BEFORE any make-pdf command) + +\`\`\`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/${ctx.paths.localSkillRoot}/make-pdf/dist/pdf" ] && P="$_ROOT/${ctx.paths.localSkillRoot}/make-pdf/dist/pdf" +[ -z "$P" ] && P="$HOME${ctx.paths.makePdfDir.replace(/^~/, '')}/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 +\`\`\` + +If \`MAKE_PDF_NOT_AVAILABLE\` is printed: tell the user the binary is not +built. Have them run \`./setup\` from the gstack repo, then retry. + +If \`MAKE_PDF_READY\` is printed: \`$P\` is the binary path for the rest of +the skill. Use \`$P\` (not an explicit path) so the skill body stays portable. + +Core commands: +- \`$P generate [output.pdf]\` — render markdown to PDF (80% use case) +- \`$P generate --cover --toc essay.md out.pdf\` — full publication layout +- \`$P generate --watermark DRAFT memo.md draft.pdf\` — diagonal DRAFT watermark +- \`$P preview \` — render HTML and open in browser (fast iteration) +- \`$P setup\` — verify browse + Chromium + pdftotext and run a smoke test +- \`$P --help\` — full flag reference + +Output contract: +- \`stdout\`: ONLY the output path on success. One line. +- \`stderr\`: progress (\`Rendering HTML... Generating PDF...\`) unless \`--quiet\`. +- Exit 0 success / 1 bad args / 2 render error / 3 Paged.js timeout / 4 browse unavailable.`; +} diff --git a/scripts/resolvers/types.ts b/scripts/resolvers/types.ts index 48204c91..1aa967fe 100644 --- a/scripts/resolvers/types.ts +++ b/scripts/resolvers/types.ts @@ -13,6 +13,7 @@ export interface HostPaths { binDir: string; browseDir: string; designDir: string; + makePdfDir: string; } /** @@ -30,6 +31,7 @@ function buildHostPaths(): Record { binDir: '$GSTACK_BIN', browseDir: '$GSTACK_BROWSE', designDir: '$GSTACK_DESIGN', + makePdfDir: '$GSTACK_MAKE_PDF', }; } else { const root = `~/${config.globalRoot}`; @@ -39,6 +41,7 @@ function buildHostPaths(): Record { binDir: `${root}/bin`, browseDir: `${root}/browse/dist`, designDir: `${root}/design/dist`, + makePdfDir: `${root}/make-pdf/dist`, }; } }