ima-claude 2.9.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,286 @@
1
+ ---
2
+ name: gh-cli
3
+ description: >-
4
+ GitHub CLI (gh) for pull requests, issues, releases, Actions, code review, and repo
5
+ management. Primary tool for GitHub operations — reliable, fast, always available.
6
+ Use when: creating PRs, reviewing PRs, managing issues, checking CI status, creating
7
+ releases, searching GitHub, or any github.com operation. Triggers on: GitHub, gh,
8
+ pull request, PR, issue, release, actions, workflow, CI status, code review, merge PR.
9
+ NOT for Gitea — use mcp-gitea for internal repos.
10
+ ---
11
+
12
+ # GitHub CLI (`gh`) — GitHub Operations
13
+
14
+ The `gh` CLI is the primary tool for all GitHub operations. It's authenticated, reliable, and covers the full GitHub API surface.
15
+
16
+ ## Prerequisites
17
+
18
+ ```bash
19
+ # Verify authentication
20
+ gh auth status
21
+ ```
22
+
23
+ The CLI must be authenticated (`gh auth login`) before use. Verify scopes with `gh auth status`.
24
+
25
+ ## Command Reference
26
+
27
+ ### Pull Requests
28
+
29
+ | Operation | Command |
30
+ |-----------|---------|
31
+ | Create PR | `gh pr create --title "..." --body "..." --base main` |
32
+ | Create PR (fill from commits) | `gh pr create --fill` |
33
+ | Create draft PR | `gh pr create --draft --title "..." --body "..."` |
34
+ | List open PRs | `gh pr list` |
35
+ | List PRs by state | `gh pr list --state closed` |
36
+ | List PRs by author | `gh pr list --author "@me"` |
37
+ | List PRs by label | `gh pr list --label "bug"` |
38
+ | View PR details | `gh pr view 123` |
39
+ | View PR in browser | `gh pr view 123 --web` |
40
+ | View PR diff | `gh pr diff 123` |
41
+ | Check CI status | `gh pr checks 123` |
42
+ | Checkout PR locally | `gh pr checkout 123` |
43
+ | Merge PR (squash) | `gh pr merge 123 --squash` |
44
+ | Merge PR (rebase) | `gh pr merge 123 --rebase` |
45
+ | Merge PR (merge commit) | `gh pr merge 123 --merge` |
46
+ | Merge + delete branch | `gh pr merge 123 --squash --delete-branch` |
47
+ | Close PR | `gh pr close 123` |
48
+ | Reopen PR | `gh pr reopen 123` |
49
+ | Edit PR | `gh pr edit 123 --title "..." --body "..."` |
50
+ | Add reviewer | `gh pr edit 123 --add-reviewer username` |
51
+ | Add label | `gh pr edit 123 --add-label "bug"` |
52
+ | Mark as ready | `gh pr ready 123` |
53
+ | Comment on PR | `gh pr comment 123 --body "..."` |
54
+ | Review PR (approve) | `gh pr review 123 --approve --body "LGTM"` |
55
+ | Review PR (request changes) | `gh pr review 123 --request-changes --body "..."` |
56
+ | Review PR (comment only) | `gh pr review 123 --comment --body "..."` |
57
+ | My PR status | `gh pr status` |
58
+
59
+ ### Issues
60
+
61
+ | Operation | Command |
62
+ |-----------|---------|
63
+ | Create issue | `gh issue create --title "..." --body "..."` |
64
+ | Create issue with labels | `gh issue create --title "..." --label "bug" --label "priority"` |
65
+ | Create issue with assignee | `gh issue create --title "..." --assignee "@me"` |
66
+ | List open issues | `gh issue list` |
67
+ | List by state | `gh issue list --state closed` |
68
+ | List by label | `gh issue list --label "bug"` |
69
+ | List by assignee | `gh issue list --assignee "@me"` |
70
+ | View issue | `gh issue view 42` |
71
+ | View in browser | `gh issue view 42 --web` |
72
+ | Close issue | `gh issue close 42` |
73
+ | Close with comment | `gh issue close 42 --comment "Fixed in v2.11.0"` |
74
+ | Reopen issue | `gh issue reopen 42` |
75
+ | Comment on issue | `gh issue comment 42 --body "..."` |
76
+ | Edit issue | `gh issue edit 42 --title "..." --body "..."` |
77
+ | Add label | `gh issue edit 42 --add-label "in-progress"` |
78
+ | Remove label | `gh issue edit 42 --remove-label "triage"` |
79
+ | Assign | `gh issue edit 42 --add-assignee username` |
80
+ | Pin issue | `gh issue pin 42` |
81
+ | Transfer issue | `gh issue transfer 42 owner/other-repo` |
82
+ | My issue status | `gh issue status` |
83
+
84
+ ### Repositories
85
+
86
+ | Operation | Command |
87
+ |-----------|---------|
88
+ | View repo | `gh repo view owner/name` |
89
+ | View in browser | `gh repo view --web` |
90
+ | Clone repo | `gh repo clone owner/name` |
91
+ | Fork repo | `gh repo fork owner/name` |
92
+ | Create repo | `gh repo create name --public --description "..."` |
93
+ | Create private repo | `gh repo create name --private` |
94
+ | List my repos | `gh repo list` |
95
+ | List org repos | `gh repo list org-name` |
96
+ | Sync fork | `gh repo sync owner/name` |
97
+ | Set default repo | `gh repo set-default owner/name` |
98
+
99
+ ### Releases & Tags
100
+
101
+ | Operation | Command |
102
+ |-----------|---------|
103
+ | Create release | `gh release create v1.0.0 --title "v1.0.0" --notes "..."` |
104
+ | Create draft release | `gh release create v1.0.0 --draft --title "v1.0.0" --notes "..."` |
105
+ | Create release from tag | `gh release create v1.0.0 --generate-notes` |
106
+ | Upload assets | `gh release upload v1.0.0 ./dist/*.tar.gz` |
107
+ | List releases | `gh release list` |
108
+ | View release | `gh release view v1.0.0` |
109
+ | Download assets | `gh release download v1.0.0` |
110
+ | Delete release | `gh release delete v1.0.0` |
111
+ | Edit release | `gh release edit v1.0.0 --title "..." --notes "..."` |
112
+
113
+ ### GitHub Actions (Workflows & Runs)
114
+
115
+ | Operation | Command |
116
+ |-----------|---------|
117
+ | List workflows | `gh workflow list` |
118
+ | View workflow | `gh workflow view workflow-name` |
119
+ | Run workflow | `gh workflow run workflow-name` |
120
+ | Run with inputs | `gh workflow run workflow-name -f key=value` |
121
+ | Disable workflow | `gh workflow disable workflow-name` |
122
+ | Enable workflow | `gh workflow enable workflow-name` |
123
+ | List recent runs | `gh run list` |
124
+ | List runs for workflow | `gh run list --workflow workflow-name` |
125
+ | View run details | `gh run view 12345` |
126
+ | View run logs | `gh run view 12345 --log` |
127
+ | Watch run progress | `gh run watch 12345` |
128
+ | Download artifacts | `gh run download 12345` |
129
+ | Rerun failed jobs | `gh run rerun 12345 --failed` |
130
+ | Cancel run | `gh run cancel 12345` |
131
+
132
+ ### Search (Cross-Repository)
133
+
134
+ | Operation | Command |
135
+ |-----------|---------|
136
+ | Search repos | `gh search repos "query" --sort stars` |
137
+ | Search issues | `gh search issues "query" --repo owner/name` |
138
+ | Search PRs | `gh search prs "query" --state open` |
139
+ | Search code | `gh search code "pattern" --repo owner/name` |
140
+ | Search commits | `gh search commits "query" --repo owner/name` |
141
+
142
+ ### Labels
143
+
144
+ | Operation | Command |
145
+ |-----------|---------|
146
+ | List labels | `gh label list` |
147
+ | Create label | `gh label create "name" --color "0075ca" --description "..."` |
148
+ | Edit label | `gh label edit "name" --new-name "..." --color "..."` |
149
+ | Delete label | `gh label delete "name" --yes` |
150
+ | Clone labels to another repo | `gh label clone source-owner/source-repo` |
151
+
152
+ ### Raw API Access
153
+
154
+ For operations not covered by built-in commands:
155
+
156
+ ```bash
157
+ # GET request
158
+ gh api repos/{owner}/{repo}/topics
159
+
160
+ # POST request
161
+ gh api repos/{owner}/{repo}/labels -f name="priority" -f color="ff0000"
162
+
163
+ # With pagination
164
+ gh api repos/{owner}/{repo}/issues --paginate
165
+
166
+ # GraphQL
167
+ gh api graphql -f query='{ viewer { login } }'
168
+
169
+ # JSON output + jq filtering
170
+ gh api repos/{owner}/{repo}/pulls --jq '.[].title'
171
+ ```
172
+
173
+ ## Decision Logic
174
+
175
+ ```
176
+ Is this a GitHub-hosted repo?
177
+ Check: git remote -v → shows github.com
178
+ → Yes: Use gh CLI (this skill)
179
+ → No: Is it Gitea-hosted?
180
+ → Yes: Use mcp-gitea tools (see mcp-gitea skill)
181
+ → Unknown: Ask the user
182
+
183
+ For local-only git operations (commit, diff, log, stash, rebase):
184
+ → Always use git CLI directly — gh is for GitHub API operations
185
+
186
+ Operation routing:
187
+ PRs (create, review, merge, list) → gh pr ...
188
+ Issues (create, comment, close) → gh issue ...
189
+ Releases (create, upload, list) → gh release ...
190
+ CI/CD status, rerun, logs → gh run ... / gh workflow ...
191
+ Search across GitHub → gh search ...
192
+ Anything not in built-in commands → gh api ...
193
+ ```
194
+
195
+ ## Common Workflows
196
+
197
+ ### Create a PR with Body via HEREDOC
198
+
199
+ ```bash
200
+ gh pr create --title "feat: add gh-cli skill" --body "$(cat <<'EOF'
201
+ ## Summary
202
+ - Added gh-cli skill for GitHub CLI operations
203
+ - Replaces unreliable MCP GitHub integration
204
+
205
+ ## Test plan
206
+ - [ ] Verify gh auth status
207
+ - [ ] Test PR creation workflow
208
+ EOF
209
+ )"
210
+ ```
211
+
212
+ ### Check CI and Merge When Ready
213
+
214
+ ```bash
215
+ # Check CI status
216
+ gh pr checks 123
217
+
218
+ # If all checks pass, merge
219
+ gh pr merge 123 --squash --delete-branch
220
+ ```
221
+
222
+ ### Create a Release with Changelog
223
+
224
+ ```bash
225
+ gh release create v2.12.0 \
226
+ --title "v2.12.0 — Add gh-cli skill" \
227
+ --generate-notes \
228
+ --latest
229
+ ```
230
+
231
+ ### Cross-Repo Issue Search
232
+
233
+ ```bash
234
+ # Find all open bugs assigned to me across repos
235
+ gh search issues "is:open assignee:@me label:bug" --limit 20
236
+
237
+ # Find PRs awaiting my review
238
+ gh search prs "is:open review-requested:@me" --limit 20
239
+ ```
240
+
241
+ ### View PR Comments (API)
242
+
243
+ ```bash
244
+ # List PR review comments
245
+ gh api repos/{owner}/{repo}/pulls/123/comments --jq '.[].body'
246
+
247
+ # List issue/PR timeline comments
248
+ gh api repos/{owner}/{repo}/issues/123/comments --jq '.[] | "\(.user.login): \(.body)"'
249
+ ```
250
+
251
+ ## Output Formatting
252
+
253
+ The `gh` CLI supports structured output:
254
+
255
+ ```bash
256
+ # JSON output
257
+ gh pr list --json number,title,state
258
+
259
+ # JSON + jq filter
260
+ gh pr list --json number,title --jq '.[] | "\(.number): \(.title)"'
261
+
262
+ # Table format (default for list commands)
263
+ gh issue list
264
+
265
+ # Web browser
266
+ gh pr view 123 --web
267
+ ```
268
+
269
+ ## Cross-Repository Operations
270
+
271
+ Use `--repo` or `-R` to target any GitHub repo:
272
+
273
+ ```bash
274
+ gh pr list -R owner/other-repo
275
+ gh issue create -R owner/other-repo --title "..."
276
+ gh run list -R owner/other-repo
277
+ ```
278
+
279
+ ## When NOT to Use
280
+
281
+ | Situation | Use Instead |
282
+ |-----------|-------------|
283
+ | Gitea-hosted repos | `mcp-gitea` tools |
284
+ | Local git operations (commit, diff, stash) | `git` CLI directly |
285
+ | Reading local files | Read tool |
286
+ | Pushing/pulling code | `git push` / `git pull` |
@@ -0,0 +1,242 @@
1
+ ---
2
+ name: ima-doc2pdf
3
+ description: >-
4
+ Convert DOCX content into branded IMA PDF documents using ReportLab with Lato
5
+ typography, navy headings, justified body text, running footers, and embedded
6
+ images. Produces branded PDF documents with content pages. Generates a placeholder
7
+ cover that can be replaced by ima-cover-creator for production output. Use when: converting a Word doc to branded PDF,
8
+ creating PDF content pages for Canva import, generating an IMA branded document PDF,
9
+ or when the user says "convert this docx to PDF," "branded PDF," "content pages,"
10
+ "PDF for Canva," or "doc to PDF." Also triggers on: "make a PDF from this Word
11
+ file," "export to PDF," "generate branded PDF," "IMA branded document PDF."
12
+ Always load ima-brand alongside for color/typography authority.
13
+ ---
14
+
15
+ # IMA DOCX → Branded PDF
16
+
17
+ Extracts content from any Word document and generates a branded IMA PDF with
18
+ Lato typography, navy/gold colors, and IMA layout standards. Works with guides,
19
+ reports, white papers, and other IMA documents. Outputs content pages only
20
+ (no cover page) — the cover is handled by `ima-cover-creator` and merged via pypdf.
21
+
22
+ ---
23
+
24
+ ## Why ReportLab for Content
25
+
26
+ Content pages are pure document flow: headings, body paragraphs, bullet lists,
27
+ inline bold/italic, embedded images, and running footers. ReportLab handles all of
28
+ this with proper Lato font registration, automatic page breaks, and precise
29
+ typographic control. No coordinate math needed — the flow engine does the work.
30
+
31
+ ---
32
+
33
+ ## Quick Start
34
+
35
+ ### 1. Install dependencies (once)
36
+
37
+ ```bash
38
+ pip install python-docx reportlab Pillow pypdf --break-system-packages
39
+ ```
40
+
41
+ ### 2. Generate content PDF
42
+
43
+ ```bash
44
+ python3 scripts/generate_pdf.py path/to/document.docx --out content.pdf
45
+ ```
46
+
47
+ ### 3. Merge with cover (from ima-cover-creator)
48
+
49
+ ```python
50
+ from pypdf import PdfReader, PdfWriter
51
+
52
+ cover = PdfReader("cover.pdf")
53
+ content = PdfReader("content.pdf")
54
+
55
+ writer = PdfWriter()
56
+ writer.add_page(cover.pages[0])
57
+
58
+ # Skip the first 2 pages (ReportLab's placeholder cover + overflow)
59
+ for page in content.pages[2:]:
60
+ writer.add_page(page)
61
+
62
+ with open("final.pdf", "wb") as f:
63
+ writer.write(f)
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Pipeline
69
+
70
+ ```
71
+ DOCX
72
+ ↓ extract_docx.py (text, structure, metadata)
73
+ ↓ generate_pdf.py (ReportLab → branded PDF)
74
+
75
+ content.pdf (N pages, no cover)
76
+ +
77
+ cover.pdf (from ima-cover-creator)
78
+ ↓ pypdf merge
79
+
80
+ final.pdf → Canva import
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Typography Spec (Canva-confirmed)
86
+
87
+ All values confirmed from Canva design data. Font: Lato (Google Fonts).
88
+
89
+ ### Headings
90
+
91
+ | Element | Size | Weight | Color | Align |
92
+ |---------|------|--------|-------|-------|
93
+ | Section heading (h2) | 15pt | Bold | #00066F | Center |
94
+ | Sub-heading (h3) | 13pt | Bold | #00066F | Left |
95
+ | Intro heading | 15pt | Bold | #00066F | Center |
96
+
97
+ ### Body Text
98
+
99
+ | Element | Size | Weight | Color | Align |
100
+ |---------|------|--------|-------|-------|
101
+ | Body paragraph | 12pt | Regular | #000000 | Justify |
102
+ | Body bold inline | 12pt | Bold | #000000 | Justify |
103
+ | Body bold navy | 12pt | Bold | #00066F | Justify |
104
+ | Bullet item | 12pt | Regular | #000000 | Left |
105
+ | Bullet marker | — | — | #00066F | — |
106
+
107
+ ### Other Elements
108
+
109
+ | Element | Size | Weight | Color |
110
+ |---------|------|--------|-------|
111
+ | Footer | 10pt | Regular | #666666 |
112
+ | Reference entry | 8pt | Regular | #333333 |
113
+ | Reference heading | 13pt | Bold | #00066F |
114
+ | Q&A question | 12pt | Bold | #00066F |
115
+ | Q&A answer | 12pt | Regular | #000000 |
116
+ | Warning box | 12pt | Bold | #FFFFFF on #00066F bg |
117
+
118
+ ### Page Setup
119
+
120
+ | Property | Value |
121
+ |----------|-------|
122
+ | Page size | US Letter (8.5 × 11 in) |
123
+ | Margins | 0.5 in all sides |
124
+ | Body width | 7.5 in |
125
+ | Footer height | 0.4 in from bottom |
126
+
127
+ ---
128
+
129
+ ## Content Extraction
130
+
131
+ The `extract_docx.py` script classifies each Word paragraph into typed blocks:
132
+
133
+ | Type | Description |
134
+ |------|-------------|
135
+ | `h1` | Top-level heading (title) |
136
+ | `h2` | Section heading |
137
+ | `h3` | Sub-heading |
138
+ | `heading_bold` | All-bold paragraph (inline heading) |
139
+ | `body` | Regular paragraph |
140
+ | `bullet` | List item |
141
+ | `author` | Author name |
142
+ | `date` | Date string |
143
+ | `disclaimer` | Disclaimer text |
144
+ | `warning` | Warning box content |
145
+ | `question` | Q&A question |
146
+ | `answer_start` | Q&A answer (YES/NO prefix) |
147
+ | `reference` | Numbered citation |
148
+ | `ref_heading` | "References" heading |
149
+ | `figure_caption` | Figure/table caption |
150
+ | `page_break` | Hard page break |
151
+
152
+ Each block includes `runs` with per-run bold/italic flags for inline formatting.
153
+
154
+ ---
155
+
156
+ ## Image Handling
157
+
158
+ The script extracts embedded DOCX images via `python-docx`:
159
+
160
+ 1. Reads all image relationships from the DOCX package
161
+ 2. Maps paragraph indices to embedded image positions
162
+ 3. Writes images to temp files
163
+ 4. Inserts ReportLab `Image` flowables at the correct positions
164
+ 5. Scales to fit within `page_width - 2 × margin`
165
+
166
+ Images that appear between text paragraphs (image-only paragraphs) are also caught
167
+ and appended after all text content.
168
+
169
+ ---
170
+
171
+ ## Cover Page Behavior
172
+
173
+ The ReportLab script generates a **placeholder cover** (navy background with title
174
+ text) as pages 1-2 of its output. This exists so the script works standalone, but
175
+ when pairing with `ima-cover-creator`, **skip the first 2 pages** during merge.
176
+
177
+ To check which pages to skip:
178
+ ```python
179
+ from pypdf import PdfReader
180
+ r = PdfReader("content.pdf")
181
+ for i in range(min(3, len(r.pages))):
182
+ text = r.pages[i].extract_text()[:100]
183
+ print(f"Page {i}: {text}")
184
+ ```
185
+
186
+ The first content page typically starts with "Introduction" or a section heading.
187
+
188
+ ---
189
+
190
+ ## Scripts
191
+
192
+ | Script | Purpose |
193
+ |--------|---------|
194
+ | `generate_pdf.py` | Main: DOCX → branded PDF via ReportLab |
195
+ | `extract_docx.py` | Extracts structured content from Word documents |
196
+ | `docx_utils.py` | Shared utilities for DOCX parsing |
197
+
198
+ ---
199
+
200
+ ## Fonts
201
+
202
+ Lato TTF files are auto-downloaded from Google Fonts on first run into the `fonts/`
203
+ directory (which is git-ignored). The font family (Regular, Bold, Italic, BoldItalic)
204
+ is registered with ReportLab so that `<b>` and `<i>` markup works in Paragraph objects.
205
+
206
+ If the fonts are already present, the download is skipped.
207
+
208
+ ---
209
+
210
+ ## Customization
211
+
212
+ ### Adjusting the footer
213
+
214
+ The footer shows the document title and date. To customize:
215
+ ```python
216
+ # In generate_pdf.py, the footer text is built from:
217
+ title_short = (title[:60] + "...") if len(title) > 60 else title
218
+ footer_text = f"{title_short} ({clean_date})"
219
+ ```
220
+
221
+ ### Adding new paragraph types
222
+
223
+ 1. Add a classifier rule in `extract_docx.py`
224
+ 2. Add a handler in `block_to_flowables()` in `generate_pdf.py`
225
+ 3. Create a ReportLab `ParagraphStyle` in `build_styles()`
226
+
227
+ ---
228
+
229
+ ## Relationship to Other Skills
230
+
231
+ | Skill | Role |
232
+ |-------|------|
233
+ | **ima-cover-creator** | Generates branded cover page (PPTX → PDF) |
234
+ | **ima-cancer-care-guides** | Full pipeline including Markdown source and Canva API mapping |
235
+ | **ima-brand** | Source of truth for colors, typography, voice |
236
+
237
+ **Typical workflow:**
238
+ ```
239
+ ima-cover-creator → cover.pdf (1 page)
240
+ ima-doc2pdf → content.pdf (skip first 2 pages)
241
+ pypdf merge → final.pdf → Canva import
242
+ ```
@@ -0,0 +1,88 @@
1
+ # Content Page Formatting Spec
2
+
3
+ Values confirmed from Canva design data (Cancer Drug Resistance Guide, March 2026).
4
+ Canva internal units × 0.75 = points.
5
+
6
+ ## Page Setup
7
+
8
+ | Property | Value |
9
+ |----------|-------|
10
+ | Page size | US Letter (8.5 × 11 in / 612 × 792 pt) |
11
+ | Margins | 0.5 in (36 pt) all sides |
12
+ | Body text width | 7.5 in (540 pt) |
13
+ | Footer position | 0.4 in from bottom |
14
+
15
+ ## Color Palette
16
+
17
+ | Name | Hex | Usage |
18
+ |------|-----|-------|
19
+ | Trustworthy Indigo | #00066F | Headings, bullet markers, warning bg |
20
+ | Body Black | #000000 | Body text (pure black, not #1A1A1A) |
21
+ | Dark Gray | #333333 | Reference entries |
22
+ | Gray Text | #666666 | Footer |
23
+ | Light Gray | #CCCCCC | Footer rule |
24
+ | White | #FFFFFF | Warning text |
25
+ | Vital Gold | #FFCC00 | Warning emphasis |
26
+
27
+ ## Section Heading (h2)
28
+
29
+ - Font: Lato 15pt Bold
30
+ - Color: #00066F (navy)
31
+ - Alignment: Center
32
+ - Spacing: 16pt above, 5pt below
33
+
34
+ ## Sub-heading (h3)
35
+
36
+ - Font: Lato 13pt Bold
37
+ - Color: #00066F (navy)
38
+ - Alignment: Left
39
+ - Spacing: 10pt above, 3pt below
40
+
41
+ ## Body Paragraph
42
+
43
+ - Font: Lato 12pt Regular
44
+ - Color: #000000
45
+ - Alignment: Justify
46
+ - Line height: 1.4 (14.5pt leading)
47
+ - Spacing: 6pt below
48
+
49
+ ## Bullet Lists
50
+
51
+ - Font: Lato 12pt Regular
52
+ - Color: #000000
53
+ - Bullet marker color: #00066F (navy)
54
+ - Indent: 18pt
55
+ - Spacing: 1pt above, 1pt below each item
56
+
57
+ ## Bold Inline Variants
58
+
59
+ - **Body bold** (#000000): Drug names, emphasis
60
+ - **Body bold navy** (#00066F): Inline sub-headings
61
+
62
+ ## Warning Box
63
+
64
+ - Background: #00066F (navy) — note: ReportLab uses solid, Canva uses gradient
65
+ - Text: Lato 12pt Bold, #FFFFFF
66
+ - Emphasis: Vital Gold #FFCC00
67
+ - Alignment: Center
68
+ - Padding: 10pt all sides
69
+
70
+ ## Footer
71
+
72
+ - Font: Lato 10pt
73
+ - Color: #666666
74
+ - Alignment: Center
75
+ - Content: "{Document Title} ({Date})"
76
+ - Rule above: 0.5pt, #CCCCCC
77
+
78
+ ## References
79
+
80
+ - Heading: Lato 13pt Bold, #00066F
81
+ - Entry: Lato 8pt Regular, #333333
82
+ - Hanging indent: 14pt
83
+
84
+ ## Q&A
85
+
86
+ - Question: Lato 12pt Bold, #00066F
87
+ - Answer: Lato 12pt Regular, #000000
88
+ - YES/NO prefix: Bold
@@ -0,0 +1,21 @@
1
+ """
2
+ Shared utilities for Word document processing across ima-cancer-care-guides scripts.
3
+ """
4
+
5
+ from docx.oxml.ns import qn
6
+
7
+
8
+ def has_page_break(para):
9
+ """Detect a hard page break in a Word paragraph.
10
+
11
+ Checks both explicit w:br type=page runs and section-level page breaks
12
+ (w:pPr/w:sectPr), which appear on the last paragraph of a section.
13
+ """
14
+ for run in para.runs:
15
+ for br in run._element.findall(qn('w:br')):
16
+ if br.get(qn('w:type')) == 'page':
17
+ return True
18
+ pPr = para._element.find(qn('w:pPr'))
19
+ if pPr is not None and pPr.find(qn('w:sectPr')) is not None:
20
+ return True
21
+ return False