stagent 0.1.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.
Files changed (333) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +399 -0
  3. package/components.json +21 -0
  4. package/dist/cli.js +171 -0
  5. package/drizzle.config.ts +12 -0
  6. package/next.config.mjs +15 -0
  7. package/package.json +114 -0
  8. package/postcss.config.mjs +8 -0
  9. package/public/icon-512.png +0 -0
  10. package/public/icon.svg +13 -0
  11. package/public/readme/home-workspace.png +0 -0
  12. package/public/readme/inbox-approvals.png +0 -0
  13. package/public/readme/workflow-blueprints.png +0 -0
  14. package/public/stagent-s-128.png +0 -0
  15. package/public/stagent-s-64.png +0 -0
  16. package/src/app/api/blueprints/[id]/instantiate/route.ts +27 -0
  17. package/src/app/api/blueprints/[id]/route.ts +39 -0
  18. package/src/app/api/blueprints/import/route.ts +68 -0
  19. package/src/app/api/blueprints/route.ts +29 -0
  20. package/src/app/api/command-palette/recent/route.ts +31 -0
  21. package/src/app/api/data/clear/route.ts +22 -0
  22. package/src/app/api/data/seed/route.ts +22 -0
  23. package/src/app/api/documents/[id]/file/route.ts +44 -0
  24. package/src/app/api/documents/[id]/route.ts +123 -0
  25. package/src/app/api/documents/route.ts +59 -0
  26. package/src/app/api/logs/stream/route.ts +101 -0
  27. package/src/app/api/notifications/[id]/route.ts +36 -0
  28. package/src/app/api/notifications/mark-all-read/route.ts +13 -0
  29. package/src/app/api/notifications/pending-approvals/route.ts +10 -0
  30. package/src/app/api/notifications/pending-approvals/stream/route.ts +101 -0
  31. package/src/app/api/notifications/route.ts +34 -0
  32. package/src/app/api/permissions/route.ts +46 -0
  33. package/src/app/api/profiles/[id]/route.ts +79 -0
  34. package/src/app/api/profiles/[id]/test/route.ts +42 -0
  35. package/src/app/api/profiles/import/route.ts +108 -0
  36. package/src/app/api/profiles/route.ts +50 -0
  37. package/src/app/api/projects/[id]/route.ts +72 -0
  38. package/src/app/api/projects/route.ts +53 -0
  39. package/src/app/api/schedules/[id]/route.ts +185 -0
  40. package/src/app/api/schedules/route.ts +117 -0
  41. package/src/app/api/settings/budgets/route.ts +24 -0
  42. package/src/app/api/settings/openai/route.ts +24 -0
  43. package/src/app/api/settings/route.ts +21 -0
  44. package/src/app/api/settings/test/route.ts +26 -0
  45. package/src/app/api/tasks/[id]/cancel/route.ts +21 -0
  46. package/src/app/api/tasks/[id]/execute/route.ts +90 -0
  47. package/src/app/api/tasks/[id]/logs/route.ts +95 -0
  48. package/src/app/api/tasks/[id]/output/route.ts +47 -0
  49. package/src/app/api/tasks/[id]/respond/route.ts +64 -0
  50. package/src/app/api/tasks/[id]/resume/route.ts +76 -0
  51. package/src/app/api/tasks/[id]/route.ts +77 -0
  52. package/src/app/api/tasks/assist/route.ts +35 -0
  53. package/src/app/api/tasks/route.ts +82 -0
  54. package/src/app/api/uploads/[id]/route.ts +81 -0
  55. package/src/app/api/uploads/cleanup/route.ts +7 -0
  56. package/src/app/api/uploads/route.ts +66 -0
  57. package/src/app/api/workflows/[id]/execute/route.ts +82 -0
  58. package/src/app/api/workflows/[id]/route.ts +133 -0
  59. package/src/app/api/workflows/[id]/status/route.ts +54 -0
  60. package/src/app/api/workflows/[id]/steps/[stepId]/retry/route.ts +22 -0
  61. package/src/app/api/workflows/route.ts +61 -0
  62. package/src/app/apple-icon.tsx +31 -0
  63. package/src/app/costs/page.tsx +256 -0
  64. package/src/app/dashboard/page.tsx +44 -0
  65. package/src/app/documents/[id]/page.tsx +46 -0
  66. package/src/app/documents/page.tsx +45 -0
  67. package/src/app/error.tsx +26 -0
  68. package/src/app/global-error.tsx +23 -0
  69. package/src/app/globals.css +733 -0
  70. package/src/app/icon.tsx +30 -0
  71. package/src/app/inbox/loading.tsx +15 -0
  72. package/src/app/inbox/page.tsx +35 -0
  73. package/src/app/layout.tsx +78 -0
  74. package/src/app/manifest.ts +32 -0
  75. package/src/app/monitor/page.tsx +37 -0
  76. package/src/app/page.tsx +162 -0
  77. package/src/app/profiles/[id]/edit/page.tsx +39 -0
  78. package/src/app/profiles/[id]/page.tsx +33 -0
  79. package/src/app/profiles/new/page.tsx +22 -0
  80. package/src/app/profiles/page.tsx +19 -0
  81. package/src/app/projects/[id]/page.tsx +134 -0
  82. package/src/app/projects/loading.tsx +17 -0
  83. package/src/app/projects/page.tsx +32 -0
  84. package/src/app/schedules/[id]/page.tsx +47 -0
  85. package/src/app/schedules/page.tsx +18 -0
  86. package/src/app/settings/loading.tsx +24 -0
  87. package/src/app/settings/page.tsx +27 -0
  88. package/src/app/tasks/[id]/page.tsx +45 -0
  89. package/src/app/tasks/new/page.tsx +27 -0
  90. package/src/app/workflows/[id]/edit/page.tsx +66 -0
  91. package/src/app/workflows/[id]/page.tsx +37 -0
  92. package/src/app/workflows/blueprints/[id]/page.tsx +40 -0
  93. package/src/app/workflows/blueprints/new/page.tsx +20 -0
  94. package/src/app/workflows/blueprints/page.tsx +11 -0
  95. package/src/app/workflows/new/page.tsx +36 -0
  96. package/src/app/workflows/page.tsx +18 -0
  97. package/src/components/charts/donut-ring.tsx +64 -0
  98. package/src/components/charts/mini-bar.tsx +75 -0
  99. package/src/components/charts/sparkline.tsx +107 -0
  100. package/src/components/costs/cost-dashboard.tsx +877 -0
  101. package/src/components/costs/cost-filters.tsx +179 -0
  102. package/src/components/dashboard/activity-feed.tsx +95 -0
  103. package/src/components/dashboard/greeting.tsx +30 -0
  104. package/src/components/dashboard/priority-queue.tsx +79 -0
  105. package/src/components/dashboard/quick-actions.tsx +62 -0
  106. package/src/components/dashboard/recent-projects.tsx +79 -0
  107. package/src/components/dashboard/stats-cards.tsx +114 -0
  108. package/src/components/documents/document-browser.tsx +235 -0
  109. package/src/components/documents/document-detail-view.tsx +367 -0
  110. package/src/components/documents/document-grid.tsx +78 -0
  111. package/src/components/documents/document-preview.tsx +68 -0
  112. package/src/components/documents/document-table.tsx +119 -0
  113. package/src/components/documents/document-upload-dialog.tsx +153 -0
  114. package/src/components/documents/types.ts +6 -0
  115. package/src/components/documents/utils.ts +57 -0
  116. package/src/components/monitoring/connection-indicator.tsx +14 -0
  117. package/src/components/monitoring/log-entry.tsx +79 -0
  118. package/src/components/monitoring/log-filters.tsx +57 -0
  119. package/src/components/monitoring/log-stream.tsx +144 -0
  120. package/src/components/monitoring/monitor-overview-wrapper.tsx +64 -0
  121. package/src/components/monitoring/monitor-overview.tsx +119 -0
  122. package/src/components/notifications/failure-action.tsx +38 -0
  123. package/src/components/notifications/inbox-list.tsx +165 -0
  124. package/src/components/notifications/message-response.tsx +196 -0
  125. package/src/components/notifications/notification-item.tsx +250 -0
  126. package/src/components/notifications/pending-approval-host.tsx +478 -0
  127. package/src/components/notifications/permission-action.tsx +37 -0
  128. package/src/components/notifications/permission-response-actions.tsx +126 -0
  129. package/src/components/notifications/unread-badge.tsx +35 -0
  130. package/src/components/profiles/profile-browser.tsx +117 -0
  131. package/src/components/profiles/profile-card.tsx +78 -0
  132. package/src/components/profiles/profile-detail-view.tsx +564 -0
  133. package/src/components/profiles/profile-form-view.tsx +480 -0
  134. package/src/components/profiles/profile-import-dialog.tsx +113 -0
  135. package/src/components/projects/project-card.tsx +58 -0
  136. package/src/components/projects/project-create-dialog.tsx +140 -0
  137. package/src/components/projects/project-detail.tsx +68 -0
  138. package/src/components/projects/project-edit-dialog.tsx +219 -0
  139. package/src/components/projects/project-list.tsx +108 -0
  140. package/src/components/schedules/schedule-create-dialog.tsx +403 -0
  141. package/src/components/schedules/schedule-detail-view.tsx +274 -0
  142. package/src/components/schedules/schedule-list.tsx +242 -0
  143. package/src/components/schedules/schedule-status-badge.tsx +16 -0
  144. package/src/components/settings/api-key-form.tsx +141 -0
  145. package/src/components/settings/auth-config-section.tsx +141 -0
  146. package/src/components/settings/auth-method-selector.tsx +67 -0
  147. package/src/components/settings/auth-status-badge.tsx +40 -0
  148. package/src/components/settings/auth-status-dot.tsx +59 -0
  149. package/src/components/settings/budget-guardrails-section.tsx +842 -0
  150. package/src/components/settings/data-management-section.tsx +141 -0
  151. package/src/components/settings/openai-runtime-section.tsx +104 -0
  152. package/src/components/settings/permissions-section.tsx +91 -0
  153. package/src/components/shared/app-sidebar.tsx +123 -0
  154. package/src/components/shared/card-skeleton.tsx +42 -0
  155. package/src/components/shared/command-palette.tsx +250 -0
  156. package/src/components/shared/confirm-dialog.tsx +52 -0
  157. package/src/components/shared/empty-state.tsx +24 -0
  158. package/src/components/shared/error-state.tsx +32 -0
  159. package/src/components/shared/form-section-card.tsx +33 -0
  160. package/src/components/shared/section-heading.tsx +14 -0
  161. package/src/components/shared/stagent-logo.tsx +21 -0
  162. package/src/components/shared/theme-toggle.tsx +46 -0
  163. package/src/components/tasks/ai-assist-panel.tsx +210 -0
  164. package/src/components/tasks/content-preview.tsx +89 -0
  165. package/src/components/tasks/empty-board.tsx +12 -0
  166. package/src/components/tasks/file-upload.tsx +120 -0
  167. package/src/components/tasks/kanban-board.tsx +275 -0
  168. package/src/components/tasks/kanban-column.tsx +75 -0
  169. package/src/components/tasks/skeleton-board.tsx +21 -0
  170. package/src/components/tasks/task-attachments.tsx +114 -0
  171. package/src/components/tasks/task-card.tsx +101 -0
  172. package/src/components/tasks/task-create-panel.tsx +360 -0
  173. package/src/components/tasks/task-detail-view.tsx +356 -0
  174. package/src/components/ui/alert-dialog.tsx +196 -0
  175. package/src/components/ui/badge.tsx +50 -0
  176. package/src/components/ui/button.tsx +71 -0
  177. package/src/components/ui/card.tsx +92 -0
  178. package/src/components/ui/checkbox.tsx +32 -0
  179. package/src/components/ui/command.tsx +184 -0
  180. package/src/components/ui/dialog.tsx +158 -0
  181. package/src/components/ui/dropdown-menu.tsx +257 -0
  182. package/src/components/ui/form.tsx +167 -0
  183. package/src/components/ui/input.tsx +21 -0
  184. package/src/components/ui/label.tsx +24 -0
  185. package/src/components/ui/popover.tsx +89 -0
  186. package/src/components/ui/progress.tsx +31 -0
  187. package/src/components/ui/radio-group.tsx +45 -0
  188. package/src/components/ui/scroll-area.tsx +58 -0
  189. package/src/components/ui/select.tsx +190 -0
  190. package/src/components/ui/separator.tsx +28 -0
  191. package/src/components/ui/sheet.tsx +143 -0
  192. package/src/components/ui/sidebar.tsx +726 -0
  193. package/src/components/ui/skeleton.tsx +13 -0
  194. package/src/components/ui/slider.tsx +63 -0
  195. package/src/components/ui/sonner.tsx +36 -0
  196. package/src/components/ui/switch.tsx +35 -0
  197. package/src/components/ui/table.tsx +116 -0
  198. package/src/components/ui/tabs.tsx +91 -0
  199. package/src/components/ui/textarea.tsx +18 -0
  200. package/src/components/ui/tooltip.tsx +57 -0
  201. package/src/components/workflows/blueprint-editor.tsx +109 -0
  202. package/src/components/workflows/blueprint-gallery.tsx +155 -0
  203. package/src/components/workflows/blueprint-preview.tsx +240 -0
  204. package/src/components/workflows/loop-status-view.tsx +272 -0
  205. package/src/components/workflows/swarm-dashboard.tsx +185 -0
  206. package/src/components/workflows/workflow-form-view.tsx +1376 -0
  207. package/src/components/workflows/workflow-list.tsx +230 -0
  208. package/src/components/workflows/workflow-status-view.tsx +477 -0
  209. package/src/hooks/use-mobile.ts +19 -0
  210. package/src/instrumentation.ts +7 -0
  211. package/src/lib/agents/claude-agent.ts +737 -0
  212. package/src/lib/agents/execution-manager.ts +27 -0
  213. package/src/lib/agents/profiles/assignment-validation.ts +75 -0
  214. package/src/lib/agents/profiles/builtins/code-reviewer/SKILL.md +21 -0
  215. package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +28 -0
  216. package/src/lib/agents/profiles/builtins/data-analyst/SKILL.md +25 -0
  217. package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +27 -0
  218. package/src/lib/agents/profiles/builtins/devops-engineer/SKILL.md +34 -0
  219. package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +27 -0
  220. package/src/lib/agents/profiles/builtins/document-writer/SKILL.md +16 -0
  221. package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +27 -0
  222. package/src/lib/agents/profiles/builtins/general/SKILL.md +13 -0
  223. package/src/lib/agents/profiles/builtins/general/profile.yaml +18 -0
  224. package/src/lib/agents/profiles/builtins/health-fitness-coach/SKILL.md +34 -0
  225. package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +26 -0
  226. package/src/lib/agents/profiles/builtins/learning-coach/SKILL.md +35 -0
  227. package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +26 -0
  228. package/src/lib/agents/profiles/builtins/project-manager/SKILL.md +26 -0
  229. package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +26 -0
  230. package/src/lib/agents/profiles/builtins/researcher/SKILL.md +15 -0
  231. package/src/lib/agents/profiles/builtins/researcher/profile.yaml +27 -0
  232. package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +34 -0
  233. package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +26 -0
  234. package/src/lib/agents/profiles/builtins/technical-writer/SKILL.md +31 -0
  235. package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +29 -0
  236. package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +23 -0
  237. package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +26 -0
  238. package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +24 -0
  239. package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +26 -0
  240. package/src/lib/agents/profiles/compatibility.ts +109 -0
  241. package/src/lib/agents/profiles/registry.ts +293 -0
  242. package/src/lib/agents/profiles/test-runner.ts +18 -0
  243. package/src/lib/agents/profiles/test-types.ts +20 -0
  244. package/src/lib/agents/profiles/types.ts +43 -0
  245. package/src/lib/agents/router.ts +56 -0
  246. package/src/lib/agents/runtime/catalog.ts +85 -0
  247. package/src/lib/agents/runtime/claude-sdk.ts +12 -0
  248. package/src/lib/agents/runtime/claude.ts +370 -0
  249. package/src/lib/agents/runtime/codex-app-server-client.ts +289 -0
  250. package/src/lib/agents/runtime/index.ts +167 -0
  251. package/src/lib/agents/runtime/openai-codex.ts +1089 -0
  252. package/src/lib/agents/runtime/task-assist-types.ts +8 -0
  253. package/src/lib/agents/runtime/types.ts +30 -0
  254. package/src/lib/constants/settings.ts +13 -0
  255. package/src/lib/constants/status-colors.ts +44 -0
  256. package/src/lib/constants/task-status.ts +49 -0
  257. package/src/lib/data/clear.ts +63 -0
  258. package/src/lib/data/seed-data/documents.ts +715 -0
  259. package/src/lib/data/seed-data/logs.ts +195 -0
  260. package/src/lib/data/seed-data/notifications.ts +141 -0
  261. package/src/lib/data/seed-data/profiles.ts +175 -0
  262. package/src/lib/data/seed-data/projects.ts +61 -0
  263. package/src/lib/data/seed-data/schedules.ts +108 -0
  264. package/src/lib/data/seed-data/tasks.ts +341 -0
  265. package/src/lib/data/seed-data/usage-ledger.ts +130 -0
  266. package/src/lib/data/seed-data/workflows.ts +213 -0
  267. package/src/lib/data/seed.ts +129 -0
  268. package/src/lib/db/index.ts +221 -0
  269. package/src/lib/db/migrations/0000_aromatic_gargoyle.sql +59 -0
  270. package/src/lib/db/migrations/0001_first_iron_patriot.sql +6 -0
  271. package/src/lib/db/migrations/0002_add_resume_count.sql +1 -0
  272. package/src/lib/db/migrations/0003_add_settings.sql +5 -0
  273. package/src/lib/db/migrations/0004_add_documents.sql +20 -0
  274. package/src/lib/db/migrations/0005_add_document_preprocessing.sql +4 -0
  275. package/src/lib/db/migrations/0006_add_agent_profile.sql +2 -0
  276. package/src/lib/db/migrations/0007_add_usage_metering_ledger.sql +30 -0
  277. package/src/lib/db/migrations/0008_add_document_version.sql +1 -0
  278. package/src/lib/db/migrations/meta/0000_snapshot.json +416 -0
  279. package/src/lib/db/migrations/meta/0001_snapshot.json +461 -0
  280. package/src/lib/db/migrations/meta/0002_snapshot.json +469 -0
  281. package/src/lib/db/migrations/meta/_journal.json +27 -0
  282. package/src/lib/db/schema.ts +227 -0
  283. package/src/lib/documents/cleanup.ts +50 -0
  284. package/src/lib/documents/context-builder.ts +75 -0
  285. package/src/lib/documents/output-scanner.ts +166 -0
  286. package/src/lib/documents/processor.ts +120 -0
  287. package/src/lib/documents/processors/image.ts +21 -0
  288. package/src/lib/documents/processors/office.ts +36 -0
  289. package/src/lib/documents/processors/pdf.ts +12 -0
  290. package/src/lib/documents/processors/spreadsheet.ts +18 -0
  291. package/src/lib/documents/processors/text.ts +8 -0
  292. package/src/lib/documents/registry.ts +25 -0
  293. package/src/lib/notifications/actionable.ts +108 -0
  294. package/src/lib/notifications/permissions.ts +169 -0
  295. package/src/lib/queries/chart-data.ts +184 -0
  296. package/src/lib/schedules/interval-parser.ts +110 -0
  297. package/src/lib/schedules/scheduler.ts +220 -0
  298. package/src/lib/settings/auth.ts +98 -0
  299. package/src/lib/settings/budget-guardrails.ts +590 -0
  300. package/src/lib/settings/helpers.ts +23 -0
  301. package/src/lib/settings/openai-auth.ts +80 -0
  302. package/src/lib/settings/permissions.ts +102 -0
  303. package/src/lib/usage/ledger.ts +489 -0
  304. package/src/lib/usage/pricing.ts +68 -0
  305. package/src/lib/utils/crypto.ts +90 -0
  306. package/src/lib/utils/format-timestamp.ts +46 -0
  307. package/src/lib/utils/session-cleanup.ts +26 -0
  308. package/src/lib/utils/stagent-paths.ts +18 -0
  309. package/src/lib/utils.ts +6 -0
  310. package/src/lib/validators/blueprint.ts +43 -0
  311. package/src/lib/validators/profile.ts +64 -0
  312. package/src/lib/validators/project.ts +17 -0
  313. package/src/lib/validators/settings.ts +57 -0
  314. package/src/lib/validators/task.ts +30 -0
  315. package/src/lib/workflows/blueprints/builtins/code-review-pipeline.yaml +72 -0
  316. package/src/lib/workflows/blueprints/builtins/documentation-generation.yaml +62 -0
  317. package/src/lib/workflows/blueprints/builtins/investment-research.yaml +81 -0
  318. package/src/lib/workflows/blueprints/builtins/meal-planning.yaml +73 -0
  319. package/src/lib/workflows/blueprints/builtins/product-research.yaml +72 -0
  320. package/src/lib/workflows/blueprints/builtins/research-report.yaml +77 -0
  321. package/src/lib/workflows/blueprints/builtins/sprint-planning.yaml +77 -0
  322. package/src/lib/workflows/blueprints/builtins/travel-planning.yaml +80 -0
  323. package/src/lib/workflows/blueprints/instantiator.ts +131 -0
  324. package/src/lib/workflows/blueprints/registry.ts +128 -0
  325. package/src/lib/workflows/blueprints/template.ts +58 -0
  326. package/src/lib/workflows/blueprints/types.ts +38 -0
  327. package/src/lib/workflows/definition-validation.ts +121 -0
  328. package/src/lib/workflows/engine.ts +1113 -0
  329. package/src/lib/workflows/loop-executor.ts +270 -0
  330. package/src/lib/workflows/parallel.ts +55 -0
  331. package/src/lib/workflows/swarm.ts +97 -0
  332. package/src/lib/workflows/types.ts +112 -0
  333. package/tsconfig.json +41 -0
