Create and edit Confluence wiki pages on wiki.cfdata.org. Use this skill whenever the user asks to create a wiki page, build a dashboard, make a Confluence page, set up wiki documentation with macros (Jira issues, panels, status lozenges, tables, charts, roadmaps, table filters), edit existing pages, or replicate/copy existing wiki pages. Also use when the user mentions wiki.cfdata.org, Confluence, or wants to revamp/redesign a wiki page.
Resources
3Install
npx skillscat add tianaegidi/wiki-builder Install via the SkillsCat registry.
Wiki Builder
Create editable, dynamic Confluence wiki pages on wiki.cfdata.org using the Confluence REST API via the wikigen-generic.sh script.
Prerequisites
Before creating any page, verify these are in place:
- Script: The user must have
wikigen-generic.shin their home directory and it must be executable - Cloudflared token: Must have a valid token in
~/.cloudflared/forwiki.cfdata.org. If missing, run:cloudflared access login https://wiki.cfdata.org/ - Jira app link ID:
cc100dec-3d79-305b-8fae-4caba5e44cd2(used asserverIdparameter)
Critical Rule: Use Storage Format, NOT Rendered HTML
ALWAYS use Confluence Storage Format (XHTML) for page content. Never use rendered HTML with JavaScript, DOM IDs, or plugin-generated markup. Rendered HTML blocks the Confluence editor and makes pages uneditable.
Storage format uses <ac:structured-macro> tags to declare macros. Confluence renders them at view time. This keeps the editor clean and functional.
Content File Structure
Every page content file must start with:
<ac:confluence xmlns:ac="https://jira.atlassian.com/wiki" xmlns:ri="http://atlassian.com/resource/identifier">And end with </ac:confluence>.
Do NOT include <?xml?> or <!DOCTYPE> declarations -- the REST API rejects them.
Macro Reference
Jira Issues Count Macro (CRITICAL - exact format required)
The Jira macro requires ALL of these parameters and attributes to work correctly:
<ac:structured-macro ac:name="jira" ac:schema-version="1" ac:macro-id="UNIQUE-UUID-HERE">
<ac:parameter ac:name="server">Cloudflare Jira</ac:parameter>
<ac:parameter ac:name="serverId">cc100dec-3d79-305b-8fae-4caba5e44cd2</ac:parameter>
<ac:parameter ac:name="jqlQuery">labels = ThreatFocusedControl AND status = Done</ac:parameter>
<ac:parameter ac:name="count">true</ac:parameter>
</ac:structured-macro>Key details:
- Parameter name is
jqlQuery(NOTjql) serveris the display nameCloudflare JiraserverIdis the app link IDcc100dec-3d79-305b-8fae-4caba5e44cd2- Each macro needs
ac:schema-version="1"and a uniqueac:macro-id(UUID) - Generate a fresh UUID for each Jira macro instance
Panel Macro
<ac:structured-macro ac:name="panel">
<ac:parameter ac:name="borderColor">#1c5e98</ac:parameter>
<ac:parameter ac:name="borderWidth">2</ac:parameter>
<ac:parameter ac:name="title">Panel Title</ac:parameter>
<ac:parameter ac:name="titleBGColor">#1c5e98</ac:parameter>
<ac:parameter ac:name="titleColor">#FFFFFF</ac:parameter>
<ac:rich-text-body>
<p>Panel content here</p>
</ac:rich-text-body>
</ac:structured-macro>For panels without a header, omit the title, titleBGColor, and titleColor parameters.
Status Lozange
<ac:structured-macro ac:name="status">
<ac:parameter ac:name="colour">Green</ac:parameter>
<ac:parameter ac:name="title">ON TRACK</ac:parameter>
</ac:structured-macro>Colours: Green, Blue, Yellow, Red, Grey
Info Macro
<ac:structured-macro ac:name="info">
<ac:parameter ac:name="title">Info Title</ac:parameter>
<ac:rich-text-body><p>Info content</p></ac:rich-text-body>
</ac:structured-macro>UI Button
<ac:structured-macro ac:name="ui-button" ac:schema-version="1" ac:macro-id="UNIQUE-UUID">
<ac:parameter ac:name="color">blue</ac:parameter>
<ac:parameter ac:name="title">Button Title</ac:parameter>
<ac:parameter ac:name="url">https://wiki.cfdata.org/...</ac:parameter>
</ac:structured-macro>Note: color is blue (not rgb(...)), parameter is url (not link), no textColor parameter.
UI Tabs (CRITICAL - exact format required)
The tab parameter is title (NOT name), and both ui-tabs and ui-tab need ac:schema-version and ac:macro-id:
<ac:structured-macro ac:name="ui-tabs" ac:schema-version="1" ac:macro-id="UNIQUE-UUID-1">
<ac:rich-text-body>
<ac:structured-macro ac:name="ui-tab" ac:schema-version="1" ac:macro-id="UNIQUE-UUID-2">
<ac:parameter ac:name="title">Tab Name</ac:parameter>
<ac:rich-text-body>
<p>Tab content</p>
</ac:rich-text-body>
</ac:structured-macro>
</ac:rich-text-body>
</ac:structured-macro>Style Macro (CSS)
<ac:structured-macro ac:name="style">
<ac:plain-text-body><![CDATA[
#grad1{ background: rgb(28,94,152); background: linear-gradient(145deg, rgba(28,94,152) 0%, rgba(32,109,177) 50%, rgba(40,133,215) 100%); box-sizing: border-box; padding: 1em;}
]]></ac:plain-text-body>
</ac:structured-macro>Section/Column Layout
<ac:structured-macro ac:name="section">
<ac:rich-text-body>
<ac:structured-macro ac:name="column">
<ac:rich-text-body>
<p>Column content</p>
</ac:rich-text-body>
</ac:structured-macro>
</ac:rich-text-body>
</ac:structured-macro>Expand Macro (Collapsible Section)
<ac:structured-macro ac:name="expand" ac:schema-version="1" ac:macro-id="UNIQUE-UUID">
<ac:parameter ac:name="title">Click to Expand</ac:parameter>
<ac:rich-text-body>
<p>Collapsible content here</p>
</ac:rich-text-body>
</ac:structured-macro>Table Filter (Stiltsoft)
<ac:structured-macro ac:name="table-filter">
<ac:rich-text-body>
<table><tbody>
<tr><th>Col1</th><th>Col2</th></tr>
<tr><td>Data</td><td>Data</td></tr>
</tbody></table>
</ac:rich-text-body>
</ac:structured-macro>Roadmap Planner (Stiltsoft)
<ac:structured-macro ac:name="roadmap">
<ac:parameter ac:name="source">manual</ac:parameter>
<ac:parameter ac:name="title">Roadmap Title</ac:parameter>
<ac:parameter ac:name="timeframe">month</ac:parameter>
<ac:parameter ac:name="startDate">2025-11-01</ac:parameter>
<ac:parameter ac:name="endDate">2026-10-31</ac:parameter>
</ac:structured-macro>UI/UX Design Patterns
When designing wiki pages, think like a UI/UX designer. Use these proven patterns:
Pattern 1: Landing Page with Tabs
- Gradient header banner (style macro + div)
- Welcome panel with program description
- Info panel inside welcome for context/status
- UI button linking to FAQ or key resource
- "How to read this page" panel explaining tab navigation
- Team/Stakeholders table
- UI tabs organizing content by topic
Pattern 2: Program Health Dashboard
- 2x2 colored panel grid with Jira count macros
- Green = completed/on track, Blue = in progress, Yellow = needs attention, Red = blocked/action needed
- Status lozenges under each count
- Reporting table with per-threat-model Jira counts
Pattern 3: Handbook / Reference Tab
- "How to Use This Handbook" intro panel
- Roles & Definitions table (Role, Definition, Responsibility)
- Threat models list with descriptions
- Step-by-step workflow guides in numbered lists
- Label reference table
- Escalation paths table (Situation, Action, Channel/Contact)
- Definition of Done panel with status lozenge
- Weekly cadence table
Pattern 4: Workstreams / Tickets
- 3-column summary cards (Backlog, In Progress, Completed) with Jira counts
- Table Filter macro for open tickets
- Color-coded panel borders matching status
Pattern 5: Timeline & Milestones
- Roadmap Planner macro
- Milestones table with status lozenges
- Expand macro for collapsible milestone lists
Creating a Page
Use the script with these flags:
-s SPACE_KEY-- Space key (e.g., INFOSEC)-t "Page Title"-- Title-i PARENT_PAGE_ID-- Parent page ID (optional)-c content.html-- Content file path
~/wikigen-generic.sh -s INFOSEC -t "My Page" -i 1424133824 -c ~/test-page-content.htmlUpdating an Existing Page
To edit a specific section of an existing page:
- Fetch the current page storage format and version:
CF_TOKEN=$(cloudflared access login https://wiki.cfdata.org/ 2>&1 | grep -o 'eyJ[a-zA-Z0-9\._-]*' | head -1)
curl -s -k -H "cf-access-token: $CF_TOKEN" "https://wiki.cfdata.org/rest/api/content/PAGE_ID?expand=body.storage,version" > /tmp/page.json- Find the exact section boundaries to replace:
old_start = body.find('Section Title</ac:parameter>')
next_section_start = body.find('NextSection</ac:parameter>', old_start)
region = body[old_start:next_section_start]
first_close = region.find('</ac:rich-text-body></ac:structured-macro>')
old_end = old_start + first_close + len('</ac:rich-text-body></ac:structured-macro>')Build replacement content in storage format XHTML
Replace and update via curl PUT:
curl -s -k -X PUT \
-H "Content-Type: application/json" \
-H "cf-access-token: $CF_TOKEN" \
-H "X-Atlassian-Token: no-check" \
-d @/ttp/wiki-update-payload.json \
"https://wiki.cfdata.org/rest/api/content/PAGE_ID"IMPORTANT: Always fetch the version right before updating — pages can be edited by others between fetches.
Workflow for Building a Page
- Understand what the user wants -- what sections, macros, data sources
- If replicating an existing page, fetch it first via the REST API to extract the storage format and JQL queries
- Design the UI -- choose the right pattern (landing page, dashboard, handbook, etc.)
- Build the content file as Confluence Storage Format XHTML
- Validate XML balance -- count open/close tags for structured-macro, rich-text-body
- Run the script to create the page
- Share the URL with the user
- Ask the user to test editing -- can they click Edit and modify content?
When Replicating an Existing Page
Fetch the source page's storage format to get exact macro parameters:
CF_TOKEN=$(cloudflared access login https://wiki.cfdata.org/ 2>&1 | grep -o 'eyJ[a-zA-Z0-9\._-]*' | head -1)
curl -s -k -H "cf-access-token: $CF_TOKEN" "https://wiki.cfdata.org/rest/api/content/PAGE_ID?expand=body.storage"Extract JQL queries from the storage format:
import re
jqls = re.findall(r'ac:name="jqlQuery">([^<]+)<', body)Key Lessons Learned
- Storage format = editable. Rendered HTML = locked editor.
- Jira macros: parameter is
jqlQuery(notjql), need bothserver(display name) andserverId(app link ID), plusac:schema-versionandac:macro-id - UI tabs: parameter is
title(notname), needac:schema-versionandac:macro-idon bothui-tabsand eachui-tab - UI button:
colorisblue(not rgb),url(notlink), notextColor, needsac:schema-versionandac:macro-id - Stiltsoft macros (table-filter, roadmap, table-chart) work when declared in storage format without pre-generated session IDs
- No labels on new pages unless the user specifically asks for them
- Always validate XML tag balance before creating -- mismatched tags cause API errors
- Remove
<?xml?>and<!DOCTYPE>from content files before sending to API - To update a page, fetch current version, find exact section boundaries, replace, PUT with incremented version
- When replacing sections, use
find()for the first closing tag after the section start, notrfind()which can match parent elements