Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 566273415f |
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
// Version is set at build time via -ldflags, or defaults to dev
|
||||
var Version = "0.4.14"
|
||||
var Version = "0.4.15"
|
||||
|
||||
func getEnvOrDefault(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vessel",
|
||||
"version": "0.4.14",
|
||||
"version": "0.4.15",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
let { toolCalls }: Props = $props();
|
||||
|
||||
// Tool metadata for icons and colors
|
||||
const toolMeta: Record<string, { icon: string; color: string; label: string }> = {
|
||||
// Tool metadata for built-in tools (exact matches)
|
||||
const builtinToolMeta: Record<string, { icon: string; color: string; label: string }> = {
|
||||
get_location: {
|
||||
icon: '📍',
|
||||
color: 'from-rose-500 to-pink-600',
|
||||
@@ -41,12 +41,103 @@
|
||||
}
|
||||
};
|
||||
|
||||
// Pattern-based styling for custom tools (checked in order, first match wins)
|
||||
const toolPatterns: Array<{ patterns: string[]; icon: string; color: string; label: string }> = [
|
||||
// Agentic Tools (check first for specific naming)
|
||||
{ patterns: ['task_manager', 'task-manager', 'taskmanager'], icon: '📋', color: 'from-indigo-500 to-purple-600', label: 'Tasks' },
|
||||
{ patterns: ['memory_store', 'memory-store', 'memorystore', 'scratchpad'], icon: '🧠', color: 'from-violet-500 to-purple-600', label: 'Memory' },
|
||||
{ patterns: ['think_step', 'structured_thinking', 'reasoning'], icon: '💭', color: 'from-cyan-500 to-blue-600', label: 'Thinking' },
|
||||
{ patterns: ['decision_matrix', 'decision-matrix', 'evaluate'], icon: '⚖️', color: 'from-amber-500 to-orange-600', label: 'Decision' },
|
||||
{ patterns: ['project_planner', 'project-planner', 'breakdown'], icon: '📊', color: 'from-emerald-500 to-teal-600', label: 'Planning' },
|
||||
// Design & UI
|
||||
{ patterns: ['design', 'brief', 'ui', 'ux', 'layout', 'wireframe'], icon: '🎨', color: 'from-pink-500 to-rose-600', label: 'Design' },
|
||||
{ patterns: ['color', 'palette', 'theme', 'style'], icon: '🎨', color: 'from-fuchsia-500 to-pink-600', label: 'Color' },
|
||||
// Search & Discovery
|
||||
{ patterns: ['search', 'find', 'lookup', 'query'], icon: '🔍', color: 'from-blue-500 to-cyan-600', label: 'Search' },
|
||||
// Web & API
|
||||
{ patterns: ['fetch', 'http', 'api', 'request', 'webhook'], icon: '🌐', color: 'from-violet-500 to-purple-600', label: 'API' },
|
||||
{ patterns: ['url', 'link', 'web', 'scrape'], icon: '🔗', color: 'from-indigo-500 to-violet-600', label: 'Web' },
|
||||
// Data & Analysis
|
||||
{ patterns: ['data', 'analyze', 'stats', 'chart', 'graph', 'metric'], icon: '📊', color: 'from-cyan-500 to-blue-600', label: 'Analysis' },
|
||||
{ patterns: ['json', 'transform', 'parse', 'convert', 'format'], icon: '🔄', color: 'from-sky-500 to-cyan-600', label: 'Transform' },
|
||||
// Math & Calculation
|
||||
{ patterns: ['calc', 'math', 'compute', 'formula', 'number'], icon: '🧮', color: 'from-emerald-500 to-teal-600', label: 'Calculate' },
|
||||
// Time & Date
|
||||
{ patterns: ['time', 'date', 'clock', 'schedule', 'calendar'], icon: '🕐', color: 'from-amber-500 to-orange-600', label: 'Time' },
|
||||
// Location & Maps
|
||||
{ patterns: ['location', 'geo', 'place', 'address', 'map', 'coord'], icon: '📍', color: 'from-rose-500 to-pink-600', label: 'Location' },
|
||||
// Text & String
|
||||
{ patterns: ['text', 'string', 'word', 'sentence', 'paragraph'], icon: '📝', color: 'from-slate-500 to-gray-600', label: 'Text' },
|
||||
// Files & Storage
|
||||
{ patterns: ['file', 'read', 'write', 'save', 'load', 'export', 'import'], icon: '📁', color: 'from-yellow-500 to-amber-600', label: 'File' },
|
||||
// Communication
|
||||
{ patterns: ['email', 'mail', 'send', 'message', 'notify', 'alert'], icon: '📧', color: 'from-red-500 to-rose-600', label: 'Message' },
|
||||
// User & Auth
|
||||
{ patterns: ['user', 'auth', 'login', 'account', 'profile', 'session'], icon: '👤', color: 'from-blue-500 to-indigo-600', label: 'User' },
|
||||
// Database
|
||||
{ patterns: ['database', 'db', 'sql', 'table', 'record', 'store'], icon: '🗄️', color: 'from-orange-500 to-red-600', label: 'Database' },
|
||||
// Code & Execution
|
||||
{ patterns: ['code', 'script', 'execute', 'run', 'shell', 'command'], icon: '💻', color: 'from-green-500 to-emerald-600', label: 'Code' },
|
||||
// Images & Media
|
||||
{ patterns: ['image', 'photo', 'picture', 'screenshot', 'media', 'video'], icon: '🖼️', color: 'from-purple-500 to-fuchsia-600', label: 'Media' },
|
||||
// Weather
|
||||
{ patterns: ['weather', 'forecast', 'temperature', 'climate'], icon: '🌤️', color: 'from-sky-400 to-blue-500', label: 'Weather' },
|
||||
// Translation & Language
|
||||
{ patterns: ['translate', 'language', 'i18n', 'locale'], icon: '🌍', color: 'from-teal-500 to-cyan-600', label: 'Translate' },
|
||||
// Security & Encryption
|
||||
{ patterns: ['encrypt', 'decrypt', 'hash', 'encode', 'decode', 'secure', 'password'], icon: '🔐', color: 'from-red-600 to-orange-600', label: 'Security' },
|
||||
// Random & Generation
|
||||
{ patterns: ['random', 'generate', 'uuid', 'create', 'make'], icon: '🎲', color: 'from-violet-500 to-purple-600', label: 'Generate' },
|
||||
// Lists & Collections
|
||||
{ patterns: ['list', 'array', 'collection', 'filter', 'sort'], icon: '📋', color: 'from-blue-400 to-indigo-500', label: 'List' },
|
||||
// Validation & Check
|
||||
{ patterns: ['valid', 'check', 'verify', 'test', 'assert'], icon: '✅', color: 'from-green-500 to-teal-600', label: 'Validate' }
|
||||
];
|
||||
|
||||
const defaultMeta = {
|
||||
icon: '⚙️',
|
||||
color: 'from-gray-500 to-gray-600',
|
||||
color: 'from-slate-500 to-slate-600',
|
||||
label: 'Tool'
|
||||
};
|
||||
|
||||
/**
|
||||
* Get tool metadata - checks builtin tools first, then pattern matches, then default
|
||||
*/
|
||||
function getToolMeta(toolName: string): { icon: string; color: string; label: string } {
|
||||
// Check builtin tools first (exact match)
|
||||
if (builtinToolMeta[toolName]) {
|
||||
return builtinToolMeta[toolName];
|
||||
}
|
||||
|
||||
// Pattern match for custom tools
|
||||
const lowerName = toolName.toLowerCase();
|
||||
for (const pattern of toolPatterns) {
|
||||
if (pattern.patterns.some((p) => lowerName.includes(p))) {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
|
||||
// Default fallback
|
||||
return defaultMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert tool name to human-readable label
|
||||
*/
|
||||
function formatToolLabel(toolName: string, detectedLabel: string): string {
|
||||
// If it's a known builtin or detected pattern, use that label
|
||||
if (detectedLabel !== 'Tool') {
|
||||
return detectedLabel;
|
||||
}
|
||||
// Otherwise, humanize the tool name
|
||||
return toolName
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
||||
.split(' ')
|
||||
.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse arguments to display-friendly format
|
||||
*/
|
||||
@@ -200,7 +291,8 @@
|
||||
|
||||
<div class="my-3 space-y-2">
|
||||
{#each toolCalls as call (call.id)}
|
||||
{@const meta = toolMeta[call.name] || defaultMeta}
|
||||
{@const meta = getToolMeta(call.name)}
|
||||
{@const displayLabel = formatToolLabel(call.name, meta.label)}
|
||||
{@const args = parseArgs(call.arguments)}
|
||||
{@const argEntries = Object.entries(args).filter(([_, v]) => v !== undefined && v !== null)}
|
||||
{@const isExpanded = expandedCalls.has(call.id)}
|
||||
@@ -216,12 +308,12 @@
|
||||
class="flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-slate-100/50 dark:hover:bg-slate-700/50"
|
||||
>
|
||||
<!-- Icon -->
|
||||
<span class="text-xl" role="img" aria-label={meta.label}>{meta.icon}</span>
|
||||
<span class="text-xl" role="img" aria-label={displayLabel}>{meta.icon}</span>
|
||||
|
||||
<!-- Tool name and summary -->
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-medium text-slate-800 dark:text-slate-100">{meta.label}</span>
|
||||
<span class="font-medium text-slate-800 dark:text-slate-100">{displayLabel}</span>
|
||||
<span class="font-mono text-xs text-slate-500 dark:text-slate-400">{call.name}</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export interface ToolTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: 'api' | 'data' | 'utility' | 'integration';
|
||||
category: 'api' | 'data' | 'utility' | 'integration' | 'agentic';
|
||||
language: ToolImplementation;
|
||||
code: string;
|
||||
parameters: JSONSchema;
|
||||
@@ -514,6 +514,531 @@ print(json.dumps(result))`,
|
||||
},
|
||||
required: ['text', 'operation']
|
||||
}
|
||||
},
|
||||
|
||||
// Agentic Templates
|
||||
{
|
||||
id: 'js-task-manager',
|
||||
name: 'Task Manager',
|
||||
description: 'Create, update, list, and complete tasks with persistent storage',
|
||||
category: 'agentic',
|
||||
language: 'javascript',
|
||||
code: `// Task Manager with localStorage persistence
|
||||
const STORAGE_KEY = 'vessel_agent_tasks';
|
||||
|
||||
const loadTasks = () => {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
||||
} catch { return []; }
|
||||
};
|
||||
|
||||
const saveTasks = (tasks) => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
|
||||
};
|
||||
|
||||
const action = args.action;
|
||||
let tasks = loadTasks();
|
||||
|
||||
switch (action) {
|
||||
case 'create': {
|
||||
const task = {
|
||||
id: Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
||||
title: args.title,
|
||||
description: args.description || '',
|
||||
priority: args.priority || 'medium',
|
||||
status: 'pending',
|
||||
created: new Date().toISOString(),
|
||||
due: args.due || null,
|
||||
tags: args.tags || []
|
||||
};
|
||||
tasks.push(task);
|
||||
saveTasks(tasks);
|
||||
return { success: true, task, message: 'Task created' };
|
||||
}
|
||||
|
||||
case 'list': {
|
||||
let filtered = tasks;
|
||||
if (args.status) filtered = filtered.filter(t => t.status === args.status);
|
||||
if (args.priority) filtered = filtered.filter(t => t.priority === args.priority);
|
||||
if (args.tag) filtered = filtered.filter(t => t.tags?.includes(args.tag));
|
||||
return {
|
||||
tasks: filtered,
|
||||
total: tasks.length,
|
||||
pending: tasks.filter(t => t.status === 'pending').length,
|
||||
completed: tasks.filter(t => t.status === 'completed').length
|
||||
};
|
||||
}
|
||||
|
||||
case 'update': {
|
||||
const idx = tasks.findIndex(t => t.id === args.id);
|
||||
if (idx === -1) return { error: 'Task not found' };
|
||||
if (args.title) tasks[idx].title = args.title;
|
||||
if (args.description !== undefined) tasks[idx].description = args.description;
|
||||
if (args.priority) tasks[idx].priority = args.priority;
|
||||
if (args.status) tasks[idx].status = args.status;
|
||||
if (args.due !== undefined) tasks[idx].due = args.due;
|
||||
if (args.tags) tasks[idx].tags = args.tags;
|
||||
tasks[idx].updated = new Date().toISOString();
|
||||
saveTasks(tasks);
|
||||
return { success: true, task: tasks[idx], message: 'Task updated' };
|
||||
}
|
||||
|
||||
case 'complete': {
|
||||
const idx = tasks.findIndex(t => t.id === args.id);
|
||||
if (idx === -1) return { error: 'Task not found' };
|
||||
tasks[idx].status = 'completed';
|
||||
tasks[idx].completedAt = new Date().toISOString();
|
||||
saveTasks(tasks);
|
||||
return { success: true, task: tasks[idx], message: 'Task completed' };
|
||||
}
|
||||
|
||||
case 'delete': {
|
||||
const idx = tasks.findIndex(t => t.id === args.id);
|
||||
if (idx === -1) return { error: 'Task not found' };
|
||||
const deleted = tasks.splice(idx, 1)[0];
|
||||
saveTasks(tasks);
|
||||
return { success: true, deleted, message: 'Task deleted' };
|
||||
}
|
||||
|
||||
case 'clear_completed': {
|
||||
const before = tasks.length;
|
||||
tasks = tasks.filter(t => t.status !== 'completed');
|
||||
saveTasks(tasks);
|
||||
return { success: true, removed: before - tasks.length, remaining: tasks.length };
|
||||
}
|
||||
|
||||
default:
|
||||
return { error: 'Unknown action. Use: create, list, update, complete, delete, clear_completed' };
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action: create, list, update, complete, delete, clear_completed'
|
||||
},
|
||||
id: { type: 'string', description: 'Task ID (for update/complete/delete)' },
|
||||
title: { type: 'string', description: 'Task title (for create/update)' },
|
||||
description: { type: 'string', description: 'Task description' },
|
||||
priority: { type: 'string', description: 'Priority: low, medium, high, urgent' },
|
||||
status: { type: 'string', description: 'Filter/set status: pending, in_progress, completed' },
|
||||
due: { type: 'string', description: 'Due date (ISO format)' },
|
||||
tags: { type: 'array', description: 'Tags for categorization' },
|
||||
tag: { type: 'string', description: 'Filter by tag (for list)' }
|
||||
},
|
||||
required: ['action']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'js-memory-store',
|
||||
name: 'Memory Store',
|
||||
description: 'Store and recall information across conversation turns',
|
||||
category: 'agentic',
|
||||
language: 'javascript',
|
||||
code: `// Memory Store - persistent key-value storage for agent context
|
||||
const STORAGE_KEY = 'vessel_agent_memory';
|
||||
|
||||
const loadMemory = () => {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
|
||||
} catch { return {}; }
|
||||
};
|
||||
|
||||
const saveMemory = (mem) => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(mem));
|
||||
};
|
||||
|
||||
const action = args.action;
|
||||
let memory = loadMemory();
|
||||
|
||||
switch (action) {
|
||||
case 'store': {
|
||||
const key = args.key;
|
||||
const value = args.value;
|
||||
const category = args.category || 'general';
|
||||
|
||||
if (!memory[category]) memory[category] = {};
|
||||
memory[category][key] = {
|
||||
value,
|
||||
stored: new Date().toISOString(),
|
||||
accessCount: 0
|
||||
};
|
||||
saveMemory(memory);
|
||||
return { success: true, key, category, message: 'Memory stored' };
|
||||
}
|
||||
|
||||
case 'recall': {
|
||||
const key = args.key;
|
||||
const category = args.category;
|
||||
|
||||
if (category && key) {
|
||||
const item = memory[category]?.[key];
|
||||
if (!item) return { found: false, key, category };
|
||||
item.accessCount++;
|
||||
item.lastAccess = new Date().toISOString();
|
||||
saveMemory(memory);
|
||||
return { found: true, key, category, value: item.value, stored: item.stored };
|
||||
}
|
||||
|
||||
if (category) {
|
||||
return { category, items: memory[category] || {} };
|
||||
}
|
||||
|
||||
if (key) {
|
||||
// Search across all categories
|
||||
for (const cat in memory) {
|
||||
if (memory[cat][key]) {
|
||||
memory[cat][key].accessCount++;
|
||||
saveMemory(memory);
|
||||
return { found: true, key, category: cat, value: memory[cat][key].value };
|
||||
}
|
||||
}
|
||||
return { found: false, key };
|
||||
}
|
||||
|
||||
return { error: 'Provide key and/or category' };
|
||||
}
|
||||
|
||||
case 'list': {
|
||||
const category = args.category;
|
||||
if (category) {
|
||||
return {
|
||||
category,
|
||||
keys: Object.keys(memory[category] || {}),
|
||||
count: Object.keys(memory[category] || {}).length
|
||||
};
|
||||
}
|
||||
const summary = {};
|
||||
for (const cat in memory) {
|
||||
summary[cat] = Object.keys(memory[cat]).length;
|
||||
}
|
||||
return { categories: summary, totalCategories: Object.keys(memory).length };
|
||||
}
|
||||
|
||||
case 'forget': {
|
||||
const key = args.key;
|
||||
const category = args.category;
|
||||
|
||||
if (category && key) {
|
||||
if (memory[category]?.[key]) {
|
||||
delete memory[category][key];
|
||||
if (Object.keys(memory[category]).length === 0) delete memory[category];
|
||||
saveMemory(memory);
|
||||
return { success: true, forgotten: key, category };
|
||||
}
|
||||
return { error: 'Memory not found' };
|
||||
}
|
||||
|
||||
if (category) {
|
||||
delete memory[category];
|
||||
saveMemory(memory);
|
||||
return { success: true, forgotten: category, type: 'category' };
|
||||
}
|
||||
|
||||
return { error: 'Provide key and/or category to forget' };
|
||||
}
|
||||
|
||||
case 'clear': {
|
||||
const before = Object.keys(memory).length;
|
||||
memory = {};
|
||||
saveMemory(memory);
|
||||
return { success: true, cleared: before, message: 'All memory cleared' };
|
||||
}
|
||||
|
||||
default:
|
||||
return { error: 'Unknown action. Use: store, recall, list, forget, clear' };
|
||||
}`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
action: {
|
||||
type: 'string',
|
||||
description: 'Action: store, recall, list, forget, clear'
|
||||
},
|
||||
key: { type: 'string', description: 'Memory key/identifier' },
|
||||
value: { type: 'string', description: 'Value to store (for store action)' },
|
||||
category: { type: 'string', description: 'Category for organizing memories (facts, preferences, context, etc.)' }
|
||||
},
|
||||
required: ['action']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'js-think-step-by-step',
|
||||
name: 'Structured Thinking',
|
||||
description: 'Break down problems into explicit reasoning steps',
|
||||
category: 'agentic',
|
||||
language: 'javascript',
|
||||
code: `// Structured Thinking - explicit step-by-step reasoning
|
||||
const problem = args.problem;
|
||||
const steps = args.steps || [];
|
||||
const conclusion = args.conclusion;
|
||||
const confidence = args.confidence || 'medium';
|
||||
|
||||
const analysis = {
|
||||
problem: problem,
|
||||
reasoning: {
|
||||
steps: steps.map((step, i) => ({
|
||||
step: i + 1,
|
||||
thought: step,
|
||||
type: step.toLowerCase().includes('assume') ? 'assumption' :
|
||||
step.toLowerCase().includes('if') ? 'conditional' :
|
||||
step.toLowerCase().includes('because') ? 'justification' :
|
||||
step.toLowerCase().includes('therefore') ? 'inference' :
|
||||
'observation'
|
||||
})),
|
||||
stepCount: steps.length
|
||||
},
|
||||
conclusion: conclusion,
|
||||
confidence: confidence,
|
||||
confidenceScore: confidence === 'high' ? 0.9 :
|
||||
confidence === 'medium' ? 0.7 :
|
||||
confidence === 'low' ? 0.4 : 0.5,
|
||||
metadata: {
|
||||
hasAssumptions: steps.some(s => s.toLowerCase().includes('assume')),
|
||||
hasConditionals: steps.some(s => s.toLowerCase().includes('if')),
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
|
||||
// Add quality indicators
|
||||
analysis.quality = {
|
||||
hasMultipleSteps: steps.length >= 3,
|
||||
hasConclusion: !!conclusion,
|
||||
isWellStructured: steps.length >= 2 && !!conclusion,
|
||||
suggestions: []
|
||||
};
|
||||
|
||||
if (steps.length < 2) {
|
||||
analysis.quality.suggestions.push('Consider breaking down into more steps');
|
||||
}
|
||||
if (!conclusion) {
|
||||
analysis.quality.suggestions.push('Add a clear conclusion');
|
||||
}
|
||||
if (confidence === 'low') {
|
||||
analysis.quality.suggestions.push('Identify what additional information would increase confidence');
|
||||
}
|
||||
|
||||
return analysis;`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
problem: {
|
||||
type: 'string',
|
||||
description: 'The problem or question to reason about'
|
||||
},
|
||||
steps: {
|
||||
type: 'array',
|
||||
description: 'Array of reasoning steps, each a string explaining one step of thought'
|
||||
},
|
||||
conclusion: {
|
||||
type: 'string',
|
||||
description: 'The final conclusion reached'
|
||||
},
|
||||
confidence: {
|
||||
type: 'string',
|
||||
description: 'Confidence level: low, medium, high'
|
||||
}
|
||||
},
|
||||
required: ['problem', 'steps']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'js-decision-matrix',
|
||||
name: 'Decision Matrix',
|
||||
description: 'Evaluate options against weighted criteria for better decisions',
|
||||
category: 'agentic',
|
||||
language: 'javascript',
|
||||
code: `// Decision Matrix - weighted multi-criteria decision analysis
|
||||
const options = args.options || [];
|
||||
const criteria = args.criteria || [];
|
||||
const scores = args.scores || {};
|
||||
|
||||
if (options.length === 0) {
|
||||
return { error: 'Provide at least one option' };
|
||||
}
|
||||
if (criteria.length === 0) {
|
||||
return { error: 'Provide at least one criterion with name and weight' };
|
||||
}
|
||||
|
||||
// Normalize weights
|
||||
const totalWeight = criteria.reduce((sum, c) => sum + (c.weight || 1), 0);
|
||||
const normalizedCriteria = criteria.map(c => ({
|
||||
name: c.name,
|
||||
weight: (c.weight || 1) / totalWeight,
|
||||
originalWeight: c.weight || 1
|
||||
}));
|
||||
|
||||
// Calculate weighted scores for each option
|
||||
const results = options.map(option => {
|
||||
let totalScore = 0;
|
||||
const breakdown = [];
|
||||
|
||||
for (const criterion of normalizedCriteria) {
|
||||
const score = scores[option]?.[criterion.name] ?? 5; // Default to 5/10
|
||||
const weighted = score * criterion.weight;
|
||||
totalScore += weighted;
|
||||
breakdown.push({
|
||||
criterion: criterion.name,
|
||||
rawScore: score,
|
||||
weight: Math.round(criterion.weight * 100) + '%',
|
||||
weightedScore: Math.round(weighted * 100) / 100
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
option,
|
||||
totalScore: Math.round(totalScore * 100) / 100,
|
||||
maxPossible: 10,
|
||||
percentage: Math.round(totalScore * 10) + '%',
|
||||
breakdown
|
||||
};
|
||||
});
|
||||
|
||||
// Sort by score
|
||||
results.sort((a, b) => b.totalScore - a.totalScore);
|
||||
|
||||
// Identify winner and insights
|
||||
const winner = results[0];
|
||||
const runnerUp = results[1];
|
||||
const margin = runnerUp ? Math.round((winner.totalScore - runnerUp.totalScore) * 100) / 100 : null;
|
||||
|
||||
return {
|
||||
recommendation: winner.option,
|
||||
confidence: margin > 1.5 ? 'high' : margin > 0.5 ? 'medium' : 'low',
|
||||
margin: margin,
|
||||
rankings: results,
|
||||
criteria: normalizedCriteria.map(c => ({
|
||||
name: c.name,
|
||||
weight: Math.round(c.weight * 100) + '%'
|
||||
})),
|
||||
insight: margin && margin < 0.5 ?
|
||||
'Options are very close - consider additional criteria or qualitative factors' :
|
||||
margin && margin > 2 ?
|
||||
\`\${winner.option} is a clear winner with significant margin\` :
|
||||
'Decision is reasonably clear but review the breakdown for nuance'
|
||||
};`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
options: {
|
||||
type: 'array',
|
||||
description: 'Array of option names to evaluate (e.g., ["Option A", "Option B"])'
|
||||
},
|
||||
criteria: {
|
||||
type: 'array',
|
||||
description: 'Array of criteria objects with name and weight (e.g., [{"name": "Cost", "weight": 3}, {"name": "Quality", "weight": 2}])'
|
||||
},
|
||||
scores: {
|
||||
type: 'object',
|
||||
description: 'Scores object: { "Option A": { "Cost": 8, "Quality": 7 }, "Option B": { "Cost": 6, "Quality": 9 } }'
|
||||
}
|
||||
},
|
||||
required: ['options', 'criteria', 'scores']
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'js-project-planner',
|
||||
name: 'Project Planner',
|
||||
description: 'Break down projects into phases, tasks, and dependencies',
|
||||
category: 'agentic',
|
||||
language: 'javascript',
|
||||
code: `// Project Planner - decompose projects into actionable plans
|
||||
const projectName = args.project_name;
|
||||
const goal = args.goal;
|
||||
const phases = args.phases || [];
|
||||
const constraints = args.constraints || [];
|
||||
|
||||
if (!projectName || !goal) {
|
||||
return { error: 'Provide project_name and goal' };
|
||||
}
|
||||
|
||||
const plan = {
|
||||
project: projectName,
|
||||
goal: goal,
|
||||
created: new Date().toISOString(),
|
||||
constraints: constraints,
|
||||
phases: phases.map((phase, phaseIdx) => ({
|
||||
id: \`phase-\${phaseIdx + 1}\`,
|
||||
name: phase.name,
|
||||
description: phase.description || '',
|
||||
order: phaseIdx + 1,
|
||||
tasks: (phase.tasks || []).map((task, taskIdx) => ({
|
||||
id: \`\${phaseIdx + 1}.\${taskIdx + 1}\`,
|
||||
title: task.title || task,
|
||||
description: task.description || '',
|
||||
dependencies: task.dependencies || [],
|
||||
status: 'pending',
|
||||
priority: task.priority || 'medium'
|
||||
})),
|
||||
deliverables: phase.deliverables || []
|
||||
})),
|
||||
summary: {
|
||||
totalPhases: phases.length,
|
||||
totalTasks: phases.reduce((sum, p) => sum + (p.tasks?.length || 0), 0),
|
||||
hasConstraints: constraints.length > 0
|
||||
}
|
||||
};
|
||||
|
||||
// Identify critical path (tasks with most dependents)
|
||||
const allTasks = plan.phases.flatMap(p => p.tasks);
|
||||
const dependencyCounts = {};
|
||||
allTasks.forEach(t => {
|
||||
t.dependencies.forEach(dep => {
|
||||
dependencyCounts[dep] = (dependencyCounts[dep] || 0) + 1;
|
||||
});
|
||||
});
|
||||
|
||||
plan.criticalTasks = Object.entries(dependencyCounts)
|
||||
.filter(([_, count]) => count > 1)
|
||||
.map(([id, count]) => ({ taskId: id, dependentCount: count }))
|
||||
.sort((a, b) => b.dependentCount - a.dependentCount);
|
||||
|
||||
// Generate next actions (tasks with no pending dependencies)
|
||||
const completedTasks = new Set();
|
||||
plan.nextActions = allTasks
|
||||
.filter(t => t.dependencies.every(d => completedTasks.has(d)))
|
||||
.slice(0, 5)
|
||||
.map(t => ({ id: t.id, title: t.title, phase: t.id.split('.')[0] }));
|
||||
|
||||
// Validation
|
||||
plan.validation = {
|
||||
isValid: phases.length > 0 && plan.summary.totalTasks > 0,
|
||||
warnings: []
|
||||
};
|
||||
|
||||
if (phases.length === 0) {
|
||||
plan.validation.warnings.push('No phases defined');
|
||||
}
|
||||
if (plan.summary.totalTasks === 0) {
|
||||
plan.validation.warnings.push('No tasks defined');
|
||||
}
|
||||
if (constraints.length === 0) {
|
||||
plan.validation.warnings.push('Consider adding constraints (time, budget, resources)');
|
||||
}
|
||||
|
||||
return plan;`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
project_name: {
|
||||
type: 'string',
|
||||
description: 'Name of the project'
|
||||
},
|
||||
goal: {
|
||||
type: 'string',
|
||||
description: 'The main goal or outcome of the project'
|
||||
},
|
||||
phases: {
|
||||
type: 'array',
|
||||
description: 'Array of phase objects: [{ name, description, tasks: [{ title, dependencies, priority }], deliverables }]'
|
||||
},
|
||||
constraints: {
|
||||
type: 'array',
|
||||
description: 'Array of constraints (e.g., ["Budget: $10k", "Timeline: 2 weeks"])'
|
||||
}
|
||||
},
|
||||
required: ['project_name', 'goal']
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -616,6 +616,18 @@
|
||||
creates a copy in your library that you can customize. Templates with capability tags
|
||||
will auto-match with compatible models.
|
||||
</p>
|
||||
<p class="mt-3 text-xs text-theme-muted">
|
||||
Inspired by prompts from the
|
||||
<a
|
||||
href="https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-purple-400 hover:text-purple-300 hover:underline"
|
||||
>
|
||||
system-prompts-and-models-of-ai-tools
|
||||
</a>
|
||||
collection.
|
||||
</p>
|
||||
</section>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user