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.
- package/LICENSE +191 -0
- package/README.md +399 -0
- package/components.json +21 -0
- package/dist/cli.js +171 -0
- package/drizzle.config.ts +12 -0
- package/next.config.mjs +15 -0
- package/package.json +114 -0
- package/postcss.config.mjs +8 -0
- package/public/icon-512.png +0 -0
- package/public/icon.svg +13 -0
- package/public/readme/home-workspace.png +0 -0
- package/public/readme/inbox-approvals.png +0 -0
- package/public/readme/workflow-blueprints.png +0 -0
- package/public/stagent-s-128.png +0 -0
- package/public/stagent-s-64.png +0 -0
- package/src/app/api/blueprints/[id]/instantiate/route.ts +27 -0
- package/src/app/api/blueprints/[id]/route.ts +39 -0
- package/src/app/api/blueprints/import/route.ts +68 -0
- package/src/app/api/blueprints/route.ts +29 -0
- package/src/app/api/command-palette/recent/route.ts +31 -0
- package/src/app/api/data/clear/route.ts +22 -0
- package/src/app/api/data/seed/route.ts +22 -0
- package/src/app/api/documents/[id]/file/route.ts +44 -0
- package/src/app/api/documents/[id]/route.ts +123 -0
- package/src/app/api/documents/route.ts +59 -0
- package/src/app/api/logs/stream/route.ts +101 -0
- package/src/app/api/notifications/[id]/route.ts +36 -0
- package/src/app/api/notifications/mark-all-read/route.ts +13 -0
- package/src/app/api/notifications/pending-approvals/route.ts +10 -0
- package/src/app/api/notifications/pending-approvals/stream/route.ts +101 -0
- package/src/app/api/notifications/route.ts +34 -0
- package/src/app/api/permissions/route.ts +46 -0
- package/src/app/api/profiles/[id]/route.ts +79 -0
- package/src/app/api/profiles/[id]/test/route.ts +42 -0
- package/src/app/api/profiles/import/route.ts +108 -0
- package/src/app/api/profiles/route.ts +50 -0
- package/src/app/api/projects/[id]/route.ts +72 -0
- package/src/app/api/projects/route.ts +53 -0
- package/src/app/api/schedules/[id]/route.ts +185 -0
- package/src/app/api/schedules/route.ts +117 -0
- package/src/app/api/settings/budgets/route.ts +24 -0
- package/src/app/api/settings/openai/route.ts +24 -0
- package/src/app/api/settings/route.ts +21 -0
- package/src/app/api/settings/test/route.ts +26 -0
- package/src/app/api/tasks/[id]/cancel/route.ts +21 -0
- package/src/app/api/tasks/[id]/execute/route.ts +90 -0
- package/src/app/api/tasks/[id]/logs/route.ts +95 -0
- package/src/app/api/tasks/[id]/output/route.ts +47 -0
- package/src/app/api/tasks/[id]/respond/route.ts +64 -0
- package/src/app/api/tasks/[id]/resume/route.ts +76 -0
- package/src/app/api/tasks/[id]/route.ts +77 -0
- package/src/app/api/tasks/assist/route.ts +35 -0
- package/src/app/api/tasks/route.ts +82 -0
- package/src/app/api/uploads/[id]/route.ts +81 -0
- package/src/app/api/uploads/cleanup/route.ts +7 -0
- package/src/app/api/uploads/route.ts +66 -0
- package/src/app/api/workflows/[id]/execute/route.ts +82 -0
- package/src/app/api/workflows/[id]/route.ts +133 -0
- package/src/app/api/workflows/[id]/status/route.ts +54 -0
- package/src/app/api/workflows/[id]/steps/[stepId]/retry/route.ts +22 -0
- package/src/app/api/workflows/route.ts +61 -0
- package/src/app/apple-icon.tsx +31 -0
- package/src/app/costs/page.tsx +256 -0
- package/src/app/dashboard/page.tsx +44 -0
- package/src/app/documents/[id]/page.tsx +46 -0
- package/src/app/documents/page.tsx +45 -0
- package/src/app/error.tsx +26 -0
- package/src/app/global-error.tsx +23 -0
- package/src/app/globals.css +733 -0
- package/src/app/icon.tsx +30 -0
- package/src/app/inbox/loading.tsx +15 -0
- package/src/app/inbox/page.tsx +35 -0
- package/src/app/layout.tsx +78 -0
- package/src/app/manifest.ts +32 -0
- package/src/app/monitor/page.tsx +37 -0
- package/src/app/page.tsx +162 -0
- package/src/app/profiles/[id]/edit/page.tsx +39 -0
- package/src/app/profiles/[id]/page.tsx +33 -0
- package/src/app/profiles/new/page.tsx +22 -0
- package/src/app/profiles/page.tsx +19 -0
- package/src/app/projects/[id]/page.tsx +134 -0
- package/src/app/projects/loading.tsx +17 -0
- package/src/app/projects/page.tsx +32 -0
- package/src/app/schedules/[id]/page.tsx +47 -0
- package/src/app/schedules/page.tsx +18 -0
- package/src/app/settings/loading.tsx +24 -0
- package/src/app/settings/page.tsx +27 -0
- package/src/app/tasks/[id]/page.tsx +45 -0
- package/src/app/tasks/new/page.tsx +27 -0
- package/src/app/workflows/[id]/edit/page.tsx +66 -0
- package/src/app/workflows/[id]/page.tsx +37 -0
- package/src/app/workflows/blueprints/[id]/page.tsx +40 -0
- package/src/app/workflows/blueprints/new/page.tsx +20 -0
- package/src/app/workflows/blueprints/page.tsx +11 -0
- package/src/app/workflows/new/page.tsx +36 -0
- package/src/app/workflows/page.tsx +18 -0
- package/src/components/charts/donut-ring.tsx +64 -0
- package/src/components/charts/mini-bar.tsx +75 -0
- package/src/components/charts/sparkline.tsx +107 -0
- package/src/components/costs/cost-dashboard.tsx +877 -0
- package/src/components/costs/cost-filters.tsx +179 -0
- package/src/components/dashboard/activity-feed.tsx +95 -0
- package/src/components/dashboard/greeting.tsx +30 -0
- package/src/components/dashboard/priority-queue.tsx +79 -0
- package/src/components/dashboard/quick-actions.tsx +62 -0
- package/src/components/dashboard/recent-projects.tsx +79 -0
- package/src/components/dashboard/stats-cards.tsx +114 -0
- package/src/components/documents/document-browser.tsx +235 -0
- package/src/components/documents/document-detail-view.tsx +367 -0
- package/src/components/documents/document-grid.tsx +78 -0
- package/src/components/documents/document-preview.tsx +68 -0
- package/src/components/documents/document-table.tsx +119 -0
- package/src/components/documents/document-upload-dialog.tsx +153 -0
- package/src/components/documents/types.ts +6 -0
- package/src/components/documents/utils.ts +57 -0
- package/src/components/monitoring/connection-indicator.tsx +14 -0
- package/src/components/monitoring/log-entry.tsx +79 -0
- package/src/components/monitoring/log-filters.tsx +57 -0
- package/src/components/monitoring/log-stream.tsx +144 -0
- package/src/components/monitoring/monitor-overview-wrapper.tsx +64 -0
- package/src/components/monitoring/monitor-overview.tsx +119 -0
- package/src/components/notifications/failure-action.tsx +38 -0
- package/src/components/notifications/inbox-list.tsx +165 -0
- package/src/components/notifications/message-response.tsx +196 -0
- package/src/components/notifications/notification-item.tsx +250 -0
- package/src/components/notifications/pending-approval-host.tsx +478 -0
- package/src/components/notifications/permission-action.tsx +37 -0
- package/src/components/notifications/permission-response-actions.tsx +126 -0
- package/src/components/notifications/unread-badge.tsx +35 -0
- package/src/components/profiles/profile-browser.tsx +117 -0
- package/src/components/profiles/profile-card.tsx +78 -0
- package/src/components/profiles/profile-detail-view.tsx +564 -0
- package/src/components/profiles/profile-form-view.tsx +480 -0
- package/src/components/profiles/profile-import-dialog.tsx +113 -0
- package/src/components/projects/project-card.tsx +58 -0
- package/src/components/projects/project-create-dialog.tsx +140 -0
- package/src/components/projects/project-detail.tsx +68 -0
- package/src/components/projects/project-edit-dialog.tsx +219 -0
- package/src/components/projects/project-list.tsx +108 -0
- package/src/components/schedules/schedule-create-dialog.tsx +403 -0
- package/src/components/schedules/schedule-detail-view.tsx +274 -0
- package/src/components/schedules/schedule-list.tsx +242 -0
- package/src/components/schedules/schedule-status-badge.tsx +16 -0
- package/src/components/settings/api-key-form.tsx +141 -0
- package/src/components/settings/auth-config-section.tsx +141 -0
- package/src/components/settings/auth-method-selector.tsx +67 -0
- package/src/components/settings/auth-status-badge.tsx +40 -0
- package/src/components/settings/auth-status-dot.tsx +59 -0
- package/src/components/settings/budget-guardrails-section.tsx +842 -0
- package/src/components/settings/data-management-section.tsx +141 -0
- package/src/components/settings/openai-runtime-section.tsx +104 -0
- package/src/components/settings/permissions-section.tsx +91 -0
- package/src/components/shared/app-sidebar.tsx +123 -0
- package/src/components/shared/card-skeleton.tsx +42 -0
- package/src/components/shared/command-palette.tsx +250 -0
- package/src/components/shared/confirm-dialog.tsx +52 -0
- package/src/components/shared/empty-state.tsx +24 -0
- package/src/components/shared/error-state.tsx +32 -0
- package/src/components/shared/form-section-card.tsx +33 -0
- package/src/components/shared/section-heading.tsx +14 -0
- package/src/components/shared/stagent-logo.tsx +21 -0
- package/src/components/shared/theme-toggle.tsx +46 -0
- package/src/components/tasks/ai-assist-panel.tsx +210 -0
- package/src/components/tasks/content-preview.tsx +89 -0
- package/src/components/tasks/empty-board.tsx +12 -0
- package/src/components/tasks/file-upload.tsx +120 -0
- package/src/components/tasks/kanban-board.tsx +275 -0
- package/src/components/tasks/kanban-column.tsx +75 -0
- package/src/components/tasks/skeleton-board.tsx +21 -0
- package/src/components/tasks/task-attachments.tsx +114 -0
- package/src/components/tasks/task-card.tsx +101 -0
- package/src/components/tasks/task-create-panel.tsx +360 -0
- package/src/components/tasks/task-detail-view.tsx +356 -0
- package/src/components/ui/alert-dialog.tsx +196 -0
- package/src/components/ui/badge.tsx +50 -0
- package/src/components/ui/button.tsx +71 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/popover.tsx +89 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +143 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +63 -0
- package/src/components/ui/sonner.tsx +36 -0
- package/src/components/ui/switch.tsx +35 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/components/workflows/blueprint-editor.tsx +109 -0
- package/src/components/workflows/blueprint-gallery.tsx +155 -0
- package/src/components/workflows/blueprint-preview.tsx +240 -0
- package/src/components/workflows/loop-status-view.tsx +272 -0
- package/src/components/workflows/swarm-dashboard.tsx +185 -0
- package/src/components/workflows/workflow-form-view.tsx +1376 -0
- package/src/components/workflows/workflow-list.tsx +230 -0
- package/src/components/workflows/workflow-status-view.tsx +477 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/instrumentation.ts +7 -0
- package/src/lib/agents/claude-agent.ts +737 -0
- package/src/lib/agents/execution-manager.ts +27 -0
- package/src/lib/agents/profiles/assignment-validation.ts +75 -0
- package/src/lib/agents/profiles/builtins/code-reviewer/SKILL.md +21 -0
- package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +28 -0
- package/src/lib/agents/profiles/builtins/data-analyst/SKILL.md +25 -0
- package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/devops-engineer/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/document-writer/SKILL.md +16 -0
- package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/general/SKILL.md +13 -0
- package/src/lib/agents/profiles/builtins/general/profile.yaml +18 -0
- package/src/lib/agents/profiles/builtins/health-fitness-coach/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/learning-coach/SKILL.md +35 -0
- package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/SKILL.md +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/researcher/SKILL.md +15 -0
- package/src/lib/agents/profiles/builtins/researcher/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/technical-writer/SKILL.md +31 -0
- package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +29 -0
- package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +23 -0
- package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +24 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +26 -0
- package/src/lib/agents/profiles/compatibility.ts +109 -0
- package/src/lib/agents/profiles/registry.ts +293 -0
- package/src/lib/agents/profiles/test-runner.ts +18 -0
- package/src/lib/agents/profiles/test-types.ts +20 -0
- package/src/lib/agents/profiles/types.ts +43 -0
- package/src/lib/agents/router.ts +56 -0
- package/src/lib/agents/runtime/catalog.ts +85 -0
- package/src/lib/agents/runtime/claude-sdk.ts +12 -0
- package/src/lib/agents/runtime/claude.ts +370 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +289 -0
- package/src/lib/agents/runtime/index.ts +167 -0
- package/src/lib/agents/runtime/openai-codex.ts +1089 -0
- package/src/lib/agents/runtime/task-assist-types.ts +8 -0
- package/src/lib/agents/runtime/types.ts +30 -0
- package/src/lib/constants/settings.ts +13 -0
- package/src/lib/constants/status-colors.ts +44 -0
- package/src/lib/constants/task-status.ts +49 -0
- package/src/lib/data/clear.ts +63 -0
- package/src/lib/data/seed-data/documents.ts +715 -0
- package/src/lib/data/seed-data/logs.ts +195 -0
- package/src/lib/data/seed-data/notifications.ts +141 -0
- package/src/lib/data/seed-data/profiles.ts +175 -0
- package/src/lib/data/seed-data/projects.ts +61 -0
- package/src/lib/data/seed-data/schedules.ts +108 -0
- package/src/lib/data/seed-data/tasks.ts +341 -0
- package/src/lib/data/seed-data/usage-ledger.ts +130 -0
- package/src/lib/data/seed-data/workflows.ts +213 -0
- package/src/lib/data/seed.ts +129 -0
- package/src/lib/db/index.ts +221 -0
- package/src/lib/db/migrations/0000_aromatic_gargoyle.sql +59 -0
- package/src/lib/db/migrations/0001_first_iron_patriot.sql +6 -0
- package/src/lib/db/migrations/0002_add_resume_count.sql +1 -0
- package/src/lib/db/migrations/0003_add_settings.sql +5 -0
- package/src/lib/db/migrations/0004_add_documents.sql +20 -0
- package/src/lib/db/migrations/0005_add_document_preprocessing.sql +4 -0
- package/src/lib/db/migrations/0006_add_agent_profile.sql +2 -0
- package/src/lib/db/migrations/0007_add_usage_metering_ledger.sql +30 -0
- package/src/lib/db/migrations/0008_add_document_version.sql +1 -0
- package/src/lib/db/migrations/meta/0000_snapshot.json +416 -0
- package/src/lib/db/migrations/meta/0001_snapshot.json +461 -0
- package/src/lib/db/migrations/meta/0002_snapshot.json +469 -0
- package/src/lib/db/migrations/meta/_journal.json +27 -0
- package/src/lib/db/schema.ts +227 -0
- package/src/lib/documents/cleanup.ts +50 -0
- package/src/lib/documents/context-builder.ts +75 -0
- package/src/lib/documents/output-scanner.ts +166 -0
- package/src/lib/documents/processor.ts +120 -0
- package/src/lib/documents/processors/image.ts +21 -0
- package/src/lib/documents/processors/office.ts +36 -0
- package/src/lib/documents/processors/pdf.ts +12 -0
- package/src/lib/documents/processors/spreadsheet.ts +18 -0
- package/src/lib/documents/processors/text.ts +8 -0
- package/src/lib/documents/registry.ts +25 -0
- package/src/lib/notifications/actionable.ts +108 -0
- package/src/lib/notifications/permissions.ts +169 -0
- package/src/lib/queries/chart-data.ts +184 -0
- package/src/lib/schedules/interval-parser.ts +110 -0
- package/src/lib/schedules/scheduler.ts +220 -0
- package/src/lib/settings/auth.ts +98 -0
- package/src/lib/settings/budget-guardrails.ts +590 -0
- package/src/lib/settings/helpers.ts +23 -0
- package/src/lib/settings/openai-auth.ts +80 -0
- package/src/lib/settings/permissions.ts +102 -0
- package/src/lib/usage/ledger.ts +489 -0
- package/src/lib/usage/pricing.ts +68 -0
- package/src/lib/utils/crypto.ts +90 -0
- package/src/lib/utils/format-timestamp.ts +46 -0
- package/src/lib/utils/session-cleanup.ts +26 -0
- package/src/lib/utils/stagent-paths.ts +18 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validators/blueprint.ts +43 -0
- package/src/lib/validators/profile.ts +64 -0
- package/src/lib/validators/project.ts +17 -0
- package/src/lib/validators/settings.ts +57 -0
- package/src/lib/validators/task.ts +30 -0
- package/src/lib/workflows/blueprints/builtins/code-review-pipeline.yaml +72 -0
- package/src/lib/workflows/blueprints/builtins/documentation-generation.yaml +62 -0
- package/src/lib/workflows/blueprints/builtins/investment-research.yaml +81 -0
- package/src/lib/workflows/blueprints/builtins/meal-planning.yaml +73 -0
- package/src/lib/workflows/blueprints/builtins/product-research.yaml +72 -0
- package/src/lib/workflows/blueprints/builtins/research-report.yaml +77 -0
- package/src/lib/workflows/blueprints/builtins/sprint-planning.yaml +77 -0
- package/src/lib/workflows/blueprints/builtins/travel-planning.yaml +80 -0
- package/src/lib/workflows/blueprints/instantiator.ts +131 -0
- package/src/lib/workflows/blueprints/registry.ts +128 -0
- package/src/lib/workflows/blueprints/template.ts +58 -0
- package/src/lib/workflows/blueprints/types.ts +38 -0
- package/src/lib/workflows/definition-validation.ts +121 -0
- package/src/lib/workflows/engine.ts +1113 -0
- package/src/lib/workflows/loop-executor.ts +270 -0
- package/src/lib/workflows/parallel.ts +55 -0
- package/src/lib/workflows/swarm.ts +97 -0
- package/src/lib/workflows/types.ts +112 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
id: meal-planning
|
|
2
|
+
name: Meal Planning
|
|
3
|
+
description: Assess goals, create a weekly meal plan, and generate a shopping list
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
domain: personal
|
|
6
|
+
tags: [health, nutrition, meal-prep, shopping]
|
|
7
|
+
pattern: sequence
|
|
8
|
+
estimatedDuration: "5-10 min"
|
|
9
|
+
difficulty: beginner
|
|
10
|
+
author: stagent
|
|
11
|
+
|
|
12
|
+
variables:
|
|
13
|
+
- id: goal
|
|
14
|
+
type: select
|
|
15
|
+
label: Health Goal
|
|
16
|
+
description: Primary nutrition goal
|
|
17
|
+
required: true
|
|
18
|
+
default: balanced
|
|
19
|
+
options:
|
|
20
|
+
- { value: balanced, label: "Balanced eating" }
|
|
21
|
+
- { value: weight-loss, label: "Weight loss" }
|
|
22
|
+
- { value: muscle-gain, label: "Muscle gain" }
|
|
23
|
+
- { value: energy, label: "More energy" }
|
|
24
|
+
- id: restrictions
|
|
25
|
+
type: text
|
|
26
|
+
label: Dietary Restrictions
|
|
27
|
+
description: Allergies, preferences, or restrictions
|
|
28
|
+
required: false
|
|
29
|
+
placeholder: "e.g., vegetarian, gluten-free, no nuts"
|
|
30
|
+
- id: days
|
|
31
|
+
type: number
|
|
32
|
+
label: Days to Plan
|
|
33
|
+
description: Number of days to plan meals for
|
|
34
|
+
required: true
|
|
35
|
+
default: 7
|
|
36
|
+
min: 1
|
|
37
|
+
max: 14
|
|
38
|
+
|
|
39
|
+
steps:
|
|
40
|
+
- name: Goal Assessment
|
|
41
|
+
profileId: health-fitness-coach
|
|
42
|
+
promptTemplate: |
|
|
43
|
+
Assess nutritional needs for the goal: {{goal}}
|
|
44
|
+
{{#if restrictions}}Dietary restrictions: {{restrictions}}{{/if}}
|
|
45
|
+
Planning for {{days}} days.
|
|
46
|
+
|
|
47
|
+
Determine: daily calorie target, macro breakdown (protein/carbs/fat),
|
|
48
|
+
key nutrients to focus on, and foods to prioritize or avoid.
|
|
49
|
+
requiresApproval: false
|
|
50
|
+
expectedOutput: nutrition-plan
|
|
51
|
+
|
|
52
|
+
- name: Meal Plan Creation
|
|
53
|
+
profileId: health-fitness-coach
|
|
54
|
+
promptTemplate: |
|
|
55
|
+
Create a {{days}}-day meal plan for the goal: {{goal}}
|
|
56
|
+
{{#if restrictions}}Restrictions: {{restrictions}}{{/if}}
|
|
57
|
+
|
|
58
|
+
Use the nutritional assessment from the previous step.
|
|
59
|
+
Include breakfast, lunch, dinner, and snacks for each day.
|
|
60
|
+
Vary meals to avoid repetition. Include prep time estimates.
|
|
61
|
+
requiresApproval: false
|
|
62
|
+
expectedOutput: meal-plan
|
|
63
|
+
|
|
64
|
+
- name: Shopping List
|
|
65
|
+
profileId: document-writer
|
|
66
|
+
promptTemplate: |
|
|
67
|
+
Generate a complete shopping list from the {{days}}-day meal plan.
|
|
68
|
+
|
|
69
|
+
Organize by grocery section (produce, proteins, dairy, pantry, frozen).
|
|
70
|
+
Combine ingredients across meals to minimize waste.
|
|
71
|
+
Include quantities and optional budget-friendly substitutions.
|
|
72
|
+
requiresApproval: true
|
|
73
|
+
expectedOutput: shopping-list
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
id: product-research
|
|
2
|
+
name: Product Research
|
|
3
|
+
description: Research options, compare reviews, and get a purchase recommendation
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
domain: personal
|
|
6
|
+
tags: [shopping, comparison, reviews, recommendation]
|
|
7
|
+
pattern: sequence
|
|
8
|
+
estimatedDuration: "5-15 min"
|
|
9
|
+
difficulty: beginner
|
|
10
|
+
author: stagent
|
|
11
|
+
|
|
12
|
+
variables:
|
|
13
|
+
- id: product
|
|
14
|
+
type: text
|
|
15
|
+
label: Product Category
|
|
16
|
+
description: What you're looking to buy
|
|
17
|
+
required: true
|
|
18
|
+
placeholder: "e.g., wireless noise-canceling headphones"
|
|
19
|
+
- id: budget
|
|
20
|
+
type: text
|
|
21
|
+
label: Budget Range
|
|
22
|
+
description: How much you want to spend
|
|
23
|
+
required: false
|
|
24
|
+
placeholder: "e.g., $100-300"
|
|
25
|
+
- id: priorities
|
|
26
|
+
type: textarea
|
|
27
|
+
label: Priorities
|
|
28
|
+
description: What matters most to you
|
|
29
|
+
required: false
|
|
30
|
+
placeholder: "e.g., comfort for long flights, good microphone, ANC quality"
|
|
31
|
+
|
|
32
|
+
steps:
|
|
33
|
+
- name: Research Options
|
|
34
|
+
profileId: researcher
|
|
35
|
+
promptTemplate: |
|
|
36
|
+
Research the best options for: {{product}}
|
|
37
|
+
{{#if budget}}Budget: {{budget}}{{/if}}
|
|
38
|
+
{{#if priorities}}Priorities: {{priorities}}{{/if}}
|
|
39
|
+
|
|
40
|
+
Find the top 5-8 options currently available. For each, note:
|
|
41
|
+
key specs, price, standout features, and known issues.
|
|
42
|
+
requiresApproval: false
|
|
43
|
+
expectedOutput: product-list
|
|
44
|
+
|
|
45
|
+
- name: Compare Reviews
|
|
46
|
+
profileId: shopping-assistant
|
|
47
|
+
promptTemplate: |
|
|
48
|
+
Compare the top options for: {{product}}
|
|
49
|
+
{{#if budget}}Budget: {{budget}}{{/if}}
|
|
50
|
+
{{#if priorities}}Priorities: {{priorities}}{{/if}}
|
|
51
|
+
|
|
52
|
+
Analyze reviews and ratings for each option from the research step.
|
|
53
|
+
Create a comparison table with: price, rating, pros, cons, best for.
|
|
54
|
+
Identify any deal-breakers or red flags.
|
|
55
|
+
requiresApproval: false
|
|
56
|
+
expectedOutput: comparison-table
|
|
57
|
+
|
|
58
|
+
- name: Recommendation
|
|
59
|
+
profileId: shopping-assistant
|
|
60
|
+
promptTemplate: |
|
|
61
|
+
Provide a final purchase recommendation for: {{product}}
|
|
62
|
+
{{#if budget}}Budget: {{budget}}{{/if}}
|
|
63
|
+
{{#if priorities}}Priorities: {{priorities}}{{/if}}
|
|
64
|
+
|
|
65
|
+
Based on the research and comparison, recommend:
|
|
66
|
+
1. Best overall pick
|
|
67
|
+
2. Best value pick
|
|
68
|
+
3. Premium pick (if applicable)
|
|
69
|
+
|
|
70
|
+
Explain why each is recommended and who it's best for.
|
|
71
|
+
requiresApproval: true
|
|
72
|
+
expectedOutput: recommendation
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
id: research-report
|
|
2
|
+
name: Research Report
|
|
3
|
+
description: Multi-step research pipeline — gather sources, analyze data, write structured report
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
domain: work
|
|
6
|
+
tags: [research, analysis, report, writing]
|
|
7
|
+
pattern: sequence
|
|
8
|
+
estimatedDuration: "15-30 min"
|
|
9
|
+
difficulty: intermediate
|
|
10
|
+
author: stagent
|
|
11
|
+
|
|
12
|
+
variables:
|
|
13
|
+
- id: topic
|
|
14
|
+
type: text
|
|
15
|
+
label: Research Topic
|
|
16
|
+
description: The main topic or question to research
|
|
17
|
+
required: true
|
|
18
|
+
placeholder: "e.g., Impact of AI on healthcare diagnostics"
|
|
19
|
+
- id: depth
|
|
20
|
+
type: select
|
|
21
|
+
label: Research Depth
|
|
22
|
+
description: How thorough the research should be
|
|
23
|
+
required: true
|
|
24
|
+
default: standard
|
|
25
|
+
options:
|
|
26
|
+
- { value: quick, label: "Quick overview (5 sources)" }
|
|
27
|
+
- { value: standard, label: "Standard (10-15 sources)" }
|
|
28
|
+
- { value: deep, label: "Deep dive (20+ sources)" }
|
|
29
|
+
- id: audience
|
|
30
|
+
type: text
|
|
31
|
+
label: Target Audience
|
|
32
|
+
description: Who will read this report
|
|
33
|
+
required: false
|
|
34
|
+
default: "General technical audience"
|
|
35
|
+
- id: includeData
|
|
36
|
+
type: boolean
|
|
37
|
+
label: Include Data Analysis
|
|
38
|
+
description: Whether to include statistical analysis section
|
|
39
|
+
required: false
|
|
40
|
+
default: false
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- name: Research Gathering
|
|
44
|
+
profileId: researcher
|
|
45
|
+
promptTemplate: |
|
|
46
|
+
Research the following topic thoroughly: {{topic}}
|
|
47
|
+
|
|
48
|
+
Depth level: {{depth}}
|
|
49
|
+
{{#if audience}}Target audience: {{audience}}{{/if}}
|
|
50
|
+
|
|
51
|
+
Find credible sources, extract key findings, and compile a structured summary
|
|
52
|
+
with full citations. Focus on recent publications (last 2 years).
|
|
53
|
+
requiresApproval: false
|
|
54
|
+
expectedOutput: structured-summary
|
|
55
|
+
|
|
56
|
+
- name: Data Analysis
|
|
57
|
+
profileId: data-analyst
|
|
58
|
+
promptTemplate: |
|
|
59
|
+
Analyze the research findings from the previous step about: {{topic}}
|
|
60
|
+
|
|
61
|
+
Identify key trends, statistical patterns, and data-driven insights.
|
|
62
|
+
Create summary statistics and recommend visualization approaches.
|
|
63
|
+
requiresApproval: false
|
|
64
|
+
expectedOutput: analysis-report
|
|
65
|
+
condition: "{{includeData}}"
|
|
66
|
+
|
|
67
|
+
- name: Report Writing
|
|
68
|
+
profileId: document-writer
|
|
69
|
+
promptTemplate: |
|
|
70
|
+
Write a comprehensive research report on: {{topic}}
|
|
71
|
+
|
|
72
|
+
{{#if audience}}Target audience: {{audience}}{{/if}}
|
|
73
|
+
|
|
74
|
+
Use the research findings and {{#if includeData}}data analysis{{/if}} from previous steps.
|
|
75
|
+
Structure with: Executive Summary, Key Findings, {{#if includeData}}Data Analysis, {{/if}}Recommendations, References.
|
|
76
|
+
requiresApproval: true
|
|
77
|
+
expectedOutput: markdown-report
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
id: sprint-planning
|
|
2
|
+
name: Sprint Planning
|
|
3
|
+
description: Decompose epics into tasks, estimate effort, and create a sprint schedule
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
domain: work
|
|
6
|
+
tags: [planning, estimation, sprint, project-management]
|
|
7
|
+
pattern: sequence
|
|
8
|
+
estimatedDuration: "10-20 min"
|
|
9
|
+
difficulty: beginner
|
|
10
|
+
author: stagent
|
|
11
|
+
|
|
12
|
+
variables:
|
|
13
|
+
- id: goal
|
|
14
|
+
type: textarea
|
|
15
|
+
label: Sprint Goal
|
|
16
|
+
description: What this sprint should achieve
|
|
17
|
+
required: true
|
|
18
|
+
placeholder: "e.g., Implement user authentication and profile management"
|
|
19
|
+
- id: duration
|
|
20
|
+
type: select
|
|
21
|
+
label: Sprint Duration
|
|
22
|
+
description: How long is this sprint
|
|
23
|
+
required: true
|
|
24
|
+
default: 2-weeks
|
|
25
|
+
options:
|
|
26
|
+
- { value: 1-week, label: "1 week" }
|
|
27
|
+
- { value: 2-weeks, label: "2 weeks" }
|
|
28
|
+
- { value: 3-weeks, label: "3 weeks" }
|
|
29
|
+
- id: teamSize
|
|
30
|
+
type: number
|
|
31
|
+
label: Team Size
|
|
32
|
+
description: Number of developers
|
|
33
|
+
required: false
|
|
34
|
+
default: 1
|
|
35
|
+
min: 1
|
|
36
|
+
max: 20
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- name: Epic Decomposition
|
|
40
|
+
profileId: project-manager
|
|
41
|
+
promptTemplate: |
|
|
42
|
+
Decompose the following sprint goal into actionable tasks:
|
|
43
|
+
|
|
44
|
+
Goal: {{goal}}
|
|
45
|
+
Sprint duration: {{duration}}
|
|
46
|
+
{{#if teamSize}}Team size: {{teamSize}}{{/if}}
|
|
47
|
+
|
|
48
|
+
Break down into epics, then into individual tasks. Each task should be
|
|
49
|
+
independently completable in 1-3 days. Include dependencies between tasks.
|
|
50
|
+
requiresApproval: false
|
|
51
|
+
expectedOutput: task-list
|
|
52
|
+
|
|
53
|
+
- name: Estimation
|
|
54
|
+
profileId: project-manager
|
|
55
|
+
promptTemplate: |
|
|
56
|
+
Estimate the effort for each task from the decomposition of: {{goal}}
|
|
57
|
+
|
|
58
|
+
Sprint duration: {{duration}}
|
|
59
|
+
{{#if teamSize}}Team size: {{teamSize}}{{/if}}
|
|
60
|
+
|
|
61
|
+
For each task, provide: story points (1/2/3/5/8), time estimate, risk level,
|
|
62
|
+
and any blockers. Flag tasks that don't fit in the sprint.
|
|
63
|
+
requiresApproval: false
|
|
64
|
+
expectedOutput: estimation-report
|
|
65
|
+
|
|
66
|
+
- name: Sprint Schedule
|
|
67
|
+
profileId: project-manager
|
|
68
|
+
promptTemplate: |
|
|
69
|
+
Create a sprint schedule from the estimated tasks for: {{goal}}
|
|
70
|
+
|
|
71
|
+
Sprint duration: {{duration}}
|
|
72
|
+
{{#if teamSize}}Team size: {{teamSize}}{{/if}}
|
|
73
|
+
|
|
74
|
+
Sequence tasks respecting dependencies. Identify the critical path.
|
|
75
|
+
Produce a day-by-day schedule with task assignments and milestones.
|
|
76
|
+
requiresApproval: true
|
|
77
|
+
expectedOutput: schedule
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
id: travel-planning
|
|
2
|
+
name: Travel Planning
|
|
3
|
+
description: Research destinations, find deals, and build a complete itinerary
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
domain: personal
|
|
6
|
+
tags: [travel, itinerary, budget, booking]
|
|
7
|
+
pattern: sequence
|
|
8
|
+
estimatedDuration: "10-20 min"
|
|
9
|
+
difficulty: beginner
|
|
10
|
+
author: stagent
|
|
11
|
+
|
|
12
|
+
variables:
|
|
13
|
+
- id: destination
|
|
14
|
+
type: text
|
|
15
|
+
label: Destination
|
|
16
|
+
description: Where you want to travel
|
|
17
|
+
required: true
|
|
18
|
+
placeholder: "e.g., Tokyo, Japan for 10 days"
|
|
19
|
+
- id: dates
|
|
20
|
+
type: text
|
|
21
|
+
label: Travel Dates
|
|
22
|
+
description: When you plan to travel
|
|
23
|
+
required: true
|
|
24
|
+
placeholder: "e.g., March 15-25, 2026"
|
|
25
|
+
- id: budget
|
|
26
|
+
type: select
|
|
27
|
+
label: Budget Level
|
|
28
|
+
description: Overall budget per person
|
|
29
|
+
required: true
|
|
30
|
+
default: moderate
|
|
31
|
+
options:
|
|
32
|
+
- { value: budget, label: "Budget ($50-100/day)" }
|
|
33
|
+
- { value: moderate, label: "Moderate ($100-250/day)" }
|
|
34
|
+
- { value: luxury, label: "Luxury ($250+/day)" }
|
|
35
|
+
- id: interests
|
|
36
|
+
type: textarea
|
|
37
|
+
label: Interests
|
|
38
|
+
description: Activities and experiences you enjoy
|
|
39
|
+
required: false
|
|
40
|
+
placeholder: "e.g., food, temples, hiking, nightlife"
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- name: Destination Research
|
|
44
|
+
profileId: travel-planner
|
|
45
|
+
promptTemplate: |
|
|
46
|
+
Research travel to: {{destination}}
|
|
47
|
+
Dates: {{dates}}
|
|
48
|
+
Budget: {{budget}}
|
|
49
|
+
{{#if interests}}Interests: {{interests}}{{/if}}
|
|
50
|
+
|
|
51
|
+
Cover: best neighborhoods to stay, transport options, must-see attractions,
|
|
52
|
+
local customs and tips, weather expectations, and safety considerations.
|
|
53
|
+
requiresApproval: false
|
|
54
|
+
expectedOutput: research-summary
|
|
55
|
+
|
|
56
|
+
- name: Deal Finding
|
|
57
|
+
profileId: shopping-assistant
|
|
58
|
+
promptTemplate: |
|
|
59
|
+
Find the best travel deals for: {{destination}}
|
|
60
|
+
Dates: {{dates}}
|
|
61
|
+
Budget: {{budget}}
|
|
62
|
+
|
|
63
|
+
Research: flight options, accommodation deals, activity passes,
|
|
64
|
+
and money-saving tips. Compare prices across budget levels.
|
|
65
|
+
requiresApproval: false
|
|
66
|
+
expectedOutput: deal-list
|
|
67
|
+
|
|
68
|
+
- name: Itinerary Building
|
|
69
|
+
profileId: travel-planner
|
|
70
|
+
promptTemplate: |
|
|
71
|
+
Create a detailed day-by-day itinerary for: {{destination}}
|
|
72
|
+
Dates: {{dates}}
|
|
73
|
+
Budget: {{budget}}
|
|
74
|
+
{{#if interests}}Interests: {{interests}}{{/if}}
|
|
75
|
+
|
|
76
|
+
Use the research and deals from previous steps. Include:
|
|
77
|
+
daily schedule, meal recommendations, transport between activities,
|
|
78
|
+
estimated costs per day, and backup options for bad weather.
|
|
79
|
+
requiresApproval: true
|
|
80
|
+
expectedOutput: itinerary
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { db } from "@/lib/db";
|
|
2
|
+
import { workflows } from "@/lib/db/schema";
|
|
3
|
+
import { getBlueprint } from "./registry";
|
|
4
|
+
import { resolveTemplate, evaluateCondition } from "./template";
|
|
5
|
+
import type { BlueprintVariable, WorkflowBlueprint } from "./types";
|
|
6
|
+
import type { WorkflowStep } from "../types";
|
|
7
|
+
|
|
8
|
+
interface InstantiateResult {
|
|
9
|
+
workflowId: string;
|
|
10
|
+
name: string;
|
|
11
|
+
stepsCount: number;
|
|
12
|
+
skippedSteps: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Instantiate a blueprint into a concrete draft workflow.
|
|
17
|
+
*
|
|
18
|
+
* 1. Validate all required variables
|
|
19
|
+
* 2. Resolve {{variable}} in prompt templates
|
|
20
|
+
* 3. Process {{#if}} conditional blocks
|
|
21
|
+
* 4. Evaluate step conditions, filter skipped steps
|
|
22
|
+
* 5. Create workflow with blueprintId lineage
|
|
23
|
+
*/
|
|
24
|
+
export async function instantiateBlueprint(
|
|
25
|
+
blueprintId: string,
|
|
26
|
+
variables: Record<string, unknown>,
|
|
27
|
+
projectId?: string
|
|
28
|
+
): Promise<InstantiateResult> {
|
|
29
|
+
const blueprint = getBlueprint(blueprintId);
|
|
30
|
+
if (!blueprint) {
|
|
31
|
+
throw new Error(`Blueprint "${blueprintId}" not found`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Validate required variables
|
|
35
|
+
validateVariables(blueprint.variables, variables);
|
|
36
|
+
|
|
37
|
+
// Apply defaults for unset optional variables
|
|
38
|
+
const resolvedVars = applyDefaults(blueprint.variables, variables);
|
|
39
|
+
|
|
40
|
+
// Process steps: resolve templates, evaluate conditions
|
|
41
|
+
const resolvedSteps: WorkflowStep[] = [];
|
|
42
|
+
const skippedSteps: string[] = [];
|
|
43
|
+
|
|
44
|
+
for (const step of blueprint.steps) {
|
|
45
|
+
// Check condition — skip if evaluates to falsy
|
|
46
|
+
if (step.condition && !evaluateCondition(step.condition, resolvedVars)) {
|
|
47
|
+
skippedSteps.push(step.name);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const resolvedPrompt = resolveTemplate(step.promptTemplate, resolvedVars);
|
|
52
|
+
|
|
53
|
+
resolvedSteps.push({
|
|
54
|
+
id: crypto.randomUUID(),
|
|
55
|
+
name: step.name,
|
|
56
|
+
prompt: resolvedPrompt,
|
|
57
|
+
requiresApproval: step.requiresApproval,
|
|
58
|
+
agentProfile: step.profileId,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (resolvedSteps.length === 0) {
|
|
63
|
+
throw new Error("All steps were skipped by conditions — at least one step must remain");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Create the workflow
|
|
67
|
+
const workflowId = crypto.randomUUID();
|
|
68
|
+
const now = new Date();
|
|
69
|
+
const workflowName = resolveTemplate(
|
|
70
|
+
`${blueprint.name}: {{${blueprint.variables[0]?.id ?? "topic"}}}`,
|
|
71
|
+
resolvedVars
|
|
72
|
+
) || blueprint.name;
|
|
73
|
+
|
|
74
|
+
const definition = {
|
|
75
|
+
pattern: blueprint.pattern,
|
|
76
|
+
steps: resolvedSteps,
|
|
77
|
+
_blueprintId: blueprintId,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
await db.insert(workflows).values({
|
|
81
|
+
id: workflowId,
|
|
82
|
+
projectId: projectId ?? null,
|
|
83
|
+
name: workflowName.slice(0, 100),
|
|
84
|
+
definition: JSON.stringify(definition),
|
|
85
|
+
status: "draft",
|
|
86
|
+
createdAt: now,
|
|
87
|
+
updatedAt: now,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
workflowId,
|
|
92
|
+
name: workflowName,
|
|
93
|
+
stepsCount: resolvedSteps.length,
|
|
94
|
+
skippedSteps,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function validateVariables(
|
|
99
|
+
definitions: BlueprintVariable[],
|
|
100
|
+
provided: Record<string, unknown>
|
|
101
|
+
): void {
|
|
102
|
+
const errors: string[] = [];
|
|
103
|
+
|
|
104
|
+
for (const def of definitions) {
|
|
105
|
+
if (def.required) {
|
|
106
|
+
const value = provided[def.id];
|
|
107
|
+
if (value === undefined || value === null || value === "") {
|
|
108
|
+
errors.push(`"${def.label}" is required`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (errors.length > 0) {
|
|
114
|
+
throw new Error(`Missing required variables: ${errors.join(", ")}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function applyDefaults(
|
|
119
|
+
definitions: BlueprintVariable[],
|
|
120
|
+
provided: Record<string, unknown>
|
|
121
|
+
): Record<string, unknown> {
|
|
122
|
+
const result = { ...provided };
|
|
123
|
+
|
|
124
|
+
for (const def of definitions) {
|
|
125
|
+
if (result[def.id] === undefined && def.default !== undefined) {
|
|
126
|
+
result[def.id] = def.default;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { BlueprintSchema } from "@/lib/validators/blueprint";
|
|
5
|
+
import { getStagentBlueprintsDir } from "@/lib/utils/stagent-paths";
|
|
6
|
+
import type { WorkflowBlueprint } from "./types";
|
|
7
|
+
|
|
8
|
+
// Use fileURLToPath for ESM compatibility in Next.js
|
|
9
|
+
const BUILTINS_DIR = path.resolve(
|
|
10
|
+
import.meta.dirname ?? path.dirname(new URL(import.meta.url).pathname),
|
|
11
|
+
"builtins"
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const USER_BLUEPRINTS_DIR = getStagentBlueprintsDir();
|
|
15
|
+
|
|
16
|
+
let blueprintCache: Map<string, WorkflowBlueprint> | null = null;
|
|
17
|
+
|
|
18
|
+
function scanDirectory(
|
|
19
|
+
dir: string,
|
|
20
|
+
isBuiltin: boolean
|
|
21
|
+
): Map<string, WorkflowBlueprint> {
|
|
22
|
+
const blueprints = new Map<string, WorkflowBlueprint>();
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(dir)) return blueprints;
|
|
25
|
+
|
|
26
|
+
for (const file of fs.readdirSync(dir)) {
|
|
27
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const content = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
31
|
+
const parsed = yaml.load(content);
|
|
32
|
+
const result = BlueprintSchema.safeParse(parsed);
|
|
33
|
+
|
|
34
|
+
if (!result.success) {
|
|
35
|
+
console.warn(
|
|
36
|
+
`[blueprints] Invalid blueprint ${file}:`,
|
|
37
|
+
result.error.issues.map((i) => i.message).join(", ")
|
|
38
|
+
);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
blueprints.set(result.data.id, { ...result.data, isBuiltin });
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.warn(`[blueprints] Error loading ${file}:`, err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return blueprints;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function loadAll(): Map<string, WorkflowBlueprint> {
|
|
52
|
+
const all = new Map<string, WorkflowBlueprint>();
|
|
53
|
+
|
|
54
|
+
// Load built-ins first
|
|
55
|
+
for (const [id, bp] of scanDirectory(BUILTINS_DIR, true)) {
|
|
56
|
+
all.set(id, bp);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// User blueprints can override built-ins
|
|
60
|
+
for (const [id, bp] of scanDirectory(USER_BLUEPRINTS_DIR, false)) {
|
|
61
|
+
all.set(id, bp);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return all;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function ensureLoaded(): Map<string, WorkflowBlueprint> {
|
|
68
|
+
if (!blueprintCache) {
|
|
69
|
+
blueprintCache = loadAll();
|
|
70
|
+
}
|
|
71
|
+
return blueprintCache;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getBlueprint(id: string): WorkflowBlueprint | undefined {
|
|
75
|
+
return ensureLoaded().get(id);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function listBlueprints(): WorkflowBlueprint[] {
|
|
79
|
+
return Array.from(ensureLoaded().values());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function reloadBlueprints(): void {
|
|
83
|
+
blueprintCache = null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function isBuiltinBlueprint(id: string): boolean {
|
|
87
|
+
const bp = ensureLoaded().get(id);
|
|
88
|
+
return bp?.isBuiltin ?? false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createBlueprint(yamlContent: string): WorkflowBlueprint {
|
|
92
|
+
const parsed = yaml.load(yamlContent);
|
|
93
|
+
const result = BlueprintSchema.safeParse(parsed);
|
|
94
|
+
if (!result.success) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Invalid blueprint: ${result.error.issues.map((i) => i.message).join(", ")}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fs.mkdirSync(USER_BLUEPRINTS_DIR, { recursive: true });
|
|
101
|
+
const filePath = path.join(USER_BLUEPRINTS_DIR, `${result.data.id}.yaml`);
|
|
102
|
+
if (fs.existsSync(filePath)) {
|
|
103
|
+
throw new Error(`Blueprint "${result.data.id}" already exists`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fs.writeFileSync(filePath, yamlContent);
|
|
107
|
+
reloadBlueprints();
|
|
108
|
+
return { ...result.data, isBuiltin: false };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function deleteBlueprint(id: string): void {
|
|
112
|
+
if (isBuiltinBlueprint(id)) {
|
|
113
|
+
throw new Error("Cannot delete built-in blueprints");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const filePath = path.join(USER_BLUEPRINTS_DIR, `${id}.yaml`);
|
|
117
|
+
if (!fs.existsSync(filePath)) {
|
|
118
|
+
throw new Error(`Blueprint "${id}" not found`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fs.unlinkSync(filePath);
|
|
122
|
+
reloadBlueprints();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Get the user blueprints directory path */
|
|
126
|
+
export function getUserBlueprintsDir(): string {
|
|
127
|
+
return USER_BLUEPRINTS_DIR;
|
|
128
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template resolution for blueprint prompt templates.
|
|
3
|
+
*
|
|
4
|
+
* Supports:
|
|
5
|
+
* - {{variable}} — simple substitution
|
|
6
|
+
* - {{#if variable}}...{{/if}} — conditional blocks
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolve all template expressions in a string.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveTemplate(
|
|
13
|
+
template: string,
|
|
14
|
+
variables: Record<string, unknown>
|
|
15
|
+
): string {
|
|
16
|
+
let result = template;
|
|
17
|
+
|
|
18
|
+
// Process {{#if variable}}...{{/if}} blocks first
|
|
19
|
+
result = result.replace(
|
|
20
|
+
/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g,
|
|
21
|
+
(_match, varName: string, content: string) => {
|
|
22
|
+
const value = variables[varName];
|
|
23
|
+
return isTruthy(value) ? content : "";
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Then substitute {{variable}} references
|
|
28
|
+
result = result.replace(/\{\{(\w+)\}\}/g, (_match, varName: string) => {
|
|
29
|
+
const value = variables[varName];
|
|
30
|
+
if (value === undefined || value === null) return "";
|
|
31
|
+
return String(value);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Clean up multiple consecutive blank lines left by removed conditionals
|
|
35
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
36
|
+
|
|
37
|
+
return result.trim();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Evaluate a condition string (a template expression like "{{variable}}").
|
|
42
|
+
* Returns true if the resolved value is truthy.
|
|
43
|
+
*/
|
|
44
|
+
export function evaluateCondition(
|
|
45
|
+
condition: string,
|
|
46
|
+
variables: Record<string, unknown>
|
|
47
|
+
): boolean {
|
|
48
|
+
// Strip template syntax to get the variable name
|
|
49
|
+
const varName = condition.replace(/\{\{|\}\}/g, "").trim();
|
|
50
|
+
return isTruthy(variables[varName]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isTruthy(value: unknown): boolean {
|
|
54
|
+
if (value === undefined || value === null || value === "" || value === false) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|