@wipal/agent-team 1.0.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/.claude/rules/common/general-rules.md +141 -0
- package/.claude/rules/lessons/lessons.md +91 -0
- package/.claude/rules/role-rules/dev-fe-rules.md +146 -0
- package/.claude/rules/role-rules/sa-rules.md +226 -0
- package/.claude/skills/SKILL-INDEX.md +299 -0
- package/.claude/skills/community/security-validator/SKILL.md +392 -0
- package/.claude/skills/core/agent-creation/SKILL.md +338 -0
- package/.claude/skills/core/code-review/SKILL.md +154 -0
- package/.claude/skills/core/git-automation/SKILL.md +93 -0
- package/.claude/skills/core/retrospect-work/SKILL.md +172 -0
- package/.claude/skills/domain/architecture/adr-writing/SKILL.md +254 -0
- package/.claude/skills/domain/architecture/adr-writing/references/adr-best-practices.md +257 -0
- package/.claude/skills/domain/architecture/adr-writing/references/adr-examples.md +246 -0
- package/.claude/skills/domain/architecture/adr-writing/references/adr-template.md +160 -0
- package/.claude/skills/domain/architecture/architecture-patterns/SKILL.md +316 -0
- package/.claude/skills/domain/architecture/architecture-patterns/references/event-driven.md +393 -0
- package/.claude/skills/domain/architecture/architecture-patterns/references/microservices.md +315 -0
- package/.claude/skills/domain/architecture/architecture-patterns/references/monolith.md +321 -0
- package/.claude/skills/domain/architecture/architecture-patterns/references/serverless.md +457 -0
- package/.claude/skills/domain/architecture/performance-engineering/SKILL.md +227 -0
- package/.claude/skills/domain/architecture/performance-engineering/references/benchmarking.md +336 -0
- package/.claude/skills/domain/architecture/performance-engineering/references/caching-strategies.md +284 -0
- package/.claude/skills/domain/architecture/performance-engineering/references/optimization.md +298 -0
- package/.claude/skills/domain/architecture/security-architecture/SKILL.md +206 -0
- package/.claude/skills/domain/architecture/security-architecture/references/auth-patterns.md +209 -0
- package/.claude/skills/domain/architecture/security-architecture/references/compliance.md +246 -0
- package/.claude/skills/domain/architecture/security-architecture/references/threat-modeling.md +219 -0
- package/.claude/skills/domain/architecture/system-design/SKILL.md +227 -0
- package/.claude/skills/domain/architecture/system-design/references/distributed-systems.md +231 -0
- package/.claude/skills/domain/architecture/system-design/references/resilience.md +344 -0
- package/.claude/skills/domain/architecture/system-design/references/scalability.md +303 -0
- package/.claude/skills/domain/architecture/tech-selection/SKILL.md +192 -0
- package/.claude/skills/domain/architecture/tech-selection/references/build-vs-buy.md +258 -0
- package/.claude/skills/domain/architecture/tech-selection/references/evaluation-framework.md +203 -0
- package/.claude/skills/domain/architecture/tech-selection/references/tech-radar.md +257 -0
- package/.claude/skills/domain/backend/api-design/SKILL.md +121 -0
- package/.claude/skills/domain/backend/database-design/SKILL.md +156 -0
- package/.claude/skills/domain/backend/performance-be/SKILL.md +210 -0
- package/.claude/skills/domain/backend/security/SKILL.md +138 -0
- package/.claude/skills/domain/backend/testing-be/SKILL.md +203 -0
- package/.claude/skills/domain/devops/ci-cd/SKILL.md +188 -0
- package/.claude/skills/domain/devops/containerization/SKILL.md +177 -0
- package/.claude/skills/domain/devops/deployment/SKILL.md +198 -0
- package/.claude/skills/domain/devops/infrastructure-as-code/SKILL.md +178 -0
- package/.claude/skills/domain/devops/monitoring/SKILL.md +163 -0
- package/.claude/skills/domain/frontend/accessibility/SKILL.md +179 -0
- package/.claude/skills/domain/frontend/frontend-design/SKILL.md +138 -0
- package/.claude/skills/domain/frontend/performance-fe/SKILL.md +195 -0
- package/.claude/skills/domain/frontend/state-management/SKILL.md +190 -0
- package/.claude/skills/domain/frontend/testing-fe/SKILL.md +193 -0
- package/.claude/skills/domain/product/requirements-gathering/SKILL.md +136 -0
- package/.claude/skills/domain/product/roadmap-planning/SKILL.md +169 -0
- package/.claude/skills/domain/product/sprint-planning/SKILL.md +151 -0
- package/.claude/skills/domain/product/stakeholder-communication/SKILL.md +162 -0
- package/.claude/skills/domain/product/user-stories/SKILL.md +141 -0
- package/.claude/skills/domain/quality/bug-reporting/SKILL.md +150 -0
- package/.claude/skills/domain/quality/regression-testing/SKILL.md +178 -0
- package/.claude/skills/domain/quality/test-automation/SKILL.md +185 -0
- package/.claude/skills/domain/quality/test-planning/SKILL.md +177 -0
- package/.claude/skills/leadership/code-review-advanced/SKILL.md +167 -0
- package/.claude/skills/leadership/mentoring/SKILL.md +151 -0
- package/.claude/skills/leadership/technical-debt/SKILL.md +166 -0
- package/.claude/skills/leadership/technical-decision/SKILL.md +160 -0
- package/.claude/skills/security-reports/.gitkeep +0 -0
- package/.claude/skills/skills-registry.yaml +441 -0
- package/README.md +232 -0
- package/bin/agent-team.js +107 -0
- package/package.json +51 -0
- package/src/commands/add.js +227 -0
- package/src/commands/init.js +136 -0
- package/src/commands/list.js +66 -0
- package/src/commands/remove.js +71 -0
- package/src/commands/switch.js +53 -0
- package/src/index.js +11 -0
- package/src/interactive/prompts.js +153 -0
- package/src/server/api/agents.js +150 -0
- package/src/server/api/roles.js +97 -0
- package/src/server/api/skills.js +79 -0
- package/src/server/index.js +78 -0
- package/src/ui/agents.html +174 -0
- package/src/ui/css/styles.css +470 -0
- package/src/ui/index.html +107 -0
- package/src/ui/roles.html +371 -0
- package/src/ui/skills.html +332 -0
- package/src/utils/file-utils.js +193 -0
- package/src/utils/skill-resolver.js +594 -0
- package/src/utils/skill-scanner.js +154 -0
- package/templates/CLAUDE.md.tmpl +42 -0
- package/templates/knowledge.md.tmpl +31 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roles API Routes
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import yaml from 'js-yaml';
|
|
9
|
+
import { getPackageRoot, getClaudeDir } from '../../utils/file-utils.js';
|
|
10
|
+
|
|
11
|
+
const router = Router();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get roles configuration
|
|
15
|
+
*/
|
|
16
|
+
router.get('/', async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const projectRoot = req.app.get('projectRoot');
|
|
19
|
+
|
|
20
|
+
// Load default config
|
|
21
|
+
const defaultPath = path.join(getPackageRoot(), 'config', 'roles.yaml');
|
|
22
|
+
const defaultConfig = await fs.exists(defaultPath)
|
|
23
|
+
? yaml.load(await fs.readFile(defaultPath, 'utf-8'))
|
|
24
|
+
: { roles: {} };
|
|
25
|
+
|
|
26
|
+
// Load project override
|
|
27
|
+
const projectPath = path.join(getClaudeDir(projectRoot), 'roles.yaml');
|
|
28
|
+
const projectConfig = await fs.exists(projectPath)
|
|
29
|
+
? yaml.load(await fs.readFile(projectPath, 'utf-8'))
|
|
30
|
+
: { roles: {} };
|
|
31
|
+
|
|
32
|
+
// Merge
|
|
33
|
+
const merged = {
|
|
34
|
+
...defaultConfig,
|
|
35
|
+
roles: {
|
|
36
|
+
...defaultConfig.roles,
|
|
37
|
+
...projectConfig.roles
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
res.json(merged);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
res.status(500).json({ error: error.message });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Update roles configuration (project-level only)
|
|
49
|
+
*/
|
|
50
|
+
router.put('/', async (req, res) => {
|
|
51
|
+
try {
|
|
52
|
+
const projectRoot = req.app.get('projectRoot');
|
|
53
|
+
const configPath = path.join(getClaudeDir(projectRoot), 'roles.yaml');
|
|
54
|
+
|
|
55
|
+
// Validate YAML
|
|
56
|
+
const content = yaml.dump(req.body);
|
|
57
|
+
|
|
58
|
+
await fs.ensureDir(path.dirname(configPath));
|
|
59
|
+
await fs.writeFile(configPath, content, 'utf-8');
|
|
60
|
+
|
|
61
|
+
res.json({ success: true, message: 'Configuration saved' });
|
|
62
|
+
} catch (error) {
|
|
63
|
+
res.status(500).json({ error: error.message });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get role details
|
|
69
|
+
*/
|
|
70
|
+
router.get('/:name', async (req, res) => {
|
|
71
|
+
try {
|
|
72
|
+
const { name } = req.params;
|
|
73
|
+
const projectRoot = req.app.get('projectRoot');
|
|
74
|
+
|
|
75
|
+
// Load configs
|
|
76
|
+
const defaultPath = path.join(getPackageRoot(), 'config', 'roles.yaml');
|
|
77
|
+
const defaultConfig = yaml.load(await fs.readFile(defaultPath, 'utf-8'));
|
|
78
|
+
|
|
79
|
+
const projectPath = path.join(getClaudeDir(projectRoot), 'roles.yaml');
|
|
80
|
+
const projectConfig = await fs.exists(projectPath)
|
|
81
|
+
? yaml.load(await fs.readFile(projectPath, 'utf-8'))
|
|
82
|
+
: { roles: {} };
|
|
83
|
+
|
|
84
|
+
// Get role (project override takes precedence)
|
|
85
|
+
const role = projectConfig.roles?.[name] || defaultConfig.roles?.[name];
|
|
86
|
+
|
|
87
|
+
if (!role) {
|
|
88
|
+
return res.status(404).json({ error: 'Role not found' });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
res.json({ name, ...role });
|
|
92
|
+
} catch (error) {
|
|
93
|
+
res.status(500).json({ error: error.message });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export default router;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills API Routes
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Router } from 'express';
|
|
6
|
+
import { getSkills, getCategories } from '../../utils/skill-scanner.js';
|
|
7
|
+
import { getPackageRoot } from '../../utils/file-utils.js';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
|
|
11
|
+
const router = Router();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* List all available skills
|
|
15
|
+
*/
|
|
16
|
+
router.get('/', async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const skills = await getSkills();
|
|
19
|
+
res.json(skills);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
res.status(500).json({ error: error.message });
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get all categories
|
|
27
|
+
*/
|
|
28
|
+
router.get('/categories', async (req, res) => {
|
|
29
|
+
try {
|
|
30
|
+
const skills = await getSkills();
|
|
31
|
+
const categories = getCategories(skills);
|
|
32
|
+
res.json(categories);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
res.status(500).json({ error: error.message });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get skill by name
|
|
40
|
+
*/
|
|
41
|
+
router.get('/:name', async (req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
const { name } = req.params;
|
|
44
|
+
const skills = await getSkills();
|
|
45
|
+
const skill = skills.find(s => s.name === name);
|
|
46
|
+
|
|
47
|
+
if (!skill) {
|
|
48
|
+
return res.status(404).json({ error: 'Skill not found' });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
res.json(skill);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
res.status(500).json({ error: error.message });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get skill content (SKILL.md)
|
|
59
|
+
*/
|
|
60
|
+
router.get('/:name/content', async (req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
const { name } = req.params;
|
|
63
|
+
const skills = await getSkills();
|
|
64
|
+
const skill = skills.find(s => s.name === name);
|
|
65
|
+
|
|
66
|
+
if (!skill) {
|
|
67
|
+
return res.status(404).json({ error: 'Skill not found' });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const skillPath = path.join(skill.path, 'SKILL.md');
|
|
71
|
+
const content = await fs.readFile(skillPath, 'utf-8');
|
|
72
|
+
|
|
73
|
+
res.type('text/markdown').send(content);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
res.status(500).json({ error: error.message });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export default router;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express Server for Agent Team UI Dashboard
|
|
3
|
+
* Lightweight server with HTMX-based UI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import open from 'open';
|
|
10
|
+
import agentsRouter from './api/agents.js';
|
|
11
|
+
import rolesRouter from './api/roles.js';
|
|
12
|
+
import skillsRouter from './api/skills.js';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
|
|
17
|
+
const DEFAULT_PORT = 3456;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create Express app
|
|
21
|
+
*/
|
|
22
|
+
export function createApp(projectRoot = process.cwd()) {
|
|
23
|
+
const app = express();
|
|
24
|
+
|
|
25
|
+
// Store project root for use in routes
|
|
26
|
+
app.set('projectRoot', projectRoot);
|
|
27
|
+
|
|
28
|
+
// Middleware
|
|
29
|
+
app.use(express.json());
|
|
30
|
+
app.use(express.urlencoded({ extended: true }));
|
|
31
|
+
|
|
32
|
+
// Static files
|
|
33
|
+
const uiDir = path.join(__dirname, '..', 'ui');
|
|
34
|
+
app.use('/ui', express.static(uiDir));
|
|
35
|
+
|
|
36
|
+
// API Routes
|
|
37
|
+
app.use('/api/agents', agentsRouter);
|
|
38
|
+
app.use('/api/roles', rolesRouter);
|
|
39
|
+
app.use('/api/skills', skillsRouter);
|
|
40
|
+
|
|
41
|
+
// Main page redirect
|
|
42
|
+
app.get('/', (req, res) => {
|
|
43
|
+
res.redirect('/ui/');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return app;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Start server
|
|
51
|
+
*/
|
|
52
|
+
export async function startServer(options = {}) {
|
|
53
|
+
const { port = DEFAULT_PORT, open: openBrowser = true, projectRoot = process.cwd() } = options;
|
|
54
|
+
|
|
55
|
+
const app = createApp(projectRoot);
|
|
56
|
+
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const server = app.listen(port, () => {
|
|
59
|
+
const url = `http://localhost:${port}`;
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(`🚀 Agent Team UI running at: ${url}`);
|
|
62
|
+
console.log('');
|
|
63
|
+
|
|
64
|
+
if (openBrowser) {
|
|
65
|
+
open(url).catch(() => {
|
|
66
|
+
console.log(`Please open ${url} in your browser`);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
resolve({ server, url });
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Run if called directly
|
|
76
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
77
|
+
startServer();
|
|
78
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Agents - Agent Team Dashboard</title>
|
|
7
|
+
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
|
8
|
+
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
|
9
|
+
<link rel="stylesheet" href="/ui/css/styles.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div class="container" x-data="agentApp()">
|
|
13
|
+
<!-- Header -->
|
|
14
|
+
<header class="header">
|
|
15
|
+
<h1>🤖 Agent Management</h1>
|
|
16
|
+
<p class="subtitle">Create and manage AI agents</p>
|
|
17
|
+
</header>
|
|
18
|
+
|
|
19
|
+
<!-- Navigation -->
|
|
20
|
+
<nav class="nav">
|
|
21
|
+
<a href="/ui/" class="nav-link">Dashboard</a>
|
|
22
|
+
<a href="/ui/agents.html" class="nav-link active">Agents</a>
|
|
23
|
+
<a href="/ui/skills.html" class="nav-link">Skills</a>
|
|
24
|
+
<a href="/ui/roles.html" class="nav-link">Roles</a>
|
|
25
|
+
</nav>
|
|
26
|
+
|
|
27
|
+
<!-- Add Agent Button -->
|
|
28
|
+
<div class="actions-bar">
|
|
29
|
+
<button class="btn btn-primary" @click="showAddModal = true">
|
|
30
|
+
➕ Add New Agent
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- Agents List -->
|
|
35
|
+
<section class="section">
|
|
36
|
+
<div id="agents-grid" hx-get="/api/agents" hx-trigger="load, refreshAgents from:body">
|
|
37
|
+
<div class="loading">Loading agents...</div>
|
|
38
|
+
</div>
|
|
39
|
+
</section>
|
|
40
|
+
|
|
41
|
+
<!-- Add Agent Modal -->
|
|
42
|
+
<div class="modal-overlay" x-show="showAddModal" x-cloak
|
|
43
|
+
@click.self="showAddModal = false"
|
|
44
|
+
@keydown.escape.window="showAddModal = false">
|
|
45
|
+
<div class="modal">
|
|
46
|
+
<div class="modal-header">
|
|
47
|
+
<h2>Add New Agent</h2>
|
|
48
|
+
<button class="modal-close" @click="showAddModal = false">×</button>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div class="modal-body">
|
|
52
|
+
<!-- Step 1: Basic Info -->
|
|
53
|
+
<div class="form-group">
|
|
54
|
+
<label>Agent Name</label>
|
|
55
|
+
<input type="text" x-model="newAgent.name" placeholder="my-agent"
|
|
56
|
+
class="input" pattern="[a-z0-9-]+">
|
|
57
|
+
<small class="hint">Use lowercase letters, numbers, and hyphens</small>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<!-- Step 2: Select Role -->
|
|
61
|
+
<div class="form-group">
|
|
62
|
+
<label>Role</label>
|
|
63
|
+
<div class="role-selector" hx-get="/api/agents/meta/roles" hx-trigger="load">
|
|
64
|
+
<div class="loading">Loading roles...</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<!-- Step 3: Select Variants (shown when role selected) -->
|
|
69
|
+
<div x-show="newAgent.role" x-cloak class="variants-section">
|
|
70
|
+
<h3>Select Variants</h3>
|
|
71
|
+
<div id="variants-container"
|
|
72
|
+
hx-get="/api/agents/meta/variants/"
|
|
73
|
+
:hx-get="'/api/agents/meta/variants/' + newAgent.role"
|
|
74
|
+
hx-trigger="roleSelected from:body">
|
|
75
|
+
<div class="loading">Loading variants...</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Step 4: Preview Skills -->
|
|
80
|
+
<div x-show="newAgent.role" x-cloak class="skills-preview">
|
|
81
|
+
<h3>Skills Preview</h3>
|
|
82
|
+
<div id="skills-preview"
|
|
83
|
+
hx-post="/api/agents/preview-skills"
|
|
84
|
+
hx-include="[name='variants']"
|
|
85
|
+
hx-vals='{"role": newAgent.role}'
|
|
86
|
+
hx-trigger="load, change from:.variant-checkbox">
|
|
87
|
+
<div class="loading">Calculating skills...</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div class="modal-footer">
|
|
93
|
+
<button class="btn btn-secondary" @click="showAddModal = false">Cancel</button>
|
|
94
|
+
<button class="btn btn-primary" @click="createAgent()"
|
|
95
|
+
:disabled="!newAgent.name || !newAgent.role">
|
|
96
|
+
Create Agent
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<script>
|
|
104
|
+
function agentApp() {
|
|
105
|
+
return {
|
|
106
|
+
showAddModal: false,
|
|
107
|
+
newAgent: {
|
|
108
|
+
name: '',
|
|
109
|
+
role: '',
|
|
110
|
+
variants: {}
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
selectRole(role) {
|
|
114
|
+
this.newAgent.role = role;
|
|
115
|
+
// Trigger variant loading
|
|
116
|
+
htmx.trigger(document.body, 'roleSelected', { role });
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
toggleVariant(category, value) {
|
|
120
|
+
if (this.newAgent.variants[category] === value) {
|
|
121
|
+
delete this.newAgent.variants[category];
|
|
122
|
+
} else {
|
|
123
|
+
this.newAgent.variants[category] = value;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
async createAgent() {
|
|
128
|
+
try {
|
|
129
|
+
const response = await fetch('/api/agents', {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: { 'Content-Type': 'application/json' },
|
|
132
|
+
body: JSON.stringify(this.newAgent)
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (response.ok) {
|
|
136
|
+
this.showAddModal = false;
|
|
137
|
+
this.resetForm();
|
|
138
|
+
htmx.trigger(document.body, 'refreshAgents');
|
|
139
|
+
alert('Agent created successfully!');
|
|
140
|
+
} else {
|
|
141
|
+
const error = await response.json();
|
|
142
|
+
alert('Error: ' + error.error);
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
alert('Error creating agent: ' + error.message);
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
resetForm() {
|
|
150
|
+
this.newAgent = { name: '', role: '', variants: {} };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Transform role list to radio buttons
|
|
156
|
+
document.body.addEventListener('htmx:beforeSwap', function(evt) {
|
|
157
|
+
if (evt.detail.pathInfo.requestPath.includes('/meta/roles')) {
|
|
158
|
+
try {
|
|
159
|
+
const roles = JSON.parse(evt.detail.xhr.response);
|
|
160
|
+
evt.detail.xhr.response = roles.map(r => `
|
|
161
|
+
<label class="role-option" :class="{'selected': newAgent.role === '${r.name}'}">
|
|
162
|
+
<input type="radio" name="role" value="${r.name}" @click="selectRole('${r.name}')">
|
|
163
|
+
<div class="role-info">
|
|
164
|
+
<strong>${r.name}</strong>
|
|
165
|
+
<p>${r.description}</p>
|
|
166
|
+
</div>
|
|
167
|
+
</label>
|
|
168
|
+
`).join('');
|
|
169
|
+
} catch (e) {}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
</script>
|
|
173
|
+
</body>
|
|
174
|
+
</html>
|