add-pdf-report
Internal implementation skill invoked by /add-native for app-generated PDF report workflows using expo-print and, when present, expo-sharing.
Install / Use
npx skills add microsoft/power-platform-skills --skill add-pdf-reportInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
AutomationSupported Platforms
Our assessment of add-pdf-report
add-pdf-report scores 85/100 on our quality scale, 1963rd of 2,892 Automation skills we index.
Its SKILL.md is 10 KB long, well organised into 11 sections with 8 code examples: a thorough specification that gives an agent plenty to work with.
It has 919 GitHub stars, a meaningful sign that others use it.
Maintenance, license and trust
- The repository was last updated 12 days ago, so add-pdf-report is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
add-pdf-report compared with similar skills
All 4 of these similar skills score higher than add-pdf-report; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| add-pdf-report (this skill)by microsoft | 85 | 919 | 12d ago | SKILL.md |
| Agent-Reachby Panniantong | 100 | 92.4k | 21d ago | CLAUDE.md |
| Scraplingby D4Vinci | 100 | 85.9k | today | MCP Server |
| rufloby ruvnet | 100 | 74.0k | today | MCP Server |
| algorithmic-artby anthropics | 100 | 177.9k | 14d ago | SKILL.md |
Frequently asked questions
- How do I install add-pdf-report?
- Run
npx skills add microsoft/power-platform-skills --skill add-pdf-report. The install tabs above show the steps for each supported agent. - Which AI agents does add-pdf-report work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is add-pdf-report safe to use?
- It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is add-pdf-report still maintained?
- The repository was last updated 12 days ago, so add-pdf-report is actively maintained.
Skill content
View source on GitHubname: add-pdf-report description: Internal implementation skill invoked by /add-native for app-generated PDF report workflows using expo-print and, when present, expo-sharing. user-invocable: false disable-model-invocation: true allowed-tools: Read, Edit, Write, Grep, Glob, Bash, AskUserQuestion model: sonnet
Shared instructions: shared-instructions.md - read first.
Add PDF Report
Internal helper. Users should invoke /add-native pdf-report, /add-native generate-pdf, or /add-native pdf-export; /add-native routes here after resolving the capability.
Generate or verify a local PDF report wrapper for app-owned PDFs created from records, evidence, certificates, receipts, or summaries. This helper uses expo-print to create a local PDF file URI. It may use expo-sharing only when that package is already present. It never installs packages or imports the native PDF viewer directly.
Capability boundaries
| User need | Correct path |
|---|---|
| Generate/export/print a report from app data | This helper: expo-print -> local PDF URI |
| Share the generated local PDF from the device | Add share method only if expo-sharing is already in package.json |
| Retain the generated PDF in Dataverse | Create/update parent row first, then upload to a Dataverse File column with generated services |
| Open an existing HTTPS or local file PDF in the Power Apps native viewer | /add-native pdf-viewer, only if @microsoft/power-apps-native-pdf-viewer 0.2.9+ is already present |
| Pick/import/upload a user-selected PDF | /add-native document-picker or host <FilePicker> for Dataverse File columns |
Local generated PDFs are usually file:// URIs and can be passed to openHttpsPdf(...) with @microsoft/power-apps-native-pdf-viewer 0.2.9+.
Steps
1. Verify app
test -f app.config.js && test -f power.config.json && test -f package.json && test -d src
If this fails, tell the user to run /create-mobile-app first and STOP.
2. Verify packages are already present
expo-print is required. expo-sharing is optional unless the plan specifically needs sharing behavior.
node -e "const p=require('./package.json'); const deps={...p.dependencies,...p.devDependencies}; const required='expo-print'; if (!deps[required]) { console.error('MISSING: expo-print is not in package.json. The template/app must already ship it for /add-native pdf-report. This skill will not install it or edit native config. Capability not added.'); process.exit(1); } console.log('OK: expo-print package present'); console.log(deps['expo-sharing'] ? 'OK: expo-sharing package present' : 'OPTIONAL_MISSING: expo-sharing is not in package.json; generated PDFs can be created/viewed/uploaded, but sharing helpers must not be generated.');"
If expo-print is missing, STOP. Do not run npm install, npx expo install, pod install, or edit app.config.js. Do not add pdf-report to the plan or generated wrappers for this app.
If expo-sharing is missing:
- Continue for generate-only, native-viewer preview, or Dataverse-upload flows.
- Do not import
expo-sharing. - Do not generate
sharePdfReport(...). - If the user's requirement specifically includes sharing, STOP and say sharing is not supported by this template.
3. Write or verify src/native/pdfReport.ts
Create src/native/pdfReport.ts if it does not exist. If it already exists, inspect it and patch only if it throws instead of returning a result, imports missing packages, or routes local URIs to the native PDF viewer.
The wrapper MUST:
- Import
expo-printonly after Step 2 confirms it is present. - Import
expo-sharingonly when Step 2 confirms it is present. - Return discriminated unions and never throw.
- Treat generated local PDFs as local files for view/share/upload flows.
- Never import
@microsoft/power-apps-native-pdf-viewerdirectly from this wrapper. - Keep HTML generation deterministic and app-owned; do not fetch remote HTML inside the wrapper.
Base wrapper when expo-sharing is present:
// src/native/pdfReport.ts
import * as Print from 'expo-print';
import * as Sharing from 'expo-sharing';
export type PdfReportResult =
| { ok: true; uri: string; numberOfPages?: number; base64?: string }
| { ok: false; reason: 'EMPTY_HTML' | 'PRINT_FAILED'; message?: string };
export type PdfShareResult =
| { ok: true }
| { ok: false; reason: 'INVALID_URI' | 'SHARING_UNAVAILABLE' | 'SHARE_FAILED'; message?: string };
export function escapePdfHtml(value: string): string {
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
export function wrapPdfDocument(input: { title: string; bodyHtml: string; styles?: string }): string {
const title = escapePdfHtml(input.title.trim() || 'Report');
return `<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>${title}</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 32px; color: #111827; }
h1, h2, h3 { margin: 0 0 12px; }
table { width: 100%; border-collapse: collapse; }
th, td { border-bottom: 1px solid #e5e7eb; padding: 8px; text-align: left; }
${input.styles ?? ''}
</style>
</head>
<body>${input.bodyHtml}</body>
</html>`;
}
export async function createPdfReport(
html: string,
options?: { includeBase64?: boolean },
): Promise<PdfReportResult> {
if (!html.trim()) {
return { ok: false, reason: 'EMPTY_HTML', message: 'PDF report HTML is empty.' };
}
try {
const result = await Print.printToFileAsync({
html,
base64: options?.includeBase64 ?? false,
});
return {
ok: true,
uri: result.uri,
numberOfPages: result.numberOfPages,
base64: result.base64,
};
} catch (error: any) {
return { ok: false, reason: 'PRINT_FAILED', message: error?.message ?? String(error) };
}
}
export async function sharePdfReport(uri: string, options?: { dialogTitle?: string }): Promise<PdfShareResult> {
if (!uri || !uri.startsWith('file://')) {
return { ok: false, reason: 'INVALID_URI', message: 'Generated PDF reports must be shared from a local file URI.' };
}
try {
const available = await Sharing.isAvailableAsync();
if (!available) {
return { ok: false, reason: 'SHARING_UNAVAILABLE', message: 'Sharing is not available on this platform.' };
}
await Sharing.shareAsync(uri, {
mimeType: 'application/pdf',
UTI: 'com.adobe.pdf',
dialogTitle: options?.dialogTitle ?? 'Share PDF report',
});
return { ok: true };
} catch (error: any) {
return { ok: false, reason: 'SHARE_FAILED', message: error?.message ?? String(error) };
}
}
When expo-sharing is absent, generate the same file without the expo-sharing import, without PdfShareResult, and without sharePdfReport(...). Keep createPdfReport(...), wrapPdfDocument(...), and escapePdfHtml(...).
4. Use the wrapper
Screens import the wrapper, not Expo modules directly:
import { createPdfReport, escapePdfHtml, sharePdfReport, wrapPdfDocument } from '@/native/pdfReport';
const html = wrapPdfDocument({
title: 'Inspection report',
bodyHtml: `<h1>Inspection report</h1><p>${escapePdfHtml(summary)}</p>`,
});
const report = await createPdfReport(html);
if (!report.ok) {
showError(report.message ?? 'Report PDF was not generated.');
return;
}
const share = await sharePdfReport(report.uri, { dialogTitle: 'Share inspection report' });
if (!share.ok) {
showError(share.message ?? 'Report PDF was generated but could not be shared.');
}
If expo-sharing is absent, screens may still call createPdfReport(...), preview the returned file:// URI through native PDF viewer 0.2.9+, or upload it to a Dataverse File column through generated services. They must not render a Share button.
5. Optional Dataverse upload
Retained PDFs use Dataverse File columns. Save or update the parent row first, verify success, then upload a payload compatible with the generated service's upload(id, columnName, file, fileDisplayName?) signature. Never put File column bytes in create/update JSON.
const save = await Cr123_inspectionService.update(inspectionId, {
cr123_reportgeneratedat: new Date().toISOString(),
});
if (!save.success) {
showError(save.error?.message ?? 'Inspection was not saved.');
return;
}
const report = await createPdfReport(html, { includeBase64: true });
if (!report.ok) {
showError(report.message ?? 'Report PDF was not generated.');
return;
}
if (!report.base64) {
showError('Report PDF was generated but could not be prepared for upload.');
return;
}
// Convert report.base64 into the File/blob/picked-file shape expected by the generated service.
// Do not pass report.uri unless the generated service explicitly documents URI support.
const reportFile = createUploadFileFromBase64(report.base64, 'inspection-report.pdf', 'application/pdf');
const upload = await Cr123_inspectionService.upload(
inspectionId,
'cr123_reportfile',
reportFile,
'inspection-report.pdf',
);
if (!upload.success) {
showError(upload.error?.message ?? 'Report PDF was not uploaded.');
}
createUploadFileFromBase64(...) is intentionally app-specific because generated upload helpers may expect a browser File, a host PickedFileInfo, bytes, or another service-specific payload shape. Use the model/service types generated for that table and do not edit generated services.
6. Type-check
npx tsc --noEmit
Fix any TypeScript errors before rebuilding.
7. Native rebuild note
This skill does not install native code. If expo-print or expo-sharing was just added outside the skill, the app needs a native rebuild outside this workflow. If the packages were already in the build, Metro hot reload is enough for wrapper edits.
8. Summary
Tell the user:
PDF report helper added
Required package : expo-print
Optional share : expo-sharing <present | absent>
Wrapper : src/native/pdfReport.ts
Output : local PDF file URI
Native viewer : optional; 0.2.9+ can open the generated file:// URI
Type-check : PASS
Native rebuild : not performed by this skill
Update memory-bank.md under Controls:
- PDF report helper added - expo-print local generation, expo-sharing <present|absent> (<ISO date>)
Related Skills
Agent-Reach
92.4kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
Scrapling
85.9k🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
ruflo
74.0k🌊 The original agent harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, federation, vector RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
