AI-Assisted Template Development
What This Tutorial Teaches
You'll use AI to develop a Presto template from scratch — turning Markdown into beautifully formatted PDFs. In the previous tutorial, you learned to write small Python programs with AI. This time, we'll use the same approach for something even more rewarding: developing a real, usable resume template.
- Estimated time: 60-90 minutes
- Final deliverable: A custom resume template that works with Presto
- Prerequisites: Completed AI Programming Basics, Presto installed
Ready? Let's begin.
Part 1: Understanding What a Template Is (10 minutes)
Template = A "Skin" for Your PDF
When you write a Word document, content and formatting are mixed together — you have to adjust fonts, colors, and margins yourself, bit by bit. In Presto, content and formatting are separated:
- You handle the content: Write text in Markdown (like writing plain text)
- The template handles the formatting: Decides how big headings are, which fonts to use, how much spacing to add
In other words, a template is a "skin" for your PDF. The same resume content can look completely different just by switching templates.
For a detailed explanation, see Glossary - Template
Binary Template = A Small Program
A Presto template is not a style file (unlike Word's .dotx templates) — it's a compiled small program. It works in a straightforward way:
- Takes in Markdown (the content you wrote)
- Outputs Typst typesetting instructions (telling the typesetting engine how to render the PDF)
You don't need to understand the program's internals. Just know this: you give it content, and it gives you a beautifully formatted PDF.
For a detailed explanation, see Glossary - Binary Template
Side-by-Side Comparison
The same Markdown content produces completely different PDFs when different templates are used:
This is the power of templates — write your content once, switch "skins" anytime.
Part 2: Create the Project Skeleton (5 minutes)
We'll use Presto's official scaffolding tool to generate the project in one step. Open your terminal and type:
npx create-presto-templateThe scaffolding tool will ask you a few questions through an interactive wizard. Just follow along:
? Template name (kebab-case): my-first-template
? Display name: My First Template
? Description: A simple personal resume template
? Language: Go (recommended)
? Category: Resume
? GitHub username: your-username
? License: MIT
✓ Created my-first-template/Your screen may look slightly different (the tool gets updated), but the questions are essentially the same. Enter your own GitHub username. If you don't have a GitHub account, any name will do — it won't affect local development.
Verify
ls my-first-template/You should see these files:
CLAUDE.md Makefile example.md
main.go manifest.json template_head.typSee them? The project skeleton is ready.
Part 3: Understanding the Project Structure (10 minutes)
Enter the project directory:
cd my-first-templateThe scaffolding tool generated 6 files, each with a clear purpose. Let's do a quick overview — no need to memorize the details. You'll encounter them repeatedly when developing with AI.
| File | One-Line Description |
|---|---|
main.go | The template's "brain" — reads Markdown, outputs Typst typesetting instructions |
manifest.json | The template's "ID card" — name, version, required fonts, and other metadata |
template_head.typ | Layout style definitions — page size, fonts, spacing, and other Typst settings |
example.md | Sample document — a Markdown file that demonstrates the template's output |
Makefile | Build command collection — compile, test, and preview in one step |
CLAUDE.md | AI development config — tells the AI assistant this project's rules and constraints |
For detailed descriptions of each file, see Template Developer Guide. We're just doing an overview here.
What Is CLAUDE.md?
In the previous tutorial, you chatted directly with AI. But AI didn't know your project's rules (like "no third-party libraries" or "must use goldmark to parse Markdown"). CLAUDE.md solves this — it's a configuration file placed in the project root directory that the AI assistant reads automatically every time it starts.
Think of it as a "project briefing" for AI, so it knows which rules to follow from the very beginning.
Verify
First, confirm that the project compiles and tests pass:
make build && make testYou should see a successful build and all tests passing. If you get an error, check whether Go is installed (go version).
Part 4: Develop the Template with AI (30-40 minutes)
Alright, here comes the main event. We're going to transform this default template into a personal resume template.
Target design:
- A4 portrait, with appropriate margins on all sides
- Name displayed at the top center (large, bold)
- Contact information on one line below the name (email, phone)
- Body divided into sections like "Education," "Projects," "Skills"
- Clean and professional overall, suitable for job applications
We'll use AI to do all the coding. Enter the project directory and launch Claude Code:
cd my-first-template
claudeRound 1: Describe Your Requirements
You:
I want to transform this template into a personal resume template. Requirements:
- A4 portrait page, 2cm top/bottom margins, 2.5cm left/right margins
- Name displayed centered at the top (large, bold, in a sans-serif font), with contact info centered on one line below
- Body text in a serif font, headings in a sans-serif font
- Supported sections: Education, Projects, Skills, Self-Assessment
- A thin separator line below each section heading
- Please update manifest.json, template_head.typ, example.md, and main.go
AI:
Sure, I'll transform the template into a personal resume template. I'll modify 4 files in order.
First, updating
manifest.json:json{ "name": "my-first-template", "displayName": "My First Template", "description": "A simple personal resume template", "version": "0.1.0", "author": "your-username", "license": "MIT", "category": "Resume", "keywords": ["resume", "CV", "job application"], "minPrestoVersion": "0.1.0", "requiredFonts": [], "frontmatterSchema": { "name": { "type": "string", "default": "Enter Your Name" }, "email": { "type": "string", "default": "example@email.com" }, "phone": { "type": "string", "default": "138-0000-0000" } } }Then updating
template_head.typwith page and font styles:typst#set page(paper: "a4", margin: (top: 2cm, bottom: 2cm, left: 2.5cm, right: 2.5cm)) #set text(font: "SimSun", size: 11pt, lang: "zh") #set par(leading: 0.8em) #show heading.where(level: 1): it => { align(center, text(font: "SimHei", size: 22pt, weight: "bold")[#it.body]) } #show heading.where(level: 2): it => { v(0.6em) text(font: "SimHei", size: 13pt)[#it.body] v(0.2em) line(length: 100%, stroke: 0.5pt + luma(180)) v(0.3em) }Next, updating
example.mdwith a sample resume:markdown--- name: Jane Doe email: janedoe@example.com phone: 138-0000-0000 --- ## Education **Some University** | Computer Science | 2022.09 - 2026.06 - GPA 3.8/4.0, top 10% in major - Core courses: Data Structures, Operating Systems, Computer Networks ## Projects **Campus Marketplace Platform** | Backend Developer | 2024.03 - 2024.06 - Built RESTful APIs using Go + Gin framework - Implemented user authentication, product search, and order management - Deployed on university server, 200+ daily active users ## Skills - **Languages:** Go, Python, JavaScript - **Tools & Frameworks:** Git, Docker, MySQL, Redis - **English:** TOEFL 100 ## Self-Assessment Passionate about programming with a love for learning new technologies. Experienced in teamwork and comfortable working under project deadlines.Finally, updating
main.go. The key change is handling thename,phonefields from frontmatter, rendering them as a centered header and contact line at the top:go// After outputting template_head.typ, first output the name and contact info if name, ok := frontmatter["name"]; ok { fmt.Fprintf(out, "#heading(level: 1)[%s]\n", typst.EscapeContent(name)) } contacts := []string{} if email, ok := frontmatter["email"]; ok { contacts = append(contacts, email) } if phone, ok := frontmatter["phone"]; ok { contacts = append(contacts, phone) } if len(contacts) > 0 { fmt.Fprintf(out, "#align(center)[%s]\n", typst.EscapeContent(strings.Join(contacts, " | "))) fmt.Fprintln(out, "#v(0.5em)") }I've modified all 4 files. You can run
make build && make testto verify.
This conversation demonstrates a typical AI development process: you describe requirements, and AI modifies all the relevant files. It didn't just change the code — it also updated the configuration, example, and styles. That's the efficiency of AI programming.
Warning: Common AI Mistakes:
AI might suggest installing additional packages (like
go get some-markdown-parser). But Presto templates should only use the standard library + goldmark (a Go Markdown parsing library that the scaffolding tool already configured for you). If AI suggests installing extra dependencies, tell it:"Please only use the standard library and goldmark. Don't introduce additional dependencies."
Why this restriction? Templates run in a sandbox. Extra dependencies not only increase size but can also introduce security risks. CLAUDE.md already states this rule, but AI occasionally overlooks it.
Round 2: Build, Test, and Fix
Now let's build and test:
make build && make testIf everything goes smoothly, you'll see all tests passing. But reality often isn't that smooth — build errors on the first try are par for the course. Let's look at a common fix scenario:
You:
Running make build gave me an error:
text./main.go:42:18: undefined: typst.EscapeContent
AI:
Sorry, it looks like the
typstpackage path is wrong. Let me check the project structure.Found it — in the starter project, the Typst escape function is in the
internal/typstpackage. I need to fix the import path:goimport ( "my-first-template/internal/typst" )Fixed. Please run
make buildagain.
You:
Build succeeded. But make test has one test failure:
textFAIL: example round-trip - output contains invalid Typst
AI:
This error means the Typst code output by the template has syntax issues. Let me test with the example document to see what the output looks like:
bashcat example.md | ./my-first-templateI see the problem — the heading output format is wrong; there's an extra layer of nesting. The
#show heading.where(level: 1)intemplate_head.typalready handles the level-1 heading style, but the Typst code inmain.goalso manually sets centering, causing a conflict.Fix: In
main.go, don't use#heading(level: 1)for the name. Instead, use centered text directly:goif name, ok := frontmatter["name"]; ok { fmt.Fprintf(out, "#align(center, text(font: \"SimHei\", size: 22pt, weight: \"bold\")[%s])\n", typst.EscapeContent(name)) }Also remove the
#show heading.where(level: 1)rule fromtemplate_head.typ, since the resume doesn't need a level-1 heading style anymore.Fixed. Try
make testagain.
This is the real AI development process — it's not about getting it right on the first try, but about the build - error - fix cycle. This is perfectly normal. Professional developers do the same thing every day. The key point: you don't need to understand every line of Go code yourself. You just need to show the error message to AI, and AI will analyze and fix it for you.
Warning: Common AI Mistakes:
AI might assume your computer has an amd64 architecture (Intel processor), but your Mac is arm64 (Apple Silicon). If AI gives you a build command containing
GOARCH=amd64, that's wrong — change it toGOARCH=arm64.A better approach: just use
make build. The Makefile automatically detects your system architecture, so you don't need to specify it manually.
Round 3: Preview and Iterate
After tests pass, install the template to Presto and preview the result:
make previewThis command compiles the template, installs it to Presto's template directory, then uses example.md to generate a PDF and open a preview.
See the PDF? But you might not be happy with some details. For example:
You:
The PDF looks mostly good. But a few issues:
- There's too much space between the name and contact info
- The separator lines under section headings like "Education" are too dark — can they be lighter?
- Can role info like "Backend Developer" in the projects section be shown in smaller gray text?
AI:
I'll address each of these three issues.
1. Reduce spacing between name and contact info
In
main.go, change the#v(0.5em)after the name to#v(0.2em):gofmt.Fprintln(out, "#v(0.2em)")2. Lighten the separator line color
In
template_head.typ, change the separator line'sluma(180)toluma(210)(higher values = lighter):typstline(length: 100%, stroke: 0.5pt + luma(210))3. Show role info in smaller gray text
This requires modifying the Markdown-to-Typst conversion logic in
main.goto recognize the|-separated format. When a bold text line is followed by|-separated content, render the parts after|in smaller gray text:go// Recognize "**Project Name** | Role | Date" format // Output: #strong[Project Name] #h(0.5em) #text(fill: luma(120), size: 10pt)[Role | Date]All changes made. Run
make previewto see the result.
You:
Much better! One more tweak: can there be more spacing between sections like Projects and Education? It feels too cramped right now.
AI:
In
template_head.typ, increase the spacing above level-2 headings by changingv(0.6em)tov(1.2em):typst#show heading.where(level: 2): it => { v(1.2em) text(font: "SimHei", size: 13pt)[#it.body] v(0.2em) line(length: 100%, stroke: 0.5pt + luma(210)) v(0.3em) }Run
make previewto see the result.
This "preview - feedback - tweak" cycle is the norm in template development. You don't need to know Typst syntax — just look at the PDF with your eyes and tell AI what's wrong with your keyboard.
Verify
Keep running make preview until you're satisfied. The final resume PDF should have:
- [ ] Centered name at the top (large, bold)
- [ ] Contact information below the name (email, phone)
- [ ] Clear section separation (Education, Projects, Skills, Self-Assessment)
- [ ] Light separator line under each section heading
- [ ] Clean, professional overall layout
Part 5: Testing and Publishing (10 minutes)
Run the Full Test Suite
make testmake test automatically checks the following:
- Whether
manifest.jsonis valid JSON - Whether
example.mdcan be correctly converted by the template (round-trip test) - Whether
--version,--manifest, and--exampleparameters work correctly - Whether the
categoryfield is valid - Security checks (no banned packages, no network access, etc.)
Passing all tests means your template meets Presto's quality standards.
Multi-Platform Build (Optional)
If you want to share your template with others, you'll need to build for multiple platforms:
make build-all # Build for 6 platforms (darwin/linux/windows x arm64/amd64)But for local development, you don't need this — make build builds for the current platform only.
Publish
When you're ready to share your template, you can publish it via GitHub Release. For the complete publishing workflow (including tagging, CI auto-build, etc.), see Release Process Guide.
Verify
make testAll tests passing means you're good.
Part 6: Review What You Learned
Congratulations on developing your first Presto template! Let's review the key takeaways from this process.
1. CLAUDE.md Helps AI Understand Project Context
Did you notice that AI knew from the start that it should "only use the standard library and goldmark" and that "the output format is Typst"? That's because CLAUDE.md told it in advance. For any future project, you can write a CLAUDE.md to help AI better understand the context.
2. Incremental Development: Get It Working First, Then Refine
We didn't try to achieve perfect layout from the start. Instead, we first made the template compile, pass tests, and generate a basic PDF, then gradually refined the details. This "get it working first, then refine" approach is especially effective in AI-assisted development — because every step has a clear verification point.
3. AI Makes Mistakes, but You Can Identify and Correct Them
During development, we encountered at least two types of AI mistakes:
- Suggesting unnecessary dependencies (should only use standard library + goldmark)
- Assuming amd64 architecture (your Mac is arm64)
These mistakes aren't scary. What matters is that you know the project's rules and can correct AI when it makes mistakes. As you gain experience, you'll get better and better at spotting these issues.
4. Build - Error - Fix Is the Normal Workflow
Getting a successful build on the first try is the exception, not the rule. Copying error messages to AI and having it analyze and fix them is something professional developers do every day — it's not a sign of "incompetence."
To learn more about AI-assisted development patterns and techniques, read AI Workflow Patterns.
Next Steps
Congratulations on developing your first Presto template! Starting from an empty project, you used AI to complete requirements analysis, code modification, build debugging, and layout fine-tuning — this is the real template development workflow.
Want to go deeper? Here are some directions:
- Deep dive into template development: Template Developer Guide contains full technical details, including the binary protocol, Typst syntax cheat sheet, pattern recipes, and more
- Learn the publishing process: Release Process Guide teaches you how to share your template with others
You've mastered the basics of developing Presto templates with AI. Next, try using the same approach to develop a template you actually need — maybe for a lab report, course paper, or club event poster. Practice is the best way to learn.
