@hailer/mcp 0.1.2 → 0.1.4
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 +71 -112
- package/.claude/agents/agent-builder.md +69 -136
- package/.claude/agents/alejandro.md +38 -52
- package/.claude/agents/bjorn.md +49 -300
- package/.claude/agents/dmitri.md +31 -50
- package/.claude/agents/giuseppe.md +45 -54
- package/.claude/agents/gunther.md +63 -350
- package/.claude/agents/helga.md +48 -91
- package/.claude/agents/ingrid.md +48 -99
- package/.claude/agents/kenji.md +44 -52
- package/.claude/agents/svetlana.md +53 -389
- package/.claude/agents/viktor.md +41 -51
- package/.claude/agents/yevgeni.md +27 -48
- package/.claude/assistant-knowledge.md +23 -0
- package/.claude/hooks/agent-failure-detector.cjs +27 -3
- package/.claude/hooks/app-edit-guard.cjs +33 -10
- package/.claude/hooks/builder-mode-manager.cjs +237 -0
- package/.claude/hooks/interactive-mode.cjs +29 -3
- package/.claude/hooks/mcp-server-guard.cjs +20 -4
- package/.claude/hooks/post-scaffold-hook.cjs +23 -4
- package/.claude/hooks/publish-template-guard.cjs +29 -11
- package/.claude/hooks/src-edit-guard.cjs +30 -6
- package/.claude/settings.json +13 -3
- package/.claude/skills/insight-join-patterns/SKILL.md +50 -88
- package/.claude/skills/json-only-output/SKILL.md +32 -0
- package/.claude/skills/optional-parameters/SKILL.md +63 -0
- package/.claude/skills/tool-response-verification/SKILL.md +58 -0
- package/CLAUDE.md +114 -111
- package/dist/client/mcp-assistant.d.ts +21 -0
- package/dist/client/mcp-assistant.js +58 -0
- package/dist/client/mcp-client.js +8 -2
- package/dist/client/providers/assistant-provider.d.ts +17 -0
- package/dist/client/providers/assistant-provider.js +51 -0
- package/dist/client/providers/openai-provider.js +10 -1
- package/dist/client/tool-schema-loader.d.ts +1 -0
- package/dist/client/tool-schema-loader.js +9 -1
- package/dist/client/types.d.ts +2 -1
- package/dist/config.d.ts +5 -1
- package/dist/config.js +11 -5
- package/mcp-system-prompt.txt +127 -0
- package/package.json +1 -1
- package/.claude/hooks/sdk-delete-guard.cjs +0 -119
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP Assistant (Lightweight)
|
|
4
|
+
*
|
|
5
|
+
* Pure pass-through chatbot. System prompt is loaded on AI PC side.
|
|
6
|
+
* Just forwards user messages, no local processing.
|
|
7
|
+
*/
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.McpAssistant = void 0;
|
|
13
|
+
const openai_1 = __importDefault(require("openai"));
|
|
14
|
+
const logger_1 = require("../lib/logger");
|
|
15
|
+
const logger = (0, logger_1.createLogger)({ component: 'McpAssistant' });
|
|
16
|
+
class McpAssistant {
|
|
17
|
+
client;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.client = new openai_1.default({
|
|
20
|
+
apiKey: config.apiKey,
|
|
21
|
+
baseURL: config.baseURL,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Chat - pure pass-through to local LLM
|
|
26
|
+
*/
|
|
27
|
+
async chat(userMessage, conversationHistory = []) {
|
|
28
|
+
const model = process.env.OPENAI_MODEL || 'gpt-4o';
|
|
29
|
+
try {
|
|
30
|
+
const messages = [
|
|
31
|
+
...conversationHistory.slice(-4).map(msg => ({
|
|
32
|
+
role: msg.role,
|
|
33
|
+
content: msg.content,
|
|
34
|
+
})),
|
|
35
|
+
{ role: 'user', content: userMessage },
|
|
36
|
+
];
|
|
37
|
+
logger.debug('Chat', { model, msgCount: messages.length });
|
|
38
|
+
const response = await this.client.chat.completions.create({
|
|
39
|
+
model,
|
|
40
|
+
messages,
|
|
41
|
+
max_tokens: 500,
|
|
42
|
+
temperature: 0.7,
|
|
43
|
+
});
|
|
44
|
+
const reply = response.choices[0]?.message?.content || 'Could not respond.';
|
|
45
|
+
logger.info('Response', {
|
|
46
|
+
in: response.usage?.prompt_tokens,
|
|
47
|
+
out: response.usage?.completion_tokens,
|
|
48
|
+
});
|
|
49
|
+
return reply;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger.error('Chat failed', error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.McpAssistant = McpAssistant;
|
|
58
|
+
//# sourceMappingURL=mcp-assistant.js.map
|
|
@@ -8,6 +8,7 @@ exports.McpClient = void 0;
|
|
|
8
8
|
const message_processor_1 = require("./message-processor");
|
|
9
9
|
const openai_provider_1 = require("./providers/openai-provider");
|
|
10
10
|
const anthropic_provider_1 = require("./providers/anthropic-provider");
|
|
11
|
+
const assistant_provider_1 = require("./providers/assistant-provider");
|
|
11
12
|
const multi_bot_manager_1 = require("./multi-bot-manager");
|
|
12
13
|
const agent_tracker_1 = require("./agent-tracker");
|
|
13
14
|
const token_tracker_1 = require("./token-tracker");
|
|
@@ -102,6 +103,9 @@ class McpClient {
|
|
|
102
103
|
case "anthropic":
|
|
103
104
|
provider = new anthropic_provider_1.AnthropicProvider(providerConfig);
|
|
104
105
|
break;
|
|
106
|
+
case "assistant":
|
|
107
|
+
provider = new assistant_provider_1.AssistantProvider(providerConfig);
|
|
108
|
+
break;
|
|
105
109
|
case "gemini":
|
|
106
110
|
// TODO: Implement GeminiProvider
|
|
107
111
|
console.warn(`⚠️ Provider type ${providerConfig.type} not yet implemented`);
|
|
@@ -306,9 +310,11 @@ class McpClient {
|
|
|
306
310
|
}
|
|
307
311
|
return; // Skip LLM processing
|
|
308
312
|
}
|
|
309
|
-
// Generate and post personalized confirmation message
|
|
313
|
+
// Generate and post personalized confirmation message (skip if empty)
|
|
310
314
|
const confirmationMessage = await provider.generateConfirmationMessage(message);
|
|
311
|
-
|
|
315
|
+
if (confirmationMessage) {
|
|
316
|
+
await this.messageProcessor.postMessage(message.discussionId, confirmationMessage, message.workspaceId, message.mentionedOrDirectMessagedBotId);
|
|
317
|
+
}
|
|
312
318
|
const response = await this.processMessage(message, provider);
|
|
313
319
|
await this.handleResponse(message, response);
|
|
314
320
|
// LOG COMPLETION EVENT - Bot finished processing
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assistant Provider
|
|
3
|
+
*
|
|
4
|
+
* Pure pass-through chatbot. No tools, no local system prompt.
|
|
5
|
+
* System prompt is loaded on AI PC side via llama-server.
|
|
6
|
+
*/
|
|
7
|
+
import { ChatMessage, McpResponse } from "../types";
|
|
8
|
+
import { LlmProvider } from "./llm-provider";
|
|
9
|
+
import { LlmProviderConfig } from "../types";
|
|
10
|
+
export declare class AssistantProvider extends LlmProvider {
|
|
11
|
+
private assistant;
|
|
12
|
+
constructor(config: LlmProviderConfig);
|
|
13
|
+
generateConfirmationMessage(_userMessage: ChatMessage): Promise<string>;
|
|
14
|
+
processMessage(userMessage: ChatMessage, _mcpServerUrl: string, _botMcpApiKey: string, _botEmail: string): Promise<McpResponse>;
|
|
15
|
+
protected callMcpTool(): Promise<any>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=assistant-provider.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Assistant Provider
|
|
4
|
+
*
|
|
5
|
+
* Pure pass-through chatbot. No tools, no local system prompt.
|
|
6
|
+
* System prompt is loaded on AI PC side via llama-server.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AssistantProvider = void 0;
|
|
10
|
+
const llm_provider_1 = require("./llm-provider");
|
|
11
|
+
const mcp_assistant_1 = require("../mcp-assistant");
|
|
12
|
+
class AssistantProvider extends llm_provider_1.LlmProvider {
|
|
13
|
+
assistant;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
super(config);
|
|
16
|
+
this.assistant = new mcp_assistant_1.McpAssistant({
|
|
17
|
+
apiKey: config.apiKey,
|
|
18
|
+
baseURL: config.baseURL,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async generateConfirmationMessage(_userMessage) {
|
|
22
|
+
// No confirmation message - direct response only
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
async processMessage(userMessage, _mcpServerUrl, _botMcpApiKey, _botEmail) {
|
|
26
|
+
if (!this.isEnabled()) {
|
|
27
|
+
return { success: false, error: "Assistant not enabled" };
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
const cleanedMessage = this.removeMentions(userMessage);
|
|
32
|
+
this.logger.debug('Chat request', { user: userMessage.userName });
|
|
33
|
+
const response = await this.assistant.chat(cleanedMessage);
|
|
34
|
+
this.logger.info('Chat done', { duration: Date.now() - startTime });
|
|
35
|
+
return {
|
|
36
|
+
success: true,
|
|
37
|
+
response,
|
|
38
|
+
toolCalls: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
this.logError(error, "processMessage");
|
|
43
|
+
return { success: false, error: error.message };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async callMcpTool() {
|
|
47
|
+
throw new Error("Assistant does not use tools");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.AssistantProvider = AssistantProvider;
|
|
51
|
+
//# sourceMappingURL=assistant-provider.js.map
|
|
@@ -20,6 +20,7 @@ class OpenAIProvider extends llm_provider_1.LlmProvider {
|
|
|
20
20
|
super(config);
|
|
21
21
|
this.client = new openai_1.default({
|
|
22
22
|
apiKey: config.apiKey,
|
|
23
|
+
baseURL: config.baseURL,
|
|
23
24
|
});
|
|
24
25
|
this.contextManager = (0, context_manager_1.getContextManager)({
|
|
25
26
|
openaiApiKey: config.apiKey,
|
|
@@ -72,11 +73,19 @@ class OpenAIProvider extends llm_provider_1.LlmProvider {
|
|
|
72
73
|
const startTime = Date.now();
|
|
73
74
|
// Load tool index with automatic filtering
|
|
74
75
|
// Chat bot only gets READ + WRITE tools (no PLAYGROUND tools)
|
|
76
|
+
// In 'minimal' mode, only load essential chat tools (~3 tools vs 40+)
|
|
75
77
|
const allowedGroups = [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE];
|
|
78
|
+
const minimalChatTools = [
|
|
79
|
+
'add_discussion_message',
|
|
80
|
+
'fetch_discussion_messages',
|
|
81
|
+
'search_workspace_users'
|
|
82
|
+
];
|
|
83
|
+
const isMinimalMode = process.env.CHAT_BOT_MODE === 'minimal';
|
|
76
84
|
const toolIndex = await this.toolSchemaLoader.loadToolIndex({
|
|
77
85
|
mcpServerUrl,
|
|
78
86
|
mcpServerApiKey: botMcpApiKey,
|
|
79
|
-
allowedGroups
|
|
87
|
+
allowedGroups,
|
|
88
|
+
allowedTools: isMinimalMode ? minimalChatTools : undefined
|
|
80
89
|
});
|
|
81
90
|
if (!toolIndex || toolIndex.length === 0) {
|
|
82
91
|
this.logger.warn("No MCP tools available");
|
|
@@ -22,9 +22,17 @@ class ToolSchemaLoader {
|
|
|
22
22
|
* Returns lightweight tool list with optional exclusions
|
|
23
23
|
*/
|
|
24
24
|
async loadToolIndex(options) {
|
|
25
|
-
const { mcpServerUrl, mcpServerApiKey, allowedGroups, excludeMessageFetchTools } = options;
|
|
25
|
+
const { mcpServerUrl, mcpServerApiKey, allowedGroups, allowedTools, excludeMessageFetchTools } = options;
|
|
26
26
|
// Fetch tool index from MCP server with group filtering
|
|
27
27
|
let toolIndex = await this.fetchMcpToolIndex(mcpServerUrl, mcpServerApiKey, allowedGroups);
|
|
28
|
+
// Filter by explicit whitelist if provided (overrides group filtering)
|
|
29
|
+
if (allowedTools && allowedTools.length > 0) {
|
|
30
|
+
toolIndex = toolIndex.filter(tool => allowedTools.includes(tool.name));
|
|
31
|
+
logger.info("Filtered tools by whitelist", {
|
|
32
|
+
allowedTools,
|
|
33
|
+
resultCount: toolIndex.length
|
|
34
|
+
});
|
|
35
|
+
}
|
|
28
36
|
// Optionally exclude message fetch tools if explicitly requested
|
|
29
37
|
if (excludeMessageFetchTools) {
|
|
30
38
|
const excludedTools = ['fetch_discussion_messages', 'fetch_previous_discussion_messages'];
|
package/dist/client/types.d.ts
CHANGED
|
@@ -16,8 +16,9 @@ export interface McpClientConfig {
|
|
|
16
16
|
}
|
|
17
17
|
export interface LlmProviderConfig {
|
|
18
18
|
name: string;
|
|
19
|
-
type: "openai" | "anthropic" | "gemini";
|
|
19
|
+
type: "openai" | "anthropic" | "gemini" | "assistant";
|
|
20
20
|
apiKey: string;
|
|
21
|
+
baseURL?: string;
|
|
21
22
|
model: string;
|
|
22
23
|
enabled: boolean;
|
|
23
24
|
maxTokens?: number;
|
package/dist/config.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare const environment: {
|
|
|
35
35
|
MCP_CLIENT_AGENT_IDS: string[];
|
|
36
36
|
TOKEN_USAGE_BOT_ENABLED: boolean;
|
|
37
37
|
AGENT_ACTIVITY_BOT_ENABLED: boolean;
|
|
38
|
+
CHAT_BOT_MODE: "full" | "minimal" | "assistant";
|
|
38
39
|
ADAPTIVE_DOCUMENTATION_BOT_ENABLED: boolean;
|
|
39
40
|
ADAPTIVE_AUTO_UPDATE: boolean;
|
|
40
41
|
ADAPTIVE_UPDATE_INTERVAL: number;
|
|
@@ -49,6 +50,8 @@ export declare const environment: {
|
|
|
49
50
|
WORKSPACE_CONFIG_PATH?: string | undefined;
|
|
50
51
|
DEV_APPS_PATH?: string | undefined;
|
|
51
52
|
OPENAI_API_KEY?: string | undefined;
|
|
53
|
+
OPENAI_API_BASE?: string | undefined;
|
|
54
|
+
OPENAI_MODEL?: string | undefined;
|
|
52
55
|
ANTHROPIC_API_KEY?: string | undefined;
|
|
53
56
|
};
|
|
54
57
|
export interface HailerAccount {
|
|
@@ -60,8 +63,9 @@ export interface HailerAccount {
|
|
|
60
63
|
}
|
|
61
64
|
export interface LlmProvider {
|
|
62
65
|
name: string;
|
|
63
|
-
type: 'openai' | 'anthropic';
|
|
66
|
+
type: 'openai' | 'anthropic' | 'assistant';
|
|
64
67
|
apiKey: string;
|
|
68
|
+
baseURL?: string;
|
|
65
69
|
model: string;
|
|
66
70
|
enabled: boolean;
|
|
67
71
|
}
|
package/dist/config.js
CHANGED
|
@@ -102,6 +102,8 @@ const environmentSchema = zod_1.z.object({
|
|
|
102
102
|
DEV_APPS_PATH: zod_1.z.string().optional(),
|
|
103
103
|
// LLM providers
|
|
104
104
|
OPENAI_API_KEY: zod_1.z.string().min(1).optional(),
|
|
105
|
+
OPENAI_API_BASE: zod_1.z.string().url().optional(),
|
|
106
|
+
OPENAI_MODEL: zod_1.z.string().optional(),
|
|
105
107
|
ANTHROPIC_API_KEY: zod_1.z.string().min(1).optional(),
|
|
106
108
|
// MCP client settings
|
|
107
109
|
MCP_SERVER_URL: zod_1.z.string().url().default('http://localhost:3030/api/mcp'),
|
|
@@ -110,6 +112,8 @@ const environmentSchema = zod_1.z.object({
|
|
|
110
112
|
// Bot features
|
|
111
113
|
TOKEN_USAGE_BOT_ENABLED: zod_1.z.string().transform(v => v !== 'false').default('true'),
|
|
112
114
|
AGENT_ACTIVITY_BOT_ENABLED: zod_1.z.string().transform(v => v !== 'false').default('true'),
|
|
115
|
+
// Chat bot mode: 'full' = all tools, 'minimal' = chat tools only, 'assistant' = no tools (knowledge-based help)
|
|
116
|
+
CHAT_BOT_MODE: zod_1.z.enum(['full', 'minimal', 'assistant']).default('full'),
|
|
113
117
|
ADAPTIVE_DOCUMENTATION_BOT_ENABLED: zod_1.z.string().transform(v => v === 'true').default('false'),
|
|
114
118
|
ADAPTIVE_AUTO_UPDATE: zod_1.z.string().transform(v => v === 'true').default('false'),
|
|
115
119
|
ADAPTIVE_UPDATE_INTERVAL: zod_1.z.string().transform(v => parseInt(v) || 60000).default('60000'),
|
|
@@ -164,12 +168,14 @@ class ApplicationConfig {
|
|
|
164
168
|
}
|
|
165
169
|
get llmProviders() {
|
|
166
170
|
const providers = [];
|
|
167
|
-
if (exports.environment.OPENAI_API_KEY) {
|
|
171
|
+
if (exports.environment.OPENAI_API_KEY || exports.environment.OPENAI_API_BASE) {
|
|
172
|
+
const isAssistantMode = exports.environment.CHAT_BOT_MODE === 'assistant';
|
|
168
173
|
providers.push({
|
|
169
|
-
name: 'openai-gpt4o',
|
|
170
|
-
type: 'openai',
|
|
171
|
-
apiKey: exports.environment.OPENAI_API_KEY,
|
|
172
|
-
|
|
174
|
+
name: isAssistantMode ? 'mcp-assistant' : (exports.environment.OPENAI_API_BASE ? 'local-llm' : 'openai-gpt4o'),
|
|
175
|
+
type: isAssistantMode ? 'assistant' : 'openai',
|
|
176
|
+
apiKey: exports.environment.OPENAI_API_KEY || 'not-needed',
|
|
177
|
+
baseURL: exports.environment.OPENAI_API_BASE,
|
|
178
|
+
model: exports.environment.OPENAI_MODEL || 'gpt-4o',
|
|
173
179
|
enabled: true,
|
|
174
180
|
});
|
|
175
181
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
You are the MCP Assistant for Hailer MCP Server. Help users understand what MCP agents can do and guide them to the right agent for their task.
|
|
2
|
+
|
|
3
|
+
# What is Hailer MCP?
|
|
4
|
+
|
|
5
|
+
Hailer MCP is a Model Context Protocol server that connects Claude Code to Hailer workspaces. It provides 34+ tools for managing workflows, activities, insights, apps, and discussions.
|
|
6
|
+
|
|
7
|
+
Hailer is a SaaS platform for business process management with:
|
|
8
|
+
- Workspaces (organizations)
|
|
9
|
+
- Workflows (kanban-style process boards with phases)
|
|
10
|
+
- Activities (data records within workflows)
|
|
11
|
+
- Discussions (chat threads attached to activities)
|
|
12
|
+
- Insights (SQL-like reports over workflow data)
|
|
13
|
+
- Apps (custom React applications)
|
|
14
|
+
|
|
15
|
+
# Available Agents
|
|
16
|
+
|
|
17
|
+
## Fast Agents (haiku model)
|
|
18
|
+
|
|
19
|
+
### kenji - Data Reader
|
|
20
|
+
- Reads schema, fields, phases from local workspace/ files
|
|
21
|
+
- Falls back to API for activity data and counts
|
|
22
|
+
- Use for: "What fields does X have?", "List workflows", "Show phases"
|
|
23
|
+
|
|
24
|
+
### dmitri - Data Writer
|
|
25
|
+
- Creates and updates activities (single or bulk)
|
|
26
|
+
- Use for: "Create customer", "Update 50 records", "Move to phase"
|
|
27
|
+
- Needs: workflow_id, phase_id, field values from kenji first
|
|
28
|
+
|
|
29
|
+
### yevgeni - Discussion Handler
|
|
30
|
+
- Reads/posts messages, manages discussion membership
|
|
31
|
+
- Use for: "Post message", "Read chat history", "Invite user to discussion"
|
|
32
|
+
|
|
33
|
+
### bjorn - Config Auditor
|
|
34
|
+
- Audits CLAUDE.md, agents, hooks, settings.json
|
|
35
|
+
- Use for: "Check if config is correct", "Find orphaned files"
|
|
36
|
+
|
|
37
|
+
## Reasoning Agents (sonnet model)
|
|
38
|
+
|
|
39
|
+
### helga - Workspace Config
|
|
40
|
+
- Manages workflows, fields, phases via TypeScript SDK files
|
|
41
|
+
- Use for: "Add field to workflow", "Create new workflow", "Rename phase"
|
|
42
|
+
- Returns commands for orchestrator to run (npm run push)
|
|
43
|
+
|
|
44
|
+
### viktor - SQL Insights
|
|
45
|
+
- Creates SQL-like reports over workflow data
|
|
46
|
+
- Use for: "Report of high priority tasks", "Sales by month", "Join customers with orders"
|
|
47
|
+
- Always previews before creating
|
|
48
|
+
|
|
49
|
+
### giuseppe - App Builder
|
|
50
|
+
- Builds Hailer apps with React/TypeScript/Chakra
|
|
51
|
+
- Use for: "Build dashboard showing customers", "Create kanban app"
|
|
52
|
+
- Needs: workflow_id, phase_id, field_ids from kenji first
|
|
53
|
+
|
|
54
|
+
### alejandro - Function Fields
|
|
55
|
+
- Creates calculated/formula fields in workflows
|
|
56
|
+
- Use for: "Add Total Cost = qty * price", "Concatenate first + last name"
|
|
57
|
+
- Always tests formulas before enabling
|
|
58
|
+
|
|
59
|
+
### gunther - MCP Tool Builder
|
|
60
|
+
- Creates new MCP tools for the server itself
|
|
61
|
+
- Use for: "Add tool to count activities", "Create new API endpoint"
|
|
62
|
+
|
|
63
|
+
### svetlana - Code Reviewer
|
|
64
|
+
- Reviews code for bugs, security, best practices (READ-ONLY)
|
|
65
|
+
- Use for: "Review my changes", "Check for security issues"
|
|
66
|
+
|
|
67
|
+
### ada - Skill Creator
|
|
68
|
+
- Creates skills when agents fail repeatedly
|
|
69
|
+
- Use for: "Viktor keeps getting JOINs wrong", "Document this pattern"
|
|
70
|
+
|
|
71
|
+
### ingrid - Document Templates
|
|
72
|
+
- Creates PDF/CSV document templates
|
|
73
|
+
- Use for: "Create invoice template", "Add report template"
|
|
74
|
+
|
|
75
|
+
### agent-builder - Agent Creator
|
|
76
|
+
- Creates new lean agents (<50 lines)
|
|
77
|
+
- Use for: "I need an agent for X"
|
|
78
|
+
|
|
79
|
+
# How Agents Work
|
|
80
|
+
|
|
81
|
+
1. User asks the orchestrator (main Claude)
|
|
82
|
+
2. Orchestrator delegates to appropriate agent
|
|
83
|
+
3. Agent returns JSON with result
|
|
84
|
+
4. Orchestrator interprets and reports back
|
|
85
|
+
|
|
86
|
+
Agents output JSON only:
|
|
87
|
+
{ "status": "success|error", "result": {...}, "summary": "..." }
|
|
88
|
+
|
|
89
|
+
# Common Workflows
|
|
90
|
+
|
|
91
|
+
## Read data
|
|
92
|
+
kenji → list workflows, get schema, get fields
|
|
93
|
+
|
|
94
|
+
## Create/update data
|
|
95
|
+
kenji (get IDs) → dmitri (create/update)
|
|
96
|
+
|
|
97
|
+
## Build report
|
|
98
|
+
kenji (get schema) → viktor (create insight)
|
|
99
|
+
|
|
100
|
+
## Build app
|
|
101
|
+
kenji (get IDs) → giuseppe (scaffold + build)
|
|
102
|
+
|
|
103
|
+
## Configure workspace
|
|
104
|
+
helga (edit files) → orchestrator runs push commands
|
|
105
|
+
|
|
106
|
+
# Current Version: 0.0.5
|
|
107
|
+
|
|
108
|
+
Recent changes:
|
|
109
|
+
- Hailer app builder improvements
|
|
110
|
+
- Marketplace template publishing
|
|
111
|
+
- Safety hooks for destructive operations
|
|
112
|
+
- 34+ MCP tools available
|
|
113
|
+
|
|
114
|
+
# Limitations
|
|
115
|
+
|
|
116
|
+
- Agents run via Claude Code CLI, not standalone
|
|
117
|
+
- Workspace-scoped (cannot access external systems)
|
|
118
|
+
- Orchestrator must provide IDs to write agents
|
|
119
|
+
- Some operations require npm run commands
|
|
120
|
+
|
|
121
|
+
# Guidelines
|
|
122
|
+
|
|
123
|
+
- Be concise (1-3 sentences per response)
|
|
124
|
+
- Match user's language
|
|
125
|
+
- If unsure which agent, ask clarifying question
|
|
126
|
+
- You guide users, you cannot execute actions yourself
|
|
127
|
+
- Recommend kenji first for any data lookup
|
package/package.json
CHANGED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* SDK Delete Guard Hook
|
|
4
|
-
*
|
|
5
|
-
* PreToolUse hook that catches SDK commands which may delete resources
|
|
6
|
-
* and blocks them, instructing Claude to use `yes |` prefix if user confirms.
|
|
7
|
-
*
|
|
8
|
-
* Triggered by: npm run push, npm run *-sync, npm run *-push commands
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// Commands that can cause deletions
|
|
12
|
-
const DELETE_RISK_PATTERNS = [
|
|
13
|
-
/npm run push\b/, // Full push - can delete workflows, fields, phases
|
|
14
|
-
/npm run workflows-sync\b/, // Sync workflows - can delete workflows
|
|
15
|
-
/npm run workflows-push\b/, // Push workflows - can delete workflows
|
|
16
|
-
/npm run phases-push\b/, // Push phases - can delete phases
|
|
17
|
-
/npm run fields-push\b/, // Push fields - can delete fields
|
|
18
|
-
/npm run groups-push\b/, // Push groups - can delete groups
|
|
19
|
-
/npm run teams-push\b/, // Push teams - can delete teams
|
|
20
|
-
/npm run insights-push\b/, // Push insights - can delete insights
|
|
21
|
-
/npm run templates-sync\b/, // Sync templates - can delete templates
|
|
22
|
-
/npm run templates-push\b/, // Push templates - can modify/delete templates
|
|
23
|
-
/hailer-sdk ws-config push\b/,
|
|
24
|
-
/hailer-sdk ws-config.*sync\b/,
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
// Commands that are already safe (have yes | prefix)
|
|
28
|
-
const SAFE_PATTERNS = [
|
|
29
|
-
/^yes\s*\|/,
|
|
30
|
-
/^echo\s+[yYnN]\s*\|/,
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
// Read stdin
|
|
34
|
-
async function readStdin() {
|
|
35
|
-
return new Promise((resolve) => {
|
|
36
|
-
let data = '';
|
|
37
|
-
process.stdin.setEncoding('utf8');
|
|
38
|
-
process.stdin.on('data', chunk => data += chunk);
|
|
39
|
-
process.stdin.on('end', () => resolve(data));
|
|
40
|
-
setTimeout(() => resolve(data), 100);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function main() {
|
|
45
|
-
try {
|
|
46
|
-
const input = await readStdin();
|
|
47
|
-
|
|
48
|
-
if (!input.trim()) {
|
|
49
|
-
process.exit(0);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const hookData = JSON.parse(input);
|
|
53
|
-
const command = hookData.tool_input?.command || '';
|
|
54
|
-
|
|
55
|
-
// If command already has yes/echo prefix, allow it
|
|
56
|
-
const isSafe = SAFE_PATTERNS.some(pattern => pattern.test(command));
|
|
57
|
-
if (isSafe) {
|
|
58
|
-
process.exit(0);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Check if this is a delete-risk command
|
|
62
|
-
const isDeleteRisk = DELETE_RISK_PATTERNS.some(pattern => pattern.test(command));
|
|
63
|
-
|
|
64
|
-
if (!isDeleteRisk) {
|
|
65
|
-
process.exit(0);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Output instructions to stderr (appears as system reminder)
|
|
69
|
-
// This ensures Claude sees and follows the instructions
|
|
70
|
-
const instructions = `============================================================
|
|
71
|
-
⚠️ MANDATORY ACTION REQUIRED - DO NOT SKIP
|
|
72
|
-
============================================================
|
|
73
|
-
|
|
74
|
-
STOP! You MUST use the AskUserQuestion tool BEFORE running this command.
|
|
75
|
-
This is a DESTRUCTIVE operation that may DELETE resources from Hailer.
|
|
76
|
-
|
|
77
|
-
USE THIS EXACT AskUserQuestion CALL:
|
|
78
|
-
|
|
79
|
-
\`\`\`json
|
|
80
|
-
{
|
|
81
|
-
"questions": [{
|
|
82
|
-
"question": "This command may delete resources from Hailer. Items removed from local config will be PERMANENTLY deleted. Proceed with: ${command}?",
|
|
83
|
-
"header": "Confirm Delete",
|
|
84
|
-
"options": [
|
|
85
|
-
{ "label": "Yes, delete", "description": "Proceed with the destructive operation" },
|
|
86
|
-
{ "label": "No, cancel", "description": "Abort - don't delete anything" }
|
|
87
|
-
],
|
|
88
|
-
"multiSelect": false
|
|
89
|
-
}]
|
|
90
|
-
}
|
|
91
|
-
\`\`\`
|
|
92
|
-
|
|
93
|
-
After user responds:
|
|
94
|
-
- If "Yes, delete": Run: yes | ${command}
|
|
95
|
-
- If "No, cancel": Acknowledge cancellation, do NOT run the command
|
|
96
|
-
|
|
97
|
-
============================================================
|
|
98
|
-
REMEMBER: ASK FIRST using AskUserQuestion, then act based on response.
|
|
99
|
-
============================================================`;
|
|
100
|
-
|
|
101
|
-
// Output to stderr so it appears as system reminder
|
|
102
|
-
console.error(instructions);
|
|
103
|
-
|
|
104
|
-
// Block the command - Claude must ask first
|
|
105
|
-
const response = {
|
|
106
|
-
permissionDecision: "deny",
|
|
107
|
-
permissionDecisionReason: "Command blocked. Follow the instructions above to ask user for confirmation first."
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
console.log(JSON.stringify(response));
|
|
111
|
-
process.exit(0);
|
|
112
|
-
|
|
113
|
-
} catch (error) {
|
|
114
|
-
console.error(`[sdk-delete-guard] Error: ${error.message}`);
|
|
115
|
-
process.exit(0);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
main();
|