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,29 @@
|
|
|
1
|
+
id: technical-writer
|
|
2
|
+
name: Technical Writer
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
domain: work
|
|
5
|
+
tags: [documentation, api-docs, adr, readme, changelog, technical-writing]
|
|
6
|
+
supportedRuntimes: [claude-code, openai-codex-app-server]
|
|
7
|
+
|
|
8
|
+
allowedTools:
|
|
9
|
+
- Read
|
|
10
|
+
- Grep
|
|
11
|
+
- Glob
|
|
12
|
+
- Write
|
|
13
|
+
- Edit
|
|
14
|
+
|
|
15
|
+
canUseToolPolicy:
|
|
16
|
+
autoApprove: [Read, Grep, Glob]
|
|
17
|
+
autoDeny: []
|
|
18
|
+
|
|
19
|
+
temperature: 0.4
|
|
20
|
+
maxTurns: 20
|
|
21
|
+
outputFormat: markdown
|
|
22
|
+
|
|
23
|
+
author: stagent
|
|
24
|
+
|
|
25
|
+
tests:
|
|
26
|
+
- task: "Write API documentation for the /api/tasks endpoint"
|
|
27
|
+
expectedKeywords: [endpoint, request, response, parameters, status]
|
|
28
|
+
- task: "Create an ADR for switching from REST to GraphQL"
|
|
29
|
+
expectedKeywords: [context, decision, consequences, alternatives, status]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: travel-planner
|
|
3
|
+
description: Itinerary building, budget optimization, and booking research
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are an experienced travel planner who creates detailed, practical itineraries.
|
|
7
|
+
|
|
8
|
+
## Core Capabilities
|
|
9
|
+
|
|
10
|
+
1. **Itinerary Building** — Day-by-day plans with timing, logistics, and alternatives
|
|
11
|
+
2. **Budget Optimization** — Cost breakdowns, money-saving tips, price comparisons
|
|
12
|
+
3. **Destination Research** — Local tips, seasonal considerations, safety information
|
|
13
|
+
4. **Logistics Planning** — Transportation routing, accommodation recommendations, booking timing
|
|
14
|
+
5. **Personalization** — Adapt plans to traveler preferences, pace, and interests
|
|
15
|
+
|
|
16
|
+
## Output Format
|
|
17
|
+
|
|
18
|
+
Structure itineraries as:
|
|
19
|
+
- **Overview**: Destination, dates, budget range, travel style
|
|
20
|
+
- **Day-by-Day Plan**: Morning/afternoon/evening activities with addresses and costs
|
|
21
|
+
- **Budget Breakdown**: Accommodation, transport, food, activities, contingency
|
|
22
|
+
- **Booking Checklist**: What to book when, with priority order
|
|
23
|
+
- **Tips**: Local customs, packing suggestions, money-saving hacks
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
id: travel-planner
|
|
2
|
+
name: Travel Planner
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
domain: personal
|
|
5
|
+
tags: [travel, itinerary, budget, booking, vacation, trip]
|
|
6
|
+
supportedRuntimes: [claude-code]
|
|
7
|
+
|
|
8
|
+
allowedTools:
|
|
9
|
+
- WebSearch
|
|
10
|
+
- WebFetch
|
|
11
|
+
- Read
|
|
12
|
+
|
|
13
|
+
canUseToolPolicy:
|
|
14
|
+
autoApprove: [WebSearch, WebFetch, Read]
|
|
15
|
+
autoDeny: [Bash, Write, Edit]
|
|
16
|
+
|
|
17
|
+
temperature: 0.6
|
|
18
|
+
maxTurns: 25
|
|
19
|
+
|
|
20
|
+
author: stagent
|
|
21
|
+
|
|
22
|
+
tests:
|
|
23
|
+
- task: "Plan a 5-day trip to Tokyo on a moderate budget"
|
|
24
|
+
expectedKeywords: [itinerary, budget, accommodation, transport]
|
|
25
|
+
- task: "Compare flight options from New York to London in March"
|
|
26
|
+
expectedKeywords: [flight, price, comparison, booking]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wealth-manager
|
|
3
|
+
description: Portfolio analysis, tax optimization, and risk assessment with appropriate disclaimers
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are a knowledgeable financial analyst who helps users understand their financial situation. You provide educational analysis, NOT financial advice.
|
|
7
|
+
|
|
8
|
+
**IMPORTANT DISCLAIMER**: You are an AI assistant providing general financial education and analysis. This is NOT personalized financial advice. Always recommend consulting a licensed financial advisor for investment decisions.
|
|
9
|
+
|
|
10
|
+
## Core Capabilities
|
|
11
|
+
|
|
12
|
+
1. **Portfolio Analysis** — Asset allocation review, diversification assessment, sector exposure
|
|
13
|
+
2. **Tax Optimization** — Tax-loss harvesting opportunities, account type optimization (401k/IRA/taxable)
|
|
14
|
+
3. **Risk Assessment** — Risk tolerance alignment, drawdown analysis, concentration risk
|
|
15
|
+
4. **Goal Planning** — Retirement projections, savings rate analysis, milestone tracking
|
|
16
|
+
5. **Market Context** — Relate portfolio positioning to current market conditions
|
|
17
|
+
|
|
18
|
+
## Output Format
|
|
19
|
+
|
|
20
|
+
- **Portfolio Summary**: Current allocation with percentages
|
|
21
|
+
- **Analysis**: Key observations about diversification, risk, and performance
|
|
22
|
+
- **Opportunities**: Potential optimizations with pros/cons
|
|
23
|
+
- **Risks**: Concentration or strategy risks identified
|
|
24
|
+
- **Disclaimer**: Always end with the standard financial disclaimer
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
id: wealth-manager
|
|
2
|
+
name: Wealth Manager
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
domain: personal
|
|
5
|
+
tags: [finance, investing, tax, portfolio, retirement, savings]
|
|
6
|
+
supportedRuntimes: [claude-code]
|
|
7
|
+
|
|
8
|
+
allowedTools:
|
|
9
|
+
- WebSearch
|
|
10
|
+
- WebFetch
|
|
11
|
+
- Read
|
|
12
|
+
|
|
13
|
+
canUseToolPolicy:
|
|
14
|
+
autoApprove: [Read]
|
|
15
|
+
autoDeny: [Bash, Write, Edit]
|
|
16
|
+
|
|
17
|
+
temperature: 0.3
|
|
18
|
+
maxTurns: 20
|
|
19
|
+
|
|
20
|
+
author: stagent
|
|
21
|
+
|
|
22
|
+
tests:
|
|
23
|
+
- task: "Review my portfolio allocation and suggest improvements"
|
|
24
|
+
expectedKeywords: [allocation, diversification, risk, disclaimer]
|
|
25
|
+
- task: "What tax optimization strategies should I consider this year?"
|
|
26
|
+
expectedKeywords: [tax, optimization, strategy, disclaimer]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_AGENT_RUNTIME,
|
|
3
|
+
SUPPORTED_AGENT_RUNTIMES,
|
|
4
|
+
resolveAgentRuntime,
|
|
5
|
+
type AgentRuntimeId,
|
|
6
|
+
} from "@/lib/agents/runtime/catalog";
|
|
7
|
+
import type {
|
|
8
|
+
CanUseToolPolicy,
|
|
9
|
+
ProfileRuntimeOverride,
|
|
10
|
+
ProfileSmokeTest,
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
type CompatibilityProfile = {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
skillMd?: string;
|
|
17
|
+
systemPrompt?: string;
|
|
18
|
+
allowedTools?: string[];
|
|
19
|
+
mcpServers?: Record<string, unknown>;
|
|
20
|
+
canUseToolPolicy?: CanUseToolPolicy;
|
|
21
|
+
tests?: ProfileSmokeTest[];
|
|
22
|
+
supportedRuntimes?: AgentRuntimeId[];
|
|
23
|
+
runtimeOverrides?: Partial<Record<AgentRuntimeId, ProfileRuntimeOverride>>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface ProfileRuntimeCompatibility {
|
|
27
|
+
runtimeId: AgentRuntimeId;
|
|
28
|
+
supported: boolean;
|
|
29
|
+
reason?: string;
|
|
30
|
+
instructionsSource: "shared" | "runtime-override";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ResolvedProfileRuntimePayload
|
|
34
|
+
extends ProfileRuntimeCompatibility {
|
|
35
|
+
profileId: string;
|
|
36
|
+
profileName: string;
|
|
37
|
+
instructions: string;
|
|
38
|
+
allowedTools?: string[];
|
|
39
|
+
mcpServers?: Record<string, unknown>;
|
|
40
|
+
canUseToolPolicy?: CanUseToolPolicy;
|
|
41
|
+
tests?: ProfileSmokeTest[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getSupportedRuntimes(
|
|
45
|
+
profile: Pick<CompatibilityProfile, "supportedRuntimes">
|
|
46
|
+
): AgentRuntimeId[] {
|
|
47
|
+
const supported =
|
|
48
|
+
profile.supportedRuntimes && profile.supportedRuntimes.length > 0
|
|
49
|
+
? profile.supportedRuntimes
|
|
50
|
+
: ["claude-code"];
|
|
51
|
+
|
|
52
|
+
return SUPPORTED_AGENT_RUNTIMES.filter((runtimeId) =>
|
|
53
|
+
supported.includes(runtimeId)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getProfileRuntimeCompatibility(
|
|
58
|
+
profile: CompatibilityProfile,
|
|
59
|
+
runtimeId?: string | null
|
|
60
|
+
): ProfileRuntimeCompatibility {
|
|
61
|
+
const resolvedRuntime = resolveAgentRuntime(runtimeId ?? DEFAULT_AGENT_RUNTIME);
|
|
62
|
+
const supportedRuntimes = getSupportedRuntimes(profile);
|
|
63
|
+
const supported = supportedRuntimes.includes(resolvedRuntime);
|
|
64
|
+
const runtimeOverride = profile.runtimeOverrides?.[resolvedRuntime];
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
runtimeId: resolvedRuntime,
|
|
68
|
+
supported,
|
|
69
|
+
reason: supported
|
|
70
|
+
? undefined
|
|
71
|
+
: `${profile.name} does not support ${resolvedRuntime}`,
|
|
72
|
+
instructionsSource: runtimeOverride?.instructions ? "runtime-override" : "shared",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function profileSupportsRuntime(
|
|
77
|
+
profile: CompatibilityProfile,
|
|
78
|
+
runtimeId?: string | null
|
|
79
|
+
): boolean {
|
|
80
|
+
return getProfileRuntimeCompatibility(profile, runtimeId).supported;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function resolveProfileRuntimePayload(
|
|
84
|
+
profile: CompatibilityProfile,
|
|
85
|
+
runtimeId?: string | null
|
|
86
|
+
): ResolvedProfileRuntimePayload {
|
|
87
|
+
const compatibility = getProfileRuntimeCompatibility(profile, runtimeId);
|
|
88
|
+
const runtimeOverride = profile.runtimeOverrides?.[compatibility.runtimeId];
|
|
89
|
+
const instructions =
|
|
90
|
+
runtimeOverride?.instructions ??
|
|
91
|
+
profile.skillMd ??
|
|
92
|
+
profile.systemPrompt ??
|
|
93
|
+
"";
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
profileId: profile.id,
|
|
97
|
+
profileName: profile.name,
|
|
98
|
+
runtimeId: compatibility.runtimeId,
|
|
99
|
+
supported: compatibility.supported,
|
|
100
|
+
reason: compatibility.reason,
|
|
101
|
+
instructionsSource: compatibility.instructionsSource,
|
|
102
|
+
instructions,
|
|
103
|
+
allowedTools: runtimeOverride?.allowedTools ?? profile.allowedTools,
|
|
104
|
+
mcpServers: runtimeOverride?.mcpServers ?? profile.mcpServers,
|
|
105
|
+
canUseToolPolicy:
|
|
106
|
+
runtimeOverride?.canUseToolPolicy ?? profile.canUseToolPolicy,
|
|
107
|
+
tests: runtimeOverride?.tests ?? profile.tests,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { ProfileConfigSchema } from "@/lib/validators/profile";
|
|
5
|
+
import type { ProfileConfig } from "@/lib/validators/profile";
|
|
6
|
+
import { getSupportedRuntimes } from "./compatibility";
|
|
7
|
+
import type { AgentProfile } from "./types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Builtins ship inside the repo at src/lib/agents/profiles/builtins/.
|
|
11
|
+
* At runtime they are copied (if missing) to ~/.claude/skills/ so users
|
|
12
|
+
* can customize them without touching source.
|
|
13
|
+
*/
|
|
14
|
+
const BUILTINS_DIR = path.resolve(
|
|
15
|
+
import.meta.dirname ?? __dirname,
|
|
16
|
+
"builtins"
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const SKILLS_DIR = path.join(
|
|
20
|
+
process.env.HOME ?? process.env.USERPROFILE ?? ".",
|
|
21
|
+
".claude",
|
|
22
|
+
"skills"
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Cache
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
let profileCache: Map<string, AgentProfile> | null = null;
|
|
30
|
+
let profileCacheSignature: string | null = null;
|
|
31
|
+
|
|
32
|
+
function getSkillsDirectorySignature(): string {
|
|
33
|
+
if (!fs.existsSync(SKILLS_DIR)) {
|
|
34
|
+
return "missing";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const entries = fs
|
|
38
|
+
.readdirSync(SKILLS_DIR, { withFileTypes: true })
|
|
39
|
+
.filter((entry) => entry.isDirectory())
|
|
40
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
41
|
+
|
|
42
|
+
const signatureParts: string[] = [];
|
|
43
|
+
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
const dir = path.join(SKILLS_DIR, entry.name);
|
|
46
|
+
const yamlPath = path.join(dir, "profile.yaml");
|
|
47
|
+
const skillPath = path.join(dir, "SKILL.md");
|
|
48
|
+
|
|
49
|
+
signatureParts.push(entry.name);
|
|
50
|
+
|
|
51
|
+
if (fs.existsSync(yamlPath)) {
|
|
52
|
+
const stats = fs.statSync(yamlPath);
|
|
53
|
+
signatureParts.push(`yaml:${stats.mtimeMs}:${stats.size}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (fs.existsSync(skillPath)) {
|
|
57
|
+
const stats = fs.statSync(skillPath);
|
|
58
|
+
signatureParts.push(`skill:${stats.mtimeMs}:${stats.size}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return signatureParts.join("|");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// ensureBuiltins — copy missing builtins to .claude/skills/ (idempotent)
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
function ensureBuiltins(): void {
|
|
70
|
+
if (!fs.existsSync(BUILTINS_DIR)) return;
|
|
71
|
+
|
|
72
|
+
fs.mkdirSync(SKILLS_DIR, { recursive: true });
|
|
73
|
+
|
|
74
|
+
for (const entry of fs.readdirSync(BUILTINS_DIR, { withFileTypes: true })) {
|
|
75
|
+
if (!entry.isDirectory()) continue;
|
|
76
|
+
|
|
77
|
+
const targetDir = path.join(SKILLS_DIR, entry.name);
|
|
78
|
+
const targetYaml = path.join(targetDir, "profile.yaml");
|
|
79
|
+
const srcYaml = path.join(BUILTINS_DIR, entry.name, "profile.yaml");
|
|
80
|
+
|
|
81
|
+
// Never overwrite user edits — only copy if profile.yaml is missing
|
|
82
|
+
if (fs.existsSync(targetYaml)) {
|
|
83
|
+
try {
|
|
84
|
+
const source = (yaml.load(fs.readFileSync(srcYaml, "utf-8")) ??
|
|
85
|
+
{}) as Record<string, unknown>;
|
|
86
|
+
const target = (yaml.load(fs.readFileSync(targetYaml, "utf-8")) ??
|
|
87
|
+
{}) as Record<string, unknown>;
|
|
88
|
+
let changed = false;
|
|
89
|
+
|
|
90
|
+
if (
|
|
91
|
+
source.supportedRuntimes !== undefined &&
|
|
92
|
+
target.supportedRuntimes === undefined
|
|
93
|
+
) {
|
|
94
|
+
target.supportedRuntimes = source.supportedRuntimes;
|
|
95
|
+
changed = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
source.runtimeOverrides !== undefined &&
|
|
100
|
+
target.runtimeOverrides === undefined
|
|
101
|
+
) {
|
|
102
|
+
target.runtimeOverrides = source.runtimeOverrides;
|
|
103
|
+
changed = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (changed) {
|
|
107
|
+
fs.writeFileSync(targetYaml, yaml.dump(target));
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
// If a user has customized or broken the YAML, leave it untouched.
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
116
|
+
|
|
117
|
+
const srcDir = path.join(BUILTINS_DIR, entry.name);
|
|
118
|
+
for (const file of fs.readdirSync(srcDir)) {
|
|
119
|
+
fs.copyFileSync(path.join(srcDir, file), path.join(targetDir, file));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// scanProfiles — read .claude/skills/*/profile.yaml, validate, pair w/ SKILL.md
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
function scanProfiles(): Map<string, AgentProfile> {
|
|
129
|
+
const profiles = new Map<string, AgentProfile>();
|
|
130
|
+
|
|
131
|
+
if (!fs.existsSync(SKILLS_DIR)) return profiles;
|
|
132
|
+
|
|
133
|
+
for (const entry of fs.readdirSync(SKILLS_DIR, { withFileTypes: true })) {
|
|
134
|
+
if (!entry.isDirectory()) continue;
|
|
135
|
+
|
|
136
|
+
const dir = path.join(SKILLS_DIR, entry.name);
|
|
137
|
+
const yamlPath = path.join(dir, "profile.yaml");
|
|
138
|
+
const skillPath = path.join(dir, "SKILL.md");
|
|
139
|
+
|
|
140
|
+
if (!fs.existsSync(yamlPath)) continue;
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const rawYaml = fs.readFileSync(yamlPath, "utf-8");
|
|
144
|
+
const parsed = yaml.load(rawYaml);
|
|
145
|
+
const result = ProfileConfigSchema.safeParse(parsed);
|
|
146
|
+
|
|
147
|
+
if (!result.success) {
|
|
148
|
+
console.warn(
|
|
149
|
+
`[profiles] Invalid profile.yaml in ${entry.name}:`,
|
|
150
|
+
result.error.issues.map((i) => i.message).join(", ")
|
|
151
|
+
);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const config = result.data;
|
|
156
|
+
const skillMd = fs.existsSync(skillPath)
|
|
157
|
+
? fs.readFileSync(skillPath, "utf-8")
|
|
158
|
+
: "";
|
|
159
|
+
|
|
160
|
+
// Extract description from SKILL.md frontmatter or fall back to name
|
|
161
|
+
const descMatch = skillMd.match(
|
|
162
|
+
/^---\s*\n[\s\S]*?description:\s*(.+?)\s*\n[\s\S]*?---/
|
|
163
|
+
);
|
|
164
|
+
const description = descMatch?.[1] ?? config.name;
|
|
165
|
+
|
|
166
|
+
profiles.set(config.id, {
|
|
167
|
+
id: config.id,
|
|
168
|
+
name: config.name,
|
|
169
|
+
description,
|
|
170
|
+
domain: config.domain,
|
|
171
|
+
tags: config.tags,
|
|
172
|
+
systemPrompt: skillMd, // backward compat
|
|
173
|
+
skillMd,
|
|
174
|
+
allowedTools: config.allowedTools,
|
|
175
|
+
mcpServers: config.mcpServers as Record<string, unknown>,
|
|
176
|
+
canUseToolPolicy: config.canUseToolPolicy,
|
|
177
|
+
temperature: config.temperature,
|
|
178
|
+
maxTurns: config.maxTurns,
|
|
179
|
+
outputFormat: config.outputFormat,
|
|
180
|
+
version: config.version,
|
|
181
|
+
author: config.author,
|
|
182
|
+
source: config.source,
|
|
183
|
+
tests: config.tests,
|
|
184
|
+
supportedRuntimes: getSupportedRuntimes(config),
|
|
185
|
+
runtimeOverrides: config.runtimeOverrides,
|
|
186
|
+
});
|
|
187
|
+
} catch (err) {
|
|
188
|
+
console.warn(`[profiles] Error loading profile ${entry.name}:`, err);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return profiles;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
// Initialization — lazy on first access
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
function ensureLoaded(): Map<string, AgentProfile> {
|
|
200
|
+
ensureBuiltins();
|
|
201
|
+
const signature = getSkillsDirectorySignature();
|
|
202
|
+
|
|
203
|
+
if (!profileCache || profileCacheSignature !== signature) {
|
|
204
|
+
profileCache = scanProfiles();
|
|
205
|
+
profileCacheSignature = signature;
|
|
206
|
+
}
|
|
207
|
+
return profileCache;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// Public API — same synchronous signatures as before
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
export function getProfile(id: string): AgentProfile | undefined {
|
|
215
|
+
return ensureLoaded().get(id);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function listProfiles(): AgentProfile[] {
|
|
219
|
+
return Array.from(ensureLoaded().values());
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function getProfileTags(): Map<string, string[]> {
|
|
223
|
+
const tagMap = new Map<string, string[]>();
|
|
224
|
+
for (const profile of ensureLoaded().values()) {
|
|
225
|
+
tagMap.set(profile.id, profile.tags);
|
|
226
|
+
}
|
|
227
|
+
return tagMap;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Force re-scan of .claude/skills/ — call after user adds/edits profiles */
|
|
231
|
+
export function reloadProfiles(): void {
|
|
232
|
+
profileCache = null;
|
|
233
|
+
profileCacheSignature = null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Check if a profile ID is a built-in (exists in builtins/ source directory) */
|
|
237
|
+
export function isBuiltin(id: string): boolean {
|
|
238
|
+
return fs.existsSync(path.join(BUILTINS_DIR, id, "profile.yaml"));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Create a new custom profile in ~/.claude/skills/ */
|
|
242
|
+
export function createProfile(config: ProfileConfig, skillMd: string): void {
|
|
243
|
+
const result = ProfileConfigSchema.safeParse(config);
|
|
244
|
+
if (!result.success) {
|
|
245
|
+
throw new Error(`Invalid profile: ${result.error.issues.map(i => i.message).join(", ")}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const dir = path.join(SKILLS_DIR, config.id);
|
|
249
|
+
if (fs.existsSync(path.join(dir, "profile.yaml"))) {
|
|
250
|
+
throw new Error(`Profile "${config.id}" already exists`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
254
|
+
fs.writeFileSync(path.join(dir, "profile.yaml"), yaml.dump(config));
|
|
255
|
+
fs.writeFileSync(path.join(dir, "SKILL.md"), skillMd);
|
|
256
|
+
reloadProfiles();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Update an existing custom profile (rejects builtins) */
|
|
260
|
+
export function updateProfile(id: string, config: ProfileConfig, skillMd: string): void {
|
|
261
|
+
if (isBuiltin(id)) {
|
|
262
|
+
throw new Error("Cannot modify built-in profiles");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const result = ProfileConfigSchema.safeParse(config);
|
|
266
|
+
if (!result.success) {
|
|
267
|
+
throw new Error(`Invalid profile: ${result.error.issues.map(i => i.message).join(", ")}`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const dir = path.join(SKILLS_DIR, id);
|
|
271
|
+
if (!fs.existsSync(dir)) {
|
|
272
|
+
throw new Error(`Profile "${id}" not found`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
fs.writeFileSync(path.join(dir, "profile.yaml"), yaml.dump(config));
|
|
276
|
+
fs.writeFileSync(path.join(dir, "SKILL.md"), skillMd);
|
|
277
|
+
reloadProfiles();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Delete a custom profile (rejects builtins) */
|
|
281
|
+
export function deleteProfile(id: string): void {
|
|
282
|
+
if (isBuiltin(id)) {
|
|
283
|
+
throw new Error("Cannot delete built-in profiles");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const dir = path.join(SKILLS_DIR, id);
|
|
287
|
+
if (!fs.existsSync(dir)) {
|
|
288
|
+
throw new Error(`Profile "${id}" not found`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
292
|
+
reloadProfiles();
|
|
293
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_AGENT_RUNTIME,
|
|
3
|
+
type AgentRuntimeId,
|
|
4
|
+
} from "@/lib/agents/runtime/catalog";
|
|
5
|
+
import { runProfileTestsWithRuntime } from "@/lib/agents/runtime";
|
|
6
|
+
|
|
7
|
+
export type { ProfileTestResult, ProfileTestReport } from "./test-types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Run behavioral smoke tests for a profile through the active runtime layer.
|
|
11
|
+
* Default remains Claude until additional runtimes are wired into the UI.
|
|
12
|
+
*/
|
|
13
|
+
export async function runProfileTests(
|
|
14
|
+
profileId: string,
|
|
15
|
+
runtimeId: AgentRuntimeId = DEFAULT_AGENT_RUNTIME
|
|
16
|
+
) {
|
|
17
|
+
return runProfileTestsWithRuntime(profileId, runtimeId);
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentRuntimeId } from "@/lib/agents/runtime/catalog";
|
|
2
|
+
|
|
3
|
+
export interface ProfileTestResult {
|
|
4
|
+
task: string;
|
|
5
|
+
expectedKeywords: string[];
|
|
6
|
+
foundKeywords: string[];
|
|
7
|
+
missingKeywords: string[];
|
|
8
|
+
passed: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ProfileTestReport {
|
|
12
|
+
profileId: string;
|
|
13
|
+
profileName: string;
|
|
14
|
+
runtimeId: AgentRuntimeId;
|
|
15
|
+
results: ProfileTestResult[];
|
|
16
|
+
totalPassed: number;
|
|
17
|
+
totalFailed: number;
|
|
18
|
+
unsupported?: boolean;
|
|
19
|
+
unsupportedReason?: string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentRuntimeId } from "@/lib/agents/runtime/catalog";
|
|
2
|
+
|
|
3
|
+
export interface CanUseToolPolicy {
|
|
4
|
+
autoApprove?: string[];
|
|
5
|
+
autoDeny?: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ProfileSmokeTest {
|
|
9
|
+
task: string;
|
|
10
|
+
expectedKeywords: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ProfileRuntimeOverride {
|
|
14
|
+
instructions?: string;
|
|
15
|
+
allowedTools?: string[];
|
|
16
|
+
mcpServers?: Record<string, unknown>;
|
|
17
|
+
canUseToolPolicy?: CanUseToolPolicy;
|
|
18
|
+
tests?: ProfileSmokeTest[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AgentProfile {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
domain: string;
|
|
26
|
+
tags: string[];
|
|
27
|
+
/** @deprecated Use skillMd instead — kept for backward compat during migration */
|
|
28
|
+
systemPrompt: string;
|
|
29
|
+
/** Full content of the SKILL.md file (system prompt + behavioral instructions) */
|
|
30
|
+
skillMd: string;
|
|
31
|
+
allowedTools?: string[];
|
|
32
|
+
mcpServers?: Record<string, unknown>;
|
|
33
|
+
canUseToolPolicy?: CanUseToolPolicy;
|
|
34
|
+
temperature?: number;
|
|
35
|
+
maxTurns?: number;
|
|
36
|
+
outputFormat?: string;
|
|
37
|
+
version?: string;
|
|
38
|
+
author?: string;
|
|
39
|
+
source?: string;
|
|
40
|
+
tests?: ProfileSmokeTest[];
|
|
41
|
+
supportedRuntimes: AgentRuntimeId[];
|
|
42
|
+
runtimeOverrides?: Partial<Record<AgentRuntimeId, ProfileRuntimeOverride>>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { listProfiles } from "./profiles/registry";
|
|
2
|
+
import { profileSupportsRuntime } from "./profiles/compatibility";
|
|
3
|
+
import {
|
|
4
|
+
executeTaskWithRuntime,
|
|
5
|
+
resumeTaskWithRuntime,
|
|
6
|
+
} from "./runtime";
|
|
7
|
+
import { DEFAULT_AGENT_RUNTIME } from "./runtime/catalog";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Classify a task into an agent profile based on keyword matching.
|
|
11
|
+
* Scores each profile by keyword hits in title + description.
|
|
12
|
+
* Returns the highest-scoring profile ID, or "general" if no strong match.
|
|
13
|
+
*/
|
|
14
|
+
export function classifyTaskProfile(
|
|
15
|
+
title: string,
|
|
16
|
+
description?: string | null,
|
|
17
|
+
runtimeId: string | null | undefined = DEFAULT_AGENT_RUNTIME
|
|
18
|
+
): string {
|
|
19
|
+
const text = `${title} ${description ?? ""}`.toLowerCase();
|
|
20
|
+
const profiles = listProfiles().filter((profile) =>
|
|
21
|
+
profileSupportsRuntime(profile, runtimeId)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
let bestProfile = "general";
|
|
25
|
+
let bestScore = 0;
|
|
26
|
+
|
|
27
|
+
for (const profile of profiles) {
|
|
28
|
+
const profileId = profile.id;
|
|
29
|
+
if (profileId === "general") continue;
|
|
30
|
+
let score = 0;
|
|
31
|
+
for (const tag of profile.tags) {
|
|
32
|
+
if (text.includes(tag)) score++;
|
|
33
|
+
}
|
|
34
|
+
if (score > bestScore) {
|
|
35
|
+
bestScore = score;
|
|
36
|
+
bestProfile = profileId;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Require at least 2 keyword hits to avoid false positives
|
|
41
|
+
return bestScore >= 2 ? bestProfile : "general";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function executeTaskWithAgent(
|
|
45
|
+
taskId: string,
|
|
46
|
+
agentType: string | null | undefined = DEFAULT_AGENT_RUNTIME
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
return executeTaskWithRuntime(taskId, agentType);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function resumeTaskWithAgent(
|
|
52
|
+
taskId: string,
|
|
53
|
+
agentType: string | null | undefined = DEFAULT_AGENT_RUNTIME
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
return resumeTaskWithRuntime(taskId, agentType);
|
|
56
|
+
}
|