portable-agent-layer 0.34.0 → 0.35.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.
- package/assets/skills/presentation/SKILL.md +2 -0
- package/assets/skills/presentation/demo/slides/004-content.md +27 -1
- package/assets/skills/presentation/theme-base/base.css +206 -0
- package/assets/skills/presentation/theme-base/skeleton.html +49 -0
- package/assets/skills/presentation/tools/lib/lint-rules.ts +25 -0
- package/assets/templates/AGENTS.md.template +2 -1
- package/package.json +1 -1
|
@@ -103,6 +103,8 @@ Exit codes: `0` = clean, `1` = errors found (or warnings under `--strict`), `2`
|
|
|
103
103
|
|
|
104
104
|
Open `<out>/<deck-name>/<deck-name>.html` in your browser. Iterate by editing a slide, re-running the build, and refreshing the tab. Reveal shortcuts: `F` = fullscreen, `S` = speaker notes window, `?` = keyboard shortcuts, `Esc` = overview.
|
|
105
105
|
|
|
106
|
+
**Print / PDF view with trainer notes.** Open the built HTML in your browser and press Cmd/Ctrl-P (no special URL param needed). The print stream interleaves trainer notes between slides — `[Slide 1][Notes 1][Slide 2][Notes 2]…` — so flipping pages reads in delivery order. Long notes flow across multiple pages. Slides without a `Note:` block produce no extra page. Each notes page carries a header identifying the source slide. Limitation: a single fenced code block inside notes cannot break across pages — the doctor warns at 30 lines (`notes-code-too-long`); split the block or shorten the example.
|
|
107
|
+
|
|
106
108
|
## Deck folder layout
|
|
107
109
|
|
|
108
110
|
```
|
|
@@ -9,4 +9,30 @@ The default. Title at top, body below.
|
|
|
9
9
|
- And this
|
|
10
10
|
- **Bold**, *italic*, `inline code`, [links](https://example.com)
|
|
11
11
|
|
|
12
|
-
Note:
|
|
12
|
+
Note:
|
|
13
|
+
- [Reveal.js print docs](https://revealjs.com/pdf-export/)
|
|
14
|
+
- Why this slide demonstrates print-with-notes
|
|
15
|
+
- Long enough to cross a page break in the trainer print-out
|
|
16
|
+
- Mixes link, bullet hierarchy, blockquote, inline code
|
|
17
|
+
- Lets you verify the multi-page flow without authoring a fixture deck
|
|
18
|
+
- Default layout — when to leave it off
|
|
19
|
+
- You want title + body, no special shape
|
|
20
|
+
- You want the layout-agnostic spacing rules to kick in
|
|
21
|
+
- You want the doctor to skip layout-shape checks
|
|
22
|
+
- Bullet hygiene reminder (per SKILL.md)
|
|
23
|
+
- 2–15 words at top level, 2–10 at sub
|
|
24
|
+
- No prose paragraphs — the `prose-paragraph-in-body` rule will warn
|
|
25
|
+
- Em-dash continuation in bullets is also flagged
|
|
26
|
+
- Quote example
|
|
27
|
+
> Slides are a delivery surface, not a document. Prose belongs in notes.
|
|
28
|
+
- Anticipated questions
|
|
29
|
+
- "Why a 30-line cap on notes code blocks?" — single `<pre>` blocks don't break across printed pages; longer blocks clip
|
|
30
|
+
- "Can I disable the trainer-notes printout?" — yes, remove the notes injection block from the `.then()` in skeleton.html in your local override
|
|
31
|
+
- "Does the speaker view (S key) still work?" — yes, the injection only affects print, not the speaker view path
|
|
32
|
+
- Forward references
|
|
33
|
+
- more on layout-specific rules in the `code` and `table` slides
|
|
34
|
+
- more on print-view polish in `base.css` under "Trainer notes in print"
|
|
35
|
+
- Closing beats
|
|
36
|
+
- The print-with-notes feature exists so a trainer can hand a delegate a printed deck and a printed prep packet at once
|
|
37
|
+
- Ordering is `[Slide 1][Notes 1][Slide 2][Notes 2]…` so flipping pages reads in delivery order
|
|
38
|
+
- Slides without a `Note:` block produce zero extra pages — title cards stay clean
|
|
@@ -436,3 +436,209 @@ html.print-pdf .reveal .slides .pdf-page {
|
|
|
436
436
|
margin: 16px auto !important;
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
+
|
|
440
|
+
/* ── Trainer notes in print (Cmd+P on the regular URL)
|
|
441
|
+
* skeleton.html injects a `.print-notes-page` div after each slide section that
|
|
442
|
+
* has speaker notes. On screen they are invisible; Cmd+P produces the ordering
|
|
443
|
+
* [Slide N][Notes N][Slide N+1][Notes N+1]…. Long notes flow across multiple
|
|
444
|
+
* pages. Slides with no Note: block produce no extra page.
|
|
445
|
+
*
|
|
446
|
+
* Key mechanics:
|
|
447
|
+
* • `page-break-after: always` on sections forces each slide to end at a page
|
|
448
|
+
* boundary even if its content is shorter than the page height.
|
|
449
|
+
* • `page-break-before/after: always` on .print-notes-page ensures the notes
|
|
450
|
+
* block starts on a fresh page and the next slide starts on a fresh page.
|
|
451
|
+
* • `break-inside: auto` lets long notes flow across pages naturally. */
|
|
452
|
+
@media print {
|
|
453
|
+
html:not(.print-pdf) .reveal .slides > section {
|
|
454
|
+
page-break-after: always !important;
|
|
455
|
+
break-after: always !important;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page {
|
|
459
|
+
display: block !important;
|
|
460
|
+
page-break-before: always !important;
|
|
461
|
+
break-before: always !important;
|
|
462
|
+
page-break-after: always !important;
|
|
463
|
+
break-after: always !important;
|
|
464
|
+
page-break-inside: auto;
|
|
465
|
+
break-inside: auto;
|
|
466
|
+
overflow: visible !important;
|
|
467
|
+
position: static !important;
|
|
468
|
+
min-height: 0 !important;
|
|
469
|
+
padding: 36px 56px 56px 56px;
|
|
470
|
+
box-sizing: border-box;
|
|
471
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
472
|
+
"Helvetica Neue", Arial, sans-serif;
|
|
473
|
+
font-size: 13pt;
|
|
474
|
+
line-height: 1.5;
|
|
475
|
+
color: #111;
|
|
476
|
+
background: #fff;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page ul,
|
|
480
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page ol {
|
|
481
|
+
margin: 0.4em 0 0.8em 0;
|
|
482
|
+
padding-left: 1.4em;
|
|
483
|
+
}
|
|
484
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page li {
|
|
485
|
+
margin: 0.15em 0;
|
|
486
|
+
}
|
|
487
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page a {
|
|
488
|
+
color: var(--brand-primary, #0040c2);
|
|
489
|
+
text-decoration: underline;
|
|
490
|
+
}
|
|
491
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page code {
|
|
492
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
493
|
+
font-size: 0.92em;
|
|
494
|
+
background: #f4f4f4;
|
|
495
|
+
padding: 1px 4px;
|
|
496
|
+
border-radius: 3px;
|
|
497
|
+
}
|
|
498
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page pre {
|
|
499
|
+
font-size: 0.92em;
|
|
500
|
+
line-height: 1.4;
|
|
501
|
+
background: #f4f4f4;
|
|
502
|
+
padding: 8px 12px;
|
|
503
|
+
border-radius: 4px;
|
|
504
|
+
white-space: pre-wrap;
|
|
505
|
+
overflow: visible;
|
|
506
|
+
}
|
|
507
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page blockquote {
|
|
508
|
+
margin: 0.6em 0 0.6em 1em;
|
|
509
|
+
padding-left: 0.8em;
|
|
510
|
+
border-left: 2px solid #a0a0a0;
|
|
511
|
+
color: #444;
|
|
512
|
+
font-style: italic;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/* Slide-context header: "Notes — slide N <slide title>" */
|
|
516
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page .print-notes-header {
|
|
517
|
+
display: flex;
|
|
518
|
+
align-items: baseline;
|
|
519
|
+
gap: 0.8em;
|
|
520
|
+
margin: 0 0 24px 0;
|
|
521
|
+
padding: 0 0 10px 0;
|
|
522
|
+
border-bottom: 1.5pt solid var(--brand-primary, #0040c2);
|
|
523
|
+
-webkit-print-color-adjust: exact;
|
|
524
|
+
print-color-adjust: exact;
|
|
525
|
+
}
|
|
526
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page .print-notes-label {
|
|
527
|
+
font-size: 11pt;
|
|
528
|
+
font-weight: 600;
|
|
529
|
+
text-transform: uppercase;
|
|
530
|
+
letter-spacing: 0.05em;
|
|
531
|
+
color: var(--brand-primary, #0040c2);
|
|
532
|
+
white-space: nowrap;
|
|
533
|
+
}
|
|
534
|
+
html:not(.print-pdf) .reveal .slides > .print-notes-page .print-notes-title {
|
|
535
|
+
font-size: 13pt;
|
|
536
|
+
font-weight: 500;
|
|
537
|
+
color: #222;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/* ── Per-slide print chrome (logo + slide number)
|
|
542
|
+
* skeleton.html injects `.print-logo` and `.print-slide-number` into every
|
|
543
|
+
* section. On screen: invisible. In Cmd+P print: positioned at the same
|
|
544
|
+
* bottom corners as the screen-mode chrome, once per slide page.
|
|
545
|
+
*
|
|
546
|
+
* The screen-mode equivalents (.slides::after logo and .slides > .slide-number)
|
|
547
|
+
* are suppressed in print because they are positioned relative to .slides — a
|
|
548
|
+
* container that spans all pages in Cmd+P mode, putting them at the very end
|
|
549
|
+
* of the document rather than on each individual slide. */
|
|
550
|
+
@media print {
|
|
551
|
+
/* Suppress the screen-mode global chrome */
|
|
552
|
+
html:not(.print-pdf) .reveal .slides::after,
|
|
553
|
+
html:not(.print-pdf) .reveal .slides > .slide-number {
|
|
554
|
+
display: none !important;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* Sections must be position:relative so absolute children anchor to the
|
|
558
|
+
* slide page, not to the full-document .slides container. */
|
|
559
|
+
html:not(.print-pdf) .reveal .slides > section {
|
|
560
|
+
position: relative !important;
|
|
561
|
+
/* Ensure background images (logos) print on all browsers. */
|
|
562
|
+
-webkit-print-color-adjust: exact !important;
|
|
563
|
+
print-color-adjust: exact !important;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
html:not(.print-pdf) .reveal .slides > section .print-logo {
|
|
567
|
+
display: block !important;
|
|
568
|
+
position: absolute;
|
|
569
|
+
bottom: 1.5rem;
|
|
570
|
+
right: 1.5rem;
|
|
571
|
+
width: 90px;
|
|
572
|
+
height: 28px;
|
|
573
|
+
background: var(--brand-logo) no-repeat right bottom / contain;
|
|
574
|
+
opacity: 0.55;
|
|
575
|
+
pointer-events: none;
|
|
576
|
+
-webkit-print-color-adjust: exact;
|
|
577
|
+
print-color-adjust: exact;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
html:not(.print-pdf) .reveal .slides > section .print-slide-number {
|
|
581
|
+
display: block !important;
|
|
582
|
+
position: absolute;
|
|
583
|
+
bottom: 1.5rem;
|
|
584
|
+
left: 1.5rem;
|
|
585
|
+
font-family: var(--font-body, sans-serif);
|
|
586
|
+
font-size: 9pt;
|
|
587
|
+
font-weight: 400;
|
|
588
|
+
line-height: 28px;
|
|
589
|
+
color: #71717A;
|
|
590
|
+
letter-spacing: 0.05em;
|
|
591
|
+
pointer-events: none;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/* ── Layout-specific print overrides
|
|
596
|
+
* Reveal's vendored print CSS forces several properties with !important on
|
|
597
|
+
* heading and paragraph elements, which clobbers layout-specific design intent:
|
|
598
|
+
* • h1-h6: font-size (28–20pt), color (#000), text-align (left),
|
|
599
|
+
* letter-spacing (normal), line-height (normal)
|
|
600
|
+
* • p, li, td: font-size (20pt), color (#000)
|
|
601
|
+
*
|
|
602
|
+
* We beat these by pairing higher-specificity selectors with !important.
|
|
603
|
+
* Reveal's rules are scoped to `html:not(.print-pdf) .reveal h1` — specificity
|
|
604
|
+
* (0,2,1). Our layout-scoped rules use `html:not(.print-pdf) .reveal
|
|
605
|
+
* section[data-layout="…"] h1` — specificity (0,3,2), which wins. */
|
|
606
|
+
@media print {
|
|
607
|
+
/* big-stat — restore the dominant number that Reveal shrinks to 28pt */
|
|
608
|
+
html:not(.print-pdf) .reveal section[data-layout="big-stat"] h1 {
|
|
609
|
+
font-size: 96pt !important;
|
|
610
|
+
color: var(--brand-primary, #0E1335) !important;
|
|
611
|
+
text-align: center !important;
|
|
612
|
+
letter-spacing: -0.025em !important;
|
|
613
|
+
line-height: 0.9 !important;
|
|
614
|
+
font-weight: 800 !important;
|
|
615
|
+
-webkit-print-color-adjust: exact !important;
|
|
616
|
+
print-color-adjust: exact !important;
|
|
617
|
+
}
|
|
618
|
+
html:not(.print-pdf) .reveal section[data-layout="big-stat"] h2,
|
|
619
|
+
html:not(.print-pdf) .reveal section[data-layout="big-stat"] p {
|
|
620
|
+
color: #71717A !important;
|
|
621
|
+
text-align: center !important;
|
|
622
|
+
font-weight: 400 !important;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/* quote / pull-quote — Reveal forces p { font-size: 20pt !important }, but
|
|
626
|
+
* blockquote text lives in p elements. `inherit` re-derives the size from the
|
|
627
|
+
* blockquote element (var(--text-xl) / var(--text-2xl)), restoring design intent. */
|
|
628
|
+
html:not(.print-pdf) .reveal section[data-layout="quote"] blockquote p,
|
|
629
|
+
html:not(.print-pdf) .reveal section[data-layout="pull-quote"] blockquote p {
|
|
630
|
+
font-size: inherit !important;
|
|
631
|
+
color: inherit !important;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/* Screen: injected notes pages are invisible. The `@media print` block above
|
|
636
|
+
* restores display:block. Using !important because Reveal may globally set
|
|
637
|
+
* visibility rules on .slides children. */
|
|
638
|
+
@media screen {
|
|
639
|
+
.reveal .slides > .print-notes-page,
|
|
640
|
+
.reveal .slides > section .print-logo,
|
|
641
|
+
.reveal .slides > section .print-slide-number {
|
|
642
|
+
display: none !important;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
@@ -47,6 +47,55 @@ Reveal.initialize({
|
|
|
47
47
|
const sn = document.querySelector('.reveal > .slide-number');
|
|
48
48
|
const slides = document.querySelector('.reveal .slides');
|
|
49
49
|
if (sn && slides) slides.appendChild(sn);
|
|
50
|
+
|
|
51
|
+
// Inject print-only chrome (logo + slide number) into every section, and a
|
|
52
|
+
// `.print-notes-page` after each section that has notes. All injected elements
|
|
53
|
+
// are display:none on screen; Cmd+P produces [Slide N][Notes N]… ordering.
|
|
54
|
+
const sections = document.querySelectorAll('.reveal .slides > section');
|
|
55
|
+
const totalSlides = sections.length;
|
|
56
|
+
let slideNum = 0;
|
|
57
|
+
for (const section of sections) {
|
|
58
|
+
slideNum++;
|
|
59
|
+
|
|
60
|
+
const logoEl = document.createElement('div');
|
|
61
|
+
logoEl.className = 'print-logo';
|
|
62
|
+
section.appendChild(logoEl);
|
|
63
|
+
|
|
64
|
+
const numEl = document.createElement('div');
|
|
65
|
+
numEl.className = 'print-slide-number';
|
|
66
|
+
numEl.textContent = `${slideNum} / ${totalSlides}`;
|
|
67
|
+
section.appendChild(numEl);
|
|
68
|
+
|
|
69
|
+
const notesEl = section.querySelector('aside.notes');
|
|
70
|
+
if (!notesEl?.innerHTML.trim()) continue;
|
|
71
|
+
|
|
72
|
+
const heading = section.querySelector('h1, h2, h3');
|
|
73
|
+
const title = heading ? heading.textContent.trim() : '';
|
|
74
|
+
|
|
75
|
+
const page = document.createElement('div');
|
|
76
|
+
page.className = 'print-notes-page';
|
|
77
|
+
|
|
78
|
+
const hdr = document.createElement('header');
|
|
79
|
+
hdr.className = 'print-notes-header';
|
|
80
|
+
const label = document.createElement('span');
|
|
81
|
+
label.className = 'print-notes-label';
|
|
82
|
+
label.textContent = `Notes — slide ${slideNum}`;
|
|
83
|
+
hdr.appendChild(label);
|
|
84
|
+
if (title) {
|
|
85
|
+
const titleSpan = document.createElement('span');
|
|
86
|
+
titleSpan.className = 'print-notes-title';
|
|
87
|
+
titleSpan.textContent = title;
|
|
88
|
+
hdr.appendChild(titleSpan);
|
|
89
|
+
}
|
|
90
|
+
page.appendChild(hdr);
|
|
91
|
+
|
|
92
|
+
const body = document.createElement('div');
|
|
93
|
+
body.className = 'print-notes-content';
|
|
94
|
+
body.innerHTML = notesEl.innerHTML;
|
|
95
|
+
page.appendChild(body);
|
|
96
|
+
|
|
97
|
+
section.insertAdjacentElement('afterend', page);
|
|
98
|
+
}
|
|
50
99
|
});
|
|
51
100
|
</script>
|
|
52
101
|
</body>
|
|
@@ -632,6 +632,31 @@ export const RULES: Rule[] = [
|
|
|
632
632
|
},
|
|
633
633
|
},
|
|
634
634
|
|
|
635
|
+
{
|
|
636
|
+
// Notes get printed as separate pages with `showNotes: 'separate-page'`.
|
|
637
|
+
// Browsers split long *list* content across pages cleanly, but a single
|
|
638
|
+
// fenced code block inside notes is one indivisible element — if it runs
|
|
639
|
+
// longer than one printed page it gets clipped. Warn at 30 lines so the
|
|
640
|
+
// author can split or shorten before the trainer discovers it on paper.
|
|
641
|
+
name: "notes-code-too-long",
|
|
642
|
+
scope: "slide",
|
|
643
|
+
check: (ctx) => {
|
|
644
|
+
const notes = extractNotes(ctx.body);
|
|
645
|
+
if (!notes.trim()) return [];
|
|
646
|
+
const findings: Finding[] = [];
|
|
647
|
+
for (const n of codeBlockLineCounts(notes)) {
|
|
648
|
+
if (n > 30) {
|
|
649
|
+
findings.push({
|
|
650
|
+
rule: "notes-code-too-long",
|
|
651
|
+
severity: "W",
|
|
652
|
+
msg: `notes code block has ${n} lines — won't break across printed pages cleanly (limit 30)`,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return findings;
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
|
|
635
660
|
// ── Deck-scope rules (Tier 3) ───────────────────────────────────────────
|
|
636
661
|
|
|
637
662
|
{
|
|
@@ -55,6 +55,7 @@ Start your response with the following header in this mode:
|
|
|
55
55
|
|
|
56
56
|
- **Mandatory output format** — Every response MUST use exactly one of the output formats above (ALGORITHM, NATIVE, or MINIMAL). No freeform output.
|
|
57
57
|
- **Response format before questions** — Always complete the current response format output FIRST, then invoke AskUserQuestion at the end.
|
|
58
|
+
- **Named project → resume first** — Whenever the user references a project by name in ANY mode, run `bun ~/.pal/tools/project.ts resume <slug>` before any file search or task execution. The project record has paths, context, and handoff notes that make manual searching unnecessary.
|
|
58
59
|
|
|
59
60
|
---
|
|
60
61
|
|
|
@@ -73,7 +74,7 @@ Load context on-demand by reading the file at the path listed. Only load what th
|
|
|
73
74
|
| Opinion tracking | `~/.pal/docs/OPINION_TRACKING.md` |
|
|
74
75
|
| Steering rules | `~/.pal/docs/STEERING_RULES.md` |
|
|
75
76
|
| Algorithm (complex work phases) | `~/.pal/docs/ALGORITHM.md` |
|
|
76
|
-
| Project lifecycle (
|
|
77
|
+
| Project lookup AND lifecycle (find paths, resume state, register, manage) | `~/.pal/skills/projects/SKILL.md` |
|
|
77
78
|
|
|
78
79
|
### User Context (TELOS)
|
|
79
80
|
|