@forwardimpact/outpost 3.0.3
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/LICENSE +201 -0
- package/README.md +15 -0
- package/bin/fit-outpost.js +3 -0
- package/config/scheduler.json +34 -0
- package/package.json +63 -0
- package/src/agent-runner.js +189 -0
- package/src/index.js +5 -0
- package/src/kb-manager.js +228 -0
- package/src/outpost.js +417 -0
- package/src/scheduler.js +195 -0
- package/src/socket-server.js +336 -0
- package/src/state-manager.js +126 -0
- package/templates/.claude/agents/chief-of-staff.md +58 -0
- package/templates/.claude/agents/concierge.md +46 -0
- package/templates/.claude/agents/head-hunter.md +57 -0
- package/templates/.claude/agents/librarian.md +60 -0
- package/templates/.claude/agents/postman.md +47 -0
- package/templates/.claude/agents/recruiter.md +63 -0
- package/templates/.claude/settings.json +88 -0
- package/templates/.claude/skills/candidate-report/SKILL.md +139 -0
- package/templates/.claude/skills/candidate-report/references/report-template.html +203 -0
- package/templates/.claude/skills/candidate-report/references/report.css +438 -0
- package/templates/.claude/skills/candidate-report/references/rubric.md +76 -0
- package/templates/.claude/skills/candidate-report/scripts/render-pdf.mjs +48 -0
- package/templates/.claude/skills/deck-create/SKILL.md +75 -0
- package/templates/.claude/skills/deck-create/references/slide.css +35 -0
- package/templates/.claude/skills/deck-create/scripts/convert-to-pdf.mjs +47 -0
- package/templates/.claude/skills/deck-summarize/SKILL.md +139 -0
- package/templates/.claude/skills/deck-summarize/references/brief-template.md +85 -0
- package/templates/.claude/skills/deck-summarize/scripts/extract-pptx.mjs +210 -0
- package/templates/.claude/skills/doc-collab/SKILL.md +112 -0
- package/templates/.claude/skills/doc-create/SKILL.md +105 -0
- package/templates/.claude/skills/doc-create/scripts/convert-to-pdf.mjs +50 -0
- package/templates/.claude/skills/draft-emails/SKILL.md +141 -0
- package/templates/.claude/skills/draft-emails/references/template.md +26 -0
- package/templates/.claude/skills/draft-emails/scripts/scan-emails.mjs +66 -0
- package/templates/.claude/skills/draft-emails/scripts/send-email.mjs +139 -0
- package/templates/.claude/skills/extract-entities/SKILL.md +187 -0
- package/templates/.claude/skills/extract-entities/references/TEMPLATES.md +14 -0
- package/templates/.claude/skills/extract-entities/references/conditions.md +55 -0
- package/templates/.claude/skills/extract-entities/references/content.md +104 -0
- package/templates/.claude/skills/extract-entities/references/links.md +48 -0
- package/templates/.claude/skills/extract-entities/references/recruitment.md +57 -0
- package/templates/.claude/skills/extract-entities/references/resolution.md +71 -0
- package/templates/.claude/skills/extract-entities/references/sources.md +70 -0
- package/templates/.claude/skills/extract-entities/references/templates-conditions.md +36 -0
- package/templates/.claude/skills/extract-entities/references/templates-goals-priorities.md +63 -0
- package/templates/.claude/skills/extract-entities/references/templates-people-orgs.md +67 -0
- package/templates/.claude/skills/extract-entities/references/templates-projects-topics.md +68 -0
- package/templates/.claude/skills/extract-entities/scripts/state.mjs +131 -0
- package/templates/.claude/skills/hyprnote-follow/SKILL.md +174 -0
- package/templates/.claude/skills/hyprnote-follow/references/coaching.md +118 -0
- package/templates/.claude/skills/hyprnote-follow/references/debrief.md +59 -0
- package/templates/.claude/skills/hyprnote-follow/references/meeting-types.md +14 -0
- package/templates/.claude/skills/hyprnote-follow/scripts/follow.mjs +243 -0
- package/templates/.claude/skills/hyprnote-process/SKILL.md +147 -0
- package/templates/.claude/skills/hyprnote-process/references/extraction.md +84 -0
- package/templates/.claude/skills/hyprnote-process/references/sessions.md +81 -0
- package/templates/.claude/skills/hyprnote-process/scripts/scan.mjs +246 -0
- package/templates/.claude/skills/hyprnote-trim/SKILL.md +177 -0
- package/templates/.claude/skills/manage-tasks/SKILL.md +152 -0
- package/templates/.claude/skills/manage-tasks/references/format.md +73 -0
- package/templates/.claude/skills/meeting-prep/SKILL.md +180 -0
- package/templates/.claude/skills/organize-files/SKILL.md +146 -0
- package/templates/.claude/skills/organize-files/scripts/organize-by-type.mjs +105 -0
- package/templates/.claude/skills/organize-files/scripts/summarize.mjs +91 -0
- package/templates/.claude/skills/req-assess/SKILL.md +137 -0
- package/templates/.claude/skills/req-assess/references/interview-template.md +62 -0
- package/templates/.claude/skills/req-assess/references/panel-template.md +117 -0
- package/templates/.claude/skills/req-assess/references/rubric.md +65 -0
- package/templates/.claude/skills/req-decide/SKILL.md +163 -0
- package/templates/.claude/skills/req-decide/references/rubric.md +63 -0
- package/templates/.claude/skills/req-decide/references/template.md +118 -0
- package/templates/.claude/skills/req-forget/SKILL.md +154 -0
- package/templates/.claude/skills/req-forget/references/classify.md +23 -0
- package/templates/.claude/skills/req-forget/references/locations.md +59 -0
- package/templates/.claude/skills/req-forget/references/report-template.md +67 -0
- package/templates/.claude/skills/req-scan/SKILL.md +145 -0
- package/templates/.claude/skills/req-scan/references/fallbacks.md +76 -0
- package/templates/.claude/skills/req-scan/references/filters.md +83 -0
- package/templates/.claude/skills/req-scan/references/sources.md +76 -0
- package/templates/.claude/skills/req-scan/references/state.md +54 -0
- package/templates/.claude/skills/req-scan/references/template.md +36 -0
- package/templates/.claude/skills/req-scan/scripts/state.mjs +396 -0
- package/templates/.claude/skills/req-screen/SKILL.md +151 -0
- package/templates/.claude/skills/req-screen/references/rubric.md +94 -0
- package/templates/.claude/skills/req-screen/references/template.md +91 -0
- package/templates/.claude/skills/req-track/SKILL.md +189 -0
- package/templates/.claude/skills/req-track/references/fields.md +114 -0
- package/templates/.claude/skills/req-track/references/signals.md +42 -0
- package/templates/.claude/skills/req-track/references/statuses.md +32 -0
- package/templates/.claude/skills/req-track/references/templates.md +127 -0
- package/templates/.claude/skills/req-workday/SKILL.md +162 -0
- package/templates/.claude/skills/req-workday/references/field-mapping.md +40 -0
- package/templates/.claude/skills/req-workday/references/status-mapping.md +38 -0
- package/templates/.claude/skills/req-workday/references/templates.md +126 -0
- package/templates/.claude/skills/req-workday/references/xlsx-format.md +56 -0
- package/templates/.claude/skills/req-workday/scripts/parse-workday.mjs +313 -0
- package/templates/.claude/skills/send-chat/SKILL.md +170 -0
- package/templates/.claude/skills/sync-apple-calendar/SKILL.md +145 -0
- package/templates/.claude/skills/sync-apple-calendar/references/SCHEMA.md +80 -0
- package/templates/.claude/skills/sync-apple-calendar/scripts/query.mjs +307 -0
- package/templates/.claude/skills/sync-apple-calendar/scripts/sync.mjs +332 -0
- package/templates/.claude/skills/sync-apple-mail/SKILL.md +151 -0
- package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +115 -0
- package/templates/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +389 -0
- package/templates/.claude/skills/sync-apple-mail/scripts/sync-helpers.mjs +372 -0
- package/templates/.claude/skills/sync-apple-mail/scripts/sync.mjs +239 -0
- package/templates/.claude/skills/sync-teams/SKILL.md +177 -0
- package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +147 -0
- package/templates/.claude/skills/sync-teams/scripts/leveldb-reader.mjs +288 -0
- package/templates/.claude/skills/sync-teams/scripts/sync.mjs +381 -0
- package/templates/.claude/skills/upstream-skill/SKILL.md +130 -0
- package/templates/.claude/skills/upstream-skill/references/examples.md +80 -0
- package/templates/.claude/skills/weekly-update/SKILL.md +141 -0
- package/templates/.claude/skills/weekly-update/references/template.md +55 -0
- package/templates/CLAUDE.md +134 -0
- package/templates/USER.md +3 -0
- package/templates/apm.yml +5 -0
- package/templates/knowledge/Briefings/.gitkeep +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Render a candidate report HTML file to A4 PDF using Playwright.
|
|
4
|
+
*
|
|
5
|
+
* Usage: node scripts/render-pdf.mjs [input.html] [output.pdf]
|
|
6
|
+
*
|
|
7
|
+
* Defaults:
|
|
8
|
+
* input → /tmp/candidate-report.html
|
|
9
|
+
* output → ~/Desktop/candidate-report.pdf
|
|
10
|
+
*
|
|
11
|
+
* Requires: bun install playwright && bunx playwright install chromium
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { resolve, join } from "node:path";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
|
|
17
|
+
const HELP = `render-pdf — render candidate report HTML to A4 PDF via Playwright
|
|
18
|
+
|
|
19
|
+
Usage: node scripts/render-pdf.mjs [input.html] [output.pdf] [-h|--help]
|
|
20
|
+
|
|
21
|
+
Arguments:
|
|
22
|
+
input.html HTML report file (default: /tmp/candidate-report.html)
|
|
23
|
+
output.pdf Output PDF path (default: ~/Desktop/candidate-report.pdf)
|
|
24
|
+
|
|
25
|
+
Requires: bun install playwright && bunx playwright install chromium`;
|
|
26
|
+
|
|
27
|
+
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
28
|
+
console.log(HELP);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const positional = process.argv.slice(2).filter((a) => !a.startsWith("-"));
|
|
33
|
+
const input = positional[0] || "/tmp/candidate-report.html";
|
|
34
|
+
const output =
|
|
35
|
+
positional[1] || join(homedir(), "Desktop", "candidate-report.pdf");
|
|
36
|
+
|
|
37
|
+
const { chromium } = await import("playwright");
|
|
38
|
+
const browser = await chromium.launch();
|
|
39
|
+
const page = await browser.newPage();
|
|
40
|
+
await page.goto(`file://${resolve(input)}`, { waitUntil: "networkidle" });
|
|
41
|
+
await page.pdf({
|
|
42
|
+
path: output,
|
|
43
|
+
format: "A4",
|
|
44
|
+
printBackground: true,
|
|
45
|
+
margin: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
46
|
+
});
|
|
47
|
+
await browser.close();
|
|
48
|
+
console.log(`Done: ${output}`);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deck-create
|
|
3
|
+
description: Generate PDF slide decks from user requests using Playwright to render HTML slides to PDF. Use when the user asks to create a presentation, slide deck, or pitch deck. Pulls context from the knowledge base for company info, project details, and people.
|
|
4
|
+
compatibility: Requires Node.js installed. Playwright is installed on first use.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Create Presentations
|
|
8
|
+
|
|
9
|
+
Generate PDF slide decks from user requests. Uses Playwright to render HTML
|
|
10
|
+
slides to PDF. Can pull context from the knowledge base for company info,
|
|
11
|
+
project details, and people.
|
|
12
|
+
|
|
13
|
+
## Trigger
|
|
14
|
+
|
|
15
|
+
Run when the user asks to create a presentation, slide deck, or pitch deck.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- Node.js installed
|
|
20
|
+
- Playwright will be installed on first use
|
|
21
|
+
|
|
22
|
+
## Inputs
|
|
23
|
+
|
|
24
|
+
- User's description of the presentation
|
|
25
|
+
- `knowledge/` — optional context about company, product, team, projects
|
|
26
|
+
|
|
27
|
+
## Outputs
|
|
28
|
+
|
|
29
|
+
- `~/Desktop/presentation.pdf` — the generated PDF presentation
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Workflow
|
|
34
|
+
|
|
35
|
+
1. Check `knowledge/` for relevant context about the company, product, team,
|
|
36
|
+
etc.
|
|
37
|
+
2. Ensure Playwright is installed:
|
|
38
|
+
`bun install playwright && bunx playwright install chromium`
|
|
39
|
+
3. Create an HTML file at `/tmp/outpost-presentation.html` with slides
|
|
40
|
+
(1280x720px each)
|
|
41
|
+
4. Include the required CSS from [references/slide.css](references/slide.css)
|
|
42
|
+
5. Run the conversion script:
|
|
43
|
+
|
|
44
|
+
node scripts/convert-to-pdf.mjs
|
|
45
|
+
|
|
46
|
+
6. Tell the user: "Your presentation is ready at ~/Desktop/presentation.pdf"
|
|
47
|
+
|
|
48
|
+
**Do NOT show HTML code to the user. Just create the PDF and deliver it.**
|
|
49
|
+
|
|
50
|
+
The conversion script accepts optional arguments:
|
|
51
|
+
|
|
52
|
+
node scripts/convert-to-pdf.mjs [input.html] [output.pdf]
|
|
53
|
+
|
|
54
|
+
Defaults: input = `/tmp/outpost-presentation.html`, output =
|
|
55
|
+
`~/Desktop/presentation.pdf`
|
|
56
|
+
|
|
57
|
+
## PDF Rendering Rules
|
|
58
|
+
|
|
59
|
+
**These prevent rendering issues in PDF:**
|
|
60
|
+
|
|
61
|
+
1. **No layered elements** — Style content elements directly, no separate
|
|
62
|
+
background elements
|
|
63
|
+
2. **No box-shadow** — Use borders instead: `border: 1px solid #e5e7eb`
|
|
64
|
+
3. **Bullets via CSS only** — Use `li::before` pseudo-elements
|
|
65
|
+
4. **Content must fit** — Slides are 1280x720px with 60px padding. Safe area is
|
|
66
|
+
1160x600px. Use `overflow: hidden`
|
|
67
|
+
5. **No footers or headers** — No fixed/absolute positioned footer/header
|
|
68
|
+
elements
|
|
69
|
+
|
|
70
|
+
## Constraints
|
|
71
|
+
|
|
72
|
+
- Always use the knowledge base for context when available
|
|
73
|
+
- Output to `~/Desktop/presentation.pdf` unless user specifies otherwise
|
|
74
|
+
- Keep slides clean and readable — max 5-6 bullet points per slide
|
|
75
|
+
- Use consistent styling throughout
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* Required CSS for HTML slide decks rendered to PDF via Playwright.
|
|
2
|
+
*
|
|
3
|
+
* Include this in the <style> block of /tmp/outpost-presentation.html.
|
|
4
|
+
* See scripts/convert-to-pdf.mjs for the rendering script.
|
|
5
|
+
*
|
|
6
|
+
* PDF rendering rules:
|
|
7
|
+
* - No layered elements — style content directly, no separate backgrounds
|
|
8
|
+
* - No box-shadow — use borders: border: 1px solid #e5e7eb
|
|
9
|
+
* - Bullets via CSS only — use li::before pseudo-elements
|
|
10
|
+
* - Content must fit — safe area is 1160x600px (1280x720 minus 60px padding)
|
|
11
|
+
* - No footers or headers — no fixed/absolute positioned elements
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
@page {
|
|
15
|
+
size: 1280px 720px;
|
|
16
|
+
margin: 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
html {
|
|
20
|
+
-webkit-print-color-adjust: exact !important;
|
|
21
|
+
print-color-adjust: exact !important;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.slide {
|
|
25
|
+
width: 1280px;
|
|
26
|
+
height: 720px;
|
|
27
|
+
padding: 60px;
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
page-break-after: always;
|
|
30
|
+
page-break-inside: avoid;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.slide:last-child {
|
|
34
|
+
page-break-after: auto;
|
|
35
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Convert HTML slides to PDF using Playwright.
|
|
4
|
+
*
|
|
5
|
+
* Renders an HTML file containing slide markup (1280x720px per slide) into a
|
|
6
|
+
* PDF document. Each slide is sized to exactly 1280x720 pixels with background
|
|
7
|
+
* colours and images preserved. Defaults to reading from /tmp and writing to
|
|
8
|
+
* ~/Desktop when no arguments are given.
|
|
9
|
+
*
|
|
10
|
+
* Requires: bun install playwright && bunx playwright install chromium
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { resolve } from "node:path";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
|
|
17
|
+
const HELP = `convert-to-pdf — render HTML slides to PDF via Playwright
|
|
18
|
+
|
|
19
|
+
Usage: bun scripts/convert-to-pdf.mjs [input.html] [output.pdf] [-h|--help]
|
|
20
|
+
|
|
21
|
+
Arguments:
|
|
22
|
+
input.html HTML slides file (default: /tmp/outpost-presentation.html)
|
|
23
|
+
output.pdf Output PDF path (default: ~/Desktop/presentation.pdf)
|
|
24
|
+
|
|
25
|
+
Requires: bun install playwright && bunx playwright install chromium`;
|
|
26
|
+
|
|
27
|
+
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
28
|
+
console.log(HELP);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const positional = process.argv.slice(2).filter((a) => !a.startsWith("-"));
|
|
33
|
+
const input = positional[0] || "/tmp/outpost-presentation.html";
|
|
34
|
+
const output = positional[1] || join(homedir(), "Desktop", "presentation.pdf");
|
|
35
|
+
|
|
36
|
+
const { chromium } = await import("playwright");
|
|
37
|
+
const browser = await chromium.launch();
|
|
38
|
+
const page = await browser.newPage();
|
|
39
|
+
await page.goto(`file://${resolve(input)}`, { waitUntil: "networkidle" });
|
|
40
|
+
await page.pdf({
|
|
41
|
+
path: output,
|
|
42
|
+
width: "1280px",
|
|
43
|
+
height: "720px",
|
|
44
|
+
printBackground: true,
|
|
45
|
+
});
|
|
46
|
+
await browser.close();
|
|
47
|
+
console.log(`Done: ${output}`);
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deck-summarize
|
|
3
|
+
description: Synthesize PowerPoint decks into engineer-friendly markdown briefs covering Jobs-To-Be-Done, dependencies, and synthetic data needs. Use when the user asks to break down, summarize, or make sense of a slide deck (.pptx) for engineering work.
|
|
4
|
+
compatibility: Node.js only — no external dependencies.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Synthesize Deck
|
|
8
|
+
|
|
9
|
+
Turn messy PowerPoint specification decks into clear, actionable markdown briefs
|
|
10
|
+
that forward deployed engineers can build from. Strip business jargon and focus
|
|
11
|
+
on what matters: what needs to be built, what blocks progress, what data is
|
|
12
|
+
needed to start prototyping.
|
|
13
|
+
|
|
14
|
+
## Trigger
|
|
15
|
+
|
|
16
|
+
The user asks to summarize, synthesize, or break down a `.pptx` deck; make sense
|
|
17
|
+
of a specification or proposal deck for engineering; create an engineering brief
|
|
18
|
+
from a slide deck; or understand what a project deck is actually asking for.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
- Node.js 18+.
|
|
23
|
+
- Input files must be `.pptx`.
|
|
24
|
+
|
|
25
|
+
## Inputs
|
|
26
|
+
|
|
27
|
+
- One or more `.pptx` file paths.
|
|
28
|
+
- Optional: focus areas the engineer cares about.
|
|
29
|
+
|
|
30
|
+
## Outputs
|
|
31
|
+
|
|
32
|
+
- One markdown file per deck (or one combined file for related decks) written to
|
|
33
|
+
`knowledge/Projects/{Project Name} - Engineering Brief.md`.
|
|
34
|
+
|
|
35
|
+
<do_confirm_checklist goal="Verify the brief is engineer-actionable before
|
|
36
|
+
delivering">
|
|
37
|
+
|
|
38
|
+
- [ ] No invented requirements — every claim traces to the deck.
|
|
39
|
+
- [ ] Plain language; no marketing jargon (no "synergize", "orchestrate",
|
|
40
|
+
"leverage", "intelligent \_\_\_ hub").
|
|
41
|
+
- [ ] JTBDs describe the user's goal, not the proposed solution; one job per
|
|
42
|
+
statement; each includes the "so that".
|
|
43
|
+
- [ ] Data dependencies table flags blockers (missing, locked, compliance).
|
|
44
|
+
- [ ] Synthetic data needs name fields, ranges, edge cases, and volume.
|
|
45
|
+
- [ ] Gaps and open questions list what an engineer would notice missing.
|
|
46
|
+
- [ ] Brief is under 2,000 lines — a summary, not a transcription.
|
|
47
|
+
- [ ] Knowledge base looked up for mentioned people, orgs, and projects.
|
|
48
|
+
|
|
49
|
+
</do_confirm_checklist>
|
|
50
|
+
|
|
51
|
+
Output template and the data dependencies table:
|
|
52
|
+
[references/brief-template.md](references/brief-template.md).
|
|
53
|
+
|
|
54
|
+
## Procedure
|
|
55
|
+
|
|
56
|
+
### 1. Extract text
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
node .claude/skills/deck-summarize/scripts/extract-pptx.mjs "$FILE_PATH"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For multiple decks, pass all files at once. To save the extracted text:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
node .claude/skills/deck-summarize/scripts/extract-pptx.mjs "$FILE_PATH" -o /tmp/deck_extract.txt
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Read all extracted text before continuing.
|
|
69
|
+
|
|
70
|
+
### 2. Identify the core problem
|
|
71
|
+
|
|
72
|
+
Plain-language answers to: what process exists today; what's broken, slow, or
|
|
73
|
+
painful; who suffers. Don't restate the deck's framing.
|
|
74
|
+
|
|
75
|
+
### 3. Extract Jobs-To-Be-Done
|
|
76
|
+
|
|
77
|
+
Format: `When [situation], I need to [action], so that [outcome].`
|
|
78
|
+
|
|
79
|
+
Group by user role/persona. One job per statement. Use the user's goal, not the
|
|
80
|
+
proposed solution. A job should still make sense if you discard the deck's
|
|
81
|
+
solution. Don't restate the deck's feature list as jobs and don't reuse its
|
|
82
|
+
jargon.
|
|
83
|
+
|
|
84
|
+
### 4. Map dependencies
|
|
85
|
+
|
|
86
|
+
**4a. Data** — fill the table in
|
|
87
|
+
[references/brief-template.md](references/brief-template.md#data-dependencies-table).
|
|
88
|
+
Flag blockers (missing, locked, unstructured, compliance).
|
|
89
|
+
|
|
90
|
+
**4b. Systems & integrations** — every external system/API/platform: what the
|
|
91
|
+
integration does, read-only vs read-write, API vs manual/scraping, access
|
|
92
|
+
confirmed?
|
|
93
|
+
|
|
94
|
+
**4c. People & approvals** — approvals, reviews, or co-creation needed before
|
|
95
|
+
engineering can proceed. Flag long lead-time items (legal, compliance, vendor
|
|
96
|
+
contracts).
|
|
97
|
+
|
|
98
|
+
### 5. Define synthetic-data needs
|
|
99
|
+
|
|
100
|
+
For each core feature/use case:
|
|
101
|
+
|
|
102
|
+
- **Generate:** entity, key fields and types, realistic value ranges and
|
|
103
|
+
distributions, edge cases that matter, volume for meaningful testing.
|
|
104
|
+
- **Simulate:** workflows and state transitions, time-series patterns,
|
|
105
|
+
multi-actor interactions, error/failure modes.
|
|
106
|
+
- **Format:** prefer CSV/JSON; PII-shaped fake data only — never real PII;
|
|
107
|
+
include happy-path _and_ adversarial examples; consider ML training/eval data.
|
|
108
|
+
|
|
109
|
+
### 6. Translate the proposed solution
|
|
110
|
+
|
|
111
|
+
Describe the build in engineering terms: components, what each does in plain
|
|
112
|
+
terms, how they connect, end-to-end data flow, AI/ML capabilities and what
|
|
113
|
+
they're actually doing. Translate branded names — e.g. "Intelligent Intake Hub"
|
|
114
|
+
→ "OCR + NLP pipeline that extracts structured fields from scanned enrollment
|
|
115
|
+
forms"; "Copay Guardian" → "Anomaly detection on weekly claims data".
|
|
116
|
+
|
|
117
|
+
### 7. Identify what's missing
|
|
118
|
+
|
|
119
|
+
Call out: features without clear data sources; AI capabilities without a
|
|
120
|
+
training-data strategy; assumed integrations; user workflows that skip edge
|
|
121
|
+
cases; metrics promised without measurement infrastructure; timeline–scope
|
|
122
|
+
mismatches.
|
|
123
|
+
|
|
124
|
+
### 8. Assemble the brief
|
|
125
|
+
|
|
126
|
+
Use the structure in
|
|
127
|
+
[references/brief-template.md](references/brief-template.md). Save to
|
|
128
|
+
`knowledge/Projects/{Project Name} - Engineering Brief.md`. For multiple related
|
|
129
|
+
decks, write one combined brief with shared dependencies.
|
|
130
|
+
|
|
131
|
+
### 9. Save and report
|
|
132
|
+
|
|
133
|
+
Tell the user the file path and give a 3-sentence project summary.
|
|
134
|
+
|
|
135
|
+
## Writing style
|
|
136
|
+
|
|
137
|
+
Plain language, concrete over abstract, honest about uncertainty, opinionated
|
|
138
|
+
when helpful (flag dependency or timeline risks), short sentences. Engineers
|
|
139
|
+
scan, they don't read essays.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Engineering Brief Template
|
|
2
|
+
|
|
3
|
+
Reference template for `deck-summarize` Step 8. Save the assembled brief to
|
|
4
|
+
`knowledge/Projects/{Project Name} - Engineering Brief.md`.
|
|
5
|
+
|
|
6
|
+
```markdown
|
|
7
|
+
---
|
|
8
|
+
project: {Project Name}
|
|
9
|
+
source: {list of deck filenames}
|
|
10
|
+
date_synthesized: {today's date}
|
|
11
|
+
status: engineering-brief
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# {Project Name} — Engineering Brief
|
|
15
|
+
|
|
16
|
+
> One-paragraph plain-English summary of what this project is and why it
|
|
17
|
+
> exists.
|
|
18
|
+
|
|
19
|
+
## The Problem
|
|
20
|
+
|
|
21
|
+
{Step 2 output — what's broken, who it affects, why it matters}
|
|
22
|
+
|
|
23
|
+
## Jobs-To-Be-Done
|
|
24
|
+
|
|
25
|
+
### {Persona 1}
|
|
26
|
+
- When [situation], I need to [action], so that [outcome].
|
|
27
|
+
- ...
|
|
28
|
+
|
|
29
|
+
### {Persona 2}
|
|
30
|
+
- ...
|
|
31
|
+
|
|
32
|
+
## What They Want to Build
|
|
33
|
+
|
|
34
|
+
{Step 6 output — solution in engineering terms, components, data flow}
|
|
35
|
+
|
|
36
|
+
### System Architecture (Simplified)
|
|
37
|
+
|
|
38
|
+
{Text-based diagram or description of how components connect}
|
|
39
|
+
|
|
40
|
+
### AI/ML Components
|
|
41
|
+
|
|
42
|
+
{What models/capabilities are needed, what they do, what data they need}
|
|
43
|
+
|
|
44
|
+
## Dependencies
|
|
45
|
+
|
|
46
|
+
### Data
|
|
47
|
+
{Step 4a table}
|
|
48
|
+
|
|
49
|
+
### Systems & Integrations
|
|
50
|
+
{Step 4b list}
|
|
51
|
+
|
|
52
|
+
### People & Approvals
|
|
53
|
+
{Step 4c list}
|
|
54
|
+
|
|
55
|
+
## Synthetic Data for Prototyping
|
|
56
|
+
|
|
57
|
+
### {Feature/Use Case 1}
|
|
58
|
+
{Step 5 output}
|
|
59
|
+
|
|
60
|
+
### {Feature/Use Case 2}
|
|
61
|
+
{Step 5 output}
|
|
62
|
+
|
|
63
|
+
## Gaps & Open Questions
|
|
64
|
+
|
|
65
|
+
{Step 7 output as a numbered list}
|
|
66
|
+
|
|
67
|
+
## Phasing
|
|
68
|
+
|
|
69
|
+
{Timeline and wave structure from the deck, with engineering commentary on
|
|
70
|
+
what's realistic and what depends on what}
|
|
71
|
+
|
|
72
|
+
## Key Metrics
|
|
73
|
+
|
|
74
|
+
{What success looks like, translated into measurable engineering terms}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Data dependencies table
|
|
78
|
+
|
|
79
|
+
For Step 4a, fill in this table. Flag any data that doesn't seem to exist yet,
|
|
80
|
+
is locked behind inaccessible systems, is unstructured and would need heavy
|
|
81
|
+
preprocessing, or carries compliance/privacy constraints.
|
|
82
|
+
|
|
83
|
+
| Data | Where It Lives | Format | Access | Blocker? |
|
|
84
|
+
| ----------- | -------------------------- | ---------------------------------- | ------------------------------- | -------------- |
|
|
85
|
+
| _What data_ | _System/team that owns it_ | _Structured/unstructured/API/file_ | _Do we have it? Can we get it?_ | _Yes/No + why_ |
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Extract text from PowerPoint (.pptx) slides.
|
|
4
|
+
*
|
|
5
|
+
* PPTX files are ZIP archives containing XML. This script extracts all text
|
|
6
|
+
* from each slide and outputs it as structured markdown with slide headings.
|
|
7
|
+
* Handles multiple files and outputs to stdout or a file.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/extract-pptx.mjs <path-to-pptx>
|
|
11
|
+
* node scripts/extract-pptx.mjs <path-to-pptx> -o /tmp/extract.txt
|
|
12
|
+
* node scripts/extract-pptx.mjs file1.pptx file2.pptx
|
|
13
|
+
* node scripts/extract-pptx.mjs -h|--help
|
|
14
|
+
*
|
|
15
|
+
* No external dependencies — uses Node.js built-in modules only.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { basename } from "node:path";
|
|
20
|
+
|
|
21
|
+
const HELP = `extract-pptx — extract slide text from .pptx files
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
node scripts/extract-pptx.mjs <file.pptx> [file2.pptx ...]
|
|
25
|
+
node scripts/extract-pptx.mjs <file.pptx> -o <output.txt>
|
|
26
|
+
node scripts/extract-pptx.mjs -h|--help
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
-o <path> Write output to file instead of stdout
|
|
30
|
+
-h, --help Show this help
|
|
31
|
+
|
|
32
|
+
Output: Markdown-formatted text with ## Slide N headings per slide.
|
|
33
|
+
Multiple files get # Deck: filename.pptx headings.`;
|
|
34
|
+
|
|
35
|
+
if (
|
|
36
|
+
process.argv.includes("-h") ||
|
|
37
|
+
process.argv.includes("--help") ||
|
|
38
|
+
process.argv.length < 3
|
|
39
|
+
) {
|
|
40
|
+
console.log(HELP);
|
|
41
|
+
process.exit(process.argv.length < 3 ? 1 : 0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// --- Parse arguments ---
|
|
45
|
+
|
|
46
|
+
const args = process.argv.slice(2);
|
|
47
|
+
let outputPath = null;
|
|
48
|
+
const files = [];
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < args.length; i++) {
|
|
51
|
+
if (args[i] === "-o" && i + 1 < args.length) {
|
|
52
|
+
outputPath = args[++i];
|
|
53
|
+
} else if (!args[i].startsWith("-")) {
|
|
54
|
+
files.push(args[i]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (files.length === 0) {
|
|
59
|
+
console.error("Error: no .pptx files provided");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// --- ZIP parsing (no dependencies) ---
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Parse a ZIP file's central directory to extract file entries.
|
|
67
|
+
* @param {Buffer} buf
|
|
68
|
+
* @returns {Array<{name: string, offset: number, compressedSize: number, compressionMethod: number}>}
|
|
69
|
+
*/
|
|
70
|
+
function parseZipEntries(buf) {
|
|
71
|
+
// Find End of Central Directory record (signature 0x06054b50)
|
|
72
|
+
let eocdOffset = -1;
|
|
73
|
+
for (let i = buf.length - 22; i >= 0; i--) {
|
|
74
|
+
if (
|
|
75
|
+
buf[i] === 0x50 &&
|
|
76
|
+
buf[i + 1] === 0x4b &&
|
|
77
|
+
buf[i + 2] === 0x05 &&
|
|
78
|
+
buf[i + 3] === 0x06
|
|
79
|
+
) {
|
|
80
|
+
eocdOffset = i;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (eocdOffset === -1) throw new Error("Not a valid ZIP file");
|
|
85
|
+
|
|
86
|
+
const cdOffset = buf.readUInt32LE(eocdOffset + 16);
|
|
87
|
+
const cdEntries = buf.readUInt16LE(eocdOffset + 10);
|
|
88
|
+
|
|
89
|
+
const entries = [];
|
|
90
|
+
let pos = cdOffset;
|
|
91
|
+
|
|
92
|
+
for (let e = 0; e < cdEntries; e++) {
|
|
93
|
+
// Central directory file header signature: 0x02014b50
|
|
94
|
+
if (buf.readUInt32LE(pos) !== 0x02014b50) break;
|
|
95
|
+
|
|
96
|
+
const compressionMethod = buf.readUInt16LE(pos + 10);
|
|
97
|
+
const compressedSize = buf.readUInt32LE(pos + 20);
|
|
98
|
+
const nameLen = buf.readUInt16LE(pos + 28);
|
|
99
|
+
const extraLen = buf.readUInt16LE(pos + 30);
|
|
100
|
+
const commentLen = buf.readUInt16LE(pos + 32);
|
|
101
|
+
const localHeaderOffset = buf.readUInt32LE(pos + 42);
|
|
102
|
+
const name = buf.toString("utf8", pos + 46, pos + 46 + nameLen);
|
|
103
|
+
|
|
104
|
+
entries.push({
|
|
105
|
+
name,
|
|
106
|
+
offset: localHeaderOffset,
|
|
107
|
+
compressedSize,
|
|
108
|
+
compressionMethod,
|
|
109
|
+
});
|
|
110
|
+
pos += 46 + nameLen + extraLen + commentLen;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return entries;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { inflateRawSync } = await import("node:zlib");
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Read the uncompressed content of a ZIP entry.
|
|
120
|
+
* @param {Buffer} buf
|
|
121
|
+
* @param {{offset: number, compressedSize: number, compressionMethod: number}} entry
|
|
122
|
+
* @returns {Buffer}
|
|
123
|
+
*/
|
|
124
|
+
function readEntry(buf, entry) {
|
|
125
|
+
const pos = entry.offset;
|
|
126
|
+
const nameLen = buf.readUInt16LE(pos + 26);
|
|
127
|
+
const extraLen = buf.readUInt16LE(pos + 28);
|
|
128
|
+
const dataStart = pos + 30 + nameLen + extraLen;
|
|
129
|
+
const raw = buf.subarray(dataStart, dataStart + entry.compressedSize);
|
|
130
|
+
|
|
131
|
+
if (entry.compressionMethod === 0) return raw;
|
|
132
|
+
if (entry.compressionMethod === 8) return inflateRawSync(raw);
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Unsupported compression method ${entry.compressionMethod} for ${entry.name}`,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Extract all text content from slide XML using the DrawingML namespace.
|
|
140
|
+
* Matches <a:t>text</a:t> elements used by PowerPoint.
|
|
141
|
+
* @param {string} xml
|
|
142
|
+
* @returns {string[]}
|
|
143
|
+
*/
|
|
144
|
+
function extractTextFromXml(xml) {
|
|
145
|
+
const texts = [];
|
|
146
|
+
const re = /<a:t>([^<]*)<\/a:t>/g;
|
|
147
|
+
let match;
|
|
148
|
+
while ((match = re.exec(xml)) !== null) {
|
|
149
|
+
const text = match[1].trim();
|
|
150
|
+
if (text) texts.push(text);
|
|
151
|
+
}
|
|
152
|
+
return texts;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Extract slide text from a .pptx file.
|
|
157
|
+
* @param {string} filePath
|
|
158
|
+
* @returns {string} Markdown-formatted slide text
|
|
159
|
+
*/
|
|
160
|
+
function extractPptx(filePath) {
|
|
161
|
+
const buf = readFileSync(filePath);
|
|
162
|
+
const entries = parseZipEntries(buf);
|
|
163
|
+
|
|
164
|
+
// Find slide XML files and sort by slide number
|
|
165
|
+
const slideEntries = entries
|
|
166
|
+
.filter(
|
|
167
|
+
(e) => e.name.startsWith("ppt/slides/slide") && e.name.endsWith(".xml"),
|
|
168
|
+
)
|
|
169
|
+
.sort((a, b) => {
|
|
170
|
+
const numA = parseInt(a.name.match(/(\d+)/)?.[1] || "0", 10);
|
|
171
|
+
const numB = parseInt(b.name.match(/(\d+)/)?.[1] || "0", 10);
|
|
172
|
+
return numA - numB;
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const lines = [];
|
|
176
|
+
|
|
177
|
+
for (const entry of slideEntries) {
|
|
178
|
+
const xml = readEntry(buf, entry).toString("utf8");
|
|
179
|
+
const texts = extractTextFromXml(xml);
|
|
180
|
+
|
|
181
|
+
if (texts.length > 0) {
|
|
182
|
+
const num = entry.name.match(/(\d+)/)?.[1] || "?";
|
|
183
|
+
lines.push(`## Slide ${num}`);
|
|
184
|
+
lines.push(texts.join("\n"));
|
|
185
|
+
lines.push("");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return lines.join("\n");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// --- Main ---
|
|
193
|
+
|
|
194
|
+
const outputs = [];
|
|
195
|
+
|
|
196
|
+
for (const file of files) {
|
|
197
|
+
if (files.length > 1) {
|
|
198
|
+
outputs.push(`# Deck: ${basename(file)}\n`);
|
|
199
|
+
}
|
|
200
|
+
outputs.push(extractPptx(file));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const result = outputs.join("\n");
|
|
204
|
+
|
|
205
|
+
if (outputPath) {
|
|
206
|
+
writeFileSync(outputPath, result);
|
|
207
|
+
console.log(`Extracted ${files.length} deck(s) → ${outputPath}`);
|
|
208
|
+
} else {
|
|
209
|
+
process.stdout.write(result);
|
|
210
|
+
}
|