@hailer/mcp 0.1.5 → 0.1.8
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/agents/{ada.md → agent-ada-skill-builder.md} +1 -1
- package/.claude/agents/{alejandro.md → agent-alejandro-function-fields.md} +1 -1
- package/.claude/agents/{bjorn.md → agent-bjorn-config-audit.md} +1 -1
- package/.claude/agents/{agent-builder.md → agent-builder-agent-creator.md} +13 -2
- package/.claude/agents/{dmitri.md → agent-dmitri-activity-crud.md} +1 -1
- package/.claude/agents/{giuseppe.md → agent-giuseppe-app-builder.md} +1 -1
- package/.claude/agents/{gunther.md → agent-gunther-mcp-tools.md} +1 -1
- package/.claude/agents/{helga.md → agent-helga-workflow-config.md} +1 -1
- package/.claude/agents/{ingrid.md → agent-ingrid-doc-templates.md} +1 -1
- package/.claude/agents/{kenji.md → agent-kenji-data-reader.md} +1 -1
- package/.claude/agents/{nora.md → agent-nora-name-functions.md} +1 -1
- package/.claude/agents/{svetlana.md → agent-svetlana-code-review.md} +1 -1
- package/.claude/agents/{viktor.md → agent-viktor-sql-insights.md} +1 -1
- package/.claude/agents/{yevgeni.md → agent-yevgeni-discussions.md} +1 -1
- package/.claude/hooks/sync-marketplace-agents.cjs +315 -0
- package/.claude/settings.json +5 -0
- package/CHANGELOG.md +63 -0
- package/CLAUDE.md +63 -14
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: ada
|
|
2
|
+
name: agent-ada-skill-builder
|
|
3
3
|
description: Creates skills and updates agents based on failure patterns.\n\n<example>\nuser: "Viktor keeps getting JOIN syntax wrong"\nassistant: {"status":"success","result":{"skill_path":".claude/skills/join-patterns","agent_updated":"viktor"},"summary":"Created JOIN patterns skill"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Read, Write, Edit, Glob
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: alejandro
|
|
2
|
+
name: agent-alejandro-function-fields
|
|
3
3
|
description: Creates and manages calculated function fields in Hailer workflows via SDK v0.8.4. Designs JavaScript formulas, sets up field dependencies with functionVariables, and deploys through workspace TypeScript files.\n\n<example>\nuser: "Add a Total Cost field that multiplies quantity by unit price"\nassistant: {"status":"ready_to_push","result":{"function_created":true,"variables":2},"commands":["npm run fields-push:force"],"summary":"Created total_cost function"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Bash, Read, Edit, Write, Glob
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: bjorn
|
|
2
|
+
name: agent-bjorn-config-audit
|
|
3
3
|
description: Audits SDK v0.8.4 codebase configuration - docs/CLAUDE.md, agents, hooks, settings.json, workspace structure, and documentation alignment.\n\n<example>\nuser: "Check if docs/CLAUDE.md is accurate"\nassistant: {"status":"success","result":{"agents_found":12,"agents_documented":12,"issues":0},"summary":"Audit passed - all agents documented"}\n</example>
|
|
4
4
|
model: haiku
|
|
5
5
|
tools: Read, Glob, Bash
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: agent-builder
|
|
2
|
+
name: agent-builder-agent-creator
|
|
3
3
|
description: Creates lean, token-efficient Claude Code agents for SDK v0.8.4. Builds agents that work with Hailer workspace configuration, MCP tools, and SDK commands.\n\n<example>\nuser: "I need an agent for SQL reports"\nassistant: {"status":"success","result":{"agent":"viktor.md","lines":45},"summary":"Created viktor agent"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Read, Write, Glob
|
|
@@ -25,10 +25,21 @@ I am the Agent Builder. Lean agents, skill references, JSON output. SDK v0.8.4.
|
|
|
25
25
|
6. **JSON ONLY** - Output closing brace, then STOP. Zero prose after JSON.
|
|
26
26
|
</rules>
|
|
27
27
|
|
|
28
|
+
<namespace>
|
|
29
|
+
All agents MUST use the `agent-` prefix for consistent identification across marketplace and SDK.
|
|
30
|
+
Format: `agent-<name>-<short-description>`
|
|
31
|
+
Examples:
|
|
32
|
+
- `agent-kenji-data-reader`
|
|
33
|
+
- `agent-dmitri-activity-crud`
|
|
34
|
+
- `agent-viktor-sql-insights`
|
|
35
|
+
- `agent-helga-workflow-config`
|
|
36
|
+
- `agent-giuseppe-app-builder`
|
|
37
|
+
</namespace>
|
|
38
|
+
|
|
28
39
|
<template>
|
|
29
40
|
```markdown
|
|
30
41
|
---
|
|
31
|
-
name:
|
|
42
|
+
name: agent-<name>-<short-description>
|
|
32
43
|
description: What it does. For SDK agents: mention SDK v0.8.4.\n\n<example>\nuser: "request"\nassistant: {"status":"success","result":{},"summary":"max 50 chars"}\n</example>
|
|
33
44
|
model: haiku|sonnet
|
|
34
45
|
tools: tool_1, tool_2
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: dmitri
|
|
2
|
+
name: agent-dmitri-activity-crud
|
|
3
3
|
description: Creates and updates Hailer activity data. WRITE-ONLY.\n\n<example>\nuser: "Create customer Acme Corp"\nassistant: {"status":"success","result":{"created_ids":["abc123"]},"summary":"Created 1 customer"}\n</example>
|
|
4
4
|
model: haiku
|
|
5
5
|
tools: mcp__hailer__create_activity, mcp__hailer__update_activity
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: giuseppe
|
|
2
|
+
name: agent-giuseppe-app-builder
|
|
3
3
|
description: Builds Hailer apps with @hailer/app-sdk - React/TypeScript/Chakra.\n\n<example>\nuser: "Build app showing customers"\nassistant: {"status":"success","result":{"app_path":"/apps/customers","build_passed":true},"summary":"Built customers-dashboard"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Bash, Read, Write, Edit, Glob, mcp__hailer__scaffold_hailer_app
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: gunther
|
|
2
|
+
name: agent-gunther-mcp-tools
|
|
3
3
|
description: Builds MCP tools for Hailer MCP server.\n\n<example>\nuser: "Create MCP tool for counting activities"\nassistant: {"status":"success","result":{"tool":"count_activities","registered":true,"build_passed":true},"summary":"Created count_activities tool"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Bash, Read, Write, Edit, Glob
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: helga
|
|
2
|
+
name: agent-helga-workflow-config
|
|
3
3
|
description: Manages Hailer workspace configuration as infrastructure-as-code using SDK v0.8.4. Creates workflows, fields, phases, teams, groups, insights, and document templates via local TypeScript files and SDK commands.\n\n<example>\nuser: "Add a Due Date field to Tasks"\nassistant: {"status":"ready_to_push","result":{"files_edited":["fields.ts"]},"commands":["npm run fields-push:force"],"summary":"Added Due Date field"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Bash, Read, Edit, Write, Glob
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: ingrid
|
|
2
|
+
name: agent-ingrid-doc-templates
|
|
3
3
|
description: Document template specialist for SDK v0.8.4. Creates and manages Hailer document templates (PDF/CSV) with precise field mappings and generation functions.\n\n<example>\nuser: "Create a PDF invoice template"\nassistant: {"status":"ready_to_push","result":{"template_added":true},"commands":["npm run templates-sync:force"],"summary":"Added Invoice template - run command"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Bash, Read, Edit, Write, Glob
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: kenji
|
|
2
|
+
name: agent-kenji-data-reader
|
|
3
3
|
description: LOCAL-FIRST data retrieval for SDK v0.8.4 - reads workspace/ before API. Knows about workflows, fields, phases, templates, functions, teams, groups, and insights.\n\n<example>\nuser: "What fields does Tasks have?"\nassistant: {"status":"success","result":{"fields":["taskName","project","dueDate"]},"source":"local","summary":"Read fields.ts"}\n</example>
|
|
4
4
|
model: haiku
|
|
5
5
|
tools: Read, Glob, mcp__hailer__list_workflows_minimal, mcp__hailer__count_activities, mcp__hailer__list_activities, mcp__hailer__list_workflow_phases
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: nora
|
|
2
|
+
name: agent-nora-name-functions
|
|
3
3
|
description: Creates and manages workflow nameFunction configurations via SDK v0.8.4. Designs JavaScript name generation functions with field dependencies for dynamic activity naming.\n\n<example>\nuser: "Add name function to Employees workflow"\nassistant: {"status":"ready_to_push","result":{"name_function_added":true,"variables":1},"commands":["npm run workflows-push"],"summary":"Added name function to Employees"}\n</example>
|
|
4
4
|
model: haiku
|
|
5
5
|
tools: Bash, Read, Edit, Glob
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: svetlana
|
|
2
|
+
name: agent-svetlana-code-review
|
|
3
3
|
description: Reviews code for bugs, security, and best practices. READ-ONLY.\n\n<example>\nuser: "Review my changes before commit"\nassistant: {"status":"success","result":{"verdict":"REQUEST_CHANGES","critical":2,"warnings":3},"summary":"Found 2 critical issues"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Read, Glob, Grep, Bash
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: viktor
|
|
2
|
+
name: agent-viktor-sql-insights
|
|
3
3
|
description: Creates and manages SQL-like insights over Hailer workflow data via SDK v0.8.4. Designs queries, defines data sources, and deploys through workspace TypeScript files.\n\n<example>\nuser: "Create report showing high priority tasks"\nassistant: {"status":"ready_to_push","result":{"insight_created":true,"sources":1},"commands":["npm run insights-push:force"],"summary":"Created Tasks by Priority insight"}\n</example>
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools: Bash, Read, Edit, Write, Glob, mcp__hailer__preview_insight, mcp__hailer__get_insight_data, mcp__hailer__list_workflows, mcp__hailer__get_workflow_schema
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: yevgeni
|
|
2
|
+
name: agent-yevgeni-discussions
|
|
3
3
|
description: Handles Hailer discussions - reading, posting, membership.\n\n<example>\nuser: "Post update to team discussion"\nassistant: {"status":"success","result":{"posted":true},"summary":"Posted to team discussion"}\n</example>
|
|
4
4
|
model: haiku
|
|
5
5
|
tools: mcp__hailer__list_my_discussions, mcp__hailer__fetch_discussion_messages, mcp__hailer__fetch_previous_discussion_messages, mcp__hailer__add_discussion_message, mcp__hailer__join_discussion, mcp__hailer__leave_discussion, mcp__hailer__invite_discussion_members, mcp__hailer__get_activity_from_discussion, mcp__hailer__search_workspace_users
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Sync Marketplace Agents Hook
|
|
4
|
+
* Watches installed_plugins.json for changes and updates CLAUDE.md
|
|
5
|
+
* with available marketplace agents when plugins are installed/uninstalled.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
|
|
13
|
+
const PLUGINS_DIR = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces');
|
|
14
|
+
const INSTALLED_PLUGINS = path.join(os.homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
15
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
16
|
+
const CLAUDE_MD = path.join(PROJECT_DIR, 'CLAUDE.md');
|
|
17
|
+
const PROJECT_SETTINGS = path.join(PROJECT_DIR, '.claude', 'settings.json');
|
|
18
|
+
const USER_SETTINGS = path.join(os.homedir(), '.claude', 'settings.json');
|
|
19
|
+
|
|
20
|
+
// Store sync state in user's home, keyed by project path hash
|
|
21
|
+
const SYNC_STATE_DIR = path.join(os.homedir(), '.claude', 'sync-state');
|
|
22
|
+
const PROJECT_HASH = crypto.createHash('md5').update(PROJECT_DIR).digest('hex').slice(0, 12);
|
|
23
|
+
const SYNC_STATE = path.join(SYNC_STATE_DIR, `${PROJECT_HASH}.state`);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get hash of installed_plugins.json content
|
|
27
|
+
*/
|
|
28
|
+
function getInstalledPluginsHash() {
|
|
29
|
+
if (!fs.existsSync(INSTALLED_PLUGINS)) {
|
|
30
|
+
return 'empty';
|
|
31
|
+
}
|
|
32
|
+
const content = fs.readFileSync(INSTALLED_PLUGINS, 'utf-8');
|
|
33
|
+
return crypto.createHash('md5').update(content).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if plugins have changed since last sync
|
|
38
|
+
*/
|
|
39
|
+
function pluginsChanged() {
|
|
40
|
+
const currentHash = getInstalledPluginsHash();
|
|
41
|
+
|
|
42
|
+
if (!fs.existsSync(SYNC_STATE)) {
|
|
43
|
+
return true; // First run
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const lastHash = fs.readFileSync(SYNC_STATE, 'utf-8').trim();
|
|
47
|
+
return currentHash !== lastHash;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Save current sync state
|
|
52
|
+
*/
|
|
53
|
+
function saveSyncState() {
|
|
54
|
+
const currentHash = getInstalledPluginsHash();
|
|
55
|
+
// Ensure sync state directory exists
|
|
56
|
+
if (!fs.existsSync(SYNC_STATE_DIR)) {
|
|
57
|
+
fs.mkdirSync(SYNC_STATE_DIR, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
fs.writeFileSync(SYNC_STATE, currentHash);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get installed plugins from installed_plugins.json
|
|
64
|
+
* Returns Set of "plugin@marketplace" strings
|
|
65
|
+
*/
|
|
66
|
+
function getInstalledPlugins() {
|
|
67
|
+
const plugins = new Set();
|
|
68
|
+
|
|
69
|
+
if (!fs.existsSync(INSTALLED_PLUGINS)) {
|
|
70
|
+
return plugins;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const data = JSON.parse(fs.readFileSync(INSTALLED_PLUGINS, 'utf-8'));
|
|
75
|
+
if (data.plugins) {
|
|
76
|
+
for (const pluginKey of Object.keys(data.plugins)) {
|
|
77
|
+
plugins.add(pluginKey);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
// Ignore parse errors
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return plugins;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Scan marketplace plugins for agents (all discovered plugins)
|
|
89
|
+
* Supports three structures:
|
|
90
|
+
* - Flat: {marketplace}/agents/ (when plugin name == marketplace name)
|
|
91
|
+
* - Root-level: {marketplace}/{plugin}/agents/ (plugin dirs at marketplace root)
|
|
92
|
+
* - Nested: {marketplace}/plugins/{plugin}/agents/
|
|
93
|
+
*/
|
|
94
|
+
function scanMarketplaceAgents(enabledPlugins) {
|
|
95
|
+
const agents = [];
|
|
96
|
+
|
|
97
|
+
if (!fs.existsSync(PLUGINS_DIR)) {
|
|
98
|
+
return agents;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const marketplaces = fs.readdirSync(PLUGINS_DIR);
|
|
102
|
+
|
|
103
|
+
for (const marketplace of marketplaces) {
|
|
104
|
+
const marketplacePath = path.join(PLUGINS_DIR, marketplace);
|
|
105
|
+
if (!fs.statSync(marketplacePath).isDirectory()) continue;
|
|
106
|
+
|
|
107
|
+
// Try flat structure first: {marketplace}/agents/
|
|
108
|
+
const flatAgentsPath = path.join(marketplacePath, 'agents');
|
|
109
|
+
if (fs.existsSync(flatAgentsPath)) {
|
|
110
|
+
// For flat structure, plugin name equals marketplace name
|
|
111
|
+
const pluginKey = `${marketplace}@${marketplace}`;
|
|
112
|
+
if (enabledPlugins.has(pluginKey)) {
|
|
113
|
+
const agentFiles = fs.readdirSync(flatAgentsPath).filter(f => f.endsWith('.md'));
|
|
114
|
+
|
|
115
|
+
for (const agentFile of agentFiles) {
|
|
116
|
+
const agentName = path.basename(agentFile, '.md');
|
|
117
|
+
const fullPath = path.join(flatAgentsPath, agentFile);
|
|
118
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
119
|
+
const frontmatter = parseFrontmatter(content);
|
|
120
|
+
|
|
121
|
+
agents.push({
|
|
122
|
+
marketplace,
|
|
123
|
+
plugin: marketplace,
|
|
124
|
+
name: agentName,
|
|
125
|
+
fullName: `${marketplace}:${agentName}`,
|
|
126
|
+
description: frontmatter.description || '',
|
|
127
|
+
model: frontmatter.model || 'sonnet'
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Try root-level plugins: {marketplace}/{plugin}/agents/
|
|
134
|
+
// Each subdirectory with .claude-plugin is a plugin
|
|
135
|
+
const marketplaceContents = fs.readdirSync(marketplacePath);
|
|
136
|
+
for (const item of marketplaceContents) {
|
|
137
|
+
if (item === '.claude-plugin' || item === '.git' || item === 'plugins') continue;
|
|
138
|
+
|
|
139
|
+
const itemPath = path.join(marketplacePath, item);
|
|
140
|
+
if (!fs.statSync(itemPath).isDirectory()) continue;
|
|
141
|
+
|
|
142
|
+
const pluginJsonPath = path.join(itemPath, '.claude-plugin', 'plugin.json');
|
|
143
|
+
|
|
144
|
+
// Check if this is a plugin (has .claude-plugin/plugin.json)
|
|
145
|
+
if (fs.existsSync(pluginJsonPath)) {
|
|
146
|
+
const pluginKey = `${item}@${marketplace}`;
|
|
147
|
+
if (!enabledPlugins.has(pluginKey)) continue;
|
|
148
|
+
|
|
149
|
+
const agentsPath = path.join(itemPath, 'agents');
|
|
150
|
+
if (!fs.existsSync(agentsPath)) continue;
|
|
151
|
+
|
|
152
|
+
const agentFiles = fs.readdirSync(agentsPath).filter(f => f.endsWith('.md'));
|
|
153
|
+
|
|
154
|
+
for (const agentFile of agentFiles) {
|
|
155
|
+
const agentName = path.basename(agentFile, '.md');
|
|
156
|
+
const fullPath = path.join(agentsPath, agentFile);
|
|
157
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
158
|
+
const frontmatter = parseFrontmatter(content);
|
|
159
|
+
|
|
160
|
+
agents.push({
|
|
161
|
+
marketplace,
|
|
162
|
+
plugin: item,
|
|
163
|
+
name: agentName,
|
|
164
|
+
fullName: `${item}:${agentName}`,
|
|
165
|
+
description: frontmatter.description || '',
|
|
166
|
+
model: frontmatter.model || 'sonnet'
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Try nested structure: {marketplace}/plugins/{plugin}/agents/
|
|
173
|
+
const pluginsPath = path.join(marketplacePath, 'plugins');
|
|
174
|
+
if (fs.existsSync(pluginsPath)) {
|
|
175
|
+
const plugins = fs.readdirSync(pluginsPath);
|
|
176
|
+
|
|
177
|
+
for (const plugin of plugins) {
|
|
178
|
+
const pluginKey = `${plugin}@${marketplace}`;
|
|
179
|
+
if (!enabledPlugins.has(pluginKey)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const pluginPath = path.join(pluginsPath, plugin);
|
|
184
|
+
if (!fs.statSync(pluginPath).isDirectory()) continue;
|
|
185
|
+
|
|
186
|
+
const agentsPath = path.join(pluginPath, 'agents');
|
|
187
|
+
if (!fs.existsSync(agentsPath)) continue;
|
|
188
|
+
|
|
189
|
+
const agentFiles = fs.readdirSync(agentsPath).filter(f => f.endsWith('.md'));
|
|
190
|
+
|
|
191
|
+
for (const agentFile of agentFiles) {
|
|
192
|
+
const agentName = path.basename(agentFile, '.md');
|
|
193
|
+
const fullPath = path.join(agentsPath, agentFile);
|
|
194
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
195
|
+
const frontmatter = parseFrontmatter(content);
|
|
196
|
+
|
|
197
|
+
agents.push({
|
|
198
|
+
marketplace,
|
|
199
|
+
plugin,
|
|
200
|
+
name: agentName,
|
|
201
|
+
fullName: `${plugin}:${agentName}`,
|
|
202
|
+
description: frontmatter.description || '',
|
|
203
|
+
model: frontmatter.model || 'sonnet'
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return agents;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Parse frontmatter from markdown
|
|
215
|
+
*/
|
|
216
|
+
function parseFrontmatter(content) {
|
|
217
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
218
|
+
if (!match) return {};
|
|
219
|
+
|
|
220
|
+
const frontmatter = {};
|
|
221
|
+
const lines = match[1].split('\n');
|
|
222
|
+
|
|
223
|
+
for (const line of lines) {
|
|
224
|
+
const colonIndex = line.indexOf(':');
|
|
225
|
+
if (colonIndex > 0) {
|
|
226
|
+
const key = line.slice(0, colonIndex).trim();
|
|
227
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
228
|
+
// Clean up description
|
|
229
|
+
if (key === 'description') {
|
|
230
|
+
value = value.replace(/\\n.*/g, '').slice(0, 100);
|
|
231
|
+
}
|
|
232
|
+
frontmatter[key] = value;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return frontmatter;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Generate marketplace agents table
|
|
241
|
+
*/
|
|
242
|
+
function generateAgentTable(agents) {
|
|
243
|
+
if (agents.length === 0) {
|
|
244
|
+
return 'No marketplace agents installed.';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let table = '| Agent | Plugin | Model | Description |\n';
|
|
248
|
+
table += '|-------|--------|-------|-------------|\n';
|
|
249
|
+
|
|
250
|
+
for (const agent of agents) {
|
|
251
|
+
const desc = agent.description.slice(0, 50) + (agent.description.length > 50 ? '...' : '');
|
|
252
|
+
table += `| \`${agent.fullName}\` | ${agent.plugin} | ${agent.model} | ${desc} |\n`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return table;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Update CLAUDE.md with marketplace agents
|
|
260
|
+
*/
|
|
261
|
+
function updateClaudeMd(agents) {
|
|
262
|
+
if (!fs.existsSync(CLAUDE_MD)) {
|
|
263
|
+
return { updated: false, reason: 'CLAUDE.md not found' };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
let content = fs.readFileSync(CLAUDE_MD, 'utf-8');
|
|
267
|
+
|
|
268
|
+
const agentTable = generateAgentTable(agents);
|
|
269
|
+
|
|
270
|
+
// Find and update plugin-marketplace section
|
|
271
|
+
const sectionRegex = /(<plugin-marketplace>[\s\S]*?)(No marketplace agents installed\.|(\| Agent \|[\s\S]*?\n)(?=\n\*\*|<\/plugin-marketplace>))/;
|
|
272
|
+
|
|
273
|
+
if (sectionRegex.test(content)) {
|
|
274
|
+
content = content.replace(sectionRegex, `$1${agentTable}\n`);
|
|
275
|
+
fs.writeFileSync(CLAUDE_MD, content);
|
|
276
|
+
return { updated: true, count: agents.length };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { updated: false, reason: 'plugin-marketplace section not found' };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Main
|
|
284
|
+
*/
|
|
285
|
+
function main() {
|
|
286
|
+
// Skip if installed_plugins.json hasn't changed
|
|
287
|
+
if (!pluginsChanged()) {
|
|
288
|
+
process.exit(0);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
// Get actually installed plugins from installed_plugins.json
|
|
293
|
+
const installedPlugins = getInstalledPlugins();
|
|
294
|
+
|
|
295
|
+
// Scan agents only from installed plugins
|
|
296
|
+
const agents = scanMarketplaceAgents(installedPlugins);
|
|
297
|
+
const result = updateClaudeMd(agents);
|
|
298
|
+
|
|
299
|
+
// Save sync state
|
|
300
|
+
saveSyncState();
|
|
301
|
+
|
|
302
|
+
// Output info
|
|
303
|
+
console.error(`[sync-agents] Detected plugin changes, found ${agents.length} marketplace agents`);
|
|
304
|
+
if (result.updated) {
|
|
305
|
+
console.error(`[sync-agents] Updated CLAUDE.md`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
process.exit(0);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error(`[sync-agents] Error: ${error.message}`);
|
|
311
|
+
process.exit(0); // Don't block on errors
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
main();
|
package/.claude/settings.json
CHANGED
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"type": "command",
|
|
17
17
|
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/interactive-mode.cjs\"",
|
|
18
18
|
"timeout": 5
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"type": "command",
|
|
22
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/sync-marketplace-agents.cjs\"",
|
|
23
|
+
"timeout": 5
|
|
19
24
|
}
|
|
20
25
|
]
|
|
21
26
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,69 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.1.8] - 15-12-2025
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Corrected marketplace git URL to `git@gitlab.com:hailer-repos/hailer-mcp-marketplace.git`
|
|
12
|
+
|
|
13
|
+
## [0.1.7] - 15-12-2025
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **Marketplace Sync Hook**:
|
|
18
|
+
- Changed from session-based to hash-based sync (tracks `installed_plugins.json` changes)
|
|
19
|
+
- Added support for root-level plugin structure: `{marketplace}/{plugin}/agents/`
|
|
20
|
+
- Per-project sync state to avoid unnecessary re-syncs
|
|
21
|
+
- Improved logging when plugin changes detected
|
|
22
|
+
|
|
23
|
+
- **Plugin Documentation**:
|
|
24
|
+
- Restructured CLAUDE.md with separate `<plugin-setup>` and `<plugin-contributing>` sections
|
|
25
|
+
- Clearer instructions for marketplace installation and contribution
|
|
26
|
+
|
|
27
|
+
## [0.1.5] - 12-12-2025
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **Marketplace System**:
|
|
32
|
+
- `sync-marketplace-agents.cjs` hook - Syncs installed marketplace agents to CLAUDE.md
|
|
33
|
+
- Support for flat and nested plugin structures
|
|
34
|
+
- Individual plugin installation support
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- **Agent Namespace Convention**:
|
|
39
|
+
- All agents renamed to `agent-<name>-<description>` format
|
|
40
|
+
- Examples: `agent-kenji-data-reader`, `agent-viktor-sql-insights`
|
|
41
|
+
- Agent builder updated with namespace template
|
|
42
|
+
|
|
43
|
+
- Updated agents with SDK v0.8.4 compatibility
|
|
44
|
+
- Added `agent-nora-name-functions` for workflow name functions
|
|
45
|
+
- Updated CLAUDE.md marketplace URL
|
|
46
|
+
|
|
47
|
+
## [0.1.4] - 11-12-2025
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- XML tag format for skills
|
|
52
|
+
- XML tag updates for hooks and client improvements
|
|
53
|
+
- Fixed stale hook reference
|
|
54
|
+
|
|
55
|
+
## [0.1.3] - 10-12-2025
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- **Agent System Refactor**:
|
|
60
|
+
- XML tag format for agents
|
|
61
|
+
- Multi-agent hook system
|
|
62
|
+
- Replaced skills system with specialized agents
|
|
63
|
+
|
|
64
|
+
## [0.0.6] - 02-12-2025
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- Updated scaffold-hailer-app skill with improved patterns
|
|
69
|
+
|
|
7
70
|
## [0.0.5] - 01-12-2025
|
|
8
71
|
|
|
9
72
|
### Added
|
package/CLAUDE.md
CHANGED
|
@@ -22,19 +22,20 @@ Agents return JSON. You interpret it for the user.
|
|
|
22
22
|
<agents>
|
|
23
23
|
| Pattern | Agent | Model |
|
|
24
24
|
|---------|-------|-------|
|
|
25
|
-
| Read data/schema | `kenji` | haiku |
|
|
26
|
-
| Activity CRUD | `dmitri` | haiku |
|
|
27
|
-
| Discussions/chat | `yevgeni` | haiku |
|
|
28
|
-
| Config audit | `bjorn` | haiku |
|
|
29
|
-
| Workflows, fields, phases | `helga` | sonnet |
|
|
30
|
-
| SQL insights | `viktor` | sonnet |
|
|
31
|
-
| Function fields | `alejandro` | sonnet |
|
|
32
|
-
| MCP tools | `gunther` | sonnet |
|
|
33
|
-
| Hailer apps | `giuseppe` | sonnet |
|
|
34
|
-
| Code review | `svetlana` | sonnet |
|
|
35
|
-
| New agents | `agent-builder` | sonnet |
|
|
36
|
-
| Skills, agent improvements | `ada` | sonnet |
|
|
37
|
-
| Document templates | `ingrid` | sonnet |
|
|
25
|
+
| Read data/schema | `agent-kenji-data-reader` | haiku |
|
|
26
|
+
| Activity CRUD | `agent-dmitri-activity-crud` | haiku |
|
|
27
|
+
| Discussions/chat | `agent-yevgeni-discussions` | haiku |
|
|
28
|
+
| Config audit | `agent-bjorn-config-audit` | haiku |
|
|
29
|
+
| Workflows, fields, phases | `agent-helga-workflow-config` | sonnet |
|
|
30
|
+
| SQL insights | `agent-viktor-sql-insights` | sonnet |
|
|
31
|
+
| Function fields | `agent-alejandro-function-fields` | sonnet |
|
|
32
|
+
| MCP tools | `agent-gunther-mcp-tools` | sonnet |
|
|
33
|
+
| Hailer apps | `agent-giuseppe-app-builder` | sonnet |
|
|
34
|
+
| Code review | `agent-svetlana-code-review` | sonnet |
|
|
35
|
+
| New agents | `agent-builder-agent-creator` | sonnet |
|
|
36
|
+
| Skills, agent improvements | `agent-ada-skill-builder` | sonnet |
|
|
37
|
+
| Document templates | `agent-ingrid-doc-templates` | sonnet |
|
|
38
|
+
| Name functions | `agent-nora-name-functions` | sonnet |
|
|
38
39
|
</agents>
|
|
39
40
|
|
|
40
41
|
<delegation-protocol>
|
|
@@ -52,7 +53,7 @@ OUTPUT (JSON only, no prose):
|
|
|
52
53
|
{ "status": "success|error|ready_to_push|needs_confirmation", "result": {}, "summary": "max 50 chars" }
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
FLOW: Get IDs first (kenji) → pass to execution agents (dmitri, helga, etc.)
|
|
56
|
+
FLOW: Get IDs first (agent-kenji-data-reader) → pass to execution agents (agent-dmitri-activity-crud, agent-helga-workflow-config, etc.)
|
|
56
57
|
</delegation-protocol>
|
|
57
58
|
|
|
58
59
|
<push-commands>
|
|
@@ -154,6 +155,54 @@ DISABLE: Move to `docs/agents/`
|
|
|
154
155
|
WITHOUT AGENTS: Use MCP tools directly
|
|
155
156
|
</customization>
|
|
156
157
|
|
|
158
|
+
<plugin-marketplace>
|
|
159
|
+
Community agents are shared via the Hailer Agent Marketplace (separate git repo).
|
|
160
|
+
|
|
161
|
+
**Marketplace URL:** `git@gitlab.com:hailer-repos/hailer-mcp-marketplace.git`
|
|
162
|
+
|
|
163
|
+
**Installed marketplace agents:**
|
|
164
|
+
No marketplace agents installed.
|
|
165
|
+
|
|
166
|
+
**Use plugin agents:**
|
|
167
|
+
```
|
|
168
|
+
Task(subagent_type="plugin:agent-name", prompt="...", model="haiku|sonnet")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Agent sources:**
|
|
172
|
+
- Default agents: `.claude/agents/` (shipped via npm)
|
|
173
|
+
- Community agents: Plugin marketplace (installed separately)
|
|
174
|
+
</plugin-marketplace>
|
|
175
|
+
|
|
176
|
+
<plugin-setup>
|
|
177
|
+
When user asks to install plugins or set up the marketplace:
|
|
178
|
+
|
|
179
|
+
1. Tell user to run these slash commands:
|
|
180
|
+
```
|
|
181
|
+
/plugin marketplace add git@gitlab.com:hailer-repos/hailer-mcp-marketplace.git
|
|
182
|
+
/plugin install <plugin-name>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
2. Tell user: "Restart Claude Code to load the new agents."
|
|
186
|
+
|
|
187
|
+
Available plugins: `permissions-handler`, `hailer-agents`
|
|
188
|
+
|
|
189
|
+
After restart, sync hook updates this file automatically with installed agents.
|
|
190
|
+
</plugin-setup>
|
|
191
|
+
|
|
192
|
+
<plugin-contributing>
|
|
193
|
+
To contribute agents to the marketplace:
|
|
194
|
+
|
|
195
|
+
1. Clone: `git clone git@gitlab.com:hailer-repos/hailer-mcp-marketplace.git`
|
|
196
|
+
2. Create plugin structure:
|
|
197
|
+
```
|
|
198
|
+
my-plugin/
|
|
199
|
+
.claude-plugin/plugin.json
|
|
200
|
+
agents/agent-my-agent.md
|
|
201
|
+
```
|
|
202
|
+
3. Follow agent structure from `<agent-structure>` section
|
|
203
|
+
4. Push to repo
|
|
204
|
+
</plugin-contributing>
|
|
205
|
+
|
|
157
206
|
<directory>
|
|
158
207
|
```
|
|
159
208
|
workspace/ # Local config (check FIRST)
|