@@ -0,0 +1,715 @@
1
+ import { writeFileSync, mkdirSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+
5
+ const uploadsDir = join(
6
+ process.env.STAGENT_DATA_DIR || join(homedir(), ".stagent"),
7
+ "uploads"
8
+ );
9
+
10
+ export interface DocumentSeed {
11
+ id: string;
12
+ taskId: string;
13
+ projectId: string;
14
+ filename: string;
15
+ originalName: string;
16
+ mimeType: string;
17
+ size: number;
18
+ storagePath: string;
19
+ direction: "input";
20
+ status: "uploaded";
21
+ createdAt: Date;
22
+ updatedAt: Date;
23
+ }
24
+
25
+ interface DocumentDef {
26
+ originalName: string;
27
+ mimeType: string;
28
+ projectIndex: number;
29
+ taskIndex: number;
30
+ content: string | (() => Promise<Buffer>);
31
+ }
32
+
33
+ // --- Binary file generators ---
34
+
35
+ /** Create a minimal valid DOCX (Open XML) using JSZip */
36
+ async function createDocx(textContent: string): Promise<Buffer> {
37
+ const JSZip = (await import("jszip")).default;
38
+ const zip = new JSZip();
39
+
40
+ zip.file(
41
+ "[Content_Types].xml",
42
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
44
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
45
+ <Default Extension="xml" ContentType="application/xml"/>
46
+ <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
47
+ </Types>`
48
+ );
49
+
50
+ zip.file(
51
+ "_rels/.rels",
52
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
53
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
54
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
55
+ </Relationships>`
56
+ );
57
+
58
+ const paragraphs = textContent
59
+ .split("\n")
60
+ .map(
61
+ (line) =>
62
+ `<w:p><w:r><w:t xml:space="preserve">${escapeXml(line)}</w:t></w:r></w:p>`
63
+ )
64
+ .join("");
65
+
66
+ zip.file(
67
+ "word/document.xml",
68
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
69
+ <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
70
+ <w:body>${paragraphs}</w:body>
71
+ </w:document>`
72
+ );
73
+
74
+ const buf = await zip.generateAsync({ type: "nodebuffer" });
75
+ return Buffer.from(buf);
76
+ }
77
+
78
+ /** Create a minimal valid PPTX using JSZip */
79
+ async function createPptx(slides: string[]): Promise<Buffer> {
80
+ const JSZip = (await import("jszip")).default;
81
+ const zip = new JSZip();
82
+
83
+ const slideOverrides = slides
84
+ .map(
85
+ (_, i) =>
86
+ `<Override PartName="/ppt/slides/slide${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>`
87
+ )
88
+ .join("");
89
+
90
+ zip.file(
91
+ "[Content_Types].xml",
92
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
93
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
94
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
95
+ <Default Extension="xml" ContentType="application/xml"/>
96
+ <Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
97
+ ${slideOverrides}
98
+ </Types>`
99
+ );
100
+
101
+ zip.file(
102
+ "_rels/.rels",
103
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
104
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
105
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
106
+ </Relationships>`
107
+ );
108
+
109
+ const slideRels = slides
110
+ .map(
111
+ (_, i) =>
112
+ `<Relationship Id="rId${i + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide${i + 1}.xml"/>`
113
+ )
114
+ .join("");
115
+
116
+ zip.file(
117
+ "ppt/_rels/presentation.xml.rels",
118
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
119
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
120
+ ${slideRels}
121
+ </Relationships>`
122
+ );
123
+
124
+ const slideIdList = slides
125
+ .map((_, i) => `<p:sldId id="${256 + i}" r:id="rId${i + 1}"/>`)
126
+ .join("");
127
+
128
+ zip.file(
129
+ "ppt/presentation.xml",
130
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
131
+ <p:presentation xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
132
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
133
+ <p:sldIdLst>${slideIdList}</p:sldIdLst>
134
+ </p:presentation>`
135
+ );
136
+
137
+ slides.forEach((text, i) => {
138
+ zip.file(
139
+ `ppt/slides/slide${i + 1}.xml`,
140
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
141
+ <p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
142
+ xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
143
+ <p:cSld>
144
+ <p:spTree>
145
+ <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
146
+ <p:grpSpPr/>
147
+ <p:sp>
148
+ <p:nvSpPr><p:cNvPr id="2" name="Title"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>
149
+ <p:spPr/>
150
+ <p:txBody>
151
+ <a:bodyPr/>
152
+ <a:p><a:r><a:t>${escapeXml(text)}</a:t></a:r></a:p>
153
+ </p:txBody>
154
+ </p:sp>
155
+ </p:spTree>
156
+ </p:cSld>
157
+ </p:sld>`
158
+ );
159
+ });
160
+
161
+ const buf = await zip.generateAsync({ type: "nodebuffer" });
162
+ return Buffer.from(buf);
163
+ }
164
+
165
+ /** Create a valid XLSX using the xlsx library */
166
+ function createXlsxSync(csvContent: string): Buffer {
167
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
168
+ const XLSX = require("xlsx");
169
+ const rows = csvContent.split("\n").map((line: string) => line.split(","));
170
+ const ws = XLSX.utils.aoa_to_sheet(rows);
171
+ const wb = XLSX.utils.book_new();
172
+ XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
173
+ return XLSX.write(wb, { type: "buffer", bookType: "xlsx" }) as Buffer;
174
+ }
175
+
176
+ /** Create a minimal valid PDF with text content */
177
+ function createPdfSync(textContent: string): Buffer {
178
+ const lines = textContent.split("\n");
179
+ const textOps = lines
180
+ .map(
181
+ (line, i) =>
182
+ `1 0 0 1 50 ${750 - i * 14} Tm (${escapePdf(line)}) Tj`
183
+ )
184
+ .join("\n");
185
+
186
+ const stream = `BT\n/F1 10 Tf\n${textOps}\nET`;
187
+ const streamBytes = Buffer.from(stream, "utf-8");
188
+
189
+ const objects: string[] = [];
190
+ const offsets: number[] = [];
191
+ let body = "";
192
+
193
+ offsets.push(body.length);
194
+ objects.push("1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");
195
+ body += objects[0];
196
+
197
+ offsets.push(body.length);
198
+ objects.push("2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n");
199
+ body += objects[1];
200
+
201
+ offsets.push(body.length);
202
+ objects.push(
203
+ "3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>\nendobj\n"
204
+ );
205
+ body += objects[2];
206
+
207
+ offsets.push(body.length);
208
+ objects.push(
209
+ `4 0 obj\n<< /Length ${streamBytes.length} >>\nstream\n${stream}\nendstream\nendobj\n`
210
+ );
211
+ body += objects[3];
212
+
213
+ offsets.push(body.length);
214
+ objects.push(
215
+ "5 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n"
216
+ );
217
+ body += objects[4];
218
+
219
+ const header = "%PDF-1.4\n";
220
+ const xrefOffset = header.length + body.length;
221
+
222
+ let xref = `xref\n0 ${offsets.length + 1}\n0000000000 65535 f \n`;
223
+ for (const offset of offsets) {
224
+ xref += `${String(header.length + offset).padStart(10, "0")} 00000 n \n`;
225
+ }
226
+
227
+ const trailer = `trailer\n<< /Size ${offsets.length + 1} /Root 1 0 R >>\nstartxref\n${xrefOffset}\n%%EOF`;
228
+
229
+ return Buffer.from(header + body + xref + trailer, "utf-8");
230
+ }
231
+
232
+ function escapeXml(text: string): string {
233
+ return text
234
+ .replace(/&/g, "&amp;")
235
+ .replace(/</g, "&lt;")
236
+ .replace(/>/g, "&gt;")
237
+ .replace(/"/g, "&quot;");
238
+ }
239
+
240
+ function escapePdf(text: string): string {
241
+ return text
242
+ .replace(/\\/g, "\\\\")
243
+ .replace(/\(/g, "\\(")
244
+ .replace(/\)/g, "\\)");
245
+ }
246
+
247
+ // --- Document definitions ---
248
+
249
+ const DOCUMENTS: DocumentDef[] = [
250
+ // Project 1 — Investment Portfolio
251
+ {
252
+ originalName: "portfolio-holdings.xlsx",
253
+ mimeType:
254
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
255
+ projectIndex: 0,
256
+ taskIndex: 0,
257
+ content: () =>
258
+ Promise.resolve(
259
+ createXlsxSync(
260
+ `Ticker,Shares,Avg Cost,Current Price,Market Value,Sector
261
+ NVDA,45,285.50,890.25,40061.25,Technology
262
+ AAPL,120,142.30,178.50,21420.00,Technology
263
+ MSFT,65,310.00,415.80,27027.00,Technology
264
+ GOOGL,80,125.40,172.30,13784.00,Technology
265
+ AMZN,55,128.50,185.60,10208.00,Consumer
266
+ JNJ,90,158.20,155.80,14022.00,Healthcare
267
+ UNH,25,485.00,528.40,13210.00,Healthcare
268
+ JPM,70,142.80,198.50,13895.00,Finance
269
+ V,50,235.60,282.40,14120.00,Finance
270
+ XOM,85,98.40,108.20,9197.00,Energy
271
+ PG,60,148.50,165.20,9912.00,Consumer
272
+ LLY,15,580.00,782.50,11737.50,Healthcare
273
+ HD,35,325.00,348.90,12211.50,Consumer
274
+ MA,40,368.50,462.80,18512.00,Finance
275
+ CVX,55,152.30,158.40,8712.00,Energy`
276
+ )
277
+ ),
278
+ },
279
+ {
280
+ originalName: "semiconductor-research.pdf",
281
+ mimeType: "application/pdf",
282
+ projectIndex: 0,
283
+ taskIndex: 1,
284
+ content: () =>
285
+ Promise.resolve(
286
+ createPdfSync(
287
+ `Semiconductor Industry ETF Analysis
288
+
289
+ Market Overview
290
+ The semiconductor sector has seen exceptional growth driven by AI
291
+ infrastructure demand, automotive chip adoption, and cloud computing.
292
+ Total addressable market projected at $1.2T by 2030.
293
+
294
+ SOXX - iShares Semiconductor ETF
295
+ Expense Ratio: 0.35%, Holdings: 30, 5-Year Return: +142%
296
+ Top 5: NVDA 9.8%, AVGO 8.2%, AMD 7.1%, QCOM 5.5%, TXN 5.2%
297
+
298
+ SMH - VanEck Semiconductor ETF
299
+ Expense Ratio: 0.35%, Holdings: 25, 5-Year Return: +158%
300
+ Top 5: NVDA 14.2%, TSM 12.8%, AVGO 6.5%, AMD 5.1%, ASML 4.8%
301
+
302
+ PSI - Invesco Dynamic Semiconductors ETF
303
+ Expense Ratio: 0.56%, Holdings: 30, 5-Year Return: +98%
304
+
305
+ Recommendation: SMH for concentrated AI exposure, SOXX for diversification.`
306
+ )
307
+ ),
308
+ },
309
+
310
+ // Project 2 — Landing Page
311
+ {
312
+ originalName: "competitor-audit.docx",
313
+ mimeType:
314
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
315
+ projectIndex: 1,
316
+ taskIndex: 5,
317
+ content: () =>
318
+ createDocx(
319
+ `Competitor Landing Page Audit
320
+
321
+ 1. Notion (notion.so)
322
+ Hero: "Your wiki, docs, & projects. Together."
323
+ Strengths: Clean design, interactive demo, strong social proof (10M+ users)
324
+ Weaknesses: Hero is text-heavy, no personalization
325
+ CTA: "Get Notion free" - low commitment
326
+
327
+ 2. Linear (linear.app)
328
+ Hero: "Linear is a purpose-built tool for planning and building products"
329
+ Strengths: Dark theme feels premium, keyboard shortcut demo, speed-focused
330
+ Weaknesses: Appeals mainly to developers, narrow audience
331
+ CTA: "Start building" - action-oriented
332
+
333
+ 3. Vercel (vercel.com)
334
+ Hero: "Develop. Preview. Ship." - three-word framework
335
+ Strengths: Live deployment demo, framework logos, dark/light toggle
336
+ Weaknesses: Very technical, assumes Next.js knowledge
337
+ CTA: "Start Deploying" - verb-first
338
+
339
+ 4. Stripe (stripe.com)
340
+ Hero: "Financial infrastructure for the internet"
341
+ Strengths: Animated code snippets, multiple audience entry points, trust badges
342
+ Weaknesses: Dense page, high cognitive load
343
+ CTA: "Start now" with "Contact sales" secondary
344
+
345
+ Key Patterns:
346
+ - All use social proof above the fold
347
+ - 2 of 4 feature interactive demos
348
+ - Average CTA count: 3 per page
349
+
350
+ Opportunity: Dynamic hero based on referral source. None of the 4 do this.`
351
+ ),
352
+ },
353
+ {
354
+ originalName: "design-brief.pdf",
355
+ mimeType: "application/pdf",
356
+ projectIndex: 1,
357
+ taskIndex: 6,
358
+ content: () =>
359
+ Promise.resolve(
360
+ createPdfSync(
361
+ `Landing Page Design Brief
362
+
363
+ Brand Voice
364
+ Tone: Confident, not arrogant. Technical, but accessible.
365
+ Personality: The smart colleague who explains things clearly
366
+ Avoid: Jargon-heavy copy, exclamation marks, hyperbole
367
+
368
+ Target Audience - Primary: Engineering Managers
369
+ Age 30-45, managing 5-20 person teams
370
+ Pain points: sprint planning overhead, context switching, tool fatigue
371
+
372
+ Target Audience - Secondary: Individual Developers
373
+ Age 25-35, working in fast-paced startups
374
+ Pain points: slow tooling, too many tabs, broken workflows
375
+
376
+ Messaging Pillars
377
+ 1. Speed - "Build faster"
378
+ 2. Intelligence - AI that understands your codebase
379
+ 3. Simplicity - One tool, not ten
380
+
381
+ Visual Direction
382
+ - OKLCH color system, primary hue 250 (blue-indigo)
383
+ - Monospace code snippets for technical credibility
384
+ - Subtle animations on scroll
385
+
386
+ Conversion Goals
387
+ Primary: Sign up for free trial
388
+ Secondary: Book a demo (enterprise)
389
+ Tertiary: Newsletter subscription`
390
+ )
391
+ ),
392
+ },
393
+
394
+ // Project 3 — Lead Generation
395
+ {
396
+ originalName: "prospect-list.xlsx",
397
+ mimeType:
398
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
399
+ projectIndex: 2,
400
+ taskIndex: 10,
401
+ content: () =>
402
+ Promise.resolve(
403
+ createXlsxSync(
404
+ `Name,Title,Company,Industry,Size,LinkedIn URL,Email,Status
405
+ Sarah Chen,VP Engineering,Acme Corp,SaaS,500-1000,linkedin.com/in/sarachen,s.chen@acmecorp.com,Qualified
406
+ Marcus Johnson,Director of Engineering,Acme Corp,SaaS,500-1000,linkedin.com/in/marcusjohnson,m.johnson@acmecorp.com,Qualified
407
+ Rachel Kim,VP Product,Acme Corp,SaaS,500-1000,linkedin.com/in/rachelkim,r.kim@acmecorp.com,Qualified
408
+ David Park,CTO,TechStart Inc,DevTools,50-200,linkedin.com/in/davidpark,d.park@techstart.io,Qualified
409
+ Lisa Wang,VP Engineering,TechStart Inc,DevTools,50-200,linkedin.com/in/lisawang,l.wang@techstart.io,Qualified
410
+ James Miller,VP Product,CloudBase,Infrastructure,200-500,linkedin.com/in/jamesmiller,j.miller@cloudbase.dev,Contacted
411
+ Maria Garcia,Director of Engineering,DataFlow AI,AI/ML,100-200,linkedin.com/in/mariagarcia,m.garcia@dataflow.ai,Qualified
412
+ Tom Anderson,VP Engineering,ScaleUp HQ,FinTech,500-1000,linkedin.com/in/tomanderson,t.anderson@scaleuphq.com,Qualified
413
+ Nina Patel,CTO,QuickShip,Logistics,200-500,linkedin.com/in/ninapatel,n.patel@quickship.co,Researching
414
+ Alex Turner,Director of Engineering,BrightPath,EdTech,100-200,linkedin.com/in/alexturner,a.turner@brightpath.edu,Qualified
415
+ Sophie Reed,VP Product,GreenGrid,CleanTech,50-200,linkedin.com/in/sophiereed,s.reed@greengrid.io,Researching
416
+ Chris Wong,VP Engineering,NexaPay,FinTech,200-500,linkedin.com/in/chriswong,c.wong@nexapay.com,Qualified`
417
+ )
418
+ ),
419
+ },
420
+ {
421
+ originalName: "outreach-templates.docx",
422
+ mimeType:
423
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
424
+ projectIndex: 2,
425
+ taskIndex: 12,
426
+ content: () =>
427
+ createDocx(
428
+ `Personalized Outreach Email Templates
429
+
430
+ Template 1: Pain Point Hook
431
+ Subject: {Company}'s engineering velocity - quick question
432
+
433
+ Hi {FirstName},
434
+
435
+ I noticed {Company} recently {recent_event}. Congrats!
436
+
437
+ When teams scale past {team_size} engineers, we often see sprint planning eat up 20%+ of leadership time. Is that something you're running into?
438
+
439
+ We built something that cuts that overhead in half using AI-powered task routing. Would love 15 minutes to show you how {similar_company} uses it.
440
+
441
+ Best, [Sender]
442
+
443
+
444
+ Template 2: Social Proof
445
+ Subject: How {similar_company} ships 2x faster
446
+
447
+ Hi {FirstName},
448
+
449
+ {similar_company}'s VP of Eng shared something interesting - their team went from 2-week sprints to continuous delivery. Deployment frequency up 180%.
450
+
451
+ I think {Company} could see similar results given your {tech_stack} setup. Worth a quick chat?
452
+
453
+ Cheers, [Sender]
454
+
455
+
456
+ Template 3: Breakup / Last Touch
457
+ Subject: Closing the loop
458
+
459
+ Hi {FirstName},
460
+
461
+ I've reached out a couple times and totally get it if the timing isn't right.
462
+
463
+ If engineering velocity is ever a priority for {Company}, here's what we do: [one-liner + link].
464
+
465
+ No hard feelings. The door's always open.
466
+
467
+ All the best, [Sender]`
468
+ ),
469
+ },
470
+
471
+ // Project 4 — Business Trip
472
+ {
473
+ originalName: "trip-itinerary.docx",
474
+ mimeType:
475
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
476
+ projectIndex: 3,
477
+ taskIndex: 17,
478
+ content: () =>
479
+ createDocx(
480
+ `NYC Business Trip Itinerary - March 15-18, 2025
481
+
482
+ Day 1 - Saturday, March 15
483
+ 7:00 AM Depart SFO - United UA 456, Gate B12 (TSA Pre)
484
+ 3:30 PM Arrive JFK Terminal 7 - Uber to hotel (~45 min)
485
+ 4:30 PM Hotel check-in - The Manhattan Club, 200 W 56th St
486
+ 6:00 PM Conference keynote - Javits Center, Hall 1A
487
+ 8:00 PM Team dinner - Carbone, 181 Thompson St
488
+
489
+ Day 2 - Sunday, March 16
490
+ 8:30 AM Breakfast meeting - The Smith (J. Rivera, Acme Corp)
491
+ 10:00 AM Partner meeting #1 - Acme Corp HQ, 350 5th Ave
492
+ 12:00 PM Lunch - Le Bernardin, 155 W 51st St
493
+ 2:00 PM Client meeting - DataFlow AI, 85 Broad St
494
+ 4:30 PM Partner meeting #2 - ScaleUp HQ, 28 W 23rd St
495
+ 7:00 PM Networking event - Rooftop at The Standard
496
+
497
+ Day 3 - Monday, March 17
498
+ 9:00 AM Workshop - Javits Center, Room 204
499
+ 12:00 PM Working lunch - Hotel lobby cafe
500
+ 2:00 PM Team standup (Zoom)
501
+ 3:00 PM Hotel checkout
502
+ 6:00 PM Depart JFK - United UA 891, Terminal 7
503
+ 9:15 PM Arrive SFO
504
+
505
+ Travel Budget:
506
+ Flights: $660 | Hotel: $867 | Meals: $225
507
+ Ground transport: $200 | Misc: $100
508
+ Total: $2,052 (Pre-approved #EXP-2025-0342)`
509
+ ),
510
+ },
511
+ {
512
+ originalName: "expense-report.xlsx",
513
+ mimeType:
514
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
515
+ projectIndex: 3,
516
+ taskIndex: 19,
517
+ content: () =>
518
+ Promise.resolve(
519
+ createXlsxSync(
520
+ `Date,Category,Vendor,Amount,Description,Receipt
521
+ 2025-03-15,Airfare,United Airlines,342.00,SFO to JFK Economy Plus UA 456,receipt-001.pdf
522
+ 2025-03-15,Ground Transport,Uber,62.50,JFK to Manhattan Club hotel,receipt-002.pdf
523
+ 2025-03-15,Meals,Carbone Restaurant,89.00,Team dinner (4 people split),receipt-003.pdf
524
+ 2025-03-16,Meals,The Smith NYC,28.50,Breakfast meeting with J. Rivera,receipt-004.pdf
525
+ 2025-03-16,Ground Transport,Uber,18.75,Hotel to Acme Corp,receipt-005.pdf
526
+ 2025-03-16,Meals,Le Bernardin,65.00,Working lunch,receipt-006.pdf
527
+ 2025-03-16,Ground Transport,Uber,32.40,Acme to DataFlow AI,receipt-007.pdf
528
+ 2025-03-16,Ground Transport,Lyft,24.80,DataFlow to ScaleUp HQ,receipt-008.pdf
529
+ 2025-03-16,Meals,Rooftop at The Standard,42.00,Networking event,receipt-009.pdf
530
+ 2025-03-17,Meals,Hotel Lobby Cafe,18.50,Working lunch,receipt-010.pdf
531
+ 2025-03-17,Ground Transport,Uber,58.20,Manhattan Club to JFK,receipt-011.pdf
532
+ 2025-03-17,Conference,TechConf 2025,299.00,Conference registration,receipt-012.pdf
533
+ 2025-03-15,Hotel,The Manhattan Club,289.00,Night 1 Standard King,receipt-013.pdf
534
+ 2025-03-16,Hotel,The Manhattan Club,289.00,Night 2 Standard King,receipt-014.pdf
535
+ 2025-03-17,Hotel,The Manhattan Club,289.00,Night 3 Standard King,receipt-015.pdf
536
+ 2025-03-15,Meals,Starbucks JFK,8.40,Coffee at terminal,receipt-016.pdf
537
+ 2025-03-16,Miscellaneous,CVS Pharmacy,12.80,Phone charger,receipt-017.pdf
538
+ 2025-03-17,Miscellaneous,Hotel Concierge,15.00,Luggage storage tip,receipt-018.pdf`
539
+ )
540
+ ),
541
+ },
542
+
543
+ // Project 5 — Tax Filing
544
+ {
545
+ originalName: "tax-documents-checklist.pdf",
546
+ mimeType: "application/pdf",
547
+ projectIndex: 4,
548
+ taskIndex: 20,
549
+ content: () =>
550
+ Promise.resolve(
551
+ createPdfSync(
552
+ `2025 Tax Documents Checklist
553
+
554
+ INCOME DOCUMENTS
555
+ [X] W-2 - TechCorp Inc (primary employer)
556
+ Gross wages: $185,000 | Federal withholding: $37,200
557
+ State withholding: $12,580 | Received: Jan 28
558
+
559
+ [X] 1099-INT - Chase Bank (savings interest)
560
+ Interest income: $1,240 | Received: Feb 2
561
+
562
+ [X] 1099-DIV - Fidelity Investments
563
+ Ordinary dividends: $3,850 | Qualified: $2,910
564
+
565
+ [X] 1099-B - Charles Schwab (stock sales)
566
+ Proceeds: $42,800 | Cost basis: $38,200 | Net gain: $4,600
567
+
568
+ [ ] 1099-NEC - Freelance Client (PENDING)
569
+ Expected: ~$8,500
570
+
571
+ DEDUCTION DOCUMENTS
572
+ [X] 1098 - Wells Fargo (mortgage interest)
573
+ Interest paid: $18,400 | Property tax: $6,200
574
+
575
+ [X] Charitable donations - receipts compiled
576
+ Cash: $2,400 | Goodwill (FMV): $800
577
+
578
+ [ ] Home office measurements - pending calculation
579
+
580
+ [X] State estimated tax payments
581
+ Q1-Q4: $3,200 each | Total: $12,800
582
+
583
+ STATUS: 7 of 8 documents collected (87.5%)`
584
+ )
585
+ ),
586
+ },
587
+ {
588
+ originalName: "deductible-expenses.xlsx",
589
+ mimeType:
590
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
591
+ projectIndex: 4,
592
+ taskIndex: 21,
593
+ content: () =>
594
+ Promise.resolve(
595
+ createXlsxSync(
596
+ `Date,Category,Description,Amount,Deductible,Notes
597
+ 2025-01-05,Home Office,Internet service (pro-rata),45.00,Yes,8.3% of total
598
+ 2025-01-05,Home Office,Electricity (pro-rata),32.00,Yes,8.3% of total
599
+ 2025-01-12,Professional Dev,Udemy - Advanced TypeScript,29.99,Yes,Job-related
600
+ 2025-01-15,Software,GitHub Pro subscription,7.00,Yes,Monthly
601
+ 2025-01-15,Software,Figma Professional,15.00,Yes,Monthly
602
+ 2025-01-20,Charitable,SF Food Bank donation,200.00,Yes,Receipt #4521
603
+ 2025-02-01,Home Office,Ergonomic desk chair,485.00,Partial,May depreciate
604
+ 2025-02-05,Home Office,Internet service (pro-rata),45.00,Yes,Monthly
605
+ 2025-02-05,Home Office,Electricity (pro-rata),28.00,Yes,Lower winter bill
606
+ 2025-02-10,Professional Dev,AWS SA exam fee,300.00,Yes,Certification
607
+ 2025-02-14,Charitable,American Red Cross,150.00,Yes,Confirmation email
608
+ 2025-02-15,Software,GitHub Pro subscription,7.00,Yes,Monthly
609
+ 2025-02-15,Software,Figma Professional,15.00,Yes,Monthly
610
+ 2025-03-01,Health,HSA contribution,500.00,Yes,Monthly
611
+ 2025-03-05,Home Office,Internet service (pro-rata),45.00,Yes,Monthly
612
+ 2025-03-05,Home Office,Electricity (pro-rata),35.00,Yes,Monthly
613
+ 2025-03-10,Professional Dev,O'Reilly Safari,49.00,Yes,Annual
614
+ 2025-03-12,Charitable,Local animal shelter,100.00,Yes,Receipt #782
615
+ 2025-03-15,Software,GitHub Pro subscription,7.00,Yes,Monthly
616
+ 2025-03-15,Software,Figma Professional,15.00,Yes,Monthly
617
+ 2025-03-15,Equipment,External monitor (4K),650.00,Partial,Section 179
618
+ 2025-03-20,Professional Dev,React Conference,350.00,Yes,Job-related
619
+ 2025-04-01,Health,HSA contribution,500.00,Yes,Monthly
620
+ 2025-04-05,Home Office,Internet (pro-rata),45.00,Yes,Monthly
621
+ 2025-04-05,Home Office,Electricity (pro-rata),38.00,Yes,Spring cycle`
622
+ )
623
+ ),
624
+ },
625
+
626
+ // Extra office docs for variety
627
+
628
+ // Campaign pitch deck for lead gen
629
+ {
630
+ originalName: "campaign-pitch-deck.pptx",
631
+ mimeType:
632
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
633
+ projectIndex: 2,
634
+ taskIndex: 13,
635
+ content: () =>
636
+ createPptx([
637
+ "Lead Generation Campaign - Q2 2025",
638
+ "Target Market: VP/Director-level at B2B SaaS companies (50-1000 employees)",
639
+ "Campaign Strategy: 3-touch email sequence over 14 days, segmented by company size and role",
640
+ "Prospect Pipeline: 15 qualified leads across 8 companies",
641
+ "Email Templates: Pain Point Hook (cold), Social Proof (warm), Breakup (last touch)",
642
+ "Success Metrics: 40% open rate target, 8% reply rate, 3 meetings booked per week",
643
+ "Timeline: Week 1 first touch, Week 2 follow-ups, Week 3 breakup + new prospects",
644
+ "Budget: $0 organic outreach + $500/mo email tooling",
645
+ ]),
646
+ },
647
+
648
+ // Quarterly performance summary for portfolio project
649
+ {
650
+ originalName: "q1-performance-summary.pptx",
651
+ mimeType:
652
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
653
+ projectIndex: 0,
654
+ taskIndex: 3,
655
+ content: () =>
656
+ createPptx([
657
+ "Q1 2025 Portfolio Performance Summary",
658
+ "Portfolio Value: $238,419 (+12.4% YTD vs S&P 500 +8.2%)",
659
+ "Top Performers: NVDA +34%, LLY +22%, MA +18%",
660
+ "Underperformers: JNJ -1.5%, CVX +3.8%, XOM +2.1%",
661
+ "Sector Allocation: Tech 42%, Healthcare 18%, Finance 15%, Consumer 12%, Energy 8%, Cash 5%",
662
+ "Risk Assessment: Tech concentration at 42% exceeds 35% target",
663
+ "Dividend Income: $1,842 Q1 ($7,368 annualized, 3.1% yield)",
664
+ "Action Items: Trim NVDA by 20 shares, Add UNH +10, Initiate SCHD position",
665
+ ]),
666
+ },
667
+ ];
668
+
669
+ /**
670
+ * Write document files to disk and return seed records.
671
+ * Async because DOCX/PPTX generation uses JSZip's async API.
672
+ */
673
+ export async function createDocuments(
674
+ projectIds: string[],
675
+ taskIds: string[]
676
+ ): Promise<DocumentSeed[]> {
677
+ mkdirSync(uploadsDir, { recursive: true });
678
+
679
+ const results: DocumentSeed[] = [];
680
+
681
+ for (const def of DOCUMENTS) {
682
+ const id = crypto.randomUUID();
683
+ const ext = def.originalName.split(".").pop()!;
684
+ const filename = `${id}.${ext}`;
685
+ const storagePath = join(uploadsDir, filename);
686
+
687
+ let buf: Buffer;
688
+ if (typeof def.content === "function") {
689
+ buf = await def.content();
690
+ } else {
691
+ buf = Buffer.from(def.content, "utf-8");
692
+ }
693
+ writeFileSync(storagePath, buf);
694
+
695
+ const taskId = taskIds[def.taskIndex];
696
+ const projectId = projectIds[def.projectIndex];
697
+
698
+ results.push({
699
+ id,
700
+ taskId,
701
+ projectId,
702
+ filename,
703
+ originalName: def.originalName,
704
+ mimeType: def.mimeType,
705
+ size: buf.length,
706
+ storagePath,
707
+ direction: "input" as const,
708
+ status: "uploaded" as const,
709
+ createdAt: new Date(),
710
+ updatedAt: new Date(),
711
+ });
712
+ }
713
+
714
+ return results;
715
+ }